diff options
author | dartraiden <wowemuh@gmail.com> | 2022-12-22 16:31:20 +0300 |
---|---|---|
committer | dartraiden <wowemuh@gmail.com> | 2022-12-22 16:31:20 +0300 |
commit | 47b6881fe726c904f87aa4be059b730ef77954d0 (patch) | |
tree | aa72b213043d3b514c883bc59a0fa0c434106156 /libs/libcurl/src | |
parent | 8fbc9220b7f3d3a04bbe45d32489ef882821558e (diff) |
libcurl: update to 7.87.0
Diffstat (limited to 'libs/libcurl/src')
332 files changed, 155713 insertions, 154009 deletions
diff --git a/libs/libcurl/src/.checksrc b/libs/libcurl/src/.checksrc index 16133a44c5..8ba24278c4 100644 --- a/libs/libcurl/src/.checksrc +++ b/libs/libcurl/src/.checksrc @@ -1 +1 @@ -enable STRERROR +enable STRERROR
diff --git a/libs/libcurl/src/CMakeLists.txt b/libs/libcurl/src/CMakeLists.txt index 8cea346c34..70ebfba3d8 100644 --- a/libs/libcurl/src/CMakeLists.txt +++ b/libs/libcurl/src/CMakeLists.txt @@ -1,147 +1,173 @@ -#*************************************************************************** -# _ _ ____ _ -# Project ___| | | | _ \| | -# / __| | | | |_) | | -# | (__| |_| | _ <| |___ -# \___|\___/|_| \_\_____| -# -# Copyright (C) 1998 - 2022, 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(LIB_NAME libcurl) -set(LIBCURL_OUTPUT_NAME libcurl CACHE STRING "Basename of the curl library") -add_definitions(-DBUILDING_LIBCURL) - -if(BUILD_SHARED_LIBS) - set(CURL_STATICLIB NO) -else() - set(CURL_STATICLIB YES) -endif() - -# Use: -# * CURL_STATICLIB -configure_file(curl_config.h.cmake - ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h) - -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 - ) - -if(WIN32 AND NOT CURL_STATICLIB) - list(APPEND CSOURCES libcurl.rc) -endif() - -# SET(CSOURCES -# # memdebug.c -not used -# # nwlib.c - Not used -# # strtok.c - specify later -# # strtoofft.c - specify later -# ) - -# #OPTION(CURL_MALLOC_DEBUG "Debug mallocs in Curl" OFF) -# MARK_AS_ADVANCED(CURL_MALLOC_DEBUG) -# IF(CURL_MALLOC_DEBUG) -# SET(CSOURCES ${CSOURCES} -# memdebug.c -# ) -# ENDIF(CURL_MALLOC_DEBUG) - -# # only build compat strtoofft if we need to -# IF(NOT HAVE_STRTOLL AND NOT HAVE__STRTOI64) -# SET(CSOURCES ${CSOURCES} -# strtoofft.c -# ) -# ENDIF(NOT HAVE_STRTOLL AND NOT HAVE__STRTOI64) - - -# The rest of the build - -include_directories(${CMAKE_CURRENT_BINARY_DIR}/../include) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include) -include_directories(${CMAKE_CURRENT_BINARY_DIR}/..) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -include_directories(${CMAKE_CURRENT_BINARY_DIR}) -if(USE_ARES) - include_directories(${CARES_INCLUDE_DIR}) -endif() - -add_library( - ${LIB_NAME} - ${HHEADERS} ${CSOURCES} - ) - -add_library( - ${PROJECT_NAME}::${LIB_NAME} - ALIAS ${LIB_NAME} - ) - -if(NOT BUILD_SHARED_LIBS) - set_target_properties(${LIB_NAME} PROPERTIES INTERFACE_COMPILE_DEFINITIONS CURL_STATICLIB) -endif() - -target_link_libraries(${LIB_NAME} PRIVATE ${CURL_LIBS}) - -set_target_properties(${LIB_NAME} PROPERTIES - COMPILE_DEFINITIONS BUILDING_LIBCURL - OUTPUT_NAME ${LIBCURL_OUTPUT_NAME} - ) - -if(HIDES_CURL_PRIVATE_SYMBOLS) - set_property(TARGET ${LIB_NAME} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS") - set_property(TARGET ${LIB_NAME} APPEND PROPERTY COMPILE_FLAGS ${CURL_CFLAG_SYMBOLS_HIDE}) -endif() - -# Remove the "lib" prefix since the library is already named "libcurl". -set_target_properties(${LIB_NAME} PROPERTIES PREFIX "") -set_target_properties(${LIB_NAME} PROPERTIES IMPORT_PREFIX "") - -if(CURL_HAS_LTO) - set_target_properties(${LIB_NAME} PROPERTIES - INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE - INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE) -endif() - -if(WIN32) - if(BUILD_SHARED_LIBS) - if(MSVC) - # Add "_imp" as a suffix before the extension to avoid conflicting with - # the statically linked "libcurl.lib" - set_target_properties(${LIB_NAME} PROPERTIES IMPORT_SUFFIX "_imp.lib") - endif() - endif() -endif() - -target_include_directories(${LIB_NAME} INTERFACE - $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> - $<BUILD_INTERFACE:${CURL_SOURCE_DIR}/include>) - -if(CURL_ENABLE_EXPORT_TARGET) - install(TARGETS ${LIB_NAME} - EXPORT ${TARGETS_EXPORT_NAME} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - ) - - export(TARGETS ${LIB_NAME} - FILE ${PROJECT_BINARY_DIR}/libcurl-target.cmake - NAMESPACE ${PROJECT_NAME}:: - ) -endif() +#***************************************************************************
+# _ _ ____ _
+# Project ___| | | | _ \| |
+# / __| | | | |_) | |
+# | (__| |_| | _ <| |___
+# \___|\___/|_| \_\_____|
+#
+# Copyright (C) 1998 - 2022, 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(LIB_NAME libcurl)
+set(LIBCURL_OUTPUT_NAME libcurl CACHE STRING "Basename of the curl library")
+add_definitions(-DBUILDING_LIBCURL)
+
+if(BUILD_SHARED_LIBS)
+ set(CURL_STATICLIB NO)
+else()
+ set(CURL_STATICLIB YES)
+endif()
+
+# Use:
+# * CURL_STATICLIB
+configure_file(curl_config.h.cmake
+ ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h)
+
+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
+ )
+
+if(WIN32 AND NOT CURL_STATICLIB)
+ list(APPEND CSOURCES libcurl.rc)
+endif()
+
+# SET(CSOURCES
+# # memdebug.c -not used
+# # nwlib.c - Not used
+# # strtok.c - specify later
+# # strtoofft.c - specify later
+# )
+
+# #OPTION(CURL_MALLOC_DEBUG "Debug mallocs in Curl" OFF)
+# MARK_AS_ADVANCED(CURL_MALLOC_DEBUG)
+# IF(CURL_MALLOC_DEBUG)
+# SET(CSOURCES ${CSOURCES}
+# memdebug.c
+# )
+# ENDIF(CURL_MALLOC_DEBUG)
+
+# # only build compat strtoofft if we need to
+# IF(NOT HAVE_STRTOLL AND NOT HAVE__STRTOI64)
+# SET(CSOURCES ${CSOURCES}
+# strtoofft.c
+# )
+# ENDIF(NOT HAVE_STRTOLL AND NOT HAVE__STRTOI64)
+
+
+# The rest of the build
+
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/../include)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include)
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/..)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+if(USE_ARES)
+ include_directories(${CARES_INCLUDE_DIR})
+endif()
+
+add_library(
+ ${LIB_NAME}
+ ${HHEADERS} ${CSOURCES}
+ )
+
+add_library(
+ ${PROJECT_NAME}::${LIB_NAME}
+ ALIAS ${LIB_NAME}
+ )
+
+if(NOT BUILD_SHARED_LIBS)
+ set_target_properties(${LIB_NAME} PROPERTIES INTERFACE_COMPILE_DEFINITIONS CURL_STATICLIB)
+endif()
+
+target_link_libraries(${LIB_NAME} PRIVATE ${CURL_LIBS})
+
+transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake")
+include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake)
+
+set_target_properties(${LIB_NAME} PROPERTIES
+ COMPILE_DEFINITIONS BUILDING_LIBCURL
+ OUTPUT_NAME ${LIBCURL_OUTPUT_NAME}
+ )
+
+if(CMAKE_SYSTEM_NAME STREQUAL "AIX" OR
+ CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
+ CMAKE_SYSTEM_NAME STREQUAL "GNU/kFreeBSD" OR
+
+ # FreeBSD comes with the a.out and elf flavours
+ # but a.out was supported up to version 3.x and
+ # elf from 3.x. I cannot imagine someone runnig
+ # CMake on those ancient systems
+ CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
+
+ CMAKE_SYSTEM_NAME STREQUAL "Haiku")
+
+ math(EXPR CMAKESONAME "${VERSIONCHANGE} - ${VERSIONDEL}")
+ set(CMAKEVERSION "${CMAKESONAME}.${VERSIONDEL}.${VERSIONADD}")
+
+ set_target_properties(${LIB_NAME} PROPERTIES
+ VERSION ${CMAKEVERSION}
+ SOVERSION ${CMAKESONAME}
+ )
+
+endif()
+
+
+if(HIDES_CURL_PRIVATE_SYMBOLS)
+ set_property(TARGET ${LIB_NAME} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
+ set_property(TARGET ${LIB_NAME} APPEND PROPERTY COMPILE_FLAGS ${CURL_CFLAG_SYMBOLS_HIDE})
+endif()
+
+# Remove the "lib" prefix since the library is already named "libcurl".
+set_target_properties(${LIB_NAME} PROPERTIES PREFIX "")
+set_target_properties(${LIB_NAME} PROPERTIES IMPORT_PREFIX "")
+
+if(CURL_HAS_LTO)
+ set_target_properties(${LIB_NAME} PROPERTIES
+ INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
+ INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
+endif()
+
+if(WIN32)
+ if(BUILD_SHARED_LIBS)
+ if(MSVC)
+ # Add "_imp" as a suffix before the extension to avoid conflicting with
+ # the statically linked "libcurl.lib"
+ set_target_properties(${LIB_NAME} PROPERTIES IMPORT_SUFFIX "_imp.lib")
+ endif()
+ endif()
+endif()
+
+target_include_directories(${LIB_NAME} INTERFACE
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+ $<BUILD_INTERFACE:${CURL_SOURCE_DIR}/include>)
+
+if(CURL_ENABLE_EXPORT_TARGET)
+ install(TARGETS ${LIB_NAME}
+ EXPORT ${TARGETS_EXPORT_NAME}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ )
+
+ export(TARGETS ${LIB_NAME}
+ FILE ${PROJECT_BINARY_DIR}/libcurl-target.cmake
+ NAMESPACE ${PROJECT_NAME}::
+ )
+endif()
diff --git a/libs/libcurl/src/Makefile.am b/libs/libcurl/src/Makefile.am index 5d2df8bffb..deca8e971d 100644 --- a/libs/libcurl/src/Makefile.am +++ b/libs/libcurl/src/Makefile.am @@ -1,161 +1,149 @@ -#*************************************************************************** -# _ _ ____ _ -# Project ___| | | | _ \| | -# / __| | | | |_) | | -# | (__| |_| | _ <| |___ -# \___|\___/|_| \_\_____| -# -# Copyright (C) 1998 - 2022, 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 - -CMAKE_DIST = CMakeLists.txt curl_config.h.cmake - -EXTRA_DIST = Makefile.m32 config-win32.h config-win32ce.h config-plan9.h \ - config-riscos.h config-mac.h curl_config.h.in makefile.dj config-dos.h \ - libcurl.plist libcurl.rc config-amigaos.h makefile.amiga config-win32ce.h \ - config-os400.h setup-os400.h $(CMAKE_DIST) setup-win32.h .checksrc - -lib_LTLIBRARIES = libcurl.la - -if BUILD_UNITTESTS -noinst_LTLIBRARIES = libcurlu.la -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 -# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "private" files - -AM_CPPFLAGS = -I$(top_srcdir)/include \ - -I$(top_builddir)/lib \ - -I$(top_srcdir)/lib - -# Prevent LIBS from being used for all link targets -LIBS = $(BLANK_AT_MAKETIME) - -VERSIONINFO=-version-info 12:0:8 -# 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 - -AM_CPPFLAGS += -DBUILDING_LIBCURL -AM_LDFLAGS = -AM_CFLAGS = - -# Makefile.inc provides the CSOURCES and HHEADERS defines -include Makefile.inc - -libcurl_la_SOURCES = $(CSOURCES) $(HHEADERS) -libcurlu_la_SOURCES = $(CSOURCES) $(HHEADERS) - -libcurl_la_CPPFLAGS_EXTRA = -libcurl_la_LDFLAGS_EXTRA = -libcurl_la_CFLAGS_EXTRA = - -if CURL_LT_SHLIB_USE_VERSION_INFO -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 OS_WINDOWS -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) $(LDFLAGS) $(LIBCURL_LIBS) -libcurl_la_CFLAGS = $(AM_CFLAGS) $(libcurl_la_CFLAGS_EXTRA) - -libcurlu_la_CPPFLAGS = $(AM_CPPFLAGS) -DCURL_STATICLIB -DUNITTESTS -libcurlu_la_LDFLAGS = $(AM_LDFLAGS) -static $(LIBCURL_LIBS) -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) \ - -W$(srcdir)/curl_config.h $(srcdir)/*.[ch] $(srcdir)/vauth/*.[ch] \ - $(srcdir)/vtls/*.[ch] $(srcdir)/vquic/*.[ch] $(srcdir)/vssh/*.[ch]) - -if CURLDEBUG -# for debug builds, we scan the sources on all regular make invokes -all-local: checksrc -endif - -# disable the tests that are mostly causing false positives -TIDYFLAGS=-checks=-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-valist.Uninitialized,-clang-analyzer-core.NonNullParamChecker,-clang-analyzer-core.NullDereference -quiet - -TIDY:=clang-tidy - -tidy: - $(TIDY) $(CSOURCES) $(TIDYFLAGS) -- $(AM_CPPFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H - -optiontable: - perl optiontable.pl < $(top_srcdir)/include/curl/curl.h > easyoptions.c - -if OS_WINDOWS -# Warning is "normal": libtool: error: ignoring unknown tag RC -.rc.lo: - $(LIBTOOL) --tag=RC --mode=compile $(RC) -I$(top_srcdir)/include $(RCFLAGS) -i $< -o $@ -endif +#***************************************************************************
+# _ _ ____ _
+# Project ___| | | | _ \| |
+# / __| | | | |_) | |
+# | (__| |_| | _ <| |___
+# \___|\___/|_| \_\_____|
+#
+# Copyright (C) 1998 - 2022, 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
+
+CMAKE_DIST = CMakeLists.txt curl_config.h.cmake
+
+EXTRA_DIST = Makefile.mk config-win32.h config-win32ce.h config-plan9.h \
+ config-riscos.h config-mac.h curl_config.h.in config-dos.h \
+ libcurl.plist libcurl.rc config-amigaos.h config-win32ce.h \
+ config-os400.h setup-os400.h $(CMAKE_DIST) setup-win32.h .checksrc \
+ Makefile.soname
+
+lib_LTLIBRARIES = libcurl.la
+
+if BUILD_UNITTESTS
+noinst_LTLIBRARIES = libcurlu.la
+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
+# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "private" files
+
+AM_CPPFLAGS = -I$(top_srcdir)/include \
+ -I$(top_builddir)/lib \
+ -I$(top_srcdir)/lib
+
+# Prevent LIBS from being used for all link targets
+LIBS = $(BLANK_AT_MAKETIME)
+
+include Makefile.soname
+
+AM_CPPFLAGS += -DBUILDING_LIBCURL
+AM_LDFLAGS =
+AM_CFLAGS =
+
+# Makefile.inc provides the CSOURCES and HHEADERS defines
+include Makefile.inc
+
+libcurl_la_SOURCES = $(CSOURCES) $(HHEADERS)
+libcurlu_la_SOURCES = $(CSOURCES) $(HHEADERS)
+
+libcurl_la_CPPFLAGS_EXTRA =
+libcurl_la_LDFLAGS_EXTRA =
+libcurl_la_CFLAGS_EXTRA =
+
+if CURL_LT_SHLIB_USE_VERSION_INFO
+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) $(LDFLAGS) $(LIBCURL_LIBS)
+libcurl_la_CFLAGS = $(AM_CFLAGS) $(libcurl_la_CFLAGS_EXTRA)
+
+libcurlu_la_CPPFLAGS = $(AM_CPPFLAGS) -DCURL_STATICLIB -DUNITTESTS
+libcurlu_la_LDFLAGS = $(AM_LDFLAGS) -static $(LIBCURL_LIBS)
+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) \
+ -W$(srcdir)/curl_config.h $(srcdir)/*.[ch] $(srcdir)/vauth/*.[ch] \
+ $(srcdir)/vtls/*.[ch] $(srcdir)/vquic/*.[ch] $(srcdir)/vssh/*.[ch])
+
+if CURLDEBUG
+# for debug builds, we scan the sources on all regular make invokes
+all-local: checksrc
+endif
+
+# disable the tests that are mostly causing false positives
+TIDYFLAGS=-checks=-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-valist.Uninitialized,-clang-analyzer-core.NonNullParamChecker,-clang-analyzer-core.NullDereference -quiet
+
+TIDY:=clang-tidy
+
+tidy:
+ $(TIDY) $(CSOURCES) $(TIDYFLAGS) -- $(AM_CPPFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H
+
+optiontable:
+ perl optiontable.pl < $(top_srcdir)/include/curl/curl.h > easyoptions.c
+
+if HAVE_WINDRES
+.rc.lo:
+ $(LIBTOOL) --tag=RC --mode=compile $(RC) -I$(top_srcdir)/include $(RCFLAGS) -i $< -o $@
+endif
diff --git a/libs/libcurl/src/Makefile.in b/libs/libcurl/src/Makefile.in index 5742ca1cb2..5238098360 100644 --- a/libs/libcurl/src/Makefile.in +++ b/libs/libcurl/src/Makefile.in @@ -1,5145 +1,5203 @@ -# Makefile.in generated by automake 1.16.5 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994-2021 Free Software Foundation, Inc. - -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -#*************************************************************************** -# _ _ ____ _ -# Project ___| | | | _ \| | -# / __| | | | |_) | | -# | (__| |_| | _ <| |___ -# \___|\___/|_| \_\_____| -# -# Copyright (C) 1998 - 2022, 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 -# -########################################################################### - -VPATH = @srcdir@ -am__is_gnu_make = { \ - if test -z '$(MAKELEVEL)'; then \ - false; \ - elif test -n '$(MAKE_HOST)'; then \ - true; \ - elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ - true; \ - else \ - false; \ - fi; \ -} -am__make_running_with_option = \ - case $${target_option-} in \ - ?) ;; \ - *) echo "am__make_running_with_option: internal error: invalid" \ - "target option '$${target_option-}' specified" >&2; \ - exit 1;; \ - esac; \ - has_opt=no; \ - sane_makeflags=$$MAKEFLAGS; \ - if $(am__is_gnu_make); then \ - sane_makeflags=$$MFLAGS; \ - else \ - case $$MAKEFLAGS in \ - *\\[\ \ ]*) \ - bs=\\; \ - sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ - | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ - esac; \ - fi; \ - skip_next=no; \ - strip_trailopt () \ - { \ - flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ - }; \ - for flg in $$sane_makeflags; do \ - test $$skip_next = yes && { skip_next=no; continue; }; \ - case $$flg in \ - *=*|--*) continue;; \ - -*I) strip_trailopt 'I'; skip_next=yes;; \ - -*I?*) strip_trailopt 'I';; \ - -*O) strip_trailopt 'O'; skip_next=yes;; \ - -*O?*) strip_trailopt 'O';; \ - -*l) strip_trailopt 'l'; skip_next=yes;; \ - -*l?*) strip_trailopt 'l';; \ - -[dEDm]) skip_next=yes;; \ - -[JT]) skip_next=yes;; \ - esac; \ - case $$flg in \ - *$$target_option*) has_opt=yes; break;; \ - esac; \ - done; \ - test $$has_opt = yes -am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) -pkgdatadir = $(datadir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkglibexecdir = $(libexecdir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ -@CURL_LT_SHLIB_USE_VERSION_INFO_TRUE@am__append_1 = $(VERSIONINFO) -@CURL_LT_SHLIB_USE_NO_UNDEFINED_TRUE@am__append_2 = -no-undefined -@CURL_LT_SHLIB_USE_MIMPURE_TEXT_TRUE@am__append_3 = -mimpure-text -@CURL_LT_SHLIB_USE_VERSIONED_SYMBOLS_TRUE@am__append_4 = -Wl,--version-script=libcurl.vers -# if symbol-hiding is enabled, hide them! -@CURL_LT_SHLIB_USE_VERSIONED_SYMBOLS_FALSE@@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_5 = -export-symbols-regex '^curl_.*' -@USE_CPPFLAG_CURL_STATICLIB_TRUE@am__append_6 = -DCURL_STATICLIB -@OS_WINDOWS_TRUE@@USE_CPPFLAG_CURL_STATICLIB_FALSE@am__append_7 = $(LIB_RCFILES) -@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_8 = -DCURL_HIDDEN_SYMBOLS -@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_9 = $(CFLAG_CURL_SYMBOL_HIDING) -subdir = lib -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/curl-amissl.m4 \ - $(top_srcdir)/m4/curl-bearssl.m4 \ - $(top_srcdir)/m4/curl-compilers.m4 \ - $(top_srcdir)/m4/curl-confopts.m4 \ - $(top_srcdir)/m4/curl-functions.m4 \ - $(top_srcdir)/m4/curl-gnutls.m4 \ - $(top_srcdir)/m4/curl-mbedtls.m4 $(top_srcdir)/m4/curl-nss.m4 \ - $(top_srcdir)/m4/curl-openssl.m4 \ - $(top_srcdir)/m4/curl-override.m4 \ - $(top_srcdir)/m4/curl-reentrant.m4 \ - $(top_srcdir)/m4/curl-rustls.m4 \ - $(top_srcdir)/m4/curl-schannel.m4 \ - $(top_srcdir)/m4/curl-sectransp.m4 \ - $(top_srcdir)/m4/curl-sysconfig.m4 \ - $(top_srcdir)/m4/curl-wolfssl.m4 $(top_srcdir)/m4/libtool.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/m4/xc-am-iface.m4 \ - $(top_srcdir)/m4/xc-cc-check.m4 \ - $(top_srcdir)/m4/xc-lt-iface.m4 \ - $(top_srcdir)/m4/xc-translit.m4 \ - $(top_srcdir)/m4/xc-val-flgs.m4 \ - $(top_srcdir)/m4/zz40-xc-ovr.m4 \ - $(top_srcdir)/m4/zz50-xc-ovr.m4 \ - $(top_srcdir)/m4/zz60-xc-ovr.m4 $(top_srcdir)/acinclude.m4 \ - $(top_srcdir)/configure.ac -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) -mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = curl_config.h -CONFIG_CLEAN_FILES = libcurl.vers libcurl.plist -CONFIG_CLEAN_VPATH_FILES = -am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; -am__vpath_adj = case $$p in \ - $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ - *) f=$$p;; \ - esac; -am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; -am__install_max = 40 -am__nobase_strip_setup = \ - srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` -am__nobase_strip = \ - for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" -am__nobase_list = $(am__nobase_strip_setup); \ - for p in $$list; do echo "$$p $$p"; done | \ - sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ - $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ - if (++n[$$2] == $(am__install_max)) \ - { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ - END { for (dir in files) print dir, files[dir] }' -am__base_list = \ - sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ - sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' -am__uninstall_files_from_dir = { \ - test -z "$$files" \ - || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ - || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ - $(am__cd) "$$dir" && rm -f $$files; }; \ - } -am__installdirs = "$(DESTDIR)$(libdir)" -LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) -libcurl_la_LIBADD = -am__libcurl_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \ - asyn-thread.c base64.c bufref.c c-hyper.c conncache.c \ - connect.c content_encoding.c cookie.c curl_addrinfo.c \ - curl_des.c curl_endian.c curl_fnmatch.c curl_get_line.c \ - curl_gethostname.c curl_gssapi.c curl_memrchr.c \ - curl_multibyte.c curl_ntlm_core.c curl_ntlm_wb.c curl_path.c \ - curl_range.c curl_rtmp.c curl_sasl.c curl_sspi.c \ - curl_threads.c dict.c doh.c dynbuf.c easy.c easygetopt.c \ - easyoptions.c escape.c file.c fileinfo.c fopen.c formdata.c \ - ftp.c ftplistparser.c getenv.c getinfo.c gopher.c h2h3.c \ - hash.c headers.c hmac.c hostasyn.c hostip.c hostip4.c \ - hostip6.c hostsyn.c hsts.c http.c http2.c http_chunks.c \ - http_digest.c http_negotiate.c http_ntlm.c http_proxy.c \ - http_aws_sigv4.c idn_win32.c if2ip.c imap.c inet_ntop.c \ - inet_pton.c krb5.c ldap.c llist.c md4.c md5.c memdebug.c \ - mime.c mprintf.c mqtt.c multi.c netrc.c nonblock.c noproxy.c \ - openldap.c parsedate.c pingpong.c pop3.c progress.c psl.c \ - rand.c rename.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 strerror.c strtok.c strtoofft.c system_win32.c \ - telnet.c tftp.c timediff.c timeval.c transfer.c url.c urlapi.c \ - version.c version_win32.c warnless.c wildcard.c ws.c \ - 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 vtls/bearssl.c vtls/gskit.c vtls/gtls.c \ - vtls/hostcheck.c vtls/keylog.c vtls/mbedtls.c \ - vtls/mbedtls_threadlock.c vtls/nss.c vtls/openssl.c \ - vtls/rustls.c vtls/schannel.c vtls/schannel_verify.c \ - vtls/sectransp.c vtls/vtls.c vtls/wolfssl.c vtls/x509asn1.c \ - vquic/msh3.c vquic/ngtcp2.c vquic/quiche.c vquic/vquic.c \ - vssh/libssh.c vssh/libssh2.c vssh/wolfssh.c altsvc.h amigaos.h \ - arpa_telnet.h asyn.h bufref.h c-hyper.h conncache.h connect.h \ - content_encoding.h cookie.h curl_addrinfo.h curl_base64.h \ - curl_ctype.h curl_des.h curl_endian.h curl_fnmatch.h \ - curl_get_line.h curl_gethostname.h curl_gssapi.h curl_hmac.h \ - curl_krb5.h curl_ldap.h curl_md4.h curl_md5.h curl_memory.h \ - curl_memrchr.h curl_multibyte.h curl_ntlm_core.h \ - curl_ntlm_wb.h curl_path.h curl_printf.h curl_range.h \ - curl_rtmp.h curl_sasl.h curl_setup.h curl_setup_once.h \ - curl_sha256.h curl_sspi.h curl_threads.h curlx.h dict.h doh.h \ - dynbuf.h easy_lock.h easyif.h easyoptions.h escape.h file.h \ - fileinfo.h fopen.h formdata.h functypes.h ftp.h \ - ftplistparser.h getinfo.h gopher.h h2h3.h hash.h headers.h \ - hostip.h hsts.h http.h http2.h http_chunks.h http_digest.h \ - http_negotiate.h http_ntlm.h http_proxy.h http_aws_sigv4.h \ - if2ip.h imap.h inet_ntop.h inet_pton.h llist.h memdebug.h \ - mime.h mqtt.h multihandle.h multiif.h netrc.h nonblock.h \ - noproxy.h parsedate.h pingpong.h pop3.h progress.h psl.h \ - quic.h rand.h rename.h rtsp.h select.h sendf.h setopt.h \ - setup-vms.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 strtok.h strtoofft.h system_win32.h telnet.h tftp.h \ - timediff.h timeval.h transfer.h url.h urlapi-int.h urldata.h \ - version_win32.h warnless.h wildcard.h ws.h vauth/digest.h \ - vauth/ntlm.h vauth/vauth.h vtls/bearssl.h vtls/gskit.h \ - vtls/gtls.h vtls/hostcheck.h vtls/keylog.h vtls/mbedtls.h \ - vtls/mbedtls_threadlock.h vtls/nssg.h vtls/openssl.h \ - vtls/rustls.h vtls/schannel.h vtls/sectransp.h vtls/vtls.h \ - vtls/wolfssl.h vtls/x509asn1.h vquic/msh3.h vquic/ngtcp2.h \ - vquic/quiche.h vquic/vquic.h vssh/ssh.h libcurl.rc -am__objects_1 = libcurl_la-altsvc.lo libcurl_la-amigaos.lo \ - libcurl_la-asyn-ares.lo libcurl_la-asyn-thread.lo \ - libcurl_la-base64.lo libcurl_la-bufref.lo \ - libcurl_la-c-hyper.lo libcurl_la-conncache.lo \ - libcurl_la-connect.lo libcurl_la-content_encoding.lo \ - libcurl_la-cookie.lo libcurl_la-curl_addrinfo.lo \ - libcurl_la-curl_des.lo libcurl_la-curl_endian.lo \ - libcurl_la-curl_fnmatch.lo libcurl_la-curl_get_line.lo \ - libcurl_la-curl_gethostname.lo libcurl_la-curl_gssapi.lo \ - libcurl_la-curl_memrchr.lo libcurl_la-curl_multibyte.lo \ - libcurl_la-curl_ntlm_core.lo libcurl_la-curl_ntlm_wb.lo \ - libcurl_la-curl_path.lo libcurl_la-curl_range.lo \ - libcurl_la-curl_rtmp.lo libcurl_la-curl_sasl.lo \ - libcurl_la-curl_sspi.lo libcurl_la-curl_threads.lo \ - libcurl_la-dict.lo libcurl_la-doh.lo libcurl_la-dynbuf.lo \ - libcurl_la-easy.lo libcurl_la-easygetopt.lo \ - libcurl_la-easyoptions.lo libcurl_la-escape.lo \ - libcurl_la-file.lo libcurl_la-fileinfo.lo libcurl_la-fopen.lo \ - libcurl_la-formdata.lo libcurl_la-ftp.lo \ - libcurl_la-ftplistparser.lo libcurl_la-getenv.lo \ - libcurl_la-getinfo.lo libcurl_la-gopher.lo libcurl_la-h2h3.lo \ - libcurl_la-hash.lo libcurl_la-headers.lo libcurl_la-hmac.lo \ - libcurl_la-hostasyn.lo libcurl_la-hostip.lo \ - libcurl_la-hostip4.lo libcurl_la-hostip6.lo \ - libcurl_la-hostsyn.lo libcurl_la-hsts.lo libcurl_la-http.lo \ - libcurl_la-http2.lo libcurl_la-http_chunks.lo \ - libcurl_la-http_digest.lo libcurl_la-http_negotiate.lo \ - libcurl_la-http_ntlm.lo libcurl_la-http_proxy.lo \ - libcurl_la-http_aws_sigv4.lo libcurl_la-idn_win32.lo \ - libcurl_la-if2ip.lo libcurl_la-imap.lo libcurl_la-inet_ntop.lo \ - libcurl_la-inet_pton.lo libcurl_la-krb5.lo libcurl_la-ldap.lo \ - libcurl_la-llist.lo libcurl_la-md4.lo libcurl_la-md5.lo \ - libcurl_la-memdebug.lo libcurl_la-mime.lo \ - libcurl_la-mprintf.lo libcurl_la-mqtt.lo libcurl_la-multi.lo \ - libcurl_la-netrc.lo libcurl_la-nonblock.lo \ - libcurl_la-noproxy.lo libcurl_la-openldap.lo \ - libcurl_la-parsedate.lo libcurl_la-pingpong.lo \ - libcurl_la-pop3.lo libcurl_la-progress.lo libcurl_la-psl.lo \ - libcurl_la-rand.lo libcurl_la-rename.lo libcurl_la-rtsp.lo \ - libcurl_la-select.lo libcurl_la-sendf.lo libcurl_la-setopt.lo \ - libcurl_la-sha256.lo libcurl_la-share.lo libcurl_la-slist.lo \ - libcurl_la-smb.lo libcurl_la-smtp.lo libcurl_la-socketpair.lo \ - libcurl_la-socks.lo libcurl_la-socks_gssapi.lo \ - libcurl_la-socks_sspi.lo libcurl_la-speedcheck.lo \ - libcurl_la-splay.lo libcurl_la-strcase.lo libcurl_la-strdup.lo \ - libcurl_la-strerror.lo libcurl_la-strtok.lo \ - libcurl_la-strtoofft.lo libcurl_la-system_win32.lo \ - libcurl_la-telnet.lo libcurl_la-tftp.lo libcurl_la-timediff.lo \ - libcurl_la-timeval.lo libcurl_la-transfer.lo libcurl_la-url.lo \ - libcurl_la-urlapi.lo libcurl_la-version.lo \ - libcurl_la-version_win32.lo libcurl_la-warnless.lo \ - libcurl_la-wildcard.lo libcurl_la-ws.lo -am__dirstamp = $(am__leading_dot)dirstamp -am__objects_2 = vauth/libcurl_la-cleartext.lo vauth/libcurl_la-cram.lo \ - vauth/libcurl_la-digest.lo vauth/libcurl_la-digest_sspi.lo \ - vauth/libcurl_la-gsasl.lo vauth/libcurl_la-krb5_gssapi.lo \ - vauth/libcurl_la-krb5_sspi.lo vauth/libcurl_la-ntlm.lo \ - vauth/libcurl_la-ntlm_sspi.lo vauth/libcurl_la-oauth2.lo \ - vauth/libcurl_la-spnego_gssapi.lo \ - vauth/libcurl_la-spnego_sspi.lo vauth/libcurl_la-vauth.lo -am__objects_3 = vtls/libcurl_la-bearssl.lo vtls/libcurl_la-gskit.lo \ - vtls/libcurl_la-gtls.lo vtls/libcurl_la-hostcheck.lo \ - vtls/libcurl_la-keylog.lo vtls/libcurl_la-mbedtls.lo \ - vtls/libcurl_la-mbedtls_threadlock.lo vtls/libcurl_la-nss.lo \ - vtls/libcurl_la-openssl.lo vtls/libcurl_la-rustls.lo \ - vtls/libcurl_la-schannel.lo vtls/libcurl_la-schannel_verify.lo \ - vtls/libcurl_la-sectransp.lo vtls/libcurl_la-vtls.lo \ - vtls/libcurl_la-wolfssl.lo vtls/libcurl_la-x509asn1.lo -am__objects_4 = vquic/libcurl_la-msh3.lo vquic/libcurl_la-ngtcp2.lo \ - vquic/libcurl_la-quiche.lo vquic/libcurl_la-vquic.lo -am__objects_5 = vssh/libcurl_la-libssh.lo vssh/libcurl_la-libssh2.lo \ - vssh/libcurl_la-wolfssh.lo -am__objects_6 = $(am__objects_1) $(am__objects_2) $(am__objects_3) \ - $(am__objects_4) $(am__objects_5) -am__objects_7 = -am__objects_8 = $(am__objects_7) $(am__objects_7) $(am__objects_7) \ - $(am__objects_7) $(am__objects_7) -am__objects_9 = libcurl.lo -@OS_WINDOWS_TRUE@@USE_CPPFLAG_CURL_STATICLIB_FALSE@am__objects_10 = $(am__objects_9) -am_libcurl_la_OBJECTS = $(am__objects_6) $(am__objects_8) \ - $(am__objects_10) -libcurl_la_OBJECTS = $(am_libcurl_la_OBJECTS) -AM_V_lt = $(am__v_lt_@AM_V@) -am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) -am__v_lt_0 = --silent -am__v_lt_1 = -libcurl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libcurl_la_CFLAGS) \ - $(CFLAGS) $(libcurl_la_LDFLAGS) $(LDFLAGS) -o $@ -libcurlu_la_LIBADD = -am__objects_11 = libcurlu_la-altsvc.lo libcurlu_la-amigaos.lo \ - libcurlu_la-asyn-ares.lo libcurlu_la-asyn-thread.lo \ - libcurlu_la-base64.lo libcurlu_la-bufref.lo \ - libcurlu_la-c-hyper.lo libcurlu_la-conncache.lo \ - libcurlu_la-connect.lo libcurlu_la-content_encoding.lo \ - libcurlu_la-cookie.lo libcurlu_la-curl_addrinfo.lo \ - libcurlu_la-curl_des.lo libcurlu_la-curl_endian.lo \ - libcurlu_la-curl_fnmatch.lo libcurlu_la-curl_get_line.lo \ - libcurlu_la-curl_gethostname.lo libcurlu_la-curl_gssapi.lo \ - libcurlu_la-curl_memrchr.lo libcurlu_la-curl_multibyte.lo \ - libcurlu_la-curl_ntlm_core.lo libcurlu_la-curl_ntlm_wb.lo \ - libcurlu_la-curl_path.lo libcurlu_la-curl_range.lo \ - libcurlu_la-curl_rtmp.lo libcurlu_la-curl_sasl.lo \ - libcurlu_la-curl_sspi.lo libcurlu_la-curl_threads.lo \ - libcurlu_la-dict.lo libcurlu_la-doh.lo libcurlu_la-dynbuf.lo \ - libcurlu_la-easy.lo libcurlu_la-easygetopt.lo \ - libcurlu_la-easyoptions.lo libcurlu_la-escape.lo \ - libcurlu_la-file.lo libcurlu_la-fileinfo.lo \ - libcurlu_la-fopen.lo libcurlu_la-formdata.lo \ - libcurlu_la-ftp.lo libcurlu_la-ftplistparser.lo \ - libcurlu_la-getenv.lo libcurlu_la-getinfo.lo \ - libcurlu_la-gopher.lo libcurlu_la-h2h3.lo libcurlu_la-hash.lo \ - libcurlu_la-headers.lo libcurlu_la-hmac.lo \ - libcurlu_la-hostasyn.lo libcurlu_la-hostip.lo \ - libcurlu_la-hostip4.lo libcurlu_la-hostip6.lo \ - libcurlu_la-hostsyn.lo libcurlu_la-hsts.lo libcurlu_la-http.lo \ - libcurlu_la-http2.lo libcurlu_la-http_chunks.lo \ - libcurlu_la-http_digest.lo libcurlu_la-http_negotiate.lo \ - libcurlu_la-http_ntlm.lo libcurlu_la-http_proxy.lo \ - libcurlu_la-http_aws_sigv4.lo libcurlu_la-idn_win32.lo \ - libcurlu_la-if2ip.lo libcurlu_la-imap.lo \ - libcurlu_la-inet_ntop.lo libcurlu_la-inet_pton.lo \ - libcurlu_la-krb5.lo libcurlu_la-ldap.lo libcurlu_la-llist.lo \ - libcurlu_la-md4.lo libcurlu_la-md5.lo libcurlu_la-memdebug.lo \ - libcurlu_la-mime.lo libcurlu_la-mprintf.lo libcurlu_la-mqtt.lo \ - libcurlu_la-multi.lo libcurlu_la-netrc.lo \ - libcurlu_la-nonblock.lo libcurlu_la-noproxy.lo \ - libcurlu_la-openldap.lo libcurlu_la-parsedate.lo \ - libcurlu_la-pingpong.lo libcurlu_la-pop3.lo \ - libcurlu_la-progress.lo libcurlu_la-psl.lo libcurlu_la-rand.lo \ - libcurlu_la-rename.lo libcurlu_la-rtsp.lo \ - libcurlu_la-select.lo libcurlu_la-sendf.lo \ - libcurlu_la-setopt.lo libcurlu_la-sha256.lo \ - libcurlu_la-share.lo libcurlu_la-slist.lo libcurlu_la-smb.lo \ - libcurlu_la-smtp.lo libcurlu_la-socketpair.lo \ - libcurlu_la-socks.lo libcurlu_la-socks_gssapi.lo \ - libcurlu_la-socks_sspi.lo libcurlu_la-speedcheck.lo \ - libcurlu_la-splay.lo libcurlu_la-strcase.lo \ - libcurlu_la-strdup.lo libcurlu_la-strerror.lo \ - libcurlu_la-strtok.lo libcurlu_la-strtoofft.lo \ - libcurlu_la-system_win32.lo libcurlu_la-telnet.lo \ - libcurlu_la-tftp.lo libcurlu_la-timediff.lo \ - libcurlu_la-timeval.lo libcurlu_la-transfer.lo \ - libcurlu_la-url.lo libcurlu_la-urlapi.lo \ - libcurlu_la-version.lo libcurlu_la-version_win32.lo \ - libcurlu_la-warnless.lo libcurlu_la-wildcard.lo \ - libcurlu_la-ws.lo -am__objects_12 = vauth/libcurlu_la-cleartext.lo \ - vauth/libcurlu_la-cram.lo vauth/libcurlu_la-digest.lo \ - vauth/libcurlu_la-digest_sspi.lo vauth/libcurlu_la-gsasl.lo \ - vauth/libcurlu_la-krb5_gssapi.lo \ - vauth/libcurlu_la-krb5_sspi.lo vauth/libcurlu_la-ntlm.lo \ - vauth/libcurlu_la-ntlm_sspi.lo vauth/libcurlu_la-oauth2.lo \ - vauth/libcurlu_la-spnego_gssapi.lo \ - vauth/libcurlu_la-spnego_sspi.lo vauth/libcurlu_la-vauth.lo -am__objects_13 = vtls/libcurlu_la-bearssl.lo vtls/libcurlu_la-gskit.lo \ - vtls/libcurlu_la-gtls.lo vtls/libcurlu_la-hostcheck.lo \ - vtls/libcurlu_la-keylog.lo vtls/libcurlu_la-mbedtls.lo \ - vtls/libcurlu_la-mbedtls_threadlock.lo vtls/libcurlu_la-nss.lo \ - vtls/libcurlu_la-openssl.lo vtls/libcurlu_la-rustls.lo \ - vtls/libcurlu_la-schannel.lo \ - vtls/libcurlu_la-schannel_verify.lo \ - vtls/libcurlu_la-sectransp.lo vtls/libcurlu_la-vtls.lo \ - vtls/libcurlu_la-wolfssl.lo vtls/libcurlu_la-x509asn1.lo -am__objects_14 = vquic/libcurlu_la-msh3.lo vquic/libcurlu_la-ngtcp2.lo \ - vquic/libcurlu_la-quiche.lo vquic/libcurlu_la-vquic.lo -am__objects_15 = vssh/libcurlu_la-libssh.lo \ - vssh/libcurlu_la-libssh2.lo vssh/libcurlu_la-wolfssh.lo -am__objects_16 = $(am__objects_11) $(am__objects_12) $(am__objects_13) \ - $(am__objects_14) $(am__objects_15) -am_libcurlu_la_OBJECTS = $(am__objects_16) $(am__objects_8) -libcurlu_la_OBJECTS = $(am_libcurlu_la_OBJECTS) -libcurlu_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libcurlu_la_CFLAGS) \ - $(CFLAGS) $(libcurlu_la_LDFLAGS) $(LDFLAGS) -o $@ -@BUILD_UNITTESTS_TRUE@am_libcurlu_la_rpath = -AM_V_P = $(am__v_P_@AM_V@) -am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -am__v_P_0 = false -am__v_P_1 = : -AM_V_GEN = $(am__v_GEN_@AM_V@) -am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -am__v_GEN_0 = @echo " GEN " $@; -am__v_GEN_1 = -AM_V_at = $(am__v_at_@AM_V@) -am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -am__v_at_0 = @ -am__v_at_1 = -DEFAULT_INCLUDES = -depcomp = $(SHELL) $(top_srcdir)/depcomp -am__maybe_remake_depfiles = depfiles -am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \ - ./$(DEPDIR)/libcurl_la-amigaos.Plo \ - ./$(DEPDIR)/libcurl_la-asyn-ares.Plo \ - ./$(DEPDIR)/libcurl_la-asyn-thread.Plo \ - ./$(DEPDIR)/libcurl_la-base64.Plo \ - ./$(DEPDIR)/libcurl_la-bufref.Plo \ - ./$(DEPDIR)/libcurl_la-c-hyper.Plo \ - ./$(DEPDIR)/libcurl_la-conncache.Plo \ - ./$(DEPDIR)/libcurl_la-connect.Plo \ - ./$(DEPDIR)/libcurl_la-content_encoding.Plo \ - ./$(DEPDIR)/libcurl_la-cookie.Plo \ - ./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo \ - ./$(DEPDIR)/libcurl_la-curl_des.Plo \ - ./$(DEPDIR)/libcurl_la-curl_endian.Plo \ - ./$(DEPDIR)/libcurl_la-curl_fnmatch.Plo \ - ./$(DEPDIR)/libcurl_la-curl_get_line.Plo \ - ./$(DEPDIR)/libcurl_la-curl_gethostname.Plo \ - ./$(DEPDIR)/libcurl_la-curl_gssapi.Plo \ - ./$(DEPDIR)/libcurl_la-curl_memrchr.Plo \ - ./$(DEPDIR)/libcurl_la-curl_multibyte.Plo \ - ./$(DEPDIR)/libcurl_la-curl_ntlm_core.Plo \ - ./$(DEPDIR)/libcurl_la-curl_ntlm_wb.Plo \ - ./$(DEPDIR)/libcurl_la-curl_path.Plo \ - ./$(DEPDIR)/libcurl_la-curl_range.Plo \ - ./$(DEPDIR)/libcurl_la-curl_rtmp.Plo \ - ./$(DEPDIR)/libcurl_la-curl_sasl.Plo \ - ./$(DEPDIR)/libcurl_la-curl_sspi.Plo \ - ./$(DEPDIR)/libcurl_la-curl_threads.Plo \ - ./$(DEPDIR)/libcurl_la-dict.Plo ./$(DEPDIR)/libcurl_la-doh.Plo \ - ./$(DEPDIR)/libcurl_la-dynbuf.Plo \ - ./$(DEPDIR)/libcurl_la-easy.Plo \ - ./$(DEPDIR)/libcurl_la-easygetopt.Plo \ - ./$(DEPDIR)/libcurl_la-easyoptions.Plo \ - ./$(DEPDIR)/libcurl_la-escape.Plo \ - ./$(DEPDIR)/libcurl_la-file.Plo \ - ./$(DEPDIR)/libcurl_la-fileinfo.Plo \ - ./$(DEPDIR)/libcurl_la-fopen.Plo \ - ./$(DEPDIR)/libcurl_la-formdata.Plo \ - ./$(DEPDIR)/libcurl_la-ftp.Plo \ - ./$(DEPDIR)/libcurl_la-ftplistparser.Plo \ - ./$(DEPDIR)/libcurl_la-getenv.Plo \ - ./$(DEPDIR)/libcurl_la-getinfo.Plo \ - ./$(DEPDIR)/libcurl_la-gopher.Plo \ - ./$(DEPDIR)/libcurl_la-h2h3.Plo \ - ./$(DEPDIR)/libcurl_la-hash.Plo \ - ./$(DEPDIR)/libcurl_la-headers.Plo \ - ./$(DEPDIR)/libcurl_la-hmac.Plo \ - ./$(DEPDIR)/libcurl_la-hostasyn.Plo \ - ./$(DEPDIR)/libcurl_la-hostip.Plo \ - ./$(DEPDIR)/libcurl_la-hostip4.Plo \ - ./$(DEPDIR)/libcurl_la-hostip6.Plo \ - ./$(DEPDIR)/libcurl_la-hostsyn.Plo \ - ./$(DEPDIR)/libcurl_la-hsts.Plo \ - ./$(DEPDIR)/libcurl_la-http.Plo \ - ./$(DEPDIR)/libcurl_la-http2.Plo \ - ./$(DEPDIR)/libcurl_la-http_aws_sigv4.Plo \ - ./$(DEPDIR)/libcurl_la-http_chunks.Plo \ - ./$(DEPDIR)/libcurl_la-http_digest.Plo \ - ./$(DEPDIR)/libcurl_la-http_negotiate.Plo \ - ./$(DEPDIR)/libcurl_la-http_ntlm.Plo \ - ./$(DEPDIR)/libcurl_la-http_proxy.Plo \ - ./$(DEPDIR)/libcurl_la-idn_win32.Plo \ - ./$(DEPDIR)/libcurl_la-if2ip.Plo \ - ./$(DEPDIR)/libcurl_la-imap.Plo \ - ./$(DEPDIR)/libcurl_la-inet_ntop.Plo \ - ./$(DEPDIR)/libcurl_la-inet_pton.Plo \ - ./$(DEPDIR)/libcurl_la-krb5.Plo \ - ./$(DEPDIR)/libcurl_la-ldap.Plo \ - ./$(DEPDIR)/libcurl_la-llist.Plo \ - ./$(DEPDIR)/libcurl_la-md4.Plo ./$(DEPDIR)/libcurl_la-md5.Plo \ - ./$(DEPDIR)/libcurl_la-memdebug.Plo \ - ./$(DEPDIR)/libcurl_la-mime.Plo \ - ./$(DEPDIR)/libcurl_la-mprintf.Plo \ - ./$(DEPDIR)/libcurl_la-mqtt.Plo \ - ./$(DEPDIR)/libcurl_la-multi.Plo \ - ./$(DEPDIR)/libcurl_la-netrc.Plo \ - ./$(DEPDIR)/libcurl_la-nonblock.Plo \ - ./$(DEPDIR)/libcurl_la-noproxy.Plo \ - ./$(DEPDIR)/libcurl_la-openldap.Plo \ - ./$(DEPDIR)/libcurl_la-parsedate.Plo \ - ./$(DEPDIR)/libcurl_la-pingpong.Plo \ - ./$(DEPDIR)/libcurl_la-pop3.Plo \ - ./$(DEPDIR)/libcurl_la-progress.Plo \ - ./$(DEPDIR)/libcurl_la-psl.Plo ./$(DEPDIR)/libcurl_la-rand.Plo \ - ./$(DEPDIR)/libcurl_la-rename.Plo \ - ./$(DEPDIR)/libcurl_la-rtsp.Plo \ - ./$(DEPDIR)/libcurl_la-select.Plo \ - ./$(DEPDIR)/libcurl_la-sendf.Plo \ - ./$(DEPDIR)/libcurl_la-setopt.Plo \ - ./$(DEPDIR)/libcurl_la-sha256.Plo \ - ./$(DEPDIR)/libcurl_la-share.Plo \ - ./$(DEPDIR)/libcurl_la-slist.Plo \ - ./$(DEPDIR)/libcurl_la-smb.Plo ./$(DEPDIR)/libcurl_la-smtp.Plo \ - ./$(DEPDIR)/libcurl_la-socketpair.Plo \ - ./$(DEPDIR)/libcurl_la-socks.Plo \ - ./$(DEPDIR)/libcurl_la-socks_gssapi.Plo \ - ./$(DEPDIR)/libcurl_la-socks_sspi.Plo \ - ./$(DEPDIR)/libcurl_la-speedcheck.Plo \ - ./$(DEPDIR)/libcurl_la-splay.Plo \ - ./$(DEPDIR)/libcurl_la-strcase.Plo \ - ./$(DEPDIR)/libcurl_la-strdup.Plo \ - ./$(DEPDIR)/libcurl_la-strerror.Plo \ - ./$(DEPDIR)/libcurl_la-strtok.Plo \ - ./$(DEPDIR)/libcurl_la-strtoofft.Plo \ - ./$(DEPDIR)/libcurl_la-system_win32.Plo \ - ./$(DEPDIR)/libcurl_la-telnet.Plo \ - ./$(DEPDIR)/libcurl_la-tftp.Plo \ - ./$(DEPDIR)/libcurl_la-timediff.Plo \ - ./$(DEPDIR)/libcurl_la-timeval.Plo \ - ./$(DEPDIR)/libcurl_la-transfer.Plo \ - ./$(DEPDIR)/libcurl_la-url.Plo \ - ./$(DEPDIR)/libcurl_la-urlapi.Plo \ - ./$(DEPDIR)/libcurl_la-version.Plo \ - ./$(DEPDIR)/libcurl_la-version_win32.Plo \ - ./$(DEPDIR)/libcurl_la-warnless.Plo \ - ./$(DEPDIR)/libcurl_la-wildcard.Plo \ - ./$(DEPDIR)/libcurl_la-ws.Plo \ - ./$(DEPDIR)/libcurlu_la-altsvc.Plo \ - ./$(DEPDIR)/libcurlu_la-amigaos.Plo \ - ./$(DEPDIR)/libcurlu_la-asyn-ares.Plo \ - ./$(DEPDIR)/libcurlu_la-asyn-thread.Plo \ - ./$(DEPDIR)/libcurlu_la-base64.Plo \ - ./$(DEPDIR)/libcurlu_la-bufref.Plo \ - ./$(DEPDIR)/libcurlu_la-c-hyper.Plo \ - ./$(DEPDIR)/libcurlu_la-conncache.Plo \ - ./$(DEPDIR)/libcurlu_la-connect.Plo \ - ./$(DEPDIR)/libcurlu_la-content_encoding.Plo \ - ./$(DEPDIR)/libcurlu_la-cookie.Plo \ - ./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo \ - ./$(DEPDIR)/libcurlu_la-curl_des.Plo \ - ./$(DEPDIR)/libcurlu_la-curl_endian.Plo \ - ./$(DEPDIR)/libcurlu_la-curl_fnmatch.Plo \ - ./$(DEPDIR)/libcurlu_la-curl_get_line.Plo \ - ./$(DEPDIR)/libcurlu_la-curl_gethostname.Plo \ - ./$(DEPDIR)/libcurlu_la-curl_gssapi.Plo \ - ./$(DEPDIR)/libcurlu_la-curl_memrchr.Plo \ - ./$(DEPDIR)/libcurlu_la-curl_multibyte.Plo \ - ./$(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo \ - ./$(DEPDIR)/libcurlu_la-curl_ntlm_wb.Plo \ - ./$(DEPDIR)/libcurlu_la-curl_path.Plo \ - ./$(DEPDIR)/libcurlu_la-curl_range.Plo \ - ./$(DEPDIR)/libcurlu_la-curl_rtmp.Plo \ - ./$(DEPDIR)/libcurlu_la-curl_sasl.Plo \ - ./$(DEPDIR)/libcurlu_la-curl_sspi.Plo \ - ./$(DEPDIR)/libcurlu_la-curl_threads.Plo \ - ./$(DEPDIR)/libcurlu_la-dict.Plo \ - ./$(DEPDIR)/libcurlu_la-doh.Plo \ - ./$(DEPDIR)/libcurlu_la-dynbuf.Plo \ - ./$(DEPDIR)/libcurlu_la-easy.Plo \ - ./$(DEPDIR)/libcurlu_la-easygetopt.Plo \ - ./$(DEPDIR)/libcurlu_la-easyoptions.Plo \ - ./$(DEPDIR)/libcurlu_la-escape.Plo \ - ./$(DEPDIR)/libcurlu_la-file.Plo \ - ./$(DEPDIR)/libcurlu_la-fileinfo.Plo \ - ./$(DEPDIR)/libcurlu_la-fopen.Plo \ - ./$(DEPDIR)/libcurlu_la-formdata.Plo \ - ./$(DEPDIR)/libcurlu_la-ftp.Plo \ - ./$(DEPDIR)/libcurlu_la-ftplistparser.Plo \ - ./$(DEPDIR)/libcurlu_la-getenv.Plo \ - ./$(DEPDIR)/libcurlu_la-getinfo.Plo \ - ./$(DEPDIR)/libcurlu_la-gopher.Plo \ - ./$(DEPDIR)/libcurlu_la-h2h3.Plo \ - ./$(DEPDIR)/libcurlu_la-hash.Plo \ - ./$(DEPDIR)/libcurlu_la-headers.Plo \ - ./$(DEPDIR)/libcurlu_la-hmac.Plo \ - ./$(DEPDIR)/libcurlu_la-hostasyn.Plo \ - ./$(DEPDIR)/libcurlu_la-hostip.Plo \ - ./$(DEPDIR)/libcurlu_la-hostip4.Plo \ - ./$(DEPDIR)/libcurlu_la-hostip6.Plo \ - ./$(DEPDIR)/libcurlu_la-hostsyn.Plo \ - ./$(DEPDIR)/libcurlu_la-hsts.Plo \ - ./$(DEPDIR)/libcurlu_la-http.Plo \ - ./$(DEPDIR)/libcurlu_la-http2.Plo \ - ./$(DEPDIR)/libcurlu_la-http_aws_sigv4.Plo \ - ./$(DEPDIR)/libcurlu_la-http_chunks.Plo \ - ./$(DEPDIR)/libcurlu_la-http_digest.Plo \ - ./$(DEPDIR)/libcurlu_la-http_negotiate.Plo \ - ./$(DEPDIR)/libcurlu_la-http_ntlm.Plo \ - ./$(DEPDIR)/libcurlu_la-http_proxy.Plo \ - ./$(DEPDIR)/libcurlu_la-idn_win32.Plo \ - ./$(DEPDIR)/libcurlu_la-if2ip.Plo \ - ./$(DEPDIR)/libcurlu_la-imap.Plo \ - ./$(DEPDIR)/libcurlu_la-inet_ntop.Plo \ - ./$(DEPDIR)/libcurlu_la-inet_pton.Plo \ - ./$(DEPDIR)/libcurlu_la-krb5.Plo \ - ./$(DEPDIR)/libcurlu_la-ldap.Plo \ - ./$(DEPDIR)/libcurlu_la-llist.Plo \ - ./$(DEPDIR)/libcurlu_la-md4.Plo \ - ./$(DEPDIR)/libcurlu_la-md5.Plo \ - ./$(DEPDIR)/libcurlu_la-memdebug.Plo \ - ./$(DEPDIR)/libcurlu_la-mime.Plo \ - ./$(DEPDIR)/libcurlu_la-mprintf.Plo \ - ./$(DEPDIR)/libcurlu_la-mqtt.Plo \ - ./$(DEPDIR)/libcurlu_la-multi.Plo \ - ./$(DEPDIR)/libcurlu_la-netrc.Plo \ - ./$(DEPDIR)/libcurlu_la-nonblock.Plo \ - ./$(DEPDIR)/libcurlu_la-noproxy.Plo \ - ./$(DEPDIR)/libcurlu_la-openldap.Plo \ - ./$(DEPDIR)/libcurlu_la-parsedate.Plo \ - ./$(DEPDIR)/libcurlu_la-pingpong.Plo \ - ./$(DEPDIR)/libcurlu_la-pop3.Plo \ - ./$(DEPDIR)/libcurlu_la-progress.Plo \ - ./$(DEPDIR)/libcurlu_la-psl.Plo \ - ./$(DEPDIR)/libcurlu_la-rand.Plo \ - ./$(DEPDIR)/libcurlu_la-rename.Plo \ - ./$(DEPDIR)/libcurlu_la-rtsp.Plo \ - ./$(DEPDIR)/libcurlu_la-select.Plo \ - ./$(DEPDIR)/libcurlu_la-sendf.Plo \ - ./$(DEPDIR)/libcurlu_la-setopt.Plo \ - ./$(DEPDIR)/libcurlu_la-sha256.Plo \ - ./$(DEPDIR)/libcurlu_la-share.Plo \ - ./$(DEPDIR)/libcurlu_la-slist.Plo \ - ./$(DEPDIR)/libcurlu_la-smb.Plo \ - ./$(DEPDIR)/libcurlu_la-smtp.Plo \ - ./$(DEPDIR)/libcurlu_la-socketpair.Plo \ - ./$(DEPDIR)/libcurlu_la-socks.Plo \ - ./$(DEPDIR)/libcurlu_la-socks_gssapi.Plo \ - ./$(DEPDIR)/libcurlu_la-socks_sspi.Plo \ - ./$(DEPDIR)/libcurlu_la-speedcheck.Plo \ - ./$(DEPDIR)/libcurlu_la-splay.Plo \ - ./$(DEPDIR)/libcurlu_la-strcase.Plo \ - ./$(DEPDIR)/libcurlu_la-strdup.Plo \ - ./$(DEPDIR)/libcurlu_la-strerror.Plo \ - ./$(DEPDIR)/libcurlu_la-strtok.Plo \ - ./$(DEPDIR)/libcurlu_la-strtoofft.Plo \ - ./$(DEPDIR)/libcurlu_la-system_win32.Plo \ - ./$(DEPDIR)/libcurlu_la-telnet.Plo \ - ./$(DEPDIR)/libcurlu_la-tftp.Plo \ - ./$(DEPDIR)/libcurlu_la-timediff.Plo \ - ./$(DEPDIR)/libcurlu_la-timeval.Plo \ - ./$(DEPDIR)/libcurlu_la-transfer.Plo \ - ./$(DEPDIR)/libcurlu_la-url.Plo \ - ./$(DEPDIR)/libcurlu_la-urlapi.Plo \ - ./$(DEPDIR)/libcurlu_la-version.Plo \ - ./$(DEPDIR)/libcurlu_la-version_win32.Plo \ - ./$(DEPDIR)/libcurlu_la-warnless.Plo \ - ./$(DEPDIR)/libcurlu_la-wildcard.Plo \ - ./$(DEPDIR)/libcurlu_la-ws.Plo \ - vauth/$(DEPDIR)/libcurl_la-cleartext.Plo \ - vauth/$(DEPDIR)/libcurl_la-cram.Plo \ - vauth/$(DEPDIR)/libcurl_la-digest.Plo \ - vauth/$(DEPDIR)/libcurl_la-digest_sspi.Plo \ - vauth/$(DEPDIR)/libcurl_la-gsasl.Plo \ - vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Plo \ - vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Plo \ - vauth/$(DEPDIR)/libcurl_la-ntlm.Plo \ - vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Plo \ - vauth/$(DEPDIR)/libcurl_la-oauth2.Plo \ - vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Plo \ - vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Plo \ - vauth/$(DEPDIR)/libcurl_la-vauth.Plo \ - vauth/$(DEPDIR)/libcurlu_la-cleartext.Plo \ - vauth/$(DEPDIR)/libcurlu_la-cram.Plo \ - vauth/$(DEPDIR)/libcurlu_la-digest.Plo \ - vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Plo \ - vauth/$(DEPDIR)/libcurlu_la-gsasl.Plo \ - vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Plo \ - vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Plo \ - vauth/$(DEPDIR)/libcurlu_la-ntlm.Plo \ - vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Plo \ - vauth/$(DEPDIR)/libcurlu_la-oauth2.Plo \ - vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Plo \ - vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Plo \ - vauth/$(DEPDIR)/libcurlu_la-vauth.Plo \ - vquic/$(DEPDIR)/libcurl_la-msh3.Plo \ - vquic/$(DEPDIR)/libcurl_la-ngtcp2.Plo \ - vquic/$(DEPDIR)/libcurl_la-quiche.Plo \ - vquic/$(DEPDIR)/libcurl_la-vquic.Plo \ - vquic/$(DEPDIR)/libcurlu_la-msh3.Plo \ - vquic/$(DEPDIR)/libcurlu_la-ngtcp2.Plo \ - vquic/$(DEPDIR)/libcurlu_la-quiche.Plo \ - vquic/$(DEPDIR)/libcurlu_la-vquic.Plo \ - vssh/$(DEPDIR)/libcurl_la-libssh.Plo \ - vssh/$(DEPDIR)/libcurl_la-libssh2.Plo \ - vssh/$(DEPDIR)/libcurl_la-wolfssh.Plo \ - vssh/$(DEPDIR)/libcurlu_la-libssh.Plo \ - vssh/$(DEPDIR)/libcurlu_la-libssh2.Plo \ - vssh/$(DEPDIR)/libcurlu_la-wolfssh.Plo \ - vtls/$(DEPDIR)/libcurl_la-bearssl.Plo \ - vtls/$(DEPDIR)/libcurl_la-gskit.Plo \ - vtls/$(DEPDIR)/libcurl_la-gtls.Plo \ - vtls/$(DEPDIR)/libcurl_la-hostcheck.Plo \ - vtls/$(DEPDIR)/libcurl_la-keylog.Plo \ - vtls/$(DEPDIR)/libcurl_la-mbedtls.Plo \ - vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Plo \ - vtls/$(DEPDIR)/libcurl_la-nss.Plo \ - vtls/$(DEPDIR)/libcurl_la-openssl.Plo \ - vtls/$(DEPDIR)/libcurl_la-rustls.Plo \ - vtls/$(DEPDIR)/libcurl_la-schannel.Plo \ - vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo \ - vtls/$(DEPDIR)/libcurl_la-sectransp.Plo \ - vtls/$(DEPDIR)/libcurl_la-vtls.Plo \ - vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo \ - vtls/$(DEPDIR)/libcurl_la-x509asn1.Plo \ - vtls/$(DEPDIR)/libcurlu_la-bearssl.Plo \ - vtls/$(DEPDIR)/libcurlu_la-gskit.Plo \ - vtls/$(DEPDIR)/libcurlu_la-gtls.Plo \ - vtls/$(DEPDIR)/libcurlu_la-hostcheck.Plo \ - vtls/$(DEPDIR)/libcurlu_la-keylog.Plo \ - vtls/$(DEPDIR)/libcurlu_la-mbedtls.Plo \ - vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Plo \ - vtls/$(DEPDIR)/libcurlu_la-nss.Plo \ - vtls/$(DEPDIR)/libcurlu_la-openssl.Plo \ - vtls/$(DEPDIR)/libcurlu_la-rustls.Plo \ - vtls/$(DEPDIR)/libcurlu_la-schannel.Plo \ - vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo \ - vtls/$(DEPDIR)/libcurlu_la-sectransp.Plo \ - vtls/$(DEPDIR)/libcurlu_la-vtls.Plo \ - vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo \ - vtls/$(DEPDIR)/libcurlu_la-x509asn1.Plo -am__mv = mv -f -COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ - $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ - $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ - $(AM_CFLAGS) $(CFLAGS) -AM_V_CC = $(am__v_CC_@AM_V@) -am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) -am__v_CC_0 = @echo " CC " $@; -am__v_CC_1 = -CCLD = $(CC) -LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ - $(AM_LDFLAGS) $(LDFLAGS) -o $@ -AM_V_CCLD = $(am__v_CCLD_@AM_V@) -am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) -am__v_CCLD_0 = @echo " CCLD " $@; -am__v_CCLD_1 = -SOURCES = $(libcurl_la_SOURCES) $(libcurlu_la_SOURCES) -DIST_SOURCES = $(am__libcurl_la_SOURCES_DIST) $(libcurlu_la_SOURCES) -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) \ - curl_config.h.in -# Read a list of newline-separated strings from the standard input, -# and print each of them once, without duplicates. Input order is -# *not* preserved. -am__uniquify_input = $(AWK) '\ - BEGIN { nonempty = 0; } \ - { items[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in items) print i; }; } \ -' -# Make sure the list of sources is unique. This is necessary because, -# e.g., the same source file might be shared among _SOURCES variables -# for different programs/libraries. -am__define_uniq_tagged_files = \ - list='$(am__tagged_files)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | $(am__uniquify_input)` -am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.inc \ - $(srcdir)/curl_config.h.in $(srcdir)/libcurl.plist.in \ - $(srcdir)/libcurl.vers.in $(top_srcdir)/depcomp -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = @ACLOCAL@ -AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ -AR = @AR@ -AR_FLAGS = @AR_FLAGS@ -AS = @AS@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -BLANK_AT_MAKETIME = @BLANK_AT_MAKETIME@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ - -# This might hold -Werror -CFLAGS = @CFLAGS@ @CURL_CFLAG_EXTRAS@ -CFLAG_CURL_SYMBOL_HIDING = @CFLAG_CURL_SYMBOL_HIDING@ -CONFIGURE_OPTIONS = @CONFIGURE_OPTIONS@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CPPFLAG_CURL_STATICLIB = @CPPFLAG_CURL_STATICLIB@ -CSCOPE = @CSCOPE@ -CTAGS = @CTAGS@ -CURLVERSION = @CURLVERSION@ -CURL_CA_BUNDLE = @CURL_CA_BUNDLE@ -CURL_CFLAG_EXTRAS = @CURL_CFLAG_EXTRAS@ -CURL_DISABLE_DICT = @CURL_DISABLE_DICT@ -CURL_DISABLE_FILE = @CURL_DISABLE_FILE@ -CURL_DISABLE_FTP = @CURL_DISABLE_FTP@ -CURL_DISABLE_GOPHER = @CURL_DISABLE_GOPHER@ -CURL_DISABLE_HTTP = @CURL_DISABLE_HTTP@ -CURL_DISABLE_IMAP = @CURL_DISABLE_IMAP@ -CURL_DISABLE_LDAP = @CURL_DISABLE_LDAP@ -CURL_DISABLE_LDAPS = @CURL_DISABLE_LDAPS@ -CURL_DISABLE_MQTT = @CURL_DISABLE_MQTT@ -CURL_DISABLE_POP3 = @CURL_DISABLE_POP3@ -CURL_DISABLE_PROXY = @CURL_DISABLE_PROXY@ -CURL_DISABLE_RTSP = @CURL_DISABLE_RTSP@ -CURL_DISABLE_SMB = @CURL_DISABLE_SMB@ -CURL_DISABLE_SMTP = @CURL_DISABLE_SMTP@ -CURL_DISABLE_TELNET = @CURL_DISABLE_TELNET@ -CURL_DISABLE_TFTP = @CURL_DISABLE_TFTP@ -CURL_LT_SHLIB_VERSIONED_FLAVOUR = @CURL_LT_SHLIB_VERSIONED_FLAVOUR@ -CURL_NETWORK_AND_TIME_LIBS = @CURL_NETWORK_AND_TIME_LIBS@ -CURL_NETWORK_LIBS = @CURL_NETWORK_LIBS@ -CURL_PLIST_VERSION = @CURL_PLIST_VERSION@ -CURL_WITH_MULTI_SSL = @CURL_WITH_MULTI_SSL@ -CYGPATH_W = @CYGPATH_W@ -DEFAULT_SSL_BACKEND = @DEFAULT_SSL_BACKEND@ -DEFS = @DEFS@ -DEPDIR = @DEPDIR@ -DLLTOOL = @DLLTOOL@ -DSYMUTIL = @DSYMUTIL@ -DUMPBIN = @DUMPBIN@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -ENABLE_SHARED = @ENABLE_SHARED@ -ENABLE_STATIC = @ENABLE_STATIC@ -ETAGS = @ETAGS@ -EXEEXT = @EXEEXT@ -FGREP = @FGREP@ -FILECMD = @FILECMD@ -FISH_FUNCTIONS_DIR = @FISH_FUNCTIONS_DIR@ -GCOV = @GCOV@ -GREP = @GREP@ -HAVE_BROTLI = @HAVE_BROTLI@ -HAVE_GNUTLS_SRP = @HAVE_GNUTLS_SRP@ -HAVE_LDAP_SSL = @HAVE_LDAP_SSL@ -HAVE_LIBZ = @HAVE_LIBZ@ -HAVE_OPENSSL_SRP = @HAVE_OPENSSL_SRP@ -HAVE_PROTO_BSDSOCKET_H = @HAVE_PROTO_BSDSOCKET_H@ -HAVE_ZSTD = @HAVE_ZSTD@ -IDN_ENABLED = @IDN_ENABLED@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -IPV6_ENABLED = @IPV6_ENABLED@ -LCOV = @LCOV@ -LD = @LD@ -LDFLAGS = @LDFLAGS@ -LIBCURL_LIBS = @LIBCURL_LIBS@ -LIBCURL_NO_SHARED = @LIBCURL_NO_SHARED@ -LIBOBJS = @LIBOBJS@ - -# Prevent LIBS from being used for all link targets -LIBS = $(BLANK_AT_MAKETIME) -LIBTOOL = @LIBTOOL@ -LIPO = @LIPO@ -LN_S = @LN_S@ -LTLIBOBJS = @LTLIBOBJS@ -LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ -MAINT = @MAINT@ -MAKEINFO = @MAKEINFO@ -MANIFEST_TOOL = @MANIFEST_TOOL@ -MANOPT = @MANOPT@ -MKDIR_P = @MKDIR_P@ -NM = @NM@ -NMEDIT = @NMEDIT@ -NROFF = @NROFF@ -NSS_LIBS = @NSS_LIBS@ -OBJDUMP = @OBJDUMP@ -OBJEXT = @OBJEXT@ -OTOOL = @OTOOL@ -OTOOL64 = @OTOOL64@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_URL = @PACKAGE_URL@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -PERL = @PERL@ -PKGADD_NAME = @PKGADD_NAME@ -PKGADD_PKG = @PKGADD_PKG@ -PKGADD_VENDOR = @PKGADD_VENDOR@ -PKGCONFIG = @PKGCONFIG@ -RANDOM_FILE = @RANDOM_FILE@ -RANLIB = @RANLIB@ -RC = @RC@ -REQUIRE_LIB_DEPS = @REQUIRE_LIB_DEPS@ -SED = @SED@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -SSL_BACKENDS = @SSL_BACKENDS@ -SSL_ENABLED = @SSL_ENABLED@ -SSL_LIBS = @SSL_LIBS@ -STRIP = @STRIP@ -SUPPORT_FEATURES = @SUPPORT_FEATURES@ -SUPPORT_PROTOCOLS = @SUPPORT_PROTOCOLS@ -USE_ARES = @USE_ARES@ -USE_BEARSSL = @USE_BEARSSL@ -USE_GNUTLS = @USE_GNUTLS@ -USE_HYPER = @USE_HYPER@ -USE_LIBRTMP = @USE_LIBRTMP@ -USE_LIBSSH = @USE_LIBSSH@ -USE_LIBSSH2 = @USE_LIBSSH2@ -USE_MBEDTLS = @USE_MBEDTLS@ -USE_MSH3 = @USE_MSH3@ -USE_NGHTTP2 = @USE_NGHTTP2@ -USE_NGHTTP3 = @USE_NGHTTP3@ -USE_NGTCP2 = @USE_NGTCP2@ -USE_NGTCP2_CRYPTO_GNUTLS = @USE_NGTCP2_CRYPTO_GNUTLS@ -USE_NGTCP2_CRYPTO_OPENSSL = @USE_NGTCP2_CRYPTO_OPENSSL@ -USE_NGTCP2_CRYPTO_WOLFSSL = @USE_NGTCP2_CRYPTO_WOLFSSL@ -USE_NSS = @USE_NSS@ -USE_OPENLDAP = @USE_OPENLDAP@ -USE_QUICHE = @USE_QUICHE@ -USE_RUSTLS = @USE_RUSTLS@ -USE_SCHANNEL = @USE_SCHANNEL@ -USE_SECTRANSP = @USE_SECTRANSP@ -USE_UNIX_SOCKETS = @USE_UNIX_SOCKETS@ -USE_WIN32_CRYPTO = @USE_WIN32_CRYPTO@ -USE_WIN32_LARGE_FILES = @USE_WIN32_LARGE_FILES@ -USE_WIN32_SMALL_FILES = @USE_WIN32_SMALL_FILES@ -USE_WINDOWS_SSPI = @USE_WINDOWS_SSPI@ -USE_WOLFSSH = @USE_WOLFSSH@ -USE_WOLFSSL = @USE_WOLFSSL@ -VERSION = @VERSION@ -VERSIONNUM = @VERSIONNUM@ -ZLIB_LIBS = @ZLIB_LIBS@ -ZSH_FUNCTIONS_DIR = @ZSH_FUNCTIONS_DIR@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_AR = @ac_ct_AR@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -dvidir = @dvidir@ -exec_prefix = @exec_prefix@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -libext = @libext@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -runstatedir = @runstatedir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -sysconfdir = @sysconfdir@ -target_alias = @target_alias@ -top_build_prefix = @top_build_prefix@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ - -#*************************************************************************** -# _ _ ____ _ -# Project ___| | | | _ \| | -# / __| | | | |_) | | -# | (__| |_| | _ <| |___ -# \___|\___/|_| \_\_____| -# -# Copyright (C) 1998 - 2022, 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 -CMAKE_DIST = CMakeLists.txt curl_config.h.cmake -EXTRA_DIST = Makefile.m32 config-win32.h config-win32ce.h config-plan9.h \ - config-riscos.h config-mac.h curl_config.h.in makefile.dj config-dos.h \ - libcurl.plist libcurl.rc config-amigaos.h makefile.amiga config-win32ce.h \ - config-os400.h setup-os400.h $(CMAKE_DIST) setup-win32.h .checksrc - -lib_LTLIBRARIES = libcurl.la -@BUILD_UNITTESTS_FALSE@noinst_LTLIBRARIES = -@BUILD_UNITTESTS_TRUE@noinst_LTLIBRARIES = libcurlu.la - -# 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 -# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "private" files -# 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 -AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/lib \ - -I$(top_srcdir)/lib -DBUILDING_LIBCURL -VERSIONINFO = -version-info 12:0:8 -AM_LDFLAGS = -AM_CFLAGS = -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/ntlm.h \ - vauth/vauth.h - -LIB_VTLS_CFILES = \ - vtls/bearssl.c \ - vtls/gskit.c \ - vtls/gtls.c \ - vtls/hostcheck.c \ - vtls/keylog.c \ - vtls/mbedtls.c \ - vtls/mbedtls_threadlock.c \ - vtls/nss.c \ - vtls/openssl.c \ - vtls/rustls.c \ - vtls/schannel.c \ - vtls/schannel_verify.c \ - vtls/sectransp.c \ - vtls/vtls.c \ - vtls/wolfssl.c \ - vtls/x509asn1.c - -LIB_VTLS_HFILES = \ - vtls/bearssl.h \ - vtls/gskit.h \ - vtls/gtls.h \ - vtls/hostcheck.h \ - vtls/keylog.h \ - vtls/mbedtls.h \ - vtls/mbedtls_threadlock.h \ - vtls/nssg.h \ - vtls/openssl.h \ - vtls/rustls.h \ - vtls/schannel.h \ - vtls/sectransp.h \ - vtls/vtls.h \ - vtls/wolfssl.h \ - vtls/x509asn1.h - -LIB_VQUIC_CFILES = \ - vquic/msh3.c \ - vquic/ngtcp2.c \ - vquic/quiche.c \ - vquic/vquic.c - -LIB_VQUIC_HFILES = \ - vquic/msh3.h \ - vquic/ngtcp2.h \ - vquic/quiche.h \ - vquic/vquic.h - -LIB_VSSH_CFILES = \ - vssh/libssh.c \ - vssh/libssh2.c \ - vssh/wolfssh.c - -LIB_VSSH_HFILES = \ - vssh/ssh.h - -LIB_CFILES = \ - altsvc.c \ - amigaos.c \ - asyn-ares.c \ - asyn-thread.c \ - base64.c \ - bufref.c \ - c-hyper.c \ - conncache.c \ - connect.c \ - content_encoding.c \ - cookie.c \ - curl_addrinfo.c \ - curl_des.c \ - curl_endian.c \ - curl_fnmatch.c \ - curl_get_line.c \ - curl_gethostname.c \ - curl_gssapi.c \ - curl_memrchr.c \ - curl_multibyte.c \ - curl_ntlm_core.c \ - curl_ntlm_wb.c \ - curl_path.c \ - curl_range.c \ - curl_rtmp.c \ - curl_sasl.c \ - curl_sspi.c \ - curl_threads.c \ - dict.c \ - doh.c \ - dynbuf.c \ - easy.c \ - easygetopt.c \ - easyoptions.c \ - escape.c \ - file.c \ - fileinfo.c \ - fopen.c \ - formdata.c \ - ftp.c \ - ftplistparser.c \ - getenv.c \ - getinfo.c \ - gopher.c \ - h2h3.c \ - hash.c \ - headers.c \ - hmac.c \ - hostasyn.c \ - hostip.c \ - hostip4.c \ - hostip6.c \ - hostsyn.c \ - hsts.c \ - http.c \ - http2.c \ - http_chunks.c \ - http_digest.c \ - http_negotiate.c \ - http_ntlm.c \ - http_proxy.c \ - http_aws_sigv4.c \ - idn_win32.c \ - if2ip.c \ - imap.c \ - inet_ntop.c \ - inet_pton.c \ - krb5.c \ - ldap.c \ - llist.c \ - md4.c \ - md5.c \ - memdebug.c \ - mime.c \ - mprintf.c \ - mqtt.c \ - multi.c \ - netrc.c \ - nonblock.c \ - noproxy.c \ - openldap.c \ - parsedate.c \ - pingpong.c \ - pop3.c \ - progress.c \ - psl.c \ - rand.c \ - rename.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 \ - strerror.c \ - strtok.c \ - strtoofft.c \ - system_win32.c \ - telnet.c \ - tftp.c \ - timediff.c \ - timeval.c \ - transfer.c \ - url.c \ - urlapi.c \ - version.c \ - version_win32.c \ - warnless.c \ - wildcard.c \ - ws.c - -LIB_HFILES = \ - altsvc.h \ - amigaos.h \ - arpa_telnet.h \ - asyn.h \ - bufref.h \ - c-hyper.h \ - conncache.h \ - connect.h \ - content_encoding.h \ - cookie.h \ - curl_addrinfo.h \ - curl_base64.h \ - curl_ctype.h \ - curl_des.h \ - curl_endian.h \ - curl_fnmatch.h \ - curl_get_line.h \ - curl_gethostname.h \ - curl_gssapi.h \ - curl_hmac.h \ - curl_krb5.h \ - curl_ldap.h \ - curl_md4.h \ - curl_md5.h \ - curl_memory.h \ - curl_memrchr.h \ - curl_multibyte.h \ - curl_ntlm_core.h \ - curl_ntlm_wb.h \ - curl_path.h \ - curl_printf.h \ - curl_range.h \ - curl_rtmp.h \ - curl_sasl.h \ - curl_setup.h \ - curl_setup_once.h \ - curl_sha256.h \ - curl_sspi.h \ - curl_threads.h \ - curlx.h \ - dict.h \ - doh.h \ - dynbuf.h \ - easy_lock.h \ - easyif.h \ - easyoptions.h \ - escape.h \ - file.h \ - fileinfo.h \ - fopen.h \ - formdata.h \ - functypes.h \ - ftp.h \ - ftplistparser.h \ - getinfo.h \ - gopher.h \ - h2h3.h \ - hash.h \ - headers.h \ - hostip.h \ - hsts.h \ - http.h \ - http2.h \ - http_chunks.h \ - http_digest.h \ - http_negotiate.h \ - http_ntlm.h \ - http_proxy.h \ - http_aws_sigv4.h \ - if2ip.h \ - imap.h \ - inet_ntop.h \ - inet_pton.h \ - llist.h \ - memdebug.h \ - mime.h \ - mqtt.h \ - multihandle.h \ - multiif.h \ - netrc.h \ - nonblock.h \ - noproxy.h \ - parsedate.h \ - pingpong.h \ - pop3.h \ - progress.h \ - psl.h \ - quic.h \ - rand.h \ - rename.h \ - rtsp.h \ - select.h \ - sendf.h \ - setopt.h \ - setup-vms.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 \ - strtok.h \ - strtoofft.h \ - system_win32.h \ - telnet.h \ - tftp.h \ - timediff.h \ - timeval.h \ - transfer.h \ - url.h \ - urlapi-int.h \ - urldata.h \ - version_win32.h \ - warnless.h \ - wildcard.h \ - ws.h - -LIB_RCFILES = libcurl.rc -CSOURCES = $(LIB_CFILES) $(LIB_VAUTH_CFILES) $(LIB_VTLS_CFILES) \ - $(LIB_VQUIC_CFILES) $(LIB_VSSH_CFILES) - -HHEADERS = $(LIB_HFILES) $(LIB_VAUTH_HFILES) $(LIB_VTLS_HFILES) \ - $(LIB_VQUIC_HFILES) $(LIB_VSSH_HFILES) - - -# Makefile.inc provides the CSOURCES and HHEADERS defines -libcurl_la_SOURCES = $(CSOURCES) $(HHEADERS) $(am__append_7) -libcurlu_la_SOURCES = $(CSOURCES) $(HHEADERS) -libcurl_la_CPPFLAGS_EXTRA = $(am__append_6) $(am__append_8) -libcurl_la_LDFLAGS_EXTRA = $(am__append_1) $(am__append_2) \ - $(am__append_3) $(am__append_4) $(am__append_5) -libcurl_la_CFLAGS_EXTRA = $(am__append_9) -libcurl_la_CPPFLAGS = $(AM_CPPFLAGS) $(libcurl_la_CPPFLAGS_EXTRA) -libcurl_la_LDFLAGS = $(AM_LDFLAGS) $(libcurl_la_LDFLAGS_EXTRA) $(LDFLAGS) $(LIBCURL_LIBS) -libcurl_la_CFLAGS = $(AM_CFLAGS) $(libcurl_la_CFLAGS_EXTRA) -libcurlu_la_CPPFLAGS = $(AM_CPPFLAGS) -DCURL_STATICLIB -DUNITTESTS -libcurlu_la_LDFLAGS = $(AM_LDFLAGS) -static $(LIBCURL_LIBS) -libcurlu_la_CFLAGS = $(AM_CFLAGS) -CHECKSRC = $(CS_$(V)) -CS_0 = @echo " RUN " $@; -CS_1 = -CS_ = $(CS_0) - -# disable the tests that are mostly causing false positives -TIDYFLAGS = -checks=-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-valist.Uninitialized,-clang-analyzer-core.NonNullParamChecker,-clang-analyzer-core.NullDereference -quiet -TIDY := clang-tidy -all: curl_config.h - $(MAKE) $(AM_MAKEFLAGS) all-am - -.SUFFIXES: -.SUFFIXES: .c .lo .o .obj .rc -$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/Makefile.inc $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ - && { if test -f $@; then exit 0; else break; fi; }; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/Makefile'; \ - $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --foreign lib/Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ - esac; -$(srcdir)/Makefile.inc $(am__empty): - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(am__aclocal_m4_deps): - -curl_config.h: stamp-h1 - @test -f $@ || rm -f stamp-h1 - @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 - -stamp-h1: $(srcdir)/curl_config.h.in $(top_builddir)/config.status - @rm -f stamp-h1 - cd $(top_builddir) && $(SHELL) ./config.status lib/curl_config.h -$(srcdir)/curl_config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) - ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) - rm -f stamp-h1 - touch $@ - -distclean-hdr: - -rm -f curl_config.h stamp-h1 -libcurl.vers: $(top_builddir)/config.status $(srcdir)/libcurl.vers.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ -libcurl.plist: $(top_builddir)/config.status $(srcdir)/libcurl.plist.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ - -install-libLTLIBRARIES: $(lib_LTLIBRARIES) - @$(NORMAL_INSTALL) - @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ - list2=; for p in $$list; do \ - if test -f $$p; then \ - list2="$$list2 $$p"; \ - else :; fi; \ - done; \ - test -z "$$list2" || { \ - echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ - $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ - echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ - $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ - } - -uninstall-libLTLIBRARIES: - @$(NORMAL_UNINSTALL) - @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ - for p in $$list; do \ - $(am__strip_dir) \ - echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ - $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ - done - -clean-libLTLIBRARIES: - -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) - @list='$(lib_LTLIBRARIES)'; \ - locs=`for p in $$list; do echo $$p; done | \ - sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ - sort -u`; \ - test -z "$$locs" || { \ - echo rm -f $${locs}; \ - rm -f $${locs}; \ - } - -clean-noinstLTLIBRARIES: - -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) - @list='$(noinst_LTLIBRARIES)'; \ - locs=`for p in $$list; do echo $$p; done | \ - sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ - sort -u`; \ - test -z "$$locs" || { \ - echo rm -f $${locs}; \ - rm -f $${locs}; \ - } -vauth/$(am__dirstamp): - @$(MKDIR_P) vauth - @: > vauth/$(am__dirstamp) -vauth/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) vauth/$(DEPDIR) - @: > vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurl_la-cleartext.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurl_la-cram.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurl_la-digest.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurl_la-digest_sspi.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurl_la-gsasl.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurl_la-krb5_gssapi.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurl_la-krb5_sspi.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurl_la-ntlm.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurl_la-ntlm_sspi.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurl_la-oauth2.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurl_la-spnego_gssapi.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurl_la-spnego_sspi.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurl_la-vauth.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vtls/$(am__dirstamp): - @$(MKDIR_P) vtls - @: > vtls/$(am__dirstamp) -vtls/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) vtls/$(DEPDIR) - @: > vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurl_la-bearssl.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurl_la-gskit.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurl_la-gtls.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurl_la-hostcheck.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurl_la-keylog.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurl_la-mbedtls.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurl_la-mbedtls_threadlock.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurl_la-nss.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurl_la-openssl.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurl_la-rustls.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurl_la-schannel.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurl_la-schannel_verify.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurl_la-sectransp.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurl_la-vtls.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurl_la-wolfssl.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurl_la-x509asn1.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vquic/$(am__dirstamp): - @$(MKDIR_P) vquic - @: > vquic/$(am__dirstamp) -vquic/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) vquic/$(DEPDIR) - @: > vquic/$(DEPDIR)/$(am__dirstamp) -vquic/libcurl_la-msh3.lo: vquic/$(am__dirstamp) \ - vquic/$(DEPDIR)/$(am__dirstamp) -vquic/libcurl_la-ngtcp2.lo: vquic/$(am__dirstamp) \ - vquic/$(DEPDIR)/$(am__dirstamp) -vquic/libcurl_la-quiche.lo: vquic/$(am__dirstamp) \ - vquic/$(DEPDIR)/$(am__dirstamp) -vquic/libcurl_la-vquic.lo: vquic/$(am__dirstamp) \ - vquic/$(DEPDIR)/$(am__dirstamp) -vssh/$(am__dirstamp): - @$(MKDIR_P) vssh - @: > vssh/$(am__dirstamp) -vssh/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) vssh/$(DEPDIR) - @: > vssh/$(DEPDIR)/$(am__dirstamp) -vssh/libcurl_la-libssh.lo: vssh/$(am__dirstamp) \ - vssh/$(DEPDIR)/$(am__dirstamp) -vssh/libcurl_la-libssh2.lo: vssh/$(am__dirstamp) \ - vssh/$(DEPDIR)/$(am__dirstamp) -vssh/libcurl_la-wolfssh.lo: vssh/$(am__dirstamp) \ - vssh/$(DEPDIR)/$(am__dirstamp) - -libcurl.la: $(libcurl_la_OBJECTS) $(libcurl_la_DEPENDENCIES) $(EXTRA_libcurl_la_DEPENDENCIES) - $(AM_V_CCLD)$(libcurl_la_LINK) -rpath $(libdir) $(libcurl_la_OBJECTS) $(libcurl_la_LIBADD) $(LIBS) -vauth/libcurlu_la-cleartext.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurlu_la-cram.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurlu_la-digest.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurlu_la-digest_sspi.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurlu_la-gsasl.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurlu_la-krb5_gssapi.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurlu_la-krb5_sspi.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurlu_la-ntlm.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurlu_la-ntlm_sspi.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurlu_la-oauth2.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurlu_la-spnego_gssapi.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurlu_la-spnego_sspi.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vauth/libcurlu_la-vauth.lo: vauth/$(am__dirstamp) \ - vauth/$(DEPDIR)/$(am__dirstamp) -vtls/libcurlu_la-bearssl.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurlu_la-gskit.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurlu_la-gtls.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurlu_la-hostcheck.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurlu_la-keylog.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurlu_la-mbedtls.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurlu_la-mbedtls_threadlock.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurlu_la-nss.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurlu_la-openssl.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurlu_la-rustls.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurlu_la-schannel.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurlu_la-schannel_verify.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurlu_la-sectransp.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurlu_la-vtls.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurlu_la-wolfssl.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vtls/libcurlu_la-x509asn1.lo: vtls/$(am__dirstamp) \ - vtls/$(DEPDIR)/$(am__dirstamp) -vquic/libcurlu_la-msh3.lo: vquic/$(am__dirstamp) \ - vquic/$(DEPDIR)/$(am__dirstamp) -vquic/libcurlu_la-ngtcp2.lo: vquic/$(am__dirstamp) \ - vquic/$(DEPDIR)/$(am__dirstamp) -vquic/libcurlu_la-quiche.lo: vquic/$(am__dirstamp) \ - vquic/$(DEPDIR)/$(am__dirstamp) -vquic/libcurlu_la-vquic.lo: vquic/$(am__dirstamp) \ - vquic/$(DEPDIR)/$(am__dirstamp) -vssh/libcurlu_la-libssh.lo: vssh/$(am__dirstamp) \ - vssh/$(DEPDIR)/$(am__dirstamp) -vssh/libcurlu_la-libssh2.lo: vssh/$(am__dirstamp) \ - vssh/$(DEPDIR)/$(am__dirstamp) -vssh/libcurlu_la-wolfssh.lo: vssh/$(am__dirstamp) \ - vssh/$(DEPDIR)/$(am__dirstamp) - -libcurlu.la: $(libcurlu_la_OBJECTS) $(libcurlu_la_DEPENDENCIES) $(EXTRA_libcurlu_la_DEPENDENCIES) - $(AM_V_CCLD)$(libcurlu_la_LINK) $(am_libcurlu_la_rpath) $(libcurlu_la_OBJECTS) $(libcurlu_la_LIBADD) $(LIBS) - -mostlyclean-compile: - -rm -f *.$(OBJEXT) - -rm -f vauth/*.$(OBJEXT) - -rm -f vauth/*.lo - -rm -f vquic/*.$(OBJEXT) - -rm -f vquic/*.lo - -rm -f vssh/*.$(OBJEXT) - -rm -f vssh/*.lo - -rm -f vtls/*.$(OBJEXT) - -rm -f vtls/*.lo - -distclean-compile: - -rm -f *.tab.c - -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-altsvc.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-amigaos.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-asyn-ares.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-asyn-thread.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-base64.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-bufref.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-c-hyper.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-conncache.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-connect.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-content_encoding.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cookie.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_des.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_endian.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_fnmatch.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_get_line.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_gethostname.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_gssapi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_memrchr.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_multibyte.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_ntlm_core.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_ntlm_wb.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_path.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_range.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_rtmp.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_sasl.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_sspi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_threads.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-dict.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-doh.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-dynbuf.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-easy.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-easygetopt.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-easyoptions.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-escape.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-file.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-fileinfo.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-fopen.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-formdata.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-ftp.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-ftplistparser.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-getenv.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-getinfo.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-gopher.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-h2h3.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hash.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-headers.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hmac.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostasyn.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostip.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostip4.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostip6.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostsyn.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hsts.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http2.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_aws_sigv4.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_chunks.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_digest.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_negotiate.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_ntlm.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_proxy.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-idn_win32.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-if2ip.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-imap.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-inet_ntop.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-inet_pton.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-krb5.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-ldap.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-llist.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-md4.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-md5.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-memdebug.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-mime.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-mprintf.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-mqtt.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-multi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-netrc.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-nonblock.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-noproxy.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-openldap.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-parsedate.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-pingpong.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-pop3.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-progress.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-psl.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-rand.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-rename.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-rtsp.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-select.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-sendf.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-setopt.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-sha256.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-share.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-slist.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-smb.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-smtp.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-socketpair.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-socks.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-socks_gssapi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-socks_sspi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-speedcheck.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-splay.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strcase.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strdup.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strerror.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strtok.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strtoofft.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-system_win32.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-telnet.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-tftp.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-timediff.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-timeval.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-transfer.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-url.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-urlapi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-version.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-version_win32.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-warnless.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-wildcard.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-ws.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-altsvc.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-amigaos.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-asyn-ares.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-asyn-thread.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-base64.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-bufref.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-c-hyper.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-conncache.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-connect.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-content_encoding.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cookie.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_des.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_endian.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_fnmatch.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_get_line.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_gethostname.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_gssapi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_memrchr.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_multibyte.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_ntlm_wb.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_path.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_range.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_rtmp.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_sasl.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_sspi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_threads.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-dict.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-doh.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-dynbuf.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-easy.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-easygetopt.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-easyoptions.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-escape.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-file.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-fileinfo.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-fopen.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-formdata.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-ftp.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-ftplistparser.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-getenv.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-getinfo.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-gopher.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-h2h3.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hash.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-headers.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hmac.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostasyn.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostip.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostip4.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostip6.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostsyn.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hsts.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http2.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_aws_sigv4.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_chunks.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_digest.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_negotiate.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_ntlm.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_proxy.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-idn_win32.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-if2ip.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-imap.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-inet_ntop.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-inet_pton.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-krb5.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-ldap.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-llist.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-md4.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-md5.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-memdebug.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-mime.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-mprintf.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-mqtt.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-multi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-netrc.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-nonblock.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-noproxy.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-openldap.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-parsedate.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-pingpong.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-pop3.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-progress.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-psl.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-rand.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-rename.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-rtsp.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-select.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-sendf.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-setopt.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-sha256.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-share.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-slist.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-smb.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-smtp.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-socketpair.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-socks.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-socks_gssapi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-socks_sspi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-speedcheck.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-splay.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strcase.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strdup.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strerror.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strtok.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strtoofft.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-system_win32.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-telnet.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-tftp.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-timediff.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-timeval.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-transfer.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-url.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-urlapi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-version.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-version_win32.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-warnless.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-wildcard.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-ws.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-cleartext.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-cram.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-digest.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-digest_sspi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-gsasl.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-ntlm.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-oauth2.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-vauth.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-cleartext.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-cram.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-digest.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-gsasl.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-ntlm.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-oauth2.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-vauth.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-msh3.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-ngtcp2.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-quiche.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-vquic.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-msh3.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-ngtcp2.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-quiche.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-vquic.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vssh/$(DEPDIR)/libcurl_la-libssh.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vssh/$(DEPDIR)/libcurl_la-libssh2.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vssh/$(DEPDIR)/libcurl_la-wolfssh.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vssh/$(DEPDIR)/libcurlu_la-libssh.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vssh/$(DEPDIR)/libcurlu_la-libssh2.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vssh/$(DEPDIR)/libcurlu_la-wolfssh.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-bearssl.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-gskit.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-gtls.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-hostcheck.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-keylog.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-mbedtls.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-nss.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-openssl.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-rustls.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-schannel.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-sectransp.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-vtls.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-x509asn1.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-bearssl.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-gskit.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-gtls.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-hostcheck.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-keylog.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-mbedtls.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-nss.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-openssl.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-rustls.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-schannel.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-sectransp.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-vtls.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-x509asn1.Plo@am__quote@ # am--include-marker - -$(am__depfiles_remade): - @$(MKDIR_P) $(@D) - @echo '# dummy' >$@-t && $(am__mv) $@-t $@ - -am--depfiles: $(am__depfiles_remade) - -.c.o: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< - -.c.obj: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` - -.c.lo: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ -@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< - -libcurl_la-altsvc.lo: altsvc.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-altsvc.lo -MD -MP -MF $(DEPDIR)/libcurl_la-altsvc.Tpo -c -o libcurl_la-altsvc.lo `test -f 'altsvc.c' || echo '$(srcdir)/'`altsvc.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-altsvc.Tpo $(DEPDIR)/libcurl_la-altsvc.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='altsvc.c' object='libcurl_la-altsvc.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-altsvc.lo `test -f 'altsvc.c' || echo '$(srcdir)/'`altsvc.c - -libcurl_la-amigaos.lo: amigaos.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-amigaos.lo -MD -MP -MF $(DEPDIR)/libcurl_la-amigaos.Tpo -c -o libcurl_la-amigaos.lo `test -f 'amigaos.c' || echo '$(srcdir)/'`amigaos.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-amigaos.Tpo $(DEPDIR)/libcurl_la-amigaos.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='amigaos.c' object='libcurl_la-amigaos.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-amigaos.lo `test -f 'amigaos.c' || echo '$(srcdir)/'`amigaos.c - -libcurl_la-asyn-ares.lo: asyn-ares.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-asyn-ares.lo -MD -MP -MF $(DEPDIR)/libcurl_la-asyn-ares.Tpo -c -o libcurl_la-asyn-ares.lo `test -f 'asyn-ares.c' || echo '$(srcdir)/'`asyn-ares.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-asyn-ares.Tpo $(DEPDIR)/libcurl_la-asyn-ares.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asyn-ares.c' object='libcurl_la-asyn-ares.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-asyn-ares.lo `test -f 'asyn-ares.c' || echo '$(srcdir)/'`asyn-ares.c - -libcurl_la-asyn-thread.lo: asyn-thread.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-asyn-thread.lo -MD -MP -MF $(DEPDIR)/libcurl_la-asyn-thread.Tpo -c -o libcurl_la-asyn-thread.lo `test -f 'asyn-thread.c' || echo '$(srcdir)/'`asyn-thread.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-asyn-thread.Tpo $(DEPDIR)/libcurl_la-asyn-thread.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asyn-thread.c' object='libcurl_la-asyn-thread.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-asyn-thread.lo `test -f 'asyn-thread.c' || echo '$(srcdir)/'`asyn-thread.c - -libcurl_la-base64.lo: base64.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-base64.lo -MD -MP -MF $(DEPDIR)/libcurl_la-base64.Tpo -c -o libcurl_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-base64.Tpo $(DEPDIR)/libcurl_la-base64.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='base64.c' object='libcurl_la-base64.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c - -libcurl_la-bufref.lo: bufref.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-bufref.lo -MD -MP -MF $(DEPDIR)/libcurl_la-bufref.Tpo -c -o libcurl_la-bufref.lo `test -f 'bufref.c' || echo '$(srcdir)/'`bufref.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-bufref.Tpo $(DEPDIR)/libcurl_la-bufref.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bufref.c' object='libcurl_la-bufref.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-bufref.lo `test -f 'bufref.c' || echo '$(srcdir)/'`bufref.c - -libcurl_la-c-hyper.lo: c-hyper.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-c-hyper.lo -MD -MP -MF $(DEPDIR)/libcurl_la-c-hyper.Tpo -c -o libcurl_la-c-hyper.lo `test -f 'c-hyper.c' || echo '$(srcdir)/'`c-hyper.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-c-hyper.Tpo $(DEPDIR)/libcurl_la-c-hyper.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='c-hyper.c' object='libcurl_la-c-hyper.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-c-hyper.lo `test -f 'c-hyper.c' || echo '$(srcdir)/'`c-hyper.c - -libcurl_la-conncache.lo: conncache.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-conncache.lo -MD -MP -MF $(DEPDIR)/libcurl_la-conncache.Tpo -c -o libcurl_la-conncache.lo `test -f 'conncache.c' || echo '$(srcdir)/'`conncache.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-conncache.Tpo $(DEPDIR)/libcurl_la-conncache.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conncache.c' object='libcurl_la-conncache.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-conncache.lo `test -f 'conncache.c' || echo '$(srcdir)/'`conncache.c - -libcurl_la-connect.lo: connect.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-connect.lo -MD -MP -MF $(DEPDIR)/libcurl_la-connect.Tpo -c -o libcurl_la-connect.lo `test -f 'connect.c' || echo '$(srcdir)/'`connect.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-connect.Tpo $(DEPDIR)/libcurl_la-connect.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='connect.c' object='libcurl_la-connect.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-connect.lo `test -f 'connect.c' || echo '$(srcdir)/'`connect.c - -libcurl_la-content_encoding.lo: content_encoding.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-content_encoding.lo -MD -MP -MF $(DEPDIR)/libcurl_la-content_encoding.Tpo -c -o libcurl_la-content_encoding.lo `test -f 'content_encoding.c' || echo '$(srcdir)/'`content_encoding.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-content_encoding.Tpo $(DEPDIR)/libcurl_la-content_encoding.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='content_encoding.c' object='libcurl_la-content_encoding.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-content_encoding.lo `test -f 'content_encoding.c' || echo '$(srcdir)/'`content_encoding.c - -libcurl_la-cookie.lo: cookie.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-cookie.lo -MD -MP -MF $(DEPDIR)/libcurl_la-cookie.Tpo -c -o libcurl_la-cookie.lo `test -f 'cookie.c' || echo '$(srcdir)/'`cookie.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-cookie.Tpo $(DEPDIR)/libcurl_la-cookie.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cookie.c' object='libcurl_la-cookie.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-cookie.lo `test -f 'cookie.c' || echo '$(srcdir)/'`cookie.c - -libcurl_la-curl_addrinfo.lo: curl_addrinfo.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_addrinfo.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_addrinfo.Tpo -c -o libcurl_la-curl_addrinfo.lo `test -f 'curl_addrinfo.c' || echo '$(srcdir)/'`curl_addrinfo.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_addrinfo.Tpo $(DEPDIR)/libcurl_la-curl_addrinfo.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_addrinfo.c' object='libcurl_la-curl_addrinfo.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_addrinfo.lo `test -f 'curl_addrinfo.c' || echo '$(srcdir)/'`curl_addrinfo.c - -libcurl_la-curl_des.lo: curl_des.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_des.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_des.Tpo -c -o libcurl_la-curl_des.lo `test -f 'curl_des.c' || echo '$(srcdir)/'`curl_des.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_des.Tpo $(DEPDIR)/libcurl_la-curl_des.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_des.c' object='libcurl_la-curl_des.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_des.lo `test -f 'curl_des.c' || echo '$(srcdir)/'`curl_des.c - -libcurl_la-curl_endian.lo: curl_endian.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_endian.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_endian.Tpo -c -o libcurl_la-curl_endian.lo `test -f 'curl_endian.c' || echo '$(srcdir)/'`curl_endian.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_endian.Tpo $(DEPDIR)/libcurl_la-curl_endian.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_endian.c' object='libcurl_la-curl_endian.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_endian.lo `test -f 'curl_endian.c' || echo '$(srcdir)/'`curl_endian.c - -libcurl_la-curl_fnmatch.lo: curl_fnmatch.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_fnmatch.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_fnmatch.Tpo -c -o libcurl_la-curl_fnmatch.lo `test -f 'curl_fnmatch.c' || echo '$(srcdir)/'`curl_fnmatch.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_fnmatch.Tpo $(DEPDIR)/libcurl_la-curl_fnmatch.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_fnmatch.c' object='libcurl_la-curl_fnmatch.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_fnmatch.lo `test -f 'curl_fnmatch.c' || echo '$(srcdir)/'`curl_fnmatch.c - -libcurl_la-curl_get_line.lo: curl_get_line.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_get_line.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_get_line.Tpo -c -o libcurl_la-curl_get_line.lo `test -f 'curl_get_line.c' || echo '$(srcdir)/'`curl_get_line.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_get_line.Tpo $(DEPDIR)/libcurl_la-curl_get_line.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_get_line.c' object='libcurl_la-curl_get_line.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_get_line.lo `test -f 'curl_get_line.c' || echo '$(srcdir)/'`curl_get_line.c - -libcurl_la-curl_gethostname.lo: curl_gethostname.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_gethostname.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_gethostname.Tpo -c -o libcurl_la-curl_gethostname.lo `test -f 'curl_gethostname.c' || echo '$(srcdir)/'`curl_gethostname.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_gethostname.Tpo $(DEPDIR)/libcurl_la-curl_gethostname.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_gethostname.c' object='libcurl_la-curl_gethostname.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_gethostname.lo `test -f 'curl_gethostname.c' || echo '$(srcdir)/'`curl_gethostname.c - -libcurl_la-curl_gssapi.lo: curl_gssapi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_gssapi.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_gssapi.Tpo -c -o libcurl_la-curl_gssapi.lo `test -f 'curl_gssapi.c' || echo '$(srcdir)/'`curl_gssapi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_gssapi.Tpo $(DEPDIR)/libcurl_la-curl_gssapi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_gssapi.c' object='libcurl_la-curl_gssapi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_gssapi.lo `test -f 'curl_gssapi.c' || echo '$(srcdir)/'`curl_gssapi.c - -libcurl_la-curl_memrchr.lo: curl_memrchr.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_memrchr.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_memrchr.Tpo -c -o libcurl_la-curl_memrchr.lo `test -f 'curl_memrchr.c' || echo '$(srcdir)/'`curl_memrchr.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_memrchr.Tpo $(DEPDIR)/libcurl_la-curl_memrchr.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_memrchr.c' object='libcurl_la-curl_memrchr.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_memrchr.lo `test -f 'curl_memrchr.c' || echo '$(srcdir)/'`curl_memrchr.c - -libcurl_la-curl_multibyte.lo: curl_multibyte.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_multibyte.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_multibyte.Tpo -c -o libcurl_la-curl_multibyte.lo `test -f 'curl_multibyte.c' || echo '$(srcdir)/'`curl_multibyte.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_multibyte.Tpo $(DEPDIR)/libcurl_la-curl_multibyte.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_multibyte.c' object='libcurl_la-curl_multibyte.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_multibyte.lo `test -f 'curl_multibyte.c' || echo '$(srcdir)/'`curl_multibyte.c - -libcurl_la-curl_ntlm_core.lo: curl_ntlm_core.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_ntlm_core.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_ntlm_core.Tpo -c -o libcurl_la-curl_ntlm_core.lo `test -f 'curl_ntlm_core.c' || echo '$(srcdir)/'`curl_ntlm_core.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_ntlm_core.Tpo $(DEPDIR)/libcurl_la-curl_ntlm_core.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_ntlm_core.c' object='libcurl_la-curl_ntlm_core.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_ntlm_core.lo `test -f 'curl_ntlm_core.c' || echo '$(srcdir)/'`curl_ntlm_core.c - -libcurl_la-curl_ntlm_wb.lo: curl_ntlm_wb.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_ntlm_wb.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_ntlm_wb.Tpo -c -o libcurl_la-curl_ntlm_wb.lo `test -f 'curl_ntlm_wb.c' || echo '$(srcdir)/'`curl_ntlm_wb.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_ntlm_wb.Tpo $(DEPDIR)/libcurl_la-curl_ntlm_wb.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_ntlm_wb.c' object='libcurl_la-curl_ntlm_wb.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_ntlm_wb.lo `test -f 'curl_ntlm_wb.c' || echo '$(srcdir)/'`curl_ntlm_wb.c - -libcurl_la-curl_path.lo: curl_path.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_path.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_path.Tpo -c -o libcurl_la-curl_path.lo `test -f 'curl_path.c' || echo '$(srcdir)/'`curl_path.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_path.Tpo $(DEPDIR)/libcurl_la-curl_path.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_path.c' object='libcurl_la-curl_path.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_path.lo `test -f 'curl_path.c' || echo '$(srcdir)/'`curl_path.c - -libcurl_la-curl_range.lo: curl_range.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_range.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_range.Tpo -c -o libcurl_la-curl_range.lo `test -f 'curl_range.c' || echo '$(srcdir)/'`curl_range.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_range.Tpo $(DEPDIR)/libcurl_la-curl_range.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_range.c' object='libcurl_la-curl_range.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_range.lo `test -f 'curl_range.c' || echo '$(srcdir)/'`curl_range.c - -libcurl_la-curl_rtmp.lo: curl_rtmp.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_rtmp.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_rtmp.Tpo -c -o libcurl_la-curl_rtmp.lo `test -f 'curl_rtmp.c' || echo '$(srcdir)/'`curl_rtmp.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_rtmp.Tpo $(DEPDIR)/libcurl_la-curl_rtmp.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_rtmp.c' object='libcurl_la-curl_rtmp.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_rtmp.lo `test -f 'curl_rtmp.c' || echo '$(srcdir)/'`curl_rtmp.c - -libcurl_la-curl_sasl.lo: curl_sasl.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_sasl.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_sasl.Tpo -c -o libcurl_la-curl_sasl.lo `test -f 'curl_sasl.c' || echo '$(srcdir)/'`curl_sasl.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_sasl.Tpo $(DEPDIR)/libcurl_la-curl_sasl.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_sasl.c' object='libcurl_la-curl_sasl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_sasl.lo `test -f 'curl_sasl.c' || echo '$(srcdir)/'`curl_sasl.c - -libcurl_la-curl_sspi.lo: curl_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_sspi.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_sspi.Tpo -c -o libcurl_la-curl_sspi.lo `test -f 'curl_sspi.c' || echo '$(srcdir)/'`curl_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_sspi.Tpo $(DEPDIR)/libcurl_la-curl_sspi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_sspi.c' object='libcurl_la-curl_sspi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_sspi.lo `test -f 'curl_sspi.c' || echo '$(srcdir)/'`curl_sspi.c - -libcurl_la-curl_threads.lo: curl_threads.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_threads.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_threads.Tpo -c -o libcurl_la-curl_threads.lo `test -f 'curl_threads.c' || echo '$(srcdir)/'`curl_threads.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_threads.Tpo $(DEPDIR)/libcurl_la-curl_threads.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_threads.c' object='libcurl_la-curl_threads.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_threads.lo `test -f 'curl_threads.c' || echo '$(srcdir)/'`curl_threads.c - -libcurl_la-dict.lo: dict.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-dict.lo -MD -MP -MF $(DEPDIR)/libcurl_la-dict.Tpo -c -o libcurl_la-dict.lo `test -f 'dict.c' || echo '$(srcdir)/'`dict.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-dict.Tpo $(DEPDIR)/libcurl_la-dict.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dict.c' object='libcurl_la-dict.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-dict.lo `test -f 'dict.c' || echo '$(srcdir)/'`dict.c - -libcurl_la-doh.lo: doh.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-doh.lo -MD -MP -MF $(DEPDIR)/libcurl_la-doh.Tpo -c -o libcurl_la-doh.lo `test -f 'doh.c' || echo '$(srcdir)/'`doh.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-doh.Tpo $(DEPDIR)/libcurl_la-doh.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='doh.c' object='libcurl_la-doh.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-doh.lo `test -f 'doh.c' || echo '$(srcdir)/'`doh.c - -libcurl_la-dynbuf.lo: dynbuf.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-dynbuf.lo -MD -MP -MF $(DEPDIR)/libcurl_la-dynbuf.Tpo -c -o libcurl_la-dynbuf.lo `test -f 'dynbuf.c' || echo '$(srcdir)/'`dynbuf.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-dynbuf.Tpo $(DEPDIR)/libcurl_la-dynbuf.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dynbuf.c' object='libcurl_la-dynbuf.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-dynbuf.lo `test -f 'dynbuf.c' || echo '$(srcdir)/'`dynbuf.c - -libcurl_la-easy.lo: easy.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-easy.lo -MD -MP -MF $(DEPDIR)/libcurl_la-easy.Tpo -c -o libcurl_la-easy.lo `test -f 'easy.c' || echo '$(srcdir)/'`easy.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-easy.Tpo $(DEPDIR)/libcurl_la-easy.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='easy.c' object='libcurl_la-easy.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-easy.lo `test -f 'easy.c' || echo '$(srcdir)/'`easy.c - -libcurl_la-easygetopt.lo: easygetopt.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-easygetopt.lo -MD -MP -MF $(DEPDIR)/libcurl_la-easygetopt.Tpo -c -o libcurl_la-easygetopt.lo `test -f 'easygetopt.c' || echo '$(srcdir)/'`easygetopt.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-easygetopt.Tpo $(DEPDIR)/libcurl_la-easygetopt.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='easygetopt.c' object='libcurl_la-easygetopt.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-easygetopt.lo `test -f 'easygetopt.c' || echo '$(srcdir)/'`easygetopt.c - -libcurl_la-easyoptions.lo: easyoptions.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-easyoptions.lo -MD -MP -MF $(DEPDIR)/libcurl_la-easyoptions.Tpo -c -o libcurl_la-easyoptions.lo `test -f 'easyoptions.c' || echo '$(srcdir)/'`easyoptions.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-easyoptions.Tpo $(DEPDIR)/libcurl_la-easyoptions.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='easyoptions.c' object='libcurl_la-easyoptions.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-easyoptions.lo `test -f 'easyoptions.c' || echo '$(srcdir)/'`easyoptions.c - -libcurl_la-escape.lo: escape.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-escape.lo -MD -MP -MF $(DEPDIR)/libcurl_la-escape.Tpo -c -o libcurl_la-escape.lo `test -f 'escape.c' || echo '$(srcdir)/'`escape.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-escape.Tpo $(DEPDIR)/libcurl_la-escape.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='escape.c' object='libcurl_la-escape.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-escape.lo `test -f 'escape.c' || echo '$(srcdir)/'`escape.c - -libcurl_la-file.lo: file.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-file.lo -MD -MP -MF $(DEPDIR)/libcurl_la-file.Tpo -c -o libcurl_la-file.lo `test -f 'file.c' || echo '$(srcdir)/'`file.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-file.Tpo $(DEPDIR)/libcurl_la-file.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file.c' object='libcurl_la-file.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-file.lo `test -f 'file.c' || echo '$(srcdir)/'`file.c - -libcurl_la-fileinfo.lo: fileinfo.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-fileinfo.lo -MD -MP -MF $(DEPDIR)/libcurl_la-fileinfo.Tpo -c -o libcurl_la-fileinfo.lo `test -f 'fileinfo.c' || echo '$(srcdir)/'`fileinfo.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-fileinfo.Tpo $(DEPDIR)/libcurl_la-fileinfo.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fileinfo.c' object='libcurl_la-fileinfo.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-fileinfo.lo `test -f 'fileinfo.c' || echo '$(srcdir)/'`fileinfo.c - -libcurl_la-fopen.lo: fopen.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-fopen.lo -MD -MP -MF $(DEPDIR)/libcurl_la-fopen.Tpo -c -o libcurl_la-fopen.lo `test -f 'fopen.c' || echo '$(srcdir)/'`fopen.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-fopen.Tpo $(DEPDIR)/libcurl_la-fopen.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fopen.c' object='libcurl_la-fopen.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-fopen.lo `test -f 'fopen.c' || echo '$(srcdir)/'`fopen.c - -libcurl_la-formdata.lo: formdata.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-formdata.lo -MD -MP -MF $(DEPDIR)/libcurl_la-formdata.Tpo -c -o libcurl_la-formdata.lo `test -f 'formdata.c' || echo '$(srcdir)/'`formdata.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-formdata.Tpo $(DEPDIR)/libcurl_la-formdata.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='formdata.c' object='libcurl_la-formdata.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-formdata.lo `test -f 'formdata.c' || echo '$(srcdir)/'`formdata.c - -libcurl_la-ftp.lo: ftp.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-ftp.lo -MD -MP -MF $(DEPDIR)/libcurl_la-ftp.Tpo -c -o libcurl_la-ftp.lo `test -f 'ftp.c' || echo '$(srcdir)/'`ftp.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-ftp.Tpo $(DEPDIR)/libcurl_la-ftp.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ftp.c' object='libcurl_la-ftp.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-ftp.lo `test -f 'ftp.c' || echo '$(srcdir)/'`ftp.c - -libcurl_la-ftplistparser.lo: ftplistparser.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-ftplistparser.lo -MD -MP -MF $(DEPDIR)/libcurl_la-ftplistparser.Tpo -c -o libcurl_la-ftplistparser.lo `test -f 'ftplistparser.c' || echo '$(srcdir)/'`ftplistparser.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-ftplistparser.Tpo $(DEPDIR)/libcurl_la-ftplistparser.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ftplistparser.c' object='libcurl_la-ftplistparser.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-ftplistparser.lo `test -f 'ftplistparser.c' || echo '$(srcdir)/'`ftplistparser.c - -libcurl_la-getenv.lo: getenv.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-getenv.lo -MD -MP -MF $(DEPDIR)/libcurl_la-getenv.Tpo -c -o libcurl_la-getenv.lo `test -f 'getenv.c' || echo '$(srcdir)/'`getenv.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-getenv.Tpo $(DEPDIR)/libcurl_la-getenv.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getenv.c' object='libcurl_la-getenv.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-getenv.lo `test -f 'getenv.c' || echo '$(srcdir)/'`getenv.c - -libcurl_la-getinfo.lo: getinfo.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-getinfo.lo -MD -MP -MF $(DEPDIR)/libcurl_la-getinfo.Tpo -c -o libcurl_la-getinfo.lo `test -f 'getinfo.c' || echo '$(srcdir)/'`getinfo.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-getinfo.Tpo $(DEPDIR)/libcurl_la-getinfo.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getinfo.c' object='libcurl_la-getinfo.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-getinfo.lo `test -f 'getinfo.c' || echo '$(srcdir)/'`getinfo.c - -libcurl_la-gopher.lo: gopher.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-gopher.lo -MD -MP -MF $(DEPDIR)/libcurl_la-gopher.Tpo -c -o libcurl_la-gopher.lo `test -f 'gopher.c' || echo '$(srcdir)/'`gopher.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-gopher.Tpo $(DEPDIR)/libcurl_la-gopher.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gopher.c' object='libcurl_la-gopher.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-gopher.lo `test -f 'gopher.c' || echo '$(srcdir)/'`gopher.c - -libcurl_la-h2h3.lo: h2h3.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-h2h3.lo -MD -MP -MF $(DEPDIR)/libcurl_la-h2h3.Tpo -c -o libcurl_la-h2h3.lo `test -f 'h2h3.c' || echo '$(srcdir)/'`h2h3.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-h2h3.Tpo $(DEPDIR)/libcurl_la-h2h3.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='h2h3.c' object='libcurl_la-h2h3.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-h2h3.lo `test -f 'h2h3.c' || echo '$(srcdir)/'`h2h3.c - -libcurl_la-hash.lo: hash.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hash.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hash.Tpo -c -o libcurl_la-hash.lo `test -f 'hash.c' || echo '$(srcdir)/'`hash.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hash.Tpo $(DEPDIR)/libcurl_la-hash.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hash.c' object='libcurl_la-hash.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hash.lo `test -f 'hash.c' || echo '$(srcdir)/'`hash.c - -libcurl_la-headers.lo: headers.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-headers.lo -MD -MP -MF $(DEPDIR)/libcurl_la-headers.Tpo -c -o libcurl_la-headers.lo `test -f 'headers.c' || echo '$(srcdir)/'`headers.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-headers.Tpo $(DEPDIR)/libcurl_la-headers.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='headers.c' object='libcurl_la-headers.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-headers.lo `test -f 'headers.c' || echo '$(srcdir)/'`headers.c - -libcurl_la-hmac.lo: hmac.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hmac.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hmac.Tpo -c -o libcurl_la-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hmac.Tpo $(DEPDIR)/libcurl_la-hmac.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hmac.c' object='libcurl_la-hmac.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c - -libcurl_la-hostasyn.lo: hostasyn.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostasyn.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostasyn.Tpo -c -o libcurl_la-hostasyn.lo `test -f 'hostasyn.c' || echo '$(srcdir)/'`hostasyn.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostasyn.Tpo $(DEPDIR)/libcurl_la-hostasyn.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostasyn.c' object='libcurl_la-hostasyn.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hostasyn.lo `test -f 'hostasyn.c' || echo '$(srcdir)/'`hostasyn.c - -libcurl_la-hostip.lo: hostip.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostip.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostip.Tpo -c -o libcurl_la-hostip.lo `test -f 'hostip.c' || echo '$(srcdir)/'`hostip.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostip.Tpo $(DEPDIR)/libcurl_la-hostip.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostip.c' object='libcurl_la-hostip.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hostip.lo `test -f 'hostip.c' || echo '$(srcdir)/'`hostip.c - -libcurl_la-hostip4.lo: hostip4.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostip4.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostip4.Tpo -c -o libcurl_la-hostip4.lo `test -f 'hostip4.c' || echo '$(srcdir)/'`hostip4.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostip4.Tpo $(DEPDIR)/libcurl_la-hostip4.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostip4.c' object='libcurl_la-hostip4.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hostip4.lo `test -f 'hostip4.c' || echo '$(srcdir)/'`hostip4.c - -libcurl_la-hostip6.lo: hostip6.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostip6.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostip6.Tpo -c -o libcurl_la-hostip6.lo `test -f 'hostip6.c' || echo '$(srcdir)/'`hostip6.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostip6.Tpo $(DEPDIR)/libcurl_la-hostip6.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostip6.c' object='libcurl_la-hostip6.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hostip6.lo `test -f 'hostip6.c' || echo '$(srcdir)/'`hostip6.c - -libcurl_la-hostsyn.lo: hostsyn.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostsyn.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostsyn.Tpo -c -o libcurl_la-hostsyn.lo `test -f 'hostsyn.c' || echo '$(srcdir)/'`hostsyn.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostsyn.Tpo $(DEPDIR)/libcurl_la-hostsyn.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostsyn.c' object='libcurl_la-hostsyn.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hostsyn.lo `test -f 'hostsyn.c' || echo '$(srcdir)/'`hostsyn.c - -libcurl_la-hsts.lo: hsts.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hsts.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hsts.Tpo -c -o libcurl_la-hsts.lo `test -f 'hsts.c' || echo '$(srcdir)/'`hsts.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hsts.Tpo $(DEPDIR)/libcurl_la-hsts.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hsts.c' object='libcurl_la-hsts.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hsts.lo `test -f 'hsts.c' || echo '$(srcdir)/'`hsts.c - -libcurl_la-http.lo: http.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http.Tpo -c -o libcurl_la-http.lo `test -f 'http.c' || echo '$(srcdir)/'`http.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http.Tpo $(DEPDIR)/libcurl_la-http.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http.c' object='libcurl_la-http.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http.lo `test -f 'http.c' || echo '$(srcdir)/'`http.c - -libcurl_la-http2.lo: http2.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http2.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http2.Tpo -c -o libcurl_la-http2.lo `test -f 'http2.c' || echo '$(srcdir)/'`http2.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http2.Tpo $(DEPDIR)/libcurl_la-http2.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http2.c' object='libcurl_la-http2.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http2.lo `test -f 'http2.c' || echo '$(srcdir)/'`http2.c - -libcurl_la-http_chunks.lo: http_chunks.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http_chunks.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http_chunks.Tpo -c -o libcurl_la-http_chunks.lo `test -f 'http_chunks.c' || echo '$(srcdir)/'`http_chunks.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http_chunks.Tpo $(DEPDIR)/libcurl_la-http_chunks.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_chunks.c' object='libcurl_la-http_chunks.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http_chunks.lo `test -f 'http_chunks.c' || echo '$(srcdir)/'`http_chunks.c - -libcurl_la-http_digest.lo: http_digest.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http_digest.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http_digest.Tpo -c -o libcurl_la-http_digest.lo `test -f 'http_digest.c' || echo '$(srcdir)/'`http_digest.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http_digest.Tpo $(DEPDIR)/libcurl_la-http_digest.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_digest.c' object='libcurl_la-http_digest.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http_digest.lo `test -f 'http_digest.c' || echo '$(srcdir)/'`http_digest.c - -libcurl_la-http_negotiate.lo: http_negotiate.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http_negotiate.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http_negotiate.Tpo -c -o libcurl_la-http_negotiate.lo `test -f 'http_negotiate.c' || echo '$(srcdir)/'`http_negotiate.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http_negotiate.Tpo $(DEPDIR)/libcurl_la-http_negotiate.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_negotiate.c' object='libcurl_la-http_negotiate.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http_negotiate.lo `test -f 'http_negotiate.c' || echo '$(srcdir)/'`http_negotiate.c - -libcurl_la-http_ntlm.lo: http_ntlm.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http_ntlm.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http_ntlm.Tpo -c -o libcurl_la-http_ntlm.lo `test -f 'http_ntlm.c' || echo '$(srcdir)/'`http_ntlm.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http_ntlm.Tpo $(DEPDIR)/libcurl_la-http_ntlm.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_ntlm.c' object='libcurl_la-http_ntlm.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http_ntlm.lo `test -f 'http_ntlm.c' || echo '$(srcdir)/'`http_ntlm.c - -libcurl_la-http_proxy.lo: http_proxy.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http_proxy.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http_proxy.Tpo -c -o libcurl_la-http_proxy.lo `test -f 'http_proxy.c' || echo '$(srcdir)/'`http_proxy.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http_proxy.Tpo $(DEPDIR)/libcurl_la-http_proxy.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_proxy.c' object='libcurl_la-http_proxy.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http_proxy.lo `test -f 'http_proxy.c' || echo '$(srcdir)/'`http_proxy.c - -libcurl_la-http_aws_sigv4.lo: http_aws_sigv4.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http_aws_sigv4.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http_aws_sigv4.Tpo -c -o libcurl_la-http_aws_sigv4.lo `test -f 'http_aws_sigv4.c' || echo '$(srcdir)/'`http_aws_sigv4.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http_aws_sigv4.Tpo $(DEPDIR)/libcurl_la-http_aws_sigv4.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_aws_sigv4.c' object='libcurl_la-http_aws_sigv4.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http_aws_sigv4.lo `test -f 'http_aws_sigv4.c' || echo '$(srcdir)/'`http_aws_sigv4.c - -libcurl_la-idn_win32.lo: idn_win32.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-idn_win32.lo -MD -MP -MF $(DEPDIR)/libcurl_la-idn_win32.Tpo -c -o libcurl_la-idn_win32.lo `test -f 'idn_win32.c' || echo '$(srcdir)/'`idn_win32.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-idn_win32.Tpo $(DEPDIR)/libcurl_la-idn_win32.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='idn_win32.c' object='libcurl_la-idn_win32.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-idn_win32.lo `test -f 'idn_win32.c' || echo '$(srcdir)/'`idn_win32.c - -libcurl_la-if2ip.lo: if2ip.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-if2ip.lo -MD -MP -MF $(DEPDIR)/libcurl_la-if2ip.Tpo -c -o libcurl_la-if2ip.lo `test -f 'if2ip.c' || echo '$(srcdir)/'`if2ip.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-if2ip.Tpo $(DEPDIR)/libcurl_la-if2ip.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='if2ip.c' object='libcurl_la-if2ip.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-if2ip.lo `test -f 'if2ip.c' || echo '$(srcdir)/'`if2ip.c - -libcurl_la-imap.lo: imap.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-imap.lo -MD -MP -MF $(DEPDIR)/libcurl_la-imap.Tpo -c -o libcurl_la-imap.lo `test -f 'imap.c' || echo '$(srcdir)/'`imap.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-imap.Tpo $(DEPDIR)/libcurl_la-imap.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='imap.c' object='libcurl_la-imap.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-imap.lo `test -f 'imap.c' || echo '$(srcdir)/'`imap.c - -libcurl_la-inet_ntop.lo: inet_ntop.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-inet_ntop.lo -MD -MP -MF $(DEPDIR)/libcurl_la-inet_ntop.Tpo -c -o libcurl_la-inet_ntop.lo `test -f 'inet_ntop.c' || echo '$(srcdir)/'`inet_ntop.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-inet_ntop.Tpo $(DEPDIR)/libcurl_la-inet_ntop.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='inet_ntop.c' object='libcurl_la-inet_ntop.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-inet_ntop.lo `test -f 'inet_ntop.c' || echo '$(srcdir)/'`inet_ntop.c - -libcurl_la-inet_pton.lo: inet_pton.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-inet_pton.lo -MD -MP -MF $(DEPDIR)/libcurl_la-inet_pton.Tpo -c -o libcurl_la-inet_pton.lo `test -f 'inet_pton.c' || echo '$(srcdir)/'`inet_pton.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-inet_pton.Tpo $(DEPDIR)/libcurl_la-inet_pton.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='inet_pton.c' object='libcurl_la-inet_pton.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-inet_pton.lo `test -f 'inet_pton.c' || echo '$(srcdir)/'`inet_pton.c - -libcurl_la-krb5.lo: krb5.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-krb5.lo -MD -MP -MF $(DEPDIR)/libcurl_la-krb5.Tpo -c -o libcurl_la-krb5.lo `test -f 'krb5.c' || echo '$(srcdir)/'`krb5.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-krb5.Tpo $(DEPDIR)/libcurl_la-krb5.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='krb5.c' object='libcurl_la-krb5.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-krb5.lo `test -f 'krb5.c' || echo '$(srcdir)/'`krb5.c - -libcurl_la-ldap.lo: ldap.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-ldap.lo -MD -MP -MF $(DEPDIR)/libcurl_la-ldap.Tpo -c -o libcurl_la-ldap.lo `test -f 'ldap.c' || echo '$(srcdir)/'`ldap.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-ldap.Tpo $(DEPDIR)/libcurl_la-ldap.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ldap.c' object='libcurl_la-ldap.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-ldap.lo `test -f 'ldap.c' || echo '$(srcdir)/'`ldap.c - -libcurl_la-llist.lo: llist.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-llist.lo -MD -MP -MF $(DEPDIR)/libcurl_la-llist.Tpo -c -o libcurl_la-llist.lo `test -f 'llist.c' || echo '$(srcdir)/'`llist.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-llist.Tpo $(DEPDIR)/libcurl_la-llist.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='llist.c' object='libcurl_la-llist.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-llist.lo `test -f 'llist.c' || echo '$(srcdir)/'`llist.c - -libcurl_la-md4.lo: md4.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-md4.lo -MD -MP -MF $(DEPDIR)/libcurl_la-md4.Tpo -c -o libcurl_la-md4.lo `test -f 'md4.c' || echo '$(srcdir)/'`md4.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-md4.Tpo $(DEPDIR)/libcurl_la-md4.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='md4.c' object='libcurl_la-md4.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-md4.lo `test -f 'md4.c' || echo '$(srcdir)/'`md4.c - -libcurl_la-md5.lo: md5.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-md5.lo -MD -MP -MF $(DEPDIR)/libcurl_la-md5.Tpo -c -o libcurl_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-md5.Tpo $(DEPDIR)/libcurl_la-md5.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='md5.c' object='libcurl_la-md5.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c - -libcurl_la-memdebug.lo: memdebug.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-memdebug.lo -MD -MP -MF $(DEPDIR)/libcurl_la-memdebug.Tpo -c -o libcurl_la-memdebug.lo `test -f 'memdebug.c' || echo '$(srcdir)/'`memdebug.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-memdebug.Tpo $(DEPDIR)/libcurl_la-memdebug.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='memdebug.c' object='libcurl_la-memdebug.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-memdebug.lo `test -f 'memdebug.c' || echo '$(srcdir)/'`memdebug.c - -libcurl_la-mime.lo: mime.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-mime.lo -MD -MP -MF $(DEPDIR)/libcurl_la-mime.Tpo -c -o libcurl_la-mime.lo `test -f 'mime.c' || echo '$(srcdir)/'`mime.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-mime.Tpo $(DEPDIR)/libcurl_la-mime.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mime.c' object='libcurl_la-mime.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-mime.lo `test -f 'mime.c' || echo '$(srcdir)/'`mime.c - -libcurl_la-mprintf.lo: mprintf.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-mprintf.lo -MD -MP -MF $(DEPDIR)/libcurl_la-mprintf.Tpo -c -o libcurl_la-mprintf.lo `test -f 'mprintf.c' || echo '$(srcdir)/'`mprintf.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-mprintf.Tpo $(DEPDIR)/libcurl_la-mprintf.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mprintf.c' object='libcurl_la-mprintf.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-mprintf.lo `test -f 'mprintf.c' || echo '$(srcdir)/'`mprintf.c - -libcurl_la-mqtt.lo: mqtt.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-mqtt.lo -MD -MP -MF $(DEPDIR)/libcurl_la-mqtt.Tpo -c -o libcurl_la-mqtt.lo `test -f 'mqtt.c' || echo '$(srcdir)/'`mqtt.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-mqtt.Tpo $(DEPDIR)/libcurl_la-mqtt.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mqtt.c' object='libcurl_la-mqtt.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-mqtt.lo `test -f 'mqtt.c' || echo '$(srcdir)/'`mqtt.c - -libcurl_la-multi.lo: multi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-multi.lo -MD -MP -MF $(DEPDIR)/libcurl_la-multi.Tpo -c -o libcurl_la-multi.lo `test -f 'multi.c' || echo '$(srcdir)/'`multi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-multi.Tpo $(DEPDIR)/libcurl_la-multi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='multi.c' object='libcurl_la-multi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-multi.lo `test -f 'multi.c' || echo '$(srcdir)/'`multi.c - -libcurl_la-netrc.lo: netrc.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-netrc.lo -MD -MP -MF $(DEPDIR)/libcurl_la-netrc.Tpo -c -o libcurl_la-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-netrc.Tpo $(DEPDIR)/libcurl_la-netrc.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netrc.c' object='libcurl_la-netrc.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c - -libcurl_la-nonblock.lo: nonblock.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-nonblock.lo -MD -MP -MF $(DEPDIR)/libcurl_la-nonblock.Tpo -c -o libcurl_la-nonblock.lo `test -f 'nonblock.c' || echo '$(srcdir)/'`nonblock.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-nonblock.Tpo $(DEPDIR)/libcurl_la-nonblock.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nonblock.c' object='libcurl_la-nonblock.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-nonblock.lo `test -f 'nonblock.c' || echo '$(srcdir)/'`nonblock.c - -libcurl_la-noproxy.lo: noproxy.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-noproxy.lo -MD -MP -MF $(DEPDIR)/libcurl_la-noproxy.Tpo -c -o libcurl_la-noproxy.lo `test -f 'noproxy.c' || echo '$(srcdir)/'`noproxy.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-noproxy.Tpo $(DEPDIR)/libcurl_la-noproxy.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='noproxy.c' object='libcurl_la-noproxy.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-noproxy.lo `test -f 'noproxy.c' || echo '$(srcdir)/'`noproxy.c - -libcurl_la-openldap.lo: openldap.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-openldap.lo -MD -MP -MF $(DEPDIR)/libcurl_la-openldap.Tpo -c -o libcurl_la-openldap.lo `test -f 'openldap.c' || echo '$(srcdir)/'`openldap.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-openldap.Tpo $(DEPDIR)/libcurl_la-openldap.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openldap.c' object='libcurl_la-openldap.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-openldap.lo `test -f 'openldap.c' || echo '$(srcdir)/'`openldap.c - -libcurl_la-parsedate.lo: parsedate.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-parsedate.lo -MD -MP -MF $(DEPDIR)/libcurl_la-parsedate.Tpo -c -o libcurl_la-parsedate.lo `test -f 'parsedate.c' || echo '$(srcdir)/'`parsedate.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-parsedate.Tpo $(DEPDIR)/libcurl_la-parsedate.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='parsedate.c' object='libcurl_la-parsedate.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-parsedate.lo `test -f 'parsedate.c' || echo '$(srcdir)/'`parsedate.c - -libcurl_la-pingpong.lo: pingpong.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-pingpong.lo -MD -MP -MF $(DEPDIR)/libcurl_la-pingpong.Tpo -c -o libcurl_la-pingpong.lo `test -f 'pingpong.c' || echo '$(srcdir)/'`pingpong.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-pingpong.Tpo $(DEPDIR)/libcurl_la-pingpong.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pingpong.c' object='libcurl_la-pingpong.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-pingpong.lo `test -f 'pingpong.c' || echo '$(srcdir)/'`pingpong.c - -libcurl_la-pop3.lo: pop3.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-pop3.lo -MD -MP -MF $(DEPDIR)/libcurl_la-pop3.Tpo -c -o libcurl_la-pop3.lo `test -f 'pop3.c' || echo '$(srcdir)/'`pop3.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-pop3.Tpo $(DEPDIR)/libcurl_la-pop3.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pop3.c' object='libcurl_la-pop3.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-pop3.lo `test -f 'pop3.c' || echo '$(srcdir)/'`pop3.c - -libcurl_la-progress.lo: progress.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-progress.lo -MD -MP -MF $(DEPDIR)/libcurl_la-progress.Tpo -c -o libcurl_la-progress.lo `test -f 'progress.c' || echo '$(srcdir)/'`progress.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-progress.Tpo $(DEPDIR)/libcurl_la-progress.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='progress.c' object='libcurl_la-progress.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-progress.lo `test -f 'progress.c' || echo '$(srcdir)/'`progress.c - -libcurl_la-psl.lo: psl.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-psl.lo -MD -MP -MF $(DEPDIR)/libcurl_la-psl.Tpo -c -o libcurl_la-psl.lo `test -f 'psl.c' || echo '$(srcdir)/'`psl.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-psl.Tpo $(DEPDIR)/libcurl_la-psl.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='psl.c' object='libcurl_la-psl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-psl.lo `test -f 'psl.c' || echo '$(srcdir)/'`psl.c - -libcurl_la-rand.lo: rand.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-rand.lo -MD -MP -MF $(DEPDIR)/libcurl_la-rand.Tpo -c -o libcurl_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-rand.Tpo $(DEPDIR)/libcurl_la-rand.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rand.c' object='libcurl_la-rand.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c - -libcurl_la-rename.lo: rename.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-rename.lo -MD -MP -MF $(DEPDIR)/libcurl_la-rename.Tpo -c -o libcurl_la-rename.lo `test -f 'rename.c' || echo '$(srcdir)/'`rename.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-rename.Tpo $(DEPDIR)/libcurl_la-rename.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rename.c' object='libcurl_la-rename.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-rename.lo `test -f 'rename.c' || echo '$(srcdir)/'`rename.c - -libcurl_la-rtsp.lo: rtsp.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-rtsp.lo -MD -MP -MF $(DEPDIR)/libcurl_la-rtsp.Tpo -c -o libcurl_la-rtsp.lo `test -f 'rtsp.c' || echo '$(srcdir)/'`rtsp.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-rtsp.Tpo $(DEPDIR)/libcurl_la-rtsp.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rtsp.c' object='libcurl_la-rtsp.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-rtsp.lo `test -f 'rtsp.c' || echo '$(srcdir)/'`rtsp.c - -libcurl_la-select.lo: select.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-select.lo -MD -MP -MF $(DEPDIR)/libcurl_la-select.Tpo -c -o libcurl_la-select.lo `test -f 'select.c' || echo '$(srcdir)/'`select.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-select.Tpo $(DEPDIR)/libcurl_la-select.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='select.c' object='libcurl_la-select.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-select.lo `test -f 'select.c' || echo '$(srcdir)/'`select.c - -libcurl_la-sendf.lo: sendf.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-sendf.lo -MD -MP -MF $(DEPDIR)/libcurl_la-sendf.Tpo -c -o libcurl_la-sendf.lo `test -f 'sendf.c' || echo '$(srcdir)/'`sendf.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-sendf.Tpo $(DEPDIR)/libcurl_la-sendf.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sendf.c' object='libcurl_la-sendf.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-sendf.lo `test -f 'sendf.c' || echo '$(srcdir)/'`sendf.c - -libcurl_la-setopt.lo: setopt.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-setopt.lo -MD -MP -MF $(DEPDIR)/libcurl_la-setopt.Tpo -c -o libcurl_la-setopt.lo `test -f 'setopt.c' || echo '$(srcdir)/'`setopt.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-setopt.Tpo $(DEPDIR)/libcurl_la-setopt.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='setopt.c' object='libcurl_la-setopt.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-setopt.lo `test -f 'setopt.c' || echo '$(srcdir)/'`setopt.c - -libcurl_la-sha256.lo: sha256.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-sha256.lo -MD -MP -MF $(DEPDIR)/libcurl_la-sha256.Tpo -c -o libcurl_la-sha256.lo `test -f 'sha256.c' || echo '$(srcdir)/'`sha256.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-sha256.Tpo $(DEPDIR)/libcurl_la-sha256.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sha256.c' object='libcurl_la-sha256.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-sha256.lo `test -f 'sha256.c' || echo '$(srcdir)/'`sha256.c - -libcurl_la-share.lo: share.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-share.lo -MD -MP -MF $(DEPDIR)/libcurl_la-share.Tpo -c -o libcurl_la-share.lo `test -f 'share.c' || echo '$(srcdir)/'`share.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-share.Tpo $(DEPDIR)/libcurl_la-share.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='share.c' object='libcurl_la-share.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-share.lo `test -f 'share.c' || echo '$(srcdir)/'`share.c - -libcurl_la-slist.lo: slist.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-slist.lo -MD -MP -MF $(DEPDIR)/libcurl_la-slist.Tpo -c -o libcurl_la-slist.lo `test -f 'slist.c' || echo '$(srcdir)/'`slist.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-slist.Tpo $(DEPDIR)/libcurl_la-slist.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='slist.c' object='libcurl_la-slist.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-slist.lo `test -f 'slist.c' || echo '$(srcdir)/'`slist.c - -libcurl_la-smb.lo: smb.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-smb.lo -MD -MP -MF $(DEPDIR)/libcurl_la-smb.Tpo -c -o libcurl_la-smb.lo `test -f 'smb.c' || echo '$(srcdir)/'`smb.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-smb.Tpo $(DEPDIR)/libcurl_la-smb.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='smb.c' object='libcurl_la-smb.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-smb.lo `test -f 'smb.c' || echo '$(srcdir)/'`smb.c - -libcurl_la-smtp.lo: smtp.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-smtp.lo -MD -MP -MF $(DEPDIR)/libcurl_la-smtp.Tpo -c -o libcurl_la-smtp.lo `test -f 'smtp.c' || echo '$(srcdir)/'`smtp.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-smtp.Tpo $(DEPDIR)/libcurl_la-smtp.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='smtp.c' object='libcurl_la-smtp.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-smtp.lo `test -f 'smtp.c' || echo '$(srcdir)/'`smtp.c - -libcurl_la-socketpair.lo: socketpair.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-socketpair.lo -MD -MP -MF $(DEPDIR)/libcurl_la-socketpair.Tpo -c -o libcurl_la-socketpair.lo `test -f 'socketpair.c' || echo '$(srcdir)/'`socketpair.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-socketpair.Tpo $(DEPDIR)/libcurl_la-socketpair.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socketpair.c' object='libcurl_la-socketpair.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-socketpair.lo `test -f 'socketpair.c' || echo '$(srcdir)/'`socketpair.c - -libcurl_la-socks.lo: socks.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-socks.lo -MD -MP -MF $(DEPDIR)/libcurl_la-socks.Tpo -c -o libcurl_la-socks.lo `test -f 'socks.c' || echo '$(srcdir)/'`socks.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-socks.Tpo $(DEPDIR)/libcurl_la-socks.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socks.c' object='libcurl_la-socks.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-socks.lo `test -f 'socks.c' || echo '$(srcdir)/'`socks.c - -libcurl_la-socks_gssapi.lo: socks_gssapi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-socks_gssapi.lo -MD -MP -MF $(DEPDIR)/libcurl_la-socks_gssapi.Tpo -c -o libcurl_la-socks_gssapi.lo `test -f 'socks_gssapi.c' || echo '$(srcdir)/'`socks_gssapi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-socks_gssapi.Tpo $(DEPDIR)/libcurl_la-socks_gssapi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socks_gssapi.c' object='libcurl_la-socks_gssapi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-socks_gssapi.lo `test -f 'socks_gssapi.c' || echo '$(srcdir)/'`socks_gssapi.c - -libcurl_la-socks_sspi.lo: socks_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-socks_sspi.lo -MD -MP -MF $(DEPDIR)/libcurl_la-socks_sspi.Tpo -c -o libcurl_la-socks_sspi.lo `test -f 'socks_sspi.c' || echo '$(srcdir)/'`socks_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-socks_sspi.Tpo $(DEPDIR)/libcurl_la-socks_sspi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socks_sspi.c' object='libcurl_la-socks_sspi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-socks_sspi.lo `test -f 'socks_sspi.c' || echo '$(srcdir)/'`socks_sspi.c - -libcurl_la-speedcheck.lo: speedcheck.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-speedcheck.lo -MD -MP -MF $(DEPDIR)/libcurl_la-speedcheck.Tpo -c -o libcurl_la-speedcheck.lo `test -f 'speedcheck.c' || echo '$(srcdir)/'`speedcheck.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-speedcheck.Tpo $(DEPDIR)/libcurl_la-speedcheck.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='speedcheck.c' object='libcurl_la-speedcheck.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-speedcheck.lo `test -f 'speedcheck.c' || echo '$(srcdir)/'`speedcheck.c - -libcurl_la-splay.lo: splay.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-splay.lo -MD -MP -MF $(DEPDIR)/libcurl_la-splay.Tpo -c -o libcurl_la-splay.lo `test -f 'splay.c' || echo '$(srcdir)/'`splay.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-splay.Tpo $(DEPDIR)/libcurl_la-splay.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='splay.c' object='libcurl_la-splay.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-splay.lo `test -f 'splay.c' || echo '$(srcdir)/'`splay.c - -libcurl_la-strcase.lo: strcase.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strcase.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strcase.Tpo -c -o libcurl_la-strcase.lo `test -f 'strcase.c' || echo '$(srcdir)/'`strcase.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strcase.Tpo $(DEPDIR)/libcurl_la-strcase.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strcase.c' object='libcurl_la-strcase.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strcase.lo `test -f 'strcase.c' || echo '$(srcdir)/'`strcase.c - -libcurl_la-strdup.lo: strdup.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strdup.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strdup.Tpo -c -o libcurl_la-strdup.lo `test -f 'strdup.c' || echo '$(srcdir)/'`strdup.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strdup.Tpo $(DEPDIR)/libcurl_la-strdup.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strdup.c' object='libcurl_la-strdup.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strdup.lo `test -f 'strdup.c' || echo '$(srcdir)/'`strdup.c - -libcurl_la-strerror.lo: strerror.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strerror.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strerror.Tpo -c -o libcurl_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strerror.Tpo $(DEPDIR)/libcurl_la-strerror.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strerror.c' object='libcurl_la-strerror.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c - -libcurl_la-strtok.lo: strtok.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strtok.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strtok.Tpo -c -o libcurl_la-strtok.lo `test -f 'strtok.c' || echo '$(srcdir)/'`strtok.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strtok.Tpo $(DEPDIR)/libcurl_la-strtok.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strtok.c' object='libcurl_la-strtok.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strtok.lo `test -f 'strtok.c' || echo '$(srcdir)/'`strtok.c - -libcurl_la-strtoofft.lo: strtoofft.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strtoofft.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strtoofft.Tpo -c -o libcurl_la-strtoofft.lo `test -f 'strtoofft.c' || echo '$(srcdir)/'`strtoofft.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strtoofft.Tpo $(DEPDIR)/libcurl_la-strtoofft.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strtoofft.c' object='libcurl_la-strtoofft.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strtoofft.lo `test -f 'strtoofft.c' || echo '$(srcdir)/'`strtoofft.c - -libcurl_la-system_win32.lo: system_win32.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-system_win32.lo -MD -MP -MF $(DEPDIR)/libcurl_la-system_win32.Tpo -c -o libcurl_la-system_win32.lo `test -f 'system_win32.c' || echo '$(srcdir)/'`system_win32.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-system_win32.Tpo $(DEPDIR)/libcurl_la-system_win32.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='system_win32.c' object='libcurl_la-system_win32.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-system_win32.lo `test -f 'system_win32.c' || echo '$(srcdir)/'`system_win32.c - -libcurl_la-telnet.lo: telnet.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-telnet.lo -MD -MP -MF $(DEPDIR)/libcurl_la-telnet.Tpo -c -o libcurl_la-telnet.lo `test -f 'telnet.c' || echo '$(srcdir)/'`telnet.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-telnet.Tpo $(DEPDIR)/libcurl_la-telnet.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='telnet.c' object='libcurl_la-telnet.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-telnet.lo `test -f 'telnet.c' || echo '$(srcdir)/'`telnet.c - -libcurl_la-tftp.lo: tftp.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-tftp.lo -MD -MP -MF $(DEPDIR)/libcurl_la-tftp.Tpo -c -o libcurl_la-tftp.lo `test -f 'tftp.c' || echo '$(srcdir)/'`tftp.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-tftp.Tpo $(DEPDIR)/libcurl_la-tftp.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tftp.c' object='libcurl_la-tftp.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-tftp.lo `test -f 'tftp.c' || echo '$(srcdir)/'`tftp.c - -libcurl_la-timediff.lo: timediff.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-timediff.lo -MD -MP -MF $(DEPDIR)/libcurl_la-timediff.Tpo -c -o libcurl_la-timediff.lo `test -f 'timediff.c' || echo '$(srcdir)/'`timediff.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-timediff.Tpo $(DEPDIR)/libcurl_la-timediff.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timediff.c' object='libcurl_la-timediff.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-timediff.lo `test -f 'timediff.c' || echo '$(srcdir)/'`timediff.c - -libcurl_la-timeval.lo: timeval.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-timeval.lo -MD -MP -MF $(DEPDIR)/libcurl_la-timeval.Tpo -c -o libcurl_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-timeval.Tpo $(DEPDIR)/libcurl_la-timeval.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timeval.c' object='libcurl_la-timeval.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c - -libcurl_la-transfer.lo: transfer.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-transfer.lo -MD -MP -MF $(DEPDIR)/libcurl_la-transfer.Tpo -c -o libcurl_la-transfer.lo `test -f 'transfer.c' || echo '$(srcdir)/'`transfer.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-transfer.Tpo $(DEPDIR)/libcurl_la-transfer.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='transfer.c' object='libcurl_la-transfer.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-transfer.lo `test -f 'transfer.c' || echo '$(srcdir)/'`transfer.c - -libcurl_la-url.lo: url.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-url.lo -MD -MP -MF $(DEPDIR)/libcurl_la-url.Tpo -c -o libcurl_la-url.lo `test -f 'url.c' || echo '$(srcdir)/'`url.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-url.Tpo $(DEPDIR)/libcurl_la-url.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='url.c' object='libcurl_la-url.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-url.lo `test -f 'url.c' || echo '$(srcdir)/'`url.c - -libcurl_la-urlapi.lo: urlapi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-urlapi.lo -MD -MP -MF $(DEPDIR)/libcurl_la-urlapi.Tpo -c -o libcurl_la-urlapi.lo `test -f 'urlapi.c' || echo '$(srcdir)/'`urlapi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-urlapi.Tpo $(DEPDIR)/libcurl_la-urlapi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='urlapi.c' object='libcurl_la-urlapi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-urlapi.lo `test -f 'urlapi.c' || echo '$(srcdir)/'`urlapi.c - -libcurl_la-version.lo: version.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-version.lo -MD -MP -MF $(DEPDIR)/libcurl_la-version.Tpo -c -o libcurl_la-version.lo `test -f 'version.c' || echo '$(srcdir)/'`version.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-version.Tpo $(DEPDIR)/libcurl_la-version.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='version.c' object='libcurl_la-version.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-version.lo `test -f 'version.c' || echo '$(srcdir)/'`version.c - -libcurl_la-version_win32.lo: version_win32.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-version_win32.lo -MD -MP -MF $(DEPDIR)/libcurl_la-version_win32.Tpo -c -o libcurl_la-version_win32.lo `test -f 'version_win32.c' || echo '$(srcdir)/'`version_win32.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-version_win32.Tpo $(DEPDIR)/libcurl_la-version_win32.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='version_win32.c' object='libcurl_la-version_win32.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-version_win32.lo `test -f 'version_win32.c' || echo '$(srcdir)/'`version_win32.c - -libcurl_la-warnless.lo: warnless.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-warnless.lo -MD -MP -MF $(DEPDIR)/libcurl_la-warnless.Tpo -c -o libcurl_la-warnless.lo `test -f 'warnless.c' || echo '$(srcdir)/'`warnless.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-warnless.Tpo $(DEPDIR)/libcurl_la-warnless.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='warnless.c' object='libcurl_la-warnless.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-warnless.lo `test -f 'warnless.c' || echo '$(srcdir)/'`warnless.c - -libcurl_la-wildcard.lo: wildcard.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-wildcard.lo -MD -MP -MF $(DEPDIR)/libcurl_la-wildcard.Tpo -c -o libcurl_la-wildcard.lo `test -f 'wildcard.c' || echo '$(srcdir)/'`wildcard.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-wildcard.Tpo $(DEPDIR)/libcurl_la-wildcard.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wildcard.c' object='libcurl_la-wildcard.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-wildcard.lo `test -f 'wildcard.c' || echo '$(srcdir)/'`wildcard.c - -libcurl_la-ws.lo: ws.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-ws.lo -MD -MP -MF $(DEPDIR)/libcurl_la-ws.Tpo -c -o libcurl_la-ws.lo `test -f 'ws.c' || echo '$(srcdir)/'`ws.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-ws.Tpo $(DEPDIR)/libcurl_la-ws.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ws.c' object='libcurl_la-ws.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-ws.lo `test -f 'ws.c' || echo '$(srcdir)/'`ws.c - -vauth/libcurl_la-cleartext.lo: vauth/cleartext.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-cleartext.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-cleartext.Tpo -c -o vauth/libcurl_la-cleartext.lo `test -f 'vauth/cleartext.c' || echo '$(srcdir)/'`vauth/cleartext.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-cleartext.Tpo vauth/$(DEPDIR)/libcurl_la-cleartext.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/cleartext.c' object='vauth/libcurl_la-cleartext.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-cleartext.lo `test -f 'vauth/cleartext.c' || echo '$(srcdir)/'`vauth/cleartext.c - -vauth/libcurl_la-cram.lo: vauth/cram.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-cram.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-cram.Tpo -c -o vauth/libcurl_la-cram.lo `test -f 'vauth/cram.c' || echo '$(srcdir)/'`vauth/cram.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-cram.Tpo vauth/$(DEPDIR)/libcurl_la-cram.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/cram.c' object='vauth/libcurl_la-cram.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-cram.lo `test -f 'vauth/cram.c' || echo '$(srcdir)/'`vauth/cram.c - -vauth/libcurl_la-digest.lo: vauth/digest.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-digest.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-digest.Tpo -c -o vauth/libcurl_la-digest.lo `test -f 'vauth/digest.c' || echo '$(srcdir)/'`vauth/digest.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-digest.Tpo vauth/$(DEPDIR)/libcurl_la-digest.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/digest.c' object='vauth/libcurl_la-digest.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-digest.lo `test -f 'vauth/digest.c' || echo '$(srcdir)/'`vauth/digest.c - -vauth/libcurl_la-digest_sspi.lo: vauth/digest_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-digest_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-digest_sspi.Tpo -c -o vauth/libcurl_la-digest_sspi.lo `test -f 'vauth/digest_sspi.c' || echo '$(srcdir)/'`vauth/digest_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-digest_sspi.Tpo vauth/$(DEPDIR)/libcurl_la-digest_sspi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/digest_sspi.c' object='vauth/libcurl_la-digest_sspi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-digest_sspi.lo `test -f 'vauth/digest_sspi.c' || echo '$(srcdir)/'`vauth/digest_sspi.c - -vauth/libcurl_la-gsasl.lo: vauth/gsasl.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-gsasl.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-gsasl.Tpo -c -o vauth/libcurl_la-gsasl.lo `test -f 'vauth/gsasl.c' || echo '$(srcdir)/'`vauth/gsasl.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-gsasl.Tpo vauth/$(DEPDIR)/libcurl_la-gsasl.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/gsasl.c' object='vauth/libcurl_la-gsasl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-gsasl.lo `test -f 'vauth/gsasl.c' || echo '$(srcdir)/'`vauth/gsasl.c - -vauth/libcurl_la-krb5_gssapi.lo: vauth/krb5_gssapi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-krb5_gssapi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Tpo -c -o vauth/libcurl_la-krb5_gssapi.lo `test -f 'vauth/krb5_gssapi.c' || echo '$(srcdir)/'`vauth/krb5_gssapi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Tpo vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/krb5_gssapi.c' object='vauth/libcurl_la-krb5_gssapi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-krb5_gssapi.lo `test -f 'vauth/krb5_gssapi.c' || echo '$(srcdir)/'`vauth/krb5_gssapi.c - -vauth/libcurl_la-krb5_sspi.lo: vauth/krb5_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-krb5_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Tpo -c -o vauth/libcurl_la-krb5_sspi.lo `test -f 'vauth/krb5_sspi.c' || echo '$(srcdir)/'`vauth/krb5_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Tpo vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/krb5_sspi.c' object='vauth/libcurl_la-krb5_sspi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-krb5_sspi.lo `test -f 'vauth/krb5_sspi.c' || echo '$(srcdir)/'`vauth/krb5_sspi.c - -vauth/libcurl_la-ntlm.lo: vauth/ntlm.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-ntlm.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-ntlm.Tpo -c -o vauth/libcurl_la-ntlm.lo `test -f 'vauth/ntlm.c' || echo '$(srcdir)/'`vauth/ntlm.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-ntlm.Tpo vauth/$(DEPDIR)/libcurl_la-ntlm.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/ntlm.c' object='vauth/libcurl_la-ntlm.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-ntlm.lo `test -f 'vauth/ntlm.c' || echo '$(srcdir)/'`vauth/ntlm.c - -vauth/libcurl_la-ntlm_sspi.lo: vauth/ntlm_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-ntlm_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Tpo -c -o vauth/libcurl_la-ntlm_sspi.lo `test -f 'vauth/ntlm_sspi.c' || echo '$(srcdir)/'`vauth/ntlm_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Tpo vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/ntlm_sspi.c' object='vauth/libcurl_la-ntlm_sspi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-ntlm_sspi.lo `test -f 'vauth/ntlm_sspi.c' || echo '$(srcdir)/'`vauth/ntlm_sspi.c - -vauth/libcurl_la-oauth2.lo: vauth/oauth2.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-oauth2.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-oauth2.Tpo -c -o vauth/libcurl_la-oauth2.lo `test -f 'vauth/oauth2.c' || echo '$(srcdir)/'`vauth/oauth2.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-oauth2.Tpo vauth/$(DEPDIR)/libcurl_la-oauth2.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/oauth2.c' object='vauth/libcurl_la-oauth2.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-oauth2.lo `test -f 'vauth/oauth2.c' || echo '$(srcdir)/'`vauth/oauth2.c - -vauth/libcurl_la-spnego_gssapi.lo: vauth/spnego_gssapi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-spnego_gssapi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Tpo -c -o vauth/libcurl_la-spnego_gssapi.lo `test -f 'vauth/spnego_gssapi.c' || echo '$(srcdir)/'`vauth/spnego_gssapi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Tpo vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/spnego_gssapi.c' object='vauth/libcurl_la-spnego_gssapi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-spnego_gssapi.lo `test -f 'vauth/spnego_gssapi.c' || echo '$(srcdir)/'`vauth/spnego_gssapi.c - -vauth/libcurl_la-spnego_sspi.lo: vauth/spnego_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-spnego_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Tpo -c -o vauth/libcurl_la-spnego_sspi.lo `test -f 'vauth/spnego_sspi.c' || echo '$(srcdir)/'`vauth/spnego_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Tpo vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/spnego_sspi.c' object='vauth/libcurl_la-spnego_sspi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-spnego_sspi.lo `test -f 'vauth/spnego_sspi.c' || echo '$(srcdir)/'`vauth/spnego_sspi.c - -vauth/libcurl_la-vauth.lo: vauth/vauth.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-vauth.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-vauth.Tpo -c -o vauth/libcurl_la-vauth.lo `test -f 'vauth/vauth.c' || echo '$(srcdir)/'`vauth/vauth.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-vauth.Tpo vauth/$(DEPDIR)/libcurl_la-vauth.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/vauth.c' object='vauth/libcurl_la-vauth.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-vauth.lo `test -f 'vauth/vauth.c' || echo '$(srcdir)/'`vauth/vauth.c - -vtls/libcurl_la-bearssl.lo: vtls/bearssl.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-bearssl.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-bearssl.Tpo -c -o vtls/libcurl_la-bearssl.lo `test -f 'vtls/bearssl.c' || echo '$(srcdir)/'`vtls/bearssl.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-bearssl.Tpo vtls/$(DEPDIR)/libcurl_la-bearssl.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/bearssl.c' object='vtls/libcurl_la-bearssl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-bearssl.lo `test -f 'vtls/bearssl.c' || echo '$(srcdir)/'`vtls/bearssl.c - -vtls/libcurl_la-gskit.lo: vtls/gskit.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-gskit.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-gskit.Tpo -c -o vtls/libcurl_la-gskit.lo `test -f 'vtls/gskit.c' || echo '$(srcdir)/'`vtls/gskit.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-gskit.Tpo vtls/$(DEPDIR)/libcurl_la-gskit.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/gskit.c' object='vtls/libcurl_la-gskit.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-gskit.lo `test -f 'vtls/gskit.c' || echo '$(srcdir)/'`vtls/gskit.c - -vtls/libcurl_la-gtls.lo: vtls/gtls.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-gtls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-gtls.Tpo -c -o vtls/libcurl_la-gtls.lo `test -f 'vtls/gtls.c' || echo '$(srcdir)/'`vtls/gtls.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-gtls.Tpo vtls/$(DEPDIR)/libcurl_la-gtls.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/gtls.c' object='vtls/libcurl_la-gtls.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-gtls.lo `test -f 'vtls/gtls.c' || echo '$(srcdir)/'`vtls/gtls.c - -vtls/libcurl_la-hostcheck.lo: vtls/hostcheck.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-hostcheck.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-hostcheck.Tpo -c -o vtls/libcurl_la-hostcheck.lo `test -f 'vtls/hostcheck.c' || echo '$(srcdir)/'`vtls/hostcheck.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-hostcheck.Tpo vtls/$(DEPDIR)/libcurl_la-hostcheck.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/hostcheck.c' object='vtls/libcurl_la-hostcheck.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-hostcheck.lo `test -f 'vtls/hostcheck.c' || echo '$(srcdir)/'`vtls/hostcheck.c - -vtls/libcurl_la-keylog.lo: vtls/keylog.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-keylog.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-keylog.Tpo -c -o vtls/libcurl_la-keylog.lo `test -f 'vtls/keylog.c' || echo '$(srcdir)/'`vtls/keylog.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-keylog.Tpo vtls/$(DEPDIR)/libcurl_la-keylog.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/keylog.c' object='vtls/libcurl_la-keylog.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-keylog.lo `test -f 'vtls/keylog.c' || echo '$(srcdir)/'`vtls/keylog.c - -vtls/libcurl_la-mbedtls.lo: vtls/mbedtls.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-mbedtls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-mbedtls.Tpo -c -o vtls/libcurl_la-mbedtls.lo `test -f 'vtls/mbedtls.c' || echo '$(srcdir)/'`vtls/mbedtls.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-mbedtls.Tpo vtls/$(DEPDIR)/libcurl_la-mbedtls.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/mbedtls.c' object='vtls/libcurl_la-mbedtls.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-mbedtls.lo `test -f 'vtls/mbedtls.c' || echo '$(srcdir)/'`vtls/mbedtls.c - -vtls/libcurl_la-mbedtls_threadlock.lo: vtls/mbedtls_threadlock.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-mbedtls_threadlock.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Tpo -c -o vtls/libcurl_la-mbedtls_threadlock.lo `test -f 'vtls/mbedtls_threadlock.c' || echo '$(srcdir)/'`vtls/mbedtls_threadlock.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Tpo vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/mbedtls_threadlock.c' object='vtls/libcurl_la-mbedtls_threadlock.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-mbedtls_threadlock.lo `test -f 'vtls/mbedtls_threadlock.c' || echo '$(srcdir)/'`vtls/mbedtls_threadlock.c - -vtls/libcurl_la-nss.lo: vtls/nss.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-nss.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-nss.Tpo -c -o vtls/libcurl_la-nss.lo `test -f 'vtls/nss.c' || echo '$(srcdir)/'`vtls/nss.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-nss.Tpo vtls/$(DEPDIR)/libcurl_la-nss.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/nss.c' object='vtls/libcurl_la-nss.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-nss.lo `test -f 'vtls/nss.c' || echo '$(srcdir)/'`vtls/nss.c - -vtls/libcurl_la-openssl.lo: vtls/openssl.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-openssl.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-openssl.Tpo -c -o vtls/libcurl_la-openssl.lo `test -f 'vtls/openssl.c' || echo '$(srcdir)/'`vtls/openssl.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-openssl.Tpo vtls/$(DEPDIR)/libcurl_la-openssl.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/openssl.c' object='vtls/libcurl_la-openssl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-openssl.lo `test -f 'vtls/openssl.c' || echo '$(srcdir)/'`vtls/openssl.c - -vtls/libcurl_la-rustls.lo: vtls/rustls.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-rustls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-rustls.Tpo -c -o vtls/libcurl_la-rustls.lo `test -f 'vtls/rustls.c' || echo '$(srcdir)/'`vtls/rustls.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-rustls.Tpo vtls/$(DEPDIR)/libcurl_la-rustls.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/rustls.c' object='vtls/libcurl_la-rustls.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-rustls.lo `test -f 'vtls/rustls.c' || echo '$(srcdir)/'`vtls/rustls.c - -vtls/libcurl_la-schannel.lo: vtls/schannel.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-schannel.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-schannel.Tpo -c -o vtls/libcurl_la-schannel.lo `test -f 'vtls/schannel.c' || echo '$(srcdir)/'`vtls/schannel.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-schannel.Tpo vtls/$(DEPDIR)/libcurl_la-schannel.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/schannel.c' object='vtls/libcurl_la-schannel.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-schannel.lo `test -f 'vtls/schannel.c' || echo '$(srcdir)/'`vtls/schannel.c - -vtls/libcurl_la-schannel_verify.lo: vtls/schannel_verify.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-schannel_verify.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-schannel_verify.Tpo -c -o vtls/libcurl_la-schannel_verify.lo `test -f 'vtls/schannel_verify.c' || echo '$(srcdir)/'`vtls/schannel_verify.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-schannel_verify.Tpo vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/schannel_verify.c' object='vtls/libcurl_la-schannel_verify.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-schannel_verify.lo `test -f 'vtls/schannel_verify.c' || echo '$(srcdir)/'`vtls/schannel_verify.c - -vtls/libcurl_la-sectransp.lo: vtls/sectransp.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-sectransp.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-sectransp.Tpo -c -o vtls/libcurl_la-sectransp.lo `test -f 'vtls/sectransp.c' || echo '$(srcdir)/'`vtls/sectransp.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-sectransp.Tpo vtls/$(DEPDIR)/libcurl_la-sectransp.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/sectransp.c' object='vtls/libcurl_la-sectransp.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-sectransp.lo `test -f 'vtls/sectransp.c' || echo '$(srcdir)/'`vtls/sectransp.c - -vtls/libcurl_la-vtls.lo: vtls/vtls.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-vtls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-vtls.Tpo -c -o vtls/libcurl_la-vtls.lo `test -f 'vtls/vtls.c' || echo '$(srcdir)/'`vtls/vtls.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-vtls.Tpo vtls/$(DEPDIR)/libcurl_la-vtls.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/vtls.c' object='vtls/libcurl_la-vtls.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-vtls.lo `test -f 'vtls/vtls.c' || echo '$(srcdir)/'`vtls/vtls.c - -vtls/libcurl_la-wolfssl.lo: vtls/wolfssl.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-wolfssl.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-wolfssl.Tpo -c -o vtls/libcurl_la-wolfssl.lo `test -f 'vtls/wolfssl.c' || echo '$(srcdir)/'`vtls/wolfssl.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-wolfssl.Tpo vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/wolfssl.c' object='vtls/libcurl_la-wolfssl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-wolfssl.lo `test -f 'vtls/wolfssl.c' || echo '$(srcdir)/'`vtls/wolfssl.c - -vtls/libcurl_la-x509asn1.lo: vtls/x509asn1.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-x509asn1.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-x509asn1.Tpo -c -o vtls/libcurl_la-x509asn1.lo `test -f 'vtls/x509asn1.c' || echo '$(srcdir)/'`vtls/x509asn1.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-x509asn1.Tpo vtls/$(DEPDIR)/libcurl_la-x509asn1.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/x509asn1.c' object='vtls/libcurl_la-x509asn1.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-x509asn1.lo `test -f 'vtls/x509asn1.c' || echo '$(srcdir)/'`vtls/x509asn1.c - -vquic/libcurl_la-msh3.lo: vquic/msh3.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vquic/libcurl_la-msh3.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-msh3.Tpo -c -o vquic/libcurl_la-msh3.lo `test -f 'vquic/msh3.c' || echo '$(srcdir)/'`vquic/msh3.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-msh3.Tpo vquic/$(DEPDIR)/libcurl_la-msh3.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/msh3.c' object='vquic/libcurl_la-msh3.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurl_la-msh3.lo `test -f 'vquic/msh3.c' || echo '$(srcdir)/'`vquic/msh3.c - -vquic/libcurl_la-ngtcp2.lo: vquic/ngtcp2.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vquic/libcurl_la-ngtcp2.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-ngtcp2.Tpo -c -o vquic/libcurl_la-ngtcp2.lo `test -f 'vquic/ngtcp2.c' || echo '$(srcdir)/'`vquic/ngtcp2.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-ngtcp2.Tpo vquic/$(DEPDIR)/libcurl_la-ngtcp2.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/ngtcp2.c' object='vquic/libcurl_la-ngtcp2.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurl_la-ngtcp2.lo `test -f 'vquic/ngtcp2.c' || echo '$(srcdir)/'`vquic/ngtcp2.c - -vquic/libcurl_la-quiche.lo: vquic/quiche.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vquic/libcurl_la-quiche.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-quiche.Tpo -c -o vquic/libcurl_la-quiche.lo `test -f 'vquic/quiche.c' || echo '$(srcdir)/'`vquic/quiche.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-quiche.Tpo vquic/$(DEPDIR)/libcurl_la-quiche.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/quiche.c' object='vquic/libcurl_la-quiche.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurl_la-quiche.lo `test -f 'vquic/quiche.c' || echo '$(srcdir)/'`vquic/quiche.c - -vquic/libcurl_la-vquic.lo: vquic/vquic.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vquic/libcurl_la-vquic.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-vquic.Tpo -c -o vquic/libcurl_la-vquic.lo `test -f 'vquic/vquic.c' || echo '$(srcdir)/'`vquic/vquic.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-vquic.Tpo vquic/$(DEPDIR)/libcurl_la-vquic.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/vquic.c' object='vquic/libcurl_la-vquic.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurl_la-vquic.lo `test -f 'vquic/vquic.c' || echo '$(srcdir)/'`vquic/vquic.c - -vssh/libcurl_la-libssh.lo: vssh/libssh.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vssh/libcurl_la-libssh.lo -MD -MP -MF vssh/$(DEPDIR)/libcurl_la-libssh.Tpo -c -o vssh/libcurl_la-libssh.lo `test -f 'vssh/libssh.c' || echo '$(srcdir)/'`vssh/libssh.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vssh/$(DEPDIR)/libcurl_la-libssh.Tpo vssh/$(DEPDIR)/libcurl_la-libssh.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vssh/libssh.c' object='vssh/libcurl_la-libssh.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vssh/libcurl_la-libssh.lo `test -f 'vssh/libssh.c' || echo '$(srcdir)/'`vssh/libssh.c - -vssh/libcurl_la-libssh2.lo: vssh/libssh2.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vssh/libcurl_la-libssh2.lo -MD -MP -MF vssh/$(DEPDIR)/libcurl_la-libssh2.Tpo -c -o vssh/libcurl_la-libssh2.lo `test -f 'vssh/libssh2.c' || echo '$(srcdir)/'`vssh/libssh2.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vssh/$(DEPDIR)/libcurl_la-libssh2.Tpo vssh/$(DEPDIR)/libcurl_la-libssh2.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vssh/libssh2.c' object='vssh/libcurl_la-libssh2.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vssh/libcurl_la-libssh2.lo `test -f 'vssh/libssh2.c' || echo '$(srcdir)/'`vssh/libssh2.c - -vssh/libcurl_la-wolfssh.lo: vssh/wolfssh.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vssh/libcurl_la-wolfssh.lo -MD -MP -MF vssh/$(DEPDIR)/libcurl_la-wolfssh.Tpo -c -o vssh/libcurl_la-wolfssh.lo `test -f 'vssh/wolfssh.c' || echo '$(srcdir)/'`vssh/wolfssh.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vssh/$(DEPDIR)/libcurl_la-wolfssh.Tpo vssh/$(DEPDIR)/libcurl_la-wolfssh.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vssh/wolfssh.c' object='vssh/libcurl_la-wolfssh.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vssh/libcurl_la-wolfssh.lo `test -f 'vssh/wolfssh.c' || echo '$(srcdir)/'`vssh/wolfssh.c - -libcurlu_la-altsvc.lo: altsvc.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-altsvc.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-altsvc.Tpo -c -o libcurlu_la-altsvc.lo `test -f 'altsvc.c' || echo '$(srcdir)/'`altsvc.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-altsvc.Tpo $(DEPDIR)/libcurlu_la-altsvc.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='altsvc.c' object='libcurlu_la-altsvc.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-altsvc.lo `test -f 'altsvc.c' || echo '$(srcdir)/'`altsvc.c - -libcurlu_la-amigaos.lo: amigaos.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-amigaos.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-amigaos.Tpo -c -o libcurlu_la-amigaos.lo `test -f 'amigaos.c' || echo '$(srcdir)/'`amigaos.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-amigaos.Tpo $(DEPDIR)/libcurlu_la-amigaos.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='amigaos.c' object='libcurlu_la-amigaos.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-amigaos.lo `test -f 'amigaos.c' || echo '$(srcdir)/'`amigaos.c - -libcurlu_la-asyn-ares.lo: asyn-ares.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-asyn-ares.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-asyn-ares.Tpo -c -o libcurlu_la-asyn-ares.lo `test -f 'asyn-ares.c' || echo '$(srcdir)/'`asyn-ares.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-asyn-ares.Tpo $(DEPDIR)/libcurlu_la-asyn-ares.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asyn-ares.c' object='libcurlu_la-asyn-ares.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-asyn-ares.lo `test -f 'asyn-ares.c' || echo '$(srcdir)/'`asyn-ares.c - -libcurlu_la-asyn-thread.lo: asyn-thread.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-asyn-thread.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-asyn-thread.Tpo -c -o libcurlu_la-asyn-thread.lo `test -f 'asyn-thread.c' || echo '$(srcdir)/'`asyn-thread.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-asyn-thread.Tpo $(DEPDIR)/libcurlu_la-asyn-thread.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asyn-thread.c' object='libcurlu_la-asyn-thread.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-asyn-thread.lo `test -f 'asyn-thread.c' || echo '$(srcdir)/'`asyn-thread.c - -libcurlu_la-base64.lo: base64.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-base64.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-base64.Tpo -c -o libcurlu_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-base64.Tpo $(DEPDIR)/libcurlu_la-base64.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='base64.c' object='libcurlu_la-base64.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c - -libcurlu_la-bufref.lo: bufref.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-bufref.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-bufref.Tpo -c -o libcurlu_la-bufref.lo `test -f 'bufref.c' || echo '$(srcdir)/'`bufref.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-bufref.Tpo $(DEPDIR)/libcurlu_la-bufref.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bufref.c' object='libcurlu_la-bufref.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-bufref.lo `test -f 'bufref.c' || echo '$(srcdir)/'`bufref.c - -libcurlu_la-c-hyper.lo: c-hyper.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-c-hyper.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-c-hyper.Tpo -c -o libcurlu_la-c-hyper.lo `test -f 'c-hyper.c' || echo '$(srcdir)/'`c-hyper.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-c-hyper.Tpo $(DEPDIR)/libcurlu_la-c-hyper.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='c-hyper.c' object='libcurlu_la-c-hyper.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-c-hyper.lo `test -f 'c-hyper.c' || echo '$(srcdir)/'`c-hyper.c - -libcurlu_la-conncache.lo: conncache.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-conncache.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-conncache.Tpo -c -o libcurlu_la-conncache.lo `test -f 'conncache.c' || echo '$(srcdir)/'`conncache.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-conncache.Tpo $(DEPDIR)/libcurlu_la-conncache.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conncache.c' object='libcurlu_la-conncache.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-conncache.lo `test -f 'conncache.c' || echo '$(srcdir)/'`conncache.c - -libcurlu_la-connect.lo: connect.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-connect.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-connect.Tpo -c -o libcurlu_la-connect.lo `test -f 'connect.c' || echo '$(srcdir)/'`connect.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-connect.Tpo $(DEPDIR)/libcurlu_la-connect.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='connect.c' object='libcurlu_la-connect.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-connect.lo `test -f 'connect.c' || echo '$(srcdir)/'`connect.c - -libcurlu_la-content_encoding.lo: content_encoding.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-content_encoding.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-content_encoding.Tpo -c -o libcurlu_la-content_encoding.lo `test -f 'content_encoding.c' || echo '$(srcdir)/'`content_encoding.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-content_encoding.Tpo $(DEPDIR)/libcurlu_la-content_encoding.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='content_encoding.c' object='libcurlu_la-content_encoding.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-content_encoding.lo `test -f 'content_encoding.c' || echo '$(srcdir)/'`content_encoding.c - -libcurlu_la-cookie.lo: cookie.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-cookie.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-cookie.Tpo -c -o libcurlu_la-cookie.lo `test -f 'cookie.c' || echo '$(srcdir)/'`cookie.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-cookie.Tpo $(DEPDIR)/libcurlu_la-cookie.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cookie.c' object='libcurlu_la-cookie.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-cookie.lo `test -f 'cookie.c' || echo '$(srcdir)/'`cookie.c - -libcurlu_la-curl_addrinfo.lo: curl_addrinfo.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_addrinfo.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_addrinfo.Tpo -c -o libcurlu_la-curl_addrinfo.lo `test -f 'curl_addrinfo.c' || echo '$(srcdir)/'`curl_addrinfo.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_addrinfo.Tpo $(DEPDIR)/libcurlu_la-curl_addrinfo.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_addrinfo.c' object='libcurlu_la-curl_addrinfo.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_addrinfo.lo `test -f 'curl_addrinfo.c' || echo '$(srcdir)/'`curl_addrinfo.c - -libcurlu_la-curl_des.lo: curl_des.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_des.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_des.Tpo -c -o libcurlu_la-curl_des.lo `test -f 'curl_des.c' || echo '$(srcdir)/'`curl_des.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_des.Tpo $(DEPDIR)/libcurlu_la-curl_des.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_des.c' object='libcurlu_la-curl_des.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_des.lo `test -f 'curl_des.c' || echo '$(srcdir)/'`curl_des.c - -libcurlu_la-curl_endian.lo: curl_endian.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_endian.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_endian.Tpo -c -o libcurlu_la-curl_endian.lo `test -f 'curl_endian.c' || echo '$(srcdir)/'`curl_endian.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_endian.Tpo $(DEPDIR)/libcurlu_la-curl_endian.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_endian.c' object='libcurlu_la-curl_endian.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_endian.lo `test -f 'curl_endian.c' || echo '$(srcdir)/'`curl_endian.c - -libcurlu_la-curl_fnmatch.lo: curl_fnmatch.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_fnmatch.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_fnmatch.Tpo -c -o libcurlu_la-curl_fnmatch.lo `test -f 'curl_fnmatch.c' || echo '$(srcdir)/'`curl_fnmatch.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_fnmatch.Tpo $(DEPDIR)/libcurlu_la-curl_fnmatch.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_fnmatch.c' object='libcurlu_la-curl_fnmatch.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_fnmatch.lo `test -f 'curl_fnmatch.c' || echo '$(srcdir)/'`curl_fnmatch.c - -libcurlu_la-curl_get_line.lo: curl_get_line.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_get_line.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_get_line.Tpo -c -o libcurlu_la-curl_get_line.lo `test -f 'curl_get_line.c' || echo '$(srcdir)/'`curl_get_line.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_get_line.Tpo $(DEPDIR)/libcurlu_la-curl_get_line.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_get_line.c' object='libcurlu_la-curl_get_line.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_get_line.lo `test -f 'curl_get_line.c' || echo '$(srcdir)/'`curl_get_line.c - -libcurlu_la-curl_gethostname.lo: curl_gethostname.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_gethostname.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_gethostname.Tpo -c -o libcurlu_la-curl_gethostname.lo `test -f 'curl_gethostname.c' || echo '$(srcdir)/'`curl_gethostname.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_gethostname.Tpo $(DEPDIR)/libcurlu_la-curl_gethostname.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_gethostname.c' object='libcurlu_la-curl_gethostname.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_gethostname.lo `test -f 'curl_gethostname.c' || echo '$(srcdir)/'`curl_gethostname.c - -libcurlu_la-curl_gssapi.lo: curl_gssapi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_gssapi.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_gssapi.Tpo -c -o libcurlu_la-curl_gssapi.lo `test -f 'curl_gssapi.c' || echo '$(srcdir)/'`curl_gssapi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_gssapi.Tpo $(DEPDIR)/libcurlu_la-curl_gssapi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_gssapi.c' object='libcurlu_la-curl_gssapi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_gssapi.lo `test -f 'curl_gssapi.c' || echo '$(srcdir)/'`curl_gssapi.c - -libcurlu_la-curl_memrchr.lo: curl_memrchr.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_memrchr.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_memrchr.Tpo -c -o libcurlu_la-curl_memrchr.lo `test -f 'curl_memrchr.c' || echo '$(srcdir)/'`curl_memrchr.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_memrchr.Tpo $(DEPDIR)/libcurlu_la-curl_memrchr.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_memrchr.c' object='libcurlu_la-curl_memrchr.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_memrchr.lo `test -f 'curl_memrchr.c' || echo '$(srcdir)/'`curl_memrchr.c - -libcurlu_la-curl_multibyte.lo: curl_multibyte.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_multibyte.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_multibyte.Tpo -c -o libcurlu_la-curl_multibyte.lo `test -f 'curl_multibyte.c' || echo '$(srcdir)/'`curl_multibyte.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_multibyte.Tpo $(DEPDIR)/libcurlu_la-curl_multibyte.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_multibyte.c' object='libcurlu_la-curl_multibyte.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_multibyte.lo `test -f 'curl_multibyte.c' || echo '$(srcdir)/'`curl_multibyte.c - -libcurlu_la-curl_ntlm_core.lo: curl_ntlm_core.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_ntlm_core.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_ntlm_core.Tpo -c -o libcurlu_la-curl_ntlm_core.lo `test -f 'curl_ntlm_core.c' || echo '$(srcdir)/'`curl_ntlm_core.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_ntlm_core.Tpo $(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_ntlm_core.c' object='libcurlu_la-curl_ntlm_core.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_ntlm_core.lo `test -f 'curl_ntlm_core.c' || echo '$(srcdir)/'`curl_ntlm_core.c - -libcurlu_la-curl_ntlm_wb.lo: curl_ntlm_wb.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_ntlm_wb.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_ntlm_wb.Tpo -c -o libcurlu_la-curl_ntlm_wb.lo `test -f 'curl_ntlm_wb.c' || echo '$(srcdir)/'`curl_ntlm_wb.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_ntlm_wb.Tpo $(DEPDIR)/libcurlu_la-curl_ntlm_wb.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_ntlm_wb.c' object='libcurlu_la-curl_ntlm_wb.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_ntlm_wb.lo `test -f 'curl_ntlm_wb.c' || echo '$(srcdir)/'`curl_ntlm_wb.c - -libcurlu_la-curl_path.lo: curl_path.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_path.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_path.Tpo -c -o libcurlu_la-curl_path.lo `test -f 'curl_path.c' || echo '$(srcdir)/'`curl_path.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_path.Tpo $(DEPDIR)/libcurlu_la-curl_path.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_path.c' object='libcurlu_la-curl_path.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_path.lo `test -f 'curl_path.c' || echo '$(srcdir)/'`curl_path.c - -libcurlu_la-curl_range.lo: curl_range.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_range.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_range.Tpo -c -o libcurlu_la-curl_range.lo `test -f 'curl_range.c' || echo '$(srcdir)/'`curl_range.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_range.Tpo $(DEPDIR)/libcurlu_la-curl_range.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_range.c' object='libcurlu_la-curl_range.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_range.lo `test -f 'curl_range.c' || echo '$(srcdir)/'`curl_range.c - -libcurlu_la-curl_rtmp.lo: curl_rtmp.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_rtmp.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_rtmp.Tpo -c -o libcurlu_la-curl_rtmp.lo `test -f 'curl_rtmp.c' || echo '$(srcdir)/'`curl_rtmp.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_rtmp.Tpo $(DEPDIR)/libcurlu_la-curl_rtmp.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_rtmp.c' object='libcurlu_la-curl_rtmp.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_rtmp.lo `test -f 'curl_rtmp.c' || echo '$(srcdir)/'`curl_rtmp.c - -libcurlu_la-curl_sasl.lo: curl_sasl.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_sasl.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_sasl.Tpo -c -o libcurlu_la-curl_sasl.lo `test -f 'curl_sasl.c' || echo '$(srcdir)/'`curl_sasl.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_sasl.Tpo $(DEPDIR)/libcurlu_la-curl_sasl.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_sasl.c' object='libcurlu_la-curl_sasl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_sasl.lo `test -f 'curl_sasl.c' || echo '$(srcdir)/'`curl_sasl.c - -libcurlu_la-curl_sspi.lo: curl_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_sspi.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_sspi.Tpo -c -o libcurlu_la-curl_sspi.lo `test -f 'curl_sspi.c' || echo '$(srcdir)/'`curl_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_sspi.Tpo $(DEPDIR)/libcurlu_la-curl_sspi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_sspi.c' object='libcurlu_la-curl_sspi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_sspi.lo `test -f 'curl_sspi.c' || echo '$(srcdir)/'`curl_sspi.c - -libcurlu_la-curl_threads.lo: curl_threads.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_threads.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_threads.Tpo -c -o libcurlu_la-curl_threads.lo `test -f 'curl_threads.c' || echo '$(srcdir)/'`curl_threads.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_threads.Tpo $(DEPDIR)/libcurlu_la-curl_threads.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_threads.c' object='libcurlu_la-curl_threads.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_threads.lo `test -f 'curl_threads.c' || echo '$(srcdir)/'`curl_threads.c - -libcurlu_la-dict.lo: dict.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-dict.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-dict.Tpo -c -o libcurlu_la-dict.lo `test -f 'dict.c' || echo '$(srcdir)/'`dict.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-dict.Tpo $(DEPDIR)/libcurlu_la-dict.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dict.c' object='libcurlu_la-dict.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-dict.lo `test -f 'dict.c' || echo '$(srcdir)/'`dict.c - -libcurlu_la-doh.lo: doh.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-doh.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-doh.Tpo -c -o libcurlu_la-doh.lo `test -f 'doh.c' || echo '$(srcdir)/'`doh.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-doh.Tpo $(DEPDIR)/libcurlu_la-doh.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='doh.c' object='libcurlu_la-doh.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-doh.lo `test -f 'doh.c' || echo '$(srcdir)/'`doh.c - -libcurlu_la-dynbuf.lo: dynbuf.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-dynbuf.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-dynbuf.Tpo -c -o libcurlu_la-dynbuf.lo `test -f 'dynbuf.c' || echo '$(srcdir)/'`dynbuf.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-dynbuf.Tpo $(DEPDIR)/libcurlu_la-dynbuf.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dynbuf.c' object='libcurlu_la-dynbuf.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-dynbuf.lo `test -f 'dynbuf.c' || echo '$(srcdir)/'`dynbuf.c - -libcurlu_la-easy.lo: easy.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-easy.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-easy.Tpo -c -o libcurlu_la-easy.lo `test -f 'easy.c' || echo '$(srcdir)/'`easy.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-easy.Tpo $(DEPDIR)/libcurlu_la-easy.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='easy.c' object='libcurlu_la-easy.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-easy.lo `test -f 'easy.c' || echo '$(srcdir)/'`easy.c - -libcurlu_la-easygetopt.lo: easygetopt.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-easygetopt.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-easygetopt.Tpo -c -o libcurlu_la-easygetopt.lo `test -f 'easygetopt.c' || echo '$(srcdir)/'`easygetopt.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-easygetopt.Tpo $(DEPDIR)/libcurlu_la-easygetopt.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='easygetopt.c' object='libcurlu_la-easygetopt.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-easygetopt.lo `test -f 'easygetopt.c' || echo '$(srcdir)/'`easygetopt.c - -libcurlu_la-easyoptions.lo: easyoptions.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-easyoptions.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-easyoptions.Tpo -c -o libcurlu_la-easyoptions.lo `test -f 'easyoptions.c' || echo '$(srcdir)/'`easyoptions.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-easyoptions.Tpo $(DEPDIR)/libcurlu_la-easyoptions.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='easyoptions.c' object='libcurlu_la-easyoptions.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-easyoptions.lo `test -f 'easyoptions.c' || echo '$(srcdir)/'`easyoptions.c - -libcurlu_la-escape.lo: escape.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-escape.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-escape.Tpo -c -o libcurlu_la-escape.lo `test -f 'escape.c' || echo '$(srcdir)/'`escape.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-escape.Tpo $(DEPDIR)/libcurlu_la-escape.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='escape.c' object='libcurlu_la-escape.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-escape.lo `test -f 'escape.c' || echo '$(srcdir)/'`escape.c - -libcurlu_la-file.lo: file.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-file.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-file.Tpo -c -o libcurlu_la-file.lo `test -f 'file.c' || echo '$(srcdir)/'`file.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-file.Tpo $(DEPDIR)/libcurlu_la-file.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file.c' object='libcurlu_la-file.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-file.lo `test -f 'file.c' || echo '$(srcdir)/'`file.c - -libcurlu_la-fileinfo.lo: fileinfo.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-fileinfo.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-fileinfo.Tpo -c -o libcurlu_la-fileinfo.lo `test -f 'fileinfo.c' || echo '$(srcdir)/'`fileinfo.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-fileinfo.Tpo $(DEPDIR)/libcurlu_la-fileinfo.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fileinfo.c' object='libcurlu_la-fileinfo.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-fileinfo.lo `test -f 'fileinfo.c' || echo '$(srcdir)/'`fileinfo.c - -libcurlu_la-fopen.lo: fopen.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-fopen.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-fopen.Tpo -c -o libcurlu_la-fopen.lo `test -f 'fopen.c' || echo '$(srcdir)/'`fopen.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-fopen.Tpo $(DEPDIR)/libcurlu_la-fopen.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fopen.c' object='libcurlu_la-fopen.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-fopen.lo `test -f 'fopen.c' || echo '$(srcdir)/'`fopen.c - -libcurlu_la-formdata.lo: formdata.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-formdata.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-formdata.Tpo -c -o libcurlu_la-formdata.lo `test -f 'formdata.c' || echo '$(srcdir)/'`formdata.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-formdata.Tpo $(DEPDIR)/libcurlu_la-formdata.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='formdata.c' object='libcurlu_la-formdata.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-formdata.lo `test -f 'formdata.c' || echo '$(srcdir)/'`formdata.c - -libcurlu_la-ftp.lo: ftp.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-ftp.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-ftp.Tpo -c -o libcurlu_la-ftp.lo `test -f 'ftp.c' || echo '$(srcdir)/'`ftp.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-ftp.Tpo $(DEPDIR)/libcurlu_la-ftp.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ftp.c' object='libcurlu_la-ftp.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-ftp.lo `test -f 'ftp.c' || echo '$(srcdir)/'`ftp.c - -libcurlu_la-ftplistparser.lo: ftplistparser.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-ftplistparser.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-ftplistparser.Tpo -c -o libcurlu_la-ftplistparser.lo `test -f 'ftplistparser.c' || echo '$(srcdir)/'`ftplistparser.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-ftplistparser.Tpo $(DEPDIR)/libcurlu_la-ftplistparser.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ftplistparser.c' object='libcurlu_la-ftplistparser.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-ftplistparser.lo `test -f 'ftplistparser.c' || echo '$(srcdir)/'`ftplistparser.c - -libcurlu_la-getenv.lo: getenv.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-getenv.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-getenv.Tpo -c -o libcurlu_la-getenv.lo `test -f 'getenv.c' || echo '$(srcdir)/'`getenv.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-getenv.Tpo $(DEPDIR)/libcurlu_la-getenv.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getenv.c' object='libcurlu_la-getenv.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-getenv.lo `test -f 'getenv.c' || echo '$(srcdir)/'`getenv.c - -libcurlu_la-getinfo.lo: getinfo.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-getinfo.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-getinfo.Tpo -c -o libcurlu_la-getinfo.lo `test -f 'getinfo.c' || echo '$(srcdir)/'`getinfo.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-getinfo.Tpo $(DEPDIR)/libcurlu_la-getinfo.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getinfo.c' object='libcurlu_la-getinfo.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-getinfo.lo `test -f 'getinfo.c' || echo '$(srcdir)/'`getinfo.c - -libcurlu_la-gopher.lo: gopher.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-gopher.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-gopher.Tpo -c -o libcurlu_la-gopher.lo `test -f 'gopher.c' || echo '$(srcdir)/'`gopher.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-gopher.Tpo $(DEPDIR)/libcurlu_la-gopher.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gopher.c' object='libcurlu_la-gopher.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-gopher.lo `test -f 'gopher.c' || echo '$(srcdir)/'`gopher.c - -libcurlu_la-h2h3.lo: h2h3.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-h2h3.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-h2h3.Tpo -c -o libcurlu_la-h2h3.lo `test -f 'h2h3.c' || echo '$(srcdir)/'`h2h3.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-h2h3.Tpo $(DEPDIR)/libcurlu_la-h2h3.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='h2h3.c' object='libcurlu_la-h2h3.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-h2h3.lo `test -f 'h2h3.c' || echo '$(srcdir)/'`h2h3.c - -libcurlu_la-hash.lo: hash.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hash.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hash.Tpo -c -o libcurlu_la-hash.lo `test -f 'hash.c' || echo '$(srcdir)/'`hash.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hash.Tpo $(DEPDIR)/libcurlu_la-hash.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hash.c' object='libcurlu_la-hash.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hash.lo `test -f 'hash.c' || echo '$(srcdir)/'`hash.c - -libcurlu_la-headers.lo: headers.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-headers.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-headers.Tpo -c -o libcurlu_la-headers.lo `test -f 'headers.c' || echo '$(srcdir)/'`headers.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-headers.Tpo $(DEPDIR)/libcurlu_la-headers.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='headers.c' object='libcurlu_la-headers.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-headers.lo `test -f 'headers.c' || echo '$(srcdir)/'`headers.c - -libcurlu_la-hmac.lo: hmac.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hmac.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hmac.Tpo -c -o libcurlu_la-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hmac.Tpo $(DEPDIR)/libcurlu_la-hmac.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hmac.c' object='libcurlu_la-hmac.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c - -libcurlu_la-hostasyn.lo: hostasyn.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostasyn.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostasyn.Tpo -c -o libcurlu_la-hostasyn.lo `test -f 'hostasyn.c' || echo '$(srcdir)/'`hostasyn.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostasyn.Tpo $(DEPDIR)/libcurlu_la-hostasyn.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostasyn.c' object='libcurlu_la-hostasyn.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hostasyn.lo `test -f 'hostasyn.c' || echo '$(srcdir)/'`hostasyn.c - -libcurlu_la-hostip.lo: hostip.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostip.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostip.Tpo -c -o libcurlu_la-hostip.lo `test -f 'hostip.c' || echo '$(srcdir)/'`hostip.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostip.Tpo $(DEPDIR)/libcurlu_la-hostip.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostip.c' object='libcurlu_la-hostip.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hostip.lo `test -f 'hostip.c' || echo '$(srcdir)/'`hostip.c - -libcurlu_la-hostip4.lo: hostip4.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostip4.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostip4.Tpo -c -o libcurlu_la-hostip4.lo `test -f 'hostip4.c' || echo '$(srcdir)/'`hostip4.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostip4.Tpo $(DEPDIR)/libcurlu_la-hostip4.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostip4.c' object='libcurlu_la-hostip4.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hostip4.lo `test -f 'hostip4.c' || echo '$(srcdir)/'`hostip4.c - -libcurlu_la-hostip6.lo: hostip6.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostip6.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostip6.Tpo -c -o libcurlu_la-hostip6.lo `test -f 'hostip6.c' || echo '$(srcdir)/'`hostip6.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostip6.Tpo $(DEPDIR)/libcurlu_la-hostip6.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostip6.c' object='libcurlu_la-hostip6.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hostip6.lo `test -f 'hostip6.c' || echo '$(srcdir)/'`hostip6.c - -libcurlu_la-hostsyn.lo: hostsyn.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostsyn.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostsyn.Tpo -c -o libcurlu_la-hostsyn.lo `test -f 'hostsyn.c' || echo '$(srcdir)/'`hostsyn.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostsyn.Tpo $(DEPDIR)/libcurlu_la-hostsyn.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostsyn.c' object='libcurlu_la-hostsyn.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hostsyn.lo `test -f 'hostsyn.c' || echo '$(srcdir)/'`hostsyn.c - -libcurlu_la-hsts.lo: hsts.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hsts.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hsts.Tpo -c -o libcurlu_la-hsts.lo `test -f 'hsts.c' || echo '$(srcdir)/'`hsts.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hsts.Tpo $(DEPDIR)/libcurlu_la-hsts.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hsts.c' object='libcurlu_la-hsts.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hsts.lo `test -f 'hsts.c' || echo '$(srcdir)/'`hsts.c - -libcurlu_la-http.lo: http.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http.Tpo -c -o libcurlu_la-http.lo `test -f 'http.c' || echo '$(srcdir)/'`http.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http.Tpo $(DEPDIR)/libcurlu_la-http.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http.c' object='libcurlu_la-http.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http.lo `test -f 'http.c' || echo '$(srcdir)/'`http.c - -libcurlu_la-http2.lo: http2.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http2.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http2.Tpo -c -o libcurlu_la-http2.lo `test -f 'http2.c' || echo '$(srcdir)/'`http2.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http2.Tpo $(DEPDIR)/libcurlu_la-http2.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http2.c' object='libcurlu_la-http2.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http2.lo `test -f 'http2.c' || echo '$(srcdir)/'`http2.c - -libcurlu_la-http_chunks.lo: http_chunks.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http_chunks.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http_chunks.Tpo -c -o libcurlu_la-http_chunks.lo `test -f 'http_chunks.c' || echo '$(srcdir)/'`http_chunks.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http_chunks.Tpo $(DEPDIR)/libcurlu_la-http_chunks.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_chunks.c' object='libcurlu_la-http_chunks.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http_chunks.lo `test -f 'http_chunks.c' || echo '$(srcdir)/'`http_chunks.c - -libcurlu_la-http_digest.lo: http_digest.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http_digest.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http_digest.Tpo -c -o libcurlu_la-http_digest.lo `test -f 'http_digest.c' || echo '$(srcdir)/'`http_digest.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http_digest.Tpo $(DEPDIR)/libcurlu_la-http_digest.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_digest.c' object='libcurlu_la-http_digest.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http_digest.lo `test -f 'http_digest.c' || echo '$(srcdir)/'`http_digest.c - -libcurlu_la-http_negotiate.lo: http_negotiate.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http_negotiate.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http_negotiate.Tpo -c -o libcurlu_la-http_negotiate.lo `test -f 'http_negotiate.c' || echo '$(srcdir)/'`http_negotiate.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http_negotiate.Tpo $(DEPDIR)/libcurlu_la-http_negotiate.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_negotiate.c' object='libcurlu_la-http_negotiate.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http_negotiate.lo `test -f 'http_negotiate.c' || echo '$(srcdir)/'`http_negotiate.c - -libcurlu_la-http_ntlm.lo: http_ntlm.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http_ntlm.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http_ntlm.Tpo -c -o libcurlu_la-http_ntlm.lo `test -f 'http_ntlm.c' || echo '$(srcdir)/'`http_ntlm.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http_ntlm.Tpo $(DEPDIR)/libcurlu_la-http_ntlm.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_ntlm.c' object='libcurlu_la-http_ntlm.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http_ntlm.lo `test -f 'http_ntlm.c' || echo '$(srcdir)/'`http_ntlm.c - -libcurlu_la-http_proxy.lo: http_proxy.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http_proxy.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http_proxy.Tpo -c -o libcurlu_la-http_proxy.lo `test -f 'http_proxy.c' || echo '$(srcdir)/'`http_proxy.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http_proxy.Tpo $(DEPDIR)/libcurlu_la-http_proxy.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_proxy.c' object='libcurlu_la-http_proxy.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http_proxy.lo `test -f 'http_proxy.c' || echo '$(srcdir)/'`http_proxy.c - -libcurlu_la-http_aws_sigv4.lo: http_aws_sigv4.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http_aws_sigv4.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http_aws_sigv4.Tpo -c -o libcurlu_la-http_aws_sigv4.lo `test -f 'http_aws_sigv4.c' || echo '$(srcdir)/'`http_aws_sigv4.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http_aws_sigv4.Tpo $(DEPDIR)/libcurlu_la-http_aws_sigv4.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_aws_sigv4.c' object='libcurlu_la-http_aws_sigv4.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http_aws_sigv4.lo `test -f 'http_aws_sigv4.c' || echo '$(srcdir)/'`http_aws_sigv4.c - -libcurlu_la-idn_win32.lo: idn_win32.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-idn_win32.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-idn_win32.Tpo -c -o libcurlu_la-idn_win32.lo `test -f 'idn_win32.c' || echo '$(srcdir)/'`idn_win32.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-idn_win32.Tpo $(DEPDIR)/libcurlu_la-idn_win32.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='idn_win32.c' object='libcurlu_la-idn_win32.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-idn_win32.lo `test -f 'idn_win32.c' || echo '$(srcdir)/'`idn_win32.c - -libcurlu_la-if2ip.lo: if2ip.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-if2ip.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-if2ip.Tpo -c -o libcurlu_la-if2ip.lo `test -f 'if2ip.c' || echo '$(srcdir)/'`if2ip.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-if2ip.Tpo $(DEPDIR)/libcurlu_la-if2ip.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='if2ip.c' object='libcurlu_la-if2ip.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-if2ip.lo `test -f 'if2ip.c' || echo '$(srcdir)/'`if2ip.c - -libcurlu_la-imap.lo: imap.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-imap.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-imap.Tpo -c -o libcurlu_la-imap.lo `test -f 'imap.c' || echo '$(srcdir)/'`imap.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-imap.Tpo $(DEPDIR)/libcurlu_la-imap.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='imap.c' object='libcurlu_la-imap.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-imap.lo `test -f 'imap.c' || echo '$(srcdir)/'`imap.c - -libcurlu_la-inet_ntop.lo: inet_ntop.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-inet_ntop.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-inet_ntop.Tpo -c -o libcurlu_la-inet_ntop.lo `test -f 'inet_ntop.c' || echo '$(srcdir)/'`inet_ntop.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-inet_ntop.Tpo $(DEPDIR)/libcurlu_la-inet_ntop.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='inet_ntop.c' object='libcurlu_la-inet_ntop.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-inet_ntop.lo `test -f 'inet_ntop.c' || echo '$(srcdir)/'`inet_ntop.c - -libcurlu_la-inet_pton.lo: inet_pton.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-inet_pton.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-inet_pton.Tpo -c -o libcurlu_la-inet_pton.lo `test -f 'inet_pton.c' || echo '$(srcdir)/'`inet_pton.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-inet_pton.Tpo $(DEPDIR)/libcurlu_la-inet_pton.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='inet_pton.c' object='libcurlu_la-inet_pton.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-inet_pton.lo `test -f 'inet_pton.c' || echo '$(srcdir)/'`inet_pton.c - -libcurlu_la-krb5.lo: krb5.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-krb5.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-krb5.Tpo -c -o libcurlu_la-krb5.lo `test -f 'krb5.c' || echo '$(srcdir)/'`krb5.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-krb5.Tpo $(DEPDIR)/libcurlu_la-krb5.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='krb5.c' object='libcurlu_la-krb5.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-krb5.lo `test -f 'krb5.c' || echo '$(srcdir)/'`krb5.c - -libcurlu_la-ldap.lo: ldap.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-ldap.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-ldap.Tpo -c -o libcurlu_la-ldap.lo `test -f 'ldap.c' || echo '$(srcdir)/'`ldap.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-ldap.Tpo $(DEPDIR)/libcurlu_la-ldap.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ldap.c' object='libcurlu_la-ldap.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-ldap.lo `test -f 'ldap.c' || echo '$(srcdir)/'`ldap.c - -libcurlu_la-llist.lo: llist.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-llist.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-llist.Tpo -c -o libcurlu_la-llist.lo `test -f 'llist.c' || echo '$(srcdir)/'`llist.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-llist.Tpo $(DEPDIR)/libcurlu_la-llist.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='llist.c' object='libcurlu_la-llist.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-llist.lo `test -f 'llist.c' || echo '$(srcdir)/'`llist.c - -libcurlu_la-md4.lo: md4.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-md4.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-md4.Tpo -c -o libcurlu_la-md4.lo `test -f 'md4.c' || echo '$(srcdir)/'`md4.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-md4.Tpo $(DEPDIR)/libcurlu_la-md4.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='md4.c' object='libcurlu_la-md4.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-md4.lo `test -f 'md4.c' || echo '$(srcdir)/'`md4.c - -libcurlu_la-md5.lo: md5.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-md5.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-md5.Tpo -c -o libcurlu_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-md5.Tpo $(DEPDIR)/libcurlu_la-md5.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='md5.c' object='libcurlu_la-md5.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c - -libcurlu_la-memdebug.lo: memdebug.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-memdebug.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-memdebug.Tpo -c -o libcurlu_la-memdebug.lo `test -f 'memdebug.c' || echo '$(srcdir)/'`memdebug.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-memdebug.Tpo $(DEPDIR)/libcurlu_la-memdebug.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='memdebug.c' object='libcurlu_la-memdebug.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-memdebug.lo `test -f 'memdebug.c' || echo '$(srcdir)/'`memdebug.c - -libcurlu_la-mime.lo: mime.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-mime.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-mime.Tpo -c -o libcurlu_la-mime.lo `test -f 'mime.c' || echo '$(srcdir)/'`mime.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-mime.Tpo $(DEPDIR)/libcurlu_la-mime.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mime.c' object='libcurlu_la-mime.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-mime.lo `test -f 'mime.c' || echo '$(srcdir)/'`mime.c - -libcurlu_la-mprintf.lo: mprintf.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-mprintf.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-mprintf.Tpo -c -o libcurlu_la-mprintf.lo `test -f 'mprintf.c' || echo '$(srcdir)/'`mprintf.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-mprintf.Tpo $(DEPDIR)/libcurlu_la-mprintf.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mprintf.c' object='libcurlu_la-mprintf.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-mprintf.lo `test -f 'mprintf.c' || echo '$(srcdir)/'`mprintf.c - -libcurlu_la-mqtt.lo: mqtt.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-mqtt.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-mqtt.Tpo -c -o libcurlu_la-mqtt.lo `test -f 'mqtt.c' || echo '$(srcdir)/'`mqtt.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-mqtt.Tpo $(DEPDIR)/libcurlu_la-mqtt.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mqtt.c' object='libcurlu_la-mqtt.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-mqtt.lo `test -f 'mqtt.c' || echo '$(srcdir)/'`mqtt.c - -libcurlu_la-multi.lo: multi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-multi.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-multi.Tpo -c -o libcurlu_la-multi.lo `test -f 'multi.c' || echo '$(srcdir)/'`multi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-multi.Tpo $(DEPDIR)/libcurlu_la-multi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='multi.c' object='libcurlu_la-multi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-multi.lo `test -f 'multi.c' || echo '$(srcdir)/'`multi.c - -libcurlu_la-netrc.lo: netrc.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-netrc.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-netrc.Tpo -c -o libcurlu_la-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-netrc.Tpo $(DEPDIR)/libcurlu_la-netrc.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netrc.c' object='libcurlu_la-netrc.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c - -libcurlu_la-nonblock.lo: nonblock.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-nonblock.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-nonblock.Tpo -c -o libcurlu_la-nonblock.lo `test -f 'nonblock.c' || echo '$(srcdir)/'`nonblock.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-nonblock.Tpo $(DEPDIR)/libcurlu_la-nonblock.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nonblock.c' object='libcurlu_la-nonblock.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-nonblock.lo `test -f 'nonblock.c' || echo '$(srcdir)/'`nonblock.c - -libcurlu_la-noproxy.lo: noproxy.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-noproxy.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-noproxy.Tpo -c -o libcurlu_la-noproxy.lo `test -f 'noproxy.c' || echo '$(srcdir)/'`noproxy.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-noproxy.Tpo $(DEPDIR)/libcurlu_la-noproxy.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='noproxy.c' object='libcurlu_la-noproxy.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-noproxy.lo `test -f 'noproxy.c' || echo '$(srcdir)/'`noproxy.c - -libcurlu_la-openldap.lo: openldap.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-openldap.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-openldap.Tpo -c -o libcurlu_la-openldap.lo `test -f 'openldap.c' || echo '$(srcdir)/'`openldap.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-openldap.Tpo $(DEPDIR)/libcurlu_la-openldap.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openldap.c' object='libcurlu_la-openldap.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-openldap.lo `test -f 'openldap.c' || echo '$(srcdir)/'`openldap.c - -libcurlu_la-parsedate.lo: parsedate.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-parsedate.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-parsedate.Tpo -c -o libcurlu_la-parsedate.lo `test -f 'parsedate.c' || echo '$(srcdir)/'`parsedate.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-parsedate.Tpo $(DEPDIR)/libcurlu_la-parsedate.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='parsedate.c' object='libcurlu_la-parsedate.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-parsedate.lo `test -f 'parsedate.c' || echo '$(srcdir)/'`parsedate.c - -libcurlu_la-pingpong.lo: pingpong.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-pingpong.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-pingpong.Tpo -c -o libcurlu_la-pingpong.lo `test -f 'pingpong.c' || echo '$(srcdir)/'`pingpong.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-pingpong.Tpo $(DEPDIR)/libcurlu_la-pingpong.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pingpong.c' object='libcurlu_la-pingpong.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-pingpong.lo `test -f 'pingpong.c' || echo '$(srcdir)/'`pingpong.c - -libcurlu_la-pop3.lo: pop3.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-pop3.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-pop3.Tpo -c -o libcurlu_la-pop3.lo `test -f 'pop3.c' || echo '$(srcdir)/'`pop3.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-pop3.Tpo $(DEPDIR)/libcurlu_la-pop3.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pop3.c' object='libcurlu_la-pop3.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-pop3.lo `test -f 'pop3.c' || echo '$(srcdir)/'`pop3.c - -libcurlu_la-progress.lo: progress.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-progress.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-progress.Tpo -c -o libcurlu_la-progress.lo `test -f 'progress.c' || echo '$(srcdir)/'`progress.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-progress.Tpo $(DEPDIR)/libcurlu_la-progress.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='progress.c' object='libcurlu_la-progress.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-progress.lo `test -f 'progress.c' || echo '$(srcdir)/'`progress.c - -libcurlu_la-psl.lo: psl.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-psl.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-psl.Tpo -c -o libcurlu_la-psl.lo `test -f 'psl.c' || echo '$(srcdir)/'`psl.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-psl.Tpo $(DEPDIR)/libcurlu_la-psl.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='psl.c' object='libcurlu_la-psl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-psl.lo `test -f 'psl.c' || echo '$(srcdir)/'`psl.c - -libcurlu_la-rand.lo: rand.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-rand.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-rand.Tpo -c -o libcurlu_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-rand.Tpo $(DEPDIR)/libcurlu_la-rand.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rand.c' object='libcurlu_la-rand.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c - -libcurlu_la-rename.lo: rename.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-rename.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-rename.Tpo -c -o libcurlu_la-rename.lo `test -f 'rename.c' || echo '$(srcdir)/'`rename.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-rename.Tpo $(DEPDIR)/libcurlu_la-rename.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rename.c' object='libcurlu_la-rename.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-rename.lo `test -f 'rename.c' || echo '$(srcdir)/'`rename.c - -libcurlu_la-rtsp.lo: rtsp.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-rtsp.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-rtsp.Tpo -c -o libcurlu_la-rtsp.lo `test -f 'rtsp.c' || echo '$(srcdir)/'`rtsp.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-rtsp.Tpo $(DEPDIR)/libcurlu_la-rtsp.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rtsp.c' object='libcurlu_la-rtsp.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-rtsp.lo `test -f 'rtsp.c' || echo '$(srcdir)/'`rtsp.c - -libcurlu_la-select.lo: select.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-select.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-select.Tpo -c -o libcurlu_la-select.lo `test -f 'select.c' || echo '$(srcdir)/'`select.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-select.Tpo $(DEPDIR)/libcurlu_la-select.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='select.c' object='libcurlu_la-select.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-select.lo `test -f 'select.c' || echo '$(srcdir)/'`select.c - -libcurlu_la-sendf.lo: sendf.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-sendf.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-sendf.Tpo -c -o libcurlu_la-sendf.lo `test -f 'sendf.c' || echo '$(srcdir)/'`sendf.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-sendf.Tpo $(DEPDIR)/libcurlu_la-sendf.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sendf.c' object='libcurlu_la-sendf.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-sendf.lo `test -f 'sendf.c' || echo '$(srcdir)/'`sendf.c - -libcurlu_la-setopt.lo: setopt.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-setopt.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-setopt.Tpo -c -o libcurlu_la-setopt.lo `test -f 'setopt.c' || echo '$(srcdir)/'`setopt.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-setopt.Tpo $(DEPDIR)/libcurlu_la-setopt.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='setopt.c' object='libcurlu_la-setopt.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-setopt.lo `test -f 'setopt.c' || echo '$(srcdir)/'`setopt.c - -libcurlu_la-sha256.lo: sha256.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-sha256.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-sha256.Tpo -c -o libcurlu_la-sha256.lo `test -f 'sha256.c' || echo '$(srcdir)/'`sha256.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-sha256.Tpo $(DEPDIR)/libcurlu_la-sha256.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sha256.c' object='libcurlu_la-sha256.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-sha256.lo `test -f 'sha256.c' || echo '$(srcdir)/'`sha256.c - -libcurlu_la-share.lo: share.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-share.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-share.Tpo -c -o libcurlu_la-share.lo `test -f 'share.c' || echo '$(srcdir)/'`share.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-share.Tpo $(DEPDIR)/libcurlu_la-share.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='share.c' object='libcurlu_la-share.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-share.lo `test -f 'share.c' || echo '$(srcdir)/'`share.c - -libcurlu_la-slist.lo: slist.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-slist.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-slist.Tpo -c -o libcurlu_la-slist.lo `test -f 'slist.c' || echo '$(srcdir)/'`slist.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-slist.Tpo $(DEPDIR)/libcurlu_la-slist.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='slist.c' object='libcurlu_la-slist.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-slist.lo `test -f 'slist.c' || echo '$(srcdir)/'`slist.c - -libcurlu_la-smb.lo: smb.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-smb.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-smb.Tpo -c -o libcurlu_la-smb.lo `test -f 'smb.c' || echo '$(srcdir)/'`smb.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-smb.Tpo $(DEPDIR)/libcurlu_la-smb.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='smb.c' object='libcurlu_la-smb.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-smb.lo `test -f 'smb.c' || echo '$(srcdir)/'`smb.c - -libcurlu_la-smtp.lo: smtp.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-smtp.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-smtp.Tpo -c -o libcurlu_la-smtp.lo `test -f 'smtp.c' || echo '$(srcdir)/'`smtp.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-smtp.Tpo $(DEPDIR)/libcurlu_la-smtp.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='smtp.c' object='libcurlu_la-smtp.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-smtp.lo `test -f 'smtp.c' || echo '$(srcdir)/'`smtp.c - -libcurlu_la-socketpair.lo: socketpair.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-socketpair.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-socketpair.Tpo -c -o libcurlu_la-socketpair.lo `test -f 'socketpair.c' || echo '$(srcdir)/'`socketpair.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-socketpair.Tpo $(DEPDIR)/libcurlu_la-socketpair.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socketpair.c' object='libcurlu_la-socketpair.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-socketpair.lo `test -f 'socketpair.c' || echo '$(srcdir)/'`socketpair.c - -libcurlu_la-socks.lo: socks.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-socks.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-socks.Tpo -c -o libcurlu_la-socks.lo `test -f 'socks.c' || echo '$(srcdir)/'`socks.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-socks.Tpo $(DEPDIR)/libcurlu_la-socks.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socks.c' object='libcurlu_la-socks.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-socks.lo `test -f 'socks.c' || echo '$(srcdir)/'`socks.c - -libcurlu_la-socks_gssapi.lo: socks_gssapi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-socks_gssapi.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-socks_gssapi.Tpo -c -o libcurlu_la-socks_gssapi.lo `test -f 'socks_gssapi.c' || echo '$(srcdir)/'`socks_gssapi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-socks_gssapi.Tpo $(DEPDIR)/libcurlu_la-socks_gssapi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socks_gssapi.c' object='libcurlu_la-socks_gssapi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-socks_gssapi.lo `test -f 'socks_gssapi.c' || echo '$(srcdir)/'`socks_gssapi.c - -libcurlu_la-socks_sspi.lo: socks_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-socks_sspi.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-socks_sspi.Tpo -c -o libcurlu_la-socks_sspi.lo `test -f 'socks_sspi.c' || echo '$(srcdir)/'`socks_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-socks_sspi.Tpo $(DEPDIR)/libcurlu_la-socks_sspi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socks_sspi.c' object='libcurlu_la-socks_sspi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-socks_sspi.lo `test -f 'socks_sspi.c' || echo '$(srcdir)/'`socks_sspi.c - -libcurlu_la-speedcheck.lo: speedcheck.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-speedcheck.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-speedcheck.Tpo -c -o libcurlu_la-speedcheck.lo `test -f 'speedcheck.c' || echo '$(srcdir)/'`speedcheck.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-speedcheck.Tpo $(DEPDIR)/libcurlu_la-speedcheck.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='speedcheck.c' object='libcurlu_la-speedcheck.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-speedcheck.lo `test -f 'speedcheck.c' || echo '$(srcdir)/'`speedcheck.c - -libcurlu_la-splay.lo: splay.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-splay.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-splay.Tpo -c -o libcurlu_la-splay.lo `test -f 'splay.c' || echo '$(srcdir)/'`splay.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-splay.Tpo $(DEPDIR)/libcurlu_la-splay.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='splay.c' object='libcurlu_la-splay.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-splay.lo `test -f 'splay.c' || echo '$(srcdir)/'`splay.c - -libcurlu_la-strcase.lo: strcase.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strcase.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strcase.Tpo -c -o libcurlu_la-strcase.lo `test -f 'strcase.c' || echo '$(srcdir)/'`strcase.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strcase.Tpo $(DEPDIR)/libcurlu_la-strcase.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strcase.c' object='libcurlu_la-strcase.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strcase.lo `test -f 'strcase.c' || echo '$(srcdir)/'`strcase.c - -libcurlu_la-strdup.lo: strdup.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strdup.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strdup.Tpo -c -o libcurlu_la-strdup.lo `test -f 'strdup.c' || echo '$(srcdir)/'`strdup.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strdup.Tpo $(DEPDIR)/libcurlu_la-strdup.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strdup.c' object='libcurlu_la-strdup.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strdup.lo `test -f 'strdup.c' || echo '$(srcdir)/'`strdup.c - -libcurlu_la-strerror.lo: strerror.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strerror.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strerror.Tpo -c -o libcurlu_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strerror.Tpo $(DEPDIR)/libcurlu_la-strerror.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strerror.c' object='libcurlu_la-strerror.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c - -libcurlu_la-strtok.lo: strtok.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strtok.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strtok.Tpo -c -o libcurlu_la-strtok.lo `test -f 'strtok.c' || echo '$(srcdir)/'`strtok.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strtok.Tpo $(DEPDIR)/libcurlu_la-strtok.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strtok.c' object='libcurlu_la-strtok.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strtok.lo `test -f 'strtok.c' || echo '$(srcdir)/'`strtok.c - -libcurlu_la-strtoofft.lo: strtoofft.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strtoofft.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strtoofft.Tpo -c -o libcurlu_la-strtoofft.lo `test -f 'strtoofft.c' || echo '$(srcdir)/'`strtoofft.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strtoofft.Tpo $(DEPDIR)/libcurlu_la-strtoofft.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strtoofft.c' object='libcurlu_la-strtoofft.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strtoofft.lo `test -f 'strtoofft.c' || echo '$(srcdir)/'`strtoofft.c - -libcurlu_la-system_win32.lo: system_win32.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-system_win32.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-system_win32.Tpo -c -o libcurlu_la-system_win32.lo `test -f 'system_win32.c' || echo '$(srcdir)/'`system_win32.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-system_win32.Tpo $(DEPDIR)/libcurlu_la-system_win32.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='system_win32.c' object='libcurlu_la-system_win32.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-system_win32.lo `test -f 'system_win32.c' || echo '$(srcdir)/'`system_win32.c - -libcurlu_la-telnet.lo: telnet.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-telnet.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-telnet.Tpo -c -o libcurlu_la-telnet.lo `test -f 'telnet.c' || echo '$(srcdir)/'`telnet.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-telnet.Tpo $(DEPDIR)/libcurlu_la-telnet.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='telnet.c' object='libcurlu_la-telnet.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-telnet.lo `test -f 'telnet.c' || echo '$(srcdir)/'`telnet.c - -libcurlu_la-tftp.lo: tftp.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-tftp.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-tftp.Tpo -c -o libcurlu_la-tftp.lo `test -f 'tftp.c' || echo '$(srcdir)/'`tftp.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-tftp.Tpo $(DEPDIR)/libcurlu_la-tftp.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tftp.c' object='libcurlu_la-tftp.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-tftp.lo `test -f 'tftp.c' || echo '$(srcdir)/'`tftp.c - -libcurlu_la-timediff.lo: timediff.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-timediff.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-timediff.Tpo -c -o libcurlu_la-timediff.lo `test -f 'timediff.c' || echo '$(srcdir)/'`timediff.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-timediff.Tpo $(DEPDIR)/libcurlu_la-timediff.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timediff.c' object='libcurlu_la-timediff.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-timediff.lo `test -f 'timediff.c' || echo '$(srcdir)/'`timediff.c - -libcurlu_la-timeval.lo: timeval.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-timeval.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-timeval.Tpo -c -o libcurlu_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-timeval.Tpo $(DEPDIR)/libcurlu_la-timeval.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timeval.c' object='libcurlu_la-timeval.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c - -libcurlu_la-transfer.lo: transfer.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-transfer.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-transfer.Tpo -c -o libcurlu_la-transfer.lo `test -f 'transfer.c' || echo '$(srcdir)/'`transfer.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-transfer.Tpo $(DEPDIR)/libcurlu_la-transfer.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='transfer.c' object='libcurlu_la-transfer.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-transfer.lo `test -f 'transfer.c' || echo '$(srcdir)/'`transfer.c - -libcurlu_la-url.lo: url.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-url.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-url.Tpo -c -o libcurlu_la-url.lo `test -f 'url.c' || echo '$(srcdir)/'`url.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-url.Tpo $(DEPDIR)/libcurlu_la-url.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='url.c' object='libcurlu_la-url.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-url.lo `test -f 'url.c' || echo '$(srcdir)/'`url.c - -libcurlu_la-urlapi.lo: urlapi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-urlapi.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-urlapi.Tpo -c -o libcurlu_la-urlapi.lo `test -f 'urlapi.c' || echo '$(srcdir)/'`urlapi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-urlapi.Tpo $(DEPDIR)/libcurlu_la-urlapi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='urlapi.c' object='libcurlu_la-urlapi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-urlapi.lo `test -f 'urlapi.c' || echo '$(srcdir)/'`urlapi.c - -libcurlu_la-version.lo: version.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-version.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-version.Tpo -c -o libcurlu_la-version.lo `test -f 'version.c' || echo '$(srcdir)/'`version.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-version.Tpo $(DEPDIR)/libcurlu_la-version.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='version.c' object='libcurlu_la-version.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-version.lo `test -f 'version.c' || echo '$(srcdir)/'`version.c - -libcurlu_la-version_win32.lo: version_win32.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-version_win32.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-version_win32.Tpo -c -o libcurlu_la-version_win32.lo `test -f 'version_win32.c' || echo '$(srcdir)/'`version_win32.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-version_win32.Tpo $(DEPDIR)/libcurlu_la-version_win32.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='version_win32.c' object='libcurlu_la-version_win32.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-version_win32.lo `test -f 'version_win32.c' || echo '$(srcdir)/'`version_win32.c - -libcurlu_la-warnless.lo: warnless.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-warnless.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-warnless.Tpo -c -o libcurlu_la-warnless.lo `test -f 'warnless.c' || echo '$(srcdir)/'`warnless.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-warnless.Tpo $(DEPDIR)/libcurlu_la-warnless.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='warnless.c' object='libcurlu_la-warnless.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-warnless.lo `test -f 'warnless.c' || echo '$(srcdir)/'`warnless.c - -libcurlu_la-wildcard.lo: wildcard.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-wildcard.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-wildcard.Tpo -c -o libcurlu_la-wildcard.lo `test -f 'wildcard.c' || echo '$(srcdir)/'`wildcard.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-wildcard.Tpo $(DEPDIR)/libcurlu_la-wildcard.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wildcard.c' object='libcurlu_la-wildcard.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-wildcard.lo `test -f 'wildcard.c' || echo '$(srcdir)/'`wildcard.c - -libcurlu_la-ws.lo: ws.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-ws.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-ws.Tpo -c -o libcurlu_la-ws.lo `test -f 'ws.c' || echo '$(srcdir)/'`ws.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-ws.Tpo $(DEPDIR)/libcurlu_la-ws.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ws.c' object='libcurlu_la-ws.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-ws.lo `test -f 'ws.c' || echo '$(srcdir)/'`ws.c - -vauth/libcurlu_la-cleartext.lo: vauth/cleartext.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-cleartext.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-cleartext.Tpo -c -o vauth/libcurlu_la-cleartext.lo `test -f 'vauth/cleartext.c' || echo '$(srcdir)/'`vauth/cleartext.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-cleartext.Tpo vauth/$(DEPDIR)/libcurlu_la-cleartext.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/cleartext.c' object='vauth/libcurlu_la-cleartext.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-cleartext.lo `test -f 'vauth/cleartext.c' || echo '$(srcdir)/'`vauth/cleartext.c - -vauth/libcurlu_la-cram.lo: vauth/cram.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-cram.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-cram.Tpo -c -o vauth/libcurlu_la-cram.lo `test -f 'vauth/cram.c' || echo '$(srcdir)/'`vauth/cram.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-cram.Tpo vauth/$(DEPDIR)/libcurlu_la-cram.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/cram.c' object='vauth/libcurlu_la-cram.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-cram.lo `test -f 'vauth/cram.c' || echo '$(srcdir)/'`vauth/cram.c - -vauth/libcurlu_la-digest.lo: vauth/digest.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-digest.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-digest.Tpo -c -o vauth/libcurlu_la-digest.lo `test -f 'vauth/digest.c' || echo '$(srcdir)/'`vauth/digest.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-digest.Tpo vauth/$(DEPDIR)/libcurlu_la-digest.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/digest.c' object='vauth/libcurlu_la-digest.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-digest.lo `test -f 'vauth/digest.c' || echo '$(srcdir)/'`vauth/digest.c - -vauth/libcurlu_la-digest_sspi.lo: vauth/digest_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-digest_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Tpo -c -o vauth/libcurlu_la-digest_sspi.lo `test -f 'vauth/digest_sspi.c' || echo '$(srcdir)/'`vauth/digest_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Tpo vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/digest_sspi.c' object='vauth/libcurlu_la-digest_sspi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-digest_sspi.lo `test -f 'vauth/digest_sspi.c' || echo '$(srcdir)/'`vauth/digest_sspi.c - -vauth/libcurlu_la-gsasl.lo: vauth/gsasl.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-gsasl.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-gsasl.Tpo -c -o vauth/libcurlu_la-gsasl.lo `test -f 'vauth/gsasl.c' || echo '$(srcdir)/'`vauth/gsasl.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-gsasl.Tpo vauth/$(DEPDIR)/libcurlu_la-gsasl.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/gsasl.c' object='vauth/libcurlu_la-gsasl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-gsasl.lo `test -f 'vauth/gsasl.c' || echo '$(srcdir)/'`vauth/gsasl.c - -vauth/libcurlu_la-krb5_gssapi.lo: vauth/krb5_gssapi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-krb5_gssapi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Tpo -c -o vauth/libcurlu_la-krb5_gssapi.lo `test -f 'vauth/krb5_gssapi.c' || echo '$(srcdir)/'`vauth/krb5_gssapi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Tpo vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/krb5_gssapi.c' object='vauth/libcurlu_la-krb5_gssapi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-krb5_gssapi.lo `test -f 'vauth/krb5_gssapi.c' || echo '$(srcdir)/'`vauth/krb5_gssapi.c - -vauth/libcurlu_la-krb5_sspi.lo: vauth/krb5_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-krb5_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Tpo -c -o vauth/libcurlu_la-krb5_sspi.lo `test -f 'vauth/krb5_sspi.c' || echo '$(srcdir)/'`vauth/krb5_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Tpo vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/krb5_sspi.c' object='vauth/libcurlu_la-krb5_sspi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-krb5_sspi.lo `test -f 'vauth/krb5_sspi.c' || echo '$(srcdir)/'`vauth/krb5_sspi.c - -vauth/libcurlu_la-ntlm.lo: vauth/ntlm.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-ntlm.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-ntlm.Tpo -c -o vauth/libcurlu_la-ntlm.lo `test -f 'vauth/ntlm.c' || echo '$(srcdir)/'`vauth/ntlm.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-ntlm.Tpo vauth/$(DEPDIR)/libcurlu_la-ntlm.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/ntlm.c' object='vauth/libcurlu_la-ntlm.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-ntlm.lo `test -f 'vauth/ntlm.c' || echo '$(srcdir)/'`vauth/ntlm.c - -vauth/libcurlu_la-ntlm_sspi.lo: vauth/ntlm_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-ntlm_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Tpo -c -o vauth/libcurlu_la-ntlm_sspi.lo `test -f 'vauth/ntlm_sspi.c' || echo '$(srcdir)/'`vauth/ntlm_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Tpo vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/ntlm_sspi.c' object='vauth/libcurlu_la-ntlm_sspi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-ntlm_sspi.lo `test -f 'vauth/ntlm_sspi.c' || echo '$(srcdir)/'`vauth/ntlm_sspi.c - -vauth/libcurlu_la-oauth2.lo: vauth/oauth2.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-oauth2.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-oauth2.Tpo -c -o vauth/libcurlu_la-oauth2.lo `test -f 'vauth/oauth2.c' || echo '$(srcdir)/'`vauth/oauth2.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-oauth2.Tpo vauth/$(DEPDIR)/libcurlu_la-oauth2.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/oauth2.c' object='vauth/libcurlu_la-oauth2.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-oauth2.lo `test -f 'vauth/oauth2.c' || echo '$(srcdir)/'`vauth/oauth2.c - -vauth/libcurlu_la-spnego_gssapi.lo: vauth/spnego_gssapi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-spnego_gssapi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Tpo -c -o vauth/libcurlu_la-spnego_gssapi.lo `test -f 'vauth/spnego_gssapi.c' || echo '$(srcdir)/'`vauth/spnego_gssapi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Tpo vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/spnego_gssapi.c' object='vauth/libcurlu_la-spnego_gssapi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-spnego_gssapi.lo `test -f 'vauth/spnego_gssapi.c' || echo '$(srcdir)/'`vauth/spnego_gssapi.c - -vauth/libcurlu_la-spnego_sspi.lo: vauth/spnego_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-spnego_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Tpo -c -o vauth/libcurlu_la-spnego_sspi.lo `test -f 'vauth/spnego_sspi.c' || echo '$(srcdir)/'`vauth/spnego_sspi.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Tpo vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/spnego_sspi.c' object='vauth/libcurlu_la-spnego_sspi.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-spnego_sspi.lo `test -f 'vauth/spnego_sspi.c' || echo '$(srcdir)/'`vauth/spnego_sspi.c - -vauth/libcurlu_la-vauth.lo: vauth/vauth.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-vauth.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-vauth.Tpo -c -o vauth/libcurlu_la-vauth.lo `test -f 'vauth/vauth.c' || echo '$(srcdir)/'`vauth/vauth.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-vauth.Tpo vauth/$(DEPDIR)/libcurlu_la-vauth.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/vauth.c' object='vauth/libcurlu_la-vauth.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-vauth.lo `test -f 'vauth/vauth.c' || echo '$(srcdir)/'`vauth/vauth.c - -vtls/libcurlu_la-bearssl.lo: vtls/bearssl.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-bearssl.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-bearssl.Tpo -c -o vtls/libcurlu_la-bearssl.lo `test -f 'vtls/bearssl.c' || echo '$(srcdir)/'`vtls/bearssl.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-bearssl.Tpo vtls/$(DEPDIR)/libcurlu_la-bearssl.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/bearssl.c' object='vtls/libcurlu_la-bearssl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-bearssl.lo `test -f 'vtls/bearssl.c' || echo '$(srcdir)/'`vtls/bearssl.c - -vtls/libcurlu_la-gskit.lo: vtls/gskit.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-gskit.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-gskit.Tpo -c -o vtls/libcurlu_la-gskit.lo `test -f 'vtls/gskit.c' || echo '$(srcdir)/'`vtls/gskit.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-gskit.Tpo vtls/$(DEPDIR)/libcurlu_la-gskit.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/gskit.c' object='vtls/libcurlu_la-gskit.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-gskit.lo `test -f 'vtls/gskit.c' || echo '$(srcdir)/'`vtls/gskit.c - -vtls/libcurlu_la-gtls.lo: vtls/gtls.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-gtls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-gtls.Tpo -c -o vtls/libcurlu_la-gtls.lo `test -f 'vtls/gtls.c' || echo '$(srcdir)/'`vtls/gtls.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-gtls.Tpo vtls/$(DEPDIR)/libcurlu_la-gtls.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/gtls.c' object='vtls/libcurlu_la-gtls.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-gtls.lo `test -f 'vtls/gtls.c' || echo '$(srcdir)/'`vtls/gtls.c - -vtls/libcurlu_la-hostcheck.lo: vtls/hostcheck.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-hostcheck.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-hostcheck.Tpo -c -o vtls/libcurlu_la-hostcheck.lo `test -f 'vtls/hostcheck.c' || echo '$(srcdir)/'`vtls/hostcheck.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-hostcheck.Tpo vtls/$(DEPDIR)/libcurlu_la-hostcheck.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/hostcheck.c' object='vtls/libcurlu_la-hostcheck.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-hostcheck.lo `test -f 'vtls/hostcheck.c' || echo '$(srcdir)/'`vtls/hostcheck.c - -vtls/libcurlu_la-keylog.lo: vtls/keylog.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-keylog.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-keylog.Tpo -c -o vtls/libcurlu_la-keylog.lo `test -f 'vtls/keylog.c' || echo '$(srcdir)/'`vtls/keylog.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-keylog.Tpo vtls/$(DEPDIR)/libcurlu_la-keylog.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/keylog.c' object='vtls/libcurlu_la-keylog.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-keylog.lo `test -f 'vtls/keylog.c' || echo '$(srcdir)/'`vtls/keylog.c - -vtls/libcurlu_la-mbedtls.lo: vtls/mbedtls.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-mbedtls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-mbedtls.Tpo -c -o vtls/libcurlu_la-mbedtls.lo `test -f 'vtls/mbedtls.c' || echo '$(srcdir)/'`vtls/mbedtls.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-mbedtls.Tpo vtls/$(DEPDIR)/libcurlu_la-mbedtls.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/mbedtls.c' object='vtls/libcurlu_la-mbedtls.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-mbedtls.lo `test -f 'vtls/mbedtls.c' || echo '$(srcdir)/'`vtls/mbedtls.c - -vtls/libcurlu_la-mbedtls_threadlock.lo: vtls/mbedtls_threadlock.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-mbedtls_threadlock.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Tpo -c -o vtls/libcurlu_la-mbedtls_threadlock.lo `test -f 'vtls/mbedtls_threadlock.c' || echo '$(srcdir)/'`vtls/mbedtls_threadlock.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Tpo vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/mbedtls_threadlock.c' object='vtls/libcurlu_la-mbedtls_threadlock.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-mbedtls_threadlock.lo `test -f 'vtls/mbedtls_threadlock.c' || echo '$(srcdir)/'`vtls/mbedtls_threadlock.c - -vtls/libcurlu_la-nss.lo: vtls/nss.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-nss.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-nss.Tpo -c -o vtls/libcurlu_la-nss.lo `test -f 'vtls/nss.c' || echo '$(srcdir)/'`vtls/nss.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-nss.Tpo vtls/$(DEPDIR)/libcurlu_la-nss.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/nss.c' object='vtls/libcurlu_la-nss.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-nss.lo `test -f 'vtls/nss.c' || echo '$(srcdir)/'`vtls/nss.c - -vtls/libcurlu_la-openssl.lo: vtls/openssl.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-openssl.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-openssl.Tpo -c -o vtls/libcurlu_la-openssl.lo `test -f 'vtls/openssl.c' || echo '$(srcdir)/'`vtls/openssl.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-openssl.Tpo vtls/$(DEPDIR)/libcurlu_la-openssl.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/openssl.c' object='vtls/libcurlu_la-openssl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-openssl.lo `test -f 'vtls/openssl.c' || echo '$(srcdir)/'`vtls/openssl.c - -vtls/libcurlu_la-rustls.lo: vtls/rustls.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-rustls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-rustls.Tpo -c -o vtls/libcurlu_la-rustls.lo `test -f 'vtls/rustls.c' || echo '$(srcdir)/'`vtls/rustls.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-rustls.Tpo vtls/$(DEPDIR)/libcurlu_la-rustls.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/rustls.c' object='vtls/libcurlu_la-rustls.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-rustls.lo `test -f 'vtls/rustls.c' || echo '$(srcdir)/'`vtls/rustls.c - -vtls/libcurlu_la-schannel.lo: vtls/schannel.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-schannel.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-schannel.Tpo -c -o vtls/libcurlu_la-schannel.lo `test -f 'vtls/schannel.c' || echo '$(srcdir)/'`vtls/schannel.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-schannel.Tpo vtls/$(DEPDIR)/libcurlu_la-schannel.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/schannel.c' object='vtls/libcurlu_la-schannel.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-schannel.lo `test -f 'vtls/schannel.c' || echo '$(srcdir)/'`vtls/schannel.c - -vtls/libcurlu_la-schannel_verify.lo: vtls/schannel_verify.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-schannel_verify.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Tpo -c -o vtls/libcurlu_la-schannel_verify.lo `test -f 'vtls/schannel_verify.c' || echo '$(srcdir)/'`vtls/schannel_verify.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Tpo vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/schannel_verify.c' object='vtls/libcurlu_la-schannel_verify.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-schannel_verify.lo `test -f 'vtls/schannel_verify.c' || echo '$(srcdir)/'`vtls/schannel_verify.c - -vtls/libcurlu_la-sectransp.lo: vtls/sectransp.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-sectransp.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-sectransp.Tpo -c -o vtls/libcurlu_la-sectransp.lo `test -f 'vtls/sectransp.c' || echo '$(srcdir)/'`vtls/sectransp.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-sectransp.Tpo vtls/$(DEPDIR)/libcurlu_la-sectransp.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/sectransp.c' object='vtls/libcurlu_la-sectransp.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-sectransp.lo `test -f 'vtls/sectransp.c' || echo '$(srcdir)/'`vtls/sectransp.c - -vtls/libcurlu_la-vtls.lo: vtls/vtls.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-vtls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-vtls.Tpo -c -o vtls/libcurlu_la-vtls.lo `test -f 'vtls/vtls.c' || echo '$(srcdir)/'`vtls/vtls.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-vtls.Tpo vtls/$(DEPDIR)/libcurlu_la-vtls.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/vtls.c' object='vtls/libcurlu_la-vtls.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-vtls.lo `test -f 'vtls/vtls.c' || echo '$(srcdir)/'`vtls/vtls.c - -vtls/libcurlu_la-wolfssl.lo: vtls/wolfssl.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-wolfssl.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-wolfssl.Tpo -c -o vtls/libcurlu_la-wolfssl.lo `test -f 'vtls/wolfssl.c' || echo '$(srcdir)/'`vtls/wolfssl.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-wolfssl.Tpo vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/wolfssl.c' object='vtls/libcurlu_la-wolfssl.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-wolfssl.lo `test -f 'vtls/wolfssl.c' || echo '$(srcdir)/'`vtls/wolfssl.c - -vtls/libcurlu_la-x509asn1.lo: vtls/x509asn1.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-x509asn1.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-x509asn1.Tpo -c -o vtls/libcurlu_la-x509asn1.lo `test -f 'vtls/x509asn1.c' || echo '$(srcdir)/'`vtls/x509asn1.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-x509asn1.Tpo vtls/$(DEPDIR)/libcurlu_la-x509asn1.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/x509asn1.c' object='vtls/libcurlu_la-x509asn1.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-x509asn1.lo `test -f 'vtls/x509asn1.c' || echo '$(srcdir)/'`vtls/x509asn1.c - -vquic/libcurlu_la-msh3.lo: vquic/msh3.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-msh3.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-msh3.Tpo -c -o vquic/libcurlu_la-msh3.lo `test -f 'vquic/msh3.c' || echo '$(srcdir)/'`vquic/msh3.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-msh3.Tpo vquic/$(DEPDIR)/libcurlu_la-msh3.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/msh3.c' object='vquic/libcurlu_la-msh3.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-msh3.lo `test -f 'vquic/msh3.c' || echo '$(srcdir)/'`vquic/msh3.c - -vquic/libcurlu_la-ngtcp2.lo: vquic/ngtcp2.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-ngtcp2.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-ngtcp2.Tpo -c -o vquic/libcurlu_la-ngtcp2.lo `test -f 'vquic/ngtcp2.c' || echo '$(srcdir)/'`vquic/ngtcp2.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-ngtcp2.Tpo vquic/$(DEPDIR)/libcurlu_la-ngtcp2.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/ngtcp2.c' object='vquic/libcurlu_la-ngtcp2.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-ngtcp2.lo `test -f 'vquic/ngtcp2.c' || echo '$(srcdir)/'`vquic/ngtcp2.c - -vquic/libcurlu_la-quiche.lo: vquic/quiche.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-quiche.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-quiche.Tpo -c -o vquic/libcurlu_la-quiche.lo `test -f 'vquic/quiche.c' || echo '$(srcdir)/'`vquic/quiche.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-quiche.Tpo vquic/$(DEPDIR)/libcurlu_la-quiche.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/quiche.c' object='vquic/libcurlu_la-quiche.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-quiche.lo `test -f 'vquic/quiche.c' || echo '$(srcdir)/'`vquic/quiche.c - -vquic/libcurlu_la-vquic.lo: vquic/vquic.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-vquic.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-vquic.Tpo -c -o vquic/libcurlu_la-vquic.lo `test -f 'vquic/vquic.c' || echo '$(srcdir)/'`vquic/vquic.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-vquic.Tpo vquic/$(DEPDIR)/libcurlu_la-vquic.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/vquic.c' object='vquic/libcurlu_la-vquic.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-vquic.lo `test -f 'vquic/vquic.c' || echo '$(srcdir)/'`vquic/vquic.c - -vssh/libcurlu_la-libssh.lo: vssh/libssh.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vssh/libcurlu_la-libssh.lo -MD -MP -MF vssh/$(DEPDIR)/libcurlu_la-libssh.Tpo -c -o vssh/libcurlu_la-libssh.lo `test -f 'vssh/libssh.c' || echo '$(srcdir)/'`vssh/libssh.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vssh/$(DEPDIR)/libcurlu_la-libssh.Tpo vssh/$(DEPDIR)/libcurlu_la-libssh.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vssh/libssh.c' object='vssh/libcurlu_la-libssh.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vssh/libcurlu_la-libssh.lo `test -f 'vssh/libssh.c' || echo '$(srcdir)/'`vssh/libssh.c - -vssh/libcurlu_la-libssh2.lo: vssh/libssh2.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vssh/libcurlu_la-libssh2.lo -MD -MP -MF vssh/$(DEPDIR)/libcurlu_la-libssh2.Tpo -c -o vssh/libcurlu_la-libssh2.lo `test -f 'vssh/libssh2.c' || echo '$(srcdir)/'`vssh/libssh2.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vssh/$(DEPDIR)/libcurlu_la-libssh2.Tpo vssh/$(DEPDIR)/libcurlu_la-libssh2.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vssh/libssh2.c' object='vssh/libcurlu_la-libssh2.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vssh/libcurlu_la-libssh2.lo `test -f 'vssh/libssh2.c' || echo '$(srcdir)/'`vssh/libssh2.c - -vssh/libcurlu_la-wolfssh.lo: vssh/wolfssh.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vssh/libcurlu_la-wolfssh.lo -MD -MP -MF vssh/$(DEPDIR)/libcurlu_la-wolfssh.Tpo -c -o vssh/libcurlu_la-wolfssh.lo `test -f 'vssh/wolfssh.c' || echo '$(srcdir)/'`vssh/wolfssh.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vssh/$(DEPDIR)/libcurlu_la-wolfssh.Tpo vssh/$(DEPDIR)/libcurlu_la-wolfssh.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vssh/wolfssh.c' object='vssh/libcurlu_la-wolfssh.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vssh/libcurlu_la-wolfssh.lo `test -f 'vssh/wolfssh.c' || echo '$(srcdir)/'`vssh/wolfssh.c - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs - -rm -rf vauth/.libs vauth/_libs - -rm -rf vquic/.libs vquic/_libs - -rm -rf vssh/.libs vssh/_libs - -rm -rf vtls/.libs vtls/_libs - -ID: $(am__tagged_files) - $(am__define_uniq_tagged_files); mkid -fID $$unique -tags: tags-am -TAGS: tags - -tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - set x; \ - here=`pwd`; \ - $(am__define_uniq_tagged_files); \ - shift; \ - if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ - test -n "$$unique" || unique=$$empty_fix; \ - if test $$# -gt 0; then \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - "$$@" $$unique; \ - else \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - $$unique; \ - fi; \ - fi -ctags: ctags-am - -CTAGS: ctags -ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - $(am__define_uniq_tagged_files); \ - test -z "$(CTAGS_ARGS)$$unique" \ - || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ - $$unique - -GTAGS: - here=`$(am__cd) $(top_builddir) && pwd` \ - && $(am__cd) $(top_srcdir) \ - && gtags -i $(GTAGS_ARGS) "$$here" -cscopelist: cscopelist-am - -cscopelist-am: $(am__tagged_files) - list='$(am__tagged_files)'; \ - case "$(srcdir)" in \ - [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ - *) sdir=$(subdir)/$(srcdir) ;; \ - esac; \ - for i in $$list; do \ - if test -f "$$i"; then \ - echo "$(subdir)/$$i"; \ - else \ - echo "$$sdir/$$i"; \ - fi; \ - done >> $(top_builddir)/cscope.files - -distclean-tags: - -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -distdir: $(BUILT_SOURCES) - $(MAKE) $(AM_MAKEFLAGS) distdir-am - -distdir-am: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d "$(distdir)/$$file"; then \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ - else \ - test -f "$(distdir)/$$file" \ - || cp -p $$d/$$file "$(distdir)/$$file" \ - || exit 1; \ - fi; \ - done -check-am: all-am -check: check-am -@CURLDEBUG_FALSE@all-local: -all-am: Makefile $(LTLIBRARIES) curl_config.h all-local -installdirs: - for dir in "$(DESTDIR)$(libdir)"; do \ - test -z "$$dir" || $(MKDIR_P) "$$dir"; \ - done -install: install-am -install-exec: install-exec-am -install-data: install-data-am -uninstall: uninstall-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-am -install-strip: - if test -z '$(STRIP)'; then \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - install; \ - else \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ - fi -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) - -rm -f vauth/$(DEPDIR)/$(am__dirstamp) - -rm -f vauth/$(am__dirstamp) - -rm -f vquic/$(DEPDIR)/$(am__dirstamp) - -rm -f vquic/$(am__dirstamp) - -rm -f vssh/$(DEPDIR)/$(am__dirstamp) - -rm -f vssh/$(am__dirstamp) - -rm -f vtls/$(DEPDIR)/$(am__dirstamp) - -rm -f vtls/$(am__dirstamp) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -clean: clean-am - -clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ - clean-noinstLTLIBRARIES mostlyclean-am - -distclean: distclean-am - -rm -f ./$(DEPDIR)/libcurl_la-altsvc.Plo - -rm -f ./$(DEPDIR)/libcurl_la-amigaos.Plo - -rm -f ./$(DEPDIR)/libcurl_la-asyn-ares.Plo - -rm -f ./$(DEPDIR)/libcurl_la-asyn-thread.Plo - -rm -f ./$(DEPDIR)/libcurl_la-base64.Plo - -rm -f ./$(DEPDIR)/libcurl_la-bufref.Plo - -rm -f ./$(DEPDIR)/libcurl_la-c-hyper.Plo - -rm -f ./$(DEPDIR)/libcurl_la-conncache.Plo - -rm -f ./$(DEPDIR)/libcurl_la-connect.Plo - -rm -f ./$(DEPDIR)/libcurl_la-content_encoding.Plo - -rm -f ./$(DEPDIR)/libcurl_la-cookie.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_des.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_endian.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_fnmatch.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_get_line.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_gethostname.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_gssapi.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_memrchr.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_multibyte.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_ntlm_core.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_ntlm_wb.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_path.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_range.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_rtmp.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_sasl.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_sspi.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_threads.Plo - -rm -f ./$(DEPDIR)/libcurl_la-dict.Plo - -rm -f ./$(DEPDIR)/libcurl_la-doh.Plo - -rm -f ./$(DEPDIR)/libcurl_la-dynbuf.Plo - -rm -f ./$(DEPDIR)/libcurl_la-easy.Plo - -rm -f ./$(DEPDIR)/libcurl_la-easygetopt.Plo - -rm -f ./$(DEPDIR)/libcurl_la-easyoptions.Plo - -rm -f ./$(DEPDIR)/libcurl_la-escape.Plo - -rm -f ./$(DEPDIR)/libcurl_la-file.Plo - -rm -f ./$(DEPDIR)/libcurl_la-fileinfo.Plo - -rm -f ./$(DEPDIR)/libcurl_la-fopen.Plo - -rm -f ./$(DEPDIR)/libcurl_la-formdata.Plo - -rm -f ./$(DEPDIR)/libcurl_la-ftp.Plo - -rm -f ./$(DEPDIR)/libcurl_la-ftplistparser.Plo - -rm -f ./$(DEPDIR)/libcurl_la-getenv.Plo - -rm -f ./$(DEPDIR)/libcurl_la-getinfo.Plo - -rm -f ./$(DEPDIR)/libcurl_la-gopher.Plo - -rm -f ./$(DEPDIR)/libcurl_la-h2h3.Plo - -rm -f ./$(DEPDIR)/libcurl_la-hash.Plo - -rm -f ./$(DEPDIR)/libcurl_la-headers.Plo - -rm -f ./$(DEPDIR)/libcurl_la-hmac.Plo - -rm -f ./$(DEPDIR)/libcurl_la-hostasyn.Plo - -rm -f ./$(DEPDIR)/libcurl_la-hostip.Plo - -rm -f ./$(DEPDIR)/libcurl_la-hostip4.Plo - -rm -f ./$(DEPDIR)/libcurl_la-hostip6.Plo - -rm -f ./$(DEPDIR)/libcurl_la-hostsyn.Plo - -rm -f ./$(DEPDIR)/libcurl_la-hsts.Plo - -rm -f ./$(DEPDIR)/libcurl_la-http.Plo - -rm -f ./$(DEPDIR)/libcurl_la-http2.Plo - -rm -f ./$(DEPDIR)/libcurl_la-http_aws_sigv4.Plo - -rm -f ./$(DEPDIR)/libcurl_la-http_chunks.Plo - -rm -f ./$(DEPDIR)/libcurl_la-http_digest.Plo - -rm -f ./$(DEPDIR)/libcurl_la-http_negotiate.Plo - -rm -f ./$(DEPDIR)/libcurl_la-http_ntlm.Plo - -rm -f ./$(DEPDIR)/libcurl_la-http_proxy.Plo - -rm -f ./$(DEPDIR)/libcurl_la-idn_win32.Plo - -rm -f ./$(DEPDIR)/libcurl_la-if2ip.Plo - -rm -f ./$(DEPDIR)/libcurl_la-imap.Plo - -rm -f ./$(DEPDIR)/libcurl_la-inet_ntop.Plo - -rm -f ./$(DEPDIR)/libcurl_la-inet_pton.Plo - -rm -f ./$(DEPDIR)/libcurl_la-krb5.Plo - -rm -f ./$(DEPDIR)/libcurl_la-ldap.Plo - -rm -f ./$(DEPDIR)/libcurl_la-llist.Plo - -rm -f ./$(DEPDIR)/libcurl_la-md4.Plo - -rm -f ./$(DEPDIR)/libcurl_la-md5.Plo - -rm -f ./$(DEPDIR)/libcurl_la-memdebug.Plo - -rm -f ./$(DEPDIR)/libcurl_la-mime.Plo - -rm -f ./$(DEPDIR)/libcurl_la-mprintf.Plo - -rm -f ./$(DEPDIR)/libcurl_la-mqtt.Plo - -rm -f ./$(DEPDIR)/libcurl_la-multi.Plo - -rm -f ./$(DEPDIR)/libcurl_la-netrc.Plo - -rm -f ./$(DEPDIR)/libcurl_la-nonblock.Plo - -rm -f ./$(DEPDIR)/libcurl_la-noproxy.Plo - -rm -f ./$(DEPDIR)/libcurl_la-openldap.Plo - -rm -f ./$(DEPDIR)/libcurl_la-parsedate.Plo - -rm -f ./$(DEPDIR)/libcurl_la-pingpong.Plo - -rm -f ./$(DEPDIR)/libcurl_la-pop3.Plo - -rm -f ./$(DEPDIR)/libcurl_la-progress.Plo - -rm -f ./$(DEPDIR)/libcurl_la-psl.Plo - -rm -f ./$(DEPDIR)/libcurl_la-rand.Plo - -rm -f ./$(DEPDIR)/libcurl_la-rename.Plo - -rm -f ./$(DEPDIR)/libcurl_la-rtsp.Plo - -rm -f ./$(DEPDIR)/libcurl_la-select.Plo - -rm -f ./$(DEPDIR)/libcurl_la-sendf.Plo - -rm -f ./$(DEPDIR)/libcurl_la-setopt.Plo - -rm -f ./$(DEPDIR)/libcurl_la-sha256.Plo - -rm -f ./$(DEPDIR)/libcurl_la-share.Plo - -rm -f ./$(DEPDIR)/libcurl_la-slist.Plo - -rm -f ./$(DEPDIR)/libcurl_la-smb.Plo - -rm -f ./$(DEPDIR)/libcurl_la-smtp.Plo - -rm -f ./$(DEPDIR)/libcurl_la-socketpair.Plo - -rm -f ./$(DEPDIR)/libcurl_la-socks.Plo - -rm -f ./$(DEPDIR)/libcurl_la-socks_gssapi.Plo - -rm -f ./$(DEPDIR)/libcurl_la-socks_sspi.Plo - -rm -f ./$(DEPDIR)/libcurl_la-speedcheck.Plo - -rm -f ./$(DEPDIR)/libcurl_la-splay.Plo - -rm -f ./$(DEPDIR)/libcurl_la-strcase.Plo - -rm -f ./$(DEPDIR)/libcurl_la-strdup.Plo - -rm -f ./$(DEPDIR)/libcurl_la-strerror.Plo - -rm -f ./$(DEPDIR)/libcurl_la-strtok.Plo - -rm -f ./$(DEPDIR)/libcurl_la-strtoofft.Plo - -rm -f ./$(DEPDIR)/libcurl_la-system_win32.Plo - -rm -f ./$(DEPDIR)/libcurl_la-telnet.Plo - -rm -f ./$(DEPDIR)/libcurl_la-tftp.Plo - -rm -f ./$(DEPDIR)/libcurl_la-timediff.Plo - -rm -f ./$(DEPDIR)/libcurl_la-timeval.Plo - -rm -f ./$(DEPDIR)/libcurl_la-transfer.Plo - -rm -f ./$(DEPDIR)/libcurl_la-url.Plo - -rm -f ./$(DEPDIR)/libcurl_la-urlapi.Plo - -rm -f ./$(DEPDIR)/libcurl_la-version.Plo - -rm -f ./$(DEPDIR)/libcurl_la-version_win32.Plo - -rm -f ./$(DEPDIR)/libcurl_la-warnless.Plo - -rm -f ./$(DEPDIR)/libcurl_la-wildcard.Plo - -rm -f ./$(DEPDIR)/libcurl_la-ws.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-altsvc.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-amigaos.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-asyn-ares.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-asyn-thread.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-base64.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-bufref.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-c-hyper.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-conncache.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-connect.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-content_encoding.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-cookie.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_des.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_endian.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_fnmatch.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_get_line.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_gethostname.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_gssapi.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_memrchr.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_multibyte.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_ntlm_wb.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_path.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_range.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_rtmp.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_sasl.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_sspi.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_threads.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-dict.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-doh.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-dynbuf.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-easy.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-easygetopt.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-easyoptions.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-escape.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-file.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-fileinfo.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-fopen.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-formdata.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-ftp.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-ftplistparser.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-getenv.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-getinfo.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-gopher.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-h2h3.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-hash.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-headers.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-hmac.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-hostasyn.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-hostip.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-hostip4.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-hostip6.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-hostsyn.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-hsts.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-http.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-http2.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-http_aws_sigv4.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-http_chunks.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-http_digest.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-http_negotiate.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-http_ntlm.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-http_proxy.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-idn_win32.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-if2ip.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-imap.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-inet_ntop.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-inet_pton.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-krb5.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-ldap.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-llist.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-md4.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-md5.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-memdebug.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-mime.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-mprintf.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-mqtt.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-multi.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-netrc.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-nonblock.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-noproxy.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-openldap.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-parsedate.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-pingpong.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-pop3.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-progress.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-psl.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-rand.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-rename.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-rtsp.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-select.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-sendf.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-setopt.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-sha256.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-share.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-slist.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-smb.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-smtp.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-socketpair.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-socks.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-socks_gssapi.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-socks_sspi.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-speedcheck.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-splay.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-strcase.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-strdup.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-strerror.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-strtok.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-strtoofft.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-system_win32.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-telnet.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-tftp.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-timediff.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-timeval.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-transfer.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-url.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-urlapi.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-version.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-version_win32.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-warnless.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-wildcard.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-ws.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-cleartext.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-cram.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-digest.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-digest_sspi.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-gsasl.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-ntlm.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-oauth2.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-vauth.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-cleartext.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-cram.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-digest.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-gsasl.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-ntlm.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-oauth2.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-vauth.Plo - -rm -f vquic/$(DEPDIR)/libcurl_la-msh3.Plo - -rm -f vquic/$(DEPDIR)/libcurl_la-ngtcp2.Plo - -rm -f vquic/$(DEPDIR)/libcurl_la-quiche.Plo - -rm -f vquic/$(DEPDIR)/libcurl_la-vquic.Plo - -rm -f vquic/$(DEPDIR)/libcurlu_la-msh3.Plo - -rm -f vquic/$(DEPDIR)/libcurlu_la-ngtcp2.Plo - -rm -f vquic/$(DEPDIR)/libcurlu_la-quiche.Plo - -rm -f vquic/$(DEPDIR)/libcurlu_la-vquic.Plo - -rm -f vssh/$(DEPDIR)/libcurl_la-libssh.Plo - -rm -f vssh/$(DEPDIR)/libcurl_la-libssh2.Plo - -rm -f vssh/$(DEPDIR)/libcurl_la-wolfssh.Plo - -rm -f vssh/$(DEPDIR)/libcurlu_la-libssh.Plo - -rm -f vssh/$(DEPDIR)/libcurlu_la-libssh2.Plo - -rm -f vssh/$(DEPDIR)/libcurlu_la-wolfssh.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-bearssl.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-gskit.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-gtls.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-hostcheck.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-keylog.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-mbedtls.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-nss.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-openssl.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-rustls.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-schannel.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-sectransp.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-vtls.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-x509asn1.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-bearssl.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-gskit.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-gtls.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-hostcheck.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-keylog.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-mbedtls.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-nss.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-openssl.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-rustls.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-schannel.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-sectransp.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-vtls.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-x509asn1.Plo - -rm -f Makefile -distclean-am: clean-am distclean-compile distclean-generic \ - distclean-hdr distclean-tags - -dvi: dvi-am - -dvi-am: - -html: html-am - -html-am: - -info: info-am - -info-am: - -install-data-am: - -install-dvi: install-dvi-am - -install-dvi-am: - -install-exec-am: install-libLTLIBRARIES - -install-html: install-html-am - -install-html-am: - -install-info: install-info-am - -install-info-am: - -install-man: - -install-pdf: install-pdf-am - -install-pdf-am: - -install-ps: install-ps-am - -install-ps-am: - -installcheck-am: - -maintainer-clean: maintainer-clean-am - -rm -f ./$(DEPDIR)/libcurl_la-altsvc.Plo - -rm -f ./$(DEPDIR)/libcurl_la-amigaos.Plo - -rm -f ./$(DEPDIR)/libcurl_la-asyn-ares.Plo - -rm -f ./$(DEPDIR)/libcurl_la-asyn-thread.Plo - -rm -f ./$(DEPDIR)/libcurl_la-base64.Plo - -rm -f ./$(DEPDIR)/libcurl_la-bufref.Plo - -rm -f ./$(DEPDIR)/libcurl_la-c-hyper.Plo - -rm -f ./$(DEPDIR)/libcurl_la-conncache.Plo - -rm -f ./$(DEPDIR)/libcurl_la-connect.Plo - -rm -f ./$(DEPDIR)/libcurl_la-content_encoding.Plo - -rm -f ./$(DEPDIR)/libcurl_la-cookie.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_des.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_endian.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_fnmatch.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_get_line.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_gethostname.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_gssapi.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_memrchr.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_multibyte.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_ntlm_core.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_ntlm_wb.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_path.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_range.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_rtmp.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_sasl.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_sspi.Plo - -rm -f ./$(DEPDIR)/libcurl_la-curl_threads.Plo - -rm -f ./$(DEPDIR)/libcurl_la-dict.Plo - -rm -f ./$(DEPDIR)/libcurl_la-doh.Plo - -rm -f ./$(DEPDIR)/libcurl_la-dynbuf.Plo - -rm -f ./$(DEPDIR)/libcurl_la-easy.Plo - -rm -f ./$(DEPDIR)/libcurl_la-easygetopt.Plo - -rm -f ./$(DEPDIR)/libcurl_la-easyoptions.Plo - -rm -f ./$(DEPDIR)/libcurl_la-escape.Plo - -rm -f ./$(DEPDIR)/libcurl_la-file.Plo - -rm -f ./$(DEPDIR)/libcurl_la-fileinfo.Plo - -rm -f ./$(DEPDIR)/libcurl_la-fopen.Plo - -rm -f ./$(DEPDIR)/libcurl_la-formdata.Plo - -rm -f ./$(DEPDIR)/libcurl_la-ftp.Plo - -rm -f ./$(DEPDIR)/libcurl_la-ftplistparser.Plo - -rm -f ./$(DEPDIR)/libcurl_la-getenv.Plo - -rm -f ./$(DEPDIR)/libcurl_la-getinfo.Plo - -rm -f ./$(DEPDIR)/libcurl_la-gopher.Plo - -rm -f ./$(DEPDIR)/libcurl_la-h2h3.Plo - -rm -f ./$(DEPDIR)/libcurl_la-hash.Plo - -rm -f ./$(DEPDIR)/libcurl_la-headers.Plo - -rm -f ./$(DEPDIR)/libcurl_la-hmac.Plo - -rm -f ./$(DEPDIR)/libcurl_la-hostasyn.Plo - -rm -f ./$(DEPDIR)/libcurl_la-hostip.Plo - -rm -f ./$(DEPDIR)/libcurl_la-hostip4.Plo - -rm -f ./$(DEPDIR)/libcurl_la-hostip6.Plo - -rm -f ./$(DEPDIR)/libcurl_la-hostsyn.Plo - -rm -f ./$(DEPDIR)/libcurl_la-hsts.Plo - -rm -f ./$(DEPDIR)/libcurl_la-http.Plo - -rm -f ./$(DEPDIR)/libcurl_la-http2.Plo - -rm -f ./$(DEPDIR)/libcurl_la-http_aws_sigv4.Plo - -rm -f ./$(DEPDIR)/libcurl_la-http_chunks.Plo - -rm -f ./$(DEPDIR)/libcurl_la-http_digest.Plo - -rm -f ./$(DEPDIR)/libcurl_la-http_negotiate.Plo - -rm -f ./$(DEPDIR)/libcurl_la-http_ntlm.Plo - -rm -f ./$(DEPDIR)/libcurl_la-http_proxy.Plo - -rm -f ./$(DEPDIR)/libcurl_la-idn_win32.Plo - -rm -f ./$(DEPDIR)/libcurl_la-if2ip.Plo - -rm -f ./$(DEPDIR)/libcurl_la-imap.Plo - -rm -f ./$(DEPDIR)/libcurl_la-inet_ntop.Plo - -rm -f ./$(DEPDIR)/libcurl_la-inet_pton.Plo - -rm -f ./$(DEPDIR)/libcurl_la-krb5.Plo - -rm -f ./$(DEPDIR)/libcurl_la-ldap.Plo - -rm -f ./$(DEPDIR)/libcurl_la-llist.Plo - -rm -f ./$(DEPDIR)/libcurl_la-md4.Plo - -rm -f ./$(DEPDIR)/libcurl_la-md5.Plo - -rm -f ./$(DEPDIR)/libcurl_la-memdebug.Plo - -rm -f ./$(DEPDIR)/libcurl_la-mime.Plo - -rm -f ./$(DEPDIR)/libcurl_la-mprintf.Plo - -rm -f ./$(DEPDIR)/libcurl_la-mqtt.Plo - -rm -f ./$(DEPDIR)/libcurl_la-multi.Plo - -rm -f ./$(DEPDIR)/libcurl_la-netrc.Plo - -rm -f ./$(DEPDIR)/libcurl_la-nonblock.Plo - -rm -f ./$(DEPDIR)/libcurl_la-noproxy.Plo - -rm -f ./$(DEPDIR)/libcurl_la-openldap.Plo - -rm -f ./$(DEPDIR)/libcurl_la-parsedate.Plo - -rm -f ./$(DEPDIR)/libcurl_la-pingpong.Plo - -rm -f ./$(DEPDIR)/libcurl_la-pop3.Plo - -rm -f ./$(DEPDIR)/libcurl_la-progress.Plo - -rm -f ./$(DEPDIR)/libcurl_la-psl.Plo - -rm -f ./$(DEPDIR)/libcurl_la-rand.Plo - -rm -f ./$(DEPDIR)/libcurl_la-rename.Plo - -rm -f ./$(DEPDIR)/libcurl_la-rtsp.Plo - -rm -f ./$(DEPDIR)/libcurl_la-select.Plo - -rm -f ./$(DEPDIR)/libcurl_la-sendf.Plo - -rm -f ./$(DEPDIR)/libcurl_la-setopt.Plo - -rm -f ./$(DEPDIR)/libcurl_la-sha256.Plo - -rm -f ./$(DEPDIR)/libcurl_la-share.Plo - -rm -f ./$(DEPDIR)/libcurl_la-slist.Plo - -rm -f ./$(DEPDIR)/libcurl_la-smb.Plo - -rm -f ./$(DEPDIR)/libcurl_la-smtp.Plo - -rm -f ./$(DEPDIR)/libcurl_la-socketpair.Plo - -rm -f ./$(DEPDIR)/libcurl_la-socks.Plo - -rm -f ./$(DEPDIR)/libcurl_la-socks_gssapi.Plo - -rm -f ./$(DEPDIR)/libcurl_la-socks_sspi.Plo - -rm -f ./$(DEPDIR)/libcurl_la-speedcheck.Plo - -rm -f ./$(DEPDIR)/libcurl_la-splay.Plo - -rm -f ./$(DEPDIR)/libcurl_la-strcase.Plo - -rm -f ./$(DEPDIR)/libcurl_la-strdup.Plo - -rm -f ./$(DEPDIR)/libcurl_la-strerror.Plo - -rm -f ./$(DEPDIR)/libcurl_la-strtok.Plo - -rm -f ./$(DEPDIR)/libcurl_la-strtoofft.Plo - -rm -f ./$(DEPDIR)/libcurl_la-system_win32.Plo - -rm -f ./$(DEPDIR)/libcurl_la-telnet.Plo - -rm -f ./$(DEPDIR)/libcurl_la-tftp.Plo - -rm -f ./$(DEPDIR)/libcurl_la-timediff.Plo - -rm -f ./$(DEPDIR)/libcurl_la-timeval.Plo - -rm -f ./$(DEPDIR)/libcurl_la-transfer.Plo - -rm -f ./$(DEPDIR)/libcurl_la-url.Plo - -rm -f ./$(DEPDIR)/libcurl_la-urlapi.Plo - -rm -f ./$(DEPDIR)/libcurl_la-version.Plo - -rm -f ./$(DEPDIR)/libcurl_la-version_win32.Plo - -rm -f ./$(DEPDIR)/libcurl_la-warnless.Plo - -rm -f ./$(DEPDIR)/libcurl_la-wildcard.Plo - -rm -f ./$(DEPDIR)/libcurl_la-ws.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-altsvc.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-amigaos.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-asyn-ares.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-asyn-thread.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-base64.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-bufref.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-c-hyper.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-conncache.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-connect.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-content_encoding.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-cookie.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_des.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_endian.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_fnmatch.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_get_line.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_gethostname.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_gssapi.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_memrchr.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_multibyte.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_ntlm_wb.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_path.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_range.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_rtmp.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_sasl.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_sspi.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-curl_threads.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-dict.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-doh.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-dynbuf.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-easy.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-easygetopt.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-easyoptions.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-escape.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-file.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-fileinfo.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-fopen.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-formdata.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-ftp.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-ftplistparser.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-getenv.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-getinfo.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-gopher.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-h2h3.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-hash.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-headers.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-hmac.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-hostasyn.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-hostip.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-hostip4.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-hostip6.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-hostsyn.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-hsts.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-http.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-http2.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-http_aws_sigv4.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-http_chunks.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-http_digest.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-http_negotiate.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-http_ntlm.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-http_proxy.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-idn_win32.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-if2ip.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-imap.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-inet_ntop.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-inet_pton.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-krb5.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-ldap.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-llist.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-md4.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-md5.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-memdebug.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-mime.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-mprintf.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-mqtt.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-multi.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-netrc.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-nonblock.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-noproxy.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-openldap.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-parsedate.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-pingpong.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-pop3.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-progress.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-psl.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-rand.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-rename.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-rtsp.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-select.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-sendf.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-setopt.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-sha256.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-share.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-slist.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-smb.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-smtp.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-socketpair.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-socks.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-socks_gssapi.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-socks_sspi.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-speedcheck.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-splay.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-strcase.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-strdup.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-strerror.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-strtok.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-strtoofft.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-system_win32.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-telnet.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-tftp.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-timediff.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-timeval.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-transfer.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-url.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-urlapi.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-version.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-version_win32.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-warnless.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-wildcard.Plo - -rm -f ./$(DEPDIR)/libcurlu_la-ws.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-cleartext.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-cram.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-digest.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-digest_sspi.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-gsasl.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-ntlm.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-oauth2.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Plo - -rm -f vauth/$(DEPDIR)/libcurl_la-vauth.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-cleartext.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-cram.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-digest.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-gsasl.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-ntlm.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-oauth2.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Plo - -rm -f vauth/$(DEPDIR)/libcurlu_la-vauth.Plo - -rm -f vquic/$(DEPDIR)/libcurl_la-msh3.Plo - -rm -f vquic/$(DEPDIR)/libcurl_la-ngtcp2.Plo - -rm -f vquic/$(DEPDIR)/libcurl_la-quiche.Plo - -rm -f vquic/$(DEPDIR)/libcurl_la-vquic.Plo - -rm -f vquic/$(DEPDIR)/libcurlu_la-msh3.Plo - -rm -f vquic/$(DEPDIR)/libcurlu_la-ngtcp2.Plo - -rm -f vquic/$(DEPDIR)/libcurlu_la-quiche.Plo - -rm -f vquic/$(DEPDIR)/libcurlu_la-vquic.Plo - -rm -f vssh/$(DEPDIR)/libcurl_la-libssh.Plo - -rm -f vssh/$(DEPDIR)/libcurl_la-libssh2.Plo - -rm -f vssh/$(DEPDIR)/libcurl_la-wolfssh.Plo - -rm -f vssh/$(DEPDIR)/libcurlu_la-libssh.Plo - -rm -f vssh/$(DEPDIR)/libcurlu_la-libssh2.Plo - -rm -f vssh/$(DEPDIR)/libcurlu_la-wolfssh.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-bearssl.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-gskit.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-gtls.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-hostcheck.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-keylog.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-mbedtls.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-nss.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-openssl.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-rustls.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-schannel.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-sectransp.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-vtls.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo - -rm -f vtls/$(DEPDIR)/libcurl_la-x509asn1.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-bearssl.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-gskit.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-gtls.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-hostcheck.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-keylog.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-mbedtls.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-nss.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-openssl.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-rustls.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-schannel.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-sectransp.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-vtls.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo - -rm -f vtls/$(DEPDIR)/libcurlu_la-x509asn1.Plo - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-am - -mostlyclean-am: mostlyclean-compile mostlyclean-generic \ - mostlyclean-libtool - -pdf: pdf-am - -pdf-am: - -ps: ps-am - -ps-am: - -uninstall-am: uninstall-libLTLIBRARIES - -.MAKE: all install-am install-strip - -.PHONY: CTAGS GTAGS TAGS all all-am all-local am--depfiles check \ - check-am clean clean-generic clean-libLTLIBRARIES \ - clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ - ctags-am distclean distclean-compile distclean-generic \ - distclean-hdr distclean-libtool distclean-tags distdir dvi \ - dvi-am html html-am info info-am install install-am \ - install-data install-data-am install-dvi install-dvi-am \ - install-exec install-exec-am install-html install-html-am \ - install-info install-info-am install-libLTLIBRARIES \ - install-man install-pdf install-pdf-am install-ps \ - install-ps-am install-strip installcheck installcheck-am \ - installdirs maintainer-clean maintainer-clean-generic \ - mostlyclean mostlyclean-compile mostlyclean-generic \ - mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ - uninstall-am uninstall-libLTLIBRARIES - -.PRECIOUS: Makefile - -@OS_WINDOWS_TRUE@@USE_CPPFLAG_CURL_STATICLIB_FALSE@$(LIB_RCFILES): $(top_srcdir)/include/curl/curlver.h - -checksrc: - $(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) \ - -W$(srcdir)/curl_config.h $(srcdir)/*.[ch] $(srcdir)/vauth/*.[ch] \ - $(srcdir)/vtls/*.[ch] $(srcdir)/vquic/*.[ch] $(srcdir)/vssh/*.[ch]) - -# for debug builds, we scan the sources on all regular make invokes -@CURLDEBUG_TRUE@all-local: checksrc - -tidy: - $(TIDY) $(CSOURCES) $(TIDYFLAGS) -- $(AM_CPPFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H - -optiontable: - perl optiontable.pl < $(top_srcdir)/include/curl/curl.h > easyoptions.c - -# Warning is "normal": libtool: error: ignoring unknown tag RC -@OS_WINDOWS_TRUE@.rc.lo: -@OS_WINDOWS_TRUE@ $(LIBTOOL) --tag=RC --mode=compile $(RC) -I$(top_srcdir)/include $(RCFLAGS) -i $< -o $@ - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: +# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+#***************************************************************************
+# _ _ ____ _
+# Project ___| | | | _ \| |
+# / __| | | | |_) | |
+# | (__| |_| | _ <| |___
+# \___|\___/|_| \_\_____|
+#
+# Copyright (C) 1998 - 2022, 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
+#
+###########################################################################
+
+#***************************************************************************
+# _ _ ____ _
+# Project ___| | | | _ \| |
+# / __| | | | |_) | |
+# | (__| |_| | _ <| |___
+# \___|\___/|_| \_\_____|
+#
+# Copyright (C) 1998 - 2022, 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
+#
+###########################################################################
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@CURL_LT_SHLIB_USE_VERSION_INFO_TRUE@am__append_1 = $(VERSIONINFO)
+@CURL_LT_SHLIB_USE_NO_UNDEFINED_TRUE@am__append_2 = -no-undefined
+@CURL_LT_SHLIB_USE_MIMPURE_TEXT_TRUE@am__append_3 = -mimpure-text
+@CURL_LT_SHLIB_USE_VERSIONED_SYMBOLS_TRUE@am__append_4 = -Wl,--version-script=libcurl.vers
+# if symbol-hiding is enabled, hide them!
+@CURL_LT_SHLIB_USE_VERSIONED_SYMBOLS_FALSE@@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_5 = -export-symbols-regex '^curl_.*'
+@USE_CPPFLAG_CURL_STATICLIB_TRUE@am__append_6 = -DCURL_STATICLIB
+@HAVE_WINDRES_TRUE@@USE_CPPFLAG_CURL_STATICLIB_FALSE@am__append_7 = $(LIB_RCFILES)
+@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_8 = -DCURL_HIDDEN_SYMBOLS
+@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_9 = $(CFLAG_CURL_SYMBOL_HIDING)
+subdir = lib
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/curl-amissl.m4 \
+ $(top_srcdir)/m4/curl-bearssl.m4 \
+ $(top_srcdir)/m4/curl-compilers.m4 \
+ $(top_srcdir)/m4/curl-confopts.m4 \
+ $(top_srcdir)/m4/curl-functions.m4 \
+ $(top_srcdir)/m4/curl-gnutls.m4 \
+ $(top_srcdir)/m4/curl-mbedtls.m4 $(top_srcdir)/m4/curl-nss.m4 \
+ $(top_srcdir)/m4/curl-openssl.m4 \
+ $(top_srcdir)/m4/curl-override.m4 \
+ $(top_srcdir)/m4/curl-reentrant.m4 \
+ $(top_srcdir)/m4/curl-rustls.m4 \
+ $(top_srcdir)/m4/curl-schannel.m4 \
+ $(top_srcdir)/m4/curl-sectransp.m4 \
+ $(top_srcdir)/m4/curl-sysconfig.m4 \
+ $(top_srcdir)/m4/curl-wolfssl.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/xc-am-iface.m4 \
+ $(top_srcdir)/m4/xc-cc-check.m4 \
+ $(top_srcdir)/m4/xc-lt-iface.m4 \
+ $(top_srcdir)/m4/xc-translit.m4 \
+ $(top_srcdir)/m4/xc-val-flgs.m4 \
+ $(top_srcdir)/m4/zz40-xc-ovr.m4 \
+ $(top_srcdir)/m4/zz50-xc-ovr.m4 \
+ $(top_srcdir)/m4/zz60-xc-ovr.m4 $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = curl_config.h
+CONFIG_CLEAN_FILES = libcurl.vers libcurl.plist
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)"
+LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES)
+libcurl_la_LIBADD =
+am__libcurl_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \
+ asyn-thread.c base64.c bufref.c c-hyper.c cfilters.c \
+ conncache.c connect.c content_encoding.c cookie.c \
+ curl_addrinfo.c curl_des.c curl_endian.c curl_fnmatch.c \
+ curl_get_line.c curl_gethostname.c curl_gssapi.c \
+ curl_memrchr.c curl_multibyte.c curl_ntlm_core.c \
+ curl_ntlm_wb.c curl_path.c curl_range.c curl_rtmp.c \
+ curl_sasl.c curl_sspi.c curl_threads.c dict.c doh.c dynbuf.c \
+ easy.c easygetopt.c easyoptions.c escape.c file.c fileinfo.c \
+ fopen.c formdata.c ftp.c ftplistparser.c getenv.c getinfo.c \
+ gopher.c h2h3.c hash.c headers.c hmac.c hostasyn.c hostip.c \
+ hostip4.c hostip6.c hostsyn.c hsts.c http.c http2.c \
+ http_chunks.c http_digest.c http_negotiate.c http_ntlm.c \
+ http_proxy.c http_aws_sigv4.c idn.c if2ip.c imap.c inet_ntop.c \
+ inet_pton.c krb5.c ldap.c llist.c md4.c md5.c memdebug.c \
+ mime.c mprintf.c mqtt.c multi.c netrc.c nonblock.c noproxy.c \
+ openldap.c parsedate.c pingpong.c pop3.c progress.c psl.c \
+ rand.c rename.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 strerror.c strtok.c strtoofft.c system_win32.c \
+ telnet.c tftp.c timediff.c timeval.c transfer.c url.c urlapi.c \
+ version.c version_win32.c warnless.c wildcard.c ws.c \
+ 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 vtls/bearssl.c vtls/gskit.c vtls/gtls.c \
+ vtls/hostcheck.c vtls/keylog.c vtls/mbedtls.c \
+ vtls/mbedtls_threadlock.c vtls/nss.c vtls/openssl.c \
+ vtls/rustls.c vtls/schannel.c vtls/schannel_verify.c \
+ vtls/sectransp.c vtls/vtls.c vtls/wolfssl.c vtls/x509asn1.c \
+ vquic/msh3.c vquic/ngtcp2.c vquic/quiche.c vquic/vquic.c \
+ vssh/libssh.c vssh/libssh2.c vssh/wolfssh.c altsvc.h amigaos.h \
+ arpa_telnet.h asyn.h bufref.h c-hyper.h cfilters.h conncache.h \
+ connect.h content_encoding.h cookie.h curl_addrinfo.h \
+ curl_base64.h curl_ctype.h curl_des.h curl_endian.h \
+ curl_fnmatch.h curl_get_line.h curl_gethostname.h \
+ curl_gssapi.h curl_hmac.h curl_krb5.h curl_ldap.h curl_md4.h \
+ curl_md5.h curl_memory.h curl_memrchr.h curl_multibyte.h \
+ curl_ntlm_core.h curl_ntlm_wb.h curl_path.h curl_printf.h \
+ curl_range.h curl_rtmp.h curl_sasl.h curl_setup.h \
+ curl_setup_once.h curl_sha256.h curl_sspi.h curl_threads.h \
+ curlx.h dict.h doh.h dynbuf.h easy_lock.h easyif.h \
+ easyoptions.h escape.h file.h fileinfo.h fopen.h formdata.h \
+ functypes.h ftp.h ftplistparser.h getinfo.h gopher.h h2h3.h \
+ hash.h headers.h hostip.h hsts.h http.h http2.h http_chunks.h \
+ http_digest.h http_negotiate.h http_ntlm.h http_proxy.h \
+ http_aws_sigv4.h idn.h if2ip.h imap.h inet_ntop.h inet_pton.h \
+ llist.h memdebug.h mime.h mqtt.h multihandle.h multiif.h \
+ netrc.h nonblock.h noproxy.h parsedate.h pingpong.h pop3.h \
+ progress.h psl.h quic.h rand.h rename.h rtsp.h select.h \
+ sendf.h setopt.h setup-vms.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 strtok.h strtoofft.h \
+ system_win32.h telnet.h tftp.h timediff.h timeval.h transfer.h \
+ url.h urlapi-int.h urldata.h version_win32.h warnless.h \
+ wildcard.h ws.h vauth/digest.h vauth/ntlm.h vauth/vauth.h \
+ vtls/bearssl.h vtls/gskit.h vtls/gtls.h vtls/hostcheck.h \
+ vtls/keylog.h vtls/mbedtls.h vtls/mbedtls_threadlock.h \
+ vtls/nssg.h vtls/openssl.h vtls/rustls.h vtls/schannel.h \
+ vtls/sectransp.h vtls/vtls.h vtls/vtls_int.h vtls/wolfssl.h \
+ vtls/x509asn1.h vquic/msh3.h vquic/ngtcp2.h vquic/quiche.h \
+ vquic/vquic.h vssh/ssh.h libcurl.rc
+am__objects_1 = libcurl_la-altsvc.lo libcurl_la-amigaos.lo \
+ libcurl_la-asyn-ares.lo libcurl_la-asyn-thread.lo \
+ libcurl_la-base64.lo libcurl_la-bufref.lo \
+ libcurl_la-c-hyper.lo libcurl_la-cfilters.lo \
+ libcurl_la-conncache.lo libcurl_la-connect.lo \
+ libcurl_la-content_encoding.lo libcurl_la-cookie.lo \
+ libcurl_la-curl_addrinfo.lo libcurl_la-curl_des.lo \
+ libcurl_la-curl_endian.lo libcurl_la-curl_fnmatch.lo \
+ libcurl_la-curl_get_line.lo libcurl_la-curl_gethostname.lo \
+ libcurl_la-curl_gssapi.lo libcurl_la-curl_memrchr.lo \
+ libcurl_la-curl_multibyte.lo libcurl_la-curl_ntlm_core.lo \
+ libcurl_la-curl_ntlm_wb.lo libcurl_la-curl_path.lo \
+ libcurl_la-curl_range.lo libcurl_la-curl_rtmp.lo \
+ libcurl_la-curl_sasl.lo libcurl_la-curl_sspi.lo \
+ libcurl_la-curl_threads.lo libcurl_la-dict.lo \
+ libcurl_la-doh.lo libcurl_la-dynbuf.lo libcurl_la-easy.lo \
+ libcurl_la-easygetopt.lo libcurl_la-easyoptions.lo \
+ libcurl_la-escape.lo libcurl_la-file.lo libcurl_la-fileinfo.lo \
+ libcurl_la-fopen.lo libcurl_la-formdata.lo libcurl_la-ftp.lo \
+ libcurl_la-ftplistparser.lo libcurl_la-getenv.lo \
+ libcurl_la-getinfo.lo libcurl_la-gopher.lo libcurl_la-h2h3.lo \
+ libcurl_la-hash.lo libcurl_la-headers.lo libcurl_la-hmac.lo \
+ libcurl_la-hostasyn.lo libcurl_la-hostip.lo \
+ libcurl_la-hostip4.lo libcurl_la-hostip6.lo \
+ libcurl_la-hostsyn.lo libcurl_la-hsts.lo libcurl_la-http.lo \
+ libcurl_la-http2.lo libcurl_la-http_chunks.lo \
+ libcurl_la-http_digest.lo libcurl_la-http_negotiate.lo \
+ libcurl_la-http_ntlm.lo libcurl_la-http_proxy.lo \
+ libcurl_la-http_aws_sigv4.lo libcurl_la-idn.lo \
+ libcurl_la-if2ip.lo libcurl_la-imap.lo libcurl_la-inet_ntop.lo \
+ libcurl_la-inet_pton.lo libcurl_la-krb5.lo libcurl_la-ldap.lo \
+ libcurl_la-llist.lo libcurl_la-md4.lo libcurl_la-md5.lo \
+ libcurl_la-memdebug.lo libcurl_la-mime.lo \
+ libcurl_la-mprintf.lo libcurl_la-mqtt.lo libcurl_la-multi.lo \
+ libcurl_la-netrc.lo libcurl_la-nonblock.lo \
+ libcurl_la-noproxy.lo libcurl_la-openldap.lo \
+ libcurl_la-parsedate.lo libcurl_la-pingpong.lo \
+ libcurl_la-pop3.lo libcurl_la-progress.lo libcurl_la-psl.lo \
+ libcurl_la-rand.lo libcurl_la-rename.lo libcurl_la-rtsp.lo \
+ libcurl_la-select.lo libcurl_la-sendf.lo libcurl_la-setopt.lo \
+ libcurl_la-sha256.lo libcurl_la-share.lo libcurl_la-slist.lo \
+ libcurl_la-smb.lo libcurl_la-smtp.lo libcurl_la-socketpair.lo \
+ libcurl_la-socks.lo libcurl_la-socks_gssapi.lo \
+ libcurl_la-socks_sspi.lo libcurl_la-speedcheck.lo \
+ libcurl_la-splay.lo libcurl_la-strcase.lo libcurl_la-strdup.lo \
+ libcurl_la-strerror.lo libcurl_la-strtok.lo \
+ libcurl_la-strtoofft.lo libcurl_la-system_win32.lo \
+ libcurl_la-telnet.lo libcurl_la-tftp.lo libcurl_la-timediff.lo \
+ libcurl_la-timeval.lo libcurl_la-transfer.lo libcurl_la-url.lo \
+ libcurl_la-urlapi.lo libcurl_la-version.lo \
+ libcurl_la-version_win32.lo libcurl_la-warnless.lo \
+ libcurl_la-wildcard.lo libcurl_la-ws.lo
+am__dirstamp = $(am__leading_dot)dirstamp
+am__objects_2 = vauth/libcurl_la-cleartext.lo vauth/libcurl_la-cram.lo \
+ vauth/libcurl_la-digest.lo vauth/libcurl_la-digest_sspi.lo \
+ vauth/libcurl_la-gsasl.lo vauth/libcurl_la-krb5_gssapi.lo \
+ vauth/libcurl_la-krb5_sspi.lo vauth/libcurl_la-ntlm.lo \
+ vauth/libcurl_la-ntlm_sspi.lo vauth/libcurl_la-oauth2.lo \
+ vauth/libcurl_la-spnego_gssapi.lo \
+ vauth/libcurl_la-spnego_sspi.lo vauth/libcurl_la-vauth.lo
+am__objects_3 = vtls/libcurl_la-bearssl.lo vtls/libcurl_la-gskit.lo \
+ vtls/libcurl_la-gtls.lo vtls/libcurl_la-hostcheck.lo \
+ vtls/libcurl_la-keylog.lo vtls/libcurl_la-mbedtls.lo \
+ vtls/libcurl_la-mbedtls_threadlock.lo vtls/libcurl_la-nss.lo \
+ vtls/libcurl_la-openssl.lo vtls/libcurl_la-rustls.lo \
+ vtls/libcurl_la-schannel.lo vtls/libcurl_la-schannel_verify.lo \
+ vtls/libcurl_la-sectransp.lo vtls/libcurl_la-vtls.lo \
+ vtls/libcurl_la-wolfssl.lo vtls/libcurl_la-x509asn1.lo
+am__objects_4 = vquic/libcurl_la-msh3.lo vquic/libcurl_la-ngtcp2.lo \
+ vquic/libcurl_la-quiche.lo vquic/libcurl_la-vquic.lo
+am__objects_5 = vssh/libcurl_la-libssh.lo vssh/libcurl_la-libssh2.lo \
+ vssh/libcurl_la-wolfssh.lo
+am__objects_6 = $(am__objects_1) $(am__objects_2) $(am__objects_3) \
+ $(am__objects_4) $(am__objects_5)
+am__objects_7 =
+am__objects_8 = $(am__objects_7) $(am__objects_7) $(am__objects_7) \
+ $(am__objects_7) $(am__objects_7)
+am__objects_9 = libcurl.lo
+@HAVE_WINDRES_TRUE@@USE_CPPFLAG_CURL_STATICLIB_FALSE@am__objects_10 = $(am__objects_9)
+am_libcurl_la_OBJECTS = $(am__objects_6) $(am__objects_8) \
+ $(am__objects_10)
+libcurl_la_OBJECTS = $(am_libcurl_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libcurl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libcurl_la_CFLAGS) \
+ $(CFLAGS) $(libcurl_la_LDFLAGS) $(LDFLAGS) -o $@
+libcurlu_la_LIBADD =
+am__objects_11 = libcurlu_la-altsvc.lo libcurlu_la-amigaos.lo \
+ libcurlu_la-asyn-ares.lo libcurlu_la-asyn-thread.lo \
+ libcurlu_la-base64.lo libcurlu_la-bufref.lo \
+ libcurlu_la-c-hyper.lo libcurlu_la-cfilters.lo \
+ libcurlu_la-conncache.lo libcurlu_la-connect.lo \
+ libcurlu_la-content_encoding.lo libcurlu_la-cookie.lo \
+ libcurlu_la-curl_addrinfo.lo libcurlu_la-curl_des.lo \
+ libcurlu_la-curl_endian.lo libcurlu_la-curl_fnmatch.lo \
+ libcurlu_la-curl_get_line.lo libcurlu_la-curl_gethostname.lo \
+ libcurlu_la-curl_gssapi.lo libcurlu_la-curl_memrchr.lo \
+ libcurlu_la-curl_multibyte.lo libcurlu_la-curl_ntlm_core.lo \
+ libcurlu_la-curl_ntlm_wb.lo libcurlu_la-curl_path.lo \
+ libcurlu_la-curl_range.lo libcurlu_la-curl_rtmp.lo \
+ libcurlu_la-curl_sasl.lo libcurlu_la-curl_sspi.lo \
+ libcurlu_la-curl_threads.lo libcurlu_la-dict.lo \
+ libcurlu_la-doh.lo libcurlu_la-dynbuf.lo libcurlu_la-easy.lo \
+ libcurlu_la-easygetopt.lo libcurlu_la-easyoptions.lo \
+ libcurlu_la-escape.lo libcurlu_la-file.lo \
+ libcurlu_la-fileinfo.lo libcurlu_la-fopen.lo \
+ libcurlu_la-formdata.lo libcurlu_la-ftp.lo \
+ libcurlu_la-ftplistparser.lo libcurlu_la-getenv.lo \
+ libcurlu_la-getinfo.lo libcurlu_la-gopher.lo \
+ libcurlu_la-h2h3.lo libcurlu_la-hash.lo libcurlu_la-headers.lo \
+ libcurlu_la-hmac.lo libcurlu_la-hostasyn.lo \
+ libcurlu_la-hostip.lo libcurlu_la-hostip4.lo \
+ libcurlu_la-hostip6.lo libcurlu_la-hostsyn.lo \
+ libcurlu_la-hsts.lo libcurlu_la-http.lo libcurlu_la-http2.lo \
+ libcurlu_la-http_chunks.lo libcurlu_la-http_digest.lo \
+ libcurlu_la-http_negotiate.lo libcurlu_la-http_ntlm.lo \
+ libcurlu_la-http_proxy.lo libcurlu_la-http_aws_sigv4.lo \
+ libcurlu_la-idn.lo libcurlu_la-if2ip.lo libcurlu_la-imap.lo \
+ libcurlu_la-inet_ntop.lo libcurlu_la-inet_pton.lo \
+ libcurlu_la-krb5.lo libcurlu_la-ldap.lo libcurlu_la-llist.lo \
+ libcurlu_la-md4.lo libcurlu_la-md5.lo libcurlu_la-memdebug.lo \
+ libcurlu_la-mime.lo libcurlu_la-mprintf.lo libcurlu_la-mqtt.lo \
+ libcurlu_la-multi.lo libcurlu_la-netrc.lo \
+ libcurlu_la-nonblock.lo libcurlu_la-noproxy.lo \
+ libcurlu_la-openldap.lo libcurlu_la-parsedate.lo \
+ libcurlu_la-pingpong.lo libcurlu_la-pop3.lo \
+ libcurlu_la-progress.lo libcurlu_la-psl.lo libcurlu_la-rand.lo \
+ libcurlu_la-rename.lo libcurlu_la-rtsp.lo \
+ libcurlu_la-select.lo libcurlu_la-sendf.lo \
+ libcurlu_la-setopt.lo libcurlu_la-sha256.lo \
+ libcurlu_la-share.lo libcurlu_la-slist.lo libcurlu_la-smb.lo \
+ libcurlu_la-smtp.lo libcurlu_la-socketpair.lo \
+ libcurlu_la-socks.lo libcurlu_la-socks_gssapi.lo \
+ libcurlu_la-socks_sspi.lo libcurlu_la-speedcheck.lo \
+ libcurlu_la-splay.lo libcurlu_la-strcase.lo \
+ libcurlu_la-strdup.lo libcurlu_la-strerror.lo \
+ libcurlu_la-strtok.lo libcurlu_la-strtoofft.lo \
+ libcurlu_la-system_win32.lo libcurlu_la-telnet.lo \
+ libcurlu_la-tftp.lo libcurlu_la-timediff.lo \
+ libcurlu_la-timeval.lo libcurlu_la-transfer.lo \
+ libcurlu_la-url.lo libcurlu_la-urlapi.lo \
+ libcurlu_la-version.lo libcurlu_la-version_win32.lo \
+ libcurlu_la-warnless.lo libcurlu_la-wildcard.lo \
+ libcurlu_la-ws.lo
+am__objects_12 = vauth/libcurlu_la-cleartext.lo \
+ vauth/libcurlu_la-cram.lo vauth/libcurlu_la-digest.lo \
+ vauth/libcurlu_la-digest_sspi.lo vauth/libcurlu_la-gsasl.lo \
+ vauth/libcurlu_la-krb5_gssapi.lo \
+ vauth/libcurlu_la-krb5_sspi.lo vauth/libcurlu_la-ntlm.lo \
+ vauth/libcurlu_la-ntlm_sspi.lo vauth/libcurlu_la-oauth2.lo \
+ vauth/libcurlu_la-spnego_gssapi.lo \
+ vauth/libcurlu_la-spnego_sspi.lo vauth/libcurlu_la-vauth.lo
+am__objects_13 = vtls/libcurlu_la-bearssl.lo vtls/libcurlu_la-gskit.lo \
+ vtls/libcurlu_la-gtls.lo vtls/libcurlu_la-hostcheck.lo \
+ vtls/libcurlu_la-keylog.lo vtls/libcurlu_la-mbedtls.lo \
+ vtls/libcurlu_la-mbedtls_threadlock.lo vtls/libcurlu_la-nss.lo \
+ vtls/libcurlu_la-openssl.lo vtls/libcurlu_la-rustls.lo \
+ vtls/libcurlu_la-schannel.lo \
+ vtls/libcurlu_la-schannel_verify.lo \
+ vtls/libcurlu_la-sectransp.lo vtls/libcurlu_la-vtls.lo \
+ vtls/libcurlu_la-wolfssl.lo vtls/libcurlu_la-x509asn1.lo
+am__objects_14 = vquic/libcurlu_la-msh3.lo vquic/libcurlu_la-ngtcp2.lo \
+ vquic/libcurlu_la-quiche.lo vquic/libcurlu_la-vquic.lo
+am__objects_15 = vssh/libcurlu_la-libssh.lo \
+ vssh/libcurlu_la-libssh2.lo vssh/libcurlu_la-wolfssh.lo
+am__objects_16 = $(am__objects_11) $(am__objects_12) $(am__objects_13) \
+ $(am__objects_14) $(am__objects_15)
+am_libcurlu_la_OBJECTS = $(am__objects_16) $(am__objects_8)
+libcurlu_la_OBJECTS = $(am_libcurlu_la_OBJECTS)
+libcurlu_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libcurlu_la_CFLAGS) \
+ $(CFLAGS) $(libcurlu_la_LDFLAGS) $(LDFLAGS) -o $@
+@BUILD_UNITTESTS_TRUE@am_libcurlu_la_rpath =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES =
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
+ ./$(DEPDIR)/libcurl_la-amigaos.Plo \
+ ./$(DEPDIR)/libcurl_la-asyn-ares.Plo \
+ ./$(DEPDIR)/libcurl_la-asyn-thread.Plo \
+ ./$(DEPDIR)/libcurl_la-base64.Plo \
+ ./$(DEPDIR)/libcurl_la-bufref.Plo \
+ ./$(DEPDIR)/libcurl_la-c-hyper.Plo \
+ ./$(DEPDIR)/libcurl_la-cfilters.Plo \
+ ./$(DEPDIR)/libcurl_la-conncache.Plo \
+ ./$(DEPDIR)/libcurl_la-connect.Plo \
+ ./$(DEPDIR)/libcurl_la-content_encoding.Plo \
+ ./$(DEPDIR)/libcurl_la-cookie.Plo \
+ ./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo \
+ ./$(DEPDIR)/libcurl_la-curl_des.Plo \
+ ./$(DEPDIR)/libcurl_la-curl_endian.Plo \
+ ./$(DEPDIR)/libcurl_la-curl_fnmatch.Plo \
+ ./$(DEPDIR)/libcurl_la-curl_get_line.Plo \
+ ./$(DEPDIR)/libcurl_la-curl_gethostname.Plo \
+ ./$(DEPDIR)/libcurl_la-curl_gssapi.Plo \
+ ./$(DEPDIR)/libcurl_la-curl_memrchr.Plo \
+ ./$(DEPDIR)/libcurl_la-curl_multibyte.Plo \
+ ./$(DEPDIR)/libcurl_la-curl_ntlm_core.Plo \
+ ./$(DEPDIR)/libcurl_la-curl_ntlm_wb.Plo \
+ ./$(DEPDIR)/libcurl_la-curl_path.Plo \
+ ./$(DEPDIR)/libcurl_la-curl_range.Plo \
+ ./$(DEPDIR)/libcurl_la-curl_rtmp.Plo \
+ ./$(DEPDIR)/libcurl_la-curl_sasl.Plo \
+ ./$(DEPDIR)/libcurl_la-curl_sspi.Plo \
+ ./$(DEPDIR)/libcurl_la-curl_threads.Plo \
+ ./$(DEPDIR)/libcurl_la-dict.Plo ./$(DEPDIR)/libcurl_la-doh.Plo \
+ ./$(DEPDIR)/libcurl_la-dynbuf.Plo \
+ ./$(DEPDIR)/libcurl_la-easy.Plo \
+ ./$(DEPDIR)/libcurl_la-easygetopt.Plo \
+ ./$(DEPDIR)/libcurl_la-easyoptions.Plo \
+ ./$(DEPDIR)/libcurl_la-escape.Plo \
+ ./$(DEPDIR)/libcurl_la-file.Plo \
+ ./$(DEPDIR)/libcurl_la-fileinfo.Plo \
+ ./$(DEPDIR)/libcurl_la-fopen.Plo \
+ ./$(DEPDIR)/libcurl_la-formdata.Plo \
+ ./$(DEPDIR)/libcurl_la-ftp.Plo \
+ ./$(DEPDIR)/libcurl_la-ftplistparser.Plo \
+ ./$(DEPDIR)/libcurl_la-getenv.Plo \
+ ./$(DEPDIR)/libcurl_la-getinfo.Plo \
+ ./$(DEPDIR)/libcurl_la-gopher.Plo \
+ ./$(DEPDIR)/libcurl_la-h2h3.Plo \
+ ./$(DEPDIR)/libcurl_la-hash.Plo \
+ ./$(DEPDIR)/libcurl_la-headers.Plo \
+ ./$(DEPDIR)/libcurl_la-hmac.Plo \
+ ./$(DEPDIR)/libcurl_la-hostasyn.Plo \
+ ./$(DEPDIR)/libcurl_la-hostip.Plo \
+ ./$(DEPDIR)/libcurl_la-hostip4.Plo \
+ ./$(DEPDIR)/libcurl_la-hostip6.Plo \
+ ./$(DEPDIR)/libcurl_la-hostsyn.Plo \
+ ./$(DEPDIR)/libcurl_la-hsts.Plo \
+ ./$(DEPDIR)/libcurl_la-http.Plo \
+ ./$(DEPDIR)/libcurl_la-http2.Plo \
+ ./$(DEPDIR)/libcurl_la-http_aws_sigv4.Plo \
+ ./$(DEPDIR)/libcurl_la-http_chunks.Plo \
+ ./$(DEPDIR)/libcurl_la-http_digest.Plo \
+ ./$(DEPDIR)/libcurl_la-http_negotiate.Plo \
+ ./$(DEPDIR)/libcurl_la-http_ntlm.Plo \
+ ./$(DEPDIR)/libcurl_la-http_proxy.Plo \
+ ./$(DEPDIR)/libcurl_la-idn.Plo \
+ ./$(DEPDIR)/libcurl_la-if2ip.Plo \
+ ./$(DEPDIR)/libcurl_la-imap.Plo \
+ ./$(DEPDIR)/libcurl_la-inet_ntop.Plo \
+ ./$(DEPDIR)/libcurl_la-inet_pton.Plo \
+ ./$(DEPDIR)/libcurl_la-krb5.Plo \
+ ./$(DEPDIR)/libcurl_la-ldap.Plo \
+ ./$(DEPDIR)/libcurl_la-llist.Plo \
+ ./$(DEPDIR)/libcurl_la-md4.Plo ./$(DEPDIR)/libcurl_la-md5.Plo \
+ ./$(DEPDIR)/libcurl_la-memdebug.Plo \
+ ./$(DEPDIR)/libcurl_la-mime.Plo \
+ ./$(DEPDIR)/libcurl_la-mprintf.Plo \
+ ./$(DEPDIR)/libcurl_la-mqtt.Plo \
+ ./$(DEPDIR)/libcurl_la-multi.Plo \
+ ./$(DEPDIR)/libcurl_la-netrc.Plo \
+ ./$(DEPDIR)/libcurl_la-nonblock.Plo \
+ ./$(DEPDIR)/libcurl_la-noproxy.Plo \
+ ./$(DEPDIR)/libcurl_la-openldap.Plo \
+ ./$(DEPDIR)/libcurl_la-parsedate.Plo \
+ ./$(DEPDIR)/libcurl_la-pingpong.Plo \
+ ./$(DEPDIR)/libcurl_la-pop3.Plo \
+ ./$(DEPDIR)/libcurl_la-progress.Plo \
+ ./$(DEPDIR)/libcurl_la-psl.Plo ./$(DEPDIR)/libcurl_la-rand.Plo \
+ ./$(DEPDIR)/libcurl_la-rename.Plo \
+ ./$(DEPDIR)/libcurl_la-rtsp.Plo \
+ ./$(DEPDIR)/libcurl_la-select.Plo \
+ ./$(DEPDIR)/libcurl_la-sendf.Plo \
+ ./$(DEPDIR)/libcurl_la-setopt.Plo \
+ ./$(DEPDIR)/libcurl_la-sha256.Plo \
+ ./$(DEPDIR)/libcurl_la-share.Plo \
+ ./$(DEPDIR)/libcurl_la-slist.Plo \
+ ./$(DEPDIR)/libcurl_la-smb.Plo ./$(DEPDIR)/libcurl_la-smtp.Plo \
+ ./$(DEPDIR)/libcurl_la-socketpair.Plo \
+ ./$(DEPDIR)/libcurl_la-socks.Plo \
+ ./$(DEPDIR)/libcurl_la-socks_gssapi.Plo \
+ ./$(DEPDIR)/libcurl_la-socks_sspi.Plo \
+ ./$(DEPDIR)/libcurl_la-speedcheck.Plo \
+ ./$(DEPDIR)/libcurl_la-splay.Plo \
+ ./$(DEPDIR)/libcurl_la-strcase.Plo \
+ ./$(DEPDIR)/libcurl_la-strdup.Plo \
+ ./$(DEPDIR)/libcurl_la-strerror.Plo \
+ ./$(DEPDIR)/libcurl_la-strtok.Plo \
+ ./$(DEPDIR)/libcurl_la-strtoofft.Plo \
+ ./$(DEPDIR)/libcurl_la-system_win32.Plo \
+ ./$(DEPDIR)/libcurl_la-telnet.Plo \
+ ./$(DEPDIR)/libcurl_la-tftp.Plo \
+ ./$(DEPDIR)/libcurl_la-timediff.Plo \
+ ./$(DEPDIR)/libcurl_la-timeval.Plo \
+ ./$(DEPDIR)/libcurl_la-transfer.Plo \
+ ./$(DEPDIR)/libcurl_la-url.Plo \
+ ./$(DEPDIR)/libcurl_la-urlapi.Plo \
+ ./$(DEPDIR)/libcurl_la-version.Plo \
+ ./$(DEPDIR)/libcurl_la-version_win32.Plo \
+ ./$(DEPDIR)/libcurl_la-warnless.Plo \
+ ./$(DEPDIR)/libcurl_la-wildcard.Plo \
+ ./$(DEPDIR)/libcurl_la-ws.Plo \
+ ./$(DEPDIR)/libcurlu_la-altsvc.Plo \
+ ./$(DEPDIR)/libcurlu_la-amigaos.Plo \
+ ./$(DEPDIR)/libcurlu_la-asyn-ares.Plo \
+ ./$(DEPDIR)/libcurlu_la-asyn-thread.Plo \
+ ./$(DEPDIR)/libcurlu_la-base64.Plo \
+ ./$(DEPDIR)/libcurlu_la-bufref.Plo \
+ ./$(DEPDIR)/libcurlu_la-c-hyper.Plo \
+ ./$(DEPDIR)/libcurlu_la-cfilters.Plo \
+ ./$(DEPDIR)/libcurlu_la-conncache.Plo \
+ ./$(DEPDIR)/libcurlu_la-connect.Plo \
+ ./$(DEPDIR)/libcurlu_la-content_encoding.Plo \
+ ./$(DEPDIR)/libcurlu_la-cookie.Plo \
+ ./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo \
+ ./$(DEPDIR)/libcurlu_la-curl_des.Plo \
+ ./$(DEPDIR)/libcurlu_la-curl_endian.Plo \
+ ./$(DEPDIR)/libcurlu_la-curl_fnmatch.Plo \
+ ./$(DEPDIR)/libcurlu_la-curl_get_line.Plo \
+ ./$(DEPDIR)/libcurlu_la-curl_gethostname.Plo \
+ ./$(DEPDIR)/libcurlu_la-curl_gssapi.Plo \
+ ./$(DEPDIR)/libcurlu_la-curl_memrchr.Plo \
+ ./$(DEPDIR)/libcurlu_la-curl_multibyte.Plo \
+ ./$(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo \
+ ./$(DEPDIR)/libcurlu_la-curl_ntlm_wb.Plo \
+ ./$(DEPDIR)/libcurlu_la-curl_path.Plo \
+ ./$(DEPDIR)/libcurlu_la-curl_range.Plo \
+ ./$(DEPDIR)/libcurlu_la-curl_rtmp.Plo \
+ ./$(DEPDIR)/libcurlu_la-curl_sasl.Plo \
+ ./$(DEPDIR)/libcurlu_la-curl_sspi.Plo \
+ ./$(DEPDIR)/libcurlu_la-curl_threads.Plo \
+ ./$(DEPDIR)/libcurlu_la-dict.Plo \
+ ./$(DEPDIR)/libcurlu_la-doh.Plo \
+ ./$(DEPDIR)/libcurlu_la-dynbuf.Plo \
+ ./$(DEPDIR)/libcurlu_la-easy.Plo \
+ ./$(DEPDIR)/libcurlu_la-easygetopt.Plo \
+ ./$(DEPDIR)/libcurlu_la-easyoptions.Plo \
+ ./$(DEPDIR)/libcurlu_la-escape.Plo \
+ ./$(DEPDIR)/libcurlu_la-file.Plo \
+ ./$(DEPDIR)/libcurlu_la-fileinfo.Plo \
+ ./$(DEPDIR)/libcurlu_la-fopen.Plo \
+ ./$(DEPDIR)/libcurlu_la-formdata.Plo \
+ ./$(DEPDIR)/libcurlu_la-ftp.Plo \
+ ./$(DEPDIR)/libcurlu_la-ftplistparser.Plo \
+ ./$(DEPDIR)/libcurlu_la-getenv.Plo \
+ ./$(DEPDIR)/libcurlu_la-getinfo.Plo \
+ ./$(DEPDIR)/libcurlu_la-gopher.Plo \
+ ./$(DEPDIR)/libcurlu_la-h2h3.Plo \
+ ./$(DEPDIR)/libcurlu_la-hash.Plo \
+ ./$(DEPDIR)/libcurlu_la-headers.Plo \
+ ./$(DEPDIR)/libcurlu_la-hmac.Plo \
+ ./$(DEPDIR)/libcurlu_la-hostasyn.Plo \
+ ./$(DEPDIR)/libcurlu_la-hostip.Plo \
+ ./$(DEPDIR)/libcurlu_la-hostip4.Plo \
+ ./$(DEPDIR)/libcurlu_la-hostip6.Plo \
+ ./$(DEPDIR)/libcurlu_la-hostsyn.Plo \
+ ./$(DEPDIR)/libcurlu_la-hsts.Plo \
+ ./$(DEPDIR)/libcurlu_la-http.Plo \
+ ./$(DEPDIR)/libcurlu_la-http2.Plo \
+ ./$(DEPDIR)/libcurlu_la-http_aws_sigv4.Plo \
+ ./$(DEPDIR)/libcurlu_la-http_chunks.Plo \
+ ./$(DEPDIR)/libcurlu_la-http_digest.Plo \
+ ./$(DEPDIR)/libcurlu_la-http_negotiate.Plo \
+ ./$(DEPDIR)/libcurlu_la-http_ntlm.Plo \
+ ./$(DEPDIR)/libcurlu_la-http_proxy.Plo \
+ ./$(DEPDIR)/libcurlu_la-idn.Plo \
+ ./$(DEPDIR)/libcurlu_la-if2ip.Plo \
+ ./$(DEPDIR)/libcurlu_la-imap.Plo \
+ ./$(DEPDIR)/libcurlu_la-inet_ntop.Plo \
+ ./$(DEPDIR)/libcurlu_la-inet_pton.Plo \
+ ./$(DEPDIR)/libcurlu_la-krb5.Plo \
+ ./$(DEPDIR)/libcurlu_la-ldap.Plo \
+ ./$(DEPDIR)/libcurlu_la-llist.Plo \
+ ./$(DEPDIR)/libcurlu_la-md4.Plo \
+ ./$(DEPDIR)/libcurlu_la-md5.Plo \
+ ./$(DEPDIR)/libcurlu_la-memdebug.Plo \
+ ./$(DEPDIR)/libcurlu_la-mime.Plo \
+ ./$(DEPDIR)/libcurlu_la-mprintf.Plo \
+ ./$(DEPDIR)/libcurlu_la-mqtt.Plo \
+ ./$(DEPDIR)/libcurlu_la-multi.Plo \
+ ./$(DEPDIR)/libcurlu_la-netrc.Plo \
+ ./$(DEPDIR)/libcurlu_la-nonblock.Plo \
+ ./$(DEPDIR)/libcurlu_la-noproxy.Plo \
+ ./$(DEPDIR)/libcurlu_la-openldap.Plo \
+ ./$(DEPDIR)/libcurlu_la-parsedate.Plo \
+ ./$(DEPDIR)/libcurlu_la-pingpong.Plo \
+ ./$(DEPDIR)/libcurlu_la-pop3.Plo \
+ ./$(DEPDIR)/libcurlu_la-progress.Plo \
+ ./$(DEPDIR)/libcurlu_la-psl.Plo \
+ ./$(DEPDIR)/libcurlu_la-rand.Plo \
+ ./$(DEPDIR)/libcurlu_la-rename.Plo \
+ ./$(DEPDIR)/libcurlu_la-rtsp.Plo \
+ ./$(DEPDIR)/libcurlu_la-select.Plo \
+ ./$(DEPDIR)/libcurlu_la-sendf.Plo \
+ ./$(DEPDIR)/libcurlu_la-setopt.Plo \
+ ./$(DEPDIR)/libcurlu_la-sha256.Plo \
+ ./$(DEPDIR)/libcurlu_la-share.Plo \
+ ./$(DEPDIR)/libcurlu_la-slist.Plo \
+ ./$(DEPDIR)/libcurlu_la-smb.Plo \
+ ./$(DEPDIR)/libcurlu_la-smtp.Plo \
+ ./$(DEPDIR)/libcurlu_la-socketpair.Plo \
+ ./$(DEPDIR)/libcurlu_la-socks.Plo \
+ ./$(DEPDIR)/libcurlu_la-socks_gssapi.Plo \
+ ./$(DEPDIR)/libcurlu_la-socks_sspi.Plo \
+ ./$(DEPDIR)/libcurlu_la-speedcheck.Plo \
+ ./$(DEPDIR)/libcurlu_la-splay.Plo \
+ ./$(DEPDIR)/libcurlu_la-strcase.Plo \
+ ./$(DEPDIR)/libcurlu_la-strdup.Plo \
+ ./$(DEPDIR)/libcurlu_la-strerror.Plo \
+ ./$(DEPDIR)/libcurlu_la-strtok.Plo \
+ ./$(DEPDIR)/libcurlu_la-strtoofft.Plo \
+ ./$(DEPDIR)/libcurlu_la-system_win32.Plo \
+ ./$(DEPDIR)/libcurlu_la-telnet.Plo \
+ ./$(DEPDIR)/libcurlu_la-tftp.Plo \
+ ./$(DEPDIR)/libcurlu_la-timediff.Plo \
+ ./$(DEPDIR)/libcurlu_la-timeval.Plo \
+ ./$(DEPDIR)/libcurlu_la-transfer.Plo \
+ ./$(DEPDIR)/libcurlu_la-url.Plo \
+ ./$(DEPDIR)/libcurlu_la-urlapi.Plo \
+ ./$(DEPDIR)/libcurlu_la-version.Plo \
+ ./$(DEPDIR)/libcurlu_la-version_win32.Plo \
+ ./$(DEPDIR)/libcurlu_la-warnless.Plo \
+ ./$(DEPDIR)/libcurlu_la-wildcard.Plo \
+ ./$(DEPDIR)/libcurlu_la-ws.Plo \
+ vauth/$(DEPDIR)/libcurl_la-cleartext.Plo \
+ vauth/$(DEPDIR)/libcurl_la-cram.Plo \
+ vauth/$(DEPDIR)/libcurl_la-digest.Plo \
+ vauth/$(DEPDIR)/libcurl_la-digest_sspi.Plo \
+ vauth/$(DEPDIR)/libcurl_la-gsasl.Plo \
+ vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Plo \
+ vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Plo \
+ vauth/$(DEPDIR)/libcurl_la-ntlm.Plo \
+ vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Plo \
+ vauth/$(DEPDIR)/libcurl_la-oauth2.Plo \
+ vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Plo \
+ vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Plo \
+ vauth/$(DEPDIR)/libcurl_la-vauth.Plo \
+ vauth/$(DEPDIR)/libcurlu_la-cleartext.Plo \
+ vauth/$(DEPDIR)/libcurlu_la-cram.Plo \
+ vauth/$(DEPDIR)/libcurlu_la-digest.Plo \
+ vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Plo \
+ vauth/$(DEPDIR)/libcurlu_la-gsasl.Plo \
+ vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Plo \
+ vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Plo \
+ vauth/$(DEPDIR)/libcurlu_la-ntlm.Plo \
+ vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Plo \
+ vauth/$(DEPDIR)/libcurlu_la-oauth2.Plo \
+ vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Plo \
+ vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Plo \
+ vauth/$(DEPDIR)/libcurlu_la-vauth.Plo \
+ vquic/$(DEPDIR)/libcurl_la-msh3.Plo \
+ vquic/$(DEPDIR)/libcurl_la-ngtcp2.Plo \
+ vquic/$(DEPDIR)/libcurl_la-quiche.Plo \
+ vquic/$(DEPDIR)/libcurl_la-vquic.Plo \
+ vquic/$(DEPDIR)/libcurlu_la-msh3.Plo \
+ vquic/$(DEPDIR)/libcurlu_la-ngtcp2.Plo \
+ vquic/$(DEPDIR)/libcurlu_la-quiche.Plo \
+ vquic/$(DEPDIR)/libcurlu_la-vquic.Plo \
+ vssh/$(DEPDIR)/libcurl_la-libssh.Plo \
+ vssh/$(DEPDIR)/libcurl_la-libssh2.Plo \
+ vssh/$(DEPDIR)/libcurl_la-wolfssh.Plo \
+ vssh/$(DEPDIR)/libcurlu_la-libssh.Plo \
+ vssh/$(DEPDIR)/libcurlu_la-libssh2.Plo \
+ vssh/$(DEPDIR)/libcurlu_la-wolfssh.Plo \
+ vtls/$(DEPDIR)/libcurl_la-bearssl.Plo \
+ vtls/$(DEPDIR)/libcurl_la-gskit.Plo \
+ vtls/$(DEPDIR)/libcurl_la-gtls.Plo \
+ vtls/$(DEPDIR)/libcurl_la-hostcheck.Plo \
+ vtls/$(DEPDIR)/libcurl_la-keylog.Plo \
+ vtls/$(DEPDIR)/libcurl_la-mbedtls.Plo \
+ vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Plo \
+ vtls/$(DEPDIR)/libcurl_la-nss.Plo \
+ vtls/$(DEPDIR)/libcurl_la-openssl.Plo \
+ vtls/$(DEPDIR)/libcurl_la-rustls.Plo \
+ vtls/$(DEPDIR)/libcurl_la-schannel.Plo \
+ vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo \
+ vtls/$(DEPDIR)/libcurl_la-sectransp.Plo \
+ vtls/$(DEPDIR)/libcurl_la-vtls.Plo \
+ vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo \
+ vtls/$(DEPDIR)/libcurl_la-x509asn1.Plo \
+ vtls/$(DEPDIR)/libcurlu_la-bearssl.Plo \
+ vtls/$(DEPDIR)/libcurlu_la-gskit.Plo \
+ vtls/$(DEPDIR)/libcurlu_la-gtls.Plo \
+ vtls/$(DEPDIR)/libcurlu_la-hostcheck.Plo \
+ vtls/$(DEPDIR)/libcurlu_la-keylog.Plo \
+ vtls/$(DEPDIR)/libcurlu_la-mbedtls.Plo \
+ vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Plo \
+ vtls/$(DEPDIR)/libcurlu_la-nss.Plo \
+ vtls/$(DEPDIR)/libcurlu_la-openssl.Plo \
+ vtls/$(DEPDIR)/libcurlu_la-rustls.Plo \
+ vtls/$(DEPDIR)/libcurlu_la-schannel.Plo \
+ vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo \
+ vtls/$(DEPDIR)/libcurlu_la-sectransp.Plo \
+ vtls/$(DEPDIR)/libcurlu_la-vtls.Plo \
+ vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo \
+ vtls/$(DEPDIR)/libcurlu_la-x509asn1.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libcurl_la_SOURCES) $(libcurlu_la_SOURCES)
+DIST_SOURCES = $(am__libcurl_la_SOURCES_DIST) $(libcurlu_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) \
+ curl_config.h.in
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.inc \
+ $(srcdir)/Makefile.soname $(srcdir)/curl_config.h.in \
+ $(srcdir)/libcurl.plist.in $(srcdir)/libcurl.vers.in \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AR_FLAGS = @AR_FLAGS@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BLANK_AT_MAKETIME = @BLANK_AT_MAKETIME@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+
+# This might hold -Werror
+CFLAGS = @CFLAGS@ @CURL_CFLAG_EXTRAS@
+CFLAG_CURL_SYMBOL_HIDING = @CFLAG_CURL_SYMBOL_HIDING@
+CONFIGURE_OPTIONS = @CONFIGURE_OPTIONS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAG_CURL_STATICLIB = @CPPFLAG_CURL_STATICLIB@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CURLVERSION = @CURLVERSION@
+CURL_CA_BUNDLE = @CURL_CA_BUNDLE@
+CURL_CFLAG_EXTRAS = @CURL_CFLAG_EXTRAS@
+CURL_DISABLE_DICT = @CURL_DISABLE_DICT@
+CURL_DISABLE_FILE = @CURL_DISABLE_FILE@
+CURL_DISABLE_FTP = @CURL_DISABLE_FTP@
+CURL_DISABLE_GOPHER = @CURL_DISABLE_GOPHER@
+CURL_DISABLE_HTTP = @CURL_DISABLE_HTTP@
+CURL_DISABLE_IMAP = @CURL_DISABLE_IMAP@
+CURL_DISABLE_LDAP = @CURL_DISABLE_LDAP@
+CURL_DISABLE_LDAPS = @CURL_DISABLE_LDAPS@
+CURL_DISABLE_MQTT = @CURL_DISABLE_MQTT@
+CURL_DISABLE_POP3 = @CURL_DISABLE_POP3@
+CURL_DISABLE_PROXY = @CURL_DISABLE_PROXY@
+CURL_DISABLE_RTSP = @CURL_DISABLE_RTSP@
+CURL_DISABLE_SMB = @CURL_DISABLE_SMB@
+CURL_DISABLE_SMTP = @CURL_DISABLE_SMTP@
+CURL_DISABLE_TELNET = @CURL_DISABLE_TELNET@
+CURL_DISABLE_TFTP = @CURL_DISABLE_TFTP@
+CURL_LT_SHLIB_VERSIONED_FLAVOUR = @CURL_LT_SHLIB_VERSIONED_FLAVOUR@
+CURL_NETWORK_AND_TIME_LIBS = @CURL_NETWORK_AND_TIME_LIBS@
+CURL_NETWORK_LIBS = @CURL_NETWORK_LIBS@
+CURL_PLIST_VERSION = @CURL_PLIST_VERSION@
+CURL_WITH_MULTI_SSL = @CURL_WITH_MULTI_SSL@
+CYGPATH_W = @CYGPATH_W@
+DEFAULT_SSL_BACKEND = @DEFAULT_SSL_BACKEND@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ENABLE_SHARED = @ENABLE_SHARED@
+ENABLE_STATIC = @ENABLE_STATIC@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILECMD = @FILECMD@
+FISH_FUNCTIONS_DIR = @FISH_FUNCTIONS_DIR@
+GCOV = @GCOV@
+GREP = @GREP@
+HAVE_BROTLI = @HAVE_BROTLI@
+HAVE_GNUTLS_SRP = @HAVE_GNUTLS_SRP@
+HAVE_LDAP_SSL = @HAVE_LDAP_SSL@
+HAVE_LIBZ = @HAVE_LIBZ@
+HAVE_OPENSSL_SRP = @HAVE_OPENSSL_SRP@
+HAVE_PROTO_BSDSOCKET_H = @HAVE_PROTO_BSDSOCKET_H@
+HAVE_ZSTD = @HAVE_ZSTD@
+IDN_ENABLED = @IDN_ENABLED@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+IPV6_ENABLED = @IPV6_ENABLED@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBCURL_LIBS = @LIBCURL_LIBS@
+LIBCURL_NO_SHARED = @LIBCURL_NO_SHARED@
+LIBOBJS = @LIBOBJS@
+
+# Prevent LIBS from being used for all link targets
+LIBS = $(BLANK_AT_MAKETIME)
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MANOPT = @MANOPT@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NROFF = @NROFF@
+NSS_LIBS = @NSS_LIBS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PKGADD_NAME = @PKGADD_NAME@
+PKGADD_PKG = @PKGADD_PKG@
+PKGADD_VENDOR = @PKGADD_VENDOR@
+PKGCONFIG = @PKGCONFIG@
+RANDOM_FILE = @RANDOM_FILE@
+RANLIB = @RANLIB@
+RC = @RC@
+REQUIRE_LIB_DEPS = @REQUIRE_LIB_DEPS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SSL_BACKENDS = @SSL_BACKENDS@
+SSL_ENABLED = @SSL_ENABLED@
+SSL_LIBS = @SSL_LIBS@
+STRIP = @STRIP@
+SUPPORT_FEATURES = @SUPPORT_FEATURES@
+SUPPORT_PROTOCOLS = @SUPPORT_PROTOCOLS@
+TEST_NGHTTPX = @TEST_NGHTTPX@
+USE_ARES = @USE_ARES@
+USE_BEARSSL = @USE_BEARSSL@
+USE_GNUTLS = @USE_GNUTLS@
+USE_HYPER = @USE_HYPER@
+USE_LIBRTMP = @USE_LIBRTMP@
+USE_LIBSSH = @USE_LIBSSH@
+USE_LIBSSH2 = @USE_LIBSSH2@
+USE_MBEDTLS = @USE_MBEDTLS@
+USE_MSH3 = @USE_MSH3@
+USE_NGHTTP2 = @USE_NGHTTP2@
+USE_NGHTTP3 = @USE_NGHTTP3@
+USE_NGTCP2 = @USE_NGTCP2@
+USE_NGTCP2_CRYPTO_GNUTLS = @USE_NGTCP2_CRYPTO_GNUTLS@
+USE_NGTCP2_CRYPTO_OPENSSL = @USE_NGTCP2_CRYPTO_OPENSSL@
+USE_NGTCP2_CRYPTO_WOLFSSL = @USE_NGTCP2_CRYPTO_WOLFSSL@
+USE_NSS = @USE_NSS@
+USE_OPENLDAP = @USE_OPENLDAP@
+USE_QUICHE = @USE_QUICHE@
+USE_RUSTLS = @USE_RUSTLS@
+USE_SCHANNEL = @USE_SCHANNEL@
+USE_SECTRANSP = @USE_SECTRANSP@
+USE_UNIX_SOCKETS = @USE_UNIX_SOCKETS@
+USE_WIN32_CRYPTO = @USE_WIN32_CRYPTO@
+USE_WIN32_LARGE_FILES = @USE_WIN32_LARGE_FILES@
+USE_WIN32_SMALL_FILES = @USE_WIN32_SMALL_FILES@
+USE_WINDOWS_SSPI = @USE_WINDOWS_SSPI@
+USE_WOLFSSH = @USE_WOLFSSH@
+USE_WOLFSSL = @USE_WOLFSSL@
+VERSION = @VERSION@
+VERSIONNUM = @VERSIONNUM@
+ZLIB_LIBS = @ZLIB_LIBS@
+ZSH_FUNCTIONS_DIR = @ZSH_FUNCTIONS_DIR@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+libext = @libext@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+#***************************************************************************
+# _ _ ____ _
+# Project ___| | | | _ \| |
+# / __| | | | |_) | |
+# | (__| |_| | _ <| |___
+# \___|\___/|_| \_\_____|
+#
+# Copyright (C) 1998 - 2022, 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
+CMAKE_DIST = CMakeLists.txt curl_config.h.cmake
+EXTRA_DIST = Makefile.mk config-win32.h config-win32ce.h config-plan9.h \
+ config-riscos.h config-mac.h curl_config.h.in config-dos.h \
+ libcurl.plist libcurl.rc config-amigaos.h config-win32ce.h \
+ config-os400.h setup-os400.h $(CMAKE_DIST) setup-win32.h .checksrc \
+ Makefile.soname
+
+lib_LTLIBRARIES = libcurl.la
+@BUILD_UNITTESTS_FALSE@noinst_LTLIBRARIES =
+@BUILD_UNITTESTS_TRUE@noinst_LTLIBRARIES = libcurlu.la
+
+# 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
+# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "private" files
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/lib \
+ -I$(top_srcdir)/lib -DBUILDING_LIBCURL
+VERSIONCHANGE = 12
+VERSIONADD = 0
+VERSIONDEL = 8
+
+# libtool version:
+VERSIONINFO = -version-info $(VERSIONCHANGE):$(VERSIONADD):$(VERSIONDEL)
+AM_LDFLAGS =
+AM_CFLAGS =
+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/ntlm.h \
+ vauth/vauth.h
+
+LIB_VTLS_CFILES = \
+ vtls/bearssl.c \
+ vtls/gskit.c \
+ vtls/gtls.c \
+ vtls/hostcheck.c \
+ vtls/keylog.c \
+ vtls/mbedtls.c \
+ vtls/mbedtls_threadlock.c \
+ vtls/nss.c \
+ vtls/openssl.c \
+ vtls/rustls.c \
+ vtls/schannel.c \
+ vtls/schannel_verify.c \
+ vtls/sectransp.c \
+ vtls/vtls.c \
+ vtls/wolfssl.c \
+ vtls/x509asn1.c
+
+LIB_VTLS_HFILES = \
+ vtls/bearssl.h \
+ vtls/gskit.h \
+ vtls/gtls.h \
+ vtls/hostcheck.h \
+ vtls/keylog.h \
+ vtls/mbedtls.h \
+ vtls/mbedtls_threadlock.h \
+ vtls/nssg.h \
+ vtls/openssl.h \
+ vtls/rustls.h \
+ vtls/schannel.h \
+ vtls/sectransp.h \
+ vtls/vtls.h \
+ vtls/vtls_int.h \
+ vtls/wolfssl.h \
+ vtls/x509asn1.h
+
+LIB_VQUIC_CFILES = \
+ vquic/msh3.c \
+ vquic/ngtcp2.c \
+ vquic/quiche.c \
+ vquic/vquic.c
+
+LIB_VQUIC_HFILES = \
+ vquic/msh3.h \
+ vquic/ngtcp2.h \
+ vquic/quiche.h \
+ vquic/vquic.h
+
+LIB_VSSH_CFILES = \
+ vssh/libssh.c \
+ vssh/libssh2.c \
+ vssh/wolfssh.c
+
+LIB_VSSH_HFILES = \
+ vssh/ssh.h
+
+LIB_CFILES = \
+ altsvc.c \
+ amigaos.c \
+ asyn-ares.c \
+ asyn-thread.c \
+ base64.c \
+ bufref.c \
+ c-hyper.c \
+ cfilters.c \
+ conncache.c \
+ connect.c \
+ content_encoding.c \
+ cookie.c \
+ curl_addrinfo.c \
+ curl_des.c \
+ curl_endian.c \
+ curl_fnmatch.c \
+ curl_get_line.c \
+ curl_gethostname.c \
+ curl_gssapi.c \
+ curl_memrchr.c \
+ curl_multibyte.c \
+ curl_ntlm_core.c \
+ curl_ntlm_wb.c \
+ curl_path.c \
+ curl_range.c \
+ curl_rtmp.c \
+ curl_sasl.c \
+ curl_sspi.c \
+ curl_threads.c \
+ dict.c \
+ doh.c \
+ dynbuf.c \
+ easy.c \
+ easygetopt.c \
+ easyoptions.c \
+ escape.c \
+ file.c \
+ fileinfo.c \
+ fopen.c \
+ formdata.c \
+ ftp.c \
+ ftplistparser.c \
+ getenv.c \
+ getinfo.c \
+ gopher.c \
+ h2h3.c \
+ hash.c \
+ headers.c \
+ hmac.c \
+ hostasyn.c \
+ hostip.c \
+ hostip4.c \
+ hostip6.c \
+ hostsyn.c \
+ hsts.c \
+ http.c \
+ http2.c \
+ http_chunks.c \
+ http_digest.c \
+ http_negotiate.c \
+ http_ntlm.c \
+ http_proxy.c \
+ http_aws_sigv4.c \
+ idn.c \
+ if2ip.c \
+ imap.c \
+ inet_ntop.c \
+ inet_pton.c \
+ krb5.c \
+ ldap.c \
+ llist.c \
+ md4.c \
+ md5.c \
+ memdebug.c \
+ mime.c \
+ mprintf.c \
+ mqtt.c \
+ multi.c \
+ netrc.c \
+ nonblock.c \
+ noproxy.c \
+ openldap.c \
+ parsedate.c \
+ pingpong.c \
+ pop3.c \
+ progress.c \
+ psl.c \
+ rand.c \
+ rename.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 \
+ strerror.c \
+ strtok.c \
+ strtoofft.c \
+ system_win32.c \
+ telnet.c \
+ tftp.c \
+ timediff.c \
+ timeval.c \
+ transfer.c \
+ url.c \
+ urlapi.c \
+ version.c \
+ version_win32.c \
+ warnless.c \
+ wildcard.c \
+ ws.c
+
+LIB_HFILES = \
+ altsvc.h \
+ amigaos.h \
+ arpa_telnet.h \
+ asyn.h \
+ bufref.h \
+ c-hyper.h \
+ cfilters.h \
+ conncache.h \
+ connect.h \
+ content_encoding.h \
+ cookie.h \
+ curl_addrinfo.h \
+ curl_base64.h \
+ curl_ctype.h \
+ curl_des.h \
+ curl_endian.h \
+ curl_fnmatch.h \
+ curl_get_line.h \
+ curl_gethostname.h \
+ curl_gssapi.h \
+ curl_hmac.h \
+ curl_krb5.h \
+ curl_ldap.h \
+ curl_md4.h \
+ curl_md5.h \
+ curl_memory.h \
+ curl_memrchr.h \
+ curl_multibyte.h \
+ curl_ntlm_core.h \
+ curl_ntlm_wb.h \
+ curl_path.h \
+ curl_printf.h \
+ curl_range.h \
+ curl_rtmp.h \
+ curl_sasl.h \
+ curl_setup.h \
+ curl_setup_once.h \
+ curl_sha256.h \
+ curl_sspi.h \
+ curl_threads.h \
+ curlx.h \
+ dict.h \
+ doh.h \
+ dynbuf.h \
+ easy_lock.h \
+ easyif.h \
+ easyoptions.h \
+ escape.h \
+ file.h \
+ fileinfo.h \
+ fopen.h \
+ formdata.h \
+ functypes.h \
+ ftp.h \
+ ftplistparser.h \
+ getinfo.h \
+ gopher.h \
+ h2h3.h \
+ hash.h \
+ headers.h \
+ hostip.h \
+ hsts.h \
+ http.h \
+ http2.h \
+ http_chunks.h \
+ http_digest.h \
+ http_negotiate.h \
+ http_ntlm.h \
+ http_proxy.h \
+ http_aws_sigv4.h \
+ idn.h \
+ if2ip.h \
+ imap.h \
+ inet_ntop.h \
+ inet_pton.h \
+ llist.h \
+ memdebug.h \
+ mime.h \
+ mqtt.h \
+ multihandle.h \
+ multiif.h \
+ netrc.h \
+ nonblock.h \
+ noproxy.h \
+ parsedate.h \
+ pingpong.h \
+ pop3.h \
+ progress.h \
+ psl.h \
+ quic.h \
+ rand.h \
+ rename.h \
+ rtsp.h \
+ select.h \
+ sendf.h \
+ setopt.h \
+ setup-vms.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 \
+ strtok.h \
+ strtoofft.h \
+ system_win32.h \
+ telnet.h \
+ tftp.h \
+ timediff.h \
+ timeval.h \
+ transfer.h \
+ url.h \
+ urlapi-int.h \
+ urldata.h \
+ version_win32.h \
+ warnless.h \
+ wildcard.h \
+ ws.h
+
+LIB_RCFILES = libcurl.rc
+CSOURCES = $(LIB_CFILES) $(LIB_VAUTH_CFILES) $(LIB_VTLS_CFILES) \
+ $(LIB_VQUIC_CFILES) $(LIB_VSSH_CFILES)
+
+HHEADERS = $(LIB_HFILES) $(LIB_VAUTH_HFILES) $(LIB_VTLS_HFILES) \
+ $(LIB_VQUIC_HFILES) $(LIB_VSSH_HFILES)
+
+
+# Makefile.inc provides the CSOURCES and HHEADERS defines
+libcurl_la_SOURCES = $(CSOURCES) $(HHEADERS) $(am__append_7)
+libcurlu_la_SOURCES = $(CSOURCES) $(HHEADERS)
+libcurl_la_CPPFLAGS_EXTRA = $(am__append_6) $(am__append_8)
+libcurl_la_LDFLAGS_EXTRA = $(am__append_1) $(am__append_2) \
+ $(am__append_3) $(am__append_4) $(am__append_5)
+libcurl_la_CFLAGS_EXTRA = $(am__append_9)
+libcurl_la_CPPFLAGS = $(AM_CPPFLAGS) $(libcurl_la_CPPFLAGS_EXTRA)
+libcurl_la_LDFLAGS = $(AM_LDFLAGS) $(libcurl_la_LDFLAGS_EXTRA) $(LDFLAGS) $(LIBCURL_LIBS)
+libcurl_la_CFLAGS = $(AM_CFLAGS) $(libcurl_la_CFLAGS_EXTRA)
+libcurlu_la_CPPFLAGS = $(AM_CPPFLAGS) -DCURL_STATICLIB -DUNITTESTS
+libcurlu_la_LDFLAGS = $(AM_LDFLAGS) -static $(LIBCURL_LIBS)
+libcurlu_la_CFLAGS = $(AM_CFLAGS)
+CHECKSRC = $(CS_$(V))
+CS_0 = @echo " RUN " $@;
+CS_1 =
+CS_ = $(CS_0)
+
+# disable the tests that are mostly causing false positives
+TIDYFLAGS = -checks=-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-valist.Uninitialized,-clang-analyzer-core.NonNullParamChecker,-clang-analyzer-core.NullDereference -quiet
+TIDY := clang-tidy
+all: curl_config.h
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj .rc
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/Makefile.soname $(srcdir)/Makefile.inc $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign lib/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(srcdir)/Makefile.soname $(srcdir)/Makefile.inc $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+curl_config.h: stamp-h1
+ @test -f $@ || rm -f stamp-h1
+ @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1
+
+stamp-h1: $(srcdir)/curl_config.h.in $(top_builddir)/config.status
+ @rm -f stamp-h1
+ cd $(top_builddir) && $(SHELL) ./config.status lib/curl_config.h
+$(srcdir)/curl_config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ ($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+ rm -f stamp-h1
+ touch $@
+
+distclean-hdr:
+ -rm -f curl_config.h stamp-h1
+libcurl.vers: $(top_builddir)/config.status $(srcdir)/libcurl.vers.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+libcurl.plist: $(top_builddir)/config.status $(srcdir)/libcurl.plist.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+vauth/$(am__dirstamp):
+ @$(MKDIR_P) vauth
+ @: > vauth/$(am__dirstamp)
+vauth/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) vauth/$(DEPDIR)
+ @: > vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurl_la-cleartext.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurl_la-cram.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurl_la-digest.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurl_la-digest_sspi.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurl_la-gsasl.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurl_la-krb5_gssapi.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurl_la-krb5_sspi.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurl_la-ntlm.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurl_la-ntlm_sspi.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurl_la-oauth2.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurl_la-spnego_gssapi.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurl_la-spnego_sspi.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurl_la-vauth.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vtls/$(am__dirstamp):
+ @$(MKDIR_P) vtls
+ @: > vtls/$(am__dirstamp)
+vtls/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) vtls/$(DEPDIR)
+ @: > vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurl_la-bearssl.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurl_la-gskit.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurl_la-gtls.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurl_la-hostcheck.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurl_la-keylog.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurl_la-mbedtls.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurl_la-mbedtls_threadlock.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurl_la-nss.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurl_la-openssl.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurl_la-rustls.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurl_la-schannel.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurl_la-schannel_verify.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurl_la-sectransp.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurl_la-vtls.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurl_la-wolfssl.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurl_la-x509asn1.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vquic/$(am__dirstamp):
+ @$(MKDIR_P) vquic
+ @: > vquic/$(am__dirstamp)
+vquic/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) vquic/$(DEPDIR)
+ @: > vquic/$(DEPDIR)/$(am__dirstamp)
+vquic/libcurl_la-msh3.lo: vquic/$(am__dirstamp) \
+ vquic/$(DEPDIR)/$(am__dirstamp)
+vquic/libcurl_la-ngtcp2.lo: vquic/$(am__dirstamp) \
+ vquic/$(DEPDIR)/$(am__dirstamp)
+vquic/libcurl_la-quiche.lo: vquic/$(am__dirstamp) \
+ vquic/$(DEPDIR)/$(am__dirstamp)
+vquic/libcurl_la-vquic.lo: vquic/$(am__dirstamp) \
+ vquic/$(DEPDIR)/$(am__dirstamp)
+vssh/$(am__dirstamp):
+ @$(MKDIR_P) vssh
+ @: > vssh/$(am__dirstamp)
+vssh/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) vssh/$(DEPDIR)
+ @: > vssh/$(DEPDIR)/$(am__dirstamp)
+vssh/libcurl_la-libssh.lo: vssh/$(am__dirstamp) \
+ vssh/$(DEPDIR)/$(am__dirstamp)
+vssh/libcurl_la-libssh2.lo: vssh/$(am__dirstamp) \
+ vssh/$(DEPDIR)/$(am__dirstamp)
+vssh/libcurl_la-wolfssh.lo: vssh/$(am__dirstamp) \
+ vssh/$(DEPDIR)/$(am__dirstamp)
+
+libcurl.la: $(libcurl_la_OBJECTS) $(libcurl_la_DEPENDENCIES) $(EXTRA_libcurl_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libcurl_la_LINK) -rpath $(libdir) $(libcurl_la_OBJECTS) $(libcurl_la_LIBADD) $(LIBS)
+vauth/libcurlu_la-cleartext.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurlu_la-cram.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurlu_la-digest.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurlu_la-digest_sspi.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurlu_la-gsasl.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurlu_la-krb5_gssapi.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurlu_la-krb5_sspi.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurlu_la-ntlm.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurlu_la-ntlm_sspi.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurlu_la-oauth2.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurlu_la-spnego_gssapi.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurlu_la-spnego_sspi.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vauth/libcurlu_la-vauth.lo: vauth/$(am__dirstamp) \
+ vauth/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurlu_la-bearssl.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurlu_la-gskit.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurlu_la-gtls.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurlu_la-hostcheck.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurlu_la-keylog.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurlu_la-mbedtls.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurlu_la-mbedtls_threadlock.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurlu_la-nss.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurlu_la-openssl.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurlu_la-rustls.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurlu_la-schannel.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurlu_la-schannel_verify.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurlu_la-sectransp.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurlu_la-vtls.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurlu_la-wolfssl.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurlu_la-x509asn1.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vquic/libcurlu_la-msh3.lo: vquic/$(am__dirstamp) \
+ vquic/$(DEPDIR)/$(am__dirstamp)
+vquic/libcurlu_la-ngtcp2.lo: vquic/$(am__dirstamp) \
+ vquic/$(DEPDIR)/$(am__dirstamp)
+vquic/libcurlu_la-quiche.lo: vquic/$(am__dirstamp) \
+ vquic/$(DEPDIR)/$(am__dirstamp)
+vquic/libcurlu_la-vquic.lo: vquic/$(am__dirstamp) \
+ vquic/$(DEPDIR)/$(am__dirstamp)
+vssh/libcurlu_la-libssh.lo: vssh/$(am__dirstamp) \
+ vssh/$(DEPDIR)/$(am__dirstamp)
+vssh/libcurlu_la-libssh2.lo: vssh/$(am__dirstamp) \
+ vssh/$(DEPDIR)/$(am__dirstamp)
+vssh/libcurlu_la-wolfssh.lo: vssh/$(am__dirstamp) \
+ vssh/$(DEPDIR)/$(am__dirstamp)
+
+libcurlu.la: $(libcurlu_la_OBJECTS) $(libcurlu_la_DEPENDENCIES) $(EXTRA_libcurlu_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libcurlu_la_LINK) $(am_libcurlu_la_rpath) $(libcurlu_la_OBJECTS) $(libcurlu_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+ -rm -f vauth/*.$(OBJEXT)
+ -rm -f vauth/*.lo
+ -rm -f vquic/*.$(OBJEXT)
+ -rm -f vquic/*.lo
+ -rm -f vssh/*.$(OBJEXT)
+ -rm -f vssh/*.lo
+ -rm -f vtls/*.$(OBJEXT)
+ -rm -f vtls/*.lo
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-altsvc.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-amigaos.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-asyn-ares.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-asyn-thread.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-base64.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-bufref.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-c-hyper.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cfilters.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-conncache.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-connect.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-content_encoding.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cookie.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_des.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_endian.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_fnmatch.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_get_line.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_gethostname.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_gssapi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_memrchr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_multibyte.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_ntlm_core.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_ntlm_wb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_path.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_range.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_rtmp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_sasl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_sspi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_threads.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-dict.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-doh.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-dynbuf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-easy.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-easygetopt.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-easyoptions.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-escape.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-file.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-fileinfo.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-fopen.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-formdata.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-ftp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-ftplistparser.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-getenv.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-getinfo.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-gopher.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-h2h3.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hash.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-headers.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hmac.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostasyn.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostip.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostip4.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostip6.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostsyn.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hsts.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http2.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_aws_sigv4.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_chunks.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_digest.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_negotiate.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_ntlm.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_proxy.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-idn.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-if2ip.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-imap.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-inet_ntop.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-inet_pton.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-krb5.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-ldap.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-llist.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-md4.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-md5.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-memdebug.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-mime.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-mprintf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-mqtt.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-multi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-netrc.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-nonblock.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-noproxy.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-openldap.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-parsedate.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-pingpong.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-pop3.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-progress.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-psl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-rand.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-rename.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-rtsp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-select.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-sendf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-setopt.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-sha256.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-share.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-slist.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-smb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-smtp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-socketpair.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-socks.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-socks_gssapi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-socks_sspi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-speedcheck.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-splay.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strcase.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strdup.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strerror.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strtok.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strtoofft.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-system_win32.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-telnet.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-tftp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-timediff.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-timeval.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-transfer.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-url.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-urlapi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-version.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-version_win32.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-warnless.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-wildcard.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-ws.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-altsvc.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-amigaos.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-asyn-ares.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-asyn-thread.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-base64.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-bufref.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-c-hyper.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cfilters.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-conncache.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-connect.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-content_encoding.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cookie.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_des.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_endian.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_fnmatch.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_get_line.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_gethostname.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_gssapi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_memrchr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_multibyte.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_ntlm_wb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_path.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_range.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_rtmp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_sasl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_sspi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_threads.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-dict.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-doh.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-dynbuf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-easy.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-easygetopt.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-easyoptions.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-escape.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-file.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-fileinfo.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-fopen.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-formdata.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-ftp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-ftplistparser.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-getenv.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-getinfo.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-gopher.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-h2h3.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hash.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-headers.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hmac.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostasyn.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostip.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostip4.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostip6.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostsyn.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hsts.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http2.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_aws_sigv4.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_chunks.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_digest.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_negotiate.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_ntlm.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_proxy.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-idn.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-if2ip.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-imap.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-inet_ntop.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-inet_pton.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-krb5.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-ldap.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-llist.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-md4.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-md5.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-memdebug.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-mime.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-mprintf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-mqtt.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-multi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-netrc.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-nonblock.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-noproxy.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-openldap.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-parsedate.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-pingpong.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-pop3.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-progress.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-psl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-rand.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-rename.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-rtsp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-select.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-sendf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-setopt.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-sha256.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-share.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-slist.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-smb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-smtp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-socketpair.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-socks.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-socks_gssapi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-socks_sspi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-speedcheck.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-splay.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strcase.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strdup.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strerror.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strtok.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strtoofft.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-system_win32.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-telnet.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-tftp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-timediff.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-timeval.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-transfer.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-url.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-urlapi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-version.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-version_win32.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-warnless.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-wildcard.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-ws.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-cleartext.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-cram.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-digest.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-digest_sspi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-gsasl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-ntlm.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-oauth2.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-vauth.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-cleartext.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-cram.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-digest.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-gsasl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-ntlm.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-oauth2.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurlu_la-vauth.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-msh3.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-ngtcp2.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-quiche.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurl_la-vquic.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-msh3.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-ngtcp2.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-quiche.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vquic/$(DEPDIR)/libcurlu_la-vquic.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vssh/$(DEPDIR)/libcurl_la-libssh.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vssh/$(DEPDIR)/libcurl_la-libssh2.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vssh/$(DEPDIR)/libcurl_la-wolfssh.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vssh/$(DEPDIR)/libcurlu_la-libssh.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vssh/$(DEPDIR)/libcurlu_la-libssh2.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vssh/$(DEPDIR)/libcurlu_la-wolfssh.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-bearssl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-gskit.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-gtls.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-hostcheck.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-keylog.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-mbedtls.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-nss.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-openssl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-rustls.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-schannel.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-sectransp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-vtls.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-x509asn1.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-bearssl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-gskit.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-gtls.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-hostcheck.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-keylog.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-mbedtls.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-nss.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-openssl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-rustls.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-schannel.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-sectransp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-vtls.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-x509asn1.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+libcurl_la-altsvc.lo: altsvc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-altsvc.lo -MD -MP -MF $(DEPDIR)/libcurl_la-altsvc.Tpo -c -o libcurl_la-altsvc.lo `test -f 'altsvc.c' || echo '$(srcdir)/'`altsvc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-altsvc.Tpo $(DEPDIR)/libcurl_la-altsvc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='altsvc.c' object='libcurl_la-altsvc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-altsvc.lo `test -f 'altsvc.c' || echo '$(srcdir)/'`altsvc.c
+
+libcurl_la-amigaos.lo: amigaos.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-amigaos.lo -MD -MP -MF $(DEPDIR)/libcurl_la-amigaos.Tpo -c -o libcurl_la-amigaos.lo `test -f 'amigaos.c' || echo '$(srcdir)/'`amigaos.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-amigaos.Tpo $(DEPDIR)/libcurl_la-amigaos.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='amigaos.c' object='libcurl_la-amigaos.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-amigaos.lo `test -f 'amigaos.c' || echo '$(srcdir)/'`amigaos.c
+
+libcurl_la-asyn-ares.lo: asyn-ares.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-asyn-ares.lo -MD -MP -MF $(DEPDIR)/libcurl_la-asyn-ares.Tpo -c -o libcurl_la-asyn-ares.lo `test -f 'asyn-ares.c' || echo '$(srcdir)/'`asyn-ares.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-asyn-ares.Tpo $(DEPDIR)/libcurl_la-asyn-ares.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asyn-ares.c' object='libcurl_la-asyn-ares.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-asyn-ares.lo `test -f 'asyn-ares.c' || echo '$(srcdir)/'`asyn-ares.c
+
+libcurl_la-asyn-thread.lo: asyn-thread.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-asyn-thread.lo -MD -MP -MF $(DEPDIR)/libcurl_la-asyn-thread.Tpo -c -o libcurl_la-asyn-thread.lo `test -f 'asyn-thread.c' || echo '$(srcdir)/'`asyn-thread.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-asyn-thread.Tpo $(DEPDIR)/libcurl_la-asyn-thread.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asyn-thread.c' object='libcurl_la-asyn-thread.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-asyn-thread.lo `test -f 'asyn-thread.c' || echo '$(srcdir)/'`asyn-thread.c
+
+libcurl_la-base64.lo: base64.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-base64.lo -MD -MP -MF $(DEPDIR)/libcurl_la-base64.Tpo -c -o libcurl_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-base64.Tpo $(DEPDIR)/libcurl_la-base64.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='base64.c' object='libcurl_la-base64.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
+
+libcurl_la-bufref.lo: bufref.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-bufref.lo -MD -MP -MF $(DEPDIR)/libcurl_la-bufref.Tpo -c -o libcurl_la-bufref.lo `test -f 'bufref.c' || echo '$(srcdir)/'`bufref.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-bufref.Tpo $(DEPDIR)/libcurl_la-bufref.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bufref.c' object='libcurl_la-bufref.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-bufref.lo `test -f 'bufref.c' || echo '$(srcdir)/'`bufref.c
+
+libcurl_la-c-hyper.lo: c-hyper.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-c-hyper.lo -MD -MP -MF $(DEPDIR)/libcurl_la-c-hyper.Tpo -c -o libcurl_la-c-hyper.lo `test -f 'c-hyper.c' || echo '$(srcdir)/'`c-hyper.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-c-hyper.Tpo $(DEPDIR)/libcurl_la-c-hyper.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='c-hyper.c' object='libcurl_la-c-hyper.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-c-hyper.lo `test -f 'c-hyper.c' || echo '$(srcdir)/'`c-hyper.c
+
+libcurl_la-cfilters.lo: cfilters.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-cfilters.lo -MD -MP -MF $(DEPDIR)/libcurl_la-cfilters.Tpo -c -o libcurl_la-cfilters.lo `test -f 'cfilters.c' || echo '$(srcdir)/'`cfilters.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-cfilters.Tpo $(DEPDIR)/libcurl_la-cfilters.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cfilters.c' object='libcurl_la-cfilters.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-cfilters.lo `test -f 'cfilters.c' || echo '$(srcdir)/'`cfilters.c
+
+libcurl_la-conncache.lo: conncache.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-conncache.lo -MD -MP -MF $(DEPDIR)/libcurl_la-conncache.Tpo -c -o libcurl_la-conncache.lo `test -f 'conncache.c' || echo '$(srcdir)/'`conncache.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-conncache.Tpo $(DEPDIR)/libcurl_la-conncache.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conncache.c' object='libcurl_la-conncache.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-conncache.lo `test -f 'conncache.c' || echo '$(srcdir)/'`conncache.c
+
+libcurl_la-connect.lo: connect.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-connect.lo -MD -MP -MF $(DEPDIR)/libcurl_la-connect.Tpo -c -o libcurl_la-connect.lo `test -f 'connect.c' || echo '$(srcdir)/'`connect.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-connect.Tpo $(DEPDIR)/libcurl_la-connect.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='connect.c' object='libcurl_la-connect.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-connect.lo `test -f 'connect.c' || echo '$(srcdir)/'`connect.c
+
+libcurl_la-content_encoding.lo: content_encoding.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-content_encoding.lo -MD -MP -MF $(DEPDIR)/libcurl_la-content_encoding.Tpo -c -o libcurl_la-content_encoding.lo `test -f 'content_encoding.c' || echo '$(srcdir)/'`content_encoding.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-content_encoding.Tpo $(DEPDIR)/libcurl_la-content_encoding.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='content_encoding.c' object='libcurl_la-content_encoding.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-content_encoding.lo `test -f 'content_encoding.c' || echo '$(srcdir)/'`content_encoding.c
+
+libcurl_la-cookie.lo: cookie.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-cookie.lo -MD -MP -MF $(DEPDIR)/libcurl_la-cookie.Tpo -c -o libcurl_la-cookie.lo `test -f 'cookie.c' || echo '$(srcdir)/'`cookie.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-cookie.Tpo $(DEPDIR)/libcurl_la-cookie.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cookie.c' object='libcurl_la-cookie.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-cookie.lo `test -f 'cookie.c' || echo '$(srcdir)/'`cookie.c
+
+libcurl_la-curl_addrinfo.lo: curl_addrinfo.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_addrinfo.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_addrinfo.Tpo -c -o libcurl_la-curl_addrinfo.lo `test -f 'curl_addrinfo.c' || echo '$(srcdir)/'`curl_addrinfo.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_addrinfo.Tpo $(DEPDIR)/libcurl_la-curl_addrinfo.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_addrinfo.c' object='libcurl_la-curl_addrinfo.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_addrinfo.lo `test -f 'curl_addrinfo.c' || echo '$(srcdir)/'`curl_addrinfo.c
+
+libcurl_la-curl_des.lo: curl_des.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_des.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_des.Tpo -c -o libcurl_la-curl_des.lo `test -f 'curl_des.c' || echo '$(srcdir)/'`curl_des.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_des.Tpo $(DEPDIR)/libcurl_la-curl_des.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_des.c' object='libcurl_la-curl_des.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_des.lo `test -f 'curl_des.c' || echo '$(srcdir)/'`curl_des.c
+
+libcurl_la-curl_endian.lo: curl_endian.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_endian.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_endian.Tpo -c -o libcurl_la-curl_endian.lo `test -f 'curl_endian.c' || echo '$(srcdir)/'`curl_endian.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_endian.Tpo $(DEPDIR)/libcurl_la-curl_endian.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_endian.c' object='libcurl_la-curl_endian.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_endian.lo `test -f 'curl_endian.c' || echo '$(srcdir)/'`curl_endian.c
+
+libcurl_la-curl_fnmatch.lo: curl_fnmatch.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_fnmatch.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_fnmatch.Tpo -c -o libcurl_la-curl_fnmatch.lo `test -f 'curl_fnmatch.c' || echo '$(srcdir)/'`curl_fnmatch.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_fnmatch.Tpo $(DEPDIR)/libcurl_la-curl_fnmatch.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_fnmatch.c' object='libcurl_la-curl_fnmatch.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_fnmatch.lo `test -f 'curl_fnmatch.c' || echo '$(srcdir)/'`curl_fnmatch.c
+
+libcurl_la-curl_get_line.lo: curl_get_line.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_get_line.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_get_line.Tpo -c -o libcurl_la-curl_get_line.lo `test -f 'curl_get_line.c' || echo '$(srcdir)/'`curl_get_line.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_get_line.Tpo $(DEPDIR)/libcurl_la-curl_get_line.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_get_line.c' object='libcurl_la-curl_get_line.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_get_line.lo `test -f 'curl_get_line.c' || echo '$(srcdir)/'`curl_get_line.c
+
+libcurl_la-curl_gethostname.lo: curl_gethostname.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_gethostname.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_gethostname.Tpo -c -o libcurl_la-curl_gethostname.lo `test -f 'curl_gethostname.c' || echo '$(srcdir)/'`curl_gethostname.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_gethostname.Tpo $(DEPDIR)/libcurl_la-curl_gethostname.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_gethostname.c' object='libcurl_la-curl_gethostname.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_gethostname.lo `test -f 'curl_gethostname.c' || echo '$(srcdir)/'`curl_gethostname.c
+
+libcurl_la-curl_gssapi.lo: curl_gssapi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_gssapi.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_gssapi.Tpo -c -o libcurl_la-curl_gssapi.lo `test -f 'curl_gssapi.c' || echo '$(srcdir)/'`curl_gssapi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_gssapi.Tpo $(DEPDIR)/libcurl_la-curl_gssapi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_gssapi.c' object='libcurl_la-curl_gssapi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_gssapi.lo `test -f 'curl_gssapi.c' || echo '$(srcdir)/'`curl_gssapi.c
+
+libcurl_la-curl_memrchr.lo: curl_memrchr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_memrchr.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_memrchr.Tpo -c -o libcurl_la-curl_memrchr.lo `test -f 'curl_memrchr.c' || echo '$(srcdir)/'`curl_memrchr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_memrchr.Tpo $(DEPDIR)/libcurl_la-curl_memrchr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_memrchr.c' object='libcurl_la-curl_memrchr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_memrchr.lo `test -f 'curl_memrchr.c' || echo '$(srcdir)/'`curl_memrchr.c
+
+libcurl_la-curl_multibyte.lo: curl_multibyte.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_multibyte.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_multibyte.Tpo -c -o libcurl_la-curl_multibyte.lo `test -f 'curl_multibyte.c' || echo '$(srcdir)/'`curl_multibyte.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_multibyte.Tpo $(DEPDIR)/libcurl_la-curl_multibyte.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_multibyte.c' object='libcurl_la-curl_multibyte.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_multibyte.lo `test -f 'curl_multibyte.c' || echo '$(srcdir)/'`curl_multibyte.c
+
+libcurl_la-curl_ntlm_core.lo: curl_ntlm_core.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_ntlm_core.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_ntlm_core.Tpo -c -o libcurl_la-curl_ntlm_core.lo `test -f 'curl_ntlm_core.c' || echo '$(srcdir)/'`curl_ntlm_core.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_ntlm_core.Tpo $(DEPDIR)/libcurl_la-curl_ntlm_core.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_ntlm_core.c' object='libcurl_la-curl_ntlm_core.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_ntlm_core.lo `test -f 'curl_ntlm_core.c' || echo '$(srcdir)/'`curl_ntlm_core.c
+
+libcurl_la-curl_ntlm_wb.lo: curl_ntlm_wb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_ntlm_wb.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_ntlm_wb.Tpo -c -o libcurl_la-curl_ntlm_wb.lo `test -f 'curl_ntlm_wb.c' || echo '$(srcdir)/'`curl_ntlm_wb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_ntlm_wb.Tpo $(DEPDIR)/libcurl_la-curl_ntlm_wb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_ntlm_wb.c' object='libcurl_la-curl_ntlm_wb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_ntlm_wb.lo `test -f 'curl_ntlm_wb.c' || echo '$(srcdir)/'`curl_ntlm_wb.c
+
+libcurl_la-curl_path.lo: curl_path.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_path.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_path.Tpo -c -o libcurl_la-curl_path.lo `test -f 'curl_path.c' || echo '$(srcdir)/'`curl_path.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_path.Tpo $(DEPDIR)/libcurl_la-curl_path.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_path.c' object='libcurl_la-curl_path.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_path.lo `test -f 'curl_path.c' || echo '$(srcdir)/'`curl_path.c
+
+libcurl_la-curl_range.lo: curl_range.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_range.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_range.Tpo -c -o libcurl_la-curl_range.lo `test -f 'curl_range.c' || echo '$(srcdir)/'`curl_range.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_range.Tpo $(DEPDIR)/libcurl_la-curl_range.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_range.c' object='libcurl_la-curl_range.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_range.lo `test -f 'curl_range.c' || echo '$(srcdir)/'`curl_range.c
+
+libcurl_la-curl_rtmp.lo: curl_rtmp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_rtmp.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_rtmp.Tpo -c -o libcurl_la-curl_rtmp.lo `test -f 'curl_rtmp.c' || echo '$(srcdir)/'`curl_rtmp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_rtmp.Tpo $(DEPDIR)/libcurl_la-curl_rtmp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_rtmp.c' object='libcurl_la-curl_rtmp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_rtmp.lo `test -f 'curl_rtmp.c' || echo '$(srcdir)/'`curl_rtmp.c
+
+libcurl_la-curl_sasl.lo: curl_sasl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_sasl.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_sasl.Tpo -c -o libcurl_la-curl_sasl.lo `test -f 'curl_sasl.c' || echo '$(srcdir)/'`curl_sasl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_sasl.Tpo $(DEPDIR)/libcurl_la-curl_sasl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_sasl.c' object='libcurl_la-curl_sasl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_sasl.lo `test -f 'curl_sasl.c' || echo '$(srcdir)/'`curl_sasl.c
+
+libcurl_la-curl_sspi.lo: curl_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_sspi.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_sspi.Tpo -c -o libcurl_la-curl_sspi.lo `test -f 'curl_sspi.c' || echo '$(srcdir)/'`curl_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_sspi.Tpo $(DEPDIR)/libcurl_la-curl_sspi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_sspi.c' object='libcurl_la-curl_sspi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_sspi.lo `test -f 'curl_sspi.c' || echo '$(srcdir)/'`curl_sspi.c
+
+libcurl_la-curl_threads.lo: curl_threads.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_threads.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_threads.Tpo -c -o libcurl_la-curl_threads.lo `test -f 'curl_threads.c' || echo '$(srcdir)/'`curl_threads.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_threads.Tpo $(DEPDIR)/libcurl_la-curl_threads.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_threads.c' object='libcurl_la-curl_threads.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_threads.lo `test -f 'curl_threads.c' || echo '$(srcdir)/'`curl_threads.c
+
+libcurl_la-dict.lo: dict.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-dict.lo -MD -MP -MF $(DEPDIR)/libcurl_la-dict.Tpo -c -o libcurl_la-dict.lo `test -f 'dict.c' || echo '$(srcdir)/'`dict.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-dict.Tpo $(DEPDIR)/libcurl_la-dict.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dict.c' object='libcurl_la-dict.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-dict.lo `test -f 'dict.c' || echo '$(srcdir)/'`dict.c
+
+libcurl_la-doh.lo: doh.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-doh.lo -MD -MP -MF $(DEPDIR)/libcurl_la-doh.Tpo -c -o libcurl_la-doh.lo `test -f 'doh.c' || echo '$(srcdir)/'`doh.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-doh.Tpo $(DEPDIR)/libcurl_la-doh.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='doh.c' object='libcurl_la-doh.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-doh.lo `test -f 'doh.c' || echo '$(srcdir)/'`doh.c
+
+libcurl_la-dynbuf.lo: dynbuf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-dynbuf.lo -MD -MP -MF $(DEPDIR)/libcurl_la-dynbuf.Tpo -c -o libcurl_la-dynbuf.lo `test -f 'dynbuf.c' || echo '$(srcdir)/'`dynbuf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-dynbuf.Tpo $(DEPDIR)/libcurl_la-dynbuf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dynbuf.c' object='libcurl_la-dynbuf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-dynbuf.lo `test -f 'dynbuf.c' || echo '$(srcdir)/'`dynbuf.c
+
+libcurl_la-easy.lo: easy.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-easy.lo -MD -MP -MF $(DEPDIR)/libcurl_la-easy.Tpo -c -o libcurl_la-easy.lo `test -f 'easy.c' || echo '$(srcdir)/'`easy.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-easy.Tpo $(DEPDIR)/libcurl_la-easy.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='easy.c' object='libcurl_la-easy.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-easy.lo `test -f 'easy.c' || echo '$(srcdir)/'`easy.c
+
+libcurl_la-easygetopt.lo: easygetopt.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-easygetopt.lo -MD -MP -MF $(DEPDIR)/libcurl_la-easygetopt.Tpo -c -o libcurl_la-easygetopt.lo `test -f 'easygetopt.c' || echo '$(srcdir)/'`easygetopt.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-easygetopt.Tpo $(DEPDIR)/libcurl_la-easygetopt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='easygetopt.c' object='libcurl_la-easygetopt.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-easygetopt.lo `test -f 'easygetopt.c' || echo '$(srcdir)/'`easygetopt.c
+
+libcurl_la-easyoptions.lo: easyoptions.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-easyoptions.lo -MD -MP -MF $(DEPDIR)/libcurl_la-easyoptions.Tpo -c -o libcurl_la-easyoptions.lo `test -f 'easyoptions.c' || echo '$(srcdir)/'`easyoptions.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-easyoptions.Tpo $(DEPDIR)/libcurl_la-easyoptions.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='easyoptions.c' object='libcurl_la-easyoptions.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-easyoptions.lo `test -f 'easyoptions.c' || echo '$(srcdir)/'`easyoptions.c
+
+libcurl_la-escape.lo: escape.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-escape.lo -MD -MP -MF $(DEPDIR)/libcurl_la-escape.Tpo -c -o libcurl_la-escape.lo `test -f 'escape.c' || echo '$(srcdir)/'`escape.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-escape.Tpo $(DEPDIR)/libcurl_la-escape.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='escape.c' object='libcurl_la-escape.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-escape.lo `test -f 'escape.c' || echo '$(srcdir)/'`escape.c
+
+libcurl_la-file.lo: file.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-file.lo -MD -MP -MF $(DEPDIR)/libcurl_la-file.Tpo -c -o libcurl_la-file.lo `test -f 'file.c' || echo '$(srcdir)/'`file.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-file.Tpo $(DEPDIR)/libcurl_la-file.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file.c' object='libcurl_la-file.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-file.lo `test -f 'file.c' || echo '$(srcdir)/'`file.c
+
+libcurl_la-fileinfo.lo: fileinfo.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-fileinfo.lo -MD -MP -MF $(DEPDIR)/libcurl_la-fileinfo.Tpo -c -o libcurl_la-fileinfo.lo `test -f 'fileinfo.c' || echo '$(srcdir)/'`fileinfo.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-fileinfo.Tpo $(DEPDIR)/libcurl_la-fileinfo.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fileinfo.c' object='libcurl_la-fileinfo.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-fileinfo.lo `test -f 'fileinfo.c' || echo '$(srcdir)/'`fileinfo.c
+
+libcurl_la-fopen.lo: fopen.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-fopen.lo -MD -MP -MF $(DEPDIR)/libcurl_la-fopen.Tpo -c -o libcurl_la-fopen.lo `test -f 'fopen.c' || echo '$(srcdir)/'`fopen.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-fopen.Tpo $(DEPDIR)/libcurl_la-fopen.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fopen.c' object='libcurl_la-fopen.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-fopen.lo `test -f 'fopen.c' || echo '$(srcdir)/'`fopen.c
+
+libcurl_la-formdata.lo: formdata.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-formdata.lo -MD -MP -MF $(DEPDIR)/libcurl_la-formdata.Tpo -c -o libcurl_la-formdata.lo `test -f 'formdata.c' || echo '$(srcdir)/'`formdata.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-formdata.Tpo $(DEPDIR)/libcurl_la-formdata.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='formdata.c' object='libcurl_la-formdata.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-formdata.lo `test -f 'formdata.c' || echo '$(srcdir)/'`formdata.c
+
+libcurl_la-ftp.lo: ftp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-ftp.lo -MD -MP -MF $(DEPDIR)/libcurl_la-ftp.Tpo -c -o libcurl_la-ftp.lo `test -f 'ftp.c' || echo '$(srcdir)/'`ftp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-ftp.Tpo $(DEPDIR)/libcurl_la-ftp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ftp.c' object='libcurl_la-ftp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-ftp.lo `test -f 'ftp.c' || echo '$(srcdir)/'`ftp.c
+
+libcurl_la-ftplistparser.lo: ftplistparser.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-ftplistparser.lo -MD -MP -MF $(DEPDIR)/libcurl_la-ftplistparser.Tpo -c -o libcurl_la-ftplistparser.lo `test -f 'ftplistparser.c' || echo '$(srcdir)/'`ftplistparser.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-ftplistparser.Tpo $(DEPDIR)/libcurl_la-ftplistparser.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ftplistparser.c' object='libcurl_la-ftplistparser.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-ftplistparser.lo `test -f 'ftplistparser.c' || echo '$(srcdir)/'`ftplistparser.c
+
+libcurl_la-getenv.lo: getenv.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-getenv.lo -MD -MP -MF $(DEPDIR)/libcurl_la-getenv.Tpo -c -o libcurl_la-getenv.lo `test -f 'getenv.c' || echo '$(srcdir)/'`getenv.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-getenv.Tpo $(DEPDIR)/libcurl_la-getenv.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getenv.c' object='libcurl_la-getenv.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-getenv.lo `test -f 'getenv.c' || echo '$(srcdir)/'`getenv.c
+
+libcurl_la-getinfo.lo: getinfo.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-getinfo.lo -MD -MP -MF $(DEPDIR)/libcurl_la-getinfo.Tpo -c -o libcurl_la-getinfo.lo `test -f 'getinfo.c' || echo '$(srcdir)/'`getinfo.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-getinfo.Tpo $(DEPDIR)/libcurl_la-getinfo.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getinfo.c' object='libcurl_la-getinfo.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-getinfo.lo `test -f 'getinfo.c' || echo '$(srcdir)/'`getinfo.c
+
+libcurl_la-gopher.lo: gopher.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-gopher.lo -MD -MP -MF $(DEPDIR)/libcurl_la-gopher.Tpo -c -o libcurl_la-gopher.lo `test -f 'gopher.c' || echo '$(srcdir)/'`gopher.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-gopher.Tpo $(DEPDIR)/libcurl_la-gopher.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gopher.c' object='libcurl_la-gopher.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-gopher.lo `test -f 'gopher.c' || echo '$(srcdir)/'`gopher.c
+
+libcurl_la-h2h3.lo: h2h3.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-h2h3.lo -MD -MP -MF $(DEPDIR)/libcurl_la-h2h3.Tpo -c -o libcurl_la-h2h3.lo `test -f 'h2h3.c' || echo '$(srcdir)/'`h2h3.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-h2h3.Tpo $(DEPDIR)/libcurl_la-h2h3.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='h2h3.c' object='libcurl_la-h2h3.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-h2h3.lo `test -f 'h2h3.c' || echo '$(srcdir)/'`h2h3.c
+
+libcurl_la-hash.lo: hash.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hash.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hash.Tpo -c -o libcurl_la-hash.lo `test -f 'hash.c' || echo '$(srcdir)/'`hash.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hash.Tpo $(DEPDIR)/libcurl_la-hash.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hash.c' object='libcurl_la-hash.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hash.lo `test -f 'hash.c' || echo '$(srcdir)/'`hash.c
+
+libcurl_la-headers.lo: headers.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-headers.lo -MD -MP -MF $(DEPDIR)/libcurl_la-headers.Tpo -c -o libcurl_la-headers.lo `test -f 'headers.c' || echo '$(srcdir)/'`headers.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-headers.Tpo $(DEPDIR)/libcurl_la-headers.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='headers.c' object='libcurl_la-headers.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-headers.lo `test -f 'headers.c' || echo '$(srcdir)/'`headers.c
+
+libcurl_la-hmac.lo: hmac.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hmac.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hmac.Tpo -c -o libcurl_la-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hmac.Tpo $(DEPDIR)/libcurl_la-hmac.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hmac.c' object='libcurl_la-hmac.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c
+
+libcurl_la-hostasyn.lo: hostasyn.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostasyn.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostasyn.Tpo -c -o libcurl_la-hostasyn.lo `test -f 'hostasyn.c' || echo '$(srcdir)/'`hostasyn.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostasyn.Tpo $(DEPDIR)/libcurl_la-hostasyn.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostasyn.c' object='libcurl_la-hostasyn.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hostasyn.lo `test -f 'hostasyn.c' || echo '$(srcdir)/'`hostasyn.c
+
+libcurl_la-hostip.lo: hostip.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostip.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostip.Tpo -c -o libcurl_la-hostip.lo `test -f 'hostip.c' || echo '$(srcdir)/'`hostip.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostip.Tpo $(DEPDIR)/libcurl_la-hostip.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostip.c' object='libcurl_la-hostip.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hostip.lo `test -f 'hostip.c' || echo '$(srcdir)/'`hostip.c
+
+libcurl_la-hostip4.lo: hostip4.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostip4.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostip4.Tpo -c -o libcurl_la-hostip4.lo `test -f 'hostip4.c' || echo '$(srcdir)/'`hostip4.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostip4.Tpo $(DEPDIR)/libcurl_la-hostip4.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostip4.c' object='libcurl_la-hostip4.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hostip4.lo `test -f 'hostip4.c' || echo '$(srcdir)/'`hostip4.c
+
+libcurl_la-hostip6.lo: hostip6.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostip6.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostip6.Tpo -c -o libcurl_la-hostip6.lo `test -f 'hostip6.c' || echo '$(srcdir)/'`hostip6.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostip6.Tpo $(DEPDIR)/libcurl_la-hostip6.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostip6.c' object='libcurl_la-hostip6.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hostip6.lo `test -f 'hostip6.c' || echo '$(srcdir)/'`hostip6.c
+
+libcurl_la-hostsyn.lo: hostsyn.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostsyn.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostsyn.Tpo -c -o libcurl_la-hostsyn.lo `test -f 'hostsyn.c' || echo '$(srcdir)/'`hostsyn.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostsyn.Tpo $(DEPDIR)/libcurl_la-hostsyn.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostsyn.c' object='libcurl_la-hostsyn.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hostsyn.lo `test -f 'hostsyn.c' || echo '$(srcdir)/'`hostsyn.c
+
+libcurl_la-hsts.lo: hsts.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hsts.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hsts.Tpo -c -o libcurl_la-hsts.lo `test -f 'hsts.c' || echo '$(srcdir)/'`hsts.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hsts.Tpo $(DEPDIR)/libcurl_la-hsts.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hsts.c' object='libcurl_la-hsts.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hsts.lo `test -f 'hsts.c' || echo '$(srcdir)/'`hsts.c
+
+libcurl_la-http.lo: http.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http.Tpo -c -o libcurl_la-http.lo `test -f 'http.c' || echo '$(srcdir)/'`http.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http.Tpo $(DEPDIR)/libcurl_la-http.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http.c' object='libcurl_la-http.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http.lo `test -f 'http.c' || echo '$(srcdir)/'`http.c
+
+libcurl_la-http2.lo: http2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http2.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http2.Tpo -c -o libcurl_la-http2.lo `test -f 'http2.c' || echo '$(srcdir)/'`http2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http2.Tpo $(DEPDIR)/libcurl_la-http2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http2.c' object='libcurl_la-http2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http2.lo `test -f 'http2.c' || echo '$(srcdir)/'`http2.c
+
+libcurl_la-http_chunks.lo: http_chunks.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http_chunks.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http_chunks.Tpo -c -o libcurl_la-http_chunks.lo `test -f 'http_chunks.c' || echo '$(srcdir)/'`http_chunks.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http_chunks.Tpo $(DEPDIR)/libcurl_la-http_chunks.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_chunks.c' object='libcurl_la-http_chunks.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http_chunks.lo `test -f 'http_chunks.c' || echo '$(srcdir)/'`http_chunks.c
+
+libcurl_la-http_digest.lo: http_digest.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http_digest.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http_digest.Tpo -c -o libcurl_la-http_digest.lo `test -f 'http_digest.c' || echo '$(srcdir)/'`http_digest.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http_digest.Tpo $(DEPDIR)/libcurl_la-http_digest.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_digest.c' object='libcurl_la-http_digest.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http_digest.lo `test -f 'http_digest.c' || echo '$(srcdir)/'`http_digest.c
+
+libcurl_la-http_negotiate.lo: http_negotiate.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http_negotiate.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http_negotiate.Tpo -c -o libcurl_la-http_negotiate.lo `test -f 'http_negotiate.c' || echo '$(srcdir)/'`http_negotiate.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http_negotiate.Tpo $(DEPDIR)/libcurl_la-http_negotiate.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_negotiate.c' object='libcurl_la-http_negotiate.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http_negotiate.lo `test -f 'http_negotiate.c' || echo '$(srcdir)/'`http_negotiate.c
+
+libcurl_la-http_ntlm.lo: http_ntlm.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http_ntlm.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http_ntlm.Tpo -c -o libcurl_la-http_ntlm.lo `test -f 'http_ntlm.c' || echo '$(srcdir)/'`http_ntlm.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http_ntlm.Tpo $(DEPDIR)/libcurl_la-http_ntlm.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_ntlm.c' object='libcurl_la-http_ntlm.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http_ntlm.lo `test -f 'http_ntlm.c' || echo '$(srcdir)/'`http_ntlm.c
+
+libcurl_la-http_proxy.lo: http_proxy.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http_proxy.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http_proxy.Tpo -c -o libcurl_la-http_proxy.lo `test -f 'http_proxy.c' || echo '$(srcdir)/'`http_proxy.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http_proxy.Tpo $(DEPDIR)/libcurl_la-http_proxy.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_proxy.c' object='libcurl_la-http_proxy.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http_proxy.lo `test -f 'http_proxy.c' || echo '$(srcdir)/'`http_proxy.c
+
+libcurl_la-http_aws_sigv4.lo: http_aws_sigv4.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-http_aws_sigv4.lo -MD -MP -MF $(DEPDIR)/libcurl_la-http_aws_sigv4.Tpo -c -o libcurl_la-http_aws_sigv4.lo `test -f 'http_aws_sigv4.c' || echo '$(srcdir)/'`http_aws_sigv4.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-http_aws_sigv4.Tpo $(DEPDIR)/libcurl_la-http_aws_sigv4.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_aws_sigv4.c' object='libcurl_la-http_aws_sigv4.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-http_aws_sigv4.lo `test -f 'http_aws_sigv4.c' || echo '$(srcdir)/'`http_aws_sigv4.c
+
+libcurl_la-idn.lo: idn.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-idn.lo -MD -MP -MF $(DEPDIR)/libcurl_la-idn.Tpo -c -o libcurl_la-idn.lo `test -f 'idn.c' || echo '$(srcdir)/'`idn.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-idn.Tpo $(DEPDIR)/libcurl_la-idn.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='idn.c' object='libcurl_la-idn.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-idn.lo `test -f 'idn.c' || echo '$(srcdir)/'`idn.c
+
+libcurl_la-if2ip.lo: if2ip.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-if2ip.lo -MD -MP -MF $(DEPDIR)/libcurl_la-if2ip.Tpo -c -o libcurl_la-if2ip.lo `test -f 'if2ip.c' || echo '$(srcdir)/'`if2ip.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-if2ip.Tpo $(DEPDIR)/libcurl_la-if2ip.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='if2ip.c' object='libcurl_la-if2ip.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-if2ip.lo `test -f 'if2ip.c' || echo '$(srcdir)/'`if2ip.c
+
+libcurl_la-imap.lo: imap.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-imap.lo -MD -MP -MF $(DEPDIR)/libcurl_la-imap.Tpo -c -o libcurl_la-imap.lo `test -f 'imap.c' || echo '$(srcdir)/'`imap.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-imap.Tpo $(DEPDIR)/libcurl_la-imap.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='imap.c' object='libcurl_la-imap.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-imap.lo `test -f 'imap.c' || echo '$(srcdir)/'`imap.c
+
+libcurl_la-inet_ntop.lo: inet_ntop.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-inet_ntop.lo -MD -MP -MF $(DEPDIR)/libcurl_la-inet_ntop.Tpo -c -o libcurl_la-inet_ntop.lo `test -f 'inet_ntop.c' || echo '$(srcdir)/'`inet_ntop.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-inet_ntop.Tpo $(DEPDIR)/libcurl_la-inet_ntop.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='inet_ntop.c' object='libcurl_la-inet_ntop.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-inet_ntop.lo `test -f 'inet_ntop.c' || echo '$(srcdir)/'`inet_ntop.c
+
+libcurl_la-inet_pton.lo: inet_pton.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-inet_pton.lo -MD -MP -MF $(DEPDIR)/libcurl_la-inet_pton.Tpo -c -o libcurl_la-inet_pton.lo `test -f 'inet_pton.c' || echo '$(srcdir)/'`inet_pton.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-inet_pton.Tpo $(DEPDIR)/libcurl_la-inet_pton.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='inet_pton.c' object='libcurl_la-inet_pton.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-inet_pton.lo `test -f 'inet_pton.c' || echo '$(srcdir)/'`inet_pton.c
+
+libcurl_la-krb5.lo: krb5.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-krb5.lo -MD -MP -MF $(DEPDIR)/libcurl_la-krb5.Tpo -c -o libcurl_la-krb5.lo `test -f 'krb5.c' || echo '$(srcdir)/'`krb5.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-krb5.Tpo $(DEPDIR)/libcurl_la-krb5.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='krb5.c' object='libcurl_la-krb5.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-krb5.lo `test -f 'krb5.c' || echo '$(srcdir)/'`krb5.c
+
+libcurl_la-ldap.lo: ldap.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-ldap.lo -MD -MP -MF $(DEPDIR)/libcurl_la-ldap.Tpo -c -o libcurl_la-ldap.lo `test -f 'ldap.c' || echo '$(srcdir)/'`ldap.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-ldap.Tpo $(DEPDIR)/libcurl_la-ldap.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ldap.c' object='libcurl_la-ldap.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-ldap.lo `test -f 'ldap.c' || echo '$(srcdir)/'`ldap.c
+
+libcurl_la-llist.lo: llist.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-llist.lo -MD -MP -MF $(DEPDIR)/libcurl_la-llist.Tpo -c -o libcurl_la-llist.lo `test -f 'llist.c' || echo '$(srcdir)/'`llist.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-llist.Tpo $(DEPDIR)/libcurl_la-llist.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='llist.c' object='libcurl_la-llist.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-llist.lo `test -f 'llist.c' || echo '$(srcdir)/'`llist.c
+
+libcurl_la-md4.lo: md4.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-md4.lo -MD -MP -MF $(DEPDIR)/libcurl_la-md4.Tpo -c -o libcurl_la-md4.lo `test -f 'md4.c' || echo '$(srcdir)/'`md4.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-md4.Tpo $(DEPDIR)/libcurl_la-md4.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='md4.c' object='libcurl_la-md4.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-md4.lo `test -f 'md4.c' || echo '$(srcdir)/'`md4.c
+
+libcurl_la-md5.lo: md5.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-md5.lo -MD -MP -MF $(DEPDIR)/libcurl_la-md5.Tpo -c -o libcurl_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-md5.Tpo $(DEPDIR)/libcurl_la-md5.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='md5.c' object='libcurl_la-md5.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c
+
+libcurl_la-memdebug.lo: memdebug.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-memdebug.lo -MD -MP -MF $(DEPDIR)/libcurl_la-memdebug.Tpo -c -o libcurl_la-memdebug.lo `test -f 'memdebug.c' || echo '$(srcdir)/'`memdebug.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-memdebug.Tpo $(DEPDIR)/libcurl_la-memdebug.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='memdebug.c' object='libcurl_la-memdebug.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-memdebug.lo `test -f 'memdebug.c' || echo '$(srcdir)/'`memdebug.c
+
+libcurl_la-mime.lo: mime.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-mime.lo -MD -MP -MF $(DEPDIR)/libcurl_la-mime.Tpo -c -o libcurl_la-mime.lo `test -f 'mime.c' || echo '$(srcdir)/'`mime.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-mime.Tpo $(DEPDIR)/libcurl_la-mime.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mime.c' object='libcurl_la-mime.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-mime.lo `test -f 'mime.c' || echo '$(srcdir)/'`mime.c
+
+libcurl_la-mprintf.lo: mprintf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-mprintf.lo -MD -MP -MF $(DEPDIR)/libcurl_la-mprintf.Tpo -c -o libcurl_la-mprintf.lo `test -f 'mprintf.c' || echo '$(srcdir)/'`mprintf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-mprintf.Tpo $(DEPDIR)/libcurl_la-mprintf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mprintf.c' object='libcurl_la-mprintf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-mprintf.lo `test -f 'mprintf.c' || echo '$(srcdir)/'`mprintf.c
+
+libcurl_la-mqtt.lo: mqtt.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-mqtt.lo -MD -MP -MF $(DEPDIR)/libcurl_la-mqtt.Tpo -c -o libcurl_la-mqtt.lo `test -f 'mqtt.c' || echo '$(srcdir)/'`mqtt.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-mqtt.Tpo $(DEPDIR)/libcurl_la-mqtt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mqtt.c' object='libcurl_la-mqtt.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-mqtt.lo `test -f 'mqtt.c' || echo '$(srcdir)/'`mqtt.c
+
+libcurl_la-multi.lo: multi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-multi.lo -MD -MP -MF $(DEPDIR)/libcurl_la-multi.Tpo -c -o libcurl_la-multi.lo `test -f 'multi.c' || echo '$(srcdir)/'`multi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-multi.Tpo $(DEPDIR)/libcurl_la-multi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='multi.c' object='libcurl_la-multi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-multi.lo `test -f 'multi.c' || echo '$(srcdir)/'`multi.c
+
+libcurl_la-netrc.lo: netrc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-netrc.lo -MD -MP -MF $(DEPDIR)/libcurl_la-netrc.Tpo -c -o libcurl_la-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-netrc.Tpo $(DEPDIR)/libcurl_la-netrc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netrc.c' object='libcurl_la-netrc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c
+
+libcurl_la-nonblock.lo: nonblock.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-nonblock.lo -MD -MP -MF $(DEPDIR)/libcurl_la-nonblock.Tpo -c -o libcurl_la-nonblock.lo `test -f 'nonblock.c' || echo '$(srcdir)/'`nonblock.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-nonblock.Tpo $(DEPDIR)/libcurl_la-nonblock.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nonblock.c' object='libcurl_la-nonblock.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-nonblock.lo `test -f 'nonblock.c' || echo '$(srcdir)/'`nonblock.c
+
+libcurl_la-noproxy.lo: noproxy.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-noproxy.lo -MD -MP -MF $(DEPDIR)/libcurl_la-noproxy.Tpo -c -o libcurl_la-noproxy.lo `test -f 'noproxy.c' || echo '$(srcdir)/'`noproxy.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-noproxy.Tpo $(DEPDIR)/libcurl_la-noproxy.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='noproxy.c' object='libcurl_la-noproxy.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-noproxy.lo `test -f 'noproxy.c' || echo '$(srcdir)/'`noproxy.c
+
+libcurl_la-openldap.lo: openldap.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-openldap.lo -MD -MP -MF $(DEPDIR)/libcurl_la-openldap.Tpo -c -o libcurl_la-openldap.lo `test -f 'openldap.c' || echo '$(srcdir)/'`openldap.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-openldap.Tpo $(DEPDIR)/libcurl_la-openldap.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openldap.c' object='libcurl_la-openldap.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-openldap.lo `test -f 'openldap.c' || echo '$(srcdir)/'`openldap.c
+
+libcurl_la-parsedate.lo: parsedate.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-parsedate.lo -MD -MP -MF $(DEPDIR)/libcurl_la-parsedate.Tpo -c -o libcurl_la-parsedate.lo `test -f 'parsedate.c' || echo '$(srcdir)/'`parsedate.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-parsedate.Tpo $(DEPDIR)/libcurl_la-parsedate.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='parsedate.c' object='libcurl_la-parsedate.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-parsedate.lo `test -f 'parsedate.c' || echo '$(srcdir)/'`parsedate.c
+
+libcurl_la-pingpong.lo: pingpong.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-pingpong.lo -MD -MP -MF $(DEPDIR)/libcurl_la-pingpong.Tpo -c -o libcurl_la-pingpong.lo `test -f 'pingpong.c' || echo '$(srcdir)/'`pingpong.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-pingpong.Tpo $(DEPDIR)/libcurl_la-pingpong.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pingpong.c' object='libcurl_la-pingpong.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-pingpong.lo `test -f 'pingpong.c' || echo '$(srcdir)/'`pingpong.c
+
+libcurl_la-pop3.lo: pop3.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-pop3.lo -MD -MP -MF $(DEPDIR)/libcurl_la-pop3.Tpo -c -o libcurl_la-pop3.lo `test -f 'pop3.c' || echo '$(srcdir)/'`pop3.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-pop3.Tpo $(DEPDIR)/libcurl_la-pop3.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pop3.c' object='libcurl_la-pop3.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-pop3.lo `test -f 'pop3.c' || echo '$(srcdir)/'`pop3.c
+
+libcurl_la-progress.lo: progress.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-progress.lo -MD -MP -MF $(DEPDIR)/libcurl_la-progress.Tpo -c -o libcurl_la-progress.lo `test -f 'progress.c' || echo '$(srcdir)/'`progress.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-progress.Tpo $(DEPDIR)/libcurl_la-progress.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='progress.c' object='libcurl_la-progress.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-progress.lo `test -f 'progress.c' || echo '$(srcdir)/'`progress.c
+
+libcurl_la-psl.lo: psl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-psl.lo -MD -MP -MF $(DEPDIR)/libcurl_la-psl.Tpo -c -o libcurl_la-psl.lo `test -f 'psl.c' || echo '$(srcdir)/'`psl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-psl.Tpo $(DEPDIR)/libcurl_la-psl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='psl.c' object='libcurl_la-psl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-psl.lo `test -f 'psl.c' || echo '$(srcdir)/'`psl.c
+
+libcurl_la-rand.lo: rand.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-rand.lo -MD -MP -MF $(DEPDIR)/libcurl_la-rand.Tpo -c -o libcurl_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-rand.Tpo $(DEPDIR)/libcurl_la-rand.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rand.c' object='libcurl_la-rand.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c
+
+libcurl_la-rename.lo: rename.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-rename.lo -MD -MP -MF $(DEPDIR)/libcurl_la-rename.Tpo -c -o libcurl_la-rename.lo `test -f 'rename.c' || echo '$(srcdir)/'`rename.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-rename.Tpo $(DEPDIR)/libcurl_la-rename.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rename.c' object='libcurl_la-rename.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-rename.lo `test -f 'rename.c' || echo '$(srcdir)/'`rename.c
+
+libcurl_la-rtsp.lo: rtsp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-rtsp.lo -MD -MP -MF $(DEPDIR)/libcurl_la-rtsp.Tpo -c -o libcurl_la-rtsp.lo `test -f 'rtsp.c' || echo '$(srcdir)/'`rtsp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-rtsp.Tpo $(DEPDIR)/libcurl_la-rtsp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rtsp.c' object='libcurl_la-rtsp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-rtsp.lo `test -f 'rtsp.c' || echo '$(srcdir)/'`rtsp.c
+
+libcurl_la-select.lo: select.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-select.lo -MD -MP -MF $(DEPDIR)/libcurl_la-select.Tpo -c -o libcurl_la-select.lo `test -f 'select.c' || echo '$(srcdir)/'`select.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-select.Tpo $(DEPDIR)/libcurl_la-select.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='select.c' object='libcurl_la-select.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-select.lo `test -f 'select.c' || echo '$(srcdir)/'`select.c
+
+libcurl_la-sendf.lo: sendf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-sendf.lo -MD -MP -MF $(DEPDIR)/libcurl_la-sendf.Tpo -c -o libcurl_la-sendf.lo `test -f 'sendf.c' || echo '$(srcdir)/'`sendf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-sendf.Tpo $(DEPDIR)/libcurl_la-sendf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sendf.c' object='libcurl_la-sendf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-sendf.lo `test -f 'sendf.c' || echo '$(srcdir)/'`sendf.c
+
+libcurl_la-setopt.lo: setopt.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-setopt.lo -MD -MP -MF $(DEPDIR)/libcurl_la-setopt.Tpo -c -o libcurl_la-setopt.lo `test -f 'setopt.c' || echo '$(srcdir)/'`setopt.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-setopt.Tpo $(DEPDIR)/libcurl_la-setopt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='setopt.c' object='libcurl_la-setopt.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-setopt.lo `test -f 'setopt.c' || echo '$(srcdir)/'`setopt.c
+
+libcurl_la-sha256.lo: sha256.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-sha256.lo -MD -MP -MF $(DEPDIR)/libcurl_la-sha256.Tpo -c -o libcurl_la-sha256.lo `test -f 'sha256.c' || echo '$(srcdir)/'`sha256.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-sha256.Tpo $(DEPDIR)/libcurl_la-sha256.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sha256.c' object='libcurl_la-sha256.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-sha256.lo `test -f 'sha256.c' || echo '$(srcdir)/'`sha256.c
+
+libcurl_la-share.lo: share.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-share.lo -MD -MP -MF $(DEPDIR)/libcurl_la-share.Tpo -c -o libcurl_la-share.lo `test -f 'share.c' || echo '$(srcdir)/'`share.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-share.Tpo $(DEPDIR)/libcurl_la-share.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='share.c' object='libcurl_la-share.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-share.lo `test -f 'share.c' || echo '$(srcdir)/'`share.c
+
+libcurl_la-slist.lo: slist.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-slist.lo -MD -MP -MF $(DEPDIR)/libcurl_la-slist.Tpo -c -o libcurl_la-slist.lo `test -f 'slist.c' || echo '$(srcdir)/'`slist.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-slist.Tpo $(DEPDIR)/libcurl_la-slist.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='slist.c' object='libcurl_la-slist.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-slist.lo `test -f 'slist.c' || echo '$(srcdir)/'`slist.c
+
+libcurl_la-smb.lo: smb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-smb.lo -MD -MP -MF $(DEPDIR)/libcurl_la-smb.Tpo -c -o libcurl_la-smb.lo `test -f 'smb.c' || echo '$(srcdir)/'`smb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-smb.Tpo $(DEPDIR)/libcurl_la-smb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='smb.c' object='libcurl_la-smb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-smb.lo `test -f 'smb.c' || echo '$(srcdir)/'`smb.c
+
+libcurl_la-smtp.lo: smtp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-smtp.lo -MD -MP -MF $(DEPDIR)/libcurl_la-smtp.Tpo -c -o libcurl_la-smtp.lo `test -f 'smtp.c' || echo '$(srcdir)/'`smtp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-smtp.Tpo $(DEPDIR)/libcurl_la-smtp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='smtp.c' object='libcurl_la-smtp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-smtp.lo `test -f 'smtp.c' || echo '$(srcdir)/'`smtp.c
+
+libcurl_la-socketpair.lo: socketpair.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-socketpair.lo -MD -MP -MF $(DEPDIR)/libcurl_la-socketpair.Tpo -c -o libcurl_la-socketpair.lo `test -f 'socketpair.c' || echo '$(srcdir)/'`socketpair.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-socketpair.Tpo $(DEPDIR)/libcurl_la-socketpair.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socketpair.c' object='libcurl_la-socketpair.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-socketpair.lo `test -f 'socketpair.c' || echo '$(srcdir)/'`socketpair.c
+
+libcurl_la-socks.lo: socks.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-socks.lo -MD -MP -MF $(DEPDIR)/libcurl_la-socks.Tpo -c -o libcurl_la-socks.lo `test -f 'socks.c' || echo '$(srcdir)/'`socks.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-socks.Tpo $(DEPDIR)/libcurl_la-socks.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socks.c' object='libcurl_la-socks.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-socks.lo `test -f 'socks.c' || echo '$(srcdir)/'`socks.c
+
+libcurl_la-socks_gssapi.lo: socks_gssapi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-socks_gssapi.lo -MD -MP -MF $(DEPDIR)/libcurl_la-socks_gssapi.Tpo -c -o libcurl_la-socks_gssapi.lo `test -f 'socks_gssapi.c' || echo '$(srcdir)/'`socks_gssapi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-socks_gssapi.Tpo $(DEPDIR)/libcurl_la-socks_gssapi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socks_gssapi.c' object='libcurl_la-socks_gssapi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-socks_gssapi.lo `test -f 'socks_gssapi.c' || echo '$(srcdir)/'`socks_gssapi.c
+
+libcurl_la-socks_sspi.lo: socks_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-socks_sspi.lo -MD -MP -MF $(DEPDIR)/libcurl_la-socks_sspi.Tpo -c -o libcurl_la-socks_sspi.lo `test -f 'socks_sspi.c' || echo '$(srcdir)/'`socks_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-socks_sspi.Tpo $(DEPDIR)/libcurl_la-socks_sspi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socks_sspi.c' object='libcurl_la-socks_sspi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-socks_sspi.lo `test -f 'socks_sspi.c' || echo '$(srcdir)/'`socks_sspi.c
+
+libcurl_la-speedcheck.lo: speedcheck.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-speedcheck.lo -MD -MP -MF $(DEPDIR)/libcurl_la-speedcheck.Tpo -c -o libcurl_la-speedcheck.lo `test -f 'speedcheck.c' || echo '$(srcdir)/'`speedcheck.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-speedcheck.Tpo $(DEPDIR)/libcurl_la-speedcheck.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='speedcheck.c' object='libcurl_la-speedcheck.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-speedcheck.lo `test -f 'speedcheck.c' || echo '$(srcdir)/'`speedcheck.c
+
+libcurl_la-splay.lo: splay.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-splay.lo -MD -MP -MF $(DEPDIR)/libcurl_la-splay.Tpo -c -o libcurl_la-splay.lo `test -f 'splay.c' || echo '$(srcdir)/'`splay.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-splay.Tpo $(DEPDIR)/libcurl_la-splay.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='splay.c' object='libcurl_la-splay.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-splay.lo `test -f 'splay.c' || echo '$(srcdir)/'`splay.c
+
+libcurl_la-strcase.lo: strcase.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strcase.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strcase.Tpo -c -o libcurl_la-strcase.lo `test -f 'strcase.c' || echo '$(srcdir)/'`strcase.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strcase.Tpo $(DEPDIR)/libcurl_la-strcase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strcase.c' object='libcurl_la-strcase.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strcase.lo `test -f 'strcase.c' || echo '$(srcdir)/'`strcase.c
+
+libcurl_la-strdup.lo: strdup.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strdup.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strdup.Tpo -c -o libcurl_la-strdup.lo `test -f 'strdup.c' || echo '$(srcdir)/'`strdup.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strdup.Tpo $(DEPDIR)/libcurl_la-strdup.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strdup.c' object='libcurl_la-strdup.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strdup.lo `test -f 'strdup.c' || echo '$(srcdir)/'`strdup.c
+
+libcurl_la-strerror.lo: strerror.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strerror.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strerror.Tpo -c -o libcurl_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strerror.Tpo $(DEPDIR)/libcurl_la-strerror.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strerror.c' object='libcurl_la-strerror.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c
+
+libcurl_la-strtok.lo: strtok.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strtok.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strtok.Tpo -c -o libcurl_la-strtok.lo `test -f 'strtok.c' || echo '$(srcdir)/'`strtok.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strtok.Tpo $(DEPDIR)/libcurl_la-strtok.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strtok.c' object='libcurl_la-strtok.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strtok.lo `test -f 'strtok.c' || echo '$(srcdir)/'`strtok.c
+
+libcurl_la-strtoofft.lo: strtoofft.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strtoofft.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strtoofft.Tpo -c -o libcurl_la-strtoofft.lo `test -f 'strtoofft.c' || echo '$(srcdir)/'`strtoofft.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strtoofft.Tpo $(DEPDIR)/libcurl_la-strtoofft.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strtoofft.c' object='libcurl_la-strtoofft.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strtoofft.lo `test -f 'strtoofft.c' || echo '$(srcdir)/'`strtoofft.c
+
+libcurl_la-system_win32.lo: system_win32.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-system_win32.lo -MD -MP -MF $(DEPDIR)/libcurl_la-system_win32.Tpo -c -o libcurl_la-system_win32.lo `test -f 'system_win32.c' || echo '$(srcdir)/'`system_win32.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-system_win32.Tpo $(DEPDIR)/libcurl_la-system_win32.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='system_win32.c' object='libcurl_la-system_win32.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-system_win32.lo `test -f 'system_win32.c' || echo '$(srcdir)/'`system_win32.c
+
+libcurl_la-telnet.lo: telnet.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-telnet.lo -MD -MP -MF $(DEPDIR)/libcurl_la-telnet.Tpo -c -o libcurl_la-telnet.lo `test -f 'telnet.c' || echo '$(srcdir)/'`telnet.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-telnet.Tpo $(DEPDIR)/libcurl_la-telnet.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='telnet.c' object='libcurl_la-telnet.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-telnet.lo `test -f 'telnet.c' || echo '$(srcdir)/'`telnet.c
+
+libcurl_la-tftp.lo: tftp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-tftp.lo -MD -MP -MF $(DEPDIR)/libcurl_la-tftp.Tpo -c -o libcurl_la-tftp.lo `test -f 'tftp.c' || echo '$(srcdir)/'`tftp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-tftp.Tpo $(DEPDIR)/libcurl_la-tftp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tftp.c' object='libcurl_la-tftp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-tftp.lo `test -f 'tftp.c' || echo '$(srcdir)/'`tftp.c
+
+libcurl_la-timediff.lo: timediff.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-timediff.lo -MD -MP -MF $(DEPDIR)/libcurl_la-timediff.Tpo -c -o libcurl_la-timediff.lo `test -f 'timediff.c' || echo '$(srcdir)/'`timediff.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-timediff.Tpo $(DEPDIR)/libcurl_la-timediff.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timediff.c' object='libcurl_la-timediff.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-timediff.lo `test -f 'timediff.c' || echo '$(srcdir)/'`timediff.c
+
+libcurl_la-timeval.lo: timeval.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-timeval.lo -MD -MP -MF $(DEPDIR)/libcurl_la-timeval.Tpo -c -o libcurl_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-timeval.Tpo $(DEPDIR)/libcurl_la-timeval.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timeval.c' object='libcurl_la-timeval.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c
+
+libcurl_la-transfer.lo: transfer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-transfer.lo -MD -MP -MF $(DEPDIR)/libcurl_la-transfer.Tpo -c -o libcurl_la-transfer.lo `test -f 'transfer.c' || echo '$(srcdir)/'`transfer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-transfer.Tpo $(DEPDIR)/libcurl_la-transfer.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='transfer.c' object='libcurl_la-transfer.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-transfer.lo `test -f 'transfer.c' || echo '$(srcdir)/'`transfer.c
+
+libcurl_la-url.lo: url.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-url.lo -MD -MP -MF $(DEPDIR)/libcurl_la-url.Tpo -c -o libcurl_la-url.lo `test -f 'url.c' || echo '$(srcdir)/'`url.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-url.Tpo $(DEPDIR)/libcurl_la-url.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='url.c' object='libcurl_la-url.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-url.lo `test -f 'url.c' || echo '$(srcdir)/'`url.c
+
+libcurl_la-urlapi.lo: urlapi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-urlapi.lo -MD -MP -MF $(DEPDIR)/libcurl_la-urlapi.Tpo -c -o libcurl_la-urlapi.lo `test -f 'urlapi.c' || echo '$(srcdir)/'`urlapi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-urlapi.Tpo $(DEPDIR)/libcurl_la-urlapi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='urlapi.c' object='libcurl_la-urlapi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-urlapi.lo `test -f 'urlapi.c' || echo '$(srcdir)/'`urlapi.c
+
+libcurl_la-version.lo: version.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-version.lo -MD -MP -MF $(DEPDIR)/libcurl_la-version.Tpo -c -o libcurl_la-version.lo `test -f 'version.c' || echo '$(srcdir)/'`version.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-version.Tpo $(DEPDIR)/libcurl_la-version.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='version.c' object='libcurl_la-version.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-version.lo `test -f 'version.c' || echo '$(srcdir)/'`version.c
+
+libcurl_la-version_win32.lo: version_win32.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-version_win32.lo -MD -MP -MF $(DEPDIR)/libcurl_la-version_win32.Tpo -c -o libcurl_la-version_win32.lo `test -f 'version_win32.c' || echo '$(srcdir)/'`version_win32.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-version_win32.Tpo $(DEPDIR)/libcurl_la-version_win32.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='version_win32.c' object='libcurl_la-version_win32.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-version_win32.lo `test -f 'version_win32.c' || echo '$(srcdir)/'`version_win32.c
+
+libcurl_la-warnless.lo: warnless.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-warnless.lo -MD -MP -MF $(DEPDIR)/libcurl_la-warnless.Tpo -c -o libcurl_la-warnless.lo `test -f 'warnless.c' || echo '$(srcdir)/'`warnless.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-warnless.Tpo $(DEPDIR)/libcurl_la-warnless.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='warnless.c' object='libcurl_la-warnless.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-warnless.lo `test -f 'warnless.c' || echo '$(srcdir)/'`warnless.c
+
+libcurl_la-wildcard.lo: wildcard.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-wildcard.lo -MD -MP -MF $(DEPDIR)/libcurl_la-wildcard.Tpo -c -o libcurl_la-wildcard.lo `test -f 'wildcard.c' || echo '$(srcdir)/'`wildcard.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-wildcard.Tpo $(DEPDIR)/libcurl_la-wildcard.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wildcard.c' object='libcurl_la-wildcard.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-wildcard.lo `test -f 'wildcard.c' || echo '$(srcdir)/'`wildcard.c
+
+libcurl_la-ws.lo: ws.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-ws.lo -MD -MP -MF $(DEPDIR)/libcurl_la-ws.Tpo -c -o libcurl_la-ws.lo `test -f 'ws.c' || echo '$(srcdir)/'`ws.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-ws.Tpo $(DEPDIR)/libcurl_la-ws.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ws.c' object='libcurl_la-ws.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-ws.lo `test -f 'ws.c' || echo '$(srcdir)/'`ws.c
+
+vauth/libcurl_la-cleartext.lo: vauth/cleartext.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-cleartext.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-cleartext.Tpo -c -o vauth/libcurl_la-cleartext.lo `test -f 'vauth/cleartext.c' || echo '$(srcdir)/'`vauth/cleartext.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-cleartext.Tpo vauth/$(DEPDIR)/libcurl_la-cleartext.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/cleartext.c' object='vauth/libcurl_la-cleartext.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-cleartext.lo `test -f 'vauth/cleartext.c' || echo '$(srcdir)/'`vauth/cleartext.c
+
+vauth/libcurl_la-cram.lo: vauth/cram.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-cram.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-cram.Tpo -c -o vauth/libcurl_la-cram.lo `test -f 'vauth/cram.c' || echo '$(srcdir)/'`vauth/cram.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-cram.Tpo vauth/$(DEPDIR)/libcurl_la-cram.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/cram.c' object='vauth/libcurl_la-cram.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-cram.lo `test -f 'vauth/cram.c' || echo '$(srcdir)/'`vauth/cram.c
+
+vauth/libcurl_la-digest.lo: vauth/digest.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-digest.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-digest.Tpo -c -o vauth/libcurl_la-digest.lo `test -f 'vauth/digest.c' || echo '$(srcdir)/'`vauth/digest.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-digest.Tpo vauth/$(DEPDIR)/libcurl_la-digest.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/digest.c' object='vauth/libcurl_la-digest.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-digest.lo `test -f 'vauth/digest.c' || echo '$(srcdir)/'`vauth/digest.c
+
+vauth/libcurl_la-digest_sspi.lo: vauth/digest_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-digest_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-digest_sspi.Tpo -c -o vauth/libcurl_la-digest_sspi.lo `test -f 'vauth/digest_sspi.c' || echo '$(srcdir)/'`vauth/digest_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-digest_sspi.Tpo vauth/$(DEPDIR)/libcurl_la-digest_sspi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/digest_sspi.c' object='vauth/libcurl_la-digest_sspi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-digest_sspi.lo `test -f 'vauth/digest_sspi.c' || echo '$(srcdir)/'`vauth/digest_sspi.c
+
+vauth/libcurl_la-gsasl.lo: vauth/gsasl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-gsasl.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-gsasl.Tpo -c -o vauth/libcurl_la-gsasl.lo `test -f 'vauth/gsasl.c' || echo '$(srcdir)/'`vauth/gsasl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-gsasl.Tpo vauth/$(DEPDIR)/libcurl_la-gsasl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/gsasl.c' object='vauth/libcurl_la-gsasl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-gsasl.lo `test -f 'vauth/gsasl.c' || echo '$(srcdir)/'`vauth/gsasl.c
+
+vauth/libcurl_la-krb5_gssapi.lo: vauth/krb5_gssapi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-krb5_gssapi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Tpo -c -o vauth/libcurl_la-krb5_gssapi.lo `test -f 'vauth/krb5_gssapi.c' || echo '$(srcdir)/'`vauth/krb5_gssapi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Tpo vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/krb5_gssapi.c' object='vauth/libcurl_la-krb5_gssapi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-krb5_gssapi.lo `test -f 'vauth/krb5_gssapi.c' || echo '$(srcdir)/'`vauth/krb5_gssapi.c
+
+vauth/libcurl_la-krb5_sspi.lo: vauth/krb5_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-krb5_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Tpo -c -o vauth/libcurl_la-krb5_sspi.lo `test -f 'vauth/krb5_sspi.c' || echo '$(srcdir)/'`vauth/krb5_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Tpo vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/krb5_sspi.c' object='vauth/libcurl_la-krb5_sspi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-krb5_sspi.lo `test -f 'vauth/krb5_sspi.c' || echo '$(srcdir)/'`vauth/krb5_sspi.c
+
+vauth/libcurl_la-ntlm.lo: vauth/ntlm.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-ntlm.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-ntlm.Tpo -c -o vauth/libcurl_la-ntlm.lo `test -f 'vauth/ntlm.c' || echo '$(srcdir)/'`vauth/ntlm.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-ntlm.Tpo vauth/$(DEPDIR)/libcurl_la-ntlm.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/ntlm.c' object='vauth/libcurl_la-ntlm.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-ntlm.lo `test -f 'vauth/ntlm.c' || echo '$(srcdir)/'`vauth/ntlm.c
+
+vauth/libcurl_la-ntlm_sspi.lo: vauth/ntlm_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-ntlm_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Tpo -c -o vauth/libcurl_la-ntlm_sspi.lo `test -f 'vauth/ntlm_sspi.c' || echo '$(srcdir)/'`vauth/ntlm_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Tpo vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/ntlm_sspi.c' object='vauth/libcurl_la-ntlm_sspi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-ntlm_sspi.lo `test -f 'vauth/ntlm_sspi.c' || echo '$(srcdir)/'`vauth/ntlm_sspi.c
+
+vauth/libcurl_la-oauth2.lo: vauth/oauth2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-oauth2.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-oauth2.Tpo -c -o vauth/libcurl_la-oauth2.lo `test -f 'vauth/oauth2.c' || echo '$(srcdir)/'`vauth/oauth2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-oauth2.Tpo vauth/$(DEPDIR)/libcurl_la-oauth2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/oauth2.c' object='vauth/libcurl_la-oauth2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-oauth2.lo `test -f 'vauth/oauth2.c' || echo '$(srcdir)/'`vauth/oauth2.c
+
+vauth/libcurl_la-spnego_gssapi.lo: vauth/spnego_gssapi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-spnego_gssapi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Tpo -c -o vauth/libcurl_la-spnego_gssapi.lo `test -f 'vauth/spnego_gssapi.c' || echo '$(srcdir)/'`vauth/spnego_gssapi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Tpo vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/spnego_gssapi.c' object='vauth/libcurl_la-spnego_gssapi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-spnego_gssapi.lo `test -f 'vauth/spnego_gssapi.c' || echo '$(srcdir)/'`vauth/spnego_gssapi.c
+
+vauth/libcurl_la-spnego_sspi.lo: vauth/spnego_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-spnego_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Tpo -c -o vauth/libcurl_la-spnego_sspi.lo `test -f 'vauth/spnego_sspi.c' || echo '$(srcdir)/'`vauth/spnego_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Tpo vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/spnego_sspi.c' object='vauth/libcurl_la-spnego_sspi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-spnego_sspi.lo `test -f 'vauth/spnego_sspi.c' || echo '$(srcdir)/'`vauth/spnego_sspi.c
+
+vauth/libcurl_la-vauth.lo: vauth/vauth.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-vauth.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-vauth.Tpo -c -o vauth/libcurl_la-vauth.lo `test -f 'vauth/vauth.c' || echo '$(srcdir)/'`vauth/vauth.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-vauth.Tpo vauth/$(DEPDIR)/libcurl_la-vauth.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/vauth.c' object='vauth/libcurl_la-vauth.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurl_la-vauth.lo `test -f 'vauth/vauth.c' || echo '$(srcdir)/'`vauth/vauth.c
+
+vtls/libcurl_la-bearssl.lo: vtls/bearssl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-bearssl.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-bearssl.Tpo -c -o vtls/libcurl_la-bearssl.lo `test -f 'vtls/bearssl.c' || echo '$(srcdir)/'`vtls/bearssl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-bearssl.Tpo vtls/$(DEPDIR)/libcurl_la-bearssl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/bearssl.c' object='vtls/libcurl_la-bearssl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-bearssl.lo `test -f 'vtls/bearssl.c' || echo '$(srcdir)/'`vtls/bearssl.c
+
+vtls/libcurl_la-gskit.lo: vtls/gskit.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-gskit.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-gskit.Tpo -c -o vtls/libcurl_la-gskit.lo `test -f 'vtls/gskit.c' || echo '$(srcdir)/'`vtls/gskit.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-gskit.Tpo vtls/$(DEPDIR)/libcurl_la-gskit.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/gskit.c' object='vtls/libcurl_la-gskit.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-gskit.lo `test -f 'vtls/gskit.c' || echo '$(srcdir)/'`vtls/gskit.c
+
+vtls/libcurl_la-gtls.lo: vtls/gtls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-gtls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-gtls.Tpo -c -o vtls/libcurl_la-gtls.lo `test -f 'vtls/gtls.c' || echo '$(srcdir)/'`vtls/gtls.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-gtls.Tpo vtls/$(DEPDIR)/libcurl_la-gtls.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/gtls.c' object='vtls/libcurl_la-gtls.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-gtls.lo `test -f 'vtls/gtls.c' || echo '$(srcdir)/'`vtls/gtls.c
+
+vtls/libcurl_la-hostcheck.lo: vtls/hostcheck.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-hostcheck.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-hostcheck.Tpo -c -o vtls/libcurl_la-hostcheck.lo `test -f 'vtls/hostcheck.c' || echo '$(srcdir)/'`vtls/hostcheck.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-hostcheck.Tpo vtls/$(DEPDIR)/libcurl_la-hostcheck.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/hostcheck.c' object='vtls/libcurl_la-hostcheck.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-hostcheck.lo `test -f 'vtls/hostcheck.c' || echo '$(srcdir)/'`vtls/hostcheck.c
+
+vtls/libcurl_la-keylog.lo: vtls/keylog.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-keylog.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-keylog.Tpo -c -o vtls/libcurl_la-keylog.lo `test -f 'vtls/keylog.c' || echo '$(srcdir)/'`vtls/keylog.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-keylog.Tpo vtls/$(DEPDIR)/libcurl_la-keylog.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/keylog.c' object='vtls/libcurl_la-keylog.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-keylog.lo `test -f 'vtls/keylog.c' || echo '$(srcdir)/'`vtls/keylog.c
+
+vtls/libcurl_la-mbedtls.lo: vtls/mbedtls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-mbedtls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-mbedtls.Tpo -c -o vtls/libcurl_la-mbedtls.lo `test -f 'vtls/mbedtls.c' || echo '$(srcdir)/'`vtls/mbedtls.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-mbedtls.Tpo vtls/$(DEPDIR)/libcurl_la-mbedtls.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/mbedtls.c' object='vtls/libcurl_la-mbedtls.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-mbedtls.lo `test -f 'vtls/mbedtls.c' || echo '$(srcdir)/'`vtls/mbedtls.c
+
+vtls/libcurl_la-mbedtls_threadlock.lo: vtls/mbedtls_threadlock.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-mbedtls_threadlock.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Tpo -c -o vtls/libcurl_la-mbedtls_threadlock.lo `test -f 'vtls/mbedtls_threadlock.c' || echo '$(srcdir)/'`vtls/mbedtls_threadlock.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Tpo vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/mbedtls_threadlock.c' object='vtls/libcurl_la-mbedtls_threadlock.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-mbedtls_threadlock.lo `test -f 'vtls/mbedtls_threadlock.c' || echo '$(srcdir)/'`vtls/mbedtls_threadlock.c
+
+vtls/libcurl_la-nss.lo: vtls/nss.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-nss.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-nss.Tpo -c -o vtls/libcurl_la-nss.lo `test -f 'vtls/nss.c' || echo '$(srcdir)/'`vtls/nss.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-nss.Tpo vtls/$(DEPDIR)/libcurl_la-nss.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/nss.c' object='vtls/libcurl_la-nss.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-nss.lo `test -f 'vtls/nss.c' || echo '$(srcdir)/'`vtls/nss.c
+
+vtls/libcurl_la-openssl.lo: vtls/openssl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-openssl.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-openssl.Tpo -c -o vtls/libcurl_la-openssl.lo `test -f 'vtls/openssl.c' || echo '$(srcdir)/'`vtls/openssl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-openssl.Tpo vtls/$(DEPDIR)/libcurl_la-openssl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/openssl.c' object='vtls/libcurl_la-openssl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-openssl.lo `test -f 'vtls/openssl.c' || echo '$(srcdir)/'`vtls/openssl.c
+
+vtls/libcurl_la-rustls.lo: vtls/rustls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-rustls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-rustls.Tpo -c -o vtls/libcurl_la-rustls.lo `test -f 'vtls/rustls.c' || echo '$(srcdir)/'`vtls/rustls.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-rustls.Tpo vtls/$(DEPDIR)/libcurl_la-rustls.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/rustls.c' object='vtls/libcurl_la-rustls.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-rustls.lo `test -f 'vtls/rustls.c' || echo '$(srcdir)/'`vtls/rustls.c
+
+vtls/libcurl_la-schannel.lo: vtls/schannel.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-schannel.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-schannel.Tpo -c -o vtls/libcurl_la-schannel.lo `test -f 'vtls/schannel.c' || echo '$(srcdir)/'`vtls/schannel.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-schannel.Tpo vtls/$(DEPDIR)/libcurl_la-schannel.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/schannel.c' object='vtls/libcurl_la-schannel.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-schannel.lo `test -f 'vtls/schannel.c' || echo '$(srcdir)/'`vtls/schannel.c
+
+vtls/libcurl_la-schannel_verify.lo: vtls/schannel_verify.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-schannel_verify.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-schannel_verify.Tpo -c -o vtls/libcurl_la-schannel_verify.lo `test -f 'vtls/schannel_verify.c' || echo '$(srcdir)/'`vtls/schannel_verify.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-schannel_verify.Tpo vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/schannel_verify.c' object='vtls/libcurl_la-schannel_verify.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-schannel_verify.lo `test -f 'vtls/schannel_verify.c' || echo '$(srcdir)/'`vtls/schannel_verify.c
+
+vtls/libcurl_la-sectransp.lo: vtls/sectransp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-sectransp.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-sectransp.Tpo -c -o vtls/libcurl_la-sectransp.lo `test -f 'vtls/sectransp.c' || echo '$(srcdir)/'`vtls/sectransp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-sectransp.Tpo vtls/$(DEPDIR)/libcurl_la-sectransp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/sectransp.c' object='vtls/libcurl_la-sectransp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-sectransp.lo `test -f 'vtls/sectransp.c' || echo '$(srcdir)/'`vtls/sectransp.c
+
+vtls/libcurl_la-vtls.lo: vtls/vtls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-vtls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-vtls.Tpo -c -o vtls/libcurl_la-vtls.lo `test -f 'vtls/vtls.c' || echo '$(srcdir)/'`vtls/vtls.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-vtls.Tpo vtls/$(DEPDIR)/libcurl_la-vtls.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/vtls.c' object='vtls/libcurl_la-vtls.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-vtls.lo `test -f 'vtls/vtls.c' || echo '$(srcdir)/'`vtls/vtls.c
+
+vtls/libcurl_la-wolfssl.lo: vtls/wolfssl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-wolfssl.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-wolfssl.Tpo -c -o vtls/libcurl_la-wolfssl.lo `test -f 'vtls/wolfssl.c' || echo '$(srcdir)/'`vtls/wolfssl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-wolfssl.Tpo vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/wolfssl.c' object='vtls/libcurl_la-wolfssl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-wolfssl.lo `test -f 'vtls/wolfssl.c' || echo '$(srcdir)/'`vtls/wolfssl.c
+
+vtls/libcurl_la-x509asn1.lo: vtls/x509asn1.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-x509asn1.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-x509asn1.Tpo -c -o vtls/libcurl_la-x509asn1.lo `test -f 'vtls/x509asn1.c' || echo '$(srcdir)/'`vtls/x509asn1.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-x509asn1.Tpo vtls/$(DEPDIR)/libcurl_la-x509asn1.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/x509asn1.c' object='vtls/libcurl_la-x509asn1.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-x509asn1.lo `test -f 'vtls/x509asn1.c' || echo '$(srcdir)/'`vtls/x509asn1.c
+
+vquic/libcurl_la-msh3.lo: vquic/msh3.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vquic/libcurl_la-msh3.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-msh3.Tpo -c -o vquic/libcurl_la-msh3.lo `test -f 'vquic/msh3.c' || echo '$(srcdir)/'`vquic/msh3.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-msh3.Tpo vquic/$(DEPDIR)/libcurl_la-msh3.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/msh3.c' object='vquic/libcurl_la-msh3.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurl_la-msh3.lo `test -f 'vquic/msh3.c' || echo '$(srcdir)/'`vquic/msh3.c
+
+vquic/libcurl_la-ngtcp2.lo: vquic/ngtcp2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vquic/libcurl_la-ngtcp2.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-ngtcp2.Tpo -c -o vquic/libcurl_la-ngtcp2.lo `test -f 'vquic/ngtcp2.c' || echo '$(srcdir)/'`vquic/ngtcp2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-ngtcp2.Tpo vquic/$(DEPDIR)/libcurl_la-ngtcp2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/ngtcp2.c' object='vquic/libcurl_la-ngtcp2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurl_la-ngtcp2.lo `test -f 'vquic/ngtcp2.c' || echo '$(srcdir)/'`vquic/ngtcp2.c
+
+vquic/libcurl_la-quiche.lo: vquic/quiche.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vquic/libcurl_la-quiche.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-quiche.Tpo -c -o vquic/libcurl_la-quiche.lo `test -f 'vquic/quiche.c' || echo '$(srcdir)/'`vquic/quiche.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-quiche.Tpo vquic/$(DEPDIR)/libcurl_la-quiche.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/quiche.c' object='vquic/libcurl_la-quiche.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurl_la-quiche.lo `test -f 'vquic/quiche.c' || echo '$(srcdir)/'`vquic/quiche.c
+
+vquic/libcurl_la-vquic.lo: vquic/vquic.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vquic/libcurl_la-vquic.lo -MD -MP -MF vquic/$(DEPDIR)/libcurl_la-vquic.Tpo -c -o vquic/libcurl_la-vquic.lo `test -f 'vquic/vquic.c' || echo '$(srcdir)/'`vquic/vquic.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurl_la-vquic.Tpo vquic/$(DEPDIR)/libcurl_la-vquic.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/vquic.c' object='vquic/libcurl_la-vquic.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurl_la-vquic.lo `test -f 'vquic/vquic.c' || echo '$(srcdir)/'`vquic/vquic.c
+
+vssh/libcurl_la-libssh.lo: vssh/libssh.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vssh/libcurl_la-libssh.lo -MD -MP -MF vssh/$(DEPDIR)/libcurl_la-libssh.Tpo -c -o vssh/libcurl_la-libssh.lo `test -f 'vssh/libssh.c' || echo '$(srcdir)/'`vssh/libssh.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vssh/$(DEPDIR)/libcurl_la-libssh.Tpo vssh/$(DEPDIR)/libcurl_la-libssh.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vssh/libssh.c' object='vssh/libcurl_la-libssh.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vssh/libcurl_la-libssh.lo `test -f 'vssh/libssh.c' || echo '$(srcdir)/'`vssh/libssh.c
+
+vssh/libcurl_la-libssh2.lo: vssh/libssh2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vssh/libcurl_la-libssh2.lo -MD -MP -MF vssh/$(DEPDIR)/libcurl_la-libssh2.Tpo -c -o vssh/libcurl_la-libssh2.lo `test -f 'vssh/libssh2.c' || echo '$(srcdir)/'`vssh/libssh2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vssh/$(DEPDIR)/libcurl_la-libssh2.Tpo vssh/$(DEPDIR)/libcurl_la-libssh2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vssh/libssh2.c' object='vssh/libcurl_la-libssh2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vssh/libcurl_la-libssh2.lo `test -f 'vssh/libssh2.c' || echo '$(srcdir)/'`vssh/libssh2.c
+
+vssh/libcurl_la-wolfssh.lo: vssh/wolfssh.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vssh/libcurl_la-wolfssh.lo -MD -MP -MF vssh/$(DEPDIR)/libcurl_la-wolfssh.Tpo -c -o vssh/libcurl_la-wolfssh.lo `test -f 'vssh/wolfssh.c' || echo '$(srcdir)/'`vssh/wolfssh.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vssh/$(DEPDIR)/libcurl_la-wolfssh.Tpo vssh/$(DEPDIR)/libcurl_la-wolfssh.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vssh/wolfssh.c' object='vssh/libcurl_la-wolfssh.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vssh/libcurl_la-wolfssh.lo `test -f 'vssh/wolfssh.c' || echo '$(srcdir)/'`vssh/wolfssh.c
+
+libcurlu_la-altsvc.lo: altsvc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-altsvc.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-altsvc.Tpo -c -o libcurlu_la-altsvc.lo `test -f 'altsvc.c' || echo '$(srcdir)/'`altsvc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-altsvc.Tpo $(DEPDIR)/libcurlu_la-altsvc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='altsvc.c' object='libcurlu_la-altsvc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-altsvc.lo `test -f 'altsvc.c' || echo '$(srcdir)/'`altsvc.c
+
+libcurlu_la-amigaos.lo: amigaos.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-amigaos.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-amigaos.Tpo -c -o libcurlu_la-amigaos.lo `test -f 'amigaos.c' || echo '$(srcdir)/'`amigaos.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-amigaos.Tpo $(DEPDIR)/libcurlu_la-amigaos.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='amigaos.c' object='libcurlu_la-amigaos.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-amigaos.lo `test -f 'amigaos.c' || echo '$(srcdir)/'`amigaos.c
+
+libcurlu_la-asyn-ares.lo: asyn-ares.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-asyn-ares.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-asyn-ares.Tpo -c -o libcurlu_la-asyn-ares.lo `test -f 'asyn-ares.c' || echo '$(srcdir)/'`asyn-ares.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-asyn-ares.Tpo $(DEPDIR)/libcurlu_la-asyn-ares.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asyn-ares.c' object='libcurlu_la-asyn-ares.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-asyn-ares.lo `test -f 'asyn-ares.c' || echo '$(srcdir)/'`asyn-ares.c
+
+libcurlu_la-asyn-thread.lo: asyn-thread.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-asyn-thread.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-asyn-thread.Tpo -c -o libcurlu_la-asyn-thread.lo `test -f 'asyn-thread.c' || echo '$(srcdir)/'`asyn-thread.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-asyn-thread.Tpo $(DEPDIR)/libcurlu_la-asyn-thread.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asyn-thread.c' object='libcurlu_la-asyn-thread.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-asyn-thread.lo `test -f 'asyn-thread.c' || echo '$(srcdir)/'`asyn-thread.c
+
+libcurlu_la-base64.lo: base64.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-base64.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-base64.Tpo -c -o libcurlu_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-base64.Tpo $(DEPDIR)/libcurlu_la-base64.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='base64.c' object='libcurlu_la-base64.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
+
+libcurlu_la-bufref.lo: bufref.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-bufref.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-bufref.Tpo -c -o libcurlu_la-bufref.lo `test -f 'bufref.c' || echo '$(srcdir)/'`bufref.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-bufref.Tpo $(DEPDIR)/libcurlu_la-bufref.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bufref.c' object='libcurlu_la-bufref.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-bufref.lo `test -f 'bufref.c' || echo '$(srcdir)/'`bufref.c
+
+libcurlu_la-c-hyper.lo: c-hyper.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-c-hyper.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-c-hyper.Tpo -c -o libcurlu_la-c-hyper.lo `test -f 'c-hyper.c' || echo '$(srcdir)/'`c-hyper.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-c-hyper.Tpo $(DEPDIR)/libcurlu_la-c-hyper.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='c-hyper.c' object='libcurlu_la-c-hyper.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-c-hyper.lo `test -f 'c-hyper.c' || echo '$(srcdir)/'`c-hyper.c
+
+libcurlu_la-cfilters.lo: cfilters.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-cfilters.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-cfilters.Tpo -c -o libcurlu_la-cfilters.lo `test -f 'cfilters.c' || echo '$(srcdir)/'`cfilters.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-cfilters.Tpo $(DEPDIR)/libcurlu_la-cfilters.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cfilters.c' object='libcurlu_la-cfilters.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-cfilters.lo `test -f 'cfilters.c' || echo '$(srcdir)/'`cfilters.c
+
+libcurlu_la-conncache.lo: conncache.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-conncache.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-conncache.Tpo -c -o libcurlu_la-conncache.lo `test -f 'conncache.c' || echo '$(srcdir)/'`conncache.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-conncache.Tpo $(DEPDIR)/libcurlu_la-conncache.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='conncache.c' object='libcurlu_la-conncache.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-conncache.lo `test -f 'conncache.c' || echo '$(srcdir)/'`conncache.c
+
+libcurlu_la-connect.lo: connect.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-connect.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-connect.Tpo -c -o libcurlu_la-connect.lo `test -f 'connect.c' || echo '$(srcdir)/'`connect.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-connect.Tpo $(DEPDIR)/libcurlu_la-connect.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='connect.c' object='libcurlu_la-connect.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-connect.lo `test -f 'connect.c' || echo '$(srcdir)/'`connect.c
+
+libcurlu_la-content_encoding.lo: content_encoding.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-content_encoding.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-content_encoding.Tpo -c -o libcurlu_la-content_encoding.lo `test -f 'content_encoding.c' || echo '$(srcdir)/'`content_encoding.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-content_encoding.Tpo $(DEPDIR)/libcurlu_la-content_encoding.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='content_encoding.c' object='libcurlu_la-content_encoding.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-content_encoding.lo `test -f 'content_encoding.c' || echo '$(srcdir)/'`content_encoding.c
+
+libcurlu_la-cookie.lo: cookie.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-cookie.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-cookie.Tpo -c -o libcurlu_la-cookie.lo `test -f 'cookie.c' || echo '$(srcdir)/'`cookie.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-cookie.Tpo $(DEPDIR)/libcurlu_la-cookie.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cookie.c' object='libcurlu_la-cookie.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-cookie.lo `test -f 'cookie.c' || echo '$(srcdir)/'`cookie.c
+
+libcurlu_la-curl_addrinfo.lo: curl_addrinfo.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_addrinfo.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_addrinfo.Tpo -c -o libcurlu_la-curl_addrinfo.lo `test -f 'curl_addrinfo.c' || echo '$(srcdir)/'`curl_addrinfo.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_addrinfo.Tpo $(DEPDIR)/libcurlu_la-curl_addrinfo.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_addrinfo.c' object='libcurlu_la-curl_addrinfo.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_addrinfo.lo `test -f 'curl_addrinfo.c' || echo '$(srcdir)/'`curl_addrinfo.c
+
+libcurlu_la-curl_des.lo: curl_des.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_des.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_des.Tpo -c -o libcurlu_la-curl_des.lo `test -f 'curl_des.c' || echo '$(srcdir)/'`curl_des.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_des.Tpo $(DEPDIR)/libcurlu_la-curl_des.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_des.c' object='libcurlu_la-curl_des.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_des.lo `test -f 'curl_des.c' || echo '$(srcdir)/'`curl_des.c
+
+libcurlu_la-curl_endian.lo: curl_endian.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_endian.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_endian.Tpo -c -o libcurlu_la-curl_endian.lo `test -f 'curl_endian.c' || echo '$(srcdir)/'`curl_endian.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_endian.Tpo $(DEPDIR)/libcurlu_la-curl_endian.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_endian.c' object='libcurlu_la-curl_endian.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_endian.lo `test -f 'curl_endian.c' || echo '$(srcdir)/'`curl_endian.c
+
+libcurlu_la-curl_fnmatch.lo: curl_fnmatch.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_fnmatch.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_fnmatch.Tpo -c -o libcurlu_la-curl_fnmatch.lo `test -f 'curl_fnmatch.c' || echo '$(srcdir)/'`curl_fnmatch.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_fnmatch.Tpo $(DEPDIR)/libcurlu_la-curl_fnmatch.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_fnmatch.c' object='libcurlu_la-curl_fnmatch.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_fnmatch.lo `test -f 'curl_fnmatch.c' || echo '$(srcdir)/'`curl_fnmatch.c
+
+libcurlu_la-curl_get_line.lo: curl_get_line.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_get_line.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_get_line.Tpo -c -o libcurlu_la-curl_get_line.lo `test -f 'curl_get_line.c' || echo '$(srcdir)/'`curl_get_line.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_get_line.Tpo $(DEPDIR)/libcurlu_la-curl_get_line.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_get_line.c' object='libcurlu_la-curl_get_line.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_get_line.lo `test -f 'curl_get_line.c' || echo '$(srcdir)/'`curl_get_line.c
+
+libcurlu_la-curl_gethostname.lo: curl_gethostname.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_gethostname.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_gethostname.Tpo -c -o libcurlu_la-curl_gethostname.lo `test -f 'curl_gethostname.c' || echo '$(srcdir)/'`curl_gethostname.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_gethostname.Tpo $(DEPDIR)/libcurlu_la-curl_gethostname.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_gethostname.c' object='libcurlu_la-curl_gethostname.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_gethostname.lo `test -f 'curl_gethostname.c' || echo '$(srcdir)/'`curl_gethostname.c
+
+libcurlu_la-curl_gssapi.lo: curl_gssapi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_gssapi.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_gssapi.Tpo -c -o libcurlu_la-curl_gssapi.lo `test -f 'curl_gssapi.c' || echo '$(srcdir)/'`curl_gssapi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_gssapi.Tpo $(DEPDIR)/libcurlu_la-curl_gssapi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_gssapi.c' object='libcurlu_la-curl_gssapi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_gssapi.lo `test -f 'curl_gssapi.c' || echo '$(srcdir)/'`curl_gssapi.c
+
+libcurlu_la-curl_memrchr.lo: curl_memrchr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_memrchr.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_memrchr.Tpo -c -o libcurlu_la-curl_memrchr.lo `test -f 'curl_memrchr.c' || echo '$(srcdir)/'`curl_memrchr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_memrchr.Tpo $(DEPDIR)/libcurlu_la-curl_memrchr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_memrchr.c' object='libcurlu_la-curl_memrchr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_memrchr.lo `test -f 'curl_memrchr.c' || echo '$(srcdir)/'`curl_memrchr.c
+
+libcurlu_la-curl_multibyte.lo: curl_multibyte.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_multibyte.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_multibyte.Tpo -c -o libcurlu_la-curl_multibyte.lo `test -f 'curl_multibyte.c' || echo '$(srcdir)/'`curl_multibyte.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_multibyte.Tpo $(DEPDIR)/libcurlu_la-curl_multibyte.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_multibyte.c' object='libcurlu_la-curl_multibyte.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_multibyte.lo `test -f 'curl_multibyte.c' || echo '$(srcdir)/'`curl_multibyte.c
+
+libcurlu_la-curl_ntlm_core.lo: curl_ntlm_core.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_ntlm_core.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_ntlm_core.Tpo -c -o libcurlu_la-curl_ntlm_core.lo `test -f 'curl_ntlm_core.c' || echo '$(srcdir)/'`curl_ntlm_core.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_ntlm_core.Tpo $(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_ntlm_core.c' object='libcurlu_la-curl_ntlm_core.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_ntlm_core.lo `test -f 'curl_ntlm_core.c' || echo '$(srcdir)/'`curl_ntlm_core.c
+
+libcurlu_la-curl_ntlm_wb.lo: curl_ntlm_wb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_ntlm_wb.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_ntlm_wb.Tpo -c -o libcurlu_la-curl_ntlm_wb.lo `test -f 'curl_ntlm_wb.c' || echo '$(srcdir)/'`curl_ntlm_wb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_ntlm_wb.Tpo $(DEPDIR)/libcurlu_la-curl_ntlm_wb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_ntlm_wb.c' object='libcurlu_la-curl_ntlm_wb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_ntlm_wb.lo `test -f 'curl_ntlm_wb.c' || echo '$(srcdir)/'`curl_ntlm_wb.c
+
+libcurlu_la-curl_path.lo: curl_path.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_path.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_path.Tpo -c -o libcurlu_la-curl_path.lo `test -f 'curl_path.c' || echo '$(srcdir)/'`curl_path.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_path.Tpo $(DEPDIR)/libcurlu_la-curl_path.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_path.c' object='libcurlu_la-curl_path.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_path.lo `test -f 'curl_path.c' || echo '$(srcdir)/'`curl_path.c
+
+libcurlu_la-curl_range.lo: curl_range.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_range.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_range.Tpo -c -o libcurlu_la-curl_range.lo `test -f 'curl_range.c' || echo '$(srcdir)/'`curl_range.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_range.Tpo $(DEPDIR)/libcurlu_la-curl_range.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_range.c' object='libcurlu_la-curl_range.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_range.lo `test -f 'curl_range.c' || echo '$(srcdir)/'`curl_range.c
+
+libcurlu_la-curl_rtmp.lo: curl_rtmp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_rtmp.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_rtmp.Tpo -c -o libcurlu_la-curl_rtmp.lo `test -f 'curl_rtmp.c' || echo '$(srcdir)/'`curl_rtmp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_rtmp.Tpo $(DEPDIR)/libcurlu_la-curl_rtmp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_rtmp.c' object='libcurlu_la-curl_rtmp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_rtmp.lo `test -f 'curl_rtmp.c' || echo '$(srcdir)/'`curl_rtmp.c
+
+libcurlu_la-curl_sasl.lo: curl_sasl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_sasl.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_sasl.Tpo -c -o libcurlu_la-curl_sasl.lo `test -f 'curl_sasl.c' || echo '$(srcdir)/'`curl_sasl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_sasl.Tpo $(DEPDIR)/libcurlu_la-curl_sasl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_sasl.c' object='libcurlu_la-curl_sasl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_sasl.lo `test -f 'curl_sasl.c' || echo '$(srcdir)/'`curl_sasl.c
+
+libcurlu_la-curl_sspi.lo: curl_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_sspi.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_sspi.Tpo -c -o libcurlu_la-curl_sspi.lo `test -f 'curl_sspi.c' || echo '$(srcdir)/'`curl_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_sspi.Tpo $(DEPDIR)/libcurlu_la-curl_sspi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_sspi.c' object='libcurlu_la-curl_sspi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_sspi.lo `test -f 'curl_sspi.c' || echo '$(srcdir)/'`curl_sspi.c
+
+libcurlu_la-curl_threads.lo: curl_threads.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_threads.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_threads.Tpo -c -o libcurlu_la-curl_threads.lo `test -f 'curl_threads.c' || echo '$(srcdir)/'`curl_threads.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_threads.Tpo $(DEPDIR)/libcurlu_la-curl_threads.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_threads.c' object='libcurlu_la-curl_threads.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_threads.lo `test -f 'curl_threads.c' || echo '$(srcdir)/'`curl_threads.c
+
+libcurlu_la-dict.lo: dict.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-dict.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-dict.Tpo -c -o libcurlu_la-dict.lo `test -f 'dict.c' || echo '$(srcdir)/'`dict.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-dict.Tpo $(DEPDIR)/libcurlu_la-dict.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dict.c' object='libcurlu_la-dict.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-dict.lo `test -f 'dict.c' || echo '$(srcdir)/'`dict.c
+
+libcurlu_la-doh.lo: doh.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-doh.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-doh.Tpo -c -o libcurlu_la-doh.lo `test -f 'doh.c' || echo '$(srcdir)/'`doh.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-doh.Tpo $(DEPDIR)/libcurlu_la-doh.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='doh.c' object='libcurlu_la-doh.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-doh.lo `test -f 'doh.c' || echo '$(srcdir)/'`doh.c
+
+libcurlu_la-dynbuf.lo: dynbuf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-dynbuf.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-dynbuf.Tpo -c -o libcurlu_la-dynbuf.lo `test -f 'dynbuf.c' || echo '$(srcdir)/'`dynbuf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-dynbuf.Tpo $(DEPDIR)/libcurlu_la-dynbuf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dynbuf.c' object='libcurlu_la-dynbuf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-dynbuf.lo `test -f 'dynbuf.c' || echo '$(srcdir)/'`dynbuf.c
+
+libcurlu_la-easy.lo: easy.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-easy.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-easy.Tpo -c -o libcurlu_la-easy.lo `test -f 'easy.c' || echo '$(srcdir)/'`easy.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-easy.Tpo $(DEPDIR)/libcurlu_la-easy.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='easy.c' object='libcurlu_la-easy.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-easy.lo `test -f 'easy.c' || echo '$(srcdir)/'`easy.c
+
+libcurlu_la-easygetopt.lo: easygetopt.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-easygetopt.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-easygetopt.Tpo -c -o libcurlu_la-easygetopt.lo `test -f 'easygetopt.c' || echo '$(srcdir)/'`easygetopt.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-easygetopt.Tpo $(DEPDIR)/libcurlu_la-easygetopt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='easygetopt.c' object='libcurlu_la-easygetopt.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-easygetopt.lo `test -f 'easygetopt.c' || echo '$(srcdir)/'`easygetopt.c
+
+libcurlu_la-easyoptions.lo: easyoptions.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-easyoptions.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-easyoptions.Tpo -c -o libcurlu_la-easyoptions.lo `test -f 'easyoptions.c' || echo '$(srcdir)/'`easyoptions.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-easyoptions.Tpo $(DEPDIR)/libcurlu_la-easyoptions.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='easyoptions.c' object='libcurlu_la-easyoptions.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-easyoptions.lo `test -f 'easyoptions.c' || echo '$(srcdir)/'`easyoptions.c
+
+libcurlu_la-escape.lo: escape.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-escape.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-escape.Tpo -c -o libcurlu_la-escape.lo `test -f 'escape.c' || echo '$(srcdir)/'`escape.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-escape.Tpo $(DEPDIR)/libcurlu_la-escape.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='escape.c' object='libcurlu_la-escape.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-escape.lo `test -f 'escape.c' || echo '$(srcdir)/'`escape.c
+
+libcurlu_la-file.lo: file.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-file.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-file.Tpo -c -o libcurlu_la-file.lo `test -f 'file.c' || echo '$(srcdir)/'`file.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-file.Tpo $(DEPDIR)/libcurlu_la-file.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file.c' object='libcurlu_la-file.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-file.lo `test -f 'file.c' || echo '$(srcdir)/'`file.c
+
+libcurlu_la-fileinfo.lo: fileinfo.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-fileinfo.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-fileinfo.Tpo -c -o libcurlu_la-fileinfo.lo `test -f 'fileinfo.c' || echo '$(srcdir)/'`fileinfo.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-fileinfo.Tpo $(DEPDIR)/libcurlu_la-fileinfo.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fileinfo.c' object='libcurlu_la-fileinfo.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-fileinfo.lo `test -f 'fileinfo.c' || echo '$(srcdir)/'`fileinfo.c
+
+libcurlu_la-fopen.lo: fopen.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-fopen.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-fopen.Tpo -c -o libcurlu_la-fopen.lo `test -f 'fopen.c' || echo '$(srcdir)/'`fopen.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-fopen.Tpo $(DEPDIR)/libcurlu_la-fopen.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fopen.c' object='libcurlu_la-fopen.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-fopen.lo `test -f 'fopen.c' || echo '$(srcdir)/'`fopen.c
+
+libcurlu_la-formdata.lo: formdata.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-formdata.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-formdata.Tpo -c -o libcurlu_la-formdata.lo `test -f 'formdata.c' || echo '$(srcdir)/'`formdata.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-formdata.Tpo $(DEPDIR)/libcurlu_la-formdata.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='formdata.c' object='libcurlu_la-formdata.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-formdata.lo `test -f 'formdata.c' || echo '$(srcdir)/'`formdata.c
+
+libcurlu_la-ftp.lo: ftp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-ftp.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-ftp.Tpo -c -o libcurlu_la-ftp.lo `test -f 'ftp.c' || echo '$(srcdir)/'`ftp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-ftp.Tpo $(DEPDIR)/libcurlu_la-ftp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ftp.c' object='libcurlu_la-ftp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-ftp.lo `test -f 'ftp.c' || echo '$(srcdir)/'`ftp.c
+
+libcurlu_la-ftplistparser.lo: ftplistparser.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-ftplistparser.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-ftplistparser.Tpo -c -o libcurlu_la-ftplistparser.lo `test -f 'ftplistparser.c' || echo '$(srcdir)/'`ftplistparser.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-ftplistparser.Tpo $(DEPDIR)/libcurlu_la-ftplistparser.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ftplistparser.c' object='libcurlu_la-ftplistparser.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-ftplistparser.lo `test -f 'ftplistparser.c' || echo '$(srcdir)/'`ftplistparser.c
+
+libcurlu_la-getenv.lo: getenv.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-getenv.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-getenv.Tpo -c -o libcurlu_la-getenv.lo `test -f 'getenv.c' || echo '$(srcdir)/'`getenv.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-getenv.Tpo $(DEPDIR)/libcurlu_la-getenv.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getenv.c' object='libcurlu_la-getenv.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-getenv.lo `test -f 'getenv.c' || echo '$(srcdir)/'`getenv.c
+
+libcurlu_la-getinfo.lo: getinfo.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-getinfo.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-getinfo.Tpo -c -o libcurlu_la-getinfo.lo `test -f 'getinfo.c' || echo '$(srcdir)/'`getinfo.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-getinfo.Tpo $(DEPDIR)/libcurlu_la-getinfo.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getinfo.c' object='libcurlu_la-getinfo.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-getinfo.lo `test -f 'getinfo.c' || echo '$(srcdir)/'`getinfo.c
+
+libcurlu_la-gopher.lo: gopher.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-gopher.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-gopher.Tpo -c -o libcurlu_la-gopher.lo `test -f 'gopher.c' || echo '$(srcdir)/'`gopher.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-gopher.Tpo $(DEPDIR)/libcurlu_la-gopher.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gopher.c' object='libcurlu_la-gopher.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-gopher.lo `test -f 'gopher.c' || echo '$(srcdir)/'`gopher.c
+
+libcurlu_la-h2h3.lo: h2h3.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-h2h3.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-h2h3.Tpo -c -o libcurlu_la-h2h3.lo `test -f 'h2h3.c' || echo '$(srcdir)/'`h2h3.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-h2h3.Tpo $(DEPDIR)/libcurlu_la-h2h3.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='h2h3.c' object='libcurlu_la-h2h3.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-h2h3.lo `test -f 'h2h3.c' || echo '$(srcdir)/'`h2h3.c
+
+libcurlu_la-hash.lo: hash.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hash.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hash.Tpo -c -o libcurlu_la-hash.lo `test -f 'hash.c' || echo '$(srcdir)/'`hash.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hash.Tpo $(DEPDIR)/libcurlu_la-hash.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hash.c' object='libcurlu_la-hash.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hash.lo `test -f 'hash.c' || echo '$(srcdir)/'`hash.c
+
+libcurlu_la-headers.lo: headers.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-headers.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-headers.Tpo -c -o libcurlu_la-headers.lo `test -f 'headers.c' || echo '$(srcdir)/'`headers.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-headers.Tpo $(DEPDIR)/libcurlu_la-headers.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='headers.c' object='libcurlu_la-headers.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-headers.lo `test -f 'headers.c' || echo '$(srcdir)/'`headers.c
+
+libcurlu_la-hmac.lo: hmac.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hmac.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hmac.Tpo -c -o libcurlu_la-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hmac.Tpo $(DEPDIR)/libcurlu_la-hmac.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hmac.c' object='libcurlu_la-hmac.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c
+
+libcurlu_la-hostasyn.lo: hostasyn.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostasyn.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostasyn.Tpo -c -o libcurlu_la-hostasyn.lo `test -f 'hostasyn.c' || echo '$(srcdir)/'`hostasyn.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostasyn.Tpo $(DEPDIR)/libcurlu_la-hostasyn.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostasyn.c' object='libcurlu_la-hostasyn.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hostasyn.lo `test -f 'hostasyn.c' || echo '$(srcdir)/'`hostasyn.c
+
+libcurlu_la-hostip.lo: hostip.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostip.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostip.Tpo -c -o libcurlu_la-hostip.lo `test -f 'hostip.c' || echo '$(srcdir)/'`hostip.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostip.Tpo $(DEPDIR)/libcurlu_la-hostip.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostip.c' object='libcurlu_la-hostip.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hostip.lo `test -f 'hostip.c' || echo '$(srcdir)/'`hostip.c
+
+libcurlu_la-hostip4.lo: hostip4.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostip4.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostip4.Tpo -c -o libcurlu_la-hostip4.lo `test -f 'hostip4.c' || echo '$(srcdir)/'`hostip4.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostip4.Tpo $(DEPDIR)/libcurlu_la-hostip4.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostip4.c' object='libcurlu_la-hostip4.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hostip4.lo `test -f 'hostip4.c' || echo '$(srcdir)/'`hostip4.c
+
+libcurlu_la-hostip6.lo: hostip6.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostip6.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostip6.Tpo -c -o libcurlu_la-hostip6.lo `test -f 'hostip6.c' || echo '$(srcdir)/'`hostip6.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostip6.Tpo $(DEPDIR)/libcurlu_la-hostip6.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostip6.c' object='libcurlu_la-hostip6.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hostip6.lo `test -f 'hostip6.c' || echo '$(srcdir)/'`hostip6.c
+
+libcurlu_la-hostsyn.lo: hostsyn.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostsyn.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostsyn.Tpo -c -o libcurlu_la-hostsyn.lo `test -f 'hostsyn.c' || echo '$(srcdir)/'`hostsyn.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostsyn.Tpo $(DEPDIR)/libcurlu_la-hostsyn.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostsyn.c' object='libcurlu_la-hostsyn.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hostsyn.lo `test -f 'hostsyn.c' || echo '$(srcdir)/'`hostsyn.c
+
+libcurlu_la-hsts.lo: hsts.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hsts.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hsts.Tpo -c -o libcurlu_la-hsts.lo `test -f 'hsts.c' || echo '$(srcdir)/'`hsts.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hsts.Tpo $(DEPDIR)/libcurlu_la-hsts.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hsts.c' object='libcurlu_la-hsts.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hsts.lo `test -f 'hsts.c' || echo '$(srcdir)/'`hsts.c
+
+libcurlu_la-http.lo: http.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http.Tpo -c -o libcurlu_la-http.lo `test -f 'http.c' || echo '$(srcdir)/'`http.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http.Tpo $(DEPDIR)/libcurlu_la-http.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http.c' object='libcurlu_la-http.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http.lo `test -f 'http.c' || echo '$(srcdir)/'`http.c
+
+libcurlu_la-http2.lo: http2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http2.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http2.Tpo -c -o libcurlu_la-http2.lo `test -f 'http2.c' || echo '$(srcdir)/'`http2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http2.Tpo $(DEPDIR)/libcurlu_la-http2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http2.c' object='libcurlu_la-http2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http2.lo `test -f 'http2.c' || echo '$(srcdir)/'`http2.c
+
+libcurlu_la-http_chunks.lo: http_chunks.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http_chunks.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http_chunks.Tpo -c -o libcurlu_la-http_chunks.lo `test -f 'http_chunks.c' || echo '$(srcdir)/'`http_chunks.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http_chunks.Tpo $(DEPDIR)/libcurlu_la-http_chunks.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_chunks.c' object='libcurlu_la-http_chunks.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http_chunks.lo `test -f 'http_chunks.c' || echo '$(srcdir)/'`http_chunks.c
+
+libcurlu_la-http_digest.lo: http_digest.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http_digest.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http_digest.Tpo -c -o libcurlu_la-http_digest.lo `test -f 'http_digest.c' || echo '$(srcdir)/'`http_digest.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http_digest.Tpo $(DEPDIR)/libcurlu_la-http_digest.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_digest.c' object='libcurlu_la-http_digest.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http_digest.lo `test -f 'http_digest.c' || echo '$(srcdir)/'`http_digest.c
+
+libcurlu_la-http_negotiate.lo: http_negotiate.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http_negotiate.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http_negotiate.Tpo -c -o libcurlu_la-http_negotiate.lo `test -f 'http_negotiate.c' || echo '$(srcdir)/'`http_negotiate.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http_negotiate.Tpo $(DEPDIR)/libcurlu_la-http_negotiate.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_negotiate.c' object='libcurlu_la-http_negotiate.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http_negotiate.lo `test -f 'http_negotiate.c' || echo '$(srcdir)/'`http_negotiate.c
+
+libcurlu_la-http_ntlm.lo: http_ntlm.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http_ntlm.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http_ntlm.Tpo -c -o libcurlu_la-http_ntlm.lo `test -f 'http_ntlm.c' || echo '$(srcdir)/'`http_ntlm.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http_ntlm.Tpo $(DEPDIR)/libcurlu_la-http_ntlm.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_ntlm.c' object='libcurlu_la-http_ntlm.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http_ntlm.lo `test -f 'http_ntlm.c' || echo '$(srcdir)/'`http_ntlm.c
+
+libcurlu_la-http_proxy.lo: http_proxy.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http_proxy.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http_proxy.Tpo -c -o libcurlu_la-http_proxy.lo `test -f 'http_proxy.c' || echo '$(srcdir)/'`http_proxy.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http_proxy.Tpo $(DEPDIR)/libcurlu_la-http_proxy.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_proxy.c' object='libcurlu_la-http_proxy.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http_proxy.lo `test -f 'http_proxy.c' || echo '$(srcdir)/'`http_proxy.c
+
+libcurlu_la-http_aws_sigv4.lo: http_aws_sigv4.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-http_aws_sigv4.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-http_aws_sigv4.Tpo -c -o libcurlu_la-http_aws_sigv4.lo `test -f 'http_aws_sigv4.c' || echo '$(srcdir)/'`http_aws_sigv4.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-http_aws_sigv4.Tpo $(DEPDIR)/libcurlu_la-http_aws_sigv4.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_aws_sigv4.c' object='libcurlu_la-http_aws_sigv4.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-http_aws_sigv4.lo `test -f 'http_aws_sigv4.c' || echo '$(srcdir)/'`http_aws_sigv4.c
+
+libcurlu_la-idn.lo: idn.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-idn.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-idn.Tpo -c -o libcurlu_la-idn.lo `test -f 'idn.c' || echo '$(srcdir)/'`idn.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-idn.Tpo $(DEPDIR)/libcurlu_la-idn.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='idn.c' object='libcurlu_la-idn.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-idn.lo `test -f 'idn.c' || echo '$(srcdir)/'`idn.c
+
+libcurlu_la-if2ip.lo: if2ip.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-if2ip.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-if2ip.Tpo -c -o libcurlu_la-if2ip.lo `test -f 'if2ip.c' || echo '$(srcdir)/'`if2ip.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-if2ip.Tpo $(DEPDIR)/libcurlu_la-if2ip.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='if2ip.c' object='libcurlu_la-if2ip.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-if2ip.lo `test -f 'if2ip.c' || echo '$(srcdir)/'`if2ip.c
+
+libcurlu_la-imap.lo: imap.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-imap.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-imap.Tpo -c -o libcurlu_la-imap.lo `test -f 'imap.c' || echo '$(srcdir)/'`imap.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-imap.Tpo $(DEPDIR)/libcurlu_la-imap.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='imap.c' object='libcurlu_la-imap.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-imap.lo `test -f 'imap.c' || echo '$(srcdir)/'`imap.c
+
+libcurlu_la-inet_ntop.lo: inet_ntop.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-inet_ntop.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-inet_ntop.Tpo -c -o libcurlu_la-inet_ntop.lo `test -f 'inet_ntop.c' || echo '$(srcdir)/'`inet_ntop.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-inet_ntop.Tpo $(DEPDIR)/libcurlu_la-inet_ntop.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='inet_ntop.c' object='libcurlu_la-inet_ntop.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-inet_ntop.lo `test -f 'inet_ntop.c' || echo '$(srcdir)/'`inet_ntop.c
+
+libcurlu_la-inet_pton.lo: inet_pton.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-inet_pton.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-inet_pton.Tpo -c -o libcurlu_la-inet_pton.lo `test -f 'inet_pton.c' || echo '$(srcdir)/'`inet_pton.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-inet_pton.Tpo $(DEPDIR)/libcurlu_la-inet_pton.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='inet_pton.c' object='libcurlu_la-inet_pton.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-inet_pton.lo `test -f 'inet_pton.c' || echo '$(srcdir)/'`inet_pton.c
+
+libcurlu_la-krb5.lo: krb5.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-krb5.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-krb5.Tpo -c -o libcurlu_la-krb5.lo `test -f 'krb5.c' || echo '$(srcdir)/'`krb5.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-krb5.Tpo $(DEPDIR)/libcurlu_la-krb5.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='krb5.c' object='libcurlu_la-krb5.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-krb5.lo `test -f 'krb5.c' || echo '$(srcdir)/'`krb5.c
+
+libcurlu_la-ldap.lo: ldap.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-ldap.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-ldap.Tpo -c -o libcurlu_la-ldap.lo `test -f 'ldap.c' || echo '$(srcdir)/'`ldap.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-ldap.Tpo $(DEPDIR)/libcurlu_la-ldap.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ldap.c' object='libcurlu_la-ldap.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-ldap.lo `test -f 'ldap.c' || echo '$(srcdir)/'`ldap.c
+
+libcurlu_la-llist.lo: llist.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-llist.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-llist.Tpo -c -o libcurlu_la-llist.lo `test -f 'llist.c' || echo '$(srcdir)/'`llist.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-llist.Tpo $(DEPDIR)/libcurlu_la-llist.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='llist.c' object='libcurlu_la-llist.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-llist.lo `test -f 'llist.c' || echo '$(srcdir)/'`llist.c
+
+libcurlu_la-md4.lo: md4.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-md4.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-md4.Tpo -c -o libcurlu_la-md4.lo `test -f 'md4.c' || echo '$(srcdir)/'`md4.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-md4.Tpo $(DEPDIR)/libcurlu_la-md4.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='md4.c' object='libcurlu_la-md4.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-md4.lo `test -f 'md4.c' || echo '$(srcdir)/'`md4.c
+
+libcurlu_la-md5.lo: md5.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-md5.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-md5.Tpo -c -o libcurlu_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-md5.Tpo $(DEPDIR)/libcurlu_la-md5.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='md5.c' object='libcurlu_la-md5.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c
+
+libcurlu_la-memdebug.lo: memdebug.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-memdebug.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-memdebug.Tpo -c -o libcurlu_la-memdebug.lo `test -f 'memdebug.c' || echo '$(srcdir)/'`memdebug.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-memdebug.Tpo $(DEPDIR)/libcurlu_la-memdebug.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='memdebug.c' object='libcurlu_la-memdebug.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-memdebug.lo `test -f 'memdebug.c' || echo '$(srcdir)/'`memdebug.c
+
+libcurlu_la-mime.lo: mime.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-mime.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-mime.Tpo -c -o libcurlu_la-mime.lo `test -f 'mime.c' || echo '$(srcdir)/'`mime.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-mime.Tpo $(DEPDIR)/libcurlu_la-mime.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mime.c' object='libcurlu_la-mime.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-mime.lo `test -f 'mime.c' || echo '$(srcdir)/'`mime.c
+
+libcurlu_la-mprintf.lo: mprintf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-mprintf.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-mprintf.Tpo -c -o libcurlu_la-mprintf.lo `test -f 'mprintf.c' || echo '$(srcdir)/'`mprintf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-mprintf.Tpo $(DEPDIR)/libcurlu_la-mprintf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mprintf.c' object='libcurlu_la-mprintf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-mprintf.lo `test -f 'mprintf.c' || echo '$(srcdir)/'`mprintf.c
+
+libcurlu_la-mqtt.lo: mqtt.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-mqtt.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-mqtt.Tpo -c -o libcurlu_la-mqtt.lo `test -f 'mqtt.c' || echo '$(srcdir)/'`mqtt.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-mqtt.Tpo $(DEPDIR)/libcurlu_la-mqtt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mqtt.c' object='libcurlu_la-mqtt.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-mqtt.lo `test -f 'mqtt.c' || echo '$(srcdir)/'`mqtt.c
+
+libcurlu_la-multi.lo: multi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-multi.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-multi.Tpo -c -o libcurlu_la-multi.lo `test -f 'multi.c' || echo '$(srcdir)/'`multi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-multi.Tpo $(DEPDIR)/libcurlu_la-multi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='multi.c' object='libcurlu_la-multi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-multi.lo `test -f 'multi.c' || echo '$(srcdir)/'`multi.c
+
+libcurlu_la-netrc.lo: netrc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-netrc.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-netrc.Tpo -c -o libcurlu_la-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-netrc.Tpo $(DEPDIR)/libcurlu_la-netrc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netrc.c' object='libcurlu_la-netrc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c
+
+libcurlu_la-nonblock.lo: nonblock.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-nonblock.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-nonblock.Tpo -c -o libcurlu_la-nonblock.lo `test -f 'nonblock.c' || echo '$(srcdir)/'`nonblock.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-nonblock.Tpo $(DEPDIR)/libcurlu_la-nonblock.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nonblock.c' object='libcurlu_la-nonblock.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-nonblock.lo `test -f 'nonblock.c' || echo '$(srcdir)/'`nonblock.c
+
+libcurlu_la-noproxy.lo: noproxy.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-noproxy.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-noproxy.Tpo -c -o libcurlu_la-noproxy.lo `test -f 'noproxy.c' || echo '$(srcdir)/'`noproxy.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-noproxy.Tpo $(DEPDIR)/libcurlu_la-noproxy.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='noproxy.c' object='libcurlu_la-noproxy.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-noproxy.lo `test -f 'noproxy.c' || echo '$(srcdir)/'`noproxy.c
+
+libcurlu_la-openldap.lo: openldap.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-openldap.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-openldap.Tpo -c -o libcurlu_la-openldap.lo `test -f 'openldap.c' || echo '$(srcdir)/'`openldap.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-openldap.Tpo $(DEPDIR)/libcurlu_la-openldap.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openldap.c' object='libcurlu_la-openldap.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-openldap.lo `test -f 'openldap.c' || echo '$(srcdir)/'`openldap.c
+
+libcurlu_la-parsedate.lo: parsedate.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-parsedate.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-parsedate.Tpo -c -o libcurlu_la-parsedate.lo `test -f 'parsedate.c' || echo '$(srcdir)/'`parsedate.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-parsedate.Tpo $(DEPDIR)/libcurlu_la-parsedate.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='parsedate.c' object='libcurlu_la-parsedate.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-parsedate.lo `test -f 'parsedate.c' || echo '$(srcdir)/'`parsedate.c
+
+libcurlu_la-pingpong.lo: pingpong.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-pingpong.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-pingpong.Tpo -c -o libcurlu_la-pingpong.lo `test -f 'pingpong.c' || echo '$(srcdir)/'`pingpong.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-pingpong.Tpo $(DEPDIR)/libcurlu_la-pingpong.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pingpong.c' object='libcurlu_la-pingpong.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-pingpong.lo `test -f 'pingpong.c' || echo '$(srcdir)/'`pingpong.c
+
+libcurlu_la-pop3.lo: pop3.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-pop3.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-pop3.Tpo -c -o libcurlu_la-pop3.lo `test -f 'pop3.c' || echo '$(srcdir)/'`pop3.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-pop3.Tpo $(DEPDIR)/libcurlu_la-pop3.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pop3.c' object='libcurlu_la-pop3.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-pop3.lo `test -f 'pop3.c' || echo '$(srcdir)/'`pop3.c
+
+libcurlu_la-progress.lo: progress.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-progress.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-progress.Tpo -c -o libcurlu_la-progress.lo `test -f 'progress.c' || echo '$(srcdir)/'`progress.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-progress.Tpo $(DEPDIR)/libcurlu_la-progress.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='progress.c' object='libcurlu_la-progress.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-progress.lo `test -f 'progress.c' || echo '$(srcdir)/'`progress.c
+
+libcurlu_la-psl.lo: psl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-psl.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-psl.Tpo -c -o libcurlu_la-psl.lo `test -f 'psl.c' || echo '$(srcdir)/'`psl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-psl.Tpo $(DEPDIR)/libcurlu_la-psl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='psl.c' object='libcurlu_la-psl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-psl.lo `test -f 'psl.c' || echo '$(srcdir)/'`psl.c
+
+libcurlu_la-rand.lo: rand.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-rand.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-rand.Tpo -c -o libcurlu_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-rand.Tpo $(DEPDIR)/libcurlu_la-rand.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rand.c' object='libcurlu_la-rand.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c
+
+libcurlu_la-rename.lo: rename.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-rename.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-rename.Tpo -c -o libcurlu_la-rename.lo `test -f 'rename.c' || echo '$(srcdir)/'`rename.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-rename.Tpo $(DEPDIR)/libcurlu_la-rename.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rename.c' object='libcurlu_la-rename.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-rename.lo `test -f 'rename.c' || echo '$(srcdir)/'`rename.c
+
+libcurlu_la-rtsp.lo: rtsp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-rtsp.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-rtsp.Tpo -c -o libcurlu_la-rtsp.lo `test -f 'rtsp.c' || echo '$(srcdir)/'`rtsp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-rtsp.Tpo $(DEPDIR)/libcurlu_la-rtsp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rtsp.c' object='libcurlu_la-rtsp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-rtsp.lo `test -f 'rtsp.c' || echo '$(srcdir)/'`rtsp.c
+
+libcurlu_la-select.lo: select.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-select.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-select.Tpo -c -o libcurlu_la-select.lo `test -f 'select.c' || echo '$(srcdir)/'`select.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-select.Tpo $(DEPDIR)/libcurlu_la-select.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='select.c' object='libcurlu_la-select.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-select.lo `test -f 'select.c' || echo '$(srcdir)/'`select.c
+
+libcurlu_la-sendf.lo: sendf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-sendf.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-sendf.Tpo -c -o libcurlu_la-sendf.lo `test -f 'sendf.c' || echo '$(srcdir)/'`sendf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-sendf.Tpo $(DEPDIR)/libcurlu_la-sendf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sendf.c' object='libcurlu_la-sendf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-sendf.lo `test -f 'sendf.c' || echo '$(srcdir)/'`sendf.c
+
+libcurlu_la-setopt.lo: setopt.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-setopt.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-setopt.Tpo -c -o libcurlu_la-setopt.lo `test -f 'setopt.c' || echo '$(srcdir)/'`setopt.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-setopt.Tpo $(DEPDIR)/libcurlu_la-setopt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='setopt.c' object='libcurlu_la-setopt.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-setopt.lo `test -f 'setopt.c' || echo '$(srcdir)/'`setopt.c
+
+libcurlu_la-sha256.lo: sha256.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-sha256.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-sha256.Tpo -c -o libcurlu_la-sha256.lo `test -f 'sha256.c' || echo '$(srcdir)/'`sha256.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-sha256.Tpo $(DEPDIR)/libcurlu_la-sha256.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sha256.c' object='libcurlu_la-sha256.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-sha256.lo `test -f 'sha256.c' || echo '$(srcdir)/'`sha256.c
+
+libcurlu_la-share.lo: share.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-share.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-share.Tpo -c -o libcurlu_la-share.lo `test -f 'share.c' || echo '$(srcdir)/'`share.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-share.Tpo $(DEPDIR)/libcurlu_la-share.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='share.c' object='libcurlu_la-share.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-share.lo `test -f 'share.c' || echo '$(srcdir)/'`share.c
+
+libcurlu_la-slist.lo: slist.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-slist.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-slist.Tpo -c -o libcurlu_la-slist.lo `test -f 'slist.c' || echo '$(srcdir)/'`slist.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-slist.Tpo $(DEPDIR)/libcurlu_la-slist.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='slist.c' object='libcurlu_la-slist.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-slist.lo `test -f 'slist.c' || echo '$(srcdir)/'`slist.c
+
+libcurlu_la-smb.lo: smb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-smb.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-smb.Tpo -c -o libcurlu_la-smb.lo `test -f 'smb.c' || echo '$(srcdir)/'`smb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-smb.Tpo $(DEPDIR)/libcurlu_la-smb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='smb.c' object='libcurlu_la-smb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-smb.lo `test -f 'smb.c' || echo '$(srcdir)/'`smb.c
+
+libcurlu_la-smtp.lo: smtp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-smtp.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-smtp.Tpo -c -o libcurlu_la-smtp.lo `test -f 'smtp.c' || echo '$(srcdir)/'`smtp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-smtp.Tpo $(DEPDIR)/libcurlu_la-smtp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='smtp.c' object='libcurlu_la-smtp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-smtp.lo `test -f 'smtp.c' || echo '$(srcdir)/'`smtp.c
+
+libcurlu_la-socketpair.lo: socketpair.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-socketpair.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-socketpair.Tpo -c -o libcurlu_la-socketpair.lo `test -f 'socketpair.c' || echo '$(srcdir)/'`socketpair.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-socketpair.Tpo $(DEPDIR)/libcurlu_la-socketpair.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socketpair.c' object='libcurlu_la-socketpair.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-socketpair.lo `test -f 'socketpair.c' || echo '$(srcdir)/'`socketpair.c
+
+libcurlu_la-socks.lo: socks.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-socks.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-socks.Tpo -c -o libcurlu_la-socks.lo `test -f 'socks.c' || echo '$(srcdir)/'`socks.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-socks.Tpo $(DEPDIR)/libcurlu_la-socks.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socks.c' object='libcurlu_la-socks.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-socks.lo `test -f 'socks.c' || echo '$(srcdir)/'`socks.c
+
+libcurlu_la-socks_gssapi.lo: socks_gssapi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-socks_gssapi.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-socks_gssapi.Tpo -c -o libcurlu_la-socks_gssapi.lo `test -f 'socks_gssapi.c' || echo '$(srcdir)/'`socks_gssapi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-socks_gssapi.Tpo $(DEPDIR)/libcurlu_la-socks_gssapi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socks_gssapi.c' object='libcurlu_la-socks_gssapi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-socks_gssapi.lo `test -f 'socks_gssapi.c' || echo '$(srcdir)/'`socks_gssapi.c
+
+libcurlu_la-socks_sspi.lo: socks_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-socks_sspi.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-socks_sspi.Tpo -c -o libcurlu_la-socks_sspi.lo `test -f 'socks_sspi.c' || echo '$(srcdir)/'`socks_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-socks_sspi.Tpo $(DEPDIR)/libcurlu_la-socks_sspi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socks_sspi.c' object='libcurlu_la-socks_sspi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-socks_sspi.lo `test -f 'socks_sspi.c' || echo '$(srcdir)/'`socks_sspi.c
+
+libcurlu_la-speedcheck.lo: speedcheck.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-speedcheck.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-speedcheck.Tpo -c -o libcurlu_la-speedcheck.lo `test -f 'speedcheck.c' || echo '$(srcdir)/'`speedcheck.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-speedcheck.Tpo $(DEPDIR)/libcurlu_la-speedcheck.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='speedcheck.c' object='libcurlu_la-speedcheck.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-speedcheck.lo `test -f 'speedcheck.c' || echo '$(srcdir)/'`speedcheck.c
+
+libcurlu_la-splay.lo: splay.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-splay.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-splay.Tpo -c -o libcurlu_la-splay.lo `test -f 'splay.c' || echo '$(srcdir)/'`splay.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-splay.Tpo $(DEPDIR)/libcurlu_la-splay.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='splay.c' object='libcurlu_la-splay.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-splay.lo `test -f 'splay.c' || echo '$(srcdir)/'`splay.c
+
+libcurlu_la-strcase.lo: strcase.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strcase.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strcase.Tpo -c -o libcurlu_la-strcase.lo `test -f 'strcase.c' || echo '$(srcdir)/'`strcase.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strcase.Tpo $(DEPDIR)/libcurlu_la-strcase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strcase.c' object='libcurlu_la-strcase.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strcase.lo `test -f 'strcase.c' || echo '$(srcdir)/'`strcase.c
+
+libcurlu_la-strdup.lo: strdup.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strdup.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strdup.Tpo -c -o libcurlu_la-strdup.lo `test -f 'strdup.c' || echo '$(srcdir)/'`strdup.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strdup.Tpo $(DEPDIR)/libcurlu_la-strdup.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strdup.c' object='libcurlu_la-strdup.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strdup.lo `test -f 'strdup.c' || echo '$(srcdir)/'`strdup.c
+
+libcurlu_la-strerror.lo: strerror.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strerror.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strerror.Tpo -c -o libcurlu_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strerror.Tpo $(DEPDIR)/libcurlu_la-strerror.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strerror.c' object='libcurlu_la-strerror.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c
+
+libcurlu_la-strtok.lo: strtok.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strtok.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strtok.Tpo -c -o libcurlu_la-strtok.lo `test -f 'strtok.c' || echo '$(srcdir)/'`strtok.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strtok.Tpo $(DEPDIR)/libcurlu_la-strtok.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strtok.c' object='libcurlu_la-strtok.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strtok.lo `test -f 'strtok.c' || echo '$(srcdir)/'`strtok.c
+
+libcurlu_la-strtoofft.lo: strtoofft.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strtoofft.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strtoofft.Tpo -c -o libcurlu_la-strtoofft.lo `test -f 'strtoofft.c' || echo '$(srcdir)/'`strtoofft.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strtoofft.Tpo $(DEPDIR)/libcurlu_la-strtoofft.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strtoofft.c' object='libcurlu_la-strtoofft.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strtoofft.lo `test -f 'strtoofft.c' || echo '$(srcdir)/'`strtoofft.c
+
+libcurlu_la-system_win32.lo: system_win32.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-system_win32.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-system_win32.Tpo -c -o libcurlu_la-system_win32.lo `test -f 'system_win32.c' || echo '$(srcdir)/'`system_win32.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-system_win32.Tpo $(DEPDIR)/libcurlu_la-system_win32.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='system_win32.c' object='libcurlu_la-system_win32.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-system_win32.lo `test -f 'system_win32.c' || echo '$(srcdir)/'`system_win32.c
+
+libcurlu_la-telnet.lo: telnet.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-telnet.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-telnet.Tpo -c -o libcurlu_la-telnet.lo `test -f 'telnet.c' || echo '$(srcdir)/'`telnet.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-telnet.Tpo $(DEPDIR)/libcurlu_la-telnet.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='telnet.c' object='libcurlu_la-telnet.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-telnet.lo `test -f 'telnet.c' || echo '$(srcdir)/'`telnet.c
+
+libcurlu_la-tftp.lo: tftp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-tftp.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-tftp.Tpo -c -o libcurlu_la-tftp.lo `test -f 'tftp.c' || echo '$(srcdir)/'`tftp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-tftp.Tpo $(DEPDIR)/libcurlu_la-tftp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tftp.c' object='libcurlu_la-tftp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-tftp.lo `test -f 'tftp.c' || echo '$(srcdir)/'`tftp.c
+
+libcurlu_la-timediff.lo: timediff.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-timediff.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-timediff.Tpo -c -o libcurlu_la-timediff.lo `test -f 'timediff.c' || echo '$(srcdir)/'`timediff.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-timediff.Tpo $(DEPDIR)/libcurlu_la-timediff.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timediff.c' object='libcurlu_la-timediff.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-timediff.lo `test -f 'timediff.c' || echo '$(srcdir)/'`timediff.c
+
+libcurlu_la-timeval.lo: timeval.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-timeval.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-timeval.Tpo -c -o libcurlu_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-timeval.Tpo $(DEPDIR)/libcurlu_la-timeval.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timeval.c' object='libcurlu_la-timeval.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c
+
+libcurlu_la-transfer.lo: transfer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-transfer.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-transfer.Tpo -c -o libcurlu_la-transfer.lo `test -f 'transfer.c' || echo '$(srcdir)/'`transfer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-transfer.Tpo $(DEPDIR)/libcurlu_la-transfer.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='transfer.c' object='libcurlu_la-transfer.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-transfer.lo `test -f 'transfer.c' || echo '$(srcdir)/'`transfer.c
+
+libcurlu_la-url.lo: url.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-url.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-url.Tpo -c -o libcurlu_la-url.lo `test -f 'url.c' || echo '$(srcdir)/'`url.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-url.Tpo $(DEPDIR)/libcurlu_la-url.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='url.c' object='libcurlu_la-url.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-url.lo `test -f 'url.c' || echo '$(srcdir)/'`url.c
+
+libcurlu_la-urlapi.lo: urlapi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-urlapi.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-urlapi.Tpo -c -o libcurlu_la-urlapi.lo `test -f 'urlapi.c' || echo '$(srcdir)/'`urlapi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-urlapi.Tpo $(DEPDIR)/libcurlu_la-urlapi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='urlapi.c' object='libcurlu_la-urlapi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-urlapi.lo `test -f 'urlapi.c' || echo '$(srcdir)/'`urlapi.c
+
+libcurlu_la-version.lo: version.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-version.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-version.Tpo -c -o libcurlu_la-version.lo `test -f 'version.c' || echo '$(srcdir)/'`version.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-version.Tpo $(DEPDIR)/libcurlu_la-version.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='version.c' object='libcurlu_la-version.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-version.lo `test -f 'version.c' || echo '$(srcdir)/'`version.c
+
+libcurlu_la-version_win32.lo: version_win32.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-version_win32.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-version_win32.Tpo -c -o libcurlu_la-version_win32.lo `test -f 'version_win32.c' || echo '$(srcdir)/'`version_win32.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-version_win32.Tpo $(DEPDIR)/libcurlu_la-version_win32.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='version_win32.c' object='libcurlu_la-version_win32.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-version_win32.lo `test -f 'version_win32.c' || echo '$(srcdir)/'`version_win32.c
+
+libcurlu_la-warnless.lo: warnless.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-warnless.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-warnless.Tpo -c -o libcurlu_la-warnless.lo `test -f 'warnless.c' || echo '$(srcdir)/'`warnless.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-warnless.Tpo $(DEPDIR)/libcurlu_la-warnless.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='warnless.c' object='libcurlu_la-warnless.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-warnless.lo `test -f 'warnless.c' || echo '$(srcdir)/'`warnless.c
+
+libcurlu_la-wildcard.lo: wildcard.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-wildcard.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-wildcard.Tpo -c -o libcurlu_la-wildcard.lo `test -f 'wildcard.c' || echo '$(srcdir)/'`wildcard.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-wildcard.Tpo $(DEPDIR)/libcurlu_la-wildcard.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wildcard.c' object='libcurlu_la-wildcard.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-wildcard.lo `test -f 'wildcard.c' || echo '$(srcdir)/'`wildcard.c
+
+libcurlu_la-ws.lo: ws.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-ws.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-ws.Tpo -c -o libcurlu_la-ws.lo `test -f 'ws.c' || echo '$(srcdir)/'`ws.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-ws.Tpo $(DEPDIR)/libcurlu_la-ws.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ws.c' object='libcurlu_la-ws.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-ws.lo `test -f 'ws.c' || echo '$(srcdir)/'`ws.c
+
+vauth/libcurlu_la-cleartext.lo: vauth/cleartext.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-cleartext.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-cleartext.Tpo -c -o vauth/libcurlu_la-cleartext.lo `test -f 'vauth/cleartext.c' || echo '$(srcdir)/'`vauth/cleartext.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-cleartext.Tpo vauth/$(DEPDIR)/libcurlu_la-cleartext.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/cleartext.c' object='vauth/libcurlu_la-cleartext.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-cleartext.lo `test -f 'vauth/cleartext.c' || echo '$(srcdir)/'`vauth/cleartext.c
+
+vauth/libcurlu_la-cram.lo: vauth/cram.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-cram.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-cram.Tpo -c -o vauth/libcurlu_la-cram.lo `test -f 'vauth/cram.c' || echo '$(srcdir)/'`vauth/cram.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-cram.Tpo vauth/$(DEPDIR)/libcurlu_la-cram.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/cram.c' object='vauth/libcurlu_la-cram.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-cram.lo `test -f 'vauth/cram.c' || echo '$(srcdir)/'`vauth/cram.c
+
+vauth/libcurlu_la-digest.lo: vauth/digest.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-digest.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-digest.Tpo -c -o vauth/libcurlu_la-digest.lo `test -f 'vauth/digest.c' || echo '$(srcdir)/'`vauth/digest.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-digest.Tpo vauth/$(DEPDIR)/libcurlu_la-digest.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/digest.c' object='vauth/libcurlu_la-digest.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-digest.lo `test -f 'vauth/digest.c' || echo '$(srcdir)/'`vauth/digest.c
+
+vauth/libcurlu_la-digest_sspi.lo: vauth/digest_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-digest_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Tpo -c -o vauth/libcurlu_la-digest_sspi.lo `test -f 'vauth/digest_sspi.c' || echo '$(srcdir)/'`vauth/digest_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Tpo vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/digest_sspi.c' object='vauth/libcurlu_la-digest_sspi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-digest_sspi.lo `test -f 'vauth/digest_sspi.c' || echo '$(srcdir)/'`vauth/digest_sspi.c
+
+vauth/libcurlu_la-gsasl.lo: vauth/gsasl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-gsasl.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-gsasl.Tpo -c -o vauth/libcurlu_la-gsasl.lo `test -f 'vauth/gsasl.c' || echo '$(srcdir)/'`vauth/gsasl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-gsasl.Tpo vauth/$(DEPDIR)/libcurlu_la-gsasl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/gsasl.c' object='vauth/libcurlu_la-gsasl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-gsasl.lo `test -f 'vauth/gsasl.c' || echo '$(srcdir)/'`vauth/gsasl.c
+
+vauth/libcurlu_la-krb5_gssapi.lo: vauth/krb5_gssapi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-krb5_gssapi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Tpo -c -o vauth/libcurlu_la-krb5_gssapi.lo `test -f 'vauth/krb5_gssapi.c' || echo '$(srcdir)/'`vauth/krb5_gssapi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Tpo vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/krb5_gssapi.c' object='vauth/libcurlu_la-krb5_gssapi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-krb5_gssapi.lo `test -f 'vauth/krb5_gssapi.c' || echo '$(srcdir)/'`vauth/krb5_gssapi.c
+
+vauth/libcurlu_la-krb5_sspi.lo: vauth/krb5_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-krb5_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Tpo -c -o vauth/libcurlu_la-krb5_sspi.lo `test -f 'vauth/krb5_sspi.c' || echo '$(srcdir)/'`vauth/krb5_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Tpo vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/krb5_sspi.c' object='vauth/libcurlu_la-krb5_sspi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-krb5_sspi.lo `test -f 'vauth/krb5_sspi.c' || echo '$(srcdir)/'`vauth/krb5_sspi.c
+
+vauth/libcurlu_la-ntlm.lo: vauth/ntlm.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-ntlm.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-ntlm.Tpo -c -o vauth/libcurlu_la-ntlm.lo `test -f 'vauth/ntlm.c' || echo '$(srcdir)/'`vauth/ntlm.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-ntlm.Tpo vauth/$(DEPDIR)/libcurlu_la-ntlm.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/ntlm.c' object='vauth/libcurlu_la-ntlm.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-ntlm.lo `test -f 'vauth/ntlm.c' || echo '$(srcdir)/'`vauth/ntlm.c
+
+vauth/libcurlu_la-ntlm_sspi.lo: vauth/ntlm_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-ntlm_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Tpo -c -o vauth/libcurlu_la-ntlm_sspi.lo `test -f 'vauth/ntlm_sspi.c' || echo '$(srcdir)/'`vauth/ntlm_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Tpo vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/ntlm_sspi.c' object='vauth/libcurlu_la-ntlm_sspi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-ntlm_sspi.lo `test -f 'vauth/ntlm_sspi.c' || echo '$(srcdir)/'`vauth/ntlm_sspi.c
+
+vauth/libcurlu_la-oauth2.lo: vauth/oauth2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-oauth2.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-oauth2.Tpo -c -o vauth/libcurlu_la-oauth2.lo `test -f 'vauth/oauth2.c' || echo '$(srcdir)/'`vauth/oauth2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-oauth2.Tpo vauth/$(DEPDIR)/libcurlu_la-oauth2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/oauth2.c' object='vauth/libcurlu_la-oauth2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-oauth2.lo `test -f 'vauth/oauth2.c' || echo '$(srcdir)/'`vauth/oauth2.c
+
+vauth/libcurlu_la-spnego_gssapi.lo: vauth/spnego_gssapi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-spnego_gssapi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Tpo -c -o vauth/libcurlu_la-spnego_gssapi.lo `test -f 'vauth/spnego_gssapi.c' || echo '$(srcdir)/'`vauth/spnego_gssapi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Tpo vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/spnego_gssapi.c' object='vauth/libcurlu_la-spnego_gssapi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-spnego_gssapi.lo `test -f 'vauth/spnego_gssapi.c' || echo '$(srcdir)/'`vauth/spnego_gssapi.c
+
+vauth/libcurlu_la-spnego_sspi.lo: vauth/spnego_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-spnego_sspi.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Tpo -c -o vauth/libcurlu_la-spnego_sspi.lo `test -f 'vauth/spnego_sspi.c' || echo '$(srcdir)/'`vauth/spnego_sspi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Tpo vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/spnego_sspi.c' object='vauth/libcurlu_la-spnego_sspi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-spnego_sspi.lo `test -f 'vauth/spnego_sspi.c' || echo '$(srcdir)/'`vauth/spnego_sspi.c
+
+vauth/libcurlu_la-vauth.lo: vauth/vauth.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-vauth.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-vauth.Tpo -c -o vauth/libcurlu_la-vauth.lo `test -f 'vauth/vauth.c' || echo '$(srcdir)/'`vauth/vauth.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-vauth.Tpo vauth/$(DEPDIR)/libcurlu_la-vauth.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vauth/vauth.c' object='vauth/libcurlu_la-vauth.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vauth/libcurlu_la-vauth.lo `test -f 'vauth/vauth.c' || echo '$(srcdir)/'`vauth/vauth.c
+
+vtls/libcurlu_la-bearssl.lo: vtls/bearssl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-bearssl.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-bearssl.Tpo -c -o vtls/libcurlu_la-bearssl.lo `test -f 'vtls/bearssl.c' || echo '$(srcdir)/'`vtls/bearssl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-bearssl.Tpo vtls/$(DEPDIR)/libcurlu_la-bearssl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/bearssl.c' object='vtls/libcurlu_la-bearssl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-bearssl.lo `test -f 'vtls/bearssl.c' || echo '$(srcdir)/'`vtls/bearssl.c
+
+vtls/libcurlu_la-gskit.lo: vtls/gskit.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-gskit.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-gskit.Tpo -c -o vtls/libcurlu_la-gskit.lo `test -f 'vtls/gskit.c' || echo '$(srcdir)/'`vtls/gskit.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-gskit.Tpo vtls/$(DEPDIR)/libcurlu_la-gskit.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/gskit.c' object='vtls/libcurlu_la-gskit.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-gskit.lo `test -f 'vtls/gskit.c' || echo '$(srcdir)/'`vtls/gskit.c
+
+vtls/libcurlu_la-gtls.lo: vtls/gtls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-gtls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-gtls.Tpo -c -o vtls/libcurlu_la-gtls.lo `test -f 'vtls/gtls.c' || echo '$(srcdir)/'`vtls/gtls.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-gtls.Tpo vtls/$(DEPDIR)/libcurlu_la-gtls.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/gtls.c' object='vtls/libcurlu_la-gtls.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-gtls.lo `test -f 'vtls/gtls.c' || echo '$(srcdir)/'`vtls/gtls.c
+
+vtls/libcurlu_la-hostcheck.lo: vtls/hostcheck.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-hostcheck.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-hostcheck.Tpo -c -o vtls/libcurlu_la-hostcheck.lo `test -f 'vtls/hostcheck.c' || echo '$(srcdir)/'`vtls/hostcheck.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-hostcheck.Tpo vtls/$(DEPDIR)/libcurlu_la-hostcheck.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/hostcheck.c' object='vtls/libcurlu_la-hostcheck.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-hostcheck.lo `test -f 'vtls/hostcheck.c' || echo '$(srcdir)/'`vtls/hostcheck.c
+
+vtls/libcurlu_la-keylog.lo: vtls/keylog.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-keylog.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-keylog.Tpo -c -o vtls/libcurlu_la-keylog.lo `test -f 'vtls/keylog.c' || echo '$(srcdir)/'`vtls/keylog.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-keylog.Tpo vtls/$(DEPDIR)/libcurlu_la-keylog.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/keylog.c' object='vtls/libcurlu_la-keylog.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-keylog.lo `test -f 'vtls/keylog.c' || echo '$(srcdir)/'`vtls/keylog.c
+
+vtls/libcurlu_la-mbedtls.lo: vtls/mbedtls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-mbedtls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-mbedtls.Tpo -c -o vtls/libcurlu_la-mbedtls.lo `test -f 'vtls/mbedtls.c' || echo '$(srcdir)/'`vtls/mbedtls.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-mbedtls.Tpo vtls/$(DEPDIR)/libcurlu_la-mbedtls.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/mbedtls.c' object='vtls/libcurlu_la-mbedtls.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-mbedtls.lo `test -f 'vtls/mbedtls.c' || echo '$(srcdir)/'`vtls/mbedtls.c
+
+vtls/libcurlu_la-mbedtls_threadlock.lo: vtls/mbedtls_threadlock.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-mbedtls_threadlock.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Tpo -c -o vtls/libcurlu_la-mbedtls_threadlock.lo `test -f 'vtls/mbedtls_threadlock.c' || echo '$(srcdir)/'`vtls/mbedtls_threadlock.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Tpo vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/mbedtls_threadlock.c' object='vtls/libcurlu_la-mbedtls_threadlock.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-mbedtls_threadlock.lo `test -f 'vtls/mbedtls_threadlock.c' || echo '$(srcdir)/'`vtls/mbedtls_threadlock.c
+
+vtls/libcurlu_la-nss.lo: vtls/nss.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-nss.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-nss.Tpo -c -o vtls/libcurlu_la-nss.lo `test -f 'vtls/nss.c' || echo '$(srcdir)/'`vtls/nss.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-nss.Tpo vtls/$(DEPDIR)/libcurlu_la-nss.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/nss.c' object='vtls/libcurlu_la-nss.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-nss.lo `test -f 'vtls/nss.c' || echo '$(srcdir)/'`vtls/nss.c
+
+vtls/libcurlu_la-openssl.lo: vtls/openssl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-openssl.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-openssl.Tpo -c -o vtls/libcurlu_la-openssl.lo `test -f 'vtls/openssl.c' || echo '$(srcdir)/'`vtls/openssl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-openssl.Tpo vtls/$(DEPDIR)/libcurlu_la-openssl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/openssl.c' object='vtls/libcurlu_la-openssl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-openssl.lo `test -f 'vtls/openssl.c' || echo '$(srcdir)/'`vtls/openssl.c
+
+vtls/libcurlu_la-rustls.lo: vtls/rustls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-rustls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-rustls.Tpo -c -o vtls/libcurlu_la-rustls.lo `test -f 'vtls/rustls.c' || echo '$(srcdir)/'`vtls/rustls.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-rustls.Tpo vtls/$(DEPDIR)/libcurlu_la-rustls.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/rustls.c' object='vtls/libcurlu_la-rustls.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-rustls.lo `test -f 'vtls/rustls.c' || echo '$(srcdir)/'`vtls/rustls.c
+
+vtls/libcurlu_la-schannel.lo: vtls/schannel.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-schannel.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-schannel.Tpo -c -o vtls/libcurlu_la-schannel.lo `test -f 'vtls/schannel.c' || echo '$(srcdir)/'`vtls/schannel.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-schannel.Tpo vtls/$(DEPDIR)/libcurlu_la-schannel.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/schannel.c' object='vtls/libcurlu_la-schannel.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-schannel.lo `test -f 'vtls/schannel.c' || echo '$(srcdir)/'`vtls/schannel.c
+
+vtls/libcurlu_la-schannel_verify.lo: vtls/schannel_verify.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-schannel_verify.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Tpo -c -o vtls/libcurlu_la-schannel_verify.lo `test -f 'vtls/schannel_verify.c' || echo '$(srcdir)/'`vtls/schannel_verify.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Tpo vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/schannel_verify.c' object='vtls/libcurlu_la-schannel_verify.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-schannel_verify.lo `test -f 'vtls/schannel_verify.c' || echo '$(srcdir)/'`vtls/schannel_verify.c
+
+vtls/libcurlu_la-sectransp.lo: vtls/sectransp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-sectransp.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-sectransp.Tpo -c -o vtls/libcurlu_la-sectransp.lo `test -f 'vtls/sectransp.c' || echo '$(srcdir)/'`vtls/sectransp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-sectransp.Tpo vtls/$(DEPDIR)/libcurlu_la-sectransp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/sectransp.c' object='vtls/libcurlu_la-sectransp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-sectransp.lo `test -f 'vtls/sectransp.c' || echo '$(srcdir)/'`vtls/sectransp.c
+
+vtls/libcurlu_la-vtls.lo: vtls/vtls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-vtls.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-vtls.Tpo -c -o vtls/libcurlu_la-vtls.lo `test -f 'vtls/vtls.c' || echo '$(srcdir)/'`vtls/vtls.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-vtls.Tpo vtls/$(DEPDIR)/libcurlu_la-vtls.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/vtls.c' object='vtls/libcurlu_la-vtls.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-vtls.lo `test -f 'vtls/vtls.c' || echo '$(srcdir)/'`vtls/vtls.c
+
+vtls/libcurlu_la-wolfssl.lo: vtls/wolfssl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-wolfssl.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-wolfssl.Tpo -c -o vtls/libcurlu_la-wolfssl.lo `test -f 'vtls/wolfssl.c' || echo '$(srcdir)/'`vtls/wolfssl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-wolfssl.Tpo vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/wolfssl.c' object='vtls/libcurlu_la-wolfssl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-wolfssl.lo `test -f 'vtls/wolfssl.c' || echo '$(srcdir)/'`vtls/wolfssl.c
+
+vtls/libcurlu_la-x509asn1.lo: vtls/x509asn1.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-x509asn1.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-x509asn1.Tpo -c -o vtls/libcurlu_la-x509asn1.lo `test -f 'vtls/x509asn1.c' || echo '$(srcdir)/'`vtls/x509asn1.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-x509asn1.Tpo vtls/$(DEPDIR)/libcurlu_la-x509asn1.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/x509asn1.c' object='vtls/libcurlu_la-x509asn1.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-x509asn1.lo `test -f 'vtls/x509asn1.c' || echo '$(srcdir)/'`vtls/x509asn1.c
+
+vquic/libcurlu_la-msh3.lo: vquic/msh3.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-msh3.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-msh3.Tpo -c -o vquic/libcurlu_la-msh3.lo `test -f 'vquic/msh3.c' || echo '$(srcdir)/'`vquic/msh3.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-msh3.Tpo vquic/$(DEPDIR)/libcurlu_la-msh3.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/msh3.c' object='vquic/libcurlu_la-msh3.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-msh3.lo `test -f 'vquic/msh3.c' || echo '$(srcdir)/'`vquic/msh3.c
+
+vquic/libcurlu_la-ngtcp2.lo: vquic/ngtcp2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-ngtcp2.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-ngtcp2.Tpo -c -o vquic/libcurlu_la-ngtcp2.lo `test -f 'vquic/ngtcp2.c' || echo '$(srcdir)/'`vquic/ngtcp2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-ngtcp2.Tpo vquic/$(DEPDIR)/libcurlu_la-ngtcp2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/ngtcp2.c' object='vquic/libcurlu_la-ngtcp2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-ngtcp2.lo `test -f 'vquic/ngtcp2.c' || echo '$(srcdir)/'`vquic/ngtcp2.c
+
+vquic/libcurlu_la-quiche.lo: vquic/quiche.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-quiche.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-quiche.Tpo -c -o vquic/libcurlu_la-quiche.lo `test -f 'vquic/quiche.c' || echo '$(srcdir)/'`vquic/quiche.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-quiche.Tpo vquic/$(DEPDIR)/libcurlu_la-quiche.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/quiche.c' object='vquic/libcurlu_la-quiche.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-quiche.lo `test -f 'vquic/quiche.c' || echo '$(srcdir)/'`vquic/quiche.c
+
+vquic/libcurlu_la-vquic.lo: vquic/vquic.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vquic/libcurlu_la-vquic.lo -MD -MP -MF vquic/$(DEPDIR)/libcurlu_la-vquic.Tpo -c -o vquic/libcurlu_la-vquic.lo `test -f 'vquic/vquic.c' || echo '$(srcdir)/'`vquic/vquic.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vquic/$(DEPDIR)/libcurlu_la-vquic.Tpo vquic/$(DEPDIR)/libcurlu_la-vquic.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vquic/vquic.c' object='vquic/libcurlu_la-vquic.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vquic/libcurlu_la-vquic.lo `test -f 'vquic/vquic.c' || echo '$(srcdir)/'`vquic/vquic.c
+
+vssh/libcurlu_la-libssh.lo: vssh/libssh.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vssh/libcurlu_la-libssh.lo -MD -MP -MF vssh/$(DEPDIR)/libcurlu_la-libssh.Tpo -c -o vssh/libcurlu_la-libssh.lo `test -f 'vssh/libssh.c' || echo '$(srcdir)/'`vssh/libssh.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vssh/$(DEPDIR)/libcurlu_la-libssh.Tpo vssh/$(DEPDIR)/libcurlu_la-libssh.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vssh/libssh.c' object='vssh/libcurlu_la-libssh.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vssh/libcurlu_la-libssh.lo `test -f 'vssh/libssh.c' || echo '$(srcdir)/'`vssh/libssh.c
+
+vssh/libcurlu_la-libssh2.lo: vssh/libssh2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vssh/libcurlu_la-libssh2.lo -MD -MP -MF vssh/$(DEPDIR)/libcurlu_la-libssh2.Tpo -c -o vssh/libcurlu_la-libssh2.lo `test -f 'vssh/libssh2.c' || echo '$(srcdir)/'`vssh/libssh2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vssh/$(DEPDIR)/libcurlu_la-libssh2.Tpo vssh/$(DEPDIR)/libcurlu_la-libssh2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vssh/libssh2.c' object='vssh/libcurlu_la-libssh2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vssh/libcurlu_la-libssh2.lo `test -f 'vssh/libssh2.c' || echo '$(srcdir)/'`vssh/libssh2.c
+
+vssh/libcurlu_la-wolfssh.lo: vssh/wolfssh.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vssh/libcurlu_la-wolfssh.lo -MD -MP -MF vssh/$(DEPDIR)/libcurlu_la-wolfssh.Tpo -c -o vssh/libcurlu_la-wolfssh.lo `test -f 'vssh/wolfssh.c' || echo '$(srcdir)/'`vssh/wolfssh.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vssh/$(DEPDIR)/libcurlu_la-wolfssh.Tpo vssh/$(DEPDIR)/libcurlu_la-wolfssh.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vssh/wolfssh.c' object='vssh/libcurlu_la-wolfssh.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vssh/libcurlu_la-wolfssh.lo `test -f 'vssh/wolfssh.c' || echo '$(srcdir)/'`vssh/wolfssh.c
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+ -rm -rf vauth/.libs vauth/_libs
+ -rm -rf vquic/.libs vquic/_libs
+ -rm -rf vssh/.libs vssh/_libs
+ -rm -rf vtls/.libs vtls/_libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+@CURLDEBUG_FALSE@all-local:
+all-am: Makefile $(LTLIBRARIES) curl_config.h all-local
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -rm -f vauth/$(DEPDIR)/$(am__dirstamp)
+ -rm -f vauth/$(am__dirstamp)
+ -rm -f vquic/$(DEPDIR)/$(am__dirstamp)
+ -rm -f vquic/$(am__dirstamp)
+ -rm -f vssh/$(DEPDIR)/$(am__dirstamp)
+ -rm -f vssh/$(am__dirstamp)
+ -rm -f vtls/$(DEPDIR)/$(am__dirstamp)
+ -rm -f vtls/$(am__dirstamp)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ clean-noinstLTLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/libcurl_la-altsvc.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-amigaos.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-asyn-ares.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-asyn-thread.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-base64.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-bufref.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-c-hyper.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-cfilters.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-conncache.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-connect.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-content_encoding.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-cookie.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_des.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_endian.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_fnmatch.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_get_line.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_gethostname.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_gssapi.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_memrchr.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_multibyte.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_ntlm_core.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_ntlm_wb.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_path.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_range.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_rtmp.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_sasl.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_sspi.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_threads.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-dict.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-doh.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-dynbuf.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-easy.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-easygetopt.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-easyoptions.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-escape.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-file.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-fileinfo.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-fopen.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-formdata.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-ftp.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-ftplistparser.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-getenv.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-getinfo.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-gopher.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-h2h3.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-hash.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-headers.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-hmac.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-hostasyn.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-hostip.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-hostip4.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-hostip6.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-hostsyn.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-hsts.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-http.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-http2.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-http_aws_sigv4.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-http_chunks.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-http_digest.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-http_negotiate.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-http_ntlm.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-http_proxy.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-idn.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-if2ip.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-imap.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-inet_ntop.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-inet_pton.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-krb5.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-ldap.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-llist.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-md4.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-md5.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-memdebug.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-mime.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-mprintf.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-mqtt.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-multi.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-netrc.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-nonblock.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-noproxy.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-openldap.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-parsedate.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-pingpong.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-pop3.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-progress.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-psl.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-rand.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-rename.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-rtsp.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-select.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-sendf.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-setopt.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-sha256.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-share.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-slist.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-smb.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-smtp.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-socketpair.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-socks.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-socks_gssapi.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-socks_sspi.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-speedcheck.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-splay.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-strcase.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-strdup.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-strerror.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-strtok.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-strtoofft.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-system_win32.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-telnet.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-tftp.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-timediff.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-timeval.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-transfer.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-url.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-urlapi.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-version.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-version_win32.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-warnless.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-wildcard.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-ws.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-altsvc.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-amigaos.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-asyn-ares.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-asyn-thread.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-base64.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-bufref.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-c-hyper.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-cfilters.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-conncache.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-connect.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-content_encoding.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-cookie.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_des.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_endian.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_fnmatch.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_get_line.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_gethostname.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_gssapi.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_memrchr.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_multibyte.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_ntlm_wb.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_path.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_range.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_rtmp.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_sasl.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_sspi.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_threads.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-dict.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-doh.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-dynbuf.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-easy.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-easygetopt.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-easyoptions.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-escape.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-file.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-fileinfo.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-fopen.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-formdata.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-ftp.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-ftplistparser.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-getenv.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-getinfo.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-gopher.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-h2h3.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-hash.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-headers.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-hmac.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-hostasyn.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-hostip.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-hostip4.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-hostip6.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-hostsyn.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-hsts.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-http.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-http2.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-http_aws_sigv4.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-http_chunks.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-http_digest.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-http_negotiate.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-http_ntlm.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-http_proxy.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-idn.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-if2ip.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-imap.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-inet_ntop.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-inet_pton.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-krb5.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-ldap.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-llist.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-md4.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-md5.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-memdebug.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-mime.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-mprintf.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-mqtt.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-multi.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-netrc.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-nonblock.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-noproxy.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-openldap.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-parsedate.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-pingpong.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-pop3.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-progress.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-psl.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-rand.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-rename.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-rtsp.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-select.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-sendf.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-setopt.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-sha256.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-share.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-slist.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-smb.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-smtp.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-socketpair.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-socks.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-socks_gssapi.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-socks_sspi.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-speedcheck.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-splay.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-strcase.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-strdup.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-strerror.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-strtok.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-strtoofft.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-system_win32.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-telnet.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-tftp.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-timediff.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-timeval.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-transfer.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-url.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-urlapi.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-version.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-version_win32.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-warnless.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-wildcard.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-ws.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-cleartext.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-cram.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-digest.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-digest_sspi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-gsasl.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-ntlm.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-oauth2.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-vauth.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-cleartext.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-cram.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-digest.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-gsasl.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-ntlm.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-oauth2.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-vauth.Plo
+ -rm -f vquic/$(DEPDIR)/libcurl_la-msh3.Plo
+ -rm -f vquic/$(DEPDIR)/libcurl_la-ngtcp2.Plo
+ -rm -f vquic/$(DEPDIR)/libcurl_la-quiche.Plo
+ -rm -f vquic/$(DEPDIR)/libcurl_la-vquic.Plo
+ -rm -f vquic/$(DEPDIR)/libcurlu_la-msh3.Plo
+ -rm -f vquic/$(DEPDIR)/libcurlu_la-ngtcp2.Plo
+ -rm -f vquic/$(DEPDIR)/libcurlu_la-quiche.Plo
+ -rm -f vquic/$(DEPDIR)/libcurlu_la-vquic.Plo
+ -rm -f vssh/$(DEPDIR)/libcurl_la-libssh.Plo
+ -rm -f vssh/$(DEPDIR)/libcurl_la-libssh2.Plo
+ -rm -f vssh/$(DEPDIR)/libcurl_la-wolfssh.Plo
+ -rm -f vssh/$(DEPDIR)/libcurlu_la-libssh.Plo
+ -rm -f vssh/$(DEPDIR)/libcurlu_la-libssh2.Plo
+ -rm -f vssh/$(DEPDIR)/libcurlu_la-wolfssh.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-bearssl.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-gskit.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-gtls.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-hostcheck.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-keylog.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-mbedtls.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-nss.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-openssl.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-rustls.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-schannel.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-sectransp.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-vtls.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-x509asn1.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-bearssl.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-gskit.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-gtls.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-hostcheck.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-keylog.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-mbedtls.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-nss.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-openssl.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-rustls.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-schannel.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-sectransp.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-vtls.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-x509asn1.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-hdr distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/libcurl_la-altsvc.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-amigaos.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-asyn-ares.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-asyn-thread.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-base64.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-bufref.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-c-hyper.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-cfilters.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-conncache.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-connect.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-content_encoding.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-cookie.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_des.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_endian.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_fnmatch.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_get_line.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_gethostname.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_gssapi.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_memrchr.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_multibyte.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_ntlm_core.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_ntlm_wb.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_path.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_range.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_rtmp.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_sasl.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_sspi.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-curl_threads.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-dict.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-doh.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-dynbuf.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-easy.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-easygetopt.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-easyoptions.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-escape.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-file.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-fileinfo.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-fopen.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-formdata.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-ftp.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-ftplistparser.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-getenv.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-getinfo.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-gopher.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-h2h3.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-hash.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-headers.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-hmac.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-hostasyn.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-hostip.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-hostip4.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-hostip6.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-hostsyn.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-hsts.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-http.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-http2.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-http_aws_sigv4.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-http_chunks.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-http_digest.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-http_negotiate.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-http_ntlm.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-http_proxy.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-idn.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-if2ip.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-imap.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-inet_ntop.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-inet_pton.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-krb5.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-ldap.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-llist.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-md4.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-md5.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-memdebug.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-mime.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-mprintf.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-mqtt.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-multi.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-netrc.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-nonblock.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-noproxy.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-openldap.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-parsedate.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-pingpong.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-pop3.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-progress.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-psl.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-rand.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-rename.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-rtsp.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-select.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-sendf.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-setopt.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-sha256.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-share.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-slist.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-smb.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-smtp.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-socketpair.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-socks.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-socks_gssapi.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-socks_sspi.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-speedcheck.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-splay.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-strcase.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-strdup.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-strerror.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-strtok.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-strtoofft.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-system_win32.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-telnet.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-tftp.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-timediff.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-timeval.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-transfer.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-url.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-urlapi.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-version.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-version_win32.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-warnless.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-wildcard.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-ws.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-altsvc.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-amigaos.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-asyn-ares.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-asyn-thread.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-base64.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-bufref.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-c-hyper.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-cfilters.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-conncache.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-connect.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-content_encoding.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-cookie.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_des.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_endian.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_fnmatch.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_get_line.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_gethostname.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_gssapi.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_memrchr.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_multibyte.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_ntlm_wb.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_path.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_range.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_rtmp.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_sasl.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_sspi.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-curl_threads.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-dict.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-doh.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-dynbuf.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-easy.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-easygetopt.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-easyoptions.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-escape.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-file.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-fileinfo.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-fopen.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-formdata.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-ftp.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-ftplistparser.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-getenv.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-getinfo.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-gopher.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-h2h3.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-hash.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-headers.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-hmac.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-hostasyn.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-hostip.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-hostip4.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-hostip6.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-hostsyn.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-hsts.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-http.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-http2.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-http_aws_sigv4.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-http_chunks.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-http_digest.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-http_negotiate.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-http_ntlm.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-http_proxy.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-idn.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-if2ip.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-imap.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-inet_ntop.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-inet_pton.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-krb5.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-ldap.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-llist.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-md4.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-md5.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-memdebug.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-mime.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-mprintf.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-mqtt.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-multi.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-netrc.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-nonblock.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-noproxy.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-openldap.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-parsedate.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-pingpong.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-pop3.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-progress.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-psl.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-rand.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-rename.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-rtsp.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-select.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-sendf.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-setopt.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-sha256.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-share.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-slist.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-smb.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-smtp.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-socketpair.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-socks.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-socks_gssapi.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-socks_sspi.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-speedcheck.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-splay.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-strcase.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-strdup.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-strerror.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-strtok.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-strtoofft.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-system_win32.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-telnet.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-tftp.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-timediff.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-timeval.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-transfer.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-url.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-urlapi.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-version.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-version_win32.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-warnless.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-wildcard.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-ws.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-cleartext.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-cram.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-digest.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-digest_sspi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-gsasl.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-krb5_gssapi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-krb5_sspi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-ntlm.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-ntlm_sspi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-oauth2.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-spnego_gssapi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-spnego_sspi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurl_la-vauth.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-cleartext.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-cram.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-digest.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-digest_sspi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-gsasl.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-krb5_gssapi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-krb5_sspi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-ntlm.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-ntlm_sspi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-oauth2.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-spnego_gssapi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-spnego_sspi.Plo
+ -rm -f vauth/$(DEPDIR)/libcurlu_la-vauth.Plo
+ -rm -f vquic/$(DEPDIR)/libcurl_la-msh3.Plo
+ -rm -f vquic/$(DEPDIR)/libcurl_la-ngtcp2.Plo
+ -rm -f vquic/$(DEPDIR)/libcurl_la-quiche.Plo
+ -rm -f vquic/$(DEPDIR)/libcurl_la-vquic.Plo
+ -rm -f vquic/$(DEPDIR)/libcurlu_la-msh3.Plo
+ -rm -f vquic/$(DEPDIR)/libcurlu_la-ngtcp2.Plo
+ -rm -f vquic/$(DEPDIR)/libcurlu_la-quiche.Plo
+ -rm -f vquic/$(DEPDIR)/libcurlu_la-vquic.Plo
+ -rm -f vssh/$(DEPDIR)/libcurl_la-libssh.Plo
+ -rm -f vssh/$(DEPDIR)/libcurl_la-libssh2.Plo
+ -rm -f vssh/$(DEPDIR)/libcurl_la-wolfssh.Plo
+ -rm -f vssh/$(DEPDIR)/libcurlu_la-libssh.Plo
+ -rm -f vssh/$(DEPDIR)/libcurlu_la-libssh2.Plo
+ -rm -f vssh/$(DEPDIR)/libcurlu_la-wolfssh.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-bearssl.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-gskit.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-gtls.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-hostcheck.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-keylog.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-mbedtls.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-mbedtls_threadlock.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-nss.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-openssl.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-rustls.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-schannel.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-sectransp.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-vtls.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-x509asn1.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-bearssl.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-gskit.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-gtls.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-hostcheck.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-keylog.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-mbedtls.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-mbedtls_threadlock.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-nss.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-openssl.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-rustls.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-schannel.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-sectransp.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-vtls.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-x509asn1.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES
+
+.MAKE: all install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am all-local am--depfiles check \
+ check-am clean clean-generic clean-libLTLIBRARIES \
+ clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-hdr distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libLTLIBRARIES \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libLTLIBRARIES
+
+.PRECIOUS: Makefile
+
+# 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
+@HAVE_WINDRES_TRUE@@USE_CPPFLAG_CURL_STATICLIB_FALSE@$(LIB_RCFILES): $(top_srcdir)/include/curl/curlver.h
+
+checksrc:
+ $(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) \
+ -W$(srcdir)/curl_config.h $(srcdir)/*.[ch] $(srcdir)/vauth/*.[ch] \
+ $(srcdir)/vtls/*.[ch] $(srcdir)/vquic/*.[ch] $(srcdir)/vssh/*.[ch])
+
+# for debug builds, we scan the sources on all regular make invokes
+@CURLDEBUG_TRUE@all-local: checksrc
+
+tidy:
+ $(TIDY) $(CSOURCES) $(TIDYFLAGS) -- $(AM_CPPFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H
+
+optiontable:
+ perl optiontable.pl < $(top_srcdir)/include/curl/curl.h > easyoptions.c
+
+@HAVE_WINDRES_TRUE@.rc.lo:
+@HAVE_WINDRES_TRUE@ $(LIBTOOL) --tag=RC --mode=compile $(RC) -I$(top_srcdir)/include $(RCFLAGS) -i $< -o $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libs/libcurl/src/Makefile.inc b/libs/libcurl/src/Makefile.inc index b2d2e9e52f..4911da9f41 100644 --- a/libs/libcurl/src/Makefile.inc +++ b/libs/libcurl/src/Makefile.inc @@ -1,353 +1,357 @@ -#*************************************************************************** -# _ _ ____ _ -# Project ___| | | | _ \| | -# / __| | | | |_) | | -# | (__| |_| | _ <| |___ -# \___|\___/|_| \_\_____| -# -# Copyright (C) 1998 - 2022, 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 -# -########################################################################### - -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/ntlm.h \ - vauth/vauth.h - -LIB_VTLS_CFILES = \ - vtls/bearssl.c \ - vtls/gskit.c \ - vtls/gtls.c \ - vtls/hostcheck.c \ - vtls/keylog.c \ - vtls/mbedtls.c \ - vtls/mbedtls_threadlock.c \ - vtls/nss.c \ - vtls/openssl.c \ - vtls/rustls.c \ - vtls/schannel.c \ - vtls/schannel_verify.c \ - vtls/sectransp.c \ - vtls/vtls.c \ - vtls/wolfssl.c \ - vtls/x509asn1.c - -LIB_VTLS_HFILES = \ - vtls/bearssl.h \ - vtls/gskit.h \ - vtls/gtls.h \ - vtls/hostcheck.h \ - vtls/keylog.h \ - vtls/mbedtls.h \ - vtls/mbedtls_threadlock.h \ - vtls/nssg.h \ - vtls/openssl.h \ - vtls/rustls.h \ - vtls/schannel.h \ - vtls/sectransp.h \ - vtls/vtls.h \ - vtls/wolfssl.h \ - vtls/x509asn1.h - -LIB_VQUIC_CFILES = \ - vquic/msh3.c \ - vquic/ngtcp2.c \ - vquic/quiche.c \ - vquic/vquic.c - -LIB_VQUIC_HFILES = \ - vquic/msh3.h \ - vquic/ngtcp2.h \ - vquic/quiche.h \ - vquic/vquic.h - -LIB_VSSH_CFILES = \ - vssh/libssh.c \ - vssh/libssh2.c \ - vssh/wolfssh.c - -LIB_VSSH_HFILES = \ - vssh/ssh.h - -LIB_CFILES = \ - altsvc.c \ - amigaos.c \ - asyn-ares.c \ - asyn-thread.c \ - base64.c \ - bufref.c \ - c-hyper.c \ - conncache.c \ - connect.c \ - content_encoding.c \ - cookie.c \ - curl_addrinfo.c \ - curl_des.c \ - curl_endian.c \ - curl_fnmatch.c \ - curl_get_line.c \ - curl_gethostname.c \ - curl_gssapi.c \ - curl_memrchr.c \ - curl_multibyte.c \ - curl_ntlm_core.c \ - curl_ntlm_wb.c \ - curl_path.c \ - curl_range.c \ - curl_rtmp.c \ - curl_sasl.c \ - curl_sspi.c \ - curl_threads.c \ - dict.c \ - doh.c \ - dynbuf.c \ - easy.c \ - easygetopt.c \ - easyoptions.c \ - escape.c \ - file.c \ - fileinfo.c \ - fopen.c \ - formdata.c \ - ftp.c \ - ftplistparser.c \ - getenv.c \ - getinfo.c \ - gopher.c \ - h2h3.c \ - hash.c \ - headers.c \ - hmac.c \ - hostasyn.c \ - hostip.c \ - hostip4.c \ - hostip6.c \ - hostsyn.c \ - hsts.c \ - http.c \ - http2.c \ - http_chunks.c \ - http_digest.c \ - http_negotiate.c \ - http_ntlm.c \ - http_proxy.c \ - http_aws_sigv4.c \ - idn_win32.c \ - if2ip.c \ - imap.c \ - inet_ntop.c \ - inet_pton.c \ - krb5.c \ - ldap.c \ - llist.c \ - md4.c \ - md5.c \ - memdebug.c \ - mime.c \ - mprintf.c \ - mqtt.c \ - multi.c \ - netrc.c \ - nonblock.c \ - noproxy.c \ - openldap.c \ - parsedate.c \ - pingpong.c \ - pop3.c \ - progress.c \ - psl.c \ - rand.c \ - rename.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 \ - strerror.c \ - strtok.c \ - strtoofft.c \ - system_win32.c \ - telnet.c \ - tftp.c \ - timediff.c \ - timeval.c \ - transfer.c \ - url.c \ - urlapi.c \ - version.c \ - version_win32.c \ - warnless.c \ - wildcard.c \ - ws.c - -LIB_HFILES = \ - altsvc.h \ - amigaos.h \ - arpa_telnet.h \ - asyn.h \ - bufref.h \ - c-hyper.h \ - conncache.h \ - connect.h \ - content_encoding.h \ - cookie.h \ - curl_addrinfo.h \ - curl_base64.h \ - curl_ctype.h \ - curl_des.h \ - curl_endian.h \ - curl_fnmatch.h \ - curl_get_line.h \ - curl_gethostname.h \ - curl_gssapi.h \ - curl_hmac.h \ - curl_krb5.h \ - curl_ldap.h \ - curl_md4.h \ - curl_md5.h \ - curl_memory.h \ - curl_memrchr.h \ - curl_multibyte.h \ - curl_ntlm_core.h \ - curl_ntlm_wb.h \ - curl_path.h \ - curl_printf.h \ - curl_range.h \ - curl_rtmp.h \ - curl_sasl.h \ - curl_setup.h \ - curl_setup_once.h \ - curl_sha256.h \ - curl_sspi.h \ - curl_threads.h \ - curlx.h \ - dict.h \ - doh.h \ - dynbuf.h \ - easy_lock.h \ - easyif.h \ - easyoptions.h \ - escape.h \ - file.h \ - fileinfo.h \ - fopen.h \ - formdata.h \ - functypes.h \ - ftp.h \ - ftplistparser.h \ - getinfo.h \ - gopher.h \ - h2h3.h \ - hash.h \ - headers.h \ - hostip.h \ - hsts.h \ - http.h \ - http2.h \ - http_chunks.h \ - http_digest.h \ - http_negotiate.h \ - http_ntlm.h \ - http_proxy.h \ - http_aws_sigv4.h \ - if2ip.h \ - imap.h \ - inet_ntop.h \ - inet_pton.h \ - llist.h \ - memdebug.h \ - mime.h \ - mqtt.h \ - multihandle.h \ - multiif.h \ - netrc.h \ - nonblock.h \ - noproxy.h \ - parsedate.h \ - pingpong.h \ - pop3.h \ - progress.h \ - psl.h \ - quic.h \ - rand.h \ - rename.h \ - rtsp.h \ - select.h \ - sendf.h \ - setopt.h \ - setup-vms.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 \ - strtok.h \ - strtoofft.h \ - system_win32.h \ - telnet.h \ - tftp.h \ - timediff.h \ - timeval.h \ - transfer.h \ - url.h \ - urlapi-int.h \ - urldata.h \ - version_win32.h \ - warnless.h \ - wildcard.h \ - ws.h - -LIB_RCFILES = libcurl.rc - -CSOURCES = $(LIB_CFILES) $(LIB_VAUTH_CFILES) $(LIB_VTLS_CFILES) \ - $(LIB_VQUIC_CFILES) $(LIB_VSSH_CFILES) -HHEADERS = $(LIB_HFILES) $(LIB_VAUTH_HFILES) $(LIB_VTLS_HFILES) \ - $(LIB_VQUIC_HFILES) $(LIB_VSSH_HFILES) +#***************************************************************************
+# _ _ ____ _
+# Project ___| | | | _ \| |
+# / __| | | | |_) | |
+# | (__| |_| | _ <| |___
+# \___|\___/|_| \_\_____|
+#
+# Copyright (C) 1998 - 2022, 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
+#
+###########################################################################
+
+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/ntlm.h \
+ vauth/vauth.h
+
+LIB_VTLS_CFILES = \
+ vtls/bearssl.c \
+ vtls/gskit.c \
+ vtls/gtls.c \
+ vtls/hostcheck.c \
+ vtls/keylog.c \
+ vtls/mbedtls.c \
+ vtls/mbedtls_threadlock.c \
+ vtls/nss.c \
+ vtls/openssl.c \
+ vtls/rustls.c \
+ vtls/schannel.c \
+ vtls/schannel_verify.c \
+ vtls/sectransp.c \
+ vtls/vtls.c \
+ vtls/wolfssl.c \
+ vtls/x509asn1.c
+
+LIB_VTLS_HFILES = \
+ vtls/bearssl.h \
+ vtls/gskit.h \
+ vtls/gtls.h \
+ vtls/hostcheck.h \
+ vtls/keylog.h \
+ vtls/mbedtls.h \
+ vtls/mbedtls_threadlock.h \
+ vtls/nssg.h \
+ vtls/openssl.h \
+ vtls/rustls.h \
+ vtls/schannel.h \
+ vtls/sectransp.h \
+ vtls/vtls.h \
+ vtls/vtls_int.h \
+ vtls/wolfssl.h \
+ vtls/x509asn1.h
+
+LIB_VQUIC_CFILES = \
+ vquic/msh3.c \
+ vquic/ngtcp2.c \
+ vquic/quiche.c \
+ vquic/vquic.c
+
+LIB_VQUIC_HFILES = \
+ vquic/msh3.h \
+ vquic/ngtcp2.h \
+ vquic/quiche.h \
+ vquic/vquic.h
+
+LIB_VSSH_CFILES = \
+ vssh/libssh.c \
+ vssh/libssh2.c \
+ vssh/wolfssh.c
+
+LIB_VSSH_HFILES = \
+ vssh/ssh.h
+
+LIB_CFILES = \
+ altsvc.c \
+ amigaos.c \
+ asyn-ares.c \
+ asyn-thread.c \
+ base64.c \
+ bufref.c \
+ c-hyper.c \
+ cfilters.c \
+ conncache.c \
+ connect.c \
+ content_encoding.c \
+ cookie.c \
+ curl_addrinfo.c \
+ curl_des.c \
+ curl_endian.c \
+ curl_fnmatch.c \
+ curl_get_line.c \
+ curl_gethostname.c \
+ curl_gssapi.c \
+ curl_memrchr.c \
+ curl_multibyte.c \
+ curl_ntlm_core.c \
+ curl_ntlm_wb.c \
+ curl_path.c \
+ curl_range.c \
+ curl_rtmp.c \
+ curl_sasl.c \
+ curl_sspi.c \
+ curl_threads.c \
+ dict.c \
+ doh.c \
+ dynbuf.c \
+ easy.c \
+ easygetopt.c \
+ easyoptions.c \
+ escape.c \
+ file.c \
+ fileinfo.c \
+ fopen.c \
+ formdata.c \
+ ftp.c \
+ ftplistparser.c \
+ getenv.c \
+ getinfo.c \
+ gopher.c \
+ h2h3.c \
+ hash.c \
+ headers.c \
+ hmac.c \
+ hostasyn.c \
+ hostip.c \
+ hostip4.c \
+ hostip6.c \
+ hostsyn.c \
+ hsts.c \
+ http.c \
+ http2.c \
+ http_chunks.c \
+ http_digest.c \
+ http_negotiate.c \
+ http_ntlm.c \
+ http_proxy.c \
+ http_aws_sigv4.c \
+ idn.c \
+ if2ip.c \
+ imap.c \
+ inet_ntop.c \
+ inet_pton.c \
+ krb5.c \
+ ldap.c \
+ llist.c \
+ md4.c \
+ md5.c \
+ memdebug.c \
+ mime.c \
+ mprintf.c \
+ mqtt.c \
+ multi.c \
+ netrc.c \
+ nonblock.c \
+ noproxy.c \
+ openldap.c \
+ parsedate.c \
+ pingpong.c \
+ pop3.c \
+ progress.c \
+ psl.c \
+ rand.c \
+ rename.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 \
+ strerror.c \
+ strtok.c \
+ strtoofft.c \
+ system_win32.c \
+ telnet.c \
+ tftp.c \
+ timediff.c \
+ timeval.c \
+ transfer.c \
+ url.c \
+ urlapi.c \
+ version.c \
+ version_win32.c \
+ warnless.c \
+ wildcard.c \
+ ws.c
+
+LIB_HFILES = \
+ altsvc.h \
+ amigaos.h \
+ arpa_telnet.h \
+ asyn.h \
+ bufref.h \
+ c-hyper.h \
+ cfilters.h \
+ conncache.h \
+ connect.h \
+ content_encoding.h \
+ cookie.h \
+ curl_addrinfo.h \
+ curl_base64.h \
+ curl_ctype.h \
+ curl_des.h \
+ curl_endian.h \
+ curl_fnmatch.h \
+ curl_get_line.h \
+ curl_gethostname.h \
+ curl_gssapi.h \
+ curl_hmac.h \
+ curl_krb5.h \
+ curl_ldap.h \
+ curl_md4.h \
+ curl_md5.h \
+ curl_memory.h \
+ curl_memrchr.h \
+ curl_multibyte.h \
+ curl_ntlm_core.h \
+ curl_ntlm_wb.h \
+ curl_path.h \
+ curl_printf.h \
+ curl_range.h \
+ curl_rtmp.h \
+ curl_sasl.h \
+ curl_setup.h \
+ curl_setup_once.h \
+ curl_sha256.h \
+ curl_sspi.h \
+ curl_threads.h \
+ curlx.h \
+ dict.h \
+ doh.h \
+ dynbuf.h \
+ easy_lock.h \
+ easyif.h \
+ easyoptions.h \
+ escape.h \
+ file.h \
+ fileinfo.h \
+ fopen.h \
+ formdata.h \
+ functypes.h \
+ ftp.h \
+ ftplistparser.h \
+ getinfo.h \
+ gopher.h \
+ h2h3.h \
+ hash.h \
+ headers.h \
+ hostip.h \
+ hsts.h \
+ http.h \
+ http2.h \
+ http_chunks.h \
+ http_digest.h \
+ http_negotiate.h \
+ http_ntlm.h \
+ http_proxy.h \
+ http_aws_sigv4.h \
+ idn.h \
+ if2ip.h \
+ imap.h \
+ inet_ntop.h \
+ inet_pton.h \
+ llist.h \
+ memdebug.h \
+ mime.h \
+ mqtt.h \
+ multihandle.h \
+ multiif.h \
+ netrc.h \
+ nonblock.h \
+ noproxy.h \
+ parsedate.h \
+ pingpong.h \
+ pop3.h \
+ progress.h \
+ psl.h \
+ quic.h \
+ rand.h \
+ rename.h \
+ rtsp.h \
+ select.h \
+ sendf.h \
+ setopt.h \
+ setup-vms.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 \
+ strtok.h \
+ strtoofft.h \
+ system_win32.h \
+ telnet.h \
+ tftp.h \
+ timediff.h \
+ timeval.h \
+ transfer.h \
+ url.h \
+ urlapi-int.h \
+ urldata.h \
+ version_win32.h \
+ warnless.h \
+ wildcard.h \
+ ws.h
+
+LIB_RCFILES = libcurl.rc
+
+CSOURCES = $(LIB_CFILES) $(LIB_VAUTH_CFILES) $(LIB_VTLS_CFILES) \
+ $(LIB_VQUIC_CFILES) $(LIB_VSSH_CFILES)
+HHEADERS = $(LIB_HFILES) $(LIB_VAUTH_HFILES) $(LIB_VTLS_HFILES) \
+ $(LIB_VQUIC_HFILES) $(LIB_VSSH_HFILES)
diff --git a/libs/libcurl/src/Makefile.m32 b/libs/libcurl/src/Makefile.m32 deleted file mode 100644 index b8b56f8c98..0000000000 --- a/libs/libcurl/src/Makefile.m32 +++ /dev/null @@ -1,329 +0,0 @@ -#*************************************************************************** -# _ _ ____ _ -# Project ___| | | | _ \| | -# / __| | | | |_) | | -# | (__| |_| | _ <| |___ -# \___|\___/|_| \_\_____| -# -# Copyright (C) 1999 - 2022, 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 -# -#*************************************************************************** - -# Makefile for building curl parts with MinGW and optional features. -# -# Usage: mingw32-make -f Makefile.m32 CFG=-feature1[-feature2][-feature3][...] -# Example: mingw32-make -f Makefile.m32 CFG=-zlib-ssl-sspi-winidn -# -# Set component roots via envvar <feature>_PATH. Also available for -# customization: CC, RC, AR, CPPFLAGS, LDFLAGS, LIBS, CFLAGS, RCFLAGS, -# ARCH[=custom], CROSSPREFIX, CURL_LDFLAGS_BIN, CURL_LDFLAGS_LIB, CURL_DLL_SUFFIX, -# and more for individual components (see below). - -# This script is reused by 'src' and 'docs/examples' Makefile.m32 scripts. -# Skip lib-specific parts when called through them. -ifndef PROOT -PROOT := .. - -CPPFLAGS += -DBUILDING_LIBCURL - -### Sources and targets - -# Provides CSOURCES, HHEADERS, LIB_RCFILES -include Makefile.inc - -libcurl_dll_LIBRARY := libcurl$(CURL_DLL_SUFFIX).dll -libcurl_dll_a_LIBRARY := libcurl.dll.a -libcurl_a_LIBRARY := libcurl.a - -TARGETS := $(libcurl_a_LIBRARY) $(libcurl_dll_LIBRARY) - -libcurl_a_OBJECTS := $(patsubst %.c,%.o,$(notdir $(strip $(CSOURCES)))) -libcurl_a_DEPENDENCIES := $(strip $(CSOURCES) $(HHEADERS)) -libcurl_dll_OBJECTS := $(libcurl_a_OBJECTS) -libcurl_dll_OBJECTS += $(patsubst %.rc,%.res,$(strip $(LIB_RCFILES))) -vpath %.c vauth vquic vssh vtls - -TOCLEAN := $(libcurl_dll_OBJECTS) -TOVCLEAN := $(libcurl_dll_LIBRARY:.dll=.def) $(libcurl_dll_a_LIBRARY) - -### Local rules - -# Keep this at the top to act as the default target. -all: $(TARGETS) - -$(libcurl_a_LIBRARY): $(libcurl_a_OBJECTS) $(libcurl_a_DEPENDENCIES) - @$(call DEL, $@) - $(AR) rcs $@ $(libcurl_a_OBJECTS) - -$(libcurl_dll_LIBRARY): $(libcurl_dll_OBJECTS) - $(CC) $(LDFLAGS) -shared $(CURL_LDFLAGS_LIB) -o $@ $(libcurl_dll_OBJECTS) $(LIBS) \ - -Wl,--output-def,$(@:.dll=.def),--out-implib,$(libcurl_dll_a_LIBRARY) -endif - -CPPFLAGS += -I. -I$(PROOT)/include -RCFLAGS += -I$(PROOT)/include - -CC := $(CROSSPREFIX)$(CC) -AR := $(CROSSPREFIX)$(AR) -RC ?= $(CROSSPREFIX)windres - -ifneq ($(ARCH),custom) - # Set environment var ARCH to your architecture to override auto-detection. - ifndef ARCH - ifneq ($(findstring x86_64,$(shell $(CC) -dumpmachine)),) - ARCH := w64 - else - ARCH := w32 - endif - endif - ifeq ($(ARCH),w64) - CFLAGS += -m64 - LDFLAGS += -m64 - RCFLAGS += --target=pe-x86-64 - else - CFLAGS += -m32 - LDFLAGS += -m32 - RCFLAGS += --target=pe-i386 - endif -endif - -### Optional features - -ifneq ($(findstring -unicode,$(CFG)),) - CPPFLAGS += -DUNICODE -D_UNICODE - CURL_LDFLAGS_BIN += -municode -endif - -# CPPFLAGS below are only necessary when building libcurl via 'lib' (see -# comments below about exceptions). Always include them anyway to match -# behavior of other build systems. - -# Linker options to exclude for shared mode executables. -_LDFLAGS := -_LIBS := - -ifneq ($(findstring -sync,$(CFG)),) - CPPFLAGS += -DUSE_SYNC_DNS -else ifneq ($(findstring -ares,$(CFG)),) - LIBCARES_PATH ?= $(PROOT)/../c-ares - CPPFLAGS += -DUSE_ARES - CPPFLAGS += -I"$(LIBCARES_PATH)/include" - _LDFLAGS += -L"$(LIBCARES_PATH)/lib" - _LIBS += -lcares -endif - -ifneq ($(findstring -rtmp,$(CFG)),) - LIBRTMP_PATH ?= $(PROOT)/../librtmp - CPPFLAGS += -DUSE_LIBRTMP - CPPFLAGS += -I"$(LIBRTMP_PATH)" - _LDFLAGS += -L"$(LIBRTMP_PATH)/librtmp" - _LIBS += -lrtmp -lwinmm - ZLIB := 1 -endif - -ifneq ($(findstring -ssh2,$(CFG)),) - LIBSSH2_PATH ?= $(PROOT)/../libssh2 - CPPFLAGS += -DUSE_LIBSSH2 - CPPFLAGS += -I"$(LIBSSH2_PATH)/include" - _LDFLAGS += -L"$(LIBSSH2_PATH)/lib" - _LDFLAGS += -L"$(LIBSSH2_PATH)/win32" - _LIBS += -lssh2 -else ifneq ($(findstring -libssh,$(CFG)),) - LIBSSH_PATH ?= $(PROOT)/../libssh - CPPFLAGS += -DUSE_LIBSSH - CPPFLAGS += -I"$(LIBSSH_PATH)/include" - _LDFLAGS += -L"$(LIBSSH_PATH)/lib" - _LIBS += -lssh -else ifneq ($(findstring -wolfssh,$(CFG)),) - WOLFSSH_PATH ?= $(PROOT)/../wolfssh - CPPFLAGS += -DUSE_WOLFSSH - CPPFLAGS += -I"$(WOLFSSH_PATH)/include" - _LDFLAGS += -L"$(WOLFSSH_PATH)/lib" - _LIBS += -lwolfssh -endif - -ifneq ($(findstring -ssl,$(CFG)),) - OPENSSL_PATH ?= $(PROOT)/../openssl - CPPFLAGS += -DUSE_OPENSSL - CPPFLAGS += -DCURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG - OPENSSL_INCLUDE ?= $(OPENSSL_PATH)/include - OPENSSL_LIBPATH ?= $(OPENSSL_PATH)/lib - CPPFLAGS += -I"$(OPENSSL_INCLUDE)" - _LDFLAGS += -L"$(OPENSSL_LIBPATH)" - OPENSSL_LIBS ?= -lssl -lcrypto - _LIBS += $(OPENSSL_LIBS) - - ifneq ($(wildcard $(OPENSSL_INCLUDE)/openssl/aead.h),) - OPENSSL := boringssl - else - # including libressl - OPENSSL := openssl - endif - - ifneq ($(findstring -srp,$(CFG)),) - ifneq ($(wildcard $(OPENSSL_INCLUDE)/openssl/srp.h),) - # OpenSSL 1.0.1 and later. - CPPFLAGS += -DHAVE_OPENSSL_SRP -DUSE_TLS_SRP - endif - endif - SSLLIBS += 1 -else ifneq ($(findstring -wolfssl,$(CFG)),) - WOLFSSL_PATH ?= $(PROOT)/../zlib - CPPFLAGS += -DUSE_WOLFSSL - CPPFLAGS += -DSIZEOF_LONG_LONG=8 - CPPFLAGS += -I"$(WOLFSSL_PATH)/include" - _LDFLAGS += -L"$(WOLFSSL_PATH)/lib" - _LIBS += -lwolfssl - OPENSSL := wolfssl - SSLLIBS += 1 -endif -ifneq ($(findstring -mbedtls,$(CFG)),) - MBEDTLS_PATH ?= $(PROOT)/../zlib - CPPFLAGS += -DUSE_MBEDTLS - CPPFLAGS += -I"$(MBEDTLS_PATH)/include" - _LDFLAGS += -L"$(MBEDTLS_PATH)/lib" - _LIBS += -lmbedtls -lmbedx509 -lmbedcrypto - SSLLIBS += 1 -endif -ifneq ($(findstring -schannel,$(CFG)),) - CPPFLAGS += -DUSE_SCHANNEL - SSLLIBS += 1 -endif - -ifneq ($(findstring -nghttp2,$(CFG)),) - NGHTTP2_PATH ?= $(PROOT)/../nghttp2 - CPPFLAGS += -DUSE_NGHTTP2 - CPPFLAGS += -I"$(NGHTTP2_PATH)/include" - _LDFLAGS += -L"$(NGHTTP2_PATH)/lib" - _LIBS += -lnghttp2 -endif - -ifeq ($(findstring -nghttp3,$(CFG))$(findstring -ngtcp2,$(CFG)),-nghttp3-ngtcp2) - NGHTTP3_PATH ?= $(PROOT)/../nghttp3 - CPPFLAGS += -DUSE_NGHTTP3 - CPPFLAGS += -I"$(NGHTTP3_PATH)/include" - _LDFLAGS += -L"$(NGHTTP3_PATH)/lib" - _LIBS += -lnghttp3 - - NGTCP2_PATH ?= $(PROOT)/../ngtcp2 - CPPFLAGS += -DUSE_NGTCP2 - CPPFLAGS += -I"$(NGTCP2_PATH)/include" - _LDFLAGS += -L"$(NGTCP2_PATH)/lib" - ifneq ($(OPENSSL),) - NGTCP2_LIBS ?= -lngtcp2_crypto_$(OPENSSL) - endif - _LIBS += -lngtcp2 $(NGTCP2_LIBS) -endif - -ifneq ($(findstring -zlib,$(CFG))$(ZLIB),) - ZLIB_PATH ?= $(PROOT)/../zlib - # These CPPFLAGS are also required when compiling the curl tool via 'src'. - CPPFLAGS += -DHAVE_LIBZ - CPPFLAGS += -I"$(ZLIB_PATH)" - _LDFLAGS += -L"$(ZLIB_PATH)" - _LIBS += -lz -endif -ifneq ($(findstring -zstd,$(CFG)),) - ZSTD_PATH ?= $(PROOT)/../zstd - CPPFLAGS += -DHAVE_ZSTD - CPPFLAGS += -I"$(ZSTD_PATH)/include" - _LDFLAGS += -L"$(ZSTD_PATH)/lib" - ZSTD_LIBS ?= -lzstd - _LIBS += $(ZSTD_LIBS) -endif -ifneq ($(findstring -brotli,$(CFG)),) - BROTLI_PATH ?= $(PROOT)/../brotli - CPPFLAGS += -DHAVE_BROTLI - CPPFLAGS += -I"$(BROTLI_PATH)/include" - _LDFLAGS += -L"$(BROTLI_PATH)/lib" - BROTLI_LIBS ?= -lbrotlidec -lbrotlicommon - _LIBS += $(BROTLI_LIBS) -endif -ifneq ($(findstring -gsasl,$(CFG)),) - LIBGSASL_PATH ?= $(PROOT)/../gsasl - CPPFLAGS += -DUSE_GSASL - CPPFLAGS += -I"$(LIBGSASL_PATH)/include" - _LDFLAGS += -L"$(LIBGSASL_PATH)/lib" - _LIBS += -lgsasl -endif - -ifneq ($(findstring -idn2,$(CFG)),) - LIBIDN2_PATH ?= $(PROOT)/../libidn2 - CPPFLAGS += -DUSE_LIBIDN2 - CPPFLAGS += -I"$(LIBIDN2_PATH)/include" - _LDFLAGS += -L"$(LIBIDN2_PATH)/lib" - _LIBS += -lidn2 - -ifneq ($(findstring -psl,$(CFG)),) - LIBPSL_PATH ?= $(PROOT)/../libpsl - CPPFLAGS += -DUSE_LIBPSL - CPPFLAGS += -I"$(LIBPSL_PATH)/include" - _LDFLAGS += -L"$(LIBPSL_PATH)/lib" - _LIBS += -lpsl -endif -else ifneq ($(findstring -winidn,$(CFG)),) - CPPFLAGS += -DUSE_WIN32_IDN - CPPFLAGS += -DWANT_IDN_PROTOTYPES - _LIBS += -lnormaliz -endif - -ifneq ($(findstring -sspi,$(CFG)),) - CPPFLAGS += -DUSE_WINDOWS_SSPI -endif -ifneq ($(findstring -ipv6,$(CFG)),) - CPPFLAGS += -DENABLE_IPV6 -endif -ifneq ($(findstring -ldaps,$(CFG)),) - CPPFLAGS += -DHAVE_LDAP_SSL -endif - -ifeq ($(findstring -lldap,$(LIBS)),) - _LIBS += -lwldap32 -endif -_LIBS += -lws2_32 -lcrypt32 -lbcrypt - -ifneq ($(findstring 11,$(subst $() ,,$(SSLLIBS))),) - CPPFLAGS += -DCURL_WITH_MULTI_SSL -endif - -ifndef DYN - LDFLAGS += $(_LDFLAGS) - LIBS += $(_LIBS) -endif - -### Global rules - -ifneq ($(findstring /sh,$(SHELL)),) -DEL = rm -f $1 -COPY = -cp -afv $1 $2 -else -DEL = -del 2>NUL /q /f $(subst /,\,$1) -COPY = -copy 2>NUL /y $(subst /,\,$1) $(subst /,\,$2) -endif - -all: $(TARGETS) - -%.o: %.c - $(CC) -W -Wall $(CFLAGS) $(CPPFLAGS) -c $< - -%.res: %.rc - $(RC) -O coff $(RCFLAGS) -i $< -o $@ - -clean: - @$(call DEL, $(TOCLEAN)) - -distclean vclean: clean - @$(call DEL, $(TARGETS) $(TOVCLEAN)) diff --git a/libs/libcurl/src/altsvc.c b/libs/libcurl/src/altsvc.c index 7bca840151..15492a7601 100644 --- a/libs/libcurl/src/altsvc.c +++ b/libs/libcurl/src/altsvc.c @@ -1,656 +1,662 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2019 - 2022, 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_get_line.h" -#include "strcase.h" -#include "parsedate.h" -#include "sendf.h" -#include "warnless.h" -#include "fopen.h" -#include "rename.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#define MAX_ALTSVC_LINE 4095 -#define MAX_ALTSVC_DATELENSTR "64" -#define MAX_ALTSVC_DATELEN 64 -#define MAX_ALTSVC_HOSTLENSTR "512" -#define MAX_ALTSVC_HOSTLEN 512 -#define MAX_ALTSVC_ALPNLENSTR "10" -#define MAX_ALTSVC_ALPNLEN 10 - -#define H3VERSION "h3" - -static enum alpnid alpn2alpnid(char *name) -{ - if(strcasecompare(name, "h1")) - return ALPN_h1; - if(strcasecompare(name, "h2")) - return ALPN_h2; - if(strcasecompare(name, H3VERSION)) - return ALPN_h3; - return ALPN_none; /* unknown, probably rubbish input */ -} - -/* 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, - const char *dsthost, - enum alpnid srcalpnid, - enum alpnid dstalpnid, - unsigned int srcport, - unsigned int dstport) -{ - struct altsvc *as = calloc(sizeof(struct altsvc), 1); - size_t hlen; - if(!as) - return NULL; - hlen = strlen(srchost); - DEBUGASSERT(hlen); - as->src.host = strdup(srchost); - if(!as->src.host) - goto error; - if(hlen && (srchost[hlen - 1] == '.')) - /* strip off trailing any dot */ - as->src.host[--hlen] = 0; - as->dst.host = strdup(dsthost); - if(!as->dst.host) - goto error; - - as->src.alpnid = srcalpnid; - as->dst.alpnid = dstalpnid; - as->src.port = curlx_ultous(srcport); - as->dst.port = curlx_ultous(dstport); - - return as; - error: - altsvc_free(as); - return NULL; -} - -static struct altsvc *altsvc_create(char *srchost, - char *dsthost, - char *srcalpn, - char *dstalpn, - unsigned int srcport, - unsigned int dstport) -{ - enum alpnid dstalpnid = alpn2alpnid(dstalpn); - enum alpnid srcalpnid = alpn2alpnid(srcalpn); - if(!srcalpnid || !dstalpnid) - return NULL; - return altsvc_createid(srchost, dsthost, srcalpnid, dstalpnid, - srcport, dstport); -} - -/* only returns SERIOUS errors */ -static CURLcode altsvc_add(struct altsvcinfo *asi, char *line) -{ - /* Example line: - h2 example.com 443 h3 shiny.example.com 8443 "20191231 10:00:00" 1 - */ - char srchost[MAX_ALTSVC_HOSTLEN + 1]; - char dsthost[MAX_ALTSVC_HOSTLEN + 1]; - char srcalpn[MAX_ALTSVC_ALPNLEN + 1]; - char dstalpn[MAX_ALTSVC_ALPNLEN + 1]; - char date[MAX_ALTSVC_DATELEN + 1]; - unsigned int srcport; - unsigned int dstport; - unsigned int prio; - unsigned int persist; - int rc; - - rc = sscanf(line, - "%" MAX_ALTSVC_ALPNLENSTR "s %" MAX_ALTSVC_HOSTLENSTR "s %u " - "%" MAX_ALTSVC_ALPNLENSTR "s %" MAX_ALTSVC_HOSTLENSTR "s %u " - "\"%" MAX_ALTSVC_DATELENSTR "[^\"]\" %u %u", - srcalpn, srchost, &srcport, - dstalpn, dsthost, &dstport, - date, &persist, &prio); - if(9 == rc) { - struct altsvc *as; - time_t expires = Curl_getdate_capped(date); - as = altsvc_create(srchost, dsthost, srcalpn, dstalpn, srcport, dstport); - if(as) { - as->expires = expires; - as->prio = prio; - as->persist = persist ? 1 : 0; - Curl_llist_insert_next(&asi->list, asi->list.tail, 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; - char *line = NULL; - FILE *fp; - - /* we need a private copy of the file name 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 = fopen(file, FOPEN_READTEXT); - if(fp) { - line = malloc(MAX_ALTSVC_LINE); - if(!line) - goto fail; - while(Curl_get_line(line, MAX_ALTSVC_LINE, fp)) { - char *lineptr = line; - while(*lineptr && ISBLANK(*lineptr)) - lineptr++; - if(*lineptr == '#') - /* skip commented lines */ - continue; - - altsvc_add(asi, lineptr); - } - free(line); /* free the line buffer */ - fclose(fp); - } - return result; - - fail: - Curl_safefree(asi->filename); - free(line); - fclose(fp); - return CURLE_OUT_OF_MEMORY; -} - -/* - * Write this single altsvc entry to a single output line - */ - -static CURLcode altsvc_out(struct altsvc *as, FILE *fp) -{ - struct tm stamp; - CURLcode result = Curl_gmtime(as->expires, &stamp); - if(result) - return result; - - fprintf(fp, - "%s %s %u " - "%s %s %u " - "\"%d%02d%02d " - "%02d:%02d:%02d\" " - "%u %d\n", - Curl_alpnid2str(as->src.alpnid), as->src.host, as->src.port, - Curl_alpnid2str(as->dst.alpnid), as->dst.host, 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(sizeof(struct altsvcinfo), 1); - if(!asi) - return NULL; - Curl_llist_init(&asi->list, NULL); - - /* set default behavior */ - asi->flags = CURLALTSVC_H1 -#ifdef USE_HTTP2 - | CURLALTSVC_H2 -#endif -#ifdef ENABLE_QUIC - | CURLALTSVC_H3 -#endif - ; - return asi; -} - -/* - * Curl_altsvc_load() loads alt-svc from file. - */ -CURLcode Curl_altsvc_load(struct altsvcinfo *asi, const char *file) -{ - CURLcode result; - DEBUGASSERT(asi); - result = altsvc_load(asi, file); - return result; -} - -/* - * Curl_altsvc_ctrl() passes on the external bitmask. - */ -CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl) -{ - DEBUGASSERT(asi); - if(!ctrl) - /* unexpected */ - return CURLE_BAD_FUNCTION_ARGUMENT; - 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) -{ - struct Curl_llist_element *e; - struct Curl_llist_element *n; - if(*altsvcp) { - struct altsvcinfo *altsvc = *altsvcp; - for(e = altsvc->list.head; e; e = n) { - struct altsvc *as = e->ptr; - n = e->next; - 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) -{ - struct Curl_llist_element *e; - struct Curl_llist_element *n; - 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 file name */ - return CURLE_OK; - - result = Curl_fopen(data, file, &out, &tempstore); - if(!result) { - 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 = altsvc->list.head; e; e = n) { - struct altsvc *as = e->ptr; - n = e->next; - result = altsvc_out(as, out); - if(result) - break; - } - fclose(out); - if(!result && tempstore && Curl_rename(tempstore, file)) - result = CURLE_WRITE_ERROR; - - if(result && tempstore) - unlink(tempstore); - } - free(tempstore); - return result; -} - -static CURLcode getalnum(const char **ptr, char *alpnbuf, size_t buflen) -{ - size_t len; - const char *protop; - const char *p = *ptr; - while(*p && ISBLANK(*p)) - p++; - protop = p; - while(*p && !ISBLANK(*p) && (*p != ';') && (*p != '=')) - p++; - len = p - protop; - *ptr = p; - - if(!len || (len >= buflen)) - return CURLE_BAD_FUNCTION_ARGUMENT; - memcpy(alpnbuf, protop, len); - alpnbuf[len] = 0; - return CURLE_OK; -} - -/* 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 can't match if they have different lengths */ - return FALSE; - return strncasecompare(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_element *e; - struct Curl_llist_element *n; - for(e = asi->list.head; e; e = n) { - struct altsvc *as = e->ptr; - n = e->next; - if((srcalpnid == as->src.alpnid) && - (srcport == as->src.port) && - hostcompare(srchost, as->src.host)) { - Curl_llist_remove(&asi->list, e, NULL); - altsvc_free(as); - } - } -} - -#ifdef DEBUGBUILD -/* to play well with debug builds, we can *set* a fixed time this will - return */ -static time_t debugtime(void *unused) -{ - char *timestr = getenv("CURL_TIME"); - (void)unused; - if(timestr) { - unsigned long val = strtol(timestr, NULL, 10); - return (time_t)val; - } - return time(NULL); -} -#define time(x) debugtime(x) -#endif - -#define ISNEWLINE(x) (((x) == '\n') || (x) == '\r') - -/* - * 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's contents to the right of the - * header name. - * - * Currently this function rejects invalid data without returning an error. - * Invalid host name, 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; - size_t len; - char namebuf[MAX_ALTSVC_HOSTLEN] = ""; - char alpnbuf[MAX_ALTSVC_ALPNLEN] = ""; - struct altsvc *as; - unsigned short dstport = srcport; /* the same by default */ - CURLcode result = getalnum(&p, alpnbuf, sizeof(alpnbuf)); - size_t entries = 0; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif - if(result) { - infof(data, "Excessive alt-svc header, ignoring."); - return CURLE_OK; - } - - DEBUGASSERT(asi); - - /* "clear" is a magic keyword */ - if(strcasecompare(alpnbuf, "clear")) { - /* Flush cached alternatives for this source origin */ - altsvc_flush(asi, srcalpnid, srchost, srcport); - return CURLE_OK; - } - - do { - if(*p == '=') { - /* [protocol]="[host][:port]" */ - enum alpnid dstalpnid = alpn2alpnid(alpnbuf); /* the same by default */ - p++; - if(*p == '\"') { - const char *dsthost = ""; - const char *value_ptr; - char option[32]; - unsigned long num; - char *end_ptr; - bool quoted = FALSE; - time_t maxage = 24 * 3600; /* default is 24 hours */ - bool persist = FALSE; - bool valid = TRUE; - p++; - if(*p != ':') { - /* host name starts here */ - const char *hostp = p; - while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-'))) - p++; - len = p - hostp; - if(!len || (len >= MAX_ALTSVC_HOSTLEN)) { - infof(data, "Excessive alt-svc host name, ignoring."); - valid = FALSE; - } - else { - memcpy(namebuf, hostp, len); - namebuf[len] = 0; - dsthost = namebuf; - } - } - else { - /* no destination name, use source host */ - dsthost = srchost; - } - if(*p == ':') { - /* a port number */ - unsigned long port = strtoul(++p, &end_ptr, 10); - if(port > USHRT_MAX || end_ptr == p || *end_ptr != '\"') { - infof(data, "Unknown alt-svc port number, ignoring."); - valid = FALSE; - } - else - dstport = curlx_ultous(port); - p = end_ptr; - } - if(*p++ != '\"') - break; - /* Handle the optional 'ma' and 'persist' flags. Unknown flags - are skipped. */ - for(;;) { - while(ISBLANK(*p)) - p++; - if(*p != ';') - break; - p++; /* pass the semicolon */ - if(!*p || ISNEWLINE(*p)) - break; - result = getalnum(&p, option, sizeof(option)); - if(result) { - /* skip option if name is too long */ - option[0] = '\0'; - } - while(*p && ISBLANK(*p)) - p++; - if(*p != '=') - return CURLE_OK; - p++; - while(*p && ISBLANK(*p)) - p++; - if(!*p) - return CURLE_OK; - if(*p == '\"') { - /* quoted value */ - p++; - quoted = TRUE; - } - value_ptr = p; - if(quoted) { - while(*p && *p != '\"') - p++; - if(!*p++) - return CURLE_OK; - } - else { - while(*p && !ISBLANK(*p) && *p!= ';' && *p != ',') - p++; - } - num = strtoul(value_ptr, &end_ptr, 10); - if((end_ptr != value_ptr) && (num < ULONG_MAX)) { - if(strcasecompare("ma", option)) - maxage = num; - else if(strcasecompare("persist", option) && (num == 1)) - persist = TRUE; - } - } - if(dstalpnid && valid) { - 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, dsthost, - srcalpnid, dstalpnid, - srcport, dstport); - if(as) { - /* The expires time also needs to take the Age: value (if any) into - account. [See RFC 7838 section 3.1] */ - as->expires = maxage + time(NULL); - as->persist = persist; - Curl_llist_insert_next(&asi->list, asi->list.tail, as, &as->node); - infof(data, "Added alt-svc: %s:%d over %s", dsthost, dstport, - Curl_alpnid2str(dstalpnid)); - } - } - } - else - break; - /* after the double quote there can be a comma if there's another - string or a semicolon if no more */ - if(*p == ',') { - /* comma means another alternative is presented */ - p++; - result = getalnum(&p, alpnbuf, sizeof(alpnbuf)); - if(result) - break; - } - } - else - break; - } while(*p && (*p != ';') && (*p != '\n') && (*p != '\r')); - - 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_element *e; - struct Curl_llist_element *n; - time_t now = time(NULL); - DEBUGASSERT(asi); - DEBUGASSERT(srchost); - DEBUGASSERT(dstentry); - - for(e = asi->list.head; e; e = n) { - struct altsvc *as = e->ptr; - n = e->next; - if(as->expires < now) { - /* an expired entry, remove */ - Curl_llist_remove(&asi->list, e, NULL); - altsvc_free(as); - continue; - } - if((as->src.alpnid == srcalpnid) && - hostcompare(srchost, as->src.host) && - (as->src.port == srcport) && - (versions & as->dst.alpnid)) { - /* match */ - *dstentry = as; - return TRUE; - } - } - return FALSE; -} - -#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_ALTSVC */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2019 - 2022, 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_get_line.h"
+#include "strcase.h"
+#include "parsedate.h"
+#include "sendf.h"
+#include "warnless.h"
+#include "fopen.h"
+#include "rename.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define MAX_ALTSVC_LINE 4095
+#define MAX_ALTSVC_DATELENSTR "64"
+#define MAX_ALTSVC_DATELEN 64
+#define MAX_ALTSVC_HOSTLENSTR "512"
+#define MAX_ALTSVC_HOSTLEN 512
+#define MAX_ALTSVC_ALPNLENSTR "10"
+#define MAX_ALTSVC_ALPNLEN 10
+
+#define H3VERSION "h3"
+
+static enum alpnid alpn2alpnid(char *name)
+{
+ if(strcasecompare(name, "h1"))
+ return ALPN_h1;
+ if(strcasecompare(name, "h2"))
+ return ALPN_h2;
+ if(strcasecompare(name, H3VERSION))
+ return ALPN_h3;
+ return ALPN_none; /* unknown, probably rubbish input */
+}
+
+/* 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,
+ const char *dsthost,
+ enum alpnid srcalpnid,
+ enum alpnid dstalpnid,
+ unsigned int srcport,
+ unsigned int dstport)
+{
+ struct altsvc *as = calloc(sizeof(struct altsvc), 1);
+ size_t hlen;
+ if(!as)
+ return NULL;
+ hlen = strlen(srchost);
+ DEBUGASSERT(hlen);
+ as->src.host = strdup(srchost);
+ if(!as->src.host)
+ goto error;
+ if(hlen && (srchost[hlen - 1] == '.'))
+ /* strip off trailing any dot */
+ as->src.host[--hlen] = 0;
+ as->dst.host = strdup(dsthost);
+ if(!as->dst.host)
+ goto error;
+
+ as->src.alpnid = srcalpnid;
+ as->dst.alpnid = dstalpnid;
+ as->src.port = curlx_ultous(srcport);
+ as->dst.port = curlx_ultous(dstport);
+
+ return as;
+ error:
+ altsvc_free(as);
+ return NULL;
+}
+
+static struct altsvc *altsvc_create(char *srchost,
+ char *dsthost,
+ char *srcalpn,
+ char *dstalpn,
+ unsigned int srcport,
+ unsigned int dstport)
+{
+ enum alpnid dstalpnid = alpn2alpnid(dstalpn);
+ enum alpnid srcalpnid = alpn2alpnid(srcalpn);
+ if(!srcalpnid || !dstalpnid)
+ return NULL;
+ return altsvc_createid(srchost, dsthost, srcalpnid, dstalpnid,
+ srcport, dstport);
+}
+
+/* only returns SERIOUS errors */
+static CURLcode altsvc_add(struct altsvcinfo *asi, char *line)
+{
+ /* Example line:
+ h2 example.com 443 h3 shiny.example.com 8443 "20191231 10:00:00" 1
+ */
+ char srchost[MAX_ALTSVC_HOSTLEN + 1];
+ char dsthost[MAX_ALTSVC_HOSTLEN + 1];
+ char srcalpn[MAX_ALTSVC_ALPNLEN + 1];
+ char dstalpn[MAX_ALTSVC_ALPNLEN + 1];
+ char date[MAX_ALTSVC_DATELEN + 1];
+ unsigned int srcport;
+ unsigned int dstport;
+ unsigned int prio;
+ unsigned int persist;
+ int rc;
+
+ rc = sscanf(line,
+ "%" MAX_ALTSVC_ALPNLENSTR "s %" MAX_ALTSVC_HOSTLENSTR "s %u "
+ "%" MAX_ALTSVC_ALPNLENSTR "s %" MAX_ALTSVC_HOSTLENSTR "s %u "
+ "\"%" MAX_ALTSVC_DATELENSTR "[^\"]\" %u %u",
+ srcalpn, srchost, &srcport,
+ dstalpn, dsthost, &dstport,
+ date, &persist, &prio);
+ if(9 == rc) {
+ struct altsvc *as;
+ time_t expires = Curl_getdate_capped(date);
+ as = altsvc_create(srchost, dsthost, srcalpn, dstalpn, srcport, dstport);
+ if(as) {
+ as->expires = expires;
+ as->prio = prio;
+ as->persist = persist ? 1 : 0;
+ Curl_llist_insert_next(&asi->list, asi->list.tail, 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;
+ char *line = NULL;
+ FILE *fp;
+
+ /* we need a private copy of the file name 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 = fopen(file, FOPEN_READTEXT);
+ if(fp) {
+ line = malloc(MAX_ALTSVC_LINE);
+ if(!line)
+ goto fail;
+ while(Curl_get_line(line, MAX_ALTSVC_LINE, fp)) {
+ char *lineptr = line;
+ while(*lineptr && ISBLANK(*lineptr))
+ lineptr++;
+ if(*lineptr == '#')
+ /* skip commented lines */
+ continue;
+
+ altsvc_add(asi, lineptr);
+ }
+ free(line); /* free the line buffer */
+ fclose(fp);
+ }
+ return result;
+
+ fail:
+ Curl_safefree(asi->filename);
+ free(line);
+ fclose(fp);
+ return CURLE_OUT_OF_MEMORY;
+}
+
+/*
+ * Write this single altsvc entry to a single output line
+ */
+
+static CURLcode altsvc_out(struct altsvc *as, FILE *fp)
+{
+ struct tm stamp;
+ CURLcode result = Curl_gmtime(as->expires, &stamp);
+ if(result)
+ return result;
+
+ fprintf(fp,
+ "%s %s %u "
+ "%s %s %u "
+ "\"%d%02d%02d "
+ "%02d:%02d:%02d\" "
+ "%u %d\n",
+ Curl_alpnid2str(as->src.alpnid), as->src.host, as->src.port,
+ Curl_alpnid2str(as->dst.alpnid), as->dst.host, 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(sizeof(struct altsvcinfo), 1);
+ if(!asi)
+ return NULL;
+ Curl_llist_init(&asi->list, NULL);
+
+ /* set default behavior */
+ asi->flags = CURLALTSVC_H1
+#ifdef USE_HTTP2
+ | CURLALTSVC_H2
+#endif
+#ifdef ENABLE_QUIC
+ | CURLALTSVC_H3
+#endif
+ ;
+ return asi;
+}
+
+/*
+ * Curl_altsvc_load() loads alt-svc from file.
+ */
+CURLcode Curl_altsvc_load(struct altsvcinfo *asi, const char *file)
+{
+ CURLcode result;
+ DEBUGASSERT(asi);
+ result = altsvc_load(asi, file);
+ return result;
+}
+
+/*
+ * Curl_altsvc_ctrl() passes on the external bitmask.
+ */
+CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl)
+{
+ DEBUGASSERT(asi);
+ if(!ctrl)
+ /* unexpected */
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ 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)
+{
+ struct Curl_llist_element *e;
+ struct Curl_llist_element *n;
+ if(*altsvcp) {
+ struct altsvcinfo *altsvc = *altsvcp;
+ for(e = altsvc->list.head; e; e = n) {
+ struct altsvc *as = e->ptr;
+ n = e->next;
+ 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)
+{
+ struct Curl_llist_element *e;
+ struct Curl_llist_element *n;
+ 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 file name */
+ return CURLE_OK;
+
+ result = Curl_fopen(data, file, &out, &tempstore);
+ if(!result) {
+ 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 = altsvc->list.head; e; e = n) {
+ struct altsvc *as = e->ptr;
+ n = e->next;
+ result = altsvc_out(as, out);
+ if(result)
+ break;
+ }
+ fclose(out);
+ if(!result && tempstore && Curl_rename(tempstore, file))
+ result = CURLE_WRITE_ERROR;
+
+ if(result && tempstore)
+ unlink(tempstore);
+ }
+ free(tempstore);
+ return result;
+}
+
+static CURLcode getalnum(const char **ptr, char *alpnbuf, size_t buflen)
+{
+ size_t len;
+ const char *protop;
+ const char *p = *ptr;
+ while(*p && ISBLANK(*p))
+ p++;
+ protop = p;
+ while(*p && !ISBLANK(*p) && (*p != ';') && (*p != '='))
+ p++;
+ len = p - protop;
+ *ptr = p;
+
+ if(!len || (len >= buflen))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ memcpy(alpnbuf, protop, len);
+ alpnbuf[len] = 0;
+ return CURLE_OK;
+}
+
+/* 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 can't match if they have different lengths */
+ return FALSE;
+ return strncasecompare(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_element *e;
+ struct Curl_llist_element *n;
+ for(e = asi->list.head; e; e = n) {
+ struct altsvc *as = e->ptr;
+ n = e->next;
+ if((srcalpnid == as->src.alpnid) &&
+ (srcport == as->src.port) &&
+ hostcompare(srchost, as->src.host)) {
+ Curl_llist_remove(&asi->list, e, NULL);
+ altsvc_free(as);
+ }
+ }
+}
+
+#ifdef DEBUGBUILD
+/* to play well with debug builds, we can *set* a fixed time this will
+ return */
+static time_t debugtime(void *unused)
+{
+ char *timestr = getenv("CURL_TIME");
+ (void)unused;
+ if(timestr) {
+ unsigned long val = strtol(timestr, NULL, 10);
+ return (time_t)val;
+ }
+ return time(NULL);
+}
+#define time(x) debugtime(x)
+#endif
+
+#define ISNEWLINE(x) (((x) == '\n') || (x) == '\r')
+
+/*
+ * 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's contents to the right of the
+ * header name.
+ *
+ * Currently this function rejects invalid data without returning an error.
+ * Invalid host name, 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;
+ size_t len;
+ char namebuf[MAX_ALTSVC_HOSTLEN] = "";
+ char alpnbuf[MAX_ALTSVC_ALPNLEN] = "";
+ struct altsvc *as;
+ unsigned short dstport = srcport; /* the same by default */
+ CURLcode result = getalnum(&p, alpnbuf, sizeof(alpnbuf));
+ size_t entries = 0;
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+ (void)data;
+#endif
+ if(result) {
+ infof(data, "Excessive alt-svc header, ignoring.");
+ return CURLE_OK;
+ }
+
+ DEBUGASSERT(asi);
+
+ /* "clear" is a magic keyword */
+ if(strcasecompare(alpnbuf, "clear")) {
+ /* Flush cached alternatives for this source origin */
+ altsvc_flush(asi, srcalpnid, srchost, srcport);
+ return CURLE_OK;
+ }
+
+ do {
+ if(*p == '=') {
+ /* [protocol]="[host][:port]" */
+ enum alpnid dstalpnid = alpn2alpnid(alpnbuf); /* the same by default */
+ p++;
+ if(*p == '\"') {
+ const char *dsthost = "";
+ const char *value_ptr;
+ char option[32];
+ unsigned long num;
+ char *end_ptr;
+ bool quoted = FALSE;
+ time_t maxage = 24 * 3600; /* default is 24 hours */
+ bool persist = FALSE;
+ bool valid = TRUE;
+ p++;
+ if(*p != ':') {
+ /* host name starts here */
+ const char *hostp = p;
+ while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-')))
+ p++;
+ len = p - hostp;
+ if(!len || (len >= MAX_ALTSVC_HOSTLEN)) {
+ infof(data, "Excessive alt-svc host name, ignoring.");
+ valid = FALSE;
+ }
+ else {
+ memcpy(namebuf, hostp, len);
+ namebuf[len] = 0;
+ dsthost = namebuf;
+ }
+ }
+ else {
+ /* no destination name, use source host */
+ dsthost = srchost;
+ }
+ if(*p == ':') {
+ unsigned long port = 0;
+ p++;
+ if(ISDIGIT(*p))
+ /* a port number */
+ port = strtoul(p, &end_ptr, 10);
+ else
+ end_ptr = (char *)p; /* not left uninitialized */
+ if(!port || port > USHRT_MAX || end_ptr == p || *end_ptr != '\"') {
+ infof(data, "Unknown alt-svc port number, ignoring.");
+ valid = FALSE;
+ }
+ else {
+ dstport = curlx_ultous(port);
+ p = end_ptr;
+ }
+ }
+ if(*p++ != '\"')
+ break;
+ /* Handle the optional 'ma' and 'persist' flags. Unknown flags
+ are skipped. */
+ for(;;) {
+ while(ISBLANK(*p))
+ p++;
+ if(*p != ';')
+ break;
+ p++; /* pass the semicolon */
+ if(!*p || ISNEWLINE(*p))
+ break;
+ result = getalnum(&p, option, sizeof(option));
+ if(result) {
+ /* skip option if name is too long */
+ option[0] = '\0';
+ }
+ while(*p && ISBLANK(*p))
+ p++;
+ if(*p != '=')
+ return CURLE_OK;
+ p++;
+ while(*p && ISBLANK(*p))
+ p++;
+ if(!*p)
+ return CURLE_OK;
+ if(*p == '\"') {
+ /* quoted value */
+ p++;
+ quoted = TRUE;
+ }
+ value_ptr = p;
+ if(quoted) {
+ while(*p && *p != '\"')
+ p++;
+ if(!*p++)
+ return CURLE_OK;
+ }
+ else {
+ while(*p && !ISBLANK(*p) && *p!= ';' && *p != ',')
+ p++;
+ }
+ num = strtoul(value_ptr, &end_ptr, 10);
+ if((end_ptr != value_ptr) && (num < ULONG_MAX)) {
+ if(strcasecompare("ma", option))
+ maxage = num;
+ else if(strcasecompare("persist", option) && (num == 1))
+ persist = TRUE;
+ }
+ }
+ if(dstalpnid && valid) {
+ 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, dsthost,
+ srcalpnid, dstalpnid,
+ srcport, dstport);
+ if(as) {
+ /* The expires time also needs to take the Age: value (if any) into
+ account. [See RFC 7838 section 3.1] */
+ as->expires = maxage + time(NULL);
+ as->persist = persist;
+ Curl_llist_insert_next(&asi->list, asi->list.tail, as, &as->node);
+ infof(data, "Added alt-svc: %s:%d over %s", dsthost, dstport,
+ Curl_alpnid2str(dstalpnid));
+ }
+ }
+ }
+ else
+ break;
+ /* after the double quote there can be a comma if there's another
+ string or a semicolon if no more */
+ if(*p == ',') {
+ /* comma means another alternative is presented */
+ p++;
+ result = getalnum(&p, alpnbuf, sizeof(alpnbuf));
+ if(result)
+ break;
+ }
+ }
+ else
+ break;
+ } while(*p && (*p != ';') && (*p != '\n') && (*p != '\r'));
+
+ 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_element *e;
+ struct Curl_llist_element *n;
+ time_t now = time(NULL);
+ DEBUGASSERT(asi);
+ DEBUGASSERT(srchost);
+ DEBUGASSERT(dstentry);
+
+ for(e = asi->list.head; e; e = n) {
+ struct altsvc *as = e->ptr;
+ n = e->next;
+ if(as->expires < now) {
+ /* an expired entry, remove */
+ Curl_llist_remove(&asi->list, e, NULL);
+ altsvc_free(as);
+ continue;
+ }
+ if((as->src.alpnid == srcalpnid) &&
+ hostcompare(srchost, as->src.host) &&
+ (as->src.port == srcport) &&
+ (versions & as->dst.alpnid)) {
+ /* match */
+ *dstentry = as;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_ALTSVC */
diff --git a/libs/libcurl/src/altsvc.h b/libs/libcurl/src/altsvc.h index 2751d272a1..f17ea5d1e3 100644 --- a/libs/libcurl/src/altsvc.h +++ b/libs/libcurl/src/altsvc.h @@ -1,81 +1,81 @@ -#ifndef HEADER_CURL_ALTSVC_H -#define HEADER_CURL_ALTSVC_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2019 - 2022, 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" - -enum alpnid { - ALPN_none = 0, - ALPN_h1 = CURLALTSVC_H1, - ALPN_h2 = CURLALTSVC_H2, - ALPN_h3 = CURLALTSVC_H3 -}; - -struct althost { - char *host; - unsigned short port; - enum alpnid alpnid; -}; - -struct altsvc { - struct althost src; - struct althost dst; - time_t expires; - bool persist; - int prio; - struct Curl_llist_element node; -}; - -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 */ +#ifndef HEADER_CURL_ALTSVC_H
+#define HEADER_CURL_ALTSVC_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2019 - 2022, 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"
+
+enum alpnid {
+ ALPN_none = 0,
+ ALPN_h1 = CURLALTSVC_H1,
+ ALPN_h2 = CURLALTSVC_H2,
+ ALPN_h3 = CURLALTSVC_H3
+};
+
+struct althost {
+ char *host;
+ unsigned short port;
+ enum alpnid alpnid;
+};
+
+struct altsvc {
+ struct althost src;
+ struct althost dst;
+ time_t expires;
+ bool persist;
+ int prio;
+ struct Curl_llist_element node;
+};
+
+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 */
diff --git a/libs/libcurl/src/amigaos.c b/libs/libcurl/src/amigaos.c index e8c2fc02e0..78de9e7cdc 100644 --- a/libs/libcurl/src/amigaos.c +++ b/libs/libcurl/src/amigaos.c @@ -1,246 +1,246 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 run-time, - * 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 -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 == EINTR)) - raise(SIGINT); - return r; -} -#endif /* USE_AMISSL */ - -#elif !defined(USE_AMISSL) /* __amigaos4__ */ -/* - * Amiga OS3 specific code - */ - -struct Library *SocketBase = NULL; -extern int errno, h_errno; - -#ifdef __libnix__ -void __request(const char *msg); -#else -# define __request(msg) Printf(msg "\n\a") -#endif - -void Curl_amiga_cleanup(void) -{ - if(SocketBase) { - CloseLibrary(SocketBase); - SocketBase = NULL; - } -} - -CURLcode Curl_amiga_init(void) -{ - if(!SocketBase) - SocketBase = OpenLibrary("bsdsocket.library", 4); - - if(!SocketBase) { - __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)) { - __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__ */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 run-time,
+ * 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
+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 == EINTR))
+ raise(SIGINT);
+ return r;
+}
+#endif /* USE_AMISSL */
+
+#elif !defined(USE_AMISSL) /* __amigaos4__ */
+/*
+ * Amiga OS3 specific code
+ */
+
+struct Library *SocketBase = NULL;
+extern int errno, h_errno;
+
+#ifdef __libnix__
+void __request(const char *msg);
+#else
+# define __request(msg) Printf(msg "\n\a")
+#endif
+
+void Curl_amiga_cleanup(void)
+{
+ if(SocketBase) {
+ CloseLibrary(SocketBase);
+ SocketBase = NULL;
+ }
+}
+
+CURLcode Curl_amiga_init(void)
+{
+ if(!SocketBase)
+ SocketBase = OpenLibrary("bsdsocket.library", 4);
+
+ if(!SocketBase) {
+ __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)) {
+ __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__ */
diff --git a/libs/libcurl/src/amigaos.h b/libs/libcurl/src/amigaos.h index 9abfb59eac..711952dff5 100644 --- a/libs/libcurl/src/amigaos.h +++ b/libs/libcurl/src/amigaos.h @@ -1,42 +1,42 @@ -#ifndef HEADER_CURL_AMIGAOS_H -#define HEADER_CURL_AMIGAOS_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 */ - +#ifndef HEADER_CURL_AMIGAOS_H
+#define HEADER_CURL_AMIGAOS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 */
+
diff --git a/libs/libcurl/src/arpa_telnet.h b/libs/libcurl/src/arpa_telnet.h index 523f7f51e7..c78cd92cf6 100644 --- a/libs/libcurl/src/arpa_telnet.h +++ b/libs/libcurl/src/arpa_telnet.h @@ -1,110 +1,110 @@ -#ifndef HEADER_CURL_ARPA_TELNET_H -#define HEADER_CURL_ARPA_TELNET_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" -}; -#endif - -#define CURL_TELOPT_MAXIMUM CURL_TELOPT_NEW_ENVIRON - -#define CURL_TELOPT_OK(x) ((x) <= CURL_TELOPT_MAXIMUM) -#define CURL_TELOPT(x) telnetoptions[x] - -#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 WON'T 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) ) -#define CURL_TELCMD(x) telnetcmds[(x)-CURL_TELCMD_MINIMUM] - -#endif /* CURL_DISABLE_TELNET */ - -#endif /* HEADER_CURL_ARPA_TELNET_H */ +#ifndef HEADER_CURL_ARPA_TELNET_H
+#define HEADER_CURL_ARPA_TELNET_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+};
+#endif
+
+#define CURL_TELOPT_MAXIMUM CURL_TELOPT_NEW_ENVIRON
+
+#define CURL_TELOPT_OK(x) ((x) <= CURL_TELOPT_MAXIMUM)
+#define CURL_TELOPT(x) telnetoptions[x]
+
+#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 WON'T 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) )
+#define CURL_TELCMD(x) telnetcmds[(x)-CURL_TELCMD_MINIMUM]
+
+#endif /* CURL_DISABLE_TELNET */
+
+#endif /* HEADER_CURL_ARPA_TELNET_H */
diff --git a/libs/libcurl/src/asyn-ares.c b/libs/libcurl/src/asyn-ares.c index 33edba1395..3276abf877 100644 --- a/libs/libcurl/src/asyn-ares.c +++ b/libs/libcurl/src/asyn-ares.c @@ -1,938 +1,929 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -/*********************************************************************** - * 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! - **********************************************************************/ - -#ifdef CURLRES_ARES - -#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 - -#ifdef HAVE_PROCESS_H -#include <process.h> -#endif - -#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) -#undef in_addr_t -#define in_addr_t unsigned long -#endif - -#include "urldata.h" -#include "sendf.h" -#include "hostip.h" -#include "hash.h" -#include "share.h" -#include "url.h" -#include "multiif.h" -#include "inet_pton.h" -#include "connect.h" -#include "select.h" -#include "progress.h" -#include "timediff.h" - -# if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \ - defined(WIN32) -# define CARES_STATICLIB -# endif -# include <ares.h> -# include <ares_version.h> /* really old c-ares didn't include this by - itself */ - -#if ARES_VERSION >= 0x010500 -/* c-ares 1.5.0 or later, the callback proto is modified */ -#define HAVE_CARES_CALLBACK_TIMEOUTS 1 -#endif - -#if ARES_VERSION >= 0x010601 -/* IPv6 supported since 1.6.1 */ -#define HAVE_CARES_IPV6 1 -#endif - -#if ARES_VERSION >= 0x010704 -#define HAVE_CARES_SERVERS_CSV 1 -#define HAVE_CARES_LOCAL_DEV 1 -#define HAVE_CARES_SET_LOCAL 1 -#endif - -#if ARES_VERSION >= 0x010b00 -#define HAVE_CARES_PORTS_CSV 1 -#endif - -#if ARES_VERSION >= 0x011000 -/* 1.16.0 or later has ares_getaddrinfo */ -#define HAVE_CARES_GETADDRINFO 1 -#endif - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -struct thread_data { - int num_pending; /* number of outstanding c-ares requests */ - struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares - parts */ - int last_status; -#ifndef HAVE_CARES_GETADDRINFO - struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */ -#endif - char hostname[1]; -}; - -/* How long we are willing to wait for additional parallel responses after - obtaining a "definitive" one. - - This is intended to equal the c-ares default timeout. cURL always uses that - default value. Unfortunately, c-ares doesn't 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 - -/* - * Curl_resolver_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_resolver_global_init(void) -{ -#ifdef CARES_HAVE_ARES_LIBRARY_INIT - if(ares_library_init(ARES_LIB_INIT_ALL)) { - return CURLE_FAILED_INIT; - } -#endif - return CURLE_OK; -} - -/* - * Curl_resolver_global_cleanup() - * - * Called from curl_global_cleanup() to destroy global resolver environment. - * Deinitializes ares library. - */ -void Curl_resolver_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_closed(easy, socket_fd); - } -} - -/* - * Curl_resolver_init() - * - * Called from curl_easy_init() -> Curl_open() to initialize resolver - * URL-state specific environment ('resolver' member of the UrlState - * structure). Fills the passed pointer by the initialized ares_channel. - */ -CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver) -{ - int status; - struct ares_options options; - int optmask = ARES_OPT_SOCK_STATE_CB; - options.sock_state_cb = sock_state_cb; - options.sock_state_cb_data = easy; - status = ares_init_options((ares_channel*)resolver, &options, optmask); - if(status != ARES_SUCCESS) { - if(status == ARES_ENOMEM) - return CURLE_OUT_OF_MEMORY; - else - return CURLE_FAILED_INIT; - } - return CURLE_OK; - /* make sure that all other returns from this function should destroy the - ares channel before returning error! */ -} - -/* - * Curl_resolver_cleanup() - * - * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver - * URL-state specific environment ('resolver' member of the UrlState - * structure). Destroys the ares channel. - */ -void Curl_resolver_cleanup(void *resolver) -{ - ares_destroy((ares_channel)resolver); -} - -/* - * Curl_resolver_duphandle() - * - * Called from curl_easy_duphandle() to duplicate resolver URL-state specific - * environment ('resolver' member of the UrlState structure). Duplicates the - * 'from' ares channel and passes the resulting channel to the 'to' pointer. - */ -CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from) -{ - (void)from; - /* - * it would be better to call ares_dup instead, but right now - * it is not possible to set 'sock_state_cb_data' outside of - * ares_init_options - */ - return Curl_resolver_init(easy, to); -} - -static void destroy_async_data(struct Curl_async *async); - -/* - * Cancel all possibly still on-going resolves for this connection. - */ -void Curl_resolver_cancel(struct Curl_easy *data) -{ - DEBUGASSERT(data); - if(data->state.async.resolver) - ares_cancel((ares_channel)data->state.async.resolver); - destroy_async_data(&data->state.async); -} - -/* - * We're equivalent to Curl_resolver_cancel() for the c-ares resolver. We - * never block. - */ -void Curl_resolver_kill(struct Curl_easy *data) -{ - /* We don't need to check the resolver state because we can be called safely - at any time and we always do the same thing. */ - Curl_resolver_cancel(data); -} - -/* - * destroy_async_data() cleans up async resolver data. - */ -static void destroy_async_data(struct Curl_async *async) -{ - if(async->tdata) { - struct thread_data *res = async->tdata; - if(res) { - if(res->temp_ai) { - Curl_freeaddrinfo(res->temp_ai); - res->temp_ai = NULL; - } - free(res); - } - async->tdata = NULL; - } -} - -/* - * Curl_resolver_getsock() is called when someone from the outside world - * (using curl_multi_fdset()) wants to get our fd_set setup and we're 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 - */ - -int Curl_resolver_getsock(struct Curl_easy *data, - curl_socket_t *socks) -{ - struct timeval maxtime; - struct timeval timebuf; - struct timeval *timeout; - long milli; - int max = ares_getsock((ares_channel)data->state.async.resolver, - (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE); - - maxtime.tv_sec = CURL_TIMEOUT_RESOLVE; - maxtime.tv_usec = 0; - - timeout = ares_timeout((ares_channel)data->state.async.resolver, &maxtime, - &timebuf); - milli = (long)curlx_tvtoms(timeout); - if(milli == 0) - milli += 10; - Curl_expire(data, milli, EXPIRE_ASYNC_NAME); - - return max; -} - -/* - * waitperform() - * - * 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 - */ - -static int waitperform(struct Curl_easy *data, 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; - - bitmask = ares_getsock((ares_channel)data->state.async.resolver, 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, 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 won't timeout! */ - ares_process_fd((ares_channel)data->state.async.resolver, 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((ares_channel)data->state.async.resolver, - (pfd[i].revents & (POLLRDNORM|POLLIN))? - pfd[i].fd:ARES_SOCKET_BAD, - (pfd[i].revents & (POLLWRNORM|POLLOUT))? - pfd[i].fd:ARES_SOCKET_BAD); - } - return nfds; -} - -/* - * Curl_resolver_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_resolver_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **dns) -{ - struct thread_data *res = data->state.async.tdata; - CURLcode result = CURLE_OK; - - DEBUGASSERT(dns); - *dns = NULL; - - if(waitperform(data, 0) < 0) - return CURLE_UNRECOVERABLE_POLL; - -#ifndef HAVE_CARES_GETADDRINFO - /* Now that we've checked for any last minute results above, see if there are - any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer - expires. */ - if(res - && res->num_pending - /* This is only set to non-zero if the timer was started. */ - && (res->happy_eyeballs_dns_time.tv_sec - || res->happy_eyeballs_dns_time.tv_usec) - && (Curl_timediff(Curl_now(), res->happy_eyeballs_dns_time) - >= HAPPY_EYEBALLS_DNS_TIMEOUT)) { - /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer - running. */ - memset( - &res->happy_eyeballs_dns_time, 0, sizeof(res->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)data->state.async.resolver); - DEBUGASSERT(res->num_pending == 0); - } -#endif - - if(res && !res->num_pending) { - (void)Curl_addrinfo_callback(data, res->last_status, res->temp_ai); - /* temp_ai ownership is moved to the connection, so we need not free-up - them */ - res->temp_ai = NULL; - - if(!data->state.async.dns) - result = Curl_resolver_error(data); - else - *dns = data->state.async.dns; - - destroy_async_data(&data->state.async); - } - - return result; -} - -/* - * Curl_resolver_wait_resolv() - * - * 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_resolver_wait_resolv(struct Curl_easy *data, - struct Curl_dns_entry **entry) -{ - CURLcode result = CURLE_OK; - timediff_t timeout; - struct curltime now = Curl_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)data->state.async.resolver, &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(waitperform(data, timeout_ms) < 0) - return CURLE_UNRECOVERABLE_POLL; - result = Curl_resolver_is_resolved(data, entry); - - if(result || data->state.async.done) - break; - - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - else { - struct curltime now2 = Curl_now(); - timediff_t timediff = Curl_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; - } - if(result) - /* failure, so we cancel the ares operation */ - ares_cancel((ares_channel)data->state.async.resolver); - - /* Operation complete, if the lookup was successful we now have the entry - in the cache. */ - if(entry) - *entry = data->state.async.dns; - - if(result) - /* close the connection, since we can't return failure here without - cleaning up this connection properly. */ - connclose(data->conn, "c-ares resolve failed"); - - return result; -} - -#ifndef HAVE_CARES_GETADDRINFO - -/* Connects results to the list */ -static void compound_results(struct thread_data *res, - struct Curl_addrinfo *ai) -{ - if(!ai) - return; - -#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */ - if(res->temp_ai && res->temp_ai->ai_family == PF_INET6) { - /* We have results already, put the new IPv6 entries at the head of the - list. */ - struct Curl_addrinfo *temp_ai_tail = res->temp_ai; - - while(temp_ai_tail->ai_next) - temp_ai_tail = temp_ai_tail->ai_next; - - temp_ai_tail->ai_next = ai; - } - else -#endif /* CURLRES_IPV6 */ - { - /* Add the new results to the list of old results. */ - struct Curl_addrinfo *ai_tail = ai; - while(ai_tail->ai_next) - ai_tail = ai_tail->ai_next; - - ai_tail->ai_next = res->temp_ai; - res->temp_ai = ai; - } -} - -/* - * ares_query_completed_cb() is the callback that ares will call when - * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(), - * when using ares, is completed either successfully or with failure. - */ -static void query_completed_cb(void *arg, /* (struct connectdata *) */ - int status, -#ifdef HAVE_CARES_CALLBACK_TIMEOUTS - int timeouts, -#endif - struct hostent *hostent) -{ - struct Curl_easy *data = (struct Curl_easy *)arg; - struct thread_data *res; - -#ifdef HAVE_CARES_CALLBACK_TIMEOUTS - (void)timeouts; /* ignored */ -#endif - - if(ARES_EDESTRUCTION == status) - /* when this ares handle is getting destroyed, the 'arg' pointer may not - be valid so only defer it when we know the 'status' says its fine! */ - return; - - res = data->state.async.tdata; - if(res) { - res->num_pending--; - - if(CURL_ASYNC_SUCCESS == status) { - struct Curl_addrinfo *ai = Curl_he2ai(hostent, data->state.async.port); - if(ai) { - compound_results(res, ai); - } - } - /* A successful result overwrites any previous error */ - if(res->last_status != ARES_SUCCESS) - res->last_status = status; - - /* If there are responses still pending, we presume they must be the - complementary IPv4 or IPv6 lookups that we started in parallel in - Curl_resolver_getaddrinfo() (for Happy Eyeballs). If we've got a - "definitive" response from one of a set of parallel queries, we need to - think about how long we're willing to wait for more responses. */ - if(res->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's 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 don't - bother handling any other cases. */ - DEBUGASSERT(res->num_pending == 1); - - /* It's possible that one of these parallel queries could succeed - quickly, but the other could always fail or timeout (when we're - talking to a pool of DNS servers that can only successfully resolve - IPv4 address, for example). - - It's 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 don't want to wait _too_ long or users will think - requests are slow when IPv6 lookups don't 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 didn't 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 can't know exactly where in - its retry cycle we are. We could guess based on how much time has - gone by, but it doesn't really matter. Happy Eyeballs tells us that, - given usable information in hand, we simply don't 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 don't even know where in the - c-ares retry cycle each request is. - */ - res->happy_eyeballs_dns_time = Curl_now(); - Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT, - EXPIRE_HAPPY_EYEBALLS_DNS); - } - } -} -#else -/* c-ares 1.16.0 or later */ - -/* - * ares2addr() converts an address list provided by c-ares to an internal - * libcurl compatible list - */ -static struct Curl_addrinfo *ares2addr(struct ares_addrinfo_node *node) -{ - /* traverse the ares_addrinfo_node list */ - struct ares_addrinfo_node *ai; - struct Curl_addrinfo *cafirst = NULL; - struct Curl_addrinfo *calast = NULL; - int error = 0; - - for(ai = node; ai != NULL; ai = ai->ai_next) { - size_t ss_size; - struct Curl_addrinfo *ca; - /* ignore elements with unsupported address family, */ - /* settle family-specific sockaddr structure size. */ - if(ai->ai_family == AF_INET) - ss_size = sizeof(struct sockaddr_in); -#ifdef ENABLE_IPV6 - else if(ai->ai_family == AF_INET6) - ss_size = sizeof(struct sockaddr_in6); -#endif - else - continue; - - /* ignore elements without required address info */ - if(!ai->ai_addr || !(ai->ai_addrlen > 0)) - continue; - - /* ignore elements with bogus address size */ - if((size_t)ai->ai_addrlen < ss_size) - continue; - - ca = malloc(sizeof(struct Curl_addrinfo) + ss_size); - if(!ca) { - error = EAI_MEMORY; - break; - } - - /* copy each structure member individually, member ordering, */ - /* size, or padding might be different for each platform. */ - - ca->ai_flags = ai->ai_flags; - ca->ai_family = ai->ai_family; - ca->ai_socktype = ai->ai_socktype; - ca->ai_protocol = ai->ai_protocol; - ca->ai_addrlen = (curl_socklen_t)ss_size; - ca->ai_addr = NULL; - ca->ai_canonname = NULL; - ca->ai_next = NULL; - - ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo)); - memcpy(ca->ai_addr, ai->ai_addr, ss_size); - - /* if the return list is empty, this becomes the first element */ - if(!cafirst) - cafirst = ca; - - /* add this element last in the return list */ - if(calast) - calast->ai_next = ca; - calast = ca; - } - - /* if we failed, destroy the Curl_addrinfo list */ - if(error) { - Curl_freeaddrinfo(cafirst); - cafirst = NULL; - } - - return cafirst; -} - -static void addrinfo_cb(void *arg, int status, int timeouts, - struct ares_addrinfo *result) -{ - struct Curl_easy *data = (struct Curl_easy *)arg; - struct thread_data *res = data->state.async.tdata; - (void)timeouts; - if(ARES_SUCCESS == status) { - res->temp_ai = ares2addr(result->nodes); - res->last_status = CURL_ASYNC_SUCCESS; - ares_freeaddrinfo(result); - } - res->num_pending--; -} - -#endif -/* - * Curl_resolver_getaddrinfo() - when using ares - * - * 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_resolver_getaddrinfo(struct Curl_easy *data, - const char *hostname, - int port, - int *waitp) -{ - struct thread_data *res = NULL; - size_t namelen = strlen(hostname); - *waitp = 0; /* default to synchronous response */ - - res = calloc(sizeof(struct thread_data) + namelen, 1); - if(res) { - strcpy(res->hostname, hostname); - data->state.async.hostname = res->hostname; - data->state.async.port = port; - data->state.async.done = FALSE; /* not done */ - data->state.async.status = 0; /* clear */ - data->state.async.dns = NULL; /* clear */ - data->state.async.tdata = res; - - /* initial status - failed */ - res->last_status = ARES_ENOTFOUND; - -#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((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) - /* The stack seems to be IPv6-enabled */ - pf = PF_UNSPEC; -#endif /* CURLRES_IPV6 */ - hints.ai_family = pf; - hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)? - SOCK_STREAM : SOCK_DGRAM; - /* 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; - msnprintf(service, sizeof(service), "%d", port); - res->num_pending = 1; - ares_getaddrinfo((ares_channel)data->state.async.resolver, hostname, - service, &hints, addrinfo_cb, data); - } -#else - -#ifdef HAVE_CARES_IPV6 - if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { - /* The stack seems to be IPv6-enabled */ - res->num_pending = 2; - - /* areschannel is already setup in the Curl_open() function */ - ares_gethostbyname((ares_channel)data->state.async.resolver, hostname, - PF_INET, query_completed_cb, data); - ares_gethostbyname((ares_channel)data->state.async.resolver, hostname, - PF_INET6, query_completed_cb, data); - } - else -#endif - { - res->num_pending = 1; - - /* areschannel is already setup in the Curl_open() function */ - ares_gethostbyname((ares_channel)data->state.async.resolver, - hostname, PF_INET, - query_completed_cb, data); - } -#endif - *waitp = 1; /* expect asynchronous response */ - } - return NULL; /* no struct yet */ -} - -CURLcode Curl_set_dns_servers(struct Curl_easy *data, - char *servers) -{ - CURLcode result = CURLE_NOT_BUILT_IN; - int ares_result; - - /* If server is NULL or empty, this would purge all DNS servers - * from ares library, which will cause any and all queries to fail. - * So, just return OK if none are configured and don't actually make - * any changes to c-ares. This lets c-ares use it's defaults, which - * it gets from the OS (for instance from /etc/resolv.conf on Linux). - */ - if(!(servers && servers[0])) - return CURLE_OK; - -#ifdef HAVE_CARES_SERVERS_CSV -#ifdef HAVE_CARES_PORTS_CSV - ares_result = ares_set_servers_ports_csv(data->state.async.resolver, - servers); -#else - ares_result = ares_set_servers_csv(data->state.async.resolver, 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: - result = CURLE_BAD_FUNCTION_ARGUMENT; - break; - } -#else /* too old c-ares version! */ - (void)data; - (void)(ares_result); -#endif - return result; -} - -CURLcode Curl_set_dns_interface(struct Curl_easy *data, - const char *interf) -{ -#ifdef HAVE_CARES_LOCAL_DEV - if(!interf) - interf = ""; - - ares_set_local_dev((ares_channel)data->state.async.resolver, interf); - - return CURLE_OK; -#else /* c-ares version too old! */ - (void)data; - (void)interf; - return CURLE_NOT_BUILT_IN; -#endif -} - -CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data, - const char *local_ip4) -{ -#ifdef HAVE_CARES_SET_LOCAL - struct in_addr a4; - - if((!local_ip4) || (local_ip4[0] == 0)) { - a4.s_addr = 0; /* disabled: do not bind to a specific address */ - } - else { - if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) { - return CURLE_BAD_FUNCTION_ARGUMENT; - } - } - - ares_set_local_ip4((ares_channel)data->state.async.resolver, - 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_set_dns_local_ip6(struct Curl_easy *data, - const char *local_ip6) -{ -#if defined(HAVE_CARES_SET_LOCAL) && defined(ENABLE_IPV6) - unsigned char a6[INET6_ADDRSTRLEN]; - - if((!local_ip6) || (local_ip6[0] == 0)) { - /* disabled: do not bind to a specific address */ - memset(a6, 0, sizeof(a6)); - } - else { - if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) { - return CURLE_BAD_FUNCTION_ARGUMENT; - } - } - - ares_set_local_ip6((ares_channel)data->state.async.resolver, a6); - - return CURLE_OK; -#else /* c-ares version too old! */ - (void)data; - (void)local_ip6; - return CURLE_NOT_BUILT_IN; -#endif -} -#endif /* CURLRES_ARES */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+/***********************************************************************
+ * 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!
+ **********************************************************************/
+
+#ifdef CURLRES_ARES
+
+#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 "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "url.h"
+#include "multiif.h"
+#include "inet_pton.h"
+#include "connect.h"
+#include "select.h"
+#include "progress.h"
+#include "timediff.h"
+
+# if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
+ defined(WIN32)
+# define CARES_STATICLIB
+# endif
+# include <ares.h>
+# include <ares_version.h> /* really old c-ares didn't include this by
+ itself */
+
+#if ARES_VERSION >= 0x010500
+/* c-ares 1.5.0 or later, the callback proto is modified */
+#define HAVE_CARES_CALLBACK_TIMEOUTS 1
+#endif
+
+#if ARES_VERSION >= 0x010601
+/* IPv6 supported since 1.6.1 */
+#define HAVE_CARES_IPV6 1
+#endif
+
+#if ARES_VERSION >= 0x010704
+#define HAVE_CARES_SERVERS_CSV 1
+#define HAVE_CARES_LOCAL_DEV 1
+#define HAVE_CARES_SET_LOCAL 1
+#endif
+
+#if ARES_VERSION >= 0x010b00
+#define HAVE_CARES_PORTS_CSV 1
+#endif
+
+#if ARES_VERSION >= 0x011000
+/* 1.16.0 or later has ares_getaddrinfo */
+#define HAVE_CARES_GETADDRINFO 1
+#endif
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+struct thread_data {
+ int num_pending; /* number of outstanding c-ares requests */
+ struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
+ parts */
+ int last_status;
+#ifndef HAVE_CARES_GETADDRINFO
+ struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */
+#endif
+ char hostname[1];
+};
+
+/* How long we are willing to wait for additional parallel responses after
+ obtaining a "definitive" one.
+
+ This is intended to equal the c-ares default timeout. cURL always uses that
+ default value. Unfortunately, c-ares doesn't 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
+
+/*
+ * Curl_resolver_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_resolver_global_init(void)
+{
+#ifdef CARES_HAVE_ARES_LIBRARY_INIT
+ if(ares_library_init(ARES_LIB_INIT_ALL)) {
+ return CURLE_FAILED_INIT;
+ }
+#endif
+ return CURLE_OK;
+}
+
+/*
+ * Curl_resolver_global_cleanup()
+ *
+ * Called from curl_global_cleanup() to destroy global resolver environment.
+ * Deinitializes ares library.
+ */
+void Curl_resolver_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_closed(easy, socket_fd);
+ }
+}
+
+/*
+ * Curl_resolver_init()
+ *
+ * Called from curl_easy_init() -> Curl_open() to initialize resolver
+ * URL-state specific environment ('resolver' member of the UrlState
+ * structure). Fills the passed pointer by the initialized ares_channel.
+ */
+CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
+{
+ int status;
+ struct ares_options options;
+ int optmask = ARES_OPT_SOCK_STATE_CB;
+ options.sock_state_cb = sock_state_cb;
+ options.sock_state_cb_data = easy;
+ status = ares_init_options((ares_channel*)resolver, &options, optmask);
+ if(status != ARES_SUCCESS) {
+ if(status == ARES_ENOMEM)
+ return CURLE_OUT_OF_MEMORY;
+ else
+ return CURLE_FAILED_INIT;
+ }
+ return CURLE_OK;
+ /* make sure that all other returns from this function should destroy the
+ ares channel before returning error! */
+}
+
+/*
+ * Curl_resolver_cleanup()
+ *
+ * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
+ * URL-state specific environment ('resolver' member of the UrlState
+ * structure). Destroys the ares channel.
+ */
+void Curl_resolver_cleanup(void *resolver)
+{
+ ares_destroy((ares_channel)resolver);
+}
+
+/*
+ * Curl_resolver_duphandle()
+ *
+ * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
+ * environment ('resolver' member of the UrlState structure). Duplicates the
+ * 'from' ares channel and passes the resulting channel to the 'to' pointer.
+ */
+CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
+{
+ (void)from;
+ /*
+ * it would be better to call ares_dup instead, but right now
+ * it is not possible to set 'sock_state_cb_data' outside of
+ * ares_init_options
+ */
+ return Curl_resolver_init(easy, to);
+}
+
+static void destroy_async_data(struct Curl_async *async);
+
+/*
+ * Cancel all possibly still on-going resolves for this connection.
+ */
+void Curl_resolver_cancel(struct Curl_easy *data)
+{
+ DEBUGASSERT(data);
+ if(data->state.async.resolver)
+ ares_cancel((ares_channel)data->state.async.resolver);
+ destroy_async_data(&data->state.async);
+}
+
+/*
+ * We're equivalent to Curl_resolver_cancel() for the c-ares resolver. We
+ * never block.
+ */
+void Curl_resolver_kill(struct Curl_easy *data)
+{
+ /* We don't need to check the resolver state because we can be called safely
+ at any time and we always do the same thing. */
+ Curl_resolver_cancel(data);
+}
+
+/*
+ * destroy_async_data() cleans up async resolver data.
+ */
+static void destroy_async_data(struct Curl_async *async)
+{
+ if(async->tdata) {
+ struct thread_data *res = async->tdata;
+ if(res) {
+ if(res->temp_ai) {
+ Curl_freeaddrinfo(res->temp_ai);
+ res->temp_ai = NULL;
+ }
+ free(res);
+ }
+ async->tdata = NULL;
+ }
+}
+
+/*
+ * Curl_resolver_getsock() is called when someone from the outside world
+ * (using curl_multi_fdset()) wants to get our fd_set setup and we're 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
+ */
+
+int Curl_resolver_getsock(struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ struct timeval maxtime;
+ struct timeval timebuf;
+ struct timeval *timeout;
+ long milli;
+ int max = ares_getsock((ares_channel)data->state.async.resolver,
+ (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
+
+ maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
+ maxtime.tv_usec = 0;
+
+ timeout = ares_timeout((ares_channel)data->state.async.resolver, &maxtime,
+ &timebuf);
+ milli = (long)curlx_tvtoms(timeout);
+ if(milli == 0)
+ milli += 10;
+ Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
+
+ return max;
+}
+
+/*
+ * waitperform()
+ *
+ * 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
+ */
+
+static int waitperform(struct Curl_easy *data, 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;
+
+ bitmask = ares_getsock((ares_channel)data->state.async.resolver, 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, 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 won't timeout! */
+ ares_process_fd((ares_channel)data->state.async.resolver, 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((ares_channel)data->state.async.resolver,
+ (pfd[i].revents & (POLLRDNORM|POLLIN))?
+ pfd[i].fd:ARES_SOCKET_BAD,
+ (pfd[i].revents & (POLLWRNORM|POLLOUT))?
+ pfd[i].fd:ARES_SOCKET_BAD);
+ }
+ return nfds;
+}
+
+/*
+ * Curl_resolver_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_resolver_is_resolved(struct Curl_easy *data,
+ struct Curl_dns_entry **dns)
+{
+ struct thread_data *res = data->state.async.tdata;
+ CURLcode result = CURLE_OK;
+
+ DEBUGASSERT(dns);
+ *dns = NULL;
+
+ if(waitperform(data, 0) < 0)
+ return CURLE_UNRECOVERABLE_POLL;
+
+#ifndef HAVE_CARES_GETADDRINFO
+ /* Now that we've checked for any last minute results above, see if there are
+ any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer
+ expires. */
+ if(res
+ && res->num_pending
+ /* This is only set to non-zero if the timer was started. */
+ && (res->happy_eyeballs_dns_time.tv_sec
+ || res->happy_eyeballs_dns_time.tv_usec)
+ && (Curl_timediff(Curl_now(), res->happy_eyeballs_dns_time)
+ >= HAPPY_EYEBALLS_DNS_TIMEOUT)) {
+ /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer
+ running. */
+ memset(
+ &res->happy_eyeballs_dns_time, 0, sizeof(res->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)data->state.async.resolver);
+ DEBUGASSERT(res->num_pending == 0);
+ }
+#endif
+
+ if(res && !res->num_pending) {
+ (void)Curl_addrinfo_callback(data, res->last_status, res->temp_ai);
+ /* temp_ai ownership is moved to the connection, so we need not free-up
+ them */
+ res->temp_ai = NULL;
+
+ if(!data->state.async.dns)
+ result = Curl_resolver_error(data);
+ else
+ *dns = data->state.async.dns;
+
+ destroy_async_data(&data->state.async);
+ }
+
+ return result;
+}
+
+/*
+ * Curl_resolver_wait_resolv()
+ *
+ * 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_resolver_wait_resolv(struct Curl_easy *data,
+ struct Curl_dns_entry **entry)
+{
+ CURLcode result = CURLE_OK;
+ timediff_t timeout;
+ struct curltime now = Curl_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)data->state.async.resolver, &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(waitperform(data, timeout_ms) < 0)
+ return CURLE_UNRECOVERABLE_POLL;
+ result = Curl_resolver_is_resolved(data, entry);
+
+ if(result || data->state.async.done)
+ break;
+
+ if(Curl_pgrsUpdate(data))
+ result = CURLE_ABORTED_BY_CALLBACK;
+ else {
+ struct curltime now2 = Curl_now();
+ timediff_t timediff = Curl_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;
+ }
+ if(result)
+ /* failure, so we cancel the ares operation */
+ ares_cancel((ares_channel)data->state.async.resolver);
+
+ /* Operation complete, if the lookup was successful we now have the entry
+ in the cache. */
+ if(entry)
+ *entry = data->state.async.dns;
+
+ if(result)
+ /* close the connection, since we can't return failure here without
+ cleaning up this connection properly. */
+ connclose(data->conn, "c-ares resolve failed");
+
+ return result;
+}
+
+#ifndef HAVE_CARES_GETADDRINFO
+
+/* Connects results to the list */
+static void compound_results(struct thread_data *res,
+ struct Curl_addrinfo *ai)
+{
+ if(!ai)
+ return;
+
+#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
+ if(res->temp_ai && res->temp_ai->ai_family == PF_INET6) {
+ /* We have results already, put the new IPv6 entries at the head of the
+ list. */
+ struct Curl_addrinfo *temp_ai_tail = res->temp_ai;
+
+ while(temp_ai_tail->ai_next)
+ temp_ai_tail = temp_ai_tail->ai_next;
+
+ temp_ai_tail->ai_next = ai;
+ }
+ else
+#endif /* CURLRES_IPV6 */
+ {
+ /* Add the new results to the list of old results. */
+ struct Curl_addrinfo *ai_tail = ai;
+ while(ai_tail->ai_next)
+ ai_tail = ai_tail->ai_next;
+
+ ai_tail->ai_next = res->temp_ai;
+ res->temp_ai = ai;
+ }
+}
+
+/*
+ * ares_query_completed_cb() is the callback that ares will call when
+ * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
+ * when using ares, is completed either successfully or with failure.
+ */
+static void query_completed_cb(void *arg, /* (struct connectdata *) */
+ int status,
+#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
+ int timeouts,
+#endif
+ struct hostent *hostent)
+{
+ struct Curl_easy *data = (struct Curl_easy *)arg;
+ struct thread_data *res;
+
+#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
+ (void)timeouts; /* ignored */
+#endif
+
+ if(ARES_EDESTRUCTION == status)
+ /* when this ares handle is getting destroyed, the 'arg' pointer may not
+ be valid so only defer it when we know the 'status' says its fine! */
+ return;
+
+ res = data->state.async.tdata;
+ if(res) {
+ res->num_pending--;
+
+ if(CURL_ASYNC_SUCCESS == status) {
+ struct Curl_addrinfo *ai = Curl_he2ai(hostent, data->state.async.port);
+ if(ai) {
+ compound_results(res, ai);
+ }
+ }
+ /* A successful result overwrites any previous error */
+ if(res->last_status != ARES_SUCCESS)
+ res->last_status = status;
+
+ /* If there are responses still pending, we presume they must be the
+ complementary IPv4 or IPv6 lookups that we started in parallel in
+ Curl_resolver_getaddrinfo() (for Happy Eyeballs). If we've got a
+ "definitive" response from one of a set of parallel queries, we need to
+ think about how long we're willing to wait for more responses. */
+ if(res->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's 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 don't
+ bother handling any other cases. */
+ DEBUGASSERT(res->num_pending == 1);
+
+ /* It's possible that one of these parallel queries could succeed
+ quickly, but the other could always fail or timeout (when we're
+ talking to a pool of DNS servers that can only successfully resolve
+ IPv4 address, for example).
+
+ It's 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 don't want to wait _too_ long or users will think
+ requests are slow when IPv6 lookups don't 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 didn't 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 can't know exactly where in
+ its retry cycle we are. We could guess based on how much time has
+ gone by, but it doesn't really matter. Happy Eyeballs tells us that,
+ given usable information in hand, we simply don't 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 don't even know where in the
+ c-ares retry cycle each request is.
+ */
+ res->happy_eyeballs_dns_time = Curl_now();
+ Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT,
+ EXPIRE_HAPPY_EYEBALLS_DNS);
+ }
+ }
+}
+#else
+/* c-ares 1.16.0 or later */
+
+/*
+ * ares2addr() converts an address list provided by c-ares to an internal
+ * libcurl compatible list
+ */
+static struct Curl_addrinfo *ares2addr(struct ares_addrinfo_node *node)
+{
+ /* traverse the ares_addrinfo_node list */
+ struct ares_addrinfo_node *ai;
+ struct Curl_addrinfo *cafirst = NULL;
+ struct Curl_addrinfo *calast = NULL;
+ int error = 0;
+
+ for(ai = node; ai != NULL; ai = ai->ai_next) {
+ size_t ss_size;
+ struct Curl_addrinfo *ca;
+ /* ignore elements with unsupported address family, */
+ /* settle family-specific sockaddr structure size. */
+ if(ai->ai_family == AF_INET)
+ ss_size = sizeof(struct sockaddr_in);
+#ifdef ENABLE_IPV6
+ else if(ai->ai_family == AF_INET6)
+ ss_size = sizeof(struct sockaddr_in6);
+#endif
+ else
+ continue;
+
+ /* ignore elements without required address info */
+ if(!ai->ai_addr || !(ai->ai_addrlen > 0))
+ continue;
+
+ /* ignore elements with bogus address size */
+ if((size_t)ai->ai_addrlen < ss_size)
+ continue;
+
+ ca = malloc(sizeof(struct Curl_addrinfo) + ss_size);
+ if(!ca) {
+ error = EAI_MEMORY;
+ break;
+ }
+
+ /* copy each structure member individually, member ordering, */
+ /* size, or padding might be different for each platform. */
+
+ ca->ai_flags = ai->ai_flags;
+ ca->ai_family = ai->ai_family;
+ ca->ai_socktype = ai->ai_socktype;
+ ca->ai_protocol = ai->ai_protocol;
+ ca->ai_addrlen = (curl_socklen_t)ss_size;
+ ca->ai_addr = NULL;
+ ca->ai_canonname = NULL;
+ ca->ai_next = NULL;
+
+ ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
+ memcpy(ca->ai_addr, ai->ai_addr, ss_size);
+
+ /* if the return list is empty, this becomes the first element */
+ if(!cafirst)
+ cafirst = ca;
+
+ /* add this element last in the return list */
+ if(calast)
+ calast->ai_next = ca;
+ calast = ca;
+ }
+
+ /* if we failed, destroy the Curl_addrinfo list */
+ if(error) {
+ Curl_freeaddrinfo(cafirst);
+ cafirst = NULL;
+ }
+
+ return cafirst;
+}
+
+static void addrinfo_cb(void *arg, int status, int timeouts,
+ struct ares_addrinfo *result)
+{
+ struct Curl_easy *data = (struct Curl_easy *)arg;
+ struct thread_data *res = data->state.async.tdata;
+ (void)timeouts;
+ if(ARES_SUCCESS == status) {
+ res->temp_ai = ares2addr(result->nodes);
+ res->last_status = CURL_ASYNC_SUCCESS;
+ ares_freeaddrinfo(result);
+ }
+ res->num_pending--;
+}
+
+#endif
+/*
+ * Curl_resolver_getaddrinfo() - when using ares
+ *
+ * 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_resolver_getaddrinfo(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int *waitp)
+{
+ struct thread_data *res = NULL;
+ size_t namelen = strlen(hostname);
+ *waitp = 0; /* default to synchronous response */
+
+ res = calloc(sizeof(struct thread_data) + namelen, 1);
+ if(res) {
+ strcpy(res->hostname, hostname);
+ data->state.async.hostname = res->hostname;
+ data->state.async.port = port;
+ data->state.async.done = FALSE; /* not done */
+ data->state.async.status = 0; /* clear */
+ data->state.async.dns = NULL; /* clear */
+ data->state.async.tdata = res;
+
+ /* initial status - failed */
+ res->last_status = ARES_ENOTFOUND;
+
+#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((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data))
+ /* The stack seems to be IPv6-enabled */
+ pf = PF_UNSPEC;
+#endif /* CURLRES_IPV6 */
+ hints.ai_family = pf;
+ hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)?
+ SOCK_STREAM : SOCK_DGRAM;
+ /* 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;
+ msnprintf(service, sizeof(service), "%d", port);
+ res->num_pending = 1;
+ ares_getaddrinfo((ares_channel)data->state.async.resolver, hostname,
+ service, &hints, addrinfo_cb, data);
+ }
+#else
+
+#ifdef HAVE_CARES_IPV6
+ if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
+ /* The stack seems to be IPv6-enabled */
+ res->num_pending = 2;
+
+ /* areschannel is already setup in the Curl_open() function */
+ ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
+ PF_INET, query_completed_cb, data);
+ ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
+ PF_INET6, query_completed_cb, data);
+ }
+ else
+#endif
+ {
+ res->num_pending = 1;
+
+ /* areschannel is already setup in the Curl_open() function */
+ ares_gethostbyname((ares_channel)data->state.async.resolver,
+ hostname, PF_INET,
+ query_completed_cb, data);
+ }
+#endif
+ *waitp = 1; /* expect asynchronous response */
+ }
+ return NULL; /* no struct yet */
+}
+
+CURLcode Curl_set_dns_servers(struct Curl_easy *data,
+ char *servers)
+{
+ CURLcode result = CURLE_NOT_BUILT_IN;
+ int ares_result;
+
+ /* If server is NULL or empty, this would purge all DNS servers
+ * from ares library, which will cause any and all queries to fail.
+ * So, just return OK if none are configured and don't actually make
+ * any changes to c-ares. This lets c-ares use it's defaults, which
+ * it gets from the OS (for instance from /etc/resolv.conf on Linux).
+ */
+ if(!(servers && servers[0]))
+ return CURLE_OK;
+
+#ifdef HAVE_CARES_SERVERS_CSV
+#ifdef HAVE_CARES_PORTS_CSV
+ ares_result = ares_set_servers_ports_csv(data->state.async.resolver,
+ servers);
+#else
+ ares_result = ares_set_servers_csv(data->state.async.resolver, 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:
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ break;
+ }
+#else /* too old c-ares version! */
+ (void)data;
+ (void)(ares_result);
+#endif
+ return result;
+}
+
+CURLcode Curl_set_dns_interface(struct Curl_easy *data,
+ const char *interf)
+{
+#ifdef HAVE_CARES_LOCAL_DEV
+ if(!interf)
+ interf = "";
+
+ ares_set_local_dev((ares_channel)data->state.async.resolver, interf);
+
+ return CURLE_OK;
+#else /* c-ares version too old! */
+ (void)data;
+ (void)interf;
+ return CURLE_NOT_BUILT_IN;
+#endif
+}
+
+CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
+ const char *local_ip4)
+{
+#ifdef HAVE_CARES_SET_LOCAL
+ struct in_addr a4;
+
+ if((!local_ip4) || (local_ip4[0] == 0)) {
+ a4.s_addr = 0; /* disabled: do not bind to a specific address */
+ }
+ else {
+ if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+ }
+
+ ares_set_local_ip4((ares_channel)data->state.async.resolver,
+ 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_set_dns_local_ip6(struct Curl_easy *data,
+ const char *local_ip6)
+{
+#if defined(HAVE_CARES_SET_LOCAL) && defined(ENABLE_IPV6)
+ unsigned char a6[INET6_ADDRSTRLEN];
+
+ if((!local_ip6) || (local_ip6[0] == 0)) {
+ /* disabled: do not bind to a specific address */
+ memset(a6, 0, sizeof(a6));
+ }
+ else {
+ if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+ }
+
+ ares_set_local_ip6((ares_channel)data->state.async.resolver, a6);
+
+ return CURLE_OK;
+#else /* c-ares version too old! */
+ (void)data;
+ (void)local_ip6;
+ return CURLE_NOT_BUILT_IN;
+#endif
+}
+#endif /* CURLRES_ARES */
diff --git a/libs/libcurl/src/asyn-thread.c b/libs/libcurl/src/asyn-thread.c index 8b375eb5ee..b4a3f37f6d 100644 --- a/libs/libcurl/src/asyn-thread.c +++ b/libs/libcurl/src/asyn-thread.c @@ -1,767 +1,756 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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) -# ifdef HAVE_PTHREAD_H -# include <pthread.h> -# endif -#elif defined(USE_THREADS_WIN32) -# ifdef HAVE_PROCESS_H -# include <process.h> -# endif -#endif - -#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) -#undef in_addr_t -#define in_addr_t unsigned long -#endif - -#ifdef HAVE_GETADDRINFO -# define RESOLVER_ENOMEM EAI_MEMORY -#else -# define RESOLVER_ENOMEM ENOMEM -#endif - -#include "urldata.h" -#include "sendf.h" -#include "hostip.h" -#include "hash.h" -#include "share.h" -#include "url.h" -#include "multiif.h" -#include "inet_ntop.h" -#include "curl_threads.h" -#include "connect.h" -#include "socketpair.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -struct resdata { - struct curltime start; -}; - -/* - * Curl_resolver_global_init() - * Called from curl_global_init() to initialize global resolver environment. - * Does nothing here. - */ -int Curl_resolver_global_init(void) -{ - return CURLE_OK; -} - -/* - * Curl_resolver_global_cleanup() - * Called from curl_global_cleanup() to destroy global resolver environment. - * Does nothing here. - */ -void Curl_resolver_global_cleanup(void) -{ -} - -/* - * Curl_resolver_init() - * Called from curl_easy_init() -> Curl_open() to initialize resolver - * URL-state specific environment ('resolver' member of the UrlState - * structure). - */ -CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver) -{ - (void)easy; - *resolver = calloc(1, sizeof(struct resdata)); - if(!*resolver) - return CURLE_OUT_OF_MEMORY; - return CURLE_OK; -} - -/* - * Curl_resolver_cleanup() - * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver - * URL-state specific environment ('resolver' member of the UrlState - * structure). - */ -void Curl_resolver_cleanup(void *resolver) -{ - free(resolver); -} - -/* - * Curl_resolver_duphandle() - * Called from curl_easy_duphandle() to duplicate resolver URL state-specific - * environment ('resolver' member of the UrlState structure). - */ -CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from) -{ - (void)from; - return Curl_resolver_init(easy, to); -} - -static void destroy_async_data(struct Curl_async *); - -/* - * Cancel all possibly still on-going resolves for this connection. - */ -void Curl_resolver_cancel(struct Curl_easy *data) -{ - destroy_async_data(&data->state.async); -} - -/* This function is used to init a threaded resolve */ -static bool init_resolve_thread(struct Curl_easy *data, - const char *hostname, int port, - const struct addrinfo *hints); - - -/* Data for synchronization between resolver thread and its parent */ -struct thread_sync_data { - curl_mutex_t *mtx; - int done; - int port; - char *hostname; /* hostname to resolve, Curl_async.hostname - duplicate */ -#ifndef CURL_DISABLE_SOCKETPAIR - struct Curl_easy *data; - curl_socket_t sock_pair[2]; /* socket pair */ -#endif - int sock_error; - struct Curl_addrinfo *res; -#ifdef HAVE_GETADDRINFO - struct addrinfo hints; -#endif - struct thread_data *td; /* for thread-self cleanup */ -}; - -struct thread_data { - curl_thread_t thread_hnd; - unsigned int poll_interval; - timediff_t interval_end; - struct thread_sync_data tsd; -}; - -static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data) -{ - return &(data->state.async.tdata->tsd); -} - -/* Destroy resolver thread synchronization data */ -static -void destroy_thread_sync_data(struct thread_sync_data *tsd) -{ - if(tsd->mtx) { - Curl_mutex_destroy(tsd->mtx); - free(tsd->mtx); - } - - free(tsd->hostname); - - if(tsd->res) - Curl_freeaddrinfo(tsd->res); - -#ifndef CURL_DISABLE_SOCKETPAIR - /* - * close one end of the socket pair (may be done in resolver thread); - * the other end (for reading) is always closed in the parent thread. - */ - if(tsd->sock_pair[1] != CURL_SOCKET_BAD) { - sclose(tsd->sock_pair[1]); - } -#endif - memset(tsd, 0, sizeof(*tsd)); -} - -/* Initialize resolver thread synchronization data */ -static -int init_thread_sync_data(struct thread_data *td, - const char *hostname, - int port, - const struct addrinfo *hints) -{ - struct thread_sync_data *tsd = &td->tsd; - - memset(tsd, 0, sizeof(*tsd)); - - tsd->td = td; - tsd->port = port; - /* Treat the request as done until the thread actually starts so any early - * cleanup gets done properly. - */ - tsd->done = 1; -#ifdef HAVE_GETADDRINFO - DEBUGASSERT(hints); - tsd->hints = *hints; -#else - (void) hints; -#endif - - tsd->mtx = malloc(sizeof(curl_mutex_t)); - if(!tsd->mtx) - goto err_exit; - - Curl_mutex_init(tsd->mtx); - -#ifndef CURL_DISABLE_SOCKETPAIR - /* create socket pair, avoid AF_LOCAL since it doesn't build on Solaris */ - if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &tsd->sock_pair[0]) < 0) { - tsd->sock_pair[0] = CURL_SOCKET_BAD; - tsd->sock_pair[1] = CURL_SOCKET_BAD; - goto err_exit; - } -#endif - tsd->sock_error = CURL_ASYNC_SUCCESS; - - /* Copying hostname string because original can be destroyed by parent - * thread during gethostbyname execution. - */ - tsd->hostname = strdup(hostname); - if(!tsd->hostname) - goto err_exit; - - return 1; - - err_exit: -#ifndef CURL_DISABLE_SOCKETPAIR - if(tsd->sock_pair[0] != CURL_SOCKET_BAD) { - sclose(tsd->sock_pair[0]); - tsd->sock_pair[0] = CURL_SOCKET_BAD; - } -#endif - destroy_thread_sync_data(tsd); - return 0; -} - -static CURLcode getaddrinfo_complete(struct Curl_easy *data) -{ - struct thread_sync_data *tsd = conn_thread_sync_data(data); - CURLcode result; - - result = Curl_addrinfo_callback(data, tsd->sock_error, tsd->res); - /* The tsd->res structure has been copied to async.dns and perhaps the DNS - cache. Set our copy to NULL so destroy_thread_sync_data doesn't free it. - */ - tsd->res = NULL; - - return result; -} - - -#ifdef HAVE_GETADDRINFO - -/* - * getaddrinfo_thread() resolves a name and then exits. - * - * For builds without ARES, but with ENABLE_IPV6, create a resolver thread - * and wait on it. - */ -static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg) -{ - struct thread_sync_data *tsd = (struct thread_sync_data *)arg; - struct thread_data *td = tsd->td; - char service[12]; - int rc; -#ifndef CURL_DISABLE_SOCKETPAIR - char buf[1]; -#endif - - msnprintf(service, sizeof(service), "%d", tsd->port); - - rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res); - - if(rc) { - tsd->sock_error = SOCKERRNO?SOCKERRNO:rc; - if(tsd->sock_error == 0) - tsd->sock_error = RESOLVER_ENOMEM; - } - else { - Curl_addrinfo_set_port(tsd->res, tsd->port); - } - - Curl_mutex_acquire(tsd->mtx); - if(tsd->done) { - /* too late, gotta clean up the mess */ - Curl_mutex_release(tsd->mtx); - destroy_thread_sync_data(tsd); - free(td); - } - else { -#ifndef CURL_DISABLE_SOCKETPAIR - if(tsd->sock_pair[1] != CURL_SOCKET_BAD) { - /* DNS has been resolved, signal client task */ - buf[0] = 1; - if(swrite(tsd->sock_pair[1], buf, sizeof(buf)) < 0) { - /* update sock_erro to errno */ - tsd->sock_error = SOCKERRNO; - } - } -#endif - tsd->done = 1; - Curl_mutex_release(tsd->mtx); - } - - return 0; -} - -#else /* HAVE_GETADDRINFO */ - -/* - * gethostbyname_thread() resolves a name and then exits. - */ -static unsigned int CURL_STDCALL gethostbyname_thread(void *arg) -{ - struct thread_sync_data *tsd = (struct thread_sync_data *)arg; - struct thread_data *td = tsd->td; - - tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port); - - if(!tsd->res) { - tsd->sock_error = SOCKERRNO; - if(tsd->sock_error == 0) - tsd->sock_error = RESOLVER_ENOMEM; - } - - Curl_mutex_acquire(tsd->mtx); - if(tsd->done) { - /* too late, gotta clean up the mess */ - Curl_mutex_release(tsd->mtx); - destroy_thread_sync_data(tsd); - free(td); - } - else { - tsd->done = 1; - Curl_mutex_release(tsd->mtx); - } - - return 0; -} - -#endif /* HAVE_GETADDRINFO */ - -/* - * destroy_async_data() cleans up async resolver data and thread handle. - */ -static void destroy_async_data(struct Curl_async *async) -{ - if(async->tdata) { - struct thread_data *td = async->tdata; - int done; -#ifndef CURL_DISABLE_SOCKETPAIR - curl_socket_t sock_rd = td->tsd.sock_pair[0]; - struct Curl_easy *data = td->tsd.data; -#endif - - /* - * if the thread is still blocking in the resolve syscall, detach it and - * let the thread do the cleanup... - */ - Curl_mutex_acquire(td->tsd.mtx); - done = td->tsd.done; - td->tsd.done = 1; - Curl_mutex_release(td->tsd.mtx); - - if(!done) { - Curl_thread_destroy(td->thread_hnd); - } - else { - if(td->thread_hnd != curl_thread_t_null) - Curl_thread_join(&td->thread_hnd); - - destroy_thread_sync_data(&td->tsd); - - free(async->tdata); - } -#ifndef CURL_DISABLE_SOCKETPAIR - /* - * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE - * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL - */ - Curl_multi_closed(data, sock_rd); - sclose(sock_rd); -#endif - } - async->tdata = NULL; - - free(async->hostname); - async->hostname = NULL; -} - -/* - * init_resolve_thread() 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 init_resolve_thread(struct Curl_easy *data, - const char *hostname, int port, - const struct addrinfo *hints) -{ - struct thread_data *td = calloc(1, sizeof(struct thread_data)); - int err = ENOMEM; - struct Curl_async *asp = &data->state.async; - - data->state.async.tdata = td; - if(!td) - goto errno_exit; - - asp->port = port; - asp->done = FALSE; - asp->status = 0; - asp->dns = NULL; - td->thread_hnd = curl_thread_t_null; - - if(!init_thread_sync_data(td, hostname, port, hints)) { - asp->tdata = NULL; - free(td); - goto errno_exit; - } - - free(asp->hostname); - asp->hostname = strdup(hostname); - if(!asp->hostname) - goto err_exit; - - /* The thread will set this to 1 when complete. */ - td->tsd.done = 0; - -#ifdef HAVE_GETADDRINFO - td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd); -#else - td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd); -#endif - - if(!td->thread_hnd) { - /* The thread never started, so mark it as done here for proper cleanup. */ - td->tsd.done = 1; - err = errno; - goto err_exit; - } - - return TRUE; - - err_exit: - destroy_async_data(asp); - - errno_exit: - errno = err; - return FALSE; -} - -/* - * 'entry' may be NULL and then no data is returned - */ -static CURLcode thread_wait_resolv(struct Curl_easy *data, - struct Curl_dns_entry **entry, - bool report) -{ - struct thread_data *td; - CURLcode result = CURLE_OK; - - DEBUGASSERT(data); - td = data->state.async.tdata; - DEBUGASSERT(td); - DEBUGASSERT(td->thread_hnd != curl_thread_t_null); - - /* wait for the thread to resolve the name */ - if(Curl_thread_join(&td->thread_hnd)) { - if(entry) - result = getaddrinfo_complete(data); - } - else - DEBUGASSERT(0); - - data->state.async.done = TRUE; - - if(entry) - *entry = data->state.async.dns; - - if(!data->state.async.dns && report) - /* a name was not resolved, report error */ - result = Curl_resolver_error(data); - - destroy_async_data(&data->state.async); - - if(!data->state.async.dns && report) - connclose(data->conn, "asynch resolve failed"); - - 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_resolver_kill(struct Curl_easy *data) -{ - struct thread_data *td = data->state.async.tdata; - - /* If we're still resolving, we must wait for the threads to fully clean up, - unfortunately. Otherwise, we can simply cancel to clean up any resolver - data. */ - if(td && td->thread_hnd != curl_thread_t_null) - (void)thread_wait_resolv(data, NULL, FALSE); - else - Curl_resolver_cancel(data); -} - -/* - * Curl_resolver_wait_resolv() - * - * 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_resolver_wait_resolv(struct Curl_easy *data, - struct Curl_dns_entry **entry) -{ - return thread_wait_resolv(data, entry, TRUE); -} - -/* - * Curl_resolver_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_resolver_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **entry) -{ - struct thread_data *td = data->state.async.tdata; - int done = 0; - - DEBUGASSERT(entry); - *entry = NULL; - - if(!td) { - DEBUGASSERT(td); - return CURLE_COULDNT_RESOLVE_HOST; - } - - Curl_mutex_acquire(td->tsd.mtx); - done = td->tsd.done; - Curl_mutex_release(td->tsd.mtx); - - if(done) { - getaddrinfo_complete(data); - - if(!data->state.async.dns) { - CURLcode result = Curl_resolver_error(data); - destroy_async_data(&data->state.async); - return result; - } - destroy_async_data(&data->state.async); - *entry = data->state.async.dns; - } - 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 = Curl_timediff(Curl_now(), - data->progress.t_startsingle); - if(elapsed < 0) - elapsed = 0; - - if(td->poll_interval == 0) - /* Start at 1ms poll interval */ - td->poll_interval = 1; - else if(elapsed >= td->interval_end) - /* Back-off exponentially if last interval expired */ - td->poll_interval *= 2; - - if(td->poll_interval > 250) - td->poll_interval = 250; - - td->interval_end = elapsed + td->poll_interval; - Curl_expire(data, td->poll_interval, EXPIRE_ASYNC_NAME); - } - - return CURLE_OK; -} - -int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks) -{ - int ret_val = 0; - timediff_t milli; - timediff_t ms; - struct resdata *reslv = (struct resdata *)data->state.async.resolver; -#ifndef CURL_DISABLE_SOCKETPAIR - struct thread_data *td = data->state.async.tdata; -#else - (void)socks; -#endif - -#ifndef CURL_DISABLE_SOCKETPAIR - if(td) { - /* return read fd to client for polling the DNS resolution status */ - socks[0] = td->tsd.sock_pair[0]; - td->tsd.data = data; - ret_val = GETSOCK_READSOCK(0); - } - else { -#endif - ms = Curl_timediff(Curl_now(), reslv->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); -#ifndef CURL_DISABLE_SOCKETPAIR - } -#endif - - - return ret_val; -} - -#ifndef HAVE_GETADDRINFO -/* - * Curl_getaddrinfo() - for platforms without getaddrinfo - */ -struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, - const char *hostname, - int port, - int *waitp) -{ - struct resdata *reslv = (struct resdata *)data->state.async.resolver; - - *waitp = 0; /* default to synchronous response */ - - reslv->start = Curl_now(); - - /* fire up a new resolver thread! */ - if(init_resolve_thread(data, hostname, port, NULL)) { - *waitp = 1; /* expect asynchronous response */ - return NULL; - } - - failf(data, "getaddrinfo() thread failed"); - - return NULL; -} - -#else /* !HAVE_GETADDRINFO */ - -/* - * Curl_resolver_getaddrinfo() - for getaddrinfo - */ -struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data, - const char *hostname, - int port, - int *waitp) -{ - struct addrinfo hints; - int pf = PF_INET; - struct resdata *reslv = (struct resdata *)data->state.async.resolver; - - *waitp = 0; /* default to synchronous response */ - -#ifdef CURLRES_IPV6 - if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) - /* The stack seems to be IPv6-enabled */ - pf = PF_UNSPEC; -#endif /* CURLRES_IPV6 */ - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = pf; - hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)? - SOCK_STREAM : SOCK_DGRAM; - - reslv->start = Curl_now(); - /* fire up a new resolver thread! */ - if(init_resolve_thread(data, hostname, port, &hints)) { - *waitp = 1; /* expect asynchronous response */ - return NULL; - } - - failf(data, "getaddrinfo() thread failed to start"); - return NULL; - -} - -#endif /* !HAVE_GETADDRINFO */ - -CURLcode Curl_set_dns_servers(struct Curl_easy *data, - char *servers) -{ - (void)data; - (void)servers; - return CURLE_NOT_BUILT_IN; - -} - -CURLcode Curl_set_dns_interface(struct Curl_easy *data, - const char *interf) -{ - (void)data; - (void)interf; - return CURLE_NOT_BUILT_IN; -} - -CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data, - const char *local_ip4) -{ - (void)data; - (void)local_ip4; - return CURLE_NOT_BUILT_IN; -} - -CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data, - const char *local_ip6) -{ - (void)data; - (void)local_ip6; - return CURLE_NOT_BUILT_IN; -} - -#endif /* CURLRES_THREADED */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+#else
+# define RESOLVER_ENOMEM ENOMEM
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "url.h"
+#include "multiif.h"
+#include "inet_ntop.h"
+#include "curl_threads.h"
+#include "connect.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+struct resdata {
+ struct curltime start;
+};
+
+/*
+ * Curl_resolver_global_init()
+ * Called from curl_global_init() to initialize global resolver environment.
+ * Does nothing here.
+ */
+int Curl_resolver_global_init(void)
+{
+ return CURLE_OK;
+}
+
+/*
+ * Curl_resolver_global_cleanup()
+ * Called from curl_global_cleanup() to destroy global resolver environment.
+ * Does nothing here.
+ */
+void Curl_resolver_global_cleanup(void)
+{
+}
+
+/*
+ * Curl_resolver_init()
+ * Called from curl_easy_init() -> Curl_open() to initialize resolver
+ * URL-state specific environment ('resolver' member of the UrlState
+ * structure).
+ */
+CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
+{
+ (void)easy;
+ *resolver = calloc(1, sizeof(struct resdata));
+ if(!*resolver)
+ return CURLE_OUT_OF_MEMORY;
+ return CURLE_OK;
+}
+
+/*
+ * Curl_resolver_cleanup()
+ * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
+ * URL-state specific environment ('resolver' member of the UrlState
+ * structure).
+ */
+void Curl_resolver_cleanup(void *resolver)
+{
+ free(resolver);
+}
+
+/*
+ * Curl_resolver_duphandle()
+ * Called from curl_easy_duphandle() to duplicate resolver URL state-specific
+ * environment ('resolver' member of the UrlState structure).
+ */
+CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
+{
+ (void)from;
+ return Curl_resolver_init(easy, to);
+}
+
+static void destroy_async_data(struct Curl_async *);
+
+/*
+ * Cancel all possibly still on-going resolves for this connection.
+ */
+void Curl_resolver_cancel(struct Curl_easy *data)
+{
+ destroy_async_data(&data->state.async);
+}
+
+/* This function is used to init a threaded resolve */
+static bool init_resolve_thread(struct Curl_easy *data,
+ const char *hostname, int port,
+ const struct addrinfo *hints);
+
+
+/* Data for synchronization between resolver thread and its parent */
+struct thread_sync_data {
+ curl_mutex_t *mtx;
+ int done;
+ int port;
+ char *hostname; /* hostname to resolve, Curl_async.hostname
+ duplicate */
+#ifndef CURL_DISABLE_SOCKETPAIR
+ struct Curl_easy *data;
+ curl_socket_t sock_pair[2]; /* socket pair */
+#endif
+ int sock_error;
+ struct Curl_addrinfo *res;
+#ifdef HAVE_GETADDRINFO
+ struct addrinfo hints;
+#endif
+ struct thread_data *td; /* for thread-self cleanup */
+};
+
+struct thread_data {
+ curl_thread_t thread_hnd;
+ unsigned int poll_interval;
+ timediff_t interval_end;
+ struct thread_sync_data tsd;
+};
+
+static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data)
+{
+ return &(data->state.async.tdata->tsd);
+}
+
+/* Destroy resolver thread synchronization data */
+static
+void destroy_thread_sync_data(struct thread_sync_data *tsd)
+{
+ if(tsd->mtx) {
+ Curl_mutex_destroy(tsd->mtx);
+ free(tsd->mtx);
+ }
+
+ free(tsd->hostname);
+
+ if(tsd->res)
+ Curl_freeaddrinfo(tsd->res);
+
+#ifndef CURL_DISABLE_SOCKETPAIR
+ /*
+ * close one end of the socket pair (may be done in resolver thread);
+ * the other end (for reading) is always closed in the parent thread.
+ */
+ if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
+ sclose(tsd->sock_pair[1]);
+ }
+#endif
+ memset(tsd, 0, sizeof(*tsd));
+}
+
+/* Initialize resolver thread synchronization data */
+static
+int init_thread_sync_data(struct thread_data *td,
+ const char *hostname,
+ int port,
+ const struct addrinfo *hints)
+{
+ struct thread_sync_data *tsd = &td->tsd;
+
+ memset(tsd, 0, sizeof(*tsd));
+
+ tsd->td = td;
+ tsd->port = port;
+ /* Treat the request as done until the thread actually starts so any early
+ * cleanup gets done properly.
+ */
+ tsd->done = 1;
+#ifdef HAVE_GETADDRINFO
+ DEBUGASSERT(hints);
+ tsd->hints = *hints;
+#else
+ (void) hints;
+#endif
+
+ tsd->mtx = malloc(sizeof(curl_mutex_t));
+ if(!tsd->mtx)
+ goto err_exit;
+
+ Curl_mutex_init(tsd->mtx);
+
+#ifndef CURL_DISABLE_SOCKETPAIR
+ /* create socket pair, avoid AF_LOCAL since it doesn't build on Solaris */
+ if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &tsd->sock_pair[0]) < 0) {
+ tsd->sock_pair[0] = CURL_SOCKET_BAD;
+ tsd->sock_pair[1] = CURL_SOCKET_BAD;
+ goto err_exit;
+ }
+#endif
+ tsd->sock_error = CURL_ASYNC_SUCCESS;
+
+ /* Copying hostname string because original can be destroyed by parent
+ * thread during gethostbyname execution.
+ */
+ tsd->hostname = strdup(hostname);
+ if(!tsd->hostname)
+ goto err_exit;
+
+ return 1;
+
+ err_exit:
+#ifndef CURL_DISABLE_SOCKETPAIR
+ if(tsd->sock_pair[0] != CURL_SOCKET_BAD) {
+ sclose(tsd->sock_pair[0]);
+ tsd->sock_pair[0] = CURL_SOCKET_BAD;
+ }
+#endif
+ destroy_thread_sync_data(tsd);
+ return 0;
+}
+
+static CURLcode getaddrinfo_complete(struct Curl_easy *data)
+{
+ struct thread_sync_data *tsd = conn_thread_sync_data(data);
+ CURLcode result;
+
+ result = Curl_addrinfo_callback(data, tsd->sock_error, tsd->res);
+ /* The tsd->res structure has been copied to async.dns and perhaps the DNS
+ cache. Set our copy to NULL so destroy_thread_sync_data doesn't free it.
+ */
+ tsd->res = NULL;
+
+ return result;
+}
+
+
+#ifdef HAVE_GETADDRINFO
+
+/*
+ * getaddrinfo_thread() resolves a name and then exits.
+ *
+ * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
+ * and wait on it.
+ */
+static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg)
+{
+ struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
+ struct thread_data *td = tsd->td;
+ char service[12];
+ int rc;
+#ifndef CURL_DISABLE_SOCKETPAIR
+ char buf[1];
+#endif
+
+ msnprintf(service, sizeof(service), "%d", tsd->port);
+
+ rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
+
+ if(rc) {
+ tsd->sock_error = SOCKERRNO?SOCKERRNO:rc;
+ if(tsd->sock_error == 0)
+ tsd->sock_error = RESOLVER_ENOMEM;
+ }
+ else {
+ Curl_addrinfo_set_port(tsd->res, tsd->port);
+ }
+
+ Curl_mutex_acquire(tsd->mtx);
+ if(tsd->done) {
+ /* too late, gotta clean up the mess */
+ Curl_mutex_release(tsd->mtx);
+ destroy_thread_sync_data(tsd);
+ free(td);
+ }
+ else {
+#ifndef CURL_DISABLE_SOCKETPAIR
+ if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
+ /* DNS has been resolved, signal client task */
+ buf[0] = 1;
+ if(swrite(tsd->sock_pair[1], buf, sizeof(buf)) < 0) {
+ /* update sock_erro to errno */
+ tsd->sock_error = SOCKERRNO;
+ }
+ }
+#endif
+ tsd->done = 1;
+ Curl_mutex_release(tsd->mtx);
+ }
+
+ return 0;
+}
+
+#else /* HAVE_GETADDRINFO */
+
+/*
+ * gethostbyname_thread() resolves a name and then exits.
+ */
+static unsigned int CURL_STDCALL gethostbyname_thread(void *arg)
+{
+ struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
+ struct thread_data *td = tsd->td;
+
+ tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
+
+ if(!tsd->res) {
+ tsd->sock_error = SOCKERRNO;
+ if(tsd->sock_error == 0)
+ tsd->sock_error = RESOLVER_ENOMEM;
+ }
+
+ Curl_mutex_acquire(tsd->mtx);
+ if(tsd->done) {
+ /* too late, gotta clean up the mess */
+ Curl_mutex_release(tsd->mtx);
+ destroy_thread_sync_data(tsd);
+ free(td);
+ }
+ else {
+ tsd->done = 1;
+ Curl_mutex_release(tsd->mtx);
+ }
+
+ return 0;
+}
+
+#endif /* HAVE_GETADDRINFO */
+
+/*
+ * destroy_async_data() cleans up async resolver data and thread handle.
+ */
+static void destroy_async_data(struct Curl_async *async)
+{
+ if(async->tdata) {
+ struct thread_data *td = async->tdata;
+ int done;
+#ifndef CURL_DISABLE_SOCKETPAIR
+ curl_socket_t sock_rd = td->tsd.sock_pair[0];
+ struct Curl_easy *data = td->tsd.data;
+#endif
+
+ /*
+ * if the thread is still blocking in the resolve syscall, detach it and
+ * let the thread do the cleanup...
+ */
+ Curl_mutex_acquire(td->tsd.mtx);
+ done = td->tsd.done;
+ td->tsd.done = 1;
+ Curl_mutex_release(td->tsd.mtx);
+
+ if(!done) {
+ Curl_thread_destroy(td->thread_hnd);
+ }
+ else {
+ if(td->thread_hnd != curl_thread_t_null)
+ Curl_thread_join(&td->thread_hnd);
+
+ destroy_thread_sync_data(&td->tsd);
+
+ free(async->tdata);
+ }
+#ifndef CURL_DISABLE_SOCKETPAIR
+ /*
+ * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE
+ * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
+ */
+ Curl_multi_closed(data, sock_rd);
+ sclose(sock_rd);
+#endif
+ }
+ async->tdata = NULL;
+
+ free(async->hostname);
+ async->hostname = NULL;
+}
+
+/*
+ * init_resolve_thread() 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 init_resolve_thread(struct Curl_easy *data,
+ const char *hostname, int port,
+ const struct addrinfo *hints)
+{
+ struct thread_data *td = calloc(1, sizeof(struct thread_data));
+ int err = ENOMEM;
+ struct Curl_async *asp = &data->state.async;
+
+ data->state.async.tdata = td;
+ if(!td)
+ goto errno_exit;
+
+ asp->port = port;
+ asp->done = FALSE;
+ asp->status = 0;
+ asp->dns = NULL;
+ td->thread_hnd = curl_thread_t_null;
+
+ if(!init_thread_sync_data(td, hostname, port, hints)) {
+ asp->tdata = NULL;
+ free(td);
+ goto errno_exit;
+ }
+
+ free(asp->hostname);
+ asp->hostname = strdup(hostname);
+ if(!asp->hostname)
+ goto err_exit;
+
+ /* The thread will set this to 1 when complete. */
+ td->tsd.done = 0;
+
+#ifdef HAVE_GETADDRINFO
+ td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
+#else
+ td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
+#endif
+
+ if(!td->thread_hnd) {
+ /* The thread never started, so mark it as done here for proper cleanup. */
+ td->tsd.done = 1;
+ err = errno;
+ goto err_exit;
+ }
+
+ return TRUE;
+
+ err_exit:
+ destroy_async_data(asp);
+
+ errno_exit:
+ errno = err;
+ return FALSE;
+}
+
+/*
+ * 'entry' may be NULL and then no data is returned
+ */
+static CURLcode thread_wait_resolv(struct Curl_easy *data,
+ struct Curl_dns_entry **entry,
+ bool report)
+{
+ struct thread_data *td;
+ CURLcode result = CURLE_OK;
+
+ DEBUGASSERT(data);
+ td = data->state.async.tdata;
+ DEBUGASSERT(td);
+ DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
+
+ /* wait for the thread to resolve the name */
+ if(Curl_thread_join(&td->thread_hnd)) {
+ if(entry)
+ result = getaddrinfo_complete(data);
+ }
+ else
+ DEBUGASSERT(0);
+
+ data->state.async.done = TRUE;
+
+ if(entry)
+ *entry = data->state.async.dns;
+
+ if(!data->state.async.dns && report)
+ /* a name was not resolved, report error */
+ result = Curl_resolver_error(data);
+
+ destroy_async_data(&data->state.async);
+
+ if(!data->state.async.dns && report)
+ connclose(data->conn, "asynch resolve failed");
+
+ 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_resolver_kill(struct Curl_easy *data)
+{
+ struct thread_data *td = data->state.async.tdata;
+
+ /* If we're still resolving, we must wait for the threads to fully clean up,
+ unfortunately. Otherwise, we can simply cancel to clean up any resolver
+ data. */
+ if(td && td->thread_hnd != curl_thread_t_null
+ && (data->set.quick_exit != 1L))
+ (void)thread_wait_resolv(data, NULL, FALSE);
+ else
+ Curl_resolver_cancel(data);
+}
+
+/*
+ * Curl_resolver_wait_resolv()
+ *
+ * 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_resolver_wait_resolv(struct Curl_easy *data,
+ struct Curl_dns_entry **entry)
+{
+ return thread_wait_resolv(data, entry, TRUE);
+}
+
+/*
+ * Curl_resolver_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_resolver_is_resolved(struct Curl_easy *data,
+ struct Curl_dns_entry **entry)
+{
+ struct thread_data *td = data->state.async.tdata;
+ int done = 0;
+
+ DEBUGASSERT(entry);
+ *entry = NULL;
+
+ if(!td) {
+ DEBUGASSERT(td);
+ return CURLE_COULDNT_RESOLVE_HOST;
+ }
+
+ Curl_mutex_acquire(td->tsd.mtx);
+ done = td->tsd.done;
+ Curl_mutex_release(td->tsd.mtx);
+
+ if(done) {
+ getaddrinfo_complete(data);
+
+ if(!data->state.async.dns) {
+ CURLcode result = Curl_resolver_error(data);
+ destroy_async_data(&data->state.async);
+ return result;
+ }
+ destroy_async_data(&data->state.async);
+ *entry = data->state.async.dns;
+ }
+ 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 = Curl_timediff(Curl_now(),
+ data->progress.t_startsingle);
+ if(elapsed < 0)
+ elapsed = 0;
+
+ if(td->poll_interval == 0)
+ /* Start at 1ms poll interval */
+ td->poll_interval = 1;
+ else if(elapsed >= td->interval_end)
+ /* Back-off exponentially if last interval expired */
+ td->poll_interval *= 2;
+
+ if(td->poll_interval > 250)
+ td->poll_interval = 250;
+
+ td->interval_end = elapsed + td->poll_interval;
+ Curl_expire(data, td->poll_interval, EXPIRE_ASYNC_NAME);
+ }
+
+ return CURLE_OK;
+}
+
+int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks)
+{
+ int ret_val = 0;
+ timediff_t milli;
+ timediff_t ms;
+ struct resdata *reslv = (struct resdata *)data->state.async.resolver;
+#ifndef CURL_DISABLE_SOCKETPAIR
+ struct thread_data *td = data->state.async.tdata;
+#else
+ (void)socks;
+#endif
+
+#ifndef CURL_DISABLE_SOCKETPAIR
+ if(td) {
+ /* return read fd to client for polling the DNS resolution status */
+ socks[0] = td->tsd.sock_pair[0];
+ td->tsd.data = data;
+ ret_val = GETSOCK_READSOCK(0);
+ }
+ else {
+#endif
+ ms = Curl_timediff(Curl_now(), reslv->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);
+#ifndef CURL_DISABLE_SOCKETPAIR
+ }
+#endif
+
+
+ return ret_val;
+}
+
+#ifndef HAVE_GETADDRINFO
+/*
+ * Curl_getaddrinfo() - for platforms without getaddrinfo
+ */
+struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int *waitp)
+{
+ struct resdata *reslv = (struct resdata *)data->state.async.resolver;
+
+ *waitp = 0; /* default to synchronous response */
+
+ reslv->start = Curl_now();
+
+ /* fire up a new resolver thread! */
+ if(init_resolve_thread(data, hostname, port, NULL)) {
+ *waitp = 1; /* expect asynchronous response */
+ return NULL;
+ }
+
+ failf(data, "getaddrinfo() thread failed");
+
+ return NULL;
+}
+
+#else /* !HAVE_GETADDRINFO */
+
+/*
+ * Curl_resolver_getaddrinfo() - for getaddrinfo
+ */
+struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int *waitp)
+{
+ struct addrinfo hints;
+ int pf = PF_INET;
+ struct resdata *reslv = (struct resdata *)data->state.async.resolver;
+
+ *waitp = 0; /* default to synchronous response */
+
+#ifdef CURLRES_IPV6
+ if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data))
+ /* The stack seems to be IPv6-enabled */
+ pf = PF_UNSPEC;
+#endif /* CURLRES_IPV6 */
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = pf;
+ hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)?
+ SOCK_STREAM : SOCK_DGRAM;
+
+ reslv->start = Curl_now();
+ /* fire up a new resolver thread! */
+ if(init_resolve_thread(data, hostname, port, &hints)) {
+ *waitp = 1; /* expect asynchronous response */
+ return NULL;
+ }
+
+ failf(data, "getaddrinfo() thread failed to start");
+ return NULL;
+
+}
+
+#endif /* !HAVE_GETADDRINFO */
+
+CURLcode Curl_set_dns_servers(struct Curl_easy *data,
+ char *servers)
+{
+ (void)data;
+ (void)servers;
+ return CURLE_NOT_BUILT_IN;
+
+}
+
+CURLcode Curl_set_dns_interface(struct Curl_easy *data,
+ const char *interf)
+{
+ (void)data;
+ (void)interf;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
+ const char *local_ip4)
+{
+ (void)data;
+ (void)local_ip4;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
+ const char *local_ip6)
+{
+ (void)data;
+ (void)local_ip6;
+ return CURLE_NOT_BUILT_IN;
+}
+
+#endif /* CURLRES_THREADED */
diff --git a/libs/libcurl/src/asyn.h b/libs/libcurl/src/asyn.h index 1aab21aaa7..8092a13396 100644 --- a/libs/libcurl/src/asyn.h +++ b/libs/libcurl/src/asyn.h @@ -1,184 +1,184 @@ -#ifndef HEADER_CURL_ASYN_H -#define HEADER_CURL_ASYN_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_addrinfo.h" - -struct addrinfo; -struct hostent; -struct Curl_easy; -struct connectdata; -struct Curl_dns_entry; - -/* - * 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_resolver_global_init() - * - * Called from curl_global_init() to initialize global resolver environment. - * Returning anything else than CURLE_OK fails curl_global_init(). - */ -int Curl_resolver_global_init(void); - -/* - * Curl_resolver_global_cleanup() - * Called from curl_global_cleanup() to destroy global resolver environment. - */ -void Curl_resolver_global_cleanup(void); - -/* - * Curl_resolver_init() - * Called from curl_easy_init() -> Curl_open() to initialize resolver - * URL-state specific environment ('resolver' member of the UrlState - * structure). Should fill the passed pointer by the initialized handler. - * Returning anything else than CURLE_OK fails curl_easy_init() with the - * correspondent code. - */ -CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver); - -/* - * Curl_resolver_cleanup() - * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver - * URL-state specific environment ('resolver' member of the UrlState - * structure). Should destroy the handler and free all resources connected to - * it. - */ -void Curl_resolver_cleanup(void *resolver); - -/* - * Curl_resolver_duphandle() - * Called from curl_easy_duphandle() to duplicate resolver URL-state specific - * environment ('resolver' member of the UrlState structure). Should - * duplicate the 'from' handle and pass the resulting handle to the 'to' - * pointer. Returning anything else than CURLE_OK causes failed - * curl_easy_duphandle() call. - */ -CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, - void *from); - -/* - * Curl_resolver_cancel(). - * - * It is called from inside other functions to cancel currently performing - * resolver request. Should also free any temporary resources allocated to - * perform a request. This never waits for resolver threads to complete. - * - * It is safe to call this when conn is in any state. - */ -void Curl_resolver_cancel(struct Curl_easy *data); - -/* - * Curl_resolver_kill(). - * - * This acts like Curl_resolver_cancel() except it will block until any threads - * associated with the resolver are complete. This never blocks for resolvers - * that do not use threads. This is intended to be the "last chance" function - * that cleans up an in-progress resolver completely (before its owner is about - * to die). - * - * It is safe to call this when conn is in any state. - */ -void Curl_resolver_kill(struct Curl_easy *data); - -/* Curl_resolver_getsock() - * - * This function is called from the multi_getsock() 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. - */ -int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *sock); - -/* - * Curl_resolver_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_resolver_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **dns); - -/* - * Curl_resolver_wait_resolv() - * - * 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. - */ -CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data, - struct Curl_dns_entry **dnsentry); - -/* - * Curl_resolver_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_resolver_getaddrinfo(struct Curl_easy *data, - const char *hostname, - int port, - int *waitp); - -#ifndef CURLRES_ASYNCH -/* convert these functions if an asynch resolver isn't used */ -#define Curl_resolver_cancel(x) Curl_nop_stmt -#define Curl_resolver_kill(x) Curl_nop_stmt -#define Curl_resolver_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST -#define Curl_resolver_wait_resolv(x,y) CURLE_COULDNT_RESOLVE_HOST -#define Curl_resolver_duphandle(x,y,z) CURLE_OK -#define Curl_resolver_init(x,y) CURLE_OK -#define Curl_resolver_global_init() CURLE_OK -#define Curl_resolver_global_cleanup() Curl_nop_stmt -#define Curl_resolver_cleanup(x) Curl_nop_stmt -#endif - -#ifdef CURLRES_ASYNCH -#define Curl_resolver_asynch() 1 -#else -#define Curl_resolver_asynch() 0 -#endif - - -/********** end of generic resolver interface functions *****************/ -#endif /* HEADER_CURL_ASYN_H */ +#ifndef HEADER_CURL_ASYN_H
+#define HEADER_CURL_ASYN_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_addrinfo.h"
+
+struct addrinfo;
+struct hostent;
+struct Curl_easy;
+struct connectdata;
+struct Curl_dns_entry;
+
+/*
+ * 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_resolver_global_init()
+ *
+ * Called from curl_global_init() to initialize global resolver environment.
+ * Returning anything else than CURLE_OK fails curl_global_init().
+ */
+int Curl_resolver_global_init(void);
+
+/*
+ * Curl_resolver_global_cleanup()
+ * Called from curl_global_cleanup() to destroy global resolver environment.
+ */
+void Curl_resolver_global_cleanup(void);
+
+/*
+ * Curl_resolver_init()
+ * Called from curl_easy_init() -> Curl_open() to initialize resolver
+ * URL-state specific environment ('resolver' member of the UrlState
+ * structure). Should fill the passed pointer by the initialized handler.
+ * Returning anything else than CURLE_OK fails curl_easy_init() with the
+ * correspondent code.
+ */
+CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver);
+
+/*
+ * Curl_resolver_cleanup()
+ * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
+ * URL-state specific environment ('resolver' member of the UrlState
+ * structure). Should destroy the handler and free all resources connected to
+ * it.
+ */
+void Curl_resolver_cleanup(void *resolver);
+
+/*
+ * Curl_resolver_duphandle()
+ * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
+ * environment ('resolver' member of the UrlState structure). Should
+ * duplicate the 'from' handle and pass the resulting handle to the 'to'
+ * pointer. Returning anything else than CURLE_OK causes failed
+ * curl_easy_duphandle() call.
+ */
+CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to,
+ void *from);
+
+/*
+ * Curl_resolver_cancel().
+ *
+ * It is called from inside other functions to cancel currently performing
+ * resolver request. Should also free any temporary resources allocated to
+ * perform a request. This never waits for resolver threads to complete.
+ *
+ * It is safe to call this when conn is in any state.
+ */
+void Curl_resolver_cancel(struct Curl_easy *data);
+
+/*
+ * Curl_resolver_kill().
+ *
+ * This acts like Curl_resolver_cancel() except it will block until any threads
+ * associated with the resolver are complete. This never blocks for resolvers
+ * that do not use threads. This is intended to be the "last chance" function
+ * that cleans up an in-progress resolver completely (before its owner is about
+ * to die).
+ *
+ * It is safe to call this when conn is in any state.
+ */
+void Curl_resolver_kill(struct Curl_easy *data);
+
+/* Curl_resolver_getsock()
+ *
+ * This function is called from the multi_getsock() 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.
+ */
+int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *sock);
+
+/*
+ * Curl_resolver_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_resolver_is_resolved(struct Curl_easy *data,
+ struct Curl_dns_entry **dns);
+
+/*
+ * Curl_resolver_wait_resolv()
+ *
+ * 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.
+ */
+CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
+ struct Curl_dns_entry **dnsentry);
+
+/*
+ * Curl_resolver_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_resolver_getaddrinfo(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int *waitp);
+
+#ifndef CURLRES_ASYNCH
+/* convert these functions if an asynch resolver isn't used */
+#define Curl_resolver_cancel(x) Curl_nop_stmt
+#define Curl_resolver_kill(x) Curl_nop_stmt
+#define Curl_resolver_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST
+#define Curl_resolver_wait_resolv(x,y) CURLE_COULDNT_RESOLVE_HOST
+#define Curl_resolver_duphandle(x,y,z) CURLE_OK
+#define Curl_resolver_init(x,y) CURLE_OK
+#define Curl_resolver_global_init() CURLE_OK
+#define Curl_resolver_global_cleanup() Curl_nop_stmt
+#define Curl_resolver_cleanup(x) Curl_nop_stmt
+#endif
+
+#ifdef CURLRES_ASYNCH
+#define Curl_resolver_asynch() 1
+#else
+#define Curl_resolver_asynch() 0
+#endif
+
+
+/********** end of generic resolver interface functions *****************/
+#endif /* HEADER_CURL_ASYN_H */
diff --git a/libs/libcurl/src/base64.c b/libs/libcurl/src/base64.c index 52654c2bc3..f8550bacf8 100644 --- a/libs/libcurl/src/base64.c +++ b/libs/libcurl/src/base64.c @@ -1,300 +1,291 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -#if !defined(CURL_DISABLE_HTTP_AUTH) || defined(USE_SSH) || \ - !defined(CURL_DISABLE_LDAP) || \ - !defined(CURL_DISABLE_SMTP) || \ - !defined(CURL_DISABLE_POP3) || \ - !defined(CURL_DISABLE_IMAP) || \ - !defined(CURL_DISABLE_DOH) || defined(USE_SSL) - -#include "urldata.h" /* for the Curl_easy definition */ -#include "warnless.h" -#include "curl_base64.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* ---- Base64 Encoding/Decoding Table --- */ -/* Padding character string starts at offset 64. */ -static const char base64[]= - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; - -/* The Base 64 encoding with a URL and filename safe alphabet, RFC 4648 - section 5 */ -static const char base64url[]= - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; - -static size_t decodeQuantum(unsigned char *dest, const char *src) -{ - size_t padding = 0; - const char *s; - unsigned long i, x = 0; - - for(i = 0, s = src; i < 4; i++, s++) { - if(*s == '=') { - x <<= 6; - padding++; - } - else { - const char *p = strchr(base64, *s); - if(p) - x = (x << 6) + curlx_uztoul(p - base64); - else - return 0; - } - } - - if(padding < 1) - dest[2] = curlx_ultouc(x & 0xFFUL); - - x >>= 8; - if(padding < 2) - dest[1] = curlx_ultouc(x & 0xFFUL); - - x >>= 8; - dest[0] = curlx_ultouc(x & 0xFFUL); - - return 3 - padding; -} - -/* - * Curl_base64_decode() - * - * Given a base64 NUL-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 Curl_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 rawlen = 0; - const char *padptr; - unsigned char *pos; - unsigned char *newstr; - - *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; - - /* Find the position of any = padding characters */ - padptr = strchr(src, '='); - if(padptr) { - padding++; - /* A maximum of two = padding characters is allowed */ - if(padptr[1] == '=') - padding++; - - /* Check the = padding characters weren't part way through the input */ - if(padptr + padding != src + srclen) - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Calculate the number of quantums */ - numQuantums = srclen / 4; - - /* 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; - - /* Decode the quantums */ - for(i = 0; i < numQuantums; i++) { - size_t result = decodeQuantum(pos, src); - if(!result) { - free(newstr); - - return CURLE_BAD_CONTENT_ENCODING; - } - - pos += result; - src += 4; - } - - /* Zero terminate */ - *pos = '\0'; - - /* Return the decoded data */ - *outptr = newstr; - *outlen = rawlen; - - return CURLE_OK; -} - -static CURLcode base64_encode(const char *table64, - const char *inputbuff, size_t insize, - char **outptr, size_t *outlen) -{ - unsigned char ibuf[3]; - unsigned char obuf[4]; - int i; - int inputparts; - char *output; - char *base64data; - const char *indata = inputbuff; - const char *padstr = &table64[64]; /* Point to padding string. */ - - *outptr = NULL; - *outlen = 0; - - if(!insize) - insize = strlen(indata); - -#if SIZEOF_SIZE_T == 4 - if(insize > UINT_MAX/4) - return CURLE_OUT_OF_MEMORY; -#endif - - base64data = output = malloc(insize * 4 / 3 + 4); - if(!output) - return CURLE_OUT_OF_MEMORY; - - while(insize > 0) { - for(i = inputparts = 0; i < 3; i++) { - if(insize > 0) { - inputparts++; - ibuf[i] = (unsigned char) *indata; - indata++; - insize--; - } - else - ibuf[i] = 0; - } - - obuf[0] = (unsigned char) ((ibuf[0] & 0xFC) >> 2); - obuf[1] = (unsigned char) (((ibuf[0] & 0x03) << 4) | \ - ((ibuf[1] & 0xF0) >> 4)); - obuf[2] = (unsigned char) (((ibuf[1] & 0x0F) << 2) | \ - ((ibuf[2] & 0xC0) >> 6)); - obuf[3] = (unsigned char) (ibuf[2] & 0x3F); - - switch(inputparts) { - case 1: /* only one byte read */ - i = msnprintf(output, 5, "%c%c%s%s", - table64[obuf[0]], - table64[obuf[1]], - padstr, - padstr); - break; - - case 2: /* two bytes read */ - i = msnprintf(output, 5, "%c%c%c%s", - table64[obuf[0]], - table64[obuf[1]], - table64[obuf[2]], - padstr); - break; - - default: - i = msnprintf(output, 5, "%c%c%c%c", - table64[obuf[0]], - table64[obuf[1]], - table64[obuf[2]], - table64[obuf[3]]); - break; - } - output += i; - } - - /* Zero terminate */ - *output = '\0'; - - /* Return the pointer to the new data (allocated memory) */ - *outptr = base64data; - - /* Return the length of the new data */ - *outlen = output - base64data; - - return CURLE_OK; -} - -/* - * Curl_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. - * - * Input length of 0 indicates input buffer holds a NUL-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 Curl_base64_encode(const char *inputbuff, size_t insize, - char **outptr, size_t *outlen) -{ - return base64_encode(base64, inputbuff, insize, outptr, outlen); -} - -/* - * Curl_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 NUL-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 Curl_base64url_encode(const char *inputbuff, size_t insize, - char **outptr, size_t *outlen) -{ - return base64_encode(base64url, inputbuff, insize, outptr, outlen); -} - -#endif /* no users so disabled */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+#if !defined(CURL_DISABLE_HTTP_AUTH) || defined(USE_SSH) || \
+ !defined(CURL_DISABLE_LDAP) || \
+ !defined(CURL_DISABLE_SMTP) || \
+ !defined(CURL_DISABLE_POP3) || \
+ !defined(CURL_DISABLE_IMAP) || \
+ !defined(CURL_DISABLE_DOH) || defined(USE_SSL)
+
+#include "urldata.h" /* for the Curl_easy definition */
+#include "warnless.h"
+#include "curl_base64.h"
+
+/* The last 2 #include files should be in this order */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* ---- Base64 Encoding/Decoding Table --- */
+/* Padding character string starts at offset 64. */
+static const char base64[]=
+ "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 };
+/*
+ * Curl_base64_decode()
+ *
+ * Given a base64 NUL-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 Curl_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));
+ /* replaces
+ {
+ unsigned char c;
+ const unsigned char *p = (const unsigned char *)base64;
+ for(c = 0; *p; c++, p++)
+ lookup[*p] = c;
+ }
+ */
+
+ /* 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,
+ const char *inputbuff, size_t insize,
+ char **outptr, size_t *outlen)
+{
+ char *output;
+ char *base64data;
+ const unsigned char *in = (unsigned char *)inputbuff;
+ const char *padstr = &table64[64]; /* Point to padding string. */
+
+ *outptr = NULL;
+ *outlen = 0;
+
+ if(!insize)
+ insize = strlen(inputbuff);
+
+#if SIZEOF_SIZE_T == 4
+ if(insize > UINT_MAX/4)
+ return CURLE_OUT_OF_MEMORY;
+#endif
+
+ 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(*padstr) {
+ *output++ = *padstr;
+ *output++ = *padstr;
+ }
+ }
+ else {
+ /* insize == 2 */
+ *output++ = table64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4) ];
+ *output++ = table64[ ((in[1] & 0x0F) << 2) ];
+ if(*padstr)
+ *output++ = *padstr;
+ }
+ }
+
+ /* Zero terminate */
+ *output = '\0';
+
+ /* Return the pointer to the new data (allocated memory) */
+ *outptr = base64data;
+
+ /* Return the length of the new data */
+ *outlen = output - base64data;
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_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.
+ *
+ * Input length of 0 indicates input buffer holds a NUL-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 Curl_base64_encode(const char *inputbuff, size_t insize,
+ char **outptr, size_t *outlen)
+{
+ return base64_encode(base64, inputbuff, insize, outptr, outlen);
+}
+
+/*
+ * Curl_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 NUL-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 Curl_base64url_encode(const char *inputbuff, size_t insize,
+ char **outptr, size_t *outlen)
+{
+ return base64_encode(base64url, inputbuff, insize, outptr, outlen);
+}
+
+#endif /* no users so disabled */
diff --git a/libs/libcurl/src/bufref.c b/libs/libcurl/src/bufref.c index 91b037431d..1d4ac09ac2 100644 --- a/libs/libcurl/src/bufref.c +++ b/libs/libcurl/src/bufref.c @@ -1,129 +1,129 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2021 - 2022, 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 "curl_memory.h" -#include "memdebug.h" - -#define SIGNATURE 0x5c48e9b2 /* Random pattern. */ - -/* - * 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 doesn't 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((void *) 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 = malloc(len + 1); - if(!cpy) - return CURLE_OUT_OF_MEMORY; - if(len) - memcpy(cpy, ptr, len); - cpy[len] = '\0'; - } - - Curl_bufref_set(br, cpy, len, curl_free); - return CURLE_OK; -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2021 - 2022, 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 "curl_memory.h"
+#include "memdebug.h"
+
+#define SIGNATURE 0x5c48e9b2 /* Random pattern. */
+
+/*
+ * 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 doesn't 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((void *) 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 = malloc(len + 1);
+ if(!cpy)
+ return CURLE_OUT_OF_MEMORY;
+ if(len)
+ memcpy(cpy, ptr, len);
+ cpy[len] = '\0';
+ }
+
+ Curl_bufref_set(br, cpy, len, curl_free);
+ return CURLE_OK;
+}
diff --git a/libs/libcurl/src/bufref.h b/libs/libcurl/src/bufref.h index 96b818b537..18881bf1f4 100644 --- a/libs/libcurl/src/bufref.h +++ b/libs/libcurl/src/bufref.h @@ -1,48 +1,48 @@ -#ifndef HEADER_CURL_BUFREF_H -#define HEADER_CURL_BUFREF_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2021 - 2022, 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 +#ifndef HEADER_CURL_BUFREF_H
+#define HEADER_CURL_BUFREF_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2021 - 2022, 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
diff --git a/libs/libcurl/src/c-hyper.c b/libs/libcurl/src/c-hyper.c index 86abcdb0fa..7aef28e8b2 100644 --- a/libs/libcurl/src/c-hyper.c +++ b/libs/libcurl/src/c-hyper.c @@ -1,1229 +1,1250 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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.haxx.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) - -#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 HAVE_NET_IF_H -#include <net/if.h> -#endif -#ifdef HAVE_SYS_IOCTL_H -#include <sys/ioctl.h> -#endif - -#ifdef HAVE_SYS_PARAM_H -#include <sys/param.h> -#endif - -#include <hyper.h> -#include "urldata.h" -#include "sendf.h" -#include "transfer.h" -#include "multiif.h" -#include "progress.h" -#include "content_encoding.h" -#include "ws.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -size_t Curl_hyper_recv(void *userp, hyper_context *ctx, - uint8_t *buf, size_t buflen) -{ - struct Curl_easy *data = userp; - struct connectdata *conn = data->conn; - CURLcode result; - ssize_t nread; - DEBUGASSERT(conn); - (void)ctx; - - result = Curl_read(data, conn->sockfd, (char *)buf, buflen, &nread); - if(result == CURLE_AGAIN) { - /* would block, register interest */ - if(data->hyp.read_waker) - hyper_waker_free(data->hyp.read_waker); - data->hyp.read_waker = hyper_context_waker(ctx); - if(!data->hyp.read_waker) { - failf(data, "Couldn't make the read hyper_context_waker"); - return HYPER_IO_ERROR; - } - return HYPER_IO_PENDING; - } - else if(result) { - failf(data, "Curl_read failed"); - return HYPER_IO_ERROR; - } - return (size_t)nread; -} - -size_t Curl_hyper_send(void *userp, hyper_context *ctx, - const uint8_t *buf, size_t buflen) -{ - struct Curl_easy *data = userp; - struct connectdata *conn = data->conn; - CURLcode result; - ssize_t nwrote; - - result = Curl_write(data, conn->sockfd, (void *)buf, buflen, &nwrote); - if(result == CURLE_AGAIN) { - /* would block, register interest */ - if(data->hyp.write_waker) - hyper_waker_free(data->hyp.write_waker); - data->hyp.write_waker = hyper_context_waker(ctx); - if(!data->hyp.write_waker) { - failf(data, "Couldn't make the write hyper_context_waker"); - return HYPER_IO_ERROR; - } - return HYPER_IO_PENDING; - } - else if(result) { - failf(data, "Curl_write failed"); - return HYPER_IO_ERROR; - } - return (size_t)nwrote; -} - -static int hyper_each_header(void *userdata, - const uint8_t *name, - size_t name_len, - const uint8_t *value, - size_t value_len) -{ - struct Curl_easy *data = (struct Curl_easy *)userdata; - size_t len; - char *headp; - CURLcode result; - int writetype; - - if(name_len + value_len + 2 > CURL_MAX_HTTP_HEADER) { - failf(data, "Too long response header"); - data->state.hresult = CURLE_OUT_OF_MEMORY; - return HYPER_ITER_BREAK; - } - - if(!data->req.bytecount) - Curl_pgrsTime(data, TIMER_STARTTRANSFER); - - Curl_dyn_reset(&data->state.headerb); - if(name_len) { - if(Curl_dyn_addf(&data->state.headerb, "%.*s: %.*s\r\n", - (int) name_len, name, (int) value_len, value)) - return HYPER_ITER_BREAK; - } - else { - if(Curl_dyn_addn(&data->state.headerb, STRCONST("\r\n"))) - return HYPER_ITER_BREAK; - } - len = Curl_dyn_len(&data->state.headerb); - headp = Curl_dyn_ptr(&data->state.headerb); - - result = Curl_http_header(data, data->conn, headp); - if(result) { - data->state.hresult = result; - return HYPER_ITER_BREAK; - } - - Curl_debug(data, CURLINFO_HEADER_IN, headp, len); - - if(!data->state.hconnect || !data->set.suppress_connect_headers) { - writetype = CLIENTWRITE_HEADER; - if(data->set.include_header) - writetype |= CLIENTWRITE_BODY; - result = Curl_client_write(data, writetype, headp, len); - if(result) { - data->state.hresult = CURLE_ABORTED_BY_CALLBACK; - return HYPER_ITER_BREAK; - } - } - - data->info.header_size += (long)len; - data->req.headerbytecount += (long)len; - return HYPER_ITER_CONTINUE; -} - -static int hyper_body_chunk(void *userdata, const hyper_buf *chunk) -{ - char *buf = (char *)hyper_buf_bytes(chunk); - size_t len = hyper_buf_len(chunk); - struct Curl_easy *data = (struct Curl_easy *)userdata; - struct SingleRequest *k = &data->req; - CURLcode result = CURLE_OK; - - if(0 == k->bodywrites++) { - bool done = FALSE; -#if defined(USE_NTLM) - struct connectdata *conn = data->conn; - if(conn->bits.close && - (((data->req.httpcode == 401) && - (conn->http_ntlm_state == NTLMSTATE_TYPE2)) || - ((data->req.httpcode == 407) && - (conn->proxy_ntlm_state == NTLMSTATE_TYPE2)))) { - infof(data, "Connection closed while negotiating NTLM"); - data->state.authproblem = TRUE; - Curl_safefree(data->req.newurl); - } -#endif - if(data->state.expect100header) { - Curl_expire_done(data, EXPIRE_100_TIMEOUT); - if(data->req.httpcode < 400) { - k->exp100 = EXP100_SEND_DATA; - if(data->hyp.exp100_waker) { - hyper_waker_wake(data->hyp.exp100_waker); - data->hyp.exp100_waker = NULL; - } - } - else { /* >= 4xx */ - k->exp100 = EXP100_FAILED; - } - } - if(data->state.hconnect && (data->req.httpcode/100 != 2) && - data->state.authproxy.done) { - done = TRUE; - result = CURLE_OK; - } - else - result = Curl_http_firstwrite(data, data->conn, &done); - if(result || done) { - infof(data, "Return early from hyper_body_chunk"); - data->state.hresult = result; - return HYPER_ITER_BREAK; - } - } - if(k->ignorebody) - return HYPER_ITER_CONTINUE; - if(0 == len) - return HYPER_ITER_CONTINUE; - Curl_debug(data, CURLINFO_DATA_IN, buf, len); - if(!data->set.http_ce_skip && k->writer_stack) - /* content-encoded data */ - result = Curl_unencode_write(data, k->writer_stack, buf, len); - else - result = Curl_client_write(data, CLIENTWRITE_BODY, buf, len); - - if(result) { - data->state.hresult = result; - return HYPER_ITER_BREAK; - } - - data->req.bytecount += len; - Curl_pgrsSetDownloadCounter(data, data->req.bytecount); - return HYPER_ITER_CONTINUE; -} - -/* - * Hyper does not consider the status line, the first line in a HTTP/1 - * response, to be a header. The libcurl API does. This function sends the - * status line in the header callback. */ -static CURLcode status_line(struct Curl_easy *data, - struct connectdata *conn, - uint16_t http_status, - int http_version, - const uint8_t *reason, size_t rlen) -{ - CURLcode result; - size_t len; - const char *vstr; - int writetype; - vstr = http_version == HYPER_HTTP_VERSION_1_1 ? "1.1" : - (http_version == HYPER_HTTP_VERSION_2 ? "2" : "1.0"); - conn->httpversion = - http_version == HYPER_HTTP_VERSION_1_1 ? 11 : - (http_version == HYPER_HTTP_VERSION_2 ? 20 : 10); - if(http_version == HYPER_HTTP_VERSION_1_0) - data->state.httpwant = CURL_HTTP_VERSION_1_0; - - if(data->state.hconnect) - /* CONNECT */ - data->info.httpproxycode = http_status; - - /* We need to set 'httpcodeq' for functions that check the response code in - a single place. */ - data->req.httpcode = http_status; - - result = Curl_http_statusline(data, conn); - if(result) - return result; - - Curl_dyn_reset(&data->state.headerb); - - result = Curl_dyn_addf(&data->state.headerb, "HTTP/%s %03d %.*s\r\n", - vstr, - (int)http_status, - (int)rlen, reason); - if(result) - return result; - len = Curl_dyn_len(&data->state.headerb); - Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&data->state.headerb), - len); - - if(!data->state.hconnect || !data->set.suppress_connect_headers) { - writetype = CLIENTWRITE_HEADER|CLIENTWRITE_STATUS; - if(data->set.include_header) - writetype |= CLIENTWRITE_BODY; - result = Curl_client_write(data, writetype, - Curl_dyn_ptr(&data->state.headerb), len); - if(result) - return result; - } - data->info.header_size += (long)len; - data->req.headerbytecount += (long)len; - data->req.httpcode = http_status; - return CURLE_OK; -} - -/* - * Hyper does not pass on the last empty response header. The libcurl API - * does. This function sends an empty header in the header callback. - */ -static CURLcode empty_header(struct Curl_easy *data) -{ - CURLcode result = Curl_http_size(data); - if(!result) { - result = hyper_each_header(data, NULL, 0, NULL, 0) ? - CURLE_WRITE_ERROR : CURLE_OK; - if(result) - failf(data, "hyperstream: couldn't pass blank header"); - } - return result; -} - -CURLcode Curl_hyper_stream(struct Curl_easy *data, - struct connectdata *conn, - int *didwhat, - bool *done, - int select_res) -{ - hyper_response *resp = NULL; - uint16_t http_status; - int http_version; - hyper_headers *headers = NULL; - hyper_body *resp_body = NULL; - struct hyptransfer *h = &data->hyp; - hyper_task *task; - hyper_task *foreach; - hyper_error *hypererr = NULL; - const uint8_t *reasonp; - size_t reason_len; - CURLcode result = CURLE_OK; - struct SingleRequest *k = &data->req; - (void)conn; - - if(k->exp100 > EXP100_SEND_DATA) { - struct curltime now = Curl_now(); - timediff_t ms = Curl_timediff(now, k->start100); - if(ms >= data->set.expect_100_timeout) { - /* we've waited long enough, continue anyway */ - k->exp100 = EXP100_SEND_DATA; - k->keepon |= KEEP_SEND; - Curl_expire_done(data, EXPIRE_100_TIMEOUT); - infof(data, "Done waiting for 100-continue"); - if(data->hyp.exp100_waker) { - hyper_waker_wake(data->hyp.exp100_waker); - data->hyp.exp100_waker = NULL; - } - } - } - - if(select_res & CURL_CSELECT_IN) { - if(h->read_waker) - hyper_waker_wake(h->read_waker); - h->read_waker = NULL; - } - if(select_res & CURL_CSELECT_OUT) { - if(h->write_waker) - hyper_waker_wake(h->write_waker); - h->write_waker = NULL; - } - - *done = FALSE; - do { - hyper_task_return_type t; - task = hyper_executor_poll(h->exec); - if(!task) { - *didwhat = KEEP_RECV; - break; - } - t = hyper_task_type(task); - switch(t) { - case HYPER_TASK_ERROR: - hypererr = hyper_task_value(task); - break; - case HYPER_TASK_RESPONSE: - resp = hyper_task_value(task); - break; - default: - break; - } - hyper_task_free(task); - - if(t == HYPER_TASK_ERROR) { - if(data->state.hresult) { - /* override Hyper's view, might not even be an error */ - result = data->state.hresult; - infof(data, "hyperstream is done (by early callback)"); - } - else { - uint8_t errbuf[256]; - size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf)); - hyper_code code = hyper_error_code(hypererr); - failf(data, "Hyper: [%d] %.*s", (int)code, (int)errlen, errbuf); - if(code == HYPERE_ABORTED_BY_CALLBACK) - result = CURLE_OK; - else if((code == HYPERE_UNEXPECTED_EOF) && !data->req.bytecount) - result = CURLE_GOT_NOTHING; - else if(code == HYPERE_INVALID_PEER_MESSAGE) - result = CURLE_UNSUPPORTED_PROTOCOL; /* maybe */ - else - result = CURLE_RECV_ERROR; - } - *done = TRUE; - hyper_error_free(hypererr); - break; - } - else if(h->endtask == task) { - /* end of transfer */ - *done = TRUE; - infof(data, "hyperstream is done"); - if(!k->bodywrites) { - /* hyper doesn't always call the body write callback */ - bool stilldone; - result = Curl_http_firstwrite(data, data->conn, &stilldone); - } - break; - } - else if(t != HYPER_TASK_RESPONSE) { - *didwhat = KEEP_RECV; - break; - } - /* HYPER_TASK_RESPONSE */ - - *didwhat = KEEP_RECV; - if(!resp) { - failf(data, "hyperstream: couldn't get response"); - return CURLE_RECV_ERROR; - } - - http_status = hyper_response_status(resp); - http_version = hyper_response_version(resp); - reasonp = hyper_response_reason_phrase(resp); - reason_len = hyper_response_reason_phrase_len(resp); - - if(http_status == 417 && data->state.expect100header) { - infof(data, "Got 417 while waiting for a 100"); - data->state.disableexpect = TRUE; - data->req.newurl = strdup(data->state.url); - Curl_done_sending(data, k); - } - - result = status_line(data, conn, - http_status, http_version, reasonp, reason_len); - if(result) - break; - - headers = hyper_response_headers(resp); - if(!headers) { - failf(data, "hyperstream: couldn't get response headers"); - result = CURLE_RECV_ERROR; - break; - } - - /* the headers are already received */ - hyper_headers_foreach(headers, hyper_each_header, data); - if(data->state.hresult) { - result = data->state.hresult; - break; - } - - result = empty_header(data); - if(result) - break; - - k->deductheadercount = - (100 <= http_status && 199 >= http_status)?k->headerbytecount:0; -#ifdef USE_WEBSOCKETS - if(k->upgr101 == UPGR101_WS) { - if(http_status == 101) { - /* verify the response */ - result = Curl_ws_accept(data); - if(result) - return result; - } - else { - failf(data, "Expected 101, got %u", k->httpcode); - result = CURLE_HTTP_RETURNED_ERROR; - break; - } - } -#endif - - /* Curl_http_auth_act() checks what authentication methods that are - * available and decides which one (if any) to use. It will set 'newurl' - * if an auth method was picked. */ - result = Curl_http_auth_act(data); - if(result) - break; - - resp_body = hyper_response_body(resp); - if(!resp_body) { - failf(data, "hyperstream: couldn't get response body"); - result = CURLE_RECV_ERROR; - break; - } - foreach = hyper_body_foreach(resp_body, hyper_body_chunk, data); - if(!foreach) { - failf(data, "hyperstream: body foreach failed"); - result = CURLE_OUT_OF_MEMORY; - break; - } - DEBUGASSERT(hyper_task_type(foreach) == HYPER_TASK_EMPTY); - if(HYPERE_OK != hyper_executor_push(h->exec, foreach)) { - failf(data, "Couldn't hyper_executor_push the body-foreach"); - result = CURLE_OUT_OF_MEMORY; - break; - } - h->endtask = foreach; - - hyper_response_free(resp); - resp = NULL; - } while(1); - if(resp) - hyper_response_free(resp); - return result; -} - -static CURLcode debug_request(struct Curl_easy *data, - const char *method, - const char *path, - bool h2) -{ - char *req = aprintf("%s %s HTTP/%s\r\n", method, path, - h2?"2":"1.1"); - if(!req) - return CURLE_OUT_OF_MEMORY; - Curl_debug(data, CURLINFO_HEADER_OUT, req, strlen(req)); - free(req); - return CURLE_OK; -} - -/* - * Given a full header line "name: value" (optional CRLF in the input, should - * be in the output), add to Hyper and send to the debug callback. - * - * Supports multiple headers. - */ - -CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers, - const char *line) -{ - const char *p; - const char *n; - size_t nlen; - const char *v; - size_t vlen; - bool newline = TRUE; - int numh = 0; - - if(!line) - return CURLE_OK; - n = line; - do { - size_t linelen = 0; - - p = strchr(n, ':'); - if(!p) - /* this is fine if we already added at least one header */ - return numh ? CURLE_OK : CURLE_BAD_FUNCTION_ARGUMENT; - nlen = p - n; - p++; /* move past the colon */ - while(*p == ' ') - p++; - v = p; - p = strchr(v, '\r'); - if(!p) { - p = strchr(v, '\n'); - if(p) - linelen = 1; /* LF only */ - else { - p = strchr(v, '\0'); - newline = FALSE; /* no newline */ - } - } - else - linelen = 2; /* CRLF ending */ - linelen += (p - n); - vlen = p - v; - - if(HYPERE_OK != hyper_headers_add(headers, (uint8_t *)n, nlen, - (uint8_t *)v, vlen)) { - failf(data, "hyper refused to add header '%s'", line); - return CURLE_OUT_OF_MEMORY; - } - if(data->set.verbose) { - char *ptr = NULL; - if(!newline) { - ptr = aprintf("%.*s\r\n", (int)linelen, line); - if(!ptr) - return CURLE_OUT_OF_MEMORY; - Curl_debug(data, CURLINFO_HEADER_OUT, ptr, linelen + 2); - free(ptr); - } - else - Curl_debug(data, CURLINFO_HEADER_OUT, (char *)n, linelen); - } - numh++; - n += linelen; - } while(newline); - return CURLE_OK; -} - -static CURLcode request_target(struct Curl_easy *data, - struct connectdata *conn, - const char *method, - bool h2, - hyper_request *req) -{ - CURLcode result; - struct dynbuf r; - - Curl_dyn_init(&r, DYN_HTTP_REQUEST); - - result = Curl_http_target(data, conn, &r); - if(result) - return result; - - if(h2 && hyper_request_set_uri_parts(req, - /* scheme */ - (uint8_t *)data->state.up.scheme, - strlen(data->state.up.scheme), - /* authority */ - (uint8_t *)conn->host.name, - strlen(conn->host.name), - /* path_and_query */ - (uint8_t *)Curl_dyn_uptr(&r), - Curl_dyn_len(&r))) { - failf(data, "error setting uri parts to hyper"); - result = CURLE_OUT_OF_MEMORY; - } - else if(!h2 && hyper_request_set_uri(req, (uint8_t *)Curl_dyn_uptr(&r), - Curl_dyn_len(&r))) { - failf(data, "error setting uri to hyper"); - result = CURLE_OUT_OF_MEMORY; - } - else - result = debug_request(data, method, Curl_dyn_ptr(&r), h2); - - Curl_dyn_free(&r); - - return result; -} - -static int uploadpostfields(void *userdata, hyper_context *ctx, - hyper_buf **chunk) -{ - struct Curl_easy *data = (struct Curl_easy *)userdata; - (void)ctx; - if(data->req.exp100 > EXP100_SEND_DATA) { - if(data->req.exp100 == EXP100_FAILED) - return HYPER_POLL_ERROR; - - /* still waiting confirmation */ - if(data->hyp.exp100_waker) - hyper_waker_free(data->hyp.exp100_waker); - data->hyp.exp100_waker = hyper_context_waker(ctx); - return HYPER_POLL_PENDING; - } - if(data->req.upload_done) - *chunk = NULL; /* nothing more to deliver */ - else { - /* send everything off in a single go */ - hyper_buf *copy = hyper_buf_copy(data->set.postfields, - (size_t)data->req.p.http->postsize); - if(copy) - *chunk = copy; - else { - data->state.hresult = CURLE_OUT_OF_MEMORY; - return HYPER_POLL_ERROR; - } - /* increasing the writebytecount here is a little premature but we - don't know exactly when the body is sent*/ - data->req.writebytecount += (size_t)data->req.p.http->postsize; - Curl_pgrsSetUploadCounter(data, data->req.writebytecount); - data->req.upload_done = TRUE; - } - return HYPER_POLL_READY; -} - -static int uploadstreamed(void *userdata, hyper_context *ctx, - hyper_buf **chunk) -{ - size_t fillcount; - struct Curl_easy *data = (struct Curl_easy *)userdata; - CURLcode result; - (void)ctx; - - if(data->req.exp100 > EXP100_SEND_DATA) { - if(data->req.exp100 == EXP100_FAILED) - return HYPER_POLL_ERROR; - - /* still waiting confirmation */ - if(data->hyp.exp100_waker) - hyper_waker_free(data->hyp.exp100_waker); - data->hyp.exp100_waker = hyper_context_waker(ctx); - return HYPER_POLL_PENDING; - } - - result = Curl_fillreadbuffer(data, data->set.upload_buffer_size, &fillcount); - if(result) { - data->state.hresult = result; - return HYPER_POLL_ERROR; - } - if(!fillcount) { - if((data->req.keepon & KEEP_SEND_PAUSE) != KEEP_SEND_PAUSE) - /* done! */ - *chunk = NULL; - else { - /* paused, save a waker */ - if(data->hyp.send_body_waker) - hyper_waker_free(data->hyp.send_body_waker); - data->hyp.send_body_waker = hyper_context_waker(ctx); - return HYPER_POLL_PENDING; - } - } - else { - hyper_buf *copy = hyper_buf_copy((uint8_t *)data->state.ulbuf, fillcount); - if(copy) - *chunk = copy; - else { - data->state.hresult = CURLE_OUT_OF_MEMORY; - return HYPER_POLL_ERROR; - } - /* increasing the writebytecount here is a little premature but we - don't know exactly when the body is sent*/ - data->req.writebytecount += fillcount; - Curl_pgrsSetUploadCounter(data, fillcount); - } - return HYPER_POLL_READY; -} - -/* - * bodysend() sets up headers in the outgoing request for a HTTP transfer that - * sends a body - */ - -static CURLcode bodysend(struct Curl_easy *data, - struct connectdata *conn, - hyper_headers *headers, - hyper_request *hyperreq, - Curl_HttpReq httpreq) -{ - struct HTTP *http = data->req.p.http; - CURLcode result = CURLE_OK; - struct dynbuf req; - if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) - Curl_pgrsSetUploadSize(data, 0); /* no request body */ - else { - hyper_body *body; - Curl_dyn_init(&req, DYN_HTTP_REQUEST); - result = Curl_http_bodysend(data, conn, &req, httpreq); - - if(!result) - result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req)); - - Curl_dyn_free(&req); - - body = hyper_body_new(); - hyper_body_set_userdata(body, data); - if(data->set.postfields) - hyper_body_set_data_func(body, uploadpostfields); - else { - result = Curl_get_upload_buffer(data); - if(result) - return result; - /* init the "upload from here" pointer */ - data->req.upload_fromhere = data->state.ulbuf; - hyper_body_set_data_func(body, uploadstreamed); - } - if(HYPERE_OK != hyper_request_set_body(hyperreq, body)) { - /* fail */ - hyper_body_free(body); - result = CURLE_OUT_OF_MEMORY; - } - } - http->sending = HTTPSEND_BODY; - return result; -} - -static CURLcode cookies(struct Curl_easy *data, - struct connectdata *conn, - hyper_headers *headers) -{ - struct dynbuf req; - CURLcode result; - Curl_dyn_init(&req, DYN_HTTP_REQUEST); - - result = Curl_http_cookies(data, conn, &req); - if(!result) - result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req)); - Curl_dyn_free(&req); - return result; -} - -/* called on 1xx responses */ -static void http1xx_cb(void *arg, struct hyper_response *resp) -{ - struct Curl_easy *data = (struct Curl_easy *)arg; - hyper_headers *headers = NULL; - CURLcode result = CURLE_OK; - uint16_t http_status; - int http_version; - const uint8_t *reasonp; - size_t reason_len; - - infof(data, "Got HTTP 1xx informational"); - - http_status = hyper_response_status(resp); - http_version = hyper_response_version(resp); - reasonp = hyper_response_reason_phrase(resp); - reason_len = hyper_response_reason_phrase_len(resp); - - result = status_line(data, data->conn, - http_status, http_version, reasonp, reason_len); - if(!result) { - headers = hyper_response_headers(resp); - if(!headers) { - failf(data, "hyperstream: couldn't get 1xx response headers"); - result = CURLE_RECV_ERROR; - } - } - data->state.hresult = result; - - if(!result) { - /* the headers are already received */ - hyper_headers_foreach(headers, hyper_each_header, data); - /* this callback also sets data->state.hresult on error */ - - if(empty_header(data)) - result = CURLE_OUT_OF_MEMORY; - } - - if(data->state.hresult) - infof(data, "ERROR in 1xx, bail out"); -} - -/* - * Curl_http() gets called from the generic multi_do() function when a HTTP - * request is to be performed. This creates and sends a properly constructed - * HTTP request. - */ -CURLcode Curl_http(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - struct hyptransfer *h = &data->hyp; - hyper_io *io = NULL; - hyper_clientconn_options *options = NULL; - hyper_task *task = NULL; /* for the handshake */ - hyper_task *sendtask = NULL; /* for the send */ - hyper_clientconn *client = NULL; - hyper_request *req = NULL; - hyper_headers *headers = NULL; - hyper_task *handshake = NULL; - CURLcode result; - const char *p_accept; /* Accept: string */ - const char *method; - Curl_HttpReq httpreq; - bool h2 = FALSE; - const char *te = NULL; /* transfer-encoding */ - hyper_code rc; - - /* Always consider the DO phase done after this function call, even if there - may be parts of the request that is not yet sent, since we can deal with - the rest of the request in the PERFORM phase. */ - *done = TRUE; - - infof(data, "Time for the Hyper dance"); - memset(h, 0, sizeof(struct hyptransfer)); - - result = Curl_http_host(data, conn); - if(result) - return result; - - Curl_http_method(data, conn, &method, &httpreq); - - /* setup the authentication headers */ - { - char *pq = NULL; - if(data->state.up.query) { - pq = aprintf("%s?%s", data->state.up.path, data->state.up.query); - if(!pq) - return CURLE_OUT_OF_MEMORY; - } - result = Curl_http_output_auth(data, conn, method, httpreq, - (pq ? pq : data->state.up.path), FALSE); - free(pq); - if(result) - return result; - } - - result = Curl_http_resume(data, conn, httpreq); - if(result) - return result; - - result = Curl_http_range(data, httpreq); - if(result) - return result; - - result = Curl_http_useragent(data); - if(result) - return result; - - io = hyper_io_new(); - if(!io) { - failf(data, "Couldn't create hyper IO"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - /* tell Hyper how to read/write network data */ - hyper_io_set_userdata(io, data); - hyper_io_set_read(io, Curl_hyper_recv); - hyper_io_set_write(io, Curl_hyper_send); - - /* create an executor to poll futures */ - if(!h->exec) { - h->exec = hyper_executor_new(); - if(!h->exec) { - failf(data, "Couldn't create hyper executor"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - } - - options = hyper_clientconn_options_new(); - if(!options) { - failf(data, "Couldn't create hyper client options"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - if(conn->alpn == CURL_HTTP_VERSION_2) { - hyper_clientconn_options_http2(options, 1); - h2 = TRUE; - } - hyper_clientconn_options_set_preserve_header_case(options, 1); - hyper_clientconn_options_set_preserve_header_order(options, 1); - hyper_clientconn_options_http1_allow_multiline_headers(options, 1); - - hyper_clientconn_options_exec(options, h->exec); - - /* "Both the `io` and the `options` are consumed in this function call" */ - handshake = hyper_clientconn_handshake(io, options); - if(!handshake) { - failf(data, "Couldn't create hyper client handshake"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - io = NULL; - options = NULL; - - if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) { - failf(data, "Couldn't hyper_executor_push the handshake"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - handshake = NULL; /* ownership passed on */ - - task = hyper_executor_poll(h->exec); - if(!task) { - failf(data, "Couldn't hyper_executor_poll the handshake"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - client = hyper_task_value(task); - hyper_task_free(task); - - req = hyper_request_new(); - if(!req) { - failf(data, "Couldn't hyper_request_new"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - if(!Curl_use_http_1_1plus(data, conn)) { - if(HYPERE_OK != hyper_request_set_version(req, - HYPER_HTTP_VERSION_1_0)) { - failf(data, "error setting HTTP version"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - } - else { - if(!h2 && !data->state.disableexpect) { - data->state.expect100header = TRUE; - } - } - - if(hyper_request_set_method(req, (uint8_t *)method, strlen(method))) { - failf(data, "error setting method"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - result = request_target(data, conn, method, h2, req); - if(result) - goto error; - - headers = hyper_request_headers(req); - if(!headers) { - failf(data, "hyper_request_headers"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - rc = hyper_request_on_informational(req, http1xx_cb, data); - if(rc) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - result = Curl_http_body(data, conn, httpreq, &te); - if(result) - goto error; - - if(!h2) { - if(data->state.aptr.host) { - result = Curl_hyper_header(data, headers, data->state.aptr.host); - if(result) - goto error; - } - } - else { - /* For HTTP/2, we show the Host: header as if we sent it, to make it look - like for HTTP/1 but it isn't actually sent since :authority is then - used. */ - Curl_debug(data, CURLINFO_HEADER_OUT, data->state.aptr.host, - strlen(data->state.aptr.host)); - } - - if(data->state.aptr.proxyuserpwd) { - result = Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd); - if(result) - goto error; - } - - if(data->state.aptr.userpwd) { - result = Curl_hyper_header(data, headers, data->state.aptr.userpwd); - if(result) - goto error; - } - - if((data->state.use_range && data->state.aptr.rangeline)) { - result = Curl_hyper_header(data, headers, data->state.aptr.rangeline); - if(result) - goto error; - } - - if(data->set.str[STRING_USERAGENT] && - *data->set.str[STRING_USERAGENT] && - data->state.aptr.uagent) { - result = Curl_hyper_header(data, headers, data->state.aptr.uagent); - if(result) - goto error; - } - - p_accept = Curl_checkheaders(data, - STRCONST("Accept"))?NULL:"Accept: */*\r\n"; - if(p_accept) { - result = Curl_hyper_header(data, headers, p_accept); - if(result) - goto error; - } - if(te) { - result = Curl_hyper_header(data, headers, te); - if(result) - goto error; - } - -#ifndef CURL_DISABLE_ALTSVC - if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) { - char *altused = aprintf("Alt-Used: %s:%d\r\n", - conn->conn_to_host.name, conn->conn_to_port); - if(!altused) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - result = Curl_hyper_header(data, headers, altused); - if(result) - goto error; - free(altused); - } -#endif - -#ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy && !conn->bits.tunnel_proxy && - !Curl_checkheaders(data, STRCONST("Proxy-Connection")) && - !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) { - result = Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive"); - if(result) - goto error; - } -#endif - - Curl_safefree(data->state.aptr.ref); - if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) { - data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer); - if(!data->state.aptr.ref) - result = CURLE_OUT_OF_MEMORY; - else - result = Curl_hyper_header(data, headers, data->state.aptr.ref); - if(result) - goto error; - } - - if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && - data->set.str[STRING_ENCODING]) { - Curl_safefree(data->state.aptr.accept_encoding); - data->state.aptr.accept_encoding = - aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]); - if(!data->state.aptr.accept_encoding) - result = CURLE_OUT_OF_MEMORY; - else - result = Curl_hyper_header(data, headers, - data->state.aptr.accept_encoding); - if(result) - goto error; - } - else - Curl_safefree(data->state.aptr.accept_encoding); - -#ifdef HAVE_LIBZ - /* we only consider transfer-encoding magic if libz support is built-in */ - result = Curl_transferencode(data); - if(result) - goto error; - result = Curl_hyper_header(data, headers, data->state.aptr.te); - if(result) - goto error; -#endif - - result = cookies(data, conn, headers); - if(result) - goto error; - - if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS)) - result = Curl_ws_request(data, headers); - - result = Curl_add_timecondition(data, headers); - if(result) - goto error; - - result = Curl_add_custom_headers(data, FALSE, headers); - if(result) - goto error; - - result = bodysend(data, conn, headers, req, httpreq); - if(result) - goto error; - - Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2); - - data->req.upload_chunky = FALSE; - sendtask = hyper_clientconn_send(client, req); - if(!sendtask) { - failf(data, "hyper_clientconn_send"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) { - failf(data, "Couldn't hyper_executor_push the send"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - hyper_clientconn_free(client); - - if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) { - /* HTTP GET/HEAD download */ - Curl_pgrsSetUploadSize(data, 0); /* nothing */ - Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1); - } - conn->datastream = Curl_hyper_stream; - if(data->state.expect100header) - /* Timeout count starts now since with Hyper we don't know exactly when - the full request has been sent. */ - data->req.start100 = Curl_now(); - - /* clear userpwd and proxyuserpwd to avoid re-using old credentials - * from re-used connections */ - Curl_safefree(data->state.aptr.userpwd); - Curl_safefree(data->state.aptr.proxyuserpwd); - return CURLE_OK; - error: - DEBUGASSERT(result); - if(io) - hyper_io_free(io); - - if(options) - hyper_clientconn_options_free(options); - - if(handshake) - hyper_task_free(handshake); - - return result; -} - -void Curl_hyper_done(struct Curl_easy *data) -{ - struct hyptransfer *h = &data->hyp; - if(h->exec) { - hyper_executor_free(h->exec); - h->exec = NULL; - } - if(h->read_waker) { - hyper_waker_free(h->read_waker); - h->read_waker = NULL; - } - if(h->write_waker) { - hyper_waker_free(h->write_waker); - h->write_waker = NULL; - } - if(h->exp100_waker) { - hyper_waker_free(h->exp100_waker); - h->exp100_waker = NULL; - } -} - -#endif /* !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER)
+
+#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 HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#include <hyper.h>
+#include "urldata.h"
+#include "sendf.h"
+#include "transfer.h"
+#include "multiif.h"
+#include "progress.h"
+#include "content_encoding.h"
+#include "ws.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+size_t Curl_hyper_recv(void *userp, hyper_context *ctx,
+ uint8_t *buf, size_t buflen)
+{
+ struct Curl_easy *data = userp;
+ struct connectdata *conn = data->conn;
+ CURLcode result;
+ ssize_t nread;
+ DEBUGASSERT(conn);
+ (void)ctx;
+
+ result = Curl_read(data, conn->sockfd, (char *)buf, buflen, &nread);
+ if(result == CURLE_AGAIN) {
+ /* would block, register interest */
+ if(data->hyp.read_waker)
+ hyper_waker_free(data->hyp.read_waker);
+ data->hyp.read_waker = hyper_context_waker(ctx);
+ if(!data->hyp.read_waker) {
+ failf(data, "Couldn't make the read hyper_context_waker");
+ return HYPER_IO_ERROR;
+ }
+ return HYPER_IO_PENDING;
+ }
+ else if(result) {
+ failf(data, "Curl_read failed");
+ return HYPER_IO_ERROR;
+ }
+ return (size_t)nread;
+}
+
+size_t Curl_hyper_send(void *userp, hyper_context *ctx,
+ const uint8_t *buf, size_t buflen)
+{
+ struct Curl_easy *data = userp;
+ struct connectdata *conn = data->conn;
+ CURLcode result;
+ ssize_t nwrote;
+
+ result = Curl_write(data, conn->sockfd, (void *)buf, buflen, &nwrote);
+ if(result == CURLE_AGAIN) {
+ /* would block, register interest */
+ if(data->hyp.write_waker)
+ hyper_waker_free(data->hyp.write_waker);
+ data->hyp.write_waker = hyper_context_waker(ctx);
+ if(!data->hyp.write_waker) {
+ failf(data, "Couldn't make the write hyper_context_waker");
+ return HYPER_IO_ERROR;
+ }
+ return HYPER_IO_PENDING;
+ }
+ else if(result) {
+ failf(data, "Curl_write failed");
+ return HYPER_IO_ERROR;
+ }
+ return (size_t)nwrote;
+}
+
+static int hyper_each_header(void *userdata,
+ const uint8_t *name,
+ size_t name_len,
+ const uint8_t *value,
+ size_t value_len)
+{
+ struct Curl_easy *data = (struct Curl_easy *)userdata;
+ size_t len;
+ char *headp;
+ CURLcode result;
+ int writetype;
+
+ if(name_len + value_len + 2 > CURL_MAX_HTTP_HEADER) {
+ failf(data, "Too long response header");
+ data->state.hresult = CURLE_OUT_OF_MEMORY;
+ return HYPER_ITER_BREAK;
+ }
+
+ if(!data->req.bytecount)
+ Curl_pgrsTime(data, TIMER_STARTTRANSFER);
+
+ Curl_dyn_reset(&data->state.headerb);
+ if(name_len) {
+ if(Curl_dyn_addf(&data->state.headerb, "%.*s: %.*s\r\n",
+ (int) name_len, name, (int) value_len, value))
+ return HYPER_ITER_BREAK;
+ }
+ else {
+ if(Curl_dyn_addn(&data->state.headerb, STRCONST("\r\n")))
+ return HYPER_ITER_BREAK;
+ }
+ len = Curl_dyn_len(&data->state.headerb);
+ headp = Curl_dyn_ptr(&data->state.headerb);
+
+ result = Curl_http_header(data, data->conn, headp);
+ if(result) {
+ data->state.hresult = result;
+ return HYPER_ITER_BREAK;
+ }
+
+ Curl_debug(data, CURLINFO_HEADER_IN, headp, len);
+
+ if(!data->state.hconnect || !data->set.suppress_connect_headers) {
+ writetype = CLIENTWRITE_HEADER;
+ if(data->set.include_header)
+ writetype |= CLIENTWRITE_BODY;
+ if(data->state.hconnect)
+ writetype |= CLIENTWRITE_CONNECT;
+ if(data->req.httpcode/100 == 1)
+ writetype |= CLIENTWRITE_1XX;
+ result = Curl_client_write(data, writetype, headp, len);
+ if(result) {
+ data->state.hresult = CURLE_ABORTED_BY_CALLBACK;
+ return HYPER_ITER_BREAK;
+ }
+ }
+
+ data->info.header_size += (curl_off_t)len;
+ data->req.headerbytecount += (curl_off_t)len;
+ return HYPER_ITER_CONTINUE;
+}
+
+static int hyper_body_chunk(void *userdata, const hyper_buf *chunk)
+{
+ char *buf = (char *)hyper_buf_bytes(chunk);
+ size_t len = hyper_buf_len(chunk);
+ struct Curl_easy *data = (struct Curl_easy *)userdata;
+ struct SingleRequest *k = &data->req;
+ CURLcode result = CURLE_OK;
+
+ if(0 == k->bodywrites++) {
+ bool done = FALSE;
+#if defined(USE_NTLM)
+ struct connectdata *conn = data->conn;
+ if(conn->bits.close &&
+ (((data->req.httpcode == 401) &&
+ (conn->http_ntlm_state == NTLMSTATE_TYPE2)) ||
+ ((data->req.httpcode == 407) &&
+ (conn->proxy_ntlm_state == NTLMSTATE_TYPE2)))) {
+ infof(data, "Connection closed while negotiating NTLM");
+ data->state.authproblem = TRUE;
+ Curl_safefree(data->req.newurl);
+ }
+#endif
+ if(data->state.expect100header) {
+ Curl_expire_done(data, EXPIRE_100_TIMEOUT);
+ if(data->req.httpcode < 400) {
+ k->exp100 = EXP100_SEND_DATA;
+ if(data->hyp.exp100_waker) {
+ hyper_waker_wake(data->hyp.exp100_waker);
+ data->hyp.exp100_waker = NULL;
+ }
+ }
+ else { /* >= 4xx */
+ k->exp100 = EXP100_FAILED;
+ }
+ }
+ if(data->state.hconnect && (data->req.httpcode/100 != 2) &&
+ data->state.authproxy.done) {
+ done = TRUE;
+ result = CURLE_OK;
+ }
+ else
+ result = Curl_http_firstwrite(data, data->conn, &done);
+ if(result || done) {
+ infof(data, "Return early from hyper_body_chunk");
+ data->state.hresult = result;
+ return HYPER_ITER_BREAK;
+ }
+ }
+ if(k->ignorebody)
+ return HYPER_ITER_CONTINUE;
+ if(0 == len)
+ return HYPER_ITER_CONTINUE;
+ Curl_debug(data, CURLINFO_DATA_IN, buf, len);
+ if(!data->set.http_ce_skip && k->writer_stack)
+ /* content-encoded data */
+ result = Curl_unencode_write(data, k->writer_stack, buf, len);
+ else
+ result = Curl_client_write(data, CLIENTWRITE_BODY, buf, len);
+
+ if(result) {
+ data->state.hresult = result;
+ return HYPER_ITER_BREAK;
+ }
+
+ data->req.bytecount += len;
+ Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
+ return HYPER_ITER_CONTINUE;
+}
+
+/*
+ * Hyper does not consider the status line, the first line in an HTTP/1
+ * response, to be a header. The libcurl API does. This function sends the
+ * status line in the header callback. */
+static CURLcode status_line(struct Curl_easy *data,
+ struct connectdata *conn,
+ uint16_t http_status,
+ int http_version,
+ const uint8_t *reason, size_t rlen)
+{
+ CURLcode result;
+ size_t len;
+ const char *vstr;
+ int writetype;
+ vstr = http_version == HYPER_HTTP_VERSION_1_1 ? "1.1" :
+ (http_version == HYPER_HTTP_VERSION_2 ? "2" : "1.0");
+
+ /* We need to set 'httpcodeq' for functions that check the response code in
+ a single place. */
+ data->req.httpcode = http_status;
+
+ if(data->state.hconnect)
+ /* CONNECT */
+ data->info.httpproxycode = http_status;
+ else {
+ conn->httpversion =
+ http_version == HYPER_HTTP_VERSION_1_1 ? 11 :
+ (http_version == HYPER_HTTP_VERSION_2 ? 20 : 10);
+ if(http_version == HYPER_HTTP_VERSION_1_0)
+ data->state.httpwant = CURL_HTTP_VERSION_1_0;
+
+ result = Curl_http_statusline(data, conn);
+ if(result)
+ return result;
+ }
+
+ Curl_dyn_reset(&data->state.headerb);
+
+ result = Curl_dyn_addf(&data->state.headerb, "HTTP/%s %03d %.*s\r\n",
+ vstr,
+ (int)http_status,
+ (int)rlen, reason);
+ if(result)
+ return result;
+ len = Curl_dyn_len(&data->state.headerb);
+ Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&data->state.headerb),
+ len);
+
+ if(!data->state.hconnect || !data->set.suppress_connect_headers) {
+ writetype = CLIENTWRITE_HEADER|CLIENTWRITE_STATUS;
+ if(data->set.include_header)
+ writetype |= CLIENTWRITE_BODY;
+ result = Curl_client_write(data, writetype,
+ Curl_dyn_ptr(&data->state.headerb), len);
+ if(result)
+ return result;
+ }
+ data->info.header_size += (curl_off_t)len;
+ data->req.headerbytecount += (curl_off_t)len;
+ return CURLE_OK;
+}
+
+/*
+ * Hyper does not pass on the last empty response header. The libcurl API
+ * does. This function sends an empty header in the header callback.
+ */
+static CURLcode empty_header(struct Curl_easy *data)
+{
+ CURLcode result = Curl_http_size(data);
+ if(!result) {
+ result = hyper_each_header(data, NULL, 0, NULL, 0) ?
+ CURLE_WRITE_ERROR : CURLE_OK;
+ if(result)
+ failf(data, "hyperstream: couldn't pass blank header");
+ }
+ return result;
+}
+
+CURLcode Curl_hyper_stream(struct Curl_easy *data,
+ struct connectdata *conn,
+ int *didwhat,
+ bool *done,
+ int select_res)
+{
+ hyper_response *resp = NULL;
+ uint16_t http_status;
+ int http_version;
+ hyper_headers *headers = NULL;
+ hyper_body *resp_body = NULL;
+ struct hyptransfer *h = &data->hyp;
+ hyper_task *task;
+ hyper_task *foreach;
+ hyper_error *hypererr = NULL;
+ const uint8_t *reasonp;
+ size_t reason_len;
+ CURLcode result = CURLE_OK;
+ struct SingleRequest *k = &data->req;
+ (void)conn;
+
+ if(k->exp100 > EXP100_SEND_DATA) {
+ struct curltime now = Curl_now();
+ timediff_t ms = Curl_timediff(now, k->start100);
+ if(ms >= data->set.expect_100_timeout) {
+ /* we've waited long enough, continue anyway */
+ k->exp100 = EXP100_SEND_DATA;
+ k->keepon |= KEEP_SEND;
+ Curl_expire_done(data, EXPIRE_100_TIMEOUT);
+ infof(data, "Done waiting for 100-continue");
+ if(data->hyp.exp100_waker) {
+ hyper_waker_wake(data->hyp.exp100_waker);
+ data->hyp.exp100_waker = NULL;
+ }
+ }
+ }
+
+ if(select_res & CURL_CSELECT_IN) {
+ if(h->read_waker)
+ hyper_waker_wake(h->read_waker);
+ h->read_waker = NULL;
+ }
+ if(select_res & CURL_CSELECT_OUT) {
+ if(h->write_waker)
+ hyper_waker_wake(h->write_waker);
+ h->write_waker = NULL;
+ }
+
+ *done = FALSE;
+ do {
+ hyper_task_return_type t;
+ task = hyper_executor_poll(h->exec);
+ if(!task) {
+ *didwhat = KEEP_RECV;
+ break;
+ }
+ t = hyper_task_type(task);
+ switch(t) {
+ case HYPER_TASK_ERROR:
+ hypererr = hyper_task_value(task);
+ break;
+ case HYPER_TASK_RESPONSE:
+ resp = hyper_task_value(task);
+ break;
+ default:
+ break;
+ }
+ hyper_task_free(task);
+
+ if(t == HYPER_TASK_ERROR) {
+ if(data->state.hresult) {
+ /* override Hyper's view, might not even be an error */
+ result = data->state.hresult;
+ infof(data, "hyperstream is done (by early callback)");
+ }
+ else {
+ uint8_t errbuf[256];
+ size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
+ hyper_code code = hyper_error_code(hypererr);
+ failf(data, "Hyper: [%d] %.*s", (int)code, (int)errlen, errbuf);
+ if(code == HYPERE_ABORTED_BY_CALLBACK)
+ result = CURLE_OK;
+ else if((code == HYPERE_UNEXPECTED_EOF) && !data->req.bytecount)
+ result = CURLE_GOT_NOTHING;
+ else if(code == HYPERE_INVALID_PEER_MESSAGE)
+ result = CURLE_UNSUPPORTED_PROTOCOL; /* maybe */
+ else
+ result = CURLE_RECV_ERROR;
+ }
+ *done = TRUE;
+ hyper_error_free(hypererr);
+ break;
+ }
+ else if(h->endtask == task) {
+ /* end of transfer, forget the task handled, we might get a
+ * new one with the same address in the future. */
+ *done = TRUE;
+ h->endtask = NULL;
+ infof(data, "hyperstream is done");
+ if(!k->bodywrites) {
+ /* hyper doesn't always call the body write callback */
+ bool stilldone;
+ result = Curl_http_firstwrite(data, data->conn, &stilldone);
+ }
+ break;
+ }
+ else if(t != HYPER_TASK_RESPONSE) {
+ *didwhat = KEEP_RECV;
+ break;
+ }
+ /* HYPER_TASK_RESPONSE */
+
+ *didwhat = KEEP_RECV;
+ if(!resp) {
+ failf(data, "hyperstream: couldn't get response");
+ return CURLE_RECV_ERROR;
+ }
+
+ http_status = hyper_response_status(resp);
+ http_version = hyper_response_version(resp);
+ reasonp = hyper_response_reason_phrase(resp);
+ reason_len = hyper_response_reason_phrase_len(resp);
+
+ if(http_status == 417 && data->state.expect100header) {
+ infof(data, "Got 417 while waiting for a 100");
+ data->state.disableexpect = TRUE;
+ data->req.newurl = strdup(data->state.url);
+ Curl_done_sending(data, k);
+ }
+
+ result = status_line(data, conn,
+ http_status, http_version, reasonp, reason_len);
+ if(result)
+ break;
+
+ headers = hyper_response_headers(resp);
+ if(!headers) {
+ failf(data, "hyperstream: couldn't get response headers");
+ result = CURLE_RECV_ERROR;
+ break;
+ }
+
+ /* the headers are already received */
+ hyper_headers_foreach(headers, hyper_each_header, data);
+ if(data->state.hresult) {
+ result = data->state.hresult;
+ break;
+ }
+
+ result = empty_header(data);
+ if(result)
+ break;
+
+ k->deductheadercount =
+ (100 <= http_status && 199 >= http_status)?k->headerbytecount:0;
+#ifdef USE_WEBSOCKETS
+ if(k->upgr101 == UPGR101_WS) {
+ if(http_status == 101) {
+ /* verify the response */
+ result = Curl_ws_accept(data);
+ if(result)
+ return result;
+ }
+ else {
+ failf(data, "Expected 101, got %u", k->httpcode);
+ result = CURLE_HTTP_RETURNED_ERROR;
+ break;
+ }
+ }
+#endif
+
+ /* Curl_http_auth_act() checks what authentication methods that are
+ * available and decides which one (if any) to use. It will set 'newurl'
+ * if an auth method was picked. */
+ result = Curl_http_auth_act(data);
+ if(result)
+ break;
+
+ resp_body = hyper_response_body(resp);
+ if(!resp_body) {
+ failf(data, "hyperstream: couldn't get response body");
+ result = CURLE_RECV_ERROR;
+ break;
+ }
+ foreach = hyper_body_foreach(resp_body, hyper_body_chunk, data);
+ if(!foreach) {
+ failf(data, "hyperstream: body foreach failed");
+ result = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+ DEBUGASSERT(hyper_task_type(foreach) == HYPER_TASK_EMPTY);
+ if(HYPERE_OK != hyper_executor_push(h->exec, foreach)) {
+ failf(data, "Couldn't hyper_executor_push the body-foreach");
+ result = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+ h->endtask = foreach;
+
+ hyper_response_free(resp);
+ resp = NULL;
+ } while(1);
+ if(resp)
+ hyper_response_free(resp);
+ return result;
+}
+
+static CURLcode debug_request(struct Curl_easy *data,
+ const char *method,
+ const char *path,
+ bool h2)
+{
+ char *req = aprintf("%s %s HTTP/%s\r\n", method, path,
+ h2?"2":"1.1");
+ if(!req)
+ return CURLE_OUT_OF_MEMORY;
+ Curl_debug(data, CURLINFO_HEADER_OUT, req, strlen(req));
+ free(req);
+ return CURLE_OK;
+}
+
+/*
+ * Given a full header line "name: value" (optional CRLF in the input, should
+ * be in the output), add to Hyper and send to the debug callback.
+ *
+ * Supports multiple headers.
+ */
+
+CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers,
+ const char *line)
+{
+ const char *p;
+ const char *n;
+ size_t nlen;
+ const char *v;
+ size_t vlen;
+ bool newline = TRUE;
+ int numh = 0;
+
+ if(!line)
+ return CURLE_OK;
+ n = line;
+ do {
+ size_t linelen = 0;
+
+ p = strchr(n, ':');
+ if(!p)
+ /* this is fine if we already added at least one header */
+ return numh ? CURLE_OK : CURLE_BAD_FUNCTION_ARGUMENT;
+ nlen = p - n;
+ p++; /* move past the colon */
+ while(*p == ' ')
+ p++;
+ v = p;
+ p = strchr(v, '\r');
+ if(!p) {
+ p = strchr(v, '\n');
+ if(p)
+ linelen = 1; /* LF only */
+ else {
+ p = strchr(v, '\0');
+ newline = FALSE; /* no newline */
+ }
+ }
+ else
+ linelen = 2; /* CRLF ending */
+ linelen += (p - n);
+ vlen = p - v;
+
+ if(HYPERE_OK != hyper_headers_add(headers, (uint8_t *)n, nlen,
+ (uint8_t *)v, vlen)) {
+ failf(data, "hyper refused to add header '%s'", line);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ if(data->set.verbose) {
+ char *ptr = NULL;
+ if(!newline) {
+ ptr = aprintf("%.*s\r\n", (int)linelen, line);
+ if(!ptr)
+ return CURLE_OUT_OF_MEMORY;
+ Curl_debug(data, CURLINFO_HEADER_OUT, ptr, linelen + 2);
+ free(ptr);
+ }
+ else
+ Curl_debug(data, CURLINFO_HEADER_OUT, (char *)n, linelen);
+ }
+ numh++;
+ n += linelen;
+ } while(newline);
+ return CURLE_OK;
+}
+
+static CURLcode request_target(struct Curl_easy *data,
+ struct connectdata *conn,
+ const char *method,
+ bool h2,
+ hyper_request *req)
+{
+ CURLcode result;
+ struct dynbuf r;
+
+ Curl_dyn_init(&r, DYN_HTTP_REQUEST);
+
+ result = Curl_http_target(data, conn, &r);
+ if(result)
+ return result;
+
+ if(h2 && hyper_request_set_uri_parts(req,
+ /* scheme */
+ (uint8_t *)data->state.up.scheme,
+ strlen(data->state.up.scheme),
+ /* authority */
+ (uint8_t *)conn->host.name,
+ strlen(conn->host.name),
+ /* path_and_query */
+ (uint8_t *)Curl_dyn_uptr(&r),
+ Curl_dyn_len(&r))) {
+ failf(data, "error setting uri parts to hyper");
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ else if(!h2 && hyper_request_set_uri(req, (uint8_t *)Curl_dyn_uptr(&r),
+ Curl_dyn_len(&r))) {
+ failf(data, "error setting uri to hyper");
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ else
+ result = debug_request(data, method, Curl_dyn_ptr(&r), h2);
+
+ Curl_dyn_free(&r);
+
+ return result;
+}
+
+static int uploadpostfields(void *userdata, hyper_context *ctx,
+ hyper_buf **chunk)
+{
+ struct Curl_easy *data = (struct Curl_easy *)userdata;
+ (void)ctx;
+ if(data->req.exp100 > EXP100_SEND_DATA) {
+ if(data->req.exp100 == EXP100_FAILED)
+ return HYPER_POLL_ERROR;
+
+ /* still waiting confirmation */
+ if(data->hyp.exp100_waker)
+ hyper_waker_free(data->hyp.exp100_waker);
+ data->hyp.exp100_waker = hyper_context_waker(ctx);
+ return HYPER_POLL_PENDING;
+ }
+ if(data->req.upload_done)
+ *chunk = NULL; /* nothing more to deliver */
+ else {
+ /* send everything off in a single go */
+ hyper_buf *copy = hyper_buf_copy(data->set.postfields,
+ (size_t)data->req.p.http->postsize);
+ if(copy)
+ *chunk = copy;
+ else {
+ data->state.hresult = CURLE_OUT_OF_MEMORY;
+ return HYPER_POLL_ERROR;
+ }
+ /* increasing the writebytecount here is a little premature but we
+ don't know exactly when the body is sent */
+ data->req.writebytecount += (size_t)data->req.p.http->postsize;
+ Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
+ data->req.upload_done = TRUE;
+ }
+ return HYPER_POLL_READY;
+}
+
+static int uploadstreamed(void *userdata, hyper_context *ctx,
+ hyper_buf **chunk)
+{
+ size_t fillcount;
+ struct Curl_easy *data = (struct Curl_easy *)userdata;
+ struct connectdata *conn = (struct connectdata *)data->conn;
+ CURLcode result;
+ (void)ctx;
+
+ if(data->req.exp100 > EXP100_SEND_DATA) {
+ if(data->req.exp100 == EXP100_FAILED)
+ return HYPER_POLL_ERROR;
+
+ /* still waiting confirmation */
+ if(data->hyp.exp100_waker)
+ hyper_waker_free(data->hyp.exp100_waker);
+ data->hyp.exp100_waker = hyper_context_waker(ctx);
+ return HYPER_POLL_PENDING;
+ }
+
+ if(data->req.upload_chunky && conn->bits.authneg) {
+ fillcount = 0;
+ data->req.upload_chunky = FALSE;
+ result = CURLE_OK;
+ }
+ else {
+ result = Curl_fillreadbuffer(data, data->set.upload_buffer_size,
+ &fillcount);
+ }
+ if(result) {
+ data->state.hresult = result;
+ return HYPER_POLL_ERROR;
+ }
+ if(!fillcount) {
+ if((data->req.keepon & KEEP_SEND_PAUSE) != KEEP_SEND_PAUSE)
+ /* done! */
+ *chunk = NULL;
+ else {
+ /* paused, save a waker */
+ if(data->hyp.send_body_waker)
+ hyper_waker_free(data->hyp.send_body_waker);
+ data->hyp.send_body_waker = hyper_context_waker(ctx);
+ return HYPER_POLL_PENDING;
+ }
+ }
+ else {
+ hyper_buf *copy = hyper_buf_copy((uint8_t *)data->state.ulbuf, fillcount);
+ if(copy)
+ *chunk = copy;
+ else {
+ data->state.hresult = CURLE_OUT_OF_MEMORY;
+ return HYPER_POLL_ERROR;
+ }
+ /* increasing the writebytecount here is a little premature but we
+ don't know exactly when the body is sent */
+ data->req.writebytecount += fillcount;
+ Curl_pgrsSetUploadCounter(data, fillcount);
+ }
+ return HYPER_POLL_READY;
+}
+
+/*
+ * bodysend() sets up headers in the outgoing request for an HTTP transfer that
+ * sends a body
+ */
+
+static CURLcode bodysend(struct Curl_easy *data,
+ struct connectdata *conn,
+ hyper_headers *headers,
+ hyper_request *hyperreq,
+ Curl_HttpReq httpreq)
+{
+ struct HTTP *http = data->req.p.http;
+ CURLcode result = CURLE_OK;
+ struct dynbuf req;
+ if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD))
+ Curl_pgrsSetUploadSize(data, 0); /* no request body */
+ else {
+ hyper_body *body;
+ Curl_dyn_init(&req, DYN_HTTP_REQUEST);
+ result = Curl_http_bodysend(data, conn, &req, httpreq);
+
+ if(!result)
+ result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req));
+
+ Curl_dyn_free(&req);
+
+ body = hyper_body_new();
+ hyper_body_set_userdata(body, data);
+ if(data->set.postfields)
+ hyper_body_set_data_func(body, uploadpostfields);
+ else {
+ result = Curl_get_upload_buffer(data);
+ if(result)
+ return result;
+ /* init the "upload from here" pointer */
+ data->req.upload_fromhere = data->state.ulbuf;
+ hyper_body_set_data_func(body, uploadstreamed);
+ }
+ if(HYPERE_OK != hyper_request_set_body(hyperreq, body)) {
+ /* fail */
+ hyper_body_free(body);
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ }
+ http->sending = HTTPSEND_BODY;
+ return result;
+}
+
+static CURLcode cookies(struct Curl_easy *data,
+ struct connectdata *conn,
+ hyper_headers *headers)
+{
+ struct dynbuf req;
+ CURLcode result;
+ Curl_dyn_init(&req, DYN_HTTP_REQUEST);
+
+ result = Curl_http_cookies(data, conn, &req);
+ if(!result)
+ result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req));
+ Curl_dyn_free(&req);
+ return result;
+}
+
+/* called on 1xx responses */
+static void http1xx_cb(void *arg, struct hyper_response *resp)
+{
+ struct Curl_easy *data = (struct Curl_easy *)arg;
+ hyper_headers *headers = NULL;
+ CURLcode result = CURLE_OK;
+ uint16_t http_status;
+ int http_version;
+ const uint8_t *reasonp;
+ size_t reason_len;
+
+ infof(data, "Got HTTP 1xx informational");
+
+ http_status = hyper_response_status(resp);
+ http_version = hyper_response_version(resp);
+ reasonp = hyper_response_reason_phrase(resp);
+ reason_len = hyper_response_reason_phrase_len(resp);
+
+ result = status_line(data, data->conn,
+ http_status, http_version, reasonp, reason_len);
+ if(!result) {
+ headers = hyper_response_headers(resp);
+ if(!headers) {
+ failf(data, "hyperstream: couldn't get 1xx response headers");
+ result = CURLE_RECV_ERROR;
+ }
+ }
+ data->state.hresult = result;
+
+ if(!result) {
+ /* the headers are already received */
+ hyper_headers_foreach(headers, hyper_each_header, data);
+ /* this callback also sets data->state.hresult on error */
+
+ if(empty_header(data))
+ result = CURLE_OUT_OF_MEMORY;
+ }
+
+ if(data->state.hresult)
+ infof(data, "ERROR in 1xx, bail out");
+}
+
+/*
+ * Curl_http() gets called from the generic multi_do() function when an HTTP
+ * request is to be performed. This creates and sends a properly constructed
+ * HTTP request.
+ */
+CURLcode Curl_http(struct Curl_easy *data, bool *done)
+{
+ struct connectdata *conn = data->conn;
+ struct hyptransfer *h = &data->hyp;
+ hyper_io *io = NULL;
+ hyper_clientconn_options *options = NULL;
+ hyper_task *task = NULL; /* for the handshake */
+ hyper_task *sendtask = NULL; /* for the send */
+ hyper_clientconn *client = NULL;
+ hyper_request *req = NULL;
+ hyper_headers *headers = NULL;
+ hyper_task *handshake = NULL;
+ CURLcode result;
+ const char *p_accept; /* Accept: string */
+ const char *method;
+ Curl_HttpReq httpreq;
+ bool h2 = FALSE;
+ const char *te = NULL; /* transfer-encoding */
+ hyper_code rc;
+
+ /* Always consider the DO phase done after this function call, even if there
+ may be parts of the request that is not yet sent, since we can deal with
+ the rest of the request in the PERFORM phase. */
+ *done = TRUE;
+
+ infof(data, "Time for the Hyper dance");
+ memset(h, 0, sizeof(struct hyptransfer));
+
+ result = Curl_http_host(data, conn);
+ if(result)
+ return result;
+
+ Curl_http_method(data, conn, &method, &httpreq);
+
+ /* setup the authentication headers */
+ {
+ char *pq = NULL;
+ if(data->state.up.query) {
+ pq = aprintf("%s?%s", data->state.up.path, data->state.up.query);
+ if(!pq)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ result = Curl_http_output_auth(data, conn, method, httpreq,
+ (pq ? pq : data->state.up.path), FALSE);
+ free(pq);
+ if(result)
+ return result;
+ }
+
+ result = Curl_http_resume(data, conn, httpreq);
+ if(result)
+ return result;
+
+ result = Curl_http_range(data, httpreq);
+ if(result)
+ return result;
+
+ result = Curl_http_useragent(data);
+ if(result)
+ return result;
+
+ io = hyper_io_new();
+ if(!io) {
+ failf(data, "Couldn't create hyper IO");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ /* tell Hyper how to read/write network data */
+ hyper_io_set_userdata(io, data);
+ hyper_io_set_read(io, Curl_hyper_recv);
+ hyper_io_set_write(io, Curl_hyper_send);
+
+ /* create an executor to poll futures */
+ if(!h->exec) {
+ h->exec = hyper_executor_new();
+ if(!h->exec) {
+ failf(data, "Couldn't create hyper executor");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ }
+
+ options = hyper_clientconn_options_new();
+ if(!options) {
+ failf(data, "Couldn't create hyper client options");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ if(conn->alpn == CURL_HTTP_VERSION_2) {
+ hyper_clientconn_options_http2(options, 1);
+ h2 = TRUE;
+ }
+ hyper_clientconn_options_set_preserve_header_case(options, 1);
+ hyper_clientconn_options_set_preserve_header_order(options, 1);
+ hyper_clientconn_options_http1_allow_multiline_headers(options, 1);
+
+ hyper_clientconn_options_exec(options, h->exec);
+
+ /* "Both the `io` and the `options` are consumed in this function call" */
+ handshake = hyper_clientconn_handshake(io, options);
+ if(!handshake) {
+ failf(data, "Couldn't create hyper client handshake");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ io = NULL;
+ options = NULL;
+
+ if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) {
+ failf(data, "Couldn't hyper_executor_push the handshake");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ handshake = NULL; /* ownership passed on */
+
+ task = hyper_executor_poll(h->exec);
+ if(!task) {
+ failf(data, "Couldn't hyper_executor_poll the handshake");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ client = hyper_task_value(task);
+ hyper_task_free(task);
+
+ req = hyper_request_new();
+ if(!req) {
+ failf(data, "Couldn't hyper_request_new");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ if(!Curl_use_http_1_1plus(data, conn)) {
+ if(HYPERE_OK != hyper_request_set_version(req,
+ HYPER_HTTP_VERSION_1_0)) {
+ failf(data, "error setting HTTP version");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ }
+ else {
+ if(!h2 && !data->state.disableexpect) {
+ data->state.expect100header = TRUE;
+ }
+ }
+
+ if(hyper_request_set_method(req, (uint8_t *)method, strlen(method))) {
+ failf(data, "error setting method");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ result = request_target(data, conn, method, h2, req);
+ if(result)
+ goto error;
+
+ headers = hyper_request_headers(req);
+ if(!headers) {
+ failf(data, "hyper_request_headers");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ rc = hyper_request_on_informational(req, http1xx_cb, data);
+ if(rc) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ result = Curl_http_body(data, conn, httpreq, &te);
+ if(result)
+ goto error;
+
+ if(!h2) {
+ if(data->state.aptr.host) {
+ result = Curl_hyper_header(data, headers, data->state.aptr.host);
+ if(result)
+ goto error;
+ }
+ }
+ else {
+ /* For HTTP/2, we show the Host: header as if we sent it, to make it look
+ like for HTTP/1 but it isn't actually sent since :authority is then
+ used. */
+ Curl_debug(data, CURLINFO_HEADER_OUT, data->state.aptr.host,
+ strlen(data->state.aptr.host));
+ }
+
+ if(data->state.aptr.proxyuserpwd) {
+ result = Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd);
+ if(result)
+ goto error;
+ }
+
+ if(data->state.aptr.userpwd) {
+ result = Curl_hyper_header(data, headers, data->state.aptr.userpwd);
+ if(result)
+ goto error;
+ }
+
+ if((data->state.use_range && data->state.aptr.rangeline)) {
+ result = Curl_hyper_header(data, headers, data->state.aptr.rangeline);
+ if(result)
+ goto error;
+ }
+
+ if(data->set.str[STRING_USERAGENT] &&
+ *data->set.str[STRING_USERAGENT] &&
+ data->state.aptr.uagent) {
+ result = Curl_hyper_header(data, headers, data->state.aptr.uagent);
+ if(result)
+ goto error;
+ }
+
+ p_accept = Curl_checkheaders(data,
+ STRCONST("Accept"))?NULL:"Accept: */*\r\n";
+ if(p_accept) {
+ result = Curl_hyper_header(data, headers, p_accept);
+ if(result)
+ goto error;
+ }
+ if(te) {
+ result = Curl_hyper_header(data, headers, te);
+ if(result)
+ goto error;
+ }
+
+#ifndef CURL_DISABLE_ALTSVC
+ if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) {
+ char *altused = aprintf("Alt-Used: %s:%d\r\n",
+ conn->conn_to_host.name, conn->conn_to_port);
+ if(!altused) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ result = Curl_hyper_header(data, headers, altused);
+ if(result)
+ goto error;
+ free(altused);
+ }
+#endif
+
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.httpproxy && !conn->bits.tunnel_proxy &&
+ !Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
+ !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) {
+ result = Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive");
+ if(result)
+ goto error;
+ }
+#endif
+
+ Curl_safefree(data->state.aptr.ref);
+ if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) {
+ data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer);
+ if(!data->state.aptr.ref)
+ result = CURLE_OUT_OF_MEMORY;
+ else
+ result = Curl_hyper_header(data, headers, data->state.aptr.ref);
+ if(result)
+ goto error;
+ }
+
+ if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
+ data->set.str[STRING_ENCODING]) {
+ Curl_safefree(data->state.aptr.accept_encoding);
+ data->state.aptr.accept_encoding =
+ aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
+ if(!data->state.aptr.accept_encoding)
+ result = CURLE_OUT_OF_MEMORY;
+ else
+ result = Curl_hyper_header(data, headers,
+ data->state.aptr.accept_encoding);
+ if(result)
+ goto error;
+ }
+ else
+ Curl_safefree(data->state.aptr.accept_encoding);
+
+#ifdef HAVE_LIBZ
+ /* we only consider transfer-encoding magic if libz support is built-in */
+ result = Curl_transferencode(data);
+ if(result)
+ goto error;
+ result = Curl_hyper_header(data, headers, data->state.aptr.te);
+ if(result)
+ goto error;
+#endif
+
+ result = cookies(data, conn, headers);
+ if(result)
+ goto error;
+
+ if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
+ result = Curl_ws_request(data, headers);
+
+ result = Curl_add_timecondition(data, headers);
+ if(result)
+ goto error;
+
+ result = Curl_add_custom_headers(data, FALSE, headers);
+ if(result)
+ goto error;
+
+ result = bodysend(data, conn, headers, req, httpreq);
+ if(result)
+ goto error;
+
+ Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2);
+
+ if(data->req.upload_chunky && conn->bits.authneg) {
+ data->req.upload_chunky = TRUE;
+ }
+ else {
+ data->req.upload_chunky = FALSE;
+ }
+ sendtask = hyper_clientconn_send(client, req);
+ if(!sendtask) {
+ failf(data, "hyper_clientconn_send");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
+ failf(data, "Couldn't hyper_executor_push the send");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ hyper_clientconn_free(client);
+
+ if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) {
+ /* HTTP GET/HEAD download */
+ Curl_pgrsSetUploadSize(data, 0); /* nothing */
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1);
+ }
+ conn->datastream = Curl_hyper_stream;
+ if(data->state.expect100header)
+ /* Timeout count starts now since with Hyper we don't know exactly when
+ the full request has been sent. */
+ data->req.start100 = Curl_now();
+
+ /* clear userpwd and proxyuserpwd to avoid re-using old credentials
+ * from re-used connections */
+ Curl_safefree(data->state.aptr.userpwd);
+ Curl_safefree(data->state.aptr.proxyuserpwd);
+ return CURLE_OK;
+ error:
+ DEBUGASSERT(result);
+ if(io)
+ hyper_io_free(io);
+
+ if(options)
+ hyper_clientconn_options_free(options);
+
+ if(handshake)
+ hyper_task_free(handshake);
+
+ return result;
+}
+
+void Curl_hyper_done(struct Curl_easy *data)
+{
+ struct hyptransfer *h = &data->hyp;
+ if(h->exec) {
+ hyper_executor_free(h->exec);
+ h->exec = NULL;
+ }
+ if(h->read_waker) {
+ hyper_waker_free(h->read_waker);
+ h->read_waker = NULL;
+ }
+ if(h->write_waker) {
+ hyper_waker_free(h->write_waker);
+ h->write_waker = NULL;
+ }
+ if(h->exp100_waker) {
+ hyper_waker_free(h->exp100_waker);
+ h->exp100_waker = NULL;
+ }
+}
+
+#endif /* !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) */
diff --git a/libs/libcurl/src/c-hyper.h b/libs/libcurl/src/c-hyper.h index 70507ad2a4..7a03afbd0e 100644 --- a/libs/libcurl/src/c-hyper.h +++ b/libs/libcurl/src/c-hyper.h @@ -1,60 +1,60 @@ -#ifndef HEADER_CURL_HYPER_H -#define HEADER_CURL_HYPER_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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.haxx.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#include "curl_setup.h" - -#if !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) - -#include <hyper.h> - -/* per-transfer data for the Hyper backend */ -struct hyptransfer { - hyper_waker *write_waker; - hyper_waker *read_waker; - const hyper_executor *exec; - hyper_task *endtask; - hyper_waker *exp100_waker; - hyper_waker *send_body_waker; -}; - -size_t Curl_hyper_recv(void *userp, hyper_context *ctx, - uint8_t *buf, size_t buflen); -size_t Curl_hyper_send(void *userp, hyper_context *ctx, - const uint8_t *buf, size_t buflen); -CURLcode Curl_hyper_stream(struct Curl_easy *data, - struct connectdata *conn, - int *didwhat, - bool *done, - int select_res); - -CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers, - const char *line); -void Curl_hyper_done(struct Curl_easy *); - -#else -#define Curl_hyper_done(x) - -#endif /* !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) */ -#endif /* HEADER_CURL_HYPER_H */ +#ifndef HEADER_CURL_HYPER_H
+#define HEADER_CURL_HYPER_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER)
+
+#include <hyper.h>
+
+/* per-transfer data for the Hyper backend */
+struct hyptransfer {
+ hyper_waker *write_waker;
+ hyper_waker *read_waker;
+ const hyper_executor *exec;
+ hyper_task *endtask;
+ hyper_waker *exp100_waker;
+ hyper_waker *send_body_waker;
+};
+
+size_t Curl_hyper_recv(void *userp, hyper_context *ctx,
+ uint8_t *buf, size_t buflen);
+size_t Curl_hyper_send(void *userp, hyper_context *ctx,
+ const uint8_t *buf, size_t buflen);
+CURLcode Curl_hyper_stream(struct Curl_easy *data,
+ struct connectdata *conn,
+ int *didwhat,
+ bool *done,
+ int select_res);
+
+CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers,
+ const char *line);
+void Curl_hyper_done(struct Curl_easy *);
+
+#else
+#define Curl_hyper_done(x)
+
+#endif /* !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) */
+#endif /* HEADER_CURL_HYPER_H */
diff --git a/libs/libcurl/src/cfilters.c b/libs/libcurl/src/cfilters.c new file mode 100644 index 0000000000..fd9c5390a9 --- /dev/null +++ b/libs/libcurl/src/cfilters.c @@ -0,0 +1,502 @@ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "strerror.h"
+#include "cfilters.h"
+#include "connect.h"
+#include "url.h" /* for Curl_safefree() */
+#include "sendf.h"
+#include "sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "multiif.h"
+#include "progress.h"
+#include "warnless.h"
+#include "http_proxy.h"
+#include "socks.h"
+#include "vtls/vtls.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
+
+
+void Curl_cf_def_destroy_this(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ (void)cf;
+ (void)data;
+}
+
+CURLcode Curl_cf_def_setup(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost)
+{
+ DEBUGASSERT(cf->next);
+ return cf->next->cft->setup(cf->next, data, remotehost);
+}
+
+void Curl_cf_def_attach_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ (void)cf;
+ (void)data;
+}
+
+void Curl_cf_def_detach_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ (void)cf;
+ (void)data;
+}
+
+void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ DEBUGASSERT(cf->next);
+ cf->connected = FALSE;
+ cf->next->cft->close(cf->next, data);
+}
+
+CURLcode Curl_cf_def_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ DEBUGASSERT(cf->next);
+ return cf->next->cft->connect(cf->next, data, blocking, done);
+}
+
+void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const char **phost, const char **pdisplay_host,
+ int *pport)
+{
+ DEBUGASSERT(cf->next);
+ cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
+}
+
+int Curl_cf_def_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ DEBUGASSERT(cf->next);
+ return cf->next->cft->get_select_socks(cf->next, data, socks);
+}
+
+bool Curl_cf_def_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ DEBUGASSERT(cf->next);
+ return cf->next->cft->has_data_pending(cf->next, data);
+}
+
+ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err)
+{
+ DEBUGASSERT(cf->next);
+ return cf->next->cft->do_send(cf->next, data, buf, len, err);
+}
+
+ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err)
+{
+ DEBUGASSERT(cf->next);
+ return cf->next->cft->do_recv(cf->next, data, buf, len, err);
+}
+
+void Curl_conn_cf_discard_all(struct Curl_easy *data,
+ struct connectdata *conn, int index)
+{
+ struct Curl_cfilter *cfn, *cf = conn->cfilter[index];
+
+ if(cf) {
+ conn->cfilter[index] = NULL;
+ while(cf) {
+ cfn = cf->next;
+ cf->cft->destroy(cf, data);
+ free(cf);
+ cf = cfn;
+ }
+ }
+}
+
+void Curl_conn_close(struct Curl_easy *data, int index)
+{
+ struct Curl_cfilter *cf;
+
+ DEBUGASSERT(data->conn);
+ /* it is valid to call that without filters being present */
+ cf = data->conn->cfilter[index];
+ if(cf) {
+ cf->cft->close(cf, data);
+ }
+}
+
+ssize_t Curl_conn_recv(struct Curl_easy *data, int num, char *buf,
+ size_t len, CURLcode *code)
+{
+ struct Curl_cfilter *cf;
+ ssize_t nread;
+
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->conn);
+ cf = data->conn->cfilter[num];
+ while(cf && !cf->connected) {
+ cf = cf->next;
+ }
+ if(cf) {
+ nread = cf->cft->do_recv(cf, data, buf, len, code);
+ /* DEBUGF(infof(data, "Curl_conn_recv(handle=%p, index=%d)"
+ "-> %ld, err=%d", data, num, nread, *code));*/
+ return nread;
+ }
+ failf(data, "no filter connected, conn=%ld, sockindex=%d",
+ data->conn->connection_id, num);
+ *code = CURLE_FAILED_INIT;
+ return -1;
+}
+
+ssize_t Curl_conn_send(struct Curl_easy *data, int num,
+ const void *mem, size_t len, CURLcode *code)
+{
+ struct Curl_cfilter *cf;
+ ssize_t nwritten;
+
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->conn);
+ cf = data->conn->cfilter[num];
+ while(cf && !cf->connected) {
+ cf = cf->next;
+ }
+ if(cf) {
+ nwritten = cf->cft->do_send(cf, data, mem, len, code);
+ /* DEBUGF(infof(data, "Curl_conn_send(handle=%p, index=%d, len=%ld)"
+ " -> %ld, err=%d", data, num, len, nwritten, *code));*/
+ return nwritten;
+ }
+ failf(data, "no filter connected, conn=%ld, sockindex=%d",
+ data->conn->connection_id, num);
+ *code = CURLE_FAILED_INIT;
+ return -1;
+}
+
+CURLcode Curl_cf_create(struct Curl_cfilter **pcf,
+ const struct Curl_cftype *cft,
+ void *ctx)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result = CURLE_OUT_OF_MEMORY;
+
+ DEBUGASSERT(cft);
+ cf = calloc(sizeof(*cf), 1);
+ if(!cf)
+ goto out;
+
+ cf->cft = cft;
+ cf->ctx = ctx;
+ result = CURLE_OK;
+out:
+ *pcf = cf;
+ return result;
+}
+
+void Curl_conn_cf_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int index,
+ struct Curl_cfilter *cf)
+{
+ (void)data;
+ DEBUGASSERT(conn);
+ DEBUGASSERT(!cf->conn);
+ DEBUGASSERT(!cf->next);
+
+ DEBUGF(infof(data, CMSGI(conn, index, "cf_add(filter=%s)"),
+ cf->cft->name));
+ cf->next = conn->cfilter[index];
+ cf->conn = conn;
+ cf->sockindex = index;
+ conn->cfilter[index] = cf;
+}
+
+void Curl_conn_cf_discard(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct Curl_cfilter **pprev = &cf->conn->cfilter[cf->sockindex];
+
+ /* remove from chain if still in there */
+ DEBUGASSERT(cf);
+ while (*pprev) {
+ if (*pprev == cf) {
+ *pprev = cf->next;
+ break;
+ }
+ pprev = &((*pprev)->next);
+ }
+ cf->cft->destroy(cf, data);
+ free(cf);
+}
+
+ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err)
+{
+ return cf->cft->do_send(cf, data, buf, len, err);
+}
+
+ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err)
+{
+ return cf->cft->do_recv(cf, data, buf, len, err);
+}
+
+CURLcode Curl_conn_setup(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ const struct Curl_dns_entry *remotehost,
+ int ssl_mode)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
+
+ DEBUGASSERT(data);
+ /* If no filter is set, we have the "default" setup of connection filters.
+ * The filter chain from botton to top will be:
+ * - SOCKET socket filter for outgoing connection to remotehost
+ * if http_proxy tunneling is engaged:
+ * - SSL if proxytype is CURLPROXY_HTTPS
+ * - HTTP_PROXY_TUNNEL
+ * otherwise, if socks_proxy is engaged:
+ * - SOCKS_PROXY_TUNNEL
+ * - SSL if conn->handler has PROTOPT_SSL
+ */
+ if(!conn->cfilter[sockindex]) {
+ DEBUGF(infof(data, DMSGI(data, sockindex, "setup, init filter chain")));
+ result = Curl_conn_socket_set(data, conn, sockindex);
+ if(result)
+ goto out;
+
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.socksproxy) {
+ result = Curl_conn_socks_proxy_add(data, conn, sockindex);
+ if(result)
+ goto out;
+ }
+
+ if(conn->bits.httpproxy) {
+#ifdef USE_SSL
+ if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) {
+ result = Curl_ssl_cfilter_proxy_add(data, conn, sockindex);
+ if(result)
+ goto out;
+ }
+#endif /* USE_SSL */
+
+#if !defined(CURL_DISABLE_HTTP)
+ if(conn->bits.tunnel_proxy) {
+ result = Curl_conn_http_proxy_add(data, conn, sockindex);
+ if(result)
+ goto out;
+ }
+#endif /* !CURL_DISABLE_HTTP */
+ }
+#endif /* !CURL_DISABLE_PROXY */
+
+#ifdef USE_SSL
+ if(ssl_mode == CURL_CF_SSL_ENABLE
+ || (ssl_mode != CURL_CF_SSL_DISABLE
+ && conn->handler->flags & PROTOPT_SSL)) {
+ result = Curl_ssl_cfilter_add(data, conn, sockindex);
+ if(result)
+ goto out;
+ }
+#else
+ (void)ssl_mode;
+#endif /* USE_SSL */
+
+#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
+ if(data->set.haproxyprotocol) {
+ result = Curl_conn_haproxy_add(data, conn, sockindex);
+ if(result)
+ goto out;
+ }
+#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
+
+ }
+ DEBUGASSERT(conn->cfilter[sockindex]);
+ cf = data->conn->cfilter[sockindex];
+ result = cf->cft->setup(cf, data, remotehost);
+
+out:
+ return result;
+}
+
+CURLcode Curl_conn_connect(struct Curl_easy *data,
+ int sockindex,
+ bool blocking,
+ bool *done)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
+
+ DEBUGASSERT(data);
+
+ cf = data->conn->cfilter[sockindex];
+ DEBUGASSERT(cf);
+ result = cf->cft->connect(cf, data, blocking, done);
+
+ DEBUGF(infof(data, DMSGI(data, sockindex, "connect(block=%d)-> %d, done=%d"),
+ blocking, result, *done));
+ return result;
+}
+
+bool Curl_conn_is_connected(struct connectdata *conn, int sockindex)
+{
+ struct Curl_cfilter *cf;
+
+ cf = conn->cfilter[sockindex];
+ return cf && cf->connected;
+}
+
+bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex)
+{
+ struct Curl_cfilter *cf;
+
+ cf = data->conn->cfilter[sockindex];
+ while(cf) {
+ if(cf->connected)
+ return TRUE;
+ if(cf->cft->flags & CF_TYPE_IP_CONNECT)
+ return FALSE;
+ cf = cf->next;
+ }
+ return FALSE;
+}
+
+bool Curl_conn_is_ssl(struct Curl_easy *data, int sockindex)
+{
+ struct Curl_cfilter *cf = data->conn? data->conn->cfilter[sockindex] : NULL;
+
+ (void)data;
+ for(; cf; cf = cf->next) {
+ if(cf->cft->flags & CF_TYPE_SSL)
+ return TRUE;
+ if(cf->cft->flags & CF_TYPE_IP_CONNECT)
+ return FALSE;
+ }
+ return FALSE;
+}
+
+
+bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex)
+{
+ struct Curl_cfilter *cf;
+
+ (void)data;
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->conn);
+ if(Curl_recv_has_postponed_data(data->conn, sockindex))
+ return TRUE;
+
+ cf = data->conn->cfilter[sockindex];
+ while(cf && !cf->connected) {
+ cf = cf->next;
+ }
+ if(cf) {
+ return cf->cft->has_data_pending(cf, data);
+ }
+ return FALSE;
+}
+
+int Curl_conn_get_select_socks(struct Curl_easy *data, int sockindex,
+ curl_socket_t *socks)
+{
+ struct Curl_cfilter *cf;
+
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->conn);
+ cf = data->conn->cfilter[sockindex];
+ if(cf) {
+ return cf->cft->get_select_socks(cf, data, socks);
+ }
+ return GETSOCK_BLANK;
+}
+
+void Curl_conn_attach_data(struct connectdata *conn,
+ struct Curl_easy *data)
+{
+ size_t i;
+ struct Curl_cfilter *cf;
+
+ for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
+ cf = conn->cfilter[i];
+ if(cf) {
+ while(cf) {
+ cf->cft->attach_data(cf, data);
+ cf = cf->next;
+ }
+ }
+ }
+}
+
+void Curl_conn_detach_data(struct connectdata *conn,
+ struct Curl_easy *data)
+{
+ size_t i;
+ struct Curl_cfilter *cf;
+
+ for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
+ cf = conn->cfilter[i];
+ if(cf) {
+ while(cf) {
+ cf->cft->detach_data(cf, data);
+ cf = cf->next;
+ }
+ }
+ }
+}
+
+void Curl_conn_get_host(struct Curl_easy *data, int sockindex,
+ const char **phost, const char **pdisplay_host,
+ int *pport)
+{
+ struct Curl_cfilter *cf;
+
+ DEBUGASSERT(data->conn);
+ cf = data->conn->cfilter[sockindex];
+ if(cf) {
+ cf->cft->get_host(cf, data, phost, pdisplay_host, pport);
+ }
+ else {
+ /* Some filter ask during shutdown for this, mainly for debugging
+ * purposes. We hand out the defaults, however this is not always
+ * accurate, as the connction might be tunneled, etc. But all that
+ * state is already gone here. */
+ *phost = data->conn->host.name;
+ *pdisplay_host = data->conn->host.dispname;
+ *pport = data->conn->remote_port;
+ }
+}
+
+
diff --git a/libs/libcurl/src/cfilters.h b/libs/libcurl/src/cfilters.h new file mode 100644 index 0000000000..e811272bfb --- /dev/null +++ b/libs/libcurl/src/cfilters.h @@ -0,0 +1,315 @@ +#ifndef HEADER_CURL_CFILTERS_H
+#define HEADER_CURL_CFILTERS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_cfilter;
+struct Curl_easy;
+struct Curl_dns_entry;
+struct connectdata;
+
+/* 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);
+
+/* Setup the connection for `data`, using destination `remotehost`.
+ */
+typedef CURLcode Curl_cft_setup(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost);
+typedef void Curl_cft_close(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+
+typedef CURLcode Curl_cft_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done);
+
+/* Return the hostname and port the connection goes to.
+ * This may change with the connection state of filters when tunneling
+ * is involved.
+ * @param cf the filter to ask
+ * @param data the easy handle currently active
+ * @param phost on return, points to the relevant, real hostname.
+ * this is owned by the connection.
+ * @param pdisplay_host on return, points to the printable hostname.
+ * this is owned by the connection.
+ * @param pport on return, contains the port number
+ */
+typedef void Curl_cft_get_host(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char **phost,
+ const char **pdisplay_host,
+ int *pport);
+
+/* Filters may return sockets and fdset flags they are waiting for.
+ * The passes array has room for up to MAX_SOCKSPEREASYHANDLE sockets.
+ * @return read/write fdset for index in socks
+ * or GETSOCK_BLANK when nothing to wait on
+ */
+typedef int Curl_cft_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks);
+
+typedef bool Curl_cft_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data);
+
+typedef ssize_t Curl_cft_send(struct Curl_cfilter *cf,
+ struct Curl_easy *data, /* transfer */
+ const void *buf, /* data to write */
+ size_t len, /* amount to write */
+ CURLcode *err); /* error to return */
+
+typedef ssize_t Curl_cft_recv(struct Curl_cfilter *cf,
+ struct Curl_easy *data, /* transfer */
+ char *buf, /* store data here */
+ size_t len, /* amount to read */
+ CURLcode *err); /* error to return */
+
+typedef void Curl_cft_attach_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+typedef void Curl_cft_detach_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+
+/**
+ * The easy handle `data` is being detached (no longer served)
+ * by connection `conn`. All filters are informed to release any resources
+ * related to `data`.
+ * Note: there may be several `data` attached to a connection at the same
+ * time.
+ */
+void Curl_conn_detach(struct connectdata *conn, struct Curl_easy *data);
+
+#define CF_TYPE_IP_CONNECT (1 << 0)
+#define CF_TYPE_SSL (1 << 1)
+
+/* A connection filter type, e.g. specific implementation. */
+struct Curl_cftype {
+ const char *name; /* name of the filter type */
+ long flags; /* flags of filter type */
+ Curl_cft_destroy_this *destroy; /* destroy resources of this cf */
+ Curl_cft_setup *setup; /* setup for a connection */
+ Curl_cft_connect *connect; /* establish connection */
+ Curl_cft_close *close; /* close conn */
+ Curl_cft_get_host *get_host; /* host filter talks to */
+ Curl_cft_get_select_socks *get_select_socks;/* sockets to select on */
+ 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_attach_data *attach_data; /* data is being handled here */
+ Curl_cft_detach_data *detach_data; /* data is no longer handled here */
+};
+
+/* 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; /* TODO: like to get rid off this */
+ BIT(connected); /* != 0 iff this filter is connected */
+};
+
+/* 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_setup(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost);
+void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data);
+CURLcode Curl_cf_def_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done);
+void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const char **phost, const char **pdisplay_host,
+ int *pport);
+int Curl_cf_def_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks);
+bool Curl_cf_def_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data);
+ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err);
+ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err);
+void Curl_cf_def_attach_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+void Curl_cf_def_detach_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+
+/**
+ * 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
+ * @parm 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
+ * `data->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);
+
+/**
+ * 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);
+
+/**
+ * Discard, e.g. remove and destroy a specific filter instance.
+ * If the filter is attached to a connection, it will be removed before
+ * it is destroyed.
+ */
+void Curl_conn_cf_discard(struct Curl_cfilter *cf, struct Curl_easy *data);
+
+
+ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err);
+ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err);
+
+#define CURL_CF_SSL_DEFAULT -1
+#define CURL_CF_SSL_DISABLE 0
+#define CURL_CF_SSL_ENABLE 1
+
+/**
+ * Setup the filter chain at `sockindex` in connection `conn`, invoking
+ * the instance `setup(remotehost)` methods. If no filter chain is
+ * installed yet, inspects the configuration in `data` to install a
+ * suitable filter chain.
+ */
+CURLcode Curl_conn_setup(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ const struct Curl_dns_entry *remotehost,
+ int ssl_mode);
+
+/**
+ * Bring the filter chain at `sockindex` for connection `data->conn` into
+ * connected state. Which will set `*done` to TRUE.
+ * This can be called on an already connected chain with no side effects.
+ * When not `blocking`, calls may return without error and `*done != TRUE`,
+ * while the individual filters negotiated the connection.
+ */
+CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex,
+ bool blocking, bool *done);
+
+/**
+ * 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 Curl_easy *data, int sockindex);
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+/**
+ * Get any select fd flags and the socket filters at chain `sockindex`
+ * at connection `conn` might be waiting for.
+ */
+int Curl_conn_get_select_socks(struct Curl_easy *data, int sockindex,
+ curl_socket_t *socks);
+
+/**
+ * Receive data through the filter chain at `sockindex` for connection
+ * `data->conn`. Copy at most `len` bytes into `buf`. Return the
+ * actuel number of bytes copied or a negative value on error.
+ * The error code is placed into `*code`.
+ */
+ssize_t Curl_conn_recv(struct Curl_easy *data, int sockindex, char *buf,
+ size_t len, CURLcode *code);
+
+/**
+ * Send `len` bytes of data from `buf` through the filter chain `sockindex`
+ * at connection `data->conn`. Return the actual number of bytes written
+ * or a negative value on error.
+ * The error code is placed into `*code`.
+ */
+ssize_t Curl_conn_send(struct Curl_easy *data, int sockindex,
+ const void *buf, size_t len, CURLcode *code);
+
+/**
+ * The easy handle `data` is being attached (served) by connection `conn`.
+ * All filters are informed to adapt to handling `data`.
+ * Note: there may be several `data` attached to a connection at the same
+ * time.
+ */
+void Curl_conn_attach_data(struct connectdata *conn,
+ struct Curl_easy *data);
+
+/**
+ * The easy handle `data` is being detached (no longer served)
+ * by connection `conn`. All filters are informed to release any resources
+ * related to `data`.
+ * Note: there may be several `data` attached to a connection at the same
+ * time.
+ */
+void Curl_conn_detach_data(struct connectdata *conn,
+ struct Curl_easy *data);
+
+void Curl_conn_get_host(struct Curl_easy *data, int sockindex,
+ const char **phost, const char **pdisplay_host,
+ int *pport);
+
+
+#endif /* HEADER_CURL_CFILTERS_H */
diff --git a/libs/libcurl/src/config-amigaos.h b/libs/libcurl/src/config-amigaos.h index b7356c7629..543381a073 100644 --- a/libs/libcurl/src/config-amigaos.h +++ b/libs/libcurl/src/config-amigaos.h @@ -1,127 +1,128 @@ -#ifndef HEADER_CURL_CONFIG_AMIGAOS_H -#define HEADER_CURL_CONFIG_AMIGAOS_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 AmigaOS */ -/* ================================================================ */ - -#ifdef __AMIGA__ /* Any AmigaOS flavour */ - -#define HAVE_ARPA_INET_H 1 -#define HAVE_CLOSESOCKET_CAMEL 1 -#define HAVE_ERRNO_H 1 -#define HAVE_INTTYPES_H 1 -#define HAVE_IOCTLSOCKET_CAMEL 1 -#define HAVE_IOCTLSOCKET_CAMEL_FIONBIO 1 -#define HAVE_LONGLONG 1 -#define HAVE_NETDB_H 1 -#define HAVE_NETINET_IN_H 1 -#define HAVE_NET_IF_H 1 -#define HAVE_PWD_H 1 -#define HAVE_RAND_EGD 1 -#define HAVE_SELECT 1 -#define HAVE_SETJMP_H 1 -#define HAVE_SIGNAL 1 -#define HAVE_SIGNAL_H 1 -#define HAVE_SOCKET 1 -#define HAVE_STRCASECMP 1 -#define HAVE_STRDUP 1 -#define HAVE_STRICMP 1 -#define HAVE_STRINGS_H 1 -#define HAVE_STRING_H 1 -#define HAVE_STRUCT_TIMEVAL 1 -#define HAVE_SYS_PARAM_H 1 -#define HAVE_SYS_SOCKET_H 1 -#define HAVE_SYS_SOCKIO_H 1 -#define HAVE_SYS_STAT_H 1 -#define HAVE_SYS_TIME_H 1 -#define HAVE_SYS_TYPES_H 1 -#define HAVE_TIME_H 1 -#define HAVE_UNISTD_H 1 -#define HAVE_UTIME 1 -#define HAVE_UTIME_H 1 -#define HAVE_WRITABLE_ARGV 1 -#define HAVE_SYS_IOCTL_H 1 - -#define NEED_MALLOC_H 1 - -#define SIZEOF_INT 4 -#define SIZEOF_SIZE_T 4 - -#ifndef SIZEOF_CURL_OFF_T -#define SIZEOF_CURL_OFF_T 8 -#endif - -#define USE_MANUAL 1 -#define CURL_DISABLE_LDAP 1 - -#define OS "AmigaOS" - -#define PACKAGE "curl" -#define PACKAGE_BUGREPORT "a suitable mailing list: https://curl.se/mail/" -#define PACKAGE_NAME "curl" -#define PACKAGE_STRING "curl -" -#define PACKAGE_TARNAME "curl" -#define PACKAGE_VERSION "-" -#define CURL_CA_BUNDLE "s:curl-ca-bundle.crt" -#define STDC_HEADERS 1 -#define TIME_WITH_SYS_TIME 1 - -#define in_addr_t int - -#ifndef F_OK -# define F_OK 0 -#endif - -#ifndef O_RDONLY -# define O_RDONLY 0x0000 -#endif - -#ifndef LONG_MAX -# define LONG_MAX 0x7fffffffL -#endif - -#ifndef LONG_MIN -# define LONG_MIN (-0x7fffffffL-1) -#endif - -#define HAVE_RECV 1 -#define RECV_TYPE_ARG1 long -#define RECV_TYPE_ARG2 char * -#define RECV_TYPE_ARG3 long -#define RECV_TYPE_ARG4 long -#define RECV_TYPE_RETV long - -#define HAVE_SEND 1 -#define SEND_TYPE_ARG1 int -#define SEND_QUAL_ARG2 const -#define SEND_TYPE_ARG2 char * -#define SEND_TYPE_ARG3 int -#define SEND_TYPE_ARG4 int -#define SEND_TYPE_RETV int - -#endif /* __AMIGA__ */ -#endif /* HEADER_CURL_CONFIG_AMIGAOS_H */ +#ifndef HEADER_CURL_CONFIG_AMIGAOS_H
+#define HEADER_CURL_CONFIG_AMIGAOS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 AmigaOS */
+/* ================================================================ */
+
+#ifdef __AMIGA__ /* Any AmigaOS flavour */
+
+#define HAVE_ARPA_INET_H 1
+#define HAVE_CLOSESOCKET_CAMEL 1
+#define HAVE_INTTYPES_H 1
+#define HAVE_IOCTLSOCKET_CAMEL 1
+#define HAVE_IOCTLSOCKET_CAMEL_FIONBIO 1
+#define HAVE_LONGLONG 1
+#define HAVE_NETDB_H 1
+#define HAVE_NETINET_IN_H 1
+#define HAVE_NET_IF_H 1
+#define HAVE_PWD_H 1
+#define HAVE_RAND_EGD 1
+#define HAVE_SELECT 1
+#define HAVE_SETJMP_H 1
+#define HAVE_SIGNAL 1
+#define HAVE_SIGNAL_H 1
+#define HAVE_SOCKET 1
+#define HAVE_STRCASECMP 1
+#define HAVE_STRDUP 1
+#define HAVE_STRICMP 1
+#define HAVE_STRINGS_H 1
+#define HAVE_STRING_H 1
+#define HAVE_STRUCT_TIMEVAL 1
+#define HAVE_SYS_PARAM_H 1
+#define HAVE_SYS_SOCKET_H 1
+#define HAVE_SYS_SOCKIO_H 1
+#define HAVE_SYS_STAT_H 1
+#define HAVE_SYS_TIME_H 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_TIME_H 1
+#define HAVE_UNISTD_H 1
+#define HAVE_UTIME 1
+#define HAVE_UTIME_H 1
+#define HAVE_WRITABLE_ARGV 1
+#define HAVE_SYS_IOCTL_H 1
+
+#define NEED_MALLOC_H 1
+
+#define SIZEOF_INT 4
+#define SIZEOF_SIZE_T 4
+
+#ifndef SIZEOF_CURL_OFF_T
+#define SIZEOF_CURL_OFF_T 8
+#endif
+
+#define USE_MANUAL 1
+#define CURL_DISABLE_LDAP 1
+
+#ifndef OS
+#define OS "AmigaOS"
+#endif
+
+#define PACKAGE "curl"
+#define PACKAGE_BUGREPORT "a suitable mailing list: https://curl.se/mail/"
+#define PACKAGE_NAME "curl"
+#define PACKAGE_STRING "curl -"
+#define PACKAGE_TARNAME "curl"
+#define PACKAGE_VERSION "-"
+#define CURL_CA_BUNDLE "s:curl-ca-bundle.crt"
+#define STDC_HEADERS 1
+#define TIME_WITH_SYS_TIME 1
+
+#define in_addr_t int
+
+#ifndef F_OK
+# define F_OK 0
+#endif
+
+#ifndef O_RDONLY
+# define O_RDONLY 0x0000
+#endif
+
+#ifndef LONG_MAX
+# define LONG_MAX 0x7fffffffL
+#endif
+
+#ifndef LONG_MIN
+# define LONG_MIN (-0x7fffffffL-1)
+#endif
+
+#define HAVE_RECV 1
+#define RECV_TYPE_ARG1 long
+#define RECV_TYPE_ARG2 char *
+#define RECV_TYPE_ARG3 long
+#define RECV_TYPE_ARG4 long
+#define RECV_TYPE_RETV long
+
+#define HAVE_SEND 1
+#define SEND_TYPE_ARG1 int
+#define SEND_QUAL_ARG2 const
+#define SEND_TYPE_ARG2 char *
+#define SEND_TYPE_ARG3 int
+#define SEND_TYPE_ARG4 int
+#define SEND_TYPE_RETV int
+
+#endif /* __AMIGA__ */
+#endif /* HEADER_CURL_CONFIG_AMIGAOS_H */
diff --git a/libs/libcurl/src/config-dos.h b/libs/libcurl/src/config-dos.h index 8e3c940d9c..b443da81ce 100644 --- a/libs/libcurl/src/config-dos.h +++ b/libs/libcurl/src/config-dos.h @@ -1,145 +1,142 @@ -#ifndef HEADER_CURL_CONFIG_DOS_H -#define HEADER_CURL_CONFIG_DOS_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - - -/* ================================================================ */ -/* lib/config-dos.h - Hand crafted config file for DOS */ -/* ================================================================ */ - -#if defined(DJGPP) - #define OS "MSDOS/djgpp" -#elif defined(__HIGHC__) - #define OS "MSDOS/HighC" -#else - #define OS "MSDOS/?" -#endif - -#define PACKAGE "curl" - -#define HAVE_ARPA_INET_H 1 -#define HAVE_ASSERT_H 1 -#define HAVE_ERRNO_H 1 -#define HAVE_FCNTL_H 1 -#define HAVE_FREEADDRINFO 1 -#define HAVE_GETADDRINFO 1 -#define HAVE_GETTIMEOFDAY 1 -#define HAVE_IO_H 1 -#define HAVE_IOCTL_FIONBIO 1 -#define HAVE_IOCTLSOCKET 1 -#define HAVE_IOCTLSOCKET_FIONBIO 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_NET_IF_H 1 -#define HAVE_PROCESS_H 1 -#define HAVE_RECV 1 -#define HAVE_SELECT 1 -#define HAVE_SEND 1 -#define HAVE_SETJMP_H 1 -#define HAVE_SETLOCALE 1 -#define HAVE_SETMODE 1 -#define HAVE_SIGNAL 1 -#define HAVE_SOCKET 1 -#define HAVE_STRDUP 1 -#define HAVE_STRICMP 1 -#define HAVE_STRTOLL 1 -#define HAVE_STRUCT_TIMEVAL 1 -#define HAVE_SYS_IOCTL_H 1 -#define HAVE_SYS_SOCKET_H 1 -#define HAVE_SYS_STAT_H 1 -#define HAVE_SYS_TYPES_H 1 -#define HAVE_TIME_H 1 -#define HAVE_UNISTD_H 1 - -#define NEED_MALLOC_H 1 - -#define SIZEOF_INT 4 -#define SIZEOF_LONG 4 -#define SIZEOF_SIZE_T 4 -#define SIZEOF_CURL_OFF_T 4 -#define STDC_HEADERS 1 -#define TIME_WITH_SYS_TIME 1 - -/* Qualifiers for send() and recv() */ - -#define SEND_TYPE_ARG1 int -#define SEND_QUAL_ARG2 const -#define SEND_TYPE_ARG2 void * -#define SEND_TYPE_ARG3 int -#define SEND_TYPE_ARG4 int -#define SEND_TYPE_RETV int - -#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 BSD - -/* CURLDEBUG definition enables memory tracking */ -/* #define CURLDEBUG */ - -#ifdef USE_ZLIB /* Deprecated. Use HAVE_LIBZ instead. */ - #define HAVE_LIBZ 1 -#endif - -/* to disable LDAP */ -#define CURL_DISABLE_LDAP 1 - -#define in_addr_t u_long - -#if defined(__HIGHC__) || \ - (defined(__GNUC__) && (__GNUC__ < 4)) - #define ssize_t int -#endif - -/* Target HAVE_x section */ - -#if defined(DJGPP) - #define HAVE_BASENAME 1 - #define HAVE_STRCASECMP 1 - #define HAVE_SIGACTION 1 - #define HAVE_SIGSETJMP 1 - #define HAVE_SYS_TIME_H 1 - #define HAVE_TERMIOS_H 1 - #define HAVE_VARIADIC_MACROS_GCC 1 - -#elif defined(__HIGHC__) - #define HAVE_SYS_TIME_H 1 - #define strerror(e) strerror_s_((e)) -#endif - -#ifdef MSDOS /* Watt-32 */ - #define HAVE_CLOSE_S 1 -#endif - -#undef word -#undef byte - -#endif /* HEADER_CURL_CONFIG_DOS_H */ +#ifndef HEADER_CURL_CONFIG_DOS_H
+#define HEADER_CURL_CONFIG_DOS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+
+/* ================================================================ */
+/* lib/config-dos.h - Hand crafted config file for DOS */
+/* ================================================================ */
+
+#ifndef OS
+#if defined(DJGPP)
+ #define OS "MSDOS/djgpp"
+#elif defined(__HIGHC__)
+ #define OS "MSDOS/HighC"
+#else
+ #define OS "MSDOS/?"
+#endif
+#endif
+
+#define PACKAGE "curl"
+
+#define USE_MANUAL 1
+
+#define HAVE_ARPA_INET_H 1
+#define HAVE_FCNTL_H 1
+#define HAVE_FREEADDRINFO 1
+#define HAVE_GETADDRINFO 1
+#define HAVE_GETTIMEOFDAY 1
+#define HAVE_IO_H 1
+#define HAVE_IOCTL_FIONBIO 1
+#define HAVE_IOCTLSOCKET 1
+#define HAVE_IOCTLSOCKET_FIONBIO 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_NET_IF_H 1
+#define HAVE_RECV 1
+#define HAVE_SELECT 1
+#define HAVE_SEND 1
+#define HAVE_SETJMP_H 1
+#define HAVE_SETLOCALE 1
+#define HAVE_SETMODE 1
+#define HAVE_SIGNAL 1
+#define HAVE_SOCKET 1
+#define HAVE_STRDUP 1
+#define HAVE_STRICMP 1
+#define HAVE_STRTOLL 1
+#define HAVE_STRUCT_TIMEVAL 1
+#define HAVE_SYS_IOCTL_H 1
+#define HAVE_SYS_SOCKET_H 1
+#define HAVE_SYS_STAT_H 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_TIME_H 1
+#define HAVE_UNISTD_H 1
+
+#define NEED_MALLOC_H 1
+
+#define SIZEOF_INT 4
+#define SIZEOF_LONG 4
+#define SIZEOF_SIZE_T 4
+#define SIZEOF_CURL_OFF_T 4
+#define STDC_HEADERS 1
+#define TIME_WITH_SYS_TIME 1
+
+/* Qualifiers for send() and recv() */
+
+#define SEND_TYPE_ARG1 int
+#define SEND_QUAL_ARG2 const
+#define SEND_TYPE_ARG2 void *
+#define SEND_TYPE_ARG3 int
+#define SEND_TYPE_ARG4 int
+#define SEND_TYPE_RETV int
+
+#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 BSD
+
+/* CURLDEBUG definition enables memory tracking */
+/* #define CURLDEBUG */
+
+/* to disable LDAP */
+#define CURL_DISABLE_LDAP 1
+
+#define in_addr_t u_long
+
+#if defined(__HIGHC__) || \
+ (defined(__GNUC__) && (__GNUC__ < 4))
+ #define ssize_t int
+#endif
+
+/* Target HAVE_x section */
+
+#if defined(DJGPP)
+ #define HAVE_BASENAME 1
+ #define HAVE_STRCASECMP 1
+ #define HAVE_SIGACTION 1
+ #define HAVE_SIGSETJMP 1
+ #define HAVE_SYS_TIME_H 1
+ #define HAVE_TERMIOS_H 1
+ #define HAVE_VARIADIC_MACROS_GCC 1
+
+#elif defined(__HIGHC__)
+ #define HAVE_SYS_TIME_H 1
+ #define strerror(e) strerror_s_((e))
+#endif
+
+#ifdef MSDOS /* Watt-32 */
+ #define HAVE_CLOSE_S 1
+#endif
+
+#undef word
+#undef byte
+
+#endif /* HEADER_CURL_CONFIG_DOS_H */
diff --git a/libs/libcurl/src/config-mac.h b/libs/libcurl/src/config-mac.h index ed07019e6b..498160e14d 100644 --- a/libs/libcurl/src/config-mac.h +++ b/libs/libcurl/src/config-mac.h @@ -1,99 +1,111 @@ -#ifndef HEADER_CURL_CONFIG_MAC_H -#define HEADER_CURL_CONFIG_MAC_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 Mac OS X you must run configure to generate curl_config.h file */ -/* =================================================================== */ - -#define OS "mac" - -/* Define if you want the built-in manual */ -#define USE_MANUAL 1 - -#define HAVE_ERRNO_H 1 -#define HAVE_NETINET_IN_H 1 -#define HAVE_SYS_SOCKET_H 1 -#define HAVE_SYS_SELECT_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_SYS_STAT_H 1 -#define HAVE_STDLIB_H 1 -#define HAVE_TIME_H 1 -#define HAVE_UTIME_H 1 -#define HAVE_SYS_TIME_H 1 -#define HAVE_SYS_UTIME_H 1 - -#define TIME_WITH_SYS_TIME 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 HAVE_SIGNAL_H 1 - -#ifdef MACOS_SSL_SUPPORT -# define USE_OPENSSL 1 -#endif - -#define CURL_DISABLE_LDAP 1 - -#define HAVE_RAND_EGD 1 - -#define HAVE_IOCTL_FIONBIO 1 - -#define SIZEOF_INT 4 -#define SIZEOF_SIZE_T 4 - -#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 - -#define HAVE_EXTRA_STRICMP_H 1 -#define HAVE_EXTRA_STRDUP_H 1 - -#endif /* HEADER_CURL_CONFIG_MAC_H */ +#ifndef HEADER_CURL_CONFIG_MAC_H
+#define HEADER_CURL_CONFIG_MAC_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 Mac OS X you must run configure to generate curl_config.h file */
+/* =================================================================== */
+
+#ifndef OS
+#define 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_SYS_SOCKET_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_SYS_STAT_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_TIME_H 1
+#define HAVE_UTIME_H 1
+#define HAVE_SYS_TIME_H 1
+#define HAVE_SYS_UTIME_H 1
+#define HAVE_SYS_IOCTL_H 1
+
+#define TIME_WITH_SYS_TIME 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 HAVE_SIGNAL_H 1
+
+#ifdef MACOS_SSL_SUPPORT
+# define USE_OPENSSL 1
+#endif
+
+#define CURL_DISABLE_LDAP 1
+
+#define HAVE_RAND_EGD 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
+
+#define HAVE_EXTRA_STRICMP_H 1
+#define HAVE_EXTRA_STRDUP_H 1
+
+#endif /* HEADER_CURL_CONFIG_MAC_H */
diff --git a/libs/libcurl/src/config-os400.h b/libs/libcurl/src/config-os400.h index 2eef1a0c1b..4df1c4ab20 100644 --- a/libs/libcurl/src/config-os400.h +++ b/libs/libcurl/src/config-os400.h @@ -1,374 +1,373 @@ -#ifndef HEADER_CURL_CONFIG_OS400_H -#define HEADER_CURL_CONFIG_OS400_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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) - -#undef PACKAGE - -/* Version number of this archive. */ -#undef VERSION - -/* Define cpu-machine-OS */ -#define OS "OS/400" - -/* 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 ENABLE_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 this as a suitable file to read random data from */ -#undef RANDOM_FILE - -/* Define this to your Entropy Gathering Daemon socket pathname */ -#undef EGD_SOCKET - -/* 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 <errno.h> header file. */ -#define HAVE_ERRNO_H - -/* 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 <inttypes.h> header file. */ -#define HAVE_INTTYPES_H - -/* Define if you have the <io.h> header file. */ -#undef HAVE_IO_H - -/* Define if you have the `socket' library (-lsocket). */ -#undef HAVE_LIBSOCKET - -/* 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 Heimdal gssapi libraries */ -#define HAVE_GSSHEIMDAL - -/* Define if you have the MIT gssapi libraries */ -#undef HAVE_GSSMIT - -/* Define if you need the malloc.h header file even with stdlib.h */ -/* #define NEED_MALLOC_H 1 */ - -/* 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 `RAND_egd' function. */ -#undef HAVE_RAND_EGD - -/* 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 <signal.h> header file. */ -#define HAVE_SIGNAL_H - -/* Define if you have the `socket' function. */ -#define HAVE_SOCKET - -/* Define if you have the <ssl.h> header file. */ -#undef HAVE_SSL_H - -/* Define if you have the <stdint.h> header file. */ -#undef HAVE_STDINT_H - -/* Define if you have the <stdlib.h> header file. */ -#define HAVE_STDLIB_H - - -/* 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 <string.h> header file. */ -#define HAVE_STRING_H - -/* Define if you have the <stropts.h> header file. */ -#undef HAVE_STROPTS_H - -/* Define if you have the `strtok_r' function. */ -#define HAVE_STRTOK_R - -/* Define if you have the `strtoll' function. */ -#undef HAVE_STRTOLL /* Allows ASCII compile on V5R1. */ - -/* 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/socket.h> header file. */ -#define HAVE_SYS_SOCKET_H - -/* Define if you have the <sys/sockio.h> header file. */ -#undef HAVE_SYS_SOCKIO_H - -/* Define if you have the <sys/stat.h> header file. */ -#define HAVE_SYS_STAT_H - -/* Define if you have the <sys/time.h> header file. */ -#define HAVE_SYS_TIME_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 <time.h> header file. */ -#define HAVE_TIME_H - -/* Define if you have the <unistd.h> header file. */ -#define HAVE_UNISTD_H - -/* Name of package */ -#undef PACKAGE - -/* 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 - -/* Define if you can safely include both <sys/time.h> and <time.h>. */ -#define TIME_WITH_SYS_TIME - -/* Define to enable HTTP3 support (experimental, requires NGTCP2, QUICHE or - MSH3) */ -#undef ENABLE_QUIC - -/* 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 - -/* type to use in place of in_addr_t if not defined */ -#define in_addr_t unsigned long - -/* 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 GSKit package. */ -#define USE_GSKIT - -/* 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 */ +#ifndef HEADER_CURL_CONFIG_OS400_H
+#define HEADER_CURL_CONFIG_OS400_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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)
+
+#undef PACKAGE
+
+/* Version number of this archive. */
+#undef VERSION
+
+/* Define cpu-machine-OS */
+#ifndef OS
+#define 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 ENABLE_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 this as a suitable file to read random data from */
+#undef RANDOM_FILE
+
+/* Define this to your Entropy Gathering Daemon socket pathname */
+#undef EGD_SOCKET
+
+/* 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 <inttypes.h> header file. */
+#define HAVE_INTTYPES_H
+
+/* Define if you have the <io.h> header file. */
+#undef HAVE_IO_H
+
+/* Define if you have the `socket' library (-lsocket). */
+#undef HAVE_LIBSOCKET
+
+/* 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 Heimdal gssapi libraries */
+#define HAVE_GSSHEIMDAL
+
+/* Define if you have the MIT gssapi libraries */
+#undef HAVE_GSSMIT
+
+/* Define if you need the malloc.h header file even with stdlib.h */
+/* #define NEED_MALLOC_H 1 */
+
+/* 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 `RAND_egd' function. */
+#undef HAVE_RAND_EGD
+
+/* 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 <signal.h> header file. */
+#define HAVE_SIGNAL_H
+
+/* Define if you have the `socket' function. */
+#define HAVE_SOCKET
+
+/* Define if you have the <ssl.h> header file. */
+#undef HAVE_SSL_H
+
+/* Define if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H
+
+
+/* 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 <string.h> header file. */
+#define HAVE_STRING_H
+
+/* Define if you have the <stropts.h> header file. */
+#undef HAVE_STROPTS_H
+
+/* Define if you have the `strtok_r' function. */
+#define HAVE_STRTOK_R
+
+/* Define if you have the `strtoll' function. */
+#undef HAVE_STRTOLL /* Allows ASCII compile on V5R1. */
+
+/* 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/socket.h> header file. */
+#define HAVE_SYS_SOCKET_H
+
+/* Define if you have the <sys/sockio.h> header file. */
+#undef HAVE_SYS_SOCKIO_H
+
+/* Define if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H
+
+/* Define if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_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 <time.h> header file. */
+#define HAVE_TIME_H
+
+/* Define if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H
+
+/* Name of package */
+#undef PACKAGE
+
+/* 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
+
+/* Define if you can safely include both <sys/time.h> and <time.h>. */
+#define TIME_WITH_SYS_TIME
+
+/* Define to enable HTTP3 support (experimental, requires NGTCP2, QUICHE or
+ MSH3) */
+#undef ENABLE_QUIC
+
+/* 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
+
+/* type to use in place of in_addr_t if not defined */
+#define in_addr_t unsigned long
+
+/* 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 GSKit package. */
+#define USE_GSKIT
+
+/* 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 */
diff --git a/libs/libcurl/src/config-plan9.h b/libs/libcurl/src/config-plan9.h index 49a719c1aa..dea75d970a 100644 --- a/libs/libcurl/src/config-plan9.h +++ b/libs/libcurl/src/config-plan9.h @@ -1,156 +1,156 @@ -#ifndef HEADER_CURL_CONFIG_PLAN9_H -#define HEADER_CURL_CONFIG_PLAN9_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 ENABLE_IPV6 1 -#define CURL_DISABLE_LDAP 1 - -#define NEED_REENTRANT 1 -#define OS "plan9" -#define PACKAGE "curl" -#define PACKAGE_NAME "curl" -#define PACKAGE_BUGREPORT "a suitable mailing list: https://curl.se/mail/" -#define PACKAGE_STRING "curl -" -#define PACKAGE_TARNAME "curl" -#define PACKAGE_VERSION "-" -#define RANDOM_FILE "/dev/random" -#define VERSION "0.0.0" /* TODO */ - -#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_ASSERT_H 1 -#define HAVE_BASENAME 1 -#define HAVE_BOOL_T 1 -#define HAVE_ERRNO_H 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_INTTYPES_H 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_FINE 1 -#define HAVE_POLL_H 1 -#define HAVE_PTHREAD_H 1 -#define HAVE_SETJMP_H 1 -#define HAVE_SETLOCALE 1 - -#define HAVE_SIGACTION 1 -#define HAVE_SIGNAL 1 -#define HAVE_SIGNAL_H 1 -#define HAVE_SIGSETJMP 1 -#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 -#define HAVE_SOCKET 1 -#define HAVE_SSL_GET_SHUTDOWN 1 -#define HAVE_STDBOOL_H 1 -#define HAVE_STDINT_H 1 -#define HAVE_STDLIB_H 1 -#define HAVE_STRCASECMP 1 -#define HAVE_STRDUP 1 -#define HAVE_STRING_H 1 -#define HAVE_STRTOK_R 1 -#define HAVE_STRTOLL 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_SOCKET_H 1 -#define HAVE_SYS_STAT_H 1 -#define HAVE_SYS_TIME_H 1 -#define HAVE_SYS_TYPES_H 1 -#define HAVE_SYS_UN_H 1 -#define HAVE_TERMIOS_H 1 -#define HAVE_TIME_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 TIME_WITH_SYS_TIME 1 -#define USE_MANUAL 1 - -#define __attribute__(x) - -#ifndef __cplusplus -#undef inline -#endif - -#endif /* HEADER_CURL_CONFIG_PLAN9_H */ +#ifndef HEADER_CURL_CONFIG_PLAN9_H
+#define HEADER_CURL_CONFIG_PLAN9_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 ENABLE_IPV6 1
+#define CURL_DISABLE_LDAP 1
+
+#define NEED_REENTRANT 1
+#ifndef OS
+#define OS "plan9"
+#endif
+#define PACKAGE "curl"
+#define PACKAGE_NAME "curl"
+#define PACKAGE_BUGREPORT "a suitable mailing list: https://curl.se/mail/"
+#define PACKAGE_STRING "curl -"
+#define PACKAGE_TARNAME "curl"
+#define PACKAGE_VERSION "-"
+#define RANDOM_FILE "/dev/random"
+#define VERSION "0.0.0" /* TODO */
+
+#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_INTTYPES_H 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_FINE 1
+#define HAVE_POLL_H 1
+#define HAVE_PTHREAD_H 1
+#define HAVE_SETJMP_H 1
+#define HAVE_SETLOCALE 1
+
+#define HAVE_SIGACTION 1
+#define HAVE_SIGNAL 1
+#define HAVE_SIGNAL_H 1
+#define HAVE_SIGSETJMP 1
+#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1
+#define HAVE_SOCKET 1
+#define HAVE_SSL_GET_SHUTDOWN 1
+#define HAVE_STDBOOL_H 1
+#define HAVE_STDINT_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STRCASECMP 1
+#define HAVE_STRDUP 1
+#define HAVE_STRING_H 1
+#define HAVE_STRTOK_R 1
+#define HAVE_STRTOLL 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_SOCKET_H 1
+#define HAVE_SYS_STAT_H 1
+#define HAVE_SYS_TIME_H 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_SYS_UN_H 1
+#define HAVE_TERMIOS_H 1
+#define HAVE_TIME_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 TIME_WITH_SYS_TIME 1
+#define USE_MANUAL 1
+
+#define __attribute__(x)
+
+#ifndef __cplusplus
+#undef inline
+#endif
+
+#endif /* HEADER_CURL_CONFIG_PLAN9_H */
diff --git a/libs/libcurl/src/config-riscos.h b/libs/libcurl/src/config-riscos.h index d6c81f3eb9..04789390d9 100644 --- a/libs/libcurl/src/config-riscos.h +++ b/libs/libcurl/src/config-riscos.h @@ -1,311 +1,310 @@ -#ifndef HEADER_CURL_CONFIG_RISCOS_H -#define HEADER_CURL_CONFIG_RISCOS_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 */ -/* ================================================================ */ - -/* Name of this package! */ -#undef PACKAGE - -/* Version number of this archive. */ -#undef VERSION - -/* Define cpu-machine-OS */ -#define OS "ARM-RISC OS" - -/* 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 ENABLE_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 this as a suitable file to read random data from */ -#undef RANDOM_FILE - -/* Define this to your Entropy Gathering Daemon socket pathname */ -#undef EGD_SOCKET - -/* Define if you want to enable IPv6 support */ -#undef ENABLE_IPV6 - -/* 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 <errno.h> header file. */ -#define HAVE_ERRNO_H - -/* 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 <inttypes.h> header file. */ -#define HAVE_INTTYPES_H - -/* Define if you have the <io.h> header file. */ -#undef HAVE_IO_H - -/* Define if you have the `socket' library (-lsocket). */ -#undef HAVE_LIBSOCKET - -/* Define if you need the malloc.h header file even with stdlib.h */ -/* #define NEED_MALLOC_H 1 */ - -/* 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 `RAND_egd' function. */ -#undef HAVE_RAND_EGD - -/* 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 <signal.h> header file. */ -#define HAVE_SIGNAL_H - -/* Define if you have the `socket' function. */ -#define HAVE_SOCKET - -/* Define if you have the <ssl.h> header file. */ -#undef HAVE_SSL_H - -/* Define if you have the <stdint.h> header file. */ -#undef HAVE_STDINT_H - -/* Define if you have the <stdlib.h> header file. */ -#define HAVE_STDLIB_H - -/* 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 <string.h> header file. */ -#define HAVE_STRING_H - -/* Define if you have the `strtok_r' function. */ -#undef HAVE_STRTOK_R - -/* Define if you have the `strtoll' function. */ -#undef HAVE_STRTOLL - -/* 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/socket.h> header file. */ -#define HAVE_SYS_SOCKET_H - -/* Define if you have the <sys/sockio.h> header file. */ -#undef HAVE_SYS_SOCKIO_H - -/* Define if you have the <sys/stat.h> header file. */ -#undef HAVE_SYS_STAT_H - -/* Define if you have the <sys/time.h> header file. */ -#define HAVE_SYS_TIME_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 <time.h> header file. */ -#undef HAVE_TIME_H - -/* Define if you have the <unistd.h> header file. */ -#define HAVE_UNISTD_H - -/* Name of package */ -#undef PACKAGE - -/* 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 - -/* Define if you can safely include both <sys/time.h> and <time.h>. */ -#undef TIME_WITH_SYS_TIME - -/* Version number of package */ -#undef VERSION - -/* Define if on AIX 3. - System headers sometimes define this. - We just want to avoid a redefinition error message. */ -#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' 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 */ +#ifndef HEADER_CURL_CONFIG_RISCOS_H
+#define HEADER_CURL_CONFIG_RISCOS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 */
+/* ================================================================ */
+
+/* Name of this package! */
+#undef PACKAGE
+
+/* Version number of this archive. */
+#undef VERSION
+
+/* Define cpu-machine-OS */
+#ifndef OS
+#define 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 ENABLE_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 this as a suitable file to read random data from */
+#undef RANDOM_FILE
+
+/* Define this to your Entropy Gathering Daemon socket pathname */
+#undef EGD_SOCKET
+
+/* Define if you want to enable IPv6 support */
+#undef ENABLE_IPV6
+
+/* 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 <inttypes.h> header file. */
+#define HAVE_INTTYPES_H
+
+/* Define if you have the <io.h> header file. */
+#undef HAVE_IO_H
+
+/* Define if you have the `socket' library (-lsocket). */
+#undef HAVE_LIBSOCKET
+
+/* Define if you need the malloc.h header file even with stdlib.h */
+/* #define NEED_MALLOC_H 1 */
+
+/* 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 `RAND_egd' function. */
+#undef HAVE_RAND_EGD
+
+/* 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 <signal.h> header file. */
+#define HAVE_SIGNAL_H
+
+/* Define if you have the `socket' function. */
+#define HAVE_SOCKET
+
+/* Define if you have the <ssl.h> header file. */
+#undef HAVE_SSL_H
+
+/* Define if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H
+
+/* 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 <string.h> header file. */
+#define HAVE_STRING_H
+
+/* Define if you have the `strtok_r' function. */
+#undef HAVE_STRTOK_R
+
+/* Define if you have the `strtoll' function. */
+#undef HAVE_STRTOLL
+
+/* 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/socket.h> header file. */
+#define HAVE_SYS_SOCKET_H
+
+/* Define if you have the <sys/sockio.h> header file. */
+#undef HAVE_SYS_SOCKIO_H
+
+/* Define if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_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 <time.h> header file. */
+#undef HAVE_TIME_H
+
+/* Define if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H
+
+/* Name of package */
+#undef PACKAGE
+
+/* 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
+
+/* Define if you can safely include both <sys/time.h> and <time.h>. */
+#undef TIME_WITH_SYS_TIME
+
+/* Version number of package */
+#undef VERSION
+
+/* Define if on AIX 3.
+ System headers sometimes define this.
+ We just want to avoid a redefinition error message. */
+#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' 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 */
diff --git a/libs/libcurl/src/config-win32.h b/libs/libcurl/src/config-win32.h index 644297db44..a8392aee06 100644 --- a/libs/libcurl/src/config-win32.h +++ b/libs/libcurl/src/config-win32.h @@ -1,673 +1,657 @@ -#ifndef HEADER_CURL_CONFIG_WIN32_H -#define HEADER_CURL_CONFIG_WIN32_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 */ -/* ================================================================ */ - -/* ---------------------------------------------------------------- */ -/* HEADER FILES */ -/* ---------------------------------------------------------------- */ - -/* Define if you have the <arpa/inet.h> header file. */ -/* #define HAVE_ARPA_INET_H 1 */ - -/* Define if you have the <assert.h> header file. */ -#define HAVE_ASSERT_H 1 - -/* Define if you have the <errno.h> header file. */ -#define HAVE_ERRNO_H 1 - -/* Define if you have the <fcntl.h> header file. */ -#define HAVE_FCNTL_H 1 - -/* Define to 1 if you have the <inttypes.h> header file. */ -#if defined(_MSC_VER) && (_MSC_VER >= 1800) -#define HAVE_INTTYPES_H 1 -#endif - -/* Define if you have the <io.h> header file. */ -#define HAVE_IO_H 1 - -/* Define if you have the <locale.h> header file. */ -#define HAVE_LOCALE_H 1 - -/* Define if you need <malloc.h> header even with <stdlib.h> header file. */ -#if !defined(__SALFORDC__) && !defined(__POCC__) -#define NEED_MALLOC_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 if you have the <process.h> header file. */ -#ifndef __SALFORDC__ -#define HAVE_PROCESS_H 1 -#endif - -/* Define if you have the <signal.h> header file. */ -#define HAVE_SIGNAL_H 1 - -/* Define if you have the <ssl.h> header file. */ -/* #define HAVE_SSL_H 1 */ - -/* Define to 1 if you have the <stdbool.h> header file. */ -#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || \ - defined(__MINGW64_VERSION_MAJOR) -#define HAVE_STDBOOL_H 1 -#endif - -/* Define if you have the <stdlib.h> header file. */ -#define HAVE_STDLIB_H 1 - -/* Define if you have the <sys/param.h> header file. */ -/* #define HAVE_SYS_PARAM_H 1 */ - -/* Define if you have the <sys/select.h> header file. */ -/* #define HAVE_SYS_SELECT_H 1 */ - -/* Define if you have the <sys/socket.h> header file. */ -/* #define HAVE_SYS_SOCKET_H 1 */ - -/* Define if you have the <sys/sockio.h> header file. */ -/* #define HAVE_SYS_SOCKIO_H 1 */ - -/* Define if you have the <sys/stat.h> header file. */ -#define HAVE_SYS_STAT_H 1 - -/* Define if you have the <sys/time.h> header file. */ -/* #define HAVE_SYS_TIME_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. */ -#ifndef __BORLANDC__ -#define HAVE_SYS_UTIME_H 1 -#endif - -/* 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 <time.h> header file. */ -#define HAVE_TIME_H 1 - -/* Define if you have the <unistd.h> header file. */ -#if defined(__MINGW32__) || defined(__LCC__) || defined(__POCC__) -#define HAVE_UNISTD_H 1 -#endif - -/* Define if you have the <windows.h> header file. */ -#define HAVE_WINDOWS_H 1 - -/* Define if you have the <winsock2.h> header file. */ -#ifndef __SALFORDC__ -#define HAVE_WINSOCK2_H 1 -#endif - -/* Define if you have the <ws2tcpip.h> header file. */ -#ifndef __SALFORDC__ -#define HAVE_WS2TCPIP_H 1 -#endif - -/* Define to 1 if you have the <setjmp.h> header file. */ -#define HAVE_SETJMP_H 1 - -/* Define to 1 if you have the <string.h> header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the <libgen.h> header file. */ -#if defined(__MINGW64_VERSION_MAJOR) -#define HAVE_LIBGEN_H 1 -#endif - -/* ---------------------------------------------------------------- */ -/* OTHER HEADER INFO */ -/* ---------------------------------------------------------------- */ - -/* Define if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Define if you can safely include both <sys/time.h> and <time.h>. */ -/* #define TIME_WITH_SYS_TIME 1 */ - -/* Define to 1 if bool is an available type. */ -#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || \ - defined(__MINGW64_VERSION_MAJOR) -#define HAVE_BOOL_T 1 -#endif - -/* ---------------------------------------------------------------- */ -/* FUNCTIONS */ -/* ---------------------------------------------------------------- */ - -/* Define if you have the closesocket function. */ -#define HAVE_CLOSESOCKET 1 - -/* Define if you have the ftruncate function. */ -#if defined(__MINGW64_VERSION_MAJOR) -#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. */ -/* #define HAVE_GETTIMEOFDAY 1 */ - -/* 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 - -/* 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 socket function. */ -#define HAVE_SOCKET 1 - -/* Define if libSSH2 is in use */ -#define USE_LIBSSH2 1 -#define HAVE_LIBSSH2_H 1 - -/* Define if you have the strcasecmp function. */ -#ifdef __MINGW32__ -#define HAVE_STRCASECMP 1 -#endif - -/* Define if you have the strdup function. */ -#define HAVE_STRDUP 1 - -/* Define if you have the stricmp function. */ -#define HAVE_STRICMP 1 - -/* Define if you have the strtoll function. */ -#if defined(__MINGW32__) || defined(__POCC__) || \ - (defined(_MSC_VER) && (_MSC_VER >= 1800)) -#define HAVE_STRTOLL 1 -#endif - -/* Define if you have the utime function. */ -#ifndef __BORLANDC__ -#define HAVE_UTIME 1 -#endif - -/* 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) -#define HAVE_SNPRINTF 1 -#endif - -#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 /* Vista */ -/* Define to 1 if you have a IPv6 capable working inet_ntop function. */ -#define HAVE_INET_NTOP 1 -/* Define to 1 if you have a IPv6 capable working inet_pton function. */ -#define HAVE_INET_PTON 1 -#endif - -/* Define to 1 if you have the `basename' function. */ -#if defined(__MINGW64_VERSION_MAJOR) -#define HAVE_BASENAME 1 -#endif - -/* Define to 1 if you have the strtok_r function. */ -#if defined(__MINGW64_VERSION_MAJOR) -#define HAVE_STRTOK_R 1 -#endif - -/* Define to 1 if you have the signal function. */ -#define HAVE_SIGNAL 1 - -/* ---------------------------------------------------------------- */ -/* TYPEDEF REPLACEMENTS */ -/* ---------------------------------------------------------------- */ - -/* Define if in_addr_t is not an available 'typedefed' type. */ -#define in_addr_t unsigned long - -/* Define if ssize_t is not an available 'typedefed' type. */ -#ifndef _SSIZE_T_DEFINED -# if defined(__POCC__) || defined(__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. */ -#if defined(_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 - -/* Define to the size of `off_t', as computed by sizeof. */ -#ifndef SIZEOF_OFF_T -#define SIZEOF_OFF_T 8 -#endif - -/* ---------------------------------------------------------------- */ -/* BSD-style lwIP TCP/IP stack SPECIFIC */ -/* ---------------------------------------------------------------- */ - -/* Define to use BSD-style lwIP TCP/IP stack. */ -/* #define USE_LWIPSOCK 1 */ - -#ifdef USE_LWIPSOCK -# undef USE_WINSOCK -# undef HAVE_WINSOCK2_H -# undef HAVE_WS2TCPIP_H -# undef HAVE_ERRNO_H -# undef HAVE_GETHOSTNAME -# undef LWIP_POSIX_SOCKETS_IO_NAMES -# undef RECV_TYPE_ARG1 -# undef RECV_TYPE_ARG3 -# undef SEND_TYPE_ARG1 -# undef SEND_TYPE_ARG3 -# define HAVE_FREEADDRINFO -# define HAVE_GETADDRINFO -# define HAVE_GETHOSTBYNAME_R -# define HAVE_GETHOSTBYNAME_R_6 -# define LWIP_POSIX_SOCKETS_IO_NAMES 0 -# define RECV_TYPE_ARG1 int -# define RECV_TYPE_ARG3 size_t -# define SEND_TYPE_ARG1 int -# define SEND_TYPE_ARG3 size_t -#endif - -/* ---------------------------------------------------------------- */ -/* Watt-32 tcp/ip SPECIFIC */ -/* ---------------------------------------------------------------- */ - -#ifdef USE_WATT32 - #include <tcp.h> - #undef byte - #undef word - #undef USE_WINSOCK - #undef HAVE_WINSOCK2_H - #undef HAVE_WS2TCPIP_H - #define HAVE_GETADDRINFO - #define HAVE_SYS_IOCTL_H - #define HAVE_SYS_SOCKET_H - #define HAVE_NETINET_IN_H - #define HAVE_NETDB_H - #define HAVE_ARPA_INET_H - #define HAVE_FREEADDRINFO - #define SOCKET int -#endif - - -/* ---------------------------------------------------------------- */ -/* 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 C99 variadic macro style. */ -#if defined(_MSC_VER) && (_MSC_VER >= 1400) -#define HAVE_VARIADIC_MACROS_C99 1 -#endif - -/* Define if the compiler supports the 'long long' data type. */ -#if defined(__MINGW32__) || \ - (defined(_MSC_VER) && (_MSC_VER >= 1310)) || \ - (defined(__BORLANDC__) && (__BORLANDC__ >= 0x561)) -#define HAVE_LONGLONG 1 -#endif - -/* Define to avoid VS2005 complaining about portable C functions. */ -#if defined(_MSC_VER) && (_MSC_VER >= 1400) -#define _CRT_SECURE_NO_DEPRECATE 1 -#define _CRT_NONSTDC_NO_DEPRECATE 1 -#endif - -/* mingw-w64, mingw using >= MSVCR80, and visual studio >= 2005 (MSVCR80) - all default to 64-bit time_t unless _USE_32BIT_TIME_T is defined */ -#if defined(__MINGW64_VERSION_MAJOR) || \ - (defined(__MINGW32__) && (__MSVCRT_VERSION__ >= 0x0800)) || \ - (defined(_MSC_VER) && (_MSC_VER >= 1400)) -# ifndef _USE_32BIT_TIME_T -# define SIZEOF_TIME_T 8 -# else -# define SIZEOF_TIME_T 4 -# endif -#endif - -/* Define some minimum and default build targets for Visual Studio */ -#if defined(_MSC_VER) - /* Officially, Microsoft's Windows SDK versions 6.X does not support Windows - 2000 as a supported build target. VS2008 default installations provides - an embedded Windows SDK v6.0A along with the claim that Windows 2000 is a - valid build target for VS2008. Popular belief is that binaries built with - VS2008 using Windows SDK versions v6.X and Windows 2000 as a build target - are functional. */ -# define VS2008_MIN_TARGET 0x0500 - - /* The minimum build target for VS2012 is Vista unless Update 1 is installed - and the v110_xp toolset is chosen. */ -# if defined(_USING_V110_SDK71_) -# define VS2012_MIN_TARGET 0x0501 -# else -# define VS2012_MIN_TARGET 0x0600 -# endif - - /* VS2008 default build target is Windows Vista. We override default target - to be Windows XP. */ -# define VS2008_DEF_TARGET 0x0501 - - /* VS2012 default build target is Windows Vista unless Update 1 is installed - and the v110_xp toolset is chosen. */ -# if defined(_USING_V110_SDK71_) -# define VS2012_DEF_TARGET 0x0501 -# else -# define VS2012_DEF_TARGET 0x0600 -# endif -#endif - -/* VS2008 default target settings and minimum build target check. */ -#if defined(_MSC_VER) && (_MSC_VER >= 1500) && (_MSC_VER <= 1600) -# 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 Windows build targets prior to Windows 2000 -# endif -#endif - -/* VS2012 default target settings and minimum build target check. */ -#if defined(_MSC_VER) && (_MSC_VER >= 1700) -# 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) -# if defined(_USING_V110_SDK71_) -# error VS2012 does not support Windows build targets prior to Windows XP -# else -# error VS2012 does not support Windows build targets prior to Windows \ -Vista -# endif -# endif -#endif - -/* When no build target is specified Pelles C 5.00 and later default build - target is Windows Vista. We override default target to be Windows 2000. */ -#if defined(__POCC__) && (__POCC__ >= 500) -# ifndef _WIN32_WINNT -# define _WIN32_WINNT 0x0500 -# endif -# ifndef WINVER -# define WINVER 0x0500 -# endif -#endif - -/* Availability of freeaddrinfo, getaddrinfo, and if_nametoindex - functions is quite convoluted, compiler dependent and even build target - dependent. */ -#if defined(HAVE_WS2TCPIP_H) -# if defined(__POCC__) -# define HAVE_FREEADDRINFO 1 -# define HAVE_GETADDRINFO 1 -# define HAVE_GETADDRINFO_THREADSAFE 1 -# elif defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) -# define HAVE_FREEADDRINFO 1 -# define HAVE_GETADDRINFO 1 -# define HAVE_GETADDRINFO_THREADSAFE 1 -# elif defined(_MSC_VER) && (_MSC_VER >= 1200) -# define HAVE_FREEADDRINFO 1 -# define HAVE_GETADDRINFO 1 -# define HAVE_GETADDRINFO_THREADSAFE 1 -# endif -#endif - -#if defined(__POCC__) -# ifndef _MSC_VER -# error Microsoft extensions /Ze compiler option is required -# endif -# ifndef __POCC__OLDNAMES -# error Compatibility names /Go compiler option is required -# endif -#endif - -/* ---------------------------------------------------------------- */ -/* STRUCT RELATED */ -/* ---------------------------------------------------------------- */ - -/* Define if you have struct sockaddr_storage. */ -#if !defined(__SALFORDC__) && !defined(__BORLANDC__) -#define HAVE_STRUCT_SOCKADDR_STORAGE 1 -#endif - -/* 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 */ -/* ---------------------------------------------------------------- */ - -#if defined(_MSC_VER) && !defined(_WIN32_WCE) -# if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64) -# define USE_WIN32_LARGE_FILES -# else -# define USE_WIN32_SMALL_FILES -# endif -#endif - -#if defined(__MINGW32__) && !defined(USE_WIN32_LARGE_FILES) -# define USE_WIN32_LARGE_FILES -#endif - -#if defined(__POCC__) -# undef USE_WIN32_LARGE_FILES -#endif - -#if !defined(USE_WIN32_LARGE_FILES) && !defined(USE_WIN32_SMALL_FILES) -# define USE_WIN32_SMALL_FILES -#endif - -/* Number of bits in a file offset, on hosts where this is settable. */ -#if defined(USE_WIN32_LARGE_FILES) && defined(__MINGW64_VERSION_MAJOR) -# ifndef _FILE_OFFSET_BITS -# define _FILE_OFFSET_BITS 64 -# endif -#endif - -/* ---------------------------------------------------------------- */ -/* 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 */ -/* ---------------------------------------------------------------- */ - -#if defined(CURL_HAS_NOVELL_LDAPSDK) -#undef USE_WIN32_LDAP -#define HAVE_LDAP_SSL_H 1 -#define HAVE_LDAP_URL_PARSE 1 -#elif defined(CURL_HAS_OPENLDAP_LDAPSDK) -#undef USE_WIN32_LDAP -#define HAVE_LDAP_URL_PARSE 1 -#else -#undef HAVE_LDAP_URL_PARSE -#define HAVE_LDAP_SSL 1 -#define USE_WIN32_LDAP 1 -#endif - -#if defined(__POCC__) && defined(USE_WIN32_LDAP) -# define CURL_DISABLE_LDAP 1 -#endif - -/* if SSL is enabled */ -#define USE_OPENSSL 1 - -/* Define to use the Windows crypto library. */ -#if !defined(CURL_WINDOWS_APP) -#define USE_WIN32_CRYPTO -#endif - -/* Define to use Unix sockets. */ -#define USE_UNIX_SOCKETS - -/* ---------------------------------------------------------------- */ -/* ADDITIONAL DEFINITIONS */ -/* ---------------------------------------------------------------- */ - -/* Define cpu-machine-OS */ -#if !defined(OS) -#if defined(_M_IX86) || defined(__i386__) /* x86 (MSVC or gcc) */ -#define OS "i386-pc-win32" -#elif defined(_M_X64) || defined(__x86_64__) /* x86_64 (MSVC >=2005 or gcc) */ -#define OS "x86_64-pc-win32" -#elif defined(_M_IA64) || defined(__ia64__) /* Itanium */ -#define OS "ia64-pc-win32" -#elif defined(_M_ARM_NT) || defined(__arm__) /* ARMv7-Thumb2 (Windows RT) */ -#define OS "thumbv7a-pc-win32" -#elif defined(_M_ARM64) || defined(__aarch64__) /* ARM64 (Windows 10) */ -#define OS "aarch64-pc-win32" -#else -#define OS "unknown-pc-win32" -#endif -#endif - -/* Name of package */ -#define PACKAGE "curl" - -/* If you want to build curl with the built-in manual */ -#define USE_MANUAL 1 - -#if defined(__POCC__) || defined(USE_IPV6) -# define ENABLE_IPV6 1 -#endif - -#endif /* HEADER_CURL_CONFIG_WIN32_H */ +#ifndef HEADER_CURL_CONFIG_WIN32_H
+#define HEADER_CURL_CONFIG_WIN32_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 */
+/* ================================================================ */
+
+/* ---------------------------------------------------------------- */
+/* HEADER FILES */
+/* ---------------------------------------------------------------- */
+
+/* Define if you have the <arpa/inet.h> header file. */
+/* #define HAVE_ARPA_INET_H 1 */
+
+/* Define if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#if defined(_MSC_VER) && (_MSC_VER >= 1800)
+#define HAVE_INTTYPES_H 1
+#endif
+
+/* Define if you have the <io.h> header file. */
+#define HAVE_IO_H 1
+
+/* Define if you have the <locale.h> header file. */
+#define HAVE_LOCALE_H 1
+
+/* Define if you need <malloc.h> header even with <stdlib.h> header file. */
+#if !defined(__SALFORDC__) && !defined(__POCC__)
+#define NEED_MALLOC_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 if you have the <signal.h> header file. */
+#define HAVE_SIGNAL_H 1
+
+/* Define if you have the <ssl.h> header file. */
+/* #define HAVE_SSL_H 1 */
+
+/* Define to 1 if you have the <stdbool.h> header file. */
+#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || \
+ defined(__MINGW64_VERSION_MAJOR)
+#define HAVE_STDBOOL_H 1
+#endif
+
+/* Define if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define if you have the <sys/param.h> header file. */
+/* #define HAVE_SYS_PARAM_H 1 */
+
+/* Define if you have the <sys/select.h> header file. */
+/* #define HAVE_SYS_SELECT_H 1 */
+
+/* Define if you have the <sys/socket.h> header file. */
+/* #define HAVE_SYS_SOCKET_H 1 */
+
+/* Define if you have the <sys/sockio.h> header file. */
+/* #define HAVE_SYS_SOCKIO_H 1 */
+
+/* Define if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define if you have the <sys/time.h> header file. */
+/* #define HAVE_SYS_TIME_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. */
+#ifndef __BORLANDC__
+#define HAVE_SYS_UTIME_H 1
+#endif
+
+/* 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 <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define if you have the <unistd.h> header file. */
+#if defined(__MINGW32__) || defined(__LCC__) || defined(__POCC__)
+#define HAVE_UNISTD_H 1
+#endif
+
+/* Define if you have the <windows.h> header file. */
+#define HAVE_WINDOWS_H 1
+
+/* Define if you have the <winsock2.h> header file. */
+#ifndef __SALFORDC__
+#define HAVE_WINSOCK2_H 1
+#endif
+
+/* Define if you have the <ws2tcpip.h> header file. */
+#ifndef __SALFORDC__
+#define HAVE_WS2TCPIP_H 1
+#endif
+
+/* Define to 1 if you have the <setjmp.h> header file. */
+#define HAVE_SETJMP_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <libgen.h> header file. */
+#if defined(__MINGW64_VERSION_MAJOR)
+#define HAVE_LIBGEN_H 1
+#endif
+
+/* ---------------------------------------------------------------- */
+/* OTHER HEADER INFO */
+/* ---------------------------------------------------------------- */
+
+/* Define if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define if you can safely include both <sys/time.h> and <time.h>. */
+/* #define TIME_WITH_SYS_TIME 1 */
+
+/* Define to 1 if bool is an available type. */
+#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || \
+ defined(__MINGW64_VERSION_MAJOR)
+#define HAVE_BOOL_T 1
+#endif
+
+/* ---------------------------------------------------------------- */
+/* FUNCTIONS */
+/* ---------------------------------------------------------------- */
+
+/* Define if you have the closesocket function. */
+#define HAVE_CLOSESOCKET 1
+
+/* Define if you have the ftruncate function. */
+#if defined(__MINGW64_VERSION_MAJOR)
+#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. */
+/* #define HAVE_GETTIMEOFDAY 1 */
+
+/* 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
+
+/* 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 socket function. */
+#define HAVE_SOCKET 1
+
+/* Define if you have the strcasecmp function. */
+#ifdef __MINGW32__
+#define HAVE_STRCASECMP 1
+#endif
+
+/* Define if you have the strdup function. */
+#define HAVE_STRDUP 1
+
+/* Define if you have the stricmp function. */
+#define HAVE_STRICMP 1
+
+/* Define if you have the strtoll function. */
+#if defined(__MINGW32__) || defined(__POCC__) || \
+ (defined(_MSC_VER) && (_MSC_VER >= 1800))
+#define HAVE_STRTOLL 1
+#endif
+
+/* Define if you have the utime function. */
+#ifndef __BORLANDC__
+#define HAVE_UTIME 1
+#endif
+
+/* 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)
+#define HAVE_SNPRINTF 1
+#endif
+
+#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 /* Vista */
+/* Define to 1 if you have a IPv6 capable working inet_ntop function. */
+#define HAVE_INET_NTOP 1
+/* Define to 1 if you have a IPv6 capable working inet_pton function. */
+#define HAVE_INET_PTON 1
+#endif
+
+/* Define to 1 if you have the `basename' function. */
+#if defined(__MINGW64_VERSION_MAJOR)
+#define HAVE_BASENAME 1
+#endif
+
+/* Define to 1 if you have the strtok_r function. */
+#if defined(__MINGW64_VERSION_MAJOR)
+#define HAVE_STRTOK_R 1
+#endif
+
+/* Define to 1 if you have the signal function. */
+#define HAVE_SIGNAL 1
+
+/* ---------------------------------------------------------------- */
+/* TYPEDEF REPLACEMENTS */
+/* ---------------------------------------------------------------- */
+
+/* Define if in_addr_t is not an available 'typedefed' type. */
+#define in_addr_t unsigned long
+
+/* Define if ssize_t is not an available 'typedefed' type. */
+#ifndef _SSIZE_T_DEFINED
+# if defined(__POCC__) || defined(__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. */
+#if defined(_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
+
+/* ---------------------------------------------------------------- */
+/* BSD-style lwIP TCP/IP stack SPECIFIC */
+/* ---------------------------------------------------------------- */
+
+/* Define to use BSD-style lwIP TCP/IP stack. */
+/* #define USE_LWIPSOCK 1 */
+
+#ifdef USE_LWIPSOCK
+# undef USE_WINSOCK
+# undef HAVE_WINSOCK2_H
+# undef HAVE_WS2TCPIP_H
+# undef HAVE_GETHOSTNAME
+# undef LWIP_POSIX_SOCKETS_IO_NAMES
+# undef RECV_TYPE_ARG1
+# undef RECV_TYPE_ARG3
+# undef SEND_TYPE_ARG1
+# undef SEND_TYPE_ARG3
+# define HAVE_FREEADDRINFO
+# define HAVE_GETADDRINFO
+# define HAVE_GETHOSTBYNAME_R
+# define HAVE_GETHOSTBYNAME_R_6
+# define LWIP_POSIX_SOCKETS_IO_NAMES 0
+# define RECV_TYPE_ARG1 int
+# define RECV_TYPE_ARG3 size_t
+# define SEND_TYPE_ARG1 int
+# define SEND_TYPE_ARG3 size_t
+#endif
+
+/* ---------------------------------------------------------------- */
+/* Watt-32 tcp/ip SPECIFIC */
+/* ---------------------------------------------------------------- */
+
+#ifdef USE_WATT32
+ #include <tcp.h>
+ #undef byte
+ #undef word
+ #undef USE_WINSOCK
+ #undef HAVE_WINSOCK2_H
+ #undef HAVE_WS2TCPIP_H
+ #define HAVE_GETADDRINFO
+ #define HAVE_SYS_IOCTL_H
+ #define HAVE_SYS_SOCKET_H
+ #define HAVE_NETINET_IN_H
+ #define HAVE_NETDB_H
+ #define HAVE_ARPA_INET_H
+ #define HAVE_FREEADDRINFO
+ #define SOCKET int
+#endif
+
+
+/* ---------------------------------------------------------------- */
+/* 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 C99 variadic macro style. */
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define HAVE_VARIADIC_MACROS_C99 1
+#endif
+
+/* Define if the compiler supports the 'long long' data type. */
+#if defined(__MINGW32__) || \
+ (defined(_MSC_VER) && (_MSC_VER >= 1310)) || \
+ (defined(__BORLANDC__) && (__BORLANDC__ >= 0x561))
+#define HAVE_LONGLONG 1
+#endif
+
+/* Define to avoid VS2005 complaining about portable C functions. */
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define _CRT_SECURE_NO_DEPRECATE 1
+#define _CRT_NONSTDC_NO_DEPRECATE 1
+#endif
+
+/* mingw-w64, mingw using >= MSVCR80, and visual studio >= 2005 (MSVCR80)
+ all default to 64-bit time_t unless _USE_32BIT_TIME_T is defined */
+#if defined(__MINGW64_VERSION_MAJOR) || \
+ (defined(__MINGW32__) && (__MSVCRT_VERSION__ >= 0x0800)) || \
+ (defined(_MSC_VER) && (_MSC_VER >= 1400))
+# ifndef _USE_32BIT_TIME_T
+# define SIZEOF_TIME_T 8
+# else
+# define SIZEOF_TIME_T 4
+# endif
+#endif
+
+/* Define some minimum and default build targets for Visual Studio */
+#if defined(_MSC_VER)
+ /* Officially, Microsoft's Windows SDK versions 6.X does not support Windows
+ 2000 as a supported build target. VS2008 default installations provides
+ an embedded Windows SDK v6.0A along with the claim that Windows 2000 is a
+ valid build target for VS2008. Popular belief is that binaries built with
+ VS2008 using Windows SDK versions v6.X and Windows 2000 as a build target
+ are functional. */
+# define VS2008_MIN_TARGET 0x0500
+
+ /* The minimum build target for VS2012 is Vista unless Update 1 is installed
+ and the v110_xp toolset is chosen. */
+# if defined(_USING_V110_SDK71_)
+# define VS2012_MIN_TARGET 0x0501
+# else
+# define VS2012_MIN_TARGET 0x0600
+# endif
+
+ /* VS2008 default build target is Windows Vista. We override default target
+ to be Windows XP. */
+# define VS2008_DEF_TARGET 0x0501
+
+ /* VS2012 default build target is Windows Vista unless Update 1 is installed
+ and the v110_xp toolset is chosen. */
+# if defined(_USING_V110_SDK71_)
+# define VS2012_DEF_TARGET 0x0501
+# else
+# define VS2012_DEF_TARGET 0x0600
+# endif
+#endif
+
+/* VS2008 default target settings and minimum build target check. */
+#if defined(_MSC_VER) && (_MSC_VER >= 1500) && (_MSC_VER <= 1600)
+# 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 Windows build targets prior to Windows 2000
+# endif
+#endif
+
+/* VS2012 default target settings and minimum build target check. */
+#if defined(_MSC_VER) && (_MSC_VER >= 1700)
+# 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)
+# if defined(_USING_V110_SDK71_)
+# error VS2012 does not support Windows build targets prior to Windows XP
+# else
+# error VS2012 does not support Windows build targets prior to Windows \
+Vista
+# endif
+# endif
+#endif
+
+/* When no build target is specified Pelles C 5.00 and later default build
+ target is Windows Vista. We override default target to be Windows 2000. */
+#if defined(__POCC__) && (__POCC__ >= 500)
+# ifndef _WIN32_WINNT
+# define _WIN32_WINNT 0x0500
+# endif
+# ifndef WINVER
+# define WINVER 0x0500
+# endif
+#endif
+
+/* Availability of freeaddrinfo, getaddrinfo, and if_nametoindex
+ functions is quite convoluted, compiler dependent and even build target
+ dependent. */
+#if defined(HAVE_WS2TCPIP_H)
+# if defined(__POCC__)
+# define HAVE_FREEADDRINFO 1
+# define HAVE_GETADDRINFO 1
+# define HAVE_GETADDRINFO_THREADSAFE 1
+# elif defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501)
+# define HAVE_FREEADDRINFO 1
+# define HAVE_GETADDRINFO 1
+# define HAVE_GETADDRINFO_THREADSAFE 1
+# elif defined(_MSC_VER) && (_MSC_VER >= 1200)
+# define HAVE_FREEADDRINFO 1
+# define HAVE_GETADDRINFO 1
+# define HAVE_GETADDRINFO_THREADSAFE 1
+# endif
+#endif
+
+#if defined(__POCC__)
+# ifndef _MSC_VER
+# error Microsoft extensions /Ze compiler option is required
+# endif
+# ifndef __POCC__OLDNAMES
+# error Compatibility names /Go compiler option is required
+# endif
+#endif
+
+/* ---------------------------------------------------------------- */
+/* STRUCT RELATED */
+/* ---------------------------------------------------------------- */
+
+/* Define if you have struct sockaddr_storage. */
+#if !defined(__SALFORDC__) && !defined(__BORLANDC__)
+#define HAVE_STRUCT_SOCKADDR_STORAGE 1
+#endif
+
+/* 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 */
+/* ---------------------------------------------------------------- */
+
+#if defined(_MSC_VER) && !defined(_WIN32_WCE)
+# if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)
+# define USE_WIN32_LARGE_FILES
+# else
+# define USE_WIN32_SMALL_FILES
+# endif
+#endif
+
+#if defined(__MINGW32__) && !defined(USE_WIN32_LARGE_FILES)
+# define USE_WIN32_LARGE_FILES
+#endif
+
+#if defined(__POCC__)
+# undef USE_WIN32_LARGE_FILES
+#endif
+
+#if !defined(USE_WIN32_LARGE_FILES) && !defined(USE_WIN32_SMALL_FILES)
+# define USE_WIN32_SMALL_FILES
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#if defined(USE_WIN32_LARGE_FILES) && defined(__MINGW64_VERSION_MAJOR)
+# ifndef _FILE_OFFSET_BITS
+# define _FILE_OFFSET_BITS 64
+# endif
+#endif
+
+/* Define to the size of `off_t', as computed by sizeof. */
+#if defined(__MINGW64_VERSION_MAJOR) && \
+ defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
+# define SIZEOF_OFF_T 8
+#else
+# define SIZEOF_OFF_T 4
+#endif
+
+/* ---------------------------------------------------------------- */
+/* 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 */
+/* ---------------------------------------------------------------- */
+
+#if defined(CURL_HAS_NOVELL_LDAPSDK)
+#undef USE_WIN32_LDAP
+#define HAVE_LDAP_SSL_H 1
+#define HAVE_LDAP_URL_PARSE 1
+#elif defined(CURL_HAS_OPENLDAP_LDAPSDK)
+#undef USE_WIN32_LDAP
+#define HAVE_LDAP_URL_PARSE 1
+#else
+#undef HAVE_LDAP_URL_PARSE
+#define HAVE_LDAP_SSL 1
+#define USE_WIN32_LDAP 1
+#endif
+
+#if defined(__POCC__) && defined(USE_WIN32_LDAP)
+# define CURL_DISABLE_LDAP 1
+#endif
+
+/* Define to use the Windows crypto library. */
+#if !defined(CURL_WINDOWS_APP)
+#define USE_WIN32_CRYPTO
+#endif
+
+/* Define to use Unix sockets. */
+#define USE_UNIX_SOCKETS
+
+/* ---------------------------------------------------------------- */
+/* ADDITIONAL DEFINITIONS */
+/* ---------------------------------------------------------------- */
+
+/* Define cpu-machine-OS */
+#ifndef OS
+#if defined(_M_IX86) || defined(__i386__) /* x86 (MSVC or gcc) */
+#define OS "i386-pc-win32"
+#elif defined(_M_X64) || defined(__x86_64__) /* x86_64 (MSVC >=2005 or gcc) */
+#define OS "x86_64-pc-win32"
+#elif defined(_M_IA64) || defined(__ia64__) /* Itanium */
+#define OS "ia64-pc-win32"
+#elif defined(_M_ARM_NT) || defined(__arm__) /* ARMv7-Thumb2 (Windows RT) */
+#define OS "thumbv7a-pc-win32"
+#elif defined(_M_ARM64) || defined(__aarch64__) /* ARM64 (Windows 10) */
+#define OS "aarch64-pc-win32"
+#else
+#define OS "unknown-pc-win32"
+#endif
+#endif
+
+/* Name of package */
+#define PACKAGE "curl"
+
+/* If you want to build curl with the built-in manual */
+#define USE_MANUAL 1
+
+#if defined(__POCC__) || defined(USE_IPV6)
+# define ENABLE_IPV6 1
+#endif
+
+#endif /* HEADER_CURL_CONFIG_WIN32_H */
diff --git a/libs/libcurl/src/config-win32ce.h b/libs/libcurl/src/config-win32ce.h index 308bfe9e0a..c71dae4258 100644 --- a/libs/libcurl/src/config-win32ce.h +++ b/libs/libcurl/src/config-win32ce.h @@ -1,336 +1,328 @@ -#ifndef HEADER_CURL_CONFIG_WIN32CE_H -#define HEADER_CURL_CONFIG_WIN32CE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - -/* ================================================================ */ -/* lib/config-win32ce.h - Hand crafted config file for windows ce */ -/* ================================================================ */ - -/* ---------------------------------------------------------------- */ -/* HEADER FILES */ -/* ---------------------------------------------------------------- */ - -/* Define if you have the <arpa/inet.h> header file. */ -/* #define HAVE_ARPA_INET_H 1 */ - -/* Define if you have the <assert.h> header file. */ -/* #define HAVE_ASSERT_H 1 */ - -/* Define if you have the <errno.h> header file. */ -/* #define HAVE_ERRNO_H 1 */ - -/* Define if you have the <fcntl.h> header file. */ -#define HAVE_FCNTL_H 1 - -/* Define if you have the <io.h> header file. */ -#define HAVE_IO_H 1 - -/* Define if you need the malloc.h header file even with stdlib.h */ -#define NEED_MALLOC_H 1 - -/* 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 if you have the <signal.h> header file. */ -#define HAVE_SIGNAL_H 1 - -/* Define if you have the <ssl.h> header file. */ -/* #define HAVE_SSL_H 1 */ - -/* Define if you have the <stdlib.h> header file. */ -#define HAVE_STDLIB_H 1 - -/* Define if you have the <process.h> header file. */ -/* #define HAVE_PROCESS_H 1 */ - -/* Define if you have the <sys/param.h> header file. */ -/* #define HAVE_SYS_PARAM_H 1 */ - -/* Define if you have the <sys/select.h> header file. */ -/* #define HAVE_SYS_SELECT_H 1 */ - -/* Define if you have the <sys/socket.h> header file. */ -/* #define HAVE_SYS_SOCKET_H 1 */ - -/* Define if you have the <sys/sockio.h> header file. */ -/* #define HAVE_SYS_SOCKIO_H 1 */ - -/* Define if you have the <sys/stat.h> header file. */ -#define HAVE_SYS_STAT_H 1 - -/* Define if you have the <sys/time.h> header file */ -/* #define HAVE_SYS_TIME_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 <time.h> header file. */ -#define HAVE_TIME_H 1 - -/* Define if you have the <unistd.h> header file. */ -#if defined(__MINGW32__) || defined(__LCC__) -#define HAVE_UNISTD_H 1 -#endif - -/* Define if you have the <windows.h> header file. */ -#define HAVE_WINDOWS_H 1 - -/* Define if you have the <winsock2.h> header file. */ -#define HAVE_WINSOCK2_H 1 - -/* Define if you have the <ws2tcpip.h> header file. */ -#define HAVE_WS2TCPIP_H 1 - -/* ---------------------------------------------------------------- */ -/* OTHER HEADER INFO */ -/* ---------------------------------------------------------------- */ - -/* Define if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Define if you can safely include both <sys/time.h> and <time.h>. */ -/* #define TIME_WITH_SYS_TIME 1 */ - -/* ---------------------------------------------------------------- */ -/* FUNCTIONS */ -/* ---------------------------------------------------------------- */ - -/* Define if you have the closesocket function. */ -#define HAVE_CLOSESOCKET 1 - -/* Define if you have the gethostname function. */ -#define HAVE_GETHOSTNAME 1 - -/* Define if you have the gettimeofday function. */ -/* #define HAVE_GETTIMEOFDAY 1 */ - -/* 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 - -/* Define if you have the socket function. */ -#define HAVE_SOCKET 1 - -/* Define if you have the strcasecmp function. */ -/* #define HAVE_STRCASECMP 1 */ - -/* Define if you have the strdup function. */ -/* #define HAVE_STRDUP 1 */ - -/* Define if you have the stricmp function. */ -/* #define HAVE_STRICMP 1 */ - -/* Define if you have the strtoll function. */ -#if defined(__MINGW32__) -#define HAVE_STRTOLL 1 -#endif - -/* 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 - -/* ---------------------------------------------------------------- */ -/* TYPEDEF REPLACEMENTS */ -/* ---------------------------------------------------------------- */ - -/* Define this if in_addr_t is not an available 'typedefed' type */ -#define in_addr_t unsigned long - -/* Define ssize_t if it is not an available 'typedefed' type */ -#if defined(__POCC__) -#elif defined(_WIN64) -#define ssize_t __int64 -#else -#define ssize_t int -#endif - -/* ---------------------------------------------------------------- */ -/* TYPE SIZES */ -/* ---------------------------------------------------------------- */ - -/* The size of `int', as computed by sizeof. */ -#define SIZEOF_INT 4 - -/* 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 - -/* The size of `size_t', as computed by sizeof. */ -#if defined(_WIN64) -# define SIZEOF_SIZE_T 8 -#else -# define SIZEOF_SIZE_T 4 -#endif - -/* ---------------------------------------------------------------- */ -/* STRUCT RELATED */ -/* ---------------------------------------------------------------- */ - -/* Define this if you have struct sockaddr_storage */ -/* #define HAVE_STRUCT_SOCKADDR_STORAGE 1 */ - -/* Define this if you have struct timeval */ -#define HAVE_STRUCT_TIMEVAL 1 - -/* Define this if struct sockaddr_in6 has the sin6_scope_id member */ -#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 - -/* ---------------------------------------------------------------- */ -/* COMPILER SPECIFIC */ -/* ---------------------------------------------------------------- */ - -/* Undef keyword 'const' if it does not work. */ -/* #undef const */ - -/* Define to avoid VS2005 complaining about portable C functions */ -#if defined(_MSC_VER) && (_MSC_VER >= 1400) -#define _CRT_SECURE_NO_DEPRECATE 1 -#define _CRT_NONSTDC_NO_DEPRECATE 1 -#endif - -/* VS2005 and later default size for time_t is 64-bit, unless */ -/* _USE_32BIT_TIME_T has been defined to get a 32-bit time_t. */ -#if defined(_MSC_VER) && (_MSC_VER >= 1400) -# ifndef _USE_32BIT_TIME_T -# define SIZEOF_TIME_T 8 -# else -# define SIZEOF_TIME_T 4 -# endif -#endif - -/* ---------------------------------------------------------------- */ -/* LARGE FILE SUPPORT */ -/* ---------------------------------------------------------------- */ - -#if defined(_MSC_VER) && !defined(_WIN32_WCE) -# if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64) -# define USE_WIN32_LARGE_FILES -# else -# define USE_WIN32_SMALL_FILES -# endif -#endif - -#if !defined(USE_WIN32_LARGE_FILES) && !defined(USE_WIN32_SMALL_FILES) -# define USE_WIN32_SMALL_FILES -#endif - -/* ---------------------------------------------------------------- */ -/* LDAP SUPPORT */ -/* ---------------------------------------------------------------- */ - -#define USE_WIN32_LDAP 1 -#undef HAVE_LDAP_URL_PARSE - -/* ---------------------------------------------------------------- */ -/* ADDITIONAL DEFINITIONS */ -/* ---------------------------------------------------------------- */ - -/* Define cpu-machine-OS */ -#undef OS -#define OS "i386-pc-win32ce" - -/* Name of package */ -#define PACKAGE "curl" - -/* ---------------------------------------------------------------- */ -/* WinCE */ -/* ---------------------------------------------------------------- */ - -#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 - -#define ENOSPC 1 -#define ENOMEM 2 -#define EAGAIN 3 - -extern int stat(const char *path, struct stat *buffer); - -#endif /* HEADER_CURL_CONFIG_WIN32CE_H */ +#ifndef HEADER_CURL_CONFIG_WIN32CE_H
+#define HEADER_CURL_CONFIG_WIN32CE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+/* ================================================================ */
+/* lib/config-win32ce.h - Hand crafted config file for windows ce */
+/* ================================================================ */
+
+/* ---------------------------------------------------------------- */
+/* HEADER FILES */
+/* ---------------------------------------------------------------- */
+
+/* Define if you have the <arpa/inet.h> header file. */
+/* #define HAVE_ARPA_INET_H 1 */
+
+/* Define if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define if you have the <io.h> header file. */
+#define HAVE_IO_H 1
+
+/* Define if you need the malloc.h header file even with stdlib.h */
+#define NEED_MALLOC_H 1
+
+/* 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 if you have the <signal.h> header file. */
+#define HAVE_SIGNAL_H 1
+
+/* Define if you have the <ssl.h> header file. */
+/* #define HAVE_SSL_H 1 */
+
+/* Define if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define if you have the <sys/param.h> header file. */
+/* #define HAVE_SYS_PARAM_H 1 */
+
+/* Define if you have the <sys/select.h> header file. */
+/* #define HAVE_SYS_SELECT_H 1 */
+
+/* Define if you have the <sys/socket.h> header file. */
+/* #define HAVE_SYS_SOCKET_H 1 */
+
+/* Define if you have the <sys/sockio.h> header file. */
+/* #define HAVE_SYS_SOCKIO_H 1 */
+
+/* Define if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define if you have the <sys/time.h> header file */
+/* #define HAVE_SYS_TIME_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 <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define if you have the <unistd.h> header file. */
+#if defined(__MINGW32__) || defined(__LCC__)
+#define HAVE_UNISTD_H 1
+#endif
+
+/* Define if you have the <windows.h> header file. */
+#define HAVE_WINDOWS_H 1
+
+/* Define if you have the <winsock2.h> header file. */
+#define HAVE_WINSOCK2_H 1
+
+/* Define if you have the <ws2tcpip.h> header file. */
+#define HAVE_WS2TCPIP_H 1
+
+/* ---------------------------------------------------------------- */
+/* OTHER HEADER INFO */
+/* ---------------------------------------------------------------- */
+
+/* Define if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define if you can safely include both <sys/time.h> and <time.h>. */
+/* #define TIME_WITH_SYS_TIME 1 */
+
+/* ---------------------------------------------------------------- */
+/* FUNCTIONS */
+/* ---------------------------------------------------------------- */
+
+/* Define if you have the closesocket function. */
+#define HAVE_CLOSESOCKET 1
+
+/* Define if you have the gethostname function. */
+#define HAVE_GETHOSTNAME 1
+
+/* Define if you have the gettimeofday function. */
+/* #define HAVE_GETTIMEOFDAY 1 */
+
+/* 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
+
+/* Define if you have the socket function. */
+#define HAVE_SOCKET 1
+
+/* Define if you have the strcasecmp function. */
+/* #define HAVE_STRCASECMP 1 */
+
+/* Define if you have the strdup function. */
+/* #define HAVE_STRDUP 1 */
+
+/* Define if you have the stricmp function. */
+/* #define HAVE_STRICMP 1 */
+
+/* Define if you have the strtoll function. */
+#if defined(__MINGW32__)
+#define HAVE_STRTOLL 1
+#endif
+
+/* 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
+
+/* ---------------------------------------------------------------- */
+/* TYPEDEF REPLACEMENTS */
+/* ---------------------------------------------------------------- */
+
+/* Define this if in_addr_t is not an available 'typedefed' type */
+#define in_addr_t unsigned long
+
+/* Define ssize_t if it is not an available 'typedefed' type */
+#if defined(__POCC__)
+#elif defined(_WIN64)
+#define ssize_t __int64
+#else
+#define ssize_t int
+#endif
+
+/* ---------------------------------------------------------------- */
+/* TYPE SIZES */
+/* ---------------------------------------------------------------- */
+
+/* The size of `int', as computed by sizeof. */
+#define SIZEOF_INT 4
+
+/* 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
+
+/* The size of `size_t', as computed by sizeof. */
+#if defined(_WIN64)
+# define SIZEOF_SIZE_T 8
+#else
+# define SIZEOF_SIZE_T 4
+#endif
+
+/* ---------------------------------------------------------------- */
+/* STRUCT RELATED */
+/* ---------------------------------------------------------------- */
+
+/* Define this if you have struct sockaddr_storage */
+/* #define HAVE_STRUCT_SOCKADDR_STORAGE 1 */
+
+/* Define this if you have struct timeval */
+#define HAVE_STRUCT_TIMEVAL 1
+
+/* Define this if struct sockaddr_in6 has the sin6_scope_id member */
+#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1
+
+/* ---------------------------------------------------------------- */
+/* COMPILER SPECIFIC */
+/* ---------------------------------------------------------------- */
+
+/* Undef keyword 'const' if it does not work. */
+/* #undef const */
+
+/* Define to avoid VS2005 complaining about portable C functions */
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define _CRT_SECURE_NO_DEPRECATE 1
+#define _CRT_NONSTDC_NO_DEPRECATE 1
+#endif
+
+/* VS2005 and later default size for time_t is 64-bit, unless */
+/* _USE_32BIT_TIME_T has been defined to get a 32-bit time_t. */
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+# ifndef _USE_32BIT_TIME_T
+# define SIZEOF_TIME_T 8
+# else
+# define SIZEOF_TIME_T 4
+# endif
+#endif
+
+/* ---------------------------------------------------------------- */
+/* LARGE FILE SUPPORT */
+/* ---------------------------------------------------------------- */
+
+#if defined(_MSC_VER) && !defined(_WIN32_WCE)
+# if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)
+# define USE_WIN32_LARGE_FILES
+# else
+# define USE_WIN32_SMALL_FILES
+# endif
+#endif
+
+#if !defined(USE_WIN32_LARGE_FILES) && !defined(USE_WIN32_SMALL_FILES)
+# define USE_WIN32_SMALL_FILES
+#endif
+
+/* ---------------------------------------------------------------- */
+/* LDAP SUPPORT */
+/* ---------------------------------------------------------------- */
+
+#define USE_WIN32_LDAP 1
+#undef HAVE_LDAP_URL_PARSE
+
+/* ---------------------------------------------------------------- */
+/* ADDITIONAL DEFINITIONS */
+/* ---------------------------------------------------------------- */
+
+/* Define cpu-machine-OS */
+#ifndef OS
+#define OS "i386-pc-win32ce"
+#endif
+
+/* Name of package */
+#define PACKAGE "curl"
+
+/* ---------------------------------------------------------------- */
+/* WinCE */
+/* ---------------------------------------------------------------- */
+
+#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
+
+#define ENOSPC 1
+#define ENOMEM 2
+#define EAGAIN 3
+
+extern int stat(const char *path, struct stat *buffer);
+
+#endif /* HEADER_CURL_CONFIG_WIN32CE_H */
diff --git a/libs/libcurl/src/conncache.c b/libs/libcurl/src/conncache.c index a557ac6dc9..0177de4026 100644 --- a/libs/libcurl/src/conncache.c +++ b/libs/libcurl/src/conncache.c @@ -1,594 +1,594 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2012 - 2016, Linus Nielsen Feltzing, <linus@haxx.se> - * Copyright (C) 2012 - 2022, 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 "progress.h" -#include "multiif.h" -#include "sendf.h" -#include "conncache.h" -#include "share.h" -#include "sigpipe.h" -#include "connect.h" -#include "strcase.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#define HASHKEY_SIZE 128 - -static void conn_llist_dtor(void *user, void *element) -{ - struct connectdata *conn = element; - (void)user; - conn->bundle = NULL; -} - -static CURLcode bundle_create(struct connectbundle **bundlep) -{ - DEBUGASSERT(*bundlep == NULL); - *bundlep = malloc(sizeof(struct connectbundle)); - if(!*bundlep) - return CURLE_OUT_OF_MEMORY; - - (*bundlep)->num_connections = 0; - (*bundlep)->multiuse = BUNDLE_UNKNOWN; - - Curl_llist_init(&(*bundlep)->conn_list, (Curl_llist_dtor) conn_llist_dtor); - return CURLE_OK; -} - -static void bundle_destroy(struct connectbundle *bundle) -{ - if(!bundle) - return; - - Curl_llist_destroy(&bundle->conn_list, NULL); - - free(bundle); -} - -/* Add a connection to a bundle */ -static void bundle_add_conn(struct connectbundle *bundle, - struct connectdata *conn) -{ - Curl_llist_insert_next(&bundle->conn_list, bundle->conn_list.tail, conn, - &conn->bundle_node); - conn->bundle = bundle; - bundle->num_connections++; -} - -/* Remove a connection from a bundle */ -static int bundle_remove_conn(struct connectbundle *bundle, - struct connectdata *conn) -{ - struct Curl_llist_element *curr; - - curr = bundle->conn_list.head; - while(curr) { - if(curr->ptr == conn) { - Curl_llist_remove(&bundle->conn_list, curr, NULL); - bundle->num_connections--; - conn->bundle = NULL; - return 1; /* we removed a handle */ - } - curr = curr->next; - } - DEBUGASSERT(0); - return 0; -} - -static void free_bundle_hash_entry(void *freethis) -{ - struct connectbundle *b = (struct connectbundle *) freethis; - - bundle_destroy(b); -} - -int Curl_conncache_init(struct conncache *connc, int size) -{ - /* allocate a new easy handle to use when closing cached connections */ - connc->closure_handle = curl_easy_init(); - if(!connc->closure_handle) - return 1; /* bad */ - - Curl_hash_init(&connc->hash, size, Curl_hash_str, - Curl_str_key_compare, free_bundle_hash_entry); - connc->closure_handle->state.conn_cache = connc; - - return 0; /* good */ -} - -void Curl_conncache_destroy(struct conncache *connc) -{ - if(connc) - Curl_hash_destroy(&connc->hash); -} - -/* creates a key to find a bundle for this connection */ -static void hashkey(struct connectdata *conn, char *buf, size_t len) -{ - const char *hostname; - long port = conn->remote_port; - DEBUGASSERT(len >= HASHKEY_SIZE); -#ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { - hostname = conn->http_proxy.host.name; - port = conn->port; - } - else -#endif - if(conn->bits.conn_to_host) - hostname = conn->conn_to_host.name; - else - hostname = conn->host.name; - - /* put the numbers first so that the hostname gets cut off if too long */ -#ifdef ENABLE_IPV6 - msnprintf(buf, len, "%u/%ld/%s", conn->scope_id, port, hostname); -#else - msnprintf(buf, len, "%ld/%s", port, hostname); -#endif - Curl_strntolower(buf, buf, len); -} - -/* Returns number of connections currently held in the connection cache. - Locks/unlocks the cache itself! -*/ -size_t Curl_conncache_size(struct Curl_easy *data) -{ - size_t num; - CONNCACHE_LOCK(data); - num = data->state.conn_cache->num_conn; - CONNCACHE_UNLOCK(data); - return num; -} - -/* Look up the bundle with all the connections to the same host this - connectdata struct is setup to use. - - **NOTE**: When it returns, it holds the connection cache lock! */ -struct connectbundle * -Curl_conncache_find_bundle(struct Curl_easy *data, - struct connectdata *conn, - struct conncache *connc) -{ - struct connectbundle *bundle = NULL; - CONNCACHE_LOCK(data); - if(connc) { - char key[HASHKEY_SIZE]; - hashkey(conn, key, sizeof(key)); - bundle = Curl_hash_pick(&connc->hash, key, strlen(key)); - } - - return bundle; -} - -static void *conncache_add_bundle(struct conncache *connc, - char *key, - struct connectbundle *bundle) -{ - return Curl_hash_add(&connc->hash, key, strlen(key), bundle); -} - -static void conncache_remove_bundle(struct conncache *connc, - struct connectbundle *bundle) -{ - struct Curl_hash_iterator iter; - struct Curl_hash_element *he; - - if(!connc) - return; - - Curl_hash_start_iterate(&connc->hash, &iter); - - he = Curl_hash_next_element(&iter); - while(he) { - if(he->ptr == bundle) { - /* The bundle is destroyed by the hash destructor function, - free_bundle_hash_entry() */ - Curl_hash_delete(&connc->hash, he->key, he->key_len); - return; - } - - he = Curl_hash_next_element(&iter); - } -} - -CURLcode Curl_conncache_add_conn(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct connectbundle *bundle = NULL; - struct connectdata *conn = data->conn; - struct conncache *connc = data->state.conn_cache; - DEBUGASSERT(conn); - - /* *find_bundle() locks the connection cache */ - bundle = Curl_conncache_find_bundle(data, conn, data->state.conn_cache); - if(!bundle) { - char key[HASHKEY_SIZE]; - - result = bundle_create(&bundle); - if(result) { - goto unlock; - } - - hashkey(conn, key, sizeof(key)); - - if(!conncache_add_bundle(data->state.conn_cache, key, bundle)) { - bundle_destroy(bundle); - result = CURLE_OUT_OF_MEMORY; - goto unlock; - } - } - - bundle_add_conn(bundle, conn); - conn->connection_id = connc->next_connection_id++; - connc->num_conn++; - - DEBUGF(infof(data, "Added connection %ld. " - "The cache now contains %zu members", - conn->connection_id, connc->num_conn)); - - unlock: - CONNCACHE_UNLOCK(data); - - return result; -} - -/* - * Removes the connectdata object from the connection cache, but the transfer - * still owns this connection. - * - * Pass TRUE/FALSE in the 'lock' argument depending on if the parent function - * already holds the lock or not. - */ -void Curl_conncache_remove_conn(struct Curl_easy *data, - struct connectdata *conn, bool lock) -{ - struct connectbundle *bundle = conn->bundle; - struct conncache *connc = data->state.conn_cache; - - /* The bundle pointer can be NULL, since this function can be called - due to a failed connection attempt, before being added to a bundle */ - if(bundle) { - if(lock) { - CONNCACHE_LOCK(data); - } - bundle_remove_conn(bundle, conn); - if(bundle->num_connections == 0) - conncache_remove_bundle(connc, bundle); - conn->bundle = NULL; /* removed from it */ - if(connc) { - connc->num_conn--; - DEBUGF(infof(data, "The cache now contains %zu members", - connc->num_conn)); - } - if(lock) { - CONNCACHE_UNLOCK(data); - } - } -} - -/* This function iterates the entire connection cache and calls the function - func() with the connection pointer as the first argument and the supplied - 'param' argument as the other. - - The conncache 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 1 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. - */ -bool Curl_conncache_foreach(struct Curl_easy *data, - struct conncache *connc, - void *param, - int (*func)(struct Curl_easy *data, - struct connectdata *conn, void *param)) -{ - struct Curl_hash_iterator iter; - struct Curl_llist_element *curr; - struct Curl_hash_element *he; - - if(!connc) - return FALSE; - - CONNCACHE_LOCK(data); - Curl_hash_start_iterate(&connc->hash, &iter); - - he = Curl_hash_next_element(&iter); - while(he) { - struct connectbundle *bundle; - - bundle = he->ptr; - he = Curl_hash_next_element(&iter); - - curr = bundle->conn_list.head; - while(curr) { - /* Yes, we need to update curr before calling func(), because func() - might decide to remove the connection */ - struct connectdata *conn = curr->ptr; - curr = curr->next; - - if(1 == func(data, conn, param)) { - CONNCACHE_UNLOCK(data); - return TRUE; - } - } - } - CONNCACHE_UNLOCK(data); - return FALSE; -} - -/* Return the first connection found in the cache. Used when closing all - connections. - - NOTE: no locking is done here as this is presumably only done when cleaning - up a cache! -*/ -static struct connectdata * -conncache_find_first_connection(struct conncache *connc) -{ - struct Curl_hash_iterator iter; - struct Curl_hash_element *he; - struct connectbundle *bundle; - - Curl_hash_start_iterate(&connc->hash, &iter); - - he = Curl_hash_next_element(&iter); - while(he) { - struct Curl_llist_element *curr; - bundle = he->ptr; - - curr = bundle->conn_list.head; - if(curr) { - return curr->ptr; - } - - he = Curl_hash_next_element(&iter); - } - - return NULL; -} - -/* - * Give ownership of a connection back to the connection cache. Might - * disconnect the oldest existing in there to make space. - * - * Return TRUE if stored, FALSE if closed. - */ -bool Curl_conncache_return_conn(struct Curl_easy *data, - struct connectdata *conn) -{ - /* data->multi->maxconnects can be negative, deal with it. */ - size_t maxconnects = - (data->multi->maxconnects < 0) ? data->multi->num_easy * 4: - data->multi->maxconnects; - struct connectdata *conn_candidate = NULL; - - conn->lastused = Curl_now(); /* it was used up until now */ - if(maxconnects > 0 && - Curl_conncache_size(data) > maxconnects) { - infof(data, "Connection cache is full, closing the oldest one"); - - conn_candidate = Curl_conncache_extract_oldest(data); - if(conn_candidate) { - /* the winner gets the honour of being disconnected */ - Curl_disconnect(data, conn_candidate, /* dead_connection */ FALSE); - } - } - - return (conn_candidate == conn) ? FALSE : TRUE; - -} - -/* - * This function finds the connection in the connection bundle that has been - * unused for the longest time. - * - * Does not lock the connection cache! - * - * Returns the pointer to the oldest idle connection, or NULL if none was - * found. - */ -struct connectdata * -Curl_conncache_extract_bundle(struct Curl_easy *data, - struct connectbundle *bundle) -{ - struct Curl_llist_element *curr; - timediff_t highscore = -1; - timediff_t score; - struct curltime now; - struct connectdata *conn_candidate = NULL; - struct connectdata *conn; - - (void)data; - - now = Curl_now(); - - curr = bundle->conn_list.head; - while(curr) { - conn = curr->ptr; - - if(!CONN_INUSE(conn)) { - /* Set higher score for the age passed since the connection was used */ - score = Curl_timediff(now, conn->lastused); - - if(score > highscore) { - highscore = score; - conn_candidate = conn; - } - } - curr = curr->next; - } - if(conn_candidate) { - /* remove it to prevent another thread from nicking it */ - bundle_remove_conn(bundle, conn_candidate); - data->state.conn_cache->num_conn--; - DEBUGF(infof(data, "The cache now contains %zu members", - data->state.conn_cache->num_conn)); - } - - return conn_candidate; -} - -/* - * This function finds the connection in the connection cache that has been - * unused for the longest time and extracts that from the bundle. - * - * Returns the pointer to the connection, or NULL if none was found. - */ -struct connectdata * -Curl_conncache_extract_oldest(struct Curl_easy *data) -{ - struct conncache *connc = data->state.conn_cache; - struct Curl_hash_iterator iter; - struct Curl_llist_element *curr; - struct Curl_hash_element *he; - timediff_t highscore =- 1; - timediff_t score; - struct curltime now; - struct connectdata *conn_candidate = NULL; - struct connectbundle *bundle; - struct connectbundle *bundle_candidate = NULL; - - now = Curl_now(); - - CONNCACHE_LOCK(data); - Curl_hash_start_iterate(&connc->hash, &iter); - - he = Curl_hash_next_element(&iter); - while(he) { - struct connectdata *conn; - - bundle = he->ptr; - - curr = bundle->conn_list.head; - while(curr) { - conn = curr->ptr; - - if(!CONN_INUSE(conn) && !conn->bits.close && - !conn->connect_only) { - /* Set higher score for the age passed since the connection was used */ - score = Curl_timediff(now, conn->lastused); - - if(score > highscore) { - highscore = score; - conn_candidate = conn; - bundle_candidate = bundle; - } - } - curr = curr->next; - } - - he = Curl_hash_next_element(&iter); - } - if(conn_candidate) { - /* remove it to prevent another thread from nicking it */ - bundle_remove_conn(bundle_candidate, conn_candidate); - connc->num_conn--; - DEBUGF(infof(data, "The cache now contains %zu members", - connc->num_conn)); - } - CONNCACHE_UNLOCK(data); - - return conn_candidate; -} - -void Curl_conncache_close_all_connections(struct conncache *connc) -{ - struct connectdata *conn; - char buffer[READBUFFER_MIN + 1]; - SIGPIPE_VARIABLE(pipe_st); - if(!connc->closure_handle) - return; - connc->closure_handle->state.buffer = buffer; - connc->closure_handle->set.buffer_size = READBUFFER_MIN; - - conn = conncache_find_first_connection(connc); - while(conn) { - sigpipe_ignore(connc->closure_handle, &pipe_st); - /* This will remove the connection from the cache */ - connclose(conn, "kill all"); - Curl_conncache_remove_conn(connc->closure_handle, conn, TRUE); - Curl_disconnect(connc->closure_handle, conn, FALSE); - sigpipe_restore(&pipe_st); - - conn = conncache_find_first_connection(connc); - } - - connc->closure_handle->state.buffer = NULL; - sigpipe_ignore(connc->closure_handle, &pipe_st); - - Curl_hostcache_clean(connc->closure_handle, - connc->closure_handle->dns.hostcache); - Curl_close(&connc->closure_handle); - sigpipe_restore(&pipe_st); -} - -#if 0 -/* Useful for debugging the connection cache */ -void Curl_conncache_print(struct conncache *connc) -{ - struct Curl_hash_iterator iter; - struct Curl_llist_element *curr; - struct Curl_hash_element *he; - - if(!connc) - return; - - fprintf(stderr, "=Bundle cache=\n"); - - Curl_hash_start_iterate(connc->hash, &iter); - - he = Curl_hash_next_element(&iter); - while(he) { - struct connectbundle *bundle; - struct connectdata *conn; - - bundle = he->ptr; - - fprintf(stderr, "%s -", he->key); - curr = bundle->conn_list->head; - while(curr) { - conn = curr->ptr; - - fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse); - curr = curr->next; - } - fprintf(stderr, "\n"); - - he = Curl_hash_next_element(&iter); - } -} -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012 - 2016, Linus Nielsen Feltzing, <linus@haxx.se>
+ * Copyright (C) 2012 - 2022, 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 "progress.h"
+#include "multiif.h"
+#include "sendf.h"
+#include "conncache.h"
+#include "share.h"
+#include "sigpipe.h"
+#include "connect.h"
+#include "strcase.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define HASHKEY_SIZE 128
+
+static void conn_llist_dtor(void *user, void *element)
+{
+ struct connectdata *conn = element;
+ (void)user;
+ conn->bundle = NULL;
+}
+
+static CURLcode bundle_create(struct connectbundle **bundlep)
+{
+ DEBUGASSERT(*bundlep == NULL);
+ *bundlep = malloc(sizeof(struct connectbundle));
+ if(!*bundlep)
+ return CURLE_OUT_OF_MEMORY;
+
+ (*bundlep)->num_connections = 0;
+ (*bundlep)->multiuse = BUNDLE_UNKNOWN;
+
+ Curl_llist_init(&(*bundlep)->conn_list, (Curl_llist_dtor) conn_llist_dtor);
+ return CURLE_OK;
+}
+
+static void bundle_destroy(struct connectbundle *bundle)
+{
+ if(!bundle)
+ return;
+
+ Curl_llist_destroy(&bundle->conn_list, NULL);
+
+ free(bundle);
+}
+
+/* Add a connection to a bundle */
+static void bundle_add_conn(struct connectbundle *bundle,
+ struct connectdata *conn)
+{
+ Curl_llist_insert_next(&bundle->conn_list, bundle->conn_list.tail, conn,
+ &conn->bundle_node);
+ conn->bundle = bundle;
+ bundle->num_connections++;
+}
+
+/* Remove a connection from a bundle */
+static int bundle_remove_conn(struct connectbundle *bundle,
+ struct connectdata *conn)
+{
+ struct Curl_llist_element *curr;
+
+ curr = bundle->conn_list.head;
+ while(curr) {
+ if(curr->ptr == conn) {
+ Curl_llist_remove(&bundle->conn_list, curr, NULL);
+ bundle->num_connections--;
+ conn->bundle = NULL;
+ return 1; /* we removed a handle */
+ }
+ curr = curr->next;
+ }
+ DEBUGASSERT(0);
+ return 0;
+}
+
+static void free_bundle_hash_entry(void *freethis)
+{
+ struct connectbundle *b = (struct connectbundle *) freethis;
+
+ bundle_destroy(b);
+}
+
+int Curl_conncache_init(struct conncache *connc, int size)
+{
+ /* allocate a new easy handle to use when closing cached connections */
+ connc->closure_handle = curl_easy_init();
+ if(!connc->closure_handle)
+ return 1; /* bad */
+
+ Curl_hash_init(&connc->hash, size, Curl_hash_str,
+ Curl_str_key_compare, free_bundle_hash_entry);
+ connc->closure_handle->state.conn_cache = connc;
+
+ return 0; /* good */
+}
+
+void Curl_conncache_destroy(struct conncache *connc)
+{
+ if(connc)
+ Curl_hash_destroy(&connc->hash);
+}
+
+/* creates a key to find a bundle for this connection */
+static void hashkey(struct connectdata *conn, char *buf, size_t len)
+{
+ const char *hostname;
+ long port = conn->remote_port;
+ DEBUGASSERT(len >= HASHKEY_SIZE);
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
+ hostname = conn->http_proxy.host.name;
+ port = conn->port;
+ }
+ else
+#endif
+ if(conn->bits.conn_to_host)
+ hostname = conn->conn_to_host.name;
+ else
+ hostname = conn->host.name;
+
+ /* put the numbers first so that the hostname gets cut off if too long */
+#ifdef ENABLE_IPV6
+ msnprintf(buf, len, "%u/%ld/%s", conn->scope_id, port, hostname);
+#else
+ msnprintf(buf, len, "%ld/%s", port, hostname);
+#endif
+ Curl_strntolower(buf, buf, len);
+}
+
+/* Returns number of connections currently held in the connection cache.
+ Locks/unlocks the cache itself!
+*/
+size_t Curl_conncache_size(struct Curl_easy *data)
+{
+ size_t num;
+ CONNCACHE_LOCK(data);
+ num = data->state.conn_cache->num_conn;
+ CONNCACHE_UNLOCK(data);
+ return num;
+}
+
+/* Look up the bundle with all the connections to the same host this
+ connectdata struct is setup to use.
+
+ **NOTE**: When it returns, it holds the connection cache lock! */
+struct connectbundle *
+Curl_conncache_find_bundle(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct conncache *connc)
+{
+ struct connectbundle *bundle = NULL;
+ CONNCACHE_LOCK(data);
+ if(connc) {
+ char key[HASHKEY_SIZE];
+ hashkey(conn, key, sizeof(key));
+ bundle = Curl_hash_pick(&connc->hash, key, strlen(key));
+ }
+
+ return bundle;
+}
+
+static void *conncache_add_bundle(struct conncache *connc,
+ char *key,
+ struct connectbundle *bundle)
+{
+ return Curl_hash_add(&connc->hash, key, strlen(key), bundle);
+}
+
+static void conncache_remove_bundle(struct conncache *connc,
+ struct connectbundle *bundle)
+{
+ struct Curl_hash_iterator iter;
+ struct Curl_hash_element *he;
+
+ if(!connc)
+ return;
+
+ Curl_hash_start_iterate(&connc->hash, &iter);
+
+ he = Curl_hash_next_element(&iter);
+ while(he) {
+ if(he->ptr == bundle) {
+ /* The bundle is destroyed by the hash destructor function,
+ free_bundle_hash_entry() */
+ Curl_hash_delete(&connc->hash, he->key, he->key_len);
+ return;
+ }
+
+ he = Curl_hash_next_element(&iter);
+ }
+}
+
+CURLcode Curl_conncache_add_conn(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct connectbundle *bundle = NULL;
+ struct connectdata *conn = data->conn;
+ struct conncache *connc = data->state.conn_cache;
+ DEBUGASSERT(conn);
+
+ /* *find_bundle() locks the connection cache */
+ bundle = Curl_conncache_find_bundle(data, conn, data->state.conn_cache);
+ if(!bundle) {
+ char key[HASHKEY_SIZE];
+
+ result = bundle_create(&bundle);
+ if(result) {
+ goto unlock;
+ }
+
+ hashkey(conn, key, sizeof(key));
+
+ if(!conncache_add_bundle(data->state.conn_cache, key, bundle)) {
+ bundle_destroy(bundle);
+ result = CURLE_OUT_OF_MEMORY;
+ goto unlock;
+ }
+ }
+
+ bundle_add_conn(bundle, conn);
+ conn->connection_id = connc->next_connection_id++;
+ connc->num_conn++;
+
+ DEBUGF(infof(data, "Added connection %ld. "
+ "The cache now contains %zu members",
+ conn->connection_id, connc->num_conn));
+
+ unlock:
+ CONNCACHE_UNLOCK(data);
+
+ return result;
+}
+
+/*
+ * Removes the connectdata object from the connection cache, but the transfer
+ * still owns this connection.
+ *
+ * Pass TRUE/FALSE in the 'lock' argument depending on if the parent function
+ * already holds the lock or not.
+ */
+void Curl_conncache_remove_conn(struct Curl_easy *data,
+ struct connectdata *conn, bool lock)
+{
+ struct connectbundle *bundle = conn->bundle;
+ struct conncache *connc = data->state.conn_cache;
+
+ /* The bundle pointer can be NULL, since this function can be called
+ due to a failed connection attempt, before being added to a bundle */
+ if(bundle) {
+ if(lock) {
+ CONNCACHE_LOCK(data);
+ }
+ bundle_remove_conn(bundle, conn);
+ if(bundle->num_connections == 0)
+ conncache_remove_bundle(connc, bundle);
+ conn->bundle = NULL; /* removed from it */
+ if(connc) {
+ connc->num_conn--;
+ DEBUGF(infof(data, "The cache now contains %zu members",
+ connc->num_conn));
+ }
+ if(lock) {
+ CONNCACHE_UNLOCK(data);
+ }
+ }
+}
+
+/* This function iterates the entire connection cache and calls the function
+ func() with the connection pointer as the first argument and the supplied
+ 'param' argument as the other.
+
+ The conncache 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 1 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.
+ */
+bool Curl_conncache_foreach(struct Curl_easy *data,
+ struct conncache *connc,
+ void *param,
+ int (*func)(struct Curl_easy *data,
+ struct connectdata *conn, void *param))
+{
+ struct Curl_hash_iterator iter;
+ struct Curl_llist_element *curr;
+ struct Curl_hash_element *he;
+
+ if(!connc)
+ return FALSE;
+
+ CONNCACHE_LOCK(data);
+ Curl_hash_start_iterate(&connc->hash, &iter);
+
+ he = Curl_hash_next_element(&iter);
+ while(he) {
+ struct connectbundle *bundle;
+
+ bundle = he->ptr;
+ he = Curl_hash_next_element(&iter);
+
+ curr = bundle->conn_list.head;
+ while(curr) {
+ /* Yes, we need to update curr before calling func(), because func()
+ might decide to remove the connection */
+ struct connectdata *conn = curr->ptr;
+ curr = curr->next;
+
+ if(1 == func(data, conn, param)) {
+ CONNCACHE_UNLOCK(data);
+ return TRUE;
+ }
+ }
+ }
+ CONNCACHE_UNLOCK(data);
+ return FALSE;
+}
+
+/* Return the first connection found in the cache. Used when closing all
+ connections.
+
+ NOTE: no locking is done here as this is presumably only done when cleaning
+ up a cache!
+*/
+static struct connectdata *
+conncache_find_first_connection(struct conncache *connc)
+{
+ struct Curl_hash_iterator iter;
+ struct Curl_hash_element *he;
+ struct connectbundle *bundle;
+
+ Curl_hash_start_iterate(&connc->hash, &iter);
+
+ he = Curl_hash_next_element(&iter);
+ while(he) {
+ struct Curl_llist_element *curr;
+ bundle = he->ptr;
+
+ curr = bundle->conn_list.head;
+ if(curr) {
+ return curr->ptr;
+ }
+
+ he = Curl_hash_next_element(&iter);
+ }
+
+ return NULL;
+}
+
+/*
+ * Give ownership of a connection back to the connection cache. Might
+ * disconnect the oldest existing in there to make space.
+ *
+ * Return TRUE if stored, FALSE if closed.
+ */
+bool Curl_conncache_return_conn(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ /* data->multi->maxconnects can be negative, deal with it. */
+ size_t maxconnects =
+ (data->multi->maxconnects < 0) ? data->multi->num_easy * 4:
+ data->multi->maxconnects;
+ struct connectdata *conn_candidate = NULL;
+
+ conn->lastused = Curl_now(); /* it was used up until now */
+ if(maxconnects > 0 &&
+ Curl_conncache_size(data) > maxconnects) {
+ infof(data, "Connection cache is full, closing the oldest one");
+
+ conn_candidate = Curl_conncache_extract_oldest(data);
+ if(conn_candidate) {
+ /* the winner gets the honour of being disconnected */
+ Curl_disconnect(data, conn_candidate, /* dead_connection */ FALSE);
+ }
+ }
+
+ return (conn_candidate == conn) ? FALSE : TRUE;
+
+}
+
+/*
+ * This function finds the connection in the connection bundle that has been
+ * unused for the longest time.
+ *
+ * Does not lock the connection cache!
+ *
+ * Returns the pointer to the oldest idle connection, or NULL if none was
+ * found.
+ */
+struct connectdata *
+Curl_conncache_extract_bundle(struct Curl_easy *data,
+ struct connectbundle *bundle)
+{
+ struct Curl_llist_element *curr;
+ timediff_t highscore = -1;
+ timediff_t score;
+ struct curltime now;
+ struct connectdata *conn_candidate = NULL;
+ struct connectdata *conn;
+
+ (void)data;
+
+ now = Curl_now();
+
+ curr = bundle->conn_list.head;
+ while(curr) {
+ conn = curr->ptr;
+
+ if(!CONN_INUSE(conn)) {
+ /* Set higher score for the age passed since the connection was used */
+ score = Curl_timediff(now, conn->lastused);
+
+ if(score > highscore) {
+ highscore = score;
+ conn_candidate = conn;
+ }
+ }
+ curr = curr->next;
+ }
+ if(conn_candidate) {
+ /* remove it to prevent another thread from nicking it */
+ bundle_remove_conn(bundle, conn_candidate);
+ data->state.conn_cache->num_conn--;
+ DEBUGF(infof(data, "The cache now contains %zu members",
+ data->state.conn_cache->num_conn));
+ }
+
+ return conn_candidate;
+}
+
+/*
+ * This function finds the connection in the connection cache that has been
+ * unused for the longest time and extracts that from the bundle.
+ *
+ * Returns the pointer to the connection, or NULL if none was found.
+ */
+struct connectdata *
+Curl_conncache_extract_oldest(struct Curl_easy *data)
+{
+ struct conncache *connc = data->state.conn_cache;
+ struct Curl_hash_iterator iter;
+ struct Curl_llist_element *curr;
+ struct Curl_hash_element *he;
+ timediff_t highscore =- 1;
+ timediff_t score;
+ struct curltime now;
+ struct connectdata *conn_candidate = NULL;
+ struct connectbundle *bundle;
+ struct connectbundle *bundle_candidate = NULL;
+
+ now = Curl_now();
+
+ CONNCACHE_LOCK(data);
+ Curl_hash_start_iterate(&connc->hash, &iter);
+
+ he = Curl_hash_next_element(&iter);
+ while(he) {
+ struct connectdata *conn;
+
+ bundle = he->ptr;
+
+ curr = bundle->conn_list.head;
+ while(curr) {
+ conn = curr->ptr;
+
+ if(!CONN_INUSE(conn) && !conn->bits.close &&
+ !conn->connect_only) {
+ /* Set higher score for the age passed since the connection was used */
+ score = Curl_timediff(now, conn->lastused);
+
+ if(score > highscore) {
+ highscore = score;
+ conn_candidate = conn;
+ bundle_candidate = bundle;
+ }
+ }
+ curr = curr->next;
+ }
+
+ he = Curl_hash_next_element(&iter);
+ }
+ if(conn_candidate) {
+ /* remove it to prevent another thread from nicking it */
+ bundle_remove_conn(bundle_candidate, conn_candidate);
+ connc->num_conn--;
+ DEBUGF(infof(data, "The cache now contains %zu members",
+ connc->num_conn));
+ }
+ CONNCACHE_UNLOCK(data);
+
+ return conn_candidate;
+}
+
+void Curl_conncache_close_all_connections(struct conncache *connc)
+{
+ struct connectdata *conn;
+ char buffer[READBUFFER_MIN + 1];
+ SIGPIPE_VARIABLE(pipe_st);
+ if(!connc->closure_handle)
+ return;
+ connc->closure_handle->state.buffer = buffer;
+ connc->closure_handle->set.buffer_size = READBUFFER_MIN;
+
+ conn = conncache_find_first_connection(connc);
+ while(conn) {
+ sigpipe_ignore(connc->closure_handle, &pipe_st);
+ /* This will remove the connection from the cache */
+ connclose(conn, "kill all");
+ Curl_conncache_remove_conn(connc->closure_handle, conn, TRUE);
+ Curl_disconnect(connc->closure_handle, conn, FALSE);
+ sigpipe_restore(&pipe_st);
+
+ conn = conncache_find_first_connection(connc);
+ }
+
+ connc->closure_handle->state.buffer = NULL;
+ sigpipe_ignore(connc->closure_handle, &pipe_st);
+
+ Curl_hostcache_clean(connc->closure_handle,
+ connc->closure_handle->dns.hostcache);
+ Curl_close(&connc->closure_handle);
+ sigpipe_restore(&pipe_st);
+}
+
+#if 0
+/* Useful for debugging the connection cache */
+void Curl_conncache_print(struct conncache *connc)
+{
+ struct Curl_hash_iterator iter;
+ struct Curl_llist_element *curr;
+ struct Curl_hash_element *he;
+
+ if(!connc)
+ return;
+
+ fprintf(stderr, "=Bundle cache=\n");
+
+ Curl_hash_start_iterate(connc->hash, &iter);
+
+ he = Curl_hash_next_element(&iter);
+ while(he) {
+ struct connectbundle *bundle;
+ struct connectdata *conn;
+
+ bundle = he->ptr;
+
+ fprintf(stderr, "%s -", he->key);
+ curr = bundle->conn_list->head;
+ while(curr) {
+ conn = curr->ptr;
+
+ fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
+ curr = curr->next;
+ }
+ fprintf(stderr, "\n");
+
+ he = Curl_hash_next_element(&iter);
+ }
+}
+#endif
diff --git a/libs/libcurl/src/conncache.h b/libs/libcurl/src/conncache.h index 94664bc357..683699a809 100644 --- a/libs/libcurl/src/conncache.h +++ b/libs/libcurl/src/conncache.h @@ -1,121 +1,121 @@ -#ifndef HEADER_CURL_CONNCACHE_H -#define HEADER_CURL_CONNCACHE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2015 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2012 - 2014, 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 - * - ***************************************************************************/ - -/* - * All accesses to struct fields and changing of data in the connection cache - * and connectbundles must be done with the conncache LOCKED. The cache might - * be shared. - */ - -#include <curl/curl.h> -#include "timeval.h" - -struct connectdata; - -struct conncache { - struct Curl_hash hash; - size_t num_conn; - long next_connection_id; - struct curltime last_cleanup; - /* handle used for closing cached connections */ - struct Curl_easy *closure_handle; -}; - -#define BUNDLE_NO_MULTIUSE -1 -#define BUNDLE_UNKNOWN 0 /* initial value */ -#define BUNDLE_MULTIPLEX 2 - -#ifdef CURLDEBUG -/* the debug versions of these macros make extra certain that the lock is - never doubly locked or unlocked */ -#define CONNCACHE_LOCK(x) \ - do { \ - if((x)->share) { \ - Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, \ - CURL_LOCK_ACCESS_SINGLE); \ - DEBUGASSERT(!(x)->state.conncache_lock); \ - (x)->state.conncache_lock = TRUE; \ - } \ - } while(0) - -#define CONNCACHE_UNLOCK(x) \ - do { \ - if((x)->share) { \ - DEBUGASSERT((x)->state.conncache_lock); \ - (x)->state.conncache_lock = FALSE; \ - Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT); \ - } \ - } while(0) -#else -#define CONNCACHE_LOCK(x) if((x)->share) \ - Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE) -#define CONNCACHE_UNLOCK(x) if((x)->share) \ - Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT) -#endif - -struct connectbundle { - int multiuse; /* supports multi-use */ - size_t num_connections; /* Number of connections in the bundle */ - struct Curl_llist conn_list; /* The connectdata members of the bundle */ -}; - -/* returns 1 on error, 0 is fine */ -int Curl_conncache_init(struct conncache *, int size); -void Curl_conncache_destroy(struct conncache *connc); - -/* return the correct bundle, to a host or a proxy */ -struct connectbundle *Curl_conncache_find_bundle(struct Curl_easy *data, - struct connectdata *conn, - struct conncache *connc); -/* returns number of connections currently held in the connection cache */ -size_t Curl_conncache_size(struct Curl_easy *data); - -bool Curl_conncache_return_conn(struct Curl_easy *data, - struct connectdata *conn); -CURLcode Curl_conncache_add_conn(struct Curl_easy *data) WARN_UNUSED_RESULT; -void Curl_conncache_remove_conn(struct Curl_easy *data, - struct connectdata *conn, - bool lock); -bool Curl_conncache_foreach(struct Curl_easy *data, - struct conncache *connc, - void *param, - int (*func)(struct Curl_easy *data, - struct connectdata *conn, - void *param)); - -struct connectdata * -Curl_conncache_find_first_connection(struct conncache *connc); - -struct connectdata * -Curl_conncache_extract_bundle(struct Curl_easy *data, - struct connectbundle *bundle); -struct connectdata * -Curl_conncache_extract_oldest(struct Curl_easy *data); -void Curl_conncache_close_all_connections(struct conncache *connc); -void Curl_conncache_print(struct conncache *connc); - -#endif /* HEADER_CURL_CONNCACHE_H */ +#ifndef HEADER_CURL_CONNCACHE_H
+#define HEADER_CURL_CONNCACHE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2015 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2014, 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
+ *
+ ***************************************************************************/
+
+/*
+ * All accesses to struct fields and changing of data in the connection cache
+ * and connectbundles must be done with the conncache LOCKED. The cache might
+ * be shared.
+ */
+
+#include <curl/curl.h>
+#include "timeval.h"
+
+struct connectdata;
+
+struct conncache {
+ struct Curl_hash hash;
+ size_t num_conn;
+ long next_connection_id;
+ struct curltime last_cleanup;
+ /* handle used for closing cached connections */
+ struct Curl_easy *closure_handle;
+};
+
+#define BUNDLE_NO_MULTIUSE -1
+#define BUNDLE_UNKNOWN 0 /* initial value */
+#define BUNDLE_MULTIPLEX 2
+
+#ifdef CURLDEBUG
+/* the debug versions of these macros make extra certain that the lock is
+ never doubly locked or unlocked */
+#define CONNCACHE_LOCK(x) \
+ do { \
+ if((x)->share) { \
+ Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, \
+ CURL_LOCK_ACCESS_SINGLE); \
+ DEBUGASSERT(!(x)->state.conncache_lock); \
+ (x)->state.conncache_lock = TRUE; \
+ } \
+ } while(0)
+
+#define CONNCACHE_UNLOCK(x) \
+ do { \
+ if((x)->share) { \
+ DEBUGASSERT((x)->state.conncache_lock); \
+ (x)->state.conncache_lock = FALSE; \
+ Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT); \
+ } \
+ } while(0)
+#else
+#define CONNCACHE_LOCK(x) if((x)->share) \
+ Curl_share_lock((x), CURL_LOCK_DATA_CONNECT, CURL_LOCK_ACCESS_SINGLE)
+#define CONNCACHE_UNLOCK(x) if((x)->share) \
+ Curl_share_unlock((x), CURL_LOCK_DATA_CONNECT)
+#endif
+
+struct connectbundle {
+ int multiuse; /* supports multi-use */
+ size_t num_connections; /* Number of connections in the bundle */
+ struct Curl_llist conn_list; /* The connectdata members of the bundle */
+};
+
+/* returns 1 on error, 0 is fine */
+int Curl_conncache_init(struct conncache *, int size);
+void Curl_conncache_destroy(struct conncache *connc);
+
+/* return the correct bundle, to a host or a proxy */
+struct connectbundle *Curl_conncache_find_bundle(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct conncache *connc);
+/* returns number of connections currently held in the connection cache */
+size_t Curl_conncache_size(struct Curl_easy *data);
+
+bool Curl_conncache_return_conn(struct Curl_easy *data,
+ struct connectdata *conn);
+CURLcode Curl_conncache_add_conn(struct Curl_easy *data) WARN_UNUSED_RESULT;
+void Curl_conncache_remove_conn(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool lock);
+bool Curl_conncache_foreach(struct Curl_easy *data,
+ struct conncache *connc,
+ void *param,
+ int (*func)(struct Curl_easy *data,
+ struct connectdata *conn,
+ void *param));
+
+struct connectdata *
+Curl_conncache_find_first_connection(struct conncache *connc);
+
+struct connectdata *
+Curl_conncache_extract_bundle(struct Curl_easy *data,
+ struct connectbundle *bundle);
+struct connectdata *
+Curl_conncache_extract_oldest(struct Curl_easy *data);
+void Curl_conncache_close_all_connections(struct conncache *connc);
+void Curl_conncache_print(struct conncache *connc);
+
+#endif /* HEADER_CURL_CONNCACHE_H */
diff --git a/libs/libcurl/src/connect.c b/libs/libcurl/src/connect.c index ac007c61b0..fa67cef0dc 100644 --- a/libs/libcurl/src/connect.c +++ b/libs/libcurl/src/connect.c @@ -1,1729 +1,1932 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_FCNTL_H -#include <fcntl.h> -#endif -#ifdef HAVE_ARPA_INET_H -#include <arpa/inet.h> -#endif - -#if (defined(HAVE_IOCTL_FIONBIO) && defined(NETWARE)) -#include <sys/filio.h> -#endif -#ifdef NETWARE -#undef in_addr_t -#define in_addr_t unsigned long -#endif -#ifdef __VMS -#include <in.h> -#include <inet.h> -#endif - -#include "urldata.h" -#include "sendf.h" -#include "if2ip.h" -#include "strerror.h" -#include "connect.h" -#include "select.h" -#include "url.h" /* for Curl_safefree() */ -#include "multiif.h" -#include "sockaddr.h" /* required for Curl_sockaddr_storage */ -#include "inet_ntop.h" -#include "inet_pton.h" -#include "vtls/vtls.h" /* for Curl_ssl_check_cxn() */ -#include "progress.h" -#include "warnless.h" -#include "conncache.h" -#include "multihandle.h" -#include "share.h" -#include "version_win32.h" -#include "quic.h" -#include "socks.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -static bool verifyconnect(curl_socket_t sockfd, int *error); - -#if defined(__DragonFly__) || defined(HAVE_WINSOCK2_H) -/* DragonFlyBSD and Windows use millisecond units */ -#define KEEPALIVE_FACTOR(x) (x *= 1000) -#else -#define KEEPALIVE_FACTOR(x) -#endif - -#if defined(HAVE_WINSOCK2_H) && !defined(SIO_KEEPALIVE_VALS) -#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4) - -struct tcp_keepalive { - u_long onoff; - u_long keepalivetime; - u_long keepaliveinterval; -}; -#endif - -static void -tcpkeepalive(struct Curl_easy *data, - curl_socket_t sockfd) -{ - int optval = data->set.tcp_keepalive?1:0; - - /* only set IDLE and INTVL if setting KEEPALIVE is successful */ - if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, - (void *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set SO_KEEPALIVE on fd %d", sockfd); - } - else { -#if defined(SIO_KEEPALIVE_VALS) - struct tcp_keepalive vals; - DWORD dummy; - vals.onoff = 1; - optval = curlx_sltosi(data->set.tcp_keepidle); - KEEPALIVE_FACTOR(optval); - vals.keepalivetime = optval; - optval = curlx_sltosi(data->set.tcp_keepintvl); - KEEPALIVE_FACTOR(optval); - vals.keepaliveinterval = optval; - if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals), - NULL, 0, &dummy, NULL, NULL) != 0) { - infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d", - (int)sockfd, WSAGetLastError()); - } -#else -#ifdef TCP_KEEPIDLE - optval = curlx_sltosi(data->set.tcp_keepidle); - KEEPALIVE_FACTOR(optval); - if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, - (void *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set TCP_KEEPIDLE on fd %d", sockfd); - } -#elif defined(TCP_KEEPALIVE) - /* Mac OS X style */ - optval = curlx_sltosi(data->set.tcp_keepidle); - KEEPALIVE_FACTOR(optval); - if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE, - (void *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set TCP_KEEPALIVE on fd %d", sockfd); - } -#endif -#ifdef TCP_KEEPINTVL - optval = curlx_sltosi(data->set.tcp_keepintvl); - KEEPALIVE_FACTOR(optval); - if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, - (void *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set TCP_KEEPINTVL on fd %d", sockfd); - } -#endif -#endif - } -} - -static CURLcode -singleipconnect(struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai, /* start connecting to this */ - int tempindex); /* 0 or 1 among the temp ones */ - -/* - * Curl_timeleft() returns the amount of milliseconds left allowed for the - * transfer/connection. If the value is 0, there's no timeout (ie there's - * infinite time left). If the value is negative, the timeout time has already - * elapsed. - * - * If 'nowp' is non-NULL, it points to the current time. - * 'duringconnect' is FALSE if not during a connect, as then of course the - * connect timeout is not taken into account! - * - * @unittest: 1303 - */ - -#define TIMEOUT_CONNECT 1 -#define TIMEOUT_MAXTIME 2 - -timediff_t Curl_timeleft(struct Curl_easy *data, - struct curltime *nowp, - bool duringconnect) -{ - unsigned int timeout_set = 0; - timediff_t connect_timeout_ms = 0; - timediff_t maxtime_timeout_ms = 0; - timediff_t timeout_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 > 0) { - timeout_set = TIMEOUT_MAXTIME; - maxtime_timeout_ms = data->set.timeout; - } - if(duringconnect) { - timeout_set |= TIMEOUT_CONNECT; - connect_timeout_ms = (data->set.connecttimeout > 0) ? - data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT; - } - if(!timeout_set) - /* no timeout */ - return 0; - - if(!nowp) { - now = Curl_now(); - nowp = &now; - } - - if(timeout_set & TIMEOUT_MAXTIME) { - maxtime_timeout_ms -= Curl_timediff(*nowp, data->progress.t_startop); - timeout_ms = maxtime_timeout_ms; - } - - if(timeout_set & TIMEOUT_CONNECT) { - connect_timeout_ms -= Curl_timediff(*nowp, data->progress.t_startsingle); - - if(!(timeout_set & TIMEOUT_MAXTIME) || - (connect_timeout_ms < maxtime_timeout_ms)) - timeout_ms = connect_timeout_ms; - } - - if(!timeout_ms) - /* avoid returning 0 as that means no timeout! */ - return -1; - - return timeout_ms; -} - -static CURLcode bindlocal(struct Curl_easy *data, - curl_socket_t sockfd, int af, unsigned int scope) -{ - struct connectdata *conn = data->conn; - struct Curl_sockaddr_storage sa; - struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */ - curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */ - struct sockaddr_in *si4 = (struct sockaddr_in *)&sa; -#ifdef ENABLE_IPV6 - struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa; -#endif - - struct Curl_dns_entry *h = NULL; - unsigned short port = data->set.localport; /* use this port number, 0 for - "random" */ - /* how many port numbers to try to bind to, increasing one at a time */ - int portnum = data->set.localportrange; - const char *dev = data->set.str[STRING_DEVICE]; - int error; -#ifdef IP_BIND_ADDRESS_NO_PORT - int on = 1; -#endif -#ifndef ENABLE_IPV6 - (void)scope; -#endif - - /************************************************************* - * Select device to bind socket to - *************************************************************/ - if(!dev && !port) - /* no local kind of binding was requested */ - return CURLE_OK; - - memset(&sa, 0, sizeof(struct Curl_sockaddr_storage)); - - if(dev && (strlen(dev)<255) ) { - char myhost[256] = ""; - int done = 0; /* -1 for error, 1 for address found */ - bool is_interface = FALSE; - bool is_host = FALSE; - static const char *if_prefix = "if!"; - static const char *host_prefix = "host!"; - - if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) { - dev += strlen(if_prefix); - is_interface = TRUE; - } - else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) { - dev += strlen(host_prefix); - is_host = TRUE; - } - - /* interface */ - if(!is_host) { -#ifdef SO_BINDTODEVICE - /* I am not sure any other OSs than Linux that provide this feature, - * and at the least I cannot test. --Ben - * - * This feature allows one to tightly bind the local socket to a - * particular interface. This will force even requests to other - * local interfaces to go out the external interface. - * - * - * Only bind to the interface when specified as interface, not just - * as a hostname or ip address. - * - * interface might be a VRF, eg: vrf-blue, which means it cannot be - * converted to an IP address and would fail Curl_if2ip. Simply try - * to use it straight away. - */ - if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, - dev, (curl_socklen_t)strlen(dev) + 1) == 0) { - /* This is typically "errno 1, error: Operation not permitted" if - * you're not running as root or another suitable privileged - * user. - * If it succeeds it means the parameter was a valid interface and - * not an IP address. Return immediately. - */ - return CURLE_OK; - } -#endif - - switch(Curl_if2ip(af, -#ifdef ENABLE_IPV6 - scope, conn->scope_id, -#endif - dev, myhost, sizeof(myhost))) { - case IF2IP_NOT_FOUND: - if(is_interface) { - /* Do not fall back to treating it as a host name */ - failf(data, "Couldn't bind to interface '%s'", dev); - return CURLE_INTERFACE_FAILED; - } - break; - case IF2IP_AF_NOT_SUPPORTED: - /* Signal the caller to try another address family if available */ - return CURLE_UNSUPPORTED_PROTOCOL; - case IF2IP_FOUND: - is_interface = TRUE; - /* - * We now have the numerical IP address in the 'myhost' buffer - */ - infof(data, "Local Interface %s is ip %s using address family %i", - dev, myhost, af); - done = 1; - break; - } - } - if(!is_interface) { - /* - * This was not an interface, resolve the name as a host name - * or IP number - * - * Temporarily force name resolution to use only the address type - * of the connection. The resolve functions should really be changed - * to take a type parameter instead. - */ - unsigned char ipver = conn->ip_version; - int rc; - - if(af == AF_INET) - conn->ip_version = CURL_IPRESOLVE_V4; -#ifdef ENABLE_IPV6 - else if(af == AF_INET6) - conn->ip_version = CURL_IPRESOLVE_V6; -#endif - - rc = Curl_resolv(data, dev, 0, FALSE, &h); - if(rc == CURLRESOLV_PENDING) - (void)Curl_resolver_wait_resolv(data, &h); - conn->ip_version = ipver; - - if(h) { - /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ - Curl_printable_address(h->addr, myhost, sizeof(myhost)); - infof(data, "Name '%s' family %i resolved to '%s' family %i", - dev, af, myhost, h->addr->ai_family); - Curl_resolv_unlock(data, h); - if(af != h->addr->ai_family) { - /* bad IP version combo, signal the caller to try another address - family if available */ - return CURLE_UNSUPPORTED_PROTOCOL; - } - done = 1; - } - else { - /* - * provided dev was no interface (or interfaces are not supported - * e.g. solaris) no ip address and no domain we fail here - */ - done = -1; - } - } - - if(done > 0) { -#ifdef ENABLE_IPV6 - /* IPv6 address */ - if(af == AF_INET6) { -#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID - char *scope_ptr = strchr(myhost, '%'); - if(scope_ptr) - *(scope_ptr++) = 0; -#endif - if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) { - si6->sin6_family = AF_INET6; - si6->sin6_port = htons(port); -#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID - if(scope_ptr) - /* The "myhost" string either comes from Curl_if2ip or from - Curl_printable_address. The latter returns only numeric scope - IDs and the former returns none at all. So the scope ID, if - present, is known to be numeric */ - si6->sin6_scope_id = atoi(scope_ptr); -#endif - } - sizeof_sa = sizeof(struct sockaddr_in6); - } - else -#endif - /* IPv4 address */ - if((af == AF_INET) && - (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) { - si4->sin_family = AF_INET; - si4->sin_port = htons(port); - sizeof_sa = sizeof(struct sockaddr_in); - } - } - - if(done < 1) { - /* errorbuf is set false so failf will overwrite any message already in - the error buffer, so the user receives this error message instead of a - generic resolve error. */ - data->state.errorbuf = FALSE; - failf(data, "Couldn't bind to '%s'", dev); - return CURLE_INTERFACE_FAILED; - } - } - else { - /* no device was given, prepare sa to match af's needs */ -#ifdef ENABLE_IPV6 - if(af == AF_INET6) { - si6->sin6_family = AF_INET6; - si6->sin6_port = htons(port); - sizeof_sa = sizeof(struct sockaddr_in6); - } - else -#endif - if(af == AF_INET) { - si4->sin_family = AF_INET; - si4->sin_port = htons(port); - sizeof_sa = sizeof(struct sockaddr_in); - } - } -#ifdef IP_BIND_ADDRESS_NO_PORT - (void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on)); -#endif - for(;;) { - if(bind(sockfd, sock, sizeof_sa) >= 0) { - /* we succeeded to bind */ - struct Curl_sockaddr_storage add; - curl_socklen_t size = sizeof(add); - memset(&add, 0, sizeof(struct Curl_sockaddr_storage)); - if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) { - char buffer[STRERROR_LEN]; - data->state.os_errno = error = SOCKERRNO; - failf(data, "getsockname() failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); - return CURLE_INTERFACE_FAILED; - } - infof(data, "Local port: %hu", port); - conn->bits.bound = TRUE; - return CURLE_OK; - } - - if(--portnum > 0) { - port++; /* try next port */ - if(port == 0) - break; - infof(data, "Bind to local port %hu failed, trying next", port - 1); - /* We re-use/clobber the port variable here below */ - if(sock->sa_family == AF_INET) - si4->sin_port = ntohs(port); -#ifdef ENABLE_IPV6 - else - si6->sin6_port = ntohs(port); -#endif - } - else - break; - } - { - char buffer[STRERROR_LEN]; - data->state.os_errno = error = SOCKERRNO; - failf(data, "bind failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); - } - - return CURLE_INTERFACE_FAILED; -} - -/* - * verifyconnect() returns TRUE if the connect really has happened. - */ -static bool verifyconnect(curl_socket_t sockfd, int *error) -{ - bool rc = TRUE; -#ifdef SO_ERROR - int err = 0; - curl_socklen_t errSize = sizeof(err); - -#ifdef WIN32 - /* - * In October 2003 we effectively nullified this function on Windows due to - * problems with it using all CPU in multi-threaded cases. - * - * In May 2004, we bring it back to offer more info back on connect failures. - * Gisle Vanem could reproduce the former problems with this function, but - * could avoid them by adding this SleepEx() call below: - * - * "I don't have Rational Quantify, but the hint from his post was - * ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe - * just Sleep(0) would be enough?) would release whatever - * mutex/critical-section the ntdll call is waiting on. - * - * Someone got to verify this on Win-NT 4.0, 2000." - */ - -#ifdef _WIN32_WCE - Sleep(0); -#else - SleepEx(0, FALSE); -#endif - -#endif - - if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize)) - err = SOCKERRNO; -#ifdef _WIN32_WCE - /* Old WinCE versions don't support SO_ERROR */ - if(WSAENOPROTOOPT == err) { - SET_SOCKERRNO(0); - err = 0; - } -#endif -#if defined(EBADIOCTL) && defined(__minix) - /* Minix 3.1.x doesn't support getsockopt on UDP sockets */ - if(EBADIOCTL == err) { - SET_SOCKERRNO(0); - err = 0; - } -#endif - if((0 == err) || (EISCONN == err)) - /* we are connected, awesome! */ - rc = TRUE; - else - /* This wasn't a successful connect */ - rc = FALSE; - if(error) - *error = err; -#else - (void)sockfd; - if(error) - *error = SOCKERRNO; -#endif - return rc; -} - -/* update tempaddr[tempindex] (to the next entry), makes sure to stick - to the correct family */ -static struct Curl_addrinfo *ainext(struct connectdata *conn, - int tempindex, - bool next) /* use next entry? */ -{ - struct Curl_addrinfo *ai = conn->tempaddr[tempindex]; - if(ai && next) - ai = ai->ai_next; - while(ai && (ai->ai_family != conn->tempfamily[tempindex])) - ai = ai->ai_next; - conn->tempaddr[tempindex] = ai; - return ai; -} - -/* Used within the multi interface. Try next IP address, returns error if no - more address exists or error */ -static CURLcode trynextip(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - int tempindex) -{ - CURLcode result = CURLE_COULDNT_CONNECT; - - /* First clean up after the failed socket. - Don't close it yet to ensure that the next IP's socket gets a different - file descriptor, which can prevent bugs when the curl_multi_socket_action - interface is used with certain select() replacements such as kqueue. */ - curl_socket_t fd_to_close = conn->tempsock[tempindex]; - conn->tempsock[tempindex] = CURL_SOCKET_BAD; - - if(sockindex == FIRSTSOCKET) { - struct Curl_addrinfo *ai = conn->tempaddr[tempindex]; - - while(ai) { - result = singleipconnect(data, conn, ai, tempindex); - if(result == CURLE_COULDNT_CONNECT) { - ai = ainext(conn, tempindex, TRUE); - continue; - } - break; - } - } - - if(fd_to_close != CURL_SOCKET_BAD) - Curl_closesocket(data, conn, fd_to_close); - - return result; -} - -/* Copies connection info into the transfer handle to make it available when - the transfer handle is no longer associated with the connection. */ -void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn, - char *local_ip, int local_port) -{ - memcpy(data->info.conn_primary_ip, conn->primary_ip, MAX_IPADR_LEN); - if(local_ip && local_ip[0]) - memcpy(data->info.conn_local_ip, local_ip, MAX_IPADR_LEN); - else - data->info.conn_local_ip[0] = 0; - data->info.conn_scheme = conn->handler->scheme; - /* conn_protocol can only provide "old" protocols */ - data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK; - data->info.conn_primary_port = conn->port; - data->info.conn_remote_port = conn->remote_port; - data->info.conn_local_port = local_port; -} - -/* retrieves ip address and port from a sockaddr structure. - note it calls Curl_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 ENABLE_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(Curl_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 ENABLE_IPV6 - case AF_INET6: - si6 = (struct sockaddr_in6 *)(void *) sa; - if(Curl_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; - msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path); - } - else - addr[0] = 0; /* socket with no name */ - *port = 0; - return TRUE; -#endif - default: - break; - } - - addr[0] = '\0'; - *port = 0; - errno = EAFNOSUPPORT; - return FALSE; -} - -/* retrieves the start/end point information of a socket of an established - connection */ -void Curl_conninfo_remote(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t sockfd) -{ -#ifdef HAVE_GETPEERNAME - char buffer[STRERROR_LEN]; - struct Curl_sockaddr_storage ssrem; - curl_socklen_t plen; - int port; - plen = sizeof(struct Curl_sockaddr_storage); - memset(&ssrem, 0, sizeof(ssrem)); - if(getpeername(sockfd, (struct sockaddr*) &ssrem, &plen)) { - int error = SOCKERRNO; - failf(data, "getpeername() failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); - return; - } - if(!Curl_addr2string((struct sockaddr*)&ssrem, plen, - conn->primary_ip, &port)) { - failf(data, "ssrem inet_ntop() failed with errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - return; - } -#else - (void)data; - (void)conn; - (void)sockfd; -#endif -} - -/* retrieves the start/end point information of a socket of an established - connection */ -void Curl_conninfo_local(struct Curl_easy *data, curl_socket_t sockfd, - char *local_ip, int *local_port) -{ -#ifdef HAVE_GETSOCKNAME - char buffer[STRERROR_LEN]; - struct Curl_sockaddr_storage ssloc; - curl_socklen_t slen; - slen = sizeof(struct Curl_sockaddr_storage); - memset(&ssloc, 0, sizeof(ssloc)); - if(getsockname(sockfd, (struct sockaddr*) &ssloc, &slen)) { - int error = SOCKERRNO; - failf(data, "getsockname() failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); - return; - } - if(!Curl_addr2string((struct sockaddr*)&ssloc, slen, - local_ip, local_port)) { - failf(data, "ssloc inet_ntop() failed with errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - return; - } -#else - (void)data; - (void)sockfd; - (void)local_ip; - (void)local_port; -#endif -} - -/* retrieves the start/end point information of a socket of an established - connection */ -void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t sockfd) -{ - /* 'local_ip' and 'local_port' get filled with local's numerical - ip address and port number whenever an outgoing connection is - **established** from the primary socket to a remote address. */ - char local_ip[MAX_IPADR_LEN] = ""; - int local_port = -1; - - if(!conn->bits.reuse && - (conn->transport != TRNSPRT_TCP || !conn->bits.tcp_fastopen)) - Curl_conninfo_remote(data, conn, sockfd); - Curl_conninfo_local(data, sockfd, local_ip, &local_port); - - /* persist connection info in session handle */ - Curl_persistconninfo(data, conn, local_ip, local_port); -} - -/* After a TCP connection to the proxy has been verified, this function does - the next magic steps. If 'done' isn't set TRUE, it is not done yet and - must be called again. - - Note: this function's sub-functions call failf() - -*/ -static CURLcode connect_SOCKS(struct Curl_easy *data, int sockindex, - bool *done) -{ - CURLcode result = CURLE_OK; -#ifndef CURL_DISABLE_PROXY - CURLproxycode pxresult = CURLPX_OK; - struct connectdata *conn = data->conn; - if(conn->bits.socksproxy) { - /* for the secondary socket (FTP), use the "connect to host" - * but ignore the "connect to port" (use the secondary port) - */ - const char * const host = - conn->bits.httpproxy ? - conn->http_proxy.host.name : - conn->bits.conn_to_host ? - conn->conn_to_host.name : - sockindex == SECONDARYSOCKET ? - conn->secondaryhostname : conn->host.name; - const int port = - conn->bits.httpproxy ? (int)conn->http_proxy.port : - sockindex == SECONDARYSOCKET ? conn->secondary_port : - conn->bits.conn_to_port ? conn->conn_to_port : - conn->remote_port; - switch(conn->socks_proxy.proxytype) { - case CURLPROXY_SOCKS5: - case CURLPROXY_SOCKS5_HOSTNAME: - pxresult = Curl_SOCKS5(conn->socks_proxy.user, conn->socks_proxy.passwd, - host, port, sockindex, data, done); - break; - - case CURLPROXY_SOCKS4: - case CURLPROXY_SOCKS4A: - pxresult = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex, - data, done); - break; - - default: - failf(data, "unknown proxytype option given"); - result = CURLE_COULDNT_CONNECT; - } /* switch proxytype */ - if(pxresult) { - result = CURLE_PROXY; - data->info.pxcode = pxresult; - } - } - else -#else - (void)data; - (void)sockindex; -#endif /* CURL_DISABLE_PROXY */ - *done = TRUE; /* no SOCKS proxy, so consider us connected */ - - return result; -} - -/* - * post_SOCKS() is called after a successful connect to the peer, which - * *could* be a SOCKS proxy - */ -static void post_SOCKS(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool *connected) -{ - conn->bits.tcpconnect[sockindex] = TRUE; - - *connected = TRUE; - if(sockindex == FIRSTSOCKET) - Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */ - Curl_updateconninfo(data, conn, conn->sock[sockindex]); - Curl_verboseconnect(data, conn); - data->info.numconnects++; /* to track the number of connections made */ -} - -/* - * Curl_is_connected() checks if the socket has connected. - */ - -CURLcode Curl_is_connected(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool *connected) -{ - CURLcode result = CURLE_OK; - timediff_t allow; - int error = 0; - struct curltime now; - int rc = 0; - unsigned int i; - - DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET); - - *connected = FALSE; /* a very negative world view is best */ - - if(conn->bits.tcpconnect[sockindex]) { - /* we are connected already! */ - *connected = TRUE; - return CURLE_OK; - } - - now = Curl_now(); - - if(SOCKS_STATE(conn->cnnct.state)) { - /* still doing SOCKS */ - result = connect_SOCKS(data, sockindex, connected); - if(!result && *connected) - post_SOCKS(data, conn, sockindex, connected); - return result; - } - - for(i = 0; i<2; i++) { - const int other = i ^ 1; - if(conn->tempsock[i] == CURL_SOCKET_BAD) - continue; - error = 0; -#ifdef ENABLE_QUIC - if(conn->transport == TRNSPRT_QUIC) { - result = Curl_quic_is_connected(data, conn, i, connected); - if(!result && *connected) { - /* use this socket from now on */ - conn->sock[sockindex] = conn->tempsock[i]; - conn->ip_addr = conn->tempaddr[i]; - conn->tempsock[i] = CURL_SOCKET_BAD; - post_SOCKS(data, conn, sockindex, connected); - connkeep(conn, "HTTP/3 default"); - if(conn->tempsock[other] != CURL_SOCKET_BAD) - Curl_quic_disconnect(data, conn, other); - return CURLE_OK; - } - /* When a QUIC connect attempt fails, the better error explanation is in - 'result' and not in errno */ - if(result) { - conn->tempsock[i] = CURL_SOCKET_BAD; - error = SOCKERRNO; - } - } - else -#endif - { -#ifdef mpeix - /* Call this function once now, and ignore the results. We do this to - "clear" the error state on the socket so that we can later read it - reliably. This is reported necessary on the MPE/iX operating - system. */ - (void)verifyconnect(conn->tempsock[i], NULL); -#endif - - /* check socket for connect */ - rc = SOCKET_WRITABLE(conn->tempsock[i], 0); - } - - if(rc == 0) { /* no connection yet */ - if(Curl_timediff(now, conn->connecttime) >= - conn->timeoutms_per_addr[i]) { - infof(data, "After %" CURL_FORMAT_TIMEDIFF_T - "ms connect time, move on!", conn->timeoutms_per_addr[i]); - error = ETIMEDOUT; - } - - /* should we try another protocol family? */ - if(i == 0 && !conn->bits.parallel_connect && - (Curl_timediff(now, conn->connecttime) >= - data->set.happy_eyeballs_timeout)) { - conn->bits.parallel_connect = TRUE; /* starting now */ - trynextip(data, conn, sockindex, 1); - } - } - else if(rc == CURL_CSELECT_OUT || conn->bits.tcp_fastopen) { - if(verifyconnect(conn->tempsock[i], &error)) { - /* we are connected with TCP, awesome! */ - - /* use this socket from now on */ - conn->sock[sockindex] = conn->tempsock[i]; - conn->ip_addr = conn->tempaddr[i]; - conn->tempsock[i] = CURL_SOCKET_BAD; -#ifdef ENABLE_IPV6 - conn->bits.ipv6 = (conn->ip_addr->ai_family == AF_INET6)?TRUE:FALSE; -#endif - - /* close the other socket, if open */ - if(conn->tempsock[other] != CURL_SOCKET_BAD) { - Curl_closesocket(data, conn, conn->tempsock[other]); - conn->tempsock[other] = CURL_SOCKET_BAD; - } - - /* see if we need to kick off any SOCKS proxy magic once we - connected */ - result = connect_SOCKS(data, sockindex, connected); - if(result || !*connected) - return result; - - post_SOCKS(data, conn, sockindex, connected); - - return CURLE_OK; - } - } - else if(rc & CURL_CSELECT_ERR) { - (void)verifyconnect(conn->tempsock[i], &error); - } - - /* - * The connection failed here, we should attempt to connect to the "next - * address" for the given host. But first remember the latest error. - */ - if(error) { - data->state.os_errno = error; - SET_SOCKERRNO(error); - if(conn->tempaddr[i]) { - CURLcode status; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - char ipaddress[MAX_IPADR_LEN]; - char buffer[STRERROR_LEN]; - Curl_printable_address(conn->tempaddr[i], ipaddress, - sizeof(ipaddress)); -#ifdef ENABLE_QUIC - if(conn->transport == TRNSPRT_QUIC) { - infof(data, "connect to %s port %u failed: %s", - ipaddress, conn->port, curl_easy_strerror(result)); - } - else -#endif - infof(data, "connect to %s port %u failed: %s", - ipaddress, conn->port, - Curl_strerror(error, buffer, sizeof(buffer))); -#endif - - allow = Curl_timeleft(data, &now, TRUE); - conn->timeoutms_per_addr[i] = conn->tempaddr[i]->ai_next == NULL ? - allow : allow / 2; - ainext(conn, i, TRUE); - status = trynextip(data, conn, sockindex, i); - if((status != CURLE_COULDNT_CONNECT) || - conn->tempsock[other] == CURL_SOCKET_BAD) { - /* the last attempt failed and no other sockets remain open */ - if(!result) - result = status; - } - } - } - } - - /* - * Now that we've checked whether we are connected, check whether we've - * already timed out. - * - * First figure out how long time we have left to connect */ - - allow = Curl_timeleft(data, &now, TRUE); - - if(allow < 0) { - /* time-out, bail out, go home */ - failf(data, "Connection timeout after %ld ms", - Curl_timediff(now, data->progress.t_startsingle)); - return CURLE_OPERATION_TIMEDOUT; - } - - if(result && - (conn->tempsock[0] == CURL_SOCKET_BAD) && - (conn->tempsock[1] == CURL_SOCKET_BAD)) { - /* no more addresses to try */ - const char *hostname; - CURLcode failreason = result; - - /* if the first address family runs out of addresses to try before the - happy eyeball timeout, go ahead and try the next family now */ - result = trynextip(data, conn, sockindex, 1); - if(!result) - return result; - - result = failreason; - -#ifndef CURL_DISABLE_PROXY - if(conn->bits.socksproxy) - hostname = conn->socks_proxy.host.name; - else if(conn->bits.httpproxy) - hostname = conn->http_proxy.host.name; - else -#endif - if(conn->bits.conn_to_host) - hostname = conn->conn_to_host.name; - else - hostname = conn->host.name; - - failf(data, "Failed to connect to %s port %u after " - "%" CURL_FORMAT_TIMEDIFF_T " ms: %s", - hostname, conn->port, - Curl_timediff(now, data->progress.t_startsingle), - curl_easy_strerror(result)); - - Curl_quic_disconnect(data, conn, 0); - Curl_quic_disconnect(data, conn, 1); - -#ifdef WSAETIMEDOUT - if(WSAETIMEDOUT == data->state.os_errno) - result = CURLE_OPERATION_TIMEDOUT; -#elif defined(ETIMEDOUT) - if(ETIMEDOUT == data->state.os_errno) - result = CURLE_OPERATION_TIMEDOUT; -#endif - } - else - result = CURLE_OK; /* still trying */ - - return result; -} - -static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd) -{ -#if defined(TCP_NODELAY) - curl_socklen_t onoff = (curl_socklen_t) 1; - int level = IPPROTO_TCP; -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) - char buffer[STRERROR_LEN]; -#else - (void) data; -#endif - - if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff, - sizeof(onoff)) < 0) - infof(data, "Could not set TCP_NODELAY: %s", - Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); -#else - (void)data; - (void)sockfd; -#endif -} - -#ifdef SO_NOSIGPIPE -/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when - sending data to a dead peer (instead of relying on the 4th argument to send - being MSG_NOSIGNAL). Possibly also existing and in use on other BSD - systems? */ -static void nosigpipe(struct Curl_easy *data, - curl_socket_t sockfd) -{ - int onoff = 1; - if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff, - sizeof(onoff)) < 0) { -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) - char buffer[STRERROR_LEN]; - infof(data, "Could not set SO_NOSIGPIPE: %s", - Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); -#endif - } -} -#else -#define nosigpipe(x,y) Curl_nop_stmt -#endif - -#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://support.microsoft.com/kb/823764 - - Work-around: Make the Socket Send Buffer Size Larger Than the Program Send - Buffer Size - - The problem described in this knowledge-base is applied only to pre-Vista - Windows. Following function trying to detect OS version and skips - SO_SNDBUF adjustment for Windows Vista and above. -*/ -#define DETECT_OS_NONE 0 -#define DETECT_OS_PREVISTA 1 -#define DETECT_OS_VISTA_OR_LATER 2 - -void Curl_sndbufset(curl_socket_t sockfd) -{ - int val = CURL_MAX_WRITE_SIZE + 32; - int curval = 0; - int curlen = sizeof(curval); - - static int detectOsState = DETECT_OS_NONE; - - if(detectOsState == DETECT_OS_NONE) { - if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL)) - detectOsState = DETECT_OS_VISTA_OR_LATER; - else - detectOsState = DETECT_OS_PREVISTA; - } - - if(detectOsState == DETECT_OS_VISTA_OR_LATER) - return; - - if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0) - if(curval > val) - return; - - setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val)); -} -#endif - -/* - * singleipconnect() - * - * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to - * CURL_SOCKET_BAD. Other errors will however return proper errors. - * - * singleipconnect() connects to the given IP only, and it may return without - * having connected. - */ -static CURLcode singleipconnect(struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai, - int tempindex) -{ - struct Curl_sockaddr_ex addr; - int rc = -1; - int error = 0; - bool isconnected = FALSE; - curl_socket_t sockfd; - CURLcode result; - char ipaddress[MAX_IPADR_LEN]; - int port; - bool is_tcp; -#ifdef TCP_FASTOPEN_CONNECT - int optval = 1; -#endif - const char *ipmsg; - char buffer[STRERROR_LEN]; - curl_socket_t *sockp = &conn->tempsock[tempindex]; - *sockp = CURL_SOCKET_BAD; - - result = Curl_socket(data, ai, &addr, &sockfd); - if(result) - return result; - - /* store remote address and port used in this connection attempt */ - if(!Curl_addr2string((struct sockaddr*)&addr.sa_addr, addr.addrlen, - ipaddress, &port)) { - /* malformed address or bug in inet_ntop, try next address */ - failf(data, "sa_addr inet_ntop() failed with errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - Curl_closesocket(data, conn, sockfd); - return CURLE_OK; - } -#ifdef ENABLE_IPV6 - if(addr.family == AF_INET6) - ipmsg = " Trying [%s]:%d..."; - else -#endif - ipmsg = " Trying %s:%d..."; - infof(data, ipmsg, ipaddress, port); - -#ifdef ENABLE_IPV6 - is_tcp = (addr.family == AF_INET || addr.family == AF_INET6) && - addr.socktype == SOCK_STREAM; -#else - is_tcp = (addr.family == AF_INET) && addr.socktype == SOCK_STREAM; -#endif - if(is_tcp && data->set.tcp_nodelay) - tcpnodelay(data, sockfd); - - nosigpipe(data, sockfd); - - Curl_sndbufset(sockfd); - - if(is_tcp && data->set.tcp_keepalive) - tcpkeepalive(data, sockfd); - - if(data->set.fsockopt) { - /* activate callback for setting socket options */ - Curl_set_in_callback(data, true); - error = data->set.fsockopt(data->set.sockopt_client, - sockfd, - CURLSOCKTYPE_IPCXN); - Curl_set_in_callback(data, false); - - if(error == CURL_SOCKOPT_ALREADY_CONNECTED) - isconnected = TRUE; - else if(error) { - Curl_closesocket(data, conn, sockfd); /* close the socket and bail out */ - return CURLE_ABORTED_BY_CALLBACK; - } - } - - /* possibly bind the local end to an IP, interface or port */ - if(addr.family == AF_INET -#ifdef ENABLE_IPV6 - || addr.family == AF_INET6 -#endif - ) { - result = bindlocal(data, sockfd, addr.family, - Curl_ipv6_scope((struct sockaddr*)&addr.sa_addr)); - if(result) { - Curl_closesocket(data, conn, sockfd); /* close socket and bail out */ - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - /* The address family is not supported on this interface. - We can continue trying addresses */ - return CURLE_COULDNT_CONNECT; - } - return result; - } - } - - /* set socket non-blocking */ - (void)curlx_nonblock(sockfd, TRUE); - - conn->connecttime = Curl_now(); - if(conn->num_addr > 1) { - Curl_expire(data, conn->timeoutms_per_addr[0], EXPIRE_DNS_PER_NAME); - Curl_expire(data, conn->timeoutms_per_addr[1], EXPIRE_DNS_PER_NAME2); - } - - /* Connect TCP and QUIC sockets */ - if(!isconnected && (conn->transport != TRNSPRT_UDP)) { - if(conn->bits.tcp_fastopen) { -#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */ -# if defined(HAVE_BUILTIN_AVAILABLE) - /* while connectx function is available since macOS 10.11 / iOS 9, - it did not have the interface declared correctly until - Xcode 9 / macOS SDK 10.13 */ - if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) { - sa_endpoints_t endpoints; - endpoints.sae_srcif = 0; - endpoints.sae_srcaddr = NULL; - endpoints.sae_srcaddrlen = 0; - endpoints.sae_dstaddr = &addr.sa_addr; - endpoints.sae_dstaddrlen = addr.addrlen; - - rc = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY, - CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT, - NULL, 0, NULL, NULL); - } - else { - rc = connect(sockfd, &addr.sa_addr, addr.addrlen); - } -# else - rc = connect(sockfd, &addr.sa_addr, addr.addrlen); -# endif /* HAVE_BUILTIN_AVAILABLE */ -#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */ - if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, - (void *)&optval, sizeof(optval)) < 0) - infof(data, "Failed to enable TCP Fast Open on fd %d", sockfd); - - rc = connect(sockfd, &addr.sa_addr, addr.addrlen); -#elif defined(MSG_FASTOPEN) /* old Linux */ - if(conn->given->flags & PROTOPT_SSL) - rc = connect(sockfd, &addr.sa_addr, addr.addrlen); - else - rc = 0; /* Do nothing */ -#endif - } - else { - rc = connect(sockfd, &addr.sa_addr, addr.addrlen); - } - - if(-1 == rc) - error = SOCKERRNO; -#ifdef ENABLE_QUIC - else if(conn->transport == TRNSPRT_QUIC) { - /* pass in 'sockfd' separately since it hasn't been put into the - tempsock array at this point */ - result = Curl_quic_connect(data, conn, sockfd, tempindex, - &addr.sa_addr, addr.addrlen); - if(result) - error = SOCKERRNO; - } -#endif - } - else { - *sockp = sockfd; - return CURLE_OK; - } - - if(-1 == rc) { - switch(error) { - case EINPROGRESS: - case EWOULDBLOCK: -#if defined(EAGAIN) -#if (EAGAIN) != (EWOULDBLOCK) - /* On some platforms EAGAIN and EWOULDBLOCK are the - * same value, and on others they are different, hence - * the odd #if - */ - case EAGAIN: -#endif -#endif - result = CURLE_OK; - break; - - default: - /* unknown error, fallthrough and try another address! */ - infof(data, "Immediate connect fail for %s: %s", - ipaddress, Curl_strerror(error, buffer, sizeof(buffer))); - data->state.os_errno = error; - - /* connect failed */ - Curl_closesocket(data, conn, sockfd); - result = CURLE_COULDNT_CONNECT; - } - } - - if(!result) - *sockp = sockfd; - - return result; -} - -/* - * TCP connect to the given host with timeout, proxy or remote doesn't matter. - * There might be more than one IP address to try out. Fill in the passed - * pointer with the connected socket. - */ - -CURLcode Curl_connecthost(struct Curl_easy *data, - struct connectdata *conn, /* context */ - const struct Curl_dns_entry *remotehost) -{ - CURLcode result = CURLE_COULDNT_CONNECT; - int i; - timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* a precaution, no need to continue if time already is up */ - failf(data, "Connection time-out"); - return CURLE_OPERATION_TIMEDOUT; - } - - conn->num_addr = Curl_num_addresses(remotehost->addr); - conn->tempaddr[0] = conn->tempaddr[1] = remotehost->addr; - conn->tempsock[0] = conn->tempsock[1] = CURL_SOCKET_BAD; - - /* Max time for the next connection attempt */ - conn->timeoutms_per_addr[0] = - conn->tempaddr[0]->ai_next == NULL ? timeout_ms : timeout_ms / 2; - conn->timeoutms_per_addr[1] = - conn->tempaddr[1]->ai_next == NULL ? timeout_ms : timeout_ms / 2; - - if(conn->ip_version == CURL_IPRESOLVE_WHATEVER) { - /* any IP version is allowed */ - conn->tempfamily[0] = conn->tempaddr[0]? - conn->tempaddr[0]->ai_family:0; -#ifdef ENABLE_IPV6 - conn->tempfamily[1] = conn->tempfamily[0] == AF_INET6 ? - AF_INET : AF_INET6; -#else - conn->tempfamily[1] = AF_UNSPEC; -#endif - } - else { - /* only one IP version is allowed */ - conn->tempfamily[0] = (conn->ip_version == CURL_IPRESOLVE_V4) ? - AF_INET : -#ifdef ENABLE_IPV6 - AF_INET6; -#else - AF_UNSPEC; -#endif - conn->tempfamily[1] = AF_UNSPEC; - - ainext(conn, 0, FALSE); /* find first address of the right type */ - } - - ainext(conn, 1, FALSE); /* assigns conn->tempaddr[1] accordingly */ - - DEBUGF(infof(data, "family0 == %s, family1 == %s", - conn->tempfamily[0] == AF_INET ? "v4" : "v6", - conn->tempfamily[1] == AF_INET ? "v4" : "v6")); - - /* get through the list in family order in case of quick failures */ - for(i = 0; (i < 2) && result; i++) { - while(conn->tempaddr[i]) { - result = singleipconnect(data, conn, conn->tempaddr[i], i); - if(!result) - break; - ainext(conn, i, TRUE); - } - } - if(result) - return result; - - Curl_expire(data, data->set.happy_eyeballs_timeout, - EXPIRE_HAPPY_EYEBALLS); - - return CURLE_OK; -} - -struct connfind { - long id_tofind; - struct connectdata *found; -}; - -static int conn_is_conn(struct Curl_easy *data, - struct connectdata *conn, void *param) -{ - struct connfind *f = (struct connfind *)param; - (void)data; - if(conn->connection_id == f->id_tofind) { - f->found = conn; - return 1; - } - return 0; -} - -/* - * 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) && (data->multi_easy || data->multi)) { - struct connectdata *c; - struct connfind find; - find.id_tofind = data->state.lastconnect_id; - find.found = NULL; - - Curl_conncache_foreach(data, - data->share && (data->share->specifier - & (1<< CURL_LOCK_DATA_CONNECT))? - &data->share->conn_cache: - data->multi_easy? - &data->multi_easy->conn_cache: - &data->multi->conn_cache, &find, conn_is_conn); - - if(!find.found) { - data->state.lastconnect_id = -1; - return CURL_SOCKET_BAD; - } - - c = find.found; - if(connp) - /* only store this if the caller cares for it */ - *connp = c; - return c->sock[FIRSTSOCKET]; - } - return CURL_SOCKET_BAD; -} - -/* - * Check if a connection seems to be alive. - */ -bool Curl_connalive(struct connectdata *conn) -{ - /* First determine if ssl */ - if(conn->ssl[FIRSTSOCKET].use) { - /* use the SSL context */ - if(!Curl_ssl_check_cxn(conn)) - return false; /* FIN received */ - } -/* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */ -#ifdef MSG_PEEK - else if(conn->sock[FIRSTSOCKET] == CURL_SOCKET_BAD) - return false; - else { - /* use the socket */ - char buf; - if(recv((RECV_TYPE_ARG1)conn->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf, - (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) { - return false; /* FIN received */ - } - } -#endif - return true; -} - -/* - * Close a socket. - * - * 'conn' can be NULL, beware! - */ -int Curl_closesocket(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t sock) -{ - if(conn && conn->fclosesocket) { - if((sock == conn->sock[SECONDARYSOCKET]) && conn->bits.sock_accepted) - /* if this socket matches the second socket, and that was created with - accept, then we MUST NOT call the callback but clear the accepted - status */ - conn->bits.sock_accepted = FALSE; - else { - int rc; - Curl_multi_closed(data, sock); - Curl_set_in_callback(data, true); - rc = conn->fclosesocket(conn->closesocket_client, sock); - Curl_set_in_callback(data, false); - return rc; - } - } - - if(conn) - /* tell the multi-socket code about this */ - Curl_multi_closed(data, sock); - - sclose(sock); - - return 0; -} - -/* - * Create a socket based on info from 'conn' and 'ai'. - * - * 'addr' should be a pointer to the correct struct to get data back, or NULL. - * 'sockfd' must be a pointer to a socket descriptor. - * - * If the open socket callback is set, used that! - * - */ -CURLcode Curl_socket(struct Curl_easy *data, - const struct Curl_addrinfo *ai, - struct Curl_sockaddr_ex *addr, - curl_socket_t *sockfd) -{ - struct connectdata *conn = data->conn; - struct Curl_sockaddr_ex dummy; - - if(!addr) - /* if the caller doesn't want info back, use a local temp copy */ - addr = &dummy; - - /* - * 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. - */ - - addr->family = ai->ai_family; - switch(conn->transport) { - case TRNSPRT_TCP: - addr->socktype = SOCK_STREAM; - addr->protocol = IPPROTO_TCP; - break; - case TRNSPRT_UNIX: - addr->socktype = SOCK_STREAM; - addr->protocol = IPPROTO_IP; - break; - default: /* UDP and QUIC */ - addr->socktype = SOCK_DGRAM; - addr->protocol = IPPROTO_UDP; - break; - } - addr->addrlen = ai->ai_addrlen; - - if(addr->addrlen > sizeof(struct Curl_sockaddr_storage)) - addr->addrlen = sizeof(struct Curl_sockaddr_storage); - memcpy(&addr->sa_addr, ai->ai_addr, addr->addrlen); - - if(data->set.fopensocket) { - /* - * If the opensocket callback is set, all the destination address - * information is passed to the callback. Depending on this information the - * callback may opt to abort the connection, this is indicated returning - * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When - * the callback returns a valid socket the destination address information - * might have been changed and this 'new' address will actually be used - * here to connect. - */ - Curl_set_in_callback(data, true); - *sockfd = data->set.fopensocket(data->set.opensocket_client, - CURLSOCKTYPE_IPCXN, - (struct curl_sockaddr *)addr); - Curl_set_in_callback(data, false); - } - else - /* opensocket callback not set, so simply create the socket now */ - *sockfd = socket(addr->family, addr->socktype, addr->protocol); - - if(*sockfd == CURL_SOCKET_BAD) - /* no socket, no connection */ - return CURLE_COULDNT_CONNECT; - - if(conn->transport == TRNSPRT_QUIC) { - /* QUIC sockets need to be nonblocking */ - (void)curlx_nonblock(*sockfd, TRUE); - switch(addr->family) { -#if defined(__linux__) && defined(IP_MTU_DISCOVER) - case AF_INET: { - int val = IP_PMTUDISC_DO; - (void)setsockopt(*sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &val, - sizeof(val)); - break; - } -#endif -#if defined(__linux__) && defined(IPV6_MTU_DISCOVER) - case AF_INET6: { - int val = IPV6_PMTUDISC_DO; - (void)setsockopt(*sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, - sizeof(val)); - break; - } -#endif - } - } - -#if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) - if(conn->scope_id && (addr->family == AF_INET6)) { - struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr; - sa6->sin6_scope_id = conn->scope_id; - } -#endif - - return CURLE_OK; -} - -/* - * 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 isn't multiplexed. */ - /* This function will be called both before and after this connection is - associated with a transfer. */ - bool closeit; - DEBUGASSERT(conn); -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - (void)reason; /* useful for debugging */ -#endif - closeit = (ctrl == CONNCTRL_CONNECTION) || - ((ctrl == CONNCTRL_STREAM) && !(conn->handler->flags & PROTOPT_STREAM)); - if((ctrl == CONNCTRL_STREAM) && - (conn->handler->flags & PROTOPT_STREAM)) - ; - else if((bit)closeit != conn->bits.close) { - conn->bits.close = closeit; /* the only place in the source code that - should assign this bit */ - } -} - -/* Data received can be cached at various levels, so check them all here. */ -bool Curl_conn_data_pending(struct connectdata *conn, int sockindex) -{ - int readable; - DEBUGASSERT(conn); - - if(Curl_ssl_data_pending(conn, sockindex) || - Curl_recv_has_postponed_data(conn, sockindex)) - return true; - - readable = SOCKET_READABLE(conn->sock[sockindex], 0); - return (readable > 0 && (readable & CURL_CSELECT_IN)); -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_FCNTL_H
+#include <fcntl.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 "select.h"
+#include "url.h" /* for Curl_safefree() */
+#include "multiif.h"
+#include "sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "inet_ntop.h"
+#include "inet_pton.h"
+#include "vtls/vtls.h" /* for Curl_ssl_check_cxn() */
+#include "progress.h"
+#include "warnless.h"
+#include "conncache.h"
+#include "multihandle.h"
+#include "share.h"
+#include "version_win32.h"
+#include "quic.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+static bool verifyconnect(curl_socket_t sockfd, int *error);
+
+#if defined(__DragonFly__) || defined(HAVE_WINSOCK2_H)
+/* DragonFlyBSD and Windows use millisecond units */
+#define KEEPALIVE_FACTOR(x) (x *= 1000)
+#else
+#define KEEPALIVE_FACTOR(x)
+#endif
+
+#if defined(HAVE_WINSOCK2_H) && !defined(SIO_KEEPALIVE_VALS)
+#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4)
+
+struct tcp_keepalive {
+ u_long onoff;
+ u_long keepalivetime;
+ u_long keepaliveinterval;
+};
+#endif
+
+static void
+tcpkeepalive(struct Curl_easy *data,
+ curl_socket_t sockfd)
+{
+ int optval = data->set.tcp_keepalive?1:0;
+
+ /* only set IDLE and INTVL if setting KEEPALIVE is successful */
+ if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
+ (void *)&optval, sizeof(optval)) < 0) {
+ infof(data, "Failed to set SO_KEEPALIVE on fd %d", sockfd);
+ }
+ else {
+#if defined(SIO_KEEPALIVE_VALS)
+ struct tcp_keepalive vals;
+ DWORD dummy;
+ vals.onoff = 1;
+ optval = curlx_sltosi(data->set.tcp_keepidle);
+ KEEPALIVE_FACTOR(optval);
+ vals.keepalivetime = optval;
+ optval = curlx_sltosi(data->set.tcp_keepintvl);
+ KEEPALIVE_FACTOR(optval);
+ vals.keepaliveinterval = optval;
+ if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals),
+ NULL, 0, &dummy, NULL, NULL) != 0) {
+ infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d",
+ (int)sockfd, WSAGetLastError());
+ }
+#else
+#ifdef TCP_KEEPIDLE
+ optval = curlx_sltosi(data->set.tcp_keepidle);
+ KEEPALIVE_FACTOR(optval);
+ if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
+ (void *)&optval, sizeof(optval)) < 0) {
+ infof(data, "Failed to set TCP_KEEPIDLE on fd %d", sockfd);
+ }
+#elif defined(TCP_KEEPALIVE)
+ /* Mac OS X style */
+ optval = curlx_sltosi(data->set.tcp_keepidle);
+ KEEPALIVE_FACTOR(optval);
+ if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
+ (void *)&optval, sizeof(optval)) < 0) {
+ infof(data, "Failed to set TCP_KEEPALIVE on fd %d", sockfd);
+ }
+#endif
+#ifdef TCP_KEEPINTVL
+ optval = curlx_sltosi(data->set.tcp_keepintvl);
+ KEEPALIVE_FACTOR(optval);
+ if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
+ (void *)&optval, sizeof(optval)) < 0) {
+ infof(data, "Failed to set TCP_KEEPINTVL on fd %d", sockfd);
+ }
+#endif
+#endif
+ }
+}
+
+static CURLcode
+singleipconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai, /* start connecting to this */
+ int tempindex); /* 0 or 1 among the temp ones */
+
+/*
+ * Curl_timeleft() returns the amount of milliseconds left allowed for the
+ * transfer/connection. If the value is 0, there's no timeout (ie there's
+ * infinite time left). If the value is negative, the timeout time has already
+ * elapsed.
+ *
+ * If 'nowp' is non-NULL, it points to the current time.
+ * 'duringconnect' is FALSE if not during a connect, as then of course the
+ * connect timeout is not taken into account!
+ *
+ * @unittest: 1303
+ */
+
+#define TIMEOUT_CONNECT 1
+#define TIMEOUT_MAXTIME 2
+
+timediff_t Curl_timeleft(struct Curl_easy *data,
+ struct curltime *nowp,
+ bool duringconnect)
+{
+ unsigned int timeout_set = 0;
+ timediff_t connect_timeout_ms = 0;
+ timediff_t maxtime_timeout_ms = 0;
+ timediff_t timeout_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 > 0) {
+ timeout_set = TIMEOUT_MAXTIME;
+ maxtime_timeout_ms = data->set.timeout;
+ }
+ if(duringconnect) {
+ timeout_set |= TIMEOUT_CONNECT;
+ connect_timeout_ms = (data->set.connecttimeout > 0) ?
+ data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
+ }
+ if(!timeout_set)
+ /* no timeout */
+ return 0;
+
+ if(!nowp) {
+ now = Curl_now();
+ nowp = &now;
+ }
+
+ if(timeout_set & TIMEOUT_MAXTIME) {
+ maxtime_timeout_ms -= Curl_timediff(*nowp, data->progress.t_startop);
+ timeout_ms = maxtime_timeout_ms;
+ }
+
+ if(timeout_set & TIMEOUT_CONNECT) {
+ connect_timeout_ms -= Curl_timediff(*nowp, data->progress.t_startsingle);
+
+ if(!(timeout_set & TIMEOUT_MAXTIME) ||
+ (connect_timeout_ms < maxtime_timeout_ms))
+ timeout_ms = connect_timeout_ms;
+ }
+
+ if(!timeout_ms)
+ /* avoid returning 0 as that means no timeout! */
+ return -1;
+
+ return timeout_ms;
+}
+
+static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t sockfd, int af, unsigned int scope)
+{
+ struct Curl_sockaddr_storage sa;
+ struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */
+ curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
+ struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
+#ifdef ENABLE_IPV6
+ struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
+#endif
+
+ struct Curl_dns_entry *h = NULL;
+ unsigned short port = data->set.localport; /* use this port number, 0 for
+ "random" */
+ /* how many port numbers to try to bind to, increasing one at a time */
+ int portnum = data->set.localportrange;
+ const char *dev = data->set.str[STRING_DEVICE];
+ int error;
+#ifdef IP_BIND_ADDRESS_NO_PORT
+ int on = 1;
+#endif
+#ifndef ENABLE_IPV6
+ (void)scope;
+#endif
+
+ /*************************************************************
+ * Select device to bind socket to
+ *************************************************************/
+ if(!dev && !port)
+ /* no local kind of binding was requested */
+ return CURLE_OK;
+
+ memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
+
+ if(dev && (strlen(dev)<255) ) {
+ char myhost[256] = "";
+ int done = 0; /* -1 for error, 1 for address found */
+ bool is_interface = FALSE;
+ bool is_host = FALSE;
+ static const char *if_prefix = "if!";
+ static const char *host_prefix = "host!";
+
+ if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) {
+ dev += strlen(if_prefix);
+ is_interface = TRUE;
+ }
+ else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) {
+ dev += strlen(host_prefix);
+ is_host = TRUE;
+ }
+
+ /* interface */
+ if(!is_host) {
+#ifdef SO_BINDTODEVICE
+ /* I am not sure any other OSs than Linux that provide this feature,
+ * and at the least I cannot test. --Ben
+ *
+ * This feature allows one to tightly bind the local socket to a
+ * particular interface. This will force even requests to other
+ * local interfaces to go out the external interface.
+ *
+ *
+ * Only bind to the interface when specified as interface, not just
+ * as a hostname or ip address.
+ *
+ * interface might be a VRF, eg: vrf-blue, which means it cannot be
+ * converted to an IP address and would fail Curl_if2ip. Simply try
+ * to use it straight away.
+ */
+ if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
+ dev, (curl_socklen_t)strlen(dev) + 1) == 0) {
+ /* This is typically "errno 1, error: Operation not permitted" if
+ * you're not running as root or another suitable privileged
+ * user.
+ * If it succeeds it means the parameter was a valid interface and
+ * not an IP address. Return immediately.
+ */
+ return CURLE_OK;
+ }
+#endif
+
+ switch(Curl_if2ip(af,
+#ifdef ENABLE_IPV6
+ scope, conn->scope_id,
+#endif
+ dev, myhost, sizeof(myhost))) {
+ case IF2IP_NOT_FOUND:
+ if(is_interface) {
+ /* Do not fall back to treating it as a host name */
+ failf(data, "Couldn't bind to interface '%s'", dev);
+ return CURLE_INTERFACE_FAILED;
+ }
+ break;
+ case IF2IP_AF_NOT_SUPPORTED:
+ /* Signal the caller to try another address family if available */
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ case IF2IP_FOUND:
+ is_interface = TRUE;
+ /*
+ * We now have the numerical IP address in the 'myhost' buffer
+ */
+ infof(data, "Local Interface %s is ip %s using address family %i",
+ dev, myhost, af);
+ done = 1;
+ break;
+ }
+ }
+ if(!is_interface) {
+ /*
+ * This was not an interface, resolve the name as a host name
+ * or IP number
+ *
+ * Temporarily force name resolution to use only the address type
+ * of the connection. The resolve functions should really be changed
+ * to take a type parameter instead.
+ */
+ unsigned char ipver = conn->ip_version;
+ int rc;
+
+ if(af == AF_INET)
+ conn->ip_version = CURL_IPRESOLVE_V4;
+#ifdef ENABLE_IPV6
+ else if(af == AF_INET6)
+ conn->ip_version = CURL_IPRESOLVE_V6;
+#endif
+
+ rc = Curl_resolv(data, dev, 0, FALSE, &h);
+ if(rc == CURLRESOLV_PENDING)
+ (void)Curl_resolver_wait_resolv(data, &h);
+ conn->ip_version = ipver;
+
+ if(h) {
+ /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
+ Curl_printable_address(h->addr, myhost, sizeof(myhost));
+ infof(data, "Name '%s' family %i resolved to '%s' family %i",
+ dev, af, myhost, h->addr->ai_family);
+ Curl_resolv_unlock(data, h);
+ if(af != h->addr->ai_family) {
+ /* bad IP version combo, signal the caller to try another address
+ family if available */
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
+ done = 1;
+ }
+ else {
+ /*
+ * provided dev was no interface (or interfaces are not supported
+ * e.g. solaris) no ip address and no domain we fail here
+ */
+ done = -1;
+ }
+ }
+
+ if(done > 0) {
+#ifdef ENABLE_IPV6
+ /* IPv6 address */
+ if(af == AF_INET6) {
+#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
+ char *scope_ptr = strchr(myhost, '%');
+ if(scope_ptr)
+ *(scope_ptr++) = '\0';
+#endif
+ if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
+ si6->sin6_family = AF_INET6;
+ si6->sin6_port = htons(port);
+#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
+ if(scope_ptr) {
+ /* The "myhost" string either comes from Curl_if2ip or from
+ Curl_printable_address. The latter returns only numeric scope
+ IDs and the former returns none at all. So the scope ID, if
+ present, is known to be numeric */
+ unsigned long scope_id = strtoul(scope_ptr, NULL, 10);
+ if(scope_id > UINT_MAX)
+ return CURLE_UNSUPPORTED_PROTOCOL;
+
+ si6->sin6_scope_id = (unsigned int)scope_id;
+ }
+#endif
+ }
+ sizeof_sa = sizeof(struct sockaddr_in6);
+ }
+ else
+#endif
+ /* IPv4 address */
+ if((af == AF_INET) &&
+ (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
+ si4->sin_family = AF_INET;
+ si4->sin_port = htons(port);
+ sizeof_sa = sizeof(struct sockaddr_in);
+ }
+ }
+
+ if(done < 1) {
+ /* errorbuf is set false so failf will overwrite any message already in
+ the error buffer, so the user receives this error message instead of a
+ generic resolve error. */
+ data->state.errorbuf = FALSE;
+ failf(data, "Couldn't bind to '%s'", dev);
+ return CURLE_INTERFACE_FAILED;
+ }
+ }
+ else {
+ /* no device was given, prepare sa to match af's needs */
+#ifdef ENABLE_IPV6
+ if(af == AF_INET6) {
+ si6->sin6_family = AF_INET6;
+ si6->sin6_port = htons(port);
+ sizeof_sa = sizeof(struct sockaddr_in6);
+ }
+ else
+#endif
+ if(af == AF_INET) {
+ si4->sin_family = AF_INET;
+ si4->sin_port = htons(port);
+ sizeof_sa = sizeof(struct sockaddr_in);
+ }
+ }
+#ifdef IP_BIND_ADDRESS_NO_PORT
+ (void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on));
+#endif
+ for(;;) {
+ if(bind(sockfd, sock, sizeof_sa) >= 0) {
+ /* we succeeded to bind */
+ struct Curl_sockaddr_storage add;
+ curl_socklen_t size = sizeof(add);
+ memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
+ if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
+ char buffer[STRERROR_LEN];
+ data->state.os_errno = error = SOCKERRNO;
+ failf(data, "getsockname() failed with errno %d: %s",
+ error, Curl_strerror(error, buffer, sizeof(buffer)));
+ return CURLE_INTERFACE_FAILED;
+ }
+ infof(data, "Local port: %hu", port);
+ conn->bits.bound = TRUE;
+ return CURLE_OK;
+ }
+
+ if(--portnum > 0) {
+ port++; /* try next port */
+ if(port == 0)
+ break;
+ infof(data, "Bind to local port %hu failed, trying next", port - 1);
+ /* We re-use/clobber the port variable here below */
+ if(sock->sa_family == AF_INET)
+ si4->sin_port = ntohs(port);
+#ifdef ENABLE_IPV6
+ else
+ si6->sin6_port = ntohs(port);
+#endif
+ }
+ else
+ break;
+ }
+ {
+ char buffer[STRERROR_LEN];
+ data->state.os_errno = error = SOCKERRNO;
+ failf(data, "bind failed with errno %d: %s",
+ error, Curl_strerror(error, buffer, sizeof(buffer)));
+ }
+
+ return CURLE_INTERFACE_FAILED;
+}
+
+/*
+ * verifyconnect() returns TRUE if the connect really has happened.
+ */
+static bool verifyconnect(curl_socket_t sockfd, int *error)
+{
+ bool rc = TRUE;
+#ifdef SO_ERROR
+ int err = 0;
+ curl_socklen_t errSize = sizeof(err);
+
+#ifdef WIN32
+ /*
+ * In October 2003 we effectively nullified this function on Windows due to
+ * problems with it using all CPU in multi-threaded cases.
+ *
+ * In May 2004, we bring it back to offer more info back on connect failures.
+ * Gisle Vanem could reproduce the former problems with this function, but
+ * could avoid them by adding this SleepEx() call below:
+ *
+ * "I don't have Rational Quantify, but the hint from his post was
+ * ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe
+ * just Sleep(0) would be enough?) would release whatever
+ * mutex/critical-section the ntdll call is waiting on.
+ *
+ * Someone got to verify this on Win-NT 4.0, 2000."
+ */
+
+#ifdef _WIN32_WCE
+ Sleep(0);
+#else
+ SleepEx(0, FALSE);
+#endif
+
+#endif
+
+ if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
+ err = SOCKERRNO;
+#ifdef _WIN32_WCE
+ /* Old WinCE versions don't support SO_ERROR */
+ if(WSAENOPROTOOPT == err) {
+ SET_SOCKERRNO(0);
+ err = 0;
+ }
+#endif
+#if defined(EBADIOCTL) && defined(__minix)
+ /* Minix 3.1.x doesn't support getsockopt on UDP sockets */
+ if(EBADIOCTL == err) {
+ SET_SOCKERRNO(0);
+ err = 0;
+ }
+#endif
+ if((0 == err) || (EISCONN == err))
+ /* we are connected, awesome! */
+ rc = TRUE;
+ else
+ /* This wasn't a successful connect */
+ rc = FALSE;
+ if(error)
+ *error = err;
+#else
+ (void)sockfd;
+ if(error)
+ *error = SOCKERRNO;
+#endif
+ return rc;
+}
+
+/* update tempaddr[tempindex] (to the next entry), makes sure to stick
+ to the correct family */
+static struct Curl_addrinfo *ainext(struct connectdata *conn,
+ int tempindex,
+ bool next) /* use next entry? */
+{
+ struct Curl_addrinfo *ai = conn->tempaddr[tempindex];
+ if(ai && next)
+ ai = ai->ai_next;
+ while(ai && (ai->ai_family != conn->tempfamily[tempindex]))
+ ai = ai->ai_next;
+ conn->tempaddr[tempindex] = ai;
+ return ai;
+}
+
+/* Used within the multi interface. Try next IP address, returns error if no
+ more address exists or error */
+static CURLcode trynextip(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ int tempindex)
+{
+ CURLcode result = CURLE_COULDNT_CONNECT;
+
+ /* First clean up after the failed socket.
+ Don't close it yet to ensure that the next IP's socket gets a different
+ file descriptor, which can prevent bugs when the curl_multi_socket_action
+ interface is used with certain select() replacements such as kqueue. */
+ curl_socket_t fd_to_close = conn->tempsock[tempindex];
+ conn->tempsock[tempindex] = CURL_SOCKET_BAD;
+
+ if(sockindex == FIRSTSOCKET) {
+ struct Curl_addrinfo *ai = conn->tempaddr[tempindex];
+
+ while(ai) {
+ result = singleipconnect(data, conn, ai, tempindex);
+ if(result == CURLE_COULDNT_CONNECT) {
+ ai = ainext(conn, tempindex, TRUE);
+ continue;
+ }
+ break;
+ }
+ }
+
+ if(fd_to_close != CURL_SOCKET_BAD)
+ Curl_closesocket(data, conn, fd_to_close);
+
+ return result;
+}
+
+/* Copies connection info into the transfer handle to make it available when
+ the transfer handle is no longer associated with the connection. */
+void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn,
+ char *local_ip, int local_port)
+{
+ memcpy(data->info.conn_primary_ip, conn->primary_ip, MAX_IPADR_LEN);
+ if(local_ip && local_ip[0])
+ memcpy(data->info.conn_local_ip, local_ip, MAX_IPADR_LEN);
+ else
+ data->info.conn_local_ip[0] = 0;
+ data->info.conn_scheme = conn->handler->scheme;
+ /* conn_protocol can only provide "old" protocols */
+ data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK;
+ data->info.conn_primary_port = conn->port;
+ data->info.conn_remote_port = conn->remote_port;
+ data->info.conn_local_port = local_port;
+}
+
+/* retrieves ip address and port from a sockaddr structure.
+ note it calls Curl_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 ENABLE_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(Curl_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 ENABLE_IPV6
+ case AF_INET6:
+ si6 = (struct sockaddr_in6 *)(void *) sa;
+ if(Curl_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;
+ msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
+ }
+ else
+ addr[0] = 0; /* socket with no name */
+ *port = 0;
+ return TRUE;
+#endif
+ default:
+ break;
+ }
+
+ addr[0] = '\0';
+ *port = 0;
+ errno = EAFNOSUPPORT;
+ return FALSE;
+}
+
+/* retrieves the start/end point information of a socket of an established
+ connection */
+void Curl_conninfo_remote(struct Curl_easy *data,
+ struct connectdata *conn, curl_socket_t sockfd)
+{
+#ifdef HAVE_GETPEERNAME
+ char buffer[STRERROR_LEN];
+ struct Curl_sockaddr_storage ssrem;
+ curl_socklen_t plen;
+ int port;
+ plen = sizeof(struct Curl_sockaddr_storage);
+ memset(&ssrem, 0, sizeof(ssrem));
+ if(getpeername(sockfd, (struct sockaddr*) &ssrem, &plen)) {
+ int error = SOCKERRNO;
+ failf(data, "getpeername() failed with errno %d: %s",
+ error, Curl_strerror(error, buffer, sizeof(buffer)));
+ return;
+ }
+ if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
+ conn->primary_ip, &port)) {
+ failf(data, "ssrem inet_ntop() failed with errno %d: %s",
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+ return;
+ }
+#else
+ (void)data;
+ (void)conn;
+ (void)sockfd;
+#endif
+}
+
+/* retrieves the start/end point information of a socket of an established
+ connection */
+void Curl_conninfo_local(struct Curl_easy *data, curl_socket_t sockfd,
+ char *local_ip, int *local_port)
+{
+#ifdef HAVE_GETSOCKNAME
+ char buffer[STRERROR_LEN];
+ struct Curl_sockaddr_storage ssloc;
+ curl_socklen_t slen;
+ slen = sizeof(struct Curl_sockaddr_storage);
+ memset(&ssloc, 0, sizeof(ssloc));
+ if(getsockname(sockfd, (struct sockaddr*) &ssloc, &slen)) {
+ int error = SOCKERRNO;
+ failf(data, "getsockname() failed with errno %d: %s",
+ error, Curl_strerror(error, buffer, sizeof(buffer)));
+ return;
+ }
+ if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
+ local_ip, local_port)) {
+ failf(data, "ssloc inet_ntop() failed with errno %d: %s",
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+ return;
+ }
+#else
+ (void)data;
+ (void)sockfd;
+ (void)local_ip;
+ (void)local_port;
+#endif
+}
+
+/* retrieves the start/end point information of a socket of an established
+ connection */
+void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t sockfd)
+{
+ /* 'local_ip' and 'local_port' get filled with local's numerical
+ ip address and port number whenever an outgoing connection is
+ **established** from the primary socket to a remote address. */
+ char local_ip[MAX_IPADR_LEN] = "";
+ int local_port = -1;
+
+ if(!conn->bits.reuse &&
+ (conn->transport != TRNSPRT_TCP || !conn->bits.tcp_fastopen))
+ Curl_conninfo_remote(data, conn, sockfd);
+ Curl_conninfo_local(data, sockfd, local_ip, &local_port);
+
+ /* persist connection info in session handle */
+ Curl_persistconninfo(data, conn, local_ip, local_port);
+}
+
+/*
+ * post_connect() is called after a successful connect to the peer
+ */
+static void post_connect(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ Curl_updateconninfo(data, conn, conn->sock[sockindex]);
+ Curl_verboseconnect(data, conn);
+ data->info.numconnects++; /* to track the number of connections made */
+}
+
+/*
+ * is_connected() checks if the socket has connected.
+ */
+static CURLcode is_connected(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ bool *connected)
+{
+ CURLcode result = CURLE_OK;
+ timediff_t allow;
+ int error = 0;
+ struct curltime now;
+ int rc = 0;
+ int i;
+
+ DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET);
+
+ *connected = FALSE; /* a very negative world view is best */
+
+ now = Curl_now();
+
+ /* Check if any of the conn->tempsock we use for establishing connections
+ * succeeded and, if so, close any ongoing other ones.
+ * Transfer the successful conn->tempsock to conn->sock[sockindex]
+ * and set conn->tempsock to CURL_SOCKET_BAD.
+ * If transport is QUIC, we need to shutdown the ongoing 'other'
+ * connect attempts in a QUIC appropriate way. */
+ for(i = 0; i<2; i++) {
+ const int other = i ^ 1;
+ if(conn->tempsock[i] == CURL_SOCKET_BAD)
+ continue;
+ error = 0;
+#ifdef ENABLE_QUIC
+ if(conn->transport == TRNSPRT_QUIC) {
+ result = Curl_quic_is_connected(data, conn, i, connected);
+ if(!result && *connected) {
+ /* use this socket from now on */
+ conn->sock[sockindex] = conn->tempsock[i];
+ conn->ip_addr = conn->tempaddr[i];
+ conn->tempsock[i] = CURL_SOCKET_BAD;
+ post_connect(data, conn, sockindex);
+ connkeep(conn, "HTTP/3 default");
+ if(conn->tempsock[other] != CURL_SOCKET_BAD)
+ Curl_quic_disconnect(data, conn, other);
+ return CURLE_OK;
+ }
+ /* When a QUIC connect attempt fails, the better error explanation is in
+ 'result' and not in errno */
+ if(result) {
+ conn->tempsock[i] = CURL_SOCKET_BAD;
+ error = SOCKERRNO;
+ }
+ }
+ else
+#endif
+ {
+#ifdef mpeix
+ /* Call this function once now, and ignore the results. We do this to
+ "clear" the error state on the socket so that we can later read it
+ reliably. This is reported necessary on the MPE/iX operating
+ system. */
+ (void)verifyconnect(conn->tempsock[i], NULL);
+#endif
+
+ /* check socket for connect */
+ rc = SOCKET_WRITABLE(conn->tempsock[i], 0);
+ }
+
+ if(rc == 0) { /* no connection yet */
+ if(Curl_timediff(now, conn->connecttime) >=
+ conn->timeoutms_per_addr[i]) {
+ infof(data, "After %" CURL_FORMAT_TIMEDIFF_T
+ "ms connect time, move on!", conn->timeoutms_per_addr[i]);
+ error = ETIMEDOUT;
+ }
+
+ /* should we try another protocol family? */
+ if(i == 0 && !conn->bits.parallel_connect &&
+ (Curl_timediff(now, conn->connecttime) >=
+ data->set.happy_eyeballs_timeout)) {
+ conn->bits.parallel_connect = TRUE; /* starting now */
+ trynextip(data, conn, sockindex, 1);
+ }
+ }
+ else if(rc == CURL_CSELECT_OUT || conn->bits.tcp_fastopen) {
+ if(verifyconnect(conn->tempsock[i], &error)) {
+ /* we are connected with TCP, awesome! */
+
+ /* use this socket from now on */
+ conn->sock[sockindex] = conn->tempsock[i];
+ conn->ip_addr = conn->tempaddr[i];
+ conn->tempsock[i] = CURL_SOCKET_BAD;
+#ifdef ENABLE_IPV6
+ conn->bits.ipv6 = (conn->ip_addr->ai_family == AF_INET6)?TRUE:FALSE;
+#endif
+
+ /* close the other socket, if open */
+ if(conn->tempsock[other] != CURL_SOCKET_BAD) {
+ Curl_closesocket(data, conn, conn->tempsock[other]);
+ conn->tempsock[other] = CURL_SOCKET_BAD;
+ }
+
+ *connected = TRUE;
+ return CURLE_OK;
+ }
+ }
+ else if(rc & CURL_CSELECT_ERR) {
+ (void)verifyconnect(conn->tempsock[i], &error);
+ }
+
+ /*
+ * The connection failed here, we should attempt to connect to the "next
+ * address" for the given host. But first remember the latest error.
+ */
+ if(error) {
+ data->state.os_errno = error;
+ SET_SOCKERRNO(error);
+ if(conn->tempaddr[i]) {
+ CURLcode status;
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ char ipaddress[MAX_IPADR_LEN];
+ char buffer[STRERROR_LEN];
+ Curl_printable_address(conn->tempaddr[i], ipaddress,
+ sizeof(ipaddress));
+#ifdef ENABLE_QUIC
+ if(conn->transport == TRNSPRT_QUIC) {
+ infof(data, "connect to %s port %u failed: %s",
+ ipaddress, conn->port, curl_easy_strerror(result));
+ }
+ else
+#endif
+ infof(data, "connect to %s port %u failed: %s",
+ ipaddress, conn->port,
+ Curl_strerror(error, buffer, sizeof(buffer)));
+#endif
+
+ allow = Curl_timeleft(data, &now, TRUE);
+ conn->timeoutms_per_addr[i] = conn->tempaddr[i]->ai_next == NULL ?
+ allow : allow / 2;
+ ainext(conn, i, TRUE);
+ status = trynextip(data, conn, sockindex, i);
+ if((status != CURLE_COULDNT_CONNECT) ||
+ conn->tempsock[other] == CURL_SOCKET_BAD) {
+ /* the last attempt failed and no other sockets remain open */
+ if(!result)
+ result = status;
+ }
+ }
+ }
+ }
+
+ /*
+ * Now that we've checked whether we are connected, check whether we've
+ * already timed out.
+ *
+ * First figure out how long time we have left to connect */
+
+ allow = Curl_timeleft(data, &now, TRUE);
+
+ if(allow < 0) {
+ /* time-out, bail out, go home */
+ failf(data, "Connection timeout after %ld ms",
+ Curl_timediff(now, data->progress.t_startsingle));
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
+ if(result &&
+ (conn->tempsock[0] == CURL_SOCKET_BAD) &&
+ (conn->tempsock[1] == CURL_SOCKET_BAD)) {
+ /* no more addresses to try */
+ const char *hostname;
+ CURLcode failreason = result;
+
+ /* if the first address family runs out of addresses to try before the
+ happy eyeball timeout, go ahead and try the next family now */
+ result = trynextip(data, conn, sockindex, 1);
+ if(!result)
+ return result;
+
+ result = failreason;
+
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.socksproxy)
+ hostname = conn->socks_proxy.host.name;
+ else if(conn->bits.httpproxy)
+ hostname = conn->http_proxy.host.name;
+ else
+#endif
+ if(conn->bits.conn_to_host)
+ hostname = conn->conn_to_host.name;
+ else
+ hostname = conn->host.name;
+
+ failf(data, "Failed to connect to %s port %u after "
+ "%" CURL_FORMAT_TIMEDIFF_T " ms: %s",
+ hostname, conn->port,
+ Curl_timediff(now, data->progress.t_startsingle),
+ curl_easy_strerror(result));
+
+ Curl_quic_disconnect(data, conn, 0);
+ Curl_quic_disconnect(data, conn, 1);
+
+#ifdef WSAETIMEDOUT
+ if(WSAETIMEDOUT == data->state.os_errno)
+ result = CURLE_OPERATION_TIMEDOUT;
+#elif defined(ETIMEDOUT)
+ if(ETIMEDOUT == data->state.os_errno)
+ result = CURLE_OPERATION_TIMEDOUT;
+#endif
+ }
+ else
+ result = CURLE_OK; /* still trying */
+
+ return result;
+}
+
+static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd)
+{
+#if defined(TCP_NODELAY)
+ curl_socklen_t onoff = (curl_socklen_t) 1;
+ int level = IPPROTO_TCP;
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ char buffer[STRERROR_LEN];
+#else
+ (void) data;
+#endif
+
+ if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
+ sizeof(onoff)) < 0)
+ infof(data, "Could not set TCP_NODELAY: %s",
+ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+#else
+ (void)data;
+ (void)sockfd;
+#endif
+}
+
+#ifdef SO_NOSIGPIPE
+/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
+ sending data to a dead peer (instead of relying on the 4th argument to send
+ being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
+ systems? */
+static void nosigpipe(struct Curl_easy *data,
+ curl_socket_t sockfd)
+{
+ int onoff = 1;
+ if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
+ sizeof(onoff)) < 0) {
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ char buffer[STRERROR_LEN];
+ infof(data, "Could not set SO_NOSIGPIPE: %s",
+ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+#endif
+ }
+}
+#else
+#define nosigpipe(x,y) Curl_nop_stmt
+#endif
+
+#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://support.microsoft.com/kb/823764
+
+ Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
+ Buffer Size
+
+ The problem described in this knowledge-base is applied only to pre-Vista
+ Windows. Following function trying to detect OS version and skips
+ SO_SNDBUF adjustment for Windows Vista and above.
+*/
+#define DETECT_OS_NONE 0
+#define DETECT_OS_PREVISTA 1
+#define DETECT_OS_VISTA_OR_LATER 2
+
+void Curl_sndbufset(curl_socket_t sockfd)
+{
+ int val = CURL_MAX_WRITE_SIZE + 32;
+ int curval = 0;
+ int curlen = sizeof(curval);
+
+ static int detectOsState = DETECT_OS_NONE;
+
+ if(detectOsState == DETECT_OS_NONE) {
+ if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL))
+ detectOsState = DETECT_OS_VISTA_OR_LATER;
+ else
+ detectOsState = DETECT_OS_PREVISTA;
+ }
+
+ if(detectOsState == DETECT_OS_VISTA_OR_LATER)
+ return;
+
+ if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
+ if(curval > val)
+ return;
+
+ setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
+}
+#endif
+
+/*
+ * singleipconnect()
+ *
+ * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to
+ * CURL_SOCKET_BAD. Other errors will however return proper errors.
+ *
+ * singleipconnect() connects to the given IP only, and it may return without
+ * having connected.
+ */
+static CURLcode singleipconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai,
+ int tempindex)
+{
+ struct Curl_sockaddr_ex addr;
+ int rc = -1;
+ int error = 0;
+ bool isconnected = FALSE;
+ curl_socket_t sockfd;
+ CURLcode result;
+ char ipaddress[MAX_IPADR_LEN];
+ int port;
+ bool is_tcp;
+#ifdef TCP_FASTOPEN_CONNECT
+ int optval = 1;
+#endif
+ const char *ipmsg;
+ char buffer[STRERROR_LEN];
+ curl_socket_t *sockp = &conn->tempsock[tempindex];
+ *sockp = CURL_SOCKET_BAD;
+
+ result = Curl_socket(data, ai, &addr, &sockfd);
+ if(result)
+ return result;
+
+ /* store remote address and port used in this connection attempt */
+ if(!Curl_addr2string(&addr.sa_addr, addr.addrlen,
+ ipaddress, &port)) {
+ /* malformed address or bug in inet_ntop, try next address */
+ failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+ Curl_closesocket(data, conn, sockfd);
+ return CURLE_OK;
+ }
+#ifdef ENABLE_IPV6
+ if(addr.family == AF_INET6)
+ ipmsg = " Trying [%s]:%d...";
+ else
+#endif
+ ipmsg = " Trying %s:%d...";
+ infof(data, ipmsg, ipaddress, port);
+
+#ifdef ENABLE_IPV6
+ is_tcp = (addr.family == AF_INET || addr.family == AF_INET6) &&
+ addr.socktype == SOCK_STREAM;
+#else
+ is_tcp = (addr.family == AF_INET) && addr.socktype == SOCK_STREAM;
+#endif
+ if(is_tcp && data->set.tcp_nodelay)
+ tcpnodelay(data, sockfd);
+
+ nosigpipe(data, sockfd);
+
+ Curl_sndbufset(sockfd);
+
+ if(is_tcp && data->set.tcp_keepalive)
+ tcpkeepalive(data, sockfd);
+
+ if(data->set.fsockopt) {
+ /* activate callback for setting socket options */
+ Curl_set_in_callback(data, true);
+ error = data->set.fsockopt(data->set.sockopt_client,
+ sockfd,
+ CURLSOCKTYPE_IPCXN);
+ Curl_set_in_callback(data, false);
+
+ if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
+ isconnected = TRUE;
+ else if(error) {
+ Curl_closesocket(data, conn, sockfd); /* close the socket and bail out */
+ return CURLE_ABORTED_BY_CALLBACK;
+ }
+ }
+
+ /* possibly bind the local end to an IP, interface or port */
+ if(addr.family == AF_INET
+#ifdef ENABLE_IPV6
+ || addr.family == AF_INET6
+#endif
+ ) {
+ result = bindlocal(data, conn, sockfd, addr.family,
+ Curl_ipv6_scope(&addr.sa_addr));
+ if(result) {
+ Curl_closesocket(data, conn, sockfd); /* close socket and bail out */
+ if(result == CURLE_UNSUPPORTED_PROTOCOL) {
+ /* The address family is not supported on this interface.
+ We can continue trying addresses */
+ return CURLE_COULDNT_CONNECT;
+ }
+ return result;
+ }
+ }
+
+ /* set socket non-blocking */
+ (void)curlx_nonblock(sockfd, TRUE);
+
+ conn->connecttime = Curl_now();
+ if(conn->num_addr > 1) {
+ Curl_expire(data, conn->timeoutms_per_addr[0], EXPIRE_DNS_PER_NAME);
+ Curl_expire(data, conn->timeoutms_per_addr[1], EXPIRE_DNS_PER_NAME2);
+ }
+
+ /* Connect TCP and QUIC sockets */
+ if(!isconnected && (conn->transport != TRNSPRT_UDP)) {
+ if(conn->bits.tcp_fastopen) {
+#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
+# if defined(HAVE_BUILTIN_AVAILABLE)
+ /* while connectx function is available since macOS 10.11 / iOS 9,
+ it did not have the interface declared correctly until
+ Xcode 9 / macOS SDK 10.13 */
+ if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) {
+ sa_endpoints_t endpoints;
+ endpoints.sae_srcif = 0;
+ endpoints.sae_srcaddr = NULL;
+ endpoints.sae_srcaddrlen = 0;
+ endpoints.sae_dstaddr = &addr.sa_addr;
+ endpoints.sae_dstaddrlen = addr.addrlen;
+
+ rc = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY,
+ CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
+ NULL, 0, NULL, NULL);
+ }
+ else {
+ rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
+ }
+# else
+ rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
+# endif /* HAVE_BUILTIN_AVAILABLE */
+#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
+ if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
+ (void *)&optval, sizeof(optval)) < 0)
+ infof(data, "Failed to enable TCP Fast Open on fd %d", sockfd);
+
+ rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
+#elif defined(MSG_FASTOPEN) /* old Linux */
+ if(conn->given->flags & PROTOPT_SSL)
+ rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
+ else
+ rc = 0; /* Do nothing */
+#endif
+ }
+ else {
+ rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
+ }
+
+ if(-1 == rc)
+ error = SOCKERRNO;
+#ifdef ENABLE_QUIC
+ else if(conn->transport == TRNSPRT_QUIC) {
+ /* pass in 'sockfd' separately since it hasn't been put into the
+ tempsock array at this point */
+ result = Curl_quic_connect(data, conn, sockfd, tempindex,
+ &addr.sa_addr, addr.addrlen);
+ if(result)
+ error = SOCKERRNO;
+ }
+#endif
+ }
+ else {
+ *sockp = sockfd;
+ return CURLE_OK;
+ }
+
+ if(-1 == rc) {
+ switch(error) {
+ case EINPROGRESS:
+ case EWOULDBLOCK:
+#if defined(EAGAIN)
+#if (EAGAIN) != (EWOULDBLOCK)
+ /* On some platforms EAGAIN and EWOULDBLOCK are the
+ * same value, and on others they are different, hence
+ * the odd #if
+ */
+ case EAGAIN:
+#endif
+#endif
+ result = CURLE_OK;
+ break;
+
+ default:
+ /* unknown error, fallthrough and try another address! */
+ infof(data, "Immediate connect fail for %s: %s",
+ ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
+ data->state.os_errno = error;
+
+ /* connect failed */
+ Curl_closesocket(data, conn, sockfd);
+ result = CURLE_COULDNT_CONNECT;
+ }
+ }
+
+ if(!result)
+ *sockp = sockfd;
+
+ return result;
+}
+
+/*
+ * TCP connect to the given host with timeout, proxy or remote doesn't matter.
+ * There might be more than one IP address to try out. Fill in the passed
+ * pointer with the connected socket.
+ */
+
+CURLcode Curl_connecthost(struct Curl_easy *data,
+ struct connectdata *conn, /* context */
+ const struct Curl_dns_entry *remotehost)
+{
+ CURLcode result = CURLE_COULDNT_CONNECT;
+ int i;
+ timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+ if(timeout_ms < 0) {
+ /* a precaution, no need to continue if time already is up */
+ failf(data, "Connection time-out");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
+ conn->num_addr = Curl_num_addresses(remotehost->addr);
+ conn->tempaddr[0] = conn->tempaddr[1] = remotehost->addr;
+ conn->tempsock[0] = conn->tempsock[1] = CURL_SOCKET_BAD;
+
+ /* Max time for the next connection attempt */
+ conn->timeoutms_per_addr[0] =
+ conn->tempaddr[0]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
+ conn->timeoutms_per_addr[1] =
+ conn->tempaddr[1]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
+
+ if(conn->ip_version == CURL_IPRESOLVE_WHATEVER) {
+ /* any IP version is allowed */
+ conn->tempfamily[0] = conn->tempaddr[0]?
+ conn->tempaddr[0]->ai_family:0;
+#ifdef ENABLE_IPV6
+ conn->tempfamily[1] = conn->tempfamily[0] == AF_INET6 ?
+ AF_INET : AF_INET6;
+#else
+ conn->tempfamily[1] = AF_UNSPEC;
+#endif
+ }
+ else {
+ /* only one IP version is allowed */
+ conn->tempfamily[0] = (conn->ip_version == CURL_IPRESOLVE_V4) ?
+ AF_INET :
+#ifdef ENABLE_IPV6
+ AF_INET6;
+#else
+ AF_UNSPEC;
+#endif
+ conn->tempfamily[1] = AF_UNSPEC;
+
+ ainext(conn, 0, FALSE); /* find first address of the right type */
+ }
+
+ ainext(conn, 1, FALSE); /* assigns conn->tempaddr[1] accordingly */
+
+ DEBUGF(infof(data, "family0 == %s, family1 == %s",
+ conn->tempfamily[0] == AF_INET ? "v4" : "v6",
+ conn->tempfamily[1] == AF_INET ? "v4" : "v6"));
+
+ /* get through the list in family order in case of quick failures */
+ for(i = 0; (i < 2) && result; i++) {
+ while(conn->tempaddr[i]) {
+ result = singleipconnect(data, conn, conn->tempaddr[i], i);
+ if(!result)
+ break;
+ ainext(conn, i, TRUE);
+ }
+ }
+ if(result)
+ return result;
+
+ Curl_expire(data, data->set.happy_eyeballs_timeout,
+ EXPIRE_HAPPY_EYEBALLS);
+
+ return CURLE_OK;
+}
+
+struct connfind {
+ long id_tofind;
+ struct connectdata *found;
+};
+
+static int conn_is_conn(struct Curl_easy *data,
+ struct connectdata *conn, void *param)
+{
+ struct connfind *f = (struct connfind *)param;
+ (void)data;
+ if(conn->connection_id == f->id_tofind) {
+ f->found = conn;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * 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) && (data->multi_easy || data->multi)) {
+ struct connectdata *c;
+ struct connfind find;
+ find.id_tofind = data->state.lastconnect_id;
+ find.found = NULL;
+
+ Curl_conncache_foreach(data,
+ data->share && (data->share->specifier
+ & (1<< CURL_LOCK_DATA_CONNECT))?
+ &data->share->conn_cache:
+ data->multi_easy?
+ &data->multi_easy->conn_cache:
+ &data->multi->conn_cache, &find, conn_is_conn);
+
+ if(!find.found) {
+ data->state.lastconnect_id = -1;
+ return CURL_SOCKET_BAD;
+ }
+
+ c = find.found;
+ if(connp)
+ /* only store this if the caller cares for it */
+ *connp = c;
+ return c->sock[FIRSTSOCKET];
+ }
+ return CURL_SOCKET_BAD;
+}
+
+/*
+ * Check if a connection seems to be alive.
+ */
+bool Curl_connalive(struct Curl_easy *data, struct connectdata *conn)
+{
+ (void)data;
+ /* First determine if ssl */
+ if(Curl_conn_is_ssl(data, FIRSTSOCKET)) {
+ /* use the SSL context */
+ if(!Curl_ssl_check_cxn(data, conn))
+ return false; /* FIN received */
+ }
+/* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */
+#ifdef MSG_PEEK
+ else if(conn->sock[FIRSTSOCKET] == CURL_SOCKET_BAD)
+ return false;
+ else {
+ /* use the socket */
+ char buf;
+ if(recv((RECV_TYPE_ARG1)conn->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf,
+ (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) {
+ return false; /* FIN received */
+ }
+ }
+#endif
+ return true;
+}
+
+/*
+ * Close a socket.
+ *
+ * 'conn' can be NULL, beware!
+ */
+int Curl_closesocket(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t sock)
+{
+ if(conn && conn->fclosesocket) {
+ if((sock == conn->sock[SECONDARYSOCKET]) && conn->bits.sock_accepted)
+ /* if this socket matches the second socket, and that was created with
+ accept, then we MUST NOT call the callback but clear the accepted
+ status */
+ conn->bits.sock_accepted = FALSE;
+ else {
+ int rc;
+ Curl_multi_closed(data, sock);
+ Curl_set_in_callback(data, true);
+ rc = conn->fclosesocket(conn->closesocket_client, sock);
+ Curl_set_in_callback(data, false);
+ return rc;
+ }
+ }
+
+ if(conn)
+ /* tell the multi-socket code about this */
+ Curl_multi_closed(data, sock);
+
+ sclose(sock);
+
+ return 0;
+}
+
+/*
+ * Create a socket based on info from 'conn' and 'ai'.
+ *
+ * 'addr' should be a pointer to the correct struct to get data back, or NULL.
+ * 'sockfd' must be a pointer to a socket descriptor.
+ *
+ * If the open socket callback is set, used that!
+ *
+ */
+CURLcode Curl_socket(struct Curl_easy *data,
+ const struct Curl_addrinfo *ai,
+ struct Curl_sockaddr_ex *addr,
+ curl_socket_t *sockfd)
+{
+ struct connectdata *conn = data->conn;
+ struct Curl_sockaddr_ex dummy;
+
+ if(!addr)
+ /* if the caller doesn't want info back, use a local temp copy */
+ addr = &dummy;
+
+ /*
+ * 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.
+ */
+
+ addr->family = ai->ai_family;
+ switch(conn->transport) {
+ case TRNSPRT_TCP:
+ addr->socktype = SOCK_STREAM;
+ addr->protocol = IPPROTO_TCP;
+ break;
+ case TRNSPRT_UNIX:
+ addr->socktype = SOCK_STREAM;
+ addr->protocol = IPPROTO_IP;
+ break;
+ default: /* UDP and QUIC */
+ addr->socktype = SOCK_DGRAM;
+ addr->protocol = IPPROTO_UDP;
+ break;
+ }
+ addr->addrlen = ai->ai_addrlen;
+
+ if(addr->addrlen > sizeof(struct Curl_sockaddr_storage))
+ addr->addrlen = sizeof(struct Curl_sockaddr_storage);
+ memcpy(&addr->sa_addr, ai->ai_addr, addr->addrlen);
+
+ if(data->set.fopensocket) {
+ /*
+ * If the opensocket callback is set, all the destination address
+ * information is passed to the callback. Depending on this information the
+ * callback may opt to abort the connection, this is indicated returning
+ * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
+ * the callback returns a valid socket the destination address information
+ * might have been changed and this 'new' address will actually be used
+ * here to connect.
+ */
+ Curl_set_in_callback(data, true);
+ *sockfd = data->set.fopensocket(data->set.opensocket_client,
+ CURLSOCKTYPE_IPCXN,
+ (struct curl_sockaddr *)addr);
+ Curl_set_in_callback(data, false);
+ }
+ else
+ /* opensocket callback not set, so simply create the socket now */
+ *sockfd = socket(addr->family, addr->socktype, addr->protocol);
+
+ if(*sockfd == CURL_SOCKET_BAD)
+ /* no socket, no connection */
+ return CURLE_COULDNT_CONNECT;
+
+ if(conn->transport == TRNSPRT_QUIC) {
+ /* QUIC sockets need to be nonblocking */
+ (void)curlx_nonblock(*sockfd, TRUE);
+ switch(addr->family) {
+#if defined(__linux__) && defined(IP_MTU_DISCOVER)
+ case AF_INET: {
+ int val = IP_PMTUDISC_DO;
+ (void)setsockopt(*sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &val,
+ sizeof(val));
+ break;
+ }
+#endif
+#if defined(__linux__) && defined(IPV6_MTU_DISCOVER)
+ case AF_INET6: {
+ int val = IPV6_PMTUDISC_DO;
+ (void)setsockopt(*sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val,
+ sizeof(val));
+ break;
+ }
+#endif
+ }
+ }
+
+#if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
+ if(conn->scope_id && (addr->family == AF_INET6)) {
+ struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
+ sa6->sin6_scope_id = conn->scope_id;
+ }
+#endif
+
+ return CURLE_OK;
+}
+
+/*
+ * 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 isn't multiplexed. */
+ /* This function will be called both before and after this connection is
+ associated with a transfer. */
+ bool closeit;
+ DEBUGASSERT(conn);
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ (void)reason; /* useful for debugging */
+#endif
+ closeit = (ctrl == CONNCTRL_CONNECTION) ||
+ ((ctrl == CONNCTRL_STREAM) && !(conn->handler->flags & PROTOPT_STREAM));
+ if((ctrl == CONNCTRL_STREAM) &&
+ (conn->handler->flags & PROTOPT_STREAM))
+ ;
+ 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 {
+ SCFST_INIT,
+ SCFST_WAITING,
+ SCFST_DONE
+} cf_connect_state;
+
+struct socket_cf_ctx {
+ const struct Curl_dns_entry *remotehost;
+ cf_connect_state state;
+};
+
+static int socket_cf_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ struct connectdata *conn = cf->conn;
+ int i, s, rc = GETSOCK_BLANK;
+
+ (void)data;
+ if(cf->connected) {
+ return rc;
+ }
+
+ for(i = s = 0; i<2; i++) {
+ if(conn->tempsock[i] != CURL_SOCKET_BAD) {
+ socks[s] = conn->tempsock[i];
+ rc |= GETSOCK_WRITESOCK(s);
+#ifdef ENABLE_QUIC
+ if(conn->transport == TRNSPRT_QUIC)
+ /* when connecting QUIC, we want to read the socket too */
+ rc |= GETSOCK_READSOCK(s);
+#endif
+ s++;
+ }
+ }
+
+ return rc;
+}
+
+static CURLcode socket_cf_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ struct connectdata *conn = cf->conn;
+ int sockindex = cf->sockindex;
+ struct socket_cf_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ (void)blocking;
+ DEBUGASSERT(ctx);
+ *done = FALSE;
+ switch(ctx->state) {
+ case SCFST_INIT:
+ DEBUGASSERT(CURL_SOCKET_BAD == conn->sock[sockindex]);
+ DEBUGASSERT(!cf->connected);
+ result = Curl_connecthost(data, conn, ctx->remotehost);
+ if(!result)
+ ctx->state = SCFST_WAITING;
+ break;
+ case SCFST_WAITING:
+ result = is_connected(data, conn, sockindex, done);
+ if(!result && *done) {
+ Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */
+ if(Curl_conn_is_ssl(data, FIRSTSOCKET) ||
+ (conn->handler->protocol & PROTO_FAMILY_SSH))
+ Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */
+ post_connect(data, conn, sockindex);
+ ctx->state = SCFST_DONE;
+ cf->connected = TRUE;
+ }
+ break;
+ case SCFST_DONE:
+ *done = TRUE;
+ break;
+ }
+ return result;
+}
+
+static CURLcode socket_cf_setup(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost)
+{
+ struct socket_cf_ctx *ctx = cf->ctx;
+
+ (void)data;
+ DEBUGASSERT(ctx);
+ if(ctx->remotehost != remotehost) {
+ if(ctx->remotehost) {
+ /* switching dns entry? TODO: reset? */
+ }
+ ctx->remotehost = remotehost;
+ }
+ DEBUGF(infof(data, CFMSG(cf, "setup(remotehost=%s)"),
+ cf->conn->hostname_resolve));
+ return CURLE_OK;
+}
+
+static void socket_cf_close(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ int sockindex = cf->sockindex;
+ struct socket_cf_ctx *ctx = cf->ctx;
+
+ DEBUGASSERT(ctx);
+ /* close possibly still open sockets */
+ if(CURL_SOCKET_BAD != cf->conn->sock[sockindex]) {
+ Curl_closesocket(data, cf->conn, cf->conn->sock[sockindex]);
+ cf->conn->sock[sockindex] = CURL_SOCKET_BAD;
+ }
+ if(CURL_SOCKET_BAD != cf->conn->tempsock[sockindex]) {
+ Curl_closesocket(data, cf->conn, cf->conn->tempsock[sockindex]);
+ cf->conn->tempsock[sockindex] = CURL_SOCKET_BAD;
+ }
+ cf->connected = FALSE;
+ ctx->state = SCFST_INIT;
+}
+
+static void socket_cf_get_host(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char **phost,
+ const char **pdisplay_host,
+ int *pport)
+{
+ (void)data;
+ *phost = cf->conn->host.name;
+ *pdisplay_host = cf->conn->host.dispname;
+ *pport = cf->conn->port;
+}
+
+static bool socket_cf_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ int readable;
+ (void)data;
+ DEBUGASSERT(cf);
+
+ readable = SOCKET_READABLE(cf->conn->sock[cf->sockindex], 0);
+ return (readable > 0 && (readable & CURL_CSELECT_IN));
+}
+
+static ssize_t socket_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err)
+{
+ ssize_t nwritten;
+ nwritten = Curl_send_plain(data, cf->sockindex, buf, len, err);
+ return nwritten;
+}
+
+static ssize_t socket_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err)
+{
+ ssize_t nread;
+ nread = Curl_recv_plain(data, cf->sockindex, buf, len, err);
+ return nread;
+}
+
+static void socket_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct socket_cf_ctx *state = cf->ctx;
+
+ (void)data;
+ if(cf->connected) {
+ socket_cf_close(cf, data);
+ }
+ /* release any resources held in state */
+ Curl_safefree(state);
+}
+
+static const struct Curl_cftype cft_socket = {
+ "SOCKET",
+ CF_TYPE_IP_CONNECT,
+ socket_cf_destroy,
+ socket_cf_setup,
+ socket_cf_connect,
+ socket_cf_close,
+ socket_cf_get_host,
+ socket_cf_get_select_socks,
+ socket_cf_data_pending,
+ socket_cf_send,
+ socket_cf_recv,
+ Curl_cf_def_attach_data,
+ Curl_cf_def_detach_data,
+};
+
+CURLcode Curl_conn_socket_set(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ CURLcode result;
+ struct Curl_cfilter *cf = NULL;
+ struct socket_cf_ctx *scf_ctx = NULL;
+
+ /* Need to be first */
+ DEBUGASSERT(conn);
+ DEBUGASSERT(!conn->cfilter[sockindex]);
+ scf_ctx = calloc(sizeof(*scf_ctx), 1);
+ if(!scf_ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ result = Curl_cf_create(&cf, &cft_socket, scf_ctx);
+ if(result)
+ goto out;
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+
+out:
+ if(result) {
+ Curl_safefree(cf);
+ Curl_safefree(scf_ctx);
+ }
+ return result;
+}
+
+static CURLcode socket_accept_cf_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ /* we start accepted, if we ever close, we cannot go on */
+ (void)data;
+ (void)blocking;
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+ return CURLE_FAILED_INIT;
+}
+
+static CURLcode socket_accept_cf_setup(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost)
+{
+ /* we start accepted, if we ever close, we cannot go on */
+ (void)data;
+ (void)remotehost;
+ if(cf->connected) {
+ return CURLE_OK;
+ }
+ return CURLE_FAILED_INIT;
+}
+
+static const struct Curl_cftype cft_socket_accept = {
+ "SOCKET-ACCEPT",
+ CF_TYPE_IP_CONNECT,
+ socket_cf_destroy,
+ socket_accept_cf_setup,
+ socket_accept_cf_connect,
+ socket_cf_close,
+ socket_cf_get_host, /* TODO: not accurate */
+ Curl_cf_def_get_select_socks,
+ socket_cf_data_pending,
+ socket_cf_send,
+ socket_cf_recv,
+ Curl_cf_def_attach_data,
+ Curl_cf_def_detach_data,
+};
+
+CURLcode Curl_conn_socket_accepted_set(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex, curl_socket_t *s)
+{
+ CURLcode result;
+ struct Curl_cfilter *cf = NULL;
+ struct socket_cf_ctx *scf_ctx = NULL;
+
+ cf = conn->cfilter[sockindex];
+ if(cf && cf->cft == &cft_socket_accept) {
+ /* already an accept filter installed, just replace the socket */
+ scf_ctx = cf->ctx;
+ result = CURLE_OK;
+ }
+ else {
+ /* replace any existing */
+ Curl_conn_cf_discard_all(data, conn, sockindex);
+ scf_ctx = calloc(sizeof(*scf_ctx), 1);
+ if(!scf_ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ result = Curl_cf_create(&cf, &cft_socket_accept, scf_ctx);
+ if(result)
+ goto out;
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+ }
+
+ /* close any existing socket and replace */
+ Curl_closesocket(data, conn, conn->sock[sockindex]);
+ conn->sock[sockindex] = *s;
+ conn->bits.sock_accepted = TRUE;
+ cf->connected = TRUE;
+ scf_ctx->state = SCFST_DONE;
+
+out:
+ if(result) {
+ Curl_safefree(cf);
+ Curl_safefree(scf_ctx);
+ }
+ return result;
+}
diff --git a/libs/libcurl/src/connect.h b/libs/libcurl/src/connect.h index 582ff0813d..bf12bd5248 100644 --- a/libs/libcurl/src/connect.h +++ b/libs/libcurl/src/connect.h @@ -1,158 +1,160 @@ -#ifndef HEADER_CURL_CONNECT_H -#define HEADER_CURL_CONNECT_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "nonblock.h" /* for curlx_nonblock(), formerly Curl_nonblock() */ -#include "sockaddr.h" -#include "timeval.h" - -CURLcode Curl_is_connected(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool *connected); - -CURLcode Curl_connecthost(struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_dns_entry *host); - -/* generic function that returns how much time there's 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 */ - -/* - * 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); - -/* - * Check if a connection seems to be alive. - */ -bool Curl_connalive(struct connectdata *conn); - -#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://support.microsoft.com/kb/823764 - - Work-around: Make the Socket Send Buffer Size Larger Than the Program Send - Buffer Size - -*/ -void Curl_sndbufset(curl_socket_t sockfd); -#else -#define Curl_sndbufset(y) Curl_nop_stmt -#endif - -void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t sockfd); -void Curl_conninfo_remote(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t sockfd); -void Curl_conninfo_local(struct Curl_easy *data, curl_socket_t sockfd, - char *local_ip, int *local_port); -void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn, - char *local_ip, int local_port); -int Curl_closesocket(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t sock); - -/* - * 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 addr; - struct Curl_sockaddr_storage buff; - } _sa_ex_u; -}; -#define sa_addr _sa_ex_u.addr - -/* - * 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(struct Curl_easy *data, - const struct Curl_addrinfo *ai, - struct Curl_sockaddr_ex *addr, - curl_socket_t *sockfd); - -/* - * 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 - -bool Curl_conn_data_pending(struct connectdata *conn, int sockindex); - -#endif /* HEADER_CURL_CONNECT_H */ +#ifndef HEADER_CURL_CONNECT_H
+#define HEADER_CURL_CONNECT_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "nonblock.h" /* for curlx_nonblock(), formerly Curl_nonblock() */
+#include "sockaddr.h"
+#include "timeval.h"
+
+CURLcode Curl_connecthost(struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_dns_entry *host);
+
+/* generic function that returns how much time there's 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 */
+
+/*
+ * 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);
+
+/*
+ * Check if a connection seems to be alive.
+ */
+bool Curl_connalive(struct Curl_easy *data, struct connectdata *conn);
+
+#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://support.microsoft.com/kb/823764
+
+ Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
+ Buffer Size
+
+*/
+void Curl_sndbufset(curl_socket_t sockfd);
+#else
+#define Curl_sndbufset(y) Curl_nop_stmt
+#endif
+
+void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t sockfd);
+void Curl_conninfo_remote(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t sockfd);
+void Curl_conninfo_local(struct Curl_easy *data, curl_socket_t sockfd,
+ char *local_ip, int *local_port);
+void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn,
+ char *local_ip, int local_port);
+int Curl_closesocket(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t sock);
+
+/*
+ * 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 addr;
+ struct Curl_sockaddr_storage buff;
+ } _sa_ex_u;
+};
+#define sa_addr _sa_ex_u.addr
+
+/*
+ * 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(struct Curl_easy *data,
+ const struct Curl_addrinfo *ai,
+ struct Curl_sockaddr_ex *addr,
+ curl_socket_t *sockfd);
+
+/*
+ * 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_conn_socket_set(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex);
+
+CURLcode Curl_conn_socket_accepted_set(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ curl_socket_t *s);
+
+#endif /* HEADER_CURL_CONNECT_H */
diff --git a/libs/libcurl/src/content_encoding.c b/libs/libcurl/src/content_encoding.c index bfc13e254d..95c87fd7ba 100644 --- a/libs/libcurl/src/content_encoding.c +++ b/libs/libcurl/src/content_encoding.c @@ -1,1131 +1,1131 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 -#include <brotli/decode.h> -#endif - -#ifdef HAVE_ZSTD -#include <zstd.h> -#endif - -#include "sendf.h" -#include "http.h" -#include "content_encoding.h" -#include "strdup.h" -#include "strcase.h" -#include "curl_memory.h" -#include "memdebug.h" - -#define CONTENT_ENCODING_DEFAULT "identity" - -#ifndef CURL_DISABLE_HTTP - -#define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */ - - -#ifdef HAVE_LIBZ - -/* Comment this out if zlib is always going to be at least ver. 1.2.0.4 - (doing so will reduce code size slightly). */ -#define OLD_ZLIB_SUPPORT 1 - -#define GZIP_MAGIC_0 0x1f -#define GZIP_MAGIC_1 0x8b - -/* gzip flag byte */ -#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ -#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ -#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ -#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ -#define COMMENT 0x10 /* bit 4 set: file comment present */ -#define RESERVED 0xE0 /* bits 5..7: reserved */ - -typedef enum { - ZLIB_UNINIT, /* uninitialized */ - ZLIB_INIT, /* initialized */ - ZLIB_INFLATING, /* inflating started. */ - ZLIB_EXTERNAL_TRAILER, /* reading external trailer */ - ZLIB_GZIP_HEADER, /* reading gzip header */ - ZLIB_GZIP_INFLATING, /* inflating gzip stream */ - ZLIB_INIT_GZIP /* initialized in transparent gzip mode */ -} zlibInitState; - -/* Deflate and gzip writer. */ -struct zlib_writer { - struct contenc_writer super; - zlibInitState zlib_init; /* zlib init state */ - 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_GZIP_HEADER) - Curl_safefree(z->next_in); - - 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 contenc_writer *writer, - zlibInitState started) -{ - struct zlib_writer *zp = (struct zlib_writer *) writer; - z_stream *z = &zp->z; /* zlib state structure */ - uInt nread = z->avail_in; - Bytef *orig_in = z->next_in; - bool done = FALSE; - CURLcode result = CURLE_OK; /* Curl_client_write status */ - char *decomp; /* Put the decompressed data here. */ - - /* Check state. */ - if(zp->zlib_init != ZLIB_INIT && - zp->zlib_init != ZLIB_INFLATING && - zp->zlib_init != ZLIB_INIT_GZIP && - zp->zlib_init != ZLIB_GZIP_INFLATING) - return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); - - /* Dynamically allocate a buffer for decompression because it's uncommonly - large to hold on the stack */ - decomp = malloc(DSIZ); - if(!decomp) - return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); - - /* because the buffer size is fixed, iteratively decompress and transfer to - the client via downstream_write function. */ - while(!done) { - int status; /* zlib status */ - done = TRUE; - - /* (re)set buffer for decompressed output for every iteration */ - z->next_out = (Bytef *) decomp; - z->avail_out = DSIZ; - -#ifdef Z_BLOCK - /* Z_BLOCK is only available in zlib ver. >= 1.2.0.5 */ - status = inflate(z, Z_BLOCK); -#else - /* fallback for zlib ver. < 1.2.0.5 */ - status = inflate(z, Z_SYNC_FLUSH); -#endif - - /* Flush output data if some. */ - if(z->avail_out != DSIZ) { - if(status == Z_OK || status == Z_STREAM_END) { - zp->zlib_init = started; /* Data started. */ - result = Curl_unencode_write(data, writer->downstream, decomp, - DSIZ - 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) { - /* Do not use inflateReset2(): only available since zlib 1.2.3.4. */ - (void) inflateEnd(z); /* don't care about the return code */ - if(inflateInit2(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; - } - } - free(decomp); - - /* We're about to leave this call so the `nread' data bytes won't 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_init_writer(struct Curl_easy *data, - struct contenc_writer *writer) -{ - struct zlib_writer *zp = (struct zlib_writer *) writer; - z_stream *z = &zp->z; /* zlib state structure */ - - if(!writer->downstream) - return CURLE_WRITE_ERROR; - - /* 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_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes) -{ - struct zlib_writer *zp = (struct zlib_writer *) writer; - z_stream *z = &zp->z; /* zlib state structure */ - - /* Set the compressed input when this function is called */ - z->next_in = (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, ZLIB_INFLATING); -} - -static void deflate_close_writer(struct Curl_easy *data, - struct contenc_writer *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 content_encoding deflate_encoding = { - "deflate", - NULL, - deflate_init_writer, - deflate_unencode_write, - deflate_close_writer, - sizeof(struct zlib_writer) -}; - - -/* Gzip handler. */ -static CURLcode gzip_init_writer(struct Curl_easy *data, - struct contenc_writer *writer) -{ - struct zlib_writer *zp = (struct zlib_writer *) writer; - z_stream *z = &zp->z; /* zlib state structure */ - - if(!writer->downstream) - return CURLE_WRITE_ERROR; - - /* Initialize zlib */ - z->zalloc = (alloc_func) zalloc_cb; - z->zfree = (free_func) zfree_cb; - - if(strcmp(zlibVersion(), "1.2.0.4") >= 0) { - /* zlib ver. >= 1.2.0.4 supports transparent gzip decompressing */ - if(inflateInit2(z, MAX_WBITS + 32) != Z_OK) { - return process_zlib_error(data, z); - } - zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */ - } - else { - /* we must parse the gzip header and trailer ourselves */ - if(inflateInit2(z, -MAX_WBITS) != Z_OK) { - return process_zlib_error(data, z); - } - zp->trailerlen = 8; /* A CRC-32 and a 32-bit input size (RFC 1952, 2.2) */ - zp->zlib_init = ZLIB_INIT; /* Initial call state */ - } - - return CURLE_OK; -} - -#ifdef OLD_ZLIB_SUPPORT -/* Skip over the gzip header */ -static enum { - GZIP_OK, - GZIP_BAD, - GZIP_UNDERFLOW -} check_gzip_header(unsigned char const *data, ssize_t len, ssize_t *headerlen) -{ - int method, flags; - const ssize_t totallen = len; - - /* The shortest header is 10 bytes */ - if(len < 10) - return GZIP_UNDERFLOW; - - if((data[0] != GZIP_MAGIC_0) || (data[1] != GZIP_MAGIC_1)) - return GZIP_BAD; - - method = data[2]; - flags = data[3]; - - if(method != Z_DEFLATED || (flags & RESERVED) != 0) { - /* Can't handle this compression method or unknown flag */ - return GZIP_BAD; - } - - /* Skip over time, xflags, OS code and all previous bytes */ - len -= 10; - data += 10; - - if(flags & EXTRA_FIELD) { - ssize_t extra_len; - - if(len < 2) - return GZIP_UNDERFLOW; - - extra_len = (data[1] << 8) | data[0]; - - if(len < (extra_len + 2)) - return GZIP_UNDERFLOW; - - len -= (extra_len + 2); - data += (extra_len + 2); - } - - if(flags & ORIG_NAME) { - /* Skip over NUL-terminated file name */ - while(len && *data) { - --len; - ++data; - } - if(!len || *data) - return GZIP_UNDERFLOW; - - /* Skip over the NUL */ - --len; - ++data; - } - - if(flags & COMMENT) { - /* Skip over NUL-terminated comment */ - while(len && *data) { - --len; - ++data; - } - if(!len || *data) - return GZIP_UNDERFLOW; - - /* Skip over the NUL */ - --len; - } - - if(flags & HEAD_CRC) { - if(len < 2) - return GZIP_UNDERFLOW; - - len -= 2; - } - - *headerlen = totallen - len; - return GZIP_OK; -} -#endif - -static CURLcode gzip_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes) -{ - struct zlib_writer *zp = (struct zlib_writer *) writer; - z_stream *z = &zp->z; /* zlib state structure */ - - if(zp->zlib_init == ZLIB_INIT_GZIP) { - /* Let zlib handle the gzip decompression entirely */ - z->next_in = (Bytef *) buf; - z->avail_in = (uInt) nbytes; - /* Now uncompress the data */ - return inflate_stream(data, writer, ZLIB_INIT_GZIP); - } - -#ifndef OLD_ZLIB_SUPPORT - /* Support for old zlib versions is compiled away and we are running with - an old version, so return an error. */ - return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR); - -#else - /* This next mess is to get around the potential case where there isn't - * enough data passed in to skip over the gzip header. If that happens, we - * malloc a block and copy what we have then wait for the next call. If - * there still isn't enough (this is definitely a worst-case scenario), we - * make the block bigger, copy the next part in and keep waiting. - * - * This is only required with zlib versions < 1.2.0.4 as newer versions - * can handle the gzip header themselves. - */ - - switch(zp->zlib_init) { - /* Skip over gzip header? */ - case ZLIB_INIT: - { - /* Initial call state */ - ssize_t hlen; - - switch(check_gzip_header((unsigned char *) buf, nbytes, &hlen)) { - case GZIP_OK: - z->next_in = (Bytef *) buf + hlen; - z->avail_in = (uInt) (nbytes - hlen); - zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ - break; - - case GZIP_UNDERFLOW: - /* We need more data so we can find the end of the gzip header. It's - * possible that the memory block we malloc here will never be freed if - * the transfer abruptly aborts after this point. Since it's unlikely - * that circumstances will be right for this code path to be followed in - * the first place, and it's even more unlikely for a transfer to fail - * immediately afterwards, it should seldom be a problem. - */ - z->avail_in = (uInt) nbytes; - z->next_in = malloc(z->avail_in); - if(!z->next_in) { - return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); - } - memcpy(z->next_in, buf, z->avail_in); - zp->zlib_init = ZLIB_GZIP_HEADER; /* Need more gzip header data state */ - /* We don't have any data to inflate yet */ - return CURLE_OK; - - case GZIP_BAD: - default: - return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); - } - - } - break; - - case ZLIB_GZIP_HEADER: - { - /* Need more gzip header data state */ - ssize_t hlen; - z->avail_in += (uInt) nbytes; - z->next_in = Curl_saferealloc(z->next_in, z->avail_in); - if(!z->next_in) { - return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY); - } - /* Append the new block of data to the previous one */ - memcpy(z->next_in + z->avail_in - nbytes, buf, nbytes); - - switch(check_gzip_header(z->next_in, z->avail_in, &hlen)) { - case GZIP_OK: - /* This is the zlib stream data */ - free(z->next_in); - /* Don't point into the malloced block since we just freed it */ - z->next_in = (Bytef *) buf + hlen + nbytes - z->avail_in; - z->avail_in = (uInt) (z->avail_in - hlen); - zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */ - break; - - case GZIP_UNDERFLOW: - /* We still don't have any data to inflate! */ - return CURLE_OK; - - case GZIP_BAD: - default: - return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); - } - - } - break; - - case ZLIB_EXTERNAL_TRAILER: - z->next_in = (Bytef *) buf; - z->avail_in = (uInt) nbytes; - return process_trailer(data, zp); - - case ZLIB_GZIP_INFLATING: - default: - /* Inflating stream state */ - z->next_in = (Bytef *) buf; - z->avail_in = (uInt) nbytes; - break; - } - - if(z->avail_in == 0) { - /* We don't have any data to inflate; wait until next time */ - return CURLE_OK; - } - - /* We've parsed the header, now uncompress the data */ - return inflate_stream(data, writer, ZLIB_GZIP_INFLATING); -#endif -} - -static void gzip_close_writer(struct Curl_easy *data, - struct contenc_writer *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 content_encoding gzip_encoding = { - "gzip", - "x-gzip", - gzip_init_writer, - gzip_unencode_write, - gzip_close_writer, - sizeof(struct zlib_writer) -}; - -#endif /* HAVE_LIBZ */ - - -#ifdef HAVE_BROTLI -/* Brotli writer. */ -struct brotli_writer { - struct contenc_writer super; - 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_init_writer(struct Curl_easy *data, - struct contenc_writer *writer) -{ - struct brotli_writer *bp = (struct brotli_writer *) writer; - (void) data; - - if(!writer->downstream) - return CURLE_WRITE_ERROR; - - bp->br = BrotliDecoderCreateInstance(NULL, NULL, NULL); - return bp->br? CURLE_OK: CURLE_OUT_OF_MEMORY; -} - -static CURLcode brotli_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes) -{ - struct brotli_writer *bp = (struct brotli_writer *) writer; - const uint8_t *src = (const uint8_t *) buf; - char *decomp; - uint8_t *dst; - size_t dstleft; - CURLcode result = CURLE_OK; - BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; - - if(!bp->br) - return CURLE_WRITE_ERROR; /* Stream already ended. */ - - decomp = malloc(DSIZ); - if(!decomp) - return CURLE_OUT_OF_MEMORY; - - while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) && - result == CURLE_OK) { - dst = (uint8_t *) decomp; - dstleft = DSIZ; - r = BrotliDecoderDecompressStream(bp->br, - &nbytes, &src, &dstleft, &dst, NULL); - result = Curl_unencode_write(data, writer->downstream, - decomp, DSIZ - 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; - } - } - free(decomp); - return result; -} - -static void brotli_close_writer(struct Curl_easy *data, - struct contenc_writer *writer) -{ - struct brotli_writer *bp = (struct brotli_writer *) writer; - - (void) data; - - if(bp->br) { - BrotliDecoderDestroyInstance(bp->br); - bp->br = NULL; - } -} - -static const struct content_encoding brotli_encoding = { - "br", - NULL, - brotli_init_writer, - brotli_unencode_write, - brotli_close_writer, - sizeof(struct brotli_writer) -}; -#endif - - -#ifdef HAVE_ZSTD -/* Zstd writer. */ -struct zstd_writer { - struct contenc_writer super; - ZSTD_DStream *zds; /* State structure for zstd. */ - void *decomp; -}; - -static CURLcode zstd_init_writer(struct Curl_easy *data, - struct contenc_writer *writer) -{ - struct zstd_writer *zp = (struct zstd_writer *) writer; - - (void)data; - - if(!writer->downstream) - return CURLE_WRITE_ERROR; - - zp->zds = ZSTD_createDStream(); - zp->decomp = NULL; - return zp->zds ? CURLE_OK : CURLE_OUT_OF_MEMORY; -} - -static CURLcode zstd_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, - 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(!zp->decomp) { - zp->decomp = malloc(DSIZ); - if(!zp->decomp) - return CURLE_OUT_OF_MEMORY; - } - in.pos = 0; - in.src = buf; - in.size = nbytes; - - for(;;) { - out.pos = 0; - out.dst = zp->decomp; - out.size = DSIZ; - - errorCode = ZSTD_decompressStream(zp->zds, &out, &in); - if(ZSTD_isError(errorCode)) { - return CURLE_BAD_CONTENT_ENCODING; - } - if(out.pos > 0) { - result = Curl_unencode_write(data, writer->downstream, - zp->decomp, out.pos); - if(result) - break; - } - if((in.pos == nbytes) && (out.pos < out.size)) - break; - } - - return result; -} - -static void zstd_close_writer(struct Curl_easy *data, - struct contenc_writer *writer) -{ - struct zstd_writer *zp = (struct zstd_writer *) writer; - - (void)data; - - if(zp->decomp) { - free(zp->decomp); - zp->decomp = NULL; - } - if(zp->zds) { - ZSTD_freeDStream(zp->zds); - zp->zds = NULL; - } -} - -static const struct content_encoding zstd_encoding = { - "zstd", - NULL, - zstd_init_writer, - zstd_unencode_write, - zstd_close_writer, - sizeof(struct zstd_writer) -}; -#endif - - -/* Identity handler. */ -static CURLcode identity_init_writer(struct Curl_easy *data, - struct contenc_writer *writer) -{ - (void) data; - return writer->downstream? CURLE_OK: CURLE_WRITE_ERROR; -} - -static CURLcode identity_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes) -{ - return Curl_unencode_write(data, writer->downstream, buf, nbytes); -} - -static void identity_close_writer(struct Curl_easy *data, - struct contenc_writer *writer) -{ - (void) data; - (void) writer; -} - -static const struct content_encoding identity_encoding = { - "identity", - "none", - identity_init_writer, - identity_unencode_write, - identity_close_writer, - sizeof(struct contenc_writer) -}; - - -/* supported content encodings table. */ -static const struct content_encoding * const encodings[] = { - &identity_encoding, -#ifdef HAVE_LIBZ - &deflate_encoding, - &gzip_encoding, -#endif -#ifdef HAVE_BROTLI - &brotli_encoding, -#endif -#ifdef HAVE_ZSTD - &zstd_encoding, -#endif - NULL -}; - - -/* Return a list of comma-separated names of supported encodings. */ -char *Curl_all_content_encodings(void) -{ - size_t len = 0; - const struct content_encoding * const *cep; - const struct content_encoding *ce; - char *ace; - - for(cep = encodings; *cep; cep++) { - ce = *cep; - if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) - len += strlen(ce->name) + 2; - } - - if(!len) - return strdup(CONTENT_ENCODING_DEFAULT); - - ace = malloc(len); - if(ace) { - char *p = ace; - for(cep = encodings; *cep; cep++) { - ce = *cep; - if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) { - strcpy(p, ce->name); - p += strlen(p); - *p++ = ','; - *p++ = ' '; - } - } - p[-2] = '\0'; - } - - return ace; -} - - -/* Real client writer: no downstream. */ -static CURLcode client_init_writer(struct Curl_easy *data, - struct contenc_writer *writer) -{ - (void) data; - return writer->downstream? CURLE_WRITE_ERROR: CURLE_OK; -} - -static CURLcode client_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes) -{ - struct SingleRequest *k = &data->req; - - (void) writer; - - if(!nbytes || k->ignorebody) - return CURLE_OK; - - return Curl_client_write(data, CLIENTWRITE_BODY, (char *) buf, nbytes); -} - -static void client_close_writer(struct Curl_easy *data, - struct contenc_writer *writer) -{ - (void) data; - (void) writer; -} - -static const struct content_encoding client_encoding = { - NULL, - NULL, - client_init_writer, - client_unencode_write, - client_close_writer, - sizeof(struct contenc_writer) -}; - - -/* Deferred error dummy writer. */ -static CURLcode error_init_writer(struct Curl_easy *data, - struct contenc_writer *writer) -{ - (void) data; - return writer->downstream? CURLE_OK: CURLE_WRITE_ERROR; -} - -static CURLcode error_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes) -{ - char *all = Curl_all_content_encodings(); - - (void) writer; - (void) buf; - (void) nbytes; - - if(!all) - return CURLE_OUT_OF_MEMORY; - failf(data, "Unrecognized content encoding type. " - "libcurl understands %s content encodings.", all); - free(all); - return CURLE_BAD_CONTENT_ENCODING; -} - -static void error_close_writer(struct Curl_easy *data, - struct contenc_writer *writer) -{ - (void) data; - (void) writer; -} - -static const struct content_encoding error_encoding = { - NULL, - NULL, - error_init_writer, - error_unencode_write, - error_close_writer, - sizeof(struct contenc_writer) -}; - -/* Create an unencoding writer stage using the given handler. */ -static struct contenc_writer * -new_unencoding_writer(struct Curl_easy *data, - const struct content_encoding *handler, - struct contenc_writer *downstream) -{ - struct contenc_writer *writer; - - DEBUGASSERT(handler->writersize >= sizeof(struct contenc_writer)); - writer = (struct contenc_writer *) calloc(1, handler->writersize); - - if(writer) { - writer->handler = handler; - writer->downstream = downstream; - if(handler->init_writer(data, writer)) { - free(writer); - writer = NULL; - } - } - - return writer; -} - -/* Write data using an unencoding writer stack. "nbytes" is not - allowed to be 0. */ -CURLcode Curl_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes) -{ - if(!nbytes) - return CURLE_OK; - return writer->handler->unencode_write(data, writer, buf, nbytes); -} - -/* Close and clean-up the connection's writer stack. */ -void Curl_unencode_cleanup(struct Curl_easy *data) -{ - struct SingleRequest *k = &data->req; - struct contenc_writer *writer = k->writer_stack; - - while(writer) { - k->writer_stack = writer->downstream; - writer->handler->close_writer(data, writer); - free(writer); - writer = k->writer_stack; - } -} - -/* Find the content encoding by name. */ -static const struct content_encoding *find_encoding(const char *name, - size_t len) -{ - const struct content_encoding * const *cep; - - for(cep = encodings; *cep; cep++) { - const struct content_encoding *ce = *cep; - if((strncasecompare(name, ce->name, len) && !ce->name[len]) || - (ce->alias && strncasecompare(name, ce->alias, len) && !ce->alias[len])) - return ce; - } - return NULL; -} - -/* allow no more than 5 "chained" compression steps */ -#define MAX_ENCODE_STACK 5 - -/* Set-up 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 maybechunked) -{ - struct SingleRequest *k = &data->req; - int counter = 0; - - do { - const char *name; - size_t namelen; - - /* Parse a single encoding name. */ - while(ISBLANK(*enclist) || *enclist == ',') - enclist++; - - name = enclist; - - for(namelen = 0; *enclist && *enclist != ','; enclist++) - if(!ISSPACE(*enclist)) - namelen = enclist - name + 1; - - /* Special case: chunked encoding is handled at the reader level. */ - if(maybechunked && namelen == 7 && strncasecompare(name, "chunked", 7)) { - k->chunk = TRUE; /* chunks coming our way. */ - Curl_httpchunk_init(data); /* init our chunky engine. */ - } - else if(namelen) { - const struct content_encoding *encoding = find_encoding(name, namelen); - struct contenc_writer *writer; - - if(!k->writer_stack) { - k->writer_stack = new_unencoding_writer(data, &client_encoding, NULL); - - if(!k->writer_stack) - return CURLE_OUT_OF_MEMORY; - } - - if(!encoding) - encoding = &error_encoding; /* Defer error at stack use. */ - - if(++counter >= MAX_ENCODE_STACK) { - failf(data, "Reject response due to %u content encodings", - counter); - return CURLE_BAD_CONTENT_ENCODING; - } - /* Stack the unencoding stage. */ - writer = new_unencoding_writer(data, encoding, k->writer_stack); - if(!writer) - return CURLE_OUT_OF_MEMORY; - k->writer_stack = writer; - } - } while(*enclist); - - return CURLE_OK; -} - -#else -/* Stubs for builds without HTTP. */ -CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, - const char *enclist, int maybechunked) -{ - (void) data; - (void) enclist; - (void) maybechunked; - return CURLE_NOT_BUILT_IN; -} - -CURLcode Curl_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes) -{ - (void) data; - (void) writer; - (void) buf; - (void) nbytes; - return CURLE_NOT_BUILT_IN; -} - -void Curl_unencode_cleanup(struct Curl_easy *data) -{ - (void) data; -} - -char *Curl_all_content_encodings(void) -{ - return strdup(CONTENT_ENCODING_DEFAULT); /* Satisfy caller. */ -} - -#endif /* CURL_DISABLE_HTTP */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+#include <brotli/decode.h>
+#endif
+
+#ifdef HAVE_ZSTD
+#include <zstd.h>
+#endif
+
+#include "sendf.h"
+#include "http.h"
+#include "content_encoding.h"
+#include "strdup.h"
+#include "strcase.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define CONTENT_ENCODING_DEFAULT "identity"
+
+#ifndef CURL_DISABLE_HTTP
+
+#define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */
+
+
+#ifdef HAVE_LIBZ
+
+/* Comment this out if zlib is always going to be at least ver. 1.2.0.4
+ (doing so will reduce code size slightly). */
+#define OLD_ZLIB_SUPPORT 1
+
+#define GZIP_MAGIC_0 0x1f
+#define GZIP_MAGIC_1 0x8b
+
+/* gzip flag byte */
+#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
+#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
+#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
+#define COMMENT 0x10 /* bit 4 set: file comment present */
+#define RESERVED 0xE0 /* bits 5..7: reserved */
+
+typedef enum {
+ ZLIB_UNINIT, /* uninitialized */
+ ZLIB_INIT, /* initialized */
+ ZLIB_INFLATING, /* inflating started. */
+ ZLIB_EXTERNAL_TRAILER, /* reading external trailer */
+ ZLIB_GZIP_HEADER, /* reading gzip header */
+ ZLIB_GZIP_INFLATING, /* inflating gzip stream */
+ ZLIB_INIT_GZIP /* initialized in transparent gzip mode */
+} zlibInitState;
+
+/* Deflate and gzip writer. */
+struct zlib_writer {
+ struct contenc_writer super;
+ zlibInitState zlib_init; /* zlib init state */
+ 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_GZIP_HEADER)
+ Curl_safefree(z->next_in);
+
+ 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 contenc_writer *writer,
+ zlibInitState started)
+{
+ struct zlib_writer *zp = (struct zlib_writer *) writer;
+ z_stream *z = &zp->z; /* zlib state structure */
+ uInt nread = z->avail_in;
+ Bytef *orig_in = z->next_in;
+ bool done = FALSE;
+ CURLcode result = CURLE_OK; /* Curl_client_write status */
+ char *decomp; /* Put the decompressed data here. */
+
+ /* Check state. */
+ if(zp->zlib_init != ZLIB_INIT &&
+ zp->zlib_init != ZLIB_INFLATING &&
+ zp->zlib_init != ZLIB_INIT_GZIP &&
+ zp->zlib_init != ZLIB_GZIP_INFLATING)
+ return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR);
+
+ /* Dynamically allocate a buffer for decompression because it's uncommonly
+ large to hold on the stack */
+ decomp = malloc(DSIZ);
+ if(!decomp)
+ return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY);
+
+ /* because the buffer size is fixed, iteratively decompress and transfer to
+ the client via downstream_write function. */
+ while(!done) {
+ int status; /* zlib status */
+ done = TRUE;
+
+ /* (re)set buffer for decompressed output for every iteration */
+ z->next_out = (Bytef *) decomp;
+ z->avail_out = DSIZ;
+
+#ifdef Z_BLOCK
+ /* Z_BLOCK is only available in zlib ver. >= 1.2.0.5 */
+ status = inflate(z, Z_BLOCK);
+#else
+ /* fallback for zlib ver. < 1.2.0.5 */
+ status = inflate(z, Z_SYNC_FLUSH);
+#endif
+
+ /* Flush output data if some. */
+ if(z->avail_out != DSIZ) {
+ if(status == Z_OK || status == Z_STREAM_END) {
+ zp->zlib_init = started; /* Data started. */
+ result = Curl_unencode_write(data, writer->downstream, decomp,
+ DSIZ - 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) {
+ /* Do not use inflateReset2(): only available since zlib 1.2.3.4. */
+ (void) inflateEnd(z); /* don't care about the return code */
+ if(inflateInit2(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;
+ }
+ }
+ free(decomp);
+
+ /* We're about to leave this call so the `nread' data bytes won't 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_init_writer(struct Curl_easy *data,
+ struct contenc_writer *writer)
+{
+ struct zlib_writer *zp = (struct zlib_writer *) writer;
+ z_stream *z = &zp->z; /* zlib state structure */
+
+ if(!writer->downstream)
+ return CURLE_WRITE_ERROR;
+
+ /* 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_unencode_write(struct Curl_easy *data,
+ struct contenc_writer *writer,
+ const char *buf, size_t nbytes)
+{
+ struct zlib_writer *zp = (struct zlib_writer *) writer;
+ z_stream *z = &zp->z; /* zlib state structure */
+
+ /* Set the compressed input when this function is called */
+ z->next_in = (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, ZLIB_INFLATING);
+}
+
+static void deflate_close_writer(struct Curl_easy *data,
+ struct contenc_writer *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 content_encoding deflate_encoding = {
+ "deflate",
+ NULL,
+ deflate_init_writer,
+ deflate_unencode_write,
+ deflate_close_writer,
+ sizeof(struct zlib_writer)
+};
+
+
+/* Gzip handler. */
+static CURLcode gzip_init_writer(struct Curl_easy *data,
+ struct contenc_writer *writer)
+{
+ struct zlib_writer *zp = (struct zlib_writer *) writer;
+ z_stream *z = &zp->z; /* zlib state structure */
+
+ if(!writer->downstream)
+ return CURLE_WRITE_ERROR;
+
+ /* Initialize zlib */
+ z->zalloc = (alloc_func) zalloc_cb;
+ z->zfree = (free_func) zfree_cb;
+
+ if(strcmp(zlibVersion(), "1.2.0.4") >= 0) {
+ /* zlib ver. >= 1.2.0.4 supports transparent gzip decompressing */
+ if(inflateInit2(z, MAX_WBITS + 32) != Z_OK) {
+ return process_zlib_error(data, z);
+ }
+ zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */
+ }
+ else {
+ /* we must parse the gzip header and trailer ourselves */
+ if(inflateInit2(z, -MAX_WBITS) != Z_OK) {
+ return process_zlib_error(data, z);
+ }
+ zp->trailerlen = 8; /* A CRC-32 and a 32-bit input size (RFC 1952, 2.2) */
+ zp->zlib_init = ZLIB_INIT; /* Initial call state */
+ }
+
+ return CURLE_OK;
+}
+
+#ifdef OLD_ZLIB_SUPPORT
+/* Skip over the gzip header */
+static enum {
+ GZIP_OK,
+ GZIP_BAD,
+ GZIP_UNDERFLOW
+} check_gzip_header(unsigned char const *data, ssize_t len, ssize_t *headerlen)
+{
+ int method, flags;
+ const ssize_t totallen = len;
+
+ /* The shortest header is 10 bytes */
+ if(len < 10)
+ return GZIP_UNDERFLOW;
+
+ if((data[0] != GZIP_MAGIC_0) || (data[1] != GZIP_MAGIC_1))
+ return GZIP_BAD;
+
+ method = data[2];
+ flags = data[3];
+
+ if(method != Z_DEFLATED || (flags & RESERVED) != 0) {
+ /* Can't handle this compression method or unknown flag */
+ return GZIP_BAD;
+ }
+
+ /* Skip over time, xflags, OS code and all previous bytes */
+ len -= 10;
+ data += 10;
+
+ if(flags & EXTRA_FIELD) {
+ ssize_t extra_len;
+
+ if(len < 2)
+ return GZIP_UNDERFLOW;
+
+ extra_len = (data[1] << 8) | data[0];
+
+ if(len < (extra_len + 2))
+ return GZIP_UNDERFLOW;
+
+ len -= (extra_len + 2);
+ data += (extra_len + 2);
+ }
+
+ if(flags & ORIG_NAME) {
+ /* Skip over NUL-terminated file name */
+ while(len && *data) {
+ --len;
+ ++data;
+ }
+ if(!len || *data)
+ return GZIP_UNDERFLOW;
+
+ /* Skip over the NUL */
+ --len;
+ ++data;
+ }
+
+ if(flags & COMMENT) {
+ /* Skip over NUL-terminated comment */
+ while(len && *data) {
+ --len;
+ ++data;
+ }
+ if(!len || *data)
+ return GZIP_UNDERFLOW;
+
+ /* Skip over the NUL */
+ --len;
+ }
+
+ if(flags & HEAD_CRC) {
+ if(len < 2)
+ return GZIP_UNDERFLOW;
+
+ len -= 2;
+ }
+
+ *headerlen = totallen - len;
+ return GZIP_OK;
+}
+#endif
+
+static CURLcode gzip_unencode_write(struct Curl_easy *data,
+ struct contenc_writer *writer,
+ const char *buf, size_t nbytes)
+{
+ struct zlib_writer *zp = (struct zlib_writer *) writer;
+ z_stream *z = &zp->z; /* zlib state structure */
+
+ if(zp->zlib_init == ZLIB_INIT_GZIP) {
+ /* Let zlib handle the gzip decompression entirely */
+ z->next_in = (Bytef *) buf;
+ z->avail_in = (uInt) nbytes;
+ /* Now uncompress the data */
+ return inflate_stream(data, writer, ZLIB_INIT_GZIP);
+ }
+
+#ifndef OLD_ZLIB_SUPPORT
+ /* Support for old zlib versions is compiled away and we are running with
+ an old version, so return an error. */
+ return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR);
+
+#else
+ /* This next mess is to get around the potential case where there isn't
+ * enough data passed in to skip over the gzip header. If that happens, we
+ * malloc a block and copy what we have then wait for the next call. If
+ * there still isn't enough (this is definitely a worst-case scenario), we
+ * make the block bigger, copy the next part in and keep waiting.
+ *
+ * This is only required with zlib versions < 1.2.0.4 as newer versions
+ * can handle the gzip header themselves.
+ */
+
+ switch(zp->zlib_init) {
+ /* Skip over gzip header? */
+ case ZLIB_INIT:
+ {
+ /* Initial call state */
+ ssize_t hlen;
+
+ switch(check_gzip_header((unsigned char *) buf, nbytes, &hlen)) {
+ case GZIP_OK:
+ z->next_in = (Bytef *) buf + hlen;
+ z->avail_in = (uInt) (nbytes - hlen);
+ zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */
+ break;
+
+ case GZIP_UNDERFLOW:
+ /* We need more data so we can find the end of the gzip header. It's
+ * possible that the memory block we malloc here will never be freed if
+ * the transfer abruptly aborts after this point. Since it's unlikely
+ * that circumstances will be right for this code path to be followed in
+ * the first place, and it's even more unlikely for a transfer to fail
+ * immediately afterwards, it should seldom be a problem.
+ */
+ z->avail_in = (uInt) nbytes;
+ z->next_in = malloc(z->avail_in);
+ if(!z->next_in) {
+ return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY);
+ }
+ memcpy(z->next_in, buf, z->avail_in);
+ zp->zlib_init = ZLIB_GZIP_HEADER; /* Need more gzip header data state */
+ /* We don't have any data to inflate yet */
+ return CURLE_OK;
+
+ case GZIP_BAD:
+ default:
+ return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z));
+ }
+
+ }
+ break;
+
+ case ZLIB_GZIP_HEADER:
+ {
+ /* Need more gzip header data state */
+ ssize_t hlen;
+ z->avail_in += (uInt) nbytes;
+ z->next_in = Curl_saferealloc(z->next_in, z->avail_in);
+ if(!z->next_in) {
+ return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY);
+ }
+ /* Append the new block of data to the previous one */
+ memcpy(z->next_in + z->avail_in - nbytes, buf, nbytes);
+
+ switch(check_gzip_header(z->next_in, z->avail_in, &hlen)) {
+ case GZIP_OK:
+ /* This is the zlib stream data */
+ free(z->next_in);
+ /* Don't point into the malloced block since we just freed it */
+ z->next_in = (Bytef *) buf + hlen + nbytes - z->avail_in;
+ z->avail_in = (uInt) (z->avail_in - hlen);
+ zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */
+ break;
+
+ case GZIP_UNDERFLOW:
+ /* We still don't have any data to inflate! */
+ return CURLE_OK;
+
+ case GZIP_BAD:
+ default:
+ return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z));
+ }
+
+ }
+ break;
+
+ case ZLIB_EXTERNAL_TRAILER:
+ z->next_in = (Bytef *) buf;
+ z->avail_in = (uInt) nbytes;
+ return process_trailer(data, zp);
+
+ case ZLIB_GZIP_INFLATING:
+ default:
+ /* Inflating stream state */
+ z->next_in = (Bytef *) buf;
+ z->avail_in = (uInt) nbytes;
+ break;
+ }
+
+ if(z->avail_in == 0) {
+ /* We don't have any data to inflate; wait until next time */
+ return CURLE_OK;
+ }
+
+ /* We've parsed the header, now uncompress the data */
+ return inflate_stream(data, writer, ZLIB_GZIP_INFLATING);
+#endif
+}
+
+static void gzip_close_writer(struct Curl_easy *data,
+ struct contenc_writer *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 content_encoding gzip_encoding = {
+ "gzip",
+ "x-gzip",
+ gzip_init_writer,
+ gzip_unencode_write,
+ gzip_close_writer,
+ sizeof(struct zlib_writer)
+};
+
+#endif /* HAVE_LIBZ */
+
+
+#ifdef HAVE_BROTLI
+/* Brotli writer. */
+struct brotli_writer {
+ struct contenc_writer super;
+ 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_init_writer(struct Curl_easy *data,
+ struct contenc_writer *writer)
+{
+ struct brotli_writer *bp = (struct brotli_writer *) writer;
+ (void) data;
+
+ if(!writer->downstream)
+ return CURLE_WRITE_ERROR;
+
+ bp->br = BrotliDecoderCreateInstance(NULL, NULL, NULL);
+ return bp->br? CURLE_OK: CURLE_OUT_OF_MEMORY;
+}
+
+static CURLcode brotli_unencode_write(struct Curl_easy *data,
+ struct contenc_writer *writer,
+ const char *buf, size_t nbytes)
+{
+ struct brotli_writer *bp = (struct brotli_writer *) writer;
+ const uint8_t *src = (const uint8_t *) buf;
+ char *decomp;
+ uint8_t *dst;
+ size_t dstleft;
+ CURLcode result = CURLE_OK;
+ BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
+
+ if(!bp->br)
+ return CURLE_WRITE_ERROR; /* Stream already ended. */
+
+ decomp = malloc(DSIZ);
+ if(!decomp)
+ return CURLE_OUT_OF_MEMORY;
+
+ while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) &&
+ result == CURLE_OK) {
+ dst = (uint8_t *) decomp;
+ dstleft = DSIZ;
+ r = BrotliDecoderDecompressStream(bp->br,
+ &nbytes, &src, &dstleft, &dst, NULL);
+ result = Curl_unencode_write(data, writer->downstream,
+ decomp, DSIZ - 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;
+ }
+ }
+ free(decomp);
+ return result;
+}
+
+static void brotli_close_writer(struct Curl_easy *data,
+ struct contenc_writer *writer)
+{
+ struct brotli_writer *bp = (struct brotli_writer *) writer;
+
+ (void) data;
+
+ if(bp->br) {
+ BrotliDecoderDestroyInstance(bp->br);
+ bp->br = NULL;
+ }
+}
+
+static const struct content_encoding brotli_encoding = {
+ "br",
+ NULL,
+ brotli_init_writer,
+ brotli_unencode_write,
+ brotli_close_writer,
+ sizeof(struct brotli_writer)
+};
+#endif
+
+
+#ifdef HAVE_ZSTD
+/* Zstd writer. */
+struct zstd_writer {
+ struct contenc_writer super;
+ ZSTD_DStream *zds; /* State structure for zstd. */
+ void *decomp;
+};
+
+static CURLcode zstd_init_writer(struct Curl_easy *data,
+ struct contenc_writer *writer)
+{
+ struct zstd_writer *zp = (struct zstd_writer *) writer;
+
+ (void)data;
+
+ if(!writer->downstream)
+ return CURLE_WRITE_ERROR;
+
+ zp->zds = ZSTD_createDStream();
+ zp->decomp = NULL;
+ return zp->zds ? CURLE_OK : CURLE_OUT_OF_MEMORY;
+}
+
+static CURLcode zstd_unencode_write(struct Curl_easy *data,
+ struct contenc_writer *writer,
+ 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(!zp->decomp) {
+ zp->decomp = malloc(DSIZ);
+ if(!zp->decomp)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ in.pos = 0;
+ in.src = buf;
+ in.size = nbytes;
+
+ for(;;) {
+ out.pos = 0;
+ out.dst = zp->decomp;
+ out.size = DSIZ;
+
+ errorCode = ZSTD_decompressStream(zp->zds, &out, &in);
+ if(ZSTD_isError(errorCode)) {
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+ if(out.pos > 0) {
+ result = Curl_unencode_write(data, writer->downstream,
+ zp->decomp, out.pos);
+ if(result)
+ break;
+ }
+ if((in.pos == nbytes) && (out.pos < out.size))
+ break;
+ }
+
+ return result;
+}
+
+static void zstd_close_writer(struct Curl_easy *data,
+ struct contenc_writer *writer)
+{
+ struct zstd_writer *zp = (struct zstd_writer *) writer;
+
+ (void)data;
+
+ if(zp->decomp) {
+ free(zp->decomp);
+ zp->decomp = NULL;
+ }
+ if(zp->zds) {
+ ZSTD_freeDStream(zp->zds);
+ zp->zds = NULL;
+ }
+}
+
+static const struct content_encoding zstd_encoding = {
+ "zstd",
+ NULL,
+ zstd_init_writer,
+ zstd_unencode_write,
+ zstd_close_writer,
+ sizeof(struct zstd_writer)
+};
+#endif
+
+
+/* Identity handler. */
+static CURLcode identity_init_writer(struct Curl_easy *data,
+ struct contenc_writer *writer)
+{
+ (void) data;
+ return writer->downstream? CURLE_OK: CURLE_WRITE_ERROR;
+}
+
+static CURLcode identity_unencode_write(struct Curl_easy *data,
+ struct contenc_writer *writer,
+ const char *buf, size_t nbytes)
+{
+ return Curl_unencode_write(data, writer->downstream, buf, nbytes);
+}
+
+static void identity_close_writer(struct Curl_easy *data,
+ struct contenc_writer *writer)
+{
+ (void) data;
+ (void) writer;
+}
+
+static const struct content_encoding identity_encoding = {
+ "identity",
+ "none",
+ identity_init_writer,
+ identity_unencode_write,
+ identity_close_writer,
+ sizeof(struct contenc_writer)
+};
+
+
+/* supported content encodings table. */
+static const struct content_encoding * const encodings[] = {
+ &identity_encoding,
+#ifdef HAVE_LIBZ
+ &deflate_encoding,
+ &gzip_encoding,
+#endif
+#ifdef HAVE_BROTLI
+ &brotli_encoding,
+#endif
+#ifdef HAVE_ZSTD
+ &zstd_encoding,
+#endif
+ NULL
+};
+
+
+/* Return a list of comma-separated names of supported encodings. */
+char *Curl_all_content_encodings(void)
+{
+ size_t len = 0;
+ const struct content_encoding * const *cep;
+ const struct content_encoding *ce;
+ char *ace;
+
+ for(cep = encodings; *cep; cep++) {
+ ce = *cep;
+ if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT))
+ len += strlen(ce->name) + 2;
+ }
+
+ if(!len)
+ return strdup(CONTENT_ENCODING_DEFAULT);
+
+ ace = malloc(len);
+ if(ace) {
+ char *p = ace;
+ for(cep = encodings; *cep; cep++) {
+ ce = *cep;
+ if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) {
+ strcpy(p, ce->name);
+ p += strlen(p);
+ *p++ = ',';
+ *p++ = ' ';
+ }
+ }
+ p[-2] = '\0';
+ }
+
+ return ace;
+}
+
+
+/* Real client writer: no downstream. */
+static CURLcode client_init_writer(struct Curl_easy *data,
+ struct contenc_writer *writer)
+{
+ (void) data;
+ return writer->downstream? CURLE_WRITE_ERROR: CURLE_OK;
+}
+
+static CURLcode client_unencode_write(struct Curl_easy *data,
+ struct contenc_writer *writer,
+ const char *buf, size_t nbytes)
+{
+ struct SingleRequest *k = &data->req;
+
+ (void) writer;
+
+ if(!nbytes || k->ignorebody)
+ return CURLE_OK;
+
+ return Curl_client_write(data, CLIENTWRITE_BODY, (char *) buf, nbytes);
+}
+
+static void client_close_writer(struct Curl_easy *data,
+ struct contenc_writer *writer)
+{
+ (void) data;
+ (void) writer;
+}
+
+static const struct content_encoding client_encoding = {
+ NULL,
+ NULL,
+ client_init_writer,
+ client_unencode_write,
+ client_close_writer,
+ sizeof(struct contenc_writer)
+};
+
+
+/* Deferred error dummy writer. */
+static CURLcode error_init_writer(struct Curl_easy *data,
+ struct contenc_writer *writer)
+{
+ (void) data;
+ return writer->downstream? CURLE_OK: CURLE_WRITE_ERROR;
+}
+
+static CURLcode error_unencode_write(struct Curl_easy *data,
+ struct contenc_writer *writer,
+ const char *buf, size_t nbytes)
+{
+ char *all = Curl_all_content_encodings();
+
+ (void) writer;
+ (void) buf;
+ (void) nbytes;
+
+ if(!all)
+ return CURLE_OUT_OF_MEMORY;
+ failf(data, "Unrecognized content encoding type. "
+ "libcurl understands %s content encodings.", all);
+ free(all);
+ return CURLE_BAD_CONTENT_ENCODING;
+}
+
+static void error_close_writer(struct Curl_easy *data,
+ struct contenc_writer *writer)
+{
+ (void) data;
+ (void) writer;
+}
+
+static const struct content_encoding error_encoding = {
+ NULL,
+ NULL,
+ error_init_writer,
+ error_unencode_write,
+ error_close_writer,
+ sizeof(struct contenc_writer)
+};
+
+/* Create an unencoding writer stage using the given handler. */
+static struct contenc_writer *
+new_unencoding_writer(struct Curl_easy *data,
+ const struct content_encoding *handler,
+ struct contenc_writer *downstream)
+{
+ struct contenc_writer *writer;
+
+ DEBUGASSERT(handler->writersize >= sizeof(struct contenc_writer));
+ writer = (struct contenc_writer *) calloc(1, handler->writersize);
+
+ if(writer) {
+ writer->handler = handler;
+ writer->downstream = downstream;
+ if(handler->init_writer(data, writer)) {
+ free(writer);
+ writer = NULL;
+ }
+ }
+
+ return writer;
+}
+
+/* Write data using an unencoding writer stack. "nbytes" is not
+ allowed to be 0. */
+CURLcode Curl_unencode_write(struct Curl_easy *data,
+ struct contenc_writer *writer,
+ const char *buf, size_t nbytes)
+{
+ if(!nbytes)
+ return CURLE_OK;
+ return writer->handler->unencode_write(data, writer, buf, nbytes);
+}
+
+/* Close and clean-up the connection's writer stack. */
+void Curl_unencode_cleanup(struct Curl_easy *data)
+{
+ struct SingleRequest *k = &data->req;
+ struct contenc_writer *writer = k->writer_stack;
+
+ while(writer) {
+ k->writer_stack = writer->downstream;
+ writer->handler->close_writer(data, writer);
+ free(writer);
+ writer = k->writer_stack;
+ }
+}
+
+/* Find the content encoding by name. */
+static const struct content_encoding *find_encoding(const char *name,
+ size_t len)
+{
+ const struct content_encoding * const *cep;
+
+ for(cep = encodings; *cep; cep++) {
+ const struct content_encoding *ce = *cep;
+ if((strncasecompare(name, ce->name, len) && !ce->name[len]) ||
+ (ce->alias && strncasecompare(name, ce->alias, len) && !ce->alias[len]))
+ return ce;
+ }
+ return NULL;
+}
+
+/* allow no more than 5 "chained" compression steps */
+#define MAX_ENCODE_STACK 5
+
+/* Set-up 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 maybechunked)
+{
+ struct SingleRequest *k = &data->req;
+ int counter = 0;
+
+ do {
+ const char *name;
+ size_t namelen;
+
+ /* Parse a single encoding name. */
+ while(ISBLANK(*enclist) || *enclist == ',')
+ enclist++;
+
+ name = enclist;
+
+ for(namelen = 0; *enclist && *enclist != ','; enclist++)
+ if(!ISSPACE(*enclist))
+ namelen = enclist - name + 1;
+
+ /* Special case: chunked encoding is handled at the reader level. */
+ if(maybechunked && namelen == 7 && strncasecompare(name, "chunked", 7)) {
+ k->chunk = TRUE; /* chunks coming our way. */
+ Curl_httpchunk_init(data); /* init our chunky engine. */
+ }
+ else if(namelen) {
+ const struct content_encoding *encoding = find_encoding(name, namelen);
+ struct contenc_writer *writer;
+
+ if(!k->writer_stack) {
+ k->writer_stack = new_unencoding_writer(data, &client_encoding, NULL);
+
+ if(!k->writer_stack)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(!encoding)
+ encoding = &error_encoding; /* Defer error at stack use. */
+
+ if(++counter >= MAX_ENCODE_STACK) {
+ failf(data, "Reject response due to %u content encodings",
+ counter);
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+ /* Stack the unencoding stage. */
+ writer = new_unencoding_writer(data, encoding, k->writer_stack);
+ if(!writer)
+ return CURLE_OUT_OF_MEMORY;
+ k->writer_stack = writer;
+ }
+ } while(*enclist);
+
+ return CURLE_OK;
+}
+
+#else
+/* Stubs for builds without HTTP. */
+CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
+ const char *enclist, int maybechunked)
+{
+ (void) data;
+ (void) enclist;
+ (void) maybechunked;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode Curl_unencode_write(struct Curl_easy *data,
+ struct contenc_writer *writer,
+ const char *buf, size_t nbytes)
+{
+ (void) data;
+ (void) writer;
+ (void) buf;
+ (void) nbytes;
+ return CURLE_NOT_BUILT_IN;
+}
+
+void Curl_unencode_cleanup(struct Curl_easy *data)
+{
+ (void) data;
+}
+
+char *Curl_all_content_encodings(void)
+{
+ return strdup(CONTENT_ENCODING_DEFAULT); /* Satisfy caller. */
+}
+
+#endif /* CURL_DISABLE_HTTP */
diff --git a/libs/libcurl/src/content_encoding.h b/libs/libcurl/src/content_encoding.h index 3c278cf727..5a3e4f6410 100644 --- a/libs/libcurl/src/content_encoding.h +++ b/libs/libcurl/src/content_encoding.h @@ -1,56 +1,56 @@ -#ifndef HEADER_CURL_CONTENT_ENCODING_H -#define HEADER_CURL_CONTENT_ENCODING_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 contenc_writer { - const struct content_encoding *handler; /* Encoding handler. */ - struct contenc_writer *downstream; /* Downstream writer. */ -}; - -/* Content encoding writer. */ -struct content_encoding { - const char *name; /* Encoding name. */ - const char *alias; /* Encoding name alias. */ - CURLcode (*init_writer)(struct Curl_easy *data, - struct contenc_writer *writer); - CURLcode (*unencode_write)(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes); - void (*close_writer)(struct Curl_easy *data, - struct contenc_writer *writer); - size_t writersize; -}; - - -CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, - const char *enclist, int maybechunked); -CURLcode Curl_unencode_write(struct Curl_easy *data, - struct contenc_writer *writer, - const char *buf, size_t nbytes); -void Curl_unencode_cleanup(struct Curl_easy *data); -char *Curl_all_content_encodings(void); - -#endif /* HEADER_CURL_CONTENT_ENCODING_H */ +#ifndef HEADER_CURL_CONTENT_ENCODING_H
+#define HEADER_CURL_CONTENT_ENCODING_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 contenc_writer {
+ const struct content_encoding *handler; /* Encoding handler. */
+ struct contenc_writer *downstream; /* Downstream writer. */
+};
+
+/* Content encoding writer. */
+struct content_encoding {
+ const char *name; /* Encoding name. */
+ const char *alias; /* Encoding name alias. */
+ CURLcode (*init_writer)(struct Curl_easy *data,
+ struct contenc_writer *writer);
+ CURLcode (*unencode_write)(struct Curl_easy *data,
+ struct contenc_writer *writer,
+ const char *buf, size_t nbytes);
+ void (*close_writer)(struct Curl_easy *data,
+ struct contenc_writer *writer);
+ size_t writersize;
+};
+
+
+CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
+ const char *enclist, int maybechunked);
+CURLcode Curl_unencode_write(struct Curl_easy *data,
+ struct contenc_writer *writer,
+ const char *buf, size_t nbytes);
+void Curl_unencode_cleanup(struct Curl_easy *data);
+char *Curl_all_content_encodings(void);
+
+#endif /* HEADER_CURL_CONTENT_ENCODING_H */
diff --git a/libs/libcurl/src/cookie.c b/libs/libcurl/src/cookie.c index 8eaedeeb7f..b9b18fc62c 100644 --- a/libs/libcurl/src/cookie.c +++ b/libs/libcurl/src/cookie.c @@ -1,1829 +1,1835 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - -/*** - - -RECEIVING COOKIE INFORMATION -============================ - -struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, - const char *file, struct CookieInfo *inc, bool newsession); - - Inits a cookie struct to store data in a local file. This is always - called before any cookies are set. - -struct Cookie *Curl_cookie_add(struct Curl_easy *data, - struct CookieInfo *c, bool httpheader, bool noexpire, - char *lineptr, const char *domain, const char *path, - bool secure); - - The 'lineptr' parameter is a full "Set-cookie:" line as - received from a server. - - The function need to replace previously stored lines that this new - line supersedes. - - It may remove lines that are expired. - - It should return an indication of success/error. - - -SENDING COOKIE INFORMATION -========================== - -struct Cookies *Curl_cookie_getlist(struct CookieInfo *cookie, - char *host, char *path, bool secure); - - For a given host and path, return a linked list of cookies that - the client should send to the server if used now. The secure - boolean informs the cookie if a secure connection is achieved or - not. - - It shall only return cookies that haven't expired. - - -Example set of cookies: - - Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure - Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; - domain=.fidelity.com; path=/ftgw; secure - Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; - domain=.fidelity.com; path=/; secure - Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; - domain=.fidelity.com; path=/; secure - Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; - domain=.fidelity.com; path=/; secure - Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; - domain=.fidelity.com; path=/; secure - Set-cookie: - Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday, - 13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure -****/ - - -#include "curl_setup.h" - -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - -#include "urldata.h" -#include "cookie.h" -#include "psl.h" -#include "strtok.h" -#include "sendf.h" -#include "slist.h" -#include "share.h" -#include "strtoofft.h" -#include "strcase.h" -#include "curl_get_line.h" -#include "curl_memrchr.h" -#include "parsedate.h" -#include "rename.h" -#include "fopen.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -static void strstore(char **str, const char *newstr); - -static void freecookie(struct Cookie *co) -{ - free(co->expirestr); - free(co->domain); - free(co->path); - free(co->spath); - free(co->name); - free(co->value); - free(co->maxage); - free(co->version); - free(co); -} - -static bool tailmatch(const char *cooke_domain, const char *hostname) -{ - size_t cookie_domain_len = strlen(cooke_domain); - size_t hostname_len = strlen(hostname); - - if(hostname_len < cookie_domain_len) - return FALSE; - - if(!strcasecompare(cooke_domain, hostname + hostname_len-cookie_domain_len)) - return FALSE; - - /* - * A lead char of cookie_domain is not '.'. - * RFC6265 4.1.2.3. The Domain Attribute says: - * For example, if the value of the Domain attribute is - * "example.com", the user agent will include the cookie in the Cookie - * header when making HTTP requests to example.com, www.example.com, and - * www.corp.example.com. - */ - if(hostname_len == cookie_domain_len) - return TRUE; - if('.' == *(hostname + hostname_len - cookie_domain_len - 1)) - return TRUE; - return FALSE; -} - -/* - * matching cookie path and url path - * RFC6265 5.1.4 Paths and Path-Match - */ -static bool pathmatch(const char *cookie_path, const char *request_uri) -{ - size_t cookie_path_len; - size_t uri_path_len; - char *uri_path = NULL; - char *pos; - bool ret = FALSE; - - /* cookie_path must not have last '/' separator. ex: /sample */ - cookie_path_len = strlen(cookie_path); - if(1 == cookie_path_len) { - /* cookie_path must be '/' */ - return TRUE; - } - - uri_path = strdup(request_uri); - if(!uri_path) - return FALSE; - pos = strchr(uri_path, '?'); - if(pos) - *pos = 0x0; - - /* #-fragments are already cut off! */ - if(0 == strlen(uri_path) || uri_path[0] != '/') { - strstore(&uri_path, "/"); - if(!uri_path) - return FALSE; - } - - /* - * here, RFC6265 5.1.4 says - * 4. Output the characters of the uri-path from the first character up - * to, but not including, the right-most %x2F ("/"). - * but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site - * without redirect. - * Ignore this algorithm because /hoge is uri path for this case - * (uri path is not /). - */ - - uri_path_len = strlen(uri_path); - - if(uri_path_len < cookie_path_len) { - ret = FALSE; - goto pathmatched; - } - - /* not using checkprefix() because matching should be case-sensitive */ - if(strncmp(cookie_path, uri_path, cookie_path_len)) { - ret = FALSE; - goto pathmatched; - } - - /* The cookie-path and the uri-path are identical. */ - if(cookie_path_len == uri_path_len) { - ret = TRUE; - goto pathmatched; - } - - /* here, cookie_path_len < uri_path_len */ - if(uri_path[cookie_path_len] == '/') { - ret = TRUE; - goto pathmatched; - } - - ret = FALSE; - -pathmatched: - free(uri_path); - return ret; -} - -/* - * Return the top-level domain, for optimal hashing. - */ -static const char *get_top_domain(const char * const domain, size_t *outlen) -{ - size_t len = 0; - const char *first = NULL, *last; - - if(domain) { - len = strlen(domain); - last = memrchr(domain, '.', len); - if(last) { - first = memrchr(domain, '.', (last - domain)); - if(first) - len -= (++first - domain); - } - } - - if(outlen) - *outlen = len; - - return first? first: domain; -} - -/* Avoid C1001, an "internal error" with MSVC14 */ -#if defined(_MSC_VER) && (_MSC_VER == 1900) -#pragma optimize("", off) -#endif - -/* - * A case-insensitive hash for the cookie domains. - */ -static size_t cookie_hash_domain(const char *domain, const size_t len) -{ - const char *end = domain + len; - size_t h = 5381; - - while(domain < end) { - h += h << 5; - h ^= Curl_raw_toupper(*domain++); - } - - return (h % COOKIE_HASH_SIZE); -} - -#if defined(_MSC_VER) && (_MSC_VER == 1900) -#pragma optimize("", on) -#endif - -/* - * Hash this domain. - */ -static size_t cookiehash(const char * const domain) -{ - const char *top; - size_t len; - - if(!domain || Curl_host_is_ipnum(domain)) - return 0; - - top = get_top_domain(domain, &len); - return cookie_hash_domain(top, len); -} - -/* - * cookie path sanitize - */ -static char *sanitize_cookie_path(const char *cookie_path) -{ - size_t len; - char *new_path = strdup(cookie_path); - if(!new_path) - return NULL; - - /* some stupid site sends path attribute with '"'. */ - len = strlen(new_path); - if(new_path[0] == '\"') { - memmove((void *)new_path, (const void *)(new_path + 1), len); - len--; - } - if(len && (new_path[len - 1] == '\"')) { - new_path[len - 1] = 0x0; - len--; - } - - /* RFC6265 5.2.4 The Path Attribute */ - if(new_path[0] != '/') { - /* Let cookie-path be the default-path. */ - strstore(&new_path, "/"); - return new_path; - } - - /* convert /hoge/ to /hoge */ - if(len && new_path[len - 1] == '/') { - new_path[len - 1] = 0x0; - } - - return new_path; -} - -/* - * Load cookies from all given cookie files (CURLOPT_COOKIEFILE). - * - * NOTE: OOM or cookie parsing failures are ignored. - */ -void Curl_cookie_loadfiles(struct Curl_easy *data) -{ - struct curl_slist *list = data->state.cookielist; - if(list) { - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - while(list) { - struct CookieInfo *newcookies = Curl_cookie_init(data, - list->data, - data->cookies, - data->set.cookiesession); - if(!newcookies) - /* - * Failure may be due to OOM or a bad cookie; both are ignored - * but only the first should be - */ - infof(data, "ignoring failed cookie_init for %s", list->data); - else - data->cookies = newcookies; - list = list->next; - } - curl_slist_free_all(data->state.cookielist); /* clean up list */ - data->state.cookielist = NULL; /* don't do this again! */ - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - } -} - -/* - * strstore - * - * A thin wrapper around strdup which ensures that any memory allocated at - * *str will be freed before the string allocated by strdup is stored there. - * The intended usecase is repeated assignments to the same variable during - * parsing in a last-wins scenario. The caller is responsible for checking - * for OOM errors. - */ -static void strstore(char **str, const char *newstr) -{ - free(*str); - *str = strdup(newstr); -} - -/* - * remove_expired - * - * Remove expired cookies from the hash by inspecting the expires timestamp on - * each cookie in the hash, freeing and deleting any where the timestamp is in - * the past. If the cookiejar has recorded the next timestamp at which one or - * more cookies expire, then processing will exit early in case this timestamp - * is in the future. - */ -static void remove_expired(struct CookieInfo *cookies) -{ - struct Cookie *co, *nx; - curl_off_t now = (curl_off_t)time(NULL); - unsigned int i; - - /* - * If the earliest expiration timestamp in the jar is in the future we can - * skip scanning the whole jar and instead exit early as there won't be any - * cookies to evict. If we need to evict however, reset the next_expiration - * counter in order to track the next one. In case the recorded first - * expiration is the max offset, then perform the safe fallback of checking - * all cookies. - */ - if(now < cookies->next_expiration && - cookies->next_expiration != CURL_OFF_T_MAX) - return; - else - cookies->next_expiration = CURL_OFF_T_MAX; - - for(i = 0; i < COOKIE_HASH_SIZE; i++) { - struct Cookie *pv = NULL; - co = cookies->cookies[i]; - while(co) { - nx = co->next; - if(co->expires && co->expires < now) { - if(!pv) { - cookies->cookies[i] = co->next; - } - else { - pv->next = co->next; - } - cookies->numcookies--; - freecookie(co); - } - else { - /* - * If this cookie has an expiration timestamp earlier than what we've - * seen so far then record it for the next round of expirations. - */ - if(co->expires && co->expires < cookies->next_expiration) - cookies->next_expiration = co->expires; - pv = co; - } - co = nx; - } - } -} - -/* Make sure domain contains a dot or is localhost. */ -static bool bad_domain(const char *domain) -{ - if(strcasecompare(domain, "localhost")) - return FALSE; - else { - /* there must be a dot present, but that dot must not be a trailing dot */ - char *dot = strchr(domain, '.'); - if(dot) - return dot[1] ? FALSE : TRUE; - } - return TRUE; -} - -/* - RFC 6265 section 4.1.1 says a server should accept this range: - - cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E - - But Firefox and Chrome as of June 2022 accept space, comma and double-quotes - fine. The prime reason for filtering out control bytes is that some HTTP - servers return 400 for requests that contain such. -*/ -static int invalid_octets(const char *p) -{ - /* Reject all bytes \x01 - \x1f (*except* \x09, TAB) + \x7f */ - static const char badoctets[] = { - "\x01\x02\x03\x04\x05\x06\x07\x08\x0a" - "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14" - "\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f" - }; - size_t len; - /* scan for all the octets that are *not* in cookie-octet */ - len = strcspn(p, badoctets); - return (p[len] != '\0'); -} - -/* - * Curl_cookie_add - * - * Add a single cookie line to the cookie keeping object. Be aware that - * sometimes we get an IP-only host name, and that might also be a numerical - * IPv6 address. - * - * Returns NULL on out of memory or invalid cookie. This is suboptimal, - * as they should be treated separately. - */ -struct Cookie * -Curl_cookie_add(struct Curl_easy *data, - /* - * The 'data' pointer here may be NULL at times, and thus - * must only be used very carefully for things that can deal - * with data being NULL. Such as infof() and similar - */ - struct CookieInfo *c, - bool httpheader, /* TRUE if HTTP header-style line */ - bool noexpire, /* if TRUE, skip remove_expired() */ - char *lineptr, /* first character of the line */ - const char *domain, /* default domain */ - const char *path, /* full path used when this cookie is set, - used to get default path for the cookie - unless set */ - bool secure) /* TRUE if connection is over secure origin */ -{ - struct Cookie *clist; - struct Cookie *co; - struct Cookie *lastc = NULL; - struct Cookie *replace_co = NULL; - struct Cookie *replace_clist = NULL; - time_t now = time(NULL); - bool replace_old = FALSE; - bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */ - size_t myhash; - -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif - - DEBUGASSERT(MAX_SET_COOKIE_AMOUNT <= 255); /* counter is an unsigned char */ - if(data->req.setcookies >= MAX_SET_COOKIE_AMOUNT) - return NULL; - - /* First, alloc and init a new struct for it */ - co = calloc(1, sizeof(struct Cookie)); - if(!co) - return NULL; /* bail out if we're this low on memory */ - - if(httpheader) { - /* This line was read off a HTTP-header */ - char name[MAX_NAME]; - char what[MAX_NAME]; - const char *ptr; - const char *semiptr; - - size_t linelength = strlen(lineptr); - if(linelength > MAX_COOKIE_LINE) { - /* discard overly long lines at once */ - free(co); - return NULL; - } - - semiptr = strchr(lineptr, ';'); /* first, find a semicolon */ - - while(*lineptr && ISBLANK(*lineptr)) - lineptr++; - - ptr = lineptr; - do { - /* we have a <what>=<this> pair or a stand-alone word here */ - name[0] = what[0] = 0; /* init the buffers */ - if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\t\r\n=] =%" - MAX_NAME_TXT "[^;\r\n]", - name, what)) { - /* - * Use strstore() below to properly deal with received cookie - * headers that have the same string property set more than once, - * and then we use the last one. - */ - const char *whatptr; - bool done = FALSE; - bool sep; - size_t len = strlen(what); - size_t nlen = strlen(name); - const char *endofn = &ptr[ nlen ]; - - /* - * Check for too long individual name or contents, or too long - * combination of name + contents. Chrome and Firefox support 4095 or - * 4096 bytes combo - */ - if(nlen >= (MAX_NAME-1) || len >= (MAX_NAME-1) || - ((nlen + len) > MAX_NAME)) { - freecookie(co); - infof(data, "oversized cookie dropped, name/val %zu + %zu bytes", - nlen, len); - return NULL; - } - - /* name ends with a '=' ? */ - sep = (*endofn == '=')?TRUE:FALSE; - - if(nlen) { - endofn--; /* move to the last character */ - if(ISBLANK(*endofn)) { - /* skip trailing spaces in name */ - while(*endofn && ISBLANK(*endofn) && nlen) { - endofn--; - nlen--; - } - name[nlen] = 0; /* new end of name */ - } - } - - /* Strip off trailing whitespace from the 'what' */ - while(len && ISBLANK(what[len-1])) { - what[len-1] = 0; - len--; - } - - /* Skip leading whitespace from the 'what' */ - whatptr = what; - while(*whatptr && ISBLANK(*whatptr)) - whatptr++; - - /* Reject cookies with a TAB inside the content */ - if(strchr(whatptr, '\t')) { - freecookie(co); - infof(data, "cookie contains TAB, dropping"); - return NULL; - } - - /* - * Check if we have a reserved prefix set before anything else, as we - * otherwise have to test for the prefix in both the cookie name and - * "the rest". Prefixes must start with '__' and end with a '-', so - * only test for names where that can possibly be true. - */ - if(nlen > 3 && name[0] == '_' && name[1] == '_') { - if(!strncmp("__Secure-", name, 9)) - co->prefix |= COOKIE_PREFIX__SECURE; - else if(!strncmp("__Host-", name, 7)) - co->prefix |= COOKIE_PREFIX__HOST; - } - - if(!co->name) { - /* The very first name/value pair is the actual cookie name */ - if(!sep) { - /* Bad name/value pair. */ - badcookie = TRUE; - break; - } - co->name = strdup(name); - co->value = strdup(whatptr); - done = TRUE; - if(!co->name || !co->value) { - badcookie = TRUE; - break; - } - if(invalid_octets(whatptr) || invalid_octets(name)) { - infof(data, "invalid octets in name/value, cookie dropped"); - badcookie = TRUE; - break; - } - } - else if(!len) { - /* - * this was a "<name>=" with no content, and we must allow - * 'secure' and 'httponly' specified this weirdly - */ - done = TRUE; - /* - * secure cookies are only allowed to be set when the connection is - * using a secure protocol, or when the cookie is being set by - * reading from file - */ - if(strcasecompare("secure", name)) { - if(secure || !c->running) { - co->secure = TRUE; - } - else { - badcookie = TRUE; - break; - } - } - else if(strcasecompare("httponly", name)) - co->httponly = TRUE; - else if(sep) - /* there was a '=' so we're not done parsing this field */ - done = FALSE; - } - if(done) - ; - else if(strcasecompare("path", name)) { - strstore(&co->path, whatptr); - if(!co->path) { - badcookie = TRUE; /* out of memory bad */ - break; - } - free(co->spath); /* if this is set again */ - co->spath = sanitize_cookie_path(co->path); - if(!co->spath) { - badcookie = TRUE; /* out of memory bad */ - break; - } - } - else if(strcasecompare("domain", name) && whatptr[0]) { - bool is_ip; - - /* - * Now, we make sure that our host is within the given domain, or - * the given domain is not valid and thus cannot be set. - */ - - if('.' == whatptr[0]) - whatptr++; /* ignore preceding dot */ - -#ifndef USE_LIBPSL - /* - * Without PSL we don't know when the incoming cookie is set on a - * TLD or otherwise "protected" suffix. To reduce risk, we require a - * dot OR the exact host name being "localhost". - */ - if(bad_domain(whatptr)) - domain = ":"; -#endif - - is_ip = Curl_host_is_ipnum(domain ? domain : whatptr); - - if(!domain - || (is_ip && !strcmp(whatptr, domain)) - || (!is_ip && tailmatch(whatptr, domain))) { - strstore(&co->domain, whatptr); - if(!co->domain) { - badcookie = TRUE; - break; - } - if(!is_ip) - co->tailmatch = TRUE; /* we always do that if the domain name was - given */ - } - else { - /* - * We did not get a tailmatch and then the attempted set domain is - * not a domain to which the current host belongs. Mark as bad. - */ - badcookie = TRUE; - infof(data, "skipped cookie with bad tailmatch domain: %s", - whatptr); - } - } - else if(strcasecompare("version", name)) { - strstore(&co->version, whatptr); - if(!co->version) { - badcookie = TRUE; - break; - } - } - else if(strcasecompare("max-age", name)) { - /* - * Defined in RFC2109: - * - * Optional. The Max-Age attribute defines the lifetime of the - * cookie, in seconds. The delta-seconds value is a decimal non- - * negative integer. After delta-seconds seconds elapse, the - * client should discard the cookie. A value of zero means the - * cookie should be discarded immediately. - */ - strstore(&co->maxage, whatptr); - if(!co->maxage) { - badcookie = TRUE; - break; - } - } - else if(strcasecompare("expires", name)) { - strstore(&co->expirestr, whatptr); - if(!co->expirestr) { - badcookie = TRUE; - break; - } - } - - /* - * Else, this is the second (or more) name we don't know about! - */ - } - else { - /* this is an "illegal" <what>=<this> pair */ - } - - if(!semiptr || !*semiptr) { - /* we already know there are no more cookies */ - semiptr = NULL; - continue; - } - - ptr = semiptr + 1; - while(*ptr && ISBLANK(*ptr)) - ptr++; - semiptr = strchr(ptr, ';'); /* now, find the next semicolon */ - - if(!semiptr && *ptr) - /* - * There are no more semicolons, but there's a final name=value pair - * coming up - */ - semiptr = strchr(ptr, '\0'); - } while(semiptr); - - if(co->maxage) { - CURLofft offt; - offt = curlx_strtoofft((*co->maxage == '\"')? - &co->maxage[1]:&co->maxage[0], NULL, 10, - &co->expires); - if(offt == CURL_OFFT_FLOW) - /* overflow, used max value */ - co->expires = CURL_OFF_T_MAX; - else if(!offt) { - if(!co->expires) - /* already expired */ - co->expires = 1; - else if(CURL_OFF_T_MAX - now < co->expires) - /* would overflow */ - co->expires = CURL_OFF_T_MAX; - else - co->expires += now; - } - } - else if(co->expirestr) { - /* - * Note that if the date couldn't get parsed for whatever reason, the - * cookie will be treated as a session cookie - */ - co->expires = Curl_getdate_capped(co->expirestr); - - /* - * Session cookies have expires set to 0 so if we get that back from the - * date parser let's add a second to make it a non-session cookie - */ - if(co->expires == 0) - co->expires = 1; - else if(co->expires < 0) - co->expires = 0; - } - - if(!badcookie && !co->domain) { - if(domain) { - /* no domain was given in the header line, set the default */ - co->domain = strdup(domain); - if(!co->domain) - badcookie = TRUE; - } - } - - if(!badcookie && !co->path && path) { - /* - * No path was given in the header line, set the default. Note that the - * passed-in path to this function MAY have a '?' and following part that - * MUST NOT be stored as part of the path. - */ - char *queryp = strchr(path, '?'); - - /* - * queryp is where the interesting part of the path ends, so now we - * want to the find the last - */ - char *endslash; - if(!queryp) - endslash = strrchr(path, '/'); - else - endslash = memrchr(path, '/', (queryp - path)); - if(endslash) { - size_t pathlen = (endslash-path + 1); /* include end slash */ - co->path = malloc(pathlen + 1); /* one extra for the zero byte */ - if(co->path) { - memcpy(co->path, path, pathlen); - co->path[pathlen] = 0; /* null-terminate */ - co->spath = sanitize_cookie_path(co->path); - if(!co->spath) - badcookie = TRUE; /* out of memory bad */ - } - else - badcookie = TRUE; - } - } - - /* - * If we didn't get a cookie name, or a bad one, the this is an illegal - * line so bail out. - */ - if(badcookie || !co->name) { - freecookie(co); - return NULL; - } - data->req.setcookies++; - } - else { - /* - * This line is NOT a HTTP header style line, we do offer support for - * reading the odd netscape cookies-file format here - */ - char *ptr; - char *firstptr; - char *tok_buf = NULL; - int fields; - - /* - * IE introduced HTTP-only cookies to prevent XSS attacks. Cookies marked - * with httpOnly after the domain name are not accessible from javascripts, - * but since curl does not operate at javascript level, we include them - * anyway. In Firefox's cookie files, these lines are preceded with - * #HttpOnly_ and then everything is as usual, so we skip 10 characters of - * the line.. - */ - if(strncmp(lineptr, "#HttpOnly_", 10) == 0) { - lineptr += 10; - co->httponly = TRUE; - } - - if(lineptr[0]=='#') { - /* don't even try the comments */ - free(co); - return NULL; - } - /* strip off the possible end-of-line characters */ - ptr = strchr(lineptr, '\r'); - if(ptr) - *ptr = 0; /* clear it */ - ptr = strchr(lineptr, '\n'); - if(ptr) - *ptr = 0; /* clear it */ - - firstptr = strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */ - - /* - * Now loop through the fields and init the struct we already have - * allocated - */ - for(ptr = firstptr, fields = 0; ptr && !badcookie; - ptr = strtok_r(NULL, "\t", &tok_buf), fields++) { - switch(fields) { - case 0: - if(ptr[0]=='.') /* skip preceding dots */ - ptr++; - co->domain = strdup(ptr); - if(!co->domain) - badcookie = TRUE; - break; - case 1: - /* - * flag: A TRUE/FALSE value indicating if all machines within a given - * domain can access the variable. Set TRUE when the cookie says - * .domain.com and to false when the domain is complete www.domain.com - */ - co->tailmatch = strcasecompare(ptr, "TRUE")?TRUE:FALSE; - break; - case 2: - /* The file format allows the path field to remain not filled in */ - if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) { - /* only if the path doesn't look like a boolean option! */ - co->path = strdup(ptr); - if(!co->path) - badcookie = TRUE; - else { - co->spath = sanitize_cookie_path(co->path); - if(!co->spath) { - badcookie = TRUE; /* out of memory bad */ - } - } - break; - } - /* this doesn't look like a path, make one up! */ - co->path = strdup("/"); - if(!co->path) - badcookie = TRUE; - co->spath = strdup("/"); - if(!co->spath) - badcookie = TRUE; - fields++; /* add a field and fall down to secure */ - /* FALLTHROUGH */ - case 3: - co->secure = FALSE; - if(strcasecompare(ptr, "TRUE")) { - if(secure || c->running) - co->secure = TRUE; - else - badcookie = TRUE; - } - break; - case 4: - if(curlx_strtoofft(ptr, NULL, 10, &co->expires)) - badcookie = TRUE; - break; - case 5: - co->name = strdup(ptr); - if(!co->name) - badcookie = TRUE; - else { - /* For Netscape file format cookies we check prefix on the name */ - if(strncasecompare("__Secure-", co->name, 9)) - co->prefix |= COOKIE_PREFIX__SECURE; - else if(strncasecompare("__Host-", co->name, 7)) - co->prefix |= COOKIE_PREFIX__HOST; - } - break; - case 6: - co->value = strdup(ptr); - if(!co->value) - badcookie = TRUE; - break; - } - } - if(6 == fields) { - /* we got a cookie with blank contents, fix it */ - co->value = strdup(""); - if(!co->value) - badcookie = TRUE; - else - fields++; - } - - if(!badcookie && (7 != fields)) - /* we did not find the sufficient number of fields */ - badcookie = TRUE; - - if(badcookie) { - freecookie(co); - return NULL; - } - - } - - if(co->prefix & COOKIE_PREFIX__SECURE) { - /* The __Secure- prefix only requires that the cookie be set secure */ - if(!co->secure) { - freecookie(co); - return NULL; - } - } - if(co->prefix & COOKIE_PREFIX__HOST) { - /* - * The __Host- prefix requires the cookie to be secure, have a "/" path - * and not have a domain set. - */ - if(co->secure && co->path && strcmp(co->path, "/") == 0 && !co->tailmatch) - ; - else { - freecookie(co); - return NULL; - } - } - - if(!c->running && /* read from a file */ - c->newsession && /* clean session cookies */ - !co->expires) { /* this is a session cookie since it doesn't expire! */ - freecookie(co); - return NULL; - } - - co->livecookie = c->running; - co->creationtime = ++c->lastct; - - /* - * Now we have parsed the incoming line, we must now check if this supersedes - * an already existing cookie, which it may if the previous have the same - * domain and path as this. - */ - - /* at first, remove expired cookies */ - if(!noexpire) - remove_expired(c); - -#ifdef USE_LIBPSL - /* - * Check if the domain is a Public Suffix and if yes, ignore the cookie. We - * must also check that the data handle isn't NULL since the psl code will - * dereference it. - */ - if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) { - const psl_ctx_t *psl = Curl_psl_use(data); - int acceptable; - - if(psl) { - acceptable = psl_is_cookie_domain_acceptable(psl, domain, co->domain); - Curl_psl_release(data); - } - else - acceptable = !bad_domain(domain); - - if(!acceptable) { - infof(data, "cookie '%s' dropped, domain '%s' must not " - "set cookies for '%s'", co->name, domain, co->domain); - freecookie(co); - return NULL; - } - } -#endif - - /* A non-secure cookie may not overlay an existing secure cookie. */ - myhash = cookiehash(co->domain); - clist = c->cookies[myhash]; - while(clist) { - if(strcasecompare(clist->name, co->name)) { - /* the names are identical */ - bool matching_domains = FALSE; - - if(clist->domain && co->domain) { - if(strcasecompare(clist->domain, co->domain)) - /* The domains are identical */ - matching_domains = TRUE; - } - else if(!clist->domain && !co->domain) - matching_domains = TRUE; - - if(matching_domains && /* the domains were identical */ - clist->spath && co->spath && /* both have paths */ - clist->secure && !co->secure && !secure) { - size_t cllen; - const char *sep; - - /* - * A non-secure cookie may not overlay an existing secure cookie. - * For an existing cookie "a" with path "/login", refuse a new - * cookie "a" with for example path "/login/en", while the path - * "/loginhelper" is ok. - */ - - sep = strchr(clist->spath + 1, '/'); - - if(sep) - cllen = sep - clist->spath; - else - cllen = strlen(clist->spath); - - if(strncasecompare(clist->spath, co->spath, cllen)) { - infof(data, "cookie '%s' for domain '%s' dropped, would " - "overlay an existing cookie", co->name, co->domain); - freecookie(co); - return NULL; - } - } - } - - if(!replace_co && strcasecompare(clist->name, co->name)) { - /* the names are identical */ - - if(clist->domain && co->domain) { - if(strcasecompare(clist->domain, co->domain) && - (clist->tailmatch == co->tailmatch)) - /* The domains are identical */ - replace_old = TRUE; - } - else if(!clist->domain && !co->domain) - replace_old = TRUE; - - if(replace_old) { - /* the domains were identical */ - - if(clist->spath && co->spath) { - if(strcasecompare(clist->spath, co->spath)) - replace_old = TRUE; - else - replace_old = FALSE; - } - else if(!clist->spath && !co->spath) - replace_old = TRUE; - else - replace_old = FALSE; - - } - - if(replace_old && !co->livecookie && clist->livecookie) { - /* - * Both cookies matched fine, except that the already present cookie is - * "live", which means it was set from a header, while the new one was - * read from a file and thus isn't "live". "live" cookies are preferred - * so the new cookie is freed. - */ - freecookie(co); - return NULL; - } - if(replace_old) { - replace_co = co; - replace_clist = clist; - } - } - lastc = clist; - clist = clist->next; - } - if(replace_co) { - co = replace_co; - clist = replace_clist; - co->next = clist->next; /* get the next-pointer first */ - - /* when replacing, creationtime is kept from old */ - co->creationtime = clist->creationtime; - - /* then free all the old pointers */ - free(clist->name); - free(clist->value); - free(clist->domain); - free(clist->path); - free(clist->spath); - free(clist->expirestr); - free(clist->version); - free(clist->maxage); - - *clist = *co; /* then store all the new data */ - - free(co); /* free the newly allocated memory */ - co = clist; - } - - if(c->running) - /* Only show this when NOT reading the cookies from a file */ - infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, " - "expire %" CURL_FORMAT_CURL_OFF_T, - replace_old?"Replaced":"Added", co->name, co->value, - co->domain, co->path, co->expires); - - if(!replace_old) { - /* then make the last item point on this new one */ - if(lastc) - lastc->next = co; - else - c->cookies[myhash] = co; - c->numcookies++; /* one more cookie in the jar */ - } - - /* - * Now that we've added a new cookie to the jar, update the expiration - * tracker in case it is the next one to expire. - */ - if(co->expires && (co->expires < c->next_expiration)) - c->next_expiration = co->expires; - - return co; -} - - -/* - * Curl_cookie_init() - * - * Inits a cookie struct to read data from a local file. This is always - * called before any cookies are set. File may be NULL in which case only the - * struct is initialized. Is file is "-" then STDIN is read. - * - * If 'newsession' is TRUE, discard all "session cookies" on read from file. - * - * Note that 'data' might be called as NULL pointer. - * - * Returns NULL on out of memory. Invalid cookies are ignored. - */ -struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, - const char *file, - struct CookieInfo *inc, - bool newsession) -{ - struct CookieInfo *c; - FILE *fp = NULL; - bool fromfile = TRUE; - char *line = NULL; - - if(!inc) { - /* we didn't get a struct, create one */ - c = calloc(1, sizeof(struct CookieInfo)); - if(!c) - return NULL; /* failed to get memory */ - c->filename = strdup(file?file:"none"); /* copy the name just in case */ - if(!c->filename) - goto fail; /* failed to get memory */ - /* - * Initialize the next_expiration time to signal that we don't have enough - * information yet. - */ - c->next_expiration = CURL_OFF_T_MAX; - } - else { - /* we got an already existing one, use that */ - c = inc; - } - c->running = FALSE; /* this is not running, this is init */ - - if(file && !strcmp(file, "-")) { - fp = stdin; - fromfile = FALSE; - } - else if(!file || !*file) { - /* points to an empty string or NULL */ - fp = NULL; - } - else { - fp = fopen(file, FOPEN_READTEXT); - if(!fp) - infof(data, "WARNING: failed to open cookie file \"%s\"", file); - } - - c->newsession = newsession; /* new session? */ - - if(fp) { - char *lineptr; - bool headerline; - - line = malloc(MAX_COOKIE_LINE); - if(!line) - goto fail; - while(Curl_get_line(line, MAX_COOKIE_LINE, fp)) { - if(checkprefix("Set-Cookie:", line)) { - /* This is a cookie line, get it! */ - lineptr = &line[11]; - headerline = TRUE; - } - else { - lineptr = line; - headerline = FALSE; - } - while(*lineptr && ISBLANK(*lineptr)) - lineptr++; - - Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE); - } - free(line); /* free the line buffer */ - - /* - * Remove expired cookies from the hash. We must make sure to run this - * after reading the file, and not on every cookie. - */ - remove_expired(c); - - if(fromfile && fp) - fclose(fp); - } - - c->running = TRUE; /* now, we're running */ - if(data) - data->state.cookie_engine = TRUE; - - return c; - -fail: - free(line); - /* - * Only clean up if we allocated it here, as the original could still be in - * use by a share handle. - */ - if(!inc) - Curl_cookie_cleanup(c); - if(fromfile && fp) - fclose(fp); - return NULL; /* out of memory */ -} - -/* - * cookie_sort - * - * Helper function to sort cookies such that the longest path gets before the - * shorter path. Path, domain and name lengths are considered in that order, - * with the creationtime as the tiebreaker. The creationtime is guaranteed to - * be unique per cookie, so we know we will get an ordering at that point. - */ -static int cookie_sort(const void *p1, const void *p2) -{ - struct Cookie *c1 = *(struct Cookie **)p1; - struct Cookie *c2 = *(struct Cookie **)p2; - size_t l1, l2; - - /* 1 - compare cookie path lengths */ - l1 = c1->path ? strlen(c1->path) : 0; - l2 = c2->path ? strlen(c2->path) : 0; - - if(l1 != l2) - return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */ - - /* 2 - compare cookie domain lengths */ - l1 = c1->domain ? strlen(c1->domain) : 0; - l2 = c2->domain ? strlen(c2->domain) : 0; - - if(l1 != l2) - return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */ - - /* 3 - compare cookie name lengths */ - l1 = c1->name ? strlen(c1->name) : 0; - l2 = c2->name ? strlen(c2->name) : 0; - - if(l1 != l2) - return (l2 > l1) ? 1 : -1; - - /* 4 - compare cookie creation time */ - return (c2->creationtime > c1->creationtime) ? 1 : -1; -} - -/* - * cookie_sort_ct - * - * Helper function to sort cookies according to creation time. - */ -static int cookie_sort_ct(const void *p1, const void *p2) -{ - struct Cookie *c1 = *(struct Cookie **)p1; - struct Cookie *c2 = *(struct Cookie **)p2; - - return (c2->creationtime > c1->creationtime) ? 1 : -1; -} - -#define CLONE(field) \ - do { \ - if(src->field) { \ - d->field = strdup(src->field); \ - if(!d->field) \ - goto fail; \ - } \ - } while(0) - -static struct Cookie *dup_cookie(struct Cookie *src) -{ - struct Cookie *d = calloc(sizeof(struct Cookie), 1); - if(d) { - CLONE(expirestr); - CLONE(domain); - CLONE(path); - CLONE(spath); - CLONE(name); - CLONE(value); - CLONE(maxage); - CLONE(version); - d->expires = src->expires; - d->tailmatch = src->tailmatch; - d->secure = src->secure; - d->livecookie = src->livecookie; - d->httponly = src->httponly; - d->creationtime = src->creationtime; - } - return d; - - fail: - freecookie(d); - return NULL; -} - -/* - * Curl_cookie_getlist - * - * For a given host and path, return a linked list of cookies that the client - * should send to the server if used now. The secure boolean informs the cookie - * if a secure connection is achieved or not. - * - * It shall only return cookies that haven't expired. - */ -struct Cookie *Curl_cookie_getlist(struct Curl_easy *data, - struct CookieInfo *c, - const char *host, const char *path, - bool secure) -{ - struct Cookie *newco; - struct Cookie *co; - struct Cookie *mainco = NULL; - size_t matches = 0; - bool is_ip; - const size_t myhash = cookiehash(host); - - if(!c || !c->cookies[myhash]) - return NULL; /* no cookie struct or no cookies in the struct */ - - /* at first, remove expired cookies */ - remove_expired(c); - - /* check if host is an IP(v4|v6) address */ - is_ip = Curl_host_is_ipnum(host); - - co = c->cookies[myhash]; - - while(co) { - /* if the cookie requires we're secure we must only continue if we are! */ - if(co->secure?secure:TRUE) { - - /* now check if the domain is correct */ - if(!co->domain || - (co->tailmatch && !is_ip && tailmatch(co->domain, host)) || - ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) { - /* - * the right part of the host matches the domain stuff in the - * cookie data - */ - - /* - * now check the left part of the path with the cookies path - * requirement - */ - if(!co->spath || pathmatch(co->spath, path) ) { - - /* - * and now, we know this is a match and we should create an - * entry for the return-linked-list - */ - - newco = dup_cookie(co); - if(newco) { - /* then modify our next */ - newco->next = mainco; - - /* point the main to us */ - mainco = newco; - - matches++; - if(matches >= MAX_COOKIE_SEND_AMOUNT) { - infof(data, "Included max number of cookies (%zu) in request!", - matches); - break; - } - } - else - goto fail; - } - } - } - co = co->next; - } - - if(matches) { - /* - * Now we need to make sure that if there is a name appearing more than - * once, the longest specified path version comes first. To make this - * the swiftest way, we just sort them all based on path length. - */ - struct Cookie **array; - size_t i; - - /* alloc an array and store all cookie pointers */ - array = malloc(sizeof(struct Cookie *) * matches); - if(!array) - goto fail; - - co = mainco; - - for(i = 0; co; co = co->next) - array[i++] = co; - - /* now sort the cookie pointers in path length order */ - qsort(array, matches, sizeof(struct Cookie *), cookie_sort); - - /* remake the linked list order according to the new order */ - - mainco = array[0]; /* start here */ - for(i = 0; i<matches-1; i++) - array[i]->next = array[i + 1]; - array[matches-1]->next = NULL; /* terminate the list */ - - free(array); /* remove the temporary data again */ - } - - return mainco; /* return the new list */ - -fail: - /* failure, clear up the allocated chain and return NULL */ - Curl_cookie_freelist(mainco); - return NULL; -} - -/* - * Curl_cookie_clearall - * - * Clear all existing cookies and reset the counter. - */ -void Curl_cookie_clearall(struct CookieInfo *cookies) -{ - if(cookies) { - unsigned int i; - for(i = 0; i < COOKIE_HASH_SIZE; i++) { - Curl_cookie_freelist(cookies->cookies[i]); - cookies->cookies[i] = NULL; - } - cookies->numcookies = 0; - } -} - -/* - * Curl_cookie_freelist - * - * Free a list of cookies previously returned by Curl_cookie_getlist(); - */ -void Curl_cookie_freelist(struct Cookie *co) -{ - struct Cookie *next; - while(co) { - next = co->next; - freecookie(co); - co = next; - } -} - -/* - * Curl_cookie_clearsess - * - * Free all session cookies in the cookies list. - */ -void Curl_cookie_clearsess(struct CookieInfo *cookies) -{ - struct Cookie *first, *curr, *next, *prev = NULL; - unsigned int i; - - if(!cookies) - return; - - for(i = 0; i < COOKIE_HASH_SIZE; i++) { - if(!cookies->cookies[i]) - continue; - - first = curr = prev = cookies->cookies[i]; - - for(; curr; curr = next) { - next = curr->next; - if(!curr->expires) { - if(first == curr) - first = next; - - if(prev == curr) - prev = next; - else - prev->next = next; - - freecookie(curr); - cookies->numcookies--; - } - else - prev = curr; - } - - cookies->cookies[i] = first; - } -} - -/* - * Curl_cookie_cleanup() - * - * Free a "cookie object" previous created with Curl_cookie_init(). - */ -void Curl_cookie_cleanup(struct CookieInfo *c) -{ - if(c) { - unsigned int i; - free(c->filename); - for(i = 0; i < COOKIE_HASH_SIZE; i++) - Curl_cookie_freelist(c->cookies[i]); - free(c); /* free the base struct as well */ - } -} - -/* - * get_netscape_format() - * - * Formats a string for Netscape output file, w/o a newline at the end. - * Function returns a char * to a formatted line. The caller is responsible - * for freeing the returned pointer. - */ -static char *get_netscape_format(const struct Cookie *co) -{ - return aprintf( - "%s" /* httponly preamble */ - "%s%s\t" /* domain */ - "%s\t" /* tailmatch */ - "%s\t" /* path */ - "%s\t" /* secure */ - "%" CURL_FORMAT_CURL_OFF_T "\t" /* expires */ - "%s\t" /* name */ - "%s", /* value */ - co->httponly?"#HttpOnly_":"", - /* - * Make sure all domains are prefixed with a dot if they allow - * tailmatching. This is Mozilla-style. - */ - (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"", - co->domain?co->domain:"unknown", - co->tailmatch?"TRUE":"FALSE", - co->path?co->path:"/", - co->secure?"TRUE":"FALSE", - co->expires, - co->name, - co->value?co->value:""); -} - -/* - * cookie_output() - * - * Writes all internally known cookies to the specified file. Specify - * "-" as file name to write to stdout. - * - * The function returns non-zero on write failure. - */ -static CURLcode cookie_output(struct Curl_easy *data, - struct CookieInfo *c, const char *filename) -{ - struct Cookie *co; - FILE *out = NULL; - bool use_stdout = FALSE; - char *tempstore = NULL; - CURLcode error = CURLE_OK; - - if(!c) - /* no cookie engine alive */ - return CURLE_OK; - - /* at first, remove expired cookies */ - remove_expired(c); - - if(!strcmp("-", filename)) { - /* use stdout */ - out = stdout; - use_stdout = TRUE; - } - else { - error = Curl_fopen(data, filename, &out, &tempstore); - if(error) - goto error; - } - - fputs("# Netscape HTTP Cookie File\n" - "# https://curl.se/docs/http-cookies.html\n" - "# This file was generated by libcurl! Edit at your own risk.\n\n", - out); - - if(c->numcookies) { - unsigned int i; - size_t nvalid = 0; - struct Cookie **array; - - array = calloc(1, sizeof(struct Cookie *) * c->numcookies); - if(!array) { - error = CURLE_OUT_OF_MEMORY; - goto error; - } - - /* only sort the cookies with a domain property */ - for(i = 0; i < COOKIE_HASH_SIZE; i++) { - for(co = c->cookies[i]; co; co = co->next) { - if(!co->domain) - continue; - array[nvalid++] = co; - } - } - - qsort(array, nvalid, sizeof(struct Cookie *), cookie_sort_ct); - - for(i = 0; i < nvalid; i++) { - char *format_ptr = get_netscape_format(array[i]); - if(!format_ptr) { - free(array); - error = CURLE_OUT_OF_MEMORY; - goto error; - } - fprintf(out, "%s\n", format_ptr); - free(format_ptr); - } - - free(array); - } - - if(!use_stdout) { - fclose(out); - out = NULL; - if(tempstore && Curl_rename(tempstore, filename)) { - unlink(tempstore); - error = CURLE_WRITE_ERROR; - goto error; - } - } - - /* - * If we reach here we have successfully written a cookie file so theree is - * no need to inspect the error, any error case should have jumped into the - * error block below. - */ - free(tempstore); - return CURLE_OK; - -error: - if(out && !use_stdout) - fclose(out); - free(tempstore); - return error; -} - -static struct curl_slist *cookie_list(struct Curl_easy *data) -{ - struct curl_slist *list = NULL; - struct curl_slist *beg; - struct Cookie *c; - char *line; - unsigned int i; - - if(!data->cookies || (data->cookies->numcookies == 0)) - return NULL; - - for(i = 0; i < COOKIE_HASH_SIZE; i++) { - for(c = data->cookies->cookies[i]; c; c = c->next) { - if(!c->domain) - continue; - line = get_netscape_format(c); - if(!line) { - curl_slist_free_all(list); - return NULL; - } - beg = Curl_slist_append_nodup(list, line); - if(!beg) { - free(line); - curl_slist_free_all(list); - return NULL; - } - list = beg; - } - } - - return list; -} - -struct curl_slist *Curl_cookie_list(struct Curl_easy *data) -{ - struct curl_slist *list; - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - list = cookie_list(data); - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - return list; -} - -void Curl_flush_cookies(struct Curl_easy *data, bool cleanup) -{ - CURLcode res; - - if(data->set.str[STRING_COOKIEJAR]) { - if(data->state.cookielist) { - /* If there is a list of cookie files to read, do it first so that - we have all the told files read before we write the new jar. - Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */ - Curl_cookie_loadfiles(data); - } - - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - - /* if we have a destination file for all the cookies to get dumped to */ - res = cookie_output(data, data->cookies, data->set.str[STRING_COOKIEJAR]); - if(res) - infof(data, "WARNING: failed to save cookies in %s: %s", - data->set.str[STRING_COOKIEJAR], curl_easy_strerror(res)); - } - else { - if(cleanup && data->state.cookielist) { - /* since nothing is written, we can just free the list of cookie file - names */ - curl_slist_free_all(data->state.cookielist); /* clean up list */ - data->state.cookielist = NULL; - } - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - } - - if(cleanup && (!data->share || (data->cookies != data->share->cookies))) { - Curl_cookie_cleanup(data->cookies); - data->cookies = NULL; - } - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); -} - -#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+/***
+
+
+RECEIVING COOKIE INFORMATION
+============================
+
+struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
+ const char *file, struct CookieInfo *inc, bool newsession);
+
+ Inits a cookie struct to store data in a local file. This is always
+ called before any cookies are set.
+
+struct Cookie *Curl_cookie_add(struct Curl_easy *data,
+ struct CookieInfo *c, bool httpheader, bool noexpire,
+ char *lineptr, const char *domain, const char *path,
+ bool secure);
+
+ The 'lineptr' parameter is a full "Set-cookie:" line as
+ received from a server.
+
+ The function need to replace previously stored lines that this new
+ line supersedes.
+
+ It may remove lines that are expired.
+
+ It should return an indication of success/error.
+
+
+SENDING COOKIE INFORMATION
+==========================
+
+struct Cookies *Curl_cookie_getlist(struct CookieInfo *cookie,
+ char *host, char *path, bool secure);
+
+ For a given host and path, return a linked list of cookies that
+ the client should send to the server if used now. The secure
+ boolean informs the cookie if a secure connection is achieved or
+ not.
+
+ It shall only return cookies that haven't expired.
+
+
+Example set of cookies:
+
+ Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure
+ Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
+ domain=.fidelity.com; path=/ftgw; secure
+ Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
+ domain=.fidelity.com; path=/; secure
+ Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
+ domain=.fidelity.com; path=/; secure
+ Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
+ domain=.fidelity.com; path=/; secure
+ Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
+ domain=.fidelity.com; path=/; secure
+ Set-cookie:
+ Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday,
+ 13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure
+****/
+
+
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+
+#include "urldata.h"
+#include "cookie.h"
+#include "psl.h"
+#include "strtok.h"
+#include "sendf.h"
+#include "slist.h"
+#include "share.h"
+#include "strtoofft.h"
+#include "strcase.h"
+#include "curl_get_line.h"
+#include "curl_memrchr.h"
+#include "parsedate.h"
+#include "rename.h"
+#include "fopen.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+static void strstore(char **str, const char *newstr);
+
+static void freecookie(struct Cookie *co)
+{
+ free(co->expirestr);
+ free(co->domain);
+ free(co->path);
+ free(co->spath);
+ free(co->name);
+ free(co->value);
+ free(co->maxage);
+ free(co->version);
+ free(co);
+}
+
+static bool tailmatch(const char *cooke_domain, const char *hostname)
+{
+ size_t cookie_domain_len = strlen(cooke_domain);
+ size_t hostname_len = strlen(hostname);
+
+ if(hostname_len < cookie_domain_len)
+ return FALSE;
+
+ if(!strcasecompare(cooke_domain, hostname + hostname_len-cookie_domain_len))
+ return FALSE;
+
+ /*
+ * A lead char of cookie_domain is not '.'.
+ * RFC6265 4.1.2.3. The Domain Attribute says:
+ * For example, if the value of the Domain attribute is
+ * "example.com", the user agent will include the cookie in the Cookie
+ * header when making HTTP requests to example.com, www.example.com, and
+ * www.corp.example.com.
+ */
+ if(hostname_len == cookie_domain_len)
+ return TRUE;
+ if('.' == *(hostname + hostname_len - cookie_domain_len - 1))
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * matching cookie path and url path
+ * RFC6265 5.1.4 Paths and Path-Match
+ */
+static bool pathmatch(const char *cookie_path, const char *request_uri)
+{
+ size_t cookie_path_len;
+ size_t uri_path_len;
+ char *uri_path = NULL;
+ char *pos;
+ bool ret = FALSE;
+
+ /* cookie_path must not have last '/' separator. ex: /sample */
+ cookie_path_len = strlen(cookie_path);
+ if(1 == cookie_path_len) {
+ /* cookie_path must be '/' */
+ return TRUE;
+ }
+
+ uri_path = strdup(request_uri);
+ if(!uri_path)
+ return FALSE;
+ pos = strchr(uri_path, '?');
+ if(pos)
+ *pos = 0x0;
+
+ /* #-fragments are already cut off! */
+ if(0 == strlen(uri_path) || uri_path[0] != '/') {
+ strstore(&uri_path, "/");
+ if(!uri_path)
+ return FALSE;
+ }
+
+ /*
+ * here, RFC6265 5.1.4 says
+ * 4. Output the characters of the uri-path from the first character up
+ * to, but not including, the right-most %x2F ("/").
+ * but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site
+ * without redirect.
+ * Ignore this algorithm because /hoge is uri path for this case
+ * (uri path is not /).
+ */
+
+ uri_path_len = strlen(uri_path);
+
+ if(uri_path_len < cookie_path_len) {
+ ret = FALSE;
+ goto pathmatched;
+ }
+
+ /* not using checkprefix() because matching should be case-sensitive */
+ if(strncmp(cookie_path, uri_path, cookie_path_len)) {
+ ret = FALSE;
+ goto pathmatched;
+ }
+
+ /* The cookie-path and the uri-path are identical. */
+ if(cookie_path_len == uri_path_len) {
+ ret = TRUE;
+ goto pathmatched;
+ }
+
+ /* here, cookie_path_len < uri_path_len */
+ if(uri_path[cookie_path_len] == '/') {
+ ret = TRUE;
+ goto pathmatched;
+ }
+
+ ret = FALSE;
+
+pathmatched:
+ free(uri_path);
+ return ret;
+}
+
+/*
+ * Return the top-level domain, for optimal hashing.
+ */
+static const char *get_top_domain(const char * const domain, size_t *outlen)
+{
+ size_t len = 0;
+ const char *first = NULL, *last;
+
+ if(domain) {
+ len = strlen(domain);
+ last = memrchr(domain, '.', len);
+ if(last) {
+ first = memrchr(domain, '.', (last - domain));
+ if(first)
+ len -= (++first - domain);
+ }
+ }
+
+ if(outlen)
+ *outlen = len;
+
+ return first? first: domain;
+}
+
+/* Avoid C1001, an "internal error" with MSVC14 */
+#if defined(_MSC_VER) && (_MSC_VER == 1900)
+#pragma optimize("", off)
+#endif
+
+/*
+ * A case-insensitive hash for the cookie domains.
+ */
+static size_t cookie_hash_domain(const char *domain, const size_t len)
+{
+ const char *end = domain + len;
+ size_t h = 5381;
+
+ while(domain < end) {
+ h += h << 5;
+ h ^= Curl_raw_toupper(*domain++);
+ }
+
+ return (h % COOKIE_HASH_SIZE);
+}
+
+#if defined(_MSC_VER) && (_MSC_VER == 1900)
+#pragma optimize("", on)
+#endif
+
+/*
+ * Hash this domain.
+ */
+static size_t cookiehash(const char * const domain)
+{
+ const char *top;
+ size_t len;
+
+ if(!domain || Curl_host_is_ipnum(domain))
+ return 0;
+
+ top = get_top_domain(domain, &len);
+ return cookie_hash_domain(top, len);
+}
+
+/*
+ * cookie path sanitize
+ */
+static char *sanitize_cookie_path(const char *cookie_path)
+{
+ size_t len;
+ char *new_path = strdup(cookie_path);
+ if(!new_path)
+ return NULL;
+
+ /* some stupid site sends path attribute with '"'. */
+ len = strlen(new_path);
+ if(new_path[0] == '\"') {
+ memmove(new_path, new_path + 1, len);
+ len--;
+ }
+ if(len && (new_path[len - 1] == '\"')) {
+ new_path[--len] = 0x0;
+ }
+
+ /* RFC6265 5.2.4 The Path Attribute */
+ if(new_path[0] != '/') {
+ /* Let cookie-path be the default-path. */
+ strstore(&new_path, "/");
+ return new_path;
+ }
+
+ /* convert /hoge/ to /hoge */
+ if(len && new_path[len - 1] == '/') {
+ new_path[len - 1] = 0x0;
+ }
+
+ return new_path;
+}
+
+/*
+ * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
+ *
+ * NOTE: OOM or cookie parsing failures are ignored.
+ */
+void Curl_cookie_loadfiles(struct Curl_easy *data)
+{
+ struct curl_slist *list = data->state.cookielist;
+ if(list) {
+ Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
+ while(list) {
+ struct CookieInfo *newcookies = Curl_cookie_init(data,
+ list->data,
+ data->cookies,
+ data->set.cookiesession);
+ if(!newcookies)
+ /*
+ * Failure may be due to OOM or a bad cookie; both are ignored
+ * but only the first should be
+ */
+ infof(data, "ignoring failed cookie_init for %s", list->data);
+ else
+ data->cookies = newcookies;
+ list = list->next;
+ }
+ curl_slist_free_all(data->state.cookielist); /* clean up list */
+ data->state.cookielist = NULL; /* don't do this again! */
+ Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
+ }
+}
+
+/*
+ * strstore
+ *
+ * A thin wrapper around strdup which ensures that any memory allocated at
+ * *str will be freed before the string allocated by strdup is stored there.
+ * The intended usecase is repeated assignments to the same variable during
+ * parsing in a last-wins scenario. The caller is responsible for checking
+ * for OOM errors.
+ */
+static void strstore(char **str, const char *newstr)
+{
+ free(*str);
+ *str = strdup(newstr);
+}
+
+/*
+ * remove_expired
+ *
+ * Remove expired cookies from the hash by inspecting the expires timestamp on
+ * each cookie in the hash, freeing and deleting any where the timestamp is in
+ * the past. If the cookiejar has recorded the next timestamp at which one or
+ * more cookies expire, then processing will exit early in case this timestamp
+ * is in the future.
+ */
+static void remove_expired(struct CookieInfo *cookies)
+{
+ struct Cookie *co, *nx;
+ curl_off_t now = (curl_off_t)time(NULL);
+ unsigned int i;
+
+ /*
+ * If the earliest expiration timestamp in the jar is in the future we can
+ * skip scanning the whole jar and instead exit early as there won't be any
+ * cookies to evict. If we need to evict however, reset the next_expiration
+ * counter in order to track the next one. In case the recorded first
+ * expiration is the max offset, then perform the safe fallback of checking
+ * all cookies.
+ */
+ if(now < cookies->next_expiration &&
+ cookies->next_expiration != CURL_OFF_T_MAX)
+ return;
+ else
+ cookies->next_expiration = CURL_OFF_T_MAX;
+
+ for(i = 0; i < COOKIE_HASH_SIZE; i++) {
+ struct Cookie *pv = NULL;
+ co = cookies->cookies[i];
+ while(co) {
+ nx = co->next;
+ if(co->expires && co->expires < now) {
+ if(!pv) {
+ cookies->cookies[i] = co->next;
+ }
+ else {
+ pv->next = co->next;
+ }
+ cookies->numcookies--;
+ freecookie(co);
+ }
+ else {
+ /*
+ * If this cookie has an expiration timestamp earlier than what we've
+ * seen so far then record it for the next round of expirations.
+ */
+ if(co->expires && co->expires < cookies->next_expiration)
+ cookies->next_expiration = co->expires;
+ pv = co;
+ }
+ co = nx;
+ }
+ }
+}
+
+/* Make sure domain contains a dot or is localhost. */
+static bool bad_domain(const char *domain)
+{
+ if(strcasecompare(domain, "localhost"))
+ return FALSE;
+ else {
+ /* there must be a dot present, but that dot must not be a trailing dot */
+ char *dot = strchr(domain, '.');
+ if(dot)
+ return dot[1] ? FALSE : TRUE;
+ }
+ return TRUE;
+}
+
+/*
+ RFC 6265 section 4.1.1 says a server should accept this range:
+
+ cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
+
+ But Firefox and Chrome as of June 2022 accept space, comma and double-quotes
+ fine. The prime reason for filtering out control bytes is that some HTTP
+ servers return 400 for requests that contain such.
+*/
+static int invalid_octets(const char *p)
+{
+ /* Reject all bytes \x01 - \x1f (*except* \x09, TAB) + \x7f */
+ static const char badoctets[] = {
+ "\x01\x02\x03\x04\x05\x06\x07\x08\x0a"
+ "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
+ "\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f"
+ };
+ size_t len;
+ /* scan for all the octets that are *not* in cookie-octet */
+ len = strcspn(p, badoctets);
+ return (p[len] != '\0');
+}
+
+/*
+ * Curl_cookie_add
+ *
+ * Add a single cookie line to the cookie keeping object. Be aware that
+ * sometimes we get an IP-only host name, and that might also be a numerical
+ * IPv6 address.
+ *
+ * Returns NULL on out of memory or invalid cookie. This is suboptimal,
+ * as they should be treated separately.
+ */
+struct Cookie *
+Curl_cookie_add(struct Curl_easy *data,
+ /*
+ * The 'data' pointer here may be NULL at times, and thus
+ * must only be used very carefully for things that can deal
+ * with data being NULL. Such as infof() and similar
+ */
+ struct CookieInfo *c,
+ bool httpheader, /* TRUE if HTTP header-style line */
+ bool noexpire, /* if TRUE, skip remove_expired() */
+ char *lineptr, /* first character of the line */
+ const char *domain, /* default domain */
+ const char *path, /* full path used when this cookie is set,
+ used to get default path for the cookie
+ unless set */
+ bool secure) /* TRUE if connection is over secure origin */
+{
+ struct Cookie *clist;
+ struct Cookie *co;
+ struct Cookie *lastc = NULL;
+ struct Cookie *replace_co = NULL;
+ struct Cookie *replace_clist = NULL;
+ time_t now = time(NULL);
+ bool replace_old = FALSE;
+ bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
+ size_t myhash;
+
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+ (void)data;
+#endif
+
+ DEBUGASSERT(MAX_SET_COOKIE_AMOUNT <= 255); /* counter is an unsigned char */
+ if(data->req.setcookies >= MAX_SET_COOKIE_AMOUNT)
+ return NULL;
+
+ /* First, alloc and init a new struct for it */
+ co = calloc(1, sizeof(struct Cookie));
+ if(!co)
+ return NULL; /* bail out if we're this low on memory */
+
+ if(httpheader) {
+ /* This line was read off an HTTP-header */
+ char name[MAX_NAME];
+ char what[MAX_NAME];
+ const char *ptr;
+ const char *semiptr;
+
+ size_t linelength = strlen(lineptr);
+ if(linelength > MAX_COOKIE_LINE) {
+ /* discard overly long lines at once */
+ free(co);
+ return NULL;
+ }
+
+ semiptr = strchr(lineptr, ';'); /* first, find a semicolon */
+
+ while(*lineptr && ISBLANK(*lineptr))
+ lineptr++;
+
+ ptr = lineptr;
+ do {
+ /* we have a <what>=<this> pair or a stand-alone word here */
+ name[0] = what[0] = 0; /* init the buffers */
+ if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\t\r\n=] =%"
+ MAX_NAME_TXT "[^;\r\n]",
+ name, what)) {
+ /*
+ * Use strstore() below to properly deal with received cookie
+ * headers that have the same string property set more than once,
+ * and then we use the last one.
+ */
+ const char *whatptr;
+ bool done = FALSE;
+ bool sep;
+ size_t len = strlen(what);
+ size_t nlen = strlen(name);
+ const char *endofn = &ptr[ nlen ];
+
+ /*
+ * Check for too long individual name or contents, or too long
+ * combination of name + contents. Chrome and Firefox support 4095 or
+ * 4096 bytes combo
+ */
+ if(nlen >= (MAX_NAME-1) || len >= (MAX_NAME-1) ||
+ ((nlen + len) > MAX_NAME)) {
+ freecookie(co);
+ infof(data, "oversized cookie dropped, name/val %zu + %zu bytes",
+ nlen, len);
+ return NULL;
+ }
+
+ /* name ends with a '=' ? */
+ sep = (*endofn == '=')?TRUE:FALSE;
+
+ if(nlen) {
+ endofn--; /* move to the last character */
+ if(ISBLANK(*endofn)) {
+ /* skip trailing spaces in name */
+ while(*endofn && ISBLANK(*endofn) && nlen) {
+ endofn--;
+ nlen--;
+ }
+ name[nlen] = 0; /* new end of name */
+ }
+ }
+
+ /* Strip off trailing whitespace from the 'what' */
+ while(len && ISBLANK(what[len-1])) {
+ what[len-1] = 0;
+ len--;
+ }
+
+ /* Skip leading whitespace from the 'what' */
+ whatptr = what;
+ while(*whatptr && ISBLANK(*whatptr))
+ whatptr++;
+
+ /* Reject cookies with a TAB inside the content */
+ if(strchr(whatptr, '\t')) {
+ freecookie(co);
+ infof(data, "cookie contains TAB, dropping");
+ return NULL;
+ }
+
+ /*
+ * Check if we have a reserved prefix set before anything else, as we
+ * otherwise have to test for the prefix in both the cookie name and
+ * "the rest". Prefixes must start with '__' and end with a '-', so
+ * only test for names where that can possibly be true.
+ */
+ if(nlen > 3 && name[0] == '_' && name[1] == '_') {
+ if(strncasecompare("__Secure-", name, 9))
+ co->prefix |= COOKIE_PREFIX__SECURE;
+ else if(strncasecompare("__Host-", name, 7))
+ co->prefix |= COOKIE_PREFIX__HOST;
+ }
+
+ if(!co->name) {
+ /* The very first name/value pair is the actual cookie name */
+ if(!sep) {
+ /* Bad name/value pair. */
+ badcookie = TRUE;
+ break;
+ }
+ co->name = strdup(name);
+ co->value = strdup(whatptr);
+ done = TRUE;
+ if(!co->name || !co->value) {
+ badcookie = TRUE;
+ break;
+ }
+ if(invalid_octets(whatptr) || invalid_octets(name)) {
+ infof(data, "invalid octets in name/value, cookie dropped");
+ badcookie = TRUE;
+ break;
+ }
+ }
+ else if(!len) {
+ /*
+ * this was a "<name>=" with no content, and we must allow
+ * 'secure' and 'httponly' specified this weirdly
+ */
+ done = TRUE;
+ /*
+ * secure cookies are only allowed to be set when the connection is
+ * using a secure protocol, or when the cookie is being set by
+ * reading from file
+ */
+ if(strcasecompare("secure", name)) {
+ if(secure || !c->running) {
+ co->secure = TRUE;
+ }
+ else {
+ badcookie = TRUE;
+ break;
+ }
+ }
+ else if(strcasecompare("httponly", name))
+ co->httponly = TRUE;
+ else if(sep)
+ /* there was a '=' so we're not done parsing this field */
+ done = FALSE;
+ }
+ if(done)
+ ;
+ else if(strcasecompare("path", name)) {
+ strstore(&co->path, whatptr);
+ if(!co->path) {
+ badcookie = TRUE; /* out of memory bad */
+ break;
+ }
+ free(co->spath); /* if this is set again */
+ co->spath = sanitize_cookie_path(co->path);
+ if(!co->spath) {
+ badcookie = TRUE; /* out of memory bad */
+ break;
+ }
+ }
+ else if(strcasecompare("domain", name) && whatptr[0]) {
+ bool is_ip;
+
+ /*
+ * Now, we make sure that our host is within the given domain, or
+ * the given domain is not valid and thus cannot be set.
+ */
+
+ if('.' == whatptr[0])
+ whatptr++; /* ignore preceding dot */
+
+#ifndef USE_LIBPSL
+ /*
+ * Without PSL we don't know when the incoming cookie is set on a
+ * TLD or otherwise "protected" suffix. To reduce risk, we require a
+ * dot OR the exact host name being "localhost".
+ */
+ if(bad_domain(whatptr))
+ domain = ":";
+#endif
+
+ is_ip = Curl_host_is_ipnum(domain ? domain : whatptr);
+
+ if(!domain
+ || (is_ip && !strcmp(whatptr, domain))
+ || (!is_ip && tailmatch(whatptr, domain))) {
+ strstore(&co->domain, whatptr);
+ if(!co->domain) {
+ badcookie = TRUE;
+ break;
+ }
+ if(!is_ip)
+ co->tailmatch = TRUE; /* we always do that if the domain name was
+ given */
+ }
+ else {
+ /*
+ * We did not get a tailmatch and then the attempted set domain is
+ * not a domain to which the current host belongs. Mark as bad.
+ */
+ badcookie = TRUE;
+ infof(data, "skipped cookie with bad tailmatch domain: %s",
+ whatptr);
+ }
+ }
+ else if(strcasecompare("version", name)) {
+ strstore(&co->version, whatptr);
+ if(!co->version) {
+ badcookie = TRUE;
+ break;
+ }
+ }
+ else if(strcasecompare("max-age", name)) {
+ /*
+ * Defined in RFC2109:
+ *
+ * Optional. The Max-Age attribute defines the lifetime of the
+ * cookie, in seconds. The delta-seconds value is a decimal non-
+ * negative integer. After delta-seconds seconds elapse, the
+ * client should discard the cookie. A value of zero means the
+ * cookie should be discarded immediately.
+ */
+ strstore(&co->maxage, whatptr);
+ if(!co->maxage) {
+ badcookie = TRUE;
+ break;
+ }
+ }
+ else if(strcasecompare("expires", name)) {
+ strstore(&co->expirestr, whatptr);
+ if(!co->expirestr) {
+ badcookie = TRUE;
+ break;
+ }
+ }
+
+ /*
+ * Else, this is the second (or more) name we don't know about!
+ */
+ }
+ else {
+ /* this is an "illegal" <what>=<this> pair */
+ }
+
+ if(!semiptr || !*semiptr) {
+ /* we already know there are no more cookies */
+ semiptr = NULL;
+ continue;
+ }
+
+ ptr = semiptr + 1;
+ while(*ptr && ISBLANK(*ptr))
+ ptr++;
+ semiptr = strchr(ptr, ';'); /* now, find the next semicolon */
+
+ if(!semiptr && *ptr)
+ /*
+ * There are no more semicolons, but there's a final name=value pair
+ * coming up
+ */
+ semiptr = strchr(ptr, '\0');
+ } while(semiptr);
+
+ if(co->maxage) {
+ CURLofft offt;
+ offt = curlx_strtoofft((*co->maxage == '\"')?
+ &co->maxage[1]:&co->maxage[0], NULL, 10,
+ &co->expires);
+ switch(offt) {
+ case CURL_OFFT_FLOW:
+ /* overflow, used max value */
+ co->expires = CURL_OFF_T_MAX;
+ break;
+ case CURL_OFFT_INVAL:
+ /* negative or otherwise bad, expire */
+ co->expires = 1;
+ break;
+ case CURL_OFFT_OK:
+ if(!co->expires)
+ /* already expired */
+ co->expires = 1;
+ else if(CURL_OFF_T_MAX - now < co->expires)
+ /* would overflow */
+ co->expires = CURL_OFF_T_MAX;
+ else
+ co->expires += now;
+ break;
+ }
+ }
+ else if(co->expirestr) {
+ /*
+ * Note that if the date couldn't get parsed for whatever reason, the
+ * cookie will be treated as a session cookie
+ */
+ co->expires = Curl_getdate_capped(co->expirestr);
+
+ /*
+ * Session cookies have expires set to 0 so if we get that back from the
+ * date parser let's add a second to make it a non-session cookie
+ */
+ if(co->expires == 0)
+ co->expires = 1;
+ else if(co->expires < 0)
+ co->expires = 0;
+ }
+
+ if(!badcookie && !co->domain) {
+ if(domain) {
+ /* no domain was given in the header line, set the default */
+ co->domain = strdup(domain);
+ if(!co->domain)
+ badcookie = TRUE;
+ }
+ }
+
+ if(!badcookie && !co->path && path) {
+ /*
+ * No path was given in the header line, set the default. Note that the
+ * passed-in path to this function MAY have a '?' and following part that
+ * MUST NOT be stored as part of the path.
+ */
+ char *queryp = strchr(path, '?');
+
+ /*
+ * queryp is where the interesting part of the path ends, so now we
+ * want to the find the last
+ */
+ char *endslash;
+ if(!queryp)
+ endslash = strrchr(path, '/');
+ else
+ endslash = memrchr(path, '/', (queryp - path));
+ if(endslash) {
+ size_t pathlen = (endslash-path + 1); /* include end slash */
+ co->path = malloc(pathlen + 1); /* one extra for the zero byte */
+ if(co->path) {
+ memcpy(co->path, path, pathlen);
+ co->path[pathlen] = 0; /* null-terminate */
+ co->spath = sanitize_cookie_path(co->path);
+ if(!co->spath)
+ badcookie = TRUE; /* out of memory bad */
+ }
+ else
+ badcookie = TRUE;
+ }
+ }
+
+ /*
+ * If we didn't get a cookie name, or a bad one, the this is an illegal
+ * line so bail out.
+ */
+ if(badcookie || !co->name) {
+ freecookie(co);
+ return NULL;
+ }
+ data->req.setcookies++;
+ }
+ else {
+ /*
+ * This line is NOT an HTTP header style line, we do offer support for
+ * reading the odd netscape cookies-file format here
+ */
+ char *ptr;
+ char *firstptr;
+ char *tok_buf = NULL;
+ int fields;
+
+ /*
+ * IE introduced HTTP-only cookies to prevent XSS attacks. Cookies marked
+ * with httpOnly after the domain name are not accessible from javascripts,
+ * but since curl does not operate at javascript level, we include them
+ * anyway. In Firefox's cookie files, these lines are preceded with
+ * #HttpOnly_ and then everything is as usual, so we skip 10 characters of
+ * the line..
+ */
+ if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
+ lineptr += 10;
+ co->httponly = TRUE;
+ }
+
+ if(lineptr[0]=='#') {
+ /* don't even try the comments */
+ free(co);
+ return NULL;
+ }
+ /* strip off the possible end-of-line characters */
+ ptr = strchr(lineptr, '\r');
+ if(ptr)
+ *ptr = 0; /* clear it */
+ ptr = strchr(lineptr, '\n');
+ if(ptr)
+ *ptr = 0; /* clear it */
+
+ firstptr = strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
+
+ /*
+ * Now loop through the fields and init the struct we already have
+ * allocated
+ */
+ for(ptr = firstptr, fields = 0; ptr && !badcookie;
+ ptr = strtok_r(NULL, "\t", &tok_buf), fields++) {
+ switch(fields) {
+ case 0:
+ if(ptr[0]=='.') /* skip preceding dots */
+ ptr++;
+ co->domain = strdup(ptr);
+ if(!co->domain)
+ badcookie = TRUE;
+ break;
+ case 1:
+ /*
+ * flag: A TRUE/FALSE value indicating if all machines within a given
+ * domain can access the variable. Set TRUE when the cookie says
+ * .domain.com and to false when the domain is complete www.domain.com
+ */
+ co->tailmatch = strcasecompare(ptr, "TRUE")?TRUE:FALSE;
+ break;
+ case 2:
+ /* The file format allows the path field to remain not filled in */
+ if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
+ /* only if the path doesn't look like a boolean option! */
+ co->path = strdup(ptr);
+ if(!co->path)
+ badcookie = TRUE;
+ else {
+ co->spath = sanitize_cookie_path(co->path);
+ if(!co->spath) {
+ badcookie = TRUE; /* out of memory bad */
+ }
+ }
+ break;
+ }
+ /* this doesn't look like a path, make one up! */
+ co->path = strdup("/");
+ if(!co->path)
+ badcookie = TRUE;
+ co->spath = strdup("/");
+ if(!co->spath)
+ badcookie = TRUE;
+ fields++; /* add a field and fall down to secure */
+ /* FALLTHROUGH */
+ case 3:
+ co->secure = FALSE;
+ if(strcasecompare(ptr, "TRUE")) {
+ if(secure || c->running)
+ co->secure = TRUE;
+ else
+ badcookie = TRUE;
+ }
+ break;
+ case 4:
+ if(curlx_strtoofft(ptr, NULL, 10, &co->expires))
+ badcookie = TRUE;
+ break;
+ case 5:
+ co->name = strdup(ptr);
+ if(!co->name)
+ badcookie = TRUE;
+ else {
+ /* For Netscape file format cookies we check prefix on the name */
+ if(strncasecompare("__Secure-", co->name, 9))
+ co->prefix |= COOKIE_PREFIX__SECURE;
+ else if(strncasecompare("__Host-", co->name, 7))
+ co->prefix |= COOKIE_PREFIX__HOST;
+ }
+ break;
+ case 6:
+ co->value = strdup(ptr);
+ if(!co->value)
+ badcookie = TRUE;
+ break;
+ }
+ }
+ if(6 == fields) {
+ /* we got a cookie with blank contents, fix it */
+ co->value = strdup("");
+ if(!co->value)
+ badcookie = TRUE;
+ else
+ fields++;
+ }
+
+ if(!badcookie && (7 != fields))
+ /* we did not find the sufficient number of fields */
+ badcookie = TRUE;
+
+ if(badcookie) {
+ freecookie(co);
+ return NULL;
+ }
+
+ }
+
+ if(co->prefix & COOKIE_PREFIX__SECURE) {
+ /* The __Secure- prefix only requires that the cookie be set secure */
+ if(!co->secure) {
+ freecookie(co);
+ return NULL;
+ }
+ }
+ if(co->prefix & COOKIE_PREFIX__HOST) {
+ /*
+ * The __Host- prefix requires the cookie to be secure, have a "/" path
+ * and not have a domain set.
+ */
+ if(co->secure && co->path && strcmp(co->path, "/") == 0 && !co->tailmatch)
+ ;
+ else {
+ freecookie(co);
+ return NULL;
+ }
+ }
+
+ if(!c->running && /* read from a file */
+ c->newsession && /* clean session cookies */
+ !co->expires) { /* this is a session cookie since it doesn't expire! */
+ freecookie(co);
+ return NULL;
+ }
+
+ co->livecookie = c->running;
+ co->creationtime = ++c->lastct;
+
+ /*
+ * Now we have parsed the incoming line, we must now check if this supersedes
+ * an already existing cookie, which it may if the previous have the same
+ * domain and path as this.
+ */
+
+ /* at first, remove expired cookies */
+ if(!noexpire)
+ remove_expired(c);
+
+#ifdef USE_LIBPSL
+ /*
+ * Check if the domain is a Public Suffix and if yes, ignore the cookie. We
+ * must also check that the data handle isn't NULL since the psl code will
+ * dereference it.
+ */
+ if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) {
+ const psl_ctx_t *psl = Curl_psl_use(data);
+ int acceptable;
+
+ if(psl) {
+ acceptable = psl_is_cookie_domain_acceptable(psl, domain, co->domain);
+ Curl_psl_release(data);
+ }
+ else
+ acceptable = !bad_domain(domain);
+
+ if(!acceptable) {
+ infof(data, "cookie '%s' dropped, domain '%s' must not "
+ "set cookies for '%s'", co->name, domain, co->domain);
+ freecookie(co);
+ return NULL;
+ }
+ }
+#endif
+
+ /* A non-secure cookie may not overlay an existing secure cookie. */
+ myhash = cookiehash(co->domain);
+ clist = c->cookies[myhash];
+ while(clist) {
+ if(strcasecompare(clist->name, co->name)) {
+ /* the names are identical */
+ bool matching_domains = FALSE;
+
+ if(clist->domain && co->domain) {
+ if(strcasecompare(clist->domain, co->domain))
+ /* The domains are identical */
+ matching_domains = TRUE;
+ }
+ else if(!clist->domain && !co->domain)
+ matching_domains = TRUE;
+
+ if(matching_domains && /* the domains were identical */
+ clist->spath && co->spath && /* both have paths */
+ clist->secure && !co->secure && !secure) {
+ size_t cllen;
+ const char *sep;
+
+ /*
+ * A non-secure cookie may not overlay an existing secure cookie.
+ * For an existing cookie "a" with path "/login", refuse a new
+ * cookie "a" with for example path "/login/en", while the path
+ * "/loginhelper" is ok.
+ */
+
+ sep = strchr(clist->spath + 1, '/');
+
+ if(sep)
+ cllen = sep - clist->spath;
+ else
+ cllen = strlen(clist->spath);
+
+ if(strncasecompare(clist->spath, co->spath, cllen)) {
+ infof(data, "cookie '%s' for domain '%s' dropped, would "
+ "overlay an existing cookie", co->name, co->domain);
+ freecookie(co);
+ return NULL;
+ }
+ }
+ }
+
+ if(!replace_co && strcasecompare(clist->name, co->name)) {
+ /* the names are identical */
+
+ if(clist->domain && co->domain) {
+ if(strcasecompare(clist->domain, co->domain) &&
+ (clist->tailmatch == co->tailmatch))
+ /* The domains are identical */
+ replace_old = TRUE;
+ }
+ else if(!clist->domain && !co->domain)
+ replace_old = TRUE;
+
+ if(replace_old) {
+ /* the domains were identical */
+
+ if(clist->spath && co->spath) {
+ if(strcasecompare(clist->spath, co->spath))
+ replace_old = TRUE;
+ else
+ replace_old = FALSE;
+ }
+ else if(!clist->spath && !co->spath)
+ replace_old = TRUE;
+ else
+ replace_old = FALSE;
+
+ }
+
+ if(replace_old && !co->livecookie && clist->livecookie) {
+ /*
+ * Both cookies matched fine, except that the already present cookie is
+ * "live", which means it was set from a header, while the new one was
+ * read from a file and thus isn't "live". "live" cookies are preferred
+ * so the new cookie is freed.
+ */
+ freecookie(co);
+ return NULL;
+ }
+ if(replace_old) {
+ replace_co = co;
+ replace_clist = clist;
+ }
+ }
+ lastc = clist;
+ clist = clist->next;
+ }
+ if(replace_co) {
+ co = replace_co;
+ clist = replace_clist;
+ co->next = clist->next; /* get the next-pointer first */
+
+ /* when replacing, creationtime is kept from old */
+ co->creationtime = clist->creationtime;
+
+ /* then free all the old pointers */
+ free(clist->name);
+ free(clist->value);
+ free(clist->domain);
+ free(clist->path);
+ free(clist->spath);
+ free(clist->expirestr);
+ free(clist->version);
+ free(clist->maxage);
+
+ *clist = *co; /* then store all the new data */
+
+ free(co); /* free the newly allocated memory */
+ co = clist;
+ }
+
+ if(c->running)
+ /* Only show this when NOT reading the cookies from a file */
+ infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
+ "expire %" CURL_FORMAT_CURL_OFF_T,
+ replace_old?"Replaced":"Added", co->name, co->value,
+ co->domain, co->path, co->expires);
+
+ if(!replace_old) {
+ /* then make the last item point on this new one */
+ if(lastc)
+ lastc->next = co;
+ else
+ c->cookies[myhash] = co;
+ c->numcookies++; /* one more cookie in the jar */
+ }
+
+ /*
+ * Now that we've added a new cookie to the jar, update the expiration
+ * tracker in case it is the next one to expire.
+ */
+ if(co->expires && (co->expires < c->next_expiration))
+ c->next_expiration = co->expires;
+
+ return co;
+}
+
+
+/*
+ * Curl_cookie_init()
+ *
+ * Inits a cookie struct to read data from a local file. This is always
+ * called before any cookies are set. File may be NULL in which case only the
+ * struct is initialized. Is file is "-" then STDIN is read.
+ *
+ * If 'newsession' is TRUE, discard all "session cookies" on read from file.
+ *
+ * Note that 'data' might be called as NULL pointer.
+ *
+ * Returns NULL on out of memory. Invalid cookies are ignored.
+ */
+struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
+ const char *file,
+ struct CookieInfo *inc,
+ bool newsession)
+{
+ struct CookieInfo *c;
+ FILE *fp = NULL;
+ bool fromfile = TRUE;
+ char *line = NULL;
+
+ if(!inc) {
+ /* we didn't get a struct, create one */
+ c = calloc(1, sizeof(struct CookieInfo));
+ if(!c)
+ return NULL; /* failed to get memory */
+ c->filename = strdup(file?file:"none"); /* copy the name just in case */
+ if(!c->filename)
+ goto fail; /* failed to get memory */
+ /*
+ * Initialize the next_expiration time to signal that we don't have enough
+ * information yet.
+ */
+ c->next_expiration = CURL_OFF_T_MAX;
+ }
+ else {
+ /* we got an already existing one, use that */
+ c = inc;
+ }
+ c->running = FALSE; /* this is not running, this is init */
+
+ if(file && !strcmp(file, "-")) {
+ fp = stdin;
+ fromfile = FALSE;
+ }
+ else if(!file || !*file) {
+ /* points to an empty string or NULL */
+ fp = NULL;
+ }
+ else {
+ fp = fopen(file, "rb");
+ if(!fp)
+ infof(data, "WARNING: failed to open cookie file \"%s\"", file);
+ }
+
+ c->newsession = newsession; /* new session? */
+
+ if(fp) {
+ char *lineptr;
+ bool headerline;
+
+ line = malloc(MAX_COOKIE_LINE);
+ if(!line)
+ goto fail;
+ while(Curl_get_line(line, MAX_COOKIE_LINE, fp)) {
+ if(checkprefix("Set-Cookie:", line)) {
+ /* This is a cookie line, get it! */
+ lineptr = &line[11];
+ headerline = TRUE;
+ }
+ else {
+ lineptr = line;
+ headerline = FALSE;
+ }
+ while(*lineptr && ISBLANK(*lineptr))
+ lineptr++;
+
+ Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE);
+ }
+ free(line); /* free the line buffer */
+
+ /*
+ * Remove expired cookies from the hash. We must make sure to run this
+ * after reading the file, and not on every cookie.
+ */
+ remove_expired(c);
+
+ if(fromfile && fp)
+ fclose(fp);
+ }
+
+ c->running = TRUE; /* now, we're running */
+ if(data)
+ data->state.cookie_engine = TRUE;
+
+ return c;
+
+fail:
+ free(line);
+ /*
+ * Only clean up if we allocated it here, as the original could still be in
+ * use by a share handle.
+ */
+ if(!inc)
+ Curl_cookie_cleanup(c);
+ if(fromfile && fp)
+ fclose(fp);
+ return NULL; /* out of memory */
+}
+
+/*
+ * cookie_sort
+ *
+ * Helper function to sort cookies such that the longest path gets before the
+ * shorter path. Path, domain and name lengths are considered in that order,
+ * with the creationtime as the tiebreaker. The creationtime is guaranteed to
+ * be unique per cookie, so we know we will get an ordering at that point.
+ */
+static int cookie_sort(const void *p1, const void *p2)
+{
+ struct Cookie *c1 = *(struct Cookie **)p1;
+ struct Cookie *c2 = *(struct Cookie **)p2;
+ size_t l1, l2;
+
+ /* 1 - compare cookie path lengths */
+ l1 = c1->path ? strlen(c1->path) : 0;
+ l2 = c2->path ? strlen(c2->path) : 0;
+
+ if(l1 != l2)
+ return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
+
+ /* 2 - compare cookie domain lengths */
+ l1 = c1->domain ? strlen(c1->domain) : 0;
+ l2 = c2->domain ? strlen(c2->domain) : 0;
+
+ if(l1 != l2)
+ return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
+
+ /* 3 - compare cookie name lengths */
+ l1 = c1->name ? strlen(c1->name) : 0;
+ l2 = c2->name ? strlen(c2->name) : 0;
+
+ if(l1 != l2)
+ return (l2 > l1) ? 1 : -1;
+
+ /* 4 - compare cookie creation time */
+ return (c2->creationtime > c1->creationtime) ? 1 : -1;
+}
+
+/*
+ * cookie_sort_ct
+ *
+ * Helper function to sort cookies according to creation time.
+ */
+static int cookie_sort_ct(const void *p1, const void *p2)
+{
+ struct Cookie *c1 = *(struct Cookie **)p1;
+ struct Cookie *c2 = *(struct Cookie **)p2;
+
+ return (c2->creationtime > c1->creationtime) ? 1 : -1;
+}
+
+#define CLONE(field) \
+ do { \
+ if(src->field) { \
+ d->field = strdup(src->field); \
+ if(!d->field) \
+ goto fail; \
+ } \
+ } while(0)
+
+static struct Cookie *dup_cookie(struct Cookie *src)
+{
+ struct Cookie *d = calloc(sizeof(struct Cookie), 1);
+ if(d) {
+ CLONE(expirestr);
+ CLONE(domain);
+ CLONE(path);
+ CLONE(spath);
+ CLONE(name);
+ CLONE(value);
+ CLONE(maxage);
+ CLONE(version);
+ d->expires = src->expires;
+ d->tailmatch = src->tailmatch;
+ d->secure = src->secure;
+ d->livecookie = src->livecookie;
+ d->httponly = src->httponly;
+ d->creationtime = src->creationtime;
+ }
+ return d;
+
+ fail:
+ freecookie(d);
+ return NULL;
+}
+
+/*
+ * Curl_cookie_getlist
+ *
+ * For a given host and path, return a linked list of cookies that the client
+ * should send to the server if used now. The secure boolean informs the cookie
+ * if a secure connection is achieved or not.
+ *
+ * It shall only return cookies that haven't expired.
+ */
+struct Cookie *Curl_cookie_getlist(struct Curl_easy *data,
+ struct CookieInfo *c,
+ const char *host, const char *path,
+ bool secure)
+{
+ struct Cookie *newco;
+ struct Cookie *co;
+ struct Cookie *mainco = NULL;
+ size_t matches = 0;
+ bool is_ip;
+ const size_t myhash = cookiehash(host);
+
+ if(!c || !c->cookies[myhash])
+ return NULL; /* no cookie struct or no cookies in the struct */
+
+ /* at first, remove expired cookies */
+ remove_expired(c);
+
+ /* check if host is an IP(v4|v6) address */
+ is_ip = Curl_host_is_ipnum(host);
+
+ co = c->cookies[myhash];
+
+ while(co) {
+ /* if the cookie requires we're secure we must only continue if we are! */
+ if(co->secure?secure:TRUE) {
+
+ /* now check if the domain is correct */
+ if(!co->domain ||
+ (co->tailmatch && !is_ip && tailmatch(co->domain, host)) ||
+ ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) {
+ /*
+ * the right part of the host matches the domain stuff in the
+ * cookie data
+ */
+
+ /*
+ * now check the left part of the path with the cookies path
+ * requirement
+ */
+ if(!co->spath || pathmatch(co->spath, path) ) {
+
+ /*
+ * and now, we know this is a match and we should create an
+ * entry for the return-linked-list
+ */
+
+ newco = dup_cookie(co);
+ if(newco) {
+ /* then modify our next */
+ newco->next = mainco;
+
+ /* point the main to us */
+ mainco = newco;
+
+ matches++;
+ if(matches >= MAX_COOKIE_SEND_AMOUNT) {
+ infof(data, "Included max number of cookies (%zu) in request!",
+ matches);
+ break;
+ }
+ }
+ else
+ goto fail;
+ }
+ }
+ }
+ co = co->next;
+ }
+
+ if(matches) {
+ /*
+ * Now we need to make sure that if there is a name appearing more than
+ * once, the longest specified path version comes first. To make this
+ * the swiftest way, we just sort them all based on path length.
+ */
+ struct Cookie **array;
+ size_t i;
+
+ /* alloc an array and store all cookie pointers */
+ array = malloc(sizeof(struct Cookie *) * matches);
+ if(!array)
+ goto fail;
+
+ co = mainco;
+
+ for(i = 0; co; co = co->next)
+ array[i++] = co;
+
+ /* now sort the cookie pointers in path length order */
+ qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
+
+ /* remake the linked list order according to the new order */
+
+ mainco = array[0]; /* start here */
+ for(i = 0; i<matches-1; i++)
+ array[i]->next = array[i + 1];
+ array[matches-1]->next = NULL; /* terminate the list */
+
+ free(array); /* remove the temporary data again */
+ }
+
+ return mainco; /* return the new list */
+
+fail:
+ /* failure, clear up the allocated chain and return NULL */
+ Curl_cookie_freelist(mainco);
+ return NULL;
+}
+
+/*
+ * Curl_cookie_clearall
+ *
+ * Clear all existing cookies and reset the counter.
+ */
+void Curl_cookie_clearall(struct CookieInfo *cookies)
+{
+ if(cookies) {
+ unsigned int i;
+ for(i = 0; i < COOKIE_HASH_SIZE; i++) {
+ Curl_cookie_freelist(cookies->cookies[i]);
+ cookies->cookies[i] = NULL;
+ }
+ cookies->numcookies = 0;
+ }
+}
+
+/*
+ * Curl_cookie_freelist
+ *
+ * Free a list of cookies previously returned by Curl_cookie_getlist();
+ */
+void Curl_cookie_freelist(struct Cookie *co)
+{
+ struct Cookie *next;
+ while(co) {
+ next = co->next;
+ freecookie(co);
+ co = next;
+ }
+}
+
+/*
+ * Curl_cookie_clearsess
+ *
+ * Free all session cookies in the cookies list.
+ */
+void Curl_cookie_clearsess(struct CookieInfo *cookies)
+{
+ struct Cookie *first, *curr, *next, *prev = NULL;
+ unsigned int i;
+
+ if(!cookies)
+ return;
+
+ for(i = 0; i < COOKIE_HASH_SIZE; i++) {
+ if(!cookies->cookies[i])
+ continue;
+
+ first = curr = prev = cookies->cookies[i];
+
+ for(; curr; curr = next) {
+ next = curr->next;
+ if(!curr->expires) {
+ if(first == curr)
+ first = next;
+
+ if(prev == curr)
+ prev = next;
+ else
+ prev->next = next;
+
+ freecookie(curr);
+ cookies->numcookies--;
+ }
+ else
+ prev = curr;
+ }
+
+ cookies->cookies[i] = first;
+ }
+}
+
+/*
+ * Curl_cookie_cleanup()
+ *
+ * Free a "cookie object" previous created with Curl_cookie_init().
+ */
+void Curl_cookie_cleanup(struct CookieInfo *c)
+{
+ if(c) {
+ unsigned int i;
+ free(c->filename);
+ for(i = 0; i < COOKIE_HASH_SIZE; i++)
+ Curl_cookie_freelist(c->cookies[i]);
+ free(c); /* free the base struct as well */
+ }
+}
+
+/*
+ * get_netscape_format()
+ *
+ * Formats a string for Netscape output file, w/o a newline at the end.
+ * Function returns a char * to a formatted line. The caller is responsible
+ * for freeing the returned pointer.
+ */
+static char *get_netscape_format(const struct Cookie *co)
+{
+ return aprintf(
+ "%s" /* httponly preamble */
+ "%s%s\t" /* domain */
+ "%s\t" /* tailmatch */
+ "%s\t" /* path */
+ "%s\t" /* secure */
+ "%" CURL_FORMAT_CURL_OFF_T "\t" /* expires */
+ "%s\t" /* name */
+ "%s", /* value */
+ co->httponly?"#HttpOnly_":"",
+ /*
+ * Make sure all domains are prefixed with a dot if they allow
+ * tailmatching. This is Mozilla-style.
+ */
+ (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
+ co->domain?co->domain:"unknown",
+ co->tailmatch?"TRUE":"FALSE",
+ co->path?co->path:"/",
+ co->secure?"TRUE":"FALSE",
+ co->expires,
+ co->name,
+ co->value?co->value:"");
+}
+
+/*
+ * cookie_output()
+ *
+ * Writes all internally known cookies to the specified file. Specify
+ * "-" as file name to write to stdout.
+ *
+ * The function returns non-zero on write failure.
+ */
+static CURLcode cookie_output(struct Curl_easy *data,
+ struct CookieInfo *c, const char *filename)
+{
+ struct Cookie *co;
+ FILE *out = NULL;
+ bool use_stdout = FALSE;
+ char *tempstore = NULL;
+ CURLcode error = CURLE_OK;
+
+ if(!c)
+ /* no cookie engine alive */
+ return CURLE_OK;
+
+ /* at first, remove expired cookies */
+ remove_expired(c);
+
+ if(!strcmp("-", filename)) {
+ /* use stdout */
+ out = stdout;
+ use_stdout = TRUE;
+ }
+ else {
+ error = Curl_fopen(data, filename, &out, &tempstore);
+ if(error)
+ goto error;
+ }
+
+ fputs("# Netscape HTTP Cookie File\n"
+ "# https://curl.se/docs/http-cookies.html\n"
+ "# This file was generated by libcurl! Edit at your own risk.\n\n",
+ out);
+
+ if(c->numcookies) {
+ unsigned int i;
+ size_t nvalid = 0;
+ struct Cookie **array;
+
+ array = calloc(1, sizeof(struct Cookie *) * c->numcookies);
+ if(!array) {
+ error = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ /* only sort the cookies with a domain property */
+ for(i = 0; i < COOKIE_HASH_SIZE; i++) {
+ for(co = c->cookies[i]; co; co = co->next) {
+ if(!co->domain)
+ continue;
+ array[nvalid++] = co;
+ }
+ }
+
+ qsort(array, nvalid, sizeof(struct Cookie *), cookie_sort_ct);
+
+ for(i = 0; i < nvalid; i++) {
+ char *format_ptr = get_netscape_format(array[i]);
+ if(!format_ptr) {
+ free(array);
+ error = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ fprintf(out, "%s\n", format_ptr);
+ free(format_ptr);
+ }
+
+ free(array);
+ }
+
+ if(!use_stdout) {
+ fclose(out);
+ out = NULL;
+ if(tempstore && Curl_rename(tempstore, filename)) {
+ unlink(tempstore);
+ error = CURLE_WRITE_ERROR;
+ goto error;
+ }
+ }
+
+ /*
+ * If we reach here we have successfully written a cookie file so theree is
+ * no need to inspect the error, any error case should have jumped into the
+ * error block below.
+ */
+ free(tempstore);
+ return CURLE_OK;
+
+error:
+ if(out && !use_stdout)
+ fclose(out);
+ free(tempstore);
+ return error;
+}
+
+static struct curl_slist *cookie_list(struct Curl_easy *data)
+{
+ struct curl_slist *list = NULL;
+ struct curl_slist *beg;
+ struct Cookie *c;
+ char *line;
+ unsigned int i;
+
+ if(!data->cookies || (data->cookies->numcookies == 0))
+ return NULL;
+
+ for(i = 0; i < COOKIE_HASH_SIZE; i++) {
+ for(c = data->cookies->cookies[i]; c; c = c->next) {
+ if(!c->domain)
+ continue;
+ line = get_netscape_format(c);
+ if(!line) {
+ curl_slist_free_all(list);
+ return NULL;
+ }
+ beg = Curl_slist_append_nodup(list, line);
+ if(!beg) {
+ free(line);
+ curl_slist_free_all(list);
+ return NULL;
+ }
+ list = beg;
+ }
+ }
+
+ return list;
+}
+
+struct curl_slist *Curl_cookie_list(struct Curl_easy *data)
+{
+ struct curl_slist *list;
+ Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
+ list = cookie_list(data);
+ Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
+ return list;
+}
+
+void Curl_flush_cookies(struct Curl_easy *data, bool cleanup)
+{
+ CURLcode res;
+
+ if(data->set.str[STRING_COOKIEJAR]) {
+ if(data->state.cookielist) {
+ /* If there is a list of cookie files to read, do it first so that
+ we have all the told files read before we write the new jar.
+ Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */
+ Curl_cookie_loadfiles(data);
+ }
+
+ Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
+
+ /* if we have a destination file for all the cookies to get dumped to */
+ res = cookie_output(data, data->cookies, data->set.str[STRING_COOKIEJAR]);
+ if(res)
+ infof(data, "WARNING: failed to save cookies in %s: %s",
+ data->set.str[STRING_COOKIEJAR], curl_easy_strerror(res));
+ }
+ else {
+ if(cleanup && data->state.cookielist) {
+ /* since nothing is written, we can just free the list of cookie file
+ names */
+ curl_slist_free_all(data->state.cookielist); /* clean up list */
+ data->state.cookielist = NULL;
+ }
+ Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
+ }
+
+ if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
+ Curl_cookie_cleanup(data->cookies);
+ data->cookies = NULL;
+ }
+ Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
+}
+
+#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */
diff --git a/libs/libcurl/src/cookie.h b/libs/libcurl/src/cookie.h index abc0a2e8a0..e4dc4f0891 100644 --- a/libs/libcurl/src/cookie.h +++ b/libs/libcurl/src/cookie.h @@ -1,141 +1,141 @@ -#ifndef HEADER_CURL_COOKIE_H -#define HEADER_CURL_COOKIE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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> - -struct Cookie { - struct Cookie *next; /* next in the chain */ - 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> */ - char *expirestr; /* the plain text version */ - - /* RFC 2109 keywords. Version=1 means 2109-compliant cookie sending */ - char *version; /* Version = <value> */ - char *maxage; /* Max-Age = <value> */ - - bool tailmatch; /* whether we do tail-matching of the domain name */ - bool secure; /* whether the 'secure' keyword was used */ - bool livecookie; /* updated from a server, not a stored file */ - bool httponly; /* true if the httponly directive is present */ - int creationtime; /* time when the cookie was written */ - unsigned char prefix; /* bitmap fields indicating which prefix are 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 256 - -struct CookieInfo { - /* linked list of cookies we know of */ - struct Cookie *cookies[COOKIE_HASH_SIZE]; - - char *filename; /* file we read from/write to */ - long numcookies; /* number of cookies in the "jar" */ - bool running; /* state info, for cookie adding information */ - bool newsession; /* new session, discard session cookies on load */ - int lastct; /* last creation-time used in the jar */ - curl_off_t next_expiration; /* the next time at which expiration happens */ -}; - -/* This is the maximum line length we accept for a cookie line. RFC 2109 - section 6.3 says: - - "at least 4096 bytes per cookie (as measured by the size of the characters - that comprise the cookie non-terminal in the syntax description of the - Set-Cookie header)" - - We allow max 5000 bytes cookie header. Max 4095 bytes length per cookie - name and value. Name + value may not exceed 4096 bytes. - -*/ -#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 -#define MAX_NAME_TXT "4095" - -/* 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 - -/* 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 - -struct Curl_easy; -/* - * Add a cookie to the internal list of cookies. The domain and path arguments - * are only used if the header boolean is TRUE. - */ - -struct Cookie *Curl_cookie_add(struct Curl_easy *data, - struct CookieInfo *c, bool header, - bool noexpiry, char *lineptr, - const char *domain, const char *path, - bool secure); - -struct Cookie *Curl_cookie_getlist(struct Curl_easy *data, - struct CookieInfo *c, const char *host, - const char *path, bool secure); -void Curl_cookie_freelist(struct Cookie *cookies); -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 */ +#ifndef HEADER_CURL_COOKIE_H
+#define HEADER_CURL_COOKIE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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>
+
+struct Cookie {
+ struct Cookie *next; /* next in the chain */
+ 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> */
+ char *expirestr; /* the plain text version */
+
+ /* RFC 2109 keywords. Version=1 means 2109-compliant cookie sending */
+ char *version; /* Version = <value> */
+ char *maxage; /* Max-Age = <value> */
+
+ bool tailmatch; /* whether we do tail-matching of the domain name */
+ bool secure; /* whether the 'secure' keyword was used */
+ bool livecookie; /* updated from a server, not a stored file */
+ bool httponly; /* true if the httponly directive is present */
+ int creationtime; /* time when the cookie was written */
+ unsigned char prefix; /* bitmap fields indicating which prefix are 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 256
+
+struct CookieInfo {
+ /* linked list of cookies we know of */
+ struct Cookie *cookies[COOKIE_HASH_SIZE];
+
+ char *filename; /* file we read from/write to */
+ long numcookies; /* number of cookies in the "jar" */
+ bool running; /* state info, for cookie adding information */
+ bool newsession; /* new session, discard session cookies on load */
+ int lastct; /* last creation-time used in the jar */
+ curl_off_t next_expiration; /* the next time at which expiration happens */
+};
+
+/* This is the maximum line length we accept for a cookie line. RFC 2109
+ section 6.3 says:
+
+ "at least 4096 bytes per cookie (as measured by the size of the characters
+ that comprise the cookie non-terminal in the syntax description of the
+ Set-Cookie header)"
+
+ We allow max 5000 bytes cookie header. Max 4095 bytes length per cookie
+ name and value. Name + value may not exceed 4096 bytes.
+
+*/
+#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
+#define MAX_NAME_TXT "4095"
+
+/* 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
+
+/* 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
+
+struct Curl_easy;
+/*
+ * Add a cookie to the internal list of cookies. The domain and path arguments
+ * are only used if the header boolean is TRUE.
+ */
+
+struct Cookie *Curl_cookie_add(struct Curl_easy *data,
+ struct CookieInfo *c, bool header,
+ bool noexpiry, char *lineptr,
+ const char *domain, const char *path,
+ bool secure);
+
+struct Cookie *Curl_cookie_getlist(struct Curl_easy *data,
+ struct CookieInfo *c, const char *host,
+ const char *path, bool secure);
+void Curl_cookie_freelist(struct Cookie *cookies);
+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 */
diff --git a/libs/libcurl/src/curl_addrinfo.c b/libs/libcurl/src/curl_addrinfo.c index 72e778b34e..fb46d68d58 100644 --- a/libs/libcurl/src/curl_addrinfo.c +++ b/libs/libcurl/src/curl_addrinfo.c @@ -1,597 +1,592 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - -#if defined(NETWARE) && defined(__NOVELL_LIBC__) -# undef in_addr_t -# define in_addr_t unsigned long -#endif - -#include <stddef.h> - -#include "curl_addrinfo.h" -#include "inet_pton.h" -#include "warnless.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#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 = 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 ENABLE_IPV6 - else if(ai->ai_family == AF_INET6) - ss_size = sizeof(struct sockaddr_in6); -#endif - else - continue; - - /* ignore elements without required address info */ - if(!ai->ai_addr || !(ai->ai_addrlen > 0)) - continue; - - /* ignore elements with bogus address size */ - if((size_t)ai->ai_addrlen < ss_size) - continue; - - ca = malloc(sizeof(struct Curl_addrinfo) + ss_size + 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) - 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 a IPv6 - * stack, but usable also for IPv4, all hosts and environments. - * - * The memory allocated by this function *MUST* be free'd later on calling - * Curl_freeaddrinfo(). For each successful call to this function there - * must be an associated call later to Curl_freeaddrinfo(). - * - * 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] - */ - -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 ENABLE_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-terminatior */ -#ifdef ENABLE_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 ENABLE_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; -} - - -struct namebuff { - struct hostent hostentry; - union { - struct in_addr ina4; -#ifdef ENABLE_IPV6 - struct in6_addr ina6; -#endif - } addrentry; - char *h_addr_list[2]; -}; - - -/* - * 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; - -#if defined(__VMS) && \ - defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64) -#pragma pointer_size save -#pragma pointer_size short -#pragma message disable PTRMISMATCH -#endif - - struct hostent *h; - struct namebuff *buf; - char *addrentry; - char *hoststr; - size_t addrsize; - - DEBUGASSERT(inaddr && hostname); - - buf = malloc(sizeof(struct namebuff)); - if(!buf) - return NULL; - - hoststr = strdup(hostname); - if(!hoststr) { - free(buf); - return NULL; - } - - switch(af) { - case AF_INET: - addrsize = sizeof(struct in_addr); - addrentry = (void *)&buf->addrentry.ina4; - memcpy(addrentry, inaddr, sizeof(struct in_addr)); - break; -#ifdef ENABLE_IPV6 - case AF_INET6: - addrsize = sizeof(struct in6_addr); - addrentry = (void *)&buf->addrentry.ina6; - memcpy(addrentry, inaddr, sizeof(struct in6_addr)); - break; -#endif - default: - free(hoststr); - free(buf); - return NULL; - } - - h = &buf->hostentry; - h->h_name = hoststr; - h->h_aliases = NULL; - h->h_addrtype = (short)af; - h->h_length = (short)addrsize; - h->h_addr_list = &buf->h_addr_list[0]; - h->h_addr_list[0] = addrentry; - h->h_addr_list[1] = NULL; /* terminate list of entries */ - -#if defined(__VMS) && \ - defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64) -#pragma pointer_size restore -#pragma message enable PTRMISMATCH -#endif - - ai = Curl_he2ai(h, port); - - free(hoststr); - free(buf); - - 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(Curl_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 ENABLE_IPV6 - { - struct in6_addr in6; - if(Curl_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 NUL-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 didn't 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); -#else - (freeaddrinfo)(freethis); -#endif -} -#endif /* defined(CURLDEBUG) && defined(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 didn't 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); -#else - int res = (getaddrinfo)(hostname, service, hints, result); -#endif - if(0 == res) - /* 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 /* defined(CURLDEBUG) && defined(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 Mac OS X - * 10.11.5. - */ -void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port) -{ - struct Curl_addrinfo *ca; - struct sockaddr_in *addr; -#ifdef ENABLE_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 ENABLE_IPV6 - case AF_INET6: - addr6 = (void *)ca->ai_addr; /* storage area for this info */ - addr6->sin6_port = htons((unsigned short)port); - break; -#endif - } - } -} -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "inet_pton.h"
+#include "warnless.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#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 = 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 ENABLE_IPV6
+ else if(ai->ai_family == AF_INET6)
+ ss_size = sizeof(struct sockaddr_in6);
+#endif
+ else
+ continue;
+
+ /* ignore elements without required address info */
+ if(!ai->ai_addr || !(ai->ai_addrlen > 0))
+ continue;
+
+ /* ignore elements with bogus address size */
+ if((size_t)ai->ai_addrlen < ss_size)
+ continue;
+
+ ca = malloc(sizeof(struct Curl_addrinfo) + ss_size + 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)
+ 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 a IPv6
+ * stack, but usable also for IPv4, all hosts and environments.
+ *
+ * The memory allocated by this function *MUST* be free'd later on calling
+ * Curl_freeaddrinfo(). For each successful call to this function there
+ * must be an associated call later to Curl_freeaddrinfo().
+ *
+ * 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]
+ */
+
+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 ENABLE_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-terminatior */
+#ifdef ENABLE_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 ENABLE_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;
+}
+
+
+struct namebuff {
+ struct hostent hostentry;
+ union {
+ struct in_addr ina4;
+#ifdef ENABLE_IPV6
+ struct in6_addr ina6;
+#endif
+ } addrentry;
+ char *h_addr_list[2];
+};
+
+
+/*
+ * 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;
+
+#if defined(__VMS) && \
+ defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64)
+#pragma pointer_size save
+#pragma pointer_size short
+#pragma message disable PTRMISMATCH
+#endif
+
+ struct hostent *h;
+ struct namebuff *buf;
+ char *addrentry;
+ char *hoststr;
+ size_t addrsize;
+
+ DEBUGASSERT(inaddr && hostname);
+
+ buf = malloc(sizeof(struct namebuff));
+ if(!buf)
+ return NULL;
+
+ hoststr = strdup(hostname);
+ if(!hoststr) {
+ free(buf);
+ return NULL;
+ }
+
+ switch(af) {
+ case AF_INET:
+ addrsize = sizeof(struct in_addr);
+ addrentry = (void *)&buf->addrentry.ina4;
+ memcpy(addrentry, inaddr, sizeof(struct in_addr));
+ break;
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ addrsize = sizeof(struct in6_addr);
+ addrentry = (void *)&buf->addrentry.ina6;
+ memcpy(addrentry, inaddr, sizeof(struct in6_addr));
+ break;
+#endif
+ default:
+ free(hoststr);
+ free(buf);
+ return NULL;
+ }
+
+ h = &buf->hostentry;
+ h->h_name = hoststr;
+ h->h_aliases = NULL;
+ h->h_addrtype = (short)af;
+ h->h_length = (short)addrsize;
+ h->h_addr_list = &buf->h_addr_list[0];
+ h->h_addr_list[0] = addrentry;
+ h->h_addr_list[1] = NULL; /* terminate list of entries */
+
+#if defined(__VMS) && \
+ defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64)
+#pragma pointer_size restore
+#pragma message enable PTRMISMATCH
+#endif
+
+ ai = Curl_he2ai(h, port);
+
+ free(hoststr);
+ free(buf);
+
+ 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(Curl_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 ENABLE_IPV6
+ {
+ struct in6_addr in6;
+ if(Curl_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 NUL-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 didn't 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);
+#else
+ (freeaddrinfo)(freethis);
+#endif
+}
+#endif /* defined(CURLDEBUG) && defined(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 didn't 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);
+#else
+ int res = (getaddrinfo)(hostname, service, hints, result);
+#endif
+ if(0 == res)
+ /* 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 /* defined(CURLDEBUG) && defined(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 Mac OS X
+ * 10.11.5.
+ */
+void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port)
+{
+ struct Curl_addrinfo *ca;
+ struct sockaddr_in *addr;
+#ifdef ENABLE_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 ENABLE_IPV6
+ case AF_INET6:
+ addr6 = (void *)ca->ai_addr; /* storage area for this info */
+ addr6->sin6_port = htons((unsigned short)port);
+ break;
+#endif
+ }
+ }
+}
+#endif
diff --git a/libs/libcurl/src/curl_addrinfo.h b/libs/libcurl/src/curl_addrinfo.h index b778121a7c..ae3b3f3608 100644 --- a/libs/libcurl/src/curl_addrinfo.h +++ b/libs/libcurl/src/curl_addrinfo.h @@ -1,108 +1,108 @@ -#ifndef HEADER_CURL_ADDRINFO_H -#define HEADER_CURL_ADDRINFO_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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. And 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 - -struct Curl_addrinfo * -Curl_he2ai(const struct hostent *he, int port); - -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 */ +#ifndef HEADER_CURL_ADDRINFO_H
+#define HEADER_CURL_ADDRINFO_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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. And 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
+
+struct Curl_addrinfo *
+Curl_he2ai(const struct hostent *he, int port);
+
+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 */
diff --git a/libs/libcurl/src/curl_base64.h b/libs/libcurl/src/curl_base64.h index 85368a163c..fa3b061503 100644 --- a/libs/libcurl/src/curl_base64.h +++ b/libs/libcurl/src/curl_base64.h @@ -1,34 +1,34 @@ -#ifndef HEADER_CURL_BASE64_H -#define HEADER_CURL_BASE64_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 Curl_base64_encode(const char *inputbuff, size_t insize, - char **outptr, size_t *outlen); -CURLcode Curl_base64url_encode(const char *inputbuff, size_t insize, - char **outptr, size_t *outlen); -CURLcode Curl_base64_decode(const char *src, - unsigned char **outptr, size_t *outlen); - -#endif /* HEADER_CURL_BASE64_H */ +#ifndef HEADER_CURL_BASE64_H
+#define HEADER_CURL_BASE64_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 Curl_base64_encode(const char *inputbuff, size_t insize,
+ char **outptr, size_t *outlen);
+CURLcode Curl_base64url_encode(const char *inputbuff, size_t insize,
+ char **outptr, size_t *outlen);
+CURLcode Curl_base64_decode(const char *src,
+ unsigned char **outptr, size_t *outlen);
+
+#endif /* HEADER_CURL_BASE64_H */
diff --git a/libs/libcurl/src/curl_config.h.cmake b/libs/libcurl/src/curl_config.h.cmake index 3ba14923cd..5317495ba1 100644 --- a/libs/libcurl/src/curl_config.h.cmake +++ b/libs/libcurl/src/curl_config.h.cmake @@ -1,797 +1,785 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ -/* lib/curl_config.h.in. Generated somehow by cmake. */ - -/* 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}" - -/* disables alt-svc */ -#cmakedefine CURL_DISABLE_ALTSVC 1 - -/* disables cookies support */ -#cmakedefine CURL_DISABLE_COOKIES 1 - -/* disables cryptographic authentication */ -#cmakedefine CURL_DISABLE_CRYPTO_AUTH 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 FTP */ -#cmakedefine CURL_DISABLE_FTP 1 - -/* disables GOPHER */ -#cmakedefine CURL_DISABLE_GOPHER 1 - -/* disables HSTS support */ -#cmakedefine CURL_DISABLE_HSTS 1 - -/* disables HTTP */ -#cmakedefine CURL_DISABLE_HTTP 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 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 RTSP */ -#cmakedefine CURL_DISABLE_RTSP 1 - -/* disables SMB */ -#cmakedefine CURL_DISABLE_SMB 1 - -/* disables SMTP */ -#cmakedefine CURL_DISABLE_SMTP 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 - -/* 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 - -/* when not building a shared library */ -#cmakedefine CURL_STATICLIB 1 - -/* your Entropy Gathering Daemon socket pathname */ -#cmakedefine EGD_SOCKET ${EGD_SOCKET} - -/* Define if you want to enable IPv6 support */ -#cmakedefine ENABLE_IPV6 1 - -/* Define to 1 if you have the alarm function. */ -#cmakedefine HAVE_ALARM 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 the <arpa/tftp.h> header file. */ -#cmakedefine HAVE_ARPA_TFTP_H 1 - -/* Define to 1 if you have the <assert.h> header file. */ -#cmakedefine HAVE_ASSERT_H 1 - -/* Define to 1 if you have _Atomic support. */ -#cmakedefine HAVE_ATOMIC 1 - -/* Define to 1 if you have the `fchmod' function. */ -#cmakedefine HAVE_FCHMOD 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 `closesocket' function. */ -#cmakedefine HAVE_CLOSESOCKET 1 - -/* Define to 1 if you have the <errno.h> header file. */ -#cmakedefine HAVE_ERRNO_H 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 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 `getppid' function. */ -#cmakedefine HAVE_GETPPID 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 - -/* Define to 1 if you have the <gssapi/gssapi_generic.h> header file. */ -#cmakedefine HAVE_GSSAPI_GSSAPI_GENERIC_H 1 - -/* Define to 1 if you have the <gssapi/gssapi.h> header file. */ -#cmakedefine HAVE_GSSAPI_GSSAPI_H 1 - -/* Define to 1 if you have the <gssapi/gssapi_krb5.h> header file. */ -#cmakedefine HAVE_GSSAPI_GSSAPI_KRB5_H 1 - -/* if you have the GNU gssapi libraries */ -#cmakedefine HAVE_GSSGNU 1 - -/* if you have the Heimdal gssapi libraries */ -#cmakedefine HAVE_GSSHEIMDAL 1 - -/* if you have the MIT gssapi libraries */ -#cmakedefine HAVE_GSSMIT 1 - -/* Define to 1 if you have the `idna_strerror' function. */ -#cmakedefine HAVE_IDNA_STRERROR 1 - -/* Define to 1 if you have the <ifaddrs.h> header file. */ -#cmakedefine HAVE_IFADDRS_H 1 - -/* Define to 1 if you have a IPv6 capable working inet_ntop function. */ -#cmakedefine HAVE_INET_NTOP 1 - -/* Define to 1 if you have a 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 symbol `ADDRESS_FAMILY' exists */ -#cmakedefine HAVE_ADDRESS_FAMILY 1 - -/* Define to 1 if you have the <inttypes.h> header file. */ -#cmakedefine HAVE_INTTYPES_H 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 - -/* Define to 1 if you have the ldap.h header file. */ -#cmakedefine HAVE_LDAP_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 - -/* Define to 1 if you have the `socket' library (-lsocket). */ -#cmakedefine HAVE_LIBSOCKET 1 - -/* Define to 1 if you have the `ssh2' library (-lssh2). */ -#cmakedefine HAVE_LIBSSH2 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 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/tcp.h> header file. */ -#cmakedefine HAVE_NETINET_TCP_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 - -/* if you have an old MIT gssapi library, lacking GSS_C_NT_HOSTBASED_SERVICE */ -#cmakedefine HAVE_OLD_GSSMIT 1 - -/* Define to 1 if you have the `pipe' function. */ -#cmakedefine HAVE_PIPE 1 - -/* If you have a fine poll */ -#cmakedefine HAVE_POLL_FINE 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 you have the `RAND_egd' function. */ -#cmakedefine HAVE_RAND_EGD 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 send function. */ -#cmakedefine HAVE_SEND 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 <setjmp.h> header file. */ -#cmakedefine HAVE_SETJMP_H 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 `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 <signal.h> header file. */ -#cmakedefine HAVE_SIGNAL_H 1 - -/* Define to 1 if you have the sigsetjmp function or macro. */ -#cmakedefine HAVE_SIGSETJMP 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 socketpair function. */ -#cmakedefine HAVE_SOCKETPAIR 1 - -/* Define to 1 if you have the <ssl.h> header file. */ -#cmakedefine HAVE_SSL_H 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 <stdint.h> header file. */ -#cmakedefine HAVE_STDINT_H 1 - -/* Define to 1 if you have the <stdlib.h> header file. */ -#cmakedefine HAVE_STDLIB_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 <string.h> header file. */ -#cmakedefine HAVE_STRING_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 strtok_r function. */ -#cmakedefine HAVE_STRTOK_R 1 - -/* Define to 1 if you have the strtoll function. */ -#cmakedefine HAVE_STRTOLL 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/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/socket.h> header file. */ -#cmakedefine HAVE_SYS_SOCKET_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/stat.h> header file. */ -#cmakedefine HAVE_SYS_STAT_H 1 - -/* Define to 1 if you have the <sys/time.h> header file. */ -#cmakedefine HAVE_SYS_TIME_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 <time.h> header file. */ -#cmakedefine HAVE_TIME_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 to 1 if compiler supports C99 variadic macro style. */ -#cmakedefine HAVE_VARIADIC_MACROS_C99 1 - -/* Define to 1 if compiler supports old gcc variadic macro style. */ -#cmakedefine HAVE_VARIADIC_MACROS_GCC 1 - -/* Define to 1 if you have the windows.h header file. */ -#cmakedefine HAVE_WINDOWS_H 1 - -/* Define to 1 if you have the winldap.h header file. */ -#cmakedefine HAVE_WINLDAP_H 1 - -/* Define to 1 if you have the winsock2.h header file. */ -#cmakedefine HAVE_WINSOCK2_H 1 - -/* Define this symbol if your OS supports changing the contents of argv */ -#cmakedefine HAVE_WRITABLE_ARGV 1 - -/* Define to 1 if you have the ws2tcpip.h header file. */ -#cmakedefine HAVE_WS2TCPIP_H 1 - -/* Define if you have the <process.h> header file. */ -#cmakedefine HAVE_PROCESS_H 1 - -/* Define to 1 if you need the lber.h header file even with ldap.h */ -#cmakedefine NEED_LBER_H 1 - -/* Define to 1 if you need the malloc.h header file even with stdlib.h */ -#cmakedefine NEED_MALLOC_H 1 - -/* Define to 1 if _REENTRANT preprocessor symbol must be defined. */ -#cmakedefine NEED_REENTRANT 1 - -/* cpu-machine-OS */ -#cmakedefine OS ${OS} - -/* Name of package */ -#cmakedefine PACKAGE ${PACKAGE} - -/* Define to the address where bug reports for this package should be sent. */ -#cmakedefine PACKAGE_BUGREPORT ${PACKAGE_BUGREPORT} - -/* Define to the full name of this package. */ -#cmakedefine PACKAGE_NAME ${PACKAGE_NAME} - -/* Define to the full name and version of this package. */ -#cmakedefine PACKAGE_STRING ${PACKAGE_STRING} - -/* Define to the one symbol short name of this package. */ -#cmakedefine PACKAGE_TARNAME ${PACKAGE_TARNAME} - -/* Define to the version of this package. */ -#cmakedefine PACKAGE_VERSION ${PACKAGE_VERSION} - -/* a suitable file to read random data from */ -#cmakedefine RANDOM_FILE "${RANDOM_FILE}" - -/* - 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 `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 `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 to 1 if you can safely include both <sys/time.h> and <time.h>. */ -#cmakedefine TIME_WITH_SYS_TIME 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 Secure Transport is enabled */ -#cmakedefine USE_SECTRANSP 1 - -/* if mbedTLS is enabled */ -#cmakedefine USE_MBEDTLS 1 - -/* if BearSSL is enabled */ -#cmakedefine USE_BEARSSL 1 - -/* if WolfSSL is enabled */ -#cmakedefine USE_WOLFSSL 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 build curl with the built-in manual */ -#cmakedefine USE_MANUAL 1 - -/* if NSS is enabled */ -#cmakedefine USE_NSS 1 - -/* if you have the PK11_CreateManagedGenericObject function */ -#cmakedefine HAVE_PK11_CREATEMANAGEDGENERICOBJECT 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 - -/* Define to 1 if you don't 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 - -/* Define to 1 if you have the quiche_conn_set_qlog_fd function. */ -#cmakedefine HAVE_QUICHE_CONN_SET_QLOG_FD 1 - -/* to enable msh3 */ -#cmakedefine USE_MSH3 1 - -/* if Unix domain sockets are enabled */ -#cmakedefine USE_UNIX_SOCKETS - -/* 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 - -/* enable multiple SSL backends */ -#cmakedefine CURL_WITH_MULTI_SSL 1 - -/* Version number of package */ -#cmakedefine VERSION ${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. */ -#cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS} - -/* Define for large files, on AIX-style hosts. */ -#cmakedefine _LARGE_FILES ${_LARGE_FILES} - -/* define this if you need it to compile thread-safe code */ -#cmakedefine _THREAD_SAFE ${_THREAD_SAFE} - -/* Define to empty if `const' does not conform to ANSI C. */ -#cmakedefine const ${const} - -/* Type to use in place of in_addr_t when system does not provide it. */ -#cmakedefine in_addr_t ${in_addr_t} - -/* Define to `__inline__' or `__inline' if that's what the C compiler - calls it, or to nothing if 'inline' is not supported under any name. */ -#ifndef __cplusplus -#undef inline -#endif - -/* Define to `unsigned int' if <sys/types.h> does not define. */ -#cmakedefine size_t ${size_t} - -/* 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 make the compiler know the prototypes of Windows IDN APIs */ -#cmakedefine WANT_IDN_PROTOTYPES 1 - -/* Define to 1 to enable websocket support. */ -#cmakedefine USE_WEBSOCKETS 1 +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+/* lib/curl_config.h.in. Generated somehow by cmake. */
+
+/* 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}"
+
+/* disables alt-svc */
+#cmakedefine CURL_DISABLE_ALTSVC 1
+
+/* disables cookies support */
+#cmakedefine CURL_DISABLE_COOKIES 1
+
+/* disables cryptographic authentication */
+#cmakedefine CURL_DISABLE_CRYPTO_AUTH 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 FTP */
+#cmakedefine CURL_DISABLE_FTP 1
+
+/* disables GOPHER */
+#cmakedefine CURL_DISABLE_GOPHER 1
+
+/* disables HSTS support */
+#cmakedefine CURL_DISABLE_HSTS 1
+
+/* disables HTTP */
+#cmakedefine CURL_DISABLE_HTTP 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 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 RTSP */
+#cmakedefine CURL_DISABLE_RTSP 1
+
+/* disables SMB */
+#cmakedefine CURL_DISABLE_SMB 1
+
+/* disables SMTP */
+#cmakedefine CURL_DISABLE_SMTP 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
+
+/* 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
+
+/* when not building a shared library */
+#cmakedefine CURL_STATICLIB 1
+
+/* your Entropy Gathering Daemon socket pathname */
+#cmakedefine EGD_SOCKET ${EGD_SOCKET}
+
+/* Define if you want to enable IPv6 support */
+#cmakedefine ENABLE_IPV6 1
+
+/* Define to 1 if you have the alarm function. */
+#cmakedefine HAVE_ALARM 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 the <arpa/tftp.h> header file. */
+#cmakedefine HAVE_ARPA_TFTP_H 1
+
+/* Define to 1 if you have _Atomic support. */
+#cmakedefine HAVE_ATOMIC 1
+
+/* Define to 1 if you have the `fchmod' function. */
+#cmakedefine HAVE_FCHMOD 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 `closesocket' function. */
+#cmakedefine HAVE_CLOSESOCKET 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 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 `getppid' function. */
+#cmakedefine HAVE_GETPPID 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
+
+/* Define to 1 if you have the <gssapi/gssapi_generic.h> header file. */
+#cmakedefine HAVE_GSSAPI_GSSAPI_GENERIC_H 1
+
+/* Define to 1 if you have the <gssapi/gssapi.h> header file. */
+#cmakedefine HAVE_GSSAPI_GSSAPI_H 1
+
+/* Define to 1 if you have the <gssapi/gssapi_krb5.h> header file. */
+#cmakedefine HAVE_GSSAPI_GSSAPI_KRB5_H 1
+
+/* if you have the GNU gssapi libraries */
+#cmakedefine HAVE_GSSGNU 1
+
+/* if you have the Heimdal gssapi libraries */
+#cmakedefine HAVE_GSSHEIMDAL 1
+
+/* if you have the MIT gssapi libraries */
+#cmakedefine HAVE_GSSMIT 1
+
+/* Define to 1 if you have the `idna_strerror' function. */
+#cmakedefine HAVE_IDNA_STRERROR 1
+
+/* Define to 1 if you have the <ifaddrs.h> header file. */
+#cmakedefine HAVE_IFADDRS_H 1
+
+/* Define to 1 if you have a IPv6 capable working inet_ntop function. */
+#cmakedefine HAVE_INET_NTOP 1
+
+/* Define to 1 if you have a 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 symbol `ADDRESS_FAMILY' exists */
+#cmakedefine HAVE_ADDRESS_FAMILY 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#cmakedefine HAVE_INTTYPES_H 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
+
+/* Define to 1 if you have the ldap.h header file. */
+#cmakedefine HAVE_LDAP_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
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+#cmakedefine HAVE_LIBSOCKET 1
+
+/* Define to 1 if you have the `ssh2' library (-lssh2). */
+#cmakedefine HAVE_LIBSSH2 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 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/tcp.h> header file. */
+#cmakedefine HAVE_NETINET_TCP_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
+
+/* if you have an old MIT gssapi library, lacking GSS_C_NT_HOSTBASED_SERVICE */
+#cmakedefine HAVE_OLD_GSSMIT 1
+
+/* Define to 1 if you have the `pipe' function. */
+#cmakedefine HAVE_PIPE 1
+
+/* If you have a fine poll */
+#cmakedefine HAVE_POLL_FINE 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 you have the `RAND_egd' function. */
+#cmakedefine HAVE_RAND_EGD 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 send function. */
+#cmakedefine HAVE_SEND 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 <setjmp.h> header file. */
+#cmakedefine HAVE_SETJMP_H 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 `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 <signal.h> header file. */
+#cmakedefine HAVE_SIGNAL_H 1
+
+/* Define to 1 if you have the sigsetjmp function or macro. */
+#cmakedefine HAVE_SIGSETJMP 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 socketpair function. */
+#cmakedefine HAVE_SOCKETPAIR 1
+
+/* Define to 1 if you have the <ssl.h> header file. */
+#cmakedefine HAVE_SSL_H 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 <stdint.h> header file. */
+#cmakedefine HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#cmakedefine HAVE_STDLIB_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 <string.h> header file. */
+#cmakedefine HAVE_STRING_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 strtok_r function. */
+#cmakedefine HAVE_STRTOK_R 1
+
+/* Define to 1 if you have the strtoll function. */
+#cmakedefine HAVE_STRTOLL 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/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/socket.h> header file. */
+#cmakedefine HAVE_SYS_SOCKET_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/stat.h> header file. */
+#cmakedefine HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#cmakedefine HAVE_SYS_TIME_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 <time.h> header file. */
+#cmakedefine HAVE_TIME_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 to 1 if compiler supports C99 variadic macro style. */
+#cmakedefine HAVE_VARIADIC_MACROS_C99 1
+
+/* Define to 1 if compiler supports old gcc variadic macro style. */
+#cmakedefine HAVE_VARIADIC_MACROS_GCC 1
+
+/* Define to 1 if you have the windows.h header file. */
+#cmakedefine HAVE_WINDOWS_H 1
+
+/* Define to 1 if you have the winldap.h header file. */
+#cmakedefine HAVE_WINLDAP_H 1
+
+/* Define to 1 if you have the winsock2.h header file. */
+#cmakedefine HAVE_WINSOCK2_H 1
+
+/* Define this symbol if your OS supports changing the contents of argv */
+#cmakedefine HAVE_WRITABLE_ARGV 1
+
+/* Define to 1 if you have the ws2tcpip.h header file. */
+#cmakedefine HAVE_WS2TCPIP_H 1
+
+/* Define to 1 if you need the lber.h header file even with ldap.h */
+#cmakedefine NEED_LBER_H 1
+
+/* Define to 1 if you need the malloc.h header file even with stdlib.h */
+#cmakedefine NEED_MALLOC_H 1
+
+/* Define to 1 if _REENTRANT preprocessor symbol must be defined. */
+#cmakedefine NEED_REENTRANT 1
+
+/* cpu-machine-OS */
+#cmakedefine OS ${OS}
+
+/* Name of package */
+#cmakedefine PACKAGE ${PACKAGE}
+
+/* Define to the address where bug reports for this package should be sent. */
+#cmakedefine PACKAGE_BUGREPORT ${PACKAGE_BUGREPORT}
+
+/* Define to the full name of this package. */
+#cmakedefine PACKAGE_NAME ${PACKAGE_NAME}
+
+/* Define to the full name and version of this package. */
+#cmakedefine PACKAGE_STRING ${PACKAGE_STRING}
+
+/* Define to the one symbol short name of this package. */
+#cmakedefine PACKAGE_TARNAME ${PACKAGE_TARNAME}
+
+/* Define to the version of this package. */
+#cmakedefine PACKAGE_VERSION ${PACKAGE_VERSION}
+
+/* a suitable file to read random data from */
+#cmakedefine RANDOM_FILE "${RANDOM_FILE}"
+
+/*
+ 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 `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 `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 to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#cmakedefine TIME_WITH_SYS_TIME 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 Secure Transport is enabled */
+#cmakedefine USE_SECTRANSP 1
+
+/* if mbedTLS is enabled */
+#cmakedefine USE_MBEDTLS 1
+
+/* if BearSSL is enabled */
+#cmakedefine USE_BEARSSL 1
+
+/* if WolfSSL is enabled */
+#cmakedefine USE_WOLFSSL 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 build curl with the built-in manual */
+#cmakedefine USE_MANUAL 1
+
+/* if NSS is enabled */
+#cmakedefine USE_NSS 1
+
+/* if you have the PK11_CreateManagedGenericObject function */
+#cmakedefine HAVE_PK11_CREATEMANAGEDGENERICOBJECT 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
+
+/* Define to 1 if you don't 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
+
+/* Define to 1 if you have the quiche_conn_set_qlog_fd function. */
+#cmakedefine HAVE_QUICHE_CONN_SET_QLOG_FD 1
+
+/* to enable msh3 */
+#cmakedefine USE_MSH3 1
+
+/* if Unix domain sockets are enabled */
+#cmakedefine USE_UNIX_SOCKETS
+
+/* 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
+
+/* enable multiple SSL backends */
+#cmakedefine CURL_WITH_MULTI_SSL 1
+
+/* Version number of package */
+#cmakedefine VERSION ${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. */
+#cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS}
+
+/* Define for large files, on AIX-style hosts. */
+#cmakedefine _LARGE_FILES ${_LARGE_FILES}
+
+/* define this if you need it to compile thread-safe code */
+#cmakedefine _THREAD_SAFE ${_THREAD_SAFE}
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#cmakedefine const ${const}
+
+/* Type to use in place of in_addr_t when system does not provide it. */
+#cmakedefine in_addr_t ${in_addr_t}
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+#undef inline
+#endif
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#cmakedefine size_t ${size_t}
+
+/* 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
+
+/* Define to 1 to enable websocket support. */
+#cmakedefine USE_WEBSOCKETS 1
diff --git a/libs/libcurl/src/curl_config.h.in b/libs/libcurl/src/curl_config.h.in index 06e1dd252a..c5289e8961 100644 --- a/libs/libcurl/src/curl_config.h.in +++ b/libs/libcurl/src/curl_config.h.in @@ -1,978 +1,975 @@ -/* lib/curl_config.h.in. Generated from configure.ac by autoheader. */ - -/* to enable curl debug memory tracking */ -#undef CURLDEBUG - -/* Location of default ca bundle */ -#undef CURL_CA_BUNDLE - -/* define "1" to use built in CA store of SSL library */ -#undef CURL_CA_FALLBACK - -/* Location of default ca path */ -#undef CURL_CA_PATH - -/* Default SSL backend */ -#undef CURL_DEFAULT_SSL_BACKEND - -/* disable alt-svc */ -#undef CURL_DISABLE_ALTSVC - -/* to disable cookies support */ -#undef CURL_DISABLE_COOKIES - -/* to disable cryptographic authentication */ -#undef CURL_DISABLE_CRYPTO_AUTH - -/* to disable DICT */ -#undef CURL_DISABLE_DICT - -/* disable DoH */ -#undef CURL_DISABLE_DOH - -/* to disable FILE */ -#undef CURL_DISABLE_FILE - -/* 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 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 - -/* 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 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 - -/* Definition to make a library symbol externally visible. */ -#undef CURL_EXTERN_SYMBOL - -/* IP address type in sockaddr */ -#undef CURL_SA_FAMILY_T - -/* built with multiple SSL backends */ -#undef CURL_WITH_MULTI_SSL - -/* enable debug build options */ -#undef DEBUGBUILD - -/* your Entropy Gathering Daemon socket pathname */ -#undef EGD_SOCKET - -/* Define if you want to enable IPv6 support */ -#undef ENABLE_IPV6 - -/* Define to the type of arg 2 for gethostname. */ -#undef GETHOSTNAME_TYPE_ARG2 - -/* Define to 1 if you have the alarm function. */ -#undef HAVE_ALARM - -/* Define to 1 if you have the <arpa/inet.h> header file. */ -#undef HAVE_ARPA_INET_H - -/* Define to 1 if you have the <arpa/tftp.h> header file. */ -#undef HAVE_ARPA_TFTP_H - -/* Define to 1 if you have the <assert.h> header file. */ -#undef HAVE_ASSERT_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 - -/* Define to 1 if using BoringSSL. */ -#undef HAVE_BORINGSSL - -/* 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 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 connect function. */ -#undef HAVE_CONNECT - -/* Define to 1 if you have the <crypto.h> header file. */ -#undef HAVE_CRYPTO_H - -/* Define to 1 if you have the declaration of `getpwuid_r', and to 0 if you - don't. */ -#undef HAVE_DECL_GETPWUID_R - -/* "Set if getpwuid_r() declaration is missing" */ -#undef HAVE_DECL_GETPWUID_R_MISSING - -/* Define to 1 if you have the <dlfcn.h> header file. */ -#undef HAVE_DLFCN_H - -/* Define to 1 if you have the <errno.h> header file. */ -#undef HAVE_ERRNO_H - -/* Define to 1 if you have the <err.h> header file. */ -#undef HAVE_ERR_H - -/* Define to 1 if you have the `fchmod' function. */ -#undef HAVE_FCHMOD - -/* 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 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 function. */ -#undef HAVE_GETHOSTBYNAME - -/* 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 - -/* 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 - -/* if you have Heimdal */ -#undef HAVE_GSSHEIMDAL - -/* if you have MIT Kerberos */ -#undef HAVE_GSSMIT - -/* Define to 1 if you have the <hyper.h> header file. */ -#undef HAVE_HYPER_H - -/* 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 a IPv6 capable working inet_ntop function. */ -#undef HAVE_INET_NTOP - -/* Define to 1 if you have a 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 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 - -/* 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 using libressl. */ -#undef HAVE_LIBRESSL - -/* 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 - -/* Define to 1 if you have the `wolfssh' library (-lwolfssh). */ -#undef HAVE_LIBWOLFSSH - -/* 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 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 <msh3.h> header file. */ -#undef HAVE_MSH3_H - -/* 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 an old MIT Kerberos version, lacking GSS_C_NT_HOSTBASED_SERVICE - */ -#undef HAVE_OLD_GSSMIT - -/* Define to 1 if using OpenSSL 3 or later. */ -#undef HAVE_OPENSSL3 - -/* 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 <pem.h> header file. */ -#undef HAVE_PEM_H - -/* Define to 1 if you have the `pipe' function. */ -#undef HAVE_PIPE - -/* if you have the PK11_CreateManagedGenericObject function */ -#undef HAVE_PK11_CREATEMANAGEDGENERICOBJECT - -/* If you have a fine poll */ -#undef HAVE_POLL_FINE - -/* 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 `RAND_egd' function. */ -#undef HAVE_RAND_EGD - -/* Define to 1 if you have the recv function. */ -#undef HAVE_RECV - -/* Define to 1 if you have the <rsa.h> header file. */ -#undef HAVE_RSA_H - -/* 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 `sendmsg' function. */ -#undef HAVE_SENDMSG - -/* Define to 1 if you have the <setjmp.h> header file. */ -#undef HAVE_SETJMP_H - -/* 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 <signal.h> header file. */ -#undef HAVE_SIGNAL_H - -/* 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 <socket.h> header file. */ -#undef HAVE_SOCKET_H - -/* Define to 1 if you have the `SSL_get_ech_status' function. */ -#undef HAVE_SSL_GET_ECH_STATUS - -/* Define to 1 if you have the <ssl.h> header file. */ -#undef HAVE_SSL_H - -/* 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 strtok_r function. */ -#undef HAVE_STRTOK_R - -/* Define to 1 if you have the strtoll function. */ -#undef HAVE_STRTOLL - -/* 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/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/socket.h> header file. */ -#undef HAVE_SYS_SOCKET_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/time.h> header file. */ -#undef HAVE_SYS_TIME_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/uio.h> header file. */ -#undef HAVE_SYS_UIO_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/wait.h> header file. */ -#undef HAVE_SYS_WAIT_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 <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 compiler supports C99 variadic macro style. */ -#undef HAVE_VARIADIC_MACROS_C99 - -/* Define to 1 if compiler supports old gcc variadic macro style. */ -#undef HAVE_VARIADIC_MACROS_GCC - -/* Define to 1 if you have the wincrypt.h header file. */ -#undef HAVE_WINCRYPT_H - -/* Define to 1 if you have the windows.h header file. */ -#undef HAVE_WINDOWS_H - -/* Define to 1 if you have the winldap.h header file. */ -#undef HAVE_WINLDAP_H - -/* Define to 1 if you have the winsock2.h header file. */ -#undef HAVE_WINSOCK2_H - -/* Define to 1 if you have the <wolfssh/ssh.h> header file. */ -#undef HAVE_WOLFSSH_SSH_H - -/* if you have wolfSSL_DES_ecb_encrypt */ -#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_UseALPN' function. */ -#undef HAVE_WOLFSSL_USEALPN - -/* Define this symbol if your OS supports changing the contents of argv */ -#undef HAVE_WRITABLE_ARGV - -/* Define to 1 if you have the ws2tcpip.h header file. */ -#undef HAVE_WS2TCPIP_H - -/* Define to 1 if you have the <x509.h> header file. */ -#undef HAVE_X509_H - -/* 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 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 - -/* Define to enable NTLM delegation to winbind's ntlm_auth helper. */ -#undef NTLM_WB_ENABLED - -/* Define absolute filename for winbind's ntlm_auth helper. */ -#undef NTLM_WB_FILE - -/* cpu-machine-OS */ -#undef OS - -/* 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 - -/* a suitable file to read random data from */ -#undef RANDOM_FILE - -/* Size of curl_off_t in number of bytes */ -#undef SIZEOF_CURL_OFF_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 - -/* Define to enable c-ares support */ -#undef USE_ARES - -/* if BearSSL is enabled */ -#undef USE_BEARSSL - -/* if ECH support is available */ -#undef USE_ECH - -/* if GnuTLS is enabled */ -#undef USE_GNUTLS - -/* GSASL support enabled */ -#undef USE_GSASL - -/* if hyper is in use */ -#undef USE_HYPER - -/* PSL support enabled */ -#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 you want to build curl with the built-in manual */ -#undef USE_MANUAL - -/* if mbedTLS is enabled */ -#undef USE_MBEDTLS - -/* if msh3 is in use */ -#undef USE_MSH3 - -/* if nghttp2 is in use */ -#undef USE_NGHTTP2 - -/* if nghttp3 is in use */ -#undef USE_NGHTTP3 - -/* if ngtcp2 is in use */ -#undef USE_NGTCP2 - -/* if ngtcp2_crypto_gnutls is in use */ -#undef USE_NGTCP2_CRYPTO_GNUTLS - -/* if ngtcp2_crypto_openssl is in use */ -#undef USE_NGTCP2_CRYPTO_OPENSSL - -/* if ngtcp2_crypto_wolfssl is in use */ -#undef USE_NGTCP2_CRYPTO_WOLFSSL - -/* if NSS is enabled */ -#undef USE_NSS - -/* Use OpenLDAP-specific code */ -#undef USE_OPENLDAP - -/* if OpenSSL is in use */ -#undef USE_OPENSSL - -/* 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 - -/* enable Secure Transport */ -#undef USE_SECTRANSP - -/* 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 - -/* enable websockets support */ -#undef USE_WEBSOCKETS - -/* 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 - -/* Define to 1 if you are building a Windows target without large file - support. */ -#undef USE_WIN32_SMALL_FILES - -/* to enable SSPI support */ -#undef USE_WINDOWS_SSPI - -/* if wolfSSH is in use */ -#undef USE_WOLFSSH - -/* if wolfSSL is enabled */ -#undef USE_WOLFSSL - -/* Version number of package */ -#undef VERSION - -/* Define to 1 to provide own prototypes. */ -#undef WANT_IDN_PROTOTYPES - -/* 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 - -/* Type to use in place of in_addr_t when system does not provide it. */ -#undef in_addr_t - -/* Define to `__inline__' or `__inline' if that's what the C compiler - calls it, or to nothing if 'inline' is not supported under any name. */ -#ifndef __cplusplus -#undef inline -#endif - -/* Define to `unsigned int' if <sys/types.h> does not define. */ -#undef size_t - -/* the signed version of size_t */ -#undef ssize_t +/* lib/curl_config.h.in. Generated from configure.ac by autoheader. */
+
+/* to enable curl debug memory tracking */
+#undef CURLDEBUG
+
+/* Location of default ca bundle */
+#undef CURL_CA_BUNDLE
+
+/* define "1" to use built in CA store of SSL library */
+#undef CURL_CA_FALLBACK
+
+/* Location of default ca path */
+#undef CURL_CA_PATH
+
+/* Default SSL backend */
+#undef CURL_DEFAULT_SSL_BACKEND
+
+/* disable alt-svc */
+#undef CURL_DISABLE_ALTSVC
+
+/* to disable cookies support */
+#undef CURL_DISABLE_COOKIES
+
+/* to disable cryptographic authentication */
+#undef CURL_DISABLE_CRYPTO_AUTH
+
+/* to disable DICT */
+#undef CURL_DISABLE_DICT
+
+/* disable DoH */
+#undef CURL_DISABLE_DOH
+
+/* to disable FILE */
+#undef CURL_DISABLE_FILE
+
+/* 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 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
+
+/* 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 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
+
+/* Definition to make a library symbol externally visible. */
+#undef CURL_EXTERN_SYMBOL
+
+/* IP address type in sockaddr */
+#undef CURL_SA_FAMILY_T
+
+/* built with multiple SSL backends */
+#undef CURL_WITH_MULTI_SSL
+
+/* enable debug build options */
+#undef DEBUGBUILD
+
+/* your Entropy Gathering Daemon socket pathname */
+#undef EGD_SOCKET
+
+/* Define if you want to enable IPv6 support */
+#undef ENABLE_IPV6
+
+/* Define to the type of arg 2 for gethostname. */
+#undef GETHOSTNAME_TYPE_ARG2
+
+/* Define to 1 if you have the alarm function. */
+#undef HAVE_ALARM
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#undef HAVE_ARPA_INET_H
+
+/* Define to 1 if you have the <arpa/tftp.h> header file. */
+#undef HAVE_ARPA_TFTP_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
+
+/* Define to 1 if using BoringSSL. */
+#undef HAVE_BORINGSSL
+
+/* 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 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 connect function. */
+#undef HAVE_CONNECT
+
+/* Define to 1 if you have the <crypto.h> header file. */
+#undef HAVE_CRYPTO_H
+
+/* Define to 1 if you have the declaration of `getpwuid_r', and to 0 if you
+ don't. */
+#undef HAVE_DECL_GETPWUID_R
+
+/* "Set if getpwuid_r() declaration is missing" */
+#undef HAVE_DECL_GETPWUID_R_MISSING
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the <err.h> header file. */
+#undef HAVE_ERR_H
+
+/* Define to 1 if you have the `fchmod' function. */
+#undef HAVE_FCHMOD
+
+/* 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 `fork' function. */
+#undef HAVE_FORK
+
+/* Define to 1 if you have the freeaddrinfo function. */
+#undef HAVE_FREEADDRINFO
+
+/* 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 function. */
+#undef HAVE_GETHOSTBYNAME
+
+/* 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
+
+/* 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
+
+/* if you have Heimdal */
+#undef HAVE_GSSHEIMDAL
+
+/* if you have MIT Kerberos */
+#undef HAVE_GSSMIT
+
+/* Define to 1 if you have the <hyper.h> header file. */
+#undef HAVE_HYPER_H
+
+/* 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 a IPv6 capable working inet_ntop function. */
+#undef HAVE_INET_NTOP
+
+/* Define to 1 if you have a 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 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
+
+/* 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 using libressl. */
+#undef HAVE_LIBRESSL
+
+/* 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
+
+/* Define to 1 if you have the `wolfssh' library (-lwolfssh). */
+#undef HAVE_LIBWOLFSSH
+
+/* 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 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 <msh3.h> header file. */
+#undef HAVE_MSH3_H
+
+/* 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 an old MIT Kerberos version, lacking GSS_C_NT_HOSTBASED_SERVICE
+ */
+#undef HAVE_OLD_GSSMIT
+
+/* Define to 1 if using OpenSSL 3 or later. */
+#undef HAVE_OPENSSL3
+
+/* 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 <pem.h> header file. */
+#undef HAVE_PEM_H
+
+/* Define to 1 if you have the `pipe' function. */
+#undef HAVE_PIPE
+
+/* if you have the PK11_CreateManagedGenericObject function */
+#undef HAVE_PK11_CREATEMANAGEDGENERICOBJECT
+
+/* If you have a fine poll */
+#undef HAVE_POLL_FINE
+
+/* 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 `RAND_egd' function. */
+#undef HAVE_RAND_EGD
+
+/* Define to 1 if you have the recv function. */
+#undef HAVE_RECV
+
+/* Define to 1 if you have the <rsa.h> header file. */
+#undef HAVE_RSA_H
+
+/* 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 `sendmsg' function. */
+#undef HAVE_SENDMSG
+
+/* Define to 1 if you have the <setjmp.h> header file. */
+#undef HAVE_SETJMP_H
+
+/* 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 <signal.h> header file. */
+#undef HAVE_SIGNAL_H
+
+/* 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 <socket.h> header file. */
+#undef HAVE_SOCKET_H
+
+/* Define to 1 if you have the `SSL_get_ech_status' function. */
+#undef HAVE_SSL_GET_ECH_STATUS
+
+/* Define to 1 if you have the <ssl.h> header file. */
+#undef HAVE_SSL_H
+
+/* 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 strtok_r function. */
+#undef HAVE_STRTOK_R
+
+/* Define to 1 if you have the strtoll function. */
+#undef HAVE_STRTOLL
+
+/* 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/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/socket.h> header file. */
+#undef HAVE_SYS_SOCKET_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/time.h> header file. */
+#undef HAVE_SYS_TIME_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/uio.h> header file. */
+#undef HAVE_SYS_UIO_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/wait.h> header file. */
+#undef HAVE_SYS_WAIT_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 <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 compiler supports C99 variadic macro style. */
+#undef HAVE_VARIADIC_MACROS_C99
+
+/* Define to 1 if compiler supports old gcc variadic macro style. */
+#undef HAVE_VARIADIC_MACROS_GCC
+
+/* Define to 1 if you have the wincrypt.h header file. */
+#undef HAVE_WINCRYPT_H
+
+/* Define to 1 if you have the windows.h header file. */
+#undef HAVE_WINDOWS_H
+
+/* Define to 1 if you have the winldap.h header file. */
+#undef HAVE_WINLDAP_H
+
+/* Define to 1 if you have the winsock2.h header file. */
+#undef HAVE_WINSOCK2_H
+
+/* Define to 1 if you have the <wolfssh/ssh.h> header file. */
+#undef HAVE_WOLFSSH_SSH_H
+
+/* if you have wolfSSL_DES_ecb_encrypt */
+#undef HAVE_WOLFSSL_DES_ECB_ENCRYPT
+
+/* if you have wolfSSL_BIO_set_shutdown */
+#undef HAVE_WOLFSSL_FULL_BIO
+
+/* 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_UseALPN' function. */
+#undef HAVE_WOLFSSL_USEALPN
+
+/* Define this symbol if your OS supports changing the contents of argv */
+#undef HAVE_WRITABLE_ARGV
+
+/* Define to 1 if you have the ws2tcpip.h header file. */
+#undef HAVE_WS2TCPIP_H
+
+/* Define to 1 if you have the <x509.h> header file. */
+#undef HAVE_X509_H
+
+/* 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 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
+
+/* Define to enable NTLM delegation to winbind's ntlm_auth helper. */
+#undef NTLM_WB_ENABLED
+
+/* Define absolute filename for winbind's ntlm_auth helper. */
+#undef NTLM_WB_FILE
+
+/* cpu-machine-OS */
+#undef OS
+
+/* 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
+
+/* a suitable file to read random data from */
+#undef RANDOM_FILE
+
+/* Size of curl_off_t in number of bytes */
+#undef SIZEOF_CURL_OFF_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
+
+/* Define to enable c-ares support */
+#undef USE_ARES
+
+/* if BearSSL is enabled */
+#undef USE_BEARSSL
+
+/* if ECH support is available */
+#undef USE_ECH
+
+/* if GnuTLS is enabled */
+#undef USE_GNUTLS
+
+/* GSASL support enabled */
+#undef USE_GSASL
+
+/* if hyper is in use */
+#undef USE_HYPER
+
+/* PSL support enabled */
+#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 you want to build curl with the built-in manual */
+#undef USE_MANUAL
+
+/* if mbedTLS is enabled */
+#undef USE_MBEDTLS
+
+/* if msh3 is in use */
+#undef USE_MSH3
+
+/* if nghttp2 is in use */
+#undef USE_NGHTTP2
+
+/* if nghttp3 is in use */
+#undef USE_NGHTTP3
+
+/* if ngtcp2 is in use */
+#undef USE_NGTCP2
+
+/* if ngtcp2_crypto_gnutls is in use */
+#undef USE_NGTCP2_CRYPTO_GNUTLS
+
+/* if ngtcp2_crypto_openssl is in use */
+#undef USE_NGTCP2_CRYPTO_OPENSSL
+
+/* if ngtcp2_crypto_wolfssl is in use */
+#undef USE_NGTCP2_CRYPTO_WOLFSSL
+
+/* if NSS is enabled */
+#undef USE_NSS
+
+/* Use OpenLDAP-specific code */
+#undef USE_OPENLDAP
+
+/* if OpenSSL is in use */
+#undef USE_OPENSSL
+
+/* 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
+
+/* enable Secure Transport */
+#undef USE_SECTRANSP
+
+/* 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
+
+/* enable websockets support */
+#undef USE_WEBSOCKETS
+
+/* 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
+
+/* Define to 1 if you are building a Windows target without large file
+ support. */
+#undef USE_WIN32_SMALL_FILES
+
+/* to enable SSPI support */
+#undef USE_WINDOWS_SSPI
+
+/* if wolfSSH is in use */
+#undef USE_WOLFSSH
+
+/* 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
+
+/* Type to use in place of in_addr_t when system does not provide it. */
+#undef in_addr_t
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+#undef inline
+#endif
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
+
+/* the signed version of size_t */
+#undef ssize_t
diff --git a/libs/libcurl/src/curl_ctype.h b/libs/libcurl/src/curl_ctype.h index dc6b8cab86..b9fff1bad9 100644 --- a/libs/libcurl/src/curl_ctype.h +++ b/libs/libcurl/src/curl_ctype.h @@ -1,47 +1,47 @@ -#ifndef HEADER_CURL_CTYPE_H -#define HEADER_CURL_CTYPE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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) ((x) >= 0 && ((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 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))) - -#endif /* HEADER_CURL_CTYPE_H */ +#ifndef HEADER_CURL_CTYPE_H
+#define HEADER_CURL_CTYPE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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) ((x) >= 0 && ((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 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)))
+
+#endif /* HEADER_CURL_CTYPE_H */
diff --git a/libs/libcurl/src/curl_des.c b/libs/libcurl/src/curl_des.c index a2bf648c29..357105728c 100644 --- a/libs/libcurl/src/curl_des.c +++ b/libs/libcurl/src/curl_des.c @@ -1,70 +1,70 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2015 - 2022, Steve Holme, <steve_holme@hotmail.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" - -#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \ - (defined(USE_GNUTLS) || \ - defined(USE_NSS) || \ - defined(USE_SECTRANSP) || \ - defined(USE_OS400CRYPTO) || \ - defined(USE_WIN32_CRYPTO)) - -#include "curl_des.h" - -/* - * Curl_des_set_odd_parity() - * - * This is used to apply odd parity to the given byte array. It is typically - * used by when a cryptography engines doesn't have it's 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. - */ -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 +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2015 - 2022, Steve Holme, <steve_holme@hotmail.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"
+
+#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \
+ (defined(USE_GNUTLS) || \
+ defined(USE_NSS) || \
+ defined(USE_SECTRANSP) || \
+ defined(USE_OS400CRYPTO) || \
+ defined(USE_WIN32_CRYPTO))
+
+#include "curl_des.h"
+
+/*
+ * Curl_des_set_odd_parity()
+ *
+ * This is used to apply odd parity to the given byte array. It is typically
+ * used by when a cryptography engines doesn't have it's 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.
+ */
+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
diff --git a/libs/libcurl/src/curl_des.h b/libs/libcurl/src/curl_des.h index c1c167471a..9ea9d78cc1 100644 --- a/libs/libcurl/src/curl_des.h +++ b/libs/libcurl/src/curl_des.h @@ -1,41 +1,41 @@ -#ifndef HEADER_CURL_DES_H -#define HEADER_CURL_DES_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2015 - 2022, Steve Holme, <steve_holme@hotmail.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" - -#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \ - (defined(USE_GNUTLS) || \ - defined(USE_NSS) || \ - defined(USE_SECTRANSP) || \ - defined(USE_OS400CRYPTO) || \ - defined(USE_WIN32_CRYPTO)) - -/* Applies odd parity to the given byte array */ -void Curl_des_set_odd_parity(unsigned char *bytes, size_t length); - -#endif - -#endif /* HEADER_CURL_DES_H */ +#ifndef HEADER_CURL_DES_H
+#define HEADER_CURL_DES_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2015 - 2022, Steve Holme, <steve_holme@hotmail.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"
+
+#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \
+ (defined(USE_GNUTLS) || \
+ defined(USE_NSS) || \
+ defined(USE_SECTRANSP) || \
+ defined(USE_OS400CRYPTO) || \
+ defined(USE_WIN32_CRYPTO))
+
+/* Applies odd parity to the given byte array */
+void Curl_des_set_odd_parity(unsigned char *bytes, size_t length);
+
+#endif
+
+#endif /* HEADER_CURL_DES_H */
diff --git a/libs/libcurl/src/curl_endian.c b/libs/libcurl/src/curl_endian.c index 3cc7734f31..6077b84100 100644 --- a/libs/libcurl/src/curl_endian.c +++ b/libs/libcurl/src/curl_endian.c @@ -1,84 +1,84 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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're 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're 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're 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])); -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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're 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're 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're 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]));
+}
diff --git a/libs/libcurl/src/curl_endian.h b/libs/libcurl/src/curl_endian.h index 758d55f2c5..61c33311d1 100644 --- a/libs/libcurl/src/curl_endian.h +++ b/libs/libcurl/src/curl_endian.h @@ -1,45 +1,36 @@ -#ifndef HEADER_CURL_ENDIAN_H -#define HEADER_CURL_ENDIAN_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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); - -#if (SIZEOF_CURL_OFF_T > 4) -/* Converts a 64-bit integer to little endian */ -#if defined(HAVE_LONGLONG) -void Curl_write64_le(const long long value, unsigned char *buffer); -#else -void Curl_write64_le(const __int64 value, unsigned char *buffer); -#endif -#endif - -#endif /* HEADER_CURL_ENDIAN_H */ +#ifndef HEADER_CURL_ENDIAN_H
+#define HEADER_CURL_ENDIAN_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 */
diff --git a/libs/libcurl/src/curl_fnmatch.c b/libs/libcurl/src/curl_fnmatch.c index 0dd1eb5ef5..fcca813bc4 100644 --- a/libs/libcurl/src/curl_fnmatch.c +++ b/libs/libcurl/src/curl_fnmatch.c @@ -1,391 +1,390 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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(unsigned char **pattern, unsigned char *charset) -{ - parsekey_state state = CURLFNM_PKW_INIT; -#define KEYLEN 10 - char keyword[KEYLEN] = { 0 }; - int found = FALSE; - int i; - unsigned char *p = *pattern; - for(i = 0; !found; i++) { - char c = *p++; - if(i >= KEYLEN) - 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(unsigned char **pp, unsigned char *charset) -{ - 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(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 == '[') { - 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) -{ - unsigned char *p = (unsigned char *)pattern; - unsigned char *s = (unsigned char *)string; - unsigned char charset[CURLFNM_CHSET_SIZE] = { 0 }; - - for(;;) { - 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)) { - int 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 = ISSPACE(*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((unsigned char *)pattern, (unsigned char *)string, 2); -} -#else -#include <fnmatch.h> -/* - * @unittest: 1307 - */ -int Curl_fnmatch(void *ptr, const char *pattern, const char *string) -{ - int rc; - (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; - } - rc = fnmatch(pattern, string, 0); - switch(rc) { - 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 */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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(unsigned char **pattern, unsigned char *charset)
+{
+ parsekey_state state = CURLFNM_PKW_INIT;
+#define KEYLEN 10
+ char keyword[KEYLEN] = { 0 };
+ int i;
+ unsigned char *p = *pattern;
+ bool found = FALSE;
+ for(i = 0; !found; i++) {
+ char c = *p++;
+ if(i >= KEYLEN)
+ 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(unsigned char **pp, unsigned char *charset)
+{
+ 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(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 == '[') {
+ 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)
+{
+ unsigned char *p = (unsigned char *)pattern;
+ unsigned char *s = (unsigned char *)string;
+ unsigned char charset[CURLFNM_CHSET_SIZE] = { 0 };
+
+ for(;;) {
+ 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)) {
+ int 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 = ISSPACE(*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((unsigned char *)pattern, (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 */
diff --git a/libs/libcurl/src/curl_fnmatch.h b/libs/libcurl/src/curl_fnmatch.h index 8324be533d..d4f31a5db8 100644 --- a/libs/libcurl/src/curl_fnmatch.h +++ b/libs/libcurl/src/curl_fnmatch.h @@ -1,46 +1,46 @@ -#ifndef HEADER_CURL_FNMATCH_H -#define HEADER_CURL_FNMATCH_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 */ +#ifndef HEADER_CURL_FNMATCH_H
+#define HEADER_CURL_FNMATCH_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 */
diff --git a/libs/libcurl/src/curl_get_line.c b/libs/libcurl/src/curl_get_line.c index 22e3705f4c..e8332dc4fc 100644 --- a/libs/libcurl/src/curl_get_line.c +++ b/libs/libcurl/src/curl_get_line.c @@ -1,62 +1,86 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -/* - * Curl_get_line() makes sure to only return complete whole lines that fit in - * 'len' bytes and end with a newline. - */ -char *Curl_get_line(char *buf, int len, FILE *input) -{ - bool partial = FALSE; - while(1) { - char *b = fgets(buf, len, input); - if(b) { - size_t rlen = strlen(b); - if(rlen && (b[rlen-1] == '\n')) { - if(partial) { - partial = FALSE; - continue; - } - return b; - } - /* read a partial, discard the next piece that ends with newline */ - partial = TRUE; - } - else - break; - } - return NULL; -} - -#endif /* if not disabled */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+/*
+ * Curl_get_line() makes sure to only return complete whole lines that fit in
+ * 'len' bytes and end with a newline.
+ */
+char *Curl_get_line(char *buf, int len, FILE *input)
+{
+ bool partial = FALSE;
+ while(1) {
+ char *b = fgets(buf, len, input);
+
+ if(b) {
+ size_t rlen = strlen(b);
+
+ if(!rlen)
+ break;
+
+ if(b[rlen-1] == '\n') {
+ /* b is \n terminated */
+ if(partial) {
+ partial = FALSE;
+ continue;
+ }
+ return b;
+ }
+ else if(feof(input)) {
+ if(partial)
+ /* Line is already too large to return, ignore rest */
+ break;
+
+ if(rlen + 1 < (size_t) len) {
+ /* b is EOF terminated, insert missing \n */
+ b[rlen] = '\n';
+ b[rlen + 1] = '\0';
+ return b;
+ }
+ else
+ /* Maximum buffersize reached + EOF
+ * This line is impossible to add a \n to so we'll ignore it
+ */
+ break;
+ }
+ else
+ /* Maximum buffersize reached */
+ partial = TRUE;
+ }
+ else
+ break;
+ }
+ return NULL;
+}
+
+#endif /* if not disabled */
diff --git a/libs/libcurl/src/curl_get_line.h b/libs/libcurl/src/curl_get_line.h index b2a534d001..d48b7cc780 100644 --- a/libs/libcurl/src/curl_get_line.h +++ b/libs/libcurl/src/curl_get_line.h @@ -1,31 +1,31 @@ -#ifndef HEADER_CURL_GET_LINE_H -#define HEADER_CURL_GET_LINE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - -/* get_line() makes sure to only return complete whole lines that fit in 'len' - * bytes and end with a newline. */ -char *Curl_get_line(char *buf, int len, FILE *input); - -#endif /* HEADER_CURL_GET_LINE_H */ +#ifndef HEADER_CURL_GET_LINE_H
+#define HEADER_CURL_GET_LINE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+/* get_line() makes sure to only return complete whole lines that fit in 'len'
+ * bytes and end with a newline. */
+char *Curl_get_line(char *buf, int len, FILE *input);
+
+#endif /* HEADER_CURL_GET_LINE_H */
diff --git a/libs/libcurl/src/curl_gethostname.c b/libs/libcurl/src/curl_gethostname.c index 4747e938db..bc18698b62 100644 --- a/libs/libcurl/src/curl_gethostname.c +++ b/libs/libcurl/src/curl_gethostname.c @@ -1,102 +1,102 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 host name 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 host name overriding takes place - * when environment variable CURL_GETHOSTNAME is set, using the value - * held by the variable to override returned host name. - * - * Note: The function always returns the un-qualified hostname rather - * than being provider dependent. - * - * For libcurl shared library release builds the test suite preloads - * another shared library named libhostname using the LD_PRELOAD - * mechanism which intercepts, and might override, the gethostname() - * function call. In this case a given platform must support the - * LD_PRELOAD mechanism and additionally have environment variable - * CURL_GETHOSTNAME set in order to override the returned host name. - * - * For libcurl static library release builds no overriding takes place. - */ - -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 host name when environment variable CURL_GETHOSTNAME is set */ - const char *force_hostname = getenv("CURL_GETHOSTNAME"); - if(force_hostname) { - strncpy(name, force_hostname, namelen); - err = 0; - } - else { - name[0] = '\0'; - err = gethostname(name, namelen); - } - -#else /* DEBUGBUILD */ - - /* The call to system's gethostname() might get intercepted by the - libhostname library when libcurl is built as a non-debug shared - library when running the test suite. */ - name[0] = '\0'; - err = gethostname(name, namelen); - -#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 - -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 host name 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 host name overriding takes place
+ * when environment variable CURL_GETHOSTNAME is set, using the value
+ * held by the variable to override returned host name.
+ *
+ * Note: The function always returns the un-qualified hostname rather
+ * than being provider dependent.
+ *
+ * For libcurl shared library release builds the test suite preloads
+ * another shared library named libhostname using the LD_PRELOAD
+ * mechanism which intercepts, and might override, the gethostname()
+ * function call. In this case a given platform must support the
+ * LD_PRELOAD mechanism and additionally have environment variable
+ * CURL_GETHOSTNAME set in order to override the returned host name.
+ *
+ * For libcurl static library release builds no overriding takes place.
+ */
+
+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 host name when environment variable CURL_GETHOSTNAME is set */
+ const char *force_hostname = getenv("CURL_GETHOSTNAME");
+ if(force_hostname) {
+ strncpy(name, force_hostname, namelen);
+ err = 0;
+ }
+ else {
+ name[0] = '\0';
+ err = gethostname(name, namelen);
+ }
+
+#else /* DEBUGBUILD */
+
+ /* The call to system's gethostname() might get intercepted by the
+ libhostname library when libcurl is built as a non-debug shared
+ library when running the test suite. */
+ name[0] = '\0';
+ err = gethostname(name, namelen);
+
+#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
+
+}
diff --git a/libs/libcurl/src/curl_gethostname.h b/libs/libcurl/src/curl_gethostname.h index b7360969ac..8685c7cb21 100644 --- a/libs/libcurl/src/curl_gethostname.h +++ b/libs/libcurl/src/curl_gethostname.h @@ -1,33 +1,33 @@ -#ifndef HEADER_CURL_GETHOSTNAME_H -#define HEADER_CURL_GETHOSTNAME_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 */ +#ifndef HEADER_CURL_GETHOSTNAME_H
+#define HEADER_CURL_GETHOSTNAME_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 */
diff --git a/libs/libcurl/src/curl_gssapi.c b/libs/libcurl/src/curl_gssapi.c index 01ab48ec3e..48adbf2598 100644 --- a/libs/libcurl/src/curl_gssapi.c +++ b/libs/libcurl/src/curl_gssapi.c @@ -1,146 +1,146 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2011 - 2022, 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" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -gss_OID_desc Curl_spnego_mech_oid = { - 6, (char *)"\x2b\x06\x01\x05\x05\x02" -}; -gss_OID_desc Curl_krb5_mech_oid = { - 9, (char *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" -}; - -OM_uint32 Curl_gss_init_sec_context( - struct Curl_easy *data, - OM_uint32 *minor_status, - gss_ctx_id_t *context, - gss_name_t target_name, - gss_OID mech_type, - gss_channel_bindings_t input_chan_bindings, - gss_buffer_t input_token, - gss_buffer_t output_token, - const bool mutual_auth, - OM_uint32 *ret_flags) -{ - 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 - 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; - - 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 */); -} - -#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 += 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 -} - -#endif /* HAVE_GSSAPI */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2011 - 2022, 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"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+gss_OID_desc Curl_spnego_mech_oid = {
+ 6, (char *)"\x2b\x06\x01\x05\x05\x02"
+};
+gss_OID_desc Curl_krb5_mech_oid = {
+ 9, (char *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"
+};
+
+OM_uint32 Curl_gss_init_sec_context(
+ struct Curl_easy *data,
+ OM_uint32 *minor_status,
+ gss_ctx_id_t *context,
+ gss_name_t target_name,
+ gss_OID mech_type,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_buffer_t input_token,
+ gss_buffer_t output_token,
+ const bool mutual_auth,
+ OM_uint32 *ret_flags)
+{
+ 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
+ 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;
+
+ 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 */);
+}
+
+#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 += 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
+}
+
+#endif /* HAVE_GSSAPI */
diff --git a/libs/libcurl/src/curl_gssapi.h b/libs/libcurl/src/curl_gssapi.h index b4ed482aa2..6c963ff676 100644 --- a/libs/libcurl/src/curl_gssapi.h +++ b/libs/libcurl/src/curl_gssapi.h @@ -1,63 +1,63 @@ -#ifndef HEADER_CURL_GSSAPI_H -#define HEADER_CURL_GSSAPI_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2011 - 2022, 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 -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); - -/* 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); - -/* Provide some definitions missing in old headers */ -#ifdef HAVE_OLD_GSSMIT -#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name -#define NCOMPAT 1 -#endif - -/* 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 */ +#ifndef HEADER_CURL_GSSAPI_H
+#define HEADER_CURL_GSSAPI_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2011 - 2022, 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
+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);
+
+/* 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);
+
+/* Provide some definitions missing in old headers */
+#ifdef HAVE_OLD_GSSMIT
+#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+#define NCOMPAT 1
+#endif
+
+/* 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 */
diff --git a/libs/libcurl/src/curl_hmac.h b/libs/libcurl/src/curl_hmac.h index 36c0bd62e5..71d18b668b 100644 --- a/libs/libcurl/src/curl_hmac.h +++ b/libs/libcurl/src/curl_hmac.h @@ -1,76 +1,76 @@ -#ifndef HEADER_CURL_HMAC_H -#define HEADER_CURL_HMAC_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_CRYPTO_AUTH - -#include <curl/curl.h> - -#define HMAC_MD5_LENGTH 16 - -typedef CURLcode (* HMAC_hinit_func)(void *context); -typedef void (* HMAC_hupdate_func)(void *context, - const unsigned char *data, - unsigned int len); -typedef void (* HMAC_hfinal_func)(unsigned char *result, void *context); - - -/* Per-hash function HMAC parameters. */ -struct HMAC_params { - HMAC_hinit_func - hmac_hinit; /* Initialize context procedure. */ - HMAC_hupdate_func hmac_hupdate; /* Update context with data. */ - HMAC_hfinal_func hmac_hfinal; /* Get final result procedure. */ - unsigned int hmac_ctxtsize; /* Context structure size. */ - unsigned int hmac_maxkeylen; /* Maximum key length (bytes). */ - unsigned int hmac_resultlen; /* Result length (bytes). */ -}; - - -/* HMAC computation context. */ -struct HMAC_context { - const struct HMAC_params *hmac_hash; /* Hash function definition. */ - void *hmac_hashctxt1; /* Hash function context 1. */ - void *hmac_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 */ +#ifndef HEADER_CURL_HMAC_H
+#define HEADER_CURL_HMAC_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_CRYPTO_AUTH
+
+#include <curl/curl.h>
+
+#define HMAC_MD5_LENGTH 16
+
+typedef CURLcode (* HMAC_hinit_func)(void *context);
+typedef void (* HMAC_hupdate_func)(void *context,
+ const unsigned char *data,
+ unsigned int len);
+typedef void (* HMAC_hfinal_func)(unsigned char *result, void *context);
+
+
+/* Per-hash function HMAC parameters. */
+struct HMAC_params {
+ HMAC_hinit_func
+ hmac_hinit; /* Initialize context procedure. */
+ HMAC_hupdate_func hmac_hupdate; /* Update context with data. */
+ HMAC_hfinal_func hmac_hfinal; /* Get final result procedure. */
+ unsigned int hmac_ctxtsize; /* Context structure size. */
+ unsigned int hmac_maxkeylen; /* Maximum key length (bytes). */
+ unsigned int hmac_resultlen; /* Result length (bytes). */
+};
+
+
+/* HMAC computation context. */
+struct HMAC_context {
+ const struct HMAC_params *hmac_hash; /* Hash function definition. */
+ void *hmac_hashctxt1; /* Hash function context 1. */
+ void *hmac_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 */
diff --git a/libs/libcurl/src/curl_krb5.h b/libs/libcurl/src/curl_krb5.h index ccd6f10e3d..125e9d9746 100644 --- a/libs/libcurl/src/curl_krb5.h +++ b/libs/libcurl/src/curl_krb5.h @@ -1,52 +1,52 @@ -#ifndef HEADER_CURL_KRB5_H -#define HEADER_CURL_KRB5_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_sec_client_mech { - const char *name; - size_t size; - int (*init)(void *); - int (*auth)(void *, struct Curl_easy *data, struct connectdata *); - void (*end)(void *); - int (*check_prot)(void *, int); - int (*encode)(void *, const void *, int, int, void **); - int (*decode)(void *, void *, int, int, struct connectdata *); -}; - -#define AUTH_OK 0 -#define AUTH_CONTINUE 1 -#define AUTH_ERROR 2 - -#ifdef HAVE_GSSAPI -int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn, char *, - enum protection_level); -void Curl_sec_end(struct connectdata *); -CURLcode Curl_sec_login(struct Curl_easy *, struct connectdata *); -int Curl_sec_request_prot(struct connectdata *conn, const char *level); -#else -#define Curl_sec_end(x) -#endif - -#endif /* HEADER_CURL_KRB5_H */ +#ifndef HEADER_CURL_KRB5_H
+#define HEADER_CURL_KRB5_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_sec_client_mech {
+ const char *name;
+ size_t size;
+ int (*init)(void *);
+ int (*auth)(void *, struct Curl_easy *data, struct connectdata *);
+ void (*end)(void *);
+ int (*check_prot)(void *, int);
+ int (*encode)(void *, const void *, int, int, void **);
+ int (*decode)(void *, void *, int, int, struct connectdata *);
+};
+
+#define AUTH_OK 0
+#define AUTH_CONTINUE 1
+#define AUTH_ERROR 2
+
+#ifdef HAVE_GSSAPI
+int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn, char *,
+ enum protection_level);
+void Curl_sec_end(struct connectdata *);
+CURLcode Curl_sec_login(struct Curl_easy *, struct connectdata *);
+int Curl_sec_request_prot(struct connectdata *conn, const char *level);
+#else
+#define Curl_sec_end(x)
+#endif
+
+#endif /* HEADER_CURL_KRB5_H */
diff --git a/libs/libcurl/src/curl_ldap.h b/libs/libcurl/src/curl_ldap.h index ba3ede4260..1b383eb84c 100644 --- a/libs/libcurl/src/curl_ldap.h +++ b/libs/libcurl/src/curl_ldap.h @@ -1,36 +1,36 @@ -#ifndef HEADER_CURL_LDAP_H -#define HEADER_CURL_LDAP_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 */ +#ifndef HEADER_CURL_LDAP_H
+#define HEADER_CURL_LDAP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 */
diff --git a/libs/libcurl/src/curl_md4.h b/libs/libcurl/src/curl_md4.h index 8049355cff..88be16b5ab 100644 --- a/libs/libcurl/src/curl_md4.h +++ b/libs/libcurl/src/curl_md4.h @@ -1,38 +1,38 @@ -#ifndef HEADER_CURL_MD4_H -#define HEADER_CURL_MD4_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_CRYPTO_AUTH) - -#define MD4_DIGEST_LENGTH 16 - -void Curl_md4it(unsigned char *output, const unsigned char *input, - const size_t len); - -#endif /* !defined(CURL_DISABLE_CRYPTO_AUTH) */ - -#endif /* HEADER_CURL_MD4_H */ +#ifndef HEADER_CURL_MD4_H
+#define HEADER_CURL_MD4_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_CRYPTO_AUTH)
+
+#define MD4_DIGEST_LENGTH 16
+
+void Curl_md4it(unsigned char *output, const unsigned char *input,
+ const size_t len);
+
+#endif /* !defined(CURL_DISABLE_CRYPTO_AUTH) */
+
+#endif /* HEADER_CURL_MD4_H */
diff --git a/libs/libcurl/src/curl_md5.h b/libs/libcurl/src/curl_md5.h index 789329654b..579f2a03f5 100644 --- a/libs/libcurl/src/curl_md5.h +++ b/libs/libcurl/src/curl_md5.h @@ -1,65 +1,65 @@ -#ifndef HEADER_CURL_MD5_H -#define HEADER_CURL_MD5_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_CRYPTO_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[1]; -extern const struct HMAC_params Curl_HMAC_MD5[1]; - -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 */ +#ifndef HEADER_CURL_MD5_H
+#define HEADER_CURL_MD5_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_CRYPTO_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[1];
+extern const struct HMAC_params Curl_HMAC_MD5[1];
+
+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 */
diff --git a/libs/libcurl/src/curl_memory.h b/libs/libcurl/src/curl_memory.h index 092fc9f758..ca7ef9e0c2 100644 --- a/libs/libcurl/src/curl_memory.h +++ b/libs/libcurl/src/curl_memory.h @@ -1,158 +1,158 @@ -#ifndef HEADER_CURL_MEMORY_H -#define HEADER_CURL_MEMORY_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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. - * - * OTOH some source files from 'lib' subdirectory may additionally be - * used directly as source code when using some curlx_ functions by - * third party programs that don't even use libcurl at all. When using - * these source files in this way it is necessary these are compiled - * with CURLX_NO_MEMORY_CALLBACKS defined, in order to ensure that no - * attempt of calling libcurl's memory callbacks is done from code - * which can not use this machinery. - * - * Notice that libcurl's 'memory tracking' system works chaining into - * the memory callback machinery. This implies that when compiling - * 'lib' source files with CURLX_NO_MEMORY_CALLBACKS defined this file - * disengages usage of libcurl's 'memory tracking' system, defining - * MEMDEBUG_NODEFINES and overriding CURLDEBUG purpose. - * - * CURLX_NO_MEMORY_CALLBACKS takes precedence over CURLDEBUG. This is - * done in order to allow building a 'memory tracking' enabled libcurl - * and at the same time allow building programs which do not use it. - * - * Programs and libraries in 'tests' subdirectories have specific - * purposes and needs, and as such each one will use whatever fits - * best, depending additionally whether it links with libcurl or not. - * - * Caveat emptor. Proper curlx_* separation is a work in progress - * the same as CURLX_NO_MEMORY_CALLBACKS usage, some adjustments may - * still be required. IOW don't use them yet, there are sharp edges. - */ - -#ifdef HEADER_CURL_MEMDEBUG_H -#error "Header memdebug.h shall not be included before curl_memory.h" -#endif - -#ifndef CURLX_NO_MEMORY_CALLBACKS - -#ifndef CURL_DID_MEMORY_FUNC_TYPEDEFS /* only if not already done */ -/* - * The following memory function replacement typedef's are COPIED from - * curl/curl.h and MUST match the originals. We copy them to avoid having to - * include curl/curl.h here. We avoid that include since it includes stdio.h - * and other headers that may get messed up with defines done here. - */ -typedef void *(*curl_malloc_callback)(size_t size); -typedef void (*curl_free_callback)(void *ptr); -typedef void *(*curl_realloc_callback)(void *ptr, size_t size); -typedef char *(*curl_strdup_callback)(const char *str); -typedef void *(*curl_calloc_callback)(size_t nmemb, size_t size); -#define CURL_DID_MEMORY_FUNC_TYPEDEFS -#endif - -extern curl_malloc_callback Curl_cmalloc; -extern curl_free_callback Curl_cfree; -extern curl_realloc_callback Curl_crealloc; -extern curl_strdup_callback Curl_cstrdup; -extern curl_calloc_callback Curl_ccalloc; -#if defined(WIN32) && defined(UNICODE) -extern curl_wcsdup_callback Curl_cwcsdup; -#endif - -#ifndef CURLDEBUG - -/* - * 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 -# ifdef UNICODE -# undef wcsdup -# define wcsdup(ptr) Curl_cwcsdup(ptr) -# undef _wcsdup -# define _wcsdup(ptr) Curl_cwcsdup(ptr) -# undef _tcsdup -# define _tcsdup(ptr) Curl_cwcsdup(ptr) -# else -# undef _tcsdup -# define _tcsdup(ptr) Curl_cstrdup(ptr) -# endif -#endif - -#endif /* CURLDEBUG */ - -#else /* CURLX_NO_MEMORY_CALLBACKS */ - -#ifndef MEMDEBUG_NODEFINES -#define MEMDEBUG_NODEFINES -#endif - -#endif /* CURLX_NO_MEMORY_CALLBACKS */ - -#endif /* HEADER_CURL_MEMORY_H */ +#ifndef HEADER_CURL_MEMORY_H
+#define HEADER_CURL_MEMORY_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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.
+ *
+ * OTOH some source files from 'lib' subdirectory may additionally be
+ * used directly as source code when using some curlx_ functions by
+ * third party programs that don't even use libcurl at all. When using
+ * these source files in this way it is necessary these are compiled
+ * with CURLX_NO_MEMORY_CALLBACKS defined, in order to ensure that no
+ * attempt of calling libcurl's memory callbacks is done from code
+ * which can not use this machinery.
+ *
+ * Notice that libcurl's 'memory tracking' system works chaining into
+ * the memory callback machinery. This implies that when compiling
+ * 'lib' source files with CURLX_NO_MEMORY_CALLBACKS defined this file
+ * disengages usage of libcurl's 'memory tracking' system, defining
+ * MEMDEBUG_NODEFINES and overriding CURLDEBUG purpose.
+ *
+ * CURLX_NO_MEMORY_CALLBACKS takes precedence over CURLDEBUG. This is
+ * done in order to allow building a 'memory tracking' enabled libcurl
+ * and at the same time allow building programs which do not use it.
+ *
+ * Programs and libraries in 'tests' subdirectories have specific
+ * purposes and needs, and as such each one will use whatever fits
+ * best, depending additionally whether it links with libcurl or not.
+ *
+ * Caveat emptor. Proper curlx_* separation is a work in progress
+ * the same as CURLX_NO_MEMORY_CALLBACKS usage, some adjustments may
+ * still be required. IOW don't use them yet, there are sharp edges.
+ */
+
+#ifdef HEADER_CURL_MEMDEBUG_H
+#error "Header memdebug.h shall not be included before curl_memory.h"
+#endif
+
+#ifndef CURLX_NO_MEMORY_CALLBACKS
+
+#ifndef CURL_DID_MEMORY_FUNC_TYPEDEFS /* only if not already done */
+/*
+ * The following memory function replacement typedef's are COPIED from
+ * curl/curl.h and MUST match the originals. We copy them to avoid having to
+ * include curl/curl.h here. We avoid that include since it includes stdio.h
+ * and other headers that may get messed up with defines done here.
+ */
+typedef void *(*curl_malloc_callback)(size_t size);
+typedef void (*curl_free_callback)(void *ptr);
+typedef void *(*curl_realloc_callback)(void *ptr, size_t size);
+typedef char *(*curl_strdup_callback)(const char *str);
+typedef void *(*curl_calloc_callback)(size_t nmemb, size_t size);
+#define CURL_DID_MEMORY_FUNC_TYPEDEFS
+#endif
+
+extern curl_malloc_callback Curl_cmalloc;
+extern curl_free_callback Curl_cfree;
+extern curl_realloc_callback Curl_crealloc;
+extern curl_strdup_callback Curl_cstrdup;
+extern curl_calloc_callback Curl_ccalloc;
+#if defined(WIN32) && defined(UNICODE)
+extern curl_wcsdup_callback Curl_cwcsdup;
+#endif
+
+#ifndef CURLDEBUG
+
+/*
+ * 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
+# ifdef UNICODE
+# undef wcsdup
+# define wcsdup(ptr) Curl_cwcsdup(ptr)
+# undef _wcsdup
+# define _wcsdup(ptr) Curl_cwcsdup(ptr)
+# undef _tcsdup
+# define _tcsdup(ptr) Curl_cwcsdup(ptr)
+# else
+# undef _tcsdup
+# define _tcsdup(ptr) Curl_cstrdup(ptr)
+# endif
+#endif
+
+#endif /* CURLDEBUG */
+
+#else /* CURLX_NO_MEMORY_CALLBACKS */
+
+#ifndef MEMDEBUG_NODEFINES
+#define MEMDEBUG_NODEFINES
+#endif
+
+#endif /* CURLX_NO_MEMORY_CALLBACKS */
+
+#endif /* HEADER_CURL_MEMORY_H */
diff --git a/libs/libcurl/src/curl_memrchr.c b/libs/libcurl/src/curl_memrchr.c index c329a6176a..da30f0048c 100644 --- a/libs/libcurl/src/curl_memrchr.c +++ b/libs/libcurl/src/curl_memrchr.c @@ -1,64 +1,64 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 (void *)p; - p--; - } - } - return NULL; -} - -#endif /* HAVE_MEMRCHR */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 (void *)p;
+ p--;
+ }
+ }
+ return NULL;
+}
+
+#endif /* HAVE_MEMRCHR */
diff --git a/libs/libcurl/src/curl_memrchr.h b/libs/libcurl/src/curl_memrchr.h index e7654e1d9a..fe2181c68d 100644 --- a/libs/libcurl/src/curl_memrchr.h +++ b/libs/libcurl/src/curl_memrchr.h @@ -1,46 +1,46 @@ -#ifndef HEADER_CURL_MEMRCHR_H -#define HEADER_CURL_MEMRCHR_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - -#ifdef HAVE_STRING_H -# include <string.h> -#endif -#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 */ +#ifndef HEADER_CURL_MEMRCHR_H
+#define HEADER_CURL_MEMRCHR_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+#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 */
diff --git a/libs/libcurl/src/curl_multibyte.c b/libs/libcurl/src/curl_multibyte.c index 309dccbfef..ad35b92e4d 100644 --- a/libs/libcurl/src/curl_multibyte.c +++ b/libs/libcurl/src/curl_multibyte.c @@ -1,179 +1,179 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 memdebug.h and - * curl_memory.h are purposely not included in this file. See test 1132. - * - * The functions in this file are curlx functions which are not tracked by the - * curl memory tracker memdebug. - */ - -#include "curl_setup.h" - -#if defined(WIN32) - -#include "curl_multibyte.h" - -/* - * MultiByte conversions using Windows kernel32 library. - */ - -wchar_t *curlx_convert_UTF8_to_wchar(const char *str_utf8) -{ - wchar_t *str_w = NULL; - - if(str_utf8) { - int str_w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, - str_utf8, -1, NULL, 0); - if(str_w_len > 0) { - str_w = malloc(str_w_len * sizeof(wchar_t)); - if(str_w) { - if(MultiByteToWideChar(CP_UTF8, 0, str_utf8, -1, str_w, - str_w_len) == 0) { - free(str_w); - return NULL; - } - } - } - } - - return str_w; -} - -char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w) -{ - char *str_utf8 = NULL; - - if(str_w) { - int bytes = WideCharToMultiByte(CP_UTF8, 0, str_w, -1, - NULL, 0, NULL, NULL); - if(bytes > 0) { - str_utf8 = malloc(bytes); - if(str_utf8) { - if(WideCharToMultiByte(CP_UTF8, 0, str_w, -1, str_utf8, bytes, - NULL, NULL) == 0) { - free(str_utf8); - return NULL; - } - } - } - } - - return str_utf8; -} - -#endif /* WIN32 */ - -#if defined(USE_WIN32_LARGE_FILES) || defined(USE_WIN32_SMALL_FILES) - -int curlx_win32_open(const char *filename, int oflag, ...) -{ - int pmode = 0; - -#ifdef _UNICODE - int result = -1; - 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) { - result = _wopen(filename_w, oflag, pmode); - curlx_unicodefree(filename_w); - } - else - errno = EINVAL; - return result; -#else - return (_open)(filename, oflag, pmode); -#endif -} - -FILE *curlx_win32_fopen(const char *filename, const char *mode) -{ -#ifdef _UNICODE - FILE *result = NULL; - 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) - result = _wfopen(filename_w, mode_w); - else - errno = EINVAL; - curlx_unicodefree(filename_w); - curlx_unicodefree(mode_w); - return result; -#else - return (fopen)(filename, mode); -#endif -} - -int curlx_win32_stat(const char *path, struct_stat *buffer) -{ -#ifdef _UNICODE - int result = -1; - wchar_t *path_w = curlx_convert_UTF8_to_wchar(path); - if(path_w) { -#if defined(USE_WIN32_SMALL_FILES) - result = _wstat(path_w, buffer); -#else - result = _wstati64(path_w, buffer); -#endif - curlx_unicodefree(path_w); - } - else - errno = EINVAL; - return result; -#else -#if defined(USE_WIN32_SMALL_FILES) - return _stat(path, buffer); -#else - return _stati64(path, buffer); -#endif -#endif -} - -int curlx_win32_access(const char *path, int mode) -{ -#if defined(_UNICODE) - int result = -1; - wchar_t *path_w = curlx_convert_UTF8_to_wchar(path); - if(path_w) { - result = _waccess(path_w, mode); - curlx_unicodefree(path_w); - } - else - errno = EINVAL; - return result; -#else - return _access(path, mode); -#endif -} - -#endif /* USE_WIN32_LARGE_FILES || USE_WIN32_SMALL_FILES */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 memdebug.h and
+ * curl_memory.h are purposely not included in this file. See test 1132.
+ *
+ * The functions in this file are curlx functions which are not tracked by the
+ * curl memory tracker memdebug.
+ */
+
+#include "curl_setup.h"
+
+#if defined(WIN32)
+
+#include "curl_multibyte.h"
+
+/*
+ * MultiByte conversions using Windows kernel32 library.
+ */
+
+wchar_t *curlx_convert_UTF8_to_wchar(const char *str_utf8)
+{
+ wchar_t *str_w = NULL;
+
+ if(str_utf8) {
+ int str_w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
+ str_utf8, -1, NULL, 0);
+ if(str_w_len > 0) {
+ str_w = malloc(str_w_len * sizeof(wchar_t));
+ if(str_w) {
+ if(MultiByteToWideChar(CP_UTF8, 0, str_utf8, -1, str_w,
+ str_w_len) == 0) {
+ free(str_w);
+ return NULL;
+ }
+ }
+ }
+ }
+
+ return str_w;
+}
+
+char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w)
+{
+ char *str_utf8 = NULL;
+
+ if(str_w) {
+ int bytes = WideCharToMultiByte(CP_UTF8, 0, str_w, -1,
+ NULL, 0, NULL, NULL);
+ if(bytes > 0) {
+ str_utf8 = malloc(bytes);
+ if(str_utf8) {
+ if(WideCharToMultiByte(CP_UTF8, 0, str_w, -1, str_utf8, bytes,
+ NULL, NULL) == 0) {
+ free(str_utf8);
+ return NULL;
+ }
+ }
+ }
+ }
+
+ return str_utf8;
+}
+
+#endif /* WIN32 */
+
+#if defined(USE_WIN32_LARGE_FILES) || defined(USE_WIN32_SMALL_FILES)
+
+int curlx_win32_open(const char *filename, int oflag, ...)
+{
+ int pmode = 0;
+
+#ifdef _UNICODE
+ int result = -1;
+ 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) {
+ result = _wopen(filename_w, oflag, pmode);
+ curlx_unicodefree(filename_w);
+ }
+ else
+ errno = EINVAL;
+ return result;
+#else
+ return (_open)(filename, oflag, pmode);
+#endif
+}
+
+FILE *curlx_win32_fopen(const char *filename, const char *mode)
+{
+#ifdef _UNICODE
+ FILE *result = NULL;
+ 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)
+ result = _wfopen(filename_w, mode_w);
+ else
+ errno = EINVAL;
+ curlx_unicodefree(filename_w);
+ curlx_unicodefree(mode_w);
+ return result;
+#else
+ return (fopen)(filename, mode);
+#endif
+}
+
+int curlx_win32_stat(const char *path, struct_stat *buffer)
+{
+#ifdef _UNICODE
+ int result = -1;
+ wchar_t *path_w = curlx_convert_UTF8_to_wchar(path);
+ if(path_w) {
+#if defined(USE_WIN32_SMALL_FILES)
+ result = _wstat(path_w, buffer);
+#else
+ result = _wstati64(path_w, buffer);
+#endif
+ curlx_unicodefree(path_w);
+ }
+ else
+ errno = EINVAL;
+ return result;
+#else
+#if defined(USE_WIN32_SMALL_FILES)
+ return _stat(path, buffer);
+#else
+ return _stati64(path, buffer);
+#endif
+#endif
+}
+
+int curlx_win32_access(const char *path, int mode)
+{
+#if defined(_UNICODE)
+ int result = -1;
+ wchar_t *path_w = curlx_convert_UTF8_to_wchar(path);
+ if(path_w) {
+ result = _waccess(path_w, mode);
+ curlx_unicodefree(path_w);
+ }
+ else
+ errno = EINVAL;
+ return result;
+#else
+ return _access(path, mode);
+#endif
+}
+
+#endif /* USE_WIN32_LARGE_FILES || USE_WIN32_SMALL_FILES */
diff --git a/libs/libcurl/src/curl_multibyte.h b/libs/libcurl/src/curl_multibyte.h index 929714873a..24b2d9661b 100644 --- a/libs/libcurl/src/curl_multibyte.h +++ b/libs/libcurl/src/curl_multibyte.h @@ -1,91 +1,91 @@ -#ifndef HEADER_CURL_MULTIBYTE_H -#define HEADER_CURL_MULTIBYTE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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(WIN32) - - /* - * MultiByte conversions using Windows kernel32 library. - */ - -wchar_t *curlx_convert_UTF8_to_wchar(const char *str_utf8); -char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w); -#endif /* WIN32 */ - -/* - * Macros curlx_convert_UTF8_to_tchar(), curlx_convert_tchar_to_UTF8() - * and curlx_unicodefree() main purpose is to minimize the number of - * preprocessor conditional directives needed by code using these - * to differentiate UNICODE from non-UNICODE builds. - * - * In the case of a non-UNICODE build the tchar strings are char strings that - * are duplicated via strdup and remain in whatever the passed in encoding is, - * which is assumed to be UTF-8 but may be other encoding. Therefore the - * significance of the conversion functions is primarily for UNICODE builds. - * - * Allocated memory should be free'd with curlx_unicodefree(). - * - * Note: Because these are curlx functions their memory usage is not tracked - * by the curl memory tracker memdebug. You'll notice that curlx function-like - * macros call free and strdup in parentheses, eg (strdup)(ptr), and that's to - * ensure that the curl memdebug override macros do not replace them. - */ - -#if defined(UNICODE) && defined(WIN32) - -#define curlx_convert_UTF8_to_tchar(ptr) curlx_convert_UTF8_to_wchar((ptr)) -#define curlx_convert_tchar_to_UTF8(ptr) curlx_convert_wchar_to_UTF8((ptr)) - -typedef union { - unsigned short *tchar_ptr; - const unsigned short *const_tchar_ptr; - unsigned short *tbyte_ptr; - const unsigned short *const_tbyte_ptr; -} xcharp_u; - -#else - -#define curlx_convert_UTF8_to_tchar(ptr) (strdup)(ptr) -#define curlx_convert_tchar_to_UTF8(ptr) (strdup)(ptr) - -typedef union { - char *tchar_ptr; - const char *const_tchar_ptr; - unsigned char *tbyte_ptr; - const unsigned char *const_tbyte_ptr; -} xcharp_u; - -#endif /* UNICODE && WIN32 */ - -#define curlx_unicodefree(ptr) \ - do { \ - if(ptr) { \ - (free)(ptr); \ - (ptr) = NULL; \ - } \ - } while(0) - -#endif /* HEADER_CURL_MULTIBYTE_H */ +#ifndef HEADER_CURL_MULTIBYTE_H
+#define HEADER_CURL_MULTIBYTE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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(WIN32)
+
+ /*
+ * MultiByte conversions using Windows kernel32 library.
+ */
+
+wchar_t *curlx_convert_UTF8_to_wchar(const char *str_utf8);
+char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w);
+#endif /* WIN32 */
+
+/*
+ * Macros curlx_convert_UTF8_to_tchar(), curlx_convert_tchar_to_UTF8()
+ * and curlx_unicodefree() main purpose is to minimize the number of
+ * preprocessor conditional directives needed by code using these
+ * to differentiate UNICODE from non-UNICODE builds.
+ *
+ * In the case of a non-UNICODE build the tchar strings are char strings that
+ * are duplicated via strdup and remain in whatever the passed in encoding is,
+ * which is assumed to be UTF-8 but may be other encoding. Therefore the
+ * significance of the conversion functions is primarily for UNICODE builds.
+ *
+ * Allocated memory should be free'd with curlx_unicodefree().
+ *
+ * Note: Because these are curlx functions their memory usage is not tracked
+ * by the curl memory tracker memdebug. You'll notice that curlx function-like
+ * macros call free and strdup in parentheses, eg (strdup)(ptr), and that's to
+ * ensure that the curl memdebug override macros do not replace them.
+ */
+
+#if defined(UNICODE) && defined(WIN32)
+
+#define curlx_convert_UTF8_to_tchar(ptr) curlx_convert_UTF8_to_wchar((ptr))
+#define curlx_convert_tchar_to_UTF8(ptr) curlx_convert_wchar_to_UTF8((ptr))
+
+typedef union {
+ unsigned short *tchar_ptr;
+ const unsigned short *const_tchar_ptr;
+ unsigned short *tbyte_ptr;
+ const unsigned short *const_tbyte_ptr;
+} xcharp_u;
+
+#else
+
+#define curlx_convert_UTF8_to_tchar(ptr) (strdup)(ptr)
+#define curlx_convert_tchar_to_UTF8(ptr) (strdup)(ptr)
+
+typedef union {
+ char *tchar_ptr;
+ const char *const_tchar_ptr;
+ unsigned char *tbyte_ptr;
+ const unsigned char *const_tbyte_ptr;
+} xcharp_u;
+
+#endif /* UNICODE && WIN32 */
+
+#define curlx_unicodefree(ptr) \
+ do { \
+ if(ptr) { \
+ (free)(ptr); \
+ (ptr) = NULL; \
+ } \
+ } while(0)
+
+#endif /* HEADER_CURL_MULTIBYTE_H */
diff --git a/libs/libcurl/src/curl_ntlm_core.c b/libs/libcurl/src/curl_ntlm_core.c index 38e193c186..8db35198e4 100644 --- a/libs/libcurl/src/curl_ntlm_core.c +++ b/libs/libcurl/src/curl_ntlm_core.c @@ -1,722 +1,723 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_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_GNUTLS - 3. USE_NSS - 4. USE_MBEDTLS - 5. USE_SECTRANSP - 6. USE_OS400CRYPTO - 7. 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 and NSS have higher priority than Windows Crypt, due - to issues with the latter supporting NTLM2Session responses - in NTLM type-3 messages. - */ - -#if defined(USE_OPENSSL) - #include <openssl/opensslconf.h> - #if !defined(OPENSSL_NO_DES) && !defined(OPENSSL_NO_DEPRECATED_3_0) - #define USE_OPENSSL_DES - #endif -#endif - -#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL) - -#if defined(USE_OPENSSL) -# include <openssl/des.h> -# include <openssl/md5.h> -# include <openssl/ssl.h> -# include <openssl/rand.h> -#else -# include <wolfssl/options.h> -# include <wolfssl/openssl/des.h> -# include <wolfssl/openssl/md5.h> -# include <wolfssl/openssl/ssl.h> -# include <wolfssl/openssl/rand.h> -#endif - -# if (defined(OPENSSL_VERSION_NUMBER) && \ - (OPENSSL_VERSION_NUMBER < 0x00907001L)) && !defined(USE_WOLFSSL) -# define DES_key_schedule des_key_schedule -# define DES_cblock des_cblock -# define DES_set_odd_parity des_set_odd_parity -# define DES_set_key des_set_key -# define DES_ecb_encrypt des_ecb_encrypt -# define DESKEY(x) x -# define DESKEYARG(x) x -# else -# define DESKEYARG(x) *x -# define DESKEY(x) &x -# endif - -#elif defined(USE_GNUTLS) - -# include <nettle/des.h> - -#elif defined(USE_NSS) - -# include <nss.h> -# include <pk11pub.h> -# include <hasht.h> - -#elif defined(USE_MBEDTLS) - -# include <mbedtls/des.h> - -#elif defined(USE_SECTRANSP) - -# include <CommonCrypto/CommonCryptor.h> -# include <CommonCrypto/CommonDigest.h> - -#elif defined(USE_OS400CRYPTO) -# include "cipher.mih" /* mih/cipher */ -#elif defined(USE_WIN32_CRYPTO) -# include <wincrypt.h> -#else -# error "Can't 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 "warnless.h" -#include "curl_endian.h" -#include "curl_des.h" -#include "curl_md4.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#define NTLMv2_BLOB_SIGNATURE "\x01\x01\x00\x00" -#define NTLMv2_BLOB_LEN (44 -16 + ntlm->target_info_len + 4) - -/* -* 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] = key_56[0]; - key[1] = (unsigned char)(((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1)); - key[2] = (unsigned char)(((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2)); - key[3] = (unsigned char)(((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3)); - key[4] = (unsigned char)(((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4)); - key[5] = (unsigned char)(((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5)); - key[6] = (unsigned char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6)); - key[7] = (unsigned char) ((key_56[6] << 1) & 0xFF); -} - -#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL) -/* - * Turns a 56 bit key into the 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_NSS) - -/* - * Expands a 56 bit key KEY_56 to 64 bit and encrypts 64 bit of data, using - * the expanded key. The caller is responsible for giving 64 bit of valid - * data is IN and (at least) 64 bit large buffer as OUT. - */ -static bool encrypt_des(const unsigned char *in, unsigned char *out, - const unsigned char *key_56) -{ - const CK_MECHANISM_TYPE mech = CKM_DES_ECB; /* DES cipher in ECB mode */ - char key[8]; /* expanded 64 bit key */ - SECItem key_item; - PK11SymKey *symkey = NULL; - SECItem *param = NULL; - PK11Context *ctx = NULL; - int out_len; /* not used, required by NSS */ - bool rv = FALSE; - - /* use internal slot for DES encryption (requires NSS to be initialized) */ - PK11SlotInfo *slot = PK11_GetInternalKeySlot(); - if(!slot) - return FALSE; - - /* 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)); - - /* Import the key */ - key_item.data = (unsigned char *)key; - key_item.len = sizeof(key); - symkey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, CKA_ENCRYPT, - &key_item, NULL); - if(!symkey) - goto fail; - - /* Create the DES encryption context */ - param = PK11_ParamFromIV(mech, /* no IV in ECB mode */ NULL); - if(!param) - goto fail; - ctx = PK11_CreateContextBySymKey(mech, CKA_ENCRYPT, symkey, param); - if(!ctx) - goto fail; - - /* Perform the encryption */ - if(SECSuccess == PK11_CipherOp(ctx, out, &out_len, /* outbuflen */ 8, - (unsigned char *)in, /* inbuflen */ 8) - && SECSuccess == PK11_Finalize(ctx)) - rv = /* all OK */ TRUE; - -fail: - /* cleanup */ - if(ctx) - PK11_DestroyContext(ctx, PR_TRUE); - if(symkey) - PK11_FreeSymKey(symkey); - if(param) - SECITEM_FreeItem(param, PR_TRUE); - PK11_FreeSlot(slot); - return rv; -} - -#elif defined(USE_MBEDTLS) - -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_SECTRANSP) - -static bool encrypt_des(const unsigned char *in, unsigned char *out, - const unsigned char *key_56) -{ - char key[8]; - size_t out_len; - CCCryptorStatus err; - - /* Expand the 56-bit key to 64-bits */ - extend_key_56_to_64(key_56, key); - - /* Set the key parity to odd */ - Curl_des_set_odd_parity((unsigned char *) key, sizeof(key)); - - /* Perform the encryption */ - err = CCCrypt(kCCEncrypt, kCCAlgorithmDES, kCCOptionECBMode, key, - kCCKeySizeDES, NULL, in, 8 /* inbuflen */, out, - 8 /* outbuflen */, &out_len); - - return err == kCCSuccess; -} - -#elif defined(USE_OS400CRYPTO) - -static bool encrypt_des(const unsigned char *in, unsigned char *out, - 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 /* defined(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) -{ -#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL) - DES_key_schedule ks; - - setup_des_key(keys, DESKEY(ks)); - DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) results, - DESKEY(ks), DES_ENCRYPT); - - setup_des_key(keys + 7, DESKEY(ks)); - DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results + 8), - DESKEY(ks), DES_ENCRYPT); - - setup_des_key(keys + 14, DESKEY(ks)); - DES_ecb_encrypt((DES_cblock*) 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_NSS) || defined(USE_MBEDTLS) || defined(USE_SECTRANSP) \ - || 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); -#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. */ - -#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL) - DES_key_schedule ks; - - setup_des_key(pw, DESKEY(ks)); - DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)lmbuffer, - DESKEY(ks), DES_ENCRYPT); - - setup_des_key(pw + 7, DESKEY(ks)); - DES_ecb_encrypt((DES_cblock *)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_NSS) || defined(USE_MBEDTLS) || defined(USE_SECTRANSP) \ - || 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'; - } -} - -#if !defined(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; - if(len > SIZE_T_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. */ - Curl_md4it(ntbuffer, pw, 2 * len); - memset(ntbuffer + 16, 0, 21 - 16); - - free(pw); - - return CURLE_OK; -} - -#if !defined(USE_WINDOWS_SSPI) - -/* 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_C(11644473600)) * 10000000; - ft->dwLowDateTime = (unsigned int) (t & 0xFFFFFFFF); - ft->dwHighDateTime = (unsigned int) (t >> 32); -#else - unsigned int r, s; - unsigned int i; - - ft->dwLowDateTime = t & 0xFFFFFFFF; - ft->dwHighDateTime = 0; - -# ifndef HAVE_TIME_T_UNSIGNED - /* Extend sign if needed. */ - if(ft->dwLowDateTime & 0x80000000) - ft->dwHighDateTime = ~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 */ - 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); - 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 */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_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_GNUTLS
+ 3. USE_NSS
+ 4. USE_MBEDTLS
+ 5. USE_SECTRANSP
+ 6. USE_OS400CRYPTO
+ 7. 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 and NSS have higher priority than Windows Crypt, due
+ to issues with the latter supporting NTLM2Session responses
+ in NTLM type-3 messages.
+ */
+
+#if defined(USE_OPENSSL)
+ #include <openssl/opensslconf.h>
+ #if !defined(OPENSSL_NO_DES) && !defined(OPENSSL_NO_DEPRECATED_3_0)
+ #define USE_OPENSSL_DES
+ #endif
+#endif
+
+#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL)
+
+#if defined(USE_OPENSSL)
+# include <openssl/des.h>
+# include <openssl/md5.h>
+# include <openssl/ssl.h>
+# include <openssl/rand.h>
+#else
+# include <wolfssl/options.h>
+# include <wolfssl/openssl/des.h>
+# include <wolfssl/openssl/md5.h>
+# include <wolfssl/openssl/ssl.h>
+# include <wolfssl/openssl/rand.h>
+#endif
+
+# if (defined(OPENSSL_VERSION_NUMBER) && \
+ (OPENSSL_VERSION_NUMBER < 0x00907001L)) && !defined(USE_WOLFSSL)
+# define DES_key_schedule des_key_schedule
+# define DES_cblock des_cblock
+# define DES_set_odd_parity des_set_odd_parity
+# define DES_set_key des_set_key
+# define DES_ecb_encrypt des_ecb_encrypt
+# define DESKEY(x) x
+# define DESKEYARG(x) x
+# else
+# define DESKEYARG(x) *x
+# define DESKEY(x) &x
+# endif
+
+#elif defined(USE_GNUTLS)
+
+# include <nettle/des.h>
+
+#elif defined(USE_NSS)
+
+# include <nss.h>
+# include <pk11pub.h>
+# include <hasht.h>
+
+#elif defined(USE_MBEDTLS)
+
+# include <mbedtls/des.h>
+
+#elif defined(USE_SECTRANSP)
+
+# include <CommonCrypto/CommonCryptor.h>
+# include <CommonCrypto/CommonDigest.h>
+
+#elif defined(USE_OS400CRYPTO)
+# include "cipher.mih" /* mih/cipher */
+#elif defined(USE_WIN32_CRYPTO)
+# include <wincrypt.h>
+#else
+# error "Can't 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 "warnless.h"
+#include "curl_endian.h"
+#include "curl_des.h"
+#include "curl_md4.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define NTLMv2_BLOB_SIGNATURE "\x01\x01\x00\x00"
+#define NTLMv2_BLOB_LEN (44 -16 + ntlm->target_info_len + 4)
+
+/*
+* 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] = key_56[0];
+ key[1] = (unsigned char)(((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1));
+ key[2] = (unsigned char)(((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2));
+ key[3] = (unsigned char)(((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3));
+ key[4] = (unsigned char)(((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4));
+ key[5] = (unsigned char)(((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5));
+ key[6] = (unsigned char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6));
+ key[7] = (unsigned char) ((key_56[6] << 1) & 0xFF);
+}
+
+#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL)
+/*
+ * Turns a 56 bit key into the 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_NSS)
+
+/*
+ * encrypt_des() expands a 56 bit key KEY_56 to 64 bit and encrypts 64 bit of
+ * data, using the expanded key. IN should point to 64 bits of source data,
+ * OUT to a 64 bit output buffer.
+ */
+static bool encrypt_des(const unsigned char *in, unsigned char *out,
+ const unsigned char *key_56)
+{
+ const CK_MECHANISM_TYPE mech = CKM_DES_ECB; /* DES cipher in ECB mode */
+ char key[8]; /* expanded 64 bit key */
+ SECItem key_item;
+ PK11SymKey *symkey = NULL;
+ SECItem *param = NULL;
+ PK11Context *ctx = NULL;
+ int out_len; /* not used, required by NSS */
+ bool rv = FALSE;
+
+ /* use internal slot for DES encryption (requires NSS to be initialized) */
+ PK11SlotInfo *slot = PK11_GetInternalKeySlot();
+ if(!slot)
+ return FALSE;
+
+ /* 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));
+
+ /* Import the key */
+ key_item.data = (unsigned char *)key;
+ key_item.len = sizeof(key);
+ symkey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, CKA_ENCRYPT,
+ &key_item, NULL);
+ if(!symkey)
+ goto fail;
+
+ /* Create the DES encryption context */
+ param = PK11_ParamFromIV(mech, /* no IV in ECB mode */ NULL);
+ if(!param)
+ goto fail;
+ ctx = PK11_CreateContextBySymKey(mech, CKA_ENCRYPT, symkey, param);
+ if(!ctx)
+ goto fail;
+
+ /* Perform the encryption */
+ if(SECSuccess == PK11_CipherOp(ctx, out, &out_len, /* outbuflen */ 8,
+ (unsigned char *)in, /* inbuflen */ 8)
+ && SECSuccess == PK11_Finalize(ctx))
+ rv = /* all OK */ TRUE;
+
+fail:
+ /* cleanup */
+ if(ctx)
+ PK11_DestroyContext(ctx, PR_TRUE);
+ if(symkey)
+ PK11_FreeSymKey(symkey);
+ if(param)
+ SECITEM_FreeItem(param, PR_TRUE);
+ PK11_FreeSlot(slot);
+ return rv;
+}
+
+#elif defined(USE_MBEDTLS)
+
+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_SECTRANSP)
+
+static bool encrypt_des(const unsigned char *in, unsigned char *out,
+ const unsigned char *key_56)
+{
+ char key[8];
+ size_t out_len;
+ CCCryptorStatus err;
+
+ /* Expand the 56-bit key to 64-bits */
+ extend_key_56_to_64(key_56, key);
+
+ /* Set the key parity to odd */
+ Curl_des_set_odd_parity((unsigned char *) key, sizeof(key));
+
+ /* Perform the encryption */
+ err = CCCrypt(kCCEncrypt, kCCAlgorithmDES, kCCOptionECBMode, key,
+ kCCKeySizeDES, NULL, in, 8 /* inbuflen */, out,
+ 8 /* outbuflen */, &out_len);
+
+ return err == kCCSuccess;
+}
+
+#elif defined(USE_OS400CRYPTO)
+
+static bool encrypt_des(const unsigned char *in, unsigned char *out,
+ 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 /* defined(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)
+{
+#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL)
+ DES_key_schedule ks;
+
+ setup_des_key(keys, DESKEY(ks));
+ DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) results,
+ DESKEY(ks), DES_ENCRYPT);
+
+ setup_des_key(keys + 7, DESKEY(ks));
+ DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results + 8),
+ DESKEY(ks), DES_ENCRYPT);
+
+ setup_des_key(keys + 14, DESKEY(ks));
+ DES_ecb_encrypt((DES_cblock*) 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_NSS) || defined(USE_MBEDTLS) || defined(USE_SECTRANSP) \
+ || 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);
+#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. */
+
+#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL)
+ DES_key_schedule ks;
+
+ setup_des_key(pw, DESKEY(ks));
+ DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)lmbuffer,
+ DESKEY(ks), DES_ENCRYPT);
+
+ setup_des_key(pw + 7, DESKEY(ks));
+ DES_ecb_encrypt((DES_cblock *)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_NSS) || defined(USE_MBEDTLS) || defined(USE_SECTRANSP) \
+ || 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';
+ }
+}
+
+#if !defined(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;
+ if(len > SIZE_T_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. */
+ Curl_md4it(ntbuffer, pw, 2 * len);
+ memset(ntbuffer + 16, 0, 21 - 16);
+
+ free(pw);
+
+ return CURLE_OK;
+}
+
+#if !defined(USE_WINDOWS_SSPI)
+
+/* 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_C(11644473600)) * 10000000;
+ ft->dwLowDateTime = (unsigned int) (t & 0xFFFFFFFF);
+ ft->dwHighDateTime = (unsigned int) (t >> 32);
+#else
+ unsigned int r, s;
+ unsigned int i;
+
+ ft->dwLowDateTime = t & 0xFFFFFFFF;
+ ft->dwHighDateTime = 0;
+
+# ifndef HAVE_TIME_T_UNSIGNED
+ /* Extend sign if needed. */
+ if(ft->dwLowDateTime & 0x80000000)
+ ft->dwHighDateTime = ~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 */
+ 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 */
diff --git a/libs/libcurl/src/curl_ntlm_core.h b/libs/libcurl/src/curl_ntlm_core.h index 60444c9dc6..03fd8ef34d 100644 --- a/libs/libcurl/src/curl_ntlm_core.h +++ b/libs/libcurl/src/curl_ntlm_core.h @@ -1,88 +1,88 @@ -#ifndef HEADER_CURL_NTLM_CORE_H -#define HEADER_CURL_NTLM_CORE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_CURL_NTLM_CORE) - -/* If NSS is the first available SSL backend (see order in curl_ntlm_core.c) - then it must be initialized to be used by NTLM. */ -#if !defined(USE_OPENSSL) && \ - !defined(USE_WOLFSSL) && \ - !defined(USE_GNUTLS) && \ - defined(USE_NSS) -#define NTLM_NEEDS_NSS_INIT -#endif - -#ifdef USE_WOLFSSL -# include <wolfssl/options.h> -# include <wolfssl/openssl/ssl.h> -#elif defined(USE_OPENSSL) -# include <openssl/ssl.h> -#endif - -/* 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 */); - -#if !defined(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 */ +#ifndef HEADER_CURL_NTLM_CORE_H
+#define HEADER_CURL_NTLM_CORE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_CURL_NTLM_CORE)
+
+/* If NSS is the first available SSL backend (see order in curl_ntlm_core.c)
+ then it must be initialized to be used by NTLM. */
+#if !defined(USE_OPENSSL) && \
+ !defined(USE_WOLFSSL) && \
+ !defined(USE_GNUTLS) && \
+ defined(USE_NSS)
+#define NTLM_NEEDS_NSS_INIT
+#endif
+
+#ifdef USE_WOLFSSL
+# include <wolfssl/options.h>
+# include <wolfssl/openssl/ssl.h>
+#elif defined(USE_OPENSSL)
+# include <openssl/ssl.h>
+#endif
+
+/* 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 */);
+
+#if !defined(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 */
diff --git a/libs/libcurl/src/curl_ntlm_wb.c b/libs/libcurl/src/curl_ntlm_wb.c index 33dcf0ce25..510b311a7e 100644 --- a/libs/libcurl/src/curl_ntlm_wb.c +++ b/libs/libcurl/src/curl_ntlm_wb.c @@ -1,500 +1,500 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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(USE_NTLM) && \ - defined(NTLM_WB_ENABLED) - -/* - * NTLM details: - * - * https://davenport.sourceforge.net/ntlm.html - * https://www.innovation.ch/java/ntlm.html - */ - -#define DEBUG_ME 0 - -#ifdef HAVE_SYS_WAIT_H -#include <sys/wait.h> -#endif -#ifdef HAVE_SIGNAL_H -#include <signal.h> -#endif -#ifdef HAVE_PWD_H -#include <pwd.h> -#endif - -#include "urldata.h" -#include "sendf.h" -#include "select.h" -#include "vauth/ntlm.h" -#include "curl_ntlm_core.h" -#include "curl_ntlm_wb.h" -#include "url.h" -#include "strerror.h" -#include "strdup.h" -#include "strcase.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#if DEBUG_ME -# define DEBUG_OUT(x) x -#else -# define DEBUG_OUT(x) Curl_nop_stmt -#endif - -/* Portable 'sclose_nolog' used only in child process instead of 'sclose' - to avoid fooling the socket leak detector */ -#if defined(HAVE_CLOSESOCKET) -# define sclose_nolog(x) closesocket((x)) -#elif defined(HAVE_CLOSESOCKET_CAMEL) -# define sclose_nolog(x) CloseSocket((x)) -#else -# define sclose_nolog(x) close((x)) -#endif - -static void ntlm_wb_cleanup(struct ntlmdata *ntlm) -{ - if(ntlm->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD) { - sclose(ntlm->ntlm_auth_hlpr_socket); - ntlm->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD; - } - - if(ntlm->ntlm_auth_hlpr_pid) { - int i; - for(i = 0; i < 4; i++) { - pid_t ret = waitpid(ntlm->ntlm_auth_hlpr_pid, NULL, WNOHANG); - if(ret == ntlm->ntlm_auth_hlpr_pid || errno == ECHILD) - break; - switch(i) { - case 0: - kill(ntlm->ntlm_auth_hlpr_pid, SIGTERM); - break; - case 1: - /* Give the process another moment to shut down cleanly before - bringing down the axe */ - Curl_wait_ms(1); - break; - case 2: - kill(ntlm->ntlm_auth_hlpr_pid, SIGKILL); - break; - case 3: - break; - } - } - ntlm->ntlm_auth_hlpr_pid = 0; - } - - Curl_safefree(ntlm->challenge); - Curl_safefree(ntlm->response); -} - -static CURLcode ntlm_wb_init(struct Curl_easy *data, struct ntlmdata *ntlm, - const char *userp) -{ - curl_socket_t sockfds[2]; - pid_t child_pid; - const char *username; - char *slash, *domain = NULL; - const char *ntlm_auth = NULL; - char *ntlm_auth_alloc = NULL; -#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID) - struct passwd pw, *pw_res; - char pwbuf[1024]; -#endif - char buffer[STRERROR_LEN]; - -#if defined(CURL_DISABLE_VERBOSE_STRINGS) - (void) data; -#endif - - /* Return if communication with ntlm_auth already set up */ - if(ntlm->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD || - ntlm->ntlm_auth_hlpr_pid) - return CURLE_OK; - - username = userp; - /* The real ntlm_auth really doesn't like being invoked with an - empty username. It won't make inferences for itself, and expects - the client to do so (mostly because it's really designed for - servers like squid to use for auth, and client support is an - afterthought for it). So try hard to provide a suitable username - if we don't already have one. But if we can't, provide the - empty one anyway. Perhaps they have an implementation of the - ntlm_auth helper which *doesn't* need it so we might as well try */ - if(!username || !username[0]) { - username = getenv("NTLMUSER"); - if(!username || !username[0]) - username = getenv("LOGNAME"); - if(!username || !username[0]) - username = getenv("USER"); -#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID) - if((!username || !username[0]) && - !getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res) && - pw_res) { - username = pw.pw_name; - } -#endif - if(!username || !username[0]) - username = userp; - } - slash = strpbrk(username, "\\/"); - if(slash) { - domain = strdup(username); - if(!domain) - return CURLE_OUT_OF_MEMORY; - slash = domain + (slash - username); - *slash = '\0'; - username = username + (slash - domain) + 1; - } - - /* For testing purposes, when DEBUGBUILD is defined and environment - variable CURL_NTLM_WB_FILE is set a fake_ntlm is used to perform - NTLM challenge/response which only accepts commands and output - strings pre-written in test case definitions */ -#ifdef DEBUGBUILD - ntlm_auth_alloc = curl_getenv("CURL_NTLM_WB_FILE"); - if(ntlm_auth_alloc) - ntlm_auth = ntlm_auth_alloc; - else -#endif - ntlm_auth = NTLM_WB_FILE; - - if(access(ntlm_auth, X_OK) != 0) { - failf(data, "Could not access ntlm_auth: %s errno %d: %s", - ntlm_auth, errno, Curl_strerror(errno, buffer, sizeof(buffer))); - goto done; - } - - if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds)) { - failf(data, "Could not open socket pair. errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - goto done; - } - - child_pid = fork(); - if(child_pid == -1) { - sclose(sockfds[0]); - sclose(sockfds[1]); - failf(data, "Could not fork. errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - goto done; - } - else if(!child_pid) { - /* - * child process - */ - - /* Don't use sclose in the child since it fools the socket leak detector */ - sclose_nolog(sockfds[0]); - if(dup2(sockfds[1], STDIN_FILENO) == -1) { - failf(data, "Could not redirect child stdin. errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - exit(1); - } - - if(dup2(sockfds[1], STDOUT_FILENO) == -1) { - failf(data, "Could not redirect child stdout. errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - exit(1); - } - - if(domain) - execl(ntlm_auth, ntlm_auth, - "--helper-protocol", "ntlmssp-client-1", - "--use-cached-creds", - "--username", username, - "--domain", domain, - NULL); - else - execl(ntlm_auth, ntlm_auth, - "--helper-protocol", "ntlmssp-client-1", - "--use-cached-creds", - "--username", username, - NULL); - - sclose_nolog(sockfds[1]); - failf(data, "Could not execl(). errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - exit(1); - } - - sclose(sockfds[1]); - ntlm->ntlm_auth_hlpr_socket = sockfds[0]; - ntlm->ntlm_auth_hlpr_pid = child_pid; - free(domain); - free(ntlm_auth_alloc); - return CURLE_OK; - -done: - free(domain); - free(ntlm_auth_alloc); - return CURLE_REMOTE_ACCESS_DENIED; -} - -/* if larger than this, something is seriously wrong */ -#define MAX_NTLM_WB_RESPONSE 100000 - -static CURLcode ntlm_wb_response(struct Curl_easy *data, struct ntlmdata *ntlm, - const char *input, curlntlm state) -{ - size_t len_in = strlen(input), len_out = 0; - struct dynbuf b; - char *ptr = NULL; - unsigned char *buf = (unsigned char *)data->state.buffer; - Curl_dyn_init(&b, MAX_NTLM_WB_RESPONSE); - - while(len_in > 0) { - ssize_t written = swrite(ntlm->ntlm_auth_hlpr_socket, input, len_in); - if(written == -1) { - /* Interrupted by a signal, retry it */ - if(errno == EINTR) - continue; - /* write failed if other errors happen */ - goto done; - } - input += written; - len_in -= written; - } - /* Read one line */ - while(1) { - ssize_t size = - sread(ntlm->ntlm_auth_hlpr_socket, buf, data->set.buffer_size); - if(size == -1) { - if(errno == EINTR) - continue; - goto done; - } - else if(size == 0) - goto done; - - if(Curl_dyn_addn(&b, buf, size)) - goto done; - - len_out = Curl_dyn_len(&b); - ptr = Curl_dyn_ptr(&b); - if(len_out && ptr[len_out - 1] == '\n') { - ptr[len_out - 1] = '\0'; - break; /* done! */ - } - /* loop */ - } - - /* Samba/winbind installed but not configured */ - if(state == NTLMSTATE_TYPE1 && - len_out == 3 && - ptr[0] == 'P' && ptr[1] == 'W') - goto done; - /* invalid response */ - if(len_out < 4) - goto done; - if(state == NTLMSTATE_TYPE1 && - (ptr[0]!='Y' || ptr[1]!='R' || ptr[2]!=' ')) - goto done; - if(state == NTLMSTATE_TYPE2 && - (ptr[0]!='K' || ptr[1]!='K' || ptr[2]!=' ') && - (ptr[0]!='A' || ptr[1]!='F' || ptr[2]!=' ')) - goto done; - - ntlm->response = strdup(ptr + 3); - Curl_dyn_free(&b); - if(!ntlm->response) - return CURLE_OUT_OF_MEMORY; - return CURLE_OK; -done: - Curl_dyn_free(&b); - return CURLE_REMOTE_ACCESS_DENIED; -} - -CURLcode Curl_input_ntlm_wb(struct Curl_easy *data, - struct connectdata *conn, - bool proxy, - const char *header) -{ - struct ntlmdata *ntlm = proxy ? &conn->proxyntlm : &conn->ntlm; - curlntlm *state = proxy ? &conn->proxy_ntlm_state : &conn->http_ntlm_state; - - (void) data; /* In case it gets unused by nop log macros. */ - - if(!checkprefix("NTLM", header)) - return CURLE_BAD_CONTENT_ENCODING; - - header += strlen("NTLM"); - while(*header && ISSPACE(*header)) - header++; - - if(*header) { - ntlm->challenge = strdup(header); - if(!ntlm->challenge) - return CURLE_OUT_OF_MEMORY; - - *state = NTLMSTATE_TYPE2; /* We got a type-2 message */ - } - else { - if(*state == NTLMSTATE_LAST) { - infof(data, "NTLM auth restarted"); - Curl_http_auth_cleanup_ntlm_wb(conn); - } - else if(*state == NTLMSTATE_TYPE3) { - infof(data, "NTLM handshake rejected"); - Curl_http_auth_cleanup_ntlm_wb(conn); - *state = NTLMSTATE_NONE; - return CURLE_REMOTE_ACCESS_DENIED; - } - else if(*state >= NTLMSTATE_TYPE1) { - infof(data, "NTLM handshake failure (internal error)"); - return CURLE_REMOTE_ACCESS_DENIED; - } - - *state = NTLMSTATE_TYPE1; /* We should send away a type-1 */ - } - - return CURLE_OK; -} - -/* - * This is for creating ntlm header output by delegating challenge/response - * to Samba's winbind daemon helper ntlm_auth. - */ -CURLcode Curl_output_ntlm_wb(struct Curl_easy *data, struct connectdata *conn, - bool proxy) -{ - /* point to the address of the pointer that holds the string to send to the - server, which is for a plain host or for a HTTP proxy */ - char **allocuserpwd; - /* point to the name and password for this */ - const char *userp; - struct ntlmdata *ntlm; - curlntlm *state; - struct auth *authp; - - CURLcode res = CURLE_OK; - - DEBUGASSERT(conn); - DEBUGASSERT(data); - - if(proxy) { -#ifndef CURL_DISABLE_PROXY - allocuserpwd = &data->state.aptr.proxyuserpwd; - userp = conn->http_proxy.user; - ntlm = &conn->proxyntlm; - state = &conn->proxy_ntlm_state; - authp = &data->state.authproxy; -#else - return CURLE_NOT_BUILT_IN; -#endif - } - else { - allocuserpwd = &data->state.aptr.userpwd; - userp = conn->user; - ntlm = &conn->ntlm; - state = &conn->http_ntlm_state; - authp = &data->state.authhost; - } - authp->done = FALSE; - - /* not set means empty */ - if(!userp) - userp = ""; - - switch(*state) { - case NTLMSTATE_TYPE1: - default: - /* Use Samba's 'winbind' daemon to support NTLM authentication, - * by delegating the NTLM challenge/response protocol to a helper - * in ntlm_auth. - * https://web.archive.org/web/20190925164737 - * /devel.squid-cache.org/ntlm/squid_helper_protocol.html - * https://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html - * https://www.samba.org/samba/docs/man/manpages-3/ntlm_auth.1.html - * Preprocessor symbol 'NTLM_WB_ENABLED' is defined when this - * feature is enabled and 'NTLM_WB_FILE' symbol holds absolute - * filename of ntlm_auth helper. - * If NTLM authentication using winbind fails, go back to original - * request handling process. - */ - /* Create communication with ntlm_auth */ - res = ntlm_wb_init(data, ntlm, userp); - if(res) - return res; - res = ntlm_wb_response(data, ntlm, "YR\n", *state); - if(res) - return res; - - free(*allocuserpwd); - *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", - proxy ? "Proxy-" : "", - ntlm->response); - DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd)); - Curl_safefree(ntlm->response); - if(!*allocuserpwd) - return CURLE_OUT_OF_MEMORY; - break; - - case NTLMSTATE_TYPE2: { - char *input = aprintf("TT %s\n", ntlm->challenge); - if(!input) - return CURLE_OUT_OF_MEMORY; - res = ntlm_wb_response(data, ntlm, input, *state); - free(input); - if(res) - return res; - - free(*allocuserpwd); - *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", - proxy ? "Proxy-" : "", - ntlm->response); - DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd)); - *state = NTLMSTATE_TYPE3; /* we sent a type-3 */ - authp->done = TRUE; - Curl_http_auth_cleanup_ntlm_wb(conn); - if(!*allocuserpwd) - return CURLE_OUT_OF_MEMORY; - break; - } - case NTLMSTATE_TYPE3: - /* connection is already authenticated, - * don't send a header in future requests */ - *state = NTLMSTATE_LAST; - /* FALLTHROUGH */ - case NTLMSTATE_LAST: - Curl_safefree(*allocuserpwd); - authp->done = TRUE; - break; - } - - return CURLE_OK; -} - -void Curl_http_auth_cleanup_ntlm_wb(struct connectdata *conn) -{ - ntlm_wb_cleanup(&conn->ntlm); - ntlm_wb_cleanup(&conn->proxyntlm); -} - -#endif /* !CURL_DISABLE_HTTP && USE_NTLM && NTLM_WB_ENABLED */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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(USE_NTLM) && \
+ defined(NTLM_WB_ENABLED)
+
+/*
+ * NTLM details:
+ *
+ * https://davenport.sourceforge.net/ntlm.html
+ * https://www.innovation.ch/java/ntlm.html
+ */
+
+#define DEBUG_ME 0
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "select.h"
+#include "vauth/ntlm.h"
+#include "curl_ntlm_core.h"
+#include "curl_ntlm_wb.h"
+#include "url.h"
+#include "strerror.h"
+#include "strdup.h"
+#include "strcase.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#if DEBUG_ME
+# define DEBUG_OUT(x) x
+#else
+# define DEBUG_OUT(x) Curl_nop_stmt
+#endif
+
+/* Portable 'sclose_nolog' used only in child process instead of 'sclose'
+ to avoid fooling the socket leak detector */
+#if defined(HAVE_CLOSESOCKET)
+# define sclose_nolog(x) closesocket((x))
+#elif defined(HAVE_CLOSESOCKET_CAMEL)
+# define sclose_nolog(x) CloseSocket((x))
+#else
+# define sclose_nolog(x) close((x))
+#endif
+
+static void ntlm_wb_cleanup(struct ntlmdata *ntlm)
+{
+ if(ntlm->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD) {
+ sclose(ntlm->ntlm_auth_hlpr_socket);
+ ntlm->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD;
+ }
+
+ if(ntlm->ntlm_auth_hlpr_pid) {
+ int i;
+ for(i = 0; i < 4; i++) {
+ pid_t ret = waitpid(ntlm->ntlm_auth_hlpr_pid, NULL, WNOHANG);
+ if(ret == ntlm->ntlm_auth_hlpr_pid || errno == ECHILD)
+ break;
+ switch(i) {
+ case 0:
+ kill(ntlm->ntlm_auth_hlpr_pid, SIGTERM);
+ break;
+ case 1:
+ /* Give the process another moment to shut down cleanly before
+ bringing down the axe */
+ Curl_wait_ms(1);
+ break;
+ case 2:
+ kill(ntlm->ntlm_auth_hlpr_pid, SIGKILL);
+ break;
+ case 3:
+ break;
+ }
+ }
+ ntlm->ntlm_auth_hlpr_pid = 0;
+ }
+
+ Curl_safefree(ntlm->challenge);
+ Curl_safefree(ntlm->response);
+}
+
+static CURLcode ntlm_wb_init(struct Curl_easy *data, struct ntlmdata *ntlm,
+ const char *userp)
+{
+ curl_socket_t sockfds[2];
+ pid_t child_pid;
+ const char *username;
+ char *slash, *domain = NULL;
+ const char *ntlm_auth = NULL;
+ char *ntlm_auth_alloc = NULL;
+#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
+ struct passwd pw, *pw_res;
+ char pwbuf[1024];
+#endif
+ char buffer[STRERROR_LEN];
+
+#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+ (void) data;
+#endif
+
+ /* Return if communication with ntlm_auth already set up */
+ if(ntlm->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD ||
+ ntlm->ntlm_auth_hlpr_pid)
+ return CURLE_OK;
+
+ username = userp;
+ /* The real ntlm_auth really doesn't like being invoked with an
+ empty username. It won't make inferences for itself, and expects
+ the client to do so (mostly because it's really designed for
+ servers like squid to use for auth, and client support is an
+ afterthought for it). So try hard to provide a suitable username
+ if we don't already have one. But if we can't, provide the
+ empty one anyway. Perhaps they have an implementation of the
+ ntlm_auth helper which *doesn't* need it so we might as well try */
+ if(!username || !username[0]) {
+ username = getenv("NTLMUSER");
+ if(!username || !username[0])
+ username = getenv("LOGNAME");
+ if(!username || !username[0])
+ username = getenv("USER");
+#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
+ if((!username || !username[0]) &&
+ !getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res) &&
+ pw_res) {
+ username = pw.pw_name;
+ }
+#endif
+ if(!username || !username[0])
+ username = userp;
+ }
+ slash = strpbrk(username, "\\/");
+ if(slash) {
+ domain = strdup(username);
+ if(!domain)
+ return CURLE_OUT_OF_MEMORY;
+ slash = domain + (slash - username);
+ *slash = '\0';
+ username = username + (slash - domain) + 1;
+ }
+
+ /* For testing purposes, when DEBUGBUILD is defined and environment
+ variable CURL_NTLM_WB_FILE is set a fake_ntlm is used to perform
+ NTLM challenge/response which only accepts commands and output
+ strings pre-written in test case definitions */
+#ifdef DEBUGBUILD
+ ntlm_auth_alloc = curl_getenv("CURL_NTLM_WB_FILE");
+ if(ntlm_auth_alloc)
+ ntlm_auth = ntlm_auth_alloc;
+ else
+#endif
+ ntlm_auth = NTLM_WB_FILE;
+
+ if(access(ntlm_auth, X_OK) != 0) {
+ failf(data, "Could not access ntlm_auth: %s errno %d: %s",
+ ntlm_auth, errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+ goto done;
+ }
+
+ if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds)) {
+ failf(data, "Could not open socket pair. errno %d: %s",
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+ goto done;
+ }
+
+ child_pid = fork();
+ if(child_pid == -1) {
+ sclose(sockfds[0]);
+ sclose(sockfds[1]);
+ failf(data, "Could not fork. errno %d: %s",
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+ goto done;
+ }
+ else if(!child_pid) {
+ /*
+ * child process
+ */
+
+ /* Don't use sclose in the child since it fools the socket leak detector */
+ sclose_nolog(sockfds[0]);
+ if(dup2(sockfds[1], STDIN_FILENO) == -1) {
+ failf(data, "Could not redirect child stdin. errno %d: %s",
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+ exit(1);
+ }
+
+ if(dup2(sockfds[1], STDOUT_FILENO) == -1) {
+ failf(data, "Could not redirect child stdout. errno %d: %s",
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+ exit(1);
+ }
+
+ if(domain)
+ execl(ntlm_auth, ntlm_auth,
+ "--helper-protocol", "ntlmssp-client-1",
+ "--use-cached-creds",
+ "--username", username,
+ "--domain", domain,
+ NULL);
+ else
+ execl(ntlm_auth, ntlm_auth,
+ "--helper-protocol", "ntlmssp-client-1",
+ "--use-cached-creds",
+ "--username", username,
+ NULL);
+
+ sclose_nolog(sockfds[1]);
+ failf(data, "Could not execl(). errno %d: %s",
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+ exit(1);
+ }
+
+ sclose(sockfds[1]);
+ ntlm->ntlm_auth_hlpr_socket = sockfds[0];
+ ntlm->ntlm_auth_hlpr_pid = child_pid;
+ free(domain);
+ free(ntlm_auth_alloc);
+ return CURLE_OK;
+
+done:
+ free(domain);
+ free(ntlm_auth_alloc);
+ return CURLE_REMOTE_ACCESS_DENIED;
+}
+
+/* if larger than this, something is seriously wrong */
+#define MAX_NTLM_WB_RESPONSE 100000
+
+static CURLcode ntlm_wb_response(struct Curl_easy *data, struct ntlmdata *ntlm,
+ const char *input, curlntlm state)
+{
+ size_t len_in = strlen(input), len_out = 0;
+ struct dynbuf b;
+ char *ptr = NULL;
+ unsigned char *buf = (unsigned char *)data->state.buffer;
+ Curl_dyn_init(&b, MAX_NTLM_WB_RESPONSE);
+
+ while(len_in > 0) {
+ ssize_t written = swrite(ntlm->ntlm_auth_hlpr_socket, input, len_in);
+ if(written == -1) {
+ /* Interrupted by a signal, retry it */
+ if(errno == EINTR)
+ continue;
+ /* write failed if other errors happen */
+ goto done;
+ }
+ input += written;
+ len_in -= written;
+ }
+ /* Read one line */
+ while(1) {
+ ssize_t size =
+ sread(ntlm->ntlm_auth_hlpr_socket, buf, data->set.buffer_size);
+ if(size == -1) {
+ if(errno == EINTR)
+ continue;
+ goto done;
+ }
+ else if(size == 0)
+ goto done;
+
+ if(Curl_dyn_addn(&b, buf, size))
+ goto done;
+
+ len_out = Curl_dyn_len(&b);
+ ptr = Curl_dyn_ptr(&b);
+ if(len_out && ptr[len_out - 1] == '\n') {
+ ptr[len_out - 1] = '\0';
+ break; /* done! */
+ }
+ /* loop */
+ }
+
+ /* Samba/winbind installed but not configured */
+ if(state == NTLMSTATE_TYPE1 &&
+ len_out == 3 &&
+ ptr[0] == 'P' && ptr[1] == 'W')
+ goto done;
+ /* invalid response */
+ if(len_out < 4)
+ goto done;
+ if(state == NTLMSTATE_TYPE1 &&
+ (ptr[0]!='Y' || ptr[1]!='R' || ptr[2]!=' '))
+ goto done;
+ if(state == NTLMSTATE_TYPE2 &&
+ (ptr[0]!='K' || ptr[1]!='K' || ptr[2]!=' ') &&
+ (ptr[0]!='A' || ptr[1]!='F' || ptr[2]!=' '))
+ goto done;
+
+ ntlm->response = strdup(ptr + 3);
+ Curl_dyn_free(&b);
+ if(!ntlm->response)
+ return CURLE_OUT_OF_MEMORY;
+ return CURLE_OK;
+done:
+ Curl_dyn_free(&b);
+ return CURLE_REMOTE_ACCESS_DENIED;
+}
+
+CURLcode Curl_input_ntlm_wb(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool proxy,
+ const char *header)
+{
+ struct ntlmdata *ntlm = proxy ? &conn->proxyntlm : &conn->ntlm;
+ curlntlm *state = proxy ? &conn->proxy_ntlm_state : &conn->http_ntlm_state;
+
+ (void) data; /* In case it gets unused by nop log macros. */
+
+ if(!checkprefix("NTLM", header))
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ header += strlen("NTLM");
+ while(*header && ISSPACE(*header))
+ header++;
+
+ if(*header) {
+ ntlm->challenge = strdup(header);
+ if(!ntlm->challenge)
+ return CURLE_OUT_OF_MEMORY;
+
+ *state = NTLMSTATE_TYPE2; /* We got a type-2 message */
+ }
+ else {
+ if(*state == NTLMSTATE_LAST) {
+ infof(data, "NTLM auth restarted");
+ Curl_http_auth_cleanup_ntlm_wb(conn);
+ }
+ else if(*state == NTLMSTATE_TYPE3) {
+ infof(data, "NTLM handshake rejected");
+ Curl_http_auth_cleanup_ntlm_wb(conn);
+ *state = NTLMSTATE_NONE;
+ return CURLE_REMOTE_ACCESS_DENIED;
+ }
+ else if(*state >= NTLMSTATE_TYPE1) {
+ infof(data, "NTLM handshake failure (internal error)");
+ return CURLE_REMOTE_ACCESS_DENIED;
+ }
+
+ *state = NTLMSTATE_TYPE1; /* We should send away a type-1 */
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * This is for creating ntlm header output by delegating challenge/response
+ * to Samba's winbind daemon helper ntlm_auth.
+ */
+CURLcode Curl_output_ntlm_wb(struct Curl_easy *data, struct connectdata *conn,
+ bool proxy)
+{
+ /* point to the address of the pointer that holds the string to send to the
+ server, which is for a plain host or for an HTTP proxy */
+ char **allocuserpwd;
+ /* point to the name and password for this */
+ const char *userp;
+ struct ntlmdata *ntlm;
+ curlntlm *state;
+ struct auth *authp;
+
+ CURLcode res = CURLE_OK;
+
+ DEBUGASSERT(conn);
+ DEBUGASSERT(data);
+
+ if(proxy) {
+#ifndef CURL_DISABLE_PROXY
+ allocuserpwd = &data->state.aptr.proxyuserpwd;
+ userp = conn->http_proxy.user;
+ ntlm = &conn->proxyntlm;
+ state = &conn->proxy_ntlm_state;
+ authp = &data->state.authproxy;
+#else
+ return CURLE_NOT_BUILT_IN;
+#endif
+ }
+ else {
+ allocuserpwd = &data->state.aptr.userpwd;
+ userp = conn->user;
+ ntlm = &conn->ntlm;
+ state = &conn->http_ntlm_state;
+ authp = &data->state.authhost;
+ }
+ authp->done = FALSE;
+
+ /* not set means empty */
+ if(!userp)
+ userp = "";
+
+ switch(*state) {
+ case NTLMSTATE_TYPE1:
+ default:
+ /* Use Samba's 'winbind' daemon to support NTLM authentication,
+ * by delegating the NTLM challenge/response protocol to a helper
+ * in ntlm_auth.
+ * https://web.archive.org/web/20190925164737
+ * /devel.squid-cache.org/ntlm/squid_helper_protocol.html
+ * https://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html
+ * https://www.samba.org/samba/docs/man/manpages-3/ntlm_auth.1.html
+ * Preprocessor symbol 'NTLM_WB_ENABLED' is defined when this
+ * feature is enabled and 'NTLM_WB_FILE' symbol holds absolute
+ * filename of ntlm_auth helper.
+ * If NTLM authentication using winbind fails, go back to original
+ * request handling process.
+ */
+ /* Create communication with ntlm_auth */
+ res = ntlm_wb_init(data, ntlm, userp);
+ if(res)
+ return res;
+ res = ntlm_wb_response(data, ntlm, "YR\n", *state);
+ if(res)
+ return res;
+
+ free(*allocuserpwd);
+ *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n",
+ proxy ? "Proxy-" : "",
+ ntlm->response);
+ DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd));
+ Curl_safefree(ntlm->response);
+ if(!*allocuserpwd)
+ return CURLE_OUT_OF_MEMORY;
+ break;
+
+ case NTLMSTATE_TYPE2: {
+ char *input = aprintf("TT %s\n", ntlm->challenge);
+ if(!input)
+ return CURLE_OUT_OF_MEMORY;
+ res = ntlm_wb_response(data, ntlm, input, *state);
+ free(input);
+ if(res)
+ return res;
+
+ free(*allocuserpwd);
+ *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n",
+ proxy ? "Proxy-" : "",
+ ntlm->response);
+ DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd));
+ *state = NTLMSTATE_TYPE3; /* we sent a type-3 */
+ authp->done = TRUE;
+ Curl_http_auth_cleanup_ntlm_wb(conn);
+ if(!*allocuserpwd)
+ return CURLE_OUT_OF_MEMORY;
+ break;
+ }
+ case NTLMSTATE_TYPE3:
+ /* connection is already authenticated,
+ * don't send a header in future requests */
+ *state = NTLMSTATE_LAST;
+ /* FALLTHROUGH */
+ case NTLMSTATE_LAST:
+ Curl_safefree(*allocuserpwd);
+ authp->done = TRUE;
+ break;
+ }
+
+ return CURLE_OK;
+}
+
+void Curl_http_auth_cleanup_ntlm_wb(struct connectdata *conn)
+{
+ ntlm_wb_cleanup(&conn->ntlm);
+ ntlm_wb_cleanup(&conn->proxyntlm);
+}
+
+#endif /* !CURL_DISABLE_HTTP && USE_NTLM && NTLM_WB_ENABLED */
diff --git a/libs/libcurl/src/curl_ntlm_wb.h b/libs/libcurl/src/curl_ntlm_wb.h index 1f04db8c3d..c8a769ddb4 100644 --- a/libs/libcurl/src/curl_ntlm_wb.h +++ b/libs/libcurl/src/curl_ntlm_wb.h @@ -1,45 +1,45 @@ -#ifndef HEADER_CURL_NTLM_WB_H -#define HEADER_CURL_NTLM_WB_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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(USE_NTLM) && \ - defined(NTLM_WB_ENABLED) - -/* this is for ntlm header input */ -CURLcode Curl_input_ntlm_wb(struct Curl_easy *data, - struct connectdata *conn, bool proxy, - const char *header); - -/* this is for creating ntlm header output */ -CURLcode Curl_output_ntlm_wb(struct Curl_easy *data, struct connectdata *conn, - bool proxy); - -void Curl_http_auth_cleanup_ntlm_wb(struct connectdata *conn); - -#endif /* !CURL_DISABLE_HTTP && USE_NTLM && NTLM_WB_ENABLED */ - -#endif /* HEADER_CURL_NTLM_WB_H */ +#ifndef HEADER_CURL_NTLM_WB_H
+#define HEADER_CURL_NTLM_WB_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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(USE_NTLM) && \
+ defined(NTLM_WB_ENABLED)
+
+/* this is for ntlm header input */
+CURLcode Curl_input_ntlm_wb(struct Curl_easy *data,
+ struct connectdata *conn, bool proxy,
+ const char *header);
+
+/* this is for creating ntlm header output */
+CURLcode Curl_output_ntlm_wb(struct Curl_easy *data, struct connectdata *conn,
+ bool proxy);
+
+void Curl_http_auth_cleanup_ntlm_wb(struct connectdata *conn);
+
+#endif /* !CURL_DISABLE_HTTP && USE_NTLM && NTLM_WB_ENABLED */
+
+#endif /* HEADER_CURL_NTLM_WB_H */
diff --git a/libs/libcurl/src/curl_path.c b/libs/libcurl/src/curl_path.c index b55e83047d..aefb623a56 100644 --- a/libs/libcurl/src/curl_path.c +++ b/libs/libcurl/src/curl_path.c @@ -1,201 +1,201 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 AND ISC - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if defined(USE_SSH) - -#include <curl/curl.h> -#include "curl_memory.h" -#include "curl_path.h" -#include "escape.h" -#include "memdebug.h" - -/* figure out the path to work with in this particular request */ -CURLcode Curl_getworkingpath(struct Curl_easy *data, - char *homedir, /* when SFTP is used */ - char **path) /* returns the allocated - real path to work with */ -{ - char *real_path = NULL; - char *working_path; - size_t working_path_len; - CURLcode result = - Curl_urldecode(data->state.up.path, 0, &working_path, - &working_path_len, REJECT_ZERO); - if(result) - return result; - - /* Check for /~/, indicating relative to the user's home directory */ - if(data->conn->handler->protocol & CURLPROTO_SCP) { - real_path = malloc(working_path_len + 1); - if(!real_path) { - free(working_path); - return CURLE_OUT_OF_MEMORY; - } - if((working_path_len > 3) && (!memcmp(working_path, "/~/", 3))) - /* It is referenced to the home directory, so strip the leading '/~/' */ - memcpy(real_path, working_path + 3, working_path_len - 2); - else - memcpy(real_path, working_path, 1 + working_path_len); - } - else if(data->conn->handler->protocol & CURLPROTO_SFTP) { - if((working_path_len > 1) && (working_path[1] == '~')) { - size_t homelen = strlen(homedir); - real_path = malloc(homelen + working_path_len + 1); - if(!real_path) { - free(working_path); - return CURLE_OUT_OF_MEMORY; - } - /* It is referenced to the home directory, so strip the - leading '/' */ - memcpy(real_path, homedir, homelen); - real_path[homelen] = '/'; - real_path[homelen + 1] = '\0'; - if(working_path_len > 3) { - memcpy(real_path + homelen + 1, working_path + 3, - 1 + working_path_len -3); - } - } - else { - real_path = malloc(working_path_len + 1); - if(!real_path) { - free(working_path); - return CURLE_OUT_OF_MEMORY; - } - memcpy(real_path, working_path, 1 + working_path_len); - } - } - - free(working_path); - - /* store the pointer for the caller to receive */ - *path = real_path; - - return CURLE_OK; -} - -/* The get_pathname() function is being borrowed from OpenSSH sftp.c - version 4.6p1. */ -/* - * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -CURLcode Curl_get_pathname(const char **cpp, char **path, char *homedir) -{ - const char *cp = *cpp, *end; - char quot; - unsigned int i, j; - size_t fullPathLength, pathLength; - bool relativePath = false; - static const char WHITESPACE[] = " \t\r\n"; - - DEBUGASSERT(homedir); - if(!*cp || !homedir) { - *cpp = NULL; - *path = NULL; - return CURLE_QUOTE_ERROR; - } - /* Ignore leading whitespace */ - cp += strspn(cp, WHITESPACE); - /* Allocate enough space for home directory and filename + separator */ - fullPathLength = strlen(cp) + strlen(homedir) + 2; - *path = malloc(fullPathLength); - if(!*path) - return CURLE_OUT_OF_MEMORY; - - /* Check for quoted filenames */ - if(*cp == '\"' || *cp == '\'') { - quot = *cp++; - - /* Search for terminating quote, unescape some chars */ - for(i = j = 0; i <= strlen(cp); i++) { - if(cp[i] == quot) { /* Found quote */ - i++; - (*path)[j] = '\0'; - break; - } - if(cp[i] == '\0') { /* End of string */ - /*error("Unterminated quote");*/ - goto fail; - } - if(cp[i] == '\\') { /* Escaped characters */ - i++; - if(cp[i] != '\'' && cp[i] != '\"' && - cp[i] != '\\') { - /*error("Bad escaped character '\\%c'", - cp[i]);*/ - goto fail; - } - } - (*path)[j++] = cp[i]; - } - - if(j == 0) { - /*error("Empty quotes");*/ - goto fail; - } - *cpp = cp + i + strspn(cp + i, WHITESPACE); - } - else { - /* Read to end of filename - either to whitespace or terminator */ - end = strpbrk(cp, WHITESPACE); - if(!end) - end = strchr(cp, '\0'); - /* return pointer to second parameter if it exists */ - *cpp = end + strspn(end, WHITESPACE); - pathLength = 0; - relativePath = (cp[0] == '/' && cp[1] == '~' && cp[2] == '/'); - /* Handling for relative path - prepend home directory */ - if(relativePath) { - strcpy(*path, homedir); - pathLength = strlen(homedir); - (*path)[pathLength++] = '/'; - (*path)[pathLength] = '\0'; - cp += 3; - } - /* Copy path name up until first "whitespace" */ - memcpy(&(*path)[pathLength], cp, (int)(end - cp)); - pathLength += (int)(end - cp); - (*path)[pathLength] = '\0'; - } - return CURLE_OK; - - fail: - Curl_safefree(*path); - return CURLE_QUOTE_ERROR; -} - -#endif /* if SSH is used */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 AND ISC
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if defined(USE_SSH)
+
+#include <curl/curl.h>
+#include "curl_memory.h"
+#include "curl_path.h"
+#include "escape.h"
+#include "memdebug.h"
+
+/* figure out the path to work with in this particular request */
+CURLcode Curl_getworkingpath(struct Curl_easy *data,
+ char *homedir, /* when SFTP is used */
+ char **path) /* returns the allocated
+ real path to work with */
+{
+ char *real_path = NULL;
+ char *working_path;
+ size_t working_path_len;
+ CURLcode result =
+ Curl_urldecode(data->state.up.path, 0, &working_path,
+ &working_path_len, REJECT_ZERO);
+ if(result)
+ return result;
+
+ /* Check for /~/, indicating relative to the user's home directory */
+ if(data->conn->handler->protocol & CURLPROTO_SCP) {
+ real_path = malloc(working_path_len + 1);
+ if(!real_path) {
+ free(working_path);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ if((working_path_len > 3) && (!memcmp(working_path, "/~/", 3)))
+ /* It is referenced to the home directory, so strip the leading '/~/' */
+ memcpy(real_path, working_path + 3, working_path_len - 2);
+ else
+ memcpy(real_path, working_path, 1 + working_path_len);
+ }
+ else if(data->conn->handler->protocol & CURLPROTO_SFTP) {
+ if((working_path_len > 1) && (working_path[1] == '~')) {
+ size_t homelen = strlen(homedir);
+ real_path = malloc(homelen + working_path_len + 1);
+ if(!real_path) {
+ free(working_path);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ /* It is referenced to the home directory, so strip the
+ leading '/' */
+ memcpy(real_path, homedir, homelen);
+ /* Only add a trailing '/' if homedir does not end with one */
+ if(homelen == 0 || real_path[homelen - 1] != '/') {
+ real_path[homelen] = '/';
+ homelen++;
+ real_path[homelen] = '\0';
+ }
+ if(working_path_len > 3) {
+ memcpy(real_path + homelen, working_path + 3,
+ 1 + working_path_len -3);
+ }
+ }
+ else {
+ real_path = malloc(working_path_len + 1);
+ if(!real_path) {
+ free(working_path);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ memcpy(real_path, working_path, 1 + working_path_len);
+ }
+ }
+
+ free(working_path);
+
+ /* store the pointer for the caller to receive */
+ *path = real_path;
+
+ return CURLE_OK;
+}
+
+/* The get_pathname() function is being borrowed from OpenSSH sftp.c
+ version 4.6p1. */
+/*
+ * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+CURLcode Curl_get_pathname(const char **cpp, char **path, char *homedir)
+{
+ const char *cp = *cpp, *end;
+ char quot;
+ unsigned int i, j;
+ size_t fullPathLength, pathLength;
+ bool relativePath = false;
+ static const char WHITESPACE[] = " \t\r\n";
+
+ DEBUGASSERT(homedir);
+ if(!*cp || !homedir) {
+ *cpp = NULL;
+ *path = NULL;
+ return CURLE_QUOTE_ERROR;
+ }
+ /* Ignore leading whitespace */
+ cp += strspn(cp, WHITESPACE);
+ /* Allocate enough space for home directory and filename + separator */
+ fullPathLength = strlen(cp) + strlen(homedir) + 2;
+ *path = malloc(fullPathLength);
+ if(!*path)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Check for quoted filenames */
+ if(*cp == '\"' || *cp == '\'') {
+ quot = *cp++;
+
+ /* Search for terminating quote, unescape some chars */
+ for(i = j = 0; i <= strlen(cp); i++) {
+ if(cp[i] == quot) { /* Found quote */
+ i++;
+ (*path)[j] = '\0';
+ break;
+ }
+ if(cp[i] == '\0') { /* End of string */
+ goto fail;
+ }
+ if(cp[i] == '\\') { /* Escaped characters */
+ i++;
+ if(cp[i] != '\'' && cp[i] != '\"' &&
+ cp[i] != '\\') {
+ goto fail;
+ }
+ }
+ (*path)[j++] = cp[i];
+ }
+
+ if(j == 0) {
+ goto fail;
+ }
+ *cpp = cp + i + strspn(cp + i, WHITESPACE);
+ }
+ else {
+ /* Read to end of filename - either to whitespace or terminator */
+ end = strpbrk(cp, WHITESPACE);
+ if(!end)
+ end = strchr(cp, '\0');
+ /* return pointer to second parameter if it exists */
+ *cpp = end + strspn(end, WHITESPACE);
+ pathLength = 0;
+ relativePath = (cp[0] == '/' && cp[1] == '~' && cp[2] == '/');
+ /* Handling for relative path - prepend home directory */
+ if(relativePath) {
+ strcpy(*path, homedir);
+ pathLength = strlen(homedir);
+ (*path)[pathLength++] = '/';
+ (*path)[pathLength] = '\0';
+ cp += 3;
+ }
+ /* Copy path name up until first "whitespace" */
+ memcpy(&(*path)[pathLength], cp, (int)(end - cp));
+ pathLength += (int)(end - cp);
+ (*path)[pathLength] = '\0';
+ }
+ return CURLE_OK;
+
+ fail:
+ Curl_safefree(*path);
+ return CURLE_QUOTE_ERROR;
+}
+
+#endif /* if SSH is used */
diff --git a/libs/libcurl/src/curl_path.h b/libs/libcurl/src/curl_path.h index 98e56eaad3..b633d532d2 100644 --- a/libs/libcurl/src/curl_path.h +++ b/libs/libcurl/src/curl_path.h @@ -1,49 +1,49 @@ -#ifndef HEADER_CURL_PATH_H -#define HEADER_CURL_PATH_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -#ifdef WIN32 -# undef PATH_MAX -# define PATH_MAX MAX_PATH -# ifndef R_OK -# define R_OK 4 -# endif -#endif - -#ifndef PATH_MAX -#define PATH_MAX 1024 /* just an extra precaution since there are systems that - have their definition hidden well */ -#endif - -CURLcode Curl_getworkingpath(struct Curl_easy *data, - char *homedir, - char **path); - -CURLcode Curl_get_pathname(const char **cpp, char **path, char *homedir); -#endif /* HEADER_CURL_PATH_H */ +#ifndef HEADER_CURL_PATH_H
+#define HEADER_CURL_PATH_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+#ifdef WIN32
+# undef PATH_MAX
+# define PATH_MAX MAX_PATH
+# ifndef R_OK
+# define R_OK 4
+# endif
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024 /* just an extra precaution since there are systems that
+ have their definition hidden well */
+#endif
+
+CURLcode Curl_getworkingpath(struct Curl_easy *data,
+ char *homedir,
+ char **path);
+
+CURLcode Curl_get_pathname(const char **cpp, char **path, char *homedir);
+#endif /* HEADER_CURL_PATH_H */
diff --git a/libs/libcurl/src/curl_printf.h b/libs/libcurl/src/curl_printf.h index 3823828cd9..84e7037872 100644 --- a/libs/libcurl/src/curl_printf.h +++ b/libs/libcurl/src/curl_printf.h @@ -1,50 +1,50 @@ -#ifndef HEADER_CURL_PRINTF_H -#define HEADER_CURL_PRINTF_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 header should be included by ALL code in libcurl that uses any - * *rintf() functions. - */ - -#include <curl/mprintf.h> - -# undef printf -# undef fprintf -# undef msnprintf -# undef vprintf -# undef vfprintf -# undef vsnprintf -# undef aprintf -# undef vaprintf -# define printf curl_mprintf -# define fprintf curl_mfprintf -# define msnprintf curl_msnprintf -# define vprintf curl_mvprintf -# define vfprintf curl_mvfprintf -# define mvsnprintf curl_mvsnprintf -# define aprintf curl_maprintf -# define vaprintf curl_mvaprintf -#endif /* HEADER_CURL_PRINTF_H */ +#ifndef HEADER_CURL_PRINTF_H
+#define HEADER_CURL_PRINTF_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 header should be included by ALL code in libcurl that uses any
+ * *rintf() functions.
+ */
+
+#include <curl/mprintf.h>
+
+# undef printf
+# undef fprintf
+# undef msnprintf
+# undef vprintf
+# undef vfprintf
+# undef vsnprintf
+# undef aprintf
+# undef vaprintf
+# define printf curl_mprintf
+# define fprintf curl_mfprintf
+# define msnprintf curl_msnprintf
+# define vprintf curl_mvprintf
+# define vfprintf curl_mvfprintf
+# define mvsnprintf curl_mvsnprintf
+# define aprintf curl_maprintf
+# define vaprintf curl_mvaprintf
+#endif /* HEADER_CURL_PRINTF_H */
diff --git a/libs/libcurl/src/curl_range.c b/libs/libcurl/src/curl_range.c index dd92d05b39..8eaf54b326 100644 --- a/libs/libcurl/src/curl_range.c +++ b/libs/libcurl/src/curl_range.c @@ -1,96 +1,96 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "strtoofft.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) -{ - curl_off_t from, to; - char *ptr; - char *ptr2; - - if(data->state.use_range && data->state.range) { - CURLofft from_t; - CURLofft to_t; - from_t = curlx_strtoofft(data->state.range, &ptr, 0, &from); - if(from_t == CURL_OFFT_FLOW) - return CURLE_RANGE_ERROR; - while(*ptr && (ISBLANK(*ptr) || (*ptr == '-'))) - ptr++; - to_t = curlx_strtoofft(ptr, &ptr2, 0, &to); - if(to_t == CURL_OFFT_FLOW) - return CURLE_RANGE_ERROR; - if((to_t == CURL_OFFT_INVAL) && !from_t) { - /* X - */ - data->state.resume_from = from; - DEBUGF(infof(data, "RANGE %" CURL_FORMAT_CURL_OFF_T " to end of file", - from)); - } - else if((from_t == CURL_OFFT_INVAL) && !to_t) { - /* -Y */ - data->req.maxdownload = to; - data->state.resume_from = -to; - DEBUGF(infof(data, "RANGE the last %" CURL_FORMAT_CURL_OFF_T " bytes", - 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 %" CURL_FORMAT_CURL_OFF_T - " getting %" CURL_FORMAT_CURL_OFF_T " bytes", - from, data->req.maxdownload)); - } - DEBUGF(infof(data, "range-download from %" CURL_FORMAT_CURL_OFF_T - " to %" CURL_FORMAT_CURL_OFF_T ", totally %" - CURL_FORMAT_CURL_OFF_T " bytes", - from, to, data->req.maxdownload)); - } - else - data->req.maxdownload = -1; - return CURLE_OK; -} - -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "strtoofft.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)
+{
+ curl_off_t from, to;
+ char *ptr;
+ char *ptr2;
+
+ if(data->state.use_range && data->state.range) {
+ CURLofft from_t;
+ CURLofft to_t;
+ from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from);
+ if(from_t == CURL_OFFT_FLOW)
+ return CURLE_RANGE_ERROR;
+ while(*ptr && (ISBLANK(*ptr) || (*ptr == '-')))
+ ptr++;
+ to_t = curlx_strtoofft(ptr, &ptr2, 10, &to);
+ if(to_t == CURL_OFFT_FLOW)
+ return CURLE_RANGE_ERROR;
+ if((to_t == CURL_OFFT_INVAL) && !from_t) {
+ /* X - */
+ data->state.resume_from = from;
+ DEBUGF(infof(data, "RANGE %" CURL_FORMAT_CURL_OFF_T " to end of file",
+ from));
+ }
+ else if((from_t == CURL_OFFT_INVAL) && !to_t) {
+ /* -Y */
+ data->req.maxdownload = to;
+ data->state.resume_from = -to;
+ DEBUGF(infof(data, "RANGE the last %" CURL_FORMAT_CURL_OFF_T " bytes",
+ 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 %" CURL_FORMAT_CURL_OFF_T
+ " getting %" CURL_FORMAT_CURL_OFF_T " bytes",
+ from, data->req.maxdownload));
+ }
+ DEBUGF(infof(data, "range-download from %" CURL_FORMAT_CURL_OFF_T
+ " to %" CURL_FORMAT_CURL_OFF_T ", totally %"
+ CURL_FORMAT_CURL_OFF_T " bytes",
+ from, to, data->req.maxdownload));
+ }
+ else
+ data->req.maxdownload = -1;
+ return CURLE_OK;
+}
+
+#endif
diff --git a/libs/libcurl/src/curl_range.h b/libs/libcurl/src/curl_range.h index 33570abc25..34d02ad269 100644 --- a/libs/libcurl/src/curl_range.h +++ b/libs/libcurl/src/curl_range.h @@ -1,31 +1,31 @@ -#ifndef HEADER_CURL_RANGE_H -#define HEADER_CURL_RANGE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 */ +#ifndef HEADER_CURL_RANGE_H
+#define HEADER_CURL_RANGE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 */
diff --git a/libs/libcurl/src/curl_rtmp.c b/libs/libcurl/src/curl_rtmp.c index b0c371041e..f7ae9a6860 100644 --- a/libs/libcurl/src/curl_rtmp.c +++ b/libs/libcurl/src/curl_rtmp.c @@ -1,338 +1,338 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2012, 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 "nonblock.h" /* for curlx_nonblock */ -#include "progress.h" /* for Curl_pgrsSetUploadSize */ -#include "transfer.h" -#include "warnless.h" -#include <curl/curl.h> -#include <librtmp/rtmp.h> -#include "curl_memory.h" -/* The last #include file should be: */ -#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 */ - -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_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - rtmp_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - 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_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - rtmp_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - 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_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - rtmp_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - 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_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - rtmp_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - 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_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - rtmp_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - 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_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - rtmp_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_RTMPS, /* defport */ - CURLPROTO_RTMPTS, /* protocol */ - CURLPROTO_RTMPT, /* family */ - PROTOPT_NONE /* flags*/ -}; - -static CURLcode rtmp_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - RTMP *r = RTMP_Alloc(); - if(!r) - 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; - } - conn->proto.rtmp = r; - return CURLE_OK; -} - -static CURLcode rtmp_connect(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - RTMP *r = conn->proto.rtmp; - SET_RCVTIMEO(tv, 10); - - r->m_sb.sb_socket = (int)conn->sock[FIRSTSOCKET]; - - /* We have to know if it's a write before we send the - * connect request packet - */ - if(data->set.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 = conn->proto.rtmp; - - if(!RTMP_ConnectStream(r, 0)) - return CURLE_FAILED_INIT; - - if(data->set.upload) { - Curl_pgrsSetUploadSize(data, data->state.infilesize); - Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); - } - else - Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); - *done = TRUE; - return CURLE_OK; -} - -static CURLcode rtmp_done(struct Curl_easy *data, CURLcode status, - bool premature) -{ - (void)data; /* unused */ - (void)status; /* unused */ - (void)premature; /* unused */ - - return CURLE_OK; -} - -static CURLcode rtmp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection) -{ - RTMP *r = conn->proto.rtmp; - (void)data; - (void)dead_connection; - if(r) { - conn->proto.rtmp = NULL; - RTMP_Close(r); - RTMP_Free(r); - } - return CURLE_OK; -} - -static ssize_t rtmp_recv(struct Curl_easy *data, int sockindex, char *buf, - size_t len, CURLcode *err) -{ - struct connectdata *conn = data->conn; - RTMP *r = conn->proto.rtmp; - ssize_t nread; - - (void)sockindex; /* unused */ - - nread = RTMP_Read(r, buf, curlx_uztosi(len)); - if(nread < 0) { - if(r->m_read.status == RTMP_READ_COMPLETE || - r->m_read.status == RTMP_READ_EOF) { - data->req.size = data->req.bytecount; - nread = 0; - } - else - *err = CURLE_RECV_ERROR; - } - return nread; -} - -static ssize_t rtmp_send(struct Curl_easy *data, int sockindex, - const void *buf, size_t len, CURLcode *err) -{ - struct connectdata *conn = data->conn; - RTMP *r = conn->proto.rtmp; - ssize_t num; - - (void)sockindex; /* unused */ - - num = RTMP_Write(r, (char *)buf, curlx_uztosi(len)); - if(num < 0) - *err = CURLE_SEND_ERROR; - - return num; -} -#endif /* USE_LIBRTMP */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012, 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 "nonblock.h" /* for curlx_nonblock */
+#include "progress.h" /* for Curl_pgrsSetUploadSize */
+#include "transfer.h"
+#include "warnless.h"
+#include <curl/curl.h>
+#include <librtmp/rtmp.h>
+#include "curl_memory.h"
+/* The last #include file should be: */
+#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 */
+
+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_getsock */
+ ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ rtmp_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ 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_getsock */
+ ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ rtmp_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ 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_getsock */
+ ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ rtmp_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ 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_getsock */
+ ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ rtmp_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ 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_getsock */
+ ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ rtmp_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ 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_getsock */
+ ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ rtmp_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_RTMPS, /* defport */
+ CURLPROTO_RTMPTS, /* protocol */
+ CURLPROTO_RTMPT, /* family */
+ PROTOPT_NONE /* flags */
+};
+
+static CURLcode rtmp_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ RTMP *r = RTMP_Alloc();
+ if(!r)
+ 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;
+ }
+ conn->proto.rtmp = r;
+ return CURLE_OK;
+}
+
+static CURLcode rtmp_connect(struct Curl_easy *data, bool *done)
+{
+ struct connectdata *conn = data->conn;
+ RTMP *r = conn->proto.rtmp;
+ SET_RCVTIMEO(tv, 10);
+
+ r->m_sb.sb_socket = (int)conn->sock[FIRSTSOCKET];
+
+ /* We have to know if it's a write before we send the
+ * connect request packet
+ */
+ if(data->set.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 = conn->proto.rtmp;
+
+ if(!RTMP_ConnectStream(r, 0))
+ return CURLE_FAILED_INIT;
+
+ if(data->set.upload) {
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+ Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
+ }
+ else
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
+ *done = TRUE;
+ return CURLE_OK;
+}
+
+static CURLcode rtmp_done(struct Curl_easy *data, CURLcode status,
+ bool premature)
+{
+ (void)data; /* unused */
+ (void)status; /* unused */
+ (void)premature; /* unused */
+
+ return CURLE_OK;
+}
+
+static CURLcode rtmp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection)
+{
+ RTMP *r = conn->proto.rtmp;
+ (void)data;
+ (void)dead_connection;
+ if(r) {
+ conn->proto.rtmp = NULL;
+ RTMP_Close(r);
+ RTMP_Free(r);
+ }
+ return CURLE_OK;
+}
+
+static ssize_t rtmp_recv(struct Curl_easy *data, int sockindex, char *buf,
+ size_t len, CURLcode *err)
+{
+ struct connectdata *conn = data->conn;
+ RTMP *r = conn->proto.rtmp;
+ ssize_t nread;
+
+ (void)sockindex; /* unused */
+
+ nread = RTMP_Read(r, buf, curlx_uztosi(len));
+ if(nread < 0) {
+ if(r->m_read.status == RTMP_READ_COMPLETE ||
+ r->m_read.status == RTMP_READ_EOF) {
+ data->req.size = data->req.bytecount;
+ nread = 0;
+ }
+ else
+ *err = CURLE_RECV_ERROR;
+ }
+ return nread;
+}
+
+static ssize_t rtmp_send(struct Curl_easy *data, int sockindex,
+ const void *buf, size_t len, CURLcode *err)
+{
+ struct connectdata *conn = data->conn;
+ RTMP *r = conn->proto.rtmp;
+ ssize_t num;
+
+ (void)sockindex; /* unused */
+
+ num = RTMP_Write(r, (char *)buf, curlx_uztosi(len));
+ if(num < 0)
+ *err = CURLE_SEND_ERROR;
+
+ return num;
+}
+#endif /* USE_LIBRTMP */
diff --git a/libs/libcurl/src/curl_rtmp.h b/libs/libcurl/src/curl_rtmp.h index f856085dc3..edab7ac36e 100644 --- a/libs/libcurl/src/curl_rtmp.h +++ b/libs/libcurl/src/curl_rtmp.h @@ -1,35 +1,35 @@ -#ifndef HEADER_CURL_RTMP_H -#define HEADER_CURL_RTMP_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2010 - 2022, 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; -#endif - -#endif /* HEADER_CURL_RTMP_H */ +#ifndef HEADER_CURL_RTMP_H
+#define HEADER_CURL_RTMP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2010 - 2022, 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;
+#endif
+
+#endif /* HEADER_CURL_RTMP_H */
diff --git a/libs/libcurl/src/curl_sasl.c b/libs/libcurl/src/curl_sasl.c index 9684ee476e..f62b65a4d0 100644 --- a/libs/libcurl/src/curl_sasl.c +++ b/libs/libcurl/src/curl_sasl.c @@ -1,748 +1,751 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2012 - 2022, 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) - -#include <curl/curl.h> -#include "urldata.h" - -#include "curl_base64.h" -#include "curl_md5.h" -#include "vauth/vauth.h" -#include "vtls/vtls.h" -#include "curl_hmac.h" -#include "curl_sasl.h" -#include "warnless.h" -#include "strtok.h" -#include "sendf.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#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_cleanup() - * - * This is used to cleanup any libraries or curl modules used by the sasl - * functions. - * - * Parameters: - * - * conn [in] - The connection data. - * authused [in] - The authentication mechanism used. - */ -void Curl_sasl_cleanup(struct connectdata *conn, unsigned short authused) -{ - (void)conn; - (void)authused; - -#if defined(USE_KERBEROS5) - /* Cleanup the gssapi structure */ - if(authused == SASL_MECH_GSSAPI) { - Curl_auth_cleanup_gssapi(&conn->krb5); - } -#endif - -#if defined(USE_GSASL) - /* Cleanup the GSASL structure */ - if(authused & (SASL_MECH_SCRAM_SHA_1 | SASL_MECH_SCRAM_SHA_256)) { - Curl_auth_gsasl_cleanup(&conn->gsasl); - } -#endif - -#if defined(USE_NTLM) - /* Cleanup the NTLM structure */ - if(authused == SASL_MECH_NTLM) { - Curl_auth_cleanup_ntlm(&conn->ntlm); - } -#endif -} - -/* - * Curl_sasl_decode_mech() - * - * 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) { - sasl->resetprefs = FALSE; - sasl->prefmech = SASL_AUTH_NONE; - if(auth & CURLAUTH_BASIC) - sasl->prefmech |= SASL_MECH_PLAIN | SASL_MECH_LOGIN; - if(auth & CURLAUTH_DIGEST) - sasl->prefmech |= SASL_MECH_DIGEST_MD5; - if(auth & CURLAUTH_NTLM) - sasl->prefmech |= SASL_MECH_NTLM; - if(auth & CURLAUTH_BEARER) - sasl->prefmech |= SASL_MECH_OAUTHBEARER | SASL_MECH_XOAUTH2; - if(auth & CURLAUTH_GSSAPI) - sasl->prefmech |= SASL_MECH_GSSAPI; - } -} - -/* - * state() - * - * This is the ONLY way to change SASL state! - */ -static void 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; -} - -/* 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 = Curl_base64_decode(serverdata, &msg, &msglen); - if(!result) - Curl_bufref_set(out, msg, msglen, curl_free); - } - } - return result; -} - -/* 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 = Curl_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 user name and/or password */ - if(sasl->authmechs & sasl->prefmech & SASL_MECH_EXTERNAL) - 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) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - unsigned short enabledmechs; - const char *mech = NULL; - struct bufref resp; - saslstate state1 = SASL_STOP; - saslstate state2 = SASL_FINAL; - const char * const hostname = SSL_HOST_NAME(); - const long int port = SSL_HOST_PORT(); -#if defined(USE_KERBEROS5) || defined(USE_NTLM) - const char *service = data->set.str[STRING_SERVICE_NAME] ? - data->set.str[STRING_SERVICE_NAME] : - sasl->params->service; -#endif - const char *oauth_bearer = data->set.str[STRING_BEARER]; - struct bufref nullmsg; - - Curl_bufref_init(&nullmsg); - Curl_bufref_init(&resp); - sasl->force_ir = force_ir; /* Latch for future use */ - sasl->authused = 0; /* No mechanism used yet */ - enabledmechs = sasl->authmechs & sasl->prefmech; - *progress = SASL_IDLE; - - /* Calculate the supported authentication mechanism, by decreasing order of - security, as well as the initial response where appropriate */ - if((enabledmechs & SASL_MECH_EXTERNAL) && !conn->passwd[0]) { - mech = SASL_MECH_STRING_EXTERNAL; - state1 = SASL_EXTERNAL; - sasl->authused = SASL_MECH_EXTERNAL; - - if(force_ir || data->set.sasl_ir) - result = Curl_auth_create_external_message(conn->user, &resp); - } - else if(data->state.aptr.user) { -#if defined(USE_KERBEROS5) - if((enabledmechs & SASL_MECH_GSSAPI) && Curl_auth_is_gssapi_supported() && - Curl_auth_user_contains_domain(conn->user)) { - sasl->mutual_auth = FALSE; - mech = SASL_MECH_STRING_GSSAPI; - state1 = SASL_GSSAPI; - state2 = SASL_GSSAPI_TOKEN; - sasl->authused = SASL_MECH_GSSAPI; - - if(force_ir || data->set.sasl_ir) - result = Curl_auth_create_gssapi_user_message(data, conn->user, - conn->passwd, - service, - conn->host.name, - sasl->mutual_auth, - NULL, &conn->krb5, - &resp); - } - else -#endif -#ifdef USE_GSASL - if((enabledmechs & SASL_MECH_SCRAM_SHA_256) && - Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_256, - &conn->gsasl)) { - mech = SASL_MECH_STRING_SCRAM_SHA_256; - sasl->authused = SASL_MECH_SCRAM_SHA_256; - state1 = SASL_GSASL; - state2 = SASL_GSASL; - - result = Curl_auth_gsasl_start(data, conn->user, - conn->passwd, &conn->gsasl); - if(result == CURLE_OK && (force_ir || data->set.sasl_ir)) - result = Curl_auth_gsasl_token(data, &nullmsg, &conn->gsasl, &resp); - } - else if((enabledmechs & SASL_MECH_SCRAM_SHA_1) && - Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_1, - &conn->gsasl)) { - mech = SASL_MECH_STRING_SCRAM_SHA_1; - sasl->authused = SASL_MECH_SCRAM_SHA_1; - state1 = SASL_GSASL; - state2 = SASL_GSASL; - - result = Curl_auth_gsasl_start(data, conn->user, - conn->passwd, &conn->gsasl); - if(result == CURLE_OK && (force_ir || data->set.sasl_ir)) - result = Curl_auth_gsasl_token(data, &nullmsg, &conn->gsasl, &resp); - } - else -#endif -#ifndef CURL_DISABLE_CRYPTO_AUTH - if((enabledmechs & SASL_MECH_DIGEST_MD5) && - Curl_auth_is_digest_supported()) { - mech = SASL_MECH_STRING_DIGEST_MD5; - state1 = SASL_DIGESTMD5; - sasl->authused = SASL_MECH_DIGEST_MD5; - } - else if(enabledmechs & SASL_MECH_CRAM_MD5) { - mech = SASL_MECH_STRING_CRAM_MD5; - state1 = SASL_CRAMMD5; - sasl->authused = SASL_MECH_CRAM_MD5; - } - else -#endif -#ifdef USE_NTLM - if((enabledmechs & SASL_MECH_NTLM) && Curl_auth_is_ntlm_supported()) { - mech = SASL_MECH_STRING_NTLM; - state1 = SASL_NTLM; - state2 = SASL_NTLM_TYPE2MSG; - sasl->authused = SASL_MECH_NTLM; - - if(force_ir || data->set.sasl_ir) - result = Curl_auth_create_ntlm_type1_message(data, - conn->user, conn->passwd, - service, - hostname, - &conn->ntlm, &resp); - } - else -#endif - if((enabledmechs & SASL_MECH_OAUTHBEARER) && oauth_bearer) { - mech = SASL_MECH_STRING_OAUTHBEARER; - state1 = SASL_OAUTH2; - state2 = SASL_OAUTH2_RESP; - sasl->authused = SASL_MECH_OAUTHBEARER; - - if(force_ir || data->set.sasl_ir) - result = Curl_auth_create_oauth_bearer_message(conn->user, - hostname, - port, - oauth_bearer, - &resp); - } - else if((enabledmechs & SASL_MECH_XOAUTH2) && oauth_bearer) { - mech = SASL_MECH_STRING_XOAUTH2; - state1 = SASL_OAUTH2; - sasl->authused = SASL_MECH_XOAUTH2; - - if(force_ir || data->set.sasl_ir) - result = Curl_auth_create_xoauth_bearer_message(conn->user, - oauth_bearer, - &resp); - } - else if(enabledmechs & SASL_MECH_PLAIN) { - mech = SASL_MECH_STRING_PLAIN; - state1 = SASL_PLAIN; - sasl->authused = SASL_MECH_PLAIN; - - if(force_ir || data->set.sasl_ir) - result = Curl_auth_create_plain_message(conn->sasl_authzid, - conn->user, conn->passwd, - &resp); - } - else if(enabledmechs & SASL_MECH_LOGIN) { - mech = SASL_MECH_STRING_LOGIN; - state1 = SASL_LOGIN; - state2 = SASL_LOGIN_PASSWD; - sasl->authused = SASL_MECH_LOGIN; - - if(force_ir || data->set.sasl_ir) - result = Curl_auth_create_login_message(conn->user, &resp); - } - } - - if(!result && mech) { - sasl->curmech = mech; - if(Curl_bufref_ptr(&resp)) - result = build_message(sasl, &resp); - - if(sasl->params->maxirlen && - strlen(mech) + Curl_bufref_len(&resp) > sasl->params->maxirlen) - Curl_bufref_free(&resp); - - if(!result) - result = sasl->params->sendauth(data, mech, &resp); - - if(!result) { - *progress = SASL_INPROGRESS; - state(sasl, data, Curl_bufref_ptr(&resp) ? state2 : state1); - } - } - - Curl_bufref_free(&resp); - return 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 * const hostname = SSL_HOST_NAME(); - const long int port = SSL_HOST_PORT(); -#if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5) || \ - defined(USE_NTLM) - const char *service = data->set.str[STRING_SERVICE_NAME] ? - data->set.str[STRING_SERVICE_NAME] : - sasl->params->service; -#endif - const char *oauth_bearer = data->set.str[STRING_BEARER]; - struct bufref serverdata; - - 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; - state(sasl, data, SASL_STOP); - return result; - } - - if(sasl->state != SASL_CANCEL && sasl->state != SASL_OAUTH2_RESP && - code != sasl->params->contcode) { - *progress = SASL_DONE; - 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: - result = Curl_auth_create_login_message(conn->user, &resp); - newstate = SASL_LOGIN_PASSWD; - break; - case SASL_LOGIN_PASSWD: - result = Curl_auth_create_login_message(conn->passwd, &resp); - break; - case SASL_EXTERNAL: - result = Curl_auth_create_external_message(conn->user, &resp); - break; -#ifndef CURL_DISABLE_CRYPTO_AUTH -#ifdef USE_GSASL - case SASL_GSASL: - result = get_server_message(sasl, data, &serverdata); - if(!result) - result = Curl_auth_gsasl_token(data, &serverdata, &conn->gsasl, &resp); - if(!result && Curl_bufref_len(&resp) > 0) - newstate = SASL_GSASL; - break; -#endif - 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 */ - result = Curl_auth_create_ntlm_type1_message(data, - conn->user, conn->passwd, - service, hostname, - &conn->ntlm, &resp); - newstate = SASL_NTLM_TYPE2MSG; - break; - case SASL_NTLM_TYPE2MSG: - /* Decode the type-2 message */ - result = get_server_message(sasl, data, &serverdata); - if(!result) - result = Curl_auth_decode_ntlm_type2_message(data, &serverdata, - &conn->ntlm); - if(!result) - result = Curl_auth_create_ntlm_type3_message(data, conn->user, - conn->passwd, &conn->ntlm, - &resp); - break; -#endif - -#if defined(USE_KERBEROS5) - case SASL_GSSAPI: - result = Curl_auth_create_gssapi_user_message(data, conn->user, - conn->passwd, - service, - conn->host.name, - sasl->mutual_auth, NULL, - &conn->krb5, - &resp); - newstate = SASL_GSSAPI_TOKEN; - break; - case SASL_GSSAPI_TOKEN: - result = get_server_message(sasl, data, &serverdata); - if(!result) { - if(sasl->mutual_auth) { - /* Decode the user token challenge and create the optional response - message */ - result = Curl_auth_create_gssapi_user_message(data, NULL, NULL, - NULL, NULL, - sasl->mutual_auth, - &serverdata, - &conn->krb5, - &resp); - 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, - &conn->krb5, - &resp); - } - break; - case SASL_GSSAPI_NO_DATA: - /* Decode the security challenge and create the response message */ - result = get_server_message(sasl, data, &serverdata); - if(!result) - result = Curl_auth_create_gssapi_security_message(data, - conn->sasl_authzid, - &serverdata, - &conn->krb5, - &resp); - 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; - 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; - state(sasl, data, SASL_STOP); - return CURLE_LOGIN_DENIED; - } - - case SASL_CANCEL: - /* Remove the offending mechanism from the supported list */ - sasl->authmechs ^= sasl->authused; - - /* 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); - - state(sasl, data, newstate); - - return result; -} -#endif /* protocols are enabled that use SASL */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012 - 2022, 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)
+
+#include <curl/curl.h>
+#include "urldata.h"
+
+#include "curl_base64.h"
+#include "curl_md5.h"
+#include "vauth/vauth.h"
+#include "cfilters.h"
+#include "vtls/vtls.h"
+#include "curl_hmac.h"
+#include "curl_sasl.h"
+#include "warnless.h"
+#include "strtok.h"
+#include "sendf.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#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_cleanup()
+ *
+ * This is used to cleanup any libraries or curl modules used by the sasl
+ * functions.
+ *
+ * Parameters:
+ *
+ * conn [in] - The connection data.
+ * authused [in] - The authentication mechanism used.
+ */
+void Curl_sasl_cleanup(struct connectdata *conn, unsigned short authused)
+{
+ (void)conn;
+ (void)authused;
+
+#if defined(USE_KERBEROS5)
+ /* Cleanup the gssapi structure */
+ if(authused == SASL_MECH_GSSAPI) {
+ Curl_auth_cleanup_gssapi(&conn->krb5);
+ }
+#endif
+
+#if defined(USE_GSASL)
+ /* Cleanup the GSASL structure */
+ if(authused & (SASL_MECH_SCRAM_SHA_1 | SASL_MECH_SCRAM_SHA_256)) {
+ Curl_auth_gsasl_cleanup(&conn->gsasl);
+ }
+#endif
+
+#if defined(USE_NTLM)
+ /* Cleanup the NTLM structure */
+ if(authused == SASL_MECH_NTLM) {
+ Curl_auth_cleanup_ntlm(&conn->ntlm);
+ }
+#endif
+}
+
+/*
+ * Curl_sasl_decode_mech()
+ *
+ * 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) {
+ sasl->resetprefs = FALSE;
+ sasl->prefmech = SASL_AUTH_NONE;
+ if(auth & CURLAUTH_BASIC)
+ sasl->prefmech |= SASL_MECH_PLAIN | SASL_MECH_LOGIN;
+ if(auth & CURLAUTH_DIGEST)
+ sasl->prefmech |= SASL_MECH_DIGEST_MD5;
+ if(auth & CURLAUTH_NTLM)
+ sasl->prefmech |= SASL_MECH_NTLM;
+ if(auth & CURLAUTH_BEARER)
+ sasl->prefmech |= SASL_MECH_OAUTHBEARER | SASL_MECH_XOAUTH2;
+ if(auth & CURLAUTH_GSSAPI)
+ sasl->prefmech |= SASL_MECH_GSSAPI;
+ }
+}
+
+/*
+ * state()
+ *
+ * This is the ONLY way to change SASL state!
+ */
+static void 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;
+}
+
+/* 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 = Curl_base64_decode(serverdata, &msg, &msglen);
+ if(!result)
+ Curl_bufref_set(out, msg, msglen, curl_free);
+ }
+ }
+ return result;
+}
+
+/* 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 = Curl_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 user name and/or password */
+ if(sasl->authmechs & sasl->prefmech & SASL_MECH_EXTERNAL)
+ 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)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ unsigned short enabledmechs;
+ const char *mech = NULL;
+ struct bufref resp;
+ saslstate state1 = SASL_STOP;
+ saslstate state2 = SASL_FINAL;
+ const char *hostname, *disp_hostname;
+ int port;
+#if defined(USE_KERBEROS5) || defined(USE_NTLM)
+ const char *service = data->set.str[STRING_SERVICE_NAME] ?
+ data->set.str[STRING_SERVICE_NAME] :
+ sasl->params->service;
+#endif
+ const char *oauth_bearer = data->set.str[STRING_BEARER];
+ struct bufref nullmsg;
+
+ Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port);
+ Curl_bufref_init(&nullmsg);
+ Curl_bufref_init(&resp);
+ sasl->force_ir = force_ir; /* Latch for future use */
+ sasl->authused = 0; /* No mechanism used yet */
+ enabledmechs = sasl->authmechs & sasl->prefmech;
+ *progress = SASL_IDLE;
+
+ /* Calculate the supported authentication mechanism, by decreasing order of
+ security, as well as the initial response where appropriate */
+ if((enabledmechs & SASL_MECH_EXTERNAL) && !conn->passwd[0]) {
+ mech = SASL_MECH_STRING_EXTERNAL;
+ state1 = SASL_EXTERNAL;
+ sasl->authused = SASL_MECH_EXTERNAL;
+
+ if(force_ir || data->set.sasl_ir)
+ result = Curl_auth_create_external_message(conn->user, &resp);
+ }
+ else if(data->state.aptr.user) {
+#if defined(USE_KERBEROS5)
+ if((enabledmechs & SASL_MECH_GSSAPI) && Curl_auth_is_gssapi_supported() &&
+ Curl_auth_user_contains_domain(conn->user)) {
+ sasl->mutual_auth = FALSE;
+ mech = SASL_MECH_STRING_GSSAPI;
+ state1 = SASL_GSSAPI;
+ state2 = SASL_GSSAPI_TOKEN;
+ sasl->authused = SASL_MECH_GSSAPI;
+
+ if(force_ir || data->set.sasl_ir)
+ result = Curl_auth_create_gssapi_user_message(data, conn->user,
+ conn->passwd,
+ service,
+ conn->host.name,
+ sasl->mutual_auth,
+ NULL, &conn->krb5,
+ &resp);
+ }
+ else
+#endif
+#ifdef USE_GSASL
+ if((enabledmechs & SASL_MECH_SCRAM_SHA_256) &&
+ Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_256,
+ &conn->gsasl)) {
+ mech = SASL_MECH_STRING_SCRAM_SHA_256;
+ sasl->authused = SASL_MECH_SCRAM_SHA_256;
+ state1 = SASL_GSASL;
+ state2 = SASL_GSASL;
+
+ result = Curl_auth_gsasl_start(data, conn->user,
+ conn->passwd, &conn->gsasl);
+ if(result == CURLE_OK && (force_ir || data->set.sasl_ir))
+ result = Curl_auth_gsasl_token(data, &nullmsg, &conn->gsasl, &resp);
+ }
+ else if((enabledmechs & SASL_MECH_SCRAM_SHA_1) &&
+ Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_1,
+ &conn->gsasl)) {
+ mech = SASL_MECH_STRING_SCRAM_SHA_1;
+ sasl->authused = SASL_MECH_SCRAM_SHA_1;
+ state1 = SASL_GSASL;
+ state2 = SASL_GSASL;
+
+ result = Curl_auth_gsasl_start(data, conn->user,
+ conn->passwd, &conn->gsasl);
+ if(result == CURLE_OK && (force_ir || data->set.sasl_ir))
+ result = Curl_auth_gsasl_token(data, &nullmsg, &conn->gsasl, &resp);
+ }
+ else
+#endif
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+ if((enabledmechs & SASL_MECH_DIGEST_MD5) &&
+ Curl_auth_is_digest_supported()) {
+ mech = SASL_MECH_STRING_DIGEST_MD5;
+ state1 = SASL_DIGESTMD5;
+ sasl->authused = SASL_MECH_DIGEST_MD5;
+ }
+ else if(enabledmechs & SASL_MECH_CRAM_MD5) {
+ mech = SASL_MECH_STRING_CRAM_MD5;
+ state1 = SASL_CRAMMD5;
+ sasl->authused = SASL_MECH_CRAM_MD5;
+ }
+ else
+#endif
+#ifdef USE_NTLM
+ if((enabledmechs & SASL_MECH_NTLM) && Curl_auth_is_ntlm_supported()) {
+ mech = SASL_MECH_STRING_NTLM;
+ state1 = SASL_NTLM;
+ state2 = SASL_NTLM_TYPE2MSG;
+ sasl->authused = SASL_MECH_NTLM;
+
+ if(force_ir || data->set.sasl_ir)
+ result = Curl_auth_create_ntlm_type1_message(data,
+ conn->user, conn->passwd,
+ service,
+ hostname,
+ &conn->ntlm, &resp);
+ }
+ else
+#endif
+ if((enabledmechs & SASL_MECH_OAUTHBEARER) && oauth_bearer) {
+ mech = SASL_MECH_STRING_OAUTHBEARER;
+ state1 = SASL_OAUTH2;
+ state2 = SASL_OAUTH2_RESP;
+ sasl->authused = SASL_MECH_OAUTHBEARER;
+
+ if(force_ir || data->set.sasl_ir)
+ result = Curl_auth_create_oauth_bearer_message(conn->user,
+ hostname,
+ port,
+ oauth_bearer,
+ &resp);
+ }
+ else if((enabledmechs & SASL_MECH_XOAUTH2) && oauth_bearer) {
+ mech = SASL_MECH_STRING_XOAUTH2;
+ state1 = SASL_OAUTH2;
+ sasl->authused = SASL_MECH_XOAUTH2;
+
+ if(force_ir || data->set.sasl_ir)
+ result = Curl_auth_create_xoauth_bearer_message(conn->user,
+ oauth_bearer,
+ &resp);
+ }
+ else if(enabledmechs & SASL_MECH_PLAIN) {
+ mech = SASL_MECH_STRING_PLAIN;
+ state1 = SASL_PLAIN;
+ sasl->authused = SASL_MECH_PLAIN;
+
+ if(force_ir || data->set.sasl_ir)
+ result = Curl_auth_create_plain_message(conn->sasl_authzid,
+ conn->user, conn->passwd,
+ &resp);
+ }
+ else if(enabledmechs & SASL_MECH_LOGIN) {
+ mech = SASL_MECH_STRING_LOGIN;
+ state1 = SASL_LOGIN;
+ state2 = SASL_LOGIN_PASSWD;
+ sasl->authused = SASL_MECH_LOGIN;
+
+ if(force_ir || data->set.sasl_ir)
+ result = Curl_auth_create_login_message(conn->user, &resp);
+ }
+ }
+
+ if(!result && mech) {
+ sasl->curmech = mech;
+ if(Curl_bufref_ptr(&resp))
+ result = build_message(sasl, &resp);
+
+ if(sasl->params->maxirlen &&
+ strlen(mech) + Curl_bufref_len(&resp) > sasl->params->maxirlen)
+ Curl_bufref_free(&resp);
+
+ if(!result)
+ result = sasl->params->sendauth(data, mech, &resp);
+
+ if(!result) {
+ *progress = SASL_INPROGRESS;
+ state(sasl, data, Curl_bufref_ptr(&resp) ? state2 : state1);
+ }
+ }
+
+ Curl_bufref_free(&resp);
+ return 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, *disp_hostname;
+ int port;
+#if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5) || \
+ defined(USE_NTLM)
+ const char *service = data->set.str[STRING_SERVICE_NAME] ?
+ data->set.str[STRING_SERVICE_NAME] :
+ sasl->params->service;
+#endif
+ const char *oauth_bearer = data->set.str[STRING_BEARER];
+ struct bufref serverdata;
+
+ Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_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;
+ state(sasl, data, SASL_STOP);
+ return result;
+ }
+
+ if(sasl->state != SASL_CANCEL && sasl->state != SASL_OAUTH2_RESP &&
+ code != sasl->params->contcode) {
+ *progress = SASL_DONE;
+ 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:
+ result = Curl_auth_create_login_message(conn->user, &resp);
+ newstate = SASL_LOGIN_PASSWD;
+ break;
+ case SASL_LOGIN_PASSWD:
+ result = Curl_auth_create_login_message(conn->passwd, &resp);
+ break;
+ case SASL_EXTERNAL:
+ result = Curl_auth_create_external_message(conn->user, &resp);
+ break;
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+#ifdef USE_GSASL
+ case SASL_GSASL:
+ result = get_server_message(sasl, data, &serverdata);
+ if(!result)
+ result = Curl_auth_gsasl_token(data, &serverdata, &conn->gsasl, &resp);
+ if(!result && Curl_bufref_len(&resp) > 0)
+ newstate = SASL_GSASL;
+ break;
+#endif
+ 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 */
+ result = Curl_auth_create_ntlm_type1_message(data,
+ conn->user, conn->passwd,
+ service, hostname,
+ &conn->ntlm, &resp);
+ newstate = SASL_NTLM_TYPE2MSG;
+ break;
+ case SASL_NTLM_TYPE2MSG:
+ /* Decode the type-2 message */
+ result = get_server_message(sasl, data, &serverdata);
+ if(!result)
+ result = Curl_auth_decode_ntlm_type2_message(data, &serverdata,
+ &conn->ntlm);
+ if(!result)
+ result = Curl_auth_create_ntlm_type3_message(data, conn->user,
+ conn->passwd, &conn->ntlm,
+ &resp);
+ break;
+#endif
+
+#if defined(USE_KERBEROS5)
+ case SASL_GSSAPI:
+ result = Curl_auth_create_gssapi_user_message(data, conn->user,
+ conn->passwd,
+ service,
+ conn->host.name,
+ sasl->mutual_auth, NULL,
+ &conn->krb5,
+ &resp);
+ newstate = SASL_GSSAPI_TOKEN;
+ break;
+ case SASL_GSSAPI_TOKEN:
+ result = get_server_message(sasl, data, &serverdata);
+ if(!result) {
+ if(sasl->mutual_auth) {
+ /* Decode the user token challenge and create the optional response
+ message */
+ result = Curl_auth_create_gssapi_user_message(data, NULL, NULL,
+ NULL, NULL,
+ sasl->mutual_auth,
+ &serverdata,
+ &conn->krb5,
+ &resp);
+ 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,
+ &conn->krb5,
+ &resp);
+ }
+ break;
+ case SASL_GSSAPI_NO_DATA:
+ /* Decode the security challenge and create the response message */
+ result = get_server_message(sasl, data, &serverdata);
+ if(!result)
+ result = Curl_auth_create_gssapi_security_message(data,
+ conn->sasl_authzid,
+ &serverdata,
+ &conn->krb5,
+ &resp);
+ 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;
+ 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;
+ state(sasl, data, SASL_STOP);
+ return CURLE_LOGIN_DENIED;
+ }
+
+ case SASL_CANCEL:
+ /* Remove the offending mechanism from the supported list */
+ sasl->authmechs ^= sasl->authused;
+
+ /* 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);
+
+ state(sasl, data, newstate);
+
+ return result;
+}
+#endif /* protocols are enabled that use SASL */
diff --git a/libs/libcurl/src/curl_sasl.h b/libs/libcurl/src/curl_sasl.h index c709d56a67..021edd7468 100644 --- a/libs/libcurl/src/curl_sasl.h +++ b/libs/libcurl/src/curl_sasl.h @@ -1,165 +1,165 @@ -#ifndef HEADER_CURL_SASL_H -#define HEADER_CURL_SASL_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2012 - 2022, 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 */ - bool resetprefs; /* For URL auth option parsing. */ - bool mutual_auth; /* Mutual authentication enabled (GSSAPI only) */ - bool 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)) - -/* This is used to cleanup any libraries or curl modules used by the sasl - functions */ -void Curl_sasl_cleanup(struct connectdata *conn, unsigned short authused); - -/* Convert a mechanism name to a token */ -unsigned short Curl_sasl_decode_mech(const char *ptr, - size_t maxlen, size_t *len); - -/* 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); - -#endif /* HEADER_CURL_SASL_H */ +#ifndef HEADER_CURL_SASL_H
+#define HEADER_CURL_SASL_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012 - 2022, 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 */
+ bool resetprefs; /* For URL auth option parsing. */
+ bool mutual_auth; /* Mutual authentication enabled (GSSAPI only) */
+ bool 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))
+
+/* This is used to cleanup any libraries or curl modules used by the sasl
+ functions */
+void Curl_sasl_cleanup(struct connectdata *conn, unsigned short authused);
+
+/* Convert a mechanism name to a token */
+unsigned short Curl_sasl_decode_mech(const char *ptr,
+ size_t maxlen, size_t *len);
+
+/* 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);
+
+#endif /* HEADER_CURL_SASL_H */
diff --git a/libs/libcurl/src/curl_setup.h b/libs/libcurl/src/curl_setup.h index ad3e837372..e395573e6b 100644 --- a/libs/libcurl/src/curl_setup.h +++ b/libs/libcurl/src/curl_setup.h @@ -1,844 +1,856 @@ -#ifndef HEADER_CURL_SETUP_H -#define HEADER_CURL_SETUP_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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(BUILDING_LIBCURL) && !defined(CURL_NO_OLDIES) -#define CURL_NO_OLDIES -#endif - -/* define mingw version macros, eg __MINGW{32,64}_{MINOR,MAJOR}_VERSION */ -#ifdef __MINGW32__ -#include <_mingw.h> -#endif - -/* - * Disable Visual Studio warnings: - * 4127 "conditional expression is constant" - */ -#ifdef _MSC_VER -#pragma warning(disable:4127) -#endif - -/* - * Define WIN32 when build target is Win32 API - */ - -#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) -#define WIN32 -#endif - -#ifdef WIN32 -/* - * Don't include unneeded stuff in Windows headers to avoid compiler - * warnings and macro clashes. - * Make sure to define this macro before including any Windows headers. - */ -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif -# ifndef NOGDI -# define NOGDI -# endif -#endif - -/* - * Include configuration script results or hand-crafted - * configuration file for platforms which lack config tool. - */ - -#ifdef HAVE_CONFIG_H - -#include "curl_config.h" - -#else /* HAVE_CONFIG_H */ - -#ifdef _WIN32_WCE -# include "config-win32ce.h" -#else -# ifdef WIN32 -# include "config-win32.h" -# endif -#endif - -#if defined(macintosh) && defined(__MRC__) -# include "config-mac.h" -#endif - -#ifdef __riscos__ -# include "config-riscos.h" -#endif - -#ifdef __AMIGA__ -# include "config-amigaos.h" -#endif - -#ifdef __OS400__ -# include "config-os400.h" -#endif - -#ifdef __PLAN9__ -# include "config-plan9.h" -#endif - -#endif /* HAVE_CONFIG_H */ - -/* ================================================================ */ -/* Definition of preprocessor macros/symbols which modify compiler */ -/* behavior or generated code characteristics must be done here, */ -/* as appropriate, before any system header file is included. It is */ -/* also possible to have them defined in the config file included */ -/* before this point. As a result of all this we frown inclusion of */ -/* system header files in our config files, avoid this at any cost. */ -/* ================================================================ */ - -/* - * AIX 4.3 and newer needs _THREAD_SAFE defined to build - * proper reentrant code. Others may also need it. - */ - -#ifdef NEED_THREAD_SAFE -# ifndef _THREAD_SAFE -# define _THREAD_SAFE -# endif -#endif - -/* - * Tru64 needs _REENTRANT set for a few function prototypes and - * things to appear in the system header files. Unixware needs it - * to build proper reentrant code. Others may also need it. - */ - -#ifdef NEED_REENTRANT -# ifndef _REENTRANT -# define _REENTRANT -# endif -#endif - -/* Solaris needs this to get a POSIX-conformant getpwuid_r */ -#if defined(sun) || defined(__sun) -# ifndef _POSIX_PTHREAD_SEMANTICS -# define _POSIX_PTHREAD_SEMANTICS 1 -# endif -#endif - -/* ================================================================ */ -/* If you need to include a system header file for your platform, */ -/* please, do it beyond the point further indicated in this file. */ -/* ================================================================ */ - -/* - * Disable other protocols when http is the only one desired. - */ - -#ifdef HTTP_ONLY -# ifndef CURL_DISABLE_DICT -# define CURL_DISABLE_DICT -# endif -# ifndef CURL_DISABLE_FILE -# define CURL_DISABLE_FILE -# endif -# ifndef CURL_DISABLE_FTP -# define CURL_DISABLE_FTP -# endif -# ifndef CURL_DISABLE_GOPHER -# define CURL_DISABLE_GOPHER -# endif -# ifndef CURL_DISABLE_IMAP -# define CURL_DISABLE_IMAP -# endif -# ifndef CURL_DISABLE_LDAP -# define CURL_DISABLE_LDAP -# endif -# ifndef CURL_DISABLE_LDAPS -# define CURL_DISABLE_LDAPS -# endif -# ifndef CURL_DISABLE_MQTT -# define CURL_DISABLE_MQTT -# endif -# ifndef CURL_DISABLE_POP3 -# define CURL_DISABLE_POP3 -# endif -# ifndef CURL_DISABLE_RTSP -# define CURL_DISABLE_RTSP -# endif -# ifndef CURL_DISABLE_SMB -# define CURL_DISABLE_SMB -# endif -# ifndef CURL_DISABLE_SMTP -# define CURL_DISABLE_SMTP -# endif -# ifndef CURL_DISABLE_TELNET -# define CURL_DISABLE_TELNET -# endif -# ifndef CURL_DISABLE_TFTP -# define CURL_DISABLE_TFTP -# endif -#endif - -/* - * When http is disabled rtsp is not supported. - */ - -#if defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_RTSP) -# define CURL_DISABLE_RTSP -#endif - -/* ================================================================ */ -/* No system header file shall be included in this file before this */ -/* point. */ -/* ================================================================ */ - -/* - * OS/400 setup file includes some system headers. - */ - -#ifdef __OS400__ -# include "setup-os400.h" -#endif - -/* - * VMS setup file includes some system headers. - */ - -#ifdef __VMS -# include "setup-vms.h" -#endif - -/* - * Windows setup file includes some system headers. - */ - -#ifdef HAVE_WINDOWS_H -# include "setup-win32.h" -#endif - -#include <curl/system.h> - -/* - * Use getaddrinfo to resolve the IPv4 address literal. If the current network - * interface doesn't support IPv4, but supports IPv6, NAT64, and DNS64, - * performing this task will result in a synthesized IPv6 address. - */ -#if defined(__APPLE__) && !defined(USE_ARES) -#include <TargetConditionals.h> -#define USE_RESOLVE_ON_IPS 1 -# if defined(TARGET_OS_OSX) && TARGET_OS_OSX -# define CURL_OSX_CALL_COPYPROXIES 1 -# endif -#endif - -#ifdef USE_LWIPSOCK -# include <lwip/init.h> -# include <lwip/sockets.h> -# include <lwip/netdb.h> -#endif - -#ifdef HAVE_EXTRA_STRICMP_H -# include <extra/stricmp.h> -#endif - -#ifdef HAVE_EXTRA_STRDUP_H -# include <extra/strdup.h> -#endif - -#ifdef __AMIGA__ -# ifdef __amigaos4__ -# define __USE_INLINE__ - /* use our own resolver which uses runtime feature detection */ -# define CURLRES_AMIGA - /* getaddrinfo() currently crashes bsdsocket.library, so disable */ -# undef HAVE_GETADDRINFO -# if !(defined(__NEWLIB__) || \ - (defined(__CLIB2__) && defined(__THREAD_SAFE))) - /* disable threaded resolver with clib2 - requires newlib or clib-ts */ -# undef USE_THREADS_POSIX -# endif -# endif -# include <exec/types.h> -# include <exec/execbase.h> -# include <proto/exec.h> -# include <proto/dos.h> -# include <unistd.h> -# if defined(HAVE_PROTO_BSDSOCKET_H) && \ - (!defined(__amigaos4__) || defined(USE_AMISSL)) - /* use bsdsocket.library directly, instead of libc networking functions */ -# include <proto/bsdsocket.h> -# ifdef __amigaos4__ - int Curl_amiga_select(int nfds, fd_set *readfds, fd_set *writefds, - fd_set *errorfds, struct timeval *timeout); -# define select(a,b,c,d,e) Curl_amiga_select(a,b,c,d,e) -# else -# define select(a,b,c,d,e) WaitSelect(a,b,c,d,e,0) -# endif - /* must not use libc's fcntl() on bsdsocket.library sockfds! */ -# undef HAVE_FCNTL -# undef HAVE_FCNTL_O_NONBLOCK -# else - /* use libc networking and hence close() and fnctl() */ -# undef HAVE_CLOSESOCKET_CAMEL -# undef HAVE_IOCTLSOCKET_CAMEL -# endif -/* - * In clib2 arpa/inet.h warns that some prototypes may clash - * with bsdsocket.library. This avoids the definition of those. - */ -# define __NO_NET_API -#endif - -#include <stdio.h> -#ifdef HAVE_ASSERT_H -#include <assert.h> -#endif - -#ifdef __TANDEM /* for ns*-tandem-nsk systems */ -# if ! defined __LP64 -# include <floss.h> /* FLOSS is only used for 32-bit builds. */ -# endif -#endif - -#ifndef STDC_HEADERS /* no standard C headers! */ -#include <curl/stdcheaders.h> -#endif - -#ifdef __POCC__ -# include <sys/types.h> -# include <unistd.h> -# define sys_nerr EILSEQ -#endif - -/* - * Salford-C kludge section (mostly borrowed from wxWidgets). - */ -#ifdef __SALFORDC__ - #pragma suppress 353 /* Possible nested comments */ - #pragma suppress 593 /* Define not used */ - #pragma suppress 61 /* enum has no name */ - #pragma suppress 106 /* unnamed, unused parameter */ - #include <clib.h> -#endif - -/* - * Large file (>2Gb) support using WIN32 functions. - */ - -#ifdef USE_WIN32_LARGE_FILES -# include <io.h> -# include <sys/types.h> -# include <sys/stat.h> -# undef lseek -# define lseek(fdes,offset,whence) _lseeki64(fdes, offset, whence) -# undef fstat -# define fstat(fdes,stp) _fstati64(fdes, stp) -# undef stat -# define stat(fname,stp) curlx_win32_stat(fname, stp) -# define struct_stat struct _stati64 -# define LSEEK_ERROR (__int64)-1 -# define open curlx_win32_open -# define fopen(fname,mode) curlx_win32_fopen(fname, mode) -# define access(fname,mode) curlx_win32_access(fname, mode) - int curlx_win32_open(const char *filename, int oflag, ...); - int curlx_win32_stat(const char *path, struct_stat *buffer); - FILE *curlx_win32_fopen(const char *filename, const char *mode); - int curlx_win32_access(const char *path, int mode); -#endif - -/* - * Small file (<2Gb) support using WIN32 functions. - */ - -#ifdef USE_WIN32_SMALL_FILES -# include <io.h> -# include <sys/types.h> -# include <sys/stat.h> -# ifndef _WIN32_WCE -# undef lseek -# define lseek(fdes,offset,whence) _lseek(fdes, (long)offset, whence) -# define fstat(fdes,stp) _fstat(fdes, stp) -# define stat(fname,stp) curlx_win32_stat(fname, stp) -# define struct_stat struct _stat -# define open curlx_win32_open -# define fopen(fname,mode) curlx_win32_fopen(fname, mode) -# define access(fname,mode) curlx_win32_access(fname, mode) - int curlx_win32_stat(const char *path, struct_stat *buffer); - int curlx_win32_open(const char *filename, int oflag, ...); - FILE *curlx_win32_fopen(const char *filename, const char *mode); - int curlx_win32_access(const char *path, int mode); -# endif -# define LSEEK_ERROR (long)-1 -#endif - -#ifndef struct_stat -# define struct_stat struct stat -#endif - -#ifndef LSEEK_ERROR -# define LSEEK_ERROR (off_t)-1 -#endif - -#ifndef SIZEOF_TIME_T -/* assume default size of time_t to be 32 bit */ -#define SIZEOF_TIME_T 4 -#endif - -/* - * Default sizeof(off_t) in case it hasn't been defined in config file. - */ - -#ifndef SIZEOF_OFF_T -# if defined(__VMS) && !defined(__VAX) -# if defined(_LARGEFILE) -# define SIZEOF_OFF_T 8 -# endif -# elif defined(__OS400__) && defined(__ILEC400__) -# if defined(_LARGE_FILES) -# define SIZEOF_OFF_T 8 -# endif -# elif defined(__MVS__) && defined(__IBMC__) -# if defined(_LP64) || defined(_LARGE_FILES) -# define SIZEOF_OFF_T 8 -# endif -# elif defined(__370__) && defined(__IBMC__) -# if defined(_LP64) || defined(_LARGE_FILES) -# define SIZEOF_OFF_T 8 -# endif -# endif -# ifndef SIZEOF_OFF_T -# define SIZEOF_OFF_T 4 -# endif -#endif - -#if (SIZEOF_CURL_OFF_T == 4) -# define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFF) -#else - /* assume SIZEOF_CURL_OFF_T == 8 */ -# define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFFFFFFFFFF) -#endif -#define CURL_OFF_T_MIN (-CURL_OFF_T_MAX - CURL_OFF_T_C(1)) - -#if (SIZEOF_TIME_T == 4) -# ifdef HAVE_TIME_T_UNSIGNED -# define TIME_T_MAX UINT_MAX -# define TIME_T_MIN 0 -# else -# define TIME_T_MAX INT_MAX -# define TIME_T_MIN INT_MIN -# endif -#else -# ifdef HAVE_TIME_T_UNSIGNED -# define TIME_T_MAX 0xFFFFFFFFFFFFFFFF -# define TIME_T_MIN 0 -# else -# define TIME_T_MAX 0x7FFFFFFFFFFFFFFF -# define TIME_T_MIN (-TIME_T_MAX - 1) -# endif -#endif - -#ifndef SIZE_T_MAX -/* some limits.h headers have this defined, some don't */ -#if defined(SIZEOF_SIZE_T) && (SIZEOF_SIZE_T > 4) -#define SIZE_T_MAX 18446744073709551615U -#else -#define SIZE_T_MAX 4294967295U -#endif -#endif - -#ifndef SSIZE_T_MAX -/* some limits.h headers have this defined, some don't */ -#if defined(SIZEOF_SIZE_T) && (SIZEOF_SIZE_T > 4) -#define SSIZE_T_MAX 9223372036854775807 -#else -#define SSIZE_T_MAX 2147483647 -#endif -#endif - -/* - * Arg 2 type for gethostname in case it hasn't been defined in config file. - */ - -#ifndef GETHOSTNAME_TYPE_ARG2 -# ifdef USE_WINSOCK -# define GETHOSTNAME_TYPE_ARG2 int -# else -# define GETHOSTNAME_TYPE_ARG2 size_t -# endif -#endif - -/* Below we define some functions. They should - - 4. set the SIGALRM signal timeout - 5. set dir/file naming defines - */ - -#ifdef WIN32 - -# define DIR_CHAR "\\" - -#else /* WIN32 */ - -# ifdef MSDOS /* Watt-32 */ - -# include <sys/ioctl.h> -# define select(n,r,w,x,t) select_s(n,r,w,x,t) -# define ioctl(x,y,z) ioctlsocket(x,y,(char *)(z)) -# include <tcp.h> -# ifdef word -# undef word -# endif -# ifdef byte -# undef byte -# endif - -# endif /* MSDOS */ - -# ifdef __minix - /* Minix 3 versions up to at least 3.1.3 are missing these prototypes */ - extern char *strtok_r(char *s, const char *delim, char **last); - extern struct tm *gmtime_r(const time_t * const timep, struct tm *tmp); -# endif - -# define DIR_CHAR "/" - -# ifndef fileno /* sunos 4 have this as a macro! */ - int fileno(FILE *stream); -# endif - -#endif /* WIN32 */ - -/* - * msvc 6.0 requires PSDK in order to have INET6_ADDRSTRLEN - * defined in ws2tcpip.h as well as to provide IPv6 support. - * Does not apply if lwIP is used. - */ - -#if defined(_MSC_VER) && !defined(__POCC__) && !defined(USE_LWIPSOCK) -# if !defined(HAVE_WS2TCPIP_H) || \ - ((_MSC_VER < 1300) && !defined(INET6_ADDRSTRLEN)) -# undef HAVE_GETADDRINFO_THREADSAFE -# undef HAVE_FREEADDRINFO -# undef HAVE_GETADDRINFO -# undef ENABLE_IPV6 -# endif -#endif - -/* ---------------------------------------------------------------- */ -/* resolver specialty compile-time defines */ -/* CURLRES_* defines to use in the host*.c sources */ -/* ---------------------------------------------------------------- */ - -/* - * lcc-win32 doesn't have _beginthreadex(), lacks threads support. - */ - -#if defined(__LCC__) && defined(WIN32) -# undef USE_THREADS_POSIX -# undef USE_THREADS_WIN32 -#endif - -/* - * MSVC threads support requires a multi-threaded runtime library. - * _beginthreadex() is not available in single-threaded ones. - */ - -#if defined(_MSC_VER) && !defined(__POCC__) && !defined(_MT) -# undef USE_THREADS_POSIX -# undef USE_THREADS_WIN32 -#endif - -/* - * Mutually exclusive CURLRES_* definitions. - */ - -#if defined(ENABLE_IPV6) && defined(HAVE_GETADDRINFO) -# define CURLRES_IPV6 -#else -# define CURLRES_IPV4 -#endif - -#ifdef USE_ARES -# define CURLRES_ASYNCH -# define CURLRES_ARES -/* now undef the stock libc functions just to avoid them being used */ -# undef HAVE_GETADDRINFO -# undef HAVE_FREEADDRINFO -#elif defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) -# define CURLRES_ASYNCH -# define CURLRES_THREADED -#else -# define CURLRES_SYNCH -#endif - -/* ---------------------------------------------------------------- */ - -/* - * msvc 6.0 does not have struct sockaddr_storage and - * does not define IPPROTO_ESP in winsock2.h. But both - * are available if PSDK is properly installed. - */ - -#if defined(_MSC_VER) && !defined(__POCC__) -# if !defined(HAVE_WINSOCK2_H) || ((_MSC_VER < 1300) && !defined(IPPROTO_ESP)) -# undef HAVE_STRUCT_SOCKADDR_STORAGE -# endif -#endif - -/* - * Intentionally fail to build when using msvc 6.0 without PSDK installed. - * The brave of heart can circumvent this, defining ALLOW_MSVC6_WITHOUT_PSDK - * in lib/config-win32.h although absolutely discouraged and unsupported. - */ - -#if defined(_MSC_VER) && !defined(__POCC__) -# if !defined(HAVE_WINDOWS_H) || ((_MSC_VER < 1300) && !defined(_FILETIME_)) -# if !defined(ALLOW_MSVC6_WITHOUT_PSDK) -# error MSVC 6.0 requires "February 2003 Platform SDK" a.k.a. \ - "Windows Server 2003 PSDK" -# else -# define CURL_DISABLE_LDAP 1 -# endif -# endif -#endif - -#if defined(HAVE_LIBIDN2) && defined(HAVE_IDN2_H) && !defined(USE_WIN32_IDN) -/* The lib and header are present */ -#define USE_LIBIDN2 -#endif - -#if defined(USE_LIBIDN2) && defined(USE_WIN32_IDN) -#error "Both libidn2 and WinIDN are enabled, choose one." -#endif - -#define LIBIDN_REQUIRED_VERSION "0.4.1" - -#if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_NSS) || \ - defined(USE_MBEDTLS) || \ - defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \ - defined(USE_SECTRANSP) || defined(USE_GSKIT) || \ - defined(USE_BEARSSL) || defined(USE_RUSTLS) -#define USE_SSL /* SSL support has been enabled */ -#endif - -/* Single point where USE_SPNEGO definition might be defined */ -#if !defined(CURL_DISABLE_CRYPTO_AUTH) && \ - (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) -#define USE_SPNEGO -#endif - -/* Single point where USE_KERBEROS5 definition might be defined */ -#if !defined(CURL_DISABLE_CRYPTO_AUTH) && \ - (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) -#define USE_KERBEROS5 -#endif - -/* Single point where USE_NTLM definition might be defined */ -#if !defined(CURL_DISABLE_CRYPTO_AUTH) && !defined(CURL_DISABLE_NTLM) -# if defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \ - defined(USE_GNUTLS) || defined(USE_NSS) || defined(USE_SECTRANSP) || \ - defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) || \ - (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT)) -# define USE_CURL_NTLM_CORE -# endif -# if defined(USE_CURL_NTLM_CORE) || defined(USE_WINDOWS_SSPI) -# define USE_NTLM -# endif -#endif - -#ifdef CURL_WANTS_CA_BUNDLE_ENV -#error "No longer supported. Set CURLOPT_CAINFO at runtime instead." -#endif - -#if defined(USE_LIBSSH2) || defined(USE_LIBSSH) || defined(USE_WOLFSSH) -#define USE_SSH -#endif - -/* - * Provide a mechanism to silence picky compilers, such as gcc 4.6+. - * Parameters should of course normally not be unused, but for example when - * we have multiple implementations of the same interface it may happen. - */ - -#if defined(__GNUC__) && ((__GNUC__ >= 3) || \ - ((__GNUC__ == 2) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 7))) -# define UNUSED_PARAM __attribute__((__unused__)) -# define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) -#else -# define UNUSED_PARAM /*NOTHING*/ -# define WARN_UNUSED_RESULT -#endif - -/* - * Include macros and defines that should only be processed once. - */ - -#ifndef HEADER_CURL_SETUP_ONCE_H -#include "curl_setup_once.h" -#endif - -/* - * Definition of our NOP statement Object-like macro - */ - -#ifndef Curl_nop_stmt -# define Curl_nop_stmt do { } while(0) -#endif - -/* - * Ensure that Winsock and lwIP TCP/IP stacks are not mixed. - */ - -#if defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H) -# if defined(SOCKET) || \ - defined(USE_WINSOCK) || \ - defined(HAVE_WINSOCK2_H) || \ - defined(HAVE_WS2TCPIP_H) -# error "WinSock and lwIP TCP/IP stack definitions shall not coexist!" -# endif -#endif - -/* - * shutdown() flags for systems that don't define them - */ - -#ifndef SHUT_RD -#define SHUT_RD 0x00 -#endif - -#ifndef SHUT_WR -#define SHUT_WR 0x01 -#endif - -#ifndef SHUT_RDWR -#define SHUT_RDWR 0x02 -#endif - -/* Define S_ISREG if not defined by system headers, e.g. MSVC */ -#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG) -#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) -#endif - -/* Define S_ISDIR if not defined by system headers, e.g. MSVC */ -#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR) -#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) -#endif - -/* In Windows the default file mode is text but an application can override it. -Therefore we specify it explicitly. https://github.com/curl/curl/pull/258 -*/ -#if defined(WIN32) || defined(MSDOS) -#define FOPEN_READTEXT "rt" -#define FOPEN_WRITETEXT "wt" -#define FOPEN_APPENDTEXT "at" -#elif defined(__CYGWIN__) -/* Cygwin has specific behavior we need to address when WIN32 is not defined. -https://cygwin.com/cygwin-ug-net/using-textbinary.html -For write we want our output to have line endings of LF and be compatible with -other Cygwin utilities. For read we want to handle input that may have line -endings either CRLF or LF so 't' is appropriate. -*/ -#define FOPEN_READTEXT "rt" -#define FOPEN_WRITETEXT "w" -#define FOPEN_APPENDTEXT "a" -#else -#define FOPEN_READTEXT "r" -#define FOPEN_WRITETEXT "w" -#define FOPEN_APPENDTEXT "a" -#endif - -/* WinSock destroys recv() buffer when send() failed. - * Enabled automatically for Windows and for Cygwin as Cygwin sockets are - * wrappers for WinSock sockets. https://github.com/curl/curl/issues/657 - * Define DONT_USE_RECV_BEFORE_SEND_WORKAROUND to force disable workaround. - */ -#if !defined(DONT_USE_RECV_BEFORE_SEND_WORKAROUND) -# if defined(WIN32) || defined(__CYGWIN__) -# define USE_RECV_BEFORE_SEND_WORKAROUND -# endif -#else /* DONT_USE_RECV_BEFORE_SEND_WORKAROUND */ -# ifdef USE_RECV_BEFORE_SEND_WORKAROUND -# undef USE_RECV_BEFORE_SEND_WORKAROUND -# endif -#endif /* DONT_USE_RECV_BEFORE_SEND_WORKAROUND */ - -/* for systems that don't detect this in configure */ -#ifndef CURL_SA_FAMILY_T -# if defined(HAVE_SA_FAMILY_T) -# define CURL_SA_FAMILY_T sa_family_t -# elif defined(HAVE_ADDRESS_FAMILY) -# define CURL_SA_FAMILY_T ADDRESS_FAMILY -# else -/* use a sensible default */ -# define CURL_SA_FAMILY_T unsigned short -# endif -#endif - -/* Some convenience macros to get the larger/smaller value out of two given. - We prefix with CURL to prevent name collisions. */ -#define CURLMAX(x,y) ((x)>(y)?(x):(y)) -#define CURLMIN(x,y) ((x)<(y)?(x):(y)) - -/* A convenience macro to provide both the string literal and the length of - the string literal in one go, useful for functions that take "string,len" - as their argument */ -#define STRCONST(x) x,sizeof(x)-1 - -/* Some versions of the Android SDK is missing the declaration */ -#if defined(HAVE_GETPWUID_R) && defined(HAVE_DECL_GETPWUID_R_MISSING) -struct passwd; -int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, - size_t buflen, struct passwd **result); -#endif - -#ifdef DEBUGBUILD -#define UNITTEST -#else -#define UNITTEST static -#endif - -#if defined(USE_NGHTTP2) || defined(USE_HYPER) -#define USE_HTTP2 -#endif - -#if defined(USE_NGTCP2) || defined(USE_QUICHE) || defined(USE_MSH3) -#define ENABLE_QUIC -#define USE_HTTP3 -#endif - -#if defined(USE_UNIX_SOCKETS) && defined(WIN32) -# if defined(__MINGW32__) && !defined(LUP_SECURE) - typedef u_short ADDRESS_FAMILY; /* Classic mingw, 11y+ old mingw-w64 */ -# endif -# if !defined(UNIX_PATH_MAX) - /* Replicating logic present in afunix.h - (distributed with newer Windows 10 SDK versions only) */ -# define UNIX_PATH_MAX 108 - /* !checksrc! disable TYPEDEFSTRUCT 1 */ - typedef struct sockaddr_un { - ADDRESS_FAMILY sun_family; - char sun_path[UNIX_PATH_MAX]; - } SOCKADDR_UN, *PSOCKADDR_UN; -# define WIN32_SOCKADDR_UN -# endif -#endif - -#endif /* HEADER_CURL_SETUP_H */ +#ifndef HEADER_CURL_SETUP_H
+#define HEADER_CURL_SETUP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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(BUILDING_LIBCURL) && !defined(CURL_NO_OLDIES)
+#define CURL_NO_OLDIES
+#endif
+
+/* define mingw version macros, eg __MINGW{32,64}_{MINOR,MAJOR}_VERSION */
+#ifdef __MINGW32__
+#include <_mingw.h>
+#endif
+
+/*
+ * Disable Visual Studio warnings:
+ * 4127 "conditional expression is constant"
+ */
+#ifdef _MSC_VER
+#pragma warning(disable:4127)
+#endif
+
+/*
+ * Define WIN32 when build target is Win32 API
+ */
+
+#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
+#define WIN32
+#endif
+
+#ifdef WIN32
+/*
+ * Don't include unneeded stuff in Windows headers to avoid compiler
+ * warnings and macro clashes.
+ * Make sure to define this macro before including any Windows headers.
+ */
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# ifndef NOGDI
+# define NOGDI
+# endif
+/* Detect Windows App environment which has a restricted access
+ * to the Win32 APIs. */
+# if (defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0602)) || \
+ defined(WINAPI_FAMILY)
+# include <winapifamily.h>
+# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \
+ !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+# define CURL_WINDOWS_APP
+# endif
+# endif
+#endif
+
+/*
+ * Include configuration script results or hand-crafted
+ * configuration file for platforms which lack config tool.
+ */
+
+#ifdef HAVE_CONFIG_H
+
+#include "curl_config.h"
+
+#else /* HAVE_CONFIG_H */
+
+#ifdef _WIN32_WCE
+# include "config-win32ce.h"
+#else
+# ifdef WIN32
+# include "config-win32.h"
+# endif
+#endif
+
+#ifdef macintosh
+# include "config-mac.h"
+#endif
+
+#ifdef __riscos__
+# include "config-riscos.h"
+#endif
+
+#ifdef __AMIGA__
+# include "config-amigaos.h"
+#endif
+
+#ifdef __OS400__
+# include "config-os400.h"
+#endif
+
+#ifdef __PLAN9__
+# include "config-plan9.h"
+#endif
+
+#ifdef MSDOS
+# include "config-dos.h"
+#endif
+
+#endif /* HAVE_CONFIG_H */
+
+/* ================================================================ */
+/* Definition of preprocessor macros/symbols which modify compiler */
+/* behavior or generated code characteristics must be done here, */
+/* as appropriate, before any system header file is included. It is */
+/* also possible to have them defined in the config file included */
+/* before this point. As a result of all this we frown inclusion of */
+/* system header files in our config files, avoid this at any cost. */
+/* ================================================================ */
+
+/*
+ * AIX 4.3 and newer needs _THREAD_SAFE defined to build
+ * proper reentrant code. Others may also need it.
+ */
+
+#ifdef NEED_THREAD_SAFE
+# ifndef _THREAD_SAFE
+# define _THREAD_SAFE
+# endif
+#endif
+
+/*
+ * Tru64 needs _REENTRANT set for a few function prototypes and
+ * things to appear in the system header files. Unixware needs it
+ * to build proper reentrant code. Others may also need it.
+ */
+
+#ifdef NEED_REENTRANT
+# ifndef _REENTRANT
+# define _REENTRANT
+# endif
+#endif
+
+/* Solaris needs this to get a POSIX-conformant getpwuid_r */
+#if defined(sun) || defined(__sun)
+# ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+# endif
+#endif
+
+/* ================================================================ */
+/* If you need to include a system header file for your platform, */
+/* please, do it beyond the point further indicated in this file. */
+/* ================================================================ */
+
+/*
+ * Disable other protocols when http is the only one desired.
+ */
+
+#ifdef HTTP_ONLY
+# ifndef CURL_DISABLE_DICT
+# define CURL_DISABLE_DICT
+# endif
+# ifndef CURL_DISABLE_FILE
+# define CURL_DISABLE_FILE
+# endif
+# ifndef CURL_DISABLE_FTP
+# define CURL_DISABLE_FTP
+# endif
+# ifndef CURL_DISABLE_GOPHER
+# define CURL_DISABLE_GOPHER
+# endif
+# ifndef CURL_DISABLE_IMAP
+# define CURL_DISABLE_IMAP
+# endif
+# ifndef CURL_DISABLE_LDAP
+# define CURL_DISABLE_LDAP
+# endif
+# ifndef CURL_DISABLE_LDAPS
+# define CURL_DISABLE_LDAPS
+# endif
+# ifndef CURL_DISABLE_MQTT
+# define CURL_DISABLE_MQTT
+# endif
+# ifndef CURL_DISABLE_POP3
+# define CURL_DISABLE_POP3
+# endif
+# ifndef CURL_DISABLE_RTSP
+# define CURL_DISABLE_RTSP
+# endif
+# ifndef CURL_DISABLE_SMB
+# define CURL_DISABLE_SMB
+# endif
+# ifndef CURL_DISABLE_SMTP
+# define CURL_DISABLE_SMTP
+# endif
+# ifndef CURL_DISABLE_TELNET
+# define CURL_DISABLE_TELNET
+# endif
+# ifndef CURL_DISABLE_TFTP
+# define CURL_DISABLE_TFTP
+# endif
+#endif
+
+/*
+ * When http is disabled rtsp is not supported.
+ */
+
+#if defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_RTSP)
+# define CURL_DISABLE_RTSP
+#endif
+
+/* ================================================================ */
+/* No system header file shall be included in this file before this */
+/* point. */
+/* ================================================================ */
+
+/*
+ * OS/400 setup file includes some system headers.
+ */
+
+#ifdef __OS400__
+# include "setup-os400.h"
+#endif
+
+/*
+ * VMS setup file includes some system headers.
+ */
+
+#ifdef __VMS
+# include "setup-vms.h"
+#endif
+
+/*
+ * Windows setup file includes some system headers.
+ */
+
+#ifdef HAVE_WINDOWS_H
+# include "setup-win32.h"
+#endif
+
+#include <curl/system.h>
+
+/*
+ * Use getaddrinfo to resolve the IPv4 address literal. If the current network
+ * interface doesn't support IPv4, but supports IPv6, NAT64, and DNS64,
+ * performing this task will result in a synthesized IPv6 address.
+ */
+#if defined(__APPLE__) && !defined(USE_ARES)
+#include <TargetConditionals.h>
+#define USE_RESOLVE_ON_IPS 1
+# if defined(TARGET_OS_OSX) && TARGET_OS_OSX
+# define CURL_OSX_CALL_COPYPROXIES 1
+# endif
+#endif
+
+#ifdef USE_LWIPSOCK
+# include <lwip/init.h>
+# include <lwip/sockets.h>
+# include <lwip/netdb.h>
+#endif
+
+#ifdef HAVE_EXTRA_STRICMP_H
+# include <extra/stricmp.h>
+#endif
+
+#ifdef HAVE_EXTRA_STRDUP_H
+# include <extra/strdup.h>
+#endif
+
+#ifdef __AMIGA__
+# ifdef __amigaos4__
+# define __USE_INLINE__
+ /* use our own resolver which uses runtime feature detection */
+# define CURLRES_AMIGA
+ /* getaddrinfo() currently crashes bsdsocket.library, so disable */
+# undef HAVE_GETADDRINFO
+# if !(defined(__NEWLIB__) || \
+ (defined(__CLIB2__) && defined(__THREAD_SAFE)))
+ /* disable threaded resolver with clib2 - requires newlib or clib-ts */
+# undef USE_THREADS_POSIX
+# endif
+# endif
+# include <exec/types.h>
+# include <exec/execbase.h>
+# include <proto/exec.h>
+# include <proto/dos.h>
+# include <unistd.h>
+# if defined(HAVE_PROTO_BSDSOCKET_H) && \
+ (!defined(__amigaos4__) || defined(USE_AMISSL))
+ /* use bsdsocket.library directly, instead of libc networking functions */
+# include <proto/bsdsocket.h>
+# ifdef __amigaos4__
+ int Curl_amiga_select(int nfds, fd_set *readfds, fd_set *writefds,
+ fd_set *errorfds, struct timeval *timeout);
+# define select(a,b,c,d,e) Curl_amiga_select(a,b,c,d,e)
+# else
+# define select(a,b,c,d,e) WaitSelect(a,b,c,d,e,0)
+# endif
+ /* must not use libc's fcntl() on bsdsocket.library sockfds! */
+# undef HAVE_FCNTL
+# undef HAVE_FCNTL_O_NONBLOCK
+# else
+ /* use libc networking and hence close() and fnctl() */
+# undef HAVE_CLOSESOCKET_CAMEL
+# undef HAVE_IOCTLSOCKET_CAMEL
+# endif
+/*
+ * In clib2 arpa/inet.h warns that some prototypes may clash
+ * with bsdsocket.library. This avoids the definition of those.
+ */
+# define __NO_NET_API
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+
+#ifdef __TANDEM /* for ns*-tandem-nsk systems */
+# if ! defined __LP64
+# include <floss.h> /* FLOSS is only used for 32-bit builds. */
+# endif
+#endif
+
+#ifndef STDC_HEADERS /* no standard C headers! */
+#include <curl/stdcheaders.h>
+#endif
+
+#ifdef __POCC__
+# include <sys/types.h>
+# include <unistd.h>
+# define sys_nerr EILSEQ
+#endif
+
+/*
+ * Salford-C kludge section (mostly borrowed from wxWidgets).
+ */
+#ifdef __SALFORDC__
+ #pragma suppress 353 /* Possible nested comments */
+ #pragma suppress 593 /* Define not used */
+ #pragma suppress 61 /* enum has no name */
+ #pragma suppress 106 /* unnamed, unused parameter */
+ #include <clib.h>
+#endif
+
+/*
+ * Large file (>2Gb) support using WIN32 functions.
+ */
+
+#ifdef USE_WIN32_LARGE_FILES
+# include <io.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# undef lseek
+# define lseek(fdes,offset,whence) _lseeki64(fdes, offset, whence)
+# undef fstat
+# define fstat(fdes,stp) _fstati64(fdes, stp)
+# undef stat
+# define stat(fname,stp) curlx_win32_stat(fname, stp)
+# define struct_stat struct _stati64
+# define LSEEK_ERROR (__int64)-1
+# define open curlx_win32_open
+# define fopen(fname,mode) curlx_win32_fopen(fname, mode)
+# define access(fname,mode) curlx_win32_access(fname, mode)
+ int curlx_win32_open(const char *filename, int oflag, ...);
+ int curlx_win32_stat(const char *path, struct_stat *buffer);
+ FILE *curlx_win32_fopen(const char *filename, const char *mode);
+ int curlx_win32_access(const char *path, int mode);
+#endif
+
+/*
+ * Small file (<2Gb) support using WIN32 functions.
+ */
+
+#ifdef USE_WIN32_SMALL_FILES
+# include <io.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# ifndef _WIN32_WCE
+# undef lseek
+# define lseek(fdes,offset,whence) _lseek(fdes, (long)offset, whence)
+# define fstat(fdes,stp) _fstat(fdes, stp)
+# define stat(fname,stp) curlx_win32_stat(fname, stp)
+# define struct_stat struct _stat
+# define open curlx_win32_open
+# define fopen(fname,mode) curlx_win32_fopen(fname, mode)
+# define access(fname,mode) curlx_win32_access(fname, mode)
+ int curlx_win32_stat(const char *path, struct_stat *buffer);
+ int curlx_win32_open(const char *filename, int oflag, ...);
+ FILE *curlx_win32_fopen(const char *filename, const char *mode);
+ int curlx_win32_access(const char *path, int mode);
+# endif
+# define LSEEK_ERROR (long)-1
+#endif
+
+#ifndef struct_stat
+# define struct_stat struct stat
+#endif
+
+#ifndef LSEEK_ERROR
+# define LSEEK_ERROR (off_t)-1
+#endif
+
+#ifndef SIZEOF_TIME_T
+/* assume default size of time_t to be 32 bit */
+#define SIZEOF_TIME_T 4
+#endif
+
+/*
+ * Default sizeof(off_t) in case it hasn't been defined in config file.
+ */
+
+#ifndef SIZEOF_OFF_T
+# if defined(__VMS) && !defined(__VAX)
+# if defined(_LARGEFILE)
+# define SIZEOF_OFF_T 8
+# endif
+# elif defined(__OS400__) && defined(__ILEC400__)
+# if defined(_LARGE_FILES)
+# define SIZEOF_OFF_T 8
+# endif
+# elif defined(__MVS__) && defined(__IBMC__)
+# if defined(_LP64) || defined(_LARGE_FILES)
+# define SIZEOF_OFF_T 8
+# endif
+# elif defined(__370__) && defined(__IBMC__)
+# if defined(_LP64) || defined(_LARGE_FILES)
+# define SIZEOF_OFF_T 8
+# endif
+# endif
+# ifndef SIZEOF_OFF_T
+# define SIZEOF_OFF_T 4
+# endif
+#endif
+
+#if (SIZEOF_CURL_OFF_T == 4)
+# define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFF)
+#else
+ /* assume SIZEOF_CURL_OFF_T == 8 */
+# define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFFFFFFFFFF)
+#endif
+#define CURL_OFF_T_MIN (-CURL_OFF_T_MAX - CURL_OFF_T_C(1))
+
+#if (SIZEOF_TIME_T == 4)
+# ifdef HAVE_TIME_T_UNSIGNED
+# define TIME_T_MAX UINT_MAX
+# define TIME_T_MIN 0
+# else
+# define TIME_T_MAX INT_MAX
+# define TIME_T_MIN INT_MIN
+# endif
+#else
+# ifdef HAVE_TIME_T_UNSIGNED
+# define TIME_T_MAX 0xFFFFFFFFFFFFFFFF
+# define TIME_T_MIN 0
+# else
+# define TIME_T_MAX 0x7FFFFFFFFFFFFFFF
+# define TIME_T_MIN (-TIME_T_MAX - 1)
+# endif
+#endif
+
+#ifndef SIZE_T_MAX
+/* some limits.h headers have this defined, some don't */
+#if defined(SIZEOF_SIZE_T) && (SIZEOF_SIZE_T > 4)
+#define SIZE_T_MAX 18446744073709551615U
+#else
+#define SIZE_T_MAX 4294967295U
+#endif
+#endif
+
+#ifndef SSIZE_T_MAX
+/* some limits.h headers have this defined, some don't */
+#if defined(SIZEOF_SIZE_T) && (SIZEOF_SIZE_T > 4)
+#define SSIZE_T_MAX 9223372036854775807
+#else
+#define SSIZE_T_MAX 2147483647
+#endif
+#endif
+
+/*
+ * Arg 2 type for gethostname in case it hasn't been defined in config file.
+ */
+
+#ifndef GETHOSTNAME_TYPE_ARG2
+# ifdef USE_WINSOCK
+# define GETHOSTNAME_TYPE_ARG2 int
+# else
+# define GETHOSTNAME_TYPE_ARG2 size_t
+# endif
+#endif
+
+/* Below we define some functions. They should
+
+ 4. set the SIGALRM signal timeout
+ 5. set dir/file naming defines
+ */
+
+#ifdef WIN32
+
+# define DIR_CHAR "\\"
+
+#else /* WIN32 */
+
+# ifdef MSDOS /* Watt-32 */
+
+# include <sys/ioctl.h>
+# define select(n,r,w,x,t) select_s(n,r,w,x,t)
+# define ioctl(x,y,z) ioctlsocket(x,y,(char *)(z))
+# include <tcp.h>
+# ifdef word
+# undef word
+# endif
+# ifdef byte
+# undef byte
+# endif
+
+# endif /* MSDOS */
+
+# ifdef __minix
+ /* Minix 3 versions up to at least 3.1.3 are missing these prototypes */
+ extern char *strtok_r(char *s, const char *delim, char **last);
+ extern struct tm *gmtime_r(const time_t * const timep, struct tm *tmp);
+# endif
+
+# define DIR_CHAR "/"
+
+# ifndef fileno /* sunos 4 have this as a macro! */
+ int fileno(FILE *stream);
+# endif
+
+#endif /* WIN32 */
+
+/*
+ * msvc 6.0 requires PSDK in order to have INET6_ADDRSTRLEN
+ * defined in ws2tcpip.h as well as to provide IPv6 support.
+ * Does not apply if lwIP is used.
+ */
+
+#if defined(_MSC_VER) && !defined(__POCC__) && !defined(USE_LWIPSOCK)
+# if !defined(HAVE_WS2TCPIP_H) || \
+ ((_MSC_VER < 1300) && !defined(INET6_ADDRSTRLEN))
+# undef HAVE_GETADDRINFO_THREADSAFE
+# undef HAVE_FREEADDRINFO
+# undef HAVE_GETADDRINFO
+# undef ENABLE_IPV6
+# endif
+#endif
+
+/* ---------------------------------------------------------------- */
+/* resolver specialty compile-time defines */
+/* CURLRES_* defines to use in the host*.c sources */
+/* ---------------------------------------------------------------- */
+
+/*
+ * lcc-win32 doesn't have _beginthreadex(), lacks threads support.
+ */
+
+#if defined(__LCC__) && defined(WIN32)
+# undef USE_THREADS_POSIX
+# undef USE_THREADS_WIN32
+#endif
+
+/*
+ * MSVC threads support requires a multi-threaded runtime library.
+ * _beginthreadex() is not available in single-threaded ones.
+ */
+
+#if defined(_MSC_VER) && !defined(__POCC__) && !defined(_MT)
+# undef USE_THREADS_POSIX
+# undef USE_THREADS_WIN32
+#endif
+
+/*
+ * Mutually exclusive CURLRES_* definitions.
+ */
+
+#if defined(ENABLE_IPV6) && defined(HAVE_GETADDRINFO)
+# define CURLRES_IPV6
+#else
+# define CURLRES_IPV4
+#endif
+
+#ifdef USE_ARES
+# define CURLRES_ASYNCH
+# define CURLRES_ARES
+/* now undef the stock libc functions just to avoid them being used */
+# undef HAVE_GETADDRINFO
+# undef HAVE_FREEADDRINFO
+#elif defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
+# define CURLRES_ASYNCH
+# define CURLRES_THREADED
+#else
+# define CURLRES_SYNCH
+#endif
+
+/* ---------------------------------------------------------------- */
+
+/*
+ * msvc 6.0 does not have struct sockaddr_storage and
+ * does not define IPPROTO_ESP in winsock2.h. But both
+ * are available if PSDK is properly installed.
+ */
+
+#if defined(_MSC_VER) && !defined(__POCC__)
+# if !defined(HAVE_WINSOCK2_H) || ((_MSC_VER < 1300) && !defined(IPPROTO_ESP))
+# undef HAVE_STRUCT_SOCKADDR_STORAGE
+# endif
+#endif
+
+/*
+ * Intentionally fail to build when using msvc 6.0 without PSDK installed.
+ * The brave of heart can circumvent this, defining ALLOW_MSVC6_WITHOUT_PSDK
+ * in lib/config-win32.h although absolutely discouraged and unsupported.
+ */
+
+#if defined(_MSC_VER) && !defined(__POCC__)
+# if !defined(HAVE_WINDOWS_H) || ((_MSC_VER < 1300) && !defined(_FILETIME_))
+# if !defined(ALLOW_MSVC6_WITHOUT_PSDK)
+# error MSVC 6.0 requires "February 2003 Platform SDK" a.k.a. \
+ "Windows Server 2003 PSDK"
+# else
+# define CURL_DISABLE_LDAP 1
+# endif
+# endif
+#endif
+
+#if defined(HAVE_LIBIDN2) && defined(HAVE_IDN2_H) && !defined(USE_WIN32_IDN)
+/* The lib and header are present */
+#define USE_LIBIDN2
+#endif
+
+#if defined(USE_LIBIDN2) && defined(USE_WIN32_IDN)
+#error "Both libidn2 and WinIDN are enabled, choose one."
+#endif
+
+#define LIBIDN_REQUIRED_VERSION "0.4.1"
+
+#if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_NSS) || \
+ defined(USE_MBEDTLS) || \
+ defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \
+ defined(USE_SECTRANSP) || defined(USE_GSKIT) || \
+ defined(USE_BEARSSL) || defined(USE_RUSTLS)
+#define USE_SSL /* SSL support has been enabled */
+#endif
+
+/* Single point where USE_SPNEGO definition might be defined */
+#if !defined(CURL_DISABLE_CRYPTO_AUTH) && \
+ (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI))
+#define USE_SPNEGO
+#endif
+
+/* Single point where USE_KERBEROS5 definition might be defined */
+#if !defined(CURL_DISABLE_CRYPTO_AUTH) && \
+ (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI))
+#define USE_KERBEROS5
+#endif
+
+/* Single point where USE_NTLM definition might be defined */
+#if !defined(CURL_DISABLE_CRYPTO_AUTH) && !defined(CURL_DISABLE_NTLM)
+# if defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \
+ defined(USE_GNUTLS) || defined(USE_NSS) || defined(USE_SECTRANSP) || \
+ defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) || \
+ (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT))
+# define USE_CURL_NTLM_CORE
+# endif
+# if defined(USE_CURL_NTLM_CORE) || defined(USE_WINDOWS_SSPI)
+# define USE_NTLM
+# endif
+#endif
+
+#ifdef CURL_WANTS_CA_BUNDLE_ENV
+#error "No longer supported. Set CURLOPT_CAINFO at runtime instead."
+#endif
+
+#if defined(USE_LIBSSH2) || defined(USE_LIBSSH) || defined(USE_WOLFSSH)
+#define USE_SSH
+#endif
+
+/*
+ * Provide a mechanism to silence picky compilers, such as gcc 4.6+.
+ * Parameters should of course normally not be unused, but for example when
+ * we have multiple implementations of the same interface it may happen.
+ */
+
+#if defined(__GNUC__) && ((__GNUC__ >= 3) || \
+ ((__GNUC__ == 2) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 7)))
+# define UNUSED_PARAM __attribute__((__unused__))
+# define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+# define UNUSED_PARAM /* NOTHING */
+# define WARN_UNUSED_RESULT
+#endif
+
+/*
+ * Include macros and defines that should only be processed once.
+ */
+
+#ifndef HEADER_CURL_SETUP_ONCE_H
+#include "curl_setup_once.h"
+#endif
+
+/*
+ * Definition of our NOP statement Object-like macro
+ */
+
+#ifndef Curl_nop_stmt
+# define Curl_nop_stmt do { } while(0)
+#endif
+
+/*
+ * Ensure that Winsock and lwIP TCP/IP stacks are not mixed.
+ */
+
+#if defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H)
+# if defined(SOCKET) || \
+ defined(USE_WINSOCK) || \
+ defined(HAVE_WINSOCK2_H) || \
+ defined(HAVE_WS2TCPIP_H)
+# error "WinSock and lwIP TCP/IP stack definitions shall not coexist!"
+# endif
+#endif
+
+/*
+ * shutdown() flags for systems that don't define them
+ */
+
+#ifndef SHUT_RD
+#define SHUT_RD 0x00
+#endif
+
+#ifndef SHUT_WR
+#define SHUT_WR 0x01
+#endif
+
+#ifndef SHUT_RDWR
+#define SHUT_RDWR 0x02
+#endif
+
+/* Define S_ISREG if not defined by system headers, e.g. MSVC */
+#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+
+/* Define S_ISDIR if not defined by system headers, e.g. MSVC */
+#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+
+/* In Windows the default file mode is text but an application can override it.
+Therefore we specify it explicitly. https://github.com/curl/curl/pull/258
+*/
+#if defined(WIN32) || defined(MSDOS)
+#define FOPEN_READTEXT "rt"
+#define FOPEN_WRITETEXT "wt"
+#define FOPEN_APPENDTEXT "at"
+#elif defined(__CYGWIN__)
+/* Cygwin has specific behavior we need to address when WIN32 is not defined.
+https://cygwin.com/cygwin-ug-net/using-textbinary.html
+For write we want our output to have line endings of LF and be compatible with
+other Cygwin utilities. For read we want to handle input that may have line
+endings either CRLF or LF so 't' is appropriate.
+*/
+#define FOPEN_READTEXT "rt"
+#define FOPEN_WRITETEXT "w"
+#define FOPEN_APPENDTEXT "a"
+#else
+#define FOPEN_READTEXT "r"
+#define FOPEN_WRITETEXT "w"
+#define FOPEN_APPENDTEXT "a"
+#endif
+
+/* WinSock destroys recv() buffer when send() failed.
+ * Enabled automatically for Windows and for Cygwin as Cygwin sockets are
+ * wrappers for WinSock sockets. https://github.com/curl/curl/issues/657
+ * Define DONT_USE_RECV_BEFORE_SEND_WORKAROUND to force disable workaround.
+ */
+#if !defined(DONT_USE_RECV_BEFORE_SEND_WORKAROUND)
+# if defined(WIN32) || defined(__CYGWIN__)
+# define USE_RECV_BEFORE_SEND_WORKAROUND
+# endif
+#else /* DONT_USE_RECV_BEFORE_SEND_WORKAROUND */
+# ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+# undef USE_RECV_BEFORE_SEND_WORKAROUND
+# endif
+#endif /* DONT_USE_RECV_BEFORE_SEND_WORKAROUND */
+
+/* for systems that don't detect this in configure */
+#ifndef CURL_SA_FAMILY_T
+# if defined(HAVE_SA_FAMILY_T)
+# define CURL_SA_FAMILY_T sa_family_t
+# elif defined(HAVE_ADDRESS_FAMILY)
+# define CURL_SA_FAMILY_T ADDRESS_FAMILY
+# else
+/* use a sensible default */
+# define CURL_SA_FAMILY_T unsigned short
+# endif
+#endif
+
+/* Some convenience macros to get the larger/smaller value out of two given.
+ We prefix with CURL to prevent name collisions. */
+#define CURLMAX(x,y) ((x)>(y)?(x):(y))
+#define CURLMIN(x,y) ((x)<(y)?(x):(y))
+
+/* A convenience macro to provide both the string literal and the length of
+ the string literal in one go, useful for functions that take "string,len"
+ as their argument */
+#define STRCONST(x) x,sizeof(x)-1
+
+/* Some versions of the Android SDK is missing the declaration */
+#if defined(HAVE_GETPWUID_R) && defined(HAVE_DECL_GETPWUID_R_MISSING)
+struct passwd;
+int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
+ size_t buflen, struct passwd **result);
+#endif
+
+#ifdef DEBUGBUILD
+#define UNITTEST
+#else
+#define UNITTEST static
+#endif
+
+#if defined(USE_NGHTTP2) || defined(USE_HYPER)
+#define USE_HTTP2
+#endif
+
+#if defined(USE_NGTCP2) || defined(USE_QUICHE) || defined(USE_MSH3)
+#define ENABLE_QUIC
+#define USE_HTTP3
+#endif
+
+#if defined(USE_UNIX_SOCKETS) && defined(WIN32)
+# if defined(__MINGW32__) && !defined(LUP_SECURE)
+ typedef u_short ADDRESS_FAMILY; /* Classic mingw, 11y+ old mingw-w64 */
+# endif
+# if !defined(UNIX_PATH_MAX)
+ /* Replicating logic present in afunix.h
+ (distributed with newer Windows 10 SDK versions only) */
+# define UNIX_PATH_MAX 108
+ /* !checksrc! disable TYPEDEFSTRUCT 1 */
+ typedef struct sockaddr_un {
+ ADDRESS_FAMILY sun_family;
+ char sun_path[UNIX_PATH_MAX];
+ } SOCKADDR_UN, *PSOCKADDR_UN;
+# define WIN32_SOCKADDR_UN
+# endif
+#endif
+
+#endif /* HEADER_CURL_SETUP_H */
diff --git a/libs/libcurl/src/curl_setup_once.h b/libs/libcurl/src/curl_setup_once.h index f09b00f9f2..5c69a9ab31 100644 --- a/libs/libcurl/src/curl_setup_once.h +++ b/libs/libcurl/src/curl_setup_once.h @@ -1,411 +1,408 @@ -#ifndef HEADER_CURL_SETUP_ONCE_H -#define HEADER_CURL_SETUP_ONCE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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> - -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif - -#ifdef HAVE_SYS_TYPES_H -#include <sys/types.h> -#endif - -#ifdef NEED_MALLOC_H -#include <malloc.h> -#endif - -#ifdef NEED_MEMORY_H -#include <memory.h> -#endif - -#ifdef HAVE_SYS_STAT_H -#include <sys/stat.h> -#endif - -#ifdef HAVE_SYS_TIME_H -#include <sys/time.h> -#endif - -#ifdef WIN32 -#include <io.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 - -#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 - -#ifdef HAVE_SYS_SOCKET_H -#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 don't 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 - - -#if defined(__minix) -/* Minix doesn't 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 don't 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 don't 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 */ - - -#if defined(__minix) -/* Minix doesn't support send on TCP sockets */ -#define swrite(x,y,z) (ssize_t)write((SEND_TYPE_ARG1)(x), \ - (SEND_TYPE_ARG2)(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)(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. - */ - -#if defined(HAVE_CLOSESOCKET) -# define sclose(x) closesocket((x)) -#elif defined(HAVE_CLOSESOCKET_CAMEL) -# define sclose(x) CloseSocket((x)) -#elif defined(HAVE_CLOSE_S) -# define sclose(x) close_s((x)) -#elif defined(USE_LWIPSOCK) -# define sclose(x) lwip_close((x)) -#else -# define sclose(x) close((x)) -#endif - -/* - * Stack-independent version of fcntl() on sockets: - */ -#if defined(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's 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 -#if defined(DEBUGBUILD) && defined(HAVE_ASSERT_H) -#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 -#undef EBADF /* override definition in errno.h */ -#define EBADF WSAEBADF -#undef EINTR /* override definition in errno.h */ -#define EINTR WSAEINTR -#undef EINVAL /* override definition in errno.h */ -#define EINVAL WSAEINVAL -#undef EWOULDBLOCK /* override definition in errno.h */ -#define EWOULDBLOCK WSAEWOULDBLOCK -#undef EINPROGRESS /* override definition in errno.h */ -#define EINPROGRESS WSAEINPROGRESS -#undef EALREADY /* override definition in errno.h */ -#define EALREADY WSAEALREADY -#undef ENOTSOCK /* override definition in errno.h */ -#define ENOTSOCK WSAENOTSOCK -#undef EDESTADDRREQ /* override definition in errno.h */ -#define EDESTADDRREQ WSAEDESTADDRREQ -#undef EMSGSIZE /* override definition in errno.h */ -#define EMSGSIZE WSAEMSGSIZE -#undef EPROTOTYPE /* override definition in errno.h */ -#define EPROTOTYPE WSAEPROTOTYPE -#undef ENOPROTOOPT /* override definition in errno.h */ -#define ENOPROTOOPT WSAENOPROTOOPT -#undef EPROTONOSUPPORT /* override definition in errno.h */ -#define EPROTONOSUPPORT WSAEPROTONOSUPPORT -#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT -#undef EOPNOTSUPP /* override definition in errno.h */ -#define EOPNOTSUPP WSAEOPNOTSUPP -#define EPFNOSUPPORT WSAEPFNOSUPPORT -#undef EAFNOSUPPORT /* override definition in errno.h */ -#define EAFNOSUPPORT WSAEAFNOSUPPORT -#undef EADDRINUSE /* override definition in errno.h */ -#define EADDRINUSE WSAEADDRINUSE -#undef EADDRNOTAVAIL /* override definition in errno.h */ -#define EADDRNOTAVAIL WSAEADDRNOTAVAIL -#undef ENETDOWN /* override definition in errno.h */ -#define ENETDOWN WSAENETDOWN -#undef ENETUNREACH /* override definition in errno.h */ -#define ENETUNREACH WSAENETUNREACH -#undef ENETRESET /* override definition in errno.h */ -#define ENETRESET WSAENETRESET -#undef ECONNABORTED /* override definition in errno.h */ -#define ECONNABORTED WSAECONNABORTED -#undef ECONNRESET /* override definition in errno.h */ -#define ECONNRESET WSAECONNRESET -#undef ENOBUFS /* override definition in errno.h */ -#define ENOBUFS WSAENOBUFS -#undef EISCONN /* override definition in errno.h */ -#define EISCONN WSAEISCONN -#undef ENOTCONN /* override definition in errno.h */ -#define ENOTCONN WSAENOTCONN -#define ESHUTDOWN WSAESHUTDOWN -#define ETOOMANYREFS WSAETOOMANYREFS -#undef ETIMEDOUT /* override definition in errno.h */ -#define ETIMEDOUT WSAETIMEDOUT -#undef ECONNREFUSED /* override definition in errno.h */ -#define ECONNREFUSED WSAECONNREFUSED -#undef ELOOP /* override definition in errno.h */ -#define ELOOP WSAELOOP -#ifndef ENAMETOOLONG /* possible previous definition in errno.h */ -#define ENAMETOOLONG WSAENAMETOOLONG -#endif -#define EHOSTDOWN WSAEHOSTDOWN -#undef EHOSTUNREACH /* override definition in errno.h */ -#define EHOSTUNREACH WSAEHOSTUNREACH -#ifndef ENOTEMPTY /* possible previous definition in errno.h */ -#define ENOTEMPTY WSAENOTEMPTY -#endif -#define EPROCLIM WSAEPROCLIM -#define EUSERS WSAEUSERS -#define EDQUOT WSAEDQUOT -#define ESTALE WSAESTALE -#define EREMOTE WSAEREMOTE -#endif - -/* - * Macro argv_item_t hides platform details to code using it. - */ - -#ifdef __VMS -#define argv_item_t __char_ptr32 -#elif defined(_UNICODE) -#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 */ +#ifndef HEADER_CURL_SETUP_ONCE_H
+#define HEADER_CURL_SETUP_ONCE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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>
+#include <errno.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef NEED_MALLOC_H
+#include <malloc.h>
+#endif
+
+#ifdef NEED_MEMORY_H
+#include <memory.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef WIN32
+#include <io.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
+
+#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
+
+#ifdef HAVE_SYS_SOCKET_H
+#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 don't 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
+
+
+#if defined(__minix)
+/* Minix doesn't 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 don't 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 don't 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 */
+
+
+#if defined(__minix)
+/* Minix doesn't support send on TCP sockets */
+#define swrite(x,y,z) (ssize_t)write((SEND_TYPE_ARG1)(x), \
+ (SEND_TYPE_ARG2)(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)(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.
+ */
+
+#if defined(HAVE_CLOSESOCKET)
+# define sclose(x) closesocket((x))
+#elif defined(HAVE_CLOSESOCKET_CAMEL)
+# define sclose(x) CloseSocket((x))
+#elif defined(HAVE_CLOSE_S)
+# define sclose(x) close_s((x))
+#elif defined(USE_LWIPSOCK)
+# define sclose(x) lwip_close((x))
+#else
+# define sclose(x) close((x))
+#endif
+
+/*
+ * Stack-independent version of fcntl() on sockets:
+ */
+#if defined(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's 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
+#if defined(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
+#undef EBADF /* override definition in errno.h */
+#define EBADF WSAEBADF
+#undef EINTR /* override definition in errno.h */
+#define EINTR WSAEINTR
+#undef EINVAL /* override definition in errno.h */
+#define EINVAL WSAEINVAL
+#undef EWOULDBLOCK /* override definition in errno.h */
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#undef EINPROGRESS /* override definition in errno.h */
+#define EINPROGRESS WSAEINPROGRESS
+#undef EALREADY /* override definition in errno.h */
+#define EALREADY WSAEALREADY
+#undef ENOTSOCK /* override definition in errno.h */
+#define ENOTSOCK WSAENOTSOCK
+#undef EDESTADDRREQ /* override definition in errno.h */
+#define EDESTADDRREQ WSAEDESTADDRREQ
+#undef EMSGSIZE /* override definition in errno.h */
+#define EMSGSIZE WSAEMSGSIZE
+#undef EPROTOTYPE /* override definition in errno.h */
+#define EPROTOTYPE WSAEPROTOTYPE
+#undef ENOPROTOOPT /* override definition in errno.h */
+#define ENOPROTOOPT WSAENOPROTOOPT
+#undef EPROTONOSUPPORT /* override definition in errno.h */
+#define EPROTONOSUPPORT WSAEPROTONOSUPPORT
+#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT
+#undef EOPNOTSUPP /* override definition in errno.h */
+#define EOPNOTSUPP WSAEOPNOTSUPP
+#define EPFNOSUPPORT WSAEPFNOSUPPORT
+#undef EAFNOSUPPORT /* override definition in errno.h */
+#define EAFNOSUPPORT WSAEAFNOSUPPORT
+#undef EADDRINUSE /* override definition in errno.h */
+#define EADDRINUSE WSAEADDRINUSE
+#undef EADDRNOTAVAIL /* override definition in errno.h */
+#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
+#undef ENETDOWN /* override definition in errno.h */
+#define ENETDOWN WSAENETDOWN
+#undef ENETUNREACH /* override definition in errno.h */
+#define ENETUNREACH WSAENETUNREACH
+#undef ENETRESET /* override definition in errno.h */
+#define ENETRESET WSAENETRESET
+#undef ECONNABORTED /* override definition in errno.h */
+#define ECONNABORTED WSAECONNABORTED
+#undef ECONNRESET /* override definition in errno.h */
+#define ECONNRESET WSAECONNRESET
+#undef ENOBUFS /* override definition in errno.h */
+#define ENOBUFS WSAENOBUFS
+#undef EISCONN /* override definition in errno.h */
+#define EISCONN WSAEISCONN
+#undef ENOTCONN /* override definition in errno.h */
+#define ENOTCONN WSAENOTCONN
+#define ESHUTDOWN WSAESHUTDOWN
+#define ETOOMANYREFS WSAETOOMANYREFS
+#undef ETIMEDOUT /* override definition in errno.h */
+#define ETIMEDOUT WSAETIMEDOUT
+#undef ECONNREFUSED /* override definition in errno.h */
+#define ECONNREFUSED WSAECONNREFUSED
+#undef ELOOP /* override definition in errno.h */
+#define ELOOP WSAELOOP
+#ifndef ENAMETOOLONG /* possible previous definition in errno.h */
+#define ENAMETOOLONG WSAENAMETOOLONG
+#endif
+#define EHOSTDOWN WSAEHOSTDOWN
+#undef EHOSTUNREACH /* override definition in errno.h */
+#define EHOSTUNREACH WSAEHOSTUNREACH
+#ifndef ENOTEMPTY /* possible previous definition in errno.h */
+#define ENOTEMPTY WSAENOTEMPTY
+#endif
+#define EPROCLIM WSAEPROCLIM
+#define EUSERS WSAEUSERS
+#define EDQUOT WSAEDQUOT
+#define ESTALE WSAESTALE
+#define EREMOTE WSAEREMOTE
+#endif
+
+/*
+ * Macro argv_item_t hides platform details to code using it.
+ */
+
+#ifdef __VMS
+#define argv_item_t __char_ptr32
+#elif defined(_UNICODE)
+#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 */
diff --git a/libs/libcurl/src/curl_sha256.h b/libs/libcurl/src/curl_sha256.h index 754c761fb3..e831b462fc 100644 --- a/libs/libcurl/src/curl_sha256.h +++ b/libs/libcurl/src/curl_sha256.h @@ -1,48 +1,48 @@ -#ifndef HEADER_CURL_SHA256_H -#define HEADER_CURL_SHA256_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2017, Florin Petriuc, <petriuc.florin@gmail.com> - * Copyright (C) 2018 - 2022, 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_CRYPTO_AUTH -#include <curl/curl.h> -#include "curl_hmac.h" - -extern const struct HMAC_params Curl_HMAC_SHA256[1]; - -#ifdef USE_WOLFSSL -/* SHA256_DIGEST_LENGTH is an enum value in wolfSSL. Need to import it from - * sha.h*/ -#include <wolfssl/options.h> -#include <wolfssl/openssl/sha.h> -#else -#define SHA256_DIGEST_LENGTH 32 -#endif - -CURLcode Curl_sha256it(unsigned char *outbuffer, const unsigned char *input, - const size_t len); - -#endif - -#endif /* HEADER_CURL_SHA256_H */ +#ifndef HEADER_CURL_SHA256_H
+#define HEADER_CURL_SHA256_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2017, Florin Petriuc, <petriuc.florin@gmail.com>
+ * Copyright (C) 2018 - 2022, 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_CRYPTO_AUTH
+#include <curl/curl.h>
+#include "curl_hmac.h"
+
+extern const struct HMAC_params Curl_HMAC_SHA256[1];
+
+#ifdef USE_WOLFSSL
+/* SHA256_DIGEST_LENGTH is an enum value in wolfSSL. Need to import it from
+ * sha.h */
+#include <wolfssl/options.h>
+#include <wolfssl/openssl/sha.h>
+#else
+#define SHA256_DIGEST_LENGTH 32
+#endif
+
+CURLcode Curl_sha256it(unsigned char *outbuffer, const unsigned char *input,
+ const size_t len);
+
+#endif
+
+#endif /* HEADER_CURL_SHA256_H */
diff --git a/libs/libcurl/src/curl_sspi.c b/libs/libcurl/src/curl_sspi.c index 33108c48e9..6bf79367dd 100644 --- a/libs/libcurl/src/curl_sspi.c +++ b/libs/libcurl/src/curl_sspi.c @@ -1,239 +1,239 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "curl_multibyte.h" -#include "system_win32.h" -#include "version_win32.h" -#include "warnless.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* We use our own typedef here since some headers might lack these */ -typedef PSecurityFunctionTable (APIENTRY *INITSECURITYINTERFACE_FN)(VOID); - -/* See definition of SECURITY_ENTRYPOINT in sspi.h */ -#ifdef UNICODE -# ifdef _WIN32_WCE -# define SECURITYENTRYPOINT L"InitSecurityInterfaceW" -# else -# define SECURITYENTRYPOINT "InitSecurityInterfaceW" -# endif -#else -# define SECURITYENTRYPOINT "InitSecurityInterfaceA" -#endif - -/* Handle of security.dll or secur32.dll, depending on Windows version */ -HMODULE s_hSecDll = NULL; - -/* Pointer to SSPI dispatch table */ -PSecurityFunctionTable s_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) -{ - INITSECURITYINTERFACE_FN pInitSecurityInterface; - - /* If security interface is not yet initialized try to do this */ - if(!s_hSecDll) { - /* Security Service Provider Interface (SSPI) functions are located in - * security.dll on WinNT 4.0 and in secur32.dll on Win9x. Win2K and XP - * have both these DLLs (security.dll forwards calls to secur32.dll) */ - - /* Load SSPI dll into the address space of the calling process */ - if(curlx_verify_windows_version(4, 0, 0, PLATFORM_WINNT, VERSION_EQUAL)) - s_hSecDll = Curl_load_library(TEXT("security.dll")); - else - s_hSecDll = Curl_load_library(TEXT("secur32.dll")); - if(!s_hSecDll) - return CURLE_FAILED_INIT; - - /* Get address of the InitSecurityInterfaceA function from the SSPI dll */ - pInitSecurityInterface = - CURLX_FUNCTION_CAST(INITSECURITYINTERFACE_FN, - (GetProcAddress(s_hSecDll, SECURITYENTRYPOINT))); - if(!pInitSecurityInterface) - return CURLE_FAILED_INIT; - - /* Get pointer to Security Service Provider Interface dispatch table */ - s_pSecFn = pInitSecurityInterface(); - if(!s_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(s_hSecDll) { - FreeLibrary(s_hSecDll); - s_hSecDll = NULL; - s_pSecFn = NULL; - } -} - -/* - * Curl_create_sspi_identity() - * - * This is used to populate a SSPI identity structure based on the supplied - * username and password. - * - * Parameters: - * - * userp [in] - The user name 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((char *)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 = _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((char *)passwdp); - if(!passwd.tchar_ptr) - return CURLE_OUT_OF_MEMORY; - dup_passwd.tchar_ptr = _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 = SECFLAG_WINNT_AUTH_IDENTITY; - - return CURLE_OK; -} - -/* - * Curl_sspi_free_identity() - * - * This is used to free the contents of a 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 */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "curl_multibyte.h"
+#include "system_win32.h"
+#include "version_win32.h"
+#include "warnless.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* We use our own typedef here since some headers might lack these */
+typedef PSecurityFunctionTable (APIENTRY *INITSECURITYINTERFACE_FN)(VOID);
+
+/* See definition of SECURITY_ENTRYPOINT in sspi.h */
+#ifdef UNICODE
+# ifdef _WIN32_WCE
+# define SECURITYENTRYPOINT L"InitSecurityInterfaceW"
+# else
+# define SECURITYENTRYPOINT "InitSecurityInterfaceW"
+# endif
+#else
+# define SECURITYENTRYPOINT "InitSecurityInterfaceA"
+#endif
+
+/* Handle of security.dll or secur32.dll, depending on Windows version */
+HMODULE s_hSecDll = NULL;
+
+/* Pointer to SSPI dispatch table */
+PSecurityFunctionTable s_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)
+{
+ INITSECURITYINTERFACE_FN pInitSecurityInterface;
+
+ /* If security interface is not yet initialized try to do this */
+ if(!s_hSecDll) {
+ /* Security Service Provider Interface (SSPI) functions are located in
+ * security.dll on WinNT 4.0 and in secur32.dll on Win9x. Win2K and XP
+ * have both these DLLs (security.dll forwards calls to secur32.dll) */
+
+ /* Load SSPI dll into the address space of the calling process */
+ if(curlx_verify_windows_version(4, 0, 0, PLATFORM_WINNT, VERSION_EQUAL))
+ s_hSecDll = Curl_load_library(TEXT("security.dll"));
+ else
+ s_hSecDll = Curl_load_library(TEXT("secur32.dll"));
+ if(!s_hSecDll)
+ return CURLE_FAILED_INIT;
+
+ /* Get address of the InitSecurityInterfaceA function from the SSPI dll */
+ pInitSecurityInterface =
+ CURLX_FUNCTION_CAST(INITSECURITYINTERFACE_FN,
+ (GetProcAddress(s_hSecDll, SECURITYENTRYPOINT)));
+ if(!pInitSecurityInterface)
+ return CURLE_FAILED_INIT;
+
+ /* Get pointer to Security Service Provider Interface dispatch table */
+ s_pSecFn = pInitSecurityInterface();
+ if(!s_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(s_hSecDll) {
+ FreeLibrary(s_hSecDll);
+ s_hSecDll = NULL;
+ s_pSecFn = NULL;
+ }
+}
+
+/*
+ * Curl_create_sspi_identity()
+ *
+ * This is used to populate a SSPI identity structure based on the supplied
+ * username and password.
+ *
+ * Parameters:
+ *
+ * userp [in] - The user name 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((char *)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 = _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((char *)passwdp);
+ if(!passwd.tchar_ptr)
+ return CURLE_OUT_OF_MEMORY;
+ dup_passwd.tchar_ptr = _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 = SECFLAG_WINNT_AUTH_IDENTITY;
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_sspi_free_identity()
+ *
+ * This is used to free the contents of a 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 */
diff --git a/libs/libcurl/src/curl_sspi.h b/libs/libcurl/src/curl_sspi.h index ad111309c5..d87fd9eae1 100644 --- a/libs/libcurl/src/curl_sspi.h +++ b/libs/libcurl/src/curl_sspi.h @@ -1,352 +1,352 @@ -#ifndef HEADER_CURL_SSPI_H -#define HEADER_CURL_SSPI_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 a 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 HMODULE s_hSecDll; -extern PSecurityFunctionTable s_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" - -#ifndef ISC_REQ_USE_HTTP_STYLE -#define ISC_REQ_USE_HTTP_STYLE 0x01000000 -#endif - -#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 -#ifndef SEC_E_INVALID_PARAMETER -# define SEC_E_INVALID_PARAMETER ((HRESULT)0x8009035DL) -#endif -#ifndef SEC_E_DELEGATION_POLICY -# define SEC_E_DELEGATION_POLICY ((HRESULT)0x8009035EL) -#endif -#ifndef SEC_E_POLICY_NLTM_ONLY -# define SEC_E_POLICY_NLTM_ONLY ((HRESULT)0x8009035FL) -#endif - -#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 -#ifndef SEC_I_SIGNATURE_NEEDED -# define SEC_I_SIGNATURE_NEEDED ((HRESULT)0x0009035CL) -#endif - -#ifndef CRYPT_E_REVOKED -# define CRYPT_E_REVOKED ((HRESULT)0x80092010L) -#endif - -#ifdef UNICODE -# define SECFLAG_WINNT_AUTH_IDENTITY \ - (unsigned long)SEC_WINNT_AUTH_IDENTITY_UNICODE -#else -# define SECFLAG_WINNT_AUTH_IDENTITY \ - (unsigned long)SEC_WINNT_AUTH_IDENTITY_ANSI -#endif - -/* - * 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 */ +#ifndef HEADER_CURL_SSPI_H
+#define HEADER_CURL_SSPI_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 a 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 HMODULE s_hSecDll;
+extern PSecurityFunctionTable s_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"
+
+#ifndef ISC_REQ_USE_HTTP_STYLE
+#define ISC_REQ_USE_HTTP_STYLE 0x01000000
+#endif
+
+#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
+#ifndef SEC_E_INVALID_PARAMETER
+# define SEC_E_INVALID_PARAMETER ((HRESULT)0x8009035DL)
+#endif
+#ifndef SEC_E_DELEGATION_POLICY
+# define SEC_E_DELEGATION_POLICY ((HRESULT)0x8009035EL)
+#endif
+#ifndef SEC_E_POLICY_NLTM_ONLY
+# define SEC_E_POLICY_NLTM_ONLY ((HRESULT)0x8009035FL)
+#endif
+
+#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
+#ifndef SEC_I_SIGNATURE_NEEDED
+# define SEC_I_SIGNATURE_NEEDED ((HRESULT)0x0009035CL)
+#endif
+
+#ifndef CRYPT_E_REVOKED
+# define CRYPT_E_REVOKED ((HRESULT)0x80092010L)
+#endif
+
+#ifdef UNICODE
+# define SECFLAG_WINNT_AUTH_IDENTITY \
+ (unsigned long)SEC_WINNT_AUTH_IDENTITY_UNICODE
+#else
+# define SECFLAG_WINNT_AUTH_IDENTITY \
+ (unsigned long)SEC_WINNT_AUTH_IDENTITY_ANSI
+#endif
+
+/*
+ * 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 */
diff --git a/libs/libcurl/src/curl_threads.c b/libs/libcurl/src/curl_threads.c index eb8e136087..43ec16deec 100644 --- a/libs/libcurl/src/curl_threads.c +++ b/libs/libcurl/src/curl_threads.c @@ -1,157 +1,155 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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) -# ifdef HAVE_PTHREAD_H -# include <pthread.h> -# endif -#elif defined(USE_THREADS_WIN32) -# ifdef HAVE_PROCESS_H -# include <process.h> -# endif -#endif - -#include "curl_threads.h" -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -#if defined(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(unsigned int (*func) (void *), void *arg) -{ - curl_thread_t t = malloc(sizeof(pthread_t)); - struct Curl_actual_call *ac = malloc(sizeof(struct Curl_actual_call)); - if(!(ac && t)) - goto err; - - ac->func = func; - ac->arg = arg; - - if(pthread_create(t, NULL, curl_thread_create_thunk, ac) != 0) - 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); - } -} - -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) - -/* !checksrc! disable SPACEBEFOREPAREN 1 */ -curl_thread_t Curl_thread_create(unsigned int (CURL_STDCALL *func) (void *), - void *arg) -{ -#ifdef _WIN32_WCE - typedef HANDLE curl_win_thread_handle_t; -#elif defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) - typedef unsigned long curl_win_thread_handle_t; -#else - typedef uintptr_t curl_win_thread_handle_t; -#endif - curl_thread_t t; - curl_win_thread_handle_t thread_handle; -#ifdef _WIN32_WCE - thread_handle = CreateThread(NULL, 0, func, arg, 0, NULL); -#else - thread_handle = _beginthreadex(NULL, 0, func, arg, 0, NULL); -#endif - t = (curl_thread_t)thread_handle; - if((t == 0) || (t == LongToHandle(-1L))) { -#ifdef _WIN32_WCE - DWORD gle = GetLastError(); - errno = ((gle == ERROR_ACCESS_DENIED || - gle == ERROR_NOT_ENOUGH_MEMORY) ? - EACCES : EINVAL); -#endif - return curl_thread_t_null; - } - return t; -} - -void Curl_thread_destroy(curl_thread_t hnd) -{ - CloseHandle(hnd); -} - -int Curl_thread_join(curl_thread_t *hnd) -{ -#if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \ - (_WIN32_WINNT < _WIN32_WINNT_VISTA) - int ret = (WaitForSingleObject(*hnd, INFINITE) == WAIT_OBJECT_0); -#else - int ret = (WaitForSingleObjectEx(*hnd, INFINITE, FALSE) == WAIT_OBJECT_0); -#endif - - Curl_thread_destroy(*hnd); - - *hnd = curl_thread_t_null; - - return ret; -} - -#endif /* USE_THREADS_* */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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)
+# ifdef HAVE_PTHREAD_H
+# include <pthread.h>
+# endif
+#elif defined(USE_THREADS_WIN32)
+# include <process.h>
+#endif
+
+#include "curl_threads.h"
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+#if defined(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(unsigned int (*func) (void *), void *arg)
+{
+ curl_thread_t t = malloc(sizeof(pthread_t));
+ struct Curl_actual_call *ac = malloc(sizeof(struct Curl_actual_call));
+ if(!(ac && t))
+ goto err;
+
+ ac->func = func;
+ ac->arg = arg;
+
+ if(pthread_create(t, NULL, curl_thread_create_thunk, ac) != 0)
+ 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);
+ }
+}
+
+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)
+
+/* !checksrc! disable SPACEBEFOREPAREN 1 */
+curl_thread_t Curl_thread_create(unsigned int (CURL_STDCALL *func) (void *),
+ void *arg)
+{
+#ifdef _WIN32_WCE
+ typedef HANDLE curl_win_thread_handle_t;
+#elif defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
+ typedef unsigned long curl_win_thread_handle_t;
+#else
+ typedef uintptr_t curl_win_thread_handle_t;
+#endif
+ curl_thread_t t;
+ curl_win_thread_handle_t thread_handle;
+#ifdef _WIN32_WCE
+ thread_handle = CreateThread(NULL, 0, func, arg, 0, NULL);
+#else
+ thread_handle = _beginthreadex(NULL, 0, func, arg, 0, NULL);
+#endif
+ t = (curl_thread_t)thread_handle;
+ if((t == 0) || (t == LongToHandle(-1L))) {
+#ifdef _WIN32_WCE
+ DWORD gle = GetLastError();
+ errno = ((gle == ERROR_ACCESS_DENIED ||
+ gle == ERROR_NOT_ENOUGH_MEMORY) ?
+ EACCES : EINVAL);
+#endif
+ return curl_thread_t_null;
+ }
+ return t;
+}
+
+void Curl_thread_destroy(curl_thread_t hnd)
+{
+ CloseHandle(hnd);
+}
+
+int Curl_thread_join(curl_thread_t *hnd)
+{
+#if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \
+ (_WIN32_WINNT < _WIN32_WINNT_VISTA)
+ int ret = (WaitForSingleObject(*hnd, INFINITE) == WAIT_OBJECT_0);
+#else
+ int ret = (WaitForSingleObjectEx(*hnd, INFINITE, FALSE) == WAIT_OBJECT_0);
+#endif
+
+ Curl_thread_destroy(*hnd);
+
+ *hnd = curl_thread_t_null;
+
+ return ret;
+}
+
+#endif /* USE_THREADS_* */
diff --git a/libs/libcurl/src/curl_threads.h b/libs/libcurl/src/curl_threads.h index 63392f671f..38660662f7 100644 --- a/libs/libcurl/src/curl_threads.h +++ b/libs/libcurl/src/curl_threads.h @@ -1,66 +1,66 @@ -#ifndef HEADER_CURL_THREADS_H -#define HEADER_CURL_THREADS_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_THREADS_POSIX) -# 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_STDCALL __stdcall -# define curl_mutex_t CRITICAL_SECTION -# define curl_thread_t HANDLE -# define curl_thread_t_null (HANDLE)0 -# if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \ - (_WIN32_WINNT < _WIN32_WINNT_VISTA) || \ - (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) -# 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) - -/* !checksrc! disable SPACEBEFOREPAREN 1 */ -curl_thread_t Curl_thread_create(unsigned int (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 */ +#ifndef HEADER_CURL_THREADS_H
+#define HEADER_CURL_THREADS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_THREADS_POSIX)
+# 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_STDCALL __stdcall
+# define curl_mutex_t CRITICAL_SECTION
+# define curl_thread_t HANDLE
+# define curl_thread_t_null (HANDLE)0
+# if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \
+ (_WIN32_WINNT < _WIN32_WINNT_VISTA) || \
+ (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
+# 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)
+
+/* !checksrc! disable SPACEBEFOREPAREN 1 */
+curl_thread_t Curl_thread_create(unsigned int (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 */
diff --git a/libs/libcurl/src/curlx.h b/libs/libcurl/src/curlx.h index 1796afa00a..5ad6c6270d 100644 --- a/libs/libcurl/src/curlx.h +++ b/libs/libcurl/src/curlx.h @@ -1,118 +1,118 @@ -#ifndef HEADER_CURL_CURLX_H -#define HEADER_CURL_CURLX_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 <curl/mprintf.h> -/* this is still a public header file that provides the curl_mprintf() - functions while they still are offered publicly. They will be made library- - private one day */ - -#include "strcase.h" -/* "strcase.h" provides the strcasecompare protos */ - -#include "strtoofft.h" -/* "strtoofft.h" provides this function: curlx_strtoofft(), returns a - curl_off_t number from a given string. -*/ - -#include "nonblock.h" -/* "nonblock.h" provides curlx_nonblock() */ - -#include "warnless.h" -/* "warnless.h" provides functions: - - curlx_ultous() - curlx_ultouc() - curlx_uztosi() -*/ - -#include "curl_multibyte.h" -/* "curl_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" -/* "version_win32.h" provides curlx_verify_windows_version() */ - -/* Now setup curlx_ * names for the functions that are to become curlx_ and - be removed from a future libcurl official API: - curlx_getenv - curlx_mprintf (and its variations) - curlx_strcasecompare - curlx_strncasecompare - -*/ - -#define curlx_getenv curl_getenv -#define curlx_mvsnprintf curl_mvsnprintf -#define curlx_msnprintf curl_msnprintf -#define curlx_maprintf curl_maprintf -#define curlx_mvaprintf curl_mvaprintf -#define curlx_msprintf curl_msprintf -#define curlx_mprintf curl_mprintf -#define curlx_mfprintf curl_mfprintf -#define curlx_mvsprintf curl_mvsprintf -#define curlx_mvprintf curl_mvprintf -#define curlx_mvfprintf curl_mvfprintf - -#ifdef ENABLE_CURLX_PRINTF -/* If this define is set, we define all "standard" printf() functions to use - the curlx_* version instead. It makes the source code transparent and - easier to understand/patch. Undefine them first. */ -# undef printf -# undef fprintf -# undef sprintf -# undef msnprintf -# undef vprintf -# undef vfprintf -# undef vsprintf -# undef mvsnprintf -# undef aprintf -# undef vaprintf - -# define printf curlx_mprintf -# define fprintf curlx_mfprintf -# define sprintf curlx_msprintf -# define msnprintf curlx_msnprintf -# define vprintf curlx_mvprintf -# define vfprintf curlx_mvfprintf -# define mvsnprintf curlx_mvsnprintf -# define aprintf curlx_maprintf -# define vaprintf curlx_mvaprintf -#endif /* ENABLE_CURLX_PRINTF */ - -#endif /* HEADER_CURL_CURLX_H */ +#ifndef HEADER_CURL_CURLX_H
+#define HEADER_CURL_CURLX_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 <curl/mprintf.h>
+/* this is still a public header file that provides the curl_mprintf()
+ functions while they still are offered publicly. They will be made library-
+ private one day */
+
+#include "strcase.h"
+/* "strcase.h" provides the strcasecompare protos */
+
+#include "strtoofft.h"
+/* "strtoofft.h" provides this function: curlx_strtoofft(), returns a
+ curl_off_t number from a given string.
+*/
+
+#include "nonblock.h"
+/* "nonblock.h" provides curlx_nonblock() */
+
+#include "warnless.h"
+/* "warnless.h" provides functions:
+
+ curlx_ultous()
+ curlx_ultouc()
+ curlx_uztosi()
+*/
+
+#include "curl_multibyte.h"
+/* "curl_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"
+/* "version_win32.h" provides curlx_verify_windows_version() */
+
+/* Now setup curlx_ * names for the functions that are to become curlx_ and
+ be removed from a future libcurl official API:
+ curlx_getenv
+ curlx_mprintf (and its variations)
+ curlx_strcasecompare
+ curlx_strncasecompare
+
+*/
+
+#define curlx_getenv curl_getenv
+#define curlx_mvsnprintf curl_mvsnprintf
+#define curlx_msnprintf curl_msnprintf
+#define curlx_maprintf curl_maprintf
+#define curlx_mvaprintf curl_mvaprintf
+#define curlx_msprintf curl_msprintf
+#define curlx_mprintf curl_mprintf
+#define curlx_mfprintf curl_mfprintf
+#define curlx_mvsprintf curl_mvsprintf
+#define curlx_mvprintf curl_mvprintf
+#define curlx_mvfprintf curl_mvfprintf
+
+#ifdef ENABLE_CURLX_PRINTF
+/* If this define is set, we define all "standard" printf() functions to use
+ the curlx_* version instead. It makes the source code transparent and
+ easier to understand/patch. Undefine them first. */
+# undef printf
+# undef fprintf
+# undef sprintf
+# undef msnprintf
+# undef vprintf
+# undef vfprintf
+# undef vsprintf
+# undef mvsnprintf
+# undef aprintf
+# undef vaprintf
+
+# define printf curlx_mprintf
+# define fprintf curlx_mfprintf
+# define sprintf curlx_msprintf
+# define msnprintf curlx_msnprintf
+# define vprintf curlx_mvprintf
+# define vfprintf curlx_mvfprintf
+# define mvsnprintf curlx_mvsnprintf
+# define aprintf curlx_maprintf
+# define vaprintf curlx_mvaprintf
+#endif /* ENABLE_CURLX_PRINTF */
+
+#endif /* HEADER_CURL_CURLX_H */
diff --git a/libs/libcurl/src/dict.c b/libs/libcurl/src/dict.c index 6f7678f5ca..7a8d1fd1bd 100644 --- a/libs/libcurl/src/dict.c +++ b/libs/libcurl/src/dict.c @@ -1,322 +1,322 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_DICT - -#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 HAVE_NET_IF_H -#include <net/if.h> -#endif -#ifdef HAVE_SYS_IOCTL_H -#include <sys/ioctl.h> -#endif - -#ifdef HAVE_SYS_PARAM_H -#include <sys/param.h> -#endif - -#ifdef HAVE_SYS_SELECT_H -#include <sys/select.h> -#elif defined(HAVE_UNISTD_H) -#include <unistd.h> -#endif - -#include "urldata.h" -#include <curl/curl.h> -#include "transfer.h" -#include "sendf.h" -#include "escape.h" -#include "progress.h" -#include "dict.h" -#include "curl_printf.h" -#include "strcase.h" -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -/* - * Forward declarations. - */ - -static CURLcode dict_do(struct Curl_easy *data, bool *done); - -/* - * DICT protocol handler. - */ - -const struct Curl_handler Curl_handler_dict = { - "DICT", /* scheme */ - ZERO_NULL, /* setup_connection */ - dict_do, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_DICT, /* defport */ - CURLPROTO_DICT, /* protocol */ - CURLPROTO_DICT, /* family */ - PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */ -}; - -static char *unescape_word(const char *inputbuff) -{ - char *newp = NULL; - char *dictp; - size_t len; - - CURLcode result = Curl_urldecode(inputbuff, 0, &newp, &len, - REJECT_NADA); - if(!newp || result) - return NULL; - - dictp = malloc(len*2 + 1); /* add one for terminating zero */ - if(dictp) { - char *ptr; - char ch; - int olen = 0; - /* According to RFC2229 section 2.2, these letters need to be escaped with - \[letter] */ - for(ptr = newp; - (ch = *ptr) != 0; - ptr++) { - if((ch <= 32) || (ch == 127) || - (ch == '\'') || (ch == '\"') || (ch == '\\')) { - dictp[olen++] = '\\'; - } - dictp[olen++] = ch; - } - dictp[olen] = 0; - } - free(newp); - return dictp; -} - -/* sendf() sends formatted data to the server */ -static CURLcode sendf(curl_socket_t sockfd, struct Curl_easy *data, - const char *fmt, ...) -{ - ssize_t bytes_written; - size_t write_len; - CURLcode result = CURLE_OK; - char *s; - char *sptr; - va_list ap; - va_start(ap, fmt); - s = vaprintf(fmt, ap); /* returns an allocated string */ - va_end(ap); - if(!s) - return CURLE_OUT_OF_MEMORY; /* failure */ - - bytes_written = 0; - write_len = strlen(s); - sptr = s; - - for(;;) { - /* Write the buffer to the socket */ - result = Curl_write(data, sockfd, sptr, write_len, &bytes_written); - - if(result) - break; - - Curl_debug(data, CURLINFO_DATA_OUT, sptr, (size_t)bytes_written); - - if((size_t)bytes_written != write_len) { - /* if not all was written at once, we must advance the pointer, decrease - the size left and try again! */ - write_len -= bytes_written; - sptr += bytes_written; - } - else - break; - } - - free(s); /* free the output string */ - - return result; -} - -static CURLcode dict_do(struct Curl_easy *data, bool *done) -{ - char *word; - char *eword; - char *ppath; - char *database = NULL; - char *strategy = NULL; - char *nthdef = NULL; /* This is not part of the protocol, but required - by RFC 2229 */ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; - - char *path = data->state.up.path; - - *done = TRUE; /* unconditionally */ - - if(strncasecompare(path, DICT_MATCH, sizeof(DICT_MATCH)-1) || - strncasecompare(path, DICT_MATCH2, sizeof(DICT_MATCH2)-1) || - strncasecompare(path, DICT_MATCH3, sizeof(DICT_MATCH3)-1)) { - - word = strchr(path, ':'); - if(word) { - word++; - database = strchr(word, ':'); - if(database) { - *database++ = (char)0; - strategy = strchr(database, ':'); - if(strategy) { - *strategy++ = (char)0; - nthdef = strchr(strategy, ':'); - if(nthdef) { - *nthdef = (char)0; - } - } - } - } - - if(!word || (*word == (char)0)) { - infof(data, "lookup word is missing"); - word = (char *)"default"; - } - if(!database || (*database == (char)0)) { - database = (char *)"!"; - } - if(!strategy || (*strategy == (char)0)) { - strategy = (char *)"."; - } - - eword = unescape_word(word); - if(!eword) - return CURLE_OUT_OF_MEMORY; - - result = sendf(sockfd, data, - "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n" - "MATCH " - "%s " /* database */ - "%s " /* strategy */ - "%s\r\n" /* word */ - "QUIT\r\n", - database, - strategy, - eword); - - free(eword); - - if(result) { - failf(data, "Failed sending DICT request"); - return result; - } - Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); /* no upload */ - } - else if(strncasecompare(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) || - strncasecompare(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-1) || - strncasecompare(path, DICT_DEFINE3, sizeof(DICT_DEFINE3)-1)) { - - word = strchr(path, ':'); - if(word) { - word++; - database = strchr(word, ':'); - if(database) { - *database++ = (char)0; - nthdef = strchr(database, ':'); - if(nthdef) { - *nthdef = (char)0; - } - } - } - - if(!word || (*word == (char)0)) { - infof(data, "lookup word is missing"); - word = (char *)"default"; - } - if(!database || (*database == (char)0)) { - database = (char *)"!"; - } - - eword = unescape_word(word); - if(!eword) - return CURLE_OUT_OF_MEMORY; - - result = sendf(sockfd, data, - "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n" - "DEFINE " - "%s " /* database */ - "%s\r\n" /* word */ - "QUIT\r\n", - database, - eword); - - free(eword); - - if(result) { - failf(data, "Failed sending DICT request"); - return result; - } - Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); - } - else { - - ppath = strchr(path, '/'); - if(ppath) { - int i; - - ppath++; - for(i = 0; ppath[i]; i++) { - if(ppath[i] == ':') - ppath[i] = ' '; - } - result = sendf(sockfd, data, - "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n" - "%s\r\n" - "QUIT\r\n", ppath); - if(result) { - failf(data, "Failed sending DICT request"); - return result; - } - - Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); - } - } - - return CURLE_OK; -} -#endif /*CURL_DISABLE_DICT*/ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_DICT
+
+#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 HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#elif defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "transfer.h"
+#include "sendf.h"
+#include "escape.h"
+#include "progress.h"
+#include "dict.h"
+#include "curl_printf.h"
+#include "strcase.h"
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/*
+ * Forward declarations.
+ */
+
+static CURLcode dict_do(struct Curl_easy *data, bool *done);
+
+/*
+ * DICT protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_dict = {
+ "DICT", /* scheme */
+ ZERO_NULL, /* setup_connection */
+ dict_do, /* do_it */
+ ZERO_NULL, /* done */
+ ZERO_NULL, /* do_more */
+ ZERO_NULL, /* connect_it */
+ ZERO_NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ ZERO_NULL, /* proto_getsock */
+ ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ ZERO_NULL, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_DICT, /* defport */
+ CURLPROTO_DICT, /* protocol */
+ CURLPROTO_DICT, /* family */
+ PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */
+};
+
+static char *unescape_word(const char *inputbuff)
+{
+ char *newp = NULL;
+ char *dictp;
+ size_t len;
+
+ CURLcode result = Curl_urldecode(inputbuff, 0, &newp, &len,
+ REJECT_NADA);
+ if(!newp || result)
+ return NULL;
+
+ dictp = malloc(len*2 + 1); /* add one for terminating zero */
+ if(dictp) {
+ char *ptr;
+ char ch;
+ int olen = 0;
+ /* According to RFC2229 section 2.2, these letters need to be escaped with
+ \[letter] */
+ for(ptr = newp;
+ (ch = *ptr) != 0;
+ ptr++) {
+ if((ch <= 32) || (ch == 127) ||
+ (ch == '\'') || (ch == '\"') || (ch == '\\')) {
+ dictp[olen++] = '\\';
+ }
+ dictp[olen++] = ch;
+ }
+ dictp[olen] = 0;
+ }
+ free(newp);
+ return dictp;
+}
+
+/* sendf() sends formatted data to the server */
+static CURLcode sendf(curl_socket_t sockfd, struct Curl_easy *data,
+ const char *fmt, ...)
+{
+ ssize_t bytes_written;
+ size_t write_len;
+ CURLcode result = CURLE_OK;
+ char *s;
+ char *sptr;
+ va_list ap;
+ va_start(ap, fmt);
+ s = vaprintf(fmt, ap); /* returns an allocated string */
+ va_end(ap);
+ if(!s)
+ return CURLE_OUT_OF_MEMORY; /* failure */
+
+ bytes_written = 0;
+ write_len = strlen(s);
+ sptr = s;
+
+ for(;;) {
+ /* Write the buffer to the socket */
+ result = Curl_write(data, sockfd, sptr, write_len, &bytes_written);
+
+ if(result)
+ break;
+
+ Curl_debug(data, CURLINFO_DATA_OUT, sptr, (size_t)bytes_written);
+
+ if((size_t)bytes_written != write_len) {
+ /* if not all was written at once, we must advance the pointer, decrease
+ the size left and try again! */
+ write_len -= bytes_written;
+ sptr += bytes_written;
+ }
+ else
+ break;
+ }
+
+ free(s); /* free the output string */
+
+ return result;
+}
+
+static CURLcode dict_do(struct Curl_easy *data, bool *done)
+{
+ char *word;
+ char *eword;
+ char *ppath;
+ char *database = NULL;
+ char *strategy = NULL;
+ char *nthdef = NULL; /* This is not part of the protocol, but required
+ by RFC 2229 */
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+
+ char *path = data->state.up.path;
+
+ *done = TRUE; /* unconditionally */
+
+ if(strncasecompare(path, DICT_MATCH, sizeof(DICT_MATCH)-1) ||
+ strncasecompare(path, DICT_MATCH2, sizeof(DICT_MATCH2)-1) ||
+ strncasecompare(path, DICT_MATCH3, sizeof(DICT_MATCH3)-1)) {
+
+ word = strchr(path, ':');
+ if(word) {
+ word++;
+ database = strchr(word, ':');
+ if(database) {
+ *database++ = (char)0;
+ strategy = strchr(database, ':');
+ if(strategy) {
+ *strategy++ = (char)0;
+ nthdef = strchr(strategy, ':');
+ if(nthdef) {
+ *nthdef = (char)0;
+ }
+ }
+ }
+ }
+
+ if(!word || (*word == (char)0)) {
+ infof(data, "lookup word is missing");
+ word = (char *)"default";
+ }
+ if(!database || (*database == (char)0)) {
+ database = (char *)"!";
+ }
+ if(!strategy || (*strategy == (char)0)) {
+ strategy = (char *)".";
+ }
+
+ eword = unescape_word(word);
+ if(!eword)
+ return CURLE_OUT_OF_MEMORY;
+
+ result = sendf(sockfd, data,
+ "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
+ "MATCH "
+ "%s " /* database */
+ "%s " /* strategy */
+ "%s\r\n" /* word */
+ "QUIT\r\n",
+ database,
+ strategy,
+ eword);
+
+ free(eword);
+
+ if(result) {
+ failf(data, "Failed sending DICT request");
+ return result;
+ }
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); /* no upload */
+ }
+ else if(strncasecompare(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) ||
+ strncasecompare(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-1) ||
+ strncasecompare(path, DICT_DEFINE3, sizeof(DICT_DEFINE3)-1)) {
+
+ word = strchr(path, ':');
+ if(word) {
+ word++;
+ database = strchr(word, ':');
+ if(database) {
+ *database++ = (char)0;
+ nthdef = strchr(database, ':');
+ if(nthdef) {
+ *nthdef = (char)0;
+ }
+ }
+ }
+
+ if(!word || (*word == (char)0)) {
+ infof(data, "lookup word is missing");
+ word = (char *)"default";
+ }
+ if(!database || (*database == (char)0)) {
+ database = (char *)"!";
+ }
+
+ eword = unescape_word(word);
+ if(!eword)
+ return CURLE_OUT_OF_MEMORY;
+
+ result = sendf(sockfd, data,
+ "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
+ "DEFINE "
+ "%s " /* database */
+ "%s\r\n" /* word */
+ "QUIT\r\n",
+ database,
+ eword);
+
+ free(eword);
+
+ if(result) {
+ failf(data, "Failed sending DICT request");
+ return result;
+ }
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
+ }
+ else {
+
+ ppath = strchr(path, '/');
+ if(ppath) {
+ int i;
+
+ ppath++;
+ for(i = 0; ppath[i]; i++) {
+ if(ppath[i] == ':')
+ ppath[i] = ' ';
+ }
+ result = sendf(sockfd, data,
+ "CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
+ "%s\r\n"
+ "QUIT\r\n", ppath);
+ if(result) {
+ failf(data, "Failed sending DICT request");
+ return result;
+ }
+
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
+ }
+ }
+
+ return CURLE_OK;
+}
+#endif /* CURL_DISABLE_DICT */
diff --git a/libs/libcurl/src/dict.h b/libs/libcurl/src/dict.h index b283a0dfc1..ca45b505f7 100644 --- a/libs/libcurl/src/dict.h +++ b/libs/libcurl/src/dict.h @@ -1,31 +1,31 @@ -#ifndef HEADER_CURL_DICT_H -#define HEADER_CURL_DICT_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_DICT -extern const struct Curl_handler Curl_handler_dict; -#endif - -#endif /* HEADER_CURL_DICT_H */ +#ifndef HEADER_CURL_DICT_H
+#define HEADER_CURL_DICT_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_DICT
+extern const struct Curl_handler Curl_handler_dict;
+#endif
+
+#endif /* HEADER_CURL_DICT_H */
diff --git a/libs/libcurl/src/doh.c b/libs/libcurl/src/doh.c index 3b1d5d60ef..cb78de4818 100644 --- a/libs/libcurl/src/doh.c +++ b/libs/libcurl/src/doh.c @@ -1,982 +1,982 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2018 - 2022, 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_DOH - -#include "urldata.h" -#include "curl_addrinfo.h" -#include "doh.h" - -#include "sendf.h" -#include "multiif.h" -#include "url.h" -#include "share.h" -#include "curl_base64.h" -#include "connect.h" -#include "strdup.h" -#include "dynbuf.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#define DNS_CLASS_IN 0x01 - -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static const char * const errors[]={ - "", - "Bad label", - "Out of range", - "Label loop", - "Too small", - "Out of memory", - "RDATA length", - "Malformat", - "Bad RCODE", - "Unexpected TYPE", - "Unexpected CLASS", - "No content", - "Bad ID", - "Name too long" -}; - -static const char *doh_strerror(DOHcode code) -{ - if((code >= DOH_OK) && (code <= DOH_DNS_NAME_TOO_LONG)) - return errors[code]; - return "bad error code"; -} -#endif - -/* @unittest 1655 - */ -UNITTEST DOHcode doh_encode(const char *host, - DNStype dnstype, - unsigned char *dnsp, /* buffer */ - size_t len, /* buffer size */ - size_t *olen) /* output length */ -{ - const size_t hostlen = strlen(host); - unsigned char *orig = dnsp; - const char *hostp = host; - - /* The expected output length is 16 bytes more than the length of - * the QNAME-encoding of the host name. - * - * A valid DNS name may not contain a zero-length label, except at - * the end. For this reason, a name beginning with a dot, or - * containing a sequence of two or more consecutive dots, is invalid - * and cannot be encoded as a QNAME. - * - * If the host name ends with a trailing dot, the corresponding - * QNAME-encoding is one byte longer than the host name. If (as is - * also valid) the hostname is shortened by the omission of the - * trailing dot, then its QNAME-encoding will be two bytes longer - * than the host name. - * - * Each [ label, dot ] pair is encoded as [ length, label ], - * preserving overall length. A final [ label ] without a dot is - * also encoded as [ length, label ], increasing overall length - * by one. The encoding is completed by appending a zero byte, - * representing the zero-length root label, again increasing - * the overall length by one. - */ - - size_t expected_len; - DEBUGASSERT(hostlen); - expected_len = 12 + 1 + hostlen + 4; - if(host[hostlen-1]!='.') - expected_len++; - - if(expected_len > (256 + 16)) /* RFCs 1034, 1035 */ - return DOH_DNS_NAME_TOO_LONG; - - if(len < expected_len) - return DOH_TOO_SMALL_BUFFER; - - *dnsp++ = 0; /* 16 bit id */ - *dnsp++ = 0; - *dnsp++ = 0x01; /* |QR| Opcode |AA|TC|RD| Set the RD bit */ - *dnsp++ = '\0'; /* |RA| Z | RCODE | */ - *dnsp++ = '\0'; - *dnsp++ = 1; /* QDCOUNT (number of entries in the question section) */ - *dnsp++ = '\0'; - *dnsp++ = '\0'; /* ANCOUNT */ - *dnsp++ = '\0'; - *dnsp++ = '\0'; /* NSCOUNT */ - *dnsp++ = '\0'; - *dnsp++ = '\0'; /* ARCOUNT */ - - /* encode each label and store it in the QNAME */ - while(*hostp) { - size_t labellen; - char *dot = strchr(hostp, '.'); - if(dot) - labellen = dot - hostp; - else - labellen = strlen(hostp); - if((labellen > 63) || (!labellen)) { - /* label is too long or too short, error out */ - *olen = 0; - return DOH_DNS_BAD_LABEL; - } - /* label is non-empty, process it */ - *dnsp++ = (unsigned char)labellen; - memcpy(dnsp, hostp, labellen); - dnsp += labellen; - hostp += labellen; - /* advance past dot, but only if there is one */ - if(dot) - hostp++; - } /* next label */ - - *dnsp++ = 0; /* append zero-length label for root */ - - /* There are assigned TYPE codes beyond 255: use range [1..65535] */ - *dnsp++ = (unsigned char)(255 & (dnstype>>8)); /* upper 8 bit TYPE */ - *dnsp++ = (unsigned char)(255 & dnstype); /* lower 8 bit TYPE */ - - *dnsp++ = '\0'; /* upper 8 bit CLASS */ - *dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */ - - *olen = dnsp - orig; - - /* verify that our estimation of length is valid, since - * this has led to buffer overflows in this function */ - DEBUGASSERT(*olen == expected_len); - return DOH_OK; -} - -static size_t -doh_write_cb(const void *contents, size_t size, size_t nmemb, void *userp) -{ - size_t realsize = size * nmemb; - struct dynbuf *mem = (struct dynbuf *)userp; - - if(Curl_dyn_addn(mem, contents, realsize)) - return 0; - - return realsize; -} - -/* called from multi.c when this DoH transfer is complete */ -static int doh_done(struct Curl_easy *doh, CURLcode result) -{ - struct Curl_easy *data = doh->set.dohfor; - struct dohdata *dohp = data->req.doh; - /* so one of the DoH request done for the 'data' transfer is now complete! */ - dohp->pending--; - infof(data, "a DoH request is completed, %u to go", dohp->pending); - if(result) - infof(data, "DoH request %s", curl_easy_strerror(result)); - - if(!dohp->pending) { - /* DoH completed */ - curl_slist_free_all(dohp->headers); - dohp->headers = NULL; - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } - return 0; -} - -#define ERROR_CHECK_SETOPT(x,y) \ -do { \ - result = curl_easy_setopt(doh, x, y); \ - if(result && \ - result != CURLE_NOT_BUILT_IN && \ - result != CURLE_UNKNOWN_OPTION) \ - goto error; \ -} while(0) - -static CURLcode dohprobe(struct Curl_easy *data, - struct dnsprobe *p, DNStype dnstype, - const char *host, - const char *url, CURLM *multi, - struct curl_slist *headers) -{ - struct Curl_easy *doh = NULL; - char *nurl = NULL; - CURLcode result = CURLE_OK; - timediff_t timeout_ms; - DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer), - &p->dohlen); - if(d) { - failf(data, "Failed to encode DoH packet [%d]", d); - return CURLE_OUT_OF_MEMORY; - } - - p->dnstype = dnstype; - Curl_dyn_init(&p->serverdoh, DYN_DOH_RESPONSE); - - timeout_ms = Curl_timeleft(data, NULL, TRUE); - if(timeout_ms <= 0) { - result = CURLE_OPERATION_TIMEDOUT; - goto error; - } - /* Curl_open() is the internal version of curl_easy_init() */ - result = Curl_open(&doh); - if(!result) { - /* pass in the struct pointer via a local variable to please coverity and - the gcc typecheck helpers */ - struct dynbuf *resp = &p->serverdoh; - ERROR_CHECK_SETOPT(CURLOPT_URL, url); - ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https"); - ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb); - ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp); - ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer); - ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen); - ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers); -#ifdef USE_HTTP2 - ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); -#endif -#ifndef CURLDEBUG - /* enforce HTTPS if not debug */ - ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS); -#else - /* in debug mode, also allow http */ - ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS); -#endif - ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms); - ERROR_CHECK_SETOPT(CURLOPT_SHARE, data->share); - if(data->set.err && data->set.err != stderr) - ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err); - if(data->set.verbose) - ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L); - if(data->set.no_signal) - ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L); - - ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST, - data->set.doh_verifyhost ? 2L : 0L); - ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER, - data->set.doh_verifypeer ? 1L : 0L); - ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS, - data->set.doh_verifystatus ? 1L : 0L); - - /* Inherit *some* SSL options from the user's transfer. This is a - best-guess as to which options are needed for compatibility. #3661 - - Note DoH does not inherit the user's proxy server so proxy SSL settings - have no effect and are not inherited. If that changes then two new - options should be added to check doh proxy insecure separately, - CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER. - */ - if(data->set.ssl.falsestart) - ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L); - if(data->set.str[STRING_SSL_CAFILE]) { - ERROR_CHECK_SETOPT(CURLOPT_CAINFO, - data->set.str[STRING_SSL_CAFILE]); - } - if(data->set.blobs[BLOB_CAINFO]) { - ERROR_CHECK_SETOPT(CURLOPT_CAINFO_BLOB, - data->set.blobs[BLOB_CAINFO]); - } - if(data->set.str[STRING_SSL_CAPATH]) { - ERROR_CHECK_SETOPT(CURLOPT_CAPATH, - data->set.str[STRING_SSL_CAPATH]); - } - if(data->set.str[STRING_SSL_CRLFILE]) { - ERROR_CHECK_SETOPT(CURLOPT_CRLFILE, - data->set.str[STRING_SSL_CRLFILE]); - } - if(data->set.ssl.certinfo) - ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L); - if(data->set.ssl.fsslctx) - ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx); - if(data->set.ssl.fsslctxp) - ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp); - if(data->set.str[STRING_SSL_EC_CURVES]) { - ERROR_CHECK_SETOPT(CURLOPT_SSL_EC_CURVES, - data->set.str[STRING_SSL_EC_CURVES]); - } - - { - long mask = - (data->set.ssl.enable_beast ? - CURLSSLOPT_ALLOW_BEAST : 0) | - (data->set.ssl.no_revoke ? - CURLSSLOPT_NO_REVOKE : 0) | - (data->set.ssl.no_partialchain ? - CURLSSLOPT_NO_PARTIALCHAIN : 0) | - (data->set.ssl.revoke_best_effort ? - CURLSSLOPT_REVOKE_BEST_EFFORT : 0) | - (data->set.ssl.native_ca_store ? - CURLSSLOPT_NATIVE_CA : 0) | - (data->set.ssl.auto_client_cert ? - CURLSSLOPT_AUTO_CLIENT_CERT : 0); - - (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask); - } - - doh->set.fmultidone = doh_done; - doh->set.dohfor = data; /* identify for which transfer this is done */ - p->easy = doh; - - /* DoH private_data must be null because the user must have a way to - distinguish their transfer's handle from DoH handles in user - callbacks (ie SSL CTX callback). */ - DEBUGASSERT(!doh->set.private_data); - - if(curl_multi_add_handle(multi, doh)) - goto error; - } - else - goto error; - free(nurl); - return CURLE_OK; - - error: - free(nurl); - Curl_close(&doh); - return result; -} - -/* - * Curl_doh() resolves a name using DoH. It resolves a name and returns a - * 'Curl_addrinfo *' with the address information. - */ - -struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, - const char *hostname, - int port, - int *waitp) -{ - CURLcode result = CURLE_OK; - int slot; - struct dohdata *dohp; - struct connectdata *conn = data->conn; - *waitp = TRUE; /* this never returns synchronously */ - (void)hostname; - (void)port; - - DEBUGASSERT(!data->req.doh); - DEBUGASSERT(conn); - - /* start clean, consider allocating this struct on demand */ - dohp = data->req.doh = calloc(sizeof(struct dohdata), 1); - if(!dohp) - return NULL; - - conn->bits.doh = TRUE; - dohp->host = hostname; - dohp->port = port; - dohp->headers = - curl_slist_append(NULL, - "Content-Type: application/dns-message"); - if(!dohp->headers) - goto error; - - /* create IPv4 DoH request */ - result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V4], - DNS_TYPE_A, hostname, data->set.str[STRING_DOH], - data->multi, dohp->headers); - if(result) - goto error; - dohp->pending++; - - if((conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { - /* create IPv6 DoH request */ - result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6], - DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH], - data->multi, dohp->headers); - if(result) - goto error; - dohp->pending++; - } - return NULL; - - error: - curl_slist_free_all(dohp->headers); - data->req.doh->headers = NULL; - for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) { - Curl_close(&dohp->probe[slot].easy); - } - Curl_safefree(data->req.doh); - return NULL; -} - -static DOHcode skipqname(const unsigned char *doh, size_t dohlen, - unsigned int *indexp) -{ - unsigned char length; - do { - if(dohlen < (*indexp + 1)) - return DOH_DNS_OUT_OF_RANGE; - length = doh[*indexp]; - if((length & 0xc0) == 0xc0) { - /* name pointer, advance over it and be done */ - if(dohlen < (*indexp + 2)) - return DOH_DNS_OUT_OF_RANGE; - *indexp += 2; - break; - } - if(length & 0xc0) - return DOH_DNS_BAD_LABEL; - if(dohlen < (*indexp + 1 + length)) - return DOH_DNS_OUT_OF_RANGE; - *indexp += 1 + length; - } while(length); - return DOH_OK; -} - -static unsigned short get16bit(const unsigned char *doh, int index) -{ - return (unsigned short)((doh[index] << 8) | doh[index + 1]); -} - -static unsigned int get32bit(const unsigned char *doh, int index) -{ - /* make clang and gcc optimize this to bswap by incrementing - the pointer first. */ - doh += index; - - /* avoid undefined behavior by casting to unsigned before shifting - 24 bits, possibly into the sign bit. codegen is same, but - ub sanitizer won't be upset */ - return ( (unsigned)doh[0] << 24) | (doh[1] << 16) |(doh[2] << 8) | doh[3]; -} - -static DOHcode store_a(const unsigned char *doh, int index, struct dohentry *d) -{ - /* silently ignore addresses over the limit */ - if(d->numaddr < DOH_MAX_ADDR) { - struct dohaddr *a = &d->addr[d->numaddr]; - a->type = DNS_TYPE_A; - memcpy(&a->ip.v4, &doh[index], 4); - d->numaddr++; - } - return DOH_OK; -} - -static DOHcode store_aaaa(const unsigned char *doh, - int index, - struct dohentry *d) -{ - /* silently ignore addresses over the limit */ - if(d->numaddr < DOH_MAX_ADDR) { - struct dohaddr *a = &d->addr[d->numaddr]; - a->type = DNS_TYPE_AAAA; - memcpy(&a->ip.v6, &doh[index], 16); - d->numaddr++; - } - return DOH_OK; -} - -static DOHcode store_cname(const unsigned char *doh, - size_t dohlen, - unsigned int index, - struct dohentry *d) -{ - struct dynbuf *c; - unsigned int loop = 128; /* a valid DNS name can never loop this much */ - unsigned char length; - - if(d->numcname == DOH_MAX_CNAME) - return DOH_OK; /* skip! */ - - c = &d->cname[d->numcname++]; - do { - if(index >= dohlen) - return DOH_DNS_OUT_OF_RANGE; - length = doh[index]; - if((length & 0xc0) == 0xc0) { - int newpos; - /* name pointer, get the new offset (14 bits) */ - if((index + 1) >= dohlen) - return DOH_DNS_OUT_OF_RANGE; - - /* move to the new index */ - newpos = (length & 0x3f) << 8 | doh[index + 1]; - index = newpos; - continue; - } - else if(length & 0xc0) - return DOH_DNS_BAD_LABEL; /* bad input */ - else - index++; - - if(length) { - if(Curl_dyn_len(c)) { - if(Curl_dyn_addn(c, STRCONST("."))) - return DOH_OUT_OF_MEM; - } - if((index + length) > dohlen) - return DOH_DNS_BAD_LABEL; - - if(Curl_dyn_addn(c, &doh[index], length)) - return DOH_OUT_OF_MEM; - index += length; - } - } while(length && --loop); - - if(!loop) - return DOH_DNS_LABEL_LOOP; - return DOH_OK; -} - -static DOHcode rdata(const unsigned char *doh, - size_t dohlen, - unsigned short rdlength, - unsigned short type, - int index, - struct dohentry *d) -{ - /* RDATA - - A (TYPE 1): 4 bytes - - AAAA (TYPE 28): 16 bytes - - NS (TYPE 2): N bytes */ - DOHcode rc; - - switch(type) { - case DNS_TYPE_A: - if(rdlength != 4) - return DOH_DNS_RDATA_LEN; - rc = store_a(doh, index, d); - if(rc) - return rc; - break; - case DNS_TYPE_AAAA: - if(rdlength != 16) - return DOH_DNS_RDATA_LEN; - rc = store_aaaa(doh, index, d); - if(rc) - return rc; - break; - case DNS_TYPE_CNAME: - rc = store_cname(doh, dohlen, index, d); - if(rc) - return rc; - break; - case DNS_TYPE_DNAME: - /* explicit for clarity; just skip; rely on synthesized CNAME */ - break; - default: - /* unsupported type, just skip it */ - break; - } - return DOH_OK; -} - -UNITTEST void de_init(struct dohentry *de) -{ - int i; - memset(de, 0, sizeof(*de)); - de->ttl = INT_MAX; - for(i = 0; i < DOH_MAX_CNAME; i++) - Curl_dyn_init(&de->cname[i], DYN_DOH_CNAME); -} - - -UNITTEST DOHcode doh_decode(const unsigned char *doh, - size_t dohlen, - DNStype dnstype, - struct dohentry *d) -{ - unsigned char rcode; - unsigned short qdcount; - unsigned short ancount; - unsigned short type = 0; - unsigned short rdlength; - unsigned short nscount; - unsigned short arcount; - unsigned int index = 12; - DOHcode rc; - - if(dohlen < 12) - return DOH_TOO_SMALL_BUFFER; /* too small */ - if(!doh || doh[0] || doh[1]) - return DOH_DNS_BAD_ID; /* bad ID */ - rcode = doh[3] & 0x0f; - if(rcode) - return DOH_DNS_BAD_RCODE; /* bad rcode */ - - qdcount = get16bit(doh, 4); - while(qdcount) { - rc = skipqname(doh, dohlen, &index); - if(rc) - return rc; /* bad qname */ - if(dohlen < (index + 4)) - return DOH_DNS_OUT_OF_RANGE; - index += 4; /* skip question's type and class */ - qdcount--; - } - - ancount = get16bit(doh, 6); - while(ancount) { - unsigned short class; - unsigned int ttl; - - rc = skipqname(doh, dohlen, &index); - if(rc) - return rc; /* bad qname */ - - if(dohlen < (index + 2)) - return DOH_DNS_OUT_OF_RANGE; - - type = get16bit(doh, index); - if((type != DNS_TYPE_CNAME) /* may be synthesized from DNAME */ - && (type != DNS_TYPE_DNAME) /* if present, accept and ignore */ - && (type != dnstype)) - /* Not the same type as was asked for nor CNAME nor DNAME */ - return DOH_DNS_UNEXPECTED_TYPE; - index += 2; - - if(dohlen < (index + 2)) - return DOH_DNS_OUT_OF_RANGE; - class = get16bit(doh, index); - if(DNS_CLASS_IN != class) - return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */ - index += 2; - - if(dohlen < (index + 4)) - return DOH_DNS_OUT_OF_RANGE; - - ttl = get32bit(doh, index); - if(ttl < d->ttl) - d->ttl = ttl; - index += 4; - - if(dohlen < (index + 2)) - return DOH_DNS_OUT_OF_RANGE; - - rdlength = get16bit(doh, index); - index += 2; - if(dohlen < (index + rdlength)) - return DOH_DNS_OUT_OF_RANGE; - - rc = rdata(doh, dohlen, rdlength, type, index, d); - if(rc) - return rc; /* bad rdata */ - index += rdlength; - ancount--; - } - - nscount = get16bit(doh, 8); - while(nscount) { - rc = skipqname(doh, dohlen, &index); - if(rc) - return rc; /* bad qname */ - - if(dohlen < (index + 8)) - return DOH_DNS_OUT_OF_RANGE; - - index += 2 + 2 + 4; /* type, class and ttl */ - - if(dohlen < (index + 2)) - return DOH_DNS_OUT_OF_RANGE; - - rdlength = get16bit(doh, index); - index += 2; - if(dohlen < (index + rdlength)) - return DOH_DNS_OUT_OF_RANGE; - index += rdlength; - nscount--; - } - - arcount = get16bit(doh, 10); - while(arcount) { - rc = skipqname(doh, dohlen, &index); - if(rc) - return rc; /* bad qname */ - - if(dohlen < (index + 8)) - return DOH_DNS_OUT_OF_RANGE; - - index += 2 + 2 + 4; /* type, class and ttl */ - - if(dohlen < (index + 2)) - return DOH_DNS_OUT_OF_RANGE; - - rdlength = get16bit(doh, index); - index += 2; - if(dohlen < (index + rdlength)) - return DOH_DNS_OUT_OF_RANGE; - index += rdlength; - arcount--; - } - - if(index != dohlen) - return DOH_DNS_MALFORMAT; /* something is wrong */ - - if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr) - /* nothing stored! */ - return DOH_NO_CONTENT; - - return DOH_OK; /* ok */ -} - -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static void showdoh(struct Curl_easy *data, - const struct dohentry *d) -{ - int i; - infof(data, "TTL: %u seconds", d->ttl); - for(i = 0; i < d->numaddr; i++) { - const struct dohaddr *a = &d->addr[i]; - if(a->type == DNS_TYPE_A) { - infof(data, "DoH A: %u.%u.%u.%u", - a->ip.v4[0], a->ip.v4[1], - a->ip.v4[2], a->ip.v4[3]); - } - else if(a->type == DNS_TYPE_AAAA) { - int j; - char buffer[128]; - char *ptr; - size_t len; - msnprintf(buffer, 128, "DoH AAAA: "); - ptr = &buffer[10]; - len = 118; - for(j = 0; j < 16; j += 2) { - size_t l; - msnprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j], - d->addr[i].ip.v6[j + 1]); - l = strlen(ptr); - len -= l; - ptr += l; - } - infof(data, "%s", buffer); - } - } - for(i = 0; i < d->numcname; i++) { - infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i])); - } -} -#else -#define showdoh(x,y) -#endif - -/* - * doh2ai() - * - * This function returns a pointer to the first element of a newly allocated - * Curl_addrinfo struct linked list filled with the data from a set of DoH - * lookups. Curl_addrinfo is meant to work like the addrinfo struct does for - * a IPv6 stack, but usable also for IPv4, all hosts and environments. - * - * The memory allocated by this function *MUST* be free'd later on calling - * Curl_freeaddrinfo(). For each successful call to this function there - * must be an associated call later to Curl_freeaddrinfo(). - */ - -static struct Curl_addrinfo * -doh2ai(const struct dohentry *de, const char *hostname, int port) -{ - struct Curl_addrinfo *ai; - struct Curl_addrinfo *prevai = NULL; - struct Curl_addrinfo *firstai = NULL; - struct sockaddr_in *addr; -#ifdef ENABLE_IPV6 - struct sockaddr_in6 *addr6; -#endif - CURLcode result = CURLE_OK; - int i; - size_t hostlen = strlen(hostname) + 1; /* include null-terminator */ - - if(!de) - /* no input == no output! */ - return NULL; - - for(i = 0; i < de->numaddr; i++) { - size_t ss_size; - CURL_SA_FAMILY_T addrtype; - if(de->addr[i].type == DNS_TYPE_AAAA) { -#ifndef ENABLE_IPV6 - /* we can't handle IPv6 addresses */ - continue; -#else - ss_size = sizeof(struct sockaddr_in6); - addrtype = AF_INET6; -#endif - } - else { - ss_size = sizeof(struct sockaddr_in); - addrtype = AF_INET; - } - - ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen); - if(!ai) { - result = CURLE_OUT_OF_MEMORY; - break; - } - ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo)); - ai->ai_canonname = (void *)((char *)ai->ai_addr + ss_size); - memcpy(ai->ai_canonname, hostname, hostlen); - - if(!firstai) - /* store the pointer we want to return from this function */ - firstai = ai; - - if(prevai) - /* make the previous entry point to this */ - prevai->ai_next = ai; - - ai->ai_family = addrtype; - - /* we return all names as STREAM, so when using this address for TFTP - the type must be ignored and conn->socktype be used instead! */ - ai->ai_socktype = SOCK_STREAM; - - ai->ai_addrlen = (curl_socklen_t)ss_size; - - /* leave the rest of the struct filled with zero */ - - switch(ai->ai_family) { - case AF_INET: - addr = (void *)ai->ai_addr; /* storage area for this info */ - DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4)); - memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr)); - addr->sin_family = addrtype; - addr->sin_port = htons((unsigned short)port); - break; - -#ifdef ENABLE_IPV6 - case AF_INET6: - addr6 = (void *)ai->ai_addr; /* storage area for this info */ - DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6)); - memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr)); - addr6->sin6_family = addrtype; - addr6->sin6_port = htons((unsigned short)port); - break; -#endif - } - - prevai = ai; - } - - if(result) { - Curl_freeaddrinfo(firstai); - firstai = NULL; - } - - return firstai; -} - -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static const char *type2name(DNStype dnstype) -{ - return (dnstype == DNS_TYPE_A)?"A":"AAAA"; -} -#endif - -UNITTEST void de_cleanup(struct dohentry *d) -{ - int i = 0; - for(i = 0; i < d->numcname; i++) { - Curl_dyn_free(&d->cname[i]); - } -} - -CURLcode Curl_doh_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **dnsp) -{ - CURLcode result; - struct dohdata *dohp = data->req.doh; - *dnsp = NULL; /* defaults to no response */ - if(!dohp) - return CURLE_OUT_OF_MEMORY; - - if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy && - !dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) { - failf(data, "Could not DoH-resolve: %s", data->state.async.hostname); - return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY: - CURLE_COULDNT_RESOLVE_HOST; - } - else if(!dohp->pending) { - DOHcode rc[DOH_PROBE_SLOTS] = { - DOH_OK, DOH_OK - }; - struct dohentry de; - int slot; - /* remove DoH handles from multi handle and close them */ - for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) { - curl_multi_remove_handle(data->multi, dohp->probe[slot].easy); - Curl_close(&dohp->probe[slot].easy); - } - /* parse the responses, create the struct and return it! */ - de_init(&de); - for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) { - struct dnsprobe *p = &dohp->probe[slot]; - if(!p->dnstype) - continue; - rc[slot] = doh_decode(Curl_dyn_uptr(&p->serverdoh), - Curl_dyn_len(&p->serverdoh), - p->dnstype, - &de); - Curl_dyn_free(&p->serverdoh); - if(rc[slot]) { - infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]), - type2name(p->dnstype), dohp->host); - } - } /* next slot */ - - result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */ - if(!rc[DOH_PROBE_SLOT_IPADDR_V4] || !rc[DOH_PROBE_SLOT_IPADDR_V6]) { - /* we have an address, of one kind or other */ - struct Curl_dns_entry *dns; - struct Curl_addrinfo *ai; - - infof(data, "DoH Host name: %s", dohp->host); - showdoh(data, &de); - - ai = doh2ai(&de, dohp->host, dohp->port); - if(!ai) { - de_cleanup(&de); - return CURLE_OUT_OF_MEMORY; - } - - if(data->share) - Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); - - /* we got a response, store it in the cache */ - dns = Curl_cache_addr(data, ai, dohp->host, dohp->port); - - if(data->share) - Curl_share_unlock(data, CURL_LOCK_DATA_DNS); - - if(!dns) { - /* returned failure, bail out nicely */ - Curl_freeaddrinfo(ai); - } - else { - data->state.async.dns = dns; - *dnsp = dns; - result = CURLE_OK; /* address resolution OK */ - } - } /* address processing done */ - - /* Now process any build-specific attributes retrieved from DNS */ - - /* All done */ - de_cleanup(&de); - Curl_safefree(data->req.doh); - return result; - - } /* !dohp->pending */ - - /* else wait for pending DoH transactions to complete */ - return CURLE_OK; -} - -#endif /* CURL_DISABLE_DOH */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2018 - 2022, 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_DOH
+
+#include "urldata.h"
+#include "curl_addrinfo.h"
+#include "doh.h"
+
+#include "sendf.h"
+#include "multiif.h"
+#include "url.h"
+#include "share.h"
+#include "curl_base64.h"
+#include "connect.h"
+#include "strdup.h"
+#include "dynbuf.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define DNS_CLASS_IN 0x01
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static const char * const errors[]={
+ "",
+ "Bad label",
+ "Out of range",
+ "Label loop",
+ "Too small",
+ "Out of memory",
+ "RDATA length",
+ "Malformat",
+ "Bad RCODE",
+ "Unexpected TYPE",
+ "Unexpected CLASS",
+ "No content",
+ "Bad ID",
+ "Name too long"
+};
+
+static const char *doh_strerror(DOHcode code)
+{
+ if((code >= DOH_OK) && (code <= DOH_DNS_NAME_TOO_LONG))
+ return errors[code];
+ return "bad error code";
+}
+#endif
+
+/* @unittest 1655
+ */
+UNITTEST DOHcode doh_encode(const char *host,
+ DNStype dnstype,
+ unsigned char *dnsp, /* buffer */
+ size_t len, /* buffer size */
+ size_t *olen) /* output length */
+{
+ const size_t hostlen = strlen(host);
+ unsigned char *orig = dnsp;
+ const char *hostp = host;
+
+ /* The expected output length is 16 bytes more than the length of
+ * the QNAME-encoding of the host name.
+ *
+ * A valid DNS name may not contain a zero-length label, except at
+ * the end. For this reason, a name beginning with a dot, or
+ * containing a sequence of two or more consecutive dots, is invalid
+ * and cannot be encoded as a QNAME.
+ *
+ * If the host name ends with a trailing dot, the corresponding
+ * QNAME-encoding is one byte longer than the host name. If (as is
+ * also valid) the hostname is shortened by the omission of the
+ * trailing dot, then its QNAME-encoding will be two bytes longer
+ * than the host name.
+ *
+ * Each [ label, dot ] pair is encoded as [ length, label ],
+ * preserving overall length. A final [ label ] without a dot is
+ * also encoded as [ length, label ], increasing overall length
+ * by one. The encoding is completed by appending a zero byte,
+ * representing the zero-length root label, again increasing
+ * the overall length by one.
+ */
+
+ size_t expected_len;
+ DEBUGASSERT(hostlen);
+ expected_len = 12 + 1 + hostlen + 4;
+ if(host[hostlen-1]!='.')
+ expected_len++;
+
+ if(expected_len > (256 + 16)) /* RFCs 1034, 1035 */
+ return DOH_DNS_NAME_TOO_LONG;
+
+ if(len < expected_len)
+ return DOH_TOO_SMALL_BUFFER;
+
+ *dnsp++ = 0; /* 16 bit id */
+ *dnsp++ = 0;
+ *dnsp++ = 0x01; /* |QR| Opcode |AA|TC|RD| Set the RD bit */
+ *dnsp++ = '\0'; /* |RA| Z | RCODE | */
+ *dnsp++ = '\0';
+ *dnsp++ = 1; /* QDCOUNT (number of entries in the question section) */
+ *dnsp++ = '\0';
+ *dnsp++ = '\0'; /* ANCOUNT */
+ *dnsp++ = '\0';
+ *dnsp++ = '\0'; /* NSCOUNT */
+ *dnsp++ = '\0';
+ *dnsp++ = '\0'; /* ARCOUNT */
+
+ /* encode each label and store it in the QNAME */
+ while(*hostp) {
+ size_t labellen;
+ char *dot = strchr(hostp, '.');
+ if(dot)
+ labellen = dot - hostp;
+ else
+ labellen = strlen(hostp);
+ if((labellen > 63) || (!labellen)) {
+ /* label is too long or too short, error out */
+ *olen = 0;
+ return DOH_DNS_BAD_LABEL;
+ }
+ /* label is non-empty, process it */
+ *dnsp++ = (unsigned char)labellen;
+ memcpy(dnsp, hostp, labellen);
+ dnsp += labellen;
+ hostp += labellen;
+ /* advance past dot, but only if there is one */
+ if(dot)
+ hostp++;
+ } /* next label */
+
+ *dnsp++ = 0; /* append zero-length label for root */
+
+ /* There are assigned TYPE codes beyond 255: use range [1..65535] */
+ *dnsp++ = (unsigned char)(255 & (dnstype>>8)); /* upper 8 bit TYPE */
+ *dnsp++ = (unsigned char)(255 & dnstype); /* lower 8 bit TYPE */
+
+ *dnsp++ = '\0'; /* upper 8 bit CLASS */
+ *dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */
+
+ *olen = dnsp - orig;
+
+ /* verify that our estimation of length is valid, since
+ * this has led to buffer overflows in this function */
+ DEBUGASSERT(*olen == expected_len);
+ return DOH_OK;
+}
+
+static size_t
+doh_write_cb(const void *contents, size_t size, size_t nmemb, void *userp)
+{
+ size_t realsize = size * nmemb;
+ struct dynbuf *mem = (struct dynbuf *)userp;
+
+ if(Curl_dyn_addn(mem, contents, realsize))
+ return 0;
+
+ return realsize;
+}
+
+/* called from multi.c when this DoH transfer is complete */
+static int doh_done(struct Curl_easy *doh, CURLcode result)
+{
+ struct Curl_easy *data = doh->set.dohfor;
+ struct dohdata *dohp = data->req.doh;
+ /* so one of the DoH request done for the 'data' transfer is now complete! */
+ dohp->pending--;
+ infof(data, "a DoH request is completed, %u to go", dohp->pending);
+ if(result)
+ infof(data, "DoH request %s", curl_easy_strerror(result));
+
+ if(!dohp->pending) {
+ /* DoH completed */
+ curl_slist_free_all(dohp->headers);
+ dohp->headers = NULL;
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+ }
+ return 0;
+}
+
+#define ERROR_CHECK_SETOPT(x,y) \
+do { \
+ result = curl_easy_setopt(doh, x, y); \
+ if(result && \
+ result != CURLE_NOT_BUILT_IN && \
+ result != CURLE_UNKNOWN_OPTION) \
+ goto error; \
+} while(0)
+
+static CURLcode dohprobe(struct Curl_easy *data,
+ struct dnsprobe *p, DNStype dnstype,
+ const char *host,
+ const char *url, CURLM *multi,
+ struct curl_slist *headers)
+{
+ struct Curl_easy *doh = NULL;
+ char *nurl = NULL;
+ CURLcode result = CURLE_OK;
+ timediff_t timeout_ms;
+ DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer),
+ &p->dohlen);
+ if(d) {
+ failf(data, "Failed to encode DoH packet [%d]", d);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ p->dnstype = dnstype;
+ Curl_dyn_init(&p->serverdoh, DYN_DOH_RESPONSE);
+
+ timeout_ms = Curl_timeleft(data, NULL, TRUE);
+ if(timeout_ms <= 0) {
+ result = CURLE_OPERATION_TIMEDOUT;
+ goto error;
+ }
+ /* Curl_open() is the internal version of curl_easy_init() */
+ result = Curl_open(&doh);
+ if(!result) {
+ /* pass in the struct pointer via a local variable to please coverity and
+ the gcc typecheck helpers */
+ struct dynbuf *resp = &p->serverdoh;
+ ERROR_CHECK_SETOPT(CURLOPT_URL, url);
+ ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https");
+ ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
+ ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp);
+ ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer);
+ ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen);
+ ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
+#ifdef USE_HTTP2
+ ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
+#endif
+#ifndef CURLDEBUG
+ /* enforce HTTPS if not debug */
+ ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
+#else
+ /* in debug mode, also allow http */
+ ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
+#endif
+ ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
+ ERROR_CHECK_SETOPT(CURLOPT_SHARE, data->share);
+ if(data->set.err && data->set.err != stderr)
+ ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err);
+ if(data->set.verbose)
+ ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
+ if(data->set.no_signal)
+ ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
+
+ ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST,
+ data->set.doh_verifyhost ? 2L : 0L);
+ ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER,
+ data->set.doh_verifypeer ? 1L : 0L);
+ ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS,
+ data->set.doh_verifystatus ? 1L : 0L);
+
+ /* Inherit *some* SSL options from the user's transfer. This is a
+ best-guess as to which options are needed for compatibility. #3661
+
+ Note DoH does not inherit the user's proxy server so proxy SSL settings
+ have no effect and are not inherited. If that changes then two new
+ options should be added to check doh proxy insecure separately,
+ CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER.
+ */
+ if(data->set.ssl.falsestart)
+ ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L);
+ if(data->set.str[STRING_SSL_CAFILE]) {
+ ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
+ data->set.str[STRING_SSL_CAFILE]);
+ }
+ if(data->set.blobs[BLOB_CAINFO]) {
+ ERROR_CHECK_SETOPT(CURLOPT_CAINFO_BLOB,
+ data->set.blobs[BLOB_CAINFO]);
+ }
+ if(data->set.str[STRING_SSL_CAPATH]) {
+ ERROR_CHECK_SETOPT(CURLOPT_CAPATH,
+ data->set.str[STRING_SSL_CAPATH]);
+ }
+ if(data->set.str[STRING_SSL_CRLFILE]) {
+ ERROR_CHECK_SETOPT(CURLOPT_CRLFILE,
+ data->set.str[STRING_SSL_CRLFILE]);
+ }
+ if(data->set.ssl.certinfo)
+ ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L);
+ if(data->set.ssl.fsslctx)
+ ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx);
+ if(data->set.ssl.fsslctxp)
+ ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp);
+ if(data->set.str[STRING_SSL_EC_CURVES]) {
+ ERROR_CHECK_SETOPT(CURLOPT_SSL_EC_CURVES,
+ data->set.str[STRING_SSL_EC_CURVES]);
+ }
+
+ {
+ long mask =
+ (data->set.ssl.enable_beast ?
+ CURLSSLOPT_ALLOW_BEAST : 0) |
+ (data->set.ssl.no_revoke ?
+ CURLSSLOPT_NO_REVOKE : 0) |
+ (data->set.ssl.no_partialchain ?
+ CURLSSLOPT_NO_PARTIALCHAIN : 0) |
+ (data->set.ssl.revoke_best_effort ?
+ CURLSSLOPT_REVOKE_BEST_EFFORT : 0) |
+ (data->set.ssl.native_ca_store ?
+ CURLSSLOPT_NATIVE_CA : 0) |
+ (data->set.ssl.auto_client_cert ?
+ CURLSSLOPT_AUTO_CLIENT_CERT : 0);
+
+ (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask);
+ }
+
+ doh->set.fmultidone = doh_done;
+ doh->set.dohfor = data; /* identify for which transfer this is done */
+ p->easy = doh;
+
+ /* DoH private_data must be null because the user must have a way to
+ distinguish their transfer's handle from DoH handles in user
+ callbacks (ie SSL CTX callback). */
+ DEBUGASSERT(!doh->set.private_data);
+
+ if(curl_multi_add_handle(multi, doh))
+ goto error;
+ }
+ else
+ goto error;
+ free(nurl);
+ return CURLE_OK;
+
+ error:
+ free(nurl);
+ Curl_close(&doh);
+ return result;
+}
+
+/*
+ * Curl_doh() resolves a name using DoH. It resolves a name and returns a
+ * 'Curl_addrinfo *' with the address information.
+ */
+
+struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int *waitp)
+{
+ CURLcode result = CURLE_OK;
+ int slot;
+ struct dohdata *dohp;
+ struct connectdata *conn = data->conn;
+ *waitp = TRUE; /* this never returns synchronously */
+ (void)hostname;
+ (void)port;
+
+ DEBUGASSERT(!data->req.doh);
+ DEBUGASSERT(conn);
+
+ /* start clean, consider allocating this struct on demand */
+ dohp = data->req.doh = calloc(sizeof(struct dohdata), 1);
+ if(!dohp)
+ return NULL;
+
+ conn->bits.doh = TRUE;
+ dohp->host = hostname;
+ dohp->port = port;
+ dohp->headers =
+ curl_slist_append(NULL,
+ "Content-Type: application/dns-message");
+ if(!dohp->headers)
+ goto error;
+
+ /* create IPv4 DoH request */
+ result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V4],
+ DNS_TYPE_A, hostname, data->set.str[STRING_DOH],
+ data->multi, dohp->headers);
+ if(result)
+ goto error;
+ dohp->pending++;
+
+ if((conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
+ /* create IPv6 DoH request */
+ result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6],
+ DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
+ data->multi, dohp->headers);
+ if(result)
+ goto error;
+ dohp->pending++;
+ }
+ return NULL;
+
+ error:
+ curl_slist_free_all(dohp->headers);
+ data->req.doh->headers = NULL;
+ for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
+ Curl_close(&dohp->probe[slot].easy);
+ }
+ Curl_safefree(data->req.doh);
+ return NULL;
+}
+
+static DOHcode skipqname(const unsigned char *doh, size_t dohlen,
+ unsigned int *indexp)
+{
+ unsigned char length;
+ do {
+ if(dohlen < (*indexp + 1))
+ return DOH_DNS_OUT_OF_RANGE;
+ length = doh[*indexp];
+ if((length & 0xc0) == 0xc0) {
+ /* name pointer, advance over it and be done */
+ if(dohlen < (*indexp + 2))
+ return DOH_DNS_OUT_OF_RANGE;
+ *indexp += 2;
+ break;
+ }
+ if(length & 0xc0)
+ return DOH_DNS_BAD_LABEL;
+ if(dohlen < (*indexp + 1 + length))
+ return DOH_DNS_OUT_OF_RANGE;
+ *indexp += 1 + length;
+ } while(length);
+ return DOH_OK;
+}
+
+static unsigned short get16bit(const unsigned char *doh, int index)
+{
+ return (unsigned short)((doh[index] << 8) | doh[index + 1]);
+}
+
+static unsigned int get32bit(const unsigned char *doh, int index)
+{
+ /* make clang and gcc optimize this to bswap by incrementing
+ the pointer first. */
+ doh += index;
+
+ /* avoid undefined behavior by casting to unsigned before shifting
+ 24 bits, possibly into the sign bit. codegen is same, but
+ ub sanitizer won't be upset */
+ return ( (unsigned)doh[0] << 24) | (doh[1] << 16) |(doh[2] << 8) | doh[3];
+}
+
+static DOHcode store_a(const unsigned char *doh, int index, struct dohentry *d)
+{
+ /* silently ignore addresses over the limit */
+ if(d->numaddr < DOH_MAX_ADDR) {
+ struct dohaddr *a = &d->addr[d->numaddr];
+ a->type = DNS_TYPE_A;
+ memcpy(&a->ip.v4, &doh[index], 4);
+ d->numaddr++;
+ }
+ return DOH_OK;
+}
+
+static DOHcode store_aaaa(const unsigned char *doh,
+ int index,
+ struct dohentry *d)
+{
+ /* silently ignore addresses over the limit */
+ if(d->numaddr < DOH_MAX_ADDR) {
+ struct dohaddr *a = &d->addr[d->numaddr];
+ a->type = DNS_TYPE_AAAA;
+ memcpy(&a->ip.v6, &doh[index], 16);
+ d->numaddr++;
+ }
+ return DOH_OK;
+}
+
+static DOHcode store_cname(const unsigned char *doh,
+ size_t dohlen,
+ unsigned int index,
+ struct dohentry *d)
+{
+ struct dynbuf *c;
+ unsigned int loop = 128; /* a valid DNS name can never loop this much */
+ unsigned char length;
+
+ if(d->numcname == DOH_MAX_CNAME)
+ return DOH_OK; /* skip! */
+
+ c = &d->cname[d->numcname++];
+ do {
+ if(index >= dohlen)
+ return DOH_DNS_OUT_OF_RANGE;
+ length = doh[index];
+ if((length & 0xc0) == 0xc0) {
+ int newpos;
+ /* name pointer, get the new offset (14 bits) */
+ if((index + 1) >= dohlen)
+ return DOH_DNS_OUT_OF_RANGE;
+
+ /* move to the new index */
+ newpos = (length & 0x3f) << 8 | doh[index + 1];
+ index = newpos;
+ continue;
+ }
+ else if(length & 0xc0)
+ return DOH_DNS_BAD_LABEL; /* bad input */
+ else
+ index++;
+
+ if(length) {
+ if(Curl_dyn_len(c)) {
+ if(Curl_dyn_addn(c, STRCONST(".")))
+ return DOH_OUT_OF_MEM;
+ }
+ if((index + length) > dohlen)
+ return DOH_DNS_BAD_LABEL;
+
+ if(Curl_dyn_addn(c, &doh[index], length))
+ return DOH_OUT_OF_MEM;
+ index += length;
+ }
+ } while(length && --loop);
+
+ if(!loop)
+ return DOH_DNS_LABEL_LOOP;
+ return DOH_OK;
+}
+
+static DOHcode rdata(const unsigned char *doh,
+ size_t dohlen,
+ unsigned short rdlength,
+ unsigned short type,
+ int index,
+ struct dohentry *d)
+{
+ /* RDATA
+ - A (TYPE 1): 4 bytes
+ - AAAA (TYPE 28): 16 bytes
+ - NS (TYPE 2): N bytes */
+ DOHcode rc;
+
+ switch(type) {
+ case DNS_TYPE_A:
+ if(rdlength != 4)
+ return DOH_DNS_RDATA_LEN;
+ rc = store_a(doh, index, d);
+ if(rc)
+ return rc;
+ break;
+ case DNS_TYPE_AAAA:
+ if(rdlength != 16)
+ return DOH_DNS_RDATA_LEN;
+ rc = store_aaaa(doh, index, d);
+ if(rc)
+ return rc;
+ break;
+ case DNS_TYPE_CNAME:
+ rc = store_cname(doh, dohlen, index, d);
+ if(rc)
+ return rc;
+ break;
+ case DNS_TYPE_DNAME:
+ /* explicit for clarity; just skip; rely on synthesized CNAME */
+ break;
+ default:
+ /* unsupported type, just skip it */
+ break;
+ }
+ return DOH_OK;
+}
+
+UNITTEST void de_init(struct dohentry *de)
+{
+ int i;
+ memset(de, 0, sizeof(*de));
+ de->ttl = INT_MAX;
+ for(i = 0; i < DOH_MAX_CNAME; i++)
+ Curl_dyn_init(&de->cname[i], DYN_DOH_CNAME);
+}
+
+
+UNITTEST DOHcode doh_decode(const unsigned char *doh,
+ size_t dohlen,
+ DNStype dnstype,
+ struct dohentry *d)
+{
+ unsigned char rcode;
+ unsigned short qdcount;
+ unsigned short ancount;
+ unsigned short type = 0;
+ unsigned short rdlength;
+ unsigned short nscount;
+ unsigned short arcount;
+ unsigned int index = 12;
+ DOHcode rc;
+
+ if(dohlen < 12)
+ return DOH_TOO_SMALL_BUFFER; /* too small */
+ if(!doh || doh[0] || doh[1])
+ return DOH_DNS_BAD_ID; /* bad ID */
+ rcode = doh[3] & 0x0f;
+ if(rcode)
+ return DOH_DNS_BAD_RCODE; /* bad rcode */
+
+ qdcount = get16bit(doh, 4);
+ while(qdcount) {
+ rc = skipqname(doh, dohlen, &index);
+ if(rc)
+ return rc; /* bad qname */
+ if(dohlen < (index + 4))
+ return DOH_DNS_OUT_OF_RANGE;
+ index += 4; /* skip question's type and class */
+ qdcount--;
+ }
+
+ ancount = get16bit(doh, 6);
+ while(ancount) {
+ unsigned short class;
+ unsigned int ttl;
+
+ rc = skipqname(doh, dohlen, &index);
+ if(rc)
+ return rc; /* bad qname */
+
+ if(dohlen < (index + 2))
+ return DOH_DNS_OUT_OF_RANGE;
+
+ type = get16bit(doh, index);
+ if((type != DNS_TYPE_CNAME) /* may be synthesized from DNAME */
+ && (type != DNS_TYPE_DNAME) /* if present, accept and ignore */
+ && (type != dnstype))
+ /* Not the same type as was asked for nor CNAME nor DNAME */
+ return DOH_DNS_UNEXPECTED_TYPE;
+ index += 2;
+
+ if(dohlen < (index + 2))
+ return DOH_DNS_OUT_OF_RANGE;
+ class = get16bit(doh, index);
+ if(DNS_CLASS_IN != class)
+ return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */
+ index += 2;
+
+ if(dohlen < (index + 4))
+ return DOH_DNS_OUT_OF_RANGE;
+
+ ttl = get32bit(doh, index);
+ if(ttl < d->ttl)
+ d->ttl = ttl;
+ index += 4;
+
+ if(dohlen < (index + 2))
+ return DOH_DNS_OUT_OF_RANGE;
+
+ rdlength = get16bit(doh, index);
+ index += 2;
+ if(dohlen < (index + rdlength))
+ return DOH_DNS_OUT_OF_RANGE;
+
+ rc = rdata(doh, dohlen, rdlength, type, index, d);
+ if(rc)
+ return rc; /* bad rdata */
+ index += rdlength;
+ ancount--;
+ }
+
+ nscount = get16bit(doh, 8);
+ while(nscount) {
+ rc = skipqname(doh, dohlen, &index);
+ if(rc)
+ return rc; /* bad qname */
+
+ if(dohlen < (index + 8))
+ return DOH_DNS_OUT_OF_RANGE;
+
+ index += 2 + 2 + 4; /* type, class and ttl */
+
+ if(dohlen < (index + 2))
+ return DOH_DNS_OUT_OF_RANGE;
+
+ rdlength = get16bit(doh, index);
+ index += 2;
+ if(dohlen < (index + rdlength))
+ return DOH_DNS_OUT_OF_RANGE;
+ index += rdlength;
+ nscount--;
+ }
+
+ arcount = get16bit(doh, 10);
+ while(arcount) {
+ rc = skipqname(doh, dohlen, &index);
+ if(rc)
+ return rc; /* bad qname */
+
+ if(dohlen < (index + 8))
+ return DOH_DNS_OUT_OF_RANGE;
+
+ index += 2 + 2 + 4; /* type, class and ttl */
+
+ if(dohlen < (index + 2))
+ return DOH_DNS_OUT_OF_RANGE;
+
+ rdlength = get16bit(doh, index);
+ index += 2;
+ if(dohlen < (index + rdlength))
+ return DOH_DNS_OUT_OF_RANGE;
+ index += rdlength;
+ arcount--;
+ }
+
+ if(index != dohlen)
+ return DOH_DNS_MALFORMAT; /* something is wrong */
+
+ if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
+ /* nothing stored! */
+ return DOH_NO_CONTENT;
+
+ return DOH_OK; /* ok */
+}
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static void showdoh(struct Curl_easy *data,
+ const struct dohentry *d)
+{
+ int i;
+ infof(data, "TTL: %u seconds", d->ttl);
+ for(i = 0; i < d->numaddr; i++) {
+ const struct dohaddr *a = &d->addr[i];
+ if(a->type == DNS_TYPE_A) {
+ infof(data, "DoH A: %u.%u.%u.%u",
+ a->ip.v4[0], a->ip.v4[1],
+ a->ip.v4[2], a->ip.v4[3]);
+ }
+ else if(a->type == DNS_TYPE_AAAA) {
+ int j;
+ char buffer[128];
+ char *ptr;
+ size_t len;
+ msnprintf(buffer, 128, "DoH AAAA: ");
+ ptr = &buffer[10];
+ len = 118;
+ for(j = 0; j < 16; j += 2) {
+ size_t l;
+ msnprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j],
+ d->addr[i].ip.v6[j + 1]);
+ l = strlen(ptr);
+ len -= l;
+ ptr += l;
+ }
+ infof(data, "%s", buffer);
+ }
+ }
+ for(i = 0; i < d->numcname; i++) {
+ infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i]));
+ }
+}
+#else
+#define showdoh(x,y)
+#endif
+
+/*
+ * doh2ai()
+ *
+ * This function returns a pointer to the first element of a newly allocated
+ * Curl_addrinfo struct linked list filled with the data from a set of DoH
+ * lookups. Curl_addrinfo is meant to work like the addrinfo struct does for
+ * a IPv6 stack, but usable also for IPv4, all hosts and environments.
+ *
+ * The memory allocated by this function *MUST* be free'd later on calling
+ * Curl_freeaddrinfo(). For each successful call to this function there
+ * must be an associated call later to Curl_freeaddrinfo().
+ */
+
+static struct Curl_addrinfo *
+doh2ai(const struct dohentry *de, const char *hostname, int port)
+{
+ struct Curl_addrinfo *ai;
+ struct Curl_addrinfo *prevai = NULL;
+ struct Curl_addrinfo *firstai = NULL;
+ struct sockaddr_in *addr;
+#ifdef ENABLE_IPV6
+ struct sockaddr_in6 *addr6;
+#endif
+ CURLcode result = CURLE_OK;
+ int i;
+ size_t hostlen = strlen(hostname) + 1; /* include null-terminator */
+
+ if(!de)
+ /* no input == no output! */
+ return NULL;
+
+ for(i = 0; i < de->numaddr; i++) {
+ size_t ss_size;
+ CURL_SA_FAMILY_T addrtype;
+ if(de->addr[i].type == DNS_TYPE_AAAA) {
+#ifndef ENABLE_IPV6
+ /* we can't handle IPv6 addresses */
+ continue;
+#else
+ ss_size = sizeof(struct sockaddr_in6);
+ addrtype = AF_INET6;
+#endif
+ }
+ else {
+ ss_size = sizeof(struct sockaddr_in);
+ addrtype = AF_INET;
+ }
+
+ ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen);
+ if(!ai) {
+ result = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+ ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo));
+ ai->ai_canonname = (void *)((char *)ai->ai_addr + ss_size);
+ memcpy(ai->ai_canonname, hostname, hostlen);
+
+ if(!firstai)
+ /* store the pointer we want to return from this function */
+ firstai = ai;
+
+ if(prevai)
+ /* make the previous entry point to this */
+ prevai->ai_next = ai;
+
+ ai->ai_family = addrtype;
+
+ /* we return all names as STREAM, so when using this address for TFTP
+ the type must be ignored and conn->socktype be used instead! */
+ ai->ai_socktype = SOCK_STREAM;
+
+ ai->ai_addrlen = (curl_socklen_t)ss_size;
+
+ /* leave the rest of the struct filled with zero */
+
+ switch(ai->ai_family) {
+ case AF_INET:
+ addr = (void *)ai->ai_addr; /* storage area for this info */
+ DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4));
+ memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr));
+ addr->sin_family = addrtype;
+ addr->sin_port = htons((unsigned short)port);
+ break;
+
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ addr6 = (void *)ai->ai_addr; /* storage area for this info */
+ DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6));
+ memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr));
+ addr6->sin6_family = addrtype;
+ addr6->sin6_port = htons((unsigned short)port);
+ break;
+#endif
+ }
+
+ prevai = ai;
+ }
+
+ if(result) {
+ Curl_freeaddrinfo(firstai);
+ firstai = NULL;
+ }
+
+ return firstai;
+}
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static const char *type2name(DNStype dnstype)
+{
+ return (dnstype == DNS_TYPE_A)?"A":"AAAA";
+}
+#endif
+
+UNITTEST void de_cleanup(struct dohentry *d)
+{
+ int i = 0;
+ for(i = 0; i < d->numcname; i++) {
+ Curl_dyn_free(&d->cname[i]);
+ }
+}
+
+CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
+ struct Curl_dns_entry **dnsp)
+{
+ CURLcode result;
+ struct dohdata *dohp = data->req.doh;
+ *dnsp = NULL; /* defaults to no response */
+ if(!dohp)
+ return CURLE_OUT_OF_MEMORY;
+
+ if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy &&
+ !dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) {
+ failf(data, "Could not DoH-resolve: %s", data->state.async.hostname);
+ return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY:
+ CURLE_COULDNT_RESOLVE_HOST;
+ }
+ else if(!dohp->pending) {
+ DOHcode rc[DOH_PROBE_SLOTS] = {
+ DOH_OK, DOH_OK
+ };
+ struct dohentry de;
+ int slot;
+ /* remove DoH handles from multi handle and close them */
+ for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
+ curl_multi_remove_handle(data->multi, dohp->probe[slot].easy);
+ Curl_close(&dohp->probe[slot].easy);
+ }
+ /* parse the responses, create the struct and return it! */
+ de_init(&de);
+ for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
+ struct dnsprobe *p = &dohp->probe[slot];
+ if(!p->dnstype)
+ continue;
+ rc[slot] = doh_decode(Curl_dyn_uptr(&p->serverdoh),
+ Curl_dyn_len(&p->serverdoh),
+ p->dnstype,
+ &de);
+ Curl_dyn_free(&p->serverdoh);
+ if(rc[slot]) {
+ infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
+ type2name(p->dnstype), dohp->host);
+ }
+ } /* next slot */
+
+ result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
+ if(!rc[DOH_PROBE_SLOT_IPADDR_V4] || !rc[DOH_PROBE_SLOT_IPADDR_V6]) {
+ /* we have an address, of one kind or other */
+ struct Curl_dns_entry *dns;
+ struct Curl_addrinfo *ai;
+
+ infof(data, "DoH Host name: %s", dohp->host);
+ showdoh(data, &de);
+
+ ai = doh2ai(&de, dohp->host, dohp->port);
+ if(!ai) {
+ de_cleanup(&de);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+ /* we got a response, store it in the cache */
+ dns = Curl_cache_addr(data, ai, dohp->host, dohp->port);
+
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+
+ if(!dns) {
+ /* returned failure, bail out nicely */
+ Curl_freeaddrinfo(ai);
+ }
+ else {
+ data->state.async.dns = dns;
+ *dnsp = dns;
+ result = CURLE_OK; /* address resolution OK */
+ }
+ } /* address processing done */
+
+ /* Now process any build-specific attributes retrieved from DNS */
+
+ /* All done */
+ de_cleanup(&de);
+ Curl_safefree(data->req.doh);
+ return result;
+
+ } /* !dohp->pending */
+
+ /* else wait for pending DoH transactions to complete */
+ return CURLE_OK;
+}
+
+#endif /* CURL_DISABLE_DOH */
diff --git a/libs/libcurl/src/doh.h b/libs/libcurl/src/doh.h index 678e807fe4..136766c469 100644 --- a/libs/libcurl/src/doh.h +++ b/libs/libcurl/src/doh.h @@ -1,128 +1,128 @@ -#ifndef HEADER_CURL_DOH_H -#define HEADER_CURL_DOH_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2018 - 2022, 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 "urldata.h" -#include "curl_addrinfo.h" - -#ifndef CURL_DISABLE_DOH - -typedef enum { - DOH_OK, - DOH_DNS_BAD_LABEL, /* 1 */ - DOH_DNS_OUT_OF_RANGE, /* 2 */ - DOH_DNS_LABEL_LOOP, /* 3 */ - DOH_TOO_SMALL_BUFFER, /* 4 */ - DOH_OUT_OF_MEM, /* 5 */ - DOH_DNS_RDATA_LEN, /* 6 */ - DOH_DNS_MALFORMAT, /* 7 */ - DOH_DNS_BAD_RCODE, /* 8 - no such name */ - DOH_DNS_UNEXPECTED_TYPE, /* 9 */ - DOH_DNS_UNEXPECTED_CLASS, /* 10 */ - DOH_NO_CONTENT, /* 11 */ - DOH_DNS_BAD_ID, /* 12 */ - DOH_DNS_NAME_TOO_LONG /* 13 */ -} DOHcode; - -typedef enum { - DNS_TYPE_A = 1, - DNS_TYPE_NS = 2, - DNS_TYPE_CNAME = 5, - DNS_TYPE_AAAA = 28, - DNS_TYPE_DNAME = 39 /* RFC6672 */ -} DNStype; - -/* one of these for each DoH request */ -struct dnsprobe { - CURL *easy; - DNStype dnstype; - unsigned char dohbuffer[512]; - size_t dohlen; - struct dynbuf serverdoh; -}; - -struct dohdata { - struct curl_slist *headers; - struct dnsprobe probe[DOH_PROBE_SLOTS]; - unsigned int pending; /* still outstanding requests */ - int port; - const char *host; -}; - -/* - * Curl_doh() resolve a name using DoH (DNS-over-HTTPS). It resolves a name - * and returns a 'Curl_addrinfo *' with the address information. - */ - -struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, - const char *hostname, - int port, - int *waitp); - -CURLcode Curl_doh_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **dns); - -int Curl_doh_getsock(struct connectdata *conn, curl_socket_t *socks); - -#define DOH_MAX_ADDR 24 -#define DOH_MAX_CNAME 4 - -struct dohaddr { - int type; - union { - unsigned char v4[4]; /* network byte order */ - unsigned char v6[16]; - } ip; -}; - -struct dohentry { - struct dynbuf cname[DOH_MAX_CNAME]; - struct dohaddr addr[DOH_MAX_ADDR]; - int numaddr; - unsigned int ttl; - int numcname; -}; - - -#ifdef DEBUGBUILD -DOHcode doh_encode(const char *host, - DNStype dnstype, - unsigned char *dnsp, /* buffer */ - size_t len, /* buffer size */ - size_t *olen); /* output length */ -DOHcode doh_decode(const unsigned char *doh, - size_t dohlen, - DNStype dnstype, - struct dohentry *d); -void de_init(struct dohentry *d); -void de_cleanup(struct dohentry *d); -#endif - -#else /* if DoH is disabled */ -#define Curl_doh(a,b,c,d) NULL -#define Curl_doh_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST -#endif - -#endif /* HEADER_CURL_DOH_H */ +#ifndef HEADER_CURL_DOH_H
+#define HEADER_CURL_DOH_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2018 - 2022, 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 "urldata.h"
+#include "curl_addrinfo.h"
+
+#ifndef CURL_DISABLE_DOH
+
+typedef enum {
+ DOH_OK,
+ DOH_DNS_BAD_LABEL, /* 1 */
+ DOH_DNS_OUT_OF_RANGE, /* 2 */
+ DOH_DNS_LABEL_LOOP, /* 3 */
+ DOH_TOO_SMALL_BUFFER, /* 4 */
+ DOH_OUT_OF_MEM, /* 5 */
+ DOH_DNS_RDATA_LEN, /* 6 */
+ DOH_DNS_MALFORMAT, /* 7 */
+ DOH_DNS_BAD_RCODE, /* 8 - no such name */
+ DOH_DNS_UNEXPECTED_TYPE, /* 9 */
+ DOH_DNS_UNEXPECTED_CLASS, /* 10 */
+ DOH_NO_CONTENT, /* 11 */
+ DOH_DNS_BAD_ID, /* 12 */
+ DOH_DNS_NAME_TOO_LONG /* 13 */
+} DOHcode;
+
+typedef enum {
+ DNS_TYPE_A = 1,
+ DNS_TYPE_NS = 2,
+ DNS_TYPE_CNAME = 5,
+ DNS_TYPE_AAAA = 28,
+ DNS_TYPE_DNAME = 39 /* RFC6672 */
+} DNStype;
+
+/* one of these for each DoH request */
+struct dnsprobe {
+ CURL *easy;
+ DNStype dnstype;
+ unsigned char dohbuffer[512];
+ size_t dohlen;
+ struct dynbuf serverdoh;
+};
+
+struct dohdata {
+ struct curl_slist *headers;
+ struct dnsprobe probe[DOH_PROBE_SLOTS];
+ unsigned int pending; /* still outstanding requests */
+ int port;
+ const char *host;
+};
+
+/*
+ * Curl_doh() resolve a name using DoH (DNS-over-HTTPS). It resolves a name
+ * and returns a 'Curl_addrinfo *' with the address information.
+ */
+
+struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int *waitp);
+
+CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
+ struct Curl_dns_entry **dns);
+
+int Curl_doh_getsock(struct connectdata *conn, curl_socket_t *socks);
+
+#define DOH_MAX_ADDR 24
+#define DOH_MAX_CNAME 4
+
+struct dohaddr {
+ int type;
+ union {
+ unsigned char v4[4]; /* network byte order */
+ unsigned char v6[16];
+ } ip;
+};
+
+struct dohentry {
+ struct dynbuf cname[DOH_MAX_CNAME];
+ struct dohaddr addr[DOH_MAX_ADDR];
+ int numaddr;
+ unsigned int ttl;
+ int numcname;
+};
+
+
+#ifdef DEBUGBUILD
+DOHcode doh_encode(const char *host,
+ DNStype dnstype,
+ unsigned char *dnsp, /* buffer */
+ size_t len, /* buffer size */
+ size_t *olen); /* output length */
+DOHcode doh_decode(const unsigned char *doh,
+ size_t dohlen,
+ DNStype dnstype,
+ struct dohentry *d);
+void de_init(struct dohentry *d);
+void de_cleanup(struct dohentry *d);
+#endif
+
+#else /* if DoH is disabled */
+#define Curl_doh(a,b,c,d) NULL
+#define Curl_doh_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST
+#endif
+
+#endif /* HEADER_CURL_DOH_H */
diff --git a/libs/libcurl/src/dynbuf.c b/libs/libcurl/src/dynbuf.c index 0b1cf9afd8..9678478f06 100644 --- a/libs/libcurl/src/dynbuf.c +++ b/libs/libcurl/src/dynbuf.c @@ -1,270 +1,270 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2020 - 2022, 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 - -#define DYNINIT 0xbee51da /* random pattern */ - -/* - * Init a dynbuf struct. - */ -void Curl_dyn_init(struct dynbuf *s, size_t toobig) -{ - DEBUGASSERT(s); - DEBUGASSERT(toobig); - 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 doesn't touch the - * 'init' field and thus this buffer can be reused to add data to again. - */ -void Curl_dyn_free(struct dynbuf *s) -{ - DEBUGASSERT(s); - 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 indx = s->leng; - size_t a = s->allc; - size_t fit = len + indx + 1; /* new string + old string + zero byte */ - - /* try to detect if there's rubbish in the struct */ - DEBUGASSERT(s->init == DYNINIT); - DEBUGASSERT(s->toobig); - DEBUGASSERT(indx < s->toobig); - DEBUGASSERT(!s->leng || s->bufr); - - if(fit > s->toobig) { - Curl_dyn_free(s); - return CURLE_OUT_OF_MEMORY; - } - else if(!a) { - DEBUGASSERT(!indx); - /* first invoke */ - if(fit < MIN_FIRST_ALLOC) - a = MIN_FIRST_ALLOC; - else - a = fit; - } - else { - while(a < fit) - a *= 2; - } - - 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) { - Curl_safefree(s->bufr); - s->leng = s->allc = 0; - return CURLE_OUT_OF_MEMORY; - } - s->bufr = p; - s->allc = a; - } - - if(len) - memcpy(&s->bufr[indx], mem, len); - s->leng = indx + 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 Curl_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 Curl_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) { - Curl_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 Curl_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 Curl_dyn_add(struct dynbuf *s, const char *str) -{ - size_t n = strlen(str); - DEBUGASSERT(s); - DEBUGASSERT(s->init == DYNINIT); - DEBUGASSERT(!s->leng || s->bufr); - return dyn_nappend(s, (unsigned char *)str, n); -} - -/* - * Append a string vprintf()-style - */ -CURLcode Curl_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); - rc = Curl_dyn_vprintf(s, fmt, ap); - - if(!rc) - return CURLE_OK; -#else - char *str; - str = vaprintf(fmt, ap); /* this allocs a new string to append */ - - if(str) { - CURLcode result = dyn_nappend(s, (unsigned char *)str, strlen(str)); - free(str); - return result; - } - /* If we failed, we cleanup the whole buffer and return error */ - Curl_dyn_free(s); -#endif - return CURLE_OUT_OF_MEMORY; -} - -/* - * Append a string printf()-style - */ -CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...) -{ - CURLcode result; - va_list ap; - DEBUGASSERT(s); - DEBUGASSERT(s->init == DYNINIT); - DEBUGASSERT(!s->leng || s->bufr); - va_start(ap, fmt); - result = Curl_dyn_vaddf(s, fmt, ap); - va_end(ap); - return result; -} - -/* - * Returns a pointer to the buffer. - */ -char *Curl_dyn_ptr(const struct dynbuf *s) -{ - DEBUGASSERT(s); - DEBUGASSERT(s->init == DYNINIT); - DEBUGASSERT(!s->leng || s->bufr); - return s->bufr; -} - -/* - * Returns an unsigned pointer to the buffer. - */ -unsigned char *Curl_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 Curl_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 Curl_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; -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2020 - 2022, 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
+
+#define DYNINIT 0xbee51da /* random pattern */
+
+/*
+ * Init a dynbuf struct.
+ */
+void Curl_dyn_init(struct dynbuf *s, size_t toobig)
+{
+ DEBUGASSERT(s);
+ DEBUGASSERT(toobig);
+ 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 doesn't touch the
+ * 'init' field and thus this buffer can be reused to add data to again.
+ */
+void Curl_dyn_free(struct dynbuf *s)
+{
+ DEBUGASSERT(s);
+ 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 indx = s->leng;
+ size_t a = s->allc;
+ size_t fit = len + indx + 1; /* new string + old string + zero byte */
+
+ /* try to detect if there's rubbish in the struct */
+ DEBUGASSERT(s->init == DYNINIT);
+ DEBUGASSERT(s->toobig);
+ DEBUGASSERT(indx < s->toobig);
+ DEBUGASSERT(!s->leng || s->bufr);
+
+ if(fit > s->toobig) {
+ Curl_dyn_free(s);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else if(!a) {
+ DEBUGASSERT(!indx);
+ /* first invoke */
+ if(fit < MIN_FIRST_ALLOC)
+ a = MIN_FIRST_ALLOC;
+ else
+ a = fit;
+ }
+ else {
+ while(a < fit)
+ a *= 2;
+ }
+
+ 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) {
+ Curl_safefree(s->bufr);
+ s->leng = s->allc = 0;
+ return CURLE_OUT_OF_MEMORY;
+ }
+ s->bufr = p;
+ s->allc = a;
+ }
+
+ if(len)
+ memcpy(&s->bufr[indx], mem, len);
+ s->leng = indx + 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 Curl_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 Curl_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) {
+ Curl_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 Curl_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 Curl_dyn_add(struct dynbuf *s, const char *str)
+{
+ size_t n = strlen(str);
+ DEBUGASSERT(s);
+ DEBUGASSERT(s->init == DYNINIT);
+ DEBUGASSERT(!s->leng || s->bufr);
+ return dyn_nappend(s, (unsigned char *)str, n);
+}
+
+/*
+ * Append a string vprintf()-style
+ */
+CURLcode Curl_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);
+ rc = Curl_dyn_vprintf(s, fmt, ap);
+
+ if(!rc)
+ return CURLE_OK;
+#else
+ char *str;
+ str = vaprintf(fmt, ap); /* this allocs a new string to append */
+
+ if(str) {
+ CURLcode result = dyn_nappend(s, (unsigned char *)str, strlen(str));
+ free(str);
+ return result;
+ }
+ /* If we failed, we cleanup the whole buffer and return error */
+ Curl_dyn_free(s);
+#endif
+ return CURLE_OUT_OF_MEMORY;
+}
+
+/*
+ * Append a string printf()-style
+ */
+CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...)
+{
+ CURLcode result;
+ va_list ap;
+ DEBUGASSERT(s);
+ DEBUGASSERT(s->init == DYNINIT);
+ DEBUGASSERT(!s->leng || s->bufr);
+ va_start(ap, fmt);
+ result = Curl_dyn_vaddf(s, fmt, ap);
+ va_end(ap);
+ return result;
+}
+
+/*
+ * Returns a pointer to the buffer.
+ */
+char *Curl_dyn_ptr(const struct dynbuf *s)
+{
+ DEBUGASSERT(s);
+ DEBUGASSERT(s->init == DYNINIT);
+ DEBUGASSERT(!s->leng || s->bufr);
+ return s->bufr;
+}
+
+/*
+ * Returns an unsigned pointer to the buffer.
+ */
+unsigned char *Curl_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 Curl_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 Curl_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;
+}
diff --git a/libs/libcurl/src/dynbuf.h b/libs/libcurl/src/dynbuf.h index 04a728c779..dfe996d6fb 100644 --- a/libs/libcurl/src/dynbuf.h +++ b/libs/libcurl/src/dynbuf.h @@ -1,94 +1,94 @@ -#ifndef HEADER_CURL_DYNBUF_H -#define HEADER_CURL_DYNBUF_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2020 - 2022, 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> - -#ifndef BUILDING_LIBCURL -/* this renames the functions so that the tool code can use the same code - without getting symbol collisions */ -#define Curl_dyn_init(a,b) curlx_dyn_init(a,b) -#define Curl_dyn_add(a,b) curlx_dyn_add(a,b) -#define Curl_dyn_addn(a,b,c) curlx_dyn_addn(a,b,c) -#define Curl_dyn_addf curlx_dyn_addf -#define Curl_dyn_vaddf curlx_dyn_vaddf -#define Curl_dyn_free(a) curlx_dyn_free(a) -#define Curl_dyn_ptr(a) curlx_dyn_ptr(a) -#define Curl_dyn_uptr(a) curlx_dyn_uptr(a) -#define Curl_dyn_len(a) curlx_dyn_len(a) -#define Curl_dyn_reset(a) curlx_dyn_reset(a) -#define Curl_dyn_tail(a,b) curlx_dyn_tail(a,b) -#define Curl_dyn_setlen(a,b) curlx_dyn_setlen(a,b) -#define curlx_dynbuf dynbuf /* for the struct name */ -#endif - -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 Curl_dyn_init(struct dynbuf *s, size_t toobig); -void Curl_dyn_free(struct dynbuf *s); -CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len) - WARN_UNUSED_RESULT; -CURLcode Curl_dyn_add(struct dynbuf *s, const char *str) - WARN_UNUSED_RESULT; -CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...) - WARN_UNUSED_RESULT; -CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap) - WARN_UNUSED_RESULT; -void Curl_dyn_reset(struct dynbuf *s); -CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail); -CURLcode Curl_dyn_setlen(struct dynbuf *s, size_t set); -char *Curl_dyn_ptr(const struct dynbuf *s); -unsigned char *Curl_dyn_uptr(const struct dynbuf *s); -size_t Curl_dyn_len(const struct dynbuf *s); - -/* returns 0 on success, -1 on error */ -/* The implementation of this function exists in mprintf.c */ -int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save); - -/* Dynamic buffer max sizes */ -#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_H2_HEADERS (128*1024) -#define DYN_H2_TRAILERS (128*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) -#endif +#ifndef HEADER_CURL_DYNBUF_H
+#define HEADER_CURL_DYNBUF_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2020 - 2022, 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>
+
+#ifndef BUILDING_LIBCURL
+/* this renames the functions so that the tool code can use the same code
+ without getting symbol collisions */
+#define Curl_dyn_init(a,b) curlx_dyn_init(a,b)
+#define Curl_dyn_add(a,b) curlx_dyn_add(a,b)
+#define Curl_dyn_addn(a,b,c) curlx_dyn_addn(a,b,c)
+#define Curl_dyn_addf curlx_dyn_addf
+#define Curl_dyn_vaddf curlx_dyn_vaddf
+#define Curl_dyn_free(a) curlx_dyn_free(a)
+#define Curl_dyn_ptr(a) curlx_dyn_ptr(a)
+#define Curl_dyn_uptr(a) curlx_dyn_uptr(a)
+#define Curl_dyn_len(a) curlx_dyn_len(a)
+#define Curl_dyn_reset(a) curlx_dyn_reset(a)
+#define Curl_dyn_tail(a,b) curlx_dyn_tail(a,b)
+#define Curl_dyn_setlen(a,b) curlx_dyn_setlen(a,b)
+#define curlx_dynbuf dynbuf /* for the struct name */
+#endif
+
+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 Curl_dyn_init(struct dynbuf *s, size_t toobig);
+void Curl_dyn_free(struct dynbuf *s);
+CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
+ WARN_UNUSED_RESULT;
+CURLcode Curl_dyn_add(struct dynbuf *s, const char *str)
+ WARN_UNUSED_RESULT;
+CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...)
+ WARN_UNUSED_RESULT;
+CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
+ WARN_UNUSED_RESULT;
+void Curl_dyn_reset(struct dynbuf *s);
+CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail);
+CURLcode Curl_dyn_setlen(struct dynbuf *s, size_t set);
+char *Curl_dyn_ptr(const struct dynbuf *s);
+unsigned char *Curl_dyn_uptr(const struct dynbuf *s);
+size_t Curl_dyn_len(const struct dynbuf *s);
+
+/* returns 0 on success, -1 on error */
+/* The implementation of this function exists in mprintf.c */
+int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save);
+
+/* Dynamic buffer max sizes */
+#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_H2_HEADERS (128*1024)
+#define DYN_H2_TRAILERS (128*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)
+#endif
diff --git a/libs/libcurl/src/easy.c b/libs/libcurl/src/easy.c index b8ac1ef8a8..cf1326b508 100644 --- a/libs/libcurl/src/easy.c +++ b/libs/libcurl/src/easy.c @@ -1,1342 +1,1341 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -/* - * See comment in curl_memory.h for the explanation of this sanity check. - */ - -#ifdef CURLX_NO_MEMORY_CALLBACKS -#error "libcurl shall not ever be built with CURLX_NO_MEMORY_CALLBACKS defined" -#endif - -#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 HAVE_NET_IF_H -#include <net/if.h> -#endif -#ifdef HAVE_SYS_IOCTL_H -#include <sys/ioctl.h> -#endif - -#ifdef HAVE_SYS_PARAM_H -#include <sys/param.h> -#endif - -#include "urldata.h" -#include <curl/curl.h> -#include "transfer.h" -#include "vtls/vtls.h" -#include "url.h" -#include "getinfo.h" -#include "hostip.h" -#include "share.h" -#include "strdup.h" -#include "progress.h" -#include "easyif.h" -#include "multiif.h" -#include "select.h" -#include "sendf.h" /* for failf function prototype */ -#include "connect.h" /* for Curl_getconnectinfo */ -#include "slist.h" -#include "mime.h" -#include "amigaos.h" -#include "warnless.h" -#include "multiif.h" -#include "sigpipe.h" -#include "vssh/ssh.h" -#include "setopt.h" -#include "http_digest.h" -#include "system_win32.h" -#include "http2.h" -#include "dynbuf.h" -#include "altsvc.h" -#include "hsts.h" - -#include "easy_lock.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* true globals -- for curl_global_init() and curl_global_cleanup() */ -static unsigned int initialized; -static long init_flags; - -#ifdef GLOBAL_INIT_IS_THREADSAFE - -static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT; -#define global_init_lock() curl_simple_lock_lock(&s_lock) -#define global_init_unlock() curl_simple_lock_unlock(&s_lock) - -#else - -#define global_init_lock() -#define global_init_unlock() - -#endif - -/* - * strdup (and other memory functions) is redefined in complicated - * ways, but at this point it must be defined as the system-supplied strdup - * so the callback pointer is initialized correctly. - */ -#if defined(_WIN32_WCE) -#define system_strdup _strdup -#elif !defined(HAVE_STRDUP) -#define system_strdup curlx_strdup -#else -#define system_strdup strdup -#endif - -#if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__) -# pragma warning(disable:4232) /* MSVC extension, dllimport identity */ -#endif - -/* - * If a memory-using function (like curl_getenv) is used before - * curl_global_init() is called, we need to have these pointers set already. - */ -curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc; -curl_free_callback Curl_cfree = (curl_free_callback)free; -curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc; -curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup; -curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc; -#if defined(WIN32) && defined(UNICODE) -curl_wcsdup_callback Curl_cwcsdup = Curl_wcsdup; -#endif - -#if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__) -# pragma warning(default:4232) /* MSVC extension, dllimport identity */ -#endif - -#ifdef DEBUGBUILD -static char *leakpointer; -#endif - -/** - * curl_global_init() globally initializes curl given a bitwise set of the - * different features of what to initialize. - */ -static CURLcode global_init(long flags, bool memoryfuncs) -{ - if(initialized++) - return CURLE_OK; - - if(memoryfuncs) { - /* Setup the default memory functions here (again) */ - Curl_cmalloc = (curl_malloc_callback)malloc; - Curl_cfree = (curl_free_callback)free; - Curl_crealloc = (curl_realloc_callback)realloc; - Curl_cstrdup = (curl_strdup_callback)system_strdup; - Curl_ccalloc = (curl_calloc_callback)calloc; -#if defined(WIN32) && defined(UNICODE) - Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup; -#endif - } - - if(!Curl_ssl_init()) { - DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n")); - goto fail; - } - -#ifdef WIN32 - if(Curl_win32_init(flags)) { - DEBUGF(fprintf(stderr, "Error: win32_init failed\n")); - goto fail; - } -#endif - -#ifdef __AMIGA__ - if(Curl_amiga_init()) { - DEBUGF(fprintf(stderr, "Error: Curl_amiga_init failed\n")); - goto fail; - } -#endif - - if(Curl_resolver_global_init()) { - DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n")); - goto fail; - } - -#if defined(USE_SSH) - if(Curl_ssh_init()) { - goto fail; - } -#endif - -#ifdef USE_WOLFSSH - if(WS_SUCCESS != wolfSSH_Init()) { - DEBUGF(fprintf(stderr, "Error: wolfSSH_Init failed\n")); - return CURLE_FAILED_INIT; - } -#endif - - init_flags = flags; - -#ifdef DEBUGBUILD - if(getenv("CURL_GLOBAL_INIT")) - /* alloc data that will leak if *cleanup() is not called! */ - leakpointer = malloc(1); -#endif - - return CURLE_OK; - - fail: - initialized--; /* undo the increase */ - return CURLE_FAILED_INIT; -} - - -/** - * curl_global_init() globally initializes curl given a bitwise set of the - * different features of what to initialize. - */ -CURLcode curl_global_init(long flags) -{ - CURLcode result; - global_init_lock(); - - result = global_init(flags, TRUE); - - global_init_unlock(); - - return result; -} - -/* - * curl_global_init_mem() globally initializes curl and also registers the - * user provided callback routines. - */ -CURLcode curl_global_init_mem(long flags, curl_malloc_callback m, - curl_free_callback f, curl_realloc_callback r, - curl_strdup_callback s, curl_calloc_callback c) -{ - CURLcode result; - - /* Invalid input, return immediately */ - if(!m || !f || !r || !s || !c) - return CURLE_FAILED_INIT; - - global_init_lock(); - - if(initialized) { - /* Already initialized, don't do it again, but bump the variable anyway to - work like curl_global_init() and require the same amount of cleanup - calls. */ - initialized++; - global_init_unlock(); - return CURLE_OK; - } - - /* set memory functions before global_init() in case it wants memory - functions */ - Curl_cmalloc = m; - Curl_cfree = f; - Curl_cstrdup = s; - Curl_crealloc = r; - Curl_ccalloc = c; - - /* Call the actual init function, but without setting */ - result = global_init(flags, FALSE); - - global_init_unlock(); - - return result; -} - -/** - * curl_global_cleanup() globally cleanups curl, uses the value of - * "init_flags" to determine what needs to be cleaned up and what doesn't. - */ -void curl_global_cleanup(void) -{ - global_init_lock(); - - if(!initialized) { - global_init_unlock(); - return; - } - - if(--initialized) { - global_init_unlock(); - return; - } - - Curl_ssl_cleanup(); - Curl_resolver_global_cleanup(); - -#ifdef WIN32 - Curl_win32_cleanup(init_flags); -#endif - - Curl_amiga_cleanup(); - - Curl_ssh_cleanup(); - -#ifdef USE_WOLFSSH - (void)wolfSSH_Cleanup(); -#endif -#ifdef DEBUGBUILD - free(leakpointer); -#endif - - init_flags = 0; - - global_init_unlock(); -} - -/* - * curl_global_sslset() globally initializes the SSL backend to use. - */ -CURLsslset curl_global_sslset(curl_sslbackend id, const char *name, - const curl_ssl_backend ***avail) -{ - CURLsslset rc; - - global_init_lock(); - - rc = Curl_init_sslset_nolock(id, name, avail); - - global_init_unlock(); - - return rc; -} - -/* - * curl_easy_init() is the external interface to alloc, setup and init an - * easy handle that is returned. If anything goes wrong, NULL is returned. - */ -struct Curl_easy *curl_easy_init(void) -{ - CURLcode result; - struct Curl_easy *data; - - /* Make sure we inited the global SSL stuff */ - global_init_lock(); - - if(!initialized) { - result = global_init(CURL_GLOBAL_DEFAULT, TRUE); - if(result) { - /* something in the global init failed, return nothing */ - DEBUGF(fprintf(stderr, "Error: curl_global_init failed\n")); - global_init_unlock(); - return NULL; - } - } - global_init_unlock(); - - /* We use curl_open() with undefined URL so far */ - result = Curl_open(&data); - if(result) { - DEBUGF(fprintf(stderr, "Error: Curl_open failed\n")); - return NULL; - } - - return data; -} - -#ifdef CURLDEBUG - -struct socketmonitor { - struct socketmonitor *next; /* the next node in the list or NULL */ - struct pollfd socket; /* socket info of what to monitor */ -}; - -struct events { - long ms; /* timeout, run the timeout function when reached */ - bool msbump; /* set TRUE when timeout is set by callback */ - int num_sockets; /* number of nodes in the monitor list */ - struct socketmonitor *list; /* list of sockets to monitor */ - int running_handles; /* store the returned number */ -}; - -/* events_timer - * - * Callback that gets called with a new value when the timeout should be - * updated. - */ - -static int events_timer(struct Curl_multi *multi, /* multi handle */ - long timeout_ms, /* see above */ - void *userp) /* private callback pointer */ -{ - struct events *ev = userp; - (void)multi; - if(timeout_ms == -1) - /* timeout removed */ - timeout_ms = 0; - else if(timeout_ms == 0) - /* timeout is already reached! */ - timeout_ms = 1; /* trigger asap */ - - ev->ms = timeout_ms; - ev->msbump = TRUE; - return 0; -} - - -/* poll2cselect - * - * convert from poll() bit definitions to libcurl's CURL_CSELECT_* ones - */ -static int poll2cselect(int pollmask) -{ - int omask = 0; - if(pollmask & POLLIN) - omask |= CURL_CSELECT_IN; - if(pollmask & POLLOUT) - omask |= CURL_CSELECT_OUT; - if(pollmask & POLLERR) - omask |= CURL_CSELECT_ERR; - return omask; -} - - -/* socketcb2poll - * - * convert from libcurl' CURL_POLL_* bit definitions to poll()'s - */ -static short socketcb2poll(int pollmask) -{ - short omask = 0; - if(pollmask & CURL_POLL_IN) - omask |= POLLIN; - if(pollmask & CURL_POLL_OUT) - omask |= POLLOUT; - return omask; -} - -/* events_socket - * - * Callback that gets called with information about socket activity to - * monitor. - */ -static int events_socket(struct Curl_easy *easy, /* easy handle */ - curl_socket_t s, /* socket */ - int what, /* see above */ - void *userp, /* private callback - pointer */ - void *socketp) /* private socket - pointer */ -{ - struct events *ev = userp; - struct socketmonitor *m; - struct socketmonitor *prev = NULL; - -#if defined(CURL_DISABLE_VERBOSE_STRINGS) - (void) easy; -#endif - (void)socketp; - - m = ev->list; - while(m) { - if(m->socket.fd == s) { - - if(what == CURL_POLL_REMOVE) { - struct socketmonitor *nxt = m->next; - /* remove this node from the list of monitored sockets */ - if(prev) - prev->next = nxt; - else - ev->list = nxt; - free(m); - m = nxt; - infof(easy, "socket cb: socket %d REMOVED", s); - } - else { - /* The socket 's' is already being monitored, update the activity - mask. Convert from libcurl bitmask to the poll one. */ - m->socket.events = socketcb2poll(what); - infof(easy, "socket cb: socket %d UPDATED as %s%s", s, - (what&CURL_POLL_IN)?"IN":"", - (what&CURL_POLL_OUT)?"OUT":""); - } - break; - } - prev = m; - m = m->next; /* move to next node */ - } - if(!m) { - if(what == CURL_POLL_REMOVE) { - /* this happens a bit too often, libcurl fix perhaps? */ - /* fprintf(stderr, - "%s: socket %d asked to be REMOVED but not present!\n", - __func__, s); */ - } - else { - m = malloc(sizeof(struct socketmonitor)); - if(m) { - m->next = ev->list; - m->socket.fd = s; - m->socket.events = socketcb2poll(what); - m->socket.revents = 0; - ev->list = m; - infof(easy, "socket cb: socket %d ADDED as %s%s", s, - (what&CURL_POLL_IN)?"IN":"", - (what&CURL_POLL_OUT)?"OUT":""); - } - else - return CURLE_OUT_OF_MEMORY; - } - } - - return 0; -} - - -/* - * events_setup() - * - * Do the multi handle setups that only event-based transfers need. - */ -static void events_setup(struct Curl_multi *multi, struct events *ev) -{ - /* timer callback */ - curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, events_timer); - curl_multi_setopt(multi, CURLMOPT_TIMERDATA, ev); - - /* socket callback */ - curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, events_socket); - curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, ev); -} - - -/* wait_or_timeout() - * - * waits for activity on any of the given sockets, or the timeout to trigger. - */ - -static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) -{ - bool done = FALSE; - CURLMcode mcode = CURLM_OK; - CURLcode result = CURLE_OK; - - while(!done) { - CURLMsg *msg; - struct socketmonitor *m; - struct pollfd *f; - struct pollfd fds[4]; - int numfds = 0; - int pollrc; - int i; - struct curltime before; - struct curltime after; - - /* populate the fds[] array */ - for(m = ev->list, f = &fds[0]; m; m = m->next) { - f->fd = m->socket.fd; - f->events = m->socket.events; - f->revents = 0; - /* fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd); */ - f++; - numfds++; - } - - /* get the time stamp to use to figure out how long poll takes */ - before = Curl_now(); - - /* wait for activity or timeout */ - pollrc = Curl_poll(fds, numfds, ev->ms); - if(pollrc < 0) - return CURLE_UNRECOVERABLE_POLL; - - after = Curl_now(); - - ev->msbump = FALSE; /* reset here */ - - if(!pollrc) { - /* timeout! */ - ev->ms = 0; - /* fprintf(stderr, "call curl_multi_socket_action(TIMEOUT)\n"); */ - mcode = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, - &ev->running_handles); - } - else { - /* here pollrc is > 0 */ - - /* loop over the monitored sockets to see which ones had activity */ - for(i = 0; i< numfds; i++) { - if(fds[i].revents) { - /* socket activity, tell libcurl */ - int act = poll2cselect(fds[i].revents); /* convert */ - infof(multi->easyp, "call curl_multi_socket_action(socket %d)", - fds[i].fd); - mcode = curl_multi_socket_action(multi, fds[i].fd, act, - &ev->running_handles); - } - } - - if(!ev->msbump) { - /* If nothing updated the timeout, we decrease it by the spent time. - * If it was updated, it has the new timeout time stored already. - */ - timediff_t timediff = Curl_timediff(after, before); - if(timediff > 0) { - if(timediff > ev->ms) - ev->ms = 0; - else - ev->ms -= (long)timediff; - } - } - } - - if(mcode) - return CURLE_URL_MALFORMAT; - - /* we don't really care about the "msgs_in_queue" value returned in the - second argument */ - msg = curl_multi_info_read(multi, &pollrc); - if(msg) { - result = msg->data.result; - done = TRUE; - } - } - - return result; -} - - -/* easy_events() - * - * Runs a transfer in a blocking manner using the events-based API - */ -static CURLcode easy_events(struct Curl_multi *multi) -{ - /* this struct is made static to allow it to be used after this function - returns and curl_multi_remove_handle() is called */ - static struct events evs = {2, FALSE, 0, NULL, 0}; - - /* if running event-based, do some further multi inits */ - events_setup(multi, &evs); - - return wait_or_timeout(multi, &evs); -} -#else /* CURLDEBUG */ -/* when not built with debug, this function doesn't exist */ -#define easy_events(x) CURLE_NOT_BUILT_IN -#endif - -static CURLcode easy_transfer(struct Curl_multi *multi) -{ - bool done = FALSE; - CURLMcode mcode = CURLM_OK; - CURLcode result = CURLE_OK; - - while(!done && !mcode) { - int still_running = 0; - - mcode = curl_multi_poll(multi, NULL, 0, 1000, NULL); - - if(!mcode) - mcode = curl_multi_perform(multi, &still_running); - - /* only read 'still_running' if curl_multi_perform() return OK */ - if(!mcode && !still_running) { - int rc; - CURLMsg *msg = curl_multi_info_read(multi, &rc); - if(msg) { - result = msg->data.result; - done = TRUE; - } - } - } - - /* Make sure to return some kind of error if there was a multi problem */ - if(mcode) { - result = (mcode == CURLM_OUT_OF_MEMORY) ? CURLE_OUT_OF_MEMORY : - /* The other multi errors should never happen, so return - something suitably generic */ - CURLE_BAD_FUNCTION_ARGUMENT; - } - - return result; -} - - -/* - * easy_perform() is the external interface that performs a blocking - * transfer as previously setup. - * - * CONCEPT: This function creates a multi handle, adds the easy handle to it, - * runs curl_multi_perform() until the transfer is done, then detaches the - * easy handle, destroys the multi handle and returns the easy handle's return - * code. - * - * REALITY: it can't just create and destroy the multi handle that easily. It - * needs to keep it around since if this easy handle is used again by this - * function, the same multi handle must be re-used so that the same pools and - * caches can be used. - * - * DEBUG: if 'events' is set TRUE, this function will use a replacement engine - * instead of curl_multi_perform() and use curl_multi_socket_action(). - */ -static CURLcode easy_perform(struct Curl_easy *data, bool events) -{ - struct Curl_multi *multi; - CURLMcode mcode; - CURLcode result = CURLE_OK; - SIGPIPE_VARIABLE(pipe_st); - - if(!data) - return CURLE_BAD_FUNCTION_ARGUMENT; - - if(data->set.errorbuffer) - /* clear this as early as possible */ - data->set.errorbuffer[0] = 0; - - if(data->multi) { - failf(data, "easy handle already used in multi handle"); - return CURLE_FAILED_INIT; - } - - if(data->multi_easy) - multi = data->multi_easy; - else { - /* this multi handle will only ever have a single easy handled attached - to it, so make it use minimal hashes */ - multi = Curl_multi_handle(1, 3, 7); - if(!multi) - return CURLE_OUT_OF_MEMORY; - data->multi_easy = multi; - } - - if(multi->in_callback) - return CURLE_RECURSIVE_API_CALL; - - /* Copy the MAXCONNECTS option to the multi handle */ - curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, data->set.maxconnects); - - mcode = curl_multi_add_handle(multi, data); - if(mcode) { - curl_multi_cleanup(multi); - data->multi_easy = NULL; - if(mcode == CURLM_OUT_OF_MEMORY) - return CURLE_OUT_OF_MEMORY; - return CURLE_FAILED_INIT; - } - - sigpipe_ignore(data, &pipe_st); - - /* run the transfer */ - result = events ? easy_events(multi) : easy_transfer(multi); - - /* ignoring the return code isn't nice, but atm we can't really handle - a failure here, room for future improvement! */ - (void)curl_multi_remove_handle(multi, data); - - sigpipe_restore(&pipe_st); - - /* The multi handle is kept alive, owned by the easy handle */ - return result; -} - - -/* - * curl_easy_perform() is the external interface that performs a blocking - * transfer as previously setup. - */ -CURLcode curl_easy_perform(struct Curl_easy *data) -{ - return easy_perform(data, FALSE); -} - -#ifdef CURLDEBUG -/* - * curl_easy_perform_ev() is the external interface that performs a blocking - * transfer using the event-based API internally. - */ -CURLcode curl_easy_perform_ev(struct Curl_easy *data) -{ - return easy_perform(data, TRUE); -} - -#endif - -/* - * curl_easy_cleanup() is the external interface to cleaning/freeing the given - * easy handle. - */ -void curl_easy_cleanup(struct Curl_easy *data) -{ - SIGPIPE_VARIABLE(pipe_st); - - if(!data) - return; - - sigpipe_ignore(data, &pipe_st); - Curl_close(&data); - sigpipe_restore(&pipe_st); -} - -/* - * curl_easy_getinfo() is an external interface that allows an app to retrieve - * information from a performed transfer and similar. - */ -#undef curl_easy_getinfo -CURLcode curl_easy_getinfo(struct Curl_easy *data, CURLINFO info, ...) -{ - va_list arg; - void *paramp; - CURLcode result; - - va_start(arg, info); - paramp = va_arg(arg, void *); - - result = Curl_getinfo(data, info, paramp); - - va_end(arg); - return result; -} - -static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) -{ - CURLcode result = CURLE_OK; - enum dupstring i; - enum dupblob j; - - /* Copy src->set into dst->set first, then deal with the strings - afterwards */ - dst->set = src->set; - Curl_mime_initpart(&dst->set.mimepost, dst); - - /* clear all string pointers first */ - memset(dst->set.str, 0, STRING_LAST * sizeof(char *)); - - /* duplicate all strings */ - for(i = (enum dupstring)0; i< STRING_LASTZEROTERMINATED; i++) { - result = Curl_setstropt(&dst->set.str[i], src->set.str[i]); - if(result) - return result; - } - - /* clear all blob pointers first */ - memset(dst->set.blobs, 0, BLOB_LAST * sizeof(struct curl_blob *)); - /* duplicate all blobs */ - for(j = (enum dupblob)0; j < BLOB_LAST; j++) { - result = Curl_setblobopt(&dst->set.blobs[j], src->set.blobs[j]); - if(result) - return result; - } - - /* duplicate memory areas pointed to */ - i = STRING_COPYPOSTFIELDS; - if(src->set.postfieldsize && src->set.str[i]) { - /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */ - dst->set.str[i] = Curl_memdup(src->set.str[i], - curlx_sotouz(src->set.postfieldsize)); - if(!dst->set.str[i]) - return CURLE_OUT_OF_MEMORY; - /* point to the new copy */ - dst->set.postfields = dst->set.str[i]; - } - - /* Duplicate mime data. */ - result = Curl_mime_duppart(&dst->set.mimepost, &src->set.mimepost); - - if(src->set.resolve) - dst->state.resolve = dst->set.resolve; - - return result; -} - -/* - * curl_easy_duphandle() is an external interface to allow duplication of a - * given input easy handle. The returned handle will be a new working handle - * with all options set exactly as the input source handle. - */ -struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) -{ - struct Curl_easy *outcurl = calloc(1, sizeof(struct Curl_easy)); - if(!outcurl) - goto fail; - - /* - * We setup a few buffers we need. We should probably make them - * get setup on-demand in the code, as that would probably decrease - * the likeliness of us forgetting to init a buffer here in the future. - */ - outcurl->set.buffer_size = data->set.buffer_size; - - /* copy all userdefined values */ - if(dupset(outcurl, data)) - goto fail; - - Curl_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER); - - /* the connection cache is setup on demand */ - outcurl->state.conn_cache = NULL; - outcurl->state.lastconnect_id = -1; - - outcurl->progress.flags = data->progress.flags; - outcurl->progress.callback = data->progress.callback; - -#ifndef CURL_DISABLE_COOKIES - if(data->cookies) { - /* If cookies are enabled in the parent handle, we enable them - in the clone as well! */ - outcurl->cookies = Curl_cookie_init(data, - data->cookies->filename, - outcurl->cookies, - data->set.cookiesession); - if(!outcurl->cookies) - goto fail; - } - - /* duplicate all values in 'change' */ - if(data->state.cookielist) { - outcurl->state.cookielist = - Curl_slist_duplicate(data->state.cookielist); - if(!outcurl->state.cookielist) - goto fail; - } -#endif - - if(data->state.url) { - outcurl->state.url = strdup(data->state.url); - if(!outcurl->state.url) - goto fail; - outcurl->state.url_alloc = TRUE; - } - - if(data->state.referer) { - outcurl->state.referer = strdup(data->state.referer); - if(!outcurl->state.referer) - goto fail; - outcurl->state.referer_alloc = TRUE; - } - - /* Reinitialize an SSL engine for the new handle - * note: the engine name has already been copied by dupset */ - if(outcurl->set.str[STRING_SSL_ENGINE]) { - if(Curl_ssl_set_engine(outcurl, outcurl->set.str[STRING_SSL_ENGINE])) - goto fail; - } - -#ifndef CURL_DISABLE_ALTSVC - if(data->asi) { - outcurl->asi = Curl_altsvc_init(); - if(!outcurl->asi) - goto fail; - if(outcurl->set.str[STRING_ALTSVC]) - (void)Curl_altsvc_load(outcurl->asi, outcurl->set.str[STRING_ALTSVC]); - } -#endif -#ifndef CURL_DISABLE_HSTS - if(data->hsts) { - outcurl->hsts = Curl_hsts_init(); - if(!outcurl->hsts) - goto fail; - if(outcurl->set.str[STRING_HSTS]) - (void)Curl_hsts_loadfile(outcurl, - outcurl->hsts, outcurl->set.str[STRING_HSTS]); - (void)Curl_hsts_loadcb(outcurl, outcurl->hsts); - } -#endif - /* Clone the resolver handle, if present, for the new handle */ - if(Curl_resolver_duphandle(outcurl, - &outcurl->state.async.resolver, - data->state.async.resolver)) - goto fail; - -#ifdef USE_ARES - { - CURLcode rc; - - rc = Curl_set_dns_servers(outcurl, data->set.str[STRING_DNS_SERVERS]); - if(rc && rc != CURLE_NOT_BUILT_IN) - goto fail; - - rc = Curl_set_dns_interface(outcurl, data->set.str[STRING_DNS_INTERFACE]); - if(rc && rc != CURLE_NOT_BUILT_IN) - goto fail; - - rc = Curl_set_dns_local_ip4(outcurl, data->set.str[STRING_DNS_LOCAL_IP4]); - if(rc && rc != CURLE_NOT_BUILT_IN) - goto fail; - - rc = Curl_set_dns_local_ip6(outcurl, data->set.str[STRING_DNS_LOCAL_IP6]); - if(rc && rc != CURLE_NOT_BUILT_IN) - goto fail; - } -#endif /* USE_ARES */ - - Curl_initinfo(outcurl); - - outcurl->magic = CURLEASY_MAGIC_NUMBER; - - /* we reach this point and thus we are OK */ - - return outcurl; - - fail: - - if(outcurl) { -#ifndef CURL_DISABLE_COOKIES - curl_slist_free_all(outcurl->state.cookielist); - outcurl->state.cookielist = NULL; -#endif - Curl_safefree(outcurl->state.buffer); - Curl_dyn_free(&outcurl->state.headerb); - Curl_safefree(outcurl->state.url); - Curl_safefree(outcurl->state.referer); - Curl_altsvc_cleanup(&outcurl->asi); - Curl_hsts_cleanup(&outcurl->hsts); - Curl_freeset(outcurl); - free(outcurl); - } - - return NULL; -} - -/* - * curl_easy_reset() is an external interface that allows an app to re- - * initialize a session handle to the default values. - */ -void curl_easy_reset(struct Curl_easy *data) -{ - Curl_free_request_state(data); - - /* zero out UserDefined data: */ - Curl_freeset(data); - memset(&data->set, 0, sizeof(struct UserDefined)); - (void)Curl_init_userdefined(data); - - /* zero out Progress data: */ - memset(&data->progress, 0, sizeof(struct Progress)); - - /* zero out PureInfo data: */ - Curl_initinfo(data); - - data->progress.flags |= PGRS_HIDE; - data->state.current_speed = -1; /* init to negative == impossible */ - data->state.retrycount = 0; /* reset the retry counter */ - - /* zero out authentication data: */ - memset(&data->state.authhost, 0, sizeof(struct auth)); - memset(&data->state.authproxy, 0, sizeof(struct auth)); - -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) - Curl_http_auth_cleanup_digest(data); -#endif -} - -/* - * curl_easy_pause() allows an application to pause or unpause a specific - * transfer and direction. This function sets the full new state for the - * current connection this easy handle operates on. - * - * NOTE: if you have the receiving paused and you call this function to remove - * the pausing, you may get your write callback called at this point. - * - * Action is a bitmask consisting of CURLPAUSE_* bits in curl/curl.h - * - * NOTE: This is one of few API functions that are allowed to be called from - * within a callback. - */ -CURLcode curl_easy_pause(struct Curl_easy *data, int action) -{ - struct SingleRequest *k; - CURLcode result = CURLE_OK; - int oldstate; - int newstate; - - if(!GOOD_EASY_HANDLE(data) || !data->conn) - /* crazy input, don't continue */ - return CURLE_BAD_FUNCTION_ARGUMENT; - - k = &data->req; - oldstate = k->keepon & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE); - - /* first switch off both pause bits then set the new pause bits */ - newstate = (k->keepon &~ (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) | - ((action & CURLPAUSE_RECV)?KEEP_RECV_PAUSE:0) | - ((action & CURLPAUSE_SEND)?KEEP_SEND_PAUSE:0); - - if((newstate & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) == oldstate) { - /* Not changing any pause state, return */ - DEBUGF(infof(data, "pause: no change, early return")); - return CURLE_OK; - } - - /* Unpause parts in active mime tree. */ - if((k->keepon & ~newstate & KEEP_SEND_PAUSE) && - (data->mstate == MSTATE_PERFORMING || - data->mstate == MSTATE_RATELIMITING) && - data->state.fread_func == (curl_read_callback) Curl_mime_read) { - Curl_mime_unpause(data->state.in); - } - - /* put it back in the keepon */ - k->keepon = newstate; - - if(!(newstate & KEEP_RECV_PAUSE)) { - Curl_http2_stream_pause(data, FALSE); - - if(data->state.tempcount) { - /* there are buffers for sending that can be delivered as the receive - pausing is lifted! */ - unsigned int i; - unsigned int count = data->state.tempcount; - struct tempbuf writebuf[3]; /* there can only be three */ - - /* copy the structs to allow for immediate re-pausing */ - for(i = 0; i < data->state.tempcount; i++) { - writebuf[i] = data->state.tempwrite[i]; - Curl_dyn_init(&data->state.tempwrite[i].b, DYN_PAUSE_BUFFER); - } - data->state.tempcount = 0; - - for(i = 0; i < count; i++) { - /* even if one function returns error, this loops through and frees - all buffers */ - if(!result) - result = Curl_client_write(data, writebuf[i].type, - Curl_dyn_ptr(&writebuf[i].b), - Curl_dyn_len(&writebuf[i].b)); - Curl_dyn_free(&writebuf[i].b); - } - - if(result) - return result; - } - } - -#ifdef USE_HYPER - if(!(newstate & KEEP_SEND_PAUSE)) { - /* need to wake the send body waker */ - if(data->hyp.send_body_waker) { - hyper_waker_wake(data->hyp.send_body_waker); - data->hyp.send_body_waker = NULL; - } - } -#endif - - /* if there's no error and we're not pausing both directions, we want - to have this handle checked soon */ - if((newstate & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) != - (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) { - Curl_expire(data, 0, EXPIRE_RUN_NOW); /* get this handle going again */ - - /* reset the too-slow time keeper */ - data->state.keeps_speed.tv_sec = 0; - - if(!data->state.tempcount) - /* if not pausing again, force a recv/send check of this connection as - the data might've been read off the socket already */ - data->conn->cselect_bits = CURL_CSELECT_IN | CURL_CSELECT_OUT; - if(data->multi) { - if(Curl_update_timer(data->multi)) - return CURLE_ABORTED_BY_CALLBACK; - } - } - - if(!data->state.done) - /* This transfer may have been moved in or out of the bundle, update the - corresponding socket callback, if used */ - result = Curl_updatesocket(data); - - return result; -} - - -static CURLcode easy_connection(struct Curl_easy *data, curl_socket_t *sfd, - struct connectdata **connp) -{ - if(!data) - return CURLE_BAD_FUNCTION_ARGUMENT; - - /* only allow these to be called on handles with CURLOPT_CONNECT_ONLY */ - if(!data->set.connect_only) { - failf(data, "CONNECT_ONLY is required"); - return CURLE_UNSUPPORTED_PROTOCOL; - } - - *sfd = Curl_getconnectinfo(data, connp); - - if(*sfd == CURL_SOCKET_BAD) { - failf(data, "Failed to get recent socket"); - return CURLE_UNSUPPORTED_PROTOCOL; - } - - return CURLE_OK; -} - -/* - * Receives data from the connected socket. Use after successful - * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. - * Returns CURLE_OK on success, error code on error. - */ -CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen, - size_t *n) -{ - curl_socket_t sfd; - CURLcode result; - ssize_t n1; - struct connectdata *c; - - if(Curl_is_in_callback(data)) - return CURLE_RECURSIVE_API_CALL; - - result = easy_connection(data, &sfd, &c); - if(result) - return result; - - if(!data->conn) - /* on first invoke, the transfer has been detached from the connection and - needs to be reattached */ - Curl_attach_connection(data, c); - - *n = 0; - result = Curl_read(data, sfd, buffer, buflen, &n1); - - if(result) - return result; - - *n = (size_t)n1; - - return CURLE_OK; -} - -/* - * Sends data over the connected socket. - * - * This is the private internal version of curl_easy_send() - */ -CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer, - size_t buflen, ssize_t *n) -{ - curl_socket_t sfd; - CURLcode result; - ssize_t n1; - struct connectdata *c = NULL; - SIGPIPE_VARIABLE(pipe_st); - - result = easy_connection(data, &sfd, &c); - if(result) - return result; - - if(!data->conn) - /* on first invoke, the transfer has been detached from the connection and - needs to be reattached */ - Curl_attach_connection(data, c); - - *n = 0; - sigpipe_ignore(data, &pipe_st); - result = Curl_write(data, sfd, buffer, buflen, &n1); - sigpipe_restore(&pipe_st); - - if(n1 == -1) - return CURLE_SEND_ERROR; - - /* detect EAGAIN */ - if(!result && !n1) - return CURLE_AGAIN; - - *n = n1; - - return result; -} - -/* - * Sends data over the connected socket. Use after successful - * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. - */ -CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer, - size_t buflen, size_t *n) -{ - ssize_t written = 0; - CURLcode result; - if(Curl_is_in_callback(data)) - return CURLE_RECURSIVE_API_CALL; - - result = Curl_senddata(data, buffer, buflen, &written); - *n = (size_t)written; - return result; -} - -/* - * Wrapper to call functions in Curl_conncache_foreach() - * - * Returns always 0. - */ -static int conn_upkeep(struct Curl_easy *data, - struct connectdata *conn, - void *param) -{ - /* Param is unused. */ - (void)param; - - if(conn->handler->connection_check) { - /* briefly attach the connection to this transfer for the purpose of - checking it */ - Curl_attach_connection(data, conn); - - /* Do a protocol-specific keepalive check on the connection. */ - conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE); - /* detach the connection again */ - Curl_detach_connection(data); - } - - return 0; /* continue iteration */ -} - -static CURLcode upkeep(struct conncache *conn_cache, void *data) -{ - /* Loop over every connection and make connection alive. */ - Curl_conncache_foreach(data, - conn_cache, - data, - conn_upkeep); - return CURLE_OK; -} - -/* - * Performs connection upkeep for the given session handle. - */ -CURLcode curl_easy_upkeep(struct Curl_easy *data) -{ - /* Verify that we got an easy handle we can work with. */ - if(!GOOD_EASY_HANDLE(data)) - return CURLE_BAD_FUNCTION_ARGUMENT; - - if(data->multi_easy) { - /* Use the common function to keep connections alive. */ - return upkeep(&data->multi_easy->conn_cache, data); - } - else { - /* No connections, so just return success */ - return CURLE_OK; - } -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+/*
+ * See comment in curl_memory.h for the explanation of this sanity check.
+ */
+
+#ifdef CURLX_NO_MEMORY_CALLBACKS
+#error "libcurl shall not ever be built with CURLX_NO_MEMORY_CALLBACKS defined"
+#endif
+
+#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 HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "transfer.h"
+#include "vtls/vtls.h"
+#include "url.h"
+#include "getinfo.h"
+#include "hostip.h"
+#include "share.h"
+#include "strdup.h"
+#include "progress.h"
+#include "easyif.h"
+#include "multiif.h"
+#include "select.h"
+#include "sendf.h" /* for failf function prototype */
+#include "connect.h" /* for Curl_getconnectinfo */
+#include "slist.h"
+#include "mime.h"
+#include "amigaos.h"
+#include "warnless.h"
+#include "sigpipe.h"
+#include "vssh/ssh.h"
+#include "setopt.h"
+#include "http_digest.h"
+#include "system_win32.h"
+#include "http2.h"
+#include "dynbuf.h"
+#include "altsvc.h"
+#include "hsts.h"
+
+#include "easy_lock.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* true globals -- for curl_global_init() and curl_global_cleanup() */
+static unsigned int initialized;
+static long init_flags;
+
+#ifdef GLOBAL_INIT_IS_THREADSAFE
+
+static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT;
+#define global_init_lock() curl_simple_lock_lock(&s_lock)
+#define global_init_unlock() curl_simple_lock_unlock(&s_lock)
+
+#else
+
+#define global_init_lock()
+#define global_init_unlock()
+
+#endif
+
+/*
+ * strdup (and other memory functions) is redefined in complicated
+ * ways, but at this point it must be defined as the system-supplied strdup
+ * so the callback pointer is initialized correctly.
+ */
+#if defined(_WIN32_WCE)
+#define system_strdup _strdup
+#elif !defined(HAVE_STRDUP)
+#define system_strdup curlx_strdup
+#else
+#define system_strdup strdup
+#endif
+
+#if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__)
+# pragma warning(disable:4232) /* MSVC extension, dllimport identity */
+#endif
+
+/*
+ * If a memory-using function (like curl_getenv) is used before
+ * curl_global_init() is called, we need to have these pointers set already.
+ */
+curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc;
+curl_free_callback Curl_cfree = (curl_free_callback)free;
+curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc;
+curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup;
+curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc;
+#if defined(WIN32) && defined(UNICODE)
+curl_wcsdup_callback Curl_cwcsdup = Curl_wcsdup;
+#endif
+
+#if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__)
+# pragma warning(default:4232) /* MSVC extension, dllimport identity */
+#endif
+
+#ifdef DEBUGBUILD
+static char *leakpointer;
+#endif
+
+/**
+ * curl_global_init() globally initializes curl given a bitwise set of the
+ * different features of what to initialize.
+ */
+static CURLcode global_init(long flags, bool memoryfuncs)
+{
+ if(initialized++)
+ return CURLE_OK;
+
+ if(memoryfuncs) {
+ /* Setup the default memory functions here (again) */
+ Curl_cmalloc = (curl_malloc_callback)malloc;
+ Curl_cfree = (curl_free_callback)free;
+ Curl_crealloc = (curl_realloc_callback)realloc;
+ Curl_cstrdup = (curl_strdup_callback)system_strdup;
+ Curl_ccalloc = (curl_calloc_callback)calloc;
+#if defined(WIN32) && defined(UNICODE)
+ Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup;
+#endif
+ }
+
+ if(!Curl_ssl_init()) {
+ DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n"));
+ goto fail;
+ }
+
+#ifdef WIN32
+ if(Curl_win32_init(flags)) {
+ DEBUGF(fprintf(stderr, "Error: win32_init failed\n"));
+ goto fail;
+ }
+#endif
+
+#ifdef __AMIGA__
+ if(Curl_amiga_init()) {
+ DEBUGF(fprintf(stderr, "Error: Curl_amiga_init failed\n"));
+ goto fail;
+ }
+#endif
+
+ if(Curl_resolver_global_init()) {
+ DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n"));
+ goto fail;
+ }
+
+#if defined(USE_SSH)
+ if(Curl_ssh_init()) {
+ goto fail;
+ }
+#endif
+
+#ifdef USE_WOLFSSH
+ if(WS_SUCCESS != wolfSSH_Init()) {
+ DEBUGF(fprintf(stderr, "Error: wolfSSH_Init failed\n"));
+ return CURLE_FAILED_INIT;
+ }
+#endif
+
+ init_flags = flags;
+
+#ifdef DEBUGBUILD
+ if(getenv("CURL_GLOBAL_INIT"))
+ /* alloc data that will leak if *cleanup() is not called! */
+ leakpointer = malloc(1);
+#endif
+
+ return CURLE_OK;
+
+ fail:
+ initialized--; /* undo the increase */
+ return CURLE_FAILED_INIT;
+}
+
+
+/**
+ * curl_global_init() globally initializes curl given a bitwise set of the
+ * different features of what to initialize.
+ */
+CURLcode curl_global_init(long flags)
+{
+ CURLcode result;
+ global_init_lock();
+
+ result = global_init(flags, TRUE);
+
+ global_init_unlock();
+
+ return result;
+}
+
+/*
+ * curl_global_init_mem() globally initializes curl and also registers the
+ * user provided callback routines.
+ */
+CURLcode curl_global_init_mem(long flags, curl_malloc_callback m,
+ curl_free_callback f, curl_realloc_callback r,
+ curl_strdup_callback s, curl_calloc_callback c)
+{
+ CURLcode result;
+
+ /* Invalid input, return immediately */
+ if(!m || !f || !r || !s || !c)
+ return CURLE_FAILED_INIT;
+
+ global_init_lock();
+
+ if(initialized) {
+ /* Already initialized, don't do it again, but bump the variable anyway to
+ work like curl_global_init() and require the same amount of cleanup
+ calls. */
+ initialized++;
+ global_init_unlock();
+ return CURLE_OK;
+ }
+
+ /* set memory functions before global_init() in case it wants memory
+ functions */
+ Curl_cmalloc = m;
+ Curl_cfree = f;
+ Curl_cstrdup = s;
+ Curl_crealloc = r;
+ Curl_ccalloc = c;
+
+ /* Call the actual init function, but without setting */
+ result = global_init(flags, FALSE);
+
+ global_init_unlock();
+
+ return result;
+}
+
+/**
+ * curl_global_cleanup() globally cleanups curl, uses the value of
+ * "init_flags" to determine what needs to be cleaned up and what doesn't.
+ */
+void curl_global_cleanup(void)
+{
+ global_init_lock();
+
+ if(!initialized) {
+ global_init_unlock();
+ return;
+ }
+
+ if(--initialized) {
+ global_init_unlock();
+ return;
+ }
+
+ Curl_ssl_cleanup();
+ Curl_resolver_global_cleanup();
+
+#ifdef WIN32
+ Curl_win32_cleanup(init_flags);
+#endif
+
+ Curl_amiga_cleanup();
+
+ Curl_ssh_cleanup();
+
+#ifdef USE_WOLFSSH
+ (void)wolfSSH_Cleanup();
+#endif
+#ifdef DEBUGBUILD
+ free(leakpointer);
+#endif
+
+ init_flags = 0;
+
+ global_init_unlock();
+}
+
+/*
+ * curl_global_sslset() globally initializes the SSL backend to use.
+ */
+CURLsslset curl_global_sslset(curl_sslbackend id, const char *name,
+ const curl_ssl_backend ***avail)
+{
+ CURLsslset rc;
+
+ global_init_lock();
+
+ rc = Curl_init_sslset_nolock(id, name, avail);
+
+ global_init_unlock();
+
+ return rc;
+}
+
+/*
+ * curl_easy_init() is the external interface to alloc, setup and init an
+ * easy handle that is returned. If anything goes wrong, NULL is returned.
+ */
+struct Curl_easy *curl_easy_init(void)
+{
+ CURLcode result;
+ struct Curl_easy *data;
+
+ /* Make sure we inited the global SSL stuff */
+ global_init_lock();
+
+ if(!initialized) {
+ result = global_init(CURL_GLOBAL_DEFAULT, TRUE);
+ if(result) {
+ /* something in the global init failed, return nothing */
+ DEBUGF(fprintf(stderr, "Error: curl_global_init failed\n"));
+ global_init_unlock();
+ return NULL;
+ }
+ }
+ global_init_unlock();
+
+ /* We use curl_open() with undefined URL so far */
+ result = Curl_open(&data);
+ if(result) {
+ DEBUGF(fprintf(stderr, "Error: Curl_open failed\n"));
+ return NULL;
+ }
+
+ return data;
+}
+
+#ifdef CURLDEBUG
+
+struct socketmonitor {
+ struct socketmonitor *next; /* the next node in the list or NULL */
+ struct pollfd socket; /* socket info of what to monitor */
+};
+
+struct events {
+ long ms; /* timeout, run the timeout function when reached */
+ bool msbump; /* set TRUE when timeout is set by callback */
+ int num_sockets; /* number of nodes in the monitor list */
+ struct socketmonitor *list; /* list of sockets to monitor */
+ int running_handles; /* store the returned number */
+};
+
+/* events_timer
+ *
+ * Callback that gets called with a new value when the timeout should be
+ * updated.
+ */
+
+static int events_timer(struct Curl_multi *multi, /* multi handle */
+ long timeout_ms, /* see above */
+ void *userp) /* private callback pointer */
+{
+ struct events *ev = userp;
+ (void)multi;
+ if(timeout_ms == -1)
+ /* timeout removed */
+ timeout_ms = 0;
+ else if(timeout_ms == 0)
+ /* timeout is already reached! */
+ timeout_ms = 1; /* trigger asap */
+
+ ev->ms = timeout_ms;
+ ev->msbump = TRUE;
+ return 0;
+}
+
+
+/* poll2cselect
+ *
+ * convert from poll() bit definitions to libcurl's CURL_CSELECT_* ones
+ */
+static int poll2cselect(int pollmask)
+{
+ int omask = 0;
+ if(pollmask & POLLIN)
+ omask |= CURL_CSELECT_IN;
+ if(pollmask & POLLOUT)
+ omask |= CURL_CSELECT_OUT;
+ if(pollmask & POLLERR)
+ omask |= CURL_CSELECT_ERR;
+ return omask;
+}
+
+
+/* socketcb2poll
+ *
+ * convert from libcurl' CURL_POLL_* bit definitions to poll()'s
+ */
+static short socketcb2poll(int pollmask)
+{
+ short omask = 0;
+ if(pollmask & CURL_POLL_IN)
+ omask |= POLLIN;
+ if(pollmask & CURL_POLL_OUT)
+ omask |= POLLOUT;
+ return omask;
+}
+
+/* events_socket
+ *
+ * Callback that gets called with information about socket activity to
+ * monitor.
+ */
+static int events_socket(struct Curl_easy *easy, /* easy handle */
+ curl_socket_t s, /* socket */
+ int what, /* see above */
+ void *userp, /* private callback
+ pointer */
+ void *socketp) /* private socket
+ pointer */
+{
+ struct events *ev = userp;
+ struct socketmonitor *m;
+ struct socketmonitor *prev = NULL;
+
+#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+ (void) easy;
+#endif
+ (void)socketp;
+
+ m = ev->list;
+ while(m) {
+ if(m->socket.fd == s) {
+
+ if(what == CURL_POLL_REMOVE) {
+ struct socketmonitor *nxt = m->next;
+ /* remove this node from the list of monitored sockets */
+ if(prev)
+ prev->next = nxt;
+ else
+ ev->list = nxt;
+ free(m);
+ m = nxt;
+ infof(easy, "socket cb: socket %d REMOVED", s);
+ }
+ else {
+ /* The socket 's' is already being monitored, update the activity
+ mask. Convert from libcurl bitmask to the poll one. */
+ m->socket.events = socketcb2poll(what);
+ infof(easy, "socket cb: socket %d UPDATED as %s%s", s,
+ (what&CURL_POLL_IN)?"IN":"",
+ (what&CURL_POLL_OUT)?"OUT":"");
+ }
+ break;
+ }
+ prev = m;
+ m = m->next; /* move to next node */
+ }
+ if(!m) {
+ if(what == CURL_POLL_REMOVE) {
+ /* this happens a bit too often, libcurl fix perhaps? */
+ /* fprintf(stderr,
+ "%s: socket %d asked to be REMOVED but not present!\n",
+ __func__, s); */
+ }
+ else {
+ m = malloc(sizeof(struct socketmonitor));
+ if(m) {
+ m->next = ev->list;
+ m->socket.fd = s;
+ m->socket.events = socketcb2poll(what);
+ m->socket.revents = 0;
+ ev->list = m;
+ infof(easy, "socket cb: socket %d ADDED as %s%s", s,
+ (what&CURL_POLL_IN)?"IN":"",
+ (what&CURL_POLL_OUT)?"OUT":"");
+ }
+ else
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * events_setup()
+ *
+ * Do the multi handle setups that only event-based transfers need.
+ */
+static void events_setup(struct Curl_multi *multi, struct events *ev)
+{
+ /* timer callback */
+ curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, events_timer);
+ curl_multi_setopt(multi, CURLMOPT_TIMERDATA, ev);
+
+ /* socket callback */
+ curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, events_socket);
+ curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, ev);
+}
+
+
+/* wait_or_timeout()
+ *
+ * waits for activity on any of the given sockets, or the timeout to trigger.
+ */
+
+static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
+{
+ bool done = FALSE;
+ CURLMcode mcode = CURLM_OK;
+ CURLcode result = CURLE_OK;
+
+ while(!done) {
+ CURLMsg *msg;
+ struct socketmonitor *m;
+ struct pollfd *f;
+ struct pollfd fds[4];
+ int numfds = 0;
+ int pollrc;
+ int i;
+ struct curltime before;
+ struct curltime after;
+
+ /* populate the fds[] array */
+ for(m = ev->list, f = &fds[0]; m; m = m->next) {
+ f->fd = m->socket.fd;
+ f->events = m->socket.events;
+ f->revents = 0;
+ /* fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd); */
+ f++;
+ numfds++;
+ }
+
+ /* get the time stamp to use to figure out how long poll takes */
+ before = Curl_now();
+
+ /* wait for activity or timeout */
+ pollrc = Curl_poll(fds, numfds, ev->ms);
+ if(pollrc < 0)
+ return CURLE_UNRECOVERABLE_POLL;
+
+ after = Curl_now();
+
+ ev->msbump = FALSE; /* reset here */
+
+ if(!pollrc) {
+ /* timeout! */
+ ev->ms = 0;
+ /* fprintf(stderr, "call curl_multi_socket_action(TIMEOUT)\n"); */
+ mcode = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0,
+ &ev->running_handles);
+ }
+ else {
+ /* here pollrc is > 0 */
+
+ /* loop over the monitored sockets to see which ones had activity */
+ for(i = 0; i< numfds; i++) {
+ if(fds[i].revents) {
+ /* socket activity, tell libcurl */
+ int act = poll2cselect(fds[i].revents); /* convert */
+ infof(multi->easyp, "call curl_multi_socket_action(socket %d)",
+ fds[i].fd);
+ mcode = curl_multi_socket_action(multi, fds[i].fd, act,
+ &ev->running_handles);
+ }
+ }
+
+ if(!ev->msbump) {
+ /* If nothing updated the timeout, we decrease it by the spent time.
+ * If it was updated, it has the new timeout time stored already.
+ */
+ timediff_t timediff = Curl_timediff(after, before);
+ if(timediff > 0) {
+ if(timediff > ev->ms)
+ ev->ms = 0;
+ else
+ ev->ms -= (long)timediff;
+ }
+ }
+ }
+
+ if(mcode)
+ return CURLE_URL_MALFORMAT;
+
+ /* we don't really care about the "msgs_in_queue" value returned in the
+ second argument */
+ msg = curl_multi_info_read(multi, &pollrc);
+ if(msg) {
+ result = msg->data.result;
+ done = TRUE;
+ }
+ }
+
+ return result;
+}
+
+
+/* easy_events()
+ *
+ * Runs a transfer in a blocking manner using the events-based API
+ */
+static CURLcode easy_events(struct Curl_multi *multi)
+{
+ /* this struct is made static to allow it to be used after this function
+ returns and curl_multi_remove_handle() is called */
+ static struct events evs = {2, FALSE, 0, NULL, 0};
+
+ /* if running event-based, do some further multi inits */
+ events_setup(multi, &evs);
+
+ return wait_or_timeout(multi, &evs);
+}
+#else /* CURLDEBUG */
+/* when not built with debug, this function doesn't exist */
+#define easy_events(x) CURLE_NOT_BUILT_IN
+#endif
+
+static CURLcode easy_transfer(struct Curl_multi *multi)
+{
+ bool done = FALSE;
+ CURLMcode mcode = CURLM_OK;
+ CURLcode result = CURLE_OK;
+
+ while(!done && !mcode) {
+ int still_running = 0;
+
+ mcode = curl_multi_poll(multi, NULL, 0, 1000, NULL);
+
+ if(!mcode)
+ mcode = curl_multi_perform(multi, &still_running);
+
+ /* only read 'still_running' if curl_multi_perform() return OK */
+ if(!mcode && !still_running) {
+ int rc;
+ CURLMsg *msg = curl_multi_info_read(multi, &rc);
+ if(msg) {
+ result = msg->data.result;
+ done = TRUE;
+ }
+ }
+ }
+
+ /* Make sure to return some kind of error if there was a multi problem */
+ if(mcode) {
+ result = (mcode == CURLM_OUT_OF_MEMORY) ? CURLE_OUT_OF_MEMORY :
+ /* The other multi errors should never happen, so return
+ something suitably generic */
+ CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+
+ return result;
+}
+
+
+/*
+ * easy_perform() is the external interface that performs a blocking
+ * transfer as previously setup.
+ *
+ * CONCEPT: This function creates a multi handle, adds the easy handle to it,
+ * runs curl_multi_perform() until the transfer is done, then detaches the
+ * easy handle, destroys the multi handle and returns the easy handle's return
+ * code.
+ *
+ * REALITY: it can't just create and destroy the multi handle that easily. It
+ * needs to keep it around since if this easy handle is used again by this
+ * function, the same multi handle must be re-used so that the same pools and
+ * caches can be used.
+ *
+ * DEBUG: if 'events' is set TRUE, this function will use a replacement engine
+ * instead of curl_multi_perform() and use curl_multi_socket_action().
+ */
+static CURLcode easy_perform(struct Curl_easy *data, bool events)
+{
+ struct Curl_multi *multi;
+ CURLMcode mcode;
+ CURLcode result = CURLE_OK;
+ SIGPIPE_VARIABLE(pipe_st);
+
+ if(!data)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ if(data->set.errorbuffer)
+ /* clear this as early as possible */
+ data->set.errorbuffer[0] = 0;
+
+ if(data->multi) {
+ failf(data, "easy handle already used in multi handle");
+ return CURLE_FAILED_INIT;
+ }
+
+ if(data->multi_easy)
+ multi = data->multi_easy;
+ else {
+ /* this multi handle will only ever have a single easy handled attached
+ to it, so make it use minimal hashes */
+ multi = Curl_multi_handle(1, 3, 7);
+ if(!multi)
+ return CURLE_OUT_OF_MEMORY;
+ data->multi_easy = multi;
+ }
+
+ if(multi->in_callback)
+ return CURLE_RECURSIVE_API_CALL;
+
+ /* Copy the MAXCONNECTS option to the multi handle */
+ curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, data->set.maxconnects);
+
+ mcode = curl_multi_add_handle(multi, data);
+ if(mcode) {
+ curl_multi_cleanup(multi);
+ data->multi_easy = NULL;
+ if(mcode == CURLM_OUT_OF_MEMORY)
+ return CURLE_OUT_OF_MEMORY;
+ return CURLE_FAILED_INIT;
+ }
+
+ sigpipe_ignore(data, &pipe_st);
+
+ /* run the transfer */
+ result = events ? easy_events(multi) : easy_transfer(multi);
+
+ /* ignoring the return code isn't nice, but atm we can't really handle
+ a failure here, room for future improvement! */
+ (void)curl_multi_remove_handle(multi, data);
+
+ sigpipe_restore(&pipe_st);
+
+ /* The multi handle is kept alive, owned by the easy handle */
+ return result;
+}
+
+
+/*
+ * curl_easy_perform() is the external interface that performs a blocking
+ * transfer as previously setup.
+ */
+CURLcode curl_easy_perform(struct Curl_easy *data)
+{
+ return easy_perform(data, FALSE);
+}
+
+#ifdef CURLDEBUG
+/*
+ * curl_easy_perform_ev() is the external interface that performs a blocking
+ * transfer using the event-based API internally.
+ */
+CURLcode curl_easy_perform_ev(struct Curl_easy *data)
+{
+ return easy_perform(data, TRUE);
+}
+
+#endif
+
+/*
+ * curl_easy_cleanup() is the external interface to cleaning/freeing the given
+ * easy handle.
+ */
+void curl_easy_cleanup(struct Curl_easy *data)
+{
+ SIGPIPE_VARIABLE(pipe_st);
+
+ if(!data)
+ return;
+
+ sigpipe_ignore(data, &pipe_st);
+ Curl_close(&data);
+ sigpipe_restore(&pipe_st);
+}
+
+/*
+ * curl_easy_getinfo() is an external interface that allows an app to retrieve
+ * information from a performed transfer and similar.
+ */
+#undef curl_easy_getinfo
+CURLcode curl_easy_getinfo(struct Curl_easy *data, CURLINFO info, ...)
+{
+ va_list arg;
+ void *paramp;
+ CURLcode result;
+
+ va_start(arg, info);
+ paramp = va_arg(arg, void *);
+
+ result = Curl_getinfo(data, info, paramp);
+
+ va_end(arg);
+ return result;
+}
+
+static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src)
+{
+ CURLcode result = CURLE_OK;
+ enum dupstring i;
+ enum dupblob j;
+
+ /* Copy src->set into dst->set first, then deal with the strings
+ afterwards */
+ dst->set = src->set;
+ Curl_mime_initpart(&dst->set.mimepost);
+
+ /* clear all string pointers first */
+ memset(dst->set.str, 0, STRING_LAST * sizeof(char *));
+
+ /* duplicate all strings */
+ for(i = (enum dupstring)0; i< STRING_LASTZEROTERMINATED; i++) {
+ result = Curl_setstropt(&dst->set.str[i], src->set.str[i]);
+ if(result)
+ return result;
+ }
+
+ /* clear all blob pointers first */
+ memset(dst->set.blobs, 0, BLOB_LAST * sizeof(struct curl_blob *));
+ /* duplicate all blobs */
+ for(j = (enum dupblob)0; j < BLOB_LAST; j++) {
+ result = Curl_setblobopt(&dst->set.blobs[j], src->set.blobs[j]);
+ if(result)
+ return result;
+ }
+
+ /* duplicate memory areas pointed to */
+ i = STRING_COPYPOSTFIELDS;
+ if(src->set.postfieldsize && src->set.str[i]) {
+ /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */
+ dst->set.str[i] = Curl_memdup(src->set.str[i],
+ curlx_sotouz(src->set.postfieldsize));
+ if(!dst->set.str[i])
+ return CURLE_OUT_OF_MEMORY;
+ /* point to the new copy */
+ dst->set.postfields = dst->set.str[i];
+ }
+
+ /* Duplicate mime data. */
+ result = Curl_mime_duppart(dst, &dst->set.mimepost, &src->set.mimepost);
+
+ if(src->set.resolve)
+ dst->state.resolve = dst->set.resolve;
+
+ return result;
+}
+
+/*
+ * curl_easy_duphandle() is an external interface to allow duplication of a
+ * given input easy handle. The returned handle will be a new working handle
+ * with all options set exactly as the input source handle.
+ */
+struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
+{
+ struct Curl_easy *outcurl = calloc(1, sizeof(struct Curl_easy));
+ if(!outcurl)
+ goto fail;
+
+ /*
+ * We setup a few buffers we need. We should probably make them
+ * get setup on-demand in the code, as that would probably decrease
+ * the likeliness of us forgetting to init a buffer here in the future.
+ */
+ outcurl->set.buffer_size = data->set.buffer_size;
+
+ /* copy all userdefined values */
+ if(dupset(outcurl, data))
+ goto fail;
+
+ Curl_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER);
+
+ /* the connection cache is setup on demand */
+ outcurl->state.conn_cache = NULL;
+ outcurl->state.lastconnect_id = -1;
+
+ outcurl->progress.flags = data->progress.flags;
+ outcurl->progress.callback = data->progress.callback;
+
+#ifndef CURL_DISABLE_COOKIES
+ if(data->cookies) {
+ /* If cookies are enabled in the parent handle, we enable them
+ in the clone as well! */
+ outcurl->cookies = Curl_cookie_init(data,
+ data->cookies->filename,
+ outcurl->cookies,
+ data->set.cookiesession);
+ if(!outcurl->cookies)
+ goto fail;
+ }
+
+ /* duplicate all values in 'change' */
+ if(data->state.cookielist) {
+ outcurl->state.cookielist =
+ Curl_slist_duplicate(data->state.cookielist);
+ if(!outcurl->state.cookielist)
+ goto fail;
+ }
+#endif
+
+ if(data->state.url) {
+ outcurl->state.url = strdup(data->state.url);
+ if(!outcurl->state.url)
+ goto fail;
+ outcurl->state.url_alloc = TRUE;
+ }
+
+ if(data->state.referer) {
+ outcurl->state.referer = strdup(data->state.referer);
+ if(!outcurl->state.referer)
+ goto fail;
+ outcurl->state.referer_alloc = TRUE;
+ }
+
+ /* Reinitialize an SSL engine for the new handle
+ * note: the engine name has already been copied by dupset */
+ if(outcurl->set.str[STRING_SSL_ENGINE]) {
+ if(Curl_ssl_set_engine(outcurl, outcurl->set.str[STRING_SSL_ENGINE]))
+ goto fail;
+ }
+
+#ifndef CURL_DISABLE_ALTSVC
+ if(data->asi) {
+ outcurl->asi = Curl_altsvc_init();
+ if(!outcurl->asi)
+ goto fail;
+ if(outcurl->set.str[STRING_ALTSVC])
+ (void)Curl_altsvc_load(outcurl->asi, outcurl->set.str[STRING_ALTSVC]);
+ }
+#endif
+#ifndef CURL_DISABLE_HSTS
+ if(data->hsts) {
+ outcurl->hsts = Curl_hsts_init();
+ if(!outcurl->hsts)
+ goto fail;
+ if(outcurl->set.str[STRING_HSTS])
+ (void)Curl_hsts_loadfile(outcurl,
+ outcurl->hsts, outcurl->set.str[STRING_HSTS]);
+ (void)Curl_hsts_loadcb(outcurl, outcurl->hsts);
+ }
+#endif
+ /* Clone the resolver handle, if present, for the new handle */
+ if(Curl_resolver_duphandle(outcurl,
+ &outcurl->state.async.resolver,
+ data->state.async.resolver))
+ goto fail;
+
+#ifdef USE_ARES
+ {
+ CURLcode rc;
+
+ rc = Curl_set_dns_servers(outcurl, data->set.str[STRING_DNS_SERVERS]);
+ if(rc && rc != CURLE_NOT_BUILT_IN)
+ goto fail;
+
+ rc = Curl_set_dns_interface(outcurl, data->set.str[STRING_DNS_INTERFACE]);
+ if(rc && rc != CURLE_NOT_BUILT_IN)
+ goto fail;
+
+ rc = Curl_set_dns_local_ip4(outcurl, data->set.str[STRING_DNS_LOCAL_IP4]);
+ if(rc && rc != CURLE_NOT_BUILT_IN)
+ goto fail;
+
+ rc = Curl_set_dns_local_ip6(outcurl, data->set.str[STRING_DNS_LOCAL_IP6]);
+ if(rc && rc != CURLE_NOT_BUILT_IN)
+ goto fail;
+ }
+#endif /* USE_ARES */
+
+ Curl_initinfo(outcurl);
+
+ outcurl->magic = CURLEASY_MAGIC_NUMBER;
+
+ /* we reach this point and thus we are OK */
+
+ return outcurl;
+
+ fail:
+
+ if(outcurl) {
+#ifndef CURL_DISABLE_COOKIES
+ curl_slist_free_all(outcurl->state.cookielist);
+ outcurl->state.cookielist = NULL;
+#endif
+ Curl_safefree(outcurl->state.buffer);
+ Curl_dyn_free(&outcurl->state.headerb);
+ Curl_safefree(outcurl->state.url);
+ Curl_safefree(outcurl->state.referer);
+ Curl_altsvc_cleanup(&outcurl->asi);
+ Curl_hsts_cleanup(&outcurl->hsts);
+ Curl_freeset(outcurl);
+ free(outcurl);
+ }
+
+ return NULL;
+}
+
+/*
+ * curl_easy_reset() is an external interface that allows an app to re-
+ * initialize a session handle to the default values.
+ */
+void curl_easy_reset(struct Curl_easy *data)
+{
+ Curl_free_request_state(data);
+
+ /* zero out UserDefined data: */
+ Curl_freeset(data);
+ memset(&data->set, 0, sizeof(struct UserDefined));
+ (void)Curl_init_userdefined(data);
+
+ /* zero out Progress data: */
+ memset(&data->progress, 0, sizeof(struct Progress));
+
+ /* zero out PureInfo data: */
+ Curl_initinfo(data);
+
+ data->progress.flags |= PGRS_HIDE;
+ data->state.current_speed = -1; /* init to negative == impossible */
+ data->state.retrycount = 0; /* reset the retry counter */
+
+ /* zero out authentication data: */
+ memset(&data->state.authhost, 0, sizeof(struct auth));
+ memset(&data->state.authproxy, 0, sizeof(struct auth));
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+ Curl_http_auth_cleanup_digest(data);
+#endif
+}
+
+/*
+ * curl_easy_pause() allows an application to pause or unpause a specific
+ * transfer and direction. This function sets the full new state for the
+ * current connection this easy handle operates on.
+ *
+ * NOTE: if you have the receiving paused and you call this function to remove
+ * the pausing, you may get your write callback called at this point.
+ *
+ * Action is a bitmask consisting of CURLPAUSE_* bits in curl/curl.h
+ *
+ * NOTE: This is one of few API functions that are allowed to be called from
+ * within a callback.
+ */
+CURLcode curl_easy_pause(struct Curl_easy *data, int action)
+{
+ struct SingleRequest *k;
+ CURLcode result = CURLE_OK;
+ int oldstate;
+ int newstate;
+
+ if(!GOOD_EASY_HANDLE(data) || !data->conn)
+ /* crazy input, don't continue */
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ k = &data->req;
+ oldstate = k->keepon & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE);
+
+ /* first switch off both pause bits then set the new pause bits */
+ newstate = (k->keepon &~ (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) |
+ ((action & CURLPAUSE_RECV)?KEEP_RECV_PAUSE:0) |
+ ((action & CURLPAUSE_SEND)?KEEP_SEND_PAUSE:0);
+
+ if((newstate & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) == oldstate) {
+ /* Not changing any pause state, return */
+ DEBUGF(infof(data, "pause: no change, early return"));
+ return CURLE_OK;
+ }
+
+ /* Unpause parts in active mime tree. */
+ if((k->keepon & ~newstate & KEEP_SEND_PAUSE) &&
+ (data->mstate == MSTATE_PERFORMING ||
+ data->mstate == MSTATE_RATELIMITING) &&
+ data->state.fread_func == (curl_read_callback) Curl_mime_read) {
+ Curl_mime_unpause(data->state.in);
+ }
+
+ /* put it back in the keepon */
+ k->keepon = newstate;
+
+ if(!(newstate & KEEP_RECV_PAUSE)) {
+ Curl_http2_stream_pause(data, FALSE);
+
+ if(data->state.tempcount) {
+ /* there are buffers for sending that can be delivered as the receive
+ pausing is lifted! */
+ unsigned int i;
+ unsigned int count = data->state.tempcount;
+ struct tempbuf writebuf[3]; /* there can only be three */
+
+ /* copy the structs to allow for immediate re-pausing */
+ for(i = 0; i < data->state.tempcount; i++) {
+ writebuf[i] = data->state.tempwrite[i];
+ Curl_dyn_init(&data->state.tempwrite[i].b, DYN_PAUSE_BUFFER);
+ }
+ data->state.tempcount = 0;
+
+ for(i = 0; i < count; i++) {
+ /* even if one function returns error, this loops through and frees
+ all buffers */
+ if(!result)
+ result = Curl_client_write(data, writebuf[i].type,
+ Curl_dyn_ptr(&writebuf[i].b),
+ Curl_dyn_len(&writebuf[i].b));
+ Curl_dyn_free(&writebuf[i].b);
+ }
+
+ if(result)
+ return result;
+ }
+ }
+
+#ifdef USE_HYPER
+ if(!(newstate & KEEP_SEND_PAUSE)) {
+ /* need to wake the send body waker */
+ if(data->hyp.send_body_waker) {
+ hyper_waker_wake(data->hyp.send_body_waker);
+ data->hyp.send_body_waker = NULL;
+ }
+ }
+#endif
+
+ /* if there's no error and we're not pausing both directions, we want
+ to have this handle checked soon */
+ if((newstate & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) !=
+ (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) {
+ Curl_expire(data, 0, EXPIRE_RUN_NOW); /* get this handle going again */
+
+ /* reset the too-slow time keeper */
+ data->state.keeps_speed.tv_sec = 0;
+
+ if(!data->state.tempcount)
+ /* if not pausing again, force a recv/send check of this connection as
+ the data might've been read off the socket already */
+ data->conn->cselect_bits = CURL_CSELECT_IN | CURL_CSELECT_OUT;
+ if(data->multi) {
+ if(Curl_update_timer(data->multi))
+ return CURLE_ABORTED_BY_CALLBACK;
+ }
+ }
+
+ if(!data->state.done)
+ /* This transfer may have been moved in or out of the bundle, update the
+ corresponding socket callback, if used */
+ result = Curl_updatesocket(data);
+
+ return result;
+}
+
+
+static CURLcode easy_connection(struct Curl_easy *data, curl_socket_t *sfd,
+ struct connectdata **connp)
+{
+ if(!data)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ /* only allow these to be called on handles with CURLOPT_CONNECT_ONLY */
+ if(!data->set.connect_only) {
+ failf(data, "CONNECT_ONLY is required");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
+
+ *sfd = Curl_getconnectinfo(data, connp);
+
+ if(*sfd == CURL_SOCKET_BAD) {
+ failf(data, "Failed to get recent socket");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Receives data from the connected socket. Use after successful
+ * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
+ * Returns CURLE_OK on success, error code on error.
+ */
+CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen,
+ size_t *n)
+{
+ curl_socket_t sfd;
+ CURLcode result;
+ ssize_t n1;
+ struct connectdata *c;
+
+ if(Curl_is_in_callback(data))
+ return CURLE_RECURSIVE_API_CALL;
+
+ result = easy_connection(data, &sfd, &c);
+ if(result)
+ return result;
+
+ if(!data->conn)
+ /* on first invoke, the transfer has been detached from the connection and
+ needs to be reattached */
+ Curl_attach_connection(data, c);
+
+ *n = 0;
+ result = Curl_read(data, sfd, buffer, buflen, &n1);
+
+ if(result)
+ return result;
+
+ *n = (size_t)n1;
+
+ return CURLE_OK;
+}
+
+/*
+ * Sends data over the connected socket.
+ *
+ * This is the private internal version of curl_easy_send()
+ */
+CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer,
+ size_t buflen, ssize_t *n)
+{
+ curl_socket_t sfd;
+ CURLcode result;
+ ssize_t n1;
+ struct connectdata *c = NULL;
+ SIGPIPE_VARIABLE(pipe_st);
+
+ result = easy_connection(data, &sfd, &c);
+ if(result)
+ return result;
+
+ if(!data->conn)
+ /* on first invoke, the transfer has been detached from the connection and
+ needs to be reattached */
+ Curl_attach_connection(data, c);
+
+ *n = 0;
+ sigpipe_ignore(data, &pipe_st);
+ result = Curl_write(data, sfd, buffer, buflen, &n1);
+ sigpipe_restore(&pipe_st);
+
+ if(n1 == -1)
+ return CURLE_SEND_ERROR;
+
+ /* detect EAGAIN */
+ if(!result && !n1)
+ return CURLE_AGAIN;
+
+ *n = n1;
+
+ return result;
+}
+
+/*
+ * Sends data over the connected socket. Use after successful
+ * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
+ */
+CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer,
+ size_t buflen, size_t *n)
+{
+ ssize_t written = 0;
+ CURLcode result;
+ if(Curl_is_in_callback(data))
+ return CURLE_RECURSIVE_API_CALL;
+
+ result = Curl_senddata(data, buffer, buflen, &written);
+ *n = (size_t)written;
+ return result;
+}
+
+/*
+ * Wrapper to call functions in Curl_conncache_foreach()
+ *
+ * Returns always 0.
+ */
+static int conn_upkeep(struct Curl_easy *data,
+ struct connectdata *conn,
+ void *param)
+{
+ /* Param is unused. */
+ (void)param;
+
+ if(conn->handler->connection_check) {
+ /* briefly attach the connection to this transfer for the purpose of
+ checking it */
+ Curl_attach_connection(data, conn);
+
+ /* Do a protocol-specific keepalive check on the connection. */
+ conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE);
+ /* detach the connection again */
+ Curl_detach_connection(data);
+ }
+
+ return 0; /* continue iteration */
+}
+
+static CURLcode upkeep(struct conncache *conn_cache, void *data)
+{
+ /* Loop over every connection and make connection alive. */
+ Curl_conncache_foreach(data,
+ conn_cache,
+ data,
+ conn_upkeep);
+ return CURLE_OK;
+}
+
+/*
+ * Performs connection upkeep for the given session handle.
+ */
+CURLcode curl_easy_upkeep(struct Curl_easy *data)
+{
+ /* Verify that we got an easy handle we can work with. */
+ if(!GOOD_EASY_HANDLE(data))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ if(data->multi_easy) {
+ /* Use the common function to keep connections alive. */
+ return upkeep(&data->multi_easy->conn_cache, data);
+ }
+ else {
+ /* No connections, so just return success */
+ return CURLE_OK;
+ }
+}
diff --git a/libs/libcurl/src/easy_lock.h b/libs/libcurl/src/easy_lock.h index d96e56b8d8..802b7d6b42 100644 --- a/libs/libcurl/src/easy_lock.h +++ b/libs/libcurl/src/easy_lock.h @@ -1,105 +1,105 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -#define GLOBAL_INIT_IS_THREADSAFE - -#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 - -#ifdef __MINGW32__ -#ifndef __MINGW64_VERSION_MAJOR -#if (__MINGW32_MAJOR_VERSION < 5) || \ - (__MINGW32_MAJOR_VERSION == 5 && __MINGW32_MINOR_VERSION == 0) -/* mingw >= 5.0.1 defines SRWLOCK, and slightly different from MS define */ -typedef PVOID SRWLOCK, *PSRWLOCK; -#endif -#endif -#ifndef SRWLOCK_INIT -#define SRWLOCK_INIT NULL -#endif -#endif /* __MINGW32__ */ - -#define curl_simple_lock SRWLOCK -#define CURL_SIMPLE_LOCK_INIT SRWLOCK_INIT - -#define curl_simple_lock_lock(m) AcquireSRWLockExclusive(m) -#define curl_simple_lock_unlock(m) ReleaseSRWLockExclusive(m) - -#elif defined(HAVE_ATOMIC) && defined(HAVE_STDATOMIC_H) -#include <stdatomic.h> -#if defined(HAVE_SCHED_YIELD) -#include <sched.h> -#endif - -#define curl_simple_lock atomic_int -#define CURL_SIMPLE_LOCK_INIT 0 - -/* a clang-thing */ -#ifndef __has_builtin -#define __has_builtin(x) 0 -#endif - -#ifndef __INTEL_COMPILER -/* The Intel compiler tries to look like GCC *and* clang *and* lies in its - __has_builtin() function, so override it. */ - -/* if GCC on i386/x86_64 or if the built-in is present */ -#if ( (defined(__GNUC__) && !defined(__clang__)) && \ - (defined(__i386__) || defined(__x86_64__))) || \ - __has_builtin(__builtin_ia32_pause) -#define HAVE_BUILTIN_IA32_PAUSE -#endif - -#endif - -static inline void curl_simple_lock_lock(curl_simple_lock *lock) -{ - for(;;) { - if(!atomic_exchange_explicit(lock, true, memory_order_acquire)) - break; - /* Reduce cache coherency traffic */ - while(atomic_load_explicit(lock, memory_order_relaxed)) { - /* Reduce load (not mandatory) */ -#ifdef HAVE_BUILTIN_IA32_PAUSE - __builtin_ia32_pause(); -#elif defined(__aarch64__) - __asm__ volatile("yield" ::: "memory"); -#elif defined(HAVE_SCHED_YIELD) - sched_yield(); -#endif - } - } -} - -static inline void curl_simple_lock_unlock(curl_simple_lock *lock) -{ - atomic_store_explicit(lock, false, memory_order_release); -} - -#else - -#undef GLOBAL_INIT_IS_THREADSAFE - -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+#define GLOBAL_INIT_IS_THREADSAFE
+
+#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600
+
+#ifdef __MINGW32__
+#ifndef __MINGW64_VERSION_MAJOR
+#if (__MINGW32_MAJOR_VERSION < 5) || \
+ (__MINGW32_MAJOR_VERSION == 5 && __MINGW32_MINOR_VERSION == 0)
+/* mingw >= 5.0.1 defines SRWLOCK, and slightly different from MS define */
+typedef PVOID SRWLOCK, *PSRWLOCK;
+#endif
+#endif
+#ifndef SRWLOCK_INIT
+#define SRWLOCK_INIT NULL
+#endif
+#endif /* __MINGW32__ */
+
+#define curl_simple_lock SRWLOCK
+#define CURL_SIMPLE_LOCK_INIT SRWLOCK_INIT
+
+#define curl_simple_lock_lock(m) AcquireSRWLockExclusive(m)
+#define curl_simple_lock_unlock(m) ReleaseSRWLockExclusive(m)
+
+#elif defined(HAVE_ATOMIC) && defined(HAVE_STDATOMIC_H)
+#include <stdatomic.h>
+#if defined(HAVE_SCHED_YIELD)
+#include <sched.h>
+#endif
+
+#define curl_simple_lock atomic_int
+#define CURL_SIMPLE_LOCK_INIT 0
+
+/* a clang-thing */
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#ifndef __INTEL_COMPILER
+/* The Intel compiler tries to look like GCC *and* clang *and* lies in its
+ __has_builtin() function, so override it. */
+
+/* if GCC on i386/x86_64 or if the built-in is present */
+#if ( (defined(__GNUC__) && !defined(__clang__)) && \
+ (defined(__i386__) || defined(__x86_64__))) || \
+ __has_builtin(__builtin_ia32_pause)
+#define HAVE_BUILTIN_IA32_PAUSE
+#endif
+
+#endif
+
+static inline void curl_simple_lock_lock(curl_simple_lock *lock)
+{
+ for(;;) {
+ if(!atomic_exchange_explicit(lock, true, memory_order_acquire))
+ break;
+ /* Reduce cache coherency traffic */
+ while(atomic_load_explicit(lock, memory_order_relaxed)) {
+ /* Reduce load (not mandatory) */
+#ifdef HAVE_BUILTIN_IA32_PAUSE
+ __builtin_ia32_pause();
+#elif defined(__aarch64__)
+ __asm__ volatile("yield" ::: "memory");
+#elif defined(HAVE_SCHED_YIELD)
+ sched_yield();
+#endif
+ }
+ }
+}
+
+static inline void curl_simple_lock_unlock(curl_simple_lock *lock)
+{
+ atomic_store_explicit(lock, false, memory_order_release);
+}
+
+#else
+
+#undef GLOBAL_INIT_IS_THREADSAFE
+
+#endif
diff --git a/libs/libcurl/src/easygetopt.c b/libs/libcurl/src/easygetopt.c index a639bb3758..ed07b6e9e4 100644 --- a/libs/libcurl/src/easygetopt.c +++ b/libs/libcurl/src/easygetopt.c @@ -1,98 +1,98 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ | | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * ___|___/|_| ______| - * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel.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 "strcase.h" -#include "easyoptions.h" - -#ifndef CURL_DISABLE_GETOPTIONS - -/* Lookups easy options at runtime */ -static struct curl_easyoption *lookup(const char *name, CURLoption id) -{ - DEBUGASSERT(name || id); - DEBUGASSERT(!Curl_easyopts_check()); - if(name || id) { - struct curl_easyoption *o = &Curl_easyopts[0]; - do { - if(name) { - if(strcasecompare(o->name, name)) - return o; - } - else { - if((o->id == id) && !(o->flags & CURLOT_FLAG_ALIAS)) - /* don't match alias options */ - return o; - } - o++; - } while(o->name); - } - return NULL; -} - -const struct curl_easyoption *curl_easy_option_by_name(const char *name) -{ - /* when name is used, the id argument is ignored */ - return lookup(name, CURLOPT_LASTENTRY); -} - -const struct curl_easyoption *curl_easy_option_by_id(CURLoption id) -{ - return lookup(NULL, id); -} - -/* Iterates over available options */ -const struct curl_easyoption * -curl_easy_option_next(const struct curl_easyoption *prev) -{ - if(prev && prev->name) { - prev++; - if(prev->name) - return prev; - } - else if(!prev) - return &Curl_easyopts[0]; - return NULL; -} - -#else -const struct curl_easyoption *curl_easy_option_by_name(const char *name) -{ - (void)name; - return NULL; -} - -const struct curl_easyoption *curl_easy_option_by_id (CURLoption id) -{ - (void)id; - return NULL; -} - -const struct curl_easyoption * -curl_easy_option_next(const struct curl_easyoption *prev) -{ - (void)prev; - return NULL; -} -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ | |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * ___|___/|_| ______|
+ *
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel.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 "strcase.h"
+#include "easyoptions.h"
+
+#ifndef CURL_DISABLE_GETOPTIONS
+
+/* Lookups easy options at runtime */
+static struct curl_easyoption *lookup(const char *name, CURLoption id)
+{
+ DEBUGASSERT(name || id);
+ DEBUGASSERT(!Curl_easyopts_check());
+ if(name || id) {
+ struct curl_easyoption *o = &Curl_easyopts[0];
+ do {
+ if(name) {
+ if(strcasecompare(o->name, name))
+ return o;
+ }
+ else {
+ if((o->id == id) && !(o->flags & CURLOT_FLAG_ALIAS))
+ /* don't match alias options */
+ return o;
+ }
+ o++;
+ } while(o->name);
+ }
+ return NULL;
+}
+
+const struct curl_easyoption *curl_easy_option_by_name(const char *name)
+{
+ /* when name is used, the id argument is ignored */
+ return lookup(name, CURLOPT_LASTENTRY);
+}
+
+const struct curl_easyoption *curl_easy_option_by_id(CURLoption id)
+{
+ return lookup(NULL, id);
+}
+
+/* Iterates over available options */
+const struct curl_easyoption *
+curl_easy_option_next(const struct curl_easyoption *prev)
+{
+ if(prev && prev->name) {
+ prev++;
+ if(prev->name)
+ return prev;
+ }
+ else if(!prev)
+ return &Curl_easyopts[0];
+ return NULL;
+}
+
+#else
+const struct curl_easyoption *curl_easy_option_by_name(const char *name)
+{
+ (void)name;
+ return NULL;
+}
+
+const struct curl_easyoption *curl_easy_option_by_id (CURLoption id)
+{
+ (void)id;
+ return NULL;
+}
+
+const struct curl_easyoption *
+curl_easy_option_next(const struct curl_easyoption *prev)
+{
+ (void)prev;
+ return NULL;
+}
+#endif
diff --git a/libs/libcurl/src/easyif.h b/libs/libcurl/src/easyif.h index 205382cd07..40132274e1 100644 --- a/libs/libcurl/src/easyif.h +++ b/libs/libcurl/src/easyif.h @@ -1,37 +1,37 @@ -#ifndef HEADER_CURL_EASYIF_H -#define HEADER_CURL_EASYIF_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - -/* - * Prototypes for library-wide functions provided by easy.c - */ -CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer, - size_t buflen, ssize_t *n); - -#ifdef CURLDEBUG -CURL_EXTERN CURLcode curl_easy_perform_ev(struct Curl_easy *easy); -#endif - -#endif /* HEADER_CURL_EASYIF_H */ +#ifndef HEADER_CURL_EASYIF_H
+#define HEADER_CURL_EASYIF_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+/*
+ * Prototypes for library-wide functions provided by easy.c
+ */
+CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer,
+ size_t buflen, ssize_t *n);
+
+#ifdef CURLDEBUG
+CURL_EXTERN CURLcode curl_easy_perform_ev(struct Curl_easy *easy);
+#endif
+
+#endif /* HEADER_CURL_EASYIF_H */
diff --git a/libs/libcurl/src/easyoptions.c b/libs/libcurl/src/easyoptions.c index e59b63af7a..c945bdfbd2 100644 --- a/libs/libcurl/src/easyoptions.c +++ b/libs/libcurl/src/easyoptions.c @@ -1,373 +1,375 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ | | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * ___|___/|_| ______| - * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel.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 source code is generated by optiontable.pl - DO NOT EDIT BY HAND */ - -#include "curl_setup.h" -#include "easyoptions.h" - -/* all easy setopt options listed in alphabetical order */ -struct curl_easyoption Curl_easyopts[] = { - {"ABSTRACT_UNIX_SOCKET", CURLOPT_ABSTRACT_UNIX_SOCKET, CURLOT_STRING, 0}, - {"ACCEPTTIMEOUT_MS", CURLOPT_ACCEPTTIMEOUT_MS, CURLOT_LONG, 0}, - {"ACCEPT_ENCODING", CURLOPT_ACCEPT_ENCODING, CURLOT_STRING, 0}, - {"ADDRESS_SCOPE", CURLOPT_ADDRESS_SCOPE, CURLOT_LONG, 0}, - {"ALTSVC", CURLOPT_ALTSVC, CURLOT_STRING, 0}, - {"ALTSVC_CTRL", CURLOPT_ALTSVC_CTRL, CURLOT_LONG, 0}, - {"APPEND", CURLOPT_APPEND, CURLOT_LONG, 0}, - {"AUTOREFERER", CURLOPT_AUTOREFERER, CURLOT_LONG, 0}, - {"AWS_SIGV4", CURLOPT_AWS_SIGV4, CURLOT_STRING, 0}, - {"BUFFERSIZE", CURLOPT_BUFFERSIZE, CURLOT_LONG, 0}, - {"CAINFO", CURLOPT_CAINFO, CURLOT_STRING, 0}, - {"CAINFO_BLOB", CURLOPT_CAINFO_BLOB, CURLOT_BLOB, 0}, - {"CAPATH", CURLOPT_CAPATH, CURLOT_STRING, 0}, - {"CERTINFO", CURLOPT_CERTINFO, CURLOT_LONG, 0}, - {"CHUNK_BGN_FUNCTION", CURLOPT_CHUNK_BGN_FUNCTION, CURLOT_FUNCTION, 0}, - {"CHUNK_DATA", CURLOPT_CHUNK_DATA, CURLOT_CBPTR, 0}, - {"CHUNK_END_FUNCTION", CURLOPT_CHUNK_END_FUNCTION, CURLOT_FUNCTION, 0}, - {"CLOSESOCKETDATA", CURLOPT_CLOSESOCKETDATA, CURLOT_CBPTR, 0}, - {"CLOSESOCKETFUNCTION", CURLOPT_CLOSESOCKETFUNCTION, CURLOT_FUNCTION, 0}, - {"CONNECTTIMEOUT", CURLOPT_CONNECTTIMEOUT, CURLOT_LONG, 0}, - {"CONNECTTIMEOUT_MS", CURLOPT_CONNECTTIMEOUT_MS, CURLOT_LONG, 0}, - {"CONNECT_ONLY", CURLOPT_CONNECT_ONLY, CURLOT_LONG, 0}, - {"CONNECT_TO", CURLOPT_CONNECT_TO, CURLOT_SLIST, 0}, - {"CONV_FROM_NETWORK_FUNCTION", CURLOPT_CONV_FROM_NETWORK_FUNCTION, - CURLOT_FUNCTION, 0}, - {"CONV_FROM_UTF8_FUNCTION", CURLOPT_CONV_FROM_UTF8_FUNCTION, - CURLOT_FUNCTION, 0}, - {"CONV_TO_NETWORK_FUNCTION", CURLOPT_CONV_TO_NETWORK_FUNCTION, - CURLOT_FUNCTION, 0}, - {"COOKIE", CURLOPT_COOKIE, CURLOT_STRING, 0}, - {"COOKIEFILE", CURLOPT_COOKIEFILE, CURLOT_STRING, 0}, - {"COOKIEJAR", CURLOPT_COOKIEJAR, CURLOT_STRING, 0}, - {"COOKIELIST", CURLOPT_COOKIELIST, CURLOT_STRING, 0}, - {"COOKIESESSION", CURLOPT_COOKIESESSION, CURLOT_LONG, 0}, - {"COPYPOSTFIELDS", CURLOPT_COPYPOSTFIELDS, CURLOT_OBJECT, 0}, - {"CRLF", CURLOPT_CRLF, CURLOT_LONG, 0}, - {"CRLFILE", CURLOPT_CRLFILE, CURLOT_STRING, 0}, - {"CURLU", CURLOPT_CURLU, CURLOT_OBJECT, 0}, - {"CUSTOMREQUEST", CURLOPT_CUSTOMREQUEST, CURLOT_STRING, 0}, - {"DEBUGDATA", CURLOPT_DEBUGDATA, CURLOT_CBPTR, 0}, - {"DEBUGFUNCTION", CURLOPT_DEBUGFUNCTION, CURLOT_FUNCTION, 0}, - {"DEFAULT_PROTOCOL", CURLOPT_DEFAULT_PROTOCOL, CURLOT_STRING, 0}, - {"DIRLISTONLY", CURLOPT_DIRLISTONLY, CURLOT_LONG, 0}, - {"DISALLOW_USERNAME_IN_URL", CURLOPT_DISALLOW_USERNAME_IN_URL, - CURLOT_LONG, 0}, - {"DNS_CACHE_TIMEOUT", CURLOPT_DNS_CACHE_TIMEOUT, CURLOT_LONG, 0}, - {"DNS_INTERFACE", CURLOPT_DNS_INTERFACE, CURLOT_STRING, 0}, - {"DNS_LOCAL_IP4", CURLOPT_DNS_LOCAL_IP4, CURLOT_STRING, 0}, - {"DNS_LOCAL_IP6", CURLOPT_DNS_LOCAL_IP6, CURLOT_STRING, 0}, - {"DNS_SERVERS", CURLOPT_DNS_SERVERS, CURLOT_STRING, 0}, - {"DNS_SHUFFLE_ADDRESSES", CURLOPT_DNS_SHUFFLE_ADDRESSES, CURLOT_LONG, 0}, - {"DNS_USE_GLOBAL_CACHE", CURLOPT_DNS_USE_GLOBAL_CACHE, CURLOT_LONG, 0}, - {"DOH_SSL_VERIFYHOST", CURLOPT_DOH_SSL_VERIFYHOST, CURLOT_LONG, 0}, - {"DOH_SSL_VERIFYPEER", CURLOPT_DOH_SSL_VERIFYPEER, CURLOT_LONG, 0}, - {"DOH_SSL_VERIFYSTATUS", CURLOPT_DOH_SSL_VERIFYSTATUS, CURLOT_LONG, 0}, - {"DOH_URL", CURLOPT_DOH_URL, CURLOT_STRING, 0}, - {"EGDSOCKET", CURLOPT_EGDSOCKET, CURLOT_STRING, 0}, - {"ENCODING", CURLOPT_ACCEPT_ENCODING, CURLOT_STRING, CURLOT_FLAG_ALIAS}, - {"ERRORBUFFER", CURLOPT_ERRORBUFFER, CURLOT_OBJECT, 0}, - {"EXPECT_100_TIMEOUT_MS", CURLOPT_EXPECT_100_TIMEOUT_MS, CURLOT_LONG, 0}, - {"FAILONERROR", CURLOPT_FAILONERROR, CURLOT_LONG, 0}, - {"FILE", CURLOPT_WRITEDATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS}, - {"FILETIME", CURLOPT_FILETIME, CURLOT_LONG, 0}, - {"FNMATCH_DATA", CURLOPT_FNMATCH_DATA, CURLOT_CBPTR, 0}, - {"FNMATCH_FUNCTION", CURLOPT_FNMATCH_FUNCTION, CURLOT_FUNCTION, 0}, - {"FOLLOWLOCATION", CURLOPT_FOLLOWLOCATION, CURLOT_LONG, 0}, - {"FORBID_REUSE", CURLOPT_FORBID_REUSE, CURLOT_LONG, 0}, - {"FRESH_CONNECT", CURLOPT_FRESH_CONNECT, CURLOT_LONG, 0}, - {"FTPAPPEND", CURLOPT_APPEND, CURLOT_LONG, CURLOT_FLAG_ALIAS}, - {"FTPLISTONLY", CURLOPT_DIRLISTONLY, CURLOT_LONG, CURLOT_FLAG_ALIAS}, - {"FTPPORT", CURLOPT_FTPPORT, CURLOT_STRING, 0}, - {"FTPSSLAUTH", CURLOPT_FTPSSLAUTH, CURLOT_VALUES, 0}, - {"FTP_ACCOUNT", CURLOPT_FTP_ACCOUNT, CURLOT_STRING, 0}, - {"FTP_ALTERNATIVE_TO_USER", CURLOPT_FTP_ALTERNATIVE_TO_USER, - CURLOT_STRING, 0}, - {"FTP_CREATE_MISSING_DIRS", CURLOPT_FTP_CREATE_MISSING_DIRS, - CURLOT_LONG, 0}, - {"FTP_FILEMETHOD", CURLOPT_FTP_FILEMETHOD, CURLOT_VALUES, 0}, - {"FTP_RESPONSE_TIMEOUT", CURLOPT_SERVER_RESPONSE_TIMEOUT, - CURLOT_LONG, CURLOT_FLAG_ALIAS}, - {"FTP_SKIP_PASV_IP", CURLOPT_FTP_SKIP_PASV_IP, CURLOT_LONG, 0}, - {"FTP_SSL", CURLOPT_USE_SSL, CURLOT_VALUES, CURLOT_FLAG_ALIAS}, - {"FTP_SSL_CCC", CURLOPT_FTP_SSL_CCC, CURLOT_LONG, 0}, - {"FTP_USE_EPRT", CURLOPT_FTP_USE_EPRT, CURLOT_LONG, 0}, - {"FTP_USE_EPSV", CURLOPT_FTP_USE_EPSV, CURLOT_LONG, 0}, - {"FTP_USE_PRET", CURLOPT_FTP_USE_PRET, CURLOT_LONG, 0}, - {"GSSAPI_DELEGATION", CURLOPT_GSSAPI_DELEGATION, CURLOT_VALUES, 0}, - {"HAPPY_EYEBALLS_TIMEOUT_MS", CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, - CURLOT_LONG, 0}, - {"HAPROXYPROTOCOL", CURLOPT_HAPROXYPROTOCOL, CURLOT_LONG, 0}, - {"HEADER", CURLOPT_HEADER, CURLOT_LONG, 0}, - {"HEADERDATA", CURLOPT_HEADERDATA, CURLOT_CBPTR, 0}, - {"HEADERFUNCTION", CURLOPT_HEADERFUNCTION, CURLOT_FUNCTION, 0}, - {"HEADEROPT", CURLOPT_HEADEROPT, CURLOT_VALUES, 0}, - {"HSTS", CURLOPT_HSTS, CURLOT_STRING, 0}, - {"HSTSREADDATA", CURLOPT_HSTSREADDATA, CURLOT_CBPTR, 0}, - {"HSTSREADFUNCTION", CURLOPT_HSTSREADFUNCTION, CURLOT_FUNCTION, 0}, - {"HSTSWRITEDATA", CURLOPT_HSTSWRITEDATA, CURLOT_CBPTR, 0}, - {"HSTSWRITEFUNCTION", CURLOPT_HSTSWRITEFUNCTION, CURLOT_FUNCTION, 0}, - {"HSTS_CTRL", CURLOPT_HSTS_CTRL, CURLOT_LONG, 0}, - {"HTTP09_ALLOWED", CURLOPT_HTTP09_ALLOWED, CURLOT_LONG, 0}, - {"HTTP200ALIASES", CURLOPT_HTTP200ALIASES, CURLOT_SLIST, 0}, - {"HTTPAUTH", CURLOPT_HTTPAUTH, CURLOT_VALUES, 0}, - {"HTTPGET", CURLOPT_HTTPGET, CURLOT_LONG, 0}, - {"HTTPHEADER", CURLOPT_HTTPHEADER, CURLOT_SLIST, 0}, - {"HTTPPOST", CURLOPT_HTTPPOST, CURLOT_OBJECT, 0}, - {"HTTPPROXYTUNNEL", CURLOPT_HTTPPROXYTUNNEL, CURLOT_LONG, 0}, - {"HTTP_CONTENT_DECODING", CURLOPT_HTTP_CONTENT_DECODING, CURLOT_LONG, 0}, - {"HTTP_TRANSFER_DECODING", CURLOPT_HTTP_TRANSFER_DECODING, CURLOT_LONG, 0}, - {"HTTP_VERSION", CURLOPT_HTTP_VERSION, CURLOT_VALUES, 0}, - {"IGNORE_CONTENT_LENGTH", CURLOPT_IGNORE_CONTENT_LENGTH, CURLOT_LONG, 0}, - {"INFILE", CURLOPT_READDATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS}, - {"INFILESIZE", CURLOPT_INFILESIZE, CURLOT_LONG, 0}, - {"INFILESIZE_LARGE", CURLOPT_INFILESIZE_LARGE, CURLOT_OFF_T, 0}, - {"INTERFACE", CURLOPT_INTERFACE, CURLOT_STRING, 0}, - {"INTERLEAVEDATA", CURLOPT_INTERLEAVEDATA, CURLOT_CBPTR, 0}, - {"INTERLEAVEFUNCTION", CURLOPT_INTERLEAVEFUNCTION, CURLOT_FUNCTION, 0}, - {"IOCTLDATA", CURLOPT_IOCTLDATA, CURLOT_CBPTR, 0}, - {"IOCTLFUNCTION", CURLOPT_IOCTLFUNCTION, CURLOT_FUNCTION, 0}, - {"IPRESOLVE", CURLOPT_IPRESOLVE, CURLOT_VALUES, 0}, - {"ISSUERCERT", CURLOPT_ISSUERCERT, CURLOT_STRING, 0}, - {"ISSUERCERT_BLOB", CURLOPT_ISSUERCERT_BLOB, CURLOT_BLOB, 0}, - {"KEEP_SENDING_ON_ERROR", CURLOPT_KEEP_SENDING_ON_ERROR, CURLOT_LONG, 0}, - {"KEYPASSWD", CURLOPT_KEYPASSWD, CURLOT_STRING, 0}, - {"KRB4LEVEL", CURLOPT_KRBLEVEL, CURLOT_STRING, CURLOT_FLAG_ALIAS}, - {"KRBLEVEL", CURLOPT_KRBLEVEL, CURLOT_STRING, 0}, - {"LOCALPORT", CURLOPT_LOCALPORT, CURLOT_LONG, 0}, - {"LOCALPORTRANGE", CURLOPT_LOCALPORTRANGE, CURLOT_LONG, 0}, - {"LOGIN_OPTIONS", CURLOPT_LOGIN_OPTIONS, CURLOT_STRING, 0}, - {"LOW_SPEED_LIMIT", CURLOPT_LOW_SPEED_LIMIT, CURLOT_LONG, 0}, - {"LOW_SPEED_TIME", CURLOPT_LOW_SPEED_TIME, CURLOT_LONG, 0}, - {"MAIL_AUTH", CURLOPT_MAIL_AUTH, CURLOT_STRING, 0}, - {"MAIL_FROM", CURLOPT_MAIL_FROM, CURLOT_STRING, 0}, - {"MAIL_RCPT", CURLOPT_MAIL_RCPT, CURLOT_SLIST, 0}, - {"MAIL_RCPT_ALLLOWFAILS", CURLOPT_MAIL_RCPT_ALLLOWFAILS, CURLOT_LONG, 0}, - {"MAXAGE_CONN", CURLOPT_MAXAGE_CONN, CURLOT_LONG, 0}, - {"MAXCONNECTS", CURLOPT_MAXCONNECTS, CURLOT_LONG, 0}, - {"MAXFILESIZE", CURLOPT_MAXFILESIZE, CURLOT_LONG, 0}, - {"MAXFILESIZE_LARGE", CURLOPT_MAXFILESIZE_LARGE, CURLOT_OFF_T, 0}, - {"MAXLIFETIME_CONN", CURLOPT_MAXLIFETIME_CONN, CURLOT_LONG, 0}, - {"MAXREDIRS", CURLOPT_MAXREDIRS, CURLOT_LONG, 0}, - {"MAX_RECV_SPEED_LARGE", CURLOPT_MAX_RECV_SPEED_LARGE, CURLOT_OFF_T, 0}, - {"MAX_SEND_SPEED_LARGE", CURLOPT_MAX_SEND_SPEED_LARGE, CURLOT_OFF_T, 0}, - {"MIMEPOST", CURLOPT_MIMEPOST, CURLOT_OBJECT, 0}, - {"MIME_OPTIONS", CURLOPT_MIME_OPTIONS, CURLOT_LONG, 0}, - {"NETRC", CURLOPT_NETRC, CURLOT_VALUES, 0}, - {"NETRC_FILE", CURLOPT_NETRC_FILE, CURLOT_STRING, 0}, - {"NEW_DIRECTORY_PERMS", CURLOPT_NEW_DIRECTORY_PERMS, CURLOT_LONG, 0}, - {"NEW_FILE_PERMS", CURLOPT_NEW_FILE_PERMS, CURLOT_LONG, 0}, - {"NOBODY", CURLOPT_NOBODY, CURLOT_LONG, 0}, - {"NOPROGRESS", CURLOPT_NOPROGRESS, CURLOT_LONG, 0}, - {"NOPROXY", CURLOPT_NOPROXY, CURLOT_STRING, 0}, - {"NOSIGNAL", CURLOPT_NOSIGNAL, CURLOT_LONG, 0}, - {"OPENSOCKETDATA", CURLOPT_OPENSOCKETDATA, CURLOT_CBPTR, 0}, - {"OPENSOCKETFUNCTION", CURLOPT_OPENSOCKETFUNCTION, CURLOT_FUNCTION, 0}, - {"PASSWORD", CURLOPT_PASSWORD, CURLOT_STRING, 0}, - {"PATH_AS_IS", CURLOPT_PATH_AS_IS, CURLOT_LONG, 0}, - {"PINNEDPUBLICKEY", CURLOPT_PINNEDPUBLICKEY, CURLOT_STRING, 0}, - {"PIPEWAIT", CURLOPT_PIPEWAIT, CURLOT_LONG, 0}, - {"PORT", CURLOPT_PORT, CURLOT_LONG, 0}, - {"POST", CURLOPT_POST, CURLOT_LONG, 0}, - {"POST301", CURLOPT_POSTREDIR, CURLOT_VALUES, CURLOT_FLAG_ALIAS}, - {"POSTFIELDS", CURLOPT_POSTFIELDS, CURLOT_OBJECT, 0}, - {"POSTFIELDSIZE", CURLOPT_POSTFIELDSIZE, CURLOT_LONG, 0}, - {"POSTFIELDSIZE_LARGE", CURLOPT_POSTFIELDSIZE_LARGE, CURLOT_OFF_T, 0}, - {"POSTQUOTE", CURLOPT_POSTQUOTE, CURLOT_SLIST, 0}, - {"POSTREDIR", CURLOPT_POSTREDIR, CURLOT_VALUES, 0}, - {"PREQUOTE", CURLOPT_PREQUOTE, CURLOT_SLIST, 0}, - {"PREREQDATA", CURLOPT_PREREQDATA, CURLOT_CBPTR, 0}, - {"PREREQFUNCTION", CURLOPT_PREREQFUNCTION, CURLOT_FUNCTION, 0}, - {"PRE_PROXY", CURLOPT_PRE_PROXY, CURLOT_STRING, 0}, - {"PRIVATE", CURLOPT_PRIVATE, CURLOT_OBJECT, 0}, - {"PROGRESSDATA", CURLOPT_XFERINFODATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS}, - {"PROGRESSFUNCTION", CURLOPT_PROGRESSFUNCTION, CURLOT_FUNCTION, 0}, - {"PROTOCOLS", CURLOPT_PROTOCOLS, CURLOT_LONG, 0}, - {"PROTOCOLS_STR", CURLOPT_PROTOCOLS_STR, CURLOT_STRING, 0}, - {"PROXY", CURLOPT_PROXY, CURLOT_STRING, 0}, - {"PROXYAUTH", CURLOPT_PROXYAUTH, CURLOT_VALUES, 0}, - {"PROXYHEADER", CURLOPT_PROXYHEADER, CURLOT_SLIST, 0}, - {"PROXYPASSWORD", CURLOPT_PROXYPASSWORD, CURLOT_STRING, 0}, - {"PROXYPORT", CURLOPT_PROXYPORT, CURLOT_LONG, 0}, - {"PROXYTYPE", CURLOPT_PROXYTYPE, CURLOT_VALUES, 0}, - {"PROXYUSERNAME", CURLOPT_PROXYUSERNAME, CURLOT_STRING, 0}, - {"PROXYUSERPWD", CURLOPT_PROXYUSERPWD, CURLOT_STRING, 0}, - {"PROXY_CAINFO", CURLOPT_PROXY_CAINFO, CURLOT_STRING, 0}, - {"PROXY_CAINFO_BLOB", CURLOPT_PROXY_CAINFO_BLOB, CURLOT_BLOB, 0}, - {"PROXY_CAPATH", CURLOPT_PROXY_CAPATH, CURLOT_STRING, 0}, - {"PROXY_CRLFILE", CURLOPT_PROXY_CRLFILE, CURLOT_STRING, 0}, - {"PROXY_ISSUERCERT", CURLOPT_PROXY_ISSUERCERT, CURLOT_STRING, 0}, - {"PROXY_ISSUERCERT_BLOB", CURLOPT_PROXY_ISSUERCERT_BLOB, CURLOT_BLOB, 0}, - {"PROXY_KEYPASSWD", CURLOPT_PROXY_KEYPASSWD, CURLOT_STRING, 0}, - {"PROXY_PINNEDPUBLICKEY", CURLOPT_PROXY_PINNEDPUBLICKEY, CURLOT_STRING, 0}, - {"PROXY_SERVICE_NAME", CURLOPT_PROXY_SERVICE_NAME, CURLOT_STRING, 0}, - {"PROXY_SSLCERT", CURLOPT_PROXY_SSLCERT, CURLOT_STRING, 0}, - {"PROXY_SSLCERTTYPE", CURLOPT_PROXY_SSLCERTTYPE, CURLOT_STRING, 0}, - {"PROXY_SSLCERT_BLOB", CURLOPT_PROXY_SSLCERT_BLOB, CURLOT_BLOB, 0}, - {"PROXY_SSLKEY", CURLOPT_PROXY_SSLKEY, CURLOT_STRING, 0}, - {"PROXY_SSLKEYTYPE", CURLOPT_PROXY_SSLKEYTYPE, CURLOT_STRING, 0}, - {"PROXY_SSLKEY_BLOB", CURLOPT_PROXY_SSLKEY_BLOB, CURLOT_BLOB, 0}, - {"PROXY_SSLVERSION", CURLOPT_PROXY_SSLVERSION, CURLOT_VALUES, 0}, - {"PROXY_SSL_CIPHER_LIST", CURLOPT_PROXY_SSL_CIPHER_LIST, CURLOT_STRING, 0}, - {"PROXY_SSL_OPTIONS", CURLOPT_PROXY_SSL_OPTIONS, CURLOT_LONG, 0}, - {"PROXY_SSL_VERIFYHOST", CURLOPT_PROXY_SSL_VERIFYHOST, CURLOT_LONG, 0}, - {"PROXY_SSL_VERIFYPEER", CURLOPT_PROXY_SSL_VERIFYPEER, CURLOT_LONG, 0}, - {"PROXY_TLS13_CIPHERS", CURLOPT_PROXY_TLS13_CIPHERS, CURLOT_STRING, 0}, - {"PROXY_TLSAUTH_PASSWORD", CURLOPT_PROXY_TLSAUTH_PASSWORD, - CURLOT_STRING, 0}, - {"PROXY_TLSAUTH_TYPE", CURLOPT_PROXY_TLSAUTH_TYPE, CURLOT_STRING, 0}, - {"PROXY_TLSAUTH_USERNAME", CURLOPT_PROXY_TLSAUTH_USERNAME, - CURLOT_STRING, 0}, - {"PROXY_TRANSFER_MODE", CURLOPT_PROXY_TRANSFER_MODE, CURLOT_LONG, 0}, - {"PUT", CURLOPT_PUT, CURLOT_LONG, 0}, - {"QUOTE", CURLOPT_QUOTE, CURLOT_SLIST, 0}, - {"RANDOM_FILE", CURLOPT_RANDOM_FILE, CURLOT_STRING, 0}, - {"RANGE", CURLOPT_RANGE, CURLOT_STRING, 0}, - {"READDATA", CURLOPT_READDATA, CURLOT_CBPTR, 0}, - {"READFUNCTION", CURLOPT_READFUNCTION, CURLOT_FUNCTION, 0}, - {"REDIR_PROTOCOLS", CURLOPT_REDIR_PROTOCOLS, CURLOT_LONG, 0}, - {"REDIR_PROTOCOLS_STR", CURLOPT_REDIR_PROTOCOLS_STR, CURLOT_STRING, 0}, - {"REFERER", CURLOPT_REFERER, CURLOT_STRING, 0}, - {"REQUEST_TARGET", CURLOPT_REQUEST_TARGET, CURLOT_STRING, 0}, - {"RESOLVE", CURLOPT_RESOLVE, CURLOT_SLIST, 0}, - {"RESOLVER_START_DATA", CURLOPT_RESOLVER_START_DATA, CURLOT_CBPTR, 0}, - {"RESOLVER_START_FUNCTION", CURLOPT_RESOLVER_START_FUNCTION, - CURLOT_FUNCTION, 0}, - {"RESUME_FROM", CURLOPT_RESUME_FROM, CURLOT_LONG, 0}, - {"RESUME_FROM_LARGE", CURLOPT_RESUME_FROM_LARGE, CURLOT_OFF_T, 0}, - {"RTSPHEADER", CURLOPT_HTTPHEADER, CURLOT_SLIST, CURLOT_FLAG_ALIAS}, - {"RTSP_CLIENT_CSEQ", CURLOPT_RTSP_CLIENT_CSEQ, CURLOT_LONG, 0}, - {"RTSP_REQUEST", CURLOPT_RTSP_REQUEST, CURLOT_VALUES, 0}, - {"RTSP_SERVER_CSEQ", CURLOPT_RTSP_SERVER_CSEQ, CURLOT_LONG, 0}, - {"RTSP_SESSION_ID", CURLOPT_RTSP_SESSION_ID, CURLOT_STRING, 0}, - {"RTSP_STREAM_URI", CURLOPT_RTSP_STREAM_URI, CURLOT_STRING, 0}, - {"RTSP_TRANSPORT", CURLOPT_RTSP_TRANSPORT, CURLOT_STRING, 0}, - {"SASL_AUTHZID", CURLOPT_SASL_AUTHZID, CURLOT_STRING, 0}, - {"SASL_IR", CURLOPT_SASL_IR, CURLOT_LONG, 0}, - {"SEEKDATA", CURLOPT_SEEKDATA, CURLOT_CBPTR, 0}, - {"SEEKFUNCTION", CURLOPT_SEEKFUNCTION, CURLOT_FUNCTION, 0}, - {"SERVER_RESPONSE_TIMEOUT", CURLOPT_SERVER_RESPONSE_TIMEOUT, - CURLOT_LONG, 0}, - {"SERVICE_NAME", CURLOPT_SERVICE_NAME, CURLOT_STRING, 0}, - {"SHARE", CURLOPT_SHARE, CURLOT_OBJECT, 0}, - {"SOCKOPTDATA", CURLOPT_SOCKOPTDATA, CURLOT_CBPTR, 0}, - {"SOCKOPTFUNCTION", CURLOPT_SOCKOPTFUNCTION, CURLOT_FUNCTION, 0}, - {"SOCKS5_AUTH", CURLOPT_SOCKS5_AUTH, CURLOT_LONG, 0}, - {"SOCKS5_GSSAPI_NEC", CURLOPT_SOCKS5_GSSAPI_NEC, CURLOT_LONG, 0}, - {"SOCKS5_GSSAPI_SERVICE", CURLOPT_SOCKS5_GSSAPI_SERVICE, CURLOT_STRING, 0}, - {"SSH_AUTH_TYPES", CURLOPT_SSH_AUTH_TYPES, CURLOT_VALUES, 0}, - {"SSH_COMPRESSION", CURLOPT_SSH_COMPRESSION, CURLOT_LONG, 0}, - {"SSH_HOSTKEYDATA", CURLOPT_SSH_HOSTKEYDATA, CURLOT_CBPTR, 0}, - {"SSH_HOSTKEYFUNCTION", CURLOPT_SSH_HOSTKEYFUNCTION, CURLOT_FUNCTION, 0}, - {"SSH_HOST_PUBLIC_KEY_MD5", CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, - CURLOT_STRING, 0}, - {"SSH_HOST_PUBLIC_KEY_SHA256", CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, - CURLOT_STRING, 0}, - {"SSH_KEYDATA", CURLOPT_SSH_KEYDATA, CURLOT_CBPTR, 0}, - {"SSH_KEYFUNCTION", CURLOPT_SSH_KEYFUNCTION, CURLOT_FUNCTION, 0}, - {"SSH_KNOWNHOSTS", CURLOPT_SSH_KNOWNHOSTS, CURLOT_STRING, 0}, - {"SSH_PRIVATE_KEYFILE", CURLOPT_SSH_PRIVATE_KEYFILE, CURLOT_STRING, 0}, - {"SSH_PUBLIC_KEYFILE", CURLOPT_SSH_PUBLIC_KEYFILE, CURLOT_STRING, 0}, - {"SSLCERT", CURLOPT_SSLCERT, CURLOT_STRING, 0}, - {"SSLCERTPASSWD", CURLOPT_KEYPASSWD, CURLOT_STRING, CURLOT_FLAG_ALIAS}, - {"SSLCERTTYPE", CURLOPT_SSLCERTTYPE, CURLOT_STRING, 0}, - {"SSLCERT_BLOB", CURLOPT_SSLCERT_BLOB, CURLOT_BLOB, 0}, - {"SSLENGINE", CURLOPT_SSLENGINE, CURLOT_STRING, 0}, - {"SSLENGINE_DEFAULT", CURLOPT_SSLENGINE_DEFAULT, CURLOT_LONG, 0}, - {"SSLKEY", CURLOPT_SSLKEY, CURLOT_STRING, 0}, - {"SSLKEYPASSWD", CURLOPT_KEYPASSWD, CURLOT_STRING, CURLOT_FLAG_ALIAS}, - {"SSLKEYTYPE", CURLOPT_SSLKEYTYPE, CURLOT_STRING, 0}, - {"SSLKEY_BLOB", CURLOPT_SSLKEY_BLOB, CURLOT_BLOB, 0}, - {"SSLVERSION", CURLOPT_SSLVERSION, CURLOT_VALUES, 0}, - {"SSL_CIPHER_LIST", CURLOPT_SSL_CIPHER_LIST, CURLOT_STRING, 0}, - {"SSL_CTX_DATA", CURLOPT_SSL_CTX_DATA, CURLOT_CBPTR, 0}, - {"SSL_CTX_FUNCTION", CURLOPT_SSL_CTX_FUNCTION, CURLOT_FUNCTION, 0}, - {"SSL_EC_CURVES", CURLOPT_SSL_EC_CURVES, CURLOT_STRING, 0}, - {"SSL_ENABLE_ALPN", CURLOPT_SSL_ENABLE_ALPN, CURLOT_LONG, 0}, - {"SSL_ENABLE_NPN", CURLOPT_SSL_ENABLE_NPN, CURLOT_LONG, 0}, - {"SSL_FALSESTART", CURLOPT_SSL_FALSESTART, CURLOT_LONG, 0}, - {"SSL_OPTIONS", CURLOPT_SSL_OPTIONS, CURLOT_VALUES, 0}, - {"SSL_SESSIONID_CACHE", CURLOPT_SSL_SESSIONID_CACHE, CURLOT_LONG, 0}, - {"SSL_VERIFYHOST", CURLOPT_SSL_VERIFYHOST, CURLOT_LONG, 0}, - {"SSL_VERIFYPEER", CURLOPT_SSL_VERIFYPEER, CURLOT_LONG, 0}, - {"SSL_VERIFYSTATUS", CURLOPT_SSL_VERIFYSTATUS, CURLOT_LONG, 0}, - {"STDERR", CURLOPT_STDERR, CURLOT_OBJECT, 0}, - {"STREAM_DEPENDS", CURLOPT_STREAM_DEPENDS, CURLOT_OBJECT, 0}, - {"STREAM_DEPENDS_E", CURLOPT_STREAM_DEPENDS_E, CURLOT_OBJECT, 0}, - {"STREAM_WEIGHT", CURLOPT_STREAM_WEIGHT, CURLOT_LONG, 0}, - {"SUPPRESS_CONNECT_HEADERS", CURLOPT_SUPPRESS_CONNECT_HEADERS, - CURLOT_LONG, 0}, - {"TCP_FASTOPEN", CURLOPT_TCP_FASTOPEN, CURLOT_LONG, 0}, - {"TCP_KEEPALIVE", CURLOPT_TCP_KEEPALIVE, CURLOT_LONG, 0}, - {"TCP_KEEPIDLE", CURLOPT_TCP_KEEPIDLE, CURLOT_LONG, 0}, - {"TCP_KEEPINTVL", CURLOPT_TCP_KEEPINTVL, CURLOT_LONG, 0}, - {"TCP_NODELAY", CURLOPT_TCP_NODELAY, CURLOT_LONG, 0}, - {"TELNETOPTIONS", CURLOPT_TELNETOPTIONS, CURLOT_SLIST, 0}, - {"TFTP_BLKSIZE", CURLOPT_TFTP_BLKSIZE, CURLOT_LONG, 0}, - {"TFTP_NO_OPTIONS", CURLOPT_TFTP_NO_OPTIONS, CURLOT_LONG, 0}, - {"TIMECONDITION", CURLOPT_TIMECONDITION, CURLOT_VALUES, 0}, - {"TIMEOUT", CURLOPT_TIMEOUT, CURLOT_LONG, 0}, - {"TIMEOUT_MS", CURLOPT_TIMEOUT_MS, CURLOT_LONG, 0}, - {"TIMEVALUE", CURLOPT_TIMEVALUE, CURLOT_LONG, 0}, - {"TIMEVALUE_LARGE", CURLOPT_TIMEVALUE_LARGE, CURLOT_OFF_T, 0}, - {"TLS13_CIPHERS", CURLOPT_TLS13_CIPHERS, CURLOT_STRING, 0}, - {"TLSAUTH_PASSWORD", CURLOPT_TLSAUTH_PASSWORD, CURLOT_STRING, 0}, - {"TLSAUTH_TYPE", CURLOPT_TLSAUTH_TYPE, CURLOT_STRING, 0}, - {"TLSAUTH_USERNAME", CURLOPT_TLSAUTH_USERNAME, CURLOT_STRING, 0}, - {"TRAILERDATA", CURLOPT_TRAILERDATA, CURLOT_CBPTR, 0}, - {"TRAILERFUNCTION", CURLOPT_TRAILERFUNCTION, CURLOT_FUNCTION, 0}, - {"TRANSFERTEXT", CURLOPT_TRANSFERTEXT, CURLOT_LONG, 0}, - {"TRANSFER_ENCODING", CURLOPT_TRANSFER_ENCODING, CURLOT_LONG, 0}, - {"UNIX_SOCKET_PATH", CURLOPT_UNIX_SOCKET_PATH, CURLOT_STRING, 0}, - {"UNRESTRICTED_AUTH", CURLOPT_UNRESTRICTED_AUTH, CURLOT_LONG, 0}, - {"UPKEEP_INTERVAL_MS", CURLOPT_UPKEEP_INTERVAL_MS, CURLOT_LONG, 0}, - {"UPLOAD", CURLOPT_UPLOAD, CURLOT_LONG, 0}, - {"UPLOAD_BUFFERSIZE", CURLOPT_UPLOAD_BUFFERSIZE, CURLOT_LONG, 0}, - {"URL", CURLOPT_URL, CURLOT_STRING, 0}, - {"USERAGENT", CURLOPT_USERAGENT, CURLOT_STRING, 0}, - {"USERNAME", CURLOPT_USERNAME, CURLOT_STRING, 0}, - {"USERPWD", CURLOPT_USERPWD, CURLOT_STRING, 0}, - {"USE_SSL", CURLOPT_USE_SSL, CURLOT_VALUES, 0}, - {"VERBOSE", CURLOPT_VERBOSE, CURLOT_LONG, 0}, - {"WILDCARDMATCH", CURLOPT_WILDCARDMATCH, CURLOT_LONG, 0}, - {"WRITEDATA", CURLOPT_WRITEDATA, CURLOT_CBPTR, 0}, - {"WRITEFUNCTION", CURLOPT_WRITEFUNCTION, CURLOT_FUNCTION, 0}, - {"WRITEHEADER", CURLOPT_HEADERDATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS}, - {"WS_OPTIONS", CURLOPT_WS_OPTIONS, CURLOT_LONG, 0}, - {"XFERINFODATA", CURLOPT_XFERINFODATA, CURLOT_CBPTR, 0}, - {"XFERINFOFUNCTION", CURLOPT_XFERINFOFUNCTION, CURLOT_FUNCTION, 0}, - {"XOAUTH2_BEARER", CURLOPT_XOAUTH2_BEARER, CURLOT_STRING, 0}, - {NULL, CURLOPT_LASTENTRY, CURLOT_LONG, 0} /* end of table */ -}; - -#ifdef DEBUGBUILD -/* - * Curl_easyopts_check() is a debug-only function that returns non-zero - * if this source file is not in sync with the options listed in curl/curl.h - */ -int Curl_easyopts_check(void) -{ - return ((CURLOPT_LASTENTRY%10000) != (320 + 1)); -} -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ | |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * ___|___/|_| ______|
+ *
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel.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 source code is generated by optiontable.pl - DO NOT EDIT BY HAND */
+
+#include "curl_setup.h"
+#include "easyoptions.h"
+
+/* all easy setopt options listed in alphabetical order */
+struct curl_easyoption Curl_easyopts[] = {
+ {"ABSTRACT_UNIX_SOCKET", CURLOPT_ABSTRACT_UNIX_SOCKET, CURLOT_STRING, 0},
+ {"ACCEPTTIMEOUT_MS", CURLOPT_ACCEPTTIMEOUT_MS, CURLOT_LONG, 0},
+ {"ACCEPT_ENCODING", CURLOPT_ACCEPT_ENCODING, CURLOT_STRING, 0},
+ {"ADDRESS_SCOPE", CURLOPT_ADDRESS_SCOPE, CURLOT_LONG, 0},
+ {"ALTSVC", CURLOPT_ALTSVC, CURLOT_STRING, 0},
+ {"ALTSVC_CTRL", CURLOPT_ALTSVC_CTRL, CURLOT_LONG, 0},
+ {"APPEND", CURLOPT_APPEND, CURLOT_LONG, 0},
+ {"AUTOREFERER", CURLOPT_AUTOREFERER, CURLOT_LONG, 0},
+ {"AWS_SIGV4", CURLOPT_AWS_SIGV4, CURLOT_STRING, 0},
+ {"BUFFERSIZE", CURLOPT_BUFFERSIZE, CURLOT_LONG, 0},
+ {"CAINFO", CURLOPT_CAINFO, CURLOT_STRING, 0},
+ {"CAINFO_BLOB", CURLOPT_CAINFO_BLOB, CURLOT_BLOB, 0},
+ {"CAPATH", CURLOPT_CAPATH, CURLOT_STRING, 0},
+ {"CA_CACHE_TIMEOUT", CURLOPT_CA_CACHE_TIMEOUT, CURLOT_LONG, 0},
+ {"CERTINFO", CURLOPT_CERTINFO, CURLOT_LONG, 0},
+ {"CHUNK_BGN_FUNCTION", CURLOPT_CHUNK_BGN_FUNCTION, CURLOT_FUNCTION, 0},
+ {"CHUNK_DATA", CURLOPT_CHUNK_DATA, CURLOT_CBPTR, 0},
+ {"CHUNK_END_FUNCTION", CURLOPT_CHUNK_END_FUNCTION, CURLOT_FUNCTION, 0},
+ {"CLOSESOCKETDATA", CURLOPT_CLOSESOCKETDATA, CURLOT_CBPTR, 0},
+ {"CLOSESOCKETFUNCTION", CURLOPT_CLOSESOCKETFUNCTION, CURLOT_FUNCTION, 0},
+ {"CONNECTTIMEOUT", CURLOPT_CONNECTTIMEOUT, CURLOT_LONG, 0},
+ {"CONNECTTIMEOUT_MS", CURLOPT_CONNECTTIMEOUT_MS, CURLOT_LONG, 0},
+ {"CONNECT_ONLY", CURLOPT_CONNECT_ONLY, CURLOT_LONG, 0},
+ {"CONNECT_TO", CURLOPT_CONNECT_TO, CURLOT_SLIST, 0},
+ {"CONV_FROM_NETWORK_FUNCTION", CURLOPT_CONV_FROM_NETWORK_FUNCTION,
+ CURLOT_FUNCTION, 0},
+ {"CONV_FROM_UTF8_FUNCTION", CURLOPT_CONV_FROM_UTF8_FUNCTION,
+ CURLOT_FUNCTION, 0},
+ {"CONV_TO_NETWORK_FUNCTION", CURLOPT_CONV_TO_NETWORK_FUNCTION,
+ CURLOT_FUNCTION, 0},
+ {"COOKIE", CURLOPT_COOKIE, CURLOT_STRING, 0},
+ {"COOKIEFILE", CURLOPT_COOKIEFILE, CURLOT_STRING, 0},
+ {"COOKIEJAR", CURLOPT_COOKIEJAR, CURLOT_STRING, 0},
+ {"COOKIELIST", CURLOPT_COOKIELIST, CURLOT_STRING, 0},
+ {"COOKIESESSION", CURLOPT_COOKIESESSION, CURLOT_LONG, 0},
+ {"COPYPOSTFIELDS", CURLOPT_COPYPOSTFIELDS, CURLOT_OBJECT, 0},
+ {"CRLF", CURLOPT_CRLF, CURLOT_LONG, 0},
+ {"CRLFILE", CURLOPT_CRLFILE, CURLOT_STRING, 0},
+ {"CURLU", CURLOPT_CURLU, CURLOT_OBJECT, 0},
+ {"CUSTOMREQUEST", CURLOPT_CUSTOMREQUEST, CURLOT_STRING, 0},
+ {"DEBUGDATA", CURLOPT_DEBUGDATA, CURLOT_CBPTR, 0},
+ {"DEBUGFUNCTION", CURLOPT_DEBUGFUNCTION, CURLOT_FUNCTION, 0},
+ {"DEFAULT_PROTOCOL", CURLOPT_DEFAULT_PROTOCOL, CURLOT_STRING, 0},
+ {"DIRLISTONLY", CURLOPT_DIRLISTONLY, CURLOT_LONG, 0},
+ {"DISALLOW_USERNAME_IN_URL", CURLOPT_DISALLOW_USERNAME_IN_URL,
+ CURLOT_LONG, 0},
+ {"DNS_CACHE_TIMEOUT", CURLOPT_DNS_CACHE_TIMEOUT, CURLOT_LONG, 0},
+ {"DNS_INTERFACE", CURLOPT_DNS_INTERFACE, CURLOT_STRING, 0},
+ {"DNS_LOCAL_IP4", CURLOPT_DNS_LOCAL_IP4, CURLOT_STRING, 0},
+ {"DNS_LOCAL_IP6", CURLOPT_DNS_LOCAL_IP6, CURLOT_STRING, 0},
+ {"DNS_SERVERS", CURLOPT_DNS_SERVERS, CURLOT_STRING, 0},
+ {"DNS_SHUFFLE_ADDRESSES", CURLOPT_DNS_SHUFFLE_ADDRESSES, CURLOT_LONG, 0},
+ {"DNS_USE_GLOBAL_CACHE", CURLOPT_DNS_USE_GLOBAL_CACHE, CURLOT_LONG, 0},
+ {"DOH_SSL_VERIFYHOST", CURLOPT_DOH_SSL_VERIFYHOST, CURLOT_LONG, 0},
+ {"DOH_SSL_VERIFYPEER", CURLOPT_DOH_SSL_VERIFYPEER, CURLOT_LONG, 0},
+ {"DOH_SSL_VERIFYSTATUS", CURLOPT_DOH_SSL_VERIFYSTATUS, CURLOT_LONG, 0},
+ {"DOH_URL", CURLOPT_DOH_URL, CURLOT_STRING, 0},
+ {"EGDSOCKET", CURLOPT_EGDSOCKET, CURLOT_STRING, 0},
+ {"ENCODING", CURLOPT_ACCEPT_ENCODING, CURLOT_STRING, CURLOT_FLAG_ALIAS},
+ {"ERRORBUFFER", CURLOPT_ERRORBUFFER, CURLOT_OBJECT, 0},
+ {"EXPECT_100_TIMEOUT_MS", CURLOPT_EXPECT_100_TIMEOUT_MS, CURLOT_LONG, 0},
+ {"FAILONERROR", CURLOPT_FAILONERROR, CURLOT_LONG, 0},
+ {"FILE", CURLOPT_WRITEDATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS},
+ {"FILETIME", CURLOPT_FILETIME, CURLOT_LONG, 0},
+ {"FNMATCH_DATA", CURLOPT_FNMATCH_DATA, CURLOT_CBPTR, 0},
+ {"FNMATCH_FUNCTION", CURLOPT_FNMATCH_FUNCTION, CURLOT_FUNCTION, 0},
+ {"FOLLOWLOCATION", CURLOPT_FOLLOWLOCATION, CURLOT_LONG, 0},
+ {"FORBID_REUSE", CURLOPT_FORBID_REUSE, CURLOT_LONG, 0},
+ {"FRESH_CONNECT", CURLOPT_FRESH_CONNECT, CURLOT_LONG, 0},
+ {"FTPAPPEND", CURLOPT_APPEND, CURLOT_LONG, CURLOT_FLAG_ALIAS},
+ {"FTPLISTONLY", CURLOPT_DIRLISTONLY, CURLOT_LONG, CURLOT_FLAG_ALIAS},
+ {"FTPPORT", CURLOPT_FTPPORT, CURLOT_STRING, 0},
+ {"FTPSSLAUTH", CURLOPT_FTPSSLAUTH, CURLOT_VALUES, 0},
+ {"FTP_ACCOUNT", CURLOPT_FTP_ACCOUNT, CURLOT_STRING, 0},
+ {"FTP_ALTERNATIVE_TO_USER", CURLOPT_FTP_ALTERNATIVE_TO_USER,
+ CURLOT_STRING, 0},
+ {"FTP_CREATE_MISSING_DIRS", CURLOPT_FTP_CREATE_MISSING_DIRS,
+ CURLOT_LONG, 0},
+ {"FTP_FILEMETHOD", CURLOPT_FTP_FILEMETHOD, CURLOT_VALUES, 0},
+ {"FTP_RESPONSE_TIMEOUT", CURLOPT_SERVER_RESPONSE_TIMEOUT,
+ CURLOT_LONG, CURLOT_FLAG_ALIAS},
+ {"FTP_SKIP_PASV_IP", CURLOPT_FTP_SKIP_PASV_IP, CURLOT_LONG, 0},
+ {"FTP_SSL", CURLOPT_USE_SSL, CURLOT_VALUES, CURLOT_FLAG_ALIAS},
+ {"FTP_SSL_CCC", CURLOPT_FTP_SSL_CCC, CURLOT_LONG, 0},
+ {"FTP_USE_EPRT", CURLOPT_FTP_USE_EPRT, CURLOT_LONG, 0},
+ {"FTP_USE_EPSV", CURLOPT_FTP_USE_EPSV, CURLOT_LONG, 0},
+ {"FTP_USE_PRET", CURLOPT_FTP_USE_PRET, CURLOT_LONG, 0},
+ {"GSSAPI_DELEGATION", CURLOPT_GSSAPI_DELEGATION, CURLOT_VALUES, 0},
+ {"HAPPY_EYEBALLS_TIMEOUT_MS", CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS,
+ CURLOT_LONG, 0},
+ {"HAPROXYPROTOCOL", CURLOPT_HAPROXYPROTOCOL, CURLOT_LONG, 0},
+ {"HEADER", CURLOPT_HEADER, CURLOT_LONG, 0},
+ {"HEADERDATA", CURLOPT_HEADERDATA, CURLOT_CBPTR, 0},
+ {"HEADERFUNCTION", CURLOPT_HEADERFUNCTION, CURLOT_FUNCTION, 0},
+ {"HEADEROPT", CURLOPT_HEADEROPT, CURLOT_VALUES, 0},
+ {"HSTS", CURLOPT_HSTS, CURLOT_STRING, 0},
+ {"HSTSREADDATA", CURLOPT_HSTSREADDATA, CURLOT_CBPTR, 0},
+ {"HSTSREADFUNCTION", CURLOPT_HSTSREADFUNCTION, CURLOT_FUNCTION, 0},
+ {"HSTSWRITEDATA", CURLOPT_HSTSWRITEDATA, CURLOT_CBPTR, 0},
+ {"HSTSWRITEFUNCTION", CURLOPT_HSTSWRITEFUNCTION, CURLOT_FUNCTION, 0},
+ {"HSTS_CTRL", CURLOPT_HSTS_CTRL, CURLOT_LONG, 0},
+ {"HTTP09_ALLOWED", CURLOPT_HTTP09_ALLOWED, CURLOT_LONG, 0},
+ {"HTTP200ALIASES", CURLOPT_HTTP200ALIASES, CURLOT_SLIST, 0},
+ {"HTTPAUTH", CURLOPT_HTTPAUTH, CURLOT_VALUES, 0},
+ {"HTTPGET", CURLOPT_HTTPGET, CURLOT_LONG, 0},
+ {"HTTPHEADER", CURLOPT_HTTPHEADER, CURLOT_SLIST, 0},
+ {"HTTPPOST", CURLOPT_HTTPPOST, CURLOT_OBJECT, 0},
+ {"HTTPPROXYTUNNEL", CURLOPT_HTTPPROXYTUNNEL, CURLOT_LONG, 0},
+ {"HTTP_CONTENT_DECODING", CURLOPT_HTTP_CONTENT_DECODING, CURLOT_LONG, 0},
+ {"HTTP_TRANSFER_DECODING", CURLOPT_HTTP_TRANSFER_DECODING, CURLOT_LONG, 0},
+ {"HTTP_VERSION", CURLOPT_HTTP_VERSION, CURLOT_VALUES, 0},
+ {"IGNORE_CONTENT_LENGTH", CURLOPT_IGNORE_CONTENT_LENGTH, CURLOT_LONG, 0},
+ {"INFILE", CURLOPT_READDATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS},
+ {"INFILESIZE", CURLOPT_INFILESIZE, CURLOT_LONG, 0},
+ {"INFILESIZE_LARGE", CURLOPT_INFILESIZE_LARGE, CURLOT_OFF_T, 0},
+ {"INTERFACE", CURLOPT_INTERFACE, CURLOT_STRING, 0},
+ {"INTERLEAVEDATA", CURLOPT_INTERLEAVEDATA, CURLOT_CBPTR, 0},
+ {"INTERLEAVEFUNCTION", CURLOPT_INTERLEAVEFUNCTION, CURLOT_FUNCTION, 0},
+ {"IOCTLDATA", CURLOPT_IOCTLDATA, CURLOT_CBPTR, 0},
+ {"IOCTLFUNCTION", CURLOPT_IOCTLFUNCTION, CURLOT_FUNCTION, 0},
+ {"IPRESOLVE", CURLOPT_IPRESOLVE, CURLOT_VALUES, 0},
+ {"ISSUERCERT", CURLOPT_ISSUERCERT, CURLOT_STRING, 0},
+ {"ISSUERCERT_BLOB", CURLOPT_ISSUERCERT_BLOB, CURLOT_BLOB, 0},
+ {"KEEP_SENDING_ON_ERROR", CURLOPT_KEEP_SENDING_ON_ERROR, CURLOT_LONG, 0},
+ {"KEYPASSWD", CURLOPT_KEYPASSWD, CURLOT_STRING, 0},
+ {"KRB4LEVEL", CURLOPT_KRBLEVEL, CURLOT_STRING, CURLOT_FLAG_ALIAS},
+ {"KRBLEVEL", CURLOPT_KRBLEVEL, CURLOT_STRING, 0},
+ {"LOCALPORT", CURLOPT_LOCALPORT, CURLOT_LONG, 0},
+ {"LOCALPORTRANGE", CURLOPT_LOCALPORTRANGE, CURLOT_LONG, 0},
+ {"LOGIN_OPTIONS", CURLOPT_LOGIN_OPTIONS, CURLOT_STRING, 0},
+ {"LOW_SPEED_LIMIT", CURLOPT_LOW_SPEED_LIMIT, CURLOT_LONG, 0},
+ {"LOW_SPEED_TIME", CURLOPT_LOW_SPEED_TIME, CURLOT_LONG, 0},
+ {"MAIL_AUTH", CURLOPT_MAIL_AUTH, CURLOT_STRING, 0},
+ {"MAIL_FROM", CURLOPT_MAIL_FROM, CURLOT_STRING, 0},
+ {"MAIL_RCPT", CURLOPT_MAIL_RCPT, CURLOT_SLIST, 0},
+ {"MAIL_RCPT_ALLLOWFAILS", CURLOPT_MAIL_RCPT_ALLLOWFAILS, CURLOT_LONG, 0},
+ {"MAXAGE_CONN", CURLOPT_MAXAGE_CONN, CURLOT_LONG, 0},
+ {"MAXCONNECTS", CURLOPT_MAXCONNECTS, CURLOT_LONG, 0},
+ {"MAXFILESIZE", CURLOPT_MAXFILESIZE, CURLOT_LONG, 0},
+ {"MAXFILESIZE_LARGE", CURLOPT_MAXFILESIZE_LARGE, CURLOT_OFF_T, 0},
+ {"MAXLIFETIME_CONN", CURLOPT_MAXLIFETIME_CONN, CURLOT_LONG, 0},
+ {"MAXREDIRS", CURLOPT_MAXREDIRS, CURLOT_LONG, 0},
+ {"MAX_RECV_SPEED_LARGE", CURLOPT_MAX_RECV_SPEED_LARGE, CURLOT_OFF_T, 0},
+ {"MAX_SEND_SPEED_LARGE", CURLOPT_MAX_SEND_SPEED_LARGE, CURLOT_OFF_T, 0},
+ {"MIMEPOST", CURLOPT_MIMEPOST, CURLOT_OBJECT, 0},
+ {"MIME_OPTIONS", CURLOPT_MIME_OPTIONS, CURLOT_LONG, 0},
+ {"NETRC", CURLOPT_NETRC, CURLOT_VALUES, 0},
+ {"NETRC_FILE", CURLOPT_NETRC_FILE, CURLOT_STRING, 0},
+ {"NEW_DIRECTORY_PERMS", CURLOPT_NEW_DIRECTORY_PERMS, CURLOT_LONG, 0},
+ {"NEW_FILE_PERMS", CURLOPT_NEW_FILE_PERMS, CURLOT_LONG, 0},
+ {"NOBODY", CURLOPT_NOBODY, CURLOT_LONG, 0},
+ {"NOPROGRESS", CURLOPT_NOPROGRESS, CURLOT_LONG, 0},
+ {"NOPROXY", CURLOPT_NOPROXY, CURLOT_STRING, 0},
+ {"NOSIGNAL", CURLOPT_NOSIGNAL, CURLOT_LONG, 0},
+ {"OPENSOCKETDATA", CURLOPT_OPENSOCKETDATA, CURLOT_CBPTR, 0},
+ {"OPENSOCKETFUNCTION", CURLOPT_OPENSOCKETFUNCTION, CURLOT_FUNCTION, 0},
+ {"PASSWORD", CURLOPT_PASSWORD, CURLOT_STRING, 0},
+ {"PATH_AS_IS", CURLOPT_PATH_AS_IS, CURLOT_LONG, 0},
+ {"PINNEDPUBLICKEY", CURLOPT_PINNEDPUBLICKEY, CURLOT_STRING, 0},
+ {"PIPEWAIT", CURLOPT_PIPEWAIT, CURLOT_LONG, 0},
+ {"PORT", CURLOPT_PORT, CURLOT_LONG, 0},
+ {"POST", CURLOPT_POST, CURLOT_LONG, 0},
+ {"POST301", CURLOPT_POSTREDIR, CURLOT_VALUES, CURLOT_FLAG_ALIAS},
+ {"POSTFIELDS", CURLOPT_POSTFIELDS, CURLOT_OBJECT, 0},
+ {"POSTFIELDSIZE", CURLOPT_POSTFIELDSIZE, CURLOT_LONG, 0},
+ {"POSTFIELDSIZE_LARGE", CURLOPT_POSTFIELDSIZE_LARGE, CURLOT_OFF_T, 0},
+ {"POSTQUOTE", CURLOPT_POSTQUOTE, CURLOT_SLIST, 0},
+ {"POSTREDIR", CURLOPT_POSTREDIR, CURLOT_VALUES, 0},
+ {"PREQUOTE", CURLOPT_PREQUOTE, CURLOT_SLIST, 0},
+ {"PREREQDATA", CURLOPT_PREREQDATA, CURLOT_CBPTR, 0},
+ {"PREREQFUNCTION", CURLOPT_PREREQFUNCTION, CURLOT_FUNCTION, 0},
+ {"PRE_PROXY", CURLOPT_PRE_PROXY, CURLOT_STRING, 0},
+ {"PRIVATE", CURLOPT_PRIVATE, CURLOT_OBJECT, 0},
+ {"PROGRESSDATA", CURLOPT_XFERINFODATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS},
+ {"PROGRESSFUNCTION", CURLOPT_PROGRESSFUNCTION, CURLOT_FUNCTION, 0},
+ {"PROTOCOLS", CURLOPT_PROTOCOLS, CURLOT_LONG, 0},
+ {"PROTOCOLS_STR", CURLOPT_PROTOCOLS_STR, CURLOT_STRING, 0},
+ {"PROXY", CURLOPT_PROXY, CURLOT_STRING, 0},
+ {"PROXYAUTH", CURLOPT_PROXYAUTH, CURLOT_VALUES, 0},
+ {"PROXYHEADER", CURLOPT_PROXYHEADER, CURLOT_SLIST, 0},
+ {"PROXYPASSWORD", CURLOPT_PROXYPASSWORD, CURLOT_STRING, 0},
+ {"PROXYPORT", CURLOPT_PROXYPORT, CURLOT_LONG, 0},
+ {"PROXYTYPE", CURLOPT_PROXYTYPE, CURLOT_VALUES, 0},
+ {"PROXYUSERNAME", CURLOPT_PROXYUSERNAME, CURLOT_STRING, 0},
+ {"PROXYUSERPWD", CURLOPT_PROXYUSERPWD, CURLOT_STRING, 0},
+ {"PROXY_CAINFO", CURLOPT_PROXY_CAINFO, CURLOT_STRING, 0},
+ {"PROXY_CAINFO_BLOB", CURLOPT_PROXY_CAINFO_BLOB, CURLOT_BLOB, 0},
+ {"PROXY_CAPATH", CURLOPT_PROXY_CAPATH, CURLOT_STRING, 0},
+ {"PROXY_CRLFILE", CURLOPT_PROXY_CRLFILE, CURLOT_STRING, 0},
+ {"PROXY_ISSUERCERT", CURLOPT_PROXY_ISSUERCERT, CURLOT_STRING, 0},
+ {"PROXY_ISSUERCERT_BLOB", CURLOPT_PROXY_ISSUERCERT_BLOB, CURLOT_BLOB, 0},
+ {"PROXY_KEYPASSWD", CURLOPT_PROXY_KEYPASSWD, CURLOT_STRING, 0},
+ {"PROXY_PINNEDPUBLICKEY", CURLOPT_PROXY_PINNEDPUBLICKEY, CURLOT_STRING, 0},
+ {"PROXY_SERVICE_NAME", CURLOPT_PROXY_SERVICE_NAME, CURLOT_STRING, 0},
+ {"PROXY_SSLCERT", CURLOPT_PROXY_SSLCERT, CURLOT_STRING, 0},
+ {"PROXY_SSLCERTTYPE", CURLOPT_PROXY_SSLCERTTYPE, CURLOT_STRING, 0},
+ {"PROXY_SSLCERT_BLOB", CURLOPT_PROXY_SSLCERT_BLOB, CURLOT_BLOB, 0},
+ {"PROXY_SSLKEY", CURLOPT_PROXY_SSLKEY, CURLOT_STRING, 0},
+ {"PROXY_SSLKEYTYPE", CURLOPT_PROXY_SSLKEYTYPE, CURLOT_STRING, 0},
+ {"PROXY_SSLKEY_BLOB", CURLOPT_PROXY_SSLKEY_BLOB, CURLOT_BLOB, 0},
+ {"PROXY_SSLVERSION", CURLOPT_PROXY_SSLVERSION, CURLOT_VALUES, 0},
+ {"PROXY_SSL_CIPHER_LIST", CURLOPT_PROXY_SSL_CIPHER_LIST, CURLOT_STRING, 0},
+ {"PROXY_SSL_OPTIONS", CURLOPT_PROXY_SSL_OPTIONS, CURLOT_LONG, 0},
+ {"PROXY_SSL_VERIFYHOST", CURLOPT_PROXY_SSL_VERIFYHOST, CURLOT_LONG, 0},
+ {"PROXY_SSL_VERIFYPEER", CURLOPT_PROXY_SSL_VERIFYPEER, CURLOT_LONG, 0},
+ {"PROXY_TLS13_CIPHERS", CURLOPT_PROXY_TLS13_CIPHERS, CURLOT_STRING, 0},
+ {"PROXY_TLSAUTH_PASSWORD", CURLOPT_PROXY_TLSAUTH_PASSWORD,
+ CURLOT_STRING, 0},
+ {"PROXY_TLSAUTH_TYPE", CURLOPT_PROXY_TLSAUTH_TYPE, CURLOT_STRING, 0},
+ {"PROXY_TLSAUTH_USERNAME", CURLOPT_PROXY_TLSAUTH_USERNAME,
+ CURLOT_STRING, 0},
+ {"PROXY_TRANSFER_MODE", CURLOPT_PROXY_TRANSFER_MODE, CURLOT_LONG, 0},
+ {"PUT", CURLOPT_PUT, CURLOT_LONG, 0},
+ {"QUICK_EXIT", CURLOPT_QUICK_EXIT, CURLOT_LONG, 0},
+ {"QUOTE", CURLOPT_QUOTE, CURLOT_SLIST, 0},
+ {"RANDOM_FILE", CURLOPT_RANDOM_FILE, CURLOT_STRING, 0},
+ {"RANGE", CURLOPT_RANGE, CURLOT_STRING, 0},
+ {"READDATA", CURLOPT_READDATA, CURLOT_CBPTR, 0},
+ {"READFUNCTION", CURLOPT_READFUNCTION, CURLOT_FUNCTION, 0},
+ {"REDIR_PROTOCOLS", CURLOPT_REDIR_PROTOCOLS, CURLOT_LONG, 0},
+ {"REDIR_PROTOCOLS_STR", CURLOPT_REDIR_PROTOCOLS_STR, CURLOT_STRING, 0},
+ {"REFERER", CURLOPT_REFERER, CURLOT_STRING, 0},
+ {"REQUEST_TARGET", CURLOPT_REQUEST_TARGET, CURLOT_STRING, 0},
+ {"RESOLVE", CURLOPT_RESOLVE, CURLOT_SLIST, 0},
+ {"RESOLVER_START_DATA", CURLOPT_RESOLVER_START_DATA, CURLOT_CBPTR, 0},
+ {"RESOLVER_START_FUNCTION", CURLOPT_RESOLVER_START_FUNCTION,
+ CURLOT_FUNCTION, 0},
+ {"RESUME_FROM", CURLOPT_RESUME_FROM, CURLOT_LONG, 0},
+ {"RESUME_FROM_LARGE", CURLOPT_RESUME_FROM_LARGE, CURLOT_OFF_T, 0},
+ {"RTSPHEADER", CURLOPT_HTTPHEADER, CURLOT_SLIST, CURLOT_FLAG_ALIAS},
+ {"RTSP_CLIENT_CSEQ", CURLOPT_RTSP_CLIENT_CSEQ, CURLOT_LONG, 0},
+ {"RTSP_REQUEST", CURLOPT_RTSP_REQUEST, CURLOT_VALUES, 0},
+ {"RTSP_SERVER_CSEQ", CURLOPT_RTSP_SERVER_CSEQ, CURLOT_LONG, 0},
+ {"RTSP_SESSION_ID", CURLOPT_RTSP_SESSION_ID, CURLOT_STRING, 0},
+ {"RTSP_STREAM_URI", CURLOPT_RTSP_STREAM_URI, CURLOT_STRING, 0},
+ {"RTSP_TRANSPORT", CURLOPT_RTSP_TRANSPORT, CURLOT_STRING, 0},
+ {"SASL_AUTHZID", CURLOPT_SASL_AUTHZID, CURLOT_STRING, 0},
+ {"SASL_IR", CURLOPT_SASL_IR, CURLOT_LONG, 0},
+ {"SEEKDATA", CURLOPT_SEEKDATA, CURLOT_CBPTR, 0},
+ {"SEEKFUNCTION", CURLOPT_SEEKFUNCTION, CURLOT_FUNCTION, 0},
+ {"SERVER_RESPONSE_TIMEOUT", CURLOPT_SERVER_RESPONSE_TIMEOUT,
+ CURLOT_LONG, 0},
+ {"SERVICE_NAME", CURLOPT_SERVICE_NAME, CURLOT_STRING, 0},
+ {"SHARE", CURLOPT_SHARE, CURLOT_OBJECT, 0},
+ {"SOCKOPTDATA", CURLOPT_SOCKOPTDATA, CURLOT_CBPTR, 0},
+ {"SOCKOPTFUNCTION", CURLOPT_SOCKOPTFUNCTION, CURLOT_FUNCTION, 0},
+ {"SOCKS5_AUTH", CURLOPT_SOCKS5_AUTH, CURLOT_LONG, 0},
+ {"SOCKS5_GSSAPI_NEC", CURLOPT_SOCKS5_GSSAPI_NEC, CURLOT_LONG, 0},
+ {"SOCKS5_GSSAPI_SERVICE", CURLOPT_SOCKS5_GSSAPI_SERVICE, CURLOT_STRING, 0},
+ {"SSH_AUTH_TYPES", CURLOPT_SSH_AUTH_TYPES, CURLOT_VALUES, 0},
+ {"SSH_COMPRESSION", CURLOPT_SSH_COMPRESSION, CURLOT_LONG, 0},
+ {"SSH_HOSTKEYDATA", CURLOPT_SSH_HOSTKEYDATA, CURLOT_CBPTR, 0},
+ {"SSH_HOSTKEYFUNCTION", CURLOPT_SSH_HOSTKEYFUNCTION, CURLOT_FUNCTION, 0},
+ {"SSH_HOST_PUBLIC_KEY_MD5", CURLOPT_SSH_HOST_PUBLIC_KEY_MD5,
+ CURLOT_STRING, 0},
+ {"SSH_HOST_PUBLIC_KEY_SHA256", CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256,
+ CURLOT_STRING, 0},
+ {"SSH_KEYDATA", CURLOPT_SSH_KEYDATA, CURLOT_CBPTR, 0},
+ {"SSH_KEYFUNCTION", CURLOPT_SSH_KEYFUNCTION, CURLOT_FUNCTION, 0},
+ {"SSH_KNOWNHOSTS", CURLOPT_SSH_KNOWNHOSTS, CURLOT_STRING, 0},
+ {"SSH_PRIVATE_KEYFILE", CURLOPT_SSH_PRIVATE_KEYFILE, CURLOT_STRING, 0},
+ {"SSH_PUBLIC_KEYFILE", CURLOPT_SSH_PUBLIC_KEYFILE, CURLOT_STRING, 0},
+ {"SSLCERT", CURLOPT_SSLCERT, CURLOT_STRING, 0},
+ {"SSLCERTPASSWD", CURLOPT_KEYPASSWD, CURLOT_STRING, CURLOT_FLAG_ALIAS},
+ {"SSLCERTTYPE", CURLOPT_SSLCERTTYPE, CURLOT_STRING, 0},
+ {"SSLCERT_BLOB", CURLOPT_SSLCERT_BLOB, CURLOT_BLOB, 0},
+ {"SSLENGINE", CURLOPT_SSLENGINE, CURLOT_STRING, 0},
+ {"SSLENGINE_DEFAULT", CURLOPT_SSLENGINE_DEFAULT, CURLOT_LONG, 0},
+ {"SSLKEY", CURLOPT_SSLKEY, CURLOT_STRING, 0},
+ {"SSLKEYPASSWD", CURLOPT_KEYPASSWD, CURLOT_STRING, CURLOT_FLAG_ALIAS},
+ {"SSLKEYTYPE", CURLOPT_SSLKEYTYPE, CURLOT_STRING, 0},
+ {"SSLKEY_BLOB", CURLOPT_SSLKEY_BLOB, CURLOT_BLOB, 0},
+ {"SSLVERSION", CURLOPT_SSLVERSION, CURLOT_VALUES, 0},
+ {"SSL_CIPHER_LIST", CURLOPT_SSL_CIPHER_LIST, CURLOT_STRING, 0},
+ {"SSL_CTX_DATA", CURLOPT_SSL_CTX_DATA, CURLOT_CBPTR, 0},
+ {"SSL_CTX_FUNCTION", CURLOPT_SSL_CTX_FUNCTION, CURLOT_FUNCTION, 0},
+ {"SSL_EC_CURVES", CURLOPT_SSL_EC_CURVES, CURLOT_STRING, 0},
+ {"SSL_ENABLE_ALPN", CURLOPT_SSL_ENABLE_ALPN, CURLOT_LONG, 0},
+ {"SSL_ENABLE_NPN", CURLOPT_SSL_ENABLE_NPN, CURLOT_LONG, 0},
+ {"SSL_FALSESTART", CURLOPT_SSL_FALSESTART, CURLOT_LONG, 0},
+ {"SSL_OPTIONS", CURLOPT_SSL_OPTIONS, CURLOT_VALUES, 0},
+ {"SSL_SESSIONID_CACHE", CURLOPT_SSL_SESSIONID_CACHE, CURLOT_LONG, 0},
+ {"SSL_VERIFYHOST", CURLOPT_SSL_VERIFYHOST, CURLOT_LONG, 0},
+ {"SSL_VERIFYPEER", CURLOPT_SSL_VERIFYPEER, CURLOT_LONG, 0},
+ {"SSL_VERIFYSTATUS", CURLOPT_SSL_VERIFYSTATUS, CURLOT_LONG, 0},
+ {"STDERR", CURLOPT_STDERR, CURLOT_OBJECT, 0},
+ {"STREAM_DEPENDS", CURLOPT_STREAM_DEPENDS, CURLOT_OBJECT, 0},
+ {"STREAM_DEPENDS_E", CURLOPT_STREAM_DEPENDS_E, CURLOT_OBJECT, 0},
+ {"STREAM_WEIGHT", CURLOPT_STREAM_WEIGHT, CURLOT_LONG, 0},
+ {"SUPPRESS_CONNECT_HEADERS", CURLOPT_SUPPRESS_CONNECT_HEADERS,
+ CURLOT_LONG, 0},
+ {"TCP_FASTOPEN", CURLOPT_TCP_FASTOPEN, CURLOT_LONG, 0},
+ {"TCP_KEEPALIVE", CURLOPT_TCP_KEEPALIVE, CURLOT_LONG, 0},
+ {"TCP_KEEPIDLE", CURLOPT_TCP_KEEPIDLE, CURLOT_LONG, 0},
+ {"TCP_KEEPINTVL", CURLOPT_TCP_KEEPINTVL, CURLOT_LONG, 0},
+ {"TCP_NODELAY", CURLOPT_TCP_NODELAY, CURLOT_LONG, 0},
+ {"TELNETOPTIONS", CURLOPT_TELNETOPTIONS, CURLOT_SLIST, 0},
+ {"TFTP_BLKSIZE", CURLOPT_TFTP_BLKSIZE, CURLOT_LONG, 0},
+ {"TFTP_NO_OPTIONS", CURLOPT_TFTP_NO_OPTIONS, CURLOT_LONG, 0},
+ {"TIMECONDITION", CURLOPT_TIMECONDITION, CURLOT_VALUES, 0},
+ {"TIMEOUT", CURLOPT_TIMEOUT, CURLOT_LONG, 0},
+ {"TIMEOUT_MS", CURLOPT_TIMEOUT_MS, CURLOT_LONG, 0},
+ {"TIMEVALUE", CURLOPT_TIMEVALUE, CURLOT_LONG, 0},
+ {"TIMEVALUE_LARGE", CURLOPT_TIMEVALUE_LARGE, CURLOT_OFF_T, 0},
+ {"TLS13_CIPHERS", CURLOPT_TLS13_CIPHERS, CURLOT_STRING, 0},
+ {"TLSAUTH_PASSWORD", CURLOPT_TLSAUTH_PASSWORD, CURLOT_STRING, 0},
+ {"TLSAUTH_TYPE", CURLOPT_TLSAUTH_TYPE, CURLOT_STRING, 0},
+ {"TLSAUTH_USERNAME", CURLOPT_TLSAUTH_USERNAME, CURLOT_STRING, 0},
+ {"TRAILERDATA", CURLOPT_TRAILERDATA, CURLOT_CBPTR, 0},
+ {"TRAILERFUNCTION", CURLOPT_TRAILERFUNCTION, CURLOT_FUNCTION, 0},
+ {"TRANSFERTEXT", CURLOPT_TRANSFERTEXT, CURLOT_LONG, 0},
+ {"TRANSFER_ENCODING", CURLOPT_TRANSFER_ENCODING, CURLOT_LONG, 0},
+ {"UNIX_SOCKET_PATH", CURLOPT_UNIX_SOCKET_PATH, CURLOT_STRING, 0},
+ {"UNRESTRICTED_AUTH", CURLOPT_UNRESTRICTED_AUTH, CURLOT_LONG, 0},
+ {"UPKEEP_INTERVAL_MS", CURLOPT_UPKEEP_INTERVAL_MS, CURLOT_LONG, 0},
+ {"UPLOAD", CURLOPT_UPLOAD, CURLOT_LONG, 0},
+ {"UPLOAD_BUFFERSIZE", CURLOPT_UPLOAD_BUFFERSIZE, CURLOT_LONG, 0},
+ {"URL", CURLOPT_URL, CURLOT_STRING, 0},
+ {"USERAGENT", CURLOPT_USERAGENT, CURLOT_STRING, 0},
+ {"USERNAME", CURLOPT_USERNAME, CURLOT_STRING, 0},
+ {"USERPWD", CURLOPT_USERPWD, CURLOT_STRING, 0},
+ {"USE_SSL", CURLOPT_USE_SSL, CURLOT_VALUES, 0},
+ {"VERBOSE", CURLOPT_VERBOSE, CURLOT_LONG, 0},
+ {"WILDCARDMATCH", CURLOPT_WILDCARDMATCH, CURLOT_LONG, 0},
+ {"WRITEDATA", CURLOPT_WRITEDATA, CURLOT_CBPTR, 0},
+ {"WRITEFUNCTION", CURLOPT_WRITEFUNCTION, CURLOT_FUNCTION, 0},
+ {"WRITEHEADER", CURLOPT_HEADERDATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS},
+ {"WS_OPTIONS", CURLOPT_WS_OPTIONS, CURLOT_LONG, 0},
+ {"XFERINFODATA", CURLOPT_XFERINFODATA, CURLOT_CBPTR, 0},
+ {"XFERINFOFUNCTION", CURLOPT_XFERINFOFUNCTION, CURLOT_FUNCTION, 0},
+ {"XOAUTH2_BEARER", CURLOPT_XOAUTH2_BEARER, CURLOT_STRING, 0},
+ {NULL, CURLOPT_LASTENTRY, CURLOT_LONG, 0} /* end of table */
+};
+
+#ifdef DEBUGBUILD
+/*
+ * Curl_easyopts_check() is a debug-only function that returns non-zero
+ * if this source file is not in sync with the options listed in curl/curl.h
+ */
+int Curl_easyopts_check(void)
+{
+ return ((CURLOPT_LASTENTRY%10000) != (322 + 1));
+}
+#endif
diff --git a/libs/libcurl/src/easyoptions.h b/libs/libcurl/src/easyoptions.h index 33f816d6b6..c72e397045 100644 --- a/libs/libcurl/src/easyoptions.h +++ b/libs/libcurl/src/easyoptions.h @@ -1,37 +1,37 @@ -#ifndef HEADER_CURL_EASYOPTIONS_H -#define HEADER_CURL_EASYOPTIONS_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - -/* should probably go into the public header */ - -#include <curl/curl.h> - -/* generated table with all easy options */ -extern struct curl_easyoption Curl_easyopts[]; - -#ifdef DEBUGBUILD -int Curl_easyopts_check(void); -#endif -#endif +#ifndef HEADER_CURL_EASYOPTIONS_H
+#define HEADER_CURL_EASYOPTIONS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+/* should probably go into the public header */
+
+#include <curl/curl.h>
+
+/* generated table with all easy options */
+extern struct curl_easyoption Curl_easyopts[];
+
+#ifdef DEBUGBUILD
+int Curl_easyopts_check(void);
+#endif
+#endif
diff --git a/libs/libcurl/src/escape.c b/libs/libcurl/src/escape.c index da7e5524f7..b6f285548a 100644 --- a/libs/libcurl/src/escape.c +++ b/libs/libcurl/src/escape.c @@ -1,229 +1,229 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - -/* Escape and unescape URL encoding in strings. The functions return a new - * allocated string or NULL if an error occurred. */ - -#include "curl_setup.h" - -#include <curl/curl.h> - -#include "urldata.h" -#include "warnless.h" -#include "escape.h" -#include "strdup.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* Portable character check (remember EBCDIC). Do not use isalnum() because - its behavior is altered by the current locale. - See https://datatracker.ietf.org/doc/html/rfc3986#section-2.3 -*/ -bool Curl_isunreserved(unsigned char in) -{ - switch(in) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - case 'a': case 'b': case 'c': case 'd': case 'e': - case 'f': case 'g': case 'h': case 'i': case 'j': - case 'k': case 'l': case 'm': case 'n': case 'o': - case 'p': case 'q': case 'r': case 's': case 't': - case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': - case 'A': case 'B': case 'C': case 'D': case 'E': - case 'F': case 'G': case 'H': case 'I': case 'J': - case 'K': case 'L': case 'M': case 'N': case 'O': - case 'P': case 'Q': case 'R': case 'S': case 'T': - case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': - case '-': case '.': case '_': case '~': - return TRUE; - default: - break; - } - return FALSE; -} - -/* for ABI-compatibility with previous versions */ -char *curl_escape(const char *string, int inlength) -{ - return curl_easy_escape(NULL, string, inlength); -} - -/* for ABI-compatibility with previous versions */ -char *curl_unescape(const char *string, int length) -{ - return curl_easy_unescape(NULL, string, length, NULL); -} - -/* Escapes for URL the given unescaped string of given length. - * 'data' is ignored since 7.82.0. - */ -char *curl_easy_escape(struct Curl_easy *data, const char *string, - int inlength) -{ - size_t length; - struct dynbuf d; - (void)data; - - if(inlength < 0) - return NULL; - - Curl_dyn_init(&d, CURL_MAX_INPUT_LENGTH * 3); - - length = (inlength?(size_t)inlength:strlen(string)); - if(!length) - return strdup(""); - - while(length--) { - unsigned char in = *string; /* we need to treat the characters unsigned */ - - if(Curl_isunreserved(in)) { - /* append this */ - if(Curl_dyn_addn(&d, &in, 1)) - return NULL; - } - else { - /* encode it */ - if(Curl_dyn_addf(&d, "%%%02X", in)) - return NULL; - } - string++; - } - - return Curl_dyn_ptr(&d); -} - -/* - * Curl_urldecode() URL decodes the given string. - * - * Returns a pointer to a malloced string in *ostring with length given in - * *olen. If length == 0, the length is assumed to be strlen(string). - * - * ctrl options: - * - REJECT_NADA: accept everything - * - REJECT_CTRL: rejects control characters (byte codes lower than 32) in - * the data - * - REJECT_ZERO: rejects decoded zero bytes - * - * The values for the enum starts at 2, to make the assert detect legacy - * invokes that used TRUE/FALSE (0 and 1). - */ - -CURLcode Curl_urldecode(const char *string, size_t length, - char **ostring, size_t *olen, - enum urlreject ctrl) -{ - size_t alloc; - char *ns; - size_t strindex = 0; - unsigned long hex; - - DEBUGASSERT(string); - DEBUGASSERT(ctrl >= REJECT_NADA); /* crash on TRUE/FALSE */ - - alloc = (length?length:strlen(string)) + 1; - ns = malloc(alloc); - - if(!ns) - return CURLE_OUT_OF_MEMORY; - - while(--alloc > 0) { - unsigned char in = *string; - if(('%' == in) && (alloc > 2) && - ISXDIGIT(string[1]) && ISXDIGIT(string[2])) { - /* this is two hexadecimal digits following a '%' */ - char hexstr[3]; - char *ptr; - hexstr[0] = string[1]; - hexstr[1] = string[2]; - hexstr[2] = 0; - - hex = strtoul(hexstr, &ptr, 16); - - in = curlx_ultouc(hex); /* this long is never bigger than 255 anyway */ - - string += 2; - alloc -= 2; - } - - if(((ctrl == REJECT_CTRL) && (in < 0x20)) || - ((ctrl == REJECT_ZERO) && (in == 0))) { - free(ns); - return CURLE_URL_MALFORMAT; - } - - ns[strindex++] = in; - string++; - } - ns[strindex] = 0; /* terminate it */ - - if(olen) - /* store output size */ - *olen = strindex; - - /* store output string */ - *ostring = ns; - - return CURLE_OK; -} - -/* - * Unescapes the given URL escaped string of given length. Returns a - * pointer to a malloced string with length given in *olen. - * If length == 0, the length is assumed to be strlen(string). - * If olen == NULL, no output length is stored. - * 'data' is ignored since 7.82.0. - */ -char *curl_easy_unescape(struct Curl_easy *data, const char *string, - int length, int *olen) -{ - char *str = NULL; - (void)data; - if(length >= 0) { - size_t inputlen = length; - size_t outputlen; - CURLcode res = Curl_urldecode(string, inputlen, &str, &outputlen, - REJECT_NADA); - if(res) - return NULL; - - if(olen) { - if(outputlen <= (size_t) INT_MAX) - *olen = curlx_uztosi(outputlen); - else - /* too large to return in an int, fail! */ - Curl_safefree(str); - } - } - return str; -} - -/* For operating systems/environments that use different malloc/free - systems for the app and for this library, we provide a free that uses - the library's memory system */ -void curl_free(void *p) -{ - free(p); -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+/* Escape and unescape URL encoding in strings. The functions return a new
+ * allocated string or NULL if an error occurred. */
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "urldata.h"
+#include "warnless.h"
+#include "escape.h"
+#include "strdup.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* Portable character check (remember EBCDIC). Do not use isalnum() because
+ its behavior is altered by the current locale.
+ See https://datatracker.ietf.org/doc/html/rfc3986#section-2.3
+*/
+bool Curl_isunreserved(unsigned char in)
+{
+ switch(in) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f': case 'g': case 'h': case 'i': case 'j':
+ case 'k': case 'l': case 'm': case 'n': case 'o':
+ case 'p': case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F': case 'G': case 'H': case 'I': case 'J':
+ case 'K': case 'L': case 'M': case 'N': case 'O':
+ case 'P': case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ case '-': case '.': case '_': case '~':
+ return TRUE;
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+/* for ABI-compatibility with previous versions */
+char *curl_escape(const char *string, int inlength)
+{
+ return curl_easy_escape(NULL, string, inlength);
+}
+
+/* for ABI-compatibility with previous versions */
+char *curl_unescape(const char *string, int length)
+{
+ return curl_easy_unescape(NULL, string, length, NULL);
+}
+
+/* Escapes for URL the given unescaped string of given length.
+ * 'data' is ignored since 7.82.0.
+ */
+char *curl_easy_escape(struct Curl_easy *data, const char *string,
+ int inlength)
+{
+ size_t length;
+ struct dynbuf d;
+ (void)data;
+
+ if(inlength < 0)
+ return NULL;
+
+ Curl_dyn_init(&d, CURL_MAX_INPUT_LENGTH * 3);
+
+ length = (inlength?(size_t)inlength:strlen(string));
+ if(!length)
+ return strdup("");
+
+ while(length--) {
+ unsigned char in = *string; /* we need to treat the characters unsigned */
+
+ if(Curl_isunreserved(in)) {
+ /* append this */
+ if(Curl_dyn_addn(&d, &in, 1))
+ return NULL;
+ }
+ else {
+ /* encode it */
+ if(Curl_dyn_addf(&d, "%%%02X", in))
+ return NULL;
+ }
+ string++;
+ }
+
+ return Curl_dyn_ptr(&d);
+}
+
+/*
+ * Curl_urldecode() URL decodes the given string.
+ *
+ * Returns a pointer to a malloced string in *ostring with length given in
+ * *olen. If length == 0, the length is assumed to be strlen(string).
+ *
+ * ctrl options:
+ * - REJECT_NADA: accept everything
+ * - REJECT_CTRL: rejects control characters (byte codes lower than 32) in
+ * the data
+ * - REJECT_ZERO: rejects decoded zero bytes
+ *
+ * The values for the enum starts at 2, to make the assert detect legacy
+ * invokes that used TRUE/FALSE (0 and 1).
+ */
+
+CURLcode Curl_urldecode(const char *string, size_t length,
+ char **ostring, size_t *olen,
+ enum urlreject ctrl)
+{
+ size_t alloc;
+ char *ns;
+ size_t strindex = 0;
+ unsigned long hex;
+
+ DEBUGASSERT(string);
+ DEBUGASSERT(ctrl >= REJECT_NADA); /* crash on TRUE/FALSE */
+
+ alloc = (length?length:strlen(string)) + 1;
+ ns = malloc(alloc);
+
+ if(!ns)
+ return CURLE_OUT_OF_MEMORY;
+
+ while(--alloc > 0) {
+ unsigned char in = *string;
+ if(('%' == in) && (alloc > 2) &&
+ ISXDIGIT(string[1]) && ISXDIGIT(string[2])) {
+ /* this is two hexadecimal digits following a '%' */
+ char hexstr[3];
+ char *ptr;
+ hexstr[0] = string[1];
+ hexstr[1] = string[2];
+ hexstr[2] = 0;
+
+ hex = strtoul(hexstr, &ptr, 16);
+
+ in = curlx_ultouc(hex); /* this long is never bigger than 255 anyway */
+
+ string += 2;
+ alloc -= 2;
+ }
+
+ if(((ctrl == REJECT_CTRL) && (in < 0x20)) ||
+ ((ctrl == REJECT_ZERO) && (in == 0))) {
+ free(ns);
+ return CURLE_URL_MALFORMAT;
+ }
+
+ ns[strindex++] = in;
+ string++;
+ }
+ ns[strindex] = 0; /* terminate it */
+
+ if(olen)
+ /* store output size */
+ *olen = strindex;
+
+ /* store output string */
+ *ostring = ns;
+
+ return CURLE_OK;
+}
+
+/*
+ * Unescapes the given URL escaped string of given length. Returns a
+ * pointer to a malloced string with length given in *olen.
+ * If length == 0, the length is assumed to be strlen(string).
+ * If olen == NULL, no output length is stored.
+ * 'data' is ignored since 7.82.0.
+ */
+char *curl_easy_unescape(struct Curl_easy *data, const char *string,
+ int length, int *olen)
+{
+ char *str = NULL;
+ (void)data;
+ if(length >= 0) {
+ size_t inputlen = (size_t)length;
+ size_t outputlen;
+ CURLcode res = Curl_urldecode(string, inputlen, &str, &outputlen,
+ REJECT_NADA);
+ if(res)
+ return NULL;
+
+ if(olen) {
+ if(outputlen <= (size_t) INT_MAX)
+ *olen = curlx_uztosi(outputlen);
+ else
+ /* too large to return in an int, fail! */
+ Curl_safefree(str);
+ }
+ }
+ return str;
+}
+
+/* For operating systems/environments that use different malloc/free
+ systems for the app and for this library, we provide a free that uses
+ the library's memory system */
+void curl_free(void *p)
+{
+ free(p);
+}
diff --git a/libs/libcurl/src/escape.h b/libs/libcurl/src/escape.h index 61d4611bf7..f59fa468d2 100644 --- a/libs/libcurl/src/escape.h +++ b/libs/libcurl/src/escape.h @@ -1,41 +1,41 @@ -#ifndef HEADER_CURL_ESCAPE_H -#define HEADER_CURL_ESCAPE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ -/* Escape and unescape URL encoding in strings. The functions return a new - * allocated string or NULL if an error occurred. */ - -bool Curl_isunreserved(unsigned char in); - -enum urlreject { - REJECT_NADA = 2, - REJECT_CTRL, - REJECT_ZERO -}; - -CURLcode Curl_urldecode(const char *string, size_t length, - char **ostring, size_t *olen, - enum urlreject ctrl); - -#endif /* HEADER_CURL_ESCAPE_H */ +#ifndef HEADER_CURL_ESCAPE_H
+#define HEADER_CURL_ESCAPE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+/* Escape and unescape URL encoding in strings. The functions return a new
+ * allocated string or NULL if an error occurred. */
+
+bool Curl_isunreserved(unsigned char in);
+
+enum urlreject {
+ REJECT_NADA = 2,
+ REJECT_CTRL,
+ REJECT_ZERO
+};
+
+CURLcode Curl_urldecode(const char *string, size_t length,
+ char **ostring, size_t *olen,
+ enum urlreject ctrl);
+
+#endif /* HEADER_CURL_ESCAPE_H */
diff --git a/libs/libcurl/src/file.c b/libs/libcurl/src/file.c index d82d57b463..33af696463 100644 --- a/libs/libcurl/src/file.c +++ b/libs/libcurl/src/file.c @@ -1,576 +1,587 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_FILE - -#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 HAVE_NET_IF_H -#include <net/if.h> -#endif -#ifdef HAVE_SYS_IOCTL_H -#include <sys/ioctl.h> -#endif - -#ifdef HAVE_SYS_PARAM_H -#include <sys/param.h> -#endif - -#ifdef HAVE_FCNTL_H -#include <fcntl.h> -#endif - -#include "strtoofft.h" -#include "urldata.h" -#include <curl/curl.h> -#include "progress.h" -#include "sendf.h" -#include "escape.h" -#include "file.h" -#include "speedcheck.h" -#include "getinfo.h" -#include "transfer.h" -#include "url.h" -#include "parsedate.h" /* for the week day and month names */ -#include "warnless.h" -#include "curl_range.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#if defined(WIN32) || defined(MSDOS) || defined(__EMX__) -#define DOS_FILESYSTEM 1 -#elif defined(__amigaos4__) -#define AMIGA_FILESYSTEM 1 -#endif - -#ifdef OPEN_NEEDS_ARG3 -# define open_readonly(p,f) open((p),(f),(0)) -#else -# define open_readonly(p,f) open((p),(f)) -#endif - -/* - * Forward declarations. - */ - -static CURLcode file_do(struct Curl_easy *data, bool *done); -static CURLcode file_done(struct Curl_easy *data, - CURLcode status, bool premature); -static CURLcode file_connect(struct Curl_easy *data, bool *done); -static CURLcode file_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection); -static CURLcode file_setup_connection(struct Curl_easy *data, - struct connectdata *conn); - -/* - * FILE scheme handler. - */ - -const struct Curl_handler Curl_handler_file = { - "FILE", /* scheme */ - file_setup_connection, /* setup_connection */ - file_do, /* do_it */ - file_done, /* done */ - ZERO_NULL, /* do_more */ - file_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - file_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - 0, /* defport */ - CURLPROTO_FILE, /* protocol */ - CURLPROTO_FILE, /* family */ - PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */ -}; - - -static CURLcode file_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - (void)conn; - /* allocate the FILE specific struct */ - data->req.p.file = calloc(1, sizeof(struct FILEPROTO)); - if(!data->req.p.file) - return CURLE_OUT_OF_MEMORY; - - return CURLE_OK; -} - -/* - * file_connect() gets called from Curl_protocol_connect() to allow us to - * do protocol-specific actions at connect-time. We emulate a - * connect-then-transfer protocol and "connect" to the file here - */ -static CURLcode file_connect(struct Curl_easy *data, bool *done) -{ - char *real_path; - struct FILEPROTO *file = data->req.p.file; - int fd; -#ifdef DOS_FILESYSTEM - size_t i; - char *actual_path; -#endif - size_t real_path_len; - - CURLcode result = Curl_urldecode(data->state.up.path, 0, &real_path, - &real_path_len, REJECT_ZERO); - if(result) - return result; - -#ifdef DOS_FILESYSTEM - /* If the first character is a slash, and there's - something that looks like a drive at the beginning of - the path, skip the slash. If we remove the initial - slash in all cases, paths without drive letters end up - relative to the current directory which isn't how - browsers work. - - Some browsers accept | instead of : as the drive letter - separator, so we do too. - - On other platforms, we need the slash to indicate an - absolute pathname. On Windows, absolute paths start - with a drive letter. - */ - actual_path = real_path; - if((actual_path[0] == '/') && - actual_path[1] && - (actual_path[2] == ':' || actual_path[2] == '|')) { - actual_path[2] = ':'; - actual_path++; - real_path_len--; - } - - /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */ - for(i = 0; i < real_path_len; ++i) - if(actual_path[i] == '/') - actual_path[i] = '\\'; - else if(!actual_path[i]) { /* binary zero */ - Curl_safefree(real_path); - return CURLE_URL_MALFORMAT; - } - - fd = open_readonly(actual_path, O_RDONLY|O_BINARY); - file->path = actual_path; -#else - if(memchr(real_path, 0, real_path_len)) { - /* binary zeroes indicate foul play */ - Curl_safefree(real_path); - return CURLE_URL_MALFORMAT; - } - - #ifdef AMIGA_FILESYSTEM - /* - * A leading slash in an AmigaDOS path denotes the parent - * directory, and hence we block this as it is relative. - * Absolute paths start with 'volumename:', so we check for - * this first. Failing that, we treat the path as a real unix - * path, but only if the application was compiled with -lunix. - */ - fd = -1; - file->path = real_path; - - if(real_path[0] == '/') { - extern int __unix_path_semantics; - if(strchr(real_path + 1, ':')) { - /* Amiga absolute path */ - fd = open_readonly(real_path + 1, O_RDONLY); - file->path++; - } - else if(__unix_path_semantics) { - /* -lunix fallback */ - fd = open_readonly(real_path, O_RDONLY); - } - } - #else - fd = open_readonly(real_path, O_RDONLY); - file->path = real_path; - #endif -#endif - file->freepath = real_path; /* free this when done */ - - file->fd = fd; - if(!data->set.upload && (fd == -1)) { - failf(data, "Couldn't open file %s", data->state.up.path); - file_done(data, CURLE_FILE_COULDNT_READ_FILE, FALSE); - return CURLE_FILE_COULDNT_READ_FILE; - } - *done = TRUE; - - return CURLE_OK; -} - -static CURLcode file_done(struct Curl_easy *data, - CURLcode status, bool premature) -{ - struct FILEPROTO *file = data->req.p.file; - (void)status; /* not used */ - (void)premature; /* not used */ - - if(file) { - Curl_safefree(file->freepath); - file->path = NULL; - if(file->fd != -1) - close(file->fd); - file->fd = -1; - } - - return CURLE_OK; -} - -static CURLcode file_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection) -{ - (void)dead_connection; /* not used */ - (void)conn; - return file_done(data, CURLE_OK, FALSE); -} - -#ifdef DOS_FILESYSTEM -#define DIRSEP '\\' -#else -#define DIRSEP '/' -#endif - -static CURLcode file_upload(struct Curl_easy *data) -{ - struct FILEPROTO *file = data->req.p.file; - const char *dir = strchr(file->path, DIRSEP); - int fd; - int mode; - CURLcode result = CURLE_OK; - char *buf = data->state.buffer; - curl_off_t bytecount = 0; - struct_stat file_stat; - const char *buf2; - - /* - * Since FILE: doesn't do the full init, we need to provide some extra - * assignments here. - */ - data->req.upload_fromhere = buf; - - if(!dir) - return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */ - - if(!dir[1]) - return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */ - -#ifdef O_BINARY -#define MODE_DEFAULT O_WRONLY|O_CREAT|O_BINARY -#else -#define MODE_DEFAULT O_WRONLY|O_CREAT -#endif - - if(data->state.resume_from) - mode = MODE_DEFAULT|O_APPEND; - else - mode = MODE_DEFAULT|O_TRUNC; - - fd = open(file->path, mode, data->set.new_file_perms); - if(fd < 0) { - failf(data, "Can't open %s for writing", file->path); - return CURLE_WRITE_ERROR; - } - - if(-1 != data->state.infilesize) - /* known size of data to "upload" */ - Curl_pgrsSetUploadSize(data, data->state.infilesize); - - /* treat the negative resume offset value as the case of "-" */ - if(data->state.resume_from < 0) { - if(fstat(fd, &file_stat)) { - close(fd); - failf(data, "Can't get the size of %s", file->path); - return CURLE_WRITE_ERROR; - } - data->state.resume_from = (curl_off_t)file_stat.st_size; - } - - while(!result) { - size_t nread; - size_t nwrite; - size_t readcount; - result = Curl_fillreadbuffer(data, data->set.buffer_size, &readcount); - if(result) - break; - - if(!readcount) - break; - - nread = readcount; - - /*skip bytes before resume point*/ - if(data->state.resume_from) { - if((curl_off_t)nread <= data->state.resume_from) { - data->state.resume_from -= nread; - nread = 0; - buf2 = buf; - } - else { - buf2 = buf + data->state.resume_from; - nread -= (size_t)data->state.resume_from; - data->state.resume_from = 0; - } - } - else - buf2 = buf; - - /* write the data to the target */ - nwrite = write(fd, buf2, nread); - if(nwrite != nread) { - result = CURLE_SEND_ERROR; - break; - } - - bytecount += nread; - - Curl_pgrsSetUploadCounter(data, bytecount); - - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - else - result = Curl_speedcheck(data, Curl_now()); - } - if(!result && Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - - close(fd); - - return result; -} - -/* - * file_do() is the protocol-specific function for the do-phase, separated - * from the connect-phase above. Other protocols merely setup the transfer in - * the do-phase, to have it done in the main transfer loop but since some - * platforms we support don't allow select()ing etc on file handles (as - * opposed to sockets) we instead perform the whole do-operation in this - * function. - */ -static CURLcode file_do(struct Curl_easy *data, bool *done) -{ - /* This implementation ignores the host name in conformance with - RFC 1738. Only local files (reachable via the standard file system) - are supported. This means that files on remotely mounted directories - (via NFS, Samba, NT sharing) can be accessed through a file:// URL - */ - CURLcode result = CURLE_OK; - struct_stat statbuf; /* struct_stat instead of struct stat just to allow the - Windows version to have a different struct without - having to redefine the simple word 'stat' */ - curl_off_t expected_size = -1; - bool size_known; - bool fstated = FALSE; - char *buf = data->state.buffer; - curl_off_t bytecount = 0; - int fd; - struct FILEPROTO *file; - - *done = TRUE; /* unconditionally */ - - Curl_pgrsStartNow(data); - - if(data->set.upload) - return file_upload(data); - - file = data->req.p.file; - - /* get the fd from the connection phase */ - fd = file->fd; - - /* VMS: This only works reliable for STREAMLF files */ - if(-1 != fstat(fd, &statbuf)) { - if(!S_ISDIR(statbuf.st_mode)) - expected_size = statbuf.st_size; - /* and store the modification time */ - data->info.filetime = statbuf.st_mtime; - fstated = TRUE; - } - - if(fstated && !data->state.range && data->set.timecondition) { - if(!Curl_meets_timecondition(data, data->info.filetime)) { - *done = TRUE; - return CURLE_OK; - } - } - - if(fstated) { - time_t filetime; - struct tm buffer; - const struct tm *tm = &buffer; - char header[80]; - int headerlen; - char accept_ranges[24]= { "Accept-ranges: bytes\r\n" }; - if(expected_size >= 0) { - headerlen = msnprintf(header, sizeof(header), - "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", - expected_size); - result = Curl_client_write(data, CLIENTWRITE_HEADER, header, headerlen); - if(result) - return result; - - result = Curl_client_write(data, CLIENTWRITE_HEADER, - accept_ranges, strlen(accept_ranges)); - if(result != CURLE_OK) - return result; - } - - filetime = (time_t)statbuf.st_mtime; - result = Curl_gmtime(filetime, &buffer); - if(result) - return result; - - /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ - headerlen = msnprintf(header, sizeof(header), - "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n%s", - Curl_wkday[tm->tm_wday?tm->tm_wday-1:6], - tm->tm_mday, - Curl_month[tm->tm_mon], - tm->tm_year + 1900, - tm->tm_hour, - tm->tm_min, - tm->tm_sec, - data->set.opt_no_body ? "": "\r\n"); - result = Curl_client_write(data, CLIENTWRITE_HEADER, header, headerlen); - if(result) - return result; - /* set the file size to make it available post transfer */ - Curl_pgrsSetDownloadSize(data, expected_size); - if(data->set.opt_no_body) - return result; - } - - /* Check whether file range has been specified */ - result = Curl_range(data); - if(result) - return result; - - /* Adjust the start offset in case we want to get the N last bytes - * of the stream if the filesize could be determined */ - if(data->state.resume_from < 0) { - if(!fstated) { - failf(data, "Can't get the size of file."); - return CURLE_READ_ERROR; - } - data->state.resume_from += (curl_off_t)statbuf.st_size; - } - - if(data->state.resume_from > 0) { - /* We check explicitly if we have a start offset, because - * expected_size may be -1 if we don't know how large the file is, - * in which case we should not adjust it. */ - if(data->state.resume_from <= expected_size) - expected_size -= data->state.resume_from; - else { - failf(data, "failed to resume file:// transfer"); - return CURLE_BAD_DOWNLOAD_RESUME; - } - } - - /* A high water mark has been specified so we obey... */ - if(data->req.maxdownload > 0) - expected_size = data->req.maxdownload; - - if(!fstated || (expected_size <= 0)) - size_known = FALSE; - else - size_known = TRUE; - - /* The following is a shortcut implementation of file reading - this is both more efficient than the former call to download() and - it avoids problems with select() and recv() on file descriptors - in Winsock */ - if(size_known) - Curl_pgrsSetDownloadSize(data, expected_size); - - if(data->state.resume_from) { - if(data->state.resume_from != - lseek(fd, data->state.resume_from, SEEK_SET)) - return CURLE_BAD_DOWNLOAD_RESUME; - } - - Curl_pgrsTime(data, TIMER_STARTTRANSFER); - - while(!result) { - ssize_t nread; - /* Don't fill a whole buffer if we want less than all data */ - size_t bytestoread; - - if(size_known) { - bytestoread = (expected_size < data->set.buffer_size) ? - curlx_sotouz(expected_size) : (size_t)data->set.buffer_size; - } - else - bytestoread = data->set.buffer_size-1; - - nread = read(fd, buf, bytestoread); - - if(nread > 0) - buf[nread] = 0; - - if(nread <= 0 || (size_known && (expected_size == 0))) - break; - - bytecount += nread; - if(size_known) - expected_size -= nread; - - result = Curl_client_write(data, CLIENTWRITE_BODY, buf, nread); - if(result) - return result; - - Curl_pgrsSetDownloadCounter(data, bytecount); - - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - else - result = Curl_speedcheck(data, Curl_now()); - } - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - - return result; -} - -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_FILE
+
+#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 HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "strtoofft.h"
+#include "urldata.h"
+#include <curl/curl.h>
+#include "progress.h"
+#include "sendf.h"
+#include "escape.h"
+#include "file.h"
+#include "speedcheck.h"
+#include "getinfo.h"
+#include "transfer.h"
+#include "url.h"
+#include "parsedate.h" /* for the week day and month names */
+#include "warnless.h"
+#include "curl_range.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
+#define DOS_FILESYSTEM 1
+#elif defined(__amigaos4__)
+#define AMIGA_FILESYSTEM 1
+#endif
+
+#ifdef OPEN_NEEDS_ARG3
+# define open_readonly(p,f) open((p),(f),(0))
+#else
+# define open_readonly(p,f) open((p),(f))
+#endif
+
+/*
+ * Forward declarations.
+ */
+
+static CURLcode file_do(struct Curl_easy *data, bool *done);
+static CURLcode file_done(struct Curl_easy *data,
+ CURLcode status, bool premature);
+static CURLcode file_connect(struct Curl_easy *data, bool *done);
+static CURLcode file_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection);
+static CURLcode file_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn);
+
+/*
+ * FILE scheme handler.
+ */
+
+const struct Curl_handler Curl_handler_file = {
+ "FILE", /* scheme */
+ file_setup_connection, /* setup_connection */
+ file_do, /* do_it */
+ file_done, /* done */
+ ZERO_NULL, /* do_more */
+ file_connect, /* connect_it */
+ ZERO_NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ ZERO_NULL, /* proto_getsock */
+ ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ file_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ 0, /* defport */
+ CURLPROTO_FILE, /* protocol */
+ CURLPROTO_FILE, /* family */
+ PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */
+};
+
+
+static CURLcode file_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ (void)conn;
+ /* allocate the FILE specific struct */
+ data->req.p.file = calloc(1, sizeof(struct FILEPROTO));
+ if(!data->req.p.file)
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_OK;
+}
+
+/*
+ * file_connect() gets called from Curl_protocol_connect() to allow us to
+ * do protocol-specific actions at connect-time. We emulate a
+ * connect-then-transfer protocol and "connect" to the file here
+ */
+static CURLcode file_connect(struct Curl_easy *data, bool *done)
+{
+ char *real_path;
+ struct FILEPROTO *file = data->req.p.file;
+ int fd;
+#ifdef DOS_FILESYSTEM
+ size_t i;
+ char *actual_path;
+#endif
+ size_t real_path_len;
+ CURLcode result;
+
+ if(file->path) {
+ /* already connected.
+ * the handler->connect_it() is normally only called once, but
+ * FILE does a special check on setting up the connection which
+ * calls this explicitly. */
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ result = Curl_urldecode(data->state.up.path, 0, &real_path,
+ &real_path_len, REJECT_ZERO);
+ if(result)
+ return result;
+
+#ifdef DOS_FILESYSTEM
+ /* If the first character is a slash, and there's
+ something that looks like a drive at the beginning of
+ the path, skip the slash. If we remove the initial
+ slash in all cases, paths without drive letters end up
+ relative to the current directory which isn't how
+ browsers work.
+
+ Some browsers accept | instead of : as the drive letter
+ separator, so we do too.
+
+ On other platforms, we need the slash to indicate an
+ absolute pathname. On Windows, absolute paths start
+ with a drive letter.
+ */
+ actual_path = real_path;
+ if((actual_path[0] == '/') &&
+ actual_path[1] &&
+ (actual_path[2] == ':' || actual_path[2] == '|')) {
+ actual_path[2] = ':';
+ actual_path++;
+ real_path_len--;
+ }
+
+ /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */
+ for(i = 0; i < real_path_len; ++i)
+ if(actual_path[i] == '/')
+ actual_path[i] = '\\';
+ else if(!actual_path[i]) { /* binary zero */
+ Curl_safefree(real_path);
+ return CURLE_URL_MALFORMAT;
+ }
+
+ fd = open_readonly(actual_path, O_RDONLY|O_BINARY);
+ file->path = actual_path;
+#else
+ if(memchr(real_path, 0, real_path_len)) {
+ /* binary zeroes indicate foul play */
+ Curl_safefree(real_path);
+ return CURLE_URL_MALFORMAT;
+ }
+
+ #ifdef AMIGA_FILESYSTEM
+ /*
+ * A leading slash in an AmigaDOS path denotes the parent
+ * directory, and hence we block this as it is relative.
+ * Absolute paths start with 'volumename:', so we check for
+ * this first. Failing that, we treat the path as a real unix
+ * path, but only if the application was compiled with -lunix.
+ */
+ fd = -1;
+ file->path = real_path;
+
+ if(real_path[0] == '/') {
+ extern int __unix_path_semantics;
+ if(strchr(real_path + 1, ':')) {
+ /* Amiga absolute path */
+ fd = open_readonly(real_path + 1, O_RDONLY);
+ file->path++;
+ }
+ else if(__unix_path_semantics) {
+ /* -lunix fallback */
+ fd = open_readonly(real_path, O_RDONLY);
+ }
+ }
+ #else
+ fd = open_readonly(real_path, O_RDONLY);
+ file->path = real_path;
+ #endif
+#endif
+ Curl_safefree(file->freepath);
+ file->freepath = real_path; /* free this when done */
+
+ file->fd = fd;
+ if(!data->set.upload && (fd == -1)) {
+ failf(data, "Couldn't open file %s", data->state.up.path);
+ file_done(data, CURLE_FILE_COULDNT_READ_FILE, FALSE);
+ return CURLE_FILE_COULDNT_READ_FILE;
+ }
+ *done = TRUE;
+
+ return CURLE_OK;
+}
+
+static CURLcode file_done(struct Curl_easy *data,
+ CURLcode status, bool premature)
+{
+ struct FILEPROTO *file = data->req.p.file;
+ (void)status; /* not used */
+ (void)premature; /* not used */
+
+ if(file) {
+ Curl_safefree(file->freepath);
+ file->path = NULL;
+ if(file->fd != -1)
+ close(file->fd);
+ file->fd = -1;
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode file_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection)
+{
+ (void)dead_connection; /* not used */
+ (void)conn;
+ return file_done(data, CURLE_OK, FALSE);
+}
+
+#ifdef DOS_FILESYSTEM
+#define DIRSEP '\\'
+#else
+#define DIRSEP '/'
+#endif
+
+static CURLcode file_upload(struct Curl_easy *data)
+{
+ struct FILEPROTO *file = data->req.p.file;
+ const char *dir = strchr(file->path, DIRSEP);
+ int fd;
+ int mode;
+ CURLcode result = CURLE_OK;
+ char *buf = data->state.buffer;
+ curl_off_t bytecount = 0;
+ struct_stat file_stat;
+ const char *buf2;
+
+ /*
+ * Since FILE: doesn't do the full init, we need to provide some extra
+ * assignments here.
+ */
+ data->req.upload_fromhere = buf;
+
+ if(!dir)
+ return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
+
+ if(!dir[1])
+ return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
+
+#ifdef O_BINARY
+#define MODE_DEFAULT O_WRONLY|O_CREAT|O_BINARY
+#else
+#define MODE_DEFAULT O_WRONLY|O_CREAT
+#endif
+
+ if(data->state.resume_from)
+ mode = MODE_DEFAULT|O_APPEND;
+ else
+ mode = MODE_DEFAULT|O_TRUNC;
+
+ fd = open(file->path, mode, data->set.new_file_perms);
+ if(fd < 0) {
+ failf(data, "Can't open %s for writing", file->path);
+ return CURLE_WRITE_ERROR;
+ }
+
+ if(-1 != data->state.infilesize)
+ /* known size of data to "upload" */
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+
+ /* treat the negative resume offset value as the case of "-" */
+ if(data->state.resume_from < 0) {
+ if(fstat(fd, &file_stat)) {
+ close(fd);
+ failf(data, "Can't get the size of %s", file->path);
+ return CURLE_WRITE_ERROR;
+ }
+ data->state.resume_from = (curl_off_t)file_stat.st_size;
+ }
+
+ while(!result) {
+ size_t nread;
+ ssize_t nwrite;
+ size_t readcount;
+ result = Curl_fillreadbuffer(data, data->set.buffer_size, &readcount);
+ if(result)
+ break;
+
+ if(!readcount)
+ break;
+
+ nread = readcount;
+
+ /* skip bytes before resume point */
+ if(data->state.resume_from) {
+ if((curl_off_t)nread <= data->state.resume_from) {
+ data->state.resume_from -= nread;
+ nread = 0;
+ buf2 = buf;
+ }
+ else {
+ buf2 = buf + data->state.resume_from;
+ nread -= (size_t)data->state.resume_from;
+ data->state.resume_from = 0;
+ }
+ }
+ else
+ buf2 = buf;
+
+ /* write the data to the target */
+ nwrite = write(fd, buf2, nread);
+ if((size_t)nwrite != nread) {
+ result = CURLE_SEND_ERROR;
+ break;
+ }
+
+ bytecount += nread;
+
+ Curl_pgrsSetUploadCounter(data, bytecount);
+
+ if(Curl_pgrsUpdate(data))
+ result = CURLE_ABORTED_BY_CALLBACK;
+ else
+ result = Curl_speedcheck(data, Curl_now());
+ }
+ if(!result && Curl_pgrsUpdate(data))
+ result = CURLE_ABORTED_BY_CALLBACK;
+
+ close(fd);
+
+ return result;
+}
+
+/*
+ * file_do() is the protocol-specific function for the do-phase, separated
+ * from the connect-phase above. Other protocols merely setup the transfer in
+ * the do-phase, to have it done in the main transfer loop but since some
+ * platforms we support don't allow select()ing etc on file handles (as
+ * opposed to sockets) we instead perform the whole do-operation in this
+ * function.
+ */
+static CURLcode file_do(struct Curl_easy *data, bool *done)
+{
+ /* This implementation ignores the host name in conformance with
+ RFC 1738. Only local files (reachable via the standard file system)
+ are supported. This means that files on remotely mounted directories
+ (via NFS, Samba, NT sharing) can be accessed through a file:// URL
+ */
+ CURLcode result = CURLE_OK;
+ struct_stat statbuf; /* struct_stat instead of struct stat just to allow the
+ Windows version to have a different struct without
+ having to redefine the simple word 'stat' */
+ curl_off_t expected_size = -1;
+ bool size_known;
+ bool fstated = FALSE;
+ char *buf = data->state.buffer;
+ curl_off_t bytecount = 0;
+ int fd;
+ struct FILEPROTO *file;
+
+ *done = TRUE; /* unconditionally */
+
+ Curl_pgrsStartNow(data);
+
+ if(data->set.upload)
+ return file_upload(data);
+
+ file = data->req.p.file;
+
+ /* get the fd from the connection phase */
+ fd = file->fd;
+
+ /* VMS: This only works reliable for STREAMLF files */
+ if(-1 != fstat(fd, &statbuf)) {
+ if(!S_ISDIR(statbuf.st_mode))
+ expected_size = statbuf.st_size;
+ /* and store the modification time */
+ data->info.filetime = statbuf.st_mtime;
+ fstated = TRUE;
+ }
+
+ if(fstated && !data->state.range && data->set.timecondition) {
+ if(!Curl_meets_timecondition(data, data->info.filetime)) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+ }
+
+ if(fstated) {
+ time_t filetime;
+ struct tm buffer;
+ const struct tm *tm = &buffer;
+ char header[80];
+ int headerlen;
+ char accept_ranges[24]= { "Accept-ranges: bytes\r\n" };
+ if(expected_size >= 0) {
+ headerlen = msnprintf(header, sizeof(header),
+ "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n",
+ expected_size);
+ result = Curl_client_write(data, CLIENTWRITE_HEADER, header, headerlen);
+ if(result)
+ return result;
+
+ result = Curl_client_write(data, CLIENTWRITE_HEADER,
+ accept_ranges, strlen(accept_ranges));
+ if(result != CURLE_OK)
+ return result;
+ }
+
+ filetime = (time_t)statbuf.st_mtime;
+ result = Curl_gmtime(filetime, &buffer);
+ if(result)
+ return result;
+
+ /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
+ headerlen = msnprintf(header, sizeof(header),
+ "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n%s",
+ Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
+ tm->tm_mday,
+ Curl_month[tm->tm_mon],
+ tm->tm_year + 1900,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec,
+ data->req.no_body ? "": "\r\n");
+ result = Curl_client_write(data, CLIENTWRITE_HEADER, header, headerlen);
+ if(result)
+ return result;
+ /* set the file size to make it available post transfer */
+ Curl_pgrsSetDownloadSize(data, expected_size);
+ if(data->req.no_body)
+ return result;
+ }
+
+ /* Check whether file range has been specified */
+ result = Curl_range(data);
+ if(result)
+ return result;
+
+ /* Adjust the start offset in case we want to get the N last bytes
+ * of the stream if the filesize could be determined */
+ if(data->state.resume_from < 0) {
+ if(!fstated) {
+ failf(data, "Can't get the size of file.");
+ return CURLE_READ_ERROR;
+ }
+ data->state.resume_from += (curl_off_t)statbuf.st_size;
+ }
+
+ if(data->state.resume_from > 0) {
+ /* We check explicitly if we have a start offset, because
+ * expected_size may be -1 if we don't know how large the file is,
+ * in which case we should not adjust it. */
+ if(data->state.resume_from <= expected_size)
+ expected_size -= data->state.resume_from;
+ else {
+ failf(data, "failed to resume file:// transfer");
+ return CURLE_BAD_DOWNLOAD_RESUME;
+ }
+ }
+
+ /* A high water mark has been specified so we obey... */
+ if(data->req.maxdownload > 0)
+ expected_size = data->req.maxdownload;
+
+ if(!fstated || (expected_size <= 0))
+ size_known = FALSE;
+ else
+ size_known = TRUE;
+
+ /* The following is a shortcut implementation of file reading
+ this is both more efficient than the former call to download() and
+ it avoids problems with select() and recv() on file descriptors
+ in Winsock */
+ if(size_known)
+ Curl_pgrsSetDownloadSize(data, expected_size);
+
+ if(data->state.resume_from) {
+ if(data->state.resume_from !=
+ lseek(fd, data->state.resume_from, SEEK_SET))
+ return CURLE_BAD_DOWNLOAD_RESUME;
+ }
+
+ Curl_pgrsTime(data, TIMER_STARTTRANSFER);
+
+ while(!result) {
+ ssize_t nread;
+ /* Don't fill a whole buffer if we want less than all data */
+ size_t bytestoread;
+
+ if(size_known) {
+ bytestoread = (expected_size < data->set.buffer_size) ?
+ curlx_sotouz(expected_size) : (size_t)data->set.buffer_size;
+ }
+ else
+ bytestoread = data->set.buffer_size-1;
+
+ nread = read(fd, buf, bytestoread);
+
+ if(nread > 0)
+ buf[nread] = 0;
+
+ if(nread <= 0 || (size_known && (expected_size == 0)))
+ break;
+
+ bytecount += nread;
+ if(size_known)
+ expected_size -= nread;
+
+ result = Curl_client_write(data, CLIENTWRITE_BODY, buf, nread);
+ if(result)
+ return result;
+
+ Curl_pgrsSetDownloadCounter(data, bytecount);
+
+ if(Curl_pgrsUpdate(data))
+ result = CURLE_ABORTED_BY_CALLBACK;
+ else
+ result = Curl_speedcheck(data, Curl_now());
+ }
+ if(Curl_pgrsUpdate(data))
+ result = CURLE_ABORTED_BY_CALLBACK;
+
+ return result;
+}
+
+#endif
diff --git a/libs/libcurl/src/file.h b/libs/libcurl/src/file.h index 826d45380d..0f6bc4e3dd 100644 --- a/libs/libcurl/src/file.h +++ b/libs/libcurl/src/file.h @@ -1,42 +1,42 @@ -#ifndef HEADER_CURL_FILE_H -#define HEADER_CURL_FILE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - - -/**************************************************************************** - * FILE unique setup - ***************************************************************************/ -struct FILEPROTO { - char *path; /* the path we operate on */ - char *freepath; /* pointer to the allocated block we must free, this might - differ from the 'path' pointer */ - int fd; /* open file descriptor to read from! */ -}; - -#ifndef CURL_DISABLE_FILE -extern const struct Curl_handler Curl_handler_file; -#endif - -#endif /* HEADER_CURL_FILE_H */ +#ifndef HEADER_CURL_FILE_H
+#define HEADER_CURL_FILE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+
+/****************************************************************************
+ * FILE unique setup
+ ***************************************************************************/
+struct FILEPROTO {
+ char *path; /* the path we operate on */
+ char *freepath; /* pointer to the allocated block we must free, this might
+ differ from the 'path' pointer */
+ int fd; /* open file descriptor to read from! */
+};
+
+#ifndef CURL_DISABLE_FILE
+extern const struct Curl_handler Curl_handler_file;
+#endif
+
+#endif /* HEADER_CURL_FILE_H */
diff --git a/libs/libcurl/src/fileinfo.c b/libs/libcurl/src/fileinfo.c index 7bbf24bdeb..843105da1c 100644 --- a/libs/libcurl/src/fileinfo.c +++ b/libs/libcurl/src/fileinfo.c @@ -1,46 +1,46 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2010 - 2022, 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 "strdup.h" -#include "fileinfo.h" -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -struct fileinfo *Curl_fileinfo_alloc(void) -{ - return calloc(1, sizeof(struct fileinfo)); -} - -void Curl_fileinfo_cleanup(struct fileinfo *finfo) -{ - if(!finfo) - return; - - Curl_safefree(finfo->info.b_data); - free(finfo); -} -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2010 - 2022, 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 "strdup.h"
+#include "fileinfo.h"
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+struct fileinfo *Curl_fileinfo_alloc(void)
+{
+ return calloc(1, sizeof(struct fileinfo));
+}
+
+void Curl_fileinfo_cleanup(struct fileinfo *finfo)
+{
+ if(!finfo)
+ return;
+
+ Curl_safefree(finfo->info.b_data);
+ free(finfo);
+}
+#endif
diff --git a/libs/libcurl/src/fileinfo.h b/libs/libcurl/src/fileinfo.h index 5bad718cc8..ec188c52bd 100644 --- a/libs/libcurl/src/fileinfo.h +++ b/libs/libcurl/src/fileinfo.h @@ -1,38 +1,38 @@ -#ifndef HEADER_CURL_FILEINFO_H -#define HEADER_CURL_FILEINFO_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2010 - 2022, 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 "llist.h" - -struct fileinfo { - struct curl_fileinfo info; - struct Curl_llist_element list; -}; - -struct fileinfo *Curl_fileinfo_alloc(void); -void Curl_fileinfo_cleanup(struct fileinfo *finfo); - -#endif /* HEADER_CURL_FILEINFO_H */ +#ifndef HEADER_CURL_FILEINFO_H
+#define HEADER_CURL_FILEINFO_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2010 - 2022, 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 "llist.h"
+
+struct fileinfo {
+ struct curl_fileinfo info;
+ struct Curl_llist_element list;
+};
+
+struct fileinfo *Curl_fileinfo_alloc(void);
+void Curl_fileinfo_cleanup(struct fileinfo *finfo);
+
+#endif /* HEADER_CURL_FILEINFO_H */
diff --git a/libs/libcurl/src/fopen.c b/libs/libcurl/src/fopen.c index ad3691ba9d..89dfc840f1 100644 --- a/libs/libcurl/src/fopen.c +++ b/libs/libcurl/src/fopen.c @@ -1,113 +1,113 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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) - -#ifdef HAVE_FCNTL_H -#include <fcntl.h> -#endif - -#include "urldata.h" -#include "rand.h" -#include "fopen.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* - * 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 randsuffix[9]; - char *tempstore = NULL; - struct_stat sb; - int fd = -1; - *tempname = NULL; - - if(stat(filename, &sb) == -1 || !S_ISREG(sb.st_mode)) { - /* a non-regular file, fallback to direct fopen() */ - *fh = fopen(filename, FOPEN_WRITETEXT); - if(*fh) - return CURLE_OK; - goto fail; - } - - result = Curl_rand_hex(data, randsuffix, sizeof(randsuffix)); - if(result) - goto fail; - - tempstore = aprintf("%s.%s.tmp", filename, randsuffix); - if(!tempstore) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } - - result = CURLE_WRITE_ERROR; - fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600); - if(fd == -1) - goto fail; - -#ifdef HAVE_FCHMOD - { - struct_stat nsb; - if((fstat(fd, &nsb) != -1) && - (nsb.st_uid == sb.st_uid) && (nsb.st_gid == sb.st_gid)) { - /* if the user and group are the same, clone the original mode */ - if(fchmod(fd, sb.st_mode) == -1) - goto fail; - } - } -#endif - - *fh = fdopen(fd, FOPEN_WRITETEXT); - if(!*fh) - goto fail; - - *tempname = tempstore; - return CURLE_OK; - -fail: - if(fd != -1) { - close(fd); - unlink(tempstore); - } - - free(tempstore); - - *tempname = NULL; - return result; -} - -#endif /* ! disabled */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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)
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "urldata.h"
+#include "rand.h"
+#include "fopen.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * 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 randsuffix[9];
+ char *tempstore = NULL;
+ struct_stat sb;
+ int fd = -1;
+ *tempname = NULL;
+
+ if(stat(filename, &sb) == -1 || !S_ISREG(sb.st_mode)) {
+ /* a non-regular file, fallback to direct fopen() */
+ *fh = fopen(filename, FOPEN_WRITETEXT);
+ if(*fh)
+ return CURLE_OK;
+ goto fail;
+ }
+
+ result = Curl_rand_hex(data, randsuffix, sizeof(randsuffix));
+ if(result)
+ goto fail;
+
+ tempstore = aprintf("%s.%s.tmp", filename, randsuffix);
+ if(!tempstore) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+
+ result = CURLE_WRITE_ERROR;
+ fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600);
+ if(fd == -1)
+ goto fail;
+
+#ifdef HAVE_FCHMOD
+ {
+ struct_stat nsb;
+ if((fstat(fd, &nsb) != -1) &&
+ (nsb.st_uid == sb.st_uid) && (nsb.st_gid == sb.st_gid)) {
+ /* if the user and group are the same, clone the original mode */
+ if(fchmod(fd, sb.st_mode) == -1)
+ goto fail;
+ }
+ }
+#endif
+
+ *fh = fdopen(fd, FOPEN_WRITETEXT);
+ if(!*fh)
+ goto fail;
+
+ *tempname = tempstore;
+ return CURLE_OK;
+
+fail:
+ if(fd != -1) {
+ close(fd);
+ unlink(tempstore);
+ }
+
+ free(tempstore);
+
+ *tempname = NULL;
+ return result;
+}
+
+#endif /* ! disabled */
diff --git a/libs/libcurl/src/fopen.h b/libs/libcurl/src/fopen.h index 289e55f2af..461a719b5e 100644 --- a/libs/libcurl/src/fopen.h +++ b/libs/libcurl/src/fopen.h @@ -1,30 +1,30 @@ -#ifndef HEADER_CURL_FOPEN_H -#define HEADER_CURL_FOPEN_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 Curl_fopen(struct Curl_easy *data, const char *filename, - FILE **fh, char **tempname); - -#endif +#ifndef HEADER_CURL_FOPEN_H
+#define HEADER_CURL_FOPEN_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 Curl_fopen(struct Curl_easy *data, const char *filename,
+ FILE **fh, char **tempname);
+
+#endif
diff --git a/libs/libcurl/src/formdata.c b/libs/libcurl/src/formdata.c index 46542b4329..3bf414bcb7 100644 --- a/libs/libcurl/src/formdata.c +++ b/libs/libcurl/src/formdata.c @@ -1,949 +1,947 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "formdata.h" -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_MIME) - -#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME) -#include <libgen.h> -#endif - -#include "urldata.h" /* for struct Curl_easy */ -#include "mime.h" -#include "vtls/vtls.h" -#include "strcase.h" -#include "sendf.h" -#include "strdup.h" -#include "rand.h" -#include "warnless.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - - -#define HTTPPOST_PTRNAME CURL_HTTPPOST_PTRNAME -#define HTTPPOST_FILENAME CURL_HTTPPOST_FILENAME -#define HTTPPOST_PTRCONTENTS CURL_HTTPPOST_PTRCONTENTS -#define HTTPPOST_READFILE CURL_HTTPPOST_READFILE -#define HTTPPOST_PTRBUFFER CURL_HTTPPOST_PTRBUFFER -#define HTTPPOST_CALLBACK CURL_HTTPPOST_CALLBACK -#define HTTPPOST_BUFFER CURL_HTTPPOST_BUFFER - -/*************************************************************************** - * - * AddHttpPost() - * - * Adds a HttpPost structure to the list, if parent_post is given becomes - * a subpost of parent_post instead of a direct list element. - * - * Returns newly allocated HttpPost on success and NULL if malloc failed. - * - ***************************************************************************/ -static struct curl_httppost * -AddHttpPost(char *name, size_t namelength, - char *value, curl_off_t contentslength, - char *buffer, size_t bufferlength, - char *contenttype, - long flags, - struct curl_slist *contentHeader, - char *showfilename, char *userp, - struct curl_httppost *parent_post, - struct curl_httppost **httppost, - struct curl_httppost **last_post) -{ - struct curl_httppost *post; - if(!namelength && name) - namelength = strlen(name); - if((bufferlength > LONG_MAX) || (namelength > LONG_MAX)) - /* avoid overflow in typecasts below */ - return NULL; - post = calloc(1, sizeof(struct curl_httppost)); - if(post) { - post->name = name; - post->namelength = (long)namelength; - post->contents = value; - post->contentlen = contentslength; - post->buffer = buffer; - post->bufferlength = (long)bufferlength; - post->contenttype = contenttype; - post->contentheader = contentHeader; - post->showfilename = showfilename; - post->userp = userp; - post->flags = flags | CURL_HTTPPOST_LARGE; - } - else - return NULL; - - if(parent_post) { - /* now, point our 'more' to the original 'more' */ - post->more = parent_post->more; - - /* then move the original 'more' to point to ourselves */ - parent_post->more = post; - } - else { - /* make the previous point to this */ - if(*last_post) - (*last_post)->next = post; - else - (*httppost) = post; - - (*last_post) = post; - } - return post; -} - -/*************************************************************************** - * - * AddFormInfo() - * - * Adds a FormInfo structure to the list presented by parent_form_info. - * - * Returns newly allocated FormInfo on success and NULL if malloc failed/ - * parent_form_info is NULL. - * - ***************************************************************************/ -static struct FormInfo *AddFormInfo(char *value, - char *contenttype, - struct FormInfo *parent_form_info) -{ - struct FormInfo *form_info; - form_info = calloc(1, sizeof(struct FormInfo)); - if(form_info) { - if(value) - form_info->value = value; - if(contenttype) - form_info->contenttype = contenttype; - form_info->flags = HTTPPOST_FILENAME; - } - else - return NULL; - - if(parent_form_info) { - /* now, point our 'more' to the original 'more' */ - form_info->more = parent_form_info->more; - - /* then move the original 'more' to point to ourselves */ - parent_form_info->more = form_info; - } - - return form_info; -} - -/*************************************************************************** - * - * FormAdd() - * - * Stores a formpost parameter and builds the appropriate linked list. - * - * Has two principal functionalities: using files and byte arrays as - * post parts. Byte arrays are either copied or just the pointer is stored - * (as the user requests) while for files only the filename and not the - * content is stored. - * - * While you may have only one byte array for each name, multiple filenames - * are allowed (and because of this feature CURLFORM_END is needed after - * using CURLFORM_FILE). - * - * Examples: - * - * Simple name/value pair with copied contents: - * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name", - * CURLFORM_COPYCONTENTS, "value", CURLFORM_END); - * - * name/value pair where only the content pointer is remembered: - * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name", - * CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10, CURLFORM_END); - * (if CURLFORM_CONTENTSLENGTH is missing strlen () is used) - * - * storing a filename (CONTENTTYPE is optional!): - * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name", - * CURLFORM_FILE, "filename1", CURLFORM_CONTENTTYPE, "plain/text", - * CURLFORM_END); - * - * storing multiple filenames: - * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name", - * CURLFORM_FILE, "filename1", CURLFORM_FILE, "filename2", CURLFORM_END); - * - * Returns: - * CURL_FORMADD_OK on success - * CURL_FORMADD_MEMORY if the FormInfo allocation fails - * CURL_FORMADD_OPTION_TWICE if one option is given twice for one Form - * CURL_FORMADD_NULL if a null pointer was given for a char - * CURL_FORMADD_MEMORY if the allocation of a FormInfo struct failed - * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used - * CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or error) - * CURL_FORMADD_MEMORY if a HttpPost struct cannot be allocated - * CURL_FORMADD_MEMORY if some allocation for string copying failed. - * CURL_FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array - * - ***************************************************************************/ - -static -CURLFORMcode FormAdd(struct curl_httppost **httppost, - struct curl_httppost **last_post, - va_list params) -{ - struct FormInfo *first_form, *current_form, *form = NULL; - CURLFORMcode return_value = CURL_FORMADD_OK; - const char *prevtype = NULL; - struct curl_httppost *post = NULL; - CURLformoption option; - struct curl_forms *forms = NULL; - char *array_value = NULL; /* value read from an array */ - - /* This is a state variable, that if TRUE means that we're parsing an - array that we got passed to us. If FALSE we're parsing the input - va_list arguments. */ - bool array_state = FALSE; - - /* - * We need to allocate the first struct to fill in. - */ - first_form = calloc(1, sizeof(struct FormInfo)); - if(!first_form) - return CURL_FORMADD_MEMORY; - - current_form = first_form; - - /* - * Loop through all the options set. Break if we have an error to report. - */ - while(return_value == CURL_FORMADD_OK) { - - /* first see if we have more parts of the array param */ - if(array_state && forms) { - /* get the upcoming option from the given array */ - option = forms->option; - array_value = (char *)forms->value; - - forms++; /* advance this to next entry */ - if(CURLFORM_END == option) { - /* end of array state */ - array_state = FALSE; - continue; - } - } - else { - /* This is not array-state, get next option. This gets an 'int' with - va_arg() because CURLformoption might be a smaller type than int and - might cause compiler warnings and wrong behavior. */ - option = (CURLformoption)va_arg(params, int); - if(CURLFORM_END == option) - break; - } - - switch(option) { - case CURLFORM_ARRAY: - if(array_state) - /* we don't support an array from within an array */ - return_value = CURL_FORMADD_ILLEGAL_ARRAY; - else { - forms = va_arg(params, struct curl_forms *); - if(forms) - array_state = TRUE; - else - return_value = CURL_FORMADD_NULL; - } - break; - - /* - * Set the Name property. - */ - case CURLFORM_PTRNAME: - current_form->flags |= HTTPPOST_PTRNAME; /* fall through */ - - /* FALLTHROUGH */ - case CURLFORM_COPYNAME: - if(current_form->name) - return_value = CURL_FORMADD_OPTION_TWICE; - else { - char *name = array_state? - array_value:va_arg(params, char *); - if(name) - current_form->name = name; /* store for the moment */ - else - return_value = CURL_FORMADD_NULL; - } - break; - case CURLFORM_NAMELENGTH: - if(current_form->namelength) - return_value = CURL_FORMADD_OPTION_TWICE; - else - current_form->namelength = - array_state?(size_t)array_value:(size_t)va_arg(params, long); - break; - - /* - * Set the contents property. - */ - case CURLFORM_PTRCONTENTS: - current_form->flags |= HTTPPOST_PTRCONTENTS; - /* FALLTHROUGH */ - case CURLFORM_COPYCONTENTS: - if(current_form->value) - return_value = CURL_FORMADD_OPTION_TWICE; - else { - char *value = - array_state?array_value:va_arg(params, char *); - if(value) - current_form->value = value; /* store for the moment */ - else - return_value = CURL_FORMADD_NULL; - } - break; - case CURLFORM_CONTENTSLENGTH: - current_form->contentslength = - array_state?(size_t)array_value:(size_t)va_arg(params, long); - break; - - case CURLFORM_CONTENTLEN: - current_form->flags |= CURL_HTTPPOST_LARGE; - current_form->contentslength = - array_state?(curl_off_t)(size_t)array_value:va_arg(params, curl_off_t); - break; - - /* Get contents from a given file name */ - case CURLFORM_FILECONTENT: - if(current_form->flags & (HTTPPOST_PTRCONTENTS|HTTPPOST_READFILE)) - return_value = CURL_FORMADD_OPTION_TWICE; - else { - const char *filename = array_state? - array_value:va_arg(params, char *); - if(filename) { - current_form->value = strdup(filename); - if(!current_form->value) - return_value = CURL_FORMADD_MEMORY; - else { - current_form->flags |= HTTPPOST_READFILE; - current_form->value_alloc = TRUE; - } - } - else - return_value = CURL_FORMADD_NULL; - } - break; - - /* We upload a file */ - case CURLFORM_FILE: - { - const char *filename = array_state?array_value: - va_arg(params, char *); - - if(current_form->value) { - if(current_form->flags & HTTPPOST_FILENAME) { - if(filename) { - char *fname = strdup(filename); - if(!fname) - return_value = CURL_FORMADD_MEMORY; - else { - form = AddFormInfo(fname, NULL, current_form); - if(!form) { - free(fname); - return_value = CURL_FORMADD_MEMORY; - } - else { - form->value_alloc = TRUE; - current_form = form; - form = NULL; - } - } - } - else - return_value = CURL_FORMADD_NULL; - } - else - return_value = CURL_FORMADD_OPTION_TWICE; - } - else { - if(filename) { - current_form->value = strdup(filename); - if(!current_form->value) - return_value = CURL_FORMADD_MEMORY; - else { - current_form->flags |= HTTPPOST_FILENAME; - current_form->value_alloc = TRUE; - } - } - else - return_value = CURL_FORMADD_NULL; - } - break; - } - - case CURLFORM_BUFFERPTR: - current_form->flags |= HTTPPOST_PTRBUFFER|HTTPPOST_BUFFER; - if(current_form->buffer) - return_value = CURL_FORMADD_OPTION_TWICE; - else { - char *buffer = - array_state?array_value:va_arg(params, char *); - if(buffer) { - current_form->buffer = buffer; /* store for the moment */ - current_form->value = buffer; /* make it non-NULL to be accepted - as fine */ - } - else - return_value = CURL_FORMADD_NULL; - } - break; - - case CURLFORM_BUFFERLENGTH: - if(current_form->bufferlength) - return_value = CURL_FORMADD_OPTION_TWICE; - else - current_form->bufferlength = - array_state?(size_t)array_value:(size_t)va_arg(params, long); - break; - - case CURLFORM_STREAM: - current_form->flags |= HTTPPOST_CALLBACK; - if(current_form->userp) - return_value = CURL_FORMADD_OPTION_TWICE; - else { - char *userp = - array_state?array_value:va_arg(params, char *); - if(userp) { - current_form->userp = userp; - current_form->value = userp; /* this isn't strictly true but we - derive a value from this later on - and we need this non-NULL to be - accepted as a fine form part */ - } - else - return_value = CURL_FORMADD_NULL; - } - break; - - case CURLFORM_CONTENTTYPE: - { - const char *contenttype = - array_state?array_value:va_arg(params, char *); - if(current_form->contenttype) { - if(current_form->flags & HTTPPOST_FILENAME) { - if(contenttype) { - char *type = strdup(contenttype); - if(!type) - return_value = CURL_FORMADD_MEMORY; - else { - form = AddFormInfo(NULL, type, current_form); - if(!form) { - free(type); - return_value = CURL_FORMADD_MEMORY; - } - else { - form->contenttype_alloc = TRUE; - current_form = form; - form = NULL; - } - } - } - else - return_value = CURL_FORMADD_NULL; - } - else - return_value = CURL_FORMADD_OPTION_TWICE; - } - else { - if(contenttype) { - current_form->contenttype = strdup(contenttype); - if(!current_form->contenttype) - return_value = CURL_FORMADD_MEMORY; - else - current_form->contenttype_alloc = TRUE; - } - else - return_value = CURL_FORMADD_NULL; - } - break; - } - case CURLFORM_CONTENTHEADER: - { - /* this "cast increases required alignment of target type" but - we consider it OK anyway */ - struct curl_slist *list = array_state? - (struct curl_slist *)(void *)array_value: - va_arg(params, struct curl_slist *); - - if(current_form->contentheader) - return_value = CURL_FORMADD_OPTION_TWICE; - else - current_form->contentheader = list; - - break; - } - case CURLFORM_FILENAME: - case CURLFORM_BUFFER: - { - const char *filename = array_state?array_value: - va_arg(params, char *); - if(current_form->showfilename) - return_value = CURL_FORMADD_OPTION_TWICE; - else { - current_form->showfilename = strdup(filename); - if(!current_form->showfilename) - return_value = CURL_FORMADD_MEMORY; - else - current_form->showfilename_alloc = TRUE; - } - break; - } - default: - return_value = CURL_FORMADD_UNKNOWN_OPTION; - break; - } - } - - if(CURL_FORMADD_OK != return_value) { - /* On error, free allocated fields for all nodes of the FormInfo linked - list without deallocating nodes. List nodes are deallocated later on */ - struct FormInfo *ptr; - for(ptr = first_form; ptr != NULL; ptr = ptr->more) { - if(ptr->name_alloc) { - Curl_safefree(ptr->name); - ptr->name_alloc = FALSE; - } - if(ptr->value_alloc) { - Curl_safefree(ptr->value); - ptr->value_alloc = FALSE; - } - if(ptr->contenttype_alloc) { - Curl_safefree(ptr->contenttype); - ptr->contenttype_alloc = FALSE; - } - if(ptr->showfilename_alloc) { - Curl_safefree(ptr->showfilename); - ptr->showfilename_alloc = FALSE; - } - } - } - - if(CURL_FORMADD_OK == return_value) { - /* go through the list, check for completeness and if everything is - * alright add the HttpPost item otherwise set return_value accordingly */ - - post = NULL; - for(form = first_form; - form != NULL; - form = form->more) { - if(((!form->name || !form->value) && !post) || - ( (form->contentslength) && - (form->flags & HTTPPOST_FILENAME) ) || - ( (form->flags & HTTPPOST_FILENAME) && - (form->flags & HTTPPOST_PTRCONTENTS) ) || - - ( (!form->buffer) && - (form->flags & HTTPPOST_BUFFER) && - (form->flags & HTTPPOST_PTRBUFFER) ) || - - ( (form->flags & HTTPPOST_READFILE) && - (form->flags & HTTPPOST_PTRCONTENTS) ) - ) { - return_value = CURL_FORMADD_INCOMPLETE; - break; - } - if(((form->flags & HTTPPOST_FILENAME) || - (form->flags & HTTPPOST_BUFFER)) && - !form->contenttype) { - char *f = (form->flags & HTTPPOST_BUFFER)? - form->showfilename : form->value; - char const *type; - type = Curl_mime_contenttype(f); - if(!type) - type = prevtype; - if(!type) - type = FILE_CONTENTTYPE_DEFAULT; - - /* our contenttype is missing */ - form->contenttype = strdup(type); - if(!form->contenttype) { - return_value = CURL_FORMADD_MEMORY; - break; - } - form->contenttype_alloc = TRUE; - } - if(form->name && form->namelength) { - /* Name should not contain nul bytes. */ - size_t i; - for(i = 0; i < form->namelength; i++) - if(!form->name[i]) { - return_value = CURL_FORMADD_NULL; - break; - } - if(return_value != CURL_FORMADD_OK) - break; - } - if(!(form->flags & HTTPPOST_PTRNAME) && - (form == first_form) ) { - /* Note that there's small risk that form->name is NULL here if the - app passed in a bad combo, so we better check for that first. */ - if(form->name) { - /* copy name (without strdup; possibly not null-terminated) */ - form->name = Curl_memdup(form->name, form->namelength? - form->namelength: - strlen(form->name) + 1); - } - if(!form->name) { - return_value = CURL_FORMADD_MEMORY; - break; - } - form->name_alloc = TRUE; - } - if(!(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE | - HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER | - HTTPPOST_CALLBACK)) && form->value) { - /* copy value (without strdup; possibly contains null characters) */ - size_t clen = (size_t) form->contentslength; - if(!clen) - clen = strlen(form->value) + 1; - - form->value = Curl_memdup(form->value, clen); - - if(!form->value) { - return_value = CURL_FORMADD_MEMORY; - break; - } - form->value_alloc = TRUE; - } - post = AddHttpPost(form->name, form->namelength, - form->value, form->contentslength, - form->buffer, form->bufferlength, - form->contenttype, form->flags, - form->contentheader, form->showfilename, - form->userp, - post, httppost, - last_post); - - if(!post) { - return_value = CURL_FORMADD_MEMORY; - break; - } - - if(form->contenttype) - prevtype = form->contenttype; - } - if(CURL_FORMADD_OK != return_value) { - /* On error, free allocated fields for nodes of the FormInfo linked - list which are not already owned by the httppost linked list - without deallocating nodes. List nodes are deallocated later on */ - struct FormInfo *ptr; - for(ptr = form; ptr != NULL; ptr = ptr->more) { - if(ptr->name_alloc) { - Curl_safefree(ptr->name); - ptr->name_alloc = FALSE; - } - if(ptr->value_alloc) { - Curl_safefree(ptr->value); - ptr->value_alloc = FALSE; - } - if(ptr->contenttype_alloc) { - Curl_safefree(ptr->contenttype); - ptr->contenttype_alloc = FALSE; - } - if(ptr->showfilename_alloc) { - Curl_safefree(ptr->showfilename); - ptr->showfilename_alloc = FALSE; - } - } - } - } - - /* Always deallocate FormInfo linked list nodes without touching node - fields given that these have either been deallocated or are owned - now by the httppost linked list */ - while(first_form) { - struct FormInfo *ptr = first_form->more; - free(first_form); - first_form = ptr; - } - - return return_value; -} - -/* - * curl_formadd() is a public API to add a section to the multipart formpost. - * - * @unittest: 1308 - */ - -CURLFORMcode curl_formadd(struct curl_httppost **httppost, - struct curl_httppost **last_post, - ...) -{ - va_list arg; - CURLFORMcode result; - va_start(arg, last_post); - result = FormAdd(httppost, last_post, arg); - va_end(arg); - return result; -} - -/* - * curl_formget() - * Serialize a curl_httppost struct. - * Returns 0 on success. - * - * @unittest: 1308 - */ -int curl_formget(struct curl_httppost *form, void *arg, - curl_formget_callback append) -{ - CURLcode result; - curl_mimepart toppart; - - Curl_mime_initpart(&toppart, NULL); /* default form is empty */ - result = Curl_getformdata(NULL, &toppart, form, NULL); - if(!result) - result = Curl_mime_prepare_headers(&toppart, "multipart/form-data", - NULL, MIMESTRATEGY_FORM); - - while(!result) { - char buffer[8192]; - size_t nread = Curl_mime_read(buffer, 1, sizeof(buffer), &toppart); - - if(!nread) - break; - - if(nread > sizeof(buffer) || append(arg, buffer, nread) != nread) { - result = CURLE_READ_ERROR; - if(nread == CURL_READFUNC_ABORT) - result = CURLE_ABORTED_BY_CALLBACK; - } - } - - Curl_mime_cleanpart(&toppart); - return (int) result; -} - -/* - * curl_formfree() is an external function to free up a whole form post - * chain - */ -void curl_formfree(struct curl_httppost *form) -{ - struct curl_httppost *next; - - if(!form) - /* no form to free, just get out of this */ - return; - - do { - next = form->next; /* the following form line */ - - /* recurse to sub-contents */ - curl_formfree(form->more); - - if(!(form->flags & HTTPPOST_PTRNAME)) - free(form->name); /* free the name */ - if(!(form->flags & - (HTTPPOST_PTRCONTENTS|HTTPPOST_BUFFER|HTTPPOST_CALLBACK)) - ) - free(form->contents); /* free the contents */ - free(form->contenttype); /* free the content type */ - free(form->showfilename); /* free the faked file name */ - free(form); /* free the struct */ - form = next; - } while(form); /* continue */ -} - - -/* Set mime part name, taking care of non null-terminated name string. */ -static CURLcode setname(curl_mimepart *part, const char *name, size_t len) -{ - char *zname; - CURLcode res; - - if(!name || !len) - return curl_mime_name(part, name); - zname = malloc(len + 1); - if(!zname) - return CURLE_OUT_OF_MEMORY; - memcpy(zname, name, len); - zname[len] = '\0'; - res = curl_mime_name(part, zname); - free(zname); - return res; -} - -/* - * Curl_getformdata() converts a linked list of "meta data" into a mime - * structure. The input list is in 'post', while the output is stored in - * mime part at '*finalform'. - * - * This function will not do a failf() for the potential memory failures but - * should for all other errors it spots. Just note that this function MAY get - * a NULL pointer in the 'data' argument. - */ - -CURLcode Curl_getformdata(struct Curl_easy *data, - curl_mimepart *finalform, - struct curl_httppost *post, - curl_read_callback fread_func) -{ - CURLcode result = CURLE_OK; - curl_mime *form = NULL; - curl_mimepart *part; - struct curl_httppost *file; - - Curl_mime_cleanpart(finalform); /* default form is empty */ - - if(!post) - return result; /* no input => no output! */ - - form = curl_mime_init(data); - if(!form) - result = CURLE_OUT_OF_MEMORY; - - if(!result) - result = curl_mime_subparts(finalform, form); - - /* Process each top part. */ - for(; !result && post; post = post->next) { - /* If we have more than a file here, create a mime subpart and fill it. */ - curl_mime *multipart = form; - if(post->more) { - part = curl_mime_addpart(form); - if(!part) - result = CURLE_OUT_OF_MEMORY; - if(!result) - result = setname(part, post->name, post->namelength); - if(!result) { - multipart = curl_mime_init(data); - if(!multipart) - result = CURLE_OUT_OF_MEMORY; - } - if(!result) - result = curl_mime_subparts(part, multipart); - } - - /* Generate all the part contents. */ - for(file = post; !result && file; file = file->more) { - /* Create the part. */ - part = curl_mime_addpart(multipart); - if(!part) - result = CURLE_OUT_OF_MEMORY; - - /* Set the headers. */ - if(!result) - result = curl_mime_headers(part, file->contentheader, 0); - - /* Set the content type. */ - if(!result && file->contenttype) - result = curl_mime_type(part, file->contenttype); - - /* Set field name. */ - if(!result && !post->more) - result = setname(part, post->name, post->namelength); - - /* Process contents. */ - if(!result) { - curl_off_t clen = post->contentslength; - - if(post->flags & CURL_HTTPPOST_LARGE) - clen = post->contentlen; - - if(post->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE)) { - if(!strcmp(file->contents, "-")) { - /* There are a few cases where the code below won't work; in - particular, freopen(stdin) by the caller is not guaranteed - to result as expected. This feature has been kept for backward - compatibility: use of "-" pseudo file name should be avoided. */ - result = curl_mime_data_cb(part, (curl_off_t) -1, - (curl_read_callback) fread, - CURLX_FUNCTION_CAST(curl_seek_callback, - fseek), - NULL, (void *) stdin); - } - else - result = curl_mime_filedata(part, file->contents); - if(!result && (post->flags & HTTPPOST_READFILE)) - result = curl_mime_filename(part, NULL); - } - else if(post->flags & HTTPPOST_BUFFER) - result = curl_mime_data(part, post->buffer, - post->bufferlength? post->bufferlength: -1); - else if(post->flags & HTTPPOST_CALLBACK) { - /* the contents should be read with the callback and the size is set - with the contentslength */ - if(!clen) - clen = -1; - result = curl_mime_data_cb(part, clen, - fread_func, NULL, NULL, post->userp); - } - else { - size_t uclen; - if(!clen) - uclen = CURL_ZERO_TERMINATED; - else - uclen = (size_t)clen; - result = curl_mime_data(part, post->contents, uclen); - } - } - - /* Set fake file name. */ - if(!result && post->showfilename) - if(post->more || (post->flags & (HTTPPOST_FILENAME | HTTPPOST_BUFFER | - HTTPPOST_CALLBACK))) - result = curl_mime_filename(part, post->showfilename); - } - } - - if(result) - Curl_mime_cleanpart(finalform); - - return result; -} - -#else -/* if disabled */ -CURLFORMcode curl_formadd(struct curl_httppost **httppost, - struct curl_httppost **last_post, - ...) -{ - (void)httppost; - (void)last_post; - return CURL_FORMADD_DISABLED; -} - -int curl_formget(struct curl_httppost *form, void *arg, - curl_formget_callback append) -{ - (void) form; - (void) arg; - (void) append; - return CURL_FORMADD_DISABLED; -} - -void curl_formfree(struct curl_httppost *form) -{ - (void)form; - /* does nothing HTTP is disabled */ -} - -#endif /* if disabled */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "formdata.h"
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_MIME)
+
+#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
+#include <libgen.h>
+#endif
+
+#include "urldata.h" /* for struct Curl_easy */
+#include "mime.h"
+#include "vtls/vtls.h"
+#include "strcase.h"
+#include "sendf.h"
+#include "strdup.h"
+#include "rand.h"
+#include "warnless.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+#define HTTPPOST_PTRNAME CURL_HTTPPOST_PTRNAME
+#define HTTPPOST_FILENAME CURL_HTTPPOST_FILENAME
+#define HTTPPOST_PTRCONTENTS CURL_HTTPPOST_PTRCONTENTS
+#define HTTPPOST_READFILE CURL_HTTPPOST_READFILE
+#define HTTPPOST_PTRBUFFER CURL_HTTPPOST_PTRBUFFER
+#define HTTPPOST_CALLBACK CURL_HTTPPOST_CALLBACK
+#define HTTPPOST_BUFFER CURL_HTTPPOST_BUFFER
+
+/***************************************************************************
+ *
+ * AddHttpPost()
+ *
+ * Adds an HttpPost structure to the list, if parent_post is given becomes
+ * a subpost of parent_post instead of a direct list element.
+ *
+ * Returns newly allocated HttpPost on success and NULL if malloc failed.
+ *
+ ***************************************************************************/
+static struct curl_httppost *
+AddHttpPost(char *name, size_t namelength,
+ char *value, curl_off_t contentslength,
+ char *buffer, size_t bufferlength,
+ char *contenttype,
+ long flags,
+ struct curl_slist *contentHeader,
+ char *showfilename, char *userp,
+ struct curl_httppost *parent_post,
+ struct curl_httppost **httppost,
+ struct curl_httppost **last_post)
+{
+ struct curl_httppost *post;
+ if(!namelength && name)
+ namelength = strlen(name);
+ if((bufferlength > LONG_MAX) || (namelength > LONG_MAX))
+ /* avoid overflow in typecasts below */
+ return NULL;
+ post = calloc(1, sizeof(struct curl_httppost));
+ if(post) {
+ post->name = name;
+ post->namelength = (long)namelength;
+ post->contents = value;
+ post->contentlen = contentslength;
+ post->buffer = buffer;
+ post->bufferlength = (long)bufferlength;
+ post->contenttype = contenttype;
+ post->contentheader = contentHeader;
+ post->showfilename = showfilename;
+ post->userp = userp;
+ post->flags = flags | CURL_HTTPPOST_LARGE;
+ }
+ else
+ return NULL;
+
+ if(parent_post) {
+ /* now, point our 'more' to the original 'more' */
+ post->more = parent_post->more;
+
+ /* then move the original 'more' to point to ourselves */
+ parent_post->more = post;
+ }
+ else {
+ /* make the previous point to this */
+ if(*last_post)
+ (*last_post)->next = post;
+ else
+ (*httppost) = post;
+
+ (*last_post) = post;
+ }
+ return post;
+}
+
+/***************************************************************************
+ *
+ * AddFormInfo()
+ *
+ * Adds a FormInfo structure to the list presented by parent_form_info.
+ *
+ * Returns newly allocated FormInfo on success and NULL if malloc failed/
+ * parent_form_info is NULL.
+ *
+ ***************************************************************************/
+static struct FormInfo *AddFormInfo(char *value,
+ char *contenttype,
+ struct FormInfo *parent_form_info)
+{
+ struct FormInfo *form_info;
+ form_info = calloc(1, sizeof(struct FormInfo));
+ if(!form_info)
+ return NULL;
+ if(value)
+ form_info->value = value;
+ if(contenttype)
+ form_info->contenttype = contenttype;
+ form_info->flags = HTTPPOST_FILENAME;
+
+ if(parent_form_info) {
+ /* now, point our 'more' to the original 'more' */
+ form_info->more = parent_form_info->more;
+
+ /* then move the original 'more' to point to ourselves */
+ parent_form_info->more = form_info;
+ }
+
+ return form_info;
+}
+
+/***************************************************************************
+ *
+ * FormAdd()
+ *
+ * Stores a formpost parameter and builds the appropriate linked list.
+ *
+ * Has two principal functionalities: using files and byte arrays as
+ * post parts. Byte arrays are either copied or just the pointer is stored
+ * (as the user requests) while for files only the filename and not the
+ * content is stored.
+ *
+ * While you may have only one byte array for each name, multiple filenames
+ * are allowed (and because of this feature CURLFORM_END is needed after
+ * using CURLFORM_FILE).
+ *
+ * Examples:
+ *
+ * Simple name/value pair with copied contents:
+ * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
+ * CURLFORM_COPYCONTENTS, "value", CURLFORM_END);
+ *
+ * name/value pair where only the content pointer is remembered:
+ * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
+ * CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10, CURLFORM_END);
+ * (if CURLFORM_CONTENTSLENGTH is missing strlen () is used)
+ *
+ * storing a filename (CONTENTTYPE is optional!):
+ * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
+ * CURLFORM_FILE, "filename1", CURLFORM_CONTENTTYPE, "plain/text",
+ * CURLFORM_END);
+ *
+ * storing multiple filenames:
+ * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
+ * CURLFORM_FILE, "filename1", CURLFORM_FILE, "filename2", CURLFORM_END);
+ *
+ * Returns:
+ * CURL_FORMADD_OK on success
+ * CURL_FORMADD_MEMORY if the FormInfo allocation fails
+ * CURL_FORMADD_OPTION_TWICE if one option is given twice for one Form
+ * CURL_FORMADD_NULL if a null pointer was given for a char
+ * CURL_FORMADD_MEMORY if the allocation of a FormInfo struct failed
+ * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used
+ * CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or error)
+ * CURL_FORMADD_MEMORY if an HttpPost struct cannot be allocated
+ * CURL_FORMADD_MEMORY if some allocation for string copying failed.
+ * CURL_FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array
+ *
+ ***************************************************************************/
+
+static
+CURLFORMcode FormAdd(struct curl_httppost **httppost,
+ struct curl_httppost **last_post,
+ va_list params)
+{
+ struct FormInfo *first_form, *current_form, *form = NULL;
+ CURLFORMcode return_value = CURL_FORMADD_OK;
+ const char *prevtype = NULL;
+ struct curl_httppost *post = NULL;
+ CURLformoption option;
+ struct curl_forms *forms = NULL;
+ char *array_value = NULL; /* value read from an array */
+
+ /* This is a state variable, that if TRUE means that we're parsing an
+ array that we got passed to us. If FALSE we're parsing the input
+ va_list arguments. */
+ bool array_state = FALSE;
+
+ /*
+ * We need to allocate the first struct to fill in.
+ */
+ first_form = calloc(1, sizeof(struct FormInfo));
+ if(!first_form)
+ return CURL_FORMADD_MEMORY;
+
+ current_form = first_form;
+
+ /*
+ * Loop through all the options set. Break if we have an error to report.
+ */
+ while(return_value == CURL_FORMADD_OK) {
+
+ /* first see if we have more parts of the array param */
+ if(array_state && forms) {
+ /* get the upcoming option from the given array */
+ option = forms->option;
+ array_value = (char *)forms->value;
+
+ forms++; /* advance this to next entry */
+ if(CURLFORM_END == option) {
+ /* end of array state */
+ array_state = FALSE;
+ continue;
+ }
+ }
+ else {
+ /* This is not array-state, get next option. This gets an 'int' with
+ va_arg() because CURLformoption might be a smaller type than int and
+ might cause compiler warnings and wrong behavior. */
+ option = (CURLformoption)va_arg(params, int);
+ if(CURLFORM_END == option)
+ break;
+ }
+
+ switch(option) {
+ case CURLFORM_ARRAY:
+ if(array_state)
+ /* we don't support an array from within an array */
+ return_value = CURL_FORMADD_ILLEGAL_ARRAY;
+ else {
+ forms = va_arg(params, struct curl_forms *);
+ if(forms)
+ array_state = TRUE;
+ else
+ return_value = CURL_FORMADD_NULL;
+ }
+ break;
+
+ /*
+ * Set the Name property.
+ */
+ case CURLFORM_PTRNAME:
+ current_form->flags |= HTTPPOST_PTRNAME; /* fall through */
+
+ /* FALLTHROUGH */
+ case CURLFORM_COPYNAME:
+ if(current_form->name)
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ else {
+ char *name = array_state?
+ array_value:va_arg(params, char *);
+ if(name)
+ current_form->name = name; /* store for the moment */
+ else
+ return_value = CURL_FORMADD_NULL;
+ }
+ break;
+ case CURLFORM_NAMELENGTH:
+ if(current_form->namelength)
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ else
+ current_form->namelength =
+ array_state?(size_t)array_value:(size_t)va_arg(params, long);
+ break;
+
+ /*
+ * Set the contents property.
+ */
+ case CURLFORM_PTRCONTENTS:
+ current_form->flags |= HTTPPOST_PTRCONTENTS;
+ /* FALLTHROUGH */
+ case CURLFORM_COPYCONTENTS:
+ if(current_form->value)
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ else {
+ char *value =
+ array_state?array_value:va_arg(params, char *);
+ if(value)
+ current_form->value = value; /* store for the moment */
+ else
+ return_value = CURL_FORMADD_NULL;
+ }
+ break;
+ case CURLFORM_CONTENTSLENGTH:
+ current_form->contentslength =
+ array_state?(size_t)array_value:(size_t)va_arg(params, long);
+ break;
+
+ case CURLFORM_CONTENTLEN:
+ current_form->flags |= CURL_HTTPPOST_LARGE;
+ current_form->contentslength =
+ array_state?(curl_off_t)(size_t)array_value:va_arg(params, curl_off_t);
+ break;
+
+ /* Get contents from a given file name */
+ case CURLFORM_FILECONTENT:
+ if(current_form->flags & (HTTPPOST_PTRCONTENTS|HTTPPOST_READFILE))
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ else {
+ const char *filename = array_state?
+ array_value:va_arg(params, char *);
+ if(filename) {
+ current_form->value = strdup(filename);
+ if(!current_form->value)
+ return_value = CURL_FORMADD_MEMORY;
+ else {
+ current_form->flags |= HTTPPOST_READFILE;
+ current_form->value_alloc = TRUE;
+ }
+ }
+ else
+ return_value = CURL_FORMADD_NULL;
+ }
+ break;
+
+ /* We upload a file */
+ case CURLFORM_FILE:
+ {
+ const char *filename = array_state?array_value:
+ va_arg(params, char *);
+
+ if(current_form->value) {
+ if(current_form->flags & HTTPPOST_FILENAME) {
+ if(filename) {
+ char *fname = strdup(filename);
+ if(!fname)
+ return_value = CURL_FORMADD_MEMORY;
+ else {
+ form = AddFormInfo(fname, NULL, current_form);
+ if(!form) {
+ free(fname);
+ return_value = CURL_FORMADD_MEMORY;
+ }
+ else {
+ form->value_alloc = TRUE;
+ current_form = form;
+ form = NULL;
+ }
+ }
+ }
+ else
+ return_value = CURL_FORMADD_NULL;
+ }
+ else
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ }
+ else {
+ if(filename) {
+ current_form->value = strdup(filename);
+ if(!current_form->value)
+ return_value = CURL_FORMADD_MEMORY;
+ else {
+ current_form->flags |= HTTPPOST_FILENAME;
+ current_form->value_alloc = TRUE;
+ }
+ }
+ else
+ return_value = CURL_FORMADD_NULL;
+ }
+ break;
+ }
+
+ case CURLFORM_BUFFERPTR:
+ current_form->flags |= HTTPPOST_PTRBUFFER|HTTPPOST_BUFFER;
+ if(current_form->buffer)
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ else {
+ char *buffer =
+ array_state?array_value:va_arg(params, char *);
+ if(buffer) {
+ current_form->buffer = buffer; /* store for the moment */
+ current_form->value = buffer; /* make it non-NULL to be accepted
+ as fine */
+ }
+ else
+ return_value = CURL_FORMADD_NULL;
+ }
+ break;
+
+ case CURLFORM_BUFFERLENGTH:
+ if(current_form->bufferlength)
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ else
+ current_form->bufferlength =
+ array_state?(size_t)array_value:(size_t)va_arg(params, long);
+ break;
+
+ case CURLFORM_STREAM:
+ current_form->flags |= HTTPPOST_CALLBACK;
+ if(current_form->userp)
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ else {
+ char *userp =
+ array_state?array_value:va_arg(params, char *);
+ if(userp) {
+ current_form->userp = userp;
+ current_form->value = userp; /* this isn't strictly true but we
+ derive a value from this later on
+ and we need this non-NULL to be
+ accepted as a fine form part */
+ }
+ else
+ return_value = CURL_FORMADD_NULL;
+ }
+ break;
+
+ case CURLFORM_CONTENTTYPE:
+ {
+ const char *contenttype =
+ array_state?array_value:va_arg(params, char *);
+ if(current_form->contenttype) {
+ if(current_form->flags & HTTPPOST_FILENAME) {
+ if(contenttype) {
+ char *type = strdup(contenttype);
+ if(!type)
+ return_value = CURL_FORMADD_MEMORY;
+ else {
+ form = AddFormInfo(NULL, type, current_form);
+ if(!form) {
+ free(type);
+ return_value = CURL_FORMADD_MEMORY;
+ }
+ else {
+ form->contenttype_alloc = TRUE;
+ current_form = form;
+ form = NULL;
+ }
+ }
+ }
+ else
+ return_value = CURL_FORMADD_NULL;
+ }
+ else
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ }
+ else {
+ if(contenttype) {
+ current_form->contenttype = strdup(contenttype);
+ if(!current_form->contenttype)
+ return_value = CURL_FORMADD_MEMORY;
+ else
+ current_form->contenttype_alloc = TRUE;
+ }
+ else
+ return_value = CURL_FORMADD_NULL;
+ }
+ break;
+ }
+ case CURLFORM_CONTENTHEADER:
+ {
+ /* this "cast increases required alignment of target type" but
+ we consider it OK anyway */
+ struct curl_slist *list = array_state?
+ (struct curl_slist *)(void *)array_value:
+ va_arg(params, struct curl_slist *);
+
+ if(current_form->contentheader)
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ else
+ current_form->contentheader = list;
+
+ break;
+ }
+ case CURLFORM_FILENAME:
+ case CURLFORM_BUFFER:
+ {
+ const char *filename = array_state?array_value:
+ va_arg(params, char *);
+ if(current_form->showfilename)
+ return_value = CURL_FORMADD_OPTION_TWICE;
+ else {
+ current_form->showfilename = strdup(filename);
+ if(!current_form->showfilename)
+ return_value = CURL_FORMADD_MEMORY;
+ else
+ current_form->showfilename_alloc = TRUE;
+ }
+ break;
+ }
+ default:
+ return_value = CURL_FORMADD_UNKNOWN_OPTION;
+ break;
+ }
+ }
+
+ if(CURL_FORMADD_OK != return_value) {
+ /* On error, free allocated fields for all nodes of the FormInfo linked
+ list without deallocating nodes. List nodes are deallocated later on */
+ struct FormInfo *ptr;
+ for(ptr = first_form; ptr != NULL; ptr = ptr->more) {
+ if(ptr->name_alloc) {
+ Curl_safefree(ptr->name);
+ ptr->name_alloc = FALSE;
+ }
+ if(ptr->value_alloc) {
+ Curl_safefree(ptr->value);
+ ptr->value_alloc = FALSE;
+ }
+ if(ptr->contenttype_alloc) {
+ Curl_safefree(ptr->contenttype);
+ ptr->contenttype_alloc = FALSE;
+ }
+ if(ptr->showfilename_alloc) {
+ Curl_safefree(ptr->showfilename);
+ ptr->showfilename_alloc = FALSE;
+ }
+ }
+ }
+
+ if(CURL_FORMADD_OK == return_value) {
+ /* go through the list, check for completeness and if everything is
+ * alright add the HttpPost item otherwise set return_value accordingly */
+
+ post = NULL;
+ for(form = first_form;
+ form != NULL;
+ form = form->more) {
+ if(((!form->name || !form->value) && !post) ||
+ ( (form->contentslength) &&
+ (form->flags & HTTPPOST_FILENAME) ) ||
+ ( (form->flags & HTTPPOST_FILENAME) &&
+ (form->flags & HTTPPOST_PTRCONTENTS) ) ||
+
+ ( (!form->buffer) &&
+ (form->flags & HTTPPOST_BUFFER) &&
+ (form->flags & HTTPPOST_PTRBUFFER) ) ||
+
+ ( (form->flags & HTTPPOST_READFILE) &&
+ (form->flags & HTTPPOST_PTRCONTENTS) )
+ ) {
+ return_value = CURL_FORMADD_INCOMPLETE;
+ break;
+ }
+ if(((form->flags & HTTPPOST_FILENAME) ||
+ (form->flags & HTTPPOST_BUFFER)) &&
+ !form->contenttype) {
+ char *f = (form->flags & HTTPPOST_BUFFER)?
+ form->showfilename : form->value;
+ char const *type;
+ type = Curl_mime_contenttype(f);
+ if(!type)
+ type = prevtype;
+ if(!type)
+ type = FILE_CONTENTTYPE_DEFAULT;
+
+ /* our contenttype is missing */
+ form->contenttype = strdup(type);
+ if(!form->contenttype) {
+ return_value = CURL_FORMADD_MEMORY;
+ break;
+ }
+ form->contenttype_alloc = TRUE;
+ }
+ if(form->name && form->namelength) {
+ /* Name should not contain nul bytes. */
+ size_t i;
+ for(i = 0; i < form->namelength; i++)
+ if(!form->name[i]) {
+ return_value = CURL_FORMADD_NULL;
+ break;
+ }
+ if(return_value != CURL_FORMADD_OK)
+ break;
+ }
+ if(!(form->flags & HTTPPOST_PTRNAME) &&
+ (form == first_form) ) {
+ /* Note that there's small risk that form->name is NULL here if the
+ app passed in a bad combo, so we better check for that first. */
+ if(form->name) {
+ /* copy name (without strdup; possibly not null-terminated) */
+ form->name = Curl_memdup(form->name, form->namelength?
+ form->namelength:
+ strlen(form->name) + 1);
+ }
+ if(!form->name) {
+ return_value = CURL_FORMADD_MEMORY;
+ break;
+ }
+ form->name_alloc = TRUE;
+ }
+ if(!(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE |
+ HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER |
+ HTTPPOST_CALLBACK)) && form->value) {
+ /* copy value (without strdup; possibly contains null characters) */
+ size_t clen = (size_t) form->contentslength;
+ if(!clen)
+ clen = strlen(form->value) + 1;
+
+ form->value = Curl_memdup(form->value, clen);
+
+ if(!form->value) {
+ return_value = CURL_FORMADD_MEMORY;
+ break;
+ }
+ form->value_alloc = TRUE;
+ }
+ post = AddHttpPost(form->name, form->namelength,
+ form->value, form->contentslength,
+ form->buffer, form->bufferlength,
+ form->contenttype, form->flags,
+ form->contentheader, form->showfilename,
+ form->userp,
+ post, httppost,
+ last_post);
+
+ if(!post) {
+ return_value = CURL_FORMADD_MEMORY;
+ break;
+ }
+
+ if(form->contenttype)
+ prevtype = form->contenttype;
+ }
+ if(CURL_FORMADD_OK != return_value) {
+ /* On error, free allocated fields for nodes of the FormInfo linked
+ list which are not already owned by the httppost linked list
+ without deallocating nodes. List nodes are deallocated later on */
+ struct FormInfo *ptr;
+ for(ptr = form; ptr != NULL; ptr = ptr->more) {
+ if(ptr->name_alloc) {
+ Curl_safefree(ptr->name);
+ ptr->name_alloc = FALSE;
+ }
+ if(ptr->value_alloc) {
+ Curl_safefree(ptr->value);
+ ptr->value_alloc = FALSE;
+ }
+ if(ptr->contenttype_alloc) {
+ Curl_safefree(ptr->contenttype);
+ ptr->contenttype_alloc = FALSE;
+ }
+ if(ptr->showfilename_alloc) {
+ Curl_safefree(ptr->showfilename);
+ ptr->showfilename_alloc = FALSE;
+ }
+ }
+ }
+ }
+
+ /* Always deallocate FormInfo linked list nodes without touching node
+ fields given that these have either been deallocated or are owned
+ now by the httppost linked list */
+ while(first_form) {
+ struct FormInfo *ptr = first_form->more;
+ free(first_form);
+ first_form = ptr;
+ }
+
+ return return_value;
+}
+
+/*
+ * curl_formadd() is a public API to add a section to the multipart formpost.
+ *
+ * @unittest: 1308
+ */
+
+CURLFORMcode curl_formadd(struct curl_httppost **httppost,
+ struct curl_httppost **last_post,
+ ...)
+{
+ va_list arg;
+ CURLFORMcode result;
+ va_start(arg, last_post);
+ result = FormAdd(httppost, last_post, arg);
+ va_end(arg);
+ return result;
+}
+
+/*
+ * curl_formget()
+ * Serialize a curl_httppost struct.
+ * Returns 0 on success.
+ *
+ * @unittest: 1308
+ */
+int curl_formget(struct curl_httppost *form, void *arg,
+ curl_formget_callback append)
+{
+ CURLcode result;
+ curl_mimepart toppart;
+
+ Curl_mime_initpart(&toppart); /* default form is empty */
+ result = Curl_getformdata(NULL, &toppart, form, NULL);
+ if(!result)
+ result = Curl_mime_prepare_headers(NULL, &toppart, "multipart/form-data",
+ NULL, MIMESTRATEGY_FORM);
+
+ while(!result) {
+ char buffer[8192];
+ size_t nread = Curl_mime_read(buffer, 1, sizeof(buffer), &toppart);
+
+ if(!nread)
+ break;
+
+ if(nread > sizeof(buffer) || append(arg, buffer, nread) != nread) {
+ result = CURLE_READ_ERROR;
+ if(nread == CURL_READFUNC_ABORT)
+ result = CURLE_ABORTED_BY_CALLBACK;
+ }
+ }
+
+ Curl_mime_cleanpart(&toppart);
+ return (int) result;
+}
+
+/*
+ * curl_formfree() is an external function to free up a whole form post
+ * chain
+ */
+void curl_formfree(struct curl_httppost *form)
+{
+ struct curl_httppost *next;
+
+ if(!form)
+ /* no form to free, just get out of this */
+ return;
+
+ do {
+ next = form->next; /* the following form line */
+
+ /* recurse to sub-contents */
+ curl_formfree(form->more);
+
+ if(!(form->flags & HTTPPOST_PTRNAME))
+ free(form->name); /* free the name */
+ if(!(form->flags &
+ (HTTPPOST_PTRCONTENTS|HTTPPOST_BUFFER|HTTPPOST_CALLBACK))
+ )
+ free(form->contents); /* free the contents */
+ free(form->contenttype); /* free the content type */
+ free(form->showfilename); /* free the faked file name */
+ free(form); /* free the struct */
+ form = next;
+ } while(form); /* continue */
+}
+
+
+/* Set mime part name, taking care of non null-terminated name string. */
+static CURLcode setname(curl_mimepart *part, const char *name, size_t len)
+{
+ char *zname;
+ CURLcode res;
+
+ if(!name || !len)
+ return curl_mime_name(part, name);
+ zname = malloc(len + 1);
+ if(!zname)
+ return CURLE_OUT_OF_MEMORY;
+ memcpy(zname, name, len);
+ zname[len] = '\0';
+ res = curl_mime_name(part, zname);
+ free(zname);
+ return res;
+}
+
+/*
+ * Curl_getformdata() converts a linked list of "meta data" into a mime
+ * structure. The input list is in 'post', while the output is stored in
+ * mime part at '*finalform'.
+ *
+ * This function will not do a failf() for the potential memory failures but
+ * should for all other errors it spots. Just note that this function MAY get
+ * a NULL pointer in the 'data' argument.
+ */
+
+CURLcode Curl_getformdata(struct Curl_easy *data,
+ curl_mimepart *finalform,
+ struct curl_httppost *post,
+ curl_read_callback fread_func)
+{
+ CURLcode result = CURLE_OK;
+ curl_mime *form = NULL;
+ curl_mimepart *part;
+ struct curl_httppost *file;
+
+ Curl_mime_cleanpart(finalform); /* default form is empty */
+
+ if(!post)
+ return result; /* no input => no output! */
+
+ form = curl_mime_init(data);
+ if(!form)
+ result = CURLE_OUT_OF_MEMORY;
+
+ if(!result)
+ result = curl_mime_subparts(finalform, form);
+
+ /* Process each top part. */
+ for(; !result && post; post = post->next) {
+ /* If we have more than a file here, create a mime subpart and fill it. */
+ curl_mime *multipart = form;
+ if(post->more) {
+ part = curl_mime_addpart(form);
+ if(!part)
+ result = CURLE_OUT_OF_MEMORY;
+ if(!result)
+ result = setname(part, post->name, post->namelength);
+ if(!result) {
+ multipart = curl_mime_init(data);
+ if(!multipart)
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ if(!result)
+ result = curl_mime_subparts(part, multipart);
+ }
+
+ /* Generate all the part contents. */
+ for(file = post; !result && file; file = file->more) {
+ /* Create the part. */
+ part = curl_mime_addpart(multipart);
+ if(!part)
+ result = CURLE_OUT_OF_MEMORY;
+
+ /* Set the headers. */
+ if(!result)
+ result = curl_mime_headers(part, file->contentheader, 0);
+
+ /* Set the content type. */
+ if(!result && file->contenttype)
+ result = curl_mime_type(part, file->contenttype);
+
+ /* Set field name. */
+ if(!result && !post->more)
+ result = setname(part, post->name, post->namelength);
+
+ /* Process contents. */
+ if(!result) {
+ curl_off_t clen = post->contentslength;
+
+ if(post->flags & CURL_HTTPPOST_LARGE)
+ clen = post->contentlen;
+
+ if(post->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE)) {
+ if(!strcmp(file->contents, "-")) {
+ /* There are a few cases where the code below won't work; in
+ particular, freopen(stdin) by the caller is not guaranteed
+ to result as expected. This feature has been kept for backward
+ compatibility: use of "-" pseudo file name should be avoided. */
+ result = curl_mime_data_cb(part, (curl_off_t) -1,
+ (curl_read_callback) fread,
+ CURLX_FUNCTION_CAST(curl_seek_callback,
+ fseek),
+ NULL, (void *) stdin);
+ }
+ else
+ result = curl_mime_filedata(part, file->contents);
+ if(!result && (post->flags & HTTPPOST_READFILE))
+ result = curl_mime_filename(part, NULL);
+ }
+ else if(post->flags & HTTPPOST_BUFFER)
+ result = curl_mime_data(part, post->buffer,
+ post->bufferlength? post->bufferlength: -1);
+ else if(post->flags & HTTPPOST_CALLBACK) {
+ /* the contents should be read with the callback and the size is set
+ with the contentslength */
+ if(!clen)
+ clen = -1;
+ result = curl_mime_data_cb(part, clen,
+ fread_func, NULL, NULL, post->userp);
+ }
+ else {
+ size_t uclen;
+ if(!clen)
+ uclen = CURL_ZERO_TERMINATED;
+ else
+ uclen = (size_t)clen;
+ result = curl_mime_data(part, post->contents, uclen);
+ }
+ }
+
+ /* Set fake file name. */
+ if(!result && post->showfilename)
+ if(post->more || (post->flags & (HTTPPOST_FILENAME | HTTPPOST_BUFFER |
+ HTTPPOST_CALLBACK)))
+ result = curl_mime_filename(part, post->showfilename);
+ }
+ }
+
+ if(result)
+ Curl_mime_cleanpart(finalform);
+
+ return result;
+}
+
+#else
+/* if disabled */
+CURLFORMcode curl_formadd(struct curl_httppost **httppost,
+ struct curl_httppost **last_post,
+ ...)
+{
+ (void)httppost;
+ (void)last_post;
+ return CURL_FORMADD_DISABLED;
+}
+
+int curl_formget(struct curl_httppost *form, void *arg,
+ curl_formget_callback append)
+{
+ (void) form;
+ (void) arg;
+ (void) append;
+ return CURL_FORMADD_DISABLED;
+}
+
+void curl_formfree(struct curl_httppost *form)
+{
+ (void)form;
+ /* does nothing HTTP is disabled */
+}
+
+#endif /* if disabled */
diff --git a/libs/libcurl/src/formdata.h b/libs/libcurl/src/formdata.h index c6c6397cd2..4ae1a30fca 100644 --- a/libs/libcurl/src/formdata.h +++ b/libs/libcurl/src/formdata.h @@ -1,62 +1,62 @@ -#ifndef HEADER_CURL_FORMDATA_H -#define HEADER_CURL_FORMDATA_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_MIME - -/* used by FormAdd for temporary storage */ -struct FormInfo { - char *name; - size_t namelength; - char *value; - curl_off_t contentslength; - char *contenttype; - long flags; - char *buffer; /* pointer to existing buffer used for file upload */ - size_t bufferlength; - char *showfilename; /* The file name to show. If not set, the actual - file name will be used */ - char *userp; /* pointer for the read callback */ - struct curl_slist *contentheader; - struct FormInfo *more; - bool name_alloc; - bool value_alloc; - bool contenttype_alloc; - bool showfilename_alloc; -}; - -CURLcode Curl_getformdata(struct Curl_easy *data, - curl_mimepart *, - struct curl_httppost *post, - curl_read_callback fread_func); -#else -/* disabled */ -#define Curl_getformdata(a,b,c,d) CURLE_NOT_BUILT_IN -#endif - - -#endif /* HEADER_CURL_FORMDATA_H */ +#ifndef HEADER_CURL_FORMDATA_H
+#define HEADER_CURL_FORMDATA_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_MIME
+
+/* used by FormAdd for temporary storage */
+struct FormInfo {
+ char *name;
+ size_t namelength;
+ char *value;
+ curl_off_t contentslength;
+ char *contenttype;
+ long flags;
+ char *buffer; /* pointer to existing buffer used for file upload */
+ size_t bufferlength;
+ char *showfilename; /* The file name to show. If not set, the actual
+ file name will be used */
+ char *userp; /* pointer for the read callback */
+ struct curl_slist *contentheader;
+ struct FormInfo *more;
+ bool name_alloc;
+ bool value_alloc;
+ bool contenttype_alloc;
+ bool showfilename_alloc;
+};
+
+CURLcode Curl_getformdata(struct Curl_easy *data,
+ curl_mimepart *,
+ struct curl_httppost *post,
+ curl_read_callback fread_func);
+#else
+/* disabled */
+#define Curl_getformdata(a,b,c,d) CURLE_NOT_BUILT_IN
+#endif
+
+
+#endif /* HEADER_CURL_FORMDATA_H */
diff --git a/libs/libcurl/src/ftp.c b/libs/libcurl/src/ftp.c index f1a25b23dc..8332029c6e 100644 --- a/libs/libcurl/src/ftp.c +++ b/libs/libcurl/src/ftp.c @@ -1,4421 +1,4385 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - -#ifdef HAVE_NETINET_IN_H -#include <netinet/in.h> -#endif -#ifdef HAVE_ARPA_INET_H -#include <arpa/inet.h> -#endif -#ifdef HAVE_UTSNAME_H -#include <sys/utsname.h> -#endif -#ifdef HAVE_NETDB_H -#include <netdb.h> -#endif -#ifdef __VMS -#include <in.h> -#include <inet.h> -#endif - -#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) -#undef in_addr_t -#define in_addr_t unsigned long -#endif - -#include <curl/curl.h> -#include "urldata.h" -#include "sendf.h" -#include "if2ip.h" -#include "hostip.h" -#include "progress.h" -#include "transfer.h" -#include "escape.h" -#include "http.h" /* for HTTP proxy tunnel stuff */ -#include "ftp.h" -#include "fileinfo.h" -#include "ftplistparser.h" -#include "curl_range.h" -#include "curl_krb5.h" -#include "strtoofft.h" -#include "strcase.h" -#include "vtls/vtls.h" -#include "connect.h" -#include "strerror.h" -#include "inet_ntop.h" -#include "inet_pton.h" -#include "select.h" -#include "parsedate.h" /* for the week day and month names */ -#include "sockaddr.h" /* required for Curl_sockaddr_storage */ -#include "multiif.h" -#include "url.h" -#include "strcase.h" -#include "speedcheck.h" -#include "warnless.h" -#include "http_proxy.h" -#include "socks.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#ifndef NI_MAXHOST -#define NI_MAXHOST 1025 -#endif -#ifndef INET_ADDRSTRLEN -#define INET_ADDRSTRLEN 16 -#endif - -#ifdef CURL_DISABLE_VERBOSE_STRINGS -#define ftp_pasv_verbose(a,b,c,d) Curl_nop_stmt -#endif - -/* Local API functions */ -#ifndef DEBUGBUILD -static void _state(struct Curl_easy *data, - ftpstate newstate); -#define state(x,y) _state(x,y) -#else -static void _state(struct Curl_easy *data, - ftpstate newstate, - int lineno); -#define state(x,y) _state(x,y,__LINE__) -#endif - -static CURLcode ftp_sendquote(struct Curl_easy *data, - struct connectdata *conn, - struct curl_slist *quote); -static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn); -static CURLcode ftp_parse_url_path(struct Curl_easy *data); -static CURLcode ftp_regular_transfer(struct Curl_easy *data, bool *done); -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static void ftp_pasv_verbose(struct Curl_easy *data, - struct Curl_addrinfo *ai, - char *newhost, /* ascii version */ - int port); -#endif -static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data); -static CURLcode ftp_state_mdtm(struct Curl_easy *data); -static CURLcode ftp_state_quote(struct Curl_easy *data, - bool init, ftpstate instate); -static CURLcode ftp_nb_type(struct Curl_easy *data, - struct connectdata *conn, - bool ascii, ftpstate newstate); -static int ftp_need_type(struct connectdata *conn, - bool ascii); -static CURLcode ftp_do(struct Curl_easy *data, bool *done); -static CURLcode ftp_done(struct Curl_easy *data, - CURLcode, bool premature); -static CURLcode ftp_connect(struct Curl_easy *data, bool *done); -static CURLcode ftp_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead_connection); -static CURLcode ftp_do_more(struct Curl_easy *data, int *completed); -static CURLcode ftp_multi_statemach(struct Curl_easy *data, bool *done); -static int ftp_getsock(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t *socks); -static int ftp_domore_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks); -static CURLcode ftp_doing(struct Curl_easy *data, - bool *dophase_done); -static CURLcode ftp_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode init_wc_data(struct Curl_easy *data); -static CURLcode wc_statemach(struct Curl_easy *data); -static void wc_data_dtor(void *ptr); -static CURLcode ftp_state_retr(struct Curl_easy *data, curl_off_t filesize); -static CURLcode ftp_readresp(struct Curl_easy *data, - curl_socket_t sockfd, - struct pingpong *pp, - int *ftpcode, - size_t *size); -static CURLcode ftp_dophase_done(struct Curl_easy *data, - bool connected); - -/* - * FTP protocol handler. - */ - -const struct Curl_handler Curl_handler_ftp = { - "FTP", /* scheme */ - ftp_setup_connection, /* setup_connection */ - ftp_do, /* do_it */ - ftp_done, /* done */ - ftp_do_more, /* do_more */ - ftp_connect, /* connect_it */ - ftp_multi_statemach, /* connecting */ - ftp_doing, /* doing */ - ftp_getsock, /* proto_getsock */ - ftp_getsock, /* doing_getsock */ - ftp_domore_getsock, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - ftp_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_FTP, /* defport */ - CURLPROTO_FTP, /* protocol */ - CURLPROTO_FTP, /* family */ - PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD | - PROTOPT_NOURLQUERY | PROTOPT_PROXY_AS_HTTP | - PROTOPT_WILDCARD /* flags */ -}; - - -#ifdef USE_SSL -/* - * FTPS protocol handler. - */ - -const struct Curl_handler Curl_handler_ftps = { - "FTPS", /* scheme */ - ftp_setup_connection, /* setup_connection */ - ftp_do, /* do_it */ - ftp_done, /* done */ - ftp_do_more, /* do_more */ - ftp_connect, /* connect_it */ - ftp_multi_statemach, /* connecting */ - ftp_doing, /* doing */ - ftp_getsock, /* proto_getsock */ - ftp_getsock, /* doing_getsock */ - ftp_domore_getsock, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - ftp_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_FTPS, /* defport */ - CURLPROTO_FTPS, /* protocol */ - CURLPROTO_FTP, /* family */ - PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION | - PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY | PROTOPT_WILDCARD /* flags */ -}; -#endif - -static void close_secondarysocket(struct Curl_easy *data, - struct connectdata *conn) -{ - if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) { - Curl_closesocket(data, conn, conn->sock[SECONDARYSOCKET]); - conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; - } - conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE; -#ifndef CURL_DISABLE_PROXY - conn->bits.proxy_ssl_connected[SECONDARYSOCKET] = FALSE; -#endif -} - -/* - * NOTE: back in the old days, we added code in the FTP code that made NOBODY - * requests on files respond with headers passed to the client/stdout that - * looked like HTTP ones. - * - * This approach is not very elegant, it causes confusion and is error-prone. - * It is subject for removal at the next (or at least a future) soname bump. - * Until then you can test the effects of the removal by undefining the - * following define named CURL_FTP_HTTPSTYLE_HEAD. - */ -#define CURL_FTP_HTTPSTYLE_HEAD 1 - -static void freedirs(struct ftp_conn *ftpc) -{ - if(ftpc->dirs) { - int i; - for(i = 0; i < ftpc->dirdepth; i++) { - free(ftpc->dirs[i]); - ftpc->dirs[i] = NULL; - } - free(ftpc->dirs); - ftpc->dirs = NULL; - ftpc->dirdepth = 0; - } - Curl_safefree(ftpc->file); - - /* no longer of any use */ - Curl_safefree(ftpc->newhost); -} - -/*********************************************************************** - * - * AcceptServerConnect() - * - * After connection request is received from the server this function is - * called to accept the connection and close the listening socket - * - */ -static CURLcode AcceptServerConnect(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - curl_socket_t sock = conn->sock[SECONDARYSOCKET]; - curl_socket_t s = CURL_SOCKET_BAD; -#ifdef ENABLE_IPV6 - struct Curl_sockaddr_storage add; -#else - struct sockaddr_in add; -#endif - curl_socklen_t size = (curl_socklen_t) sizeof(add); - - if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) { - size = sizeof(add); - - s = accept(sock, (struct sockaddr *) &add, &size); - } - Curl_closesocket(data, conn, sock); /* close the first socket */ - - if(CURL_SOCKET_BAD == s) { - failf(data, "Error accept()ing server connect"); - return CURLE_FTP_PORT_FAILED; - } - infof(data, "Connection accepted from server"); - /* when this happens within the DO state it is important that we mark us as - not needing DO_MORE anymore */ - conn->bits.do_more = FALSE; - - conn->sock[SECONDARYSOCKET] = s; - (void)curlx_nonblock(s, TRUE); /* enable non-blocking */ - conn->bits.sock_accepted = TRUE; - - if(data->set.fsockopt) { - int error = 0; - - /* activate callback for setting socket options */ - Curl_set_in_callback(data, true); - error = data->set.fsockopt(data->set.sockopt_client, - s, - CURLSOCKTYPE_ACCEPT); - Curl_set_in_callback(data, false); - - if(error) { - close_secondarysocket(data, conn); - return CURLE_ABORTED_BY_CALLBACK; - } - } - - return CURLE_OK; - -} - -/* - * ftp_timeleft_accept() returns the amount of milliseconds left allowed for - * waiting server to connect. If the value is negative, the timeout time has - * already elapsed. - * - * The start time is stored in progress.t_acceptdata - as set with - * Curl_pgrsTime(..., TIMER_STARTACCEPT); - * - */ -static timediff_t ftp_timeleft_accept(struct Curl_easy *data) -{ - timediff_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT; - timediff_t other; - struct curltime now; - - if(data->set.accepttimeout > 0) - timeout_ms = data->set.accepttimeout; - - now = Curl_now(); - - /* check if the generic timeout possibly is set shorter */ - other = Curl_timeleft(data, &now, FALSE); - if(other && (other < timeout_ms)) - /* note that this also works fine for when other happens to be negative - due to it already having elapsed */ - timeout_ms = other; - else { - /* subtract elapsed time */ - timeout_ms -= Curl_timediff(now, data->progress.t_acceptdata); - if(!timeout_ms) - /* avoid returning 0 as that means no timeout! */ - return -1; - } - - return timeout_ms; -} - - -/*********************************************************************** - * - * ReceivedServerConnect() - * - * After allowing server to connect to us from data port, this function - * checks both data connection for connection establishment and ctrl - * connection for a negative response regarding a failure in connecting - * - */ -static CURLcode ReceivedServerConnect(struct Curl_easy *data, bool *received) -{ - struct connectdata *conn = data->conn; - curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET]; - curl_socket_t data_sock = conn->sock[SECONDARYSOCKET]; - struct ftp_conn *ftpc = &conn->proto.ftpc; - struct pingpong *pp = &ftpc->pp; - int result; - timediff_t timeout_ms; - ssize_t nread; - int ftpcode; - - *received = FALSE; - - timeout_ms = ftp_timeleft_accept(data); - infof(data, "Checking for server connect"); - if(timeout_ms < 0) { - /* if a timeout was already reached, bail out */ - failf(data, "Accept timeout occurred while waiting server connect"); - return CURLE_FTP_ACCEPT_TIMEOUT; - } - - /* First check whether there is a cached response from server */ - if(pp->cache_size && pp->cache && pp->cache[0] > '3') { - /* Data connection could not be established, let's return */ - infof(data, "There is negative response in cache while serv connect"); - (void)Curl_GetFTPResponse(data, &nread, &ftpcode); - return CURLE_FTP_ACCEPT_FAILED; - } - - result = Curl_socket_check(ctrl_sock, data_sock, CURL_SOCKET_BAD, 0); - - /* see if the connection request is already here */ - switch(result) { - case -1: /* error */ - /* let's die here */ - failf(data, "Error while waiting for server connect"); - return CURLE_FTP_ACCEPT_FAILED; - case 0: /* Server connect is not received yet */ - break; /* loop */ - default: - - if(result & CURL_CSELECT_IN2) { - infof(data, "Ready to accept data connection from server"); - *received = TRUE; - } - else if(result & CURL_CSELECT_IN) { - infof(data, "Ctrl conn has data while waiting for data conn"); - (void)Curl_GetFTPResponse(data, &nread, &ftpcode); - - if(ftpcode/100 > 3) - return CURLE_FTP_ACCEPT_FAILED; - - return CURLE_WEIRD_SERVER_REPLY; - } - - break; - } /* switch() */ - - return CURLE_OK; -} - - -/*********************************************************************** - * - * InitiateTransfer() - * - * After connection from server is accepted this function is called to - * setup transfer parameters and initiate the data transfer. - * - */ -static CURLcode InitiateTransfer(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - - if(conn->bits.ftp_use_data_ssl) { - /* since we only have a plaintext TCP connection here, we must now - * do the TLS stuff */ - infof(data, "Doing the SSL/TLS handshake on the data stream"); - result = Curl_ssl_connect(data, conn, SECONDARYSOCKET); - if(result) - return result; - } - - if(conn->proto.ftpc.state_saved == FTP_STOR) { - /* When we know we're uploading a specified file, we can get the file - size prior to the actual upload. */ - Curl_pgrsSetUploadSize(data, data->state.infilesize); - - /* set the SO_SNDBUF for the secondary socket for those who need it */ - Curl_sndbufset(conn->sock[SECONDARYSOCKET]); - - Curl_setup_transfer(data, -1, -1, FALSE, SECONDARYSOCKET); - } - else { - /* FTP download: */ - Curl_setup_transfer(data, SECONDARYSOCKET, - conn->proto.ftpc.retr_size_saved, FALSE, -1); - } - - conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */ - state(data, FTP_STOP); - - return CURLE_OK; -} - -/*********************************************************************** - * - * AllowServerConnect() - * - * When we've issue the PORT command, we have told the server to connect to - * us. This function checks whether data connection is established if so it is - * accepted. - * - */ -static CURLcode AllowServerConnect(struct Curl_easy *data, bool *connected) -{ - timediff_t timeout_ms; - CURLcode result = CURLE_OK; - - *connected = FALSE; - infof(data, "Preparing for accepting server on data port"); - - /* Save the time we start accepting server connect */ - Curl_pgrsTime(data, TIMER_STARTACCEPT); - - timeout_ms = ftp_timeleft_accept(data); - if(timeout_ms < 0) { - /* if a timeout was already reached, bail out */ - failf(data, "Accept timeout occurred while waiting server connect"); - return CURLE_FTP_ACCEPT_TIMEOUT; - } - - /* see if the connection request is already here */ - result = ReceivedServerConnect(data, connected); - if(result) - return result; - - if(*connected) { - result = AcceptServerConnect(data); - if(result) - return result; - - result = InitiateTransfer(data); - if(result) - return result; - } - else { - /* Add timeout to multi handle and break out of the loop */ - Curl_expire(data, data->set.accepttimeout ? - data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT, - EXPIRE_FTP_ACCEPT); - } - - return result; -} - -/* macro to check for a three-digit ftp status code at the start of the - given string */ -#define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) && \ - ISDIGIT(line[2])) - -/* macro to check for the last line in an FTP server response */ -#define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3])) - -static bool ftp_endofresp(struct Curl_easy *data, struct connectdata *conn, - char *line, size_t len, int *code) -{ - (void)data; - (void)conn; - - if((len > 3) && LASTLINE(line)) { - *code = curlx_sltosi(strtol(line, NULL, 10)); - return TRUE; - } - - return FALSE; -} - -static CURLcode ftp_readresp(struct Curl_easy *data, - curl_socket_t sockfd, - struct pingpong *pp, - int *ftpcode, /* return the ftp-code if done */ - size_t *size) /* size of the response */ -{ - int code; - CURLcode result = Curl_pp_readresp(data, sockfd, pp, &code, size); - -#ifdef HAVE_GSSAPI - { - struct connectdata *conn = data->conn; - char * const buf = data->state.buffer; - - /* handle the security-oriented responses 6xx ***/ - switch(code) { - case 631: - code = Curl_sec_read_msg(data, conn, buf, PROT_SAFE); - break; - case 632: - code = Curl_sec_read_msg(data, conn, buf, PROT_PRIVATE); - break; - case 633: - code = Curl_sec_read_msg(data, conn, buf, PROT_CONFIDENTIAL); - break; - default: - /* normal ftp stuff we pass through! */ - break; - } - } -#endif - - /* store the latest code for later retrieval */ - data->info.httpcode = code; - - if(ftpcode) - *ftpcode = code; - - if(421 == code) { - /* 421 means "Service not available, closing control connection." and FTP - * servers use it to signal that idle session timeout has been exceeded. - * If we ignored the response, it could end up hanging in some cases. - * - * This response code can come at any point so having it treated - * generically is a good idea. - */ - infof(data, "We got a 421 - timeout"); - state(data, FTP_STOP); - return CURLE_OPERATION_TIMEDOUT; - } - - return result; -} - -/* --- parse FTP server responses --- */ - -/* - * Curl_GetFTPResponse() is a BLOCKING function to read the full response - * from a server after a command. - * - */ - -CURLcode Curl_GetFTPResponse(struct Curl_easy *data, - ssize_t *nreadp, /* return number of bytes read */ - int *ftpcode) /* return the ftp-code */ -{ - /* - * We cannot read just one byte per read() and then go back to select() as - * the OpenSSL read() doesn't grok that properly. - * - * Alas, read as much as possible, split up into lines, use the ending - * line in a response or continue reading. */ - - struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; - CURLcode result = CURLE_OK; - struct ftp_conn *ftpc = &conn->proto.ftpc; - struct pingpong *pp = &ftpc->pp; - size_t nread; - int cache_skip = 0; - int value_to_be_ignored = 0; - - if(ftpcode) - *ftpcode = 0; /* 0 for errors */ - else - /* make the pointer point to something for the rest of this function */ - ftpcode = &value_to_be_ignored; - - *nreadp = 0; - - while(!*ftpcode && !result) { - /* check and reset timeout value every lap */ - timediff_t timeout = Curl_pp_state_timeout(data, pp, FALSE); - timediff_t interval_ms; - - if(timeout <= 0) { - failf(data, "FTP response timeout"); - return CURLE_OPERATION_TIMEDOUT; /* already too little time */ - } - - interval_ms = 1000; /* use 1 second timeout intervals */ - if(timeout < interval_ms) - interval_ms = timeout; - - /* - * Since this function is blocking, we need to wait here for input on the - * connection and only then we call the response reading function. We do - * timeout at least every second to make the timeout check run. - * - * A caution here is that the ftp_readresp() function has a cache that may - * contain pieces of a response from the previous invoke and we need to - * make sure we don't just wait for input while there is unhandled data in - * that cache. But also, if the cache is there, we call ftp_readresp() and - * the cache wasn't good enough to continue we must not just busy-loop - * around this function. - * - */ - - if(pp->cache && (cache_skip < 2)) { - /* - * There's a cache left since before. We then skipping the wait for - * socket action, unless this is the same cache like the previous round - * as then the cache was deemed not enough to act on and we then need to - * wait for more data anyway. - */ - } - else if(!Curl_conn_data_pending(conn, FIRSTSOCKET)) { - switch(SOCKET_READABLE(sockfd, interval_ms)) { - case -1: /* select() error, stop reading */ - failf(data, "FTP response aborted due to select/poll error: %d", - SOCKERRNO); - return CURLE_RECV_ERROR; - - case 0: /* timeout */ - if(Curl_pgrsUpdate(data)) - return CURLE_ABORTED_BY_CALLBACK; - continue; /* just continue in our loop for the timeout duration */ - - default: /* for clarity */ - break; - } - } - result = ftp_readresp(data, sockfd, pp, ftpcode, &nread); - if(result) - break; - - if(!nread && pp->cache) - /* bump cache skip counter as on repeated skips we must wait for more - data */ - cache_skip++; - else - /* when we got data or there is no cache left, we reset the cache skip - counter */ - cache_skip = 0; - - *nreadp += nread; - - } /* while there's buffer left and loop is requested */ - - pp->pending_resp = FALSE; - - return result; -} - -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - /* for debug purposes */ -static const char * const ftp_state_names[]={ - "STOP", - "WAIT220", - "AUTH", - "USER", - "PASS", - "ACCT", - "PBSZ", - "PROT", - "CCC", - "PWD", - "SYST", - "NAMEFMT", - "QUOTE", - "RETR_PREQUOTE", - "STOR_PREQUOTE", - "POSTQUOTE", - "CWD", - "MKD", - "MDTM", - "TYPE", - "LIST_TYPE", - "RETR_TYPE", - "STOR_TYPE", - "SIZE", - "RETR_SIZE", - "STOR_SIZE", - "REST", - "RETR_REST", - "PORT", - "PRET", - "PASV", - "LIST", - "RETR", - "STOR", - "QUIT" -}; -#endif - -/* This is the ONLY way to change FTP state! */ -static void _state(struct Curl_easy *data, - ftpstate newstate -#ifdef DEBUGBUILD - , int lineno -#endif - ) -{ - struct connectdata *conn = data->conn; - struct ftp_conn *ftpc = &conn->proto.ftpc; - -#if defined(DEBUGBUILD) - -#if defined(CURL_DISABLE_VERBOSE_STRINGS) - (void) lineno; -#else - if(ftpc->state != newstate) - infof(data, "FTP %p (line %d) state change from %s to %s", - (void *)ftpc, lineno, ftp_state_names[ftpc->state], - ftp_state_names[newstate]); -#endif -#endif - - ftpc->state = newstate; -} - -static CURLcode ftp_state_user(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = Curl_pp_sendf(data, - &conn->proto.ftpc.pp, "USER %s", - conn->user?conn->user:""); - if(!result) { - struct ftp_conn *ftpc = &conn->proto.ftpc; - ftpc->ftp_trying_alternative = FALSE; - state(data, FTP_USER); - } - return result; -} - -static CURLcode ftp_state_pwd(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PWD"); - if(!result) - state(data, FTP_PWD); - - return result; -} - -/* For the FTP "protocol connect" and "doing" phases only */ -static int ftp_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *socks) -{ - return Curl_pp_getsock(data, &conn->proto.ftpc.pp, socks); -} - -/* For the FTP "DO_MORE" phase only */ -static int ftp_domore_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks) -{ - struct ftp_conn *ftpc = &conn->proto.ftpc; - (void)data; - - /* When in DO_MORE state, we could be either waiting for us to connect to a - * remote site, or we could wait for that site to connect to us. Or just - * handle ordinary commands. - */ - - if(SOCKS_STATE(conn->cnnct.state)) - return Curl_SOCKS_getsock(conn, socks, SECONDARYSOCKET); - - if(FTP_STOP == ftpc->state) { - int bits = GETSOCK_READSOCK(0); - bool any = FALSE; - - /* if stopped and still in this state, then we're also waiting for a - connect on the secondary connection */ - socks[0] = conn->sock[FIRSTSOCKET]; - - if(!data->set.ftp_use_port) { - int s; - int i; - /* PORT is used to tell the server to connect to us, and during that we - don't do happy eyeballs, but we do if we connect to the server */ - for(s = 1, i = 0; i<2; i++) { - if(conn->tempsock[i] != CURL_SOCKET_BAD) { - socks[s] = conn->tempsock[i]; - bits |= GETSOCK_WRITESOCK(s++); - any = TRUE; - } - } - } - if(!any) { - socks[1] = conn->sock[SECONDARYSOCKET]; - bits |= GETSOCK_WRITESOCK(1) | GETSOCK_READSOCK(1); - } - - return bits; - } - return Curl_pp_getsock(data, &conn->proto.ftpc.pp, socks); -} - -/* This is called after the FTP_QUOTE state is passed. - - ftp_state_cwd() sends the range of CWD commands to the server to change to - the correct directory. It may also need to send MKD commands to create - missing ones, if that option is enabled. -*/ -static CURLcode ftp_state_cwd(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - struct ftp_conn *ftpc = &conn->proto.ftpc; - - if(ftpc->cwddone) - /* already done and fine */ - result = ftp_state_mdtm(data); - else { - /* FTPFILE_NOCWD with full path: expect ftpc->cwddone! */ - DEBUGASSERT((data->set.ftp_filemethod != FTPFILE_NOCWD) || - !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')); - - ftpc->count2 = 0; /* count2 counts failed CWDs */ - - if(conn->bits.reuse && ftpc->entrypath && - /* no need to go to entrypath when we have an absolute path */ - !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')) { - /* This is a re-used connection. Since we change directory to where the - transfer is taking place, we must first get back to the original dir - where we ended up after login: */ - ftpc->cwdcount = 0; /* we count this as the first path, then we add one - for all upcoming ones in the ftp->dirs[] array */ - result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->entrypath); - if(!result) - state(data, FTP_CWD); - } - else { - if(ftpc->dirdepth) { - ftpc->cwdcount = 1; - /* issue the first CWD, the rest is sent when the CWD responses are - received... */ - result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", - ftpc->dirs[ftpc->cwdcount -1]); - if(!result) - state(data, FTP_CWD); - } - else { - /* No CWD necessary */ - result = ftp_state_mdtm(data); - } - } - } - return result; -} - -typedef enum { - EPRT, - PORT, - DONE -} ftpport; - -static CURLcode ftp_state_use_port(struct Curl_easy *data, - ftpport fcmd) /* start with this */ -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct ftp_conn *ftpc = &conn->proto.ftpc; - curl_socket_t portsock = CURL_SOCKET_BAD; - char myhost[MAX_IPADR_LEN + 1] = ""; - - struct Curl_sockaddr_storage ss; - struct Curl_addrinfo *res, *ai; - curl_socklen_t sslen; - char hbuf[NI_MAXHOST]; - struct sockaddr *sa = (struct sockaddr *)&ss; - struct sockaddr_in * const sa4 = (void *)sa; -#ifdef ENABLE_IPV6 - struct sockaddr_in6 * const sa6 = (void *)sa; -#endif - static const char mode[][5] = { "EPRT", "PORT" }; - enum resolve_t rc; - int error; - char *host = NULL; - char *string_ftpport = data->set.str[STRING_FTPPORT]; - struct Curl_dns_entry *h = NULL; - unsigned short port_min = 0; - unsigned short port_max = 0; - unsigned short port; - bool possibly_non_local = TRUE; - char buffer[STRERROR_LEN]; - char *addr = NULL; - - /* Step 1, figure out what is requested, - * accepted format : - * (ipv4|ipv6|domain|interface)?(:port(-range)?)? - */ - - if(data->set.str[STRING_FTPPORT] && - (strlen(data->set.str[STRING_FTPPORT]) > 1)) { - -#ifdef ENABLE_IPV6 - size_t addrlen = INET6_ADDRSTRLEN > strlen(string_ftpport) ? - INET6_ADDRSTRLEN : strlen(string_ftpport); -#else - size_t addrlen = INET_ADDRSTRLEN > strlen(string_ftpport) ? - INET_ADDRSTRLEN : strlen(string_ftpport); -#endif - char *ip_start = string_ftpport; - char *ip_end = NULL; - char *port_start = NULL; - char *port_sep = NULL; - - addr = calloc(addrlen + 1, 1); - if(!addr) - return CURLE_OUT_OF_MEMORY; - -#ifdef ENABLE_IPV6 - if(*string_ftpport == '[') { - /* [ipv6]:port(-range) */ - ip_start = string_ftpport + 1; - ip_end = strchr(string_ftpport, ']'); - if(ip_end) - strncpy(addr, ip_start, ip_end - ip_start); - } - else -#endif - if(*string_ftpport == ':') { - /* :port */ - ip_end = string_ftpport; - } - else { - ip_end = strchr(string_ftpport, ':'); - if(ip_end) { - /* either ipv6 or (ipv4|domain|interface):port(-range) */ -#ifdef ENABLE_IPV6 - if(Curl_inet_pton(AF_INET6, string_ftpport, sa6) == 1) { - /* ipv6 */ - port_min = port_max = 0; - strcpy(addr, string_ftpport); - ip_end = NULL; /* this got no port ! */ - } - else -#endif - /* (ipv4|domain|interface):port(-range) */ - strncpy(addr, string_ftpport, ip_end - ip_start); - } - else - /* ipv4|interface */ - strcpy(addr, string_ftpport); - } - - /* parse the port */ - if(ip_end) { - port_start = strchr(ip_end, ':'); - if(port_start) { - port_min = curlx_ultous(strtoul(port_start + 1, NULL, 10)); - port_sep = strchr(port_start, '-'); - if(port_sep) { - port_max = curlx_ultous(strtoul(port_sep + 1, NULL, 10)); - } - else - port_max = port_min; - } - } - - /* correct errors like: - * :1234-1230 - * :-4711, in this case port_min is (unsigned)-1, - * therefore port_min > port_max for all cases - * but port_max = (unsigned)-1 - */ - if(port_min > port_max) - port_min = port_max = 0; - - - if(*addr != '\0') { - /* attempt to get the address of the given interface name */ - switch(Curl_if2ip(conn->ip_addr->ai_family, -#ifdef ENABLE_IPV6 - Curl_ipv6_scope(conn->ip_addr->ai_addr), - conn->scope_id, -#endif - addr, hbuf, sizeof(hbuf))) { - case IF2IP_NOT_FOUND: - /* not an interface, use the given string as host name instead */ - host = addr; - break; - case IF2IP_AF_NOT_SUPPORTED: - return CURLE_FTP_PORT_FAILED; - case IF2IP_FOUND: - host = hbuf; /* use the hbuf for host name */ - } - } - else - /* there was only a port(-range) given, default the host */ - host = NULL; - } /* data->set.ftpport */ - - if(!host) { - const char *r; - /* not an interface and not a host name, get default by extracting - the IP from the control connection */ - sslen = sizeof(ss); - if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) { - failf(data, "getsockname() failed: %s", - Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - free(addr); - return CURLE_FTP_PORT_FAILED; - } - switch(sa->sa_family) { -#ifdef ENABLE_IPV6 - case AF_INET6: - r = Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf)); - break; -#endif - default: - r = Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf)); - break; - } - if(!r) - return CURLE_FTP_PORT_FAILED; - host = hbuf; /* use this host name */ - possibly_non_local = FALSE; /* we know it is local now */ - } - - /* resolv ip/host to ip */ - rc = Curl_resolv(data, host, 0, FALSE, &h); - if(rc == CURLRESOLV_PENDING) - (void)Curl_resolver_wait_resolv(data, &h); - if(h) { - res = h->addr; - /* when we return from this function, we can forget about this entry - to we can unlock it now already */ - Curl_resolv_unlock(data, h); - } /* (h) */ - else - res = NULL; /* failure! */ - - if(!res) { - failf(data, "failed to resolve the address provided to PORT: %s", host); - free(addr); - return CURLE_FTP_PORT_FAILED; - } - - free(addr); - host = NULL; - - /* step 2, create a socket for the requested address */ - - portsock = CURL_SOCKET_BAD; - error = 0; - for(ai = res; ai; ai = ai->ai_next) { - result = Curl_socket(data, ai, NULL, &portsock); - if(result) { - error = SOCKERRNO; - continue; - } - break; - } - if(!ai) { - failf(data, "socket failure: %s", - Curl_strerror(error, buffer, sizeof(buffer))); - return CURLE_FTP_PORT_FAILED; - } - - /* step 3, bind to a suitable local address */ - - memcpy(sa, ai->ai_addr, ai->ai_addrlen); - sslen = ai->ai_addrlen; - - for(port = port_min; port <= port_max;) { - if(sa->sa_family == AF_INET) - sa4->sin_port = htons(port); -#ifdef ENABLE_IPV6 - else - sa6->sin6_port = htons(port); -#endif - /* Try binding the given address. */ - if(bind(portsock, sa, sslen) ) { - /* It failed. */ - error = SOCKERRNO; - if(possibly_non_local && (error == EADDRNOTAVAIL)) { - /* The requested bind address is not local. Use the address used for - * the control connection instead and restart the port loop - */ - infof(data, "bind(port=%hu) on non-local address failed: %s", port, - Curl_strerror(error, buffer, sizeof(buffer))); - - sslen = sizeof(ss); - if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) { - failf(data, "getsockname() failed: %s", - Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - Curl_closesocket(data, conn, portsock); - return CURLE_FTP_PORT_FAILED; - } - port = port_min; - possibly_non_local = FALSE; /* don't try this again */ - continue; - } - if(error != EADDRINUSE && error != EACCES) { - failf(data, "bind(port=%hu) failed: %s", port, - Curl_strerror(error, buffer, sizeof(buffer))); - Curl_closesocket(data, conn, portsock); - return CURLE_FTP_PORT_FAILED; - } - } - else - break; - - port++; - } - - /* maybe all ports were in use already*/ - if(port > port_max) { - failf(data, "bind() failed, we ran out of ports"); - Curl_closesocket(data, conn, portsock); - return CURLE_FTP_PORT_FAILED; - } - - /* get the name again after the bind() so that we can extract the - port number it uses now */ - sslen = sizeof(ss); - if(getsockname(portsock, (struct sockaddr *)sa, &sslen)) { - failf(data, "getsockname() failed: %s", - Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - Curl_closesocket(data, conn, portsock); - return CURLE_FTP_PORT_FAILED; - } - - /* step 4, listen on the socket */ - - if(listen(portsock, 1)) { - failf(data, "socket failure: %s", - Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - Curl_closesocket(data, conn, portsock); - return CURLE_FTP_PORT_FAILED; - } - - /* step 5, send the proper FTP command */ - - /* get a plain printable version of the numerical address to work with - below */ - Curl_printable_address(ai, myhost, sizeof(myhost)); - -#ifdef ENABLE_IPV6 - if(!conn->bits.ftp_use_eprt && conn->bits.ipv6) - /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the - request and enable EPRT again! */ - conn->bits.ftp_use_eprt = TRUE; -#endif - - for(; fcmd != DONE; fcmd++) { - - if(!conn->bits.ftp_use_eprt && (EPRT == fcmd)) - /* if disabled, goto next */ - continue; - - if((PORT == fcmd) && sa->sa_family != AF_INET) - /* PORT is IPv4 only */ - continue; - - switch(sa->sa_family) { - case AF_INET: - port = ntohs(sa4->sin_port); - break; -#ifdef ENABLE_IPV6 - case AF_INET6: - port = ntohs(sa6->sin6_port); - break; -#endif - default: - continue; /* might as well skip this */ - } - - if(EPRT == fcmd) { - /* - * Two fine examples from RFC2428; - * - * EPRT |1|132.235.1.2|6275| - * - * EPRT |2|1080::8:800:200C:417A|5282| - */ - - result = Curl_pp_sendf(data, &ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd], - sa->sa_family == AF_INET?1:2, - myhost, port); - if(result) { - failf(data, "Failure sending EPRT command: %s", - curl_easy_strerror(result)); - Curl_closesocket(data, conn, portsock); - /* don't retry using PORT */ - ftpc->count1 = PORT; - /* bail out */ - state(data, FTP_STOP); - return result; - } - break; - } - if(PORT == fcmd) { - /* large enough for [IP address],[num],[num] */ - char target[sizeof(myhost) + 20]; - char *source = myhost; - char *dest = target; - - /* translate x.x.x.x to x,x,x,x */ - while(source && *source) { - if(*source == '.') - *dest = ','; - else - *dest = *source; - dest++; - source++; - } - *dest = 0; - msnprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff)); - - result = Curl_pp_sendf(data, &ftpc->pp, "%s %s", mode[fcmd], target); - if(result) { - failf(data, "Failure sending PORT command: %s", - curl_easy_strerror(result)); - Curl_closesocket(data, conn, portsock); - /* bail out */ - state(data, FTP_STOP); - return result; - } - break; - } - } - - /* store which command was sent */ - ftpc->count1 = fcmd; - - close_secondarysocket(data, conn); - - /* we set the secondary socket variable to this for now, it is only so that - the cleanup function will close it in case we fail before the true - secondary stuff is made */ - conn->sock[SECONDARYSOCKET] = portsock; - - /* this tcpconnect assignment below is a hackish work-around to make the - multi interface with active FTP work - as it will not wait for a - (passive) connect in Curl_is_connected(). - - The *proper* fix is to make sure that the active connection from the - server is done in a non-blocking way. Currently, it is still BLOCKING. - */ - conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE; - - state(data, FTP_PORT); - return result; -} - -static CURLcode ftp_state_use_pasv(struct Curl_easy *data, - struct connectdata *conn) -{ - struct ftp_conn *ftpc = &conn->proto.ftpc; - CURLcode result = CURLE_OK; - /* - Here's the executive summary on what to do: - - PASV is RFC959, expect: - 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2) - - LPSV is RFC1639, expect: - 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2) - - EPSV is RFC2428, expect: - 229 Entering Extended Passive Mode (|||port|) - - */ - - static const char mode[][5] = { "EPSV", "PASV" }; - int modeoff; - -#ifdef PF_INET6 - if(!conn->bits.ftp_use_epsv && conn->bits.ipv6) - /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the - request and enable EPSV again! */ - conn->bits.ftp_use_epsv = TRUE; -#endif - - modeoff = conn->bits.ftp_use_epsv?0:1; - - result = Curl_pp_sendf(data, &ftpc->pp, "%s", mode[modeoff]); - if(!result) { - ftpc->count1 = modeoff; - state(data, FTP_PASV); - infof(data, "Connect data stream passively"); - } - return result; -} - -/* - * ftp_state_prepare_transfer() starts PORT, PASV or PRET etc. - * - * REST is the last command in the chain of commands when a "head"-like - * request is made. Thus, if an actual transfer is to be made this is where we - * take off for real. - */ -static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct FTP *ftp = data->req.p.ftp; - struct connectdata *conn = data->conn; - - if(ftp->transfer != PPTRANSFER_BODY) { - /* doesn't transfer any data */ - - /* still possibly do PRE QUOTE jobs */ - state(data, FTP_RETR_PREQUOTE); - result = ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE); - } - else if(data->set.ftp_use_port) { - /* We have chosen to use the PORT (or similar) command */ - result = ftp_state_use_port(data, EPRT); - } - else { - /* We have chosen (this is default) to use the PASV (or similar) command */ - if(data->set.ftp_use_pret) { - /* The user has requested that we send a PRET command - to prepare the server for the upcoming PASV */ - struct ftp_conn *ftpc = &conn->proto.ftpc; - if(!conn->proto.ftpc.file) - result = Curl_pp_sendf(data, &ftpc->pp, "PRET %s", - data->set.str[STRING_CUSTOMREQUEST]? - data->set.str[STRING_CUSTOMREQUEST]: - (data->state.list_only?"NLST":"LIST")); - else if(data->set.upload) - result = Curl_pp_sendf(data, &ftpc->pp, "PRET STOR %s", - conn->proto.ftpc.file); - else - result = Curl_pp_sendf(data, &ftpc->pp, "PRET RETR %s", - conn->proto.ftpc.file); - if(!result) - state(data, FTP_PRET); - } - else - result = ftp_state_use_pasv(data, conn); - } - return result; -} - -static CURLcode ftp_state_rest(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - struct FTP *ftp = data->req.p.ftp; - struct ftp_conn *ftpc = &conn->proto.ftpc; - - if((ftp->transfer != PPTRANSFER_BODY) && ftpc->file) { - /* if a "head"-like request is being made (on a file) */ - - /* Determine if server can respond to REST command and therefore - whether it supports range */ - result = Curl_pp_sendf(data, &ftpc->pp, "REST %d", 0); - if(!result) - state(data, FTP_REST); - } - else - result = ftp_state_prepare_transfer(data); - - return result; -} - -static CURLcode ftp_state_size(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - struct FTP *ftp = data->req.p.ftp; - struct ftp_conn *ftpc = &conn->proto.ftpc; - - if((ftp->transfer == PPTRANSFER_INFO) && ftpc->file) { - /* if a "head"-like request is being made (on a file) */ - - /* we know ftpc->file is a valid pointer to a file name */ - result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file); - if(!result) - state(data, FTP_SIZE); - } - else - result = ftp_state_rest(data, conn); - - return result; -} - -static CURLcode ftp_state_list(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct FTP *ftp = data->req.p.ftp; - struct connectdata *conn = data->conn; - - /* If this output is to be machine-parsed, the NLST command might be better - to use, since the LIST command output is not specified or standard in any - way. It has turned out that the NLST list output is not the same on all - servers either... */ - - /* - if FTPFILE_NOCWD was specified, we should add the path - as argument for the LIST / NLST / or custom command. - Whether the server will support this, is uncertain. - - The other ftp_filemethods will CWD into dir/dir/ first and - then just do LIST (in that case: nothing to do here) - */ - char *lstArg = NULL; - char *cmd; - - if((data->set.ftp_filemethod == FTPFILE_NOCWD) && ftp->path) { - /* url-decode before evaluation: e.g. paths starting/ending with %2f */ - const char *slashPos = NULL; - char *rawPath = NULL; - result = Curl_urldecode(ftp->path, 0, &rawPath, NULL, REJECT_CTRL); - if(result) - return result; - - slashPos = strrchr(rawPath, '/'); - if(slashPos) { - /* chop off the file part if format is dir/file otherwise remove - the trailing slash for dir/dir/ except for absolute path / */ - size_t n = slashPos - rawPath; - if(n == 0) - ++n; - - lstArg = rawPath; - lstArg[n] = '\0'; - } - else - free(rawPath); - } - - cmd = aprintf("%s%s%s", - data->set.str[STRING_CUSTOMREQUEST]? - data->set.str[STRING_CUSTOMREQUEST]: - (data->state.list_only?"NLST":"LIST"), - lstArg? " ": "", - lstArg? lstArg: ""); - free(lstArg); - - if(!cmd) - return CURLE_OUT_OF_MEMORY; - - result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", cmd); - free(cmd); - - if(!result) - state(data, FTP_LIST); - - return result; -} - -static CURLcode ftp_state_retr_prequote(struct Curl_easy *data) -{ - /* We've sent the TYPE, now we must send the list of prequote strings */ - return ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE); -} - -static CURLcode ftp_state_stor_prequote(struct Curl_easy *data) -{ - /* We've sent the TYPE, now we must send the list of prequote strings */ - return ftp_state_quote(data, TRUE, FTP_STOR_PREQUOTE); -} - -static CURLcode ftp_state_type(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct FTP *ftp = data->req.p.ftp; - struct connectdata *conn = data->conn; - struct ftp_conn *ftpc = &conn->proto.ftpc; - - /* If we have selected NOBODY and HEADER, it means that we only want file - information. Which in FTP can't be much more than the file size and - date. */ - if(data->set.opt_no_body && ftpc->file && - ftp_need_type(conn, data->state.prefer_ascii)) { - /* The SIZE command is _not_ RFC 959 specified, and therefore many servers - may not support it! It is however the only way we have to get a file's - size! */ - - ftp->transfer = PPTRANSFER_INFO; - /* this means no actual transfer will be made */ - - /* Some servers return different sizes for different modes, and thus we - must set the proper type before we check the size */ - result = ftp_nb_type(data, conn, data->state.prefer_ascii, FTP_TYPE); - if(result) - return result; - } - else - result = ftp_state_size(data, conn); - - return result; -} - -/* This is called after the CWD commands have been done in the beginning of - the DO phase */ -static CURLcode ftp_state_mdtm(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct ftp_conn *ftpc = &conn->proto.ftpc; - - /* Requested time of file or time-depended transfer? */ - if((data->set.get_filetime || data->set.timecondition) && ftpc->file) { - - /* we have requested to get the modified-time of the file, this is a white - spot as the MDTM is not mentioned in RFC959 */ - result = Curl_pp_sendf(data, &ftpc->pp, "MDTM %s", ftpc->file); - - if(!result) - state(data, FTP_MDTM); - } - else - result = ftp_state_type(data); - - return result; -} - - -/* This is called after the TYPE and possible quote commands have been sent */ -static CURLcode ftp_state_ul_setup(struct Curl_easy *data, - bool sizechecked) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct FTP *ftp = data->req.p.ftp; - struct ftp_conn *ftpc = &conn->proto.ftpc; - bool append = data->set.remote_append; - - if((data->state.resume_from && !sizechecked) || - ((data->state.resume_from > 0) && sizechecked)) { - /* we're about to continue the uploading of a file */ - /* 1. get already existing file's size. We use the SIZE command for this - which may not exist in the server! The SIZE command is not in - RFC959. */ - - /* 2. This used to set REST. But since we can do append, we - don't another ftp command. We just skip the source file - offset and then we APPEND the rest on the file instead */ - - /* 3. pass file-size number of bytes in the source file */ - /* 4. lower the infilesize counter */ - /* => transfer as usual */ - int seekerr = CURL_SEEKFUNC_OK; - - if(data->state.resume_from < 0) { - /* Got no given size to start from, figure it out */ - result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file); - if(!result) - state(data, FTP_STOR_SIZE); - return result; - } - - /* enable append */ - append = TRUE; - - /* Let's read off the proper amount of bytes from the input. */ - if(conn->seek_func) { - Curl_set_in_callback(data, true); - seekerr = conn->seek_func(conn->seek_client, data->state.resume_from, - SEEK_SET); - Curl_set_in_callback(data, false); - } - - if(seekerr != CURL_SEEKFUNC_OK) { - curl_off_t passed = 0; - if(seekerr != CURL_SEEKFUNC_CANTSEEK) { - failf(data, "Could not seek stream"); - return CURLE_FTP_COULDNT_USE_REST; - } - /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */ - do { - size_t readthisamountnow = - (data->state.resume_from - passed > data->set.buffer_size) ? - (size_t)data->set.buffer_size : - curlx_sotouz(data->state.resume_from - passed); - - size_t actuallyread = - data->state.fread_func(data->state.buffer, 1, readthisamountnow, - data->state.in); - - passed += actuallyread; - if((actuallyread == 0) || (actuallyread > readthisamountnow)) { - /* this checks for greater-than only to make sure that the - CURL_READFUNC_ABORT return code still aborts */ - failf(data, "Failed to read data"); - return CURLE_FTP_COULDNT_USE_REST; - } - } while(passed < data->state.resume_from); - } - /* now, decrease the size of the read */ - if(data->state.infilesize>0) { - data->state.infilesize -= data->state.resume_from; - - if(data->state.infilesize <= 0) { - infof(data, "File already completely uploaded"); - - /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); - - /* Set ->transfer so that we won't get any error in - * ftp_done() because we didn't transfer anything! */ - ftp->transfer = PPTRANSFER_NONE; - - state(data, FTP_STOP); - return CURLE_OK; - } - } - /* we've passed, proceed as normal */ - } /* resume_from */ - - result = Curl_pp_sendf(data, &ftpc->pp, append?"APPE %s":"STOR %s", - ftpc->file); - if(!result) - state(data, FTP_STOR); - - return result; -} - -static CURLcode ftp_state_quote(struct Curl_easy *data, - bool init, - ftpstate instate) -{ - CURLcode result = CURLE_OK; - struct FTP *ftp = data->req.p.ftp; - struct connectdata *conn = data->conn; - struct ftp_conn *ftpc = &conn->proto.ftpc; - bool quote = FALSE; - struct curl_slist *item; - - switch(instate) { - case FTP_QUOTE: - default: - item = data->set.quote; - break; - case FTP_RETR_PREQUOTE: - case FTP_STOR_PREQUOTE: - item = data->set.prequote; - break; - case FTP_POSTQUOTE: - item = data->set.postquote; - break; - } - - /* - * This state uses: - * 'count1' to iterate over the commands to send - * 'count2' to store whether to allow commands to fail - */ - - if(init) - ftpc->count1 = 0; - else - ftpc->count1++; - - if(item) { - int i = 0; - - /* Skip count1 items in the linked list */ - while((i< ftpc->count1) && item) { - item = item->next; - i++; - } - if(item) { - char *cmd = item->data; - if(cmd[0] == '*') { - cmd++; - ftpc->count2 = 1; /* the sent command is allowed to fail */ - } - else - ftpc->count2 = 0; /* failure means cancel operation */ - - result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd); - if(result) - return result; - state(data, instate); - quote = TRUE; - } - } - - if(!quote) { - /* No more quote to send, continue to ... */ - switch(instate) { - case FTP_QUOTE: - default: - result = ftp_state_cwd(data, conn); - break; - case FTP_RETR_PREQUOTE: - if(ftp->transfer != PPTRANSFER_BODY) - state(data, FTP_STOP); - else { - if(ftpc->known_filesize != -1) { - Curl_pgrsSetDownloadSize(data, ftpc->known_filesize); - result = ftp_state_retr(data, ftpc->known_filesize); - } - else { - if(data->set.ignorecl || data->state.prefer_ascii) { - /* 'ignorecl' is used to support download of growing files. It - prevents the state machine from requesting the file size from - the server. With an unknown file size the download continues - until the server terminates it, otherwise the client stops if - the received byte count exceeds the reported file size. Set - option CURLOPT_IGNORE_CONTENT_LENGTH to 1 to enable this - behavior. - - In addition: asking for the size for 'TYPE A' transfers is not - constructive since servers don't report the converted size. So - skip it. - */ - result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); - if(!result) - state(data, FTP_RETR); - } - else { - result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file); - if(!result) - state(data, FTP_RETR_SIZE); - } - } - } - break; - case FTP_STOR_PREQUOTE: - result = ftp_state_ul_setup(data, FALSE); - break; - case FTP_POSTQUOTE: - break; - } - } - - return result; -} - -/* called from ftp_state_pasv_resp to switch to PASV in case of EPSV - problems */ -static CURLcode ftp_epsv_disable(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - - if(conn->bits.ipv6 -#ifndef CURL_DISABLE_PROXY - && !(conn->bits.tunnel_proxy || conn->bits.socksproxy) -#endif - ) { - /* We can't disable EPSV when doing IPv6, so this is instead a fail */ - failf(data, "Failed EPSV attempt, exiting"); - return CURLE_WEIRD_SERVER_REPLY; - } - - infof(data, "Failed EPSV attempt. Disabling EPSV"); - /* disable it for next transfer */ - conn->bits.ftp_use_epsv = FALSE; - data->state.errorbuf = FALSE; /* allow error message to get - rewritten */ - result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PASV"); - if(!result) { - conn->proto.ftpc.count1++; - /* remain in/go to the FTP_PASV state */ - state(data, FTP_PASV); - } - return result; -} - - -static char *control_address(struct connectdata *conn) -{ - /* Returns the control connection IP address. - If a proxy tunnel is used, returns the original host name instead, because - the effective control connection address is the proxy address, - not the ftp host. */ -#ifndef CURL_DISABLE_PROXY - if(conn->bits.tunnel_proxy || conn->bits.socksproxy) - return conn->host.name; -#endif - return conn->primary_ip; -} - -static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, - int ftpcode) -{ - struct connectdata *conn = data->conn; - struct ftp_conn *ftpc = &conn->proto.ftpc; - CURLcode result; - struct Curl_dns_entry *addr = NULL; - enum resolve_t rc; - unsigned short connectport; /* the local port connect() should use! */ - char *str = &data->state.buffer[4]; /* start on the first letter */ - - /* if we come here again, make sure the former name is cleared */ - Curl_safefree(ftpc->newhost); - - if((ftpc->count1 == 0) && - (ftpcode == 229)) { - /* positive EPSV response */ - char *ptr = strchr(str, '('); - if(ptr) { - unsigned int num; - char separator[4]; - ptr++; - if(5 == sscanf(ptr, "%c%c%c%u%c", - &separator[0], - &separator[1], - &separator[2], - &num, - &separator[3])) { - const char sep1 = separator[0]; - int i; - - /* The four separators should be identical, or else this is an oddly - formatted reply and we bail out immediately. */ - for(i = 1; i<4; i++) { - if(separator[i] != sep1) { - ptr = NULL; /* set to NULL to signal error */ - break; - } - } - if(num > 0xffff) { - failf(data, "Illegal port number in EPSV reply"); - return CURLE_FTP_WEIRD_PASV_REPLY; - } - if(ptr) { - ftpc->newport = (unsigned short)(num & 0xffff); - ftpc->newhost = strdup(control_address(conn)); - if(!ftpc->newhost) - return CURLE_OUT_OF_MEMORY; - } - } - else - ptr = NULL; - } - if(!ptr) { - failf(data, "Weirdly formatted EPSV reply"); - return CURLE_FTP_WEIRD_PASV_REPLY; - } - } - else if((ftpc->count1 == 1) && - (ftpcode == 227)) { - /* positive PASV response */ - unsigned int ip[4] = {0, 0, 0, 0}; - unsigned int port[2] = {0, 0}; - - /* - * Scan for a sequence of six comma-separated numbers and use them as - * IP+port indicators. - * - * Found reply-strings include: - * "227 Entering Passive Mode (127,0,0,1,4,51)" - * "227 Data transfer will passively listen to 127,0,0,1,4,51" - * "227 Entering passive mode. 127,0,0,1,4,51" - */ - while(*str) { - if(6 == sscanf(str, "%u,%u,%u,%u,%u,%u", - &ip[0], &ip[1], &ip[2], &ip[3], - &port[0], &port[1])) - break; - str++; - } - - if(!*str || (ip[0] > 255) || (ip[1] > 255) || (ip[2] > 255) || - (ip[3] > 255) || (port[0] > 255) || (port[1] > 255) ) { - failf(data, "Couldn't interpret the 227-response"); - return CURLE_FTP_WEIRD_227_FORMAT; - } - - /* we got OK from server */ - if(data->set.ftp_skip_ip) { - /* told to ignore the remotely given IP but instead use the host we used - for the control connection */ - infof(data, "Skip %u.%u.%u.%u for data connection, re-use %s instead", - ip[0], ip[1], ip[2], ip[3], - conn->host.name); - ftpc->newhost = strdup(control_address(conn)); - } - else - ftpc->newhost = aprintf("%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); - - if(!ftpc->newhost) - return CURLE_OUT_OF_MEMORY; - - ftpc->newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff); - } - else if(ftpc->count1 == 0) { - /* EPSV failed, move on to PASV */ - return ftp_epsv_disable(data, conn); - } - else { - failf(data, "Bad PASV/EPSV response: %03d", ftpcode); - return CURLE_FTP_WEIRD_PASV_REPLY; - } - -#ifndef CURL_DISABLE_PROXY - if(conn->bits.proxy) { - /* - * This connection uses a proxy and we need to connect to the proxy again - * here. We don't want to rely on a former host lookup that might've - * expired now, instead we remake the lookup here and now! - */ - const char * const host_name = conn->bits.socksproxy ? - conn->socks_proxy.host.name : conn->http_proxy.host.name; - rc = Curl_resolv(data, host_name, (int)conn->port, FALSE, &addr); - if(rc == CURLRESOLV_PENDING) - /* BLOCKING, ignores the return code but 'addr' will be NULL in - case of failure */ - (void)Curl_resolver_wait_resolv(data, &addr); - - connectport = - (unsigned short)conn->port; /* we connect to the proxy's port */ - - if(!addr) { - failf(data, "Can't resolve proxy host %s:%hu", host_name, connectport); - return CURLE_COULDNT_RESOLVE_PROXY; - } - } - else -#endif - { - /* normal, direct, ftp connection */ - DEBUGASSERT(ftpc->newhost); - - /* postponed address resolution in case of tcp fastopen */ - if(conn->bits.tcp_fastopen && !conn->bits.reuse && !ftpc->newhost[0]) { - Curl_conninfo_remote(data, conn, conn->sock[FIRSTSOCKET]); - Curl_safefree(ftpc->newhost); - ftpc->newhost = strdup(control_address(conn)); - if(!ftpc->newhost) - return CURLE_OUT_OF_MEMORY; - } - - rc = Curl_resolv(data, ftpc->newhost, ftpc->newport, FALSE, &addr); - if(rc == CURLRESOLV_PENDING) - /* BLOCKING */ - (void)Curl_resolver_wait_resolv(data, &addr); - - connectport = ftpc->newport; /* we connect to the remote port */ - - if(!addr) { - failf(data, "Can't resolve new host %s:%hu", ftpc->newhost, connectport); - return CURLE_FTP_CANT_GET_HOST; - } - } - - conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE; - result = Curl_connecthost(data, conn, addr); - - if(result) { - Curl_resolv_unlock(data, addr); /* we're done using this address */ - if(ftpc->count1 == 0 && ftpcode == 229) - return ftp_epsv_disable(data, conn); - - return result; - } - - - /* - * When this is used from the multi interface, this might've returned with - * the 'connected' set to FALSE and thus we are now awaiting a non-blocking - * connect to connect. - */ - - if(data->set.verbose) - /* this just dumps information about this second connection */ - ftp_pasv_verbose(data, addr->addr, ftpc->newhost, connectport); - - Curl_resolv_unlock(data, addr); /* we're done using this address */ - - Curl_safefree(conn->secondaryhostname); - conn->secondary_port = ftpc->newport; - conn->secondaryhostname = strdup(ftpc->newhost); - if(!conn->secondaryhostname) - return CURLE_OUT_OF_MEMORY; - - conn->bits.do_more = TRUE; - state(data, FTP_STOP); /* this phase is completed */ - - return result; -} - -static CURLcode ftp_state_port_resp(struct Curl_easy *data, - int ftpcode) -{ - struct connectdata *conn = data->conn; - struct ftp_conn *ftpc = &conn->proto.ftpc; - ftpport fcmd = (ftpport)ftpc->count1; - CURLcode result = CURLE_OK; - - /* The FTP spec tells a positive response should have code 200. - Be more permissive here to tolerate deviant servers. */ - if(ftpcode / 100 != 2) { - /* the command failed */ - - if(EPRT == fcmd) { - infof(data, "disabling EPRT usage"); - conn->bits.ftp_use_eprt = FALSE; - } - fcmd++; - - if(fcmd == DONE) { - failf(data, "Failed to do PORT"); - result = CURLE_FTP_PORT_FAILED; - } - else - /* try next */ - result = ftp_state_use_port(data, fcmd); - } - else { - infof(data, "Connect data stream actively"); - state(data, FTP_STOP); /* end of DO phase */ - result = ftp_dophase_done(data, FALSE); - } - - return result; -} - -static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data, - int ftpcode) -{ - CURLcode result = CURLE_OK; - struct FTP *ftp = data->req.p.ftp; - struct connectdata *conn = data->conn; - struct ftp_conn *ftpc = &conn->proto.ftpc; - - switch(ftpcode) { - case 213: - { - /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the - last .sss part is optional and means fractions of a second */ - int year, month, day, hour, minute, second; - if(6 == sscanf(&data->state.buffer[4], "%04d%02d%02d%02d%02d%02d", - &year, &month, &day, &hour, &minute, &second)) { - /* we have a time, reformat it */ - char timebuf[24]; - msnprintf(timebuf, sizeof(timebuf), - "%04d%02d%02d %02d:%02d:%02d GMT", - year, month, day, hour, minute, second); - /* now, convert this into a time() value: */ - data->info.filetime = Curl_getdate_capped(timebuf); - } - -#ifdef CURL_FTP_HTTPSTYLE_HEAD - /* If we asked for a time of the file and we actually got one as well, - we "emulate" a HTTP-style header in our output. */ - - if(data->set.opt_no_body && - ftpc->file && - data->set.get_filetime && - (data->info.filetime >= 0) ) { - char headerbuf[128]; - int headerbuflen; - time_t filetime = data->info.filetime; - struct tm buffer; - const struct tm *tm = &buffer; - - result = Curl_gmtime(filetime, &buffer); - if(result) - return result; - - /* format: "Tue, 15 Nov 1994 12:45:26" */ - headerbuflen = msnprintf(headerbuf, sizeof(headerbuf), - "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n", - Curl_wkday[tm->tm_wday?tm->tm_wday-1:6], - tm->tm_mday, - Curl_month[tm->tm_mon], - tm->tm_year + 1900, - tm->tm_hour, - tm->tm_min, - tm->tm_sec); - result = Curl_client_write(data, CLIENTWRITE_BOTH, headerbuf, - headerbuflen); - if(result) - return result; - } /* end of a ridiculous amount of conditionals */ -#endif - } - break; - default: - infof(data, "unsupported MDTM reply format"); - break; - case 550: /* 550 is used for several different problems, e.g. - "No such file or directory" or "Permission denied". - It does not mean that the file does not exist at all. */ - infof(data, "MDTM failed: file does not exist or permission problem," - " continuing"); - break; - } - - if(data->set.timecondition) { - if((data->info.filetime > 0) && (data->set.timevalue > 0)) { - switch(data->set.timecondition) { - case CURL_TIMECOND_IFMODSINCE: - default: - if(data->info.filetime <= data->set.timevalue) { - infof(data, "The requested document is not new enough"); - ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */ - data->info.timecond = TRUE; - state(data, FTP_STOP); - return CURLE_OK; - } - break; - case CURL_TIMECOND_IFUNMODSINCE: - if(data->info.filetime > data->set.timevalue) { - infof(data, "The requested document is not old enough"); - ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */ - data->info.timecond = TRUE; - state(data, FTP_STOP); - return CURLE_OK; - } - break; - } /* switch */ - } - else { - infof(data, "Skipping time comparison"); - } - } - - if(!result) - result = ftp_state_type(data); - - return result; -} - -static CURLcode ftp_state_type_resp(struct Curl_easy *data, - int ftpcode, - ftpstate instate) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - - if(ftpcode/100 != 2) { - /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a - successful 'TYPE I'. While that is not as RFC959 says, it is still a - positive response code and we allow that. */ - failf(data, "Couldn't set desired mode"); - return CURLE_FTP_COULDNT_SET_TYPE; - } - if(ftpcode != 200) - infof(data, "Got a %03d response code instead of the assumed 200", - ftpcode); - - if(instate == FTP_TYPE) - result = ftp_state_size(data, conn); - else if(instate == FTP_LIST_TYPE) - result = ftp_state_list(data); - else if(instate == FTP_RETR_TYPE) - result = ftp_state_retr_prequote(data); - else if(instate == FTP_STOR_TYPE) - result = ftp_state_stor_prequote(data); - - return result; -} - -static CURLcode ftp_state_retr(struct Curl_easy *data, - curl_off_t filesize) -{ - CURLcode result = CURLE_OK; - struct FTP *ftp = data->req.p.ftp; - struct connectdata *conn = data->conn; - struct ftp_conn *ftpc = &conn->proto.ftpc; - - if(data->set.max_filesize && (filesize > data->set.max_filesize)) { - failf(data, "Maximum file size exceeded"); - return CURLE_FILESIZE_EXCEEDED; - } - ftp->downloadsize = filesize; - - if(data->state.resume_from) { - /* We always (attempt to) get the size of downloads, so it is done before - this even when not doing resumes. */ - if(filesize == -1) { - infof(data, "ftp server doesn't support SIZE"); - /* We couldn't get the size and therefore we can't know if there really - is a part of the file left to get, although the server will just - close the connection when we start the connection so it won't cause - us any harm, just not make us exit as nicely. */ - } - else { - /* We got a file size report, so we check that there actually is a - part of the file left to get, or else we go home. */ - if(data->state.resume_from< 0) { - /* We're supposed to download the last abs(from) bytes */ - if(filesize < -data->state.resume_from) { - failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T - ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")", - data->state.resume_from, filesize); - return CURLE_BAD_DOWNLOAD_RESUME; - } - /* convert to size to download */ - ftp->downloadsize = -data->state.resume_from; - /* download from where? */ - data->state.resume_from = filesize - ftp->downloadsize; - } - else { - if(filesize < data->state.resume_from) { - failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T - ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")", - data->state.resume_from, filesize); - return CURLE_BAD_DOWNLOAD_RESUME; - } - /* Now store the number of bytes we are expected to download */ - ftp->downloadsize = filesize-data->state.resume_from; - } - } - - if(ftp->downloadsize == 0) { - /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); - infof(data, "File already completely downloaded"); - - /* Set ->transfer so that we won't get any error in ftp_done() - * because we didn't transfer the any file */ - ftp->transfer = PPTRANSFER_NONE; - state(data, FTP_STOP); - return CURLE_OK; - } - - /* Set resume file transfer offset */ - infof(data, "Instructs server to resume from offset %" - CURL_FORMAT_CURL_OFF_T, data->state.resume_from); - - result = Curl_pp_sendf(data, &ftpc->pp, "REST %" CURL_FORMAT_CURL_OFF_T, - data->state.resume_from); - if(!result) - state(data, FTP_RETR_REST); - } - else { - /* no resume */ - result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); - if(!result) - state(data, FTP_RETR); - } - - return result; -} - -static CURLcode ftp_state_size_resp(struct Curl_easy *data, - int ftpcode, - ftpstate instate) -{ - CURLcode result = CURLE_OK; - curl_off_t filesize = -1; - char *buf = data->state.buffer; - - /* get the size from the ascii string: */ - if(ftpcode == 213) { - /* To allow servers to prepend "rubbish" in the response string, we scan - for all the digits at the end of the response and parse only those as a - number. */ - char *start = &buf[4]; - char *fdigit = strchr(start, '\r'); - if(fdigit) { - do - fdigit--; - while(ISDIGIT(*fdigit) && (fdigit > start)); - if(!ISDIGIT(*fdigit)) - fdigit++; - } - else - fdigit = start; - /* ignores parsing errors, which will make the size remain unknown */ - (void)curlx_strtoofft(fdigit, NULL, 0, &filesize); - - } - else if(ftpcode == 550) { /* "No such file or directory" */ - /* allow a SIZE failure for (resumed) uploads, when probing what command - to use */ - if(instate != FTP_STOR_SIZE) { - failf(data, "The file does not exist"); - return CURLE_REMOTE_FILE_NOT_FOUND; - } - } - - if(instate == FTP_SIZE) { -#ifdef CURL_FTP_HTTPSTYLE_HEAD - if(-1 != filesize) { - char clbuf[128]; - int clbuflen = msnprintf(clbuf, sizeof(clbuf), - "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", filesize); - result = Curl_client_write(data, CLIENTWRITE_BOTH, clbuf, clbuflen); - if(result) - return result; - } -#endif - Curl_pgrsSetDownloadSize(data, filesize); - result = ftp_state_rest(data, data->conn); - } - else if(instate == FTP_RETR_SIZE) { - Curl_pgrsSetDownloadSize(data, filesize); - result = ftp_state_retr(data, filesize); - } - else if(instate == FTP_STOR_SIZE) { - data->state.resume_from = filesize; - result = ftp_state_ul_setup(data, TRUE); - } - - return result; -} - -static CURLcode ftp_state_rest_resp(struct Curl_easy *data, - struct connectdata *conn, - int ftpcode, - ftpstate instate) -{ - CURLcode result = CURLE_OK; - struct ftp_conn *ftpc = &conn->proto.ftpc; - - switch(instate) { - case FTP_REST: - default: -#ifdef CURL_FTP_HTTPSTYLE_HEAD - if(ftpcode == 350) { - char buffer[24]= { "Accept-ranges: bytes\r\n" }; - result = Curl_client_write(data, CLIENTWRITE_BOTH, buffer, - strlen(buffer)); - if(result) - return result; - } -#endif - result = ftp_state_prepare_transfer(data); - break; - - case FTP_RETR_REST: - if(ftpcode != 350) { - failf(data, "Couldn't use REST"); - result = CURLE_FTP_COULDNT_USE_REST; - } - else { - result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); - if(!result) - state(data, FTP_RETR); - } - break; - } - - return result; -} - -static CURLcode ftp_state_stor_resp(struct Curl_easy *data, - int ftpcode, ftpstate instate) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - - if(ftpcode >= 400) { - failf(data, "Failed FTP upload: %0d", ftpcode); - state(data, FTP_STOP); - /* oops, we never close the sockets! */ - return CURLE_UPLOAD_FAILED; - } - - conn->proto.ftpc.state_saved = instate; - - /* PORT means we are now awaiting the server to connect to us. */ - if(data->set.ftp_use_port) { - bool connected; - - state(data, FTP_STOP); /* no longer in STOR state */ - - result = AllowServerConnect(data, &connected); - if(result) - return result; - - if(!connected) { - struct ftp_conn *ftpc = &conn->proto.ftpc; - infof(data, "Data conn was not available immediately"); - ftpc->wait_data_conn = TRUE; - } - - return CURLE_OK; - } - return InitiateTransfer(data); -} - -/* for LIST and RETR responses */ -static CURLcode ftp_state_get_resp(struct Curl_easy *data, - int ftpcode, - ftpstate instate) -{ - CURLcode result = CURLE_OK; - struct FTP *ftp = data->req.p.ftp; - struct connectdata *conn = data->conn; - - if((ftpcode == 150) || (ftpcode == 125)) { - - /* - A; - 150 Opening BINARY mode data connection for /etc/passwd (2241 - bytes). (ok, the file is being transferred) - - B: - 150 Opening ASCII mode data connection for /bin/ls - - C: - 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes). - - D: - 150 Opening ASCII mode data connection for [file] (0.0.0.0,0) (545 bytes) - - E: - 125 Data connection already open; Transfer starting. */ - - curl_off_t size = -1; /* default unknown size */ - - - /* - * It appears that there are FTP-servers that return size 0 for files when - * SIZE is used on the file while being in BINARY mode. To work around - * that (stupid) behavior, we attempt to parse the RETR response even if - * the SIZE returned size zero. - * - * Debugging help from Salvatore Sorrentino on February 26, 2003. - */ - - if((instate != FTP_LIST) && - !data->state.prefer_ascii && - (ftp->downloadsize < 1)) { - /* - * It seems directory listings either don't show the size or very - * often uses size 0 anyway. ASCII transfers may very well turn out - * that the transferred amount of data is not the same as this line - * tells, why using this number in those cases only confuses us. - * - * Example D above makes this parsing a little tricky */ - char *bytes; - char *buf = data->state.buffer; - bytes = strstr(buf, " bytes"); - if(bytes) { - long in = (long)(--bytes-buf); - /* this is a hint there is size information in there! ;-) */ - while(--in) { - /* scan for the left parenthesis and break there */ - if('(' == *bytes) - break; - /* skip only digits */ - if(!ISDIGIT(*bytes)) { - bytes = NULL; - break; - } - /* one more estep backwards */ - bytes--; - } - /* if we have nothing but digits: */ - if(bytes) { - ++bytes; - /* get the number! */ - (void)curlx_strtoofft(bytes, NULL, 0, &size); - } - } - } - else if(ftp->downloadsize > -1) - size = ftp->downloadsize; - - if(size > data->req.maxdownload && data->req.maxdownload > 0) - size = data->req.size = data->req.maxdownload; - else if((instate != FTP_LIST) && (data->state.prefer_ascii)) - size = -1; /* kludge for servers that understate ASCII mode file size */ - - infof(data, "Maxdownload = %" CURL_FORMAT_CURL_OFF_T, - data->req.maxdownload); - - if(instate != FTP_LIST) - infof(data, "Getting file with size: %" CURL_FORMAT_CURL_OFF_T, - size); - - /* FTP download: */ - conn->proto.ftpc.state_saved = instate; - conn->proto.ftpc.retr_size_saved = size; - - if(data->set.ftp_use_port) { - bool connected; - - result = AllowServerConnect(data, &connected); - if(result) - return result; - - if(!connected) { - struct ftp_conn *ftpc = &conn->proto.ftpc; - infof(data, "Data conn was not available immediately"); - state(data, FTP_STOP); - ftpc->wait_data_conn = TRUE; - } - } - else - return InitiateTransfer(data); - } - else { - if((instate == FTP_LIST) && (ftpcode == 450)) { - /* simply no matching files in the dir listing */ - ftp->transfer = PPTRANSFER_NONE; /* don't download anything */ - state(data, FTP_STOP); /* this phase is over */ - } - else { - failf(data, "RETR response: %03d", ftpcode); - return instate == FTP_RETR && ftpcode == 550? - CURLE_REMOTE_FILE_NOT_FOUND: - CURLE_FTP_COULDNT_RETR_FILE; - } - } - - return result; -} - -/* after USER, PASS and ACCT */ -static CURLcode ftp_state_loggedin(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - - if(conn->bits.ftp_use_control_ssl) { - /* PBSZ = PROTECTION BUFFER SIZE. - - The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says: - - Specifically, the PROT command MUST be preceded by a PBSZ - command and a PBSZ command MUST be preceded by a successful - security data exchange (the TLS negotiation in this case) - - ... (and on page 8): - - Thus the PBSZ command must still be issued, but must have a - parameter of '0' to indicate that no buffering is taking place - and the data connection should not be encapsulated. - */ - result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "PBSZ %d", 0); - if(!result) - state(data, FTP_PBSZ); - } - else { - result = ftp_state_pwd(data, conn); - } - return result; -} - -/* for USER and PASS responses */ -static CURLcode ftp_state_user_resp(struct Curl_easy *data, - int ftpcode, - ftpstate instate) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct ftp_conn *ftpc = &conn->proto.ftpc; - (void)instate; /* no use for this yet */ - - /* some need password anyway, and others just return 2xx ignored */ - if((ftpcode == 331) && (ftpc->state == FTP_USER)) { - /* 331 Password required for ... - (the server requires to send the user's password too) */ - result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s", - conn->passwd?conn->passwd:""); - if(!result) - state(data, FTP_PASS); - } - else if(ftpcode/100 == 2) { - /* 230 User ... logged in. - (the user logged in with or without password) */ - result = ftp_state_loggedin(data); - } - else if(ftpcode == 332) { - if(data->set.str[STRING_FTP_ACCOUNT]) { - result = Curl_pp_sendf(data, &ftpc->pp, "ACCT %s", - data->set.str[STRING_FTP_ACCOUNT]); - if(!result) - state(data, FTP_ACCT); - } - else { - failf(data, "ACCT requested but none available"); - result = CURLE_LOGIN_DENIED; - } - } - else { - /* All other response codes, like: - - 530 User ... access denied - (the server denies to log the specified user) */ - - if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] && - !ftpc->ftp_trying_alternative) { - /* Ok, USER failed. Let's try the supplied command. */ - result = - Curl_pp_sendf(data, &ftpc->pp, "%s", - data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]); - if(!result) { - ftpc->ftp_trying_alternative = TRUE; - state(data, FTP_USER); - } - } - else { - failf(data, "Access denied: %03d", ftpcode); - result = CURLE_LOGIN_DENIED; - } - } - return result; -} - -/* for ACCT response */ -static CURLcode ftp_state_acct_resp(struct Curl_easy *data, - int ftpcode) -{ - CURLcode result = CURLE_OK; - if(ftpcode != 230) { - failf(data, "ACCT rejected by server: %03d", ftpcode); - result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */ - } - else - result = ftp_state_loggedin(data); - - return result; -} - - -static CURLcode ftp_statemachine(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; - int ftpcode; - struct ftp_conn *ftpc = &conn->proto.ftpc; - struct pingpong *pp = &ftpc->pp; - static const char ftpauth[][4] = { "SSL", "TLS" }; - size_t nread = 0; - - if(pp->sendleft) - return Curl_pp_flushsend(data, pp); - - result = ftp_readresp(data, sock, pp, &ftpcode, &nread); - if(result) - return result; - - if(ftpcode) { - /* we have now received a full FTP server response */ - switch(ftpc->state) { - case FTP_WAIT220: - if(ftpcode == 230) { - /* 230 User logged in - already! Take as 220 if TLS required. */ - if(data->set.use_ssl <= CURLUSESSL_TRY || - conn->bits.ftp_use_control_ssl) - return ftp_state_user_resp(data, ftpcode, ftpc->state); - } - else if(ftpcode != 220) { - failf(data, "Got a %03d ftp-server response when 220 was expected", - ftpcode); - return CURLE_WEIRD_SERVER_REPLY; - } - - /* We have received a 220 response fine, now we proceed. */ -#ifdef HAVE_GSSAPI - if(data->set.krb) { - /* If not anonymous login, try a secure login. Note that this - procedure is still BLOCKING. */ - - Curl_sec_request_prot(conn, "private"); - /* We set private first as default, in case the line below fails to - set a valid level */ - Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]); - - if(Curl_sec_login(data, conn)) { - failf(data, "secure login failed"); - return CURLE_WEIRD_SERVER_REPLY; - } - infof(data, "Authentication successful"); - } -#endif - - if(data->set.use_ssl && !conn->bits.ftp_use_control_ssl) { - /* We don't have a SSL/TLS control connection yet, but FTPS is - requested. Try a FTPS connection now */ - - ftpc->count3 = 0; - switch(data->set.ftpsslauth) { - case CURLFTPAUTH_DEFAULT: - case CURLFTPAUTH_SSL: - ftpc->count2 = 1; /* add one to get next */ - ftpc->count1 = 0; - break; - case CURLFTPAUTH_TLS: - ftpc->count2 = -1; /* subtract one to get next */ - ftpc->count1 = 1; - break; - default: - failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d", - (int)data->set.ftpsslauth); - return CURLE_UNKNOWN_OPTION; /* we don't know what to do */ - } - result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s", - ftpauth[ftpc->count1]); - if(!result) - state(data, FTP_AUTH); - } - else - result = ftp_state_user(data, conn); - break; - - case FTP_AUTH: - /* we have gotten the response to a previous AUTH command */ - - if(pp->cache_size) - return CURLE_WEIRD_SERVER_REPLY; /* Forbid pipelining in response. */ - - /* RFC2228 (page 5) says: - * - * If the server is willing to accept the named security mechanism, - * and does not require any security data, it must respond with - * reply code 234/334. - */ - - if((ftpcode == 234) || (ftpcode == 334)) { - /* Curl_ssl_connect is BLOCKING */ - result = Curl_ssl_connect(data, conn, FIRSTSOCKET); - if(!result) { - conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */ - conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */ - result = ftp_state_user(data, conn); - } - } - else if(ftpc->count3 < 1) { - ftpc->count3++; - ftpc->count1 += ftpc->count2; /* get next attempt */ - result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s", - ftpauth[ftpc->count1]); - /* remain in this same state */ - } - else { - if(data->set.use_ssl > CURLUSESSL_TRY) - /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */ - result = CURLE_USE_SSL_FAILED; - else - /* ignore the failure and continue */ - result = ftp_state_user(data, conn); - } - break; - - case FTP_USER: - case FTP_PASS: - result = ftp_state_user_resp(data, ftpcode, ftpc->state); - break; - - case FTP_ACCT: - result = ftp_state_acct_resp(data, ftpcode); - break; - - case FTP_PBSZ: - result = - Curl_pp_sendf(data, &ftpc->pp, "PROT %c", - data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P'); - if(!result) - state(data, FTP_PROT); - break; - - case FTP_PROT: - if(ftpcode/100 == 2) - /* We have enabled SSL for the data connection! */ - conn->bits.ftp_use_data_ssl = - (data->set.use_ssl != CURLUSESSL_CONTROL) ? TRUE : FALSE; - /* FTP servers typically responds with 500 if they decide to reject - our 'P' request */ - else if(data->set.use_ssl > CURLUSESSL_CONTROL) - /* we failed and bails out */ - return CURLE_USE_SSL_FAILED; - - if(data->set.ftp_ccc) { - /* CCC - Clear Command Channel - */ - result = Curl_pp_sendf(data, &ftpc->pp, "%s", "CCC"); - if(!result) - state(data, FTP_CCC); - } - else - result = ftp_state_pwd(data, conn); - break; - - case FTP_CCC: - if(ftpcode < 500) { - /* First shut down the SSL layer (note: this call will block) */ - result = Curl_ssl_shutdown(data, conn, FIRSTSOCKET); - - if(result) - failf(data, "Failed to clear the command channel (CCC)"); - } - if(!result) - /* Then continue as normal */ - result = ftp_state_pwd(data, conn); - break; - - case FTP_PWD: - if(ftpcode == 257) { - char *ptr = &data->state.buffer[4]; /* start on the first letter */ - const size_t buf_size = data->set.buffer_size; - char *dir; - bool entry_extracted = FALSE; - - dir = malloc(nread + 1); - if(!dir) - return CURLE_OUT_OF_MEMORY; - - /* Reply format is like - 257<space>[rubbish]"<directory-name>"<space><commentary> and the - RFC959 says - - The directory name can contain any character; embedded - double-quotes should be escaped by double-quotes (the - "quote-doubling" convention). - */ - - /* scan for the first double-quote for non-standard responses */ - while(ptr < &data->state.buffer[buf_size] - && *ptr != '\n' && *ptr != '\0' && *ptr != '"') - ptr++; - - if('\"' == *ptr) { - /* it started good */ - char *store; - ptr++; - for(store = dir; *ptr;) { - if('\"' == *ptr) { - if('\"' == ptr[1]) { - /* "quote-doubling" */ - *store = ptr[1]; - ptr++; - } - else { - /* end of path */ - entry_extracted = TRUE; - break; /* get out of this loop */ - } - } - else - *store = *ptr; - store++; - ptr++; - } - *store = '\0'; /* null-terminate */ - } - if(entry_extracted) { - /* If the path name does not look like an absolute path (i.e.: it - does not start with a '/'), we probably need some server-dependent - adjustments. For example, this is the case when connecting to - an OS400 FTP server: this server supports two name syntaxes, - the default one being incompatible with standard paths. In - addition, this server switches automatically to the regular path - syntax when one is encountered in a command: this results in - having an entrypath in the wrong syntax when later used in CWD. - The method used here is to check the server OS: we do it only - if the path name looks strange to minimize overhead on other - systems. */ - - if(!ftpc->server_os && dir[0] != '/') { - result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SYST"); - if(result) { - free(dir); - return result; - } - Curl_safefree(ftpc->entrypath); - ftpc->entrypath = dir; /* remember this */ - infof(data, "Entry path is '%s'", ftpc->entrypath); - /* also save it where getinfo can access it: */ - data->state.most_recent_ftp_entrypath = ftpc->entrypath; - state(data, FTP_SYST); - break; - } - - Curl_safefree(ftpc->entrypath); - ftpc->entrypath = dir; /* remember this */ - infof(data, "Entry path is '%s'", ftpc->entrypath); - /* also save it where getinfo can access it: */ - data->state.most_recent_ftp_entrypath = ftpc->entrypath; - } - else { - /* couldn't get the path */ - free(dir); - infof(data, "Failed to figure out path"); - } - } - state(data, FTP_STOP); /* we are done with the CONNECT phase! */ - DEBUGF(infof(data, "protocol connect phase DONE")); - break; - - case FTP_SYST: - if(ftpcode == 215) { - char *ptr = &data->state.buffer[4]; /* start on the first letter */ - char *os; - char *store; - - os = malloc(nread + 1); - if(!os) - return CURLE_OUT_OF_MEMORY; - - /* Reply format is like - 215<space><OS-name><space><commentary> - */ - while(*ptr == ' ') - ptr++; - for(store = os; *ptr && *ptr != ' ';) - *store++ = *ptr++; - *store = '\0'; /* null-terminate */ - - /* Check for special servers here. */ - - if(strcasecompare(os, "OS/400")) { - /* Force OS400 name format 1. */ - result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SITE NAMEFMT 1"); - if(result) { - free(os); - return result; - } - /* remember target server OS */ - Curl_safefree(ftpc->server_os); - ftpc->server_os = os; - state(data, FTP_NAMEFMT); - break; - } - /* Nothing special for the target server. */ - /* remember target server OS */ - Curl_safefree(ftpc->server_os); - ftpc->server_os = os; - } - else { - /* Cannot identify server OS. Continue anyway and cross fingers. */ - } - - state(data, FTP_STOP); /* we are done with the CONNECT phase! */ - DEBUGF(infof(data, "protocol connect phase DONE")); - break; - - case FTP_NAMEFMT: - if(ftpcode == 250) { - /* Name format change successful: reload initial path. */ - ftp_state_pwd(data, conn); - break; - } - - state(data, FTP_STOP); /* we are done with the CONNECT phase! */ - DEBUGF(infof(data, "protocol connect phase DONE")); - break; - - case FTP_QUOTE: - case FTP_POSTQUOTE: - case FTP_RETR_PREQUOTE: - case FTP_STOR_PREQUOTE: - if((ftpcode >= 400) && !ftpc->count2) { - /* failure response code, and not allowed to fail */ - failf(data, "QUOT command failed with %03d", ftpcode); - result = CURLE_QUOTE_ERROR; - } - else - result = ftp_state_quote(data, FALSE, ftpc->state); - break; - - case FTP_CWD: - if(ftpcode/100 != 2) { - /* failure to CWD there */ - if(data->set.ftp_create_missing_dirs && - ftpc->cwdcount && !ftpc->count2) { - /* try making it */ - ftpc->count2++; /* counter to prevent CWD-MKD loops */ - - /* count3 is set to allow MKD to fail once per dir. In the case when - CWD fails and then MKD fails (due to another session raced it to - create the dir) this then allows for a second try to CWD to it. */ - ftpc->count3 = (data->set.ftp_create_missing_dirs == 2) ? 1 : 0; - - result = Curl_pp_sendf(data, &ftpc->pp, "MKD %s", - ftpc->dirs[ftpc->cwdcount - 1]); - if(!result) - state(data, FTP_MKD); - } - else { - /* return failure */ - failf(data, "Server denied you to change to the given directory"); - ftpc->cwdfail = TRUE; /* don't remember this path as we failed - to enter it */ - result = CURLE_REMOTE_ACCESS_DENIED; - } - } - else { - /* success */ - ftpc->count2 = 0; - if(++ftpc->cwdcount <= ftpc->dirdepth) - /* send next CWD */ - result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", - ftpc->dirs[ftpc->cwdcount - 1]); - else - result = ftp_state_mdtm(data); - } - break; - - case FTP_MKD: - if((ftpcode/100 != 2) && !ftpc->count3--) { - /* failure to MKD the dir */ - failf(data, "Failed to MKD dir: %03d", ftpcode); - result = CURLE_REMOTE_ACCESS_DENIED; - } - else { - state(data, FTP_CWD); - /* send CWD */ - result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", - ftpc->dirs[ftpc->cwdcount - 1]); - } - break; - - case FTP_MDTM: - result = ftp_state_mdtm_resp(data, ftpcode); - break; - - case FTP_TYPE: - case FTP_LIST_TYPE: - case FTP_RETR_TYPE: - case FTP_STOR_TYPE: - result = ftp_state_type_resp(data, ftpcode, ftpc->state); - break; - - case FTP_SIZE: - case FTP_RETR_SIZE: - case FTP_STOR_SIZE: - result = ftp_state_size_resp(data, ftpcode, ftpc->state); - break; - - case FTP_REST: - case FTP_RETR_REST: - result = ftp_state_rest_resp(data, conn, ftpcode, ftpc->state); - break; - - case FTP_PRET: - if(ftpcode != 200) { - /* there only is this one standard OK return code. */ - failf(data, "PRET command not accepted: %03d", ftpcode); - return CURLE_FTP_PRET_FAILED; - } - result = ftp_state_use_pasv(data, conn); - break; - - case FTP_PASV: - result = ftp_state_pasv_resp(data, ftpcode); - break; - - case FTP_PORT: - result = ftp_state_port_resp(data, ftpcode); - break; - - case FTP_LIST: - case FTP_RETR: - result = ftp_state_get_resp(data, ftpcode, ftpc->state); - break; - - case FTP_STOR: - result = ftp_state_stor_resp(data, ftpcode, ftpc->state); - break; - - case FTP_QUIT: - /* fallthrough, just stop! */ - default: - /* internal error */ - state(data, FTP_STOP); - break; - } - } /* if(ftpcode) */ - - return result; -} - - -/* called repeatedly until done from multi.c */ -static CURLcode ftp_multi_statemach(struct Curl_easy *data, - bool *done) -{ - struct connectdata *conn = data->conn; - struct ftp_conn *ftpc = &conn->proto.ftpc; - CURLcode result = Curl_pp_statemach(data, &ftpc->pp, FALSE, FALSE); - - /* Check for the state outside of the Curl_socket_check() return code checks - since at times we are in fact already in this state when this function - gets called. */ - *done = (ftpc->state == FTP_STOP) ? TRUE : FALSE; - - return result; -} - -static CURLcode ftp_block_statemach(struct Curl_easy *data, - struct connectdata *conn) -{ - struct ftp_conn *ftpc = &conn->proto.ftpc; - struct pingpong *pp = &ftpc->pp; - CURLcode result = CURLE_OK; - - while(ftpc->state != FTP_STOP) { - result = Curl_pp_statemach(data, pp, TRUE, TRUE /* disconnecting */); - if(result) - break; - } - - return result; -} - -/* - * ftp_connect() should do everything that is to be considered a part of - * the connection phase. - * - * The variable 'done' points to will be TRUE if the protocol-layer connect - * phase is done when this function returns, or FALSE if not. - * - */ -static CURLcode ftp_connect(struct Curl_easy *data, - bool *done) /* see description above */ -{ - CURLcode result; - struct connectdata *conn = data->conn; - struct ftp_conn *ftpc = &conn->proto.ftpc; - struct pingpong *pp = &ftpc->pp; - - *done = FALSE; /* default to not done yet */ - - /* We always support persistent connections on ftp */ - connkeep(conn, "FTP default"); - - PINGPONG_SETUP(pp, ftp_statemachine, ftp_endofresp); - - if(conn->handler->flags & PROTOPT_SSL) { - /* BLOCKING */ - result = Curl_ssl_connect(data, conn, FIRSTSOCKET); - if(result) - return result; - conn->bits.ftp_use_control_ssl = TRUE; - } - - Curl_pp_setup(pp); /* once per transfer */ - Curl_pp_init(data, pp); /* init the generic pingpong data */ - - /* When we connect, we start in the state where we await the 220 - response */ - state(data, FTP_WAIT220); - - result = ftp_multi_statemach(data, done); - - return result; -} - -/*********************************************************************** - * - * ftp_done() - * - * The DONE function. This does what needs to be done after a single DO has - * performed. - * - * Input argument is already checked for validity. - */ -static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, - bool premature) -{ - struct connectdata *conn = data->conn; - struct FTP *ftp = data->req.p.ftp; - struct ftp_conn *ftpc = &conn->proto.ftpc; - struct pingpong *pp = &ftpc->pp; - ssize_t nread; - int ftpcode; - CURLcode result = CURLE_OK; - char *rawPath = NULL; - size_t pathLen = 0; - - if(!ftp) - return CURLE_OK; - - switch(status) { - case CURLE_BAD_DOWNLOAD_RESUME: - case CURLE_FTP_WEIRD_PASV_REPLY: - case CURLE_FTP_PORT_FAILED: - case CURLE_FTP_ACCEPT_FAILED: - case CURLE_FTP_ACCEPT_TIMEOUT: - case CURLE_FTP_COULDNT_SET_TYPE: - case CURLE_FTP_COULDNT_RETR_FILE: - case CURLE_PARTIAL_FILE: - case CURLE_UPLOAD_FAILED: - case CURLE_REMOTE_ACCESS_DENIED: - case CURLE_FILESIZE_EXCEEDED: - case CURLE_REMOTE_FILE_NOT_FOUND: - case CURLE_WRITE_ERROR: - /* the connection stays alive fine even though this happened */ - /* fall-through */ - case CURLE_OK: /* doesn't affect the control connection's status */ - if(!premature) - break; - - /* until we cope better with prematurely ended requests, let them - * fallback as if in complete failure */ - /* FALLTHROUGH */ - default: /* by default, an error means the control connection is - wedged and should not be used anymore */ - ftpc->ctl_valid = FALSE; - ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the - current path, as this connection is going */ - connclose(conn, "FTP ended with bad error code"); - result = status; /* use the already set error code */ - break; - } - - if(data->state.wildcardmatch) { - if(data->set.chunk_end && ftpc->file) { - Curl_set_in_callback(data, true); - data->set.chunk_end(data->wildcard.customptr); - Curl_set_in_callback(data, false); - } - ftpc->known_filesize = -1; - } - - if(!result) - /* get the url-decoded "raw" path */ - result = Curl_urldecode(ftp->path, 0, &rawPath, &pathLen, - REJECT_CTRL); - if(result) { - /* We can limp along anyway (and should try to since we may already be in - * the error path) */ - ftpc->ctl_valid = FALSE; /* mark control connection as bad */ - connclose(conn, "FTP: out of memory!"); /* mark for connection closure */ - free(ftpc->prevpath); - ftpc->prevpath = NULL; /* no path remembering */ - } - else { /* remember working directory for connection reuse */ - if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/')) - free(rawPath); /* full path => no CWDs happened => keep ftpc->prevpath */ - else { - free(ftpc->prevpath); - - if(!ftpc->cwdfail) { - if(data->set.ftp_filemethod == FTPFILE_NOCWD) - pathLen = 0; /* relative path => working directory is FTP home */ - else - pathLen -= ftpc->file?strlen(ftpc->file):0; /* file is url-decoded */ - - rawPath[pathLen] = '\0'; - ftpc->prevpath = rawPath; - } - else { - free(rawPath); - ftpc->prevpath = NULL; /* no path */ - } - } - - if(ftpc->prevpath) - infof(data, "Remembering we are in dir \"%s\"", ftpc->prevpath); - } - - /* free the dir tree and file parts */ - freedirs(ftpc); - - /* shut down the socket to inform the server we're done */ - -#ifdef _WIN32_WCE - shutdown(conn->sock[SECONDARYSOCKET], 2); /* SD_BOTH */ -#endif - - if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) { - if(!result && ftpc->dont_check && data->req.maxdownload > 0) { - /* partial download completed */ - result = Curl_pp_sendf(data, pp, "%s", "ABOR"); - if(result) { - failf(data, "Failure sending ABOR command: %s", - curl_easy_strerror(result)); - ftpc->ctl_valid = FALSE; /* mark control connection as bad */ - connclose(conn, "ABOR command failed"); /* connection closure */ - } - } - - if(conn->ssl[SECONDARYSOCKET].use) { - /* The secondary socket is using SSL so we must close down that part - first before we close the socket for real */ - Curl_ssl_close(data, conn, SECONDARYSOCKET); - - /* Note that we keep "use" set to TRUE since that (next) connection is - still requested to use SSL */ - } - close_secondarysocket(data, conn); - } - - if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid && - pp->pending_resp && !premature) { - /* - * Let's see what the server says about the transfer we just performed, - * but lower the timeout as sometimes this connection has died while the - * data has been transferred. This happens when doing through NATs etc that - * abandon old silent connections. - */ - timediff_t old_time = pp->response_time; - - pp->response_time = 60*1000; /* give it only a minute for now */ - pp->response = Curl_now(); /* timeout relative now */ - - result = Curl_GetFTPResponse(data, &nread, &ftpcode); - - pp->response_time = old_time; /* set this back to previous value */ - - if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) { - failf(data, "control connection looks dead"); - ftpc->ctl_valid = FALSE; /* mark control connection as bad */ - connclose(conn, "Timeout or similar in FTP DONE operation"); /* close */ - } - - if(result) { - Curl_safefree(ftp->pathalloc); - return result; - } - - if(ftpc->dont_check && data->req.maxdownload > 0) { - /* we have just sent ABOR and there is no reliable way to check if it was - * successful or not; we have to close the connection now */ - infof(data, "partial download completed, closing connection"); - connclose(conn, "Partial download with no ability to check"); - return result; - } - - if(!ftpc->dont_check) { - /* 226 Transfer complete, 250 Requested file action okay, completed. */ - switch(ftpcode) { - case 226: - case 250: - break; - case 552: - failf(data, "Exceeded storage allocation"); - result = CURLE_REMOTE_DISK_FULL; - break; - default: - failf(data, "server did not report OK, got %d", ftpcode); - result = CURLE_PARTIAL_FILE; - break; - } - } - } - - if(result || premature) - /* the response code from the transfer showed an error already so no - use checking further */ - ; - else if(data->set.upload) { - if((-1 != data->state.infilesize) && - (data->state.infilesize != data->req.writebytecount) && - !data->set.crlf && - (ftp->transfer == PPTRANSFER_BODY)) { - failf(data, "Uploaded unaligned file size (%" CURL_FORMAT_CURL_OFF_T - " out of %" CURL_FORMAT_CURL_OFF_T " bytes)", - data->req.writebytecount, data->state.infilesize); - result = CURLE_PARTIAL_FILE; - } - } - else { - if((-1 != data->req.size) && - (data->req.size != data->req.bytecount) && -#ifdef CURL_DO_LINEEND_CONV - /* Most FTP servers don't adjust their file SIZE response for CRLFs, so - * we'll check to see if the discrepancy can be explained by the number - * of CRLFs we've changed to LFs. - */ - ((data->req.size + data->state.crlf_conversions) != - data->req.bytecount) && -#endif /* CURL_DO_LINEEND_CONV */ - (data->req.maxdownload != data->req.bytecount)) { - failf(data, "Received only partial file: %" CURL_FORMAT_CURL_OFF_T - " bytes", data->req.bytecount); - result = CURLE_PARTIAL_FILE; - } - else if(!ftpc->dont_check && - !data->req.bytecount && - (data->req.size>0)) { - failf(data, "No data was received"); - result = CURLE_FTP_COULDNT_RETR_FILE; - } - } - - /* clear these for next connection */ - ftp->transfer = PPTRANSFER_BODY; - ftpc->dont_check = FALSE; - - /* Send any post-transfer QUOTE strings? */ - if(!status && !result && !premature && data->set.postquote) - result = ftp_sendquote(data, conn, data->set.postquote); - Curl_safefree(ftp->pathalloc); - return result; -} - -/*********************************************************************** - * - * ftp_sendquote() - * - * Where a 'quote' means a list of custom commands to send to the server. - * The quote list is passed as an argument. - * - * BLOCKING - */ - -static -CURLcode ftp_sendquote(struct Curl_easy *data, - struct connectdata *conn, struct curl_slist *quote) -{ - struct curl_slist *item; - struct ftp_conn *ftpc = &conn->proto.ftpc; - struct pingpong *pp = &ftpc->pp; - - item = quote; - while(item) { - if(item->data) { - ssize_t nread; - char *cmd = item->data; - bool acceptfail = FALSE; - CURLcode result; - int ftpcode = 0; - - /* if a command starts with an asterisk, which a legal FTP command never - can, the command will be allowed to fail without it causing any - aborts or cancels etc. It will cause libcurl to act as if the command - is successful, whatever the server reponds. */ - - if(cmd[0] == '*') { - cmd++; - acceptfail = TRUE; - } - - result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd); - if(!result) { - pp->response = Curl_now(); /* timeout relative now */ - result = Curl_GetFTPResponse(data, &nread, &ftpcode); - } - if(result) - return result; - - if(!acceptfail && (ftpcode >= 400)) { - failf(data, "QUOT string not accepted: %s", cmd); - return CURLE_QUOTE_ERROR; - } - } - - item = item->next; - } - - return CURLE_OK; -} - -/*********************************************************************** - * - * ftp_need_type() - * - * Returns TRUE if we in the current situation should send TYPE - */ -static int ftp_need_type(struct connectdata *conn, - bool ascii_wanted) -{ - return conn->proto.ftpc.transfertype != (ascii_wanted?'A':'I'); -} - -/*********************************************************************** - * - * ftp_nb_type() - * - * Set TYPE. We only deal with ASCII or BINARY so this function - * sets one of them. - * If the transfer type is not sent, simulate on OK response in newstate - */ -static CURLcode ftp_nb_type(struct Curl_easy *data, - struct connectdata *conn, - bool ascii, ftpstate newstate) -{ - struct ftp_conn *ftpc = &conn->proto.ftpc; - CURLcode result; - char want = (char)(ascii?'A':'I'); - - if(ftpc->transfertype == want) { - state(data, newstate); - return ftp_state_type_resp(data, 200, newstate); - } - - result = Curl_pp_sendf(data, &ftpc->pp, "TYPE %c", want); - if(!result) { - state(data, newstate); - - /* keep track of our current transfer type */ - ftpc->transfertype = want; - } - return result; -} - -/*************************************************************************** - * - * ftp_pasv_verbose() - * - * This function only outputs some informationals about this second connection - * when we've issued a PASV command before and thus we have connected to a - * possibly new IP address. - * - */ -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static void -ftp_pasv_verbose(struct Curl_easy *data, - struct Curl_addrinfo *ai, - char *newhost, /* ascii version */ - int port) -{ - char buf[256]; - Curl_printable_address(ai, buf, sizeof(buf)); - infof(data, "Connecting to %s (%s) port %d", newhost, buf, port); -} -#endif - -/* - * ftp_do_more() - * - * This function shall be called when the second FTP (data) connection is - * connected. - * - * 'complete' can return 0 for incomplete, 1 for done and -1 for go back - * (which basically is only for when PASV is being sent to retry a failed - * EPSV). - */ - -static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) -{ - struct connectdata *conn = data->conn; - struct ftp_conn *ftpc = &conn->proto.ftpc; - CURLcode result = CURLE_OK; - bool connected = FALSE; - bool complete = FALSE; - - /* the ftp struct is inited in ftp_connect(). If we are connecting to an HTTP - * proxy then the state will not be valid until after that connection is - * complete */ - struct FTP *ftp = NULL; - - /* if the second connection isn't done yet, wait for it */ - if(!conn->bits.tcpconnect[SECONDARYSOCKET]) { - if(Curl_connect_ongoing(conn)) { - /* As we're in TUNNEL_CONNECT state now, we know the proxy name and port - aren't used so we blank their arguments. */ - result = Curl_proxyCONNECT(data, SECONDARYSOCKET, NULL, 0); - - return result; - } - - result = Curl_is_connected(data, conn, SECONDARYSOCKET, &connected); - - /* Ready to do more? */ - if(connected) { - DEBUGF(infof(data, "DO-MORE connected phase starts")); - } - else { - if(result && (ftpc->count1 == 0)) { - *completep = -1; /* go back to DOING please */ - /* this is a EPSV connect failing, try PASV instead */ - return ftp_epsv_disable(data, conn); - } - return result; - } - } - -#ifndef CURL_DISABLE_PROXY - result = Curl_proxy_connect(data, SECONDARYSOCKET); - if(result) - return result; - - if(CONNECT_SECONDARYSOCKET_PROXY_SSL()) - return result; - - if(conn->bits.tunnel_proxy && conn->bits.httpproxy && - Curl_connect_ongoing(conn)) - return result; -#endif - - /* Curl_proxy_connect might have moved the protocol state */ - ftp = data->req.p.ftp; - - if(ftpc->state) { - /* already in a state so skip the initial commands. - They are only done to kickstart the do_more state */ - result = ftp_multi_statemach(data, &complete); - - *completep = (int)complete; - - /* if we got an error or if we don't wait for a data connection return - immediately */ - if(result || !ftpc->wait_data_conn) - return result; - - /* if we reach the end of the FTP state machine here, *complete will be - TRUE but so is ftpc->wait_data_conn, which says we need to wait for the - data connection and therefore we're not actually complete */ - *completep = 0; - } - - if(ftp->transfer <= PPTRANSFER_INFO) { - /* a transfer is about to take place, or if not a file name was given - so we'll do a SIZE on it later and then we need the right TYPE first */ - - if(ftpc->wait_data_conn == TRUE) { - bool serv_conned; - - result = ReceivedServerConnect(data, &serv_conned); - if(result) - return result; /* Failed to accept data connection */ - - if(serv_conned) { - /* It looks data connection is established */ - result = AcceptServerConnect(data); - ftpc->wait_data_conn = FALSE; - if(!result) - result = InitiateTransfer(data); - - if(result) - return result; - - *completep = 1; /* this state is now complete when the server has - connected back to us */ - } - } - else if(data->set.upload) { - result = ftp_nb_type(data, conn, data->state.prefer_ascii, - FTP_STOR_TYPE); - if(result) - return result; - - result = ftp_multi_statemach(data, &complete); - if(ftpc->wait_data_conn) - /* if we reach the end of the FTP state machine here, *complete will be - TRUE but so is ftpc->wait_data_conn, which says we need to wait for - the data connection and therefore we're not actually complete */ - *completep = 0; - else - *completep = (int)complete; - } - else { - /* download */ - ftp->downloadsize = -1; /* unknown as of yet */ - - result = Curl_range(data); - - if(result == CURLE_OK && data->req.maxdownload >= 0) { - /* Don't check for successful transfer */ - ftpc->dont_check = TRUE; - } - - if(result) - ; - else if(data->state.list_only || !ftpc->file) { - /* The specified path ends with a slash, and therefore we think this - is a directory that is requested, use LIST. But before that we - need to set ASCII transfer mode. */ - - /* But only if a body transfer was requested. */ - if(ftp->transfer == PPTRANSFER_BODY) { - result = ftp_nb_type(data, conn, TRUE, FTP_LIST_TYPE); - if(result) - return result; - } - /* otherwise just fall through */ - } - else { - result = ftp_nb_type(data, conn, data->state.prefer_ascii, - FTP_RETR_TYPE); - if(result) - return result; - } - - result = ftp_multi_statemach(data, &complete); - *completep = (int)complete; - } - return result; - } - - /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); - - if(!ftpc->wait_data_conn) { - /* no waiting for the data connection so this is now complete */ - *completep = 1; - DEBUGF(infof(data, "DO-MORE phase ends with %d", (int)result)); - } - - return result; -} - - - -/*********************************************************************** - * - * ftp_perform() - * - * This is the actual DO function for FTP. Get a file/directory according to - * the options previously setup. - */ - -static -CURLcode ftp_perform(struct Curl_easy *data, - bool *connected, /* connect status after PASV / PORT */ - bool *dophase_done) -{ - /* this is FTP and no proxy */ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - - DEBUGF(infof(data, "DO phase starts")); - - if(data->set.opt_no_body) { - /* requested no body means no transfer... */ - struct FTP *ftp = data->req.p.ftp; - ftp->transfer = PPTRANSFER_INFO; - } - - *dophase_done = FALSE; /* not done yet */ - - /* start the first command in the DO phase */ - result = ftp_state_quote(data, TRUE, FTP_QUOTE); - if(result) - return result; - - /* run the state-machine */ - result = ftp_multi_statemach(data, dophase_done); - - *connected = conn->bits.tcpconnect[SECONDARYSOCKET]; - - infof(data, "ftp_perform ends with SECONDARY: %d", *connected); - - if(*dophase_done) - DEBUGF(infof(data, "DO phase is complete1")); - - return result; -} - -static void wc_data_dtor(void *ptr) -{ - struct ftp_wc *ftpwc = ptr; - if(ftpwc && ftpwc->parser) - Curl_ftp_parselist_data_free(&ftpwc->parser); - free(ftpwc); -} - -static CURLcode init_wc_data(struct Curl_easy *data) -{ - char *last_slash; - struct FTP *ftp = data->req.p.ftp; - char *path = ftp->path; - struct WildcardData *wildcard = &(data->wildcard); - CURLcode result = CURLE_OK; - struct ftp_wc *ftpwc = NULL; - - last_slash = strrchr(ftp->path, '/'); - if(last_slash) { - last_slash++; - if(last_slash[0] == '\0') { - wildcard->state = CURLWC_CLEAN; - result = ftp_parse_url_path(data); - return result; - } - wildcard->pattern = strdup(last_slash); - if(!wildcard->pattern) - return CURLE_OUT_OF_MEMORY; - last_slash[0] = '\0'; /* cut file from path */ - } - else { /* there is only 'wildcard pattern' or nothing */ - if(path[0]) { - wildcard->pattern = strdup(path); - if(!wildcard->pattern) - return CURLE_OUT_OF_MEMORY; - path[0] = '\0'; - } - else { /* only list */ - wildcard->state = CURLWC_CLEAN; - result = ftp_parse_url_path(data); - return result; - } - } - - /* program continues only if URL is not ending with slash, allocate needed - resources for wildcard transfer */ - - /* allocate ftp protocol specific wildcard data */ - ftpwc = calloc(1, sizeof(struct ftp_wc)); - if(!ftpwc) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } - - /* INITIALIZE parselist structure */ - ftpwc->parser = Curl_ftp_parselist_data_alloc(); - if(!ftpwc->parser) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } - - wildcard->protdata = ftpwc; /* put it to the WildcardData tmp pointer */ - wildcard->dtor = wc_data_dtor; - - /* wildcard does not support NOCWD option (assert it?) */ - if(data->set.ftp_filemethod == FTPFILE_NOCWD) - data->set.ftp_filemethod = FTPFILE_MULTICWD; - - /* try to parse ftp url */ - result = ftp_parse_url_path(data); - if(result) { - goto fail; - } - - wildcard->path = strdup(ftp->path); - if(!wildcard->path) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } - - /* backup old write_function */ - ftpwc->backup.write_function = data->set.fwrite_func; - /* parsing write function */ - data->set.fwrite_func = Curl_ftp_parselist; - /* backup old file descriptor */ - ftpwc->backup.file_descriptor = data->set.out; - /* let the writefunc callback know the transfer */ - data->set.out = data; - - infof(data, "Wildcard - Parsing started"); - return CURLE_OK; - - fail: - if(ftpwc) { - Curl_ftp_parselist_data_free(&ftpwc->parser); - free(ftpwc); - } - Curl_safefree(wildcard->pattern); - wildcard->dtor = ZERO_NULL; - wildcard->protdata = NULL; - return result; -} - -static CURLcode wc_statemach(struct Curl_easy *data) -{ - struct WildcardData * const wildcard = &(data->wildcard); - struct connectdata *conn = data->conn; - CURLcode result = CURLE_OK; - - for(;;) { - switch(wildcard->state) { - case CURLWC_INIT: - result = init_wc_data(data); - if(wildcard->state == CURLWC_CLEAN) - /* only listing! */ - return result; - wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING; - return result; - - case CURLWC_MATCHING: { - /* In this state is LIST response successfully parsed, so lets restore - previous WRITEFUNCTION callback and WRITEDATA pointer */ - struct ftp_wc *ftpwc = wildcard->protdata; - data->set.fwrite_func = ftpwc->backup.write_function; - data->set.out = ftpwc->backup.file_descriptor; - ftpwc->backup.write_function = ZERO_NULL; - ftpwc->backup.file_descriptor = NULL; - wildcard->state = CURLWC_DOWNLOADING; - - if(Curl_ftp_parselist_geterror(ftpwc->parser)) { - /* error found in LIST parsing */ - wildcard->state = CURLWC_CLEAN; - continue; - } - if(wildcard->filelist.size == 0) { - /* no corresponding file */ - wildcard->state = CURLWC_CLEAN; - return CURLE_REMOTE_FILE_NOT_FOUND; - } - continue; - } - - case CURLWC_DOWNLOADING: { - /* filelist has at least one file, lets get first one */ - struct ftp_conn *ftpc = &conn->proto.ftpc; - struct curl_fileinfo *finfo = wildcard->filelist.head->ptr; - struct FTP *ftp = data->req.p.ftp; - - char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename); - if(!tmp_path) - return CURLE_OUT_OF_MEMORY; - - /* switch default ftp->path and tmp_path */ - free(ftp->pathalloc); - ftp->pathalloc = ftp->path = tmp_path; - - infof(data, "Wildcard - START of \"%s\"", finfo->filename); - if(data->set.chunk_bgn) { - long userresponse; - Curl_set_in_callback(data, true); - userresponse = data->set.chunk_bgn( - finfo, wildcard->customptr, (int)wildcard->filelist.size); - Curl_set_in_callback(data, false); - switch(userresponse) { - case CURL_CHUNK_BGN_FUNC_SKIP: - infof(data, "Wildcard - \"%s\" skipped by user", - finfo->filename); - wildcard->state = CURLWC_SKIP; - continue; - case CURL_CHUNK_BGN_FUNC_FAIL: - return CURLE_CHUNK_FAILED; - } - } - - if(finfo->filetype != CURLFILETYPE_FILE) { - wildcard->state = CURLWC_SKIP; - continue; - } - - if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE) - ftpc->known_filesize = finfo->size; - - result = ftp_parse_url_path(data); - if(result) - return result; - - /* we don't need the Curl_fileinfo of first file anymore */ - Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL); - - if(wildcard->filelist.size == 0) { /* remains only one file to down. */ - wildcard->state = CURLWC_CLEAN; - /* after that will be ftp_do called once again and no transfer - will be done because of CURLWC_CLEAN state */ - return CURLE_OK; - } - return result; - } - - case CURLWC_SKIP: { - if(data->set.chunk_end) { - Curl_set_in_callback(data, true); - data->set.chunk_end(data->wildcard.customptr); - Curl_set_in_callback(data, false); - } - Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL); - wildcard->state = (wildcard->filelist.size == 0) ? - CURLWC_CLEAN : CURLWC_DOWNLOADING; - continue; - } - - case CURLWC_CLEAN: { - struct ftp_wc *ftpwc = wildcard->protdata; - result = CURLE_OK; - if(ftpwc) - result = Curl_ftp_parselist_geterror(ftpwc->parser); - - wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE; - return result; - } - - case CURLWC_DONE: - case CURLWC_ERROR: - case CURLWC_CLEAR: - if(wildcard->dtor) - wildcard->dtor(wildcard->protdata); - return result; - } - } - /* UNREACHABLE */ -} - -/*********************************************************************** - * - * ftp_do() - * - * This function is registered as 'curl_do' function. It decodes the path - * parts etc as a wrapper to the actual DO function (ftp_perform). - * - * The input argument is already checked for validity. - */ -static CURLcode ftp_do(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct ftp_conn *ftpc = &conn->proto.ftpc; - - *done = FALSE; /* default to false */ - ftpc->wait_data_conn = FALSE; /* default to no such wait */ - - if(data->state.wildcardmatch) { - result = wc_statemach(data); - if(data->wildcard.state == CURLWC_SKIP || - data->wildcard.state == CURLWC_DONE) { - /* do not call ftp_regular_transfer */ - return CURLE_OK; - } - if(result) /* error, loop or skipping the file */ - return result; - } - else { /* no wildcard FSM needed */ - result = ftp_parse_url_path(data); - if(result) - return result; - } - - result = ftp_regular_transfer(data, done); - - return result; -} - -/*********************************************************************** - * - * ftp_quit() - * - * This should be called before calling sclose() on an ftp control connection - * (not data connections). We should then wait for the response from the - * server before returning. The calling code should then try to close the - * connection. - * - */ -static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - - if(conn->proto.ftpc.ctl_valid) { - result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "QUIT"); - if(result) { - failf(data, "Failure sending QUIT command: %s", - curl_easy_strerror(result)); - conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */ - connclose(conn, "QUIT command failed"); /* mark for connection closure */ - state(data, FTP_STOP); - return result; - } - - state(data, FTP_QUIT); - - result = ftp_block_statemach(data, conn); - } - - return result; -} - -/*********************************************************************** - * - * ftp_disconnect() - * - * Disconnect from an FTP server. Cleanup protocol-specific per-connection - * resources. BLOCKING. - */ -static CURLcode ftp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection) -{ - struct ftp_conn *ftpc = &conn->proto.ftpc; - struct pingpong *pp = &ftpc->pp; - - /* We cannot send quit unconditionally. If this connection is stale or - bad in any way, sending quit and waiting around here will make the - disconnect wait in vain and cause more problems than we need to. - - ftp_quit() will check the state of ftp->ctl_valid. If it's ok it - will try to send the QUIT command, otherwise it will just return. - */ - if(dead_connection) - ftpc->ctl_valid = FALSE; - - /* The FTP session may or may not have been allocated/setup at this point! */ - (void)ftp_quit(data, conn); /* ignore errors on the QUIT */ - - if(ftpc->entrypath) { - if(data->state.most_recent_ftp_entrypath == ftpc->entrypath) { - data->state.most_recent_ftp_entrypath = NULL; - } - Curl_safefree(ftpc->entrypath); - } - - freedirs(ftpc); - Curl_safefree(ftpc->prevpath); - Curl_safefree(ftpc->server_os); - Curl_pp_disconnect(pp); - Curl_sec_end(conn); - return CURLE_OK; -} - -#ifdef _MSC_VER -/* warning C4706: assignment within conditional expression */ -#pragma warning(disable:4706) -#endif - -/*********************************************************************** - * - * ftp_parse_url_path() - * - * Parse the URL path into separate path components. - * - */ -static -CURLcode ftp_parse_url_path(struct Curl_easy *data) -{ - /* the ftp struct is already inited in ftp_connect() */ - struct FTP *ftp = data->req.p.ftp; - struct connectdata *conn = data->conn; - struct ftp_conn *ftpc = &conn->proto.ftpc; - const char *slashPos = NULL; - const char *fileName = NULL; - CURLcode result = CURLE_OK; - char *rawPath = NULL; /* url-decoded "raw" path */ - size_t pathLen = 0; - - ftpc->ctl_valid = FALSE; - ftpc->cwdfail = FALSE; - - /* url-decode ftp path before further evaluation */ - result = Curl_urldecode(ftp->path, 0, &rawPath, &pathLen, REJECT_CTRL); - if(result) { - failf(data, "path contains control characters"); - return result; - } - - switch(data->set.ftp_filemethod) { - case FTPFILE_NOCWD: /* fastest, but less standard-compliant */ - - if((pathLen > 0) && (rawPath[pathLen - 1] != '/')) - fileName = rawPath; /* this is a full file path */ - /* - else: ftpc->file is not used anywhere other than for operations on - a file. In other words, never for directory operations. - So we can safely leave filename as NULL here and use it as a - argument in dir/file decisions. - */ - break; - - case FTPFILE_SINGLECWD: - slashPos = strrchr(rawPath, '/'); - if(slashPos) { - /* get path before last slash, except for / */ - size_t dirlen = slashPos - rawPath; - if(dirlen == 0) - dirlen++; - - ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0])); - if(!ftpc->dirs) { - free(rawPath); - return CURLE_OUT_OF_MEMORY; - } - - ftpc->dirs[0] = calloc(1, dirlen + 1); - if(!ftpc->dirs[0]) { - free(rawPath); - return CURLE_OUT_OF_MEMORY; - } - - strncpy(ftpc->dirs[0], rawPath, dirlen); - ftpc->dirdepth = 1; /* we consider it to be a single dir */ - fileName = slashPos + 1; /* rest is file name */ - } - else - fileName = rawPath; /* file name only (or empty) */ - break; - - default: /* allow pretty much anything */ - case FTPFILE_MULTICWD: { - /* current position: begin of next path component */ - const char *curPos = rawPath; - - int dirAlloc = 0; /* number of entries allocated for the 'dirs' array */ - const char *str = rawPath; - for(; *str != 0; ++str) - if (*str == '/') - ++dirAlloc; - - if(dirAlloc > 0) { - ftpc->dirs = calloc(dirAlloc, sizeof(ftpc->dirs[0])); - if(!ftpc->dirs) { - free(rawPath); - return CURLE_OUT_OF_MEMORY; - } - - /* parse the URL path into separate path components */ - while((slashPos = strchr(curPos, '/'))) { - size_t compLen = slashPos - curPos; - - /* path starts with a slash: add that as a directory */ - if((compLen == 0) && (ftpc->dirdepth == 0)) - ++compLen; - - /* we skip empty path components, like "x//y" since the FTP command - CWD requires a parameter and a non-existent parameter a) doesn't - work on many servers and b) has no effect on the others. */ - if(compLen > 0) { - char *comp = calloc(1, compLen + 1); - if(!comp) { - free(rawPath); - return CURLE_OUT_OF_MEMORY; - } - strncpy(comp, curPos, compLen); - ftpc->dirs[ftpc->dirdepth++] = comp; - } - curPos = slashPos + 1; - } - } - DEBUGASSERT(ftpc->dirdepth <= dirAlloc); - fileName = curPos; /* the rest is the file name (or empty) */ - } - break; - } /* switch */ - - if(fileName && *fileName) - ftpc->file = strdup(fileName); - else - ftpc->file = NULL; /* instead of point to a zero byte, - we make it a NULL pointer */ - - if(data->set.upload && !ftpc->file && (ftp->transfer == PPTRANSFER_BODY)) { - /* We need a file name when uploading. Return error! */ - failf(data, "Uploading to a URL without a file name"); - free(rawPath); - return CURLE_URL_MALFORMAT; - } - - ftpc->cwddone = FALSE; /* default to not done */ - - if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/')) - ftpc->cwddone = TRUE; /* skip CWD for absolute paths */ - else { /* newly created FTP connections are already in entry path */ - const char *oldPath = conn->bits.reuse ? ftpc->prevpath : ""; - if(oldPath) { - size_t n = pathLen; - if(data->set.ftp_filemethod == FTPFILE_NOCWD) - n = 0; /* CWD to entry for relative paths */ - else - n -= ftpc->file?strlen(ftpc->file):0; - - if((strlen(oldPath) == n) && !strncmp(rawPath, oldPath, n)) { - infof(data, "Request has same path as previous transfer"); - ftpc->cwddone = TRUE; - } - } - } - - free(rawPath); - return CURLE_OK; -} - -/* call this when the DO phase has completed */ -static CURLcode ftp_dophase_done(struct Curl_easy *data, bool connected) -{ - struct connectdata *conn = data->conn; - struct FTP *ftp = data->req.p.ftp; - struct ftp_conn *ftpc = &conn->proto.ftpc; - - if(connected) { - int completed; - CURLcode result = ftp_do_more(data, &completed); - - if(result) { - close_secondarysocket(data, conn); - return result; - } - } - - if(ftp->transfer != PPTRANSFER_BODY) - /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); - else if(!connected) - /* since we didn't connect now, we want do_more to get called */ - conn->bits.do_more = TRUE; - - ftpc->ctl_valid = TRUE; /* seems good */ - - return CURLE_OK; -} - -/* called from multi.c while DOing */ -static CURLcode ftp_doing(struct Curl_easy *data, - bool *dophase_done) -{ - CURLcode result = ftp_multi_statemach(data, dophase_done); - - if(result) - DEBUGF(infof(data, "DO phase failed")); - else if(*dophase_done) { - result = ftp_dophase_done(data, FALSE /* not connected */); - - DEBUGF(infof(data, "DO phase is complete2")); - } - return result; -} - -/*********************************************************************** - * - * ftp_regular_transfer() - * - * The input argument is already checked for validity. - * - * Performs all commands done before a regular transfer between a local and a - * remote host. - * - * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the - * ftp_done() function without finding any major problem. - */ -static -CURLcode ftp_regular_transfer(struct Curl_easy *data, - bool *dophase_done) -{ - CURLcode result = CURLE_OK; - bool connected = FALSE; - struct connectdata *conn = data->conn; - struct ftp_conn *ftpc = &conn->proto.ftpc; - data->req.size = -1; /* make sure this is unknown at this point */ - - Curl_pgrsSetUploadCounter(data, 0); - Curl_pgrsSetDownloadCounter(data, 0); - Curl_pgrsSetUploadSize(data, -1); - Curl_pgrsSetDownloadSize(data, -1); - - ftpc->ctl_valid = TRUE; /* starts good */ - - result = ftp_perform(data, - &connected, /* have we connected after PASV/PORT */ - dophase_done); /* all commands in the DO-phase done? */ - - if(!result) { - - if(!*dophase_done) - /* the DO phase has not completed yet */ - return CURLE_OK; - - result = ftp_dophase_done(data, connected); - - if(result) - return result; - } - else - freedirs(ftpc); - - return result; -} - -static CURLcode ftp_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - char *type; - struct FTP *ftp; - - data->req.p.ftp = ftp = calloc(sizeof(struct FTP), 1); - if(!ftp) - return CURLE_OUT_OF_MEMORY; - - ftp->path = &data->state.up.path[1]; /* don't include the initial slash */ - - /* FTP URLs support an extension like ";type=<typecode>" that - * we'll try to get now! */ - type = strstr(ftp->path, ";type="); - - if(!type) - type = strstr(conn->host.rawalloc, ";type="); - - if(type) { - char command; - *type = 0; /* it was in the middle of the hostname */ - command = Curl_raw_toupper(type[6]); - - switch(command) { - case 'A': /* ASCII mode */ - data->state.prefer_ascii = TRUE; - break; - - case 'D': /* directory mode */ - data->state.list_only = TRUE; - break; - - case 'I': /* binary mode */ - default: - /* switch off ASCII */ - data->state.prefer_ascii = FALSE; - break; - } - } - - /* get some initial data into the ftp struct */ - ftp->transfer = PPTRANSFER_BODY; - ftp->downloadsize = 0; - conn->proto.ftpc.known_filesize = -1; /* unknown size for now */ - - return CURLE_OK; -} - -#endif /* CURL_DISABLE_FTP */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#include <curl/curl.h>
+#include "urldata.h"
+#include "sendf.h"
+#include "if2ip.h"
+#include "hostip.h"
+#include "progress.h"
+#include "transfer.h"
+#include "escape.h"
+#include "http.h" /* for HTTP proxy tunnel stuff */
+#include "ftp.h"
+#include "fileinfo.h"
+#include "ftplistparser.h"
+#include "curl_range.h"
+#include "curl_krb5.h"
+#include "strtoofft.h"
+#include "strcase.h"
+#include "vtls/vtls.h"
+#include "cfilters.h"
+#include "connect.h"
+#include "strerror.h"
+#include "inet_ntop.h"
+#include "inet_pton.h"
+#include "select.h"
+#include "parsedate.h" /* for the week day and month names */
+#include "sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "multiif.h"
+#include "url.h"
+#include "speedcheck.h"
+#include "warnless.h"
+#include "http_proxy.h"
+#include "socks.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#ifndef NI_MAXHOST
+#define NI_MAXHOST 1025
+#endif
+#ifndef INET_ADDRSTRLEN
+#define INET_ADDRSTRLEN 16
+#endif
+
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+#define ftp_pasv_verbose(a,b,c,d) Curl_nop_stmt
+#endif
+
+/* Local API functions */
+#ifndef DEBUGBUILD
+static void _state(struct Curl_easy *data,
+ ftpstate newstate);
+#define state(x,y) _state(x,y)
+#else
+static void _state(struct Curl_easy *data,
+ ftpstate newstate,
+ int lineno);
+#define state(x,y) _state(x,y,__LINE__)
+#endif
+
+static CURLcode ftp_sendquote(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct curl_slist *quote);
+static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn);
+static CURLcode ftp_parse_url_path(struct Curl_easy *data);
+static CURLcode ftp_regular_transfer(struct Curl_easy *data, bool *done);
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static void ftp_pasv_verbose(struct Curl_easy *data,
+ struct Curl_addrinfo *ai,
+ char *newhost, /* ascii version */
+ int port);
+#endif
+static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data);
+static CURLcode ftp_state_mdtm(struct Curl_easy *data);
+static CURLcode ftp_state_quote(struct Curl_easy *data,
+ bool init, ftpstate instate);
+static CURLcode ftp_nb_type(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool ascii, ftpstate newstate);
+static int ftp_need_type(struct connectdata *conn,
+ bool ascii);
+static CURLcode ftp_do(struct Curl_easy *data, bool *done);
+static CURLcode ftp_done(struct Curl_easy *data,
+ CURLcode, bool premature);
+static CURLcode ftp_connect(struct Curl_easy *data, bool *done);
+static CURLcode ftp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn, bool dead_connection);
+static CURLcode ftp_do_more(struct Curl_easy *data, int *completed);
+static CURLcode ftp_multi_statemach(struct Curl_easy *data, bool *done);
+static int ftp_getsock(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t *socks);
+static int ftp_domore_getsock(struct Curl_easy *data,
+ struct connectdata *conn, curl_socket_t *socks);
+static CURLcode ftp_doing(struct Curl_easy *data,
+ bool *dophase_done);
+static CURLcode ftp_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn);
+static CURLcode init_wc_data(struct Curl_easy *data);
+static CURLcode wc_statemach(struct Curl_easy *data);
+static void wc_data_dtor(void *ptr);
+static CURLcode ftp_state_retr(struct Curl_easy *data, curl_off_t filesize);
+static CURLcode ftp_readresp(struct Curl_easy *data,
+ curl_socket_t sockfd,
+ struct pingpong *pp,
+ int *ftpcode,
+ size_t *size);
+static CURLcode ftp_dophase_done(struct Curl_easy *data,
+ bool connected);
+
+/*
+ * FTP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_ftp = {
+ "FTP", /* scheme */
+ ftp_setup_connection, /* setup_connection */
+ ftp_do, /* do_it */
+ ftp_done, /* done */
+ ftp_do_more, /* do_more */
+ ftp_connect, /* connect_it */
+ ftp_multi_statemach, /* connecting */
+ ftp_doing, /* doing */
+ ftp_getsock, /* proto_getsock */
+ ftp_getsock, /* doing_getsock */
+ ftp_domore_getsock, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ ftp_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_FTP, /* defport */
+ CURLPROTO_FTP, /* protocol */
+ CURLPROTO_FTP, /* family */
+ PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD |
+ PROTOPT_NOURLQUERY | PROTOPT_PROXY_AS_HTTP |
+ PROTOPT_WILDCARD /* flags */
+};
+
+
+#ifdef USE_SSL
+/*
+ * FTPS protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_ftps = {
+ "FTPS", /* scheme */
+ ftp_setup_connection, /* setup_connection */
+ ftp_do, /* do_it */
+ ftp_done, /* done */
+ ftp_do_more, /* do_more */
+ ftp_connect, /* connect_it */
+ ftp_multi_statemach, /* connecting */
+ ftp_doing, /* doing */
+ ftp_getsock, /* proto_getsock */
+ ftp_getsock, /* doing_getsock */
+ ftp_domore_getsock, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ ftp_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_FTPS, /* defport */
+ CURLPROTO_FTPS, /* protocol */
+ CURLPROTO_FTP, /* family */
+ PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION |
+ PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY | PROTOPT_WILDCARD /* flags */
+};
+#endif
+
+static void close_secondarysocket(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ Curl_conn_close(data, SECONDARYSOCKET);
+ Curl_conn_cf_discard_all(data, conn, SECONDARYSOCKET);
+}
+
+/*
+ * NOTE: back in the old days, we added code in the FTP code that made NOBODY
+ * requests on files respond with headers passed to the client/stdout that
+ * looked like HTTP ones.
+ *
+ * This approach is not very elegant, it causes confusion and is error-prone.
+ * It is subject for removal at the next (or at least a future) soname bump.
+ * Until then you can test the effects of the removal by undefining the
+ * following define named CURL_FTP_HTTPSTYLE_HEAD.
+ */
+#define CURL_FTP_HTTPSTYLE_HEAD 1
+
+static void freedirs(struct ftp_conn *ftpc)
+{
+ if(ftpc->dirs) {
+ int i;
+ for(i = 0; i < ftpc->dirdepth; i++) {
+ free(ftpc->dirs[i]);
+ ftpc->dirs[i] = NULL;
+ }
+ free(ftpc->dirs);
+ ftpc->dirs = NULL;
+ ftpc->dirdepth = 0;
+ }
+ Curl_safefree(ftpc->file);
+
+ /* no longer of any use */
+ Curl_safefree(ftpc->newhost);
+}
+
+/***********************************************************************
+ *
+ * AcceptServerConnect()
+ *
+ * After connection request is received from the server this function is
+ * called to accept the connection and close the listening socket
+ *
+ */
+static CURLcode AcceptServerConnect(struct Curl_easy *data)
+{
+ struct connectdata *conn = data->conn;
+ curl_socket_t sock = conn->sock[SECONDARYSOCKET];
+ curl_socket_t s = CURL_SOCKET_BAD;
+#ifdef ENABLE_IPV6
+ struct Curl_sockaddr_storage add;
+#else
+ struct sockaddr_in add;
+#endif
+ curl_socklen_t size = (curl_socklen_t) sizeof(add);
+ CURLcode result;
+
+ if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
+ size = sizeof(add);
+
+ s = accept(sock, (struct sockaddr *) &add, &size);
+ }
+
+ if(CURL_SOCKET_BAD == s) {
+ failf(data, "Error accept()ing server connect");
+ return CURLE_FTP_PORT_FAILED;
+ }
+ infof(data, "Connection accepted from server");
+ /* when this happens within the DO state it is important that we mark us as
+ not needing DO_MORE anymore */
+ conn->bits.do_more = FALSE;
+
+ (void)curlx_nonblock(s, TRUE); /* enable non-blocking */
+ /* Replace any filter on SECONDARY with one listeing on this socket */
+ result = Curl_conn_socket_accepted_set(data, conn, SECONDARYSOCKET, &s);
+ if(result)
+ return result;
+
+ if(data->set.fsockopt) {
+ int error = 0;
+
+ /* activate callback for setting socket options */
+ Curl_set_in_callback(data, true);
+ error = data->set.fsockopt(data->set.sockopt_client,
+ s,
+ CURLSOCKTYPE_ACCEPT);
+ Curl_set_in_callback(data, false);
+
+ if(error) {
+ close_secondarysocket(data, conn);
+ return CURLE_ABORTED_BY_CALLBACK;
+ }
+ }
+
+ return CURLE_OK;
+
+}
+
+/*
+ * ftp_timeleft_accept() returns the amount of milliseconds left allowed for
+ * waiting server to connect. If the value is negative, the timeout time has
+ * already elapsed.
+ *
+ * The start time is stored in progress.t_acceptdata - as set with
+ * Curl_pgrsTime(..., TIMER_STARTACCEPT);
+ *
+ */
+static timediff_t ftp_timeleft_accept(struct Curl_easy *data)
+{
+ timediff_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT;
+ timediff_t other;
+ struct curltime now;
+
+ if(data->set.accepttimeout > 0)
+ timeout_ms = data->set.accepttimeout;
+
+ now = Curl_now();
+
+ /* check if the generic timeout possibly is set shorter */
+ other = Curl_timeleft(data, &now, FALSE);
+ if(other && (other < timeout_ms))
+ /* note that this also works fine for when other happens to be negative
+ due to it already having elapsed */
+ timeout_ms = other;
+ else {
+ /* subtract elapsed time */
+ timeout_ms -= Curl_timediff(now, data->progress.t_acceptdata);
+ if(!timeout_ms)
+ /* avoid returning 0 as that means no timeout! */
+ return -1;
+ }
+
+ return timeout_ms;
+}
+
+
+/***********************************************************************
+ *
+ * ReceivedServerConnect()
+ *
+ * After allowing server to connect to us from data port, this function
+ * checks both data connection for connection establishment and ctrl
+ * connection for a negative response regarding a failure in connecting
+ *
+ */
+static CURLcode ReceivedServerConnect(struct Curl_easy *data, bool *received)
+{
+ struct connectdata *conn = data->conn;
+ curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET];
+ curl_socket_t data_sock = conn->sock[SECONDARYSOCKET];
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct pingpong *pp = &ftpc->pp;
+ int result;
+ timediff_t timeout_ms;
+ ssize_t nread;
+ int ftpcode;
+
+ *received = FALSE;
+
+ timeout_ms = ftp_timeleft_accept(data);
+ infof(data, "Checking for server connect");
+ if(timeout_ms < 0) {
+ /* if a timeout was already reached, bail out */
+ failf(data, "Accept timeout occurred while waiting server connect");
+ return CURLE_FTP_ACCEPT_TIMEOUT;
+ }
+
+ /* First check whether there is a cached response from server */
+ if(pp->cache_size && pp->cache && pp->cache[0] > '3') {
+ /* Data connection could not be established, let's return */
+ infof(data, "There is negative response in cache while serv connect");
+ (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
+ return CURLE_FTP_ACCEPT_FAILED;
+ }
+
+ result = Curl_socket_check(ctrl_sock, data_sock, CURL_SOCKET_BAD, 0);
+
+ /* see if the connection request is already here */
+ switch(result) {
+ case -1: /* error */
+ /* let's die here */
+ failf(data, "Error while waiting for server connect");
+ return CURLE_FTP_ACCEPT_FAILED;
+ case 0: /* Server connect is not received yet */
+ break; /* loop */
+ default:
+
+ if(result & CURL_CSELECT_IN2) {
+ infof(data, "Ready to accept data connection from server");
+ *received = TRUE;
+ }
+ else if(result & CURL_CSELECT_IN) {
+ infof(data, "Ctrl conn has data while waiting for data conn");
+ (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
+
+ if(ftpcode/100 > 3)
+ return CURLE_FTP_ACCEPT_FAILED;
+
+ return CURLE_WEIRD_SERVER_REPLY;
+ }
+
+ break;
+ } /* switch() */
+
+ return CURLE_OK;
+}
+
+
+/***********************************************************************
+ *
+ * InitiateTransfer()
+ *
+ * After connection from server is accepted this function is called to
+ * setup transfer parameters and initiate the data transfer.
+ *
+ */
+static CURLcode InitiateTransfer(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ bool connected;
+
+ DEBUGF(infof(data, "ftp InitiateTransfer()"));
+ result = Curl_conn_connect(data, SECONDARYSOCKET, TRUE, &connected);
+ if(result || !connected)
+ return result;
+
+ if(conn->proto.ftpc.state_saved == FTP_STOR) {
+ /* When we know we're uploading a specified file, we can get the file
+ size prior to the actual upload. */
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+
+ /* set the SO_SNDBUF for the secondary socket for those who need it */
+ Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
+
+ Curl_setup_transfer(data, -1, -1, FALSE, SECONDARYSOCKET);
+ }
+ else {
+ /* FTP download: */
+ Curl_setup_transfer(data, SECONDARYSOCKET,
+ conn->proto.ftpc.retr_size_saved, FALSE, -1);
+ }
+
+ conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
+ state(data, FTP_STOP);
+
+ return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * AllowServerConnect()
+ *
+ * When we've issue the PORT command, we have told the server to connect to
+ * us. This function checks whether data connection is established if so it is
+ * accepted.
+ *
+ */
+static CURLcode AllowServerConnect(struct Curl_easy *data, bool *connected)
+{
+ timediff_t timeout_ms;
+ CURLcode result = CURLE_OK;
+
+ *connected = FALSE;
+ infof(data, "Preparing for accepting server on data port");
+
+ /* Save the time we start accepting server connect */
+ Curl_pgrsTime(data, TIMER_STARTACCEPT);
+
+ timeout_ms = ftp_timeleft_accept(data);
+ if(timeout_ms < 0) {
+ /* if a timeout was already reached, bail out */
+ failf(data, "Accept timeout occurred while waiting server connect");
+ result = CURLE_FTP_ACCEPT_TIMEOUT;
+ goto out;
+ }
+
+ /* see if the connection request is already here */
+ result = ReceivedServerConnect(data, connected);
+ if(result)
+ goto out;
+
+ if(*connected) {
+ result = AcceptServerConnect(data);
+ if(result)
+ goto out;
+
+ result = InitiateTransfer(data);
+ if(result)
+ goto out;
+ }
+ else {
+ /* Add timeout to multi handle and break out of the loop */
+ Curl_expire(data, data->set.accepttimeout ?
+ data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT,
+ EXPIRE_FTP_ACCEPT);
+ }
+
+out:
+ DEBUGF(infof(data, "ftp AllowServerConnect() -> %d", result));
+ return result;
+}
+
+/* macro to check for a three-digit ftp status code at the start of the
+ given string */
+#define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) && \
+ ISDIGIT(line[2]))
+
+/* macro to check for the last line in an FTP server response */
+#define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3]))
+
+static bool ftp_endofresp(struct Curl_easy *data, struct connectdata *conn,
+ char *line, size_t len, int *code)
+{
+ (void)data;
+ (void)conn;
+
+ if((len > 3) && LASTLINE(line)) {
+ *code = curlx_sltosi(strtol(line, NULL, 10));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static CURLcode ftp_readresp(struct Curl_easy *data,
+ curl_socket_t sockfd,
+ struct pingpong *pp,
+ int *ftpcode, /* return the ftp-code if done */
+ size_t *size) /* size of the response */
+{
+ int code;
+ CURLcode result = Curl_pp_readresp(data, sockfd, pp, &code, size);
+
+#ifdef HAVE_GSSAPI
+ {
+ struct connectdata *conn = data->conn;
+ char * const buf = data->state.buffer;
+
+ /* handle the security-oriented responses 6xx ***/
+ switch(code) {
+ case 631:
+ code = Curl_sec_read_msg(data, conn, buf, PROT_SAFE);
+ break;
+ case 632:
+ code = Curl_sec_read_msg(data, conn, buf, PROT_PRIVATE);
+ break;
+ case 633:
+ code = Curl_sec_read_msg(data, conn, buf, PROT_CONFIDENTIAL);
+ break;
+ default:
+ /* normal ftp stuff we pass through! */
+ break;
+ }
+ }
+#endif
+
+ /* store the latest code for later retrieval */
+ data->info.httpcode = code;
+
+ if(ftpcode)
+ *ftpcode = code;
+
+ if(421 == code) {
+ /* 421 means "Service not available, closing control connection." and FTP
+ * servers use it to signal that idle session timeout has been exceeded.
+ * If we ignored the response, it could end up hanging in some cases.
+ *
+ * This response code can come at any point so having it treated
+ * generically is a good idea.
+ */
+ infof(data, "We got a 421 - timeout");
+ state(data, FTP_STOP);
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
+ return result;
+}
+
+/* --- parse FTP server responses --- */
+
+/*
+ * Curl_GetFTPResponse() is a BLOCKING function to read the full response
+ * from a server after a command.
+ *
+ */
+
+CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
+ ssize_t *nreadp, /* return number of bytes read */
+ int *ftpcode) /* return the ftp-code */
+{
+ /*
+ * We cannot read just one byte per read() and then go back to select() as
+ * the OpenSSL read() doesn't grok that properly.
+ *
+ * Alas, read as much as possible, split up into lines, use the ending
+ * line in a response or continue reading. */
+
+ struct connectdata *conn = data->conn;
+ curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+ CURLcode result = CURLE_OK;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct pingpong *pp = &ftpc->pp;
+ size_t nread;
+ int cache_skip = 0;
+ int value_to_be_ignored = 0;
+
+ if(ftpcode)
+ *ftpcode = 0; /* 0 for errors */
+ else
+ /* make the pointer point to something for the rest of this function */
+ ftpcode = &value_to_be_ignored;
+
+ *nreadp = 0;
+
+ while(!*ftpcode && !result) {
+ /* check and reset timeout value every lap */
+ timediff_t timeout = Curl_pp_state_timeout(data, pp, FALSE);
+ timediff_t interval_ms;
+
+ if(timeout <= 0) {
+ failf(data, "FTP response timeout");
+ return CURLE_OPERATION_TIMEDOUT; /* already too little time */
+ }
+
+ interval_ms = 1000; /* use 1 second timeout intervals */
+ if(timeout < interval_ms)
+ interval_ms = timeout;
+
+ /*
+ * Since this function is blocking, we need to wait here for input on the
+ * connection and only then we call the response reading function. We do
+ * timeout at least every second to make the timeout check run.
+ *
+ * A caution here is that the ftp_readresp() function has a cache that may
+ * contain pieces of a response from the previous invoke and we need to
+ * make sure we don't just wait for input while there is unhandled data in
+ * that cache. But also, if the cache is there, we call ftp_readresp() and
+ * the cache wasn't good enough to continue we must not just busy-loop
+ * around this function.
+ *
+ */
+
+ if(pp->cache && (cache_skip < 2)) {
+ /*
+ * There's a cache left since before. We then skipping the wait for
+ * socket action, unless this is the same cache like the previous round
+ * as then the cache was deemed not enough to act on and we then need to
+ * wait for more data anyway.
+ */
+ }
+ else if(!Curl_conn_data_pending(data, FIRSTSOCKET)) {
+ switch(SOCKET_READABLE(sockfd, interval_ms)) {
+ case -1: /* select() error, stop reading */
+ failf(data, "FTP response aborted due to select/poll error: %d",
+ SOCKERRNO);
+ return CURLE_RECV_ERROR;
+
+ case 0: /* timeout */
+ if(Curl_pgrsUpdate(data))
+ return CURLE_ABORTED_BY_CALLBACK;
+ continue; /* just continue in our loop for the timeout duration */
+
+ default: /* for clarity */
+ break;
+ }
+ }
+ result = ftp_readresp(data, sockfd, pp, ftpcode, &nread);
+ if(result)
+ break;
+
+ if(!nread && pp->cache)
+ /* bump cache skip counter as on repeated skips we must wait for more
+ data */
+ cache_skip++;
+ else
+ /* when we got data or there is no cache left, we reset the cache skip
+ counter */
+ cache_skip = 0;
+
+ *nreadp += nread;
+
+ } /* while there's buffer left and loop is requested */
+
+ pp->pending_resp = FALSE;
+
+ return result;
+}
+
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ /* for debug purposes */
+static const char * const ftp_state_names[]={
+ "STOP",
+ "WAIT220",
+ "AUTH",
+ "USER",
+ "PASS",
+ "ACCT",
+ "PBSZ",
+ "PROT",
+ "CCC",
+ "PWD",
+ "SYST",
+ "NAMEFMT",
+ "QUOTE",
+ "RETR_PREQUOTE",
+ "STOR_PREQUOTE",
+ "POSTQUOTE",
+ "CWD",
+ "MKD",
+ "MDTM",
+ "TYPE",
+ "LIST_TYPE",
+ "RETR_TYPE",
+ "STOR_TYPE",
+ "SIZE",
+ "RETR_SIZE",
+ "STOR_SIZE",
+ "REST",
+ "RETR_REST",
+ "PORT",
+ "PRET",
+ "PASV",
+ "LIST",
+ "RETR",
+ "STOR",
+ "QUIT"
+};
+#endif
+
+/* This is the ONLY way to change FTP state! */
+static void _state(struct Curl_easy *data,
+ ftpstate newstate
+#ifdef DEBUGBUILD
+ , int lineno
+#endif
+ )
+{
+ struct connectdata *conn = data->conn;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+#if defined(DEBUGBUILD)
+
+#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+ (void) lineno;
+#else
+ if(ftpc->state != newstate)
+ infof(data, "FTP %p (line %d) state change from %s to %s",
+ (void *)ftpc, lineno, ftp_state_names[ftpc->state],
+ ftp_state_names[newstate]);
+#endif
+#endif
+
+ ftpc->state = newstate;
+}
+
+static CURLcode ftp_state_user(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result = Curl_pp_sendf(data,
+ &conn->proto.ftpc.pp, "USER %s",
+ conn->user?conn->user:"");
+ if(!result) {
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ ftpc->ftp_trying_alternative = FALSE;
+ state(data, FTP_USER);
+ }
+ return result;
+}
+
+static CURLcode ftp_state_pwd(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PWD");
+ if(!result)
+ state(data, FTP_PWD);
+
+ return result;
+}
+
+/* For the FTP "protocol connect" and "doing" phases only */
+static int ftp_getsock(struct Curl_easy *data,
+ struct connectdata *conn,
+ curl_socket_t *socks)
+{
+ return Curl_pp_getsock(data, &conn->proto.ftpc.pp, socks);
+}
+
+/* For the FTP "DO_MORE" phase only */
+static int ftp_domore_getsock(struct Curl_easy *data,
+ struct connectdata *conn, curl_socket_t *socks)
+{
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ (void)data;
+
+ /* When in DO_MORE state, we could be either waiting for us to connect to a
+ * remote site, or we could wait for that site to connect to us. Or just
+ * handle ordinary commands.
+ */
+
+ DEBUGF(infof(data, "ftp_domore_getsock()"));
+ if(conn->cfilter[SECONDARYSOCKET]
+ && !Curl_conn_is_connected(conn, SECONDARYSOCKET))
+ return Curl_conn_get_select_socks(data, SECONDARYSOCKET, socks);
+
+ if(FTP_STOP == ftpc->state) {
+ int bits = GETSOCK_READSOCK(0);
+ bool any = FALSE;
+
+ /* if stopped and still in this state, then we're also waiting for a
+ connect on the secondary connection */
+ socks[0] = conn->sock[FIRSTSOCKET];
+
+ if(!data->set.ftp_use_port) {
+ int s;
+ int i;
+ /* PORT is used to tell the server to connect to us, and during that we
+ don't do happy eyeballs, but we do if we connect to the server */
+ for(s = 1, i = 0; i<2; i++) {
+ if(conn->tempsock[i] != CURL_SOCKET_BAD) {
+ socks[s] = conn->tempsock[i];
+ bits |= GETSOCK_WRITESOCK(s++);
+ any = TRUE;
+ }
+ }
+ }
+ if(!any) {
+ socks[1] = conn->sock[SECONDARYSOCKET];
+ bits |= GETSOCK_WRITESOCK(1) | GETSOCK_READSOCK(1);
+ }
+
+ return bits;
+ }
+ return Curl_pp_getsock(data, &conn->proto.ftpc.pp, socks);
+}
+
+/* This is called after the FTP_QUOTE state is passed.
+
+ ftp_state_cwd() sends the range of CWD commands to the server to change to
+ the correct directory. It may also need to send MKD commands to create
+ missing ones, if that option is enabled.
+*/
+static CURLcode ftp_state_cwd(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+ if(ftpc->cwddone)
+ /* already done and fine */
+ result = ftp_state_mdtm(data);
+ else {
+ /* FTPFILE_NOCWD with full path: expect ftpc->cwddone! */
+ DEBUGASSERT((data->set.ftp_filemethod != FTPFILE_NOCWD) ||
+ !(ftpc->dirdepth && ftpc->dirs[0][0] == '/'));
+
+ ftpc->count2 = 0; /* count2 counts failed CWDs */
+
+ if(conn->bits.reuse && ftpc->entrypath &&
+ /* no need to go to entrypath when we have an absolute path */
+ !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')) {
+ /* This is a re-used connection. Since we change directory to where the
+ transfer is taking place, we must first get back to the original dir
+ where we ended up after login: */
+ ftpc->cwdcount = 0; /* we count this as the first path, then we add one
+ for all upcoming ones in the ftp->dirs[] array */
+ result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->entrypath);
+ if(!result)
+ state(data, FTP_CWD);
+ }
+ else {
+ if(ftpc->dirdepth) {
+ ftpc->cwdcount = 1;
+ /* issue the first CWD, the rest is sent when the CWD responses are
+ received... */
+ result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
+ ftpc->dirs[ftpc->cwdcount -1]);
+ if(!result)
+ state(data, FTP_CWD);
+ }
+ else {
+ /* No CWD necessary */
+ result = ftp_state_mdtm(data);
+ }
+ }
+ }
+ return result;
+}
+
+typedef enum {
+ EPRT,
+ PORT,
+ DONE
+} ftpport;
+
+static CURLcode ftp_state_use_port(struct Curl_easy *data,
+ ftpport fcmd) /* start with this */
+{
+ CURLcode result = CURLE_FTP_PORT_FAILED;
+ struct connectdata *conn = data->conn;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ curl_socket_t portsock = CURL_SOCKET_BAD;
+ char myhost[MAX_IPADR_LEN + 1] = "";
+
+ struct Curl_sockaddr_storage ss;
+ struct Curl_addrinfo *res, *ai;
+ curl_socklen_t sslen;
+ char hbuf[NI_MAXHOST];
+ struct sockaddr *sa = (struct sockaddr *)&ss;
+ struct sockaddr_in * const sa4 = (void *)sa;
+#ifdef ENABLE_IPV6
+ struct sockaddr_in6 * const sa6 = (void *)sa;
+#endif
+ static const char mode[][5] = { "EPRT", "PORT" };
+ enum resolve_t rc;
+ int error;
+ char *host = NULL;
+ char *string_ftpport = data->set.str[STRING_FTPPORT];
+ struct Curl_dns_entry *h = NULL;
+ unsigned short port_min = 0;
+ unsigned short port_max = 0;
+ unsigned short port;
+ bool possibly_non_local = TRUE;
+ char buffer[STRERROR_LEN];
+ char *addr = NULL;
+
+ /* Step 1, figure out what is requested,
+ * accepted format :
+ * (ipv4|ipv6|domain|interface)?(:port(-range)?)?
+ */
+
+ if(data->set.str[STRING_FTPPORT] &&
+ (strlen(data->set.str[STRING_FTPPORT]) > 1)) {
+
+#ifdef ENABLE_IPV6
+ size_t addrlen = INET6_ADDRSTRLEN > strlen(string_ftpport) ?
+ INET6_ADDRSTRLEN : strlen(string_ftpport);
+#else
+ size_t addrlen = INET_ADDRSTRLEN > strlen(string_ftpport) ?
+ INET_ADDRSTRLEN : strlen(string_ftpport);
+#endif
+ char *ip_start = string_ftpport;
+ char *ip_end = NULL;
+ char *port_start = NULL;
+ char *port_sep = NULL;
+
+ addr = calloc(addrlen + 1, 1);
+ if(!addr) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+#ifdef ENABLE_IPV6
+ if(*string_ftpport == '[') {
+ /* [ipv6]:port(-range) */
+ ip_start = string_ftpport + 1;
+ ip_end = strchr(string_ftpport, ']');
+ if(ip_end)
+ strncpy(addr, ip_start, ip_end - ip_start);
+ }
+ else
+#endif
+ if(*string_ftpport == ':') {
+ /* :port */
+ ip_end = string_ftpport;
+ }
+ else {
+ ip_end = strchr(string_ftpport, ':');
+ if(ip_end) {
+ /* either ipv6 or (ipv4|domain|interface):port(-range) */
+#ifdef ENABLE_IPV6
+ if(Curl_inet_pton(AF_INET6, string_ftpport, sa6) == 1) {
+ /* ipv6 */
+ port_min = port_max = 0;
+ strcpy(addr, string_ftpport);
+ ip_end = NULL; /* this got no port ! */
+ }
+ else
+#endif
+ /* (ipv4|domain|interface):port(-range) */
+ strncpy(addr, string_ftpport, ip_end - ip_start);
+ }
+ else
+ /* ipv4|interface */
+ strcpy(addr, string_ftpport);
+ }
+
+ /* parse the port */
+ if(ip_end) {
+ port_start = strchr(ip_end, ':');
+ if(port_start) {
+ port_min = curlx_ultous(strtoul(port_start + 1, NULL, 10));
+ port_sep = strchr(port_start, '-');
+ if(port_sep) {
+ port_max = curlx_ultous(strtoul(port_sep + 1, NULL, 10));
+ }
+ else
+ port_max = port_min;
+ }
+ }
+
+ /* correct errors like:
+ * :1234-1230
+ * :-4711, in this case port_min is (unsigned)-1,
+ * therefore port_min > port_max for all cases
+ * but port_max = (unsigned)-1
+ */
+ if(port_min > port_max)
+ port_min = port_max = 0;
+
+ if(*addr != '\0') {
+ /* attempt to get the address of the given interface name */
+ switch(Curl_if2ip(conn->ip_addr->ai_family,
+#ifdef ENABLE_IPV6
+ Curl_ipv6_scope(conn->ip_addr->ai_addr),
+ conn->scope_id,
+#endif
+ addr, hbuf, sizeof(hbuf))) {
+ case IF2IP_NOT_FOUND:
+ /* not an interface, use the given string as host name instead */
+ host = addr;
+ break;
+ case IF2IP_AF_NOT_SUPPORTED:
+ goto out;
+ case IF2IP_FOUND:
+ host = hbuf; /* use the hbuf for host name */
+ }
+ }
+ else
+ /* there was only a port(-range) given, default the host */
+ host = NULL;
+ } /* data->set.ftpport */
+
+ if(!host) {
+ const char *r;
+ /* not an interface and not a host name, get default by extracting
+ the IP from the control connection */
+ sslen = sizeof(ss);
+ if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
+ failf(data, "getsockname() failed: %s",
+ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+ goto out;
+ }
+ switch(sa->sa_family) {
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ r = Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
+ break;
+#endif
+ default:
+ r = Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
+ break;
+ }
+ if(!r) {
+ goto out;
+ }
+ host = hbuf; /* use this host name */
+ possibly_non_local = FALSE; /* we know it is local now */
+ }
+
+ /* resolv ip/host to ip */
+ rc = Curl_resolv(data, host, 0, FALSE, &h);
+ if(rc == CURLRESOLV_PENDING)
+ (void)Curl_resolver_wait_resolv(data, &h);
+ if(h) {
+ res = h->addr;
+ /* when we return from this function, we can forget about this entry
+ to we can unlock it now already */
+ Curl_resolv_unlock(data, h);
+ } /* (h) */
+ else
+ res = NULL; /* failure! */
+
+ if(!res) {
+ failf(data, "failed to resolve the address provided to PORT: %s", host);
+ goto out;
+ }
+
+ host = NULL;
+
+ /* step 2, create a socket for the requested address */
+
+ portsock = CURL_SOCKET_BAD;
+ error = 0;
+ for(ai = res; ai; ai = ai->ai_next) {
+ if(Curl_socket(data, ai, NULL, &portsock)) {
+ error = SOCKERRNO;
+ continue;
+ }
+ break;
+ }
+ if(!ai) {
+ failf(data, "socket failure: %s",
+ Curl_strerror(error, buffer, sizeof(buffer)));
+ goto out;
+ }
+ DEBUGF(infof(data, "ftp_state_use_port(), opened socket"));
+
+ /* step 3, bind to a suitable local address */
+
+ memcpy(sa, ai->ai_addr, ai->ai_addrlen);
+ sslen = ai->ai_addrlen;
+
+ for(port = port_min; port <= port_max;) {
+ if(sa->sa_family == AF_INET)
+ sa4->sin_port = htons(port);
+#ifdef ENABLE_IPV6
+ else
+ sa6->sin6_port = htons(port);
+#endif
+ /* Try binding the given address. */
+ if(bind(portsock, sa, sslen) ) {
+ /* It failed. */
+ error = SOCKERRNO;
+ if(possibly_non_local && (error == EADDRNOTAVAIL)) {
+ /* The requested bind address is not local. Use the address used for
+ * the control connection instead and restart the port loop
+ */
+ infof(data, "bind(port=%hu) on non-local address failed: %s", port,
+ Curl_strerror(error, buffer, sizeof(buffer)));
+
+ sslen = sizeof(ss);
+ if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
+ failf(data, "getsockname() failed: %s",
+ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+ goto out;
+ }
+ port = port_min;
+ possibly_non_local = FALSE; /* don't try this again */
+ continue;
+ }
+ if(error != EADDRINUSE && error != EACCES) {
+ failf(data, "bind(port=%hu) failed: %s", port,
+ Curl_strerror(error, buffer, sizeof(buffer)));
+ goto out;
+ }
+ }
+ else
+ break;
+
+ port++;
+ }
+
+ /* maybe all ports were in use already */
+ if(port > port_max) {
+ failf(data, "bind() failed, we ran out of ports");
+ goto out;
+ }
+
+ /* get the name again after the bind() so that we can extract the
+ port number it uses now */
+ sslen = sizeof(ss);
+ if(getsockname(portsock, sa, &sslen)) {
+ failf(data, "getsockname() failed: %s",
+ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+ goto out;
+ }
+ DEBUGF(infof(data, "ftp_state_use_port(), socket bound to port %d", port));
+
+ /* step 4, listen on the socket */
+
+ if(listen(portsock, 1)) {
+ failf(data, "socket failure: %s",
+ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+ goto out;
+ }
+ DEBUGF(infof(data, "ftp_state_use_port(), listening on %d", port));
+
+ /* step 5, send the proper FTP command */
+
+ /* get a plain printable version of the numerical address to work with
+ below */
+ Curl_printable_address(ai, myhost, sizeof(myhost));
+
+#ifdef ENABLE_IPV6
+ if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
+ /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
+ request and enable EPRT again! */
+ conn->bits.ftp_use_eprt = TRUE;
+#endif
+
+ for(; fcmd != DONE; fcmd++) {
+
+ if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
+ /* if disabled, goto next */
+ continue;
+
+ if((PORT == fcmd) && sa->sa_family != AF_INET)
+ /* PORT is IPv4 only */
+ continue;
+
+ switch(sa->sa_family) {
+ case AF_INET:
+ port = ntohs(sa4->sin_port);
+ break;
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ port = ntohs(sa6->sin6_port);
+ break;
+#endif
+ default:
+ continue; /* might as well skip this */
+ }
+
+ if(EPRT == fcmd) {
+ /*
+ * Two fine examples from RFC2428;
+ *
+ * EPRT |1|132.235.1.2|6275|
+ *
+ * EPRT |2|1080::8:800:200C:417A|5282|
+ */
+
+ result = Curl_pp_sendf(data, &ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd],
+ sa->sa_family == AF_INET?1:2,
+ myhost, port);
+ if(result) {
+ failf(data, "Failure sending EPRT command: %s",
+ curl_easy_strerror(result));
+ goto out;
+ }
+ break;
+ }
+ if(PORT == fcmd) {
+ /* large enough for [IP address],[num],[num] */
+ char target[sizeof(myhost) + 20];
+ char *source = myhost;
+ char *dest = target;
+
+ /* translate x.x.x.x to x,x,x,x */
+ while(source && *source) {
+ if(*source == '.')
+ *dest = ',';
+ else
+ *dest = *source;
+ dest++;
+ source++;
+ }
+ *dest = 0;
+ msnprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff));
+
+ result = Curl_pp_sendf(data, &ftpc->pp, "%s %s", mode[fcmd], target);
+ if(result) {
+ failf(data, "Failure sending PORT command: %s",
+ curl_easy_strerror(result));
+ goto out;
+ }
+ break;
+ }
+ }
+
+ /* store which command was sent */
+ ftpc->count1 = fcmd;
+
+ /* Replace any filter on SECONDARY with one listeing on this socket */
+ result = Curl_conn_socket_accepted_set(data, conn, SECONDARYSOCKET,
+ &portsock);
+ if(result)
+ goto out;
+ portsock = CURL_SOCKET_BAD; /* now held in filter */
+ state(data, FTP_PORT);
+
+out:
+ if(result) {
+ state(data, FTP_STOP);
+ }
+ if(portsock != CURL_SOCKET_BAD)
+ Curl_closesocket(data, conn, portsock);
+ free(addr);
+ return result;
+}
+
+static CURLcode ftp_state_use_pasv(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ CURLcode result = CURLE_OK;
+ /*
+ Here's the executive summary on what to do:
+
+ PASV is RFC959, expect:
+ 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
+
+ LPSV is RFC1639, expect:
+ 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
+
+ EPSV is RFC2428, expect:
+ 229 Entering Extended Passive Mode (|||port|)
+
+ */
+
+ static const char mode[][5] = { "EPSV", "PASV" };
+ int modeoff;
+
+#ifdef PF_INET6
+ if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
+ /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
+ request and enable EPSV again! */
+ conn->bits.ftp_use_epsv = TRUE;
+#endif
+
+ modeoff = conn->bits.ftp_use_epsv?0:1;
+
+ result = Curl_pp_sendf(data, &ftpc->pp, "%s", mode[modeoff]);
+ if(!result) {
+ ftpc->count1 = modeoff;
+ state(data, FTP_PASV);
+ infof(data, "Connect data stream passively");
+ }
+ return result;
+}
+
+/*
+ * ftp_state_prepare_transfer() starts PORT, PASV or PRET etc.
+ *
+ * REST is the last command in the chain of commands when a "head"-like
+ * request is made. Thus, if an actual transfer is to be made this is where we
+ * take off for real.
+ */
+static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct FTP *ftp = data->req.p.ftp;
+ struct connectdata *conn = data->conn;
+
+ if(ftp->transfer != PPTRANSFER_BODY) {
+ /* doesn't transfer any data */
+
+ /* still possibly do PRE QUOTE jobs */
+ state(data, FTP_RETR_PREQUOTE);
+ result = ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE);
+ }
+ else if(data->set.ftp_use_port) {
+ /* We have chosen to use the PORT (or similar) command */
+ result = ftp_state_use_port(data, EPRT);
+ }
+ else {
+ /* We have chosen (this is default) to use the PASV (or similar) command */
+ if(data->set.ftp_use_pret) {
+ /* The user has requested that we send a PRET command
+ to prepare the server for the upcoming PASV */
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ if(!conn->proto.ftpc.file)
+ result = Curl_pp_sendf(data, &ftpc->pp, "PRET %s",
+ data->set.str[STRING_CUSTOMREQUEST]?
+ data->set.str[STRING_CUSTOMREQUEST]:
+ (data->state.list_only?"NLST":"LIST"));
+ else if(data->set.upload)
+ result = Curl_pp_sendf(data, &ftpc->pp, "PRET STOR %s",
+ conn->proto.ftpc.file);
+ else
+ result = Curl_pp_sendf(data, &ftpc->pp, "PRET RETR %s",
+ conn->proto.ftpc.file);
+ if(!result)
+ state(data, FTP_PRET);
+ }
+ else
+ result = ftp_state_use_pasv(data, conn);
+ }
+ return result;
+}
+
+static CURLcode ftp_state_rest(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct FTP *ftp = data->req.p.ftp;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+ if((ftp->transfer != PPTRANSFER_BODY) && ftpc->file) {
+ /* if a "head"-like request is being made (on a file) */
+
+ /* Determine if server can respond to REST command and therefore
+ whether it supports range */
+ result = Curl_pp_sendf(data, &ftpc->pp, "REST %d", 0);
+ if(!result)
+ state(data, FTP_REST);
+ }
+ else
+ result = ftp_state_prepare_transfer(data);
+
+ return result;
+}
+
+static CURLcode ftp_state_size(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct FTP *ftp = data->req.p.ftp;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+ if((ftp->transfer == PPTRANSFER_INFO) && ftpc->file) {
+ /* if a "head"-like request is being made (on a file) */
+
+ /* we know ftpc->file is a valid pointer to a file name */
+ result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
+ if(!result)
+ state(data, FTP_SIZE);
+ }
+ else
+ result = ftp_state_rest(data, conn);
+
+ return result;
+}
+
+static CURLcode ftp_state_list(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct FTP *ftp = data->req.p.ftp;
+ struct connectdata *conn = data->conn;
+
+ /* If this output is to be machine-parsed, the NLST command might be better
+ to use, since the LIST command output is not specified or standard in any
+ way. It has turned out that the NLST list output is not the same on all
+ servers either... */
+
+ /*
+ if FTPFILE_NOCWD was specified, we should add the path
+ as argument for the LIST / NLST / or custom command.
+ Whether the server will support this, is uncertain.
+
+ The other ftp_filemethods will CWD into dir/dir/ first and
+ then just do LIST (in that case: nothing to do here)
+ */
+ char *lstArg = NULL;
+ char *cmd;
+
+ if((data->set.ftp_filemethod == FTPFILE_NOCWD) && ftp->path) {
+ /* url-decode before evaluation: e.g. paths starting/ending with %2f */
+ const char *slashPos = NULL;
+ char *rawPath = NULL;
+ result = Curl_urldecode(ftp->path, 0, &rawPath, NULL, REJECT_CTRL);
+ if(result)
+ return result;
+
+ slashPos = strrchr(rawPath, '/');
+ if(slashPos) {
+ /* chop off the file part if format is dir/file otherwise remove
+ the trailing slash for dir/dir/ except for absolute path / */
+ size_t n = slashPos - rawPath;
+ if(n == 0)
+ ++n;
+
+ lstArg = rawPath;
+ lstArg[n] = '\0';
+ }
+ else
+ free(rawPath);
+ }
+
+ cmd = aprintf("%s%s%s",
+ data->set.str[STRING_CUSTOMREQUEST]?
+ data->set.str[STRING_CUSTOMREQUEST]:
+ (data->state.list_only?"NLST":"LIST"),
+ lstArg? " ": "",
+ lstArg? lstArg: "");
+ free(lstArg);
+
+ if(!cmd)
+ return CURLE_OUT_OF_MEMORY;
+
+ result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", cmd);
+ free(cmd);
+
+ if(!result)
+ state(data, FTP_LIST);
+
+ return result;
+}
+
+static CURLcode ftp_state_retr_prequote(struct Curl_easy *data)
+{
+ /* We've sent the TYPE, now we must send the list of prequote strings */
+ return ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE);
+}
+
+static CURLcode ftp_state_stor_prequote(struct Curl_easy *data)
+{
+ /* We've sent the TYPE, now we must send the list of prequote strings */
+ return ftp_state_quote(data, TRUE, FTP_STOR_PREQUOTE);
+}
+
+static CURLcode ftp_state_type(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct FTP *ftp = data->req.p.ftp;
+ struct connectdata *conn = data->conn;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+ /* If we have selected NOBODY and HEADER, it means that we only want file
+ information. Which in FTP can't be much more than the file size and
+ date. */
+ if(data->req.no_body && ftpc->file &&
+ ftp_need_type(conn, data->state.prefer_ascii)) {
+ /* The SIZE command is _not_ RFC 959 specified, and therefore many servers
+ may not support it! It is however the only way we have to get a file's
+ size! */
+
+ ftp->transfer = PPTRANSFER_INFO;
+ /* this means no actual transfer will be made */
+
+ /* Some servers return different sizes for different modes, and thus we
+ must set the proper type before we check the size */
+ result = ftp_nb_type(data, conn, data->state.prefer_ascii, FTP_TYPE);
+ if(result)
+ return result;
+ }
+ else
+ result = ftp_state_size(data, conn);
+
+ return result;
+}
+
+/* This is called after the CWD commands have been done in the beginning of
+ the DO phase */
+static CURLcode ftp_state_mdtm(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+ /* Requested time of file or time-depended transfer? */
+ if((data->set.get_filetime || data->set.timecondition) && ftpc->file) {
+
+ /* we have requested to get the modified-time of the file, this is a white
+ spot as the MDTM is not mentioned in RFC959 */
+ result = Curl_pp_sendf(data, &ftpc->pp, "MDTM %s", ftpc->file);
+
+ if(!result)
+ state(data, FTP_MDTM);
+ }
+ else
+ result = ftp_state_type(data);
+
+ return result;
+}
+
+
+/* This is called after the TYPE and possible quote commands have been sent */
+static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
+ bool sizechecked)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct FTP *ftp = data->req.p.ftp;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ bool append = data->set.remote_append;
+
+ if((data->state.resume_from && !sizechecked) ||
+ ((data->state.resume_from > 0) && sizechecked)) {
+ /* we're about to continue the uploading of a file */
+ /* 1. get already existing file's size. We use the SIZE command for this
+ which may not exist in the server! The SIZE command is not in
+ RFC959. */
+
+ /* 2. This used to set REST. But since we can do append, we
+ don't another ftp command. We just skip the source file
+ offset and then we APPEND the rest on the file instead */
+
+ /* 3. pass file-size number of bytes in the source file */
+ /* 4. lower the infilesize counter */
+ /* => transfer as usual */
+ int seekerr = CURL_SEEKFUNC_OK;
+
+ if(data->state.resume_from < 0) {
+ /* Got no given size to start from, figure it out */
+ result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
+ if(!result)
+ state(data, FTP_STOR_SIZE);
+ return result;
+ }
+
+ /* enable append */
+ append = TRUE;
+
+ /* Let's read off the proper amount of bytes from the input. */
+ if(conn->seek_func) {
+ Curl_set_in_callback(data, true);
+ seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
+ SEEK_SET);
+ Curl_set_in_callback(data, false);
+ }
+
+ if(seekerr != CURL_SEEKFUNC_OK) {
+ curl_off_t passed = 0;
+ if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
+ failf(data, "Could not seek stream");
+ return CURLE_FTP_COULDNT_USE_REST;
+ }
+ /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
+ do {
+ size_t readthisamountnow =
+ (data->state.resume_from - passed > data->set.buffer_size) ?
+ (size_t)data->set.buffer_size :
+ curlx_sotouz(data->state.resume_from - passed);
+
+ size_t actuallyread =
+ data->state.fread_func(data->state.buffer, 1, readthisamountnow,
+ data->state.in);
+
+ passed += actuallyread;
+ if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
+ /* this checks for greater-than only to make sure that the
+ CURL_READFUNC_ABORT return code still aborts */
+ failf(data, "Failed to read data");
+ return CURLE_FTP_COULDNT_USE_REST;
+ }
+ } while(passed < data->state.resume_from);
+ }
+ /* now, decrease the size of the read */
+ if(data->state.infilesize>0) {
+ data->state.infilesize -= data->state.resume_from;
+
+ if(data->state.infilesize <= 0) {
+ infof(data, "File already completely uploaded");
+
+ /* no data to transfer */
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
+
+ /* Set ->transfer so that we won't get any error in
+ * ftp_done() because we didn't transfer anything! */
+ ftp->transfer = PPTRANSFER_NONE;
+
+ state(data, FTP_STOP);
+ return CURLE_OK;
+ }
+ }
+ /* we've passed, proceed as normal */
+ } /* resume_from */
+
+ result = Curl_pp_sendf(data, &ftpc->pp, append?"APPE %s":"STOR %s",
+ ftpc->file);
+ if(!result)
+ state(data, FTP_STOR);
+
+ return result;
+}
+
+static CURLcode ftp_state_quote(struct Curl_easy *data,
+ bool init,
+ ftpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct FTP *ftp = data->req.p.ftp;
+ struct connectdata *conn = data->conn;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ bool quote = FALSE;
+ struct curl_slist *item;
+
+ switch(instate) {
+ case FTP_QUOTE:
+ default:
+ item = data->set.quote;
+ break;
+ case FTP_RETR_PREQUOTE:
+ case FTP_STOR_PREQUOTE:
+ item = data->set.prequote;
+ break;
+ case FTP_POSTQUOTE:
+ item = data->set.postquote;
+ break;
+ }
+
+ /*
+ * This state uses:
+ * 'count1' to iterate over the commands to send
+ * 'count2' to store whether to allow commands to fail
+ */
+
+ if(init)
+ ftpc->count1 = 0;
+ else
+ ftpc->count1++;
+
+ if(item) {
+ int i = 0;
+
+ /* Skip count1 items in the linked list */
+ while((i< ftpc->count1) && item) {
+ item = item->next;
+ i++;
+ }
+ if(item) {
+ char *cmd = item->data;
+ if(cmd[0] == '*') {
+ cmd++;
+ ftpc->count2 = 1; /* the sent command is allowed to fail */
+ }
+ else
+ ftpc->count2 = 0; /* failure means cancel operation */
+
+ result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
+ if(result)
+ return result;
+ state(data, instate);
+ quote = TRUE;
+ }
+ }
+
+ if(!quote) {
+ /* No more quote to send, continue to ... */
+ switch(instate) {
+ case FTP_QUOTE:
+ default:
+ result = ftp_state_cwd(data, conn);
+ break;
+ case FTP_RETR_PREQUOTE:
+ if(ftp->transfer != PPTRANSFER_BODY)
+ state(data, FTP_STOP);
+ else {
+ if(ftpc->known_filesize != -1) {
+ Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
+ result = ftp_state_retr(data, ftpc->known_filesize);
+ }
+ else {
+ if(data->set.ignorecl || data->state.prefer_ascii) {
+ /* 'ignorecl' is used to support download of growing files. It
+ prevents the state machine from requesting the file size from
+ the server. With an unknown file size the download continues
+ until the server terminates it, otherwise the client stops if
+ the received byte count exceeds the reported file size. Set
+ option CURLOPT_IGNORE_CONTENT_LENGTH to 1 to enable this
+ behavior.
+
+ In addition: asking for the size for 'TYPE A' transfers is not
+ constructive since servers don't report the converted size. So
+ skip it.
+ */
+ result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
+ if(!result)
+ state(data, FTP_RETR);
+ }
+ else {
+ result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
+ if(!result)
+ state(data, FTP_RETR_SIZE);
+ }
+ }
+ }
+ break;
+ case FTP_STOR_PREQUOTE:
+ result = ftp_state_ul_setup(data, FALSE);
+ break;
+ case FTP_POSTQUOTE:
+ break;
+ }
+ }
+
+ return result;
+}
+
+/* called from ftp_state_pasv_resp to switch to PASV in case of EPSV
+ problems */
+static CURLcode ftp_epsv_disable(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+
+ if(conn->bits.ipv6
+#ifndef CURL_DISABLE_PROXY
+ && !(conn->bits.tunnel_proxy || conn->bits.socksproxy)
+#endif
+ ) {
+ /* We can't disable EPSV when doing IPv6, so this is instead a fail */
+ failf(data, "Failed EPSV attempt, exiting");
+ return CURLE_WEIRD_SERVER_REPLY;
+ }
+
+ infof(data, "Failed EPSV attempt. Disabling EPSV");
+ /* disable it for next transfer */
+ conn->bits.ftp_use_epsv = FALSE;
+ Curl_conn_close(data, SECONDARYSOCKET);
+ Curl_conn_cf_discard_all(data, conn, SECONDARYSOCKET);
+ data->state.errorbuf = FALSE; /* allow error message to get
+ rewritten */
+ result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PASV");
+ if(!result) {
+ conn->proto.ftpc.count1++;
+ /* remain in/go to the FTP_PASV state */
+ state(data, FTP_PASV);
+ }
+ return result;
+}
+
+
+static char *control_address(struct connectdata *conn)
+{
+ /* Returns the control connection IP address.
+ If a proxy tunnel is used, returns the original host name instead, because
+ the effective control connection address is the proxy address,
+ not the ftp host. */
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.tunnel_proxy || conn->bits.socksproxy)
+ return conn->host.name;
+#endif
+ return conn->primary_ip;
+}
+
+static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
+ int ftpcode)
+{
+ struct connectdata *conn = data->conn;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ CURLcode result;
+ struct Curl_dns_entry *addr = NULL;
+ enum resolve_t rc;
+ unsigned short connectport; /* the local port connect() should use! */
+ char *str = &data->state.buffer[4]; /* start on the first letter */
+
+ /* if we come here again, make sure the former name is cleared */
+ Curl_safefree(ftpc->newhost);
+
+ if((ftpc->count1 == 0) &&
+ (ftpcode == 229)) {
+ /* positive EPSV response */
+ char *ptr = strchr(str, '(');
+ if(ptr) {
+ unsigned int num;
+ char separator[4];
+ ptr++;
+ if(5 == sscanf(ptr, "%c%c%c%u%c",
+ &separator[0],
+ &separator[1],
+ &separator[2],
+ &num,
+ &separator[3])) {
+ const char sep1 = separator[0];
+ int i;
+
+ /* The four separators should be identical, or else this is an oddly
+ formatted reply and we bail out immediately. */
+ for(i = 1; i<4; i++) {
+ if(separator[i] != sep1) {
+ ptr = NULL; /* set to NULL to signal error */
+ break;
+ }
+ }
+ if(num > 0xffff) {
+ failf(data, "Illegal port number in EPSV reply");
+ return CURLE_FTP_WEIRD_PASV_REPLY;
+ }
+ if(ptr) {
+ ftpc->newport = (unsigned short)(num & 0xffff);
+ ftpc->newhost = strdup(control_address(conn));
+ if(!ftpc->newhost)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ else
+ ptr = NULL;
+ }
+ if(!ptr) {
+ failf(data, "Weirdly formatted EPSV reply");
+ return CURLE_FTP_WEIRD_PASV_REPLY;
+ }
+ }
+ else if((ftpc->count1 == 1) &&
+ (ftpcode == 227)) {
+ /* positive PASV response */
+ unsigned int ip[4] = {0, 0, 0, 0};
+ unsigned int port[2] = {0, 0};
+
+ /*
+ * Scan for a sequence of six comma-separated numbers and use them as
+ * IP+port indicators.
+ *
+ * Found reply-strings include:
+ * "227 Entering Passive Mode (127,0,0,1,4,51)"
+ * "227 Data transfer will passively listen to 127,0,0,1,4,51"
+ * "227 Entering passive mode. 127,0,0,1,4,51"
+ */
+ while(*str) {
+ if(6 == sscanf(str, "%u,%u,%u,%u,%u,%u",
+ &ip[0], &ip[1], &ip[2], &ip[3],
+ &port[0], &port[1]))
+ break;
+ str++;
+ }
+
+ if(!*str || (ip[0] > 255) || (ip[1] > 255) || (ip[2] > 255) ||
+ (ip[3] > 255) || (port[0] > 255) || (port[1] > 255) ) {
+ failf(data, "Couldn't interpret the 227-response");
+ return CURLE_FTP_WEIRD_227_FORMAT;
+ }
+
+ /* we got OK from server */
+ if(data->set.ftp_skip_ip) {
+ /* told to ignore the remotely given IP but instead use the host we used
+ for the control connection */
+ infof(data, "Skip %u.%u.%u.%u for data connection, re-use %s instead",
+ ip[0], ip[1], ip[2], ip[3],
+ conn->host.name);
+ ftpc->newhost = strdup(control_address(conn));
+ }
+ else
+ ftpc->newhost = aprintf("%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
+
+ if(!ftpc->newhost)
+ return CURLE_OUT_OF_MEMORY;
+
+ ftpc->newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff);
+ }
+ else if(ftpc->count1 == 0) {
+ /* EPSV failed, move on to PASV */
+ return ftp_epsv_disable(data, conn);
+ }
+ else {
+ failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
+ return CURLE_FTP_WEIRD_PASV_REPLY;
+ }
+
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.proxy) {
+ /*
+ * This connection uses a proxy and we need to connect to the proxy again
+ * here. We don't want to rely on a former host lookup that might've
+ * expired now, instead we remake the lookup here and now!
+ */
+ const char * const host_name = conn->bits.socksproxy ?
+ conn->socks_proxy.host.name : conn->http_proxy.host.name;
+ rc = Curl_resolv(data, host_name, conn->port, FALSE, &addr);
+ if(rc == CURLRESOLV_PENDING)
+ /* BLOCKING, ignores the return code but 'addr' will be NULL in
+ case of failure */
+ (void)Curl_resolver_wait_resolv(data, &addr);
+
+ connectport =
+ (unsigned short)conn->port; /* we connect to the proxy's port */
+
+ if(!addr) {
+ failf(data, "Can't resolve proxy host %s:%hu", host_name, connectport);
+ return CURLE_COULDNT_RESOLVE_PROXY;
+ }
+ }
+ else
+#endif
+ {
+ /* normal, direct, ftp connection */
+ DEBUGASSERT(ftpc->newhost);
+
+ /* postponed address resolution in case of tcp fastopen */
+ if(conn->bits.tcp_fastopen && !conn->bits.reuse && !ftpc->newhost[0]) {
+ Curl_conninfo_remote(data, conn, conn->sock[FIRSTSOCKET]);
+ Curl_safefree(ftpc->newhost);
+ ftpc->newhost = strdup(control_address(conn));
+ if(!ftpc->newhost)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ rc = Curl_resolv(data, ftpc->newhost, ftpc->newport, FALSE, &addr);
+ if(rc == CURLRESOLV_PENDING)
+ /* BLOCKING */
+ (void)Curl_resolver_wait_resolv(data, &addr);
+
+ connectport = ftpc->newport; /* we connect to the remote port */
+
+ if(!addr) {
+ failf(data, "Can't resolve new host %s:%hu", ftpc->newhost, connectport);
+ return CURLE_FTP_CANT_GET_HOST;
+ }
+ }
+
+ result = Curl_conn_setup(data, conn, SECONDARYSOCKET, addr,
+ conn->bits.ftp_use_data_ssl?
+ CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE);
+
+ if(result) {
+ Curl_resolv_unlock(data, addr); /* we're done using this address */
+ if(ftpc->count1 == 0 && ftpcode == 229)
+ return ftp_epsv_disable(data, conn);
+
+ return result;
+ }
+
+
+ /*
+ * When this is used from the multi interface, this might've returned with
+ * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
+ * connect to connect.
+ */
+
+ if(data->set.verbose)
+ /* this just dumps information about this second connection */
+ ftp_pasv_verbose(data, addr->addr, ftpc->newhost, connectport);
+
+ Curl_resolv_unlock(data, addr); /* we're done using this address */
+
+ Curl_safefree(conn->secondaryhostname);
+ conn->secondary_port = ftpc->newport;
+ conn->secondaryhostname = strdup(ftpc->newhost);
+ if(!conn->secondaryhostname)
+ return CURLE_OUT_OF_MEMORY;
+
+ conn->bits.do_more = TRUE;
+ state(data, FTP_STOP); /* this phase is completed */
+
+ return result;
+}
+
+static CURLcode ftp_state_port_resp(struct Curl_easy *data,
+ int ftpcode)
+{
+ struct connectdata *conn = data->conn;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ ftpport fcmd = (ftpport)ftpc->count1;
+ CURLcode result = CURLE_OK;
+
+ /* The FTP spec tells a positive response should have code 200.
+ Be more permissive here to tolerate deviant servers. */
+ if(ftpcode / 100 != 2) {
+ /* the command failed */
+
+ if(EPRT == fcmd) {
+ infof(data, "disabling EPRT usage");
+ conn->bits.ftp_use_eprt = FALSE;
+ }
+ fcmd++;
+
+ if(fcmd == DONE) {
+ failf(data, "Failed to do PORT");
+ result = CURLE_FTP_PORT_FAILED;
+ }
+ else
+ /* try next */
+ result = ftp_state_use_port(data, fcmd);
+ }
+ else {
+ infof(data, "Connect data stream actively");
+ state(data, FTP_STOP); /* end of DO phase */
+ result = ftp_dophase_done(data, FALSE);
+ }
+
+ return result;
+}
+
+static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
+ int ftpcode)
+{
+ CURLcode result = CURLE_OK;
+ struct FTP *ftp = data->req.p.ftp;
+ struct connectdata *conn = data->conn;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+ switch(ftpcode) {
+ case 213:
+ {
+ /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
+ last .sss part is optional and means fractions of a second */
+ int year, month, day, hour, minute, second;
+ if(6 == sscanf(&data->state.buffer[4], "%04d%02d%02d%02d%02d%02d",
+ &year, &month, &day, &hour, &minute, &second)) {
+ /* we have a time, reformat it */
+ char timebuf[24];
+ msnprintf(timebuf, sizeof(timebuf),
+ "%04d%02d%02d %02d:%02d:%02d GMT",
+ year, month, day, hour, minute, second);
+ /* now, convert this into a time() value: */
+ data->info.filetime = Curl_getdate_capped(timebuf);
+ }
+
+#ifdef CURL_FTP_HTTPSTYLE_HEAD
+ /* If we asked for a time of the file and we actually got one as well,
+ we "emulate" an HTTP-style header in our output. */
+
+ if(data->req.no_body &&
+ ftpc->file &&
+ data->set.get_filetime &&
+ (data->info.filetime >= 0) ) {
+ char headerbuf[128];
+ int headerbuflen;
+ time_t filetime = data->info.filetime;
+ struct tm buffer;
+ const struct tm *tm = &buffer;
+
+ result = Curl_gmtime(filetime, &buffer);
+ if(result)
+ return result;
+
+ /* format: "Tue, 15 Nov 1994 12:45:26" */
+ headerbuflen = msnprintf(headerbuf, sizeof(headerbuf),
+ "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
+ Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
+ tm->tm_mday,
+ Curl_month[tm->tm_mon],
+ tm->tm_year + 1900,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec);
+ result = Curl_client_write(data, CLIENTWRITE_BOTH, headerbuf,
+ headerbuflen);
+ if(result)
+ return result;
+ } /* end of a ridiculous amount of conditionals */
+#endif
+ }
+ break;
+ default:
+ infof(data, "unsupported MDTM reply format");
+ break;
+ case 550: /* 550 is used for several different problems, e.g.
+ "No such file or directory" or "Permission denied".
+ It does not mean that the file does not exist at all. */
+ infof(data, "MDTM failed: file does not exist or permission problem,"
+ " continuing");
+ break;
+ }
+
+ if(data->set.timecondition) {
+ if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
+ switch(data->set.timecondition) {
+ case CURL_TIMECOND_IFMODSINCE:
+ default:
+ if(data->info.filetime <= data->set.timevalue) {
+ infof(data, "The requested document is not new enough");
+ ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
+ data->info.timecond = TRUE;
+ state(data, FTP_STOP);
+ return CURLE_OK;
+ }
+ break;
+ case CURL_TIMECOND_IFUNMODSINCE:
+ if(data->info.filetime > data->set.timevalue) {
+ infof(data, "The requested document is not old enough");
+ ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
+ data->info.timecond = TRUE;
+ state(data, FTP_STOP);
+ return CURLE_OK;
+ }
+ break;
+ } /* switch */
+ }
+ else {
+ infof(data, "Skipping time comparison");
+ }
+ }
+
+ if(!result)
+ result = ftp_state_type(data);
+
+ return result;
+}
+
+static CURLcode ftp_state_type_resp(struct Curl_easy *data,
+ int ftpcode,
+ ftpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+
+ if(ftpcode/100 != 2) {
+ /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a
+ successful 'TYPE I'. While that is not as RFC959 says, it is still a
+ positive response code and we allow that. */
+ failf(data, "Couldn't set desired mode");
+ return CURLE_FTP_COULDNT_SET_TYPE;
+ }
+ if(ftpcode != 200)
+ infof(data, "Got a %03d response code instead of the assumed 200",
+ ftpcode);
+
+ if(instate == FTP_TYPE)
+ result = ftp_state_size(data, conn);
+ else if(instate == FTP_LIST_TYPE)
+ result = ftp_state_list(data);
+ else if(instate == FTP_RETR_TYPE)
+ result = ftp_state_retr_prequote(data);
+ else if(instate == FTP_STOR_TYPE)
+ result = ftp_state_stor_prequote(data);
+
+ return result;
+}
+
+static CURLcode ftp_state_retr(struct Curl_easy *data,
+ curl_off_t filesize)
+{
+ CURLcode result = CURLE_OK;
+ struct FTP *ftp = data->req.p.ftp;
+ struct connectdata *conn = data->conn;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+ DEBUGF(infof(data, "ftp_state_retr()"));
+ if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
+ failf(data, "Maximum file size exceeded");
+ return CURLE_FILESIZE_EXCEEDED;
+ }
+ ftp->downloadsize = filesize;
+
+ if(data->state.resume_from) {
+ /* We always (attempt to) get the size of downloads, so it is done before
+ this even when not doing resumes. */
+ if(filesize == -1) {
+ infof(data, "ftp server doesn't support SIZE");
+ /* We couldn't get the size and therefore we can't know if there really
+ is a part of the file left to get, although the server will just
+ close the connection when we start the connection so it won't cause
+ us any harm, just not make us exit as nicely. */
+ }
+ else {
+ /* We got a file size report, so we check that there actually is a
+ part of the file left to get, or else we go home. */
+ if(data->state.resume_from< 0) {
+ /* We're supposed to download the last abs(from) bytes */
+ if(filesize < -data->state.resume_from) {
+ failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
+ ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
+ data->state.resume_from, filesize);
+ return CURLE_BAD_DOWNLOAD_RESUME;
+ }
+ /* convert to size to download */
+ ftp->downloadsize = -data->state.resume_from;
+ /* download from where? */
+ data->state.resume_from = filesize - ftp->downloadsize;
+ }
+ else {
+ if(filesize < data->state.resume_from) {
+ failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
+ ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
+ data->state.resume_from, filesize);
+ return CURLE_BAD_DOWNLOAD_RESUME;
+ }
+ /* Now store the number of bytes we are expected to download */
+ ftp->downloadsize = filesize-data->state.resume_from;
+ }
+ }
+
+ if(ftp->downloadsize == 0) {
+ /* no data to transfer */
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
+ infof(data, "File already completely downloaded");
+
+ /* Set ->transfer so that we won't get any error in ftp_done()
+ * because we didn't transfer the any file */
+ ftp->transfer = PPTRANSFER_NONE;
+ state(data, FTP_STOP);
+ return CURLE_OK;
+ }
+
+ /* Set resume file transfer offset */
+ infof(data, "Instructs server to resume from offset %"
+ CURL_FORMAT_CURL_OFF_T, data->state.resume_from);
+
+ result = Curl_pp_sendf(data, &ftpc->pp, "REST %" CURL_FORMAT_CURL_OFF_T,
+ data->state.resume_from);
+ if(!result)
+ state(data, FTP_RETR_REST);
+ }
+ else {
+ /* no resume */
+ result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
+ if(!result)
+ state(data, FTP_RETR);
+ }
+
+ return result;
+}
+
+static CURLcode ftp_state_size_resp(struct Curl_easy *data,
+ int ftpcode,
+ ftpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ curl_off_t filesize = -1;
+ char *buf = data->state.buffer;
+
+ /* get the size from the ascii string: */
+ if(ftpcode == 213) {
+ /* To allow servers to prepend "rubbish" in the response string, we scan
+ for all the digits at the end of the response and parse only those as a
+ number. */
+ char *start = &buf[4];
+ char *fdigit = strchr(start, '\r');
+ if(fdigit) {
+ do
+ fdigit--;
+ while(ISDIGIT(*fdigit) && (fdigit > start));
+ if(!ISDIGIT(*fdigit))
+ fdigit++;
+ }
+ else
+ fdigit = start;
+ /* ignores parsing errors, which will make the size remain unknown */
+ (void)curlx_strtoofft(fdigit, NULL, 10, &filesize);
+
+ }
+ else if(ftpcode == 550) { /* "No such file or directory" */
+ /* allow a SIZE failure for (resumed) uploads, when probing what command
+ to use */
+ if(instate != FTP_STOR_SIZE) {
+ failf(data, "The file does not exist");
+ return CURLE_REMOTE_FILE_NOT_FOUND;
+ }
+ }
+
+ if(instate == FTP_SIZE) {
+#ifdef CURL_FTP_HTTPSTYLE_HEAD
+ if(-1 != filesize) {
+ char clbuf[128];
+ int clbuflen = msnprintf(clbuf, sizeof(clbuf),
+ "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", filesize);
+ result = Curl_client_write(data, CLIENTWRITE_BOTH, clbuf, clbuflen);
+ if(result)
+ return result;
+ }
+#endif
+ Curl_pgrsSetDownloadSize(data, filesize);
+ result = ftp_state_rest(data, data->conn);
+ }
+ else if(instate == FTP_RETR_SIZE) {
+ Curl_pgrsSetDownloadSize(data, filesize);
+ result = ftp_state_retr(data, filesize);
+ }
+ else if(instate == FTP_STOR_SIZE) {
+ data->state.resume_from = filesize;
+ result = ftp_state_ul_setup(data, TRUE);
+ }
+
+ return result;
+}
+
+static CURLcode ftp_state_rest_resp(struct Curl_easy *data,
+ struct connectdata *conn,
+ int ftpcode,
+ ftpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+ switch(instate) {
+ case FTP_REST:
+ default:
+#ifdef CURL_FTP_HTTPSTYLE_HEAD
+ if(ftpcode == 350) {
+ char buffer[24]= { "Accept-ranges: bytes\r\n" };
+ result = Curl_client_write(data, CLIENTWRITE_BOTH, buffer,
+ strlen(buffer));
+ if(result)
+ return result;
+ }
+#endif
+ result = ftp_state_prepare_transfer(data);
+ break;
+
+ case FTP_RETR_REST:
+ if(ftpcode != 350) {
+ failf(data, "Couldn't use REST");
+ result = CURLE_FTP_COULDNT_USE_REST;
+ }
+ else {
+ result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
+ if(!result)
+ state(data, FTP_RETR);
+ }
+ break;
+ }
+
+ return result;
+}
+
+static CURLcode ftp_state_stor_resp(struct Curl_easy *data,
+ int ftpcode, ftpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+
+ if(ftpcode >= 400) {
+ failf(data, "Failed FTP upload: %0d", ftpcode);
+ state(data, FTP_STOP);
+ /* oops, we never close the sockets! */
+ return CURLE_UPLOAD_FAILED;
+ }
+
+ conn->proto.ftpc.state_saved = instate;
+
+ /* PORT means we are now awaiting the server to connect to us. */
+ if(data->set.ftp_use_port) {
+ bool connected;
+
+ state(data, FTP_STOP); /* no longer in STOR state */
+
+ result = AllowServerConnect(data, &connected);
+ if(result)
+ return result;
+
+ if(!connected) {
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ infof(data, "Data conn was not available immediately");
+ ftpc->wait_data_conn = TRUE;
+ }
+
+ return CURLE_OK;
+ }
+ return InitiateTransfer(data);
+}
+
+/* for LIST and RETR responses */
+static CURLcode ftp_state_get_resp(struct Curl_easy *data,
+ int ftpcode,
+ ftpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct FTP *ftp = data->req.p.ftp;
+ struct connectdata *conn = data->conn;
+
+ if((ftpcode == 150) || (ftpcode == 125)) {
+
+ /*
+ A;
+ 150 Opening BINARY mode data connection for /etc/passwd (2241
+ bytes). (ok, the file is being transferred)
+
+ B:
+ 150 Opening ASCII mode data connection for /bin/ls
+
+ C:
+ 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
+
+ D:
+ 150 Opening ASCII mode data connection for [file] (0.0.0.0,0) (545 bytes)
+
+ E:
+ 125 Data connection already open; Transfer starting. */
+
+ curl_off_t size = -1; /* default unknown size */
+
+
+ /*
+ * It appears that there are FTP-servers that return size 0 for files when
+ * SIZE is used on the file while being in BINARY mode. To work around
+ * that (stupid) behavior, we attempt to parse the RETR response even if
+ * the SIZE returned size zero.
+ *
+ * Debugging help from Salvatore Sorrentino on February 26, 2003.
+ */
+
+ if((instate != FTP_LIST) &&
+ !data->state.prefer_ascii &&
+ !data->set.ignorecl &&
+ (ftp->downloadsize < 1)) {
+ /*
+ * It seems directory listings either don't show the size or very
+ * often uses size 0 anyway. ASCII transfers may very well turn out
+ * that the transferred amount of data is not the same as this line
+ * tells, why using this number in those cases only confuses us.
+ *
+ * Example D above makes this parsing a little tricky */
+ char *bytes;
+ char *buf = data->state.buffer;
+ bytes = strstr(buf, " bytes");
+ if(bytes) {
+ long in = (long)(--bytes-buf);
+ /* this is a hint there is size information in there! ;-) */
+ while(--in) {
+ /* scan for the left parenthesis and break there */
+ if('(' == *bytes)
+ break;
+ /* skip only digits */
+ if(!ISDIGIT(*bytes)) {
+ bytes = NULL;
+ break;
+ }
+ /* one more estep backwards */
+ bytes--;
+ }
+ /* if we have nothing but digits: */
+ if(bytes) {
+ ++bytes;
+ /* get the number! */
+ (void)curlx_strtoofft(bytes, NULL, 10, &size);
+ }
+ }
+ }
+ else if(ftp->downloadsize > -1)
+ size = ftp->downloadsize;
+
+ if(size > data->req.maxdownload && data->req.maxdownload > 0)
+ size = data->req.size = data->req.maxdownload;
+ else if((instate != FTP_LIST) && (data->state.prefer_ascii))
+ size = -1; /* kludge for servers that understate ASCII mode file size */
+
+ infof(data, "Maxdownload = %" CURL_FORMAT_CURL_OFF_T,
+ data->req.maxdownload);
+
+ if(instate != FTP_LIST)
+ infof(data, "Getting file with size: %" CURL_FORMAT_CURL_OFF_T,
+ size);
+
+ /* FTP download: */
+ conn->proto.ftpc.state_saved = instate;
+ conn->proto.ftpc.retr_size_saved = size;
+
+ if(data->set.ftp_use_port) {
+ bool connected;
+
+ result = AllowServerConnect(data, &connected);
+ if(result)
+ return result;
+
+ if(!connected) {
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ infof(data, "Data conn was not available immediately");
+ state(data, FTP_STOP);
+ ftpc->wait_data_conn = TRUE;
+ }
+ }
+ else
+ return InitiateTransfer(data);
+ }
+ else {
+ if((instate == FTP_LIST) && (ftpcode == 450)) {
+ /* simply no matching files in the dir listing */
+ ftp->transfer = PPTRANSFER_NONE; /* don't download anything */
+ state(data, FTP_STOP); /* this phase is over */
+ }
+ else {
+ failf(data, "RETR response: %03d", ftpcode);
+ return instate == FTP_RETR && ftpcode == 550?
+ CURLE_REMOTE_FILE_NOT_FOUND:
+ CURLE_FTP_COULDNT_RETR_FILE;
+ }
+ }
+
+ return result;
+}
+
+/* after USER, PASS and ACCT */
+static CURLcode ftp_state_loggedin(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+
+ if(conn->bits.ftp_use_control_ssl) {
+ /* PBSZ = PROTECTION BUFFER SIZE.
+
+ The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
+
+ Specifically, the PROT command MUST be preceded by a PBSZ
+ command and a PBSZ command MUST be preceded by a successful
+ security data exchange (the TLS negotiation in this case)
+
+ ... (and on page 8):
+
+ Thus the PBSZ command must still be issued, but must have a
+ parameter of '0' to indicate that no buffering is taking place
+ and the data connection should not be encapsulated.
+ */
+ result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "PBSZ %d", 0);
+ if(!result)
+ state(data, FTP_PBSZ);
+ }
+ else {
+ result = ftp_state_pwd(data, conn);
+ }
+ return result;
+}
+
+/* for USER and PASS responses */
+static CURLcode ftp_state_user_resp(struct Curl_easy *data,
+ int ftpcode,
+ ftpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ (void)instate; /* no use for this yet */
+
+ /* some need password anyway, and others just return 2xx ignored */
+ if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
+ /* 331 Password required for ...
+ (the server requires to send the user's password too) */
+ result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s",
+ conn->passwd?conn->passwd:"");
+ if(!result)
+ state(data, FTP_PASS);
+ }
+ else if(ftpcode/100 == 2) {
+ /* 230 User ... logged in.
+ (the user logged in with or without password) */
+ result = ftp_state_loggedin(data);
+ }
+ else if(ftpcode == 332) {
+ if(data->set.str[STRING_FTP_ACCOUNT]) {
+ result = Curl_pp_sendf(data, &ftpc->pp, "ACCT %s",
+ data->set.str[STRING_FTP_ACCOUNT]);
+ if(!result)
+ state(data, FTP_ACCT);
+ }
+ else {
+ failf(data, "ACCT requested but none available");
+ result = CURLE_LOGIN_DENIED;
+ }
+ }
+ else {
+ /* All other response codes, like:
+
+ 530 User ... access denied
+ (the server denies to log the specified user) */
+
+ if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] &&
+ !ftpc->ftp_trying_alternative) {
+ /* Ok, USER failed. Let's try the supplied command. */
+ result =
+ Curl_pp_sendf(data, &ftpc->pp, "%s",
+ data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
+ if(!result) {
+ ftpc->ftp_trying_alternative = TRUE;
+ state(data, FTP_USER);
+ }
+ }
+ else {
+ failf(data, "Access denied: %03d", ftpcode);
+ result = CURLE_LOGIN_DENIED;
+ }
+ }
+ return result;
+}
+
+/* for ACCT response */
+static CURLcode ftp_state_acct_resp(struct Curl_easy *data,
+ int ftpcode)
+{
+ CURLcode result = CURLE_OK;
+ if(ftpcode != 230) {
+ failf(data, "ACCT rejected by server: %03d", ftpcode);
+ result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
+ }
+ else
+ result = ftp_state_loggedin(data);
+
+ return result;
+}
+
+
+static CURLcode ftp_statemachine(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result;
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ int ftpcode;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct pingpong *pp = &ftpc->pp;
+ static const char ftpauth[][4] = { "SSL", "TLS" };
+ size_t nread = 0;
+
+ if(pp->sendleft)
+ return Curl_pp_flushsend(data, pp);
+
+ result = ftp_readresp(data, sock, pp, &ftpcode, &nread);
+ if(result)
+ return result;
+
+ if(ftpcode) {
+ /* we have now received a full FTP server response */
+ switch(ftpc->state) {
+ case FTP_WAIT220:
+ if(ftpcode == 230) {
+ /* 230 User logged in - already! Take as 220 if TLS required. */
+ if(data->set.use_ssl <= CURLUSESSL_TRY ||
+ conn->bits.ftp_use_control_ssl)
+ return ftp_state_user_resp(data, ftpcode, ftpc->state);
+ }
+ else if(ftpcode != 220) {
+ failf(data, "Got a %03d ftp-server response when 220 was expected",
+ ftpcode);
+ return CURLE_WEIRD_SERVER_REPLY;
+ }
+
+ /* We have received a 220 response fine, now we proceed. */
+#ifdef HAVE_GSSAPI
+ if(data->set.krb) {
+ /* If not anonymous login, try a secure login. Note that this
+ procedure is still BLOCKING. */
+
+ Curl_sec_request_prot(conn, "private");
+ /* We set private first as default, in case the line below fails to
+ set a valid level */
+ Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
+
+ if(Curl_sec_login(data, conn)) {
+ failf(data, "secure login failed");
+ return CURLE_WEIRD_SERVER_REPLY;
+ }
+ infof(data, "Authentication successful");
+ }
+#endif
+
+ if(data->set.use_ssl && !conn->bits.ftp_use_control_ssl) {
+ /* We don't have a SSL/TLS control connection yet, but FTPS is
+ requested. Try a FTPS connection now */
+
+ ftpc->count3 = 0;
+ switch(data->set.ftpsslauth) {
+ case CURLFTPAUTH_DEFAULT:
+ case CURLFTPAUTH_SSL:
+ ftpc->count2 = 1; /* add one to get next */
+ ftpc->count1 = 0;
+ break;
+ case CURLFTPAUTH_TLS:
+ ftpc->count2 = -1; /* subtract one to get next */
+ ftpc->count1 = 1;
+ break;
+ default:
+ failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
+ (int)data->set.ftpsslauth);
+ return CURLE_UNKNOWN_OPTION; /* we don't know what to do */
+ }
+ result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s",
+ ftpauth[ftpc->count1]);
+ if(!result)
+ state(data, FTP_AUTH);
+ }
+ else
+ result = ftp_state_user(data, conn);
+ break;
+
+ case FTP_AUTH:
+ /* we have gotten the response to a previous AUTH command */
+
+ if(pp->cache_size)
+ return CURLE_WEIRD_SERVER_REPLY; /* Forbid pipelining in response. */
+
+ /* RFC2228 (page 5) says:
+ *
+ * If the server is willing to accept the named security mechanism,
+ * and does not require any security data, it must respond with
+ * reply code 234/334.
+ */
+
+ if((ftpcode == 234) || (ftpcode == 334)) {
+ /* this was BLOCKING, keep it so for now */
+ bool done;
+ if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) {
+ result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
+ if(result) {
+ /* we failed and bail out */
+ return CURLE_USE_SSL_FAILED;
+ }
+ }
+ result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, &done);
+ if(!result) {
+ conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */
+ conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */
+ result = ftp_state_user(data, conn);
+ }
+ }
+ else if(ftpc->count3 < 1) {
+ ftpc->count3++;
+ ftpc->count1 += ftpc->count2; /* get next attempt */
+ result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s",
+ ftpauth[ftpc->count1]);
+ /* remain in this same state */
+ }
+ else {
+ if(data->set.use_ssl > CURLUSESSL_TRY)
+ /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
+ result = CURLE_USE_SSL_FAILED;
+ else
+ /* ignore the failure and continue */
+ result = ftp_state_user(data, conn);
+ }
+ break;
+
+ case FTP_USER:
+ case FTP_PASS:
+ result = ftp_state_user_resp(data, ftpcode, ftpc->state);
+ break;
+
+ case FTP_ACCT:
+ result = ftp_state_acct_resp(data, ftpcode);
+ break;
+
+ case FTP_PBSZ:
+ result =
+ Curl_pp_sendf(data, &ftpc->pp, "PROT %c",
+ data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
+ if(!result)
+ state(data, FTP_PROT);
+ break;
+
+ case FTP_PROT:
+ if(ftpcode/100 == 2)
+ /* We have enabled SSL for the data connection! */
+ conn->bits.ftp_use_data_ssl =
+ (data->set.use_ssl != CURLUSESSL_CONTROL) ? TRUE : FALSE;
+ /* FTP servers typically responds with 500 if they decide to reject
+ our 'P' request */
+ else if(data->set.use_ssl > CURLUSESSL_CONTROL)
+ /* we failed and bails out */
+ return CURLE_USE_SSL_FAILED;
+
+ if(data->set.ftp_ccc) {
+ /* CCC - Clear Command Channel
+ */
+ result = Curl_pp_sendf(data, &ftpc->pp, "%s", "CCC");
+ if(!result)
+ state(data, FTP_CCC);
+ }
+ else
+ result = ftp_state_pwd(data, conn);
+ break;
+
+ case FTP_CCC:
+ if(ftpcode < 500) {
+ /* First shut down the SSL layer (note: this call will block) */
+ result = Curl_ssl_cfilter_remove(data, FIRSTSOCKET);
+
+ if(result)
+ failf(data, "Failed to clear the command channel (CCC)");
+ }
+ if(!result)
+ /* Then continue as normal */
+ result = ftp_state_pwd(data, conn);
+ break;
+
+ case FTP_PWD:
+ if(ftpcode == 257) {
+ char *ptr = &data->state.buffer[4]; /* start on the first letter */
+ const size_t buf_size = data->set.buffer_size;
+ char *dir;
+ bool entry_extracted = FALSE;
+
+ dir = malloc(nread + 1);
+ if(!dir)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Reply format is like
+ 257<space>[rubbish]"<directory-name>"<space><commentary> and the
+ RFC959 says
+
+ The directory name can contain any character; embedded
+ double-quotes should be escaped by double-quotes (the
+ "quote-doubling" convention).
+ */
+
+ /* scan for the first double-quote for non-standard responses */
+ while(ptr < &data->state.buffer[buf_size]
+ && *ptr != '\n' && *ptr != '\0' && *ptr != '"')
+ ptr++;
+
+ if('\"' == *ptr) {
+ /* it started good */
+ char *store;
+ ptr++;
+ for(store = dir; *ptr;) {
+ if('\"' == *ptr) {
+ if('\"' == ptr[1]) {
+ /* "quote-doubling" */
+ *store = ptr[1];
+ ptr++;
+ }
+ else {
+ /* end of path */
+ entry_extracted = TRUE;
+ break; /* get out of this loop */
+ }
+ }
+ else
+ *store = *ptr;
+ store++;
+ ptr++;
+ }
+ *store = '\0'; /* null-terminate */
+ }
+ if(entry_extracted) {
+ /* If the path name does not look like an absolute path (i.e.: it
+ does not start with a '/'), we probably need some server-dependent
+ adjustments. For example, this is the case when connecting to
+ an OS400 FTP server: this server supports two name syntaxes,
+ the default one being incompatible with standard paths. In
+ addition, this server switches automatically to the regular path
+ syntax when one is encountered in a command: this results in
+ having an entrypath in the wrong syntax when later used in CWD.
+ The method used here is to check the server OS: we do it only
+ if the path name looks strange to minimize overhead on other
+ systems. */
+
+ if(!ftpc->server_os && dir[0] != '/') {
+ result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SYST");
+ if(result) {
+ free(dir);
+ return result;
+ }
+ Curl_safefree(ftpc->entrypath);
+ ftpc->entrypath = dir; /* remember this */
+ infof(data, "Entry path is '%s'", ftpc->entrypath);
+ /* also save it where getinfo can access it: */
+ data->state.most_recent_ftp_entrypath = ftpc->entrypath;
+ state(data, FTP_SYST);
+ break;
+ }
+
+ Curl_safefree(ftpc->entrypath);
+ ftpc->entrypath = dir; /* remember this */
+ infof(data, "Entry path is '%s'", ftpc->entrypath);
+ /* also save it where getinfo can access it: */
+ data->state.most_recent_ftp_entrypath = ftpc->entrypath;
+ }
+ else {
+ /* couldn't get the path */
+ free(dir);
+ infof(data, "Failed to figure out path");
+ }
+ }
+ state(data, FTP_STOP); /* we are done with the CONNECT phase! */
+ DEBUGF(infof(data, "protocol connect phase DONE"));
+ break;
+
+ case FTP_SYST:
+ if(ftpcode == 215) {
+ char *ptr = &data->state.buffer[4]; /* start on the first letter */
+ char *os;
+ char *store;
+
+ os = malloc(nread + 1);
+ if(!os)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Reply format is like
+ 215<space><OS-name><space><commentary>
+ */
+ while(*ptr == ' ')
+ ptr++;
+ for(store = os; *ptr && *ptr != ' ';)
+ *store++ = *ptr++;
+ *store = '\0'; /* null-terminate */
+
+ /* Check for special servers here. */
+
+ if(strcasecompare(os, "OS/400")) {
+ /* Force OS400 name format 1. */
+ result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SITE NAMEFMT 1");
+ if(result) {
+ free(os);
+ return result;
+ }
+ /* remember target server OS */
+ Curl_safefree(ftpc->server_os);
+ ftpc->server_os = os;
+ state(data, FTP_NAMEFMT);
+ break;
+ }
+ /* Nothing special for the target server. */
+ /* remember target server OS */
+ Curl_safefree(ftpc->server_os);
+ ftpc->server_os = os;
+ }
+ else {
+ /* Cannot identify server OS. Continue anyway and cross fingers. */
+ }
+
+ state(data, FTP_STOP); /* we are done with the CONNECT phase! */
+ DEBUGF(infof(data, "protocol connect phase DONE"));
+ break;
+
+ case FTP_NAMEFMT:
+ if(ftpcode == 250) {
+ /* Name format change successful: reload initial path. */
+ ftp_state_pwd(data, conn);
+ break;
+ }
+
+ state(data, FTP_STOP); /* we are done with the CONNECT phase! */
+ DEBUGF(infof(data, "protocol connect phase DONE"));
+ break;
+
+ case FTP_QUOTE:
+ case FTP_POSTQUOTE:
+ case FTP_RETR_PREQUOTE:
+ case FTP_STOR_PREQUOTE:
+ if((ftpcode >= 400) && !ftpc->count2) {
+ /* failure response code, and not allowed to fail */
+ failf(data, "QUOT command failed with %03d", ftpcode);
+ result = CURLE_QUOTE_ERROR;
+ }
+ else
+ result = ftp_state_quote(data, FALSE, ftpc->state);
+ break;
+
+ case FTP_CWD:
+ if(ftpcode/100 != 2) {
+ /* failure to CWD there */
+ if(data->set.ftp_create_missing_dirs &&
+ ftpc->cwdcount && !ftpc->count2) {
+ /* try making it */
+ ftpc->count2++; /* counter to prevent CWD-MKD loops */
+
+ /* count3 is set to allow MKD to fail once per dir. In the case when
+ CWD fails and then MKD fails (due to another session raced it to
+ create the dir) this then allows for a second try to CWD to it. */
+ ftpc->count3 = (data->set.ftp_create_missing_dirs == 2) ? 1 : 0;
+
+ result = Curl_pp_sendf(data, &ftpc->pp, "MKD %s",
+ ftpc->dirs[ftpc->cwdcount - 1]);
+ if(!result)
+ state(data, FTP_MKD);
+ }
+ else {
+ /* return failure */
+ failf(data, "Server denied you to change to the given directory");
+ ftpc->cwdfail = TRUE; /* don't remember this path as we failed
+ to enter it */
+ result = CURLE_REMOTE_ACCESS_DENIED;
+ }
+ }
+ else {
+ /* success */
+ ftpc->count2 = 0;
+ if(++ftpc->cwdcount <= ftpc->dirdepth)
+ /* send next CWD */
+ result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
+ ftpc->dirs[ftpc->cwdcount - 1]);
+ else
+ result = ftp_state_mdtm(data);
+ }
+ break;
+
+ case FTP_MKD:
+ if((ftpcode/100 != 2) && !ftpc->count3--) {
+ /* failure to MKD the dir */
+ failf(data, "Failed to MKD dir: %03d", ftpcode);
+ result = CURLE_REMOTE_ACCESS_DENIED;
+ }
+ else {
+ state(data, FTP_CWD);
+ /* send CWD */
+ result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
+ ftpc->dirs[ftpc->cwdcount - 1]);
+ }
+ break;
+
+ case FTP_MDTM:
+ result = ftp_state_mdtm_resp(data, ftpcode);
+ break;
+
+ case FTP_TYPE:
+ case FTP_LIST_TYPE:
+ case FTP_RETR_TYPE:
+ case FTP_STOR_TYPE:
+ result = ftp_state_type_resp(data, ftpcode, ftpc->state);
+ break;
+
+ case FTP_SIZE:
+ case FTP_RETR_SIZE:
+ case FTP_STOR_SIZE:
+ result = ftp_state_size_resp(data, ftpcode, ftpc->state);
+ break;
+
+ case FTP_REST:
+ case FTP_RETR_REST:
+ result = ftp_state_rest_resp(data, conn, ftpcode, ftpc->state);
+ break;
+
+ case FTP_PRET:
+ if(ftpcode != 200) {
+ /* there only is this one standard OK return code. */
+ failf(data, "PRET command not accepted: %03d", ftpcode);
+ return CURLE_FTP_PRET_FAILED;
+ }
+ result = ftp_state_use_pasv(data, conn);
+ break;
+
+ case FTP_PASV:
+ result = ftp_state_pasv_resp(data, ftpcode);
+ break;
+
+ case FTP_PORT:
+ result = ftp_state_port_resp(data, ftpcode);
+ break;
+
+ case FTP_LIST:
+ case FTP_RETR:
+ result = ftp_state_get_resp(data, ftpcode, ftpc->state);
+ break;
+
+ case FTP_STOR:
+ result = ftp_state_stor_resp(data, ftpcode, ftpc->state);
+ break;
+
+ case FTP_QUIT:
+ /* fallthrough, just stop! */
+ default:
+ /* internal error */
+ state(data, FTP_STOP);
+ break;
+ }
+ } /* if(ftpcode) */
+
+ return result;
+}
+
+
+/* called repeatedly until done from multi.c */
+static CURLcode ftp_multi_statemach(struct Curl_easy *data,
+ bool *done)
+{
+ struct connectdata *conn = data->conn;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ CURLcode result = Curl_pp_statemach(data, &ftpc->pp, FALSE, FALSE);
+
+ /* Check for the state outside of the Curl_socket_check() return code checks
+ since at times we are in fact already in this state when this function
+ gets called. */
+ *done = (ftpc->state == FTP_STOP) ? TRUE : FALSE;
+
+ return result;
+}
+
+static CURLcode ftp_block_statemach(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct pingpong *pp = &ftpc->pp;
+ CURLcode result = CURLE_OK;
+
+ while(ftpc->state != FTP_STOP) {
+ result = Curl_pp_statemach(data, pp, TRUE, TRUE /* disconnecting */);
+ if(result)
+ break;
+ }
+
+ return result;
+}
+
+/*
+ * ftp_connect() should do everything that is to be considered a part of
+ * the connection phase.
+ *
+ * The variable 'done' points to will be TRUE if the protocol-layer connect
+ * phase is done when this function returns, or FALSE if not.
+ *
+ */
+static CURLcode ftp_connect(struct Curl_easy *data,
+ bool *done) /* see description above */
+{
+ CURLcode result;
+ struct connectdata *conn = data->conn;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct pingpong *pp = &ftpc->pp;
+
+ *done = FALSE; /* default to not done yet */
+
+ /* We always support persistent connections on ftp */
+ connkeep(conn, "FTP default");
+
+ PINGPONG_SETUP(pp, ftp_statemachine, ftp_endofresp);
+
+ if(conn->handler->flags & PROTOPT_SSL) {
+ /* BLOCKING */
+ result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done);
+ if(result)
+ return result;
+ conn->bits.ftp_use_control_ssl = TRUE;
+ }
+
+ Curl_pp_setup(pp); /* once per transfer */
+ Curl_pp_init(data, pp); /* init the generic pingpong data */
+
+ /* When we connect, we start in the state where we await the 220
+ response */
+ state(data, FTP_WAIT220);
+
+ result = ftp_multi_statemach(data, done);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * ftp_done()
+ *
+ * The DONE function. This does what needs to be done after a single DO has
+ * performed.
+ *
+ * Input argument is already checked for validity.
+ */
+static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
+ bool premature)
+{
+ struct connectdata *conn = data->conn;
+ struct FTP *ftp = data->req.p.ftp;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct pingpong *pp = &ftpc->pp;
+ ssize_t nread;
+ int ftpcode;
+ CURLcode result = CURLE_OK;
+ char *rawPath = NULL;
+ size_t pathLen = 0;
+
+ if(!ftp)
+ return CURLE_OK;
+
+ switch(status) {
+ case CURLE_BAD_DOWNLOAD_RESUME:
+ case CURLE_FTP_WEIRD_PASV_REPLY:
+ case CURLE_FTP_PORT_FAILED:
+ case CURLE_FTP_ACCEPT_FAILED:
+ case CURLE_FTP_ACCEPT_TIMEOUT:
+ case CURLE_FTP_COULDNT_SET_TYPE:
+ case CURLE_FTP_COULDNT_RETR_FILE:
+ case CURLE_PARTIAL_FILE:
+ case CURLE_UPLOAD_FAILED:
+ case CURLE_REMOTE_ACCESS_DENIED:
+ case CURLE_FILESIZE_EXCEEDED:
+ case CURLE_REMOTE_FILE_NOT_FOUND:
+ case CURLE_WRITE_ERROR:
+ /* the connection stays alive fine even though this happened */
+ /* fall-through */
+ case CURLE_OK: /* doesn't affect the control connection's status */
+ if(!premature)
+ break;
+
+ /* until we cope better with prematurely ended requests, let them
+ * fallback as if in complete failure */
+ /* FALLTHROUGH */
+ default: /* by default, an error means the control connection is
+ wedged and should not be used anymore */
+ ftpc->ctl_valid = FALSE;
+ ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
+ current path, as this connection is going */
+ connclose(conn, "FTP ended with bad error code");
+ result = status; /* use the already set error code */
+ break;
+ }
+
+ if(data->state.wildcardmatch) {
+ if(data->set.chunk_end && ftpc->file) {
+ Curl_set_in_callback(data, true);
+ data->set.chunk_end(data->wildcard.customptr);
+ Curl_set_in_callback(data, false);
+ }
+ ftpc->known_filesize = -1;
+ }
+
+ if(!result)
+ /* get the url-decoded "raw" path */
+ result = Curl_urldecode(ftp->path, 0, &rawPath, &pathLen,
+ REJECT_CTRL);
+ if(result) {
+ /* We can limp along anyway (and should try to since we may already be in
+ * the error path) */
+ ftpc->ctl_valid = FALSE; /* mark control connection as bad */
+ connclose(conn, "FTP: out of memory!"); /* mark for connection closure */
+ free(ftpc->prevpath);
+ ftpc->prevpath = NULL; /* no path remembering */
+ }
+ else { /* remember working directory for connection reuse */
+ if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/'))
+ free(rawPath); /* full path => no CWDs happened => keep ftpc->prevpath */
+ else {
+ free(ftpc->prevpath);
+
+ if(!ftpc->cwdfail) {
+ if(data->set.ftp_filemethod == FTPFILE_NOCWD)
+ pathLen = 0; /* relative path => working directory is FTP home */
+ else
+ pathLen -= ftpc->file?strlen(ftpc->file):0; /* file is url-decoded */
+
+ rawPath[pathLen] = '\0';
+ ftpc->prevpath = rawPath;
+ }
+ else {
+ free(rawPath);
+ ftpc->prevpath = NULL; /* no path */
+ }
+ }
+
+ if(ftpc->prevpath)
+ infof(data, "Remembering we are in dir \"%s\"", ftpc->prevpath);
+ }
+
+ /* free the dir tree and file parts */
+ freedirs(ftpc);
+
+ /* shut down the socket to inform the server we're done */
+
+#ifdef _WIN32_WCE
+ shutdown(conn->sock[SECONDARYSOCKET], 2); /* SD_BOTH */
+#endif
+
+ if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
+ if(!result && ftpc->dont_check && data->req.maxdownload > 0) {
+ /* partial download completed */
+ result = Curl_pp_sendf(data, pp, "%s", "ABOR");
+ if(result) {
+ failf(data, "Failure sending ABOR command: %s",
+ curl_easy_strerror(result));
+ ftpc->ctl_valid = FALSE; /* mark control connection as bad */
+ connclose(conn, "ABOR command failed"); /* connection closure */
+ }
+ }
+
+ close_secondarysocket(data, conn);
+ }
+
+ if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid &&
+ pp->pending_resp && !premature) {
+ /*
+ * Let's see what the server says about the transfer we just performed,
+ * but lower the timeout as sometimes this connection has died while the
+ * data has been transferred. This happens when doing through NATs etc that
+ * abandon old silent connections.
+ */
+ timediff_t old_time = pp->response_time;
+
+ pp->response_time = 60*1000; /* give it only a minute for now */
+ pp->response = Curl_now(); /* timeout relative now */
+
+ result = Curl_GetFTPResponse(data, &nread, &ftpcode);
+
+ pp->response_time = old_time; /* set this back to previous value */
+
+ if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
+ failf(data, "control connection looks dead");
+ ftpc->ctl_valid = FALSE; /* mark control connection as bad */
+ connclose(conn, "Timeout or similar in FTP DONE operation"); /* close */
+ }
+
+ if(result) {
+ Curl_safefree(ftp->pathalloc);
+ return result;
+ }
+
+ if(ftpc->dont_check && data->req.maxdownload > 0) {
+ /* we have just sent ABOR and there is no reliable way to check if it was
+ * successful or not; we have to close the connection now */
+ infof(data, "partial download completed, closing connection");
+ connclose(conn, "Partial download with no ability to check");
+ return result;
+ }
+
+ if(!ftpc->dont_check) {
+ /* 226 Transfer complete, 250 Requested file action okay, completed. */
+ switch(ftpcode) {
+ case 226:
+ case 250:
+ break;
+ case 552:
+ failf(data, "Exceeded storage allocation");
+ result = CURLE_REMOTE_DISK_FULL;
+ break;
+ default:
+ failf(data, "server did not report OK, got %d", ftpcode);
+ result = CURLE_PARTIAL_FILE;
+ break;
+ }
+ }
+ }
+
+ if(result || premature)
+ /* the response code from the transfer showed an error already so no
+ use checking further */
+ ;
+ else if(data->set.upload) {
+ if((-1 != data->state.infilesize) &&
+ (data->state.infilesize != data->req.writebytecount) &&
+ !data->set.crlf &&
+ (ftp->transfer == PPTRANSFER_BODY)) {
+ failf(data, "Uploaded unaligned file size (%" CURL_FORMAT_CURL_OFF_T
+ " out of %" CURL_FORMAT_CURL_OFF_T " bytes)",
+ data->req.writebytecount, data->state.infilesize);
+ result = CURLE_PARTIAL_FILE;
+ }
+ }
+ else {
+ if((-1 != data->req.size) &&
+ (data->req.size != data->req.bytecount) &&
+#ifdef CURL_DO_LINEEND_CONV
+ /* Most FTP servers don't adjust their file SIZE response for CRLFs, so
+ * we'll check to see if the discrepancy can be explained by the number
+ * of CRLFs we've changed to LFs.
+ */
+ ((data->req.size + data->state.crlf_conversions) !=
+ data->req.bytecount) &&
+#endif /* CURL_DO_LINEEND_CONV */
+ (data->req.maxdownload != data->req.bytecount)) {
+ failf(data, "Received only partial file: %" CURL_FORMAT_CURL_OFF_T
+ " bytes", data->req.bytecount);
+ result = CURLE_PARTIAL_FILE;
+ }
+ else if(!ftpc->dont_check &&
+ !data->req.bytecount &&
+ (data->req.size>0)) {
+ failf(data, "No data was received");
+ result = CURLE_FTP_COULDNT_RETR_FILE;
+ }
+ }
+
+ /* clear these for next connection */
+ ftp->transfer = PPTRANSFER_BODY;
+ ftpc->dont_check = FALSE;
+
+ /* Send any post-transfer QUOTE strings? */
+ if(!status && !result && !premature && data->set.postquote)
+ result = ftp_sendquote(data, conn, data->set.postquote);
+ Curl_safefree(ftp->pathalloc);
+ return result;
+}
+
+/***********************************************************************
+ *
+ * ftp_sendquote()
+ *
+ * Where a 'quote' means a list of custom commands to send to the server.
+ * The quote list is passed as an argument.
+ *
+ * BLOCKING
+ */
+
+static
+CURLcode ftp_sendquote(struct Curl_easy *data,
+ struct connectdata *conn, struct curl_slist *quote)
+{
+ struct curl_slist *item;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct pingpong *pp = &ftpc->pp;
+
+ item = quote;
+ while(item) {
+ if(item->data) {
+ ssize_t nread;
+ char *cmd = item->data;
+ bool acceptfail = FALSE;
+ CURLcode result;
+ int ftpcode = 0;
+
+ /* if a command starts with an asterisk, which a legal FTP command never
+ can, the command will be allowed to fail without it causing any
+ aborts or cancels etc. It will cause libcurl to act as if the command
+ is successful, whatever the server reponds. */
+
+ if(cmd[0] == '*') {
+ cmd++;
+ acceptfail = TRUE;
+ }
+
+ result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
+ if(!result) {
+ pp->response = Curl_now(); /* timeout relative now */
+ result = Curl_GetFTPResponse(data, &nread, &ftpcode);
+ }
+ if(result)
+ return result;
+
+ if(!acceptfail && (ftpcode >= 400)) {
+ failf(data, "QUOT string not accepted: %s", cmd);
+ return CURLE_QUOTE_ERROR;
+ }
+ }
+
+ item = item->next;
+ }
+
+ return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * ftp_need_type()
+ *
+ * Returns TRUE if we in the current situation should send TYPE
+ */
+static int ftp_need_type(struct connectdata *conn,
+ bool ascii_wanted)
+{
+ return conn->proto.ftpc.transfertype != (ascii_wanted?'A':'I');
+}
+
+/***********************************************************************
+ *
+ * ftp_nb_type()
+ *
+ * Set TYPE. We only deal with ASCII or BINARY so this function
+ * sets one of them.
+ * If the transfer type is not sent, simulate on OK response in newstate
+ */
+static CURLcode ftp_nb_type(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool ascii, ftpstate newstate)
+{
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ CURLcode result;
+ char want = (char)(ascii?'A':'I');
+
+ if(ftpc->transfertype == want) {
+ state(data, newstate);
+ return ftp_state_type_resp(data, 200, newstate);
+ }
+
+ result = Curl_pp_sendf(data, &ftpc->pp, "TYPE %c", want);
+ if(!result) {
+ state(data, newstate);
+
+ /* keep track of our current transfer type */
+ ftpc->transfertype = want;
+ }
+ return result;
+}
+
+/***************************************************************************
+ *
+ * ftp_pasv_verbose()
+ *
+ * This function only outputs some informationals about this second connection
+ * when we've issued a PASV command before and thus we have connected to a
+ * possibly new IP address.
+ *
+ */
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static void
+ftp_pasv_verbose(struct Curl_easy *data,
+ struct Curl_addrinfo *ai,
+ char *newhost, /* ascii version */
+ int port)
+{
+ char buf[256];
+ Curl_printable_address(ai, buf, sizeof(buf));
+ infof(data, "Connecting to %s (%s) port %d", newhost, buf, port);
+}
+#endif
+
+/*
+ * ftp_do_more()
+ *
+ * This function shall be called when the second FTP (data) connection is
+ * connected.
+ *
+ * 'complete' can return 0 for incomplete, 1 for done and -1 for go back
+ * (which basically is only for when PASV is being sent to retry a failed
+ * EPSV).
+ */
+
+static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
+{
+ struct connectdata *conn = data->conn;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ CURLcode result = CURLE_OK;
+ bool connected = FALSE;
+ bool complete = FALSE;
+
+ /* the ftp struct is inited in ftp_connect(). If we are connecting to an HTTP
+ * proxy then the state will not be valid until after that connection is
+ * complete */
+ struct FTP *ftp = NULL;
+
+ /* if the second connection isn't done yet, wait for it to have
+ * connected to the remote host. When using proxy tunneling, this
+ * means the tunnel needs to have been establish. However, we
+ * can not expect the remote host to talk to us in any way yet.
+ * So, when using ftps: the SSL handshake will not start until we
+ * tell the remote server that we are there. */
+ if(conn->cfilter[SECONDARYSOCKET]) {
+ result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected);
+ if(result || !Curl_conn_is_ip_connected(data, SECONDARYSOCKET)) {
+ if(result && (ftpc->count1 == 0)) {
+ *completep = -1; /* go back to DOING please */
+ /* this is a EPSV connect failing, try PASV instead */
+ return ftp_epsv_disable(data, conn);
+ }
+ return result;
+ }
+ }
+
+ /* Curl_proxy_connect might have moved the protocol state */
+ ftp = data->req.p.ftp;
+
+ if(ftpc->state) {
+ /* already in a state so skip the initial commands.
+ They are only done to kickstart the do_more state */
+ result = ftp_multi_statemach(data, &complete);
+
+ *completep = (int)complete;
+
+ /* if we got an error or if we don't wait for a data connection return
+ immediately */
+ if(result || !ftpc->wait_data_conn)
+ return result;
+
+ /* if we reach the end of the FTP state machine here, *complete will be
+ TRUE but so is ftpc->wait_data_conn, which says we need to wait for the
+ data connection and therefore we're not actually complete */
+ *completep = 0;
+ }
+
+ if(ftp->transfer <= PPTRANSFER_INFO) {
+ /* a transfer is about to take place, or if not a file name was given
+ so we'll do a SIZE on it later and then we need the right TYPE first */
+
+ if(ftpc->wait_data_conn == TRUE) {
+ bool serv_conned;
+
+ result = ReceivedServerConnect(data, &serv_conned);
+ if(result)
+ return result; /* Failed to accept data connection */
+
+ if(serv_conned) {
+ /* It looks data connection is established */
+ result = AcceptServerConnect(data);
+ ftpc->wait_data_conn = FALSE;
+ if(!result)
+ result = InitiateTransfer(data);
+
+ if(result)
+ return result;
+
+ *completep = 1; /* this state is now complete when the server has
+ connected back to us */
+ }
+ }
+ else if(data->set.upload) {
+ result = ftp_nb_type(data, conn, data->state.prefer_ascii,
+ FTP_STOR_TYPE);
+ if(result)
+ return result;
+
+ result = ftp_multi_statemach(data, &complete);
+ if(ftpc->wait_data_conn)
+ /* if we reach the end of the FTP state machine here, *complete will be
+ TRUE but so is ftpc->wait_data_conn, which says we need to wait for
+ the data connection and therefore we're not actually complete */
+ *completep = 0;
+ else
+ *completep = (int)complete;
+ }
+ else {
+ /* download */
+ ftp->downloadsize = -1; /* unknown as of yet */
+
+ result = Curl_range(data);
+
+ if(result == CURLE_OK && data->req.maxdownload >= 0) {
+ /* Don't check for successful transfer */
+ ftpc->dont_check = TRUE;
+ }
+
+ if(result)
+ ;
+ else if(data->state.list_only || !ftpc->file) {
+ /* The specified path ends with a slash, and therefore we think this
+ is a directory that is requested, use LIST. But before that we
+ need to set ASCII transfer mode. */
+
+ /* But only if a body transfer was requested. */
+ if(ftp->transfer == PPTRANSFER_BODY) {
+ result = ftp_nb_type(data, conn, TRUE, FTP_LIST_TYPE);
+ if(result)
+ return result;
+ }
+ /* otherwise just fall through */
+ }
+ else {
+ result = ftp_nb_type(data, conn, data->state.prefer_ascii,
+ FTP_RETR_TYPE);
+ if(result)
+ return result;
+ }
+
+ result = ftp_multi_statemach(data, &complete);
+ *completep = (int)complete;
+ }
+ return result;
+ }
+
+ /* no data to transfer */
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
+
+ if(!ftpc->wait_data_conn) {
+ /* no waiting for the data connection so this is now complete */
+ *completep = 1;
+ DEBUGF(infof(data, "DO-MORE phase ends with %d", (int)result));
+ }
+
+ return result;
+}
+
+
+
+/***********************************************************************
+ *
+ * ftp_perform()
+ *
+ * This is the actual DO function for FTP. Get a file/directory according to
+ * the options previously setup.
+ */
+
+static
+CURLcode ftp_perform(struct Curl_easy *data,
+ bool *connected, /* connect status after PASV / PORT */
+ bool *dophase_done)
+{
+ /* this is FTP and no proxy */
+ CURLcode result = CURLE_OK;
+
+ DEBUGF(infof(data, "DO phase starts"));
+
+ if(data->req.no_body) {
+ /* requested no body means no transfer... */
+ struct FTP *ftp = data->req.p.ftp;
+ ftp->transfer = PPTRANSFER_INFO;
+ }
+
+ *dophase_done = FALSE; /* not done yet */
+
+ /* start the first command in the DO phase */
+ result = ftp_state_quote(data, TRUE, FTP_QUOTE);
+ if(result)
+ return result;
+
+ /* run the state-machine */
+ result = ftp_multi_statemach(data, dophase_done);
+
+ *connected = Curl_conn_is_connected(data->conn, SECONDARYSOCKET);
+
+ infof(data, "ftp_perform ends with SECONDARY: %d", *connected);
+
+ if(*dophase_done)
+ DEBUGF(infof(data, "DO phase is complete1"));
+
+ return result;
+}
+
+static void wc_data_dtor(void *ptr)
+{
+ struct ftp_wc *ftpwc = ptr;
+ if(ftpwc && ftpwc->parser)
+ Curl_ftp_parselist_data_free(&ftpwc->parser);
+ free(ftpwc);
+}
+
+static CURLcode init_wc_data(struct Curl_easy *data)
+{
+ char *last_slash;
+ struct FTP *ftp = data->req.p.ftp;
+ char *path = ftp->path;
+ struct WildcardData *wildcard = &(data->wildcard);
+ CURLcode result = CURLE_OK;
+ struct ftp_wc *ftpwc = NULL;
+
+ last_slash = strrchr(ftp->path, '/');
+ if(last_slash) {
+ last_slash++;
+ if(last_slash[0] == '\0') {
+ wildcard->state = CURLWC_CLEAN;
+ result = ftp_parse_url_path(data);
+ return result;
+ }
+ wildcard->pattern = strdup(last_slash);
+ if(!wildcard->pattern)
+ return CURLE_OUT_OF_MEMORY;
+ last_slash[0] = '\0'; /* cut file from path */
+ }
+ else { /* there is only 'wildcard pattern' or nothing */
+ if(path[0]) {
+ wildcard->pattern = strdup(path);
+ if(!wildcard->pattern)
+ return CURLE_OUT_OF_MEMORY;
+ path[0] = '\0';
+ }
+ else { /* only list */
+ wildcard->state = CURLWC_CLEAN;
+ result = ftp_parse_url_path(data);
+ return result;
+ }
+ }
+
+ /* program continues only if URL is not ending with slash, allocate needed
+ resources for wildcard transfer */
+
+ /* allocate ftp protocol specific wildcard data */
+ ftpwc = calloc(1, sizeof(struct ftp_wc));
+ if(!ftpwc) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+
+ /* INITIALIZE parselist structure */
+ ftpwc->parser = Curl_ftp_parselist_data_alloc();
+ if(!ftpwc->parser) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+
+ wildcard->protdata = ftpwc; /* put it to the WildcardData tmp pointer */
+ wildcard->dtor = wc_data_dtor;
+
+ /* wildcard does not support NOCWD option (assert it?) */
+ if(data->set.ftp_filemethod == FTPFILE_NOCWD)
+ data->set.ftp_filemethod = FTPFILE_MULTICWD;
+
+ /* try to parse ftp url */
+ result = ftp_parse_url_path(data);
+ if(result) {
+ goto fail;
+ }
+
+ wildcard->path = strdup(ftp->path);
+ if(!wildcard->path) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+
+ /* backup old write_function */
+ ftpwc->backup.write_function = data->set.fwrite_func;
+ /* parsing write function */
+ data->set.fwrite_func = Curl_ftp_parselist;
+ /* backup old file descriptor */
+ ftpwc->backup.file_descriptor = data->set.out;
+ /* let the writefunc callback know the transfer */
+ data->set.out = data;
+
+ infof(data, "Wildcard - Parsing started");
+ return CURLE_OK;
+
+ fail:
+ if(ftpwc) {
+ Curl_ftp_parselist_data_free(&ftpwc->parser);
+ free(ftpwc);
+ }
+ Curl_safefree(wildcard->pattern);
+ wildcard->dtor = ZERO_NULL;
+ wildcard->protdata = NULL;
+ return result;
+}
+
+static CURLcode wc_statemach(struct Curl_easy *data)
+{
+ struct WildcardData * const wildcard = &(data->wildcard);
+ struct connectdata *conn = data->conn;
+ CURLcode result = CURLE_OK;
+
+ for(;;) {
+ switch(wildcard->state) {
+ case CURLWC_INIT:
+ result = init_wc_data(data);
+ if(wildcard->state == CURLWC_CLEAN)
+ /* only listing! */
+ return result;
+ wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING;
+ return result;
+
+ case CURLWC_MATCHING: {
+ /* In this state is LIST response successfully parsed, so lets restore
+ previous WRITEFUNCTION callback and WRITEDATA pointer */
+ struct ftp_wc *ftpwc = wildcard->protdata;
+ data->set.fwrite_func = ftpwc->backup.write_function;
+ data->set.out = ftpwc->backup.file_descriptor;
+ ftpwc->backup.write_function = ZERO_NULL;
+ ftpwc->backup.file_descriptor = NULL;
+ wildcard->state = CURLWC_DOWNLOADING;
+
+ if(Curl_ftp_parselist_geterror(ftpwc->parser)) {
+ /* error found in LIST parsing */
+ wildcard->state = CURLWC_CLEAN;
+ continue;
+ }
+ if(wildcard->filelist.size == 0) {
+ /* no corresponding file */
+ wildcard->state = CURLWC_CLEAN;
+ return CURLE_REMOTE_FILE_NOT_FOUND;
+ }
+ continue;
+ }
+
+ case CURLWC_DOWNLOADING: {
+ /* filelist has at least one file, lets get first one */
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct curl_fileinfo *finfo = wildcard->filelist.head->ptr;
+ struct FTP *ftp = data->req.p.ftp;
+
+ char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename);
+ if(!tmp_path)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* switch default ftp->path and tmp_path */
+ free(ftp->pathalloc);
+ ftp->pathalloc = ftp->path = tmp_path;
+
+ infof(data, "Wildcard - START of \"%s\"", finfo->filename);
+ if(data->set.chunk_bgn) {
+ long userresponse;
+ Curl_set_in_callback(data, true);
+ userresponse = data->set.chunk_bgn(
+ finfo, wildcard->customptr, (int)wildcard->filelist.size);
+ Curl_set_in_callback(data, false);
+ switch(userresponse) {
+ case CURL_CHUNK_BGN_FUNC_SKIP:
+ infof(data, "Wildcard - \"%s\" skipped by user",
+ finfo->filename);
+ wildcard->state = CURLWC_SKIP;
+ continue;
+ case CURL_CHUNK_BGN_FUNC_FAIL:
+ return CURLE_CHUNK_FAILED;
+ }
+ }
+
+ if(finfo->filetype != CURLFILETYPE_FILE) {
+ wildcard->state = CURLWC_SKIP;
+ continue;
+ }
+
+ if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
+ ftpc->known_filesize = finfo->size;
+
+ result = ftp_parse_url_path(data);
+ if(result)
+ return result;
+
+ /* we don't need the Curl_fileinfo of first file anymore */
+ Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
+
+ if(wildcard->filelist.size == 0) { /* remains only one file to down. */
+ wildcard->state = CURLWC_CLEAN;
+ /* after that will be ftp_do called once again and no transfer
+ will be done because of CURLWC_CLEAN state */
+ return CURLE_OK;
+ }
+ return result;
+ }
+
+ case CURLWC_SKIP: {
+ if(data->set.chunk_end) {
+ Curl_set_in_callback(data, true);
+ data->set.chunk_end(data->wildcard.customptr);
+ Curl_set_in_callback(data, false);
+ }
+ Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
+ wildcard->state = (wildcard->filelist.size == 0) ?
+ CURLWC_CLEAN : CURLWC_DOWNLOADING;
+ continue;
+ }
+
+ case CURLWC_CLEAN: {
+ struct ftp_wc *ftpwc = wildcard->protdata;
+ result = CURLE_OK;
+ if(ftpwc)
+ result = Curl_ftp_parselist_geterror(ftpwc->parser);
+
+ wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE;
+ return result;
+ }
+
+ case CURLWC_DONE:
+ case CURLWC_ERROR:
+ case CURLWC_CLEAR:
+ if(wildcard->dtor)
+ wildcard->dtor(wildcard->protdata);
+ return result;
+ }
+ }
+ /* UNREACHABLE */
+}
+
+/***********************************************************************
+ *
+ * ftp_do()
+ *
+ * This function is registered as 'curl_do' function. It decodes the path
+ * parts etc as a wrapper to the actual DO function (ftp_perform).
+ *
+ * The input argument is already checked for validity.
+ */
+static CURLcode ftp_do(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+ *done = FALSE; /* default to false */
+ ftpc->wait_data_conn = FALSE; /* default to no such wait */
+
+ if(data->state.wildcardmatch) {
+ result = wc_statemach(data);
+ if(data->wildcard.state == CURLWC_SKIP ||
+ data->wildcard.state == CURLWC_DONE) {
+ /* do not call ftp_regular_transfer */
+ return CURLE_OK;
+ }
+ if(result) /* error, loop or skipping the file */
+ return result;
+ }
+ else { /* no wildcard FSM needed */
+ result = ftp_parse_url_path(data);
+ if(result)
+ return result;
+ }
+
+ result = ftp_regular_transfer(data, done);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * ftp_quit()
+ *
+ * This should be called before calling sclose() on an ftp control connection
+ * (not data connections). We should then wait for the response from the
+ * server before returning. The calling code should then try to close the
+ * connection.
+ *
+ */
+static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+
+ if(conn->proto.ftpc.ctl_valid) {
+ result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "QUIT");
+ if(result) {
+ failf(data, "Failure sending QUIT command: %s",
+ curl_easy_strerror(result));
+ conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */
+ connclose(conn, "QUIT command failed"); /* mark for connection closure */
+ state(data, FTP_STOP);
+ return result;
+ }
+
+ state(data, FTP_QUIT);
+
+ result = ftp_block_statemach(data, conn);
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * ftp_disconnect()
+ *
+ * Disconnect from an FTP server. Cleanup protocol-specific per-connection
+ * resources. BLOCKING.
+ */
+static CURLcode ftp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection)
+{
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct pingpong *pp = &ftpc->pp;
+
+ /* We cannot send quit unconditionally. If this connection is stale or
+ bad in any way, sending quit and waiting around here will make the
+ disconnect wait in vain and cause more problems than we need to.
+
+ ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
+ will try to send the QUIT command, otherwise it will just return.
+ */
+ if(dead_connection)
+ ftpc->ctl_valid = FALSE;
+
+ /* The FTP session may or may not have been allocated/setup at this point! */
+ (void)ftp_quit(data, conn); /* ignore errors on the QUIT */
+
+ if(ftpc->entrypath) {
+ if(data->state.most_recent_ftp_entrypath == ftpc->entrypath) {
+ data->state.most_recent_ftp_entrypath = NULL;
+ }
+ Curl_safefree(ftpc->entrypath);
+ }
+
+ freedirs(ftpc);
+ Curl_safefree(ftpc->prevpath);
+ Curl_safefree(ftpc->server_os);
+ Curl_pp_disconnect(pp);
+ Curl_sec_end(conn);
+ return CURLE_OK;
+}
+
+#ifdef _MSC_VER
+/* warning C4706: assignment within conditional expression */
+#pragma warning(disable:4706)
+#endif
+
+/***********************************************************************
+ *
+ * ftp_parse_url_path()
+ *
+ * Parse the URL path into separate path components.
+ *
+ */
+static
+CURLcode ftp_parse_url_path(struct Curl_easy *data)
+{
+ /* the ftp struct is already inited in ftp_connect() */
+ struct FTP *ftp = data->req.p.ftp;
+ struct connectdata *conn = data->conn;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ const char *slashPos = NULL;
+ const char *fileName = NULL;
+ CURLcode result = CURLE_OK;
+ char *rawPath = NULL; /* url-decoded "raw" path */
+ size_t pathLen = 0;
+
+ ftpc->ctl_valid = FALSE;
+ ftpc->cwdfail = FALSE;
+
+ /* url-decode ftp path before further evaluation */
+ result = Curl_urldecode(ftp->path, 0, &rawPath, &pathLen, REJECT_CTRL);
+ if(result) {
+ failf(data, "path contains control characters");
+ return result;
+ }
+
+ switch(data->set.ftp_filemethod) {
+ case FTPFILE_NOCWD: /* fastest, but less standard-compliant */
+
+ if((pathLen > 0) && (rawPath[pathLen - 1] != '/'))
+ fileName = rawPath; /* this is a full file path */
+ /*
+ else: ftpc->file is not used anywhere other than for operations on
+ a file. In other words, never for directory operations.
+ So we can safely leave filename as NULL here and use it as a
+ argument in dir/file decisions.
+ */
+ break;
+
+ case FTPFILE_SINGLECWD:
+ slashPos = strrchr(rawPath, '/');
+ if(slashPos) {
+ /* get path before last slash, except for / */
+ size_t dirlen = slashPos - rawPath;
+ if(dirlen == 0)
+ dirlen = 1;
+
+ ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0]));
+ if(!ftpc->dirs) {
+ free(rawPath);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ ftpc->dirs[0] = calloc(1, dirlen + 1);
+ if(!ftpc->dirs[0]) {
+ free(rawPath);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ strncpy(ftpc->dirs[0], rawPath, dirlen);
+ ftpc->dirdepth = 1; /* we consider it to be a single dir */
+ fileName = slashPos + 1; /* rest is file name */
+ }
+ else
+ fileName = rawPath; /* file name only (or empty) */
+ break;
+
+ default: /* allow pretty much anything */
+ case FTPFILE_MULTICWD: {
+ /* current position: begin of next path component */
+ const char *curPos = rawPath;
+
+ /* number of entries allocated for the 'dirs' array */
+ size_t dirAlloc = 0;
+ const char *str = rawPath;
+ for(; *str != 0; ++str)
+ if (*str == '/')
+ ++dirAlloc;
+
+ if(dirAlloc) {
+ ftpc->dirs = calloc(dirAlloc, sizeof(ftpc->dirs[0]));
+ if(!ftpc->dirs) {
+ free(rawPath);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* parse the URL path into separate path components */
+ while((slashPos = strchr(curPos, '/'))) {
+ size_t compLen = slashPos - curPos;
+
+ /* path starts with a slash: add that as a directory */
+ if((compLen == 0) && (ftpc->dirdepth == 0))
+ ++compLen;
+
+ /* we skip empty path components, like "x//y" since the FTP command
+ CWD requires a parameter and a non-existent parameter a) doesn't
+ work on many servers and b) has no effect on the others. */
+ if(compLen > 0) {
+ char *comp = calloc(1, compLen + 1);
+ if(!comp) {
+ free(rawPath);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ strncpy(comp, curPos, compLen);
+ ftpc->dirs[ftpc->dirdepth++] = comp;
+ }
+ curPos = slashPos + 1;
+ }
+ }
+ DEBUGASSERT((size_t)ftpc->dirdepth <= dirAlloc);
+ fileName = curPos; /* the rest is the file name (or empty) */
+ }
+ break;
+ } /* switch */
+
+ if(fileName && *fileName)
+ ftpc->file = strdup(fileName);
+ else
+ ftpc->file = NULL; /* instead of point to a zero byte,
+ we make it a NULL pointer */
+
+ if(data->set.upload && !ftpc->file && (ftp->transfer == PPTRANSFER_BODY)) {
+ /* We need a file name when uploading. Return error! */
+ failf(data, "Uploading to a URL without a file name");
+ free(rawPath);
+ return CURLE_URL_MALFORMAT;
+ }
+
+ ftpc->cwddone = FALSE; /* default to not done */
+
+ if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/'))
+ ftpc->cwddone = TRUE; /* skip CWD for absolute paths */
+ else { /* newly created FTP connections are already in entry path */
+ const char *oldPath = conn->bits.reuse ? ftpc->prevpath : "";
+ if(oldPath) {
+ size_t n = pathLen;
+ if(data->set.ftp_filemethod == FTPFILE_NOCWD)
+ n = 0; /* CWD to entry for relative paths */
+ else
+ n -= ftpc->file?strlen(ftpc->file):0;
+
+ if((strlen(oldPath) == n) && !strncmp(rawPath, oldPath, n)) {
+ infof(data, "Request has same path as previous transfer");
+ ftpc->cwddone = TRUE;
+ }
+ }
+ }
+
+ free(rawPath);
+ return CURLE_OK;
+}
+
+/* call this when the DO phase has completed */
+static CURLcode ftp_dophase_done(struct Curl_easy *data, bool connected)
+{
+ struct connectdata *conn = data->conn;
+ struct FTP *ftp = data->req.p.ftp;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+ if(connected) {
+ int completed;
+ CURLcode result = ftp_do_more(data, &completed);
+
+ if(result) {
+ close_secondarysocket(data, conn);
+ return result;
+ }
+ }
+
+ if(ftp->transfer != PPTRANSFER_BODY)
+ /* no data to transfer */
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
+ else if(!connected)
+ /* since we didn't connect now, we want do_more to get called */
+ conn->bits.do_more = TRUE;
+
+ ftpc->ctl_valid = TRUE; /* seems good */
+
+ return CURLE_OK;
+}
+
+/* called from multi.c while DOing */
+static CURLcode ftp_doing(struct Curl_easy *data,
+ bool *dophase_done)
+{
+ CURLcode result = ftp_multi_statemach(data, dophase_done);
+
+ if(result)
+ DEBUGF(infof(data, "DO phase failed"));
+ else if(*dophase_done) {
+ result = ftp_dophase_done(data, FALSE /* not connected */);
+
+ DEBUGF(infof(data, "DO phase is complete2"));
+ }
+ return result;
+}
+
+/***********************************************************************
+ *
+ * ftp_regular_transfer()
+ *
+ * The input argument is already checked for validity.
+ *
+ * Performs all commands done before a regular transfer between a local and a
+ * remote host.
+ *
+ * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
+ * ftp_done() function without finding any major problem.
+ */
+static
+CURLcode ftp_regular_transfer(struct Curl_easy *data,
+ bool *dophase_done)
+{
+ CURLcode result = CURLE_OK;
+ bool connected = FALSE;
+ struct connectdata *conn = data->conn;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ data->req.size = -1; /* make sure this is unknown at this point */
+
+ Curl_pgrsSetUploadCounter(data, 0);
+ Curl_pgrsSetDownloadCounter(data, 0);
+ Curl_pgrsSetUploadSize(data, -1);
+ Curl_pgrsSetDownloadSize(data, -1);
+
+ ftpc->ctl_valid = TRUE; /* starts good */
+
+ result = ftp_perform(data,
+ &connected, /* have we connected after PASV/PORT */
+ dophase_done); /* all commands in the DO-phase done? */
+
+ if(!result) {
+
+ if(!*dophase_done)
+ /* the DO phase has not completed yet */
+ return CURLE_OK;
+
+ result = ftp_dophase_done(data, connected);
+
+ if(result)
+ return result;
+ }
+ else
+ freedirs(ftpc);
+
+ return result;
+}
+
+static CURLcode ftp_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ char *type;
+ struct FTP *ftp;
+ CURLcode result = CURLE_OK;
+
+ data->req.p.ftp = ftp = calloc(sizeof(struct FTP), 1);
+ if(!ftp)
+ return CURLE_OUT_OF_MEMORY;
+
+ ftp->path = &data->state.up.path[1]; /* don't include the initial slash */
+
+ /* FTP URLs support an extension like ";type=<typecode>" that
+ * we'll try to get now! */
+ type = strstr(ftp->path, ";type=");
+
+ if(!type)
+ type = strstr(conn->host.rawalloc, ";type=");
+
+ if(type) {
+ char command;
+ *type = 0; /* it was in the middle of the hostname */
+ command = Curl_raw_toupper(type[6]);
+
+ switch(command) {
+ case 'A': /* ASCII mode */
+ data->state.prefer_ascii = TRUE;
+ break;
+
+ case 'D': /* directory mode */
+ data->state.list_only = TRUE;
+ break;
+
+ case 'I': /* binary mode */
+ default:
+ /* switch off ASCII */
+ data->state.prefer_ascii = FALSE;
+ break;
+ }
+ }
+
+ /* get some initial data into the ftp struct */
+ ftp->transfer = PPTRANSFER_BODY;
+ ftp->downloadsize = 0;
+ conn->proto.ftpc.known_filesize = -1; /* unknown size for now */
+
+ return result;
+}
+
+#endif /* CURL_DISABLE_FTP */
diff --git a/libs/libcurl/src/ftp.h b/libs/libcurl/src/ftp.h index 7f6f4328d1..9a27adbb07 100644 --- a/libs/libcurl/src/ftp.h +++ b/libs/libcurl/src/ftp.h @@ -1,161 +1,161 @@ -#ifndef HEADER_CURL_FTP_H -#define HEADER_CURL_FTP_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "pingpong.h" - -#ifndef CURL_DISABLE_FTP -extern const struct Curl_handler Curl_handler_ftp; - -#ifdef USE_SSL -extern const struct Curl_handler Curl_handler_ftps; -#endif - -CURLcode Curl_GetFTPResponse(struct Curl_easy *data, ssize_t *nread, - int *ftpcode); -#endif /* CURL_DISABLE_FTP */ - -/**************************************************************************** - * FTP unique setup - ***************************************************************************/ -typedef enum { - FTP_STOP, /* do nothing state, stops the state machine */ - FTP_WAIT220, /* waiting for the initial 220 response immediately after - a connect */ - FTP_AUTH, - FTP_USER, - FTP_PASS, - FTP_ACCT, - FTP_PBSZ, - FTP_PROT, - FTP_CCC, - FTP_PWD, - FTP_SYST, - FTP_NAMEFMT, - FTP_QUOTE, /* waiting for a response to a command sent in a quote list */ - FTP_RETR_PREQUOTE, - FTP_STOR_PREQUOTE, - FTP_POSTQUOTE, - FTP_CWD, /* change dir */ - FTP_MKD, /* if the dir didn't exist */ - FTP_MDTM, /* to figure out the datestamp */ - FTP_TYPE, /* to set type when doing a head-like request */ - FTP_LIST_TYPE, /* set type when about to do a dir list */ - FTP_RETR_TYPE, /* set type when about to RETR a file */ - FTP_STOR_TYPE, /* set type when about to STOR a file */ - FTP_SIZE, /* get the remote file's size for head-like request */ - FTP_RETR_SIZE, /* get the remote file's size for RETR */ - FTP_STOR_SIZE, /* get the size for STOR */ - FTP_REST, /* when used to check if the server supports it in head-like */ - FTP_RETR_REST, /* when asking for "resume" in for RETR */ - FTP_PORT, /* generic state for PORT, LPRT and EPRT, check count1 */ - FTP_PRET, /* generic state for PRET RETR, PRET STOR and PRET LIST/NLST */ - FTP_PASV, /* generic state for PASV and EPSV, check count1 */ - FTP_LIST, /* generic state for LIST, NLST or a custom list command */ - FTP_RETR, - FTP_STOR, /* generic state for STOR and APPE */ - FTP_QUIT, - FTP_LAST /* never used */ -} ftpstate; - -struct ftp_parselist_data; /* defined later in ftplistparser.c */ - -struct ftp_wc { - struct ftp_parselist_data *parser; - - struct { - curl_write_callback write_function; - FILE *file_descriptor; - } backup; -}; - -typedef enum { - FTPFILE_MULTICWD = 1, /* as defined by RFC1738 */ - FTPFILE_NOCWD = 2, /* use SIZE / RETR / STOR on the full path */ - FTPFILE_SINGLECWD = 3 /* make one CWD, then SIZE / RETR / STOR on the - file */ -} curl_ftpfile; - -/* This FTP struct is used in the Curl_easy. All FTP data that is - connection-oriented must be in FTP_conn to properly deal with the fact that - perhaps the Curl_easy is changed between the times the connection is - used. */ -struct FTP { - char *path; /* points to the urlpieces struct field */ - char *pathalloc; /* if non-NULL a pointer to an allocated path */ - - /* transfer a file/body or not, done as a typedefed enum just to make - debuggers display the full symbol and not just the numerical value */ - curl_pp_transfer transfer; - curl_off_t downloadsize; -}; - - -/* ftp_conn is used for struct connection-oriented data in the connectdata - struct */ -struct ftp_conn { - struct pingpong pp; - char *entrypath; /* the PWD reply when we logged on */ - char *file; /* url-decoded file name (or path) */ - char **dirs; /* realloc()ed array for path components */ - int dirdepth; /* number of entries used in the 'dirs' array */ - bool dont_check; /* Set to TRUE to prevent the final (post-transfer) - file size and 226/250 status check. It should still - read the line, just ignore the result. */ - bool ctl_valid; /* Tells Curl_ftp_quit() whether or not to do anything. If - the connection has timed out or been closed, this - should be FALSE when it gets to Curl_ftp_quit() */ - bool cwddone; /* if it has been determined that the proper CWD combo - already has been done */ - int cwdcount; /* number of CWD commands issued */ - bool cwdfail; /* set TRUE if a CWD command fails, as then we must prevent - caching the current directory */ - bool wait_data_conn; /* this is set TRUE if data connection is waited */ - /* newhost is the (allocated) IP addr or host name to connect the data - connection to */ - unsigned short newport; - char *newhost; - char *prevpath; /* url-decoded conn->path from the previous transfer */ - char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a - and others (A/I or zero) */ - int count1; /* general purpose counter for the state machine */ - int count2; /* general purpose counter for the state machine */ - int count3; /* general purpose counter for the state machine */ - ftpstate state; /* always use ftp.c:state() to change state! */ - ftpstate state_saved; /* transfer type saved to be reloaded after - data connection is established */ - curl_off_t retr_size_saved; /* Size of retrieved file saved */ - char *server_os; /* The target server operating system. */ - curl_off_t known_filesize; /* file size is different from -1, if wildcard - LIST parsing was done and wc_statemach set - it */ - BIT(ftp_trying_alternative); -}; - -#define DEFAULT_ACCEPT_TIMEOUT 60000 /* milliseconds == one minute */ - -#endif /* HEADER_CURL_FTP_H */ +#ifndef HEADER_CURL_FTP_H
+#define HEADER_CURL_FTP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "pingpong.h"
+
+#ifndef CURL_DISABLE_FTP
+extern const struct Curl_handler Curl_handler_ftp;
+
+#ifdef USE_SSL
+extern const struct Curl_handler Curl_handler_ftps;
+#endif
+
+CURLcode Curl_GetFTPResponse(struct Curl_easy *data, ssize_t *nread,
+ int *ftpcode);
+#endif /* CURL_DISABLE_FTP */
+
+/****************************************************************************
+ * FTP unique setup
+ ***************************************************************************/
+typedef enum {
+ FTP_STOP, /* do nothing state, stops the state machine */
+ FTP_WAIT220, /* waiting for the initial 220 response immediately after
+ a connect */
+ FTP_AUTH,
+ FTP_USER,
+ FTP_PASS,
+ FTP_ACCT,
+ FTP_PBSZ,
+ FTP_PROT,
+ FTP_CCC,
+ FTP_PWD,
+ FTP_SYST,
+ FTP_NAMEFMT,
+ FTP_QUOTE, /* waiting for a response to a command sent in a quote list */
+ FTP_RETR_PREQUOTE,
+ FTP_STOR_PREQUOTE,
+ FTP_POSTQUOTE,
+ FTP_CWD, /* change dir */
+ FTP_MKD, /* if the dir didn't exist */
+ FTP_MDTM, /* to figure out the datestamp */
+ FTP_TYPE, /* to set type when doing a head-like request */
+ FTP_LIST_TYPE, /* set type when about to do a dir list */
+ FTP_RETR_TYPE, /* set type when about to RETR a file */
+ FTP_STOR_TYPE, /* set type when about to STOR a file */
+ FTP_SIZE, /* get the remote file's size for head-like request */
+ FTP_RETR_SIZE, /* get the remote file's size for RETR */
+ FTP_STOR_SIZE, /* get the size for STOR */
+ FTP_REST, /* when used to check if the server supports it in head-like */
+ FTP_RETR_REST, /* when asking for "resume" in for RETR */
+ FTP_PORT, /* generic state for PORT, LPRT and EPRT, check count1 */
+ FTP_PRET, /* generic state for PRET RETR, PRET STOR and PRET LIST/NLST */
+ FTP_PASV, /* generic state for PASV and EPSV, check count1 */
+ FTP_LIST, /* generic state for LIST, NLST or a custom list command */
+ FTP_RETR,
+ FTP_STOR, /* generic state for STOR and APPE */
+ FTP_QUIT,
+ FTP_LAST /* never used */
+} ftpstate;
+
+struct ftp_parselist_data; /* defined later in ftplistparser.c */
+
+struct ftp_wc {
+ struct ftp_parselist_data *parser;
+
+ struct {
+ curl_write_callback write_function;
+ FILE *file_descriptor;
+ } backup;
+};
+
+typedef enum {
+ FTPFILE_MULTICWD = 1, /* as defined by RFC1738 */
+ FTPFILE_NOCWD = 2, /* use SIZE / RETR / STOR on the full path */
+ FTPFILE_SINGLECWD = 3 /* make one CWD, then SIZE / RETR / STOR on the
+ file */
+} curl_ftpfile;
+
+/* This FTP struct is used in the Curl_easy. All FTP data that is
+ connection-oriented must be in FTP_conn to properly deal with the fact that
+ perhaps the Curl_easy is changed between the times the connection is
+ used. */
+struct FTP {
+ char *path; /* points to the urlpieces struct field */
+ char *pathalloc; /* if non-NULL a pointer to an allocated path */
+
+ /* transfer a file/body or not, done as a typedefed enum just to make
+ debuggers display the full symbol and not just the numerical value */
+ curl_pp_transfer transfer;
+ curl_off_t downloadsize;
+};
+
+
+/* ftp_conn is used for struct connection-oriented data in the connectdata
+ struct */
+struct ftp_conn {
+ struct pingpong pp;
+ char *entrypath; /* the PWD reply when we logged on */
+ char *file; /* url-decoded file name (or path) */
+ char **dirs; /* realloc()ed array for path components */
+ int dirdepth; /* number of entries used in the 'dirs' array */
+ bool dont_check; /* Set to TRUE to prevent the final (post-transfer)
+ file size and 226/250 status check. It should still
+ read the line, just ignore the result. */
+ bool ctl_valid; /* Tells Curl_ftp_quit() whether or not to do anything. If
+ the connection has timed out or been closed, this
+ should be FALSE when it gets to Curl_ftp_quit() */
+ bool cwddone; /* if it has been determined that the proper CWD combo
+ already has been done */
+ int cwdcount; /* number of CWD commands issued */
+ bool cwdfail; /* set TRUE if a CWD command fails, as then we must prevent
+ caching the current directory */
+ bool wait_data_conn; /* this is set TRUE if data connection is waited */
+ /* newhost is the (allocated) IP addr or host name to connect the data
+ connection to */
+ unsigned short newport;
+ char *newhost;
+ char *prevpath; /* url-decoded conn->path from the previous transfer */
+ char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a
+ and others (A/I or zero) */
+ int count1; /* general purpose counter for the state machine */
+ int count2; /* general purpose counter for the state machine */
+ int count3; /* general purpose counter for the state machine */
+ ftpstate state; /* always use ftp.c:state() to change state! */
+ ftpstate state_saved; /* transfer type saved to be reloaded after
+ data connection is established */
+ curl_off_t retr_size_saved; /* Size of retrieved file saved */
+ char *server_os; /* The target server operating system. */
+ curl_off_t known_filesize; /* file size is different from -1, if wildcard
+ LIST parsing was done and wc_statemach set
+ it */
+ BIT(ftp_trying_alternative);
+};
+
+#define DEFAULT_ACCEPT_TIMEOUT 60000 /* milliseconds == one minute */
+
+#endif /* HEADER_CURL_FTP_H */
diff --git a/libs/libcurl/src/ftplistparser.c b/libs/libcurl/src/ftplistparser.c index 40f5f3f189..d00654ef3e 100644 --- a/libs/libcurl/src/ftplistparser.c +++ b/libs/libcurl/src/ftplistparser.c @@ -1,1020 +1,1020 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - -/** - * Now implemented: - * - * 1) Unix version 1 - * drwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog - * 2) Unix version 2 - * drwxr-xr-x 1 user01 ftp 512 Jan 29 1997 prog - * 3) Unix version 3 - * drwxr-xr-x 1 1 1 512 Jan 29 23:32 prog - * 4) Unix symlink - * lrwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog -> prog2000 - * 5) DOS style - * 01-29-97 11:32PM <DIR> prog - */ - -#include "curl_setup.h" - -#ifndef CURL_DISABLE_FTP - -#include <curl/curl.h> - -#include "urldata.h" -#include "fileinfo.h" -#include "llist.h" -#include "strtoofft.h" -#include "ftp.h" -#include "ftplistparser.h" -#include "curl_fnmatch.h" -#include "curl_memory.h" -#include "multiif.h" -/* The last #include file should be: */ -#include "memdebug.h" - -/* allocs buffer which will contain one line of LIST command response */ -#define FTP_BUFFER_ALLOCSIZE 160 - -typedef enum { - PL_UNIX_TOTALSIZE = 0, - PL_UNIX_FILETYPE, - PL_UNIX_PERMISSION, - PL_UNIX_HLINKS, - PL_UNIX_USER, - PL_UNIX_GROUP, - PL_UNIX_SIZE, - PL_UNIX_TIME, - PL_UNIX_FILENAME, - PL_UNIX_SYMLINK -} pl_unix_mainstate; - -typedef union { - enum { - PL_UNIX_TOTALSIZE_INIT = 0, - PL_UNIX_TOTALSIZE_READING - } total_dirsize; - - enum { - PL_UNIX_HLINKS_PRESPACE = 0, - PL_UNIX_HLINKS_NUMBER - } hlinks; - - enum { - PL_UNIX_USER_PRESPACE = 0, - PL_UNIX_USER_PARSING - } user; - - enum { - PL_UNIX_GROUP_PRESPACE = 0, - PL_UNIX_GROUP_NAME - } group; - - enum { - PL_UNIX_SIZE_PRESPACE = 0, - PL_UNIX_SIZE_NUMBER - } size; - - enum { - PL_UNIX_TIME_PREPART1 = 0, - PL_UNIX_TIME_PART1, - PL_UNIX_TIME_PREPART2, - PL_UNIX_TIME_PART2, - PL_UNIX_TIME_PREPART3, - PL_UNIX_TIME_PART3 - } time; - - enum { - PL_UNIX_FILENAME_PRESPACE = 0, - PL_UNIX_FILENAME_NAME, - PL_UNIX_FILENAME_WINDOWSEOL - } filename; - - enum { - PL_UNIX_SYMLINK_PRESPACE = 0, - PL_UNIX_SYMLINK_NAME, - PL_UNIX_SYMLINK_PRETARGET1, - PL_UNIX_SYMLINK_PRETARGET2, - PL_UNIX_SYMLINK_PRETARGET3, - PL_UNIX_SYMLINK_PRETARGET4, - PL_UNIX_SYMLINK_TARGET, - PL_UNIX_SYMLINK_WINDOWSEOL - } symlink; -} pl_unix_substate; - -typedef enum { - PL_WINNT_DATE = 0, - PL_WINNT_TIME, - PL_WINNT_DIRORSIZE, - PL_WINNT_FILENAME -} pl_winNT_mainstate; - -typedef union { - enum { - PL_WINNT_TIME_PRESPACE = 0, - PL_WINNT_TIME_TIME - } time; - enum { - PL_WINNT_DIRORSIZE_PRESPACE = 0, - PL_WINNT_DIRORSIZE_CONTENT - } dirorsize; - enum { - PL_WINNT_FILENAME_PRESPACE = 0, - PL_WINNT_FILENAME_CONTENT, - PL_WINNT_FILENAME_WINEOL - } filename; -} pl_winNT_substate; - -/* This struct is used in wildcard downloading - for parsing LIST response */ -struct ftp_parselist_data { - enum { - OS_TYPE_UNKNOWN = 0, - OS_TYPE_UNIX, - OS_TYPE_WIN_NT - } os_type; - - union { - struct { - pl_unix_mainstate main; - pl_unix_substate sub; - } UNIX; - - struct { - pl_winNT_mainstate main; - pl_winNT_substate sub; - } NT; - } state; - - CURLcode error; - struct fileinfo *file_data; - unsigned int item_length; - size_t item_offset; - struct { - size_t filename; - size_t user; - size_t group; - size_t time; - size_t perm; - size_t symlink_target; - } offsets; -}; - -struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void) -{ - return calloc(1, sizeof(struct ftp_parselist_data)); -} - - -void Curl_ftp_parselist_data_free(struct ftp_parselist_data **parserp) -{ - struct ftp_parselist_data *parser = *parserp; - if(parser) - Curl_fileinfo_cleanup(parser->file_data); - free(parser); - *parserp = NULL; -} - - -CURLcode Curl_ftp_parselist_geterror(struct ftp_parselist_data *pl_data) -{ - return pl_data->error; -} - - -#define FTP_LP_MALFORMATED_PERM 0x01000000 - -static int ftp_pl_get_permission(const char *str) -{ - int permissions = 0; - /* USER */ - if(str[0] == 'r') - permissions |= 1 << 8; - else if(str[0] != '-') - permissions |= FTP_LP_MALFORMATED_PERM; - if(str[1] == 'w') - permissions |= 1 << 7; - else if(str[1] != '-') - permissions |= FTP_LP_MALFORMATED_PERM; - - if(str[2] == 'x') - permissions |= 1 << 6; - else if(str[2] == 's') { - permissions |= 1 << 6; - permissions |= 1 << 11; - } - else if(str[2] == 'S') - permissions |= 1 << 11; - else if(str[2] != '-') - permissions |= FTP_LP_MALFORMATED_PERM; - /* GROUP */ - if(str[3] == 'r') - permissions |= 1 << 5; - else if(str[3] != '-') - permissions |= FTP_LP_MALFORMATED_PERM; - if(str[4] == 'w') - permissions |= 1 << 4; - else if(str[4] != '-') - permissions |= FTP_LP_MALFORMATED_PERM; - if(str[5] == 'x') - permissions |= 1 << 3; - else if(str[5] == 's') { - permissions |= 1 << 3; - permissions |= 1 << 10; - } - else if(str[5] == 'S') - permissions |= 1 << 10; - else if(str[5] != '-') - permissions |= FTP_LP_MALFORMATED_PERM; - /* others */ - if(str[6] == 'r') - permissions |= 1 << 2; - else if(str[6] != '-') - permissions |= FTP_LP_MALFORMATED_PERM; - if(str[7] == 'w') - permissions |= 1 << 1; - else if(str[7] != '-') - permissions |= FTP_LP_MALFORMATED_PERM; - if(str[8] == 'x') - permissions |= 1; - else if(str[8] == 't') { - permissions |= 1; - permissions |= 1 << 9; - } - else if(str[8] == 'T') - permissions |= 1 << 9; - else if(str[8] != '-') - permissions |= FTP_LP_MALFORMATED_PERM; - - return permissions; -} - -static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data, - struct fileinfo *infop) -{ - curl_fnmatch_callback compare; - struct WildcardData *wc = &data->wildcard; - struct ftp_wc *ftpwc = wc->protdata; - struct Curl_llist *llist = &wc->filelist; - struct ftp_parselist_data *parser = ftpwc->parser; - bool add = TRUE; - struct curl_fileinfo *finfo = &infop->info; - - /* move finfo pointers to b_data */ - char *str = finfo->b_data; - finfo->filename = str + parser->offsets.filename; - finfo->strings.group = parser->offsets.group ? - str + parser->offsets.group : NULL; - finfo->strings.perm = parser->offsets.perm ? - str + parser->offsets.perm : NULL; - finfo->strings.target = parser->offsets.symlink_target ? - str + parser->offsets.symlink_target : NULL; - finfo->strings.time = str + parser->offsets.time; - finfo->strings.user = parser->offsets.user ? - str + parser->offsets.user : NULL; - - /* get correct fnmatch callback */ - compare = data->set.fnmatch; - if(!compare) - compare = Curl_fnmatch; - - /* filter pattern-corresponding filenames */ - Curl_set_in_callback(data, true); - if(compare(data->set.fnmatch_data, wc->pattern, - finfo->filename) == 0) { - /* discard symlink which is containing multiple " -> " */ - if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target && - (strstr(finfo->strings.target, " -> "))) { - add = FALSE; - } - } - else { - add = FALSE; - } - Curl_set_in_callback(data, false); - - if(add) { - Curl_llist_insert_next(llist, llist->tail, finfo, &infop->list); - } - else { - Curl_fileinfo_cleanup(infop); - } - - ftpwc->parser->file_data = NULL; - return CURLE_OK; -} - -size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, - void *connptr) -{ - size_t bufflen = size*nmemb; - struct Curl_easy *data = (struct Curl_easy *)connptr; - struct ftp_wc *ftpwc = data->wildcard.protdata; - struct ftp_parselist_data *parser = ftpwc->parser; - struct fileinfo *infop; - struct curl_fileinfo *finfo; - unsigned long i = 0; - CURLcode result; - size_t retsize = bufflen; - - if(parser->error) { /* error in previous call */ - /* scenario: - * 1. call => OK.. - * 2. call => OUT_OF_MEMORY (or other error) - * 3. (last) call => is skipped RIGHT HERE and the error is hadled later - * in wc_statemach() - */ - goto fail; - } - - if(parser->os_type == OS_TYPE_UNKNOWN && bufflen > 0) { - /* considering info about FILE response format */ - parser->os_type = (buffer[0] >= '0' && buffer[0] <= '9') ? - OS_TYPE_WIN_NT : OS_TYPE_UNIX; - } - - while(i < bufflen) { /* FSM */ - - char c = buffer[i]; - if(!parser->file_data) { /* tmp file data is not allocated yet */ - parser->file_data = Curl_fileinfo_alloc(); - if(!parser->file_data) { - parser->error = CURLE_OUT_OF_MEMORY; - goto fail; - } - parser->file_data->info.b_data = malloc(FTP_BUFFER_ALLOCSIZE); - if(!parser->file_data->info.b_data) { - parser->error = CURLE_OUT_OF_MEMORY; - goto fail; - } - parser->file_data->info.b_size = FTP_BUFFER_ALLOCSIZE; - parser->item_offset = 0; - parser->item_length = 0; - } - - infop = parser->file_data; - finfo = &infop->info; - finfo->b_data[finfo->b_used++] = c; - - if(finfo->b_used >= finfo->b_size - 1) { - /* if it is important, extend buffer space for file data */ - char *tmp = realloc(finfo->b_data, - finfo->b_size + FTP_BUFFER_ALLOCSIZE); - if(tmp) { - finfo->b_size += FTP_BUFFER_ALLOCSIZE; - finfo->b_data = tmp; - } - else { - Curl_fileinfo_cleanup(parser->file_data); - parser->file_data = NULL; - parser->error = CURLE_OUT_OF_MEMORY; - goto fail; - } - } - - switch(parser->os_type) { - case OS_TYPE_UNIX: - switch(parser->state.UNIX.main) { - case PL_UNIX_TOTALSIZE: - switch(parser->state.UNIX.sub.total_dirsize) { - case PL_UNIX_TOTALSIZE_INIT: - if(c == 't') { - parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING; - parser->item_length++; - } - else { - parser->state.UNIX.main = PL_UNIX_FILETYPE; - /* start FSM again not considering size of directory */ - finfo->b_used = 0; - continue; - } - break; - case PL_UNIX_TOTALSIZE_READING: - parser->item_length++; - if(c == '\r') { - parser->item_length--; - finfo->b_used--; - } - else if(c == '\n') { - finfo->b_data[parser->item_length - 1] = 0; - if(strncmp("total ", finfo->b_data, 6) == 0) { - char *endptr = finfo->b_data + 6; - /* here we can deal with directory size, pass the leading - whitespace and then the digits */ - while(ISBLANK(*endptr)) - endptr++; - while(ISDIGIT(*endptr)) - endptr++; - if(*endptr) { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - parser->state.UNIX.main = PL_UNIX_FILETYPE; - finfo->b_used = 0; - } - else { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - } - break; - } - break; - case PL_UNIX_FILETYPE: - switch(c) { - case '-': - finfo->filetype = CURLFILETYPE_FILE; - break; - case 'd': - finfo->filetype = CURLFILETYPE_DIRECTORY; - break; - case 'l': - finfo->filetype = CURLFILETYPE_SYMLINK; - break; - case 'p': - finfo->filetype = CURLFILETYPE_NAMEDPIPE; - break; - case 's': - finfo->filetype = CURLFILETYPE_SOCKET; - break; - case 'c': - finfo->filetype = CURLFILETYPE_DEVICE_CHAR; - break; - case 'b': - finfo->filetype = CURLFILETYPE_DEVICE_BLOCK; - break; - case 'D': - finfo->filetype = CURLFILETYPE_DOOR; - break; - default: - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - parser->state.UNIX.main = PL_UNIX_PERMISSION; - parser->item_length = 0; - parser->item_offset = 1; - break; - case PL_UNIX_PERMISSION: - parser->item_length++; - if(parser->item_length <= 9) { - if(!strchr("rwx-tTsS", c)) { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - } - else if(parser->item_length == 10) { - unsigned int perm; - if(c != ' ') { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - finfo->b_data[10] = 0; /* terminate permissions */ - perm = ftp_pl_get_permission(finfo->b_data + parser->item_offset); - if(perm & FTP_LP_MALFORMATED_PERM) { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM; - parser->file_data->info.perm = perm; - parser->offsets.perm = parser->item_offset; - - parser->item_length = 0; - parser->state.UNIX.main = PL_UNIX_HLINKS; - parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE; - } - break; - case PL_UNIX_HLINKS: - switch(parser->state.UNIX.sub.hlinks) { - case PL_UNIX_HLINKS_PRESPACE: - if(c != ' ') { - if(c >= '0' && c <= '9') { - parser->item_offset = finfo->b_used - 1; - parser->item_length = 1; - parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER; - } - else { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - } - break; - case PL_UNIX_HLINKS_NUMBER: - parser->item_length ++; - if(c == ' ') { - char *p; - long int hlinks; - finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; - hlinks = strtol(finfo->b_data + parser->item_offset, &p, 10); - if(p[0] == '\0' && hlinks != LONG_MAX && hlinks != LONG_MIN) { - parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT; - parser->file_data->info.hardlinks = hlinks; - } - parser->item_length = 0; - parser->item_offset = 0; - parser->state.UNIX.main = PL_UNIX_USER; - parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE; - } - else if(c < '0' || c > '9') { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - break; - } - break; - case PL_UNIX_USER: - switch(parser->state.UNIX.sub.user) { - case PL_UNIX_USER_PRESPACE: - if(c != ' ') { - parser->item_offset = finfo->b_used - 1; - parser->item_length = 1; - parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING; - } - break; - case PL_UNIX_USER_PARSING: - parser->item_length++; - if(c == ' ') { - finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; - parser->offsets.user = parser->item_offset; - parser->state.UNIX.main = PL_UNIX_GROUP; - parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE; - parser->item_offset = 0; - parser->item_length = 0; - } - break; - } - break; - case PL_UNIX_GROUP: - switch(parser->state.UNIX.sub.group) { - case PL_UNIX_GROUP_PRESPACE: - if(c != ' ') { - parser->item_offset = finfo->b_used - 1; - parser->item_length = 1; - parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME; - } - break; - case PL_UNIX_GROUP_NAME: - parser->item_length++; - if(c == ' ') { - finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; - parser->offsets.group = parser->item_offset; - parser->state.UNIX.main = PL_UNIX_SIZE; - parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE; - parser->item_offset = 0; - parser->item_length = 0; - } - break; - } - break; - case PL_UNIX_SIZE: - switch(parser->state.UNIX.sub.size) { - case PL_UNIX_SIZE_PRESPACE: - if(c != ' ') { - if(c >= '0' && c <= '9') { - parser->item_offset = finfo->b_used - 1; - parser->item_length = 1; - parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER; - } - else { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - } - break; - case PL_UNIX_SIZE_NUMBER: - parser->item_length++; - if(c == ' ') { - char *p; - curl_off_t fsize; - finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; - if(!curlx_strtoofft(finfo->b_data + parser->item_offset, - &p, 10, &fsize)) { - if(p[0] == '\0' && fsize != CURL_OFF_T_MAX && - fsize != CURL_OFF_T_MIN) { - parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE; - parser->file_data->info.size = fsize; - } - parser->item_length = 0; - parser->item_offset = 0; - parser->state.UNIX.main = PL_UNIX_TIME; - parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1; - } - } - else if(!ISDIGIT(c)) { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - break; - } - break; - case PL_UNIX_TIME: - switch(parser->state.UNIX.sub.time) { - case PL_UNIX_TIME_PREPART1: - if(c != ' ') { - if(ISALNUM(c)) { - parser->item_offset = finfo->b_used -1; - parser->item_length = 1; - parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1; - } - else { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - } - break; - case PL_UNIX_TIME_PART1: - parser->item_length++; - if(c == ' ') { - parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2; - } - else if(!ISALNUM(c) && c != '.') { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - break; - case PL_UNIX_TIME_PREPART2: - parser->item_length++; - if(c != ' ') { - if(ISALNUM(c)) { - parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2; - } - else { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - } - break; - case PL_UNIX_TIME_PART2: - parser->item_length++; - if(c == ' ') { - parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3; - } - else if(!ISALNUM(c) && c != '.') { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - break; - case PL_UNIX_TIME_PREPART3: - parser->item_length++; - if(c != ' ') { - if(ISALNUM(c)) { - parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3; - } - else { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - } - break; - case PL_UNIX_TIME_PART3: - parser->item_length++; - if(c == ' ') { - finfo->b_data[parser->item_offset + parser->item_length -1] = 0; - parser->offsets.time = parser->item_offset; - /* - if(ftp_pl_gettime(parser, finfo->b_data + parser->item_offset)) { - parser->file_data->flags |= CURLFINFOFLAG_KNOWN_TIME; - } - */ - if(finfo->filetype == CURLFILETYPE_SYMLINK) { - parser->state.UNIX.main = PL_UNIX_SYMLINK; - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE; - } - else { - parser->state.UNIX.main = PL_UNIX_FILENAME; - parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE; - } - } - else if(!ISALNUM(c) && c != '.' && c != ':') { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - break; - } - break; - case PL_UNIX_FILENAME: - switch(parser->state.UNIX.sub.filename) { - case PL_UNIX_FILENAME_PRESPACE: - if(c != ' ') { - parser->item_offset = finfo->b_used - 1; - parser->item_length = 1; - parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME; - } - break; - case PL_UNIX_FILENAME_NAME: - parser->item_length++; - if(c == '\r') { - parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL; - } - else if(c == '\n') { - finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; - parser->offsets.filename = parser->item_offset; - parser->state.UNIX.main = PL_UNIX_FILETYPE; - result = ftp_pl_insert_finfo(data, infop); - if(result) { - parser->error = result; - goto fail; - } - } - break; - case PL_UNIX_FILENAME_WINDOWSEOL: - if(c == '\n') { - finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; - parser->offsets.filename = parser->item_offset; - parser->state.UNIX.main = PL_UNIX_FILETYPE; - result = ftp_pl_insert_finfo(data, infop); - if(result) { - parser->error = result; - goto fail; - } - } - else { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - break; - } - break; - case PL_UNIX_SYMLINK: - switch(parser->state.UNIX.sub.symlink) { - case PL_UNIX_SYMLINK_PRESPACE: - if(c != ' ') { - parser->item_offset = finfo->b_used - 1; - parser->item_length = 1; - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; - } - break; - case PL_UNIX_SYMLINK_NAME: - parser->item_length++; - if(c == ' ') { - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1; - } - else if(c == '\r' || c == '\n') { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - break; - case PL_UNIX_SYMLINK_PRETARGET1: - parser->item_length++; - if(c == '-') { - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2; - } - else if(c == '\r' || c == '\n') { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - else { - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; - } - break; - case PL_UNIX_SYMLINK_PRETARGET2: - parser->item_length++; - if(c == '>') { - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3; - } - else if(c == '\r' || c == '\n') { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - else { - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; - } - break; - case PL_UNIX_SYMLINK_PRETARGET3: - parser->item_length++; - if(c == ' ') { - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4; - /* now place where is symlink following */ - finfo->b_data[parser->item_offset + parser->item_length - 4] = 0; - parser->offsets.filename = parser->item_offset; - parser->item_length = 0; - parser->item_offset = 0; - } - else if(c == '\r' || c == '\n') { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - else { - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; - } - break; - case PL_UNIX_SYMLINK_PRETARGET4: - if(c != '\r' && c != '\n') { - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET; - parser->item_offset = finfo->b_used - 1; - parser->item_length = 1; - } - else { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - break; - case PL_UNIX_SYMLINK_TARGET: - parser->item_length++; - if(c == '\r') { - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL; - } - else if(c == '\n') { - finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; - parser->offsets.symlink_target = parser->item_offset; - result = ftp_pl_insert_finfo(data, infop); - if(result) { - parser->error = result; - goto fail; - } - parser->state.UNIX.main = PL_UNIX_FILETYPE; - } - break; - case PL_UNIX_SYMLINK_WINDOWSEOL: - if(c == '\n') { - finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; - parser->offsets.symlink_target = parser->item_offset; - result = ftp_pl_insert_finfo(data, infop); - if(result) { - parser->error = result; - goto fail; - } - parser->state.UNIX.main = PL_UNIX_FILETYPE; - } - else { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - break; - } - break; - } - break; - case OS_TYPE_WIN_NT: - switch(parser->state.NT.main) { - case PL_WINNT_DATE: - parser->item_length++; - if(parser->item_length < 9) { - if(!strchr("0123456789-", c)) { /* only simple control */ - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - } - else if(parser->item_length == 9) { - if(c == ' ') { - parser->state.NT.main = PL_WINNT_TIME; - parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE; - } - else { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - } - else { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - break; - case PL_WINNT_TIME: - parser->item_length++; - switch(parser->state.NT.sub.time) { - case PL_WINNT_TIME_PRESPACE: - if(!ISBLANK(c)) { - parser->state.NT.sub.time = PL_WINNT_TIME_TIME; - } - break; - case PL_WINNT_TIME_TIME: - if(c == ' ') { - parser->offsets.time = parser->item_offset; - finfo->b_data[parser->item_offset + parser->item_length -1] = 0; - parser->state.NT.main = PL_WINNT_DIRORSIZE; - parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE; - parser->item_length = 0; - } - else if(!strchr("APM0123456789:", c)) { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - break; - } - break; - case PL_WINNT_DIRORSIZE: - switch(parser->state.NT.sub.dirorsize) { - case PL_WINNT_DIRORSIZE_PRESPACE: - if(c != ' ') { - parser->item_offset = finfo->b_used - 1; - parser->item_length = 1; - parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT; - } - break; - case PL_WINNT_DIRORSIZE_CONTENT: - parser->item_length ++; - if(c == ' ') { - finfo->b_data[parser->item_offset + parser->item_length - 1] = 0; - if(strcmp("<DIR>", finfo->b_data + parser->item_offset) == 0) { - finfo->filetype = CURLFILETYPE_DIRECTORY; - finfo->size = 0; - } - else { - char *endptr; - if(curlx_strtoofft(finfo->b_data + - parser->item_offset, - &endptr, 10, &finfo->size)) { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - /* correct file type */ - parser->file_data->info.filetype = CURLFILETYPE_FILE; - } - - parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE; - parser->item_length = 0; - parser->state.NT.main = PL_WINNT_FILENAME; - parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE; - } - break; - } - break; - case PL_WINNT_FILENAME: - switch(parser->state.NT.sub.filename) { - case PL_WINNT_FILENAME_PRESPACE: - if(c != ' ') { - parser->item_offset = finfo->b_used -1; - parser->item_length = 1; - parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT; - } - break; - case PL_WINNT_FILENAME_CONTENT: - parser->item_length++; - if(c == '\r') { - parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL; - finfo->b_data[finfo->b_used - 1] = 0; - } - else if(c == '\n') { - parser->offsets.filename = parser->item_offset; - finfo->b_data[finfo->b_used - 1] = 0; - result = ftp_pl_insert_finfo(data, infop); - if(result) { - parser->error = result; - goto fail; - } - parser->state.NT.main = PL_WINNT_DATE; - parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE; - } - break; - case PL_WINNT_FILENAME_WINEOL: - if(c == '\n') { - parser->offsets.filename = parser->item_offset; - result = ftp_pl_insert_finfo(data, infop); - if(result) { - parser->error = result; - goto fail; - } - parser->state.NT.main = PL_WINNT_DATE; - parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE; - } - else { - parser->error = CURLE_FTP_BAD_FILE_LIST; - goto fail; - } - break; - } - break; - } - break; - default: - retsize = bufflen + 1; - goto fail; - } - - i++; - } - return retsize; - -fail: - - /* Clean up any allocated memory. */ - if(parser->file_data) { - Curl_fileinfo_cleanup(parser->file_data); - parser->file_data = NULL; - } - - return retsize; -} - -#endif /* CURL_DISABLE_FTP */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+/**
+ * Now implemented:
+ *
+ * 1) Unix version 1
+ * drwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog
+ * 2) Unix version 2
+ * drwxr-xr-x 1 user01 ftp 512 Jan 29 1997 prog
+ * 3) Unix version 3
+ * drwxr-xr-x 1 1 1 512 Jan 29 23:32 prog
+ * 4) Unix symlink
+ * lrwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog -> prog2000
+ * 5) DOS style
+ * 01-29-97 11:32PM <DIR> prog
+ */
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_FTP
+
+#include <curl/curl.h>
+
+#include "urldata.h"
+#include "fileinfo.h"
+#include "llist.h"
+#include "strtoofft.h"
+#include "ftp.h"
+#include "ftplistparser.h"
+#include "curl_fnmatch.h"
+#include "curl_memory.h"
+#include "multiif.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/* allocs buffer which will contain one line of LIST command response */
+#define FTP_BUFFER_ALLOCSIZE 160
+
+typedef enum {
+ PL_UNIX_TOTALSIZE = 0,
+ PL_UNIX_FILETYPE,
+ PL_UNIX_PERMISSION,
+ PL_UNIX_HLINKS,
+ PL_UNIX_USER,
+ PL_UNIX_GROUP,
+ PL_UNIX_SIZE,
+ PL_UNIX_TIME,
+ PL_UNIX_FILENAME,
+ PL_UNIX_SYMLINK
+} pl_unix_mainstate;
+
+typedef union {
+ enum {
+ PL_UNIX_TOTALSIZE_INIT = 0,
+ PL_UNIX_TOTALSIZE_READING
+ } total_dirsize;
+
+ enum {
+ PL_UNIX_HLINKS_PRESPACE = 0,
+ PL_UNIX_HLINKS_NUMBER
+ } hlinks;
+
+ enum {
+ PL_UNIX_USER_PRESPACE = 0,
+ PL_UNIX_USER_PARSING
+ } user;
+
+ enum {
+ PL_UNIX_GROUP_PRESPACE = 0,
+ PL_UNIX_GROUP_NAME
+ } group;
+
+ enum {
+ PL_UNIX_SIZE_PRESPACE = 0,
+ PL_UNIX_SIZE_NUMBER
+ } size;
+
+ enum {
+ PL_UNIX_TIME_PREPART1 = 0,
+ PL_UNIX_TIME_PART1,
+ PL_UNIX_TIME_PREPART2,
+ PL_UNIX_TIME_PART2,
+ PL_UNIX_TIME_PREPART3,
+ PL_UNIX_TIME_PART3
+ } time;
+
+ enum {
+ PL_UNIX_FILENAME_PRESPACE = 0,
+ PL_UNIX_FILENAME_NAME,
+ PL_UNIX_FILENAME_WINDOWSEOL
+ } filename;
+
+ enum {
+ PL_UNIX_SYMLINK_PRESPACE = 0,
+ PL_UNIX_SYMLINK_NAME,
+ PL_UNIX_SYMLINK_PRETARGET1,
+ PL_UNIX_SYMLINK_PRETARGET2,
+ PL_UNIX_SYMLINK_PRETARGET3,
+ PL_UNIX_SYMLINK_PRETARGET4,
+ PL_UNIX_SYMLINK_TARGET,
+ PL_UNIX_SYMLINK_WINDOWSEOL
+ } symlink;
+} pl_unix_substate;
+
+typedef enum {
+ PL_WINNT_DATE = 0,
+ PL_WINNT_TIME,
+ PL_WINNT_DIRORSIZE,
+ PL_WINNT_FILENAME
+} pl_winNT_mainstate;
+
+typedef union {
+ enum {
+ PL_WINNT_TIME_PRESPACE = 0,
+ PL_WINNT_TIME_TIME
+ } time;
+ enum {
+ PL_WINNT_DIRORSIZE_PRESPACE = 0,
+ PL_WINNT_DIRORSIZE_CONTENT
+ } dirorsize;
+ enum {
+ PL_WINNT_FILENAME_PRESPACE = 0,
+ PL_WINNT_FILENAME_CONTENT,
+ PL_WINNT_FILENAME_WINEOL
+ } filename;
+} pl_winNT_substate;
+
+/* This struct is used in wildcard downloading - for parsing LIST response */
+struct ftp_parselist_data {
+ enum {
+ OS_TYPE_UNKNOWN = 0,
+ OS_TYPE_UNIX,
+ OS_TYPE_WIN_NT
+ } os_type;
+
+ union {
+ struct {
+ pl_unix_mainstate main;
+ pl_unix_substate sub;
+ } UNIX;
+
+ struct {
+ pl_winNT_mainstate main;
+ pl_winNT_substate sub;
+ } NT;
+ } state;
+
+ CURLcode error;
+ struct fileinfo *file_data;
+ unsigned int item_length;
+ size_t item_offset;
+ struct {
+ size_t filename;
+ size_t user;
+ size_t group;
+ size_t time;
+ size_t perm;
+ size_t symlink_target;
+ } offsets;
+};
+
+struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void)
+{
+ return calloc(1, sizeof(struct ftp_parselist_data));
+}
+
+
+void Curl_ftp_parselist_data_free(struct ftp_parselist_data **parserp)
+{
+ struct ftp_parselist_data *parser = *parserp;
+ if(parser)
+ Curl_fileinfo_cleanup(parser->file_data);
+ free(parser);
+ *parserp = NULL;
+}
+
+
+CURLcode Curl_ftp_parselist_geterror(struct ftp_parselist_data *pl_data)
+{
+ return pl_data->error;
+}
+
+
+#define FTP_LP_MALFORMATED_PERM 0x01000000
+
+static unsigned int ftp_pl_get_permission(const char *str)
+{
+ unsigned int permissions = 0;
+ /* USER */
+ if(str[0] == 'r')
+ permissions |= 1 << 8;
+ else if(str[0] != '-')
+ permissions |= FTP_LP_MALFORMATED_PERM;
+ if(str[1] == 'w')
+ permissions |= 1 << 7;
+ else if(str[1] != '-')
+ permissions |= FTP_LP_MALFORMATED_PERM;
+
+ if(str[2] == 'x')
+ permissions |= 1 << 6;
+ else if(str[2] == 's') {
+ permissions |= 1 << 6;
+ permissions |= 1 << 11;
+ }
+ else if(str[2] == 'S')
+ permissions |= 1 << 11;
+ else if(str[2] != '-')
+ permissions |= FTP_LP_MALFORMATED_PERM;
+ /* GROUP */
+ if(str[3] == 'r')
+ permissions |= 1 << 5;
+ else if(str[3] != '-')
+ permissions |= FTP_LP_MALFORMATED_PERM;
+ if(str[4] == 'w')
+ permissions |= 1 << 4;
+ else if(str[4] != '-')
+ permissions |= FTP_LP_MALFORMATED_PERM;
+ if(str[5] == 'x')
+ permissions |= 1 << 3;
+ else if(str[5] == 's') {
+ permissions |= 1 << 3;
+ permissions |= 1 << 10;
+ }
+ else if(str[5] == 'S')
+ permissions |= 1 << 10;
+ else if(str[5] != '-')
+ permissions |= FTP_LP_MALFORMATED_PERM;
+ /* others */
+ if(str[6] == 'r')
+ permissions |= 1 << 2;
+ else if(str[6] != '-')
+ permissions |= FTP_LP_MALFORMATED_PERM;
+ if(str[7] == 'w')
+ permissions |= 1 << 1;
+ else if(str[7] != '-')
+ permissions |= FTP_LP_MALFORMATED_PERM;
+ if(str[8] == 'x')
+ permissions |= 1;
+ else if(str[8] == 't') {
+ permissions |= 1;
+ permissions |= 1 << 9;
+ }
+ else if(str[8] == 'T')
+ permissions |= 1 << 9;
+ else if(str[8] != '-')
+ permissions |= FTP_LP_MALFORMATED_PERM;
+
+ return permissions;
+}
+
+static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data,
+ struct fileinfo *infop)
+{
+ curl_fnmatch_callback compare;
+ struct WildcardData *wc = &data->wildcard;
+ struct ftp_wc *ftpwc = wc->protdata;
+ struct Curl_llist *llist = &wc->filelist;
+ struct ftp_parselist_data *parser = ftpwc->parser;
+ bool add = TRUE;
+ struct curl_fileinfo *finfo = &infop->info;
+
+ /* move finfo pointers to b_data */
+ char *str = finfo->b_data;
+ finfo->filename = str + parser->offsets.filename;
+ finfo->strings.group = parser->offsets.group ?
+ str + parser->offsets.group : NULL;
+ finfo->strings.perm = parser->offsets.perm ?
+ str + parser->offsets.perm : NULL;
+ finfo->strings.target = parser->offsets.symlink_target ?
+ str + parser->offsets.symlink_target : NULL;
+ finfo->strings.time = str + parser->offsets.time;
+ finfo->strings.user = parser->offsets.user ?
+ str + parser->offsets.user : NULL;
+
+ /* get correct fnmatch callback */
+ compare = data->set.fnmatch;
+ if(!compare)
+ compare = Curl_fnmatch;
+
+ /* filter pattern-corresponding filenames */
+ Curl_set_in_callback(data, true);
+ if(compare(data->set.fnmatch_data, wc->pattern,
+ finfo->filename) == 0) {
+ /* discard symlink which is containing multiple " -> " */
+ if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target &&
+ (strstr(finfo->strings.target, " -> "))) {
+ add = FALSE;
+ }
+ }
+ else {
+ add = FALSE;
+ }
+ Curl_set_in_callback(data, false);
+
+ if(add) {
+ Curl_llist_insert_next(llist, llist->tail, finfo, &infop->list);
+ }
+ else {
+ Curl_fileinfo_cleanup(infop);
+ }
+
+ ftpwc->parser->file_data = NULL;
+ return CURLE_OK;
+}
+
+size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
+ void *connptr)
+{
+ size_t bufflen = size*nmemb;
+ struct Curl_easy *data = (struct Curl_easy *)connptr;
+ struct ftp_wc *ftpwc = data->wildcard.protdata;
+ struct ftp_parselist_data *parser = ftpwc->parser;
+ struct fileinfo *infop;
+ struct curl_fileinfo *finfo;
+ size_t i = 0;
+ CURLcode result;
+ size_t retsize = bufflen;
+
+ if(parser->error) { /* error in previous call */
+ /* scenario:
+ * 1. call => OK..
+ * 2. call => OUT_OF_MEMORY (or other error)
+ * 3. (last) call => is skipped RIGHT HERE and the error is hadled later
+ * in wc_statemach()
+ */
+ goto fail;
+ }
+
+ if(parser->os_type == OS_TYPE_UNKNOWN && bufflen > 0) {
+ /* considering info about FILE response format */
+ parser->os_type = (buffer[0] >= '0' && buffer[0] <= '9') ?
+ OS_TYPE_WIN_NT : OS_TYPE_UNIX;
+ }
+
+ while(i < bufflen) { /* FSM */
+
+ char c = buffer[i];
+ if(!parser->file_data) { /* tmp file data is not allocated yet */
+ parser->file_data = Curl_fileinfo_alloc();
+ if(!parser->file_data) {
+ parser->error = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ parser->file_data->info.b_data = malloc(FTP_BUFFER_ALLOCSIZE);
+ if(!parser->file_data->info.b_data) {
+ parser->error = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ parser->file_data->info.b_size = FTP_BUFFER_ALLOCSIZE;
+ parser->item_offset = 0;
+ parser->item_length = 0;
+ }
+
+ infop = parser->file_data;
+ finfo = &infop->info;
+ finfo->b_data[finfo->b_used++] = c;
+
+ if(finfo->b_used >= finfo->b_size - 1) {
+ /* if it is important, extend buffer space for file data */
+ char *tmp = realloc(finfo->b_data,
+ finfo->b_size + FTP_BUFFER_ALLOCSIZE);
+ if(tmp) {
+ finfo->b_size += FTP_BUFFER_ALLOCSIZE;
+ finfo->b_data = tmp;
+ }
+ else {
+ Curl_fileinfo_cleanup(parser->file_data);
+ parser->file_data = NULL;
+ parser->error = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ }
+
+ switch(parser->os_type) {
+ case OS_TYPE_UNIX:
+ switch(parser->state.UNIX.main) {
+ case PL_UNIX_TOTALSIZE:
+ switch(parser->state.UNIX.sub.total_dirsize) {
+ case PL_UNIX_TOTALSIZE_INIT:
+ if(c == 't') {
+ parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING;
+ parser->item_length++;
+ }
+ else {
+ parser->state.UNIX.main = PL_UNIX_FILETYPE;
+ /* start FSM again not considering size of directory */
+ finfo->b_used = 0;
+ continue;
+ }
+ break;
+ case PL_UNIX_TOTALSIZE_READING:
+ parser->item_length++;
+ if(c == '\r') {
+ parser->item_length--;
+ finfo->b_used--;
+ }
+ else if(c == '\n') {
+ finfo->b_data[parser->item_length - 1] = 0;
+ if(strncmp("total ", finfo->b_data, 6) == 0) {
+ char *endptr = finfo->b_data + 6;
+ /* here we can deal with directory size, pass the leading
+ whitespace and then the digits */
+ while(ISBLANK(*endptr))
+ endptr++;
+ while(ISDIGIT(*endptr))
+ endptr++;
+ if(*endptr) {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ parser->state.UNIX.main = PL_UNIX_FILETYPE;
+ finfo->b_used = 0;
+ }
+ else {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ }
+ break;
+ }
+ break;
+ case PL_UNIX_FILETYPE:
+ switch(c) {
+ case '-':
+ finfo->filetype = CURLFILETYPE_FILE;
+ break;
+ case 'd':
+ finfo->filetype = CURLFILETYPE_DIRECTORY;
+ break;
+ case 'l':
+ finfo->filetype = CURLFILETYPE_SYMLINK;
+ break;
+ case 'p':
+ finfo->filetype = CURLFILETYPE_NAMEDPIPE;
+ break;
+ case 's':
+ finfo->filetype = CURLFILETYPE_SOCKET;
+ break;
+ case 'c':
+ finfo->filetype = CURLFILETYPE_DEVICE_CHAR;
+ break;
+ case 'b':
+ finfo->filetype = CURLFILETYPE_DEVICE_BLOCK;
+ break;
+ case 'D':
+ finfo->filetype = CURLFILETYPE_DOOR;
+ break;
+ default:
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ parser->state.UNIX.main = PL_UNIX_PERMISSION;
+ parser->item_length = 0;
+ parser->item_offset = 1;
+ break;
+ case PL_UNIX_PERMISSION:
+ parser->item_length++;
+ if(parser->item_length <= 9) {
+ if(!strchr("rwx-tTsS", c)) {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ }
+ else if(parser->item_length == 10) {
+ unsigned int perm;
+ if(c != ' ') {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ finfo->b_data[10] = 0; /* terminate permissions */
+ perm = ftp_pl_get_permission(finfo->b_data + parser->item_offset);
+ if(perm & FTP_LP_MALFORMATED_PERM) {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM;
+ parser->file_data->info.perm = perm;
+ parser->offsets.perm = parser->item_offset;
+
+ parser->item_length = 0;
+ parser->state.UNIX.main = PL_UNIX_HLINKS;
+ parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE;
+ }
+ break;
+ case PL_UNIX_HLINKS:
+ switch(parser->state.UNIX.sub.hlinks) {
+ case PL_UNIX_HLINKS_PRESPACE:
+ if(c != ' ') {
+ if(c >= '0' && c <= '9') {
+ parser->item_offset = finfo->b_used - 1;
+ parser->item_length = 1;
+ parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER;
+ }
+ else {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ }
+ break;
+ case PL_UNIX_HLINKS_NUMBER:
+ parser->item_length ++;
+ if(c == ' ') {
+ char *p;
+ long int hlinks;
+ finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
+ hlinks = strtol(finfo->b_data + parser->item_offset, &p, 10);
+ if(p[0] == '\0' && hlinks != LONG_MAX && hlinks != LONG_MIN) {
+ parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT;
+ parser->file_data->info.hardlinks = hlinks;
+ }
+ parser->item_length = 0;
+ parser->item_offset = 0;
+ parser->state.UNIX.main = PL_UNIX_USER;
+ parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE;
+ }
+ else if(c < '0' || c > '9') {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ break;
+ }
+ break;
+ case PL_UNIX_USER:
+ switch(parser->state.UNIX.sub.user) {
+ case PL_UNIX_USER_PRESPACE:
+ if(c != ' ') {
+ parser->item_offset = finfo->b_used - 1;
+ parser->item_length = 1;
+ parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING;
+ }
+ break;
+ case PL_UNIX_USER_PARSING:
+ parser->item_length++;
+ if(c == ' ') {
+ finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
+ parser->offsets.user = parser->item_offset;
+ parser->state.UNIX.main = PL_UNIX_GROUP;
+ parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE;
+ parser->item_offset = 0;
+ parser->item_length = 0;
+ }
+ break;
+ }
+ break;
+ case PL_UNIX_GROUP:
+ switch(parser->state.UNIX.sub.group) {
+ case PL_UNIX_GROUP_PRESPACE:
+ if(c != ' ') {
+ parser->item_offset = finfo->b_used - 1;
+ parser->item_length = 1;
+ parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME;
+ }
+ break;
+ case PL_UNIX_GROUP_NAME:
+ parser->item_length++;
+ if(c == ' ') {
+ finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
+ parser->offsets.group = parser->item_offset;
+ parser->state.UNIX.main = PL_UNIX_SIZE;
+ parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE;
+ parser->item_offset = 0;
+ parser->item_length = 0;
+ }
+ break;
+ }
+ break;
+ case PL_UNIX_SIZE:
+ switch(parser->state.UNIX.sub.size) {
+ case PL_UNIX_SIZE_PRESPACE:
+ if(c != ' ') {
+ if(c >= '0' && c <= '9') {
+ parser->item_offset = finfo->b_used - 1;
+ parser->item_length = 1;
+ parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER;
+ }
+ else {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ }
+ break;
+ case PL_UNIX_SIZE_NUMBER:
+ parser->item_length++;
+ if(c == ' ') {
+ char *p;
+ curl_off_t fsize;
+ finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
+ if(!curlx_strtoofft(finfo->b_data + parser->item_offset,
+ &p, 10, &fsize)) {
+ if(p[0] == '\0' && fsize != CURL_OFF_T_MAX &&
+ fsize != CURL_OFF_T_MIN) {
+ parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
+ parser->file_data->info.size = fsize;
+ }
+ parser->item_length = 0;
+ parser->item_offset = 0;
+ parser->state.UNIX.main = PL_UNIX_TIME;
+ parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1;
+ }
+ }
+ else if(!ISDIGIT(c)) {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ break;
+ }
+ break;
+ case PL_UNIX_TIME:
+ switch(parser->state.UNIX.sub.time) {
+ case PL_UNIX_TIME_PREPART1:
+ if(c != ' ') {
+ if(ISALNUM(c)) {
+ parser->item_offset = finfo->b_used -1;
+ parser->item_length = 1;
+ parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1;
+ }
+ else {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ }
+ break;
+ case PL_UNIX_TIME_PART1:
+ parser->item_length++;
+ if(c == ' ') {
+ parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2;
+ }
+ else if(!ISALNUM(c) && c != '.') {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ break;
+ case PL_UNIX_TIME_PREPART2:
+ parser->item_length++;
+ if(c != ' ') {
+ if(ISALNUM(c)) {
+ parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2;
+ }
+ else {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ }
+ break;
+ case PL_UNIX_TIME_PART2:
+ parser->item_length++;
+ if(c == ' ') {
+ parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3;
+ }
+ else if(!ISALNUM(c) && c != '.') {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ break;
+ case PL_UNIX_TIME_PREPART3:
+ parser->item_length++;
+ if(c != ' ') {
+ if(ISALNUM(c)) {
+ parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3;
+ }
+ else {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ }
+ break;
+ case PL_UNIX_TIME_PART3:
+ parser->item_length++;
+ if(c == ' ') {
+ finfo->b_data[parser->item_offset + parser->item_length -1] = 0;
+ parser->offsets.time = parser->item_offset;
+ /*
+ if(ftp_pl_gettime(parser, finfo->b_data + parser->item_offset)) {
+ parser->file_data->flags |= CURLFINFOFLAG_KNOWN_TIME;
+ }
+ */
+ if(finfo->filetype == CURLFILETYPE_SYMLINK) {
+ parser->state.UNIX.main = PL_UNIX_SYMLINK;
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE;
+ }
+ else {
+ parser->state.UNIX.main = PL_UNIX_FILENAME;
+ parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE;
+ }
+ }
+ else if(!ISALNUM(c) && c != '.' && c != ':') {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ break;
+ }
+ break;
+ case PL_UNIX_FILENAME:
+ switch(parser->state.UNIX.sub.filename) {
+ case PL_UNIX_FILENAME_PRESPACE:
+ if(c != ' ') {
+ parser->item_offset = finfo->b_used - 1;
+ parser->item_length = 1;
+ parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME;
+ }
+ break;
+ case PL_UNIX_FILENAME_NAME:
+ parser->item_length++;
+ if(c == '\r') {
+ parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL;
+ }
+ else if(c == '\n') {
+ finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
+ parser->offsets.filename = parser->item_offset;
+ parser->state.UNIX.main = PL_UNIX_FILETYPE;
+ result = ftp_pl_insert_finfo(data, infop);
+ if(result) {
+ parser->error = result;
+ goto fail;
+ }
+ }
+ break;
+ case PL_UNIX_FILENAME_WINDOWSEOL:
+ if(c == '\n') {
+ finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
+ parser->offsets.filename = parser->item_offset;
+ parser->state.UNIX.main = PL_UNIX_FILETYPE;
+ result = ftp_pl_insert_finfo(data, infop);
+ if(result) {
+ parser->error = result;
+ goto fail;
+ }
+ }
+ else {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ break;
+ }
+ break;
+ case PL_UNIX_SYMLINK:
+ switch(parser->state.UNIX.sub.symlink) {
+ case PL_UNIX_SYMLINK_PRESPACE:
+ if(c != ' ') {
+ parser->item_offset = finfo->b_used - 1;
+ parser->item_length = 1;
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+ }
+ break;
+ case PL_UNIX_SYMLINK_NAME:
+ parser->item_length++;
+ if(c == ' ') {
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1;
+ }
+ else if(c == '\r' || c == '\n') {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ break;
+ case PL_UNIX_SYMLINK_PRETARGET1:
+ parser->item_length++;
+ if(c == '-') {
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2;
+ }
+ else if(c == '\r' || c == '\n') {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ else {
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+ }
+ break;
+ case PL_UNIX_SYMLINK_PRETARGET2:
+ parser->item_length++;
+ if(c == '>') {
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3;
+ }
+ else if(c == '\r' || c == '\n') {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ else {
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+ }
+ break;
+ case PL_UNIX_SYMLINK_PRETARGET3:
+ parser->item_length++;
+ if(c == ' ') {
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4;
+ /* now place where is symlink following */
+ finfo->b_data[parser->item_offset + parser->item_length - 4] = 0;
+ parser->offsets.filename = parser->item_offset;
+ parser->item_length = 0;
+ parser->item_offset = 0;
+ }
+ else if(c == '\r' || c == '\n') {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ else {
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+ }
+ break;
+ case PL_UNIX_SYMLINK_PRETARGET4:
+ if(c != '\r' && c != '\n') {
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET;
+ parser->item_offset = finfo->b_used - 1;
+ parser->item_length = 1;
+ }
+ else {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ break;
+ case PL_UNIX_SYMLINK_TARGET:
+ parser->item_length++;
+ if(c == '\r') {
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL;
+ }
+ else if(c == '\n') {
+ finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
+ parser->offsets.symlink_target = parser->item_offset;
+ result = ftp_pl_insert_finfo(data, infop);
+ if(result) {
+ parser->error = result;
+ goto fail;
+ }
+ parser->state.UNIX.main = PL_UNIX_FILETYPE;
+ }
+ break;
+ case PL_UNIX_SYMLINK_WINDOWSEOL:
+ if(c == '\n') {
+ finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
+ parser->offsets.symlink_target = parser->item_offset;
+ result = ftp_pl_insert_finfo(data, infop);
+ if(result) {
+ parser->error = result;
+ goto fail;
+ }
+ parser->state.UNIX.main = PL_UNIX_FILETYPE;
+ }
+ else {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case OS_TYPE_WIN_NT:
+ switch(parser->state.NT.main) {
+ case PL_WINNT_DATE:
+ parser->item_length++;
+ if(parser->item_length < 9) {
+ if(!strchr("0123456789-", c)) { /* only simple control */
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ }
+ else if(parser->item_length == 9) {
+ if(c == ' ') {
+ parser->state.NT.main = PL_WINNT_TIME;
+ parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE;
+ }
+ else {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ }
+ else {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ break;
+ case PL_WINNT_TIME:
+ parser->item_length++;
+ switch(parser->state.NT.sub.time) {
+ case PL_WINNT_TIME_PRESPACE:
+ if(!ISBLANK(c)) {
+ parser->state.NT.sub.time = PL_WINNT_TIME_TIME;
+ }
+ break;
+ case PL_WINNT_TIME_TIME:
+ if(c == ' ') {
+ parser->offsets.time = parser->item_offset;
+ finfo->b_data[parser->item_offset + parser->item_length -1] = 0;
+ parser->state.NT.main = PL_WINNT_DIRORSIZE;
+ parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE;
+ parser->item_length = 0;
+ }
+ else if(!strchr("APM0123456789:", c)) {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ break;
+ }
+ break;
+ case PL_WINNT_DIRORSIZE:
+ switch(parser->state.NT.sub.dirorsize) {
+ case PL_WINNT_DIRORSIZE_PRESPACE:
+ if(c != ' ') {
+ parser->item_offset = finfo->b_used - 1;
+ parser->item_length = 1;
+ parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT;
+ }
+ break;
+ case PL_WINNT_DIRORSIZE_CONTENT:
+ parser->item_length ++;
+ if(c == ' ') {
+ finfo->b_data[parser->item_offset + parser->item_length - 1] = 0;
+ if(strcmp("<DIR>", finfo->b_data + parser->item_offset) == 0) {
+ finfo->filetype = CURLFILETYPE_DIRECTORY;
+ finfo->size = 0;
+ }
+ else {
+ char *endptr;
+ if(curlx_strtoofft(finfo->b_data +
+ parser->item_offset,
+ &endptr, 10, &finfo->size)) {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ /* correct file type */
+ parser->file_data->info.filetype = CURLFILETYPE_FILE;
+ }
+
+ parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
+ parser->item_length = 0;
+ parser->state.NT.main = PL_WINNT_FILENAME;
+ parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
+ }
+ break;
+ }
+ break;
+ case PL_WINNT_FILENAME:
+ switch(parser->state.NT.sub.filename) {
+ case PL_WINNT_FILENAME_PRESPACE:
+ if(c != ' ') {
+ parser->item_offset = finfo->b_used -1;
+ parser->item_length = 1;
+ parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT;
+ }
+ break;
+ case PL_WINNT_FILENAME_CONTENT:
+ parser->item_length++;
+ if(c == '\r') {
+ parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL;
+ finfo->b_data[finfo->b_used - 1] = 0;
+ }
+ else if(c == '\n') {
+ parser->offsets.filename = parser->item_offset;
+ finfo->b_data[finfo->b_used - 1] = 0;
+ result = ftp_pl_insert_finfo(data, infop);
+ if(result) {
+ parser->error = result;
+ goto fail;
+ }
+ parser->state.NT.main = PL_WINNT_DATE;
+ parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
+ }
+ break;
+ case PL_WINNT_FILENAME_WINEOL:
+ if(c == '\n') {
+ parser->offsets.filename = parser->item_offset;
+ result = ftp_pl_insert_finfo(data, infop);
+ if(result) {
+ parser->error = result;
+ goto fail;
+ }
+ parser->state.NT.main = PL_WINNT_DATE;
+ parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
+ }
+ else {
+ parser->error = CURLE_FTP_BAD_FILE_LIST;
+ goto fail;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ default:
+ retsize = bufflen + 1;
+ goto fail;
+ }
+
+ i++;
+ }
+ return retsize;
+
+fail:
+
+ /* Clean up any allocated memory. */
+ if(parser->file_data) {
+ Curl_fileinfo_cleanup(parser->file_data);
+ parser->file_data = NULL;
+ }
+
+ return retsize;
+}
+
+#endif /* CURL_DISABLE_FTP */
diff --git a/libs/libcurl/src/ftplistparser.h b/libs/libcurl/src/ftplistparser.h index 0a80543417..0cb4c84a72 100644 --- a/libs/libcurl/src/ftplistparser.h +++ b/libs/libcurl/src/ftplistparser.h @@ -1,43 +1,43 @@ -#ifndef HEADER_CURL_FTPLISTPARSER_H -#define HEADER_CURL_FTPLISTPARSER_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - -/* WRITEFUNCTION callback for parsing LIST responses */ -size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, - void *connptr); - -struct ftp_parselist_data; /* defined inside ftplibparser.c */ - -CURLcode Curl_ftp_parselist_geterror(struct ftp_parselist_data *pl_data); - -struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void); - -void Curl_ftp_parselist_data_free(struct ftp_parselist_data **pl_data); - -#endif /* CURL_DISABLE_FTP */ -#endif /* HEADER_CURL_FTPLISTPARSER_H */ +#ifndef HEADER_CURL_FTPLISTPARSER_H
+#define HEADER_CURL_FTPLISTPARSER_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+
+/* WRITEFUNCTION callback for parsing LIST responses */
+size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
+ void *connptr);
+
+struct ftp_parselist_data; /* defined inside ftplibparser.c */
+
+CURLcode Curl_ftp_parselist_geterror(struct ftp_parselist_data *pl_data);
+
+struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void);
+
+void Curl_ftp_parselist_data_free(struct ftp_parselist_data **pl_data);
+
+#endif /* CURL_DISABLE_FTP */
+#endif /* HEADER_CURL_FTPLISTPARSER_H */
diff --git a/libs/libcurl/src/functypes.h b/libs/libcurl/src/functypes.h index 8891b1d5d6..3741833ec5 100644 --- a/libs/libcurl/src/functypes.h +++ b/libs/libcurl/src/functypes.h @@ -1,115 +1,115 @@ -#ifndef HEADER_CURL_FUNCTYPES_H -#define HEADER_CURL_FUNCTYPES_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -/* defaults: - - ssize_t recv(int, void *, size_t, int); - ssize_t send(int, const void *, size_t, int); - - If other argument or return types are needed: - - 1. For systems that run configure or cmake, the alternatives are provided - here. - 2. For systems with config-*.h files, define them there. -*/ - -#ifdef WIN32 -/* int recv(SOCKET, char *, int, int) */ -#define RECV_TYPE_ARG1 SOCKET -#define RECV_TYPE_ARG2 char * -#define RECV_TYPE_ARG3 int -#define RECV_TYPE_RETV int - -/* int send(SOCKET, const char *, int, int); */ -#define SEND_TYPE_ARG1 SOCKET -#define SEND_TYPE_ARG2 char * -#define SEND_TYPE_ARG3 int -#define SEND_TYPE_RETV int - -#elif defined(__AMIGA__) /* Any AmigaOS flavour */ - -/* long recv(long, char *, long, long); */ -#define RECV_TYPE_ARG1 long -#define RECV_TYPE_ARG2 char * -#define RECV_TYPE_ARG3 long -#define RECV_TYPE_ARG4 long -#define RECV_TYPE_RETV long - -/* int send(int, const char *, int, int); */ -#define SEND_TYPE_ARG1 int -#define SEND_TYPE_ARG2 char * -#define SEND_TYPE_ARG3 int -#define SEND_TYPE_RETV int -#endif - - -#ifndef RECV_TYPE_ARG1 -#define RECV_TYPE_ARG1 int -#endif - -#ifndef RECV_TYPE_ARG2 -#define RECV_TYPE_ARG2 void * -#endif - -#ifndef RECV_TYPE_ARG3 -#define RECV_TYPE_ARG3 size_t -#endif - -#ifndef RECV_TYPE_ARG4 -#define RECV_TYPE_ARG4 int -#endif - -#ifndef RECV_TYPE_RETV -#define RECV_TYPE_RETV ssize_t -#endif - -#ifndef SEND_QUAL_ARG2 -#define SEND_QUAL_ARG2 const -#endif - -#ifndef SEND_TYPE_ARG1 -#define SEND_TYPE_ARG1 int -#endif - -#ifndef SEND_TYPE_ARG2 -#define SEND_TYPE_ARG2 void * -#endif - -#ifndef SEND_TYPE_ARG3 -#define SEND_TYPE_ARG3 size_t -#endif - -#ifndef SEND_TYPE_ARG4 -#define SEND_TYPE_ARG4 int -#endif - -#ifndef SEND_TYPE_RETV -#define SEND_TYPE_RETV ssize_t -#endif - -#endif /* HEADER_CURL_FUNCTYPES_H */ +#ifndef HEADER_CURL_FUNCTYPES_H
+#define HEADER_CURL_FUNCTYPES_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+/* defaults:
+
+ ssize_t recv(int, void *, size_t, int);
+ ssize_t send(int, const void *, size_t, int);
+
+ If other argument or return types are needed:
+
+ 1. For systems that run configure or cmake, the alternatives are provided
+ here.
+ 2. For systems with config-*.h files, define them there.
+*/
+
+#ifdef WIN32
+/* int recv(SOCKET, char *, int, int) */
+#define RECV_TYPE_ARG1 SOCKET
+#define RECV_TYPE_ARG2 char *
+#define RECV_TYPE_ARG3 int
+#define RECV_TYPE_RETV int
+
+/* int send(SOCKET, const char *, int, int); */
+#define SEND_TYPE_ARG1 SOCKET
+#define SEND_TYPE_ARG2 char *
+#define SEND_TYPE_ARG3 int
+#define SEND_TYPE_RETV int
+
+#elif defined(__AMIGA__) /* Any AmigaOS flavour */
+
+/* long recv(long, char *, long, long); */
+#define RECV_TYPE_ARG1 long
+#define RECV_TYPE_ARG2 char *
+#define RECV_TYPE_ARG3 long
+#define RECV_TYPE_ARG4 long
+#define RECV_TYPE_RETV long
+
+/* int send(int, const char *, int, int); */
+#define SEND_TYPE_ARG1 int
+#define SEND_TYPE_ARG2 char *
+#define SEND_TYPE_ARG3 int
+#define SEND_TYPE_RETV int
+#endif
+
+
+#ifndef RECV_TYPE_ARG1
+#define RECV_TYPE_ARG1 int
+#endif
+
+#ifndef RECV_TYPE_ARG2
+#define RECV_TYPE_ARG2 void *
+#endif
+
+#ifndef RECV_TYPE_ARG3
+#define RECV_TYPE_ARG3 size_t
+#endif
+
+#ifndef RECV_TYPE_ARG4
+#define RECV_TYPE_ARG4 int
+#endif
+
+#ifndef RECV_TYPE_RETV
+#define RECV_TYPE_RETV ssize_t
+#endif
+
+#ifndef SEND_QUAL_ARG2
+#define SEND_QUAL_ARG2 const
+#endif
+
+#ifndef SEND_TYPE_ARG1
+#define SEND_TYPE_ARG1 int
+#endif
+
+#ifndef SEND_TYPE_ARG2
+#define SEND_TYPE_ARG2 void *
+#endif
+
+#ifndef SEND_TYPE_ARG3
+#define SEND_TYPE_ARG3 size_t
+#endif
+
+#ifndef SEND_TYPE_ARG4
+#define SEND_TYPE_ARG4 int
+#endif
+
+#ifndef SEND_TYPE_RETV
+#define SEND_TYPE_RETV ssize_t
+#endif
+
+#endif /* HEADER_CURL_FUNCTYPES_H */
diff --git a/libs/libcurl/src/getenv.c b/libs/libcurl/src/getenv.c index 5f00fd13a4..2bb2e2ef91 100644 --- a/libs/libcurl/src/getenv.c +++ b/libs/libcurl/src/getenv.c @@ -1,79 +1,79 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_memory.h" - -#include "memdebug.h" - -static char *GetEnv(const char *variable) -{ -#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) - (void)variable; - return NULL; -#elif defined(WIN32) - /* This uses Windows API instead of C runtime getenv() to get the environment - variable since some changes aren't always visible to the latter. #4774 */ - char *buf = NULL; - char *tmp; - DWORD bufsize; - DWORD rc = 1; - const DWORD max = 32768; /* max env var size from MSCRT source */ - - for(;;) { - tmp = realloc(buf, rc); - if(!tmp) { - free(buf); - return NULL; - } - - buf = tmp; - bufsize = rc; - - /* It's possible for rc to be 0 if the variable was found but empty. - Since getenv doesn't make that distinction we ignore it as well. */ - rc = GetEnvironmentVariableA(variable, buf, bufsize); - if(!rc || rc == bufsize || rc > max) { - free(buf); - return NULL; - } - - /* if rc < bufsize then rc is bytes written not including null */ - if(rc < bufsize) - return buf; - - /* else rc is bytes needed, try again */ - } -#else - char *env = getenv(variable); - return (env && env[0])?strdup(env):NULL; -#endif -} - -char *curl_getenv(const char *v) -{ - return GetEnv(v); -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_memory.h"
+
+#include "memdebug.h"
+
+static char *GetEnv(const char *variable)
+{
+#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP)
+ (void)variable;
+ return NULL;
+#elif defined(WIN32)
+ /* This uses Windows API instead of C runtime getenv() to get the environment
+ variable since some changes aren't always visible to the latter. #4774 */
+ char *buf = NULL;
+ char *tmp;
+ DWORD bufsize;
+ DWORD rc = 1;
+ const DWORD max = 32768; /* max env var size from MSCRT source */
+
+ for(;;) {
+ tmp = realloc(buf, rc);
+ if(!tmp) {
+ free(buf);
+ return NULL;
+ }
+
+ buf = tmp;
+ bufsize = rc;
+
+ /* It's possible for rc to be 0 if the variable was found but empty.
+ Since getenv doesn't make that distinction we ignore it as well. */
+ rc = GetEnvironmentVariableA(variable, buf, bufsize);
+ if(!rc || rc == bufsize || rc > max) {
+ free(buf);
+ return NULL;
+ }
+
+ /* if rc < bufsize then rc is bytes written not including null */
+ if(rc < bufsize)
+ return buf;
+
+ /* else rc is bytes needed, try again */
+ }
+#else
+ char *env = getenv(variable);
+ return (env && env[0])?strdup(env):NULL;
+#endif
+}
+
+char *curl_getenv(const char *v)
+{
+ return GetEnv(v);
+}
diff --git a/libs/libcurl/src/getinfo.c b/libs/libcurl/src/getinfo.c index c3556b3102..49ed7b50be 100644 --- a/libs/libcurl/src/getinfo.c +++ b/libs/libcurl/src/getinfo.c @@ -1,624 +1,618 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "getinfo.h" - -#include "vtls/vtls.h" -#include "connect.h" /* Curl_getconnectinfo() */ -#include "progress.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* - * Initialize statistical and informational data. - * - * This function is called in curl_easy_reset, curl_easy_duphandle and at the - * beginning of a perform session. It must reset the session-info variables, - * in particular all variables in struct PureInfo. - */ -CURLcode Curl_initinfo(struct Curl_easy *data) -{ - struct Progress *pro = &data->progress; - struct PureInfo *info = &data->info; - - pro->t_nslookup = 0; - pro->t_connect = 0; - pro->t_appconnect = 0; - pro->t_pretransfer = 0; - pro->t_starttransfer = 0; - pro->timespent = 0; - pro->t_redirect = 0; - pro->is_t_startransfer_set = false; - - info->httpcode = 0; - info->httpproxycode = 0; - info->httpversion = 0; - info->filetime = -1; /* -1 is an illegal time and thus means unknown */ - info->timecond = FALSE; - - info->header_size = 0; - info->request_size = 0; - info->proxyauthavail = 0; - info->httpauthavail = 0; - info->numconnects = 0; - - free(info->contenttype); - info->contenttype = NULL; - - free(info->wouldredirect); - info->wouldredirect = NULL; - - info->conn_primary_ip[0] = '\0'; - info->conn_local_ip[0] = '\0'; - info->conn_primary_port = 0; - info->conn_local_port = 0; - info->retry_after = 0; - - info->conn_scheme = 0; - info->conn_protocol = 0; - -#ifdef USE_SSL - Curl_ssl_free_certinfo(data); -#endif - return CURLE_OK; -} - -static CURLcode getinfo_char(struct Curl_easy *data, CURLINFO info, - const char **param_charp) -{ - switch(info) { - case CURLINFO_EFFECTIVE_URL: - *param_charp = data->state.url?data->state.url:(char *)""; - break; - case CURLINFO_EFFECTIVE_METHOD: { - const char *m = data->set.str[STRING_CUSTOMREQUEST]; - if(!m) { - if(data->set.opt_no_body) - m = "HEAD"; -#ifndef CURL_DISABLE_HTTP - else { - switch(data->state.httpreq) { - case HTTPREQ_POST: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - m = "POST"; - break; - case HTTPREQ_PUT: - m = "PUT"; - break; - default: /* this should never happen */ - case HTTPREQ_GET: - m = "GET"; - break; - case HTTPREQ_HEAD: - m = "HEAD"; - break; - } - } -#endif - } - *param_charp = m; - } - break; - case CURLINFO_CONTENT_TYPE: - *param_charp = data->info.contenttype; - break; - case CURLINFO_PRIVATE: - *param_charp = (char *) data->set.private_data; - break; - case CURLINFO_FTP_ENTRY_PATH: - /* Return the entrypath string from the most recent connection. - This pointer was copied from the connectdata structure by FTP. - The actual string may be free()ed by subsequent libcurl calls so - it must be copied to a safer area before the next libcurl call. - Callers must never free it themselves. */ - *param_charp = data->state.most_recent_ftp_entrypath; - break; - case CURLINFO_REDIRECT_URL: - /* Return the URL this request would have been redirected to if that - option had been enabled! */ - *param_charp = data->info.wouldredirect; - break; - case CURLINFO_REFERER: - /* Return the referrer header for this request, or NULL if unset */ - *param_charp = data->state.referer; - break; - case CURLINFO_PRIMARY_IP: - /* Return the ip address of the most recent (primary) connection */ - *param_charp = data->info.conn_primary_ip; - break; - case CURLINFO_LOCAL_IP: - /* Return the source/local ip address of the most recent (primary) - connection */ - *param_charp = data->info.conn_local_ip; - break; - case CURLINFO_RTSP_SESSION_ID: - *param_charp = data->set.str[STRING_RTSP_SESSION_ID]; - break; - case CURLINFO_SCHEME: - *param_charp = data->info.conn_scheme; - break; - case CURLINFO_CAPATH: -#ifdef CURL_CA_PATH - *param_charp = CURL_CA_PATH; -#else - *param_charp = NULL; -#endif - break; - case CURLINFO_CAINFO: -#ifdef CURL_CA_BUNDLE - *param_charp = CURL_CA_BUNDLE; -#else - *param_charp = NULL; -#endif - break; - - default: - return CURLE_UNKNOWN_OPTION; - } - - return CURLE_OK; -} - -static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, - long *param_longp) -{ - curl_socket_t sockfd; - - union { - unsigned long *to_ulong; - long *to_long; - } lptr; - -#ifdef DEBUGBUILD - char *timestr = getenv("CURL_TIME"); - if(timestr) { - unsigned long val = strtol(timestr, NULL, 10); - switch(info) { - case CURLINFO_LOCAL_PORT: - *param_longp = (long)val; - return CURLE_OK; - default: - break; - } - } - /* use another variable for this to allow different values */ - timestr = getenv("CURL_DEBUG_SIZE"); - if(timestr) { - unsigned long val = strtol(timestr, NULL, 10); - switch(info) { - case CURLINFO_HEADER_SIZE: - case CURLINFO_REQUEST_SIZE: - *param_longp = (long)val; - return CURLE_OK; - default: - break; - } - } -#endif - - switch(info) { - case CURLINFO_RESPONSE_CODE: - *param_longp = data->info.httpcode; - break; - case CURLINFO_HTTP_CONNECTCODE: - *param_longp = data->info.httpproxycode; - break; - case CURLINFO_FILETIME: - if(data->info.filetime > LONG_MAX) - *param_longp = LONG_MAX; - else if(data->info.filetime < LONG_MIN) - *param_longp = LONG_MIN; - else - *param_longp = (long)data->info.filetime; - break; - case CURLINFO_HEADER_SIZE: - *param_longp = (long)data->info.header_size; - break; - case CURLINFO_REQUEST_SIZE: - *param_longp = (long)data->info.request_size; - break; - case CURLINFO_SSL_VERIFYRESULT: - *param_longp = data->set.ssl.certverifyresult; - break; -#ifndef CURL_DISABLE_PROXY - case CURLINFO_PROXY_SSL_VERIFYRESULT: - *param_longp = data->set.proxy_ssl.certverifyresult; - break; -#endif - case CURLINFO_REDIRECT_COUNT: - *param_longp = data->state.followlocation; - break; - case CURLINFO_HTTPAUTH_AVAIL: - lptr.to_long = param_longp; - *lptr.to_ulong = data->info.httpauthavail; - break; - case CURLINFO_PROXYAUTH_AVAIL: - lptr.to_long = param_longp; - *lptr.to_ulong = data->info.proxyauthavail; - break; - case CURLINFO_OS_ERRNO: - *param_longp = data->state.os_errno; - break; - case CURLINFO_NUM_CONNECTS: - *param_longp = data->info.numconnects; - break; - case CURLINFO_LASTSOCKET: - sockfd = Curl_getconnectinfo(data, NULL); - - /* note: this is not a good conversion for systems with 64 bit sockets and - 32 bit longs */ - if(sockfd != CURL_SOCKET_BAD) - *param_longp = (long)sockfd; - else - /* this interface is documented to return -1 in case of badness, which - may not be the same as the CURL_SOCKET_BAD value */ - *param_longp = -1; - break; - case CURLINFO_PRIMARY_PORT: - /* Return the (remote) port of the most recent (primary) connection */ - *param_longp = data->info.conn_primary_port; - break; - case CURLINFO_LOCAL_PORT: - /* Return the local port of the most recent (primary) connection */ - *param_longp = data->info.conn_local_port; - break; - case CURLINFO_PROXY_ERROR: - *param_longp = (long)data->info.pxcode; - break; - case CURLINFO_CONDITION_UNMET: - if(data->info.httpcode == 304) - *param_longp = 1L; - else - /* return if the condition prevented the document to get transferred */ - *param_longp = data->info.timecond ? 1L : 0L; - break; -#ifndef CURL_DISABLE_RTSP - case CURLINFO_RTSP_CLIENT_CSEQ: - *param_longp = data->state.rtsp_next_client_CSeq; - break; - case CURLINFO_RTSP_SERVER_CSEQ: - *param_longp = data->state.rtsp_next_server_CSeq; - break; - case CURLINFO_RTSP_CSEQ_RECV: - *param_longp = data->state.rtsp_CSeq_recv; - break; -#endif - case CURLINFO_HTTP_VERSION: - switch(data->info.httpversion) { - case 10: - *param_longp = CURL_HTTP_VERSION_1_0; - break; - case 11: - *param_longp = CURL_HTTP_VERSION_1_1; - break; - case 20: - *param_longp = CURL_HTTP_VERSION_2_0; - break; - case 30: - *param_longp = CURL_HTTP_VERSION_3; - break; - default: - *param_longp = CURL_HTTP_VERSION_NONE; - break; - } - break; - case CURLINFO_PROTOCOL: - *param_longp = data->info.conn_protocol; - break; - default: - return CURLE_UNKNOWN_OPTION; - } - - return CURLE_OK; -} - -#define DOUBLE_SECS(x) (double)(x)/1000000 - -static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info, - curl_off_t *param_offt) -{ -#ifdef DEBUGBUILD - char *timestr = getenv("CURL_TIME"); - if(timestr) { - unsigned long val = strtol(timestr, NULL, 10); - switch(info) { - case CURLINFO_TOTAL_TIME_T: - case CURLINFO_NAMELOOKUP_TIME_T: - case CURLINFO_CONNECT_TIME_T: - case CURLINFO_APPCONNECT_TIME_T: - case CURLINFO_PRETRANSFER_TIME_T: - case CURLINFO_STARTTRANSFER_TIME_T: - case CURLINFO_REDIRECT_TIME_T: - case CURLINFO_SPEED_DOWNLOAD_T: - case CURLINFO_SPEED_UPLOAD_T: - *param_offt = (curl_off_t)val; - return CURLE_OK; - default: - break; - } - } -#endif - switch(info) { - case CURLINFO_FILETIME_T: - *param_offt = (curl_off_t)data->info.filetime; - break; - case CURLINFO_SIZE_UPLOAD_T: - *param_offt = data->progress.uploaded; - break; - case CURLINFO_SIZE_DOWNLOAD_T: - *param_offt = data->progress.downloaded; - break; - case CURLINFO_SPEED_DOWNLOAD_T: - *param_offt = data->progress.dlspeed; - break; - case CURLINFO_SPEED_UPLOAD_T: - *param_offt = data->progress.ulspeed; - break; - case CURLINFO_CONTENT_LENGTH_DOWNLOAD_T: - *param_offt = (data->progress.flags & PGRS_DL_SIZE_KNOWN)? - data->progress.size_dl:-1; - break; - case CURLINFO_CONTENT_LENGTH_UPLOAD_T: - *param_offt = (data->progress.flags & PGRS_UL_SIZE_KNOWN)? - data->progress.size_ul:-1; - break; - case CURLINFO_TOTAL_TIME_T: - *param_offt = data->progress.timespent; - break; - case CURLINFO_NAMELOOKUP_TIME_T: - *param_offt = data->progress.t_nslookup; - break; - case CURLINFO_CONNECT_TIME_T: - *param_offt = data->progress.t_connect; - break; - case CURLINFO_APPCONNECT_TIME_T: - *param_offt = data->progress.t_appconnect; - break; - case CURLINFO_PRETRANSFER_TIME_T: - *param_offt = data->progress.t_pretransfer; - break; - case CURLINFO_STARTTRANSFER_TIME_T: - *param_offt = data->progress.t_starttransfer; - break; - case CURLINFO_REDIRECT_TIME_T: - *param_offt = data->progress.t_redirect; - break; - case CURLINFO_RETRY_AFTER: - *param_offt = data->info.retry_after; - break; - default: - return CURLE_UNKNOWN_OPTION; - } - - return CURLE_OK; -} - -static CURLcode getinfo_double(struct Curl_easy *data, CURLINFO info, - double *param_doublep) -{ -#ifdef DEBUGBUILD - char *timestr = getenv("CURL_TIME"); - if(timestr) { - unsigned long val = strtol(timestr, NULL, 10); - switch(info) { - case CURLINFO_TOTAL_TIME: - case CURLINFO_NAMELOOKUP_TIME: - case CURLINFO_CONNECT_TIME: - case CURLINFO_APPCONNECT_TIME: - case CURLINFO_PRETRANSFER_TIME: - case CURLINFO_STARTTRANSFER_TIME: - case CURLINFO_REDIRECT_TIME: - case CURLINFO_SPEED_DOWNLOAD: - case CURLINFO_SPEED_UPLOAD: - *param_doublep = (double)val; - return CURLE_OK; - default: - break; - } - } -#endif - switch(info) { - case CURLINFO_TOTAL_TIME: - *param_doublep = DOUBLE_SECS(data->progress.timespent); - break; - case CURLINFO_NAMELOOKUP_TIME: - *param_doublep = DOUBLE_SECS(data->progress.t_nslookup); - break; - case CURLINFO_CONNECT_TIME: - *param_doublep = DOUBLE_SECS(data->progress.t_connect); - break; - case CURLINFO_APPCONNECT_TIME: - *param_doublep = DOUBLE_SECS(data->progress.t_appconnect); - break; - case CURLINFO_PRETRANSFER_TIME: - *param_doublep = DOUBLE_SECS(data->progress.t_pretransfer); - break; - case CURLINFO_STARTTRANSFER_TIME: - *param_doublep = DOUBLE_SECS(data->progress.t_starttransfer); - break; - case CURLINFO_SIZE_UPLOAD: - *param_doublep = (double)data->progress.uploaded; - break; - case CURLINFO_SIZE_DOWNLOAD: - *param_doublep = (double)data->progress.downloaded; - break; - case CURLINFO_SPEED_DOWNLOAD: - *param_doublep = (double)data->progress.dlspeed; - break; - case CURLINFO_SPEED_UPLOAD: - *param_doublep = (double)data->progress.ulspeed; - break; - case CURLINFO_CONTENT_LENGTH_DOWNLOAD: - *param_doublep = (data->progress.flags & PGRS_DL_SIZE_KNOWN)? - (double)data->progress.size_dl:-1; - break; - case CURLINFO_CONTENT_LENGTH_UPLOAD: - *param_doublep = (data->progress.flags & PGRS_UL_SIZE_KNOWN)? - (double)data->progress.size_ul:-1; - break; - case CURLINFO_REDIRECT_TIME: - *param_doublep = DOUBLE_SECS(data->progress.t_redirect); - break; - - default: - return CURLE_UNKNOWN_OPTION; - } - - return CURLE_OK; -} - -static CURLcode getinfo_slist(struct Curl_easy *data, CURLINFO info, - struct curl_slist **param_slistp) -{ - union { - struct curl_certinfo *to_certinfo; - struct curl_slist *to_slist; - } ptr; - - switch(info) { - case CURLINFO_SSL_ENGINES: - *param_slistp = Curl_ssl_engines_list(data); - break; - case CURLINFO_COOKIELIST: - *param_slistp = Curl_cookie_list(data); - break; - case CURLINFO_CERTINFO: - /* Return the a pointer to the certinfo struct. Not really an slist - pointer but we can pretend it is here */ - ptr.to_certinfo = &data->info.certs; - *param_slistp = ptr.to_slist; - break; - case CURLINFO_TLS_SESSION: - case CURLINFO_TLS_SSL_PTR: - { - struct curl_tlssessioninfo **tsip = (struct curl_tlssessioninfo **) - param_slistp; - struct curl_tlssessioninfo *tsi = &data->tsi; -#ifdef USE_SSL - struct connectdata *conn = data->conn; -#endif - - *tsip = tsi; - tsi->backend = Curl_ssl_backend(); - tsi->internals = NULL; - -#ifdef USE_SSL - if(conn && tsi->backend != CURLSSLBACKEND_NONE) { - unsigned int i; - for(i = 0; i < (sizeof(conn->ssl) / sizeof(conn->ssl[0])); ++i) { - if(conn->ssl[i].use) { - tsi->internals = Curl_ssl->get_internals(&conn->ssl[i], info); - break; - } - } - } -#endif - } - break; - default: - return CURLE_UNKNOWN_OPTION; - } - - return CURLE_OK; -} - -static CURLcode getinfo_socket(struct Curl_easy *data, CURLINFO info, - curl_socket_t *param_socketp) -{ - switch(info) { - case CURLINFO_ACTIVESOCKET: - *param_socketp = Curl_getconnectinfo(data, NULL); - break; - default: - return CURLE_UNKNOWN_OPTION; - } - - return CURLE_OK; -} - -CURLcode Curl_getinfo(struct Curl_easy *data, CURLINFO info, ...) -{ - va_list arg; - long *param_longp = NULL; - double *param_doublep = NULL; - curl_off_t *param_offt = NULL; - const char **param_charp = NULL; - struct curl_slist **param_slistp = NULL; - curl_socket_t *param_socketp = NULL; - int type; - CURLcode result = CURLE_UNKNOWN_OPTION; - - if(!data) - return CURLE_BAD_FUNCTION_ARGUMENT; - - va_start(arg, info); - - type = CURLINFO_TYPEMASK & (int)info; - switch(type) { - case CURLINFO_STRING: - param_charp = va_arg(arg, const char **); - if(param_charp) - result = getinfo_char(data, info, param_charp); - break; - case CURLINFO_LONG: - param_longp = va_arg(arg, long *); - if(param_longp) - result = getinfo_long(data, info, param_longp); - break; - case CURLINFO_DOUBLE: - param_doublep = va_arg(arg, double *); - if(param_doublep) - result = getinfo_double(data, info, param_doublep); - break; - case CURLINFO_OFF_T: - param_offt = va_arg(arg, curl_off_t *); - if(param_offt) - result = getinfo_offt(data, info, param_offt); - break; - case CURLINFO_SLIST: - param_slistp = va_arg(arg, struct curl_slist **); - if(param_slistp) - result = getinfo_slist(data, info, param_slistp); - break; - case CURLINFO_SOCKET: - param_socketp = va_arg(arg, curl_socket_t *); - if(param_socketp) - result = getinfo_socket(data, info, param_socketp); - break; - default: - break; - } - - va_end(arg); - - return result; -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "getinfo.h"
+
+#include "vtls/vtls.h"
+#include "connect.h" /* Curl_getconnectinfo() */
+#include "progress.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * Initialize statistical and informational data.
+ *
+ * This function is called in curl_easy_reset, curl_easy_duphandle and at the
+ * beginning of a perform session. It must reset the session-info variables,
+ * in particular all variables in struct PureInfo.
+ */
+CURLcode Curl_initinfo(struct Curl_easy *data)
+{
+ struct Progress *pro = &data->progress;
+ struct PureInfo *info = &data->info;
+
+ pro->t_nslookup = 0;
+ pro->t_connect = 0;
+ pro->t_appconnect = 0;
+ pro->t_pretransfer = 0;
+ pro->t_starttransfer = 0;
+ pro->timespent = 0;
+ pro->t_redirect = 0;
+ pro->is_t_startransfer_set = false;
+
+ info->httpcode = 0;
+ info->httpproxycode = 0;
+ info->httpversion = 0;
+ info->filetime = -1; /* -1 is an illegal time and thus means unknown */
+ info->timecond = FALSE;
+
+ info->header_size = 0;
+ info->request_size = 0;
+ info->proxyauthavail = 0;
+ info->httpauthavail = 0;
+ info->numconnects = 0;
+
+ free(info->contenttype);
+ info->contenttype = NULL;
+
+ free(info->wouldredirect);
+ info->wouldredirect = NULL;
+
+ info->conn_primary_ip[0] = '\0';
+ info->conn_local_ip[0] = '\0';
+ info->conn_primary_port = 0;
+ info->conn_local_port = 0;
+ info->retry_after = 0;
+
+ info->conn_scheme = 0;
+ info->conn_protocol = 0;
+
+#ifdef USE_SSL
+ Curl_ssl_free_certinfo(data);
+#endif
+ return CURLE_OK;
+}
+
+static CURLcode getinfo_char(struct Curl_easy *data, CURLINFO info,
+ const char **param_charp)
+{
+ switch(info) {
+ case CURLINFO_EFFECTIVE_URL:
+ *param_charp = data->state.url?data->state.url:(char *)"";
+ break;
+ case CURLINFO_EFFECTIVE_METHOD: {
+ const char *m = data->set.str[STRING_CUSTOMREQUEST];
+ if(!m) {
+ if(data->set.opt_no_body)
+ m = "HEAD";
+#ifndef CURL_DISABLE_HTTP
+ else {
+ switch(data->state.httpreq) {
+ case HTTPREQ_POST:
+ case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
+ m = "POST";
+ break;
+ case HTTPREQ_PUT:
+ m = "PUT";
+ break;
+ default: /* this should never happen */
+ case HTTPREQ_GET:
+ m = "GET";
+ break;
+ case HTTPREQ_HEAD:
+ m = "HEAD";
+ break;
+ }
+ }
+#endif
+ }
+ *param_charp = m;
+ }
+ break;
+ case CURLINFO_CONTENT_TYPE:
+ *param_charp = data->info.contenttype;
+ break;
+ case CURLINFO_PRIVATE:
+ *param_charp = (char *) data->set.private_data;
+ break;
+ case CURLINFO_FTP_ENTRY_PATH:
+ /* Return the entrypath string from the most recent connection.
+ This pointer was copied from the connectdata structure by FTP.
+ The actual string may be free()ed by subsequent libcurl calls so
+ it must be copied to a safer area before the next libcurl call.
+ Callers must never free it themselves. */
+ *param_charp = data->state.most_recent_ftp_entrypath;
+ break;
+ case CURLINFO_REDIRECT_URL:
+ /* Return the URL this request would have been redirected to if that
+ option had been enabled! */
+ *param_charp = data->info.wouldredirect;
+ break;
+ case CURLINFO_REFERER:
+ /* Return the referrer header for this request, or NULL if unset */
+ *param_charp = data->state.referer;
+ break;
+ case CURLINFO_PRIMARY_IP:
+ /* Return the ip address of the most recent (primary) connection */
+ *param_charp = data->info.conn_primary_ip;
+ break;
+ case CURLINFO_LOCAL_IP:
+ /* Return the source/local ip address of the most recent (primary)
+ connection */
+ *param_charp = data->info.conn_local_ip;
+ break;
+ case CURLINFO_RTSP_SESSION_ID:
+ *param_charp = data->set.str[STRING_RTSP_SESSION_ID];
+ break;
+ case CURLINFO_SCHEME:
+ *param_charp = data->info.conn_scheme;
+ break;
+ case CURLINFO_CAPATH:
+#ifdef CURL_CA_PATH
+ *param_charp = CURL_CA_PATH;
+#else
+ *param_charp = NULL;
+#endif
+ break;
+ case CURLINFO_CAINFO:
+#ifdef CURL_CA_BUNDLE
+ *param_charp = CURL_CA_BUNDLE;
+#else
+ *param_charp = NULL;
+#endif
+ break;
+
+ default:
+ return CURLE_UNKNOWN_OPTION;
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
+ long *param_longp)
+{
+ curl_socket_t sockfd;
+
+ union {
+ unsigned long *to_ulong;
+ long *to_long;
+ } lptr;
+
+#ifdef DEBUGBUILD
+ char *timestr = getenv("CURL_TIME");
+ if(timestr) {
+ unsigned long val = strtol(timestr, NULL, 10);
+ switch(info) {
+ case CURLINFO_LOCAL_PORT:
+ *param_longp = (long)val;
+ return CURLE_OK;
+ default:
+ break;
+ }
+ }
+ /* use another variable for this to allow different values */
+ timestr = getenv("CURL_DEBUG_SIZE");
+ if(timestr) {
+ unsigned long val = strtol(timestr, NULL, 10);
+ switch(info) {
+ case CURLINFO_HEADER_SIZE:
+ case CURLINFO_REQUEST_SIZE:
+ *param_longp = (long)val;
+ return CURLE_OK;
+ default:
+ break;
+ }
+ }
+#endif
+
+ switch(info) {
+ case CURLINFO_RESPONSE_CODE:
+ *param_longp = data->info.httpcode;
+ break;
+ case CURLINFO_HTTP_CONNECTCODE:
+ *param_longp = data->info.httpproxycode;
+ break;
+ case CURLINFO_FILETIME:
+ if(data->info.filetime > LONG_MAX)
+ *param_longp = LONG_MAX;
+ else if(data->info.filetime < LONG_MIN)
+ *param_longp = LONG_MIN;
+ else
+ *param_longp = (long)data->info.filetime;
+ break;
+ case CURLINFO_HEADER_SIZE:
+ *param_longp = (long)data->info.header_size;
+ break;
+ case CURLINFO_REQUEST_SIZE:
+ *param_longp = (long)data->info.request_size;
+ break;
+ case CURLINFO_SSL_VERIFYRESULT:
+ *param_longp = data->set.ssl.certverifyresult;
+ break;
+#ifndef CURL_DISABLE_PROXY
+ case CURLINFO_PROXY_SSL_VERIFYRESULT:
+ *param_longp = data->set.proxy_ssl.certverifyresult;
+ break;
+#endif
+ case CURLINFO_REDIRECT_COUNT:
+ *param_longp = data->state.followlocation;
+ break;
+ case CURLINFO_HTTPAUTH_AVAIL:
+ lptr.to_long = param_longp;
+ *lptr.to_ulong = data->info.httpauthavail;
+ break;
+ case CURLINFO_PROXYAUTH_AVAIL:
+ lptr.to_long = param_longp;
+ *lptr.to_ulong = data->info.proxyauthavail;
+ break;
+ case CURLINFO_OS_ERRNO:
+ *param_longp = data->state.os_errno;
+ break;
+ case CURLINFO_NUM_CONNECTS:
+ *param_longp = data->info.numconnects;
+ break;
+ case CURLINFO_LASTSOCKET:
+ sockfd = Curl_getconnectinfo(data, NULL);
+
+ /* note: this is not a good conversion for systems with 64 bit sockets and
+ 32 bit longs */
+ if(sockfd != CURL_SOCKET_BAD)
+ *param_longp = (long)sockfd;
+ else
+ /* this interface is documented to return -1 in case of badness, which
+ may not be the same as the CURL_SOCKET_BAD value */
+ *param_longp = -1;
+ break;
+ case CURLINFO_PRIMARY_PORT:
+ /* Return the (remote) port of the most recent (primary) connection */
+ *param_longp = data->info.conn_primary_port;
+ break;
+ case CURLINFO_LOCAL_PORT:
+ /* Return the local port of the most recent (primary) connection */
+ *param_longp = data->info.conn_local_port;
+ break;
+ case CURLINFO_PROXY_ERROR:
+ *param_longp = (long)data->info.pxcode;
+ break;
+ case CURLINFO_CONDITION_UNMET:
+ if(data->info.httpcode == 304)
+ *param_longp = 1L;
+ else
+ /* return if the condition prevented the document to get transferred */
+ *param_longp = data->info.timecond ? 1L : 0L;
+ break;
+#ifndef CURL_DISABLE_RTSP
+ case CURLINFO_RTSP_CLIENT_CSEQ:
+ *param_longp = data->state.rtsp_next_client_CSeq;
+ break;
+ case CURLINFO_RTSP_SERVER_CSEQ:
+ *param_longp = data->state.rtsp_next_server_CSeq;
+ break;
+ case CURLINFO_RTSP_CSEQ_RECV:
+ *param_longp = data->state.rtsp_CSeq_recv;
+ break;
+#endif
+ case CURLINFO_HTTP_VERSION:
+ switch(data->info.httpversion) {
+ case 10:
+ *param_longp = CURL_HTTP_VERSION_1_0;
+ break;
+ case 11:
+ *param_longp = CURL_HTTP_VERSION_1_1;
+ break;
+ case 20:
+ *param_longp = CURL_HTTP_VERSION_2_0;
+ break;
+ case 30:
+ *param_longp = CURL_HTTP_VERSION_3;
+ break;
+ default:
+ *param_longp = CURL_HTTP_VERSION_NONE;
+ break;
+ }
+ break;
+ case CURLINFO_PROTOCOL:
+ *param_longp = data->info.conn_protocol;
+ break;
+ default:
+ return CURLE_UNKNOWN_OPTION;
+ }
+
+ return CURLE_OK;
+}
+
+#define DOUBLE_SECS(x) (double)(x)/1000000
+
+static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info,
+ curl_off_t *param_offt)
+{
+#ifdef DEBUGBUILD
+ char *timestr = getenv("CURL_TIME");
+ if(timestr) {
+ unsigned long val = strtol(timestr, NULL, 10);
+ switch(info) {
+ case CURLINFO_TOTAL_TIME_T:
+ case CURLINFO_NAMELOOKUP_TIME_T:
+ case CURLINFO_CONNECT_TIME_T:
+ case CURLINFO_APPCONNECT_TIME_T:
+ case CURLINFO_PRETRANSFER_TIME_T:
+ case CURLINFO_STARTTRANSFER_TIME_T:
+ case CURLINFO_REDIRECT_TIME_T:
+ case CURLINFO_SPEED_DOWNLOAD_T:
+ case CURLINFO_SPEED_UPLOAD_T:
+ *param_offt = (curl_off_t)val;
+ return CURLE_OK;
+ default:
+ break;
+ }
+ }
+#endif
+ switch(info) {
+ case CURLINFO_FILETIME_T:
+ *param_offt = (curl_off_t)data->info.filetime;
+ break;
+ case CURLINFO_SIZE_UPLOAD_T:
+ *param_offt = data->progress.uploaded;
+ break;
+ case CURLINFO_SIZE_DOWNLOAD_T:
+ *param_offt = data->progress.downloaded;
+ break;
+ case CURLINFO_SPEED_DOWNLOAD_T:
+ *param_offt = data->progress.dlspeed;
+ break;
+ case CURLINFO_SPEED_UPLOAD_T:
+ *param_offt = data->progress.ulspeed;
+ break;
+ case CURLINFO_CONTENT_LENGTH_DOWNLOAD_T:
+ *param_offt = (data->progress.flags & PGRS_DL_SIZE_KNOWN)?
+ data->progress.size_dl:-1;
+ break;
+ case CURLINFO_CONTENT_LENGTH_UPLOAD_T:
+ *param_offt = (data->progress.flags & PGRS_UL_SIZE_KNOWN)?
+ data->progress.size_ul:-1;
+ break;
+ case CURLINFO_TOTAL_TIME_T:
+ *param_offt = data->progress.timespent;
+ break;
+ case CURLINFO_NAMELOOKUP_TIME_T:
+ *param_offt = data->progress.t_nslookup;
+ break;
+ case CURLINFO_CONNECT_TIME_T:
+ *param_offt = data->progress.t_connect;
+ break;
+ case CURLINFO_APPCONNECT_TIME_T:
+ *param_offt = data->progress.t_appconnect;
+ break;
+ case CURLINFO_PRETRANSFER_TIME_T:
+ *param_offt = data->progress.t_pretransfer;
+ break;
+ case CURLINFO_STARTTRANSFER_TIME_T:
+ *param_offt = data->progress.t_starttransfer;
+ break;
+ case CURLINFO_REDIRECT_TIME_T:
+ *param_offt = data->progress.t_redirect;
+ break;
+ case CURLINFO_RETRY_AFTER:
+ *param_offt = data->info.retry_after;
+ break;
+ default:
+ return CURLE_UNKNOWN_OPTION;
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode getinfo_double(struct Curl_easy *data, CURLINFO info,
+ double *param_doublep)
+{
+#ifdef DEBUGBUILD
+ char *timestr = getenv("CURL_TIME");
+ if(timestr) {
+ unsigned long val = strtol(timestr, NULL, 10);
+ switch(info) {
+ case CURLINFO_TOTAL_TIME:
+ case CURLINFO_NAMELOOKUP_TIME:
+ case CURLINFO_CONNECT_TIME:
+ case CURLINFO_APPCONNECT_TIME:
+ case CURLINFO_PRETRANSFER_TIME:
+ case CURLINFO_STARTTRANSFER_TIME:
+ case CURLINFO_REDIRECT_TIME:
+ case CURLINFO_SPEED_DOWNLOAD:
+ case CURLINFO_SPEED_UPLOAD:
+ *param_doublep = (double)val;
+ return CURLE_OK;
+ default:
+ break;
+ }
+ }
+#endif
+ switch(info) {
+ case CURLINFO_TOTAL_TIME:
+ *param_doublep = DOUBLE_SECS(data->progress.timespent);
+ break;
+ case CURLINFO_NAMELOOKUP_TIME:
+ *param_doublep = DOUBLE_SECS(data->progress.t_nslookup);
+ break;
+ case CURLINFO_CONNECT_TIME:
+ *param_doublep = DOUBLE_SECS(data->progress.t_connect);
+ break;
+ case CURLINFO_APPCONNECT_TIME:
+ *param_doublep = DOUBLE_SECS(data->progress.t_appconnect);
+ break;
+ case CURLINFO_PRETRANSFER_TIME:
+ *param_doublep = DOUBLE_SECS(data->progress.t_pretransfer);
+ break;
+ case CURLINFO_STARTTRANSFER_TIME:
+ *param_doublep = DOUBLE_SECS(data->progress.t_starttransfer);
+ break;
+ case CURLINFO_SIZE_UPLOAD:
+ *param_doublep = (double)data->progress.uploaded;
+ break;
+ case CURLINFO_SIZE_DOWNLOAD:
+ *param_doublep = (double)data->progress.downloaded;
+ break;
+ case CURLINFO_SPEED_DOWNLOAD:
+ *param_doublep = (double)data->progress.dlspeed;
+ break;
+ case CURLINFO_SPEED_UPLOAD:
+ *param_doublep = (double)data->progress.ulspeed;
+ break;
+ case CURLINFO_CONTENT_LENGTH_DOWNLOAD:
+ *param_doublep = (data->progress.flags & PGRS_DL_SIZE_KNOWN)?
+ (double)data->progress.size_dl:-1;
+ break;
+ case CURLINFO_CONTENT_LENGTH_UPLOAD:
+ *param_doublep = (data->progress.flags & PGRS_UL_SIZE_KNOWN)?
+ (double)data->progress.size_ul:-1;
+ break;
+ case CURLINFO_REDIRECT_TIME:
+ *param_doublep = DOUBLE_SECS(data->progress.t_redirect);
+ break;
+
+ default:
+ return CURLE_UNKNOWN_OPTION;
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode getinfo_slist(struct Curl_easy *data, CURLINFO info,
+ struct curl_slist **param_slistp)
+{
+ union {
+ struct curl_certinfo *to_certinfo;
+ struct curl_slist *to_slist;
+ } ptr;
+
+ switch(info) {
+ case CURLINFO_SSL_ENGINES:
+ *param_slistp = Curl_ssl_engines_list(data);
+ break;
+ case CURLINFO_COOKIELIST:
+ *param_slistp = Curl_cookie_list(data);
+ break;
+ case CURLINFO_CERTINFO:
+ /* Return the a pointer to the certinfo struct. Not really an slist
+ pointer but we can pretend it is here */
+ ptr.to_certinfo = &data->info.certs;
+ *param_slistp = ptr.to_slist;
+ break;
+ case CURLINFO_TLS_SESSION:
+ case CURLINFO_TLS_SSL_PTR:
+ {
+ struct curl_tlssessioninfo **tsip = (struct curl_tlssessioninfo **)
+ param_slistp;
+ struct curl_tlssessioninfo *tsi = &data->tsi;
+#ifdef USE_SSL
+ struct connectdata *conn = data->conn;
+#endif
+
+ *tsip = tsi;
+ tsi->backend = Curl_ssl_backend();
+ tsi->internals = NULL;
+
+#ifdef USE_SSL
+ if(conn && tsi->backend != CURLSSLBACKEND_NONE) {
+ tsi->internals = Curl_ssl_get_internals(data, FIRSTSOCKET, info, 0);
+ }
+#endif
+ }
+ break;
+ default:
+ return CURLE_UNKNOWN_OPTION;
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode getinfo_socket(struct Curl_easy *data, CURLINFO info,
+ curl_socket_t *param_socketp)
+{
+ switch(info) {
+ case CURLINFO_ACTIVESOCKET:
+ *param_socketp = Curl_getconnectinfo(data, NULL);
+ break;
+ default:
+ return CURLE_UNKNOWN_OPTION;
+ }
+
+ return CURLE_OK;
+}
+
+CURLcode Curl_getinfo(struct Curl_easy *data, CURLINFO info, ...)
+{
+ va_list arg;
+ long *param_longp = NULL;
+ double *param_doublep = NULL;
+ curl_off_t *param_offt = NULL;
+ const char **param_charp = NULL;
+ struct curl_slist **param_slistp = NULL;
+ curl_socket_t *param_socketp = NULL;
+ int type;
+ CURLcode result = CURLE_UNKNOWN_OPTION;
+
+ if(!data)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ va_start(arg, info);
+
+ type = CURLINFO_TYPEMASK & (int)info;
+ switch(type) {
+ case CURLINFO_STRING:
+ param_charp = va_arg(arg, const char **);
+ if(param_charp)
+ result = getinfo_char(data, info, param_charp);
+ break;
+ case CURLINFO_LONG:
+ param_longp = va_arg(arg, long *);
+ if(param_longp)
+ result = getinfo_long(data, info, param_longp);
+ break;
+ case CURLINFO_DOUBLE:
+ param_doublep = va_arg(arg, double *);
+ if(param_doublep)
+ result = getinfo_double(data, info, param_doublep);
+ break;
+ case CURLINFO_OFF_T:
+ param_offt = va_arg(arg, curl_off_t *);
+ if(param_offt)
+ result = getinfo_offt(data, info, param_offt);
+ break;
+ case CURLINFO_SLIST:
+ param_slistp = va_arg(arg, struct curl_slist **);
+ if(param_slistp)
+ result = getinfo_slist(data, info, param_slistp);
+ break;
+ case CURLINFO_SOCKET:
+ param_socketp = va_arg(arg, curl_socket_t *);
+ if(param_socketp)
+ result = getinfo_socket(data, info, param_socketp);
+ break;
+ default:
+ break;
+ }
+
+ va_end(arg);
+
+ return result;
+}
diff --git a/libs/libcurl/src/getinfo.h b/libs/libcurl/src/getinfo.h index 1b5e8c20f8..82bcf4e2eb 100644 --- a/libs/libcurl/src/getinfo.h +++ b/libs/libcurl/src/getinfo.h @@ -1,29 +1,29 @@ -#ifndef HEADER_CURL_GETINFO_H -#define HEADER_CURL_GETINFO_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 Curl_getinfo(struct Curl_easy *data, CURLINFO info, ...); -CURLcode Curl_initinfo(struct Curl_easy *data); - -#endif /* HEADER_CURL_GETINFO_H */ +#ifndef HEADER_CURL_GETINFO_H
+#define HEADER_CURL_GETINFO_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 Curl_getinfo(struct Curl_easy *data, CURLINFO info, ...);
+CURLcode Curl_initinfo(struct Curl_easy *data);
+
+#endif /* HEADER_CURL_GETINFO_H */
diff --git a/libs/libcurl/src/gopher.c b/libs/libcurl/src/gopher.c index 01f4bdef0c..797d64956a 100644 --- a/libs/libcurl/src/gopher.c +++ b/libs/libcurl/src/gopher.c @@ -1,239 +1,242 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_GOPHER - -#include "urldata.h" -#include <curl/curl.h> -#include "transfer.h" -#include "sendf.h" -#include "connect.h" -#include "progress.h" -#include "gopher.h" -#include "select.h" -#include "strdup.h" -#include "vtls/vtls.h" -#include "url.h" -#include "escape.h" -#include "warnless.h" -#include "curl_printf.h" -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -/* - * Forward declarations. - */ - -static CURLcode gopher_do(struct Curl_easy *data, bool *done); -#ifdef USE_SSL -static CURLcode gopher_connect(struct Curl_easy *data, bool *done); -static CURLcode gopher_connecting(struct Curl_easy *data, bool *done); -#endif - -/* - * Gopher protocol handler. - * This is also a nice simple template to build off for simple - * connect-command-download protocols. - */ - -const struct Curl_handler Curl_handler_gopher = { - "GOPHER", /* scheme */ - ZERO_NULL, /* setup_connection */ - gopher_do, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_GOPHER, /* defport */ - CURLPROTO_GOPHER, /* protocol */ - CURLPROTO_GOPHER, /* family */ - PROTOPT_NONE /* flags */ -}; - -#ifdef USE_SSL -const struct Curl_handler Curl_handler_gophers = { - "GOPHERS", /* scheme */ - ZERO_NULL, /* setup_connection */ - gopher_do, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - gopher_connect, /* connect_it */ - gopher_connecting, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_GOPHER, /* defport */ - CURLPROTO_GOPHERS, /* protocol */ - CURLPROTO_GOPHER, /* family */ - PROTOPT_SSL /* flags */ -}; - -static CURLcode gopher_connect(struct Curl_easy *data, bool *done) -{ - (void)data; - (void)done; - return CURLE_OK; -} - -static CURLcode gopher_connecting(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - CURLcode result = Curl_ssl_connect(data, conn, FIRSTSOCKET); - if(result) - connclose(conn, "Failed TLS connection"); - *done = TRUE; - return result; -} -#endif - -static CURLcode gopher_do(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; - char *gopherpath; - char *path = data->state.up.path; - char *query = data->state.up.query; - char *sel = NULL; - char *sel_org = NULL; - timediff_t timeout_ms; - ssize_t amount, k; - size_t len; - int what; - - *done = TRUE; /* unconditionally */ - - /* path is guaranteed non-NULL */ - DEBUGASSERT(path); - - if(query) - gopherpath = aprintf("%s?%s", path, query); - else - gopherpath = strdup(path); - - if(!gopherpath) - return CURLE_OUT_OF_MEMORY; - - /* Create selector. Degenerate cases: / and /1 => convert to "" */ - if(strlen(gopherpath) <= 2) { - sel = (char *)""; - len = strlen(sel); - free(gopherpath); - } - else { - char *newp; - - /* Otherwise, drop / and the first character (i.e., item type) ... */ - newp = gopherpath; - newp += 2; - - /* ... and finally unescape */ - result = Curl_urldecode(newp, 0, &sel, &len, REJECT_ZERO); - free(gopherpath); - if(result) - return result; - sel_org = sel; - } - - k = curlx_uztosz(len); - - for(;;) { - /* Break out of the loop if the selector is empty because OpenSSL and/or - LibreSSL fail with errno 0 if this is the case. */ - if(strlen(sel) < 1) - break; - - result = Curl_write(data, sockfd, sel, k, &amount); - if(!result) { /* Which may not have written it all! */ - result = Curl_client_write(data, CLIENTWRITE_HEADER, sel, amount); - if(result) - break; - - k -= amount; - sel += amount; - if(k < 1) - break; /* but it did write it all */ - } - else - break; - - timeout_ms = Curl_timeleft(data, NULL, FALSE); - if(timeout_ms < 0) { - result = CURLE_OPERATION_TIMEDOUT; - break; - } - if(!timeout_ms) - timeout_ms = TIMEDIFF_T_MAX; - - /* Don't busyloop. The entire loop thing is a work-around as it causes a - BLOCKING behavior which is a NO-NO. This function should rather be - split up in a do and a doing piece where the pieces that aren't - possible to send now will be sent in the doing function repeatedly - until the entire request is sent. - */ - what = SOCKET_WRITABLE(sockfd, timeout_ms); - if(what < 0) { - result = CURLE_SEND_ERROR; - break; - } - else if(!what) { - result = CURLE_OPERATION_TIMEDOUT; - break; - } - } - - free(sel_org); - - if(!result) - result = Curl_write(data, sockfd, "\r\n", 2, &amount); - if(result) { - failf(data, "Failed sending Gopher request"); - return result; - } - result = Curl_client_write(data, CLIENTWRITE_HEADER, (char *)"\r\n", 2); - if(result) - return result; - - Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); - return CURLE_OK; -} -#endif /*CURL_DISABLE_GOPHER*/ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_GOPHER
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "transfer.h"
+#include "sendf.h"
+#include "cfilters.h"
+#include "connect.h"
+#include "progress.h"
+#include "gopher.h"
+#include "select.h"
+#include "strdup.h"
+#include "vtls/vtls.h"
+#include "url.h"
+#include "escape.h"
+#include "warnless.h"
+#include "curl_printf.h"
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/*
+ * Forward declarations.
+ */
+
+static CURLcode gopher_do(struct Curl_easy *data, bool *done);
+#ifdef USE_SSL
+static CURLcode gopher_connect(struct Curl_easy *data, bool *done);
+static CURLcode gopher_connecting(struct Curl_easy *data, bool *done);
+#endif
+
+/*
+ * Gopher protocol handler.
+ * This is also a nice simple template to build off for simple
+ * connect-command-download protocols.
+ */
+
+const struct Curl_handler Curl_handler_gopher = {
+ "GOPHER", /* scheme */
+ ZERO_NULL, /* setup_connection */
+ gopher_do, /* do_it */
+ ZERO_NULL, /* done */
+ ZERO_NULL, /* do_more */
+ ZERO_NULL, /* connect_it */
+ ZERO_NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ ZERO_NULL, /* proto_getsock */
+ ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ ZERO_NULL, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_GOPHER, /* defport */
+ CURLPROTO_GOPHER, /* protocol */
+ CURLPROTO_GOPHER, /* family */
+ PROTOPT_NONE /* flags */
+};
+
+#ifdef USE_SSL
+const struct Curl_handler Curl_handler_gophers = {
+ "GOPHERS", /* scheme */
+ ZERO_NULL, /* setup_connection */
+ gopher_do, /* do_it */
+ ZERO_NULL, /* done */
+ ZERO_NULL, /* do_more */
+ gopher_connect, /* connect_it */
+ gopher_connecting, /* connecting */
+ ZERO_NULL, /* doing */
+ ZERO_NULL, /* proto_getsock */
+ ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ ZERO_NULL, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_GOPHER, /* defport */
+ CURLPROTO_GOPHERS, /* protocol */
+ CURLPROTO_GOPHER, /* family */
+ PROTOPT_SSL /* flags */
+};
+
+static CURLcode gopher_connect(struct Curl_easy *data, bool *done)
+{
+ (void)data;
+ (void)done;
+ return CURLE_OK;
+}
+
+static CURLcode gopher_connecting(struct Curl_easy *data, bool *done)
+{
+ struct connectdata *conn = data->conn;
+ CURLcode result;
+
+ result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done);
+ if(result)
+ connclose(conn, "Failed TLS connection");
+ *done = TRUE;
+ return result;
+}
+#endif
+
+static CURLcode gopher_do(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+ char *gopherpath;
+ char *path = data->state.up.path;
+ char *query = data->state.up.query;
+ char *sel = NULL;
+ char *sel_org = NULL;
+ timediff_t timeout_ms;
+ ssize_t amount, k;
+ size_t len;
+ int what;
+
+ *done = TRUE; /* unconditionally */
+
+ /* path is guaranteed non-NULL */
+ DEBUGASSERT(path);
+
+ if(query)
+ gopherpath = aprintf("%s?%s", path, query);
+ else
+ gopherpath = strdup(path);
+
+ if(!gopherpath)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Create selector. Degenerate cases: / and /1 => convert to "" */
+ if(strlen(gopherpath) <= 2) {
+ sel = (char *)"";
+ len = strlen(sel);
+ free(gopherpath);
+ }
+ else {
+ char *newp;
+
+ /* Otherwise, drop / and the first character (i.e., item type) ... */
+ newp = gopherpath;
+ newp += 2;
+
+ /* ... and finally unescape */
+ result = Curl_urldecode(newp, 0, &sel, &len, REJECT_ZERO);
+ free(gopherpath);
+ if(result)
+ return result;
+ sel_org = sel;
+ }
+
+ k = curlx_uztosz(len);
+
+ for(;;) {
+ /* Break out of the loop if the selector is empty because OpenSSL and/or
+ LibreSSL fail with errno 0 if this is the case. */
+ if(strlen(sel) < 1)
+ break;
+
+ result = Curl_write(data, sockfd, sel, k, &amount);
+ if(!result) { /* Which may not have written it all! */
+ result = Curl_client_write(data, CLIENTWRITE_HEADER, sel, amount);
+ if(result)
+ break;
+
+ k -= amount;
+ sel += amount;
+ if(k < 1)
+ break; /* but it did write it all */
+ }
+ else
+ break;
+
+ timeout_ms = Curl_timeleft(data, NULL, FALSE);
+ if(timeout_ms < 0) {
+ result = CURLE_OPERATION_TIMEDOUT;
+ break;
+ }
+ if(!timeout_ms)
+ timeout_ms = TIMEDIFF_T_MAX;
+
+ /* Don't busyloop. The entire loop thing is a work-around as it causes a
+ BLOCKING behavior which is a NO-NO. This function should rather be
+ split up in a do and a doing piece where the pieces that aren't
+ possible to send now will be sent in the doing function repeatedly
+ until the entire request is sent.
+ */
+ what = SOCKET_WRITABLE(sockfd, timeout_ms);
+ if(what < 0) {
+ result = CURLE_SEND_ERROR;
+ break;
+ }
+ else if(!what) {
+ result = CURLE_OPERATION_TIMEDOUT;
+ break;
+ }
+ }
+
+ free(sel_org);
+
+ if(!result)
+ result = Curl_write(data, sockfd, "\r\n", 2, &amount);
+ if(result) {
+ failf(data, "Failed sending Gopher request");
+ return result;
+ }
+ result = Curl_client_write(data, CLIENTWRITE_HEADER, (char *)"\r\n", 2);
+ if(result)
+ return result;
+
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
+ return CURLE_OK;
+}
+#endif /* CURL_DISABLE_GOPHER */
diff --git a/libs/libcurl/src/gopher.h b/libs/libcurl/src/gopher.h index 4ea269d2b4..68ca41741f 100644 --- a/libs/libcurl/src/gopher.h +++ b/libs/libcurl/src/gopher.h @@ -1,34 +1,34 @@ -#ifndef HEADER_CURL_GOPHER_H -#define HEADER_CURL_GOPHER_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_GOPHER -extern const struct Curl_handler Curl_handler_gopher; -#ifdef USE_SSL -extern const struct Curl_handler Curl_handler_gophers; -#endif -#endif - -#endif /* HEADER_CURL_GOPHER_H */ +#ifndef HEADER_CURL_GOPHER_H
+#define HEADER_CURL_GOPHER_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_GOPHER
+extern const struct Curl_handler Curl_handler_gopher;
+#ifdef USE_SSL
+extern const struct Curl_handler Curl_handler_gophers;
+#endif
+#endif
+
+#endif /* HEADER_CURL_GOPHER_H */
diff --git a/libs/libcurl/src/h2h3.c b/libs/libcurl/src/h2h3.c index 50254ad0fa..fb301b4cbb 100644 --- a/libs/libcurl/src/h2h3.c +++ b/libs/libcurl/src/h2h3.c @@ -1,309 +1,309 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "h2h3.h" -#include "transfer.h" -#include "sendf.h" -#include "strcase.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* - * Curl_pseudo_headers() creates the array with pseudo headers to be - * used in a HTTP/2 or HTTP/3 request. - */ - -#if defined(USE_NGHTTP2) || defined(ENABLE_QUIC) - -/* Index where :authority header field will appear in request header - field list. */ -#define AUTHORITY_DST_IDX 3 - -/* USHRT_MAX is 65535 == 0xffff */ -#define HEADER_OVERFLOW(x) \ - (x.namelen > 0xffff || x.valuelen > 0xffff - x.namelen) - -/* - * Check header memory for the token "trailers". - * Parse the tokens as separated by comma and surrounded by whitespace. - * Returns TRUE if found or FALSE if not. - */ -static bool contains_trailers(const char *p, size_t len) -{ - const char *end = p + len; - for(;;) { - for(; p != end && (*p == ' ' || *p == '\t'); ++p) - ; - if(p == end || (size_t)(end - p) < sizeof("trailers") - 1) - return FALSE; - if(strncasecompare("trailers", p, sizeof("trailers") - 1)) { - p += sizeof("trailers") - 1; - for(; p != end && (*p == ' ' || *p == '\t'); ++p) - ; - if(p == end || *p == ',') - return TRUE; - } - /* skip to next token */ - for(; p != end && *p != ','; ++p) - ; - if(p == end) - return FALSE; - ++p; - } -} - -typedef enum { - /* Send header to server */ - HEADERINST_FORWARD, - /* Don't send header to server */ - HEADERINST_IGNORE, - /* Discard header, and replace it with "te: trailers" */ - HEADERINST_TE_TRAILERS -} header_instruction; - -/* Decides how to treat given header field. */ -static header_instruction inspect_header(const char *name, size_t namelen, - const char *value, size_t valuelen) { - switch(namelen) { - case 2: - if(!strncasecompare("te", name, namelen)) - return HEADERINST_FORWARD; - - return contains_trailers(value, valuelen) ? - HEADERINST_TE_TRAILERS : HEADERINST_IGNORE; - case 7: - return strncasecompare("upgrade", name, namelen) ? - HEADERINST_IGNORE : HEADERINST_FORWARD; - case 10: - return (strncasecompare("connection", name, namelen) || - strncasecompare("keep-alive", name, namelen)) ? - HEADERINST_IGNORE : HEADERINST_FORWARD; - case 16: - return strncasecompare("proxy-connection", name, namelen) ? - HEADERINST_IGNORE : HEADERINST_FORWARD; - case 17: - return strncasecompare("transfer-encoding", name, namelen) ? - HEADERINST_IGNORE : HEADERINST_FORWARD; - default: - return HEADERINST_FORWARD; - } -} - -CURLcode Curl_pseudo_headers(struct Curl_easy *data, - const char *mem, /* the request */ - const size_t len /* size of request */, - struct h2h3req **hp) -{ - struct connectdata *conn = data->conn; - size_t nheader = 0; - size_t i; - size_t authority_idx; - char *hdbuf = (char *)mem; - char *end, *line_end; - struct h2h3pseudo *nva = NULL; - struct h2h3req *hreq = NULL; - char *vptr; - - /* Calculate number of headers contained in [mem, mem + len). Assumes a - correctly generated HTTP header field block. */ - for(i = 1; i < len; ++i) { - if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') { - ++nheader; - ++i; - } - } - if(nheader < 2) { - goto fail; - } - /* We counted additional 2 \r\n in the first and last line. We need 3 - new headers: :method, :path and :scheme. Therefore we need one - more space. */ - nheader += 1; - hreq = malloc(sizeof(struct h2h3req) + - sizeof(struct h2h3pseudo) * (nheader - 1)); - if(!hreq) { - goto fail; - } - - nva = &hreq->header[0]; - - /* Extract :method, :path from request line - We do line endings with CRLF so checking for CR is enough */ - line_end = memchr(hdbuf, '\r', len); - if(!line_end) { - goto fail; - } - - /* Method does not contain spaces */ - end = memchr(hdbuf, ' ', line_end - hdbuf); - if(!end || end == hdbuf) - goto fail; - nva[0].name = H2H3_PSEUDO_METHOD; - nva[0].namelen = sizeof(H2H3_PSEUDO_METHOD) - 1; - nva[0].value = hdbuf; - nva[0].valuelen = (size_t)(end - hdbuf); - - hdbuf = end + 1; - - /* Path may contain spaces so scan backwards */ - end = NULL; - for(i = (size_t)(line_end - hdbuf); i; --i) { - if(hdbuf[i - 1] == ' ') { - end = &hdbuf[i - 1]; - break; - } - } - if(!end || end == hdbuf) - goto fail; - nva[1].name = H2H3_PSEUDO_PATH; - nva[1].namelen = sizeof(H2H3_PSEUDO_PATH) - 1; - nva[1].value = hdbuf; - nva[1].valuelen = (end - hdbuf); - - nva[2].name = H2H3_PSEUDO_SCHEME; - nva[2].namelen = sizeof(H2H3_PSEUDO_SCHEME) - 1; - vptr = Curl_checkheaders(data, STRCONST(H2H3_PSEUDO_SCHEME)); - if(vptr) { - vptr += sizeof(H2H3_PSEUDO_SCHEME); - while(*vptr && ISBLANK(*vptr)) - vptr++; - nva[2].value = vptr; - infof(data, "set pseudo header %s to %s", H2H3_PSEUDO_SCHEME, vptr); - } - else { - if(conn->handler->flags & PROTOPT_SSL) - nva[2].value = "https"; - else - nva[2].value = "http"; - } - nva[2].valuelen = strlen((char *)nva[2].value); - - authority_idx = 0; - i = 3; - while(i < nheader) { - size_t hlen; - - hdbuf = line_end + 2; - - /* check for next CR, but only within the piece of data left in the given - buffer */ - line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem)); - if(!line_end || (line_end == hdbuf)) - goto fail; - - /* header continuation lines are not supported */ - if(*hdbuf == ' ' || *hdbuf == '\t') - goto fail; - - for(end = hdbuf; end < line_end && *end != ':'; ++end) - ; - if(end == hdbuf || end == line_end) - goto fail; - hlen = end - hdbuf; - - if(hlen == 4 && strncasecompare("host", hdbuf, 4)) { - authority_idx = i; - nva[i].name = H2H3_PSEUDO_AUTHORITY; - nva[i].namelen = sizeof(H2H3_PSEUDO_AUTHORITY) - 1; - } - else { - nva[i].namelen = (size_t)(end - hdbuf); - /* Lower case the header name for HTTP/3 */ - Curl_strntolower((char *)hdbuf, hdbuf, nva[i].namelen); - nva[i].name = hdbuf; - } - hdbuf = end + 1; - while(*hdbuf == ' ' || *hdbuf == '\t') - ++hdbuf; - end = line_end; - - switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf, - end - hdbuf)) { - case HEADERINST_IGNORE: - /* skip header fields prohibited by HTTP/2 specification. */ - --nheader; - continue; - case HEADERINST_TE_TRAILERS: - nva[i].value = "trailers"; - nva[i].valuelen = sizeof("trailers") - 1; - break; - default: - nva[i].value = hdbuf; - nva[i].valuelen = (end - hdbuf); - } - - ++i; - } - - /* :authority must come before non-pseudo header fields */ - if(authority_idx && authority_idx != AUTHORITY_DST_IDX) { - struct h2h3pseudo authority = nva[authority_idx]; - for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) { - nva[i] = nva[i - 1]; - } - nva[i] = authority; - } - - /* Warn stream may be rejected if cumulative length of headers is too - large. */ -#define MAX_ACC 60000 /* <64KB to account for some overhead */ - { - size_t acc = 0; - - for(i = 0; i < nheader; ++i) { - acc += nva[i].namelen + nva[i].valuelen; - - infof(data, "h2h3 [%.*s: %.*s]", - (int)nva[i].namelen, nva[i].name, - (int)nva[i].valuelen, nva[i].value); - } - - if(acc > MAX_ACC) { - infof(data, "http_request: Warning: The cumulative length of all " - "headers exceeds %d bytes and that could cause the " - "stream to be rejected.", MAX_ACC); - } - } - - hreq->entries = nheader; - *hp = hreq; - - return CURLE_OK; - - fail: - free(hreq); - return CURLE_OUT_OF_MEMORY; -} - -void Curl_pseudo_free(struct h2h3req *hp) -{ - free(hp); -} - -#endif /* USE_NGHTTP2 or HTTP/3 enabled */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "h2h3.h"
+#include "transfer.h"
+#include "sendf.h"
+#include "strcase.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * Curl_pseudo_headers() creates the array with pseudo headers to be
+ * used in an HTTP/2 or HTTP/3 request.
+ */
+
+#if defined(USE_NGHTTP2) || defined(ENABLE_QUIC)
+
+/* Index where :authority header field will appear in request header
+ field list. */
+#define AUTHORITY_DST_IDX 3
+
+/* USHRT_MAX is 65535 == 0xffff */
+#define HEADER_OVERFLOW(x) \
+ (x.namelen > 0xffff || x.valuelen > 0xffff - x.namelen)
+
+/*
+ * Check header memory for the token "trailers".
+ * Parse the tokens as separated by comma and surrounded by whitespace.
+ * Returns TRUE if found or FALSE if not.
+ */
+static bool contains_trailers(const char *p, size_t len)
+{
+ const char *end = p + len;
+ for(;;) {
+ for(; p != end && (*p == ' ' || *p == '\t'); ++p)
+ ;
+ if(p == end || (size_t)(end - p) < sizeof("trailers") - 1)
+ return FALSE;
+ if(strncasecompare("trailers", p, sizeof("trailers") - 1)) {
+ p += sizeof("trailers") - 1;
+ for(; p != end && (*p == ' ' || *p == '\t'); ++p)
+ ;
+ if(p == end || *p == ',')
+ return TRUE;
+ }
+ /* skip to next token */
+ for(; p != end && *p != ','; ++p)
+ ;
+ if(p == end)
+ return FALSE;
+ ++p;
+ }
+}
+
+typedef enum {
+ /* Send header to server */
+ HEADERINST_FORWARD,
+ /* Don't send header to server */
+ HEADERINST_IGNORE,
+ /* Discard header, and replace it with "te: trailers" */
+ HEADERINST_TE_TRAILERS
+} header_instruction;
+
+/* Decides how to treat given header field. */
+static header_instruction inspect_header(const char *name, size_t namelen,
+ const char *value, size_t valuelen) {
+ switch(namelen) {
+ case 2:
+ if(!strncasecompare("te", name, namelen))
+ return HEADERINST_FORWARD;
+
+ return contains_trailers(value, valuelen) ?
+ HEADERINST_TE_TRAILERS : HEADERINST_IGNORE;
+ case 7:
+ return strncasecompare("upgrade", name, namelen) ?
+ HEADERINST_IGNORE : HEADERINST_FORWARD;
+ case 10:
+ return (strncasecompare("connection", name, namelen) ||
+ strncasecompare("keep-alive", name, namelen)) ?
+ HEADERINST_IGNORE : HEADERINST_FORWARD;
+ case 16:
+ return strncasecompare("proxy-connection", name, namelen) ?
+ HEADERINST_IGNORE : HEADERINST_FORWARD;
+ case 17:
+ return strncasecompare("transfer-encoding", name, namelen) ?
+ HEADERINST_IGNORE : HEADERINST_FORWARD;
+ default:
+ return HEADERINST_FORWARD;
+ }
+}
+
+CURLcode Curl_pseudo_headers(struct Curl_easy *data,
+ const char *mem, /* the request */
+ const size_t len /* size of request */,
+ struct h2h3req **hp)
+{
+ struct connectdata *conn = data->conn;
+ size_t nheader = 0;
+ size_t i;
+ size_t authority_idx;
+ char *hdbuf = (char *)mem;
+ char *end, *line_end;
+ struct h2h3pseudo *nva = NULL;
+ struct h2h3req *hreq = NULL;
+ char *vptr;
+
+ /* Calculate number of headers contained in [mem, mem + len). Assumes a
+ correctly generated HTTP header field block. */
+ for(i = 1; i < len; ++i) {
+ if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') {
+ ++nheader;
+ ++i;
+ }
+ }
+ if(nheader < 2) {
+ goto fail;
+ }
+ /* We counted additional 2 \r\n in the first and last line. We need 3
+ new headers: :method, :path and :scheme. Therefore we need one
+ more space. */
+ nheader += 1;
+ hreq = malloc(sizeof(struct h2h3req) +
+ sizeof(struct h2h3pseudo) * (nheader - 1));
+ if(!hreq) {
+ goto fail;
+ }
+
+ nva = &hreq->header[0];
+
+ /* Extract :method, :path from request line
+ We do line endings with CRLF so checking for CR is enough */
+ line_end = memchr(hdbuf, '\r', len);
+ if(!line_end) {
+ goto fail;
+ }
+
+ /* Method does not contain spaces */
+ end = memchr(hdbuf, ' ', line_end - hdbuf);
+ if(!end || end == hdbuf)
+ goto fail;
+ nva[0].name = H2H3_PSEUDO_METHOD;
+ nva[0].namelen = sizeof(H2H3_PSEUDO_METHOD) - 1;
+ nva[0].value = hdbuf;
+ nva[0].valuelen = (size_t)(end - hdbuf);
+
+ hdbuf = end + 1;
+
+ /* Path may contain spaces so scan backwards */
+ end = NULL;
+ for(i = (size_t)(line_end - hdbuf); i; --i) {
+ if(hdbuf[i - 1] == ' ') {
+ end = &hdbuf[i - 1];
+ break;
+ }
+ }
+ if(!end || end == hdbuf)
+ goto fail;
+ nva[1].name = H2H3_PSEUDO_PATH;
+ nva[1].namelen = sizeof(H2H3_PSEUDO_PATH) - 1;
+ nva[1].value = hdbuf;
+ nva[1].valuelen = (end - hdbuf);
+
+ nva[2].name = H2H3_PSEUDO_SCHEME;
+ nva[2].namelen = sizeof(H2H3_PSEUDO_SCHEME) - 1;
+ vptr = Curl_checkheaders(data, STRCONST(H2H3_PSEUDO_SCHEME));
+ if(vptr) {
+ vptr += sizeof(H2H3_PSEUDO_SCHEME);
+ while(*vptr && ISBLANK(*vptr))
+ vptr++;
+ nva[2].value = vptr;
+ infof(data, "set pseudo header %s to %s", H2H3_PSEUDO_SCHEME, vptr);
+ }
+ else {
+ if(conn->handler->flags & PROTOPT_SSL)
+ nva[2].value = "https";
+ else
+ nva[2].value = "http";
+ }
+ nva[2].valuelen = strlen((char *)nva[2].value);
+
+ authority_idx = 0;
+ i = 3;
+ while(i < nheader) {
+ size_t hlen;
+
+ hdbuf = line_end + 2;
+
+ /* check for next CR, but only within the piece of data left in the given
+ buffer */
+ line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem));
+ if(!line_end || (line_end == hdbuf))
+ goto fail;
+
+ /* header continuation lines are not supported */
+ if(*hdbuf == ' ' || *hdbuf == '\t')
+ goto fail;
+
+ for(end = hdbuf; end < line_end && *end != ':'; ++end)
+ ;
+ if(end == hdbuf || end == line_end)
+ goto fail;
+ hlen = end - hdbuf;
+
+ if(hlen == 4 && strncasecompare("host", hdbuf, 4)) {
+ authority_idx = i;
+ nva[i].name = H2H3_PSEUDO_AUTHORITY;
+ nva[i].namelen = sizeof(H2H3_PSEUDO_AUTHORITY) - 1;
+ }
+ else {
+ nva[i].namelen = (size_t)(end - hdbuf);
+ /* Lower case the header name for HTTP/3 */
+ Curl_strntolower((char *)hdbuf, hdbuf, nva[i].namelen);
+ nva[i].name = hdbuf;
+ }
+ hdbuf = end + 1;
+ while(*hdbuf == ' ' || *hdbuf == '\t')
+ ++hdbuf;
+ end = line_end;
+
+ switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf,
+ end - hdbuf)) {
+ case HEADERINST_IGNORE:
+ /* skip header fields prohibited by HTTP/2 specification. */
+ --nheader;
+ continue;
+ case HEADERINST_TE_TRAILERS:
+ nva[i].value = "trailers";
+ nva[i].valuelen = sizeof("trailers") - 1;
+ break;
+ default:
+ nva[i].value = hdbuf;
+ nva[i].valuelen = (end - hdbuf);
+ }
+
+ ++i;
+ }
+
+ /* :authority must come before non-pseudo header fields */
+ if(authority_idx && authority_idx != AUTHORITY_DST_IDX) {
+ struct h2h3pseudo authority = nva[authority_idx];
+ for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
+ nva[i] = nva[i - 1];
+ }
+ nva[i] = authority;
+ }
+
+ /* Warn stream may be rejected if cumulative length of headers is too
+ large. */
+#define MAX_ACC 60000 /* <64KB to account for some overhead */
+ {
+ size_t acc = 0;
+
+ for(i = 0; i < nheader; ++i) {
+ acc += nva[i].namelen + nva[i].valuelen;
+
+ infof(data, "h2h3 [%.*s: %.*s]",
+ (int)nva[i].namelen, nva[i].name,
+ (int)nva[i].valuelen, nva[i].value);
+ }
+
+ if(acc > MAX_ACC) {
+ infof(data, "http_request: Warning: The cumulative length of all "
+ "headers exceeds %d bytes and that could cause the "
+ "stream to be rejected.", MAX_ACC);
+ }
+ }
+
+ hreq->entries = nheader;
+ *hp = hreq;
+
+ return CURLE_OK;
+
+ fail:
+ free(hreq);
+ return CURLE_OUT_OF_MEMORY;
+}
+
+void Curl_pseudo_free(struct h2h3req *hp)
+{
+ free(hp);
+}
+
+#endif /* USE_NGHTTP2 or HTTP/3 enabled */
diff --git a/libs/libcurl/src/h2h3.h b/libs/libcurl/src/h2h3.h index 84caec5d5e..9812d877ad 100644 --- a/libs/libcurl/src/h2h3.h +++ b/libs/libcurl/src/h2h3.h @@ -1,61 +1,61 @@ -#ifndef HEADER_CURL_H2H3_H -#define HEADER_CURL_H2H3_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -#define H2H3_PSEUDO_METHOD ":method" -#define H2H3_PSEUDO_SCHEME ":scheme" -#define H2H3_PSEUDO_AUTHORITY ":authority" -#define H2H3_PSEUDO_PATH ":path" -#define H2H3_PSEUDO_STATUS ":status" - -struct h2h3pseudo { - const char *name; - size_t namelen; - const char *value; - size_t valuelen; -}; - -struct h2h3req { - size_t entries; - struct h2h3pseudo header[1]; /* the array is allocated to contain entries */ -}; - -/* - * Curl_pseudo_headers() creates the array with pseudo headers to be - * used in a HTTP/2 or HTTP/3 request. Returns an allocated struct. - * Free it with Curl_pseudo_free(). - */ -CURLcode Curl_pseudo_headers(struct Curl_easy *data, - const char *request, - const size_t len, - struct h2h3req **hp); - -/* - * Curl_pseudo_free() frees a h2h3req struct. - */ -void Curl_pseudo_free(struct h2h3req *hp); - -#endif /* HEADER_CURL_H2H3_H */ +#ifndef HEADER_CURL_H2H3_H
+#define HEADER_CURL_H2H3_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+#define H2H3_PSEUDO_METHOD ":method"
+#define H2H3_PSEUDO_SCHEME ":scheme"
+#define H2H3_PSEUDO_AUTHORITY ":authority"
+#define H2H3_PSEUDO_PATH ":path"
+#define H2H3_PSEUDO_STATUS ":status"
+
+struct h2h3pseudo {
+ const char *name;
+ size_t namelen;
+ const char *value;
+ size_t valuelen;
+};
+
+struct h2h3req {
+ size_t entries;
+ struct h2h3pseudo header[1]; /* the array is allocated to contain entries */
+};
+
+/*
+ * Curl_pseudo_headers() creates the array with pseudo headers to be
+ * used in an HTTP/2 or HTTP/3 request. Returns an allocated struct.
+ * Free it with Curl_pseudo_free().
+ */
+CURLcode Curl_pseudo_headers(struct Curl_easy *data,
+ const char *request,
+ const size_t len,
+ struct h2h3req **hp);
+
+/*
+ * Curl_pseudo_free() frees a h2h3req struct.
+ */
+void Curl_pseudo_free(struct h2h3req *hp);
+
+#endif /* HEADER_CURL_H2H3_H */
diff --git a/libs/libcurl/src/hash.c b/libs/libcurl/src/hash.c index b6a2a33c72..f32315e4a2 100644 --- a/libs/libcurl/src/hash.c +++ b/libs/libcurl/src/hash.c @@ -1,371 +1,371 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "hash.h" -#include "llist.h" -#include "curl_memory.h" - -/* The last #include file should be: */ -#include "memdebug.h" - -static void -hash_element_dtor(void *user, void *element) -{ - struct Curl_hash *h = (struct Curl_hash *) user; - struct Curl_hash_element *e = (struct Curl_hash_element *) element; - - if(e->ptr) { - h->dtor(e->ptr); - e->ptr = NULL; - } - - e->key_len = 0; - - free(e); -} - -/* Initializes a hash structure. - * Return 1 on error, 0 is fine. - * - * @unittest: 1602 - * @unittest: 1603 - */ -void -Curl_hash_init(struct Curl_hash *h, - int slots, - hash_function hfunc, - comp_function comparator, - Curl_hash_dtor dtor) -{ - DEBUGASSERT(h); - DEBUGASSERT(slots); - DEBUGASSERT(hfunc); - DEBUGASSERT(comparator); - DEBUGASSERT(dtor); - - h->table = NULL; - h->hash_func = hfunc; - h->comp_func = comparator; - h->dtor = dtor; - h->size = 0; - h->slots = slots; -} - -static struct Curl_hash_element * -mk_hash_element(const void *key, size_t key_len, const void *p) -{ - /* allocate the struct plus memory after it to store the key */ - struct Curl_hash_element *he = malloc(sizeof(struct Curl_hash_element) + - key_len); - if(he) { - /* copy the key */ - memcpy(he->key, key, key_len); - he->key_len = key_len; - he->ptr = (void *) p; - } - return he; -} - -#define FETCH_LIST(x,y,z) &x->table[x->hash_func(y, z, x->slots)] - -/* Insert the data in the hash. If there already was a match in the hash, that - * data is replaced. This function also "lazily" allocates the table if - * needed, as it isn't done in the _init function (anymore). - * - * @unittest: 1305 - * @unittest: 1602 - * @unittest: 1603 - */ -void * -Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p) -{ - struct Curl_hash_element *he; - struct Curl_llist_element *le; - struct Curl_llist *l; - - DEBUGASSERT(h); - DEBUGASSERT(h->slots); - if(!h->table) { - int i; - h->table = malloc(h->slots * sizeof(struct Curl_llist)); - if(!h->table) - return NULL; /* OOM */ - for(i = 0; i < h->slots; ++i) - Curl_llist_init(&h->table[i], hash_element_dtor); - } - - l = FETCH_LIST(h, key, key_len); - - for(le = l->head; le; le = le->next) { - he = (struct Curl_hash_element *) le->ptr; - if(h->comp_func(he->key, he->key_len, key, key_len)) { - Curl_llist_remove(l, le, (void *)h); - --h->size; - break; - } - } - - he = mk_hash_element(key, key_len, p); - if(he) { - Curl_llist_insert_next(l, l->tail, he, &he->list); - ++h->size; - return p; /* return the new entry */ - } - - return NULL; /* failure */ -} - -/* Remove the identified hash entry. - * Returns non-zero on failure. - * - * @unittest: 1603 - */ -int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len) -{ - struct Curl_llist_element *le; - struct Curl_llist *l; - - DEBUGASSERT(h); - DEBUGASSERT(h->slots); - if(h->table) { - l = FETCH_LIST(h, key, key_len); - - for(le = l->head; le; le = le->next) { - struct Curl_hash_element *he = le->ptr; - if(h->comp_func(he->key, he->key_len, key, key_len)) { - Curl_llist_remove(l, le, (void *) h); - --h->size; - return 0; - } - } - } - return 1; -} - -/* Retrieves a hash element. - * - * @unittest: 1603 - */ -void * -Curl_hash_pick(struct Curl_hash *h, void *key, size_t key_len) -{ - struct Curl_llist_element *le; - struct Curl_llist *l; - - DEBUGASSERT(h); - if(h->table) { - DEBUGASSERT(h->slots); - l = FETCH_LIST(h, key, key_len); - for(le = l->head; le; le = le->next) { - struct Curl_hash_element *he = le->ptr; - if(h->comp_func(he->key, he->key_len, key, key_len)) { - return he->ptr; - } - } - } - - return NULL; -} - -#if defined(DEBUGBUILD) && defined(AGGRESSIVE_TEST) -void -Curl_hash_apply(Curl_hash *h, void *user, - void (*cb)(void *user, void *ptr)) -{ - struct Curl_llist_element *le; - int i; - - for(i = 0; i < h->slots; ++i) { - for(le = (h->table[i])->head; - le; - le = le->next) { - Curl_hash_element *el = le->ptr; - cb(user, el->ptr); - } - } -} -#endif - -/* Destroys all the entries in the given hash and resets its attributes, - * prepping the given hash for [static|dynamic] deallocation. - * - * @unittest: 1305 - * @unittest: 1602 - * @unittest: 1603 - */ -void -Curl_hash_destroy(struct Curl_hash *h) -{ - if(h->table) { - int i; - for(i = 0; i < h->slots; ++i) { - Curl_llist_destroy(&h->table[i], (void *) h); - } - Curl_safefree(h->table); - } - h->size = 0; - h->slots = 0; -} - -/* Removes all the entries in the given hash. - * - * @unittest: 1602 - */ -void -Curl_hash_clean(struct Curl_hash *h) -{ - Curl_hash_clean_with_criterium(h, NULL, NULL); -} - -/* Cleans all entries that pass the comp function criteria. */ -void -Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user, - int (*comp)(void *, void *)) -{ - struct Curl_llist_element *le; - struct Curl_llist_element *lnext; - struct Curl_llist *list; - int i; - - if(!h || !h->table) - return; - - for(i = 0; i < h->slots; ++i) { - list = &h->table[i]; - le = list->head; /* get first list entry */ - while(le) { - struct Curl_hash_element *he = le->ptr; - lnext = le->next; - /* ask the callback function if we shall remove this entry or not */ - if(!comp || comp(user, he->ptr)) { - Curl_llist_remove(list, le, (void *) h); - --h->size; /* one less entry in the hash now */ - } - le = lnext; - } - } -} - -size_t Curl_hash_str(void *key, size_t key_length, size_t slots_num) -{ - const char *key_str = (const char *) key; - const char *end = key_str + key_length; - size_t h = 5381; - - while(key_str < end) { - h += h << 5; - h ^= *key_str++; - } - - return (h % slots_num); -} - -size_t Curl_str_key_compare(void *k1, size_t key1_len, - void *k2, size_t key2_len) -{ - if((key1_len == key2_len) && !memcmp(k1, k2, key1_len)) - return 1; - - return 0; -} - -void Curl_hash_start_iterate(struct Curl_hash *hash, - struct Curl_hash_iterator *iter) -{ - iter->hash = hash; - iter->slot_index = 0; - iter->current_element = NULL; -} - -struct Curl_hash_element * -Curl_hash_next_element(struct Curl_hash_iterator *iter) -{ - struct Curl_hash *h = iter->hash; - - if(!h->table) - return NULL; /* empty hash, nothing to return */ - - /* Get the next element in the current list, if any */ - if(iter->current_element) - iter->current_element = iter->current_element->next; - - /* If we have reached the end of the list, find the next one */ - if(!iter->current_element) { - int i; - for(i = iter->slot_index; i < h->slots; i++) { - if(h->table[i].head) { - iter->current_element = h->table[i].head; - iter->slot_index = i + 1; - break; - } - } - } - - if(iter->current_element) { - struct Curl_hash_element *he = iter->current_element->ptr; - return he; - } - iter->current_element = NULL; - return NULL; -} - -#if 0 /* useful function for debugging hashes and their contents */ -void Curl_hash_print(struct Curl_hash *h, - void (*func)(void *)) -{ - struct Curl_hash_iterator iter; - struct Curl_hash_element *he; - int last_index = -1; - - if(!h) - return; - - fprintf(stderr, "=Hash dump=\n"); - - Curl_hash_start_iterate(h, &iter); - - he = Curl_hash_next_element(&iter); - while(he) { - if(iter.slot_index != last_index) { - fprintf(stderr, "index %d:", iter.slot_index); - if(last_index >= 0) { - fprintf(stderr, "\n"); - } - last_index = iter.slot_index; - } - - if(func) - func(he->ptr); - else - fprintf(stderr, " [%p]", (void *)he->ptr); - - he = Curl_hash_next_element(&iter); - } - fprintf(stderr, "\n"); -} -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "hash.h"
+#include "llist.h"
+#include "curl_memory.h"
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+static void
+hash_element_dtor(void *user, void *element)
+{
+ struct Curl_hash *h = (struct Curl_hash *) user;
+ struct Curl_hash_element *e = (struct Curl_hash_element *) element;
+
+ if(e->ptr) {
+ h->dtor(e->ptr);
+ e->ptr = NULL;
+ }
+
+ e->key_len = 0;
+
+ free(e);
+}
+
+/* Initializes a hash structure.
+ * Return 1 on error, 0 is fine.
+ *
+ * @unittest: 1602
+ * @unittest: 1603
+ */
+void
+Curl_hash_init(struct Curl_hash *h,
+ int slots,
+ hash_function hfunc,
+ comp_function comparator,
+ Curl_hash_dtor dtor)
+{
+ DEBUGASSERT(h);
+ DEBUGASSERT(slots);
+ DEBUGASSERT(hfunc);
+ DEBUGASSERT(comparator);
+ DEBUGASSERT(dtor);
+
+ h->table = NULL;
+ h->hash_func = hfunc;
+ h->comp_func = comparator;
+ h->dtor = dtor;
+ h->size = 0;
+ h->slots = slots;
+}
+
+static struct Curl_hash_element *
+mk_hash_element(const void *key, size_t key_len, const void *p)
+{
+ /* allocate the struct plus memory after it to store the key */
+ struct Curl_hash_element *he = malloc(sizeof(struct Curl_hash_element) +
+ key_len);
+ if(he) {
+ /* copy the key */
+ memcpy(he->key, key, key_len);
+ he->key_len = key_len;
+ he->ptr = (void *) p;
+ }
+ return he;
+}
+
+#define FETCH_LIST(x,y,z) &x->table[x->hash_func(y, z, x->slots)]
+
+/* Insert the data in the hash. If there already was a match in the hash, that
+ * data is replaced. This function also "lazily" allocates the table if
+ * needed, as it isn't done in the _init function (anymore).
+ *
+ * @unittest: 1305
+ * @unittest: 1602
+ * @unittest: 1603
+ */
+void *
+Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p)
+{
+ struct Curl_hash_element *he;
+ struct Curl_llist_element *le;
+ struct Curl_llist *l;
+
+ DEBUGASSERT(h);
+ DEBUGASSERT(h->slots);
+ if(!h->table) {
+ int i;
+ h->table = malloc(h->slots * sizeof(struct Curl_llist));
+ if(!h->table)
+ return NULL; /* OOM */
+ for(i = 0; i < h->slots; ++i)
+ Curl_llist_init(&h->table[i], hash_element_dtor);
+ }
+
+ l = FETCH_LIST(h, key, key_len);
+
+ for(le = l->head; le; le = le->next) {
+ he = (struct Curl_hash_element *) le->ptr;
+ if(h->comp_func(he->key, he->key_len, key, key_len)) {
+ Curl_llist_remove(l, le, (void *)h);
+ --h->size;
+ break;
+ }
+ }
+
+ he = mk_hash_element(key, key_len, p);
+ if(he) {
+ Curl_llist_insert_next(l, l->tail, he, &he->list);
+ ++h->size;
+ return p; /* return the new entry */
+ }
+
+ return NULL; /* failure */
+}
+
+/* Remove the identified hash entry.
+ * Returns non-zero on failure.
+ *
+ * @unittest: 1603
+ */
+int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len)
+{
+ struct Curl_llist_element *le;
+ struct Curl_llist *l;
+
+ DEBUGASSERT(h);
+ DEBUGASSERT(h->slots);
+ if(h->table) {
+ l = FETCH_LIST(h, key, key_len);
+
+ for(le = l->head; le; le = le->next) {
+ struct Curl_hash_element *he = le->ptr;
+ if(h->comp_func(he->key, he->key_len, key, key_len)) {
+ Curl_llist_remove(l, le, (void *) h);
+ --h->size;
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+/* Retrieves a hash element.
+ *
+ * @unittest: 1603
+ */
+void *
+Curl_hash_pick(struct Curl_hash *h, void *key, size_t key_len)
+{
+ struct Curl_llist_element *le;
+ struct Curl_llist *l;
+
+ DEBUGASSERT(h);
+ if(h->table) {
+ DEBUGASSERT(h->slots);
+ l = FETCH_LIST(h, key, key_len);
+ for(le = l->head; le; le = le->next) {
+ struct Curl_hash_element *he = le->ptr;
+ if(h->comp_func(he->key, he->key_len, key, key_len)) {
+ return he->ptr;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+#if defined(DEBUGBUILD) && defined(AGGRESSIVE_TEST)
+void
+Curl_hash_apply(Curl_hash *h, void *user,
+ void (*cb)(void *user, void *ptr))
+{
+ struct Curl_llist_element *le;
+ int i;
+
+ for(i = 0; i < h->slots; ++i) {
+ for(le = (h->table[i])->head;
+ le;
+ le = le->next) {
+ Curl_hash_element *el = le->ptr;
+ cb(user, el->ptr);
+ }
+ }
+}
+#endif
+
+/* Destroys all the entries in the given hash and resets its attributes,
+ * prepping the given hash for [static|dynamic] deallocation.
+ *
+ * @unittest: 1305
+ * @unittest: 1602
+ * @unittest: 1603
+ */
+void
+Curl_hash_destroy(struct Curl_hash *h)
+{
+ if(h->table) {
+ int i;
+ for(i = 0; i < h->slots; ++i) {
+ Curl_llist_destroy(&h->table[i], (void *) h);
+ }
+ Curl_safefree(h->table);
+ }
+ h->size = 0;
+ h->slots = 0;
+}
+
+/* Removes all the entries in the given hash.
+ *
+ * @unittest: 1602
+ */
+void
+Curl_hash_clean(struct Curl_hash *h)
+{
+ Curl_hash_clean_with_criterium(h, NULL, NULL);
+}
+
+/* Cleans all entries that pass the comp function criteria. */
+void
+Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user,
+ int (*comp)(void *, void *))
+{
+ struct Curl_llist_element *le;
+ struct Curl_llist_element *lnext;
+ struct Curl_llist *list;
+ int i;
+
+ if(!h || !h->table)
+ return;
+
+ for(i = 0; i < h->slots; ++i) {
+ list = &h->table[i];
+ le = list->head; /* get first list entry */
+ while(le) {
+ struct Curl_hash_element *he = le->ptr;
+ lnext = le->next;
+ /* ask the callback function if we shall remove this entry or not */
+ if(!comp || comp(user, he->ptr)) {
+ Curl_llist_remove(list, le, (void *) h);
+ --h->size; /* one less entry in the hash now */
+ }
+ le = lnext;
+ }
+ }
+}
+
+size_t Curl_hash_str(void *key, size_t key_length, size_t slots_num)
+{
+ const char *key_str = (const char *) key;
+ const char *end = key_str + key_length;
+ size_t h = 5381;
+
+ while(key_str < end) {
+ h += h << 5;
+ h ^= *key_str++;
+ }
+
+ return (h % slots_num);
+}
+
+size_t Curl_str_key_compare(void *k1, size_t key1_len,
+ void *k2, size_t key2_len)
+{
+ if((key1_len == key2_len) && !memcmp(k1, k2, key1_len))
+ return 1;
+
+ return 0;
+}
+
+void Curl_hash_start_iterate(struct Curl_hash *hash,
+ struct Curl_hash_iterator *iter)
+{
+ iter->hash = hash;
+ iter->slot_index = 0;
+ iter->current_element = NULL;
+}
+
+struct Curl_hash_element *
+Curl_hash_next_element(struct Curl_hash_iterator *iter)
+{
+ struct Curl_hash *h = iter->hash;
+
+ if(!h->table)
+ return NULL; /* empty hash, nothing to return */
+
+ /* Get the next element in the current list, if any */
+ if(iter->current_element)
+ iter->current_element = iter->current_element->next;
+
+ /* If we have reached the end of the list, find the next one */
+ if(!iter->current_element) {
+ int i;
+ for(i = iter->slot_index; i < h->slots; i++) {
+ if(h->table[i].head) {
+ iter->current_element = h->table[i].head;
+ iter->slot_index = i + 1;
+ break;
+ }
+ }
+ }
+
+ if(iter->current_element) {
+ struct Curl_hash_element *he = iter->current_element->ptr;
+ return he;
+ }
+ iter->current_element = NULL;
+ return NULL;
+}
+
+#if 0 /* useful function for debugging hashes and their contents */
+void Curl_hash_print(struct Curl_hash *h,
+ void (*func)(void *))
+{
+ struct Curl_hash_iterator iter;
+ struct Curl_hash_element *he;
+ int last_index = -1;
+
+ if(!h)
+ return;
+
+ fprintf(stderr, "=Hash dump=\n");
+
+ Curl_hash_start_iterate(h, &iter);
+
+ he = Curl_hash_next_element(&iter);
+ while(he) {
+ if(iter.slot_index != last_index) {
+ fprintf(stderr, "index %d:", iter.slot_index);
+ if(last_index >= 0) {
+ fprintf(stderr, "\n");
+ }
+ last_index = iter.slot_index;
+ }
+
+ if(func)
+ func(he->ptr);
+ else
+ fprintf(stderr, " [%p]", (void *)he->ptr);
+
+ he = Curl_hash_next_element(&iter);
+ }
+ fprintf(stderr, "\n");
+}
+#endif
diff --git a/libs/libcurl/src/hash.h b/libs/libcurl/src/hash.h index 5b59bf1118..c8a2ab3671 100644 --- a/libs/libcurl/src/hash.h +++ b/libs/libcurl/src/hash.h @@ -1,102 +1,102 @@ -#ifndef HEADER_CURL_HASH_H -#define HEADER_CURL_HASH_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#include <stddef.h> - -#include "llist.h" - -/* Hash function prototype */ -typedef size_t (*hash_function) (void *key, - size_t key_length, - size_t slots_num); - -/* - Comparator function prototype. Compares two keys. -*/ -typedef size_t (*comp_function) (void *key1, - size_t key1_len, - void *key2, - size_t key2_len); - -typedef void (*Curl_hash_dtor)(void *); - -struct Curl_hash { - struct Curl_llist *table; - - /* Hash function to be used for this hash table */ - hash_function hash_func; - - /* Comparator function to compare keys */ - comp_function comp_func; - Curl_hash_dtor dtor; - int slots; - size_t size; -}; - -struct Curl_hash_element { - struct Curl_llist_element list; - void *ptr; - size_t key_len; - char key[1]; /* allocated memory following the struct */ -}; - -struct Curl_hash_iterator { - struct Curl_hash *hash; - int slot_index; - struct Curl_llist_element *current_element; -}; - -void Curl_hash_init(struct Curl_hash *h, - int slots, - hash_function hfunc, - comp_function comparator, - Curl_hash_dtor dtor); - -void *Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p); -int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len); -void *Curl_hash_pick(struct Curl_hash *, void *key, size_t key_len); -void Curl_hash_apply(struct Curl_hash *h, void *user, - void (*cb)(void *user, void *ptr)); -#define Curl_hash_count(h) ((h)->size) -void Curl_hash_destroy(struct Curl_hash *h); -void Curl_hash_clean(struct Curl_hash *h); -void Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user, - int (*comp)(void *, void *)); -size_t Curl_hash_str(void *key, size_t key_length, size_t slots_num); -size_t Curl_str_key_compare(void *k1, size_t key1_len, void *k2, - size_t key2_len); -void Curl_hash_start_iterate(struct Curl_hash *hash, - struct Curl_hash_iterator *iter); -struct Curl_hash_element * -Curl_hash_next_element(struct Curl_hash_iterator *iter); - -void Curl_hash_print(struct Curl_hash *h, - void (*func)(void *)); - - -#endif /* HEADER_CURL_HASH_H */ +#ifndef HEADER_CURL_HASH_H
+#define HEADER_CURL_HASH_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <stddef.h>
+
+#include "llist.h"
+
+/* Hash function prototype */
+typedef size_t (*hash_function) (void *key,
+ size_t key_length,
+ size_t slots_num);
+
+/*
+ Comparator function prototype. Compares two keys.
+*/
+typedef size_t (*comp_function) (void *key1,
+ size_t key1_len,
+ void *key2,
+ size_t key2_len);
+
+typedef void (*Curl_hash_dtor)(void *);
+
+struct Curl_hash {
+ struct Curl_llist *table;
+
+ /* Hash function to be used for this hash table */
+ hash_function hash_func;
+
+ /* Comparator function to compare keys */
+ comp_function comp_func;
+ Curl_hash_dtor dtor;
+ int slots;
+ size_t size;
+};
+
+struct Curl_hash_element {
+ struct Curl_llist_element list;
+ void *ptr;
+ size_t key_len;
+ char key[1]; /* allocated memory following the struct */
+};
+
+struct Curl_hash_iterator {
+ struct Curl_hash *hash;
+ int slot_index;
+ struct Curl_llist_element *current_element;
+};
+
+void Curl_hash_init(struct Curl_hash *h,
+ int slots,
+ hash_function hfunc,
+ comp_function comparator,
+ Curl_hash_dtor dtor);
+
+void *Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p);
+int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len);
+void *Curl_hash_pick(struct Curl_hash *, void *key, size_t key_len);
+void Curl_hash_apply(struct Curl_hash *h, void *user,
+ void (*cb)(void *user, void *ptr));
+#define Curl_hash_count(h) ((h)->size)
+void Curl_hash_destroy(struct Curl_hash *h);
+void Curl_hash_clean(struct Curl_hash *h);
+void Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user,
+ int (*comp)(void *, void *));
+size_t Curl_hash_str(void *key, size_t key_length, size_t slots_num);
+size_t Curl_str_key_compare(void *k1, size_t key1_len, void *k2,
+ size_t key2_len);
+void Curl_hash_start_iterate(struct Curl_hash *hash,
+ struct Curl_hash_iterator *iter);
+struct Curl_hash_element *
+Curl_hash_next_element(struct Curl_hash_iterator *iter);
+
+void Curl_hash_print(struct Curl_hash *h,
+ void (*func)(void *));
+
+
+#endif /* HEADER_CURL_HASH_H */
diff --git a/libs/libcurl/src/headers.c b/libs/libcurl/src/headers.c index 978c918f4f..836c58574b 100644 --- a/libs/libcurl/src/headers.c +++ b/libs/libcurl/src/headers.c @@ -1,386 +1,386 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2022, 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 "strdup.h" -#include "strcase.h" -#include "headers.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API) - -/* Generate the curl_header struct for the user. This function MUST assign all - struct fields in the output struct. */ -static void copy_header_external(struct Curl_easy *data, - struct Curl_header_store *hs, - size_t index, - size_t amount, - struct Curl_llist_element *e, - struct curl_header **hout) -{ - struct curl_header *h = *hout = &data->state.headerout; - h->name = hs->name; - h->value = hs->value; - h->amount = amount; - h->index = index; - /* this will randomly OR a reserved bit for the sole purpose of making it - impossible for applications to do == comparisons, as that would otherwise - be very tempting and then lead to the reserved bits not being reserved - anymore. */ - h->origin = hs->type | (1<<27); - h->anchor = e; -} - -/* public API */ -CURLHcode curl_easy_header(CURL *easy, - const char *name, - size_t nameindex, - unsigned int type, - int request, - struct curl_header **hout) -{ - struct Curl_llist_element *e; - struct Curl_llist_element *e_pick = NULL; - struct Curl_easy *data = easy; - size_t match = 0; - size_t amount = 0; - struct Curl_header_store *hs = NULL; - struct Curl_header_store *pick = NULL; - if(!name || !hout || !data || - (type > (CURLH_HEADER|CURLH_TRAILER|CURLH_CONNECT|CURLH_1XX| - CURLH_PSEUDO)) || !type || (request < -1)) - return CURLHE_BAD_ARGUMENT; - if(!Curl_llist_count(&data->state.httphdrs)) - return CURLHE_NOHEADERS; /* no headers available */ - if(request > data->state.requests) - return CURLHE_NOREQUEST; - if(request == -1) - request = data->state.requests; - - /* we need a first round to count amount of this header */ - for(e = data->state.httphdrs.head; e; e = e->next) { - hs = e->ptr; - if(strcasecompare(hs->name, name) && - (hs->type & type) && - (hs->request == request)) { - amount++; - pick = hs; - e_pick = e; - } - } - if(!amount) - return CURLHE_MISSING; - else if(nameindex >= amount) - return CURLHE_BADINDEX; - - if(nameindex == amount - 1) - /* if the last or only occurrence is what's asked for, then we know it */ - hs = pick; - else { - for(e = data->state.httphdrs.head; e; e = e->next) { - hs = e->ptr; - if(strcasecompare(hs->name, name) && - (hs->type & type) && - (hs->request == request) && - (match++ == nameindex)) { - e_pick = e; - break; - } - } - if(!e) /* this shouldn't happen */ - return CURLHE_MISSING; - } - /* this is the name we want */ - copy_header_external(data, hs, nameindex, amount, e_pick, hout); - return CURLHE_OK; -} - -/* public API */ -struct curl_header *curl_easy_nextheader(CURL *easy, - unsigned int type, - int request, - struct curl_header *prev) -{ - struct Curl_easy *data = easy; - struct Curl_llist_element *pick; - struct Curl_llist_element *e; - struct Curl_header_store *hs; - struct curl_header *hout; - size_t amount = 0; - size_t index = 0; - - if(request > data->state.requests) - return NULL; - if(request == -1) - request = data->state.requests; - - if(prev) { - pick = prev->anchor; - if(!pick) - /* something is wrong */ - return NULL; - pick = pick->next; - } - else - pick = data->state.httphdrs.head; - - if(pick) { - /* make sure it is the next header of the desired type */ - do { - hs = pick->ptr; - if((hs->type & type) && (hs->request == request)) - break; - pick = pick->next; - } while(pick); - } - - if(!pick) - /* no more headers available */ - return NULL; - - hs = pick->ptr; - - /* count number of occurrences of this name within the mask and figure out - the index for the currently selected entry */ - for(e = data->state.httphdrs.head; e; e = e->next) { - struct Curl_header_store *check = e->ptr; - if(strcasecompare(hs->name, check->name) && - (check->request == request) && - (check->type & type)) - amount++; - if(e == pick) - index = amount - 1; - } - - copy_header_external(data, hs, index, amount, pick, &hout); - return hout; -} - -static CURLcode namevalue(char *header, size_t hlen, unsigned int type, - char **name, char **value) -{ - char *end = header + hlen - 1; /* point to the last byte */ - DEBUGASSERT(hlen); - *name = header; - - if(type == CURLH_PSEUDO) { - if(*header != ':') - return CURLE_BAD_FUNCTION_ARGUMENT; - header++; - } - - /* Find the end of the header name */ - while(*header && (*header != ':')) - ++header; - - if(*header) - /* Skip over colon, null it */ - *header++ = 0; - else - return CURLE_BAD_FUNCTION_ARGUMENT; - - /* skip all leading space letters */ - while(*header && ISBLANK(*header)) - header++; - - *value = header; - - /* skip all trailing space letters */ - while((end > header) && ISSPACE(*end)) - *end-- = 0; /* nul terminate */ - return CURLE_OK; -} - -static CURLcode unfold_value(struct Curl_easy *data, const char *value, - size_t vlen) /* length of the incoming header */ -{ - struct Curl_header_store *hs; - struct Curl_header_store *newhs; - size_t olen; /* length of the old value */ - size_t oalloc; /* length of the old name + value + separator */ - size_t offset; - DEBUGASSERT(data->state.prevhead); - hs = data->state.prevhead; - olen = strlen(hs->value); - offset = hs->value - hs->buffer; - oalloc = olen + offset + 1; - - /* skip all trailing space letters */ - while(vlen && ISSPACE(value[vlen - 1])) - vlen--; - - /* save only one leading space */ - while((vlen > 1) && ISBLANK(value[0]) && ISBLANK(value[1])) { - vlen--; - value++; - } - - /* since this header block might move in the realloc below, it needs to - first be unlinked from the list and then re-added again after the - realloc */ - Curl_llist_remove(&data->state.httphdrs, &hs->node, NULL); - - /* new size = struct + new value length + old name+value length */ - newhs = Curl_saferealloc(hs, sizeof(*hs) + vlen + oalloc + 1); - if(!newhs) - return CURLE_OUT_OF_MEMORY; - /* ->name' and ->value point into ->buffer (to keep the header allocation - in a single memory block), which now potentially have moved. Adjust - them. */ - newhs->name = newhs->buffer; - newhs->value = &newhs->buffer[offset]; - - /* put the data at the end of the previous data, not the newline */ - memcpy(&newhs->value[olen], value, vlen); - newhs->value[olen + vlen] = 0; /* null-terminate at newline */ - - /* insert this node into the list of headers */ - Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail, - newhs, &newhs->node); - data->state.prevhead = newhs; - return CURLE_OK; -} - - -/* - * Curl_headers_push() gets passed a full HTTP header to store. It gets called - * immediately before the header callback. The header is CRLF terminated. - */ -CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, - unsigned char type) -{ - char *value = NULL; - char *name = NULL; - char *end; - size_t hlen; /* length of the incoming header */ - struct Curl_header_store *hs; - CURLcode result = CURLE_OUT_OF_MEMORY; - - if((header[0] == '\r') || (header[0] == '\n')) - /* ignore the body separator */ - return CURLE_OK; - - end = strchr(header, '\r'); - if(!end) { - end = strchr(header, '\n'); - if(!end) - return CURLE_BAD_FUNCTION_ARGUMENT; - } - hlen = end - header + 1; - - if((header[0] == ' ') || (header[0] == '\t')) { - if(data->state.prevhead) - /* line folding, append value to the previous header's value */ - return unfold_value(data, header, hlen); - else - /* can't unfold without a previous header */ - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - hs = calloc(1, sizeof(*hs) + hlen); - if(!hs) - return CURLE_OUT_OF_MEMORY; - memcpy(hs->buffer, header, hlen); - hs->buffer[hlen] = 0; /* nul terminate */ - - result = namevalue(hs->buffer, hlen, type, &name, &value); - if(result) - goto fail; - - hs->name = name; - hs->value = value; - hs->type = type; - hs->request = data->state.requests; - - /* insert this node into the list of headers */ - Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail, - hs, &hs->node); - data->state.prevhead = hs; - return CURLE_OK; - fail: - free(hs); - return result; -} - -/* - * Curl_headers_init(). Init the headers subsystem. - */ -static void headers_init(struct Curl_easy *data) -{ - Curl_llist_init(&data->state.httphdrs, NULL); -} - -/* - * Curl_headers_cleanup(). Free all stored headers and associated memory. - */ -CURLcode Curl_headers_cleanup(struct Curl_easy *data) -{ - struct Curl_llist_element *e; - struct Curl_llist_element *n; - - for(e = data->state.httphdrs.head; e; e = n) { - struct Curl_header_store *hs = e->ptr; - n = e->next; - free(hs); - } - headers_init(data); - return CURLE_OK; -} - -#else /* HTTP-disabled builds below */ - -CURLHcode curl_easy_header(CURL *easy, - const char *name, - size_t index, - unsigned int origin, - int request, - struct curl_header **hout) -{ - (void)easy; - (void)name; - (void)index; - (void)origin; - (void)request; - (void)hout; - return CURLHE_NOT_BUILT_IN; -} - -struct curl_header *curl_easy_nextheader(CURL *easy, - unsigned int type, - int request, - struct curl_header *prev) -{ - (void)easy; - (void)type; - (void)request; - (void)prev; - return NULL; -} -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2022, 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 "strdup.h"
+#include "strcase.h"
+#include "headers.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API)
+
+/* Generate the curl_header struct for the user. This function MUST assign all
+ struct fields in the output struct. */
+static void copy_header_external(struct Curl_easy *data,
+ struct Curl_header_store *hs,
+ size_t index,
+ size_t amount,
+ struct Curl_llist_element *e,
+ struct curl_header **hout)
+{
+ struct curl_header *h = *hout = &data->state.headerout;
+ h->name = hs->name;
+ h->value = hs->value;
+ h->amount = amount;
+ h->index = index;
+ /* this will randomly OR a reserved bit for the sole purpose of making it
+ impossible for applications to do == comparisons, as that would otherwise
+ be very tempting and then lead to the reserved bits not being reserved
+ anymore. */
+ h->origin = hs->type | (1<<27);
+ h->anchor = e;
+}
+
+/* public API */
+CURLHcode curl_easy_header(CURL *easy,
+ const char *name,
+ size_t nameindex,
+ unsigned int type,
+ int request,
+ struct curl_header **hout)
+{
+ struct Curl_llist_element *e;
+ struct Curl_llist_element *e_pick = NULL;
+ struct Curl_easy *data = easy;
+ size_t match = 0;
+ size_t amount = 0;
+ struct Curl_header_store *hs = NULL;
+ struct Curl_header_store *pick = NULL;
+ if(!name || !hout || !data ||
+ (type > (CURLH_HEADER|CURLH_TRAILER|CURLH_CONNECT|CURLH_1XX|
+ CURLH_PSEUDO)) || !type || (request < -1))
+ return CURLHE_BAD_ARGUMENT;
+ if(!Curl_llist_count(&data->state.httphdrs))
+ return CURLHE_NOHEADERS; /* no headers available */
+ if(request > data->state.requests)
+ return CURLHE_NOREQUEST;
+ if(request == -1)
+ request = data->state.requests;
+
+ /* we need a first round to count amount of this header */
+ for(e = data->state.httphdrs.head; e; e = e->next) {
+ hs = e->ptr;
+ if(strcasecompare(hs->name, name) &&
+ (hs->type & type) &&
+ (hs->request == request)) {
+ amount++;
+ pick = hs;
+ e_pick = e;
+ }
+ }
+ if(!amount)
+ return CURLHE_MISSING;
+ else if(nameindex >= amount)
+ return CURLHE_BADINDEX;
+
+ if(nameindex == amount - 1)
+ /* if the last or only occurrence is what's asked for, then we know it */
+ hs = pick;
+ else {
+ for(e = data->state.httphdrs.head; e; e = e->next) {
+ hs = e->ptr;
+ if(strcasecompare(hs->name, name) &&
+ (hs->type & type) &&
+ (hs->request == request) &&
+ (match++ == nameindex)) {
+ e_pick = e;
+ break;
+ }
+ }
+ if(!e) /* this shouldn't happen */
+ return CURLHE_MISSING;
+ }
+ /* this is the name we want */
+ copy_header_external(data, hs, nameindex, amount, e_pick, hout);
+ return CURLHE_OK;
+}
+
+/* public API */
+struct curl_header *curl_easy_nextheader(CURL *easy,
+ unsigned int type,
+ int request,
+ struct curl_header *prev)
+{
+ struct Curl_easy *data = easy;
+ struct Curl_llist_element *pick;
+ struct Curl_llist_element *e;
+ struct Curl_header_store *hs;
+ struct curl_header *hout;
+ size_t amount = 0;
+ size_t index = 0;
+
+ if(request > data->state.requests)
+ return NULL;
+ if(request == -1)
+ request = data->state.requests;
+
+ if(prev) {
+ pick = prev->anchor;
+ if(!pick)
+ /* something is wrong */
+ return NULL;
+ pick = pick->next;
+ }
+ else
+ pick = data->state.httphdrs.head;
+
+ if(pick) {
+ /* make sure it is the next header of the desired type */
+ do {
+ hs = pick->ptr;
+ if((hs->type & type) && (hs->request == request))
+ break;
+ pick = pick->next;
+ } while(pick);
+ }
+
+ if(!pick)
+ /* no more headers available */
+ return NULL;
+
+ hs = pick->ptr;
+
+ /* count number of occurrences of this name within the mask and figure out
+ the index for the currently selected entry */
+ for(e = data->state.httphdrs.head; e; e = e->next) {
+ struct Curl_header_store *check = e->ptr;
+ if(strcasecompare(hs->name, check->name) &&
+ (check->request == request) &&
+ (check->type & type))
+ amount++;
+ if(e == pick)
+ index = amount - 1;
+ }
+
+ copy_header_external(data, hs, index, amount, pick, &hout);
+ return hout;
+}
+
+static CURLcode namevalue(char *header, size_t hlen, unsigned int type,
+ char **name, char **value)
+{
+ char *end = header + hlen - 1; /* point to the last byte */
+ DEBUGASSERT(hlen);
+ *name = header;
+
+ if(type == CURLH_PSEUDO) {
+ if(*header != ':')
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ header++;
+ }
+
+ /* Find the end of the header name */
+ while(*header && (*header != ':'))
+ ++header;
+
+ if(*header)
+ /* Skip over colon, null it */
+ *header++ = 0;
+ else
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ /* skip all leading space letters */
+ while(*header && ISBLANK(*header))
+ header++;
+
+ *value = header;
+
+ /* skip all trailing space letters */
+ while((end > header) && ISSPACE(*end))
+ *end-- = 0; /* nul terminate */
+ return CURLE_OK;
+}
+
+static CURLcode unfold_value(struct Curl_easy *data, const char *value,
+ size_t vlen) /* length of the incoming header */
+{
+ struct Curl_header_store *hs;
+ struct Curl_header_store *newhs;
+ size_t olen; /* length of the old value */
+ size_t oalloc; /* length of the old name + value + separator */
+ size_t offset;
+ DEBUGASSERT(data->state.prevhead);
+ hs = data->state.prevhead;
+ olen = strlen(hs->value);
+ offset = hs->value - hs->buffer;
+ oalloc = olen + offset + 1;
+
+ /* skip all trailing space letters */
+ while(vlen && ISSPACE(value[vlen - 1]))
+ vlen--;
+
+ /* save only one leading space */
+ while((vlen > 1) && ISBLANK(value[0]) && ISBLANK(value[1])) {
+ vlen--;
+ value++;
+ }
+
+ /* since this header block might move in the realloc below, it needs to
+ first be unlinked from the list and then re-added again after the
+ realloc */
+ Curl_llist_remove(&data->state.httphdrs, &hs->node, NULL);
+
+ /* new size = struct + new value length + old name+value length */
+ newhs = Curl_saferealloc(hs, sizeof(*hs) + vlen + oalloc + 1);
+ if(!newhs)
+ return CURLE_OUT_OF_MEMORY;
+ /* ->name' and ->value point into ->buffer (to keep the header allocation
+ in a single memory block), which now potentially have moved. Adjust
+ them. */
+ newhs->name = newhs->buffer;
+ newhs->value = &newhs->buffer[offset];
+
+ /* put the data at the end of the previous data, not the newline */
+ memcpy(&newhs->value[olen], value, vlen);
+ newhs->value[olen + vlen] = 0; /* null-terminate at newline */
+
+ /* insert this node into the list of headers */
+ Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail,
+ newhs, &newhs->node);
+ data->state.prevhead = newhs;
+ return CURLE_OK;
+}
+
+
+/*
+ * Curl_headers_push() gets passed a full HTTP header to store. It gets called
+ * immediately before the header callback. The header is CRLF terminated.
+ */
+CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
+ unsigned char type)
+{
+ char *value = NULL;
+ char *name = NULL;
+ char *end;
+ size_t hlen; /* length of the incoming header */
+ struct Curl_header_store *hs;
+ CURLcode result = CURLE_OUT_OF_MEMORY;
+
+ if((header[0] == '\r') || (header[0] == '\n'))
+ /* ignore the body separator */
+ return CURLE_OK;
+
+ end = strchr(header, '\r');
+ if(!end) {
+ end = strchr(header, '\n');
+ if(!end)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+ hlen = end - header + 1;
+
+ if((header[0] == ' ') || (header[0] == '\t')) {
+ if(data->state.prevhead)
+ /* line folding, append value to the previous header's value */
+ return unfold_value(data, header, hlen);
+ else
+ /* can't unfold without a previous header */
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+
+ hs = calloc(1, sizeof(*hs) + hlen);
+ if(!hs)
+ return CURLE_OUT_OF_MEMORY;
+ memcpy(hs->buffer, header, hlen);
+ hs->buffer[hlen] = 0; /* nul terminate */
+
+ result = namevalue(hs->buffer, hlen, type, &name, &value);
+ if(result)
+ goto fail;
+
+ hs->name = name;
+ hs->value = value;
+ hs->type = type;
+ hs->request = data->state.requests;
+
+ /* insert this node into the list of headers */
+ Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail,
+ hs, &hs->node);
+ data->state.prevhead = hs;
+ return CURLE_OK;
+ fail:
+ free(hs);
+ return result;
+}
+
+/*
+ * Curl_headers_init(). Init the headers subsystem.
+ */
+static void headers_init(struct Curl_easy *data)
+{
+ Curl_llist_init(&data->state.httphdrs, NULL);
+}
+
+/*
+ * Curl_headers_cleanup(). Free all stored headers and associated memory.
+ */
+CURLcode Curl_headers_cleanup(struct Curl_easy *data)
+{
+ struct Curl_llist_element *e;
+ struct Curl_llist_element *n;
+
+ for(e = data->state.httphdrs.head; e; e = n) {
+ struct Curl_header_store *hs = e->ptr;
+ n = e->next;
+ free(hs);
+ }
+ headers_init(data);
+ return CURLE_OK;
+}
+
+#else /* HTTP-disabled builds below */
+
+CURLHcode curl_easy_header(CURL *easy,
+ const char *name,
+ size_t index,
+ unsigned int origin,
+ int request,
+ struct curl_header **hout)
+{
+ (void)easy;
+ (void)name;
+ (void)index;
+ (void)origin;
+ (void)request;
+ (void)hout;
+ return CURLHE_NOT_BUILT_IN;
+}
+
+struct curl_header *curl_easy_nextheader(CURL *easy,
+ unsigned int type,
+ int request,
+ struct curl_header *prev)
+{
+ (void)easy;
+ (void)type;
+ (void)request;
+ (void)prev;
+ return NULL;
+}
+#endif
diff --git a/libs/libcurl/src/headers.h b/libs/libcurl/src/headers.h index 96332dbd0b..e839d91f8c 100644 --- a/libs/libcurl/src/headers.h +++ b/libs/libcurl/src/headers.h @@ -1,55 +1,55 @@ -#ifndef HEADER_CURL_HEADER_H -#define HEADER_CURL_HEADER_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_HEADERS_API) - -struct Curl_header_store { - struct Curl_llist_element node; - char *name; /* points into 'buffer' */ - char *value; /* points into 'buffer */ - int request; /* 0 is the first request, then 1.. 2.. */ - unsigned char type; /* CURLH_* defines */ - char buffer[1]; /* this is the raw header blob */ -}; - -/* - * Curl_headers_push() gets passed a full header to store. - */ -CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, - unsigned char type); - -/* - * Curl_headers_cleanup(). Free all stored headers and associated memory. - */ -CURLcode Curl_headers_cleanup(struct Curl_easy *data); - -#else -#define Curl_headers_push(x,y,z) CURLE_OK -#define Curl_headers_cleanup(x) Curl_nop_stmt -#endif - -#endif /* HEADER_CURL_HEADER_H */ +#ifndef HEADER_CURL_HEADER_H
+#define HEADER_CURL_HEADER_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_HEADERS_API)
+
+struct Curl_header_store {
+ struct Curl_llist_element node;
+ char *name; /* points into 'buffer' */
+ char *value; /* points into 'buffer */
+ int request; /* 0 is the first request, then 1.. 2.. */
+ unsigned char type; /* CURLH_* defines */
+ char buffer[1]; /* this is the raw header blob */
+};
+
+/*
+ * Curl_headers_push() gets passed a full header to store.
+ */
+CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
+ unsigned char type);
+
+/*
+ * Curl_headers_cleanup(). Free all stored headers and associated memory.
+ */
+CURLcode Curl_headers_cleanup(struct Curl_easy *data);
+
+#else
+#define Curl_headers_push(x,y,z) CURLE_OK
+#define Curl_headers_cleanup(x) Curl_nop_stmt
+#endif
+
+#endif /* HEADER_CURL_HEADER_H */
diff --git a/libs/libcurl/src/hmac.c b/libs/libcurl/src/hmac.c index dfb0db5757..b0111bb59e 100644 --- a/libs/libcurl/src/hmac.c +++ b/libs/libcurl/src/hmac.c @@ -1,172 +1,172 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - * RFC2104 Keyed-Hashing for Message Authentication - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifndef CURL_DISABLE_CRYPTO_AUTH - -#include <curl/curl.h> - -#include "curl_hmac.h" -#include "curl_memory.h" -#include "warnless.h" - -/* The last #include file should be: */ -#include "memdebug.h" - -/* - * Generic HMAC algorithm. - * - * This module computes HMAC digests based on any hash function. Parameters - * and computing procedures are set-up dynamically at HMAC computation context - * initialization. - */ - -static const unsigned char hmac_ipad = 0x36; -static const unsigned char hmac_opad = 0x5C; - - - -struct HMAC_context * -Curl_HMAC_init(const struct HMAC_params *hashparams, - const unsigned char *key, - unsigned int keylen) -{ - size_t i; - struct HMAC_context *ctxt; - unsigned char *hkey; - unsigned char b; - - /* Create HMAC context. */ - i = sizeof(*ctxt) + 2 * hashparams->hmac_ctxtsize + - hashparams->hmac_resultlen; - ctxt = malloc(i); - - if(!ctxt) - return ctxt; - - ctxt->hmac_hash = hashparams; - ctxt->hmac_hashctxt1 = (void *) (ctxt + 1); - ctxt->hmac_hashctxt2 = (void *) ((char *) ctxt->hmac_hashctxt1 + - hashparams->hmac_ctxtsize); - - /* If the key is too long, replace it by its hash digest. */ - if(keylen > hashparams->hmac_maxkeylen) { - (*hashparams->hmac_hinit)(ctxt->hmac_hashctxt1); - (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt1, key, keylen); - hkey = (unsigned char *) ctxt->hmac_hashctxt2 + hashparams->hmac_ctxtsize; - (*hashparams->hmac_hfinal)(hkey, ctxt->hmac_hashctxt1); - key = hkey; - keylen = hashparams->hmac_resultlen; - } - - /* Prime the two hash contexts with the modified key. */ - (*hashparams->hmac_hinit)(ctxt->hmac_hashctxt1); - (*hashparams->hmac_hinit)(ctxt->hmac_hashctxt2); - - for(i = 0; i < keylen; i++) { - b = (unsigned char)(*key ^ hmac_ipad); - (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt1, &b, 1); - b = (unsigned char)(*key++ ^ hmac_opad); - (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt2, &b, 1); - } - - for(; i < hashparams->hmac_maxkeylen; i++) { - (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt1, &hmac_ipad, 1); - (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt2, &hmac_opad, 1); - } - - /* Done, return pointer to HMAC context. */ - return ctxt; -} - -int Curl_HMAC_update(struct HMAC_context *ctxt, - const unsigned char *data, - unsigned int len) -{ - /* Update first hash calculation. */ - (*ctxt->hmac_hash->hmac_hupdate)(ctxt->hmac_hashctxt1, data, len); - return 0; -} - - -int Curl_HMAC_final(struct HMAC_context *ctxt, unsigned char *result) -{ - const struct HMAC_params *hashparams = ctxt->hmac_hash; - - /* Do not get result if called with a null parameter: only release - storage. */ - - if(!result) - result = (unsigned char *) ctxt->hmac_hashctxt2 + - ctxt->hmac_hash->hmac_ctxtsize; - - (*hashparams->hmac_hfinal)(result, ctxt->hmac_hashctxt1); - (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt2, - result, hashparams->hmac_resultlen); - (*hashparams->hmac_hfinal)(result, ctxt->hmac_hashctxt2); - free((char *) ctxt); - return 0; -} - -/* - * Curl_hmacit() - * - * This is used to generate a HMAC hash, for the specified input data, given - * the specified hash function and key. - * - * Parameters: - * - * hashparams [in] - The hash function (Curl_HMAC_MD5). - * key [in] - The key to use. - * keylen [in] - The length of the key. - * data [in] - The data to encrypt. - * datalen [in] - The length of the data. - * output [in/out] - The output buffer. - * - * Returns CURLE_OK on success. - */ -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) -{ - struct HMAC_context *ctxt = - Curl_HMAC_init(hashparams, key, curlx_uztoui(keylen)); - - if(!ctxt) - return CURLE_OUT_OF_MEMORY; - - /* Update the digest with the given challenge */ - Curl_HMAC_update(ctxt, data, curlx_uztoui(datalen)); - - /* Finalise the digest */ - Curl_HMAC_final(ctxt, output); - - return CURLE_OK; -} - -#endif /* CURL_DISABLE_CRYPTO_AUTH */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ * RFC2104 Keyed-Hashing for Message Authentication
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+
+#include <curl/curl.h>
+
+#include "curl_hmac.h"
+#include "curl_memory.h"
+#include "warnless.h"
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/*
+ * Generic HMAC algorithm.
+ *
+ * This module computes HMAC digests based on any hash function. Parameters
+ * and computing procedures are set-up dynamically at HMAC computation context
+ * initialization.
+ */
+
+static const unsigned char hmac_ipad = 0x36;
+static const unsigned char hmac_opad = 0x5C;
+
+
+
+struct HMAC_context *
+Curl_HMAC_init(const struct HMAC_params *hashparams,
+ const unsigned char *key,
+ unsigned int keylen)
+{
+ size_t i;
+ struct HMAC_context *ctxt;
+ unsigned char *hkey;
+ unsigned char b;
+
+ /* Create HMAC context. */
+ i = sizeof(*ctxt) + 2 * hashparams->hmac_ctxtsize +
+ hashparams->hmac_resultlen;
+ ctxt = malloc(i);
+
+ if(!ctxt)
+ return ctxt;
+
+ ctxt->hmac_hash = hashparams;
+ ctxt->hmac_hashctxt1 = (void *) (ctxt + 1);
+ ctxt->hmac_hashctxt2 = (void *) ((char *) ctxt->hmac_hashctxt1 +
+ hashparams->hmac_ctxtsize);
+
+ /* If the key is too long, replace it by its hash digest. */
+ if(keylen > hashparams->hmac_maxkeylen) {
+ (*hashparams->hmac_hinit)(ctxt->hmac_hashctxt1);
+ (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt1, key, keylen);
+ hkey = (unsigned char *) ctxt->hmac_hashctxt2 + hashparams->hmac_ctxtsize;
+ (*hashparams->hmac_hfinal)(hkey, ctxt->hmac_hashctxt1);
+ key = hkey;
+ keylen = hashparams->hmac_resultlen;
+ }
+
+ /* Prime the two hash contexts with the modified key. */
+ (*hashparams->hmac_hinit)(ctxt->hmac_hashctxt1);
+ (*hashparams->hmac_hinit)(ctxt->hmac_hashctxt2);
+
+ for(i = 0; i < keylen; i++) {
+ b = (unsigned char)(*key ^ hmac_ipad);
+ (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt1, &b, 1);
+ b = (unsigned char)(*key++ ^ hmac_opad);
+ (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt2, &b, 1);
+ }
+
+ for(; i < hashparams->hmac_maxkeylen; i++) {
+ (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt1, &hmac_ipad, 1);
+ (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt2, &hmac_opad, 1);
+ }
+
+ /* Done, return pointer to HMAC context. */
+ return ctxt;
+}
+
+int Curl_HMAC_update(struct HMAC_context *ctxt,
+ const unsigned char *data,
+ unsigned int len)
+{
+ /* Update first hash calculation. */
+ (*ctxt->hmac_hash->hmac_hupdate)(ctxt->hmac_hashctxt1, data, len);
+ return 0;
+}
+
+
+int Curl_HMAC_final(struct HMAC_context *ctxt, unsigned char *result)
+{
+ const struct HMAC_params *hashparams = ctxt->hmac_hash;
+
+ /* Do not get result if called with a null parameter: only release
+ storage. */
+
+ if(!result)
+ result = (unsigned char *) ctxt->hmac_hashctxt2 +
+ ctxt->hmac_hash->hmac_ctxtsize;
+
+ (*hashparams->hmac_hfinal)(result, ctxt->hmac_hashctxt1);
+ (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt2,
+ result, hashparams->hmac_resultlen);
+ (*hashparams->hmac_hfinal)(result, ctxt->hmac_hashctxt2);
+ free((char *) ctxt);
+ return 0;
+}
+
+/*
+ * Curl_hmacit()
+ *
+ * This is used to generate a HMAC hash, for the specified input data, given
+ * the specified hash function and key.
+ *
+ * Parameters:
+ *
+ * hashparams [in] - The hash function (Curl_HMAC_MD5).
+ * key [in] - The key to use.
+ * keylen [in] - The length of the key.
+ * data [in] - The data to encrypt.
+ * datalen [in] - The length of the data.
+ * output [in/out] - The output buffer.
+ *
+ * Returns CURLE_OK on success.
+ */
+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)
+{
+ struct HMAC_context *ctxt =
+ Curl_HMAC_init(hashparams, key, curlx_uztoui(keylen));
+
+ if(!ctxt)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Update the digest with the given challenge */
+ Curl_HMAC_update(ctxt, data, curlx_uztoui(datalen));
+
+ /* Finalise the digest */
+ Curl_HMAC_final(ctxt, output);
+
+ return CURLE_OK;
+}
+
+#endif /* CURL_DISABLE_CRYPTO_AUTH */
diff --git a/libs/libcurl/src/hostasyn.c b/libs/libcurl/src/hostasyn.c index 0bfbe2ef86..01a550852a 100644 --- a/libs/libcurl/src/hostasyn.c +++ b/libs/libcurl/src/hostasyn.c @@ -1,127 +1,123 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -/*********************************************************************** - * Only for builds using asynchronous name resolves - **********************************************************************/ -#ifdef CURLRES_ASYNCH - -#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 HAVE_PROCESS_H -#include <process.h> -#endif - -#include "urldata.h" -#include "sendf.h" -#include "hostip.h" -#include "hash.h" -#include "share.h" -#include "url.h" -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -/* - * Curl_addrinfo_callback() gets called by ares, gethostbyname_thread() - * or getaddrinfo_thread() when we got the name resolved (or not!). - * - * If the status argument is CURL_ASYNC_SUCCESS, this function takes - * ownership of the Curl_addrinfo passed, storing the resolved data - * in the DNS cache. - * - * The storage operation locks and unlocks the DNS cache. - */ -CURLcode Curl_addrinfo_callback(struct Curl_easy *data, - int status, - struct Curl_addrinfo *ai) -{ - struct Curl_dns_entry *dns = NULL; - CURLcode result = CURLE_OK; - - data->state.async.status = status; - - if(CURL_ASYNC_SUCCESS == status) { - if(ai) { - if(data->share) - Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); - - dns = Curl_cache_addr(data, ai, - data->state.async.hostname, - data->state.async.port); - if(data->share) - Curl_share_unlock(data, CURL_LOCK_DATA_DNS); - - if(!dns) { - /* failed to store, cleanup and return error */ - Curl_freeaddrinfo(ai); - result = CURLE_OUT_OF_MEMORY; - } - } - else { - result = CURLE_OUT_OF_MEMORY; - } - } - - data->state.async.dns = dns; - - /* Set async.done TRUE last in this function since it may be used multi- - threaded and once this is TRUE the other thread may read fields from the - async struct */ - data->state.async.done = TRUE; - - /* IPv4: The input hostent struct will be freed by ares when we return from - this function */ - return result; -} - -/* - * Curl_getaddrinfo() is the generic low-level name resolve API within this - * source file. There are several versions of this function - for different - * name resolve layers (selected at build-time). They all take this same set - * of arguments - */ -struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data, - const char *hostname, - int port, - int *waitp) -{ - return Curl_resolver_getaddrinfo(data, hostname, port, waitp); -} - -#endif /* CURLRES_ASYNCH */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+/***********************************************************************
+ * Only for builds using asynchronous name resolves
+ **********************************************************************/
+#ifdef CURLRES_ASYNCH
+
+#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 "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "url.h"
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/*
+ * Curl_addrinfo_callback() gets called by ares, gethostbyname_thread()
+ * or getaddrinfo_thread() when we got the name resolved (or not!).
+ *
+ * If the status argument is CURL_ASYNC_SUCCESS, this function takes
+ * ownership of the Curl_addrinfo passed, storing the resolved data
+ * in the DNS cache.
+ *
+ * The storage operation locks and unlocks the DNS cache.
+ */
+CURLcode Curl_addrinfo_callback(struct Curl_easy *data,
+ int status,
+ struct Curl_addrinfo *ai)
+{
+ struct Curl_dns_entry *dns = NULL;
+ CURLcode result = CURLE_OK;
+
+ data->state.async.status = status;
+
+ if(CURL_ASYNC_SUCCESS == status) {
+ if(ai) {
+ if(data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+ dns = Curl_cache_addr(data, ai,
+ data->state.async.hostname,
+ data->state.async.port);
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+
+ if(!dns) {
+ /* failed to store, cleanup and return error */
+ Curl_freeaddrinfo(ai);
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ }
+ else {
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ }
+
+ data->state.async.dns = dns;
+
+ /* Set async.done TRUE last in this function since it may be used multi-
+ threaded and once this is TRUE the other thread may read fields from the
+ async struct */
+ data->state.async.done = TRUE;
+
+ /* IPv4: The input hostent struct will be freed by ares when we return from
+ this function */
+ return result;
+}
+
+/*
+ * Curl_getaddrinfo() is the generic low-level name resolve API within this
+ * source file. There are several versions of this function - for different
+ * name resolve layers (selected at build-time). They all take this same set
+ * of arguments
+ */
+struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int *waitp)
+{
+ return Curl_resolver_getaddrinfo(data, hostname, port, waitp);
+}
+
+#endif /* CURLRES_ASYNCH */
diff --git a/libs/libcurl/src/hostip.c b/libs/libcurl/src/hostip.c index 941ecacf4a..3ff2e12e42 100644 --- a/libs/libcurl/src/hostip.c +++ b/libs/libcurl/src/hostip.c @@ -1,1350 +1,1346 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_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 __VMS -#include <in.h> -#include <inet.h> -#endif - -#ifdef HAVE_SETJMP_H -#include <setjmp.h> -#endif -#ifdef HAVE_SIGNAL_H -#include <signal.h> -#endif - -#ifdef HAVE_PROCESS_H -#include <process.h> -#endif - -#include "urldata.h" -#include "sendf.h" -#include "hostip.h" -#include "hash.h" -#include "rand.h" -#include "share.h" -#include "url.h" -#include "inet_ntop.h" -#include "inet_pton.h" -#include "multiif.h" -#include "doh.h" -#include "warnless.h" -#include "strcase.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES) -#include <SystemConfiguration/SCDynamicStoreCopySpecific.h> -#endif - -#if defined(CURLRES_SYNCH) && \ - defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP) -/* alarm-based timeouts can only be used with all the dependencies satisfied */ -#define USE_ALARM_TIMEOUT -#endif - -#define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */ - -/* - * hostip.c explained - * ================== - * - * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c - * source file are these: - * - * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use - * that. The host may not be able to resolve IPv6, but we don't really have to - * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4 - * defined. - * - * CURLRES_ARES - is defined if libcurl is built to use c-ares for - * asynchronous name resolves. This can be Windows or *nix. - * - * CURLRES_THREADED - is defined if libcurl is built to run under (native) - * Windows, and then the name resolve will be done in a new thread, and the - * supported API will be the same as for ares-builds. - * - * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If - * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is - * defined. - * - * The host*.c sources files are split up like this: - * - * hostip.c - method-independent resolver functions and utility functions - * hostasyn.c - functions for asynchronous name resolves - * hostsyn.c - functions for synchronous name resolves - * hostip4.c - IPv4 specific functions - * hostip6.c - IPv6 specific functions - * - * The two asynchronous name resolver backends are implemented in: - * asyn-ares.c - functions for ares-using name resolves - * asyn-thread.c - functions for threaded name resolves - - * The hostip.h is the united header file for all this. It defines the - * CURLRES_* defines based on the config*.h and curl_setup.h defines. - */ - -static void freednsentry(void *freethis); - -/* - * Return # of addresses in a Curl_addrinfo struct - */ -int Curl_num_addresses(const struct Curl_addrinfo *addr) -{ - int i = 0; - while(addr) { - addr = addr->ai_next; - i++; - } - return i; -} - -/* - * Curl_printable_address() stores a printable version of the 1st address - * given in the 'ai' argument. The result will be stored in the buf that is - * bufsize bytes big. - * - * If the conversion fails, the target buffer is empty. - */ -void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf, - size_t bufsize) -{ - DEBUGASSERT(bufsize); - buf[0] = 0; - - switch(ai->ai_family) { - case AF_INET: { - const struct sockaddr_in *sa4 = (const void *)ai->ai_addr; - const struct in_addr *ipaddr4 = &sa4->sin_addr; - (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, bufsize); - break; - } -#ifdef ENABLE_IPV6 - case AF_INET6: { - const struct sockaddr_in6 *sa6 = (const void *)ai->ai_addr; - const struct in6_addr *ipaddr6 = &sa6->sin6_addr; - (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf, bufsize); - break; - } -#endif - default: - break; - } -} - -/* - * Create a hostcache id string for the provided host + port, to be used by - * the DNS caching. Without alloc. - */ -static void -create_hostcache_id(const char *name, int port, char *ptr, size_t buflen) -{ - size_t len = strlen(name); - if(len > (buflen - 7)) - len = buflen - 7; - /* store and lower case the name */ - while(len--) - *ptr++ = Curl_raw_tolower(*name++); - msnprintf(ptr, 7, ":%u", port); -} - -struct hostcache_prune_data { - long cache_timeout; - time_t now; -}; - -/* - * This function is set as a callback to be called for every entry in the DNS - * cache when we want to prune old unused entries. - * - * Returning non-zero means remove the entry, return 0 to keep it in the - * cache. - */ -static int -hostcache_timestamp_remove(void *datap, void *hc) -{ - struct hostcache_prune_data *data = - (struct hostcache_prune_data *) datap; - struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc; - - return (0 != c->timestamp) - && (data->now - c->timestamp >= data->cache_timeout); -} - -/* - * Prune the DNS cache. This assumes that a lock has already been taken. - */ -static void -hostcache_prune(struct Curl_hash *hostcache, long cache_timeout, time_t now) -{ - struct hostcache_prune_data user; - - user.cache_timeout = cache_timeout; - user.now = now; - - Curl_hash_clean_with_criterium(hostcache, - (void *) &user, - hostcache_timestamp_remove); -} - -/* - * Library-wide function for pruning the DNS cache. This function takes and - * returns the appropriate locks. - */ -void Curl_hostcache_prune(struct Curl_easy *data) -{ - time_t now; - - if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache) - /* cache forever means never prune, and NULL hostcache means - we can't do it */ - return; - - if(data->share) - Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); - - time(&now); - - /* Remove outdated and unused entries from the hostcache */ - hostcache_prune(data->dns.hostcache, - data->set.dns_cache_timeout, - now); - - if(data->share) - Curl_share_unlock(data, CURL_LOCK_DATA_DNS); -} - -#ifdef HAVE_SIGSETJMP -/* Beware this is a global and unique instance. This is used to store the - return address that we can jump back to from inside a signal handler. This - is not thread-safe stuff. */ -sigjmp_buf curl_jmpenv; -#endif - -/* lookup address, returns entry if found and not stale */ -static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data, - const char *hostname, - int port) -{ - struct Curl_dns_entry *dns = NULL; - size_t entry_len; - char entry_id[MAX_HOSTCACHE_LEN]; - - /* Create an entry id, based upon the hostname and port */ - create_hostcache_id(hostname, port, entry_id, sizeof(entry_id)); - entry_len = strlen(entry_id); - - /* See if its already in our dns cache */ - dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); - - /* No entry found in cache, check if we might have a wildcard entry */ - if(!dns && data->state.wildcard_resolve) { - create_hostcache_id("*", port, entry_id, sizeof(entry_id)); - entry_len = strlen(entry_id); - - /* See if it's already in our dns cache */ - dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); - } - - if(dns && (data->set.dns_cache_timeout != -1)) { - /* See whether the returned entry is stale. Done before we release lock */ - struct hostcache_prune_data user; - - time(&user.now); - user.cache_timeout = data->set.dns_cache_timeout; - - if(hostcache_timestamp_remove(&user, dns)) { - infof(data, "Hostname in DNS cache was stale, zapped"); - dns = NULL; /* the memory deallocation is being handled by the hash */ - Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1); - } - } - - /* See if the returned entry matches the required resolve mode */ - if(dns && data->conn->ip_version != CURL_IPRESOLVE_WHATEVER) { - int pf = PF_INET; - bool found = false; - struct Curl_addrinfo *addr = dns->addr; - -#ifdef PF_INET6 - if(data->conn->ip_version == CURL_IPRESOLVE_V6) - pf = PF_INET6; -#endif - - while(addr) { - if(addr->ai_family == pf) { - found = true; - break; - } - addr = addr->ai_next; - } - - if(!found) { - infof(data, "Hostname in DNS cache doesn't have needed family, zapped"); - dns = NULL; /* the memory deallocation is being handled by the hash */ - Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1); - } - } - return dns; -} - -/* - * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache. - * - * Curl_resolv() checks initially and multi_runsingle() checks each time - * it discovers the handle in the state WAITRESOLVE whether the hostname - * has already been resolved and the address has already been stored in - * the DNS cache. This short circuits waiting for a lot of pending - * lookups for the same hostname requested by different handles. - * - * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. - * - * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after - * use, or we'll leak memory! - */ -struct Curl_dns_entry * -Curl_fetch_addr(struct Curl_easy *data, - const char *hostname, - int port) -{ - struct Curl_dns_entry *dns = NULL; - - if(data->share) - Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); - - dns = fetch_addr(data, hostname, port); - - if(dns) - dns->inuse++; /* we use it! */ - - if(data->share) - Curl_share_unlock(data, CURL_LOCK_DATA_DNS); - - return dns; -} - -#ifndef CURL_DISABLE_SHUFFLE_DNS -UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, - struct Curl_addrinfo **addr); -/* - * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo' - * struct by re-linking its linked list. - * - * The addr argument should be the address of a pointer to the head node of a - * `Curl_addrinfo` list and it will be modified to point to the new head after - * shuffling. - * - * Not declared static only to make it easy to use in a unit test! - * - * @unittest: 1608 - */ -UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, - struct Curl_addrinfo **addr) -{ - CURLcode result = CURLE_OK; - const int num_addrs = Curl_num_addresses(*addr); - - if(num_addrs > 1) { - struct Curl_addrinfo **nodes; - infof(data, "Shuffling %i addresses", num_addrs); - - nodes = malloc(num_addrs*sizeof(*nodes)); - if(nodes) { - int i; - unsigned int *rnd; - const size_t rnd_size = num_addrs * sizeof(*rnd); - - /* build a plain array of Curl_addrinfo pointers */ - nodes[0] = *addr; - for(i = 1; i < num_addrs; i++) { - nodes[i] = nodes[i-1]->ai_next; - } - - rnd = malloc(rnd_size); - if(rnd) { - /* Fisher-Yates shuffle */ - if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) { - struct Curl_addrinfo *swap_tmp; - for(i = num_addrs - 1; i > 0; i--) { - swap_tmp = nodes[rnd[i] % (i + 1)]; - nodes[rnd[i] % (i + 1)] = nodes[i]; - nodes[i] = swap_tmp; - } - - /* relink list in the new order */ - for(i = 1; i < num_addrs; i++) { - nodes[i-1]->ai_next = nodes[i]; - } - - nodes[num_addrs-1]->ai_next = NULL; - *addr = nodes[0]; - } - free(rnd); - } - else - result = CURLE_OUT_OF_MEMORY; - free(nodes); - } - else - result = CURLE_OUT_OF_MEMORY; - } - return result; -} -#endif - -/* - * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache. - * - * When calling Curl_resolv() has resulted in a response with a returned - * address, we call this function to store the information in the dns - * cache etc - * - * Returns the Curl_dns_entry entry pointer or NULL if the storage failed. - */ -struct Curl_dns_entry * -Curl_cache_addr(struct Curl_easy *data, - struct Curl_addrinfo *addr, - const char *hostname, - int port) -{ - char entry_id[MAX_HOSTCACHE_LEN]; - size_t entry_len; - struct Curl_dns_entry *dns; - struct Curl_dns_entry *dns2; - -#ifndef CURL_DISABLE_SHUFFLE_DNS - /* shuffle addresses if requested */ - if(data->set.dns_shuffle_addresses) { - CURLcode result = Curl_shuffle_addr(data, &addr); - if(result) - return NULL; - } -#endif - - /* Create a new cache entry */ - dns = calloc(1, sizeof(struct Curl_dns_entry)); - if(!dns) { - return NULL; - } - - /* Create an entry id, based upon the hostname and port */ - create_hostcache_id(hostname, port, entry_id, sizeof(entry_id)); - entry_len = strlen(entry_id); - - dns->inuse = 1; /* the cache has the first reference */ - dns->addr = addr; /* this is the address(es) */ - time(&dns->timestamp); - if(dns->timestamp == 0) - dns->timestamp = 1; /* zero indicates permanent CURLOPT_RESOLVE entry */ - - /* Store the resolved data in our DNS cache. */ - dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1, - (void *)dns); - if(!dns2) { - free(dns); - return NULL; - } - - dns = dns2; - dns->inuse++; /* mark entry as in-use */ - return dns; -} - -#ifdef ENABLE_IPV6 -/* return a static IPv6 ::1 for the name */ -static struct Curl_addrinfo *get_localhost6(int port, const char *name) -{ - struct Curl_addrinfo *ca; - const size_t ss_size = sizeof(struct sockaddr_in6); - const size_t hostlen = strlen(name); - struct sockaddr_in6 sa6; - unsigned char ipv6[16]; - unsigned short port16 = (unsigned short)(port & 0xffff); - ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1); - if(!ca) - return NULL; - - sa6.sin6_family = AF_INET6; - sa6.sin6_port = htons(port16); - sa6.sin6_flowinfo = 0; - sa6.sin6_scope_id = 0; - if(Curl_inet_pton(AF_INET6, "::1", ipv6) < 1) - return NULL; - memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6)); - - ca->ai_flags = 0; - ca->ai_family = AF_INET6; - ca->ai_socktype = SOCK_STREAM; - ca->ai_protocol = IPPROTO_TCP; - ca->ai_addrlen = (curl_socklen_t)ss_size; - ca->ai_next = NULL; - ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo)); - memcpy(ca->ai_addr, &sa6, ss_size); - ca->ai_canonname = (char *)ca->ai_addr + ss_size; - strcpy(ca->ai_canonname, name); - return ca; -} -#else -#define get_localhost6(x,y) NULL -#endif - -/* return a static IPv4 127.0.0.1 for the given name */ -static struct Curl_addrinfo *get_localhost(int port, const char *name) -{ - struct Curl_addrinfo *ca; - const size_t ss_size = sizeof(struct sockaddr_in); - const size_t hostlen = strlen(name); - struct sockaddr_in sa; - unsigned int ipv4; - unsigned short port16 = (unsigned short)(port & 0xffff); - - /* memset to clear the sa.sin_zero field */ - memset(&sa, 0, sizeof(sa)); - sa.sin_family = AF_INET; - sa.sin_port = htons(port16); - if(Curl_inet_pton(AF_INET, "127.0.0.1", (char *)&ipv4) < 1) - return NULL; - memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4)); - - ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1); - if(!ca) - return NULL; - ca->ai_flags = 0; - ca->ai_family = AF_INET; - ca->ai_socktype = SOCK_STREAM; - ca->ai_protocol = IPPROTO_TCP; - ca->ai_addrlen = (curl_socklen_t)ss_size; - ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo)); - memcpy(ca->ai_addr, &sa, ss_size); - ca->ai_canonname = (char *)ca->ai_addr + ss_size; - strcpy(ca->ai_canonname, name); - ca->ai_next = get_localhost6(port, name); - return ca; -} - -#ifdef ENABLE_IPV6 -/* - * Curl_ipv6works() returns TRUE if IPv6 seems to work. - */ -bool Curl_ipv6works(struct Curl_easy *data) -{ - if(data) { - /* the nature of most system is that IPv6 status doesn't come and go - during a program's lifetime so we only probe the first time and then we - have the info kept for fast re-use */ - DEBUGASSERT(data); - DEBUGASSERT(data->multi); - if(data->multi->ipv6_up == IPV6_UNKNOWN) { - bool works = Curl_ipv6works(NULL); - data->multi->ipv6_up = works ? IPV6_WORKS : IPV6_DEAD; - } - return data->multi->ipv6_up == IPV6_WORKS; - } - else { - int ipv6_works = -1; - /* probe to see if we have a working IPv6 stack */ - curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0); - if(s == CURL_SOCKET_BAD) - /* an IPv6 address was requested but we can't get/use one */ - ipv6_works = 0; - else { - ipv6_works = 1; - sclose(s); - } - return (ipv6_works>0)?TRUE:FALSE; - } -} -#endif /* ENABLE_IPV6 */ - -/* - * Curl_host_is_ipnum() returns TRUE if the given string is a numerical IPv4 - * (or IPv6 if supported) address. - */ -bool Curl_host_is_ipnum(const char *hostname) -{ - struct in_addr in; -#ifdef ENABLE_IPV6 - struct in6_addr in6; -#endif - if(Curl_inet_pton(AF_INET, hostname, &in) > 0 -#ifdef ENABLE_IPV6 - || Curl_inet_pton(AF_INET6, hostname, &in6) > 0 -#endif - ) - return TRUE; - return FALSE; -} - - -/* return TRUE if 'part' is a case insensitive tail of 'full' */ -static bool tailmatch(const char *full, const char *part) -{ - size_t plen = strlen(part); - size_t flen = strlen(full); - if(plen > flen) - return FALSE; - return strncasecompare(part, &full[flen - plen], plen); -} - -/* - * Curl_resolv() is the main name resolve function within libcurl. It resolves - * a name and returns a pointer to the entry in the 'entry' argument (if one - * is provided). This function might return immediately if we're using asynch - * resolves. See the return codes. - * - * The cache entry we return will get its 'inuse' counter increased when this - * function is used. You MUST call Curl_resolv_unlock() later (when you're - * done using this struct) to decrease the counter again. - * - * Return codes: - * - * CURLRESOLV_ERROR (-1) = error, no pointer - * CURLRESOLV_RESOLVED (0) = OK, pointer provided - * CURLRESOLV_PENDING (1) = waiting for response, no pointer - */ - -enum resolve_t Curl_resolv(struct Curl_easy *data, - const char *hostname, - int port, - bool allowDOH, - struct Curl_dns_entry **entry) -{ - struct Curl_dns_entry *dns = NULL; - CURLcode result; - enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */ - struct connectdata *conn = data->conn; - *entry = NULL; -#ifndef CURL_DISABLE_DOH - conn->bits.doh = FALSE; /* default is not */ -#else - (void)allowDOH; -#endif - - if(data->share) - Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); - - dns = fetch_addr(data, hostname, port); - - if(dns) { - infof(data, "Hostname %s was found in DNS cache", hostname); - dns->inuse++; /* we use it! */ - rc = CURLRESOLV_RESOLVED; - } - - if(data->share) - Curl_share_unlock(data, CURL_LOCK_DATA_DNS); - - if(!dns) { - /* The entry was not in the cache. Resolve it to IP address */ - - struct Curl_addrinfo *addr = NULL; - int respwait = 0; -#if !defined(CURL_DISABLE_DOH) || !defined(USE_RESOLVE_ON_IPS) - struct in_addr in; -#endif -#ifndef CURL_DISABLE_DOH -#ifndef USE_RESOLVE_ON_IPS - const -#endif - bool ipnum = FALSE; -#endif - - /* notify the resolver start callback */ - if(data->set.resolver_start) { - int st; - Curl_set_in_callback(data, true); - st = data->set.resolver_start( -#ifdef USE_CURL_ASYNC - data->state.async.resolver, -#else - NULL, -#endif - NULL, - data->set.resolver_start_client); - Curl_set_in_callback(data, false); - if(st) - return CURLRESOLV_ERROR; - } - -#if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES) - { - /* - * The automagic conversion from IPv4 literals to IPv6 literals only - * works if the SCDynamicStoreCopyProxies system function gets called - * first. As Curl currently doesn't support system-wide HTTP proxies, we - * therefore don't use any value this function might return. - * - * This function is only available on a macOS and is not needed for - * IPv4-only builds, hence the conditions above. - */ - CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL); - if(dict) - CFRelease(dict); - } -#endif - -#ifndef USE_RESOLVE_ON_IPS - /* First check if this is an IPv4 address string */ - if(Curl_inet_pton(AF_INET, hostname, &in) > 0) - /* This is a dotted IP address 123.123.123.123-style */ - addr = Curl_ip2addr(AF_INET, &in, hostname, port); -#ifdef ENABLE_IPV6 - if(!addr) { - struct in6_addr in6; - /* check if this is an IPv6 address string */ - if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) - /* This is an IPv6 address literal */ - addr = Curl_ip2addr(AF_INET6, &in6, hostname, port); - } -#endif /* ENABLE_IPV6 */ - -#else /* if USE_RESOLVE_ON_IPS */ -#ifndef CURL_DISABLE_DOH - /* First check if this is an IPv4 address string */ - if(Curl_inet_pton(AF_INET, hostname, &in) > 0) - /* This is a dotted IP address 123.123.123.123-style */ - ipnum = TRUE; -#ifdef ENABLE_IPV6 - else { - struct in6_addr in6; - /* check if this is an IPv6 address string */ - if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) - /* This is an IPv6 address literal */ - ipnum = TRUE; - } -#endif /* ENABLE_IPV6 */ -#endif /* CURL_DISABLE_DOH */ - -#endif /* !USE_RESOLVE_ON_IPS */ - - if(!addr) { - if(conn->ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data)) - return CURLRESOLV_ERROR; - - if(strcasecompare(hostname, "localhost") || - tailmatch(hostname, ".localhost")) - addr = get_localhost(port, hostname); -#ifndef CURL_DISABLE_DOH - else if(allowDOH && data->set.doh && !ipnum) - addr = Curl_doh(data, hostname, port, &respwait); -#endif - else { - /* Check what IP specifics the app has requested and if we can provide - * it. If not, bail out. */ - if(!Curl_ipvalid(data, conn)) - return CURLRESOLV_ERROR; - /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a - non-zero value indicating that we need to wait for the response to - the resolve call */ - addr = Curl_getaddrinfo(data, hostname, port, &respwait); - } - } - if(!addr) { - if(respwait) { - /* the response to our resolve call will come asynchronously at - a later time, good or bad */ - /* First, check that we haven't received the info by now */ - result = Curl_resolv_check(data, &dns); - if(result) /* error detected */ - return CURLRESOLV_ERROR; - if(dns) - rc = CURLRESOLV_RESOLVED; /* pointer provided */ - else - rc = CURLRESOLV_PENDING; /* no info yet */ - } - } - else { - if(data->share) - Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); - - /* we got a response, store it in the cache */ - dns = Curl_cache_addr(data, addr, hostname, port); - - if(data->share) - Curl_share_unlock(data, CURL_LOCK_DATA_DNS); - - if(!dns) - /* returned failure, bail out nicely */ - Curl_freeaddrinfo(addr); - else - rc = CURLRESOLV_RESOLVED; - } - } - - *entry = dns; - - return rc; -} - -#ifdef USE_ALARM_TIMEOUT -/* - * This signal handler jumps back into the main libcurl code and continues - * execution. This effectively causes the remainder of the application to run - * within a signal handler which is nonportable and could lead to problems. - */ -static -void alarmfunc(int sig) -{ - /* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */ - (void)sig; - siglongjmp(curl_jmpenv, 1); -} -#endif /* USE_ALARM_TIMEOUT */ - -/* - * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a - * timeout. This function might return immediately if we're using asynch - * resolves. See the return codes. - * - * The cache entry we return will get its 'inuse' counter increased when this - * function is used. You MUST call Curl_resolv_unlock() later (when you're - * done using this struct) to decrease the counter again. - * - * If built with a synchronous resolver and use of signals is not - * disabled by the application, then a nonzero timeout will cause a - * timeout after the specified number of milliseconds. Otherwise, timeout - * is ignored. - * - * Return codes: - * - * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired - * CURLRESOLV_ERROR (-1) = error, no pointer - * CURLRESOLV_RESOLVED (0) = OK, pointer provided - * CURLRESOLV_PENDING (1) = waiting for response, no pointer - */ - -enum resolve_t Curl_resolv_timeout(struct Curl_easy *data, - const char *hostname, - int port, - struct Curl_dns_entry **entry, - timediff_t timeoutms) -{ -#ifdef USE_ALARM_TIMEOUT -#ifdef HAVE_SIGACTION - struct sigaction keep_sigact; /* store the old struct here */ - volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */ - struct sigaction sigact; -#else -#ifdef HAVE_SIGNAL - void (*keep_sigact)(int); /* store the old handler here */ -#endif /* HAVE_SIGNAL */ -#endif /* HAVE_SIGACTION */ - volatile long timeout; - volatile unsigned int prev_alarm = 0; -#endif /* USE_ALARM_TIMEOUT */ - enum resolve_t rc; - - *entry = NULL; - - if(timeoutms < 0) - /* got an already expired timeout */ - return CURLRESOLV_TIMEDOUT; - -#ifdef USE_ALARM_TIMEOUT - if(data->set.no_signal) - /* Ignore the timeout when signals are disabled */ - timeout = 0; - else - timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms; - - if(!timeout) - /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */ - return Curl_resolv(data, hostname, port, TRUE, entry); - - if(timeout < 1000) { - /* The alarm() function only provides integer second resolution, so if - we want to wait less than one second we must bail out already now. */ - failf(data, - "remaining timeout of %ld too small to resolve via SIGALRM method", - timeout); - return CURLRESOLV_TIMEDOUT; - } - /* This allows us to time-out from the name resolver, as the timeout - will generate a signal and we will siglongjmp() from that here. - This technique has problems (see alarmfunc). - This should be the last thing we do before calling Curl_resolv(), - as otherwise we'd have to worry about variables that get modified - before we invoke Curl_resolv() (and thus use "volatile"). */ - if(sigsetjmp(curl_jmpenv, 1)) { - /* this is coming from a siglongjmp() after an alarm signal */ - failf(data, "name lookup timed out"); - rc = CURLRESOLV_ERROR; - goto clean_up; - } - else { - /************************************************************* - * Set signal handler to catch SIGALRM - * Store the old value to be able to set it back later! - *************************************************************/ -#ifdef HAVE_SIGACTION - sigaction(SIGALRM, NULL, &sigact); - keep_sigact = sigact; - keep_copysig = TRUE; /* yes, we have a copy */ - sigact.sa_handler = alarmfunc; -#ifdef SA_RESTART - /* HPUX doesn't have SA_RESTART but defaults to that behavior! */ - sigact.sa_flags &= ~SA_RESTART; -#endif - /* now set the new struct */ - sigaction(SIGALRM, &sigact, NULL); -#else /* HAVE_SIGACTION */ - /* no sigaction(), revert to the much lamer signal() */ -#ifdef HAVE_SIGNAL - keep_sigact = signal(SIGALRM, alarmfunc); -#endif -#endif /* HAVE_SIGACTION */ - - /* alarm() makes a signal get sent when the timeout fires off, and that - will abort system calls */ - prev_alarm = alarm(curlx_sltoui(timeout/1000L)); - } - -#else -#ifndef CURLRES_ASYNCH - if(timeoutms) - infof(data, "timeout on name lookup is not supported"); -#else - (void)timeoutms; /* timeoutms not used with an async resolver */ -#endif -#endif /* USE_ALARM_TIMEOUT */ - - /* Perform the actual name resolution. This might be interrupted by an - * alarm if it takes too long. - */ - rc = Curl_resolv(data, hostname, port, TRUE, entry); - -#ifdef USE_ALARM_TIMEOUT -clean_up: - - if(!prev_alarm) - /* deactivate a possibly active alarm before uninstalling the handler */ - alarm(0); - -#ifdef HAVE_SIGACTION - if(keep_copysig) { - /* we got a struct as it looked before, now put that one back nice - and clean */ - sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */ - } -#else -#ifdef HAVE_SIGNAL - /* restore the previous SIGALRM handler */ - signal(SIGALRM, keep_sigact); -#endif -#endif /* HAVE_SIGACTION */ - - /* switch back the alarm() to either zero or to what it was before minus - the time we spent until now! */ - if(prev_alarm) { - /* there was an alarm() set before us, now put it back */ - timediff_t elapsed_secs = Curl_timediff(Curl_now(), - data->conn->created) / 1000; - - /* the alarm period is counted in even number of seconds */ - unsigned long alarm_set = (unsigned long)(prev_alarm - elapsed_secs); - - if(!alarm_set || - ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) { - /* if the alarm time-left reached zero or turned "negative" (counted - with unsigned values), we should fire off a SIGALRM here, but we - won't, and zero would be to switch it off so we never set it to - less than 1! */ - alarm(1); - rc = CURLRESOLV_TIMEDOUT; - failf(data, "Previous alarm fired off"); - } - else - alarm((unsigned int)alarm_set); - } -#endif /* USE_ALARM_TIMEOUT */ - - return rc; -} - -/* - * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been - * made, the struct may be destroyed due to pruning. It is important that only - * one unlock is made for each Curl_resolv() call. - * - * May be called with 'data' == NULL for global cache. - */ -void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns) -{ - if(data && data->share) - Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); - - freednsentry(dns); - - if(data && data->share) - Curl_share_unlock(data, CURL_LOCK_DATA_DNS); -} - -/* - * File-internal: release cache dns entry reference, free if inuse drops to 0 - */ -static void freednsentry(void *freethis) -{ - struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis; - DEBUGASSERT(dns && (dns->inuse>0)); - - dns->inuse--; - if(dns->inuse == 0) { - Curl_freeaddrinfo(dns->addr); - free(dns); - } -} - -/* - * Curl_init_dnscache() inits a new DNS cache. - */ -void Curl_init_dnscache(struct Curl_hash *hash, int size) -{ - Curl_hash_init(hash, size, Curl_hash_str, Curl_str_key_compare, - freednsentry); -} - -/* - * Curl_hostcache_clean() - * - * This _can_ be called with 'data' == NULL but then of course no locking - * can be done! - */ - -void Curl_hostcache_clean(struct Curl_easy *data, - struct Curl_hash *hash) -{ - if(data && data->share) - Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); - - Curl_hash_clean(hash); - - if(data && data->share) - Curl_share_unlock(data, CURL_LOCK_DATA_DNS); -} - - -CURLcode Curl_loadhostpairs(struct Curl_easy *data) -{ - struct curl_slist *hostp; - char hostname[256]; - int port = 0; - - /* Default is no wildcard found */ - data->state.wildcard_resolve = false; - - for(hostp = data->state.resolve; hostp; hostp = hostp->next) { - char entry_id[MAX_HOSTCACHE_LEN]; - if(!hostp->data) - continue; - if(hostp->data[0] == '-') { - size_t entry_len; - - if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) { - infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'", - hostp->data); - continue; - } - - /* Create an entry id, based upon the hostname and port */ - create_hostcache_id(hostname, port, entry_id, sizeof(entry_id)); - entry_len = strlen(entry_id); - - if(data->share) - Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); - - /* delete entry, ignore if it didn't exist */ - Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1); - - if(data->share) - Curl_share_unlock(data, CURL_LOCK_DATA_DNS); - } - else { - struct Curl_dns_entry *dns; - struct Curl_addrinfo *head = NULL, *tail = NULL; - size_t entry_len; - char address[64]; -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) - char *addresses = NULL; -#endif - char *addr_begin; - char *addr_end; - char *port_ptr; - char *end_ptr; - bool permanent = TRUE; - char *host_begin; - char *host_end; - unsigned long tmp_port; - bool error = true; - - host_begin = hostp->data; - if(host_begin[0] == '+') { - host_begin++; - permanent = FALSE; - } - host_end = strchr(host_begin, ':'); - if(!host_end || - ((host_end - host_begin) >= (ptrdiff_t)sizeof(hostname))) - goto err; - - memcpy(hostname, host_begin, host_end - host_begin); - hostname[host_end - host_begin] = '\0'; - - port_ptr = host_end + 1; - tmp_port = strtoul(port_ptr, &end_ptr, 10); - if(tmp_port > USHRT_MAX || end_ptr == port_ptr || *end_ptr != ':') - goto err; - - port = (int)tmp_port; -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) - addresses = end_ptr + 1; -#endif - - while(*end_ptr) { - size_t alen; - struct Curl_addrinfo *ai; - - addr_begin = end_ptr + 1; - addr_end = strchr(addr_begin, ','); - if(!addr_end) - addr_end = addr_begin + strlen(addr_begin); - end_ptr = addr_end; - - /* allow IP(v6) address within [brackets] */ - if(*addr_begin == '[') { - if(addr_end == addr_begin || *(addr_end - 1) != ']') - goto err; - ++addr_begin; - --addr_end; - } - - alen = addr_end - addr_begin; - if(!alen) - continue; - - if(alen >= sizeof(address)) - goto err; - - memcpy(address, addr_begin, alen); - address[alen] = '\0'; - -#ifndef ENABLE_IPV6 - if(strchr(address, ':')) { - infof(data, "Ignoring resolve address '%s', missing IPv6 support.", - address); - continue; - } -#endif - - ai = Curl_str2addr(address, port); - if(!ai) { - infof(data, "Resolve address '%s' found illegal", address); - goto err; - } - - if(tail) { - tail->ai_next = ai; - tail = tail->ai_next; - } - else { - head = tail = ai; - } - } - - if(!head) - goto err; - - error = false; - err: - if(error) { - failf(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'", - hostp->data); - Curl_freeaddrinfo(head); - return CURLE_SETOPT_OPTION_SYNTAX; - } - - /* Create an entry id, based upon the hostname and port */ - create_hostcache_id(hostname, port, entry_id, sizeof(entry_id)); - entry_len = strlen(entry_id); - - if(data->share) - Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); - - /* See if it's already in our dns cache */ - dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1); - - if(dns) { - infof(data, "RESOLVE %s:%d is - old addresses discarded", - hostname, port); - /* delete old entry, there are two reasons for this - 1. old entry may have different addresses. - 2. even if entry with correct addresses is already in the cache, - but if it is close to expire, then by the time next http - request is made, it can get expired and pruned because old - entry is not necessarily marked as permanent. - 3. when adding a non-permanent entry, we want it to remove and - replace an existing permanent entry. - 4. when adding a non-permanent entry, we want it to get a "fresh" - timeout that starts _now_. */ - - Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1); - } - - /* put this new host in the cache */ - dns = Curl_cache_addr(data, head, hostname, port); - if(dns) { - if(permanent) - dns->timestamp = 0; /* mark as permanent */ - /* release the returned reference; the cache itself will keep the - * entry alive: */ - dns->inuse--; - } - - if(data->share) - Curl_share_unlock(data, CURL_LOCK_DATA_DNS); - - if(!dns) { - Curl_freeaddrinfo(head); - return CURLE_OUT_OF_MEMORY; - } - infof(data, "Added %s:%d:%s to DNS cache%s", - hostname, port, addresses, permanent ? "" : " (non-permanent)"); - - /* Wildcard hostname */ - if(hostname[0] == '*' && hostname[1] == '\0') { - infof(data, "RESOLVE %s:%d is wildcard, enabling wildcard checks", - hostname, port); - data->state.wildcard_resolve = true; - } - } - } - data->state.resolve = NULL; /* dealt with now */ - - return CURLE_OK; -} - -CURLcode Curl_resolv_check(struct Curl_easy *data, - struct Curl_dns_entry **dns) -{ -#if defined(CURL_DISABLE_DOH) && !defined(CURLRES_ASYNCH) - (void)data; - (void)dns; -#endif -#ifndef CURL_DISABLE_DOH - if(data->conn->bits.doh) - return Curl_doh_is_resolved(data, dns); -#endif - return Curl_resolver_is_resolved(data, dns); -} - -int Curl_resolv_getsock(struct Curl_easy *data, - curl_socket_t *socks) -{ -#ifdef CURLRES_ASYNCH -#ifndef CURL_DISABLE_DOH - if(data->conn->bits.doh) - /* nothing to wait for during DoH resolve, those handles have their own - sockets */ - return GETSOCK_BLANK; -#endif - return Curl_resolver_getsock(data, socks); -#else - (void)data; - (void)socks; - return GETSOCK_BLANK; -#endif -} - -/* Call this function after Curl_connect() has returned async=TRUE and - then a successful name resolve has been received. - - Note: this function disconnects and frees the conn data in case of - resolve failure */ -CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done) -{ - CURLcode result; - struct connectdata *conn = data->conn; - -#ifdef USE_CURL_ASYNC - if(data->state.async.dns) { - conn->dns_entry = data->state.async.dns; - data->state.async.dns = NULL; - } -#endif - - result = Curl_setup_conn(data, protocol_done); - - if(result) { - Curl_detach_connection(data); - Curl_conncache_remove_conn(data, conn, TRUE); - Curl_disconnect(data, conn, TRUE); - } - return result; -} - -/* - * Curl_resolver_error() calls failf() with the appropriate message after a - * resolve error - */ - -#ifdef USE_CURL_ASYNC -CURLcode Curl_resolver_error(struct Curl_easy *data) -{ - const char *host_or_proxy; - CURLcode result; - -#ifndef CURL_DISABLE_PROXY - struct connectdata *conn = data->conn; - if(conn->bits.httpproxy) { - host_or_proxy = "proxy"; - result = CURLE_COULDNT_RESOLVE_PROXY; - } - else -#endif - { - host_or_proxy = "host"; - result = CURLE_COULDNT_RESOLVE_HOST; - } - - failf(data, "Could not resolve %s: %s", host_or_proxy, - data->state.async.hostname); - - return result; -} -#endif /* USE_CURL_ASYNC */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_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 __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "rand.h"
+#include "share.h"
+#include "url.h"
+#include "inet_ntop.h"
+#include "inet_pton.h"
+#include "multiif.h"
+#include "doh.h"
+#include "warnless.h"
+#include "strcase.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES)
+#include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
+#endif
+
+#if defined(CURLRES_SYNCH) && \
+ defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP)
+/* alarm-based timeouts can only be used with all the dependencies satisfied */
+#define USE_ALARM_TIMEOUT
+#endif
+
+#define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */
+
+/*
+ * hostip.c explained
+ * ==================
+ *
+ * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
+ * source file are these:
+ *
+ * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
+ * that. The host may not be able to resolve IPv6, but we don't really have to
+ * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
+ * defined.
+ *
+ * CURLRES_ARES - is defined if libcurl is built to use c-ares for
+ * asynchronous name resolves. This can be Windows or *nix.
+ *
+ * CURLRES_THREADED - is defined if libcurl is built to run under (native)
+ * Windows, and then the name resolve will be done in a new thread, and the
+ * supported API will be the same as for ares-builds.
+ *
+ * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
+ * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
+ * defined.
+ *
+ * The host*.c sources files are split up like this:
+ *
+ * hostip.c - method-independent resolver functions and utility functions
+ * hostasyn.c - functions for asynchronous name resolves
+ * hostsyn.c - functions for synchronous name resolves
+ * hostip4.c - IPv4 specific functions
+ * hostip6.c - IPv6 specific functions
+ *
+ * The two asynchronous name resolver backends are implemented in:
+ * asyn-ares.c - functions for ares-using name resolves
+ * asyn-thread.c - functions for threaded name resolves
+
+ * The hostip.h is the united header file for all this. It defines the
+ * CURLRES_* defines based on the config*.h and curl_setup.h defines.
+ */
+
+static void freednsentry(void *freethis);
+
+/*
+ * Return # of addresses in a Curl_addrinfo struct
+ */
+int Curl_num_addresses(const struct Curl_addrinfo *addr)
+{
+ int i = 0;
+ while(addr) {
+ addr = addr->ai_next;
+ i++;
+ }
+ return i;
+}
+
+/*
+ * Curl_printable_address() stores a printable version of the 1st address
+ * given in the 'ai' argument. The result will be stored in the buf that is
+ * bufsize bytes big.
+ *
+ * If the conversion fails, the target buffer is empty.
+ */
+void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf,
+ size_t bufsize)
+{
+ DEBUGASSERT(bufsize);
+ buf[0] = 0;
+
+ switch(ai->ai_family) {
+ case AF_INET: {
+ const struct sockaddr_in *sa4 = (const void *)ai->ai_addr;
+ const struct in_addr *ipaddr4 = &sa4->sin_addr;
+ (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, bufsize);
+ break;
+ }
+#ifdef ENABLE_IPV6
+ case AF_INET6: {
+ const struct sockaddr_in6 *sa6 = (const void *)ai->ai_addr;
+ const struct in6_addr *ipaddr6 = &sa6->sin6_addr;
+ (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf, bufsize);
+ break;
+ }
+#endif
+ default:
+ break;
+ }
+}
+
+/*
+ * Create a hostcache id string for the provided host + port, to be used by
+ * the DNS caching. Without alloc.
+ */
+static void
+create_hostcache_id(const char *name, int port, char *ptr, size_t buflen)
+{
+ size_t len = strlen(name);
+ if(len > (buflen - 7))
+ len = buflen - 7;
+ /* store and lower case the name */
+ while(len--)
+ *ptr++ = Curl_raw_tolower(*name++);
+ msnprintf(ptr, 7, ":%u", port);
+}
+
+struct hostcache_prune_data {
+ long cache_timeout;
+ time_t now;
+};
+
+/*
+ * This function is set as a callback to be called for every entry in the DNS
+ * cache when we want to prune old unused entries.
+ *
+ * Returning non-zero means remove the entry, return 0 to keep it in the
+ * cache.
+ */
+static int
+hostcache_timestamp_remove(void *datap, void *hc)
+{
+ struct hostcache_prune_data *data =
+ (struct hostcache_prune_data *) datap;
+ struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
+
+ return (0 != c->timestamp)
+ && (data->now - c->timestamp >= data->cache_timeout);
+}
+
+/*
+ * Prune the DNS cache. This assumes that a lock has already been taken.
+ */
+static void
+hostcache_prune(struct Curl_hash *hostcache, long cache_timeout, time_t now)
+{
+ struct hostcache_prune_data user;
+
+ user.cache_timeout = cache_timeout;
+ user.now = now;
+
+ Curl_hash_clean_with_criterium(hostcache,
+ (void *) &user,
+ hostcache_timestamp_remove);
+}
+
+/*
+ * Library-wide function for pruning the DNS cache. This function takes and
+ * returns the appropriate locks.
+ */
+void Curl_hostcache_prune(struct Curl_easy *data)
+{
+ time_t now;
+
+ if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
+ /* cache forever means never prune, and NULL hostcache means
+ we can't do it */
+ return;
+
+ if(data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+ time(&now);
+
+ /* Remove outdated and unused entries from the hostcache */
+ hostcache_prune(data->dns.hostcache,
+ data->set.dns_cache_timeout,
+ now);
+
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+}
+
+#ifdef HAVE_SIGSETJMP
+/* Beware this is a global and unique instance. This is used to store the
+ return address that we can jump back to from inside a signal handler. This
+ is not thread-safe stuff. */
+sigjmp_buf curl_jmpenv;
+#endif
+
+/* lookup address, returns entry if found and not stale */
+static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
+ const char *hostname,
+ int port)
+{
+ struct Curl_dns_entry *dns = NULL;
+ size_t entry_len;
+ char entry_id[MAX_HOSTCACHE_LEN];
+
+ /* Create an entry id, based upon the hostname and port */
+ create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
+ entry_len = strlen(entry_id);
+
+ /* See if its already in our dns cache */
+ dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
+
+ /* No entry found in cache, check if we might have a wildcard entry */
+ if(!dns && data->state.wildcard_resolve) {
+ create_hostcache_id("*", port, entry_id, sizeof(entry_id));
+ entry_len = strlen(entry_id);
+
+ /* See if it's already in our dns cache */
+ dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
+ }
+
+ if(dns && (data->set.dns_cache_timeout != -1)) {
+ /* See whether the returned entry is stale. Done before we release lock */
+ struct hostcache_prune_data user;
+
+ time(&user.now);
+ user.cache_timeout = data->set.dns_cache_timeout;
+
+ if(hostcache_timestamp_remove(&user, dns)) {
+ infof(data, "Hostname in DNS cache was stale, zapped");
+ dns = NULL; /* the memory deallocation is being handled by the hash */
+ Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
+ }
+ }
+
+ /* See if the returned entry matches the required resolve mode */
+ if(dns && data->conn->ip_version != CURL_IPRESOLVE_WHATEVER) {
+ int pf = PF_INET;
+ bool found = false;
+ struct Curl_addrinfo *addr = dns->addr;
+
+#ifdef PF_INET6
+ if(data->conn->ip_version == CURL_IPRESOLVE_V6)
+ pf = PF_INET6;
+#endif
+
+ while(addr) {
+ if(addr->ai_family == pf) {
+ found = true;
+ break;
+ }
+ addr = addr->ai_next;
+ }
+
+ if(!found) {
+ infof(data, "Hostname in DNS cache doesn't have needed family, zapped");
+ dns = NULL; /* the memory deallocation is being handled by the hash */
+ Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
+ }
+ }
+ return dns;
+}
+
+/*
+ * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache.
+ *
+ * Curl_resolv() checks initially and multi_runsingle() checks each time
+ * it discovers the handle in the state WAITRESOLVE whether the hostname
+ * has already been resolved and the address has already been stored in
+ * the DNS cache. This short circuits waiting for a lot of pending
+ * lookups for the same hostname requested by different handles.
+ *
+ * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
+ *
+ * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
+ * use, or we'll leak memory!
+ */
+struct Curl_dns_entry *
+Curl_fetch_addr(struct Curl_easy *data,
+ const char *hostname,
+ int port)
+{
+ struct Curl_dns_entry *dns = NULL;
+
+ if(data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+ dns = fetch_addr(data, hostname, port);
+
+ if(dns)
+ dns->inuse++; /* we use it! */
+
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+
+ return dns;
+}
+
+#ifndef CURL_DISABLE_SHUFFLE_DNS
+UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
+ struct Curl_addrinfo **addr);
+/*
+ * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo'
+ * struct by re-linking its linked list.
+ *
+ * The addr argument should be the address of a pointer to the head node of a
+ * `Curl_addrinfo` list and it will be modified to point to the new head after
+ * shuffling.
+ *
+ * Not declared static only to make it easy to use in a unit test!
+ *
+ * @unittest: 1608
+ */
+UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
+ struct Curl_addrinfo **addr)
+{
+ CURLcode result = CURLE_OK;
+ const int num_addrs = Curl_num_addresses(*addr);
+
+ if(num_addrs > 1) {
+ struct Curl_addrinfo **nodes;
+ infof(data, "Shuffling %i addresses", num_addrs);
+
+ nodes = malloc(num_addrs*sizeof(*nodes));
+ if(nodes) {
+ int i;
+ unsigned int *rnd;
+ const size_t rnd_size = num_addrs * sizeof(*rnd);
+
+ /* build a plain array of Curl_addrinfo pointers */
+ nodes[0] = *addr;
+ for(i = 1; i < num_addrs; i++) {
+ nodes[i] = nodes[i-1]->ai_next;
+ }
+
+ rnd = malloc(rnd_size);
+ if(rnd) {
+ /* Fisher-Yates shuffle */
+ if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) {
+ struct Curl_addrinfo *swap_tmp;
+ for(i = num_addrs - 1; i > 0; i--) {
+ swap_tmp = nodes[rnd[i] % (i + 1)];
+ nodes[rnd[i] % (i + 1)] = nodes[i];
+ nodes[i] = swap_tmp;
+ }
+
+ /* relink list in the new order */
+ for(i = 1; i < num_addrs; i++) {
+ nodes[i-1]->ai_next = nodes[i];
+ }
+
+ nodes[num_addrs-1]->ai_next = NULL;
+ *addr = nodes[0];
+ }
+ free(rnd);
+ }
+ else
+ result = CURLE_OUT_OF_MEMORY;
+ free(nodes);
+ }
+ else
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ return result;
+}
+#endif
+
+/*
+ * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
+ *
+ * When calling Curl_resolv() has resulted in a response with a returned
+ * address, we call this function to store the information in the dns
+ * cache etc
+ *
+ * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
+ */
+struct Curl_dns_entry *
+Curl_cache_addr(struct Curl_easy *data,
+ struct Curl_addrinfo *addr,
+ const char *hostname,
+ int port)
+{
+ char entry_id[MAX_HOSTCACHE_LEN];
+ size_t entry_len;
+ struct Curl_dns_entry *dns;
+ struct Curl_dns_entry *dns2;
+
+#ifndef CURL_DISABLE_SHUFFLE_DNS
+ /* shuffle addresses if requested */
+ if(data->set.dns_shuffle_addresses) {
+ CURLcode result = Curl_shuffle_addr(data, &addr);
+ if(result)
+ return NULL;
+ }
+#endif
+
+ /* Create a new cache entry */
+ dns = calloc(1, sizeof(struct Curl_dns_entry));
+ if(!dns) {
+ return NULL;
+ }
+
+ /* Create an entry id, based upon the hostname and port */
+ create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
+ entry_len = strlen(entry_id);
+
+ dns->inuse = 1; /* the cache has the first reference */
+ dns->addr = addr; /* this is the address(es) */
+ time(&dns->timestamp);
+ if(dns->timestamp == 0)
+ dns->timestamp = 1; /* zero indicates permanent CURLOPT_RESOLVE entry */
+
+ /* Store the resolved data in our DNS cache. */
+ dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1,
+ (void *)dns);
+ if(!dns2) {
+ free(dns);
+ return NULL;
+ }
+
+ dns = dns2;
+ dns->inuse++; /* mark entry as in-use */
+ return dns;
+}
+
+#ifdef ENABLE_IPV6
+/* return a static IPv6 ::1 for the name */
+static struct Curl_addrinfo *get_localhost6(int port, const char *name)
+{
+ struct Curl_addrinfo *ca;
+ const size_t ss_size = sizeof(struct sockaddr_in6);
+ const size_t hostlen = strlen(name);
+ struct sockaddr_in6 sa6;
+ unsigned char ipv6[16];
+ unsigned short port16 = (unsigned short)(port & 0xffff);
+ ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1);
+ if(!ca)
+ return NULL;
+
+ sa6.sin6_family = AF_INET6;
+ sa6.sin6_port = htons(port16);
+ sa6.sin6_flowinfo = 0;
+ sa6.sin6_scope_id = 0;
+ if(Curl_inet_pton(AF_INET6, "::1", ipv6) < 1)
+ return NULL;
+ memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6));
+
+ ca->ai_flags = 0;
+ ca->ai_family = AF_INET6;
+ ca->ai_socktype = SOCK_STREAM;
+ ca->ai_protocol = IPPROTO_TCP;
+ ca->ai_addrlen = (curl_socklen_t)ss_size;
+ ca->ai_next = NULL;
+ ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
+ memcpy(ca->ai_addr, &sa6, ss_size);
+ ca->ai_canonname = (char *)ca->ai_addr + ss_size;
+ strcpy(ca->ai_canonname, name);
+ return ca;
+}
+#else
+#define get_localhost6(x,y) NULL
+#endif
+
+/* return a static IPv4 127.0.0.1 for the given name */
+static struct Curl_addrinfo *get_localhost(int port, const char *name)
+{
+ struct Curl_addrinfo *ca;
+ const size_t ss_size = sizeof(struct sockaddr_in);
+ const size_t hostlen = strlen(name);
+ struct sockaddr_in sa;
+ unsigned int ipv4;
+ unsigned short port16 = (unsigned short)(port & 0xffff);
+
+ /* memset to clear the sa.sin_zero field */
+ memset(&sa, 0, sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(port16);
+ if(Curl_inet_pton(AF_INET, "127.0.0.1", (char *)&ipv4) < 1)
+ return NULL;
+ memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4));
+
+ ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1);
+ if(!ca)
+ return NULL;
+ ca->ai_flags = 0;
+ ca->ai_family = AF_INET;
+ ca->ai_socktype = SOCK_STREAM;
+ ca->ai_protocol = IPPROTO_TCP;
+ ca->ai_addrlen = (curl_socklen_t)ss_size;
+ ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
+ memcpy(ca->ai_addr, &sa, ss_size);
+ ca->ai_canonname = (char *)ca->ai_addr + ss_size;
+ strcpy(ca->ai_canonname, name);
+ ca->ai_next = get_localhost6(port, name);
+ return ca;
+}
+
+#ifdef ENABLE_IPV6
+/*
+ * Curl_ipv6works() returns TRUE if IPv6 seems to work.
+ */
+bool Curl_ipv6works(struct Curl_easy *data)
+{
+ if(data) {
+ /* the nature of most system is that IPv6 status doesn't come and go
+ during a program's lifetime so we only probe the first time and then we
+ have the info kept for fast re-use */
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->multi);
+ if(data->multi->ipv6_up == IPV6_UNKNOWN) {
+ bool works = Curl_ipv6works(NULL);
+ data->multi->ipv6_up = works ? IPV6_WORKS : IPV6_DEAD;
+ }
+ return data->multi->ipv6_up == IPV6_WORKS;
+ }
+ else {
+ int ipv6_works = -1;
+ /* probe to see if we have a working IPv6 stack */
+ curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
+ if(s == CURL_SOCKET_BAD)
+ /* an IPv6 address was requested but we can't get/use one */
+ ipv6_works = 0;
+ else {
+ ipv6_works = 1;
+ sclose(s);
+ }
+ return (ipv6_works>0)?TRUE:FALSE;
+ }
+}
+#endif /* ENABLE_IPV6 */
+
+/*
+ * Curl_host_is_ipnum() returns TRUE if the given string is a numerical IPv4
+ * (or IPv6 if supported) address.
+ */
+bool Curl_host_is_ipnum(const char *hostname)
+{
+ struct in_addr in;
+#ifdef ENABLE_IPV6
+ struct in6_addr in6;
+#endif
+ if(Curl_inet_pton(AF_INET, hostname, &in) > 0
+#ifdef ENABLE_IPV6
+ || Curl_inet_pton(AF_INET6, hostname, &in6) > 0
+#endif
+ )
+ return TRUE;
+ return FALSE;
+}
+
+
+/* return TRUE if 'part' is a case insensitive tail of 'full' */
+static bool tailmatch(const char *full, const char *part)
+{
+ size_t plen = strlen(part);
+ size_t flen = strlen(full);
+ if(plen > flen)
+ return FALSE;
+ return strncasecompare(part, &full[flen - plen], plen);
+}
+
+/*
+ * Curl_resolv() is the main name resolve function within libcurl. It resolves
+ * a name and returns a pointer to the entry in the 'entry' argument (if one
+ * is provided). This function might return immediately if we're using asynch
+ * resolves. See the return codes.
+ *
+ * The cache entry we return will get its 'inuse' counter increased when this
+ * function is used. You MUST call Curl_resolv_unlock() later (when you're
+ * done using this struct) to decrease the counter again.
+ *
+ * Return codes:
+ *
+ * CURLRESOLV_ERROR (-1) = error, no pointer
+ * CURLRESOLV_RESOLVED (0) = OK, pointer provided
+ * CURLRESOLV_PENDING (1) = waiting for response, no pointer
+ */
+
+enum resolve_t Curl_resolv(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ bool allowDOH,
+ struct Curl_dns_entry **entry)
+{
+ struct Curl_dns_entry *dns = NULL;
+ CURLcode result;
+ enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */
+ struct connectdata *conn = data->conn;
+ *entry = NULL;
+#ifndef CURL_DISABLE_DOH
+ conn->bits.doh = FALSE; /* default is not */
+#else
+ (void)allowDOH;
+#endif
+
+ if(data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+ dns = fetch_addr(data, hostname, port);
+
+ if(dns) {
+ infof(data, "Hostname %s was found in DNS cache", hostname);
+ dns->inuse++; /* we use it! */
+ rc = CURLRESOLV_RESOLVED;
+ }
+
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+
+ if(!dns) {
+ /* The entry was not in the cache. Resolve it to IP address */
+
+ struct Curl_addrinfo *addr = NULL;
+ int respwait = 0;
+#if !defined(CURL_DISABLE_DOH) || !defined(USE_RESOLVE_ON_IPS)
+ struct in_addr in;
+#endif
+#ifndef CURL_DISABLE_DOH
+#ifndef USE_RESOLVE_ON_IPS
+ const
+#endif
+ bool ipnum = FALSE;
+#endif
+
+ /* notify the resolver start callback */
+ if(data->set.resolver_start) {
+ int st;
+ Curl_set_in_callback(data, true);
+ st = data->set.resolver_start(
+#ifdef USE_CURL_ASYNC
+ data->state.async.resolver,
+#else
+ NULL,
+#endif
+ NULL,
+ data->set.resolver_start_client);
+ Curl_set_in_callback(data, false);
+ if(st)
+ return CURLRESOLV_ERROR;
+ }
+
+#if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES)
+ {
+ /*
+ * The automagic conversion from IPv4 literals to IPv6 literals only
+ * works if the SCDynamicStoreCopyProxies system function gets called
+ * first. As Curl currently doesn't support system-wide HTTP proxies, we
+ * therefore don't use any value this function might return.
+ *
+ * This function is only available on a macOS and is not needed for
+ * IPv4-only builds, hence the conditions above.
+ */
+ CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL);
+ if(dict)
+ CFRelease(dict);
+ }
+#endif
+
+#ifndef USE_RESOLVE_ON_IPS
+ /* First check if this is an IPv4 address string */
+ if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
+ /* This is a dotted IP address 123.123.123.123-style */
+ addr = Curl_ip2addr(AF_INET, &in, hostname, port);
+#ifdef ENABLE_IPV6
+ if(!addr) {
+ struct in6_addr in6;
+ /* check if this is an IPv6 address string */
+ if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
+ /* This is an IPv6 address literal */
+ addr = Curl_ip2addr(AF_INET6, &in6, hostname, port);
+ }
+#endif /* ENABLE_IPV6 */
+
+#else /* if USE_RESOLVE_ON_IPS */
+#ifndef CURL_DISABLE_DOH
+ /* First check if this is an IPv4 address string */
+ if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
+ /* This is a dotted IP address 123.123.123.123-style */
+ ipnum = TRUE;
+#ifdef ENABLE_IPV6
+ else {
+ struct in6_addr in6;
+ /* check if this is an IPv6 address string */
+ if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
+ /* This is an IPv6 address literal */
+ ipnum = TRUE;
+ }
+#endif /* ENABLE_IPV6 */
+#endif /* CURL_DISABLE_DOH */
+
+#endif /* !USE_RESOLVE_ON_IPS */
+
+ if(!addr) {
+ if(conn->ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
+ return CURLRESOLV_ERROR;
+
+ if(strcasecompare(hostname, "localhost") ||
+ tailmatch(hostname, ".localhost"))
+ addr = get_localhost(port, hostname);
+#ifndef CURL_DISABLE_DOH
+ else if(allowDOH && data->set.doh && !ipnum)
+ addr = Curl_doh(data, hostname, port, &respwait);
+#endif
+ else {
+ /* Check what IP specifics the app has requested and if we can provide
+ * it. If not, bail out. */
+ if(!Curl_ipvalid(data, conn))
+ return CURLRESOLV_ERROR;
+ /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
+ non-zero value indicating that we need to wait for the response to
+ the resolve call */
+ addr = Curl_getaddrinfo(data, hostname, port, &respwait);
+ }
+ }
+ if(!addr) {
+ if(respwait) {
+ /* the response to our resolve call will come asynchronously at
+ a later time, good or bad */
+ /* First, check that we haven't received the info by now */
+ result = Curl_resolv_check(data, &dns);
+ if(result) /* error detected */
+ return CURLRESOLV_ERROR;
+ if(dns)
+ rc = CURLRESOLV_RESOLVED; /* pointer provided */
+ else
+ rc = CURLRESOLV_PENDING; /* no info yet */
+ }
+ }
+ else {
+ if(data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+ /* we got a response, store it in the cache */
+ dns = Curl_cache_addr(data, addr, hostname, port);
+
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+
+ if(!dns)
+ /* returned failure, bail out nicely */
+ Curl_freeaddrinfo(addr);
+ else
+ rc = CURLRESOLV_RESOLVED;
+ }
+ }
+
+ *entry = dns;
+
+ return rc;
+}
+
+#ifdef USE_ALARM_TIMEOUT
+/*
+ * This signal handler jumps back into the main libcurl code and continues
+ * execution. This effectively causes the remainder of the application to run
+ * within a signal handler which is nonportable and could lead to problems.
+ */
+static
+void alarmfunc(int sig)
+{
+ /* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */
+ (void)sig;
+ siglongjmp(curl_jmpenv, 1);
+}
+#endif /* USE_ALARM_TIMEOUT */
+
+/*
+ * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
+ * timeout. This function might return immediately if we're using asynch
+ * resolves. See the return codes.
+ *
+ * The cache entry we return will get its 'inuse' counter increased when this
+ * function is used. You MUST call Curl_resolv_unlock() later (when you're
+ * done using this struct) to decrease the counter again.
+ *
+ * If built with a synchronous resolver and use of signals is not
+ * disabled by the application, then a nonzero timeout will cause a
+ * timeout after the specified number of milliseconds. Otherwise, timeout
+ * is ignored.
+ *
+ * Return codes:
+ *
+ * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
+ * CURLRESOLV_ERROR (-1) = error, no pointer
+ * CURLRESOLV_RESOLVED (0) = OK, pointer provided
+ * CURLRESOLV_PENDING (1) = waiting for response, no pointer
+ */
+
+enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ struct Curl_dns_entry **entry,
+ timediff_t timeoutms)
+{
+#ifdef USE_ALARM_TIMEOUT
+#ifdef HAVE_SIGACTION
+ struct sigaction keep_sigact; /* store the old struct here */
+ volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */
+ struct sigaction sigact;
+#else
+#ifdef HAVE_SIGNAL
+ void (*keep_sigact)(int); /* store the old handler here */
+#endif /* HAVE_SIGNAL */
+#endif /* HAVE_SIGACTION */
+ volatile long timeout;
+ volatile unsigned int prev_alarm = 0;
+#endif /* USE_ALARM_TIMEOUT */
+ enum resolve_t rc;
+
+ *entry = NULL;
+
+ if(timeoutms < 0)
+ /* got an already expired timeout */
+ return CURLRESOLV_TIMEDOUT;
+
+#ifdef USE_ALARM_TIMEOUT
+ if(data->set.no_signal)
+ /* Ignore the timeout when signals are disabled */
+ timeout = 0;
+ else
+ timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms;
+
+ if(!timeout)
+ /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
+ return Curl_resolv(data, hostname, port, TRUE, entry);
+
+ if(timeout < 1000) {
+ /* The alarm() function only provides integer second resolution, so if
+ we want to wait less than one second we must bail out already now. */
+ failf(data,
+ "remaining timeout of %ld too small to resolve via SIGALRM method",
+ timeout);
+ return CURLRESOLV_TIMEDOUT;
+ }
+ /* This allows us to time-out from the name resolver, as the timeout
+ will generate a signal and we will siglongjmp() from that here.
+ This technique has problems (see alarmfunc).
+ This should be the last thing we do before calling Curl_resolv(),
+ as otherwise we'd have to worry about variables that get modified
+ before we invoke Curl_resolv() (and thus use "volatile"). */
+ if(sigsetjmp(curl_jmpenv, 1)) {
+ /* this is coming from a siglongjmp() after an alarm signal */
+ failf(data, "name lookup timed out");
+ rc = CURLRESOLV_ERROR;
+ goto clean_up;
+ }
+ else {
+ /*************************************************************
+ * Set signal handler to catch SIGALRM
+ * Store the old value to be able to set it back later!
+ *************************************************************/
+#ifdef HAVE_SIGACTION
+ sigaction(SIGALRM, NULL, &sigact);
+ keep_sigact = sigact;
+ keep_copysig = TRUE; /* yes, we have a copy */
+ sigact.sa_handler = alarmfunc;
+#ifdef SA_RESTART
+ /* HPUX doesn't have SA_RESTART but defaults to that behavior! */
+ sigact.sa_flags &= ~SA_RESTART;
+#endif
+ /* now set the new struct */
+ sigaction(SIGALRM, &sigact, NULL);
+#else /* HAVE_SIGACTION */
+ /* no sigaction(), revert to the much lamer signal() */
+#ifdef HAVE_SIGNAL
+ keep_sigact = signal(SIGALRM, alarmfunc);
+#endif
+#endif /* HAVE_SIGACTION */
+
+ /* alarm() makes a signal get sent when the timeout fires off, and that
+ will abort system calls */
+ prev_alarm = alarm(curlx_sltoui(timeout/1000L));
+ }
+
+#else
+#ifndef CURLRES_ASYNCH
+ if(timeoutms)
+ infof(data, "timeout on name lookup is not supported");
+#else
+ (void)timeoutms; /* timeoutms not used with an async resolver */
+#endif
+#endif /* USE_ALARM_TIMEOUT */
+
+ /* Perform the actual name resolution. This might be interrupted by an
+ * alarm if it takes too long.
+ */
+ rc = Curl_resolv(data, hostname, port, TRUE, entry);
+
+#ifdef USE_ALARM_TIMEOUT
+clean_up:
+
+ if(!prev_alarm)
+ /* deactivate a possibly active alarm before uninstalling the handler */
+ alarm(0);
+
+#ifdef HAVE_SIGACTION
+ if(keep_copysig) {
+ /* we got a struct as it looked before, now put that one back nice
+ and clean */
+ sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
+ }
+#else
+#ifdef HAVE_SIGNAL
+ /* restore the previous SIGALRM handler */
+ signal(SIGALRM, keep_sigact);
+#endif
+#endif /* HAVE_SIGACTION */
+
+ /* switch back the alarm() to either zero or to what it was before minus
+ the time we spent until now! */
+ if(prev_alarm) {
+ /* there was an alarm() set before us, now put it back */
+ timediff_t elapsed_secs = Curl_timediff(Curl_now(),
+ data->conn->created) / 1000;
+
+ /* the alarm period is counted in even number of seconds */
+ unsigned long alarm_set = (unsigned long)(prev_alarm - elapsed_secs);
+
+ if(!alarm_set ||
+ ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
+ /* if the alarm time-left reached zero or turned "negative" (counted
+ with unsigned values), we should fire off a SIGALRM here, but we
+ won't, and zero would be to switch it off so we never set it to
+ less than 1! */
+ alarm(1);
+ rc = CURLRESOLV_TIMEDOUT;
+ failf(data, "Previous alarm fired off");
+ }
+ else
+ alarm((unsigned int)alarm_set);
+ }
+#endif /* USE_ALARM_TIMEOUT */
+
+ return rc;
+}
+
+/*
+ * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
+ * made, the struct may be destroyed due to pruning. It is important that only
+ * one unlock is made for each Curl_resolv() call.
+ *
+ * May be called with 'data' == NULL for global cache.
+ */
+void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns)
+{
+ if(data && data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+ freednsentry(dns);
+
+ if(data && data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+}
+
+/*
+ * File-internal: release cache dns entry reference, free if inuse drops to 0
+ */
+static void freednsentry(void *freethis)
+{
+ struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis;
+ DEBUGASSERT(dns && (dns->inuse>0));
+
+ dns->inuse--;
+ if(dns->inuse == 0) {
+ Curl_freeaddrinfo(dns->addr);
+ free(dns);
+ }
+}
+
+/*
+ * Curl_init_dnscache() inits a new DNS cache.
+ */
+void Curl_init_dnscache(struct Curl_hash *hash, int size)
+{
+ Curl_hash_init(hash, size, Curl_hash_str, Curl_str_key_compare,
+ freednsentry);
+}
+
+/*
+ * Curl_hostcache_clean()
+ *
+ * This _can_ be called with 'data' == NULL but then of course no locking
+ * can be done!
+ */
+
+void Curl_hostcache_clean(struct Curl_easy *data,
+ struct Curl_hash *hash)
+{
+ if(data && data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+ Curl_hash_clean(hash);
+
+ if(data && data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+}
+
+
+CURLcode Curl_loadhostpairs(struct Curl_easy *data)
+{
+ struct curl_slist *hostp;
+ char hostname[256];
+ int port = 0;
+
+ /* Default is no wildcard found */
+ data->state.wildcard_resolve = false;
+
+ for(hostp = data->state.resolve; hostp; hostp = hostp->next) {
+ char entry_id[MAX_HOSTCACHE_LEN];
+ if(!hostp->data)
+ continue;
+ if(hostp->data[0] == '-') {
+ size_t entry_len;
+
+ if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) {
+ infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'",
+ hostp->data);
+ continue;
+ }
+
+ /* Create an entry id, based upon the hostname and port */
+ create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
+ entry_len = strlen(entry_id);
+
+ if(data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+ /* delete entry, ignore if it didn't exist */
+ Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
+
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+ }
+ else {
+ struct Curl_dns_entry *dns;
+ struct Curl_addrinfo *head = NULL, *tail = NULL;
+ size_t entry_len;
+ char address[64];
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ char *addresses = NULL;
+#endif
+ char *addr_begin;
+ char *addr_end;
+ char *port_ptr;
+ char *end_ptr;
+ bool permanent = TRUE;
+ char *host_begin;
+ char *host_end;
+ unsigned long tmp_port;
+ bool error = true;
+
+ host_begin = hostp->data;
+ if(host_begin[0] == '+') {
+ host_begin++;
+ permanent = FALSE;
+ }
+ host_end = strchr(host_begin, ':');
+ if(!host_end ||
+ ((host_end - host_begin) >= (ptrdiff_t)sizeof(hostname)))
+ goto err;
+
+ memcpy(hostname, host_begin, host_end - host_begin);
+ hostname[host_end - host_begin] = '\0';
+
+ port_ptr = host_end + 1;
+ tmp_port = strtoul(port_ptr, &end_ptr, 10);
+ if(tmp_port > USHRT_MAX || end_ptr == port_ptr || *end_ptr != ':')
+ goto err;
+
+ port = (int)tmp_port;
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ addresses = end_ptr + 1;
+#endif
+
+ while(*end_ptr) {
+ size_t alen;
+ struct Curl_addrinfo *ai;
+
+ addr_begin = end_ptr + 1;
+ addr_end = strchr(addr_begin, ',');
+ if(!addr_end)
+ addr_end = addr_begin + strlen(addr_begin);
+ end_ptr = addr_end;
+
+ /* allow IP(v6) address within [brackets] */
+ if(*addr_begin == '[') {
+ if(addr_end == addr_begin || *(addr_end - 1) != ']')
+ goto err;
+ ++addr_begin;
+ --addr_end;
+ }
+
+ alen = addr_end - addr_begin;
+ if(!alen)
+ continue;
+
+ if(alen >= sizeof(address))
+ goto err;
+
+ memcpy(address, addr_begin, alen);
+ address[alen] = '\0';
+
+#ifndef ENABLE_IPV6
+ if(strchr(address, ':')) {
+ infof(data, "Ignoring resolve address '%s', missing IPv6 support.",
+ address);
+ continue;
+ }
+#endif
+
+ ai = Curl_str2addr(address, port);
+ if(!ai) {
+ infof(data, "Resolve address '%s' found illegal", address);
+ goto err;
+ }
+
+ if(tail) {
+ tail->ai_next = ai;
+ tail = tail->ai_next;
+ }
+ else {
+ head = tail = ai;
+ }
+ }
+
+ if(!head)
+ goto err;
+
+ error = false;
+ err:
+ if(error) {
+ failf(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'",
+ hostp->data);
+ Curl_freeaddrinfo(head);
+ return CURLE_SETOPT_OPTION_SYNTAX;
+ }
+
+ /* Create an entry id, based upon the hostname and port */
+ create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
+ entry_len = strlen(entry_id);
+
+ if(data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+ /* See if it's already in our dns cache */
+ dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
+
+ if(dns) {
+ infof(data, "RESOLVE %s:%d is - old addresses discarded",
+ hostname, port);
+ /* delete old entry, there are two reasons for this
+ 1. old entry may have different addresses.
+ 2. even if entry with correct addresses is already in the cache,
+ but if it is close to expire, then by the time next http
+ request is made, it can get expired and pruned because old
+ entry is not necessarily marked as permanent.
+ 3. when adding a non-permanent entry, we want it to remove and
+ replace an existing permanent entry.
+ 4. when adding a non-permanent entry, we want it to get a "fresh"
+ timeout that starts _now_. */
+
+ Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
+ }
+
+ /* put this new host in the cache */
+ dns = Curl_cache_addr(data, head, hostname, port);
+ if(dns) {
+ if(permanent)
+ dns->timestamp = 0; /* mark as permanent */
+ /* release the returned reference; the cache itself will keep the
+ * entry alive: */
+ dns->inuse--;
+ }
+
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+
+ if(!dns) {
+ Curl_freeaddrinfo(head);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ infof(data, "Added %s:%d:%s to DNS cache%s",
+ hostname, port, addresses, permanent ? "" : " (non-permanent)");
+
+ /* Wildcard hostname */
+ if(hostname[0] == '*' && hostname[1] == '\0') {
+ infof(data, "RESOLVE %s:%d is wildcard, enabling wildcard checks",
+ hostname, port);
+ data->state.wildcard_resolve = true;
+ }
+ }
+ }
+ data->state.resolve = NULL; /* dealt with now */
+
+ return CURLE_OK;
+}
+
+CURLcode Curl_resolv_check(struct Curl_easy *data,
+ struct Curl_dns_entry **dns)
+{
+#if defined(CURL_DISABLE_DOH) && !defined(CURLRES_ASYNCH)
+ (void)data;
+ (void)dns;
+#endif
+#ifndef CURL_DISABLE_DOH
+ if(data->conn->bits.doh)
+ return Curl_doh_is_resolved(data, dns);
+#endif
+ return Curl_resolver_is_resolved(data, dns);
+}
+
+int Curl_resolv_getsock(struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+#ifdef CURLRES_ASYNCH
+#ifndef CURL_DISABLE_DOH
+ if(data->conn->bits.doh)
+ /* nothing to wait for during DoH resolve, those handles have their own
+ sockets */
+ return GETSOCK_BLANK;
+#endif
+ return Curl_resolver_getsock(data, socks);
+#else
+ (void)data;
+ (void)socks;
+ return GETSOCK_BLANK;
+#endif
+}
+
+/* Call this function after Curl_connect() has returned async=TRUE and
+ then a successful name resolve has been received.
+
+ Note: this function disconnects and frees the conn data in case of
+ resolve failure */
+CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done)
+{
+ CURLcode result;
+ struct connectdata *conn = data->conn;
+
+#ifdef USE_CURL_ASYNC
+ if(data->state.async.dns) {
+ conn->dns_entry = data->state.async.dns;
+ data->state.async.dns = NULL;
+ }
+#endif
+
+ result = Curl_setup_conn(data, protocol_done);
+
+ if(result) {
+ Curl_detach_connection(data);
+ Curl_conncache_remove_conn(data, conn, TRUE);
+ Curl_disconnect(data, conn, TRUE);
+ }
+ return result;
+}
+
+/*
+ * Curl_resolver_error() calls failf() with the appropriate message after a
+ * resolve error
+ */
+
+#ifdef USE_CURL_ASYNC
+CURLcode Curl_resolver_error(struct Curl_easy *data)
+{
+ const char *host_or_proxy;
+ CURLcode result;
+
+#ifndef CURL_DISABLE_PROXY
+ struct connectdata *conn = data->conn;
+ if(conn->bits.httpproxy) {
+ host_or_proxy = "proxy";
+ result = CURLE_COULDNT_RESOLVE_PROXY;
+ }
+ else
+#endif
+ {
+ host_or_proxy = "host";
+ result = CURLE_COULDNT_RESOLVE_HOST;
+ }
+
+ failf(data, "Could not resolve %s: %s", host_or_proxy,
+ data->state.async.hostname);
+
+ return result;
+}
+#endif /* USE_CURL_ASYNC */
diff --git a/libs/libcurl/src/hostip.h b/libs/libcurl/src/hostip.h index 9d3170737c..69144d0272 100644 --- a/libs/libcurl/src/hostip.h +++ b/libs/libcurl/src/hostip.h @@ -1,244 +1,239 @@ -#ifndef HEADER_CURL_HOSTIP_H -#define HEADER_CURL_HOSTIP_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "hash.h" -#include "curl_addrinfo.h" -#include "timeval.h" /* for timediff_t */ -#include "asyn.h" - -#ifdef HAVE_SETJMP_H -#include <setjmp.h> -#endif - -#ifdef NETWARE -#undef in_addr_t -#define in_addr_t unsigned long -#endif - -/* Allocate enough memory to hold the full name information structs and - * everything. OSF1 is known to require at least 8872 bytes. The buffer - * required for storing all possible aliases and IP numbers is according to - * Stevens' Unix Network Programming 2nd edition, p. 304: 8192 bytes! - */ -#define CURL_HOSTENT_SIZE 9000 - -#define CURL_TIMEOUT_RESOLVE 300 /* when using asynch methods, we allow this - many seconds for a name resolve */ - -#define CURL_ASYNC_SUCCESS CURLE_OK - -struct addrinfo; -struct hostent; -struct Curl_easy; -struct connectdata; - -/* - * Curl_global_host_cache_init() initializes and sets up a global DNS cache. - * Global DNS cache is general badness. Do not use. This will be removed in - * a future version. Use the share interface instead! - * - * Returns a struct Curl_hash pointer on success, NULL on failure. - */ -struct Curl_hash *Curl_global_host_cache_init(void); - -struct Curl_dns_entry { - struct Curl_addrinfo *addr; - /* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (doesn't time out) */ - time_t timestamp; - /* use-counter, use Curl_resolv_unlock to release reference */ - long inuse; -}; - -bool Curl_host_is_ipnum(const char *hostname); - -/* - * Curl_resolv() returns an entry with the info for the specified host - * and port. - * - * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after - * use, or we'll leak memory! - */ -/* return codes */ -enum resolve_t { - CURLRESOLV_TIMEDOUT = -2, - CURLRESOLV_ERROR = -1, - CURLRESOLV_RESOLVED = 0, - CURLRESOLV_PENDING = 1 -}; -enum resolve_t Curl_resolv(struct Curl_easy *data, - const char *hostname, - int port, - bool allowDOH, - struct Curl_dns_entry **dnsentry); -enum resolve_t Curl_resolv_timeout(struct Curl_easy *data, - const char *hostname, int port, - struct Curl_dns_entry **dnsentry, - timediff_t timeoutms); - -#ifdef ENABLE_IPV6 -/* - * Curl_ipv6works() returns TRUE if IPv6 seems to work. - */ -bool Curl_ipv6works(struct Curl_easy *data); -#else -#define Curl_ipv6works(x) FALSE -#endif - -/* - * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've - * been set and returns TRUE if they are OK. - */ -bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn); - - -/* - * Curl_getaddrinfo() is the generic low-level name resolve API within this - * source file. There are several versions of this function - for different - * name resolve layers (selected at build-time). They all take this same set - * of arguments - */ -struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data, - const char *hostname, - int port, - int *waitp); - - -/* unlock a previously resolved dns entry */ -void Curl_resolv_unlock(struct Curl_easy *data, - struct Curl_dns_entry *dns); - -/* init a new dns cache */ -void Curl_init_dnscache(struct Curl_hash *hash, int hashsize); - -/* prune old entries from the DNS cache */ -void Curl_hostcache_prune(struct Curl_easy *data); - -/* Return # of addresses in a Curl_addrinfo struct */ -int Curl_num_addresses(const struct Curl_addrinfo *addr); - -/* IPv4 threadsafe resolve function used for synch and asynch builds */ -struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port); - -CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_connect); - -/* - * Curl_addrinfo_callback() is used when we build with any asynch specialty. - * Handles end of async request processing. Inserts ai into hostcache when - * status is CURL_ASYNC_SUCCESS. Twiddles fields in conn to indicate async - * request completed whether successful or failed. - */ -CURLcode Curl_addrinfo_callback(struct Curl_easy *data, - int status, - struct Curl_addrinfo *ai); - -/* - * Curl_printable_address() returns a printable version of the 1st address - * given in the 'ip' argument. The result will be stored in the buf that is - * bufsize bytes big. - */ -void Curl_printable_address(const struct Curl_addrinfo *ip, - char *buf, size_t bufsize); - -/* - * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache. - * - * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. - * - * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after - * use, or we'll leak memory! - */ -struct Curl_dns_entry * -Curl_fetch_addr(struct Curl_easy *data, - const char *hostname, - int port); - -/* - * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache. - * - * Returns the Curl_dns_entry entry pointer or NULL if the storage failed. - */ -struct Curl_dns_entry * -Curl_cache_addr(struct Curl_easy *data, struct Curl_addrinfo *addr, - const char *hostname, int port); - -#ifndef INADDR_NONE -#define CURL_INADDR_NONE (in_addr_t) ~0 -#else -#define CURL_INADDR_NONE INADDR_NONE -#endif - -#ifdef HAVE_SIGSETJMP -/* Forward-declaration of variable defined in hostip.c. Beware this - * is a global and unique instance. This is used to store the return - * address that we can jump back to from inside a signal handler. - * This is not thread-safe stuff. - */ -extern sigjmp_buf curl_jmpenv; -#endif - -/* - * Function provided by the resolver backend to set DNS servers to use. - */ -CURLcode Curl_set_dns_servers(struct Curl_easy *data, char *servers); - -/* - * Function provided by the resolver backend to set - * outgoing interface to use for DNS requests - */ -CURLcode Curl_set_dns_interface(struct Curl_easy *data, - const char *interf); - -/* - * Function provided by the resolver backend to set - * local IPv4 address to use as source address for DNS requests - */ -CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data, - const char *local_ip4); - -/* - * Function provided by the resolver backend to set - * local IPv6 address to use as source address for DNS requests - */ -CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data, - const char *local_ip6); - -/* - * Clean off entries from the cache - */ -void Curl_hostcache_clean(struct Curl_easy *data, struct Curl_hash *hash); - -/* - * Populate the cache with specified entries from CURLOPT_RESOLVE. - */ -CURLcode Curl_loadhostpairs(struct Curl_easy *data); -CURLcode Curl_resolv_check(struct Curl_easy *data, - struct Curl_dns_entry **dns); -int Curl_resolv_getsock(struct Curl_easy *data, - curl_socket_t *socks); - -CURLcode Curl_resolver_error(struct Curl_easy *data); -#endif /* HEADER_CURL_HOSTIP_H */ +#ifndef HEADER_CURL_HOSTIP_H
+#define HEADER_CURL_HOSTIP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "hash.h"
+#include "curl_addrinfo.h"
+#include "timeval.h" /* for timediff_t */
+#include "asyn.h"
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+/* Allocate enough memory to hold the full name information structs and
+ * everything. OSF1 is known to require at least 8872 bytes. The buffer
+ * required for storing all possible aliases and IP numbers is according to
+ * Stevens' Unix Network Programming 2nd edition, p. 304: 8192 bytes!
+ */
+#define CURL_HOSTENT_SIZE 9000
+
+#define CURL_TIMEOUT_RESOLVE 300 /* when using asynch methods, we allow this
+ many seconds for a name resolve */
+
+#define CURL_ASYNC_SUCCESS CURLE_OK
+
+struct addrinfo;
+struct hostent;
+struct Curl_easy;
+struct connectdata;
+
+/*
+ * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
+ * Global DNS cache is general badness. Do not use. This will be removed in
+ * a future version. Use the share interface instead!
+ *
+ * Returns a struct Curl_hash pointer on success, NULL on failure.
+ */
+struct Curl_hash *Curl_global_host_cache_init(void);
+
+struct Curl_dns_entry {
+ struct Curl_addrinfo *addr;
+ /* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (doesn't time out) */
+ time_t timestamp;
+ /* use-counter, use Curl_resolv_unlock to release reference */
+ long inuse;
+};
+
+bool Curl_host_is_ipnum(const char *hostname);
+
+/*
+ * Curl_resolv() returns an entry with the info for the specified host
+ * and port.
+ *
+ * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
+ * use, or we'll leak memory!
+ */
+/* return codes */
+enum resolve_t {
+ CURLRESOLV_TIMEDOUT = -2,
+ CURLRESOLV_ERROR = -1,
+ CURLRESOLV_RESOLVED = 0,
+ CURLRESOLV_PENDING = 1
+};
+enum resolve_t Curl_resolv(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ bool allowDOH,
+ struct Curl_dns_entry **dnsentry);
+enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
+ const char *hostname, int port,
+ struct Curl_dns_entry **dnsentry,
+ timediff_t timeoutms);
+
+#ifdef ENABLE_IPV6
+/*
+ * Curl_ipv6works() returns TRUE if IPv6 seems to work.
+ */
+bool Curl_ipv6works(struct Curl_easy *data);
+#else
+#define Curl_ipv6works(x) FALSE
+#endif
+
+/*
+ * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
+ * been set and returns TRUE if they are OK.
+ */
+bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn);
+
+
+/*
+ * Curl_getaddrinfo() is the generic low-level name resolve API within this
+ * source file. There are several versions of this function - for different
+ * name resolve layers (selected at build-time). They all take this same set
+ * of arguments
+ */
+struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int *waitp);
+
+
+/* unlock a previously resolved dns entry */
+void Curl_resolv_unlock(struct Curl_easy *data,
+ struct Curl_dns_entry *dns);
+
+/* init a new dns cache */
+void Curl_init_dnscache(struct Curl_hash *hash, int hashsize);
+
+/* prune old entries from the DNS cache */
+void Curl_hostcache_prune(struct Curl_easy *data);
+
+/* Return # of addresses in a Curl_addrinfo struct */
+int Curl_num_addresses(const struct Curl_addrinfo *addr);
+
+/* IPv4 threadsafe resolve function used for synch and asynch builds */
+struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port);
+
+CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_connect);
+
+/*
+ * Curl_addrinfo_callback() is used when we build with any asynch specialty.
+ * Handles end of async request processing. Inserts ai into hostcache when
+ * status is CURL_ASYNC_SUCCESS. Twiddles fields in conn to indicate async
+ * request completed whether successful or failed.
+ */
+CURLcode Curl_addrinfo_callback(struct Curl_easy *data,
+ int status,
+ struct Curl_addrinfo *ai);
+
+/*
+ * Curl_printable_address() returns a printable version of the 1st address
+ * given in the 'ip' argument. The result will be stored in the buf that is
+ * bufsize bytes big.
+ */
+void Curl_printable_address(const struct Curl_addrinfo *ip,
+ char *buf, size_t bufsize);
+
+/*
+ * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache.
+ *
+ * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
+ *
+ * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
+ * use, or we'll leak memory!
+ */
+struct Curl_dns_entry *
+Curl_fetch_addr(struct Curl_easy *data,
+ const char *hostname,
+ int port);
+
+/*
+ * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
+ *
+ * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
+ */
+struct Curl_dns_entry *
+Curl_cache_addr(struct Curl_easy *data, struct Curl_addrinfo *addr,
+ const char *hostname, int port);
+
+#ifndef INADDR_NONE
+#define CURL_INADDR_NONE (in_addr_t) ~0
+#else
+#define CURL_INADDR_NONE INADDR_NONE
+#endif
+
+#ifdef HAVE_SIGSETJMP
+/* Forward-declaration of variable defined in hostip.c. Beware this
+ * is a global and unique instance. This is used to store the return
+ * address that we can jump back to from inside a signal handler.
+ * This is not thread-safe stuff.
+ */
+extern sigjmp_buf curl_jmpenv;
+#endif
+
+/*
+ * Function provided by the resolver backend to set DNS servers to use.
+ */
+CURLcode Curl_set_dns_servers(struct Curl_easy *data, char *servers);
+
+/*
+ * Function provided by the resolver backend to set
+ * outgoing interface to use for DNS requests
+ */
+CURLcode Curl_set_dns_interface(struct Curl_easy *data,
+ const char *interf);
+
+/*
+ * Function provided by the resolver backend to set
+ * local IPv4 address to use as source address for DNS requests
+ */
+CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
+ const char *local_ip4);
+
+/*
+ * Function provided by the resolver backend to set
+ * local IPv6 address to use as source address for DNS requests
+ */
+CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
+ const char *local_ip6);
+
+/*
+ * Clean off entries from the cache
+ */
+void Curl_hostcache_clean(struct Curl_easy *data, struct Curl_hash *hash);
+
+/*
+ * Populate the cache with specified entries from CURLOPT_RESOLVE.
+ */
+CURLcode Curl_loadhostpairs(struct Curl_easy *data);
+CURLcode Curl_resolv_check(struct Curl_easy *data,
+ struct Curl_dns_entry **dns);
+int Curl_resolv_getsock(struct Curl_easy *data,
+ curl_socket_t *socks);
+
+CURLcode Curl_resolver_error(struct Curl_easy *data);
+#endif /* HEADER_CURL_HOSTIP_H */
diff --git a/libs/libcurl/src/hostip4.c b/libs/libcurl/src/hostip4.c index 1dd54e879d..37d47b6444 100644 --- a/libs/libcurl/src/hostip4.c +++ b/libs/libcurl/src/hostip4.c @@ -1,302 +1,301 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -/*********************************************************************** - * Only for plain IPv4 builds - **********************************************************************/ -#ifdef CURLRES_IPV4 /* plain IPv4 code coming up */ - -#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 HAVE_PROCESS_H -#include <process.h> -#endif - -#include "urldata.h" -#include "sendf.h" -#include "hostip.h" -#include "hash.h" -#include "share.h" -#include "url.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* - * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've - * been set and returns TRUE if they are OK. - */ -bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn) -{ - (void)data; - if(conn->ip_version == CURL_IPRESOLVE_V6) - /* An IPv6 address was requested and we can't get/use one */ - return FALSE; - - return TRUE; /* OK, proceed */ -} - -#ifdef CURLRES_SYNCH - -/* - * Curl_getaddrinfo() - the IPv4 synchronous version. - * - * The original code to this function was from the Dancer source code, written - * by Bjorn Reese, it has since been patched and modified considerably. - * - * gethostbyname_r() is the thread-safe version of the gethostbyname() - * function. When we build for plain IPv4, we attempt to use this - * function. There are _three_ different gethostbyname_r() versions, and we - * detect which one this platform supports in the configure script and set up - * the HAVE_GETHOSTBYNAME_R_3, HAVE_GETHOSTBYNAME_R_5 or - * HAVE_GETHOSTBYNAME_R_6 defines accordingly. Note that HAVE_GETADDRBYNAME - * has the corresponding rules. This is primarily on *nix. Note that some unix - * flavours have thread-safe versions of the plain gethostbyname() etc. - * - */ -struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data, - const char *hostname, - int port, - int *waitp) -{ - struct Curl_addrinfo *ai = NULL; - -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif - - *waitp = 0; /* synchronous response only */ - - ai = Curl_ipv4_resolve_r(hostname, port); - if(!ai) - infof(data, "Curl_ipv4_resolve_r failed for %s", hostname); - - return ai; -} -#endif /* CURLRES_SYNCH */ -#endif /* CURLRES_IPV4 */ - -#if defined(CURLRES_IPV4) && \ - !defined(CURLRES_ARES) && !defined(CURLRES_AMIGA) - -/* - * Curl_ipv4_resolve_r() - ipv4 threadsafe resolver function. - * - * This is used for both synchronous and asynchronous resolver builds, - * implying that only threadsafe code and function calls may be used. - * - */ -struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, - int port) -{ -#if !defined(HAVE_GETADDRINFO_THREADSAFE) && defined(HAVE_GETHOSTBYNAME_R_3) - int res; -#endif - struct Curl_addrinfo *ai = NULL; - struct hostent *h = NULL; - struct hostent *buf = NULL; - -#if defined(HAVE_GETADDRINFO_THREADSAFE) - struct addrinfo hints; - char sbuf[12]; - char *sbufptr = NULL; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_INET; - hints.ai_socktype = SOCK_STREAM; - if(port) { - msnprintf(sbuf, sizeof(sbuf), "%d", port); - sbufptr = sbuf; - } - - (void)Curl_getaddrinfo_ex(hostname, sbufptr, &hints, &ai); - -#elif defined(HAVE_GETHOSTBYNAME_R) - /* - * gethostbyname_r() is the preferred resolve function for many platforms. - * Since there are three different versions of it, the following code is - * somewhat #ifdef-ridden. - */ - int h_errnop; - - buf = calloc(1, CURL_HOSTENT_SIZE); - if(!buf) - return NULL; /* major failure */ - /* - * The clearing of the buffer is a workaround for a gethostbyname_r bug in - * qnx nto and it is also _required_ for some of these functions on some - * platforms. - */ - -#if defined(HAVE_GETHOSTBYNAME_R_5) - /* Solaris, IRIX and more */ - h = gethostbyname_r(hostname, - (struct hostent *)buf, - (char *)buf + sizeof(struct hostent), - CURL_HOSTENT_SIZE - sizeof(struct hostent), - &h_errnop); - - /* If the buffer is too small, it returns NULL and sets errno to - * ERANGE. The errno is thread safe if this is compiled with - * -D_REENTRANT as then the 'errno' variable is a macro defined to get - * used properly for threads. - */ - - if(h) { - ; - } - else -#elif defined(HAVE_GETHOSTBYNAME_R_6) - /* Linux */ - - (void)gethostbyname_r(hostname, - (struct hostent *)buf, - (char *)buf + sizeof(struct hostent), - CURL_HOSTENT_SIZE - sizeof(struct hostent), - &h, /* DIFFERENCE */ - &h_errnop); - /* Redhat 8, using glibc 2.2.93 changed the behavior. Now all of a - * sudden this function returns EAGAIN if the given buffer size is too - * small. Previous versions are known to return ERANGE for the same - * problem. - * - * This wouldn't be such a big problem if older versions wouldn't - * sometimes return EAGAIN on a common failure case. Alas, we can't - * assume that EAGAIN *or* ERANGE means ERANGE for any given version of - * glibc. - * - * For now, we do that and thus we may call the function repeatedly and - * fail for older glibc versions that return EAGAIN, until we run out of - * buffer size (step_size grows beyond CURL_HOSTENT_SIZE). - * - * If anyone has a better fix, please tell us! - * - * ------------------------------------------------------------------- - * - * On October 23rd 2003, Dan C dug up more details on the mysteries of - * gethostbyname_r() in glibc: - * - * In glibc 2.2.5 the interface is different (this has also been - * discovered in glibc 2.1.1-6 as shipped by Redhat 6). What I can't - * explain, is that tests performed on glibc 2.2.4-34 and 2.2.4-32 - * (shipped/upgraded by Redhat 7.2) don't show this behavior! - * - * In this "buggy" version, the return code is -1 on error and 'errno' - * is set to the ERANGE or EAGAIN code. Note that 'errno' is not a - * thread-safe variable. - */ - - if(!h) /* failure */ -#elif defined(HAVE_GETHOSTBYNAME_R_3) - /* AIX, Digital Unix/Tru64, HPUX 10, more? */ - - /* For AIX 4.3 or later, we don't use gethostbyname_r() at all, because of - * the plain fact that it does not return unique full buffers on each - * call, but instead several of the pointers in the hostent structs will - * point to the same actual data! This have the unfortunate down-side that - * our caching system breaks down horribly. Luckily for us though, AIX 4.3 - * and more recent versions have a "completely thread-safe"[*] libc where - * all the data is stored in thread-specific memory areas making calls to - * the plain old gethostbyname() work fine even for multi-threaded - * programs. - * - * This AIX 4.3 or later detection is all made in the configure script. - * - * Troels Walsted Hansen helped us work this out on March 3rd, 2003. - * - * [*] = much later we've found out that it isn't at all "completely - * thread-safe", but at least the gethostbyname() function is. - */ - - if(CURL_HOSTENT_SIZE >= - (sizeof(struct hostent) + sizeof(struct hostent_data))) { - - /* August 22nd, 2000: Albert Chin-A-Young brought an updated version - * that should work! September 20: Richard Prescott worked on the buffer - * size dilemma. - */ - - res = gethostbyname_r(hostname, - (struct hostent *)buf, - (struct hostent_data *)((char *)buf + - sizeof(struct hostent))); - h_errnop = SOCKERRNO; /* we don't deal with this, but set it anyway */ - } - else - res = -1; /* failure, too smallish buffer size */ - - if(!res) { /* success */ - - h = buf; /* result expected in h */ - - /* This is the worst kind of the different gethostbyname_r() interfaces. - * Since we don't know how big buffer this particular lookup required, - * we can't realloc down the huge alloc without doing closer analysis of - * the returned data. Thus, we always use CURL_HOSTENT_SIZE for every - * name lookup. Fixing this would require an extra malloc() and then - * calling Curl_addrinfo_copy() that subsequent realloc()s down the new - * memory area to the actually used amount. - */ - } - else -#endif /* HAVE_...BYNAME_R_5 || HAVE_...BYNAME_R_6 || HAVE_...BYNAME_R_3 */ - { - h = NULL; /* set return code to NULL */ - free(buf); - } -#else /* HAVE_GETADDRINFO_THREADSAFE || HAVE_GETHOSTBYNAME_R */ - /* - * Here is code for platforms that don't have a thread safe - * getaddrinfo() nor gethostbyname_r() function or for which - * gethostbyname() is the preferred one. - */ - h = gethostbyname((void *)hostname); -#endif /* HAVE_GETADDRINFO_THREADSAFE || HAVE_GETHOSTBYNAME_R */ - - if(h) { - ai = Curl_he2ai(h, port); - - if(buf) /* used a *_r() function */ - free(buf); - } - - return ai; -} -#endif /* defined(CURLRES_IPV4) && !defined(CURLRES_ARES) && - !defined(CURLRES_AMIGA) */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+/***********************************************************************
+ * Only for plain IPv4 builds
+ **********************************************************************/
+#ifdef CURLRES_IPV4 /* plain IPv4 code coming up */
+
+#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 "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "url.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
+ * been set and returns TRUE if they are OK.
+ */
+bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn)
+{
+ (void)data;
+ if(conn->ip_version == CURL_IPRESOLVE_V6)
+ /* An IPv6 address was requested and we can't get/use one */
+ return FALSE;
+
+ return TRUE; /* OK, proceed */
+}
+
+#ifdef CURLRES_SYNCH
+
+/*
+ * Curl_getaddrinfo() - the IPv4 synchronous version.
+ *
+ * The original code to this function was from the Dancer source code, written
+ * by Bjorn Reese, it has since been patched and modified considerably.
+ *
+ * gethostbyname_r() is the thread-safe version of the gethostbyname()
+ * function. When we build for plain IPv4, we attempt to use this
+ * function. There are _three_ different gethostbyname_r() versions, and we
+ * detect which one this platform supports in the configure script and set up
+ * the HAVE_GETHOSTBYNAME_R_3, HAVE_GETHOSTBYNAME_R_5 or
+ * HAVE_GETHOSTBYNAME_R_6 defines accordingly. Note that HAVE_GETADDRBYNAME
+ * has the corresponding rules. This is primarily on *nix. Note that some unix
+ * flavours have thread-safe versions of the plain gethostbyname() etc.
+ *
+ */
+struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int *waitp)
+{
+ struct Curl_addrinfo *ai = NULL;
+
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+ (void)data;
+#endif
+
+ *waitp = 0; /* synchronous response only */
+
+ ai = Curl_ipv4_resolve_r(hostname, port);
+ if(!ai)
+ infof(data, "Curl_ipv4_resolve_r failed for %s", hostname);
+
+ return ai;
+}
+#endif /* CURLRES_SYNCH */
+#endif /* CURLRES_IPV4 */
+
+#if defined(CURLRES_IPV4) && \
+ !defined(CURLRES_ARES) && !defined(CURLRES_AMIGA)
+
+/*
+ * Curl_ipv4_resolve_r() - ipv4 threadsafe resolver function.
+ *
+ * This is used for both synchronous and asynchronous resolver builds,
+ * implying that only threadsafe code and function calls may be used.
+ *
+ */
+struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname,
+ int port)
+{
+#if !(defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE)) && \
+ defined(HAVE_GETHOSTBYNAME_R_3)
+ int res;
+#endif
+ struct Curl_addrinfo *ai = NULL;
+ struct hostent *h = NULL;
+ struct hostent *buf = NULL;
+
+#if defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE)
+ struct addrinfo hints;
+ char sbuf[12];
+ char *sbufptr = NULL;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ if(port) {
+ msnprintf(sbuf, sizeof(sbuf), "%d", port);
+ sbufptr = sbuf;
+ }
+
+ (void)Curl_getaddrinfo_ex(hostname, sbufptr, &hints, &ai);
+
+#elif defined(HAVE_GETHOSTBYNAME_R)
+ /*
+ * gethostbyname_r() is the preferred resolve function for many platforms.
+ * Since there are three different versions of it, the following code is
+ * somewhat #ifdef-ridden.
+ */
+ int h_errnop;
+
+ buf = calloc(1, CURL_HOSTENT_SIZE);
+ if(!buf)
+ return NULL; /* major failure */
+ /*
+ * The clearing of the buffer is a workaround for a gethostbyname_r bug in
+ * qnx nto and it is also _required_ for some of these functions on some
+ * platforms.
+ */
+
+#if defined(HAVE_GETHOSTBYNAME_R_5)
+ /* Solaris, IRIX and more */
+ h = gethostbyname_r(hostname,
+ (struct hostent *)buf,
+ (char *)buf + sizeof(struct hostent),
+ CURL_HOSTENT_SIZE - sizeof(struct hostent),
+ &h_errnop);
+
+ /* If the buffer is too small, it returns NULL and sets errno to
+ * ERANGE. The errno is thread safe if this is compiled with
+ * -D_REENTRANT as then the 'errno' variable is a macro defined to get
+ * used properly for threads.
+ */
+
+ if(h) {
+ ;
+ }
+ else
+#elif defined(HAVE_GETHOSTBYNAME_R_6)
+ /* Linux */
+
+ (void)gethostbyname_r(hostname,
+ (struct hostent *)buf,
+ (char *)buf + sizeof(struct hostent),
+ CURL_HOSTENT_SIZE - sizeof(struct hostent),
+ &h, /* DIFFERENCE */
+ &h_errnop);
+ /* Redhat 8, using glibc 2.2.93 changed the behavior. Now all of a
+ * sudden this function returns EAGAIN if the given buffer size is too
+ * small. Previous versions are known to return ERANGE for the same
+ * problem.
+ *
+ * This wouldn't be such a big problem if older versions wouldn't
+ * sometimes return EAGAIN on a common failure case. Alas, we can't
+ * assume that EAGAIN *or* ERANGE means ERANGE for any given version of
+ * glibc.
+ *
+ * For now, we do that and thus we may call the function repeatedly and
+ * fail for older glibc versions that return EAGAIN, until we run out of
+ * buffer size (step_size grows beyond CURL_HOSTENT_SIZE).
+ *
+ * If anyone has a better fix, please tell us!
+ *
+ * -------------------------------------------------------------------
+ *
+ * On October 23rd 2003, Dan C dug up more details on the mysteries of
+ * gethostbyname_r() in glibc:
+ *
+ * In glibc 2.2.5 the interface is different (this has also been
+ * discovered in glibc 2.1.1-6 as shipped by Redhat 6). What I can't
+ * explain, is that tests performed on glibc 2.2.4-34 and 2.2.4-32
+ * (shipped/upgraded by Redhat 7.2) don't show this behavior!
+ *
+ * In this "buggy" version, the return code is -1 on error and 'errno'
+ * is set to the ERANGE or EAGAIN code. Note that 'errno' is not a
+ * thread-safe variable.
+ */
+
+ if(!h) /* failure */
+#elif defined(HAVE_GETHOSTBYNAME_R_3)
+ /* AIX, Digital Unix/Tru64, HPUX 10, more? */
+
+ /* For AIX 4.3 or later, we don't use gethostbyname_r() at all, because of
+ * the plain fact that it does not return unique full buffers on each
+ * call, but instead several of the pointers in the hostent structs will
+ * point to the same actual data! This have the unfortunate down-side that
+ * our caching system breaks down horribly. Luckily for us though, AIX 4.3
+ * and more recent versions have a "completely thread-safe"[*] libc where
+ * all the data is stored in thread-specific memory areas making calls to
+ * the plain old gethostbyname() work fine even for multi-threaded
+ * programs.
+ *
+ * This AIX 4.3 or later detection is all made in the configure script.
+ *
+ * Troels Walsted Hansen helped us work this out on March 3rd, 2003.
+ *
+ * [*] = much later we've found out that it isn't at all "completely
+ * thread-safe", but at least the gethostbyname() function is.
+ */
+
+ if(CURL_HOSTENT_SIZE >=
+ (sizeof(struct hostent) + sizeof(struct hostent_data))) {
+
+ /* August 22nd, 2000: Albert Chin-A-Young brought an updated version
+ * that should work! September 20: Richard Prescott worked on the buffer
+ * size dilemma.
+ */
+
+ res = gethostbyname_r(hostname,
+ (struct hostent *)buf,
+ (struct hostent_data *)((char *)buf +
+ sizeof(struct hostent)));
+ h_errnop = SOCKERRNO; /* we don't deal with this, but set it anyway */
+ }
+ else
+ res = -1; /* failure, too smallish buffer size */
+
+ if(!res) { /* success */
+
+ h = buf; /* result expected in h */
+
+ /* This is the worst kind of the different gethostbyname_r() interfaces.
+ * Since we don't know how big buffer this particular lookup required,
+ * we can't realloc down the huge alloc without doing closer analysis of
+ * the returned data. Thus, we always use CURL_HOSTENT_SIZE for every
+ * name lookup. Fixing this would require an extra malloc() and then
+ * calling Curl_addrinfo_copy() that subsequent realloc()s down the new
+ * memory area to the actually used amount.
+ */
+ }
+ else
+#endif /* HAVE_...BYNAME_R_5 || HAVE_...BYNAME_R_6 || HAVE_...BYNAME_R_3 */
+ {
+ h = NULL; /* set return code to NULL */
+ free(buf);
+ }
+#else /* (HAVE_GETADDRINFO && HAVE_GETADDRINFO_THREADSAFE) ||
+ HAVE_GETHOSTBYNAME_R */
+ /*
+ * Here is code for platforms that don't have a thread safe
+ * getaddrinfo() nor gethostbyname_r() function or for which
+ * gethostbyname() is the preferred one.
+ */
+ h = gethostbyname((void *)hostname);
+#endif /* (HAVE_GETADDRINFO && HAVE_GETADDRINFO_THREADSAFE) ||
+ HAVE_GETHOSTBYNAME_R */
+
+ if(h) {
+ ai = Curl_he2ai(h, port);
+
+ if(buf) /* used a *_r() function */
+ free(buf);
+ }
+
+ return ai;
+}
+#endif /* defined(CURLRES_IPV4) && !defined(CURLRES_ARES) &&
+ !defined(CURLRES_AMIGA) */
diff --git a/libs/libcurl/src/hostip6.c b/libs/libcurl/src/hostip6.c index c62c254c72..79f20f484e 100644 --- a/libs/libcurl/src/hostip6.c +++ b/libs/libcurl/src/hostip6.c @@ -1,162 +1,158 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -/*********************************************************************** - * Only for IPv6-enabled builds - **********************************************************************/ -#ifdef CURLRES_IPV6 - -#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 HAVE_PROCESS_H -#include <process.h> -#endif - -#include "urldata.h" -#include "sendf.h" -#include "hostip.h" -#include "hash.h" -#include "share.h" -#include "url.h" -#include "inet_pton.h" -#include "connect.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* - * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've - * been set and returns TRUE if they are OK. - */ -bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn) -{ - if(conn->ip_version == CURL_IPRESOLVE_V6) - return Curl_ipv6works(data); - - return TRUE; -} - -#if defined(CURLRES_SYNCH) - -#ifdef DEBUG_ADDRINFO -static void dump_addrinfo(struct connectdata *conn, - const struct Curl_addrinfo *ai) -{ - printf("dump_addrinfo:\n"); - for(; ai; ai = ai->ai_next) { - char buf[INET6_ADDRSTRLEN]; - printf(" fam %2d, CNAME %s, ", - ai->ai_family, ai->ai_canonname ? ai->ai_canonname : "<none>"); - Curl_printable_address(ai, buf, sizeof(buf)); - printf("%s\n", buf); - } -} -#else -#define dump_addrinfo(x,y) Curl_nop_stmt -#endif - -/* - * Curl_getaddrinfo() when built IPv6-enabled (non-threading and - * non-ares version). - * - * Returns name information about the given hostname and port number. If - * successful, the 'addrinfo' 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_getaddrinfo(struct Curl_easy *data, - const char *hostname, - int port, - int *waitp) -{ - struct addrinfo hints; - struct Curl_addrinfo *res; - int error; - char sbuf[12]; - char *sbufptr = NULL; -#ifndef USE_RESOLVE_ON_IPS - char addrbuf[128]; -#endif - int pf = PF_INET; - - *waitp = 0; /* synchronous response only */ - - if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) - /* The stack seems to be IPv6-enabled */ - pf = PF_UNSPEC; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = pf; - hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ? - SOCK_STREAM : SOCK_DGRAM; - -#ifndef USE_RESOLVE_ON_IPS - /* - * The AI_NUMERICHOST must not be set to get synthesized IPv6 address from - * an IPv4 address on iOS and Mac OS X. - */ - if((1 == Curl_inet_pton(AF_INET, hostname, addrbuf)) || - (1 == Curl_inet_pton(AF_INET6, hostname, addrbuf))) { - /* the given address is numerical only, prevent a reverse lookup */ - hints.ai_flags = AI_NUMERICHOST; - } -#endif - - if(port) { - msnprintf(sbuf, sizeof(sbuf), "%d", port); - sbufptr = sbuf; - } - - error = Curl_getaddrinfo_ex(hostname, sbufptr, &hints, &res); - if(error) { - infof(data, "getaddrinfo(3) failed for %s:%d", hostname, port); - return NULL; - } - - if(port) { - Curl_addrinfo_set_port(res, port); - } - - dump_addrinfo(conn, res); - - return res; -} -#endif /* CURLRES_SYNCH */ - -#endif /* CURLRES_IPV6 */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+/***********************************************************************
+ * Only for IPv6-enabled builds
+ **********************************************************************/
+#ifdef CURLRES_IPV6
+
+#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 "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "url.h"
+#include "inet_pton.h"
+#include "connect.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
+ * been set and returns TRUE if they are OK.
+ */
+bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn)
+{
+ if(conn->ip_version == CURL_IPRESOLVE_V6)
+ return Curl_ipv6works(data);
+
+ return TRUE;
+}
+
+#if defined(CURLRES_SYNCH)
+
+#ifdef DEBUG_ADDRINFO
+static void dump_addrinfo(struct connectdata *conn,
+ const struct Curl_addrinfo *ai)
+{
+ printf("dump_addrinfo:\n");
+ for(; ai; ai = ai->ai_next) {
+ char buf[INET6_ADDRSTRLEN];
+ printf(" fam %2d, CNAME %s, ",
+ ai->ai_family, ai->ai_canonname ? ai->ai_canonname : "<none>");
+ Curl_printable_address(ai, buf, sizeof(buf));
+ printf("%s\n", buf);
+ }
+}
+#else
+#define dump_addrinfo(x,y) Curl_nop_stmt
+#endif
+
+/*
+ * Curl_getaddrinfo() when built IPv6-enabled (non-threading and
+ * non-ares version).
+ *
+ * Returns name information about the given hostname and port number. If
+ * successful, the 'addrinfo' 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_getaddrinfo(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int *waitp)
+{
+ struct addrinfo hints;
+ struct Curl_addrinfo *res;
+ int error;
+ char sbuf[12];
+ char *sbufptr = NULL;
+#ifndef USE_RESOLVE_ON_IPS
+ char addrbuf[128];
+#endif
+ int pf = PF_INET;
+
+ *waitp = 0; /* synchronous response only */
+
+ if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data))
+ /* The stack seems to be IPv6-enabled */
+ pf = PF_UNSPEC;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = pf;
+ hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ?
+ SOCK_STREAM : SOCK_DGRAM;
+
+#ifndef USE_RESOLVE_ON_IPS
+ /*
+ * The AI_NUMERICHOST must not be set to get synthesized IPv6 address from
+ * an IPv4 address on iOS and Mac OS X.
+ */
+ if((1 == Curl_inet_pton(AF_INET, hostname, addrbuf)) ||
+ (1 == Curl_inet_pton(AF_INET6, hostname, addrbuf))) {
+ /* the given address is numerical only, prevent a reverse lookup */
+ hints.ai_flags = AI_NUMERICHOST;
+ }
+#endif
+
+ if(port) {
+ msnprintf(sbuf, sizeof(sbuf), "%d", port);
+ sbufptr = sbuf;
+ }
+
+ error = Curl_getaddrinfo_ex(hostname, sbufptr, &hints, &res);
+ if(error) {
+ infof(data, "getaddrinfo(3) failed for %s:%d", hostname, port);
+ return NULL;
+ }
+
+ if(port) {
+ Curl_addrinfo_set_port(res, port);
+ }
+
+ dump_addrinfo(conn, res);
+
+ return res;
+}
+#endif /* CURLRES_SYNCH */
+
+#endif /* CURLRES_IPV6 */
diff --git a/libs/libcurl/src/hostsyn.c b/libs/libcurl/src/hostsyn.c index ee54363bf9..6aaa2f87e4 100644 --- a/libs/libcurl/src/hostsyn.c +++ b/libs/libcurl/src/hostsyn.c @@ -1,108 +1,104 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -/*********************************************************************** - * Only for builds using synchronous name resolves - **********************************************************************/ -#ifdef CURLRES_SYNCH - -#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 HAVE_PROCESS_H -#include <process.h> -#endif - -#include "urldata.h" -#include "sendf.h" -#include "hostip.h" -#include "hash.h" -#include "share.h" -#include "url.h" -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -/* - * Function provided by the resolver backend to set DNS servers to use. - */ -CURLcode Curl_set_dns_servers(struct Curl_easy *data, - char *servers) -{ - (void)data; - (void)servers; - return CURLE_NOT_BUILT_IN; - -} - -/* - * Function provided by the resolver backend to set - * outgoing interface to use for DNS requests - */ -CURLcode Curl_set_dns_interface(struct Curl_easy *data, - const char *interf) -{ - (void)data; - (void)interf; - return CURLE_NOT_BUILT_IN; -} - -/* - * Function provided by the resolver backend to set - * local IPv4 address to use as source address for DNS requests - */ -CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data, - const char *local_ip4) -{ - (void)data; - (void)local_ip4; - return CURLE_NOT_BUILT_IN; -} - -/* - * Function provided by the resolver backend to set - * local IPv6 address to use as source address for DNS requests - */ -CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data, - const char *local_ip6) -{ - (void)data; - (void)local_ip6; - return CURLE_NOT_BUILT_IN; -} - -#endif /* truly sync */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+/***********************************************************************
+ * Only for builds using synchronous name resolves
+ **********************************************************************/
+#ifdef CURLRES_SYNCH
+
+#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 "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "url.h"
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/*
+ * Function provided by the resolver backend to set DNS servers to use.
+ */
+CURLcode Curl_set_dns_servers(struct Curl_easy *data,
+ char *servers)
+{
+ (void)data;
+ (void)servers;
+ return CURLE_NOT_BUILT_IN;
+
+}
+
+/*
+ * Function provided by the resolver backend to set
+ * outgoing interface to use for DNS requests
+ */
+CURLcode Curl_set_dns_interface(struct Curl_easy *data,
+ const char *interf)
+{
+ (void)data;
+ (void)interf;
+ return CURLE_NOT_BUILT_IN;
+}
+
+/*
+ * Function provided by the resolver backend to set
+ * local IPv4 address to use as source address for DNS requests
+ */
+CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
+ const char *local_ip4)
+{
+ (void)data;
+ (void)local_ip4;
+ return CURLE_NOT_BUILT_IN;
+}
+
+/*
+ * Function provided by the resolver backend to set
+ * local IPv6 address to use as source address for DNS requests
+ */
+CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
+ const char *local_ip6)
+{
+ (void)data;
+ (void)local_ip6;
+ return CURLE_NOT_BUILT_IN;
+}
+
+#endif /* truly sync */
diff --git a/libs/libcurl/src/hsts.c b/libs/libcurl/src/hsts.c index e3b686ebfa..6ce3f72890 100644 --- a/libs/libcurl/src/hsts.c +++ b/libs/libcurl/src/hsts.c @@ -1,555 +1,554 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2020 - 2022, 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 Strict-Transport-Security header is defined in RFC 6797: - * https://datatracker.ietf.org/doc/html/rfc6797 - */ -#include "curl_setup.h" - -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HSTS) -#include <curl/curl.h> -#include "urldata.h" -#include "llist.h" -#include "hsts.h" -#include "curl_get_line.h" -#include "strcase.h" -#include "sendf.h" -#include "strtoofft.h" -#include "parsedate.h" -#include "fopen.h" -#include "rename.h" -#include "strtoofft.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#define MAX_HSTS_LINE 4095 -#define MAX_HSTS_HOSTLEN 256 -#define MAX_HSTS_HOSTLENSTR "256" -#define MAX_HSTS_DATELEN 64 -#define MAX_HSTS_DATELENSTR "64" -#define UNLIMITED "unlimited" - -#ifdef DEBUGBUILD -/* to play well with debug builds, we can *set* a fixed time this will - return */ -time_t deltatime; /* allow for "adjustments" for unit test purposes */ -static time_t debugtime(void *unused) -{ - char *timestr = getenv("CURL_TIME"); - (void)unused; - if(timestr) { - curl_off_t val; - (void)curlx_strtoofft(timestr, NULL, 10, &val); - - val += (curl_off_t)deltatime; - return (time_t)val; - } - return time(NULL); -} -#define time(x) debugtime(x) -#endif - -struct hsts *Curl_hsts_init(void) -{ - struct hsts *h = calloc(sizeof(struct hsts), 1); - if(h) { - Curl_llist_init(&h->list, NULL); - } - return h; -} - -static void hsts_free(struct stsentry *e) -{ - free((char *)e->host); - free(e); -} - -void Curl_hsts_cleanup(struct hsts **hp) -{ - struct hsts *h = *hp; - if(h) { - struct Curl_llist_element *e; - struct Curl_llist_element *n; - for(e = h->list.head; e; e = n) { - struct stsentry *sts = e->ptr; - n = e->next; - hsts_free(sts); - } - free(h->filename); - free(h); - *hp = NULL; - } -} - -static struct stsentry *hsts_entry(void) -{ - return calloc(sizeof(struct stsentry), 1); -} - -static CURLcode hsts_create(struct hsts *h, - const char *hostname, - bool subdomains, - curl_off_t expires) -{ - struct stsentry *sts = hsts_entry(); - char *duphost; - size_t hlen; - if(!sts) - return CURLE_OUT_OF_MEMORY; - - duphost = strdup(hostname); - if(!duphost) { - free(sts); - return CURLE_OUT_OF_MEMORY; - } - - hlen = strlen(duphost); - if(duphost[hlen - 1] == '.') - /* strip off trailing any dot */ - duphost[--hlen] = 0; - - sts->host = duphost; - sts->expires = expires; - sts->includeSubDomains = subdomains; - Curl_llist_insert_next(&h->list, h->list.tail, sts, &sts->node); - return CURLE_OK; -} - -CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, - const char *header) -{ - const char *p = header; - curl_off_t expires = 0; - bool gotma = FALSE; - bool gotinc = FALSE; - bool subdomains = FALSE; - struct stsentry *sts; - time_t now = time(NULL); - - if(Curl_host_is_ipnum(hostname)) - /* "explicit IP address identification of all forms is excluded." - / RFC 6797 */ - return CURLE_OK; - - do { - while(*p && ISBLANK(*p)) - p++; - if(Curl_strncasecompare("max-age=", p, 8)) { - bool quoted = FALSE; - CURLofft offt; - char *endp; - - if(gotma) - return CURLE_BAD_FUNCTION_ARGUMENT; - - p += 8; - while(*p && ISBLANK(*p)) - p++; - if(*p == '\"') { - p++; - quoted = TRUE; - } - offt = curlx_strtoofft(p, &endp, 10, &expires); - if(offt == CURL_OFFT_FLOW) - expires = CURL_OFF_T_MAX; - else if(offt) - /* invalid max-age */ - return CURLE_BAD_FUNCTION_ARGUMENT; - p = endp; - if(quoted) { - if(*p != '\"') - return CURLE_BAD_FUNCTION_ARGUMENT; - p++; - } - gotma = TRUE; - } - else if(Curl_strncasecompare("includesubdomains", p, 17)) { - if(gotinc) - return CURLE_BAD_FUNCTION_ARGUMENT; - subdomains = TRUE; - p += 17; - gotinc = TRUE; - } - else { - /* unknown directive, do a lame attempt to skip */ - while(*p && (*p != ';')) - p++; - } - - while(*p && ISBLANK(*p)) - p++; - if(*p == ';') - p++; - } while (*p); - - if(!gotma) - /* max-age is mandatory */ - return CURLE_BAD_FUNCTION_ARGUMENT; - - if(!expires) { - /* remove the entry if present verbatim (without subdomain match) */ - sts = Curl_hsts(h, hostname, FALSE); - if(sts) { - Curl_llist_remove(&h->list, &sts->node, NULL); - hsts_free(sts); - } - return CURLE_OK; - } - - if(CURL_OFF_T_MAX - now < expires) - /* would overflow, use maximum value */ - expires = CURL_OFF_T_MAX; - else - expires += now; - - /* check if it already exists */ - sts = Curl_hsts(h, hostname, FALSE); - if(sts) { - /* just update these fields */ - sts->expires = expires; - sts->includeSubDomains = subdomains; - } - else - return hsts_create(h, hostname, subdomains, expires); - - return CURLE_OK; -} - -/* - * Return TRUE if the given host name is currently an HSTS one. - * - * The 'subdomain' argument tells the function if subdomain matching should be - * attempted. - */ -struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, - bool subdomain) -{ - if(h) { - char buffer[MAX_HSTS_HOSTLEN + 1]; - time_t now = time(NULL); - size_t hlen = strlen(hostname); - struct Curl_llist_element *e; - struct Curl_llist_element *n; - - if((hlen > MAX_HSTS_HOSTLEN) || !hlen) - return NULL; - memcpy(buffer, hostname, hlen); - if(hostname[hlen-1] == '.') - /* remove the trailing dot */ - --hlen; - buffer[hlen] = 0; - hostname = buffer; - - for(e = h->list.head; e; e = n) { - struct stsentry *sts = e->ptr; - n = e->next; - if(sts->expires <= now) { - /* remove expired entries */ - Curl_llist_remove(&h->list, &sts->node, NULL); - hsts_free(sts); - continue; - } - if(subdomain && sts->includeSubDomains) { - size_t ntail = strlen(sts->host); - if(ntail < hlen) { - size_t offs = hlen - ntail; - if((hostname[offs-1] == '.') && - Curl_strncasecompare(&hostname[offs], sts->host, ntail)) - return sts; - } - } - if(Curl_strcasecompare(hostname, sts->host)) - return sts; - } - } - return NULL; /* no match */ -} - -/* - * Send this HSTS entry to the write callback. - */ -static CURLcode hsts_push(struct Curl_easy *data, - struct curl_index *i, - struct stsentry *sts, - bool *stop) -{ - struct curl_hstsentry e; - CURLSTScode sc; - struct tm stamp; - CURLcode result; - - e.name = (char *)sts->host; - e.namelen = strlen(sts->host); - e.includeSubDomains = sts->includeSubDomains; - - if(sts->expires != TIME_T_MAX) { - result = Curl_gmtime((time_t)sts->expires, &stamp); - if(result) - return result; - - msnprintf(e.expire, sizeof(e.expire), "%d%02d%02d %02d:%02d:%02d", - stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday, - stamp.tm_hour, stamp.tm_min, stamp.tm_sec); - } - else - strcpy(e.expire, UNLIMITED); - - sc = data->set.hsts_write(data, &e, i, - data->set.hsts_write_userp); - *stop = (sc != CURLSTS_OK); - return sc == CURLSTS_FAIL ? CURLE_BAD_FUNCTION_ARGUMENT : CURLE_OK; -} - -/* - * Write this single hsts entry to a single output line - */ -static CURLcode hsts_out(struct stsentry *sts, FILE *fp) -{ - struct tm stamp; - if(sts->expires != TIME_T_MAX) { - CURLcode result = Curl_gmtime((time_t)sts->expires, &stamp); - if(result) - return result; - fprintf(fp, "%s%s \"%d%02d%02d %02d:%02d:%02d\"\n", - sts->includeSubDomains ? ".": "", sts->host, - stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday, - stamp.tm_hour, stamp.tm_min, stamp.tm_sec); - } - else - fprintf(fp, "%s%s \"%s\"\n", - sts->includeSubDomains ? ".": "", sts->host, UNLIMITED); - return CURLE_OK; -} - - -/* - * Curl_https_save() writes the HSTS cache to file and callback. - */ -CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h, - const char *file) -{ - struct Curl_llist_element *e; - struct Curl_llist_element *n; - CURLcode result = CURLE_OK; - FILE *out; - char *tempstore = NULL; - - if(!h) - /* no cache activated */ - return CURLE_OK; - - /* if no new name is given, use the one we stored from the load */ - if(!file && h->filename) - file = h->filename; - - if((h->flags & CURLHSTS_READONLYFILE) || !file || !file[0]) - /* marked as read-only, no file or zero length file name */ - goto skipsave; - - result = Curl_fopen(data, file, &out, &tempstore); - if(!result) { - fputs("# Your HSTS cache. https://curl.se/docs/hsts.html\n" - "# This file was generated by libcurl! Edit at your own risk.\n", - out); - for(e = h->list.head; e; e = n) { - struct stsentry *sts = e->ptr; - n = e->next; - result = hsts_out(sts, out); - if(result) - break; - } - fclose(out); - if(!result && tempstore && Curl_rename(tempstore, file)) - result = CURLE_WRITE_ERROR; - - if(result && tempstore) - unlink(tempstore); - } - free(tempstore); - skipsave: - if(data->set.hsts_write) { - /* if there's a write callback */ - struct curl_index i; /* count */ - i.total = h->list.size; - i.index = 0; - for(e = h->list.head; e; e = n) { - struct stsentry *sts = e->ptr; - bool stop; - n = e->next; - result = hsts_push(data, &i, sts, &stop); - if(result || stop) - break; - i.index++; - } - } - return result; -} - -/* only returns SERIOUS errors */ -static CURLcode hsts_add(struct hsts *h, char *line) -{ - /* Example lines: - example.com "20191231 10:00:00" - .example.net "20191231 10:00:00" - */ - char host[MAX_HSTS_HOSTLEN + 1]; - char date[MAX_HSTS_DATELEN + 1]; - int rc; - - rc = sscanf(line, - "%" MAX_HSTS_HOSTLENSTR "s \"%" MAX_HSTS_DATELENSTR "[^\"]\"", - host, date); - if(2 == rc) { - time_t expires = strcmp(date, UNLIMITED) ? Curl_getdate_capped(date) : - TIME_T_MAX; - CURLcode result; - char *p = host; - bool subdomain = FALSE; - if(p[0] == '.') { - p++; - subdomain = TRUE; - } - result = hsts_create(h, p, subdomain, expires); - if(result) - return result; - } - - return CURLE_OK; -} - -/* - * Load HSTS data from callback. - * - */ -static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h) -{ - /* if the HSTS read callback is set, use it */ - if(data->set.hsts_read) { - CURLSTScode sc; - DEBUGASSERT(h); - do { - char buffer[MAX_HSTS_HOSTLEN + 1]; - struct curl_hstsentry e; - e.name = buffer; - e.namelen = sizeof(buffer)-1; - e.includeSubDomains = FALSE; /* default */ - e.expire[0] = 0; - e.name[0] = 0; /* just to make it clean */ - sc = data->set.hsts_read(data, &e, data->set.hsts_read_userp); - if(sc == CURLSTS_OK) { - time_t expires; - CURLcode result; - if(!e.name[0]) - /* bail out if no name was stored */ - return CURLE_BAD_FUNCTION_ARGUMENT; - if(e.expire[0]) - expires = Curl_getdate_capped(e.expire); - else - expires = TIME_T_MAX; /* the end of time */ - result = hsts_create(h, e.name, - /* bitfield to bool conversion: */ - e.includeSubDomains ? TRUE : FALSE, - expires); - if(result) - return result; - } - else if(sc == CURLSTS_FAIL) - return CURLE_ABORTED_BY_CALLBACK; - } while(sc == CURLSTS_OK); - } - return CURLE_OK; -} - -/* - * Load the HSTS cache from the given file. The text based line-oriented file - * format is documented here: https://curl.se/docs/hsts.html - * - * This function only returns error on major problems that prevent hsts - * handling to work completely. It will ignore individual syntactical errors - * etc. - */ -static CURLcode hsts_load(struct hsts *h, const char *file) -{ - CURLcode result = CURLE_OK; - char *line = NULL; - FILE *fp; - - /* we need a private copy of the file name so that the hsts cache file - name survives an easy handle reset */ - free(h->filename); - h->filename = strdup(file); - if(!h->filename) - return CURLE_OUT_OF_MEMORY; - - fp = fopen(file, FOPEN_READTEXT); - if(fp) { - line = malloc(MAX_HSTS_LINE); - if(!line) - goto fail; - while(Curl_get_line(line, MAX_HSTS_LINE, fp)) { - char *lineptr = line; - while(*lineptr && ISBLANK(*lineptr)) - lineptr++; - if(*lineptr == '#') - /* skip commented lines */ - continue; - - hsts_add(h, lineptr); - } - free(line); /* free the line buffer */ - fclose(fp); - } - return result; - - fail: - Curl_safefree(h->filename); - fclose(fp); - return CURLE_OUT_OF_MEMORY; -} - -/* - * Curl_hsts_loadfile() loads HSTS from file - */ -CURLcode Curl_hsts_loadfile(struct Curl_easy *data, - struct hsts *h, const char *file) -{ - DEBUGASSERT(h); - (void)data; - return hsts_load(h, file); -} - -/* - * Curl_hsts_loadcb() loads HSTS from callback - */ -CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h) -{ - if(h) - return hsts_pull(data, h); - return CURLE_OK; -} - -#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2020 - 2022, 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 Strict-Transport-Security header is defined in RFC 6797:
+ * https://datatracker.ietf.org/doc/html/rfc6797
+ */
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HSTS)
+#include <curl/curl.h>
+#include "urldata.h"
+#include "llist.h"
+#include "hsts.h"
+#include "curl_get_line.h"
+#include "strcase.h"
+#include "sendf.h"
+#include "strtoofft.h"
+#include "parsedate.h"
+#include "fopen.h"
+#include "rename.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define MAX_HSTS_LINE 4095
+#define MAX_HSTS_HOSTLEN 256
+#define MAX_HSTS_HOSTLENSTR "256"
+#define MAX_HSTS_DATELEN 64
+#define MAX_HSTS_DATELENSTR "64"
+#define UNLIMITED "unlimited"
+
+#ifdef DEBUGBUILD
+/* to play well with debug builds, we can *set* a fixed time this will
+ return */
+time_t deltatime; /* allow for "adjustments" for unit test purposes */
+static time_t debugtime(void *unused)
+{
+ char *timestr = getenv("CURL_TIME");
+ (void)unused;
+ if(timestr) {
+ curl_off_t val;
+ (void)curlx_strtoofft(timestr, NULL, 10, &val);
+
+ val += (curl_off_t)deltatime;
+ return (time_t)val;
+ }
+ return time(NULL);
+}
+#define time(x) debugtime(x)
+#endif
+
+struct hsts *Curl_hsts_init(void)
+{
+ struct hsts *h = calloc(sizeof(struct hsts), 1);
+ if(h) {
+ Curl_llist_init(&h->list, NULL);
+ }
+ return h;
+}
+
+static void hsts_free(struct stsentry *e)
+{
+ free((char *)e->host);
+ free(e);
+}
+
+void Curl_hsts_cleanup(struct hsts **hp)
+{
+ struct hsts *h = *hp;
+ if(h) {
+ struct Curl_llist_element *e;
+ struct Curl_llist_element *n;
+ for(e = h->list.head; e; e = n) {
+ struct stsentry *sts = e->ptr;
+ n = e->next;
+ hsts_free(sts);
+ }
+ free(h->filename);
+ free(h);
+ *hp = NULL;
+ }
+}
+
+static struct stsentry *hsts_entry(void)
+{
+ return calloc(sizeof(struct stsentry), 1);
+}
+
+static CURLcode hsts_create(struct hsts *h,
+ const char *hostname,
+ bool subdomains,
+ curl_off_t expires)
+{
+ struct stsentry *sts = hsts_entry();
+ char *duphost;
+ size_t hlen;
+ if(!sts)
+ return CURLE_OUT_OF_MEMORY;
+
+ duphost = strdup(hostname);
+ if(!duphost) {
+ free(sts);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ hlen = strlen(duphost);
+ if(duphost[hlen - 1] == '.')
+ /* strip off trailing any dot */
+ duphost[--hlen] = 0;
+
+ sts->host = duphost;
+ sts->expires = expires;
+ sts->includeSubDomains = subdomains;
+ Curl_llist_insert_next(&h->list, h->list.tail, sts, &sts->node);
+ return CURLE_OK;
+}
+
+CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
+ const char *header)
+{
+ const char *p = header;
+ curl_off_t expires = 0;
+ bool gotma = FALSE;
+ bool gotinc = FALSE;
+ bool subdomains = FALSE;
+ struct stsentry *sts;
+ time_t now = time(NULL);
+
+ if(Curl_host_is_ipnum(hostname))
+ /* "explicit IP address identification of all forms is excluded."
+ / RFC 6797 */
+ return CURLE_OK;
+
+ do {
+ while(*p && ISBLANK(*p))
+ p++;
+ if(strncasecompare("max-age=", p, 8)) {
+ bool quoted = FALSE;
+ CURLofft offt;
+ char *endp;
+
+ if(gotma)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ p += 8;
+ while(*p && ISBLANK(*p))
+ p++;
+ if(*p == '\"') {
+ p++;
+ quoted = TRUE;
+ }
+ offt = curlx_strtoofft(p, &endp, 10, &expires);
+ if(offt == CURL_OFFT_FLOW)
+ expires = CURL_OFF_T_MAX;
+ else if(offt)
+ /* invalid max-age */
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ p = endp;
+ if(quoted) {
+ if(*p != '\"')
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ p++;
+ }
+ gotma = TRUE;
+ }
+ else if(strncasecompare("includesubdomains", p, 17)) {
+ if(gotinc)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ subdomains = TRUE;
+ p += 17;
+ gotinc = TRUE;
+ }
+ else {
+ /* unknown directive, do a lame attempt to skip */
+ while(*p && (*p != ';'))
+ p++;
+ }
+
+ while(*p && ISBLANK(*p))
+ p++;
+ if(*p == ';')
+ p++;
+ } while (*p);
+
+ if(!gotma)
+ /* max-age is mandatory */
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ if(!expires) {
+ /* remove the entry if present verbatim (without subdomain match) */
+ sts = Curl_hsts(h, hostname, FALSE);
+ if(sts) {
+ Curl_llist_remove(&h->list, &sts->node, NULL);
+ hsts_free(sts);
+ }
+ return CURLE_OK;
+ }
+
+ if(CURL_OFF_T_MAX - now < expires)
+ /* would overflow, use maximum value */
+ expires = CURL_OFF_T_MAX;
+ else
+ expires += now;
+
+ /* check if it already exists */
+ sts = Curl_hsts(h, hostname, FALSE);
+ if(sts) {
+ /* just update these fields */
+ sts->expires = expires;
+ sts->includeSubDomains = subdomains;
+ }
+ else
+ return hsts_create(h, hostname, subdomains, expires);
+
+ return CURLE_OK;
+}
+
+/*
+ * Return TRUE if the given host name is currently an HSTS one.
+ *
+ * The 'subdomain' argument tells the function if subdomain matching should be
+ * attempted.
+ */
+struct stsentry *Curl_hsts(struct hsts *h, const char *hostname,
+ bool subdomain)
+{
+ if(h) {
+ char buffer[MAX_HSTS_HOSTLEN + 1];
+ time_t now = time(NULL);
+ size_t hlen = strlen(hostname);
+ struct Curl_llist_element *e;
+ struct Curl_llist_element *n;
+
+ if((hlen > MAX_HSTS_HOSTLEN) || !hlen)
+ return NULL;
+ memcpy(buffer, hostname, hlen);
+ if(hostname[hlen-1] == '.')
+ /* remove the trailing dot */
+ --hlen;
+ buffer[hlen] = 0;
+ hostname = buffer;
+
+ for(e = h->list.head; e; e = n) {
+ struct stsentry *sts = e->ptr;
+ n = e->next;
+ if(sts->expires <= now) {
+ /* remove expired entries */
+ Curl_llist_remove(&h->list, &sts->node, NULL);
+ hsts_free(sts);
+ continue;
+ }
+ if(subdomain && sts->includeSubDomains) {
+ size_t ntail = strlen(sts->host);
+ if(ntail < hlen) {
+ size_t offs = hlen - ntail;
+ if((hostname[offs-1] == '.') &&
+ strncasecompare(&hostname[offs], sts->host, ntail))
+ return sts;
+ }
+ }
+ if(strcasecompare(hostname, sts->host))
+ return sts;
+ }
+ }
+ return NULL; /* no match */
+}
+
+/*
+ * Send this HSTS entry to the write callback.
+ */
+static CURLcode hsts_push(struct Curl_easy *data,
+ struct curl_index *i,
+ struct stsentry *sts,
+ bool *stop)
+{
+ struct curl_hstsentry e;
+ CURLSTScode sc;
+ struct tm stamp;
+ CURLcode result;
+
+ e.name = (char *)sts->host;
+ e.namelen = strlen(sts->host);
+ e.includeSubDomains = sts->includeSubDomains;
+
+ if(sts->expires != TIME_T_MAX) {
+ result = Curl_gmtime((time_t)sts->expires, &stamp);
+ if(result)
+ return result;
+
+ msnprintf(e.expire, sizeof(e.expire), "%d%02d%02d %02d:%02d:%02d",
+ stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
+ stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
+ }
+ else
+ strcpy(e.expire, UNLIMITED);
+
+ sc = data->set.hsts_write(data, &e, i,
+ data->set.hsts_write_userp);
+ *stop = (sc != CURLSTS_OK);
+ return sc == CURLSTS_FAIL ? CURLE_BAD_FUNCTION_ARGUMENT : CURLE_OK;
+}
+
+/*
+ * Write this single hsts entry to a single output line
+ */
+static CURLcode hsts_out(struct stsentry *sts, FILE *fp)
+{
+ struct tm stamp;
+ if(sts->expires != TIME_T_MAX) {
+ CURLcode result = Curl_gmtime((time_t)sts->expires, &stamp);
+ if(result)
+ return result;
+ fprintf(fp, "%s%s \"%d%02d%02d %02d:%02d:%02d\"\n",
+ sts->includeSubDomains ? ".": "", sts->host,
+ stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
+ stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
+ }
+ else
+ fprintf(fp, "%s%s \"%s\"\n",
+ sts->includeSubDomains ? ".": "", sts->host, UNLIMITED);
+ return CURLE_OK;
+}
+
+
+/*
+ * Curl_https_save() writes the HSTS cache to file and callback.
+ */
+CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
+ const char *file)
+{
+ struct Curl_llist_element *e;
+ struct Curl_llist_element *n;
+ CURLcode result = CURLE_OK;
+ FILE *out;
+ char *tempstore = NULL;
+
+ if(!h)
+ /* no cache activated */
+ return CURLE_OK;
+
+ /* if no new name is given, use the one we stored from the load */
+ if(!file && h->filename)
+ file = h->filename;
+
+ if((h->flags & CURLHSTS_READONLYFILE) || !file || !file[0])
+ /* marked as read-only, no file or zero length file name */
+ goto skipsave;
+
+ result = Curl_fopen(data, file, &out, &tempstore);
+ if(!result) {
+ fputs("# Your HSTS cache. https://curl.se/docs/hsts.html\n"
+ "# This file was generated by libcurl! Edit at your own risk.\n",
+ out);
+ for(e = h->list.head; e; e = n) {
+ struct stsentry *sts = e->ptr;
+ n = e->next;
+ result = hsts_out(sts, out);
+ if(result)
+ break;
+ }
+ fclose(out);
+ if(!result && tempstore && Curl_rename(tempstore, file))
+ result = CURLE_WRITE_ERROR;
+
+ if(result && tempstore)
+ unlink(tempstore);
+ }
+ free(tempstore);
+ skipsave:
+ if(data->set.hsts_write) {
+ /* if there's a write callback */
+ struct curl_index i; /* count */
+ i.total = h->list.size;
+ i.index = 0;
+ for(e = h->list.head; e; e = n) {
+ struct stsentry *sts = e->ptr;
+ bool stop;
+ n = e->next;
+ result = hsts_push(data, &i, sts, &stop);
+ if(result || stop)
+ break;
+ i.index++;
+ }
+ }
+ return result;
+}
+
+/* only returns SERIOUS errors */
+static CURLcode hsts_add(struct hsts *h, char *line)
+{
+ /* Example lines:
+ example.com "20191231 10:00:00"
+ .example.net "20191231 10:00:00"
+ */
+ char host[MAX_HSTS_HOSTLEN + 1];
+ char date[MAX_HSTS_DATELEN + 1];
+ int rc;
+
+ rc = sscanf(line,
+ "%" MAX_HSTS_HOSTLENSTR "s \"%" MAX_HSTS_DATELENSTR "[^\"]\"",
+ host, date);
+ if(2 == rc) {
+ time_t expires = strcmp(date, UNLIMITED) ? Curl_getdate_capped(date) :
+ TIME_T_MAX;
+ CURLcode result;
+ char *p = host;
+ bool subdomain = FALSE;
+ if(p[0] == '.') {
+ p++;
+ subdomain = TRUE;
+ }
+ result = hsts_create(h, p, subdomain, expires);
+ if(result)
+ return result;
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Load HSTS data from callback.
+ *
+ */
+static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h)
+{
+ /* if the HSTS read callback is set, use it */
+ if(data->set.hsts_read) {
+ CURLSTScode sc;
+ DEBUGASSERT(h);
+ do {
+ char buffer[MAX_HSTS_HOSTLEN + 1];
+ struct curl_hstsentry e;
+ e.name = buffer;
+ e.namelen = sizeof(buffer)-1;
+ e.includeSubDomains = FALSE; /* default */
+ e.expire[0] = 0;
+ e.name[0] = 0; /* just to make it clean */
+ sc = data->set.hsts_read(data, &e, data->set.hsts_read_userp);
+ if(sc == CURLSTS_OK) {
+ time_t expires;
+ CURLcode result;
+ if(!e.name[0])
+ /* bail out if no name was stored */
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ if(e.expire[0])
+ expires = Curl_getdate_capped(e.expire);
+ else
+ expires = TIME_T_MAX; /* the end of time */
+ result = hsts_create(h, e.name,
+ /* bitfield to bool conversion: */
+ e.includeSubDomains ? TRUE : FALSE,
+ expires);
+ if(result)
+ return result;
+ }
+ else if(sc == CURLSTS_FAIL)
+ return CURLE_ABORTED_BY_CALLBACK;
+ } while(sc == CURLSTS_OK);
+ }
+ return CURLE_OK;
+}
+
+/*
+ * Load the HSTS cache from the given file. The text based line-oriented file
+ * format is documented here: https://curl.se/docs/hsts.html
+ *
+ * This function only returns error on major problems that prevent hsts
+ * handling to work completely. It will ignore individual syntactical errors
+ * etc.
+ */
+static CURLcode hsts_load(struct hsts *h, const char *file)
+{
+ CURLcode result = CURLE_OK;
+ char *line = NULL;
+ FILE *fp;
+
+ /* we need a private copy of the file name so that the hsts cache file
+ name survives an easy handle reset */
+ free(h->filename);
+ h->filename = strdup(file);
+ if(!h->filename)
+ return CURLE_OUT_OF_MEMORY;
+
+ fp = fopen(file, FOPEN_READTEXT);
+ if(fp) {
+ line = malloc(MAX_HSTS_LINE);
+ if(!line)
+ goto fail;
+ while(Curl_get_line(line, MAX_HSTS_LINE, fp)) {
+ char *lineptr = line;
+ while(*lineptr && ISBLANK(*lineptr))
+ lineptr++;
+ if(*lineptr == '#')
+ /* skip commented lines */
+ continue;
+
+ hsts_add(h, lineptr);
+ }
+ free(line); /* free the line buffer */
+ fclose(fp);
+ }
+ return result;
+
+ fail:
+ Curl_safefree(h->filename);
+ fclose(fp);
+ return CURLE_OUT_OF_MEMORY;
+}
+
+/*
+ * Curl_hsts_loadfile() loads HSTS from file
+ */
+CURLcode Curl_hsts_loadfile(struct Curl_easy *data,
+ struct hsts *h, const char *file)
+{
+ DEBUGASSERT(h);
+ (void)data;
+ return hsts_load(h, file);
+}
+
+/*
+ * Curl_hsts_loadcb() loads HSTS from callback
+ */
+CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h)
+{
+ if(h)
+ return hsts_pull(data, h);
+ return CURLE_OK;
+}
+
+#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */
diff --git a/libs/libcurl/src/hsts.h b/libs/libcurl/src/hsts.h index 0e36a7756b..9fffa2230a 100644 --- a/libs/libcurl/src/hsts.h +++ b/libs/libcurl/src/hsts.h @@ -1,67 +1,67 @@ -#ifndef HEADER_CURL_HSTS_H -#define HEADER_CURL_HSTS_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2020 - 2022, 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_HSTS) -#include <curl/curl.h> -#include "llist.h" - -#ifdef DEBUGBUILD -extern time_t deltatime; -#endif - -struct stsentry { - struct Curl_llist_element node; - const char *host; - bool includeSubDomains; - curl_off_t expires; /* the timestamp of this entry's expiry */ -}; - -/* The HSTS cache. Needs to be able to tailmatch host names. */ -struct hsts { - struct Curl_llist list; - char *filename; - unsigned int flags; -}; - -struct hsts *Curl_hsts_init(void); -void Curl_hsts_cleanup(struct hsts **hp); -CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, - const char *sts); -struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, - bool subdomain); -CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h, - const char *file); -CURLcode Curl_hsts_loadfile(struct Curl_easy *data, - struct hsts *h, const char *file); -CURLcode Curl_hsts_loadcb(struct Curl_easy *data, - struct hsts *h); -#else -#define Curl_hsts_cleanup(x) -#define Curl_hsts_loadcb(x,y) CURLE_OK -#define Curl_hsts_save(x,y,z) -#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */ -#endif /* HEADER_CURL_HSTS_H */ +#ifndef HEADER_CURL_HSTS_H
+#define HEADER_CURL_HSTS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2020 - 2022, 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_HSTS)
+#include <curl/curl.h>
+#include "llist.h"
+
+#ifdef DEBUGBUILD
+extern time_t deltatime;
+#endif
+
+struct stsentry {
+ struct Curl_llist_element node;
+ const char *host;
+ bool includeSubDomains;
+ curl_off_t expires; /* the timestamp of this entry's expiry */
+};
+
+/* The HSTS cache. Needs to be able to tailmatch host names. */
+struct hsts {
+ struct Curl_llist list;
+ char *filename;
+ unsigned int flags;
+};
+
+struct hsts *Curl_hsts_init(void);
+void Curl_hsts_cleanup(struct hsts **hp);
+CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
+ const char *sts);
+struct stsentry *Curl_hsts(struct hsts *h, const char *hostname,
+ bool subdomain);
+CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
+ const char *file);
+CURLcode Curl_hsts_loadfile(struct Curl_easy *data,
+ struct hsts *h, const char *file);
+CURLcode Curl_hsts_loadcb(struct Curl_easy *data,
+ struct hsts *h);
+#else
+#define Curl_hsts_cleanup(x)
+#define Curl_hsts_loadcb(x,y) CURLE_OK
+#define Curl_hsts_save(x,y,z)
+#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */
+#endif /* HEADER_CURL_HSTS_H */
diff --git a/libs/libcurl/src/http.c b/libs/libcurl/src/http.c index f57859e8b0..e92051dc48 100644 --- a/libs/libcurl/src/http.c +++ b/libs/libcurl/src/http.c @@ -1,4468 +1,4390 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - -#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 HAVE_NET_IF_H -#include <net/if.h> -#endif -#ifdef HAVE_SYS_IOCTL_H -#include <sys/ioctl.h> -#endif - -#ifdef HAVE_SYS_PARAM_H -#include <sys/param.h> -#endif - -#ifdef USE_HYPER -#include <hyper.h> -#endif - -#include "urldata.h" -#include <curl/curl.h> -#include "transfer.h" -#include "sendf.h" -#include "formdata.h" -#include "mime.h" -#include "progress.h" -#include "curl_base64.h" -#include "cookie.h" -#include "vauth/vauth.h" -#include "vtls/vtls.h" -#include "http_digest.h" -#include "http_ntlm.h" -#include "curl_ntlm_wb.h" -#include "http_negotiate.h" -#include "http_aws_sigv4.h" -#include "url.h" -#include "share.h" -#include "hostip.h" -#include "http.h" -#include "select.h" -#include "parsedate.h" /* for the week day and month names */ -#include "strtoofft.h" -#include "multiif.h" -#include "strcase.h" -#include "content_encoding.h" -#include "http_proxy.h" -#include "warnless.h" -#include "http2.h" -#include "connect.h" -#include "strdup.h" -#include "altsvc.h" -#include "hsts.h" -#include "ws.h" -#include "c-hyper.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* - * Forward declarations. - */ - -static int http_getsock_do(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *socks); -static bool http_should_fail(struct Curl_easy *data); - -#ifndef CURL_DISABLE_PROXY -static CURLcode add_haproxy_protocol_header(struct Curl_easy *data); -#endif - -#ifdef USE_SSL -static CURLcode https_connecting(struct Curl_easy *data, bool *done); -static int https_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *socks); -#else -#define https_connecting(x,y) CURLE_COULDNT_CONNECT -#endif -static CURLcode http_setup_conn(struct Curl_easy *data, - struct connectdata *conn); -#ifdef USE_WEBSOCKETS -static CURLcode ws_setup_conn(struct Curl_easy *data, - struct connectdata *conn); -#endif - -/* - * HTTP handler interface. - */ -const struct Curl_handler Curl_handler_http = { - "HTTP", /* scheme */ - http_setup_conn, /* setup_connection */ - Curl_http, /* do_it */ - Curl_http_done, /* done */ - ZERO_NULL, /* do_more */ - Curl_http_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - http_getsock_do, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_HTTP, /* defport */ - CURLPROTO_HTTP, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_CREDSPERREQUEST | /* flags */ - PROTOPT_USERPWDCTRL -}; - -#ifdef USE_WEBSOCKETS -const struct Curl_handler Curl_handler_ws = { - "WS", /* scheme */ - ws_setup_conn, /* setup_connection */ - Curl_http, /* do_it */ - Curl_http_done, /* done */ - ZERO_NULL, /* do_more */ - Curl_http_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - http_getsock_do, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_HTTP, /* defport */ - CURLPROTO_WS, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_CREDSPERREQUEST | /* flags */ - PROTOPT_USERPWDCTRL -}; -#endif - -#ifdef USE_SSL -/* - * HTTPS handler interface. - */ -const struct Curl_handler Curl_handler_https = { - "HTTPS", /* scheme */ - http_setup_conn, /* setup_connection */ - Curl_http, /* do_it */ - Curl_http_done, /* done */ - ZERO_NULL, /* do_more */ - Curl_http_connect, /* connect_it */ - https_connecting, /* connecting */ - ZERO_NULL, /* doing */ - https_getsock, /* proto_getsock */ - http_getsock_do, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_HTTPS, /* defport */ - CURLPROTO_HTTPS, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */ - PROTOPT_USERPWDCTRL -}; - -#ifdef USE_WEBSOCKETS -const struct Curl_handler Curl_handler_wss = { - "WSS", /* scheme */ - ws_setup_conn, /* setup_connection */ - Curl_http, /* do_it */ - Curl_http_done, /* done */ - ZERO_NULL, /* do_more */ - Curl_http_connect, /* connect_it */ - https_connecting, /* connecting */ - ZERO_NULL, /* doing */ - https_getsock, /* proto_getsock */ - http_getsock_do, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_HTTPS, /* defport */ - CURLPROTO_WSS, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */ - PROTOPT_USERPWDCTRL -}; -#endif - -#endif - -static CURLcode http_setup_conn(struct Curl_easy *data, - struct connectdata *conn) -{ - /* allocate the HTTP-specific struct for the Curl_easy, only to survive - during this request */ - struct HTTP *http; - DEBUGASSERT(data->req.p.http == NULL); - - http = calloc(1, sizeof(struct HTTP)); - if(!http) - return CURLE_OUT_OF_MEMORY; - - Curl_mime_initpart(&http->form, data); - data->req.p.http = http; - - if(data->state.httpwant == CURL_HTTP_VERSION_3) { - if(conn->handler->flags & PROTOPT_SSL) - /* Only go HTTP/3 directly on HTTPS URLs. It needs a UDP socket and does - the QUIC dance. */ - conn->transport = TRNSPRT_QUIC; - else { - failf(data, "HTTP/3 requested for non-HTTPS URL"); - return CURLE_URL_MALFORMAT; - } - } - else { - if(!CONN_INUSE(conn)) - /* if not already multi-using, setup connection details */ - Curl_http2_setup_conn(conn); - Curl_http2_setup_req(data); - } - return CURLE_OK; -} - -#ifdef USE_WEBSOCKETS -static CURLcode ws_setup_conn(struct Curl_easy *data, - struct connectdata *conn) -{ - /* websockets is 1.1 only (for now) */ - data->state.httpwant = CURL_HTTP_VERSION_1_1; - return http_setup_conn(data, conn); -} -#endif - -#ifndef CURL_DISABLE_PROXY -/* - * checkProxyHeaders() checks the linked list of custom proxy headers - * if proxy headers are not available, then it will lookup into http header - * link list - * - * It takes a connectdata struct as input to see if this is a proxy request or - * not, as it then might check a different header list. Provide the header - * prefix without colon! - */ -char *Curl_checkProxyheaders(struct Curl_easy *data, - const struct connectdata *conn, - const char *thisheader, - const size_t thislen) -{ - struct curl_slist *head; - - for(head = (conn->bits.proxy && data->set.sep_headers) ? - data->set.proxyheaders : data->set.headers; - head; head = head->next) { - if(strncasecompare(head->data, thisheader, thislen) && - Curl_headersep(head->data[thislen])) - return head->data; - } - - return NULL; -} -#else -/* disabled */ -#define Curl_checkProxyheaders(x,y,z,a) NULL -#endif - -/* - * Strip off leading and trailing whitespace from the value in the - * given HTTP header line and return a strdupped copy. Returns NULL in - * case of allocation failure. Returns an empty string if the header value - * consists entirely of whitespace. - */ -char *Curl_copy_header_value(const char *header) -{ - const char *start; - const char *end; - char *value; - size_t len; - - /* Find the end of the header name */ - while(*header && (*header != ':')) - ++header; - - if(*header) - /* Skip over colon */ - ++header; - - /* Find the first non-space letter */ - start = header; - while(*start && ISSPACE(*start)) - start++; - - /* data is in the host encoding so - use '\r' and '\n' instead of 0x0d and 0x0a */ - end = strchr(start, '\r'); - if(!end) - end = strchr(start, '\n'); - if(!end) - end = strchr(start, '\0'); - if(!end) - return NULL; - - /* skip all trailing space letters */ - while((end > start) && ISSPACE(*end)) - end--; - - /* get length of the type */ - len = end - start + 1; - - value = malloc(len + 1); - if(!value) - return NULL; - - memcpy(value, start, len); - value[len] = 0; /* null-terminate */ - - return value; -} - -#ifndef CURL_DISABLE_HTTP_AUTH -/* - * http_output_basic() sets up an Authorization: header (or the proxy version) - * for HTTP Basic authentication. - * - * Returns CURLcode. - */ -static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) -{ - size_t size = 0; - char *authorization = NULL; - char **userp; - const char *user; - const char *pwd; - CURLcode result; - char *out; - - /* credentials are unique per transfer for HTTP, do not use the ones for the - connection */ - if(proxy) { -#ifndef CURL_DISABLE_PROXY - userp = &data->state.aptr.proxyuserpwd; - user = data->state.aptr.proxyuser; - pwd = data->state.aptr.proxypasswd; -#else - return CURLE_NOT_BUILT_IN; -#endif - } - else { - userp = &data->state.aptr.userpwd; - user = data->state.aptr.user; - pwd = data->state.aptr.passwd; - } - - out = aprintf("%s:%s", user ? user : "", pwd ? pwd : ""); - if(!out) - return CURLE_OUT_OF_MEMORY; - - result = Curl_base64_encode(out, strlen(out), &authorization, &size); - if(result) - goto fail; - - if(!authorization) { - result = CURLE_REMOTE_ACCESS_DENIED; - goto fail; - } - - free(*userp); - *userp = aprintf("%sAuthorization: Basic %s\r\n", - proxy ? "Proxy-" : "", - authorization); - free(authorization); - if(!*userp) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } - - fail: - free(out); - return result; -} - -/* - * http_output_bearer() sets up an Authorization: header - * for HTTP Bearer authentication. - * - * Returns CURLcode. - */ -static CURLcode http_output_bearer(struct Curl_easy *data) -{ - char **userp; - CURLcode result = CURLE_OK; - - userp = &data->state.aptr.userpwd; - free(*userp); - *userp = aprintf("Authorization: Bearer %s\r\n", - data->set.str[STRING_BEARER]); - - if(!*userp) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } - - fail: - return result; -} - -#endif - -/* pickoneauth() selects the most favourable authentication method from the - * ones available and the ones we want. - * - * return TRUE if one was picked - */ -static bool pickoneauth(struct auth *pick, unsigned long mask) -{ - bool picked; - /* only deal with authentication we want */ - unsigned long avail = pick->avail & pick->want & mask; - picked = TRUE; - - /* The order of these checks is highly relevant, as this will be the order - of preference in case of the existence of multiple accepted types. */ - if(avail & CURLAUTH_NEGOTIATE) - pick->picked = CURLAUTH_NEGOTIATE; - else if(avail & CURLAUTH_BEARER) - pick->picked = CURLAUTH_BEARER; - else if(avail & CURLAUTH_DIGEST) - pick->picked = CURLAUTH_DIGEST; - else if(avail & CURLAUTH_NTLM) - pick->picked = CURLAUTH_NTLM; - else if(avail & CURLAUTH_NTLM_WB) - pick->picked = CURLAUTH_NTLM_WB; - else if(avail & CURLAUTH_BASIC) - pick->picked = CURLAUTH_BASIC; - else if(avail & CURLAUTH_AWS_SIGV4) - pick->picked = CURLAUTH_AWS_SIGV4; - else { - pick->picked = CURLAUTH_PICKNONE; /* we select to use nothing */ - picked = FALSE; - } - pick->avail = CURLAUTH_NONE; /* clear it here */ - - return picked; -} - -/* - * http_perhapsrewind() - * - * If we are doing POST or PUT { - * If we have more data to send { - * If we are doing NTLM { - * Keep sending since we must not disconnect - * } - * else { - * If there is more than just a little data left to send, close - * the current connection by force. - * } - * } - * If we have sent any data { - * If we don't have track of all the data { - * call app to tell it to rewind - * } - * else { - * rewind internally so that the operation can restart fine - * } - * } - * } - */ -static CURLcode http_perhapsrewind(struct Curl_easy *data, - struct connectdata *conn) -{ - struct HTTP *http = data->req.p.http; - curl_off_t bytessent; - curl_off_t expectsend = -1; /* default is unknown */ - - if(!http) - /* If this is still NULL, we have not reach very far and we can safely - skip this rewinding stuff */ - return CURLE_OK; - - switch(data->state.httpreq) { - case HTTPREQ_GET: - case HTTPREQ_HEAD: - return CURLE_OK; - default: - break; - } - - bytessent = data->req.writebytecount; - - if(conn->bits.authneg) { - /* This is a state where we are known to be negotiating and we don't send - any data then. */ - expectsend = 0; - } - else if(!conn->bits.protoconnstart) { - /* HTTP CONNECT in progress: there is no body */ - expectsend = 0; - } - else { - /* figure out how much data we are expected to send */ - switch(data->state.httpreq) { - case HTTPREQ_POST: - case HTTPREQ_PUT: - if(data->state.infilesize != -1) - expectsend = data->state.infilesize; - break; - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - expectsend = http->postsize; - break; - default: - break; - } - } - - conn->bits.rewindaftersend = FALSE; /* default */ - - if((expectsend == -1) || (expectsend > bytessent)) { -#if defined(USE_NTLM) - /* There is still data left to send */ - if((data->state.authproxy.picked == CURLAUTH_NTLM) || - (data->state.authhost.picked == CURLAUTH_NTLM) || - (data->state.authproxy.picked == CURLAUTH_NTLM_WB) || - (data->state.authhost.picked == CURLAUTH_NTLM_WB)) { - if(((expectsend - bytessent) < 2000) || - (conn->http_ntlm_state != NTLMSTATE_NONE) || - (conn->proxy_ntlm_state != NTLMSTATE_NONE)) { - /* The NTLM-negotiation has started *OR* there is just a little (<2K) - data left to send, keep on sending. */ - - /* rewind data when completely done sending! */ - if(!conn->bits.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) { - conn->bits.rewindaftersend = TRUE; - infof(data, "Rewind stream after send"); - } - - return CURLE_OK; - } - - if(conn->bits.close) - /* this is already marked to get closed */ - return CURLE_OK; - - infof(data, "NTLM send, close instead of sending %" - CURL_FORMAT_CURL_OFF_T " bytes", - (curl_off_t)(expectsend - bytessent)); - } -#endif -#if defined(USE_SPNEGO) - /* There is still data left to send */ - if((data->state.authproxy.picked == CURLAUTH_NEGOTIATE) || - (data->state.authhost.picked == CURLAUTH_NEGOTIATE)) { - if(((expectsend - bytessent) < 2000) || - (conn->http_negotiate_state != GSS_AUTHNONE) || - (conn->proxy_negotiate_state != GSS_AUTHNONE)) { - /* The NEGOTIATE-negotiation has started *OR* - there is just a little (<2K) data left to send, keep on sending. */ - - /* rewind data when completely done sending! */ - if(!conn->bits.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) { - conn->bits.rewindaftersend = TRUE; - infof(data, "Rewind stream after send"); - } - - return CURLE_OK; - } - - if(conn->bits.close) - /* this is already marked to get closed */ - return CURLE_OK; - - infof(data, "NEGOTIATE send, close instead of sending %" - CURL_FORMAT_CURL_OFF_T " bytes", - (curl_off_t)(expectsend - bytessent)); - } -#endif - - /* This is not NEGOTIATE/NTLM or many bytes left to send: close */ - streamclose(conn, "Mid-auth HTTP and much data left to send"); - data->req.size = 0; /* don't download any more than 0 bytes */ - - /* There still is data left to send, but this connection is marked for - closure so we can safely do the rewind right now */ - } - - if(bytessent) - /* we rewind now at once since if we already sent something */ - return Curl_readrewind(data); - - return CURLE_OK; -} - -/* - * Curl_http_auth_act() gets called when all HTTP headers have been received - * and it checks what authentication methods that are available and decides - * which one (if any) to use. It will set 'newurl' if an auth method was - * picked. - */ - -CURLcode Curl_http_auth_act(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - bool pickhost = FALSE; - bool pickproxy = FALSE; - CURLcode result = CURLE_OK; - unsigned long authmask = ~0ul; - - if(!data->set.str[STRING_BEARER]) - authmask &= (unsigned long)~CURLAUTH_BEARER; - - if(100 <= data->req.httpcode && 199 >= data->req.httpcode) - /* this is a transient response code, ignore */ - return CURLE_OK; - - if(data->state.authproblem) - return data->set.http_fail_on_error?CURLE_HTTP_RETURNED_ERROR:CURLE_OK; - - if((data->state.aptr.user || data->set.str[STRING_BEARER]) && - ((data->req.httpcode == 401) || - (conn->bits.authneg && data->req.httpcode < 300))) { - pickhost = pickoneauth(&data->state.authhost, authmask); - if(!pickhost) - data->state.authproblem = TRUE; - if(data->state.authhost.picked == CURLAUTH_NTLM && - conn->httpversion > 11) { - infof(data, "Forcing HTTP/1.1 for NTLM"); - connclose(conn, "Force HTTP/1.1 connection"); - data->state.httpwant = CURL_HTTP_VERSION_1_1; - } - } -#ifndef CURL_DISABLE_PROXY - if(conn->bits.proxy_user_passwd && - ((data->req.httpcode == 407) || - (conn->bits.authneg && data->req.httpcode < 300))) { - pickproxy = pickoneauth(&data->state.authproxy, - authmask & ~CURLAUTH_BEARER); - if(!pickproxy) - data->state.authproblem = TRUE; - } -#endif - - if(pickhost || pickproxy) { - if((data->state.httpreq != HTTPREQ_GET) && - (data->state.httpreq != HTTPREQ_HEAD) && - !conn->bits.rewindaftersend) { - result = http_perhapsrewind(data, conn); - if(result) - return result; - } - /* In case this is GSS auth, the newurl field is already allocated so - we must make sure to free it before allocating a new one. As figured - out in bug #2284386 */ - Curl_safefree(data->req.newurl); - data->req.newurl = strdup(data->state.url); /* clone URL */ - if(!data->req.newurl) - return CURLE_OUT_OF_MEMORY; - } - else if((data->req.httpcode < 300) && - (!data->state.authhost.done) && - conn->bits.authneg) { - /* no (known) authentication available, - authentication is not "done" yet and - no authentication seems to be required and - we didn't try HEAD or GET */ - if((data->state.httpreq != HTTPREQ_GET) && - (data->state.httpreq != HTTPREQ_HEAD)) { - data->req.newurl = strdup(data->state.url); /* clone URL */ - if(!data->req.newurl) - return CURLE_OUT_OF_MEMORY; - data->state.authhost.done = TRUE; - } - } - if(http_should_fail(data)) { - failf(data, "The requested URL returned error: %d", - data->req.httpcode); - result = CURLE_HTTP_RETURNED_ERROR; - } - - return result; -} - -#ifndef CURL_DISABLE_HTTP_AUTH -/* - * Output the correct authentication header depending on the auth type - * and whether or not it is to a proxy. - */ -static CURLcode -output_auth_headers(struct Curl_easy *data, - struct connectdata *conn, - struct auth *authstatus, - const char *request, - const char *path, - bool proxy) -{ - const char *auth = NULL; - CURLcode result = CURLE_OK; - (void)conn; - -#ifdef CURL_DISABLE_CRYPTO_AUTH - (void)request; - (void)path; -#endif -#ifndef CURL_DISABLE_CRYPTO_AUTH - if(authstatus->picked == CURLAUTH_AWS_SIGV4) { - auth = "AWS_SIGV4"; - result = Curl_output_aws_sigv4(data, proxy); - if(result) - return result; - } - else -#endif -#ifdef USE_SPNEGO - if(authstatus->picked == CURLAUTH_NEGOTIATE) { - auth = "Negotiate"; - result = Curl_output_negotiate(data, conn, proxy); - if(result) - return result; - } - else -#endif -#ifdef USE_NTLM - if(authstatus->picked == CURLAUTH_NTLM) { - auth = "NTLM"; - result = Curl_output_ntlm(data, proxy); - if(result) - return result; - } - else -#endif -#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED) - if(authstatus->picked == CURLAUTH_NTLM_WB) { - auth = "NTLM_WB"; - result = Curl_output_ntlm_wb(data, conn, proxy); - if(result) - return result; - } - else -#endif -#ifndef CURL_DISABLE_CRYPTO_AUTH - if(authstatus->picked == CURLAUTH_DIGEST) { - auth = "Digest"; - result = Curl_output_digest(data, - proxy, - (const unsigned char *)request, - (const unsigned char *)path); - if(result) - return result; - } - else -#endif - if(authstatus->picked == CURLAUTH_BASIC) { - /* Basic */ - if( -#ifndef CURL_DISABLE_PROXY - (proxy && conn->bits.proxy_user_passwd && - !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-authorization"))) || -#endif - (!proxy && data->state.aptr.user && - !Curl_checkheaders(data, STRCONST("Authorization")))) { - auth = "Basic"; - result = http_output_basic(data, proxy); - if(result) - return result; - } - - /* NOTE: this function should set 'done' TRUE, as the other auth - functions work that way */ - authstatus->done = TRUE; - } - if(authstatus->picked == CURLAUTH_BEARER) { - /* Bearer */ - if((!proxy && data->set.str[STRING_BEARER] && - !Curl_checkheaders(data, STRCONST("Authorization")))) { - auth = "Bearer"; - result = http_output_bearer(data); - if(result) - return result; - } - - /* NOTE: this function should set 'done' TRUE, as the other auth - functions work that way */ - authstatus->done = TRUE; - } - - if(auth) { -#ifndef CURL_DISABLE_PROXY - infof(data, "%s auth using %s with user '%s'", - proxy ? "Proxy" : "Server", auth, - proxy ? (data->state.aptr.proxyuser ? - data->state.aptr.proxyuser : "") : - (data->state.aptr.user ? - data->state.aptr.user : "")); -#else - infof(data, "Server auth using %s with user '%s'", - auth, data->state.aptr.user ? - data->state.aptr.user : ""); -#endif - authstatus->multipass = (!authstatus->done) ? TRUE : FALSE; - } - else - authstatus->multipass = FALSE; - - return CURLE_OK; -} - -/** - * Curl_http_output_auth() setups the authentication headers for the - * host/proxy and the correct authentication - * method. data->state.authdone is set to TRUE when authentication is - * done. - * - * @param conn all information about the current connection - * @param request pointer to the request keyword - * @param path pointer to the requested path; should include query part - * @param proxytunnel boolean if this is the request setting up a "proxy - * tunnel" - * - * @returns CURLcode - */ -CURLcode -Curl_http_output_auth(struct Curl_easy *data, - struct connectdata *conn, - const char *request, - Curl_HttpReq httpreq, - const char *path, - bool proxytunnel) /* TRUE if this is the request setting - up the proxy tunnel */ -{ - CURLcode result = CURLE_OK; - struct auth *authhost; - struct auth *authproxy; - - DEBUGASSERT(data); - - authhost = &data->state.authhost; - authproxy = &data->state.authproxy; - - if( -#ifndef CURL_DISABLE_PROXY - (conn->bits.httpproxy && conn->bits.proxy_user_passwd) || -#endif - data->state.aptr.user || data->set.str[STRING_BEARER]) - /* continue please */; - else { - authhost->done = TRUE; - authproxy->done = TRUE; - return CURLE_OK; /* no authentication with no user or password */ - } - - if(authhost->want && !authhost->picked) - /* The app has selected one or more methods, but none has been picked - so far by a server round-trip. Then we set the picked one to the - want one, and if this is one single bit it'll be used instantly. */ - authhost->picked = authhost->want; - - if(authproxy->want && !authproxy->picked) - /* The app has selected one or more methods, but none has been picked so - far by a proxy round-trip. Then we set the picked one to the want one, - and if this is one single bit it'll be used instantly. */ - authproxy->picked = authproxy->want; - -#ifndef CURL_DISABLE_PROXY - /* Send proxy authentication header if needed */ - if(conn->bits.httpproxy && - (conn->bits.tunnel_proxy == (bit)proxytunnel)) { - result = output_auth_headers(data, conn, authproxy, request, path, TRUE); - if(result) - return result; - } - else -#else - (void)proxytunnel; -#endif /* CURL_DISABLE_PROXY */ - /* we have no proxy so let's pretend we're done authenticating - with it */ - authproxy->done = TRUE; - - /* To prevent the user+password to get sent to other than the original host - due to a location-follow */ - if(Curl_auth_allowed_to_host(data) -#ifndef CURL_DISABLE_NETRC - || conn->bits.netrc -#endif - ) - result = output_auth_headers(data, conn, authhost, request, path, FALSE); - else - authhost->done = TRUE; - - if(((authhost->multipass && !authhost->done) || - (authproxy->multipass && !authproxy->done)) && - (httpreq != HTTPREQ_GET) && - (httpreq != HTTPREQ_HEAD)) { - /* Auth is required and we are not authenticated yet. Make a PUT or POST - with content-length zero as a "probe". */ - conn->bits.authneg = TRUE; - } - else - conn->bits.authneg = FALSE; - - return result; -} - -#else -/* when disabled */ -CURLcode -Curl_http_output_auth(struct Curl_easy *data, - struct connectdata *conn, - const char *request, - Curl_HttpReq httpreq, - const char *path, - bool proxytunnel) -{ - (void)data; - (void)conn; - (void)request; - (void)httpreq; - (void)path; - (void)proxytunnel; - return CURLE_OK; -} -#endif - -/* - * Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate: - * headers. They are dealt with both in the transfer.c main loop and in the - * proxy CONNECT loop. - */ - -static int is_valid_auth_separator(char ch) -{ - return ch == '\0' || ch == ',' || ISSPACE(ch); -} - -CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, - const char *auth) /* the first non-space */ -{ - /* - * This resource requires authentication - */ - struct connectdata *conn = data->conn; -#ifdef USE_SPNEGO - curlnegotiate *negstate = proxy ? &conn->proxy_negotiate_state : - &conn->http_negotiate_state; -#endif - unsigned long *availp; - struct auth *authp; - - (void) conn; /* In case conditionals make it unused. */ - - if(proxy) { - availp = &data->info.proxyauthavail; - authp = &data->state.authproxy; - } - else { - availp = &data->info.httpauthavail; - authp = &data->state.authhost; - } - - /* - * Here we check if we want the specific single authentication (using ==) and - * if we do, we initiate usage of it. - * - * If the provided authentication is wanted as one out of several accepted - * types (using &), we OR this authentication type to the authavail - * variable. - * - * Note: - * - * ->picked is first set to the 'want' value (one or more bits) before the - * request is sent, and then it is again set _after_ all response 401/407 - * headers have been received but then only to a single preferred method - * (bit). - */ - - while(*auth) { -#ifdef USE_SPNEGO - if(checkprefix("Negotiate", auth) && is_valid_auth_separator(auth[9])) { - if((authp->avail & CURLAUTH_NEGOTIATE) || - Curl_auth_is_spnego_supported()) { - *availp |= CURLAUTH_NEGOTIATE; - authp->avail |= CURLAUTH_NEGOTIATE; - - if(authp->picked == CURLAUTH_NEGOTIATE) { - CURLcode result = Curl_input_negotiate(data, conn, proxy, auth); - if(!result) { - DEBUGASSERT(!data->req.newurl); - data->req.newurl = strdup(data->state.url); - if(!data->req.newurl) - return CURLE_OUT_OF_MEMORY; - data->state.authproblem = FALSE; - /* we received a GSS auth token and we dealt with it fine */ - *negstate = GSS_AUTHRECV; - } - else - data->state.authproblem = TRUE; - } - } - } - else -#endif -#ifdef USE_NTLM - /* NTLM support requires the SSL crypto libs */ - if(checkprefix("NTLM", auth) && is_valid_auth_separator(auth[4])) { - if((authp->avail & CURLAUTH_NTLM) || - (authp->avail & CURLAUTH_NTLM_WB) || - Curl_auth_is_ntlm_supported()) { - *availp |= CURLAUTH_NTLM; - authp->avail |= CURLAUTH_NTLM; - - if(authp->picked == CURLAUTH_NTLM || - authp->picked == CURLAUTH_NTLM_WB) { - /* NTLM authentication is picked and activated */ - CURLcode result = Curl_input_ntlm(data, proxy, auth); - if(!result) { - data->state.authproblem = FALSE; -#ifdef NTLM_WB_ENABLED - if(authp->picked == CURLAUTH_NTLM_WB) { - *availp &= ~CURLAUTH_NTLM; - authp->avail &= ~CURLAUTH_NTLM; - *availp |= CURLAUTH_NTLM_WB; - authp->avail |= CURLAUTH_NTLM_WB; - - result = Curl_input_ntlm_wb(data, conn, proxy, auth); - if(result) { - infof(data, "Authentication problem. Ignoring this."); - data->state.authproblem = TRUE; - } - } -#endif - } - else { - infof(data, "Authentication problem. Ignoring this."); - data->state.authproblem = TRUE; - } - } - } - } - else -#endif -#ifndef CURL_DISABLE_CRYPTO_AUTH - if(checkprefix("Digest", auth) && is_valid_auth_separator(auth[6])) { - if((authp->avail & CURLAUTH_DIGEST) != 0) - infof(data, "Ignoring duplicate digest auth header."); - else if(Curl_auth_is_digest_supported()) { - CURLcode result; - - *availp |= CURLAUTH_DIGEST; - authp->avail |= CURLAUTH_DIGEST; - - /* We call this function on input Digest headers even if Digest - * authentication isn't activated yet, as we need to store the - * incoming data from this header in case we are going to use - * Digest */ - result = Curl_input_digest(data, proxy, auth); - if(result) { - infof(data, "Authentication problem. Ignoring this."); - data->state.authproblem = TRUE; - } - } - } - else -#endif - if(checkprefix("Basic", auth) && - is_valid_auth_separator(auth[5])) { - *availp |= CURLAUTH_BASIC; - authp->avail |= CURLAUTH_BASIC; - if(authp->picked == CURLAUTH_BASIC) { - /* We asked for Basic authentication but got a 40X back - anyway, which basically means our name+password isn't - valid. */ - authp->avail = CURLAUTH_NONE; - infof(data, "Authentication problem. Ignoring this."); - data->state.authproblem = TRUE; - } - } - else - if(checkprefix("Bearer", auth) && - is_valid_auth_separator(auth[6])) { - *availp |= CURLAUTH_BEARER; - authp->avail |= CURLAUTH_BEARER; - if(authp->picked == CURLAUTH_BEARER) { - /* We asked for Bearer authentication but got a 40X back - anyway, which basically means our token isn't valid. */ - authp->avail = CURLAUTH_NONE; - infof(data, "Authentication problem. Ignoring this."); - data->state.authproblem = TRUE; - } - } - - /* there may be multiple methods on one line, so keep reading */ - while(*auth && *auth != ',') /* read up to the next comma */ - auth++; - if(*auth == ',') /* if we're on a comma, skip it */ - auth++; - while(*auth && ISSPACE(*auth)) - auth++; - } - - return CURLE_OK; -} - -/** - * http_should_fail() determines whether an HTTP response has gotten us - * into an error state or not. - * - * @param conn all information about the current connection - * - * @retval FALSE communications should continue - * - * @retval TRUE communications should not continue - */ -static bool http_should_fail(struct Curl_easy *data) -{ - int httpcode; - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - - httpcode = data->req.httpcode; - - /* - ** If we haven't been asked to fail on error, - ** don't fail. - */ - if(!data->set.http_fail_on_error) - return FALSE; - - /* - ** Any code < 400 is never terminal. - */ - if(httpcode < 400) - return FALSE; - - /* - ** A 416 response to a resume request is presumably because the file is - ** already completely downloaded and thus not actually a fail. - */ - if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET && - httpcode == 416) - return FALSE; - - /* - ** Any code >= 400 that's not 401 or 407 is always - ** a terminal error - */ - if((httpcode != 401) && (httpcode != 407)) - return TRUE; - - /* - ** All we have left to deal with is 401 and 407 - */ - DEBUGASSERT((httpcode == 401) || (httpcode == 407)); - - /* - ** Examine the current authentication state to see if this - ** is an error. The idea is for this function to get - ** called after processing all the headers in a response - ** message. So, if we've been to asked to authenticate a - ** particular stage, and we've done it, we're OK. But, if - ** we're already completely authenticated, it's not OK to - ** get another 401 or 407. - ** - ** It is possible for authentication to go stale such that - ** the client needs to reauthenticate. Once that info is - ** available, use it here. - */ - - /* - ** Either we're not authenticating, or we're supposed to - ** be authenticating something else. This is an error. - */ - if((httpcode == 401) && !data->state.aptr.user) - return TRUE; -#ifndef CURL_DISABLE_PROXY - if((httpcode == 407) && !data->conn->bits.proxy_user_passwd) - return TRUE; -#endif - - return data->state.authproblem; -} - -/* - * readmoredata() is a "fread() emulation" to provide POST and/or request - * data. It is used when a huge POST is to be made and the entire chunk wasn't - * sent in the first send(). This function will then be called from the - * transfer.c loop when more data is to be sent to the peer. - * - * Returns the amount of bytes it filled the buffer with. - */ -static size_t readmoredata(char *buffer, - size_t size, - size_t nitems, - void *userp) -{ - struct Curl_easy *data = (struct Curl_easy *)userp; - struct HTTP *http = data->req.p.http; - size_t fullsize = size * nitems; - - if(!http->postsize) - /* nothing to return */ - return 0; - - /* make sure that a HTTP request is never sent away chunked! */ - data->req.forbidchunk = (http->sending == HTTPSEND_REQUEST)?TRUE:FALSE; - - if(data->set.max_send_speed && - (data->set.max_send_speed < (curl_off_t)fullsize) && - (data->set.max_send_speed < http->postsize)) - /* speed limit */ - fullsize = (size_t)data->set.max_send_speed; - - else if(http->postsize <= (curl_off_t)fullsize) { - memcpy(buffer, http->postdata, (size_t)http->postsize); - fullsize = (size_t)http->postsize; - - if(http->backup.postsize) { - /* move backup data into focus and continue on that */ - http->postdata = http->backup.postdata; - http->postsize = http->backup.postsize; - data->state.fread_func = http->backup.fread_func; - data->state.in = http->backup.fread_in; - - http->sending++; /* move one step up */ - - http->backup.postsize = 0; - } - else - http->postsize = 0; - - return fullsize; - } - - memcpy(buffer, http->postdata, fullsize); - http->postdata += fullsize; - http->postsize -= fullsize; - - return fullsize; -} - -/* - * Curl_buffer_send() sends a header buffer and frees all associated - * memory. Body data may be appended to the header data if desired. - * - * Returns CURLcode - */ -CURLcode Curl_buffer_send(struct dynbuf *in, - struct Curl_easy *data, - /* add the number of sent bytes to this - counter */ - curl_off_t *bytes_written, - /* how much of the buffer contains body data */ - curl_off_t included_body_bytes, - int socketindex) -{ - ssize_t amount; - CURLcode result; - char *ptr; - size_t size; - struct connectdata *conn = data->conn; - struct HTTP *http = data->req.p.http; - size_t sendsize; - curl_socket_t sockfd; - size_t headersize; - - DEBUGASSERT(socketindex <= SECONDARYSOCKET); - - sockfd = conn->sock[socketindex]; - - /* The looping below is required since we use non-blocking sockets, but due - to the circumstances we will just loop and try again and again etc */ - - ptr = Curl_dyn_ptr(in); - size = Curl_dyn_len(in); - - headersize = size - (size_t)included_body_bytes; /* the initial part that - isn't body is header */ - - DEBUGASSERT(size > (size_t)included_body_bytes); - - if((conn->handler->flags & PROTOPT_SSL -#ifndef CURL_DISABLE_PROXY - || conn->http_proxy.proxytype == CURLPROXY_HTTPS -#endif - ) - && conn->httpversion != 20) { - /* Make sure this doesn't send more body bytes than what the max send - speed says. The request bytes do not count to the max speed. - */ - if(data->set.max_send_speed && - (included_body_bytes > data->set.max_send_speed)) { - curl_off_t overflow = included_body_bytes - data->set.max_send_speed; - DEBUGASSERT((size_t)overflow < size); - sendsize = size - (size_t)overflow; - } - else - sendsize = size; - - /* OpenSSL is very picky and we must send the SAME buffer pointer to the - library when we attempt to re-send this buffer. Sending the same data - is not enough, we must use the exact same address. For this reason, we - must copy the data to the uploadbuffer first, since that is the buffer - we will be using if this send is retried later. - */ - result = Curl_get_upload_buffer(data); - if(result) { - /* malloc failed, free memory and return to the caller */ - Curl_dyn_free(in); - return result; - } - /* We never send more than upload_buffer_size bytes in one single chunk - when we speak HTTPS, as if only a fraction of it is sent now, this data - needs to fit into the normal read-callback buffer later on and that - buffer is using this size. - */ - if(sendsize > (size_t)data->set.upload_buffer_size) - sendsize = (size_t)data->set.upload_buffer_size; - - memcpy(data->state.ulbuf, ptr, sendsize); - ptr = data->state.ulbuf; - } - else { -#ifdef CURLDEBUG - /* Allow debug builds to override this logic to force short initial - sends - */ - char *p = getenv("CURL_SMALLREQSEND"); - if(p) { - size_t altsize = (size_t)strtoul(p, NULL, 10); - if(altsize) - sendsize = CURLMIN(size, altsize); - else - sendsize = size; - } - else -#endif - { - /* Make sure this doesn't send more body bytes than what the max send - speed says. The request bytes do not count to the max speed. - */ - if(data->set.max_send_speed && - (included_body_bytes > data->set.max_send_speed)) { - curl_off_t overflow = included_body_bytes - data->set.max_send_speed; - DEBUGASSERT((size_t)overflow < size); - sendsize = size - (size_t)overflow; - } - else - sendsize = size; - } - } - - result = Curl_write(data, sockfd, ptr, sendsize, &amount); - - if(!result) { - /* - * Note that we may not send the entire chunk at once, and we have a set - * number of data bytes at the end of the big buffer (out of which we may - * only send away a part). - */ - /* how much of the header that was sent */ - size_t headlen = (size_t)amount>headersize ? headersize : (size_t)amount; - size_t bodylen = amount - headlen; - - /* this data _may_ contain binary stuff */ - Curl_debug(data, CURLINFO_HEADER_OUT, ptr, headlen); - if(bodylen) - /* there was body data sent beyond the initial header part, pass that on - to the debug callback too */ - Curl_debug(data, CURLINFO_DATA_OUT, ptr + headlen, bodylen); - - /* 'amount' can never be a very large value here so typecasting it so a - signed 31 bit value should not cause problems even if ssize_t is - 64bit */ - *bytes_written += (long)amount; - - if(http) { - /* if we sent a piece of the body here, up the byte counter for it - accordingly */ - data->req.writebytecount += bodylen; - Curl_pgrsSetUploadCounter(data, data->req.writebytecount); - - if((size_t)amount != size) { - /* The whole request could not be sent in one system call. We must - queue it up and send it later when we get the chance. We must not - loop here and wait until it might work again. */ - - size -= amount; - - ptr = Curl_dyn_ptr(in) + amount; - - /* backup the currently set pointers */ - http->backup.fread_func = data->state.fread_func; - http->backup.fread_in = data->state.in; - http->backup.postdata = http->postdata; - http->backup.postsize = http->postsize; - - /* set the new pointers for the request-sending */ - data->state.fread_func = (curl_read_callback)readmoredata; - data->state.in = (void *)data; - http->postdata = ptr; - http->postsize = (curl_off_t)size; - - /* this much data is remaining header: */ - data->req.pendingheader = headersize - headlen; - - http->send_buffer = *in; /* copy the whole struct */ - http->sending = HTTPSEND_REQUEST; - - return CURLE_OK; - } - http->sending = HTTPSEND_BODY; - /* the full buffer was sent, clean up and return */ - } - else { - if((size_t)amount != size) - /* We have no continue-send mechanism now, fail. This can only happen - when this function is used from the CONNECT sending function. We - currently (stupidly) assume that the whole request is always sent - away in the first single chunk. - - This needs FIXing. - */ - return CURLE_SEND_ERROR; - } - } - Curl_dyn_free(in); - - /* no remaining header data */ - data->req.pendingheader = 0; - return result; -} - -/* end of the add_buffer functions */ -/* ------------------------------------------------------------------------- */ - - - -/* - * Curl_compareheader() - * - * Returns TRUE if 'headerline' contains the 'header' with given 'content'. - * Pass headers WITH the colon. - */ -bool -Curl_compareheader(const char *headerline, /* line to check */ - const char *header, /* header keyword _with_ colon */ - const size_t hlen, /* len of the keyword in bytes */ - const char *content, /* content string to find */ - const size_t clen) /* len of the content in bytes */ -{ - /* RFC2616, section 4.2 says: "Each header field consists of a name followed - * by a colon (":") and the field value. Field names are case-insensitive. - * The field value MAY be preceded by any amount of LWS, though a single SP - * is preferred." */ - - size_t len; - const char *start; - const char *end; - DEBUGASSERT(hlen); - DEBUGASSERT(clen); - DEBUGASSERT(header); - DEBUGASSERT(content); - - if(!strncasecompare(headerline, header, hlen)) - return FALSE; /* doesn't start with header */ - - /* pass the header */ - start = &headerline[hlen]; - - /* pass all whitespace */ - while(*start && ISSPACE(*start)) - start++; - - /* find the end of the header line */ - end = strchr(start, '\r'); /* lines end with CRLF */ - if(!end) { - /* in case there's a non-standard compliant line here */ - end = strchr(start, '\n'); - - if(!end) - /* hm, there's no line ending here, use the zero byte! */ - end = strchr(start, '\0'); - } - - len = end-start; /* length of the content part of the input line */ - - /* find the content string in the rest of the line */ - for(; len >= clen; len--, start++) { - if(strncasecompare(start, content, clen)) - return TRUE; /* match! */ - } - - return FALSE; /* no match */ -} - -/* - * Curl_http_connect() performs HTTP stuff to do at connect-time, called from - * the generic Curl_connect(). - */ -CURLcode Curl_http_connect(struct Curl_easy *data, bool *done) -{ - CURLcode result; - struct connectdata *conn = data->conn; - - /* We default to persistent connections. We set this already in this connect - function to make the re-use checks properly be able to check this bit. */ - connkeep(conn, "HTTP default"); - -#ifndef CURL_DISABLE_PROXY - /* the CONNECT procedure might not have been completed */ - result = Curl_proxy_connect(data, FIRSTSOCKET); - if(result) - return result; - - if(conn->bits.proxy_connect_closed) - /* this is not an error, just part of the connection negotiation */ - return CURLE_OK; - - if(CONNECT_FIRSTSOCKET_PROXY_SSL()) - return CURLE_OK; /* wait for HTTPS proxy SSL initialization to complete */ - - if(Curl_connect_ongoing(conn)) - /* nothing else to do except wait right now - we're not done here. */ - return CURLE_OK; - - if(data->set.haproxyprotocol) { - /* add HAProxy PROXY protocol header */ - result = add_haproxy_protocol_header(data); - if(result) - return result; - } -#endif - - if(conn->given->flags & PROTOPT_SSL) { - /* perform SSL initialization */ - result = https_connecting(data, done); - if(result) - return result; - } - else - *done = TRUE; - - return CURLE_OK; -} - -/* this returns the socket to wait for in the DO and DOING state for the multi - interface and then we're always _sending_ a request and thus we wait for - the single socket to become writable only */ -static int http_getsock_do(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *socks) -{ - /* write mode */ - (void)data; - socks[0] = conn->sock[FIRSTSOCKET]; - return GETSOCK_WRITESOCK(0); -} - -#ifndef CURL_DISABLE_PROXY -static CURLcode add_haproxy_protocol_header(struct Curl_easy *data) -{ - struct dynbuf req; - CURLcode result; - const char *tcp_version; - DEBUGASSERT(data->conn); - Curl_dyn_init(&req, DYN_HAXPROXY); - -#ifdef USE_UNIX_SOCKETS - if(data->conn->unix_domain_socket) - /* the buffer is large enough to hold this! */ - result = Curl_dyn_addn(&req, STRCONST("PROXY UNKNOWN\r\n")); - else { -#endif - /* Emit the correct prefix for IPv6 */ - tcp_version = data->conn->bits.ipv6 ? "TCP6" : "TCP4"; - - result = Curl_dyn_addf(&req, "PROXY %s %s %s %i %i\r\n", - tcp_version, - data->info.conn_local_ip, - data->info.conn_primary_ip, - data->info.conn_local_port, - data->info.conn_primary_port); - -#ifdef USE_UNIX_SOCKETS - } -#endif - - if(!result) - result = Curl_buffer_send(&req, data, &data->info.request_size, - 0, FIRSTSOCKET); - return result; -} -#endif - -#ifdef USE_SSL -static CURLcode https_connecting(struct Curl_easy *data, bool *done) -{ - CURLcode result; - struct connectdata *conn = data->conn; - DEBUGASSERT((data) && (data->conn->handler->flags & PROTOPT_SSL)); - -#ifdef ENABLE_QUIC - if(conn->transport == TRNSPRT_QUIC) { - *done = TRUE; - return CURLE_OK; - } -#endif - - /* perform SSL initialization for this socket */ - result = Curl_ssl_connect_nonblocking(data, conn, FALSE, FIRSTSOCKET, done); - if(result) - connclose(conn, "Failed HTTPS connection"); - - return result; -} - -static int https_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *socks) -{ - (void)data; - if(conn->handler->flags & PROTOPT_SSL) - return Curl_ssl->getsock(conn, socks); - return GETSOCK_BLANK; -} -#endif /* USE_SSL */ - -/* - * Curl_http_done() gets called after a single HTTP request has been - * performed. - */ - -CURLcode Curl_http_done(struct Curl_easy *data, - CURLcode status, bool premature) -{ - struct connectdata *conn = data->conn; - struct HTTP *http = data->req.p.http; - - /* Clear multipass flag. If authentication isn't done yet, then it will get - * a chance to be set back to true when we output the next auth header */ - data->state.authhost.multipass = FALSE; - data->state.authproxy.multipass = FALSE; - - Curl_unencode_cleanup(data); - - /* set the proper values (possibly modified on POST) */ - conn->seek_func = data->set.seek_func; /* restore */ - conn->seek_client = data->set.seek_client; /* restore */ - - if(!http) - return CURLE_OK; - - Curl_dyn_free(&http->send_buffer); - Curl_http2_done(data, premature); - Curl_quic_done(data, premature); - Curl_mime_cleanpart(&http->form); - Curl_dyn_reset(&data->state.headerb); - Curl_hyper_done(data); - Curl_ws_done(data); - - if(status) - return status; - - if(!premature && /* this check is pointless when DONE is called before the - entire operation is complete */ - !conn->bits.retry && - !data->set.connect_only && - (data->req.bytecount + - data->req.headerbytecount - - data->req.deductheadercount) <= 0) { - /* If this connection isn't simply closed to be retried, AND nothing was - read from the HTTP server (that counts), this can't be right so we - return an error here */ - failf(data, "Empty reply from server"); - /* Mark it as closed to avoid the "left intact" message */ - streamclose(conn, "Empty reply from server"); - return CURLE_GOT_NOTHING; - } - - return CURLE_OK; -} - -/* - * Determine if we should use HTTP 1.1 (OR BETTER) for this request. Reasons - * to avoid it include: - * - * - if the user specifically requested HTTP 1.0 - * - if the server we are connected to only supports 1.0 - * - if any server previously contacted to handle this request only supports - * 1.0. - */ -bool Curl_use_http_1_1plus(const struct Curl_easy *data, - const struct connectdata *conn) -{ - if((data->state.httpversion == 10) || (conn->httpversion == 10)) - return FALSE; - if((data->state.httpwant == CURL_HTTP_VERSION_1_0) && - (conn->httpversion <= 10)) - return FALSE; - return ((data->state.httpwant == CURL_HTTP_VERSION_NONE) || - (data->state.httpwant >= CURL_HTTP_VERSION_1_1)); -} - -#ifndef USE_HYPER -static const char *get_http_string(const struct Curl_easy *data, - const struct connectdata *conn) -{ -#ifdef ENABLE_QUIC - if((data->state.httpwant == CURL_HTTP_VERSION_3) || - (conn->httpversion == 30)) - return "3"; -#endif - -#ifdef USE_NGHTTP2 - if(conn->proto.httpc.h2) - return "2"; -#endif - - if(Curl_use_http_1_1plus(data, conn)) - return "1.1"; - - return "1.0"; -} -#endif - -/* check and possibly add an Expect: header */ -static CURLcode expect100(struct Curl_easy *data, - struct connectdata *conn, - struct dynbuf *req) -{ - CURLcode result = CURLE_OK; - data->state.expect100header = FALSE; /* default to false unless it is set - to TRUE below */ - if(!data->state.disableexpect && Curl_use_http_1_1plus(data, conn) && - (conn->httpversion < 20)) { - /* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an - Expect: 100-continue to the headers which actually speeds up post - operations (as there is one packet coming back from the web server) */ - const char *ptr = Curl_checkheaders(data, STRCONST("Expect")); - if(ptr) { - data->state.expect100header = - Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue")); - } - else { - result = Curl_dyn_addn(req, STRCONST("Expect: 100-continue\r\n")); - if(!result) - data->state.expect100header = TRUE; - } - } - - return result; -} - -enum proxy_use { - HEADER_SERVER, /* direct to server */ - HEADER_PROXY, /* regular request to proxy */ - HEADER_CONNECT /* sending CONNECT to a proxy */ -}; - -/* used to compile the provided trailers into one buffer - will return an error code if one of the headers is - not formatted correctly */ -CURLcode Curl_http_compile_trailers(struct curl_slist *trailers, - struct dynbuf *b, - struct Curl_easy *handle) -{ - char *ptr = NULL; - CURLcode result = CURLE_OK; - const char *endofline_native = NULL; - const char *endofline_network = NULL; - - if( -#ifdef CURL_DO_LINEEND_CONV - (handle->state.prefer_ascii) || -#endif - (handle->set.crlf)) { - /* \n will become \r\n later on */ - endofline_native = "\n"; - endofline_network = "\x0a"; - } - else { - endofline_native = "\r\n"; - endofline_network = "\x0d\x0a"; - } - - while(trailers) { - /* only add correctly formatted trailers */ - ptr = strchr(trailers->data, ':'); - if(ptr && *(ptr + 1) == ' ') { - result = Curl_dyn_add(b, trailers->data); - if(result) - return result; - result = Curl_dyn_add(b, endofline_native); - if(result) - return result; - } - else - infof(handle, "Malformatted trailing header, skipping trailer"); - trailers = trailers->next; - } - result = Curl_dyn_add(b, endofline_network); - return result; -} - -CURLcode Curl_add_custom_headers(struct Curl_easy *data, - bool is_connect, -#ifndef USE_HYPER - struct dynbuf *req -#else - void *req -#endif - ) -{ - struct connectdata *conn = data->conn; - char *ptr; - struct curl_slist *h[2]; - struct curl_slist *headers; - int numlists = 1; /* by default */ - int i; - -#ifndef CURL_DISABLE_PROXY - enum proxy_use proxy; - - if(is_connect) - proxy = HEADER_CONNECT; - else - proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy? - HEADER_PROXY:HEADER_SERVER; - - switch(proxy) { - case HEADER_SERVER: - h[0] = data->set.headers; - break; - case HEADER_PROXY: - h[0] = data->set.headers; - if(data->set.sep_headers) { - h[1] = data->set.proxyheaders; - numlists++; - } - break; - case HEADER_CONNECT: - if(data->set.sep_headers) - h[0] = data->set.proxyheaders; - else - h[0] = data->set.headers; - break; - } -#else - (void)is_connect; - h[0] = data->set.headers; -#endif - - /* loop through one or two lists */ - for(i = 0; i < numlists; i++) { - headers = h[i]; - - while(headers) { - char *semicolonp = NULL; - ptr = strchr(headers->data, ':'); - if(!ptr) { - char *optr; - /* no colon, semicolon? */ - ptr = strchr(headers->data, ';'); - if(ptr) { - optr = ptr; - ptr++; /* pass the semicolon */ - while(*ptr && ISSPACE(*ptr)) - ptr++; - - if(*ptr) { - /* this may be used for something else in the future */ - optr = NULL; - } - else { - if(*(--ptr) == ';') { - /* copy the source */ - semicolonp = strdup(headers->data); - if(!semicolonp) { -#ifndef USE_HYPER - Curl_dyn_free(req); -#endif - return CURLE_OUT_OF_MEMORY; - } - /* put a colon where the semicolon is */ - semicolonp[ptr - headers->data] = ':'; - /* point at the colon */ - optr = &semicolonp [ptr - headers->data]; - } - } - ptr = optr; - } - } - if(ptr && (ptr != headers->data)) { - /* we require a colon for this to be a true header */ - - ptr++; /* pass the colon */ - while(*ptr && ISSPACE(*ptr)) - ptr++; - - if(*ptr || semicolonp) { - /* only send this if the contents was non-blank or done special */ - CURLcode result = CURLE_OK; - char *compare = semicolonp ? semicolonp : headers->data; - - if(data->state.aptr.host && - /* a Host: header was sent already, don't pass on any custom Host: - header as that will produce *two* in the same request! */ - checkprefix("Host:", compare)) - ; - else if(data->state.httpreq == HTTPREQ_POST_FORM && - /* this header (extended by formdata.c) is sent later */ - checkprefix("Content-Type:", compare)) - ; - else if(data->state.httpreq == HTTPREQ_POST_MIME && - /* this header is sent later */ - checkprefix("Content-Type:", compare)) - ; - else if(conn->bits.authneg && - /* while doing auth neg, don't allow the custom length since - we will force length zero then */ - checkprefix("Content-Length:", compare)) - ; - else if(data->state.aptr.te && - /* when asking for Transfer-Encoding, don't pass on a custom - Connection: */ - checkprefix("Connection:", compare)) - ; - else if((conn->httpversion >= 20) && - checkprefix("Transfer-Encoding:", compare)) - /* HTTP/2 doesn't support chunked requests */ - ; - else if((checkprefix("Authorization:", compare) || - checkprefix("Cookie:", compare)) && - /* be careful of sending this potentially sensitive header to - other hosts */ - !Curl_auth_allowed_to_host(data)) - ; - else { -#ifdef USE_HYPER - result = Curl_hyper_header(data, req, compare); -#else - result = Curl_dyn_addf(req, "%s\r\n", compare); -#endif - } - if(semicolonp) - free(semicolonp); - if(result) - return result; - } - } - headers = headers->next; - } - } - - return CURLE_OK; -} - -#ifndef CURL_DISABLE_PARSEDATE -CURLcode Curl_add_timecondition(struct Curl_easy *data, -#ifndef USE_HYPER - struct dynbuf *req -#else - void *req -#endif - ) -{ - const struct tm *tm; - struct tm keeptime; - CURLcode result; - char datestr[80]; - const char *condp; - size_t len; - - if(data->set.timecondition == CURL_TIMECOND_NONE) - /* no condition was asked for */ - return CURLE_OK; - - result = Curl_gmtime(data->set.timevalue, &keeptime); - if(result) { - failf(data, "Invalid TIMEVALUE"); - return result; - } - tm = &keeptime; - - switch(data->set.timecondition) { - default: - return CURLE_BAD_FUNCTION_ARGUMENT; - - case CURL_TIMECOND_IFMODSINCE: - condp = "If-Modified-Since"; - len = 17; - break; - case CURL_TIMECOND_IFUNMODSINCE: - condp = "If-Unmodified-Since"; - len = 19; - break; - case CURL_TIMECOND_LASTMOD: - condp = "Last-Modified"; - len = 13; - break; - } - - if(Curl_checkheaders(data, condp, len)) { - /* A custom header was specified; it will be sent instead. */ - return CURLE_OK; - } - - /* The If-Modified-Since header family should have their times set in - * GMT as RFC2616 defines: "All HTTP date/time stamps MUST be - * represented in Greenwich Mean Time (GMT), without exception. For the - * purposes of HTTP, GMT is exactly equal to UTC (Coordinated Universal - * Time)." (see page 20 of RFC2616). - */ - - /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ - msnprintf(datestr, sizeof(datestr), - "%s: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n", - condp, - Curl_wkday[tm->tm_wday?tm->tm_wday-1:6], - tm->tm_mday, - Curl_month[tm->tm_mon], - tm->tm_year + 1900, - tm->tm_hour, - tm->tm_min, - tm->tm_sec); - -#ifndef USE_HYPER - result = Curl_dyn_add(req, datestr); -#else - result = Curl_hyper_header(data, req, datestr); -#endif - - return result; -} -#else -/* disabled */ -CURLcode Curl_add_timecondition(struct Curl_easy *data, - struct dynbuf *req) -{ - (void)data; - (void)req; - return CURLE_OK; -} -#endif - -void Curl_http_method(struct Curl_easy *data, struct connectdata *conn, - const char **method, Curl_HttpReq *reqp) -{ - Curl_HttpReq httpreq = (Curl_HttpReq)data->state.httpreq; - const char *request; - if((conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) && - data->set.upload) - httpreq = HTTPREQ_PUT; - - /* Now set the 'request' pointer to the proper request string */ - if(data->set.str[STRING_CUSTOMREQUEST]) - request = data->set.str[STRING_CUSTOMREQUEST]; - else { - if(data->set.opt_no_body) - request = "HEAD"; - else { - DEBUGASSERT((httpreq >= HTTPREQ_GET) && (httpreq <= HTTPREQ_HEAD)); - switch(httpreq) { - case HTTPREQ_POST: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - request = "POST"; - break; - case HTTPREQ_PUT: - request = "PUT"; - break; - default: /* this should never happen */ - case HTTPREQ_GET: - request = "GET"; - break; - case HTTPREQ_HEAD: - request = "HEAD"; - break; - } - } - } - *method = request; - *reqp = httpreq; -} - -CURLcode Curl_http_useragent(struct Curl_easy *data) -{ - /* The User-Agent string might have been allocated in url.c already, because - it might have been used in the proxy connect, but if we have got a header - with the user-agent string specified, we erase the previously made string - here. */ - if(Curl_checkheaders(data, STRCONST("User-Agent"))) { - free(data->state.aptr.uagent); - data->state.aptr.uagent = NULL; - } - return CURLE_OK; -} - - -CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn) -{ - const char *ptr; - if(!data->state.this_is_a_follow) { - /* Free to avoid leaking memory on multiple requests*/ - free(data->state.first_host); - - data->state.first_host = strdup(conn->host.name); - if(!data->state.first_host) - return CURLE_OUT_OF_MEMORY; - - data->state.first_remote_port = conn->remote_port; - data->state.first_remote_protocol = conn->handler->protocol; - } - Curl_safefree(data->state.aptr.host); - - ptr = Curl_checkheaders(data, STRCONST("Host")); - if(ptr && (!data->state.this_is_a_follow || - strcasecompare(data->state.first_host, conn->host.name))) { -#if !defined(CURL_DISABLE_COOKIES) - /* If we have a given custom Host: header, we extract the host name in - order to possibly use it for cookie reasons later on. We only allow the - custom Host: header if this is NOT a redirect, as setting Host: in the - redirected request is being out on thin ice. Except if the host name - is the same as the first one! */ - char *cookiehost = Curl_copy_header_value(ptr); - if(!cookiehost) - return CURLE_OUT_OF_MEMORY; - if(!*cookiehost) - /* ignore empty data */ - free(cookiehost); - else { - /* If the host begins with '[', we start searching for the port after - the bracket has been closed */ - if(*cookiehost == '[') { - char *closingbracket; - /* since the 'cookiehost' is an allocated memory area that will be - freed later we cannot simply increment the pointer */ - memmove(cookiehost, cookiehost + 1, strlen(cookiehost) - 1); - closingbracket = strchr(cookiehost, ']'); - if(closingbracket) - *closingbracket = 0; - } - else { - int startsearch = 0; - char *colon = strchr(cookiehost + startsearch, ':'); - if(colon) - *colon = 0; /* The host must not include an embedded port number */ - } - Curl_safefree(data->state.aptr.cookiehost); - data->state.aptr.cookiehost = cookiehost; - } -#endif - - if(strcmp("Host:", ptr)) { - data->state.aptr.host = aprintf("Host:%s\r\n", &ptr[5]); - if(!data->state.aptr.host) - return CURLE_OUT_OF_MEMORY; - } - else - /* when clearing the header */ - data->state.aptr.host = NULL; - } - else { - /* When building Host: headers, we must put the host name within - [brackets] if the host name is a plain IPv6-address. RFC2732-style. */ - const char *host = conn->host.name; - - if(((conn->given->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS)) && - (conn->remote_port == PORT_HTTPS)) || - ((conn->given->protocol&(CURLPROTO_HTTP|CURLPROTO_WS)) && - (conn->remote_port == PORT_HTTP)) ) - /* if(HTTPS on port 443) OR (HTTP on port 80) then don't include - the port number in the host string */ - data->state.aptr.host = aprintf("Host: %s%s%s\r\n", - conn->bits.ipv6_ip?"[":"", - host, - conn->bits.ipv6_ip?"]":""); - else - data->state.aptr.host = aprintf("Host: %s%s%s:%d\r\n", - conn->bits.ipv6_ip?"[":"", - host, - conn->bits.ipv6_ip?"]":"", - conn->remote_port); - - if(!data->state.aptr.host) - /* without Host: we can't make a nice request */ - return CURLE_OUT_OF_MEMORY; - } - return CURLE_OK; -} - -/* - * Append the request-target to the HTTP request - */ -CURLcode Curl_http_target(struct Curl_easy *data, - struct connectdata *conn, - struct dynbuf *r) -{ - CURLcode result = CURLE_OK; - const char *path = data->state.up.path; - const char *query = data->state.up.query; - - if(data->set.str[STRING_TARGET]) { - path = data->set.str[STRING_TARGET]; - query = NULL; - } - -#ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { - /* Using a proxy but does not tunnel through it */ - - /* The path sent to the proxy is in fact the entire URL. But if the remote - host is a IDN-name, we must make sure that the request we produce only - uses the encoded host name! */ - - /* and no fragment part */ - CURLUcode uc; - char *url; - CURLU *h = curl_url_dup(data->state.uh); - if(!h) - return CURLE_OUT_OF_MEMORY; - - if(conn->host.dispname != conn->host.name) { - uc = curl_url_set(h, CURLUPART_HOST, conn->host.name, 0); - if(uc) { - curl_url_cleanup(h); - return CURLE_OUT_OF_MEMORY; - } - } - uc = curl_url_set(h, CURLUPART_FRAGMENT, NULL, 0); - if(uc) { - curl_url_cleanup(h); - return CURLE_OUT_OF_MEMORY; - } - - if(strcasecompare("http", data->state.up.scheme)) { - /* when getting HTTP, we don't want the userinfo the URL */ - uc = curl_url_set(h, CURLUPART_USER, NULL, 0); - if(uc) { - curl_url_cleanup(h); - return CURLE_OUT_OF_MEMORY; - } - uc = curl_url_set(h, CURLUPART_PASSWORD, NULL, 0); - if(uc) { - curl_url_cleanup(h); - return CURLE_OUT_OF_MEMORY; - } - } - /* Extract the URL to use in the request. Store in STRING_TEMP_URL for - clean-up reasons if the function returns before the free() further - down. */ - uc = curl_url_get(h, CURLUPART_URL, &url, CURLU_NO_DEFAULT_PORT); - if(uc) { - curl_url_cleanup(h); - return CURLE_OUT_OF_MEMORY; - } - - curl_url_cleanup(h); - - /* target or url */ - result = Curl_dyn_add(r, data->set.str[STRING_TARGET]? - data->set.str[STRING_TARGET]:url); - free(url); - if(result) - return (result); - - if(strcasecompare("ftp", data->state.up.scheme)) { - if(data->set.proxy_transfer_mode) { - /* when doing ftp, append ;type=<a|i> if not present */ - char *type = strstr(path, ";type="); - if(type && type[6] && type[7] == 0) { - switch(Curl_raw_toupper(type[6])) { - case 'A': - case 'D': - case 'I': - break; - default: - type = NULL; - } - } - if(!type) { - result = Curl_dyn_addf(r, ";type=%c", - data->state.prefer_ascii ? 'a' : 'i'); - if(result) - return result; - } - } - } - } - - else -#else - (void)conn; /* not used in disabled-proxy builds */ -#endif - { - result = Curl_dyn_add(r, path); - if(result) - return result; - if(query) - result = Curl_dyn_addf(r, "?%s", query); - } - - return result; -} - -CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn, - Curl_HttpReq httpreq, const char **tep) -{ - CURLcode result = CURLE_OK; - const char *ptr; - struct HTTP *http = data->req.p.http; - http->postsize = 0; - - switch(httpreq) { - case HTTPREQ_POST_MIME: - http->sendit = &data->set.mimepost; - break; - case HTTPREQ_POST_FORM: - /* Convert the form structure into a mime structure. */ - Curl_mime_cleanpart(&http->form); - result = Curl_getformdata(data, &http->form, data->set.httppost, - data->state.fread_func); - if(result) - return result; - http->sendit = &http->form; - break; - default: - http->sendit = NULL; - } - -#ifndef CURL_DISABLE_MIME - if(http->sendit) { - const char *cthdr = Curl_checkheaders(data, STRCONST("Content-Type")); - - /* Read and seek body only. */ - http->sendit->flags |= MIME_BODY_ONLY; - - /* Prepare the mime structure headers & set content type. */ - - if(cthdr) - for(cthdr += 13; *cthdr == ' '; cthdr++) - ; - else if(http->sendit->kind == MIMEKIND_MULTIPART) - cthdr = "multipart/form-data"; - - curl_mime_headers(http->sendit, data->set.headers, 0); - result = Curl_mime_prepare_headers(http->sendit, cthdr, - NULL, MIMESTRATEGY_FORM); - curl_mime_headers(http->sendit, NULL, 0); - if(!result) - result = Curl_mime_rewind(http->sendit); - if(result) - return result; - http->postsize = Curl_mime_size(http->sendit); - } -#endif - - ptr = Curl_checkheaders(data, STRCONST("Transfer-Encoding")); - if(ptr) { - /* Some kind of TE is requested, check if 'chunked' is chosen */ - data->req.upload_chunky = - Curl_compareheader(ptr, - STRCONST("Transfer-Encoding:"), STRCONST("chunked")); - } - else { - if((conn->handler->protocol & PROTO_FAMILY_HTTP) && - (((httpreq == HTTPREQ_POST_MIME || httpreq == HTTPREQ_POST_FORM) && - http->postsize < 0) || - ((data->set.upload || httpreq == HTTPREQ_POST) && - data->state.infilesize == -1))) { - if(conn->bits.authneg) - /* don't enable chunked during auth neg */ - ; - else if(Curl_use_http_1_1plus(data, conn)) { - if(conn->httpversion < 20) - /* HTTP, upload, unknown file size and not HTTP 1.0 */ - data->req.upload_chunky = TRUE; - } - else { - failf(data, "Chunky upload is not supported by HTTP 1.0"); - return CURLE_UPLOAD_FAILED; - } - } - else { - /* else, no chunky upload */ - data->req.upload_chunky = FALSE; - } - - if(data->req.upload_chunky) - *tep = "Transfer-Encoding: chunked\r\n"; - } - return result; -} - -CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, - struct dynbuf *r, Curl_HttpReq httpreq) -{ -#ifndef USE_HYPER - /* Hyper always handles the body separately */ - curl_off_t included_body = 0; -#else - /* from this point down, this function should not be used */ -#define Curl_buffer_send(a,b,c,d,e) CURLE_OK -#endif - CURLcode result = CURLE_OK; - struct HTTP *http = data->req.p.http; - const char *ptr; - - /* If 'authdone' is FALSE, we must not set the write socket index to the - Curl_transfer() call below, as we're not ready to actually upload any - data yet. */ - - switch(httpreq) { - - case HTTPREQ_PUT: /* Let's PUT the data to the server! */ - - if(conn->bits.authneg) - http->postsize = 0; - else - http->postsize = data->state.infilesize; - - if((http->postsize != -1) && !data->req.upload_chunky && - (conn->bits.authneg || - !Curl_checkheaders(data, STRCONST("Content-Length")))) { - /* only add Content-Length if not uploading chunked */ - result = Curl_dyn_addf(r, "Content-Length: %" CURL_FORMAT_CURL_OFF_T - "\r\n", http->postsize); - if(result) - return result; - } - - if(http->postsize) { - result = expect100(data, conn, r); - if(result) - return result; - } - - /* end of headers */ - result = Curl_dyn_addn(r, STRCONST("\r\n")); - if(result) - return result; - - /* set the upload size to the progress meter */ - Curl_pgrsSetUploadSize(data, http->postsize); - - /* this sends the buffer and frees all the buffer resources */ - result = Curl_buffer_send(r, data, &data->info.request_size, 0, - FIRSTSOCKET); - if(result) - failf(data, "Failed sending PUT request"); - else - /* prepare for transfer */ - Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, - http->postsize?FIRSTSOCKET:-1); - if(result) - return result; - break; - - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - /* This is form posting using mime data. */ - if(conn->bits.authneg) { - /* nothing to post! */ - result = Curl_dyn_addn(r, STRCONST("Content-Length: 0\r\n\r\n")); - if(result) - return result; - - result = Curl_buffer_send(r, data, &data->info.request_size, 0, - FIRSTSOCKET); - if(result) - failf(data, "Failed sending POST request"); - else - /* setup variables for the upcoming transfer */ - Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1); - break; - } - - data->state.infilesize = http->postsize; - - /* We only set Content-Length and allow a custom Content-Length if - we don't upload data chunked, as RFC2616 forbids us to set both - kinds of headers (Transfer-Encoding: chunked and Content-Length) */ - if(http->postsize != -1 && !data->req.upload_chunky && - (conn->bits.authneg || - !Curl_checkheaders(data, STRCONST("Content-Length")))) { - /* we allow replacing this header if not during auth negotiation, - although it isn't very wise to actually set your own */ - result = Curl_dyn_addf(r, - "Content-Length: %" CURL_FORMAT_CURL_OFF_T - "\r\n", http->postsize); - if(result) - return result; - } - -#ifndef CURL_DISABLE_MIME - /* Output mime-generated headers. */ - { - struct curl_slist *hdr; - - for(hdr = http->sendit->curlheaders; hdr; hdr = hdr->next) { - result = Curl_dyn_addf(r, "%s\r\n", hdr->data); - if(result) - return result; - } - } -#endif - - /* For really small posts we don't use Expect: headers at all, and for - the somewhat bigger ones we allow the app to disable it. Just make - sure that the expect100header is always set to the preferred value - here. */ - ptr = Curl_checkheaders(data, STRCONST("Expect")); - if(ptr) { - data->state.expect100header = - Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue")); - } - else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) { - result = expect100(data, conn, r); - if(result) - return result; - } - else - data->state.expect100header = FALSE; - - /* make the request end in a true CRLF */ - result = Curl_dyn_addn(r, STRCONST("\r\n")); - if(result) - return result; - - /* set the upload size to the progress meter */ - Curl_pgrsSetUploadSize(data, http->postsize); - - /* Read from mime structure. */ - data->state.fread_func = (curl_read_callback) Curl_mime_read; - data->state.in = (void *) http->sendit; - http->sending = HTTPSEND_BODY; - - /* this sends the buffer and frees all the buffer resources */ - result = Curl_buffer_send(r, data, &data->info.request_size, 0, - FIRSTSOCKET); - if(result) - failf(data, "Failed sending POST request"); - else - /* prepare for transfer */ - Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, - http->postsize?FIRSTSOCKET:-1); - if(result) - return result; - - break; - - case HTTPREQ_POST: - /* this is the simple POST, using x-www-form-urlencoded style */ - - if(conn->bits.authneg) - http->postsize = 0; - else - /* the size of the post body */ - http->postsize = data->state.infilesize; - - /* We only set Content-Length and allow a custom Content-Length if - we don't upload data chunked, as RFC2616 forbids us to set both - kinds of headers (Transfer-Encoding: chunked and Content-Length) */ - if((http->postsize != -1) && !data->req.upload_chunky && - (conn->bits.authneg || - !Curl_checkheaders(data, STRCONST("Content-Length")))) { - /* we allow replacing this header if not during auth negotiation, - although it isn't very wise to actually set your own */ - result = Curl_dyn_addf(r, "Content-Length: %" CURL_FORMAT_CURL_OFF_T - "\r\n", http->postsize); - if(result) - return result; - } - - if(!Curl_checkheaders(data, STRCONST("Content-Type"))) { - result = Curl_dyn_addn(r, STRCONST("Content-Type: application/" - "x-www-form-urlencoded\r\n")); - if(result) - return result; - } - - /* For really small posts we don't use Expect: headers at all, and for - the somewhat bigger ones we allow the app to disable it. Just make - sure that the expect100header is always set to the preferred value - here. */ - ptr = Curl_checkheaders(data, STRCONST("Expect")); - if(ptr) { - data->state.expect100header = - Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue")); - } - else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) { - result = expect100(data, conn, r); - if(result) - return result; - } - else - data->state.expect100header = FALSE; - -#ifndef USE_HYPER - /* With Hyper the body is always passed on separately */ - if(data->set.postfields) { - - /* In HTTP2, we send request body in DATA frame regardless of - its size. */ - if(conn->httpversion != 20 && - !data->state.expect100header && - (http->postsize < MAX_INITIAL_POST_SIZE)) { - /* if we don't use expect: 100 AND - postsize is less than MAX_INITIAL_POST_SIZE - - then append the post data to the HTTP request header. This limit - is no magic limit but only set to prevent really huge POSTs to - get the data duplicated with malloc() and family. */ - - /* end of headers! */ - result = Curl_dyn_addn(r, STRCONST("\r\n")); - if(result) - return result; - - if(!data->req.upload_chunky) { - /* We're not sending it 'chunked', append it to the request - already now to reduce the number if send() calls */ - result = Curl_dyn_addn(r, data->set.postfields, - (size_t)http->postsize); - included_body = http->postsize; - } - else { - if(http->postsize) { - char chunk[16]; - /* Append the POST data chunky-style */ - msnprintf(chunk, sizeof(chunk), "%x\r\n", (int)http->postsize); - result = Curl_dyn_add(r, chunk); - if(!result) { - included_body = http->postsize + strlen(chunk); - result = Curl_dyn_addn(r, data->set.postfields, - (size_t)http->postsize); - if(!result) - result = Curl_dyn_addn(r, STRCONST("\r\n")); - included_body += 2; - } - } - if(!result) { - result = Curl_dyn_addn(r, STRCONST("\x30\x0d\x0a\x0d\x0a")); - /* 0 CR LF CR LF */ - included_body += 5; - } - } - if(result) - return result; - /* Make sure the progress information is accurate */ - Curl_pgrsSetUploadSize(data, http->postsize); - } - else { - /* A huge POST coming up, do data separate from the request */ - http->postdata = data->set.postfields; - - http->sending = HTTPSEND_BODY; - - data->state.fread_func = (curl_read_callback)readmoredata; - data->state.in = (void *)data; - - /* set the upload size to the progress meter */ - Curl_pgrsSetUploadSize(data, http->postsize); - - /* end of headers! */ - result = Curl_dyn_addn(r, STRCONST("\r\n")); - if(result) - return result; - } - } - else -#endif - { - /* end of headers! */ - result = Curl_dyn_addn(r, STRCONST("\r\n")); - if(result) - return result; - - if(data->req.upload_chunky && conn->bits.authneg) { - /* Chunky upload is selected and we're negotiating auth still, send - end-of-data only */ - result = Curl_dyn_addn(r, (char *)STRCONST("\x30\x0d\x0a\x0d\x0a")); - /* 0 CR LF CR LF */ - if(result) - return result; - } - - else if(data->state.infilesize) { - /* set the upload size to the progress meter */ - Curl_pgrsSetUploadSize(data, http->postsize?http->postsize:-1); - - /* set the pointer to mark that we will send the post body using the - read callback, but only if we're not in authenticate negotiation */ - if(!conn->bits.authneg) - http->postdata = (char *)&http->postdata; - } - } - /* issue the request */ - result = Curl_buffer_send(r, data, &data->info.request_size, included_body, - FIRSTSOCKET); - - if(result) - failf(data, "Failed sending HTTP POST request"); - else - Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, - http->postdata?FIRSTSOCKET:-1); - break; - - default: - result = Curl_dyn_addn(r, STRCONST("\r\n")); - if(result) - return result; - - /* issue the request */ - result = Curl_buffer_send(r, data, &data->info.request_size, 0, - FIRSTSOCKET); - if(result) - failf(data, "Failed sending HTTP request"); -#ifdef USE_WEBSOCKETS - else if((conn->handler->protocol & (CURLPROTO_WS|CURLPROTO_WSS)) && - !(data->set.connect_only)) - /* Set up the transfer for two-way since without CONNECT_ONLY set, this - request probably wants to send data too post upgrade */ - Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET); -#endif - else - /* HTTP GET/HEAD download: */ - Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1); - } - - return result; -} - -#if !defined(CURL_DISABLE_COOKIES) - -CURLcode Curl_http_cookies(struct Curl_easy *data, - struct connectdata *conn, - struct dynbuf *r) -{ - CURLcode result = CURLE_OK; - char *addcookies = NULL; - bool linecap = FALSE; - if(data->set.str[STRING_COOKIE] && - !Curl_checkheaders(data, STRCONST("Cookie"))) - addcookies = data->set.str[STRING_COOKIE]; - - if(data->cookies || addcookies) { - struct Cookie *co = NULL; /* no cookies from start */ - int count = 0; - - if(data->cookies && data->state.cookie_engine) { - const char *host = data->state.aptr.cookiehost ? - data->state.aptr.cookiehost : conn->host.name; - const bool secure_context = - conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) || - strcasecompare("localhost", host) || - !strcmp(host, "127.0.0.1") || - !strcmp(host, "[::1]") ? TRUE : FALSE; - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - co = Curl_cookie_getlist(data, data->cookies, host, data->state.up.path, - secure_context); - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - } - if(co) { - struct Cookie *store = co; - /* now loop through all cookies that matched */ - while(co) { - if(co->value) { - if(0 == count) { - result = Curl_dyn_addn(r, STRCONST("Cookie: ")); - if(result) - break; - } - if((Curl_dyn_len(r) + strlen(co->name) + strlen(co->value) + 1) >= - MAX_COOKIE_HEADER_LEN) { - infof(data, "Restricted outgoing cookies due to header size, " - "'%s' not sent", co->name); - linecap = TRUE; - break; - } - result = Curl_dyn_addf(r, "%s%s=%s", count?"; ":"", - co->name, co->value); - if(result) - break; - count++; - } - co = co->next; /* next cookie please */ - } - Curl_cookie_freelist(store); - } - if(addcookies && !result && !linecap) { - if(!count) - result = Curl_dyn_addn(r, STRCONST("Cookie: ")); - if(!result) { - result = Curl_dyn_addf(r, "%s%s", count?"; ":"", addcookies); - count++; - } - } - if(count && !result) - result = Curl_dyn_addn(r, STRCONST("\r\n")); - - if(result) - return result; - } - return result; -} -#endif - -CURLcode Curl_http_range(struct Curl_easy *data, - Curl_HttpReq httpreq) -{ - if(data->state.use_range) { - /* - * A range is selected. We use different headers whether we're downloading - * or uploading and we always let customized headers override our internal - * ones if any such are specified. - */ - if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) && - !Curl_checkheaders(data, STRCONST("Range"))) { - /* if a line like this was already allocated, free the previous one */ - free(data->state.aptr.rangeline); - data->state.aptr.rangeline = aprintf("Range: bytes=%s\r\n", - data->state.range); - } - else if((httpreq == HTTPREQ_POST || httpreq == HTTPREQ_PUT) && - !Curl_checkheaders(data, STRCONST("Content-Range"))) { - - /* if a line like this was already allocated, free the previous one */ - free(data->state.aptr.rangeline); - - if(data->set.set_resume_from < 0) { - /* Upload resume was asked for, but we don't know the size of the - remote part so we tell the server (and act accordingly) that we - upload the whole file (again) */ - data->state.aptr.rangeline = - aprintf("Content-Range: bytes 0-%" CURL_FORMAT_CURL_OFF_T - "/%" CURL_FORMAT_CURL_OFF_T "\r\n", - data->state.infilesize - 1, data->state.infilesize); - - } - else if(data->state.resume_from) { - /* This is because "resume" was selected */ - curl_off_t total_expected_size = - data->state.resume_from + data->state.infilesize; - data->state.aptr.rangeline = - aprintf("Content-Range: bytes %s%" CURL_FORMAT_CURL_OFF_T - "/%" CURL_FORMAT_CURL_OFF_T "\r\n", - data->state.range, total_expected_size-1, - total_expected_size); - } - else { - /* Range was selected and then we just pass the incoming range and - append total size */ - data->state.aptr.rangeline = - aprintf("Content-Range: bytes %s/%" CURL_FORMAT_CURL_OFF_T "\r\n", - data->state.range, data->state.infilesize); - } - if(!data->state.aptr.rangeline) - return CURLE_OUT_OF_MEMORY; - } - } - return CURLE_OK; -} - -CURLcode Curl_http_resume(struct Curl_easy *data, - struct connectdata *conn, - Curl_HttpReq httpreq) -{ - if((HTTPREQ_POST == httpreq || HTTPREQ_PUT == httpreq) && - data->state.resume_from) { - /********************************************************************** - * Resuming upload in HTTP means that we PUT or POST and that we have - * got a resume_from value set. The resume value has already created - * a Range: header that will be passed along. We need to "fast forward" - * the file the given number of bytes and decrease the assume upload - * file size before we continue this venture in the dark lands of HTTP. - * Resuming mime/form posting at an offset > 0 has no sense and is ignored. - *********************************************************************/ - - if(data->state.resume_from < 0) { - /* - * This is meant to get the size of the present remote-file by itself. - * We don't support this now. Bail out! - */ - data->state.resume_from = 0; - } - - if(data->state.resume_from && !data->state.this_is_a_follow) { - /* do we still game? */ - - /* Now, let's read off the proper amount of bytes from the - input. */ - int seekerr = CURL_SEEKFUNC_CANTSEEK; - if(conn->seek_func) { - Curl_set_in_callback(data, true); - seekerr = conn->seek_func(conn->seek_client, data->state.resume_from, - SEEK_SET); - Curl_set_in_callback(data, false); - } - - if(seekerr != CURL_SEEKFUNC_OK) { - curl_off_t passed = 0; - - if(seekerr != CURL_SEEKFUNC_CANTSEEK) { - failf(data, "Could not seek stream"); - return CURLE_READ_ERROR; - } - /* when seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */ - do { - size_t readthisamountnow = - (data->state.resume_from - passed > data->set.buffer_size) ? - (size_t)data->set.buffer_size : - curlx_sotouz(data->state.resume_from - passed); - - size_t actuallyread = - data->state.fread_func(data->state.buffer, 1, readthisamountnow, - data->state.in); - - passed += actuallyread; - if((actuallyread == 0) || (actuallyread > readthisamountnow)) { - /* this checks for greater-than only to make sure that the - CURL_READFUNC_ABORT return code still aborts */ - failf(data, "Could only read %" CURL_FORMAT_CURL_OFF_T - " bytes from the input", passed); - return CURLE_READ_ERROR; - } - } while(passed < data->state.resume_from); - } - - /* now, decrease the size of the read */ - if(data->state.infilesize>0) { - data->state.infilesize -= data->state.resume_from; - - if(data->state.infilesize <= 0) { - failf(data, "File already completely uploaded"); - return CURLE_PARTIAL_FILE; - } - } - /* we've passed, proceed as normal */ - } - } - return CURLE_OK; -} - -CURLcode Curl_http_firstwrite(struct Curl_easy *data, - struct connectdata *conn, - bool *done) -{ - struct SingleRequest *k = &data->req; - - if(data->req.newurl) { - if(conn->bits.close) { - /* Abort after the headers if "follow Location" is set - and we're set to close anyway. */ - k->keepon &= ~KEEP_RECV; - *done = TRUE; - return CURLE_OK; - } - /* We have a new url to load, but since we want to be able to re-use this - connection properly, we read the full response in "ignore more" */ - k->ignorebody = TRUE; - infof(data, "Ignoring the response-body"); - } - if(data->state.resume_from && !k->content_range && - (data->state.httpreq == HTTPREQ_GET) && - !k->ignorebody) { - - if(k->size == data->state.resume_from) { - /* The resume point is at the end of file, consider this fine even if it - doesn't allow resume from here. */ - infof(data, "The entire document is already downloaded"); - streamclose(conn, "already downloaded"); - /* Abort download */ - k->keepon &= ~KEEP_RECV; - *done = TRUE; - return CURLE_OK; - } - - /* we wanted to resume a download, although the server doesn't seem to - * support this and we did this with a GET (if it wasn't a GET we did a - * POST or PUT resume) */ - failf(data, "HTTP server doesn't seem to support " - "byte ranges. Cannot resume."); - return CURLE_RANGE_ERROR; - } - - if(data->set.timecondition && !data->state.range) { - /* A time condition has been set AND no ranges have been requested. This - seems to be what chapter 13.3.4 of RFC 2616 defines to be the correct - action for a HTTP/1.1 client */ - - if(!Curl_meets_timecondition(data, k->timeofdoc)) { - *done = TRUE; - /* We're simulating a http 304 from server so we return - what should have been returned from the server */ - data->info.httpcode = 304; - infof(data, "Simulate a HTTP 304 response"); - /* we abort the transfer before it is completed == we ruin the - re-use ability. Close the connection */ - streamclose(conn, "Simulated 304 handling"); - return CURLE_OK; - } - } /* we have a time condition */ - - return CURLE_OK; -} - -#ifdef HAVE_LIBZ -CURLcode Curl_transferencode(struct Curl_easy *data) -{ - if(!Curl_checkheaders(data, STRCONST("TE")) && - data->set.http_transfer_encoding) { - /* When we are to insert a TE: header in the request, we must also insert - TE in a Connection: header, so we need to merge the custom provided - Connection: header and prevent the original to get sent. Note that if - the user has inserted his/her own TE: header we don't do this magic - but then assume that the user will handle it all! */ - char *cptr = Curl_checkheaders(data, STRCONST("Connection")); -#define TE_HEADER "TE: gzip\r\n" - - Curl_safefree(data->state.aptr.te); - - if(cptr) { - cptr = Curl_copy_header_value(cptr); - if(!cptr) - return CURLE_OUT_OF_MEMORY; - } - - /* Create the (updated) Connection: header */ - data->state.aptr.te = aprintf("Connection: %s%sTE\r\n" TE_HEADER, - cptr ? cptr : "", (cptr && *cptr) ? ", ":""); - - free(cptr); - if(!data->state.aptr.te) - return CURLE_OUT_OF_MEMORY; - } - return CURLE_OK; -} -#endif - -#ifndef USE_HYPER -/* - * Curl_http() gets called from the generic multi_do() function when a HTTP - * request is to be performed. This creates and sends a properly constructed - * HTTP request. - */ -CURLcode Curl_http(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - CURLcode result = CURLE_OK; - struct HTTP *http; - Curl_HttpReq httpreq; - const char *te = ""; /* transfer-encoding */ - const char *request; - const char *httpstring; - struct dynbuf req; - char *altused = NULL; - const char *p_accept; /* Accept: string */ - - /* Always consider the DO phase done after this function call, even if there - may be parts of the request that are not yet sent, since we can deal with - the rest of the request in the PERFORM phase. */ - *done = TRUE; - - if(conn->transport != TRNSPRT_QUIC) { - if(conn->httpversion < 20) { /* unless the connection is re-used and - already http2 */ - switch(conn->alpn) { - case CURL_HTTP_VERSION_2: - conn->httpversion = 20; /* we know we're on HTTP/2 now */ - - result = Curl_http2_switched(data, NULL, 0); - if(result) - return result; - break; - case CURL_HTTP_VERSION_1_1: - /* continue with HTTP/1.1 when explicitly requested */ - break; - default: - /* Check if user wants to use HTTP/2 with clear TCP*/ -#ifdef USE_NGHTTP2 - if(data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) { -#ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { - /* We don't support HTTP/2 proxies yet. Also it's debatable - whether or not this setting should apply to HTTP/2 proxies. */ - infof(data, "Ignoring HTTP/2 prior knowledge due to proxy"); - break; - } -#endif - DEBUGF(infof(data, "HTTP/2 over clean TCP")); - conn->httpversion = 20; - - result = Curl_http2_switched(data, NULL, 0); - if(result) - return result; - } -#endif - break; - } - } - else { - /* prepare for a http2 request */ - result = Curl_http2_setup(data, conn); - if(result) - return result; - } - } - http = data->req.p.http; - DEBUGASSERT(http); - - result = Curl_http_host(data, conn); - if(result) - return result; - - result = Curl_http_useragent(data); - if(result) - return result; - - Curl_http_method(data, conn, &request, &httpreq); - - /* setup the authentication headers */ - { - char *pq = NULL; - if(data->state.up.query) { - pq = aprintf("%s?%s", data->state.up.path, data->state.up.query); - if(!pq) - return CURLE_OUT_OF_MEMORY; - } - result = Curl_http_output_auth(data, conn, request, httpreq, - (pq ? pq : data->state.up.path), FALSE); - free(pq); - if(result) - return result; - } - - Curl_safefree(data->state.aptr.ref); - if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) { - data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer); - if(!data->state.aptr.ref) - return CURLE_OUT_OF_MEMORY; - } - - if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && - data->set.str[STRING_ENCODING]) { - Curl_safefree(data->state.aptr.accept_encoding); - data->state.aptr.accept_encoding = - aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]); - if(!data->state.aptr.accept_encoding) - return CURLE_OUT_OF_MEMORY; - } - else - Curl_safefree(data->state.aptr.accept_encoding); - -#ifdef HAVE_LIBZ - /* we only consider transfer-encoding magic if libz support is built-in */ - result = Curl_transferencode(data); - if(result) - return result; -#endif - - result = Curl_http_body(data, conn, httpreq, &te); - if(result) - return result; - - p_accept = Curl_checkheaders(data, - STRCONST("Accept"))?NULL:"Accept: */*\r\n"; - - result = Curl_http_resume(data, conn, httpreq); - if(result) - return result; - - result = Curl_http_range(data, httpreq); - if(result) - return result; - - httpstring = get_http_string(data, conn); - - /* initialize a dynamic send-buffer */ - Curl_dyn_init(&req, DYN_HTTP_REQUEST); - - /* make sure the header buffer is reset - if there are leftovers from a - previous transfer */ - Curl_dyn_reset(&data->state.headerb); - - /* add the main request stuff */ - /* GET/HEAD/POST/PUT */ - result = Curl_dyn_addf(&req, "%s ", request); - if(!result) - result = Curl_http_target(data, conn, &req); - if(result) { - Curl_dyn_free(&req); - return result; - } - -#ifndef CURL_DISABLE_ALTSVC - if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) { - altused = aprintf("Alt-Used: %s:%d\r\n", - conn->conn_to_host.name, conn->conn_to_port); - if(!altused) { - Curl_dyn_free(&req); - return CURLE_OUT_OF_MEMORY; - } - } -#endif - result = - Curl_dyn_addf(&req, - " HTTP/%s\r\n" /* HTTP version */ - "%s" /* host */ - "%s" /* proxyuserpwd */ - "%s" /* userpwd */ - "%s" /* range */ - "%s" /* user agent */ - "%s" /* accept */ - "%s" /* TE: */ - "%s" /* accept-encoding */ - "%s" /* referer */ - "%s" /* Proxy-Connection */ - "%s" /* transfer-encoding */ - "%s",/* Alt-Used */ - - httpstring, - (data->state.aptr.host?data->state.aptr.host:""), - data->state.aptr.proxyuserpwd? - data->state.aptr.proxyuserpwd:"", - data->state.aptr.userpwd?data->state.aptr.userpwd:"", - (data->state.use_range && data->state.aptr.rangeline)? - data->state.aptr.rangeline:"", - (data->set.str[STRING_USERAGENT] && - *data->set.str[STRING_USERAGENT] && - data->state.aptr.uagent)? - data->state.aptr.uagent:"", - p_accept?p_accept:"", - data->state.aptr.te?data->state.aptr.te:"", - (data->set.str[STRING_ENCODING] && - *data->set.str[STRING_ENCODING] && - data->state.aptr.accept_encoding)? - data->state.aptr.accept_encoding:"", - (data->state.referer && data->state.aptr.ref)? - data->state.aptr.ref:"" /* Referer: <data> */, -#ifndef CURL_DISABLE_PROXY - (conn->bits.httpproxy && - !conn->bits.tunnel_proxy && - !Curl_checkheaders(data, STRCONST("Proxy-Connection")) && - !Curl_checkProxyheaders(data, - conn, - STRCONST("Proxy-Connection")))? - "Proxy-Connection: Keep-Alive\r\n":"", -#else - "", -#endif - te, - altused ? altused : "" - ); - - /* clear userpwd and proxyuserpwd to avoid re-using old credentials - * from re-used connections */ - Curl_safefree(data->state.aptr.userpwd); - Curl_safefree(data->state.aptr.proxyuserpwd); - free(altused); - - if(result) { - Curl_dyn_free(&req); - return result; - } - - if(!(conn->handler->flags&PROTOPT_SSL) && - conn->httpversion != 20 && - (data->state.httpwant == CURL_HTTP_VERSION_2)) { - /* append HTTP2 upgrade magic stuff to the HTTP request if it isn't done - over SSL */ - result = Curl_http2_request_upgrade(&req, data); - if(result) { - Curl_dyn_free(&req); - return result; - } - } - - result = Curl_http_cookies(data, conn, &req); - if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS)) - result = Curl_ws_request(data, &req); - if(!result) - result = Curl_add_timecondition(data, &req); - if(!result) - result = Curl_add_custom_headers(data, FALSE, &req); - - if(!result) { - http->postdata = NULL; /* nothing to post at this point */ - if((httpreq == HTTPREQ_GET) || - (httpreq == HTTPREQ_HEAD)) - Curl_pgrsSetUploadSize(data, 0); /* nothing */ - - /* bodysend takes ownership of the 'req' memory on success */ - result = Curl_http_bodysend(data, conn, &req, httpreq); - } - if(result) { - Curl_dyn_free(&req); - return result; - } - - if((http->postsize > -1) && - (http->postsize <= data->req.writebytecount) && - (http->sending != HTTPSEND_REQUEST)) - data->req.upload_done = TRUE; - - if(data->req.writebytecount) { - /* if a request-body has been sent off, we make sure this progress is noted - properly */ - Curl_pgrsSetUploadCounter(data, data->req.writebytecount); - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - - if(!http->postsize) { - /* already sent the entire request body, mark the "upload" as - complete */ - infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T - " out of %" CURL_FORMAT_CURL_OFF_T " bytes", - data->req.writebytecount, http->postsize); - data->req.upload_done = TRUE; - data->req.keepon &= ~KEEP_SEND; /* we're done writing */ - data->req.exp100 = EXP100_SEND_DATA; /* already sent */ - Curl_expire_done(data, EXPIRE_100_TIMEOUT); - } - } - - if((conn->httpversion == 20) && data->req.upload_chunky) - /* upload_chunky was set above to set up the request in a chunky fashion, - but is disabled here again to avoid that the chunked encoded version is - actually used when sending the request body over h2 */ - data->req.upload_chunky = FALSE; - return result; -} - -#endif /* USE_HYPER */ - -typedef enum { - STATUS_UNKNOWN, /* not enough data to tell yet */ - STATUS_DONE, /* a status line was read */ - STATUS_BAD /* not a status line */ -} statusline; - - -/* Check a string for a prefix. Check no more than 'len' bytes */ -static bool checkprefixmax(const char *prefix, const char *buffer, size_t len) -{ - size_t ch = CURLMIN(strlen(prefix), len); - return curl_strnequal(prefix, buffer, ch); -} - -/* - * checkhttpprefix() - * - * Returns TRUE if member of the list matches prefix of string - */ -static statusline -checkhttpprefix(struct Curl_easy *data, - const char *s, size_t len) -{ - struct curl_slist *head = data->set.http200aliases; - statusline rc = STATUS_BAD; - statusline onmatch = len >= 5? STATUS_DONE : STATUS_UNKNOWN; - - while(head) { - if(checkprefixmax(head->data, s, len)) { - rc = onmatch; - break; - } - head = head->next; - } - - if((rc != STATUS_DONE) && (checkprefixmax("HTTP/", s, len))) - rc = onmatch; - - return rc; -} - -#ifndef CURL_DISABLE_RTSP -static statusline -checkrtspprefix(struct Curl_easy *data, - const char *s, size_t len) -{ - statusline result = STATUS_BAD; - statusline onmatch = len >= 5? STATUS_DONE : STATUS_UNKNOWN; - (void)data; /* unused */ - if(checkprefixmax("RTSP/", s, len)) - result = onmatch; - - return result; -} -#endif /* CURL_DISABLE_RTSP */ - -static statusline -checkprotoprefix(struct Curl_easy *data, struct connectdata *conn, - const char *s, size_t len) -{ -#ifndef CURL_DISABLE_RTSP - if(conn->handler->protocol & CURLPROTO_RTSP) - return checkrtspprefix(data, s, len); -#else - (void)conn; -#endif /* CURL_DISABLE_RTSP */ - - return checkhttpprefix(data, s, len); -} - -/* - * Curl_http_header() parses a single response header. - */ -CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, - char *headp) -{ - CURLcode result; - struct SingleRequest *k = &data->req; - /* Check for Content-Length: header lines to get size */ - if(!k->http_bodyless && - !data->set.ignorecl && checkprefix("Content-Length:", headp)) { - curl_off_t contentlength; - CURLofft offt = curlx_strtoofft(headp + strlen("Content-Length:"), - NULL, 10, &contentlength); - - if(offt == CURL_OFFT_OK) { - k->size = contentlength; - k->maxdownload = k->size; - } - else if(offt == CURL_OFFT_FLOW) { - /* out of range */ - if(data->set.max_filesize) { - failf(data, "Maximum file size exceeded"); - return CURLE_FILESIZE_EXCEEDED; - } - streamclose(conn, "overflow content-length"); - infof(data, "Overflow Content-Length: value"); - } - else { - /* negative or just rubbish - bad HTTP */ - failf(data, "Invalid Content-Length: value"); - return CURLE_WEIRD_SERVER_REPLY; - } - } - /* check for Content-Type: header lines to get the MIME-type */ - else if(checkprefix("Content-Type:", headp)) { - char *contenttype = Curl_copy_header_value(headp); - if(!contenttype) - return CURLE_OUT_OF_MEMORY; - if(!*contenttype) - /* ignore empty data */ - free(contenttype); - else { - Curl_safefree(data->info.contenttype); - data->info.contenttype = contenttype; - } - } -#ifndef CURL_DISABLE_PROXY - else if((conn->httpversion == 10) && - conn->bits.httpproxy && - Curl_compareheader(headp, - STRCONST("Proxy-Connection:"), - STRCONST("keep-alive"))) { - /* - * When a HTTP/1.0 reply comes when using a proxy, the - * 'Proxy-Connection: keep-alive' line tells us the - * connection will be kept alive for our pleasure. - * Default action for 1.0 is to close. - */ - connkeep(conn, "Proxy-Connection keep-alive"); /* don't close */ - infof(data, "HTTP/1.0 proxy connection set to keep alive"); - } - else if((conn->httpversion == 11) && - conn->bits.httpproxy && - Curl_compareheader(headp, - STRCONST("Proxy-Connection:"), - STRCONST("close"))) { - /* - * We get a HTTP/1.1 response from a proxy and it says it'll - * close down after this transfer. - */ - connclose(conn, "Proxy-Connection: asked to close after done"); - infof(data, "HTTP/1.1 proxy connection set close"); - } -#endif - else if((conn->httpversion == 10) && - Curl_compareheader(headp, - STRCONST("Connection:"), - STRCONST("keep-alive"))) { - /* - * A HTTP/1.0 reply with the 'Connection: keep-alive' line - * tells us the connection will be kept alive for our - * pleasure. Default action for 1.0 is to close. - * - * [RFC2068, section 19.7.1] */ - connkeep(conn, "Connection keep-alive"); - infof(data, "HTTP/1.0 connection set to keep alive"); - } - else if(Curl_compareheader(headp, - STRCONST("Connection:"), STRCONST("close"))) { - /* - * [RFC 2616, section 8.1.2.1] - * "Connection: close" is HTTP/1.1 language and means that - * the connection will close when this request has been - * served. - */ - streamclose(conn, "Connection: close used"); - } - else if(!k->http_bodyless && checkprefix("Transfer-Encoding:", headp)) { - /* One or more encodings. We check for chunked and/or a compression - algorithm. */ - /* - * [RFC 2616, section 3.6.1] A 'chunked' transfer encoding - * means that the server will send a series of "chunks". Each - * chunk starts with line with info (including size of the - * coming block) (terminated with CRLF), then a block of data - * with the previously mentioned size. There can be any amount - * of chunks, and a chunk-data set to zero signals the - * end-of-chunks. */ - - result = Curl_build_unencoding_stack(data, - headp + strlen("Transfer-Encoding:"), - TRUE); - if(result) - return result; - if(!k->chunk) { - /* if this isn't chunked, only close can signal the end of this transfer - as Content-Length is said not to be trusted for transfer-encoding! */ - connclose(conn, "HTTP/1.1 transfer-encoding without chunks"); - k->ignore_cl = TRUE; - } - } - else if(!k->http_bodyless && checkprefix("Content-Encoding:", headp) && - data->set.str[STRING_ENCODING]) { - /* - * Process Content-Encoding. Look for the values: identity, - * gzip, deflate, compress, x-gzip and x-compress. x-gzip and - * x-compress are the same as gzip and compress. (Sec 3.5 RFC - * 2616). zlib cannot handle compress. However, errors are - * handled further down when the response body is processed - */ - result = Curl_build_unencoding_stack(data, - headp + strlen("Content-Encoding:"), - FALSE); - if(result) - return result; - } - else if(checkprefix("Retry-After:", headp)) { - /* Retry-After = HTTP-date / delay-seconds */ - curl_off_t retry_after = 0; /* zero for unknown or "now" */ - /* Try it as a decimal number, if it works it is not a date */ - (void)curlx_strtoofft(headp + strlen("Retry-After:"), - NULL, 10, &retry_after); - if(!retry_after) { - time_t date = Curl_getdate_capped(headp + strlen("Retry-After:")); - if(-1 != date) - /* convert date to number of seconds into the future */ - retry_after = date - time(NULL); - } - data->info.retry_after = retry_after; /* store it */ - } - else if(!k->http_bodyless && checkprefix("Content-Range:", headp)) { - /* Content-Range: bytes [num]- - Content-Range: bytes: [num]- - Content-Range: [num]- - Content-Range: [asterisk]/[total] - - The second format was added since Sun's webserver - JavaWebServer/1.1.1 obviously sends the header this way! - The third added since some servers use that! - The fourth means the requested range was unsatisfied. - */ - - char *ptr = headp + strlen("Content-Range:"); - - /* Move forward until first digit or asterisk */ - while(*ptr && !ISDIGIT(*ptr) && *ptr != '*') - ptr++; - - /* if it truly stopped on a digit */ - if(ISDIGIT(*ptr)) { - if(!curlx_strtoofft(ptr, NULL, 10, &k->offset)) { - if(data->state.resume_from == k->offset) - /* we asked for a resume and we got it */ - k->content_range = TRUE; - } - } - else - data->state.resume_from = 0; /* get everything */ - } -#if !defined(CURL_DISABLE_COOKIES) - else if(data->cookies && data->state.cookie_engine && - checkprefix("Set-Cookie:", headp)) { - /* If there is a custom-set Host: name, use it here, or else use real peer - host name. */ - const char *host = data->state.aptr.cookiehost? - data->state.aptr.cookiehost:conn->host.name; - const bool secure_context = - conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) || - strcasecompare("localhost", host) || - !strcmp(host, "127.0.0.1") || - !strcmp(host, "[::1]") ? TRUE : FALSE; - - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, - CURL_LOCK_ACCESS_SINGLE); - Curl_cookie_add(data, data->cookies, TRUE, FALSE, - headp + strlen("Set-Cookie:"), host, - data->state.up.path, secure_context); - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - } -#endif - else if(!k->http_bodyless && checkprefix("Last-Modified:", headp) && - (data->set.timecondition || data->set.get_filetime) ) { - k->timeofdoc = Curl_getdate_capped(headp + strlen("Last-Modified:")); - if(data->set.get_filetime) - data->info.filetime = k->timeofdoc; - } - else if((checkprefix("WWW-Authenticate:", headp) && - (401 == k->httpcode)) || - (checkprefix("Proxy-authenticate:", headp) && - (407 == k->httpcode))) { - - bool proxy = (k->httpcode == 407) ? TRUE : FALSE; - char *auth = Curl_copy_header_value(headp); - if(!auth) - return CURLE_OUT_OF_MEMORY; - - result = Curl_http_input_auth(data, proxy, auth); - - free(auth); - - if(result) - return result; - } -#ifdef USE_SPNEGO - else if(checkprefix("Persistent-Auth:", headp)) { - struct negotiatedata *negdata = &conn->negotiate; - struct auth *authp = &data->state.authhost; - if(authp->picked == CURLAUTH_NEGOTIATE) { - char *persistentauth = Curl_copy_header_value(headp); - if(!persistentauth) - return CURLE_OUT_OF_MEMORY; - negdata->noauthpersist = checkprefix("false", persistentauth)? - TRUE:FALSE; - negdata->havenoauthpersist = TRUE; - infof(data, "Negotiate: noauthpersist -> %d, header part: %s", - negdata->noauthpersist, persistentauth); - free(persistentauth); - } - } -#endif - else if((k->httpcode >= 300 && k->httpcode < 400) && - checkprefix("Location:", headp) && - !data->req.location) { - /* this is the URL that the server advises us to use instead */ - char *location = Curl_copy_header_value(headp); - if(!location) - return CURLE_OUT_OF_MEMORY; - if(!*location) - /* ignore empty data */ - free(location); - else { - data->req.location = location; - - if(data->set.http_follow_location) { - DEBUGASSERT(!data->req.newurl); - data->req.newurl = strdup(data->req.location); /* clone */ - if(!data->req.newurl) - return CURLE_OUT_OF_MEMORY; - - /* some cases of POST and PUT etc needs to rewind the data - stream at this point */ - result = http_perhapsrewind(data, conn); - if(result) - return result; - } - } - } - -#ifndef CURL_DISABLE_HSTS - /* If enabled, the header is incoming and this is over HTTPS */ - else if(data->hsts && checkprefix("Strict-Transport-Security:", headp) && - ((conn->handler->flags & PROTOPT_SSL) || -#ifdef CURLDEBUG - /* allow debug builds to circumvent the HTTPS restriction */ - getenv("CURL_HSTS_HTTP") -#else - 0 -#endif - )) { - CURLcode check = - Curl_hsts_parse(data->hsts, data->state.up.hostname, - headp + strlen("Strict-Transport-Security:")); - if(check) - infof(data, "Illegal STS header skipped"); -#ifdef DEBUGBUILD - else - infof(data, "Parsed STS header fine (%zu entries)", - data->hsts->list.size); -#endif - } -#endif -#ifndef CURL_DISABLE_ALTSVC - /* If enabled, the header is incoming and this is over HTTPS */ - else if(data->asi && checkprefix("Alt-Svc:", headp) && - ((conn->handler->flags & PROTOPT_SSL) || -#ifdef CURLDEBUG - /* allow debug builds to circumvent the HTTPS restriction */ - getenv("CURL_ALTSVC_HTTP") -#else - 0 -#endif - )) { - /* the ALPN of the current request */ - enum alpnid id = (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1; - result = Curl_altsvc_parse(data, data->asi, - headp + strlen("Alt-Svc:"), - id, conn->host.name, - curlx_uitous(conn->remote_port)); - if(result) - return result; - } -#endif - else if(conn->handler->protocol & CURLPROTO_RTSP) { - result = Curl_rtsp_parseheader(data, headp); - if(result) - return result; - } - return CURLE_OK; -} - -/* - * Called after the first HTTP response line (the status line) has been - * received and parsed. - */ - -CURLcode Curl_http_statusline(struct Curl_easy *data, - struct connectdata *conn) -{ - struct SingleRequest *k = &data->req; - data->info.httpcode = k->httpcode; - - data->info.httpversion = conn->httpversion; - if(!data->state.httpversion || - data->state.httpversion > conn->httpversion) - /* store the lowest server version we encounter */ - data->state.httpversion = conn->httpversion; - - /* - * This code executes as part of processing the header. As a - * result, it's not totally clear how to interpret the - * response code yet as that depends on what other headers may - * be present. 401 and 407 may be errors, but may be OK - * depending on how authentication is working. Other codes - * are definitely errors, so give up here. - */ - if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET && - k->httpcode == 416) { - /* "Requested Range Not Satisfiable", just proceed and - pretend this is no error */ - k->ignorebody = TRUE; /* Avoid appending error msg to good data. */ - } - - if(conn->httpversion == 10) { - /* Default action for HTTP/1.0 must be to close, unless - we get one of those fancy headers that tell us the - server keeps it open for us! */ - infof(data, "HTTP 1.0, assume close after body"); - connclose(conn, "HTTP/1.0 close after body"); - } - else if(conn->httpversion == 20 || - (k->upgr101 == UPGR101_H2 && k->httpcode == 101)) { - DEBUGF(infof(data, "HTTP/2 found, allow multiplexing")); - /* HTTP/2 cannot avoid multiplexing since it is a core functionality - of the protocol */ - conn->bundle->multiuse = BUNDLE_MULTIPLEX; - } - else if(conn->httpversion >= 11 && - !conn->bits.close) { - /* If HTTP version is >= 1.1 and connection is persistent */ - DEBUGF(infof(data, - "HTTP 1.1 or later with persistent connection")); - } - - k->http_bodyless = k->httpcode >= 100 && k->httpcode < 200; - switch(k->httpcode) { - case 304: - /* (quote from RFC2616, section 10.3.5): The 304 response - * MUST NOT contain a message-body, and thus is always - * terminated by the first empty line after the header - * fields. */ - if(data->set.timecondition) - data->info.timecond = TRUE; - /* FALLTHROUGH */ - case 204: - /* (quote from RFC2616, section 10.2.5): The server has - * fulfilled the request but does not need to return an - * entity-body ... The 204 response MUST NOT include a - * message-body, and thus is always terminated by the first - * empty line after the header fields. */ - k->size = 0; - k->maxdownload = 0; - k->http_bodyless = TRUE; - break; - default: - break; - } - return CURLE_OK; -} - -/* Content-Length must be ignored if any Transfer-Encoding is present in the - response. Refer to RFC 7230 section 3.3.3 and RFC2616 section 4.4. This is - figured out here after all headers have been received but before the final - call to the user's header callback, so that a valid content length can be - retrieved by the user in the final call. */ -CURLcode Curl_http_size(struct Curl_easy *data) -{ - struct SingleRequest *k = &data->req; - if(data->req.ignore_cl || k->chunk) { - k->size = k->maxdownload = -1; - } - else if(k->size != -1) { - if(data->set.max_filesize && - k->size > data->set.max_filesize) { - failf(data, "Maximum file size exceeded"); - return CURLE_FILESIZE_EXCEEDED; - } - Curl_pgrsSetDownloadSize(data, k->size); - k->maxdownload = k->size; - } - return CURLE_OK; -} - -static CURLcode verify_header(struct Curl_easy *data) -{ - struct SingleRequest *k = &data->req; - const char *header = Curl_dyn_ptr(&data->state.headerb); - size_t hlen = Curl_dyn_len(&data->state.headerb); - char *ptr = memchr(header, 0x00, hlen); - if(ptr) { - /* this is bad, bail out */ - failf(data, "Nul byte in header"); - return CURLE_WEIRD_SERVER_REPLY; - } - if(k->headerline < 2) - /* the first "header" is the status-line and it has no colon */ - return CURLE_OK; - if(((header[0] == ' ') || (header[0] == '\t')) && k->headerline > 2) - /* line folding, can't happen on line 2 */ - ; - else { - ptr = memchr(header, ':', hlen); - if(!ptr) { - /* this is bad, bail out */ - failf(data, "Header without colon"); - return CURLE_WEIRD_SERVER_REPLY; - } - } - return CURLE_OK; -} - -/* - * Read any HTTP header lines from the server and pass them to the client app. - */ -CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, - struct connectdata *conn, - ssize_t *nread, - bool *stop_reading) -{ - CURLcode result; - struct SingleRequest *k = &data->req; - ssize_t onread = *nread; - char *ostr = k->str; - char *headp; - char *str_start; - char *end_ptr; - - /* header line within buffer loop */ - do { - size_t rest_length; - size_t full_length; - int writetype; - - /* str_start is start of line within buf */ - str_start = k->str; - - /* data is in network encoding so use 0x0a instead of '\n' */ - end_ptr = memchr(str_start, 0x0a, *nread); - - if(!end_ptr) { - /* Not a complete header line within buffer, append the data to - the end of the headerbuff. */ - result = Curl_dyn_addn(&data->state.headerb, str_start, *nread); - if(result) - return result; - - if(!k->headerline) { - /* check if this looks like a protocol header */ - statusline st = - checkprotoprefix(data, conn, - Curl_dyn_ptr(&data->state.headerb), - Curl_dyn_len(&data->state.headerb)); - - if(st == STATUS_BAD) { - /* this is not the beginning of a protocol first header line */ - k->header = FALSE; - k->badheader = HEADER_ALLBAD; - streamclose(conn, "bad HTTP: No end-of-message indicator"); - if(!data->set.http09_allowed) { - failf(data, "Received HTTP/0.9 when not allowed"); - return CURLE_UNSUPPORTED_PROTOCOL; - } - break; - } - } - - break; /* read more and try again */ - } - - /* decrease the size of the remaining (supposed) header line */ - rest_length = (end_ptr - k->str) + 1; - *nread -= (ssize_t)rest_length; - - k->str = end_ptr + 1; /* move past new line */ - - full_length = k->str - str_start; - - result = Curl_dyn_addn(&data->state.headerb, str_start, full_length); - if(result) - return result; - - /**** - * We now have a FULL header line in 'headerb'. - *****/ - - if(!k->headerline) { - /* the first read header */ - statusline st = checkprotoprefix(data, conn, - Curl_dyn_ptr(&data->state.headerb), - Curl_dyn_len(&data->state.headerb)); - if(st == STATUS_BAD) { - streamclose(conn, "bad HTTP: No end-of-message indicator"); - /* this is not the beginning of a protocol first header line */ - if(!data->set.http09_allowed) { - failf(data, "Received HTTP/0.9 when not allowed"); - return CURLE_UNSUPPORTED_PROTOCOL; - } - k->header = FALSE; - if(*nread) - /* since there's more, this is a partial bad header */ - k->badheader = HEADER_PARTHEADER; - else { - /* this was all we read so it's all a bad header */ - k->badheader = HEADER_ALLBAD; - *nread = onread; - k->str = ostr; - return CURLE_OK; - } - break; - } - } - - /* headers are in network encoding so use 0x0a and 0x0d instead of '\n' - and '\r' */ - headp = Curl_dyn_ptr(&data->state.headerb); - if((0x0a == *headp) || (0x0d == *headp)) { - size_t headerlen; - /* Zero-length header line means end of headers! */ - - if('\r' == *headp) - headp++; /* pass the \r byte */ - if('\n' == *headp) - headp++; /* pass the \n byte */ - - if(100 <= k->httpcode && 199 >= k->httpcode) { - /* "A user agent MAY ignore unexpected 1xx status responses." */ - switch(k->httpcode) { - case 100: - /* - * We have made a HTTP PUT or POST and this is 1.1-lingo - * that tells us that the server is OK with this and ready - * to receive the data. - * However, we'll get more headers now so we must get - * back into the header-parsing state! - */ - k->header = TRUE; - k->headerline = 0; /* restart the header line counter */ - - /* if we did wait for this do enable write now! */ - if(k->exp100 > EXP100_SEND_DATA) { - k->exp100 = EXP100_SEND_DATA; - k->keepon |= KEEP_SEND; - Curl_expire_done(data, EXPIRE_100_TIMEOUT); - } - break; - case 101: - /* Switching Protocols */ - if(k->upgr101 == UPGR101_H2) { - /* Switching to HTTP/2 */ - infof(data, "Received 101, Switching to HTTP/2"); - k->upgr101 = UPGR101_RECEIVED; - - /* we'll get more headers (HTTP/2 response) */ - k->header = TRUE; - k->headerline = 0; /* restart the header line counter */ - - /* switch to http2 now. The bytes after response headers - are also processed here, otherwise they are lost. */ - result = Curl_http2_switched(data, k->str, *nread); - if(result) - return result; - *nread = 0; - } -#ifdef USE_WEBSOCKETS - else if(k->upgr101 == UPGR101_WS) { - /* verify the response */ - result = Curl_ws_accept(data); - if(result) - return result; - k->header = FALSE; /* no more header to parse! */ - if(data->set.connect_only) { - k->keepon &= ~KEEP_RECV; /* read no more content */ - *nread = 0; - } - } -#endif - else { - /* Not switching to another protocol */ - k->header = FALSE; /* no more header to parse! */ - } - break; - default: - /* the status code 1xx indicates a provisional response, so - we'll get another set of headers */ - k->header = TRUE; - k->headerline = 0; /* restart the header line counter */ - break; - } - } - else { - k->header = FALSE; /* no more header to parse! */ - - if((k->size == -1) && !k->chunk && !conn->bits.close && - (conn->httpversion == 11) && - !(conn->handler->protocol & CURLPROTO_RTSP) && - data->state.httpreq != HTTPREQ_HEAD) { - /* On HTTP 1.1, when connection is not to get closed, but no - Content-Length nor Transfer-Encoding chunked have been - received, according to RFC2616 section 4.4 point 5, we - assume that the server will close the connection to - signal the end of the document. */ - infof(data, "no chunk, no close, no size. Assume close to " - "signal end"); - streamclose(conn, "HTTP: No end-of-message indicator"); - } - } - - if(!k->header) { - result = Curl_http_size(data); - if(result) - return result; - } - - /* At this point we have some idea about the fate of the connection. - If we are closing the connection it may result auth failure. */ -#if defined(USE_NTLM) - if(conn->bits.close && - (((data->req.httpcode == 401) && - (conn->http_ntlm_state == NTLMSTATE_TYPE2)) || - ((data->req.httpcode == 407) && - (conn->proxy_ntlm_state == NTLMSTATE_TYPE2)))) { - infof(data, "Connection closure while negotiating auth (HTTP 1.0?)"); - data->state.authproblem = TRUE; - } -#endif -#if defined(USE_SPNEGO) - if(conn->bits.close && - (((data->req.httpcode == 401) && - (conn->http_negotiate_state == GSS_AUTHRECV)) || - ((data->req.httpcode == 407) && - (conn->proxy_negotiate_state == GSS_AUTHRECV)))) { - infof(data, "Connection closure while negotiating auth (HTTP 1.0?)"); - data->state.authproblem = TRUE; - } - if((conn->http_negotiate_state == GSS_AUTHDONE) && - (data->req.httpcode != 401)) { - conn->http_negotiate_state = GSS_AUTHSUCC; - } - if((conn->proxy_negotiate_state == GSS_AUTHDONE) && - (data->req.httpcode != 407)) { - conn->proxy_negotiate_state = GSS_AUTHSUCC; - } -#endif - - /* now, only output this if the header AND body are requested: - */ - writetype = CLIENTWRITE_HEADER | - (data->set.include_header ? CLIENTWRITE_BODY : 0) | - ((k->httpcode/100 == 1) ? CLIENTWRITE_1XX : 0); - - headerlen = Curl_dyn_len(&data->state.headerb); - result = Curl_client_write(data, writetype, - Curl_dyn_ptr(&data->state.headerb), - headerlen); - if(result) - return result; - - data->info.header_size += (long)headerlen; - data->req.headerbytecount += (long)headerlen; - - /* - * When all the headers have been parsed, see if we should give - * up and return an error. - */ - if(http_should_fail(data)) { - failf(data, "The requested URL returned error: %d", - k->httpcode); - return CURLE_HTTP_RETURNED_ERROR; - } - -#ifdef USE_WEBSOCKETS - /* All non-101 HTTP status codes are bad when wanting to upgrade to - websockets */ - if(data->req.upgr101 == UPGR101_WS) { - failf(data, "Refused WebSockets upgrade: %d", k->httpcode); - return CURLE_HTTP_RETURNED_ERROR; - } -#endif - - - data->req.deductheadercount = - (100 <= k->httpcode && 199 >= k->httpcode)?data->req.headerbytecount:0; - - /* Curl_http_auth_act() checks what authentication methods - * that are available and decides which one (if any) to - * use. It will set 'newurl' if an auth method was picked. */ - result = Curl_http_auth_act(data); - - if(result) - return result; - - if(k->httpcode >= 300) { - if((!conn->bits.authneg) && !conn->bits.close && - !conn->bits.rewindaftersend) { - /* - * General treatment of errors when about to send data. Including : - * "417 Expectation Failed", while waiting for 100-continue. - * - * The check for close above is done simply because of something - * else has already deemed the connection to get closed then - * something else should've considered the big picture and we - * avoid this check. - * - * rewindaftersend indicates that something has told libcurl to - * continue sending even if it gets discarded - */ - - switch(data->state.httpreq) { - case HTTPREQ_PUT: - case HTTPREQ_POST: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - /* We got an error response. If this happened before the whole - * request body has been sent we stop sending and mark the - * connection for closure after we've read the entire response. - */ - Curl_expire_done(data, EXPIRE_100_TIMEOUT); - if(!k->upload_done) { - if((k->httpcode == 417) && data->state.expect100header) { - /* 417 Expectation Failed - try again without the Expect - header */ - infof(data, "Got 417 while waiting for a 100"); - data->state.disableexpect = TRUE; - DEBUGASSERT(!data->req.newurl); - data->req.newurl = strdup(data->state.url); - Curl_done_sending(data, k); - } - else if(data->set.http_keep_sending_on_error) { - infof(data, "HTTP error before end of send, keep sending"); - if(k->exp100 > EXP100_SEND_DATA) { - k->exp100 = EXP100_SEND_DATA; - k->keepon |= KEEP_SEND; - } - } - else { - infof(data, "HTTP error before end of send, stop sending"); - streamclose(conn, "Stop sending data before everything sent"); - result = Curl_done_sending(data, k); - if(result) - return result; - k->upload_done = TRUE; - if(data->state.expect100header) - k->exp100 = EXP100_FAILED; - } - } - break; - - default: /* default label present to avoid compiler warnings */ - break; - } - } - - if(conn->bits.rewindaftersend) { - /* We rewind after a complete send, so thus we continue - sending now */ - infof(data, "Keep sending data to get tossed away"); - k->keepon |= KEEP_SEND; - } - } - - if(!k->header) { - /* - * really end-of-headers. - * - * If we requested a "no body", this is a good time to get - * out and return home. - */ - if(data->set.opt_no_body) - *stop_reading = TRUE; -#ifndef CURL_DISABLE_RTSP - else if((conn->handler->protocol & CURLPROTO_RTSP) && - (data->set.rtspreq == RTSPREQ_DESCRIBE) && - (k->size <= -1)) - /* Respect section 4.4 of rfc2326: If the Content-Length header is - absent, a length 0 must be assumed. It will prevent libcurl from - hanging on DESCRIBE request that got refused for whatever - reason */ - *stop_reading = TRUE; -#endif - - /* If max download size is *zero* (nothing) we already have - nothing and can safely return ok now! But for HTTP/2, we'd - like to call http2_handle_stream_close to properly close a - stream. In order to do this, we keep reading until we - close the stream. */ - if(0 == k->maxdownload -#if defined(USE_NGHTTP2) - && !((conn->handler->protocol & PROTO_FAMILY_HTTP) && - conn->httpversion == 20) -#endif - ) - *stop_reading = TRUE; - - if(*stop_reading) { - /* we make sure that this socket isn't read more now */ - k->keepon &= ~KEEP_RECV; - } - - Curl_debug(data, CURLINFO_HEADER_IN, str_start, headerlen); - break; /* exit header line loop */ - } - - /* We continue reading headers, reset the line-based header */ - Curl_dyn_reset(&data->state.headerb); - continue; - } - - /* - * Checks for special headers coming up. - */ - - writetype = CLIENTWRITE_HEADER; - if(!k->headerline++) { - /* This is the first header, it MUST be the error code line - or else we consider this to be the body right away! */ - int httpversion_major; - int rtspversion_major; - int nc = 0; -#define HEADER1 headp /* no conversion needed, just use headp */ - - if(conn->handler->protocol & PROTO_FAMILY_HTTP) { - /* - * https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2 - * - * The response code is always a three-digit number in HTTP as the spec - * says. We allow any three-digit number here, but we cannot make - * guarantees on future behaviors since it isn't within the protocol. - */ - char separator; - char twoorthree[2]; - int httpversion = 0; - char digit4 = 0; - nc = sscanf(HEADER1, - " HTTP/%1d.%1d%c%3d%c", - &httpversion_major, - &httpversion, - &separator, - &k->httpcode, - &digit4); - - if(nc == 1 && httpversion_major >= 2 && - 2 == sscanf(HEADER1, " HTTP/%1[23] %d", twoorthree, &k->httpcode)) { - conn->httpversion = 0; - nc = 4; - separator = ' '; - } - - /* There can only be a 4th response code digit stored in 'digit4' if - all the other fields were parsed and stored first, so nc is 5 when - digit4 a digit. - - The sscanf() line above will also allow zero-prefixed and negative - numbers, so we check for that too here. - */ - else if(ISDIGIT(digit4) || (nc >= 4 && k->httpcode < 100)) { - failf(data, "Unsupported response code in HTTP response"); - return CURLE_UNSUPPORTED_PROTOCOL; - } - - if((nc >= 4) && (' ' == separator)) { - httpversion += 10 * httpversion_major; - switch(httpversion) { - case 10: - case 11: -#ifdef USE_HTTP2 - case 20: -#endif -#ifdef ENABLE_QUIC - case 30: -#endif - conn->httpversion = (unsigned char)httpversion; - break; - default: - failf(data, "Unsupported HTTP version (%u.%d) in response", - httpversion/10, httpversion%10); - return CURLE_UNSUPPORTED_PROTOCOL; - } - - if(k->upgr101 == UPGR101_RECEIVED) { - /* supposedly upgraded to http2 now */ - if(conn->httpversion != 20) - infof(data, "Lying server, not serving HTTP/2"); - } - if(conn->httpversion < 20) { - conn->bundle->multiuse = BUNDLE_NO_MULTIUSE; - infof(data, "Mark bundle as not supporting multiuse"); - } - } - else if(!nc) { - /* this is the real world, not a Nirvana - NCSA 1.5.x returns this crap when asked for HTTP/1.1 - */ - nc = sscanf(HEADER1, " HTTP %3d", &k->httpcode); - conn->httpversion = 10; - - /* If user has set option HTTP200ALIASES, - compare header line against list of aliases - */ - if(!nc) { - statusline check = - checkhttpprefix(data, - Curl_dyn_ptr(&data->state.headerb), - Curl_dyn_len(&data->state.headerb)); - if(check == STATUS_DONE) { - nc = 1; - k->httpcode = 200; - conn->httpversion = 10; - } - } - } - else { - failf(data, "Unsupported HTTP version in response"); - return CURLE_UNSUPPORTED_PROTOCOL; - } - } - else if(conn->handler->protocol & CURLPROTO_RTSP) { - char separator; - int rtspversion; - nc = sscanf(HEADER1, - " RTSP/%1d.%1d%c%3d", - &rtspversion_major, - &rtspversion, - &separator, - &k->httpcode); - if((nc == 4) && (' ' == separator)) { - conn->httpversion = 11; /* For us, RTSP acts like HTTP 1.1 */ - } - else { - nc = 0; - } - } - - if(nc) { - result = Curl_http_statusline(data, conn); - if(result) - return result; - writetype |= CLIENTWRITE_STATUS; - } - else { - k->header = FALSE; /* this is not a header line */ - break; - } - } - - result = verify_header(data); - if(result) - return result; - - result = Curl_http_header(data, conn, headp); - if(result) - return result; - - /* - * End of header-checks. Write them to the client. - */ - if(data->set.include_header) - writetype |= CLIENTWRITE_BODY; - if(k->httpcode/100 == 1) - writetype |= CLIENTWRITE_1XX; - - Curl_debug(data, CURLINFO_HEADER_IN, headp, - Curl_dyn_len(&data->state.headerb)); - - result = Curl_client_write(data, writetype, headp, - Curl_dyn_len(&data->state.headerb)); - if(result) - return result; - - data->info.header_size += Curl_dyn_len(&data->state.headerb); - data->req.headerbytecount += Curl_dyn_len(&data->state.headerb); - - Curl_dyn_reset(&data->state.headerb); - } - while(*k->str); /* header line within buffer */ - - /* We might have reached the end of the header part here, but - there might be a non-header part left in the end of the read - buffer. */ - - return CURLE_OK; -} - -#endif /* CURL_DISABLE_HTTP */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+
+#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 HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef USE_HYPER
+#include <hyper.h>
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "transfer.h"
+#include "sendf.h"
+#include "formdata.h"
+#include "mime.h"
+#include "progress.h"
+#include "curl_base64.h"
+#include "cookie.h"
+#include "vauth/vauth.h"
+#include "vtls/vtls.h"
+#include "http_digest.h"
+#include "http_ntlm.h"
+#include "curl_ntlm_wb.h"
+#include "http_negotiate.h"
+#include "http_aws_sigv4.h"
+#include "url.h"
+#include "share.h"
+#include "hostip.h"
+#include "http.h"
+#include "select.h"
+#include "parsedate.h" /* for the week day and month names */
+#include "strtoofft.h"
+#include "multiif.h"
+#include "strcase.h"
+#include "content_encoding.h"
+#include "http_proxy.h"
+#include "warnless.h"
+#include "http2.h"
+#include "cfilters.h"
+#include "connect.h"
+#include "strdup.h"
+#include "altsvc.h"
+#include "hsts.h"
+#include "ws.h"
+#include "c-hyper.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * Forward declarations.
+ */
+
+static int http_getsock_do(struct Curl_easy *data,
+ struct connectdata *conn,
+ curl_socket_t *socks);
+static bool http_should_fail(struct Curl_easy *data);
+
+static CURLcode http_setup_conn(struct Curl_easy *data,
+ struct connectdata *conn);
+#ifdef USE_WEBSOCKETS
+static CURLcode ws_setup_conn(struct Curl_easy *data,
+ struct connectdata *conn);
+#endif
+
+/*
+ * HTTP handler interface.
+ */
+const struct Curl_handler Curl_handler_http = {
+ "HTTP", /* scheme */
+ http_setup_conn, /* setup_connection */
+ Curl_http, /* do_it */
+ Curl_http_done, /* done */
+ ZERO_NULL, /* do_more */
+ Curl_http_connect, /* connect_it */
+ ZERO_NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ ZERO_NULL, /* proto_getsock */
+ http_getsock_do, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ ZERO_NULL, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_HTTP, /* defport */
+ CURLPROTO_HTTP, /* protocol */
+ CURLPROTO_HTTP, /* family */
+ PROTOPT_CREDSPERREQUEST | /* flags */
+ PROTOPT_USERPWDCTRL
+};
+
+#ifdef USE_WEBSOCKETS
+const struct Curl_handler Curl_handler_ws = {
+ "WS", /* scheme */
+ ws_setup_conn, /* setup_connection */
+ Curl_http, /* do_it */
+ Curl_http_done, /* done */
+ ZERO_NULL, /* do_more */
+ Curl_http_connect, /* connect_it */
+ ZERO_NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ ZERO_NULL, /* proto_getsock */
+ http_getsock_do, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ ZERO_NULL, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_HTTP, /* defport */
+ CURLPROTO_WS, /* protocol */
+ CURLPROTO_HTTP, /* family */
+ PROTOPT_CREDSPERREQUEST | /* flags */
+ PROTOPT_USERPWDCTRL
+};
+#endif
+
+#ifdef USE_SSL
+/*
+ * HTTPS handler interface.
+ */
+const struct Curl_handler Curl_handler_https = {
+ "HTTPS", /* scheme */
+ http_setup_conn, /* setup_connection */
+ Curl_http, /* do_it */
+ Curl_http_done, /* done */
+ ZERO_NULL, /* do_more */
+ Curl_http_connect, /* connect_it */
+ NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ NULL, /* proto_getsock */
+ http_getsock_do, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ ZERO_NULL, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_HTTPS, /* defport */
+ CURLPROTO_HTTPS, /* protocol */
+ CURLPROTO_HTTP, /* family */
+ PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */
+ PROTOPT_USERPWDCTRL
+};
+
+#ifdef USE_WEBSOCKETS
+const struct Curl_handler Curl_handler_wss = {
+ "WSS", /* scheme */
+ ws_setup_conn, /* setup_connection */
+ Curl_http, /* do_it */
+ Curl_http_done, /* done */
+ ZERO_NULL, /* do_more */
+ Curl_http_connect, /* connect_it */
+ NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ NULL, /* proto_getsock */
+ http_getsock_do, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ ZERO_NULL, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_HTTPS, /* defport */
+ CURLPROTO_WSS, /* protocol */
+ CURLPROTO_HTTP, /* family */
+ PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */
+ PROTOPT_USERPWDCTRL
+};
+#endif
+
+#endif
+
+static CURLcode h3_setup_conn(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+#ifdef ENABLE_QUIC
+ /* We want HTTP/3 directly, setup the filter chain ourself,
+ * overriding the default behaviour. */
+ DEBUGASSERT(conn->transport == TRNSPRT_QUIC);
+
+ if(!(conn->handler->flags & PROTOPT_SSL)) {
+ failf(data, "HTTP/3 requested for non-HTTPS URL");
+ return CURLE_URL_MALFORMAT;
+ }
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.socksproxy) {
+ failf(data, "HTTP/3 is not supported over a SOCKS proxy");
+ return CURLE_URL_MALFORMAT;
+ }
+ if(conn->bits.httpproxy && conn->bits.tunnel_proxy) {
+ failf(data, "HTTP/3 is not supported over a HTTP proxy");
+ return CURLE_URL_MALFORMAT;
+ }
+#endif
+
+ DEBUGF(infof(data, "HTTP/3 direct conn setup(conn #%ld, index=%d)",
+ conn->connection_id, FIRSTSOCKET));
+ return Curl_conn_socket_set(data, conn, FIRSTSOCKET);
+
+#else /* ENABLE_QUIC */
+ (void)conn;
+ (void)data;
+ DEBUGF(infof(data, "QUIC is not supported in this build"));
+ return CURLE_NOT_BUILT_IN;
+#endif /* !ENABLE_QUIC */
+}
+
+static CURLcode http_setup_conn(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ /* allocate the HTTP-specific struct for the Curl_easy, only to survive
+ during this request */
+ struct HTTP *http;
+ DEBUGASSERT(data->req.p.http == NULL);
+
+ http = calloc(1, sizeof(struct HTTP));
+ if(!http)
+ return CURLE_OUT_OF_MEMORY;
+
+ Curl_mime_initpart(&http->form);
+ data->req.p.http = http;
+
+ if(data->state.httpwant == CURL_HTTP_VERSION_3) {
+ conn->transport = TRNSPRT_QUIC;
+ }
+
+ if(conn->transport == TRNSPRT_QUIC) {
+ return h3_setup_conn(data, conn);
+ }
+ else {
+ if(!CONN_INUSE(conn))
+ /* if not already multi-using, setup connection details */
+ Curl_http2_setup_conn(conn);
+ Curl_http2_setup_req(data);
+ }
+ return CURLE_OK;
+}
+
+#ifdef USE_WEBSOCKETS
+static CURLcode ws_setup_conn(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ /* websockets is 1.1 only (for now) */
+ data->state.httpwant = CURL_HTTP_VERSION_1_1;
+ return http_setup_conn(data, conn);
+}
+#endif
+
+#ifndef CURL_DISABLE_PROXY
+/*
+ * checkProxyHeaders() checks the linked list of custom proxy headers
+ * if proxy headers are not available, then it will lookup into http header
+ * link list
+ *
+ * It takes a connectdata struct as input to see if this is a proxy request or
+ * not, as it then might check a different header list. Provide the header
+ * prefix without colon!
+ */
+char *Curl_checkProxyheaders(struct Curl_easy *data,
+ const struct connectdata *conn,
+ const char *thisheader,
+ const size_t thislen)
+{
+ struct curl_slist *head;
+
+ for(head = (conn->bits.proxy && data->set.sep_headers) ?
+ data->set.proxyheaders : data->set.headers;
+ head; head = head->next) {
+ if(strncasecompare(head->data, thisheader, thislen) &&
+ Curl_headersep(head->data[thislen]))
+ return head->data;
+ }
+
+ return NULL;
+}
+#else
+/* disabled */
+#define Curl_checkProxyheaders(x,y,z,a) NULL
+#endif
+
+/*
+ * Strip off leading and trailing whitespace from the value in the
+ * given HTTP header line and return a strdupped copy. Returns NULL in
+ * case of allocation failure. Returns an empty string if the header value
+ * consists entirely of whitespace.
+ */
+char *Curl_copy_header_value(const char *header)
+{
+ const char *start;
+ const char *end;
+ char *value;
+ size_t len;
+
+ /* Find the end of the header name */
+ while(*header && (*header != ':'))
+ ++header;
+
+ if(*header)
+ /* Skip over colon */
+ ++header;
+
+ /* Find the first non-space letter */
+ start = header;
+ while(*start && ISSPACE(*start))
+ start++;
+
+ /* data is in the host encoding so
+ use '\r' and '\n' instead of 0x0d and 0x0a */
+ end = strchr(start, '\r');
+ if(!end)
+ end = strchr(start, '\n');
+ if(!end)
+ end = strchr(start, '\0');
+ if(!end)
+ return NULL;
+
+ /* skip all trailing space letters */
+ while((end > start) && ISSPACE(*end))
+ end--;
+
+ /* get length of the type */
+ len = end - start + 1;
+
+ value = malloc(len + 1);
+ if(!value)
+ return NULL;
+
+ memcpy(value, start, len);
+ value[len] = 0; /* null-terminate */
+
+ return value;
+}
+
+#ifndef CURL_DISABLE_HTTP_AUTH
+/*
+ * http_output_basic() sets up an Authorization: header (or the proxy version)
+ * for HTTP Basic authentication.
+ *
+ * Returns CURLcode.
+ */
+static CURLcode http_output_basic(struct Curl_easy *data, bool proxy)
+{
+ size_t size = 0;
+ char *authorization = NULL;
+ char **userp;
+ const char *user;
+ const char *pwd;
+ CURLcode result;
+ char *out;
+
+ /* credentials are unique per transfer for HTTP, do not use the ones for the
+ connection */
+ if(proxy) {
+#ifndef CURL_DISABLE_PROXY
+ userp = &data->state.aptr.proxyuserpwd;
+ user = data->state.aptr.proxyuser;
+ pwd = data->state.aptr.proxypasswd;
+#else
+ return CURLE_NOT_BUILT_IN;
+#endif
+ }
+ else {
+ userp = &data->state.aptr.userpwd;
+ user = data->state.aptr.user;
+ pwd = data->state.aptr.passwd;
+ }
+
+ out = aprintf("%s:%s", user ? user : "", pwd ? pwd : "");
+ if(!out)
+ return CURLE_OUT_OF_MEMORY;
+
+ result = Curl_base64_encode(out, strlen(out), &authorization, &size);
+ if(result)
+ goto fail;
+
+ if(!authorization) {
+ result = CURLE_REMOTE_ACCESS_DENIED;
+ goto fail;
+ }
+
+ free(*userp);
+ *userp = aprintf("%sAuthorization: Basic %s\r\n",
+ proxy ? "Proxy-" : "",
+ authorization);
+ free(authorization);
+ if(!*userp) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+
+ fail:
+ free(out);
+ return result;
+}
+
+/*
+ * http_output_bearer() sets up an Authorization: header
+ * for HTTP Bearer authentication.
+ *
+ * Returns CURLcode.
+ */
+static CURLcode http_output_bearer(struct Curl_easy *data)
+{
+ char **userp;
+ CURLcode result = CURLE_OK;
+
+ userp = &data->state.aptr.userpwd;
+ free(*userp);
+ *userp = aprintf("Authorization: Bearer %s\r\n",
+ data->set.str[STRING_BEARER]);
+
+ if(!*userp) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+
+ fail:
+ return result;
+}
+
+#endif
+
+/* pickoneauth() selects the most favourable authentication method from the
+ * ones available and the ones we want.
+ *
+ * return TRUE if one was picked
+ */
+static bool pickoneauth(struct auth *pick, unsigned long mask)
+{
+ bool picked;
+ /* only deal with authentication we want */
+ unsigned long avail = pick->avail & pick->want & mask;
+ picked = TRUE;
+
+ /* The order of these checks is highly relevant, as this will be the order
+ of preference in case of the existence of multiple accepted types. */
+ if(avail & CURLAUTH_NEGOTIATE)
+ pick->picked = CURLAUTH_NEGOTIATE;
+ else if(avail & CURLAUTH_BEARER)
+ pick->picked = CURLAUTH_BEARER;
+ else if(avail & CURLAUTH_DIGEST)
+ pick->picked = CURLAUTH_DIGEST;
+ else if(avail & CURLAUTH_NTLM)
+ pick->picked = CURLAUTH_NTLM;
+ else if(avail & CURLAUTH_NTLM_WB)
+ pick->picked = CURLAUTH_NTLM_WB;
+ else if(avail & CURLAUTH_BASIC)
+ pick->picked = CURLAUTH_BASIC;
+ else if(avail & CURLAUTH_AWS_SIGV4)
+ pick->picked = CURLAUTH_AWS_SIGV4;
+ else {
+ pick->picked = CURLAUTH_PICKNONE; /* we select to use nothing */
+ picked = FALSE;
+ }
+ pick->avail = CURLAUTH_NONE; /* clear it here */
+
+ return picked;
+}
+
+/*
+ * http_perhapsrewind()
+ *
+ * If we are doing POST or PUT {
+ * If we have more data to send {
+ * If we are doing NTLM {
+ * Keep sending since we must not disconnect
+ * }
+ * else {
+ * If there is more than just a little data left to send, close
+ * the current connection by force.
+ * }
+ * }
+ * If we have sent any data {
+ * If we don't have track of all the data {
+ * call app to tell it to rewind
+ * }
+ * else {
+ * rewind internally so that the operation can restart fine
+ * }
+ * }
+ * }
+ */
+static CURLcode http_perhapsrewind(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ struct HTTP *http = data->req.p.http;
+ curl_off_t bytessent;
+ curl_off_t expectsend = -1; /* default is unknown */
+
+ if(!http)
+ /* If this is still NULL, we have not reach very far and we can safely
+ skip this rewinding stuff */
+ return CURLE_OK;
+
+ switch(data->state.httpreq) {
+ case HTTPREQ_GET:
+ case HTTPREQ_HEAD:
+ return CURLE_OK;
+ default:
+ break;
+ }
+
+ bytessent = data->req.writebytecount;
+
+ if(conn->bits.authneg) {
+ /* This is a state where we are known to be negotiating and we don't send
+ any data then. */
+ expectsend = 0;
+ }
+ else if(!conn->bits.protoconnstart) {
+ /* HTTP CONNECT in progress: there is no body */
+ expectsend = 0;
+ }
+ else {
+ /* figure out how much data we are expected to send */
+ switch(data->state.httpreq) {
+ case HTTPREQ_POST:
+ case HTTPREQ_PUT:
+ if(data->state.infilesize != -1)
+ expectsend = data->state.infilesize;
+ break;
+ case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
+ expectsend = http->postsize;
+ break;
+ default:
+ break;
+ }
+ }
+
+ data->state.rewindbeforesend = FALSE; /* default */
+
+ if((expectsend == -1) || (expectsend > bytessent)) {
+#if defined(USE_NTLM)
+ /* There is still data left to send */
+ if((data->state.authproxy.picked == CURLAUTH_NTLM) ||
+ (data->state.authhost.picked == CURLAUTH_NTLM) ||
+ (data->state.authproxy.picked == CURLAUTH_NTLM_WB) ||
+ (data->state.authhost.picked == CURLAUTH_NTLM_WB)) {
+ if(((expectsend - bytessent) < 2000) ||
+ (conn->http_ntlm_state != NTLMSTATE_NONE) ||
+ (conn->proxy_ntlm_state != NTLMSTATE_NONE)) {
+ /* The NTLM-negotiation has started *OR* there is just a little (<2K)
+ data left to send, keep on sending. */
+
+ /* rewind data when completely done sending! */
+ if(!conn->bits.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) {
+ data->state.rewindbeforesend = TRUE;
+ infof(data, "Rewind stream before next send");
+ }
+
+ return CURLE_OK;
+ }
+
+ if(conn->bits.close)
+ /* this is already marked to get closed */
+ return CURLE_OK;
+
+ infof(data, "NTLM send, close instead of sending %"
+ CURL_FORMAT_CURL_OFF_T " bytes",
+ (curl_off_t)(expectsend - bytessent));
+ }
+#endif
+#if defined(USE_SPNEGO)
+ /* There is still data left to send */
+ if((data->state.authproxy.picked == CURLAUTH_NEGOTIATE) ||
+ (data->state.authhost.picked == CURLAUTH_NEGOTIATE)) {
+ if(((expectsend - bytessent) < 2000) ||
+ (conn->http_negotiate_state != GSS_AUTHNONE) ||
+ (conn->proxy_negotiate_state != GSS_AUTHNONE)) {
+ /* The NEGOTIATE-negotiation has started *OR*
+ there is just a little (<2K) data left to send, keep on sending. */
+
+ /* rewind data when completely done sending! */
+ if(!conn->bits.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) {
+ data->state.rewindbeforesend = TRUE;
+ infof(data, "Rewind stream before next send");
+ }
+
+ return CURLE_OK;
+ }
+
+ if(conn->bits.close)
+ /* this is already marked to get closed */
+ return CURLE_OK;
+
+ infof(data, "NEGOTIATE send, close instead of sending %"
+ CURL_FORMAT_CURL_OFF_T " bytes",
+ (curl_off_t)(expectsend - bytessent));
+ }
+#endif
+
+ /* This is not NEGOTIATE/NTLM or many bytes left to send: close */
+ streamclose(conn, "Mid-auth HTTP and much data left to send");
+ data->req.size = 0; /* don't download any more than 0 bytes */
+
+ /* There still is data left to send, but this connection is marked for
+ closure so we can safely do the rewind right now */
+ }
+
+ if(bytessent) {
+ /* mark for rewind since if we already sent something */
+ data->state.rewindbeforesend = TRUE;
+ infof(data, "Please rewind output before next send");
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_http_auth_act() gets called when all HTTP headers have been received
+ * and it checks what authentication methods that are available and decides
+ * which one (if any) to use. It will set 'newurl' if an auth method was
+ * picked.
+ */
+
+CURLcode Curl_http_auth_act(struct Curl_easy *data)
+{
+ struct connectdata *conn = data->conn;
+ bool pickhost = FALSE;
+ bool pickproxy = FALSE;
+ CURLcode result = CURLE_OK;
+ unsigned long authmask = ~0ul;
+
+ if(!data->set.str[STRING_BEARER])
+ authmask &= (unsigned long)~CURLAUTH_BEARER;
+
+ if(100 <= data->req.httpcode && data->req.httpcode <= 199)
+ /* this is a transient response code, ignore */
+ return CURLE_OK;
+
+ if(data->state.authproblem)
+ return data->set.http_fail_on_error?CURLE_HTTP_RETURNED_ERROR:CURLE_OK;
+
+ if((data->state.aptr.user || data->set.str[STRING_BEARER]) &&
+ ((data->req.httpcode == 401) ||
+ (conn->bits.authneg && data->req.httpcode < 300))) {
+ pickhost = pickoneauth(&data->state.authhost, authmask);
+ if(!pickhost)
+ data->state.authproblem = TRUE;
+ if(data->state.authhost.picked == CURLAUTH_NTLM &&
+ conn->httpversion > 11) {
+ infof(data, "Forcing HTTP/1.1 for NTLM");
+ connclose(conn, "Force HTTP/1.1 connection");
+ data->state.httpwant = CURL_HTTP_VERSION_1_1;
+ }
+ }
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.proxy_user_passwd &&
+ ((data->req.httpcode == 407) ||
+ (conn->bits.authneg && data->req.httpcode < 300))) {
+ pickproxy = pickoneauth(&data->state.authproxy,
+ authmask & ~CURLAUTH_BEARER);
+ if(!pickproxy)
+ data->state.authproblem = TRUE;
+ }
+#endif
+
+ if(pickhost || pickproxy) {
+ if((data->state.httpreq != HTTPREQ_GET) &&
+ (data->state.httpreq != HTTPREQ_HEAD) &&
+ !data->state.rewindbeforesend) {
+ result = http_perhapsrewind(data, conn);
+ if(result)
+ return result;
+ }
+ /* In case this is GSS auth, the newurl field is already allocated so
+ we must make sure to free it before allocating a new one. As figured
+ out in bug #2284386 */
+ Curl_safefree(data->req.newurl);
+ data->req.newurl = strdup(data->state.url); /* clone URL */
+ if(!data->req.newurl)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else if((data->req.httpcode < 300) &&
+ (!data->state.authhost.done) &&
+ conn->bits.authneg) {
+ /* no (known) authentication available,
+ authentication is not "done" yet and
+ no authentication seems to be required and
+ we didn't try HEAD or GET */
+ if((data->state.httpreq != HTTPREQ_GET) &&
+ (data->state.httpreq != HTTPREQ_HEAD)) {
+ data->req.newurl = strdup(data->state.url); /* clone URL */
+ if(!data->req.newurl)
+ return CURLE_OUT_OF_MEMORY;
+ data->state.authhost.done = TRUE;
+ }
+ }
+ if(http_should_fail(data)) {
+ failf(data, "The requested URL returned error: %d",
+ data->req.httpcode);
+ result = CURLE_HTTP_RETURNED_ERROR;
+ }
+
+ return result;
+}
+
+#ifndef CURL_DISABLE_HTTP_AUTH
+/*
+ * Output the correct authentication header depending on the auth type
+ * and whether or not it is to a proxy.
+ */
+static CURLcode
+output_auth_headers(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct auth *authstatus,
+ const char *request,
+ const char *path,
+ bool proxy)
+{
+ const char *auth = NULL;
+ CURLcode result = CURLE_OK;
+ (void)conn;
+
+#ifdef CURL_DISABLE_CRYPTO_AUTH
+ (void)request;
+ (void)path;
+#endif
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+ if(authstatus->picked == CURLAUTH_AWS_SIGV4) {
+ auth = "AWS_SIGV4";
+ result = Curl_output_aws_sigv4(data, proxy);
+ if(result)
+ return result;
+ }
+ else
+#endif
+#ifdef USE_SPNEGO
+ if(authstatus->picked == CURLAUTH_NEGOTIATE) {
+ auth = "Negotiate";
+ result = Curl_output_negotiate(data, conn, proxy);
+ if(result)
+ return result;
+ }
+ else
+#endif
+#ifdef USE_NTLM
+ if(authstatus->picked == CURLAUTH_NTLM) {
+ auth = "NTLM";
+ result = Curl_output_ntlm(data, proxy);
+ if(result)
+ return result;
+ }
+ else
+#endif
+#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED)
+ if(authstatus->picked == CURLAUTH_NTLM_WB) {
+ auth = "NTLM_WB";
+ result = Curl_output_ntlm_wb(data, conn, proxy);
+ if(result)
+ return result;
+ }
+ else
+#endif
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+ if(authstatus->picked == CURLAUTH_DIGEST) {
+ auth = "Digest";
+ result = Curl_output_digest(data,
+ proxy,
+ (const unsigned char *)request,
+ (const unsigned char *)path);
+ if(result)
+ return result;
+ }
+ else
+#endif
+ if(authstatus->picked == CURLAUTH_BASIC) {
+ /* Basic */
+ if(
+#ifndef CURL_DISABLE_PROXY
+ (proxy && conn->bits.proxy_user_passwd &&
+ !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-authorization"))) ||
+#endif
+ (!proxy && data->state.aptr.user &&
+ !Curl_checkheaders(data, STRCONST("Authorization")))) {
+ auth = "Basic";
+ result = http_output_basic(data, proxy);
+ if(result)
+ return result;
+ }
+
+ /* NOTE: this function should set 'done' TRUE, as the other auth
+ functions work that way */
+ authstatus->done = TRUE;
+ }
+ if(authstatus->picked == CURLAUTH_BEARER) {
+ /* Bearer */
+ if((!proxy && data->set.str[STRING_BEARER] &&
+ !Curl_checkheaders(data, STRCONST("Authorization")))) {
+ auth = "Bearer";
+ result = http_output_bearer(data);
+ if(result)
+ return result;
+ }
+
+ /* NOTE: this function should set 'done' TRUE, as the other auth
+ functions work that way */
+ authstatus->done = TRUE;
+ }
+
+ if(auth) {
+#ifndef CURL_DISABLE_PROXY
+ infof(data, "%s auth using %s with user '%s'",
+ proxy ? "Proxy" : "Server", auth,
+ proxy ? (data->state.aptr.proxyuser ?
+ data->state.aptr.proxyuser : "") :
+ (data->state.aptr.user ?
+ data->state.aptr.user : ""));
+#else
+ infof(data, "Server auth using %s with user '%s'",
+ auth, data->state.aptr.user ?
+ data->state.aptr.user : "");
+#endif
+ authstatus->multipass = (!authstatus->done) ? TRUE : FALSE;
+ }
+ else
+ authstatus->multipass = FALSE;
+
+ return CURLE_OK;
+}
+
+/**
+ * Curl_http_output_auth() setups the authentication headers for the
+ * host/proxy and the correct authentication
+ * method. data->state.authdone is set to TRUE when authentication is
+ * done.
+ *
+ * @param conn all information about the current connection
+ * @param request pointer to the request keyword
+ * @param path pointer to the requested path; should include query part
+ * @param proxytunnel boolean if this is the request setting up a "proxy
+ * tunnel"
+ *
+ * @returns CURLcode
+ */
+CURLcode
+Curl_http_output_auth(struct Curl_easy *data,
+ struct connectdata *conn,
+ const char *request,
+ Curl_HttpReq httpreq,
+ const char *path,
+ bool proxytunnel) /* TRUE if this is the request setting
+ up the proxy tunnel */
+{
+ CURLcode result = CURLE_OK;
+ struct auth *authhost;
+ struct auth *authproxy;
+
+ DEBUGASSERT(data);
+
+ authhost = &data->state.authhost;
+ authproxy = &data->state.authproxy;
+
+ if(
+#ifndef CURL_DISABLE_PROXY
+ (conn->bits.httpproxy && conn->bits.proxy_user_passwd) ||
+#endif
+ data->state.aptr.user || data->set.str[STRING_BEARER])
+ /* continue please */;
+ else {
+ authhost->done = TRUE;
+ authproxy->done = TRUE;
+ return CURLE_OK; /* no authentication with no user or password */
+ }
+
+ if(authhost->want && !authhost->picked)
+ /* The app has selected one or more methods, but none has been picked
+ so far by a server round-trip. Then we set the picked one to the
+ want one, and if this is one single bit it'll be used instantly. */
+ authhost->picked = authhost->want;
+
+ if(authproxy->want && !authproxy->picked)
+ /* The app has selected one or more methods, but none has been picked so
+ far by a proxy round-trip. Then we set the picked one to the want one,
+ and if this is one single bit it'll be used instantly. */
+ authproxy->picked = authproxy->want;
+
+#ifndef CURL_DISABLE_PROXY
+ /* Send proxy authentication header if needed */
+ if(conn->bits.httpproxy &&
+ (conn->bits.tunnel_proxy == (bit)proxytunnel)) {
+ result = output_auth_headers(data, conn, authproxy, request, path, TRUE);
+ if(result)
+ return result;
+ }
+ else
+#else
+ (void)proxytunnel;
+#endif /* CURL_DISABLE_PROXY */
+ /* we have no proxy so let's pretend we're done authenticating
+ with it */
+ authproxy->done = TRUE;
+
+ /* To prevent the user+password to get sent to other than the original host
+ due to a location-follow */
+ if(Curl_auth_allowed_to_host(data)
+#ifndef CURL_DISABLE_NETRC
+ || conn->bits.netrc
+#endif
+ )
+ result = output_auth_headers(data, conn, authhost, request, path, FALSE);
+ else
+ authhost->done = TRUE;
+
+ if(((authhost->multipass && !authhost->done) ||
+ (authproxy->multipass && !authproxy->done)) &&
+ (httpreq != HTTPREQ_GET) &&
+ (httpreq != HTTPREQ_HEAD)) {
+ /* Auth is required and we are not authenticated yet. Make a PUT or POST
+ with content-length zero as a "probe". */
+ conn->bits.authneg = TRUE;
+ }
+ else
+ conn->bits.authneg = FALSE;
+
+ return result;
+}
+
+#else
+/* when disabled */
+CURLcode
+Curl_http_output_auth(struct Curl_easy *data,
+ struct connectdata *conn,
+ const char *request,
+ Curl_HttpReq httpreq,
+ const char *path,
+ bool proxytunnel)
+{
+ (void)data;
+ (void)conn;
+ (void)request;
+ (void)httpreq;
+ (void)path;
+ (void)proxytunnel;
+ return CURLE_OK;
+}
+#endif
+
+/*
+ * Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate:
+ * headers. They are dealt with both in the transfer.c main loop and in the
+ * proxy CONNECT loop.
+ */
+
+static int is_valid_auth_separator(char ch)
+{
+ return ch == '\0' || ch == ',' || ISSPACE(ch);
+}
+
+CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
+ const char *auth) /* the first non-space */
+{
+ /*
+ * This resource requires authentication
+ */
+ struct connectdata *conn = data->conn;
+#ifdef USE_SPNEGO
+ curlnegotiate *negstate = proxy ? &conn->proxy_negotiate_state :
+ &conn->http_negotiate_state;
+#endif
+ unsigned long *availp;
+ struct auth *authp;
+
+ (void) conn; /* In case conditionals make it unused. */
+
+ if(proxy) {
+ availp = &data->info.proxyauthavail;
+ authp = &data->state.authproxy;
+ }
+ else {
+ availp = &data->info.httpauthavail;
+ authp = &data->state.authhost;
+ }
+
+ /*
+ * Here we check if we want the specific single authentication (using ==) and
+ * if we do, we initiate usage of it.
+ *
+ * If the provided authentication is wanted as one out of several accepted
+ * types (using &), we OR this authentication type to the authavail
+ * variable.
+ *
+ * Note:
+ *
+ * ->picked is first set to the 'want' value (one or more bits) before the
+ * request is sent, and then it is again set _after_ all response 401/407
+ * headers have been received but then only to a single preferred method
+ * (bit).
+ */
+
+ while(*auth) {
+#ifdef USE_SPNEGO
+ if(checkprefix("Negotiate", auth) && is_valid_auth_separator(auth[9])) {
+ if((authp->avail & CURLAUTH_NEGOTIATE) ||
+ Curl_auth_is_spnego_supported()) {
+ *availp |= CURLAUTH_NEGOTIATE;
+ authp->avail |= CURLAUTH_NEGOTIATE;
+
+ if(authp->picked == CURLAUTH_NEGOTIATE) {
+ CURLcode result = Curl_input_negotiate(data, conn, proxy, auth);
+ if(!result) {
+ DEBUGASSERT(!data->req.newurl);
+ data->req.newurl = strdup(data->state.url);
+ if(!data->req.newurl)
+ return CURLE_OUT_OF_MEMORY;
+ data->state.authproblem = FALSE;
+ /* we received a GSS auth token and we dealt with it fine */
+ *negstate = GSS_AUTHRECV;
+ }
+ else
+ data->state.authproblem = TRUE;
+ }
+ }
+ }
+ else
+#endif
+#ifdef USE_NTLM
+ /* NTLM support requires the SSL crypto libs */
+ if(checkprefix("NTLM", auth) && is_valid_auth_separator(auth[4])) {
+ if((authp->avail & CURLAUTH_NTLM) ||
+ (authp->avail & CURLAUTH_NTLM_WB) ||
+ Curl_auth_is_ntlm_supported()) {
+ *availp |= CURLAUTH_NTLM;
+ authp->avail |= CURLAUTH_NTLM;
+
+ if(authp->picked == CURLAUTH_NTLM ||
+ authp->picked == CURLAUTH_NTLM_WB) {
+ /* NTLM authentication is picked and activated */
+ CURLcode result = Curl_input_ntlm(data, proxy, auth);
+ if(!result) {
+ data->state.authproblem = FALSE;
+#ifdef NTLM_WB_ENABLED
+ if(authp->picked == CURLAUTH_NTLM_WB) {
+ *availp &= ~CURLAUTH_NTLM;
+ authp->avail &= ~CURLAUTH_NTLM;
+ *availp |= CURLAUTH_NTLM_WB;
+ authp->avail |= CURLAUTH_NTLM_WB;
+
+ result = Curl_input_ntlm_wb(data, conn, proxy, auth);
+ if(result) {
+ infof(data, "Authentication problem. Ignoring this.");
+ data->state.authproblem = TRUE;
+ }
+ }
+#endif
+ }
+ else {
+ infof(data, "Authentication problem. Ignoring this.");
+ data->state.authproblem = TRUE;
+ }
+ }
+ }
+ }
+ else
+#endif
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+ if(checkprefix("Digest", auth) && is_valid_auth_separator(auth[6])) {
+ if((authp->avail & CURLAUTH_DIGEST) != 0)
+ infof(data, "Ignoring duplicate digest auth header.");
+ else if(Curl_auth_is_digest_supported()) {
+ CURLcode result;
+
+ *availp |= CURLAUTH_DIGEST;
+ authp->avail |= CURLAUTH_DIGEST;
+
+ /* We call this function on input Digest headers even if Digest
+ * authentication isn't activated yet, as we need to store the
+ * incoming data from this header in case we are going to use
+ * Digest */
+ result = Curl_input_digest(data, proxy, auth);
+ if(result) {
+ infof(data, "Authentication problem. Ignoring this.");
+ data->state.authproblem = TRUE;
+ }
+ }
+ }
+ else
+#endif
+ if(checkprefix("Basic", auth) &&
+ is_valid_auth_separator(auth[5])) {
+ *availp |= CURLAUTH_BASIC;
+ authp->avail |= CURLAUTH_BASIC;
+ if(authp->picked == CURLAUTH_BASIC) {
+ /* We asked for Basic authentication but got a 40X back
+ anyway, which basically means our name+password isn't
+ valid. */
+ authp->avail = CURLAUTH_NONE;
+ infof(data, "Authentication problem. Ignoring this.");
+ data->state.authproblem = TRUE;
+ }
+ }
+ else
+ if(checkprefix("Bearer", auth) &&
+ is_valid_auth_separator(auth[6])) {
+ *availp |= CURLAUTH_BEARER;
+ authp->avail |= CURLAUTH_BEARER;
+ if(authp->picked == CURLAUTH_BEARER) {
+ /* We asked for Bearer authentication but got a 40X back
+ anyway, which basically means our token isn't valid. */
+ authp->avail = CURLAUTH_NONE;
+ infof(data, "Authentication problem. Ignoring this.");
+ data->state.authproblem = TRUE;
+ }
+ }
+
+ /* there may be multiple methods on one line, so keep reading */
+ while(*auth && *auth != ',') /* read up to the next comma */
+ auth++;
+ if(*auth == ',') /* if we're on a comma, skip it */
+ auth++;
+ while(*auth && ISSPACE(*auth))
+ auth++;
+ }
+
+ return CURLE_OK;
+}
+
+/**
+ * http_should_fail() determines whether an HTTP response has gotten us
+ * into an error state or not.
+ *
+ * @param conn all information about the current connection
+ *
+ * @retval FALSE communications should continue
+ *
+ * @retval TRUE communications should not continue
+ */
+static bool http_should_fail(struct Curl_easy *data)
+{
+ int httpcode;
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->conn);
+
+ httpcode = data->req.httpcode;
+
+ /*
+ ** If we haven't been asked to fail on error,
+ ** don't fail.
+ */
+ if(!data->set.http_fail_on_error)
+ return FALSE;
+
+ /*
+ ** Any code < 400 is never terminal.
+ */
+ if(httpcode < 400)
+ return FALSE;
+
+ /*
+ ** A 416 response to a resume request is presumably because the file is
+ ** already completely downloaded and thus not actually a fail.
+ */
+ if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET &&
+ httpcode == 416)
+ return FALSE;
+
+ /*
+ ** Any code >= 400 that's not 401 or 407 is always
+ ** a terminal error
+ */
+ if((httpcode != 401) && (httpcode != 407))
+ return TRUE;
+
+ /*
+ ** All we have left to deal with is 401 and 407
+ */
+ DEBUGASSERT((httpcode == 401) || (httpcode == 407));
+
+ /*
+ ** Examine the current authentication state to see if this
+ ** is an error. The idea is for this function to get
+ ** called after processing all the headers in a response
+ ** message. So, if we've been to asked to authenticate a
+ ** particular stage, and we've done it, we're OK. But, if
+ ** we're already completely authenticated, it's not OK to
+ ** get another 401 or 407.
+ **
+ ** It is possible for authentication to go stale such that
+ ** the client needs to reauthenticate. Once that info is
+ ** available, use it here.
+ */
+
+ /*
+ ** Either we're not authenticating, or we're supposed to
+ ** be authenticating something else. This is an error.
+ */
+ if((httpcode == 401) && !data->state.aptr.user)
+ return TRUE;
+#ifndef CURL_DISABLE_PROXY
+ if((httpcode == 407) && !data->conn->bits.proxy_user_passwd)
+ return TRUE;
+#endif
+
+ return data->state.authproblem;
+}
+
+/*
+ * readmoredata() is a "fread() emulation" to provide POST and/or request
+ * data. It is used when a huge POST is to be made and the entire chunk wasn't
+ * sent in the first send(). This function will then be called from the
+ * transfer.c loop when more data is to be sent to the peer.
+ *
+ * Returns the amount of bytes it filled the buffer with.
+ */
+static size_t readmoredata(char *buffer,
+ size_t size,
+ size_t nitems,
+ void *userp)
+{
+ struct Curl_easy *data = (struct Curl_easy *)userp;
+ struct HTTP *http = data->req.p.http;
+ size_t fullsize = size * nitems;
+
+ if(!http->postsize)
+ /* nothing to return */
+ return 0;
+
+ /* make sure that an HTTP request is never sent away chunked! */
+ data->req.forbidchunk = (http->sending == HTTPSEND_REQUEST)?TRUE:FALSE;
+
+ if(data->set.max_send_speed &&
+ (data->set.max_send_speed < (curl_off_t)fullsize) &&
+ (data->set.max_send_speed < http->postsize))
+ /* speed limit */
+ fullsize = (size_t)data->set.max_send_speed;
+
+ else if(http->postsize <= (curl_off_t)fullsize) {
+ memcpy(buffer, http->postdata, (size_t)http->postsize);
+ fullsize = (size_t)http->postsize;
+
+ if(http->backup.postsize) {
+ /* move backup data into focus and continue on that */
+ http->postdata = http->backup.postdata;
+ http->postsize = http->backup.postsize;
+ data->state.fread_func = http->backup.fread_func;
+ data->state.in = http->backup.fread_in;
+
+ http->sending++; /* move one step up */
+
+ http->backup.postsize = 0;
+ }
+ else
+ http->postsize = 0;
+
+ return fullsize;
+ }
+
+ memcpy(buffer, http->postdata, fullsize);
+ http->postdata += fullsize;
+ http->postsize -= fullsize;
+
+ return fullsize;
+}
+
+/*
+ * Curl_buffer_send() sends a header buffer and frees all associated
+ * memory. Body data may be appended to the header data if desired.
+ *
+ * Returns CURLcode
+ */
+CURLcode Curl_buffer_send(struct dynbuf *in,
+ struct Curl_easy *data,
+ /* add the number of sent bytes to this
+ counter */
+ curl_off_t *bytes_written,
+ /* how much of the buffer contains body data */
+ curl_off_t included_body_bytes,
+ int socketindex)
+{
+ ssize_t amount;
+ CURLcode result;
+ char *ptr;
+ size_t size;
+ struct connectdata *conn = data->conn;
+ struct HTTP *http = data->req.p.http;
+ size_t sendsize;
+ curl_socket_t sockfd;
+ size_t headersize;
+
+ DEBUGASSERT(socketindex <= SECONDARYSOCKET);
+
+ sockfd = conn->sock[socketindex];
+
+ /* The looping below is required since we use non-blocking sockets, but due
+ to the circumstances we will just loop and try again and again etc */
+
+ ptr = Curl_dyn_ptr(in);
+ size = Curl_dyn_len(in);
+
+ headersize = size - (size_t)included_body_bytes; /* the initial part that
+ isn't body is header */
+
+ DEBUGASSERT(size > (size_t)included_body_bytes);
+
+ if((conn->handler->flags & PROTOPT_SSL
+#ifndef CURL_DISABLE_PROXY
+ || conn->http_proxy.proxytype == CURLPROXY_HTTPS
+#endif
+ )
+ && conn->httpversion != 20) {
+ /* Make sure this doesn't send more body bytes than what the max send
+ speed says. The request bytes do not count to the max speed.
+ */
+ if(data->set.max_send_speed &&
+ (included_body_bytes > data->set.max_send_speed)) {
+ curl_off_t overflow = included_body_bytes - data->set.max_send_speed;
+ DEBUGASSERT((size_t)overflow < size);
+ sendsize = size - (size_t)overflow;
+ }
+ else
+ sendsize = size;
+
+ /* OpenSSL is very picky and we must send the SAME buffer pointer to the
+ library when we attempt to re-send this buffer. Sending the same data
+ is not enough, we must use the exact same address. For this reason, we
+ must copy the data to the uploadbuffer first, since that is the buffer
+ we will be using if this send is retried later.
+ */
+ result = Curl_get_upload_buffer(data);
+ if(result) {
+ /* malloc failed, free memory and return to the caller */
+ Curl_dyn_free(in);
+ return result;
+ }
+ /* We never send more than upload_buffer_size bytes in one single chunk
+ when we speak HTTPS, as if only a fraction of it is sent now, this data
+ needs to fit into the normal read-callback buffer later on and that
+ buffer is using this size.
+ */
+ if(sendsize > (size_t)data->set.upload_buffer_size)
+ sendsize = (size_t)data->set.upload_buffer_size;
+
+ memcpy(data->state.ulbuf, ptr, sendsize);
+ ptr = data->state.ulbuf;
+ }
+ else {
+#ifdef CURLDEBUG
+ /* Allow debug builds to override this logic to force short initial
+ sends
+ */
+ char *p = getenv("CURL_SMALLREQSEND");
+ if(p) {
+ size_t altsize = (size_t)strtoul(p, NULL, 10);
+ if(altsize)
+ sendsize = CURLMIN(size, altsize);
+ else
+ sendsize = size;
+ }
+ else
+#endif
+ {
+ /* Make sure this doesn't send more body bytes than what the max send
+ speed says. The request bytes do not count to the max speed.
+ */
+ if(data->set.max_send_speed &&
+ (included_body_bytes > data->set.max_send_speed)) {
+ curl_off_t overflow = included_body_bytes - data->set.max_send_speed;
+ DEBUGASSERT((size_t)overflow < size);
+ sendsize = size - (size_t)overflow;
+ }
+ else
+ sendsize = size;
+ }
+ }
+
+ result = Curl_write(data, sockfd, ptr, sendsize, &amount);
+
+ if(!result) {
+ /*
+ * Note that we may not send the entire chunk at once, and we have a set
+ * number of data bytes at the end of the big buffer (out of which we may
+ * only send away a part).
+ */
+ /* how much of the header that was sent */
+ size_t headlen = (size_t)amount>headersize ? headersize : (size_t)amount;
+ size_t bodylen = amount - headlen;
+
+ /* this data _may_ contain binary stuff */
+ Curl_debug(data, CURLINFO_HEADER_OUT, ptr, headlen);
+ if(bodylen)
+ /* there was body data sent beyond the initial header part, pass that on
+ to the debug callback too */
+ Curl_debug(data, CURLINFO_DATA_OUT, ptr + headlen, bodylen);
+
+ /* 'amount' can never be a very large value here so typecasting it so a
+ signed 31 bit value should not cause problems even if ssize_t is
+ 64bit */
+ *bytes_written += (long)amount;
+
+ if(http) {
+ /* if we sent a piece of the body here, up the byte counter for it
+ accordingly */
+ data->req.writebytecount += bodylen;
+ Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
+
+ if((size_t)amount != size) {
+ /* The whole request could not be sent in one system call. We must
+ queue it up and send it later when we get the chance. We must not
+ loop here and wait until it might work again. */
+
+ size -= amount;
+
+ ptr = Curl_dyn_ptr(in) + amount;
+
+ /* backup the currently set pointers */
+ http->backup.fread_func = data->state.fread_func;
+ http->backup.fread_in = data->state.in;
+ http->backup.postdata = http->postdata;
+ http->backup.postsize = http->postsize;
+
+ /* set the new pointers for the request-sending */
+ data->state.fread_func = (curl_read_callback)readmoredata;
+ data->state.in = (void *)data;
+ http->postdata = ptr;
+ http->postsize = (curl_off_t)size;
+
+ /* this much data is remaining header: */
+ data->req.pendingheader = headersize - headlen;
+
+ http->send_buffer = *in; /* copy the whole struct */
+ http->sending = HTTPSEND_REQUEST;
+
+ return CURLE_OK;
+ }
+ http->sending = HTTPSEND_BODY;
+ /* the full buffer was sent, clean up and return */
+ }
+ else {
+ if((size_t)amount != size)
+ /* We have no continue-send mechanism now, fail. This can only happen
+ when this function is used from the CONNECT sending function. We
+ currently (stupidly) assume that the whole request is always sent
+ away in the first single chunk.
+
+ This needs FIXing.
+ */
+ return CURLE_SEND_ERROR;
+ }
+ }
+ Curl_dyn_free(in);
+
+ /* no remaining header data */
+ data->req.pendingheader = 0;
+ return result;
+}
+
+/* end of the add_buffer functions */
+/* ------------------------------------------------------------------------- */
+
+
+
+/*
+ * Curl_compareheader()
+ *
+ * Returns TRUE if 'headerline' contains the 'header' with given 'content'.
+ * Pass headers WITH the colon.
+ */
+bool
+Curl_compareheader(const char *headerline, /* line to check */
+ const char *header, /* header keyword _with_ colon */
+ const size_t hlen, /* len of the keyword in bytes */
+ const char *content, /* content string to find */
+ const size_t clen) /* len of the content in bytes */
+{
+ /* RFC2616, section 4.2 says: "Each header field consists of a name followed
+ * by a colon (":") and the field value. Field names are case-insensitive.
+ * The field value MAY be preceded by any amount of LWS, though a single SP
+ * is preferred." */
+
+ size_t len;
+ const char *start;
+ const char *end;
+ DEBUGASSERT(hlen);
+ DEBUGASSERT(clen);
+ DEBUGASSERT(header);
+ DEBUGASSERT(content);
+
+ if(!strncasecompare(headerline, header, hlen))
+ return FALSE; /* doesn't start with header */
+
+ /* pass the header */
+ start = &headerline[hlen];
+
+ /* pass all whitespace */
+ while(*start && ISSPACE(*start))
+ start++;
+
+ /* find the end of the header line */
+ end = strchr(start, '\r'); /* lines end with CRLF */
+ if(!end) {
+ /* in case there's a non-standard compliant line here */
+ end = strchr(start, '\n');
+
+ if(!end)
+ /* hm, there's no line ending here, use the zero byte! */
+ end = strchr(start, '\0');
+ }
+
+ len = end-start; /* length of the content part of the input line */
+
+ /* find the content string in the rest of the line */
+ for(; len >= clen; len--, start++) {
+ if(strncasecompare(start, content, clen))
+ return TRUE; /* match! */
+ }
+
+ return FALSE; /* no match */
+}
+
+/*
+ * Curl_http_connect() performs HTTP stuff to do at connect-time, called from
+ * the generic Curl_connect().
+ */
+CURLcode Curl_http_connect(struct Curl_easy *data, bool *done)
+{
+ struct connectdata *conn = data->conn;
+
+ /* We default to persistent connections. We set this already in this connect
+ function to make the re-use checks properly be able to check this bit. */
+ connkeep(conn, "HTTP default");
+
+ return Curl_conn_connect(data, FIRSTSOCKET, FALSE, done);
+}
+
+/* this returns the socket to wait for in the DO and DOING state for the multi
+ interface and then we're always _sending_ a request and thus we wait for
+ the single socket to become writable only */
+static int http_getsock_do(struct Curl_easy *data,
+ struct connectdata *conn,
+ curl_socket_t *socks)
+{
+ /* write mode */
+ (void)data;
+ socks[0] = conn->sock[FIRSTSOCKET];
+ return GETSOCK_WRITESOCK(0);
+}
+
+/*
+ * Curl_http_done() gets called after a single HTTP request has been
+ * performed.
+ */
+
+CURLcode Curl_http_done(struct Curl_easy *data,
+ CURLcode status, bool premature)
+{
+ struct connectdata *conn = data->conn;
+ struct HTTP *http = data->req.p.http;
+
+ /* Clear multipass flag. If authentication isn't done yet, then it will get
+ * a chance to be set back to true when we output the next auth header */
+ data->state.authhost.multipass = FALSE;
+ data->state.authproxy.multipass = FALSE;
+
+ Curl_unencode_cleanup(data);
+
+ /* set the proper values (possibly modified on POST) */
+ conn->seek_func = data->set.seek_func; /* restore */
+ conn->seek_client = data->set.seek_client; /* restore */
+
+ if(!http)
+ return CURLE_OK;
+
+ Curl_dyn_free(&http->send_buffer);
+ Curl_http2_done(data, premature);
+ Curl_quic_done(data, premature);
+ Curl_mime_cleanpart(&http->form);
+ Curl_dyn_reset(&data->state.headerb);
+ Curl_hyper_done(data);
+ Curl_ws_done(data);
+
+ if(status)
+ return status;
+
+ if(!premature && /* this check is pointless when DONE is called before the
+ entire operation is complete */
+ !conn->bits.retry &&
+ !data->set.connect_only &&
+ (data->req.bytecount +
+ data->req.headerbytecount -
+ data->req.deductheadercount) <= 0) {
+ /* If this connection isn't simply closed to be retried, AND nothing was
+ read from the HTTP server (that counts), this can't be right so we
+ return an error here */
+ failf(data, "Empty reply from server");
+ /* Mark it as closed to avoid the "left intact" message */
+ streamclose(conn, "Empty reply from server");
+ return CURLE_GOT_NOTHING;
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Determine if we should use HTTP 1.1 (OR BETTER) for this request. Reasons
+ * to avoid it include:
+ *
+ * - if the user specifically requested HTTP 1.0
+ * - if the server we are connected to only supports 1.0
+ * - if any server previously contacted to handle this request only supports
+ * 1.0.
+ */
+bool Curl_use_http_1_1plus(const struct Curl_easy *data,
+ const struct connectdata *conn)
+{
+ if((data->state.httpversion == 10) || (conn->httpversion == 10))
+ return FALSE;
+ if((data->state.httpwant == CURL_HTTP_VERSION_1_0) &&
+ (conn->httpversion <= 10))
+ return FALSE;
+ return ((data->state.httpwant == CURL_HTTP_VERSION_NONE) ||
+ (data->state.httpwant >= CURL_HTTP_VERSION_1_1));
+}
+
+#ifndef USE_HYPER
+static const char *get_http_string(const struct Curl_easy *data,
+ const struct connectdata *conn)
+{
+#ifdef ENABLE_QUIC
+ if((data->state.httpwant == CURL_HTTP_VERSION_3) ||
+ (conn->httpversion == 30))
+ return "3";
+#endif
+
+#ifdef USE_NGHTTP2
+ if(conn->proto.httpc.h2)
+ return "2";
+#endif
+
+ if(Curl_use_http_1_1plus(data, conn))
+ return "1.1";
+
+ return "1.0";
+}
+#endif
+
+/* check and possibly add an Expect: header */
+static CURLcode expect100(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct dynbuf *req)
+{
+ CURLcode result = CURLE_OK;
+ data->state.expect100header = FALSE; /* default to false unless it is set
+ to TRUE below */
+ if(!data->state.disableexpect && Curl_use_http_1_1plus(data, conn) &&
+ (conn->httpversion < 20)) {
+ /* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an
+ Expect: 100-continue to the headers which actually speeds up post
+ operations (as there is one packet coming back from the web server) */
+ const char *ptr = Curl_checkheaders(data, STRCONST("Expect"));
+ if(ptr) {
+ data->state.expect100header =
+ Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue"));
+ }
+ else {
+ result = Curl_dyn_addn(req, STRCONST("Expect: 100-continue\r\n"));
+ if(!result)
+ data->state.expect100header = TRUE;
+ }
+ }
+
+ return result;
+}
+
+enum proxy_use {
+ HEADER_SERVER, /* direct to server */
+ HEADER_PROXY, /* regular request to proxy */
+ HEADER_CONNECT /* sending CONNECT to a proxy */
+};
+
+/* used to compile the provided trailers into one buffer
+ will return an error code if one of the headers is
+ not formatted correctly */
+CURLcode Curl_http_compile_trailers(struct curl_slist *trailers,
+ struct dynbuf *b,
+ struct Curl_easy *handle)
+{
+ char *ptr = NULL;
+ CURLcode result = CURLE_OK;
+ const char *endofline_native = NULL;
+ const char *endofline_network = NULL;
+
+ if(
+#ifdef CURL_DO_LINEEND_CONV
+ (handle->state.prefer_ascii) ||
+#endif
+ (handle->set.crlf)) {
+ /* \n will become \r\n later on */
+ endofline_native = "\n";
+ endofline_network = "\x0a";
+ }
+ else {
+ endofline_native = "\r\n";
+ endofline_network = "\x0d\x0a";
+ }
+
+ while(trailers) {
+ /* only add correctly formatted trailers */
+ ptr = strchr(trailers->data, ':');
+ if(ptr && *(ptr + 1) == ' ') {
+ result = Curl_dyn_add(b, trailers->data);
+ if(result)
+ return result;
+ result = Curl_dyn_add(b, endofline_native);
+ if(result)
+ return result;
+ }
+ else
+ infof(handle, "Malformatted trailing header, skipping trailer");
+ trailers = trailers->next;
+ }
+ result = Curl_dyn_add(b, endofline_network);
+ return result;
+}
+
+CURLcode Curl_add_custom_headers(struct Curl_easy *data,
+ bool is_connect,
+#ifndef USE_HYPER
+ struct dynbuf *req
+#else
+ void *req
+#endif
+ )
+{
+ struct connectdata *conn = data->conn;
+ char *ptr;
+ struct curl_slist *h[2];
+ struct curl_slist *headers;
+ int numlists = 1; /* by default */
+ int i;
+
+#ifndef CURL_DISABLE_PROXY
+ enum proxy_use proxy;
+
+ if(is_connect)
+ proxy = HEADER_CONNECT;
+ else
+ proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy?
+ HEADER_PROXY:HEADER_SERVER;
+
+ switch(proxy) {
+ case HEADER_SERVER:
+ h[0] = data->set.headers;
+ break;
+ case HEADER_PROXY:
+ h[0] = data->set.headers;
+ if(data->set.sep_headers) {
+ h[1] = data->set.proxyheaders;
+ numlists++;
+ }
+ break;
+ case HEADER_CONNECT:
+ if(data->set.sep_headers)
+ h[0] = data->set.proxyheaders;
+ else
+ h[0] = data->set.headers;
+ break;
+ }
+#else
+ (void)is_connect;
+ h[0] = data->set.headers;
+#endif
+
+ /* loop through one or two lists */
+ for(i = 0; i < numlists; i++) {
+ headers = h[i];
+
+ while(headers) {
+ char *semicolonp = NULL;
+ ptr = strchr(headers->data, ':');
+ if(!ptr) {
+ char *optr;
+ /* no colon, semicolon? */
+ ptr = strchr(headers->data, ';');
+ if(ptr) {
+ optr = ptr;
+ ptr++; /* pass the semicolon */
+ while(*ptr && ISSPACE(*ptr))
+ ptr++;
+
+ if(*ptr) {
+ /* this may be used for something else in the future */
+ optr = NULL;
+ }
+ else {
+ if(*(--ptr) == ';') {
+ /* copy the source */
+ semicolonp = strdup(headers->data);
+ if(!semicolonp) {
+#ifndef USE_HYPER
+ Curl_dyn_free(req);
+#endif
+ return CURLE_OUT_OF_MEMORY;
+ }
+ /* put a colon where the semicolon is */
+ semicolonp[ptr - headers->data] = ':';
+ /* point at the colon */
+ optr = &semicolonp [ptr - headers->data];
+ }
+ }
+ ptr = optr;
+ }
+ }
+ if(ptr && (ptr != headers->data)) {
+ /* we require a colon for this to be a true header */
+
+ ptr++; /* pass the colon */
+ while(*ptr && ISSPACE(*ptr))
+ ptr++;
+
+ if(*ptr || semicolonp) {
+ /* only send this if the contents was non-blank or done special */
+ CURLcode result = CURLE_OK;
+ char *compare = semicolonp ? semicolonp : headers->data;
+
+ if(data->state.aptr.host &&
+ /* a Host: header was sent already, don't pass on any custom Host:
+ header as that will produce *two* in the same request! */
+ checkprefix("Host:", compare))
+ ;
+ else if(data->state.httpreq == HTTPREQ_POST_FORM &&
+ /* this header (extended by formdata.c) is sent later */
+ checkprefix("Content-Type:", compare))
+ ;
+ else if(data->state.httpreq == HTTPREQ_POST_MIME &&
+ /* this header is sent later */
+ checkprefix("Content-Type:", compare))
+ ;
+ else if(conn->bits.authneg &&
+ /* while doing auth neg, don't allow the custom length since
+ we will force length zero then */
+ checkprefix("Content-Length:", compare))
+ ;
+ else if(data->state.aptr.te &&
+ /* when asking for Transfer-Encoding, don't pass on a custom
+ Connection: */
+ checkprefix("Connection:", compare))
+ ;
+ else if((conn->httpversion >= 20) &&
+ checkprefix("Transfer-Encoding:", compare))
+ /* HTTP/2 doesn't support chunked requests */
+ ;
+ else if((checkprefix("Authorization:", compare) ||
+ checkprefix("Cookie:", compare)) &&
+ /* be careful of sending this potentially sensitive header to
+ other hosts */
+ !Curl_auth_allowed_to_host(data))
+ ;
+ else {
+#ifdef USE_HYPER
+ result = Curl_hyper_header(data, req, compare);
+#else
+ result = Curl_dyn_addf(req, "%s\r\n", compare);
+#endif
+ }
+ if(semicolonp)
+ free(semicolonp);
+ if(result)
+ return result;
+ }
+ }
+ headers = headers->next;
+ }
+ }
+
+ return CURLE_OK;
+}
+
+#ifndef CURL_DISABLE_PARSEDATE
+CURLcode Curl_add_timecondition(struct Curl_easy *data,
+#ifndef USE_HYPER
+ struct dynbuf *req
+#else
+ void *req
+#endif
+ )
+{
+ const struct tm *tm;
+ struct tm keeptime;
+ CURLcode result;
+ char datestr[80];
+ const char *condp;
+ size_t len;
+
+ if(data->set.timecondition == CURL_TIMECOND_NONE)
+ /* no condition was asked for */
+ return CURLE_OK;
+
+ result = Curl_gmtime(data->set.timevalue, &keeptime);
+ if(result) {
+ failf(data, "Invalid TIMEVALUE");
+ return result;
+ }
+ tm = &keeptime;
+
+ switch(data->set.timecondition) {
+ default:
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ case CURL_TIMECOND_IFMODSINCE:
+ condp = "If-Modified-Since";
+ len = 17;
+ break;
+ case CURL_TIMECOND_IFUNMODSINCE:
+ condp = "If-Unmodified-Since";
+ len = 19;
+ break;
+ case CURL_TIMECOND_LASTMOD:
+ condp = "Last-Modified";
+ len = 13;
+ break;
+ }
+
+ if(Curl_checkheaders(data, condp, len)) {
+ /* A custom header was specified; it will be sent instead. */
+ return CURLE_OK;
+ }
+
+ /* The If-Modified-Since header family should have their times set in
+ * GMT as RFC2616 defines: "All HTTP date/time stamps MUST be
+ * represented in Greenwich Mean Time (GMT), without exception. For the
+ * purposes of HTTP, GMT is exactly equal to UTC (Coordinated Universal
+ * Time)." (see page 20 of RFC2616).
+ */
+
+ /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
+ msnprintf(datestr, sizeof(datestr),
+ "%s: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
+ condp,
+ Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
+ tm->tm_mday,
+ Curl_month[tm->tm_mon],
+ tm->tm_year + 1900,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec);
+
+#ifndef USE_HYPER
+ result = Curl_dyn_add(req, datestr);
+#else
+ result = Curl_hyper_header(data, req, datestr);
+#endif
+
+ return result;
+}
+#else
+/* disabled */
+CURLcode Curl_add_timecondition(struct Curl_easy *data,
+ struct dynbuf *req)
+{
+ (void)data;
+ (void)req;
+ return CURLE_OK;
+}
+#endif
+
+void Curl_http_method(struct Curl_easy *data, struct connectdata *conn,
+ const char **method, Curl_HttpReq *reqp)
+{
+ Curl_HttpReq httpreq = (Curl_HttpReq)data->state.httpreq;
+ const char *request;
+ if((conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) &&
+ data->set.upload)
+ httpreq = HTTPREQ_PUT;
+
+ /* Now set the 'request' pointer to the proper request string */
+ if(data->set.str[STRING_CUSTOMREQUEST])
+ request = data->set.str[STRING_CUSTOMREQUEST];
+ else {
+ if(data->req.no_body)
+ request = "HEAD";
+ else {
+ DEBUGASSERT((httpreq >= HTTPREQ_GET) && (httpreq <= HTTPREQ_HEAD));
+ switch(httpreq) {
+ case HTTPREQ_POST:
+ case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
+ request = "POST";
+ break;
+ case HTTPREQ_PUT:
+ request = "PUT";
+ break;
+ default: /* this should never happen */
+ case HTTPREQ_GET:
+ request = "GET";
+ break;
+ case HTTPREQ_HEAD:
+ request = "HEAD";
+ break;
+ }
+ }
+ }
+ *method = request;
+ *reqp = httpreq;
+}
+
+CURLcode Curl_http_useragent(struct Curl_easy *data)
+{
+ /* The User-Agent string might have been allocated in url.c already, because
+ it might have been used in the proxy connect, but if we have got a header
+ with the user-agent string specified, we erase the previously made string
+ here. */
+ if(Curl_checkheaders(data, STRCONST("User-Agent"))) {
+ free(data->state.aptr.uagent);
+ data->state.aptr.uagent = NULL;
+ }
+ return CURLE_OK;
+}
+
+
+CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn)
+{
+ const char *ptr;
+ if(!data->state.this_is_a_follow) {
+ /* Free to avoid leaking memory on multiple requests */
+ free(data->state.first_host);
+
+ data->state.first_host = strdup(conn->host.name);
+ if(!data->state.first_host)
+ return CURLE_OUT_OF_MEMORY;
+
+ data->state.first_remote_port = conn->remote_port;
+ data->state.first_remote_protocol = conn->handler->protocol;
+ }
+ Curl_safefree(data->state.aptr.host);
+
+ ptr = Curl_checkheaders(data, STRCONST("Host"));
+ if(ptr && (!data->state.this_is_a_follow ||
+ strcasecompare(data->state.first_host, conn->host.name))) {
+#if !defined(CURL_DISABLE_COOKIES)
+ /* If we have a given custom Host: header, we extract the host name in
+ order to possibly use it for cookie reasons later on. We only allow the
+ custom Host: header if this is NOT a redirect, as setting Host: in the
+ redirected request is being out on thin ice. Except if the host name
+ is the same as the first one! */
+ char *cookiehost = Curl_copy_header_value(ptr);
+ if(!cookiehost)
+ return CURLE_OUT_OF_MEMORY;
+ if(!*cookiehost)
+ /* ignore empty data */
+ free(cookiehost);
+ else {
+ /* If the host begins with '[', we start searching for the port after
+ the bracket has been closed */
+ if(*cookiehost == '[') {
+ char *closingbracket;
+ /* since the 'cookiehost' is an allocated memory area that will be
+ freed later we cannot simply increment the pointer */
+ memmove(cookiehost, cookiehost + 1, strlen(cookiehost) - 1);
+ closingbracket = strchr(cookiehost, ']');
+ if(closingbracket)
+ *closingbracket = 0;
+ }
+ else {
+ int startsearch = 0;
+ char *colon = strchr(cookiehost + startsearch, ':');
+ if(colon)
+ *colon = 0; /* The host must not include an embedded port number */
+ }
+ Curl_safefree(data->state.aptr.cookiehost);
+ data->state.aptr.cookiehost = cookiehost;
+ }
+#endif
+
+ if(strcmp("Host:", ptr)) {
+ data->state.aptr.host = aprintf("Host:%s\r\n", &ptr[5]);
+ if(!data->state.aptr.host)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else
+ /* when clearing the header */
+ data->state.aptr.host = NULL;
+ }
+ else {
+ /* When building Host: headers, we must put the host name within
+ [brackets] if the host name is a plain IPv6-address. RFC2732-style. */
+ const char *host = conn->host.name;
+
+ if(((conn->given->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS)) &&
+ (conn->remote_port == PORT_HTTPS)) ||
+ ((conn->given->protocol&(CURLPROTO_HTTP|CURLPROTO_WS)) &&
+ (conn->remote_port == PORT_HTTP)) )
+ /* if(HTTPS on port 443) OR (HTTP on port 80) then don't include
+ the port number in the host string */
+ data->state.aptr.host = aprintf("Host: %s%s%s\r\n",
+ conn->bits.ipv6_ip?"[":"",
+ host,
+ conn->bits.ipv6_ip?"]":"");
+ else
+ data->state.aptr.host = aprintf("Host: %s%s%s:%d\r\n",
+ conn->bits.ipv6_ip?"[":"",
+ host,
+ conn->bits.ipv6_ip?"]":"",
+ conn->remote_port);
+
+ if(!data->state.aptr.host)
+ /* without Host: we can't make a nice request */
+ return CURLE_OUT_OF_MEMORY;
+ }
+ return CURLE_OK;
+}
+
+/*
+ * Append the request-target to the HTTP request
+ */
+CURLcode Curl_http_target(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct dynbuf *r)
+{
+ CURLcode result = CURLE_OK;
+ const char *path = data->state.up.path;
+ const char *query = data->state.up.query;
+
+ if(data->set.str[STRING_TARGET]) {
+ path = data->set.str[STRING_TARGET];
+ query = NULL;
+ }
+
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
+ /* Using a proxy but does not tunnel through it */
+
+ /* The path sent to the proxy is in fact the entire URL. But if the remote
+ host is a IDN-name, we must make sure that the request we produce only
+ uses the encoded host name! */
+
+ /* and no fragment part */
+ CURLUcode uc;
+ char *url;
+ CURLU *h = curl_url_dup(data->state.uh);
+ if(!h)
+ return CURLE_OUT_OF_MEMORY;
+
+ if(conn->host.dispname != conn->host.name) {
+ uc = curl_url_set(h, CURLUPART_HOST, conn->host.name, 0);
+ if(uc) {
+ curl_url_cleanup(h);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ uc = curl_url_set(h, CURLUPART_FRAGMENT, NULL, 0);
+ if(uc) {
+ curl_url_cleanup(h);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(strcasecompare("http", data->state.up.scheme)) {
+ /* when getting HTTP, we don't want the userinfo the URL */
+ uc = curl_url_set(h, CURLUPART_USER, NULL, 0);
+ if(uc) {
+ curl_url_cleanup(h);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ uc = curl_url_set(h, CURLUPART_PASSWORD, NULL, 0);
+ if(uc) {
+ curl_url_cleanup(h);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ /* Extract the URL to use in the request. Store in STRING_TEMP_URL for
+ clean-up reasons if the function returns before the free() further
+ down. */
+ uc = curl_url_get(h, CURLUPART_URL, &url, CURLU_NO_DEFAULT_PORT);
+ if(uc) {
+ curl_url_cleanup(h);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ curl_url_cleanup(h);
+
+ /* target or url */
+ result = Curl_dyn_add(r, data->set.str[STRING_TARGET]?
+ data->set.str[STRING_TARGET]:url);
+ free(url);
+ if(result)
+ return (result);
+
+ if(strcasecompare("ftp", data->state.up.scheme)) {
+ if(data->set.proxy_transfer_mode) {
+ /* when doing ftp, append ;type=<a|i> if not present */
+ char *type = strstr(path, ";type=");
+ if(type && type[6] && type[7] == 0) {
+ switch(Curl_raw_toupper(type[6])) {
+ case 'A':
+ case 'D':
+ case 'I':
+ break;
+ default:
+ type = NULL;
+ }
+ }
+ if(!type) {
+ result = Curl_dyn_addf(r, ";type=%c",
+ data->state.prefer_ascii ? 'a' : 'i');
+ if(result)
+ return result;
+ }
+ }
+ }
+ }
+
+ else
+#else
+ (void)conn; /* not used in disabled-proxy builds */
+#endif
+ {
+ result = Curl_dyn_add(r, path);
+ if(result)
+ return result;
+ if(query)
+ result = Curl_dyn_addf(r, "?%s", query);
+ }
+
+ return result;
+}
+
+CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn,
+ Curl_HttpReq httpreq, const char **tep)
+{
+ CURLcode result = CURLE_OK;
+ const char *ptr;
+ struct HTTP *http = data->req.p.http;
+ http->postsize = 0;
+
+ switch(httpreq) {
+ case HTTPREQ_POST_MIME:
+ http->sendit = &data->set.mimepost;
+ break;
+ case HTTPREQ_POST_FORM:
+ /* Convert the form structure into a mime structure. */
+ Curl_mime_cleanpart(&http->form);
+ result = Curl_getformdata(data, &http->form, data->set.httppost,
+ data->state.fread_func);
+ if(result)
+ return result;
+ http->sendit = &http->form;
+ break;
+ default:
+ http->sendit = NULL;
+ }
+
+#ifndef CURL_DISABLE_MIME
+ if(http->sendit) {
+ const char *cthdr = Curl_checkheaders(data, STRCONST("Content-Type"));
+
+ /* Read and seek body only. */
+ http->sendit->flags |= MIME_BODY_ONLY;
+
+ /* Prepare the mime structure headers & set content type. */
+
+ if(cthdr)
+ for(cthdr += 13; *cthdr == ' '; cthdr++)
+ ;
+ else if(http->sendit->kind == MIMEKIND_MULTIPART)
+ cthdr = "multipart/form-data";
+
+ curl_mime_headers(http->sendit, data->set.headers, 0);
+ result = Curl_mime_prepare_headers(data, http->sendit, cthdr,
+ NULL, MIMESTRATEGY_FORM);
+ curl_mime_headers(http->sendit, NULL, 0);
+ if(!result)
+ result = Curl_mime_rewind(http->sendit);
+ if(result)
+ return result;
+ http->postsize = Curl_mime_size(http->sendit);
+ }
+#endif
+
+ ptr = Curl_checkheaders(data, STRCONST("Transfer-Encoding"));
+ if(ptr) {
+ /* Some kind of TE is requested, check if 'chunked' is chosen */
+ data->req.upload_chunky =
+ Curl_compareheader(ptr,
+ STRCONST("Transfer-Encoding:"), STRCONST("chunked"));
+ }
+ else {
+ if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
+ (((httpreq == HTTPREQ_POST_MIME || httpreq == HTTPREQ_POST_FORM) &&
+ http->postsize < 0) ||
+ ((data->set.upload || httpreq == HTTPREQ_POST) &&
+ data->state.infilesize == -1))) {
+ if(conn->bits.authneg)
+ /* don't enable chunked during auth neg */
+ ;
+ else if(Curl_use_http_1_1plus(data, conn)) {
+ if(conn->httpversion < 20)
+ /* HTTP, upload, unknown file size and not HTTP 1.0 */
+ data->req.upload_chunky = TRUE;
+ }
+ else {
+ failf(data, "Chunky upload is not supported by HTTP 1.0");
+ return CURLE_UPLOAD_FAILED;
+ }
+ }
+ else {
+ /* else, no chunky upload */
+ data->req.upload_chunky = FALSE;
+ }
+
+ if(data->req.upload_chunky)
+ *tep = "Transfer-Encoding: chunked\r\n";
+ }
+ return result;
+}
+
+CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
+ struct dynbuf *r, Curl_HttpReq httpreq)
+{
+#ifndef USE_HYPER
+ /* Hyper always handles the body separately */
+ curl_off_t included_body = 0;
+#else
+ /* from this point down, this function should not be used */
+#define Curl_buffer_send(a,b,c,d,e) CURLE_OK
+#endif
+ CURLcode result = CURLE_OK;
+ struct HTTP *http = data->req.p.http;
+ const char *ptr;
+
+ /* If 'authdone' is FALSE, we must not set the write socket index to the
+ Curl_transfer() call below, as we're not ready to actually upload any
+ data yet. */
+
+ switch(httpreq) {
+
+ case HTTPREQ_PUT: /* Let's PUT the data to the server! */
+
+ if(conn->bits.authneg)
+ http->postsize = 0;
+ else
+ http->postsize = data->state.infilesize;
+
+ if((http->postsize != -1) && !data->req.upload_chunky &&
+ (conn->bits.authneg ||
+ !Curl_checkheaders(data, STRCONST("Content-Length")))) {
+ /* only add Content-Length if not uploading chunked */
+ result = Curl_dyn_addf(r, "Content-Length: %" CURL_FORMAT_CURL_OFF_T
+ "\r\n", http->postsize);
+ if(result)
+ return result;
+ }
+
+ if(http->postsize) {
+ result = expect100(data, conn, r);
+ if(result)
+ return result;
+ }
+
+ /* end of headers */
+ result = Curl_dyn_addn(r, STRCONST("\r\n"));
+ if(result)
+ return result;
+
+ /* set the upload size to the progress meter */
+ Curl_pgrsSetUploadSize(data, http->postsize);
+
+ /* this sends the buffer and frees all the buffer resources */
+ result = Curl_buffer_send(r, data, &data->info.request_size, 0,
+ FIRSTSOCKET);
+ if(result)
+ failf(data, "Failed sending PUT request");
+ else
+ /* prepare for transfer */
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE,
+ http->postsize?FIRSTSOCKET:-1);
+ if(result)
+ return result;
+ break;
+
+ case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
+ /* This is form posting using mime data. */
+ if(conn->bits.authneg) {
+ /* nothing to post! */
+ result = Curl_dyn_addn(r, STRCONST("Content-Length: 0\r\n\r\n"));
+ if(result)
+ return result;
+
+ result = Curl_buffer_send(r, data, &data->info.request_size, 0,
+ FIRSTSOCKET);
+ if(result)
+ failf(data, "Failed sending POST request");
+ else
+ /* setup variables for the upcoming transfer */
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1);
+ break;
+ }
+
+ data->state.infilesize = http->postsize;
+
+ /* We only set Content-Length and allow a custom Content-Length if
+ we don't upload data chunked, as RFC2616 forbids us to set both
+ kinds of headers (Transfer-Encoding: chunked and Content-Length) */
+ if(http->postsize != -1 && !data->req.upload_chunky &&
+ (conn->bits.authneg ||
+ !Curl_checkheaders(data, STRCONST("Content-Length")))) {
+ /* we allow replacing this header if not during auth negotiation,
+ although it isn't very wise to actually set your own */
+ result = Curl_dyn_addf(r,
+ "Content-Length: %" CURL_FORMAT_CURL_OFF_T
+ "\r\n", http->postsize);
+ if(result)
+ return result;
+ }
+
+#ifndef CURL_DISABLE_MIME
+ /* Output mime-generated headers. */
+ {
+ struct curl_slist *hdr;
+
+ for(hdr = http->sendit->curlheaders; hdr; hdr = hdr->next) {
+ result = Curl_dyn_addf(r, "%s\r\n", hdr->data);
+ if(result)
+ return result;
+ }
+ }
+#endif
+
+ /* For really small posts we don't use Expect: headers at all, and for
+ the somewhat bigger ones we allow the app to disable it. Just make
+ sure that the expect100header is always set to the preferred value
+ here. */
+ ptr = Curl_checkheaders(data, STRCONST("Expect"));
+ if(ptr) {
+ data->state.expect100header =
+ Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue"));
+ }
+ else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) {
+ result = expect100(data, conn, r);
+ if(result)
+ return result;
+ }
+ else
+ data->state.expect100header = FALSE;
+
+ /* make the request end in a true CRLF */
+ result = Curl_dyn_addn(r, STRCONST("\r\n"));
+ if(result)
+ return result;
+
+ /* set the upload size to the progress meter */
+ Curl_pgrsSetUploadSize(data, http->postsize);
+
+ /* Read from mime structure. */
+ data->state.fread_func = (curl_read_callback) Curl_mime_read;
+ data->state.in = (void *) http->sendit;
+ http->sending = HTTPSEND_BODY;
+
+ /* this sends the buffer and frees all the buffer resources */
+ result = Curl_buffer_send(r, data, &data->info.request_size, 0,
+ FIRSTSOCKET);
+ if(result)
+ failf(data, "Failed sending POST request");
+ else
+ /* prepare for transfer */
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE,
+ http->postsize?FIRSTSOCKET:-1);
+ if(result)
+ return result;
+
+ break;
+
+ case HTTPREQ_POST:
+ /* this is the simple POST, using x-www-form-urlencoded style */
+
+ if(conn->bits.authneg)
+ http->postsize = 0;
+ else
+ /* the size of the post body */
+ http->postsize = data->state.infilesize;
+
+ /* We only set Content-Length and allow a custom Content-Length if
+ we don't upload data chunked, as RFC2616 forbids us to set both
+ kinds of headers (Transfer-Encoding: chunked and Content-Length) */
+ if((http->postsize != -1) && !data->req.upload_chunky &&
+ (conn->bits.authneg ||
+ !Curl_checkheaders(data, STRCONST("Content-Length")))) {
+ /* we allow replacing this header if not during auth negotiation,
+ although it isn't very wise to actually set your own */
+ result = Curl_dyn_addf(r, "Content-Length: %" CURL_FORMAT_CURL_OFF_T
+ "\r\n", http->postsize);
+ if(result)
+ return result;
+ }
+
+ if(!Curl_checkheaders(data, STRCONST("Content-Type"))) {
+ result = Curl_dyn_addn(r, STRCONST("Content-Type: application/"
+ "x-www-form-urlencoded\r\n"));
+ if(result)
+ return result;
+ }
+
+ /* For really small posts we don't use Expect: headers at all, and for
+ the somewhat bigger ones we allow the app to disable it. Just make
+ sure that the expect100header is always set to the preferred value
+ here. */
+ ptr = Curl_checkheaders(data, STRCONST("Expect"));
+ if(ptr) {
+ data->state.expect100header =
+ Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue"));
+ }
+ else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) {
+ result = expect100(data, conn, r);
+ if(result)
+ return result;
+ }
+ else
+ data->state.expect100header = FALSE;
+
+#ifndef USE_HYPER
+ /* With Hyper the body is always passed on separately */
+ if(data->set.postfields) {
+
+ /* In HTTP2, we send request body in DATA frame regardless of
+ its size. */
+ if(conn->httpversion != 20 &&
+ !data->state.expect100header &&
+ (http->postsize < MAX_INITIAL_POST_SIZE)) {
+ /* if we don't use expect: 100 AND
+ postsize is less than MAX_INITIAL_POST_SIZE
+
+ then append the post data to the HTTP request header. This limit
+ is no magic limit but only set to prevent really huge POSTs to
+ get the data duplicated with malloc() and family. */
+
+ /* end of headers! */
+ result = Curl_dyn_addn(r, STRCONST("\r\n"));
+ if(result)
+ return result;
+
+ if(!data->req.upload_chunky) {
+ /* We're not sending it 'chunked', append it to the request
+ already now to reduce the number if send() calls */
+ result = Curl_dyn_addn(r, data->set.postfields,
+ (size_t)http->postsize);
+ included_body = http->postsize;
+ }
+ else {
+ if(http->postsize) {
+ char chunk[16];
+ /* Append the POST data chunky-style */
+ msnprintf(chunk, sizeof(chunk), "%x\r\n", (int)http->postsize);
+ result = Curl_dyn_add(r, chunk);
+ if(!result) {
+ included_body = http->postsize + strlen(chunk);
+ result = Curl_dyn_addn(r, data->set.postfields,
+ (size_t)http->postsize);
+ if(!result)
+ result = Curl_dyn_addn(r, STRCONST("\r\n"));
+ included_body += 2;
+ }
+ }
+ if(!result) {
+ result = Curl_dyn_addn(r, STRCONST("\x30\x0d\x0a\x0d\x0a"));
+ /* 0 CR LF CR LF */
+ included_body += 5;
+ }
+ }
+ if(result)
+ return result;
+ /* Make sure the progress information is accurate */
+ Curl_pgrsSetUploadSize(data, http->postsize);
+ }
+ else {
+ /* A huge POST coming up, do data separate from the request */
+ http->postdata = data->set.postfields;
+
+ http->sending = HTTPSEND_BODY;
+
+ data->state.fread_func = (curl_read_callback)readmoredata;
+ data->state.in = (void *)data;
+
+ /* set the upload size to the progress meter */
+ Curl_pgrsSetUploadSize(data, http->postsize);
+
+ /* end of headers! */
+ result = Curl_dyn_addn(r, STRCONST("\r\n"));
+ if(result)
+ return result;
+ }
+ }
+ else
+#endif
+ {
+ /* end of headers! */
+ result = Curl_dyn_addn(r, STRCONST("\r\n"));
+ if(result)
+ return result;
+
+ if(data->req.upload_chunky && conn->bits.authneg) {
+ /* Chunky upload is selected and we're negotiating auth still, send
+ end-of-data only */
+ result = Curl_dyn_addn(r, (char *)STRCONST("\x30\x0d\x0a\x0d\x0a"));
+ /* 0 CR LF CR LF */
+ if(result)
+ return result;
+ }
+
+ else if(data->state.infilesize) {
+ /* set the upload size to the progress meter */
+ Curl_pgrsSetUploadSize(data, http->postsize?http->postsize:-1);
+
+ /* set the pointer to mark that we will send the post body using the
+ read callback, but only if we're not in authenticate negotiation */
+ if(!conn->bits.authneg)
+ http->postdata = (char *)&http->postdata;
+ }
+ }
+ /* issue the request */
+ result = Curl_buffer_send(r, data, &data->info.request_size, included_body,
+ FIRSTSOCKET);
+
+ if(result)
+ failf(data, "Failed sending HTTP POST request");
+ else
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE,
+ http->postdata?FIRSTSOCKET:-1);
+ break;
+
+ default:
+ result = Curl_dyn_addn(r, STRCONST("\r\n"));
+ if(result)
+ return result;
+
+ /* issue the request */
+ result = Curl_buffer_send(r, data, &data->info.request_size, 0,
+ FIRSTSOCKET);
+ if(result)
+ failf(data, "Failed sending HTTP request");
+#ifdef USE_WEBSOCKETS
+ else if((conn->handler->protocol & (CURLPROTO_WS|CURLPROTO_WSS)) &&
+ !(data->set.connect_only))
+ /* Set up the transfer for two-way since without CONNECT_ONLY set, this
+ request probably wants to send data too post upgrade */
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET);
+#endif
+ else
+ /* HTTP GET/HEAD download: */
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1);
+ }
+
+ return result;
+}
+
+#if !defined(CURL_DISABLE_COOKIES)
+
+CURLcode Curl_http_cookies(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct dynbuf *r)
+{
+ CURLcode result = CURLE_OK;
+ char *addcookies = NULL;
+ bool linecap = FALSE;
+ if(data->set.str[STRING_COOKIE] &&
+ !Curl_checkheaders(data, STRCONST("Cookie")))
+ addcookies = data->set.str[STRING_COOKIE];
+
+ if(data->cookies || addcookies) {
+ struct Cookie *co = NULL; /* no cookies from start */
+ int count = 0;
+
+ if(data->cookies && data->state.cookie_engine) {
+ const char *host = data->state.aptr.cookiehost ?
+ data->state.aptr.cookiehost : conn->host.name;
+ const bool secure_context =
+ conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) ||
+ strcasecompare("localhost", host) ||
+ !strcmp(host, "127.0.0.1") ||
+ !strcmp(host, "::1") ? TRUE : FALSE;
+ Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
+ co = Curl_cookie_getlist(data, data->cookies, host, data->state.up.path,
+ secure_context);
+ Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
+ }
+ if(co) {
+ struct Cookie *store = co;
+ /* now loop through all cookies that matched */
+ while(co) {
+ if(co->value) {
+ if(0 == count) {
+ result = Curl_dyn_addn(r, STRCONST("Cookie: "));
+ if(result)
+ break;
+ }
+ if((Curl_dyn_len(r) + strlen(co->name) + strlen(co->value) + 1) >=
+ MAX_COOKIE_HEADER_LEN) {
+ infof(data, "Restricted outgoing cookies due to header size, "
+ "'%s' not sent", co->name);
+ linecap = TRUE;
+ break;
+ }
+ result = Curl_dyn_addf(r, "%s%s=%s", count?"; ":"",
+ co->name, co->value);
+ if(result)
+ break;
+ count++;
+ }
+ co = co->next; /* next cookie please */
+ }
+ Curl_cookie_freelist(store);
+ }
+ if(addcookies && !result && !linecap) {
+ if(!count)
+ result = Curl_dyn_addn(r, STRCONST("Cookie: "));
+ if(!result) {
+ result = Curl_dyn_addf(r, "%s%s", count?"; ":"", addcookies);
+ count++;
+ }
+ }
+ if(count && !result)
+ result = Curl_dyn_addn(r, STRCONST("\r\n"));
+
+ if(result)
+ return result;
+ }
+ return result;
+}
+#endif
+
+CURLcode Curl_http_range(struct Curl_easy *data,
+ Curl_HttpReq httpreq)
+{
+ if(data->state.use_range) {
+ /*
+ * A range is selected. We use different headers whether we're downloading
+ * or uploading and we always let customized headers override our internal
+ * ones if any such are specified.
+ */
+ if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) &&
+ !Curl_checkheaders(data, STRCONST("Range"))) {
+ /* if a line like this was already allocated, free the previous one */
+ free(data->state.aptr.rangeline);
+ data->state.aptr.rangeline = aprintf("Range: bytes=%s\r\n",
+ data->state.range);
+ }
+ else if((httpreq == HTTPREQ_POST || httpreq == HTTPREQ_PUT) &&
+ !Curl_checkheaders(data, STRCONST("Content-Range"))) {
+
+ /* if a line like this was already allocated, free the previous one */
+ free(data->state.aptr.rangeline);
+
+ if(data->set.set_resume_from < 0) {
+ /* Upload resume was asked for, but we don't know the size of the
+ remote part so we tell the server (and act accordingly) that we
+ upload the whole file (again) */
+ data->state.aptr.rangeline =
+ aprintf("Content-Range: bytes 0-%" CURL_FORMAT_CURL_OFF_T
+ "/%" CURL_FORMAT_CURL_OFF_T "\r\n",
+ data->state.infilesize - 1, data->state.infilesize);
+
+ }
+ else if(data->state.resume_from) {
+ /* This is because "resume" was selected */
+ curl_off_t total_expected_size =
+ data->state.resume_from + data->state.infilesize;
+ data->state.aptr.rangeline =
+ aprintf("Content-Range: bytes %s%" CURL_FORMAT_CURL_OFF_T
+ "/%" CURL_FORMAT_CURL_OFF_T "\r\n",
+ data->state.range, total_expected_size-1,
+ total_expected_size);
+ }
+ else {
+ /* Range was selected and then we just pass the incoming range and
+ append total size */
+ data->state.aptr.rangeline =
+ aprintf("Content-Range: bytes %s/%" CURL_FORMAT_CURL_OFF_T "\r\n",
+ data->state.range, data->state.infilesize);
+ }
+ if(!data->state.aptr.rangeline)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ return CURLE_OK;
+}
+
+CURLcode Curl_http_resume(struct Curl_easy *data,
+ struct connectdata *conn,
+ Curl_HttpReq httpreq)
+{
+ if((HTTPREQ_POST == httpreq || HTTPREQ_PUT == httpreq) &&
+ data->state.resume_from) {
+ /**********************************************************************
+ * Resuming upload in HTTP means that we PUT or POST and that we have
+ * got a resume_from value set. The resume value has already created
+ * a Range: header that will be passed along. We need to "fast forward"
+ * the file the given number of bytes and decrease the assume upload
+ * file size before we continue this venture in the dark lands of HTTP.
+ * Resuming mime/form posting at an offset > 0 has no sense and is ignored.
+ *********************************************************************/
+
+ if(data->state.resume_from < 0) {
+ /*
+ * This is meant to get the size of the present remote-file by itself.
+ * We don't support this now. Bail out!
+ */
+ data->state.resume_from = 0;
+ }
+
+ if(data->state.resume_from && !data->state.followlocation) {
+ /* only act on the first request */
+
+ /* Now, let's read off the proper amount of bytes from the
+ input. */
+ int seekerr = CURL_SEEKFUNC_CANTSEEK;
+ if(conn->seek_func) {
+ Curl_set_in_callback(data, true);
+ seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
+ SEEK_SET);
+ Curl_set_in_callback(data, false);
+ }
+
+ if(seekerr != CURL_SEEKFUNC_OK) {
+ curl_off_t passed = 0;
+
+ if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
+ failf(data, "Could not seek stream");
+ return CURLE_READ_ERROR;
+ }
+ /* when seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
+ do {
+ size_t readthisamountnow =
+ (data->state.resume_from - passed > data->set.buffer_size) ?
+ (size_t)data->set.buffer_size :
+ curlx_sotouz(data->state.resume_from - passed);
+
+ size_t actuallyread =
+ data->state.fread_func(data->state.buffer, 1, readthisamountnow,
+ data->state.in);
+
+ passed += actuallyread;
+ if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
+ /* this checks for greater-than only to make sure that the
+ CURL_READFUNC_ABORT return code still aborts */
+ failf(data, "Could only read %" CURL_FORMAT_CURL_OFF_T
+ " bytes from the input", passed);
+ return CURLE_READ_ERROR;
+ }
+ } while(passed < data->state.resume_from);
+ }
+
+ /* now, decrease the size of the read */
+ if(data->state.infilesize>0) {
+ data->state.infilesize -= data->state.resume_from;
+
+ if(data->state.infilesize <= 0) {
+ failf(data, "File already completely uploaded");
+ return CURLE_PARTIAL_FILE;
+ }
+ }
+ /* we've passed, proceed as normal */
+ }
+ }
+ return CURLE_OK;
+}
+
+CURLcode Curl_http_firstwrite(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool *done)
+{
+ struct SingleRequest *k = &data->req;
+
+ if(data->req.newurl) {
+ if(conn->bits.close) {
+ /* Abort after the headers if "follow Location" is set
+ and we're set to close anyway. */
+ k->keepon &= ~KEEP_RECV;
+ *done = TRUE;
+ return CURLE_OK;
+ }
+ /* We have a new url to load, but since we want to be able to re-use this
+ connection properly, we read the full response in "ignore more" */
+ k->ignorebody = TRUE;
+ infof(data, "Ignoring the response-body");
+ }
+ if(data->state.resume_from && !k->content_range &&
+ (data->state.httpreq == HTTPREQ_GET) &&
+ !k->ignorebody) {
+
+ if(k->size == data->state.resume_from) {
+ /* The resume point is at the end of file, consider this fine even if it
+ doesn't allow resume from here. */
+ infof(data, "The entire document is already downloaded");
+ streamclose(conn, "already downloaded");
+ /* Abort download */
+ k->keepon &= ~KEEP_RECV;
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ /* we wanted to resume a download, although the server doesn't seem to
+ * support this and we did this with a GET (if it wasn't a GET we did a
+ * POST or PUT resume) */
+ failf(data, "HTTP server doesn't seem to support "
+ "byte ranges. Cannot resume.");
+ return CURLE_RANGE_ERROR;
+ }
+
+ if(data->set.timecondition && !data->state.range) {
+ /* A time condition has been set AND no ranges have been requested. This
+ seems to be what chapter 13.3.4 of RFC 2616 defines to be the correct
+ action for an HTTP/1.1 client */
+
+ if(!Curl_meets_timecondition(data, k->timeofdoc)) {
+ *done = TRUE;
+ /* We're simulating an HTTP 304 from server so we return
+ what should have been returned from the server */
+ data->info.httpcode = 304;
+ infof(data, "Simulate an HTTP 304 response");
+ /* we abort the transfer before it is completed == we ruin the
+ re-use ability. Close the connection */
+ streamclose(conn, "Simulated 304 handling");
+ return CURLE_OK;
+ }
+ } /* we have a time condition */
+
+ return CURLE_OK;
+}
+
+#ifdef HAVE_LIBZ
+CURLcode Curl_transferencode(struct Curl_easy *data)
+{
+ if(!Curl_checkheaders(data, STRCONST("TE")) &&
+ data->set.http_transfer_encoding) {
+ /* When we are to insert a TE: header in the request, we must also insert
+ TE in a Connection: header, so we need to merge the custom provided
+ Connection: header and prevent the original to get sent. Note that if
+ the user has inserted his/her own TE: header we don't do this magic
+ but then assume that the user will handle it all! */
+ char *cptr = Curl_checkheaders(data, STRCONST("Connection"));
+#define TE_HEADER "TE: gzip\r\n"
+
+ Curl_safefree(data->state.aptr.te);
+
+ if(cptr) {
+ cptr = Curl_copy_header_value(cptr);
+ if(!cptr)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* Create the (updated) Connection: header */
+ data->state.aptr.te = aprintf("Connection: %s%sTE\r\n" TE_HEADER,
+ cptr ? cptr : "", (cptr && *cptr) ? ", ":"");
+
+ free(cptr);
+ if(!data->state.aptr.te)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ return CURLE_OK;
+}
+#endif
+
+#ifndef USE_HYPER
+/*
+ * Curl_http() gets called from the generic multi_do() function when an HTTP
+ * request is to be performed. This creates and sends a properly constructed
+ * HTTP request.
+ */
+CURLcode Curl_http(struct Curl_easy *data, bool *done)
+{
+ struct connectdata *conn = data->conn;
+ CURLcode result = CURLE_OK;
+ struct HTTP *http;
+ Curl_HttpReq httpreq;
+ const char *te = ""; /* transfer-encoding */
+ const char *request;
+ const char *httpstring;
+ struct dynbuf req;
+ char *altused = NULL;
+ const char *p_accept; /* Accept: string */
+
+ /* Always consider the DO phase done after this function call, even if there
+ may be parts of the request that are not yet sent, since we can deal with
+ the rest of the request in the PERFORM phase. */
+ *done = TRUE;
+
+ if(conn->transport != TRNSPRT_QUIC) {
+ if(conn->httpversion < 20) { /* unless the connection is re-used and
+ already http2 */
+ switch(conn->alpn) {
+ case CURL_HTTP_VERSION_2:
+ conn->httpversion = 20; /* we know we're on HTTP/2 now */
+
+ result = Curl_http2_switched(data, NULL, 0);
+ if(result)
+ return result;
+ break;
+ case CURL_HTTP_VERSION_1_1:
+ /* continue with HTTP/1.1 when explicitly requested */
+ break;
+ default:
+ /* Check if user wants to use HTTP/2 with clear TCP */
+#ifdef USE_NGHTTP2
+ if(data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) {
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
+ /* We don't support HTTP/2 proxies yet. Also it's debatable
+ whether or not this setting should apply to HTTP/2 proxies. */
+ infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
+ break;
+ }
+#endif
+ DEBUGF(infof(data, "HTTP/2 over clean TCP"));
+ conn->httpversion = 20;
+
+ result = Curl_http2_switched(data, NULL, 0);
+ if(result)
+ return result;
+ }
+#endif
+ break;
+ }
+ }
+ else {
+ /* prepare for an http2 request */
+ result = Curl_http2_setup(data, conn);
+ if(result)
+ return result;
+ }
+ }
+ http = data->req.p.http;
+ DEBUGASSERT(http);
+
+ result = Curl_http_host(data, conn);
+ if(result)
+ return result;
+
+ result = Curl_http_useragent(data);
+ if(result)
+ return result;
+
+ Curl_http_method(data, conn, &request, &httpreq);
+
+ /* setup the authentication headers */
+ {
+ char *pq = NULL;
+ if(data->state.up.query) {
+ pq = aprintf("%s?%s", data->state.up.path, data->state.up.query);
+ if(!pq)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ result = Curl_http_output_auth(data, conn, request, httpreq,
+ (pq ? pq : data->state.up.path), FALSE);
+ free(pq);
+ if(result)
+ return result;
+ }
+
+ Curl_safefree(data->state.aptr.ref);
+ if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) {
+ data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer);
+ if(!data->state.aptr.ref)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
+ data->set.str[STRING_ENCODING]) {
+ Curl_safefree(data->state.aptr.accept_encoding);
+ data->state.aptr.accept_encoding =
+ aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
+ if(!data->state.aptr.accept_encoding)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else
+ Curl_safefree(data->state.aptr.accept_encoding);
+
+#ifdef HAVE_LIBZ
+ /* we only consider transfer-encoding magic if libz support is built-in */
+ result = Curl_transferencode(data);
+ if(result)
+ return result;
+#endif
+
+ result = Curl_http_body(data, conn, httpreq, &te);
+ if(result)
+ return result;
+
+ p_accept = Curl_checkheaders(data,
+ STRCONST("Accept"))?NULL:"Accept: */*\r\n";
+
+ result = Curl_http_resume(data, conn, httpreq);
+ if(result)
+ return result;
+
+ result = Curl_http_range(data, httpreq);
+ if(result)
+ return result;
+
+ httpstring = get_http_string(data, conn);
+
+ /* initialize a dynamic send-buffer */
+ Curl_dyn_init(&req, DYN_HTTP_REQUEST);
+
+ /* make sure the header buffer is reset - if there are leftovers from a
+ previous transfer */
+ Curl_dyn_reset(&data->state.headerb);
+
+ /* add the main request stuff */
+ /* GET/HEAD/POST/PUT */
+ result = Curl_dyn_addf(&req, "%s ", request);
+ if(!result)
+ result = Curl_http_target(data, conn, &req);
+ if(result) {
+ Curl_dyn_free(&req);
+ return result;
+ }
+
+#ifndef CURL_DISABLE_ALTSVC
+ if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) {
+ altused = aprintf("Alt-Used: %s:%d\r\n",
+ conn->conn_to_host.name, conn->conn_to_port);
+ if(!altused) {
+ Curl_dyn_free(&req);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+#endif
+ result =
+ Curl_dyn_addf(&req,
+ " HTTP/%s\r\n" /* HTTP version */
+ "%s" /* host */
+ "%s" /* proxyuserpwd */
+ "%s" /* userpwd */
+ "%s" /* range */
+ "%s" /* user agent */
+ "%s" /* accept */
+ "%s" /* TE: */
+ "%s" /* accept-encoding */
+ "%s" /* referer */
+ "%s" /* Proxy-Connection */
+ "%s" /* transfer-encoding */
+ "%s",/* Alt-Used */
+
+ httpstring,
+ (data->state.aptr.host?data->state.aptr.host:""),
+ data->state.aptr.proxyuserpwd?
+ data->state.aptr.proxyuserpwd:"",
+ data->state.aptr.userpwd?data->state.aptr.userpwd:"",
+ (data->state.use_range && data->state.aptr.rangeline)?
+ data->state.aptr.rangeline:"",
+ (data->set.str[STRING_USERAGENT] &&
+ *data->set.str[STRING_USERAGENT] &&
+ data->state.aptr.uagent)?
+ data->state.aptr.uagent:"",
+ p_accept?p_accept:"",
+ data->state.aptr.te?data->state.aptr.te:"",
+ (data->set.str[STRING_ENCODING] &&
+ *data->set.str[STRING_ENCODING] &&
+ data->state.aptr.accept_encoding)?
+ data->state.aptr.accept_encoding:"",
+ (data->state.referer && data->state.aptr.ref)?
+ data->state.aptr.ref:"" /* Referer: <data> */,
+#ifndef CURL_DISABLE_PROXY
+ (conn->bits.httpproxy &&
+ !conn->bits.tunnel_proxy &&
+ !Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
+ !Curl_checkProxyheaders(data,
+ conn,
+ STRCONST("Proxy-Connection")))?
+ "Proxy-Connection: Keep-Alive\r\n":"",
+#else
+ "",
+#endif
+ te,
+ altused ? altused : ""
+ );
+
+ /* clear userpwd and proxyuserpwd to avoid re-using old credentials
+ * from re-used connections */
+ Curl_safefree(data->state.aptr.userpwd);
+ Curl_safefree(data->state.aptr.proxyuserpwd);
+ free(altused);
+
+ if(result) {
+ Curl_dyn_free(&req);
+ return result;
+ }
+
+ if(!(conn->handler->flags&PROTOPT_SSL) &&
+ conn->httpversion != 20 &&
+ (data->state.httpwant == CURL_HTTP_VERSION_2)) {
+ /* append HTTP2 upgrade magic stuff to the HTTP request if it isn't done
+ over SSL */
+ result = Curl_http2_request_upgrade(&req, data);
+ if(result) {
+ Curl_dyn_free(&req);
+ return result;
+ }
+ }
+
+ result = Curl_http_cookies(data, conn, &req);
+ if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
+ result = Curl_ws_request(data, &req);
+ if(!result)
+ result = Curl_add_timecondition(data, &req);
+ if(!result)
+ result = Curl_add_custom_headers(data, FALSE, &req);
+
+ if(!result) {
+ http->postdata = NULL; /* nothing to post at this point */
+ if((httpreq == HTTPREQ_GET) ||
+ (httpreq == HTTPREQ_HEAD))
+ Curl_pgrsSetUploadSize(data, 0); /* nothing */
+
+ /* bodysend takes ownership of the 'req' memory on success */
+ result = Curl_http_bodysend(data, conn, &req, httpreq);
+ }
+ if(result) {
+ Curl_dyn_free(&req);
+ return result;
+ }
+
+ if((http->postsize > -1) &&
+ (http->postsize <= data->req.writebytecount) &&
+ (http->sending != HTTPSEND_REQUEST))
+ data->req.upload_done = TRUE;
+
+ if(data->req.writebytecount) {
+ /* if a request-body has been sent off, we make sure this progress is noted
+ properly */
+ Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
+ if(Curl_pgrsUpdate(data))
+ result = CURLE_ABORTED_BY_CALLBACK;
+
+ if(!http->postsize) {
+ /* already sent the entire request body, mark the "upload" as
+ complete */
+ infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T
+ " out of %" CURL_FORMAT_CURL_OFF_T " bytes",
+ data->req.writebytecount, http->postsize);
+ data->req.upload_done = TRUE;
+ data->req.keepon &= ~KEEP_SEND; /* we're done writing */
+ data->req.exp100 = EXP100_SEND_DATA; /* already sent */
+ Curl_expire_done(data, EXPIRE_100_TIMEOUT);
+ }
+ }
+
+ if((conn->httpversion == 20) && data->req.upload_chunky)
+ /* upload_chunky was set above to set up the request in a chunky fashion,
+ but is disabled here again to avoid that the chunked encoded version is
+ actually used when sending the request body over h2 */
+ data->req.upload_chunky = FALSE;
+ return result;
+}
+
+#endif /* USE_HYPER */
+
+typedef enum {
+ STATUS_UNKNOWN, /* not enough data to tell yet */
+ STATUS_DONE, /* a status line was read */
+ STATUS_BAD /* not a status line */
+} statusline;
+
+
+/* Check a string for a prefix. Check no more than 'len' bytes */
+static bool checkprefixmax(const char *prefix, const char *buffer, size_t len)
+{
+ size_t ch = CURLMIN(strlen(prefix), len);
+ return curl_strnequal(prefix, buffer, ch);
+}
+
+/*
+ * checkhttpprefix()
+ *
+ * Returns TRUE if member of the list matches prefix of string
+ */
+static statusline
+checkhttpprefix(struct Curl_easy *data,
+ const char *s, size_t len)
+{
+ struct curl_slist *head = data->set.http200aliases;
+ statusline rc = STATUS_BAD;
+ statusline onmatch = len >= 5? STATUS_DONE : STATUS_UNKNOWN;
+
+ while(head) {
+ if(checkprefixmax(head->data, s, len)) {
+ rc = onmatch;
+ break;
+ }
+ head = head->next;
+ }
+
+ if((rc != STATUS_DONE) && (checkprefixmax("HTTP/", s, len)))
+ rc = onmatch;
+
+ return rc;
+}
+
+#ifndef CURL_DISABLE_RTSP
+static statusline
+checkrtspprefix(struct Curl_easy *data,
+ const char *s, size_t len)
+{
+ statusline result = STATUS_BAD;
+ statusline onmatch = len >= 5? STATUS_DONE : STATUS_UNKNOWN;
+ (void)data; /* unused */
+ if(checkprefixmax("RTSP/", s, len))
+ result = onmatch;
+
+ return result;
+}
+#endif /* CURL_DISABLE_RTSP */
+
+static statusline
+checkprotoprefix(struct Curl_easy *data, struct connectdata *conn,
+ const char *s, size_t len)
+{
+#ifndef CURL_DISABLE_RTSP
+ if(conn->handler->protocol & CURLPROTO_RTSP)
+ return checkrtspprefix(data, s, len);
+#else
+ (void)conn;
+#endif /* CURL_DISABLE_RTSP */
+
+ return checkhttpprefix(data, s, len);
+}
+
+/*
+ * Curl_http_header() parses a single response header.
+ */
+CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
+ char *headp)
+{
+ CURLcode result;
+ struct SingleRequest *k = &data->req;
+ /* Check for Content-Length: header lines to get size */
+ if(!k->http_bodyless &&
+ !data->set.ignorecl && checkprefix("Content-Length:", headp)) {
+ curl_off_t contentlength;
+ CURLofft offt = curlx_strtoofft(headp + strlen("Content-Length:"),
+ NULL, 10, &contentlength);
+
+ if(offt == CURL_OFFT_OK) {
+ k->size = contentlength;
+ k->maxdownload = k->size;
+ }
+ else if(offt == CURL_OFFT_FLOW) {
+ /* out of range */
+ if(data->set.max_filesize) {
+ failf(data, "Maximum file size exceeded");
+ return CURLE_FILESIZE_EXCEEDED;
+ }
+ streamclose(conn, "overflow content-length");
+ infof(data, "Overflow Content-Length: value");
+ }
+ else {
+ /* negative or just rubbish - bad HTTP */
+ failf(data, "Invalid Content-Length: value");
+ return CURLE_WEIRD_SERVER_REPLY;
+ }
+ }
+ /* check for Content-Type: header lines to get the MIME-type */
+ else if(checkprefix("Content-Type:", headp)) {
+ char *contenttype = Curl_copy_header_value(headp);
+ if(!contenttype)
+ return CURLE_OUT_OF_MEMORY;
+ if(!*contenttype)
+ /* ignore empty data */
+ free(contenttype);
+ else {
+ Curl_safefree(data->info.contenttype);
+ data->info.contenttype = contenttype;
+ }
+ }
+#ifndef CURL_DISABLE_PROXY
+ else if((conn->httpversion == 10) &&
+ conn->bits.httpproxy &&
+ Curl_compareheader(headp,
+ STRCONST("Proxy-Connection:"),
+ STRCONST("keep-alive"))) {
+ /*
+ * When an HTTP/1.0 reply comes when using a proxy, the
+ * 'Proxy-Connection: keep-alive' line tells us the
+ * connection will be kept alive for our pleasure.
+ * Default action for 1.0 is to close.
+ */
+ connkeep(conn, "Proxy-Connection keep-alive"); /* don't close */
+ infof(data, "HTTP/1.0 proxy connection set to keep alive");
+ }
+ else if((conn->httpversion == 11) &&
+ conn->bits.httpproxy &&
+ Curl_compareheader(headp,
+ STRCONST("Proxy-Connection:"),
+ STRCONST("close"))) {
+ /*
+ * We get an HTTP/1.1 response from a proxy and it says it'll
+ * close down after this transfer.
+ */
+ connclose(conn, "Proxy-Connection: asked to close after done");
+ infof(data, "HTTP/1.1 proxy connection set close");
+ }
+#endif
+ else if((conn->httpversion == 10) &&
+ Curl_compareheader(headp,
+ STRCONST("Connection:"),
+ STRCONST("keep-alive"))) {
+ /*
+ * An HTTP/1.0 reply with the 'Connection: keep-alive' line
+ * tells us the connection will be kept alive for our
+ * pleasure. Default action for 1.0 is to close.
+ *
+ * [RFC2068, section 19.7.1] */
+ connkeep(conn, "Connection keep-alive");
+ infof(data, "HTTP/1.0 connection set to keep alive");
+ }
+ else if(Curl_compareheader(headp,
+ STRCONST("Connection:"), STRCONST("close"))) {
+ /*
+ * [RFC 2616, section 8.1.2.1]
+ * "Connection: close" is HTTP/1.1 language and means that
+ * the connection will close when this request has been
+ * served.
+ */
+ streamclose(conn, "Connection: close used");
+ }
+ else if(!k->http_bodyless && checkprefix("Transfer-Encoding:", headp)) {
+ /* One or more encodings. We check for chunked and/or a compression
+ algorithm. */
+ /*
+ * [RFC 2616, section 3.6.1] A 'chunked' transfer encoding
+ * means that the server will send a series of "chunks". Each
+ * chunk starts with line with info (including size of the
+ * coming block) (terminated with CRLF), then a block of data
+ * with the previously mentioned size. There can be any amount
+ * of chunks, and a chunk-data set to zero signals the
+ * end-of-chunks. */
+
+ result = Curl_build_unencoding_stack(data,
+ headp + strlen("Transfer-Encoding:"),
+ TRUE);
+ if(result)
+ return result;
+ if(!k->chunk) {
+ /* if this isn't chunked, only close can signal the end of this transfer
+ as Content-Length is said not to be trusted for transfer-encoding! */
+ connclose(conn, "HTTP/1.1 transfer-encoding without chunks");
+ k->ignore_cl = TRUE;
+ }
+ }
+ else if(!k->http_bodyless && checkprefix("Content-Encoding:", headp) &&
+ data->set.str[STRING_ENCODING]) {
+ /*
+ * Process Content-Encoding. Look for the values: identity,
+ * gzip, deflate, compress, x-gzip and x-compress. x-gzip and
+ * x-compress are the same as gzip and compress. (Sec 3.5 RFC
+ * 2616). zlib cannot handle compress. However, errors are
+ * handled further down when the response body is processed
+ */
+ result = Curl_build_unencoding_stack(data,
+ headp + strlen("Content-Encoding:"),
+ FALSE);
+ if(result)
+ return result;
+ }
+ else if(checkprefix("Retry-After:", headp)) {
+ /* Retry-After = HTTP-date / delay-seconds */
+ curl_off_t retry_after = 0; /* zero for unknown or "now" */
+ /* Try it as a decimal number, if it works it is not a date */
+ (void)curlx_strtoofft(headp + strlen("Retry-After:"),
+ NULL, 10, &retry_after);
+ if(!retry_after) {
+ time_t date = Curl_getdate_capped(headp + strlen("Retry-After:"));
+ if(-1 != date)
+ /* convert date to number of seconds into the future */
+ retry_after = date - time(NULL);
+ }
+ data->info.retry_after = retry_after; /* store it */
+ }
+ else if(!k->http_bodyless && checkprefix("Content-Range:", headp)) {
+ /* Content-Range: bytes [num]-
+ Content-Range: bytes: [num]-
+ Content-Range: [num]-
+ Content-Range: [asterisk]/[total]
+
+ The second format was added since Sun's webserver
+ JavaWebServer/1.1.1 obviously sends the header this way!
+ The third added since some servers use that!
+ The fourth means the requested range was unsatisfied.
+ */
+
+ char *ptr = headp + strlen("Content-Range:");
+
+ /* Move forward until first digit or asterisk */
+ while(*ptr && !ISDIGIT(*ptr) && *ptr != '*')
+ ptr++;
+
+ /* if it truly stopped on a digit */
+ if(ISDIGIT(*ptr)) {
+ if(!curlx_strtoofft(ptr, NULL, 10, &k->offset)) {
+ if(data->state.resume_from == k->offset)
+ /* we asked for a resume and we got it */
+ k->content_range = TRUE;
+ }
+ }
+ else
+ data->state.resume_from = 0; /* get everything */
+ }
+#if !defined(CURL_DISABLE_COOKIES)
+ else if(data->cookies && data->state.cookie_engine &&
+ checkprefix("Set-Cookie:", headp)) {
+ /* If there is a custom-set Host: name, use it here, or else use real peer
+ host name. */
+ const char *host = data->state.aptr.cookiehost?
+ data->state.aptr.cookiehost:conn->host.name;
+ const bool secure_context =
+ conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) ||
+ strcasecompare("localhost", host) ||
+ !strcmp(host, "127.0.0.1") ||
+ !strcmp(host, "::1") ? TRUE : FALSE;
+
+ Curl_share_lock(data, CURL_LOCK_DATA_COOKIE,
+ CURL_LOCK_ACCESS_SINGLE);
+ Curl_cookie_add(data, data->cookies, TRUE, FALSE,
+ headp + strlen("Set-Cookie:"), host,
+ data->state.up.path, secure_context);
+ Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
+ }
+#endif
+ else if(!k->http_bodyless && checkprefix("Last-Modified:", headp) &&
+ (data->set.timecondition || data->set.get_filetime) ) {
+ k->timeofdoc = Curl_getdate_capped(headp + strlen("Last-Modified:"));
+ if(data->set.get_filetime)
+ data->info.filetime = k->timeofdoc;
+ }
+ else if((checkprefix("WWW-Authenticate:", headp) &&
+ (401 == k->httpcode)) ||
+ (checkprefix("Proxy-authenticate:", headp) &&
+ (407 == k->httpcode))) {
+
+ bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
+ char *auth = Curl_copy_header_value(headp);
+ if(!auth)
+ return CURLE_OUT_OF_MEMORY;
+
+ result = Curl_http_input_auth(data, proxy, auth);
+
+ free(auth);
+
+ if(result)
+ return result;
+ }
+#ifdef USE_SPNEGO
+ else if(checkprefix("Persistent-Auth:", headp)) {
+ struct negotiatedata *negdata = &conn->negotiate;
+ struct auth *authp = &data->state.authhost;
+ if(authp->picked == CURLAUTH_NEGOTIATE) {
+ char *persistentauth = Curl_copy_header_value(headp);
+ if(!persistentauth)
+ return CURLE_OUT_OF_MEMORY;
+ negdata->noauthpersist = checkprefix("false", persistentauth)?
+ TRUE:FALSE;
+ negdata->havenoauthpersist = TRUE;
+ infof(data, "Negotiate: noauthpersist -> %d, header part: %s",
+ negdata->noauthpersist, persistentauth);
+ free(persistentauth);
+ }
+ }
+#endif
+ else if((k->httpcode >= 300 && k->httpcode < 400) &&
+ checkprefix("Location:", headp) &&
+ !data->req.location) {
+ /* this is the URL that the server advises us to use instead */
+ char *location = Curl_copy_header_value(headp);
+ if(!location)
+ return CURLE_OUT_OF_MEMORY;
+ if(!*location)
+ /* ignore empty data */
+ free(location);
+ else {
+ data->req.location = location;
+
+ if(data->set.http_follow_location) {
+ DEBUGASSERT(!data->req.newurl);
+ data->req.newurl = strdup(data->req.location); /* clone */
+ if(!data->req.newurl)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* some cases of POST and PUT etc needs to rewind the data
+ stream at this point */
+ result = http_perhapsrewind(data, conn);
+ if(result)
+ return result;
+
+ /* mark the next request as a followed location: */
+ data->state.this_is_a_follow = TRUE;
+ }
+ }
+ }
+
+#ifndef CURL_DISABLE_HSTS
+ /* If enabled, the header is incoming and this is over HTTPS */
+ else if(data->hsts && checkprefix("Strict-Transport-Security:", headp) &&
+ ((conn->handler->flags & PROTOPT_SSL) ||
+#ifdef CURLDEBUG
+ /* allow debug builds to circumvent the HTTPS restriction */
+ getenv("CURL_HSTS_HTTP")
+#else
+ 0
+#endif
+ )) {
+ CURLcode check =
+ Curl_hsts_parse(data->hsts, conn->host.name,
+ headp + strlen("Strict-Transport-Security:"));
+ if(check)
+ infof(data, "Illegal STS header skipped");
+#ifdef DEBUGBUILD
+ else
+ infof(data, "Parsed STS header fine (%zu entries)",
+ data->hsts->list.size);
+#endif
+ }
+#endif
+#ifndef CURL_DISABLE_ALTSVC
+ /* If enabled, the header is incoming and this is over HTTPS */
+ else if(data->asi && checkprefix("Alt-Svc:", headp) &&
+ ((conn->handler->flags & PROTOPT_SSL) ||
+#ifdef CURLDEBUG
+ /* allow debug builds to circumvent the HTTPS restriction */
+ getenv("CURL_ALTSVC_HTTP")
+#else
+ 0
+#endif
+ )) {
+ /* the ALPN of the current request */
+ enum alpnid id = (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1;
+ result = Curl_altsvc_parse(data, data->asi,
+ headp + strlen("Alt-Svc:"),
+ id, conn->host.name,
+ curlx_uitous((unsigned int)conn->remote_port));
+ if(result)
+ return result;
+ }
+#endif
+ else if(conn->handler->protocol & CURLPROTO_RTSP) {
+ result = Curl_rtsp_parseheader(data, headp);
+ if(result)
+ return result;
+ }
+ return CURLE_OK;
+}
+
+/*
+ * Called after the first HTTP response line (the status line) has been
+ * received and parsed.
+ */
+
+CURLcode Curl_http_statusline(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ struct SingleRequest *k = &data->req;
+ data->info.httpcode = k->httpcode;
+
+ data->info.httpversion = conn->httpversion;
+ if(!data->state.httpversion ||
+ data->state.httpversion > conn->httpversion)
+ /* store the lowest server version we encounter */
+ data->state.httpversion = conn->httpversion;
+
+ /*
+ * This code executes as part of processing the header. As a
+ * result, it's not totally clear how to interpret the
+ * response code yet as that depends on what other headers may
+ * be present. 401 and 407 may be errors, but may be OK
+ * depending on how authentication is working. Other codes
+ * are definitely errors, so give up here.
+ */
+ if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET &&
+ k->httpcode == 416) {
+ /* "Requested Range Not Satisfiable", just proceed and
+ pretend this is no error */
+ k->ignorebody = TRUE; /* Avoid appending error msg to good data. */
+ }
+
+ if(conn->httpversion == 10) {
+ /* Default action for HTTP/1.0 must be to close, unless
+ we get one of those fancy headers that tell us the
+ server keeps it open for us! */
+ infof(data, "HTTP 1.0, assume close after body");
+ connclose(conn, "HTTP/1.0 close after body");
+ }
+ else if(conn->httpversion == 20 ||
+ (k->upgr101 == UPGR101_H2 && k->httpcode == 101)) {
+ DEBUGF(infof(data, "HTTP/2 found, allow multiplexing"));
+ /* HTTP/2 cannot avoid multiplexing since it is a core functionality
+ of the protocol */
+ conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+ }
+ else if(conn->httpversion >= 11 &&
+ !conn->bits.close) {
+ /* If HTTP version is >= 1.1 and connection is persistent */
+ DEBUGF(infof(data,
+ "HTTP 1.1 or later with persistent connection"));
+ }
+
+ k->http_bodyless = k->httpcode >= 100 && k->httpcode < 200;
+ switch(k->httpcode) {
+ case 304:
+ /* (quote from RFC2616, section 10.3.5): The 304 response
+ * MUST NOT contain a message-body, and thus is always
+ * terminated by the first empty line after the header
+ * fields. */
+ if(data->set.timecondition)
+ data->info.timecond = TRUE;
+ /* FALLTHROUGH */
+ case 204:
+ /* (quote from RFC2616, section 10.2.5): The server has
+ * fulfilled the request but does not need to return an
+ * entity-body ... The 204 response MUST NOT include a
+ * message-body, and thus is always terminated by the first
+ * empty line after the header fields. */
+ k->size = 0;
+ k->maxdownload = 0;
+ k->http_bodyless = TRUE;
+ break;
+ default:
+ break;
+ }
+ return CURLE_OK;
+}
+
+/* Content-Length must be ignored if any Transfer-Encoding is present in the
+ response. Refer to RFC 7230 section 3.3.3 and RFC2616 section 4.4. This is
+ figured out here after all headers have been received but before the final
+ call to the user's header callback, so that a valid content length can be
+ retrieved by the user in the final call. */
+CURLcode Curl_http_size(struct Curl_easy *data)
+{
+ struct SingleRequest *k = &data->req;
+ if(data->req.ignore_cl || k->chunk) {
+ k->size = k->maxdownload = -1;
+ }
+ else if(k->size != -1) {
+ if(data->set.max_filesize &&
+ k->size > data->set.max_filesize) {
+ failf(data, "Maximum file size exceeded");
+ return CURLE_FILESIZE_EXCEEDED;
+ }
+ Curl_pgrsSetDownloadSize(data, k->size);
+ k->maxdownload = k->size;
+ }
+ return CURLE_OK;
+}
+
+static CURLcode verify_header(struct Curl_easy *data)
+{
+ struct SingleRequest *k = &data->req;
+ const char *header = Curl_dyn_ptr(&data->state.headerb);
+ size_t hlen = Curl_dyn_len(&data->state.headerb);
+ char *ptr = memchr(header, 0x00, hlen);
+ if(ptr) {
+ /* this is bad, bail out */
+ failf(data, "Nul byte in header");
+ return CURLE_WEIRD_SERVER_REPLY;
+ }
+ if(k->headerline < 2)
+ /* the first "header" is the status-line and it has no colon */
+ return CURLE_OK;
+ if(((header[0] == ' ') || (header[0] == '\t')) && k->headerline > 2)
+ /* line folding, can't happen on line 2 */
+ ;
+ else {
+ ptr = memchr(header, ':', hlen);
+ if(!ptr) {
+ /* this is bad, bail out */
+ failf(data, "Header without colon");
+ return CURLE_WEIRD_SERVER_REPLY;
+ }
+ }
+ return CURLE_OK;
+}
+
+/*
+ * Read any HTTP header lines from the server and pass them to the client app.
+ */
+CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
+ struct connectdata *conn,
+ ssize_t *nread,
+ bool *stop_reading)
+{
+ CURLcode result;
+ struct SingleRequest *k = &data->req;
+ ssize_t onread = *nread;
+ char *ostr = k->str;
+ char *headp;
+ char *str_start;
+ char *end_ptr;
+
+ /* header line within buffer loop */
+ do {
+ size_t rest_length;
+ size_t full_length;
+ int writetype;
+
+ /* str_start is start of line within buf */
+ str_start = k->str;
+
+ /* data is in network encoding so use 0x0a instead of '\n' */
+ end_ptr = memchr(str_start, 0x0a, *nread);
+
+ if(!end_ptr) {
+ /* Not a complete header line within buffer, append the data to
+ the end of the headerbuff. */
+ result = Curl_dyn_addn(&data->state.headerb, str_start, *nread);
+ if(result)
+ return result;
+
+ if(!k->headerline) {
+ /* check if this looks like a protocol header */
+ statusline st =
+ checkprotoprefix(data, conn,
+ Curl_dyn_ptr(&data->state.headerb),
+ Curl_dyn_len(&data->state.headerb));
+
+ if(st == STATUS_BAD) {
+ /* this is not the beginning of a protocol first header line */
+ k->header = FALSE;
+ k->badheader = HEADER_ALLBAD;
+ streamclose(conn, "bad HTTP: No end-of-message indicator");
+ if(!data->set.http09_allowed) {
+ failf(data, "Received HTTP/0.9 when not allowed");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
+ break;
+ }
+ }
+
+ break; /* read more and try again */
+ }
+
+ /* decrease the size of the remaining (supposed) header line */
+ rest_length = (end_ptr - k->str) + 1;
+ *nread -= (ssize_t)rest_length;
+
+ k->str = end_ptr + 1; /* move past new line */
+
+ full_length = k->str - str_start;
+
+ result = Curl_dyn_addn(&data->state.headerb, str_start, full_length);
+ if(result)
+ return result;
+
+ /****
+ * We now have a FULL header line in 'headerb'.
+ *****/
+
+ if(!k->headerline) {
+ /* the first read header */
+ statusline st = checkprotoprefix(data, conn,
+ Curl_dyn_ptr(&data->state.headerb),
+ Curl_dyn_len(&data->state.headerb));
+ if(st == STATUS_BAD) {
+ streamclose(conn, "bad HTTP: No end-of-message indicator");
+ /* this is not the beginning of a protocol first header line */
+ if(!data->set.http09_allowed) {
+ failf(data, "Received HTTP/0.9 when not allowed");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
+ k->header = FALSE;
+ if(*nread)
+ /* since there's more, this is a partial bad header */
+ k->badheader = HEADER_PARTHEADER;
+ else {
+ /* this was all we read so it's all a bad header */
+ k->badheader = HEADER_ALLBAD;
+ *nread = onread;
+ k->str = ostr;
+ return CURLE_OK;
+ }
+ break;
+ }
+ }
+
+ /* headers are in network encoding so use 0x0a and 0x0d instead of '\n'
+ and '\r' */
+ headp = Curl_dyn_ptr(&data->state.headerb);
+ if((0x0a == *headp) || (0x0d == *headp)) {
+ size_t headerlen;
+ /* Zero-length header line means end of headers! */
+
+ if('\r' == *headp)
+ headp++; /* pass the \r byte */
+ if('\n' == *headp)
+ headp++; /* pass the \n byte */
+
+ if(100 <= k->httpcode && 199 >= k->httpcode) {
+ /* "A user agent MAY ignore unexpected 1xx status responses." */
+ switch(k->httpcode) {
+ case 100:
+ /*
+ * We have made an HTTP PUT or POST and this is 1.1-lingo
+ * that tells us that the server is OK with this and ready
+ * to receive the data.
+ * However, we'll get more headers now so we must get
+ * back into the header-parsing state!
+ */
+ k->header = TRUE;
+ k->headerline = 0; /* restart the header line counter */
+
+ /* if we did wait for this do enable write now! */
+ if(k->exp100 > EXP100_SEND_DATA) {
+ k->exp100 = EXP100_SEND_DATA;
+ k->keepon |= KEEP_SEND;
+ Curl_expire_done(data, EXPIRE_100_TIMEOUT);
+ }
+ break;
+ case 101:
+ /* Switching Protocols */
+ if(k->upgr101 == UPGR101_H2) {
+ /* Switching to HTTP/2 */
+ infof(data, "Received 101, Switching to HTTP/2");
+ k->upgr101 = UPGR101_RECEIVED;
+
+ /* we'll get more headers (HTTP/2 response) */
+ k->header = TRUE;
+ k->headerline = 0; /* restart the header line counter */
+
+ /* switch to http2 now. The bytes after response headers
+ are also processed here, otherwise they are lost. */
+ result = Curl_http2_switched(data, k->str, *nread);
+ if(result)
+ return result;
+ *nread = 0;
+ }
+#ifdef USE_WEBSOCKETS
+ else if(k->upgr101 == UPGR101_WS) {
+ /* verify the response */
+ result = Curl_ws_accept(data);
+ if(result)
+ return result;
+ k->header = FALSE; /* no more header to parse! */
+ if(data->set.connect_only) {
+ k->keepon &= ~KEEP_RECV; /* read no more content */
+ *nread = 0;
+ }
+ }
+#endif
+ else {
+ /* Not switching to another protocol */
+ k->header = FALSE; /* no more header to parse! */
+ }
+ break;
+ default:
+ /* the status code 1xx indicates a provisional response, so
+ we'll get another set of headers */
+ k->header = TRUE;
+ k->headerline = 0; /* restart the header line counter */
+ break;
+ }
+ }
+ else {
+ k->header = FALSE; /* no more header to parse! */
+
+ if((k->size == -1) && !k->chunk && !conn->bits.close &&
+ (conn->httpversion == 11) &&
+ !(conn->handler->protocol & CURLPROTO_RTSP) &&
+ data->state.httpreq != HTTPREQ_HEAD) {
+ /* On HTTP 1.1, when connection is not to get closed, but no
+ Content-Length nor Transfer-Encoding chunked have been
+ received, according to RFC2616 section 4.4 point 5, we
+ assume that the server will close the connection to
+ signal the end of the document. */
+ infof(data, "no chunk, no close, no size. Assume close to "
+ "signal end");
+ streamclose(conn, "HTTP: No end-of-message indicator");
+ }
+ }
+
+ if(!k->header) {
+ result = Curl_http_size(data);
+ if(result)
+ return result;
+ }
+
+ /* At this point we have some idea about the fate of the connection.
+ If we are closing the connection it may result auth failure. */
+#if defined(USE_NTLM)
+ if(conn->bits.close &&
+ (((data->req.httpcode == 401) &&
+ (conn->http_ntlm_state == NTLMSTATE_TYPE2)) ||
+ ((data->req.httpcode == 407) &&
+ (conn->proxy_ntlm_state == NTLMSTATE_TYPE2)))) {
+ infof(data, "Connection closure while negotiating auth (HTTP 1.0?)");
+ data->state.authproblem = TRUE;
+ }
+#endif
+#if defined(USE_SPNEGO)
+ if(conn->bits.close &&
+ (((data->req.httpcode == 401) &&
+ (conn->http_negotiate_state == GSS_AUTHRECV)) ||
+ ((data->req.httpcode == 407) &&
+ (conn->proxy_negotiate_state == GSS_AUTHRECV)))) {
+ infof(data, "Connection closure while negotiating auth (HTTP 1.0?)");
+ data->state.authproblem = TRUE;
+ }
+ if((conn->http_negotiate_state == GSS_AUTHDONE) &&
+ (data->req.httpcode != 401)) {
+ conn->http_negotiate_state = GSS_AUTHSUCC;
+ }
+ if((conn->proxy_negotiate_state == GSS_AUTHDONE) &&
+ (data->req.httpcode != 407)) {
+ conn->proxy_negotiate_state = GSS_AUTHSUCC;
+ }
+#endif
+
+ /* now, only output this if the header AND body are requested:
+ */
+ writetype = CLIENTWRITE_HEADER |
+ (data->set.include_header ? CLIENTWRITE_BODY : 0) |
+ ((k->httpcode/100 == 1) ? CLIENTWRITE_1XX : 0);
+
+ headerlen = Curl_dyn_len(&data->state.headerb);
+ result = Curl_client_write(data, writetype,
+ Curl_dyn_ptr(&data->state.headerb),
+ headerlen);
+ if(result)
+ return result;
+
+ data->info.header_size += (long)headerlen;
+ data->req.headerbytecount += (long)headerlen;
+
+ /*
+ * When all the headers have been parsed, see if we should give
+ * up and return an error.
+ */
+ if(http_should_fail(data)) {
+ failf(data, "The requested URL returned error: %d",
+ k->httpcode);
+ return CURLE_HTTP_RETURNED_ERROR;
+ }
+
+#ifdef USE_WEBSOCKETS
+ /* All non-101 HTTP status codes are bad when wanting to upgrade to
+ websockets */
+ if(data->req.upgr101 == UPGR101_WS) {
+ failf(data, "Refused WebSockets upgrade: %d", k->httpcode);
+ return CURLE_HTTP_RETURNED_ERROR;
+ }
+#endif
+
+
+ data->req.deductheadercount =
+ (100 <= k->httpcode && 199 >= k->httpcode)?data->req.headerbytecount:0;
+
+ /* Curl_http_auth_act() checks what authentication methods
+ * that are available and decides which one (if any) to
+ * use. It will set 'newurl' if an auth method was picked. */
+ result = Curl_http_auth_act(data);
+
+ if(result)
+ return result;
+
+ if(k->httpcode >= 300) {
+ if((!conn->bits.authneg) && !conn->bits.close &&
+ !data->state.rewindbeforesend) {
+ /*
+ * General treatment of errors when about to send data. Including :
+ * "417 Expectation Failed", while waiting for 100-continue.
+ *
+ * The check for close above is done simply because of something
+ * else has already deemed the connection to get closed then
+ * something else should've considered the big picture and we
+ * avoid this check.
+ *
+ * rewindbeforesend indicates that something has told libcurl to
+ * continue sending even if it gets discarded
+ */
+
+ switch(data->state.httpreq) {
+ case HTTPREQ_PUT:
+ case HTTPREQ_POST:
+ case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
+ /* We got an error response. If this happened before the whole
+ * request body has been sent we stop sending and mark the
+ * connection for closure after we've read the entire response.
+ */
+ Curl_expire_done(data, EXPIRE_100_TIMEOUT);
+ if(!k->upload_done) {
+ if((k->httpcode == 417) && data->state.expect100header) {
+ /* 417 Expectation Failed - try again without the Expect
+ header */
+ infof(data, "Got 417 while waiting for a 100");
+ data->state.disableexpect = TRUE;
+ DEBUGASSERT(!data->req.newurl);
+ data->req.newurl = strdup(data->state.url);
+ Curl_done_sending(data, k);
+ }
+ else if(data->set.http_keep_sending_on_error) {
+ infof(data, "HTTP error before end of send, keep sending");
+ if(k->exp100 > EXP100_SEND_DATA) {
+ k->exp100 = EXP100_SEND_DATA;
+ k->keepon |= KEEP_SEND;
+ }
+ }
+ else {
+ infof(data, "HTTP error before end of send, stop sending");
+ streamclose(conn, "Stop sending data before everything sent");
+ result = Curl_done_sending(data, k);
+ if(result)
+ return result;
+ k->upload_done = TRUE;
+ if(data->state.expect100header)
+ k->exp100 = EXP100_FAILED;
+ }
+ }
+ break;
+
+ default: /* default label present to avoid compiler warnings */
+ break;
+ }
+ }
+
+ if(data->state.rewindbeforesend &&
+ (conn->writesockfd != CURL_SOCKET_BAD)) {
+ /* We rewind before next send, continue sending now */
+ infof(data, "Keep sending data to get tossed away");
+ k->keepon |= KEEP_SEND;
+ }
+ }
+
+ if(!k->header) {
+ /*
+ * really end-of-headers.
+ *
+ * If we requested a "no body", this is a good time to get
+ * out and return home.
+ */
+ if(data->req.no_body)
+ *stop_reading = TRUE;
+#ifndef CURL_DISABLE_RTSP
+ else if((conn->handler->protocol & CURLPROTO_RTSP) &&
+ (data->set.rtspreq == RTSPREQ_DESCRIBE) &&
+ (k->size <= -1))
+ /* Respect section 4.4 of rfc2326: If the Content-Length header is
+ absent, a length 0 must be assumed. It will prevent libcurl from
+ hanging on DESCRIBE request that got refused for whatever
+ reason */
+ *stop_reading = TRUE;
+#endif
+
+ /* If max download size is *zero* (nothing) we already have
+ nothing and can safely return ok now! But for HTTP/2, we'd
+ like to call http2_handle_stream_close to properly close a
+ stream. In order to do this, we keep reading until we
+ close the stream. */
+ if(0 == k->maxdownload
+#if defined(USE_NGHTTP2)
+ && !((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
+ conn->httpversion == 20)
+#endif
+ )
+ *stop_reading = TRUE;
+
+ if(*stop_reading) {
+ /* we make sure that this socket isn't read more now */
+ k->keepon &= ~KEEP_RECV;
+ }
+
+ Curl_debug(data, CURLINFO_HEADER_IN, str_start, headerlen);
+ break; /* exit header line loop */
+ }
+
+ /* We continue reading headers, reset the line-based header */
+ Curl_dyn_reset(&data->state.headerb);
+ continue;
+ }
+
+ /*
+ * Checks for special headers coming up.
+ */
+
+ writetype = CLIENTWRITE_HEADER;
+ if(!k->headerline++) {
+ /* This is the first header, it MUST be the error code line
+ or else we consider this to be the body right away! */
+ int httpversion_major;
+ int rtspversion_major;
+ int nc = 0;
+#define HEADER1 headp /* no conversion needed, just use headp */
+
+ if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
+ /*
+ * https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2
+ *
+ * The response code is always a three-digit number in HTTP as the spec
+ * says. We allow any three-digit number here, but we cannot make
+ * guarantees on future behaviors since it isn't within the protocol.
+ */
+ char separator;
+ char twoorthree[2];
+ int httpversion = 0;
+ char digit4 = 0;
+ nc = sscanf(HEADER1,
+ " HTTP/%1d.%1d%c%3d%c",
+ &httpversion_major,
+ &httpversion,
+ &separator,
+ &k->httpcode,
+ &digit4);
+
+ if(nc == 1 && httpversion_major >= 2 &&
+ 2 == sscanf(HEADER1, " HTTP/%1[23] %d", twoorthree, &k->httpcode)) {
+ conn->httpversion = 0;
+ nc = 4;
+ separator = ' ';
+ }
+
+ /* There can only be a 4th response code digit stored in 'digit4' if
+ all the other fields were parsed and stored first, so nc is 5 when
+ digit4 a digit.
+
+ The sscanf() line above will also allow zero-prefixed and negative
+ numbers, so we check for that too here.
+ */
+ else if(ISDIGIT(digit4) || (nc >= 4 && k->httpcode < 100)) {
+ failf(data, "Unsupported response code in HTTP response");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
+
+ if((nc >= 4) && (' ' == separator)) {
+ httpversion += 10 * httpversion_major;
+ switch(httpversion) {
+ case 10:
+ case 11:
+#ifdef USE_HTTP2
+ case 20:
+#endif
+#ifdef ENABLE_QUIC
+ case 30:
+#endif
+ conn->httpversion = (unsigned char)httpversion;
+ break;
+ default:
+ failf(data, "Unsupported HTTP version (%u.%d) in response",
+ httpversion/10, httpversion%10);
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
+
+ if(k->upgr101 == UPGR101_RECEIVED) {
+ /* supposedly upgraded to http2 now */
+ if(conn->httpversion != 20)
+ infof(data, "Lying server, not serving HTTP/2");
+ }
+ if(conn->httpversion < 20) {
+ conn->bundle->multiuse = BUNDLE_NO_MULTIUSE;
+ infof(data, "Mark bundle as not supporting multiuse");
+ }
+ }
+ else if(!nc) {
+ /* this is the real world, not a Nirvana
+ NCSA 1.5.x returns this crap when asked for HTTP/1.1
+ */
+ nc = sscanf(HEADER1, " HTTP %3d", &k->httpcode);
+ conn->httpversion = 10;
+
+ /* If user has set option HTTP200ALIASES,
+ compare header line against list of aliases
+ */
+ if(!nc) {
+ statusline check =
+ checkhttpprefix(data,
+ Curl_dyn_ptr(&data->state.headerb),
+ Curl_dyn_len(&data->state.headerb));
+ if(check == STATUS_DONE) {
+ nc = 1;
+ k->httpcode = 200;
+ conn->httpversion = 10;
+ }
+ }
+ }
+ else {
+ failf(data, "Unsupported HTTP version in response");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
+ }
+ else if(conn->handler->protocol & CURLPROTO_RTSP) {
+ char separator;
+ int rtspversion;
+ nc = sscanf(HEADER1,
+ " RTSP/%1d.%1d%c%3d",
+ &rtspversion_major,
+ &rtspversion,
+ &separator,
+ &k->httpcode);
+ if((nc == 4) && (' ' == separator)) {
+ conn->httpversion = 11; /* For us, RTSP acts like HTTP 1.1 */
+ }
+ else {
+ nc = 0;
+ }
+ }
+
+ if(nc) {
+ result = Curl_http_statusline(data, conn);
+ if(result)
+ return result;
+ writetype |= CLIENTWRITE_STATUS;
+ }
+ else {
+ k->header = FALSE; /* this is not a header line */
+ break;
+ }
+ }
+
+ result = verify_header(data);
+ if(result)
+ return result;
+
+ result = Curl_http_header(data, conn, headp);
+ if(result)
+ return result;
+
+ /*
+ * End of header-checks. Write them to the client.
+ */
+ if(data->set.include_header)
+ writetype |= CLIENTWRITE_BODY;
+ if(k->httpcode/100 == 1)
+ writetype |= CLIENTWRITE_1XX;
+
+ Curl_debug(data, CURLINFO_HEADER_IN, headp,
+ Curl_dyn_len(&data->state.headerb));
+
+ result = Curl_client_write(data, writetype, headp,
+ Curl_dyn_len(&data->state.headerb));
+ if(result)
+ return result;
+
+ data->info.header_size += Curl_dyn_len(&data->state.headerb);
+ data->req.headerbytecount += Curl_dyn_len(&data->state.headerb);
+
+ Curl_dyn_reset(&data->state.headerb);
+ }
+ while(*k->str); /* header line within buffer */
+
+ /* We might have reached the end of the header part here, but
+ there might be a non-header part left in the end of the read
+ buffer. */
+
+ return CURLE_OK;
+}
+
+#endif /* CURL_DISABLE_HTTP */
diff --git a/libs/libcurl/src/http.h b/libs/libcurl/src/http.h index f7cbb34244..ff3c44bcca 100644 --- a/libs/libcurl/src/http.h +++ b/libs/libcurl/src/http.h @@ -1,384 +1,384 @@ -#ifndef HEADER_CURL_HTTP_H -#define HEADER_CURL_HTTP_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "ws.h" - -typedef enum { - HTTPREQ_GET, - HTTPREQ_POST, - HTTPREQ_POST_FORM, /* we make a difference internally */ - HTTPREQ_POST_MIME, /* we make a difference internally */ - HTTPREQ_PUT, - HTTPREQ_HEAD -} Curl_HttpReq; - -#ifndef CURL_DISABLE_HTTP - -#ifdef USE_NGHTTP2 -#include <nghttp2/nghttp2.h> -#endif - -#if defined(_WIN32) && defined(ENABLE_QUIC) -#include <stdint.h> -#endif - -extern const struct Curl_handler Curl_handler_http; - -#ifdef USE_SSL -extern const struct Curl_handler Curl_handler_https; -#endif - -#ifdef USE_WEBSOCKETS -extern const struct Curl_handler Curl_handler_ws; - -#ifdef USE_SSL -extern const struct Curl_handler Curl_handler_wss; -#endif -#endif /* websockets */ - - -/* Header specific functions */ -bool Curl_compareheader(const char *headerline, /* line to check */ - const char *header, /* header keyword _with_ colon */ - const size_t hlen, /* len of the keyword in bytes */ - const char *content, /* content string to find */ - const size_t clen); /* len of the content in bytes */ - -char *Curl_copy_header_value(const char *header); - -char *Curl_checkProxyheaders(struct Curl_easy *data, - const struct connectdata *conn, - const char *thisheader, - const size_t thislen); -CURLcode Curl_buffer_send(struct dynbuf *in, - struct Curl_easy *data, - curl_off_t *bytes_written, - curl_off_t included_body_bytes, - int socketindex); - -CURLcode Curl_add_timecondition(struct Curl_easy *data, -#ifndef USE_HYPER - struct dynbuf *req -#else - void *headers -#endif - ); -CURLcode Curl_add_custom_headers(struct Curl_easy *data, - bool is_connect, -#ifndef USE_HYPER - struct dynbuf *req -#else - void *headers -#endif - ); -CURLcode Curl_http_compile_trailers(struct curl_slist *trailers, - struct dynbuf *buf, - struct Curl_easy *handle); - -void Curl_http_method(struct Curl_easy *data, struct connectdata *conn, - const char **method, Curl_HttpReq *); -CURLcode Curl_http_useragent(struct Curl_easy *data); -CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn); -CURLcode Curl_http_target(struct Curl_easy *data, struct connectdata *conn, - struct dynbuf *req); -CURLcode Curl_http_statusline(struct Curl_easy *data, - struct connectdata *conn); -CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, - char *headp); -CURLcode Curl_transferencode(struct Curl_easy *data); -CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn, - Curl_HttpReq httpreq, - const char **teep); -CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn, - struct dynbuf *r, Curl_HttpReq httpreq); -bool Curl_use_http_1_1plus(const struct Curl_easy *data, - const struct connectdata *conn); -#ifndef CURL_DISABLE_COOKIES -CURLcode Curl_http_cookies(struct Curl_easy *data, - struct connectdata *conn, - struct dynbuf *r); -#else -#define Curl_http_cookies(a,b,c) CURLE_OK -#endif -CURLcode Curl_http_resume(struct Curl_easy *data, - struct connectdata *conn, - Curl_HttpReq httpreq); -CURLcode Curl_http_range(struct Curl_easy *data, - Curl_HttpReq httpreq); -CURLcode Curl_http_firstwrite(struct Curl_easy *data, - struct connectdata *conn, - bool *done); - -/* protocol-specific functions set up to be called by the main engine */ -CURLcode Curl_http(struct Curl_easy *data, bool *done); -CURLcode Curl_http_done(struct Curl_easy *data, CURLcode, bool premature); -CURLcode Curl_http_connect(struct Curl_easy *data, bool *done); - -/* These functions are in http.c */ -CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, - const char *auth); -CURLcode Curl_http_auth_act(struct Curl_easy *data); - -/* If only the PICKNONE bit is set, there has been a round-trip and we - selected to use no auth at all. Ie, we actively select no auth, as opposed - to not having one selected. The other CURLAUTH_* defines are present in the - public curl/curl.h header. */ -#define CURLAUTH_PICKNONE (1<<30) /* don't use auth */ - -/* MAX_INITIAL_POST_SIZE indicates the number of bytes that will make the POST - data get included in the initial data chunk sent to the server. If the - data is larger than this, it will automatically get split up in multiple - system calls. - - This value used to be fairly big (100K), but we must take into account that - if the server rejects the POST due for authentication reasons, this data - will always be unconditionally sent and thus it may not be larger than can - always be afforded to send twice. - - It must not be greater than 64K to work on VMS. -*/ -#ifndef MAX_INITIAL_POST_SIZE -#define MAX_INITIAL_POST_SIZE (64*1024) -#endif - -/* EXPECT_100_THRESHOLD is the request body size limit for when libcurl will - * automatically add an "Expect: 100-continue" header in HTTP requests. When - * the size is unknown, it will always add it. - * - */ -#ifndef EXPECT_100_THRESHOLD -#define EXPECT_100_THRESHOLD (1024*1024) -#endif - -#endif /* CURL_DISABLE_HTTP */ - -#ifdef USE_NGHTTP3 -struct h3out; /* see ngtcp2 */ -#endif - -#ifdef USE_MSH3 -#ifdef _WIN32 -#define msh3_lock CRITICAL_SECTION -#define msh3_lock_initialize(lock) InitializeCriticalSection(lock) -#define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock) -#define msh3_lock_acquire(lock) EnterCriticalSection(lock) -#define msh3_lock_release(lock) LeaveCriticalSection(lock) -#else /* !_WIN32 */ -#include <pthread.h> -#define msh3_lock pthread_mutex_t -#define msh3_lock_initialize(lock) { \ - pthread_mutexattr_t attr; \ - pthread_mutexattr_init(&attr); \ - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \ - pthread_mutex_init(lock, &attr); \ - pthread_mutexattr_destroy(&attr); \ -} -#define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock) -#define msh3_lock_acquire(lock) pthread_mutex_lock(lock) -#define msh3_lock_release(lock) pthread_mutex_unlock(lock) -#endif /* _WIN32 */ -#endif /* USE_MSH3 */ - -/**************************************************************************** - * HTTP unique setup - ***************************************************************************/ -struct HTTP { - curl_mimepart *sendit; - curl_off_t postsize; /* off_t to handle large file sizes */ - const char *postdata; - - const char *p_pragma; /* Pragma: string */ - - /* For FORM posting */ - curl_mimepart form; - - struct back { - curl_read_callback fread_func; /* backup storage for fread pointer */ - void *fread_in; /* backup storage for fread_in pointer */ - const char *postdata; - curl_off_t postsize; - } backup; - - enum { - HTTPSEND_NADA, /* init */ - HTTPSEND_REQUEST, /* sending a request */ - HTTPSEND_BODY /* sending body */ - } sending; - -#ifdef USE_WEBSOCKETS - struct websocket ws; -#endif - -#ifndef CURL_DISABLE_HTTP - struct dynbuf send_buffer; /* used if the request couldn't be sent in one - chunk, points to an allocated send_buffer - struct */ -#endif -#ifdef USE_NGHTTP2 - /*********** for HTTP/2 we store stream-local data here *************/ - int32_t stream_id; /* stream we are interested in */ - - /* We store non-final and final response headers here, per-stream */ - struct dynbuf header_recvbuf; - size_t nread_header_recvbuf; /* number of bytes in header_recvbuf fed into - upper layer */ - struct dynbuf trailer_recvbuf; - const uint8_t *pausedata; /* pointer to data received in on_data_chunk */ - size_t pauselen; /* the number of bytes left in data */ - bool close_handled; /* TRUE if stream closure is handled by libcurl */ - - char **push_headers; /* allocated array */ - size_t push_headers_used; /* number of entries filled in */ - size_t push_headers_alloc; /* number of entries allocated */ - uint32_t error; /* HTTP/2 stream error code */ -#endif -#if defined(USE_NGHTTP2) || defined(USE_NGHTTP3) - bool bodystarted; - int status_code; /* HTTP status code */ - bool closed; /* TRUE on HTTP2 stream close */ - char *mem; /* points to a buffer in memory to store received data */ - size_t len; /* size of the buffer 'mem' points to */ - size_t memlen; /* size of data copied to mem */ -#endif -#if defined(USE_NGHTTP2) || defined(ENABLE_QUIC) - /* fields used by both HTTP/2 and HTTP/3 */ - const uint8_t *upload_mem; /* points to a buffer to read from */ - size_t upload_len; /* size of the buffer 'upload_mem' points to */ - curl_off_t upload_left; /* number of bytes left to upload */ -#endif - -#ifdef ENABLE_QUIC -#ifndef USE_MSH3 - /*********** for HTTP/3 we store stream-local data here *************/ - int64_t stream3_id; /* stream we are interested in */ - uint64_t error3; /* HTTP/3 stream error code */ - bool firstheader; /* FALSE until headers arrive */ - bool firstbody; /* FALSE until body arrives */ - bool h3req; /* FALSE until request is issued */ -#endif - bool upload_done; -#endif -#ifdef USE_NGHTTP3 - size_t unacked_window; - struct h3out *h3out; /* per-stream buffers for upload */ - struct dynbuf overflow; /* excess data received during a single Curl_read */ -#endif -#ifdef USE_MSH3 - struct MSH3_REQUEST *req; - msh3_lock recv_lock; - /* Receive Buffer (Headers and Data) */ - uint8_t* recv_buf; - size_t recv_buf_alloc; - /* Receive Headers */ - size_t recv_header_len; - bool recv_header_complete; - /* Receive Data */ - size_t recv_data_len; - bool recv_data_complete; - /* General Receive Error */ - CURLcode recv_error; -#endif -}; - -#ifdef USE_NGHTTP2 -/* h2 settings for this connection */ -struct h2settings { - uint32_t max_concurrent_streams; - bool enable_push; -}; -#endif - -struct http_conn { -#ifdef USE_NGHTTP2 -#define H2_BINSETTINGS_LEN 80 - uint8_t binsettings[H2_BINSETTINGS_LEN]; - size_t binlen; /* length of the binsettings data */ - - /* We associate the connnectdata struct with the connection, but we need to - make sure we can identify the current "driving" transfer. This is a - work-around for the lack of nghttp2_session_set_user_data() in older - nghttp2 versions that we want to support. (Added in 1.31.0) */ - struct Curl_easy *trnsfr; - - nghttp2_session *h2; - Curl_send *send_underlying; /* underlying send Curl_send callback */ - Curl_recv *recv_underlying; /* underlying recv Curl_recv callback */ - char *inbuf; /* buffer to receive data from underlying socket */ - size_t inbuflen; /* number of bytes filled in inbuf */ - size_t nread_inbuf; /* number of bytes read from in inbuf */ - /* We need separate buffer for transmission and reception because we - may call nghttp2_session_send() after the - nghttp2_session_mem_recv() but mem buffer is still not full. In - this case, we wrongly sends the content of mem buffer if we share - them for both cases. */ - int32_t pause_stream_id; /* stream ID which paused - nghttp2_session_mem_recv */ - size_t drain_total; /* sum of all stream's UrlState.drain */ - - /* this is a hash of all individual streams (Curl_easy structs) */ - struct h2settings settings; - - /* list of settings that will be sent */ - nghttp2_settings_entry local_settings[3]; - size_t local_settings_num; -#else - int unused; /* prevent a compiler warning */ -#endif -}; - -CURLcode Curl_http_size(struct Curl_easy *data); - -CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, - struct connectdata *conn, - ssize_t *nread, - bool *stop_reading); - -/** - * Curl_http_output_auth() setups the authentication headers for the - * host/proxy and the correct authentication - * method. data->state.authdone is set to TRUE when authentication is - * done. - * - * @param data all information about the current transfer - * @param conn all information about the current connection - * @param request pointer to the request keyword - * @param httpreq is the request type - * @param path pointer to the requested path - * @param proxytunnel boolean if this is the request setting up a "proxy - * tunnel" - * - * @returns CURLcode - */ -CURLcode -Curl_http_output_auth(struct Curl_easy *data, - struct connectdata *conn, - const char *request, - Curl_HttpReq httpreq, - const char *path, - bool proxytunnel); /* TRUE if this is the request setting - up the proxy tunnel */ - -#endif /* HEADER_CURL_HTTP_H */ +#ifndef HEADER_CURL_HTTP_H
+#define HEADER_CURL_HTTP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "ws.h"
+
+typedef enum {
+ HTTPREQ_GET,
+ HTTPREQ_POST,
+ HTTPREQ_POST_FORM, /* we make a difference internally */
+ HTTPREQ_POST_MIME, /* we make a difference internally */
+ HTTPREQ_PUT,
+ HTTPREQ_HEAD
+} Curl_HttpReq;
+
+#ifndef CURL_DISABLE_HTTP
+
+#ifdef USE_NGHTTP2
+#include <nghttp2/nghttp2.h>
+#endif
+
+#if defined(_WIN32) && defined(ENABLE_QUIC)
+#include <stdint.h>
+#endif
+
+extern const struct Curl_handler Curl_handler_http;
+
+#ifdef USE_SSL
+extern const struct Curl_handler Curl_handler_https;
+#endif
+
+#ifdef USE_WEBSOCKETS
+extern const struct Curl_handler Curl_handler_ws;
+
+#ifdef USE_SSL
+extern const struct Curl_handler Curl_handler_wss;
+#endif
+#endif /* websockets */
+
+
+/* Header specific functions */
+bool Curl_compareheader(const char *headerline, /* line to check */
+ const char *header, /* header keyword _with_ colon */
+ const size_t hlen, /* len of the keyword in bytes */
+ const char *content, /* content string to find */
+ const size_t clen); /* len of the content in bytes */
+
+char *Curl_copy_header_value(const char *header);
+
+char *Curl_checkProxyheaders(struct Curl_easy *data,
+ const struct connectdata *conn,
+ const char *thisheader,
+ const size_t thislen);
+CURLcode Curl_buffer_send(struct dynbuf *in,
+ struct Curl_easy *data,
+ curl_off_t *bytes_written,
+ curl_off_t included_body_bytes,
+ int socketindex);
+
+CURLcode Curl_add_timecondition(struct Curl_easy *data,
+#ifndef USE_HYPER
+ struct dynbuf *req
+#else
+ void *headers
+#endif
+ );
+CURLcode Curl_add_custom_headers(struct Curl_easy *data,
+ bool is_connect,
+#ifndef USE_HYPER
+ struct dynbuf *req
+#else
+ void *headers
+#endif
+ );
+CURLcode Curl_http_compile_trailers(struct curl_slist *trailers,
+ struct dynbuf *buf,
+ struct Curl_easy *handle);
+
+void Curl_http_method(struct Curl_easy *data, struct connectdata *conn,
+ const char **method, Curl_HttpReq *);
+CURLcode Curl_http_useragent(struct Curl_easy *data);
+CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn);
+CURLcode Curl_http_target(struct Curl_easy *data, struct connectdata *conn,
+ struct dynbuf *req);
+CURLcode Curl_http_statusline(struct Curl_easy *data,
+ struct connectdata *conn);
+CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
+ char *headp);
+CURLcode Curl_transferencode(struct Curl_easy *data);
+CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn,
+ Curl_HttpReq httpreq,
+ const char **teep);
+CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
+ struct dynbuf *r, Curl_HttpReq httpreq);
+bool Curl_use_http_1_1plus(const struct Curl_easy *data,
+ const struct connectdata *conn);
+#ifndef CURL_DISABLE_COOKIES
+CURLcode Curl_http_cookies(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct dynbuf *r);
+#else
+#define Curl_http_cookies(a,b,c) CURLE_OK
+#endif
+CURLcode Curl_http_resume(struct Curl_easy *data,
+ struct connectdata *conn,
+ Curl_HttpReq httpreq);
+CURLcode Curl_http_range(struct Curl_easy *data,
+ Curl_HttpReq httpreq);
+CURLcode Curl_http_firstwrite(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool *done);
+
+/* protocol-specific functions set up to be called by the main engine */
+CURLcode Curl_http(struct Curl_easy *data, bool *done);
+CURLcode Curl_http_done(struct Curl_easy *data, CURLcode, bool premature);
+CURLcode Curl_http_connect(struct Curl_easy *data, bool *done);
+
+/* These functions are in http.c */
+CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
+ const char *auth);
+CURLcode Curl_http_auth_act(struct Curl_easy *data);
+
+/* If only the PICKNONE bit is set, there has been a round-trip and we
+ selected to use no auth at all. Ie, we actively select no auth, as opposed
+ to not having one selected. The other CURLAUTH_* defines are present in the
+ public curl/curl.h header. */
+#define CURLAUTH_PICKNONE (1<<30) /* don't use auth */
+
+/* MAX_INITIAL_POST_SIZE indicates the number of bytes that will make the POST
+ data get included in the initial data chunk sent to the server. If the
+ data is larger than this, it will automatically get split up in multiple
+ system calls.
+
+ This value used to be fairly big (100K), but we must take into account that
+ if the server rejects the POST due for authentication reasons, this data
+ will always be unconditionally sent and thus it may not be larger than can
+ always be afforded to send twice.
+
+ It must not be greater than 64K to work on VMS.
+*/
+#ifndef MAX_INITIAL_POST_SIZE
+#define MAX_INITIAL_POST_SIZE (64*1024)
+#endif
+
+/* EXPECT_100_THRESHOLD is the request body size limit for when libcurl will
+ * automatically add an "Expect: 100-continue" header in HTTP requests. When
+ * the size is unknown, it will always add it.
+ *
+ */
+#ifndef EXPECT_100_THRESHOLD
+#define EXPECT_100_THRESHOLD (1024*1024)
+#endif
+
+#endif /* CURL_DISABLE_HTTP */
+
+#ifdef USE_NGHTTP3
+struct h3out; /* see ngtcp2 */
+#endif
+
+#ifdef USE_MSH3
+#ifdef _WIN32
+#define msh3_lock CRITICAL_SECTION
+#define msh3_lock_initialize(lock) InitializeCriticalSection(lock)
+#define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock)
+#define msh3_lock_acquire(lock) EnterCriticalSection(lock)
+#define msh3_lock_release(lock) LeaveCriticalSection(lock)
+#else /* !_WIN32 */
+#include <pthread.h>
+#define msh3_lock pthread_mutex_t
+#define msh3_lock_initialize(lock) { \
+ pthread_mutexattr_t attr; \
+ pthread_mutexattr_init(&attr); \
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
+ pthread_mutex_init(lock, &attr); \
+ pthread_mutexattr_destroy(&attr); \
+}
+#define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock)
+#define msh3_lock_acquire(lock) pthread_mutex_lock(lock)
+#define msh3_lock_release(lock) pthread_mutex_unlock(lock)
+#endif /* _WIN32 */
+#endif /* USE_MSH3 */
+
+/****************************************************************************
+ * HTTP unique setup
+ ***************************************************************************/
+struct HTTP {
+ curl_mimepart *sendit;
+ curl_off_t postsize; /* off_t to handle large file sizes */
+ const char *postdata;
+
+ const char *p_pragma; /* Pragma: string */
+
+ /* For FORM posting */
+ curl_mimepart form;
+
+ struct back {
+ curl_read_callback fread_func; /* backup storage for fread pointer */
+ void *fread_in; /* backup storage for fread_in pointer */
+ const char *postdata;
+ curl_off_t postsize;
+ } backup;
+
+ enum {
+ HTTPSEND_NADA, /* init */
+ HTTPSEND_REQUEST, /* sending a request */
+ HTTPSEND_BODY /* sending body */
+ } sending;
+
+#ifdef USE_WEBSOCKETS
+ struct websocket ws;
+#endif
+
+#ifndef CURL_DISABLE_HTTP
+ struct dynbuf send_buffer; /* used if the request couldn't be sent in one
+ chunk, points to an allocated send_buffer
+ struct */
+#endif
+#ifdef USE_NGHTTP2
+ /*********** for HTTP/2 we store stream-local data here *************/
+ int32_t stream_id; /* stream we are interested in */
+
+ /* We store non-final and final response headers here, per-stream */
+ struct dynbuf header_recvbuf;
+ size_t nread_header_recvbuf; /* number of bytes in header_recvbuf fed into
+ upper layer */
+ struct dynbuf trailer_recvbuf;
+ const uint8_t *pausedata; /* pointer to data received in on_data_chunk */
+ size_t pauselen; /* the number of bytes left in data */
+ bool close_handled; /* TRUE if stream closure is handled by libcurl */
+
+ char **push_headers; /* allocated array */
+ size_t push_headers_used; /* number of entries filled in */
+ size_t push_headers_alloc; /* number of entries allocated */
+ uint32_t error; /* HTTP/2 stream error code */
+#endif
+#if defined(USE_NGHTTP2) || defined(USE_NGHTTP3)
+ bool bodystarted;
+ int status_code; /* HTTP status code */
+ bool closed; /* TRUE on HTTP2 stream close */
+ char *mem; /* points to a buffer in memory to store received data */
+ size_t len; /* size of the buffer 'mem' points to */
+ size_t memlen; /* size of data copied to mem */
+#endif
+#if defined(USE_NGHTTP2) || defined(ENABLE_QUIC)
+ /* fields used by both HTTP/2 and HTTP/3 */
+ const uint8_t *upload_mem; /* points to a buffer to read from */
+ size_t upload_len; /* size of the buffer 'upload_mem' points to */
+ curl_off_t upload_left; /* number of bytes left to upload */
+#endif
+
+#ifdef ENABLE_QUIC
+#ifndef USE_MSH3
+ /*********** for HTTP/3 we store stream-local data here *************/
+ int64_t stream3_id; /* stream we are interested in */
+ uint64_t error3; /* HTTP/3 stream error code */
+ bool firstheader; /* FALSE until headers arrive */
+ bool firstbody; /* FALSE until body arrives */
+ bool h3req; /* FALSE until request is issued */
+#endif
+ bool upload_done;
+#endif
+#ifdef USE_NGHTTP3
+ size_t unacked_window;
+ struct h3out *h3out; /* per-stream buffers for upload */
+ struct dynbuf overflow; /* excess data received during a single Curl_read */
+#endif
+#ifdef USE_MSH3
+ struct MSH3_REQUEST *req;
+ msh3_lock recv_lock;
+ /* Receive Buffer (Headers and Data) */
+ uint8_t* recv_buf;
+ size_t recv_buf_alloc;
+ /* Receive Headers */
+ size_t recv_header_len;
+ bool recv_header_complete;
+ /* Receive Data */
+ size_t recv_data_len;
+ bool recv_data_complete;
+ /* General Receive Error */
+ CURLcode recv_error;
+#endif
+};
+
+#ifdef USE_NGHTTP2
+/* h2 settings for this connection */
+struct h2settings {
+ uint32_t max_concurrent_streams;
+ bool enable_push;
+};
+#endif
+
+struct http_conn {
+#ifdef USE_NGHTTP2
+#define H2_BINSETTINGS_LEN 80
+ uint8_t binsettings[H2_BINSETTINGS_LEN];
+ size_t binlen; /* length of the binsettings data */
+
+ /* We associate the connectdata struct with the connection, but we need to
+ make sure we can identify the current "driving" transfer. This is a
+ work-around for the lack of nghttp2_session_set_user_data() in older
+ nghttp2 versions that we want to support. (Added in 1.31.0) */
+ struct Curl_easy *trnsfr;
+
+ nghttp2_session *h2;
+ Curl_send *send_underlying; /* underlying send Curl_send callback */
+ Curl_recv *recv_underlying; /* underlying recv Curl_recv callback */
+ char *inbuf; /* buffer to receive data from underlying socket */
+ size_t inbuflen; /* number of bytes filled in inbuf */
+ size_t nread_inbuf; /* number of bytes read from in inbuf */
+ /* We need separate buffer for transmission and reception because we
+ may call nghttp2_session_send() after the
+ nghttp2_session_mem_recv() but mem buffer is still not full. In
+ this case, we wrongly sends the content of mem buffer if we share
+ them for both cases. */
+ int32_t pause_stream_id; /* stream ID which paused
+ nghttp2_session_mem_recv */
+ size_t drain_total; /* sum of all stream's UrlState.drain */
+
+ /* this is a hash of all individual streams (Curl_easy structs) */
+ struct h2settings settings;
+
+ /* list of settings that will be sent */
+ nghttp2_settings_entry local_settings[3];
+ size_t local_settings_num;
+#else
+ int unused; /* prevent a compiler warning */
+#endif
+};
+
+CURLcode Curl_http_size(struct Curl_easy *data);
+
+CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
+ struct connectdata *conn,
+ ssize_t *nread,
+ bool *stop_reading);
+
+/**
+ * Curl_http_output_auth() setups the authentication headers for the
+ * host/proxy and the correct authentication
+ * method. data->state.authdone is set to TRUE when authentication is
+ * done.
+ *
+ * @param data all information about the current transfer
+ * @param conn all information about the current connection
+ * @param request pointer to the request keyword
+ * @param httpreq is the request type
+ * @param path pointer to the requested path
+ * @param proxytunnel boolean if this is the request setting up a "proxy
+ * tunnel"
+ *
+ * @returns CURLcode
+ */
+CURLcode
+Curl_http_output_auth(struct Curl_easy *data,
+ struct connectdata *conn,
+ const char *request,
+ Curl_HttpReq httpreq,
+ const char *path,
+ bool proxytunnel); /* TRUE if this is the request setting
+ up the proxy tunnel */
+
+#endif /* HEADER_CURL_HTTP_H */
diff --git a/libs/libcurl/src/http2.c b/libs/libcurl/src/http2.c index b7409b027d..26829729d6 100644 --- a/libs/libcurl/src/http2.c +++ b/libs/libcurl/src/http2.c @@ -1,2337 +1,2337 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_NGHTTP2 -#include <nghttp2/nghttp2.h> -#include "urldata.h" -#include "http2.h" -#include "http.h" -#include "sendf.h" -#include "select.h" -#include "curl_base64.h" -#include "strcase.h" -#include "multiif.h" -#include "url.h" -#include "connect.h" -#include "strtoofft.h" -#include "strdup.h" -#include "transfer.h" -#include "dynbuf.h" -#include "h2h3.h" -#include "headers.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#define H2_BUFSIZE 32768 - -#if (NGHTTP2_VERSION_NUM < 0x010c00) -#error too old nghttp2 version, upgrade! -#endif - -#ifdef CURL_DISABLE_VERBOSE_STRINGS -#define nghttp2_session_callbacks_set_error_callback(x,y) -#endif - -#if (NGHTTP2_VERSION_NUM >= 0x010c00) -#define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1 -#endif - -#define HTTP2_HUGE_WINDOW_SIZE (32 * 1024 * 1024) /* 32 MB */ - -#ifdef DEBUG_HTTP2 -#define H2BUGF(x) x -#else -#define H2BUGF(x) do { } while(0) -#endif - -static ssize_t http2_recv(struct Curl_easy *data, int sockindex, - char *mem, size_t len, CURLcode *err); -static bool http2_connisdead(struct Curl_easy *data, - struct connectdata *conn); -static int h2_session_send(struct Curl_easy *data, - nghttp2_session *h2); -static int h2_process_pending_input(struct Curl_easy *data, - struct http_conn *httpc, - CURLcode *err); - -/* - * Curl_http2_init_state() is called when the easy handle is created and - * allows for HTTP/2 specific init of state. - */ -void Curl_http2_init_state(struct UrlState *state) -{ - state->stream_weight = NGHTTP2_DEFAULT_WEIGHT; -} - -/* - * Curl_http2_init_userset() is called when the easy handle is created and - * allows for HTTP/2 specific user-set fields. - */ -void Curl_http2_init_userset(struct UserDefined *set) -{ - set->stream_weight = NGHTTP2_DEFAULT_WEIGHT; -} - -static int http2_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *sock) -{ - const struct http_conn *c = &conn->proto.httpc; - struct SingleRequest *k = &data->req; - int bitmap = GETSOCK_BLANK; - struct HTTP *stream = data->req.p.http; - - sock[0] = conn->sock[FIRSTSOCKET]; - - if(!(k->keepon & KEEP_RECV_PAUSE)) - /* Unless paused - in a HTTP/2 connection we can basically always get a - frame so we should always be ready for one */ - bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); - - /* we're (still uploading OR the HTTP/2 layer wants to send data) AND - there's a window to send data in */ - if((((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) || - nghttp2_session_want_write(c->h2)) && - (nghttp2_session_get_remote_window_size(c->h2) && - nghttp2_session_get_stream_remote_window_size(c->h2, - stream->stream_id))) - bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); - - return bitmap; -} - -/* - * http2_stream_free() free HTTP2 stream related data - */ -static void http2_stream_free(struct HTTP *http) -{ - if(http) { - Curl_dyn_free(&http->header_recvbuf); - for(; http->push_headers_used > 0; --http->push_headers_used) { - free(http->push_headers[http->push_headers_used - 1]); - } - free(http->push_headers); - http->push_headers = NULL; - } -} - -/* - * Disconnects *a* connection used for HTTP/2. It might be an old one from the - * connection cache and not the "main" one. Don't touch the easy handle! - */ - -static CURLcode http2_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection) -{ - struct http_conn *c = &conn->proto.httpc; - (void)dead_connection; -#ifndef DEBUG_HTTP2 - (void)data; -#endif - - H2BUGF(infof(data, "HTTP/2 DISCONNECT starts now")); - - nghttp2_session_del(c->h2); - Curl_safefree(c->inbuf); - - H2BUGF(infof(data, "HTTP/2 DISCONNECT done")); - - return CURLE_OK; -} - -/* - * The server may send us data at any point (e.g. PING frames). Therefore, - * we cannot assume that an HTTP/2 socket is dead just because it is readable. - * - * Instead, if it is readable, run Curl_connalive() to peek at the socket - * and distinguish between closed and data. - */ -static bool http2_connisdead(struct Curl_easy *data, struct connectdata *conn) -{ - int sval; - bool dead = TRUE; - - if(conn->bits.close) - return TRUE; - - sval = SOCKET_READABLE(conn->sock[FIRSTSOCKET], 0); - if(sval == 0) { - /* timeout */ - dead = FALSE; - } - else if(sval & CURL_CSELECT_ERR) { - /* socket is in an error state */ - dead = TRUE; - } - else if(sval & CURL_CSELECT_IN) { - /* readable with no error. could still be closed */ - dead = !Curl_connalive(conn); - if(!dead) { - /* This happens before we've sent off a request and the connection is - not in use by any other transfer, there shouldn't be any data here, - only "protocol frames" */ - CURLcode result; - struct http_conn *httpc = &conn->proto.httpc; - ssize_t nread = -1; - if(httpc->recv_underlying) - /* if called "too early", this pointer isn't setup yet! */ - nread = ((Curl_recv *)httpc->recv_underlying)( - data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result); - if(nread != -1) { - H2BUGF(infof(data, - "%d bytes stray data read before trying h2 connection", - (int)nread)); - httpc->nread_inbuf = 0; - httpc->inbuflen = nread; - if(h2_process_pending_input(data, httpc, &result) < 0) - /* immediate error, considered dead */ - dead = TRUE; - } - else - /* the read failed so let's say this is dead anyway */ - dead = TRUE; - } - } - - return dead; -} - -/* - * Set the transfer that is currently using this HTTP/2 connection. - */ -static void set_transfer(struct http_conn *c, - struct Curl_easy *data) -{ - c->trnsfr = data; -} - -/* - * Get the transfer that is currently using this HTTP/2 connection. - */ -static struct Curl_easy *get_transfer(struct http_conn *c) -{ - DEBUGASSERT(c && c->trnsfr); - return c->trnsfr; -} - -static unsigned int http2_conncheck(struct Curl_easy *data, - struct connectdata *conn, - unsigned int checks_to_perform) -{ - unsigned int ret_val = CONNRESULT_NONE; - struct http_conn *c = &conn->proto.httpc; - int rc; - bool send_frames = false; - - if(checks_to_perform & CONNCHECK_ISDEAD) { - if(http2_connisdead(data, conn)) - ret_val |= CONNRESULT_DEAD; - } - - if(checks_to_perform & CONNCHECK_KEEPALIVE) { - struct curltime now = Curl_now(); - timediff_t elapsed = Curl_timediff(now, conn->keepalive); - - if(elapsed > data->set.upkeep_interval_ms) { - /* Perform an HTTP/2 PING */ - rc = nghttp2_submit_ping(c->h2, 0, ZERO_NULL); - if(!rc) { - /* Successfully added a PING frame to the session. Need to flag this - so the frame is sent. */ - send_frames = true; - } - else { - failf(data, "nghttp2_submit_ping() failed: %s(%d)", - nghttp2_strerror(rc), rc); - } - - conn->keepalive = now; - } - } - - if(send_frames) { - set_transfer(c, data); /* set the transfer */ - rc = nghttp2_session_send(c->h2); - if(rc) - failf(data, "nghttp2_session_send() failed: %s(%d)", - nghttp2_strerror(rc), rc); - } - - return ret_val; -} - -/* called from http_setup_conn */ -void Curl_http2_setup_req(struct Curl_easy *data) -{ - struct HTTP *http = data->req.p.http; - http->bodystarted = FALSE; - http->status_code = -1; - http->pausedata = NULL; - http->pauselen = 0; - http->closed = FALSE; - http->close_handled = FALSE; - http->mem = NULL; - http->len = 0; - http->memlen = 0; - http->error = NGHTTP2_NO_ERROR; -} - -/* called from http_setup_conn */ -void Curl_http2_setup_conn(struct connectdata *conn) -{ - conn->proto.httpc.settings.max_concurrent_streams = - DEFAULT_MAX_CONCURRENT_STREAMS; -} - -/* - * HTTP2 handler interface. This isn't added to the general list of protocols - * but will be used at run-time when the protocol is dynamically switched from - * HTTP to HTTP2. - */ -static const struct Curl_handler Curl_handler_http2 = { - "HTTP", /* scheme */ - ZERO_NULL, /* setup_connection */ - Curl_http, /* do_it */ - Curl_http_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - http2_getsock, /* proto_getsock */ - http2_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - http2_getsock, /* perform_getsock */ - http2_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - http2_conncheck, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_HTTP, /* defport */ - CURLPROTO_HTTP, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_STREAM /* flags */ -}; - -static const struct Curl_handler Curl_handler_http2_ssl = { - "HTTPS", /* scheme */ - ZERO_NULL, /* setup_connection */ - Curl_http, /* do_it */ - Curl_http_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - http2_getsock, /* proto_getsock */ - http2_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - http2_getsock, /* perform_getsock */ - http2_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - http2_conncheck, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_HTTP, /* defport */ - CURLPROTO_HTTPS, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_SSL | PROTOPT_STREAM /* flags */ -}; - -/* - * Store nghttp2 version info in this buffer. - */ -void Curl_http2_ver(char *p, size_t len) -{ - nghttp2_info *h2 = nghttp2_version(0); - (void)msnprintf(p, len, "nghttp2/%s", h2->version_str); -} - -/* - * The implementation of nghttp2_send_callback type. Here we write |data| with - * size |length| to the network and return the number of bytes actually - * written. See the documentation of nghttp2_send_callback for the details. - */ -static ssize_t send_callback(nghttp2_session *h2, - const uint8_t *mem, size_t length, int flags, - void *userp) -{ - struct connectdata *conn = (struct connectdata *)userp; - struct http_conn *c = &conn->proto.httpc; - struct Curl_easy *data = get_transfer(c); - ssize_t written; - CURLcode result = CURLE_OK; - - (void)h2; - (void)flags; - - if(!c->send_underlying) - /* called before setup properly! */ - return NGHTTP2_ERR_CALLBACK_FAILURE; - - written = ((Curl_send*)c->send_underlying)(data, FIRSTSOCKET, - mem, length, &result); - - if(result == CURLE_AGAIN) { - return NGHTTP2_ERR_WOULDBLOCK; - } - - if(written == -1) { - failf(data, "Failed sending HTTP2 data"); - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - if(!written) - return NGHTTP2_ERR_WOULDBLOCK; - - return written; -} - - -/* We pass a pointer to this struct in the push callback, but the contents of - the struct are hidden from the user. */ -struct curl_pushheaders { - struct Curl_easy *data; - const nghttp2_push_promise *frame; -}; - -/* - * push header access function. Only to be used from within the push callback - */ -char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num) -{ - /* Verify that we got a good easy handle in the push header struct, mostly to - detect rubbish input fast(er). */ - if(!h || !GOOD_EASY_HANDLE(h->data)) - return NULL; - else { - struct HTTP *stream = h->data->req.p.http; - if(num < stream->push_headers_used) - return stream->push_headers[num]; - } - return NULL; -} - -/* - * push header access function. Only to be used from within the push callback - */ -char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header) -{ - /* Verify that we got a good easy handle in the push header struct, - mostly to detect rubbish input fast(er). Also empty header name - is just a rubbish too. We have to allow ":" at the beginning of - the header, but header == ":" must be rejected. If we have ':' in - the middle of header, it could be matched in middle of the value, - this is because we do prefix match.*/ - if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] || - !strcmp(header, ":") || strchr(header + 1, ':')) - return NULL; - else { - struct HTTP *stream = h->data->req.p.http; - size_t len = strlen(header); - size_t i; - for(i = 0; i<stream->push_headers_used; i++) { - if(!strncmp(header, stream->push_headers[i], len)) { - /* sub-match, make sure that it is followed by a colon */ - if(stream->push_headers[i][len] != ':') - continue; - return &stream->push_headers[i][len + 1]; - } - } - } - return NULL; -} - -/* - * This specific transfer on this connection has been "drained". - */ -static void drained_transfer(struct Curl_easy *data, - struct http_conn *httpc) -{ - DEBUGASSERT(httpc->drain_total >= data->state.drain); - httpc->drain_total -= data->state.drain; - data->state.drain = 0; -} - -/* - * Mark this transfer to get "drained". - */ -static void drain_this(struct Curl_easy *data, - struct http_conn *httpc) -{ - data->state.drain++; - httpc->drain_total++; - DEBUGASSERT(httpc->drain_total >= data->state.drain); -} - -static struct Curl_easy *duphandle(struct Curl_easy *data) -{ - struct Curl_easy *second = curl_easy_duphandle(data); - if(second) { - /* setup the request struct */ - struct HTTP *http = calloc(1, sizeof(struct HTTP)); - if(!http) { - (void)Curl_close(&second); - } - else { - second->req.p.http = http; - Curl_dyn_init(&http->header_recvbuf, DYN_H2_HEADERS); - Curl_http2_setup_req(second); - second->state.stream_weight = data->state.stream_weight; - } - } - return second; -} - -static int set_transfer_url(struct Curl_easy *data, - struct curl_pushheaders *hp) -{ - const char *v; - CURLUcode uc; - char *url = NULL; - int rc = 0; - CURLU *u = curl_url(); - - if(!u) - return 5; - - v = curl_pushheader_byname(hp, H2H3_PSEUDO_SCHEME); - if(v) { - uc = curl_url_set(u, CURLUPART_SCHEME, v, 0); - if(uc) { - rc = 1; - goto fail; - } - } - - v = curl_pushheader_byname(hp, H2H3_PSEUDO_AUTHORITY); - if(v) { - uc = curl_url_set(u, CURLUPART_HOST, v, 0); - if(uc) { - rc = 2; - goto fail; - } - } - - v = curl_pushheader_byname(hp, H2H3_PSEUDO_PATH); - if(v) { - uc = curl_url_set(u, CURLUPART_PATH, v, 0); - if(uc) { - rc = 3; - goto fail; - } - } - - uc = curl_url_get(u, CURLUPART_URL, &url, 0); - if(uc) - rc = 4; - fail: - curl_url_cleanup(u); - if(rc) - return rc; - - if(data->state.url_alloc) - free(data->state.url); - data->state.url_alloc = TRUE; - data->state.url = url; - return 0; -} - -static int push_promise(struct Curl_easy *data, - struct connectdata *conn, - const nghttp2_push_promise *frame) -{ - int rv; /* one of the CURL_PUSH_* defines */ - H2BUGF(infof(data, "PUSH_PROMISE received, stream %u", - frame->promised_stream_id)); - if(data->multi->push_cb) { - struct HTTP *stream; - struct HTTP *newstream; - struct curl_pushheaders heads; - CURLMcode rc; - struct http_conn *httpc; - size_t i; - /* clone the parent */ - struct Curl_easy *newhandle = duphandle(data); - if(!newhandle) { - infof(data, "failed to duplicate handle"); - rv = CURL_PUSH_DENY; /* FAIL HARD */ - goto fail; - } - - heads.data = data; - heads.frame = frame; - /* ask the application */ - H2BUGF(infof(data, "Got PUSH_PROMISE, ask application")); - - stream = data->req.p.http; - if(!stream) { - failf(data, "Internal NULL stream"); - (void)Curl_close(&newhandle); - rv = CURL_PUSH_DENY; - goto fail; - } - - rv = set_transfer_url(newhandle, &heads); - if(rv) { - (void)Curl_close(&newhandle); - rv = CURL_PUSH_DENY; - goto fail; - } - - Curl_set_in_callback(data, true); - rv = data->multi->push_cb(data, newhandle, - stream->push_headers_used, &heads, - data->multi->push_userp); - Curl_set_in_callback(data, false); - - /* free the headers again */ - for(i = 0; i<stream->push_headers_used; i++) - free(stream->push_headers[i]); - free(stream->push_headers); - stream->push_headers = NULL; - stream->push_headers_used = 0; - - if(rv) { - DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT)); - /* denied, kill off the new handle again */ - http2_stream_free(newhandle->req.p.http); - newhandle->req.p.http = NULL; - (void)Curl_close(&newhandle); - goto fail; - } - - newstream = newhandle->req.p.http; - newstream->stream_id = frame->promised_stream_id; - newhandle->req.maxdownload = -1; - newhandle->req.size = -1; - - /* approved, add to the multi handle and immediately switch to PERFORM - state with the given connection !*/ - rc = Curl_multi_add_perform(data->multi, newhandle, conn); - if(rc) { - infof(data, "failed to add handle to multi"); - http2_stream_free(newhandle->req.p.http); - newhandle->req.p.http = NULL; - Curl_close(&newhandle); - rv = CURL_PUSH_DENY; - goto fail; - } - - httpc = &conn->proto.httpc; - rv = nghttp2_session_set_stream_user_data(httpc->h2, - frame->promised_stream_id, - newhandle); - if(rv) { - infof(data, "failed to set user_data for stream %u", - frame->promised_stream_id); - DEBUGASSERT(0); - rv = CURL_PUSH_DENY; - goto fail; - } - Curl_dyn_init(&newstream->header_recvbuf, DYN_H2_HEADERS); - Curl_dyn_init(&newstream->trailer_recvbuf, DYN_H2_TRAILERS); - } - else { - H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it")); - rv = CURL_PUSH_DENY; - } - fail: - return rv; -} - -/* - * multi_connchanged() is called to tell that there is a connection in - * this multi handle that has changed state (multiplexing become possible, the - * number of allowed streams changed or similar), and a subsequent use of this - * multi handle should move CONNECT_PEND handles back to CONNECT to have them - * retry. - */ -static void multi_connchanged(struct Curl_multi *multi) -{ - multi->recheckstate = TRUE; -} - -static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, - void *userp) -{ - struct connectdata *conn = (struct connectdata *)userp; - struct http_conn *httpc = &conn->proto.httpc; - struct Curl_easy *data_s = NULL; - struct HTTP *stream = NULL; - struct Curl_easy *data = get_transfer(httpc); - int rv; - size_t left, ncopy; - int32_t stream_id = frame->hd.stream_id; - CURLcode result; - - if(!stream_id) { - /* stream ID zero is for connection-oriented stuff */ - if(frame->hd.type == NGHTTP2_SETTINGS) { - uint32_t max_conn = httpc->settings.max_concurrent_streams; - H2BUGF(infof(data, "Got SETTINGS")); - httpc->settings.max_concurrent_streams = - nghttp2_session_get_remote_settings( - session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); - httpc->settings.enable_push = - nghttp2_session_get_remote_settings( - session, NGHTTP2_SETTINGS_ENABLE_PUSH); - H2BUGF(infof(data, "MAX_CONCURRENT_STREAMS == %d", - httpc->settings.max_concurrent_streams)); - H2BUGF(infof(data, "ENABLE_PUSH == %s", - httpc->settings.enable_push?"TRUE":"false")); - if(max_conn != httpc->settings.max_concurrent_streams) { - /* only signal change if the value actually changed */ - infof(data, - "Connection state changed (MAX_CONCURRENT_STREAMS == %u)!", - httpc->settings.max_concurrent_streams); - multi_connchanged(data->multi); - } - } - return 0; - } - data_s = nghttp2_session_get_stream_user_data(session, stream_id); - if(!data_s) { - H2BUGF(infof(data, - "No Curl_easy associated with stream: %u", - stream_id)); - return 0; - } - - stream = data_s->req.p.http; - if(!stream) { - H2BUGF(infof(data_s, "No proto pointer for stream: %u", - stream_id)); - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - H2BUGF(infof(data_s, "on_frame_recv() header %x stream %u", - frame->hd.type, stream_id)); - - switch(frame->hd.type) { - case NGHTTP2_DATA: - /* If body started on this stream, then receiving DATA is illegal. */ - if(!stream->bodystarted) { - rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, - stream_id, NGHTTP2_PROTOCOL_ERROR); - - if(nghttp2_is_fatal(rv)) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - break; - case NGHTTP2_HEADERS: - if(stream->bodystarted) { - /* Only valid HEADERS after body started is trailer HEADERS. We - buffer them in on_header callback. */ - break; - } - - /* nghttp2 guarantees that :status is received, and we store it to - stream->status_code. Fuzzing has proven this can still be reached - without status code having been set. */ - if(stream->status_code == -1) - return NGHTTP2_ERR_CALLBACK_FAILURE; - - /* Only final status code signals the end of header */ - if(stream->status_code / 100 != 1) { - stream->bodystarted = TRUE; - stream->status_code = -1; - } - - result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("\r\n")); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - - left = Curl_dyn_len(&stream->header_recvbuf) - - stream->nread_header_recvbuf; - ncopy = CURLMIN(stream->len, left); - - memcpy(&stream->mem[stream->memlen], - Curl_dyn_ptr(&stream->header_recvbuf) + - stream->nread_header_recvbuf, - ncopy); - stream->nread_header_recvbuf += ncopy; - - DEBUGASSERT(stream->mem); - H2BUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p", - ncopy, stream_id, stream->mem)); - - stream->len -= ncopy; - stream->memlen += ncopy; - - drain_this(data_s, httpc); - /* if we receive data for another handle, wake that up */ - if(get_transfer(httpc) != data_s) - Curl_expire(data_s, 0, EXPIRE_RUN_NOW); - break; - case NGHTTP2_PUSH_PROMISE: - rv = push_promise(data_s, conn, &frame->push_promise); - if(rv) { /* deny! */ - int h2; - DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT)); - h2 = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, - frame->push_promise.promised_stream_id, - NGHTTP2_CANCEL); - if(nghttp2_is_fatal(h2)) - return NGHTTP2_ERR_CALLBACK_FAILURE; - else if(rv == CURL_PUSH_ERROROUT) { - DEBUGF(infof(data_s, "Fail the parent stream (too)")); - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - break; - default: - H2BUGF(infof(data_s, "Got frame type %x for stream %u", - frame->hd.type, stream_id)); - break; - } - return 0; -} - -static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const uint8_t *mem, size_t len, void *userp) -{ - struct HTTP *stream; - struct Curl_easy *data_s; - size_t nread; - struct connectdata *conn = (struct connectdata *)userp; - struct http_conn *httpc = &conn->proto.httpc; - (void)session; - (void)flags; - - DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ - - /* get the stream from the hash based on Stream ID */ - data_s = nghttp2_session_get_stream_user_data(session, stream_id); - if(!data_s) { - /* Receiving a Stream ID not in the hash should not happen - unless - we have aborted a transfer artificially and there were more data - in the pipeline. Silently ignore. */ - H2BUGF(fprintf(stderr, "Data for stream %u but it doesn't exist\n", - stream_id)); - return 0; - } - - stream = data_s->req.p.http; - if(!stream) - return NGHTTP2_ERR_CALLBACK_FAILURE; - - nread = CURLMIN(stream->len, len); - memcpy(&stream->mem[stream->memlen], mem, nread); - - stream->len -= nread; - stream->memlen += nread; - - drain_this(data_s, &conn->proto.httpc); - - /* if we receive data for another handle, wake that up */ - if(get_transfer(httpc) != data_s) - Curl_expire(data_s, 0, EXPIRE_RUN_NOW); - - H2BUGF(infof(data_s, "%zu data received for stream %u " - "(%zu left in buffer %p, total %zu)", - nread, stream_id, - stream->len, stream->mem, - stream->memlen)); - - if(nread < len) { - stream->pausedata = mem + nread; - stream->pauselen = len - nread; - H2BUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer" - ", stream %u", - len - nread, stream_id)); - data_s->conn->proto.httpc.pause_stream_id = stream_id; - - return NGHTTP2_ERR_PAUSE; - } - - /* pause execution of nghttp2 if we received data for another handle - in order to process them first. */ - if(get_transfer(httpc) != data_s) { - data_s->conn->proto.httpc.pause_stream_id = stream_id; - - return NGHTTP2_ERR_PAUSE; - } - - return 0; -} - -static int on_stream_close(nghttp2_session *session, int32_t stream_id, - uint32_t error_code, void *userp) -{ - struct Curl_easy *data_s; - struct HTTP *stream; - struct connectdata *conn = (struct connectdata *)userp; - int rv; - (void)session; - (void)stream_id; - - if(stream_id) { - struct http_conn *httpc; - /* get the stream from the hash based on Stream ID, stream ID zero is for - connection-oriented stuff */ - data_s = nghttp2_session_get_stream_user_data(session, stream_id); - if(!data_s) { - /* We could get stream ID not in the hash. For example, if we - decided to reject stream (e.g., PUSH_PROMISE). */ - return 0; - } - H2BUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u", - nghttp2_http2_strerror(error_code), error_code, stream_id)); - stream = data_s->req.p.http; - if(!stream) - return NGHTTP2_ERR_CALLBACK_FAILURE; - - stream->closed = TRUE; - httpc = &conn->proto.httpc; - drain_this(data_s, httpc); - Curl_expire(data_s, 0, EXPIRE_RUN_NOW); - stream->error = error_code; - - /* remove the entry from the hash as the stream is now gone */ - rv = nghttp2_session_set_stream_user_data(session, stream_id, 0); - if(rv) { - infof(data_s, "http/2: failed to clear user_data for stream %u", - stream_id); - DEBUGASSERT(0); - } - if(stream_id == httpc->pause_stream_id) { - H2BUGF(infof(data_s, "Stopped the pause stream")); - httpc->pause_stream_id = 0; - } - H2BUGF(infof(data_s, "Removed stream %u hash", stream_id)); - stream->stream_id = 0; /* cleared */ - } - return 0; -} - -static int on_begin_headers(nghttp2_session *session, - const nghttp2_frame *frame, void *userp) -{ - struct HTTP *stream; - struct Curl_easy *data_s = NULL; - (void)userp; - - data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); - if(!data_s) { - return 0; - } - - H2BUGF(infof(data_s, "on_begin_headers() was called")); - - if(frame->hd.type != NGHTTP2_HEADERS) { - return 0; - } - - stream = data_s->req.p.http; - if(!stream || !stream->bodystarted) { - return 0; - } - - return 0; -} - -/* Decode HTTP status code. Returns -1 if no valid status code was - decoded. */ -static int decode_status_code(const uint8_t *value, size_t len) -{ - int i; - int res; - - if(len != 3) { - return -1; - } - - res = 0; - - for(i = 0; i < 3; ++i) { - char c = value[i]; - - if(c < '0' || c > '9') { - return -1; - } - - res *= 10; - res += c - '0'; - } - - return res; -} - -/* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */ -static int on_header(nghttp2_session *session, const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - uint8_t flags, - void *userp) -{ - struct HTTP *stream; - struct Curl_easy *data_s; - int32_t stream_id = frame->hd.stream_id; - struct connectdata *conn = (struct connectdata *)userp; - struct http_conn *httpc = &conn->proto.httpc; - CURLcode result; - (void)flags; - - DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ - - /* get the stream from the hash based on Stream ID */ - data_s = nghttp2_session_get_stream_user_data(session, stream_id); - if(!data_s) - /* Receiving a Stream ID not in the hash should not happen, this is an - internal error more than anything else! */ - return NGHTTP2_ERR_CALLBACK_FAILURE; - - stream = data_s->req.p.http; - if(!stream) { - failf(data_s, "Internal NULL stream"); - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - /* Store received PUSH_PROMISE headers to be used when the subsequent - PUSH_PROMISE callback comes */ - if(frame->hd.type == NGHTTP2_PUSH_PROMISE) { - char *h; - - if(!strcmp(H2H3_PSEUDO_AUTHORITY, (const char *)name)) { - /* pseudo headers are lower case */ - int rc = 0; - char *check = aprintf("%s:%d", conn->host.name, conn->remote_port); - if(!check) - /* no memory */ - return NGHTTP2_ERR_CALLBACK_FAILURE; - if(!Curl_strcasecompare(check, (const char *)value) && - ((conn->remote_port != conn->given->defport) || - !Curl_strcasecompare(conn->host.name, (const char *)value))) { - /* This is push is not for the same authority that was asked for in - * the URL. RFC 7540 section 8.2 says: "A client MUST treat a - * PUSH_PROMISE for which the server is not authoritative as a stream - * error of type PROTOCOL_ERROR." - */ - (void)nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, - stream_id, NGHTTP2_PROTOCOL_ERROR); - rc = NGHTTP2_ERR_CALLBACK_FAILURE; - } - free(check); - if(rc) - return rc; - } - - if(!stream->push_headers) { - stream->push_headers_alloc = 10; - stream->push_headers = malloc(stream->push_headers_alloc * - sizeof(char *)); - if(!stream->push_headers) - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - stream->push_headers_used = 0; - } - else if(stream->push_headers_used == - stream->push_headers_alloc) { - char **headp; - if(stream->push_headers_alloc > 1000) { - /* this is beyond crazy many headers, bail out */ - failf(data_s, "Too many PUSH_PROMISE headers"); - Curl_safefree(stream->push_headers); - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - stream->push_headers_alloc *= 2; - headp = Curl_saferealloc(stream->push_headers, - stream->push_headers_alloc * sizeof(char *)); - if(!headp) { - stream->push_headers = NULL; - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - stream->push_headers = headp; - } - h = aprintf("%s:%s", name, value); - if(h) - stream->push_headers[stream->push_headers_used++] = h; - return 0; - } - - if(stream->bodystarted) { - /* This is a trailer */ - H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s", namelen, name, valuelen, - value)); - result = Curl_dyn_addf(&stream->trailer_recvbuf, - "%.*s: %.*s\r\n", namelen, name, - valuelen, value); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - - return 0; - } - - if(namelen == sizeof(H2H3_PSEUDO_STATUS) - 1 && - memcmp(H2H3_PSEUDO_STATUS, name, namelen) == 0) { - /* nghttp2 guarantees :status is received first and only once, and - value is 3 digits status code, and decode_status_code always - succeeds. */ - char buffer[32]; - stream->status_code = decode_status_code(value, valuelen); - DEBUGASSERT(stream->status_code != -1); - msnprintf(buffer, sizeof(buffer), H2H3_PSEUDO_STATUS ":%u\r", - stream->status_code); - result = Curl_headers_push(data_s, buffer, CURLH_PSEUDO); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("HTTP/2 ")); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - result = Curl_dyn_addn(&stream->header_recvbuf, value, valuelen); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - /* the space character after the status code is mandatory */ - result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST(" \r\n")); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - /* if we receive data for another handle, wake that up */ - if(get_transfer(httpc) != data_s) - Curl_expire(data_s, 0, EXPIRE_RUN_NOW); - - H2BUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)", - stream->status_code, data_s)); - return 0; - } - - /* nghttp2 guarantees that namelen > 0, and :status was already - received, and this is not pseudo-header field . */ - /* convert to a HTTP1-style header */ - result = Curl_dyn_addn(&stream->header_recvbuf, name, namelen); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST(": ")); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - result = Curl_dyn_addn(&stream->header_recvbuf, value, valuelen); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("\r\n")); - if(result) - return NGHTTP2_ERR_CALLBACK_FAILURE; - /* if we receive data for another handle, wake that up */ - if(get_transfer(httpc) != data_s) - Curl_expire(data_s, 0, EXPIRE_RUN_NOW); - - H2BUGF(infof(data_s, "h2 header: %.*s: %.*s", namelen, name, valuelen, - value)); - - return 0; /* 0 is successful */ -} - -static ssize_t data_source_read_callback(nghttp2_session *session, - int32_t stream_id, - uint8_t *buf, size_t length, - uint32_t *data_flags, - nghttp2_data_source *source, - void *userp) -{ - struct Curl_easy *data_s; - struct HTTP *stream = NULL; - size_t nread; - (void)source; - (void)userp; - - if(stream_id) { - /* get the stream from the hash based on Stream ID, stream ID zero is for - connection-oriented stuff */ - data_s = nghttp2_session_get_stream_user_data(session, stream_id); - if(!data_s) - /* Receiving a Stream ID not in the hash should not happen, this is an - internal error more than anything else! */ - return NGHTTP2_ERR_CALLBACK_FAILURE; - - stream = data_s->req.p.http; - if(!stream) - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - else - return NGHTTP2_ERR_INVALID_ARGUMENT; - - nread = CURLMIN(stream->upload_len, length); - if(nread > 0) { - memcpy(buf, stream->upload_mem, nread); - stream->upload_mem += nread; - stream->upload_len -= nread; - if(data_s->state.infilesize != -1) - stream->upload_left -= nread; - } - - if(stream->upload_left == 0) - *data_flags = NGHTTP2_DATA_FLAG_EOF; - else if(nread == 0) - return NGHTTP2_ERR_DEFERRED; - - H2BUGF(infof(data_s, "data_source_read_callback: " - "returns %zu bytes stream %u", - nread, stream_id)); - - return nread; -} - -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) -static int error_callback(nghttp2_session *session, - const char *msg, - size_t len, - void *userp) -{ - (void)session; - (void)msg; - (void)len; - (void)userp; - return 0; -} -#endif - -static void populate_settings(struct Curl_easy *data, - struct http_conn *httpc) -{ - nghttp2_settings_entry *iv = httpc->local_settings; - - iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[0].value = Curl_multi_max_concurrent_streams(data->multi); - - iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[1].value = HTTP2_HUGE_WINDOW_SIZE; - - iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; - iv[2].value = data->multi->push_cb != NULL; - - httpc->local_settings_num = 3; -} - -void Curl_http2_done(struct Curl_easy *data, bool premature) -{ - struct HTTP *http = data->req.p.http; - struct http_conn *httpc = &data->conn->proto.httpc; - - /* there might be allocated resources done before this got the 'h2' pointer - setup */ - Curl_dyn_free(&http->header_recvbuf); - Curl_dyn_free(&http->trailer_recvbuf); - if(http->push_headers) { - /* if they weren't used and then freed before */ - for(; http->push_headers_used > 0; --http->push_headers_used) { - free(http->push_headers[http->push_headers_used - 1]); - } - free(http->push_headers); - http->push_headers = NULL; - } - - if(!(data->conn->handler->protocol&PROTO_FAMILY_HTTP) || - !httpc->h2) /* not HTTP/2 ? */ - return; - - /* do this before the reset handling, as that might clear ->stream_id */ - if(http->stream_id == httpc->pause_stream_id) { - H2BUGF(infof(data, "DONE the pause stream (%u)", http->stream_id)); - httpc->pause_stream_id = 0; - } - if(premature || (!http->closed && http->stream_id)) { - /* RST_STREAM */ - set_transfer(httpc, data); /* set the transfer */ - H2BUGF(infof(data, "RST stream %u", http->stream_id)); - if(!nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE, - http->stream_id, NGHTTP2_STREAM_CLOSED)) - (void)nghttp2_session_send(httpc->h2); - } - - if(data->state.drain) - drained_transfer(data, httpc); - - /* -1 means unassigned and 0 means cleared */ - if(http->stream_id > 0) { - int rv = nghttp2_session_set_stream_user_data(httpc->h2, - http->stream_id, 0); - if(rv) { - infof(data, "http/2: failed to clear user_data for stream %u", - http->stream_id); - DEBUGASSERT(0); - } - set_transfer(httpc, NULL); - http->stream_id = 0; - } -} - -static int client_new(struct connectdata *conn, - nghttp2_session_callbacks *callbacks) -{ -#if NGHTTP2_VERSION_NUM < 0x013200 - /* before 1.50.0 */ - return nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn); -#else - nghttp2_option *o; - int rc = nghttp2_option_new(&o); - if(rc) - return rc; - /* turn off RFC 9113 leading and trailing white spaces validation against - HTTP field value. */ - nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1); - rc = nghttp2_session_client_new2(&conn->proto.httpc.h2, callbacks, conn, - o); - nghttp2_option_del(o); - return rc; -#endif -} - -/* - * Initialize nghttp2 for a Curl connection - */ -static CURLcode http2_init(struct Curl_easy *data, struct connectdata *conn) -{ - if(!conn->proto.httpc.h2) { - int rc; - nghttp2_session_callbacks *callbacks; - - conn->proto.httpc.inbuf = malloc(H2_BUFSIZE); - if(!conn->proto.httpc.inbuf) - return CURLE_OUT_OF_MEMORY; - - rc = nghttp2_session_callbacks_new(&callbacks); - - if(rc) { - failf(data, "Couldn't initialize nghttp2 callbacks"); - return CURLE_OUT_OF_MEMORY; /* most likely at least */ - } - - /* nghttp2_send_callback */ - nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); - /* nghttp2_on_frame_recv_callback */ - nghttp2_session_callbacks_set_on_frame_recv_callback - (callbacks, on_frame_recv); - /* nghttp2_on_data_chunk_recv_callback */ - nghttp2_session_callbacks_set_on_data_chunk_recv_callback - (callbacks, on_data_chunk_recv); - /* nghttp2_on_stream_close_callback */ - nghttp2_session_callbacks_set_on_stream_close_callback - (callbacks, on_stream_close); - /* nghttp2_on_begin_headers_callback */ - nghttp2_session_callbacks_set_on_begin_headers_callback - (callbacks, on_begin_headers); - /* nghttp2_on_header_callback */ - nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header); - - nghttp2_session_callbacks_set_error_callback(callbacks, error_callback); - - /* The nghttp2 session is not yet setup, do it */ - rc = client_new(conn, callbacks); - - nghttp2_session_callbacks_del(callbacks); - - if(rc) { - failf(data, "Couldn't initialize nghttp2"); - return CURLE_OUT_OF_MEMORY; /* most likely at least */ - } - } - return CURLE_OK; -} - -/* - * Append headers to ask for a HTTP1.1 to HTTP2 upgrade. - */ -CURLcode Curl_http2_request_upgrade(struct dynbuf *req, - struct Curl_easy *data) -{ - CURLcode result; - ssize_t binlen; - char *base64; - size_t blen; - struct connectdata *conn = data->conn; - struct SingleRequest *k = &data->req; - uint8_t *binsettings = conn->proto.httpc.binsettings; - struct http_conn *httpc = &conn->proto.httpc; - - populate_settings(data, httpc); - - /* this returns number of bytes it wrote */ - binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN, - httpc->local_settings, - httpc->local_settings_num); - if(binlen <= 0) { - failf(data, "nghttp2 unexpectedly failed on pack_settings_payload"); - Curl_dyn_free(req); - return CURLE_FAILED_INIT; - } - conn->proto.httpc.binlen = binlen; - - result = Curl_base64url_encode((const char *)binsettings, binlen, - &base64, &blen); - if(result) { - Curl_dyn_free(req); - return result; - } - - result = Curl_dyn_addf(req, - "Connection: Upgrade, HTTP2-Settings\r\n" - "Upgrade: %s\r\n" - "HTTP2-Settings: %s\r\n", - NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64); - free(base64); - - k->upgr101 = UPGR101_H2; - - return result; -} - -/* - * Returns nonzero if current HTTP/2 session should be closed. - */ -static int should_close_session(struct http_conn *httpc) -{ - return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) && - !nghttp2_session_want_write(httpc->h2); -} - -/* - * h2_process_pending_input() processes pending input left in - * httpc->inbuf. Then, call h2_session_send() to send pending data. - * This function returns 0 if it succeeds, or -1 and error code will - * be assigned to *err. - */ -static int h2_process_pending_input(struct Curl_easy *data, - struct http_conn *httpc, - CURLcode *err) -{ - ssize_t nread; - char *inbuf; - ssize_t rv; - - nread = httpc->inbuflen - httpc->nread_inbuf; - inbuf = httpc->inbuf + httpc->nread_inbuf; - - set_transfer(httpc, data); /* set the transfer */ - rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread); - if(rv < 0) { - failf(data, - "h2_process_pending_input: nghttp2_session_mem_recv() returned " - "%zd:%s", rv, nghttp2_strerror((int)rv)); - *err = CURLE_RECV_ERROR; - return -1; - } - - if(nread == rv) { - H2BUGF(infof(data, - "h2_process_pending_input: All data in connection buffer " - "processed")); - httpc->inbuflen = 0; - httpc->nread_inbuf = 0; - } - else { - httpc->nread_inbuf += rv; - H2BUGF(infof(data, - "h2_process_pending_input: %zu bytes left in connection " - "buffer", - httpc->inbuflen - httpc->nread_inbuf)); - } - - rv = h2_session_send(data, httpc->h2); - if(rv) { - *err = CURLE_SEND_ERROR; - return -1; - } - - if(nghttp2_session_check_request_allowed(httpc->h2) == 0) { - /* No more requests are allowed in the current session, so - the connection may not be reused. This is set when a - GOAWAY frame has been received or when the limit of stream - identifiers has been reached. */ - connclose(data->conn, "http/2: No new requests allowed"); - } - - if(should_close_session(httpc)) { - struct HTTP *stream = data->req.p.http; - H2BUGF(infof(data, - "h2_process_pending_input: nothing to do in this session")); - if(stream->error) - *err = CURLE_HTTP2; - else { - /* not an error per se, but should still close the connection */ - connclose(data->conn, "GOAWAY received"); - *err = CURLE_OK; - } - return -1; - } - return 0; -} - -/* - * Called from transfer.c:done_sending when we stop uploading. - */ -CURLcode Curl_http2_done_sending(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - - if((conn->handler == &Curl_handler_http2_ssl) || - (conn->handler == &Curl_handler_http2)) { - /* make sure this is only attempted for HTTP/2 transfers */ - struct HTTP *stream = data->req.p.http; - struct http_conn *httpc = &conn->proto.httpc; - nghttp2_session *h2 = httpc->h2; - - if(stream->upload_left) { - /* If the stream still thinks there's data left to upload. */ - - stream->upload_left = 0; /* DONE! */ - - /* resume sending here to trigger the callback to get called again so - that it can signal EOF to nghttp2 */ - (void)nghttp2_session_resume_data(h2, stream->stream_id); - (void)h2_process_pending_input(data, httpc, &result); - } - - /* If nghttp2 still has pending frames unsent */ - if(nghttp2_session_want_write(h2)) { - struct SingleRequest *k = &data->req; - int rv; - - H2BUGF(infof(data, "HTTP/2 still wants to send data (easy %p)", data)); - - /* and attempt to send the pending frames */ - rv = h2_session_send(data, h2); - if(rv) - result = CURLE_SEND_ERROR; - - if(nghttp2_session_want_write(h2)) { - /* re-set KEEP_SEND to make sure we are called again */ - k->keepon |= KEEP_SEND; - } - } - } - return result; -} - -static ssize_t http2_handle_stream_close(struct connectdata *conn, - struct Curl_easy *data, - struct HTTP *stream, CURLcode *err) -{ - struct http_conn *httpc = &conn->proto.httpc; - - if(httpc->pause_stream_id == stream->stream_id) { - httpc->pause_stream_id = 0; - } - - drained_transfer(data, httpc); - - if(httpc->pause_stream_id == 0) { - if(h2_process_pending_input(data, httpc, err) != 0) { - return -1; - } - } - - DEBUGASSERT(data->state.drain == 0); - - /* Reset to FALSE to prevent infinite loop in readwrite_data function. */ - stream->closed = FALSE; - if(stream->error == NGHTTP2_REFUSED_STREAM) { - H2BUGF(infof(data, "REFUSED_STREAM (%u), try again on a new connection", - stream->stream_id)); - connclose(conn, "REFUSED_STREAM"); /* don't use this anymore */ - data->state.refused_stream = TRUE; - *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ - return -1; - } - else if(stream->error != NGHTTP2_NO_ERROR) { - failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)", - stream->stream_id, nghttp2_http2_strerror(stream->error), - stream->error); - *err = CURLE_HTTP2_STREAM; - return -1; - } - - if(!stream->bodystarted) { - failf(data, "HTTP/2 stream %u was closed cleanly, but before getting " - " all response header fields, treated as error", - stream->stream_id); - *err = CURLE_HTTP2_STREAM; - return -1; - } - - if(Curl_dyn_len(&stream->trailer_recvbuf)) { - char *trailp = Curl_dyn_ptr(&stream->trailer_recvbuf); - char *lf; - - do { - size_t len = 0; - CURLcode result; - /* each trailer line ends with a newline */ - lf = strchr(trailp, '\n'); - if(!lf) - break; - len = lf + 1 - trailp; - - Curl_debug(data, CURLINFO_HEADER_IN, trailp, len); - /* pass the trailers one by one to the callback */ - result = Curl_client_write(data, CLIENTWRITE_HEADER, trailp, len); - if(result) { - *err = result; - return -1; - } - trailp = ++lf; - } while(lf); - } - - stream->close_handled = TRUE; - - H2BUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close")); - return 0; -} - -/* - * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight - * and dependency to the peer. It also stores the updated values in the state - * struct. - */ - -static void h2_pri_spec(struct Curl_easy *data, - nghttp2_priority_spec *pri_spec) -{ - struct HTTP *depstream = (data->set.stream_depends_on? - data->set.stream_depends_on->req.p.http:NULL); - int32_t depstream_id = depstream? depstream->stream_id:0; - nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_weight, - data->set.stream_depends_e); - data->state.stream_weight = data->set.stream_weight; - data->state.stream_depends_e = data->set.stream_depends_e; - data->state.stream_depends_on = data->set.stream_depends_on; -} - -/* - * h2_session_send() checks if there's been an update in the priority / - * dependency settings and if so it submits a PRIORITY frame with the updated - * info. - */ -static int h2_session_send(struct Curl_easy *data, - nghttp2_session *h2) -{ - struct HTTP *stream = data->req.p.http; - struct http_conn *httpc = &data->conn->proto.httpc; - set_transfer(httpc, data); - if((data->set.stream_weight != data->state.stream_weight) || - (data->set.stream_depends_e != data->state.stream_depends_e) || - (data->set.stream_depends_on != data->state.stream_depends_on) ) { - /* send new weight and/or dependency */ - nghttp2_priority_spec pri_spec; - int rv; - - h2_pri_spec(data, &pri_spec); - - H2BUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)", - stream->stream_id, data)); - DEBUGASSERT(stream->stream_id != -1); - rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id, - &pri_spec); - if(rv) - return rv; - } - - return nghttp2_session_send(h2); -} - -static ssize_t http2_recv(struct Curl_easy *data, int sockindex, - char *mem, size_t len, CURLcode *err) -{ - ssize_t nread; - struct connectdata *conn = data->conn; - struct http_conn *httpc = &conn->proto.httpc; - struct HTTP *stream = data->req.p.http; - - (void)sockindex; /* we always do HTTP2 on sockindex 0 */ - - if(should_close_session(httpc)) { - H2BUGF(infof(data, - "http2_recv: nothing to do in this session")); - if(conn->bits.close) { - /* already marked for closure, return OK and we're done */ - *err = CURLE_OK; - return 0; - } - *err = CURLE_HTTP2; - return -1; - } - - /* Nullify here because we call nghttp2_session_send() and they - might refer to the old buffer. */ - stream->upload_mem = NULL; - stream->upload_len = 0; - - /* - * At this point 'stream' is just in the Curl_easy the connection - * identifies as its owner at this time. - */ - - if(stream->bodystarted && - stream->nread_header_recvbuf < Curl_dyn_len(&stream->header_recvbuf)) { - /* If there is header data pending for this stream to return, do that */ - size_t left = - Curl_dyn_len(&stream->header_recvbuf) - stream->nread_header_recvbuf; - size_t ncopy = CURLMIN(len, left); - memcpy(mem, Curl_dyn_ptr(&stream->header_recvbuf) + - stream->nread_header_recvbuf, ncopy); - stream->nread_header_recvbuf += ncopy; - - H2BUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf", - (int)ncopy)); - return ncopy; - } - - H2BUGF(infof(data, "http2_recv: easy %p (stream %u) win %u/%u", - data, stream->stream_id, - nghttp2_session_get_local_window_size(httpc->h2), - nghttp2_session_get_stream_local_window_size(httpc->h2, - stream->stream_id) - )); - - if((data->state.drain) && stream->memlen) { - H2BUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u (%p => %p)", - stream->memlen, stream->stream_id, - stream->mem, mem)); - if(mem != stream->mem) { - /* if we didn't get the same buffer this time, we must move the data to - the beginning */ - memmove(mem, stream->mem, stream->memlen); - stream->len = len - stream->memlen; - stream->mem = mem; - } - if(httpc->pause_stream_id == stream->stream_id && !stream->pausedata) { - /* We have paused nghttp2, but we have no pause data (see - on_data_chunk_recv). */ - httpc->pause_stream_id = 0; - if(h2_process_pending_input(data, httpc, err) != 0) { - return -1; - } - } - } - else if(stream->pausedata) { - DEBUGASSERT(httpc->pause_stream_id == stream->stream_id); - nread = CURLMIN(len, stream->pauselen); - memcpy(mem, stream->pausedata, nread); - - stream->pausedata += nread; - stream->pauselen -= nread; - - if(stream->pauselen == 0) { - H2BUGF(infof(data, "Unpaused by stream %u", stream->stream_id)); - DEBUGASSERT(httpc->pause_stream_id == stream->stream_id); - httpc->pause_stream_id = 0; - - stream->pausedata = NULL; - stream->pauselen = 0; - - /* When NGHTTP2_ERR_PAUSE is returned from - data_source_read_callback, we might not process DATA frame - fully. Calling nghttp2_session_mem_recv() again will - continue to process DATA frame, but if there is no incoming - frames, then we have to call it again with 0-length data. - Without this, on_stream_close callback will not be called, - and stream could be hanged. */ - if(h2_process_pending_input(data, httpc, err) != 0) { - return -1; - } - } - H2BUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u", - nread, stream->stream_id)); - return nread; - } - else if(httpc->pause_stream_id) { - /* If a stream paused nghttp2_session_mem_recv previously, and has - not processed all data, it still refers to the buffer in - nghttp2_session. If we call nghttp2_session_mem_recv(), we may - overwrite that buffer. To avoid that situation, just return - here with CURLE_AGAIN. This could be busy loop since data in - socket is not read. But it seems that usually streams are - notified with its drain property, and socket is read again - quickly. */ - if(stream->closed) - /* closed overrides paused */ - return 0; - H2BUGF(infof(data, "stream %u is paused, pause id: %u", - stream->stream_id, httpc->pause_stream_id)); - *err = CURLE_AGAIN; - return -1; - } - else { - /* remember where to store incoming data for this stream and how big the - buffer is */ - stream->mem = mem; - stream->len = len; - stream->memlen = 0; - - if(httpc->inbuflen == 0) { - nread = ((Curl_recv *)httpc->recv_underlying)( - data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, err); - - if(nread == -1) { - if(*err != CURLE_AGAIN) - failf(data, "Failed receiving HTTP2 data"); - else if(stream->closed) - /* received when the stream was already closed! */ - return http2_handle_stream_close(conn, data, stream, err); - - return -1; - } - - if(nread == 0) { - if(!stream->closed) { - /* This will happen when the server or proxy server is SIGKILLed - during data transfer. We should emit an error since our data - received may be incomplete. */ - failf(data, "HTTP/2 stream %u was not closed cleanly before" - " end of the underlying stream", - stream->stream_id); - *err = CURLE_HTTP2_STREAM; - return -1; - } - - H2BUGF(infof(data, "end of stream")); - *err = CURLE_OK; - return 0; - } - - H2BUGF(infof(data, "nread=%zd", nread)); - - httpc->inbuflen = nread; - - DEBUGASSERT(httpc->nread_inbuf == 0); - } - else { - nread = httpc->inbuflen - httpc->nread_inbuf; - (void)nread; /* silence warning, used in debug */ - H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd", - nread)); - } - - if(h2_process_pending_input(data, httpc, err)) - return -1; - } - if(stream->memlen) { - ssize_t retlen = stream->memlen; - H2BUGF(infof(data, "http2_recv: returns %zd for stream %u", - retlen, stream->stream_id)); - stream->memlen = 0; - - if(httpc->pause_stream_id == stream->stream_id) { - /* data for this stream is returned now, but this stream caused a pause - already so we need it called again asap */ - H2BUGF(infof(data, "Data returned for PAUSED stream %u", - stream->stream_id)); - } - else if(!stream->closed) { - drained_transfer(data, httpc); - } - else - /* this stream is closed, trigger a another read ASAP to detect that */ - Curl_expire(data, 0, EXPIRE_RUN_NOW); - - return retlen; - } - if(stream->closed) - return http2_handle_stream_close(conn, data, stream, err); - *err = CURLE_AGAIN; - H2BUGF(infof(data, "http2_recv returns AGAIN for stream %u", - stream->stream_id)); - return -1; -} - -static ssize_t http2_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, CURLcode *err) -{ - /* - * Currently, we send request in this function, but this function is also - * used to send request body. It would be nice to add dedicated function for - * request. - */ - int rv; - struct connectdata *conn = data->conn; - struct http_conn *httpc = &conn->proto.httpc; - struct HTTP *stream = data->req.p.http; - nghttp2_nv *nva = NULL; - size_t nheader; - nghttp2_data_provider data_prd; - int32_t stream_id; - nghttp2_session *h2 = httpc->h2; - nghttp2_priority_spec pri_spec; - CURLcode result; - struct h2h3req *hreq; - - (void)sockindex; - - H2BUGF(infof(data, "http2_send len=%zu", len)); - - if(stream->stream_id != -1) { - if(stream->close_handled) { - infof(data, "stream %u closed", stream->stream_id); - *err = CURLE_HTTP2_STREAM; - return -1; - } - else if(stream->closed) { - return http2_handle_stream_close(conn, data, stream, err); - } - /* If stream_id != -1, we have dispatched request HEADERS, and now - are going to send or sending request body in DATA frame */ - stream->upload_mem = mem; - stream->upload_len = len; - rv = nghttp2_session_resume_data(h2, stream->stream_id); - if(nghttp2_is_fatal(rv)) { - *err = CURLE_SEND_ERROR; - return -1; - } - rv = h2_session_send(data, h2); - if(nghttp2_is_fatal(rv)) { - *err = CURLE_SEND_ERROR; - return -1; - } - len -= stream->upload_len; - - /* Nullify here because we call nghttp2_session_send() and they - might refer to the old buffer. */ - stream->upload_mem = NULL; - stream->upload_len = 0; - - if(should_close_session(httpc)) { - H2BUGF(infof(data, "http2_send: nothing to do in this session")); - *err = CURLE_HTTP2; - return -1; - } - - if(stream->upload_left) { - /* we are sure that we have more data to send here. Calling the - following API will make nghttp2_session_want_write() return - nonzero if remote window allows it, which then libcurl checks - socket is writable or not. See http2_perform_getsock(). */ - nghttp2_session_resume_data(h2, stream->stream_id); - } - -#ifdef DEBUG_HTTP2 - if(!len) { - infof(data, "http2_send: easy %p (stream %u) win %u/%u", - data, stream->stream_id, - nghttp2_session_get_remote_window_size(httpc->h2), - nghttp2_session_get_stream_remote_window_size(httpc->h2, - stream->stream_id) - ); - - } - infof(data, "http2_send returns %zu for stream %u", len, - stream->stream_id); -#endif - return len; - } - - result = Curl_pseudo_headers(data, mem, len, &hreq); - if(result) { - *err = result; - return -1; - } - nheader = hreq->entries; - - nva = malloc(sizeof(nghttp2_nv) * nheader); - if(!nva) { - Curl_pseudo_free(hreq); - *err = CURLE_OUT_OF_MEMORY; - return -1; - } - else { - unsigned int i; - for(i = 0; i < nheader; i++) { - nva[i].name = (unsigned char *)hreq->header[i].name; - nva[i].namelen = hreq->header[i].namelen; - nva[i].value = (unsigned char *)hreq->header[i].value; - nva[i].valuelen = hreq->header[i].valuelen; - nva[i].flags = NGHTTP2_NV_FLAG_NONE; - } - Curl_pseudo_free(hreq); - } - - h2_pri_spec(data, &pri_spec); - - H2BUGF(infof(data, "http2_send request allowed %d (easy handle %p)", - nghttp2_session_check_request_allowed(h2), (void *)data)); - - switch(data->state.httpreq) { - case HTTPREQ_POST: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - case HTTPREQ_PUT: - if(data->state.infilesize != -1) - stream->upload_left = data->state.infilesize; - else - /* data sending without specifying the data amount up front */ - stream->upload_left = -1; /* unknown, but not zero */ - - data_prd.read_callback = data_source_read_callback; - data_prd.source.ptr = NULL; - stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader, - &data_prd, data); - break; - default: - stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader, - NULL, data); - } - - Curl_safefree(nva); - - if(stream_id < 0) { - H2BUGF(infof(data, - "http2_send() nghttp2_submit_request error (%s)%u", - nghttp2_strerror(stream_id), stream_id)); - *err = CURLE_SEND_ERROR; - return -1; - } - - infof(data, "Using Stream ID: %u (easy handle %p)", - stream_id, (void *)data); - stream->stream_id = stream_id; - - rv = h2_session_send(data, h2); - if(rv) { - H2BUGF(infof(data, - "http2_send() nghttp2_session_send error (%s)%d", - nghttp2_strerror(rv), rv)); - - *err = CURLE_SEND_ERROR; - return -1; - } - - if(should_close_session(httpc)) { - H2BUGF(infof(data, "http2_send: nothing to do in this session")); - *err = CURLE_HTTP2; - return -1; - } - - /* If whole HEADERS frame was sent off to the underlying socket, the nghttp2 - library calls data_source_read_callback. But only it found that no data - available, so it deferred the DATA transmission. Which means that - nghttp2_session_want_write() returns 0 on http2_perform_getsock(), which - results that no writable socket check is performed. To workaround this, - we issue nghttp2_session_resume_data() here to bring back DATA - transmission from deferred state. */ - nghttp2_session_resume_data(h2, stream->stream_id); - - return len; -} - -CURLcode Curl_http2_setup(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result; - struct http_conn *httpc = &conn->proto.httpc; - struct HTTP *stream = data->req.p.http; - - DEBUGASSERT(data->state.buffer); - - stream->stream_id = -1; - - Curl_dyn_init(&stream->header_recvbuf, DYN_H2_HEADERS); - Curl_dyn_init(&stream->trailer_recvbuf, DYN_H2_TRAILERS); - - stream->upload_left = 0; - stream->upload_mem = NULL; - stream->upload_len = 0; - stream->mem = data->state.buffer; - stream->len = data->set.buffer_size; - - multi_connchanged(data->multi); - /* below this point only connection related inits are done, which only needs - to be done once per connection */ - - if((conn->handler == &Curl_handler_http2_ssl) || - (conn->handler == &Curl_handler_http2)) - return CURLE_OK; /* already done */ - - if(conn->handler->flags & PROTOPT_SSL) - conn->handler = &Curl_handler_http2_ssl; - else - conn->handler = &Curl_handler_http2; - - result = http2_init(data, conn); - if(result) { - Curl_dyn_free(&stream->header_recvbuf); - return result; - } - - infof(data, "Using HTTP2, server supports multiplexing"); - - conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - conn->httpversion = 20; - conn->bundle->multiuse = BUNDLE_MULTIPLEX; - - httpc->inbuflen = 0; - httpc->nread_inbuf = 0; - - httpc->pause_stream_id = 0; - httpc->drain_total = 0; - - return CURLE_OK; -} - -CURLcode Curl_http2_switched(struct Curl_easy *data, - const char *mem, size_t nread) -{ - CURLcode result; - struct connectdata *conn = data->conn; - struct http_conn *httpc = &conn->proto.httpc; - int rv; - struct HTTP *stream = data->req.p.http; - - result = Curl_http2_setup(data, conn); - if(result) - return result; - - httpc->recv_underlying = conn->recv[FIRSTSOCKET]; - httpc->send_underlying = conn->send[FIRSTSOCKET]; - conn->recv[FIRSTSOCKET] = http2_recv; - conn->send[FIRSTSOCKET] = http2_send; - - if(data->req.upgr101 == UPGR101_RECEIVED) { - /* stream 1 is opened implicitly on upgrade */ - stream->stream_id = 1; - /* queue SETTINGS frame (again) */ - rv = nghttp2_session_upgrade2(httpc->h2, httpc->binsettings, httpc->binlen, - data->state.httpreq == HTTPREQ_HEAD, NULL); - if(rv) { - failf(data, "nghttp2_session_upgrade2() failed: %s(%d)", - nghttp2_strerror(rv), rv); - return CURLE_HTTP2; - } - - rv = nghttp2_session_set_stream_user_data(httpc->h2, - stream->stream_id, - data); - if(rv) { - infof(data, "http/2: failed to set user_data for stream %u", - stream->stream_id); - DEBUGASSERT(0); - } - } - else { - populate_settings(data, httpc); - - /* stream ID is unknown at this point */ - stream->stream_id = -1; - rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE, - httpc->local_settings, - httpc->local_settings_num); - if(rv) { - failf(data, "nghttp2_submit_settings() failed: %s(%d)", - nghttp2_strerror(rv), rv); - return CURLE_HTTP2; - } - } - - rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, 0, - HTTP2_HUGE_WINDOW_SIZE); - if(rv) { - failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", - nghttp2_strerror(rv), rv); - return CURLE_HTTP2; - } - - /* we are going to copy mem to httpc->inbuf. This is required since - mem is part of buffer pointed by stream->mem, and callbacks - called by nghttp2_session_mem_recv() will write stream specific - data into stream->mem, overwriting data already there. */ - if(H2_BUFSIZE < nread) { - failf(data, "connection buffer size is too small to store data following " - "HTTP Upgrade response header: buflen=%d, datalen=%zu", - H2_BUFSIZE, nread); - return CURLE_HTTP2; - } - - infof(data, "Copying HTTP/2 data in stream buffer to connection buffer" - " after upgrade: len=%zu", - nread); - - if(nread) - memcpy(httpc->inbuf, mem, nread); - - httpc->inbuflen = nread; - - DEBUGASSERT(httpc->nread_inbuf == 0); - - if(-1 == h2_process_pending_input(data, httpc, &result)) - return CURLE_HTTP2; - - return CURLE_OK; -} - -CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause) -{ - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - /* if it isn't HTTP/2, we're done */ - if(!(data->conn->handler->protocol & PROTO_FAMILY_HTTP) || - !data->conn->proto.httpc.h2) - return CURLE_OK; -#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE - else { - struct HTTP *stream = data->req.p.http; - struct http_conn *httpc = &data->conn->proto.httpc; - uint32_t window = !pause * HTTP2_HUGE_WINDOW_SIZE; - int rv = nghttp2_session_set_local_window_size(httpc->h2, - NGHTTP2_FLAG_NONE, - stream->stream_id, - window); - if(rv) { - failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", - nghttp2_strerror(rv), rv); - return CURLE_HTTP2; - } - - /* make sure the window update gets sent */ - rv = h2_session_send(data, httpc->h2); - if(rv) - return CURLE_SEND_ERROR; - - DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u", - window, stream->stream_id)); - -#ifdef DEBUGBUILD - { - /* read out the stream local window again */ - uint32_t window2 = - nghttp2_session_get_stream_local_window_size(httpc->h2, - stream->stream_id); - DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u", - window2, stream->stream_id)); - } -#endif - } -#endif - return CURLE_OK; -} - -CURLcode Curl_http2_add_child(struct Curl_easy *parent, - struct Curl_easy *child, - bool exclusive) -{ - if(parent) { - struct Curl_http2_dep **tail; - struct Curl_http2_dep *dep = calloc(1, sizeof(struct Curl_http2_dep)); - if(!dep) - return CURLE_OUT_OF_MEMORY; - dep->data = child; - - if(parent->set.stream_dependents && exclusive) { - struct Curl_http2_dep *node = parent->set.stream_dependents; - while(node) { - node->data->set.stream_depends_on = child; - node = node->next; - } - - tail = &child->set.stream_dependents; - while(*tail) - tail = &(*tail)->next; - - DEBUGASSERT(!*tail); - *tail = parent->set.stream_dependents; - parent->set.stream_dependents = 0; - } - - tail = &parent->set.stream_dependents; - while(*tail) { - (*tail)->data->set.stream_depends_e = FALSE; - tail = &(*tail)->next; - } - - DEBUGASSERT(!*tail); - *tail = dep; - } - - child->set.stream_depends_on = parent; - child->set.stream_depends_e = exclusive; - return CURLE_OK; -} - -void Curl_http2_remove_child(struct Curl_easy *parent, struct Curl_easy *child) -{ - struct Curl_http2_dep *last = 0; - struct Curl_http2_dep *data = parent->set.stream_dependents; - DEBUGASSERT(child->set.stream_depends_on == parent); - - while(data && data->data != child) { - last = data; - data = data->next; - } - - DEBUGASSERT(data); - - if(data) { - if(last) { - last->next = data->next; - } - else { - parent->set.stream_dependents = data->next; - } - free(data); - } - - child->set.stream_depends_on = 0; - child->set.stream_depends_e = FALSE; -} - -void Curl_http2_cleanup_dependencies(struct Curl_easy *data) -{ - while(data->set.stream_dependents) { - struct Curl_easy *tmp = data->set.stream_dependents->data; - Curl_http2_remove_child(data, tmp); - if(data->set.stream_depends_on) - Curl_http2_add_child(data->set.stream_depends_on, tmp, FALSE); - } - - if(data->set.stream_depends_on) - Curl_http2_remove_child(data->set.stream_depends_on, data); -} - -/* Only call this function for a transfer that already got a HTTP/2 - CURLE_HTTP2_STREAM error! */ -bool Curl_h2_http_1_1_error(struct Curl_easy *data) -{ - struct HTTP *stream = data->req.p.http; - return (stream->error == NGHTTP2_HTTP_1_1_REQUIRED); -} - -#else /* !USE_NGHTTP2 */ - -/* Satisfy external references even if http2 is not compiled in. */ -#include <curl/curl.h> - -char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num) -{ - (void) h; - (void) num; - return NULL; -} - -char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header) -{ - (void) h; - (void) header; - return NULL; -} - -#endif /* USE_NGHTTP2 */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_NGHTTP2
+#include <nghttp2/nghttp2.h>
+#include "urldata.h"
+#include "http2.h"
+#include "http.h"
+#include "sendf.h"
+#include "select.h"
+#include "curl_base64.h"
+#include "strcase.h"
+#include "multiif.h"
+#include "url.h"
+#include "connect.h"
+#include "strtoofft.h"
+#include "strdup.h"
+#include "transfer.h"
+#include "dynbuf.h"
+#include "h2h3.h"
+#include "headers.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define H2_BUFSIZE 32768
+
+#if (NGHTTP2_VERSION_NUM < 0x010c00)
+#error too old nghttp2 version, upgrade!
+#endif
+
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+#define nghttp2_session_callbacks_set_error_callback(x,y)
+#endif
+
+#if (NGHTTP2_VERSION_NUM >= 0x010c00)
+#define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1
+#endif
+
+#define HTTP2_HUGE_WINDOW_SIZE (32 * 1024 * 1024) /* 32 MB */
+
+#ifdef DEBUG_HTTP2
+#define H2BUGF(x) x
+#else
+#define H2BUGF(x) do { } while(0)
+#endif
+
+static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
+ char *mem, size_t len, CURLcode *err);
+static bool http2_connisdead(struct Curl_easy *data,
+ struct connectdata *conn);
+static int h2_session_send(struct Curl_easy *data,
+ nghttp2_session *h2);
+static int h2_process_pending_input(struct Curl_easy *data,
+ struct http_conn *httpc,
+ CURLcode *err);
+
+/*
+ * Curl_http2_init_state() is called when the easy handle is created and
+ * allows for HTTP/2 specific init of state.
+ */
+void Curl_http2_init_state(struct UrlState *state)
+{
+ state->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
+}
+
+/*
+ * Curl_http2_init_userset() is called when the easy handle is created and
+ * allows for HTTP/2 specific user-set fields.
+ */
+void Curl_http2_init_userset(struct UserDefined *set)
+{
+ set->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
+}
+
+static int http2_getsock(struct Curl_easy *data,
+ struct connectdata *conn,
+ curl_socket_t *sock)
+{
+ const struct http_conn *c = &conn->proto.httpc;
+ struct SingleRequest *k = &data->req;
+ int bitmap = GETSOCK_BLANK;
+ struct HTTP *stream = data->req.p.http;
+
+ sock[0] = conn->sock[FIRSTSOCKET];
+
+ if(!(k->keepon & KEEP_RECV_PAUSE))
+ /* Unless paused - in an HTTP/2 connection we can basically always get a
+ frame so we should always be ready for one */
+ bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
+
+ /* we're (still uploading OR the HTTP/2 layer wants to send data) AND
+ there's a window to send data in */
+ if((((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) ||
+ nghttp2_session_want_write(c->h2)) &&
+ (nghttp2_session_get_remote_window_size(c->h2) &&
+ nghttp2_session_get_stream_remote_window_size(c->h2,
+ stream->stream_id)))
+ bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
+
+ return bitmap;
+}
+
+/*
+ * http2_stream_free() free HTTP2 stream related data
+ */
+static void http2_stream_free(struct HTTP *http)
+{
+ if(http) {
+ Curl_dyn_free(&http->header_recvbuf);
+ for(; http->push_headers_used > 0; --http->push_headers_used) {
+ free(http->push_headers[http->push_headers_used - 1]);
+ }
+ free(http->push_headers);
+ http->push_headers = NULL;
+ }
+}
+
+/*
+ * Disconnects *a* connection used for HTTP/2. It might be an old one from the
+ * connection cache and not the "main" one. Don't touch the easy handle!
+ */
+
+static CURLcode http2_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection)
+{
+ struct http_conn *c = &conn->proto.httpc;
+ (void)dead_connection;
+#ifndef DEBUG_HTTP2
+ (void)data;
+#endif
+
+ H2BUGF(infof(data, "HTTP/2 DISCONNECT starts now"));
+
+ nghttp2_session_del(c->h2);
+ Curl_safefree(c->inbuf);
+
+ H2BUGF(infof(data, "HTTP/2 DISCONNECT done"));
+
+ return CURLE_OK;
+}
+
+/*
+ * The server may send us data at any point (e.g. PING frames). Therefore,
+ * we cannot assume that an HTTP/2 socket is dead just because it is readable.
+ *
+ * Instead, if it is readable, run Curl_connalive() to peek at the socket
+ * and distinguish between closed and data.
+ */
+static bool http2_connisdead(struct Curl_easy *data, struct connectdata *conn)
+{
+ int sval;
+ bool dead = TRUE;
+
+ if(conn->bits.close)
+ return TRUE;
+
+ sval = SOCKET_READABLE(conn->sock[FIRSTSOCKET], 0);
+ if(sval == 0) {
+ /* timeout */
+ dead = FALSE;
+ }
+ else if(sval & CURL_CSELECT_ERR) {
+ /* socket is in an error state */
+ dead = TRUE;
+ }
+ else if(sval & CURL_CSELECT_IN) {
+ /* readable with no error. could still be closed */
+ dead = !Curl_connalive(data, conn);
+ if(!dead) {
+ /* This happens before we've sent off a request and the connection is
+ not in use by any other transfer, there shouldn't be any data here,
+ only "protocol frames" */
+ CURLcode result;
+ struct http_conn *httpc = &conn->proto.httpc;
+ ssize_t nread = -1;
+ if(httpc->recv_underlying)
+ /* if called "too early", this pointer isn't setup yet! */
+ nread = ((Curl_recv *)httpc->recv_underlying)(
+ data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
+ if(nread != -1) {
+ H2BUGF(infof(data,
+ "%d bytes stray data read before trying h2 connection",
+ (int)nread));
+ httpc->nread_inbuf = 0;
+ httpc->inbuflen = nread;
+ if(h2_process_pending_input(data, httpc, &result) < 0)
+ /* immediate error, considered dead */
+ dead = TRUE;
+ }
+ else
+ /* the read failed so let's say this is dead anyway */
+ dead = TRUE;
+ }
+ }
+
+ return dead;
+}
+
+/*
+ * Set the transfer that is currently using this HTTP/2 connection.
+ */
+static void set_transfer(struct http_conn *c,
+ struct Curl_easy *data)
+{
+ c->trnsfr = data;
+}
+
+/*
+ * Get the transfer that is currently using this HTTP/2 connection.
+ */
+static struct Curl_easy *get_transfer(struct http_conn *c)
+{
+ DEBUGASSERT(c && c->trnsfr);
+ return c->trnsfr;
+}
+
+static unsigned int http2_conncheck(struct Curl_easy *data,
+ struct connectdata *conn,
+ unsigned int checks_to_perform)
+{
+ unsigned int ret_val = CONNRESULT_NONE;
+ struct http_conn *c = &conn->proto.httpc;
+ int rc;
+ bool send_frames = false;
+
+ if(checks_to_perform & CONNCHECK_ISDEAD) {
+ if(http2_connisdead(data, conn))
+ ret_val |= CONNRESULT_DEAD;
+ }
+
+ if(checks_to_perform & CONNCHECK_KEEPALIVE) {
+ struct curltime now = Curl_now();
+ timediff_t elapsed = Curl_timediff(now, conn->keepalive);
+
+ if(elapsed > data->set.upkeep_interval_ms) {
+ /* Perform an HTTP/2 PING */
+ rc = nghttp2_submit_ping(c->h2, 0, ZERO_NULL);
+ if(!rc) {
+ /* Successfully added a PING frame to the session. Need to flag this
+ so the frame is sent. */
+ send_frames = true;
+ }
+ else {
+ failf(data, "nghttp2_submit_ping() failed: %s(%d)",
+ nghttp2_strerror(rc), rc);
+ }
+
+ conn->keepalive = now;
+ }
+ }
+
+ if(send_frames) {
+ set_transfer(c, data); /* set the transfer */
+ rc = nghttp2_session_send(c->h2);
+ if(rc)
+ failf(data, "nghttp2_session_send() failed: %s(%d)",
+ nghttp2_strerror(rc), rc);
+ }
+
+ return ret_val;
+}
+
+/* called from http_setup_conn */
+void Curl_http2_setup_req(struct Curl_easy *data)
+{
+ struct HTTP *http = data->req.p.http;
+ http->bodystarted = FALSE;
+ http->status_code = -1;
+ http->pausedata = NULL;
+ http->pauselen = 0;
+ http->closed = FALSE;
+ http->close_handled = FALSE;
+ http->mem = NULL;
+ http->len = 0;
+ http->memlen = 0;
+ http->error = NGHTTP2_NO_ERROR;
+}
+
+/* called from http_setup_conn */
+void Curl_http2_setup_conn(struct connectdata *conn)
+{
+ conn->proto.httpc.settings.max_concurrent_streams =
+ DEFAULT_MAX_CONCURRENT_STREAMS;
+}
+
+/*
+ * HTTP2 handler interface. This isn't added to the general list of protocols
+ * but will be used at run-time when the protocol is dynamically switched from
+ * HTTP to HTTP2.
+ */
+static const struct Curl_handler Curl_handler_http2 = {
+ "HTTP", /* scheme */
+ ZERO_NULL, /* setup_connection */
+ Curl_http, /* do_it */
+ Curl_http_done, /* done */
+ ZERO_NULL, /* do_more */
+ ZERO_NULL, /* connect_it */
+ ZERO_NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ http2_getsock, /* proto_getsock */
+ http2_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ http2_getsock, /* perform_getsock */
+ http2_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ http2_conncheck, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_HTTP, /* defport */
+ CURLPROTO_HTTP, /* protocol */
+ CURLPROTO_HTTP, /* family */
+ PROTOPT_STREAM /* flags */
+};
+
+static const struct Curl_handler Curl_handler_http2_ssl = {
+ "HTTPS", /* scheme */
+ ZERO_NULL, /* setup_connection */
+ Curl_http, /* do_it */
+ Curl_http_done, /* done */
+ ZERO_NULL, /* do_more */
+ ZERO_NULL, /* connect_it */
+ ZERO_NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ http2_getsock, /* proto_getsock */
+ http2_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ http2_getsock, /* perform_getsock */
+ http2_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ http2_conncheck, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_HTTP, /* defport */
+ CURLPROTO_HTTPS, /* protocol */
+ CURLPROTO_HTTP, /* family */
+ PROTOPT_SSL | PROTOPT_STREAM /* flags */
+};
+
+/*
+ * Store nghttp2 version info in this buffer.
+ */
+void Curl_http2_ver(char *p, size_t len)
+{
+ nghttp2_info *h2 = nghttp2_version(0);
+ (void)msnprintf(p, len, "nghttp2/%s", h2->version_str);
+}
+
+/*
+ * The implementation of nghttp2_send_callback type. Here we write |data| with
+ * size |length| to the network and return the number of bytes actually
+ * written. See the documentation of nghttp2_send_callback for the details.
+ */
+static ssize_t send_callback(nghttp2_session *h2,
+ const uint8_t *mem, size_t length, int flags,
+ void *userp)
+{
+ struct connectdata *conn = (struct connectdata *)userp;
+ struct http_conn *c = &conn->proto.httpc;
+ struct Curl_easy *data = get_transfer(c);
+ ssize_t written;
+ CURLcode result = CURLE_OK;
+
+ (void)h2;
+ (void)flags;
+
+ if(!c->send_underlying)
+ /* called before setup properly! */
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+
+ written = ((Curl_send*)c->send_underlying)(data, FIRSTSOCKET,
+ mem, length, &result);
+
+ if(result == CURLE_AGAIN) {
+ return NGHTTP2_ERR_WOULDBLOCK;
+ }
+
+ if(written == -1) {
+ failf(data, "Failed sending HTTP2 data");
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
+
+ if(!written)
+ return NGHTTP2_ERR_WOULDBLOCK;
+
+ return written;
+}
+
+
+/* We pass a pointer to this struct in the push callback, but the contents of
+ the struct are hidden from the user. */
+struct curl_pushheaders {
+ struct Curl_easy *data;
+ const nghttp2_push_promise *frame;
+};
+
+/*
+ * push header access function. Only to be used from within the push callback
+ */
+char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
+{
+ /* Verify that we got a good easy handle in the push header struct, mostly to
+ detect rubbish input fast(er). */
+ if(!h || !GOOD_EASY_HANDLE(h->data))
+ return NULL;
+ else {
+ struct HTTP *stream = h->data->req.p.http;
+ if(num < stream->push_headers_used)
+ return stream->push_headers[num];
+ }
+ return NULL;
+}
+
+/*
+ * push header access function. Only to be used from within the push callback
+ */
+char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
+{
+ /* Verify that we got a good easy handle in the push header struct,
+ mostly to detect rubbish input fast(er). Also empty header name
+ is just a rubbish too. We have to allow ":" at the beginning of
+ the header, but header == ":" must be rejected. If we have ':' in
+ the middle of header, it could be matched in middle of the value,
+ this is because we do prefix match.*/
+ if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] ||
+ !strcmp(header, ":") || strchr(header + 1, ':'))
+ return NULL;
+ else {
+ struct HTTP *stream = h->data->req.p.http;
+ size_t len = strlen(header);
+ size_t i;
+ for(i = 0; i<stream->push_headers_used; i++) {
+ if(!strncmp(header, stream->push_headers[i], len)) {
+ /* sub-match, make sure that it is followed by a colon */
+ if(stream->push_headers[i][len] != ':')
+ continue;
+ return &stream->push_headers[i][len + 1];
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ * This specific transfer on this connection has been "drained".
+ */
+static void drained_transfer(struct Curl_easy *data,
+ struct http_conn *httpc)
+{
+ DEBUGASSERT(httpc->drain_total >= data->state.drain);
+ httpc->drain_total -= data->state.drain;
+ data->state.drain = 0;
+}
+
+/*
+ * Mark this transfer to get "drained".
+ */
+static void drain_this(struct Curl_easy *data,
+ struct http_conn *httpc)
+{
+ data->state.drain++;
+ httpc->drain_total++;
+ DEBUGASSERT(httpc->drain_total >= data->state.drain);
+}
+
+static struct Curl_easy *duphandle(struct Curl_easy *data)
+{
+ struct Curl_easy *second = curl_easy_duphandle(data);
+ if(second) {
+ /* setup the request struct */
+ struct HTTP *http = calloc(1, sizeof(struct HTTP));
+ if(!http) {
+ (void)Curl_close(&second);
+ }
+ else {
+ second->req.p.http = http;
+ Curl_dyn_init(&http->header_recvbuf, DYN_H2_HEADERS);
+ Curl_http2_setup_req(second);
+ second->state.stream_weight = data->state.stream_weight;
+ }
+ }
+ return second;
+}
+
+static int set_transfer_url(struct Curl_easy *data,
+ struct curl_pushheaders *hp)
+{
+ const char *v;
+ CURLUcode uc;
+ char *url = NULL;
+ int rc = 0;
+ CURLU *u = curl_url();
+
+ if(!u)
+ return 5;
+
+ v = curl_pushheader_byname(hp, H2H3_PSEUDO_SCHEME);
+ if(v) {
+ uc = curl_url_set(u, CURLUPART_SCHEME, v, 0);
+ if(uc) {
+ rc = 1;
+ goto fail;
+ }
+ }
+
+ v = curl_pushheader_byname(hp, H2H3_PSEUDO_AUTHORITY);
+ if(v) {
+ uc = curl_url_set(u, CURLUPART_HOST, v, 0);
+ if(uc) {
+ rc = 2;
+ goto fail;
+ }
+ }
+
+ v = curl_pushheader_byname(hp, H2H3_PSEUDO_PATH);
+ if(v) {
+ uc = curl_url_set(u, CURLUPART_PATH, v, 0);
+ if(uc) {
+ rc = 3;
+ goto fail;
+ }
+ }
+
+ uc = curl_url_get(u, CURLUPART_URL, &url, 0);
+ if(uc)
+ rc = 4;
+ fail:
+ curl_url_cleanup(u);
+ if(rc)
+ return rc;
+
+ if(data->state.url_alloc)
+ free(data->state.url);
+ data->state.url_alloc = TRUE;
+ data->state.url = url;
+ return 0;
+}
+
+static int push_promise(struct Curl_easy *data,
+ struct connectdata *conn,
+ const nghttp2_push_promise *frame)
+{
+ int rv; /* one of the CURL_PUSH_* defines */
+ H2BUGF(infof(data, "PUSH_PROMISE received, stream %u",
+ frame->promised_stream_id));
+ if(data->multi->push_cb) {
+ struct HTTP *stream;
+ struct HTTP *newstream;
+ struct curl_pushheaders heads;
+ CURLMcode rc;
+ struct http_conn *httpc;
+ size_t i;
+ /* clone the parent */
+ struct Curl_easy *newhandle = duphandle(data);
+ if(!newhandle) {
+ infof(data, "failed to duplicate handle");
+ rv = CURL_PUSH_DENY; /* FAIL HARD */
+ goto fail;
+ }
+
+ heads.data = data;
+ heads.frame = frame;
+ /* ask the application */
+ H2BUGF(infof(data, "Got PUSH_PROMISE, ask application"));
+
+ stream = data->req.p.http;
+ if(!stream) {
+ failf(data, "Internal NULL stream");
+ (void)Curl_close(&newhandle);
+ rv = CURL_PUSH_DENY;
+ goto fail;
+ }
+
+ rv = set_transfer_url(newhandle, &heads);
+ if(rv) {
+ (void)Curl_close(&newhandle);
+ rv = CURL_PUSH_DENY;
+ goto fail;
+ }
+
+ Curl_set_in_callback(data, true);
+ rv = data->multi->push_cb(data, newhandle,
+ stream->push_headers_used, &heads,
+ data->multi->push_userp);
+ Curl_set_in_callback(data, false);
+
+ /* free the headers again */
+ for(i = 0; i<stream->push_headers_used; i++)
+ free(stream->push_headers[i]);
+ free(stream->push_headers);
+ stream->push_headers = NULL;
+ stream->push_headers_used = 0;
+
+ if(rv) {
+ DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
+ /* denied, kill off the new handle again */
+ http2_stream_free(newhandle->req.p.http);
+ newhandle->req.p.http = NULL;
+ (void)Curl_close(&newhandle);
+ goto fail;
+ }
+
+ newstream = newhandle->req.p.http;
+ newstream->stream_id = frame->promised_stream_id;
+ newhandle->req.maxdownload = -1;
+ newhandle->req.size = -1;
+
+ /* approved, add to the multi handle and immediately switch to PERFORM
+ state with the given connection !*/
+ rc = Curl_multi_add_perform(data->multi, newhandle, conn);
+ if(rc) {
+ infof(data, "failed to add handle to multi");
+ http2_stream_free(newhandle->req.p.http);
+ newhandle->req.p.http = NULL;
+ Curl_close(&newhandle);
+ rv = CURL_PUSH_DENY;
+ goto fail;
+ }
+
+ httpc = &conn->proto.httpc;
+ rv = nghttp2_session_set_stream_user_data(httpc->h2,
+ frame->promised_stream_id,
+ newhandle);
+ if(rv) {
+ infof(data, "failed to set user_data for stream %u",
+ frame->promised_stream_id);
+ DEBUGASSERT(0);
+ rv = CURL_PUSH_DENY;
+ goto fail;
+ }
+ Curl_dyn_init(&newstream->header_recvbuf, DYN_H2_HEADERS);
+ Curl_dyn_init(&newstream->trailer_recvbuf, DYN_H2_TRAILERS);
+ }
+ else {
+ H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it"));
+ rv = CURL_PUSH_DENY;
+ }
+ fail:
+ return rv;
+}
+
+/*
+ * multi_connchanged() is called to tell that there is a connection in
+ * this multi handle that has changed state (multiplexing become possible, the
+ * number of allowed streams changed or similar), and a subsequent use of this
+ * multi handle should move CONNECT_PEND handles back to CONNECT to have them
+ * retry.
+ */
+static void multi_connchanged(struct Curl_multi *multi)
+{
+ multi->recheckstate = TRUE;
+}
+
+static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
+ void *userp)
+{
+ struct connectdata *conn = (struct connectdata *)userp;
+ struct http_conn *httpc = &conn->proto.httpc;
+ struct Curl_easy *data_s = NULL;
+ struct HTTP *stream = NULL;
+ struct Curl_easy *data = get_transfer(httpc);
+ int rv;
+ size_t left, ncopy;
+ int32_t stream_id = frame->hd.stream_id;
+ CURLcode result;
+
+ if(!stream_id) {
+ /* stream ID zero is for connection-oriented stuff */
+ if(frame->hd.type == NGHTTP2_SETTINGS) {
+ uint32_t max_conn = httpc->settings.max_concurrent_streams;
+ H2BUGF(infof(data, "Got SETTINGS"));
+ httpc->settings.max_concurrent_streams =
+ nghttp2_session_get_remote_settings(
+ session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
+ httpc->settings.enable_push =
+ nghttp2_session_get_remote_settings(
+ session, NGHTTP2_SETTINGS_ENABLE_PUSH);
+ H2BUGF(infof(data, "MAX_CONCURRENT_STREAMS == %d",
+ httpc->settings.max_concurrent_streams));
+ H2BUGF(infof(data, "ENABLE_PUSH == %s",
+ httpc->settings.enable_push?"TRUE":"false"));
+ if(max_conn != httpc->settings.max_concurrent_streams) {
+ /* only signal change if the value actually changed */
+ infof(data,
+ "Connection state changed (MAX_CONCURRENT_STREAMS == %u)!",
+ httpc->settings.max_concurrent_streams);
+ multi_connchanged(data->multi);
+ }
+ }
+ return 0;
+ }
+ data_s = nghttp2_session_get_stream_user_data(session, stream_id);
+ if(!data_s) {
+ H2BUGF(infof(data,
+ "No Curl_easy associated with stream: %u",
+ stream_id));
+ return 0;
+ }
+
+ stream = data_s->req.p.http;
+ if(!stream) {
+ H2BUGF(infof(data_s, "No proto pointer for stream: %u",
+ stream_id));
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
+
+ H2BUGF(infof(data_s, "on_frame_recv() header %x stream %u",
+ frame->hd.type, stream_id));
+
+ switch(frame->hd.type) {
+ case NGHTTP2_DATA:
+ /* If body started on this stream, then receiving DATA is illegal. */
+ if(!stream->bodystarted) {
+ rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
+ stream_id, NGHTTP2_PROTOCOL_ERROR);
+
+ if(nghttp2_is_fatal(rv)) {
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
+ }
+ break;
+ case NGHTTP2_HEADERS:
+ if(stream->bodystarted) {
+ /* Only valid HEADERS after body started is trailer HEADERS. We
+ buffer them in on_header callback. */
+ break;
+ }
+
+ /* nghttp2 guarantees that :status is received, and we store it to
+ stream->status_code. Fuzzing has proven this can still be reached
+ without status code having been set. */
+ if(stream->status_code == -1)
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+
+ /* Only final status code signals the end of header */
+ if(stream->status_code / 100 != 1) {
+ stream->bodystarted = TRUE;
+ stream->status_code = -1;
+ }
+
+ result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("\r\n"));
+ if(result)
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+
+ left = Curl_dyn_len(&stream->header_recvbuf) -
+ stream->nread_header_recvbuf;
+ ncopy = CURLMIN(stream->len, left);
+
+ memcpy(&stream->mem[stream->memlen],
+ Curl_dyn_ptr(&stream->header_recvbuf) +
+ stream->nread_header_recvbuf,
+ ncopy);
+ stream->nread_header_recvbuf += ncopy;
+
+ DEBUGASSERT(stream->mem);
+ H2BUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p",
+ ncopy, stream_id, stream->mem));
+
+ stream->len -= ncopy;
+ stream->memlen += ncopy;
+
+ drain_this(data_s, httpc);
+ /* if we receive data for another handle, wake that up */
+ if(get_transfer(httpc) != data_s)
+ Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
+ break;
+ case NGHTTP2_PUSH_PROMISE:
+ rv = push_promise(data_s, conn, &frame->push_promise);
+ if(rv) { /* deny! */
+ int h2;
+ DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
+ h2 = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
+ frame->push_promise.promised_stream_id,
+ NGHTTP2_CANCEL);
+ if(nghttp2_is_fatal(h2))
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ else if(rv == CURL_PUSH_ERROROUT) {
+ DEBUGF(infof(data_s, "Fail the parent stream (too)"));
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
+ }
+ break;
+ default:
+ H2BUGF(infof(data_s, "Got frame type %x for stream %u",
+ frame->hd.type, stream_id));
+ break;
+ }
+ return 0;
+}
+
+static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
+ int32_t stream_id,
+ const uint8_t *mem, size_t len, void *userp)
+{
+ struct HTTP *stream;
+ struct Curl_easy *data_s;
+ size_t nread;
+ struct connectdata *conn = (struct connectdata *)userp;
+ struct http_conn *httpc = &conn->proto.httpc;
+ (void)session;
+ (void)flags;
+
+ DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
+
+ /* get the stream from the hash based on Stream ID */
+ data_s = nghttp2_session_get_stream_user_data(session, stream_id);
+ if(!data_s) {
+ /* Receiving a Stream ID not in the hash should not happen - unless
+ we have aborted a transfer artificially and there were more data
+ in the pipeline. Silently ignore. */
+ H2BUGF(fprintf(stderr, "Data for stream %u but it doesn't exist\n",
+ stream_id));
+ return 0;
+ }
+
+ stream = data_s->req.p.http;
+ if(!stream)
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+
+ nread = CURLMIN(stream->len, len);
+ memcpy(&stream->mem[stream->memlen], mem, nread);
+
+ stream->len -= nread;
+ stream->memlen += nread;
+
+ drain_this(data_s, &conn->proto.httpc);
+
+ /* if we receive data for another handle, wake that up */
+ if(get_transfer(httpc) != data_s)
+ Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
+
+ H2BUGF(infof(data_s, "%zu data received for stream %u "
+ "(%zu left in buffer %p, total %zu)",
+ nread, stream_id,
+ stream->len, stream->mem,
+ stream->memlen));
+
+ if(nread < len) {
+ stream->pausedata = mem + nread;
+ stream->pauselen = len - nread;
+ H2BUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
+ ", stream %u",
+ len - nread, stream_id));
+ data_s->conn->proto.httpc.pause_stream_id = stream_id;
+
+ return NGHTTP2_ERR_PAUSE;
+ }
+
+ /* pause execution of nghttp2 if we received data for another handle
+ in order to process them first. */
+ if(get_transfer(httpc) != data_s) {
+ data_s->conn->proto.httpc.pause_stream_id = stream_id;
+
+ return NGHTTP2_ERR_PAUSE;
+ }
+
+ return 0;
+}
+
+static int on_stream_close(nghttp2_session *session, int32_t stream_id,
+ uint32_t error_code, void *userp)
+{
+ struct Curl_easy *data_s;
+ struct HTTP *stream;
+ struct connectdata *conn = (struct connectdata *)userp;
+ int rv;
+ (void)session;
+ (void)stream_id;
+
+ if(stream_id) {
+ struct http_conn *httpc;
+ /* get the stream from the hash based on Stream ID, stream ID zero is for
+ connection-oriented stuff */
+ data_s = nghttp2_session_get_stream_user_data(session, stream_id);
+ if(!data_s) {
+ /* We could get stream ID not in the hash. For example, if we
+ decided to reject stream (e.g., PUSH_PROMISE). */
+ return 0;
+ }
+ H2BUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u",
+ nghttp2_http2_strerror(error_code), error_code, stream_id));
+ stream = data_s->req.p.http;
+ if(!stream)
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+
+ stream->closed = TRUE;
+ httpc = &conn->proto.httpc;
+ drain_this(data_s, httpc);
+ Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
+ stream->error = error_code;
+
+ /* remove the entry from the hash as the stream is now gone */
+ rv = nghttp2_session_set_stream_user_data(session, stream_id, 0);
+ if(rv) {
+ infof(data_s, "http/2: failed to clear user_data for stream %u",
+ stream_id);
+ DEBUGASSERT(0);
+ }
+ if(stream_id == httpc->pause_stream_id) {
+ H2BUGF(infof(data_s, "Stopped the pause stream"));
+ httpc->pause_stream_id = 0;
+ }
+ H2BUGF(infof(data_s, "Removed stream %u hash", stream_id));
+ stream->stream_id = 0; /* cleared */
+ }
+ return 0;
+}
+
+static int on_begin_headers(nghttp2_session *session,
+ const nghttp2_frame *frame, void *userp)
+{
+ struct HTTP *stream;
+ struct Curl_easy *data_s = NULL;
+ (void)userp;
+
+ data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
+ if(!data_s) {
+ return 0;
+ }
+
+ H2BUGF(infof(data_s, "on_begin_headers() was called"));
+
+ if(frame->hd.type != NGHTTP2_HEADERS) {
+ return 0;
+ }
+
+ stream = data_s->req.p.http;
+ if(!stream || !stream->bodystarted) {
+ return 0;
+ }
+
+ return 0;
+}
+
+/* Decode HTTP status code. Returns -1 if no valid status code was
+ decoded. */
+static int decode_status_code(const uint8_t *value, size_t len)
+{
+ int i;
+ int res;
+
+ if(len != 3) {
+ return -1;
+ }
+
+ res = 0;
+
+ for(i = 0; i < 3; ++i) {
+ char c = value[i];
+
+ if(c < '0' || c > '9') {
+ return -1;
+ }
+
+ res *= 10;
+ res += c - '0';
+ }
+
+ return res;
+}
+
+/* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
+static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
+ const uint8_t *name, size_t namelen,
+ const uint8_t *value, size_t valuelen,
+ uint8_t flags,
+ void *userp)
+{
+ struct HTTP *stream;
+ struct Curl_easy *data_s;
+ int32_t stream_id = frame->hd.stream_id;
+ struct connectdata *conn = (struct connectdata *)userp;
+ struct http_conn *httpc = &conn->proto.httpc;
+ CURLcode result;
+ (void)flags;
+
+ DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
+
+ /* get the stream from the hash based on Stream ID */
+ data_s = nghttp2_session_get_stream_user_data(session, stream_id);
+ if(!data_s)
+ /* Receiving a Stream ID not in the hash should not happen, this is an
+ internal error more than anything else! */
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+
+ stream = data_s->req.p.http;
+ if(!stream) {
+ failf(data_s, "Internal NULL stream");
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
+
+ /* Store received PUSH_PROMISE headers to be used when the subsequent
+ PUSH_PROMISE callback comes */
+ if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
+ char *h;
+
+ if(!strcmp(H2H3_PSEUDO_AUTHORITY, (const char *)name)) {
+ /* pseudo headers are lower case */
+ int rc = 0;
+ char *check = aprintf("%s:%d", conn->host.name, conn->remote_port);
+ if(!check)
+ /* no memory */
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ if(!strcasecompare(check, (const char *)value) &&
+ ((conn->remote_port != conn->given->defport) ||
+ !strcasecompare(conn->host.name, (const char *)value))) {
+ /* This is push is not for the same authority that was asked for in
+ * the URL. RFC 7540 section 8.2 says: "A client MUST treat a
+ * PUSH_PROMISE for which the server is not authoritative as a stream
+ * error of type PROTOCOL_ERROR."
+ */
+ (void)nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
+ stream_id, NGHTTP2_PROTOCOL_ERROR);
+ rc = NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
+ free(check);
+ if(rc)
+ return rc;
+ }
+
+ if(!stream->push_headers) {
+ stream->push_headers_alloc = 10;
+ stream->push_headers = malloc(stream->push_headers_alloc *
+ sizeof(char *));
+ if(!stream->push_headers)
+ return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
+ stream->push_headers_used = 0;
+ }
+ else if(stream->push_headers_used ==
+ stream->push_headers_alloc) {
+ char **headp;
+ if(stream->push_headers_alloc > 1000) {
+ /* this is beyond crazy many headers, bail out */
+ failf(data_s, "Too many PUSH_PROMISE headers");
+ Curl_safefree(stream->push_headers);
+ return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
+ }
+ stream->push_headers_alloc *= 2;
+ headp = Curl_saferealloc(stream->push_headers,
+ stream->push_headers_alloc * sizeof(char *));
+ if(!headp) {
+ stream->push_headers = NULL;
+ return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
+ }
+ stream->push_headers = headp;
+ }
+ h = aprintf("%s:%s", name, value);
+ if(h)
+ stream->push_headers[stream->push_headers_used++] = h;
+ return 0;
+ }
+
+ if(stream->bodystarted) {
+ /* This is a trailer */
+ H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s", namelen, name, valuelen,
+ value));
+ result = Curl_dyn_addf(&stream->trailer_recvbuf,
+ "%.*s: %.*s\r\n", namelen, name,
+ valuelen, value);
+ if(result)
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+
+ return 0;
+ }
+
+ if(namelen == sizeof(H2H3_PSEUDO_STATUS) - 1 &&
+ memcmp(H2H3_PSEUDO_STATUS, name, namelen) == 0) {
+ /* nghttp2 guarantees :status is received first and only once, and
+ value is 3 digits status code, and decode_status_code always
+ succeeds. */
+ char buffer[32];
+ stream->status_code = decode_status_code(value, valuelen);
+ DEBUGASSERT(stream->status_code != -1);
+ msnprintf(buffer, sizeof(buffer), H2H3_PSEUDO_STATUS ":%u\r",
+ stream->status_code);
+ result = Curl_headers_push(data_s, buffer, CURLH_PSEUDO);
+ if(result)
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("HTTP/2 "));
+ if(result)
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ result = Curl_dyn_addn(&stream->header_recvbuf, value, valuelen);
+ if(result)
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ /* the space character after the status code is mandatory */
+ result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST(" \r\n"));
+ if(result)
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ /* if we receive data for another handle, wake that up */
+ if(get_transfer(httpc) != data_s)
+ Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
+
+ H2BUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)",
+ stream->status_code, data_s));
+ return 0;
+ }
+
+ /* nghttp2 guarantees that namelen > 0, and :status was already
+ received, and this is not pseudo-header field . */
+ /* convert to an HTTP1-style header */
+ result = Curl_dyn_addn(&stream->header_recvbuf, name, namelen);
+ if(result)
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST(": "));
+ if(result)
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ result = Curl_dyn_addn(&stream->header_recvbuf, value, valuelen);
+ if(result)
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("\r\n"));
+ if(result)
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ /* if we receive data for another handle, wake that up */
+ if(get_transfer(httpc) != data_s)
+ Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
+
+ H2BUGF(infof(data_s, "h2 header: %.*s: %.*s", namelen, name, valuelen,
+ value));
+
+ return 0; /* 0 is successful */
+}
+
+static ssize_t data_source_read_callback(nghttp2_session *session,
+ int32_t stream_id,
+ uint8_t *buf, size_t length,
+ uint32_t *data_flags,
+ nghttp2_data_source *source,
+ void *userp)
+{
+ struct Curl_easy *data_s;
+ struct HTTP *stream = NULL;
+ size_t nread;
+ (void)source;
+ (void)userp;
+
+ if(stream_id) {
+ /* get the stream from the hash based on Stream ID, stream ID zero is for
+ connection-oriented stuff */
+ data_s = nghttp2_session_get_stream_user_data(session, stream_id);
+ if(!data_s)
+ /* Receiving a Stream ID not in the hash should not happen, this is an
+ internal error more than anything else! */
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+
+ stream = data_s->req.p.http;
+ if(!stream)
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
+ else
+ return NGHTTP2_ERR_INVALID_ARGUMENT;
+
+ nread = CURLMIN(stream->upload_len, length);
+ if(nread > 0) {
+ memcpy(buf, stream->upload_mem, nread);
+ stream->upload_mem += nread;
+ stream->upload_len -= nread;
+ if(data_s->state.infilesize != -1)
+ stream->upload_left -= nread;
+ }
+
+ if(stream->upload_left == 0)
+ *data_flags = NGHTTP2_DATA_FLAG_EOF;
+ else if(nread == 0)
+ return NGHTTP2_ERR_DEFERRED;
+
+ H2BUGF(infof(data_s, "data_source_read_callback: "
+ "returns %zu bytes stream %u",
+ nread, stream_id));
+
+ return nread;
+}
+
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
+static int error_callback(nghttp2_session *session,
+ const char *msg,
+ size_t len,
+ void *userp)
+{
+ (void)session;
+ (void)msg;
+ (void)len;
+ (void)userp;
+ return 0;
+}
+#endif
+
+static void populate_settings(struct Curl_easy *data,
+ struct http_conn *httpc)
+{
+ nghttp2_settings_entry *iv = httpc->local_settings;
+
+ iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
+ iv[0].value = Curl_multi_max_concurrent_streams(data->multi);
+
+ iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
+ iv[1].value = HTTP2_HUGE_WINDOW_SIZE;
+
+ iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
+ iv[2].value = data->multi->push_cb != NULL;
+
+ httpc->local_settings_num = 3;
+}
+
+void Curl_http2_done(struct Curl_easy *data, bool premature)
+{
+ struct HTTP *http = data->req.p.http;
+ struct http_conn *httpc = &data->conn->proto.httpc;
+
+ /* there might be allocated resources done before this got the 'h2' pointer
+ setup */
+ Curl_dyn_free(&http->header_recvbuf);
+ Curl_dyn_free(&http->trailer_recvbuf);
+ if(http->push_headers) {
+ /* if they weren't used and then freed before */
+ for(; http->push_headers_used > 0; --http->push_headers_used) {
+ free(http->push_headers[http->push_headers_used - 1]);
+ }
+ free(http->push_headers);
+ http->push_headers = NULL;
+ }
+
+ if(!(data->conn->handler->protocol&PROTO_FAMILY_HTTP) ||
+ !httpc->h2) /* not HTTP/2 ? */
+ return;
+
+ /* do this before the reset handling, as that might clear ->stream_id */
+ if(http->stream_id == httpc->pause_stream_id) {
+ H2BUGF(infof(data, "DONE the pause stream (%u)", http->stream_id));
+ httpc->pause_stream_id = 0;
+ }
+ if(premature || (!http->closed && http->stream_id)) {
+ /* RST_STREAM */
+ set_transfer(httpc, data); /* set the transfer */
+ H2BUGF(infof(data, "RST stream %u", http->stream_id));
+ if(!nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE,
+ http->stream_id, NGHTTP2_STREAM_CLOSED))
+ (void)nghttp2_session_send(httpc->h2);
+ }
+
+ if(data->state.drain)
+ drained_transfer(data, httpc);
+
+ /* -1 means unassigned and 0 means cleared */
+ if(http->stream_id > 0) {
+ int rv = nghttp2_session_set_stream_user_data(httpc->h2,
+ http->stream_id, 0);
+ if(rv) {
+ infof(data, "http/2: failed to clear user_data for stream %u",
+ http->stream_id);
+ DEBUGASSERT(0);
+ }
+ set_transfer(httpc, NULL);
+ http->stream_id = 0;
+ }
+}
+
+static int client_new(struct connectdata *conn,
+ nghttp2_session_callbacks *callbacks)
+{
+#if NGHTTP2_VERSION_NUM < 0x013200
+ /* before 1.50.0 */
+ return nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
+#else
+ nghttp2_option *o;
+ int rc = nghttp2_option_new(&o);
+ if(rc)
+ return rc;
+ /* turn off RFC 9113 leading and trailing white spaces validation against
+ HTTP field value. */
+ nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1);
+ rc = nghttp2_session_client_new2(&conn->proto.httpc.h2, callbacks, conn,
+ o);
+ nghttp2_option_del(o);
+ return rc;
+#endif
+}
+
+/*
+ * Initialize nghttp2 for a Curl connection
+ */
+static CURLcode http2_init(struct Curl_easy *data, struct connectdata *conn)
+{
+ if(!conn->proto.httpc.h2) {
+ int rc;
+ nghttp2_session_callbacks *callbacks;
+
+ conn->proto.httpc.inbuf = malloc(H2_BUFSIZE);
+ if(!conn->proto.httpc.inbuf)
+ return CURLE_OUT_OF_MEMORY;
+
+ rc = nghttp2_session_callbacks_new(&callbacks);
+
+ if(rc) {
+ failf(data, "Couldn't initialize nghttp2 callbacks");
+ return CURLE_OUT_OF_MEMORY; /* most likely at least */
+ }
+
+ /* nghttp2_send_callback */
+ nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
+ /* nghttp2_on_frame_recv_callback */
+ nghttp2_session_callbacks_set_on_frame_recv_callback
+ (callbacks, on_frame_recv);
+ /* nghttp2_on_data_chunk_recv_callback */
+ nghttp2_session_callbacks_set_on_data_chunk_recv_callback
+ (callbacks, on_data_chunk_recv);
+ /* nghttp2_on_stream_close_callback */
+ nghttp2_session_callbacks_set_on_stream_close_callback
+ (callbacks, on_stream_close);
+ /* nghttp2_on_begin_headers_callback */
+ nghttp2_session_callbacks_set_on_begin_headers_callback
+ (callbacks, on_begin_headers);
+ /* nghttp2_on_header_callback */
+ nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
+
+ nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
+
+ /* The nghttp2 session is not yet setup, do it */
+ rc = client_new(conn, callbacks);
+
+ nghttp2_session_callbacks_del(callbacks);
+
+ if(rc) {
+ failf(data, "Couldn't initialize nghttp2");
+ return CURLE_OUT_OF_MEMORY; /* most likely at least */
+ }
+ }
+ return CURLE_OK;
+}
+
+/*
+ * Append headers to ask for an HTTP1.1 to HTTP2 upgrade.
+ */
+CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
+ struct Curl_easy *data)
+{
+ CURLcode result;
+ ssize_t binlen;
+ char *base64;
+ size_t blen;
+ struct connectdata *conn = data->conn;
+ struct SingleRequest *k = &data->req;
+ uint8_t *binsettings = conn->proto.httpc.binsettings;
+ struct http_conn *httpc = &conn->proto.httpc;
+
+ populate_settings(data, httpc);
+
+ /* this returns number of bytes it wrote */
+ binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
+ httpc->local_settings,
+ httpc->local_settings_num);
+ if(binlen <= 0) {
+ failf(data, "nghttp2 unexpectedly failed on pack_settings_payload");
+ Curl_dyn_free(req);
+ return CURLE_FAILED_INIT;
+ }
+ conn->proto.httpc.binlen = binlen;
+
+ result = Curl_base64url_encode((const char *)binsettings, binlen,
+ &base64, &blen);
+ if(result) {
+ Curl_dyn_free(req);
+ return result;
+ }
+
+ result = Curl_dyn_addf(req,
+ "Connection: Upgrade, HTTP2-Settings\r\n"
+ "Upgrade: %s\r\n"
+ "HTTP2-Settings: %s\r\n",
+ NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
+ free(base64);
+
+ k->upgr101 = UPGR101_H2;
+
+ return result;
+}
+
+/*
+ * Returns nonzero if current HTTP/2 session should be closed.
+ */
+static int should_close_session(struct http_conn *httpc)
+{
+ return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) &&
+ !nghttp2_session_want_write(httpc->h2);
+}
+
+/*
+ * h2_process_pending_input() processes pending input left in
+ * httpc->inbuf. Then, call h2_session_send() to send pending data.
+ * This function returns 0 if it succeeds, or -1 and error code will
+ * be assigned to *err.
+ */
+static int h2_process_pending_input(struct Curl_easy *data,
+ struct http_conn *httpc,
+ CURLcode *err)
+{
+ ssize_t nread;
+ char *inbuf;
+ ssize_t rv;
+
+ nread = httpc->inbuflen - httpc->nread_inbuf;
+ inbuf = httpc->inbuf + httpc->nread_inbuf;
+
+ set_transfer(httpc, data); /* set the transfer */
+ rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
+ if(rv < 0) {
+ failf(data,
+ "h2_process_pending_input: nghttp2_session_mem_recv() returned "
+ "%zd:%s", rv, nghttp2_strerror((int)rv));
+ *err = CURLE_RECV_ERROR;
+ return -1;
+ }
+
+ if(nread == rv) {
+ H2BUGF(infof(data,
+ "h2_process_pending_input: All data in connection buffer "
+ "processed"));
+ httpc->inbuflen = 0;
+ httpc->nread_inbuf = 0;
+ }
+ else {
+ httpc->nread_inbuf += rv;
+ H2BUGF(infof(data,
+ "h2_process_pending_input: %zu bytes left in connection "
+ "buffer",
+ httpc->inbuflen - httpc->nread_inbuf));
+ }
+
+ rv = h2_session_send(data, httpc->h2);
+ if(rv) {
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+
+ if(nghttp2_session_check_request_allowed(httpc->h2) == 0) {
+ /* No more requests are allowed in the current session, so
+ the connection may not be reused. This is set when a
+ GOAWAY frame has been received or when the limit of stream
+ identifiers has been reached. */
+ connclose(data->conn, "http/2: No new requests allowed");
+ }
+
+ if(should_close_session(httpc)) {
+ struct HTTP *stream = data->req.p.http;
+ H2BUGF(infof(data,
+ "h2_process_pending_input: nothing to do in this session"));
+ if(stream->error)
+ *err = CURLE_HTTP2;
+ else {
+ /* not an error per se, but should still close the connection */
+ connclose(data->conn, "GOAWAY received");
+ *err = CURLE_OK;
+ }
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Called from transfer.c:done_sending when we stop uploading.
+ */
+CURLcode Curl_http2_done_sending(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+
+ if((conn->handler == &Curl_handler_http2_ssl) ||
+ (conn->handler == &Curl_handler_http2)) {
+ /* make sure this is only attempted for HTTP/2 transfers */
+ struct HTTP *stream = data->req.p.http;
+ struct http_conn *httpc = &conn->proto.httpc;
+ nghttp2_session *h2 = httpc->h2;
+
+ if(stream->upload_left) {
+ /* If the stream still thinks there's data left to upload. */
+
+ stream->upload_left = 0; /* DONE! */
+
+ /* resume sending here to trigger the callback to get called again so
+ that it can signal EOF to nghttp2 */
+ (void)nghttp2_session_resume_data(h2, stream->stream_id);
+ (void)h2_process_pending_input(data, httpc, &result);
+ }
+
+ /* If nghttp2 still has pending frames unsent */
+ if(nghttp2_session_want_write(h2)) {
+ struct SingleRequest *k = &data->req;
+ int rv;
+
+ H2BUGF(infof(data, "HTTP/2 still wants to send data (easy %p)", data));
+
+ /* and attempt to send the pending frames */
+ rv = h2_session_send(data, h2);
+ if(rv)
+ result = CURLE_SEND_ERROR;
+
+ if(nghttp2_session_want_write(h2)) {
+ /* re-set KEEP_SEND to make sure we are called again */
+ k->keepon |= KEEP_SEND;
+ }
+ }
+ }
+ return result;
+}
+
+static ssize_t http2_handle_stream_close(struct connectdata *conn,
+ struct Curl_easy *data,
+ struct HTTP *stream, CURLcode *err)
+{
+ struct http_conn *httpc = &conn->proto.httpc;
+
+ if(httpc->pause_stream_id == stream->stream_id) {
+ httpc->pause_stream_id = 0;
+ }
+
+ drained_transfer(data, httpc);
+
+ if(httpc->pause_stream_id == 0) {
+ if(h2_process_pending_input(data, httpc, err) != 0) {
+ return -1;
+ }
+ }
+
+ DEBUGASSERT(data->state.drain == 0);
+
+ /* Reset to FALSE to prevent infinite loop in readwrite_data function. */
+ stream->closed = FALSE;
+ if(stream->error == NGHTTP2_REFUSED_STREAM) {
+ H2BUGF(infof(data, "REFUSED_STREAM (%u), try again on a new connection",
+ stream->stream_id));
+ connclose(conn, "REFUSED_STREAM"); /* don't use this anymore */
+ data->state.refused_stream = TRUE;
+ *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */
+ return -1;
+ }
+ else if(stream->error != NGHTTP2_NO_ERROR) {
+ failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)",
+ stream->stream_id, nghttp2_http2_strerror(stream->error),
+ stream->error);
+ *err = CURLE_HTTP2_STREAM;
+ return -1;
+ }
+
+ if(!stream->bodystarted) {
+ failf(data, "HTTP/2 stream %u was closed cleanly, but before getting "
+ " all response header fields, treated as error",
+ stream->stream_id);
+ *err = CURLE_HTTP2_STREAM;
+ return -1;
+ }
+
+ if(Curl_dyn_len(&stream->trailer_recvbuf)) {
+ char *trailp = Curl_dyn_ptr(&stream->trailer_recvbuf);
+ char *lf;
+
+ do {
+ size_t len = 0;
+ CURLcode result;
+ /* each trailer line ends with a newline */
+ lf = strchr(trailp, '\n');
+ if(!lf)
+ break;
+ len = lf + 1 - trailp;
+
+ Curl_debug(data, CURLINFO_HEADER_IN, trailp, len);
+ /* pass the trailers one by one to the callback */
+ result = Curl_client_write(data, CLIENTWRITE_HEADER, trailp, len);
+ if(result) {
+ *err = result;
+ return -1;
+ }
+ trailp = ++lf;
+ } while(lf);
+ }
+
+ stream->close_handled = TRUE;
+
+ H2BUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close"));
+ return 0;
+}
+
+/*
+ * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
+ * and dependency to the peer. It also stores the updated values in the state
+ * struct.
+ */
+
+static void h2_pri_spec(struct Curl_easy *data,
+ nghttp2_priority_spec *pri_spec)
+{
+ struct HTTP *depstream = (data->set.stream_depends_on?
+ data->set.stream_depends_on->req.p.http:NULL);
+ int32_t depstream_id = depstream? depstream->stream_id:0;
+ nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_weight,
+ data->set.stream_depends_e);
+ data->state.stream_weight = data->set.stream_weight;
+ data->state.stream_depends_e = data->set.stream_depends_e;
+ data->state.stream_depends_on = data->set.stream_depends_on;
+}
+
+/*
+ * h2_session_send() checks if there's been an update in the priority /
+ * dependency settings and if so it submits a PRIORITY frame with the updated
+ * info.
+ */
+static int h2_session_send(struct Curl_easy *data,
+ nghttp2_session *h2)
+{
+ struct HTTP *stream = data->req.p.http;
+ struct http_conn *httpc = &data->conn->proto.httpc;
+ set_transfer(httpc, data);
+ if((data->set.stream_weight != data->state.stream_weight) ||
+ (data->set.stream_depends_e != data->state.stream_depends_e) ||
+ (data->set.stream_depends_on != data->state.stream_depends_on) ) {
+ /* send new weight and/or dependency */
+ nghttp2_priority_spec pri_spec;
+ int rv;
+
+ h2_pri_spec(data, &pri_spec);
+
+ H2BUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)",
+ stream->stream_id, data));
+ DEBUGASSERT(stream->stream_id != -1);
+ rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
+ &pri_spec);
+ if(rv)
+ return rv;
+ }
+
+ return nghttp2_session_send(h2);
+}
+
+static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
+ char *mem, size_t len, CURLcode *err)
+{
+ ssize_t nread;
+ struct connectdata *conn = data->conn;
+ struct http_conn *httpc = &conn->proto.httpc;
+ struct HTTP *stream = data->req.p.http;
+
+ (void)sockindex; /* we always do HTTP2 on sockindex 0 */
+
+ if(should_close_session(httpc)) {
+ H2BUGF(infof(data,
+ "http2_recv: nothing to do in this session"));
+ if(conn->bits.close) {
+ /* already marked for closure, return OK and we're done */
+ *err = CURLE_OK;
+ return 0;
+ }
+ *err = CURLE_HTTP2;
+ return -1;
+ }
+
+ /* Nullify here because we call nghttp2_session_send() and they
+ might refer to the old buffer. */
+ stream->upload_mem = NULL;
+ stream->upload_len = 0;
+
+ /*
+ * At this point 'stream' is just in the Curl_easy the connection
+ * identifies as its owner at this time.
+ */
+
+ if(stream->bodystarted &&
+ stream->nread_header_recvbuf < Curl_dyn_len(&stream->header_recvbuf)) {
+ /* If there is header data pending for this stream to return, do that */
+ size_t left =
+ Curl_dyn_len(&stream->header_recvbuf) - stream->nread_header_recvbuf;
+ size_t ncopy = CURLMIN(len, left);
+ memcpy(mem, Curl_dyn_ptr(&stream->header_recvbuf) +
+ stream->nread_header_recvbuf, ncopy);
+ stream->nread_header_recvbuf += ncopy;
+
+ H2BUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf",
+ (int)ncopy));
+ return ncopy;
+ }
+
+ H2BUGF(infof(data, "http2_recv: easy %p (stream %u) win %u/%u",
+ data, stream->stream_id,
+ nghttp2_session_get_local_window_size(httpc->h2),
+ nghttp2_session_get_stream_local_window_size(httpc->h2,
+ stream->stream_id)
+ ));
+
+ if((data->state.drain) && stream->memlen) {
+ H2BUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u (%p => %p)",
+ stream->memlen, stream->stream_id,
+ stream->mem, mem));
+ if(mem != stream->mem) {
+ /* if we didn't get the same buffer this time, we must move the data to
+ the beginning */
+ memmove(mem, stream->mem, stream->memlen);
+ stream->len = len - stream->memlen;
+ stream->mem = mem;
+ }
+ if(httpc->pause_stream_id == stream->stream_id && !stream->pausedata) {
+ /* We have paused nghttp2, but we have no pause data (see
+ on_data_chunk_recv). */
+ httpc->pause_stream_id = 0;
+ if(h2_process_pending_input(data, httpc, err) != 0) {
+ return -1;
+ }
+ }
+ }
+ else if(stream->pausedata) {
+ DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
+ nread = CURLMIN(len, stream->pauselen);
+ memcpy(mem, stream->pausedata, nread);
+
+ stream->pausedata += nread;
+ stream->pauselen -= nread;
+
+ if(stream->pauselen == 0) {
+ H2BUGF(infof(data, "Unpaused by stream %u", stream->stream_id));
+ DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
+ httpc->pause_stream_id = 0;
+
+ stream->pausedata = NULL;
+ stream->pauselen = 0;
+
+ /* When NGHTTP2_ERR_PAUSE is returned from
+ data_source_read_callback, we might not process DATA frame
+ fully. Calling nghttp2_session_mem_recv() again will
+ continue to process DATA frame, but if there is no incoming
+ frames, then we have to call it again with 0-length data.
+ Without this, on_stream_close callback will not be called,
+ and stream could be hanged. */
+ if(h2_process_pending_input(data, httpc, err) != 0) {
+ return -1;
+ }
+ }
+ H2BUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u",
+ nread, stream->stream_id));
+ return nread;
+ }
+ else if(httpc->pause_stream_id) {
+ /* If a stream paused nghttp2_session_mem_recv previously, and has
+ not processed all data, it still refers to the buffer in
+ nghttp2_session. If we call nghttp2_session_mem_recv(), we may
+ overwrite that buffer. To avoid that situation, just return
+ here with CURLE_AGAIN. This could be busy loop since data in
+ socket is not read. But it seems that usually streams are
+ notified with its drain property, and socket is read again
+ quickly. */
+ if(stream->closed)
+ /* closed overrides paused */
+ return 0;
+ H2BUGF(infof(data, "stream %u is paused, pause id: %u",
+ stream->stream_id, httpc->pause_stream_id));
+ *err = CURLE_AGAIN;
+ return -1;
+ }
+ else {
+ /* remember where to store incoming data for this stream and how big the
+ buffer is */
+ stream->mem = mem;
+ stream->len = len;
+ stream->memlen = 0;
+
+ if(httpc->inbuflen == 0) {
+ nread = ((Curl_recv *)httpc->recv_underlying)(
+ data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, err);
+
+ if(nread == -1) {
+ if(*err != CURLE_AGAIN)
+ failf(data, "Failed receiving HTTP2 data");
+ else if(stream->closed)
+ /* received when the stream was already closed! */
+ return http2_handle_stream_close(conn, data, stream, err);
+
+ return -1;
+ }
+
+ if(nread == 0) {
+ if(!stream->closed) {
+ /* This will happen when the server or proxy server is SIGKILLed
+ during data transfer. We should emit an error since our data
+ received may be incomplete. */
+ failf(data, "HTTP/2 stream %u was not closed cleanly before"
+ " end of the underlying stream",
+ stream->stream_id);
+ *err = CURLE_HTTP2_STREAM;
+ return -1;
+ }
+
+ H2BUGF(infof(data, "end of stream"));
+ *err = CURLE_OK;
+ return 0;
+ }
+
+ H2BUGF(infof(data, "nread=%zd", nread));
+
+ httpc->inbuflen = nread;
+
+ DEBUGASSERT(httpc->nread_inbuf == 0);
+ }
+ else {
+ nread = httpc->inbuflen - httpc->nread_inbuf;
+ (void)nread; /* silence warning, used in debug */
+ H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd",
+ nread));
+ }
+
+ if(h2_process_pending_input(data, httpc, err))
+ return -1;
+ }
+ if(stream->memlen) {
+ ssize_t retlen = stream->memlen;
+ H2BUGF(infof(data, "http2_recv: returns %zd for stream %u",
+ retlen, stream->stream_id));
+ stream->memlen = 0;
+
+ if(httpc->pause_stream_id == stream->stream_id) {
+ /* data for this stream is returned now, but this stream caused a pause
+ already so we need it called again asap */
+ H2BUGF(infof(data, "Data returned for PAUSED stream %u",
+ stream->stream_id));
+ }
+ else if(!stream->closed) {
+ drained_transfer(data, httpc);
+ }
+ else
+ /* this stream is closed, trigger a another read ASAP to detect that */
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+
+ return retlen;
+ }
+ if(stream->closed)
+ return http2_handle_stream_close(conn, data, stream, err);
+ *err = CURLE_AGAIN;
+ H2BUGF(infof(data, "http2_recv returns AGAIN for stream %u",
+ stream->stream_id));
+ return -1;
+}
+
+static ssize_t http2_send(struct Curl_easy *data, int sockindex,
+ const void *mem, size_t len, CURLcode *err)
+{
+ /*
+ * Currently, we send request in this function, but this function is also
+ * used to send request body. It would be nice to add dedicated function for
+ * request.
+ */
+ int rv;
+ struct connectdata *conn = data->conn;
+ struct http_conn *httpc = &conn->proto.httpc;
+ struct HTTP *stream = data->req.p.http;
+ nghttp2_nv *nva = NULL;
+ size_t nheader;
+ nghttp2_data_provider data_prd;
+ int32_t stream_id;
+ nghttp2_session *h2 = httpc->h2;
+ nghttp2_priority_spec pri_spec;
+ CURLcode result;
+ struct h2h3req *hreq;
+
+ (void)sockindex;
+
+ H2BUGF(infof(data, "http2_send len=%zu", len));
+
+ if(stream->stream_id != -1) {
+ if(stream->close_handled) {
+ infof(data, "stream %u closed", stream->stream_id);
+ *err = CURLE_HTTP2_STREAM;
+ return -1;
+ }
+ else if(stream->closed) {
+ return http2_handle_stream_close(conn, data, stream, err);
+ }
+ /* If stream_id != -1, we have dispatched request HEADERS, and now
+ are going to send or sending request body in DATA frame */
+ stream->upload_mem = mem;
+ stream->upload_len = len;
+ rv = nghttp2_session_resume_data(h2, stream->stream_id);
+ if(nghttp2_is_fatal(rv)) {
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+ rv = h2_session_send(data, h2);
+ if(nghttp2_is_fatal(rv)) {
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+ len -= stream->upload_len;
+
+ /* Nullify here because we call nghttp2_session_send() and they
+ might refer to the old buffer. */
+ stream->upload_mem = NULL;
+ stream->upload_len = 0;
+
+ if(should_close_session(httpc)) {
+ H2BUGF(infof(data, "http2_send: nothing to do in this session"));
+ *err = CURLE_HTTP2;
+ return -1;
+ }
+
+ if(stream->upload_left) {
+ /* we are sure that we have more data to send here. Calling the
+ following API will make nghttp2_session_want_write() return
+ nonzero if remote window allows it, which then libcurl checks
+ socket is writable or not. See http2_perform_getsock(). */
+ nghttp2_session_resume_data(h2, stream->stream_id);
+ }
+
+#ifdef DEBUG_HTTP2
+ if(!len) {
+ infof(data, "http2_send: easy %p (stream %u) win %u/%u",
+ data, stream->stream_id,
+ nghttp2_session_get_remote_window_size(httpc->h2),
+ nghttp2_session_get_stream_remote_window_size(httpc->h2,
+ stream->stream_id)
+ );
+
+ }
+ infof(data, "http2_send returns %zu for stream %u", len,
+ stream->stream_id);
+#endif
+ return len;
+ }
+
+ result = Curl_pseudo_headers(data, mem, len, &hreq);
+ if(result) {
+ *err = result;
+ return -1;
+ }
+ nheader = hreq->entries;
+
+ nva = malloc(sizeof(nghttp2_nv) * nheader);
+ if(!nva) {
+ Curl_pseudo_free(hreq);
+ *err = CURLE_OUT_OF_MEMORY;
+ return -1;
+ }
+ else {
+ unsigned int i;
+ for(i = 0; i < nheader; i++) {
+ nva[i].name = (unsigned char *)hreq->header[i].name;
+ nva[i].namelen = hreq->header[i].namelen;
+ nva[i].value = (unsigned char *)hreq->header[i].value;
+ nva[i].valuelen = hreq->header[i].valuelen;
+ nva[i].flags = NGHTTP2_NV_FLAG_NONE;
+ }
+ Curl_pseudo_free(hreq);
+ }
+
+ h2_pri_spec(data, &pri_spec);
+
+ H2BUGF(infof(data, "http2_send request allowed %d (easy handle %p)",
+ nghttp2_session_check_request_allowed(h2), (void *)data));
+
+ switch(data->state.httpreq) {
+ case HTTPREQ_POST:
+ case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
+ case HTTPREQ_PUT:
+ if(data->state.infilesize != -1)
+ stream->upload_left = data->state.infilesize;
+ else
+ /* data sending without specifying the data amount up front */
+ stream->upload_left = -1; /* unknown, but not zero */
+
+ data_prd.read_callback = data_source_read_callback;
+ data_prd.source.ptr = NULL;
+ stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
+ &data_prd, data);
+ break;
+ default:
+ stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
+ NULL, data);
+ }
+
+ Curl_safefree(nva);
+
+ if(stream_id < 0) {
+ H2BUGF(infof(data,
+ "http2_send() nghttp2_submit_request error (%s)%u",
+ nghttp2_strerror(stream_id), stream_id));
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+
+ infof(data, "Using Stream ID: %u (easy handle %p)",
+ stream_id, (void *)data);
+ stream->stream_id = stream_id;
+
+ rv = h2_session_send(data, h2);
+ if(rv) {
+ H2BUGF(infof(data,
+ "http2_send() nghttp2_session_send error (%s)%d",
+ nghttp2_strerror(rv), rv));
+
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+
+ if(should_close_session(httpc)) {
+ H2BUGF(infof(data, "http2_send: nothing to do in this session"));
+ *err = CURLE_HTTP2;
+ return -1;
+ }
+
+ /* If whole HEADERS frame was sent off to the underlying socket, the nghttp2
+ library calls data_source_read_callback. But only it found that no data
+ available, so it deferred the DATA transmission. Which means that
+ nghttp2_session_want_write() returns 0 on http2_perform_getsock(), which
+ results that no writable socket check is performed. To workaround this,
+ we issue nghttp2_session_resume_data() here to bring back DATA
+ transmission from deferred state. */
+ nghttp2_session_resume_data(h2, stream->stream_id);
+
+ return len;
+}
+
+CURLcode Curl_http2_setup(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result;
+ struct http_conn *httpc = &conn->proto.httpc;
+ struct HTTP *stream = data->req.p.http;
+
+ DEBUGASSERT(data->state.buffer);
+
+ stream->stream_id = -1;
+
+ Curl_dyn_init(&stream->header_recvbuf, DYN_H2_HEADERS);
+ Curl_dyn_init(&stream->trailer_recvbuf, DYN_H2_TRAILERS);
+
+ stream->upload_left = 0;
+ stream->upload_mem = NULL;
+ stream->upload_len = 0;
+ stream->mem = data->state.buffer;
+ stream->len = data->set.buffer_size;
+
+ multi_connchanged(data->multi);
+ /* below this point only connection related inits are done, which only needs
+ to be done once per connection */
+
+ if((conn->handler == &Curl_handler_http2_ssl) ||
+ (conn->handler == &Curl_handler_http2))
+ return CURLE_OK; /* already done */
+
+ if(conn->handler->flags & PROTOPT_SSL)
+ conn->handler = &Curl_handler_http2_ssl;
+ else
+ conn->handler = &Curl_handler_http2;
+
+ result = http2_init(data, conn);
+ if(result) {
+ Curl_dyn_free(&stream->header_recvbuf);
+ return result;
+ }
+
+ infof(data, "Using HTTP2, server supports multiplexing");
+
+ conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ conn->httpversion = 20;
+ conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+
+ httpc->inbuflen = 0;
+ httpc->nread_inbuf = 0;
+
+ httpc->pause_stream_id = 0;
+ httpc->drain_total = 0;
+
+ return CURLE_OK;
+}
+
+CURLcode Curl_http2_switched(struct Curl_easy *data,
+ const char *mem, size_t nread)
+{
+ CURLcode result;
+ struct connectdata *conn = data->conn;
+ struct http_conn *httpc = &conn->proto.httpc;
+ int rv;
+ struct HTTP *stream = data->req.p.http;
+
+ result = Curl_http2_setup(data, conn);
+ if(result)
+ return result;
+
+ httpc->recv_underlying = conn->recv[FIRSTSOCKET];
+ httpc->send_underlying = conn->send[FIRSTSOCKET];
+ conn->recv[FIRSTSOCKET] = http2_recv;
+ conn->send[FIRSTSOCKET] = http2_send;
+
+ if(data->req.upgr101 == UPGR101_RECEIVED) {
+ /* stream 1 is opened implicitly on upgrade */
+ stream->stream_id = 1;
+ /* queue SETTINGS frame (again) */
+ rv = nghttp2_session_upgrade2(httpc->h2, httpc->binsettings, httpc->binlen,
+ data->state.httpreq == HTTPREQ_HEAD, NULL);
+ if(rv) {
+ failf(data, "nghttp2_session_upgrade2() failed: %s(%d)",
+ nghttp2_strerror(rv), rv);
+ return CURLE_HTTP2;
+ }
+
+ rv = nghttp2_session_set_stream_user_data(httpc->h2,
+ stream->stream_id,
+ data);
+ if(rv) {
+ infof(data, "http/2: failed to set user_data for stream %u",
+ stream->stream_id);
+ DEBUGASSERT(0);
+ }
+ }
+ else {
+ populate_settings(data, httpc);
+
+ /* stream ID is unknown at this point */
+ stream->stream_id = -1;
+ rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE,
+ httpc->local_settings,
+ httpc->local_settings_num);
+ if(rv) {
+ failf(data, "nghttp2_submit_settings() failed: %s(%d)",
+ nghttp2_strerror(rv), rv);
+ return CURLE_HTTP2;
+ }
+ }
+
+ rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, 0,
+ HTTP2_HUGE_WINDOW_SIZE);
+ if(rv) {
+ failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
+ nghttp2_strerror(rv), rv);
+ return CURLE_HTTP2;
+ }
+
+ /* we are going to copy mem to httpc->inbuf. This is required since
+ mem is part of buffer pointed by stream->mem, and callbacks
+ called by nghttp2_session_mem_recv() will write stream specific
+ data into stream->mem, overwriting data already there. */
+ if(H2_BUFSIZE < nread) {
+ failf(data, "connection buffer size is too small to store data following "
+ "HTTP Upgrade response header: buflen=%d, datalen=%zu",
+ H2_BUFSIZE, nread);
+ return CURLE_HTTP2;
+ }
+
+ infof(data, "Copying HTTP/2 data in stream buffer to connection buffer"
+ " after upgrade: len=%zu",
+ nread);
+
+ if(nread)
+ memcpy(httpc->inbuf, mem, nread);
+
+ httpc->inbuflen = nread;
+
+ DEBUGASSERT(httpc->nread_inbuf == 0);
+
+ if(-1 == h2_process_pending_input(data, httpc, &result))
+ return CURLE_HTTP2;
+
+ return CURLE_OK;
+}
+
+CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause)
+{
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->conn);
+ /* if it isn't HTTP/2, we're done */
+ if(!(data->conn->handler->protocol & PROTO_FAMILY_HTTP) ||
+ !data->conn->proto.httpc.h2)
+ return CURLE_OK;
+#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
+ else {
+ struct HTTP *stream = data->req.p.http;
+ struct http_conn *httpc = &data->conn->proto.httpc;
+ uint32_t window = !pause * HTTP2_HUGE_WINDOW_SIZE;
+ int rv = nghttp2_session_set_local_window_size(httpc->h2,
+ NGHTTP2_FLAG_NONE,
+ stream->stream_id,
+ window);
+ if(rv) {
+ failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
+ nghttp2_strerror(rv), rv);
+ return CURLE_HTTP2;
+ }
+
+ /* make sure the window update gets sent */
+ rv = h2_session_send(data, httpc->h2);
+ if(rv)
+ return CURLE_SEND_ERROR;
+
+ DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u",
+ window, stream->stream_id));
+
+#ifdef DEBUGBUILD
+ {
+ /* read out the stream local window again */
+ uint32_t window2 =
+ nghttp2_session_get_stream_local_window_size(httpc->h2,
+ stream->stream_id);
+ DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u",
+ window2, stream->stream_id));
+ }
+#endif
+ }
+#endif
+ return CURLE_OK;
+}
+
+CURLcode Curl_http2_add_child(struct Curl_easy *parent,
+ struct Curl_easy *child,
+ bool exclusive)
+{
+ if(parent) {
+ struct Curl_http2_dep **tail;
+ struct Curl_http2_dep *dep = calloc(1, sizeof(struct Curl_http2_dep));
+ if(!dep)
+ return CURLE_OUT_OF_MEMORY;
+ dep->data = child;
+
+ if(parent->set.stream_dependents && exclusive) {
+ struct Curl_http2_dep *node = parent->set.stream_dependents;
+ while(node) {
+ node->data->set.stream_depends_on = child;
+ node = node->next;
+ }
+
+ tail = &child->set.stream_dependents;
+ while(*tail)
+ tail = &(*tail)->next;
+
+ DEBUGASSERT(!*tail);
+ *tail = parent->set.stream_dependents;
+ parent->set.stream_dependents = 0;
+ }
+
+ tail = &parent->set.stream_dependents;
+ while(*tail) {
+ (*tail)->data->set.stream_depends_e = FALSE;
+ tail = &(*tail)->next;
+ }
+
+ DEBUGASSERT(!*tail);
+ *tail = dep;
+ }
+
+ child->set.stream_depends_on = parent;
+ child->set.stream_depends_e = exclusive;
+ return CURLE_OK;
+}
+
+void Curl_http2_remove_child(struct Curl_easy *parent, struct Curl_easy *child)
+{
+ struct Curl_http2_dep *last = 0;
+ struct Curl_http2_dep *data = parent->set.stream_dependents;
+ DEBUGASSERT(child->set.stream_depends_on == parent);
+
+ while(data && data->data != child) {
+ last = data;
+ data = data->next;
+ }
+
+ DEBUGASSERT(data);
+
+ if(data) {
+ if(last) {
+ last->next = data->next;
+ }
+ else {
+ parent->set.stream_dependents = data->next;
+ }
+ free(data);
+ }
+
+ child->set.stream_depends_on = 0;
+ child->set.stream_depends_e = FALSE;
+}
+
+void Curl_http2_cleanup_dependencies(struct Curl_easy *data)
+{
+ while(data->set.stream_dependents) {
+ struct Curl_easy *tmp = data->set.stream_dependents->data;
+ Curl_http2_remove_child(data, tmp);
+ if(data->set.stream_depends_on)
+ Curl_http2_add_child(data->set.stream_depends_on, tmp, FALSE);
+ }
+
+ if(data->set.stream_depends_on)
+ Curl_http2_remove_child(data->set.stream_depends_on, data);
+}
+
+/* Only call this function for a transfer that already got an HTTP/2
+ CURLE_HTTP2_STREAM error! */
+bool Curl_h2_http_1_1_error(struct Curl_easy *data)
+{
+ struct HTTP *stream = data->req.p.http;
+ return (stream->error == NGHTTP2_HTTP_1_1_REQUIRED);
+}
+
+#else /* !USE_NGHTTP2 */
+
+/* Satisfy external references even if http2 is not compiled in. */
+#include <curl/curl.h>
+
+char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
+{
+ (void) h;
+ (void) num;
+ return NULL;
+}
+
+char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
+{
+ (void) h;
+ (void) header;
+ return NULL;
+}
+
+#endif /* USE_NGHTTP2 */
diff --git a/libs/libcurl/src/http2.h b/libs/libcurl/src/http2.h index f0390596ce..fd4cf68c83 100644 --- a/libs/libcurl/src/http2.h +++ b/libs/libcurl/src/http2.h @@ -1,84 +1,84 @@ -#ifndef HEADER_CURL_HTTP2_H -#define HEADER_CURL_HTTP2_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_NGHTTP2 -#include "http.h" - -/* value for MAX_CONCURRENT_STREAMS we use until we get an updated setting - from the peer */ -#define DEFAULT_MAX_CONCURRENT_STREAMS 100 - -/* - * Store nghttp2 version info in this buffer. - */ -void Curl_http2_ver(char *p, size_t len); - -const char *Curl_http2_strerror(uint32_t err); - -CURLcode Curl_http2_init(struct connectdata *conn); -void Curl_http2_init_state(struct UrlState *state); -void Curl_http2_init_userset(struct UserDefined *set); -CURLcode Curl_http2_request_upgrade(struct dynbuf *req, - struct Curl_easy *data); -CURLcode Curl_http2_setup(struct Curl_easy *data, struct connectdata *conn); -CURLcode Curl_http2_switched(struct Curl_easy *data, - const char *ptr, size_t nread); -/* called from http_setup_conn */ -void Curl_http2_setup_conn(struct connectdata *conn); -void Curl_http2_setup_req(struct Curl_easy *data); -void Curl_http2_done(struct Curl_easy *data, bool premature); -CURLcode Curl_http2_done_sending(struct Curl_easy *data, - struct connectdata *conn); -CURLcode Curl_http2_add_child(struct Curl_easy *parent, - struct Curl_easy *child, - bool exclusive); -void Curl_http2_remove_child(struct Curl_easy *parent, - struct Curl_easy *child); -void Curl_http2_cleanup_dependencies(struct Curl_easy *data); -CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause); - -/* returns true if the HTTP/2 stream error was HTTP_1_1_REQUIRED */ -bool Curl_h2_http_1_1_error(struct Curl_easy *data); -#else /* USE_NGHTTP2 */ -#define Curl_http2_request_upgrade(x,y) CURLE_UNSUPPORTED_PROTOCOL -#define Curl_http2_setup(x,y) CURLE_UNSUPPORTED_PROTOCOL -#define Curl_http2_switched(x,y,z) CURLE_UNSUPPORTED_PROTOCOL -#define Curl_http2_setup_conn(x) Curl_nop_stmt -#define Curl_http2_setup_req(x) -#define Curl_http2_init_state(x) -#define Curl_http2_init_userset(x) -#define Curl_http2_done(x,y) -#define Curl_http2_done_sending(x,y) -#define Curl_http2_add_child(x, y, z) -#define Curl_http2_remove_child(x, y) -#define Curl_http2_cleanup_dependencies(x) -#define Curl_http2_stream_pause(x, y) -#define Curl_h2_http_1_1_error(x) 0 -#endif - -#endif /* HEADER_CURL_HTTP2_H */ +#ifndef HEADER_CURL_HTTP2_H
+#define HEADER_CURL_HTTP2_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_NGHTTP2
+#include "http.h"
+
+/* value for MAX_CONCURRENT_STREAMS we use until we get an updated setting
+ from the peer */
+#define DEFAULT_MAX_CONCURRENT_STREAMS 100
+
+/*
+ * Store nghttp2 version info in this buffer.
+ */
+void Curl_http2_ver(char *p, size_t len);
+
+const char *Curl_http2_strerror(uint32_t err);
+
+CURLcode Curl_http2_init(struct connectdata *conn);
+void Curl_http2_init_state(struct UrlState *state);
+void Curl_http2_init_userset(struct UserDefined *set);
+CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
+ struct Curl_easy *data);
+CURLcode Curl_http2_setup(struct Curl_easy *data, struct connectdata *conn);
+CURLcode Curl_http2_switched(struct Curl_easy *data,
+ const char *ptr, size_t nread);
+/* called from http_setup_conn */
+void Curl_http2_setup_conn(struct connectdata *conn);
+void Curl_http2_setup_req(struct Curl_easy *data);
+void Curl_http2_done(struct Curl_easy *data, bool premature);
+CURLcode Curl_http2_done_sending(struct Curl_easy *data,
+ struct connectdata *conn);
+CURLcode Curl_http2_add_child(struct Curl_easy *parent,
+ struct Curl_easy *child,
+ bool exclusive);
+void Curl_http2_remove_child(struct Curl_easy *parent,
+ struct Curl_easy *child);
+void Curl_http2_cleanup_dependencies(struct Curl_easy *data);
+CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause);
+
+/* returns true if the HTTP/2 stream error was HTTP_1_1_REQUIRED */
+bool Curl_h2_http_1_1_error(struct Curl_easy *data);
+#else /* USE_NGHTTP2 */
+#define Curl_http2_request_upgrade(x,y) CURLE_UNSUPPORTED_PROTOCOL
+#define Curl_http2_setup(x,y) CURLE_UNSUPPORTED_PROTOCOL
+#define Curl_http2_switched(x,y,z) CURLE_UNSUPPORTED_PROTOCOL
+#define Curl_http2_setup_conn(x) Curl_nop_stmt
+#define Curl_http2_setup_req(x)
+#define Curl_http2_init_state(x)
+#define Curl_http2_init_userset(x)
+#define Curl_http2_done(x,y)
+#define Curl_http2_done_sending(x,y) (void)y
+#define Curl_http2_add_child(x, y, z)
+#define Curl_http2_remove_child(x, y)
+#define Curl_http2_cleanup_dependencies(x)
+#define Curl_http2_stream_pause(x, y)
+#define Curl_h2_http_1_1_error(x) 0
+#endif
+
+#endif /* HEADER_CURL_HTTP2_H */
diff --git a/libs/libcurl/src/http_aws_sigv4.c b/libs/libcurl/src/http_aws_sigv4.c index 440eb385f8..f450e81e17 100644 --- a/libs/libcurl/src/http_aws_sigv4.c +++ b/libs/libcurl/src/http_aws_sigv4.c @@ -1,525 +1,565 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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.haxx.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) - -#include "urldata.h" -#include "strcase.h" -#include "strdup.h" -#include "http_aws_sigv4.h" -#include "curl_sha256.h" -#include "transfer.h" - -#include "strcase.h" -#include "parsedate.h" -#include "sendf.h" - -#include <time.h> - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#include "slist.h" - -#define HMAC_SHA256(k, kl, d, dl, o) \ - do { \ - ret = Curl_hmacit(Curl_HMAC_SHA256, \ - (unsigned char *)k, \ - (unsigned int)kl, \ - (unsigned char *)d, \ - (unsigned int)dl, o); \ - if(ret) { \ - goto fail; \ - } \ - } while(0) - -#define TIMESTAMP_SIZE 17 - -static void sha256_to_hex(char *dst, unsigned char *sha, size_t dst_l) -{ - int i; - - DEBUGASSERT(dst_l >= 65); - for(i = 0; i < 32; ++i) { - msnprintf(dst + (i * 2), dst_l - (i * 2), "%02x", sha[i]); - } -} - -static char *find_date_hdr(struct Curl_easy *data, const char *sig_hdr) -{ - char *tmp = Curl_checkheaders(data, sig_hdr, strlen(sig_hdr)); - - if(tmp) - return tmp; - return Curl_checkheaders(data, STRCONST("Date")); -} - -/* remove whitespace, and lowercase all headers */ -static void trim_headers(struct curl_slist *head) -{ - struct curl_slist *l; - for(l = head; l; l = l->next) { - char *value; /* to read from */ - char *store; - size_t colon = strcspn(l->data, ":"); - Curl_strntolower(l->data, l->data, colon); - - value = &l->data[colon]; - if(!*value) - continue; - ++value; - store = value; - - /* skip leading whitespace */ - while(*value && ISBLANK(*value)) - value++; - - while(*value) { - int space = 0; - while(*value && ISBLANK(*value)) { - value++; - space++; - } - if(space) { - /* replace any number of consecutive whitespace with a single space, - unless at the end of the string, then nothing */ - if(*value) - *store++ = ' '; - } - else - *store++ = *value++; - } - *store = 0; /* null terminate */ - } -} - -/* maximum lenth for the aws sivg4 parts */ -#define MAX_SIGV4_LEN 64 -#define MAX_SIGV4_LEN_TXT "64" - -#define DATE_HDR_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Date")) - -#define MAX_HOST_LEN 255 -/* FQDN + host: */ -#define FULL_HOST_LEN (MAX_HOST_LEN + sizeof("host:")) - -/* string been x-PROVIDER-date:TIMESTAMP, I need +1 for ':' */ -#define DATE_FULL_HDR_LEN (DATE_HDR_KEY_LEN + TIMESTAMP_SIZE + 1) - -/* timestamp should point to a buffer of at last TIMESTAMP_SIZE bytes */ -static CURLcode make_headers(struct Curl_easy *data, - const char *hostname, - char *timestamp, - char *provider1, - char **date_header, - struct dynbuf *canonical_headers, - struct dynbuf *signed_headers) -{ - char date_hdr_key[DATE_HDR_KEY_LEN]; - char date_full_hdr[DATE_FULL_HDR_LEN]; - struct curl_slist *head = NULL; - struct curl_slist *tmp_head = NULL; - CURLcode ret = CURLE_OUT_OF_MEMORY; - struct curl_slist *l; - int again = 1; - - /* provider1 mid */ - Curl_strntolower(provider1, provider1, strlen(provider1)); - provider1[0] = Curl_raw_toupper(provider1[0]); - - msnprintf(date_hdr_key, DATE_HDR_KEY_LEN, "X-%s-Date", provider1); - - /* provider1 lowercase */ - Curl_strntolower(provider1, provider1, 1); /* first byte only */ - msnprintf(date_full_hdr, DATE_FULL_HDR_LEN, - "x-%s-date:%s", provider1, timestamp); - - if(Curl_checkheaders(data, STRCONST("Host"))) { - head = NULL; - } - else { - char full_host[FULL_HOST_LEN + 1]; - - if(data->state.aptr.host) { - size_t pos; - - if(strlen(data->state.aptr.host) > FULL_HOST_LEN) { - ret = CURLE_URL_MALFORMAT; - goto fail; - } - strcpy(full_host, data->state.aptr.host); - /* remove /r/n as the separator for canonical request must be '\n' */ - pos = strcspn(full_host, "\n\r"); - full_host[pos] = 0; - } - else { - if(strlen(hostname) > MAX_HOST_LEN) { - ret = CURLE_URL_MALFORMAT; - goto fail; - } - msnprintf(full_host, FULL_HOST_LEN, "host:%s", hostname); - } - - head = curl_slist_append(NULL, full_host); - if(!head) - goto fail; - } - - - for(l = data->set.headers; l; l = l->next) { - tmp_head = curl_slist_append(head, l->data); - if(!tmp_head) - goto fail; - head = tmp_head; - } - - trim_headers(head); - - *date_header = find_date_hdr(data, date_hdr_key); - if(!*date_header) { - tmp_head = curl_slist_append(head, date_full_hdr); - if(!tmp_head) - goto fail; - head = tmp_head; - *date_header = curl_maprintf("%s: %s", date_hdr_key, timestamp); - } - else { - char *value; - - *date_header = strdup(*date_header); - if(!*date_header) - goto fail; - - value = strchr(*date_header, ':'); - if(!value) - goto fail; - ++value; - while(ISBLANK(*value)) - ++value; - strncpy(timestamp, value, TIMESTAMP_SIZE - 1); - timestamp[TIMESTAMP_SIZE - 1] = 0; - } - - /* alpha-sort in a case sensitive manner */ - do { - again = 0; - for(l = head; l; l = l->next) { - struct curl_slist *next = l->next; - - if(next && strcmp(l->data, next->data) > 0) { - char *tmp = l->data; - - l->data = next->data; - next->data = tmp; - again = 1; - } - } - } while(again); - - for(l = head; l; l = l->next) { - char *tmp; - - if(Curl_dyn_add(canonical_headers, l->data)) - goto fail; - if(Curl_dyn_add(canonical_headers, "\n")) - goto fail; - - tmp = strchr(l->data, ':'); - if(tmp) - *tmp = 0; - - if(l != head) { - if(Curl_dyn_add(signed_headers, ";")) - goto fail; - } - if(Curl_dyn_add(signed_headers, l->data)) - goto fail; - } - - ret = CURLE_OK; -fail: - curl_slist_free_all(head); - - return ret; -} - -CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) -{ - CURLcode ret = CURLE_OUT_OF_MEMORY; - struct connectdata *conn = data->conn; - size_t len; - const char *arg; - char provider0[MAX_SIGV4_LEN + 1]=""; - char provider1[MAX_SIGV4_LEN + 1]=""; - char region[MAX_SIGV4_LEN + 1]=""; - char service[MAX_SIGV4_LEN + 1]=""; - const char *hostname = conn->host.name; - time_t clock; - struct tm tm; - char timestamp[TIMESTAMP_SIZE]; - char date[9]; - struct dynbuf canonical_headers; - struct dynbuf signed_headers; - char *date_header = NULL; - const char *post_data = data->set.postfields; - size_t post_data_len = 0; - unsigned char sha_hash[32]; - char sha_hex[65]; - char *canonical_request = NULL; - char *request_type = NULL; - char *credential_scope = NULL; - char *str_to_sign = NULL; - const char *user = data->state.aptr.user ? data->state.aptr.user : ""; - char *secret = NULL; - unsigned char sign0[32] = {0}; - unsigned char sign1[32] = {0}; - char *auth_headers = NULL; - - DEBUGASSERT(!proxy); - (void)proxy; - - if(Curl_checkheaders(data, STRCONST("Authorization"))) { - /* Authorization already present, Bailing out */ - return CURLE_OK; - } - - /* we init thoses buffers here, so goto fail will free initialized dynbuf */ - Curl_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER); - Curl_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER); - - /* - * Parameters parsing - * Google and Outscale use the same OSC or GOOG, - * but Amazon uses AWS and AMZ for header arguments. - * AWS is the default because most of non-amazon providers - * are still using aws:amz as a prefix. - */ - arg = data->set.str[STRING_AWS_SIGV4] ? - data->set.str[STRING_AWS_SIGV4] : "aws:amz"; - - /* provider1[:provider2[:region[:service]]] - - No string can be longer than N bytes of non-whitespace - */ - (void)sscanf(arg, "%" MAX_SIGV4_LEN_TXT "[^:]" - ":%" MAX_SIGV4_LEN_TXT "[^:]" - ":%" MAX_SIGV4_LEN_TXT "[^:]" - ":%" MAX_SIGV4_LEN_TXT "s", - provider0, provider1, region, service); - if(!provider0[0]) { - failf(data, "first provider can't be empty"); - ret = CURLE_BAD_FUNCTION_ARGUMENT; - goto fail; - } - else if(!provider1[0]) - strcpy(provider1, provider0); - - if(!service[0]) { - char *hostdot = strchr(hostname, '.'); - if(!hostdot) { - failf(data, "service missing in parameters and hostname"); - ret = CURLE_URL_MALFORMAT; - goto fail; - } - len = hostdot - hostname; - if(len > MAX_SIGV4_LEN) { - failf(data, "service too long in hostname"); - ret = CURLE_URL_MALFORMAT; - goto fail; - } - strncpy(service, hostname, len); - service[len] = '\0'; - - if(!region[0]) { - const char *reg = hostdot + 1; - const char *hostreg = strchr(reg, '.'); - if(!hostreg) { - failf(data, "region missing in parameters and hostname"); - ret = CURLE_URL_MALFORMAT; - goto fail; - } - len = hostreg - reg; - if(len > MAX_SIGV4_LEN) { - failf(data, "region too long in hostname"); - ret = CURLE_URL_MALFORMAT; - goto fail; - } - strncpy(region, reg, len); - region[len] = '\0'; - } - } - -#ifdef DEBUGBUILD - { - char *force_timestamp = getenv("CURL_FORCETIME"); - if(force_timestamp) - clock = 0; - else - time(&clock); - } -#else - time(&clock); -#endif - ret = Curl_gmtime(clock, &tm); - if(ret) { - goto fail; - } - if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) { - ret = CURLE_OUT_OF_MEMORY; - goto fail; - } - - ret = make_headers(data, hostname, timestamp, provider1, - &date_header, &canonical_headers, &signed_headers); - if(ret) - goto fail; - ret = CURLE_OUT_OF_MEMORY; - - memcpy(date, timestamp, sizeof(date)); - date[sizeof(date) - 1] = 0; - - if(post_data) { - if(data->set.postfieldsize < 0) - post_data_len = strlen(post_data); - else - post_data_len = (size_t)data->set.postfieldsize; - } - if(Curl_sha256it(sha_hash, (const unsigned char *) post_data, - post_data_len)) - goto fail; - - sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex)); - - { - Curl_HttpReq httpreq; - const char *method; - - Curl_http_method(data, conn, &method, &httpreq); - - canonical_request = - curl_maprintf("%s\n" /* HTTPRequestMethod */ - "%s\n" /* CanonicalURI */ - "%s\n" /* CanonicalQueryString */ - "%s\n" /* CanonicalHeaders */ - "%s\n" /* SignedHeaders */ - "%s", /* HashedRequestPayload in hex */ - method, - data->state.up.path, - data->state.up.query ? data->state.up.query : "", - Curl_dyn_ptr(&canonical_headers), - Curl_dyn_ptr(&signed_headers), - sha_hex); - if(!canonical_request) - goto fail; - } - - /* provider 0 lowercase */ - Curl_strntolower(provider0, provider0, strlen(provider0)); - request_type = curl_maprintf("%s4_request", provider0); - if(!request_type) - goto fail; - - credential_scope = curl_maprintf("%s/%s/%s/%s", - date, region, service, request_type); - if(!credential_scope) - goto fail; - - if(Curl_sha256it(sha_hash, (unsigned char *) canonical_request, - strlen(canonical_request))) - goto fail; - - sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex)); - - /* provider 0 uppercase */ - Curl_strntoupper(provider0, provider0, strlen(provider0)); - - /* - * Google allows using RSA key instead of HMAC, so this code might change - * in the future. For now we ony support HMAC. - */ - str_to_sign = curl_maprintf("%s4-HMAC-SHA256\n" /* Algorithm */ - "%s\n" /* RequestDateTime */ - "%s\n" /* CredentialScope */ - "%s", /* HashedCanonicalRequest in hex */ - provider0, - timestamp, - credential_scope, - sha_hex); - if(!str_to_sign) { - goto fail; - } - - /* provider 0 uppercase */ - secret = curl_maprintf("%s4%s", provider0, - data->state.aptr.passwd ? - data->state.aptr.passwd : ""); - if(!secret) - goto fail; - - HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0); - HMAC_SHA256(sign0, sizeof(sign0), region, strlen(region), sign1); - HMAC_SHA256(sign1, sizeof(sign1), service, strlen(service), sign0); - HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1); - HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0); - - sha256_to_hex(sha_hex, sign0, sizeof(sha_hex)); - - /* provider 0 uppercase */ - auth_headers = curl_maprintf("Authorization: %s4-HMAC-SHA256 " - "Credential=%s/%s, " - "SignedHeaders=%s, " - "Signature=%s\r\n" - "%s\r\n", - provider0, - user, - credential_scope, - Curl_dyn_ptr(&signed_headers), - sha_hex, - date_header); - if(!auth_headers) { - goto fail; - } - - Curl_safefree(data->state.aptr.userpwd); - data->state.aptr.userpwd = auth_headers; - data->state.authhost.done = TRUE; - ret = CURLE_OK; - -fail: - Curl_dyn_free(&canonical_headers); - Curl_dyn_free(&signed_headers); - free(canonical_request); - free(request_type); - free(credential_scope); - free(str_to_sign); - free(secret); - free(date_header); - return ret; -} - -#endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+
+#include "urldata.h"
+#include "strcase.h"
+#include "strdup.h"
+#include "http_aws_sigv4.h"
+#include "curl_sha256.h"
+#include "transfer.h"
+#include "parsedate.h"
+#include "sendf.h"
+
+#include <time.h>
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#include "slist.h"
+
+#define HMAC_SHA256(k, kl, d, dl, o) \
+ do { \
+ ret = Curl_hmacit(Curl_HMAC_SHA256, \
+ (unsigned char *)k, \
+ (unsigned int)kl, \
+ (unsigned char *)d, \
+ (unsigned int)dl, o); \
+ if(ret) { \
+ goto fail; \
+ } \
+ } while(0)
+
+#define TIMESTAMP_SIZE 17
+
+static void sha256_to_hex(char *dst, unsigned char *sha, size_t dst_l)
+{
+ int i;
+
+ DEBUGASSERT(dst_l >= 65);
+ for(i = 0; i < 32; ++i) {
+ msnprintf(dst + (i * 2), dst_l - (i * 2), "%02x", sha[i]);
+ }
+}
+
+static char *find_date_hdr(struct Curl_easy *data, const char *sig_hdr)
+{
+ char *tmp = Curl_checkheaders(data, sig_hdr, strlen(sig_hdr));
+
+ if(tmp)
+ return tmp;
+ return Curl_checkheaders(data, STRCONST("Date"));
+}
+
+/* remove whitespace, and lowercase all headers */
+static void trim_headers(struct curl_slist *head)
+{
+ struct curl_slist *l;
+ for(l = head; l; l = l->next) {
+ char *value; /* to read from */
+ char *store;
+ size_t colon = strcspn(l->data, ":");
+ Curl_strntolower(l->data, l->data, colon);
+
+ value = &l->data[colon];
+ if(!*value)
+ continue;
+ ++value;
+ store = value;
+
+ /* skip leading whitespace */
+ while(*value && ISBLANK(*value))
+ value++;
+
+ while(*value) {
+ int space = 0;
+ while(*value && ISBLANK(*value)) {
+ value++;
+ space++;
+ }
+ if(space) {
+ /* replace any number of consecutive whitespace with a single space,
+ unless at the end of the string, then nothing */
+ if(*value)
+ *store++ = ' ';
+ }
+ else
+ *store++ = *value++;
+ }
+ *store = 0; /* null terminate */
+ }
+}
+
+/* maximum length for the aws sivg4 parts */
+#define MAX_SIGV4_LEN 64
+#define MAX_SIGV4_LEN_TXT "64"
+
+#define DATE_HDR_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Date"))
+
+#define MAX_HOST_LEN 255
+/* FQDN + host: */
+#define FULL_HOST_LEN (MAX_HOST_LEN + sizeof("host:"))
+
+/* string been x-PROVIDER-date:TIMESTAMP, I need +1 for ':' */
+#define DATE_FULL_HDR_LEN (DATE_HDR_KEY_LEN + TIMESTAMP_SIZE + 1)
+
+/* timestamp should point to a buffer of at last TIMESTAMP_SIZE bytes */
+static CURLcode make_headers(struct Curl_easy *data,
+ const char *hostname,
+ char *timestamp,
+ char *provider1,
+ char **date_header,
+ struct dynbuf *canonical_headers,
+ struct dynbuf *signed_headers)
+{
+ char date_hdr_key[DATE_HDR_KEY_LEN];
+ char date_full_hdr[DATE_FULL_HDR_LEN];
+ struct curl_slist *head = NULL;
+ struct curl_slist *tmp_head = NULL;
+ CURLcode ret = CURLE_OUT_OF_MEMORY;
+ struct curl_slist *l;
+ int again = 1;
+
+ /* provider1 mid */
+ Curl_strntolower(provider1, provider1, strlen(provider1));
+ provider1[0] = Curl_raw_toupper(provider1[0]);
+
+ msnprintf(date_hdr_key, DATE_HDR_KEY_LEN, "X-%s-Date", provider1);
+
+ /* provider1 lowercase */
+ Curl_strntolower(provider1, provider1, 1); /* first byte only */
+ msnprintf(date_full_hdr, DATE_FULL_HDR_LEN,
+ "x-%s-date:%s", provider1, timestamp);
+
+ if(Curl_checkheaders(data, STRCONST("Host"))) {
+ head = NULL;
+ }
+ else {
+ char full_host[FULL_HOST_LEN + 1];
+
+ if(data->state.aptr.host) {
+ size_t pos;
+
+ if(strlen(data->state.aptr.host) > FULL_HOST_LEN) {
+ ret = CURLE_URL_MALFORMAT;
+ goto fail;
+ }
+ strcpy(full_host, data->state.aptr.host);
+ /* remove /r/n as the separator for canonical request must be '\n' */
+ pos = strcspn(full_host, "\n\r");
+ full_host[pos] = 0;
+ }
+ else {
+ if(strlen(hostname) > MAX_HOST_LEN) {
+ ret = CURLE_URL_MALFORMAT;
+ goto fail;
+ }
+ msnprintf(full_host, FULL_HOST_LEN, "host:%s", hostname);
+ }
+
+ head = curl_slist_append(NULL, full_host);
+ if(!head)
+ goto fail;
+ }
+
+
+ for(l = data->set.headers; l; l = l->next) {
+ tmp_head = curl_slist_append(head, l->data);
+ if(!tmp_head)
+ goto fail;
+ head = tmp_head;
+ }
+
+ trim_headers(head);
+
+ *date_header = find_date_hdr(data, date_hdr_key);
+ if(!*date_header) {
+ tmp_head = curl_slist_append(head, date_full_hdr);
+ if(!tmp_head)
+ goto fail;
+ head = tmp_head;
+ *date_header = curl_maprintf("%s: %s", date_hdr_key, timestamp);
+ }
+ else {
+ char *value;
+
+ *date_header = strdup(*date_header);
+ if(!*date_header)
+ goto fail;
+
+ value = strchr(*date_header, ':');
+ if(!value)
+ goto fail;
+ ++value;
+ while(ISBLANK(*value))
+ ++value;
+ strncpy(timestamp, value, TIMESTAMP_SIZE - 1);
+ timestamp[TIMESTAMP_SIZE - 1] = 0;
+ }
+
+ /* alpha-sort in a case sensitive manner */
+ do {
+ again = 0;
+ for(l = head; l; l = l->next) {
+ struct curl_slist *next = l->next;
+
+ if(next && strcmp(l->data, next->data) > 0) {
+ char *tmp = l->data;
+
+ l->data = next->data;
+ next->data = tmp;
+ again = 1;
+ }
+ }
+ } while(again);
+
+ for(l = head; l; l = l->next) {
+ char *tmp;
+
+ if(Curl_dyn_add(canonical_headers, l->data))
+ goto fail;
+ if(Curl_dyn_add(canonical_headers, "\n"))
+ goto fail;
+
+ tmp = strchr(l->data, ':');
+ if(tmp)
+ *tmp = 0;
+
+ if(l != head) {
+ if(Curl_dyn_add(signed_headers, ";"))
+ goto fail;
+ }
+ if(Curl_dyn_add(signed_headers, l->data))
+ goto fail;
+ }
+
+ ret = CURLE_OK;
+fail:
+ curl_slist_free_all(head);
+
+ return ret;
+}
+
+#define CONTENT_SHA256_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Content-Sha256"))
+
+/* try to parse a payload hash from the content-sha256 header */
+static char *parse_content_sha_hdr(struct Curl_easy *data,
+ const char *provider1,
+ size_t *value_len)
+{
+ char key[CONTENT_SHA256_KEY_LEN];
+ size_t key_len;
+ char *value;
+ size_t len;
+
+ key_len = msnprintf(key, sizeof(key), "x-%s-content-sha256", provider1);
+
+ value = Curl_checkheaders(data, key, key_len);
+ if(!value)
+ return NULL;
+
+ value = strchr(value, ':');
+ if(!value)
+ return NULL;
+ ++value;
+
+ while(*value && ISBLANK(*value))
+ ++value;
+
+ len = strlen(value);
+ while(len > 0 && ISBLANK(value[len-1]))
+ --len;
+
+ *value_len = len;
+ return value;
+}
+
+CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
+{
+ CURLcode ret = CURLE_OUT_OF_MEMORY;
+ struct connectdata *conn = data->conn;
+ size_t len;
+ const char *arg;
+ char provider0[MAX_SIGV4_LEN + 1]="";
+ char provider1[MAX_SIGV4_LEN + 1]="";
+ char region[MAX_SIGV4_LEN + 1]="";
+ char service[MAX_SIGV4_LEN + 1]="";
+ const char *hostname = conn->host.name;
+ time_t clock;
+ struct tm tm;
+ char timestamp[TIMESTAMP_SIZE];
+ char date[9];
+ struct dynbuf canonical_headers;
+ struct dynbuf signed_headers;
+ char *date_header = NULL;
+ char *payload_hash = NULL;
+ size_t payload_hash_len = 0;
+ const char *post_data = data->set.postfields;
+ size_t post_data_len = 0;
+ unsigned char sha_hash[32];
+ char sha_hex[65];
+ char *canonical_request = NULL;
+ char *request_type = NULL;
+ char *credential_scope = NULL;
+ char *str_to_sign = NULL;
+ const char *user = data->state.aptr.user ? data->state.aptr.user : "";
+ char *secret = NULL;
+ unsigned char sign0[32] = {0};
+ unsigned char sign1[32] = {0};
+ char *auth_headers = NULL;
+
+ DEBUGASSERT(!proxy);
+ (void)proxy;
+
+ if(Curl_checkheaders(data, STRCONST("Authorization"))) {
+ /* Authorization already present, Bailing out */
+ return CURLE_OK;
+ }
+
+ /* we init those buffers here, so goto fail will free initialized dynbuf */
+ Curl_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER);
+ Curl_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER);
+
+ /*
+ * Parameters parsing
+ * Google and Outscale use the same OSC or GOOG,
+ * but Amazon uses AWS and AMZ for header arguments.
+ * AWS is the default because most of non-amazon providers
+ * are still using aws:amz as a prefix.
+ */
+ arg = data->set.str[STRING_AWS_SIGV4] ?
+ data->set.str[STRING_AWS_SIGV4] : "aws:amz";
+
+ /* provider1[:provider2[:region[:service]]]
+
+ No string can be longer than N bytes of non-whitespace
+ */
+ (void)sscanf(arg, "%" MAX_SIGV4_LEN_TXT "[^:]"
+ ":%" MAX_SIGV4_LEN_TXT "[^:]"
+ ":%" MAX_SIGV4_LEN_TXT "[^:]"
+ ":%" MAX_SIGV4_LEN_TXT "s",
+ provider0, provider1, region, service);
+ if(!provider0[0]) {
+ failf(data, "first provider can't be empty");
+ ret = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto fail;
+ }
+ else if(!provider1[0])
+ strcpy(provider1, provider0);
+
+ if(!service[0]) {
+ char *hostdot = strchr(hostname, '.');
+ if(!hostdot) {
+ failf(data, "service missing in parameters and hostname");
+ ret = CURLE_URL_MALFORMAT;
+ goto fail;
+ }
+ len = hostdot - hostname;
+ if(len > MAX_SIGV4_LEN) {
+ failf(data, "service too long in hostname");
+ ret = CURLE_URL_MALFORMAT;
+ goto fail;
+ }
+ strncpy(service, hostname, len);
+ service[len] = '\0';
+
+ if(!region[0]) {
+ const char *reg = hostdot + 1;
+ const char *hostreg = strchr(reg, '.');
+ if(!hostreg) {
+ failf(data, "region missing in parameters and hostname");
+ ret = CURLE_URL_MALFORMAT;
+ goto fail;
+ }
+ len = hostreg - reg;
+ if(len > MAX_SIGV4_LEN) {
+ failf(data, "region too long in hostname");
+ ret = CURLE_URL_MALFORMAT;
+ goto fail;
+ }
+ strncpy(region, reg, len);
+ region[len] = '\0';
+ }
+ }
+
+#ifdef DEBUGBUILD
+ {
+ char *force_timestamp = getenv("CURL_FORCETIME");
+ if(force_timestamp)
+ clock = 0;
+ else
+ time(&clock);
+ }
+#else
+ time(&clock);
+#endif
+ ret = Curl_gmtime(clock, &tm);
+ if(ret) {
+ goto fail;
+ }
+ if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) {
+ ret = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+
+ ret = make_headers(data, hostname, timestamp, provider1,
+ &date_header, &canonical_headers, &signed_headers);
+ if(ret)
+ goto fail;
+ ret = CURLE_OUT_OF_MEMORY;
+
+ memcpy(date, timestamp, sizeof(date));
+ date[sizeof(date) - 1] = 0;
+
+ payload_hash = parse_content_sha_hdr(data, provider1, &payload_hash_len);
+
+ if(!payload_hash) {
+ if(post_data) {
+ if(data->set.postfieldsize < 0)
+ post_data_len = strlen(post_data);
+ else
+ post_data_len = (size_t)data->set.postfieldsize;
+ }
+ if(Curl_sha256it(sha_hash, (const unsigned char *) post_data,
+ post_data_len))
+ goto fail;
+
+ sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex));
+ payload_hash = sha_hex;
+ payload_hash_len = strlen(sha_hex);
+ }
+
+ {
+ Curl_HttpReq httpreq;
+ const char *method;
+
+ Curl_http_method(data, conn, &method, &httpreq);
+
+ canonical_request =
+ curl_maprintf("%s\n" /* HTTPRequestMethod */
+ "%s\n" /* CanonicalURI */
+ "%s\n" /* CanonicalQueryString */
+ "%s\n" /* CanonicalHeaders */
+ "%s\n" /* SignedHeaders */
+ "%.*s", /* HashedRequestPayload in hex */
+ method,
+ data->state.up.path,
+ data->state.up.query ? data->state.up.query : "",
+ Curl_dyn_ptr(&canonical_headers),
+ Curl_dyn_ptr(&signed_headers),
+ (int)payload_hash_len, payload_hash);
+ if(!canonical_request)
+ goto fail;
+ }
+
+ /* provider 0 lowercase */
+ Curl_strntolower(provider0, provider0, strlen(provider0));
+ request_type = curl_maprintf("%s4_request", provider0);
+ if(!request_type)
+ goto fail;
+
+ credential_scope = curl_maprintf("%s/%s/%s/%s",
+ date, region, service, request_type);
+ if(!credential_scope)
+ goto fail;
+
+ if(Curl_sha256it(sha_hash, (unsigned char *) canonical_request,
+ strlen(canonical_request)))
+ goto fail;
+
+ sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex));
+
+ /* provider 0 uppercase */
+ Curl_strntoupper(provider0, provider0, strlen(provider0));
+
+ /*
+ * Google allows using RSA key instead of HMAC, so this code might change
+ * in the future. For now we only support HMAC.
+ */
+ str_to_sign = curl_maprintf("%s4-HMAC-SHA256\n" /* Algorithm */
+ "%s\n" /* RequestDateTime */
+ "%s\n" /* CredentialScope */
+ "%s", /* HashedCanonicalRequest in hex */
+ provider0,
+ timestamp,
+ credential_scope,
+ sha_hex);
+ if(!str_to_sign) {
+ goto fail;
+ }
+
+ /* provider 0 uppercase */
+ secret = curl_maprintf("%s4%s", provider0,
+ data->state.aptr.passwd ?
+ data->state.aptr.passwd : "");
+ if(!secret)
+ goto fail;
+
+ HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0);
+ HMAC_SHA256(sign0, sizeof(sign0), region, strlen(region), sign1);
+ HMAC_SHA256(sign1, sizeof(sign1), service, strlen(service), sign0);
+ HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1);
+ HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0);
+
+ sha256_to_hex(sha_hex, sign0, sizeof(sha_hex));
+
+ /* provider 0 uppercase */
+ auth_headers = curl_maprintf("Authorization: %s4-HMAC-SHA256 "
+ "Credential=%s/%s, "
+ "SignedHeaders=%s, "
+ "Signature=%s\r\n"
+ "%s\r\n",
+ provider0,
+ user,
+ credential_scope,
+ Curl_dyn_ptr(&signed_headers),
+ sha_hex,
+ date_header);
+ if(!auth_headers) {
+ goto fail;
+ }
+
+ Curl_safefree(data->state.aptr.userpwd);
+ data->state.aptr.userpwd = auth_headers;
+ data->state.authhost.done = TRUE;
+ ret = CURLE_OK;
+
+fail:
+ Curl_dyn_free(&canonical_headers);
+ Curl_dyn_free(&signed_headers);
+ free(canonical_request);
+ free(request_type);
+ free(credential_scope);
+ free(str_to_sign);
+ free(secret);
+ free(date_header);
+ return ret;
+}
+
+#endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) */
diff --git a/libs/libcurl/src/http_aws_sigv4.h b/libs/libcurl/src/http_aws_sigv4.h index 85755e937b..6fccf9ae6e 100644 --- a/libs/libcurl/src/http_aws_sigv4.h +++ b/libs/libcurl/src/http_aws_sigv4.h @@ -1,31 +1,31 @@ -#ifndef HEADER_CURL_HTTP_AWS_SIGV4_H -#define HEADER_CURL_HTTP_AWS_SIGV4_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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.haxx.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#include "curl_setup.h" - -/* this is for creating aws_sigv4 header output */ -CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy); - -#endif /* HEADER_CURL_HTTP_AWS_SIGV4_H */ +#ifndef HEADER_CURL_HTTP_AWS_SIGV4_H
+#define HEADER_CURL_HTTP_AWS_SIGV4_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+/* this is for creating aws_sigv4 header output */
+CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy);
+
+#endif /* HEADER_CURL_HTTP_AWS_SIGV4_H */
diff --git a/libs/libcurl/src/http_chunks.c b/libs/libcurl/src/http_chunks.c index 0b836851ac..41ba6a314e 100644 --- a/libs/libcurl/src/http_chunks.c +++ b/libs/libcurl/src/http_chunks.c @@ -1,319 +1,319 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" /* it includes http_chunks.h */ -#include "sendf.h" /* for the client write stuff */ -#include "dynbuf.h" -#include "content_encoding.h" -#include "http.h" -#include "strtoofft.h" -#include "warnless.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* - * Chunk format (simplified): - * - * <HEX SIZE>[ chunk extension ] CRLF - * <DATA> CRLF - * - * Highlights from RFC2616 section 3.6 say: - - The chunked encoding modifies the body of a message in order to - transfer it as a series of chunks, each with its own size indicator, - followed by an OPTIONAL trailer containing entity-header fields. This - allows dynamically produced content to be transferred along with the - information necessary for the recipient to verify that it has - received the full message. - - Chunked-Body = *chunk - last-chunk - trailer - CRLF - - chunk = chunk-size [ chunk-extension ] CRLF - chunk-data CRLF - chunk-size = 1*HEX - last-chunk = 1*("0") [ chunk-extension ] CRLF - - chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) - chunk-ext-name = token - chunk-ext-val = token | quoted-string - chunk-data = chunk-size(OCTET) - trailer = *(entity-header CRLF) - - The chunk-size field is a string of hex digits indicating the size of - the chunk. The chunked encoding is ended by any chunk whose size is - zero, followed by the trailer, which is terminated by an empty line. - - */ - -#define isxdigit_ascii(x) Curl_isxdigit(x) - -void Curl_httpchunk_init(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - struct Curl_chunker *chunk = &conn->chunk; - chunk->hexindex = 0; /* start at 0 */ - chunk->state = CHUNK_HEX; /* we get hex first! */ - Curl_dyn_init(&conn->trailer, DYN_H1_TRAILER); -} - -/* - * chunk_read() returns a OK for normal operations, or a positive return code - * for errors. STOP means this sequence of chunks is complete. The 'wrote' - * argument is set to tell the caller how many bytes we actually passed to the - * client (for byte-counting and whatever). - * - * The states and the state-machine is further explained in the header file. - * - * This function always uses ASCII hex values to accommodate non-ASCII hosts. - * For example, 0x0d and 0x0a are used instead of '\r' and '\n'. - */ -CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, - char *datap, - ssize_t datalen, - ssize_t *wrote, - CURLcode *extrap) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct Curl_chunker *ch = &conn->chunk; - struct SingleRequest *k = &data->req; - size_t piece; - curl_off_t length = (curl_off_t)datalen; - - *wrote = 0; /* nothing's written yet */ - - /* the original data is written to the client, but we go on with the - chunk read process, to properly calculate the content length*/ - if(data->set.http_te_skip && !k->ignorebody) { - result = Curl_client_write(data, CLIENTWRITE_BODY, datap, datalen); - if(result) { - *extrap = result; - return CHUNKE_PASSTHRU_ERROR; - } - } - - while(length) { - switch(ch->state) { - case CHUNK_HEX: - if(ISXDIGIT(*datap)) { - if(ch->hexindex < CHUNK_MAXNUM_LEN) { - ch->hexbuffer[ch->hexindex] = *datap; - datap++; - length--; - ch->hexindex++; - } - else { - return CHUNKE_TOO_LONG_HEX; /* longer hex than we support */ - } - } - else { - char *endptr; - if(0 == ch->hexindex) - /* This is illegal data, we received junk where we expected - a hexadecimal digit. */ - return CHUNKE_ILLEGAL_HEX; - - /* length and datap are unmodified */ - ch->hexbuffer[ch->hexindex] = 0; - - if(curlx_strtoofft(ch->hexbuffer, &endptr, 16, &ch->datasize)) - return CHUNKE_ILLEGAL_HEX; - ch->state = CHUNK_LF; /* now wait for the CRLF */ - } - break; - - case CHUNK_LF: - /* waiting for the LF after a chunk size */ - if(*datap == 0x0a) { - /* we're now expecting data to come, unless size was zero! */ - if(0 == ch->datasize) { - ch->state = CHUNK_TRAILER; /* now check for trailers */ - } - else - ch->state = CHUNK_DATA; - } - - datap++; - length--; - break; - - case CHUNK_DATA: - /* We expect 'datasize' of data. We have 'length' right now, it can be - more or less than 'datasize'. Get the smallest piece. - */ - piece = curlx_sotouz((ch->datasize >= length)?length:ch->datasize); - - /* Write the data portion available */ - if(!data->set.http_te_skip && !k->ignorebody) { - if(!data->set.http_ce_skip && k->writer_stack) - result = Curl_unencode_write(data, k->writer_stack, datap, piece); - else - result = Curl_client_write(data, CLIENTWRITE_BODY, datap, piece); - - if(result) { - *extrap = result; - return CHUNKE_PASSTHRU_ERROR; - } - } - - *wrote += piece; - ch->datasize -= piece; /* decrease amount left to expect */ - datap += piece; /* move read pointer forward */ - length -= piece; /* decrease space left in this round */ - - if(0 == ch->datasize) - /* end of data this round, we now expect a trailing CRLF */ - ch->state = CHUNK_POSTLF; - break; - - case CHUNK_POSTLF: - if(*datap == 0x0a) { - /* The last one before we go back to hex state and start all over. */ - Curl_httpchunk_init(data); /* sets state back to CHUNK_HEX */ - } - else if(*datap != 0x0d) - return CHUNKE_BAD_CHUNK; - datap++; - length--; - break; - - case CHUNK_TRAILER: - if((*datap == 0x0d) || (*datap == 0x0a)) { - char *tr = Curl_dyn_ptr(&conn->trailer); - /* this is the end of a trailer, but if the trailer was zero bytes - there was no trailer and we move on */ - - if(tr) { - size_t trlen; - result = Curl_dyn_addn(&conn->trailer, (char *)STRCONST("\x0d\x0a")); - if(result) - return CHUNKE_OUT_OF_MEMORY; - - tr = Curl_dyn_ptr(&conn->trailer); - trlen = Curl_dyn_len(&conn->trailer); - if(!data->set.http_te_skip) { - result = Curl_client_write(data, - CLIENTWRITE_HEADER|CLIENTWRITE_TRAILER, - tr, trlen); - if(result) { - *extrap = result; - return CHUNKE_PASSTHRU_ERROR; - } - } - Curl_dyn_reset(&conn->trailer); - ch->state = CHUNK_TRAILER_CR; - if(*datap == 0x0a) - /* already on the LF */ - break; - } - else { - /* no trailer, we're on the final CRLF pair */ - ch->state = CHUNK_TRAILER_POSTCR; - break; /* don't advance the pointer */ - } - } - else { - result = Curl_dyn_addn(&conn->trailer, datap, 1); - if(result) - return CHUNKE_OUT_OF_MEMORY; - } - datap++; - length--; - break; - - case CHUNK_TRAILER_CR: - if(*datap == 0x0a) { - ch->state = CHUNK_TRAILER_POSTCR; - datap++; - length--; - } - else - return CHUNKE_BAD_CHUNK; - break; - - case CHUNK_TRAILER_POSTCR: - /* We enter this state when a CR should arrive so we expect to - have to first pass a CR before we wait for LF */ - if((*datap != 0x0d) && (*datap != 0x0a)) { - /* not a CR then it must be another header in the trailer */ - ch->state = CHUNK_TRAILER; - break; - } - if(*datap == 0x0d) { - /* skip if CR */ - datap++; - length--; - } - /* now wait for the final LF */ - ch->state = CHUNK_STOP; - break; - - case CHUNK_STOP: - if(*datap == 0x0a) { - length--; - - /* Record the length of any data left in the end of the buffer - even if there's no more chunks to read */ - ch->datasize = curlx_sotouz(length); - - return CHUNKE_STOP; /* return stop */ - } - else - return CHUNKE_BAD_CHUNK; - } - } - return CHUNKE_OK; -} - -const char *Curl_chunked_strerror(CHUNKcode code) -{ - switch(code) { - default: - return "OK"; - case CHUNKE_TOO_LONG_HEX: - return "Too long hexadecimal number"; - case CHUNKE_ILLEGAL_HEX: - return "Illegal or missing hexadecimal sequence"; - case CHUNKE_BAD_CHUNK: - return "Malformed encoding found"; - case CHUNKE_PASSTHRU_ERROR: - DEBUGASSERT(0); /* never used */ - return ""; - case CHUNKE_BAD_ENCODING: - return "Bad content-encoding found"; - case CHUNKE_OUT_OF_MEMORY: - return "Out of memory"; - } -} - -#endif /* CURL_DISABLE_HTTP */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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" /* it includes http_chunks.h */
+#include "sendf.h" /* for the client write stuff */
+#include "dynbuf.h"
+#include "content_encoding.h"
+#include "http.h"
+#include "strtoofft.h"
+#include "warnless.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * Chunk format (simplified):
+ *
+ * <HEX SIZE>[ chunk extension ] CRLF
+ * <DATA> CRLF
+ *
+ * Highlights from RFC2616 section 3.6 say:
+
+ The chunked encoding modifies the body of a message in order to
+ transfer it as a series of chunks, each with its own size indicator,
+ followed by an OPTIONAL trailer containing entity-header fields. This
+ allows dynamically produced content to be transferred along with the
+ information necessary for the recipient to verify that it has
+ received the full message.
+
+ Chunked-Body = *chunk
+ last-chunk
+ trailer
+ CRLF
+
+ chunk = chunk-size [ chunk-extension ] CRLF
+ chunk-data CRLF
+ chunk-size = 1*HEX
+ last-chunk = 1*("0") [ chunk-extension ] CRLF
+
+ chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
+ chunk-ext-name = token
+ chunk-ext-val = token | quoted-string
+ chunk-data = chunk-size(OCTET)
+ trailer = *(entity-header CRLF)
+
+ The chunk-size field is a string of hex digits indicating the size of
+ the chunk. The chunked encoding is ended by any chunk whose size is
+ zero, followed by the trailer, which is terminated by an empty line.
+
+ */
+
+#define isxdigit_ascii(x) Curl_isxdigit(x)
+
+void Curl_httpchunk_init(struct Curl_easy *data)
+{
+ struct connectdata *conn = data->conn;
+ struct Curl_chunker *chunk = &conn->chunk;
+ chunk->hexindex = 0; /* start at 0 */
+ chunk->state = CHUNK_HEX; /* we get hex first! */
+ Curl_dyn_init(&conn->trailer, DYN_H1_TRAILER);
+}
+
+/*
+ * chunk_read() returns a OK for normal operations, or a positive return code
+ * for errors. STOP means this sequence of chunks is complete. The 'wrote'
+ * argument is set to tell the caller how many bytes we actually passed to the
+ * client (for byte-counting and whatever).
+ *
+ * The states and the state-machine is further explained in the header file.
+ *
+ * This function always uses ASCII hex values to accommodate non-ASCII hosts.
+ * For example, 0x0d and 0x0a are used instead of '\r' and '\n'.
+ */
+CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
+ char *datap,
+ ssize_t datalen,
+ ssize_t *wrote,
+ CURLcode *extrap)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct Curl_chunker *ch = &conn->chunk;
+ struct SingleRequest *k = &data->req;
+ size_t piece;
+ curl_off_t length = (curl_off_t)datalen;
+
+ *wrote = 0; /* nothing's written yet */
+
+ /* the original data is written to the client, but we go on with the
+ chunk read process, to properly calculate the content length */
+ if(data->set.http_te_skip && !k->ignorebody) {
+ result = Curl_client_write(data, CLIENTWRITE_BODY, datap, datalen);
+ if(result) {
+ *extrap = result;
+ return CHUNKE_PASSTHRU_ERROR;
+ }
+ }
+
+ while(length) {
+ switch(ch->state) {
+ case CHUNK_HEX:
+ if(ISXDIGIT(*datap)) {
+ if(ch->hexindex < CHUNK_MAXNUM_LEN) {
+ ch->hexbuffer[ch->hexindex] = *datap;
+ datap++;
+ length--;
+ ch->hexindex++;
+ }
+ else {
+ return CHUNKE_TOO_LONG_HEX; /* longer hex than we support */
+ }
+ }
+ else {
+ char *endptr;
+ if(0 == ch->hexindex)
+ /* This is illegal data, we received junk where we expected
+ a hexadecimal digit. */
+ return CHUNKE_ILLEGAL_HEX;
+
+ /* length and datap are unmodified */
+ ch->hexbuffer[ch->hexindex] = 0;
+
+ if(curlx_strtoofft(ch->hexbuffer, &endptr, 16, &ch->datasize))
+ return CHUNKE_ILLEGAL_HEX;
+ ch->state = CHUNK_LF; /* now wait for the CRLF */
+ }
+ break;
+
+ case CHUNK_LF:
+ /* waiting for the LF after a chunk size */
+ if(*datap == 0x0a) {
+ /* we're now expecting data to come, unless size was zero! */
+ if(0 == ch->datasize) {
+ ch->state = CHUNK_TRAILER; /* now check for trailers */
+ }
+ else
+ ch->state = CHUNK_DATA;
+ }
+
+ datap++;
+ length--;
+ break;
+
+ case CHUNK_DATA:
+ /* We expect 'datasize' of data. We have 'length' right now, it can be
+ more or less than 'datasize'. Get the smallest piece.
+ */
+ piece = curlx_sotouz((ch->datasize >= length)?length:ch->datasize);
+
+ /* Write the data portion available */
+ if(!data->set.http_te_skip && !k->ignorebody) {
+ if(!data->set.http_ce_skip && k->writer_stack)
+ result = Curl_unencode_write(data, k->writer_stack, datap, piece);
+ else
+ result = Curl_client_write(data, CLIENTWRITE_BODY, datap, piece);
+
+ if(result) {
+ *extrap = result;
+ return CHUNKE_PASSTHRU_ERROR;
+ }
+ }
+
+ *wrote += piece;
+ ch->datasize -= piece; /* decrease amount left to expect */
+ datap += piece; /* move read pointer forward */
+ length -= piece; /* decrease space left in this round */
+
+ if(0 == ch->datasize)
+ /* end of data this round, we now expect a trailing CRLF */
+ ch->state = CHUNK_POSTLF;
+ break;
+
+ case CHUNK_POSTLF:
+ if(*datap == 0x0a) {
+ /* The last one before we go back to hex state and start all over. */
+ Curl_httpchunk_init(data); /* sets state back to CHUNK_HEX */
+ }
+ else if(*datap != 0x0d)
+ return CHUNKE_BAD_CHUNK;
+ datap++;
+ length--;
+ break;
+
+ case CHUNK_TRAILER:
+ if((*datap == 0x0d) || (*datap == 0x0a)) {
+ char *tr = Curl_dyn_ptr(&conn->trailer);
+ /* this is the end of a trailer, but if the trailer was zero bytes
+ there was no trailer and we move on */
+
+ if(tr) {
+ size_t trlen;
+ result = Curl_dyn_addn(&conn->trailer, (char *)STRCONST("\x0d\x0a"));
+ if(result)
+ return CHUNKE_OUT_OF_MEMORY;
+
+ tr = Curl_dyn_ptr(&conn->trailer);
+ trlen = Curl_dyn_len(&conn->trailer);
+ if(!data->set.http_te_skip) {
+ result = Curl_client_write(data,
+ CLIENTWRITE_HEADER|CLIENTWRITE_TRAILER,
+ tr, trlen);
+ if(result) {
+ *extrap = result;
+ return CHUNKE_PASSTHRU_ERROR;
+ }
+ }
+ Curl_dyn_reset(&conn->trailer);
+ ch->state = CHUNK_TRAILER_CR;
+ if(*datap == 0x0a)
+ /* already on the LF */
+ break;
+ }
+ else {
+ /* no trailer, we're on the final CRLF pair */
+ ch->state = CHUNK_TRAILER_POSTCR;
+ break; /* don't advance the pointer */
+ }
+ }
+ else {
+ result = Curl_dyn_addn(&conn->trailer, datap, 1);
+ if(result)
+ return CHUNKE_OUT_OF_MEMORY;
+ }
+ datap++;
+ length--;
+ break;
+
+ case CHUNK_TRAILER_CR:
+ if(*datap == 0x0a) {
+ ch->state = CHUNK_TRAILER_POSTCR;
+ datap++;
+ length--;
+ }
+ else
+ return CHUNKE_BAD_CHUNK;
+ break;
+
+ case CHUNK_TRAILER_POSTCR:
+ /* We enter this state when a CR should arrive so we expect to
+ have to first pass a CR before we wait for LF */
+ if((*datap != 0x0d) && (*datap != 0x0a)) {
+ /* not a CR then it must be another header in the trailer */
+ ch->state = CHUNK_TRAILER;
+ break;
+ }
+ if(*datap == 0x0d) {
+ /* skip if CR */
+ datap++;
+ length--;
+ }
+ /* now wait for the final LF */
+ ch->state = CHUNK_STOP;
+ break;
+
+ case CHUNK_STOP:
+ if(*datap == 0x0a) {
+ length--;
+
+ /* Record the length of any data left in the end of the buffer
+ even if there's no more chunks to read */
+ ch->datasize = curlx_sotouz(length);
+
+ return CHUNKE_STOP; /* return stop */
+ }
+ else
+ return CHUNKE_BAD_CHUNK;
+ }
+ }
+ return CHUNKE_OK;
+}
+
+const char *Curl_chunked_strerror(CHUNKcode code)
+{
+ switch(code) {
+ default:
+ return "OK";
+ case CHUNKE_TOO_LONG_HEX:
+ return "Too long hexadecimal number";
+ case CHUNKE_ILLEGAL_HEX:
+ return "Illegal or missing hexadecimal sequence";
+ case CHUNKE_BAD_CHUNK:
+ return "Malformed encoding found";
+ case CHUNKE_PASSTHRU_ERROR:
+ DEBUGASSERT(0); /* never used */
+ return "";
+ case CHUNKE_BAD_ENCODING:
+ return "Bad content-encoding found";
+ case CHUNKE_OUT_OF_MEMORY:
+ return "Out of memory";
+ }
+}
+
+#endif /* CURL_DISABLE_HTTP */
diff --git a/libs/libcurl/src/http_chunks.h b/libs/libcurl/src/http_chunks.h index 2cf5507c21..b96d6ec045 100644 --- a/libs/libcurl/src/http_chunks.h +++ b/libs/libcurl/src/http_chunks.h @@ -1,100 +1,100 @@ -#ifndef HEADER_CURL_HTTP_CHUNKS_H -#define HEADER_CURL_HTTP_CHUNKS_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 connectdata; - -/* - * The longest possible hexadecimal number we support in a chunked transfer. - * Neither RFC2616 nor the later HTTP specs define a maximum chunk size. - * For 64 bit curl_off_t we support 16 digits. For 32 bit, 8 digits. - */ -#define CHUNK_MAXNUM_LEN (SIZEOF_CURL_OFF_T * 2) - -typedef enum { - /* await and buffer all hexadecimal digits until we get one that isn't a - hexadecimal digit. When done, we go CHUNK_LF */ - CHUNK_HEX, - - /* wait for LF, ignore all else */ - CHUNK_LF, - - /* We eat the amount of data specified. When done, we move on to the - POST_CR state. */ - CHUNK_DATA, - - /* POSTLF should get a CR and then a LF and nothing else, then move back to - HEX as the CRLF combination marks the end of a chunk. A missing CR is no - big deal. */ - CHUNK_POSTLF, - - /* Used to mark that we're out of the game. NOTE: that there's a 'datasize' - field in the struct that will tell how many bytes that were not passed to - the client in the end of the last buffer! */ - CHUNK_STOP, - - /* At this point optional trailer headers can be found, unless the next line - is CRLF */ - CHUNK_TRAILER, - - /* A trailer CR has been found - next state is CHUNK_TRAILER_POSTCR. - Next char must be a LF */ - CHUNK_TRAILER_CR, - - /* A trailer LF must be found now, otherwise CHUNKE_BAD_CHUNK will be - signalled If this is an empty trailer CHUNKE_STOP will be signalled. - Otherwise the trailer will be broadcasted via Curl_client_write() and the - next state will be CHUNK_TRAILER */ - CHUNK_TRAILER_POSTCR -} ChunkyState; - -typedef enum { - CHUNKE_STOP = -1, - CHUNKE_OK = 0, - CHUNKE_TOO_LONG_HEX = 1, - CHUNKE_ILLEGAL_HEX, - CHUNKE_BAD_CHUNK, - CHUNKE_BAD_ENCODING, - CHUNKE_OUT_OF_MEMORY, - CHUNKE_PASSTHRU_ERROR, /* Curl_httpchunk_read() returns a CURLcode to use */ - CHUNKE_LAST -} CHUNKcode; - -const char *Curl_chunked_strerror(CHUNKcode code); - -struct Curl_chunker { - curl_off_t datasize; - ChunkyState state; - unsigned char hexindex; - char hexbuffer[ CHUNK_MAXNUM_LEN + 1]; /* +1 for null-terminator */ -}; - -/* The following functions are defined in http_chunks.c */ -void Curl_httpchunk_init(struct Curl_easy *data); -CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, char *datap, - ssize_t length, ssize_t *wrote, - CURLcode *passthru); - -#endif /* HEADER_CURL_HTTP_CHUNKS_H */ +#ifndef HEADER_CURL_HTTP_CHUNKS_H
+#define HEADER_CURL_HTTP_CHUNKS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 connectdata;
+
+/*
+ * The longest possible hexadecimal number we support in a chunked transfer.
+ * Neither RFC2616 nor the later HTTP specs define a maximum chunk size.
+ * For 64 bit curl_off_t we support 16 digits. For 32 bit, 8 digits.
+ */
+#define CHUNK_MAXNUM_LEN (SIZEOF_CURL_OFF_T * 2)
+
+typedef enum {
+ /* await and buffer all hexadecimal digits until we get one that isn't a
+ hexadecimal digit. When done, we go CHUNK_LF */
+ CHUNK_HEX,
+
+ /* wait for LF, ignore all else */
+ CHUNK_LF,
+
+ /* We eat the amount of data specified. When done, we move on to the
+ POST_CR state. */
+ CHUNK_DATA,
+
+ /* POSTLF should get a CR and then a LF and nothing else, then move back to
+ HEX as the CRLF combination marks the end of a chunk. A missing CR is no
+ big deal. */
+ CHUNK_POSTLF,
+
+ /* Used to mark that we're out of the game. NOTE: that there's a 'datasize'
+ field in the struct that will tell how many bytes that were not passed to
+ the client in the end of the last buffer! */
+ CHUNK_STOP,
+
+ /* At this point optional trailer headers can be found, unless the next line
+ is CRLF */
+ CHUNK_TRAILER,
+
+ /* A trailer CR has been found - next state is CHUNK_TRAILER_POSTCR.
+ Next char must be a LF */
+ CHUNK_TRAILER_CR,
+
+ /* A trailer LF must be found now, otherwise CHUNKE_BAD_CHUNK will be
+ signalled If this is an empty trailer CHUNKE_STOP will be signalled.
+ Otherwise the trailer will be broadcasted via Curl_client_write() and the
+ next state will be CHUNK_TRAILER */
+ CHUNK_TRAILER_POSTCR
+} ChunkyState;
+
+typedef enum {
+ CHUNKE_STOP = -1,
+ CHUNKE_OK = 0,
+ CHUNKE_TOO_LONG_HEX = 1,
+ CHUNKE_ILLEGAL_HEX,
+ CHUNKE_BAD_CHUNK,
+ CHUNKE_BAD_ENCODING,
+ CHUNKE_OUT_OF_MEMORY,
+ CHUNKE_PASSTHRU_ERROR, /* Curl_httpchunk_read() returns a CURLcode to use */
+ CHUNKE_LAST
+} CHUNKcode;
+
+const char *Curl_chunked_strerror(CHUNKcode code);
+
+struct Curl_chunker {
+ curl_off_t datasize;
+ ChunkyState state;
+ unsigned char hexindex;
+ char hexbuffer[ CHUNK_MAXNUM_LEN + 1]; /* +1 for null-terminator */
+};
+
+/* The following functions are defined in http_chunks.c */
+void Curl_httpchunk_init(struct Curl_easy *data);
+CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, char *datap,
+ ssize_t length, ssize_t *wrote,
+ CURLcode *passthru);
+
+#endif /* HEADER_CURL_HTTP_CHUNKS_H */
diff --git a/libs/libcurl/src/http_digest.c b/libs/libcurl/src/http_digest.c index a71c6b7cfb..33a9193e2e 100644 --- a/libs/libcurl/src/http_digest.c +++ b/libs/libcurl/src/http_digest.c @@ -1,185 +1,185 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_CRYPTO_AUTH) - -#include "urldata.h" -#include "strcase.h" -#include "vauth/vauth.h" -#include "http_digest.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* Test example headers: - -WWW-Authenticate: Digest realm="testrealm", nonce="1053604598" -Proxy-Authenticate: Digest realm="testrealm", nonce="1053604598" - -*/ - -CURLcode Curl_input_digest(struct Curl_easy *data, - bool proxy, - const char *header) /* rest of the *-authenticate: - header */ -{ - /* Point to the correct struct with this */ - struct digestdata *digest; - - if(proxy) { - digest = &data->state.proxydigest; - } - else { - digest = &data->state.digest; - } - - if(!checkprefix("Digest", header) || !ISBLANK(header[6])) - return CURLE_BAD_CONTENT_ENCODING; - - header += strlen("Digest"); - while(*header && ISBLANK(*header)) - header++; - - return Curl_auth_decode_digest_http_message(header, digest); -} - -CURLcode Curl_output_digest(struct Curl_easy *data, - bool proxy, - const unsigned char *request, - const unsigned char *uripath) -{ - CURLcode result; - unsigned char *path = NULL; - char *tmp = NULL; - char *response; - size_t len; - bool have_chlg; - - /* Point to the address of the pointer that holds the string to send to the - server, which is for a plain host or for a HTTP proxy */ - char **allocuserpwd; - - /* Point to the name and password for this */ - const char *userp; - const char *passwdp; - - /* Point to the correct struct with this */ - struct digestdata *digest; - struct auth *authp; - - if(proxy) { -#ifdef CURL_DISABLE_PROXY - return CURLE_NOT_BUILT_IN; -#else - digest = &data->state.proxydigest; - allocuserpwd = &data->state.aptr.proxyuserpwd; - userp = data->state.aptr.proxyuser; - passwdp = data->state.aptr.proxypasswd; - authp = &data->state.authproxy; -#endif - } - else { - digest = &data->state.digest; - allocuserpwd = &data->state.aptr.userpwd; - userp = data->state.aptr.user; - passwdp = data->state.aptr.passwd; - authp = &data->state.authhost; - } - - Curl_safefree(*allocuserpwd); - - /* not set means empty */ - if(!userp) - userp = ""; - - if(!passwdp) - passwdp = ""; - -#if defined(USE_WINDOWS_SSPI) - have_chlg = digest->input_token ? TRUE : FALSE; -#else - have_chlg = digest->nonce ? TRUE : FALSE; -#endif - - if(!have_chlg) { - authp->done = FALSE; - return CURLE_OK; - } - - /* So IE browsers < v7 cut off the URI part at the query part when they - evaluate the MD5 and some (IIS?) servers work with them so we may need to - do the Digest IE-style. Note that the different ways cause different MD5 - sums to get sent. - - Apache servers can be set to do the Digest IE-style automatically using - the BrowserMatch feature: - https://httpd.apache.org/docs/2.2/mod/mod_auth_digest.html#msie - - Further details on Digest implementation differences: - http://www.fngtps.com/2006/09/http-authentication - */ - - if(authp->iestyle) { - tmp = strchr((char *)uripath, '?'); - if(tmp) { - size_t urilen = tmp - (char *)uripath; - /* typecast is fine here since the value is always less than 32 bits */ - path = (unsigned char *) aprintf("%.*s", (int)urilen, uripath); - } - } - if(!tmp) - path = (unsigned char *) strdup((char *) uripath); - - if(!path) - return CURLE_OUT_OF_MEMORY; - - result = Curl_auth_create_digest_http_message(data, userp, passwdp, request, - path, digest, &response, &len); - free(path); - if(result) - return result; - - *allocuserpwd = aprintf("%sAuthorization: Digest %s\r\n", - proxy ? "Proxy-" : "", - response); - free(response); - if(!*allocuserpwd) - return CURLE_OUT_OF_MEMORY; - - authp->done = TRUE; - - return CURLE_OK; -} - -void Curl_http_auth_cleanup_digest(struct Curl_easy *data) -{ - Curl_auth_digest_cleanup(&data->state.digest); - Curl_auth_digest_cleanup(&data->state.proxydigest); -} - -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_CRYPTO_AUTH)
+
+#include "urldata.h"
+#include "strcase.h"
+#include "vauth/vauth.h"
+#include "http_digest.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* Test example headers:
+
+WWW-Authenticate: Digest realm="testrealm", nonce="1053604598"
+Proxy-Authenticate: Digest realm="testrealm", nonce="1053604598"
+
+*/
+
+CURLcode Curl_input_digest(struct Curl_easy *data,
+ bool proxy,
+ const char *header) /* rest of the *-authenticate:
+ header */
+{
+ /* Point to the correct struct with this */
+ struct digestdata *digest;
+
+ if(proxy) {
+ digest = &data->state.proxydigest;
+ }
+ else {
+ digest = &data->state.digest;
+ }
+
+ if(!checkprefix("Digest", header) || !ISBLANK(header[6]))
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ header += strlen("Digest");
+ while(*header && ISBLANK(*header))
+ header++;
+
+ return Curl_auth_decode_digest_http_message(header, digest);
+}
+
+CURLcode Curl_output_digest(struct Curl_easy *data,
+ bool proxy,
+ const unsigned char *request,
+ const unsigned char *uripath)
+{
+ CURLcode result;
+ unsigned char *path = NULL;
+ char *tmp = NULL;
+ char *response;
+ size_t len;
+ bool have_chlg;
+
+ /* Point to the address of the pointer that holds the string to send to the
+ server, which is for a plain host or for an HTTP proxy */
+ char **allocuserpwd;
+
+ /* Point to the name and password for this */
+ const char *userp;
+ const char *passwdp;
+
+ /* Point to the correct struct with this */
+ struct digestdata *digest;
+ struct auth *authp;
+
+ if(proxy) {
+#ifdef CURL_DISABLE_PROXY
+ return CURLE_NOT_BUILT_IN;
+#else
+ digest = &data->state.proxydigest;
+ allocuserpwd = &data->state.aptr.proxyuserpwd;
+ userp = data->state.aptr.proxyuser;
+ passwdp = data->state.aptr.proxypasswd;
+ authp = &data->state.authproxy;
+#endif
+ }
+ else {
+ digest = &data->state.digest;
+ allocuserpwd = &data->state.aptr.userpwd;
+ userp = data->state.aptr.user;
+ passwdp = data->state.aptr.passwd;
+ authp = &data->state.authhost;
+ }
+
+ Curl_safefree(*allocuserpwd);
+
+ /* not set means empty */
+ if(!userp)
+ userp = "";
+
+ if(!passwdp)
+ passwdp = "";
+
+#if defined(USE_WINDOWS_SSPI)
+ have_chlg = digest->input_token ? TRUE : FALSE;
+#else
+ have_chlg = digest->nonce ? TRUE : FALSE;
+#endif
+
+ if(!have_chlg) {
+ authp->done = FALSE;
+ return CURLE_OK;
+ }
+
+ /* So IE browsers < v7 cut off the URI part at the query part when they
+ evaluate the MD5 and some (IIS?) servers work with them so we may need to
+ do the Digest IE-style. Note that the different ways cause different MD5
+ sums to get sent.
+
+ Apache servers can be set to do the Digest IE-style automatically using
+ the BrowserMatch feature:
+ https://httpd.apache.org/docs/2.2/mod/mod_auth_digest.html#msie
+
+ Further details on Digest implementation differences:
+ http://www.fngtps.com/2006/09/http-authentication
+ */
+
+ if(authp->iestyle) {
+ tmp = strchr((char *)uripath, '?');
+ if(tmp) {
+ size_t urilen = tmp - (char *)uripath;
+ /* typecast is fine here since the value is always less than 32 bits */
+ path = (unsigned char *) aprintf("%.*s", (int)urilen, uripath);
+ }
+ }
+ if(!tmp)
+ path = (unsigned char *) strdup((char *) uripath);
+
+ if(!path)
+ return CURLE_OUT_OF_MEMORY;
+
+ result = Curl_auth_create_digest_http_message(data, userp, passwdp, request,
+ path, digest, &response, &len);
+ free(path);
+ if(result)
+ return result;
+
+ *allocuserpwd = aprintf("%sAuthorization: Digest %s\r\n",
+ proxy ? "Proxy-" : "",
+ response);
+ free(response);
+ if(!*allocuserpwd)
+ return CURLE_OUT_OF_MEMORY;
+
+ authp->done = TRUE;
+
+ return CURLE_OK;
+}
+
+void Curl_http_auth_cleanup_digest(struct Curl_easy *data)
+{
+ Curl_auth_digest_cleanup(&data->state.digest);
+ Curl_auth_digest_cleanup(&data->state.proxydigest);
+}
+
+#endif
diff --git a/libs/libcurl/src/http_digest.h b/libs/libcurl/src/http_digest.h index eea90b7439..26c22b1c3e 100644 --- a/libs/libcurl/src/http_digest.h +++ b/libs/libcurl/src/http_digest.h @@ -1,44 +1,44 @@ -#ifndef HEADER_CURL_HTTP_DIGEST_H -#define HEADER_CURL_HTTP_DIGEST_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_CRYPTO_AUTH) - -/* this is for digest header input */ -CURLcode Curl_input_digest(struct Curl_easy *data, - bool proxy, const char *header); - -/* this is for creating digest header output */ -CURLcode Curl_output_digest(struct Curl_easy *data, - bool proxy, - const unsigned char *request, - const unsigned char *uripath); - -void Curl_http_auth_cleanup_digest(struct Curl_easy *data); - -#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_CRYPTO_AUTH */ - -#endif /* HEADER_CURL_HTTP_DIGEST_H */ +#ifndef HEADER_CURL_HTTP_DIGEST_H
+#define HEADER_CURL_HTTP_DIGEST_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_CRYPTO_AUTH)
+
+/* this is for digest header input */
+CURLcode Curl_input_digest(struct Curl_easy *data,
+ bool proxy, const char *header);
+
+/* this is for creating digest header output */
+CURLcode Curl_output_digest(struct Curl_easy *data,
+ bool proxy,
+ const unsigned char *request,
+ const unsigned char *uripath);
+
+void Curl_http_auth_cleanup_digest(struct Curl_easy *data);
+
+#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_CRYPTO_AUTH */
+
+#endif /* HEADER_CURL_HTTP_DIGEST_H */
diff --git a/libs/libcurl/src/http_negotiate.c b/libs/libcurl/src/http_negotiate.c index 5909f85b0d..7c11a4dead 100644 --- a/libs/libcurl/src/http_negotiate.c +++ b/libs/libcurl/src/http_negotiate.c @@ -1,224 +1,224 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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(USE_SPNEGO) - -#include "urldata.h" -#include "sendf.h" -#include "http_negotiate.h" -#include "vauth/vauth.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, - bool proxy, const char *header) -{ - CURLcode result; - size_t len; - - /* Point to the username, password, service and host */ - const char *userp; - const char *passwdp; - const char *service; - const char *host; - - /* Point to the correct struct with this */ - struct negotiatedata *neg_ctx; - curlnegotiate state; - - if(proxy) { -#ifndef CURL_DISABLE_PROXY - userp = conn->http_proxy.user; - passwdp = conn->http_proxy.passwd; - service = data->set.str[STRING_PROXY_SERVICE_NAME] ? - data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; - host = conn->http_proxy.host.name; - neg_ctx = &conn->proxyneg; - state = conn->proxy_negotiate_state; -#else - return CURLE_NOT_BUILT_IN; -#endif - } - else { - userp = conn->user; - passwdp = conn->passwd; - service = data->set.str[STRING_SERVICE_NAME] ? - data->set.str[STRING_SERVICE_NAME] : "HTTP"; - host = conn->host.name; - neg_ctx = &conn->negotiate; - state = conn->http_negotiate_state; - } - - /* Not set means empty */ - if(!userp) - userp = ""; - - if(!passwdp) - passwdp = ""; - - /* Obtain the input token, if any */ - header += strlen("Negotiate"); - while(*header && ISBLANK(*header)) - header++; - - len = strlen(header); - neg_ctx->havenegdata = len != 0; - if(!len) { - if(state == GSS_AUTHSUCC) { - infof(data, "Negotiate auth restarted"); - Curl_http_auth_cleanup_negotiate(conn); - } - else if(state != GSS_AUTHNONE) { - /* The server rejected our authentication and hasn't supplied any more - negotiation mechanisms */ - Curl_http_auth_cleanup_negotiate(conn); - return CURLE_LOGIN_DENIED; - } - } - - /* Supports SSL channel binding for Windows ISS extended protection */ -#if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS) - neg_ctx->sslContext = conn->sslContext; -#endif - - /* Initialize the security context and decode our challenge */ - result = Curl_auth_decode_spnego_message(data, userp, passwdp, service, - host, header, neg_ctx); - - if(result) - Curl_http_auth_cleanup_negotiate(conn); - - return result; -} - -CURLcode Curl_output_negotiate(struct Curl_easy *data, - struct connectdata *conn, bool proxy) -{ - struct negotiatedata *neg_ctx = proxy ? &conn->proxyneg : - &conn->negotiate; - struct auth *authp = proxy ? &data->state.authproxy : &data->state.authhost; - curlnegotiate *state = proxy ? &conn->proxy_negotiate_state : - &conn->http_negotiate_state; - char *base64 = NULL; - size_t len = 0; - char *userp; - CURLcode result; - - authp->done = FALSE; - - if(*state == GSS_AUTHRECV) { - if(neg_ctx->havenegdata) { - neg_ctx->havemultiplerequests = TRUE; - } - } - else if(*state == GSS_AUTHSUCC) { - if(!neg_ctx->havenoauthpersist) { - neg_ctx->noauthpersist = !neg_ctx->havemultiplerequests; - } - } - - if(neg_ctx->noauthpersist || - (*state != GSS_AUTHDONE && *state != GSS_AUTHSUCC)) { - - if(neg_ctx->noauthpersist && *state == GSS_AUTHSUCC) { - infof(data, "Curl_output_negotiate, " - "no persistent authentication: cleanup existing context"); - Curl_http_auth_cleanup_negotiate(conn); - } - if(!neg_ctx->context) { - result = Curl_input_negotiate(data, conn, proxy, "Negotiate"); - if(result == CURLE_AUTH_ERROR) { - /* negotiate auth failed, let's continue unauthenticated to stay - * compatible with the behavior before curl-7_64_0-158-g6c6035532 */ - authp->done = TRUE; - return CURLE_OK; - } - else if(result) - return result; - } - - result = Curl_auth_create_spnego_message(neg_ctx, &base64, &len); - if(result) - return result; - - userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "", - base64); - - if(proxy) { - Curl_safefree(data->state.aptr.proxyuserpwd); - data->state.aptr.proxyuserpwd = userp; - } - else { - Curl_safefree(data->state.aptr.userpwd); - data->state.aptr.userpwd = userp; - } - - free(base64); - - if(!userp) { - return CURLE_OUT_OF_MEMORY; - } - - *state = GSS_AUTHSENT; - #ifdef HAVE_GSSAPI - if(neg_ctx->status == GSS_S_COMPLETE || - neg_ctx->status == GSS_S_CONTINUE_NEEDED) { - *state = GSS_AUTHDONE; - } - #else - #ifdef USE_WINDOWS_SSPI - if(neg_ctx->status == SEC_E_OK || - neg_ctx->status == SEC_I_CONTINUE_NEEDED) { - *state = GSS_AUTHDONE; - } - #endif - #endif - } - - if(*state == GSS_AUTHDONE || *state == GSS_AUTHSUCC) { - /* connection is already authenticated, - * don't send a header in future requests */ - authp->done = TRUE; - } - - neg_ctx->havenegdata = FALSE; - - return CURLE_OK; -} - -void Curl_http_auth_cleanup_negotiate(struct connectdata *conn) -{ - conn->http_negotiate_state = GSS_AUTHNONE; - conn->proxy_negotiate_state = GSS_AUTHNONE; - - Curl_auth_cleanup_spnego(&conn->negotiate); - Curl_auth_cleanup_spnego(&conn->proxyneg); -} - -#endif /* !CURL_DISABLE_HTTP && USE_SPNEGO */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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(USE_SPNEGO)
+
+#include "urldata.h"
+#include "sendf.h"
+#include "http_negotiate.h"
+#include "vauth/vauth.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
+ bool proxy, const char *header)
+{
+ CURLcode result;
+ size_t len;
+
+ /* Point to the username, password, service and host */
+ const char *userp;
+ const char *passwdp;
+ const char *service;
+ const char *host;
+
+ /* Point to the correct struct with this */
+ struct negotiatedata *neg_ctx;
+ curlnegotiate state;
+
+ if(proxy) {
+#ifndef CURL_DISABLE_PROXY
+ userp = conn->http_proxy.user;
+ passwdp = conn->http_proxy.passwd;
+ service = data->set.str[STRING_PROXY_SERVICE_NAME] ?
+ data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP";
+ host = conn->http_proxy.host.name;
+ neg_ctx = &conn->proxyneg;
+ state = conn->proxy_negotiate_state;
+#else
+ return CURLE_NOT_BUILT_IN;
+#endif
+ }
+ else {
+ userp = conn->user;
+ passwdp = conn->passwd;
+ service = data->set.str[STRING_SERVICE_NAME] ?
+ data->set.str[STRING_SERVICE_NAME] : "HTTP";
+ host = conn->host.name;
+ neg_ctx = &conn->negotiate;
+ state = conn->http_negotiate_state;
+ }
+
+ /* Not set means empty */
+ if(!userp)
+ userp = "";
+
+ if(!passwdp)
+ passwdp = "";
+
+ /* Obtain the input token, if any */
+ header += strlen("Negotiate");
+ while(*header && ISBLANK(*header))
+ header++;
+
+ len = strlen(header);
+ neg_ctx->havenegdata = len != 0;
+ if(!len) {
+ if(state == GSS_AUTHSUCC) {
+ infof(data, "Negotiate auth restarted");
+ Curl_http_auth_cleanup_negotiate(conn);
+ }
+ else if(state != GSS_AUTHNONE) {
+ /* The server rejected our authentication and hasn't supplied any more
+ negotiation mechanisms */
+ Curl_http_auth_cleanup_negotiate(conn);
+ return CURLE_LOGIN_DENIED;
+ }
+ }
+
+ /* Supports SSL channel binding for Windows ISS extended protection */
+#if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS)
+ neg_ctx->sslContext = conn->sslContext;
+#endif
+
+ /* Initialize the security context and decode our challenge */
+ result = Curl_auth_decode_spnego_message(data, userp, passwdp, service,
+ host, header, neg_ctx);
+
+ if(result)
+ Curl_http_auth_cleanup_negotiate(conn);
+
+ return result;
+}
+
+CURLcode Curl_output_negotiate(struct Curl_easy *data,
+ struct connectdata *conn, bool proxy)
+{
+ struct negotiatedata *neg_ctx = proxy ? &conn->proxyneg :
+ &conn->negotiate;
+ struct auth *authp = proxy ? &data->state.authproxy : &data->state.authhost;
+ curlnegotiate *state = proxy ? &conn->proxy_negotiate_state :
+ &conn->http_negotiate_state;
+ char *base64 = NULL;
+ size_t len = 0;
+ char *userp;
+ CURLcode result;
+
+ authp->done = FALSE;
+
+ if(*state == GSS_AUTHRECV) {
+ if(neg_ctx->havenegdata) {
+ neg_ctx->havemultiplerequests = TRUE;
+ }
+ }
+ else if(*state == GSS_AUTHSUCC) {
+ if(!neg_ctx->havenoauthpersist) {
+ neg_ctx->noauthpersist = !neg_ctx->havemultiplerequests;
+ }
+ }
+
+ if(neg_ctx->noauthpersist ||
+ (*state != GSS_AUTHDONE && *state != GSS_AUTHSUCC)) {
+
+ if(neg_ctx->noauthpersist && *state == GSS_AUTHSUCC) {
+ infof(data, "Curl_output_negotiate, "
+ "no persistent authentication: cleanup existing context");
+ Curl_http_auth_cleanup_negotiate(conn);
+ }
+ if(!neg_ctx->context) {
+ result = Curl_input_negotiate(data, conn, proxy, "Negotiate");
+ if(result == CURLE_AUTH_ERROR) {
+ /* negotiate auth failed, let's continue unauthenticated to stay
+ * compatible with the behavior before curl-7_64_0-158-g6c6035532 */
+ authp->done = TRUE;
+ return CURLE_OK;
+ }
+ else if(result)
+ return result;
+ }
+
+ result = Curl_auth_create_spnego_message(neg_ctx, &base64, &len);
+ if(result)
+ return result;
+
+ userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "",
+ base64);
+
+ if(proxy) {
+ Curl_safefree(data->state.aptr.proxyuserpwd);
+ data->state.aptr.proxyuserpwd = userp;
+ }
+ else {
+ Curl_safefree(data->state.aptr.userpwd);
+ data->state.aptr.userpwd = userp;
+ }
+
+ free(base64);
+
+ if(!userp) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ *state = GSS_AUTHSENT;
+ #ifdef HAVE_GSSAPI
+ if(neg_ctx->status == GSS_S_COMPLETE ||
+ neg_ctx->status == GSS_S_CONTINUE_NEEDED) {
+ *state = GSS_AUTHDONE;
+ }
+ #else
+ #ifdef USE_WINDOWS_SSPI
+ if(neg_ctx->status == SEC_E_OK ||
+ neg_ctx->status == SEC_I_CONTINUE_NEEDED) {
+ *state = GSS_AUTHDONE;
+ }
+ #endif
+ #endif
+ }
+
+ if(*state == GSS_AUTHDONE || *state == GSS_AUTHSUCC) {
+ /* connection is already authenticated,
+ * don't send a header in future requests */
+ authp->done = TRUE;
+ }
+
+ neg_ctx->havenegdata = FALSE;
+
+ return CURLE_OK;
+}
+
+void Curl_http_auth_cleanup_negotiate(struct connectdata *conn)
+{
+ conn->http_negotiate_state = GSS_AUTHNONE;
+ conn->proxy_negotiate_state = GSS_AUTHNONE;
+
+ Curl_auth_cleanup_spnego(&conn->negotiate);
+ Curl_auth_cleanup_spnego(&conn->proxyneg);
+}
+
+#endif /* !CURL_DISABLE_HTTP && USE_SPNEGO */
diff --git a/libs/libcurl/src/http_negotiate.h b/libs/libcurl/src/http_negotiate.h index 6e2096c697..f1dd7d47fa 100644 --- a/libs/libcurl/src/http_negotiate.h +++ b/libs/libcurl/src/http_negotiate.h @@ -1,43 +1,43 @@ -#ifndef HEADER_CURL_HTTP_NEGOTIATE_H -#define HEADER_CURL_HTTP_NEGOTIATE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_HTTP) && defined(USE_SPNEGO) - -/* this is for Negotiate header input */ -CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, - bool proxy, const char *header); - -/* this is for creating Negotiate header output */ -CURLcode Curl_output_negotiate(struct Curl_easy *data, - struct connectdata *conn, bool proxy); - -void Curl_http_auth_cleanup_negotiate(struct connectdata *conn); - -#else /* !CURL_DISABLE_HTTP && USE_SPNEGO */ -#define Curl_http_auth_cleanup_negotiate(x) -#endif - -#endif /* HEADER_CURL_HTTP_NEGOTIATE_H */ +#ifndef HEADER_CURL_HTTP_NEGOTIATE_H
+#define HEADER_CURL_HTTP_NEGOTIATE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_HTTP) && defined(USE_SPNEGO)
+
+/* this is for Negotiate header input */
+CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
+ bool proxy, const char *header);
+
+/* this is for creating Negotiate header output */
+CURLcode Curl_output_negotiate(struct Curl_easy *data,
+ struct connectdata *conn, bool proxy);
+
+void Curl_http_auth_cleanup_negotiate(struct connectdata *conn);
+
+#else /* !CURL_DISABLE_HTTP && USE_SPNEGO */
+#define Curl_http_auth_cleanup_negotiate(x)
+#endif
+
+#endif /* HEADER_CURL_HTTP_NEGOTIATE_H */
diff --git a/libs/libcurl/src/http_ntlm.c b/libs/libcurl/src/http_ntlm.c index 5a6a977905..29e265dacb 100644 --- a/libs/libcurl/src/http_ntlm.c +++ b/libs/libcurl/src/http_ntlm.c @@ -1,275 +1,275 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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(USE_NTLM) - -/* - * NTLM details: - * - * https://davenport.sourceforge.net/ntlm.html - * https://www.innovation.ch/java/ntlm.html - */ - -#define DEBUG_ME 0 - -#include "urldata.h" -#include "sendf.h" -#include "strcase.h" -#include "http_ntlm.h" -#include "curl_ntlm_core.h" -#include "curl_ntlm_wb.h" -#include "curl_base64.h" -#include "vauth/vauth.h" -#include "url.h" - -/* SSL backend-specific #if branches in this file must be kept in the order - documented in curl_ntlm_core. */ -#if defined(USE_WINDOWS_SSPI) -#include "curl_sspi.h" -#endif - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#if DEBUG_ME -# define DEBUG_OUT(x) x -#else -# define DEBUG_OUT(x) Curl_nop_stmt -#endif - -CURLcode Curl_input_ntlm(struct Curl_easy *data, - bool proxy, /* if proxy or not */ - const char *header) /* rest of the www-authenticate: - header */ -{ - /* point to the correct struct with this */ - struct ntlmdata *ntlm; - curlntlm *state; - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - - ntlm = proxy ? &conn->proxyntlm : &conn->ntlm; - state = proxy ? &conn->proxy_ntlm_state : &conn->http_ntlm_state; - - if(checkprefix("NTLM", header)) { - header += strlen("NTLM"); - - while(*header && ISSPACE(*header)) - header++; - - if(*header) { - unsigned char *hdr; - size_t hdrlen; - - result = Curl_base64_decode(header, &hdr, &hdrlen); - if(!result) { - struct bufref hdrbuf; - - Curl_bufref_init(&hdrbuf); - Curl_bufref_set(&hdrbuf, hdr, hdrlen, curl_free); - result = Curl_auth_decode_ntlm_type2_message(data, &hdrbuf, ntlm); - Curl_bufref_free(&hdrbuf); - } - if(result) - return result; - - *state = NTLMSTATE_TYPE2; /* We got a type-2 message */ - } - else { - if(*state == NTLMSTATE_LAST) { - infof(data, "NTLM auth restarted"); - Curl_http_auth_cleanup_ntlm(conn); - } - else if(*state == NTLMSTATE_TYPE3) { - infof(data, "NTLM handshake rejected"); - Curl_http_auth_cleanup_ntlm(conn); - *state = NTLMSTATE_NONE; - return CURLE_REMOTE_ACCESS_DENIED; - } - else if(*state >= NTLMSTATE_TYPE1) { - infof(data, "NTLM handshake failure (internal error)"); - return CURLE_REMOTE_ACCESS_DENIED; - } - - *state = NTLMSTATE_TYPE1; /* We should send away a type-1 */ - } - } - - return result; -} - -/* - * This is for creating ntlm header output - */ -CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) -{ - char *base64 = NULL; - size_t len = 0; - CURLcode result = CURLE_OK; - struct bufref ntlmmsg; - - /* point to the address of the pointer that holds the string to send to the - server, which is for a plain host or for a HTTP proxy */ - char **allocuserpwd; - - /* point to the username, password, service and host */ - const char *userp; - const char *passwdp; - const char *service = NULL; - const char *hostname = NULL; - - /* point to the correct struct with this */ - struct ntlmdata *ntlm; - curlntlm *state; - struct auth *authp; - struct connectdata *conn = data->conn; - - DEBUGASSERT(conn); - DEBUGASSERT(data); - - if(proxy) { -#ifndef CURL_DISABLE_PROXY - allocuserpwd = &data->state.aptr.proxyuserpwd; - userp = data->state.aptr.proxyuser; - passwdp = data->state.aptr.proxypasswd; - service = data->set.str[STRING_PROXY_SERVICE_NAME] ? - data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; - hostname = conn->http_proxy.host.name; - ntlm = &conn->proxyntlm; - state = &conn->proxy_ntlm_state; - authp = &data->state.authproxy; -#else - return CURLE_NOT_BUILT_IN; -#endif - } - else { - allocuserpwd = &data->state.aptr.userpwd; - userp = data->state.aptr.user; - passwdp = data->state.aptr.passwd; - service = data->set.str[STRING_SERVICE_NAME] ? - data->set.str[STRING_SERVICE_NAME] : "HTTP"; - hostname = conn->host.name; - ntlm = &conn->ntlm; - state = &conn->http_ntlm_state; - authp = &data->state.authhost; - } - authp->done = FALSE; - - /* not set means empty */ - if(!userp) - userp = ""; - - if(!passwdp) - passwdp = ""; - -#ifdef USE_WINDOWS_SSPI - if(!s_hSecDll) { - /* not thread safe and leaks - use curl_global_init() to avoid */ - CURLcode err = Curl_sspi_global_init(); - if(!s_hSecDll) - return err; - } -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS - ntlm->sslContext = conn->sslContext; -#endif -#endif - - Curl_bufref_init(&ntlmmsg); - - /* connection is already authenticated, don't send a header in future - * requests so go directly to NTLMSTATE_LAST */ - if(*state == NTLMSTATE_TYPE3) - *state = NTLMSTATE_LAST; - - switch(*state) { - case NTLMSTATE_TYPE1: - default: /* for the weird cases we (re)start here */ - /* Create a type-1 message */ - result = Curl_auth_create_ntlm_type1_message(data, userp, passwdp, - service, hostname, - ntlm, &ntlmmsg); - if(!result) { - DEBUGASSERT(Curl_bufref_len(&ntlmmsg) != 0); - result = Curl_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg), - Curl_bufref_len(&ntlmmsg), &base64, &len); - if(!result) { - free(*allocuserpwd); - *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", - proxy ? "Proxy-" : "", - base64); - free(base64); - if(!*allocuserpwd) - result = CURLE_OUT_OF_MEMORY; - } - } - break; - - case NTLMSTATE_TYPE2: - /* We already received the type-2 message, create a type-3 message */ - result = Curl_auth_create_ntlm_type3_message(data, userp, passwdp, - ntlm, &ntlmmsg); - if(!result && Curl_bufref_len(&ntlmmsg)) { - result = Curl_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg), - Curl_bufref_len(&ntlmmsg), &base64, &len); - if(!result) { - free(*allocuserpwd); - *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", - proxy ? "Proxy-" : "", - base64); - free(base64); - if(!*allocuserpwd) - result = CURLE_OUT_OF_MEMORY; - else { - *state = NTLMSTATE_TYPE3; /* we send a type-3 */ - authp->done = TRUE; - } - } - } - break; - - case NTLMSTATE_LAST: - Curl_safefree(*allocuserpwd); - authp->done = TRUE; - break; - } - Curl_bufref_free(&ntlmmsg); - - return result; -} - -void Curl_http_auth_cleanup_ntlm(struct connectdata *conn) -{ - Curl_auth_cleanup_ntlm(&conn->ntlm); - Curl_auth_cleanup_ntlm(&conn->proxyntlm); - -#if defined(NTLM_WB_ENABLED) - Curl_http_auth_cleanup_ntlm_wb(conn); -#endif -} - -#endif /* !CURL_DISABLE_HTTP && USE_NTLM */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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(USE_NTLM)
+
+/*
+ * NTLM details:
+ *
+ * https://davenport.sourceforge.net/ntlm.html
+ * https://www.innovation.ch/java/ntlm.html
+ */
+
+#define DEBUG_ME 0
+
+#include "urldata.h"
+#include "sendf.h"
+#include "strcase.h"
+#include "http_ntlm.h"
+#include "curl_ntlm_core.h"
+#include "curl_ntlm_wb.h"
+#include "curl_base64.h"
+#include "vauth/vauth.h"
+#include "url.h"
+
+/* SSL backend-specific #if branches in this file must be kept in the order
+ documented in curl_ntlm_core. */
+#if defined(USE_WINDOWS_SSPI)
+#include "curl_sspi.h"
+#endif
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#if DEBUG_ME
+# define DEBUG_OUT(x) x
+#else
+# define DEBUG_OUT(x) Curl_nop_stmt
+#endif
+
+CURLcode Curl_input_ntlm(struct Curl_easy *data,
+ bool proxy, /* if proxy or not */
+ const char *header) /* rest of the www-authenticate:
+ header */
+{
+ /* point to the correct struct with this */
+ struct ntlmdata *ntlm;
+ curlntlm *state;
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+
+ ntlm = proxy ? &conn->proxyntlm : &conn->ntlm;
+ state = proxy ? &conn->proxy_ntlm_state : &conn->http_ntlm_state;
+
+ if(checkprefix("NTLM", header)) {
+ header += strlen("NTLM");
+
+ while(*header && ISSPACE(*header))
+ header++;
+
+ if(*header) {
+ unsigned char *hdr;
+ size_t hdrlen;
+
+ result = Curl_base64_decode(header, &hdr, &hdrlen);
+ if(!result) {
+ struct bufref hdrbuf;
+
+ Curl_bufref_init(&hdrbuf);
+ Curl_bufref_set(&hdrbuf, hdr, hdrlen, curl_free);
+ result = Curl_auth_decode_ntlm_type2_message(data, &hdrbuf, ntlm);
+ Curl_bufref_free(&hdrbuf);
+ }
+ if(result)
+ return result;
+
+ *state = NTLMSTATE_TYPE2; /* We got a type-2 message */
+ }
+ else {
+ if(*state == NTLMSTATE_LAST) {
+ infof(data, "NTLM auth restarted");
+ Curl_http_auth_cleanup_ntlm(conn);
+ }
+ else if(*state == NTLMSTATE_TYPE3) {
+ infof(data, "NTLM handshake rejected");
+ Curl_http_auth_cleanup_ntlm(conn);
+ *state = NTLMSTATE_NONE;
+ return CURLE_REMOTE_ACCESS_DENIED;
+ }
+ else if(*state >= NTLMSTATE_TYPE1) {
+ infof(data, "NTLM handshake failure (internal error)");
+ return CURLE_REMOTE_ACCESS_DENIED;
+ }
+
+ *state = NTLMSTATE_TYPE1; /* We should send away a type-1 */
+ }
+ }
+
+ return result;
+}
+
+/*
+ * This is for creating ntlm header output
+ */
+CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
+{
+ char *base64 = NULL;
+ size_t len = 0;
+ CURLcode result = CURLE_OK;
+ struct bufref ntlmmsg;
+
+ /* point to the address of the pointer that holds the string to send to the
+ server, which is for a plain host or for an HTTP proxy */
+ char **allocuserpwd;
+
+ /* point to the username, password, service and host */
+ const char *userp;
+ const char *passwdp;
+ const char *service = NULL;
+ const char *hostname = NULL;
+
+ /* point to the correct struct with this */
+ struct ntlmdata *ntlm;
+ curlntlm *state;
+ struct auth *authp;
+ struct connectdata *conn = data->conn;
+
+ DEBUGASSERT(conn);
+ DEBUGASSERT(data);
+
+ if(proxy) {
+#ifndef CURL_DISABLE_PROXY
+ allocuserpwd = &data->state.aptr.proxyuserpwd;
+ userp = data->state.aptr.proxyuser;
+ passwdp = data->state.aptr.proxypasswd;
+ service = data->set.str[STRING_PROXY_SERVICE_NAME] ?
+ data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP";
+ hostname = conn->http_proxy.host.name;
+ ntlm = &conn->proxyntlm;
+ state = &conn->proxy_ntlm_state;
+ authp = &data->state.authproxy;
+#else
+ return CURLE_NOT_BUILT_IN;
+#endif
+ }
+ else {
+ allocuserpwd = &data->state.aptr.userpwd;
+ userp = data->state.aptr.user;
+ passwdp = data->state.aptr.passwd;
+ service = data->set.str[STRING_SERVICE_NAME] ?
+ data->set.str[STRING_SERVICE_NAME] : "HTTP";
+ hostname = conn->host.name;
+ ntlm = &conn->ntlm;
+ state = &conn->http_ntlm_state;
+ authp = &data->state.authhost;
+ }
+ authp->done = FALSE;
+
+ /* not set means empty */
+ if(!userp)
+ userp = "";
+
+ if(!passwdp)
+ passwdp = "";
+
+#ifdef USE_WINDOWS_SSPI
+ if(!s_hSecDll) {
+ /* not thread safe and leaks - use curl_global_init() to avoid */
+ CURLcode err = Curl_sspi_global_init();
+ if(!s_hSecDll)
+ return err;
+ }
+#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
+ ntlm->sslContext = conn->sslContext;
+#endif
+#endif
+
+ Curl_bufref_init(&ntlmmsg);
+
+ /* connection is already authenticated, don't send a header in future
+ * requests so go directly to NTLMSTATE_LAST */
+ if(*state == NTLMSTATE_TYPE3)
+ *state = NTLMSTATE_LAST;
+
+ switch(*state) {
+ case NTLMSTATE_TYPE1:
+ default: /* for the weird cases we (re)start here */
+ /* Create a type-1 message */
+ result = Curl_auth_create_ntlm_type1_message(data, userp, passwdp,
+ service, hostname,
+ ntlm, &ntlmmsg);
+ if(!result) {
+ DEBUGASSERT(Curl_bufref_len(&ntlmmsg) != 0);
+ result = Curl_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg),
+ Curl_bufref_len(&ntlmmsg), &base64, &len);
+ if(!result) {
+ free(*allocuserpwd);
+ *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n",
+ proxy ? "Proxy-" : "",
+ base64);
+ free(base64);
+ if(!*allocuserpwd)
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ }
+ break;
+
+ case NTLMSTATE_TYPE2:
+ /* We already received the type-2 message, create a type-3 message */
+ result = Curl_auth_create_ntlm_type3_message(data, userp, passwdp,
+ ntlm, &ntlmmsg);
+ if(!result && Curl_bufref_len(&ntlmmsg)) {
+ result = Curl_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg),
+ Curl_bufref_len(&ntlmmsg), &base64, &len);
+ if(!result) {
+ free(*allocuserpwd);
+ *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n",
+ proxy ? "Proxy-" : "",
+ base64);
+ free(base64);
+ if(!*allocuserpwd)
+ result = CURLE_OUT_OF_MEMORY;
+ else {
+ *state = NTLMSTATE_TYPE3; /* we send a type-3 */
+ authp->done = TRUE;
+ }
+ }
+ }
+ break;
+
+ case NTLMSTATE_LAST:
+ Curl_safefree(*allocuserpwd);
+ authp->done = TRUE;
+ break;
+ }
+ Curl_bufref_free(&ntlmmsg);
+
+ return result;
+}
+
+void Curl_http_auth_cleanup_ntlm(struct connectdata *conn)
+{
+ Curl_auth_cleanup_ntlm(&conn->ntlm);
+ Curl_auth_cleanup_ntlm(&conn->proxyntlm);
+
+#if defined(NTLM_WB_ENABLED)
+ Curl_http_auth_cleanup_ntlm_wb(conn);
+#endif
+}
+
+#endif /* !CURL_DISABLE_HTTP && USE_NTLM */
diff --git a/libs/libcurl/src/http_ntlm.h b/libs/libcurl/src/http_ntlm.h index cec63b82c3..186cb4357b 100644 --- a/libs/libcurl/src/http_ntlm.h +++ b/libs/libcurl/src/http_ntlm.h @@ -1,44 +1,44 @@ -#ifndef HEADER_CURL_HTTP_NTLM_H -#define HEADER_CURL_HTTP_NTLM_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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(USE_NTLM) - -/* this is for ntlm header input */ -CURLcode Curl_input_ntlm(struct Curl_easy *data, bool proxy, - const char *header); - -/* this is for creating ntlm header output */ -CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy); - -void Curl_http_auth_cleanup_ntlm(struct connectdata *conn); - -#else /* !CURL_DISABLE_HTTP && USE_NTLM */ -#define Curl_http_auth_cleanup_ntlm(x) -#endif - -#endif /* HEADER_CURL_HTTP_NTLM_H */ +#ifndef HEADER_CURL_HTTP_NTLM_H
+#define HEADER_CURL_HTTP_NTLM_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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(USE_NTLM)
+
+/* this is for ntlm header input */
+CURLcode Curl_input_ntlm(struct Curl_easy *data, bool proxy,
+ const char *header);
+
+/* this is for creating ntlm header output */
+CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy);
+
+void Curl_http_auth_cleanup_ntlm(struct connectdata *conn);
+
+#else /* !CURL_DISABLE_HTTP && USE_NTLM */
+#define Curl_http_auth_cleanup_ntlm(x)
+#endif
+
+#endif /* HEADER_CURL_HTTP_NTLM_H */
diff --git a/libs/libcurl/src/http_proxy.c b/libs/libcurl/src/http_proxy.c index cc20b3a801..3a47720778 100644 --- a/libs/libcurl/src/http_proxy.c +++ b/libs/libcurl/src/http_proxy.c @@ -1,1077 +1,1283 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "http_proxy.h" - -#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) - -#include <curl/curl.h> -#ifdef USE_HYPER -#include <hyper.h> -#endif -#include "sendf.h" -#include "http.h" -#include "url.h" -#include "select.h" -#include "progress.h" -#include "connect.h" -#include "curlx.h" -#include "vtls/vtls.h" -#include "transfer.h" -#include "multiif.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* - * Perform SSL initialization for HTTPS proxy. Sets - * proxy_ssl_connected connection bit when complete. Can be - * called multiple times. - */ -static CURLcode https_proxy_connect(struct Curl_easy *data, int sockindex) -{ -#ifdef USE_SSL - struct connectdata *conn = data->conn; - CURLcode result = CURLE_OK; - DEBUGASSERT(conn->http_proxy.proxytype == CURLPROXY_HTTPS); - if(!conn->bits.proxy_ssl_connected[sockindex]) { - /* perform SSL initialization for this socket */ - result = - Curl_ssl_connect_nonblocking(data, conn, TRUE, sockindex, - &conn->bits.proxy_ssl_connected[sockindex]); - if(result) - /* a failed connection is marked for closure to prevent (bad) re-use or - similar */ - connclose(conn, "TLS handshake failed"); - } - return result; -#else - (void) data; - (void) sockindex; - return CURLE_NOT_BUILT_IN; -#endif -} - -CURLcode Curl_proxy_connect(struct Curl_easy *data, int sockindex) -{ - struct connectdata *conn = data->conn; - if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) { - const CURLcode result = https_proxy_connect(data, sockindex); - if(result) - return result; - if(!conn->bits.proxy_ssl_connected[sockindex]) - return result; /* wait for HTTPS proxy SSL initialization to complete */ - } - - if(conn->bits.tunnel_proxy && conn->bits.httpproxy) { -#ifndef CURL_DISABLE_PROXY - /* for [protocol] tunneled through HTTP proxy */ - const char *hostname; - int remote_port; - CURLcode result; - - /* We want "seamless" operations through HTTP proxy tunnel */ - - /* for the secondary socket (FTP), use the "connect to host" - * but ignore the "connect to port" (use the secondary port) - */ - - if(conn->bits.conn_to_host) - hostname = conn->conn_to_host.name; - else if(sockindex == SECONDARYSOCKET) - hostname = conn->secondaryhostname; - else - hostname = conn->host.name; - - if(sockindex == SECONDARYSOCKET) - remote_port = conn->secondary_port; - else if(conn->bits.conn_to_port) - remote_port = conn->conn_to_port; - else - remote_port = conn->remote_port; - - result = Curl_proxyCONNECT(data, sockindex, hostname, remote_port); - if(CURLE_OK != result) - return result; - Curl_safefree(data->state.aptr.proxyuserpwd); -#else - return CURLE_NOT_BUILT_IN; -#endif - } - /* no HTTP tunnel proxy, just return */ - return CURLE_OK; -} - -bool Curl_connect_complete(struct connectdata *conn) -{ - return !conn->connect_state || - (conn->connect_state->tunnel_state >= TUNNEL_COMPLETE); -} - -bool Curl_connect_ongoing(struct connectdata *conn) -{ - return conn->connect_state && - (conn->connect_state->tunnel_state <= TUNNEL_COMPLETE); -} - -/* when we've 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're still sending the request, wait for write. */ -int Curl_connect_getsock(struct connectdata *conn) -{ - struct HTTP *http; - DEBUGASSERT(conn); - DEBUGASSERT(conn->connect_state); - http = &conn->connect_state->http_proxy; - - if(http->sending == HTTPSEND_REQUEST) - return GETSOCK_WRITESOCK(0); - - return GETSOCK_READSOCK(0); -} - -static CURLcode connect_init(struct Curl_easy *data, bool reinit) -{ - struct http_connect_state *s; - struct connectdata *conn = data->conn; - if(conn->handler->flags & PROTOPT_NOTCPPROXY) { - failf(data, "%s cannot be done over CONNECT", conn->handler->scheme); - return CURLE_UNSUPPORTED_PROTOCOL; - } - if(!reinit) { - CURLcode result; - DEBUGASSERT(!conn->connect_state); - /* we might need the upload buffer for streaming a partial request */ - result = Curl_get_upload_buffer(data); - if(result) - return result; - - s = calloc(1, sizeof(struct http_connect_state)); - if(!s) - return CURLE_OUT_OF_MEMORY; - infof(data, "allocate connect buffer"); - conn->connect_state = s; - Curl_dyn_init(&s->rcvbuf, DYN_PROXY_CONNECT_HEADERS); - - /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the - * member conn->proto.http; we want [protocol] through HTTP and we have - * to change the member temporarily for connecting to the HTTP - * proxy. After Curl_proxyCONNECT we have to set back the member to the - * original pointer - * - * This function might be called several times in the multi interface case - * if the proxy's CONNECT response is not instant. - */ - s->prot_save = data->req.p.http; - data->req.p.http = &s->http_proxy; - connkeep(conn, "HTTP proxy CONNECT"); - } - else { - DEBUGASSERT(conn->connect_state); - s = conn->connect_state; - Curl_dyn_reset(&s->rcvbuf); - } - s->tunnel_state = TUNNEL_INIT; - s->keepon = KEEPON_CONNECT; - s->cl = 0; - s->close_connection = FALSE; - return CURLE_OK; -} - -void Curl_connect_done(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - struct http_connect_state *s = conn->connect_state; - if(s && (s->tunnel_state != TUNNEL_EXIT)) { - s->tunnel_state = TUNNEL_EXIT; - Curl_dyn_free(&s->rcvbuf); - Curl_dyn_free(&s->req); - - /* restore the protocol pointer */ - data->req.p.http = s->prot_save; - data->info.httpcode = 0; /* clear it as it might've been used for the - proxy */ - data->req.ignorebody = FALSE; -#ifdef USE_HYPER - data->state.hconnect = FALSE; -#endif - infof(data, "CONNECT phase completed"); - } -} - -static CURLcode CONNECT_host(struct Curl_easy *data, - struct connectdata *conn, - const char *hostname, - int remote_port, - char **connecthostp, - char **hostp) -{ - char *hostheader; /* for CONNECT */ - char *host = NULL; /* Host: */ - bool ipv6_ip = conn->bits.ipv6_ip; - - /* the hostname may be different */ - if(hostname != conn->host.name) - ipv6_ip = (strchr(hostname, ':') != NULL); - hostheader = /* host:port with IPv6 support */ - aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"", - remote_port); - if(!hostheader) - return CURLE_OUT_OF_MEMORY; - - if(!Curl_checkProxyheaders(data, conn, STRCONST("Host"))) { - host = aprintf("Host: %s\r\n", hostheader); - if(!host) { - free(hostheader); - return CURLE_OUT_OF_MEMORY; - } - } - *connecthostp = hostheader; - *hostp = host; - return CURLE_OK; -} - -#ifndef USE_HYPER -static CURLcode CONNECT(struct Curl_easy *data, - int sockindex, - const char *hostname, - int remote_port) -{ - int subversion = 0; - struct SingleRequest *k = &data->req; - CURLcode result; - struct connectdata *conn = data->conn; - curl_socket_t tunnelsocket = conn->sock[sockindex]; - struct http_connect_state *s = conn->connect_state; - struct HTTP *http = data->req.p.http; - char *linep; - size_t perline; - -#define SELECT_OK 0 -#define SELECT_ERROR 1 - - if(Curl_connect_complete(conn)) - return CURLE_OK; /* CONNECT is already completed */ - - conn->bits.proxy_connect_closed = FALSE; - - do { - timediff_t check; - if(TUNNEL_INIT == s->tunnel_state) { - /* BEGIN CONNECT PHASE */ - struct dynbuf *req = &s->req; - char *hostheader = NULL; - char *host = NULL; - - infof(data, "Establish HTTP proxy tunnel to %s:%d", - hostname, remote_port); - - /* This only happens if we've looped here due to authentication - reasons, and we don't really use the newly cloned URL here - then. Just free() it. */ - Curl_safefree(data->req.newurl); - - /* initialize send-buffer */ - Curl_dyn_init(req, DYN_HTTP_REQUEST); - - result = CONNECT_host(data, conn, - hostname, remote_port, &hostheader, &host); - if(result) - return result; - - /* Setup the proxy-authorization header, if any */ - result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, - hostheader, TRUE); - - if(!result) { - const char *httpv = - (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? "1.0" : "1.1"; - - result = - Curl_dyn_addf(req, - "CONNECT %s HTTP/%s\r\n" - "%s" /* Host: */ - "%s", /* Proxy-Authorization */ - hostheader, - httpv, - host?host:"", - data->state.aptr.proxyuserpwd? - data->state.aptr.proxyuserpwd:""); - - if(!result && !Curl_checkProxyheaders(data, - conn, STRCONST("User-Agent")) && - data->set.str[STRING_USERAGENT]) - result = Curl_dyn_addf(req, "User-Agent: %s\r\n", - data->set.str[STRING_USERAGENT]); - - if(!result && !Curl_checkProxyheaders(data, conn, - STRCONST("Proxy-Connection"))) - result = Curl_dyn_addn(req, - STRCONST("Proxy-Connection: Keep-Alive\r\n")); - - if(!result) - result = Curl_add_custom_headers(data, TRUE, req); - - if(!result) - /* CRLF terminate the request */ - result = Curl_dyn_addn(req, STRCONST("\r\n")); - - if(!result) { - /* Send the connect request to the proxy */ - result = Curl_buffer_send(req, data, &data->info.request_size, 0, - sockindex); - s->headerlines = 0; - } - if(result) - failf(data, "Failed sending CONNECT to proxy"); - } - free(host); - free(hostheader); - if(result) - return result; - - s->tunnel_state = TUNNEL_CONNECT; - } /* END CONNECT PHASE */ - - check = Curl_timeleft(data, NULL, TRUE); - if(check <= 0) { - failf(data, "Proxy CONNECT aborted due to timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - if(!Curl_conn_data_pending(conn, sockindex) && !http->sending) - /* return so we'll be called again polling-style */ - return CURLE_OK; - - /* at this point, the tunnel_connecting phase is over. */ - - if(http->sending == HTTPSEND_REQUEST) { - if(!s->nsend) { - size_t fillcount; - k->upload_fromhere = data->state.ulbuf; - result = Curl_fillreadbuffer(data, data->set.upload_buffer_size, - &fillcount); - if(result) - return result; - s->nsend = fillcount; - } - if(s->nsend) { - ssize_t bytes_written; - /* write to socket (send away data) */ - result = Curl_write(data, - conn->writesockfd, /* socket to send to */ - k->upload_fromhere, /* buffer pointer */ - s->nsend, /* buffer size */ - &bytes_written); /* actually sent */ - - if(!result) - /* send to debug callback! */ - Curl_debug(data, CURLINFO_HEADER_OUT, - k->upload_fromhere, bytes_written); - - s->nsend -= bytes_written; - k->upload_fromhere += bytes_written; - return result; - } - http->sending = HTTPSEND_NADA; - /* if nothing left to send, continue */ - } - { /* READING RESPONSE PHASE */ - int error = SELECT_OK; - - while(s->keepon) { - ssize_t gotbytes; - 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_read(data, tunnelsocket, &byte, 1, &gotbytes); - if(result == CURLE_AGAIN) - /* socket buffer drained, return */ - return CURLE_OK; - - if(Curl_pgrsUpdate(data)) - return CURLE_ABORTED_BY_CALLBACK; - - if(result) { - s->keepon = KEEPON_DONE; - break; - } - else if(gotbytes <= 0) { - 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 */ - conn->bits.proxy_connect_closed = TRUE; - infof(data, "Proxy CONNECT connection closed"); - } - else { - error = SELECT_ERROR; - failf(data, "Proxy CONNECT aborted"); - } - s->keepon = KEEPON_DONE; - break; - } - - if(s->keepon == KEEPON_IGNORE) { - /* This means we are currently ignoring a response-body */ - - if(s->cl) { - /* A Content-Length based body: simply count down the counter - and make sure to break out of the loop when we're done! */ - s->cl--; - if(s->cl <= 0) { - s->keepon = KEEPON_DONE; - s->tunnel_state = TUNNEL_COMPLETE; - break; - } - } - else { - /* chunked-encoded body, so we need to do the chunked dance - properly to know when the end of the body is reached */ - CHUNKcode r; - CURLcode extra; - ssize_t tookcareof = 0; - - /* now parse the chunked piece of data so that we can - properly tell when the stream ends */ - r = Curl_httpchunk_read(data, &byte, 1, &tookcareof, &extra); - if(r == CHUNKE_STOP) { - /* we're done reading chunks! */ - infof(data, "chunk reading DONE"); - s->keepon = KEEPON_DONE; - /* we did the full CONNECT treatment, go COMPLETE */ - s->tunnel_state = TUNNEL_COMPLETE; - } - } - continue; - } - - if(Curl_dyn_addn(&s->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; - - s->headerlines++; - linep = Curl_dyn_ptr(&s->rcvbuf); - perline = Curl_dyn_len(&s->rcvbuf); /* amount of bytes in this line */ - - /* output debug if that is requested */ - Curl_debug(data, CURLINFO_HEADER_IN, linep, perline); - - if(!data->set.suppress_connect_headers) { - /* send the header to the callback */ - int writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT | - (data->set.include_header ? CLIENTWRITE_BODY : 0) | - (s->headerlines == 1 ? CLIENTWRITE_STATUS : 0); - - result = Curl_client_write(data, writetype, linep, perline); - if(result) - return result; - } - - data->info.header_size += (long)perline; - - /* Newlines are CRLF, so the CR is ignored as the line isn't - 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 */ - s->keepon = KEEPON_IGNORE; - - if(s->cl) { - infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T - " bytes of response-body", s->cl); - } - else if(s->chunked_encoding) { - CHUNKcode r; - CURLcode extra; - - infof(data, "Ignore chunked response-body"); - - /* We set ignorebody true here since the chunked decoder - function will acknowledge that. Pay attention so that this is - cleared again when this function returns! */ - k->ignorebody = TRUE; - - if(linep[1] == '\n') - /* this can only be a LF if the letter at index 0 was a CR */ - linep++; - - /* now parse the chunked piece of data so that we can properly - tell when the stream ends */ - r = Curl_httpchunk_read(data, linep + 1, 1, &gotbytes, - &extra); - if(r == CHUNKE_STOP) { - /* we're done reading chunks! */ - infof(data, "chunk reading DONE"); - s->keepon = KEEPON_DONE; - /* we did the full CONNECT treatment, go to COMPLETE */ - s->tunnel_state = TUNNEL_COMPLETE; - } - } - else { - /* without content-length or chunked encoding, we - can't keep the connection alive since the close is - the end signal so we bail out at once instead */ - s->keepon = KEEPON_DONE; - } - } - else - s->keepon = KEEPON_DONE; - - if(s->keepon == KEEPON_DONE && !s->cl) - /* we did the full CONNECT treatment, go to COMPLETE */ - s->tunnel_state = TUNNEL_COMPLETE; - - DEBUGASSERT(s->keepon == KEEPON_IGNORE || s->keepon == KEEPON_DONE); - continue; - } - - if((checkprefix("WWW-Authenticate:", linep) && - (401 == k->httpcode)) || - (checkprefix("Proxy-authenticate:", linep) && - (407 == k->httpcode))) { - - bool proxy = (k->httpcode == 407) ? TRUE : FALSE; - char *auth = Curl_copy_header_value(linep); - if(!auth) - return CURLE_OUT_OF_MEMORY; - - result = Curl_http_input_auth(data, proxy, auth); - - free(auth); - - if(result) - return result; - } - else if(checkprefix("Content-Length:", linep)) { - 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 { - (void)curlx_strtoofft(linep + - strlen("Content-Length:"), NULL, 10, &s->cl); - } - } - else if(Curl_compareheader(linep, - STRCONST("Connection:"), STRCONST("close"))) - s->close_connection = TRUE; - else if(checkprefix("Transfer-Encoding:", linep)) { - 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(linep, - STRCONST("Transfer-Encoding:"), - STRCONST("chunked"))) { - infof(data, "CONNECT responded chunked"); - s->chunked_encoding = TRUE; - /* init our chunky engine */ - Curl_httpchunk_init(data); - } - } - else if(Curl_compareheader(linep, - STRCONST("Proxy-Connection:"), - STRCONST("close"))) - s->close_connection = TRUE; - else if(2 == sscanf(linep, "HTTP/1.%d %d", - &subversion, - &k->httpcode)) { - /* store the HTTP code from the proxy */ - data->info.httpproxycode = k->httpcode; - } - - Curl_dyn_reset(&s->rcvbuf); - } /* while there's buffer left and loop is requested */ - - if(Curl_pgrsUpdate(data)) - return CURLE_ABORTED_BY_CALLBACK; - - if(error) - return CURLE_RECV_ERROR; - - if(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); - if(result) - return result; - - if(conn->bits.close) - /* the connection has been marked for closure, most likely in the - Curl_http_auth_act() function and thus we can kill it at once - below */ - s->close_connection = TRUE; - } - - if(s->close_connection && data->req.newurl) { - /* Connection closed by server. Don't use it anymore */ - Curl_closesocket(data, conn, conn->sock[sockindex]); - conn->sock[sockindex] = CURL_SOCKET_BAD; - break; - } - } /* END READING RESPONSE PHASE */ - - /* If we are supposed to continue and request a new URL, which basically - * means the HTTP authentication is still going on so if the tunnel - * is complete we start over in INIT state */ - if(data->req.newurl && (TUNNEL_COMPLETE == s->tunnel_state)) { - connect_init(data, TRUE); /* reinit */ - } - - } while(data->req.newurl); - - if(data->info.httpproxycode/100 != 2) { - if(s->close_connection && data->req.newurl) { - conn->bits.proxy_connect_closed = TRUE; - infof(data, "Connect me again please"); - Curl_connect_done(data); - } - else { - free(data->req.newurl); - data->req.newurl = NULL; - /* failure, close this connection to avoid re-use */ - streamclose(conn, "proxy CONNECT failure"); - } - - /* to back to init state */ - s->tunnel_state = TUNNEL_INIT; - - if(conn->bits.proxy_connect_closed) - /* this is not an error, just part of the connection negotiation */ - return CURLE_OK; - Curl_dyn_free(&s->rcvbuf); - failf(data, "Received HTTP code %d from proxy after CONNECT", - data->req.httpcode); - return CURLE_RECV_ERROR; - } - - s->tunnel_state = TUNNEL_COMPLETE; - - /* If a proxy-authorization header was used for the proxy, then we should - make sure that it isn't accidentally used for the document request - after we've connected. So let's free and clear it here. */ - Curl_safefree(data->state.aptr.proxyuserpwd); - data->state.aptr.proxyuserpwd = NULL; - - data->state.authproxy.done = TRUE; - data->state.authproxy.multipass = FALSE; - - infof(data, "Proxy replied %d to CONNECT request", - data->info.httpproxycode); - data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */ - conn->bits.rewindaftersend = FALSE; /* make sure this isn't set for the - document request */ - Curl_dyn_free(&s->rcvbuf); - return CURLE_OK; -} -#else -/* The Hyper version of CONNECT */ -static CURLcode CONNECT(struct Curl_easy *data, - int sockindex, - const char *hostname, - int remote_port) -{ - struct connectdata *conn = data->conn; - struct hyptransfer *h = &data->hyp; - curl_socket_t tunnelsocket = conn->sock[sockindex]; - struct http_connect_state *s = conn->connect_state; - CURLcode result = CURLE_OUT_OF_MEMORY; - hyper_io *io = NULL; - hyper_request *req = NULL; - hyper_headers *headers = NULL; - hyper_clientconn_options *options = NULL; - hyper_task *handshake = NULL; - hyper_task *task = NULL; /* for the handshake */ - hyper_task *sendtask = NULL; /* for the send */ - hyper_clientconn *client = NULL; - hyper_error *hypererr = NULL; - char *hostheader = NULL; /* for CONNECT */ - char *host = NULL; /* Host: */ - - if(Curl_connect_complete(conn)) - return CURLE_OK; /* CONNECT is already completed */ - - conn->bits.proxy_connect_closed = FALSE; - - do { - switch(s->tunnel_state) { - case TUNNEL_INIT: - /* BEGIN CONNECT PHASE */ - io = hyper_io_new(); - if(!io) { - failf(data, "Couldn't create hyper IO"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - /* tell Hyper how to read/write network data */ - hyper_io_set_userdata(io, data); - hyper_io_set_read(io, Curl_hyper_recv); - hyper_io_set_write(io, Curl_hyper_send); - conn->sockfd = tunnelsocket; - - data->state.hconnect = TRUE; - - /* create an executor to poll futures */ - if(!h->exec) { - h->exec = hyper_executor_new(); - if(!h->exec) { - failf(data, "Couldn't create hyper executor"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - } - - options = hyper_clientconn_options_new(); - hyper_clientconn_options_set_preserve_header_case(options, 1); - hyper_clientconn_options_set_preserve_header_order(options, 1); - - if(!options) { - failf(data, "Couldn't create hyper client options"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - hyper_clientconn_options_exec(options, h->exec); - - /* "Both the `io` and the `options` are consumed in this function - call" */ - handshake = hyper_clientconn_handshake(io, options); - if(!handshake) { - failf(data, "Couldn't create hyper client handshake"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - io = NULL; - options = NULL; - - if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) { - failf(data, "Couldn't hyper_executor_push the handshake"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - handshake = NULL; /* ownership passed on */ - - task = hyper_executor_poll(h->exec); - if(!task) { - failf(data, "Couldn't hyper_executor_poll the handshake"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - client = hyper_task_value(task); - hyper_task_free(task); - req = hyper_request_new(); - if(!req) { - failf(data, "Couldn't hyper_request_new"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - if(hyper_request_set_method(req, (uint8_t *)"CONNECT", - strlen("CONNECT"))) { - failf(data, "error setting method"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - infof(data, "Establish HTTP proxy tunnel to %s:%d", - hostname, remote_port); - - /* This only happens if we've looped here due to authentication - reasons, and we don't really use the newly cloned URL here - then. Just free() it. */ - Curl_safefree(data->req.newurl); - - result = CONNECT_host(data, conn, hostname, remote_port, - &hostheader, &host); - if(result) - goto error; - - if(hyper_request_set_uri(req, (uint8_t *)hostheader, - strlen(hostheader))) { - failf(data, "error setting path"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - if(data->set.verbose) { - char *se = aprintf("CONNECT %s HTTP/1.1\r\n", hostheader); - if(!se) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - Curl_debug(data, CURLINFO_HEADER_OUT, se, strlen(se)); - free(se); - } - /* Setup the proxy-authorization header, if any */ - result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, - hostheader, TRUE); - if(result) - goto error; - Curl_safefree(hostheader); - - /* default is 1.1 */ - if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) && - (HYPERE_OK != hyper_request_set_version(req, - HYPER_HTTP_VERSION_1_0))) { - failf(data, "error setting HTTP version"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - headers = hyper_request_headers(req); - if(!headers) { - failf(data, "hyper_request_headers"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - if(host) { - result = Curl_hyper_header(data, headers, host); - if(result) - goto error; - Curl_safefree(host); - } - - if(data->state.aptr.proxyuserpwd) { - result = Curl_hyper_header(data, headers, - data->state.aptr.proxyuserpwd); - if(result) - goto error; - } - - if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) && - data->set.str[STRING_USERAGENT]) { - struct dynbuf ua; - Curl_dyn_init(&ua, DYN_HTTP_REQUEST); - result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n", - data->set.str[STRING_USERAGENT]); - if(result) - goto error; - result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua)); - if(result) - goto error; - Curl_dyn_free(&ua); - } - - if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) { - result = Curl_hyper_header(data, headers, - "Proxy-Connection: Keep-Alive"); - if(result) - goto error; - } - - result = Curl_add_custom_headers(data, TRUE, headers); - if(result) - goto error; - - sendtask = hyper_clientconn_send(client, req); - if(!sendtask) { - failf(data, "hyper_clientconn_send"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) { - failf(data, "Couldn't hyper_executor_push the send"); - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - hyper_clientconn_free(client); - - do { - task = hyper_executor_poll(h->exec); - if(task) { - bool error = hyper_task_type(task) == HYPER_TASK_ERROR; - if(error) - hypererr = hyper_task_value(task); - hyper_task_free(task); - if(error) { - /* this could probably use a better error code? */ - result = CURLE_OUT_OF_MEMORY; - goto error; - } - } - } while(task); - s->tunnel_state = TUNNEL_CONNECT; - /* FALLTHROUGH */ - case TUNNEL_CONNECT: { - int didwhat; - bool done = FALSE; - result = Curl_hyper_stream(data, conn, &didwhat, &done, - CURL_CSELECT_IN | CURL_CSELECT_OUT); - if(result) - goto error; - if(!done) - break; - s->tunnel_state = TUNNEL_COMPLETE; - if(h->exec) { - hyper_executor_free(h->exec); - h->exec = NULL; - } - if(h->read_waker) { - hyper_waker_free(h->read_waker); - h->read_waker = NULL; - } - if(h->write_waker) { - hyper_waker_free(h->write_waker); - h->write_waker = NULL; - } - } - break; - - default: - break; - } - - if(conn->bits.close && data->req.newurl) { - /* Connection closed by server. Don't use it anymore */ - Curl_closesocket(data, conn, conn->sock[sockindex]); - conn->sock[sockindex] = CURL_SOCKET_BAD; - break; - } - - /* If we are supposed to continue and request a new URL, which basically - * means the HTTP authentication is still going on so if the tunnel - * is complete we start over in INIT state */ - if(data->req.newurl && (TUNNEL_COMPLETE == s->tunnel_state)) { - infof(data, "CONNECT request done, loop to make another"); - connect_init(data, TRUE); /* reinit */ - } - } while(data->req.newurl); - - result = CURLE_OK; - if(s->tunnel_state == TUNNEL_COMPLETE) { - if(data->info.httpproxycode/100 != 2) { - if(conn->bits.close && data->req.newurl) { - conn->bits.proxy_connect_closed = TRUE; - infof(data, "Connect me again please"); - Curl_connect_done(data); - } - else { - free(data->req.newurl); - data->req.newurl = NULL; - /* failure, close this connection to avoid re-use */ - streamclose(conn, "proxy CONNECT failure"); - Curl_closesocket(data, conn, conn->sock[sockindex]); - conn->sock[sockindex] = CURL_SOCKET_BAD; - } - - /* to back to init state */ - s->tunnel_state = TUNNEL_INIT; - - if(!conn->bits.proxy_connect_closed) { - failf(data, "Received HTTP code %d from proxy after CONNECT", - data->req.httpcode); - result = CURLE_RECV_ERROR; - } - } - } - error: - free(host); - free(hostheader); - if(io) - hyper_io_free(io); - - if(options) - hyper_clientconn_options_free(options); - - if(handshake) - hyper_task_free(handshake); - - if(hypererr) { - uint8_t errbuf[256]; - size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf)); - failf(data, "Hyper: %.*s", (int)errlen, errbuf); - hyper_error_free(hypererr); - } - return result; -} -#endif - -void Curl_connect_free(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - struct http_connect_state *s = conn->connect_state; - if(s) { - free(s); - conn->connect_state = NULL; - } -} - -/* - * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This - * function will issue the necessary commands to get a seamless tunnel through - * this proxy. After that, the socket can be used just as a normal socket. - */ - -CURLcode Curl_proxyCONNECT(struct Curl_easy *data, - int sockindex, - const char *hostname, - int remote_port) -{ - CURLcode result; - struct connectdata *conn = data->conn; - if(!conn->connect_state) { - result = connect_init(data, FALSE); - if(result) - return result; - } - result = CONNECT(data, sockindex, hostname, remote_port); - - if(result || Curl_connect_complete(conn)) - Curl_connect_done(data); - - return result; -} - -#else -void Curl_connect_free(struct Curl_easy *data) -{ - (void)data; -} - -#endif /* CURL_DISABLE_PROXY */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "http_proxy.h"
+
+#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
+
+#include <curl/curl.h>
+#ifdef USE_HYPER
+#include <hyper.h>
+#endif
+#include "sendf.h"
+#include "http.h"
+#include "url.h"
+#include "select.h"
+#include "progress.h"
+#include "cfilters.h"
+#include "connect.h"
+#include "curlx.h"
+#include "vtls/vtls.h"
+#include "transfer.h"
+#include "multiif.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+typedef enum {
+ TUNNEL_INIT, /* init/default/no tunnel state */
+ TUNNEL_CONNECT, /* CONNECT request is being send */
+ TUNNEL_RECEIVE, /* CONNECT answer is being received */
+ TUNNEL_RESPONSE, /* CONNECT response received completely */
+ TUNNEL_ESTABLISHED,
+ TUNNEL_FAILED
+} tunnel_state;
+
+/* struct for HTTP CONNECT tunneling */
+struct tunnel_state {
+ int sockindex;
+ const char *hostname;
+ int remote_port;
+ struct HTTP http_proxy;
+ struct HTTP *prot_save;
+ struct dynbuf rcvbuf;
+ struct dynbuf req;
+ size_t nsend;
+ size_t headerlines;
+ enum keeponval {
+ KEEPON_DONE,
+ KEEPON_CONNECT,
+ KEEPON_IGNORE
+ } keepon;
+ curl_off_t cl; /* size of content to read and ignore */
+ tunnel_state tunnel_state;
+ BIT(chunked_encoding);
+ BIT(close_connection);
+};
+
+
+static bool tunnel_is_established(struct tunnel_state *ts)
+{
+ return ts && (ts->tunnel_state == TUNNEL_ESTABLISHED);
+}
+
+static bool tunnel_is_failed(struct tunnel_state *ts)
+{
+ return ts && (ts->tunnel_state == TUNNEL_FAILED);
+}
+
+static CURLcode tunnel_reinit(struct tunnel_state *ts,
+ struct connectdata *conn,
+ struct Curl_easy *data)
+{
+ (void)data;
+ DEBUGASSERT(ts);
+ Curl_dyn_reset(&ts->rcvbuf);
+ Curl_dyn_reset(&ts->req);
+ ts->tunnel_state = TUNNEL_INIT;
+ ts->keepon = KEEPON_CONNECT;
+ ts->cl = 0;
+ ts->close_connection = FALSE;
+
+ if(conn->bits.conn_to_host)
+ ts->hostname = conn->conn_to_host.name;
+ else if(ts->sockindex == SECONDARYSOCKET)
+ ts->hostname = conn->secondaryhostname;
+ else
+ ts->hostname = conn->host.name;
+
+ if(ts->sockindex == SECONDARYSOCKET)
+ ts->remote_port = conn->secondary_port;
+ else if(conn->bits.conn_to_port)
+ ts->remote_port = conn->conn_to_port;
+ else
+ ts->remote_port = conn->remote_port;
+
+ return CURLE_OK;
+}
+
+static CURLcode tunnel_init(struct tunnel_state **pts,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ struct tunnel_state *ts;
+ CURLcode result;
+
+ if(conn->handler->flags & PROTOPT_NOTCPPROXY) {
+ failf(data, "%s cannot be done over CONNECT", conn->handler->scheme);
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
+
+ /* we might need the upload buffer for streaming a partial request */
+ result = Curl_get_upload_buffer(data);
+ if(result)
+ return result;
+
+ ts = calloc(1, sizeof(*ts));
+ if(!ts)
+ return CURLE_OUT_OF_MEMORY;
+
+ ts->sockindex = sockindex;
+ infof(data, "allocate connect buffer");
+
+ Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
+ Curl_dyn_init(&ts->req, DYN_HTTP_REQUEST);
+
+ /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the
+ * member conn->proto.http; we want [protocol] through HTTP and we have
+ * to change the member temporarily for connecting to the HTTP
+ * proxy. After Curl_proxyCONNECT we have to set back the member to the
+ * original pointer
+ *
+ * This function might be called several times in the multi interface case
+ * if the proxy's CONNECT response is not instant.
+ */
+ ts->prot_save = data->req.p.http;
+ data->req.p.http = &ts->http_proxy;
+ *pts = ts;
+ connkeep(conn, "HTTP proxy CONNECT");
+ return tunnel_reinit(ts, conn, data);
+}
+
+static void tunnel_go_state(struct Curl_cfilter *cf,
+ struct tunnel_state *ts,
+ tunnel_state new_state,
+ struct Curl_easy *data)
+{
+ if(ts->tunnel_state == new_state)
+ return;
+ /* leaving this one */
+ switch(ts->tunnel_state) {
+ case TUNNEL_CONNECT:
+ data->req.ignorebody = FALSE;
+ break;
+ default:
+ break;
+ }
+ /* entering this one */
+ switch(new_state) {
+ case TUNNEL_INIT:
+ tunnel_reinit(ts, cf->conn, data);
+ break;
+
+ case TUNNEL_CONNECT:
+ ts->tunnel_state = TUNNEL_CONNECT;
+ ts->keepon = KEEPON_CONNECT;
+ Curl_dyn_reset(&ts->rcvbuf);
+ break;
+
+ case TUNNEL_RECEIVE:
+ ts->tunnel_state = TUNNEL_RECEIVE;
+ break;
+
+ case TUNNEL_RESPONSE:
+ ts->tunnel_state = TUNNEL_RESPONSE;
+ break;
+
+ case TUNNEL_ESTABLISHED:
+ infof(data, "CONNECT phase completed");
+ data->state.authproxy.done = TRUE;
+ data->state.authproxy.multipass = FALSE;
+ /* FALLTHROUGH */
+ case TUNNEL_FAILED:
+ ts->tunnel_state = new_state;
+ Curl_dyn_reset(&ts->rcvbuf);
+ Curl_dyn_reset(&ts->req);
+ /* restore the protocol pointer */
+ data->req.p.http = ts->prot_save;
+ 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 isn't accidentally used for the document request
+ after we've connected. So let's free and clear it here. */
+ Curl_safefree(data->state.aptr.proxyuserpwd);
+ data->state.aptr.proxyuserpwd = NULL;
+#ifdef USE_HYPER
+ data->state.hconnect = FALSE;
+#endif
+ break;
+ }
+}
+
+static void tunnel_free(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct tunnel_state *ts = cf->ctx;
+ if(ts) {
+ tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
+ Curl_dyn_free(&ts->rcvbuf);
+ Curl_dyn_free(&ts->req);
+ free(ts);
+ cf->ctx = NULL;
+ }
+}
+
+static CURLcode CONNECT_host(struct Curl_easy *data,
+ struct connectdata *conn,
+ const char *hostname,
+ int remote_port,
+ char **connecthostp,
+ char **hostp)
+{
+ char *hostheader; /* for CONNECT */
+ char *host = NULL; /* Host: */
+ bool ipv6_ip = conn->bits.ipv6_ip;
+
+ /* the hostname may be different */
+ if(hostname != conn->host.name)
+ ipv6_ip = (strchr(hostname, ':') != NULL);
+ hostheader = /* host:port with IPv6 support */
+ aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"",
+ remote_port);
+ if(!hostheader)
+ return CURLE_OUT_OF_MEMORY;
+
+ if(!Curl_checkProxyheaders(data, conn, STRCONST("Host"))) {
+ host = aprintf("Host: %s\r\n", hostheader);
+ if(!host) {
+ free(hostheader);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ *connecthostp = hostheader;
+ *hostp = host;
+ return CURLE_OK;
+}
+
+#ifndef USE_HYPER
+static CURLcode start_CONNECT(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct tunnel_state *ts)
+{
+ char *hostheader = NULL;
+ char *host = NULL;
+ const char *httpv;
+ CURLcode result;
+
+ infof(data, "Establish HTTP proxy tunnel to %s:%d",
+ ts->hostname, ts->remote_port);
+
+ /* This only happens if we've looped here due to authentication
+ reasons, and we don't really use the newly cloned URL here
+ then. Just free() it. */
+ Curl_safefree(data->req.newurl);
+
+ result = CONNECT_host(data, conn,
+ ts->hostname, ts->remote_port,
+ &hostheader, &host);
+ if(result)
+ goto out;
+
+ /* Setup the proxy-authorization header, if any */
+ result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET,
+ hostheader, TRUE);
+ if(result)
+ goto out;
+
+ httpv = (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? "1.0" : "1.1";
+
+ result =
+ Curl_dyn_addf(&ts->req,
+ "CONNECT %s HTTP/%s\r\n"
+ "%s" /* Host: */
+ "%s", /* Proxy-Authorization */
+ hostheader,
+ httpv,
+ host?host:"",
+ data->state.aptr.proxyuserpwd?
+ data->state.aptr.proxyuserpwd:"");
+ if(result)
+ goto out;
+
+ if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent"))
+ && data->set.str[STRING_USERAGENT])
+ result = Curl_dyn_addf(&ts->req, "User-Agent: %s\r\n",
+ data->set.str[STRING_USERAGENT]);
+ if(result)
+ goto out;
+
+ if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection")))
+ result = Curl_dyn_addn(&ts->req,
+ STRCONST("Proxy-Connection: Keep-Alive\r\n"));
+ if(result)
+ goto out;
+
+ result = Curl_add_custom_headers(data, TRUE, &ts->req);
+ if(result)
+ goto out;
+
+ /* CRLF terminate the request */
+ result = Curl_dyn_addn(&ts->req, STRCONST("\r\n"));
+ if(result)
+ goto out;
+
+ /* Send the connect request to the proxy */
+ result = Curl_buffer_send(&ts->req, data, &data->info.request_size, 0,
+ ts->sockindex);
+ ts->headerlines = 0;
+
+out:
+ if(result)
+ failf(data, "Failed sending CONNECT to proxy");
+ free(host);
+ free(hostheader);
+ return result;
+}
+
+static CURLcode send_CONNECT(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct tunnel_state *ts,
+ bool *done)
+{
+ struct SingleRequest *k = &data->req;
+ struct HTTP *http = data->req.p.http;
+ CURLcode result = CURLE_OK;
+
+ if(http->sending != HTTPSEND_REQUEST)
+ goto out;
+
+ if(!ts->nsend) {
+ size_t fillcount;
+ k->upload_fromhere = data->state.ulbuf;
+ result = Curl_fillreadbuffer(data, data->set.upload_buffer_size,
+ &fillcount);
+ if(result)
+ goto out;
+ ts->nsend = fillcount;
+ }
+ if(ts->nsend) {
+ ssize_t bytes_written;
+ /* write to socket (send away data) */
+ result = Curl_write(data,
+ conn->writesockfd, /* socket to send to */
+ k->upload_fromhere, /* buffer pointer */
+ ts->nsend, /* buffer size */
+ &bytes_written); /* actually sent */
+ if(result)
+ goto out;
+ /* send to debug callback! */
+ Curl_debug(data, CURLINFO_HEADER_OUT,
+ k->upload_fromhere, bytes_written);
+
+ ts->nsend -= bytes_written;
+ k->upload_fromhere += bytes_written;
+ }
+ if(!ts->nsend)
+ http->sending = HTTPSEND_NADA;
+
+out:
+ if(result)
+ failf(data, "Failed sending CONNECT to proxy");
+ *done = (http->sending != HTTPSEND_REQUEST);
+ return result;
+}
+
+static CURLcode on_resp_header(struct Curl_easy *data,
+ struct tunnel_state *ts,
+ const char *header)
+{
+ CURLcode result = CURLE_OK;
+ struct SingleRequest *k = &data->req;
+ int subversion = 0;
+
+ if((checkprefix("WWW-Authenticate:", header) &&
+ (401 == k->httpcode)) ||
+ (checkprefix("Proxy-authenticate:", header) &&
+ (407 == k->httpcode))) {
+
+ bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
+ char *auth = Curl_copy_header_value(header);
+ if(!auth)
+ return CURLE_OUT_OF_MEMORY;
+
+ DEBUGF(infof(data, "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 {
+ (void)curlx_strtoofft(header + strlen("Content-Length:"),
+ NULL, 10, &ts->cl);
+ }
+ }
+ 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;
+ /* init our chunky engine */
+ Curl_httpchunk_init(data);
+ }
+ }
+ else if(Curl_compareheader(header,
+ STRCONST("Proxy-Connection:"),
+ STRCONST("close")))
+ ts->close_connection = TRUE;
+ else if(2 == sscanf(header, "HTTP/1.%d %d",
+ &subversion,
+ &k->httpcode)) {
+ /* store the HTTP code from the proxy */
+ data->info.httpproxycode = k->httpcode;
+ }
+ return result;
+}
+
+static CURLcode recv_CONNECT_resp(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct tunnel_state *ts,
+ bool *done)
+{
+ CURLcode result = CURLE_OK;
+ struct SingleRequest *k = &data->req;
+ curl_socket_t tunnelsocket = conn->sock[ts->sockindex];
+ char *linep;
+ size_t perline;
+ int error;
+
+#define SELECT_OK 0
+#define SELECT_ERROR 1
+
+ error = SELECT_OK;
+ *done = FALSE;
+
+ if(!Curl_conn_data_pending(data, ts->sockindex))
+ return CURLE_OK;
+
+ while(ts->keepon) {
+ ssize_t gotbytes;
+ 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_read(data, tunnelsocket, &byte, 1, &gotbytes);
+ 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(gotbytes <= 0) {
+ 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're done! */
+ ts->cl--;
+ if(ts->cl <= 0) {
+ ts->keepon = KEEPON_DONE;
+ break;
+ }
+ }
+ else {
+ /* chunked-encoded body, so we need to do the chunked dance
+ properly to know when the end of the body is reached */
+ CHUNKcode r;
+ CURLcode extra;
+ ssize_t tookcareof = 0;
+
+ /* now parse the chunked piece of data so that we can
+ properly tell when the stream ends */
+ r = Curl_httpchunk_read(data, &byte, 1, &tookcareof, &extra);
+ if(r == CHUNKE_STOP) {
+ /* we're done reading chunks! */
+ infof(data, "chunk reading DONE");
+ ts->keepon = KEEPON_DONE;
+ }
+ }
+ continue;
+ }
+
+ if(Curl_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 = Curl_dyn_ptr(&ts->rcvbuf);
+ perline = Curl_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */
+
+ /* output debug if that is requested */
+ Curl_debug(data, CURLINFO_HEADER_IN, linep, perline);
+
+ if(!data->set.suppress_connect_headers) {
+ /* send the header to the callback */
+ int writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
+ (data->set.include_header ? CLIENTWRITE_BODY : 0) |
+ (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
+
+ result = Curl_client_write(data, writetype, linep, perline);
+ if(result)
+ return result;
+ }
+
+ data->info.header_size += (long)perline;
+
+ /* Newlines are CRLF, so the CR is ignored as the line isn't
+ 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 %" CURL_FORMAT_CURL_OFF_T
+ " bytes of response-body", ts->cl);
+ }
+ else if(ts->chunked_encoding) {
+ CHUNKcode r;
+ CURLcode extra;
+
+ infof(data, "Ignore chunked response-body");
+
+ /* We set ignorebody true here since the chunked decoder
+ function will acknowledge that. Pay attention so that this is
+ cleared again when this function returns! */
+ k->ignorebody = TRUE;
+
+ if(linep[1] == '\n')
+ /* this can only be a LF if the letter at index 0 was a CR */
+ linep++;
+
+ /* now parse the chunked piece of data so that we can properly
+ tell when the stream ends */
+ r = Curl_httpchunk_read(data, linep + 1, 1, &gotbytes,
+ &extra);
+ if(r == CHUNKE_STOP) {
+ /* we're done reading chunks! */
+ infof(data, "chunk reading DONE");
+ ts->keepon = KEEPON_DONE;
+ }
+ }
+ else {
+ /* without content-length or chunked encoding, we
+ can't keep the connection alive since the close is
+ the end signal so we bail out at once instead */
+ DEBUGF(infof(data, "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(data, ts, linep);
+ if(result)
+ return result;
+
+ Curl_dyn_reset(&ts->rcvbuf);
+ } /* while there's 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;
+}
+
+#else /* USE_HYPER */
+/* The Hyper version of CONNECT */
+static CURLcode start_CONNECT(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct tunnel_state *ts)
+{
+ struct hyptransfer *h = &data->hyp;
+ curl_socket_t tunnelsocket = conn->sock[ts->sockindex];
+ hyper_io *io = NULL;
+ hyper_request *req = NULL;
+ hyper_headers *headers = NULL;
+ hyper_clientconn_options *options = NULL;
+ hyper_task *handshake = NULL;
+ hyper_task *task = NULL; /* for the handshake */
+ hyper_clientconn *client = NULL;
+ hyper_task *sendtask = NULL; /* for the send */
+ char *hostheader = NULL; /* for CONNECT */
+ char *host = NULL; /* Host: */
+ CURLcode result = CURLE_OUT_OF_MEMORY;
+
+ io = hyper_io_new();
+ if(!io) {
+ failf(data, "Couldn't create hyper IO");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ /* tell Hyper how to read/write network data */
+ hyper_io_set_userdata(io, data);
+ hyper_io_set_read(io, Curl_hyper_recv);
+ hyper_io_set_write(io, Curl_hyper_send);
+ conn->sockfd = tunnelsocket;
+
+ data->state.hconnect = TRUE;
+
+ /* create an executor to poll futures */
+ if(!h->exec) {
+ h->exec = hyper_executor_new();
+ if(!h->exec) {
+ failf(data, "Couldn't create hyper executor");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ }
+
+ options = hyper_clientconn_options_new();
+ hyper_clientconn_options_set_preserve_header_case(options, 1);
+ hyper_clientconn_options_set_preserve_header_order(options, 1);
+
+ if(!options) {
+ failf(data, "Couldn't create hyper client options");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ hyper_clientconn_options_exec(options, h->exec);
+
+ /* "Both the `io` and the `options` are consumed in this function
+ call" */
+ handshake = hyper_clientconn_handshake(io, options);
+ if(!handshake) {
+ failf(data, "Couldn't create hyper client handshake");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ io = NULL;
+ options = NULL;
+
+ if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) {
+ failf(data, "Couldn't hyper_executor_push the handshake");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ handshake = NULL; /* ownership passed on */
+
+ task = hyper_executor_poll(h->exec);
+ if(!task) {
+ failf(data, "Couldn't hyper_executor_poll the handshake");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ client = hyper_task_value(task);
+ hyper_task_free(task);
+ req = hyper_request_new();
+ if(!req) {
+ failf(data, "Couldn't hyper_request_new");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ if(hyper_request_set_method(req, (uint8_t *)"CONNECT",
+ strlen("CONNECT"))) {
+ failf(data, "error setting method");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ infof(data, "Establish HTTP proxy tunnel to %s:%d",
+ ts->hostname, ts->remote_port);
+
+ /* This only happens if we've looped here due to authentication
+ reasons, and we don't really use the newly cloned URL here
+ then. Just free() it. */
+ Curl_safefree(data->req.newurl);
+
+ result = CONNECT_host(data, conn, ts->hostname, ts->remote_port,
+ &hostheader, &host);
+ if(result)
+ goto error;
+
+ if(hyper_request_set_uri(req, (uint8_t *)hostheader,
+ strlen(hostheader))) {
+ failf(data, "error setting path");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ if(data->set.verbose) {
+ char *se = aprintf("CONNECT %s HTTP/1.1\r\n", hostheader);
+ if(!se) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ Curl_debug(data, CURLINFO_HEADER_OUT, se, strlen(se));
+ free(se);
+ }
+ /* Setup the proxy-authorization header, if any */
+ result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET,
+ hostheader, TRUE);
+ if(result)
+ goto error;
+ Curl_safefree(hostheader);
+
+ /* default is 1.1 */
+ if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) &&
+ (HYPERE_OK != hyper_request_set_version(req,
+ HYPER_HTTP_VERSION_1_0))) {
+ failf(data, "error setting HTTP version");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ headers = hyper_request_headers(req);
+ if(!headers) {
+ failf(data, "hyper_request_headers");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ if(host) {
+ result = Curl_hyper_header(data, headers, host);
+ if(result)
+ goto error;
+ Curl_safefree(host);
+ }
+
+ if(data->state.aptr.proxyuserpwd) {
+ result = Curl_hyper_header(data, headers,
+ data->state.aptr.proxyuserpwd);
+ if(result)
+ goto error;
+ }
+
+ if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) &&
+ data->set.str[STRING_USERAGENT]) {
+ struct dynbuf ua;
+ Curl_dyn_init(&ua, DYN_HTTP_REQUEST);
+ result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n",
+ data->set.str[STRING_USERAGENT]);
+ if(result)
+ goto error;
+ result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua));
+ if(result)
+ goto error;
+ Curl_dyn_free(&ua);
+ }
+
+ if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) {
+ result = Curl_hyper_header(data, headers,
+ "Proxy-Connection: Keep-Alive");
+ if(result)
+ goto error;
+ }
+
+ result = Curl_add_custom_headers(data, TRUE, headers);
+ if(result)
+ goto error;
+
+ sendtask = hyper_clientconn_send(client, req);
+ if(!sendtask) {
+ failf(data, "hyper_clientconn_send");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
+ failf(data, "Couldn't hyper_executor_push the send");
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+
+error:
+ free(host);
+ free(hostheader);
+ if(io)
+ hyper_io_free(io);
+ if(options)
+ hyper_clientconn_options_free(options);
+ if(handshake)
+ hyper_task_free(handshake);
+ if(client)
+ hyper_clientconn_free(client);
+ return result;
+}
+
+static CURLcode send_CONNECT(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct tunnel_state *ts,
+ bool *done)
+{
+ struct hyptransfer *h = &data->hyp;
+ hyper_task *task = NULL;
+ hyper_error *hypererr = NULL;
+ CURLcode result = CURLE_OK;
+
+ (void)ts;
+ (void)conn;
+ do {
+ task = hyper_executor_poll(h->exec);
+ if(task) {
+ bool error = hyper_task_type(task) == HYPER_TASK_ERROR;
+ if(error)
+ hypererr = hyper_task_value(task);
+ hyper_task_free(task);
+ if(error) {
+ /* this could probably use a better error code? */
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ }
+ } while(task);
+error:
+ *done = (result == CURLE_OK);
+ if(hypererr) {
+ uint8_t errbuf[256];
+ size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
+ failf(data, "Hyper: %.*s", (int)errlen, errbuf);
+ hyper_error_free(hypererr);
+ }
+ return result;
+}
+
+static CURLcode recv_CONNECT_resp(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct tunnel_state *ts,
+ bool *done)
+{
+ struct hyptransfer *h = &data->hyp;
+ CURLcode result;
+ int didwhat;
+
+ (void)ts;
+ *done = FALSE;
+ result = Curl_hyper_stream(data, conn, &didwhat, done,
+ CURL_CSELECT_IN | CURL_CSELECT_OUT);
+ if(result || !*done)
+ return result;
+ if(h->exec) {
+ hyper_executor_free(h->exec);
+ h->exec = NULL;
+ }
+ if(h->read_waker) {
+ hyper_waker_free(h->read_waker);
+ h->read_waker = NULL;
+ }
+ if(h->write_waker) {
+ hyper_waker_free(h->write_waker);
+ h->write_waker = NULL;
+ }
+ return result;
+}
+
+#endif /* USE_HYPER */
+
+static CURLcode CONNECT(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct 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 TUNNEL_INIT:
+ /* Prepare the CONNECT request and make a first attempt to send. */
+ result = start_CONNECT(data, cf->conn, ts);
+ if(result)
+ goto out;
+ tunnel_go_state(cf, ts, TUNNEL_CONNECT, data);
+ /* FALLTHROUGH */
+
+ case TUNNEL_CONNECT:
+ /* see that the request is completely sent */
+ result = send_CONNECT(data, cf->conn, ts, &done);
+ if(result || !done)
+ goto out;
+ tunnel_go_state(cf, ts, TUNNEL_RECEIVE, data);
+ /* FALLTHROUGH */
+
+ case TUNNEL_RECEIVE:
+ /* read what is there */
+ result = recv_CONNECT_resp(data, cf->conn, 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 */
+ tunnel_go_state(cf, ts, TUNNEL_RESPONSE, data);
+ /* FALLTHROUGH */
+
+ case TUNNEL_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. */
+ if(ts->close_connection || conn->bits.close) {
+ /* Close the filter chain and trigger connect, non-blocking
+ * again, so the process is ongoing. This will
+ * a) the close resets our tunnel state
+ * b) the connect makes sure that there will be a socket
+ * to select on again.
+ * We return and expect to be called again. */
+ infof(data, "Connect me again please");
+ Curl_conn_close(data, cf->sockindex);
+ result = cf->next->cft->connect(cf->next, data, FALSE, &done);
+ goto out;
+ }
+ /* staying on this connection, reset state */
+ tunnel_go_state(cf, ts, TUNNEL_INIT, data);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ } while(data->req.newurl);
+
+ DEBUGASSERT(ts->tunnel_state == TUNNEL_RESPONSE);
+ if(data->info.httpproxycode/100 != 2) {
+ /* a non-2xx response and we have no next url to try. */
+ free(data->req.newurl);
+ data->req.newurl = NULL;
+ /* failure, close this connection to avoid re-use */
+ streamclose(conn, "proxy CONNECT failure");
+ tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
+ failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode);
+ return CURLE_RECV_ERROR;
+ }
+ /* 2xx response, SUCCESS! */
+ tunnel_go_state(cf, ts, TUNNEL_ESTABLISHED, data);
+ infof(data, "CONNECT tunnel established, response %d",
+ data->info.httpproxycode);
+ result = CURLE_OK;
+
+out:
+ if(result)
+ tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
+ return result;
+}
+
+static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ CURLcode result;
+ struct tunnel_state *ts = cf->ctx;
+
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ result = cf->next->cft->connect(cf->next, data, blocking, done);
+ if(result || !*done)
+ return result;
+
+ /* TODO: can we do blocking? */
+ /* We want "seamless" operations through HTTP proxy tunnel */
+
+ /* for the secondary socket (FTP), use the "connect to host"
+ * but ignore the "connect to port" (use the secondary port)
+ */
+ *done = FALSE;
+ if(!ts) {
+ result = tunnel_init(&ts, data, cf->conn, cf->sockindex);
+ if(result)
+ return result;
+ cf->ctx = ts;
+ }
+
+ result = 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;
+ tunnel_free(cf, data);
+ }
+ return result;
+}
+
+static void http_proxy_cf_get_host(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char **phost,
+ const char **pdisplay_host,
+ int *pport)
+{
+ (void)data;
+ if(!cf->connected) {
+ *phost = cf->conn->http_proxy.host.name;
+ *pdisplay_host = cf->conn->http_proxy.host.dispname;
+ *pport = (int)cf->conn->http_proxy.port;
+ }
+ else {
+ cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
+ }
+}
+
+static int http_proxy_cf_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ struct tunnel_state *ts = cf->ctx;
+ struct connectdata *conn = cf->conn;
+ int fds;
+
+ DEBUGASSERT(conn);
+ fds = cf->next->cft->get_select_socks(cf->next, data, socks);
+ if(!fds && cf->next->connected && !cf->connected) {
+ /* If we are not connected, but the filter "below" is
+ * and not waiting on something, we are tunneling. */
+ socks[0] = conn->sock[cf->sockindex];
+ if(ts) {
+ /* when we've 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're still sending the request, wait
+ for write. */
+ if(ts->http_proxy.sending == HTTPSEND_REQUEST)
+ return GETSOCK_WRITESOCK(0);
+ return GETSOCK_READSOCK(0);
+ }
+ return GETSOCK_WRITESOCK(0);
+ }
+ return fds;
+}
+
+static void http_proxy_cf_detach_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ if(cf->ctx) {
+ tunnel_free(cf, data);
+ }
+}
+
+static void http_proxy_cf_destroy(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ http_proxy_cf_detach_data(cf, data);
+}
+
+static void http_proxy_cf_close(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ DEBUGASSERT(cf->next);
+ cf->connected = FALSE;
+ cf->next->cft->close(cf->next, data);
+ if(cf->ctx) {
+ tunnel_go_state(cf, cf->ctx, TUNNEL_INIT, data);
+ }
+}
+
+
+static const struct Curl_cftype cft_http_proxy = {
+ "HTTP-PROXY",
+ CF_TYPE_IP_CONNECT,
+ http_proxy_cf_destroy,
+ Curl_cf_def_setup,
+ http_proxy_cf_connect,
+ http_proxy_cf_close,
+ http_proxy_cf_get_host,
+ http_proxy_cf_get_select_socks,
+ Curl_cf_def_data_pending,
+ Curl_cf_def_send,
+ Curl_cf_def_recv,
+ Curl_cf_def_attach_data,
+ http_proxy_cf_detach_data,
+};
+
+CURLcode Curl_conn_http_proxy_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
+
+ result = Curl_cf_create(&cf, &cft_http_proxy, NULL);
+ if(!result)
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+ return result;
+}
+
+
+static CURLcode send_haproxy_header(struct Curl_cfilter*cf,
+ struct Curl_easy *data)
+{
+ struct dynbuf req;
+ CURLcode result;
+ const char *tcp_version;
+ Curl_dyn_init(&req, DYN_HAXPROXY);
+
+#ifdef USE_UNIX_SOCKETS
+ if(cf->conn->unix_domain_socket)
+ /* the buffer is large enough to hold this! */
+ result = Curl_dyn_addn(&req, STRCONST("PROXY UNKNOWN\r\n"));
+ else {
+#endif /* USE_UNIX_SOCKETS */
+ /* Emit the correct prefix for IPv6 */
+ tcp_version = cf->conn->bits.ipv6 ? "TCP6" : "TCP4";
+
+ result = Curl_dyn_addf(&req, "PROXY %s %s %s %i %i\r\n",
+ tcp_version,
+ data->info.conn_local_ip,
+ data->info.conn_primary_ip,
+ data->info.conn_local_port,
+ data->info.conn_primary_port);
+
+#ifdef USE_UNIX_SOCKETS
+ }
+#endif /* USE_UNIX_SOCKETS */
+
+ if(!result)
+ result = Curl_buffer_send(&req, data, &data->info.request_size,
+ 0, FIRSTSOCKET);
+ return result;
+}
+
+static CURLcode haproxy_cf_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ CURLcode result;
+
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ result = cf->next->cft->connect(cf->next, data, blocking, done);
+ if(result || !*done)
+ return result;
+
+ result = send_haproxy_header(cf, data);
+ *done = (!result);
+ cf->connected = *done;
+ return result;
+}
+
+static const struct Curl_cftype cft_haproxy = {
+ "HAPROXY",
+ 0,
+ Curl_cf_def_destroy_this,
+ Curl_cf_def_setup,
+ haproxy_cf_connect,
+ Curl_cf_def_close,
+ Curl_cf_def_get_host,
+ Curl_cf_def_get_select_socks,
+ Curl_cf_def_data_pending,
+ Curl_cf_def_send,
+ Curl_cf_def_recv,
+ Curl_cf_def_attach_data,
+ Curl_cf_def_detach_data,
+};
+
+CURLcode Curl_conn_haproxy_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
+
+ result = Curl_cf_create(&cf, &cft_haproxy, NULL);
+ if(!result)
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+ return result;
+}
+
+#endif /* !CURL_DISABLE_PROXY &6 ! CURL_DISABLE_HTTP */
diff --git a/libs/libcurl/src/http_proxy.h b/libs/libcurl/src/http_proxy.h index 1e650ee571..bbc581a005 100644 --- a/libs/libcurl/src/http_proxy.h +++ b/libs/libcurl/src/http_proxy.h @@ -1,81 +1,45 @@ -#ifndef HEADER_CURL_HTTP_PROXY_H -#define HEADER_CURL_HTTP_PROXY_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) -/* ftp can use this as well */ -CURLcode Curl_proxyCONNECT(struct Curl_easy *data, - int tunnelsocket, - const char *hostname, int remote_port); - -/* Default proxy timeout in milliseconds */ -#define PROXY_TIMEOUT (3600*1000) - -CURLcode Curl_proxy_connect(struct Curl_easy *data, int sockindex); - -bool Curl_connect_complete(struct connectdata *conn); -bool Curl_connect_ongoing(struct connectdata *conn); -int Curl_connect_getsock(struct connectdata *conn); -void Curl_connect_done(struct Curl_easy *data); - -#else -#define Curl_proxyCONNECT(x,y,z,w) CURLE_NOT_BUILT_IN -#define Curl_proxy_connect(x,y) CURLE_OK -#define Curl_connect_complete(x) CURLE_OK -#define Curl_connect_ongoing(x) FALSE -#define Curl_connect_getsock(x) 0 -#define Curl_connect_done(x) -#endif - -void Curl_connect_free(struct Curl_easy *data); - -/* struct for HTTP CONNECT state data */ -struct http_connect_state { - struct HTTP http_proxy; - struct HTTP *prot_save; - struct dynbuf rcvbuf; - struct dynbuf req; - size_t nsend; - size_t headerlines; - enum keeponval { - KEEPON_DONE, - KEEPON_CONNECT, - KEEPON_IGNORE - } keepon; - curl_off_t cl; /* size of content to read and ignore */ - enum { - TUNNEL_INIT, /* init/default/no tunnel state */ - TUNNEL_CONNECT, /* CONNECT has been sent off */ - TUNNEL_COMPLETE, /* CONNECT response received completely */ - TUNNEL_EXIT - } tunnel_state; - BIT(chunked_encoding); - BIT(close_connection); -}; - -#endif /* HEADER_CURL_HTTP_PROXY_H */ +#ifndef HEADER_CURL_HTTP_PROXY_H
+#define HEADER_CURL_HTTP_PROXY_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
+
+/* Default proxy timeout in milliseconds */
+#define PROXY_TIMEOUT (3600*1000)
+
+CURLcode Curl_conn_http_proxy_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex);
+
+CURLcode Curl_conn_haproxy_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex);
+
+#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
+
+#endif /* HEADER_CURL_HTTP_PROXY_H */
diff --git a/libs/libcurl/src/idn.c b/libs/libcurl/src/idn.c new file mode 100644 index 0000000000..76664da383 --- /dev/null +++ b/libs/libcurl/src/idn.c @@ -0,0 +1,193 @@ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+ /*
+ * IDN conversions
+ */
+
+#include "curl_setup.h"
+#include "urldata.h"
+#include "idn.h"
+#include "sendf.h"
+#include "curl_multibyte.h"
+#include "warnless.h"
+
+#ifdef USE_LIBIDN2
+#include <idn2.h>
+
+#if defined(WIN32) && defined(UNICODE)
+#define IDN2_LOOKUP(name, host, flags) \
+ idn2_lookup_u8((const uint8_t *)name, (uint8_t **)host, flags)
+#else
+#define IDN2_LOOKUP(name, host, flags) \
+ idn2_lookup_ul((const char *)name, (char **)host, flags)
+#endif
+#endif /* USE_LIBIDN2 */
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#ifdef USE_WIN32_IDN
+/* using Windows kernel32 and normaliz libraries. */
+
+#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x600
+WINBASEAPI int WINAPI IdnToAscii(DWORD dwFlags,
+ const WCHAR *lpUnicodeCharStr,
+ int cchUnicodeChar,
+ WCHAR *lpASCIICharStr,
+ int cchASCIIChar);
+WINBASEAPI int WINAPI IdnToUnicode(DWORD dwFlags,
+ const WCHAR *lpASCIICharStr,
+ int cchASCIIChar,
+ WCHAR *lpUnicodeCharStr,
+ int cchUnicodeChar);
+#endif
+
+#define IDN_MAX_LENGTH 255
+
+bool Curl_win32_idn_to_ascii(const char *in, char **out)
+{
+ bool success = FALSE;
+
+ wchar_t *in_w = curlx_convert_UTF8_to_wchar(in);
+ if(in_w) {
+ wchar_t punycode[IDN_MAX_LENGTH];
+ int chars = IdnToAscii(0, in_w, -1, punycode, IDN_MAX_LENGTH);
+ curlx_unicodefree(in_w);
+ if(chars) {
+ char *mstr = curlx_convert_wchar_to_UTF8(punycode);
+ if(mstr) {
+ *out = strdup(mstr);
+ curlx_unicodefree(mstr);
+ if(*out)
+ success = TRUE;
+ }
+ }
+ }
+
+ return success;
+}
+
+#endif /* USE_WIN32_IDN */
+
+/*
+ * Helpers for IDNA conversions.
+ */
+bool Curl_is_ASCII_name(const char *hostname)
+{
+ /* get an UNSIGNED local version of the pointer */
+ const unsigned char *ch = (const unsigned char *)hostname;
+
+ if(!hostname) /* bad input, consider it ASCII! */
+ return TRUE;
+
+ while(*ch) {
+ if(*ch++ & 0x80)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#ifdef USE_IDN
+/*
+ * Curl_idn_decode() returns an allocated IDN decoded string if it was
+ * possible. NULL on error.
+ */
+static char *Curl_idn_decode(const char *input)
+{
+ char *decoded = NULL;
+#ifdef USE_LIBIDN2
+ if(idn2_check_version(IDN2_VERSION)) {
+ int flags = IDN2_NFC_INPUT
+#if IDN2_VERSION_NUMBER >= 0x00140000
+ /* IDN2_NFC_INPUT: Normalize input string using normalization form C.
+ IDN2_NONTRANSITIONAL: Perform Unicode TR46 non-transitional
+ processing. */
+ | IDN2_NONTRANSITIONAL
+#endif
+ ;
+ int rc = IDN2_LOOKUP(input, &decoded, flags);
+ if(rc != IDN2_OK)
+ /* fallback to TR46 Transitional mode for better IDNA2003
+ compatibility */
+ rc = IDN2_LOOKUP(input, &decoded, IDN2_TRANSITIONAL);
+ if(rc != IDN2_OK)
+ decoded = NULL;
+ }
+#elif defined(USE_WIN32_IDN)
+ if(!Curl_win32_idn_to_ascii(input, &decoded))
+ decoded = NULL;
+#endif
+ return decoded;
+}
+
+/*
+ * Frees data allocated by idnconvert_hostname()
+ */
+void Curl_free_idnconverted_hostname(struct hostname *host)
+{
+#if defined(USE_LIBIDN2)
+ if(host->encalloc) {
+ idn2_free(host->encalloc); /* must be freed with idn2_free() since this was
+ allocated by libidn */
+ host->encalloc = NULL;
+ }
+#elif defined(USE_WIN32_IDN)
+ free(host->encalloc); /* must be freed with free() since this was
+ allocated by Curl_win32_idn_to_ascii */
+ host->encalloc = NULL;
+#else
+ (void)host;
+#endif
+}
+
+#endif /* USE_IDN */
+
+/*
+ * Perform any necessary IDN conversion of hostname
+ */
+CURLcode Curl_idnconvert_hostname(struct hostname *host)
+{
+ /* set the name we use to display the host name */
+ host->dispname = host->name;
+
+#ifdef USE_IDN
+ /* Check name for non-ASCII and convert hostname if we can */
+ if(!Curl_is_ASCII_name(host->name)) {
+ char *decoded = Curl_idn_decode(host->name);
+ if(decoded) {
+ /* successful */
+ host->encalloc = decoded;
+ /* change the name pointer to point to the encoded hostname */
+ host->name = host->encalloc;
+ }
+ else
+ return CURLE_URL_MALFORMAT;
+ }
+#endif
+ return CURLE_OK;
+}
+
diff --git a/libs/libcurl/src/idn.h b/libs/libcurl/src/idn.h new file mode 100644 index 0000000000..9a4ef59d17 --- /dev/null +++ b/libs/libcurl/src/idn.h @@ -0,0 +1,38 @@ +#ifndef HEADER_CURL_IDN_H
+#define HEADER_CURL_IDN_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+#ifdef USE_WIN32_IDN
+bool Curl_win32_idn_to_ascii(const char *in, char **out);
+#endif /* USE_WIN32_IDN */
+bool Curl_is_ASCII_name(const char *hostname);
+CURLcode Curl_idnconvert_hostname(struct hostname *host);
+#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN)
+#define USE_IDN
+void Curl_free_idnconverted_hostname(struct hostname *host);
+#else
+#define Curl_free_idnconverted_hostname(x)
+#endif
+#endif /* HEADER_CURL_IDN_H */
diff --git a/libs/libcurl/src/idn_win32.c b/libs/libcurl/src/idn_win32.c deleted file mode 100644 index 2433d927ee..0000000000 --- a/libs/libcurl/src/idn_win32.c +++ /dev/null @@ -1,121 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - - /* - * IDN conversions using Windows kernel32 and normaliz libraries. - */ - -#include "curl_setup.h" - -#ifdef USE_WIN32_IDN - -#include "curl_multibyte.h" -#include "curl_memory.h" -#include "warnless.h" - - /* The last #include file should be: */ -#include "memdebug.h" - -#ifdef WANT_IDN_PROTOTYPES -# if defined(_SAL_VERSION) -WINNORMALIZEAPI int WINAPI -IdnToAscii(_In_ DWORD dwFlags, - _In_reads_(cchUnicodeChar) LPCWSTR lpUnicodeCharStr, - _In_ int cchUnicodeChar, - _Out_writes_opt_(cchASCIIChar) LPWSTR lpASCIICharStr, - _In_ int cchASCIIChar); -WINNORMALIZEAPI int WINAPI -IdnToUnicode(_In_ DWORD dwFlags, - _In_reads_(cchASCIIChar) LPCWSTR lpASCIICharStr, - _In_ int cchASCIIChar, - _Out_writes_opt_(cchUnicodeChar) LPWSTR lpUnicodeCharStr, - _In_ int cchUnicodeChar); -# else -WINBASEAPI int WINAPI IdnToAscii(DWORD dwFlags, - const WCHAR *lpUnicodeCharStr, - int cchUnicodeChar, - WCHAR *lpASCIICharStr, - int cchASCIIChar); -WINBASEAPI int WINAPI IdnToUnicode(DWORD dwFlags, - const WCHAR *lpASCIICharStr, - int cchASCIIChar, - WCHAR *lpUnicodeCharStr, - int cchUnicodeChar); -# endif -#endif - -#define IDN_MAX_LENGTH 255 - -bool Curl_win32_idn_to_ascii(const char *in, char **out); -bool Curl_win32_ascii_to_idn(const char *in, char **out); - -bool Curl_win32_idn_to_ascii(const char *in, char **out) -{ - bool success = FALSE; - - wchar_t *in_w = curlx_convert_UTF8_to_wchar(in); - if(in_w) { - wchar_t punycode[IDN_MAX_LENGTH]; - int chars = IdnToAscii(0, in_w, -1, punycode, IDN_MAX_LENGTH); - curlx_unicodefree(in_w); - if(chars) { - char *mstr = curlx_convert_wchar_to_UTF8(punycode); - if(mstr) { - *out = strdup(mstr); - curlx_unicodefree(mstr); - if(*out) - success = TRUE; - } - } - } - - return success; -} - -bool Curl_win32_ascii_to_idn(const char *in, char **out) -{ - bool success = FALSE; - - wchar_t *in_w = curlx_convert_UTF8_to_wchar(in); - if(in_w) { - size_t in_len = wcslen(in_w) + 1; - wchar_t unicode[IDN_MAX_LENGTH]; - int chars = IdnToUnicode(0, in_w, curlx_uztosi(in_len), - unicode, IDN_MAX_LENGTH); - curlx_unicodefree(in_w); - if(chars) { - char *mstr = curlx_convert_wchar_to_UTF8(unicode); - if(mstr) { - *out = strdup(mstr); - curlx_unicodefree(mstr); - if(*out) - success = TRUE; - } - } - } - - return success; -} - -#endif /* USE_WIN32_IDN */ diff --git a/libs/libcurl/src/if2ip.c b/libs/libcurl/src/if2ip.c index c29194878f..c1046cda39 100644 --- a/libs/libcurl/src/if2ip.c +++ b/libs/libcurl/src/if2ip.c @@ -1,256 +1,256 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_ARPA_INET_H -# include <arpa/inet.h> -#endif -#ifdef HAVE_NET_IF_H -# include <net/if.h> -#endif -#ifdef HAVE_SYS_IOCTL_H -# include <sys/ioctl.h> -#endif -#ifdef HAVE_NETDB_H -# include <netdb.h> -#endif -#ifdef HAVE_SYS_SOCKIO_H -# include <sys/sockio.h> -#endif -#ifdef HAVE_IFADDRS_H -# include <ifaddrs.h> -#endif -#ifdef HAVE_STROPTS_H -# include <stropts.h> -#endif -#ifdef __VMS -# include <inet.h> -#endif - -#include "inet_ntop.h" -#include "strcase.h" -#include "if2ip.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* ------------------------------------------------------------------ */ - -#ifdef ENABLE_IPV6 -/* Return the scope of the given address. */ -unsigned int Curl_ipv6_scope(const struct sockaddr *sa) -{ - if(sa->sa_family == AF_INET6) { - const struct sockaddr_in6 * sa6 = (const struct sockaddr_in6 *)(void *) sa; - const unsigned char *b = sa6->sin6_addr.s6_addr; - unsigned short w = (unsigned short) ((b[0] << 8) | b[1]); - - if((b[0] & 0xFE) == 0xFC) /* Handle ULAs */ - return IPV6_SCOPE_UNIQUELOCAL; - switch(w & 0xFFC0) { - case 0xFE80: - return IPV6_SCOPE_LINKLOCAL; - case 0xFEC0: - return IPV6_SCOPE_SITELOCAL; - case 0x0000: - w = b[1] | b[2] | b[3] | b[4] | b[5] | b[6] | b[7] | b[8] | b[9] | - b[10] | b[11] | b[12] | b[13] | b[14]; - if(w || b[15] != 0x01) - break; - return IPV6_SCOPE_NODELOCAL; - default: - break; - } - } - return IPV6_SCOPE_GLOBAL; -} -#endif - -#if defined(HAVE_GETIFADDRS) - -if2ip_result_t Curl_if2ip(int af, -#ifdef ENABLE_IPV6 - unsigned int remote_scope, - unsigned int local_scope_id, -#endif - const char *interf, - char *buf, int buf_size) -{ - struct ifaddrs *iface, *head; - if2ip_result_t res = IF2IP_NOT_FOUND; - -#if defined(ENABLE_IPV6) && \ - !defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) - (void) local_scope_id; -#endif - - if(getifaddrs(&head) >= 0) { - for(iface = head; iface != NULL; iface = iface->ifa_next) { - if(iface->ifa_addr) { - if(iface->ifa_addr->sa_family == af) { - if(strcasecompare(iface->ifa_name, interf)) { - void *addr; - const char *ip; - char scope[12] = ""; - char ipstr[64]; -#ifdef ENABLE_IPV6 - if(af == AF_INET6) { -#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID - unsigned int scopeid = 0; -#endif - unsigned int ifscope = Curl_ipv6_scope(iface->ifa_addr); - - if(ifscope != remote_scope) { - /* We are interested only in interface addresses whose scope - matches the remote address we want to connect to: global - for global, link-local for link-local, etc... */ - if(res == IF2IP_NOT_FOUND) - res = IF2IP_AF_NOT_SUPPORTED; - continue; - } - - addr = - &((struct sockaddr_in6 *)(void *)iface->ifa_addr)->sin6_addr; -#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID - /* Include the scope of this interface as part of the address */ - scopeid = ((struct sockaddr_in6 *)(void *)iface->ifa_addr) - ->sin6_scope_id; - - /* If given, scope id should match. */ - if(local_scope_id && scopeid != local_scope_id) { - if(res == IF2IP_NOT_FOUND) - res = IF2IP_AF_NOT_SUPPORTED; - - continue; - } - - if(scopeid) - msnprintf(scope, sizeof(scope), "%%%u", scopeid); -#endif - } - else -#endif - addr = - &((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr; - res = IF2IP_FOUND; - ip = Curl_inet_ntop(af, addr, ipstr, sizeof(ipstr)); - msnprintf(buf, buf_size, "%s%s", ip, scope); - break; - } - } - else if((res == IF2IP_NOT_FOUND) && - strcasecompare(iface->ifa_name, interf)) { - res = IF2IP_AF_NOT_SUPPORTED; - } - } - } - - freeifaddrs(head); - } - - return res; -} - -#elif defined(HAVE_IOCTL_SIOCGIFADDR) - -if2ip_result_t Curl_if2ip(int af, -#ifdef ENABLE_IPV6 - unsigned int remote_scope, - unsigned int local_scope_id, -#endif - const char *interf, - char *buf, int buf_size) -{ - struct ifreq req; - struct in_addr in; - struct sockaddr_in *s; - curl_socket_t dummy; - size_t len; - const char *r; - -#ifdef ENABLE_IPV6 - (void)remote_scope; - (void)local_scope_id; -#endif - - if(!interf || (af != AF_INET)) - return IF2IP_NOT_FOUND; - - len = strlen(interf); - if(len >= sizeof(req.ifr_name)) - return IF2IP_NOT_FOUND; - - dummy = socket(AF_INET, SOCK_STREAM, 0); - if(CURL_SOCKET_BAD == dummy) - return IF2IP_NOT_FOUND; - - memset(&req, 0, sizeof(req)); - memcpy(req.ifr_name, interf, len + 1); - req.ifr_addr.sa_family = AF_INET; - - if(ioctl(dummy, SIOCGIFADDR, &req) < 0) { - sclose(dummy); - /* With SIOCGIFADDR, we cannot tell the difference between an interface - that does not exist and an interface that has no address of the - correct family. Assume the interface does not exist */ - return IF2IP_NOT_FOUND; - } - - s = (struct sockaddr_in *)(void *)&req.ifr_addr; - memcpy(&in, &s->sin_addr, sizeof(in)); - r = Curl_inet_ntop(s->sin_family, &in, buf, buf_size); - - sclose(dummy); - if(!r) - return IF2IP_NOT_FOUND; - return IF2IP_FOUND; -} - -#else - -if2ip_result_t Curl_if2ip(int af, -#ifdef ENABLE_IPV6 - unsigned int remote_scope, - unsigned int local_scope_id, -#endif - const char *interf, - char *buf, int buf_size) -{ - (void) af; -#ifdef ENABLE_IPV6 - (void) remote_scope; - (void) local_scope_id; -#endif - (void) interf; - (void) buf; - (void) buf_size; - return IF2IP_NOT_FOUND; -} - -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+#ifdef HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#ifdef HAVE_NETDB_H
+# include <netdb.h>
+#endif
+#ifdef HAVE_SYS_SOCKIO_H
+# include <sys/sockio.h>
+#endif
+#ifdef HAVE_IFADDRS_H
+# include <ifaddrs.h>
+#endif
+#ifdef HAVE_STROPTS_H
+# include <stropts.h>
+#endif
+#ifdef __VMS
+# include <inet.h>
+#endif
+
+#include "inet_ntop.h"
+#include "strcase.h"
+#include "if2ip.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* ------------------------------------------------------------------ */
+
+#ifdef ENABLE_IPV6
+/* Return the scope of the given address. */
+unsigned int Curl_ipv6_scope(const struct sockaddr *sa)
+{
+ if(sa->sa_family == AF_INET6) {
+ const struct sockaddr_in6 * sa6 = (const struct sockaddr_in6 *)(void *) sa;
+ const unsigned char *b = sa6->sin6_addr.s6_addr;
+ unsigned short w = (unsigned short) ((b[0] << 8) | b[1]);
+
+ if((b[0] & 0xFE) == 0xFC) /* Handle ULAs */
+ return IPV6_SCOPE_UNIQUELOCAL;
+ switch(w & 0xFFC0) {
+ case 0xFE80:
+ return IPV6_SCOPE_LINKLOCAL;
+ case 0xFEC0:
+ return IPV6_SCOPE_SITELOCAL;
+ case 0x0000:
+ w = b[1] | b[2] | b[3] | b[4] | b[5] | b[6] | b[7] | b[8] | b[9] |
+ b[10] | b[11] | b[12] | b[13] | b[14];
+ if(w || b[15] != 0x01)
+ break;
+ return IPV6_SCOPE_NODELOCAL;
+ default:
+ break;
+ }
+ }
+ return IPV6_SCOPE_GLOBAL;
+}
+#endif
+
+#if defined(HAVE_GETIFADDRS)
+
+if2ip_result_t Curl_if2ip(int af,
+#ifdef ENABLE_IPV6
+ unsigned int remote_scope,
+ unsigned int local_scope_id,
+#endif
+ const char *interf,
+ char *buf, int buf_size)
+{
+ struct ifaddrs *iface, *head;
+ if2ip_result_t res = IF2IP_NOT_FOUND;
+
+#if defined(ENABLE_IPV6) && \
+ !defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
+ (void) local_scope_id;
+#endif
+
+ if(getifaddrs(&head) >= 0) {
+ for(iface = head; iface != NULL; iface = iface->ifa_next) {
+ if(iface->ifa_addr) {
+ if(iface->ifa_addr->sa_family == af) {
+ if(strcasecompare(iface->ifa_name, interf)) {
+ void *addr;
+ const char *ip;
+ char scope[12] = "";
+ char ipstr[64];
+#ifdef ENABLE_IPV6
+ if(af == AF_INET6) {
+#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
+ unsigned int scopeid = 0;
+#endif
+ unsigned int ifscope = Curl_ipv6_scope(iface->ifa_addr);
+
+ if(ifscope != remote_scope) {
+ /* We are interested only in interface addresses whose scope
+ matches the remote address we want to connect to: global
+ for global, link-local for link-local, etc... */
+ if(res == IF2IP_NOT_FOUND)
+ res = IF2IP_AF_NOT_SUPPORTED;
+ continue;
+ }
+
+ addr =
+ &((struct sockaddr_in6 *)(void *)iface->ifa_addr)->sin6_addr;
+#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
+ /* Include the scope of this interface as part of the address */
+ scopeid = ((struct sockaddr_in6 *)(void *)iface->ifa_addr)
+ ->sin6_scope_id;
+
+ /* If given, scope id should match. */
+ if(local_scope_id && scopeid != local_scope_id) {
+ if(res == IF2IP_NOT_FOUND)
+ res = IF2IP_AF_NOT_SUPPORTED;
+
+ continue;
+ }
+
+ if(scopeid)
+ msnprintf(scope, sizeof(scope), "%%%u", scopeid);
+#endif
+ }
+ else
+#endif
+ addr =
+ &((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr;
+ res = IF2IP_FOUND;
+ ip = Curl_inet_ntop(af, addr, ipstr, sizeof(ipstr));
+ msnprintf(buf, buf_size, "%s%s", ip, scope);
+ break;
+ }
+ }
+ else if((res == IF2IP_NOT_FOUND) &&
+ strcasecompare(iface->ifa_name, interf)) {
+ res = IF2IP_AF_NOT_SUPPORTED;
+ }
+ }
+ }
+
+ freeifaddrs(head);
+ }
+
+ return res;
+}
+
+#elif defined(HAVE_IOCTL_SIOCGIFADDR)
+
+if2ip_result_t Curl_if2ip(int af,
+#ifdef ENABLE_IPV6
+ unsigned int remote_scope,
+ unsigned int local_scope_id,
+#endif
+ const char *interf,
+ char *buf, int buf_size)
+{
+ struct ifreq req;
+ struct in_addr in;
+ struct sockaddr_in *s;
+ curl_socket_t dummy;
+ size_t len;
+ const char *r;
+
+#ifdef ENABLE_IPV6
+ (void)remote_scope;
+ (void)local_scope_id;
+#endif
+
+ if(!interf || (af != AF_INET))
+ return IF2IP_NOT_FOUND;
+
+ len = strlen(interf);
+ if(len >= sizeof(req.ifr_name))
+ return IF2IP_NOT_FOUND;
+
+ dummy = socket(AF_INET, SOCK_STREAM, 0);
+ if(CURL_SOCKET_BAD == dummy)
+ return IF2IP_NOT_FOUND;
+
+ memset(&req, 0, sizeof(req));
+ memcpy(req.ifr_name, interf, len + 1);
+ req.ifr_addr.sa_family = AF_INET;
+
+ if(ioctl(dummy, SIOCGIFADDR, &req) < 0) {
+ sclose(dummy);
+ /* With SIOCGIFADDR, we cannot tell the difference between an interface
+ that does not exist and an interface that has no address of the
+ correct family. Assume the interface does not exist */
+ return IF2IP_NOT_FOUND;
+ }
+
+ s = (struct sockaddr_in *)(void *)&req.ifr_addr;
+ memcpy(&in, &s->sin_addr, sizeof(in));
+ r = Curl_inet_ntop(s->sin_family, &in, buf, buf_size);
+
+ sclose(dummy);
+ if(!r)
+ return IF2IP_NOT_FOUND;
+ return IF2IP_FOUND;
+}
+
+#else
+
+if2ip_result_t Curl_if2ip(int af,
+#ifdef ENABLE_IPV6
+ unsigned int remote_scope,
+ unsigned int local_scope_id,
+#endif
+ const char *interf,
+ char *buf, int buf_size)
+{
+ (void) af;
+#ifdef ENABLE_IPV6
+ (void) remote_scope;
+ (void) local_scope_id;
+#endif
+ (void) interf;
+ (void) buf;
+ (void) buf_size;
+ return IF2IP_NOT_FOUND;
+}
+
+#endif
diff --git a/libs/libcurl/src/if2ip.h b/libs/libcurl/src/if2ip.h index 5d15459e98..bebc665a41 100644 --- a/libs/libcurl/src/if2ip.h +++ b/libs/libcurl/src/if2ip.h @@ -1,92 +1,92 @@ -#ifndef HEADER_CURL_IF2IP_H -#define HEADER_CURL_IF2IP_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2020, 2022, 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" - -/* IPv6 address scopes. */ -#define IPV6_SCOPE_GLOBAL 0 /* Global scope. */ -#define IPV6_SCOPE_LINKLOCAL 1 /* Link-local scope. */ -#define IPV6_SCOPE_SITELOCAL 2 /* Site-local scope (deprecated). */ -#define IPV6_SCOPE_UNIQUELOCAL 3 /* Unique local */ -#define IPV6_SCOPE_NODELOCAL 4 /* Loopback. */ - -#ifdef ENABLE_IPV6 -unsigned int Curl_ipv6_scope(const struct sockaddr *sa); -#else -#define Curl_ipv6_scope(x) 0 -#endif - -typedef enum { - IF2IP_NOT_FOUND = 0, /* Interface not found */ - IF2IP_AF_NOT_SUPPORTED = 1, /* Int. exists but has no address for this af */ - IF2IP_FOUND = 2 /* The address has been stored in "buf" */ -} if2ip_result_t; - -if2ip_result_t Curl_if2ip(int af, -#ifdef ENABLE_IPV6 - unsigned int remote_scope, - unsigned int local_scope_id, -#endif - const char *interf, - char *buf, int buf_size); - -#ifdef __INTERIX - -/* Nedelcho Stanev's work-around for SFU 3.0 */ -struct ifreq { -#define IFNAMSIZ 16 -#define IFHWADDRLEN 6 - union { - char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ - } ifr_ifrn; - - union { - struct sockaddr ifru_addr; - struct sockaddr ifru_broadaddr; - struct sockaddr ifru_netmask; - struct sockaddr ifru_hwaddr; - short ifru_flags; - int ifru_metric; - int ifru_mtu; - } ifr_ifru; -}; - -/* This define was added by Daniel to avoid an extra #ifdef INTERIX in the - C code. */ - -#define ifr_name ifr_ifrn.ifrn_name /* interface name */ -#define ifr_addr ifr_ifru.ifru_addr /* address */ -#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */ -#define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */ -#define ifr_flags ifr_ifru.ifru_flags /* flags */ -#define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ -#define ifr_metric ifr_ifru.ifru_metric /* metric */ -#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */ - -#define SIOCGIFADDR _IOW('s', 102, struct ifreq) /* Get if addr */ - -#endif /* __INTERIX */ - -#endif /* HEADER_CURL_IF2IP_H */ +#ifndef HEADER_CURL_IF2IP_H
+#define HEADER_CURL_IF2IP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2020, 2022, 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"
+
+/* IPv6 address scopes. */
+#define IPV6_SCOPE_GLOBAL 0 /* Global scope. */
+#define IPV6_SCOPE_LINKLOCAL 1 /* Link-local scope. */
+#define IPV6_SCOPE_SITELOCAL 2 /* Site-local scope (deprecated). */
+#define IPV6_SCOPE_UNIQUELOCAL 3 /* Unique local */
+#define IPV6_SCOPE_NODELOCAL 4 /* Loopback. */
+
+#ifdef ENABLE_IPV6
+unsigned int Curl_ipv6_scope(const struct sockaddr *sa);
+#else
+#define Curl_ipv6_scope(x) 0
+#endif
+
+typedef enum {
+ IF2IP_NOT_FOUND = 0, /* Interface not found */
+ IF2IP_AF_NOT_SUPPORTED = 1, /* Int. exists but has no address for this af */
+ IF2IP_FOUND = 2 /* The address has been stored in "buf" */
+} if2ip_result_t;
+
+if2ip_result_t Curl_if2ip(int af,
+#ifdef ENABLE_IPV6
+ unsigned int remote_scope,
+ unsigned int local_scope_id,
+#endif
+ const char *interf,
+ char *buf, int buf_size);
+
+#ifdef __INTERIX
+
+/* Nedelcho Stanev's work-around for SFU 3.0 */
+struct ifreq {
+#define IFNAMSIZ 16
+#define IFHWADDRLEN 6
+ union {
+ char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */
+ } ifr_ifrn;
+
+ union {
+ struct sockaddr ifru_addr;
+ struct sockaddr ifru_broadaddr;
+ struct sockaddr ifru_netmask;
+ struct sockaddr ifru_hwaddr;
+ short ifru_flags;
+ int ifru_metric;
+ int ifru_mtu;
+ } ifr_ifru;
+};
+
+/* This define was added by Daniel to avoid an extra #ifdef INTERIX in the
+ C code. */
+
+#define ifr_name ifr_ifrn.ifrn_name /* interface name */
+#define ifr_addr ifr_ifru.ifru_addr /* address */
+#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
+#define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */
+#define ifr_flags ifr_ifru.ifru_flags /* flags */
+#define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */
+#define ifr_metric ifr_ifru.ifru_metric /* metric */
+#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */
+
+#define SIOCGIFADDR _IOW('s', 102, struct ifreq) /* Get if addr */
+
+#endif /* __INTERIX */
+
+#endif /* HEADER_CURL_IF2IP_H */
diff --git a/libs/libcurl/src/imap.c b/libs/libcurl/src/imap.c index ffa08bf7a4..11980286c2 100644 --- a/libs/libcurl/src/imap.c +++ b/libs/libcurl/src/imap.c @@ -1,2131 +1,2131 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * RFC2595 Using TLS with IMAP, POP3 and ACAP - * RFC2831 DIGEST-MD5 authentication - * RFC3501 IMAPv4 protocol - * RFC4422 Simple Authentication and Security Layer (SASL) - * RFC4616 PLAIN authentication - * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism - * RFC4959 IMAP Extension for SASL Initial Client Response - * RFC5092 IMAP URL Scheme - * RFC6749 OAuth 2.0 Authorization Framework - * RFC8314 Use of TLS for Email Submission and Access - * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt> - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifndef CURL_DISABLE_IMAP - -#ifdef HAVE_NETINET_IN_H -#include <netinet/in.h> -#endif -#ifdef HAVE_ARPA_INET_H -#include <arpa/inet.h> -#endif -#ifdef HAVE_UTSNAME_H -#include <sys/utsname.h> -#endif -#ifdef HAVE_NETDB_H -#include <netdb.h> -#endif -#ifdef __VMS -#include <in.h> -#include <inet.h> -#endif - -#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) -#undef in_addr_t -#define in_addr_t unsigned long -#endif - -#include <curl/curl.h> -#include "urldata.h" -#include "sendf.h" -#include "hostip.h" -#include "progress.h" -#include "transfer.h" -#include "escape.h" -#include "http.h" /* for HTTP proxy tunnel stuff */ -#include "socks.h" -#include "imap.h" -#include "mime.h" -#include "strtoofft.h" -#include "strcase.h" -#include "vtls/vtls.h" -#include "connect.h" -#include "select.h" -#include "multiif.h" -#include "url.h" -#include "strcase.h" -#include "bufref.h" -#include "curl_sasl.h" -#include "warnless.h" -#include "curl_ctype.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* Local API functions */ -static CURLcode imap_regular_transfer(struct Curl_easy *data, bool *done); -static CURLcode imap_do(struct Curl_easy *data, bool *done); -static CURLcode imap_done(struct Curl_easy *data, CURLcode status, - bool premature); -static CURLcode imap_connect(struct Curl_easy *data, bool *done); -static CURLcode imap_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead); -static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done); -static int imap_getsock(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t *socks); -static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done); -static CURLcode imap_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static char *imap_atom(const char *str, bool escape_only); -static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...); -static CURLcode imap_parse_url_options(struct connectdata *conn); -static CURLcode imap_parse_url_path(struct Curl_easy *data); -static CURLcode imap_parse_custom_request(struct Curl_easy *data); -static CURLcode imap_perform_authenticate(struct Curl_easy *data, - const char *mech, - const struct bufref *initresp); -static CURLcode imap_continue_authenticate(struct Curl_easy *data, - const char *mech, - const struct bufref *resp); -static CURLcode imap_cancel_authenticate(struct Curl_easy *data, - const char *mech); -static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out); - -/* - * IMAP protocol handler. - */ - -const struct Curl_handler Curl_handler_imap = { - "IMAP", /* scheme */ - imap_setup_connection, /* setup_connection */ - imap_do, /* do_it */ - imap_done, /* done */ - ZERO_NULL, /* do_more */ - imap_connect, /* connect_it */ - imap_multi_statemach, /* connecting */ - imap_doing, /* doing */ - imap_getsock, /* proto_getsock */ - imap_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - imap_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_IMAP, /* defport */ - CURLPROTO_IMAP, /* protocol */ - CURLPROTO_IMAP, /* family */ - PROTOPT_CLOSEACTION| /* flags */ - PROTOPT_URLOPTIONS -}; - -#ifdef USE_SSL -/* - * IMAPS protocol handler. - */ - -const struct Curl_handler Curl_handler_imaps = { - "IMAPS", /* scheme */ - imap_setup_connection, /* setup_connection */ - imap_do, /* do_it */ - imap_done, /* done */ - ZERO_NULL, /* do_more */ - imap_connect, /* connect_it */ - imap_multi_statemach, /* connecting */ - imap_doing, /* doing */ - imap_getsock, /* proto_getsock */ - imap_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - imap_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_IMAPS, /* defport */ - CURLPROTO_IMAPS, /* protocol */ - CURLPROTO_IMAP, /* family */ - PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ - PROTOPT_URLOPTIONS -}; -#endif - -#define IMAP_RESP_OK 1 -#define IMAP_RESP_NOT_OK 2 -#define IMAP_RESP_PREAUTH 3 - -/* SASL parameters for the imap protocol */ -static const struct SASLproto saslimap = { - "imap", /* The service name */ - imap_perform_authenticate, /* Send authentication command */ - imap_continue_authenticate, /* Send authentication continuation */ - imap_cancel_authenticate, /* Send authentication cancellation */ - imap_get_message, /* Get SASL response message */ - 0, /* No maximum initial response length */ - '+', /* Code received when continuation is expected */ - IMAP_RESP_OK, /* Code to receive upon authentication success */ - SASL_AUTH_DEFAULT, /* Default mechanisms */ - SASL_FLAG_BASE64 /* Configuration flags */ -}; - - -#ifdef USE_SSL -static void imap_to_imaps(struct connectdata *conn) -{ - /* Change the connection handler */ - conn->handler = &Curl_handler_imaps; - - /* Set the connection's upgraded to TLS flag */ - conn->bits.tls_upgraded = TRUE; -} -#else -#define imap_to_imaps(x) Curl_nop_stmt -#endif - -/*********************************************************************** - * - * imap_matchresp() - * - * Determines whether the untagged response is related to the specified - * command by checking if it is in format "* <command-name> ..." or - * "* <number> <command-name> ...". - * - * The "* " marker is assumed to have already been checked by the caller. - */ -static bool imap_matchresp(const char *line, size_t len, const char *cmd) -{ - const char *end = line + len; - size_t cmd_len = strlen(cmd); - - /* Skip the untagged response marker */ - line += 2; - - /* Do we have a number after the marker? */ - if(line < end && ISDIGIT(*line)) { - /* Skip the number */ - do - line++; - while(line < end && ISDIGIT(*line)); - - /* Do we have the space character? */ - if(line == end || *line != ' ') - return FALSE; - - line++; - } - - /* Does the command name match and is it followed by a space character or at - the end of line? */ - if(line + cmd_len <= end && strncasecompare(line, cmd, cmd_len) && - (line[cmd_len] == ' ' || line + cmd_len + 2 == end)) - return TRUE; - - return FALSE; -} - -/*********************************************************************** - * - * imap_endofresp() - * - * Checks whether the given string is a valid tagged, untagged or continuation - * response which can be processed by the response handler. - */ -static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn, - char *line, size_t len, int *resp) -{ - struct IMAP *imap = data->req.p.imap; - struct imap_conn *imapc = &conn->proto.imapc; - const char *id = imapc->resptag; - size_t id_len = strlen(id); - - /* Do we have a tagged command response? */ - if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') { - line += id_len + 1; - len -= id_len + 1; - - if(len >= 2 && !memcmp(line, "OK", 2)) - *resp = IMAP_RESP_OK; - else if(len >= 7 && !memcmp(line, "PREAUTH", 7)) - *resp = IMAP_RESP_PREAUTH; - else - *resp = IMAP_RESP_NOT_OK; - - return TRUE; - } - - /* Do we have an untagged command response? */ - if(len >= 2 && !memcmp("* ", line, 2)) { - switch(imapc->state) { - /* States which are interested in untagged responses */ - case IMAP_CAPABILITY: - if(!imap_matchresp(line, len, "CAPABILITY")) - return FALSE; - break; - - case IMAP_LIST: - if((!imap->custom && !imap_matchresp(line, len, "LIST")) || - (imap->custom && !imap_matchresp(line, len, imap->custom) && - (!strcasecompare(imap->custom, "STORE") || - !imap_matchresp(line, len, "FETCH")) && - !strcasecompare(imap->custom, "SELECT") && - !strcasecompare(imap->custom, "EXAMINE") && - !strcasecompare(imap->custom, "SEARCH") && - !strcasecompare(imap->custom, "EXPUNGE") && - !strcasecompare(imap->custom, "LSUB") && - !strcasecompare(imap->custom, "UID") && - !strcasecompare(imap->custom, "GETQUOTAROOT") && - !strcasecompare(imap->custom, "NOOP"))) - return FALSE; - break; - - case IMAP_SELECT: - /* SELECT is special in that its untagged responses do not have a - common prefix so accept anything! */ - break; - - case IMAP_FETCH: - if(!imap_matchresp(line, len, "FETCH")) - return FALSE; - break; - - case IMAP_SEARCH: - if(!imap_matchresp(line, len, "SEARCH")) - return FALSE; - break; - - /* Ignore other untagged responses */ - default: - return FALSE; - } - - *resp = '*'; - return TRUE; - } - - /* Do we have a continuation response? This should be a + symbol followed by - a space and optionally some text as per RFC-3501 for the AUTHENTICATE and - APPEND commands and as outlined in Section 4. Examples of RFC-4959 but - some email servers ignore this and only send a single + instead. */ - if(imap && !imap->custom && ((len == 3 && line[0] == '+') || - (len >= 2 && !memcmp("+ ", line, 2)))) { - switch(imapc->state) { - /* States which are interested in continuation responses */ - case IMAP_AUTHENTICATE: - case IMAP_APPEND: - *resp = '+'; - break; - - default: - failf(data, "Unexpected continuation response"); - *resp = -1; - break; - } - - return TRUE; - } - - return FALSE; /* Nothing for us */ -} - -/*********************************************************************** - * - * imap_get_message() - * - * Gets the authentication message from the response buffer. - */ -static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out) -{ - char *message = data->state.buffer; - size_t len = strlen(message); - - if(len > 2) { - /* Find the start of the message */ - len -= 2; - for(message += 2; *message == ' ' || *message == '\t'; message++, len--) - ; - - /* Find the end of the message */ - while(len--) - if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && - message[len] != '\t') - break; - - /* Terminate the message */ - message[++len] = '\0'; - Curl_bufref_set(out, message, len, NULL); - } - else - /* junk input => zero length output */ - Curl_bufref_set(out, "", 0, NULL); - - return CURLE_OK; -} - -/*********************************************************************** - * - * state() - * - * This is the ONLY way to change IMAP state! - */ -static void state(struct Curl_easy *data, imapstate newstate) -{ - struct imap_conn *imapc = &data->conn->proto.imapc; -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - /* for debug purposes */ - static const char * const names[]={ - "STOP", - "SERVERGREET", - "CAPABILITY", - "STARTTLS", - "UPGRADETLS", - "AUTHENTICATE", - "LOGIN", - "LIST", - "SELECT", - "FETCH", - "FETCH_FINAL", - "APPEND", - "APPEND_FINAL", - "SEARCH", - "LOGOUT", - /* LAST */ - }; - - if(imapc->state != newstate) - infof(data, "IMAP %p state change from %s to %s", - (void *)imapc, names[imapc->state], names[newstate]); -#endif - - imapc->state = newstate; -} - -/*********************************************************************** - * - * imap_perform_capability() - * - * Sends the CAPABILITY command in order to obtain a list of server side - * supported capabilities. - */ -static CURLcode imap_perform_capability(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - struct imap_conn *imapc = &conn->proto.imapc; - imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */ - imapc->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */ - imapc->tls_supported = FALSE; /* Clear the TLS capability */ - - /* Send the CAPABILITY command */ - result = imap_sendf(data, "CAPABILITY"); - - if(!result) - state(data, IMAP_CAPABILITY); - - return result; -} - -/*********************************************************************** - * - * imap_perform_starttls() - * - * Sends the STARTTLS command to start the upgrade to TLS. - */ -static CURLcode imap_perform_starttls(struct Curl_easy *data) -{ - /* Send the STARTTLS command */ - CURLcode result = imap_sendf(data, "STARTTLS"); - - if(!result) - state(data, IMAP_STARTTLS); - - return result; -} - -/*********************************************************************** - * - * imap_perform_upgrade_tls() - * - * Performs the upgrade to TLS. - */ -static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data, - struct connectdata *conn) -{ - /* Start the SSL connection */ - struct imap_conn *imapc = &conn->proto.imapc; - CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FALSE, - FIRSTSOCKET, &imapc->ssldone); - - if(!result) { - if(imapc->state != IMAP_UPGRADETLS) - state(data, IMAP_UPGRADETLS); - - if(imapc->ssldone) { - imap_to_imaps(conn); - result = imap_perform_capability(data, conn); - } - } - - return result; -} - -/*********************************************************************** - * - * imap_perform_login() - * - * Sends a clear text LOGIN command to authenticate with. - */ -static CURLcode imap_perform_login(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - char *user; - char *passwd; - - /* Check we have a username and password to authenticate with and end the - connect phase if we don't */ - if(!data->state.aptr.user) { - state(data, IMAP_STOP); - - return result; - } - - /* Make sure the username and password are in the correct atom format */ - user = imap_atom(conn->user, false); - passwd = imap_atom(conn->passwd, false); - - /* Send the LOGIN command */ - result = imap_sendf(data, "LOGIN %s %s", user ? user : "", - passwd ? passwd : ""); - - free(user); - free(passwd); - - if(!result) - state(data, IMAP_LOGIN); - - return result; -} - -/*********************************************************************** - * - * imap_perform_authenticate() - * - * Sends an AUTHENTICATE command allowing the client to login with the given - * SASL authentication mechanism. - */ -static CURLcode imap_perform_authenticate(struct Curl_easy *data, - const char *mech, - const struct bufref *initresp) -{ - CURLcode result = CURLE_OK; - const char *ir = (const char *) Curl_bufref_ptr(initresp); - - if(ir) { - /* Send the AUTHENTICATE command with the initial response */ - result = imap_sendf(data, "AUTHENTICATE %s %s", mech, ir); - } - else { - /* Send the AUTHENTICATE command */ - result = imap_sendf(data, "AUTHENTICATE %s", mech); - } - - return result; -} - -/*********************************************************************** - * - * imap_continue_authenticate() - * - * Sends SASL continuation data. - */ -static CURLcode imap_continue_authenticate(struct Curl_easy *data, - const char *mech, - const struct bufref *resp) -{ - struct imap_conn *imapc = &data->conn->proto.imapc; - - (void)mech; - - return Curl_pp_sendf(data, &imapc->pp, - "%s", (const char *) Curl_bufref_ptr(resp)); -} - -/*********************************************************************** - * - * imap_cancel_authenticate() - * - * Sends SASL cancellation. - */ -static CURLcode imap_cancel_authenticate(struct Curl_easy *data, - const char *mech) -{ - struct imap_conn *imapc = &data->conn->proto.imapc; - - (void)mech; - - return Curl_pp_sendf(data, &imapc->pp, "*"); -} - -/*********************************************************************** - * - * imap_perform_authentication() - * - * Initiates the authentication sequence, with the appropriate SASL - * authentication mechanism, falling back to clear text should a common - * mechanism not be available between the client and server. - */ -static CURLcode imap_perform_authentication(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - struct imap_conn *imapc = &conn->proto.imapc; - saslprogress progress; - - /* Check if already authenticated OR if there is enough data to authenticate - with and end the connect phase if we don't */ - if(imapc->preauth || - !Curl_sasl_can_authenticate(&imapc->sasl, data)) { - state(data, IMAP_STOP); - return result; - } - - /* Calculate the SASL login details */ - result = Curl_sasl_start(&imapc->sasl, data, imapc->ir_supported, &progress); - - if(!result) { - if(progress == SASL_INPROGRESS) - state(data, IMAP_AUTHENTICATE); - else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT)) - /* Perform clear text authentication */ - result = imap_perform_login(data, conn); - else { - /* Other mechanisms not supported */ - infof(data, "No known authentication mechanisms supported"); - result = CURLE_LOGIN_DENIED; - } - } - - return result; -} - -/*********************************************************************** - * - * imap_perform_list() - * - * Sends a LIST command or an alternative custom request. - */ -static CURLcode imap_perform_list(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct IMAP *imap = data->req.p.imap; - - if(imap->custom) - /* Send the custom request */ - result = imap_sendf(data, "%s%s", imap->custom, - imap->custom_params ? imap->custom_params : ""); - else { - /* Make sure the mailbox is in the correct atom format if necessary */ - char *mailbox = imap->mailbox ? imap_atom(imap->mailbox, true) - : strdup(""); - if(!mailbox) - return CURLE_OUT_OF_MEMORY; - - /* Send the LIST command */ - result = imap_sendf(data, "LIST \"%s\" *", mailbox); - - free(mailbox); - } - - if(!result) - state(data, IMAP_LIST); - - return result; -} - -/*********************************************************************** - * - * imap_perform_select() - * - * Sends a SELECT command to ask the server to change the selected mailbox. - */ -static CURLcode imap_perform_select(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct IMAP *imap = data->req.p.imap; - struct imap_conn *imapc = &conn->proto.imapc; - char *mailbox; - - /* Invalidate old information as we are switching mailboxes */ - Curl_safefree(imapc->mailbox); - Curl_safefree(imapc->mailbox_uidvalidity); - - /* Check we have a mailbox */ - if(!imap->mailbox) { - failf(data, "Cannot SELECT without a mailbox."); - return CURLE_URL_MALFORMAT; - } - - /* Make sure the mailbox is in the correct atom format */ - mailbox = imap_atom(imap->mailbox, false); - if(!mailbox) - return CURLE_OUT_OF_MEMORY; - - /* Send the SELECT command */ - result = imap_sendf(data, "SELECT %s", mailbox); - - free(mailbox); - - if(!result) - state(data, IMAP_SELECT); - - return result; -} - -/*********************************************************************** - * - * imap_perform_fetch() - * - * Sends a FETCH command to initiate the download of a message. - */ -static CURLcode imap_perform_fetch(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct IMAP *imap = data->req.p.imap; - /* Check we have a UID */ - if(imap->uid) { - - /* Send the FETCH command */ - if(imap->partial) - result = imap_sendf(data, "UID FETCH %s BODY[%s]<%s>", - imap->uid, imap->section ? imap->section : "", - imap->partial); - else - result = imap_sendf(data, "UID FETCH %s BODY[%s]", - imap->uid, imap->section ? imap->section : ""); - } - else if(imap->mindex) { - /* Send the FETCH command */ - if(imap->partial) - result = imap_sendf(data, "FETCH %s BODY[%s]<%s>", - imap->mindex, imap->section ? imap->section : "", - imap->partial); - else - result = imap_sendf(data, "FETCH %s BODY[%s]", - imap->mindex, imap->section ? imap->section : ""); - } - else { - failf(data, "Cannot FETCH without a UID."); - return CURLE_URL_MALFORMAT; - } - if(!result) - state(data, IMAP_FETCH); - - return result; -} - -/*********************************************************************** - * - * imap_perform_append() - * - * Sends an APPEND command to initiate the upload of a message. - */ -static CURLcode imap_perform_append(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct IMAP *imap = data->req.p.imap; - char *mailbox; - - /* Check we have a mailbox */ - if(!imap->mailbox) { - failf(data, "Cannot APPEND without a mailbox."); - return CURLE_URL_MALFORMAT; - } - - /* Prepare the mime data if some. */ - if(data->set.mimepost.kind != MIMEKIND_NONE) { - /* Use the whole structure as data. */ - data->set.mimepost.flags &= ~MIME_BODY_ONLY; - - /* Add external headers and mime version. */ - curl_mime_headers(&data->set.mimepost, data->set.headers, 0); - result = Curl_mime_prepare_headers(&data->set.mimepost, NULL, - NULL, MIMESTRATEGY_MAIL); - - if(!result) - if(!Curl_checkheaders(data, STRCONST("Mime-Version"))) - result = Curl_mime_add_header(&data->set.mimepost.curlheaders, - "Mime-Version: 1.0"); - - /* Make sure we will read the entire mime structure. */ - if(!result) - result = Curl_mime_rewind(&data->set.mimepost); - - if(result) - return result; - - data->state.infilesize = Curl_mime_size(&data->set.mimepost); - - /* Read from mime structure. */ - data->state.fread_func = (curl_read_callback) Curl_mime_read; - data->state.in = (void *) &data->set.mimepost; - } - - /* Check we know the size of the upload */ - if(data->state.infilesize < 0) { - failf(data, "Cannot APPEND with unknown input file size"); - return CURLE_UPLOAD_FAILED; - } - - /* Make sure the mailbox is in the correct atom format */ - mailbox = imap_atom(imap->mailbox, false); - if(!mailbox) - return CURLE_OUT_OF_MEMORY; - - /* Send the APPEND command */ - result = imap_sendf(data, - "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}", - mailbox, data->state.infilesize); - - free(mailbox); - - if(!result) - state(data, IMAP_APPEND); - - return result; -} - -/*********************************************************************** - * - * imap_perform_search() - * - * Sends a SEARCH command. - */ -static CURLcode imap_perform_search(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct IMAP *imap = data->req.p.imap; - - /* Check we have a query string */ - if(!imap->query) { - failf(data, "Cannot SEARCH without a query string."); - return CURLE_URL_MALFORMAT; - } - - /* Send the SEARCH command */ - result = imap_sendf(data, "SEARCH %s", imap->query); - - if(!result) - state(data, IMAP_SEARCH); - - return result; -} - -/*********************************************************************** - * - * imap_perform_logout() - * - * Performs the logout action prior to sclose() being called. - */ -static CURLcode imap_perform_logout(struct Curl_easy *data) -{ - /* Send the LOGOUT command */ - CURLcode result = imap_sendf(data, "LOGOUT"); - - if(!result) - state(data, IMAP_LOGOUT); - - return result; -} - -/* For the initial server greeting */ -static CURLcode imap_state_servergreet_resp(struct Curl_easy *data, - int imapcode, - imapstate instate) -{ - struct connectdata *conn = data->conn; - (void)instate; /* no use for this yet */ - - if(imapcode == IMAP_RESP_PREAUTH) { - /* PREAUTH */ - struct imap_conn *imapc = &conn->proto.imapc; - imapc->preauth = TRUE; - infof(data, "PREAUTH connection, already authenticated"); - } - else if(imapcode != IMAP_RESP_OK) { - failf(data, "Got unexpected imap-server response"); - return CURLE_WEIRD_SERVER_REPLY; - } - - return imap_perform_capability(data, conn); -} - -/* For CAPABILITY responses */ -static CURLcode imap_state_capability_resp(struct Curl_easy *data, - int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct imap_conn *imapc = &conn->proto.imapc; - const char *line = data->state.buffer; - - (void)instate; /* no use for this yet */ - - /* Do we have a untagged response? */ - if(imapcode == '*') { - line += 2; - - /* Loop through the data line */ - for(;;) { - size_t wordlen; - while(*line && - (*line == ' ' || *line == '\t' || - *line == '\r' || *line == '\n')) { - - line++; - } - - if(!*line) - break; - - /* Extract the word */ - for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' && - line[wordlen] != '\t' && line[wordlen] != '\r' && - line[wordlen] != '\n';) - wordlen++; - - /* Does the server support the STARTTLS capability? */ - if(wordlen == 8 && !memcmp(line, "STARTTLS", 8)) - imapc->tls_supported = TRUE; - - /* Has the server explicitly disabled clear text authentication? */ - else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13)) - imapc->login_disabled = TRUE; - - /* Does the server support the SASL-IR capability? */ - else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7)) - imapc->ir_supported = TRUE; - - /* Do we have a SASL based authentication mechanism? */ - else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) { - size_t llen; - unsigned short mechbit; - - line += 5; - wordlen -= 5; - - /* Test the word for a matching authentication mechanism */ - mechbit = Curl_sasl_decode_mech(line, wordlen, &llen); - if(mechbit && llen == wordlen) - imapc->sasl.authmechs |= mechbit; - } - - line += wordlen; - } - } - else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { - /* PREAUTH is not compatible with STARTTLS. */ - if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) { - /* Switch to TLS connection now */ - result = imap_perform_starttls(data); - } - else if(data->set.use_ssl <= CURLUSESSL_TRY) - result = imap_perform_authentication(data, conn); - else { - failf(data, "STARTTLS not available."); - result = CURLE_USE_SSL_FAILED; - } - } - else - result = imap_perform_authentication(data, conn); - - return result; -} - -/* For STARTTLS responses */ -static CURLcode imap_state_starttls_resp(struct Curl_easy *data, - int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - - (void)instate; /* no use for this yet */ - - /* Pipelining in response is forbidden. */ - if(data->conn->proto.imapc.pp.cache_size) - return CURLE_WEIRD_SERVER_REPLY; - - if(imapcode != IMAP_RESP_OK) { - if(data->set.use_ssl != CURLUSESSL_TRY) { - failf(data, "STARTTLS denied"); - result = CURLE_USE_SSL_FAILED; - } - else - result = imap_perform_authentication(data, conn); - } - else - result = imap_perform_upgrade_tls(data, conn); - - return result; -} - -/* For SASL authentication responses */ -static CURLcode imap_state_auth_resp(struct Curl_easy *data, - struct connectdata *conn, - int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - struct imap_conn *imapc = &conn->proto.imapc; - saslprogress progress; - - (void)instate; /* no use for this yet */ - - result = Curl_sasl_continue(&imapc->sasl, data, imapcode, &progress); - if(!result) - switch(progress) { - case SASL_DONE: - state(data, IMAP_STOP); /* Authenticated */ - break; - case SASL_IDLE: /* No mechanism left after cancellation */ - if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT)) - /* Perform clear text authentication */ - result = imap_perform_login(data, conn); - else { - failf(data, "Authentication cancelled"); - result = CURLE_LOGIN_DENIED; - } - break; - default: - break; - } - - return result; -} - -/* For LOGIN responses */ -static CURLcode imap_state_login_resp(struct Curl_easy *data, - int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - (void)instate; /* no use for this yet */ - - if(imapcode != IMAP_RESP_OK) { - failf(data, "Access denied. %c", imapcode); - result = CURLE_LOGIN_DENIED; - } - else - /* End of connect phase */ - state(data, IMAP_STOP); - - return result; -} - -/* For LIST and SEARCH responses */ -static CURLcode imap_state_listsearch_resp(struct Curl_easy *data, - int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - char *line = data->state.buffer; - size_t len = strlen(line); - - (void)instate; /* No use for this yet */ - - if(imapcode == '*') { - /* Temporarily add the LF character back and send as body to the client */ - line[len] = '\n'; - result = Curl_client_write(data, CLIENTWRITE_BODY, line, len + 1); - line[len] = '\0'; - } - else if(imapcode != IMAP_RESP_OK) - result = CURLE_QUOTE_ERROR; - else - /* End of DO phase */ - state(data, IMAP_STOP); - - return result; -} - -/* For SELECT responses */ -static CURLcode imap_state_select_resp(struct Curl_easy *data, int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct IMAP *imap = data->req.p.imap; - struct imap_conn *imapc = &conn->proto.imapc; - const char *line = data->state.buffer; - - (void)instate; /* no use for this yet */ - - if(imapcode == '*') { - /* See if this is an UIDVALIDITY response */ - char tmp[20]; - if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) { - Curl_safefree(imapc->mailbox_uidvalidity); - imapc->mailbox_uidvalidity = strdup(tmp); - } - } - else if(imapcode == IMAP_RESP_OK) { - /* Check if the UIDVALIDITY has been specified and matches */ - if(imap->uidvalidity && imapc->mailbox_uidvalidity && - !strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)) { - failf(data, "Mailbox UIDVALIDITY has changed"); - result = CURLE_REMOTE_FILE_NOT_FOUND; - } - else { - /* Note the currently opened mailbox on this connection */ - imapc->mailbox = strdup(imap->mailbox); - - if(imap->custom) - result = imap_perform_list(data); - else if(imap->query) - result = imap_perform_search(data); - else - result = imap_perform_fetch(data); - } - } - else { - failf(data, "Select failed"); - result = CURLE_LOGIN_DENIED; - } - - return result; -} - -/* For the (first line of the) FETCH responses */ -static CURLcode imap_state_fetch_resp(struct Curl_easy *data, - struct connectdata *conn, int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - struct imap_conn *imapc = &conn->proto.imapc; - struct pingpong *pp = &imapc->pp; - const char *ptr = data->state.buffer; - bool parsed = FALSE; - curl_off_t size = 0; - - (void)instate; /* no use for this yet */ - - if(imapcode != '*') { - Curl_pgrsSetDownloadSize(data, -1); - state(data, IMAP_STOP); - return CURLE_REMOTE_FILE_NOT_FOUND; - } - - /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse - the continuation data contained within the curly brackets */ - while(*ptr && (*ptr != '{')) - ptr++; - - if(*ptr == '{') { - char *endptr; - if(!curlx_strtoofft(ptr + 1, &endptr, 10, &size)) { - if(endptr - ptr > 1 && endptr[0] == '}' && - endptr[1] == '\r' && endptr[2] == '\0') - parsed = TRUE; - } - } - - if(parsed) { - infof(data, "Found %" CURL_FORMAT_CURL_OFF_T " bytes to download", - size); - Curl_pgrsSetDownloadSize(data, size); - - if(pp->cache) { - /* At this point there is a bunch of data in the header "cache" that is - actually body content, send it as body and then skip it. Do note - that there may even be additional "headers" after the body. */ - size_t chunk = pp->cache_size; - - if(chunk > (size_t)size) - /* The conversion from curl_off_t to size_t is always fine here */ - chunk = (size_t)size; - - if(!chunk) { - /* no size, we're done with the data */ - state(data, IMAP_STOP); - return CURLE_OK; - } - result = Curl_client_write(data, CLIENTWRITE_BODY, pp->cache, chunk); - if(result) - return result; - - data->req.bytecount += chunk; - - infof(data, "Written %zu bytes, %" CURL_FORMAT_CURL_OFF_TU - " bytes are left for transfer", chunk, size - chunk); - - /* Have we used the entire cache or just part of it?*/ - if(pp->cache_size > chunk) { - /* Only part of it so shrink the cache to fit the trailing data */ - memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk); - pp->cache_size -= chunk; - } - else { - /* Free the cache */ - Curl_safefree(pp->cache); - - /* Reset the cache size */ - pp->cache_size = 0; - } - } - - if(data->req.bytecount == size) - /* The entire data is already transferred! */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); - else { - /* IMAP download */ - data->req.maxdownload = size; - /* force a recv/send check of this connection, as the data might've been - read off the socket already */ - data->conn->cselect_bits = CURL_CSELECT_IN; - Curl_setup_transfer(data, FIRSTSOCKET, size, FALSE, -1); - } - } - else { - /* We don't know how to parse this line */ - failf(data, "Failed to parse FETCH response."); - result = CURLE_WEIRD_SERVER_REPLY; - } - - /* End of DO phase */ - state(data, IMAP_STOP); - - return result; -} - -/* For final FETCH responses performed after the download */ -static CURLcode imap_state_fetch_final_resp(struct Curl_easy *data, - int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - - (void)instate; /* No use for this yet */ - - if(imapcode != IMAP_RESP_OK) - result = CURLE_WEIRD_SERVER_REPLY; - else - /* End of DONE phase */ - state(data, IMAP_STOP); - - return result; -} - -/* For APPEND responses */ -static CURLcode imap_state_append_resp(struct Curl_easy *data, int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - (void)instate; /* No use for this yet */ - - if(imapcode != '+') { - result = CURLE_UPLOAD_FAILED; - } - else { - /* Set the progress upload size */ - Curl_pgrsSetUploadSize(data, data->state.infilesize); - - /* IMAP upload */ - Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); - - /* End of DO phase */ - state(data, IMAP_STOP); - } - - return result; -} - -/* For final APPEND responses performed after the upload */ -static CURLcode imap_state_append_final_resp(struct Curl_easy *data, - int imapcode, - imapstate instate) -{ - CURLcode result = CURLE_OK; - - (void)instate; /* No use for this yet */ - - if(imapcode != IMAP_RESP_OK) - result = CURLE_UPLOAD_FAILED; - else - /* End of DONE phase */ - state(data, IMAP_STOP); - - return result; -} - -static CURLcode imap_statemachine(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; - int imapcode; - struct imap_conn *imapc = &conn->proto.imapc; - struct pingpong *pp = &imapc->pp; - size_t nread = 0; - (void)data; - - /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */ - if(imapc->state == IMAP_UPGRADETLS) - return imap_perform_upgrade_tls(data, conn); - - /* Flush any data that needs to be sent */ - if(pp->sendleft) - return Curl_pp_flushsend(data, pp); - - do { - /* Read the response from the server */ - result = Curl_pp_readresp(data, sock, pp, &imapcode, &nread); - if(result) - return result; - - /* Was there an error parsing the response line? */ - if(imapcode == -1) - return CURLE_WEIRD_SERVER_REPLY; - - if(!imapcode) - break; - - /* We have now received a full IMAP server response */ - switch(imapc->state) { - case IMAP_SERVERGREET: - result = imap_state_servergreet_resp(data, imapcode, imapc->state); - break; - - case IMAP_CAPABILITY: - result = imap_state_capability_resp(data, imapcode, imapc->state); - break; - - case IMAP_STARTTLS: - result = imap_state_starttls_resp(data, imapcode, imapc->state); - break; - - case IMAP_AUTHENTICATE: - result = imap_state_auth_resp(data, conn, imapcode, imapc->state); - break; - - case IMAP_LOGIN: - result = imap_state_login_resp(data, imapcode, imapc->state); - break; - - case IMAP_LIST: - case IMAP_SEARCH: - result = imap_state_listsearch_resp(data, imapcode, imapc->state); - break; - - case IMAP_SELECT: - result = imap_state_select_resp(data, imapcode, imapc->state); - break; - - case IMAP_FETCH: - result = imap_state_fetch_resp(data, conn, imapcode, imapc->state); - break; - - case IMAP_FETCH_FINAL: - result = imap_state_fetch_final_resp(data, imapcode, imapc->state); - break; - - case IMAP_APPEND: - result = imap_state_append_resp(data, imapcode, imapc->state); - break; - - case IMAP_APPEND_FINAL: - result = imap_state_append_final_resp(data, imapcode, imapc->state); - break; - - case IMAP_LOGOUT: - /* fallthrough, just stop! */ - default: - /* internal error */ - state(data, IMAP_STOP); - break; - } - } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp)); - - return result; -} - -/* Called repeatedly until done from multi.c */ -static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct imap_conn *imapc = &conn->proto.imapc; - - if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) { - result = Curl_ssl_connect_nonblocking(data, conn, FALSE, - FIRSTSOCKET, &imapc->ssldone); - if(result || !imapc->ssldone) - return result; - } - - result = Curl_pp_statemach(data, &imapc->pp, FALSE, FALSE); - *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE; - - return result; -} - -static CURLcode imap_block_statemach(struct Curl_easy *data, - struct connectdata *conn, - bool disconnecting) -{ - CURLcode result = CURLE_OK; - struct imap_conn *imapc = &conn->proto.imapc; - - while(imapc->state != IMAP_STOP && !result) - result = Curl_pp_statemach(data, &imapc->pp, TRUE, disconnecting); - - return result; -} - -/* Allocate and initialize the struct IMAP for the current Curl_easy if - required */ -static CURLcode imap_init(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct IMAP *imap; - - imap = data->req.p.imap = calloc(sizeof(struct IMAP), 1); - if(!imap) - result = CURLE_OUT_OF_MEMORY; - - return result; -} - -/* For the IMAP "protocol connect" and "doing" phases only */ -static int imap_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *socks) -{ - return Curl_pp_getsock(data, &conn->proto.imapc.pp, socks); -} - -/*********************************************************************** - * - * imap_connect() - * - * This function should do everything that is to be considered a part of the - * connection phase. - * - * The variable 'done' points to will be TRUE if the protocol-layer connect - * phase is done when this function returns, or FALSE if not. - */ -static CURLcode imap_connect(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct imap_conn *imapc = &conn->proto.imapc; - struct pingpong *pp = &imapc->pp; - - *done = FALSE; /* default to not done yet */ - - /* We always support persistent connections in IMAP */ - connkeep(conn, "IMAP default"); - - PINGPONG_SETUP(pp, imap_statemachine, imap_endofresp); - - /* Set the default preferred authentication type and mechanism */ - imapc->preftype = IMAP_TYPE_ANY; - Curl_sasl_init(&imapc->sasl, data, &saslimap); - - Curl_dyn_init(&imapc->dyn, DYN_IMAP_CMD); - /* Initialise the pingpong layer */ - Curl_pp_setup(pp); - Curl_pp_init(data, pp); - - /* Parse the URL options */ - result = imap_parse_url_options(conn); - if(result) - return result; - - /* Start off waiting for the server greeting response */ - state(data, IMAP_SERVERGREET); - - /* Start off with an response id of '*' */ - strcpy(imapc->resptag, "*"); - - result = imap_multi_statemach(data, done); - - return result; -} - -/*********************************************************************** - * - * imap_done() - * - * The DONE function. This does what needs to be done after a single DO has - * performed. - * - * Input argument is already checked for validity. - */ -static CURLcode imap_done(struct Curl_easy *data, CURLcode status, - bool premature) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct IMAP *imap = data->req.p.imap; - - (void)premature; - - if(!imap) - return CURLE_OK; - - if(status) { - connclose(conn, "IMAP done with bad status"); /* marked for closure */ - result = status; /* use the already set error code */ - } - else if(!data->set.connect_only && !imap->custom && - (imap->uid || imap->mindex || data->set.upload || - data->set.mimepost.kind != MIMEKIND_NONE)) { - /* Handle responses after FETCH or APPEND transfer has finished */ - - if(!data->set.upload && data->set.mimepost.kind == MIMEKIND_NONE) - state(data, IMAP_FETCH_FINAL); - else { - /* End the APPEND command first by sending an empty line */ - result = Curl_pp_sendf(data, &conn->proto.imapc.pp, "%s", ""); - if(!result) - state(data, IMAP_APPEND_FINAL); - } - - /* Run the state-machine */ - if(!result) - result = imap_block_statemach(data, conn, FALSE); - } - - /* Cleanup our per-request based variables */ - Curl_safefree(imap->mailbox); - Curl_safefree(imap->uidvalidity); - Curl_safefree(imap->uid); - Curl_safefree(imap->mindex); - Curl_safefree(imap->section); - Curl_safefree(imap->partial); - Curl_safefree(imap->query); - Curl_safefree(imap->custom); - Curl_safefree(imap->custom_params); - - /* Clear the transfer mode for the next request */ - imap->transfer = PPTRANSFER_BODY; - - return result; -} - -/*********************************************************************** - * - * imap_perform() - * - * This is the actual DO function for IMAP. Fetch or append a message, or do - * other things according to the options previously setup. - */ -static CURLcode imap_perform(struct Curl_easy *data, bool *connected, - bool *dophase_done) -{ - /* This is IMAP and no proxy */ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct IMAP *imap = data->req.p.imap; - struct imap_conn *imapc = &conn->proto.imapc; - bool selected = FALSE; - - DEBUGF(infof(data, "DO phase starts")); - - if(data->set.opt_no_body) { - /* Requested no body means no transfer */ - imap->transfer = PPTRANSFER_INFO; - } - - *dophase_done = FALSE; /* not done yet */ - - /* Determine if the requested mailbox (with the same UIDVALIDITY if set) - has already been selected on this connection */ - if(imap->mailbox && imapc->mailbox && - strcasecompare(imap->mailbox, imapc->mailbox) && - (!imap->uidvalidity || !imapc->mailbox_uidvalidity || - strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity))) - selected = TRUE; - - /* Start the first command in the DO phase */ - if(data->set.upload || data->set.mimepost.kind != MIMEKIND_NONE) - /* APPEND can be executed directly */ - result = imap_perform_append(data); - else if(imap->custom && (selected || !imap->mailbox)) - /* Custom command using the same mailbox or no mailbox */ - result = imap_perform_list(data); - else if(!imap->custom && selected && (imap->uid || imap->mindex)) - /* FETCH from the same mailbox */ - result = imap_perform_fetch(data); - else if(!imap->custom && selected && imap->query) - /* SEARCH the current mailbox */ - result = imap_perform_search(data); - else if(imap->mailbox && !selected && - (imap->custom || imap->uid || imap->mindex || imap->query)) - /* SELECT the mailbox */ - result = imap_perform_select(data); - else - /* LIST */ - result = imap_perform_list(data); - - if(result) - return result; - - /* Run the state-machine */ - result = imap_multi_statemach(data, dophase_done); - - *connected = conn->bits.tcpconnect[FIRSTSOCKET]; - - if(*dophase_done) - DEBUGF(infof(data, "DO phase is complete")); - - return result; -} - -/*********************************************************************** - * - * imap_do() - * - * This function is registered as 'curl_do' function. It decodes the path - * parts etc as a wrapper to the actual DO function (imap_perform). - * - * The input argument is already checked for validity. - */ -static CURLcode imap_do(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - *done = FALSE; /* default to false */ - - /* Parse the URL path */ - result = imap_parse_url_path(data); - if(result) - return result; - - /* Parse the custom request */ - result = imap_parse_custom_request(data); - if(result) - return result; - - result = imap_regular_transfer(data, done); - - return result; -} - -/*********************************************************************** - * - * imap_disconnect() - * - * Disconnect from an IMAP server. Cleanup protocol-specific per-connection - * resources. BLOCKING. - */ -static CURLcode imap_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead_connection) -{ - struct imap_conn *imapc = &conn->proto.imapc; - (void)data; - - /* We cannot send quit unconditionally. If this connection is stale or - bad in any way, sending quit and waiting around here will make the - disconnect wait in vain and cause more problems than we need to. */ - - /* The IMAP session may or may not have been allocated/setup at this - point! */ - if(!dead_connection && conn->bits.protoconnstart) { - if(!imap_perform_logout(data)) - (void)imap_block_statemach(data, conn, TRUE); /* ignore errors */ - } - - /* Disconnect from the server */ - Curl_pp_disconnect(&imapc->pp); - Curl_dyn_free(&imapc->dyn); - - /* Cleanup the SASL module */ - Curl_sasl_cleanup(conn, imapc->sasl.authused); - - /* Cleanup our connection based variables */ - Curl_safefree(imapc->mailbox); - Curl_safefree(imapc->mailbox_uidvalidity); - - return CURLE_OK; -} - -/* Call this when the DO phase has completed */ -static CURLcode imap_dophase_done(struct Curl_easy *data, bool connected) -{ - struct IMAP *imap = data->req.p.imap; - - (void)connected; - - if(imap->transfer != PPTRANSFER_BODY) - /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); - - return CURLE_OK; -} - -/* Called from multi.c while DOing */ -static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done) -{ - CURLcode result = imap_multi_statemach(data, dophase_done); - - if(result) - DEBUGF(infof(data, "DO phase failed")); - else if(*dophase_done) { - result = imap_dophase_done(data, FALSE /* not connected */); - - DEBUGF(infof(data, "DO phase is complete")); - } - - return result; -} - -/*********************************************************************** - * - * imap_regular_transfer() - * - * The input argument is already checked for validity. - * - * Performs all commands done before a regular transfer between a local and a - * remote host. - */ -static CURLcode imap_regular_transfer(struct Curl_easy *data, - bool *dophase_done) -{ - CURLcode result = CURLE_OK; - bool connected = FALSE; - - /* Make sure size is unknown at this point */ - data->req.size = -1; - - /* Set the progress data */ - Curl_pgrsSetUploadCounter(data, 0); - Curl_pgrsSetDownloadCounter(data, 0); - Curl_pgrsSetUploadSize(data, -1); - Curl_pgrsSetDownloadSize(data, -1); - - /* Carry out the perform */ - result = imap_perform(data, &connected, dophase_done); - - /* Perform post DO phase operations if necessary */ - if(!result && *dophase_done) - result = imap_dophase_done(data, connected); - - return result; -} - -static CURLcode imap_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - /* Initialise the IMAP layer */ - CURLcode result = imap_init(data); - if(result) - return result; - - /* Clear the TLS upgraded flag */ - conn->bits.tls_upgraded = FALSE; - - return CURLE_OK; -} - -/*********************************************************************** - * - * imap_sendf() - * - * Sends the formatted string as an IMAP command to the server. - * - * Designed to never block. - */ -static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...) -{ - CURLcode result = CURLE_OK; - struct imap_conn *imapc = &data->conn->proto.imapc; - - DEBUGASSERT(fmt); - - /* Calculate the tag based on the connection ID and command ID */ - msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d", - 'A' + curlx_sltosi(data->conn->connection_id % 26), - (++imapc->cmdid)%1000); - - /* start with a blank buffer */ - Curl_dyn_reset(&imapc->dyn); - - /* append tag + space + fmt */ - result = Curl_dyn_addf(&imapc->dyn, "%s %s", imapc->resptag, fmt); - if(!result) { - va_list ap; - va_start(ap, fmt); - result = Curl_pp_vsendf(data, &imapc->pp, Curl_dyn_ptr(&imapc->dyn), ap); - va_end(ap); - } - return result; -} - -/*********************************************************************** - * - * imap_atom() - * - * Checks the input string for characters that need escaping and returns an - * atom ready for sending to the server. - * - * The returned string needs to be freed. - * - */ -static char *imap_atom(const char *str, bool escape_only) -{ - /* !checksrc! disable PARENBRACE 1 */ - const char atom_specials[] = "(){ %*]"; - const char *p1; - char *p2; - size_t backsp_count = 0; - size_t quote_count = 0; - bool others_exists = FALSE; - size_t newlen = 0; - char *newstr = NULL; - - if(!str) - return NULL; - - /* Look for "atom-specials", counting the backslash and quote characters as - these will need escaping */ - p1 = str; - while(*p1) { - if(*p1 == '\\') - backsp_count++; - else if(*p1 == '"') - quote_count++; - else if(!escape_only) { - const char *p3 = atom_specials; - - while(*p3 && !others_exists) { - if(*p1 == *p3) - others_exists = TRUE; - - p3++; - } - } - - p1++; - } - - /* Does the input contain any "atom-special" characters? */ - if(!backsp_count && !quote_count && !others_exists) - return strdup(str); - - /* Calculate the new string length */ - newlen = strlen(str) + backsp_count + quote_count + (escape_only ? 0 : 2); - - /* Allocate the new string */ - newstr = (char *) malloc((newlen + 1) * sizeof(char)); - if(!newstr) - return NULL; - - /* Surround the string in quotes if necessary */ - p2 = newstr; - if(!escape_only) { - newstr[0] = '"'; - newstr[newlen - 1] = '"'; - p2++; - } - - /* Copy the string, escaping backslash and quote characters along the way */ - p1 = str; - while(*p1) { - if(*p1 == '\\' || *p1 == '"') { - *p2 = '\\'; - p2++; - } - - *p2 = *p1; - - p1++; - p2++; - } - - /* Terminate the string */ - newstr[newlen] = '\0'; - - return newstr; -} - -/*********************************************************************** - * - * imap_is_bchar() - * - * Portable test of whether the specified char is a "bchar" as defined in the - * grammar of RFC-5092. - */ -static bool imap_is_bchar(char ch) -{ - /* Performing the alnum check with this macro is faster because of ASCII - arithmetic */ - if(ISALNUM(ch)) - return true; - - switch(ch) { - /* bchar */ - case ':': case '@': case '/': - /* bchar -> achar */ - case '&': case '=': - /* bchar -> achar -> uchar -> unreserved (without alphanumeric) */ - case '-': case '.': case '_': case '~': - /* bchar -> achar -> uchar -> sub-delims-sh */ - case '!': case '$': case '\'': case '(': case ')': case '*': - case '+': case ',': - /* bchar -> achar -> uchar -> pct-encoded */ - case '%': /* HEXDIG chars are already included above */ - return true; - - default: - return false; - } -} - -/*********************************************************************** - * - * imap_parse_url_options() - * - * Parse the URL login options. - */ -static CURLcode imap_parse_url_options(struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - struct imap_conn *imapc = &conn->proto.imapc; - const char *ptr = conn->options; - - while(!result && ptr && *ptr) { - const char *key = ptr; - const char *value; - - while(*ptr && *ptr != '=') - ptr++; - - value = ptr + 1; - - while(*ptr && *ptr != ';') - ptr++; - - if(strncasecompare(key, "AUTH=", 5)) - result = Curl_sasl_parse_url_auth_option(&imapc->sasl, - value, ptr - value); - else - result = CURLE_URL_MALFORMAT; - - if(*ptr == ';') - ptr++; - } - - switch(imapc->sasl.prefmech) { - case SASL_AUTH_NONE: - imapc->preftype = IMAP_TYPE_NONE; - break; - case SASL_AUTH_DEFAULT: - imapc->preftype = IMAP_TYPE_ANY; - break; - default: - imapc->preftype = IMAP_TYPE_SASL; - break; - } - - return result; -} - -/*********************************************************************** - * - * imap_parse_url_path() - * - * Parse the URL path into separate path components. - * - */ -static CURLcode imap_parse_url_path(struct Curl_easy *data) -{ - /* The imap struct is already initialised in imap_connect() */ - CURLcode result = CURLE_OK; - struct IMAP *imap = data->req.p.imap; - const char *begin = &data->state.up.path[1]; /* skip leading slash */ - const char *ptr = begin; - - /* See how much of the URL is a valid path and decode it */ - while(imap_is_bchar(*ptr)) - ptr++; - - if(ptr != begin) { - /* Remove the trailing slash if present */ - const char *end = ptr; - if(end > begin && end[-1] == '/') - end--; - - result = Curl_urldecode(begin, end - begin, &imap->mailbox, NULL, - REJECT_CTRL); - if(result) - return result; - } - else - imap->mailbox = NULL; - - /* There can be any number of parameters in the form ";NAME=VALUE" */ - while(*ptr == ';') { - char *name; - char *value; - size_t valuelen; - - /* Find the length of the name parameter */ - begin = ++ptr; - while(*ptr && *ptr != '=') - ptr++; - - if(!*ptr) - return CURLE_URL_MALFORMAT; - - /* Decode the name parameter */ - result = Curl_urldecode(begin, ptr - begin, &name, NULL, - REJECT_CTRL); - if(result) - return result; - - /* Find the length of the value parameter */ - begin = ++ptr; - while(imap_is_bchar(*ptr)) - ptr++; - - /* Decode the value parameter */ - result = Curl_urldecode(begin, ptr - begin, &value, &valuelen, - REJECT_CTRL); - if(result) { - free(name); - return result; - } - - DEBUGF(infof(data, "IMAP URL parameter '%s' = '%s'", name, value)); - - /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and - PARTIAL) stripping of the trailing slash character if it is present. - - Note: Unknown parameters trigger a URL_MALFORMAT error. */ - if(strcasecompare(name, "UIDVALIDITY") && !imap->uidvalidity) { - if(valuelen > 0 && value[valuelen - 1] == '/') - value[valuelen - 1] = '\0'; - - imap->uidvalidity = value; - value = NULL; - } - else if(strcasecompare(name, "UID") && !imap->uid) { - if(valuelen > 0 && value[valuelen - 1] == '/') - value[valuelen - 1] = '\0'; - - imap->uid = value; - value = NULL; - } - else if(strcasecompare(name, "MAILINDEX") && !imap->mindex) { - if(valuelen > 0 && value[valuelen - 1] == '/') - value[valuelen - 1] = '\0'; - - imap->mindex = value; - value = NULL; - } - else if(strcasecompare(name, "SECTION") && !imap->section) { - if(valuelen > 0 && value[valuelen - 1] == '/') - value[valuelen - 1] = '\0'; - - imap->section = value; - value = NULL; - } - else if(strcasecompare(name, "PARTIAL") && !imap->partial) { - if(valuelen > 0 && value[valuelen - 1] == '/') - value[valuelen - 1] = '\0'; - - imap->partial = value; - value = NULL; - } - else { - free(name); - free(value); - - return CURLE_URL_MALFORMAT; - } - - free(name); - free(value); - } - - /* Does the URL contain a query parameter? Only valid when we have a mailbox - and no UID as per RFC-5092 */ - if(imap->mailbox && !imap->uid && !imap->mindex) { - /* Get the query parameter, URL decoded */ - (void)curl_url_get(data->state.uh, CURLUPART_QUERY, &imap->query, - CURLU_URLDECODE); - } - - /* Any extra stuff at the end of the URL is an error */ - if(*ptr) - return CURLE_URL_MALFORMAT; - - return CURLE_OK; -} - -/*********************************************************************** - * - * imap_parse_custom_request() - * - * Parse the custom request. - */ -static CURLcode imap_parse_custom_request(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct IMAP *imap = data->req.p.imap; - const char *custom = data->set.str[STRING_CUSTOMREQUEST]; - - if(custom) { - /* URL decode the custom request */ - result = Curl_urldecode(custom, 0, &imap->custom, NULL, REJECT_CTRL); - - /* Extract the parameters if specified */ - if(!result) { - const char *params = imap->custom; - - while(*params && *params != ' ') - params++; - - if(*params) { - imap->custom_params = strdup(params); - imap->custom[params - imap->custom] = '\0'; - - if(!imap->custom_params) - result = CURLE_OUT_OF_MEMORY; - } - } - } - - return result; -} - -#endif /* CURL_DISABLE_IMAP */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ * RFC2595 Using TLS with IMAP, POP3 and ACAP
+ * RFC2831 DIGEST-MD5 authentication
+ * RFC3501 IMAPv4 protocol
+ * RFC4422 Simple Authentication and Security Layer (SASL)
+ * RFC4616 PLAIN authentication
+ * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
+ * RFC4959 IMAP Extension for SASL Initial Client Response
+ * RFC5092 IMAP URL Scheme
+ * RFC6749 OAuth 2.0 Authorization Framework
+ * RFC8314 Use of TLS for Email Submission and Access
+ * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_IMAP
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#include <curl/curl.h>
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "progress.h"
+#include "transfer.h"
+#include "escape.h"
+#include "http.h" /* for HTTP proxy tunnel stuff */
+#include "socks.h"
+#include "imap.h"
+#include "mime.h"
+#include "strtoofft.h"
+#include "strcase.h"
+#include "vtls/vtls.h"
+#include "cfilters.h"
+#include "connect.h"
+#include "select.h"
+#include "multiif.h"
+#include "url.h"
+#include "bufref.h"
+#include "curl_sasl.h"
+#include "warnless.h"
+#include "curl_ctype.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* Local API functions */
+static CURLcode imap_regular_transfer(struct Curl_easy *data, bool *done);
+static CURLcode imap_do(struct Curl_easy *data, bool *done);
+static CURLcode imap_done(struct Curl_easy *data, CURLcode status,
+ bool premature);
+static CURLcode imap_connect(struct Curl_easy *data, bool *done);
+static CURLcode imap_disconnect(struct Curl_easy *data,
+ struct connectdata *conn, bool dead);
+static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done);
+static int imap_getsock(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t *socks);
+static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done);
+static CURLcode imap_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn);
+static char *imap_atom(const char *str, bool escape_only);
+static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...);
+static CURLcode imap_parse_url_options(struct connectdata *conn);
+static CURLcode imap_parse_url_path(struct Curl_easy *data);
+static CURLcode imap_parse_custom_request(struct Curl_easy *data);
+static CURLcode imap_perform_authenticate(struct Curl_easy *data,
+ const char *mech,
+ const struct bufref *initresp);
+static CURLcode imap_continue_authenticate(struct Curl_easy *data,
+ const char *mech,
+ const struct bufref *resp);
+static CURLcode imap_cancel_authenticate(struct Curl_easy *data,
+ const char *mech);
+static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out);
+
+/*
+ * IMAP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_imap = {
+ "IMAP", /* scheme */
+ imap_setup_connection, /* setup_connection */
+ imap_do, /* do_it */
+ imap_done, /* done */
+ ZERO_NULL, /* do_more */
+ imap_connect, /* connect_it */
+ imap_multi_statemach, /* connecting */
+ imap_doing, /* doing */
+ imap_getsock, /* proto_getsock */
+ imap_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ imap_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_IMAP, /* defport */
+ CURLPROTO_IMAP, /* protocol */
+ CURLPROTO_IMAP, /* family */
+ PROTOPT_CLOSEACTION| /* flags */
+ PROTOPT_URLOPTIONS
+};
+
+#ifdef USE_SSL
+/*
+ * IMAPS protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_imaps = {
+ "IMAPS", /* scheme */
+ imap_setup_connection, /* setup_connection */
+ imap_do, /* do_it */
+ imap_done, /* done */
+ ZERO_NULL, /* do_more */
+ imap_connect, /* connect_it */
+ imap_multi_statemach, /* connecting */
+ imap_doing, /* doing */
+ imap_getsock, /* proto_getsock */
+ imap_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ imap_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_IMAPS, /* defport */
+ CURLPROTO_IMAPS, /* protocol */
+ CURLPROTO_IMAP, /* family */
+ PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */
+ PROTOPT_URLOPTIONS
+};
+#endif
+
+#define IMAP_RESP_OK 1
+#define IMAP_RESP_NOT_OK 2
+#define IMAP_RESP_PREAUTH 3
+
+/* SASL parameters for the imap protocol */
+static const struct SASLproto saslimap = {
+ "imap", /* The service name */
+ imap_perform_authenticate, /* Send authentication command */
+ imap_continue_authenticate, /* Send authentication continuation */
+ imap_cancel_authenticate, /* Send authentication cancellation */
+ imap_get_message, /* Get SASL response message */
+ 0, /* No maximum initial response length */
+ '+', /* Code received when continuation is expected */
+ IMAP_RESP_OK, /* Code to receive upon authentication success */
+ SASL_AUTH_DEFAULT, /* Default mechanisms */
+ SASL_FLAG_BASE64 /* Configuration flags */
+};
+
+
+#ifdef USE_SSL
+static void imap_to_imaps(struct connectdata *conn)
+{
+ /* Change the connection handler */
+ conn->handler = &Curl_handler_imaps;
+
+ /* Set the connection's upgraded to TLS flag */
+ conn->bits.tls_upgraded = TRUE;
+}
+#else
+#define imap_to_imaps(x) Curl_nop_stmt
+#endif
+
+/***********************************************************************
+ *
+ * imap_matchresp()
+ *
+ * Determines whether the untagged response is related to the specified
+ * command by checking if it is in format "* <command-name> ..." or
+ * "* <number> <command-name> ...".
+ *
+ * The "* " marker is assumed to have already been checked by the caller.
+ */
+static bool imap_matchresp(const char *line, size_t len, const char *cmd)
+{
+ const char *end = line + len;
+ size_t cmd_len = strlen(cmd);
+
+ /* Skip the untagged response marker */
+ line += 2;
+
+ /* Do we have a number after the marker? */
+ if(line < end && ISDIGIT(*line)) {
+ /* Skip the number */
+ do
+ line++;
+ while(line < end && ISDIGIT(*line));
+
+ /* Do we have the space character? */
+ if(line == end || *line != ' ')
+ return FALSE;
+
+ line++;
+ }
+
+ /* Does the command name match and is it followed by a space character or at
+ the end of line? */
+ if(line + cmd_len <= end && strncasecompare(line, cmd, cmd_len) &&
+ (line[cmd_len] == ' ' || line + cmd_len + 2 == end))
+ return TRUE;
+
+ return FALSE;
+}
+
+/***********************************************************************
+ *
+ * imap_endofresp()
+ *
+ * Checks whether the given string is a valid tagged, untagged or continuation
+ * response which can be processed by the response handler.
+ */
+static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn,
+ char *line, size_t len, int *resp)
+{
+ struct IMAP *imap = data->req.p.imap;
+ struct imap_conn *imapc = &conn->proto.imapc;
+ const char *id = imapc->resptag;
+ size_t id_len = strlen(id);
+
+ /* Do we have a tagged command response? */
+ if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
+ line += id_len + 1;
+ len -= id_len + 1;
+
+ if(len >= 2 && !memcmp(line, "OK", 2))
+ *resp = IMAP_RESP_OK;
+ else if(len >= 7 && !memcmp(line, "PREAUTH", 7))
+ *resp = IMAP_RESP_PREAUTH;
+ else
+ *resp = IMAP_RESP_NOT_OK;
+
+ return TRUE;
+ }
+
+ /* Do we have an untagged command response? */
+ if(len >= 2 && !memcmp("* ", line, 2)) {
+ switch(imapc->state) {
+ /* States which are interested in untagged responses */
+ case IMAP_CAPABILITY:
+ if(!imap_matchresp(line, len, "CAPABILITY"))
+ return FALSE;
+ break;
+
+ case IMAP_LIST:
+ if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
+ (imap->custom && !imap_matchresp(line, len, imap->custom) &&
+ (!strcasecompare(imap->custom, "STORE") ||
+ !imap_matchresp(line, len, "FETCH")) &&
+ !strcasecompare(imap->custom, "SELECT") &&
+ !strcasecompare(imap->custom, "EXAMINE") &&
+ !strcasecompare(imap->custom, "SEARCH") &&
+ !strcasecompare(imap->custom, "EXPUNGE") &&
+ !strcasecompare(imap->custom, "LSUB") &&
+ !strcasecompare(imap->custom, "UID") &&
+ !strcasecompare(imap->custom, "GETQUOTAROOT") &&
+ !strcasecompare(imap->custom, "NOOP")))
+ return FALSE;
+ break;
+
+ case IMAP_SELECT:
+ /* SELECT is special in that its untagged responses do not have a
+ common prefix so accept anything! */
+ break;
+
+ case IMAP_FETCH:
+ if(!imap_matchresp(line, len, "FETCH"))
+ return FALSE;
+ break;
+
+ case IMAP_SEARCH:
+ if(!imap_matchresp(line, len, "SEARCH"))
+ return FALSE;
+ break;
+
+ /* Ignore other untagged responses */
+ default:
+ return FALSE;
+ }
+
+ *resp = '*';
+ return TRUE;
+ }
+
+ /* Do we have a continuation response? This should be a + symbol followed by
+ a space and optionally some text as per RFC-3501 for the AUTHENTICATE and
+ APPEND commands and as outlined in Section 4. Examples of RFC-4959 but
+ some email servers ignore this and only send a single + instead. */
+ if(imap && !imap->custom && ((len == 3 && line[0] == '+') ||
+ (len >= 2 && !memcmp("+ ", line, 2)))) {
+ switch(imapc->state) {
+ /* States which are interested in continuation responses */
+ case IMAP_AUTHENTICATE:
+ case IMAP_APPEND:
+ *resp = '+';
+ break;
+
+ default:
+ failf(data, "Unexpected continuation response");
+ *resp = -1;
+ break;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE; /* Nothing for us */
+}
+
+/***********************************************************************
+ *
+ * imap_get_message()
+ *
+ * Gets the authentication message from the response buffer.
+ */
+static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out)
+{
+ char *message = data->state.buffer;
+ size_t len = strlen(message);
+
+ if(len > 2) {
+ /* Find the start of the message */
+ len -= 2;
+ for(message += 2; *message == ' ' || *message == '\t'; message++, len--)
+ ;
+
+ /* Find the end of the message */
+ while(len--)
+ if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
+ message[len] != '\t')
+ break;
+
+ /* Terminate the message */
+ message[++len] = '\0';
+ Curl_bufref_set(out, message, len, NULL);
+ }
+ else
+ /* junk input => zero length output */
+ Curl_bufref_set(out, "", 0, NULL);
+
+ return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * state()
+ *
+ * This is the ONLY way to change IMAP state!
+ */
+static void state(struct Curl_easy *data, imapstate newstate)
+{
+ struct imap_conn *imapc = &data->conn->proto.imapc;
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ /* for debug purposes */
+ static const char * const names[]={
+ "STOP",
+ "SERVERGREET",
+ "CAPABILITY",
+ "STARTTLS",
+ "UPGRADETLS",
+ "AUTHENTICATE",
+ "LOGIN",
+ "LIST",
+ "SELECT",
+ "FETCH",
+ "FETCH_FINAL",
+ "APPEND",
+ "APPEND_FINAL",
+ "SEARCH",
+ "LOGOUT",
+ /* LAST */
+ };
+
+ if(imapc->state != newstate)
+ infof(data, "IMAP %p state change from %s to %s",
+ (void *)imapc, names[imapc->state], names[newstate]);
+#endif
+
+ imapc->state = newstate;
+}
+
+/***********************************************************************
+ *
+ * imap_perform_capability()
+ *
+ * Sends the CAPABILITY command in order to obtain a list of server side
+ * supported capabilities.
+ */
+static CURLcode imap_perform_capability(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct imap_conn *imapc = &conn->proto.imapc;
+ imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
+ imapc->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */
+ imapc->tls_supported = FALSE; /* Clear the TLS capability */
+
+ /* Send the CAPABILITY command */
+ result = imap_sendf(data, "CAPABILITY");
+
+ if(!result)
+ state(data, IMAP_CAPABILITY);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * imap_perform_starttls()
+ *
+ * Sends the STARTTLS command to start the upgrade to TLS.
+ */
+static CURLcode imap_perform_starttls(struct Curl_easy *data)
+{
+ /* Send the STARTTLS command */
+ CURLcode result = imap_sendf(data, "STARTTLS");
+
+ if(!result)
+ state(data, IMAP_STARTTLS);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * imap_perform_upgrade_tls()
+ *
+ * Performs the upgrade to TLS.
+ */
+static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ /* Start the SSL connection */
+ struct imap_conn *imapc = &conn->proto.imapc;
+ CURLcode result;
+
+ if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) {
+ result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
+ if(result)
+ goto out;
+ }
+
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &imapc->ssldone);
+ if(!result) {
+ if(imapc->state != IMAP_UPGRADETLS)
+ state(data, IMAP_UPGRADETLS);
+
+ if(imapc->ssldone) {
+ imap_to_imaps(conn);
+ result = imap_perform_capability(data, conn);
+ }
+ }
+out:
+ return result;
+}
+
+/***********************************************************************
+ *
+ * imap_perform_login()
+ *
+ * Sends a clear text LOGIN command to authenticate with.
+ */
+static CURLcode imap_perform_login(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ char *user;
+ char *passwd;
+
+ /* Check we have a username and password to authenticate with and end the
+ connect phase if we don't */
+ if(!data->state.aptr.user) {
+ state(data, IMAP_STOP);
+
+ return result;
+ }
+
+ /* Make sure the username and password are in the correct atom format */
+ user = imap_atom(conn->user, false);
+ passwd = imap_atom(conn->passwd, false);
+
+ /* Send the LOGIN command */
+ result = imap_sendf(data, "LOGIN %s %s", user ? user : "",
+ passwd ? passwd : "");
+
+ free(user);
+ free(passwd);
+
+ if(!result)
+ state(data, IMAP_LOGIN);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * imap_perform_authenticate()
+ *
+ * Sends an AUTHENTICATE command allowing the client to login with the given
+ * SASL authentication mechanism.
+ */
+static CURLcode imap_perform_authenticate(struct Curl_easy *data,
+ const char *mech,
+ const struct bufref *initresp)
+{
+ CURLcode result = CURLE_OK;
+ const char *ir = (const char *) Curl_bufref_ptr(initresp);
+
+ if(ir) {
+ /* Send the AUTHENTICATE command with the initial response */
+ result = imap_sendf(data, "AUTHENTICATE %s %s", mech, ir);
+ }
+ else {
+ /* Send the AUTHENTICATE command */
+ result = imap_sendf(data, "AUTHENTICATE %s", mech);
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * imap_continue_authenticate()
+ *
+ * Sends SASL continuation data.
+ */
+static CURLcode imap_continue_authenticate(struct Curl_easy *data,
+ const char *mech,
+ const struct bufref *resp)
+{
+ struct imap_conn *imapc = &data->conn->proto.imapc;
+
+ (void)mech;
+
+ return Curl_pp_sendf(data, &imapc->pp,
+ "%s", (const char *) Curl_bufref_ptr(resp));
+}
+
+/***********************************************************************
+ *
+ * imap_cancel_authenticate()
+ *
+ * Sends SASL cancellation.
+ */
+static CURLcode imap_cancel_authenticate(struct Curl_easy *data,
+ const char *mech)
+{
+ struct imap_conn *imapc = &data->conn->proto.imapc;
+
+ (void)mech;
+
+ return Curl_pp_sendf(data, &imapc->pp, "*");
+}
+
+/***********************************************************************
+ *
+ * imap_perform_authentication()
+ *
+ * Initiates the authentication sequence, with the appropriate SASL
+ * authentication mechanism, falling back to clear text should a common
+ * mechanism not be available between the client and server.
+ */
+static CURLcode imap_perform_authentication(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct imap_conn *imapc = &conn->proto.imapc;
+ saslprogress progress;
+
+ /* Check if already authenticated OR if there is enough data to authenticate
+ with and end the connect phase if we don't */
+ if(imapc->preauth ||
+ !Curl_sasl_can_authenticate(&imapc->sasl, data)) {
+ state(data, IMAP_STOP);
+ return result;
+ }
+
+ /* Calculate the SASL login details */
+ result = Curl_sasl_start(&imapc->sasl, data, imapc->ir_supported, &progress);
+
+ if(!result) {
+ if(progress == SASL_INPROGRESS)
+ state(data, IMAP_AUTHENTICATE);
+ else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
+ /* Perform clear text authentication */
+ result = imap_perform_login(data, conn);
+ else {
+ /* Other mechanisms not supported */
+ infof(data, "No known authentication mechanisms supported");
+ result = CURLE_LOGIN_DENIED;
+ }
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * imap_perform_list()
+ *
+ * Sends a LIST command or an alternative custom request.
+ */
+static CURLcode imap_perform_list(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct IMAP *imap = data->req.p.imap;
+
+ if(imap->custom)
+ /* Send the custom request */
+ result = imap_sendf(data, "%s%s", imap->custom,
+ imap->custom_params ? imap->custom_params : "");
+ else {
+ /* Make sure the mailbox is in the correct atom format if necessary */
+ char *mailbox = imap->mailbox ? imap_atom(imap->mailbox, true)
+ : strdup("");
+ if(!mailbox)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Send the LIST command */
+ result = imap_sendf(data, "LIST \"%s\" *", mailbox);
+
+ free(mailbox);
+ }
+
+ if(!result)
+ state(data, IMAP_LIST);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * imap_perform_select()
+ *
+ * Sends a SELECT command to ask the server to change the selected mailbox.
+ */
+static CURLcode imap_perform_select(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct IMAP *imap = data->req.p.imap;
+ struct imap_conn *imapc = &conn->proto.imapc;
+ char *mailbox;
+
+ /* Invalidate old information as we are switching mailboxes */
+ Curl_safefree(imapc->mailbox);
+ Curl_safefree(imapc->mailbox_uidvalidity);
+
+ /* Check we have a mailbox */
+ if(!imap->mailbox) {
+ failf(data, "Cannot SELECT without a mailbox.");
+ return CURLE_URL_MALFORMAT;
+ }
+
+ /* Make sure the mailbox is in the correct atom format */
+ mailbox = imap_atom(imap->mailbox, false);
+ if(!mailbox)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Send the SELECT command */
+ result = imap_sendf(data, "SELECT %s", mailbox);
+
+ free(mailbox);
+
+ if(!result)
+ state(data, IMAP_SELECT);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * imap_perform_fetch()
+ *
+ * Sends a FETCH command to initiate the download of a message.
+ */
+static CURLcode imap_perform_fetch(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct IMAP *imap = data->req.p.imap;
+ /* Check we have a UID */
+ if(imap->uid) {
+
+ /* Send the FETCH command */
+ if(imap->partial)
+ result = imap_sendf(data, "UID FETCH %s BODY[%s]<%s>",
+ imap->uid, imap->section ? imap->section : "",
+ imap->partial);
+ else
+ result = imap_sendf(data, "UID FETCH %s BODY[%s]",
+ imap->uid, imap->section ? imap->section : "");
+ }
+ else if(imap->mindex) {
+ /* Send the FETCH command */
+ if(imap->partial)
+ result = imap_sendf(data, "FETCH %s BODY[%s]<%s>",
+ imap->mindex, imap->section ? imap->section : "",
+ imap->partial);
+ else
+ result = imap_sendf(data, "FETCH %s BODY[%s]",
+ imap->mindex, imap->section ? imap->section : "");
+ }
+ else {
+ failf(data, "Cannot FETCH without a UID.");
+ return CURLE_URL_MALFORMAT;
+ }
+ if(!result)
+ state(data, IMAP_FETCH);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * imap_perform_append()
+ *
+ * Sends an APPEND command to initiate the upload of a message.
+ */
+static CURLcode imap_perform_append(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct IMAP *imap = data->req.p.imap;
+ char *mailbox;
+
+ /* Check we have a mailbox */
+ if(!imap->mailbox) {
+ failf(data, "Cannot APPEND without a mailbox.");
+ return CURLE_URL_MALFORMAT;
+ }
+
+ /* Prepare the mime data if some. */
+ if(data->set.mimepost.kind != MIMEKIND_NONE) {
+ /* Use the whole structure as data. */
+ data->set.mimepost.flags &= ~MIME_BODY_ONLY;
+
+ /* Add external headers and mime version. */
+ curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
+ result = Curl_mime_prepare_headers(data, &data->set.mimepost, NULL,
+ NULL, MIMESTRATEGY_MAIL);
+
+ if(!result)
+ if(!Curl_checkheaders(data, STRCONST("Mime-Version")))
+ result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
+ "Mime-Version: 1.0");
+
+ /* Make sure we will read the entire mime structure. */
+ if(!result)
+ result = Curl_mime_rewind(&data->set.mimepost);
+
+ if(result)
+ return result;
+
+ data->state.infilesize = Curl_mime_size(&data->set.mimepost);
+
+ /* Read from mime structure. */
+ data->state.fread_func = (curl_read_callback) Curl_mime_read;
+ data->state.in = (void *) &data->set.mimepost;
+ }
+
+ /* Check we know the size of the upload */
+ if(data->state.infilesize < 0) {
+ failf(data, "Cannot APPEND with unknown input file size");
+ return CURLE_UPLOAD_FAILED;
+ }
+
+ /* Make sure the mailbox is in the correct atom format */
+ mailbox = imap_atom(imap->mailbox, false);
+ if(!mailbox)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Send the APPEND command */
+ result = imap_sendf(data,
+ "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}",
+ mailbox, data->state.infilesize);
+
+ free(mailbox);
+
+ if(!result)
+ state(data, IMAP_APPEND);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * imap_perform_search()
+ *
+ * Sends a SEARCH command.
+ */
+static CURLcode imap_perform_search(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct IMAP *imap = data->req.p.imap;
+
+ /* Check we have a query string */
+ if(!imap->query) {
+ failf(data, "Cannot SEARCH without a query string.");
+ return CURLE_URL_MALFORMAT;
+ }
+
+ /* Send the SEARCH command */
+ result = imap_sendf(data, "SEARCH %s", imap->query);
+
+ if(!result)
+ state(data, IMAP_SEARCH);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * imap_perform_logout()
+ *
+ * Performs the logout action prior to sclose() being called.
+ */
+static CURLcode imap_perform_logout(struct Curl_easy *data)
+{
+ /* Send the LOGOUT command */
+ CURLcode result = imap_sendf(data, "LOGOUT");
+
+ if(!result)
+ state(data, IMAP_LOGOUT);
+
+ return result;
+}
+
+/* For the initial server greeting */
+static CURLcode imap_state_servergreet_resp(struct Curl_easy *data,
+ int imapcode,
+ imapstate instate)
+{
+ struct connectdata *conn = data->conn;
+ (void)instate; /* no use for this yet */
+
+ if(imapcode == IMAP_RESP_PREAUTH) {
+ /* PREAUTH */
+ struct imap_conn *imapc = &conn->proto.imapc;
+ imapc->preauth = TRUE;
+ infof(data, "PREAUTH connection, already authenticated");
+ }
+ else if(imapcode != IMAP_RESP_OK) {
+ failf(data, "Got unexpected imap-server response");
+ return CURLE_WEIRD_SERVER_REPLY;
+ }
+
+ return imap_perform_capability(data, conn);
+}
+
+/* For CAPABILITY responses */
+static CURLcode imap_state_capability_resp(struct Curl_easy *data,
+ int imapcode,
+ imapstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct imap_conn *imapc = &conn->proto.imapc;
+ const char *line = data->state.buffer;
+
+ (void)instate; /* no use for this yet */
+
+ /* Do we have a untagged response? */
+ if(imapcode == '*') {
+ line += 2;
+
+ /* Loop through the data line */
+ for(;;) {
+ size_t wordlen;
+ while(*line &&
+ (*line == ' ' || *line == '\t' ||
+ *line == '\r' || *line == '\n')) {
+
+ line++;
+ }
+
+ if(!*line)
+ break;
+
+ /* Extract the word */
+ for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
+ line[wordlen] != '\t' && line[wordlen] != '\r' &&
+ line[wordlen] != '\n';)
+ wordlen++;
+
+ /* Does the server support the STARTTLS capability? */
+ if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
+ imapc->tls_supported = TRUE;
+
+ /* Has the server explicitly disabled clear text authentication? */
+ else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
+ imapc->login_disabled = TRUE;
+
+ /* Does the server support the SASL-IR capability? */
+ else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
+ imapc->ir_supported = TRUE;
+
+ /* Do we have a SASL based authentication mechanism? */
+ else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
+ size_t llen;
+ unsigned short mechbit;
+
+ line += 5;
+ wordlen -= 5;
+
+ /* Test the word for a matching authentication mechanism */
+ mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
+ if(mechbit && llen == wordlen)
+ imapc->sasl.authmechs |= mechbit;
+ }
+
+ line += wordlen;
+ }
+ }
+ else if(data->set.use_ssl && !Curl_conn_is_ssl(data, FIRSTSOCKET)) {
+ /* PREAUTH is not compatible with STARTTLS. */
+ if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) {
+ /* Switch to TLS connection now */
+ result = imap_perform_starttls(data);
+ }
+ else if(data->set.use_ssl <= CURLUSESSL_TRY)
+ result = imap_perform_authentication(data, conn);
+ else {
+ failf(data, "STARTTLS not available.");
+ result = CURLE_USE_SSL_FAILED;
+ }
+ }
+ else
+ result = imap_perform_authentication(data, conn);
+
+ return result;
+}
+
+/* For STARTTLS responses */
+static CURLcode imap_state_starttls_resp(struct Curl_easy *data,
+ int imapcode,
+ imapstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+
+ (void)instate; /* no use for this yet */
+
+ /* Pipelining in response is forbidden. */
+ if(data->conn->proto.imapc.pp.cache_size)
+ return CURLE_WEIRD_SERVER_REPLY;
+
+ if(imapcode != IMAP_RESP_OK) {
+ if(data->set.use_ssl != CURLUSESSL_TRY) {
+ failf(data, "STARTTLS denied");
+ result = CURLE_USE_SSL_FAILED;
+ }
+ else
+ result = imap_perform_authentication(data, conn);
+ }
+ else
+ result = imap_perform_upgrade_tls(data, conn);
+
+ return result;
+}
+
+/* For SASL authentication responses */
+static CURLcode imap_state_auth_resp(struct Curl_easy *data,
+ struct connectdata *conn,
+ int imapcode,
+ imapstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct imap_conn *imapc = &conn->proto.imapc;
+ saslprogress progress;
+
+ (void)instate; /* no use for this yet */
+
+ result = Curl_sasl_continue(&imapc->sasl, data, imapcode, &progress);
+ if(!result)
+ switch(progress) {
+ case SASL_DONE:
+ state(data, IMAP_STOP); /* Authenticated */
+ break;
+ case SASL_IDLE: /* No mechanism left after cancellation */
+ if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
+ /* Perform clear text authentication */
+ result = imap_perform_login(data, conn);
+ else {
+ failf(data, "Authentication cancelled");
+ result = CURLE_LOGIN_DENIED;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+/* For LOGIN responses */
+static CURLcode imap_state_login_resp(struct Curl_easy *data,
+ int imapcode,
+ imapstate instate)
+{
+ CURLcode result = CURLE_OK;
+ (void)instate; /* no use for this yet */
+
+ if(imapcode != IMAP_RESP_OK) {
+ failf(data, "Access denied. %c", imapcode);
+ result = CURLE_LOGIN_DENIED;
+ }
+ else
+ /* End of connect phase */
+ state(data, IMAP_STOP);
+
+ return result;
+}
+
+/* For LIST and SEARCH responses */
+static CURLcode imap_state_listsearch_resp(struct Curl_easy *data,
+ int imapcode,
+ imapstate instate)
+{
+ CURLcode result = CURLE_OK;
+ char *line = data->state.buffer;
+ size_t len = strlen(line);
+
+ (void)instate; /* No use for this yet */
+
+ if(imapcode == '*') {
+ /* Temporarily add the LF character back and send as body to the client */
+ line[len] = '\n';
+ result = Curl_client_write(data, CLIENTWRITE_BODY, line, len + 1);
+ line[len] = '\0';
+ }
+ else if(imapcode != IMAP_RESP_OK)
+ result = CURLE_QUOTE_ERROR;
+ else
+ /* End of DO phase */
+ state(data, IMAP_STOP);
+
+ return result;
+}
+
+/* For SELECT responses */
+static CURLcode imap_state_select_resp(struct Curl_easy *data, int imapcode,
+ imapstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct IMAP *imap = data->req.p.imap;
+ struct imap_conn *imapc = &conn->proto.imapc;
+ const char *line = data->state.buffer;
+
+ (void)instate; /* no use for this yet */
+
+ if(imapcode == '*') {
+ /* See if this is an UIDVALIDITY response */
+ char tmp[20];
+ if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
+ Curl_safefree(imapc->mailbox_uidvalidity);
+ imapc->mailbox_uidvalidity = strdup(tmp);
+ }
+ }
+ else if(imapcode == IMAP_RESP_OK) {
+ /* Check if the UIDVALIDITY has been specified and matches */
+ if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
+ !strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
+ failf(data, "Mailbox UIDVALIDITY has changed");
+ result = CURLE_REMOTE_FILE_NOT_FOUND;
+ }
+ else {
+ /* Note the currently opened mailbox on this connection */
+ imapc->mailbox = strdup(imap->mailbox);
+
+ if(imap->custom)
+ result = imap_perform_list(data);
+ else if(imap->query)
+ result = imap_perform_search(data);
+ else
+ result = imap_perform_fetch(data);
+ }
+ }
+ else {
+ failf(data, "Select failed");
+ result = CURLE_LOGIN_DENIED;
+ }
+
+ return result;
+}
+
+/* For the (first line of the) FETCH responses */
+static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
+ struct connectdata *conn, int imapcode,
+ imapstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct imap_conn *imapc = &conn->proto.imapc;
+ struct pingpong *pp = &imapc->pp;
+ const char *ptr = data->state.buffer;
+ bool parsed = FALSE;
+ curl_off_t size = 0;
+
+ (void)instate; /* no use for this yet */
+
+ if(imapcode != '*') {
+ Curl_pgrsSetDownloadSize(data, -1);
+ state(data, IMAP_STOP);
+ return CURLE_REMOTE_FILE_NOT_FOUND;
+ }
+
+ /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
+ the continuation data contained within the curly brackets */
+ while(*ptr && (*ptr != '{'))
+ ptr++;
+
+ if(*ptr == '{') {
+ char *endptr;
+ if(!curlx_strtoofft(ptr + 1, &endptr, 10, &size)) {
+ if(endptr - ptr > 1 && endptr[0] == '}' &&
+ endptr[1] == '\r' && endptr[2] == '\0')
+ parsed = TRUE;
+ }
+ }
+
+ if(parsed) {
+ infof(data, "Found %" CURL_FORMAT_CURL_OFF_T " bytes to download",
+ size);
+ Curl_pgrsSetDownloadSize(data, size);
+
+ if(pp->cache) {
+ /* At this point there is a bunch of data in the header "cache" that is
+ actually body content, send it as body and then skip it. Do note
+ that there may even be additional "headers" after the body. */
+ size_t chunk = pp->cache_size;
+
+ if(chunk > (size_t)size)
+ /* The conversion from curl_off_t to size_t is always fine here */
+ chunk = (size_t)size;
+
+ if(!chunk) {
+ /* no size, we're done with the data */
+ state(data, IMAP_STOP);
+ return CURLE_OK;
+ }
+ result = Curl_client_write(data, CLIENTWRITE_BODY, pp->cache, chunk);
+ if(result)
+ return result;
+
+ data->req.bytecount += chunk;
+
+ infof(data, "Written %zu bytes, %" CURL_FORMAT_CURL_OFF_TU
+ " bytes are left for transfer", chunk, size - chunk);
+
+ /* Have we used the entire cache or just part of it?*/
+ if(pp->cache_size > chunk) {
+ /* Only part of it so shrink the cache to fit the trailing data */
+ memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
+ pp->cache_size -= chunk;
+ }
+ else {
+ /* Free the cache */
+ Curl_safefree(pp->cache);
+
+ /* Reset the cache size */
+ pp->cache_size = 0;
+ }
+ }
+
+ if(data->req.bytecount == size)
+ /* The entire data is already transferred! */
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
+ else {
+ /* IMAP download */
+ data->req.maxdownload = size;
+ /* force a recv/send check of this connection, as the data might've been
+ read off the socket already */
+ data->conn->cselect_bits = CURL_CSELECT_IN;
+ Curl_setup_transfer(data, FIRSTSOCKET, size, FALSE, -1);
+ }
+ }
+ else {
+ /* We don't know how to parse this line */
+ failf(data, "Failed to parse FETCH response.");
+ result = CURLE_WEIRD_SERVER_REPLY;
+ }
+
+ /* End of DO phase */
+ state(data, IMAP_STOP);
+
+ return result;
+}
+
+/* For final FETCH responses performed after the download */
+static CURLcode imap_state_fetch_final_resp(struct Curl_easy *data,
+ int imapcode,
+ imapstate instate)
+{
+ CURLcode result = CURLE_OK;
+
+ (void)instate; /* No use for this yet */
+
+ if(imapcode != IMAP_RESP_OK)
+ result = CURLE_WEIRD_SERVER_REPLY;
+ else
+ /* End of DONE phase */
+ state(data, IMAP_STOP);
+
+ return result;
+}
+
+/* For APPEND responses */
+static CURLcode imap_state_append_resp(struct Curl_easy *data, int imapcode,
+ imapstate instate)
+{
+ CURLcode result = CURLE_OK;
+ (void)instate; /* No use for this yet */
+
+ if(imapcode != '+') {
+ result = CURLE_UPLOAD_FAILED;
+ }
+ else {
+ /* Set the progress upload size */
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+
+ /* IMAP upload */
+ Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
+
+ /* End of DO phase */
+ state(data, IMAP_STOP);
+ }
+
+ return result;
+}
+
+/* For final APPEND responses performed after the upload */
+static CURLcode imap_state_append_final_resp(struct Curl_easy *data,
+ int imapcode,
+ imapstate instate)
+{
+ CURLcode result = CURLE_OK;
+
+ (void)instate; /* No use for this yet */
+
+ if(imapcode != IMAP_RESP_OK)
+ result = CURLE_UPLOAD_FAILED;
+ else
+ /* End of DONE phase */
+ state(data, IMAP_STOP);
+
+ return result;
+}
+
+static CURLcode imap_statemachine(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ int imapcode;
+ struct imap_conn *imapc = &conn->proto.imapc;
+ struct pingpong *pp = &imapc->pp;
+ size_t nread = 0;
+ (void)data;
+
+ /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
+ if(imapc->state == IMAP_UPGRADETLS)
+ return imap_perform_upgrade_tls(data, conn);
+
+ /* Flush any data that needs to be sent */
+ if(pp->sendleft)
+ return Curl_pp_flushsend(data, pp);
+
+ do {
+ /* Read the response from the server */
+ result = Curl_pp_readresp(data, sock, pp, &imapcode, &nread);
+ if(result)
+ return result;
+
+ /* Was there an error parsing the response line? */
+ if(imapcode == -1)
+ return CURLE_WEIRD_SERVER_REPLY;
+
+ if(!imapcode)
+ break;
+
+ /* We have now received a full IMAP server response */
+ switch(imapc->state) {
+ case IMAP_SERVERGREET:
+ result = imap_state_servergreet_resp(data, imapcode, imapc->state);
+ break;
+
+ case IMAP_CAPABILITY:
+ result = imap_state_capability_resp(data, imapcode, imapc->state);
+ break;
+
+ case IMAP_STARTTLS:
+ result = imap_state_starttls_resp(data, imapcode, imapc->state);
+ break;
+
+ case IMAP_AUTHENTICATE:
+ result = imap_state_auth_resp(data, conn, imapcode, imapc->state);
+ break;
+
+ case IMAP_LOGIN:
+ result = imap_state_login_resp(data, imapcode, imapc->state);
+ break;
+
+ case IMAP_LIST:
+ case IMAP_SEARCH:
+ result = imap_state_listsearch_resp(data, imapcode, imapc->state);
+ break;
+
+ case IMAP_SELECT:
+ result = imap_state_select_resp(data, imapcode, imapc->state);
+ break;
+
+ case IMAP_FETCH:
+ result = imap_state_fetch_resp(data, conn, imapcode, imapc->state);
+ break;
+
+ case IMAP_FETCH_FINAL:
+ result = imap_state_fetch_final_resp(data, imapcode, imapc->state);
+ break;
+
+ case IMAP_APPEND:
+ result = imap_state_append_resp(data, imapcode, imapc->state);
+ break;
+
+ case IMAP_APPEND_FINAL:
+ result = imap_state_append_final_resp(data, imapcode, imapc->state);
+ break;
+
+ case IMAP_LOGOUT:
+ /* fallthrough, just stop! */
+ default:
+ /* internal error */
+ state(data, IMAP_STOP);
+ break;
+ }
+ } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
+
+ return result;
+}
+
+/* Called repeatedly until done from multi.c */
+static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct imap_conn *imapc = &conn->proto.imapc;
+
+ if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &imapc->ssldone);
+ if(result || !imapc->ssldone)
+ return result;
+ }
+
+ result = Curl_pp_statemach(data, &imapc->pp, FALSE, FALSE);
+ *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
+
+ return result;
+}
+
+static CURLcode imap_block_statemach(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool disconnecting)
+{
+ CURLcode result = CURLE_OK;
+ struct imap_conn *imapc = &conn->proto.imapc;
+
+ while(imapc->state != IMAP_STOP && !result)
+ result = Curl_pp_statemach(data, &imapc->pp, TRUE, disconnecting);
+
+ return result;
+}
+
+/* Allocate and initialize the struct IMAP for the current Curl_easy if
+ required */
+static CURLcode imap_init(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct IMAP *imap;
+
+ imap = data->req.p.imap = calloc(sizeof(struct IMAP), 1);
+ if(!imap)
+ result = CURLE_OUT_OF_MEMORY;
+
+ return result;
+}
+
+/* For the IMAP "protocol connect" and "doing" phases only */
+static int imap_getsock(struct Curl_easy *data,
+ struct connectdata *conn,
+ curl_socket_t *socks)
+{
+ return Curl_pp_getsock(data, &conn->proto.imapc.pp, socks);
+}
+
+/***********************************************************************
+ *
+ * imap_connect()
+ *
+ * This function should do everything that is to be considered a part of the
+ * connection phase.
+ *
+ * The variable 'done' points to will be TRUE if the protocol-layer connect
+ * phase is done when this function returns, or FALSE if not.
+ */
+static CURLcode imap_connect(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct imap_conn *imapc = &conn->proto.imapc;
+ struct pingpong *pp = &imapc->pp;
+
+ *done = FALSE; /* default to not done yet */
+
+ /* We always support persistent connections in IMAP */
+ connkeep(conn, "IMAP default");
+
+ PINGPONG_SETUP(pp, imap_statemachine, imap_endofresp);
+
+ /* Set the default preferred authentication type and mechanism */
+ imapc->preftype = IMAP_TYPE_ANY;
+ Curl_sasl_init(&imapc->sasl, data, &saslimap);
+
+ Curl_dyn_init(&imapc->dyn, DYN_IMAP_CMD);
+ /* Initialise the pingpong layer */
+ Curl_pp_setup(pp);
+ Curl_pp_init(data, pp);
+
+ /* Parse the URL options */
+ result = imap_parse_url_options(conn);
+ if(result)
+ return result;
+
+ /* Start off waiting for the server greeting response */
+ state(data, IMAP_SERVERGREET);
+
+ /* Start off with an response id of '*' */
+ strcpy(imapc->resptag, "*");
+
+ result = imap_multi_statemach(data, done);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * imap_done()
+ *
+ * The DONE function. This does what needs to be done after a single DO has
+ * performed.
+ *
+ * Input argument is already checked for validity.
+ */
+static CURLcode imap_done(struct Curl_easy *data, CURLcode status,
+ bool premature)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct IMAP *imap = data->req.p.imap;
+
+ (void)premature;
+
+ if(!imap)
+ return CURLE_OK;
+
+ if(status) {
+ connclose(conn, "IMAP done with bad status"); /* marked for closure */
+ result = status; /* use the already set error code */
+ }
+ else if(!data->set.connect_only && !imap->custom &&
+ (imap->uid || imap->mindex || data->set.upload ||
+ data->set.mimepost.kind != MIMEKIND_NONE)) {
+ /* Handle responses after FETCH or APPEND transfer has finished */
+
+ if(!data->set.upload && data->set.mimepost.kind == MIMEKIND_NONE)
+ state(data, IMAP_FETCH_FINAL);
+ else {
+ /* End the APPEND command first by sending an empty line */
+ result = Curl_pp_sendf(data, &conn->proto.imapc.pp, "%s", "");
+ if(!result)
+ state(data, IMAP_APPEND_FINAL);
+ }
+
+ /* Run the state-machine */
+ if(!result)
+ result = imap_block_statemach(data, conn, FALSE);
+ }
+
+ /* Cleanup our per-request based variables */
+ Curl_safefree(imap->mailbox);
+ Curl_safefree(imap->uidvalidity);
+ Curl_safefree(imap->uid);
+ Curl_safefree(imap->mindex);
+ Curl_safefree(imap->section);
+ Curl_safefree(imap->partial);
+ Curl_safefree(imap->query);
+ Curl_safefree(imap->custom);
+ Curl_safefree(imap->custom_params);
+
+ /* Clear the transfer mode for the next request */
+ imap->transfer = PPTRANSFER_BODY;
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * imap_perform()
+ *
+ * This is the actual DO function for IMAP. Fetch or append a message, or do
+ * other things according to the options previously setup.
+ */
+static CURLcode imap_perform(struct Curl_easy *data, bool *connected,
+ bool *dophase_done)
+{
+ /* This is IMAP and no proxy */
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct IMAP *imap = data->req.p.imap;
+ struct imap_conn *imapc = &conn->proto.imapc;
+ bool selected = FALSE;
+
+ DEBUGF(infof(data, "DO phase starts"));
+
+ if(data->req.no_body) {
+ /* Requested no body means no transfer */
+ imap->transfer = PPTRANSFER_INFO;
+ }
+
+ *dophase_done = FALSE; /* not done yet */
+
+ /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
+ has already been selected on this connection */
+ if(imap->mailbox && imapc->mailbox &&
+ strcasecompare(imap->mailbox, imapc->mailbox) &&
+ (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
+ strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)))
+ selected = TRUE;
+
+ /* Start the first command in the DO phase */
+ if(data->set.upload || data->set.mimepost.kind != MIMEKIND_NONE)
+ /* APPEND can be executed directly */
+ result = imap_perform_append(data);
+ else if(imap->custom && (selected || !imap->mailbox))
+ /* Custom command using the same mailbox or no mailbox */
+ result = imap_perform_list(data);
+ else if(!imap->custom && selected && (imap->uid || imap->mindex))
+ /* FETCH from the same mailbox */
+ result = imap_perform_fetch(data);
+ else if(!imap->custom && selected && imap->query)
+ /* SEARCH the current mailbox */
+ result = imap_perform_search(data);
+ else if(imap->mailbox && !selected &&
+ (imap->custom || imap->uid || imap->mindex || imap->query))
+ /* SELECT the mailbox */
+ result = imap_perform_select(data);
+ else
+ /* LIST */
+ result = imap_perform_list(data);
+
+ if(result)
+ return result;
+
+ /* Run the state-machine */
+ result = imap_multi_statemach(data, dophase_done);
+
+ *connected = Curl_conn_is_connected(conn, FIRSTSOCKET);
+
+ if(*dophase_done)
+ DEBUGF(infof(data, "DO phase is complete"));
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * imap_do()
+ *
+ * This function is registered as 'curl_do' function. It decodes the path
+ * parts etc as a wrapper to the actual DO function (imap_perform).
+ *
+ * The input argument is already checked for validity.
+ */
+static CURLcode imap_do(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ *done = FALSE; /* default to false */
+
+ /* Parse the URL path */
+ result = imap_parse_url_path(data);
+ if(result)
+ return result;
+
+ /* Parse the custom request */
+ result = imap_parse_custom_request(data);
+ if(result)
+ return result;
+
+ result = imap_regular_transfer(data, done);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * imap_disconnect()
+ *
+ * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
+ * resources. BLOCKING.
+ */
+static CURLcode imap_disconnect(struct Curl_easy *data,
+ struct connectdata *conn, bool dead_connection)
+{
+ struct imap_conn *imapc = &conn->proto.imapc;
+ (void)data;
+
+ /* We cannot send quit unconditionally. If this connection is stale or
+ bad in any way, sending quit and waiting around here will make the
+ disconnect wait in vain and cause more problems than we need to. */
+
+ /* The IMAP session may or may not have been allocated/setup at this
+ point! */
+ if(!dead_connection && conn->bits.protoconnstart) {
+ if(!imap_perform_logout(data))
+ (void)imap_block_statemach(data, conn, TRUE); /* ignore errors */
+ }
+
+ /* Disconnect from the server */
+ Curl_pp_disconnect(&imapc->pp);
+ Curl_dyn_free(&imapc->dyn);
+
+ /* Cleanup the SASL module */
+ Curl_sasl_cleanup(conn, imapc->sasl.authused);
+
+ /* Cleanup our connection based variables */
+ Curl_safefree(imapc->mailbox);
+ Curl_safefree(imapc->mailbox_uidvalidity);
+
+ return CURLE_OK;
+}
+
+/* Call this when the DO phase has completed */
+static CURLcode imap_dophase_done(struct Curl_easy *data, bool connected)
+{
+ struct IMAP *imap = data->req.p.imap;
+
+ (void)connected;
+
+ if(imap->transfer != PPTRANSFER_BODY)
+ /* no data to transfer */
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
+
+ return CURLE_OK;
+}
+
+/* Called from multi.c while DOing */
+static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done)
+{
+ CURLcode result = imap_multi_statemach(data, dophase_done);
+
+ if(result)
+ DEBUGF(infof(data, "DO phase failed"));
+ else if(*dophase_done) {
+ result = imap_dophase_done(data, FALSE /* not connected */);
+
+ DEBUGF(infof(data, "DO phase is complete"));
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * imap_regular_transfer()
+ *
+ * The input argument is already checked for validity.
+ *
+ * Performs all commands done before a regular transfer between a local and a
+ * remote host.
+ */
+static CURLcode imap_regular_transfer(struct Curl_easy *data,
+ bool *dophase_done)
+{
+ CURLcode result = CURLE_OK;
+ bool connected = FALSE;
+
+ /* Make sure size is unknown at this point */
+ data->req.size = -1;
+
+ /* Set the progress data */
+ Curl_pgrsSetUploadCounter(data, 0);
+ Curl_pgrsSetDownloadCounter(data, 0);
+ Curl_pgrsSetUploadSize(data, -1);
+ Curl_pgrsSetDownloadSize(data, -1);
+
+ /* Carry out the perform */
+ result = imap_perform(data, &connected, dophase_done);
+
+ /* Perform post DO phase operations if necessary */
+ if(!result && *dophase_done)
+ result = imap_dophase_done(data, connected);
+
+ return result;
+}
+
+static CURLcode imap_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ /* Initialise the IMAP layer */
+ CURLcode result = imap_init(data);
+ if(result)
+ return result;
+
+ /* Clear the TLS upgraded flag */
+ conn->bits.tls_upgraded = FALSE;
+
+ return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * imap_sendf()
+ *
+ * Sends the formatted string as an IMAP command to the server.
+ *
+ * Designed to never block.
+ */
+static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...)
+{
+ CURLcode result = CURLE_OK;
+ struct imap_conn *imapc = &data->conn->proto.imapc;
+
+ DEBUGASSERT(fmt);
+
+ /* Calculate the tag based on the connection ID and command ID */
+ msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
+ 'A' + curlx_sltosi(data->conn->connection_id % 26),
+ (++imapc->cmdid)%1000);
+
+ /* start with a blank buffer */
+ Curl_dyn_reset(&imapc->dyn);
+
+ /* append tag + space + fmt */
+ result = Curl_dyn_addf(&imapc->dyn, "%s %s", imapc->resptag, fmt);
+ if(!result) {
+ va_list ap;
+ va_start(ap, fmt);
+ result = Curl_pp_vsendf(data, &imapc->pp, Curl_dyn_ptr(&imapc->dyn), ap);
+ va_end(ap);
+ }
+ return result;
+}
+
+/***********************************************************************
+ *
+ * imap_atom()
+ *
+ * Checks the input string for characters that need escaping and returns an
+ * atom ready for sending to the server.
+ *
+ * The returned string needs to be freed.
+ *
+ */
+static char *imap_atom(const char *str, bool escape_only)
+{
+ /* !checksrc! disable PARENBRACE 1 */
+ const char atom_specials[] = "(){ %*]";
+ const char *p1;
+ char *p2;
+ size_t backsp_count = 0;
+ size_t quote_count = 0;
+ bool others_exists = FALSE;
+ size_t newlen = 0;
+ char *newstr = NULL;
+
+ if(!str)
+ return NULL;
+
+ /* Look for "atom-specials", counting the backslash and quote characters as
+ these will need escaping */
+ p1 = str;
+ while(*p1) {
+ if(*p1 == '\\')
+ backsp_count++;
+ else if(*p1 == '"')
+ quote_count++;
+ else if(!escape_only) {
+ const char *p3 = atom_specials;
+
+ while(*p3 && !others_exists) {
+ if(*p1 == *p3)
+ others_exists = TRUE;
+
+ p3++;
+ }
+ }
+
+ p1++;
+ }
+
+ /* Does the input contain any "atom-special" characters? */
+ if(!backsp_count && !quote_count && !others_exists)
+ return strdup(str);
+
+ /* Calculate the new string length */
+ newlen = strlen(str) + backsp_count + quote_count + (escape_only ? 0 : 2);
+
+ /* Allocate the new string */
+ newstr = (char *) malloc((newlen + 1) * sizeof(char));
+ if(!newstr)
+ return NULL;
+
+ /* Surround the string in quotes if necessary */
+ p2 = newstr;
+ if(!escape_only) {
+ newstr[0] = '"';
+ newstr[newlen - 1] = '"';
+ p2++;
+ }
+
+ /* Copy the string, escaping backslash and quote characters along the way */
+ p1 = str;
+ while(*p1) {
+ if(*p1 == '\\' || *p1 == '"') {
+ *p2 = '\\';
+ p2++;
+ }
+
+ *p2 = *p1;
+
+ p1++;
+ p2++;
+ }
+
+ /* Terminate the string */
+ newstr[newlen] = '\0';
+
+ return newstr;
+}
+
+/***********************************************************************
+ *
+ * imap_is_bchar()
+ *
+ * Portable test of whether the specified char is a "bchar" as defined in the
+ * grammar of RFC-5092.
+ */
+static bool imap_is_bchar(char ch)
+{
+ /* Performing the alnum check with this macro is faster because of ASCII
+ arithmetic */
+ if(ISALNUM(ch))
+ return true;
+
+ switch(ch) {
+ /* bchar */
+ case ':': case '@': case '/':
+ /* bchar -> achar */
+ case '&': case '=':
+ /* bchar -> achar -> uchar -> unreserved (without alphanumeric) */
+ case '-': case '.': case '_': case '~':
+ /* bchar -> achar -> uchar -> sub-delims-sh */
+ case '!': case '$': case '\'': case '(': case ')': case '*':
+ case '+': case ',':
+ /* bchar -> achar -> uchar -> pct-encoded */
+ case '%': /* HEXDIG chars are already included above */
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/***********************************************************************
+ *
+ * imap_parse_url_options()
+ *
+ * Parse the URL login options.
+ */
+static CURLcode imap_parse_url_options(struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct imap_conn *imapc = &conn->proto.imapc;
+ const char *ptr = conn->options;
+
+ while(!result && ptr && *ptr) {
+ const char *key = ptr;
+ const char *value;
+
+ while(*ptr && *ptr != '=')
+ ptr++;
+
+ value = ptr + 1;
+
+ while(*ptr && *ptr != ';')
+ ptr++;
+
+ if(strncasecompare(key, "AUTH=", 5))
+ result = Curl_sasl_parse_url_auth_option(&imapc->sasl,
+ value, ptr - value);
+ else
+ result = CURLE_URL_MALFORMAT;
+
+ if(*ptr == ';')
+ ptr++;
+ }
+
+ switch(imapc->sasl.prefmech) {
+ case SASL_AUTH_NONE:
+ imapc->preftype = IMAP_TYPE_NONE;
+ break;
+ case SASL_AUTH_DEFAULT:
+ imapc->preftype = IMAP_TYPE_ANY;
+ break;
+ default:
+ imapc->preftype = IMAP_TYPE_SASL;
+ break;
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * imap_parse_url_path()
+ *
+ * Parse the URL path into separate path components.
+ *
+ */
+static CURLcode imap_parse_url_path(struct Curl_easy *data)
+{
+ /* The imap struct is already initialised in imap_connect() */
+ CURLcode result = CURLE_OK;
+ struct IMAP *imap = data->req.p.imap;
+ const char *begin = &data->state.up.path[1]; /* skip leading slash */
+ const char *ptr = begin;
+
+ /* See how much of the URL is a valid path and decode it */
+ while(imap_is_bchar(*ptr))
+ ptr++;
+
+ if(ptr != begin) {
+ /* Remove the trailing slash if present */
+ const char *end = ptr;
+ if(end > begin && end[-1] == '/')
+ end--;
+
+ result = Curl_urldecode(begin, end - begin, &imap->mailbox, NULL,
+ REJECT_CTRL);
+ if(result)
+ return result;
+ }
+ else
+ imap->mailbox = NULL;
+
+ /* There can be any number of parameters in the form ";NAME=VALUE" */
+ while(*ptr == ';') {
+ char *name;
+ char *value;
+ size_t valuelen;
+
+ /* Find the length of the name parameter */
+ begin = ++ptr;
+ while(*ptr && *ptr != '=')
+ ptr++;
+
+ if(!*ptr)
+ return CURLE_URL_MALFORMAT;
+
+ /* Decode the name parameter */
+ result = Curl_urldecode(begin, ptr - begin, &name, NULL,
+ REJECT_CTRL);
+ if(result)
+ return result;
+
+ /* Find the length of the value parameter */
+ begin = ++ptr;
+ while(imap_is_bchar(*ptr))
+ ptr++;
+
+ /* Decode the value parameter */
+ result = Curl_urldecode(begin, ptr - begin, &value, &valuelen,
+ REJECT_CTRL);
+ if(result) {
+ free(name);
+ return result;
+ }
+
+ DEBUGF(infof(data, "IMAP URL parameter '%s' = '%s'", name, value));
+
+ /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and
+ PARTIAL) stripping of the trailing slash character if it is present.
+
+ Note: Unknown parameters trigger a URL_MALFORMAT error. */
+ if(strcasecompare(name, "UIDVALIDITY") && !imap->uidvalidity) {
+ if(valuelen > 0 && value[valuelen - 1] == '/')
+ value[valuelen - 1] = '\0';
+
+ imap->uidvalidity = value;
+ value = NULL;
+ }
+ else if(strcasecompare(name, "UID") && !imap->uid) {
+ if(valuelen > 0 && value[valuelen - 1] == '/')
+ value[valuelen - 1] = '\0';
+
+ imap->uid = value;
+ value = NULL;
+ }
+ else if(strcasecompare(name, "MAILINDEX") && !imap->mindex) {
+ if(valuelen > 0 && value[valuelen - 1] == '/')
+ value[valuelen - 1] = '\0';
+
+ imap->mindex = value;
+ value = NULL;
+ }
+ else if(strcasecompare(name, "SECTION") && !imap->section) {
+ if(valuelen > 0 && value[valuelen - 1] == '/')
+ value[valuelen - 1] = '\0';
+
+ imap->section = value;
+ value = NULL;
+ }
+ else if(strcasecompare(name, "PARTIAL") && !imap->partial) {
+ if(valuelen > 0 && value[valuelen - 1] == '/')
+ value[valuelen - 1] = '\0';
+
+ imap->partial = value;
+ value = NULL;
+ }
+ else {
+ free(name);
+ free(value);
+
+ return CURLE_URL_MALFORMAT;
+ }
+
+ free(name);
+ free(value);
+ }
+
+ /* Does the URL contain a query parameter? Only valid when we have a mailbox
+ and no UID as per RFC-5092 */
+ if(imap->mailbox && !imap->uid && !imap->mindex) {
+ /* Get the query parameter, URL decoded */
+ (void)curl_url_get(data->state.uh, CURLUPART_QUERY, &imap->query,
+ CURLU_URLDECODE);
+ }
+
+ /* Any extra stuff at the end of the URL is an error */
+ if(*ptr)
+ return CURLE_URL_MALFORMAT;
+
+ return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * imap_parse_custom_request()
+ *
+ * Parse the custom request.
+ */
+static CURLcode imap_parse_custom_request(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct IMAP *imap = data->req.p.imap;
+ const char *custom = data->set.str[STRING_CUSTOMREQUEST];
+
+ if(custom) {
+ /* URL decode the custom request */
+ result = Curl_urldecode(custom, 0, &imap->custom, NULL, REJECT_CTRL);
+
+ /* Extract the parameters if specified */
+ if(!result) {
+ const char *params = imap->custom;
+
+ while(*params && *params != ' ')
+ params++;
+
+ if(*params) {
+ imap->custom_params = strdup(params);
+ imap->custom[params - imap->custom] = '\0';
+
+ if(!imap->custom_params)
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ }
+ }
+
+ return result;
+}
+
+#endif /* CURL_DISABLE_IMAP */
diff --git a/libs/libcurl/src/imap.h b/libs/libcurl/src/imap.h index 43cc1e98fc..f6216acad6 100644 --- a/libs/libcurl/src/imap.h +++ b/libs/libcurl/src/imap.h @@ -1,101 +1,101 @@ -#ifndef HEADER_CURL_IMAP_H -#define HEADER_CURL_IMAP_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2009 - 2022, 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 "pingpong.h" -#include "curl_sasl.h" - -/**************************************************************************** - * IMAP unique setup - ***************************************************************************/ -typedef enum { - IMAP_STOP, /* do nothing state, stops the state machine */ - IMAP_SERVERGREET, /* waiting for the initial greeting immediately after - a connect */ - IMAP_CAPABILITY, - IMAP_STARTTLS, - IMAP_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS - (multi mode only) */ - IMAP_AUTHENTICATE, - IMAP_LOGIN, - IMAP_LIST, - IMAP_SELECT, - IMAP_FETCH, - IMAP_FETCH_FINAL, - IMAP_APPEND, - IMAP_APPEND_FINAL, - IMAP_SEARCH, - IMAP_LOGOUT, - IMAP_LAST /* never used */ -} imapstate; - -/* This IMAP struct is used in the Curl_easy. All IMAP data that is - connection-oriented must be in imap_conn to properly deal with the fact that - perhaps the Curl_easy is changed between the times the connection is - used. */ -struct IMAP { - curl_pp_transfer transfer; - char *mailbox; /* Mailbox to select */ - char *uidvalidity; /* UIDVALIDITY to check in select */ - char *uid; /* Message UID to fetch */ - char *mindex; /* Index in mail box of mail to fetch */ - char *section; /* Message SECTION to fetch */ - char *partial; /* Message PARTIAL to fetch */ - char *query; /* Query to search for */ - char *custom; /* Custom request */ - char *custom_params; /* Parameters for the custom request */ -}; - -/* imap_conn is used for struct connection-oriented data in the connectdata - struct */ -struct imap_conn { - struct pingpong pp; - imapstate state; /* Always use imap.c:state() to change state! */ - bool ssldone; /* Is connect() over SSL done? */ - bool preauth; /* Is this connection PREAUTH? */ - struct SASL sasl; /* SASL-related parameters */ - unsigned int preftype; /* Preferred authentication type */ - unsigned int cmdid; /* Last used command ID */ - char resptag[5]; /* Response tag to wait for */ - bool tls_supported; /* StartTLS capability supported by server */ - bool login_disabled; /* LOGIN command disabled by server */ - bool ir_supported; /* Initial response supported by server */ - char *mailbox; /* The last selected mailbox */ - char *mailbox_uidvalidity; /* UIDVALIDITY parsed from select response */ - struct dynbuf dyn; /* for the IMAP commands */ -}; - -extern const struct Curl_handler Curl_handler_imap; -extern const struct Curl_handler Curl_handler_imaps; - -/* Authentication type flags */ -#define IMAP_TYPE_CLEARTEXT (1 << 0) -#define IMAP_TYPE_SASL (1 << 1) - -/* Authentication type values */ -#define IMAP_TYPE_NONE 0 -#define IMAP_TYPE_ANY ~0U - -#endif /* HEADER_CURL_IMAP_H */ +#ifndef HEADER_CURL_IMAP_H
+#define HEADER_CURL_IMAP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2009 - 2022, 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 "pingpong.h"
+#include "curl_sasl.h"
+
+/****************************************************************************
+ * IMAP unique setup
+ ***************************************************************************/
+typedef enum {
+ IMAP_STOP, /* do nothing state, stops the state machine */
+ IMAP_SERVERGREET, /* waiting for the initial greeting immediately after
+ a connect */
+ IMAP_CAPABILITY,
+ IMAP_STARTTLS,
+ IMAP_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS
+ (multi mode only) */
+ IMAP_AUTHENTICATE,
+ IMAP_LOGIN,
+ IMAP_LIST,
+ IMAP_SELECT,
+ IMAP_FETCH,
+ IMAP_FETCH_FINAL,
+ IMAP_APPEND,
+ IMAP_APPEND_FINAL,
+ IMAP_SEARCH,
+ IMAP_LOGOUT,
+ IMAP_LAST /* never used */
+} imapstate;
+
+/* This IMAP struct is used in the Curl_easy. All IMAP data that is
+ connection-oriented must be in imap_conn to properly deal with the fact that
+ perhaps the Curl_easy is changed between the times the connection is
+ used. */
+struct IMAP {
+ curl_pp_transfer transfer;
+ char *mailbox; /* Mailbox to select */
+ char *uidvalidity; /* UIDVALIDITY to check in select */
+ char *uid; /* Message UID to fetch */
+ char *mindex; /* Index in mail box of mail to fetch */
+ char *section; /* Message SECTION to fetch */
+ char *partial; /* Message PARTIAL to fetch */
+ char *query; /* Query to search for */
+ char *custom; /* Custom request */
+ char *custom_params; /* Parameters for the custom request */
+};
+
+/* imap_conn is used for struct connection-oriented data in the connectdata
+ struct */
+struct imap_conn {
+ struct pingpong pp;
+ imapstate state; /* Always use imap.c:state() to change state! */
+ bool ssldone; /* Is connect() over SSL done? */
+ bool preauth; /* Is this connection PREAUTH? */
+ struct SASL sasl; /* SASL-related parameters */
+ unsigned int preftype; /* Preferred authentication type */
+ unsigned int cmdid; /* Last used command ID */
+ char resptag[5]; /* Response tag to wait for */
+ bool tls_supported; /* StartTLS capability supported by server */
+ bool login_disabled; /* LOGIN command disabled by server */
+ bool ir_supported; /* Initial response supported by server */
+ char *mailbox; /* The last selected mailbox */
+ char *mailbox_uidvalidity; /* UIDVALIDITY parsed from select response */
+ struct dynbuf dyn; /* for the IMAP commands */
+};
+
+extern const struct Curl_handler Curl_handler_imap;
+extern const struct Curl_handler Curl_handler_imaps;
+
+/* Authentication type flags */
+#define IMAP_TYPE_CLEARTEXT (1 << 0)
+#define IMAP_TYPE_SASL (1 << 1)
+
+/* Authentication type values */
+#define IMAP_TYPE_NONE 0
+#define IMAP_TYPE_ANY ~0U
+
+#endif /* HEADER_CURL_IMAP_H */
diff --git a/libs/libcurl/src/inet_ntop.c b/libs/libcurl/src/inet_ntop.c index 024f8da36d..e4e4a34605 100644 --- a/libs/libcurl/src/inet_ntop.c +++ b/libs/libcurl/src/inet_ntop.c @@ -1,199 +1,199 @@ -/* - * Copyright (C) 1996-2022 Internet Software Consortium. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM - * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL - * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * SPDX-License-Identifier: ISC - */ -/* - * Original code by Paul Vixie. "curlified" by Gisle Vanem. - */ - -#include "curl_setup.h" - -#ifndef HAVE_INET_NTOP - -#ifdef HAVE_SYS_PARAM_H -#include <sys/param.h> -#endif -#ifdef HAVE_NETINET_IN_H -#include <netinet/in.h> -#endif -#ifdef HAVE_ARPA_INET_H -#include <arpa/inet.h> -#endif - -#include "inet_ntop.h" -#include "curl_printf.h" - -#define IN6ADDRSZ 16 -#define INADDRSZ 4 -#define INT16SZ 2 - -/* - * Format an IPv4 address, more or less like inet_ntop(). - * - * Returns `dst' (as a const) - * Note: - * - uses no statics - * - takes a unsigned char* not an in_addr as input - */ -static char *inet_ntop4 (const unsigned char *src, char *dst, size_t size) -{ - char tmp[sizeof("255.255.255.255")]; - size_t len; - - DEBUGASSERT(size >= 16); - - tmp[0] = '\0'; - (void)msnprintf(tmp, sizeof(tmp), "%d.%d.%d.%d", - ((int)((unsigned char)src[0])) & 0xff, - ((int)((unsigned char)src[1])) & 0xff, - ((int)((unsigned char)src[2])) & 0xff, - ((int)((unsigned char)src[3])) & 0xff); - - len = strlen(tmp); - if(len == 0 || len >= size) { - errno = ENOSPC; - return (NULL); - } - strcpy(dst, tmp); - return dst; -} - -#ifdef ENABLE_IPV6 -/* - * Convert IPv6 binary address into presentation (printable) format. - */ -static char *inet_ntop6 (const unsigned char *src, char *dst, size_t size) -{ - /* - * Note that int32_t and int16_t need only be "at least" large enough - * to contain a value of the specified size. On some systems, like - * Crays, there is no such thing as an integer variable with 16 bits. - * Keep this in mind if you think this function should have been coded - * to use pointer overlays. All the world's not a VAX. - */ - char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; - char *tp; - struct { - long base; - long len; - } best, cur; - unsigned long words[IN6ADDRSZ / INT16SZ]; - int i; - - /* Preprocess: - * Copy the input (bytewise) array into a wordwise array. - * Find the longest run of 0x00's in src[] for :: shorthanding. - */ - memset(words, '\0', sizeof(words)); - for(i = 0; i < IN6ADDRSZ; i++) - words[i/2] |= (src[i] << ((1 - (i % 2)) << 3)); - - best.base = -1; - cur.base = -1; - best.len = 0; - cur.len = 0; - - for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { - if(words[i] == 0) { - if(cur.base == -1) - cur.base = i, cur.len = 1; - else - cur.len++; - } - else if(cur.base != -1) { - if(best.base == -1 || cur.len > best.len) - best = cur; - cur.base = -1; - } - } - if((cur.base != -1) && (best.base == -1 || cur.len > best.len)) - best = cur; - if(best.base != -1 && best.len < 2) - best.base = -1; - /* Format the result. */ - tp = tmp; - for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { - /* Are we inside the best run of 0x00's? */ - if(best.base != -1 && i >= best.base && i < (best.base + best.len)) { - if(i == best.base) - *tp++ = ':'; - continue; - } - - /* Are we following an initial run of 0x00s or any real hex? - */ - if(i) - *tp++ = ':'; - - /* Is this address an encapsulated IPv4? - */ - if(i == 6 && best.base == 0 && - (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { - if(!inet_ntop4(src + 12, tp, sizeof(tmp) - (tp - tmp))) { - errno = ENOSPC; - return (NULL); - } - tp += strlen(tp); - break; - } - tp += msnprintf(tp, 5, "%lx", words[i]); - } - - /* Was it a trailing run of 0x00's? - */ - if(best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) - *tp++ = ':'; - *tp++ = '\0'; - - /* Check for overflow, copy, and we're done. - */ - if((size_t)(tp - tmp) > size) { - errno = ENOSPC; - return (NULL); - } - strcpy(dst, tmp); - return dst; -} -#endif /* ENABLE_IPV6 */ - -/* - * Convert a network format address to presentation format. - * - * Returns pointer to presentation format address (`buf'). - * Returns NULL on error and errno set with the specific - * error, EAFNOSUPPORT or ENOSPC. - * - * On Windows we store the error in the thread errno, not - * in the winsock error code. This is to avoid losing the - * actual last winsock error. So when this function returns - * NULL, check errno not SOCKERRNO. - */ -char *Curl_inet_ntop(int af, const void *src, char *buf, size_t size) -{ - switch(af) { - case AF_INET: - return inet_ntop4((const unsigned char *)src, buf, size); -#ifdef ENABLE_IPV6 - case AF_INET6: - return inet_ntop6((const unsigned char *)src, buf, size); -#endif - default: - errno = EAFNOSUPPORT; - return NULL; - } -} -#endif /* HAVE_INET_NTOP */ +/*
+ * Copyright (C) 1996-2022 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * SPDX-License-Identifier: ISC
+ */
+/*
+ * Original code by Paul Vixie. "curlified" by Gisle Vanem.
+ */
+
+#include "curl_setup.h"
+
+#ifndef HAVE_INET_NTOP
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include "inet_ntop.h"
+#include "curl_printf.h"
+
+#define IN6ADDRSZ 16
+#define INADDRSZ 4
+#define INT16SZ 2
+
+/*
+ * Format an IPv4 address, more or less like inet_ntop().
+ *
+ * Returns `dst' (as a const)
+ * Note:
+ * - uses no statics
+ * - takes a unsigned char* not an in_addr as input
+ */
+static char *inet_ntop4 (const unsigned char *src, char *dst, size_t size)
+{
+ char tmp[sizeof("255.255.255.255")];
+ size_t len;
+
+ DEBUGASSERT(size >= 16);
+
+ tmp[0] = '\0';
+ (void)msnprintf(tmp, sizeof(tmp), "%d.%d.%d.%d",
+ ((int)((unsigned char)src[0])) & 0xff,
+ ((int)((unsigned char)src[1])) & 0xff,
+ ((int)((unsigned char)src[2])) & 0xff,
+ ((int)((unsigned char)src[3])) & 0xff);
+
+ len = strlen(tmp);
+ if(len == 0 || len >= size) {
+ errno = ENOSPC;
+ return (NULL);
+ }
+ strcpy(dst, tmp);
+ return dst;
+}
+
+#ifdef ENABLE_IPV6
+/*
+ * Convert IPv6 binary address into presentation (printable) format.
+ */
+static char *inet_ntop6 (const unsigned char *src, char *dst, size_t size)
+{
+ /*
+ * Note that int32_t and int16_t need only be "at least" large enough
+ * to contain a value of the specified size. On some systems, like
+ * Crays, there is no such thing as an integer variable with 16 bits.
+ * Keep this in mind if you think this function should have been coded
+ * to use pointer overlays. All the world's not a VAX.
+ */
+ char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+ char *tp;
+ struct {
+ long base;
+ long len;
+ } best, cur;
+ unsigned long words[IN6ADDRSZ / INT16SZ];
+ int i;
+
+ /* Preprocess:
+ * Copy the input (bytewise) array into a wordwise array.
+ * Find the longest run of 0x00's in src[] for :: shorthanding.
+ */
+ memset(words, '\0', sizeof(words));
+ for(i = 0; i < IN6ADDRSZ; i++)
+ words[i/2] |= (src[i] << ((1 - (i % 2)) << 3));
+
+ best.base = -1;
+ cur.base = -1;
+ best.len = 0;
+ cur.len = 0;
+
+ for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
+ if(words[i] == 0) {
+ if(cur.base == -1)
+ cur.base = i, cur.len = 1;
+ else
+ cur.len++;
+ }
+ else if(cur.base != -1) {
+ if(best.base == -1 || cur.len > best.len)
+ best = cur;
+ cur.base = -1;
+ }
+ }
+ if((cur.base != -1) && (best.base == -1 || cur.len > best.len))
+ best = cur;
+ if(best.base != -1 && best.len < 2)
+ best.base = -1;
+ /* Format the result. */
+ tp = tmp;
+ for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
+ /* Are we inside the best run of 0x00's? */
+ if(best.base != -1 && i >= best.base && i < (best.base + best.len)) {
+ if(i == best.base)
+ *tp++ = ':';
+ continue;
+ }
+
+ /* Are we following an initial run of 0x00s or any real hex?
+ */
+ if(i)
+ *tp++ = ':';
+
+ /* Is this address an encapsulated IPv4?
+ */
+ if(i == 6 && best.base == 0 &&
+ (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
+ if(!inet_ntop4(src + 12, tp, sizeof(tmp) - (tp - tmp))) {
+ errno = ENOSPC;
+ return (NULL);
+ }
+ tp += strlen(tp);
+ break;
+ }
+ tp += msnprintf(tp, 5, "%lx", words[i]);
+ }
+
+ /* Was it a trailing run of 0x00's?
+ */
+ if(best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ))
+ *tp++ = ':';
+ *tp++ = '\0';
+
+ /* Check for overflow, copy, and we're done.
+ */
+ if((size_t)(tp - tmp) > size) {
+ errno = ENOSPC;
+ return (NULL);
+ }
+ strcpy(dst, tmp);
+ return dst;
+}
+#endif /* ENABLE_IPV6 */
+
+/*
+ * Convert a network format address to presentation format.
+ *
+ * Returns pointer to presentation format address (`buf').
+ * Returns NULL on error and errno set with the specific
+ * error, EAFNOSUPPORT or ENOSPC.
+ *
+ * On Windows we store the error in the thread errno, not
+ * in the winsock error code. This is to avoid losing the
+ * actual last winsock error. So when this function returns
+ * NULL, check errno not SOCKERRNO.
+ */
+char *Curl_inet_ntop(int af, const void *src, char *buf, size_t size)
+{
+ switch(af) {
+ case AF_INET:
+ return inet_ntop4((const unsigned char *)src, buf, size);
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ return inet_ntop6((const unsigned char *)src, buf, size);
+#endif
+ default:
+ errno = EAFNOSUPPORT;
+ return NULL;
+ }
+}
+#endif /* HAVE_INET_NTOP */
diff --git a/libs/libcurl/src/inet_ntop.h b/libs/libcurl/src/inet_ntop.h index 18fbd8ba3a..6761d18979 100644 --- a/libs/libcurl/src/inet_ntop.h +++ b/libs/libcurl/src/inet_ntop.h @@ -1,39 +1,39 @@ -#ifndef HEADER_CURL_INET_NTOP_H -#define HEADER_CURL_INET_NTOP_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -char *Curl_inet_ntop(int af, const void *addr, char *buf, size_t size); - -#ifdef HAVE_INET_NTOP -#ifdef HAVE_ARPA_INET_H -#include <arpa/inet.h> -#endif -#define Curl_inet_ntop(af,addr,buf,size) \ - inet_ntop(af, addr, buf, (curl_socklen_t)size) -#endif - -#endif /* HEADER_CURL_INET_NTOP_H */ +#ifndef HEADER_CURL_INET_NTOP_H
+#define HEADER_CURL_INET_NTOP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+char *Curl_inet_ntop(int af, const void *addr, char *buf, size_t size);
+
+#ifdef HAVE_INET_NTOP
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#define Curl_inet_ntop(af,addr,buf,size) \
+ inet_ntop(af, addr, buf, (curl_socklen_t)size)
+#endif
+
+#endif /* HEADER_CURL_INET_NTOP_H */
diff --git a/libs/libcurl/src/inet_pton.c b/libs/libcurl/src/inet_pton.c index 47fb77834d..fa654d02e3 100644 --- a/libs/libcurl/src/inet_pton.c +++ b/libs/libcurl/src/inet_pton.c @@ -1,239 +1,239 @@ -/* This is from the BIND 4.9.4 release, modified to compile by itself */ - -/* Copyright (c) 2003 - 2022 by Internet Software Consortium. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS - * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE - * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL - * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR - * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS - * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS - * SOFTWARE. - * - * SPDX-License-Identifier: ISC - */ - -#include "curl_setup.h" - -#ifndef HAVE_INET_PTON - -#ifdef HAVE_SYS_PARAM_H -#include <sys/param.h> -#endif -#ifdef HAVE_NETINET_IN_H -#include <netinet/in.h> -#endif -#ifdef HAVE_ARPA_INET_H -#include <arpa/inet.h> -#endif - -#include "inet_pton.h" - -#define IN6ADDRSZ 16 -#define INADDRSZ 4 -#define INT16SZ 2 - -/* - * WARNING: Don't even consider trying to compile this on a system where - * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. - */ - -static int inet_pton4(const char *src, unsigned char *dst); -#ifdef ENABLE_IPV6 -static int inet_pton6(const char *src, unsigned char *dst); -#endif - -/* int - * inet_pton(af, src, dst) - * convert from presentation format (which usually means ASCII printable) - * to network format (which is usually some kind of binary format). - * return: - * 1 if the address was valid for the specified address family - * 0 if the address wasn't valid (`dst' is untouched in this case) - * -1 if some other error occurred (`dst' is untouched in this case, too) - * notice: - * On Windows we store the error in the thread errno, not - * in the winsock error code. This is to avoid losing the - * actual last winsock error. So when this function returns - * -1, check errno not SOCKERRNO. - * author: - * Paul Vixie, 1996. - */ -int -Curl_inet_pton(int af, const char *src, void *dst) -{ - switch(af) { - case AF_INET: - return (inet_pton4(src, (unsigned char *)dst)); -#ifdef ENABLE_IPV6 - case AF_INET6: - return (inet_pton6(src, (unsigned char *)dst)); -#endif - default: - errno = EAFNOSUPPORT; - return (-1); - } - /* NOTREACHED */ -} - -/* int - * inet_pton4(src, dst) - * like inet_aton() but without all the hexadecimal and shorthand. - * return: - * 1 if `src' is a valid dotted quad, else 0. - * notice: - * does not touch `dst' unless it's returning 1. - * author: - * Paul Vixie, 1996. - */ -static int -inet_pton4(const char *src, unsigned char *dst) -{ - static const char digits[] = "0123456789"; - int saw_digit, octets, ch; - unsigned char tmp[INADDRSZ], *tp; - - saw_digit = 0; - octets = 0; - tp = tmp; - *tp = 0; - while((ch = *src++) != '\0') { - const char *pch; - - pch = strchr(digits, ch); - if(pch) { - unsigned int val = *tp * 10 + (unsigned int)(pch - digits); - - if(saw_digit && *tp == 0) - return (0); - if(val > 255) - return (0); - *tp = (unsigned char)val; - if(!saw_digit) { - if(++octets > 4) - return (0); - saw_digit = 1; - } - } - else if(ch == '.' && saw_digit) { - if(octets == 4) - return (0); - *++tp = 0; - saw_digit = 0; - } - else - return (0); - } - if(octets < 4) - return (0); - memcpy(dst, tmp, INADDRSZ); - return (1); -} - -#ifdef ENABLE_IPV6 -/* int - * inet_pton6(src, dst) - * convert presentation level address to network order binary form. - * return: - * 1 if `src' is a valid [RFC1884 2.2] address, else 0. - * notice: - * (1) does not touch `dst' unless it's returning 1. - * (2) :: in a full address is silently ignored. - * credit: - * inspired by Mark Andrews. - * author: - * Paul Vixie, 1996. - */ -static int -inet_pton6(const char *src, unsigned char *dst) -{ - static const char xdigits_l[] = "0123456789abcdef", - xdigits_u[] = "0123456789ABCDEF"; - unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp; - const char *curtok; - int ch, saw_xdigit; - size_t val; - - memset((tp = tmp), 0, IN6ADDRSZ); - endp = tp + IN6ADDRSZ; - colonp = NULL; - /* Leading :: requires some special handling. */ - if(*src == ':') - if(*++src != ':') - return (0); - curtok = src; - saw_xdigit = 0; - val = 0; - while((ch = *src++) != '\0') { - const char *xdigits; - const char *pch; - - pch = strchr((xdigits = xdigits_l), ch); - if(!pch) - pch = strchr((xdigits = xdigits_u), ch); - if(pch) { - val <<= 4; - val |= (pch - xdigits); - if(++saw_xdigit > 4) - return (0); - continue; - } - if(ch == ':') { - curtok = src; - if(!saw_xdigit) { - if(colonp) - return (0); - colonp = tp; - continue; - } - if(tp + INT16SZ > endp) - return (0); - *tp++ = (unsigned char) ((val >> 8) & 0xff); - *tp++ = (unsigned char) (val & 0xff); - saw_xdigit = 0; - val = 0; - continue; - } - if(ch == '.' && ((tp + INADDRSZ) <= endp) && - inet_pton4(curtok, tp) > 0) { - tp += INADDRSZ; - saw_xdigit = 0; - break; /* '\0' was seen by inet_pton4(). */ - } - return (0); - } - if(saw_xdigit) { - if(tp + INT16SZ > endp) - return (0); - *tp++ = (unsigned char) ((val >> 8) & 0xff); - *tp++ = (unsigned char) (val & 0xff); - } - if(colonp) { - /* - * Since some memmove()'s erroneously fail to handle - * overlapping regions, we'll do the shift by hand. - */ - const ssize_t n = tp - colonp; - ssize_t i; - - if(tp == endp) - return (0); - for(i = 1; i <= n; i++) { - *(endp - i) = *(colonp + n - i); - *(colonp + n - i) = 0; - } - tp = endp; - } - if(tp != endp) - return (0); - memcpy(dst, tmp, IN6ADDRSZ); - return (1); -} -#endif /* ENABLE_IPV6 */ - -#endif /* HAVE_INET_PTON */ +/* This is from the BIND 4.9.4 release, modified to compile by itself */
+
+/* Copyright (c) 2003 - 2022 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * SPDX-License-Identifier: ISC
+ */
+
+#include "curl_setup.h"
+
+#ifndef HAVE_INET_PTON
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include "inet_pton.h"
+
+#define IN6ADDRSZ 16
+#define INADDRSZ 4
+#define INT16SZ 2
+
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+static int inet_pton4(const char *src, unsigned char *dst);
+#ifdef ENABLE_IPV6
+static int inet_pton6(const char *src, unsigned char *dst);
+#endif
+
+/* int
+ * inet_pton(af, src, dst)
+ * convert from presentation format (which usually means ASCII printable)
+ * to network format (which is usually some kind of binary format).
+ * return:
+ * 1 if the address was valid for the specified address family
+ * 0 if the address wasn't valid (`dst' is untouched in this case)
+ * -1 if some other error occurred (`dst' is untouched in this case, too)
+ * notice:
+ * On Windows we store the error in the thread errno, not
+ * in the winsock error code. This is to avoid losing the
+ * actual last winsock error. So when this function returns
+ * -1, check errno not SOCKERRNO.
+ * author:
+ * Paul Vixie, 1996.
+ */
+int
+Curl_inet_pton(int af, const char *src, void *dst)
+{
+ switch(af) {
+ case AF_INET:
+ return (inet_pton4(src, (unsigned char *)dst));
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ return (inet_pton6(src, (unsigned char *)dst));
+#endif
+ default:
+ errno = EAFNOSUPPORT;
+ return (-1);
+ }
+ /* NOTREACHED */
+}
+
+/* int
+ * inet_pton4(src, dst)
+ * like inet_aton() but without all the hexadecimal and shorthand.
+ * return:
+ * 1 if `src' is a valid dotted quad, else 0.
+ * notice:
+ * does not touch `dst' unless it's returning 1.
+ * author:
+ * Paul Vixie, 1996.
+ */
+static int
+inet_pton4(const char *src, unsigned char *dst)
+{
+ static const char digits[] = "0123456789";
+ int saw_digit, octets, ch;
+ unsigned char tmp[INADDRSZ], *tp;
+
+ saw_digit = 0;
+ octets = 0;
+ tp = tmp;
+ *tp = 0;
+ while((ch = *src++) != '\0') {
+ const char *pch;
+
+ pch = strchr(digits, ch);
+ if(pch) {
+ unsigned int val = *tp * 10 + (unsigned int)(pch - digits);
+
+ if(saw_digit && *tp == 0)
+ return (0);
+ if(val > 255)
+ return (0);
+ *tp = (unsigned char)val;
+ if(!saw_digit) {
+ if(++octets > 4)
+ return (0);
+ saw_digit = 1;
+ }
+ }
+ else if(ch == '.' && saw_digit) {
+ if(octets == 4)
+ return (0);
+ *++tp = 0;
+ saw_digit = 0;
+ }
+ else
+ return (0);
+ }
+ if(octets < 4)
+ return (0);
+ memcpy(dst, tmp, INADDRSZ);
+ return (1);
+}
+
+#ifdef ENABLE_IPV6
+/* int
+ * inet_pton6(src, dst)
+ * convert presentation level address to network order binary form.
+ * return:
+ * 1 if `src' is a valid [RFC1884 2.2] address, else 0.
+ * notice:
+ * (1) does not touch `dst' unless it's returning 1.
+ * (2) :: in a full address is silently ignored.
+ * credit:
+ * inspired by Mark Andrews.
+ * author:
+ * Paul Vixie, 1996.
+ */
+static int
+inet_pton6(const char *src, unsigned char *dst)
+{
+ static const char xdigits_l[] = "0123456789abcdef",
+ xdigits_u[] = "0123456789ABCDEF";
+ unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
+ const char *curtok;
+ int ch, saw_xdigit;
+ size_t val;
+
+ memset((tp = tmp), 0, IN6ADDRSZ);
+ endp = tp + IN6ADDRSZ;
+ colonp = NULL;
+ /* Leading :: requires some special handling. */
+ if(*src == ':')
+ if(*++src != ':')
+ return (0);
+ curtok = src;
+ saw_xdigit = 0;
+ val = 0;
+ while((ch = *src++) != '\0') {
+ const char *xdigits;
+ const char *pch;
+
+ pch = strchr((xdigits = xdigits_l), ch);
+ if(!pch)
+ pch = strchr((xdigits = xdigits_u), ch);
+ if(pch) {
+ val <<= 4;
+ val |= (pch - xdigits);
+ if(++saw_xdigit > 4)
+ return (0);
+ continue;
+ }
+ if(ch == ':') {
+ curtok = src;
+ if(!saw_xdigit) {
+ if(colonp)
+ return (0);
+ colonp = tp;
+ continue;
+ }
+ if(tp + INT16SZ > endp)
+ return (0);
+ *tp++ = (unsigned char) ((val >> 8) & 0xff);
+ *tp++ = (unsigned char) (val & 0xff);
+ saw_xdigit = 0;
+ val = 0;
+ continue;
+ }
+ if(ch == '.' && ((tp + INADDRSZ) <= endp) &&
+ inet_pton4(curtok, tp) > 0) {
+ tp += INADDRSZ;
+ saw_xdigit = 0;
+ break; /* '\0' was seen by inet_pton4(). */
+ }
+ return (0);
+ }
+ if(saw_xdigit) {
+ if(tp + INT16SZ > endp)
+ return (0);
+ *tp++ = (unsigned char) ((val >> 8) & 0xff);
+ *tp++ = (unsigned char) (val & 0xff);
+ }
+ if(colonp) {
+ /*
+ * Since some memmove()'s erroneously fail to handle
+ * overlapping regions, we'll do the shift by hand.
+ */
+ const ssize_t n = tp - colonp;
+ ssize_t i;
+
+ if(tp == endp)
+ return (0);
+ for(i = 1; i <= n; i++) {
+ *(endp - i) = *(colonp + n - i);
+ *(colonp + n - i) = 0;
+ }
+ tp = endp;
+ }
+ if(tp != endp)
+ return (0);
+ memcpy(dst, tmp, IN6ADDRSZ);
+ return (1);
+}
+#endif /* ENABLE_IPV6 */
+
+#endif /* HAVE_INET_PTON */
diff --git a/libs/libcurl/src/inet_pton.h b/libs/libcurl/src/inet_pton.h index 92ae93ea1f..c3ff2e5323 100644 --- a/libs/libcurl/src/inet_pton.h +++ b/libs/libcurl/src/inet_pton.h @@ -1,41 +1,41 @@ -#ifndef HEADER_CURL_INET_PTON_H -#define HEADER_CURL_INET_PTON_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -int Curl_inet_pton(int, const char *, void *); - -#ifdef HAVE_INET_PTON -#ifdef HAVE_ARPA_INET_H -#include <arpa/inet.h> -#elif defined(HAVE_WS2TCPIP_H) -/* inet_pton() exists in Vista or later */ -#include <ws2tcpip.h> -#endif -#define Curl_inet_pton(x,y,z) inet_pton(x,y,z) -#endif - -#endif /* HEADER_CURL_INET_PTON_H */ +#ifndef HEADER_CURL_INET_PTON_H
+#define HEADER_CURL_INET_PTON_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+int Curl_inet_pton(int, const char *, void *);
+
+#ifdef HAVE_INET_PTON
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#elif defined(HAVE_WS2TCPIP_H)
+/* inet_pton() exists in Vista or later */
+#include <ws2tcpip.h>
+#endif
+#define Curl_inet_pton(x,y,z) inet_pton(x,y,z)
+#endif
+
+#endif /* HEADER_CURL_INET_PTON_H */
diff --git a/libs/libcurl/src/krb5.c b/libs/libcurl/src/krb5.c index 517491c465..21e5cc242b 100644 --- a/libs/libcurl/src/krb5.c +++ b/libs/libcurl/src/krb5.c @@ -1,899 +1,902 @@ -/* GSSAPI/krb5 support for FTP - loosely based on old krb4.c - * - * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan - * (Royal Institute of Technology, Stockholm, Sweden). - * Copyright (c) 2004 - 2022 Daniel Stenberg - * All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. */ - -#include "curl_setup.h" - -#if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_FTP) - -#ifdef HAVE_NETDB_H -#include <netdb.h> -#endif - -#include "urldata.h" -#include "curl_base64.h" -#include "ftp.h" -#include "curl_gssapi.h" -#include "sendf.h" -#include "curl_krb5.h" -#include "warnless.h" -#include "strcase.h" -#include "strdup.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn, - const char *cmd) -{ - ssize_t bytes_written; -#define SBUF_SIZE 1024 - char s[SBUF_SIZE]; - size_t write_len; - char *sptr = s; - CURLcode result = CURLE_OK; -#ifdef HAVE_GSSAPI - enum protection_level data_sec = conn->data_prot; -#endif - - if(!cmd) - return CURLE_BAD_FUNCTION_ARGUMENT; - - write_len = strlen(cmd); - if(!write_len || write_len > (sizeof(s) -3)) - return CURLE_BAD_FUNCTION_ARGUMENT; - - memcpy(&s, cmd, write_len); - strcpy(&s[write_len], "\r\n"); /* append a trailing CRLF */ - write_len += 2; - bytes_written = 0; - - for(;;) { -#ifdef HAVE_GSSAPI - conn->data_prot = PROT_CMD; -#endif - result = Curl_write(data, conn->sock[FIRSTSOCKET], sptr, write_len, - &bytes_written); -#ifdef HAVE_GSSAPI - DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST); - conn->data_prot = data_sec; -#endif - - if(result) - break; - - Curl_debug(data, CURLINFO_HEADER_OUT, sptr, (size_t)bytes_written); - - if(bytes_written != (ssize_t)write_len) { - write_len -= bytes_written; - sptr += bytes_written; - } - else - break; - } - - return result; -} - -static int -krb5_init(void *app_data) -{ - gss_ctx_id_t *context = app_data; - /* Make sure our context is initialized for krb5_end. */ - *context = GSS_C_NO_CONTEXT; - return 0; -} - -static int -krb5_check_prot(void *app_data, int level) -{ - (void)app_data; /* unused */ - if(level == PROT_CONFIDENTIAL) - return -1; - return 0; -} - -static int -krb5_decode(void *app_data, void *buf, int len, - int level UNUSED_PARAM, - struct connectdata *conn UNUSED_PARAM) -{ - gss_ctx_id_t *context = app_data; - OM_uint32 maj, min; - gss_buffer_desc enc, dec; - - (void)level; - (void)conn; - - enc.value = buf; - enc.length = len; - maj = gss_unwrap(&min, *context, &enc, &dec, NULL, NULL); - if(maj != GSS_S_COMPLETE) - return -1; - - memcpy(buf, dec.value, dec.length); - len = curlx_uztosi(dec.length); - gss_release_buffer(&min, &dec); - - return len; -} - -static int -krb5_encode(void *app_data, const void *from, int length, int level, void **to) -{ - gss_ctx_id_t *context = app_data; - gss_buffer_desc dec, enc; - OM_uint32 maj, min; - int state; - int len; - - /* NOTE that the cast is safe, neither of the krb5, gnu gss and heimdal - * libraries modify the input buffer in gss_wrap() - */ - dec.value = (void *)from; - dec.length = length; - maj = gss_wrap(&min, *context, - level == PROT_PRIVATE, - GSS_C_QOP_DEFAULT, - &dec, &state, &enc); - - if(maj != GSS_S_COMPLETE) - return -1; - - /* malloc a new buffer, in case gss_release_buffer doesn't work as - expected */ - *to = malloc(enc.length); - if(!*to) - return -1; - memcpy(*to, enc.value, enc.length); - len = curlx_uztosi(enc.length); - gss_release_buffer(&min, &enc); - return len; -} - -static int -krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn) -{ - int ret = AUTH_OK; - char *p; - const char *host = conn->host.name; - ssize_t nread; - curl_socklen_t l = sizeof(conn->local_addr); - CURLcode result; - const char *service = data->set.str[STRING_SERVICE_NAME] ? - data->set.str[STRING_SERVICE_NAME] : - "ftp"; - const char *srv_host = "host"; - gss_buffer_desc input_buffer, output_buffer, _gssresp, *gssresp; - OM_uint32 maj, min; - gss_name_t gssname; - gss_ctx_id_t *context = app_data; - struct gss_channel_bindings_struct chan; - size_t base64_sz = 0; - struct sockaddr_in **remote_addr = - (struct sockaddr_in **)&conn->ip_addr->ai_addr; - char *stringp; - - if(getsockname(conn->sock[FIRSTSOCKET], - (struct sockaddr *)&conn->local_addr, &l) < 0) - perror("getsockname()"); - - chan.initiator_addrtype = GSS_C_AF_INET; - chan.initiator_address.length = l - 4; - chan.initiator_address.value = &conn->local_addr.sin_addr.s_addr; - chan.acceptor_addrtype = GSS_C_AF_INET; - chan.acceptor_address.length = l - 4; - chan.acceptor_address.value = &(*remote_addr)->sin_addr.s_addr; - chan.application_data.length = 0; - chan.application_data.value = NULL; - - /* this loop will execute twice (once for service, once for host) */ - for(;;) { - /* this really shouldn't be repeated here, but can't help it */ - if(service == srv_host) { - result = ftpsend(data, conn, "AUTH GSSAPI"); - if(result) - return -2; - - if(Curl_GetFTPResponse(data, &nread, NULL)) - return -1; - - if(data->state.buffer[0] != '3') - return -1; - } - - stringp = aprintf("%s@%s", service, host); - if(!stringp) - return -2; - - input_buffer.value = stringp; - input_buffer.length = strlen(stringp); - maj = gss_import_name(&min, &input_buffer, GSS_C_NT_HOSTBASED_SERVICE, - &gssname); - free(stringp); - if(maj != GSS_S_COMPLETE) { - gss_release_name(&min, &gssname); - if(service == srv_host) { - failf(data, "Error importing service name %s@%s", service, host); - return AUTH_ERROR; - } - service = srv_host; - continue; - } - /* We pass NULL as |output_name_type| to avoid a leak. */ - gss_display_name(&min, gssname, &output_buffer, NULL); - infof(data, "Trying against %s", output_buffer.value); - gssresp = GSS_C_NO_BUFFER; - *context = GSS_C_NO_CONTEXT; - - do { - /* Release the buffer at each iteration to avoid leaking: the first time - we are releasing the memory from gss_display_name. The last item is - taken care by a final gss_release_buffer. */ - gss_release_buffer(&min, &output_buffer); - ret = AUTH_OK; - maj = Curl_gss_init_sec_context(data, - &min, - context, - gssname, - &Curl_krb5_mech_oid, - &chan, - gssresp, - &output_buffer, - TRUE, - NULL); - - if(gssresp) { - free(_gssresp.value); - gssresp = NULL; - } - - if(GSS_ERROR(maj)) { - infof(data, "Error creating security context"); - ret = AUTH_ERROR; - break; - } - - if(output_buffer.length) { - char *cmd; - - result = Curl_base64_encode((char *)output_buffer.value, - output_buffer.length, &p, &base64_sz); - if(result) { - infof(data, "base64-encoding: %s", curl_easy_strerror(result)); - ret = AUTH_ERROR; - break; - } - - cmd = aprintf("ADAT %s", p); - if(cmd) - result = ftpsend(data, conn, cmd); - else - result = CURLE_OUT_OF_MEMORY; - - free(p); - free(cmd); - - if(result) { - ret = -2; - break; - } - - if(Curl_GetFTPResponse(data, &nread, NULL)) { - ret = -1; - break; - } - - if(data->state.buffer[0] != '2' && data->state.buffer[0] != '3') { - infof(data, "Server didn't accept auth data"); - ret = AUTH_ERROR; - break; - } - - _gssresp.value = NULL; /* make sure it is initialized */ - p = data->state.buffer + 4; - p = strstr(p, "ADAT="); - if(p) { - result = Curl_base64_decode(p + 5, - (unsigned char **)&_gssresp.value, - &_gssresp.length); - if(result) { - failf(data, "base64-decoding: %s", curl_easy_strerror(result)); - ret = AUTH_CONTINUE; - break; - } - } - - gssresp = &_gssresp; - } - } while(maj == GSS_S_CONTINUE_NEEDED); - - gss_release_name(&min, &gssname); - gss_release_buffer(&min, &output_buffer); - - if(gssresp) - free(_gssresp.value); - - if(ret == AUTH_OK || service == srv_host) - return ret; - - service = srv_host; - } - return ret; -} - -static void krb5_end(void *app_data) -{ - OM_uint32 min; - gss_ctx_id_t *context = app_data; - if(*context != GSS_C_NO_CONTEXT) { - OM_uint32 maj = gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER); - (void)maj; - DEBUGASSERT(maj == GSS_S_COMPLETE); - } -} - -static const struct Curl_sec_client_mech Curl_krb5_client_mech = { - "GSSAPI", - sizeof(gss_ctx_id_t), - krb5_init, - krb5_auth, - krb5_end, - krb5_check_prot, - - krb5_encode, - krb5_decode -}; - -static const struct { - enum protection_level level; - const char *name; -} level_names[] = { - { PROT_CLEAR, "clear" }, - { PROT_SAFE, "safe" }, - { PROT_CONFIDENTIAL, "confidential" }, - { PROT_PRIVATE, "private" } -}; - -static enum protection_level -name_to_level(const char *name) -{ - int i; - for(i = 0; i < (int)sizeof(level_names)/(int)sizeof(level_names[0]); i++) - if(curl_strequal(name, level_names[i].name)) - return level_names[i].level; - return PROT_NONE; -} - -/* Convert a protocol |level| to its char representation. - We take an int to catch programming mistakes. */ -static char level_to_char(int level) -{ - switch(level) { - case PROT_CLEAR: - return 'C'; - case PROT_SAFE: - return 'S'; - case PROT_CONFIDENTIAL: - return 'E'; - case PROT_PRIVATE: - return 'P'; - case PROT_CMD: - /* Fall through */ - default: - /* Those 2 cases should not be reached! */ - break; - } - DEBUGASSERT(0); - /* Default to the most secure alternative. */ - return 'P'; -} - -/* Send an FTP command defined by |message| and the optional arguments. The - function returns the ftp_code. If an error occurs, -1 is returned. */ -static int ftp_send_command(struct Curl_easy *data, const char *message, ...) -{ - int ftp_code; - ssize_t nread = 0; - va_list args; - char print_buffer[50]; - - va_start(args, message); - mvsnprintf(print_buffer, sizeof(print_buffer), message, args); - va_end(args); - - if(ftpsend(data, data->conn, print_buffer)) { - ftp_code = -1; - } - else { - if(Curl_GetFTPResponse(data, &nread, &ftp_code)) - ftp_code = -1; - } - - (void)nread; /* Unused */ - return ftp_code; -} - -/* Read |len| from the socket |fd| and store it in |to|. Return a CURLcode - saying whether an error occurred or CURLE_OK if |len| was read. */ -static CURLcode -socket_read(curl_socket_t fd, void *to, size_t len) -{ - char *to_p = to; - CURLcode result; - ssize_t nread = 0; - - while(len > 0) { - result = Curl_read_plain(fd, to_p, len, &nread); - if(!result) { - len -= nread; - to_p += nread; - } - else { - if(result == CURLE_AGAIN) - continue; - return result; - } - } - return CURLE_OK; -} - - -/* Write |len| bytes from the buffer |to| to the socket |fd|. Return a - CURLcode saying whether an error occurred or CURLE_OK if |len| was - written. */ -static CURLcode -socket_write(struct Curl_easy *data, curl_socket_t fd, const void *to, - size_t len) -{ - const char *to_p = to; - CURLcode result; - ssize_t written; - - while(len > 0) { - result = Curl_write_plain(data, fd, to_p, len, &written); - if(!result) { - len -= written; - to_p += written; - } - else { - if(result == CURLE_AGAIN) - continue; - return result; - } - } - return CURLE_OK; -} - -static CURLcode read_data(struct connectdata *conn, - curl_socket_t fd, - struct krb5buffer *buf) -{ - int len; - CURLcode result; - int nread; - - result = socket_read(fd, &len, sizeof(len)); - if(result) - return result; - - if(len) { - /* only realloc if there was a length */ - len = ntohl(len); - if(len > CURL_MAX_INPUT_LENGTH) - len = 0; - else - buf->data = Curl_saferealloc(buf->data, len); - } - if(!len || !buf->data) - return CURLE_OUT_OF_MEMORY; - - result = socket_read(fd, buf->data, len); - if(result) - return result; - nread = conn->mech->decode(conn->app_data, buf->data, len, - conn->data_prot, conn); - if(nread < 0) - return CURLE_RECV_ERROR; - buf->size = (size_t)nread; - buf->index = 0; - return CURLE_OK; -} - -static size_t -buffer_read(struct krb5buffer *buf, void *data, size_t len) -{ - if(buf->size - buf->index < len) - len = buf->size - buf->index; - memcpy(data, (char *)buf->data + buf->index, len); - buf->index += len; - return len; -} - -/* Matches Curl_recv signature */ -static ssize_t sec_recv(struct Curl_easy *data, int sockindex, - char *buffer, size_t len, CURLcode *err) -{ - size_t bytes_read; - size_t total_read = 0; - struct connectdata *conn = data->conn; - curl_socket_t fd = conn->sock[sockindex]; - - *err = CURLE_OK; - - /* Handle clear text response. */ - if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR) - return sread(fd, buffer, len); - - if(conn->in_buffer.eof_flag) { - conn->in_buffer.eof_flag = 0; - return 0; - } - - bytes_read = buffer_read(&conn->in_buffer, buffer, len); - len -= bytes_read; - total_read += bytes_read; - buffer += bytes_read; - - while(len > 0) { - if(read_data(conn, fd, &conn->in_buffer)) - return -1; - if(conn->in_buffer.size == 0) { - if(bytes_read > 0) - conn->in_buffer.eof_flag = 1; - return bytes_read; - } - bytes_read = buffer_read(&conn->in_buffer, buffer, len); - len -= bytes_read; - total_read += bytes_read; - buffer += bytes_read; - } - return total_read; -} - -/* Send |length| bytes from |from| to the |fd| socket taking care of encoding - and negotiating with the server. |from| can be NULL. */ -static void do_sec_send(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t fd, const char *from, int length) -{ - int bytes, htonl_bytes; /* 32-bit integers for htonl */ - char *buffer = NULL; - char *cmd_buffer; - size_t cmd_size = 0; - CURLcode error; - enum protection_level prot_level = conn->data_prot; - bool iscmd = (prot_level == PROT_CMD)?TRUE:FALSE; - - DEBUGASSERT(prot_level > PROT_NONE && prot_level < PROT_LAST); - - if(iscmd) { - if(!strncmp(from, "PASS ", 5) || !strncmp(from, "ACCT ", 5)) - prot_level = PROT_PRIVATE; - else - prot_level = conn->command_prot; - } - bytes = conn->mech->encode(conn->app_data, from, length, prot_level, - (void **)&buffer); - if(!buffer || bytes <= 0) - return; /* error */ - - if(iscmd) { - error = Curl_base64_encode(buffer, curlx_sitouz(bytes), - &cmd_buffer, &cmd_size); - if(error) { - free(buffer); - return; /* error */ - } - if(cmd_size > 0) { - static const char *enc = "ENC "; - static const char *mic = "MIC "; - if(prot_level == PROT_PRIVATE) - socket_write(data, fd, enc, 4); - else - socket_write(data, fd, mic, 4); - - socket_write(data, fd, cmd_buffer, cmd_size); - socket_write(data, fd, "\r\n", 2); - infof(data, "Send: %s%s", prot_level == PROT_PRIVATE?enc:mic, - cmd_buffer); - free(cmd_buffer); - } - } - else { - htonl_bytes = htonl(bytes); - socket_write(data, fd, &htonl_bytes, sizeof(htonl_bytes)); - socket_write(data, fd, buffer, curlx_sitouz(bytes)); - } - free(buffer); -} - -static ssize_t sec_write(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t fd, const char *buffer, size_t length) -{ - ssize_t tx = 0, len = conn->buffer_size; - - if(len <= 0) - len = length; - while(length) { - if(length < (size_t)len) - len = length; - - do_sec_send(data, conn, fd, buffer, curlx_sztosi(len)); - length -= len; - buffer += len; - tx += len; - } - return tx; -} - -/* Matches Curl_send signature */ -static ssize_t sec_send(struct Curl_easy *data, int sockindex, - const void *buffer, size_t len, CURLcode *err) -{ - struct connectdata *conn = data->conn; - curl_socket_t fd = conn->sock[sockindex]; - *err = CURLE_OK; - return sec_write(data, conn, fd, buffer, len); -} - -int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn, - char *buffer, enum protection_level level) -{ - /* decoded_len should be size_t or ssize_t but conn->mech->decode returns an - int */ - int decoded_len; - char *buf; - int ret_code = 0; - size_t decoded_sz = 0; - CURLcode error; - - (void) data; - - if(!conn->mech) - /* not initialized, return error */ - return -1; - - DEBUGASSERT(level > PROT_NONE && level < PROT_LAST); - - error = Curl_base64_decode(buffer + 4, (unsigned char **)&buf, &decoded_sz); - if(error || decoded_sz == 0) - return -1; - - if(decoded_sz > (size_t)INT_MAX) { - free(buf); - return -1; - } - decoded_len = curlx_uztosi(decoded_sz); - - decoded_len = conn->mech->decode(conn->app_data, buf, decoded_len, - level, conn); - if(decoded_len <= 0) { - free(buf); - return -1; - } - - { - buf[decoded_len] = '\n'; - Curl_debug(data, CURLINFO_HEADER_IN, buf, decoded_len + 1); - } - - buf[decoded_len] = '\0'; - if(decoded_len <= 3) - /* suspiciously short */ - return 0; - - if(buf[3] != '-') - /* safe to ignore return code */ - (void)sscanf(buf, "%d", &ret_code); - - if(buf[decoded_len - 1] == '\n') - buf[decoded_len - 1] = '\0'; - strcpy(buffer, buf); - free(buf); - return ret_code; -} - -static int sec_set_protection_level(struct Curl_easy *data) -{ - int code; - struct connectdata *conn = data->conn; - enum protection_level level = conn->request_data_prot; - - DEBUGASSERT(level > PROT_NONE && level < PROT_LAST); - - if(!conn->sec_complete) { - infof(data, "Trying to change the protection level after the" - " completion of the data exchange."); - return -1; - } - - /* Bail out if we try to set up the same level */ - if(conn->data_prot == level) - return 0; - - if(level) { - char *pbsz; - unsigned int buffer_size = 1 << 20; /* 1048576 */ - - code = ftp_send_command(data, "PBSZ %u", buffer_size); - if(code < 0) - return -1; - - if(code/100 != 2) { - failf(data, "Failed to set the protection's buffer size."); - return -1; - } - conn->buffer_size = buffer_size; - - pbsz = strstr(data->state.buffer, "PBSZ="); - if(pbsz) { - /* ignore return code, use default value if it fails */ - (void)sscanf(pbsz, "PBSZ=%u", &buffer_size); - if(buffer_size < conn->buffer_size) - conn->buffer_size = buffer_size; - } - } - - /* Now try to negotiate the protection level. */ - code = ftp_send_command(data, "PROT %c", level_to_char(level)); - - if(code < 0) - return -1; - - if(code/100 != 2) { - failf(data, "Failed to set the protection level."); - return -1; - } - - conn->data_prot = level; - if(level == PROT_PRIVATE) - conn->command_prot = level; - - return 0; -} - -int -Curl_sec_request_prot(struct connectdata *conn, const char *level) -{ - enum protection_level l = name_to_level(level); - if(l == PROT_NONE) - return -1; - DEBUGASSERT(l > PROT_NONE && l < PROT_LAST); - conn->request_data_prot = l; - return 0; -} - -static CURLcode choose_mech(struct Curl_easy *data, struct connectdata *conn) -{ - int ret; - void *tmp_allocation; - const struct Curl_sec_client_mech *mech = &Curl_krb5_client_mech; - - tmp_allocation = realloc(conn->app_data, mech->size); - if(!tmp_allocation) { - failf(data, "Failed realloc of size %zu", mech->size); - mech = NULL; - return CURLE_OUT_OF_MEMORY; - } - conn->app_data = tmp_allocation; - - if(mech->init) { - ret = mech->init(conn->app_data); - if(ret) { - infof(data, "Failed initialization for %s. Skipping it.", - mech->name); - return CURLE_FAILED_INIT; - } - } - - infof(data, "Trying mechanism %s...", mech->name); - ret = ftp_send_command(data, "AUTH %s", mech->name); - if(ret < 0) - return CURLE_COULDNT_CONNECT; - - if(ret/100 != 3) { - switch(ret) { - case 504: - infof(data, "Mechanism %s is not supported by the server (server " - "returned ftp code: 504).", mech->name); - break; - case 534: - infof(data, "Mechanism %s was rejected by the server (server returned " - "ftp code: 534).", mech->name); - break; - default: - if(ret/100 == 5) { - infof(data, "server does not support the security extensions"); - return CURLE_USE_SSL_FAILED; - } - break; - } - return CURLE_LOGIN_DENIED; - } - - /* Authenticate */ - ret = mech->auth(conn->app_data, data, conn); - - if(ret != AUTH_CONTINUE) { - if(ret != AUTH_OK) { - /* Mechanism has dumped the error to stderr, don't error here. */ - return CURLE_USE_SSL_FAILED; - } - DEBUGASSERT(ret == AUTH_OK); - - conn->mech = mech; - conn->sec_complete = 1; - conn->recv[FIRSTSOCKET] = sec_recv; - conn->send[FIRSTSOCKET] = sec_send; - conn->recv[SECONDARYSOCKET] = sec_recv; - conn->send[SECONDARYSOCKET] = sec_send; - conn->command_prot = PROT_SAFE; - /* Set the requested protection level */ - /* BLOCKING */ - (void)sec_set_protection_level(data); - } - - return CURLE_OK; -} - -CURLcode -Curl_sec_login(struct Curl_easy *data, struct connectdata *conn) -{ - return choose_mech(data, conn); -} - - -void -Curl_sec_end(struct connectdata *conn) -{ - if(conn->mech && conn->mech->end) - conn->mech->end(conn->app_data); - free(conn->app_data); - conn->app_data = NULL; - if(conn->in_buffer.data) { - free(conn->in_buffer.data); - conn->in_buffer.data = NULL; - conn->in_buffer.size = 0; - conn->in_buffer.index = 0; - conn->in_buffer.eof_flag = 0; - } - conn->sec_complete = 0; - conn->data_prot = PROT_CLEAR; - conn->mech = NULL; -} - -#endif /* HAVE_GSSAPI && !CURL_DISABLE_FTP */ +/* GSSAPI/krb5 support for FTP - loosely based on old krb4.c
+ *
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * Copyright (c) 2004 - 2022 Daniel Stenberg
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE. */
+
+#include "curl_setup.h"
+
+#if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_FTP)
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include "urldata.h"
+#include "curl_base64.h"
+#include "ftp.h"
+#include "curl_gssapi.h"
+#include "sendf.h"
+#include "curl_krb5.h"
+#include "warnless.h"
+#include "strcase.h"
+#include "strdup.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn,
+ const char *cmd)
+{
+ ssize_t bytes_written;
+#define SBUF_SIZE 1024
+ char s[SBUF_SIZE];
+ size_t write_len;
+ char *sptr = s;
+ CURLcode result = CURLE_OK;
+#ifdef HAVE_GSSAPI
+ enum protection_level data_sec = conn->data_prot;
+#endif
+
+ if(!cmd)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ write_len = strlen(cmd);
+ if(!write_len || write_len > (sizeof(s) -3))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ memcpy(&s, cmd, write_len);
+ strcpy(&s[write_len], "\r\n"); /* append a trailing CRLF */
+ write_len += 2;
+ bytes_written = 0;
+
+ for(;;) {
+#ifdef HAVE_GSSAPI
+ conn->data_prot = PROT_CMD;
+#endif
+ result = Curl_write(data, conn->sock[FIRSTSOCKET], sptr, write_len,
+ &bytes_written);
+#ifdef HAVE_GSSAPI
+ DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
+ conn->data_prot = data_sec;
+#endif
+
+ if(result)
+ break;
+
+ Curl_debug(data, CURLINFO_HEADER_OUT, sptr, (size_t)bytes_written);
+
+ if(bytes_written != (ssize_t)write_len) {
+ write_len -= bytes_written;
+ sptr += bytes_written;
+ }
+ else
+ break;
+ }
+
+ return result;
+}
+
+static int
+krb5_init(void *app_data)
+{
+ gss_ctx_id_t *context = app_data;
+ /* Make sure our context is initialized for krb5_end. */
+ *context = GSS_C_NO_CONTEXT;
+ return 0;
+}
+
+static int
+krb5_check_prot(void *app_data, int level)
+{
+ (void)app_data; /* unused */
+ if(level == PROT_CONFIDENTIAL)
+ return -1;
+ return 0;
+}
+
+static int
+krb5_decode(void *app_data, void *buf, int len,
+ int level UNUSED_PARAM,
+ struct connectdata *conn UNUSED_PARAM)
+{
+ gss_ctx_id_t *context = app_data;
+ OM_uint32 maj, min;
+ gss_buffer_desc enc, dec;
+
+ (void)level;
+ (void)conn;
+
+ enc.value = buf;
+ enc.length = len;
+ maj = gss_unwrap(&min, *context, &enc, &dec, NULL, NULL);
+ if(maj != GSS_S_COMPLETE)
+ return -1;
+
+ memcpy(buf, dec.value, dec.length);
+ len = curlx_uztosi(dec.length);
+ gss_release_buffer(&min, &dec);
+
+ return len;
+}
+
+static int
+krb5_encode(void *app_data, const void *from, int length, int level, void **to)
+{
+ gss_ctx_id_t *context = app_data;
+ gss_buffer_desc dec, enc;
+ OM_uint32 maj, min;
+ int state;
+ int len;
+
+ /* NOTE that the cast is safe, neither of the krb5, gnu gss and heimdal
+ * libraries modify the input buffer in gss_wrap()
+ */
+ dec.value = (void *)from;
+ dec.length = length;
+ maj = gss_wrap(&min, *context,
+ level == PROT_PRIVATE,
+ GSS_C_QOP_DEFAULT,
+ &dec, &state, &enc);
+
+ if(maj != GSS_S_COMPLETE)
+ return -1;
+
+ /* malloc a new buffer, in case gss_release_buffer doesn't work as
+ expected */
+ *to = malloc(enc.length);
+ if(!*to)
+ return -1;
+ memcpy(*to, enc.value, enc.length);
+ len = curlx_uztosi(enc.length);
+ gss_release_buffer(&min, &enc);
+ return len;
+}
+
+static int
+krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
+{
+ int ret = AUTH_OK;
+ char *p;
+ const char *host = conn->host.name;
+ ssize_t nread;
+ curl_socklen_t l = sizeof(conn->local_addr);
+ CURLcode result;
+ const char *service = data->set.str[STRING_SERVICE_NAME] ?
+ data->set.str[STRING_SERVICE_NAME] :
+ "ftp";
+ const char *srv_host = "host";
+ gss_buffer_desc input_buffer, output_buffer, _gssresp, *gssresp;
+ OM_uint32 maj, min;
+ gss_name_t gssname;
+ gss_ctx_id_t *context = app_data;
+ struct gss_channel_bindings_struct chan;
+ size_t base64_sz = 0;
+ struct sockaddr_in **remote_addr =
+ (struct sockaddr_in **)&conn->ip_addr->ai_addr;
+ char *stringp;
+
+ if(getsockname(conn->sock[FIRSTSOCKET],
+ (struct sockaddr *)&conn->local_addr, &l) < 0)
+ perror("getsockname()");
+
+ chan.initiator_addrtype = GSS_C_AF_INET;
+ chan.initiator_address.length = l - 4;
+ chan.initiator_address.value = &conn->local_addr.sin_addr.s_addr;
+ chan.acceptor_addrtype = GSS_C_AF_INET;
+ chan.acceptor_address.length = l - 4;
+ chan.acceptor_address.value = &(*remote_addr)->sin_addr.s_addr;
+ chan.application_data.length = 0;
+ chan.application_data.value = NULL;
+
+ /* this loop will execute twice (once for service, once for host) */
+ for(;;) {
+ /* this really shouldn't be repeated here, but can't help it */
+ if(service == srv_host) {
+ result = ftpsend(data, conn, "AUTH GSSAPI");
+ if(result)
+ return -2;
+
+ if(Curl_GetFTPResponse(data, &nread, NULL))
+ return -1;
+
+ if(data->state.buffer[0] != '3')
+ return -1;
+ }
+
+ stringp = aprintf("%s@%s", service, host);
+ if(!stringp)
+ return -2;
+
+ input_buffer.value = stringp;
+ input_buffer.length = strlen(stringp);
+ maj = gss_import_name(&min, &input_buffer, GSS_C_NT_HOSTBASED_SERVICE,
+ &gssname);
+ free(stringp);
+ if(maj != GSS_S_COMPLETE) {
+ gss_release_name(&min, &gssname);
+ if(service == srv_host) {
+ failf(data, "Error importing service name %s@%s", service, host);
+ return AUTH_ERROR;
+ }
+ service = srv_host;
+ continue;
+ }
+ /* We pass NULL as |output_name_type| to avoid a leak. */
+ gss_display_name(&min, gssname, &output_buffer, NULL);
+ infof(data, "Trying against %s", output_buffer.value);
+ gssresp = GSS_C_NO_BUFFER;
+ *context = GSS_C_NO_CONTEXT;
+
+ do {
+ /* Release the buffer at each iteration to avoid leaking: the first time
+ we are releasing the memory from gss_display_name. The last item is
+ taken care by a final gss_release_buffer. */
+ gss_release_buffer(&min, &output_buffer);
+ ret = AUTH_OK;
+ maj = Curl_gss_init_sec_context(data,
+ &min,
+ context,
+ gssname,
+ &Curl_krb5_mech_oid,
+ &chan,
+ gssresp,
+ &output_buffer,
+ TRUE,
+ NULL);
+
+ if(gssresp) {
+ free(_gssresp.value);
+ gssresp = NULL;
+ }
+
+ if(GSS_ERROR(maj)) {
+ infof(data, "Error creating security context");
+ ret = AUTH_ERROR;
+ break;
+ }
+
+ if(output_buffer.length) {
+ char *cmd;
+
+ result = Curl_base64_encode((char *)output_buffer.value,
+ output_buffer.length, &p, &base64_sz);
+ if(result) {
+ infof(data, "base64-encoding: %s", curl_easy_strerror(result));
+ ret = AUTH_ERROR;
+ break;
+ }
+
+ cmd = aprintf("ADAT %s", p);
+ if(cmd)
+ result = ftpsend(data, conn, cmd);
+ else
+ result = CURLE_OUT_OF_MEMORY;
+
+ free(p);
+ free(cmd);
+
+ if(result) {
+ ret = -2;
+ break;
+ }
+
+ if(Curl_GetFTPResponse(data, &nread, NULL)) {
+ ret = -1;
+ break;
+ }
+
+ if(data->state.buffer[0] != '2' && data->state.buffer[0] != '3') {
+ infof(data, "Server didn't accept auth data");
+ ret = AUTH_ERROR;
+ break;
+ }
+
+ _gssresp.value = NULL; /* make sure it is initialized */
+ p = data->state.buffer + 4;
+ p = strstr(p, "ADAT=");
+ if(p) {
+ result = Curl_base64_decode(p + 5,
+ (unsigned char **)&_gssresp.value,
+ &_gssresp.length);
+ if(result) {
+ failf(data, "base64-decoding: %s", curl_easy_strerror(result));
+ ret = AUTH_CONTINUE;
+ break;
+ }
+ }
+
+ gssresp = &_gssresp;
+ }
+ } while(maj == GSS_S_CONTINUE_NEEDED);
+
+ gss_release_name(&min, &gssname);
+ gss_release_buffer(&min, &output_buffer);
+
+ if(gssresp)
+ free(_gssresp.value);
+
+ if(ret == AUTH_OK || service == srv_host)
+ return ret;
+
+ service = srv_host;
+ }
+ return ret;
+}
+
+static void krb5_end(void *app_data)
+{
+ OM_uint32 min;
+ gss_ctx_id_t *context = app_data;
+ if(*context != GSS_C_NO_CONTEXT) {
+ OM_uint32 maj = gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER);
+ (void)maj;
+ DEBUGASSERT(maj == GSS_S_COMPLETE);
+ }
+}
+
+static const struct Curl_sec_client_mech Curl_krb5_client_mech = {
+ "GSSAPI",
+ sizeof(gss_ctx_id_t),
+ krb5_init,
+ krb5_auth,
+ krb5_end,
+ krb5_check_prot,
+
+ krb5_encode,
+ krb5_decode
+};
+
+static const struct {
+ enum protection_level level;
+ const char *name;
+} level_names[] = {
+ { PROT_CLEAR, "clear" },
+ { PROT_SAFE, "safe" },
+ { PROT_CONFIDENTIAL, "confidential" },
+ { PROT_PRIVATE, "private" }
+};
+
+static enum protection_level
+name_to_level(const char *name)
+{
+ int i;
+ for(i = 0; i < (int)sizeof(level_names)/(int)sizeof(level_names[0]); i++)
+ if(curl_strequal(name, level_names[i].name))
+ return level_names[i].level;
+ return PROT_NONE;
+}
+
+/* Convert a protocol |level| to its char representation.
+ We take an int to catch programming mistakes. */
+static char level_to_char(int level)
+{
+ switch(level) {
+ case PROT_CLEAR:
+ return 'C';
+ case PROT_SAFE:
+ return 'S';
+ case PROT_CONFIDENTIAL:
+ return 'E';
+ case PROT_PRIVATE:
+ return 'P';
+ case PROT_CMD:
+ /* Fall through */
+ default:
+ /* Those 2 cases should not be reached! */
+ break;
+ }
+ DEBUGASSERT(0);
+ /* Default to the most secure alternative. */
+ return 'P';
+}
+
+/* Send an FTP command defined by |message| and the optional arguments. The
+ function returns the ftp_code. If an error occurs, -1 is returned. */
+static int ftp_send_command(struct Curl_easy *data, const char *message, ...)
+{
+ int ftp_code;
+ ssize_t nread = 0;
+ va_list args;
+ char print_buffer[50];
+
+ va_start(args, message);
+ mvsnprintf(print_buffer, sizeof(print_buffer), message, args);
+ va_end(args);
+
+ if(ftpsend(data, data->conn, print_buffer)) {
+ ftp_code = -1;
+ }
+ else {
+ if(Curl_GetFTPResponse(data, &nread, &ftp_code))
+ ftp_code = -1;
+ }
+
+ (void)nread; /* Unused */
+ return ftp_code;
+}
+
+/* Read |len| from the socket |fd| and store it in |to|. Return a CURLcode
+ saying whether an error occurred or CURLE_OK if |len| was read. */
+static CURLcode
+socket_read(struct Curl_easy *data, curl_socket_t fd, void *to, size_t len)
+{
+ char *to_p = to;
+ CURLcode result;
+ ssize_t nread = 0;
+
+ while(len > 0) {
+ result = Curl_read_plain(data, fd, to_p, len, &nread);
+ if(!result) {
+ len -= nread;
+ to_p += nread;
+ }
+ else {
+ if(result == CURLE_AGAIN)
+ continue;
+ return result;
+ }
+ }
+ return CURLE_OK;
+}
+
+
+/* Write |len| bytes from the buffer |to| to the socket |fd|. Return a
+ CURLcode saying whether an error occurred or CURLE_OK if |len| was
+ written. */
+static CURLcode
+socket_write(struct Curl_easy *data, curl_socket_t fd, const void *to,
+ size_t len)
+{
+ const char *to_p = to;
+ CURLcode result;
+ ssize_t written;
+
+ while(len > 0) {
+ result = Curl_write_plain(data, fd, to_p, len, &written);
+ if(!result) {
+ len -= written;
+ to_p += written;
+ }
+ else {
+ if(result == CURLE_AGAIN)
+ continue;
+ return result;
+ }
+ }
+ return CURLE_OK;
+}
+
+static CURLcode read_data(struct Curl_easy *data, curl_socket_t fd,
+ struct krb5buffer *buf)
+{
+ struct connectdata *conn = data->conn;
+ int len;
+ CURLcode result;
+ int nread;
+
+ result = socket_read(data, fd, &len, sizeof(len));
+ if(result)
+ return result;
+
+ if(len) {
+ /* only realloc if there was a length */
+ len = ntohl(len);
+ if(len > CURL_MAX_INPUT_LENGTH)
+ len = 0;
+ else
+ buf->data = Curl_saferealloc(buf->data, len);
+ }
+ if(!len || !buf->data)
+ return CURLE_OUT_OF_MEMORY;
+
+ result = socket_read(data, fd, buf->data, len);
+ if(result)
+ return result;
+ nread = conn->mech->decode(conn->app_data, buf->data, len,
+ conn->data_prot, conn);
+ if(nread < 0)
+ return CURLE_RECV_ERROR;
+ buf->size = (size_t)nread;
+ buf->index = 0;
+ return CURLE_OK;
+}
+
+static size_t
+buffer_read(struct krb5buffer *buf, void *data, size_t len)
+{
+ if(buf->size - buf->index < len)
+ len = buf->size - buf->index;
+ memcpy(data, (char *)buf->data + buf->index, len);
+ buf->index += len;
+ return len;
+}
+
+/* Matches Curl_recv signature */
+static ssize_t sec_recv(struct Curl_easy *data, int sockindex,
+ char *buffer, size_t len, CURLcode *err)
+{
+ size_t bytes_read;
+ size_t total_read = 0;
+ struct connectdata *conn = data->conn;
+ curl_socket_t fd = conn->sock[sockindex];
+
+ *err = CURLE_OK;
+
+ /* Handle clear text response. */
+ if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR)
+ return Curl_recv_plain(data, sockindex, buffer, len, err);
+
+ if(conn->in_buffer.eof_flag) {
+ conn->in_buffer.eof_flag = 0;
+ return 0;
+ }
+
+ bytes_read = buffer_read(&conn->in_buffer, buffer, len);
+ len -= bytes_read;
+ total_read += bytes_read;
+ buffer += bytes_read;
+
+ while(len > 0) {
+ if(read_data(data, fd, &conn->in_buffer))
+ return -1;
+ if(conn->in_buffer.size == 0) {
+ if(bytes_read > 0)
+ conn->in_buffer.eof_flag = 1;
+ return bytes_read;
+ }
+ bytes_read = buffer_read(&conn->in_buffer, buffer, len);
+ len -= bytes_read;
+ total_read += bytes_read;
+ buffer += bytes_read;
+ }
+ return total_read;
+}
+
+/* Send |length| bytes from |from| to the |fd| socket taking care of encoding
+ and negotiating with the server. |from| can be NULL. */
+static void do_sec_send(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t fd, const char *from, int length)
+{
+ int bytes, htonl_bytes; /* 32-bit integers for htonl */
+ char *buffer = NULL;
+ char *cmd_buffer;
+ size_t cmd_size = 0;
+ CURLcode error;
+ enum protection_level prot_level = conn->data_prot;
+ bool iscmd = (prot_level == PROT_CMD)?TRUE:FALSE;
+
+ DEBUGASSERT(prot_level > PROT_NONE && prot_level < PROT_LAST);
+
+ if(iscmd) {
+ if(!strncmp(from, "PASS ", 5) || !strncmp(from, "ACCT ", 5))
+ prot_level = PROT_PRIVATE;
+ else
+ prot_level = conn->command_prot;
+ }
+ bytes = conn->mech->encode(conn->app_data, from, length, prot_level,
+ (void **)&buffer);
+ if(!buffer || bytes <= 0)
+ return; /* error */
+
+ if(iscmd) {
+ error = Curl_base64_encode(buffer, curlx_sitouz(bytes),
+ &cmd_buffer, &cmd_size);
+ if(error) {
+ free(buffer);
+ return; /* error */
+ }
+ if(cmd_size > 0) {
+ static const char *enc = "ENC ";
+ static const char *mic = "MIC ";
+ if(prot_level == PROT_PRIVATE)
+ socket_write(data, fd, enc, 4);
+ else
+ socket_write(data, fd, mic, 4);
+
+ socket_write(data, fd, cmd_buffer, cmd_size);
+ socket_write(data, fd, "\r\n", 2);
+ infof(data, "Send: %s%s", prot_level == PROT_PRIVATE?enc:mic,
+ cmd_buffer);
+ free(cmd_buffer);
+ }
+ }
+ else {
+ htonl_bytes = htonl(bytes);
+ socket_write(data, fd, &htonl_bytes, sizeof(htonl_bytes));
+ socket_write(data, fd, buffer, curlx_sitouz(bytes));
+ }
+ free(buffer);
+}
+
+static ssize_t sec_write(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t fd, const char *buffer, size_t length)
+{
+ ssize_t tx = 0, len = conn->buffer_size;
+
+ if(len <= 0)
+ len = length;
+ while(length) {
+ if(length < (size_t)len)
+ len = length;
+
+ do_sec_send(data, conn, fd, buffer, curlx_sztosi(len));
+ length -= len;
+ buffer += len;
+ tx += len;
+ }
+ return tx;
+}
+
+/* Matches Curl_send signature */
+static ssize_t sec_send(struct Curl_easy *data, int sockindex,
+ const void *buffer, size_t len, CURLcode *err)
+{
+ struct connectdata *conn = data->conn;
+ curl_socket_t fd = conn->sock[sockindex];
+ *err = CURLE_OK;
+ return sec_write(data, conn, fd, buffer, len);
+}
+
+int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn,
+ char *buffer, enum protection_level level)
+{
+ /* decoded_len should be size_t or ssize_t but conn->mech->decode returns an
+ int */
+ int decoded_len;
+ char *buf;
+ int ret_code = 0;
+ size_t decoded_sz = 0;
+ CURLcode error;
+
+ (void) data;
+
+ if(!conn->mech)
+ /* not initialized, return error */
+ return -1;
+
+ DEBUGASSERT(level > PROT_NONE && level < PROT_LAST);
+
+ error = Curl_base64_decode(buffer + 4, (unsigned char **)&buf, &decoded_sz);
+ if(error || decoded_sz == 0)
+ return -1;
+
+ if(decoded_sz > (size_t)INT_MAX) {
+ free(buf);
+ return -1;
+ }
+ decoded_len = curlx_uztosi(decoded_sz);
+
+ decoded_len = conn->mech->decode(conn->app_data, buf, decoded_len,
+ level, conn);
+ if(decoded_len <= 0) {
+ free(buf);
+ return -1;
+ }
+
+ {
+ buf[decoded_len] = '\n';
+ Curl_debug(data, CURLINFO_HEADER_IN, buf, decoded_len + 1);
+ }
+
+ buf[decoded_len] = '\0';
+ if(decoded_len <= 3)
+ /* suspiciously short */
+ return 0;
+
+ if(buf[3] != '-')
+ /* safe to ignore return code */
+ (void)sscanf(buf, "%d", &ret_code);
+
+ if(buf[decoded_len - 1] == '\n')
+ buf[decoded_len - 1] = '\0';
+ strcpy(buffer, buf);
+ free(buf);
+ return ret_code;
+}
+
+static int sec_set_protection_level(struct Curl_easy *data)
+{
+ int code;
+ struct connectdata *conn = data->conn;
+ enum protection_level level = conn->request_data_prot;
+
+ DEBUGASSERT(level > PROT_NONE && level < PROT_LAST);
+
+ if(!conn->sec_complete) {
+ infof(data, "Trying to change the protection level after the"
+ " completion of the data exchange.");
+ return -1;
+ }
+
+ /* Bail out if we try to set up the same level */
+ if(conn->data_prot == level)
+ return 0;
+
+ if(level) {
+ char *pbsz;
+ unsigned int buffer_size = 1 << 20; /* 1048576 */
+
+ code = ftp_send_command(data, "PBSZ %u", buffer_size);
+ if(code < 0)
+ return -1;
+
+ if(code/100 != 2) {
+ failf(data, "Failed to set the protection's buffer size.");
+ return -1;
+ }
+ conn->buffer_size = buffer_size;
+
+ pbsz = strstr(data->state.buffer, "PBSZ=");
+ if(pbsz) {
+ /* ignore return code, use default value if it fails */
+ (void)sscanf(pbsz, "PBSZ=%u", &buffer_size);
+ if(buffer_size < conn->buffer_size)
+ conn->buffer_size = buffer_size;
+ }
+ }
+
+ /* Now try to negotiate the protection level. */
+ code = ftp_send_command(data, "PROT %c", level_to_char(level));
+
+ if(code < 0)
+ return -1;
+
+ if(code/100 != 2) {
+ failf(data, "Failed to set the protection level.");
+ return -1;
+ }
+
+ conn->data_prot = level;
+ if(level == PROT_PRIVATE)
+ conn->command_prot = level;
+
+ return 0;
+}
+
+int
+Curl_sec_request_prot(struct connectdata *conn, const char *level)
+{
+ enum protection_level l = name_to_level(level);
+ if(l == PROT_NONE)
+ return -1;
+ DEBUGASSERT(l > PROT_NONE && l < PROT_LAST);
+ conn->request_data_prot = l;
+ return 0;
+}
+
+static CURLcode choose_mech(struct Curl_easy *data, struct connectdata *conn)
+{
+ int ret;
+ void *tmp_allocation;
+ const struct Curl_sec_client_mech *mech = &Curl_krb5_client_mech;
+
+ tmp_allocation = realloc(conn->app_data, mech->size);
+ if(!tmp_allocation) {
+ failf(data, "Failed realloc of size %zu", mech->size);
+ mech = NULL;
+ return CURLE_OUT_OF_MEMORY;
+ }
+ conn->app_data = tmp_allocation;
+
+ if(mech->init) {
+ ret = mech->init(conn->app_data);
+ if(ret) {
+ infof(data, "Failed initialization for %s. Skipping it.",
+ mech->name);
+ return CURLE_FAILED_INIT;
+ }
+ }
+
+ infof(data, "Trying mechanism %s...", mech->name);
+ ret = ftp_send_command(data, "AUTH %s", mech->name);
+ if(ret < 0)
+ return CURLE_COULDNT_CONNECT;
+
+ if(ret/100 != 3) {
+ switch(ret) {
+ case 504:
+ infof(data, "Mechanism %s is not supported by the server (server "
+ "returned ftp code: 504).", mech->name);
+ break;
+ case 534:
+ infof(data, "Mechanism %s was rejected by the server (server returned "
+ "ftp code: 534).", mech->name);
+ break;
+ default:
+ if(ret/100 == 5) {
+ infof(data, "server does not support the security extensions");
+ return CURLE_USE_SSL_FAILED;
+ }
+ break;
+ }
+ return CURLE_LOGIN_DENIED;
+ }
+
+ /* Authenticate */
+ ret = mech->auth(conn->app_data, data, conn);
+
+ if(ret != AUTH_CONTINUE) {
+ if(ret != AUTH_OK) {
+ /* Mechanism has dumped the error to stderr, don't error here. */
+ return CURLE_USE_SSL_FAILED;
+ }
+ DEBUGASSERT(ret == AUTH_OK);
+
+ conn->mech = mech;
+ conn->sec_complete = 1;
+ conn->recv[FIRSTSOCKET] = sec_recv;
+ conn->send[FIRSTSOCKET] = sec_send;
+ conn->recv[SECONDARYSOCKET] = sec_recv;
+ conn->send[SECONDARYSOCKET] = sec_send;
+ conn->command_prot = PROT_SAFE;
+ /* Set the requested protection level */
+ /* BLOCKING */
+ (void)sec_set_protection_level(data);
+ }
+
+ return CURLE_OK;
+}
+
+CURLcode
+Curl_sec_login(struct Curl_easy *data, struct connectdata *conn)
+{
+ return choose_mech(data, conn);
+}
+
+
+void
+Curl_sec_end(struct connectdata *conn)
+{
+ if(conn->mech && conn->mech->end)
+ conn->mech->end(conn->app_data);
+ free(conn->app_data);
+ conn->app_data = NULL;
+ if(conn->in_buffer.data) {
+ free(conn->in_buffer.data);
+ conn->in_buffer.data = NULL;
+ conn->in_buffer.size = 0;
+ conn->in_buffer.index = 0;
+ conn->in_buffer.eof_flag = 0;
+ }
+ conn->sec_complete = 0;
+ conn->data_prot = PROT_CLEAR;
+ conn->mech = NULL;
+}
+
+#endif /* HAVE_GSSAPI && !CURL_DISABLE_FTP */
diff --git a/libs/libcurl/src/ldap.c b/libs/libcurl/src/ldap.c index b073349042..c03cf10bde 100644 --- a/libs/libcurl/src/ldap.c +++ b/libs/libcurl/src/ldap.c @@ -1,1105 +1,1103 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_LDAP) && !defined(USE_OPENLDAP) - -/* - * Notice that USE_OPENLDAP is only a source code selection switch. When - * libcurl is built with USE_OPENLDAP defined the libcurl source code that - * gets compiled is the code from openldap.c, otherwise the code that gets - * compiled is the code from ldap.c. - * - * When USE_OPENLDAP is defined a recent version of the OpenLDAP library - * might be required for compilation and runtime. In order to use ancient - * OpenLDAP library versions, USE_OPENLDAP shall not be defined. - */ - -/* Wincrypt must be included before anything that could include OpenSSL. */ -#if defined(USE_WIN32_CRYPTO) -#include <wincrypt.h> -/* Undefine wincrypt conflicting symbols for BoringSSL. */ -#undef X509_NAME -#undef X509_EXTENSIONS -#undef PKCS7_ISSUER_AND_SERIAL -#undef PKCS7_SIGNER_INFO -#undef OCSP_REQUEST -#undef OCSP_RESPONSE -#endif - -#ifdef USE_WIN32_LDAP /* Use Windows LDAP implementation. */ -# include <winldap.h> -# ifndef LDAP_VENDOR_NAME -# error Your Platform SDK is NOT sufficient for LDAP support! \ - Update your Platform SDK, or disable LDAP support! -# else -# include <winber.h> -# endif -#else -# define LDAP_DEPRECATED 1 /* Be sure ldap_init() is defined. */ -# ifdef HAVE_LBER_H -# include <lber.h> -# endif -# include <ldap.h> -# if (defined(HAVE_LDAP_SSL) && defined(HAVE_LDAP_SSL_H)) -# include <ldap_ssl.h> -# endif /* HAVE_LDAP_SSL && HAVE_LDAP_SSL_H */ -#endif - -#include "urldata.h" -#include <curl/curl.h> -#include "sendf.h" -#include "escape.h" -#include "progress.h" -#include "transfer.h" -#include "strcase.h" -#include "strtok.h" -#include "curl_ldap.h" -#include "curl_multibyte.h" -#include "curl_base64.h" -#include "connect.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#ifndef HAVE_LDAP_URL_PARSE - -/* Use our own implementation. */ - -struct ldap_urldesc { - char *lud_host; - int lud_port; -#if defined(USE_WIN32_LDAP) - TCHAR *lud_dn; - TCHAR **lud_attrs; -#else - char *lud_dn; - char **lud_attrs; -#endif - int lud_scope; -#if defined(USE_WIN32_LDAP) - TCHAR *lud_filter; -#else - char *lud_filter; -#endif - char **lud_exts; - size_t lud_attrs_dups; /* how many were dup'ed, this field is not in the - "real" struct so can only be used in code - without HAVE_LDAP_URL_PARSE defined */ -}; - -#undef LDAPURLDesc -#define LDAPURLDesc struct ldap_urldesc - -static int _ldap_url_parse(struct Curl_easy *data, - const struct connectdata *conn, - LDAPURLDesc **ludp); -static void _ldap_free_urldesc(LDAPURLDesc *ludp); - -#undef ldap_free_urldesc -#define ldap_free_urldesc _ldap_free_urldesc -#endif - -#ifdef DEBUG_LDAP - #define LDAP_TRACE(x) do { \ - _ldap_trace("%u: ", __LINE__); \ - _ldap_trace x; \ - } while(0) - - static void _ldap_trace(const char *fmt, ...); -#else - #define LDAP_TRACE(x) Curl_nop_stmt -#endif - -#if defined(USE_WIN32_LDAP) && defined(ldap_err2string) -/* Use ansi error strings in UNICODE builds */ -#undef ldap_err2string -#define ldap_err2string ldap_err2stringA -#endif - - -static CURLcode ldap_do(struct Curl_easy *data, bool *done); - -/* - * LDAP protocol handler. - */ - -const struct Curl_handler Curl_handler_ldap = { - "LDAP", /* scheme */ - ZERO_NULL, /* setup_connection */ - ldap_do, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_LDAP, /* defport */ - CURLPROTO_LDAP, /* protocol */ - CURLPROTO_LDAP, /* family */ - PROTOPT_NONE /* flags */ -}; - -#ifdef HAVE_LDAP_SSL -/* - * LDAPS protocol handler. - */ - -const struct Curl_handler Curl_handler_ldaps = { - "LDAPS", /* scheme */ - ZERO_NULL, /* setup_connection */ - ldap_do, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_LDAPS, /* defport */ - CURLPROTO_LDAPS, /* protocol */ - CURLPROTO_LDAP, /* family */ - PROTOPT_SSL /* flags */ -}; -#endif - -#if defined(USE_WIN32_LDAP) - -#if defined(USE_WINDOWS_SSPI) -static int ldap_win_bind_auth(LDAP *server, const char *user, - const char *passwd, unsigned long authflags) -{ - ULONG method = 0; - SEC_WINNT_AUTH_IDENTITY cred; - int rc = LDAP_AUTH_METHOD_NOT_SUPPORTED; - - memset(&cred, 0, sizeof(cred)); - -#if defined(USE_SPNEGO) - if(authflags & CURLAUTH_NEGOTIATE) { - method = LDAP_AUTH_NEGOTIATE; - } - else -#endif -#if defined(USE_NTLM) - if(authflags & CURLAUTH_NTLM) { - method = LDAP_AUTH_NTLM; - } - else -#endif -#if !defined(CURL_DISABLE_CRYPTO_AUTH) - if(authflags & CURLAUTH_DIGEST) { - method = LDAP_AUTH_DIGEST; - } - else -#endif - { - /* required anyway if one of upper preprocessor definitions enabled */ - } - - if(method && user && passwd) { - rc = Curl_create_sspi_identity(user, passwd, &cred); - if(!rc) { - rc = ldap_bind_s(server, NULL, (TCHAR *)&cred, method); - Curl_sspi_free_identity(&cred); - } - } - else { - /* proceed with current user credentials */ - method = LDAP_AUTH_NEGOTIATE; - rc = ldap_bind_s(server, NULL, NULL, method); - } - return rc; -} -#endif /* #if defined(USE_WINDOWS_SSPI) */ - -static int ldap_win_bind(struct Curl_easy *data, LDAP *server, - const char *user, const char *passwd) -{ - int rc = LDAP_INVALID_CREDENTIALS; - - PTCHAR inuser = NULL; - PTCHAR inpass = NULL; - - if(user && passwd && (data->set.httpauth & CURLAUTH_BASIC)) { - inuser = curlx_convert_UTF8_to_tchar((char *) user); - inpass = curlx_convert_UTF8_to_tchar((char *) passwd); - - rc = ldap_simple_bind_s(server, inuser, inpass); - - curlx_unicodefree(inuser); - curlx_unicodefree(inpass); - } -#if defined(USE_WINDOWS_SSPI) - else { - rc = ldap_win_bind_auth(server, user, passwd, data->set.httpauth); - } -#endif - - return rc; -} -#endif /* #if defined(USE_WIN32_LDAP) */ - -#if defined(USE_WIN32_LDAP) -#define FREE_ON_WINLDAP(x) curlx_unicodefree(x) -#else -#define FREE_ON_WINLDAP(x) -#endif - - -static CURLcode ldap_do(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - int rc = 0; - LDAP *server = NULL; - LDAPURLDesc *ludp = NULL; - LDAPMessage *ldapmsg = NULL; - LDAPMessage *entryIterator; - int num = 0; - struct connectdata *conn = data->conn; - int ldap_proto = LDAP_VERSION3; - int ldap_ssl = 0; - char *val_b64 = NULL; - size_t val_b64_sz = 0; - curl_off_t dlsize = 0; -#ifdef LDAP_OPT_NETWORK_TIMEOUT - struct timeval ldap_timeout = {10, 0}; /* 10 sec connection/search timeout */ -#endif -#if defined(USE_WIN32_LDAP) - TCHAR *host = NULL; -#else - char *host = NULL; -#endif - char *user = NULL; - char *passwd = NULL; - - *done = TRUE; /* unconditionally */ - infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d", - LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION); - infof(data, "LDAP local: %s", data->state.url); - -#ifdef HAVE_LDAP_URL_PARSE - rc = ldap_url_parse(data->state.url, &ludp); -#else - rc = _ldap_url_parse(data, conn, &ludp); -#endif - if(rc) { - failf(data, "Bad LDAP URL: %s", ldap_err2string(rc)); - result = CURLE_URL_MALFORMAT; - goto quit; - } - - /* Get the URL scheme (either ldap or ldaps) */ - if(conn->given->flags & PROTOPT_SSL) - ldap_ssl = 1; - infof(data, "LDAP local: trying to establish %s connection", - ldap_ssl ? "encrypted" : "cleartext"); - -#if defined(USE_WIN32_LDAP) - host = curlx_convert_UTF8_to_tchar(conn->host.name); - if(!host) { - result = CURLE_OUT_OF_MEMORY; - - goto quit; - } -#else - host = conn->host.name; -#endif - - if(data->state.aptr.user) { - user = conn->user; - passwd = conn->passwd; - } - -#ifdef LDAP_OPT_NETWORK_TIMEOUT - ldap_set_option(NULL, LDAP_OPT_NETWORK_TIMEOUT, &ldap_timeout); -#endif - ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto); - - if(ldap_ssl) { -#ifdef HAVE_LDAP_SSL -#ifdef USE_WIN32_LDAP - /* Win32 LDAP SDK doesn't support insecure mode without CA! */ - server = ldap_sslinit(host, conn->port, 1); - ldap_set_option(server, LDAP_OPT_SSL, LDAP_OPT_ON); -#else - int ldap_option; - char *ldap_ca = conn->ssl_config.CAfile; -#if defined(CURL_HAS_NOVELL_LDAPSDK) - rc = ldapssl_client_init(NULL, NULL); - if(rc != LDAP_SUCCESS) { - failf(data, "LDAP local: ldapssl_client_init %s", ldap_err2string(rc)); - result = CURLE_SSL_CERTPROBLEM; - goto quit; - } - if(conn->ssl_config.verifypeer) { - /* Novell SDK supports DER or BASE64 files. */ - int cert_type = LDAPSSL_CERT_FILETYPE_B64; - if((data->set.ssl.cert_type) && - (strcasecompare(data->set.ssl.cert_type, "DER"))) - cert_type = LDAPSSL_CERT_FILETYPE_DER; - if(!ldap_ca) { - failf(data, "LDAP local: ERROR %s CA cert not set", - (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM")); - result = CURLE_SSL_CERTPROBLEM; - goto quit; - } - infof(data, "LDAP local: using %s CA cert '%s'", - (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"), - ldap_ca); - rc = ldapssl_add_trusted_cert(ldap_ca, cert_type); - if(rc != LDAP_SUCCESS) { - failf(data, "LDAP local: ERROR setting %s CA cert: %s", - (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"), - ldap_err2string(rc)); - result = CURLE_SSL_CERTPROBLEM; - goto quit; - } - ldap_option = LDAPSSL_VERIFY_SERVER; - } - else - ldap_option = LDAPSSL_VERIFY_NONE; - rc = ldapssl_set_verify_mode(ldap_option); - if(rc != LDAP_SUCCESS) { - failf(data, "LDAP local: ERROR setting cert verify mode: %s", - ldap_err2string(rc)); - result = CURLE_SSL_CERTPROBLEM; - goto quit; - } - server = ldapssl_init(host, conn->port, 1); - if(!server) { - failf(data, "LDAP local: Cannot connect to %s:%u", - conn->host.dispname, conn->port); - result = CURLE_COULDNT_CONNECT; - goto quit; - } -#elif defined(LDAP_OPT_X_TLS) - if(conn->ssl_config.verifypeer) { - /* OpenLDAP SDK supports BASE64 files. */ - if((data->set.ssl.cert_type) && - (!strcasecompare(data->set.ssl.cert_type, "PEM"))) { - failf(data, "LDAP local: ERROR OpenLDAP only supports PEM cert-type"); - result = CURLE_SSL_CERTPROBLEM; - goto quit; - } - if(!ldap_ca) { - failf(data, "LDAP local: ERROR PEM CA cert not set"); - result = CURLE_SSL_CERTPROBLEM; - goto quit; - } - infof(data, "LDAP local: using PEM CA cert: %s", ldap_ca); - rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, ldap_ca); - if(rc != LDAP_SUCCESS) { - failf(data, "LDAP local: ERROR setting PEM CA cert: %s", - ldap_err2string(rc)); - result = CURLE_SSL_CERTPROBLEM; - goto quit; - } - ldap_option = LDAP_OPT_X_TLS_DEMAND; - } - else - ldap_option = LDAP_OPT_X_TLS_NEVER; - - rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &ldap_option); - if(rc != LDAP_SUCCESS) { - failf(data, "LDAP local: ERROR setting cert verify mode: %s", - ldap_err2string(rc)); - result = CURLE_SSL_CERTPROBLEM; - goto quit; - } - server = ldap_init(host, conn->port); - if(!server) { - failf(data, "LDAP local: Cannot connect to %s:%u", - conn->host.dispname, conn->port); - result = CURLE_COULDNT_CONNECT; - goto quit; - } - ldap_option = LDAP_OPT_X_TLS_HARD; - rc = ldap_set_option(server, LDAP_OPT_X_TLS, &ldap_option); - if(rc != LDAP_SUCCESS) { - failf(data, "LDAP local: ERROR setting SSL/TLS mode: %s", - ldap_err2string(rc)); - result = CURLE_SSL_CERTPROBLEM; - goto quit; - } -/* - rc = ldap_start_tls_s(server, NULL, NULL); - if(rc != LDAP_SUCCESS) { - failf(data, "LDAP local: ERROR starting SSL/TLS mode: %s", - ldap_err2string(rc)); - result = CURLE_SSL_CERTPROBLEM; - goto quit; - } -*/ -#else - /* we should probably never come up to here since configure - should check in first place if we can support LDAP SSL/TLS */ - failf(data, "LDAP local: SSL/TLS not supported with this version " - "of the OpenLDAP toolkit\n"); - result = CURLE_SSL_CERTPROBLEM; - goto quit; -#endif -#endif -#endif /* CURL_LDAP_USE_SSL */ - } - else if(data->set.use_ssl > CURLUSESSL_TRY) { - failf(data, "LDAP local: explicit TLS not supported"); - result = CURLE_NOT_BUILT_IN; - goto quit; - } - else { - server = ldap_init(host, conn->port); - if(!server) { - failf(data, "LDAP local: Cannot connect to %s:%u", - conn->host.dispname, conn->port); - result = CURLE_COULDNT_CONNECT; - goto quit; - } - } -#ifdef USE_WIN32_LDAP - ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto); - rc = ldap_win_bind(data, server, user, passwd); -#else - rc = ldap_simple_bind_s(server, user, passwd); -#endif - if(!ldap_ssl && rc) { - ldap_proto = LDAP_VERSION2; - ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto); -#ifdef USE_WIN32_LDAP - rc = ldap_win_bind(data, server, user, passwd); -#else - rc = ldap_simple_bind_s(server, user, passwd); -#endif - } - if(rc) { -#ifdef USE_WIN32_LDAP - failf(data, "LDAP local: bind via ldap_win_bind %s", - ldap_err2string(rc)); -#else - failf(data, "LDAP local: bind via ldap_simple_bind_s %s", - ldap_err2string(rc)); -#endif - result = CURLE_LDAP_CANNOT_BIND; - goto quit; - } - - rc = ldap_search_s(server, ludp->lud_dn, ludp->lud_scope, - ludp->lud_filter, ludp->lud_attrs, 0, &ldapmsg); - - if(rc && rc != LDAP_SIZELIMIT_EXCEEDED) { - failf(data, "LDAP remote: %s", ldap_err2string(rc)); - result = CURLE_LDAP_SEARCH_FAILED; - goto quit; - } - - for(num = 0, entryIterator = ldap_first_entry(server, ldapmsg); - entryIterator; - entryIterator = ldap_next_entry(server, entryIterator), num++) { - BerElement *ber = NULL; -#if defined(USE_WIN32_LDAP) - TCHAR *attribute; -#else - char *attribute; -#endif - int i; - - /* Get the DN and write it to the client */ - { - char *name; - size_t name_len; -#if defined(USE_WIN32_LDAP) - TCHAR *dn = ldap_get_dn(server, entryIterator); - name = curlx_convert_tchar_to_UTF8(dn); - if(!name) { - ldap_memfree(dn); - - result = CURLE_OUT_OF_MEMORY; - - goto quit; - } -#else - char *dn = name = ldap_get_dn(server, entryIterator); -#endif - name_len = strlen(name); - - result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"DN: ", 4); - if(result) { - FREE_ON_WINLDAP(name); - ldap_memfree(dn); - goto quit; - } - - result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) name, - name_len); - if(result) { - FREE_ON_WINLDAP(name); - ldap_memfree(dn); - goto quit; - } - - result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1); - if(result) { - FREE_ON_WINLDAP(name); - ldap_memfree(dn); - - goto quit; - } - - dlsize += name_len + 5; - - FREE_ON_WINLDAP(name); - ldap_memfree(dn); - } - - /* Get the attributes and write them to the client */ - for(attribute = ldap_first_attribute(server, entryIterator, &ber); - attribute; - attribute = ldap_next_attribute(server, entryIterator, ber)) { - BerValue **vals; - size_t attr_len; -#if defined(USE_WIN32_LDAP) - char *attr = curlx_convert_tchar_to_UTF8(attribute); - if(!attr) { - if(ber) - ber_free(ber, 0); - - result = CURLE_OUT_OF_MEMORY; - - goto quit; - } -#else - char *attr = attribute; -#endif - attr_len = strlen(attr); - - vals = ldap_get_values_len(server, entryIterator, attribute); - if(vals) { - for(i = 0; (vals[i] != NULL); i++) { - result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\t", 1); - if(result) { - ldap_value_free_len(vals); - FREE_ON_WINLDAP(attr); - ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - - goto quit; - } - - result = Curl_client_write(data, CLIENTWRITE_BODY, - (char *) attr, attr_len); - if(result) { - ldap_value_free_len(vals); - FREE_ON_WINLDAP(attr); - ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - - goto quit; - } - - result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)": ", 2); - if(result) { - ldap_value_free_len(vals); - FREE_ON_WINLDAP(attr); - ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - - goto quit; - } - - dlsize += attr_len + 3; - - if((attr_len > 7) && - (strcmp(";binary", (char *) attr + (attr_len - 7)) == 0)) { - /* Binary attribute, encode to base64. */ - result = Curl_base64_encode(vals[i]->bv_val, vals[i]->bv_len, - &val_b64, &val_b64_sz); - if(result) { - ldap_value_free_len(vals); - FREE_ON_WINLDAP(attr); - ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - - goto quit; - } - - if(val_b64_sz > 0) { - result = Curl_client_write(data, CLIENTWRITE_BODY, val_b64, - val_b64_sz); - free(val_b64); - if(result) { - ldap_value_free_len(vals); - FREE_ON_WINLDAP(attr); - ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - - goto quit; - } - - dlsize += val_b64_sz; - } - } - else { - result = Curl_client_write(data, CLIENTWRITE_BODY, vals[i]->bv_val, - vals[i]->bv_len); - if(result) { - ldap_value_free_len(vals); - FREE_ON_WINLDAP(attr); - ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - - goto quit; - } - - dlsize += vals[i]->bv_len; - } - - result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1); - if(result) { - ldap_value_free_len(vals); - FREE_ON_WINLDAP(attr); - ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - - goto quit; - } - - dlsize++; - } - - /* Free memory used to store values */ - ldap_value_free_len(vals); - } - - /* Free the attribute as we are done with it */ - FREE_ON_WINLDAP(attr); - ldap_memfree(attribute); - - result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1); - if(result) - goto quit; - dlsize++; - Curl_pgrsSetDownloadCounter(data, dlsize); - } - - if(ber) - ber_free(ber, 0); - } - -quit: - if(ldapmsg) { - ldap_msgfree(ldapmsg); - LDAP_TRACE(("Received %d entries\n", num)); - } - if(rc == LDAP_SIZELIMIT_EXCEEDED) - infof(data, "There are more than %d entries", num); - if(ludp) - ldap_free_urldesc(ludp); - if(server) - ldap_unbind_s(server); -#if defined(HAVE_LDAP_SSL) && defined(CURL_HAS_NOVELL_LDAPSDK) - if(ldap_ssl) - ldapssl_client_deinit(); -#endif /* HAVE_LDAP_SSL && CURL_HAS_NOVELL_LDAPSDK */ - - FREE_ON_WINLDAP(host); - - /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); - connclose(conn, "LDAP connection always disable re-use"); - - return result; -} - -#ifdef DEBUG_LDAP -static void _ldap_trace(const char *fmt, ...) -{ - static int do_trace = -1; - va_list args; - - if(do_trace == -1) { - const char *env = getenv("CURL_TRACE"); - do_trace = (env && strtol(env, NULL, 10) > 0); - } - if(!do_trace) - return; - - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); -} -#endif - -#ifndef HAVE_LDAP_URL_PARSE - -/* - * Return scope-value for a scope-string. - */ -static int str2scope(const char *p) -{ - if(strcasecompare(p, "one")) - return LDAP_SCOPE_ONELEVEL; - if(strcasecompare(p, "onetree")) - return LDAP_SCOPE_ONELEVEL; - if(strcasecompare(p, "base")) - return LDAP_SCOPE_BASE; - if(strcasecompare(p, "sub")) - return LDAP_SCOPE_SUBTREE; - if(strcasecompare(p, "subtree")) - return LDAP_SCOPE_SUBTREE; - return (-1); -} - -/* - * Split 'str' into strings separated by commas. - * Note: out[] points into 'str'. - */ -static bool split_str(char *str, char ***out, size_t *count) -{ - char **res; - char *lasts; - char *s; - size_t i; - size_t items = 1; - - s = strchr(str, ','); - while(s) { - items++; - s = strchr(++s, ','); - } - - res = calloc(items, sizeof(char *)); - if(!res) - return FALSE; - - for(i = 0, s = strtok_r(str, ",", &lasts); s && i < items; - s = strtok_r(NULL, ",", &lasts), i++) - res[i] = s; - - *out = res; - *count = items; - - return TRUE; -} - -/* - * Break apart the pieces of an LDAP URL. - * Syntax: - * ldap://<hostname>:<port>/<base_dn>?<attributes>?<scope>?<filter>?<ext> - * - * <hostname> already known from 'conn->host.name'. - * <port> already known from 'conn->remote_port'. - * extract the rest from 'data->state.path+1'. All fields are optional. - * e.g. - * ldap://<hostname>:<port>/?<attributes>?<scope>?<filter> - * yields ludp->lud_dn = "". - * - * Defined in RFC4516 section 2. - */ -static int _ldap_url_parse2(struct Curl_easy *data, - const struct connectdata *conn, LDAPURLDesc *ludp) -{ - int rc = LDAP_SUCCESS; - char *p; - char *path; - char *q = NULL; - char *query = NULL; - size_t i; - - if(!data || - !data->state.up.path || - data->state.up.path[0] != '/' || - !strncasecompare("LDAP", data->state.up.scheme, 4)) - return LDAP_INVALID_SYNTAX; - - ludp->lud_scope = LDAP_SCOPE_BASE; - ludp->lud_port = conn->remote_port; - ludp->lud_host = conn->host.name; - - /* Duplicate the path */ - p = path = strdup(data->state.up.path + 1); - if(!path) - return LDAP_NO_MEMORY; - - /* Duplicate the query if present */ - if(data->state.up.query) { - q = query = strdup(data->state.up.query); - if(!query) { - free(path); - return LDAP_NO_MEMORY; - } - } - - /* Parse the DN (Distinguished Name) */ - if(*p) { - char *dn = p; - char *unescaped; - CURLcode result; - - LDAP_TRACE(("DN '%s'\n", dn)); - - /* Unescape the DN */ - result = Curl_urldecode(dn, 0, &unescaped, NULL, REJECT_ZERO); - if(result) { - rc = LDAP_NO_MEMORY; - - goto quit; - } - -#if defined(USE_WIN32_LDAP) - /* Convert the unescaped string to a tchar */ - ludp->lud_dn = curlx_convert_UTF8_to_tchar(unescaped); - - /* Free the unescaped string as we are done with it */ - free(unescaped); - - if(!ludp->lud_dn) { - rc = LDAP_NO_MEMORY; - - goto quit; - } -#else - ludp->lud_dn = unescaped; -#endif - } - - p = q; - if(!p) - goto quit; - - /* Parse the attributes. skip "??" */ - q = strchr(p, '?'); - if(q) - *q++ = '\0'; - - if(*p) { - char **attributes; - size_t count = 0; - - /* Split the string into an array of attributes */ - if(!split_str(p, &attributes, &count)) { - rc = LDAP_NO_MEMORY; - - goto quit; - } - - /* Allocate our array (+1 for the NULL entry) */ -#if defined(USE_WIN32_LDAP) - ludp->lud_attrs = calloc(count + 1, sizeof(TCHAR *)); -#else - ludp->lud_attrs = calloc(count + 1, sizeof(char *)); -#endif - if(!ludp->lud_attrs) { - free(attributes); - - rc = LDAP_NO_MEMORY; - - goto quit; - } - - for(i = 0; i < count; i++) { - char *unescaped; - CURLcode result; - - LDAP_TRACE(("attr[%zu] '%s'\n", i, attributes[i])); - - /* Unescape the attribute */ - result = Curl_urldecode(attributes[i], 0, &unescaped, NULL, - REJECT_ZERO); - if(result) { - free(attributes); - - rc = LDAP_NO_MEMORY; - - goto quit; - } - -#if defined(USE_WIN32_LDAP) - /* Convert the unescaped string to a tchar */ - ludp->lud_attrs[i] = curlx_convert_UTF8_to_tchar(unescaped); - - /* Free the unescaped string as we are done with it */ - free(unescaped); - - if(!ludp->lud_attrs[i]) { - free(attributes); - - rc = LDAP_NO_MEMORY; - - goto quit; - } -#else - ludp->lud_attrs[i] = unescaped; -#endif - - ludp->lud_attrs_dups++; - } - - free(attributes); - } - - p = q; - if(!p) - goto quit; - - /* Parse the scope. skip "??" */ - q = strchr(p, '?'); - if(q) - *q++ = '\0'; - - if(*p) { - ludp->lud_scope = str2scope(p); - if(ludp->lud_scope == -1) { - rc = LDAP_INVALID_SYNTAX; - - goto quit; - } - LDAP_TRACE(("scope %d\n", ludp->lud_scope)); - } - - p = q; - if(!p) - goto quit; - - /* Parse the filter */ - q = strchr(p, '?'); - if(q) - *q++ = '\0'; - - if(*p) { - char *filter = p; - char *unescaped; - CURLcode result; - - LDAP_TRACE(("filter '%s'\n", filter)); - - /* Unescape the filter */ - result = Curl_urldecode(filter, 0, &unescaped, NULL, REJECT_ZERO); - if(result) { - rc = LDAP_NO_MEMORY; - - goto quit; - } - -#if defined(USE_WIN32_LDAP) - /* Convert the unescaped string to a tchar */ - ludp->lud_filter = curlx_convert_UTF8_to_tchar(unescaped); - - /* Free the unescaped string as we are done with it */ - free(unescaped); - - if(!ludp->lud_filter) { - rc = LDAP_NO_MEMORY; - - goto quit; - } -#else - ludp->lud_filter = unescaped; -#endif - } - - p = q; - if(p && !*p) { - rc = LDAP_INVALID_SYNTAX; - - goto quit; - } - -quit: - free(path); - free(query); - - return rc; -} - -static int _ldap_url_parse(struct Curl_easy *data, - const struct connectdata *conn, - LDAPURLDesc **ludpp) -{ - LDAPURLDesc *ludp = calloc(1, sizeof(*ludp)); - int rc; - - *ludpp = NULL; - if(!ludp) - return LDAP_NO_MEMORY; - - rc = _ldap_url_parse2(data, conn, ludp); - if(rc != LDAP_SUCCESS) { - _ldap_free_urldesc(ludp); - ludp = NULL; - } - *ludpp = ludp; - return (rc); -} - -static void _ldap_free_urldesc(LDAPURLDesc *ludp) -{ - if(!ludp) - return; - -#if defined(USE_WIN32_LDAP) - curlx_unicodefree(ludp->lud_dn); - curlx_unicodefree(ludp->lud_filter); -#else - free(ludp->lud_dn); - free(ludp->lud_filter); -#endif - - if(ludp->lud_attrs) { - size_t i; - for(i = 0; i < ludp->lud_attrs_dups; i++) { -#if defined(USE_WIN32_LDAP) - curlx_unicodefree(ludp->lud_attrs[i]); -#else - free(ludp->lud_attrs[i]); -#endif - } - free(ludp->lud_attrs); - } - - free(ludp); -} -#endif /* !HAVE_LDAP_URL_PARSE */ -#endif /* !CURL_DISABLE_LDAP && !USE_OPENLDAP */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_LDAP) && !defined(USE_OPENLDAP)
+
+/*
+ * Notice that USE_OPENLDAP is only a source code selection switch. When
+ * libcurl is built with USE_OPENLDAP defined the libcurl source code that
+ * gets compiled is the code from openldap.c, otherwise the code that gets
+ * compiled is the code from ldap.c.
+ *
+ * When USE_OPENLDAP is defined a recent version of the OpenLDAP library
+ * might be required for compilation and runtime. In order to use ancient
+ * OpenLDAP library versions, USE_OPENLDAP shall not be defined.
+ */
+
+/* Wincrypt must be included before anything that could include OpenSSL. */
+#if defined(USE_WIN32_CRYPTO)
+#include <wincrypt.h>
+/* Undefine wincrypt conflicting symbols for BoringSSL. */
+#undef X509_NAME
+#undef X509_EXTENSIONS
+#undef PKCS7_ISSUER_AND_SERIAL
+#undef PKCS7_SIGNER_INFO
+#undef OCSP_REQUEST
+#undef OCSP_RESPONSE
+#endif
+
+#ifdef USE_WIN32_LDAP /* Use Windows LDAP implementation. */
+# include <winldap.h>
+# ifndef LDAP_VENDOR_NAME
+# error Your Platform SDK is NOT sufficient for LDAP support! \
+ Update your Platform SDK, or disable LDAP support!
+# else
+# include <winber.h>
+# endif
+#else
+# define LDAP_DEPRECATED 1 /* Be sure ldap_init() is defined. */
+# ifdef HAVE_LBER_H
+# include <lber.h>
+# endif
+# include <ldap.h>
+# if (defined(HAVE_LDAP_SSL) && defined(HAVE_LDAP_SSL_H))
+# include <ldap_ssl.h>
+# endif /* HAVE_LDAP_SSL && HAVE_LDAP_SSL_H */
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "sendf.h"
+#include "escape.h"
+#include "progress.h"
+#include "transfer.h"
+#include "strcase.h"
+#include "strtok.h"
+#include "curl_ldap.h"
+#include "curl_multibyte.h"
+#include "curl_base64.h"
+#include "connect.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#ifndef HAVE_LDAP_URL_PARSE
+
+/* Use our own implementation. */
+
+struct ldap_urldesc {
+ char *lud_host;
+ int lud_port;
+#if defined(USE_WIN32_LDAP)
+ TCHAR *lud_dn;
+ TCHAR **lud_attrs;
+#else
+ char *lud_dn;
+ char **lud_attrs;
+#endif
+ int lud_scope;
+#if defined(USE_WIN32_LDAP)
+ TCHAR *lud_filter;
+#else
+ char *lud_filter;
+#endif
+ char **lud_exts;
+ size_t lud_attrs_dups; /* how many were dup'ed, this field is not in the
+ "real" struct so can only be used in code
+ without HAVE_LDAP_URL_PARSE defined */
+};
+
+#undef LDAPURLDesc
+#define LDAPURLDesc struct ldap_urldesc
+
+static int _ldap_url_parse(struct Curl_easy *data,
+ const struct connectdata *conn,
+ LDAPURLDesc **ludp);
+static void _ldap_free_urldesc(LDAPURLDesc *ludp);
+
+#undef ldap_free_urldesc
+#define ldap_free_urldesc _ldap_free_urldesc
+#endif
+
+#ifdef DEBUG_LDAP
+ #define LDAP_TRACE(x) do { \
+ _ldap_trace("%u: ", __LINE__); \
+ _ldap_trace x; \
+ } while(0)
+
+ static void _ldap_trace(const char *fmt, ...);
+#else
+ #define LDAP_TRACE(x) Curl_nop_stmt
+#endif
+
+#if defined(USE_WIN32_LDAP) && defined(ldap_err2string)
+/* Use ansi error strings in UNICODE builds */
+#undef ldap_err2string
+#define ldap_err2string ldap_err2stringA
+#endif
+
+
+static CURLcode ldap_do(struct Curl_easy *data, bool *done);
+
+/*
+ * LDAP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_ldap = {
+ "LDAP", /* scheme */
+ ZERO_NULL, /* setup_connection */
+ ldap_do, /* do_it */
+ ZERO_NULL, /* done */
+ ZERO_NULL, /* do_more */
+ ZERO_NULL, /* connect_it */
+ ZERO_NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ ZERO_NULL, /* proto_getsock */
+ ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ ZERO_NULL, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_LDAP, /* defport */
+ CURLPROTO_LDAP, /* protocol */
+ CURLPROTO_LDAP, /* family */
+ PROTOPT_NONE /* flags */
+};
+
+#ifdef HAVE_LDAP_SSL
+/*
+ * LDAPS protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_ldaps = {
+ "LDAPS", /* scheme */
+ ZERO_NULL, /* setup_connection */
+ ldap_do, /* do_it */
+ ZERO_NULL, /* done */
+ ZERO_NULL, /* do_more */
+ ZERO_NULL, /* connect_it */
+ ZERO_NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ ZERO_NULL, /* proto_getsock */
+ ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ ZERO_NULL, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_LDAPS, /* defport */
+ CURLPROTO_LDAPS, /* protocol */
+ CURLPROTO_LDAP, /* family */
+ PROTOPT_SSL /* flags */
+};
+#endif
+
+#if defined(USE_WIN32_LDAP)
+
+#if defined(USE_WINDOWS_SSPI)
+static int ldap_win_bind_auth(LDAP *server, const char *user,
+ const char *passwd, unsigned long authflags)
+{
+ ULONG method = 0;
+ SEC_WINNT_AUTH_IDENTITY cred;
+ int rc = LDAP_AUTH_METHOD_NOT_SUPPORTED;
+
+ memset(&cred, 0, sizeof(cred));
+
+#if defined(USE_SPNEGO)
+ if(authflags & CURLAUTH_NEGOTIATE) {
+ method = LDAP_AUTH_NEGOTIATE;
+ }
+ else
+#endif
+#if defined(USE_NTLM)
+ if(authflags & CURLAUTH_NTLM) {
+ method = LDAP_AUTH_NTLM;
+ }
+ else
+#endif
+#if !defined(CURL_DISABLE_CRYPTO_AUTH)
+ if(authflags & CURLAUTH_DIGEST) {
+ method = LDAP_AUTH_DIGEST;
+ }
+ else
+#endif
+ {
+ /* required anyway if one of upper preprocessor definitions enabled */
+ }
+
+ if(method && user && passwd) {
+ rc = Curl_create_sspi_identity(user, passwd, &cred);
+ if(!rc) {
+ rc = ldap_bind_s(server, NULL, (TCHAR *)&cred, method);
+ Curl_sspi_free_identity(&cred);
+ }
+ }
+ else {
+ /* proceed with current user credentials */
+ method = LDAP_AUTH_NEGOTIATE;
+ rc = ldap_bind_s(server, NULL, NULL, method);
+ }
+ return rc;
+}
+#endif /* #if defined(USE_WINDOWS_SSPI) */
+
+static int ldap_win_bind(struct Curl_easy *data, LDAP *server,
+ const char *user, const char *passwd)
+{
+ int rc = LDAP_INVALID_CREDENTIALS;
+
+ PTCHAR inuser = NULL;
+ PTCHAR inpass = NULL;
+
+ if(user && passwd && (data->set.httpauth & CURLAUTH_BASIC)) {
+ inuser = curlx_convert_UTF8_to_tchar((char *) user);
+ inpass = curlx_convert_UTF8_to_tchar((char *) passwd);
+
+ rc = ldap_simple_bind_s(server, inuser, inpass);
+
+ curlx_unicodefree(inuser);
+ curlx_unicodefree(inpass);
+ }
+#if defined(USE_WINDOWS_SSPI)
+ else {
+ rc = ldap_win_bind_auth(server, user, passwd, data->set.httpauth);
+ }
+#endif
+
+ return rc;
+}
+#endif /* #if defined(USE_WIN32_LDAP) */
+
+#if defined(USE_WIN32_LDAP)
+#define FREE_ON_WINLDAP(x) curlx_unicodefree(x)
+#else
+#define FREE_ON_WINLDAP(x)
+#endif
+
+
+static CURLcode ldap_do(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ int rc = 0;
+ LDAP *server = NULL;
+ LDAPURLDesc *ludp = NULL;
+ LDAPMessage *ldapmsg = NULL;
+ LDAPMessage *entryIterator;
+ int num = 0;
+ struct connectdata *conn = data->conn;
+ int ldap_proto = LDAP_VERSION3;
+ int ldap_ssl = 0;
+ char *val_b64 = NULL;
+ size_t val_b64_sz = 0;
+ curl_off_t dlsize = 0;
+#ifdef LDAP_OPT_NETWORK_TIMEOUT
+ struct timeval ldap_timeout = {10, 0}; /* 10 sec connection/search timeout */
+#endif
+#if defined(USE_WIN32_LDAP)
+ TCHAR *host = NULL;
+#else
+ char *host = NULL;
+#endif
+ char *user = NULL;
+ char *passwd = NULL;
+
+ *done = TRUE; /* unconditionally */
+ infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d",
+ LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION);
+ infof(data, "LDAP local: %s", data->state.url);
+
+#ifdef HAVE_LDAP_URL_PARSE
+ rc = ldap_url_parse(data->state.url, &ludp);
+#else
+ rc = _ldap_url_parse(data, conn, &ludp);
+#endif
+ if(rc) {
+ failf(data, "Bad LDAP URL: %s", ldap_err2string(rc));
+ result = CURLE_URL_MALFORMAT;
+ goto quit;
+ }
+
+ /* Get the URL scheme (either ldap or ldaps) */
+ if(conn->given->flags & PROTOPT_SSL)
+ ldap_ssl = 1;
+ infof(data, "LDAP local: trying to establish %s connection",
+ ldap_ssl ? "encrypted" : "cleartext");
+
+#if defined(USE_WIN32_LDAP)
+ host = curlx_convert_UTF8_to_tchar(conn->host.name);
+ if(!host) {
+ result = CURLE_OUT_OF_MEMORY;
+
+ goto quit;
+ }
+#else
+ host = conn->host.name;
+#endif
+
+ if(data->state.aptr.user) {
+ user = conn->user;
+ passwd = conn->passwd;
+ }
+
+#ifdef LDAP_OPT_NETWORK_TIMEOUT
+ ldap_set_option(NULL, LDAP_OPT_NETWORK_TIMEOUT, &ldap_timeout);
+#endif
+ ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
+
+ if(ldap_ssl) {
+#ifdef HAVE_LDAP_SSL
+#ifdef USE_WIN32_LDAP
+ /* Win32 LDAP SDK doesn't support insecure mode without CA! */
+ server = ldap_sslinit(host, conn->port, 1);
+ ldap_set_option(server, LDAP_OPT_SSL, LDAP_OPT_ON);
+#else
+ int ldap_option;
+ char *ldap_ca = conn->ssl_config.CAfile;
+#if defined(CURL_HAS_NOVELL_LDAPSDK)
+ rc = ldapssl_client_init(NULL, NULL);
+ if(rc != LDAP_SUCCESS) {
+ failf(data, "LDAP local: ldapssl_client_init %s", ldap_err2string(rc));
+ result = CURLE_SSL_CERTPROBLEM;
+ goto quit;
+ }
+ if(conn->ssl_config.verifypeer) {
+ /* Novell SDK supports DER or BASE64 files. */
+ int cert_type = LDAPSSL_CERT_FILETYPE_B64;
+ if((data->set.ssl.cert_type) &&
+ (strcasecompare(data->set.ssl.cert_type, "DER")))
+ cert_type = LDAPSSL_CERT_FILETYPE_DER;
+ if(!ldap_ca) {
+ failf(data, "LDAP local: ERROR %s CA cert not set",
+ (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"));
+ result = CURLE_SSL_CERTPROBLEM;
+ goto quit;
+ }
+ infof(data, "LDAP local: using %s CA cert '%s'",
+ (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
+ ldap_ca);
+ rc = ldapssl_add_trusted_cert(ldap_ca, cert_type);
+ if(rc != LDAP_SUCCESS) {
+ failf(data, "LDAP local: ERROR setting %s CA cert: %s",
+ (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
+ ldap_err2string(rc));
+ result = CURLE_SSL_CERTPROBLEM;
+ goto quit;
+ }
+ ldap_option = LDAPSSL_VERIFY_SERVER;
+ }
+ else
+ ldap_option = LDAPSSL_VERIFY_NONE;
+ rc = ldapssl_set_verify_mode(ldap_option);
+ if(rc != LDAP_SUCCESS) {
+ failf(data, "LDAP local: ERROR setting cert verify mode: %s",
+ ldap_err2string(rc));
+ result = CURLE_SSL_CERTPROBLEM;
+ goto quit;
+ }
+ server = ldapssl_init(host, conn->port, 1);
+ if(!server) {
+ failf(data, "LDAP local: Cannot connect to %s:%u",
+ conn->host.dispname, conn->port);
+ result = CURLE_COULDNT_CONNECT;
+ goto quit;
+ }
+#elif defined(LDAP_OPT_X_TLS)
+ if(conn->ssl_config.verifypeer) {
+ /* OpenLDAP SDK supports BASE64 files. */
+ if((data->set.ssl.cert_type) &&
+ (!strcasecompare(data->set.ssl.cert_type, "PEM"))) {
+ failf(data, "LDAP local: ERROR OpenLDAP only supports PEM cert-type");
+ result = CURLE_SSL_CERTPROBLEM;
+ goto quit;
+ }
+ if(!ldap_ca) {
+ failf(data, "LDAP local: ERROR PEM CA cert not set");
+ result = CURLE_SSL_CERTPROBLEM;
+ goto quit;
+ }
+ infof(data, "LDAP local: using PEM CA cert: %s", ldap_ca);
+ rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, ldap_ca);
+ if(rc != LDAP_SUCCESS) {
+ failf(data, "LDAP local: ERROR setting PEM CA cert: %s",
+ ldap_err2string(rc));
+ result = CURLE_SSL_CERTPROBLEM;
+ goto quit;
+ }
+ ldap_option = LDAP_OPT_X_TLS_DEMAND;
+ }
+ else
+ ldap_option = LDAP_OPT_X_TLS_NEVER;
+
+ rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &ldap_option);
+ if(rc != LDAP_SUCCESS) {
+ failf(data, "LDAP local: ERROR setting cert verify mode: %s",
+ ldap_err2string(rc));
+ result = CURLE_SSL_CERTPROBLEM;
+ goto quit;
+ }
+ server = ldap_init(host, conn->port);
+ if(!server) {
+ failf(data, "LDAP local: Cannot connect to %s:%u",
+ conn->host.dispname, conn->port);
+ result = CURLE_COULDNT_CONNECT;
+ goto quit;
+ }
+ ldap_option = LDAP_OPT_X_TLS_HARD;
+ rc = ldap_set_option(server, LDAP_OPT_X_TLS, &ldap_option);
+ if(rc != LDAP_SUCCESS) {
+ failf(data, "LDAP local: ERROR setting SSL/TLS mode: %s",
+ ldap_err2string(rc));
+ result = CURLE_SSL_CERTPROBLEM;
+ goto quit;
+ }
+/*
+ rc = ldap_start_tls_s(server, NULL, NULL);
+ if(rc != LDAP_SUCCESS) {
+ failf(data, "LDAP local: ERROR starting SSL/TLS mode: %s",
+ ldap_err2string(rc));
+ result = CURLE_SSL_CERTPROBLEM;
+ goto quit;
+ }
+*/
+#else
+ /* we should probably never come up to here since configure
+ should check in first place if we can support LDAP SSL/TLS */
+ failf(data, "LDAP local: SSL/TLS not supported with this version "
+ "of the OpenLDAP toolkit\n");
+ result = CURLE_SSL_CERTPROBLEM;
+ goto quit;
+#endif
+#endif
+#endif /* CURL_LDAP_USE_SSL */
+ }
+ else if(data->set.use_ssl > CURLUSESSL_TRY) {
+ failf(data, "LDAP local: explicit TLS not supported");
+ result = CURLE_NOT_BUILT_IN;
+ goto quit;
+ }
+ else {
+ server = ldap_init(host, conn->port);
+ if(!server) {
+ failf(data, "LDAP local: Cannot connect to %s:%u",
+ conn->host.dispname, conn->port);
+ result = CURLE_COULDNT_CONNECT;
+ goto quit;
+ }
+ }
+#ifdef USE_WIN32_LDAP
+ ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
+ rc = ldap_win_bind(data, server, user, passwd);
+#else
+ rc = ldap_simple_bind_s(server, user, passwd);
+#endif
+ if(!ldap_ssl && rc) {
+ ldap_proto = LDAP_VERSION2;
+ ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
+#ifdef USE_WIN32_LDAP
+ rc = ldap_win_bind(data, server, user, passwd);
+#else
+ rc = ldap_simple_bind_s(server, user, passwd);
+#endif
+ }
+ if(rc) {
+#ifdef USE_WIN32_LDAP
+ failf(data, "LDAP local: bind via ldap_win_bind %s",
+ ldap_err2string(rc));
+#else
+ failf(data, "LDAP local: bind via ldap_simple_bind_s %s",
+ ldap_err2string(rc));
+#endif
+ result = CURLE_LDAP_CANNOT_BIND;
+ goto quit;
+ }
+
+ rc = ldap_search_s(server, ludp->lud_dn, ludp->lud_scope,
+ ludp->lud_filter, ludp->lud_attrs, 0, &ldapmsg);
+
+ if(rc && rc != LDAP_SIZELIMIT_EXCEEDED) {
+ failf(data, "LDAP remote: %s", ldap_err2string(rc));
+ result = CURLE_LDAP_SEARCH_FAILED;
+ goto quit;
+ }
+
+ for(num = 0, entryIterator = ldap_first_entry(server, ldapmsg);
+ entryIterator;
+ entryIterator = ldap_next_entry(server, entryIterator), num++) {
+ BerElement *ber = NULL;
+#if defined(USE_WIN32_LDAP)
+ TCHAR *attribute;
+#else
+ char *attribute;
+#endif
+ int i;
+
+ /* Get the DN and write it to the client */
+ {
+ char *name;
+ size_t name_len;
+#if defined(USE_WIN32_LDAP)
+ TCHAR *dn = ldap_get_dn(server, entryIterator);
+ name = curlx_convert_tchar_to_UTF8(dn);
+ if(!name) {
+ ldap_memfree(dn);
+
+ result = CURLE_OUT_OF_MEMORY;
+
+ goto quit;
+ }
+#else
+ char *dn = name = ldap_get_dn(server, entryIterator);
+#endif
+ name_len = strlen(name);
+
+ result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"DN: ", 4);
+ if(result) {
+ FREE_ON_WINLDAP(name);
+ ldap_memfree(dn);
+ goto quit;
+ }
+
+ result = Curl_client_write(data, CLIENTWRITE_BODY, name, name_len);
+ if(result) {
+ FREE_ON_WINLDAP(name);
+ ldap_memfree(dn);
+ goto quit;
+ }
+
+ result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
+ if(result) {
+ FREE_ON_WINLDAP(name);
+ ldap_memfree(dn);
+
+ goto quit;
+ }
+
+ dlsize += name_len + 5;
+
+ FREE_ON_WINLDAP(name);
+ ldap_memfree(dn);
+ }
+
+ /* Get the attributes and write them to the client */
+ for(attribute = ldap_first_attribute(server, entryIterator, &ber);
+ attribute;
+ attribute = ldap_next_attribute(server, entryIterator, ber)) {
+ BerValue **vals;
+ size_t attr_len;
+#if defined(USE_WIN32_LDAP)
+ char *attr = curlx_convert_tchar_to_UTF8(attribute);
+ if(!attr) {
+ if(ber)
+ ber_free(ber, 0);
+
+ result = CURLE_OUT_OF_MEMORY;
+
+ goto quit;
+ }
+#else
+ char *attr = attribute;
+#endif
+ attr_len = strlen(attr);
+
+ vals = ldap_get_values_len(server, entryIterator, attribute);
+ if(vals) {
+ for(i = 0; (vals[i] != NULL); i++) {
+ result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\t", 1);
+ if(result) {
+ ldap_value_free_len(vals);
+ FREE_ON_WINLDAP(attr);
+ ldap_memfree(attribute);
+ if(ber)
+ ber_free(ber, 0);
+
+ goto quit;
+ }
+
+ result = Curl_client_write(data, CLIENTWRITE_BODY, attr, attr_len);
+ if(result) {
+ ldap_value_free_len(vals);
+ FREE_ON_WINLDAP(attr);
+ ldap_memfree(attribute);
+ if(ber)
+ ber_free(ber, 0);
+
+ goto quit;
+ }
+
+ result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)": ", 2);
+ if(result) {
+ ldap_value_free_len(vals);
+ FREE_ON_WINLDAP(attr);
+ ldap_memfree(attribute);
+ if(ber)
+ ber_free(ber, 0);
+
+ goto quit;
+ }
+
+ dlsize += attr_len + 3;
+
+ if((attr_len > 7) &&
+ (strcmp(";binary", attr + (attr_len - 7)) == 0)) {
+ /* Binary attribute, encode to base64. */
+ result = Curl_base64_encode(vals[i]->bv_val, vals[i]->bv_len,
+ &val_b64, &val_b64_sz);
+ if(result) {
+ ldap_value_free_len(vals);
+ FREE_ON_WINLDAP(attr);
+ ldap_memfree(attribute);
+ if(ber)
+ ber_free(ber, 0);
+
+ goto quit;
+ }
+
+ if(val_b64_sz > 0) {
+ result = Curl_client_write(data, CLIENTWRITE_BODY, val_b64,
+ val_b64_sz);
+ free(val_b64);
+ if(result) {
+ ldap_value_free_len(vals);
+ FREE_ON_WINLDAP(attr);
+ ldap_memfree(attribute);
+ if(ber)
+ ber_free(ber, 0);
+
+ goto quit;
+ }
+
+ dlsize += val_b64_sz;
+ }
+ }
+ else {
+ result = Curl_client_write(data, CLIENTWRITE_BODY, vals[i]->bv_val,
+ vals[i]->bv_len);
+ if(result) {
+ ldap_value_free_len(vals);
+ FREE_ON_WINLDAP(attr);
+ ldap_memfree(attribute);
+ if(ber)
+ ber_free(ber, 0);
+
+ goto quit;
+ }
+
+ dlsize += vals[i]->bv_len;
+ }
+
+ result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
+ if(result) {
+ ldap_value_free_len(vals);
+ FREE_ON_WINLDAP(attr);
+ ldap_memfree(attribute);
+ if(ber)
+ ber_free(ber, 0);
+
+ goto quit;
+ }
+
+ dlsize++;
+ }
+
+ /* Free memory used to store values */
+ ldap_value_free_len(vals);
+ }
+
+ /* Free the attribute as we are done with it */
+ FREE_ON_WINLDAP(attr);
+ ldap_memfree(attribute);
+
+ result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
+ if(result)
+ goto quit;
+ dlsize++;
+ Curl_pgrsSetDownloadCounter(data, dlsize);
+ }
+
+ if(ber)
+ ber_free(ber, 0);
+ }
+
+quit:
+ if(ldapmsg) {
+ ldap_msgfree(ldapmsg);
+ LDAP_TRACE(("Received %d entries\n", num));
+ }
+ if(rc == LDAP_SIZELIMIT_EXCEEDED)
+ infof(data, "There are more than %d entries", num);
+ if(ludp)
+ ldap_free_urldesc(ludp);
+ if(server)
+ ldap_unbind_s(server);
+#if defined(HAVE_LDAP_SSL) && defined(CURL_HAS_NOVELL_LDAPSDK)
+ if(ldap_ssl)
+ ldapssl_client_deinit();
+#endif /* HAVE_LDAP_SSL && CURL_HAS_NOVELL_LDAPSDK */
+
+ FREE_ON_WINLDAP(host);
+
+ /* no data to transfer */
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
+ connclose(conn, "LDAP connection always disable re-use");
+
+ return result;
+}
+
+#ifdef DEBUG_LDAP
+static void _ldap_trace(const char *fmt, ...)
+{
+ static int do_trace = -1;
+ va_list args;
+
+ if(do_trace == -1) {
+ const char *env = getenv("CURL_TRACE");
+ do_trace = (env && strtol(env, NULL, 10) > 0);
+ }
+ if(!do_trace)
+ return;
+
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+}
+#endif
+
+#ifndef HAVE_LDAP_URL_PARSE
+
+/*
+ * Return scope-value for a scope-string.
+ */
+static int str2scope(const char *p)
+{
+ if(strcasecompare(p, "one"))
+ return LDAP_SCOPE_ONELEVEL;
+ if(strcasecompare(p, "onetree"))
+ return LDAP_SCOPE_ONELEVEL;
+ if(strcasecompare(p, "base"))
+ return LDAP_SCOPE_BASE;
+ if(strcasecompare(p, "sub"))
+ return LDAP_SCOPE_SUBTREE;
+ if(strcasecompare(p, "subtree"))
+ return LDAP_SCOPE_SUBTREE;
+ return (-1);
+}
+
+/*
+ * Split 'str' into strings separated by commas.
+ * Note: out[] points into 'str'.
+ */
+static bool split_str(char *str, char ***out, size_t *count)
+{
+ char **res;
+ char *lasts;
+ char *s;
+ size_t i;
+ size_t items = 1;
+
+ s = strchr(str, ',');
+ while(s) {
+ items++;
+ s = strchr(++s, ',');
+ }
+
+ res = calloc(items, sizeof(char *));
+ if(!res)
+ return FALSE;
+
+ for(i = 0, s = strtok_r(str, ",", &lasts); s && i < items;
+ s = strtok_r(NULL, ",", &lasts), i++)
+ res[i] = s;
+
+ *out = res;
+ *count = items;
+
+ return TRUE;
+}
+
+/*
+ * Break apart the pieces of an LDAP URL.
+ * Syntax:
+ * ldap://<hostname>:<port>/<base_dn>?<attributes>?<scope>?<filter>?<ext>
+ *
+ * <hostname> already known from 'conn->host.name'.
+ * <port> already known from 'conn->remote_port'.
+ * extract the rest from 'data->state.path+1'. All fields are optional.
+ * e.g.
+ * ldap://<hostname>:<port>/?<attributes>?<scope>?<filter>
+ * yields ludp->lud_dn = "".
+ *
+ * Defined in RFC4516 section 2.
+ */
+static int _ldap_url_parse2(struct Curl_easy *data,
+ const struct connectdata *conn, LDAPURLDesc *ludp)
+{
+ int rc = LDAP_SUCCESS;
+ char *p;
+ char *path;
+ char *q = NULL;
+ char *query = NULL;
+ size_t i;
+
+ if(!data ||
+ !data->state.up.path ||
+ data->state.up.path[0] != '/' ||
+ !strncasecompare("LDAP", data->state.up.scheme, 4))
+ return LDAP_INVALID_SYNTAX;
+
+ ludp->lud_scope = LDAP_SCOPE_BASE;
+ ludp->lud_port = conn->remote_port;
+ ludp->lud_host = conn->host.name;
+
+ /* Duplicate the path */
+ p = path = strdup(data->state.up.path + 1);
+ if(!path)
+ return LDAP_NO_MEMORY;
+
+ /* Duplicate the query if present */
+ if(data->state.up.query) {
+ q = query = strdup(data->state.up.query);
+ if(!query) {
+ free(path);
+ return LDAP_NO_MEMORY;
+ }
+ }
+
+ /* Parse the DN (Distinguished Name) */
+ if(*p) {
+ char *dn = p;
+ char *unescaped;
+ CURLcode result;
+
+ LDAP_TRACE(("DN '%s'\n", dn));
+
+ /* Unescape the DN */
+ result = Curl_urldecode(dn, 0, &unescaped, NULL, REJECT_ZERO);
+ if(result) {
+ rc = LDAP_NO_MEMORY;
+
+ goto quit;
+ }
+
+#if defined(USE_WIN32_LDAP)
+ /* Convert the unescaped string to a tchar */
+ ludp->lud_dn = curlx_convert_UTF8_to_tchar(unescaped);
+
+ /* Free the unescaped string as we are done with it */
+ free(unescaped);
+
+ if(!ludp->lud_dn) {
+ rc = LDAP_NO_MEMORY;
+
+ goto quit;
+ }
+#else
+ ludp->lud_dn = unescaped;
+#endif
+ }
+
+ p = q;
+ if(!p)
+ goto quit;
+
+ /* Parse the attributes. skip "??" */
+ q = strchr(p, '?');
+ if(q)
+ *q++ = '\0';
+
+ if(*p) {
+ char **attributes;
+ size_t count = 0;
+
+ /* Split the string into an array of attributes */
+ if(!split_str(p, &attributes, &count)) {
+ rc = LDAP_NO_MEMORY;
+
+ goto quit;
+ }
+
+ /* Allocate our array (+1 for the NULL entry) */
+#if defined(USE_WIN32_LDAP)
+ ludp->lud_attrs = calloc(count + 1, sizeof(TCHAR *));
+#else
+ ludp->lud_attrs = calloc(count + 1, sizeof(char *));
+#endif
+ if(!ludp->lud_attrs) {
+ free(attributes);
+
+ rc = LDAP_NO_MEMORY;
+
+ goto quit;
+ }
+
+ for(i = 0; i < count; i++) {
+ char *unescaped;
+ CURLcode result;
+
+ LDAP_TRACE(("attr[%zu] '%s'\n", i, attributes[i]));
+
+ /* Unescape the attribute */
+ result = Curl_urldecode(attributes[i], 0, &unescaped, NULL,
+ REJECT_ZERO);
+ if(result) {
+ free(attributes);
+
+ rc = LDAP_NO_MEMORY;
+
+ goto quit;
+ }
+
+#if defined(USE_WIN32_LDAP)
+ /* Convert the unescaped string to a tchar */
+ ludp->lud_attrs[i] = curlx_convert_UTF8_to_tchar(unescaped);
+
+ /* Free the unescaped string as we are done with it */
+ free(unescaped);
+
+ if(!ludp->lud_attrs[i]) {
+ free(attributes);
+
+ rc = LDAP_NO_MEMORY;
+
+ goto quit;
+ }
+#else
+ ludp->lud_attrs[i] = unescaped;
+#endif
+
+ ludp->lud_attrs_dups++;
+ }
+
+ free(attributes);
+ }
+
+ p = q;
+ if(!p)
+ goto quit;
+
+ /* Parse the scope. skip "??" */
+ q = strchr(p, '?');
+ if(q)
+ *q++ = '\0';
+
+ if(*p) {
+ ludp->lud_scope = str2scope(p);
+ if(ludp->lud_scope == -1) {
+ rc = LDAP_INVALID_SYNTAX;
+
+ goto quit;
+ }
+ LDAP_TRACE(("scope %d\n", ludp->lud_scope));
+ }
+
+ p = q;
+ if(!p)
+ goto quit;
+
+ /* Parse the filter */
+ q = strchr(p, '?');
+ if(q)
+ *q++ = '\0';
+
+ if(*p) {
+ char *filter = p;
+ char *unescaped;
+ CURLcode result;
+
+ LDAP_TRACE(("filter '%s'\n", filter));
+
+ /* Unescape the filter */
+ result = Curl_urldecode(filter, 0, &unescaped, NULL, REJECT_ZERO);
+ if(result) {
+ rc = LDAP_NO_MEMORY;
+
+ goto quit;
+ }
+
+#if defined(USE_WIN32_LDAP)
+ /* Convert the unescaped string to a tchar */
+ ludp->lud_filter = curlx_convert_UTF8_to_tchar(unescaped);
+
+ /* Free the unescaped string as we are done with it */
+ free(unescaped);
+
+ if(!ludp->lud_filter) {
+ rc = LDAP_NO_MEMORY;
+
+ goto quit;
+ }
+#else
+ ludp->lud_filter = unescaped;
+#endif
+ }
+
+ p = q;
+ if(p && !*p) {
+ rc = LDAP_INVALID_SYNTAX;
+
+ goto quit;
+ }
+
+quit:
+ free(path);
+ free(query);
+
+ return rc;
+}
+
+static int _ldap_url_parse(struct Curl_easy *data,
+ const struct connectdata *conn,
+ LDAPURLDesc **ludpp)
+{
+ LDAPURLDesc *ludp = calloc(1, sizeof(*ludp));
+ int rc;
+
+ *ludpp = NULL;
+ if(!ludp)
+ return LDAP_NO_MEMORY;
+
+ rc = _ldap_url_parse2(data, conn, ludp);
+ if(rc != LDAP_SUCCESS) {
+ _ldap_free_urldesc(ludp);
+ ludp = NULL;
+ }
+ *ludpp = ludp;
+ return (rc);
+}
+
+static void _ldap_free_urldesc(LDAPURLDesc *ludp)
+{
+ if(!ludp)
+ return;
+
+#if defined(USE_WIN32_LDAP)
+ curlx_unicodefree(ludp->lud_dn);
+ curlx_unicodefree(ludp->lud_filter);
+#else
+ free(ludp->lud_dn);
+ free(ludp->lud_filter);
+#endif
+
+ if(ludp->lud_attrs) {
+ size_t i;
+ for(i = 0; i < ludp->lud_attrs_dups; i++) {
+#if defined(USE_WIN32_LDAP)
+ curlx_unicodefree(ludp->lud_attrs[i]);
+#else
+ free(ludp->lud_attrs[i]);
+#endif
+ }
+ free(ludp->lud_attrs);
+ }
+
+ free(ludp);
+}
+#endif /* !HAVE_LDAP_URL_PARSE */
+#endif /* !CURL_DISABLE_LDAP && !USE_OPENLDAP */
diff --git a/libs/libcurl/src/libcurl.plist b/libs/libcurl/src/libcurl.plist index fee5827e48..bc18b6ff66 100644 --- a/libs/libcurl/src/libcurl.plist +++ b/libs/libcurl/src/libcurl.plist @@ -1,35 +1,35 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd"> -<plist version="0.9"> -<dict> - <key>CFBundleInfoDictionaryVersion</key> - <string>6.0</string> - - <key>CFBundleDevelopmentRegion</key> - <string>English</string> - - <key>CFBundleExecutable</key> - <string>curl</string> - - <key>CFBundleIdentifier</key> - <string>se.curl.libcurl</string> - - <key>CFBundleVersion</key> - <string>7.86.0-DEV</string> - - <key>CFBundleName</key> - <string>libcurl</string> - - <key>CFBundlePackageType</key> - <string>FMWK</string> - - <key>CFBundleSignature</key> - <string>????</string> - - <key>CFBundleShortVersionString</key> - <string>libcurl 7.86.0-DEV</string> - - <key>CFBundleGetInfoString</key> - <string>libcurl.plist 7.86.0-DEV</string> -</dict> -</plist> +<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+<dict>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+
+ <key>CFBundleExecutable</key>
+ <string>curl</string>
+
+ <key>CFBundleIdentifier</key>
+ <string>se.curl.libcurl</string>
+
+ <key>CFBundleVersion</key>
+ <string>7.87.0</string>
+
+ <key>CFBundleName</key>
+ <string>libcurl</string>
+
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+
+ <key>CFBundleSignature</key>
+ <string>????</string>
+
+ <key>CFBundleShortVersionString</key>
+ <string>libcurl 7.87.0</string>
+
+ <key>CFBundleGetInfoString</key>
+ <string>libcurl.plist 7.87.0</string>
+</dict>
+</plist>
diff --git a/libs/libcurl/src/libcurl.plist.in b/libs/libcurl/src/libcurl.plist.in index d2e6492f69..37711f9e5b 100644 --- a/libs/libcurl/src/libcurl.plist.in +++ b/libs/libcurl/src/libcurl.plist.in @@ -1,35 +1,35 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd"> -<plist version="0.9"> -<dict> - <key>CFBundleInfoDictionaryVersion</key> - <string>6.0</string> - - <key>CFBundleDevelopmentRegion</key> - <string>English</string> - - <key>CFBundleExecutable</key> - <string>curl</string> - - <key>CFBundleIdentifier</key> - <string>se.curl.libcurl</string> - - <key>CFBundleVersion</key> - <string>@CURL_PLIST_VERSION@</string> - - <key>CFBundleName</key> - <string>libcurl</string> - - <key>CFBundlePackageType</key> - <string>FMWK</string> - - <key>CFBundleSignature</key> - <string>????</string> - - <key>CFBundleShortVersionString</key> - <string>libcurl @CURL_PLIST_VERSION@</string> - - <key>CFBundleGetInfoString</key> - <string>libcurl.plist @CURL_PLIST_VERSION@</string> -</dict> -</plist> +<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+<dict>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+
+ <key>CFBundleExecutable</key>
+ <string>curl</string>
+
+ <key>CFBundleIdentifier</key>
+ <string>se.curl.libcurl</string>
+
+ <key>CFBundleVersion</key>
+ <string>@CURL_PLIST_VERSION@</string>
+
+ <key>CFBundleName</key>
+ <string>libcurl</string>
+
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+
+ <key>CFBundleSignature</key>
+ <string>????</string>
+
+ <key>CFBundleShortVersionString</key>
+ <string>libcurl @CURL_PLIST_VERSION@</string>
+
+ <key>CFBundleGetInfoString</key>
+ <string>libcurl.plist @CURL_PLIST_VERSION@</string>
+</dict>
+</plist>
diff --git a/libs/libcurl/src/libcurl.rc b/libs/libcurl/src/libcurl.rc index 23134a7102..b864b61238 100644 --- a/libs/libcurl/src/libcurl.rc +++ b/libs/libcurl/src/libcurl.rc @@ -1,65 +1,65 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 <winver.h> -#include "../include/curl/curlver.h" - -LANGUAGE 0, 0 - -#define RC_VERSION LIBCURL_VERSION_MAJOR, LIBCURL_VERSION_MINOR, LIBCURL_VERSION_PATCH, 0 - -VS_VERSION_INFO VERSIONINFO - FILEVERSION RC_VERSION - PRODUCTVERSION RC_VERSION - FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#if defined(DEBUGBUILD) || defined(_DEBUG) - FILEFLAGS VS_FF_DEBUG -#else - FILEFLAGS 0L -#endif - FILEOS VOS__WINDOWS32 - FILETYPE VFT_DLL - FILESUBTYPE 0L - -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "CompanyName", "The curl library, https://curl.se/\0" - VALUE "FileDescription", "libcurl Shared Library\0" - VALUE "FileVersion", LIBCURL_VERSION "\0" - VALUE "InternalName", "libcurl\0" - VALUE "OriginalFilename", "libcurl.dll\0" - VALUE "ProductName", "The curl library\0" - VALUE "ProductVersion", LIBCURL_VERSION "\0" - VALUE "LegalCopyright", "Copyright (C) " LIBCURL_COPYRIGHT "\0" - VALUE "License", "https://curl.se/docs/copyright.html\0" - END - END - - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 <winver.h>
+#include "../include/curl/curlver.h"
+
+LANGUAGE 0, 0
+
+#define RC_VERSION LIBCURL_VERSION_MAJOR, LIBCURL_VERSION_MINOR, LIBCURL_VERSION_PATCH, 0
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION RC_VERSION
+ PRODUCTVERSION RC_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#if defined(DEBUGBUILD) || defined(_DEBUG)
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0L
+
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "The curl library, https://curl.se/\0"
+ VALUE "FileDescription", "libcurl Shared Library\0"
+ VALUE "FileVersion", LIBCURL_VERSION "\0"
+ VALUE "InternalName", "libcurl\0"
+ VALUE "OriginalFilename", "libcurl.dll\0"
+ VALUE "ProductName", "The curl library\0"
+ VALUE "ProductVersion", LIBCURL_VERSION "\0"
+ VALUE "LegalCopyright", "Copyright (C) " LIBCURL_COPYRIGHT "\0"
+ VALUE "License", "https://curl.se/docs/copyright.html\0"
+ END
+ END
+
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/libs/libcurl/src/libcurl.vers.in b/libs/libcurl/src/libcurl.vers.in index ae978a485d..2e01646ea2 100644 --- a/libs/libcurl/src/libcurl.vers.in +++ b/libs/libcurl/src/libcurl.vers.in @@ -1,13 +1,13 @@ -HIDDEN -{ - local: - __*; - _rest*; - _save*; -}; - -CURL_@CURL_LT_SHLIB_VERSIONED_FLAVOUR@4 -{ - global: curl_*; - local: *; -}; +HIDDEN
+{
+ local:
+ __*;
+ _rest*;
+ _save*;
+};
+
+CURL_@CURL_LT_SHLIB_VERSIONED_FLAVOUR@4
+{
+ global: curl_*;
+ local: *;
+};
diff --git a/libs/libcurl/src/llist.c b/libs/libcurl/src/llist.c index fa2d366cb4..229e36623f 100644 --- a/libs/libcurl/src/llist.c +++ b/libs/libcurl/src/llist.c @@ -1,146 +1,146 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" -#include "curl_memory.h" - -/* this must be the last include file */ -#include "memdebug.h" - -/* - * @unittest: 1300 - */ -void -Curl_llist_init(struct Curl_llist *l, Curl_llist_dtor dtor) -{ - l->size = 0; - l->dtor = dtor; - l->head = NULL; - l->tail = NULL; -} - -/* - * Curl_llist_insert_next() - * - * Inserts a new list element after the given one 'e'. If the given existing - * entry is NULL and the list already has elements, the new one will be - * inserted first in the list. - * - * The 'ne' argument should be a pointer into the object to store. - * - * @unittest: 1300 - */ -void -Curl_llist_insert_next(struct Curl_llist *list, struct Curl_llist_element *e, - const void *p, - struct Curl_llist_element *ne) -{ - ne->ptr = (void *) p; - if(list->size == 0) { - list->head = ne; - list->head->prev = NULL; - list->head->next = NULL; - list->tail = ne; - } - else { - /* if 'e' is NULL here, we insert the new element first in the list */ - ne->next = e?e->next:list->head; - ne->prev = e; - if(!e) { - list->head->prev = ne; - list->head = ne; - } - else if(e->next) { - e->next->prev = ne; - } - else { - list->tail = ne; - } - if(e) - e->next = ne; - } - - ++list->size; -} - -/* - * @unittest: 1300 - */ -void -Curl_llist_remove(struct Curl_llist *list, struct Curl_llist_element *e, - void *user) -{ - void *ptr; - if(!e || list->size == 0) - return; - - if(e == list->head) { - list->head = e->next; - - if(!list->head) - list->tail = NULL; - else - e->next->prev = NULL; - } - else { - if(e->prev) - e->prev->next = e->next; - - if(!e->next) - list->tail = e->prev; - else - e->next->prev = e->prev; - } - - ptr = e->ptr; - - e->ptr = NULL; - e->prev = NULL; - e->next = NULL; - - --list->size; - - /* call the dtor() last for when it actually frees the 'e' memory itself */ - if(list->dtor) - list->dtor(user, ptr); -} - -void -Curl_llist_destroy(struct Curl_llist *list, void *user) -{ - if(list) { - while(list->size > 0) - Curl_llist_remove(list, list->tail, user); - } -} - -size_t -Curl_llist_count(struct Curl_llist *list) -{ - return list->size; -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+#include "curl_memory.h"
+
+/* this must be the last include file */
+#include "memdebug.h"
+
+/*
+ * @unittest: 1300
+ */
+void
+Curl_llist_init(struct Curl_llist *l, Curl_llist_dtor dtor)
+{
+ l->size = 0;
+ l->dtor = dtor;
+ l->head = NULL;
+ l->tail = NULL;
+}
+
+/*
+ * Curl_llist_insert_next()
+ *
+ * Inserts a new list element after the given one 'e'. If the given existing
+ * entry is NULL and the list already has elements, the new one will be
+ * inserted first in the list.
+ *
+ * The 'ne' argument should be a pointer into the object to store.
+ *
+ * @unittest: 1300
+ */
+void
+Curl_llist_insert_next(struct Curl_llist *list, struct Curl_llist_element *e,
+ const void *p,
+ struct Curl_llist_element *ne)
+{
+ ne->ptr = (void *) p;
+ if(list->size == 0) {
+ list->head = ne;
+ list->head->prev = NULL;
+ list->head->next = NULL;
+ list->tail = ne;
+ }
+ else {
+ /* if 'e' is NULL here, we insert the new element first in the list */
+ ne->next = e?e->next:list->head;
+ ne->prev = e;
+ if(!e) {
+ list->head->prev = ne;
+ list->head = ne;
+ }
+ else if(e->next) {
+ e->next->prev = ne;
+ }
+ else {
+ list->tail = ne;
+ }
+ if(e)
+ e->next = ne;
+ }
+
+ ++list->size;
+}
+
+/*
+ * @unittest: 1300
+ */
+void
+Curl_llist_remove(struct Curl_llist *list, struct Curl_llist_element *e,
+ void *user)
+{
+ void *ptr;
+ if(!e || list->size == 0)
+ return;
+
+ if(e == list->head) {
+ list->head = e->next;
+
+ if(!list->head)
+ list->tail = NULL;
+ else
+ e->next->prev = NULL;
+ }
+ else {
+ if(e->prev)
+ e->prev->next = e->next;
+
+ if(!e->next)
+ list->tail = e->prev;
+ else
+ e->next->prev = e->prev;
+ }
+
+ ptr = e->ptr;
+
+ e->ptr = NULL;
+ e->prev = NULL;
+ e->next = NULL;
+
+ --list->size;
+
+ /* call the dtor() last for when it actually frees the 'e' memory itself */
+ if(list->dtor)
+ list->dtor(user, ptr);
+}
+
+void
+Curl_llist_destroy(struct Curl_llist *list, void *user)
+{
+ if(list) {
+ while(list->size > 0)
+ Curl_llist_remove(list, list->tail, user);
+ }
+}
+
+size_t
+Curl_llist_count(struct Curl_llist *list)
+{
+ return list->size;
+}
diff --git a/libs/libcurl/src/llist.h b/libs/libcurl/src/llist.h index 2fcb91ca50..e25bcee041 100644 --- a/libs/libcurl/src/llist.h +++ b/libs/libcurl/src/llist.h @@ -1,52 +1,52 @@ -#ifndef HEADER_CURL_LLIST_H -#define HEADER_CURL_LLIST_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" -#include <stddef.h> - -typedef void (*Curl_llist_dtor)(void *, void *); - -struct Curl_llist_element { - void *ptr; - struct Curl_llist_element *prev; - struct Curl_llist_element *next; -}; - -struct Curl_llist { - struct Curl_llist_element *head; - struct Curl_llist_element *tail; - Curl_llist_dtor dtor; - size_t size; -}; - -void Curl_llist_init(struct Curl_llist *, Curl_llist_dtor); -void Curl_llist_insert_next(struct Curl_llist *, struct Curl_llist_element *, - const void *, struct Curl_llist_element *node); -void Curl_llist_remove(struct Curl_llist *, struct Curl_llist_element *, - void *); -size_t Curl_llist_count(struct Curl_llist *); -void Curl_llist_destroy(struct Curl_llist *, void *); -#endif /* HEADER_CURL_LLIST_H */ +#ifndef HEADER_CURL_LLIST_H
+#define HEADER_CURL_LLIST_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+#include <stddef.h>
+
+typedef void (*Curl_llist_dtor)(void *, void *);
+
+struct Curl_llist_element {
+ void *ptr;
+ struct Curl_llist_element *prev;
+ struct Curl_llist_element *next;
+};
+
+struct Curl_llist {
+ struct Curl_llist_element *head;
+ struct Curl_llist_element *tail;
+ Curl_llist_dtor dtor;
+ size_t size;
+};
+
+void Curl_llist_init(struct Curl_llist *, Curl_llist_dtor);
+void Curl_llist_insert_next(struct Curl_llist *, struct Curl_llist_element *,
+ const void *, struct Curl_llist_element *node);
+void Curl_llist_remove(struct Curl_llist *, struct Curl_llist_element *,
+ void *);
+size_t Curl_llist_count(struct Curl_llist *);
+void Curl_llist_destroy(struct Curl_llist *, void *);
+#endif /* HEADER_CURL_LLIST_H */
diff --git a/libs/libcurl/src/makefile.amiga b/libs/libcurl/src/makefile.amiga deleted file mode 100644 index d5c8fe615e..0000000000 --- a/libs/libcurl/src/makefile.amiga +++ /dev/null @@ -1,44 +0,0 @@ -#*************************************************************************** -# _ _ ____ _ -# Project ___| | | | _ \| | -# / __| | | | |_) | | -# | (__| |_| | _ <| |___ -# \___|\___/|_| \_\_____| -# -# Copyright (C) 1998 - 2022, 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 -# -########################################################################### -# -# libcurl Makefile for AmigaOS ... -# - -# change the follow to where you have the AmiTCP SDK v4.3 includes: - -ATCPSDKI= /GG/netinclude - - -CC = m68k-amigaos-gcc -CFLAGS = -I$(ATCPSDKI) -m68020-60 -O2 -msoft-float -noixemul -g -I. -I../include -W -Wall -DUSE_OPENSSL -DHAVE_LIBZ - -include Makefile.inc -OBJS = $(CSOURCES:.c=.o) - -all: $(OBJS) - ar cru libcurl.a $(OBJS) - ranlib libcurl.a - -install: - $(INSTALL) -c ./libcurl.a /lib/libcurl.a diff --git a/libs/libcurl/src/makefile.dj b/libs/libcurl/src/makefile.dj deleted file mode 100644 index bb95f2bbfd..0000000000 --- a/libs/libcurl/src/makefile.dj +++ /dev/null @@ -1,73 +0,0 @@ -#*************************************************************************** -# _ _ ____ _ -# Project ___| | | | _ \| | -# / __| | | | |_) | | -# | (__| |_| | _ <| |___ -# \___|\___/|_| \_\_____| -# -# Copyright (C) 2003 - 2022, Gisle Vanem <gvanem@yahoo.no>. -# Copyright (C) 2003 - 2020, 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 -# -#*************************************************************************** - -# -# Adapted for djgpp2 / Watt-32 / DOS -# - -DEPEND_PREREQ = curl_config.h -VPATH = vtls vauth vquic vssh -TOPDIR = .. - -include ../packages/DOS/common.dj -include Makefile.inc - -CFLAGS += -DBUILDING_LIBCURL - -SOURCES = $(sort $(CSOURCES)) -OBJECTS = $(addprefix $(OBJ_DIR)/, $(notdir $(SOURCES:.c=.o))) - -CURL_LIB = libcurl.a - -all: $(OBJ_DIR) curl_config.h $(CURL_LIB) - -$(CURL_LIB): $(OBJECTS) - ar rs $@ $? - -curl_config.h: config-dos.h - $(COPY) $^ $@ - -# clean generated files -# -genclean: - - $(DELETE) curl_config.h - -# clean object files and subdir -# -objclean: genclean - - $(DELETE) $(OBJ_DIR)$(DS)*.o - - $(RMDIR) $(OBJ_DIR) - -# clean without removing built library -# -clean: objclean - - $(DELETE) depend.dj - -# clean everything -# -realclean vclean: clean - - $(DELETE) $(CURL_LIB) - --include depend.dj diff --git a/libs/libcurl/src/md4.c b/libs/libcurl/src/md4.c index e976fe7bdc..afd0c4e94e 100644 --- a/libs/libcurl/src/md4.c +++ b/libs/libcurl/src/md4.c @@ -1,520 +1,507 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_CRYPTO_AUTH) - -#include "curl_md4.h" -#include "warnless.h" - - -#ifdef USE_OPENSSL -#include <openssl/opensslconf.h> -#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) && \ - !defined(USE_AMISSL) -/* OpenSSL 3.0.0 marks the MD4 functions as deprecated */ -#define OPENSSL_NO_MD4 -#endif -#endif /* USE_OPENSSL */ - -#ifdef USE_WOLFSSL -#include <wolfssl/options.h> -#ifdef NO_MD4 -#define WOLFSSL_NO_MD4 -#endif -#endif - -#ifdef USE_MBEDTLS -#include <mbedtls/version.h> -#if MBEDTLS_VERSION_NUMBER >= 0x03000000 -#include <mbedtls/mbedtls_config.h> -#else -#include <mbedtls/config.h> -#endif - -#if(MBEDTLS_VERSION_NUMBER >= 0x02070000) - #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS -#endif -#endif /* USE_MBEDTLS */ - -#if defined(USE_GNUTLS) - -#include <nettle/md4.h> - -#include "curl_memory.h" - -/* The last #include file should be: */ -#include "memdebug.h" - -typedef struct md4_ctx MD4_CTX; - -static void MD4_Init(MD4_CTX *ctx) -{ - md4_init(ctx); -} - -static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) -{ - md4_update(ctx, size, data); -} - -static void MD4_Final(unsigned char *result, MD4_CTX *ctx) -{ - md4_digest(ctx, MD4_DIGEST_SIZE, result); -} - -/* When OpenSSL or wolfSSL is available, we use their MD4 functions. */ -#elif defined(USE_WOLFSSL) && !defined(WOLFSSL_NO_MD4) -#include <wolfssl/openssl/md4.h> - -#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4) -#include <openssl/md4.h> - -#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ - (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040) && \ - defined(__MAC_OS_X_VERSION_MIN_ALLOWED) && \ - (__MAC_OS_X_VERSION_MIN_ALLOWED < 101500)) || \ - (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ - (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000)) - -#include <CommonCrypto/CommonDigest.h> - -#include "curl_memory.h" - -/* The last #include file should be: */ -#include "memdebug.h" - -typedef CC_MD4_CTX MD4_CTX; - -static void MD4_Init(MD4_CTX *ctx) -{ - (void)CC_MD4_Init(ctx); -} - -static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) -{ - (void)CC_MD4_Update(ctx, data, (CC_LONG)size); -} - -static void MD4_Final(unsigned char *result, MD4_CTX *ctx) -{ - (void)CC_MD4_Final(result, ctx); -} - -#elif defined(USE_WIN32_CRYPTO) - -#include <wincrypt.h> - -#include "curl_memory.h" - -/* The last #include file should be: */ -#include "memdebug.h" - -struct md4_ctx { - HCRYPTPROV hCryptProv; - HCRYPTHASH hHash; -}; -typedef struct md4_ctx MD4_CTX; - -static void MD4_Init(MD4_CTX *ctx) -{ - ctx->hCryptProv = 0; - ctx->hHash = 0; - - if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { - CryptCreateHash(ctx->hCryptProv, CALG_MD4, 0, 0, &ctx->hHash); - } -} - -static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) -{ - CryptHashData(ctx->hHash, (BYTE *)data, (unsigned int) size, 0); -} - -static void MD4_Final(unsigned char *result, MD4_CTX *ctx) -{ - unsigned long length = 0; - - CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0); - if(length == MD4_DIGEST_LENGTH) - CryptGetHashParam(ctx->hHash, HP_HASHVAL, result, &length, 0); - - if(ctx->hHash) - CryptDestroyHash(ctx->hHash); - - if(ctx->hCryptProv) - CryptReleaseContext(ctx->hCryptProv, 0); -} - -#elif(defined(USE_MBEDTLS) && defined(MBEDTLS_MD4_C)) - -#include <mbedtls/md4.h> - -#include "curl_memory.h" - -/* The last #include file should be: */ -#include "memdebug.h" - -struct md4_ctx { - void *data; - unsigned long size; -}; -typedef struct md4_ctx MD4_CTX; - -static void MD4_Init(MD4_CTX *ctx) -{ - ctx->data = NULL; - ctx->size = 0; -} - -static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) -{ - if(!ctx->data) { - ctx->data = malloc(size); - if(ctx->data) { - memcpy(ctx->data, data, size); - ctx->size = size; - } - } -} - -static void MD4_Final(unsigned char *result, MD4_CTX *ctx) -{ - if(ctx->data) { -#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) - mbedtls_md4(ctx->data, ctx->size, result); -#else - (void) mbedtls_md4_ret(ctx->data, ctx->size, result); -#endif - - Curl_safefree(ctx->data); - ctx->size = 0; - } -} - -#else -/* When no other crypto library is available, or the crypto library doesn't - * support MD4, we use this code segment this implementation of it - * - * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. - * MD4 Message-Digest Algorithm (RFC 1320). - * - * Homepage: - https://openwall.info/wiki/people/solar/software/public-domain-source-code/md4 - * - * Author: - * Alexander Peslyak, better known as Solar Designer <solar at openwall.com> - * - * This software was written by Alexander Peslyak in 2001. No copyright is - * claimed, and the software is hereby placed in the public domain. In case - * this attempt to disclaim copyright and place the software in the public - * domain is deemed null and void, then the software is Copyright (c) 2001 - * Alexander Peslyak and it is hereby released to the general public under the - * following terms: - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted. - * - * There's ABSOLUTELY NO WARRANTY, express or implied. - * - * (This is a heavily cut-down "BSD license".) - * - * This differs from Colin Plumb's older public domain implementation in that - * no exactly 32-bit integer data type is required (any 32-bit or wider - * unsigned integer data type will do), there's no compile-time endianness - * configuration, and the function prototypes match OpenSSL's. No code from - * Colin Plumb's implementation has been reused; this comment merely compares - * the properties of the two independent implementations. - * - * The primary goals of this implementation are portability and ease of use. - * It is meant to be fast, but not as fast as possible. Some known - * optimizations are not included to reduce source code size and avoid - * compile-time configuration. - */ - - -#include <string.h> - -/* Any 32-bit or wider unsigned integer data type will do */ -typedef unsigned int MD4_u32plus; - -struct md4_ctx { - MD4_u32plus lo, hi; - MD4_u32plus a, b, c, d; - unsigned char buffer[64]; - MD4_u32plus block[16]; -}; -typedef struct md4_ctx MD4_CTX; - -static void MD4_Init(MD4_CTX *ctx); -static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size); -static void MD4_Final(unsigned char *result, MD4_CTX *ctx); - -/* - * The basic MD4 functions. - * - * F and G are optimized compared to their RFC 1320 definitions, with the - * optimization for F borrowed from Colin Plumb's MD5 implementation. - */ -#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) -#define G(x, y, z) (((x) & ((y) | (z))) | ((y) & (z))) -#define H(x, y, z) ((x) ^ (y) ^ (z)) - -/* - * The MD4 transformation for all three rounds. - */ -#define STEP(f, a, b, c, d, x, s) \ - (a) += f((b), (c), (d)) + (x); \ - (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); - -/* - * SET reads 4 input bytes in little-endian byte order and stores them - * in a properly aligned word in host byte order. - * - * The check for little-endian architectures that tolerate unaligned - * memory accesses is just an optimization. Nothing will break if it - * doesn't work. - */ -#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) -#define SET(n) \ - (*(MD4_u32plus *)(void *)&ptr[(n) * 4]) -#define GET(n) \ - SET(n) -#else -#define SET(n) \ - (ctx->block[(n)] = \ - (MD4_u32plus)ptr[(n) * 4] | \ - ((MD4_u32plus)ptr[(n) * 4 + 1] << 8) | \ - ((MD4_u32plus)ptr[(n) * 4 + 2] << 16) | \ - ((MD4_u32plus)ptr[(n) * 4 + 3] << 24)) -#define GET(n) \ - (ctx->block[(n)]) -#endif - -/* - * This processes one or more 64-byte data blocks, but does NOT update - * the bit counters. There are no alignment requirements. - */ -static const void *body(MD4_CTX *ctx, const void *data, unsigned long size) -{ - const unsigned char *ptr; - MD4_u32plus a, b, c, d; - - ptr = (const unsigned char *)data; - - a = ctx->a; - b = ctx->b; - c = ctx->c; - d = ctx->d; - - do { - MD4_u32plus saved_a, saved_b, saved_c, saved_d; - - saved_a = a; - saved_b = b; - saved_c = c; - saved_d = d; - -/* Round 1 */ - STEP(F, a, b, c, d, SET(0), 3) - STEP(F, d, a, b, c, SET(1), 7) - STEP(F, c, d, a, b, SET(2), 11) - STEP(F, b, c, d, a, SET(3), 19) - STEP(F, a, b, c, d, SET(4), 3) - STEP(F, d, a, b, c, SET(5), 7) - STEP(F, c, d, a, b, SET(6), 11) - STEP(F, b, c, d, a, SET(7), 19) - STEP(F, a, b, c, d, SET(8), 3) - STEP(F, d, a, b, c, SET(9), 7) - STEP(F, c, d, a, b, SET(10), 11) - STEP(F, b, c, d, a, SET(11), 19) - STEP(F, a, b, c, d, SET(12), 3) - STEP(F, d, a, b, c, SET(13), 7) - STEP(F, c, d, a, b, SET(14), 11) - STEP(F, b, c, d, a, SET(15), 19) - -/* Round 2 */ - STEP(G, a, b, c, d, GET(0) + 0x5a827999, 3) - STEP(G, d, a, b, c, GET(4) + 0x5a827999, 5) - STEP(G, c, d, a, b, GET(8) + 0x5a827999, 9) - STEP(G, b, c, d, a, GET(12) + 0x5a827999, 13) - STEP(G, a, b, c, d, GET(1) + 0x5a827999, 3) - STEP(G, d, a, b, c, GET(5) + 0x5a827999, 5) - STEP(G, c, d, a, b, GET(9) + 0x5a827999, 9) - STEP(G, b, c, d, a, GET(13) + 0x5a827999, 13) - STEP(G, a, b, c, d, GET(2) + 0x5a827999, 3) - STEP(G, d, a, b, c, GET(6) + 0x5a827999, 5) - STEP(G, c, d, a, b, GET(10) + 0x5a827999, 9) - STEP(G, b, c, d, a, GET(14) + 0x5a827999, 13) - STEP(G, a, b, c, d, GET(3) + 0x5a827999, 3) - STEP(G, d, a, b, c, GET(7) + 0x5a827999, 5) - STEP(G, c, d, a, b, GET(11) + 0x5a827999, 9) - STEP(G, b, c, d, a, GET(15) + 0x5a827999, 13) - -/* Round 3 */ - STEP(H, a, b, c, d, GET(0) + 0x6ed9eba1, 3) - STEP(H, d, a, b, c, GET(8) + 0x6ed9eba1, 9) - STEP(H, c, d, a, b, GET(4) + 0x6ed9eba1, 11) - STEP(H, b, c, d, a, GET(12) + 0x6ed9eba1, 15) - STEP(H, a, b, c, d, GET(2) + 0x6ed9eba1, 3) - STEP(H, d, a, b, c, GET(10) + 0x6ed9eba1, 9) - STEP(H, c, d, a, b, GET(6) + 0x6ed9eba1, 11) - STEP(H, b, c, d, a, GET(14) + 0x6ed9eba1, 15) - STEP(H, a, b, c, d, GET(1) + 0x6ed9eba1, 3) - STEP(H, d, a, b, c, GET(9) + 0x6ed9eba1, 9) - STEP(H, c, d, a, b, GET(5) + 0x6ed9eba1, 11) - STEP(H, b, c, d, a, GET(13) + 0x6ed9eba1, 15) - STEP(H, a, b, c, d, GET(3) + 0x6ed9eba1, 3) - STEP(H, d, a, b, c, GET(11) + 0x6ed9eba1, 9) - STEP(H, c, d, a, b, GET(7) + 0x6ed9eba1, 11) - STEP(H, b, c, d, a, GET(15) + 0x6ed9eba1, 15) - - a += saved_a; - b += saved_b; - c += saved_c; - d += saved_d; - - ptr += 64; - } while(size -= 64); - - ctx->a = a; - ctx->b = b; - ctx->c = c; - ctx->d = d; - - return ptr; -} - -static void MD4_Init(MD4_CTX *ctx) -{ - ctx->a = 0x67452301; - ctx->b = 0xefcdab89; - ctx->c = 0x98badcfe; - ctx->d = 0x10325476; - - ctx->lo = 0; - ctx->hi = 0; -} - -static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) -{ - MD4_u32plus saved_lo; - unsigned long used; - - saved_lo = ctx->lo; - ctx->lo = (saved_lo + size) & 0x1fffffff; - if(ctx->lo < saved_lo) - ctx->hi++; - ctx->hi += (MD4_u32plus)size >> 29; - - used = saved_lo & 0x3f; - - if(used) { - unsigned long available = 64 - used; - - if(size < available) { - memcpy(&ctx->buffer[used], data, size); - return; - } - - memcpy(&ctx->buffer[used], data, available); - data = (const unsigned char *)data + available; - size -= available; - body(ctx, ctx->buffer, 64); - } - - if(size >= 64) { - data = body(ctx, data, size & ~(unsigned long)0x3f); - size &= 0x3f; - } - - memcpy(ctx->buffer, data, size); -} - -static void MD4_Final(unsigned char *result, MD4_CTX *ctx) -{ - unsigned long used, available; - - used = ctx->lo & 0x3f; - - ctx->buffer[used++] = 0x80; - - available = 64 - used; - - if(available < 8) { - memset(&ctx->buffer[used], 0, available); - body(ctx, ctx->buffer, 64); - used = 0; - available = 64; - } - - memset(&ctx->buffer[used], 0, available - 8); - - ctx->lo <<= 3; - ctx->buffer[56] = curlx_ultouc((ctx->lo)&0xff); - ctx->buffer[57] = curlx_ultouc((ctx->lo >> 8)&0xff); - ctx->buffer[58] = curlx_ultouc((ctx->lo >> 16)&0xff); - ctx->buffer[59] = curlx_ultouc((ctx->lo >> 24)&0xff); - ctx->buffer[60] = curlx_ultouc((ctx->hi)&0xff); - ctx->buffer[61] = curlx_ultouc((ctx->hi >> 8)&0xff); - ctx->buffer[62] = curlx_ultouc((ctx->hi >> 16)&0xff); - ctx->buffer[63] = curlx_ultouc(ctx->hi >> 24); - - body(ctx, ctx->buffer, 64); - - result[0] = curlx_ultouc((ctx->a)&0xff); - result[1] = curlx_ultouc((ctx->a >> 8)&0xff); - result[2] = curlx_ultouc((ctx->a >> 16)&0xff); - result[3] = curlx_ultouc(ctx->a >> 24); - result[4] = curlx_ultouc((ctx->b)&0xff); - result[5] = curlx_ultouc((ctx->b >> 8)&0xff); - result[6] = curlx_ultouc((ctx->b >> 16)&0xff); - result[7] = curlx_ultouc(ctx->b >> 24); - result[8] = curlx_ultouc((ctx->c)&0xff); - result[9] = curlx_ultouc((ctx->c >> 8)&0xff); - result[10] = curlx_ultouc((ctx->c >> 16)&0xff); - result[11] = curlx_ultouc(ctx->c >> 24); - result[12] = curlx_ultouc((ctx->d)&0xff); - result[13] = curlx_ultouc((ctx->d >> 8)&0xff); - result[14] = curlx_ultouc((ctx->d >> 16)&0xff); - result[15] = curlx_ultouc(ctx->d >> 24); - - memset(ctx, 0, sizeof(*ctx)); -} - -#endif /* CRYPTO LIBS */ - -void Curl_md4it(unsigned char *output, const unsigned char *input, - const size_t len) -{ - MD4_CTX ctx; - - MD4_Init(&ctx); - MD4_Update(&ctx, input, curlx_uztoui(len)); - MD4_Final(output, &ctx); -} - -#endif /* CURL_DISABLE_CRYPTO_AUTH */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_CRYPTO_AUTH)
+
+#include <string.h>
+
+#include "curl_md4.h"
+#include "warnless.h"
+
+#ifdef USE_OPENSSL
+#include <openssl/opensslconf.h>
+#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) && \
+ !defined(USE_AMISSL)
+/* OpenSSL 3.0.0 marks the MD4 functions as deprecated */
+#define OPENSSL_NO_MD4
+#endif
+#endif /* USE_OPENSSL */
+
+#ifdef USE_WOLFSSL
+#include <wolfssl/options.h>
+#ifdef NO_MD4
+#define WOLFSSL_NO_MD4
+#endif
+#endif
+
+#ifdef USE_MBEDTLS
+#include <mbedtls/version.h>
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+#include <mbedtls/mbedtls_config.h>
+#else
+#include <mbedtls/config.h>
+#endif
+#if(MBEDTLS_VERSION_NUMBER >= 0x02070000)
+ #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS
+#endif
+#endif /* USE_MBEDTLS */
+
+#if defined(USE_GNUTLS)
+#include <nettle/md4.h>
+/* When OpenSSL or wolfSSL is available, we use their MD4 functions. */
+#elif defined(USE_WOLFSSL) && !defined(WOLFSSL_NO_MD4)
+#include <wolfssl/openssl/md4.h>
+#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4)
+#include <openssl/md4.h>
+#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \
+ (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040) && \
+ defined(__MAC_OS_X_VERSION_MIN_ALLOWED) && \
+ (__MAC_OS_X_VERSION_MIN_ALLOWED < 101500)) || \
+ (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \
+ (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000))
+#define AN_APPLE_OS
+#include <CommonCrypto/CommonDigest.h>
+#elif defined(USE_WIN32_CRYPTO)
+#include <wincrypt.h>
+#elif(defined(USE_MBEDTLS) && defined(MBEDTLS_MD4_C))
+#include <mbedtls/md4.h>
+#endif
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+#if defined(USE_WOLFSSL) && !defined(WOLFSSL_NO_MD4)
+
+#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4)
+
+#elif defined(USE_GNUTLS)
+
+typedef struct md4_ctx MD4_CTX;
+
+static void MD4_Init(MD4_CTX *ctx)
+{
+ md4_init(ctx);
+}
+
+static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
+{
+ md4_update(ctx, size, data);
+}
+
+static void MD4_Final(unsigned char *result, MD4_CTX *ctx)
+{
+ md4_digest(ctx, MD4_DIGEST_SIZE, result);
+}
+
+#elif defined(AN_APPLE_OS)
+typedef CC_MD4_CTX MD4_CTX;
+
+static void MD4_Init(MD4_CTX *ctx)
+{
+ (void)CC_MD4_Init(ctx);
+}
+
+static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
+{
+ (void)CC_MD4_Update(ctx, data, (CC_LONG)size);
+}
+
+static void MD4_Final(unsigned char *result, MD4_CTX *ctx)
+{
+ (void)CC_MD4_Final(result, ctx);
+}
+
+#elif defined(USE_WIN32_CRYPTO)
+
+struct md4_ctx {
+ HCRYPTPROV hCryptProv;
+ HCRYPTHASH hHash;
+};
+typedef struct md4_ctx MD4_CTX;
+
+static void MD4_Init(MD4_CTX *ctx)
+{
+ ctx->hCryptProv = 0;
+ ctx->hHash = 0;
+
+ if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
+ CryptCreateHash(ctx->hCryptProv, CALG_MD4, 0, 0, &ctx->hHash);
+ }
+}
+
+static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
+{
+ CryptHashData(ctx->hHash, (BYTE *)data, (unsigned int) size, 0);
+}
+
+static void MD4_Final(unsigned char *result, MD4_CTX *ctx)
+{
+ unsigned long length = 0;
+
+ CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0);
+ if(length == MD4_DIGEST_LENGTH)
+ CryptGetHashParam(ctx->hHash, HP_HASHVAL, result, &length, 0);
+
+ if(ctx->hHash)
+ CryptDestroyHash(ctx->hHash);
+
+ if(ctx->hCryptProv)
+ CryptReleaseContext(ctx->hCryptProv, 0);
+}
+
+#elif(defined(USE_MBEDTLS) && defined(MBEDTLS_MD4_C))
+
+struct md4_ctx {
+ void *data;
+ unsigned long size;
+};
+typedef struct md4_ctx MD4_CTX;
+
+static void MD4_Init(MD4_CTX *ctx)
+{
+ ctx->data = NULL;
+ ctx->size = 0;
+}
+
+static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
+{
+ if(!ctx->data) {
+ ctx->data = malloc(size);
+ if(ctx->data) {
+ memcpy(ctx->data, data, size);
+ ctx->size = size;
+ }
+ }
+}
+
+static void MD4_Final(unsigned char *result, MD4_CTX *ctx)
+{
+ if(ctx->data) {
+#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
+ mbedtls_md4(ctx->data, ctx->size, result);
+#else
+ (void) mbedtls_md4_ret(ctx->data, ctx->size, result);
+#endif
+
+ Curl_safefree(ctx->data);
+ ctx->size = 0;
+ }
+}
+
+#else
+/* When no other crypto library is available, or the crypto library doesn't
+ * support MD4, we use this code segment this implementation of it
+ *
+ * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
+ * MD4 Message-Digest Algorithm (RFC 1320).
+ *
+ * Homepage:
+ https://openwall.info/wiki/people/solar/software/public-domain-source-code/md4
+ *
+ * Author:
+ * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
+ *
+ * This software was written by Alexander Peslyak in 2001. No copyright is
+ * claimed, and the software is hereby placed in the public domain. In case
+ * this attempt to disclaim copyright and place the software in the public
+ * domain is deemed null and void, then the software is Copyright (c) 2001
+ * Alexander Peslyak and it is hereby released to the general public under the
+ * following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * (This is a heavily cut-down "BSD license".)
+ *
+ * This differs from Colin Plumb's older public domain implementation in that
+ * no exactly 32-bit integer data type is required (any 32-bit or wider
+ * unsigned integer data type will do), there's no compile-time endianness
+ * configuration, and the function prototypes match OpenSSL's. No code from
+ * Colin Plumb's implementation has been reused; this comment merely compares
+ * the properties of the two independent implementations.
+ *
+ * The primary goals of this implementation are portability and ease of use.
+ * It is meant to be fast, but not as fast as possible. Some known
+ * optimizations are not included to reduce source code size and avoid
+ * compile-time configuration.
+ */
+
+/* Any 32-bit or wider unsigned integer data type will do */
+typedef unsigned int MD4_u32plus;
+
+struct md4_ctx {
+ MD4_u32plus lo, hi;
+ MD4_u32plus a, b, c, d;
+ unsigned char buffer[64];
+ MD4_u32plus block[16];
+};
+typedef struct md4_ctx MD4_CTX;
+
+static void MD4_Init(MD4_CTX *ctx);
+static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size);
+static void MD4_Final(unsigned char *result, MD4_CTX *ctx);
+
+/*
+ * The basic MD4 functions.
+ *
+ * F and G are optimized compared to their RFC 1320 definitions, with the
+ * optimization for F borrowed from Colin Plumb's MD5 implementation.
+ */
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z) (((x) & ((y) | (z))) | ((y) & (z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+
+/*
+ * The MD4 transformation for all three rounds.
+ */
+#define STEP(f, a, b, c, d, x, s) \
+ (a) += f((b), (c), (d)) + (x); \
+ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s))));
+
+/*
+ * SET reads 4 input bytes in little-endian byte order and stores them
+ * in a properly aligned word in host byte order.
+ *
+ * The check for little-endian architectures that tolerate unaligned
+ * memory accesses is just an optimization. Nothing will break if it
+ * doesn't work.
+ */
+#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
+#define SET(n) \
+ (*(MD4_u32plus *)(void *)&ptr[(n) * 4])
+#define GET(n) \
+ SET(n)
+#else
+#define SET(n) \
+ (ctx->block[(n)] = \
+ (MD4_u32plus)ptr[(n) * 4] | \
+ ((MD4_u32plus)ptr[(n) * 4 + 1] << 8) | \
+ ((MD4_u32plus)ptr[(n) * 4 + 2] << 16) | \
+ ((MD4_u32plus)ptr[(n) * 4 + 3] << 24))
+#define GET(n) \
+ (ctx->block[(n)])
+#endif
+
+/*
+ * This processes one or more 64-byte data blocks, but does NOT update
+ * the bit counters. There are no alignment requirements.
+ */
+static const void *body(MD4_CTX *ctx, const void *data, unsigned long size)
+{
+ const unsigned char *ptr;
+ MD4_u32plus a, b, c, d;
+
+ ptr = (const unsigned char *)data;
+
+ a = ctx->a;
+ b = ctx->b;
+ c = ctx->c;
+ d = ctx->d;
+
+ do {
+ MD4_u32plus saved_a, saved_b, saved_c, saved_d;
+
+ saved_a = a;
+ saved_b = b;
+ saved_c = c;
+ saved_d = d;
+
+/* Round 1 */
+ STEP(F, a, b, c, d, SET(0), 3)
+ STEP(F, d, a, b, c, SET(1), 7)
+ STEP(F, c, d, a, b, SET(2), 11)
+ STEP(F, b, c, d, a, SET(3), 19)
+ STEP(F, a, b, c, d, SET(4), 3)
+ STEP(F, d, a, b, c, SET(5), 7)
+ STEP(F, c, d, a, b, SET(6), 11)
+ STEP(F, b, c, d, a, SET(7), 19)
+ STEP(F, a, b, c, d, SET(8), 3)
+ STEP(F, d, a, b, c, SET(9), 7)
+ STEP(F, c, d, a, b, SET(10), 11)
+ STEP(F, b, c, d, a, SET(11), 19)
+ STEP(F, a, b, c, d, SET(12), 3)
+ STEP(F, d, a, b, c, SET(13), 7)
+ STEP(F, c, d, a, b, SET(14), 11)
+ STEP(F, b, c, d, a, SET(15), 19)
+
+/* Round 2 */
+ STEP(G, a, b, c, d, GET(0) + 0x5a827999, 3)
+ STEP(G, d, a, b, c, GET(4) + 0x5a827999, 5)
+ STEP(G, c, d, a, b, GET(8) + 0x5a827999, 9)
+ STEP(G, b, c, d, a, GET(12) + 0x5a827999, 13)
+ STEP(G, a, b, c, d, GET(1) + 0x5a827999, 3)
+ STEP(G, d, a, b, c, GET(5) + 0x5a827999, 5)
+ STEP(G, c, d, a, b, GET(9) + 0x5a827999, 9)
+ STEP(G, b, c, d, a, GET(13) + 0x5a827999, 13)
+ STEP(G, a, b, c, d, GET(2) + 0x5a827999, 3)
+ STEP(G, d, a, b, c, GET(6) + 0x5a827999, 5)
+ STEP(G, c, d, a, b, GET(10) + 0x5a827999, 9)
+ STEP(G, b, c, d, a, GET(14) + 0x5a827999, 13)
+ STEP(G, a, b, c, d, GET(3) + 0x5a827999, 3)
+ STEP(G, d, a, b, c, GET(7) + 0x5a827999, 5)
+ STEP(G, c, d, a, b, GET(11) + 0x5a827999, 9)
+ STEP(G, b, c, d, a, GET(15) + 0x5a827999, 13)
+
+/* Round 3 */
+ STEP(H, a, b, c, d, GET(0) + 0x6ed9eba1, 3)
+ STEP(H, d, a, b, c, GET(8) + 0x6ed9eba1, 9)
+ STEP(H, c, d, a, b, GET(4) + 0x6ed9eba1, 11)
+ STEP(H, b, c, d, a, GET(12) + 0x6ed9eba1, 15)
+ STEP(H, a, b, c, d, GET(2) + 0x6ed9eba1, 3)
+ STEP(H, d, a, b, c, GET(10) + 0x6ed9eba1, 9)
+ STEP(H, c, d, a, b, GET(6) + 0x6ed9eba1, 11)
+ STEP(H, b, c, d, a, GET(14) + 0x6ed9eba1, 15)
+ STEP(H, a, b, c, d, GET(1) + 0x6ed9eba1, 3)
+ STEP(H, d, a, b, c, GET(9) + 0x6ed9eba1, 9)
+ STEP(H, c, d, a, b, GET(5) + 0x6ed9eba1, 11)
+ STEP(H, b, c, d, a, GET(13) + 0x6ed9eba1, 15)
+ STEP(H, a, b, c, d, GET(3) + 0x6ed9eba1, 3)
+ STEP(H, d, a, b, c, GET(11) + 0x6ed9eba1, 9)
+ STEP(H, c, d, a, b, GET(7) + 0x6ed9eba1, 11)
+ STEP(H, b, c, d, a, GET(15) + 0x6ed9eba1, 15)
+
+ a += saved_a;
+ b += saved_b;
+ c += saved_c;
+ d += saved_d;
+
+ ptr += 64;
+ } while(size -= 64);
+
+ ctx->a = a;
+ ctx->b = b;
+ ctx->c = c;
+ ctx->d = d;
+
+ return ptr;
+}
+
+static void MD4_Init(MD4_CTX *ctx)
+{
+ ctx->a = 0x67452301;
+ ctx->b = 0xefcdab89;
+ ctx->c = 0x98badcfe;
+ ctx->d = 0x10325476;
+
+ ctx->lo = 0;
+ ctx->hi = 0;
+}
+
+static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
+{
+ MD4_u32plus saved_lo;
+ unsigned long used;
+
+ saved_lo = ctx->lo;
+ ctx->lo = (saved_lo + size) & 0x1fffffff;
+ if(ctx->lo < saved_lo)
+ ctx->hi++;
+ ctx->hi += (MD4_u32plus)size >> 29;
+
+ used = saved_lo & 0x3f;
+
+ if(used) {
+ unsigned long available = 64 - used;
+
+ if(size < available) {
+ memcpy(&ctx->buffer[used], data, size);
+ return;
+ }
+
+ memcpy(&ctx->buffer[used], data, available);
+ data = (const unsigned char *)data + available;
+ size -= available;
+ body(ctx, ctx->buffer, 64);
+ }
+
+ if(size >= 64) {
+ data = body(ctx, data, size & ~(unsigned long)0x3f);
+ size &= 0x3f;
+ }
+
+ memcpy(ctx->buffer, data, size);
+}
+
+static void MD4_Final(unsigned char *result, MD4_CTX *ctx)
+{
+ unsigned long used, available;
+
+ used = ctx->lo & 0x3f;
+
+ ctx->buffer[used++] = 0x80;
+
+ available = 64 - used;
+
+ if(available < 8) {
+ memset(&ctx->buffer[used], 0, available);
+ body(ctx, ctx->buffer, 64);
+ used = 0;
+ available = 64;
+ }
+
+ memset(&ctx->buffer[used], 0, available - 8);
+
+ ctx->lo <<= 3;
+ ctx->buffer[56] = curlx_ultouc((ctx->lo)&0xff);
+ ctx->buffer[57] = curlx_ultouc((ctx->lo >> 8)&0xff);
+ ctx->buffer[58] = curlx_ultouc((ctx->lo >> 16)&0xff);
+ ctx->buffer[59] = curlx_ultouc((ctx->lo >> 24)&0xff);
+ ctx->buffer[60] = curlx_ultouc((ctx->hi)&0xff);
+ ctx->buffer[61] = curlx_ultouc((ctx->hi >> 8)&0xff);
+ ctx->buffer[62] = curlx_ultouc((ctx->hi >> 16)&0xff);
+ ctx->buffer[63] = curlx_ultouc(ctx->hi >> 24);
+
+ body(ctx, ctx->buffer, 64);
+
+ result[0] = curlx_ultouc((ctx->a)&0xff);
+ result[1] = curlx_ultouc((ctx->a >> 8)&0xff);
+ result[2] = curlx_ultouc((ctx->a >> 16)&0xff);
+ result[3] = curlx_ultouc(ctx->a >> 24);
+ result[4] = curlx_ultouc((ctx->b)&0xff);
+ result[5] = curlx_ultouc((ctx->b >> 8)&0xff);
+ result[6] = curlx_ultouc((ctx->b >> 16)&0xff);
+ result[7] = curlx_ultouc(ctx->b >> 24);
+ result[8] = curlx_ultouc((ctx->c)&0xff);
+ result[9] = curlx_ultouc((ctx->c >> 8)&0xff);
+ result[10] = curlx_ultouc((ctx->c >> 16)&0xff);
+ result[11] = curlx_ultouc(ctx->c >> 24);
+ result[12] = curlx_ultouc((ctx->d)&0xff);
+ result[13] = curlx_ultouc((ctx->d >> 8)&0xff);
+ result[14] = curlx_ultouc((ctx->d >> 16)&0xff);
+ result[15] = curlx_ultouc(ctx->d >> 24);
+
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+#endif /* CRYPTO LIBS */
+
+void Curl_md4it(unsigned char *output, const unsigned char *input,
+ const size_t len)
+{
+ MD4_CTX ctx;
+
+ MD4_Init(&ctx);
+ MD4_Update(&ctx, input, curlx_uztoui(len));
+ MD4_Final(output, &ctx);
+}
+
+#endif /* CURL_DISABLE_CRYPTO_AUTH */
diff --git a/libs/libcurl/src/md5.c b/libs/libcurl/src/md5.c index 5be639929d..1a09851917 100644 --- a/libs/libcurl/src/md5.c +++ b/libs/libcurl/src/md5.c @@ -1,669 +1,652 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_CRYPTO_AUTH - -#include <curl/curl.h> - -#include "curl_md5.h" -#include "curl_hmac.h" -#include "warnless.h" - -#ifdef USE_MBEDTLS -#include <mbedtls/version.h> - -#if(MBEDTLS_VERSION_NUMBER >= 0x02070000) && \ - (MBEDTLS_VERSION_NUMBER < 0x03000000) - #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS -#endif -#endif /* USE_MBEDTLS */ - -#ifdef USE_OPENSSL - #include <openssl/opensslconf.h> - #if !defined(OPENSSL_NO_MD5) && !defined(OPENSSL_NO_DEPRECATED_3_0) - #define USE_OPENSSL_MD5 - #endif -#endif - -#ifdef USE_WOLFSSL - #include <wolfssl/options.h> - #ifndef NO_MD5 - #define USE_WOLFSSL_MD5 - #endif -#endif - -#if defined(USE_GNUTLS) - -#include <nettle/md5.h> -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -typedef struct md5_ctx my_md5_ctx; - -static CURLcode my_md5_init(my_md5_ctx *ctx) -{ - md5_init(ctx); - return CURLE_OK; -} - -static void my_md5_update(my_md5_ctx *ctx, - const unsigned char *input, - unsigned int inputLen) -{ - md5_update(ctx, inputLen, input); -} - -static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) -{ - md5_digest(ctx, 16, digest); -} - -#elif defined(USE_OPENSSL_MD5) || defined(USE_WOLFSSL_MD5) - -/* When OpenSSL or wolfSSL is available, we use their MD5 functions. */ -#if defined(USE_OPENSSL_MD5) -#include <openssl/md5.h> -#elif defined(USE_WOLFSSL_MD5) -#include <wolfssl/openssl/md5.h> -#endif - -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -typedef MD5_CTX my_md5_ctx; - -static CURLcode my_md5_init(my_md5_ctx *ctx) -{ - if(!MD5_Init(ctx)) - return CURLE_OUT_OF_MEMORY; - - return CURLE_OK; -} - -static void my_md5_update(my_md5_ctx *ctx, - const unsigned char *input, - unsigned int len) -{ - (void)MD5_Update(ctx, input, len); -} - -static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) -{ - (void)MD5_Final(digest, ctx); -} - -#elif defined(USE_MBEDTLS) - -#include <mbedtls/md5.h> - -#include "curl_memory.h" - -/* The last #include file should be: */ -#include "memdebug.h" - -typedef mbedtls_md5_context my_md5_ctx; - -static CURLcode my_md5_init(my_md5_ctx *ctx) -{ -#if (MBEDTLS_VERSION_NUMBER >= 0x03000000) - if(mbedtls_md5_starts(ctx)) - return CURLE_OUT_OF_MEMORY; -#elif defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) - if(mbedtls_md5_starts_ret(ctx)) - return CURLE_OUT_OF_MEMORY; -#else - (void)mbedtls_md5_starts(ctx); -#endif - return CURLE_OK; -} - -static void my_md5_update(my_md5_ctx *ctx, - const unsigned char *data, - unsigned int length) -{ -#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) - (void) mbedtls_md5_update(ctx, data, length); -#else - (void) mbedtls_md5_update_ret(ctx, data, length); -#endif -} - -static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) -{ -#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) - (void) mbedtls_md5_finish(ctx, digest); -#else - (void) mbedtls_md5_finish_ret(ctx, digest); -#endif -} - -#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ - (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040) && \ - defined(__MAC_OS_X_VERSION_MIN_ALLOWED) && \ - (__MAC_OS_X_VERSION_MIN_ALLOWED < 101500)) || \ - (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ - (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000)) - -/* For Apple operating systems: CommonCrypto has the functions we need. - These functions are available on Tiger and later, as well as iOS 2.0 - and later. If you're building for an older cat, well, sorry. - - Declaring the functions as static like this seems to be a bit more - reliable than defining COMMON_DIGEST_FOR_OPENSSL on older cats. */ -# include <CommonCrypto/CommonDigest.h> -# define my_md5_ctx CC_MD5_CTX -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -static CURLcode my_md5_init(my_md5_ctx *ctx) -{ - if(!CC_MD5_Init(ctx)) - return CURLE_OUT_OF_MEMORY; - - return CURLE_OK; -} - -static void my_md5_update(my_md5_ctx *ctx, - const unsigned char *input, - unsigned int inputLen) -{ - CC_MD5_Update(ctx, input, inputLen); -} - -static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) -{ - CC_MD5_Final(digest, ctx); -} - -#elif defined(USE_WIN32_CRYPTO) - -#include <wincrypt.h> -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -struct md5_ctx { - HCRYPTPROV hCryptProv; - HCRYPTHASH hHash; -}; -typedef struct md5_ctx my_md5_ctx; - -static CURLcode my_md5_init(my_md5_ctx *ctx) -{ - if(!CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) - return CURLE_OUT_OF_MEMORY; - - if(!CryptCreateHash(ctx->hCryptProv, CALG_MD5, 0, 0, &ctx->hHash)) { - CryptReleaseContext(ctx->hCryptProv, 0); - return CURLE_OUT_OF_MEMORY; - } - - return CURLE_OK; -} - -static void my_md5_update(my_md5_ctx *ctx, - const unsigned char *input, - unsigned int inputLen) -{ - CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0); -} - -static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) -{ - unsigned long length = 0; - CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0); - if(length == 16) - CryptGetHashParam(ctx->hHash, HP_HASHVAL, digest, &length, 0); - if(ctx->hHash) - CryptDestroyHash(ctx->hHash); - if(ctx->hCryptProv) - CryptReleaseContext(ctx->hCryptProv, 0); -} - -#else - -/* When no other crypto library is available we use this code segment */ - -/* - * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. - * MD5 Message-Digest Algorithm (RFC 1321). - * - * Homepage: - https://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 - * - * Author: - * Alexander Peslyak, better known as Solar Designer <solar at openwall.com> - * - * This software was written by Alexander Peslyak in 2001. No copyright is - * claimed, and the software is hereby placed in the public domain. - * In case this attempt to disclaim copyright and place the software in the - * public domain is deemed null and void, then the software is - * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the - * general public under the following terms: - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted. - * - * There's ABSOLUTELY NO WARRANTY, express or implied. - * - * (This is a heavily cut-down "BSD license".) - * - * This differs from Colin Plumb's older public domain implementation in that - * no exactly 32-bit integer data type is required (any 32-bit or wider - * unsigned integer data type will do), there's no compile-time endianness - * configuration, and the function prototypes match OpenSSL's. No code from - * Colin Plumb's implementation has been reused; this comment merely compares - * the properties of the two independent implementations. - * - * The primary goals of this implementation are portability and ease of use. - * It is meant to be fast, but not as fast as possible. Some known - * optimizations are not included to reduce source code size and avoid - * compile-time configuration. - */ - -#include <string.h> - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* Any 32-bit or wider unsigned integer data type will do */ -typedef unsigned int MD5_u32plus; - -struct md5_ctx { - MD5_u32plus lo, hi; - MD5_u32plus a, b, c, d; - unsigned char buffer[64]; - MD5_u32plus block[16]; -}; -typedef struct md5_ctx my_md5_ctx; - -static CURLcode my_md5_init(my_md5_ctx *ctx); -static void my_md5_update(my_md5_ctx *ctx, const void *data, - unsigned long size); -static void my_md5_final(unsigned char *result, my_md5_ctx *ctx); - -/* - * The basic MD5 functions. - * - * F and G are optimized compared to their RFC 1321 definitions for - * architectures that lack an AND-NOT instruction, just like in Colin Plumb's - * implementation. - */ -#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) -#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) -#define H(x, y, z) (((x) ^ (y)) ^ (z)) -#define H2(x, y, z) ((x) ^ ((y) ^ (z))) -#define I(x, y, z) ((y) ^ ((x) | ~(z))) - -/* - * The MD5 transformation for all four rounds. - */ -#define STEP(f, a, b, c, d, x, t, s) \ - (a) += f((b), (c), (d)) + (x) + (t); \ - (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ - (a) += (b); - -/* - * SET reads 4 input bytes in little-endian byte order and stores them - * in a properly aligned word in host byte order. - * - * The check for little-endian architectures that tolerate unaligned - * memory accesses is just an optimization. Nothing will break if it - * doesn't work. - */ -#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) -#define SET(n) \ - (*(MD5_u32plus *)(void *)&ptr[(n) * 4]) -#define GET(n) \ - SET(n) -#else -#define SET(n) \ - (ctx->block[(n)] = \ - (MD5_u32plus)ptr[(n) * 4] | \ - ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ - ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ - ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) -#define GET(n) \ - (ctx->block[(n)]) -#endif - -/* - * This processes one or more 64-byte data blocks, but does NOT update - * the bit counters. There are no alignment requirements. - */ -static const void *body(my_md5_ctx *ctx, const void *data, unsigned long size) -{ - const unsigned char *ptr; - MD5_u32plus a, b, c, d; - - ptr = (const unsigned char *)data; - - a = ctx->a; - b = ctx->b; - c = ctx->c; - d = ctx->d; - - do { - MD5_u32plus saved_a, saved_b, saved_c, saved_d; - - saved_a = a; - saved_b = b; - saved_c = c; - saved_d = d; - -/* Round 1 */ - STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) - STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) - STEP(F, c, d, a, b, SET(2), 0x242070db, 17) - STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) - STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) - STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) - STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) - STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) - STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) - STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) - STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) - STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) - STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) - STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) - STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) - STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) - -/* Round 2 */ - STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) - STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) - STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) - STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) - STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) - STEP(G, d, a, b, c, GET(10), 0x02441453, 9) - STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) - STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) - STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) - STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) - STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) - STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) - STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) - STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) - STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) - STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) - -/* Round 3 */ - STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) - STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) - STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) - STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) - STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) - STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) - STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) - STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) - STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) - STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) - STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) - STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) - STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) - STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) - STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) - STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) - -/* Round 4 */ - STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) - STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) - STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) - STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) - STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) - STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) - STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) - STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) - STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) - STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) - STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) - STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) - STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) - STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) - STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) - STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) - - a += saved_a; - b += saved_b; - c += saved_c; - d += saved_d; - - ptr += 64; - } while(size -= 64); - - ctx->a = a; - ctx->b = b; - ctx->c = c; - ctx->d = d; - - return ptr; -} - -static CURLcode my_md5_init(my_md5_ctx *ctx) -{ - ctx->a = 0x67452301; - ctx->b = 0xefcdab89; - ctx->c = 0x98badcfe; - ctx->d = 0x10325476; - - ctx->lo = 0; - ctx->hi = 0; - - return CURLE_OK; -} - -static void my_md5_update(my_md5_ctx *ctx, const void *data, - unsigned long size) -{ - MD5_u32plus saved_lo; - unsigned long used; - - saved_lo = ctx->lo; - ctx->lo = (saved_lo + size) & 0x1fffffff; - if(ctx->lo < saved_lo) - ctx->hi++; - ctx->hi += (MD5_u32plus)size >> 29; - - used = saved_lo & 0x3f; - - if(used) { - unsigned long available = 64 - used; - - if(size < available) { - memcpy(&ctx->buffer[used], data, size); - return; - } - - memcpy(&ctx->buffer[used], data, available); - data = (const unsigned char *)data + available; - size -= available; - body(ctx, ctx->buffer, 64); - } - - if(size >= 64) { - data = body(ctx, data, size & ~(unsigned long)0x3f); - size &= 0x3f; - } - - memcpy(ctx->buffer, data, size); -} - -static void my_md5_final(unsigned char *result, my_md5_ctx *ctx) -{ - unsigned long used, available; - - used = ctx->lo & 0x3f; - - ctx->buffer[used++] = 0x80; - - available = 64 - used; - - if(available < 8) { - memset(&ctx->buffer[used], 0, available); - body(ctx, ctx->buffer, 64); - used = 0; - available = 64; - } - - memset(&ctx->buffer[used], 0, available - 8); - - ctx->lo <<= 3; - ctx->buffer[56] = curlx_ultouc((ctx->lo)&0xff); - ctx->buffer[57] = curlx_ultouc((ctx->lo >> 8)&0xff); - ctx->buffer[58] = curlx_ultouc((ctx->lo >> 16)&0xff); - ctx->buffer[59] = curlx_ultouc(ctx->lo >> 24); - ctx->buffer[60] = curlx_ultouc((ctx->hi)&0xff); - ctx->buffer[61] = curlx_ultouc((ctx->hi >> 8)&0xff); - ctx->buffer[62] = curlx_ultouc((ctx->hi >> 16)&0xff); - ctx->buffer[63] = curlx_ultouc(ctx->hi >> 24); - - body(ctx, ctx->buffer, 64); - - result[0] = curlx_ultouc((ctx->a)&0xff); - result[1] = curlx_ultouc((ctx->a >> 8)&0xff); - result[2] = curlx_ultouc((ctx->a >> 16)&0xff); - result[3] = curlx_ultouc(ctx->a >> 24); - result[4] = curlx_ultouc((ctx->b)&0xff); - result[5] = curlx_ultouc((ctx->b >> 8)&0xff); - result[6] = curlx_ultouc((ctx->b >> 16)&0xff); - result[7] = curlx_ultouc(ctx->b >> 24); - result[8] = curlx_ultouc((ctx->c)&0xff); - result[9] = curlx_ultouc((ctx->c >> 8)&0xff); - result[10] = curlx_ultouc((ctx->c >> 16)&0xff); - result[11] = curlx_ultouc(ctx->c >> 24); - result[12] = curlx_ultouc((ctx->d)&0xff); - result[13] = curlx_ultouc((ctx->d >> 8)&0xff); - result[14] = curlx_ultouc((ctx->d >> 16)&0xff); - result[15] = curlx_ultouc(ctx->d >> 24); - - memset(ctx, 0, sizeof(*ctx)); -} - -#endif /* CRYPTO LIBS */ - -const struct HMAC_params Curl_HMAC_MD5[] = { - { - /* Hash initialization function. */ - CURLX_FUNCTION_CAST(HMAC_hinit_func, my_md5_init), - /* Hash update function. */ - CURLX_FUNCTION_CAST(HMAC_hupdate_func, my_md5_update), - /* Hash computation end function. */ - CURLX_FUNCTION_CAST(HMAC_hfinal_func, my_md5_final), - /* Size of hash context structure. */ - sizeof(my_md5_ctx), - /* Maximum key length. */ - 64, - /* Result size. */ - 16 - } -}; - -const struct MD5_params Curl_DIGEST_MD5[] = { - { - /* Digest initialization function */ - CURLX_FUNCTION_CAST(Curl_MD5_init_func, my_md5_init), - /* Digest update function */ - CURLX_FUNCTION_CAST(Curl_MD5_update_func, my_md5_update), - /* Digest computation end function */ - CURLX_FUNCTION_CAST(Curl_MD5_final_func, my_md5_final), - /* Size of digest context struct */ - sizeof(my_md5_ctx), - /* Result size */ - 16 - } -}; - -/* - * @unittest: 1601 - * Returns CURLE_OK on success. - */ -CURLcode Curl_md5it(unsigned char *outbuffer, const unsigned char *input, - const size_t len) -{ - CURLcode result; - my_md5_ctx ctx; - - result = my_md5_init(&ctx); - if(!result) { - my_md5_update(&ctx, input, curlx_uztoui(len)); - my_md5_final(outbuffer, &ctx); - } - return result; -} - -struct MD5_context *Curl_MD5_init(const struct MD5_params *md5params) -{ - struct MD5_context *ctxt; - - /* Create MD5 context */ - ctxt = malloc(sizeof(*ctxt)); - - if(!ctxt) - return ctxt; - - ctxt->md5_hashctx = malloc(md5params->md5_ctxtsize); - - if(!ctxt->md5_hashctx) { - free(ctxt); - return NULL; - } - - ctxt->md5_hash = md5params; - - if((*md5params->md5_init_func)(ctxt->md5_hashctx)) { - free(ctxt->md5_hashctx); - free(ctxt); - return NULL; - } - - return ctxt; -} - -CURLcode Curl_MD5_update(struct MD5_context *context, - const unsigned char *data, - unsigned int len) -{ - (*context->md5_hash->md5_update_func)(context->md5_hashctx, data, len); - - return CURLE_OK; -} - -CURLcode Curl_MD5_final(struct MD5_context *context, unsigned char *result) -{ - (*context->md5_hash->md5_final_func)(result, context->md5_hashctx); - - free(context->md5_hashctx); - free(context); - - return CURLE_OK; -} - -#endif /* CURL_DISABLE_CRYPTO_AUTH */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_CRYPTO_AUTH
+
+#include <string.h>
+#include <curl/curl.h>
+
+#include "curl_md5.h"
+#include "curl_hmac.h"
+#include "warnless.h"
+
+#ifdef USE_MBEDTLS
+#include <mbedtls/version.h>
+
+#if(MBEDTLS_VERSION_NUMBER >= 0x02070000) && \
+ (MBEDTLS_VERSION_NUMBER < 0x03000000)
+ #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS
+#endif
+#endif /* USE_MBEDTLS */
+
+#ifdef USE_OPENSSL
+ #include <openssl/opensslconf.h>
+ #if !defined(OPENSSL_NO_MD5) && !defined(OPENSSL_NO_DEPRECATED_3_0)
+ #define USE_OPENSSL_MD5
+ #endif
+#endif
+
+#ifdef USE_WOLFSSL
+ #include <wolfssl/options.h>
+ #ifndef NO_MD5
+ #define USE_WOLFSSL_MD5
+ #endif
+#endif
+
+#if defined(USE_GNUTLS)
+#include <nettle/md5.h>
+#elif defined(USE_OPENSSL_MD5)
+#include <openssl/md5.h>
+#elif defined(USE_WOLFSSL_MD5)
+#include <wolfssl/openssl/md5.h>
+#elif defined(USE_MBEDTLS)
+#include <mbedtls/md5.h>
+#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \
+ (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040) && \
+ defined(__MAC_OS_X_VERSION_MIN_ALLOWED) && \
+ (__MAC_OS_X_VERSION_MIN_ALLOWED < 101500)) || \
+ (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \
+ (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000))
+#define AN_APPLE_OS
+#include <CommonCrypto/CommonDigest.h>
+#elif defined(USE_WIN32_CRYPTO)
+#include <wincrypt.h>
+#endif
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#if defined(USE_GNUTLS)
+
+typedef struct md5_ctx my_md5_ctx;
+
+static CURLcode my_md5_init(my_md5_ctx *ctx)
+{
+ md5_init(ctx);
+ return CURLE_OK;
+}
+
+static void my_md5_update(my_md5_ctx *ctx,
+ const unsigned char *input,
+ unsigned int inputLen)
+{
+ md5_update(ctx, inputLen, input);
+}
+
+static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx)
+{
+ md5_digest(ctx, 16, digest);
+}
+
+#elif defined(USE_OPENSSL_MD5) || defined(USE_WOLFSSL_MD5)
+
+typedef MD5_CTX my_md5_ctx;
+
+static CURLcode my_md5_init(my_md5_ctx *ctx)
+{
+ if(!MD5_Init(ctx))
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_OK;
+}
+
+static void my_md5_update(my_md5_ctx *ctx,
+ const unsigned char *input,
+ unsigned int len)
+{
+ (void)MD5_Update(ctx, input, len);
+}
+
+static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx)
+{
+ (void)MD5_Final(digest, ctx);
+}
+
+#elif defined(USE_MBEDTLS)
+
+typedef mbedtls_md5_context my_md5_ctx;
+
+static CURLcode my_md5_init(my_md5_ctx *ctx)
+{
+#if (MBEDTLS_VERSION_NUMBER >= 0x03000000)
+ if(mbedtls_md5_starts(ctx))
+ return CURLE_OUT_OF_MEMORY;
+#elif defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
+ if(mbedtls_md5_starts_ret(ctx))
+ return CURLE_OUT_OF_MEMORY;
+#else
+ (void)mbedtls_md5_starts(ctx);
+#endif
+ return CURLE_OK;
+}
+
+static void my_md5_update(my_md5_ctx *ctx,
+ const unsigned char *data,
+ unsigned int length)
+{
+#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
+ (void) mbedtls_md5_update(ctx, data, length);
+#else
+ (void) mbedtls_md5_update_ret(ctx, data, length);
+#endif
+}
+
+static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx)
+{
+#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
+ (void) mbedtls_md5_finish(ctx, digest);
+#else
+ (void) mbedtls_md5_finish_ret(ctx, digest);
+#endif
+}
+
+#elif defined(AN_APPLE_OS)
+
+/* For Apple operating systems: CommonCrypto has the functions we need.
+ These functions are available on Tiger and later, as well as iOS 2.0
+ and later. If you're building for an older cat, well, sorry.
+
+ Declaring the functions as static like this seems to be a bit more
+ reliable than defining COMMON_DIGEST_FOR_OPENSSL on older cats. */
+# define my_md5_ctx CC_MD5_CTX
+
+static CURLcode my_md5_init(my_md5_ctx *ctx)
+{
+ if(!CC_MD5_Init(ctx))
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_OK;
+}
+
+static void my_md5_update(my_md5_ctx *ctx,
+ const unsigned char *input,
+ unsigned int inputLen)
+{
+ CC_MD5_Update(ctx, input, inputLen);
+}
+
+static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx)
+{
+ CC_MD5_Final(digest, ctx);
+}
+
+#elif defined(USE_WIN32_CRYPTO)
+
+struct md5_ctx {
+ HCRYPTPROV hCryptProv;
+ HCRYPTHASH hHash;
+};
+typedef struct md5_ctx my_md5_ctx;
+
+static CURLcode my_md5_init(my_md5_ctx *ctx)
+{
+ if(!CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
+ return CURLE_OUT_OF_MEMORY;
+
+ if(!CryptCreateHash(ctx->hCryptProv, CALG_MD5, 0, 0, &ctx->hHash)) {
+ CryptReleaseContext(ctx->hCryptProv, 0);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ return CURLE_OK;
+}
+
+static void my_md5_update(my_md5_ctx *ctx,
+ const unsigned char *input,
+ unsigned int inputLen)
+{
+ CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0);
+}
+
+static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx)
+{
+ unsigned long length = 0;
+ CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0);
+ if(length == 16)
+ CryptGetHashParam(ctx->hHash, HP_HASHVAL, digest, &length, 0);
+ if(ctx->hHash)
+ CryptDestroyHash(ctx->hHash);
+ if(ctx->hCryptProv)
+ CryptReleaseContext(ctx->hCryptProv, 0);
+}
+
+#else
+
+/* When no other crypto library is available we use this code segment */
+
+/*
+ * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
+ * MD5 Message-Digest Algorithm (RFC 1321).
+ *
+ * Homepage:
+ https://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
+ *
+ * Author:
+ * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
+ *
+ * This software was written by Alexander Peslyak in 2001. No copyright is
+ * claimed, and the software is hereby placed in the public domain.
+ * In case this attempt to disclaim copyright and place the software in the
+ * public domain is deemed null and void, then the software is
+ * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * (This is a heavily cut-down "BSD license".)
+ *
+ * This differs from Colin Plumb's older public domain implementation in that
+ * no exactly 32-bit integer data type is required (any 32-bit or wider
+ * unsigned integer data type will do), there's no compile-time endianness
+ * configuration, and the function prototypes match OpenSSL's. No code from
+ * Colin Plumb's implementation has been reused; this comment merely compares
+ * the properties of the two independent implementations.
+ *
+ * The primary goals of this implementation are portability and ease of use.
+ * It is meant to be fast, but not as fast as possible. Some known
+ * optimizations are not included to reduce source code size and avoid
+ * compile-time configuration.
+ */
+
+/* Any 32-bit or wider unsigned integer data type will do */
+typedef unsigned int MD5_u32plus;
+
+struct md5_ctx {
+ MD5_u32plus lo, hi;
+ MD5_u32plus a, b, c, d;
+ unsigned char buffer[64];
+ MD5_u32plus block[16];
+};
+typedef struct md5_ctx my_md5_ctx;
+
+static CURLcode my_md5_init(my_md5_ctx *ctx);
+static void my_md5_update(my_md5_ctx *ctx, const void *data,
+ unsigned long size);
+static void my_md5_final(unsigned char *result, my_md5_ctx *ctx);
+
+/*
+ * The basic MD5 functions.
+ *
+ * F and G are optimized compared to their RFC 1321 definitions for
+ * architectures that lack an AND-NOT instruction, just like in Colin Plumb's
+ * implementation.
+ */
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
+#define H(x, y, z) (((x) ^ (y)) ^ (z))
+#define H2(x, y, z) ((x) ^ ((y) ^ (z)))
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+
+/*
+ * The MD5 transformation for all four rounds.
+ */
+#define STEP(f, a, b, c, d, x, t, s) \
+ (a) += f((b), (c), (d)) + (x) + (t); \
+ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
+ (a) += (b);
+
+/*
+ * SET reads 4 input bytes in little-endian byte order and stores them
+ * in a properly aligned word in host byte order.
+ *
+ * The check for little-endian architectures that tolerate unaligned
+ * memory accesses is just an optimization. Nothing will break if it
+ * doesn't work.
+ */
+#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
+#define SET(n) \
+ (*(MD5_u32plus *)(void *)&ptr[(n) * 4])
+#define GET(n) \
+ SET(n)
+#else
+#define SET(n) \
+ (ctx->block[(n)] = \
+ (MD5_u32plus)ptr[(n) * 4] | \
+ ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
+ ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
+ ((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
+#define GET(n) \
+ (ctx->block[(n)])
+#endif
+
+/*
+ * This processes one or more 64-byte data blocks, but does NOT update
+ * the bit counters. There are no alignment requirements.
+ */
+static const void *body(my_md5_ctx *ctx, const void *data, unsigned long size)
+{
+ const unsigned char *ptr;
+ MD5_u32plus a, b, c, d;
+
+ ptr = (const unsigned char *)data;
+
+ a = ctx->a;
+ b = ctx->b;
+ c = ctx->c;
+ d = ctx->d;
+
+ do {
+ MD5_u32plus saved_a, saved_b, saved_c, saved_d;
+
+ saved_a = a;
+ saved_b = b;
+ saved_c = c;
+ saved_d = d;
+
+/* Round 1 */
+ STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
+ STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
+ STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
+ STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
+ STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
+ STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
+ STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
+ STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
+ STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
+ STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
+ STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
+ STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
+ STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
+ STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
+ STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
+ STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
+
+/* Round 2 */
+ STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
+ STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
+ STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
+ STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
+ STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
+ STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
+ STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
+ STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
+ STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
+ STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
+ STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
+ STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
+ STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
+ STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
+ STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
+ STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
+
+/* Round 3 */
+ STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
+ STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11)
+ STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
+ STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23)
+ STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
+ STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11)
+ STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
+ STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23)
+ STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
+ STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11)
+ STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
+ STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23)
+ STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
+ STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11)
+ STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
+ STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23)
+
+/* Round 4 */
+ STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
+ STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
+ STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
+ STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
+ STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
+ STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
+ STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
+ STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
+ STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
+ STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
+ STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
+ STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
+ STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
+ STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
+ STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
+ STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
+
+ a += saved_a;
+ b += saved_b;
+ c += saved_c;
+ d += saved_d;
+
+ ptr += 64;
+ } while(size -= 64);
+
+ ctx->a = a;
+ ctx->b = b;
+ ctx->c = c;
+ ctx->d = d;
+
+ return ptr;
+}
+
+static CURLcode my_md5_init(my_md5_ctx *ctx)
+{
+ ctx->a = 0x67452301;
+ ctx->b = 0xefcdab89;
+ ctx->c = 0x98badcfe;
+ ctx->d = 0x10325476;
+
+ ctx->lo = 0;
+ ctx->hi = 0;
+
+ return CURLE_OK;
+}
+
+static void my_md5_update(my_md5_ctx *ctx, const void *data,
+ unsigned long size)
+{
+ MD5_u32plus saved_lo;
+ unsigned long used;
+
+ saved_lo = ctx->lo;
+ ctx->lo = (saved_lo + size) & 0x1fffffff;
+ if(ctx->lo < saved_lo)
+ ctx->hi++;
+ ctx->hi += (MD5_u32plus)size >> 29;
+
+ used = saved_lo & 0x3f;
+
+ if(used) {
+ unsigned long available = 64 - used;
+
+ if(size < available) {
+ memcpy(&ctx->buffer[used], data, size);
+ return;
+ }
+
+ memcpy(&ctx->buffer[used], data, available);
+ data = (const unsigned char *)data + available;
+ size -= available;
+ body(ctx, ctx->buffer, 64);
+ }
+
+ if(size >= 64) {
+ data = body(ctx, data, size & ~(unsigned long)0x3f);
+ size &= 0x3f;
+ }
+
+ memcpy(ctx->buffer, data, size);
+}
+
+static void my_md5_final(unsigned char *result, my_md5_ctx *ctx)
+{
+ unsigned long used, available;
+
+ used = ctx->lo & 0x3f;
+
+ ctx->buffer[used++] = 0x80;
+
+ available = 64 - used;
+
+ if(available < 8) {
+ memset(&ctx->buffer[used], 0, available);
+ body(ctx, ctx->buffer, 64);
+ used = 0;
+ available = 64;
+ }
+
+ memset(&ctx->buffer[used], 0, available - 8);
+
+ ctx->lo <<= 3;
+ ctx->buffer[56] = curlx_ultouc((ctx->lo)&0xff);
+ ctx->buffer[57] = curlx_ultouc((ctx->lo >> 8)&0xff);
+ ctx->buffer[58] = curlx_ultouc((ctx->lo >> 16)&0xff);
+ ctx->buffer[59] = curlx_ultouc(ctx->lo >> 24);
+ ctx->buffer[60] = curlx_ultouc((ctx->hi)&0xff);
+ ctx->buffer[61] = curlx_ultouc((ctx->hi >> 8)&0xff);
+ ctx->buffer[62] = curlx_ultouc((ctx->hi >> 16)&0xff);
+ ctx->buffer[63] = curlx_ultouc(ctx->hi >> 24);
+
+ body(ctx, ctx->buffer, 64);
+
+ result[0] = curlx_ultouc((ctx->a)&0xff);
+ result[1] = curlx_ultouc((ctx->a >> 8)&0xff);
+ result[2] = curlx_ultouc((ctx->a >> 16)&0xff);
+ result[3] = curlx_ultouc(ctx->a >> 24);
+ result[4] = curlx_ultouc((ctx->b)&0xff);
+ result[5] = curlx_ultouc((ctx->b >> 8)&0xff);
+ result[6] = curlx_ultouc((ctx->b >> 16)&0xff);
+ result[7] = curlx_ultouc(ctx->b >> 24);
+ result[8] = curlx_ultouc((ctx->c)&0xff);
+ result[9] = curlx_ultouc((ctx->c >> 8)&0xff);
+ result[10] = curlx_ultouc((ctx->c >> 16)&0xff);
+ result[11] = curlx_ultouc(ctx->c >> 24);
+ result[12] = curlx_ultouc((ctx->d)&0xff);
+ result[13] = curlx_ultouc((ctx->d >> 8)&0xff);
+ result[14] = curlx_ultouc((ctx->d >> 16)&0xff);
+ result[15] = curlx_ultouc(ctx->d >> 24);
+
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+#endif /* CRYPTO LIBS */
+
+const struct HMAC_params Curl_HMAC_MD5[] = {
+ {
+ /* Hash initialization function. */
+ CURLX_FUNCTION_CAST(HMAC_hinit_func, my_md5_init),
+ /* Hash update function. */
+ CURLX_FUNCTION_CAST(HMAC_hupdate_func, my_md5_update),
+ /* Hash computation end function. */
+ CURLX_FUNCTION_CAST(HMAC_hfinal_func, my_md5_final),
+ /* Size of hash context structure. */
+ sizeof(my_md5_ctx),
+ /* Maximum key length. */
+ 64,
+ /* Result size. */
+ 16
+ }
+};
+
+const struct MD5_params Curl_DIGEST_MD5[] = {
+ {
+ /* Digest initialization function */
+ CURLX_FUNCTION_CAST(Curl_MD5_init_func, my_md5_init),
+ /* Digest update function */
+ CURLX_FUNCTION_CAST(Curl_MD5_update_func, my_md5_update),
+ /* Digest computation end function */
+ CURLX_FUNCTION_CAST(Curl_MD5_final_func, my_md5_final),
+ /* Size of digest context struct */
+ sizeof(my_md5_ctx),
+ /* Result size */
+ 16
+ }
+};
+
+/*
+ * @unittest: 1601
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_md5it(unsigned char *outbuffer, const unsigned char *input,
+ const size_t len)
+{
+ CURLcode result;
+ my_md5_ctx ctx;
+
+ result = my_md5_init(&ctx);
+ if(!result) {
+ my_md5_update(&ctx, input, curlx_uztoui(len));
+ my_md5_final(outbuffer, &ctx);
+ }
+ return result;
+}
+
+struct MD5_context *Curl_MD5_init(const struct MD5_params *md5params)
+{
+ struct MD5_context *ctxt;
+
+ /* Create MD5 context */
+ ctxt = malloc(sizeof(*ctxt));
+
+ if(!ctxt)
+ return ctxt;
+
+ ctxt->md5_hashctx = malloc(md5params->md5_ctxtsize);
+
+ if(!ctxt->md5_hashctx) {
+ free(ctxt);
+ return NULL;
+ }
+
+ ctxt->md5_hash = md5params;
+
+ if((*md5params->md5_init_func)(ctxt->md5_hashctx)) {
+ free(ctxt->md5_hashctx);
+ free(ctxt);
+ return NULL;
+ }
+
+ return ctxt;
+}
+
+CURLcode Curl_MD5_update(struct MD5_context *context,
+ const unsigned char *data,
+ unsigned int len)
+{
+ (*context->md5_hash->md5_update_func)(context->md5_hashctx, data, len);
+
+ return CURLE_OK;
+}
+
+CURLcode Curl_MD5_final(struct MD5_context *context, unsigned char *result)
+{
+ (*context->md5_hash->md5_final_func)(result, context->md5_hashctx);
+
+ free(context->md5_hashctx);
+ free(context);
+
+ return CURLE_OK;
+}
+
+#endif /* CURL_DISABLE_CRYPTO_AUTH */
diff --git a/libs/libcurl/src/memdebug.c b/libs/libcurl/src/memdebug.c index 15fb491559..4c7ed91079 100644 --- a/libs/libcurl/src/memdebug.c +++ b/libs/libcurl/src/memdebug.c @@ -1,482 +1,482 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 CURLDEBUG - -#include <curl/curl.h> - -#include "urldata.h" - -#define MEMDEBUG_NODEFINES /* don't redefine the standard functions */ - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -struct memdebug { - size_t size; - union { - curl_off_t o; - double d; - void *p; - } mem[1]; - /* I'm hoping this is the thing with the strictest alignment - * requirements. That also means we waste some space :-( */ -}; - -/* - * Note that these debug functions are very simple and they are meant to - * remain so. For advanced analysis, record a log file and write perl scripts - * to analyze them! - * - * Don't use these with multithreaded test programs! - */ - -FILE *curl_dbg_logfile = NULL; -static bool registered_cleanup = FALSE; /* atexit registered cleanup */ -static bool memlimit = FALSE; /* enable memory limit */ -static long memsize = 0; /* set number of mallocs allowed */ - -/* LeakSantizier (LSAN) calls _exit() instead of exit() when a leak is detected - on exit so the logfile must be closed explicitly or data could be lost. - Though _exit() does not call atexit handlers such as this, LSAN's call to - _exit() comes after the atexit handlers are called. curl/curl#6620 */ -static void curl_dbg_cleanup(void) -{ - if(curl_dbg_logfile && - curl_dbg_logfile != stderr && - curl_dbg_logfile != stdout) { - fclose(curl_dbg_logfile); - } - curl_dbg_logfile = NULL; -} - -/* this sets the log file name */ -void curl_dbg_memdebug(const char *logname) -{ - if(!curl_dbg_logfile) { - if(logname && *logname) - curl_dbg_logfile = fopen(logname, FOPEN_WRITETEXT); - else - curl_dbg_logfile = stderr; -#ifdef MEMDEBUG_LOG_SYNC - /* Flush the log file after every line so the log isn't lost in a crash */ - if(curl_dbg_logfile) - setbuf(curl_dbg_logfile, (char *)NULL); -#endif - } - if(!registered_cleanup) - registered_cleanup = !atexit(curl_dbg_cleanup); -} - -/* This function sets the number of malloc() calls that should return - successfully! */ -void curl_dbg_memlimit(long limit) -{ - if(!memlimit) { - memlimit = TRUE; - memsize = limit; - } -} - -/* returns TRUE if this isn't allowed! */ -static bool countcheck(const char *func, int line, const char *source) -{ - /* if source is NULL, then the call is made internally and this check - should not be made */ - if(memlimit && source) { - if(!memsize) { - /* log to file */ - curl_dbg_log("LIMIT %s:%d %s reached memlimit\n", - source, line, func); - /* log to stderr also */ - fprintf(stderr, "LIMIT %s:%d %s reached memlimit\n", - source, line, func); - fflush(curl_dbg_logfile); /* because it might crash now */ - errno = ENOMEM; - return TRUE; /* RETURN ERROR! */ - } - else - memsize--; /* countdown */ - - - } - - return FALSE; /* allow this */ -} - -ALLOC_FUNC void *curl_dbg_malloc(size_t wantedsize, - int line, const char *source) -{ - struct memdebug *mem; - size_t size; - - DEBUGASSERT(wantedsize != 0); - - if(countcheck("malloc", line, source)) - return NULL; - - /* alloc at least 64 bytes */ - size = sizeof(struct memdebug) + wantedsize; - - mem = (Curl_cmalloc)(size); - if(mem) { - mem->size = wantedsize; - } - - if(source) - curl_dbg_log("MEM %s:%d malloc(%zu) = %p\n", - source, line, wantedsize, - mem ? (void *)mem->mem : (void *)0); - - return (mem ? mem->mem : NULL); -} - -ALLOC_FUNC void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size, - int line, const char *source) -{ - struct memdebug *mem; - size_t size, user_size; - - DEBUGASSERT(wanted_elements != 0); - DEBUGASSERT(wanted_size != 0); - - if(countcheck("calloc", line, source)) - return NULL; - - /* alloc at least 64 bytes */ - user_size = wanted_size * wanted_elements; - size = sizeof(struct memdebug) + user_size; - - mem = (Curl_ccalloc)(1, size); - if(mem) - mem->size = user_size; - - if(source) - curl_dbg_log("MEM %s:%d calloc(%zu,%zu) = %p\n", - source, line, wanted_elements, wanted_size, - mem ? (void *)mem->mem : (void *)0); - - return (mem ? mem->mem : NULL); -} - -ALLOC_FUNC char *curl_dbg_strdup(const char *str, - int line, const char *source) -{ - char *mem; - size_t len; - - DEBUGASSERT(str != NULL); - - if(countcheck("strdup", line, source)) - return NULL; - - len = strlen(str) + 1; - - mem = curl_dbg_malloc(len, 0, NULL); /* NULL prevents logging */ - if(mem) - memcpy(mem, str, len); - - if(source) - curl_dbg_log("MEM %s:%d strdup(%p) (%zu) = %p\n", - source, line, (const void *)str, len, (const void *)mem); - - return mem; -} - -#if defined(WIN32) && defined(UNICODE) -ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str, - int line, const char *source) -{ - wchar_t *mem; - size_t wsiz, bsiz; - - DEBUGASSERT(str != NULL); - - if(countcheck("wcsdup", line, source)) - return NULL; - - wsiz = wcslen(str) + 1; - bsiz = wsiz * sizeof(wchar_t); - - mem = curl_dbg_malloc(bsiz, 0, NULL); /* NULL prevents logging */ - if(mem) - memcpy(mem, str, bsiz); - - if(source) - curl_dbg_log("MEM %s:%d wcsdup(%p) (%zu) = %p\n", - source, line, (void *)str, bsiz, (void *)mem); - - return mem; -} -#endif - -/* We provide a realloc() that accepts a NULL as pointer, which then - performs a malloc(). In order to work with ares. */ -void *curl_dbg_realloc(void *ptr, size_t wantedsize, - int line, const char *source) -{ - struct memdebug *mem = NULL; - - size_t size = sizeof(struct memdebug) + wantedsize; - - DEBUGASSERT(wantedsize != 0); - - if(countcheck("realloc", line, source)) - return NULL; - -#ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:1684) - /* 1684: conversion from pointer to same-sized integral type */ -#endif - - if(ptr) - mem = (void *)((char *)ptr - offsetof(struct memdebug, mem)); - -#ifdef __INTEL_COMPILER -# pragma warning(pop) -#endif - - mem = (Curl_crealloc)(mem, size); - if(source) - curl_dbg_log("MEM %s:%d realloc(%p, %zu) = %p\n", - source, line, (void *)ptr, wantedsize, - mem ? (void *)mem->mem : (void *)0); - - if(mem) { - mem->size = wantedsize; - return mem->mem; - } - - return NULL; -} - -void curl_dbg_free(void *ptr, int line, const char *source) -{ - if(ptr) { - struct memdebug *mem; - -#ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:1684) - /* 1684: conversion from pointer to same-sized integral type */ -#endif - - mem = (void *)((char *)ptr - offsetof(struct memdebug, mem)); - -#ifdef __INTEL_COMPILER -# pragma warning(pop) -#endif - - /* free for real */ - (Curl_cfree)(mem); - } - - if(source && ptr) - curl_dbg_log("MEM %s:%d free(%p)\n", source, line, (void *)ptr); -} - -curl_socket_t curl_dbg_socket(int domain, int type, int protocol, - int line, const char *source) -{ - const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ? - "FD %s:%d socket() = %d\n" : - (sizeof(curl_socket_t) == sizeof(long)) ? - "FD %s:%d socket() = %ld\n" : - "FD %s:%d socket() = %zd\n"; - - curl_socket_t sockfd; - - if(countcheck("socket", line, source)) - return CURL_SOCKET_BAD; - - sockfd = socket(domain, type, protocol); - - if(source && (sockfd != CURL_SOCKET_BAD)) - curl_dbg_log(fmt, source, line, sockfd); - - return sockfd; -} - -SEND_TYPE_RETV curl_dbg_send(SEND_TYPE_ARG1 sockfd, - SEND_QUAL_ARG2 SEND_TYPE_ARG2 buf, - SEND_TYPE_ARG3 len, SEND_TYPE_ARG4 flags, int line, - const char *source) -{ - SEND_TYPE_RETV rc; - if(countcheck("send", line, source)) - return -1; - rc = send(sockfd, buf, len, flags); - if(source) - curl_dbg_log("SEND %s:%d send(%lu) = %ld\n", - source, line, (unsigned long)len, (long)rc); - return rc; -} - -RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd, RECV_TYPE_ARG2 buf, - RECV_TYPE_ARG3 len, RECV_TYPE_ARG4 flags, int line, - const char *source) -{ - RECV_TYPE_RETV rc; - if(countcheck("recv", line, source)) - return -1; - rc = recv(sockfd, buf, len, flags); - if(source) - curl_dbg_log("RECV %s:%d recv(%lu) = %ld\n", - source, line, (unsigned long)len, (long)rc); - return rc; -} - -#ifdef HAVE_SOCKETPAIR -int curl_dbg_socketpair(int domain, int type, int protocol, - curl_socket_t socket_vector[2], - int line, const char *source) -{ - const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ? - "FD %s:%d socketpair() = %d %d\n" : - (sizeof(curl_socket_t) == sizeof(long)) ? - "FD %s:%d socketpair() = %ld %ld\n" : - "FD %s:%d socketpair() = %zd %zd\n"; - - int res = socketpair(domain, type, protocol, socket_vector); - - if(source && (0 == res)) - curl_dbg_log(fmt, source, line, socket_vector[0], socket_vector[1]); - - return res; -} -#endif - -curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen, - int line, const char *source) -{ - const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ? - "FD %s:%d accept() = %d\n" : - (sizeof(curl_socket_t) == sizeof(long)) ? - "FD %s:%d accept() = %ld\n" : - "FD %s:%d accept() = %zd\n"; - - struct sockaddr *addr = (struct sockaddr *)saddr; - curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen; - - curl_socket_t sockfd = accept(s, addr, addrlen); - - if(source && (sockfd != CURL_SOCKET_BAD)) - curl_dbg_log(fmt, source, line, sockfd); - - return sockfd; -} - -/* separate function to allow libcurl to mark a "faked" close */ -void curl_dbg_mark_sclose(curl_socket_t sockfd, int line, const char *source) -{ - const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ? - "FD %s:%d sclose(%d)\n": - (sizeof(curl_socket_t) == sizeof(long)) ? - "FD %s:%d sclose(%ld)\n": - "FD %s:%d sclose(%zd)\n"; - - if(source) - curl_dbg_log(fmt, source, line, sockfd); -} - -/* this is our own defined way to close sockets on *ALL* platforms */ -int curl_dbg_sclose(curl_socket_t sockfd, int line, const char *source) -{ - int res = sclose(sockfd); - curl_dbg_mark_sclose(sockfd, line, source); - return res; -} - -ALLOC_FUNC FILE *curl_dbg_fopen(const char *file, const char *mode, - int line, const char *source) -{ - FILE *res = fopen(file, mode); - - if(source) - curl_dbg_log("FILE %s:%d fopen(\"%s\",\"%s\") = %p\n", - source, line, file, mode, (void *)res); - - return res; -} - -ALLOC_FUNC FILE *curl_dbg_fdopen(int filedes, const char *mode, - int line, const char *source) -{ - FILE *res = fdopen(filedes, mode); - if(source) - curl_dbg_log("FILE %s:%d fdopen(\"%d\",\"%s\") = %p\n", - source, line, filedes, mode, (void *)res); - return res; -} - -int curl_dbg_fclose(FILE *file, int line, const char *source) -{ - int res; - - DEBUGASSERT(file != NULL); - - if(source) - curl_dbg_log("FILE %s:%d fclose(%p)\n", - source, line, (void *)file); - - res = fclose(file); - - return res; -} - -#define LOGLINE_BUFSIZE 1024 - -/* this does the writing to the memory tracking log file */ -void curl_dbg_log(const char *format, ...) -{ - char *buf; - int nchars; - va_list ap; - - if(!curl_dbg_logfile) - return; - - buf = (Curl_cmalloc)(LOGLINE_BUFSIZE); - if(!buf) - return; - - va_start(ap, format); - nchars = mvsnprintf(buf, LOGLINE_BUFSIZE, format, ap); - va_end(ap); - - if(nchars > LOGLINE_BUFSIZE - 1) - nchars = LOGLINE_BUFSIZE - 1; - - if(nchars > 0) - fwrite(buf, 1, (size_t)nchars, curl_dbg_logfile); - - (Curl_cfree)(buf); -} - -#endif /* CURLDEBUG */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 CURLDEBUG
+
+#include <curl/curl.h>
+
+#include "urldata.h"
+
+#define MEMDEBUG_NODEFINES /* don't redefine the standard functions */
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+struct memdebug {
+ size_t size;
+ union {
+ curl_off_t o;
+ double d;
+ void *p;
+ } mem[1];
+ /* I'm hoping this is the thing with the strictest alignment
+ * requirements. That also means we waste some space :-( */
+};
+
+/*
+ * Note that these debug functions are very simple and they are meant to
+ * remain so. For advanced analysis, record a log file and write perl scripts
+ * to analyze them!
+ *
+ * Don't use these with multithreaded test programs!
+ */
+
+FILE *curl_dbg_logfile = NULL;
+static bool registered_cleanup = FALSE; /* atexit registered cleanup */
+static bool memlimit = FALSE; /* enable memory limit */
+static long memsize = 0; /* set number of mallocs allowed */
+
+/* LeakSantizier (LSAN) calls _exit() instead of exit() when a leak is detected
+ on exit so the logfile must be closed explicitly or data could be lost.
+ Though _exit() does not call atexit handlers such as this, LSAN's call to
+ _exit() comes after the atexit handlers are called. curl/curl#6620 */
+static void curl_dbg_cleanup(void)
+{
+ if(curl_dbg_logfile &&
+ curl_dbg_logfile != stderr &&
+ curl_dbg_logfile != stdout) {
+ fclose(curl_dbg_logfile);
+ }
+ curl_dbg_logfile = NULL;
+}
+
+/* this sets the log file name */
+void curl_dbg_memdebug(const char *logname)
+{
+ if(!curl_dbg_logfile) {
+ if(logname && *logname)
+ curl_dbg_logfile = fopen(logname, FOPEN_WRITETEXT);
+ else
+ curl_dbg_logfile = stderr;
+#ifdef MEMDEBUG_LOG_SYNC
+ /* Flush the log file after every line so the log isn't lost in a crash */
+ if(curl_dbg_logfile)
+ setbuf(curl_dbg_logfile, (char *)NULL);
+#endif
+ }
+ if(!registered_cleanup)
+ registered_cleanup = !atexit(curl_dbg_cleanup);
+}
+
+/* This function sets the number of malloc() calls that should return
+ successfully! */
+void curl_dbg_memlimit(long limit)
+{
+ if(!memlimit) {
+ memlimit = TRUE;
+ memsize = limit;
+ }
+}
+
+/* returns TRUE if this isn't allowed! */
+static bool countcheck(const char *func, int line, const char *source)
+{
+ /* if source is NULL, then the call is made internally and this check
+ should not be made */
+ if(memlimit && source) {
+ if(!memsize) {
+ /* log to file */
+ curl_dbg_log("LIMIT %s:%d %s reached memlimit\n",
+ source, line, func);
+ /* log to stderr also */
+ fprintf(stderr, "LIMIT %s:%d %s reached memlimit\n",
+ source, line, func);
+ fflush(curl_dbg_logfile); /* because it might crash now */
+ errno = ENOMEM;
+ return TRUE; /* RETURN ERROR! */
+ }
+ else
+ memsize--; /* countdown */
+
+
+ }
+
+ return FALSE; /* allow this */
+}
+
+ALLOC_FUNC void *curl_dbg_malloc(size_t wantedsize,
+ int line, const char *source)
+{
+ struct memdebug *mem;
+ size_t size;
+
+ DEBUGASSERT(wantedsize != 0);
+
+ if(countcheck("malloc", line, source))
+ return NULL;
+
+ /* alloc at least 64 bytes */
+ size = sizeof(struct memdebug) + wantedsize;
+
+ mem = (Curl_cmalloc)(size);
+ if(mem) {
+ mem->size = wantedsize;
+ }
+
+ if(source)
+ curl_dbg_log("MEM %s:%d malloc(%zu) = %p\n",
+ source, line, wantedsize,
+ mem ? (void *)mem->mem : (void *)0);
+
+ return (mem ? mem->mem : NULL);
+}
+
+ALLOC_FUNC void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size,
+ int line, const char *source)
+{
+ struct memdebug *mem;
+ size_t size, user_size;
+
+ DEBUGASSERT(wanted_elements != 0);
+ DEBUGASSERT(wanted_size != 0);
+
+ if(countcheck("calloc", line, source))
+ return NULL;
+
+ /* alloc at least 64 bytes */
+ user_size = wanted_size * wanted_elements;
+ size = sizeof(struct memdebug) + user_size;
+
+ mem = (Curl_ccalloc)(1, size);
+ if(mem)
+ mem->size = user_size;
+
+ if(source)
+ curl_dbg_log("MEM %s:%d calloc(%zu,%zu) = %p\n",
+ source, line, wanted_elements, wanted_size,
+ mem ? (void *)mem->mem : (void *)0);
+
+ return (mem ? mem->mem : NULL);
+}
+
+ALLOC_FUNC char *curl_dbg_strdup(const char *str,
+ int line, const char *source)
+{
+ char *mem;
+ size_t len;
+
+ DEBUGASSERT(str != NULL);
+
+ if(countcheck("strdup", line, source))
+ return NULL;
+
+ len = strlen(str) + 1;
+
+ mem = curl_dbg_malloc(len, 0, NULL); /* NULL prevents logging */
+ if(mem)
+ memcpy(mem, str, len);
+
+ if(source)
+ curl_dbg_log("MEM %s:%d strdup(%p) (%zu) = %p\n",
+ source, line, (const void *)str, len, (const void *)mem);
+
+ return mem;
+}
+
+#if defined(WIN32) && defined(UNICODE)
+ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str,
+ int line, const char *source)
+{
+ wchar_t *mem;
+ size_t wsiz, bsiz;
+
+ DEBUGASSERT(str != NULL);
+
+ if(countcheck("wcsdup", line, source))
+ return NULL;
+
+ wsiz = wcslen(str) + 1;
+ bsiz = wsiz * sizeof(wchar_t);
+
+ mem = curl_dbg_malloc(bsiz, 0, NULL); /* NULL prevents logging */
+ if(mem)
+ memcpy(mem, str, bsiz);
+
+ if(source)
+ curl_dbg_log("MEM %s:%d wcsdup(%p) (%zu) = %p\n",
+ source, line, (void *)str, bsiz, (void *)mem);
+
+ return mem;
+}
+#endif
+
+/* We provide a realloc() that accepts a NULL as pointer, which then
+ performs a malloc(). In order to work with ares. */
+void *curl_dbg_realloc(void *ptr, size_t wantedsize,
+ int line, const char *source)
+{
+ struct memdebug *mem = NULL;
+
+ size_t size = sizeof(struct memdebug) + wantedsize;
+
+ DEBUGASSERT(wantedsize != 0);
+
+ if(countcheck("realloc", line, source))
+ return NULL;
+
+#ifdef __INTEL_COMPILER
+# pragma warning(push)
+# pragma warning(disable:1684)
+ /* 1684: conversion from pointer to same-sized integral type */
+#endif
+
+ if(ptr)
+ mem = (void *)((char *)ptr - offsetof(struct memdebug, mem));
+
+#ifdef __INTEL_COMPILER
+# pragma warning(pop)
+#endif
+
+ mem = (Curl_crealloc)(mem, size);
+ if(source)
+ curl_dbg_log("MEM %s:%d realloc(%p, %zu) = %p\n",
+ source, line, (void *)ptr, wantedsize,
+ mem ? (void *)mem->mem : (void *)0);
+
+ if(mem) {
+ mem->size = wantedsize;
+ return mem->mem;
+ }
+
+ return NULL;
+}
+
+void curl_dbg_free(void *ptr, int line, const char *source)
+{
+ if(ptr) {
+ struct memdebug *mem;
+
+#ifdef __INTEL_COMPILER
+# pragma warning(push)
+# pragma warning(disable:1684)
+ /* 1684: conversion from pointer to same-sized integral type */
+#endif
+
+ mem = (void *)((char *)ptr - offsetof(struct memdebug, mem));
+
+#ifdef __INTEL_COMPILER
+# pragma warning(pop)
+#endif
+
+ /* free for real */
+ (Curl_cfree)(mem);
+ }
+
+ if(source && ptr)
+ curl_dbg_log("MEM %s:%d free(%p)\n", source, line, (void *)ptr);
+}
+
+curl_socket_t curl_dbg_socket(int domain, int type, int protocol,
+ int line, const char *source)
+{
+ const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
+ "FD %s:%d socket() = %d\n" :
+ (sizeof(curl_socket_t) == sizeof(long)) ?
+ "FD %s:%d socket() = %ld\n" :
+ "FD %s:%d socket() = %zd\n";
+
+ curl_socket_t sockfd;
+
+ if(countcheck("socket", line, source))
+ return CURL_SOCKET_BAD;
+
+ sockfd = socket(domain, type, protocol);
+
+ if(source && (sockfd != CURL_SOCKET_BAD))
+ curl_dbg_log(fmt, source, line, sockfd);
+
+ return sockfd;
+}
+
+SEND_TYPE_RETV curl_dbg_send(SEND_TYPE_ARG1 sockfd,
+ SEND_QUAL_ARG2 SEND_TYPE_ARG2 buf,
+ SEND_TYPE_ARG3 len, SEND_TYPE_ARG4 flags, int line,
+ const char *source)
+{
+ SEND_TYPE_RETV rc;
+ if(countcheck("send", line, source))
+ return -1;
+ rc = send(sockfd, buf, len, flags);
+ if(source)
+ curl_dbg_log("SEND %s:%d send(%lu) = %ld\n",
+ source, line, (unsigned long)len, (long)rc);
+ return rc;
+}
+
+RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd, RECV_TYPE_ARG2 buf,
+ RECV_TYPE_ARG3 len, RECV_TYPE_ARG4 flags, int line,
+ const char *source)
+{
+ RECV_TYPE_RETV rc;
+ if(countcheck("recv", line, source))
+ return -1;
+ rc = recv(sockfd, buf, len, flags);
+ if(source)
+ curl_dbg_log("RECV %s:%d recv(%lu) = %ld\n",
+ source, line, (unsigned long)len, (long)rc);
+ return rc;
+}
+
+#ifdef HAVE_SOCKETPAIR
+int curl_dbg_socketpair(int domain, int type, int protocol,
+ curl_socket_t socket_vector[2],
+ int line, const char *source)
+{
+ const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
+ "FD %s:%d socketpair() = %d %d\n" :
+ (sizeof(curl_socket_t) == sizeof(long)) ?
+ "FD %s:%d socketpair() = %ld %ld\n" :
+ "FD %s:%d socketpair() = %zd %zd\n";
+
+ int res = socketpair(domain, type, protocol, socket_vector);
+
+ if(source && (0 == res))
+ curl_dbg_log(fmt, source, line, socket_vector[0], socket_vector[1]);
+
+ return res;
+}
+#endif
+
+curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen,
+ int line, const char *source)
+{
+ const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
+ "FD %s:%d accept() = %d\n" :
+ (sizeof(curl_socket_t) == sizeof(long)) ?
+ "FD %s:%d accept() = %ld\n" :
+ "FD %s:%d accept() = %zd\n";
+
+ struct sockaddr *addr = (struct sockaddr *)saddr;
+ curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen;
+
+ curl_socket_t sockfd = accept(s, addr, addrlen);
+
+ if(source && (sockfd != CURL_SOCKET_BAD))
+ curl_dbg_log(fmt, source, line, sockfd);
+
+ return sockfd;
+}
+
+/* separate function to allow libcurl to mark a "faked" close */
+void curl_dbg_mark_sclose(curl_socket_t sockfd, int line, const char *source)
+{
+ const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
+ "FD %s:%d sclose(%d)\n":
+ (sizeof(curl_socket_t) == sizeof(long)) ?
+ "FD %s:%d sclose(%ld)\n":
+ "FD %s:%d sclose(%zd)\n";
+
+ if(source)
+ curl_dbg_log(fmt, source, line, sockfd);
+}
+
+/* this is our own defined way to close sockets on *ALL* platforms */
+int curl_dbg_sclose(curl_socket_t sockfd, int line, const char *source)
+{
+ int res = sclose(sockfd);
+ curl_dbg_mark_sclose(sockfd, line, source);
+ return res;
+}
+
+ALLOC_FUNC FILE *curl_dbg_fopen(const char *file, const char *mode,
+ int line, const char *source)
+{
+ FILE *res = fopen(file, mode);
+
+ if(source)
+ curl_dbg_log("FILE %s:%d fopen(\"%s\",\"%s\") = %p\n",
+ source, line, file, mode, (void *)res);
+
+ return res;
+}
+
+ALLOC_FUNC FILE *curl_dbg_fdopen(int filedes, const char *mode,
+ int line, const char *source)
+{
+ FILE *res = fdopen(filedes, mode);
+ if(source)
+ curl_dbg_log("FILE %s:%d fdopen(\"%d\",\"%s\") = %p\n",
+ source, line, filedes, mode, (void *)res);
+ return res;
+}
+
+int curl_dbg_fclose(FILE *file, int line, const char *source)
+{
+ int res;
+
+ DEBUGASSERT(file != NULL);
+
+ if(source)
+ curl_dbg_log("FILE %s:%d fclose(%p)\n",
+ source, line, (void *)file);
+
+ res = fclose(file);
+
+ return res;
+}
+
+#define LOGLINE_BUFSIZE 1024
+
+/* this does the writing to the memory tracking log file */
+void curl_dbg_log(const char *format, ...)
+{
+ char *buf;
+ int nchars;
+ va_list ap;
+
+ if(!curl_dbg_logfile)
+ return;
+
+ buf = (Curl_cmalloc)(LOGLINE_BUFSIZE);
+ if(!buf)
+ return;
+
+ va_start(ap, format);
+ nchars = mvsnprintf(buf, LOGLINE_BUFSIZE, format, ap);
+ va_end(ap);
+
+ if(nchars > LOGLINE_BUFSIZE - 1)
+ nchars = LOGLINE_BUFSIZE - 1;
+
+ if(nchars > 0)
+ fwrite(buf, 1, (size_t)nchars, curl_dbg_logfile);
+
+ (Curl_cfree)(buf);
+}
+
+#endif /* CURLDEBUG */
diff --git a/libs/libcurl/src/memdebug.h b/libs/libcurl/src/memdebug.h index 7fc90e83a0..cd9fb03730 100644 --- a/libs/libcurl/src/memdebug.h +++ b/libs/libcurl/src/memdebug.h @@ -1,202 +1,202 @@ -#ifndef HEADER_CURL_MEMDEBUG_H -#define HEADER_CURL_MEMDEBUG_H -#ifdef CURLDEBUG -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - -/* - * CAUTION: this header is designed to work when included by the app-side - * as well as the library. Do not mix with library internals! - */ - -#include <curl/curl.h> -#include "functypes.h" - -#if defined(__GNUC__) && __GNUC__ >= 3 -# define ALLOC_FUNC __attribute__((malloc)) -# define ALLOC_SIZE(s) __attribute__((alloc_size(s))) -# define ALLOC_SIZE2(n, s) __attribute__((alloc_size(n, s))) -#elif defined(_MSC_VER) -# define ALLOC_FUNC __declspec(restrict) -# define ALLOC_SIZE(s) -# define ALLOC_SIZE2(n, s) -#else -# define ALLOC_FUNC -# define ALLOC_SIZE(s) -# define ALLOC_SIZE2(n, s) -#endif - -#define CURL_MT_LOGFNAME_BUFSIZE 512 - -extern FILE *curl_dbg_logfile; - -/* memory functions */ -CURL_EXTERN ALLOC_FUNC ALLOC_SIZE(1) void *curl_dbg_malloc(size_t size, - int line, - const char *source); -CURL_EXTERN ALLOC_FUNC ALLOC_SIZE2(1, 2) void *curl_dbg_calloc(size_t elements, - size_t size, int line, const char *source); -CURL_EXTERN ALLOC_SIZE(2) void *curl_dbg_realloc(void *ptr, - size_t size, - int line, - const char *source); -CURL_EXTERN void curl_dbg_free(void *ptr, int line, const char *source); -CURL_EXTERN ALLOC_FUNC char *curl_dbg_strdup(const char *str, int line, - const char *src); -#if defined(WIN32) && defined(UNICODE) -CURL_EXTERN ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str, - int line, - const char *source); -#endif - -CURL_EXTERN void curl_dbg_memdebug(const char *logname); -CURL_EXTERN void curl_dbg_memlimit(long limit); -CURL_EXTERN void curl_dbg_log(const char *format, ...); - -/* file descriptor manipulators */ -CURL_EXTERN curl_socket_t curl_dbg_socket(int domain, int type, int protocol, - int line, const char *source); -CURL_EXTERN void curl_dbg_mark_sclose(curl_socket_t sockfd, - int line, const char *source); -CURL_EXTERN int curl_dbg_sclose(curl_socket_t sockfd, - int line, const char *source); -CURL_EXTERN curl_socket_t curl_dbg_accept(curl_socket_t s, void *a, void *alen, - int line, const char *source); -#ifdef HAVE_SOCKETPAIR -CURL_EXTERN int curl_dbg_socketpair(int domain, int type, int protocol, - curl_socket_t socket_vector[2], - int line, const char *source); -#endif - -/* send/receive sockets */ -CURL_EXTERN SEND_TYPE_RETV curl_dbg_send(SEND_TYPE_ARG1 sockfd, - SEND_QUAL_ARG2 SEND_TYPE_ARG2 buf, - SEND_TYPE_ARG3 len, - SEND_TYPE_ARG4 flags, int line, - const char *source); -CURL_EXTERN RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd, - RECV_TYPE_ARG2 buf, - RECV_TYPE_ARG3 len, - RECV_TYPE_ARG4 flags, int line, - const char *source); - -/* FILE functions */ -CURL_EXTERN ALLOC_FUNC FILE *curl_dbg_fopen(const char *file, const char *mode, - int line, const char *source); -CURL_EXTERN ALLOC_FUNC FILE *curl_dbg_fdopen(int filedes, const char *mode, - int line, const char *source); - -CURL_EXTERN int curl_dbg_fclose(FILE *file, int line, const char *source); - -#ifndef MEMDEBUG_NODEFINES - -/* Set this symbol on the command-line, recompile all lib-sources */ -#undef strdup -#define strdup(ptr) curl_dbg_strdup(ptr, __LINE__, __FILE__) -#define malloc(size) curl_dbg_malloc(size, __LINE__, __FILE__) -#define calloc(nbelem,size) curl_dbg_calloc(nbelem, size, __LINE__, __FILE__) -#define realloc(ptr,size) curl_dbg_realloc(ptr, size, __LINE__, __FILE__) -#define free(ptr) curl_dbg_free(ptr, __LINE__, __FILE__) -#define send(a,b,c,d) curl_dbg_send(a,b,c,d, __LINE__, __FILE__) -#define recv(a,b,c,d) curl_dbg_recv(a,b,c,d, __LINE__, __FILE__) - -#ifdef WIN32 -# ifdef UNICODE -# undef wcsdup -# define wcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__) -# undef _wcsdup -# define _wcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__) -# undef _tcsdup -# define _tcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__) -# else -# undef _tcsdup -# define _tcsdup(ptr) curl_dbg_strdup(ptr, __LINE__, __FILE__) -# endif -#endif - -#undef socket -#define socket(domain,type,protocol)\ - curl_dbg_socket(domain, type, protocol, __LINE__, __FILE__) -#undef accept /* for those with accept as a macro */ -#define accept(sock,addr,len)\ - curl_dbg_accept(sock, addr, len, __LINE__, __FILE__) -#ifdef HAVE_SOCKETPAIR -#define socketpair(domain,type,protocol,socket_vector)\ - curl_dbg_socketpair(domain, type, protocol, socket_vector, __LINE__, __FILE__) -#endif - -#ifdef HAVE_GETADDRINFO -#if defined(getaddrinfo) && defined(__osf__) -/* OSF/1 and Tru64 have getaddrinfo as a define already, so we cannot define - our macro as for other platforms. Instead, we redefine the new name they - define getaddrinfo to become! */ -#define ogetaddrinfo(host,serv,hint,res) \ - curl_dbg_getaddrinfo(host, serv, hint, res, __LINE__, __FILE__) -#else -#undef getaddrinfo -#define getaddrinfo(host,serv,hint,res) \ - curl_dbg_getaddrinfo(host, serv, hint, res, __LINE__, __FILE__) -#endif -#endif /* HAVE_GETADDRINFO */ - -#ifdef HAVE_FREEADDRINFO -#undef freeaddrinfo -#define freeaddrinfo(data) \ - curl_dbg_freeaddrinfo(data, __LINE__, __FILE__) -#endif /* HAVE_FREEADDRINFO */ - -/* sclose is probably already defined, redefine it! */ -#undef sclose -#define sclose(sockfd) curl_dbg_sclose(sockfd,__LINE__,__FILE__) - -#define fake_sclose(sockfd) curl_dbg_mark_sclose(sockfd,__LINE__,__FILE__) - -#undef fopen -#define fopen(file,mode) curl_dbg_fopen(file,mode,__LINE__,__FILE__) -#undef fdopen -#define fdopen(file,mode) curl_dbg_fdopen(file,mode,__LINE__,__FILE__) -#define fclose(file) curl_dbg_fclose(file,__LINE__,__FILE__) - -#endif /* MEMDEBUG_NODEFINES */ - -#endif /* CURLDEBUG */ - -/* -** Following section applies even when CURLDEBUG is not defined. -*/ - -#ifndef fake_sclose -#define fake_sclose(x) Curl_nop_stmt -#endif - -/* - * Curl_safefree defined as a macro to allow MemoryTracking feature - * to log free() calls at same location where Curl_safefree is used. - * This macro also assigns NULL to given pointer when free'd. - */ - -#define Curl_safefree(ptr) \ - do { free((ptr)); (ptr) = NULL;} while(0) - -#endif /* HEADER_CURL_MEMDEBUG_H */ +#ifndef HEADER_CURL_MEMDEBUG_H
+#define HEADER_CURL_MEMDEBUG_H
+#ifdef CURLDEBUG
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+/*
+ * CAUTION: this header is designed to work when included by the app-side
+ * as well as the library. Do not mix with library internals!
+ */
+
+#include <curl/curl.h>
+#include "functypes.h"
+
+#if defined(__GNUC__) && __GNUC__ >= 3
+# define ALLOC_FUNC __attribute__((malloc))
+# define ALLOC_SIZE(s) __attribute__((alloc_size(s)))
+# define ALLOC_SIZE2(n, s) __attribute__((alloc_size(n, s)))
+#elif defined(_MSC_VER)
+# define ALLOC_FUNC __declspec(restrict)
+# define ALLOC_SIZE(s)
+# define ALLOC_SIZE2(n, s)
+#else
+# define ALLOC_FUNC
+# define ALLOC_SIZE(s)
+# define ALLOC_SIZE2(n, s)
+#endif
+
+#define CURL_MT_LOGFNAME_BUFSIZE 512
+
+extern FILE *curl_dbg_logfile;
+
+/* memory functions */
+CURL_EXTERN ALLOC_FUNC ALLOC_SIZE(1) void *curl_dbg_malloc(size_t size,
+ int line,
+ const char *source);
+CURL_EXTERN ALLOC_FUNC ALLOC_SIZE2(1, 2) void *curl_dbg_calloc(size_t elements,
+ size_t size, int line, const char *source);
+CURL_EXTERN ALLOC_SIZE(2) void *curl_dbg_realloc(void *ptr,
+ size_t size,
+ int line,
+ const char *source);
+CURL_EXTERN void curl_dbg_free(void *ptr, int line, const char *source);
+CURL_EXTERN ALLOC_FUNC char *curl_dbg_strdup(const char *str, int line,
+ const char *src);
+#if defined(WIN32) && defined(UNICODE)
+CURL_EXTERN ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str,
+ int line,
+ const char *source);
+#endif
+
+CURL_EXTERN void curl_dbg_memdebug(const char *logname);
+CURL_EXTERN void curl_dbg_memlimit(long limit);
+CURL_EXTERN void curl_dbg_log(const char *format, ...);
+
+/* file descriptor manipulators */
+CURL_EXTERN curl_socket_t curl_dbg_socket(int domain, int type, int protocol,
+ int line, const char *source);
+CURL_EXTERN void curl_dbg_mark_sclose(curl_socket_t sockfd,
+ int line, const char *source);
+CURL_EXTERN int curl_dbg_sclose(curl_socket_t sockfd,
+ int line, const char *source);
+CURL_EXTERN curl_socket_t curl_dbg_accept(curl_socket_t s, void *a, void *alen,
+ int line, const char *source);
+#ifdef HAVE_SOCKETPAIR
+CURL_EXTERN int curl_dbg_socketpair(int domain, int type, int protocol,
+ curl_socket_t socket_vector[2],
+ int line, const char *source);
+#endif
+
+/* send/receive sockets */
+CURL_EXTERN SEND_TYPE_RETV curl_dbg_send(SEND_TYPE_ARG1 sockfd,
+ SEND_QUAL_ARG2 SEND_TYPE_ARG2 buf,
+ SEND_TYPE_ARG3 len,
+ SEND_TYPE_ARG4 flags, int line,
+ const char *source);
+CURL_EXTERN RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd,
+ RECV_TYPE_ARG2 buf,
+ RECV_TYPE_ARG3 len,
+ RECV_TYPE_ARG4 flags, int line,
+ const char *source);
+
+/* FILE functions */
+CURL_EXTERN ALLOC_FUNC FILE *curl_dbg_fopen(const char *file, const char *mode,
+ int line, const char *source);
+CURL_EXTERN ALLOC_FUNC FILE *curl_dbg_fdopen(int filedes, const char *mode,
+ int line, const char *source);
+
+CURL_EXTERN int curl_dbg_fclose(FILE *file, int line, const char *source);
+
+#ifndef MEMDEBUG_NODEFINES
+
+/* Set this symbol on the command-line, recompile all lib-sources */
+#undef strdup
+#define strdup(ptr) curl_dbg_strdup(ptr, __LINE__, __FILE__)
+#define malloc(size) curl_dbg_malloc(size, __LINE__, __FILE__)
+#define calloc(nbelem,size) curl_dbg_calloc(nbelem, size, __LINE__, __FILE__)
+#define realloc(ptr,size) curl_dbg_realloc(ptr, size, __LINE__, __FILE__)
+#define free(ptr) curl_dbg_free(ptr, __LINE__, __FILE__)
+#define send(a,b,c,d) curl_dbg_send(a,b,c,d, __LINE__, __FILE__)
+#define recv(a,b,c,d) curl_dbg_recv(a,b,c,d, __LINE__, __FILE__)
+
+#ifdef WIN32
+# ifdef UNICODE
+# undef wcsdup
+# define wcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__)
+# undef _wcsdup
+# define _wcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__)
+# undef _tcsdup
+# define _tcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__)
+# else
+# undef _tcsdup
+# define _tcsdup(ptr) curl_dbg_strdup(ptr, __LINE__, __FILE__)
+# endif
+#endif
+
+#undef socket
+#define socket(domain,type,protocol)\
+ curl_dbg_socket(domain, type, protocol, __LINE__, __FILE__)
+#undef accept /* for those with accept as a macro */
+#define accept(sock,addr,len)\
+ curl_dbg_accept(sock, addr, len, __LINE__, __FILE__)
+#ifdef HAVE_SOCKETPAIR
+#define socketpair(domain,type,protocol,socket_vector)\
+ curl_dbg_socketpair(domain, type, protocol, socket_vector, __LINE__, __FILE__)
+#endif
+
+#ifdef HAVE_GETADDRINFO
+#if defined(getaddrinfo) && defined(__osf__)
+/* OSF/1 and Tru64 have getaddrinfo as a define already, so we cannot define
+ our macro as for other platforms. Instead, we redefine the new name they
+ define getaddrinfo to become! */
+#define ogetaddrinfo(host,serv,hint,res) \
+ curl_dbg_getaddrinfo(host, serv, hint, res, __LINE__, __FILE__)
+#else
+#undef getaddrinfo
+#define getaddrinfo(host,serv,hint,res) \
+ curl_dbg_getaddrinfo(host, serv, hint, res, __LINE__, __FILE__)
+#endif
+#endif /* HAVE_GETADDRINFO */
+
+#ifdef HAVE_FREEADDRINFO
+#undef freeaddrinfo
+#define freeaddrinfo(data) \
+ curl_dbg_freeaddrinfo(data, __LINE__, __FILE__)
+#endif /* HAVE_FREEADDRINFO */
+
+/* sclose is probably already defined, redefine it! */
+#undef sclose
+#define sclose(sockfd) curl_dbg_sclose(sockfd,__LINE__,__FILE__)
+
+#define fake_sclose(sockfd) curl_dbg_mark_sclose(sockfd,__LINE__,__FILE__)
+
+#undef fopen
+#define fopen(file,mode) curl_dbg_fopen(file,mode,__LINE__,__FILE__)
+#undef fdopen
+#define fdopen(file,mode) curl_dbg_fdopen(file,mode,__LINE__,__FILE__)
+#define fclose(file) curl_dbg_fclose(file,__LINE__,__FILE__)
+
+#endif /* MEMDEBUG_NODEFINES */
+
+#endif /* CURLDEBUG */
+
+/*
+** Following section applies even when CURLDEBUG is not defined.
+*/
+
+#ifndef fake_sclose
+#define fake_sclose(x) Curl_nop_stmt
+#endif
+
+/*
+ * Curl_safefree defined as a macro to allow MemoryTracking feature
+ * to log free() calls at same location where Curl_safefree is used.
+ * This macro also assigns NULL to given pointer when free'd.
+ */
+
+#define Curl_safefree(ptr) \
+ do { free((ptr)); (ptr) = NULL;} while(0)
+
+#endif /* HEADER_CURL_MEMDEBUG_H */
diff --git a/libs/libcurl/src/mime.c b/libs/libcurl/src/mime.c index 042141fc80..1653af4970 100644 --- a/libs/libcurl/src/mime.c +++ b/libs/libcurl/src/mime.c @@ -1,2032 +1,2028 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "mime.h" -#include "warnless.h" -#include "urldata.h" -#include "sendf.h" - -#if !defined(CURL_DISABLE_MIME) && (!defined(CURL_DISABLE_HTTP) || \ - !defined(CURL_DISABLE_SMTP) || \ - !defined(CURL_DISABLE_IMAP)) - -#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME) -#include <libgen.h> -#endif - -#include "rand.h" -#include "slist.h" -#include "strcase.h" -#include "dynbuf.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#ifdef WIN32 -# ifndef R_OK -# define R_OK 4 -# endif -#endif - - -#define READ_ERROR ((size_t) -1) -#define STOP_FILLING ((size_t) -2) - -static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems, - void *instream, bool *hasread); - -/* Encoders. */ -static size_t encoder_nop_read(char *buffer, size_t size, bool ateof, - curl_mimepart *part); -static curl_off_t encoder_nop_size(curl_mimepart *part); -static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof, - curl_mimepart *part); -static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, - curl_mimepart *part); -static curl_off_t encoder_base64_size(curl_mimepart *part); -static size_t encoder_qp_read(char *buffer, size_t size, bool ateof, - curl_mimepart *part); -static curl_off_t encoder_qp_size(curl_mimepart *part); - -static const struct mime_encoder encoders[] = { - {"binary", encoder_nop_read, encoder_nop_size}, - {"8bit", encoder_nop_read, encoder_nop_size}, - {"7bit", encoder_7bit_read, encoder_nop_size}, - {"base64", encoder_base64_read, encoder_base64_size}, - {"quoted-printable", encoder_qp_read, encoder_qp_size}, - {ZERO_NULL, ZERO_NULL, ZERO_NULL} -}; - -/* Base64 encoding table */ -static const char base64[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -/* Quoted-printable character class table. - * - * We cannot rely on ctype functions since quoted-printable input data - * is assumed to be ascii-compatible, even on non-ascii platforms. */ -#define QP_OK 1 /* Can be represented by itself. */ -#define QP_SP 2 /* Space or tab. */ -#define QP_CR 3 /* Carriage return. */ -#define QP_LF 4 /* Line-feed. */ -static const unsigned char qp_class[] = { - 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 07 */ - 0, QP_SP, QP_LF, 0, 0, QP_CR, 0, 0, /* 08 - 0F */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 17 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 18 - 1F */ - QP_SP, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 20 - 27 */ - QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 28 - 2F */ - QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 30 - 37 */ - QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, 0 , QP_OK, QP_OK, /* 38 - 3F */ - QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 40 - 47 */ - QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 48 - 4F */ - QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 50 - 57 */ - QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 58 - 5F */ - QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 60 - 67 */ - QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 68 - 6F */ - QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 70 - 77 */ - QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, 0, /* 78 - 7F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */ -}; - - -/* Binary --> hexadecimal ASCII table. */ -static const char aschex[] = - "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x41\x42\x43\x44\x45\x46"; - - - -#ifndef __VMS -#define filesize(name, stat_data) (stat_data.st_size) -#define fopen_read fopen - -#else - -#include <fabdef.h> -/* - * get_vms_file_size does what it takes to get the real size of the file - * - * For fixed files, find out the size of the EOF block and adjust. - * - * For all others, have to read the entire file in, discarding the contents. - * Most posted text files will be small, and binary files like zlib archives - * and CD/DVD images should be either a STREAM_LF format or a fixed format. - * - */ -curl_off_t VmsRealFileSize(const char *name, - const struct_stat *stat_buf) -{ - char buffer[8192]; - curl_off_t count; - int ret_stat; - FILE * file; - - file = fopen(name, FOPEN_READTEXT); /* VMS */ - if(!file) - return 0; - - count = 0; - ret_stat = 1; - while(ret_stat > 0) { - ret_stat = fread(buffer, 1, sizeof(buffer), file); - if(ret_stat) - count += ret_stat; - } - fclose(file); - - return count; -} - -/* - * - * VmsSpecialSize checks to see if the stat st_size can be trusted and - * if not to call a routine to get the correct size. - * - */ -static curl_off_t VmsSpecialSize(const char *name, - const struct_stat *stat_buf) -{ - switch(stat_buf->st_fab_rfm) { - case FAB$C_VAR: - case FAB$C_VFC: - return VmsRealFileSize(name, stat_buf); - break; - default: - return stat_buf->st_size; - } -} - -#define filesize(name, stat_data) VmsSpecialSize(name, &stat_data) - -/* - * vmsfopenread - * - * For upload to work as expected on VMS, different optional - * parameters must be added to the fopen command based on - * record format of the file. - * - */ -static FILE * vmsfopenread(const char *file, const char *mode) -{ - struct_stat statbuf; - int result; - - result = stat(file, &statbuf); - - switch(statbuf.st_fab_rfm) { - case FAB$C_VAR: - case FAB$C_VFC: - case FAB$C_STMCR: - return fopen(file, FOPEN_READTEXT); /* VMS */ - break; - default: - return fopen(file, FOPEN_READTEXT, "rfm=stmlf", "ctx=stm"); - } -} - -#define fopen_read vmsfopenread -#endif - - -#ifndef HAVE_BASENAME -/* - (Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004 - Edition) - - The basename() function shall take the pathname pointed to by path and - return a pointer to the final component of the pathname, deleting any - trailing '/' characters. - - If the string pointed to by path consists entirely of the '/' character, - basename() shall return a pointer to the string "/". If the string pointed - to by path is exactly "//", it is implementation-defined whether '/' or "//" - is returned. - - If path is a null pointer or points to an empty string, basename() shall - return a pointer to the string ".". - - The basename() function may modify the string pointed to by path, and may - return a pointer to static storage that may then be overwritten by a - subsequent call to basename(). - - The basename() function need not be reentrant. A function that is not - required to be reentrant is not required to be thread-safe. - -*/ -static char *Curl_basename(char *path) -{ - /* Ignore all the details above for now and make a quick and simple - implementation here */ - char *s1; - char *s2; - - s1 = strrchr(path, '/'); - s2 = strrchr(path, '\\'); - - if(s1 && s2) { - path = (s1 > s2? s1 : s2) + 1; - } - else if(s1) - path = s1 + 1; - else if(s2) - path = s2 + 1; - - return path; -} - -#define basename(x) Curl_basename((x)) -#endif - - -/* Set readback state. */ -static void mimesetstate(struct mime_state *state, - enum mimestate tok, void *ptr) -{ - state->state = tok; - state->ptr = ptr; - state->offset = 0; -} - - -/* Escape header string into allocated memory. */ -static char *escape_string(struct Curl_easy *data, - const char *src, enum mimestrategy strategy) -{ - CURLcode result; - struct dynbuf db; - const char * const *table; - const char * const *p; - /* replace first character by rest of string. */ - static const char * const mimetable[] = { - "\\\\\\", - "\"\\\"", - NULL - }; - /* WHATWG HTML living standard 4.10.21.8 2 specifies: - For field names and filenames for file fields, the result of the - encoding in the previous bullet point must be escaped by replacing - any 0x0A (LF) bytes with the byte sequence `%0A`, 0x0D (CR) with `%0D` - and 0x22 (") with `%22`. - The user agent must not perform any other escapes. */ - static const char * const formtable[] = { - "\"%22", - "\r%0D", - "\n%0A", - NULL - }; - - table = formtable; - /* data can be NULL when this function is called indirectly from - curl_formget(). */ - if(strategy == MIMESTRATEGY_MAIL || - (data && (data->set.mime_options & CURLMIMEOPT_FORMESCAPE))) - table = mimetable; - - Curl_dyn_init(&db, CURL_MAX_INPUT_LENGTH); - - for(result = Curl_dyn_addn(&db, STRCONST("")); !result && *src; src++) { - for(p = table; *p && **p != *src; p++) - ; - - if(*p) - result = Curl_dyn_add(&db, *p + 1); - else - result = Curl_dyn_addn(&db, src, 1); - } - - return Curl_dyn_ptr(&db); -} - -/* Check if header matches. */ -static char *match_header(struct curl_slist *hdr, const char *lbl, size_t len) -{ - char *value = NULL; - - if(strncasecompare(hdr->data, lbl, len) && hdr->data[len] == ':') - for(value = hdr->data + len + 1; *value == ' '; value++) - ; - return value; -} - -/* Get a header from an slist. */ -static char *search_header(struct curl_slist *hdrlist, - const char *hdr, size_t len) -{ - char *value = NULL; - - for(; !value && hdrlist; hdrlist = hdrlist->next) - value = match_header(hdrlist, hdr, len); - - return value; -} - -static char *strippath(const char *fullfile) -{ - char *filename; - char *base; - filename = strdup(fullfile); /* duplicate since basename() may ruin the - buffer it works on */ - if(!filename) - return NULL; - base = strdup(basename(filename)); - - free(filename); /* free temporary buffer */ - - return base; /* returns an allocated string or NULL ! */ -} - -/* Initialize data encoder state. */ -static void cleanup_encoder_state(struct mime_encoder_state *p) -{ - p->pos = 0; - p->bufbeg = 0; - p->bufend = 0; -} - - -/* Dummy encoder. This is used for 8bit and binary content encodings. */ -static size_t encoder_nop_read(char *buffer, size_t size, bool ateof, - struct curl_mimepart *part) -{ - struct mime_encoder_state *st = &part->encstate; - size_t insize = st->bufend - st->bufbeg; - - (void) ateof; - - if(!size) - return STOP_FILLING; - - if(size > insize) - size = insize; - - if(size) - memcpy(buffer, st->buf + st->bufbeg, size); - - st->bufbeg += size; - return size; -} - -static curl_off_t encoder_nop_size(curl_mimepart *part) -{ - return part->datasize; -} - - -/* 7bit encoder: the encoder is just a data validity check. */ -static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof, - curl_mimepart *part) -{ - struct mime_encoder_state *st = &part->encstate; - size_t cursize = st->bufend - st->bufbeg; - - (void) ateof; - - if(!size) - return STOP_FILLING; - - if(size > cursize) - size = cursize; - - for(cursize = 0; cursize < size; cursize++) { - *buffer = st->buf[st->bufbeg]; - if(*buffer++ & 0x80) - return cursize? cursize: READ_ERROR; - st->bufbeg++; - } - - return cursize; -} - - -/* Base64 content encoder. */ -static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, - curl_mimepart *part) -{ - struct mime_encoder_state *st = &part->encstate; - size_t cursize = 0; - int i; - char *ptr = buffer; - - while(st->bufbeg < st->bufend) { - /* Line full ? */ - if(st->pos > MAX_ENCODED_LINE_LENGTH - 4) { - /* Yes, we need 2 characters for CRLF. */ - if(size < 2) { - if(!cursize) - return STOP_FILLING; - break; - } - *ptr++ = '\r'; - *ptr++ = '\n'; - st->pos = 0; - cursize += 2; - size -= 2; - } - - /* Be sure there is enough space and input data for a base64 group. */ - if(size < 4) { - if(!cursize) - return STOP_FILLING; - break; - } - if(st->bufend - st->bufbeg < 3) - break; - - /* Encode three bytes as four characters. */ - i = st->buf[st->bufbeg++] & 0xFF; - i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF); - i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF); - *ptr++ = base64[(i >> 18) & 0x3F]; - *ptr++ = base64[(i >> 12) & 0x3F]; - *ptr++ = base64[(i >> 6) & 0x3F]; - *ptr++ = base64[i & 0x3F]; - cursize += 4; - st->pos += 4; - size -= 4; - } - - /* If at eof, we have to flush the buffered data. */ - if(ateof) { - if(size < 4) { - if(!cursize) - return STOP_FILLING; - } - else { - /* Buffered data size can only be 0, 1 or 2. */ - ptr[2] = ptr[3] = '='; - i = 0; - - /* If there is buffered data */ - if(st->bufend != st->bufbeg) { - - if(st->bufend - st->bufbeg == 2) - i = (st->buf[st->bufbeg + 1] & 0xFF) << 8; - - i |= (st->buf[st->bufbeg] & 0xFF) << 16; - ptr[0] = base64[(i >> 18) & 0x3F]; - ptr[1] = base64[(i >> 12) & 0x3F]; - if(++st->bufbeg != st->bufend) { - ptr[2] = base64[(i >> 6) & 0x3F]; - st->bufbeg++; - } - cursize += 4; - st->pos += 4; - } - } - } - - return cursize; -} - -static curl_off_t encoder_base64_size(curl_mimepart *part) -{ - curl_off_t size = part->datasize; - - if(size <= 0) - return size; /* Unknown size or no data. */ - - /* Compute base64 character count. */ - size = 4 * (1 + (size - 1) / 3); - - /* Effective character count must include CRLFs. */ - return size + 2 * ((size - 1) / MAX_ENCODED_LINE_LENGTH); -} - - -/* Quoted-printable lookahead. - * - * Check if a CRLF or end of data is in input buffer at current position + n. - * Return -1 if more data needed, 1 if CRLF or end of data, else 0. - */ -static int qp_lookahead_eol(struct mime_encoder_state *st, int ateof, size_t n) -{ - n += st->bufbeg; - if(n >= st->bufend && ateof) - return 1; - if(n + 2 > st->bufend) - return ateof? 0: -1; - if(qp_class[st->buf[n] & 0xFF] == QP_CR && - qp_class[st->buf[n + 1] & 0xFF] == QP_LF) - return 1; - return 0; -} - -/* Quoted-printable encoder. */ -static size_t encoder_qp_read(char *buffer, size_t size, bool ateof, - curl_mimepart *part) -{ - struct mime_encoder_state *st = &part->encstate; - char *ptr = buffer; - size_t cursize = 0; - int softlinebreak; - char buf[4]; - - /* On all platforms, input is supposed to be ASCII compatible: for this - reason, we use hexadecimal ASCII codes in this function rather than - character constants that can be interpreted as non-ascii on some - platforms. Preserve ASCII encoding on output too. */ - while(st->bufbeg < st->bufend) { - size_t len = 1; - size_t consumed = 1; - int i = st->buf[st->bufbeg]; - buf[0] = (char) i; - buf[1] = aschex[(i >> 4) & 0xF]; - buf[2] = aschex[i & 0xF]; - - switch(qp_class[st->buf[st->bufbeg] & 0xFF]) { - case QP_OK: /* Not a special character. */ - break; - case QP_SP: /* Space or tab. */ - /* Spacing must be escaped if followed by CRLF. */ - switch(qp_lookahead_eol(st, ateof, 1)) { - case -1: /* More input data needed. */ - return cursize; - case 0: /* No encoding needed. */ - break; - default: /* CRLF after space or tab. */ - buf[0] = '\x3D'; /* '=' */ - len = 3; - break; - } - break; - case QP_CR: /* Carriage return. */ - /* If followed by a line-feed, output the CRLF pair. - Else escape it. */ - switch(qp_lookahead_eol(st, ateof, 0)) { - case -1: /* Need more data. */ - return cursize; - case 1: /* CRLF found. */ - buf[len++] = '\x0A'; /* Append '\n'. */ - consumed = 2; - break; - default: /* Not followed by LF: escape. */ - buf[0] = '\x3D'; /* '=' */ - len = 3; - break; - } - break; - default: /* Character must be escaped. */ - buf[0] = '\x3D'; /* '=' */ - len = 3; - break; - } - - /* Be sure the encoded character fits within maximum line length. */ - if(buf[len - 1] != '\x0A') { /* '\n' */ - softlinebreak = st->pos + len > MAX_ENCODED_LINE_LENGTH; - if(!softlinebreak && st->pos + len == MAX_ENCODED_LINE_LENGTH) { - /* We may use the current line only if end of data or followed by - a CRLF. */ - switch(qp_lookahead_eol(st, ateof, consumed)) { - case -1: /* Need more data. */ - return cursize; - case 0: /* Not followed by a CRLF. */ - softlinebreak = 1; - break; - } - } - if(softlinebreak) { - strcpy(buf, "\x3D\x0D\x0A"); /* "=\r\n" */ - len = 3; - consumed = 0; - } - } - - /* If the output buffer would overflow, do not store. */ - if(len > size) { - if(!cursize) - return STOP_FILLING; - break; - } - - /* Append to output buffer. */ - memcpy(ptr, buf, len); - cursize += len; - ptr += len; - size -= len; - st->pos += len; - if(buf[len - 1] == '\x0A') /* '\n' */ - st->pos = 0; - st->bufbeg += consumed; - } - - return cursize; -} - -static curl_off_t encoder_qp_size(curl_mimepart *part) -{ - /* Determining the size can only be done by reading the data: unless the - data size is 0, we return it as unknown (-1). */ - return part->datasize? -1: 0; -} - - -/* In-memory data callbacks. */ -/* Argument is a pointer to the mime part. */ -static size_t mime_mem_read(char *buffer, size_t size, size_t nitems, - void *instream) -{ - curl_mimepart *part = (curl_mimepart *) instream; - size_t sz = curlx_sotouz(part->datasize - part->state.offset); - (void) size; /* Always 1.*/ - - if(!nitems) - return STOP_FILLING; - - if(sz > nitems) - sz = nitems; - - if(sz) - memcpy(buffer, part->data + curlx_sotouz(part->state.offset), sz); - - return sz; -} - -static int mime_mem_seek(void *instream, curl_off_t offset, int whence) -{ - curl_mimepart *part = (curl_mimepart *) instream; - - switch(whence) { - case SEEK_CUR: - offset += part->state.offset; - break; - case SEEK_END: - offset += part->datasize; - break; - } - - if(offset < 0 || offset > part->datasize) - return CURL_SEEKFUNC_FAIL; - - part->state.offset = offset; - return CURL_SEEKFUNC_OK; -} - -static void mime_mem_free(void *ptr) -{ - Curl_safefree(((curl_mimepart *) ptr)->data); -} - - -/* Named file callbacks. */ -/* Argument is a pointer to the mime part. */ -static int mime_open_file(curl_mimepart *part) -{ - /* Open a MIMEKIND_FILE part. */ - - if(part->fp) - return 0; - part->fp = fopen_read(part->data, "rb"); - return part->fp? 0: -1; -} - -static size_t mime_file_read(char *buffer, size_t size, size_t nitems, - void *instream) -{ - curl_mimepart *part = (curl_mimepart *) instream; - - if(!nitems) - return STOP_FILLING; - - if(mime_open_file(part)) - return READ_ERROR; - - return fread(buffer, size, nitems, part->fp); -} - -static int mime_file_seek(void *instream, curl_off_t offset, int whence) -{ - curl_mimepart *part = (curl_mimepart *) instream; - - if(whence == SEEK_SET && !offset && !part->fp) - return CURL_SEEKFUNC_OK; /* Not open: implicitly already at BOF. */ - - if(mime_open_file(part)) - return CURL_SEEKFUNC_FAIL; - - return fseek(part->fp, (long) offset, whence)? - CURL_SEEKFUNC_CANTSEEK: CURL_SEEKFUNC_OK; -} - -static void mime_file_free(void *ptr) -{ - curl_mimepart *part = (curl_mimepart *) ptr; - - if(part->fp) { - fclose(part->fp); - part->fp = NULL; - } - Curl_safefree(part->data); - part->data = NULL; -} - - -/* Subparts callbacks. */ -/* Argument is a pointer to the mime structure. */ - -/* Readback a byte string segment. */ -static size_t readback_bytes(struct mime_state *state, - char *buffer, size_t bufsize, - const char *bytes, size_t numbytes, - const char *trail, size_t traillen) -{ - size_t sz; - size_t offset = curlx_sotouz(state->offset); - - if(numbytes > offset) { - sz = numbytes - offset; - bytes += offset; - } - else { - sz = offset - numbytes; - if(sz >= traillen) - return 0; - bytes = trail + sz; - sz = traillen - sz; - } - - if(sz > bufsize) - sz = bufsize; - - memcpy(buffer, bytes, sz); - state->offset += sz; - return sz; -} - -/* Read a non-encoded part content. */ -static size_t read_part_content(curl_mimepart *part, - char *buffer, size_t bufsize, bool *hasread) -{ - size_t sz = 0; - - switch(part->lastreadstatus) { - case 0: - case CURL_READFUNC_ABORT: - case CURL_READFUNC_PAUSE: - case READ_ERROR: - return part->lastreadstatus; - default: - break; - } - - /* If we can determine we are at end of part data, spare a read. */ - if(part->datasize != (curl_off_t) -1 && - part->state.offset >= part->datasize) { - /* sz is already zero. */ - } - else { - switch(part->kind) { - case MIMEKIND_MULTIPART: - /* - * Cannot be processed as other kinds since read function requires - * an additional parameter and is highly recursive. - */ - sz = mime_subparts_read(buffer, 1, bufsize, part->arg, hasread); - break; - case MIMEKIND_FILE: - if(part->fp && feof(part->fp)) - break; /* At EOF. */ - /* FALLTHROUGH */ - default: - if(part->readfunc) { - if(!(part->flags & MIME_FAST_READ)) { - if(*hasread) - return STOP_FILLING; - *hasread = TRUE; - } - sz = part->readfunc(buffer, 1, bufsize, part->arg); - } - break; - } - } - - switch(sz) { - case STOP_FILLING: - break; - case 0: - case CURL_READFUNC_ABORT: - case CURL_READFUNC_PAUSE: - case READ_ERROR: - part->lastreadstatus = sz; - break; - default: - part->state.offset += sz; - part->lastreadstatus = sz; - break; - } - - return sz; -} - -/* Read and encode part content. */ -static size_t read_encoded_part_content(curl_mimepart *part, char *buffer, - size_t bufsize, bool *hasread) -{ - struct mime_encoder_state *st = &part->encstate; - size_t cursize = 0; - size_t sz; - bool ateof = FALSE; - - for(;;) { - if(st->bufbeg < st->bufend || ateof) { - /* Encode buffered data. */ - sz = part->encoder->encodefunc(buffer, bufsize, ateof, part); - switch(sz) { - case 0: - if(ateof) - return cursize; - break; - case READ_ERROR: - case STOP_FILLING: - return cursize? cursize: sz; - default: - cursize += sz; - buffer += sz; - bufsize -= sz; - continue; - } - } - - /* We need more data in input buffer. */ - if(st->bufbeg) { - size_t len = st->bufend - st->bufbeg; - - if(len) - memmove(st->buf, st->buf + st->bufbeg, len); - st->bufbeg = 0; - st->bufend = len; - } - if(st->bufend >= sizeof(st->buf)) - return cursize? cursize: READ_ERROR; /* Buffer full. */ - sz = read_part_content(part, st->buf + st->bufend, - sizeof(st->buf) - st->bufend, hasread); - switch(sz) { - case 0: - ateof = TRUE; - break; - case CURL_READFUNC_ABORT: - case CURL_READFUNC_PAUSE: - case READ_ERROR: - case STOP_FILLING: - return cursize? cursize: sz; - default: - st->bufend += sz; - break; - } - } - - /* NOTREACHED */ -} - -/* Readback a mime part. */ -static size_t readback_part(curl_mimepart *part, - char *buffer, size_t bufsize, bool *hasread) -{ - size_t cursize = 0; - - /* Readback from part. */ - - while(bufsize) { - size_t sz = 0; - struct curl_slist *hdr = (struct curl_slist *) part->state.ptr; - switch(part->state.state) { - case MIMESTATE_BEGIN: - mimesetstate(&part->state, - (part->flags & MIME_BODY_ONLY)? - MIMESTATE_BODY: MIMESTATE_CURLHEADERS, - part->curlheaders); - break; - case MIMESTATE_USERHEADERS: - if(!hdr) { - mimesetstate(&part->state, MIMESTATE_EOH, NULL); - break; - } - if(match_header(hdr, "Content-Type", 12)) { - mimesetstate(&part->state, MIMESTATE_USERHEADERS, hdr->next); - break; - } - /* FALLTHROUGH */ - case MIMESTATE_CURLHEADERS: - if(!hdr) - mimesetstate(&part->state, MIMESTATE_USERHEADERS, part->userheaders); - else { - sz = readback_bytes(&part->state, buffer, bufsize, - hdr->data, strlen(hdr->data), STRCONST("\r\n")); - if(!sz) - mimesetstate(&part->state, part->state.state, hdr->next); - } - break; - case MIMESTATE_EOH: - sz = readback_bytes(&part->state, buffer, bufsize, STRCONST("\r\n"), - STRCONST("")); - if(!sz) - mimesetstate(&part->state, MIMESTATE_BODY, NULL); - break; - case MIMESTATE_BODY: - cleanup_encoder_state(&part->encstate); - mimesetstate(&part->state, MIMESTATE_CONTENT, NULL); - break; - case MIMESTATE_CONTENT: - if(part->encoder) - sz = read_encoded_part_content(part, buffer, bufsize, hasread); - else - sz = read_part_content(part, buffer, bufsize, hasread); - switch(sz) { - case 0: - mimesetstate(&part->state, MIMESTATE_END, NULL); - /* Try sparing open file descriptors. */ - if(part->kind == MIMEKIND_FILE && part->fp) { - fclose(part->fp); - part->fp = NULL; - } - /* FALLTHROUGH */ - case CURL_READFUNC_ABORT: - case CURL_READFUNC_PAUSE: - case READ_ERROR: - case STOP_FILLING: - return cursize? cursize: sz; - } - break; - case MIMESTATE_END: - return cursize; - default: - break; /* Other values not in part state. */ - } - - /* Bump buffer and counters according to read size. */ - cursize += sz; - buffer += sz; - bufsize -= sz; - } - - return cursize; -} - -/* Readback from mime. Warning: not a read callback function. */ -static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems, - void *instream, bool *hasread) -{ - curl_mime *mime = (curl_mime *) instream; - size_t cursize = 0; - (void) size; /* Always 1. */ - - while(nitems) { - size_t sz = 0; - curl_mimepart *part = mime->state.ptr; - switch(mime->state.state) { - case MIMESTATE_BEGIN: - case MIMESTATE_BODY: - mimesetstate(&mime->state, MIMESTATE_BOUNDARY1, mime->firstpart); - /* The first boundary always follows the header termination empty line, - so is always preceded by a CRLF. We can then spare 2 characters - by skipping the leading CRLF in boundary. */ - mime->state.offset += 2; - break; - case MIMESTATE_BOUNDARY1: - sz = readback_bytes(&mime->state, buffer, nitems, STRCONST("\r\n--"), - STRCONST("")); - if(!sz) - mimesetstate(&mime->state, MIMESTATE_BOUNDARY2, part); - break; - case MIMESTATE_BOUNDARY2: - if(part) - sz = readback_bytes(&mime->state, buffer, nitems, mime->boundary, - MIME_BOUNDARY_LEN, STRCONST("\r\n")); - else - sz = readback_bytes(&mime->state, buffer, nitems, mime->boundary, - MIME_BOUNDARY_LEN, STRCONST("--\r\n")); - if(!sz) { - mimesetstate(&mime->state, MIMESTATE_CONTENT, part); - } - break; - case MIMESTATE_CONTENT: - if(!part) { - mimesetstate(&mime->state, MIMESTATE_END, NULL); - break; - } - sz = readback_part(part, buffer, nitems, hasread); - switch(sz) { - case CURL_READFUNC_ABORT: - case CURL_READFUNC_PAUSE: - case READ_ERROR: - case STOP_FILLING: - return cursize? cursize: sz; - case 0: - mimesetstate(&mime->state, MIMESTATE_BOUNDARY1, part->nextpart); - break; - } - break; - case MIMESTATE_END: - return cursize; - default: - break; /* other values not used in mime state. */ - } - - /* Bump buffer and counters according to read size. */ - cursize += sz; - buffer += sz; - nitems -= sz; - } - - return cursize; -} - -static int mime_part_rewind(curl_mimepart *part) -{ - int res = CURL_SEEKFUNC_OK; - enum mimestate targetstate = MIMESTATE_BEGIN; - - if(part->flags & MIME_BODY_ONLY) - targetstate = MIMESTATE_BODY; - cleanup_encoder_state(&part->encstate); - if(part->state.state > targetstate) { - res = CURL_SEEKFUNC_CANTSEEK; - if(part->seekfunc) { - res = part->seekfunc(part->arg, (curl_off_t) 0, SEEK_SET); - switch(res) { - case CURL_SEEKFUNC_OK: - case CURL_SEEKFUNC_FAIL: - case CURL_SEEKFUNC_CANTSEEK: - break; - case -1: /* For fseek() error. */ - res = CURL_SEEKFUNC_CANTSEEK; - break; - default: - res = CURL_SEEKFUNC_FAIL; - break; - } - } - } - - if(res == CURL_SEEKFUNC_OK) - mimesetstate(&part->state, targetstate, NULL); - - part->lastreadstatus = 1; /* Successful read status. */ - return res; -} - -static int mime_subparts_seek(void *instream, curl_off_t offset, int whence) -{ - curl_mime *mime = (curl_mime *) instream; - curl_mimepart *part; - int result = CURL_SEEKFUNC_OK; - - if(whence != SEEK_SET || offset) - return CURL_SEEKFUNC_CANTSEEK; /* Only support full rewind. */ - - if(mime->state.state == MIMESTATE_BEGIN) - return CURL_SEEKFUNC_OK; /* Already rewound. */ - - for(part = mime->firstpart; part; part = part->nextpart) { - int res = mime_part_rewind(part); - if(res != CURL_SEEKFUNC_OK) - result = res; - } - - if(result == CURL_SEEKFUNC_OK) - mimesetstate(&mime->state, MIMESTATE_BEGIN, NULL); - - return result; -} - -/* Release part content. */ -static void cleanup_part_content(curl_mimepart *part) -{ - if(part->freefunc) - part->freefunc(part->arg); - - part->readfunc = NULL; - part->seekfunc = NULL; - part->freefunc = NULL; - part->arg = (void *) part; /* Defaults to part itself. */ - part->data = NULL; - part->fp = NULL; - part->datasize = (curl_off_t) 0; /* No size yet. */ - cleanup_encoder_state(&part->encstate); - part->kind = MIMEKIND_NONE; - part->flags &= ~MIME_FAST_READ; - part->lastreadstatus = 1; /* Successful read status. */ - part->state.state = MIMESTATE_BEGIN; -} - -static void mime_subparts_free(void *ptr) -{ - curl_mime *mime = (curl_mime *) ptr; - - if(mime && mime->parent) { - mime->parent->freefunc = NULL; /* Be sure we won't be called again. */ - cleanup_part_content(mime->parent); /* Avoid dangling pointer in part. */ - } - curl_mime_free(mime); -} - -/* Do not free subparts: unbind them. This is used for the top level only. */ -static void mime_subparts_unbind(void *ptr) -{ - curl_mime *mime = (curl_mime *) ptr; - - if(mime && mime->parent) { - mime->parent->freefunc = NULL; /* Be sure we won't be called again. */ - cleanup_part_content(mime->parent); /* Avoid dangling pointer in part. */ - mime->parent = NULL; - } -} - - -void Curl_mime_cleanpart(curl_mimepart *part) -{ - cleanup_part_content(part); - curl_slist_free_all(part->curlheaders); - if(part->flags & MIME_USERHEADERS_OWNER) - curl_slist_free_all(part->userheaders); - Curl_safefree(part->mimetype); - Curl_safefree(part->name); - Curl_safefree(part->filename); - Curl_mime_initpart(part, part->easy); -} - -/* Recursively delete a mime handle and its parts. */ -void curl_mime_free(curl_mime *mime) -{ - curl_mimepart *part; - - if(mime) { - mime_subparts_unbind(mime); /* Be sure it's not referenced anymore. */ - while(mime->firstpart) { - part = mime->firstpart; - mime->firstpart = part->nextpart; - Curl_mime_cleanpart(part); - free(part); - } - free(mime); - } -} - -CURLcode Curl_mime_duppart(curl_mimepart *dst, const curl_mimepart *src) -{ - curl_mime *mime; - curl_mimepart *d; - const curl_mimepart *s; - CURLcode res = CURLE_OK; - - DEBUGASSERT(dst); - - /* Duplicate content. */ - switch(src->kind) { - case MIMEKIND_NONE: - break; - case MIMEKIND_DATA: - res = curl_mime_data(dst, src->data, (size_t) src->datasize); - break; - case MIMEKIND_FILE: - res = curl_mime_filedata(dst, src->data); - /* Do not abort duplication if file is not readable. */ - if(res == CURLE_READ_ERROR) - res = CURLE_OK; - break; - case MIMEKIND_CALLBACK: - res = curl_mime_data_cb(dst, src->datasize, src->readfunc, - src->seekfunc, src->freefunc, src->arg); - break; - case MIMEKIND_MULTIPART: - /* No one knows about the cloned subparts, thus always attach ownership - to the part. */ - mime = curl_mime_init(dst->easy); - res = mime? curl_mime_subparts(dst, mime): CURLE_OUT_OF_MEMORY; - - /* Duplicate subparts. */ - for(s = ((curl_mime *) src->arg)->firstpart; !res && s; s = s->nextpart) { - d = curl_mime_addpart(mime); - res = d? Curl_mime_duppart(d, s): CURLE_OUT_OF_MEMORY; - } - break; - default: /* Invalid kind: should not occur. */ - res = CURLE_BAD_FUNCTION_ARGUMENT; /* Internal error? */ - break; - } - - /* Duplicate headers. */ - if(!res && src->userheaders) { - struct curl_slist *hdrs = Curl_slist_duplicate(src->userheaders); - - if(!hdrs) - res = CURLE_OUT_OF_MEMORY; - else { - /* No one but this procedure knows about the new header list, - so always take ownership. */ - res = curl_mime_headers(dst, hdrs, TRUE); - if(res) - curl_slist_free_all(hdrs); - } - } - - if(!res) { - /* Duplicate other fields. */ - dst->encoder = src->encoder; - res = curl_mime_type(dst, src->mimetype); - } - if(!res) - res = curl_mime_name(dst, src->name); - if(!res) - res = curl_mime_filename(dst, src->filename); - - /* If an error occurred, rollback. */ - if(res) - Curl_mime_cleanpart(dst); - - return res; -} - -/* - * Mime build functions. - */ - -/* Create a mime handle. */ -curl_mime *curl_mime_init(struct Curl_easy *easy) -{ - curl_mime *mime; - - mime = (curl_mime *) malloc(sizeof(*mime)); - - if(mime) { - mime->easy = easy; - mime->parent = NULL; - mime->firstpart = NULL; - mime->lastpart = NULL; - - memset(mime->boundary, '-', MIME_BOUNDARY_DASHES); - if(Curl_rand_hex(easy, - (unsigned char *) &mime->boundary[MIME_BOUNDARY_DASHES], - MIME_RAND_BOUNDARY_CHARS + 1)) { - /* failed to get random separator, bail out */ - free(mime); - return NULL; - } - mimesetstate(&mime->state, MIMESTATE_BEGIN, NULL); - } - - return mime; -} - -/* Initialize a mime part. */ -void Curl_mime_initpart(curl_mimepart *part, struct Curl_easy *easy) -{ - memset((char *) part, 0, sizeof(*part)); - part->easy = easy; - part->lastreadstatus = 1; /* Successful read status. */ - mimesetstate(&part->state, MIMESTATE_BEGIN, NULL); -} - -/* Create a mime part and append it to a mime handle's part list. */ -curl_mimepart *curl_mime_addpart(curl_mime *mime) -{ - curl_mimepart *part; - - if(!mime) - return NULL; - - part = (curl_mimepart *) malloc(sizeof(*part)); - - if(part) { - Curl_mime_initpart(part, mime->easy); - part->parent = mime; - - if(mime->lastpart) - mime->lastpart->nextpart = part; - else - mime->firstpart = part; - - mime->lastpart = part; - } - - return part; -} - -/* Set mime part name. */ -CURLcode curl_mime_name(curl_mimepart *part, const char *name) -{ - if(!part) - return CURLE_BAD_FUNCTION_ARGUMENT; - - Curl_safefree(part->name); - part->name = NULL; - - if(name) { - part->name = strdup(name); - if(!part->name) - return CURLE_OUT_OF_MEMORY; - } - - return CURLE_OK; -} - -/* Set mime part remote file name. */ -CURLcode curl_mime_filename(curl_mimepart *part, const char *filename) -{ - if(!part) - return CURLE_BAD_FUNCTION_ARGUMENT; - - Curl_safefree(part->filename); - part->filename = NULL; - - if(filename) { - part->filename = strdup(filename); - if(!part->filename) - return CURLE_OUT_OF_MEMORY; - } - - return CURLE_OK; -} - -/* Set mime part content from memory data. */ -CURLcode curl_mime_data(curl_mimepart *part, - const char *data, size_t datasize) -{ - if(!part) - return CURLE_BAD_FUNCTION_ARGUMENT; - - cleanup_part_content(part); - - if(data) { - if(datasize == CURL_ZERO_TERMINATED) - datasize = strlen(data); - - part->data = malloc(datasize + 1); - if(!part->data) - return CURLE_OUT_OF_MEMORY; - - part->datasize = datasize; - - if(datasize) - memcpy(part->data, data, datasize); - part->data[datasize] = '\0'; /* Set a null terminator as sentinel. */ - - part->readfunc = mime_mem_read; - part->seekfunc = mime_mem_seek; - part->freefunc = mime_mem_free; - part->flags |= MIME_FAST_READ; - part->kind = MIMEKIND_DATA; - } - - return CURLE_OK; -} - -/* Set mime part content from named local file. */ -CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename) -{ - CURLcode result = CURLE_OK; - - if(!part) - return CURLE_BAD_FUNCTION_ARGUMENT; - - cleanup_part_content(part); - - if(filename) { - char *base; - struct_stat sbuf; - - if(stat(filename, &sbuf) || access(filename, R_OK)) - result = CURLE_READ_ERROR; - - part->data = strdup(filename); - if(!part->data) - result = CURLE_OUT_OF_MEMORY; - - part->datasize = -1; - if(!result && S_ISREG(sbuf.st_mode)) { - part->datasize = filesize(filename, sbuf); - part->seekfunc = mime_file_seek; - } - - part->readfunc = mime_file_read; - part->freefunc = mime_file_free; - part->kind = MIMEKIND_FILE; - - /* As a side effect, set the filename to the current file's base name. - It is possible to withdraw this by explicitly calling - curl_mime_filename() with a NULL filename argument after the current - call. */ - base = strippath(filename); - if(!base) - result = CURLE_OUT_OF_MEMORY; - else { - CURLcode res = curl_mime_filename(part, base); - - if(res) - result = res; - free(base); - } - } - return result; -} - -/* Set mime part type. */ -CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype) -{ - if(!part) - return CURLE_BAD_FUNCTION_ARGUMENT; - - Curl_safefree(part->mimetype); - part->mimetype = NULL; - - if(mimetype) { - part->mimetype = strdup(mimetype); - if(!part->mimetype) - return CURLE_OUT_OF_MEMORY; - } - - return CURLE_OK; -} - -/* Set mime data transfer encoder. */ -CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding) -{ - CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT; - const struct mime_encoder *mep; - - if(!part) - return result; - - part->encoder = NULL; - - if(!encoding) - return CURLE_OK; /* Removing current encoder. */ - - for(mep = encoders; mep->name; mep++) - if(strcasecompare(encoding, mep->name)) { - part->encoder = mep; - result = CURLE_OK; - } - - return result; -} - -/* Set mime part headers. */ -CURLcode curl_mime_headers(curl_mimepart *part, - struct curl_slist *headers, int take_ownership) -{ - if(!part) - return CURLE_BAD_FUNCTION_ARGUMENT; - - if(part->flags & MIME_USERHEADERS_OWNER) { - if(part->userheaders != headers) /* Allow setting twice the same list. */ - curl_slist_free_all(part->userheaders); - part->flags &= ~MIME_USERHEADERS_OWNER; - } - part->userheaders = headers; - if(headers && take_ownership) - part->flags |= MIME_USERHEADERS_OWNER; - return CURLE_OK; -} - -/* Set mime part content from callback. */ -CURLcode curl_mime_data_cb(curl_mimepart *part, curl_off_t datasize, - curl_read_callback readfunc, - curl_seek_callback seekfunc, - curl_free_callback freefunc, void *arg) -{ - if(!part) - return CURLE_BAD_FUNCTION_ARGUMENT; - - cleanup_part_content(part); - - if(readfunc) { - part->readfunc = readfunc; - part->seekfunc = seekfunc; - part->freefunc = freefunc; - part->arg = arg; - part->datasize = datasize; - part->kind = MIMEKIND_CALLBACK; - } - - return CURLE_OK; -} - -/* Set mime part content from subparts. */ -CURLcode Curl_mime_set_subparts(curl_mimepart *part, - curl_mime *subparts, int take_ownership) -{ - curl_mime *root; - - if(!part) - return CURLE_BAD_FUNCTION_ARGUMENT; - - /* Accept setting twice the same subparts. */ - if(part->kind == MIMEKIND_MULTIPART && part->arg == subparts) - return CURLE_OK; - - cleanup_part_content(part); - - if(subparts) { - /* Must belong to the same data handle. */ - if(part->easy && subparts->easy && part->easy != subparts->easy) - return CURLE_BAD_FUNCTION_ARGUMENT; - - /* Should not have been attached already. */ - if(subparts->parent) - return CURLE_BAD_FUNCTION_ARGUMENT; - - /* Should not be the part's root. */ - root = part->parent; - if(root) { - while(root->parent && root->parent->parent) - root = root->parent->parent; - if(subparts == root) { - if(part->easy) - failf(part->easy, "Can't add itself as a subpart"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - } - - subparts->parent = part; - /* Subparts are processed internally: no read callback. */ - part->seekfunc = mime_subparts_seek; - part->freefunc = take_ownership? mime_subparts_free: mime_subparts_unbind; - part->arg = subparts; - part->datasize = -1; - part->kind = MIMEKIND_MULTIPART; - } - - return CURLE_OK; -} - -CURLcode curl_mime_subparts(curl_mimepart *part, curl_mime *subparts) -{ - return Curl_mime_set_subparts(part, subparts, TRUE); -} - - -/* Readback from top mime. */ -/* Argument is the dummy top part. */ -size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream) -{ - curl_mimepart *part = (curl_mimepart *) instream; - size_t ret; - bool hasread; - - (void) size; /* Always 1. */ - - do { - hasread = FALSE; - ret = readback_part(part, buffer, nitems, &hasread); - /* - * If this is not possible to get some data without calling more than - * one read callback (probably because a content encoder is not able to - * deliver a new bunch for the few data accumulated so far), force another - * read until we get enough data or a special exit code. - */ - } while(ret == STOP_FILLING); - - return ret; -} - -/* Rewind mime stream. */ -CURLcode Curl_mime_rewind(curl_mimepart *part) -{ - return mime_part_rewind(part) == CURL_SEEKFUNC_OK? - CURLE_OK: CURLE_SEND_FAIL_REWIND; -} - -/* Compute header list size. */ -static size_t slist_size(struct curl_slist *s, - size_t overhead, const char *skip, size_t skiplen) -{ - size_t size = 0; - - for(; s; s = s->next) - if(!skip || !match_header(s, skip, skiplen)) - size += strlen(s->data) + overhead; - return size; -} - -/* Get/compute multipart size. */ -static curl_off_t multipart_size(curl_mime *mime) -{ - curl_off_t size; - size_t boundarysize; - curl_mimepart *part; - - if(!mime) - return 0; /* Not present -> empty. */ - - boundarysize = 4 + MIME_BOUNDARY_LEN + 2; - size = boundarysize; /* Final boundary - CRLF after headers. */ - - for(part = mime->firstpart; part; part = part->nextpart) { - curl_off_t sz = Curl_mime_size(part); - - if(sz < 0) - size = sz; - - if(size >= 0) - size += boundarysize + sz; - } - - return size; -} - -/* Get/compute mime size. */ -curl_off_t Curl_mime_size(curl_mimepart *part) -{ - curl_off_t size; - - if(part->kind == MIMEKIND_MULTIPART) - part->datasize = multipart_size(part->arg); - - size = part->datasize; - - if(part->encoder) - size = part->encoder->sizefunc(part); - - if(size >= 0 && !(part->flags & MIME_BODY_ONLY)) { - /* Compute total part size. */ - size += slist_size(part->curlheaders, 2, NULL, 0); - size += slist_size(part->userheaders, 2, STRCONST("Content-Type")); - size += 2; /* CRLF after headers. */ - } - return size; -} - -/* Add a header. */ -/* VARARGS2 */ -CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...) -{ - struct curl_slist *hdr = NULL; - char *s = NULL; - va_list ap; - - va_start(ap, fmt); - s = curl_mvaprintf(fmt, ap); - va_end(ap); - - if(s) { - hdr = Curl_slist_append_nodup(*slp, s); - if(hdr) - *slp = hdr; - else - free(s); - } - - return hdr? CURLE_OK: CURLE_OUT_OF_MEMORY; -} - -/* Add a content type header. */ -static CURLcode add_content_type(struct curl_slist **slp, - const char *type, const char *boundary) -{ - return Curl_mime_add_header(slp, "Content-Type: %s%s%s", type, - boundary? "; boundary=": "", - boundary? boundary: ""); -} - -const char *Curl_mime_contenttype(const char *filename) -{ - /* - * If no content type was specified, we scan through a few well-known - * extensions and pick the first we match! - */ - struct ContentType { - const char *extension; - const char *type; - }; - static const struct ContentType ctts[] = { - {".gif", "image/gif"}, - {".jpg", "image/jpeg"}, - {".jpeg", "image/jpeg"}, - {".png", "image/png"}, - {".svg", "image/svg+xml"}, - {".txt", "text/plain"}, - {".htm", "text/html"}, - {".html", "text/html"}, - {".pdf", "application/pdf"}, - {".xml", "application/xml"} - }; - - if(filename) { - size_t len1 = strlen(filename); - const char *nameend = filename + len1; - unsigned int i; - - for(i = 0; i < sizeof(ctts) / sizeof(ctts[0]); i++) { - size_t len2 = strlen(ctts[i].extension); - - if(len1 >= len2 && strcasecompare(nameend - len2, ctts[i].extension)) - return ctts[i].type; - } - } - return NULL; -} - -static bool content_type_match(const char *contenttype, - const char *target, size_t len) -{ - if(contenttype && strncasecompare(contenttype, target, len)) - switch(contenttype[len]) { - case '\0': - case '\t': - case '\r': - case '\n': - case ' ': - case ';': - return TRUE; - } - return FALSE; -} - -CURLcode Curl_mime_prepare_headers(curl_mimepart *part, - const char *contenttype, - const char *disposition, - enum mimestrategy strategy) -{ - curl_mime *mime = NULL; - const char *boundary = NULL; - char *customct; - const char *cte = NULL; - CURLcode ret = CURLE_OK; - - /* Get rid of previously prepared headers. */ - curl_slist_free_all(part->curlheaders); - part->curlheaders = NULL; - - /* Be sure we won't access old headers later. */ - if(part->state.state == MIMESTATE_CURLHEADERS) - mimesetstate(&part->state, MIMESTATE_CURLHEADERS, NULL); - - /* Check if content type is specified. */ - customct = part->mimetype; - if(!customct) - customct = search_header(part->userheaders, STRCONST("Content-Type")); - if(customct) - contenttype = customct; - - /* If content type is not specified, try to determine it. */ - if(!contenttype) { - switch(part->kind) { - case MIMEKIND_MULTIPART: - contenttype = MULTIPART_CONTENTTYPE_DEFAULT; - break; - case MIMEKIND_FILE: - contenttype = Curl_mime_contenttype(part->filename); - if(!contenttype) - contenttype = Curl_mime_contenttype(part->data); - if(!contenttype && part->filename) - contenttype = FILE_CONTENTTYPE_DEFAULT; - break; - default: - contenttype = Curl_mime_contenttype(part->filename); - break; - } - } - - if(part->kind == MIMEKIND_MULTIPART) { - mime = (curl_mime *) part->arg; - if(mime) - boundary = mime->boundary; - } - else if(contenttype && !customct && - content_type_match(contenttype, STRCONST("text/plain"))) - if(strategy == MIMESTRATEGY_MAIL || !part->filename) - contenttype = NULL; - - /* Issue content-disposition header only if not already set by caller. */ - if(!search_header(part->userheaders, STRCONST("Content-Disposition"))) { - if(!disposition) - if(part->filename || part->name || - (contenttype && !strncasecompare(contenttype, "multipart/", 10))) - disposition = DISPOSITION_DEFAULT; - if(disposition && curl_strequal(disposition, "attachment") && - !part->name && !part->filename) - disposition = NULL; - if(disposition) { - char *name = NULL; - char *filename = NULL; - - if(part->name) { - name = escape_string(part->easy, part->name, strategy); - if(!name) - ret = CURLE_OUT_OF_MEMORY; - } - if(!ret && part->filename) { - filename = escape_string(part->easy, part->filename, strategy); - if(!filename) - ret = CURLE_OUT_OF_MEMORY; - } - if(!ret) - ret = Curl_mime_add_header(&part->curlheaders, - "Content-Disposition: %s%s%s%s%s%s%s", - disposition, - name? "; name=\"": "", - name? name: "", - name? "\"": "", - filename? "; filename=\"": "", - filename? filename: "", - filename? "\"": ""); - Curl_safefree(name); - Curl_safefree(filename); - if(ret) - return ret; - } - } - - /* Issue Content-Type header. */ - if(contenttype) { - ret = add_content_type(&part->curlheaders, contenttype, boundary); - if(ret) - return ret; - } - - /* Content-Transfer-Encoding header. */ - if(!search_header(part->userheaders, - STRCONST("Content-Transfer-Encoding"))) { - if(part->encoder) - cte = part->encoder->name; - else if(contenttype && strategy == MIMESTRATEGY_MAIL && - part->kind != MIMEKIND_MULTIPART) - cte = "8bit"; - if(cte) { - ret = Curl_mime_add_header(&part->curlheaders, - "Content-Transfer-Encoding: %s", cte); - if(ret) - return ret; - } - } - - /* If we were reading curl-generated headers, restart with new ones (this - should not occur). */ - if(part->state.state == MIMESTATE_CURLHEADERS) - mimesetstate(&part->state, MIMESTATE_CURLHEADERS, part->curlheaders); - - /* Process subparts. */ - if(part->kind == MIMEKIND_MULTIPART && mime) { - curl_mimepart *subpart; - - disposition = NULL; - if(content_type_match(contenttype, STRCONST("multipart/form-data"))) - disposition = "form-data"; - for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart) { - ret = Curl_mime_prepare_headers(subpart, NULL, disposition, strategy); - if(ret) - return ret; - } - } - return ret; -} - -/* Recursively reset paused status in the given part. */ -void Curl_mime_unpause(curl_mimepart *part) -{ - if(part) { - if(part->lastreadstatus == CURL_READFUNC_PAUSE) - part->lastreadstatus = 1; /* Successful read status. */ - if(part->kind == MIMEKIND_MULTIPART) { - curl_mime *mime = (curl_mime *) part->arg; - - if(mime) { - curl_mimepart *subpart; - - for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart) - Curl_mime_unpause(subpart); - } - } - } -} - - -#else /* !CURL_DISABLE_MIME && (!CURL_DISABLE_HTTP || - !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP) */ - -/* Mime not compiled in: define stubs for externally-referenced functions. */ -curl_mime *curl_mime_init(CURL *easy) -{ - (void) easy; - return NULL; -} - -void curl_mime_free(curl_mime *mime) -{ - (void) mime; -} - -curl_mimepart *curl_mime_addpart(curl_mime *mime) -{ - (void) mime; - return NULL; -} - -CURLcode curl_mime_name(curl_mimepart *part, const char *name) -{ - (void) part; - (void) name; - return CURLE_NOT_BUILT_IN; -} - -CURLcode curl_mime_filename(curl_mimepart *part, const char *filename) -{ - (void) part; - (void) filename; - return CURLE_NOT_BUILT_IN; -} - -CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype) -{ - (void) part; - (void) mimetype; - return CURLE_NOT_BUILT_IN; -} - -CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding) -{ - (void) part; - (void) encoding; - return CURLE_NOT_BUILT_IN; -} - -CURLcode curl_mime_data(curl_mimepart *part, - const char *data, size_t datasize) -{ - (void) part; - (void) data; - (void) datasize; - return CURLE_NOT_BUILT_IN; -} - -CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename) -{ - (void) part; - (void) filename; - return CURLE_NOT_BUILT_IN; -} - -CURLcode curl_mime_data_cb(curl_mimepart *part, - curl_off_t datasize, - curl_read_callback readfunc, - curl_seek_callback seekfunc, - curl_free_callback freefunc, - void *arg) -{ - (void) part; - (void) datasize; - (void) readfunc; - (void) seekfunc; - (void) freefunc; - (void) arg; - return CURLE_NOT_BUILT_IN; -} - -CURLcode curl_mime_subparts(curl_mimepart *part, curl_mime *subparts) -{ - (void) part; - (void) subparts; - return CURLE_NOT_BUILT_IN; -} - -CURLcode curl_mime_headers(curl_mimepart *part, - struct curl_slist *headers, int take_ownership) -{ - (void) part; - (void) headers; - (void) take_ownership; - return CURLE_NOT_BUILT_IN; -} - -CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...) -{ - (void)slp; - (void)fmt; - return CURLE_NOT_BUILT_IN; -} - -#endif /* if disabled */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "mime.h"
+#include "warnless.h"
+#include "urldata.h"
+#include "sendf.h"
+
+#if !defined(CURL_DISABLE_MIME) && (!defined(CURL_DISABLE_HTTP) || \
+ !defined(CURL_DISABLE_SMTP) || \
+ !defined(CURL_DISABLE_IMAP))
+
+#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
+#include <libgen.h>
+#endif
+
+#include "rand.h"
+#include "slist.h"
+#include "strcase.h"
+#include "dynbuf.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#ifdef WIN32
+# ifndef R_OK
+# define R_OK 4
+# endif
+#endif
+
+
+#define READ_ERROR ((size_t) -1)
+#define STOP_FILLING ((size_t) -2)
+
+static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems,
+ void *instream, bool *hasread);
+
+/* Encoders. */
+static size_t encoder_nop_read(char *buffer, size_t size, bool ateof,
+ curl_mimepart *part);
+static curl_off_t encoder_nop_size(curl_mimepart *part);
+static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof,
+ curl_mimepart *part);
+static size_t encoder_base64_read(char *buffer, size_t size, bool ateof,
+ curl_mimepart *part);
+static curl_off_t encoder_base64_size(curl_mimepart *part);
+static size_t encoder_qp_read(char *buffer, size_t size, bool ateof,
+ curl_mimepart *part);
+static curl_off_t encoder_qp_size(curl_mimepart *part);
+
+static const struct mime_encoder encoders[] = {
+ {"binary", encoder_nop_read, encoder_nop_size},
+ {"8bit", encoder_nop_read, encoder_nop_size},
+ {"7bit", encoder_7bit_read, encoder_nop_size},
+ {"base64", encoder_base64_read, encoder_base64_size},
+ {"quoted-printable", encoder_qp_read, encoder_qp_size},
+ {ZERO_NULL, ZERO_NULL, ZERO_NULL}
+};
+
+/* Base64 encoding table */
+static const char base64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/* Quoted-printable character class table.
+ *
+ * We cannot rely on ctype functions since quoted-printable input data
+ * is assumed to be ascii-compatible, even on non-ascii platforms. */
+#define QP_OK 1 /* Can be represented by itself. */
+#define QP_SP 2 /* Space or tab. */
+#define QP_CR 3 /* Carriage return. */
+#define QP_LF 4 /* Line-feed. */
+static const unsigned char qp_class[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 07 */
+ 0, QP_SP, QP_LF, 0, 0, QP_CR, 0, 0, /* 08 - 0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 17 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 18 - 1F */
+ QP_SP, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 20 - 27 */
+ QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 28 - 2F */
+ QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 30 - 37 */
+ QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, 0 , QP_OK, QP_OK, /* 38 - 3F */
+ QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 40 - 47 */
+ QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 48 - 4F */
+ QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 50 - 57 */
+ QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 58 - 5F */
+ QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 60 - 67 */
+ QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 68 - 6F */
+ QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, /* 70 - 77 */
+ QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, QP_OK, 0, /* 78 - 7F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */
+};
+
+
+/* Binary --> hexadecimal ASCII table. */
+static const char aschex[] =
+ "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x41\x42\x43\x44\x45\x46";
+
+
+
+#ifndef __VMS
+#define filesize(name, stat_data) (stat_data.st_size)
+#define fopen_read fopen
+
+#else
+
+#include <fabdef.h>
+/*
+ * get_vms_file_size does what it takes to get the real size of the file
+ *
+ * For fixed files, find out the size of the EOF block and adjust.
+ *
+ * For all others, have to read the entire file in, discarding the contents.
+ * Most posted text files will be small, and binary files like zlib archives
+ * and CD/DVD images should be either a STREAM_LF format or a fixed format.
+ *
+ */
+curl_off_t VmsRealFileSize(const char *name,
+ const struct_stat *stat_buf)
+{
+ char buffer[8192];
+ curl_off_t count;
+ int ret_stat;
+ FILE * file;
+
+ file = fopen(name, FOPEN_READTEXT); /* VMS */
+ if(!file)
+ return 0;
+
+ count = 0;
+ ret_stat = 1;
+ while(ret_stat > 0) {
+ ret_stat = fread(buffer, 1, sizeof(buffer), file);
+ if(ret_stat)
+ count += ret_stat;
+ }
+ fclose(file);
+
+ return count;
+}
+
+/*
+ *
+ * VmsSpecialSize checks to see if the stat st_size can be trusted and
+ * if not to call a routine to get the correct size.
+ *
+ */
+static curl_off_t VmsSpecialSize(const char *name,
+ const struct_stat *stat_buf)
+{
+ switch(stat_buf->st_fab_rfm) {
+ case FAB$C_VAR:
+ case FAB$C_VFC:
+ return VmsRealFileSize(name, stat_buf);
+ break;
+ default:
+ return stat_buf->st_size;
+ }
+}
+
+#define filesize(name, stat_data) VmsSpecialSize(name, &stat_data)
+
+/*
+ * vmsfopenread
+ *
+ * For upload to work as expected on VMS, different optional
+ * parameters must be added to the fopen command based on
+ * record format of the file.
+ *
+ */
+static FILE * vmsfopenread(const char *file, const char *mode)
+{
+ struct_stat statbuf;
+ int result;
+
+ result = stat(file, &statbuf);
+
+ switch(statbuf.st_fab_rfm) {
+ case FAB$C_VAR:
+ case FAB$C_VFC:
+ case FAB$C_STMCR:
+ return fopen(file, FOPEN_READTEXT); /* VMS */
+ break;
+ default:
+ return fopen(file, FOPEN_READTEXT, "rfm=stmlf", "ctx=stm");
+ }
+}
+
+#define fopen_read vmsfopenread
+#endif
+
+
+#ifndef HAVE_BASENAME
+/*
+ (Quote from The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004
+ Edition)
+
+ The basename() function shall take the pathname pointed to by path and
+ return a pointer to the final component of the pathname, deleting any
+ trailing '/' characters.
+
+ If the string pointed to by path consists entirely of the '/' character,
+ basename() shall return a pointer to the string "/". If the string pointed
+ to by path is exactly "//", it is implementation-defined whether '/' or "//"
+ is returned.
+
+ If path is a null pointer or points to an empty string, basename() shall
+ return a pointer to the string ".".
+
+ The basename() function may modify the string pointed to by path, and may
+ return a pointer to static storage that may then be overwritten by a
+ subsequent call to basename().
+
+ The basename() function need not be reentrant. A function that is not
+ required to be reentrant is not required to be thread-safe.
+
+*/
+static char *Curl_basename(char *path)
+{
+ /* Ignore all the details above for now and make a quick and simple
+ implementation here */
+ char *s1;
+ char *s2;
+
+ s1 = strrchr(path, '/');
+ s2 = strrchr(path, '\\');
+
+ if(s1 && s2) {
+ path = (s1 > s2? s1 : s2) + 1;
+ }
+ else if(s1)
+ path = s1 + 1;
+ else if(s2)
+ path = s2 + 1;
+
+ return path;
+}
+
+#define basename(x) Curl_basename((x))
+#endif
+
+
+/* Set readback state. */
+static void mimesetstate(struct mime_state *state,
+ enum mimestate tok, void *ptr)
+{
+ state->state = tok;
+ state->ptr = ptr;
+ state->offset = 0;
+}
+
+
+/* Escape header string into allocated memory. */
+static char *escape_string(struct Curl_easy *data,
+ const char *src, enum mimestrategy strategy)
+{
+ CURLcode result;
+ struct dynbuf db;
+ const char * const *table;
+ const char * const *p;
+ /* replace first character by rest of string. */
+ static const char * const mimetable[] = {
+ "\\\\\\",
+ "\"\\\"",
+ NULL
+ };
+ /* WHATWG HTML living standard 4.10.21.8 2 specifies:
+ For field names and filenames for file fields, the result of the
+ encoding in the previous bullet point must be escaped by replacing
+ any 0x0A (LF) bytes with the byte sequence `%0A`, 0x0D (CR) with `%0D`
+ and 0x22 (") with `%22`.
+ The user agent must not perform any other escapes. */
+ static const char * const formtable[] = {
+ "\"%22",
+ "\r%0D",
+ "\n%0A",
+ NULL
+ };
+
+ table = formtable;
+ /* data can be NULL when this function is called indirectly from
+ curl_formget(). */
+ if(strategy == MIMESTRATEGY_MAIL ||
+ (data && (data->set.mime_options & CURLMIMEOPT_FORMESCAPE)))
+ table = mimetable;
+
+ Curl_dyn_init(&db, CURL_MAX_INPUT_LENGTH);
+
+ for(result = Curl_dyn_addn(&db, STRCONST("")); !result && *src; src++) {
+ for(p = table; *p && **p != *src; p++)
+ ;
+
+ if(*p)
+ result = Curl_dyn_add(&db, *p + 1);
+ else
+ result = Curl_dyn_addn(&db, src, 1);
+ }
+
+ return Curl_dyn_ptr(&db);
+}
+
+/* Check if header matches. */
+static char *match_header(struct curl_slist *hdr, const char *lbl, size_t len)
+{
+ char *value = NULL;
+
+ if(strncasecompare(hdr->data, lbl, len) && hdr->data[len] == ':')
+ for(value = hdr->data + len + 1; *value == ' '; value++)
+ ;
+ return value;
+}
+
+/* Get a header from an slist. */
+static char *search_header(struct curl_slist *hdrlist,
+ const char *hdr, size_t len)
+{
+ char *value = NULL;
+
+ for(; !value && hdrlist; hdrlist = hdrlist->next)
+ value = match_header(hdrlist, hdr, len);
+
+ return value;
+}
+
+static char *strippath(const char *fullfile)
+{
+ char *filename;
+ char *base;
+ filename = strdup(fullfile); /* duplicate since basename() may ruin the
+ buffer it works on */
+ if(!filename)
+ return NULL;
+ base = strdup(basename(filename));
+
+ free(filename); /* free temporary buffer */
+
+ return base; /* returns an allocated string or NULL ! */
+}
+
+/* Initialize data encoder state. */
+static void cleanup_encoder_state(struct mime_encoder_state *p)
+{
+ p->pos = 0;
+ p->bufbeg = 0;
+ p->bufend = 0;
+}
+
+
+/* Dummy encoder. This is used for 8bit and binary content encodings. */
+static size_t encoder_nop_read(char *buffer, size_t size, bool ateof,
+ struct curl_mimepart *part)
+{
+ struct mime_encoder_state *st = &part->encstate;
+ size_t insize = st->bufend - st->bufbeg;
+
+ (void) ateof;
+
+ if(!size)
+ return STOP_FILLING;
+
+ if(size > insize)
+ size = insize;
+
+ if(size)
+ memcpy(buffer, st->buf + st->bufbeg, size);
+
+ st->bufbeg += size;
+ return size;
+}
+
+static curl_off_t encoder_nop_size(curl_mimepart *part)
+{
+ return part->datasize;
+}
+
+
+/* 7bit encoder: the encoder is just a data validity check. */
+static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof,
+ curl_mimepart *part)
+{
+ struct mime_encoder_state *st = &part->encstate;
+ size_t cursize = st->bufend - st->bufbeg;
+
+ (void) ateof;
+
+ if(!size)
+ return STOP_FILLING;
+
+ if(size > cursize)
+ size = cursize;
+
+ for(cursize = 0; cursize < size; cursize++) {
+ *buffer = st->buf[st->bufbeg];
+ if(*buffer++ & 0x80)
+ return cursize? cursize: READ_ERROR;
+ st->bufbeg++;
+ }
+
+ return cursize;
+}
+
+
+/* Base64 content encoder. */
+static size_t encoder_base64_read(char *buffer, size_t size, bool ateof,
+ curl_mimepart *part)
+{
+ struct mime_encoder_state *st = &part->encstate;
+ size_t cursize = 0;
+ int i;
+ char *ptr = buffer;
+
+ while(st->bufbeg < st->bufend) {
+ /* Line full ? */
+ if(st->pos > MAX_ENCODED_LINE_LENGTH - 4) {
+ /* Yes, we need 2 characters for CRLF. */
+ if(size < 2) {
+ if(!cursize)
+ return STOP_FILLING;
+ break;
+ }
+ *ptr++ = '\r';
+ *ptr++ = '\n';
+ st->pos = 0;
+ cursize += 2;
+ size -= 2;
+ }
+
+ /* Be sure there is enough space and input data for a base64 group. */
+ if(size < 4) {
+ if(!cursize)
+ return STOP_FILLING;
+ break;
+ }
+ if(st->bufend - st->bufbeg < 3)
+ break;
+
+ /* Encode three bytes as four characters. */
+ i = st->buf[st->bufbeg++] & 0xFF;
+ i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF);
+ i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF);
+ *ptr++ = base64[(i >> 18) & 0x3F];
+ *ptr++ = base64[(i >> 12) & 0x3F];
+ *ptr++ = base64[(i >> 6) & 0x3F];
+ *ptr++ = base64[i & 0x3F];
+ cursize += 4;
+ st->pos += 4;
+ size -= 4;
+ }
+
+ /* If at eof, we have to flush the buffered data. */
+ if(ateof) {
+ if(size < 4) {
+ if(!cursize)
+ return STOP_FILLING;
+ }
+ else {
+ /* Buffered data size can only be 0, 1 or 2. */
+ ptr[2] = ptr[3] = '=';
+ i = 0;
+
+ /* If there is buffered data */
+ if(st->bufend != st->bufbeg) {
+
+ if(st->bufend - st->bufbeg == 2)
+ i = (st->buf[st->bufbeg + 1] & 0xFF) << 8;
+
+ i |= (st->buf[st->bufbeg] & 0xFF) << 16;
+ ptr[0] = base64[(i >> 18) & 0x3F];
+ ptr[1] = base64[(i >> 12) & 0x3F];
+ if(++st->bufbeg != st->bufend) {
+ ptr[2] = base64[(i >> 6) & 0x3F];
+ st->bufbeg++;
+ }
+ cursize += 4;
+ st->pos += 4;
+ }
+ }
+ }
+
+ return cursize;
+}
+
+static curl_off_t encoder_base64_size(curl_mimepart *part)
+{
+ curl_off_t size = part->datasize;
+
+ if(size <= 0)
+ return size; /* Unknown size or no data. */
+
+ /* Compute base64 character count. */
+ size = 4 * (1 + (size - 1) / 3);
+
+ /* Effective character count must include CRLFs. */
+ return size + 2 * ((size - 1) / MAX_ENCODED_LINE_LENGTH);
+}
+
+
+/* Quoted-printable lookahead.
+ *
+ * Check if a CRLF or end of data is in input buffer at current position + n.
+ * Return -1 if more data needed, 1 if CRLF or end of data, else 0.
+ */
+static int qp_lookahead_eol(struct mime_encoder_state *st, int ateof, size_t n)
+{
+ n += st->bufbeg;
+ if(n >= st->bufend && ateof)
+ return 1;
+ if(n + 2 > st->bufend)
+ return ateof? 0: -1;
+ if(qp_class[st->buf[n] & 0xFF] == QP_CR &&
+ qp_class[st->buf[n + 1] & 0xFF] == QP_LF)
+ return 1;
+ return 0;
+}
+
+/* Quoted-printable encoder. */
+static size_t encoder_qp_read(char *buffer, size_t size, bool ateof,
+ curl_mimepart *part)
+{
+ struct mime_encoder_state *st = &part->encstate;
+ char *ptr = buffer;
+ size_t cursize = 0;
+ int softlinebreak;
+ char buf[4];
+
+ /* On all platforms, input is supposed to be ASCII compatible: for this
+ reason, we use hexadecimal ASCII codes in this function rather than
+ character constants that can be interpreted as non-ascii on some
+ platforms. Preserve ASCII encoding on output too. */
+ while(st->bufbeg < st->bufend) {
+ size_t len = 1;
+ size_t consumed = 1;
+ int i = st->buf[st->bufbeg];
+ buf[0] = (char) i;
+ buf[1] = aschex[(i >> 4) & 0xF];
+ buf[2] = aschex[i & 0xF];
+
+ switch(qp_class[st->buf[st->bufbeg] & 0xFF]) {
+ case QP_OK: /* Not a special character. */
+ break;
+ case QP_SP: /* Space or tab. */
+ /* Spacing must be escaped if followed by CRLF. */
+ switch(qp_lookahead_eol(st, ateof, 1)) {
+ case -1: /* More input data needed. */
+ return cursize;
+ case 0: /* No encoding needed. */
+ break;
+ default: /* CRLF after space or tab. */
+ buf[0] = '\x3D'; /* '=' */
+ len = 3;
+ break;
+ }
+ break;
+ case QP_CR: /* Carriage return. */
+ /* If followed by a line-feed, output the CRLF pair.
+ Else escape it. */
+ switch(qp_lookahead_eol(st, ateof, 0)) {
+ case -1: /* Need more data. */
+ return cursize;
+ case 1: /* CRLF found. */
+ buf[len++] = '\x0A'; /* Append '\n'. */
+ consumed = 2;
+ break;
+ default: /* Not followed by LF: escape. */
+ buf[0] = '\x3D'; /* '=' */
+ len = 3;
+ break;
+ }
+ break;
+ default: /* Character must be escaped. */
+ buf[0] = '\x3D'; /* '=' */
+ len = 3;
+ break;
+ }
+
+ /* Be sure the encoded character fits within maximum line length. */
+ if(buf[len - 1] != '\x0A') { /* '\n' */
+ softlinebreak = st->pos + len > MAX_ENCODED_LINE_LENGTH;
+ if(!softlinebreak && st->pos + len == MAX_ENCODED_LINE_LENGTH) {
+ /* We may use the current line only if end of data or followed by
+ a CRLF. */
+ switch(qp_lookahead_eol(st, ateof, consumed)) {
+ case -1: /* Need more data. */
+ return cursize;
+ case 0: /* Not followed by a CRLF. */
+ softlinebreak = 1;
+ break;
+ }
+ }
+ if(softlinebreak) {
+ strcpy(buf, "\x3D\x0D\x0A"); /* "=\r\n" */
+ len = 3;
+ consumed = 0;
+ }
+ }
+
+ /* If the output buffer would overflow, do not store. */
+ if(len > size) {
+ if(!cursize)
+ return STOP_FILLING;
+ break;
+ }
+
+ /* Append to output buffer. */
+ memcpy(ptr, buf, len);
+ cursize += len;
+ ptr += len;
+ size -= len;
+ st->pos += len;
+ if(buf[len - 1] == '\x0A') /* '\n' */
+ st->pos = 0;
+ st->bufbeg += consumed;
+ }
+
+ return cursize;
+}
+
+static curl_off_t encoder_qp_size(curl_mimepart *part)
+{
+ /* Determining the size can only be done by reading the data: unless the
+ data size is 0, we return it as unknown (-1). */
+ return part->datasize? -1: 0;
+}
+
+
+/* In-memory data callbacks. */
+/* Argument is a pointer to the mime part. */
+static size_t mime_mem_read(char *buffer, size_t size, size_t nitems,
+ void *instream)
+{
+ curl_mimepart *part = (curl_mimepart *) instream;
+ size_t sz = curlx_sotouz(part->datasize - part->state.offset);
+ (void) size; /* Always 1.*/
+
+ if(!nitems)
+ return STOP_FILLING;
+
+ if(sz > nitems)
+ sz = nitems;
+
+ if(sz)
+ memcpy(buffer, part->data + curlx_sotouz(part->state.offset), sz);
+
+ return sz;
+}
+
+static int mime_mem_seek(void *instream, curl_off_t offset, int whence)
+{
+ curl_mimepart *part = (curl_mimepart *) instream;
+
+ switch(whence) {
+ case SEEK_CUR:
+ offset += part->state.offset;
+ break;
+ case SEEK_END:
+ offset += part->datasize;
+ break;
+ }
+
+ if(offset < 0 || offset > part->datasize)
+ return CURL_SEEKFUNC_FAIL;
+
+ part->state.offset = offset;
+ return CURL_SEEKFUNC_OK;
+}
+
+static void mime_mem_free(void *ptr)
+{
+ Curl_safefree(((curl_mimepart *) ptr)->data);
+}
+
+
+/* Named file callbacks. */
+/* Argument is a pointer to the mime part. */
+static int mime_open_file(curl_mimepart *part)
+{
+ /* Open a MIMEKIND_FILE part. */
+
+ if(part->fp)
+ return 0;
+ part->fp = fopen_read(part->data, "rb");
+ return part->fp? 0: -1;
+}
+
+static size_t mime_file_read(char *buffer, size_t size, size_t nitems,
+ void *instream)
+{
+ curl_mimepart *part = (curl_mimepart *) instream;
+
+ if(!nitems)
+ return STOP_FILLING;
+
+ if(mime_open_file(part))
+ return READ_ERROR;
+
+ return fread(buffer, size, nitems, part->fp);
+}
+
+static int mime_file_seek(void *instream, curl_off_t offset, int whence)
+{
+ curl_mimepart *part = (curl_mimepart *) instream;
+
+ if(whence == SEEK_SET && !offset && !part->fp)
+ return CURL_SEEKFUNC_OK; /* Not open: implicitly already at BOF. */
+
+ if(mime_open_file(part))
+ return CURL_SEEKFUNC_FAIL;
+
+ return fseek(part->fp, (long) offset, whence)?
+ CURL_SEEKFUNC_CANTSEEK: CURL_SEEKFUNC_OK;
+}
+
+static void mime_file_free(void *ptr)
+{
+ curl_mimepart *part = (curl_mimepart *) ptr;
+
+ if(part->fp) {
+ fclose(part->fp);
+ part->fp = NULL;
+ }
+ Curl_safefree(part->data);
+ part->data = NULL;
+}
+
+
+/* Subparts callbacks. */
+/* Argument is a pointer to the mime structure. */
+
+/* Readback a byte string segment. */
+static size_t readback_bytes(struct mime_state *state,
+ char *buffer, size_t bufsize,
+ const char *bytes, size_t numbytes,
+ const char *trail, size_t traillen)
+{
+ size_t sz;
+ size_t offset = curlx_sotouz(state->offset);
+
+ if(numbytes > offset) {
+ sz = numbytes - offset;
+ bytes += offset;
+ }
+ else {
+ sz = offset - numbytes;
+ if(sz >= traillen)
+ return 0;
+ bytes = trail + sz;
+ sz = traillen - sz;
+ }
+
+ if(sz > bufsize)
+ sz = bufsize;
+
+ memcpy(buffer, bytes, sz);
+ state->offset += sz;
+ return sz;
+}
+
+/* Read a non-encoded part content. */
+static size_t read_part_content(curl_mimepart *part,
+ char *buffer, size_t bufsize, bool *hasread)
+{
+ size_t sz = 0;
+
+ switch(part->lastreadstatus) {
+ case 0:
+ case CURL_READFUNC_ABORT:
+ case CURL_READFUNC_PAUSE:
+ case READ_ERROR:
+ return part->lastreadstatus;
+ default:
+ break;
+ }
+
+ /* If we can determine we are at end of part data, spare a read. */
+ if(part->datasize != (curl_off_t) -1 &&
+ part->state.offset >= part->datasize) {
+ /* sz is already zero. */
+ }
+ else {
+ switch(part->kind) {
+ case MIMEKIND_MULTIPART:
+ /*
+ * Cannot be processed as other kinds since read function requires
+ * an additional parameter and is highly recursive.
+ */
+ sz = mime_subparts_read(buffer, 1, bufsize, part->arg, hasread);
+ break;
+ case MIMEKIND_FILE:
+ if(part->fp && feof(part->fp))
+ break; /* At EOF. */
+ /* FALLTHROUGH */
+ default:
+ if(part->readfunc) {
+ if(!(part->flags & MIME_FAST_READ)) {
+ if(*hasread)
+ return STOP_FILLING;
+ *hasread = TRUE;
+ }
+ sz = part->readfunc(buffer, 1, bufsize, part->arg);
+ }
+ break;
+ }
+ }
+
+ switch(sz) {
+ case STOP_FILLING:
+ break;
+ case 0:
+ case CURL_READFUNC_ABORT:
+ case CURL_READFUNC_PAUSE:
+ case READ_ERROR:
+ part->lastreadstatus = sz;
+ break;
+ default:
+ part->state.offset += sz;
+ part->lastreadstatus = sz;
+ break;
+ }
+
+ return sz;
+}
+
+/* Read and encode part content. */
+static size_t read_encoded_part_content(curl_mimepart *part, char *buffer,
+ size_t bufsize, bool *hasread)
+{
+ struct mime_encoder_state *st = &part->encstate;
+ size_t cursize = 0;
+ size_t sz;
+ bool ateof = FALSE;
+
+ for(;;) {
+ if(st->bufbeg < st->bufend || ateof) {
+ /* Encode buffered data. */
+ sz = part->encoder->encodefunc(buffer, bufsize, ateof, part);
+ switch(sz) {
+ case 0:
+ if(ateof)
+ return cursize;
+ break;
+ case READ_ERROR:
+ case STOP_FILLING:
+ return cursize? cursize: sz;
+ default:
+ cursize += sz;
+ buffer += sz;
+ bufsize -= sz;
+ continue;
+ }
+ }
+
+ /* We need more data in input buffer. */
+ if(st->bufbeg) {
+ size_t len = st->bufend - st->bufbeg;
+
+ if(len)
+ memmove(st->buf, st->buf + st->bufbeg, len);
+ st->bufbeg = 0;
+ st->bufend = len;
+ }
+ if(st->bufend >= sizeof(st->buf))
+ return cursize? cursize: READ_ERROR; /* Buffer full. */
+ sz = read_part_content(part, st->buf + st->bufend,
+ sizeof(st->buf) - st->bufend, hasread);
+ switch(sz) {
+ case 0:
+ ateof = TRUE;
+ break;
+ case CURL_READFUNC_ABORT:
+ case CURL_READFUNC_PAUSE:
+ case READ_ERROR:
+ case STOP_FILLING:
+ return cursize? cursize: sz;
+ default:
+ st->bufend += sz;
+ break;
+ }
+ }
+
+ /* NOTREACHED */
+}
+
+/* Readback a mime part. */
+static size_t readback_part(curl_mimepart *part,
+ char *buffer, size_t bufsize, bool *hasread)
+{
+ size_t cursize = 0;
+
+ /* Readback from part. */
+
+ while(bufsize) {
+ size_t sz = 0;
+ struct curl_slist *hdr = (struct curl_slist *) part->state.ptr;
+ switch(part->state.state) {
+ case MIMESTATE_BEGIN:
+ mimesetstate(&part->state,
+ (part->flags & MIME_BODY_ONLY)?
+ MIMESTATE_BODY: MIMESTATE_CURLHEADERS,
+ part->curlheaders);
+ break;
+ case MIMESTATE_USERHEADERS:
+ if(!hdr) {
+ mimesetstate(&part->state, MIMESTATE_EOH, NULL);
+ break;
+ }
+ if(match_header(hdr, "Content-Type", 12)) {
+ mimesetstate(&part->state, MIMESTATE_USERHEADERS, hdr->next);
+ break;
+ }
+ /* FALLTHROUGH */
+ case MIMESTATE_CURLHEADERS:
+ if(!hdr)
+ mimesetstate(&part->state, MIMESTATE_USERHEADERS, part->userheaders);
+ else {
+ sz = readback_bytes(&part->state, buffer, bufsize,
+ hdr->data, strlen(hdr->data), STRCONST("\r\n"));
+ if(!sz)
+ mimesetstate(&part->state, part->state.state, hdr->next);
+ }
+ break;
+ case MIMESTATE_EOH:
+ sz = readback_bytes(&part->state, buffer, bufsize, STRCONST("\r\n"),
+ STRCONST(""));
+ if(!sz)
+ mimesetstate(&part->state, MIMESTATE_BODY, NULL);
+ break;
+ case MIMESTATE_BODY:
+ cleanup_encoder_state(&part->encstate);
+ mimesetstate(&part->state, MIMESTATE_CONTENT, NULL);
+ break;
+ case MIMESTATE_CONTENT:
+ if(part->encoder)
+ sz = read_encoded_part_content(part, buffer, bufsize, hasread);
+ else
+ sz = read_part_content(part, buffer, bufsize, hasread);
+ switch(sz) {
+ case 0:
+ mimesetstate(&part->state, MIMESTATE_END, NULL);
+ /* Try sparing open file descriptors. */
+ if(part->kind == MIMEKIND_FILE && part->fp) {
+ fclose(part->fp);
+ part->fp = NULL;
+ }
+ /* FALLTHROUGH */
+ case CURL_READFUNC_ABORT:
+ case CURL_READFUNC_PAUSE:
+ case READ_ERROR:
+ case STOP_FILLING:
+ return cursize? cursize: sz;
+ }
+ break;
+ case MIMESTATE_END:
+ return cursize;
+ default:
+ break; /* Other values not in part state. */
+ }
+
+ /* Bump buffer and counters according to read size. */
+ cursize += sz;
+ buffer += sz;
+ bufsize -= sz;
+ }
+
+ return cursize;
+}
+
+/* Readback from mime. Warning: not a read callback function. */
+static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems,
+ void *instream, bool *hasread)
+{
+ curl_mime *mime = (curl_mime *) instream;
+ size_t cursize = 0;
+ (void) size; /* Always 1. */
+
+ while(nitems) {
+ size_t sz = 0;
+ curl_mimepart *part = mime->state.ptr;
+ switch(mime->state.state) {
+ case MIMESTATE_BEGIN:
+ case MIMESTATE_BODY:
+ mimesetstate(&mime->state, MIMESTATE_BOUNDARY1, mime->firstpart);
+ /* The first boundary always follows the header termination empty line,
+ so is always preceded by a CRLF. We can then spare 2 characters
+ by skipping the leading CRLF in boundary. */
+ mime->state.offset += 2;
+ break;
+ case MIMESTATE_BOUNDARY1:
+ sz = readback_bytes(&mime->state, buffer, nitems, STRCONST("\r\n--"),
+ STRCONST(""));
+ if(!sz)
+ mimesetstate(&mime->state, MIMESTATE_BOUNDARY2, part);
+ break;
+ case MIMESTATE_BOUNDARY2:
+ if(part)
+ sz = readback_bytes(&mime->state, buffer, nitems, mime->boundary,
+ MIME_BOUNDARY_LEN, STRCONST("\r\n"));
+ else
+ sz = readback_bytes(&mime->state, buffer, nitems, mime->boundary,
+ MIME_BOUNDARY_LEN, STRCONST("--\r\n"));
+ if(!sz) {
+ mimesetstate(&mime->state, MIMESTATE_CONTENT, part);
+ }
+ break;
+ case MIMESTATE_CONTENT:
+ if(!part) {
+ mimesetstate(&mime->state, MIMESTATE_END, NULL);
+ break;
+ }
+ sz = readback_part(part, buffer, nitems, hasread);
+ switch(sz) {
+ case CURL_READFUNC_ABORT:
+ case CURL_READFUNC_PAUSE:
+ case READ_ERROR:
+ case STOP_FILLING:
+ return cursize? cursize: sz;
+ case 0:
+ mimesetstate(&mime->state, MIMESTATE_BOUNDARY1, part->nextpart);
+ break;
+ }
+ break;
+ case MIMESTATE_END:
+ return cursize;
+ default:
+ break; /* other values not used in mime state. */
+ }
+
+ /* Bump buffer and counters according to read size. */
+ cursize += sz;
+ buffer += sz;
+ nitems -= sz;
+ }
+
+ return cursize;
+}
+
+static int mime_part_rewind(curl_mimepart *part)
+{
+ int res = CURL_SEEKFUNC_OK;
+ enum mimestate targetstate = MIMESTATE_BEGIN;
+
+ if(part->flags & MIME_BODY_ONLY)
+ targetstate = MIMESTATE_BODY;
+ cleanup_encoder_state(&part->encstate);
+ if(part->state.state > targetstate) {
+ res = CURL_SEEKFUNC_CANTSEEK;
+ if(part->seekfunc) {
+ res = part->seekfunc(part->arg, (curl_off_t) 0, SEEK_SET);
+ switch(res) {
+ case CURL_SEEKFUNC_OK:
+ case CURL_SEEKFUNC_FAIL:
+ case CURL_SEEKFUNC_CANTSEEK:
+ break;
+ case -1: /* For fseek() error. */
+ res = CURL_SEEKFUNC_CANTSEEK;
+ break;
+ default:
+ res = CURL_SEEKFUNC_FAIL;
+ break;
+ }
+ }
+ }
+
+ if(res == CURL_SEEKFUNC_OK)
+ mimesetstate(&part->state, targetstate, NULL);
+
+ part->lastreadstatus = 1; /* Successful read status. */
+ return res;
+}
+
+static int mime_subparts_seek(void *instream, curl_off_t offset, int whence)
+{
+ curl_mime *mime = (curl_mime *) instream;
+ curl_mimepart *part;
+ int result = CURL_SEEKFUNC_OK;
+
+ if(whence != SEEK_SET || offset)
+ return CURL_SEEKFUNC_CANTSEEK; /* Only support full rewind. */
+
+ if(mime->state.state == MIMESTATE_BEGIN)
+ return CURL_SEEKFUNC_OK; /* Already rewound. */
+
+ for(part = mime->firstpart; part; part = part->nextpart) {
+ int res = mime_part_rewind(part);
+ if(res != CURL_SEEKFUNC_OK)
+ result = res;
+ }
+
+ if(result == CURL_SEEKFUNC_OK)
+ mimesetstate(&mime->state, MIMESTATE_BEGIN, NULL);
+
+ return result;
+}
+
+/* Release part content. */
+static void cleanup_part_content(curl_mimepart *part)
+{
+ if(part->freefunc)
+ part->freefunc(part->arg);
+
+ part->readfunc = NULL;
+ part->seekfunc = NULL;
+ part->freefunc = NULL;
+ part->arg = (void *) part; /* Defaults to part itself. */
+ part->data = NULL;
+ part->fp = NULL;
+ part->datasize = (curl_off_t) 0; /* No size yet. */
+ cleanup_encoder_state(&part->encstate);
+ part->kind = MIMEKIND_NONE;
+ part->flags &= ~MIME_FAST_READ;
+ part->lastreadstatus = 1; /* Successful read status. */
+ part->state.state = MIMESTATE_BEGIN;
+}
+
+static void mime_subparts_free(void *ptr)
+{
+ curl_mime *mime = (curl_mime *) ptr;
+
+ if(mime && mime->parent) {
+ mime->parent->freefunc = NULL; /* Be sure we won't be called again. */
+ cleanup_part_content(mime->parent); /* Avoid dangling pointer in part. */
+ }
+ curl_mime_free(mime);
+}
+
+/* Do not free subparts: unbind them. This is used for the top level only. */
+static void mime_subparts_unbind(void *ptr)
+{
+ curl_mime *mime = (curl_mime *) ptr;
+
+ if(mime && mime->parent) {
+ mime->parent->freefunc = NULL; /* Be sure we won't be called again. */
+ cleanup_part_content(mime->parent); /* Avoid dangling pointer in part. */
+ mime->parent = NULL;
+ }
+}
+
+
+void Curl_mime_cleanpart(curl_mimepart *part)
+{
+ cleanup_part_content(part);
+ curl_slist_free_all(part->curlheaders);
+ if(part->flags & MIME_USERHEADERS_OWNER)
+ curl_slist_free_all(part->userheaders);
+ Curl_safefree(part->mimetype);
+ Curl_safefree(part->name);
+ Curl_safefree(part->filename);
+ Curl_mime_initpart(part);
+}
+
+/* Recursively delete a mime handle and its parts. */
+void curl_mime_free(curl_mime *mime)
+{
+ curl_mimepart *part;
+
+ if(mime) {
+ mime_subparts_unbind(mime); /* Be sure it's not referenced anymore. */
+ while(mime->firstpart) {
+ part = mime->firstpart;
+ mime->firstpart = part->nextpart;
+ Curl_mime_cleanpart(part);
+ free(part);
+ }
+ free(mime);
+ }
+}
+
+CURLcode Curl_mime_duppart(struct Curl_easy *data,
+ curl_mimepart *dst, const curl_mimepart *src)
+{
+ curl_mime *mime;
+ curl_mimepart *d;
+ const curl_mimepart *s;
+ CURLcode res = CURLE_OK;
+
+ DEBUGASSERT(dst);
+
+ /* Duplicate content. */
+ switch(src->kind) {
+ case MIMEKIND_NONE:
+ break;
+ case MIMEKIND_DATA:
+ res = curl_mime_data(dst, src->data, (size_t) src->datasize);
+ break;
+ case MIMEKIND_FILE:
+ res = curl_mime_filedata(dst, src->data);
+ /* Do not abort duplication if file is not readable. */
+ if(res == CURLE_READ_ERROR)
+ res = CURLE_OK;
+ break;
+ case MIMEKIND_CALLBACK:
+ res = curl_mime_data_cb(dst, src->datasize, src->readfunc,
+ src->seekfunc, src->freefunc, src->arg);
+ break;
+ case MIMEKIND_MULTIPART:
+ /* No one knows about the cloned subparts, thus always attach ownership
+ to the part. */
+ mime = curl_mime_init(data);
+ res = mime? curl_mime_subparts(dst, mime): CURLE_OUT_OF_MEMORY;
+
+ /* Duplicate subparts. */
+ for(s = ((curl_mime *) src->arg)->firstpart; !res && s; s = s->nextpart) {
+ d = curl_mime_addpart(mime);
+ res = d? Curl_mime_duppart(data, d, s): CURLE_OUT_OF_MEMORY;
+ }
+ break;
+ default: /* Invalid kind: should not occur. */
+ res = CURLE_BAD_FUNCTION_ARGUMENT; /* Internal error? */
+ break;
+ }
+
+ /* Duplicate headers. */
+ if(!res && src->userheaders) {
+ struct curl_slist *hdrs = Curl_slist_duplicate(src->userheaders);
+
+ if(!hdrs)
+ res = CURLE_OUT_OF_MEMORY;
+ else {
+ /* No one but this procedure knows about the new header list,
+ so always take ownership. */
+ res = curl_mime_headers(dst, hdrs, TRUE);
+ if(res)
+ curl_slist_free_all(hdrs);
+ }
+ }
+
+ if(!res) {
+ /* Duplicate other fields. */
+ dst->encoder = src->encoder;
+ res = curl_mime_type(dst, src->mimetype);
+ }
+ if(!res)
+ res = curl_mime_name(dst, src->name);
+ if(!res)
+ res = curl_mime_filename(dst, src->filename);
+
+ /* If an error occurred, rollback. */
+ if(res)
+ Curl_mime_cleanpart(dst);
+
+ return res;
+}
+
+/*
+ * Mime build functions.
+ */
+
+/* Create a mime handle. */
+curl_mime *curl_mime_init(struct Curl_easy *easy)
+{
+ curl_mime *mime;
+
+ mime = (curl_mime *) malloc(sizeof(*mime));
+
+ if(mime) {
+ mime->parent = NULL;
+ mime->firstpart = NULL;
+ mime->lastpart = NULL;
+
+ memset(mime->boundary, '-', MIME_BOUNDARY_DASHES);
+ if(Curl_rand_hex(easy,
+ (unsigned char *) &mime->boundary[MIME_BOUNDARY_DASHES],
+ MIME_RAND_BOUNDARY_CHARS + 1)) {
+ /* failed to get random separator, bail out */
+ free(mime);
+ return NULL;
+ }
+ mimesetstate(&mime->state, MIMESTATE_BEGIN, NULL);
+ }
+
+ return mime;
+}
+
+/* Initialize a mime part. */
+void Curl_mime_initpart(curl_mimepart *part)
+{
+ memset((char *) part, 0, sizeof(*part));
+ part->lastreadstatus = 1; /* Successful read status. */
+ mimesetstate(&part->state, MIMESTATE_BEGIN, NULL);
+}
+
+/* Create a mime part and append it to a mime handle's part list. */
+curl_mimepart *curl_mime_addpart(curl_mime *mime)
+{
+ curl_mimepart *part;
+
+ if(!mime)
+ return NULL;
+
+ part = (curl_mimepart *) malloc(sizeof(*part));
+
+ if(part) {
+ Curl_mime_initpart(part);
+ part->parent = mime;
+
+ if(mime->lastpart)
+ mime->lastpart->nextpart = part;
+ else
+ mime->firstpart = part;
+
+ mime->lastpart = part;
+ }
+
+ return part;
+}
+
+/* Set mime part name. */
+CURLcode curl_mime_name(curl_mimepart *part, const char *name)
+{
+ if(!part)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ Curl_safefree(part->name);
+ part->name = NULL;
+
+ if(name) {
+ part->name = strdup(name);
+ if(!part->name)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ return CURLE_OK;
+}
+
+/* Set mime part remote file name. */
+CURLcode curl_mime_filename(curl_mimepart *part, const char *filename)
+{
+ if(!part)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ Curl_safefree(part->filename);
+ part->filename = NULL;
+
+ if(filename) {
+ part->filename = strdup(filename);
+ if(!part->filename)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ return CURLE_OK;
+}
+
+/* Set mime part content from memory data. */
+CURLcode curl_mime_data(curl_mimepart *part,
+ const char *data, size_t datasize)
+{
+ if(!part)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ cleanup_part_content(part);
+
+ if(data) {
+ if(datasize == CURL_ZERO_TERMINATED)
+ datasize = strlen(data);
+
+ part->data = malloc(datasize + 1);
+ if(!part->data)
+ return CURLE_OUT_OF_MEMORY;
+
+ part->datasize = datasize;
+
+ if(datasize)
+ memcpy(part->data, data, datasize);
+ part->data[datasize] = '\0'; /* Set a null terminator as sentinel. */
+
+ part->readfunc = mime_mem_read;
+ part->seekfunc = mime_mem_seek;
+ part->freefunc = mime_mem_free;
+ part->flags |= MIME_FAST_READ;
+ part->kind = MIMEKIND_DATA;
+ }
+
+ return CURLE_OK;
+}
+
+/* Set mime part content from named local file. */
+CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename)
+{
+ CURLcode result = CURLE_OK;
+
+ if(!part)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ cleanup_part_content(part);
+
+ if(filename) {
+ char *base;
+ struct_stat sbuf;
+
+ if(stat(filename, &sbuf) || access(filename, R_OK))
+ result = CURLE_READ_ERROR;
+
+ part->data = strdup(filename);
+ if(!part->data)
+ result = CURLE_OUT_OF_MEMORY;
+
+ part->datasize = -1;
+ if(!result && S_ISREG(sbuf.st_mode)) {
+ part->datasize = filesize(filename, sbuf);
+ part->seekfunc = mime_file_seek;
+ }
+
+ part->readfunc = mime_file_read;
+ part->freefunc = mime_file_free;
+ part->kind = MIMEKIND_FILE;
+
+ /* As a side effect, set the filename to the current file's base name.
+ It is possible to withdraw this by explicitly calling
+ curl_mime_filename() with a NULL filename argument after the current
+ call. */
+ base = strippath(filename);
+ if(!base)
+ result = CURLE_OUT_OF_MEMORY;
+ else {
+ CURLcode res = curl_mime_filename(part, base);
+
+ if(res)
+ result = res;
+ free(base);
+ }
+ }
+ return result;
+}
+
+/* Set mime part type. */
+CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype)
+{
+ if(!part)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ Curl_safefree(part->mimetype);
+ part->mimetype = NULL;
+
+ if(mimetype) {
+ part->mimetype = strdup(mimetype);
+ if(!part->mimetype)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ return CURLE_OK;
+}
+
+/* Set mime data transfer encoder. */
+CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding)
+{
+ CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT;
+ const struct mime_encoder *mep;
+
+ if(!part)
+ return result;
+
+ part->encoder = NULL;
+
+ if(!encoding)
+ return CURLE_OK; /* Removing current encoder. */
+
+ for(mep = encoders; mep->name; mep++)
+ if(strcasecompare(encoding, mep->name)) {
+ part->encoder = mep;
+ result = CURLE_OK;
+ }
+
+ return result;
+}
+
+/* Set mime part headers. */
+CURLcode curl_mime_headers(curl_mimepart *part,
+ struct curl_slist *headers, int take_ownership)
+{
+ if(!part)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ if(part->flags & MIME_USERHEADERS_OWNER) {
+ if(part->userheaders != headers) /* Allow setting twice the same list. */
+ curl_slist_free_all(part->userheaders);
+ part->flags &= ~MIME_USERHEADERS_OWNER;
+ }
+ part->userheaders = headers;
+ if(headers && take_ownership)
+ part->flags |= MIME_USERHEADERS_OWNER;
+ return CURLE_OK;
+}
+
+/* Set mime part content from callback. */
+CURLcode curl_mime_data_cb(curl_mimepart *part, curl_off_t datasize,
+ curl_read_callback readfunc,
+ curl_seek_callback seekfunc,
+ curl_free_callback freefunc, void *arg)
+{
+ if(!part)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ cleanup_part_content(part);
+
+ if(readfunc) {
+ part->readfunc = readfunc;
+ part->seekfunc = seekfunc;
+ part->freefunc = freefunc;
+ part->arg = arg;
+ part->datasize = datasize;
+ part->kind = MIMEKIND_CALLBACK;
+ }
+
+ return CURLE_OK;
+}
+
+/* Set mime part content from subparts. */
+CURLcode Curl_mime_set_subparts(curl_mimepart *part,
+ curl_mime *subparts, int take_ownership)
+{
+ curl_mime *root;
+
+ if(!part)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ /* Accept setting twice the same subparts. */
+ if(part->kind == MIMEKIND_MULTIPART && part->arg == subparts)
+ return CURLE_OK;
+
+ cleanup_part_content(part);
+
+ if(subparts) {
+ /* Should not have been attached already. */
+ if(subparts->parent)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ /* Should not be the part's root. */
+ root = part->parent;
+ if(root) {
+ while(root->parent && root->parent->parent)
+ root = root->parent->parent;
+ if(subparts == root) {
+ /* Can't add as a subpart of itself. */
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+ }
+
+ subparts->parent = part;
+ /* Subparts are processed internally: no read callback. */
+ part->seekfunc = mime_subparts_seek;
+ part->freefunc = take_ownership? mime_subparts_free: mime_subparts_unbind;
+ part->arg = subparts;
+ part->datasize = -1;
+ part->kind = MIMEKIND_MULTIPART;
+ }
+
+ return CURLE_OK;
+}
+
+CURLcode curl_mime_subparts(curl_mimepart *part, curl_mime *subparts)
+{
+ return Curl_mime_set_subparts(part, subparts, TRUE);
+}
+
+
+/* Readback from top mime. */
+/* Argument is the dummy top part. */
+size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream)
+{
+ curl_mimepart *part = (curl_mimepart *) instream;
+ size_t ret;
+ bool hasread;
+
+ (void) size; /* Always 1. */
+
+ do {
+ hasread = FALSE;
+ ret = readback_part(part, buffer, nitems, &hasread);
+ /*
+ * If this is not possible to get some data without calling more than
+ * one read callback (probably because a content encoder is not able to
+ * deliver a new bunch for the few data accumulated so far), force another
+ * read until we get enough data or a special exit code.
+ */
+ } while(ret == STOP_FILLING);
+
+ return ret;
+}
+
+/* Rewind mime stream. */
+CURLcode Curl_mime_rewind(curl_mimepart *part)
+{
+ return mime_part_rewind(part) == CURL_SEEKFUNC_OK?
+ CURLE_OK: CURLE_SEND_FAIL_REWIND;
+}
+
+/* Compute header list size. */
+static size_t slist_size(struct curl_slist *s,
+ size_t overhead, const char *skip, size_t skiplen)
+{
+ size_t size = 0;
+
+ for(; s; s = s->next)
+ if(!skip || !match_header(s, skip, skiplen))
+ size += strlen(s->data) + overhead;
+ return size;
+}
+
+/* Get/compute multipart size. */
+static curl_off_t multipart_size(curl_mime *mime)
+{
+ curl_off_t size;
+ curl_off_t boundarysize;
+ curl_mimepart *part;
+
+ if(!mime)
+ return 0; /* Not present -> empty. */
+
+ boundarysize = 4 + MIME_BOUNDARY_LEN + 2;
+ size = boundarysize; /* Final boundary - CRLF after headers. */
+
+ for(part = mime->firstpart; part; part = part->nextpart) {
+ curl_off_t sz = Curl_mime_size(part);
+
+ if(sz < 0)
+ size = sz;
+
+ if(size >= 0)
+ size += boundarysize + sz;
+ }
+
+ return size;
+}
+
+/* Get/compute mime size. */
+curl_off_t Curl_mime_size(curl_mimepart *part)
+{
+ curl_off_t size;
+
+ if(part->kind == MIMEKIND_MULTIPART)
+ part->datasize = multipart_size(part->arg);
+
+ size = part->datasize;
+
+ if(part->encoder)
+ size = part->encoder->sizefunc(part);
+
+ if(size >= 0 && !(part->flags & MIME_BODY_ONLY)) {
+ /* Compute total part size. */
+ size += slist_size(part->curlheaders, 2, NULL, 0);
+ size += slist_size(part->userheaders, 2, STRCONST("Content-Type"));
+ size += 2; /* CRLF after headers. */
+ }
+ return size;
+}
+
+/* Add a header. */
+/* VARARGS2 */
+CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...)
+{
+ struct curl_slist *hdr = NULL;
+ char *s = NULL;
+ va_list ap;
+
+ va_start(ap, fmt);
+ s = curl_mvaprintf(fmt, ap);
+ va_end(ap);
+
+ if(s) {
+ hdr = Curl_slist_append_nodup(*slp, s);
+ if(hdr)
+ *slp = hdr;
+ else
+ free(s);
+ }
+
+ return hdr? CURLE_OK: CURLE_OUT_OF_MEMORY;
+}
+
+/* Add a content type header. */
+static CURLcode add_content_type(struct curl_slist **slp,
+ const char *type, const char *boundary)
+{
+ return Curl_mime_add_header(slp, "Content-Type: %s%s%s", type,
+ boundary? "; boundary=": "",
+ boundary? boundary: "");
+}
+
+const char *Curl_mime_contenttype(const char *filename)
+{
+ /*
+ * If no content type was specified, we scan through a few well-known
+ * extensions and pick the first we match!
+ */
+ struct ContentType {
+ const char *extension;
+ const char *type;
+ };
+ static const struct ContentType ctts[] = {
+ {".gif", "image/gif"},
+ {".jpg", "image/jpeg"},
+ {".jpeg", "image/jpeg"},
+ {".png", "image/png"},
+ {".svg", "image/svg+xml"},
+ {".txt", "text/plain"},
+ {".htm", "text/html"},
+ {".html", "text/html"},
+ {".pdf", "application/pdf"},
+ {".xml", "application/xml"}
+ };
+
+ if(filename) {
+ size_t len1 = strlen(filename);
+ const char *nameend = filename + len1;
+ unsigned int i;
+
+ for(i = 0; i < sizeof(ctts) / sizeof(ctts[0]); i++) {
+ size_t len2 = strlen(ctts[i].extension);
+
+ if(len1 >= len2 && strcasecompare(nameend - len2, ctts[i].extension))
+ return ctts[i].type;
+ }
+ }
+ return NULL;
+}
+
+static bool content_type_match(const char *contenttype,
+ const char *target, size_t len)
+{
+ if(contenttype && strncasecompare(contenttype, target, len))
+ switch(contenttype[len]) {
+ case '\0':
+ case '\t':
+ case '\r':
+ case '\n':
+ case ' ':
+ case ';':
+ return TRUE;
+ }
+ return FALSE;
+}
+
+CURLcode Curl_mime_prepare_headers(struct Curl_easy *data,
+ curl_mimepart *part,
+ const char *contenttype,
+ const char *disposition,
+ enum mimestrategy strategy)
+{
+ curl_mime *mime = NULL;
+ const char *boundary = NULL;
+ char *customct;
+ const char *cte = NULL;
+ CURLcode ret = CURLE_OK;
+
+ /* Get rid of previously prepared headers. */
+ curl_slist_free_all(part->curlheaders);
+ part->curlheaders = NULL;
+
+ /* Be sure we won't access old headers later. */
+ if(part->state.state == MIMESTATE_CURLHEADERS)
+ mimesetstate(&part->state, MIMESTATE_CURLHEADERS, NULL);
+
+ /* Check if content type is specified. */
+ customct = part->mimetype;
+ if(!customct)
+ customct = search_header(part->userheaders, STRCONST("Content-Type"));
+ if(customct)
+ contenttype = customct;
+
+ /* If content type is not specified, try to determine it. */
+ if(!contenttype) {
+ switch(part->kind) {
+ case MIMEKIND_MULTIPART:
+ contenttype = MULTIPART_CONTENTTYPE_DEFAULT;
+ break;
+ case MIMEKIND_FILE:
+ contenttype = Curl_mime_contenttype(part->filename);
+ if(!contenttype)
+ contenttype = Curl_mime_contenttype(part->data);
+ if(!contenttype && part->filename)
+ contenttype = FILE_CONTENTTYPE_DEFAULT;
+ break;
+ default:
+ contenttype = Curl_mime_contenttype(part->filename);
+ break;
+ }
+ }
+
+ if(part->kind == MIMEKIND_MULTIPART) {
+ mime = (curl_mime *) part->arg;
+ if(mime)
+ boundary = mime->boundary;
+ }
+ else if(contenttype && !customct &&
+ content_type_match(contenttype, STRCONST("text/plain")))
+ if(strategy == MIMESTRATEGY_MAIL || !part->filename)
+ contenttype = NULL;
+
+ /* Issue content-disposition header only if not already set by caller. */
+ if(!search_header(part->userheaders, STRCONST("Content-Disposition"))) {
+ if(!disposition)
+ if(part->filename || part->name ||
+ (contenttype && !strncasecompare(contenttype, "multipart/", 10)))
+ disposition = DISPOSITION_DEFAULT;
+ if(disposition && curl_strequal(disposition, "attachment") &&
+ !part->name && !part->filename)
+ disposition = NULL;
+ if(disposition) {
+ char *name = NULL;
+ char *filename = NULL;
+
+ if(part->name) {
+ name = escape_string(data, part->name, strategy);
+ if(!name)
+ ret = CURLE_OUT_OF_MEMORY;
+ }
+ if(!ret && part->filename) {
+ filename = escape_string(data, part->filename, strategy);
+ if(!filename)
+ ret = CURLE_OUT_OF_MEMORY;
+ }
+ if(!ret)
+ ret = Curl_mime_add_header(&part->curlheaders,
+ "Content-Disposition: %s%s%s%s%s%s%s",
+ disposition,
+ name? "; name=\"": "",
+ name? name: "",
+ name? "\"": "",
+ filename? "; filename=\"": "",
+ filename? filename: "",
+ filename? "\"": "");
+ Curl_safefree(name);
+ Curl_safefree(filename);
+ if(ret)
+ return ret;
+ }
+ }
+
+ /* Issue Content-Type header. */
+ if(contenttype) {
+ ret = add_content_type(&part->curlheaders, contenttype, boundary);
+ if(ret)
+ return ret;
+ }
+
+ /* Content-Transfer-Encoding header. */
+ if(!search_header(part->userheaders,
+ STRCONST("Content-Transfer-Encoding"))) {
+ if(part->encoder)
+ cte = part->encoder->name;
+ else if(contenttype && strategy == MIMESTRATEGY_MAIL &&
+ part->kind != MIMEKIND_MULTIPART)
+ cte = "8bit";
+ if(cte) {
+ ret = Curl_mime_add_header(&part->curlheaders,
+ "Content-Transfer-Encoding: %s", cte);
+ if(ret)
+ return ret;
+ }
+ }
+
+ /* If we were reading curl-generated headers, restart with new ones (this
+ should not occur). */
+ if(part->state.state == MIMESTATE_CURLHEADERS)
+ mimesetstate(&part->state, MIMESTATE_CURLHEADERS, part->curlheaders);
+
+ /* Process subparts. */
+ if(part->kind == MIMEKIND_MULTIPART && mime) {
+ curl_mimepart *subpart;
+
+ disposition = NULL;
+ if(content_type_match(contenttype, STRCONST("multipart/form-data")))
+ disposition = "form-data";
+ for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart) {
+ ret = Curl_mime_prepare_headers(data, subpart, NULL,
+ disposition, strategy);
+ if(ret)
+ return ret;
+ }
+ }
+ return ret;
+}
+
+/* Recursively reset paused status in the given part. */
+void Curl_mime_unpause(curl_mimepart *part)
+{
+ if(part) {
+ if(part->lastreadstatus == CURL_READFUNC_PAUSE)
+ part->lastreadstatus = 1; /* Successful read status. */
+ if(part->kind == MIMEKIND_MULTIPART) {
+ curl_mime *mime = (curl_mime *) part->arg;
+
+ if(mime) {
+ curl_mimepart *subpart;
+
+ for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart)
+ Curl_mime_unpause(subpart);
+ }
+ }
+ }
+}
+
+
+#else /* !CURL_DISABLE_MIME && (!CURL_DISABLE_HTTP ||
+ !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP) */
+
+/* Mime not compiled in: define stubs for externally-referenced functions. */
+curl_mime *curl_mime_init(CURL *easy)
+{
+ (void) easy;
+ return NULL;
+}
+
+void curl_mime_free(curl_mime *mime)
+{
+ (void) mime;
+}
+
+curl_mimepart *curl_mime_addpart(curl_mime *mime)
+{
+ (void) mime;
+ return NULL;
+}
+
+CURLcode curl_mime_name(curl_mimepart *part, const char *name)
+{
+ (void) part;
+ (void) name;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode curl_mime_filename(curl_mimepart *part, const char *filename)
+{
+ (void) part;
+ (void) filename;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype)
+{
+ (void) part;
+ (void) mimetype;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding)
+{
+ (void) part;
+ (void) encoding;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode curl_mime_data(curl_mimepart *part,
+ const char *data, size_t datasize)
+{
+ (void) part;
+ (void) data;
+ (void) datasize;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename)
+{
+ (void) part;
+ (void) filename;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode curl_mime_data_cb(curl_mimepart *part,
+ curl_off_t datasize,
+ curl_read_callback readfunc,
+ curl_seek_callback seekfunc,
+ curl_free_callback freefunc,
+ void *arg)
+{
+ (void) part;
+ (void) datasize;
+ (void) readfunc;
+ (void) seekfunc;
+ (void) freefunc;
+ (void) arg;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode curl_mime_subparts(curl_mimepart *part, curl_mime *subparts)
+{
+ (void) part;
+ (void) subparts;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode curl_mime_headers(curl_mimepart *part,
+ struct curl_slist *headers, int take_ownership)
+{
+ (void) part;
+ (void) headers;
+ (void) take_ownership;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...)
+{
+ (void)slp;
+ (void)fmt;
+ return CURLE_NOT_BUILT_IN;
+}
+
+#endif /* if disabled */
diff --git a/libs/libcurl/src/mime.h b/libs/libcurl/src/mime.h index bafde29f40..58c12341f8 100644 --- a/libs/libcurl/src/mime.h +++ b/libs/libcurl/src/mime.h @@ -1,174 +1,174 @@ -#ifndef HEADER_CURL_MIME_H -#define HEADER_CURL_MIME_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -#define MIME_BOUNDARY_DASHES 24 /* leading boundary dashes */ -#define MIME_RAND_BOUNDARY_CHARS 16 /* Nb. of random boundary chars. */ -#define MAX_ENCODED_LINE_LENGTH 76 /* Maximum encoded line length. */ -#define ENCODING_BUFFER_SIZE 256 /* Encoding temp buffers size. */ - -/* Part flags. */ -#define MIME_USERHEADERS_OWNER (1 << 0) -#define MIME_BODY_ONLY (1 << 1) -#define MIME_FAST_READ (1 << 2) - -#define FILE_CONTENTTYPE_DEFAULT "application/octet-stream" -#define MULTIPART_CONTENTTYPE_DEFAULT "multipart/mixed" -#define DISPOSITION_DEFAULT "attachment" - -/* Part source kinds. */ -enum mimekind { - MIMEKIND_NONE = 0, /* Part not set. */ - MIMEKIND_DATA, /* Allocated mime data. */ - MIMEKIND_FILE, /* Data from file. */ - MIMEKIND_CALLBACK, /* Data from `read' callback. */ - MIMEKIND_MULTIPART, /* Data is a mime subpart. */ - MIMEKIND_LAST -}; - -/* Readback state tokens. */ -enum mimestate { - MIMESTATE_BEGIN, /* Readback has not yet started. */ - MIMESTATE_CURLHEADERS, /* In curl-generated headers. */ - MIMESTATE_USERHEADERS, /* In caller's supplied headers. */ - MIMESTATE_EOH, /* End of headers. */ - MIMESTATE_BODY, /* Placeholder. */ - MIMESTATE_BOUNDARY1, /* In boundary prefix. */ - MIMESTATE_BOUNDARY2, /* In boundary. */ - MIMESTATE_CONTENT, /* In content. */ - MIMESTATE_END, /* End of part reached. */ - MIMESTATE_LAST -}; - -/* Mime headers strategies. */ -enum mimestrategy { - MIMESTRATEGY_MAIL, /* Mime mail. */ - MIMESTRATEGY_FORM, /* HTTP post form. */ - MIMESTRATEGY_LAST -}; - -/* Content transfer encoder. */ -struct mime_encoder { - const char * name; /* Encoding name. */ - size_t (*encodefunc)(char *buffer, size_t size, bool ateof, - curl_mimepart *part); /* Encoded read. */ - curl_off_t (*sizefunc)(curl_mimepart *part); /* Encoded size. */ -}; - -/* Content transfer encoder state. */ -struct mime_encoder_state { - size_t pos; /* Position on output line. */ - size_t bufbeg; /* Next data index in input buffer. */ - size_t bufend; /* First unused byte index in input buffer. */ - char buf[ENCODING_BUFFER_SIZE]; /* Input buffer. */ -}; - -/* Mime readback state. */ -struct mime_state { - enum mimestate state; /* Current state token. */ - void *ptr; /* State-dependent pointer. */ - curl_off_t offset; /* State-dependent offset. */ -}; - -/* Boundary string length. */ -#define MIME_BOUNDARY_LEN (MIME_BOUNDARY_DASHES + MIME_RAND_BOUNDARY_CHARS) - -/* A mime multipart. */ -struct curl_mime { - struct Curl_easy *easy; /* The associated easy handle. */ - curl_mimepart *parent; /* Parent part. */ - curl_mimepart *firstpart; /* First part. */ - curl_mimepart *lastpart; /* Last part. */ - char boundary[MIME_BOUNDARY_LEN + 1]; /* The part boundary. */ - struct mime_state state; /* Current readback state. */ -}; - -/* A mime part. */ -struct curl_mimepart { - struct Curl_easy *easy; /* The associated easy handle. */ - curl_mime *parent; /* Parent mime structure. */ - curl_mimepart *nextpart; /* Forward linked list. */ - enum mimekind kind; /* The part kind. */ - unsigned int flags; /* Flags. */ - char *data; /* Memory data or file name. */ - curl_read_callback readfunc; /* Read function. */ - curl_seek_callback seekfunc; /* Seek function. */ - curl_free_callback freefunc; /* Argument free function. */ - void *arg; /* Argument to callback functions. */ - FILE *fp; /* File pointer. */ - struct curl_slist *curlheaders; /* Part headers. */ - struct curl_slist *userheaders; /* Part headers. */ - char *mimetype; /* Part mime type. */ - char *filename; /* Remote file name. */ - char *name; /* Data name. */ - curl_off_t datasize; /* Expected data size. */ - struct mime_state state; /* Current readback state. */ - const struct mime_encoder *encoder; /* Content data encoder. */ - struct mime_encoder_state encstate; /* Data encoder state. */ - size_t lastreadstatus; /* Last read callback returned status. */ -}; - -CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...); - -#if !defined(CURL_DISABLE_MIME) && (!defined(CURL_DISABLE_HTTP) || \ - !defined(CURL_DISABLE_SMTP) || \ - !defined(CURL_DISABLE_IMAP)) - -/* Prototypes. */ -void Curl_mime_initpart(struct curl_mimepart *part, struct Curl_easy *easy); -void Curl_mime_cleanpart(struct curl_mimepart *part); -CURLcode Curl_mime_duppart(struct curl_mimepart *dst, - const curl_mimepart *src); -CURLcode Curl_mime_set_subparts(struct curl_mimepart *part, - struct curl_mime *subparts, - int take_ownership); -CURLcode Curl_mime_prepare_headers(struct curl_mimepart *part, - const char *contenttype, - const char *disposition, - enum mimestrategy strategy); -curl_off_t Curl_mime_size(struct curl_mimepart *part); -size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, - void *instream); -CURLcode Curl_mime_rewind(struct curl_mimepart *part); -const char *Curl_mime_contenttype(const char *filename); -void Curl_mime_unpause(struct curl_mimepart *part); - -#else -/* if disabled */ -#define Curl_mime_initpart(x,y) -#define Curl_mime_cleanpart(x) -#define Curl_mime_duppart(x,y) CURLE_OK /* Nothing to duplicate. Succeed */ -#define Curl_mime_set_subparts(a,b,c) CURLE_NOT_BUILT_IN -#define Curl_mime_prepare_headers(a,b,c,d) CURLE_NOT_BUILT_IN -#define Curl_mime_size(x) (curl_off_t) -1 -#define Curl_mime_read NULL -#define Curl_mime_rewind(x) ((void)x, CURLE_NOT_BUILT_IN) -#define Curl_mime_unpause(x) -#endif - - -#endif /* HEADER_CURL_MIME_H */ +#ifndef HEADER_CURL_MIME_H
+#define HEADER_CURL_MIME_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+#define MIME_BOUNDARY_DASHES 24 /* leading boundary dashes */
+#define MIME_RAND_BOUNDARY_CHARS 16 /* Nb. of random boundary chars. */
+#define MAX_ENCODED_LINE_LENGTH 76 /* Maximum encoded line length. */
+#define ENCODING_BUFFER_SIZE 256 /* Encoding temp buffers size. */
+
+/* Part flags. */
+#define MIME_USERHEADERS_OWNER (1 << 0)
+#define MIME_BODY_ONLY (1 << 1)
+#define MIME_FAST_READ (1 << 2)
+
+#define FILE_CONTENTTYPE_DEFAULT "application/octet-stream"
+#define MULTIPART_CONTENTTYPE_DEFAULT "multipart/mixed"
+#define DISPOSITION_DEFAULT "attachment"
+
+/* Part source kinds. */
+enum mimekind {
+ MIMEKIND_NONE = 0, /* Part not set. */
+ MIMEKIND_DATA, /* Allocated mime data. */
+ MIMEKIND_FILE, /* Data from file. */
+ MIMEKIND_CALLBACK, /* Data from `read' callback. */
+ MIMEKIND_MULTIPART, /* Data is a mime subpart. */
+ MIMEKIND_LAST
+};
+
+/* Readback state tokens. */
+enum mimestate {
+ MIMESTATE_BEGIN, /* Readback has not yet started. */
+ MIMESTATE_CURLHEADERS, /* In curl-generated headers. */
+ MIMESTATE_USERHEADERS, /* In caller's supplied headers. */
+ MIMESTATE_EOH, /* End of headers. */
+ MIMESTATE_BODY, /* Placeholder. */
+ MIMESTATE_BOUNDARY1, /* In boundary prefix. */
+ MIMESTATE_BOUNDARY2, /* In boundary. */
+ MIMESTATE_CONTENT, /* In content. */
+ MIMESTATE_END, /* End of part reached. */
+ MIMESTATE_LAST
+};
+
+/* Mime headers strategies. */
+enum mimestrategy {
+ MIMESTRATEGY_MAIL, /* Mime mail. */
+ MIMESTRATEGY_FORM, /* HTTP post form. */
+ MIMESTRATEGY_LAST
+};
+
+/* Content transfer encoder. */
+struct mime_encoder {
+ const char * name; /* Encoding name. */
+ size_t (*encodefunc)(char *buffer, size_t size, bool ateof,
+ curl_mimepart *part); /* Encoded read. */
+ curl_off_t (*sizefunc)(curl_mimepart *part); /* Encoded size. */
+};
+
+/* Content transfer encoder state. */
+struct mime_encoder_state {
+ size_t pos; /* Position on output line. */
+ size_t bufbeg; /* Next data index in input buffer. */
+ size_t bufend; /* First unused byte index in input buffer. */
+ char buf[ENCODING_BUFFER_SIZE]; /* Input buffer. */
+};
+
+/* Mime readback state. */
+struct mime_state {
+ enum mimestate state; /* Current state token. */
+ void *ptr; /* State-dependent pointer. */
+ curl_off_t offset; /* State-dependent offset. */
+};
+
+/* Boundary string length. */
+#define MIME_BOUNDARY_LEN (MIME_BOUNDARY_DASHES + MIME_RAND_BOUNDARY_CHARS)
+
+/* A mime multipart. */
+struct curl_mime {
+ curl_mimepart *parent; /* Parent part. */
+ curl_mimepart *firstpart; /* First part. */
+ curl_mimepart *lastpart; /* Last part. */
+ char boundary[MIME_BOUNDARY_LEN + 1]; /* The part boundary. */
+ struct mime_state state; /* Current readback state. */
+};
+
+/* A mime part. */
+struct curl_mimepart {
+ curl_mime *parent; /* Parent mime structure. */
+ curl_mimepart *nextpart; /* Forward linked list. */
+ enum mimekind kind; /* The part kind. */
+ unsigned int flags; /* Flags. */
+ char *data; /* Memory data or file name. */
+ curl_read_callback readfunc; /* Read function. */
+ curl_seek_callback seekfunc; /* Seek function. */
+ curl_free_callback freefunc; /* Argument free function. */
+ void *arg; /* Argument to callback functions. */
+ FILE *fp; /* File pointer. */
+ struct curl_slist *curlheaders; /* Part headers. */
+ struct curl_slist *userheaders; /* Part headers. */
+ char *mimetype; /* Part mime type. */
+ char *filename; /* Remote file name. */
+ char *name; /* Data name. */
+ curl_off_t datasize; /* Expected data size. */
+ struct mime_state state; /* Current readback state. */
+ const struct mime_encoder *encoder; /* Content data encoder. */
+ struct mime_encoder_state encstate; /* Data encoder state. */
+ size_t lastreadstatus; /* Last read callback returned status. */
+};
+
+CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...);
+
+#if !defined(CURL_DISABLE_MIME) && (!defined(CURL_DISABLE_HTTP) || \
+ !defined(CURL_DISABLE_SMTP) || \
+ !defined(CURL_DISABLE_IMAP))
+
+/* Prototypes. */
+void Curl_mime_initpart(struct curl_mimepart *part);
+void Curl_mime_cleanpart(struct curl_mimepart *part);
+CURLcode Curl_mime_duppart(struct Curl_easy *data,
+ struct curl_mimepart *dst,
+ const curl_mimepart *src);
+CURLcode Curl_mime_set_subparts(struct curl_mimepart *part,
+ struct curl_mime *subparts,
+ int take_ownership);
+CURLcode Curl_mime_prepare_headers(struct Curl_easy *data,
+ struct curl_mimepart *part,
+ const char *contenttype,
+ const char *disposition,
+ enum mimestrategy strategy);
+curl_off_t Curl_mime_size(struct curl_mimepart *part);
+size_t Curl_mime_read(char *buffer, size_t size, size_t nitems,
+ void *instream);
+CURLcode Curl_mime_rewind(struct curl_mimepart *part);
+const char *Curl_mime_contenttype(const char *filename);
+void Curl_mime_unpause(struct curl_mimepart *part);
+
+#else
+/* if disabled */
+#define Curl_mime_initpart(x)
+#define Curl_mime_cleanpart(x)
+#define Curl_mime_duppart(x,y,z) CURLE_OK /* Nothing to duplicate. Succeed */
+#define Curl_mime_set_subparts(a,b,c) CURLE_NOT_BUILT_IN
+#define Curl_mime_prepare_headers(a,b,c,d,e) CURLE_NOT_BUILT_IN
+#define Curl_mime_size(x) (curl_off_t) -1
+#define Curl_mime_read NULL
+#define Curl_mime_rewind(x) ((void)x, CURLE_NOT_BUILT_IN)
+#define Curl_mime_unpause(x)
+#endif
+
+
+#endif /* HEADER_CURL_MIME_H */
diff --git a/libs/libcurl/src/mprintf.c b/libs/libcurl/src/mprintf.c index 8a7c17a7ff..fbafb22b39 100644 --- a/libs/libcurl/src/mprintf.c +++ b/libs/libcurl/src/mprintf.c @@ -1,1177 +1,1177 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1999 - 2022, 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 - * - * - * Purpose: - * A merge of Bjorn Reese's format() function and Daniel's dsprintf() - * 1.0. A full blooded printf() clone with full support for <num>$ - * everywhere (parameters, widths and precisions) including variabled - * sized parameters (like doubles, long longs, long doubles and even - * void * in 64-bit architectures). - * - * Current restrictions: - * - Max 128 parameters - * - No 'long double' support. - * - * If you ever want truly portable and good *printf() clones, the project that - * took on from here is named 'Trio' and you find more details on the trio web - * page at https://daniel.haxx.se/projects/trio/ - */ - -#include "curl_setup.h" -#include "dynbuf.h" -#include <curl/mprintf.h> - -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -/* - * If SIZEOF_SIZE_T has not been defined, default to the size of long. - */ - -#ifdef HAVE_LONGLONG -# define LONG_LONG_TYPE long long -# define HAVE_LONG_LONG_TYPE -#else -# if defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64) -# define LONG_LONG_TYPE __int64 -# define HAVE_LONG_LONG_TYPE -# else -# undef LONG_LONG_TYPE -# undef HAVE_LONG_LONG_TYPE -# endif -#endif - -/* - * Non-ANSI integer extensions - */ - -#if (defined(__BORLANDC__) && (__BORLANDC__ >= 0x520)) || \ - (defined(__POCC__) && defined(_MSC_VER)) || \ - (defined(_WIN32_WCE)) || \ - (defined(__MINGW32__)) || \ - (defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)) -# define MP_HAVE_INT_EXTENSIONS -#endif - -/* - * Max integer data types that mprintf.c is capable - */ - -#ifdef HAVE_LONG_LONG_TYPE -# define mp_intmax_t LONG_LONG_TYPE -# define mp_uintmax_t unsigned LONG_LONG_TYPE -#else -# define mp_intmax_t long -# define mp_uintmax_t unsigned long -#endif - -#define BUFFSIZE 326 /* buffer for long-to-str and float-to-str calcs, should - fit negative DBL_MAX (317 letters) */ -#define MAX_PARAMETERS 128 /* lame static limit */ - -#ifdef __AMIGA__ -# undef FORMAT_INT -#endif - -/* Lower-case digits. */ -static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; - -/* Upper-case digits. */ -static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - -#define OUTCHAR(x) \ - do { \ - if(stream((unsigned char)(x), (FILE *)data) != -1) \ - done++; \ - else \ - return done; /* return immediately on failure */ \ - } while(0) - -/* Data type to read from the arglist */ -typedef enum { - FORMAT_UNKNOWN = 0, - FORMAT_STRING, - FORMAT_PTR, - FORMAT_INT, - FORMAT_INTPTR, - FORMAT_LONG, - FORMAT_LONGLONG, - FORMAT_DOUBLE, - FORMAT_LONGDOUBLE, - FORMAT_WIDTH /* For internal use */ -} FormatType; - -/* conversion and display flags */ -enum { - FLAGS_NEW = 0, - FLAGS_SPACE = 1<<0, - FLAGS_SHOWSIGN = 1<<1, - FLAGS_LEFT = 1<<2, - FLAGS_ALT = 1<<3, - FLAGS_SHORT = 1<<4, - FLAGS_LONG = 1<<5, - FLAGS_LONGLONG = 1<<6, - FLAGS_LONGDOUBLE = 1<<7, - FLAGS_PAD_NIL = 1<<8, - FLAGS_UNSIGNED = 1<<9, - FLAGS_OCTAL = 1<<10, - FLAGS_HEX = 1<<11, - FLAGS_UPPER = 1<<12, - FLAGS_WIDTH = 1<<13, /* '*' or '*<num>$' used */ - FLAGS_WIDTHPARAM = 1<<14, /* width PARAMETER was specified */ - FLAGS_PREC = 1<<15, /* precision was specified */ - FLAGS_PRECPARAM = 1<<16, /* precision PARAMETER was specified */ - FLAGS_CHAR = 1<<17, /* %c story */ - FLAGS_FLOATE = 1<<18, /* %e or %E */ - FLAGS_FLOATG = 1<<19 /* %g or %G */ -}; - -struct va_stack { - FormatType type; - int flags; - long width; /* width OR width parameter number */ - long precision; /* precision OR precision parameter number */ - union { - char *str; - void *ptr; - union { - mp_intmax_t as_signed; - mp_uintmax_t as_unsigned; - } num; - double dnum; - } data; -}; - -struct nsprintf { - char *buffer; - size_t length; - size_t max; -}; - -struct asprintf { - struct dynbuf *b; - bool fail; /* if an alloc has failed and thus the output is not the complete - data */ -}; - -static long dprintf_DollarString(char *input, char **end) -{ - int number = 0; - while(ISDIGIT(*input)) { - if(number < MAX_PARAMETERS) { - number *= 10; - number += *input - '0'; - } - input++; - } - if(number <= MAX_PARAMETERS && ('$' == *input)) { - *end = ++input; - return number; - } - return 0; -} - -static bool dprintf_IsQualifierNoDollar(const char *fmt) -{ -#if defined(MP_HAVE_INT_EXTENSIONS) - if(!strncmp(fmt, "I32", 3) || !strncmp(fmt, "I64", 3)) { - return TRUE; - } -#endif - - switch(*fmt) { - case '-': case '+': case ' ': case '#': case '.': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - case 'h': case 'l': case 'L': case 'z': case 'q': - case '*': case 'O': -#if defined(MP_HAVE_INT_EXTENSIONS) - case 'I': -#endif - return TRUE; - - default: - return FALSE; - } -} - -/****************************************************************** - * - * Pass 1: - * Create an index with the type of each parameter entry and its - * value (may vary in size) - * - * Returns zero on success. - * - ******************************************************************/ - -static int dprintf_Pass1(const char *format, struct va_stack *vto, - char **endpos, va_list arglist) -{ - char *fmt = (char *)format; - int param_num = 0; - long this_param; - long width; - long precision; - int flags; - long max_param = 0; - long i; - - while(*fmt) { - if(*fmt++ == '%') { - if(*fmt == '%') { - fmt++; - continue; /* while */ - } - - flags = FLAGS_NEW; - - /* Handle the positional case (N$) */ - - param_num++; - - this_param = dprintf_DollarString(fmt, &fmt); - if(0 == this_param) - /* we got no positional, get the next counter */ - this_param = param_num; - - if(this_param > max_param) - max_param = this_param; - - /* - * The parameter with number 'i' should be used. Next, we need - * to get SIZE and TYPE of the parameter. Add the information - * to our array. - */ - - width = 0; - precision = 0; - - /* Handle the flags */ - - while(dprintf_IsQualifierNoDollar(fmt)) { -#if defined(MP_HAVE_INT_EXTENSIONS) - if(!strncmp(fmt, "I32", 3)) { - flags |= FLAGS_LONG; - fmt += 3; - } - else if(!strncmp(fmt, "I64", 3)) { - flags |= FLAGS_LONGLONG; - fmt += 3; - } - else -#endif - - switch(*fmt++) { - case ' ': - flags |= FLAGS_SPACE; - break; - case '+': - flags |= FLAGS_SHOWSIGN; - break; - case '-': - flags |= FLAGS_LEFT; - flags &= ~FLAGS_PAD_NIL; - break; - case '#': - flags |= FLAGS_ALT; - break; - case '.': - if('*' == *fmt) { - /* The precision is picked from a specified parameter */ - - flags |= FLAGS_PRECPARAM; - fmt++; - param_num++; - - i = dprintf_DollarString(fmt, &fmt); - if(i) - precision = i; - else - precision = param_num; - - if(precision > max_param) - max_param = precision; - } - else { - flags |= FLAGS_PREC; - precision = strtol(fmt, &fmt, 10); - } - if((flags & (FLAGS_PREC | FLAGS_PRECPARAM)) == - (FLAGS_PREC | FLAGS_PRECPARAM)) - /* it is not permitted to use both kinds of precision for the same - argument */ - return 1; - break; - case 'h': - flags |= FLAGS_SHORT; - break; -#if defined(MP_HAVE_INT_EXTENSIONS) - case 'I': -#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) - flags |= FLAGS_LONGLONG; -#else - flags |= FLAGS_LONG; -#endif - break; -#endif - case 'l': - if(flags & FLAGS_LONG) - flags |= FLAGS_LONGLONG; - else - flags |= FLAGS_LONG; - break; - case 'L': - flags |= FLAGS_LONGDOUBLE; - break; - case 'q': - flags |= FLAGS_LONGLONG; - break; - case 'z': - /* the code below generates a warning if -Wunreachable-code is - used */ -#if (SIZEOF_SIZE_T > SIZEOF_LONG) - flags |= FLAGS_LONGLONG; -#else - flags |= FLAGS_LONG; -#endif - break; - case 'O': -#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) - flags |= FLAGS_LONGLONG; -#else - flags |= FLAGS_LONG; -#endif - break; - case '0': - if(!(flags & FLAGS_LEFT)) - flags |= FLAGS_PAD_NIL; - /* FALLTHROUGH */ - case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - flags |= FLAGS_WIDTH; - width = strtol(fmt-1, &fmt, 10); - break; - case '*': /* Special case */ - flags |= FLAGS_WIDTHPARAM; - param_num++; - - i = dprintf_DollarString(fmt, &fmt); - if(i) - width = i; - else - width = param_num; - if(width > max_param) - max_param = width; - break; - case '\0': - fmt--; - default: - break; - } - } /* switch */ - - /* Handle the specifier */ - - i = this_param - 1; - - if((i < 0) || (i >= MAX_PARAMETERS)) - /* out of allowed range */ - return 1; - - switch (*fmt) { - case 'S': - flags |= FLAGS_ALT; - /* FALLTHROUGH */ - case 's': - vto[i].type = FORMAT_STRING; - break; - case 'n': - vto[i].type = FORMAT_INTPTR; - break; - case 'p': - vto[i].type = FORMAT_PTR; - break; - case 'd': case 'i': - vto[i].type = FORMAT_INT; - break; - case 'u': - vto[i].type = FORMAT_INT; - flags |= FLAGS_UNSIGNED; - break; - case 'o': - vto[i].type = FORMAT_INT; - flags |= FLAGS_OCTAL; - break; - case 'x': - vto[i].type = FORMAT_INT; - flags |= FLAGS_HEX|FLAGS_UNSIGNED; - break; - case 'X': - vto[i].type = FORMAT_INT; - flags |= FLAGS_HEX|FLAGS_UPPER|FLAGS_UNSIGNED; - break; - case 'c': - vto[i].type = FORMAT_INT; - flags |= FLAGS_CHAR; - break; - case 'f': - vto[i].type = FORMAT_DOUBLE; - break; - case 'e': - vto[i].type = FORMAT_DOUBLE; - flags |= FLAGS_FLOATE; - break; - case 'E': - vto[i].type = FORMAT_DOUBLE; - flags |= FLAGS_FLOATE|FLAGS_UPPER; - break; - case 'g': - vto[i].type = FORMAT_DOUBLE; - flags |= FLAGS_FLOATG; - break; - case 'G': - vto[i].type = FORMAT_DOUBLE; - flags |= FLAGS_FLOATG|FLAGS_UPPER; - break; - default: - vto[i].type = FORMAT_UNKNOWN; - break; - } /* switch */ - - vto[i].flags = flags; - vto[i].width = width; - vto[i].precision = precision; - - if(flags & FLAGS_WIDTHPARAM) { - /* we have the width specified from a parameter, so we make that - parameter's info setup properly */ - long k = width - 1; - if((k < 0) || (k >= MAX_PARAMETERS)) - /* out of allowed range */ - return 1; - vto[i].width = k; - vto[k].type = FORMAT_WIDTH; - vto[k].flags = FLAGS_NEW; - /* can't use width or precision of width! */ - vto[k].width = 0; - vto[k].precision = 0; - } - if(flags & FLAGS_PRECPARAM) { - /* we have the precision specified from a parameter, so we make that - parameter's info setup properly */ - long k = precision - 1; - if((k < 0) || (k >= MAX_PARAMETERS)) - /* out of allowed range */ - return 1; - vto[i].precision = k; - vto[k].type = FORMAT_WIDTH; - vto[k].flags = FLAGS_NEW; - /* can't use width or precision of width! */ - vto[k].width = 0; - vto[k].precision = 0; - } - *endpos++ = fmt + ((*fmt == '\0') ? 0 : 1); /* end of this sequence */ - } - } - - /* Read the arg list parameters into our data list */ - for(i = 0; i<max_param; i++) { - /* Width/precision arguments must be read before the main argument - they are attached to */ - if(vto[i].flags & FLAGS_WIDTHPARAM) { - vto[vto[i].width].data.num.as_signed = - (mp_intmax_t)va_arg(arglist, int); - } - if(vto[i].flags & FLAGS_PRECPARAM) { - vto[vto[i].precision].data.num.as_signed = - (mp_intmax_t)va_arg(arglist, int); - } - - switch(vto[i].type) { - case FORMAT_STRING: - vto[i].data.str = va_arg(arglist, char *); - break; - - case FORMAT_INTPTR: - case FORMAT_UNKNOWN: - case FORMAT_PTR: - vto[i].data.ptr = va_arg(arglist, void *); - break; - - case FORMAT_INT: -#ifdef HAVE_LONG_LONG_TYPE - if((vto[i].flags & FLAGS_LONGLONG) && (vto[i].flags & FLAGS_UNSIGNED)) - vto[i].data.num.as_unsigned = - (mp_uintmax_t)va_arg(arglist, mp_uintmax_t); - else if(vto[i].flags & FLAGS_LONGLONG) - vto[i].data.num.as_signed = - (mp_intmax_t)va_arg(arglist, mp_intmax_t); - else -#endif - { - if((vto[i].flags & FLAGS_LONG) && (vto[i].flags & FLAGS_UNSIGNED)) - vto[i].data.num.as_unsigned = - (mp_uintmax_t)va_arg(arglist, unsigned long); - else if(vto[i].flags & FLAGS_LONG) - vto[i].data.num.as_signed = - (mp_intmax_t)va_arg(arglist, long); - else if(vto[i].flags & FLAGS_UNSIGNED) - vto[i].data.num.as_unsigned = - (mp_uintmax_t)va_arg(arglist, unsigned int); - else - vto[i].data.num.as_signed = - (mp_intmax_t)va_arg(arglist, int); - } - break; - - case FORMAT_DOUBLE: - vto[i].data.dnum = va_arg(arglist, double); - break; - - case FORMAT_WIDTH: - /* Argument has been read. Silently convert it into an integer - * for later use - */ - vto[i].type = FORMAT_INT; - break; - - default: - break; - } - } - - return 0; - -} - -static int dprintf_formatf( - void *data, /* untouched by format(), just sent to the stream() function in - the second argument */ - /* function pointer called for each output character */ - int (*stream)(int, FILE *), - const char *format, /* %-formatted string */ - va_list ap_save) /* list of parameters */ -{ - /* Base-36 digits for numbers. */ - const char *digits = lower_digits; - - /* Pointer into the format string. */ - char *f; - - /* Number of characters written. */ - int done = 0; - - long param; /* current parameter to read */ - long param_num = 0; /* parameter counter */ - - struct va_stack vto[MAX_PARAMETERS]; - char *endpos[MAX_PARAMETERS]; - char **end; - char work[BUFFSIZE]; - struct va_stack *p; - - /* 'workend' points to the final buffer byte position, but with an extra - byte as margin to avoid the (false?) warning Coverity gives us - otherwise */ - char *workend = &work[sizeof(work) - 2]; - - /* Do the actual %-code parsing */ - if(dprintf_Pass1(format, vto, endpos, ap_save)) - return 0; - - end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1() - created for us */ - - f = (char *)format; - while(*f != '\0') { - /* Format spec modifiers. */ - int is_alt; - - /* Width of a field. */ - long width; - - /* Precision of a field. */ - long prec; - - /* Decimal integer is negative. */ - int is_neg; - - /* Base of a number to be written. */ - unsigned long base; - - /* Integral values to be written. */ - mp_uintmax_t num; - - /* Used to convert negative in positive. */ - mp_intmax_t signed_num; - - char *w; - - if(*f != '%') { - /* This isn't a format spec, so write everything out until the next one - OR end of string is reached. */ - do { - OUTCHAR(*f); - } while(*++f && ('%' != *f)); - continue; - } - - ++f; - - /* Check for "%%". Note that although the ANSI standard lists - '%' as a conversion specifier, it says "The complete format - specification shall be `%%'," so we can avoid all the width - and precision processing. */ - if(*f == '%') { - ++f; - OUTCHAR('%'); - continue; - } - - /* If this is a positional parameter, the position must follow immediately - after the %, thus create a %<num>$ sequence */ - param = dprintf_DollarString(f, &f); - - if(!param) - param = param_num; - else - --param; - - param_num++; /* increase this always to allow "%2$s %1$s %s" and then the - third %s will pick the 3rd argument */ - - p = &vto[param]; - - /* pick up the specified width */ - if(p->flags & FLAGS_WIDTHPARAM) { - width = (long)vto[p->width].data.num.as_signed; - param_num++; /* since the width is extracted from a parameter, we - must skip that to get to the next one properly */ - if(width < 0) { - /* "A negative field width is taken as a '-' flag followed by a - positive field width." */ - width = -width; - p->flags |= FLAGS_LEFT; - p->flags &= ~FLAGS_PAD_NIL; - } - } - else - width = p->width; - - /* pick up the specified precision */ - if(p->flags & FLAGS_PRECPARAM) { - prec = (long)vto[p->precision].data.num.as_signed; - param_num++; /* since the precision is extracted from a parameter, we - must skip that to get to the next one properly */ - if(prec < 0) - /* "A negative precision is taken as if the precision were - omitted." */ - prec = -1; - } - else if(p->flags & FLAGS_PREC) - prec = p->precision; - else - prec = -1; - - is_alt = (p->flags & FLAGS_ALT) ? 1 : 0; - - switch(p->type) { - case FORMAT_INT: - num = p->data.num.as_unsigned; - if(p->flags & FLAGS_CHAR) { - /* Character. */ - if(!(p->flags & FLAGS_LEFT)) - while(--width > 0) - OUTCHAR(' '); - OUTCHAR((char) num); - if(p->flags & FLAGS_LEFT) - while(--width > 0) - OUTCHAR(' '); - break; - } - if(p->flags & FLAGS_OCTAL) { - /* Octal unsigned integer. */ - base = 8; - goto unsigned_number; - } - else if(p->flags & FLAGS_HEX) { - /* Hexadecimal unsigned integer. */ - - digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits; - base = 16; - goto unsigned_number; - } - else if(p->flags & FLAGS_UNSIGNED) { - /* Decimal unsigned integer. */ - base = 10; - goto unsigned_number; - } - - /* Decimal integer. */ - base = 10; - - is_neg = (p->data.num.as_signed < (mp_intmax_t)0) ? 1 : 0; - if(is_neg) { - /* signed_num might fail to hold absolute negative minimum by 1 */ - signed_num = p->data.num.as_signed + (mp_intmax_t)1; - signed_num = -signed_num; - num = (mp_uintmax_t)signed_num; - num += (mp_uintmax_t)1; - } - - goto number; - - unsigned_number: - /* Unsigned number of base BASE. */ - is_neg = 0; - - number: - /* Number of base BASE. */ - - /* Supply a default precision if none was given. */ - if(prec == -1) - prec = 1; - - /* Put the number in WORK. */ - w = workend; - while(num > 0) { - *w-- = digits[num % base]; - num /= base; - } - width -= (long)(workend - w); - prec -= (long)(workend - w); - - if(is_alt && base == 8 && prec <= 0) { - *w-- = '0'; - --width; - } - - if(prec > 0) { - width -= prec; - while(prec-- > 0 && w >= work) - *w-- = '0'; - } - - if(is_alt && base == 16) - width -= 2; - - if(is_neg || (p->flags & FLAGS_SHOWSIGN) || (p->flags & FLAGS_SPACE)) - --width; - - if(!(p->flags & FLAGS_LEFT) && !(p->flags & FLAGS_PAD_NIL)) - while(width-- > 0) - OUTCHAR(' '); - - if(is_neg) - OUTCHAR('-'); - else if(p->flags & FLAGS_SHOWSIGN) - OUTCHAR('+'); - else if(p->flags & FLAGS_SPACE) - OUTCHAR(' '); - - if(is_alt && base == 16) { - OUTCHAR('0'); - if(p->flags & FLAGS_UPPER) - OUTCHAR('X'); - else - OUTCHAR('x'); - } - - if(!(p->flags & FLAGS_LEFT) && (p->flags & FLAGS_PAD_NIL)) - while(width-- > 0) - OUTCHAR('0'); - - /* Write the number. */ - while(++w <= workend) { - OUTCHAR(*w); - } - - if(p->flags & FLAGS_LEFT) - while(width-- > 0) - OUTCHAR(' '); - break; - - case FORMAT_STRING: - /* String. */ - { - static const char null[] = "(nil)"; - const char *str; - size_t len; - - str = (char *) p->data.str; - if(!str) { - /* Write null[] if there's space. */ - if(prec == -1 || prec >= (long) sizeof(null) - 1) { - str = null; - len = sizeof(null) - 1; - /* Disable quotes around (nil) */ - p->flags &= (~FLAGS_ALT); - } - else { - str = ""; - len = 0; - } - } - else if(prec != -1) - len = (size_t)prec; - else if(*str == '\0') - len = 0; - else - len = strlen(str); - - width -= (len > LONG_MAX) ? LONG_MAX : (long)len; - - if(p->flags & FLAGS_ALT) - OUTCHAR('"'); - - if(!(p->flags&FLAGS_LEFT)) - while(width-- > 0) - OUTCHAR(' '); - - for(; len && *str; len--) - OUTCHAR(*str++); - if(p->flags&FLAGS_LEFT) - while(width-- > 0) - OUTCHAR(' '); - - if(p->flags & FLAGS_ALT) - OUTCHAR('"'); - } - break; - - case FORMAT_PTR: - /* Generic pointer. */ - { - void *ptr; - ptr = (void *) p->data.ptr; - if(ptr) { - /* If the pointer is not NULL, write it as a %#x spec. */ - base = 16; - digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits; - is_alt = 1; - num = (size_t) ptr; - is_neg = 0; - goto number; - } - else { - /* Write "(nil)" for a nil pointer. */ - static const char strnil[] = "(nil)"; - const char *point; - - width -= (long)(sizeof(strnil) - 1); - if(p->flags & FLAGS_LEFT) - while(width-- > 0) - OUTCHAR(' '); - for(point = strnil; *point != '\0'; ++point) - OUTCHAR(*point); - if(!(p->flags & FLAGS_LEFT)) - while(width-- > 0) - OUTCHAR(' '); - } - } - break; - - case FORMAT_DOUBLE: - { - char formatbuf[32]="%"; - char *fptr = &formatbuf[1]; - size_t left = sizeof(formatbuf)-strlen(formatbuf); - int len; - - width = -1; - if(p->flags & FLAGS_WIDTH) - width = p->width; - else if(p->flags & FLAGS_WIDTHPARAM) - width = (long)vto[p->width].data.num.as_signed; - - prec = -1; - if(p->flags & FLAGS_PREC) - prec = p->precision; - else if(p->flags & FLAGS_PRECPARAM) - prec = (long)vto[p->precision].data.num.as_signed; - - if(p->flags & FLAGS_LEFT) - *fptr++ = '-'; - if(p->flags & FLAGS_SHOWSIGN) - *fptr++ = '+'; - if(p->flags & FLAGS_SPACE) - *fptr++ = ' '; - if(p->flags & FLAGS_ALT) - *fptr++ = '#'; - - *fptr = 0; - - if(width >= 0) { - if(width >= (long)sizeof(work)) - width = sizeof(work)-1; - /* RECURSIVE USAGE */ - len = curl_msnprintf(fptr, left, "%ld", width); - fptr += len; - left -= len; - } - if(prec >= 0) { - /* for each digit in the integer part, we can have one less - precision */ - size_t maxprec = sizeof(work) - 2; - double val = p->data.dnum; - if(width > 0 && prec <= width) - maxprec -= width; - while(val >= 10.0) { - val /= 10; - maxprec--; - } - - if(prec > (long)maxprec) - prec = (long)maxprec-1; - if(prec < 0) - prec = 0; - /* RECURSIVE USAGE */ - len = curl_msnprintf(fptr, left, ".%ld", prec); - fptr += len; - } - if(p->flags & FLAGS_LONG) - *fptr++ = 'l'; - - if(p->flags & FLAGS_FLOATE) - *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'E':'e'); - else if(p->flags & FLAGS_FLOATG) - *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'G' : 'g'); - else - *fptr++ = 'f'; - - *fptr = 0; /* and a final null-termination */ - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wformat-nonliteral" -#endif - /* NOTE NOTE NOTE!! Not all sprintf implementations return number of - output characters */ -#ifdef HAVE_SNPRINTF - (snprintf)(work, sizeof(work), formatbuf, p->data.dnum); -#else - (sprintf)(work, formatbuf, p->data.dnum); -#endif -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - DEBUGASSERT(strlen(work) <= sizeof(work)); - for(fptr = work; *fptr; fptr++) - OUTCHAR(*fptr); - } - break; - - case FORMAT_INTPTR: - /* Answer the count of characters written. */ -#ifdef HAVE_LONG_LONG_TYPE - if(p->flags & FLAGS_LONGLONG) - *(LONG_LONG_TYPE *) p->data.ptr = (LONG_LONG_TYPE)done; - else -#endif - if(p->flags & FLAGS_LONG) - *(long *) p->data.ptr = (long)done; - else if(!(p->flags & FLAGS_SHORT)) - *(int *) p->data.ptr = (int)done; - else - *(short *) p->data.ptr = (short)done; - break; - - default: - break; - } - f = *end++; /* goto end of %-code */ - - } - return done; -} - -/* fputc() look-alike */ -static int addbyter(int output, FILE *data) -{ - struct nsprintf *infop = (struct nsprintf *)data; - unsigned char outc = (unsigned char)output; - - if(infop->length < infop->max) { - /* only do this if we haven't reached max length yet */ - infop->buffer[0] = outc; /* store */ - infop->buffer++; /* increase pointer */ - infop->length++; /* we are now one byte larger */ - return outc; /* fputc() returns like this on success */ - } - return -1; -} - -int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format, - va_list ap_save) -{ - int retcode; - struct nsprintf info; - - info.buffer = buffer; - info.length = 0; - info.max = maxlength; - - retcode = dprintf_formatf(&info, addbyter, format, ap_save); - if(info.max) { - /* we terminate this with a zero byte */ - if(info.max == info.length) { - /* we're at maximum, scrap the last letter */ - info.buffer[-1] = 0; - DEBUGASSERT(retcode); - retcode--; /* don't count the nul byte */ - } - else - info.buffer[0] = 0; - } - return retcode; -} - -int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...) -{ - int retcode; - va_list ap_save; /* argument pointer */ - va_start(ap_save, format); - retcode = curl_mvsnprintf(buffer, maxlength, format, ap_save); - va_end(ap_save); - return retcode; -} - -/* fputc() look-alike */ -static int alloc_addbyter(int output, FILE *data) -{ - struct asprintf *infop = (struct asprintf *)data; - unsigned char outc = (unsigned char)output; - - if(Curl_dyn_addn(infop->b, &outc, 1)) { - infop->fail = 1; - return -1; /* fail */ - } - return outc; /* fputc() returns like this on success */ -} - -extern int Curl_dyn_vprintf(struct dynbuf *dyn, - const char *format, va_list ap_save); - -/* appends the formatted string, returns 0 on success, 1 on error */ -int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save) -{ - struct asprintf info; - info.b = dyn; - info.fail = 0; - - (void)dprintf_formatf(&info, alloc_addbyter, format, ap_save); - if(info.fail) { - Curl_dyn_free(info.b); - return 1; - } - return 0; -} - -char *curl_mvaprintf(const char *format, va_list ap_save) -{ - struct asprintf info; - struct dynbuf dyn; - info.b = &dyn; - Curl_dyn_init(info.b, DYN_APRINTF); - info.fail = 0; - - (void)dprintf_formatf(&info, alloc_addbyter, format, ap_save); - if(info.fail) { - Curl_dyn_free(info.b); - return NULL; - } - if(Curl_dyn_len(info.b)) - return Curl_dyn_ptr(info.b); - return strdup(""); -} - -char *curl_maprintf(const char *format, ...) -{ - va_list ap_save; - char *s; - va_start(ap_save, format); - s = curl_mvaprintf(format, ap_save); - va_end(ap_save); - return s; -} - -static int storebuffer(int output, FILE *data) -{ - char **buffer = (char **)data; - unsigned char outc = (unsigned char)output; - **buffer = outc; - (*buffer)++; - return outc; /* act like fputc() ! */ -} - -int curl_msprintf(char *buffer, const char *format, ...) -{ - va_list ap_save; /* argument pointer */ - int retcode; - va_start(ap_save, format); - retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save); - va_end(ap_save); - *buffer = 0; /* we terminate this with a zero byte */ - return retcode; -} - -int curl_mprintf(const char *format, ...) -{ - int retcode; - va_list ap_save; /* argument pointer */ - va_start(ap_save, format); - - retcode = dprintf_formatf(stdout, fputc, format, ap_save); - va_end(ap_save); - return retcode; -} - -int curl_mfprintf(FILE *whereto, const char *format, ...) -{ - int retcode; - va_list ap_save; /* argument pointer */ - va_start(ap_save, format); - retcode = dprintf_formatf(whereto, fputc, format, ap_save); - va_end(ap_save); - return retcode; -} - -int curl_mvsprintf(char *buffer, const char *format, va_list ap_save) -{ - int retcode; - retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save); - *buffer = 0; /* we terminate this with a zero byte */ - return retcode; -} - -int curl_mvprintf(const char *format, va_list ap_save) -{ - return dprintf_formatf(stdout, fputc, format, ap_save); -} - -int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save) -{ - return dprintf_formatf(whereto, fputc, format, ap_save); -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1999 - 2022, 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
+ *
+ *
+ * Purpose:
+ * A merge of Bjorn Reese's format() function and Daniel's dsprintf()
+ * 1.0. A full blooded printf() clone with full support for <num>$
+ * everywhere (parameters, widths and precisions) including variabled
+ * sized parameters (like doubles, long longs, long doubles and even
+ * void * in 64-bit architectures).
+ *
+ * Current restrictions:
+ * - Max 128 parameters
+ * - No 'long double' support.
+ *
+ * If you ever want truly portable and good *printf() clones, the project that
+ * took on from here is named 'Trio' and you find more details on the trio web
+ * page at https://daniel.haxx.se/projects/trio/
+ */
+
+#include "curl_setup.h"
+#include "dynbuf.h"
+#include <curl/mprintf.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/*
+ * If SIZEOF_SIZE_T has not been defined, default to the size of long.
+ */
+
+#ifdef HAVE_LONGLONG
+# define LONG_LONG_TYPE long long
+# define HAVE_LONG_LONG_TYPE
+#else
+# if defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)
+# define LONG_LONG_TYPE __int64
+# define HAVE_LONG_LONG_TYPE
+# else
+# undef LONG_LONG_TYPE
+# undef HAVE_LONG_LONG_TYPE
+# endif
+#endif
+
+/*
+ * Non-ANSI integer extensions
+ */
+
+#if (defined(__BORLANDC__) && (__BORLANDC__ >= 0x520)) || \
+ (defined(__POCC__) && defined(_MSC_VER)) || \
+ (defined(_WIN32_WCE)) || \
+ (defined(__MINGW32__)) || \
+ (defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64))
+# define MP_HAVE_INT_EXTENSIONS
+#endif
+
+/*
+ * Max integer data types that mprintf.c is capable
+ */
+
+#ifdef HAVE_LONG_LONG_TYPE
+# define mp_intmax_t LONG_LONG_TYPE
+# define mp_uintmax_t unsigned LONG_LONG_TYPE
+#else
+# define mp_intmax_t long
+# define mp_uintmax_t unsigned long
+#endif
+
+#define BUFFSIZE 326 /* buffer for long-to-str and float-to-str calcs, should
+ fit negative DBL_MAX (317 letters) */
+#define MAX_PARAMETERS 128 /* lame static limit */
+
+#ifdef __AMIGA__
+# undef FORMAT_INT
+#endif
+
+/* Lower-case digits. */
+static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+
+/* Upper-case digits. */
+static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+#define OUTCHAR(x) \
+ do { \
+ if(stream((unsigned char)(x), (FILE *)data) != -1) \
+ done++; \
+ else \
+ return done; /* return immediately on failure */ \
+ } while(0)
+
+/* Data type to read from the arglist */
+typedef enum {
+ FORMAT_UNKNOWN = 0,
+ FORMAT_STRING,
+ FORMAT_PTR,
+ FORMAT_INT,
+ FORMAT_INTPTR,
+ FORMAT_LONG,
+ FORMAT_LONGLONG,
+ FORMAT_DOUBLE,
+ FORMAT_LONGDOUBLE,
+ FORMAT_WIDTH /* For internal use */
+} FormatType;
+
+/* conversion and display flags */
+enum {
+ FLAGS_NEW = 0,
+ FLAGS_SPACE = 1<<0,
+ FLAGS_SHOWSIGN = 1<<1,
+ FLAGS_LEFT = 1<<2,
+ FLAGS_ALT = 1<<3,
+ FLAGS_SHORT = 1<<4,
+ FLAGS_LONG = 1<<5,
+ FLAGS_LONGLONG = 1<<6,
+ FLAGS_LONGDOUBLE = 1<<7,
+ FLAGS_PAD_NIL = 1<<8,
+ FLAGS_UNSIGNED = 1<<9,
+ FLAGS_OCTAL = 1<<10,
+ FLAGS_HEX = 1<<11,
+ FLAGS_UPPER = 1<<12,
+ FLAGS_WIDTH = 1<<13, /* '*' or '*<num>$' used */
+ FLAGS_WIDTHPARAM = 1<<14, /* width PARAMETER was specified */
+ FLAGS_PREC = 1<<15, /* precision was specified */
+ FLAGS_PRECPARAM = 1<<16, /* precision PARAMETER was specified */
+ FLAGS_CHAR = 1<<17, /* %c story */
+ FLAGS_FLOATE = 1<<18, /* %e or %E */
+ FLAGS_FLOATG = 1<<19 /* %g or %G */
+};
+
+struct va_stack {
+ FormatType type;
+ int flags;
+ long width; /* width OR width parameter number */
+ long precision; /* precision OR precision parameter number */
+ union {
+ char *str;
+ void *ptr;
+ union {
+ mp_intmax_t as_signed;
+ mp_uintmax_t as_unsigned;
+ } num;
+ double dnum;
+ } data;
+};
+
+struct nsprintf {
+ char *buffer;
+ size_t length;
+ size_t max;
+};
+
+struct asprintf {
+ struct dynbuf *b;
+ bool fail; /* if an alloc has failed and thus the output is not the complete
+ data */
+};
+
+static long dprintf_DollarString(char *input, char **end)
+{
+ int number = 0;
+ while(ISDIGIT(*input)) {
+ if(number < MAX_PARAMETERS) {
+ number *= 10;
+ number += *input - '0';
+ }
+ input++;
+ }
+ if(number <= MAX_PARAMETERS && ('$' == *input)) {
+ *end = ++input;
+ return number;
+ }
+ return 0;
+}
+
+static bool dprintf_IsQualifierNoDollar(const char *fmt)
+{
+#if defined(MP_HAVE_INT_EXTENSIONS)
+ if(!strncmp(fmt, "I32", 3) || !strncmp(fmt, "I64", 3)) {
+ return TRUE;
+ }
+#endif
+
+ switch(*fmt) {
+ case '-': case '+': case ' ': case '#': case '.':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case 'h': case 'l': case 'L': case 'z': case 'q':
+ case '*': case 'O':
+#if defined(MP_HAVE_INT_EXTENSIONS)
+ case 'I':
+#endif
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+/******************************************************************
+ *
+ * Pass 1:
+ * Create an index with the type of each parameter entry and its
+ * value (may vary in size)
+ *
+ * Returns zero on success.
+ *
+ ******************************************************************/
+
+static int dprintf_Pass1(const char *format, struct va_stack *vto,
+ char **endpos, va_list arglist)
+{
+ char *fmt = (char *)format;
+ int param_num = 0;
+ long this_param;
+ long width;
+ long precision;
+ int flags;
+ long max_param = 0;
+ long i;
+
+ while(*fmt) {
+ if(*fmt++ == '%') {
+ if(*fmt == '%') {
+ fmt++;
+ continue; /* while */
+ }
+
+ flags = FLAGS_NEW;
+
+ /* Handle the positional case (N$) */
+
+ param_num++;
+
+ this_param = dprintf_DollarString(fmt, &fmt);
+ if(0 == this_param)
+ /* we got no positional, get the next counter */
+ this_param = param_num;
+
+ if(this_param > max_param)
+ max_param = this_param;
+
+ /*
+ * The parameter with number 'i' should be used. Next, we need
+ * to get SIZE and TYPE of the parameter. Add the information
+ * to our array.
+ */
+
+ width = 0;
+ precision = 0;
+
+ /* Handle the flags */
+
+ while(dprintf_IsQualifierNoDollar(fmt)) {
+#if defined(MP_HAVE_INT_EXTENSIONS)
+ if(!strncmp(fmt, "I32", 3)) {
+ flags |= FLAGS_LONG;
+ fmt += 3;
+ }
+ else if(!strncmp(fmt, "I64", 3)) {
+ flags |= FLAGS_LONGLONG;
+ fmt += 3;
+ }
+ else
+#endif
+
+ switch(*fmt++) {
+ case ' ':
+ flags |= FLAGS_SPACE;
+ break;
+ case '+':
+ flags |= FLAGS_SHOWSIGN;
+ break;
+ case '-':
+ flags |= FLAGS_LEFT;
+ flags &= ~FLAGS_PAD_NIL;
+ break;
+ case '#':
+ flags |= FLAGS_ALT;
+ break;
+ case '.':
+ if('*' == *fmt) {
+ /* The precision is picked from a specified parameter */
+
+ flags |= FLAGS_PRECPARAM;
+ fmt++;
+ param_num++;
+
+ i = dprintf_DollarString(fmt, &fmt);
+ if(i)
+ precision = i;
+ else
+ precision = param_num;
+
+ if(precision > max_param)
+ max_param = precision;
+ }
+ else {
+ flags |= FLAGS_PREC;
+ precision = strtol(fmt, &fmt, 10);
+ }
+ if((flags & (FLAGS_PREC | FLAGS_PRECPARAM)) ==
+ (FLAGS_PREC | FLAGS_PRECPARAM))
+ /* it is not permitted to use both kinds of precision for the same
+ argument */
+ return 1;
+ break;
+ case 'h':
+ flags |= FLAGS_SHORT;
+ break;
+#if defined(MP_HAVE_INT_EXTENSIONS)
+ case 'I':
+#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
+ flags |= FLAGS_LONGLONG;
+#else
+ flags |= FLAGS_LONG;
+#endif
+ break;
+#endif
+ case 'l':
+ if(flags & FLAGS_LONG)
+ flags |= FLAGS_LONGLONG;
+ else
+ flags |= FLAGS_LONG;
+ break;
+ case 'L':
+ flags |= FLAGS_LONGDOUBLE;
+ break;
+ case 'q':
+ flags |= FLAGS_LONGLONG;
+ break;
+ case 'z':
+ /* the code below generates a warning if -Wunreachable-code is
+ used */
+#if (SIZEOF_SIZE_T > SIZEOF_LONG)
+ flags |= FLAGS_LONGLONG;
+#else
+ flags |= FLAGS_LONG;
+#endif
+ break;
+ case 'O':
+#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
+ flags |= FLAGS_LONGLONG;
+#else
+ flags |= FLAGS_LONG;
+#endif
+ break;
+ case '0':
+ if(!(flags & FLAGS_LEFT))
+ flags |= FLAGS_PAD_NIL;
+ /* FALLTHROUGH */
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ flags |= FLAGS_WIDTH;
+ width = strtol(fmt-1, &fmt, 10);
+ break;
+ case '*': /* Special case */
+ flags |= FLAGS_WIDTHPARAM;
+ param_num++;
+
+ i = dprintf_DollarString(fmt, &fmt);
+ if(i)
+ width = i;
+ else
+ width = param_num;
+ if(width > max_param)
+ max_param = width;
+ break;
+ case '\0':
+ fmt--;
+ default:
+ break;
+ }
+ } /* switch */
+
+ /* Handle the specifier */
+
+ i = this_param - 1;
+
+ if((i < 0) || (i >= MAX_PARAMETERS))
+ /* out of allowed range */
+ return 1;
+
+ switch (*fmt) {
+ case 'S':
+ flags |= FLAGS_ALT;
+ /* FALLTHROUGH */
+ case 's':
+ vto[i].type = FORMAT_STRING;
+ break;
+ case 'n':
+ vto[i].type = FORMAT_INTPTR;
+ break;
+ case 'p':
+ vto[i].type = FORMAT_PTR;
+ break;
+ case 'd': case 'i':
+ vto[i].type = FORMAT_INT;
+ break;
+ case 'u':
+ vto[i].type = FORMAT_INT;
+ flags |= FLAGS_UNSIGNED;
+ break;
+ case 'o':
+ vto[i].type = FORMAT_INT;
+ flags |= FLAGS_OCTAL;
+ break;
+ case 'x':
+ vto[i].type = FORMAT_INT;
+ flags |= FLAGS_HEX|FLAGS_UNSIGNED;
+ break;
+ case 'X':
+ vto[i].type = FORMAT_INT;
+ flags |= FLAGS_HEX|FLAGS_UPPER|FLAGS_UNSIGNED;
+ break;
+ case 'c':
+ vto[i].type = FORMAT_INT;
+ flags |= FLAGS_CHAR;
+ break;
+ case 'f':
+ vto[i].type = FORMAT_DOUBLE;
+ break;
+ case 'e':
+ vto[i].type = FORMAT_DOUBLE;
+ flags |= FLAGS_FLOATE;
+ break;
+ case 'E':
+ vto[i].type = FORMAT_DOUBLE;
+ flags |= FLAGS_FLOATE|FLAGS_UPPER;
+ break;
+ case 'g':
+ vto[i].type = FORMAT_DOUBLE;
+ flags |= FLAGS_FLOATG;
+ break;
+ case 'G':
+ vto[i].type = FORMAT_DOUBLE;
+ flags |= FLAGS_FLOATG|FLAGS_UPPER;
+ break;
+ default:
+ vto[i].type = FORMAT_UNKNOWN;
+ break;
+ } /* switch */
+
+ vto[i].flags = flags;
+ vto[i].width = width;
+ vto[i].precision = precision;
+
+ if(flags & FLAGS_WIDTHPARAM) {
+ /* we have the width specified from a parameter, so we make that
+ parameter's info setup properly */
+ long k = width - 1;
+ if((k < 0) || (k >= MAX_PARAMETERS))
+ /* out of allowed range */
+ return 1;
+ vto[i].width = k;
+ vto[k].type = FORMAT_WIDTH;
+ vto[k].flags = FLAGS_NEW;
+ /* can't use width or precision of width! */
+ vto[k].width = 0;
+ vto[k].precision = 0;
+ }
+ if(flags & FLAGS_PRECPARAM) {
+ /* we have the precision specified from a parameter, so we make that
+ parameter's info setup properly */
+ long k = precision - 1;
+ if((k < 0) || (k >= MAX_PARAMETERS))
+ /* out of allowed range */
+ return 1;
+ vto[i].precision = k;
+ vto[k].type = FORMAT_WIDTH;
+ vto[k].flags = FLAGS_NEW;
+ /* can't use width or precision of width! */
+ vto[k].width = 0;
+ vto[k].precision = 0;
+ }
+ *endpos++ = fmt + ((*fmt == '\0') ? 0 : 1); /* end of this sequence */
+ }
+ }
+
+ /* Read the arg list parameters into our data list */
+ for(i = 0; i<max_param; i++) {
+ /* Width/precision arguments must be read before the main argument
+ they are attached to */
+ if(vto[i].flags & FLAGS_WIDTHPARAM) {
+ vto[vto[i].width].data.num.as_signed =
+ (mp_intmax_t)va_arg(arglist, int);
+ }
+ if(vto[i].flags & FLAGS_PRECPARAM) {
+ vto[vto[i].precision].data.num.as_signed =
+ (mp_intmax_t)va_arg(arglist, int);
+ }
+
+ switch(vto[i].type) {
+ case FORMAT_STRING:
+ vto[i].data.str = va_arg(arglist, char *);
+ break;
+
+ case FORMAT_INTPTR:
+ case FORMAT_UNKNOWN:
+ case FORMAT_PTR:
+ vto[i].data.ptr = va_arg(arglist, void *);
+ break;
+
+ case FORMAT_INT:
+#ifdef HAVE_LONG_LONG_TYPE
+ if((vto[i].flags & FLAGS_LONGLONG) && (vto[i].flags & FLAGS_UNSIGNED))
+ vto[i].data.num.as_unsigned =
+ (mp_uintmax_t)va_arg(arglist, mp_uintmax_t);
+ else if(vto[i].flags & FLAGS_LONGLONG)
+ vto[i].data.num.as_signed =
+ (mp_intmax_t)va_arg(arglist, mp_intmax_t);
+ else
+#endif
+ {
+ if((vto[i].flags & FLAGS_LONG) && (vto[i].flags & FLAGS_UNSIGNED))
+ vto[i].data.num.as_unsigned =
+ (mp_uintmax_t)va_arg(arglist, unsigned long);
+ else if(vto[i].flags & FLAGS_LONG)
+ vto[i].data.num.as_signed =
+ (mp_intmax_t)va_arg(arglist, long);
+ else if(vto[i].flags & FLAGS_UNSIGNED)
+ vto[i].data.num.as_unsigned =
+ (mp_uintmax_t)va_arg(arglist, unsigned int);
+ else
+ vto[i].data.num.as_signed =
+ (mp_intmax_t)va_arg(arglist, int);
+ }
+ break;
+
+ case FORMAT_DOUBLE:
+ vto[i].data.dnum = va_arg(arglist, double);
+ break;
+
+ case FORMAT_WIDTH:
+ /* Argument has been read. Silently convert it into an integer
+ * for later use
+ */
+ vto[i].type = FORMAT_INT;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return 0;
+
+}
+
+static int dprintf_formatf(
+ void *data, /* untouched by format(), just sent to the stream() function in
+ the second argument */
+ /* function pointer called for each output character */
+ int (*stream)(int, FILE *),
+ const char *format, /* %-formatted string */
+ va_list ap_save) /* list of parameters */
+{
+ /* Base-36 digits for numbers. */
+ const char *digits = lower_digits;
+
+ /* Pointer into the format string. */
+ char *f;
+
+ /* Number of characters written. */
+ int done = 0;
+
+ long param; /* current parameter to read */
+ long param_num = 0; /* parameter counter */
+
+ struct va_stack vto[MAX_PARAMETERS];
+ char *endpos[MAX_PARAMETERS];
+ char **end;
+ char work[BUFFSIZE];
+ struct va_stack *p;
+
+ /* 'workend' points to the final buffer byte position, but with an extra
+ byte as margin to avoid the (false?) warning Coverity gives us
+ otherwise */
+ char *workend = &work[sizeof(work) - 2];
+
+ /* Do the actual %-code parsing */
+ if(dprintf_Pass1(format, vto, endpos, ap_save))
+ return 0;
+
+ end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1()
+ created for us */
+
+ f = (char *)format;
+ while(*f != '\0') {
+ /* Format spec modifiers. */
+ int is_alt;
+
+ /* Width of a field. */
+ long width;
+
+ /* Precision of a field. */
+ long prec;
+
+ /* Decimal integer is negative. */
+ int is_neg;
+
+ /* Base of a number to be written. */
+ unsigned long base;
+
+ /* Integral values to be written. */
+ mp_uintmax_t num;
+
+ /* Used to convert negative in positive. */
+ mp_intmax_t signed_num;
+
+ char *w;
+
+ if(*f != '%') {
+ /* This isn't a format spec, so write everything out until the next one
+ OR end of string is reached. */
+ do {
+ OUTCHAR(*f);
+ } while(*++f && ('%' != *f));
+ continue;
+ }
+
+ ++f;
+
+ /* Check for "%%". Note that although the ANSI standard lists
+ '%' as a conversion specifier, it says "The complete format
+ specification shall be `%%'," so we can avoid all the width
+ and precision processing. */
+ if(*f == '%') {
+ ++f;
+ OUTCHAR('%');
+ continue;
+ }
+
+ /* If this is a positional parameter, the position must follow immediately
+ after the %, thus create a %<num>$ sequence */
+ param = dprintf_DollarString(f, &f);
+
+ if(!param)
+ param = param_num;
+ else
+ --param;
+
+ param_num++; /* increase this always to allow "%2$s %1$s %s" and then the
+ third %s will pick the 3rd argument */
+
+ p = &vto[param];
+
+ /* pick up the specified width */
+ if(p->flags & FLAGS_WIDTHPARAM) {
+ width = (long)vto[p->width].data.num.as_signed;
+ param_num++; /* since the width is extracted from a parameter, we
+ must skip that to get to the next one properly */
+ if(width < 0) {
+ /* "A negative field width is taken as a '-' flag followed by a
+ positive field width." */
+ width = -width;
+ p->flags |= FLAGS_LEFT;
+ p->flags &= ~FLAGS_PAD_NIL;
+ }
+ }
+ else
+ width = p->width;
+
+ /* pick up the specified precision */
+ if(p->flags & FLAGS_PRECPARAM) {
+ prec = (long)vto[p->precision].data.num.as_signed;
+ param_num++; /* since the precision is extracted from a parameter, we
+ must skip that to get to the next one properly */
+ if(prec < 0)
+ /* "A negative precision is taken as if the precision were
+ omitted." */
+ prec = -1;
+ }
+ else if(p->flags & FLAGS_PREC)
+ prec = p->precision;
+ else
+ prec = -1;
+
+ is_alt = (p->flags & FLAGS_ALT) ? 1 : 0;
+
+ switch(p->type) {
+ case FORMAT_INT:
+ num = p->data.num.as_unsigned;
+ if(p->flags & FLAGS_CHAR) {
+ /* Character. */
+ if(!(p->flags & FLAGS_LEFT))
+ while(--width > 0)
+ OUTCHAR(' ');
+ OUTCHAR((char) num);
+ if(p->flags & FLAGS_LEFT)
+ while(--width > 0)
+ OUTCHAR(' ');
+ break;
+ }
+ if(p->flags & FLAGS_OCTAL) {
+ /* Octal unsigned integer. */
+ base = 8;
+ goto unsigned_number;
+ }
+ else if(p->flags & FLAGS_HEX) {
+ /* Hexadecimal unsigned integer. */
+
+ digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
+ base = 16;
+ goto unsigned_number;
+ }
+ else if(p->flags & FLAGS_UNSIGNED) {
+ /* Decimal unsigned integer. */
+ base = 10;
+ goto unsigned_number;
+ }
+
+ /* Decimal integer. */
+ base = 10;
+
+ is_neg = (p->data.num.as_signed < (mp_intmax_t)0) ? 1 : 0;
+ if(is_neg) {
+ /* signed_num might fail to hold absolute negative minimum by 1 */
+ signed_num = p->data.num.as_signed + (mp_intmax_t)1;
+ signed_num = -signed_num;
+ num = (mp_uintmax_t)signed_num;
+ num += (mp_uintmax_t)1;
+ }
+
+ goto number;
+
+ unsigned_number:
+ /* Unsigned number of base BASE. */
+ is_neg = 0;
+
+ number:
+ /* Number of base BASE. */
+
+ /* Supply a default precision if none was given. */
+ if(prec == -1)
+ prec = 1;
+
+ /* Put the number in WORK. */
+ w = workend;
+ while(num > 0) {
+ *w-- = digits[num % base];
+ num /= base;
+ }
+ width -= (long)(workend - w);
+ prec -= (long)(workend - w);
+
+ if(is_alt && base == 8 && prec <= 0) {
+ *w-- = '0';
+ --width;
+ }
+
+ if(prec > 0) {
+ width -= prec;
+ while(prec-- > 0 && w >= work)
+ *w-- = '0';
+ }
+
+ if(is_alt && base == 16)
+ width -= 2;
+
+ if(is_neg || (p->flags & FLAGS_SHOWSIGN) || (p->flags & FLAGS_SPACE))
+ --width;
+
+ if(!(p->flags & FLAGS_LEFT) && !(p->flags & FLAGS_PAD_NIL))
+ while(width-- > 0)
+ OUTCHAR(' ');
+
+ if(is_neg)
+ OUTCHAR('-');
+ else if(p->flags & FLAGS_SHOWSIGN)
+ OUTCHAR('+');
+ else if(p->flags & FLAGS_SPACE)
+ OUTCHAR(' ');
+
+ if(is_alt && base == 16) {
+ OUTCHAR('0');
+ if(p->flags & FLAGS_UPPER)
+ OUTCHAR('X');
+ else
+ OUTCHAR('x');
+ }
+
+ if(!(p->flags & FLAGS_LEFT) && (p->flags & FLAGS_PAD_NIL))
+ while(width-- > 0)
+ OUTCHAR('0');
+
+ /* Write the number. */
+ while(++w <= workend) {
+ OUTCHAR(*w);
+ }
+
+ if(p->flags & FLAGS_LEFT)
+ while(width-- > 0)
+ OUTCHAR(' ');
+ break;
+
+ case FORMAT_STRING:
+ /* String. */
+ {
+ static const char null[] = "(nil)";
+ const char *str;
+ size_t len;
+
+ str = (char *) p->data.str;
+ if(!str) {
+ /* Write null[] if there's space. */
+ if(prec == -1 || prec >= (long) sizeof(null) - 1) {
+ str = null;
+ len = sizeof(null) - 1;
+ /* Disable quotes around (nil) */
+ p->flags &= (~FLAGS_ALT);
+ }
+ else {
+ str = "";
+ len = 0;
+ }
+ }
+ else if(prec != -1)
+ len = (size_t)prec;
+ else if(*str == '\0')
+ len = 0;
+ else
+ len = strlen(str);
+
+ width -= (len > LONG_MAX) ? LONG_MAX : (long)len;
+
+ if(p->flags & FLAGS_ALT)
+ OUTCHAR('"');
+
+ if(!(p->flags&FLAGS_LEFT))
+ while(width-- > 0)
+ OUTCHAR(' ');
+
+ for(; len && *str; len--)
+ OUTCHAR(*str++);
+ if(p->flags&FLAGS_LEFT)
+ while(width-- > 0)
+ OUTCHAR(' ');
+
+ if(p->flags & FLAGS_ALT)
+ OUTCHAR('"');
+ }
+ break;
+
+ case FORMAT_PTR:
+ /* Generic pointer. */
+ {
+ void *ptr;
+ ptr = (void *) p->data.ptr;
+ if(ptr) {
+ /* If the pointer is not NULL, write it as a %#x spec. */
+ base = 16;
+ digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
+ is_alt = 1;
+ num = (size_t) ptr;
+ is_neg = 0;
+ goto number;
+ }
+ else {
+ /* Write "(nil)" for a nil pointer. */
+ static const char strnil[] = "(nil)";
+ const char *point;
+
+ width -= (long)(sizeof(strnil) - 1);
+ if(p->flags & FLAGS_LEFT)
+ while(width-- > 0)
+ OUTCHAR(' ');
+ for(point = strnil; *point != '\0'; ++point)
+ OUTCHAR(*point);
+ if(!(p->flags & FLAGS_LEFT))
+ while(width-- > 0)
+ OUTCHAR(' ');
+ }
+ }
+ break;
+
+ case FORMAT_DOUBLE:
+ {
+ char formatbuf[32]="%";
+ char *fptr = &formatbuf[1];
+ size_t left = sizeof(formatbuf)-strlen(formatbuf);
+ int len;
+
+ width = -1;
+ if(p->flags & FLAGS_WIDTH)
+ width = p->width;
+ else if(p->flags & FLAGS_WIDTHPARAM)
+ width = (long)vto[p->width].data.num.as_signed;
+
+ prec = -1;
+ if(p->flags & FLAGS_PREC)
+ prec = p->precision;
+ else if(p->flags & FLAGS_PRECPARAM)
+ prec = (long)vto[p->precision].data.num.as_signed;
+
+ if(p->flags & FLAGS_LEFT)
+ *fptr++ = '-';
+ if(p->flags & FLAGS_SHOWSIGN)
+ *fptr++ = '+';
+ if(p->flags & FLAGS_SPACE)
+ *fptr++ = ' ';
+ if(p->flags & FLAGS_ALT)
+ *fptr++ = '#';
+
+ *fptr = 0;
+
+ if(width >= 0) {
+ if(width >= (long)sizeof(work))
+ width = sizeof(work)-1;
+ /* RECURSIVE USAGE */
+ len = curl_msnprintf(fptr, left, "%ld", width);
+ fptr += len;
+ left -= len;
+ }
+ if(prec >= 0) {
+ /* for each digit in the integer part, we can have one less
+ precision */
+ size_t maxprec = sizeof(work) - 2;
+ double val = p->data.dnum;
+ if(width > 0 && prec <= width)
+ maxprec -= width;
+ while(val >= 10.0) {
+ val /= 10;
+ maxprec--;
+ }
+
+ if(prec > (long)maxprec)
+ prec = (long)maxprec-1;
+ if(prec < 0)
+ prec = 0;
+ /* RECURSIVE USAGE */
+ len = curl_msnprintf(fptr, left, ".%ld", prec);
+ fptr += len;
+ }
+ if(p->flags & FLAGS_LONG)
+ *fptr++ = 'l';
+
+ if(p->flags & FLAGS_FLOATE)
+ *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'E':'e');
+ else if(p->flags & FLAGS_FLOATG)
+ *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'G' : 'g');
+ else
+ *fptr++ = 'f';
+
+ *fptr = 0; /* and a final null-termination */
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-nonliteral"
+#endif
+ /* NOTE NOTE NOTE!! Not all sprintf implementations return number of
+ output characters */
+#ifdef HAVE_SNPRINTF
+ (snprintf)(work, sizeof(work), formatbuf, p->data.dnum);
+#else
+ (sprintf)(work, formatbuf, p->data.dnum);
+#endif
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ DEBUGASSERT(strlen(work) <= sizeof(work));
+ for(fptr = work; *fptr; fptr++)
+ OUTCHAR(*fptr);
+ }
+ break;
+
+ case FORMAT_INTPTR:
+ /* Answer the count of characters written. */
+#ifdef HAVE_LONG_LONG_TYPE
+ if(p->flags & FLAGS_LONGLONG)
+ *(LONG_LONG_TYPE *) p->data.ptr = (LONG_LONG_TYPE)done;
+ else
+#endif
+ if(p->flags & FLAGS_LONG)
+ *(long *) p->data.ptr = (long)done;
+ else if(!(p->flags & FLAGS_SHORT))
+ *(int *) p->data.ptr = (int)done;
+ else
+ *(short *) p->data.ptr = (short)done;
+ break;
+
+ default:
+ break;
+ }
+ f = *end++; /* goto end of %-code */
+
+ }
+ return done;
+}
+
+/* fputc() look-alike */
+static int addbyter(int output, FILE *data)
+{
+ struct nsprintf *infop = (struct nsprintf *)data;
+ unsigned char outc = (unsigned char)output;
+
+ if(infop->length < infop->max) {
+ /* only do this if we haven't reached max length yet */
+ infop->buffer[0] = outc; /* store */
+ infop->buffer++; /* increase pointer */
+ infop->length++; /* we are now one byte larger */
+ return outc; /* fputc() returns like this on success */
+ }
+ return -1;
+}
+
+int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
+ va_list ap_save)
+{
+ int retcode;
+ struct nsprintf info;
+
+ info.buffer = buffer;
+ info.length = 0;
+ info.max = maxlength;
+
+ retcode = dprintf_formatf(&info, addbyter, format, ap_save);
+ if(info.max) {
+ /* we terminate this with a zero byte */
+ if(info.max == info.length) {
+ /* we're at maximum, scrap the last letter */
+ info.buffer[-1] = 0;
+ DEBUGASSERT(retcode);
+ retcode--; /* don't count the nul byte */
+ }
+ else
+ info.buffer[0] = 0;
+ }
+ return retcode;
+}
+
+int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
+{
+ int retcode;
+ va_list ap_save; /* argument pointer */
+ va_start(ap_save, format);
+ retcode = curl_mvsnprintf(buffer, maxlength, format, ap_save);
+ va_end(ap_save);
+ return retcode;
+}
+
+/* fputc() look-alike */
+static int alloc_addbyter(int output, FILE *data)
+{
+ struct asprintf *infop = (struct asprintf *)data;
+ unsigned char outc = (unsigned char)output;
+
+ if(Curl_dyn_addn(infop->b, &outc, 1)) {
+ infop->fail = 1;
+ return -1; /* fail */
+ }
+ return outc; /* fputc() returns like this on success */
+}
+
+extern int Curl_dyn_vprintf(struct dynbuf *dyn,
+ const char *format, va_list ap_save);
+
+/* appends the formatted string, returns 0 on success, 1 on error */
+int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save)
+{
+ struct asprintf info;
+ info.b = dyn;
+ info.fail = 0;
+
+ (void)dprintf_formatf(&info, alloc_addbyter, format, ap_save);
+ if(info.fail) {
+ Curl_dyn_free(info.b);
+ return 1;
+ }
+ return 0;
+}
+
+char *curl_mvaprintf(const char *format, va_list ap_save)
+{
+ struct asprintf info;
+ struct dynbuf dyn;
+ info.b = &dyn;
+ Curl_dyn_init(info.b, DYN_APRINTF);
+ info.fail = 0;
+
+ (void)dprintf_formatf(&info, alloc_addbyter, format, ap_save);
+ if(info.fail) {
+ Curl_dyn_free(info.b);
+ return NULL;
+ }
+ if(Curl_dyn_len(info.b))
+ return Curl_dyn_ptr(info.b);
+ return strdup("");
+}
+
+char *curl_maprintf(const char *format, ...)
+{
+ va_list ap_save;
+ char *s;
+ va_start(ap_save, format);
+ s = curl_mvaprintf(format, ap_save);
+ va_end(ap_save);
+ return s;
+}
+
+static int storebuffer(int output, FILE *data)
+{
+ char **buffer = (char **)data;
+ unsigned char outc = (unsigned char)output;
+ **buffer = outc;
+ (*buffer)++;
+ return outc; /* act like fputc() ! */
+}
+
+int curl_msprintf(char *buffer, const char *format, ...)
+{
+ va_list ap_save; /* argument pointer */
+ int retcode;
+ va_start(ap_save, format);
+ retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
+ va_end(ap_save);
+ *buffer = 0; /* we terminate this with a zero byte */
+ return retcode;
+}
+
+int curl_mprintf(const char *format, ...)
+{
+ int retcode;
+ va_list ap_save; /* argument pointer */
+ va_start(ap_save, format);
+
+ retcode = dprintf_formatf(stdout, fputc, format, ap_save);
+ va_end(ap_save);
+ return retcode;
+}
+
+int curl_mfprintf(FILE *whereto, const char *format, ...)
+{
+ int retcode;
+ va_list ap_save; /* argument pointer */
+ va_start(ap_save, format);
+ retcode = dprintf_formatf(whereto, fputc, format, ap_save);
+ va_end(ap_save);
+ return retcode;
+}
+
+int curl_mvsprintf(char *buffer, const char *format, va_list ap_save)
+{
+ int retcode;
+ retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
+ *buffer = 0; /* we terminate this with a zero byte */
+ return retcode;
+}
+
+int curl_mvprintf(const char *format, va_list ap_save)
+{
+ return dprintf_formatf(stdout, fputc, format, ap_save);
+}
+
+int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save)
+{
+ return dprintf_formatf(whereto, fputc, format, ap_save);
+}
diff --git a/libs/libcurl/src/mqtt.c b/libs/libcurl/src/mqtt.c index 4f3d14386f..92a6783d5f 100644 --- a/libs/libcurl/src/mqtt.c +++ b/libs/libcurl/src/mqtt.c @@ -1,823 +1,823 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2019, Björn Stenberg, <bjorn@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_setup.h" - -#ifndef CURL_DISABLE_MQTT - -#include "urldata.h" -#include <curl/curl.h> -#include "transfer.h" -#include "sendf.h" -#include "progress.h" -#include "mqtt.h" -#include "select.h" -#include "strdup.h" -#include "url.h" -#include "escape.h" -#include "warnless.h" -#include "curl_printf.h" -#include "curl_memory.h" -#include "multiif.h" -#include "rand.h" - -/* The last #include file should be: */ -#include "memdebug.h" - -#define MQTT_MSG_CONNECT 0x10 -#define MQTT_MSG_CONNACK 0x20 -#define MQTT_MSG_PUBLISH 0x30 -#define MQTT_MSG_SUBSCRIBE 0x82 -#define MQTT_MSG_SUBACK 0x90 -#define MQTT_MSG_DISCONNECT 0xe0 - -#define MQTT_CONNACK_LEN 2 -#define MQTT_SUBACK_LEN 3 -#define MQTT_CLIENTID_LEN 12 /* "curl0123abcd" */ - -/* - * Forward declarations. - */ - -static CURLcode mqtt_do(struct Curl_easy *data, bool *done); -static CURLcode mqtt_done(struct Curl_easy *data, - CURLcode status, bool premature); -static CURLcode mqtt_doing(struct Curl_easy *data, bool *done); -static int mqtt_getsock(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t *sock); -static CURLcode mqtt_setup_conn(struct Curl_easy *data, - struct connectdata *conn); - -/* - * MQTT protocol handler. - */ - -const struct Curl_handler Curl_handler_mqtt = { - "MQTT", /* scheme */ - mqtt_setup_conn, /* setup_connection */ - mqtt_do, /* do_it */ - mqtt_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - mqtt_doing, /* doing */ - ZERO_NULL, /* proto_getsock */ - mqtt_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_MQTT, /* defport */ - CURLPROTO_MQTT, /* protocol */ - CURLPROTO_MQTT, /* family */ - PROTOPT_NONE /* flags */ -}; - -static CURLcode mqtt_setup_conn(struct Curl_easy *data, - struct connectdata *conn) -{ - /* allocate the HTTP-specific struct for the Curl_easy, only to survive - during this request */ - struct MQTT *mq; - (void)conn; - DEBUGASSERT(data->req.p.mqtt == NULL); - - mq = calloc(1, sizeof(struct MQTT)); - if(!mq) - return CURLE_OUT_OF_MEMORY; - data->req.p.mqtt = mq; - return CURLE_OK; -} - -static CURLcode mqtt_send(struct Curl_easy *data, - char *buf, size_t len) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; - struct MQTT *mq = data->req.p.mqtt; - ssize_t n; - result = Curl_write(data, sockfd, buf, len, &n); - if(!result) - Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n); - if(len != (size_t)n) { - size_t nsend = len - n; - char *sendleftovers = Curl_memdup(&buf[n], nsend); - if(!sendleftovers) - return CURLE_OUT_OF_MEMORY; - mq->sendleftovers = sendleftovers; - mq->nsend = nsend; - } - else { - mq->sendleftovers = NULL; - mq->nsend = 0; - } - return result; -} - -/* Generic function called by the multi interface to figure out what socket(s) - to wait for and for what actions during the DOING and PROTOCONNECT - states */ -static int mqtt_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *sock) -{ - (void)data; - sock[0] = conn->sock[FIRSTSOCKET]; - return GETSOCK_READSOCK(FIRSTSOCKET); -} - -static int mqtt_encode_len(char *buf, size_t len) -{ - unsigned char encoded; - int i; - - for(i = 0; (len > 0) && (i<4); i++) { - encoded = len % 0x80; - len /= 0x80; - if(len) - encoded |= 0x80; - buf[i] = encoded; - } - - return i; -} - -/* add the passwd to the CONNECT packet */ -static int add_passwd(const char *passwd, const size_t plen, - char *pkt, const size_t start, int remain_pos) -{ - /* magic number that need to be set properly */ - const size_t conn_flags_pos = remain_pos + 8; - if(plen > 0xffff) - return 1; - - /* set password flag */ - pkt[conn_flags_pos] |= 0x40; - - /* length of password provided */ - pkt[start] = (char)((plen >> 8) & 0xFF); - pkt[start + 1] = (char)(plen & 0xFF); - memcpy(&pkt[start + 2], passwd, plen); - return 0; -} - -/* add user to the CONNECT packet */ -static int add_user(const char *username, const size_t ulen, - unsigned char *pkt, const size_t start, int remain_pos) -{ - /* magic number that need to be set properly */ - const size_t conn_flags_pos = remain_pos + 8; - if(ulen > 0xffff) - return 1; - - /* set username flag */ - pkt[conn_flags_pos] |= 0x80; - /* length of username provided */ - pkt[start] = (unsigned char)((ulen >> 8) & 0xFF); - pkt[start + 1] = (unsigned char)(ulen & 0xFF); - memcpy(&pkt[start + 2], username, ulen); - return 0; -} - -/* add client ID to the CONNECT packet */ -static int add_client_id(const char *client_id, const size_t client_id_len, - char *pkt, const size_t start) -{ - if(client_id_len != MQTT_CLIENTID_LEN) - return 1; - pkt[start] = 0x00; - pkt[start + 1] = MQTT_CLIENTID_LEN; - memcpy(&pkt[start + 2], client_id, MQTT_CLIENTID_LEN); - return 0; -} - -/* Set initial values of CONNECT packet */ -static int init_connpack(char *packet, char *remain, int remain_pos) -{ - /* Fixed header starts */ - /* packet type */ - packet[0] = MQTT_MSG_CONNECT; - /* remaining length field */ - memcpy(&packet[1], remain, remain_pos); - /* Fixed header ends */ - - /* Variable header starts */ - /* protocol length */ - packet[remain_pos + 1] = 0x00; - packet[remain_pos + 2] = 0x04; - /* protocol name */ - packet[remain_pos + 3] = 'M'; - packet[remain_pos + 4] = 'Q'; - packet[remain_pos + 5] = 'T'; - packet[remain_pos + 6] = 'T'; - /* protocol level */ - packet[remain_pos + 7] = 0x04; - /* CONNECT flag: CleanSession */ - packet[remain_pos + 8] = 0x02; - /* keep-alive 0 = disabled */ - packet[remain_pos + 9] = 0x00; - packet[remain_pos + 10] = 0x3c; - /*end of variable header*/ - return remain_pos + 10; -} - -static CURLcode mqtt_connect(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - int pos = 0; - int rc = 0; - /*remain length*/ - int remain_pos = 0; - char remain[4] = {0}; - size_t packetlen = 0; - size_t payloadlen = 0; - size_t start_user = 0; - size_t start_pwd = 0; - char client_id[MQTT_CLIENTID_LEN + 1] = "curl"; - const size_t clen = strlen("curl"); - char *packet = NULL; - - /* extracting username from request */ - const char *username = data->state.aptr.user ? - data->state.aptr.user : ""; - const size_t ulen = strlen(username); - /* extracting password from request */ - const char *passwd = data->state.aptr.passwd ? - data->state.aptr.passwd : ""; - const size_t plen = strlen(passwd); - - payloadlen = ulen + plen + MQTT_CLIENTID_LEN + 2; - /* The plus 2 are for the MSB and LSB describing the length of the string to - * be added on the payload. Refer to spec 1.5.2 and 1.5.4 */ - if(ulen) - payloadlen += 2; - if(plen) - payloadlen += 2; - - /* getting how much occupy the remain length */ - remain_pos = mqtt_encode_len(remain, payloadlen + 10); - - /* 10 length of variable header and 1 the first byte of the fixed header */ - packetlen = payloadlen + 10 + remain_pos + 1; - - /* allocating packet */ - if(packetlen > 268435455) - return CURLE_WEIRD_SERVER_REPLY; - packet = malloc(packetlen); - if(!packet) - return CURLE_OUT_OF_MEMORY; - memset(packet, 0, packetlen); - - /* set initial values for the CONNECT packet */ - pos = init_connpack(packet, remain, remain_pos); - - result = Curl_rand_hex(data, (unsigned char *)&client_id[clen], - MQTT_CLIENTID_LEN - clen + 1); - /* add client id */ - rc = add_client_id(client_id, strlen(client_id), packet, pos + 1); - if(rc) { - failf(data, "Client ID length mismatched: [%lu]", strlen(client_id)); - result = CURLE_WEIRD_SERVER_REPLY; - goto end; - } - infof(data, "Using client id '%s'", client_id); - - /* position where starts the user payload */ - start_user = pos + 3 + MQTT_CLIENTID_LEN; - /* position where starts the password payload */ - start_pwd = start_user + ulen; - /* if user name was provided, add it to the packet */ - if(ulen) { - start_pwd += 2; - - rc = add_user(username, ulen, - (unsigned char *)packet, start_user, remain_pos); - if(rc) { - failf(data, "Username is too large: [%lu]", ulen); - result = CURLE_WEIRD_SERVER_REPLY; - goto end; - } - } - - /* if passwd was provided, add it to the packet */ - if(plen) { - rc = add_passwd(passwd, plen, packet, start_pwd, remain_pos); - if(rc) { - failf(data, "Password is too large: [%lu]", plen); - result = CURLE_WEIRD_SERVER_REPLY; - goto end; - } - } - - if(!result) - result = mqtt_send(data, packet, packetlen); - -end: - if(packet) - free(packet); - Curl_safefree(data->state.aptr.user); - Curl_safefree(data->state.aptr.passwd); - return result; -} - -static CURLcode mqtt_disconnect(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct MQTT *mq = data->req.p.mqtt; - result = mqtt_send(data, (char *)"\xe0\x00", 2); - Curl_safefree(mq->sendleftovers); - return result; -} - -static CURLcode mqtt_verify_connack(struct Curl_easy *data) -{ - CURLcode result; - struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; - unsigned char readbuf[MQTT_CONNACK_LEN]; - ssize_t nread; - - result = Curl_read(data, sockfd, (char *)readbuf, MQTT_CONNACK_LEN, &nread); - if(result) - goto fail; - - Curl_debug(data, CURLINFO_HEADER_IN, (char *)readbuf, (size_t)nread); - - /* fixme */ - if(nread < MQTT_CONNACK_LEN) { - result = CURLE_WEIRD_SERVER_REPLY; - goto fail; - } - - /* verify CONNACK */ - if(readbuf[0] != 0x00 || readbuf[1] != 0x00) { - failf(data, "Expected %02x%02x but got %02x%02x", - 0x00, 0x00, readbuf[0], readbuf[1]); - result = CURLE_WEIRD_SERVER_REPLY; - } - -fail: - return result; -} - -static CURLcode mqtt_get_topic(struct Curl_easy *data, - char **topic, size_t *topiclen) -{ - char *path = data->state.up.path; - CURLcode result = CURLE_URL_MALFORMAT; - if(strlen(path) > 1) { - result = Curl_urldecode(path + 1, 0, topic, topiclen, REJECT_NADA); - if(!result && (*topiclen > 0xffff)) { - failf(data, "Too long MQTT topic"); - result = CURLE_URL_MALFORMAT; - } - } - else - failf(data, "No MQTT topic found. Forgot to URL encode it?"); - - return result; -} - -static CURLcode mqtt_subscribe(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - char *topic = NULL; - size_t topiclen; - unsigned char *packet = NULL; - size_t packetlen; - char encodedsize[4]; - size_t n; - struct connectdata *conn = data->conn; - - result = mqtt_get_topic(data, &topic, &topiclen); - if(result) - goto fail; - - conn->proto.mqtt.packetid++; - - packetlen = topiclen + 5; /* packetid + topic (has a two byte length field) - + 2 bytes topic length + QoS byte */ - n = mqtt_encode_len((char *)encodedsize, packetlen); - packetlen += n + 1; /* add one for the control packet type byte */ - - packet = malloc(packetlen); - if(!packet) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } - - packet[0] = MQTT_MSG_SUBSCRIBE; - memcpy(&packet[1], encodedsize, n); - packet[1 + n] = (conn->proto.mqtt.packetid >> 8) & 0xff; - packet[2 + n] = conn->proto.mqtt.packetid & 0xff; - packet[3 + n] = (topiclen >> 8) & 0xff; - packet[4 + n ] = topiclen & 0xff; - memcpy(&packet[5 + n], topic, topiclen); - packet[5 + n + topiclen] = 0; /* QoS zero */ - - result = mqtt_send(data, (char *)packet, packetlen); - -fail: - free(topic); - free(packet); - return result; -} - -/* - * Called when the first byte was already read. - */ -static CURLcode mqtt_verify_suback(struct Curl_easy *data) -{ - CURLcode result; - struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; - unsigned char readbuf[MQTT_SUBACK_LEN]; - ssize_t nread; - struct mqtt_conn *mqtt = &conn->proto.mqtt; - - result = Curl_read(data, sockfd, (char *)readbuf, MQTT_SUBACK_LEN, &nread); - if(result) - goto fail; - - Curl_debug(data, CURLINFO_HEADER_IN, (char *)readbuf, (size_t)nread); - - /* fixme */ - if(nread < MQTT_SUBACK_LEN) { - result = CURLE_WEIRD_SERVER_REPLY; - goto fail; - } - - /* verify SUBACK */ - if(readbuf[0] != ((mqtt->packetid >> 8) & 0xff) || - readbuf[1] != (mqtt->packetid & 0xff) || - readbuf[2] != 0x00) - result = CURLE_WEIRD_SERVER_REPLY; - -fail: - return result; -} - -static CURLcode mqtt_publish(struct Curl_easy *data) -{ - CURLcode result; - char *payload = data->set.postfields; - size_t payloadlen; - char *topic = NULL; - size_t topiclen; - unsigned char *pkt = NULL; - size_t i = 0; - size_t remaininglength; - size_t encodelen; - char encodedbytes[4]; - curl_off_t postfieldsize = data->set.postfieldsize; - - if(!payload) - return CURLE_BAD_FUNCTION_ARGUMENT; - if(postfieldsize < 0) - payloadlen = strlen(payload); - else - payloadlen = (size_t)postfieldsize; - - result = mqtt_get_topic(data, &topic, &topiclen); - if(result) - goto fail; - - remaininglength = payloadlen + 2 + topiclen; - encodelen = mqtt_encode_len(encodedbytes, remaininglength); - - /* add the control byte and the encoded remaining length */ - pkt = malloc(remaininglength + 1 + encodelen); - if(!pkt) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } - - /* assemble packet */ - pkt[i++] = MQTT_MSG_PUBLISH; - memcpy(&pkt[i], encodedbytes, encodelen); - i += encodelen; - pkt[i++] = (topiclen >> 8) & 0xff; - pkt[i++] = (topiclen & 0xff); - memcpy(&pkt[i], topic, topiclen); - i += topiclen; - memcpy(&pkt[i], payload, payloadlen); - i += payloadlen; - result = mqtt_send(data, (char *)pkt, i); - -fail: - free(pkt); - free(topic); - return result; -} - -static size_t mqtt_decode_len(unsigned char *buf, - size_t buflen, size_t *lenbytes) -{ - size_t len = 0; - size_t mult = 1; - size_t i; - unsigned char encoded = 128; - - for(i = 0; (i < buflen) && (encoded & 128); i++) { - encoded = buf[i]; - len += (encoded & 127) * mult; - mult *= 128; - } - - if(lenbytes) - *lenbytes = i; - - return len; -} - -#ifdef CURLDEBUG -static const char *statenames[]={ - "MQTT_FIRST", - "MQTT_REMAINING_LENGTH", - "MQTT_CONNACK", - "MQTT_SUBACK", - "MQTT_SUBACK_COMING", - "MQTT_PUBWAIT", - "MQTT_PUB_REMAIN", - - "NOT A STATE" -}; -#endif - -/* The only way to change state */ -static void mqstate(struct Curl_easy *data, - enum mqttstate state, - enum mqttstate nextstate) /* used if state == FIRST */ -{ - struct connectdata *conn = data->conn; - struct mqtt_conn *mqtt = &conn->proto.mqtt; -#ifdef CURLDEBUG - infof(data, "%s (from %s) (next is %s)", - statenames[state], - statenames[mqtt->state], - (state == MQTT_FIRST)? statenames[nextstate] : ""); -#endif - mqtt->state = state; - if(state == MQTT_FIRST) - mqtt->nextstate = nextstate; -} - - -/* for the publish packet */ -#define MQTT_HEADER_LEN 5 /* max 5 bytes */ - -static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; - ssize_t nread; - unsigned char *pkt = (unsigned char *)data->state.buffer; - size_t remlen; - struct mqtt_conn *mqtt = &conn->proto.mqtt; - struct MQTT *mq = data->req.p.mqtt; - unsigned char packet; - - switch(mqtt->state) { - MQTT_SUBACK_COMING: - case MQTT_SUBACK_COMING: - result = mqtt_verify_suback(data); - if(result) - break; - - mqstate(data, MQTT_FIRST, MQTT_PUBWAIT); - break; - - case MQTT_SUBACK: - case MQTT_PUBWAIT: - /* we are expecting PUBLISH or SUBACK */ - packet = mq->firstbyte & 0xf0; - if(packet == MQTT_MSG_PUBLISH) - mqstate(data, MQTT_PUB_REMAIN, MQTT_NOSTATE); - else if(packet == MQTT_MSG_SUBACK) { - mqstate(data, MQTT_SUBACK_COMING, MQTT_NOSTATE); - goto MQTT_SUBACK_COMING; - } - else if(packet == MQTT_MSG_DISCONNECT) { - infof(data, "Got DISCONNECT"); - *done = TRUE; - goto end; - } - else { - result = CURLE_WEIRD_SERVER_REPLY; - goto end; - } - - /* -- switched state -- */ - remlen = mq->remaining_length; - infof(data, "Remaining length: %zd bytes", remlen); - if(data->set.max_filesize && - (curl_off_t)remlen > data->set.max_filesize) { - failf(data, "Maximum file size exceeded"); - result = CURLE_FILESIZE_EXCEEDED; - goto end; - } - Curl_pgrsSetDownloadSize(data, remlen); - data->req.bytecount = 0; - data->req.size = remlen; - mq->npacket = remlen; /* get this many bytes */ - /* FALLTHROUGH */ - case MQTT_PUB_REMAIN: { - /* read rest of packet, but no more. Cap to buffer size */ - struct SingleRequest *k = &data->req; - size_t rest = mq->npacket; - if(rest > (size_t)data->set.buffer_size) - rest = (size_t)data->set.buffer_size; - result = Curl_read(data, sockfd, (char *)pkt, rest, &nread); - if(result) { - if(CURLE_AGAIN == result) { - infof(data, "EEEE AAAAGAIN"); - } - goto end; - } - if(!nread) { - infof(data, "server disconnected"); - result = CURLE_PARTIAL_FILE; - goto end; - } - Curl_debug(data, CURLINFO_DATA_IN, (char *)pkt, (size_t)nread); - - mq->npacket -= nread; - k->bytecount += nread; - Curl_pgrsSetDownloadCounter(data, k->bytecount); - - /* if QoS is set, message contains packet id */ - - result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)pkt, nread); - if(result) - goto end; - - if(!mq->npacket) - /* no more PUBLISH payload, back to subscribe wait state */ - mqstate(data, MQTT_FIRST, MQTT_PUBWAIT); - break; - } - default: - DEBUGASSERT(NULL); /* illegal state */ - result = CURLE_WEIRD_SERVER_REPLY; - goto end; - } - end: - return result; -} - -static CURLcode mqtt_do(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - *done = FALSE; /* unconditionally */ - - result = mqtt_connect(data); - if(result) { - failf(data, "Error %d sending MQTT CONNECT request", result); - return result; - } - mqstate(data, MQTT_FIRST, MQTT_CONNACK); - return CURLE_OK; -} - -static CURLcode mqtt_done(struct Curl_easy *data, - CURLcode status, bool premature) -{ - struct MQTT *mq = data->req.p.mqtt; - (void)status; - (void)premature; - Curl_safefree(mq->sendleftovers); - return CURLE_OK; -} - -static CURLcode mqtt_doing(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct mqtt_conn *mqtt = &conn->proto.mqtt; - struct MQTT *mq = data->req.p.mqtt; - ssize_t nread; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; - unsigned char *pkt = (unsigned char *)data->state.buffer; - unsigned char byte; - - *done = FALSE; - - if(mq->nsend) { - /* send the remainder of an outgoing packet */ - char *ptr = mq->sendleftovers; - result = mqtt_send(data, mq->sendleftovers, mq->nsend); - free(ptr); - if(result) - return result; - } - - infof(data, "mqtt_doing: state [%d]", (int) mqtt->state); - switch(mqtt->state) { - case MQTT_FIRST: - /* Read the initial byte only */ - result = Curl_read(data, sockfd, (char *)&mq->firstbyte, 1, &nread); - if(result) - break; - else if(!nread) { - failf(data, "Connection disconnected"); - *done = TRUE; - result = CURLE_RECV_ERROR; - break; - } - Curl_debug(data, CURLINFO_HEADER_IN, (char *)&mq->firstbyte, 1); - /* remember the first byte */ - mq->npacket = 0; - mqstate(data, MQTT_REMAINING_LENGTH, MQTT_NOSTATE); - /* FALLTHROUGH */ - case MQTT_REMAINING_LENGTH: - do { - result = Curl_read(data, sockfd, (char *)&byte, 1, &nread); - if(!nread) - break; - Curl_debug(data, CURLINFO_HEADER_IN, (char *)&byte, 1); - pkt[mq->npacket++] = byte; - } while((byte & 0x80) && (mq->npacket < 4)); - if(nread && (byte & 0x80)) - /* MQTT supports up to 127 * 128^0 + 127 * 128^1 + 127 * 128^2 + - 127 * 128^3 bytes. server tried to send more */ - result = CURLE_WEIRD_SERVER_REPLY; - if(result) - break; - mq->remaining_length = mqtt_decode_len(&pkt[0], mq->npacket, NULL); - mq->npacket = 0; - if(mq->remaining_length) { - mqstate(data, mqtt->nextstate, MQTT_NOSTATE); - break; - } - mqstate(data, MQTT_FIRST, MQTT_FIRST); - - if(mq->firstbyte == MQTT_MSG_DISCONNECT) { - infof(data, "Got DISCONNECT"); - *done = TRUE; - } - break; - case MQTT_CONNACK: - result = mqtt_verify_connack(data); - if(result) - break; - - if(data->state.httpreq == HTTPREQ_POST) { - result = mqtt_publish(data); - if(!result) { - result = mqtt_disconnect(data); - *done = TRUE; - } - mqtt->nextstate = MQTT_FIRST; - } - else { - result = mqtt_subscribe(data); - if(!result) { - mqstate(data, MQTT_FIRST, MQTT_SUBACK); - } - } - break; - - case MQTT_SUBACK: - case MQTT_PUBWAIT: - case MQTT_PUB_REMAIN: - result = mqtt_read_publish(data, done); - break; - - default: - failf(data, "State not handled yet"); - *done = TRUE; - break; - } - - if(result == CURLE_AGAIN) - result = CURLE_OK; - return result; -} - -#endif /* CURL_DISABLE_MQTT */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2019, Björn Stenberg, <bjorn@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_setup.h"
+
+#ifndef CURL_DISABLE_MQTT
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "transfer.h"
+#include "sendf.h"
+#include "progress.h"
+#include "mqtt.h"
+#include "select.h"
+#include "strdup.h"
+#include "url.h"
+#include "escape.h"
+#include "warnless.h"
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "multiif.h"
+#include "rand.h"
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+#define MQTT_MSG_CONNECT 0x10
+#define MQTT_MSG_CONNACK 0x20
+#define MQTT_MSG_PUBLISH 0x30
+#define MQTT_MSG_SUBSCRIBE 0x82
+#define MQTT_MSG_SUBACK 0x90
+#define MQTT_MSG_DISCONNECT 0xe0
+
+#define MQTT_CONNACK_LEN 2
+#define MQTT_SUBACK_LEN 3
+#define MQTT_CLIENTID_LEN 12 /* "curl0123abcd" */
+
+/*
+ * Forward declarations.
+ */
+
+static CURLcode mqtt_do(struct Curl_easy *data, bool *done);
+static CURLcode mqtt_done(struct Curl_easy *data,
+ CURLcode status, bool premature);
+static CURLcode mqtt_doing(struct Curl_easy *data, bool *done);
+static int mqtt_getsock(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t *sock);
+static CURLcode mqtt_setup_conn(struct Curl_easy *data,
+ struct connectdata *conn);
+
+/*
+ * MQTT protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_mqtt = {
+ "MQTT", /* scheme */
+ mqtt_setup_conn, /* setup_connection */
+ mqtt_do, /* do_it */
+ mqtt_done, /* done */
+ ZERO_NULL, /* do_more */
+ ZERO_NULL, /* connect_it */
+ ZERO_NULL, /* connecting */
+ mqtt_doing, /* doing */
+ ZERO_NULL, /* proto_getsock */
+ mqtt_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ ZERO_NULL, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_MQTT, /* defport */
+ CURLPROTO_MQTT, /* protocol */
+ CURLPROTO_MQTT, /* family */
+ PROTOPT_NONE /* flags */
+};
+
+static CURLcode mqtt_setup_conn(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ /* allocate the HTTP-specific struct for the Curl_easy, only to survive
+ during this request */
+ struct MQTT *mq;
+ (void)conn;
+ DEBUGASSERT(data->req.p.mqtt == NULL);
+
+ mq = calloc(1, sizeof(struct MQTT));
+ if(!mq)
+ return CURLE_OUT_OF_MEMORY;
+ data->req.p.mqtt = mq;
+ return CURLE_OK;
+}
+
+static CURLcode mqtt_send(struct Curl_easy *data,
+ char *buf, size_t len)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+ struct MQTT *mq = data->req.p.mqtt;
+ ssize_t n;
+ result = Curl_write(data, sockfd, buf, len, &n);
+ if(!result)
+ Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n);
+ if(len != (size_t)n) {
+ size_t nsend = len - n;
+ char *sendleftovers = Curl_memdup(&buf[n], nsend);
+ if(!sendleftovers)
+ return CURLE_OUT_OF_MEMORY;
+ mq->sendleftovers = sendleftovers;
+ mq->nsend = nsend;
+ }
+ else {
+ mq->sendleftovers = NULL;
+ mq->nsend = 0;
+ }
+ return result;
+}
+
+/* Generic function called by the multi interface to figure out what socket(s)
+ to wait for and for what actions during the DOING and PROTOCONNECT
+ states */
+static int mqtt_getsock(struct Curl_easy *data,
+ struct connectdata *conn,
+ curl_socket_t *sock)
+{
+ (void)data;
+ sock[0] = conn->sock[FIRSTSOCKET];
+ return GETSOCK_READSOCK(FIRSTSOCKET);
+}
+
+static int mqtt_encode_len(char *buf, size_t len)
+{
+ unsigned char encoded;
+ int i;
+
+ for(i = 0; (len > 0) && (i<4); i++) {
+ encoded = len % 0x80;
+ len /= 0x80;
+ if(len)
+ encoded |= 0x80;
+ buf[i] = encoded;
+ }
+
+ return i;
+}
+
+/* add the passwd to the CONNECT packet */
+static int add_passwd(const char *passwd, const size_t plen,
+ char *pkt, const size_t start, int remain_pos)
+{
+ /* magic number that need to be set properly */
+ const size_t conn_flags_pos = remain_pos + 8;
+ if(plen > 0xffff)
+ return 1;
+
+ /* set password flag */
+ pkt[conn_flags_pos] |= 0x40;
+
+ /* length of password provided */
+ pkt[start] = (char)((plen >> 8) & 0xFF);
+ pkt[start + 1] = (char)(plen & 0xFF);
+ memcpy(&pkt[start + 2], passwd, plen);
+ return 0;
+}
+
+/* add user to the CONNECT packet */
+static int add_user(const char *username, const size_t ulen,
+ unsigned char *pkt, const size_t start, int remain_pos)
+{
+ /* magic number that need to be set properly */
+ const size_t conn_flags_pos = remain_pos + 8;
+ if(ulen > 0xffff)
+ return 1;
+
+ /* set username flag */
+ pkt[conn_flags_pos] |= 0x80;
+ /* length of username provided */
+ pkt[start] = (unsigned char)((ulen >> 8) & 0xFF);
+ pkt[start + 1] = (unsigned char)(ulen & 0xFF);
+ memcpy(&pkt[start + 2], username, ulen);
+ return 0;
+}
+
+/* add client ID to the CONNECT packet */
+static int add_client_id(const char *client_id, const size_t client_id_len,
+ char *pkt, const size_t start)
+{
+ if(client_id_len != MQTT_CLIENTID_LEN)
+ return 1;
+ pkt[start] = 0x00;
+ pkt[start + 1] = MQTT_CLIENTID_LEN;
+ memcpy(&pkt[start + 2], client_id, MQTT_CLIENTID_LEN);
+ return 0;
+}
+
+/* Set initial values of CONNECT packet */
+static int init_connpack(char *packet, char *remain, int remain_pos)
+{
+ /* Fixed header starts */
+ /* packet type */
+ packet[0] = MQTT_MSG_CONNECT;
+ /* remaining length field */
+ memcpy(&packet[1], remain, remain_pos);
+ /* Fixed header ends */
+
+ /* Variable header starts */
+ /* protocol length */
+ packet[remain_pos + 1] = 0x00;
+ packet[remain_pos + 2] = 0x04;
+ /* protocol name */
+ packet[remain_pos + 3] = 'M';
+ packet[remain_pos + 4] = 'Q';
+ packet[remain_pos + 5] = 'T';
+ packet[remain_pos + 6] = 'T';
+ /* protocol level */
+ packet[remain_pos + 7] = 0x04;
+ /* CONNECT flag: CleanSession */
+ packet[remain_pos + 8] = 0x02;
+ /* keep-alive 0 = disabled */
+ packet[remain_pos + 9] = 0x00;
+ packet[remain_pos + 10] = 0x3c;
+ /* end of variable header */
+ return remain_pos + 10;
+}
+
+static CURLcode mqtt_connect(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ int pos = 0;
+ int rc = 0;
+ /* remain length */
+ int remain_pos = 0;
+ char remain[4] = {0};
+ size_t packetlen = 0;
+ size_t payloadlen = 0;
+ size_t start_user = 0;
+ size_t start_pwd = 0;
+ char client_id[MQTT_CLIENTID_LEN + 1] = "curl";
+ const size_t clen = strlen("curl");
+ char *packet = NULL;
+
+ /* extracting username from request */
+ const char *username = data->state.aptr.user ?
+ data->state.aptr.user : "";
+ const size_t ulen = strlen(username);
+ /* extracting password from request */
+ const char *passwd = data->state.aptr.passwd ?
+ data->state.aptr.passwd : "";
+ const size_t plen = strlen(passwd);
+
+ payloadlen = ulen + plen + MQTT_CLIENTID_LEN + 2;
+ /* The plus 2 are for the MSB and LSB describing the length of the string to
+ * be added on the payload. Refer to spec 1.5.2 and 1.5.4 */
+ if(ulen)
+ payloadlen += 2;
+ if(plen)
+ payloadlen += 2;
+
+ /* getting how much occupy the remain length */
+ remain_pos = mqtt_encode_len(remain, payloadlen + 10);
+
+ /* 10 length of variable header and 1 the first byte of the fixed header */
+ packetlen = payloadlen + 10 + remain_pos + 1;
+
+ /* allocating packet */
+ if(packetlen > 268435455)
+ return CURLE_WEIRD_SERVER_REPLY;
+ packet = malloc(packetlen);
+ if(!packet)
+ return CURLE_OUT_OF_MEMORY;
+ memset(packet, 0, packetlen);
+
+ /* set initial values for the CONNECT packet */
+ pos = init_connpack(packet, remain, remain_pos);
+
+ result = Curl_rand_hex(data, (unsigned char *)&client_id[clen],
+ MQTT_CLIENTID_LEN - clen + 1);
+ /* add client id */
+ rc = add_client_id(client_id, strlen(client_id), packet, pos + 1);
+ if(rc) {
+ failf(data, "Client ID length mismatched: [%lu]", strlen(client_id));
+ result = CURLE_WEIRD_SERVER_REPLY;
+ goto end;
+ }
+ infof(data, "Using client id '%s'", client_id);
+
+ /* position where starts the user payload */
+ start_user = pos + 3 + MQTT_CLIENTID_LEN;
+ /* position where starts the password payload */
+ start_pwd = start_user + ulen;
+ /* if user name was provided, add it to the packet */
+ if(ulen) {
+ start_pwd += 2;
+
+ rc = add_user(username, ulen,
+ (unsigned char *)packet, start_user, remain_pos);
+ if(rc) {
+ failf(data, "Username is too large: [%lu]", ulen);
+ result = CURLE_WEIRD_SERVER_REPLY;
+ goto end;
+ }
+ }
+
+ /* if passwd was provided, add it to the packet */
+ if(plen) {
+ rc = add_passwd(passwd, plen, packet, start_pwd, remain_pos);
+ if(rc) {
+ failf(data, "Password is too large: [%lu]", plen);
+ result = CURLE_WEIRD_SERVER_REPLY;
+ goto end;
+ }
+ }
+
+ if(!result)
+ result = mqtt_send(data, packet, packetlen);
+
+end:
+ if(packet)
+ free(packet);
+ Curl_safefree(data->state.aptr.user);
+ Curl_safefree(data->state.aptr.passwd);
+ return result;
+}
+
+static CURLcode mqtt_disconnect(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct MQTT *mq = data->req.p.mqtt;
+ result = mqtt_send(data, (char *)"\xe0\x00", 2);
+ Curl_safefree(mq->sendleftovers);
+ return result;
+}
+
+static CURLcode mqtt_verify_connack(struct Curl_easy *data)
+{
+ CURLcode result;
+ struct connectdata *conn = data->conn;
+ curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+ unsigned char readbuf[MQTT_CONNACK_LEN];
+ ssize_t nread;
+
+ result = Curl_read(data, sockfd, (char *)readbuf, MQTT_CONNACK_LEN, &nread);
+ if(result)
+ goto fail;
+
+ Curl_debug(data, CURLINFO_HEADER_IN, (char *)readbuf, (size_t)nread);
+
+ /* fixme */
+ if(nread < MQTT_CONNACK_LEN) {
+ result = CURLE_WEIRD_SERVER_REPLY;
+ goto fail;
+ }
+
+ /* verify CONNACK */
+ if(readbuf[0] != 0x00 || readbuf[1] != 0x00) {
+ failf(data, "Expected %02x%02x but got %02x%02x",
+ 0x00, 0x00, readbuf[0], readbuf[1]);
+ result = CURLE_WEIRD_SERVER_REPLY;
+ }
+
+fail:
+ return result;
+}
+
+static CURLcode mqtt_get_topic(struct Curl_easy *data,
+ char **topic, size_t *topiclen)
+{
+ char *path = data->state.up.path;
+ CURLcode result = CURLE_URL_MALFORMAT;
+ if(strlen(path) > 1) {
+ result = Curl_urldecode(path + 1, 0, topic, topiclen, REJECT_NADA);
+ if(!result && (*topiclen > 0xffff)) {
+ failf(data, "Too long MQTT topic");
+ result = CURLE_URL_MALFORMAT;
+ }
+ }
+ else
+ failf(data, "No MQTT topic found. Forgot to URL encode it?");
+
+ return result;
+}
+
+static CURLcode mqtt_subscribe(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ char *topic = NULL;
+ size_t topiclen;
+ unsigned char *packet = NULL;
+ size_t packetlen;
+ char encodedsize[4];
+ size_t n;
+ struct connectdata *conn = data->conn;
+
+ result = mqtt_get_topic(data, &topic, &topiclen);
+ if(result)
+ goto fail;
+
+ conn->proto.mqtt.packetid++;
+
+ packetlen = topiclen + 5; /* packetid + topic (has a two byte length field)
+ + 2 bytes topic length + QoS byte */
+ n = mqtt_encode_len((char *)encodedsize, packetlen);
+ packetlen += n + 1; /* add one for the control packet type byte */
+
+ packet = malloc(packetlen);
+ if(!packet) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+
+ packet[0] = MQTT_MSG_SUBSCRIBE;
+ memcpy(&packet[1], encodedsize, n);
+ packet[1 + n] = (conn->proto.mqtt.packetid >> 8) & 0xff;
+ packet[2 + n] = conn->proto.mqtt.packetid & 0xff;
+ packet[3 + n] = (topiclen >> 8) & 0xff;
+ packet[4 + n ] = topiclen & 0xff;
+ memcpy(&packet[5 + n], topic, topiclen);
+ packet[5 + n + topiclen] = 0; /* QoS zero */
+
+ result = mqtt_send(data, (char *)packet, packetlen);
+
+fail:
+ free(topic);
+ free(packet);
+ return result;
+}
+
+/*
+ * Called when the first byte was already read.
+ */
+static CURLcode mqtt_verify_suback(struct Curl_easy *data)
+{
+ CURLcode result;
+ struct connectdata *conn = data->conn;
+ curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+ unsigned char readbuf[MQTT_SUBACK_LEN];
+ ssize_t nread;
+ struct mqtt_conn *mqtt = &conn->proto.mqtt;
+
+ result = Curl_read(data, sockfd, (char *)readbuf, MQTT_SUBACK_LEN, &nread);
+ if(result)
+ goto fail;
+
+ Curl_debug(data, CURLINFO_HEADER_IN, (char *)readbuf, (size_t)nread);
+
+ /* fixme */
+ if(nread < MQTT_SUBACK_LEN) {
+ result = CURLE_WEIRD_SERVER_REPLY;
+ goto fail;
+ }
+
+ /* verify SUBACK */
+ if(readbuf[0] != ((mqtt->packetid >> 8) & 0xff) ||
+ readbuf[1] != (mqtt->packetid & 0xff) ||
+ readbuf[2] != 0x00)
+ result = CURLE_WEIRD_SERVER_REPLY;
+
+fail:
+ return result;
+}
+
+static CURLcode mqtt_publish(struct Curl_easy *data)
+{
+ CURLcode result;
+ char *payload = data->set.postfields;
+ size_t payloadlen;
+ char *topic = NULL;
+ size_t topiclen;
+ unsigned char *pkt = NULL;
+ size_t i = 0;
+ size_t remaininglength;
+ size_t encodelen;
+ char encodedbytes[4];
+ curl_off_t postfieldsize = data->set.postfieldsize;
+
+ if(!payload)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ if(postfieldsize < 0)
+ payloadlen = strlen(payload);
+ else
+ payloadlen = (size_t)postfieldsize;
+
+ result = mqtt_get_topic(data, &topic, &topiclen);
+ if(result)
+ goto fail;
+
+ remaininglength = payloadlen + 2 + topiclen;
+ encodelen = mqtt_encode_len(encodedbytes, remaininglength);
+
+ /* add the control byte and the encoded remaining length */
+ pkt = malloc(remaininglength + 1 + encodelen);
+ if(!pkt) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+
+ /* assemble packet */
+ pkt[i++] = MQTT_MSG_PUBLISH;
+ memcpy(&pkt[i], encodedbytes, encodelen);
+ i += encodelen;
+ pkt[i++] = (topiclen >> 8) & 0xff;
+ pkt[i++] = (topiclen & 0xff);
+ memcpy(&pkt[i], topic, topiclen);
+ i += topiclen;
+ memcpy(&pkt[i], payload, payloadlen);
+ i += payloadlen;
+ result = mqtt_send(data, (char *)pkt, i);
+
+fail:
+ free(pkt);
+ free(topic);
+ return result;
+}
+
+static size_t mqtt_decode_len(unsigned char *buf,
+ size_t buflen, size_t *lenbytes)
+{
+ size_t len = 0;
+ size_t mult = 1;
+ size_t i;
+ unsigned char encoded = 128;
+
+ for(i = 0; (i < buflen) && (encoded & 128); i++) {
+ encoded = buf[i];
+ len += (encoded & 127) * mult;
+ mult *= 128;
+ }
+
+ if(lenbytes)
+ *lenbytes = i;
+
+ return len;
+}
+
+#ifdef CURLDEBUG
+static const char *statenames[]={
+ "MQTT_FIRST",
+ "MQTT_REMAINING_LENGTH",
+ "MQTT_CONNACK",
+ "MQTT_SUBACK",
+ "MQTT_SUBACK_COMING",
+ "MQTT_PUBWAIT",
+ "MQTT_PUB_REMAIN",
+
+ "NOT A STATE"
+};
+#endif
+
+/* The only way to change state */
+static void mqstate(struct Curl_easy *data,
+ enum mqttstate state,
+ enum mqttstate nextstate) /* used if state == FIRST */
+{
+ struct connectdata *conn = data->conn;
+ struct mqtt_conn *mqtt = &conn->proto.mqtt;
+#ifdef CURLDEBUG
+ infof(data, "%s (from %s) (next is %s)",
+ statenames[state],
+ statenames[mqtt->state],
+ (state == MQTT_FIRST)? statenames[nextstate] : "");
+#endif
+ mqtt->state = state;
+ if(state == MQTT_FIRST)
+ mqtt->nextstate = nextstate;
+}
+
+
+/* for the publish packet */
+#define MQTT_HEADER_LEN 5 /* max 5 bytes */
+
+static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+ ssize_t nread;
+ unsigned char *pkt = (unsigned char *)data->state.buffer;
+ size_t remlen;
+ struct mqtt_conn *mqtt = &conn->proto.mqtt;
+ struct MQTT *mq = data->req.p.mqtt;
+ unsigned char packet;
+
+ switch(mqtt->state) {
+ MQTT_SUBACK_COMING:
+ case MQTT_SUBACK_COMING:
+ result = mqtt_verify_suback(data);
+ if(result)
+ break;
+
+ mqstate(data, MQTT_FIRST, MQTT_PUBWAIT);
+ break;
+
+ case MQTT_SUBACK:
+ case MQTT_PUBWAIT:
+ /* we are expecting PUBLISH or SUBACK */
+ packet = mq->firstbyte & 0xf0;
+ if(packet == MQTT_MSG_PUBLISH)
+ mqstate(data, MQTT_PUB_REMAIN, MQTT_NOSTATE);
+ else if(packet == MQTT_MSG_SUBACK) {
+ mqstate(data, MQTT_SUBACK_COMING, MQTT_NOSTATE);
+ goto MQTT_SUBACK_COMING;
+ }
+ else if(packet == MQTT_MSG_DISCONNECT) {
+ infof(data, "Got DISCONNECT");
+ *done = TRUE;
+ goto end;
+ }
+ else {
+ result = CURLE_WEIRD_SERVER_REPLY;
+ goto end;
+ }
+
+ /* -- switched state -- */
+ remlen = mq->remaining_length;
+ infof(data, "Remaining length: %zd bytes", remlen);
+ if(data->set.max_filesize &&
+ (curl_off_t)remlen > data->set.max_filesize) {
+ failf(data, "Maximum file size exceeded");
+ result = CURLE_FILESIZE_EXCEEDED;
+ goto end;
+ }
+ Curl_pgrsSetDownloadSize(data, remlen);
+ data->req.bytecount = 0;
+ data->req.size = remlen;
+ mq->npacket = remlen; /* get this many bytes */
+ /* FALLTHROUGH */
+ case MQTT_PUB_REMAIN: {
+ /* read rest of packet, but no more. Cap to buffer size */
+ struct SingleRequest *k = &data->req;
+ size_t rest = mq->npacket;
+ if(rest > (size_t)data->set.buffer_size)
+ rest = (size_t)data->set.buffer_size;
+ result = Curl_read(data, sockfd, (char *)pkt, rest, &nread);
+ if(result) {
+ if(CURLE_AGAIN == result) {
+ infof(data, "EEEE AAAAGAIN");
+ }
+ goto end;
+ }
+ if(!nread) {
+ infof(data, "server disconnected");
+ result = CURLE_PARTIAL_FILE;
+ goto end;
+ }
+ Curl_debug(data, CURLINFO_DATA_IN, (char *)pkt, (size_t)nread);
+
+ mq->npacket -= nread;
+ k->bytecount += nread;
+ Curl_pgrsSetDownloadCounter(data, k->bytecount);
+
+ /* if QoS is set, message contains packet id */
+
+ result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)pkt, nread);
+ if(result)
+ goto end;
+
+ if(!mq->npacket)
+ /* no more PUBLISH payload, back to subscribe wait state */
+ mqstate(data, MQTT_FIRST, MQTT_PUBWAIT);
+ break;
+ }
+ default:
+ DEBUGASSERT(NULL); /* illegal state */
+ result = CURLE_WEIRD_SERVER_REPLY;
+ goto end;
+ }
+ end:
+ return result;
+}
+
+static CURLcode mqtt_do(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ *done = FALSE; /* unconditionally */
+
+ result = mqtt_connect(data);
+ if(result) {
+ failf(data, "Error %d sending MQTT CONNECT request", result);
+ return result;
+ }
+ mqstate(data, MQTT_FIRST, MQTT_CONNACK);
+ return CURLE_OK;
+}
+
+static CURLcode mqtt_done(struct Curl_easy *data,
+ CURLcode status, bool premature)
+{
+ struct MQTT *mq = data->req.p.mqtt;
+ (void)status;
+ (void)premature;
+ Curl_safefree(mq->sendleftovers);
+ return CURLE_OK;
+}
+
+static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct mqtt_conn *mqtt = &conn->proto.mqtt;
+ struct MQTT *mq = data->req.p.mqtt;
+ ssize_t nread;
+ curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+ unsigned char *pkt = (unsigned char *)data->state.buffer;
+ unsigned char byte;
+
+ *done = FALSE;
+
+ if(mq->nsend) {
+ /* send the remainder of an outgoing packet */
+ char *ptr = mq->sendleftovers;
+ result = mqtt_send(data, mq->sendleftovers, mq->nsend);
+ free(ptr);
+ if(result)
+ return result;
+ }
+
+ infof(data, "mqtt_doing: state [%d]", (int) mqtt->state);
+ switch(mqtt->state) {
+ case MQTT_FIRST:
+ /* Read the initial byte only */
+ result = Curl_read(data, sockfd, (char *)&mq->firstbyte, 1, &nread);
+ if(result)
+ break;
+ else if(!nread) {
+ failf(data, "Connection disconnected");
+ *done = TRUE;
+ result = CURLE_RECV_ERROR;
+ break;
+ }
+ Curl_debug(data, CURLINFO_HEADER_IN, (char *)&mq->firstbyte, 1);
+ /* remember the first byte */
+ mq->npacket = 0;
+ mqstate(data, MQTT_REMAINING_LENGTH, MQTT_NOSTATE);
+ /* FALLTHROUGH */
+ case MQTT_REMAINING_LENGTH:
+ do {
+ result = Curl_read(data, sockfd, (char *)&byte, 1, &nread);
+ if(!nread)
+ break;
+ Curl_debug(data, CURLINFO_HEADER_IN, (char *)&byte, 1);
+ pkt[mq->npacket++] = byte;
+ } while((byte & 0x80) && (mq->npacket < 4));
+ if(nread && (byte & 0x80))
+ /* MQTT supports up to 127 * 128^0 + 127 * 128^1 + 127 * 128^2 +
+ 127 * 128^3 bytes. server tried to send more */
+ result = CURLE_WEIRD_SERVER_REPLY;
+ if(result)
+ break;
+ mq->remaining_length = mqtt_decode_len(&pkt[0], mq->npacket, NULL);
+ mq->npacket = 0;
+ if(mq->remaining_length) {
+ mqstate(data, mqtt->nextstate, MQTT_NOSTATE);
+ break;
+ }
+ mqstate(data, MQTT_FIRST, MQTT_FIRST);
+
+ if(mq->firstbyte == MQTT_MSG_DISCONNECT) {
+ infof(data, "Got DISCONNECT");
+ *done = TRUE;
+ }
+ break;
+ case MQTT_CONNACK:
+ result = mqtt_verify_connack(data);
+ if(result)
+ break;
+
+ if(data->state.httpreq == HTTPREQ_POST) {
+ result = mqtt_publish(data);
+ if(!result) {
+ result = mqtt_disconnect(data);
+ *done = TRUE;
+ }
+ mqtt->nextstate = MQTT_FIRST;
+ }
+ else {
+ result = mqtt_subscribe(data);
+ if(!result) {
+ mqstate(data, MQTT_FIRST, MQTT_SUBACK);
+ }
+ }
+ break;
+
+ case MQTT_SUBACK:
+ case MQTT_PUBWAIT:
+ case MQTT_PUB_REMAIN:
+ result = mqtt_read_publish(data, done);
+ break;
+
+ default:
+ failf(data, "State not handled yet");
+ *done = TRUE;
+ break;
+ }
+
+ if(result == CURLE_AGAIN)
+ result = CURLE_OK;
+ return result;
+}
+
+#endif /* CURL_DISABLE_MQTT */
diff --git a/libs/libcurl/src/mqtt.h b/libs/libcurl/src/mqtt.h index c400d9b14e..0ed7cb4741 100644 --- a/libs/libcurl/src/mqtt.h +++ b/libs/libcurl/src/mqtt.h @@ -1,61 +1,61 @@ -#ifndef HEADER_CURL_MQTT_H -#define HEADER_CURL_MQTT_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2019 - 2022, Björn Stenberg, <bjorn@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 - * - ***************************************************************************/ - -#ifndef CURL_DISABLE_MQTT -extern const struct Curl_handler Curl_handler_mqtt; -#endif - -enum mqttstate { - MQTT_FIRST, /* 0 */ - MQTT_REMAINING_LENGTH, /* 1 */ - MQTT_CONNACK, /* 2 */ - MQTT_SUBACK, /* 3 */ - MQTT_SUBACK_COMING, /* 4 - the SUBACK remainder */ - MQTT_PUBWAIT, /* 5 - wait for publish */ - MQTT_PUB_REMAIN, /* 6 - wait for the remainder of the publish */ - - MQTT_NOSTATE /* 7 - never used an actual state */ -}; - -struct mqtt_conn { - enum mqttstate state; - enum mqttstate nextstate; /* switch to this after remaining length is - done */ - unsigned int packetid; -}; - -/* protocol-specific transfer-related data */ -struct MQTT { - char *sendleftovers; - size_t nsend; /* size of sendleftovers */ - - /* when receiving */ - size_t npacket; /* byte counter */ - unsigned char firstbyte; - size_t remaining_length; -}; - -#endif /* HEADER_CURL_MQTT_H */ +#ifndef HEADER_CURL_MQTT_H
+#define HEADER_CURL_MQTT_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2019 - 2022, Björn Stenberg, <bjorn@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
+ *
+ ***************************************************************************/
+
+#ifndef CURL_DISABLE_MQTT
+extern const struct Curl_handler Curl_handler_mqtt;
+#endif
+
+enum mqttstate {
+ MQTT_FIRST, /* 0 */
+ MQTT_REMAINING_LENGTH, /* 1 */
+ MQTT_CONNACK, /* 2 */
+ MQTT_SUBACK, /* 3 */
+ MQTT_SUBACK_COMING, /* 4 - the SUBACK remainder */
+ MQTT_PUBWAIT, /* 5 - wait for publish */
+ MQTT_PUB_REMAIN, /* 6 - wait for the remainder of the publish */
+
+ MQTT_NOSTATE /* 7 - never used an actual state */
+};
+
+struct mqtt_conn {
+ enum mqttstate state;
+ enum mqttstate nextstate; /* switch to this after remaining length is
+ done */
+ unsigned int packetid;
+};
+
+/* protocol-specific transfer-related data */
+struct MQTT {
+ char *sendleftovers;
+ size_t nsend; /* size of sendleftovers */
+
+ /* when receiving */
+ size_t npacket; /* byte counter */
+ unsigned char firstbyte;
+ size_t remaining_length;
+};
+
+#endif /* HEADER_CURL_MQTT_H */
diff --git a/libs/libcurl/src/multi.c b/libs/libcurl/src/multi.c index 51acba73ac..0218012685 100644 --- a/libs/libcurl/src/multi.c +++ b/libs/libcurl/src/multi.c @@ -1,3732 +1,3745 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "transfer.h" -#include "url.h" -#include "connect.h" -#include "progress.h" -#include "easyif.h" -#include "share.h" -#include "psl.h" -#include "multiif.h" -#include "sendf.h" -#include "timeval.h" -#include "http.h" -#include "select.h" -#include "warnless.h" -#include "speedcheck.h" -#include "conncache.h" -#include "multihandle.h" -#include "sigpipe.h" -#include "vtls/vtls.h" -#include "http_proxy.h" -#include "http2.h" -#include "socketpair.h" -#include "socks.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#ifdef __APPLE__ - -#define wakeup_write write -#define wakeup_read read -#define wakeup_close close -#define wakeup_create pipe - -#else /* __APPLE__ */ - -#define wakeup_write swrite -#define wakeup_read sread -#define wakeup_close sclose -#define wakeup_create(p) Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, p) - -#endif /* __APPLE__ */ - -/* - CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97 - to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes. Still, every - CURL handle takes 45-50 K memory, therefore this 3K are not significant. -*/ -#ifndef CURL_SOCKET_HASH_TABLE_SIZE -#define CURL_SOCKET_HASH_TABLE_SIZE 911 -#endif - -#ifndef CURL_CONNECTION_HASH_SIZE -#define CURL_CONNECTION_HASH_SIZE 97 -#endif - -#ifndef CURL_DNS_HASH_SIZE -#define CURL_DNS_HASH_SIZE 71 -#endif - -#define CURL_MULTI_HANDLE 0x000bab1e - -#define GOOD_MULTI_HANDLE(x) \ - ((x) && (x)->magic == CURL_MULTI_HANDLE) - -static CURLMcode singlesocket(struct Curl_multi *multi, - struct Curl_easy *data); -static CURLMcode add_next_timeout(struct curltime now, - struct Curl_multi *multi, - struct Curl_easy *d); -static CURLMcode multi_timeout(struct Curl_multi *multi, - long *timeout_ms); -static void process_pending_handles(struct Curl_multi *multi); - -#ifdef DEBUGBUILD -static const char * const statename[]={ - "INIT", - "PENDING", - "CONNECT", - "RESOLVING", - "CONNECTING", - "TUNNELING", - "PROTOCONNECT", - "PROTOCONNECTING", - "DO", - "DOING", - "DOING_MORE", - "DID", - "PERFORMING", - "RATELIMITING", - "DONE", - "COMPLETED", - "MSGSENT", -}; -#endif - -/* function pointer called once when switching TO a state */ -typedef void (*init_multistate_func)(struct Curl_easy *data); - -/* called in DID state, before PERFORMING state */ -static void before_perform(struct Curl_easy *data) -{ - data->req.chunk = FALSE; - Curl_pgrsTime(data, TIMER_PRETRANSFER); -} - -static void init_completed(struct Curl_easy *data) -{ - /* this is a completed transfer */ - - /* Important: reset the conn pointer so that we don't point to memory - that could be freed anytime */ - Curl_detach_connection(data); - Curl_expire_clear(data); /* stop all timers */ -} - -/* always use this function to change state, to make debugging easier */ -static void mstate(struct Curl_easy *data, CURLMstate state -#ifdef DEBUGBUILD - , int lineno -#endif -) -{ - CURLMstate oldstate = data->mstate; - static const init_multistate_func finit[MSTATE_LAST] = { - NULL, /* INIT */ - NULL, /* PENDING */ - Curl_init_CONNECT, /* CONNECT */ - NULL, /* RESOLVING */ - NULL, /* CONNECTING */ - NULL, /* TUNNELING */ - NULL, /* PROTOCONNECT */ - NULL, /* PROTOCONNECTING */ - Curl_connect_free, /* DO */ - NULL, /* DOING */ - NULL, /* DOING_MORE */ - before_perform, /* DID */ - NULL, /* PERFORMING */ - NULL, /* RATELIMITING */ - NULL, /* DONE */ - init_completed, /* COMPLETED */ - NULL /* MSGSENT */ - }; - -#if defined(DEBUGBUILD) && defined(CURL_DISABLE_VERBOSE_STRINGS) - (void) lineno; -#endif - - if(oldstate == state) - /* don't bother when the new state is the same as the old state */ - return; - - data->mstate = state; - -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - if(data->mstate >= MSTATE_PENDING && - data->mstate < MSTATE_COMPLETED) { - long connection_id = -5000; - - if(data->conn) - connection_id = data->conn->connection_id; - - infof(data, - "STATE: %s => %s handle %p; line %d (connection #%ld)", - statename[oldstate], statename[data->mstate], - (void *)data, lineno, connection_id); - } -#endif - - if(state == MSTATE_COMPLETED) { - /* changing to COMPLETED means there's one less easy handle 'alive' */ - DEBUGASSERT(data->multi->num_alive > 0); - data->multi->num_alive--; - } - - /* if this state has an init-function, run it */ - if(finit[state]) - finit[state](data); -} - -#ifndef DEBUGBUILD -#define multistate(x,y) mstate(x,y) -#else -#define multistate(x,y) mstate(x,y, __LINE__) -#endif - -/* - * We add one of these structs to the sockhash for each socket - */ - -struct Curl_sh_entry { - struct Curl_hash transfers; /* hash of transfers using this socket */ - unsigned int action; /* what combined action READ/WRITE this socket waits - for */ - unsigned int users; /* number of transfers using this */ - void *socketp; /* settable by users with curl_multi_assign() */ - unsigned int readers; /* this many transfers want to read */ - unsigned int writers; /* this many transfers want to write */ -}; -/* bits for 'action' having no bits means this socket is not expecting any - action */ -#define SH_READ 1 -#define SH_WRITE 2 - -/* look up a given socket in the socket hash, skip invalid sockets */ -static struct Curl_sh_entry *sh_getentry(struct Curl_hash *sh, - curl_socket_t s) -{ - if(s != CURL_SOCKET_BAD) { - /* only look for proper sockets */ - return Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t)); - } - return NULL; -} - -#define TRHASH_SIZE 13 -static size_t trhash(void *key, size_t key_length, size_t slots_num) -{ - size_t keyval = (size_t)*(struct Curl_easy **)key; - (void) key_length; - - return (keyval % slots_num); -} - -static size_t trhash_compare(void *k1, size_t k1_len, void *k2, size_t k2_len) -{ - (void)k1_len; - (void)k2_len; - - return *(struct Curl_easy **)k1 == *(struct Curl_easy **)k2; -} - -static void trhash_dtor(void *nada) -{ - (void)nada; -} - -/* - * The sockhash has its own separate subhash in each entry that need to be - * safely destroyed first. - */ -static void sockhash_destroy(struct Curl_hash *h) -{ - struct Curl_hash_iterator iter; - struct Curl_hash_element *he; - - DEBUGASSERT(h); - Curl_hash_start_iterate(h, &iter); - he = Curl_hash_next_element(&iter); - while(he) { - struct Curl_sh_entry *sh = (struct Curl_sh_entry *)he->ptr; - Curl_hash_destroy(&sh->transfers); - he = Curl_hash_next_element(&iter); - } - Curl_hash_destroy(h); -} - - -/* make sure this socket is present in the hash for this handle */ -static struct Curl_sh_entry *sh_addentry(struct Curl_hash *sh, - curl_socket_t s) -{ - struct Curl_sh_entry *there = sh_getentry(sh, s); - struct Curl_sh_entry *check; - - if(there) { - /* it is present, return fine */ - return there; - } - - /* not present, add it */ - check = calloc(1, sizeof(struct Curl_sh_entry)); - if(!check) - return NULL; /* major failure */ - - Curl_hash_init(&check->transfers, TRHASH_SIZE, trhash, trhash_compare, - trhash_dtor); - - /* make/add new hash entry */ - if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) { - Curl_hash_destroy(&check->transfers); - free(check); - return NULL; /* major failure */ - } - - return check; /* things are good in sockhash land */ -} - - -/* delete the given socket + handle from the hash */ -static void sh_delentry(struct Curl_sh_entry *entry, - struct Curl_hash *sh, curl_socket_t s) -{ - Curl_hash_destroy(&entry->transfers); - - /* We remove the hash entry. This will end up in a call to - sh_freeentry(). */ - Curl_hash_delete(sh, (char *)&s, sizeof(curl_socket_t)); -} - -/* - * free a sockhash entry - */ -static void sh_freeentry(void *freethis) -{ - struct Curl_sh_entry *p = (struct Curl_sh_entry *) freethis; - - free(p); -} - -static size_t fd_key_compare(void *k1, size_t k1_len, void *k2, size_t k2_len) -{ - (void) k1_len; (void) k2_len; - - return (*((curl_socket_t *) k1)) == (*((curl_socket_t *) k2)); -} - -static size_t hash_fd(void *key, size_t key_length, size_t slots_num) -{ - curl_socket_t fd = *((curl_socket_t *) key); - (void) key_length; - - return (fd % slots_num); -} - -/* - * sh_init() creates a new socket hash and returns the handle for it. - * - * Quote from README.multi_socket: - * - * "Some tests at 7000 and 9000 connections showed that the socket hash lookup - * is somewhat of a bottle neck. Its current implementation may be a bit too - * limiting. It simply has a fixed-size array, and on each entry in the array - * it has a linked list with entries. So the hash only checks which list to - * scan through. The code I had used so for used a list with merely 7 slots - * (as that is what the DNS hash uses) but with 7000 connections that would - * make an average of 1000 nodes in each list to run through. I upped that to - * 97 slots (I believe a prime is suitable) and noticed a significant speed - * increase. I need to reconsider the hash implementation or use a rather - * large default value like this. At 9000 connections I was still below 10us - * per call." - * - */ -static void sh_init(struct Curl_hash *hash, int hashsize) -{ - Curl_hash_init(hash, hashsize, hash_fd, fd_key_compare, - sh_freeentry); -} - -/* - * multi_addmsg() - * - * Called when a transfer is completed. Adds the given msg pointer to - * the list kept in the multi handle. - */ -static CURLMcode multi_addmsg(struct Curl_multi *multi, - struct Curl_message *msg) -{ - Curl_llist_insert_next(&multi->msglist, multi->msglist.tail, msg, - &msg->list); - return CURLM_OK; -} - -struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */ - int chashsize, /* connection hash */ - int dnssize) /* dns hash */ -{ - struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi)); - - if(!multi) - return NULL; - - multi->magic = CURL_MULTI_HANDLE; - - Curl_init_dnscache(&multi->hostcache, dnssize); - - sh_init(&multi->sockhash, hashsize); - - if(Curl_conncache_init(&multi->conn_cache, chashsize)) - goto error; - - Curl_llist_init(&multi->msglist, NULL); - Curl_llist_init(&multi->pending, NULL); - - multi->multiplexing = TRUE; - - /* -1 means it not set by user, use the default value */ - multi->maxconnects = -1; - multi->max_concurrent_streams = 100; - -#ifdef USE_WINSOCK - multi->wsa_event = WSACreateEvent(); - if(multi->wsa_event == WSA_INVALID_EVENT) - goto error; -#else -#ifdef ENABLE_WAKEUP - if(wakeup_create(multi->wakeup_pair) < 0) { - multi->wakeup_pair[0] = CURL_SOCKET_BAD; - multi->wakeup_pair[1] = CURL_SOCKET_BAD; - } - else if(curlx_nonblock(multi->wakeup_pair[0], TRUE) < 0 || - curlx_nonblock(multi->wakeup_pair[1], TRUE) < 0) { - wakeup_close(multi->wakeup_pair[0]); - wakeup_close(multi->wakeup_pair[1]); - multi->wakeup_pair[0] = CURL_SOCKET_BAD; - multi->wakeup_pair[1] = CURL_SOCKET_BAD; - } -#endif -#endif - - return multi; - - error: - - sockhash_destroy(&multi->sockhash); - Curl_hash_destroy(&multi->hostcache); - Curl_conncache_destroy(&multi->conn_cache); - Curl_llist_destroy(&multi->msglist, NULL); - Curl_llist_destroy(&multi->pending, NULL); - - free(multi); - return NULL; -} - -struct Curl_multi *curl_multi_init(void) -{ - return Curl_multi_handle(CURL_SOCKET_HASH_TABLE_SIZE, - CURL_CONNECTION_HASH_SIZE, - CURL_DNS_HASH_SIZE); -} - -CURLMcode curl_multi_add_handle(struct Curl_multi *multi, - struct Curl_easy *data) -{ - CURLMcode rc; - /* First, make some basic checks that the CURLM handle is a good handle */ - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - - /* Verify that we got a somewhat good easy handle too */ - if(!GOOD_EASY_HANDLE(data)) - return CURLM_BAD_EASY_HANDLE; - - /* Prevent users from adding same easy handle more than once and prevent - adding to more than one multi stack */ - if(data->multi) - return CURLM_ADDED_ALREADY; - - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - - if(multi->dead) { - /* a "dead" handle cannot get added transfers while any existing easy - handles are still alive - but if there are none alive anymore, it is - fine to start over and unmark the "deadness" of this handle */ - if(multi->num_alive) - return CURLM_ABORTED_BY_CALLBACK; - multi->dead = FALSE; - } - - /* Initialize timeout list for this handle */ - Curl_llist_init(&data->state.timeoutlist, NULL); - - /* - * No failure allowed in this function beyond this point. And no - * modification of easy nor multi handle allowed before this except for - * potential multi's connection cache growing which won't be undone in this - * function no matter what. - */ - if(data->set.errorbuffer) - data->set.errorbuffer[0] = 0; - - /* make the Curl_easy refer back to this multi handle - before Curl_expire() - is called. */ - data->multi = multi; - - /* Set the timeout for this handle to expire really soon so that it will - be taken care of even when this handle is added in the midst of operation - when only the curl_multi_socket() API is used. During that flow, only - sockets that time-out or have actions will be dealt with. Since this - handle has no action yet, we make sure it times out to get things to - happen. */ - Curl_expire(data, 0, EXPIRE_RUN_NOW); - - /* A somewhat crude work-around for a little glitch in Curl_update_timer() - that happens if the lastcall time is set to the same time when the handle - is removed as when the next handle is added, as then the check in - Curl_update_timer() that prevents calling the application multiple times - with the same timer info will not trigger and then the new handle's - timeout will not be notified to the app. - - The work-around is thus simply to clear the 'lastcall' variable to force - Curl_update_timer() to always trigger a callback to the app when a new - easy handle is added */ - memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall)); - - rc = Curl_update_timer(multi); - if(rc) - return rc; - - /* set the easy handle */ - multistate(data, MSTATE_INIT); - - /* for multi interface connections, we share DNS cache automatically if the - easy handle's one is currently not set. */ - if(!data->dns.hostcache || - (data->dns.hostcachetype == HCACHE_NONE)) { - data->dns.hostcache = &multi->hostcache; - data->dns.hostcachetype = HCACHE_MULTI; - } - - /* Point to the shared or multi handle connection cache */ - if(data->share && (data->share->specifier & (1<< CURL_LOCK_DATA_CONNECT))) - data->state.conn_cache = &data->share->conn_cache; - else - data->state.conn_cache = &multi->conn_cache; - data->state.lastconnect_id = -1; - -#ifdef USE_LIBPSL - /* Do the same for PSL. */ - if(data->share && (data->share->specifier & (1 << CURL_LOCK_DATA_PSL))) - data->psl = &data->share->psl; - else - data->psl = &multi->psl; -#endif - - /* We add the new entry last in the list. */ - data->next = NULL; /* end of the line */ - if(multi->easyp) { - struct Curl_easy *last = multi->easylp; - last->next = data; - data->prev = last; - multi->easylp = data; /* the new last node */ - } - else { - /* first node, make prev NULL! */ - data->prev = NULL; - multi->easylp = multi->easyp = data; /* both first and last */ - } - - /* increase the node-counter */ - multi->num_easy++; - - /* increase the alive-counter */ - multi->num_alive++; - - CONNCACHE_LOCK(data); - /* The closure handle only ever has default timeouts set. To improve the - state somewhat we clone the timeouts from each added handle so that the - closure handle always has the same timeouts as the most recently added - easy handle. */ - data->state.conn_cache->closure_handle->set.timeout = data->set.timeout; - data->state.conn_cache->closure_handle->set.server_response_timeout = - data->set.server_response_timeout; - data->state.conn_cache->closure_handle->set.no_signal = - data->set.no_signal; - CONNCACHE_UNLOCK(data); - - return CURLM_OK; -} - -#if 0 -/* Debug-function, used like this: - * - * Curl_hash_print(&multi->sockhash, debug_print_sock_hash); - * - * Enable the hash print function first by editing hash.c - */ -static void debug_print_sock_hash(void *p) -{ - struct Curl_sh_entry *sh = (struct Curl_sh_entry *)p; - - fprintf(stderr, " [readers %u][writers %u]", - sh->readers, sh->writers); -} -#endif - -static CURLcode multi_done(struct Curl_easy *data, - CURLcode status, /* an error if this is called - after an error was detected */ - bool premature) -{ - CURLcode result; - struct connectdata *conn = data->conn; - unsigned int i; - - DEBUGF(infof(data, "multi_done: status: %d prem: %d done: %d", - (int)status, (int)premature, data->state.done)); - - if(data->state.done) - /* Stop if multi_done() has already been called */ - return CURLE_OK; - - /* Stop the resolver and free its own resources (but not dns_entry yet). */ - Curl_resolver_kill(data); - - /* Cleanup possible redirect junk */ - Curl_safefree(data->req.newurl); - Curl_safefree(data->req.location); - - switch(status) { - case CURLE_ABORTED_BY_CALLBACK: - case CURLE_READ_ERROR: - case CURLE_WRITE_ERROR: - /* When we're aborted due to a callback return code it basically have to - be counted as premature as there is trouble ahead if we don't. We have - many callbacks and protocols work differently, we could potentially do - this more fine-grained in the future. */ - premature = TRUE; - default: - break; - } - - /* this calls the protocol-specific function pointer previously set */ - if(conn->handler->done) - result = conn->handler->done(data, status, premature); - else - result = status; - - if(CURLE_ABORTED_BY_CALLBACK != result) { - /* avoid this if we already aborted by callback to avoid this calling - another callback */ - int rc = Curl_pgrsDone(data); - if(!result && rc) - result = CURLE_ABORTED_BY_CALLBACK; - } - - process_pending_handles(data->multi); /* connection / multiplex */ - - CONNCACHE_LOCK(data); - Curl_detach_connection(data); - if(CONN_INUSE(conn)) { - /* Stop if still used. */ - CONNCACHE_UNLOCK(data); - DEBUGF(infof(data, "Connection still in use %zu, " - "no more multi_done now!", - conn->easyq.size)); - return CURLE_OK; - } - - data->state.done = TRUE; /* called just now! */ - - if(conn->dns_entry) { - Curl_resolv_unlock(data, conn->dns_entry); /* done with this */ - conn->dns_entry = NULL; - } - Curl_hostcache_prune(data); - Curl_safefree(data->state.ulbuf); - - /* if the transfer was completed in a paused state there can be buffered - data left to free */ - for(i = 0; i < data->state.tempcount; i++) { - Curl_dyn_free(&data->state.tempwrite[i].b); - } - data->state.tempcount = 0; - - /* if data->set.reuse_forbid is TRUE, it means the libcurl client has - forced us to close this connection. This is ignored for requests taking - place in a NTLM/NEGOTIATE authentication handshake - - if conn->bits.close is TRUE, it means that the connection should be - closed in spite of all our efforts to be nice, due to protocol - restrictions in our or the server's end - - if premature is TRUE, it means this connection was said to be DONE before - the entire request operation is complete and thus we can't know in what - state it is for re-using, so we're forced to close it. In a perfect world - we can add code that keep track of if we really must close it here or not, - but currently we have no such detail knowledge. - */ - - if((data->set.reuse_forbid -#if defined(USE_NTLM) - && !(conn->http_ntlm_state == NTLMSTATE_TYPE2 || - conn->proxy_ntlm_state == NTLMSTATE_TYPE2) -#endif -#if defined(USE_SPNEGO) - && !(conn->http_negotiate_state == GSS_AUTHRECV || - conn->proxy_negotiate_state == GSS_AUTHRECV) -#endif - ) || conn->bits.close - || (premature && !(conn->handler->flags & PROTOPT_STREAM))) { - connclose(conn, "disconnecting"); - Curl_conncache_remove_conn(data, conn, FALSE); - CONNCACHE_UNLOCK(data); - Curl_disconnect(data, conn, premature); - } - else { - char buffer[256]; - const char *host = -#ifndef CURL_DISABLE_PROXY - conn->bits.socksproxy ? - conn->socks_proxy.host.dispname : - conn->bits.httpproxy ? conn->http_proxy.host.dispname : -#endif - conn->bits.conn_to_host ? conn->conn_to_host.dispname : - conn->host.dispname; - /* create string before returning the connection */ - long connection_id = conn->connection_id; - msnprintf(buffer, sizeof(buffer), - "Connection #%ld to host %s left intact", - connection_id, host); - /* the connection is no longer in use by this transfer */ - CONNCACHE_UNLOCK(data); - if(Curl_conncache_return_conn(data, conn)) { - /* remember the most recently used connection */ - data->state.lastconnect_id = connection_id; - infof(data, "%s", buffer); - } - else - data->state.lastconnect_id = -1; - } - - Curl_safefree(data->state.buffer); - return result; -} - -static int close_connect_only(struct Curl_easy *data, - struct connectdata *conn, void *param) -{ - (void)param; - if(data->state.lastconnect_id != conn->connection_id) - return 0; - - if(!conn->connect_only) - return 1; - - connclose(conn, "Removing connect-only easy handle"); - - return 1; -} - -CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, - struct Curl_easy *data) -{ - struct Curl_easy *easy = data; - bool premature; - struct Curl_llist_element *e; - CURLMcode rc; - - /* First, make some basic checks that the CURLM handle is a good handle */ - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - - /* Verify that we got a somewhat good easy handle too */ - if(!GOOD_EASY_HANDLE(data)) - return CURLM_BAD_EASY_HANDLE; - - /* Prevent users from trying to remove same easy handle more than once */ - if(!data->multi) - return CURLM_OK; /* it is already removed so let's say it is fine! */ - - /* Prevent users from trying to remove an easy handle from the wrong multi */ - if(data->multi != multi) - return CURLM_BAD_EASY_HANDLE; - - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - - premature = (data->mstate < MSTATE_COMPLETED) ? TRUE : FALSE; - - /* If the 'state' is not INIT or COMPLETED, we might need to do something - nice to put the easy_handle in a good known state when this returns. */ - if(premature) { - /* this handle is "alive" so we need to count down the total number of - alive connections when this is removed */ - multi->num_alive--; - } - - if(data->conn && - data->mstate > MSTATE_DO && - data->mstate < MSTATE_COMPLETED) { - /* Set connection owner so that the DONE function closes it. We can - safely do this here since connection is killed. */ - streamclose(data->conn, "Removed with partial response"); - } - - if(data->conn) { - /* multi_done() clears the association between the easy handle and the - connection. - - Note that this ignores the return code simply because there's - nothing really useful to do with it anyway! */ - (void)multi_done(data, data->result, premature); - } - - /* The timer must be shut down before data->multi is set to NULL, else the - timenode will remain in the splay tree after curl_easy_cleanup is - called. Do it after multi_done() in case that sets another time! */ - Curl_expire_clear(data); - - if(data->connect_queue.ptr) - /* the handle was in the pending list waiting for an available connection, - so go ahead and remove it */ - Curl_llist_remove(&multi->pending, &data->connect_queue, NULL); - - if(data->dns.hostcachetype == HCACHE_MULTI) { - /* stop using the multi handle's DNS cache, *after* the possible - multi_done() call above */ - data->dns.hostcache = NULL; - data->dns.hostcachetype = HCACHE_NONE; - } - - Curl_wildcard_dtor(&data->wildcard); - - /* destroy the timeout list that is held in the easy handle, do this *after* - multi_done() as that may actually call Curl_expire that uses this */ - Curl_llist_destroy(&data->state.timeoutlist, NULL); - - /* change state without using multistate(), only to make singlesocket() do - what we want */ - data->mstate = MSTATE_COMPLETED; - - /* This ignores the return code even in case of problems because there's - nothing more to do about that, here */ - (void)singlesocket(multi, easy); /* to let the application know what sockets - that vanish with this handle */ - - /* Remove the association between the connection and the handle */ - Curl_detach_connection(data); - - if(data->set.connect_only && !data->multi_easy) { - /* This removes a handle that was part the multi interface that used - CONNECT_ONLY, that connection is now left alive but since this handle - has bits.close set nothing can use that transfer anymore and it is - forbidden from reuse. And this easy handle cannot find the connection - anymore once removed from the multi handle - - Better close the connection here, at once. - */ - struct connectdata *c; - curl_socket_t s; - s = Curl_getconnectinfo(data, &c); - if((s != CURL_SOCKET_BAD) && c) { - Curl_conncache_remove_conn(data, c, TRUE); - Curl_disconnect(data, c, TRUE); - } - } - - if(data->state.lastconnect_id != -1) { - /* Mark any connect-only connection for closure */ - Curl_conncache_foreach(data, data->state.conn_cache, - NULL, close_connect_only); - } - -#ifdef USE_LIBPSL - /* Remove the PSL association. */ - if(data->psl == &multi->psl) - data->psl = NULL; -#endif - - /* as this was using a shared connection cache we clear the pointer to that - since we're not part of that multi handle anymore */ - data->state.conn_cache = NULL; - - data->multi = NULL; /* clear the association to this multi handle */ - - /* make sure there's no pending message in the queue sent from this easy - handle */ - - for(e = multi->msglist.head; e; e = e->next) { - struct Curl_message *msg = e->ptr; - - if(msg->extmsg.easy_handle == easy) { - Curl_llist_remove(&multi->msglist, e, NULL); - /* there can only be one from this specific handle */ - break; - } - } - - /* Remove from the pending list if it is there. Otherwise this will - remain on the pending list forever due to the state change. */ - for(e = multi->pending.head; e; e = e->next) { - struct Curl_easy *curr_data = e->ptr; - - if(curr_data == data) { - Curl_llist_remove(&multi->pending, e, NULL); - break; - } - } - - /* make the previous node point to our next */ - if(data->prev) - data->prev->next = data->next; - else - multi->easyp = data->next; /* point to first node */ - - /* make our next point to our previous node */ - if(data->next) - data->next->prev = data->prev; - else - multi->easylp = data->prev; /* point to last node */ - - /* NOTE NOTE NOTE - We do not touch the easy handle here! */ - multi->num_easy--; /* one less to care about now */ - - process_pending_handles(multi); - - rc = Curl_update_timer(multi); - if(rc) - return rc; - return CURLM_OK; -} - -/* Return TRUE if the application asked for multiplexing */ -bool Curl_multiplex_wanted(const struct Curl_multi *multi) -{ - return (multi && (multi->multiplexing)); -} - -/* - * Curl_detach_connection() removes the given transfer from the connection. - * - * This is the only function that should clear data->conn. This will - * occasionally be called with the data->conn pointer already cleared. - */ -void Curl_detach_connection(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - if(conn) { - Curl_connect_done(data); /* if mid-CONNECT, shut it down */ - Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL); - Curl_ssl_detach_conn(data, conn); - } - data->conn = NULL; -} - -/* - * Curl_attach_connection() attaches this transfer to this connection. - * - * This is the only function that should assign data->conn - */ -void Curl_attach_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - DEBUGASSERT(!data->conn); - DEBUGASSERT(conn); - data->conn = conn; - Curl_llist_insert_next(&conn->easyq, conn->easyq.tail, data, - &data->conn_queue); - if(conn->handler->attach) - conn->handler->attach(data, conn); - Curl_ssl_associate_conn(data, conn); -} - -static int waitconnect_getsock(struct connectdata *conn, - curl_socket_t *sock) -{ - int i; - int s = 0; - int rc = 0; - -#ifdef USE_SSL -#ifndef CURL_DISABLE_PROXY - if(CONNECT_FIRSTSOCKET_PROXY_SSL()) - return Curl_ssl->getsock(conn, sock); -#endif -#endif - - if(SOCKS_STATE(conn->cnnct.state)) - return Curl_SOCKS_getsock(conn, sock, FIRSTSOCKET); - - for(i = 0; i<2; i++) { - if(conn->tempsock[i] != CURL_SOCKET_BAD) { - sock[s] = conn->tempsock[i]; - rc |= GETSOCK_WRITESOCK(s); -#ifdef ENABLE_QUIC - if(conn->transport == TRNSPRT_QUIC) - /* when connecting QUIC, we want to read the socket too */ - rc |= GETSOCK_READSOCK(s); -#endif - s++; - } - } - - return rc; -} - -static int waitproxyconnect_getsock(struct connectdata *conn, - curl_socket_t *sock) -{ - sock[0] = conn->sock[FIRSTSOCKET]; - - if(conn->connect_state) - return Curl_connect_getsock(conn); - - return GETSOCK_WRITESOCK(0); -} - -static int domore_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *socks) -{ - if(conn && conn->handler->domore_getsock) - return conn->handler->domore_getsock(data, conn, socks); - return GETSOCK_BLANK; -} - -static int doing_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *socks) -{ - if(conn && conn->handler->doing_getsock) - return conn->handler->doing_getsock(data, conn, socks); - return GETSOCK_BLANK; -} - -static int protocol_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *socks) -{ - if(conn->handler->proto_getsock) - return conn->handler->proto_getsock(data, conn, socks); - /* Backup getsock logic. Since there is a live socket in use, we must wait - for it or it will be removed from watching when the multi_socket API is - used. */ - socks[0] = conn->sock[FIRSTSOCKET]; - return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0); -} - -/* returns bitmapped flags for this handle and its sockets. The 'socks[]' - array contains MAX_SOCKSPEREASYHANDLE entries. */ -static int multi_getsock(struct Curl_easy *data, - curl_socket_t *socks) -{ - struct connectdata *conn = data->conn; - /* The no connection case can happen when this is called from - curl_multi_remove_handle() => singlesocket() => multi_getsock(). - */ - if(!conn) - return 0; - - switch(data->mstate) { - default: - return 0; - - case MSTATE_RESOLVING: - return Curl_resolv_getsock(data, socks); - - case MSTATE_PROTOCONNECTING: - case MSTATE_PROTOCONNECT: - return protocol_getsock(data, conn, socks); - - case MSTATE_DO: - case MSTATE_DOING: - return doing_getsock(data, conn, socks); - - case MSTATE_TUNNELING: - return waitproxyconnect_getsock(conn, socks); - - case MSTATE_CONNECTING: - return waitconnect_getsock(conn, socks); - - case MSTATE_DOING_MORE: - return domore_getsock(data, conn, socks); - - case MSTATE_DID: /* since is set after DO is completed, we switch to - waiting for the same as the PERFORMING state */ - case MSTATE_PERFORMING: - return Curl_single_getsock(data, conn, socks); - } - -} - -CURLMcode curl_multi_fdset(struct Curl_multi *multi, - fd_set *read_fd_set, fd_set *write_fd_set, - fd_set *exc_fd_set, int *max_fd) -{ - /* Scan through all the easy handles to get the file descriptors set. - Some easy handles may not have connected to the remote host yet, - and then we must make sure that is done. */ - struct Curl_easy *data; - int this_max_fd = -1; - curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE]; - int i; - (void)exc_fd_set; /* not used */ - - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - - data = multi->easyp; - while(data) { - int bitmap; -#ifdef __clang_analyzer_ - /* to prevent "The left operand of '>=' is a garbage value" warnings */ - memset(sockbunch, 0, sizeof(sockbunch)); -#endif - bitmap = multi_getsock(data, sockbunch); - - for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) { - curl_socket_t s = CURL_SOCKET_BAD; - - if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK(sockbunch[i])) { - if(!FDSET_SOCK(sockbunch[i])) - /* pretend it doesn't exist */ - continue; - FD_SET(sockbunch[i], read_fd_set); - s = sockbunch[i]; - } - if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK(sockbunch[i])) { - if(!FDSET_SOCK(sockbunch[i])) - /* pretend it doesn't exist */ - continue; - FD_SET(sockbunch[i], write_fd_set); - s = sockbunch[i]; - } - if(s == CURL_SOCKET_BAD) - /* this socket is unused, break out of loop */ - break; - if((int)s > this_max_fd) - this_max_fd = (int)s; - } - - data = data->next; /* check next handle */ - } - - *max_fd = this_max_fd; - - return CURLM_OK; -} - -#define NUM_POLLS_ON_STACK 10 - -static CURLMcode multi_wait(struct Curl_multi *multi, - struct curl_waitfd extra_fds[], - unsigned int extra_nfds, - int timeout_ms, - int *ret, - bool extrawait, /* when no socket, wait */ - bool use_wakeup) -{ - struct Curl_easy *data; - curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE]; - int bitmap; - unsigned int i; - unsigned int nfds = 0; - unsigned int curlfds; - long timeout_internal; - int retcode = 0; - struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; - struct pollfd *ufds = &a_few_on_stack[0]; - bool ufds_malloc = FALSE; -#ifdef USE_WINSOCK - WSANETWORKEVENTS wsa_events; - DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT); -#endif -#ifndef ENABLE_WAKEUP - (void)use_wakeup; -#endif - - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - - if(timeout_ms < 0) - return CURLM_BAD_FUNCTION_ARGUMENT; - - /* Count up how many fds we have from the multi handle */ - data = multi->easyp; - while(data) { - bitmap = multi_getsock(data, sockbunch); - - for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) { - curl_socket_t s = CURL_SOCKET_BAD; - - if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) { - ++nfds; - s = sockbunch[i]; - } - if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) { - ++nfds; - s = sockbunch[i]; - } - if(s == CURL_SOCKET_BAD) { - break; - } - } - - data = data->next; /* check next handle */ - } - - /* If the internally desired timeout is actually shorter than requested from - the outside, then use the shorter time! But only if the internal timer - is actually larger than -1! */ - (void)multi_timeout(multi, &timeout_internal); - if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms)) - timeout_ms = (int)timeout_internal; - - curlfds = nfds; /* number of internal file descriptors */ - nfds += extra_nfds; /* add the externally provided ones */ - -#ifdef ENABLE_WAKEUP -#ifdef USE_WINSOCK - if(use_wakeup) { -#else - if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { -#endif - ++nfds; - } -#endif - - if(nfds > NUM_POLLS_ON_STACK) { - /* 'nfds' is a 32 bit value and 'struct pollfd' is typically 8 bytes - big, so at 2^29 sockets this value might wrap. When a process gets - the capability to actually handle over 500 million sockets this - calculation needs a integer overflow check. */ - ufds = malloc(nfds * sizeof(struct pollfd)); - if(!ufds) - return CURLM_OUT_OF_MEMORY; - ufds_malloc = TRUE; - } - nfds = 0; - - /* only do the second loop if we found descriptors in the first stage run - above */ - - if(curlfds) { - /* Add the curl handles to our pollfds first */ - data = multi->easyp; - while(data) { - bitmap = multi_getsock(data, sockbunch); - - for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) { - curl_socket_t s = CURL_SOCKET_BAD; -#ifdef USE_WINSOCK - long mask = 0; -#endif - if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) { - s = sockbunch[i]; -#ifdef USE_WINSOCK - mask |= FD_READ|FD_ACCEPT|FD_CLOSE; -#endif - ufds[nfds].fd = s; - ufds[nfds].events = POLLIN; - ++nfds; - } - if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) { - s = sockbunch[i]; -#ifdef USE_WINSOCK - mask |= FD_WRITE|FD_CONNECT|FD_CLOSE; - send(s, NULL, 0, 0); /* reset FD_WRITE */ -#endif - ufds[nfds].fd = s; - ufds[nfds].events = POLLOUT; - ++nfds; - } - /* s is only set if either being readable or writable is checked */ - if(s == CURL_SOCKET_BAD) { - /* break on entry not checked for being readable or writable */ - break; - } -#ifdef USE_WINSOCK - if(WSAEventSelect(s, multi->wsa_event, mask) != 0) { - if(ufds_malloc) - free(ufds); - return CURLM_INTERNAL_ERROR; - } -#endif - } - - data = data->next; /* check next handle */ - } - } - - /* Add external file descriptions from poll-like struct curl_waitfd */ - for(i = 0; i < extra_nfds; i++) { -#ifdef USE_WINSOCK - long mask = 0; - if(extra_fds[i].events & CURL_WAIT_POLLIN) - mask |= FD_READ|FD_ACCEPT|FD_CLOSE; - if(extra_fds[i].events & CURL_WAIT_POLLPRI) - mask |= FD_OOB; - if(extra_fds[i].events & CURL_WAIT_POLLOUT) { - mask |= FD_WRITE|FD_CONNECT|FD_CLOSE; - send(extra_fds[i].fd, NULL, 0, 0); /* reset FD_WRITE */ - } - if(WSAEventSelect(extra_fds[i].fd, multi->wsa_event, mask) != 0) { - if(ufds_malloc) - free(ufds); - return CURLM_INTERNAL_ERROR; - } -#endif - ufds[nfds].fd = extra_fds[i].fd; - ufds[nfds].events = 0; - if(extra_fds[i].events & CURL_WAIT_POLLIN) - ufds[nfds].events |= POLLIN; - if(extra_fds[i].events & CURL_WAIT_POLLPRI) - ufds[nfds].events |= POLLPRI; - if(extra_fds[i].events & CURL_WAIT_POLLOUT) - ufds[nfds].events |= POLLOUT; - ++nfds; - } - -#ifdef ENABLE_WAKEUP -#ifndef USE_WINSOCK - if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { - ufds[nfds].fd = multi->wakeup_pair[0]; - ufds[nfds].events = POLLIN; - ++nfds; - } -#endif -#endif - -#if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK) - if(nfds || use_wakeup) { -#else - if(nfds) { -#endif - int pollrc; -#ifdef USE_WINSOCK - if(nfds) - pollrc = Curl_poll(ufds, nfds, 0); /* just pre-check with WinSock */ - else - pollrc = 0; -#else - pollrc = Curl_poll(ufds, nfds, timeout_ms); /* wait... */ -#endif - if(pollrc < 0) - return CURLM_UNRECOVERABLE_POLL; - - if(pollrc > 0) { - retcode = pollrc; -#ifdef USE_WINSOCK - } - else { /* now wait... if not ready during the pre-check (pollrc == 0) */ - WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, timeout_ms, FALSE); - } - /* With WinSock, we have to run the following section unconditionally - to call WSAEventSelect(fd, event, 0) on all the sockets */ - { -#endif - /* copy revents results from the poll to the curl_multi_wait poll - struct, the bit values of the actual underlying poll() implementation - may not be the same as the ones in the public libcurl API! */ - for(i = 0; i < extra_nfds; i++) { - unsigned r = ufds[curlfds + i].revents; - unsigned short mask = 0; -#ifdef USE_WINSOCK - curl_socket_t s = extra_fds[i].fd; - wsa_events.lNetworkEvents = 0; - if(WSAEnumNetworkEvents(s, NULL, &wsa_events) == 0) { - if(wsa_events.lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE)) - mask |= CURL_WAIT_POLLIN; - if(wsa_events.lNetworkEvents & (FD_WRITE|FD_CONNECT|FD_CLOSE)) - mask |= CURL_WAIT_POLLOUT; - if(wsa_events.lNetworkEvents & FD_OOB) - mask |= CURL_WAIT_POLLPRI; - if(ret && !pollrc && wsa_events.lNetworkEvents) - retcode++; - } - WSAEventSelect(s, multi->wsa_event, 0); - if(!pollrc) { - extra_fds[i].revents = mask; - continue; - } -#endif - if(r & POLLIN) - mask |= CURL_WAIT_POLLIN; - if(r & POLLOUT) - mask |= CURL_WAIT_POLLOUT; - if(r & POLLPRI) - mask |= CURL_WAIT_POLLPRI; - extra_fds[i].revents = mask; - } - -#ifdef USE_WINSOCK - /* Count up all our own sockets that had activity, - and remove them from the event. */ - if(curlfds) { - data = multi->easyp; - while(data) { - bitmap = multi_getsock(data, sockbunch); - - for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) { - if(bitmap & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))) { - wsa_events.lNetworkEvents = 0; - if(WSAEnumNetworkEvents(sockbunch[i], NULL, &wsa_events) == 0) { - if(ret && !pollrc && wsa_events.lNetworkEvents) - retcode++; - } - WSAEventSelect(sockbunch[i], multi->wsa_event, 0); - } - else { - /* break on entry not checked for being readable or writable */ - break; - } - } - - data = data->next; - } - } - - WSAResetEvent(multi->wsa_event); -#else -#ifdef ENABLE_WAKEUP - if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { - if(ufds[curlfds + extra_nfds].revents & POLLIN) { - char buf[64]; - ssize_t nread; - while(1) { - /* the reading socket is non-blocking, try to read - data from it until it receives an error (except EINTR). - In normal cases it will get EAGAIN or EWOULDBLOCK - when there is no more data, breaking the loop. */ - nread = wakeup_read(multi->wakeup_pair[0], buf, sizeof(buf)); - if(nread <= 0) { - if(nread < 0 && EINTR == SOCKERRNO) - continue; - break; - } - } - /* do not count the wakeup socket into the returned value */ - retcode--; - } - } -#endif -#endif - } - } - - if(ufds_malloc) - free(ufds); - if(ret) - *ret = retcode; -#if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK) - if(extrawait && !nfds && !use_wakeup) { -#else - if(extrawait && !nfds) { -#endif - long sleep_ms = 0; - - /* Avoid busy-looping when there's nothing particular to wait for */ - if(!curl_multi_timeout(multi, &sleep_ms) && sleep_ms) { - if(sleep_ms > timeout_ms) - sleep_ms = timeout_ms; - /* when there are no easy handles in the multi, this holds a -1 - timeout */ - else if(sleep_ms < 0) - sleep_ms = timeout_ms; - Curl_wait_ms(sleep_ms); - } - } - - return CURLM_OK; -} - -CURLMcode curl_multi_wait(struct Curl_multi *multi, - struct curl_waitfd extra_fds[], - unsigned int extra_nfds, - int timeout_ms, - int *ret) -{ - return multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, FALSE, - FALSE); -} - -CURLMcode curl_multi_poll(struct Curl_multi *multi, - struct curl_waitfd extra_fds[], - unsigned int extra_nfds, - int timeout_ms, - int *ret) -{ - return multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, TRUE, - TRUE); -} - -CURLMcode curl_multi_wakeup(struct Curl_multi *multi) -{ - /* this function is usually called from another thread, - it has to be careful only to access parts of the - Curl_multi struct that are constant */ - - /* GOOD_MULTI_HANDLE can be safely called */ - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - -#ifdef ENABLE_WAKEUP -#ifdef USE_WINSOCK - if(WSASetEvent(multi->wsa_event)) - return CURLM_OK; -#else - /* the wakeup_pair variable is only written during init and cleanup, - making it safe to access from another thread after the init part - and before cleanup */ - if(multi->wakeup_pair[1] != CURL_SOCKET_BAD) { - char buf[1]; - buf[0] = 1; - while(1) { - /* swrite() is not thread-safe in general, because concurrent calls - can have their messages interleaved, but in this case the content - of the messages does not matter, which makes it ok to call. - - The write socket is set to non-blocking, this way this function - cannot block, making it safe to call even from the same thread - that will call curl_multi_wait(). If swrite() returns that it - would block, it's considered successful because it means that - previous calls to this function will wake up the poll(). */ - if(wakeup_write(multi->wakeup_pair[1], buf, sizeof(buf)) < 0) { - int err = SOCKERRNO; - int return_success; -#ifdef USE_WINSOCK - return_success = WSAEWOULDBLOCK == err; -#else - if(EINTR == err) - continue; - return_success = EWOULDBLOCK == err || EAGAIN == err; -#endif - if(!return_success) - return CURLM_WAKEUP_FAILURE; - } - return CURLM_OK; - } - } -#endif -#endif - return CURLM_WAKEUP_FAILURE; -} - -/* - * multi_ischanged() is called - * - * Returns TRUE/FALSE whether the state is changed to trigger a CONNECT_PEND - * => CONNECT action. - * - * Set 'clear' to TRUE to have it also clear the state variable. - */ -static bool multi_ischanged(struct Curl_multi *multi, bool clear) -{ - bool retval = multi->recheckstate; - if(clear) - multi->recheckstate = FALSE; - return retval; -} - -CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, - struct Curl_easy *data, - struct connectdata *conn) -{ - CURLMcode rc; - - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - - rc = curl_multi_add_handle(multi, data); - if(!rc) { - struct SingleRequest *k = &data->req; - - /* pass in NULL for 'conn' here since we don't want to init the - connection, only this transfer */ - Curl_init_do(data, NULL); - - /* take this handle to the perform state right away */ - multistate(data, MSTATE_PERFORMING); - Curl_attach_connection(data, conn); - k->keepon |= KEEP_RECV; /* setup to receive! */ - } - return rc; -} - -static CURLcode multi_do(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - - DEBUGASSERT(conn); - DEBUGASSERT(conn->handler); - - if(conn->handler->do_it) - /* generic protocol-specific function pointer set in curl_connect() */ - result = conn->handler->do_it(data, done); - - return result; -} - -/* - * multi_do_more() is called during the DO_MORE multi state. It is basically a - * second stage DO state which (wrongly) was introduced to support FTP's - * second connection. - * - * 'complete' can return 0 for incomplete, 1 for done and -1 for go back to - * DOING state there's more work to do! - */ - -static CURLcode multi_do_more(struct Curl_easy *data, int *complete) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - - *complete = 0; - - if(conn->handler->do_more) - result = conn->handler->do_more(data, complete); - - return result; -} - -/* - * Check whether a timeout occurred, and handle it if it did - */ -static bool multi_handle_timeout(struct Curl_easy *data, - struct curltime *now, - bool *stream_error, - CURLcode *result, - bool connect_timeout) -{ - timediff_t timeout_ms; - timeout_ms = Curl_timeleft(data, now, connect_timeout); - - if(timeout_ms < 0) { - /* Handle timed out */ - if(data->mstate == MSTATE_RESOLVING) - failf(data, "Resolving timed out after %" CURL_FORMAT_TIMEDIFF_T - " milliseconds", - Curl_timediff(*now, data->progress.t_startsingle)); - else if(data->mstate == MSTATE_CONNECTING) - failf(data, "Connection timed out after %" CURL_FORMAT_TIMEDIFF_T - " milliseconds", - Curl_timediff(*now, data->progress.t_startsingle)); - else { - struct SingleRequest *k = &data->req; - if(k->size != -1) { - failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T - " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %" - CURL_FORMAT_CURL_OFF_T " bytes received", - Curl_timediff(*now, data->progress.t_startsingle), - k->bytecount, k->size); - } - else { - failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T - " milliseconds with %" CURL_FORMAT_CURL_OFF_T - " bytes received", - Curl_timediff(*now, data->progress.t_startsingle), - k->bytecount); - } - } - - /* Force connection closed if the connection has indeed been used */ - if(data->mstate > MSTATE_DO) { - streamclose(data->conn, "Disconnected with pending data"); - *stream_error = TRUE; - } - *result = CURLE_OPERATION_TIMEDOUT; - (void)multi_done(data, *result, TRUE); - } - - return (timeout_ms < 0); -} - -/* - * We are doing protocol-specific connecting and this is being called over and - * over from the multi interface until the connection phase is done on - * protocol layer. - */ - -static CURLcode protocol_connecting(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - - if(conn && conn->handler->connecting) { - *done = FALSE; - result = conn->handler->connecting(data, done); - } - else - *done = TRUE; - - return result; -} - -/* - * We are DOING this is being called over and over from the multi interface - * until the DOING phase is done on protocol layer. - */ - -static CURLcode protocol_doing(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - - if(conn && conn->handler->doing) { - *done = FALSE; - result = conn->handler->doing(data, done); - } - else - *done = TRUE; - - return result; -} - -/* - * We have discovered that the TCP connection has been successful, we can now - * proceed with some action. - * - */ -static CURLcode protocol_connect(struct Curl_easy *data, - bool *protocol_done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - DEBUGASSERT(conn); - DEBUGASSERT(protocol_done); - - *protocol_done = FALSE; - - if(conn->bits.tcpconnect[FIRSTSOCKET] && conn->bits.protoconnstart) { - /* We already are connected, get back. This may happen when the connect - worked fine in the first call, like when we connect to a local server - or proxy. Note that we don't know if the protocol is actually done. - - Unless this protocol doesn't have any protocol-connect callback, as - then we know we're done. */ - if(!conn->handler->connecting) - *protocol_done = TRUE; - - return CURLE_OK; - } - - if(!conn->bits.protoconnstart) { -#ifndef CURL_DISABLE_PROXY - result = Curl_proxy_connect(data, FIRSTSOCKET); - if(result) - return result; - - if(CONNECT_FIRSTSOCKET_PROXY_SSL()) - /* wait for HTTPS proxy SSL initialization to complete */ - return CURLE_OK; - - if(conn->bits.tunnel_proxy && conn->bits.httpproxy && - Curl_connect_ongoing(conn)) - /* when using an HTTP tunnel proxy, await complete tunnel establishment - before proceeding further. Return CURLE_OK so we'll be called again */ - return CURLE_OK; -#endif - if(conn->handler->connect_it) { - /* is there a protocol-specific connect() procedure? */ - - /* Call the protocol-specific connect function */ - result = conn->handler->connect_it(data, protocol_done); - } - else - *protocol_done = TRUE; - - /* it has started, possibly even completed but that knowledge isn't stored - in this bit! */ - if(!result) - conn->bits.protoconnstart = TRUE; - } - - return result; /* pass back status */ -} - -/* - * Curl_preconnect() is called immediately before a connect starts. When a - * redirect is followed, this is then called multiple times during a single - * transfer. - */ -CURLcode Curl_preconnect(struct Curl_easy *data) -{ - if(!data->state.buffer) { - data->state.buffer = malloc(data->set.buffer_size + 1); - if(!data->state.buffer) - return CURLE_OUT_OF_MEMORY; - } - return CURLE_OK; -} - -static void set_in_callback(struct Curl_multi *multi, bool value) -{ - multi->in_callback = value; -} - -static CURLMcode multi_runsingle(struct Curl_multi *multi, - struct curltime *nowp, - struct Curl_easy *data) -{ - struct Curl_message *msg = NULL; - bool connected; - bool async; - bool protocol_connected = FALSE; - bool dophase_done = FALSE; - bool done = FALSE; - CURLMcode rc; - CURLcode result = CURLE_OK; - timediff_t recv_timeout_ms; - timediff_t send_timeout_ms; - int control; - - if(!GOOD_EASY_HANDLE(data)) - return CURLM_BAD_EASY_HANDLE; - - if(multi->dead) { - /* a multi-level callback returned error before, meaning every individual - transfer now has failed */ - result = CURLE_ABORTED_BY_CALLBACK; - Curl_posttransfer(data); - multi_done(data, result, FALSE); - multistate(data, MSTATE_COMPLETED); - } - - do { - /* A "stream" here is a logical stream if the protocol can handle that - (HTTP/2), or the full connection for older protocols */ - bool stream_error = FALSE; - rc = CURLM_OK; - - if(multi_ischanged(multi, TRUE)) { - DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue")); - process_pending_handles(multi); /* multiplexed */ - } - - if(data->mstate > MSTATE_CONNECT && - data->mstate < MSTATE_COMPLETED) { - /* Make sure we set the connection's current owner */ - DEBUGASSERT(data->conn); - if(!data->conn) - return CURLM_INTERNAL_ERROR; - } - - if(data->conn && - (data->mstate >= MSTATE_CONNECT) && - (data->mstate < MSTATE_COMPLETED)) { - /* Check for overall operation timeout here but defer handling the - * connection timeout to later, to allow for a connection to be set up - * in the window since we last checked timeout. This prevents us - * tearing down a completed connection in the case where we were slow - * to check the timeout (e.g. process descheduled during this loop). - * We set connect_timeout=FALSE to do this. */ - - /* we need to wait for the connect state as only then is the start time - stored, but we must not check already completed handles */ - if(multi_handle_timeout(data, nowp, &stream_error, &result, FALSE)) { - /* Skip the statemachine and go directly to error handling section. */ - goto statemachine_end; - } - } - - switch(data->mstate) { - case MSTATE_INIT: - /* init this transfer. */ - result = Curl_pretransfer(data); - - if(!result) { - /* after init, go CONNECT */ - multistate(data, MSTATE_CONNECT); - *nowp = Curl_pgrsTime(data, TIMER_STARTOP); - rc = CURLM_CALL_MULTI_PERFORM; - } - break; - - case MSTATE_PENDING: - /* We will stay here until there is a connection available. Then - we try again in the MSTATE_CONNECT state. */ - break; - - case MSTATE_CONNECT: - /* Connect. We want to get a connection identifier filled in. */ - /* init this transfer. */ - result = Curl_preconnect(data); - if(result) - break; - - *nowp = Curl_pgrsTime(data, TIMER_STARTSINGLE); - if(data->set.timeout) - Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT); - - if(data->set.connecttimeout) - Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT); - - result = Curl_connect(data, &async, &protocol_connected); - if(CURLE_NO_CONNECTION_AVAILABLE == result) { - /* There was no connection available. We will go to the pending - state and wait for an available connection. */ - multistate(data, MSTATE_PENDING); - - /* add this handle to the list of connect-pending handles */ - Curl_llist_insert_next(&multi->pending, multi->pending.tail, data, - &data->connect_queue); - result = CURLE_OK; - break; - } - else if(data->state.previouslypending) { - /* this transfer comes from the pending queue so try move another */ - infof(data, "Transfer was pending, now try another"); - process_pending_handles(data->multi); - } - - if(!result) { - if(async) - /* We're now waiting for an asynchronous name lookup */ - multistate(data, MSTATE_RESOLVING); - else { - /* after the connect has been sent off, go WAITCONNECT unless the - protocol connect is already done and we can go directly to - WAITDO or DO! */ - rc = CURLM_CALL_MULTI_PERFORM; - - if(protocol_connected) - multistate(data, MSTATE_DO); - else { -#ifndef CURL_DISABLE_HTTP - if(Curl_connect_ongoing(data->conn)) - multistate(data, MSTATE_TUNNELING); - else -#endif - multistate(data, MSTATE_CONNECTING); - } - } - } - break; - - case MSTATE_RESOLVING: - /* awaiting an asynch name resolve to complete */ - { - struct Curl_dns_entry *dns = NULL; - struct connectdata *conn = data->conn; - const char *hostname; - - DEBUGASSERT(conn); -#ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy) - hostname = conn->http_proxy.host.name; - else -#endif - if(conn->bits.conn_to_host) - hostname = conn->conn_to_host.name; - else - hostname = conn->host.name; - - /* check if we have the name resolved by now */ - dns = Curl_fetch_addr(data, hostname, (int)conn->port); - - if(dns) { -#ifdef CURLRES_ASYNCH - data->state.async.dns = dns; - data->state.async.done = TRUE; -#endif - result = CURLE_OK; - infof(data, "Hostname '%s' was found in DNS cache", hostname); - } - - if(!dns) - result = Curl_resolv_check(data, &dns); - - /* Update sockets here, because the socket(s) may have been - closed and the application thus needs to be told, even if it - is likely that the same socket(s) will again be used further - down. If the name has not yet been resolved, it is likely - that new sockets have been opened in an attempt to contact - another resolver. */ - rc = singlesocket(multi, data); - if(rc) - return rc; - - if(dns) { - /* Perform the next step in the connection phase, and then move on - to the WAITCONNECT state */ - result = Curl_once_resolved(data, &protocol_connected); - - if(result) - /* if Curl_once_resolved() returns failure, the connection struct - is already freed and gone */ - data->conn = NULL; /* no more connection */ - else { - /* call again please so that we get the next socket setup */ - rc = CURLM_CALL_MULTI_PERFORM; - if(protocol_connected) - multistate(data, MSTATE_DO); - else { -#ifndef CURL_DISABLE_HTTP - if(Curl_connect_ongoing(data->conn)) - multistate(data, MSTATE_TUNNELING); - else -#endif - multistate(data, MSTATE_CONNECTING); - } - } - } - - if(result) { - /* failure detected */ - stream_error = TRUE; - break; - } - } - break; - -#ifndef CURL_DISABLE_HTTP - case MSTATE_TUNNELING: - /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */ - DEBUGASSERT(data->conn); - result = Curl_http_connect(data, &protocol_connected); -#ifndef CURL_DISABLE_PROXY - if(data->conn->bits.proxy_connect_closed) { - rc = CURLM_CALL_MULTI_PERFORM; - /* connect back to proxy again */ - result = CURLE_OK; - multi_done(data, CURLE_OK, FALSE); - multistate(data, MSTATE_CONNECT); - } - else -#endif - if(!result) { - if( -#ifndef CURL_DISABLE_PROXY - (data->conn->http_proxy.proxytype != CURLPROXY_HTTPS || - data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) && -#endif - Curl_connect_complete(data->conn)) { - rc = CURLM_CALL_MULTI_PERFORM; - /* initiate protocol connect phase */ - multistate(data, MSTATE_PROTOCONNECT); - } - } - else - stream_error = TRUE; - break; -#endif - - case MSTATE_CONNECTING: - /* awaiting a completion of an asynch TCP connect */ - DEBUGASSERT(data->conn); - result = Curl_is_connected(data, data->conn, FIRSTSOCKET, &connected); - if(connected && !result) { -#ifndef CURL_DISABLE_HTTP - if( -#ifndef CURL_DISABLE_PROXY - (data->conn->http_proxy.proxytype == CURLPROXY_HTTPS && - !data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) || -#endif - Curl_connect_ongoing(data->conn)) { - multistate(data, MSTATE_TUNNELING); - break; - } -#endif - rc = CURLM_CALL_MULTI_PERFORM; -#ifndef CURL_DISABLE_PROXY - multistate(data, - data->conn->bits.tunnel_proxy? - MSTATE_TUNNELING : MSTATE_PROTOCONNECT); -#else - multistate(data, MSTATE_PROTOCONNECT); -#endif - } - else if(result) { - /* failure detected */ - Curl_posttransfer(data); - multi_done(data, result, TRUE); - stream_error = TRUE; - break; - } - break; - - case MSTATE_PROTOCONNECT: - result = protocol_connect(data, &protocol_connected); - if(!result && !protocol_connected) - /* switch to waiting state */ - multistate(data, MSTATE_PROTOCONNECTING); - else if(!result) { - /* protocol connect has completed, go WAITDO or DO */ - multistate(data, MSTATE_DO); - rc = CURLM_CALL_MULTI_PERFORM; - } - else { - /* failure detected */ - Curl_posttransfer(data); - multi_done(data, result, TRUE); - stream_error = TRUE; - } - break; - - case MSTATE_PROTOCONNECTING: - /* protocol-specific connect phase */ - result = protocol_connecting(data, &protocol_connected); - if(!result && protocol_connected) { - /* after the connect has completed, go WAITDO or DO */ - multistate(data, MSTATE_DO); - rc = CURLM_CALL_MULTI_PERFORM; - } - else if(result) { - /* failure detected */ - Curl_posttransfer(data); - multi_done(data, result, TRUE); - stream_error = TRUE; - } - break; - - case MSTATE_DO: - if(data->set.fprereq) { - int prereq_rc; - - /* call the prerequest callback function */ - Curl_set_in_callback(data, true); - prereq_rc = data->set.fprereq(data->set.prereq_userp, - data->info.conn_primary_ip, - data->info.conn_local_ip, - data->info.conn_primary_port, - data->info.conn_local_port); - Curl_set_in_callback(data, false); - if(prereq_rc != CURL_PREREQFUNC_OK) { - failf(data, "operation aborted by pre-request callback"); - /* failure in pre-request callback - don't do any other processing */ - result = CURLE_ABORTED_BY_CALLBACK; - Curl_posttransfer(data); - multi_done(data, result, FALSE); - stream_error = TRUE; - break; - } - } - - if(data->set.connect_only == 1) { - /* keep connection open for application to use the socket */ - connkeep(data->conn, "CONNECT_ONLY"); - multistate(data, MSTATE_DONE); - result = CURLE_OK; - rc = CURLM_CALL_MULTI_PERFORM; - } - else { - /* Perform the protocol's DO action */ - result = multi_do(data, &dophase_done); - - /* When multi_do() returns failure, data->conn might be NULL! */ - - if(!result) { - if(!dophase_done) { -#ifndef CURL_DISABLE_FTP - /* some steps needed for wildcard matching */ - if(data->state.wildcardmatch) { - struct WildcardData *wc = &data->wildcard; - if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) { - /* skip some states if it is important */ - multi_done(data, CURLE_OK, FALSE); - - /* if there's no connection left, skip the DONE state */ - multistate(data, data->conn ? - MSTATE_DONE : MSTATE_COMPLETED); - rc = CURLM_CALL_MULTI_PERFORM; - break; - } - } -#endif - /* DO was not completed in one function call, we must continue - DOING... */ - multistate(data, MSTATE_DOING); - rc = CURLM_OK; - } - - /* after DO, go DO_DONE... or DO_MORE */ - else if(data->conn->bits.do_more) { - /* we're supposed to do more, but we need to sit down, relax - and wait a little while first */ - multistate(data, MSTATE_DOING_MORE); - rc = CURLM_OK; - } - else { - /* we're done with the DO, now DID */ - multistate(data, MSTATE_DID); - rc = CURLM_CALL_MULTI_PERFORM; - } - } - else if((CURLE_SEND_ERROR == result) && - data->conn->bits.reuse) { - /* - * In this situation, a connection that we were trying to use - * may have unexpectedly died. If possible, send the connection - * back to the CONNECT phase so we can try again. - */ - char *newurl = NULL; - followtype follow = FOLLOW_NONE; - CURLcode drc; - - drc = Curl_retry_request(data, &newurl); - if(drc) { - /* a failure here pretty much implies an out of memory */ - result = drc; - stream_error = TRUE; - } - - Curl_posttransfer(data); - drc = multi_done(data, result, FALSE); - - /* When set to retry the connection, we must go back to the CONNECT - * state */ - if(newurl) { - if(!drc || (drc == CURLE_SEND_ERROR)) { - follow = FOLLOW_RETRY; - drc = Curl_follow(data, newurl, follow); - if(!drc) { - multistate(data, MSTATE_CONNECT); - rc = CURLM_CALL_MULTI_PERFORM; - result = CURLE_OK; - } - else { - /* Follow failed */ - result = drc; - } - } - else { - /* done didn't return OK or SEND_ERROR */ - result = drc; - } - } - else { - /* Have error handler disconnect conn if we can't retry */ - stream_error = TRUE; - } - free(newurl); - } - else { - /* failure detected */ - Curl_posttransfer(data); - if(data->conn) - multi_done(data, result, FALSE); - stream_error = TRUE; - } - } - break; - - case MSTATE_DOING: - /* we continue DOING until the DO phase is complete */ - DEBUGASSERT(data->conn); - result = protocol_doing(data, &dophase_done); - if(!result) { - if(dophase_done) { - /* after DO, go DO_DONE or DO_MORE */ - multistate(data, data->conn->bits.do_more? - MSTATE_DOING_MORE : MSTATE_DID); - rc = CURLM_CALL_MULTI_PERFORM; - } /* dophase_done */ - } - else { - /* failure detected */ - Curl_posttransfer(data); - multi_done(data, result, FALSE); - stream_error = TRUE; - } - break; - - case MSTATE_DOING_MORE: - /* - * When we are connected, DOING MORE and then go DID - */ - DEBUGASSERT(data->conn); - result = multi_do_more(data, &control); - - if(!result) { - if(control) { - /* if positive, advance to DO_DONE - if negative, go back to DOING */ - multistate(data, control == 1? - MSTATE_DID : MSTATE_DOING); - rc = CURLM_CALL_MULTI_PERFORM; - } - else - /* stay in DO_MORE */ - rc = CURLM_OK; - } - else { - /* failure detected */ - Curl_posttransfer(data); - multi_done(data, result, FALSE); - stream_error = TRUE; - } - break; - - case MSTATE_DID: - DEBUGASSERT(data->conn); - if(data->conn->bits.multiplex) - /* Check if we can move pending requests to send pipe */ - process_pending_handles(multi); /* multiplexed */ - - /* Only perform the transfer if there's a good socket to work with. - Having both BAD is a signal to skip immediately to DONE */ - if((data->conn->sockfd != CURL_SOCKET_BAD) || - (data->conn->writesockfd != CURL_SOCKET_BAD)) - multistate(data, MSTATE_PERFORMING); - else { -#ifndef CURL_DISABLE_FTP - if(data->state.wildcardmatch && - ((data->conn->handler->flags & PROTOPT_WILDCARD) == 0)) { - data->wildcard.state = CURLWC_DONE; - } -#endif - multistate(data, MSTATE_DONE); - } - rc = CURLM_CALL_MULTI_PERFORM; - break; - - case MSTATE_RATELIMITING: /* limit-rate exceeded in either direction */ - DEBUGASSERT(data->conn); - /* if both rates are within spec, resume transfer */ - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - else - result = Curl_speedcheck(data, *nowp); - - if(result) { - if(!(data->conn->handler->flags & PROTOPT_DUAL) && - result != CURLE_HTTP2_STREAM) - streamclose(data->conn, "Transfer returned error"); - - Curl_posttransfer(data); - multi_done(data, result, TRUE); - } - else { - send_timeout_ms = 0; - if(data->set.max_send_speed) - send_timeout_ms = - Curl_pgrsLimitWaitTime(data->progress.uploaded, - data->progress.ul_limit_size, - data->set.max_send_speed, - data->progress.ul_limit_start, - *nowp); - - recv_timeout_ms = 0; - if(data->set.max_recv_speed) - recv_timeout_ms = - Curl_pgrsLimitWaitTime(data->progress.downloaded, - data->progress.dl_limit_size, - data->set.max_recv_speed, - data->progress.dl_limit_start, - *nowp); - - if(!send_timeout_ms && !recv_timeout_ms) { - multistate(data, MSTATE_PERFORMING); - Curl_ratelimit(data, *nowp); - } - else if(send_timeout_ms >= recv_timeout_ms) - Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST); - else - Curl_expire(data, recv_timeout_ms, EXPIRE_TOOFAST); - } - break; - - case MSTATE_PERFORMING: - { - char *newurl = NULL; - bool retry = FALSE; - bool comeback = FALSE; - DEBUGASSERT(data->state.buffer); - /* check if over send speed */ - send_timeout_ms = 0; - if(data->set.max_send_speed) - send_timeout_ms = Curl_pgrsLimitWaitTime(data->progress.uploaded, - data->progress.ul_limit_size, - data->set.max_send_speed, - data->progress.ul_limit_start, - *nowp); - - /* check if over recv speed */ - recv_timeout_ms = 0; - if(data->set.max_recv_speed) - recv_timeout_ms = Curl_pgrsLimitWaitTime(data->progress.downloaded, - data->progress.dl_limit_size, - data->set.max_recv_speed, - data->progress.dl_limit_start, - *nowp); - - if(send_timeout_ms || recv_timeout_ms) { - Curl_ratelimit(data, *nowp); - multistate(data, MSTATE_RATELIMITING); - if(send_timeout_ms >= recv_timeout_ms) - Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST); - else - Curl_expire(data, recv_timeout_ms, EXPIRE_TOOFAST); - break; - } - - /* read/write data if it is ready to do so */ - result = Curl_readwrite(data->conn, data, &done, &comeback); - - if(done || (result == CURLE_RECV_ERROR)) { - /* If CURLE_RECV_ERROR happens early enough, we assume it was a race - * condition and the server closed the re-used connection exactly when - * we wanted to use it, so figure out if that is indeed the case. - */ - CURLcode ret = Curl_retry_request(data, &newurl); - if(!ret) - retry = (newurl)?TRUE:FALSE; - else if(!result) - result = ret; - - if(retry) { - /* if we are to retry, set the result to OK and consider the - request as done */ - result = CURLE_OK; - done = TRUE; - } - } - else if((CURLE_HTTP2_STREAM == result) && - Curl_h2_http_1_1_error(data)) { - CURLcode ret = Curl_retry_request(data, &newurl); - - if(!ret) { - infof(data, "Downgrades to HTTP/1.1"); - streamclose(data->conn, "Disconnect HTTP/2 for HTTP/1"); - data->state.httpwant = CURL_HTTP_VERSION_1_1; - /* clear the error message bit too as we ignore the one we got */ - data->state.errorbuf = FALSE; - if(!newurl) - /* typically for HTTP_1_1_REQUIRED error on first flight */ - newurl = strdup(data->state.url); - /* if we are to retry, set the result to OK and consider the request - as done */ - retry = TRUE; - result = CURLE_OK; - done = TRUE; - } - else - result = ret; - } - - if(result) { - /* - * The transfer phase returned error, we mark the connection to get - * closed to prevent being re-used. This is because we can't possibly - * know if the connection is in a good shape or not now. Unless it is - * a protocol which uses two "channels" like FTP, as then the error - * happened in the data connection. - */ - - if(!(data->conn->handler->flags & PROTOPT_DUAL) && - result != CURLE_HTTP2_STREAM) - streamclose(data->conn, "Transfer returned error"); - - Curl_posttransfer(data); - multi_done(data, result, TRUE); - } - else if(done) { - - /* call this even if the readwrite function returned error */ - Curl_posttransfer(data); - - /* When we follow redirects or is set to retry the connection, we must - to go back to the CONNECT state */ - if(data->req.newurl || retry) { - followtype follow = FOLLOW_NONE; - if(!retry) { - /* if the URL is a follow-location and not just a retried request - then figure out the URL here */ - free(newurl); - newurl = data->req.newurl; - data->req.newurl = NULL; - follow = FOLLOW_REDIR; - } - else - follow = FOLLOW_RETRY; - (void)multi_done(data, CURLE_OK, FALSE); - /* multi_done() might return CURLE_GOT_NOTHING */ - result = Curl_follow(data, newurl, follow); - if(!result) { - multistate(data, MSTATE_CONNECT); - rc = CURLM_CALL_MULTI_PERFORM; - } - free(newurl); - } - else { - /* after the transfer is done, go DONE */ - - /* but first check to see if we got a location info even though we're - not following redirects */ - if(data->req.location) { - free(newurl); - newurl = data->req.location; - data->req.location = NULL; - result = Curl_follow(data, newurl, FOLLOW_FAKE); - free(newurl); - if(result) { - stream_error = TRUE; - result = multi_done(data, result, TRUE); - } - } - - if(!result) { - multistate(data, MSTATE_DONE); - rc = CURLM_CALL_MULTI_PERFORM; - } - } - } - else if(comeback) { - /* This avoids CURLM_CALL_MULTI_PERFORM so that a very fast transfer - won't get stuck on this transfer at the expense of other concurrent - transfers */ - Curl_expire(data, 0, EXPIRE_RUN_NOW); - rc = CURLM_OK; - } - break; - } - - case MSTATE_DONE: - /* this state is highly transient, so run another loop after this */ - rc = CURLM_CALL_MULTI_PERFORM; - - if(data->conn) { - CURLcode res; - - if(data->conn->bits.multiplex) - /* Check if we can move pending requests to connection */ - process_pending_handles(multi); /* multiplexing */ - - /* post-transfer command */ - res = multi_done(data, result, FALSE); - - /* allow a previously set error code take precedence */ - if(!result) - result = res; - } - -#ifndef CURL_DISABLE_FTP - if(data->state.wildcardmatch) { - if(data->wildcard.state != CURLWC_DONE) { - /* if a wildcard is set and we are not ending -> lets start again - with MSTATE_INIT */ - multistate(data, MSTATE_INIT); - break; - } - } -#endif - /* after we have DONE what we're supposed to do, go COMPLETED, and - it doesn't matter what the multi_done() returned! */ - multistate(data, MSTATE_COMPLETED); - break; - - case MSTATE_COMPLETED: - break; - - case MSTATE_MSGSENT: - data->result = result; - return CURLM_OK; /* do nothing */ - - default: - return CURLM_INTERNAL_ERROR; - } - - if(data->conn && - data->mstate >= MSTATE_CONNECT && - data->mstate < MSTATE_DO && - rc != CURLM_CALL_MULTI_PERFORM && - !multi_ischanged(multi, false)) { - /* We now handle stream timeouts if and only if this will be the last - * loop iteration. We only check this on the last iteration to ensure - * that if we know we have additional work to do immediately - * (i.e. CURLM_CALL_MULTI_PERFORM == TRUE) then we should do that before - * declaring the connection timed out as we may almost have a completed - * connection. */ - multi_handle_timeout(data, nowp, &stream_error, &result, TRUE); - } - - statemachine_end: - - if(data->mstate < MSTATE_COMPLETED) { - if(result) { - /* - * If an error was returned, and we aren't in completed state now, - * then we go to completed and consider this transfer aborted. - */ - - /* NOTE: no attempt to disconnect connections must be made - in the case blocks above - cleanup happens only here */ - - /* Check if we can move pending requests to send pipe */ - process_pending_handles(multi); /* connection */ - - if(data->conn) { - if(stream_error) { - /* Don't attempt to send data over a connection that timed out */ - bool dead_connection = result == CURLE_OPERATION_TIMEDOUT; - struct connectdata *conn = data->conn; - - /* This is where we make sure that the conn pointer is reset. - We don't have to do this in every case block above where a - failure is detected */ - Curl_detach_connection(data); - - /* remove connection from cache */ - Curl_conncache_remove_conn(data, conn, TRUE); - - /* disconnect properly */ - Curl_disconnect(data, conn, dead_connection); - } - } - else if(data->mstate == MSTATE_CONNECT) { - /* Curl_connect() failed */ - (void)Curl_posttransfer(data); - } - - multistate(data, MSTATE_COMPLETED); - rc = CURLM_CALL_MULTI_PERFORM; - } - /* if there's still a connection to use, call the progress function */ - else if(data->conn && Curl_pgrsUpdate(data)) { - /* aborted due to progress callback return code must close the - connection */ - result = CURLE_ABORTED_BY_CALLBACK; - streamclose(data->conn, "Aborted by callback"); - - /* if not yet in DONE state, go there, otherwise COMPLETED */ - multistate(data, (data->mstate < MSTATE_DONE)? - MSTATE_DONE: MSTATE_COMPLETED); - rc = CURLM_CALL_MULTI_PERFORM; - } - } - - if(MSTATE_COMPLETED == data->mstate) { - if(data->set.fmultidone) { - /* signal via callback instead */ - data->set.fmultidone(data, result); - } - else { - /* now fill in the Curl_message with this info */ - msg = &data->msg; - - msg->extmsg.msg = CURLMSG_DONE; - msg->extmsg.easy_handle = data; - msg->extmsg.data.result = result; - - rc = multi_addmsg(multi, msg); - DEBUGASSERT(!data->conn); - } - multistate(data, MSTATE_MSGSENT); - } - } while((rc == CURLM_CALL_MULTI_PERFORM) || multi_ischanged(multi, FALSE)); - - data->result = result; - return rc; -} - - -CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles) -{ - struct Curl_easy *data; - CURLMcode returncode = CURLM_OK; - struct Curl_tree *t; - struct curltime now = Curl_now(); - - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - - data = multi->easyp; - while(data) { - CURLMcode result; - SIGPIPE_VARIABLE(pipe_st); - - sigpipe_ignore(data, &pipe_st); - result = multi_runsingle(multi, &now, data); - sigpipe_restore(&pipe_st); - - if(result) - returncode = result; - - data = data->next; /* operate on next handle */ - } - - /* - * Simply remove all expired timers from the splay since handles are dealt - * with unconditionally by this function and curl_multi_timeout() requires - * that already passed/handled expire times are removed from the splay. - * - * It is important that the 'now' value is set at the entry of this function - * and not for the current time as it may have ticked a little while since - * then and then we risk this loop to remove timers that actually have not - * been handled! - */ - do { - multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); - if(t) - /* the removed may have another timeout in queue */ - (void)add_next_timeout(now, multi, t->payload); - - } while(t); - - *running_handles = multi->num_alive; - - if(CURLM_OK >= returncode) - returncode = Curl_update_timer(multi); - - return returncode; -} - -CURLMcode curl_multi_cleanup(struct Curl_multi *multi) -{ - struct Curl_easy *data; - struct Curl_easy *nextdata; - - if(GOOD_MULTI_HANDLE(multi)) { - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - - multi->magic = 0; /* not good anymore */ - - /* First remove all remaining easy handles */ - data = multi->easyp; - while(data) { - nextdata = data->next; - if(!data->state.done && data->conn) - /* if DONE was never called for this handle */ - (void)multi_done(data, CURLE_OK, TRUE); - if(data->dns.hostcachetype == HCACHE_MULTI) { - /* clear out the usage of the shared DNS cache */ - Curl_hostcache_clean(data, data->dns.hostcache); - data->dns.hostcache = NULL; - data->dns.hostcachetype = HCACHE_NONE; - } - - /* Clear the pointer to the connection cache */ - data->state.conn_cache = NULL; - data->multi = NULL; /* clear the association */ - -#ifdef USE_LIBPSL - if(data->psl == &multi->psl) - data->psl = NULL; -#endif - - data = nextdata; - } - - /* Close all the connections in the connection cache */ - Curl_conncache_close_all_connections(&multi->conn_cache); - - sockhash_destroy(&multi->sockhash); - Curl_conncache_destroy(&multi->conn_cache); - Curl_llist_destroy(&multi->msglist, NULL); - Curl_llist_destroy(&multi->pending, NULL); - - Curl_hash_destroy(&multi->hostcache); - Curl_psl_destroy(&multi->psl); - -#ifdef USE_WINSOCK - WSACloseEvent(multi->wsa_event); -#else -#ifdef ENABLE_WAKEUP - wakeup_close(multi->wakeup_pair[0]); - wakeup_close(multi->wakeup_pair[1]); -#endif -#endif - free(multi); - - return CURLM_OK; - } - return CURLM_BAD_HANDLE; -} - -/* - * curl_multi_info_read() - * - * This function is the primary way for a multi/multi_socket application to - * figure out if a transfer has ended. We MUST make this function as fast as - * possible as it will be polled frequently and we MUST NOT scan any lists in - * here to figure out things. We must scale fine to thousands of handles and - * beyond. The current design is fully O(1). - */ - -CURLMsg *curl_multi_info_read(struct Curl_multi *multi, int *msgs_in_queue) -{ - struct Curl_message *msg; - - *msgs_in_queue = 0; /* default to none */ - - if(GOOD_MULTI_HANDLE(multi) && - !multi->in_callback && - Curl_llist_count(&multi->msglist)) { - /* there is one or more messages in the list */ - struct Curl_llist_element *e; - - /* extract the head of the list to return */ - e = multi->msglist.head; - - msg = e->ptr; - - /* remove the extracted entry */ - Curl_llist_remove(&multi->msglist, e, NULL); - - *msgs_in_queue = curlx_uztosi(Curl_llist_count(&multi->msglist)); - - return &msg->extmsg; - } - return NULL; -} - -/* - * singlesocket() checks what sockets we deal with and their "action state" - * and if we have a different state in any of those sockets from last time we - * call the callback accordingly. - */ -static CURLMcode singlesocket(struct Curl_multi *multi, - struct Curl_easy *data) -{ - curl_socket_t socks[MAX_SOCKSPEREASYHANDLE]; - int i; - struct Curl_sh_entry *entry; - curl_socket_t s; - int num; - unsigned int curraction; - unsigned char actions[MAX_SOCKSPEREASYHANDLE]; - int rc; - - for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) - socks[i] = CURL_SOCKET_BAD; - - /* Fill in the 'current' struct with the state as it is now: what sockets to - supervise and for what actions */ - curraction = multi_getsock(data, socks); - - /* We have 0 .. N sockets already and we get to know about the 0 .. M - sockets we should have from now on. Detect the differences, remove no - longer supervised ones and add new ones */ - - /* walk over the sockets we got right now */ - for(i = 0; (i< MAX_SOCKSPEREASYHANDLE) && - (curraction & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))); - i++) { - unsigned char action = CURL_POLL_NONE; - unsigned char prevaction = 0; - int comboaction; - bool sincebefore = FALSE; - - s = socks[i]; - - /* get it from the hash */ - entry = sh_getentry(&multi->sockhash, s); - - if(curraction & GETSOCK_READSOCK(i)) - action |= CURL_POLL_IN; - if(curraction & GETSOCK_WRITESOCK(i)) - action |= CURL_POLL_OUT; - - actions[i] = action; - if(entry) { - /* check if new for this transfer */ - int j; - for(j = 0; j< data->numsocks; j++) { - if(s == data->sockets[j]) { - prevaction = data->actions[j]; - sincebefore = TRUE; - break; - } - } - } - else { - /* this is a socket we didn't have before, add it to the hash! */ - entry = sh_addentry(&multi->sockhash, s); - if(!entry) - /* fatal */ - return CURLM_OUT_OF_MEMORY; - } - if(sincebefore && (prevaction != action)) { - /* Socket was used already, but different action now */ - if(prevaction & CURL_POLL_IN) - entry->readers--; - if(prevaction & CURL_POLL_OUT) - entry->writers--; - if(action & CURL_POLL_IN) - entry->readers++; - if(action & CURL_POLL_OUT) - entry->writers++; - } - else if(!sincebefore) { - /* a new user */ - entry->users++; - if(action & CURL_POLL_IN) - entry->readers++; - if(action & CURL_POLL_OUT) - entry->writers++; - - /* add 'data' to the transfer hash on this socket! */ - if(!Curl_hash_add(&entry->transfers, (char *)&data, /* hash key */ - sizeof(struct Curl_easy *), data)) { - Curl_hash_destroy(&entry->transfers); - return CURLM_OUT_OF_MEMORY; - } - } - - comboaction = (entry->writers? CURL_POLL_OUT : 0) | - (entry->readers ? CURL_POLL_IN : 0); - - /* socket existed before and has the same action set as before */ - if(sincebefore && ((int)entry->action == comboaction)) - /* same, continue */ - continue; - - if(multi->socket_cb) { - set_in_callback(multi, TRUE); - rc = multi->socket_cb(data, s, comboaction, multi->socket_userp, - entry->socketp); - set_in_callback(multi, FALSE); - if(rc == -1) { - multi->dead = TRUE; - return CURLM_ABORTED_BY_CALLBACK; - } - } - - entry->action = comboaction; /* store the current action state */ - } - - num = i; /* number of sockets */ - - /* when we've walked over all the sockets we should have right now, we must - make sure to detect sockets that are removed */ - for(i = 0; i< data->numsocks; i++) { - int j; - bool stillused = FALSE; - s = data->sockets[i]; - for(j = 0; j < num; j++) { - if(s == socks[j]) { - /* this is still supervised */ - stillused = TRUE; - break; - } - } - if(stillused) - continue; - - entry = sh_getentry(&multi->sockhash, s); - /* if this is NULL here, the socket has been closed and notified so - already by Curl_multi_closed() */ - if(entry) { - unsigned char oldactions = data->actions[i]; - /* this socket has been removed. Decrease user count */ - entry->users--; - if(oldactions & CURL_POLL_OUT) - entry->writers--; - if(oldactions & CURL_POLL_IN) - entry->readers--; - if(!entry->users) { - if(multi->socket_cb) { - set_in_callback(multi, TRUE); - rc = multi->socket_cb(data, s, CURL_POLL_REMOVE, - multi->socket_userp, entry->socketp); - set_in_callback(multi, FALSE); - if(rc == -1) { - multi->dead = TRUE; - return CURLM_ABORTED_BY_CALLBACK; - } - } - sh_delentry(entry, &multi->sockhash, s); - } - else { - /* still users, but remove this handle as a user of this socket */ - if(Curl_hash_delete(&entry->transfers, (char *)&data, - sizeof(struct Curl_easy *))) { - DEBUGASSERT(NULL); - } - } - } - } /* for loop over numsocks */ - - memcpy(data->sockets, socks, num*sizeof(curl_socket_t)); - memcpy(data->actions, actions, num*sizeof(char)); - data->numsocks = num; - return CURLM_OK; -} - -CURLcode Curl_updatesocket(struct Curl_easy *data) -{ - if(singlesocket(data->multi, data)) - return CURLE_ABORTED_BY_CALLBACK; - return CURLE_OK; -} - - -/* - * Curl_multi_closed() - * - * Used by the connect code to tell the multi_socket code that one of the - * sockets we were using is about to be closed. This function will then - * remove it from the sockethash for this handle to make the multi_socket API - * behave properly, especially for the case when libcurl will create another - * socket again and it gets the same file descriptor number. - */ - -void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s) -{ - if(data) { - /* if there's still an easy handle associated with this connection */ - struct Curl_multi *multi = data->multi; - if(multi) { - /* this is set if this connection is part of a handle that is added to - a multi handle, and only then this is necessary */ - struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s); - - if(entry) { - int rc = 0; - if(multi->socket_cb) { - set_in_callback(multi, TRUE); - rc = multi->socket_cb(data, s, CURL_POLL_REMOVE, - multi->socket_userp, entry->socketp); - set_in_callback(multi, FALSE); - } - - /* now remove it from the socket hash */ - sh_delentry(entry, &multi->sockhash, s); - if(rc == -1) - /* This just marks the multi handle as "dead" without returning an - error code primarily because this function is used from many - places where propagating an error back is tricky. */ - multi->dead = TRUE; - } - } - } -} - -/* - * add_next_timeout() - * - * Each Curl_easy has a list of timeouts. The add_next_timeout() is called - * when it has just been removed from the splay tree because the timeout has - * expired. This function is then to advance in the list to pick the next - * timeout to use (skip the already expired ones) and add this node back to - * the splay tree again. - * - * The splay tree only has each sessionhandle as a single node and the nearest - * timeout is used to sort it on. - */ -static CURLMcode add_next_timeout(struct curltime now, - struct Curl_multi *multi, - struct Curl_easy *d) -{ - struct curltime *tv = &d->state.expiretime; - struct Curl_llist *list = &d->state.timeoutlist; - struct Curl_llist_element *e; - struct time_node *node = NULL; - - /* move over the timeout list for this specific handle and remove all - timeouts that are now passed tense and store the next pending - timeout in *tv */ - for(e = list->head; e;) { - struct Curl_llist_element *n = e->next; - timediff_t diff; - node = (struct time_node *)e->ptr; - diff = Curl_timediff(node->time, now); - if(diff <= 0) - /* remove outdated entry */ - Curl_llist_remove(list, e, NULL); - else - /* the list is sorted so get out on the first mismatch */ - break; - e = n; - } - e = list->head; - if(!e) { - /* clear the expire times within the handles that we remove from the - splay tree */ - tv->tv_sec = 0; - tv->tv_usec = 0; - } - else { - /* copy the first entry to 'tv' */ - memcpy(tv, &node->time, sizeof(*tv)); - - /* Insert this node again into the splay. Keep the timer in the list in - case we need to recompute future timers. */ - multi->timetree = Curl_splayinsert(*tv, multi->timetree, - &d->state.timenode); - } - return CURLM_OK; -} - -static CURLMcode multi_socket(struct Curl_multi *multi, - bool checkall, - curl_socket_t s, - int ev_bitmask, - int *running_handles) -{ - CURLMcode result = CURLM_OK; - struct Curl_easy *data = NULL; - struct Curl_tree *t; - struct curltime now = Curl_now(); - - if(checkall) { - /* *perform() deals with running_handles on its own */ - result = curl_multi_perform(multi, running_handles); - - /* walk through each easy handle and do the socket state change magic - and callbacks */ - if(result != CURLM_BAD_HANDLE) { - data = multi->easyp; - while(data && !result) { - result = singlesocket(multi, data); - data = data->next; - } - } - - /* or should we fall-through and do the timer-based stuff? */ - return result; - } - if(s != CURL_SOCKET_TIMEOUT) { - struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s); - - if(!entry) - /* Unmatched socket, we can't act on it but we ignore this fact. In - real-world tests it has been proved that libevent can in fact give - the application actions even though the socket was just previously - asked to get removed, so thus we better survive stray socket actions - and just move on. */ - ; - else { - struct Curl_hash_iterator iter; - struct Curl_hash_element *he; - - /* the socket can be shared by many transfers, iterate */ - Curl_hash_start_iterate(&entry->transfers, &iter); - for(he = Curl_hash_next_element(&iter); he; - he = Curl_hash_next_element(&iter)) { - data = (struct Curl_easy *)he->ptr; - DEBUGASSERT(data); - DEBUGASSERT(data->magic == CURLEASY_MAGIC_NUMBER); - - if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK)) - /* set socket event bitmask if they're not locked */ - data->conn->cselect_bits = ev_bitmask; - - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } - - /* Now we fall-through and do the timer-based stuff, since we don't want - to force the user to have to deal with timeouts as long as at least - one connection in fact has traffic. */ - - data = NULL; /* set data to NULL again to avoid calling - multi_runsingle() in case there's no need to */ - now = Curl_now(); /* get a newer time since the multi_runsingle() loop - may have taken some time */ - } - } - else { - /* Asked to run due to time-out. Clear the 'lastcall' variable to force - Curl_update_timer() to trigger a callback to the app again even if the - same timeout is still the one to run after this call. That handles the - case when the application asks libcurl to run the timeout - prematurely. */ - memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall)); - } - - /* - * The loop following here will go on as long as there are expire-times left - * to process in the splay and 'data' will be re-assigned for every expired - * handle we deal with. - */ - do { - /* the first loop lap 'data' can be NULL */ - if(data) { - SIGPIPE_VARIABLE(pipe_st); - - sigpipe_ignore(data, &pipe_st); - result = multi_runsingle(multi, &now, data); - sigpipe_restore(&pipe_st); - - if(CURLM_OK >= result) { - /* get the socket(s) and check if the state has been changed since - last */ - result = singlesocket(multi, data); - if(result) - return result; - } - } - - /* Check if there's one (more) expired timer to deal with! This function - extracts a matching node if there is one */ - - multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); - if(t) { - data = t->payload; /* assign this for next loop */ - (void)add_next_timeout(now, multi, t->payload); - } - - } while(t); - - *running_handles = multi->num_alive; - return result; -} - -#undef curl_multi_setopt -CURLMcode curl_multi_setopt(struct Curl_multi *multi, - CURLMoption option, ...) -{ - CURLMcode res = CURLM_OK; - va_list param; - - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - - va_start(param, option); - - switch(option) { - case CURLMOPT_SOCKETFUNCTION: - multi->socket_cb = va_arg(param, curl_socket_callback); - break; - case CURLMOPT_SOCKETDATA: - multi->socket_userp = va_arg(param, void *); - break; - case CURLMOPT_PUSHFUNCTION: - multi->push_cb = va_arg(param, curl_push_callback); - break; - case CURLMOPT_PUSHDATA: - multi->push_userp = va_arg(param, void *); - break; - case CURLMOPT_PIPELINING: - multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX; - break; - case CURLMOPT_TIMERFUNCTION: - multi->timer_cb = va_arg(param, curl_multi_timer_callback); - break; - case CURLMOPT_TIMERDATA: - multi->timer_userp = va_arg(param, void *); - break; - case CURLMOPT_MAXCONNECTS: - multi->maxconnects = va_arg(param, long); - break; - case CURLMOPT_MAX_HOST_CONNECTIONS: - multi->max_host_connections = va_arg(param, long); - break; - case CURLMOPT_MAX_TOTAL_CONNECTIONS: - multi->max_total_connections = va_arg(param, long); - break; - /* options formerly used for pipelining */ - case CURLMOPT_MAX_PIPELINE_LENGTH: - break; - case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: - break; - case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: - break; - case CURLMOPT_PIPELINING_SITE_BL: - break; - case CURLMOPT_PIPELINING_SERVER_BL: - break; - case CURLMOPT_MAX_CONCURRENT_STREAMS: - { - long streams = va_arg(param, long); - if(streams < 1) - streams = 100; - multi->max_concurrent_streams = curlx_sltoui(streams); - } - break; - default: - res = CURLM_UNKNOWN_OPTION; - break; - } - va_end(param); - return res; -} - -/* we define curl_multi_socket() in the public multi.h header */ -#undef curl_multi_socket - -CURLMcode curl_multi_socket(struct Curl_multi *multi, curl_socket_t s, - int *running_handles) -{ - CURLMcode result; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - result = multi_socket(multi, FALSE, s, 0, running_handles); - if(CURLM_OK >= result) - result = Curl_update_timer(multi); - return result; -} - -CURLMcode curl_multi_socket_action(struct Curl_multi *multi, curl_socket_t s, - int ev_bitmask, int *running_handles) -{ - CURLMcode result; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - result = multi_socket(multi, FALSE, s, ev_bitmask, running_handles); - if(CURLM_OK >= result) - result = Curl_update_timer(multi); - return result; -} - -CURLMcode curl_multi_socket_all(struct Curl_multi *multi, int *running_handles) -{ - CURLMcode result; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - result = multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles); - if(CURLM_OK >= result) - result = Curl_update_timer(multi); - return result; -} - -static CURLMcode multi_timeout(struct Curl_multi *multi, - long *timeout_ms) -{ - static const struct curltime tv_zero = {0, 0}; - - if(multi->dead) { - *timeout_ms = 0; - return CURLM_OK; - } - - if(multi->timetree) { - /* we have a tree of expire times */ - struct curltime now = Curl_now(); - - /* splay the lowest to the bottom */ - multi->timetree = Curl_splay(tv_zero, multi->timetree); - - if(Curl_splaycomparekeys(multi->timetree->key, now) > 0) { - /* some time left before expiration */ - timediff_t diff = Curl_timediff(multi->timetree->key, now); - if(diff <= 0) - /* - * Since we only provide millisecond resolution on the returned value - * and the diff might be less than one millisecond here, we don't - * return zero as that may cause short bursts of busyloops on fast - * processors while the diff is still present but less than one - * millisecond! instead we return 1 until the time is ripe. - */ - *timeout_ms = 1; - else - /* this should be safe even on 64 bit archs, as we don't use that - overly long timeouts */ - *timeout_ms = (long)diff; - } - else - /* 0 means immediately */ - *timeout_ms = 0; - } - else - *timeout_ms = -1; - - return CURLM_OK; -} - -CURLMcode curl_multi_timeout(struct Curl_multi *multi, - long *timeout_ms) -{ - /* First, make some basic checks that the CURLM handle is a good handle */ - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - - return multi_timeout(multi, timeout_ms); -} - -/* - * Tell the application it should update its timers, if it subscribes to the - * update timer callback. - */ -CURLMcode Curl_update_timer(struct Curl_multi *multi) -{ - long timeout_ms; - int rc; - - if(!multi->timer_cb || multi->dead) - return CURLM_OK; - if(multi_timeout(multi, &timeout_ms)) { - return CURLM_OK; - } - if(timeout_ms < 0) { - static const struct curltime none = {0, 0}; - if(Curl_splaycomparekeys(none, multi->timer_lastcall)) { - multi->timer_lastcall = none; - /* there's no timeout now but there was one previously, tell the app to - disable it */ - set_in_callback(multi, TRUE); - rc = multi->timer_cb(multi, -1, multi->timer_userp); - set_in_callback(multi, FALSE); - if(rc == -1) { - multi->dead = TRUE; - return CURLM_ABORTED_BY_CALLBACK; - } - return CURLM_OK; - } - return CURLM_OK; - } - - /* When multi_timeout() is done, multi->timetree points to the node with the - * timeout we got the (relative) time-out time for. We can thus easily check - * if this is the same (fixed) time as we got in a previous call and then - * avoid calling the callback again. */ - if(Curl_splaycomparekeys(multi->timetree->key, multi->timer_lastcall) == 0) - return CURLM_OK; - - multi->timer_lastcall = multi->timetree->key; - - set_in_callback(multi, TRUE); - rc = multi->timer_cb(multi, timeout_ms, multi->timer_userp); - set_in_callback(multi, FALSE); - if(rc == -1) { - multi->dead = TRUE; - return CURLM_ABORTED_BY_CALLBACK; - } - return CURLM_OK; -} - -/* - * multi_deltimeout() - * - * Remove a given timestamp from the list of timeouts. - */ -static void -multi_deltimeout(struct Curl_easy *data, expire_id eid) -{ - struct Curl_llist_element *e; - struct Curl_llist *timeoutlist = &data->state.timeoutlist; - /* find and remove the specific node from the list */ - for(e = timeoutlist->head; e; e = e->next) { - struct time_node *n = (struct time_node *)e->ptr; - if(n->eid == eid) { - Curl_llist_remove(timeoutlist, e, NULL); - return; - } - } -} - -/* - * multi_addtimeout() - * - * Add a timestamp to the list of timeouts. Keep the list sorted so that head - * of list is always the timeout nearest in time. - * - */ -static CURLMcode -multi_addtimeout(struct Curl_easy *data, - struct curltime *stamp, - expire_id eid) -{ - struct Curl_llist_element *e; - struct time_node *node; - struct Curl_llist_element *prev = NULL; - size_t n; - struct Curl_llist *timeoutlist = &data->state.timeoutlist; - - node = &data->state.expires[eid]; - - /* copy the timestamp and id */ - memcpy(&node->time, stamp, sizeof(*stamp)); - node->eid = eid; /* also marks it as in use */ - - n = Curl_llist_count(timeoutlist); - if(n) { - /* find the correct spot in the list */ - for(e = timeoutlist->head; e; e = e->next) { - struct time_node *check = (struct time_node *)e->ptr; - timediff_t diff = Curl_timediff(check->time, node->time); - if(diff > 0) - break; - prev = e; - } - - } - /* else - this is the first timeout on the list */ - - Curl_llist_insert_next(timeoutlist, prev, node, &node->list); - return CURLM_OK; -} - -/* - * Curl_expire() - * - * given a number of milliseconds from now to use to set the 'act before - * this'-time for the transfer, to be extracted by curl_multi_timeout() - * - * The timeout will be added to a queue of timeouts if it defines a moment in - * time that is later than the current head of queue. - * - * Expire replaces a former timeout using the same id if already set. - */ -void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id) -{ - struct Curl_multi *multi = data->multi; - struct curltime *nowp = &data->state.expiretime; - struct curltime set; - - /* this is only interesting while there is still an associated multi struct - remaining! */ - if(!multi) - return; - - DEBUGASSERT(id < EXPIRE_LAST); - - set = Curl_now(); - set.tv_sec += (time_t)(milli/1000); /* might be a 64 to 32 bit conversion */ - set.tv_usec += (unsigned int)(milli%1000)*1000; - - if(set.tv_usec >= 1000000) { - set.tv_sec++; - set.tv_usec -= 1000000; - } - - /* Remove any timer with the same id just in case. */ - multi_deltimeout(data, id); - - /* Add it to the timer list. It must stay in the list until it has expired - in case we need to recompute the minimum timer later. */ - multi_addtimeout(data, &set, id); - - if(nowp->tv_sec || nowp->tv_usec) { - /* This means that the struct is added as a node in the splay tree. - Compare if the new time is earlier, and only remove-old/add-new if it - is. */ - timediff_t diff = Curl_timediff(set, *nowp); - int rc; - - if(diff > 0) { - /* The current splay tree entry is sooner than this new expiry time. - We don't need to update our splay tree entry. */ - return; - } - - /* Since this is an updated time, we must remove the previous entry from - the splay tree first and then re-add the new value */ - rc = Curl_splayremove(multi->timetree, &data->state.timenode, - &multi->timetree); - if(rc) - infof(data, "Internal error removing splay node = %d", rc); - } - - /* Indicate that we are in the splay tree and insert the new timer expiry - value since it is our local minimum. */ - *nowp = set; - data->state.timenode.payload = data; - multi->timetree = Curl_splayinsert(*nowp, multi->timetree, - &data->state.timenode); -} - -/* - * Curl_expire_done() - * - * Removes the expire timer. Marks it as done. - * - */ -void Curl_expire_done(struct Curl_easy *data, expire_id id) -{ - /* remove the timer, if there */ - multi_deltimeout(data, id); -} - -/* - * Curl_expire_clear() - * - * Clear ALL timeout values for this handle. - */ -void Curl_expire_clear(struct Curl_easy *data) -{ - struct Curl_multi *multi = data->multi; - struct curltime *nowp = &data->state.expiretime; - - /* this is only interesting while there is still an associated multi struct - remaining! */ - if(!multi) - return; - - if(nowp->tv_sec || nowp->tv_usec) { - /* Since this is an cleared time, we must remove the previous entry from - the splay tree */ - struct Curl_llist *list = &data->state.timeoutlist; - int rc; - - rc = Curl_splayremove(multi->timetree, &data->state.timenode, - &multi->timetree); - if(rc) - infof(data, "Internal error clearing splay node = %d", rc); - - /* flush the timeout list too */ - while(list->size > 0) { - Curl_llist_remove(list, list->tail, NULL); - } - -#ifdef DEBUGBUILD - infof(data, "Expire cleared (transfer %p)", data); -#endif - nowp->tv_sec = 0; - nowp->tv_usec = 0; - } -} - - - - -CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s, - void *hashp) -{ - struct Curl_sh_entry *there = NULL; - - there = sh_getentry(&multi->sockhash, s); - - if(!there) - return CURLM_BAD_SOCKET; - - there->socketp = hashp; - - return CURLM_OK; -} - -size_t Curl_multi_max_host_connections(struct Curl_multi *multi) -{ - return multi ? multi->max_host_connections : 0; -} - -size_t Curl_multi_max_total_connections(struct Curl_multi *multi) -{ - return multi ? multi->max_total_connections : 0; -} - -/* - * When information about a connection has appeared, call this! - */ - -void Curl_multiuse_state(struct Curl_easy *data, - int bundlestate) /* use BUNDLE_* defines */ -{ - struct connectdata *conn; - DEBUGASSERT(data); - DEBUGASSERT(data->multi); - conn = data->conn; - DEBUGASSERT(conn); - DEBUGASSERT(conn->bundle); - - conn->bundle->multiuse = bundlestate; - process_pending_handles(data->multi); -} - -static void process_pending_handles(struct Curl_multi *multi) -{ - struct Curl_llist_element *e = multi->pending.head; - if(e) { - struct Curl_easy *data = e->ptr; - - DEBUGASSERT(data->mstate == MSTATE_PENDING); - - multistate(data, MSTATE_CONNECT); - - /* Remove this node from the list */ - Curl_llist_remove(&multi->pending, e, NULL); - - /* Make sure that the handle will be processed soonish. */ - Curl_expire(data, 0, EXPIRE_RUN_NOW); - - /* mark this as having been in the pending queue */ - data->state.previouslypending = TRUE; - } -} - -void Curl_set_in_callback(struct Curl_easy *data, bool value) -{ - /* might get called when there is no data pointer! */ - if(data) { - if(data->multi_easy) - data->multi_easy->in_callback = value; - else if(data->multi) - data->multi->in_callback = value; - } -} - -bool Curl_is_in_callback(struct Curl_easy *easy) -{ - return ((easy->multi && easy->multi->in_callback) || - (easy->multi_easy && easy->multi_easy->in_callback)); -} - -#ifdef DEBUGBUILD -void Curl_multi_dump(struct Curl_multi *multi) -{ - struct Curl_easy *data; - int i; - fprintf(stderr, "* Multi status: %d handles, %d alive\n", - multi->num_easy, multi->num_alive); - for(data = multi->easyp; data; data = data->next) { - if(data->mstate < MSTATE_COMPLETED) { - /* only display handles that are not completed */ - fprintf(stderr, "handle %p, state %s, %d sockets\n", - (void *)data, - statename[data->mstate], data->numsocks); - for(i = 0; i < data->numsocks; i++) { - curl_socket_t s = data->sockets[i]; - struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s); - - fprintf(stderr, "%d ", (int)s); - if(!entry) { - fprintf(stderr, "INTERNAL CONFUSION\n"); - continue; - } - fprintf(stderr, "[%s %s] ", - (entry->action&CURL_POLL_IN)?"RECVING":"", - (entry->action&CURL_POLL_OUT)?"SENDING":""); - } - if(data->numsocks) - fprintf(stderr, "\n"); - } - } -} -#endif - -unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi) -{ - DEBUGASSERT(multi); - return multi->max_concurrent_streams; -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "transfer.h"
+#include "url.h"
+#include "cfilters.h"
+#include "connect.h"
+#include "progress.h"
+#include "easyif.h"
+#include "share.h"
+#include "psl.h"
+#include "multiif.h"
+#include "sendf.h"
+#include "timeval.h"
+#include "http.h"
+#include "select.h"
+#include "warnless.h"
+#include "speedcheck.h"
+#include "conncache.h"
+#include "multihandle.h"
+#include "sigpipe.h"
+#include "vtls/vtls.h"
+#include "http_proxy.h"
+#include "http2.h"
+#include "socketpair.h"
+#include "socks.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#ifdef __APPLE__
+
+#define wakeup_write write
+#define wakeup_read read
+#define wakeup_close close
+#define wakeup_create pipe
+
+#else /* __APPLE__ */
+
+#define wakeup_write swrite
+#define wakeup_read sread
+#define wakeup_close sclose
+#define wakeup_create(p) Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, p)
+
+#endif /* __APPLE__ */
+
+/*
+ CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97
+ to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes. Still, every
+ CURL handle takes 45-50 K memory, therefore this 3K are not significant.
+*/
+#ifndef CURL_SOCKET_HASH_TABLE_SIZE
+#define CURL_SOCKET_HASH_TABLE_SIZE 911
+#endif
+
+#ifndef CURL_CONNECTION_HASH_SIZE
+#define CURL_CONNECTION_HASH_SIZE 97
+#endif
+
+#ifndef CURL_DNS_HASH_SIZE
+#define CURL_DNS_HASH_SIZE 71
+#endif
+
+#define CURL_MULTI_HANDLE 0x000bab1e
+
+#define GOOD_MULTI_HANDLE(x) \
+ ((x) && (x)->magic == CURL_MULTI_HANDLE)
+
+static CURLMcode singlesocket(struct Curl_multi *multi,
+ struct Curl_easy *data);
+static CURLMcode add_next_timeout(struct curltime now,
+ struct Curl_multi *multi,
+ struct Curl_easy *d);
+static CURLMcode multi_timeout(struct Curl_multi *multi,
+ long *timeout_ms);
+static void process_pending_handles(struct Curl_multi *multi);
+
+#ifdef DEBUGBUILD
+static const char * const statename[]={
+ "INIT",
+ "PENDING",
+ "CONNECT",
+ "RESOLVING",
+ "CONNECTING",
+ "TUNNELING",
+ "PROTOCONNECT",
+ "PROTOCONNECTING",
+ "DO",
+ "DOING",
+ "DOING_MORE",
+ "DID",
+ "PERFORMING",
+ "RATELIMITING",
+ "DONE",
+ "COMPLETED",
+ "MSGSENT",
+};
+#endif
+
+/* function pointer called once when switching TO a state */
+typedef void (*init_multistate_func)(struct Curl_easy *data);
+
+/* called in DID state, before PERFORMING state */
+static void before_perform(struct Curl_easy *data)
+{
+ data->req.chunk = FALSE;
+ Curl_pgrsTime(data, TIMER_PRETRANSFER);
+}
+
+static void init_completed(struct Curl_easy *data)
+{
+ /* this is a completed transfer */
+
+ /* Important: reset the conn pointer so that we don't point to memory
+ that could be freed anytime */
+ Curl_detach_connection(data);
+ Curl_expire_clear(data); /* stop all timers */
+}
+
+/* always use this function to change state, to make debugging easier */
+static void mstate(struct Curl_easy *data, CURLMstate state
+#ifdef DEBUGBUILD
+ , int lineno
+#endif
+)
+{
+ CURLMstate oldstate = data->mstate;
+ static const init_multistate_func finit[MSTATE_LAST] = {
+ NULL, /* INIT */
+ NULL, /* PENDING */
+ Curl_init_CONNECT, /* CONNECT */
+ NULL, /* RESOLVING */
+ NULL, /* CONNECTING */
+ NULL, /* TUNNELING */
+ NULL, /* PROTOCONNECT */
+ NULL, /* PROTOCONNECTING */
+ NULL, /* DO */
+ NULL, /* DOING */
+ NULL, /* DOING_MORE */
+ before_perform, /* DID */
+ NULL, /* PERFORMING */
+ NULL, /* RATELIMITING */
+ NULL, /* DONE */
+ init_completed, /* COMPLETED */
+ NULL /* MSGSENT */
+ };
+
+#if defined(DEBUGBUILD) && defined(CURL_DISABLE_VERBOSE_STRINGS)
+ (void) lineno;
+#endif
+
+ if(oldstate == state)
+ /* don't bother when the new state is the same as the old state */
+ return;
+
+ data->mstate = state;
+
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ if(data->mstate >= MSTATE_PENDING &&
+ data->mstate < MSTATE_COMPLETED) {
+ long connection_id = -5000;
+
+ if(data->conn)
+ connection_id = data->conn->connection_id;
+
+ infof(data,
+ "STATE: %s => %s handle %p; line %d (connection #%ld)",
+ statename[oldstate], statename[data->mstate],
+ (void *)data, lineno, connection_id);
+ }
+#endif
+
+ if(state == MSTATE_COMPLETED) {
+ /* changing to COMPLETED means there's one less easy handle 'alive' */
+ DEBUGASSERT(data->multi->num_alive > 0);
+ data->multi->num_alive--;
+ }
+
+ /* if this state has an init-function, run it */
+ if(finit[state])
+ finit[state](data);
+}
+
+#ifndef DEBUGBUILD
+#define multistate(x,y) mstate(x,y)
+#else
+#define multistate(x,y) mstate(x,y, __LINE__)
+#endif
+
+/*
+ * We add one of these structs to the sockhash for each socket
+ */
+
+struct Curl_sh_entry {
+ struct Curl_hash transfers; /* hash of transfers using this socket */
+ unsigned int action; /* what combined action READ/WRITE this socket waits
+ for */
+ unsigned int users; /* number of transfers using this */
+ void *socketp; /* settable by users with curl_multi_assign() */
+ unsigned int readers; /* this many transfers want to read */
+ unsigned int writers; /* this many transfers want to write */
+};
+/* bits for 'action' having no bits means this socket is not expecting any
+ action */
+#define SH_READ 1
+#define SH_WRITE 2
+
+/* look up a given socket in the socket hash, skip invalid sockets */
+static struct Curl_sh_entry *sh_getentry(struct Curl_hash *sh,
+ curl_socket_t s)
+{
+ if(s != CURL_SOCKET_BAD) {
+ /* only look for proper sockets */
+ return Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
+ }
+ return NULL;
+}
+
+#define TRHASH_SIZE 13
+static size_t trhash(void *key, size_t key_length, size_t slots_num)
+{
+ size_t keyval = (size_t)*(struct Curl_easy **)key;
+ (void) key_length;
+
+ return (keyval % slots_num);
+}
+
+static size_t trhash_compare(void *k1, size_t k1_len, void *k2, size_t k2_len)
+{
+ (void)k1_len;
+ (void)k2_len;
+
+ return *(struct Curl_easy **)k1 == *(struct Curl_easy **)k2;
+}
+
+static void trhash_dtor(void *nada)
+{
+ (void)nada;
+}
+
+/*
+ * The sockhash has its own separate subhash in each entry that need to be
+ * safely destroyed first.
+ */
+static void sockhash_destroy(struct Curl_hash *h)
+{
+ struct Curl_hash_iterator iter;
+ struct Curl_hash_element *he;
+
+ DEBUGASSERT(h);
+ Curl_hash_start_iterate(h, &iter);
+ he = Curl_hash_next_element(&iter);
+ while(he) {
+ struct Curl_sh_entry *sh = (struct Curl_sh_entry *)he->ptr;
+ Curl_hash_destroy(&sh->transfers);
+ he = Curl_hash_next_element(&iter);
+ }
+ Curl_hash_destroy(h);
+}
+
+
+/* make sure this socket is present in the hash for this handle */
+static struct Curl_sh_entry *sh_addentry(struct Curl_hash *sh,
+ curl_socket_t s)
+{
+ struct Curl_sh_entry *there = sh_getentry(sh, s);
+ struct Curl_sh_entry *check;
+
+ if(there) {
+ /* it is present, return fine */
+ return there;
+ }
+
+ /* not present, add it */
+ check = calloc(1, sizeof(struct Curl_sh_entry));
+ if(!check)
+ return NULL; /* major failure */
+
+ Curl_hash_init(&check->transfers, TRHASH_SIZE, trhash, trhash_compare,
+ trhash_dtor);
+
+ /* make/add new hash entry */
+ if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) {
+ Curl_hash_destroy(&check->transfers);
+ free(check);
+ return NULL; /* major failure */
+ }
+
+ return check; /* things are good in sockhash land */
+}
+
+
+/* delete the given socket + handle from the hash */
+static void sh_delentry(struct Curl_sh_entry *entry,
+ struct Curl_hash *sh, curl_socket_t s)
+{
+ Curl_hash_destroy(&entry->transfers);
+
+ /* We remove the hash entry. This will end up in a call to
+ sh_freeentry(). */
+ Curl_hash_delete(sh, (char *)&s, sizeof(curl_socket_t));
+}
+
+/*
+ * free a sockhash entry
+ */
+static void sh_freeentry(void *freethis)
+{
+ struct Curl_sh_entry *p = (struct Curl_sh_entry *) freethis;
+
+ free(p);
+}
+
+static size_t fd_key_compare(void *k1, size_t k1_len, void *k2, size_t k2_len)
+{
+ (void) k1_len; (void) k2_len;
+
+ return (*((curl_socket_t *) k1)) == (*((curl_socket_t *) k2));
+}
+
+static size_t hash_fd(void *key, size_t key_length, size_t slots_num)
+{
+ curl_socket_t fd = *((curl_socket_t *) key);
+ (void) key_length;
+
+ return (fd % slots_num);
+}
+
+/*
+ * sh_init() creates a new socket hash and returns the handle for it.
+ *
+ * Quote from README.multi_socket:
+ *
+ * "Some tests at 7000 and 9000 connections showed that the socket hash lookup
+ * is somewhat of a bottle neck. Its current implementation may be a bit too
+ * limiting. It simply has a fixed-size array, and on each entry in the array
+ * it has a linked list with entries. So the hash only checks which list to
+ * scan through. The code I had used so for used a list with merely 7 slots
+ * (as that is what the DNS hash uses) but with 7000 connections that would
+ * make an average of 1000 nodes in each list to run through. I upped that to
+ * 97 slots (I believe a prime is suitable) and noticed a significant speed
+ * increase. I need to reconsider the hash implementation or use a rather
+ * large default value like this. At 9000 connections I was still below 10us
+ * per call."
+ *
+ */
+static void sh_init(struct Curl_hash *hash, int hashsize)
+{
+ Curl_hash_init(hash, hashsize, hash_fd, fd_key_compare,
+ sh_freeentry);
+}
+
+/*
+ * multi_addmsg()
+ *
+ * Called when a transfer is completed. Adds the given msg pointer to
+ * the list kept in the multi handle.
+ */
+static CURLMcode multi_addmsg(struct Curl_multi *multi,
+ struct Curl_message *msg)
+{
+ Curl_llist_insert_next(&multi->msglist, multi->msglist.tail, msg,
+ &msg->list);
+ return CURLM_OK;
+}
+
+struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
+ int chashsize, /* connection hash */
+ int dnssize) /* dns hash */
+{
+ struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi));
+
+ if(!multi)
+ return NULL;
+
+ multi->magic = CURL_MULTI_HANDLE;
+
+ Curl_init_dnscache(&multi->hostcache, dnssize);
+
+ sh_init(&multi->sockhash, hashsize);
+
+ if(Curl_conncache_init(&multi->conn_cache, chashsize))
+ goto error;
+
+ Curl_llist_init(&multi->msglist, NULL);
+ Curl_llist_init(&multi->pending, NULL);
+
+ multi->multiplexing = TRUE;
+
+ /* -1 means it not set by user, use the default value */
+ multi->maxconnects = -1;
+ multi->max_concurrent_streams = 100;
+
+#ifdef USE_WINSOCK
+ multi->wsa_event = WSACreateEvent();
+ if(multi->wsa_event == WSA_INVALID_EVENT)
+ goto error;
+#else
+#ifdef ENABLE_WAKEUP
+ if(wakeup_create(multi->wakeup_pair) < 0) {
+ multi->wakeup_pair[0] = CURL_SOCKET_BAD;
+ multi->wakeup_pair[1] = CURL_SOCKET_BAD;
+ }
+ else if(curlx_nonblock(multi->wakeup_pair[0], TRUE) < 0 ||
+ curlx_nonblock(multi->wakeup_pair[1], TRUE) < 0) {
+ wakeup_close(multi->wakeup_pair[0]);
+ wakeup_close(multi->wakeup_pair[1]);
+ multi->wakeup_pair[0] = CURL_SOCKET_BAD;
+ multi->wakeup_pair[1] = CURL_SOCKET_BAD;
+ }
+#endif
+#endif
+
+ return multi;
+
+ error:
+
+ sockhash_destroy(&multi->sockhash);
+ Curl_hash_destroy(&multi->hostcache);
+ Curl_conncache_destroy(&multi->conn_cache);
+ Curl_llist_destroy(&multi->msglist, NULL);
+ Curl_llist_destroy(&multi->pending, NULL);
+
+ free(multi);
+ return NULL;
+}
+
+struct Curl_multi *curl_multi_init(void)
+{
+ return Curl_multi_handle(CURL_SOCKET_HASH_TABLE_SIZE,
+ CURL_CONNECTION_HASH_SIZE,
+ CURL_DNS_HASH_SIZE);
+}
+
+CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
+ struct Curl_easy *data)
+{
+ CURLMcode rc;
+ /* First, make some basic checks that the CURLM handle is a good handle */
+ if(!GOOD_MULTI_HANDLE(multi))
+ return CURLM_BAD_HANDLE;
+
+ /* Verify that we got a somewhat good easy handle too */
+ if(!GOOD_EASY_HANDLE(data))
+ return CURLM_BAD_EASY_HANDLE;
+
+ /* Prevent users from adding same easy handle more than once and prevent
+ adding to more than one multi stack */
+ if(data->multi)
+ return CURLM_ADDED_ALREADY;
+
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+
+ if(multi->dead) {
+ /* a "dead" handle cannot get added transfers while any existing easy
+ handles are still alive - but if there are none alive anymore, it is
+ fine to start over and unmark the "deadness" of this handle */
+ if(multi->num_alive)
+ return CURLM_ABORTED_BY_CALLBACK;
+ multi->dead = FALSE;
+ }
+
+ /* Initialize timeout list for this handle */
+ Curl_llist_init(&data->state.timeoutlist, NULL);
+
+ /*
+ * No failure allowed in this function beyond this point. And no
+ * modification of easy nor multi handle allowed before this except for
+ * potential multi's connection cache growing which won't be undone in this
+ * function no matter what.
+ */
+ if(data->set.errorbuffer)
+ data->set.errorbuffer[0] = 0;
+
+ /* make the Curl_easy refer back to this multi handle - before Curl_expire()
+ is called. */
+ data->multi = multi;
+
+ /* Set the timeout for this handle to expire really soon so that it will
+ be taken care of even when this handle is added in the midst of operation
+ when only the curl_multi_socket() API is used. During that flow, only
+ sockets that time-out or have actions will be dealt with. Since this
+ handle has no action yet, we make sure it times out to get things to
+ happen. */
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+
+ /* A somewhat crude work-around for a little glitch in Curl_update_timer()
+ that happens if the lastcall time is set to the same time when the handle
+ is removed as when the next handle is added, as then the check in
+ Curl_update_timer() that prevents calling the application multiple times
+ with the same timer info will not trigger and then the new handle's
+ timeout will not be notified to the app.
+
+ The work-around is thus simply to clear the 'lastcall' variable to force
+ Curl_update_timer() to always trigger a callback to the app when a new
+ easy handle is added */
+ memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall));
+
+ rc = Curl_update_timer(multi);
+ if(rc)
+ return rc;
+
+ /* set the easy handle */
+ multistate(data, MSTATE_INIT);
+
+ /* for multi interface connections, we share DNS cache automatically if the
+ easy handle's one is currently not set. */
+ if(!data->dns.hostcache ||
+ (data->dns.hostcachetype == HCACHE_NONE)) {
+ data->dns.hostcache = &multi->hostcache;
+ data->dns.hostcachetype = HCACHE_MULTI;
+ }
+
+ /* Point to the shared or multi handle connection cache */
+ if(data->share && (data->share->specifier & (1<< CURL_LOCK_DATA_CONNECT)))
+ data->state.conn_cache = &data->share->conn_cache;
+ else
+ data->state.conn_cache = &multi->conn_cache;
+ data->state.lastconnect_id = -1;
+
+#ifdef USE_LIBPSL
+ /* Do the same for PSL. */
+ if(data->share && (data->share->specifier & (1 << CURL_LOCK_DATA_PSL)))
+ data->psl = &data->share->psl;
+ else
+ data->psl = &multi->psl;
+#endif
+
+ /* We add the new entry last in the list. */
+ data->next = NULL; /* end of the line */
+ if(multi->easyp) {
+ struct Curl_easy *last = multi->easylp;
+ last->next = data;
+ data->prev = last;
+ multi->easylp = data; /* the new last node */
+ }
+ else {
+ /* first node, make prev NULL! */
+ data->prev = NULL;
+ multi->easylp = multi->easyp = data; /* both first and last */
+ }
+
+ /* increase the node-counter */
+ multi->num_easy++;
+
+ /* increase the alive-counter */
+ multi->num_alive++;
+
+ CONNCACHE_LOCK(data);
+ /* The closure handle only ever has default timeouts set. To improve the
+ state somewhat we clone the timeouts from each added handle so that the
+ closure handle always has the same timeouts as the most recently added
+ easy handle. */
+ data->state.conn_cache->closure_handle->set.timeout = data->set.timeout;
+ data->state.conn_cache->closure_handle->set.server_response_timeout =
+ data->set.server_response_timeout;
+ data->state.conn_cache->closure_handle->set.no_signal =
+ data->set.no_signal;
+ CONNCACHE_UNLOCK(data);
+
+ return CURLM_OK;
+}
+
+#if 0
+/* Debug-function, used like this:
+ *
+ * Curl_hash_print(&multi->sockhash, debug_print_sock_hash);
+ *
+ * Enable the hash print function first by editing hash.c
+ */
+static void debug_print_sock_hash(void *p)
+{
+ struct Curl_sh_entry *sh = (struct Curl_sh_entry *)p;
+
+ fprintf(stderr, " [readers %u][writers %u]",
+ sh->readers, sh->writers);
+}
+#endif
+
+static CURLcode multi_done(struct Curl_easy *data,
+ CURLcode status, /* an error if this is called
+ after an error was detected */
+ bool premature)
+{
+ CURLcode result;
+ struct connectdata *conn = data->conn;
+ unsigned int i;
+
+ DEBUGF(infof(data, "multi_done: status: %d prem: %d done: %d",
+ (int)status, (int)premature, data->state.done));
+
+ if(data->state.done)
+ /* Stop if multi_done() has already been called */
+ return CURLE_OK;
+
+ /* Stop the resolver and free its own resources (but not dns_entry yet). */
+ Curl_resolver_kill(data);
+
+ /* Cleanup possible redirect junk */
+ Curl_safefree(data->req.newurl);
+ Curl_safefree(data->req.location);
+
+ switch(status) {
+ case CURLE_ABORTED_BY_CALLBACK:
+ case CURLE_READ_ERROR:
+ case CURLE_WRITE_ERROR:
+ /* When we're aborted due to a callback return code it basically have to
+ be counted as premature as there is trouble ahead if we don't. We have
+ many callbacks and protocols work differently, we could potentially do
+ this more fine-grained in the future. */
+ premature = TRUE;
+ default:
+ break;
+ }
+
+ /* this calls the protocol-specific function pointer previously set */
+ if(conn->handler->done)
+ result = conn->handler->done(data, status, premature);
+ else
+ result = status;
+
+ if(CURLE_ABORTED_BY_CALLBACK != result) {
+ /* avoid this if we already aborted by callback to avoid this calling
+ another callback */
+ int rc = Curl_pgrsDone(data);
+ if(!result && rc)
+ result = CURLE_ABORTED_BY_CALLBACK;
+ }
+
+ process_pending_handles(data->multi); /* connection / multiplex */
+
+ CONNCACHE_LOCK(data);
+ Curl_detach_connection(data);
+ if(CONN_INUSE(conn)) {
+ /* Stop if still used. */
+ CONNCACHE_UNLOCK(data);
+ DEBUGF(infof(data, "Connection still in use %zu, "
+ "no more multi_done now!",
+ conn->easyq.size));
+ return CURLE_OK;
+ }
+
+ data->state.done = TRUE; /* called just now! */
+
+ if(conn->dns_entry) {
+ Curl_resolv_unlock(data, conn->dns_entry); /* done with this */
+ conn->dns_entry = NULL;
+ }
+ Curl_hostcache_prune(data);
+ Curl_safefree(data->state.ulbuf);
+
+ /* if the transfer was completed in a paused state there can be buffered
+ data left to free */
+ for(i = 0; i < data->state.tempcount; i++) {
+ Curl_dyn_free(&data->state.tempwrite[i].b);
+ }
+ data->state.tempcount = 0;
+
+ /* if data->set.reuse_forbid is TRUE, it means the libcurl client has
+ forced us to close this connection. This is ignored for requests taking
+ place in a NTLM/NEGOTIATE authentication handshake
+
+ if conn->bits.close is TRUE, it means that the connection should be
+ closed in spite of all our efforts to be nice, due to protocol
+ restrictions in our or the server's end
+
+ if premature is TRUE, it means this connection was said to be DONE before
+ the entire request operation is complete and thus we can't know in what
+ state it is for re-using, so we're forced to close it. In a perfect world
+ we can add code that keep track of if we really must close it here or not,
+ but currently we have no such detail knowledge.
+ */
+
+ if((data->set.reuse_forbid
+#if defined(USE_NTLM)
+ && !(conn->http_ntlm_state == NTLMSTATE_TYPE2 ||
+ conn->proxy_ntlm_state == NTLMSTATE_TYPE2)
+#endif
+#if defined(USE_SPNEGO)
+ && !(conn->http_negotiate_state == GSS_AUTHRECV ||
+ conn->proxy_negotiate_state == GSS_AUTHRECV)
+#endif
+ ) || conn->bits.close
+ || (premature && !(conn->handler->flags & PROTOPT_STREAM))) {
+ DEBUGF(infof(data, "multi_done, not re-using connection=%ld, forbid=%d"
+ ", close=%d, premature=%d, stream=%d",
+ conn->connection_id,
+ data->set.reuse_forbid, conn->bits.close, premature,
+ (conn->handler->flags & PROTOPT_STREAM)));
+ connclose(conn, "disconnecting");
+ Curl_conncache_remove_conn(data, conn, FALSE);
+ CONNCACHE_UNLOCK(data);
+ Curl_disconnect(data, conn, premature);
+ }
+ else {
+ char buffer[256];
+ const char *host =
+#ifndef CURL_DISABLE_PROXY
+ conn->bits.socksproxy ?
+ conn->socks_proxy.host.dispname :
+ conn->bits.httpproxy ? conn->http_proxy.host.dispname :
+#endif
+ conn->bits.conn_to_host ? conn->conn_to_host.dispname :
+ conn->host.dispname;
+ /* create string before returning the connection */
+ long connection_id = conn->connection_id;
+ msnprintf(buffer, sizeof(buffer),
+ "Connection #%ld to host %s left intact",
+ connection_id, host);
+ /* the connection is no longer in use by this transfer */
+ CONNCACHE_UNLOCK(data);
+ if(Curl_conncache_return_conn(data, conn)) {
+ /* remember the most recently used connection */
+ data->state.lastconnect_id = connection_id;
+ infof(data, "%s", buffer);
+ }
+ else
+ data->state.lastconnect_id = -1;
+ }
+
+ Curl_safefree(data->state.buffer);
+ return result;
+}
+
+static int close_connect_only(struct Curl_easy *data,
+ struct connectdata *conn, void *param)
+{
+ (void)param;
+ if(data->state.lastconnect_id != conn->connection_id)
+ return 0;
+
+ if(!conn->connect_only)
+ return 1;
+
+ connclose(conn, "Removing connect-only easy handle");
+
+ return 1;
+}
+
+CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
+ struct Curl_easy *data)
+{
+ struct Curl_easy *easy = data;
+ bool premature;
+ struct Curl_llist_element *e;
+ CURLMcode rc;
+
+ /* First, make some basic checks that the CURLM handle is a good handle */
+ if(!GOOD_MULTI_HANDLE(multi))
+ return CURLM_BAD_HANDLE;
+
+ /* Verify that we got a somewhat good easy handle too */
+ if(!GOOD_EASY_HANDLE(data))
+ return CURLM_BAD_EASY_HANDLE;
+
+ /* Prevent users from trying to remove same easy handle more than once */
+ if(!data->multi)
+ return CURLM_OK; /* it is already removed so let's say it is fine! */
+
+ /* Prevent users from trying to remove an easy handle from the wrong multi */
+ if(data->multi != multi)
+ return CURLM_BAD_EASY_HANDLE;
+
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+
+ premature = (data->mstate < MSTATE_COMPLETED) ? TRUE : FALSE;
+
+ /* If the 'state' is not INIT or COMPLETED, we might need to do something
+ nice to put the easy_handle in a good known state when this returns. */
+ if(premature) {
+ /* this handle is "alive" so we need to count down the total number of
+ alive connections when this is removed */
+ multi->num_alive--;
+ }
+
+ if(data->conn &&
+ data->mstate > MSTATE_DO &&
+ data->mstate < MSTATE_COMPLETED) {
+ /* Set connection owner so that the DONE function closes it. We can
+ safely do this here since connection is killed. */
+ streamclose(data->conn, "Removed with partial response");
+ }
+
+ if(data->conn) {
+ /* multi_done() clears the association between the easy handle and the
+ connection.
+
+ Note that this ignores the return code simply because there's
+ nothing really useful to do with it anyway! */
+ (void)multi_done(data, data->result, premature);
+ }
+
+ /* The timer must be shut down before data->multi is set to NULL, else the
+ timenode will remain in the splay tree after curl_easy_cleanup is
+ called. Do it after multi_done() in case that sets another time! */
+ Curl_expire_clear(data);
+
+ if(data->connect_queue.ptr)
+ /* the handle was in the pending list waiting for an available connection,
+ so go ahead and remove it */
+ Curl_llist_remove(&multi->pending, &data->connect_queue, NULL);
+
+ if(data->dns.hostcachetype == HCACHE_MULTI) {
+ /* stop using the multi handle's DNS cache, *after* the possible
+ multi_done() call above */
+ data->dns.hostcache = NULL;
+ data->dns.hostcachetype = HCACHE_NONE;
+ }
+
+ Curl_wildcard_dtor(&data->wildcard);
+
+ /* destroy the timeout list that is held in the easy handle, do this *after*
+ multi_done() as that may actually call Curl_expire that uses this */
+ Curl_llist_destroy(&data->state.timeoutlist, NULL);
+
+ /* change state without using multistate(), only to make singlesocket() do
+ what we want */
+ data->mstate = MSTATE_COMPLETED;
+
+ /* This ignores the return code even in case of problems because there's
+ nothing more to do about that, here */
+ (void)singlesocket(multi, easy); /* to let the application know what sockets
+ that vanish with this handle */
+
+ /* Remove the association between the connection and the handle */
+ Curl_detach_connection(data);
+
+ if(data->set.connect_only && !data->multi_easy) {
+ /* This removes a handle that was part the multi interface that used
+ CONNECT_ONLY, that connection is now left alive but since this handle
+ has bits.close set nothing can use that transfer anymore and it is
+ forbidden from reuse. And this easy handle cannot find the connection
+ anymore once removed from the multi handle
+
+ Better close the connection here, at once.
+ */
+ struct connectdata *c;
+ curl_socket_t s;
+ s = Curl_getconnectinfo(data, &c);
+ if((s != CURL_SOCKET_BAD) && c) {
+ Curl_conncache_remove_conn(data, c, TRUE);
+ Curl_disconnect(data, c, TRUE);
+ }
+ }
+
+ if(data->state.lastconnect_id != -1) {
+ /* Mark any connect-only connection for closure */
+ Curl_conncache_foreach(data, data->state.conn_cache,
+ NULL, close_connect_only);
+ }
+
+#ifdef USE_LIBPSL
+ /* Remove the PSL association. */
+ if(data->psl == &multi->psl)
+ data->psl = NULL;
+#endif
+
+ /* as this was using a shared connection cache we clear the pointer to that
+ since we're not part of that multi handle anymore */
+ data->state.conn_cache = NULL;
+
+ data->multi = NULL; /* clear the association to this multi handle */
+
+ /* make sure there's no pending message in the queue sent from this easy
+ handle */
+
+ for(e = multi->msglist.head; e; e = e->next) {
+ struct Curl_message *msg = e->ptr;
+
+ if(msg->extmsg.easy_handle == easy) {
+ Curl_llist_remove(&multi->msglist, e, NULL);
+ /* there can only be one from this specific handle */
+ break;
+ }
+ }
+
+ /* Remove from the pending list if it is there. Otherwise this will
+ remain on the pending list forever due to the state change. */
+ for(e = multi->pending.head; e; e = e->next) {
+ struct Curl_easy *curr_data = e->ptr;
+
+ if(curr_data == data) {
+ Curl_llist_remove(&multi->pending, e, NULL);
+ break;
+ }
+ }
+
+ /* make the previous node point to our next */
+ if(data->prev)
+ data->prev->next = data->next;
+ else
+ multi->easyp = data->next; /* point to first node */
+
+ /* make our next point to our previous node */
+ if(data->next)
+ data->next->prev = data->prev;
+ else
+ multi->easylp = data->prev; /* point to last node */
+
+ /* NOTE NOTE NOTE
+ We do not touch the easy handle here! */
+ multi->num_easy--; /* one less to care about now */
+
+ process_pending_handles(multi);
+
+ rc = Curl_update_timer(multi);
+ if(rc)
+ return rc;
+ return CURLM_OK;
+}
+
+/* Return TRUE if the application asked for multiplexing */
+bool Curl_multiplex_wanted(const struct Curl_multi *multi)
+{
+ return (multi && (multi->multiplexing));
+}
+
+/*
+ * Curl_detach_connection() removes the given transfer from the connection.
+ *
+ * This is the only function that should clear data->conn. This will
+ * occasionally be called with the data->conn pointer already cleared.
+ */
+void Curl_detach_connection(struct Curl_easy *data)
+{
+ struct connectdata *conn = data->conn;
+ if(conn) {
+ Curl_conn_detach_data(conn, data);
+ Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL);
+ }
+ data->conn = NULL;
+}
+
+/*
+ * Curl_attach_connection() attaches this transfer to this connection.
+ *
+ * This is the only function that should assign data->conn
+ */
+void Curl_attach_connection(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ DEBUGASSERT(!data->conn);
+ DEBUGASSERT(conn);
+ data->conn = conn;
+ Curl_llist_insert_next(&conn->easyq, conn->easyq.tail, data,
+ &data->conn_queue);
+ Curl_conn_attach_data(conn, data);
+ if(conn->handler->attach)
+ conn->handler->attach(data, conn);
+}
+
+static int domore_getsock(struct Curl_easy *data,
+ struct connectdata *conn,
+ curl_socket_t *socks)
+{
+ if(conn && conn->handler->domore_getsock)
+ return conn->handler->domore_getsock(data, conn, socks);
+ return GETSOCK_BLANK;
+}
+
+static int doing_getsock(struct Curl_easy *data,
+ struct connectdata *conn,
+ curl_socket_t *socks)
+{
+ if(conn && conn->handler->doing_getsock)
+ return conn->handler->doing_getsock(data, conn, socks);
+ return GETSOCK_BLANK;
+}
+
+static int protocol_getsock(struct Curl_easy *data,
+ struct connectdata *conn,
+ curl_socket_t *socks)
+{
+ if(conn->handler->proto_getsock)
+ return conn->handler->proto_getsock(data, conn, socks);
+ /* Backup getsock logic. Since there is a live socket in use, we must wait
+ for it or it will be removed from watching when the multi_socket API is
+ used. */
+ socks[0] = conn->sock[FIRSTSOCKET];
+ return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0);
+}
+
+/* returns bitmapped flags for this handle and its sockets. The 'socks[]'
+ array contains MAX_SOCKSPEREASYHANDLE entries. */
+static int multi_getsock(struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ struct connectdata *conn = data->conn;
+ /* The no connection case can happen when this is called from
+ curl_multi_remove_handle() => singlesocket() => multi_getsock().
+ */
+ if(!conn)
+ return 0;
+
+ switch(data->mstate) {
+ default:
+ return 0;
+
+ case MSTATE_RESOLVING:
+ return Curl_resolv_getsock(data, socks);
+
+ case MSTATE_PROTOCONNECTING:
+ case MSTATE_PROTOCONNECT:
+ return protocol_getsock(data, conn, socks);
+
+ case MSTATE_DO:
+ case MSTATE_DOING:
+ return doing_getsock(data, conn, socks);
+
+ case MSTATE_TUNNELING:
+ case MSTATE_CONNECTING:
+ return Curl_conn_get_select_socks(data, FIRSTSOCKET, socks);
+
+ case MSTATE_DOING_MORE:
+ return domore_getsock(data, conn, socks);
+
+ case MSTATE_DID: /* since is set after DO is completed, we switch to
+ waiting for the same as the PERFORMING state */
+ case MSTATE_PERFORMING:
+ return Curl_single_getsock(data, conn, socks);
+ }
+
+}
+
+CURLMcode curl_multi_fdset(struct Curl_multi *multi,
+ fd_set *read_fd_set, fd_set *write_fd_set,
+ fd_set *exc_fd_set, int *max_fd)
+{
+ /* Scan through all the easy handles to get the file descriptors set.
+ Some easy handles may not have connected to the remote host yet,
+ and then we must make sure that is done. */
+ struct Curl_easy *data;
+ int this_max_fd = -1;
+ curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
+ int i;
+ (void)exc_fd_set; /* not used */
+
+ if(!GOOD_MULTI_HANDLE(multi))
+ return CURLM_BAD_HANDLE;
+
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+
+ data = multi->easyp;
+ while(data) {
+ int bitmap;
+#ifdef __clang_analyzer_
+ /* to prevent "The left operand of '>=' is a garbage value" warnings */
+ memset(sockbunch, 0, sizeof(sockbunch));
+#endif
+ bitmap = multi_getsock(data, sockbunch);
+
+ for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) {
+ curl_socket_t s = CURL_SOCKET_BAD;
+
+ if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK(sockbunch[i])) {
+ if(!FDSET_SOCK(sockbunch[i]))
+ /* pretend it doesn't exist */
+ continue;
+ FD_SET(sockbunch[i], read_fd_set);
+ s = sockbunch[i];
+ }
+ if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK(sockbunch[i])) {
+ if(!FDSET_SOCK(sockbunch[i]))
+ /* pretend it doesn't exist */
+ continue;
+ FD_SET(sockbunch[i], write_fd_set);
+ s = sockbunch[i];
+ }
+ if(s == CURL_SOCKET_BAD)
+ /* this socket is unused, break out of loop */
+ break;
+ if((int)s > this_max_fd)
+ this_max_fd = (int)s;
+ }
+
+ data = data->next; /* check next handle */
+ }
+
+ *max_fd = this_max_fd;
+
+ return CURLM_OK;
+}
+
+#define NUM_POLLS_ON_STACK 10
+
+static CURLMcode multi_wait(struct Curl_multi *multi,
+ struct curl_waitfd extra_fds[],
+ unsigned int extra_nfds,
+ int timeout_ms,
+ int *ret,
+ bool extrawait, /* when no socket, wait */
+ bool use_wakeup)
+{
+ struct Curl_easy *data;
+ curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
+ int bitmap;
+ unsigned int i;
+ unsigned int nfds = 0;
+ unsigned int curlfds;
+ long timeout_internal;
+ int retcode = 0;
+ struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
+ struct pollfd *ufds = &a_few_on_stack[0];
+ bool ufds_malloc = FALSE;
+#ifdef USE_WINSOCK
+ WSANETWORKEVENTS wsa_events;
+ DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT);
+#endif
+#ifndef ENABLE_WAKEUP
+ (void)use_wakeup;
+#endif
+
+ if(!GOOD_MULTI_HANDLE(multi))
+ return CURLM_BAD_HANDLE;
+
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+
+ if(timeout_ms < 0)
+ return CURLM_BAD_FUNCTION_ARGUMENT;
+
+ /* Count up how many fds we have from the multi handle */
+ data = multi->easyp;
+ while(data) {
+ bitmap = multi_getsock(data, sockbunch);
+
+ for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) {
+ curl_socket_t s = CURL_SOCKET_BAD;
+
+ if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) {
+ ++nfds;
+ s = sockbunch[i];
+ }
+ if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) {
+ ++nfds;
+ s = sockbunch[i];
+ }
+ if(s == CURL_SOCKET_BAD) {
+ break;
+ }
+ }
+
+ data = data->next; /* check next handle */
+ }
+
+ /* If the internally desired timeout is actually shorter than requested from
+ the outside, then use the shorter time! But only if the internal timer
+ is actually larger than -1! */
+ (void)multi_timeout(multi, &timeout_internal);
+ if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms))
+ timeout_ms = (int)timeout_internal;
+
+ curlfds = nfds; /* number of internal file descriptors */
+ nfds += extra_nfds; /* add the externally provided ones */
+
+#ifdef ENABLE_WAKEUP
+#ifdef USE_WINSOCK
+ if(use_wakeup) {
+#else
+ if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
+#endif
+ ++nfds;
+ }
+#endif
+
+ if(nfds > NUM_POLLS_ON_STACK) {
+ /* 'nfds' is a 32 bit value and 'struct pollfd' is typically 8 bytes
+ big, so at 2^29 sockets this value might wrap. When a process gets
+ the capability to actually handle over 500 million sockets this
+ calculation needs a integer overflow check. */
+ ufds = malloc(nfds * sizeof(struct pollfd));
+ if(!ufds)
+ return CURLM_OUT_OF_MEMORY;
+ ufds_malloc = TRUE;
+ }
+ nfds = 0;
+
+ /* only do the second loop if we found descriptors in the first stage run
+ above */
+
+ if(curlfds) {
+ /* Add the curl handles to our pollfds first */
+ data = multi->easyp;
+ while(data) {
+ bitmap = multi_getsock(data, sockbunch);
+
+ for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) {
+ curl_socket_t s = CURL_SOCKET_BAD;
+#ifdef USE_WINSOCK
+ long mask = 0;
+#endif
+ if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) {
+ s = sockbunch[i];
+#ifdef USE_WINSOCK
+ mask |= FD_READ|FD_ACCEPT|FD_CLOSE;
+#endif
+ ufds[nfds].fd = s;
+ ufds[nfds].events = POLLIN;
+ ++nfds;
+ }
+ if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) {
+ s = sockbunch[i];
+#ifdef USE_WINSOCK
+ mask |= FD_WRITE|FD_CONNECT|FD_CLOSE;
+ send(s, NULL, 0, 0); /* reset FD_WRITE */
+#endif
+ ufds[nfds].fd = s;
+ ufds[nfds].events = POLLOUT;
+ ++nfds;
+ }
+ /* s is only set if either being readable or writable is checked */
+ if(s == CURL_SOCKET_BAD) {
+ /* break on entry not checked for being readable or writable */
+ break;
+ }
+#ifdef USE_WINSOCK
+ if(WSAEventSelect(s, multi->wsa_event, mask) != 0) {
+ if(ufds_malloc)
+ free(ufds);
+ return CURLM_INTERNAL_ERROR;
+ }
+#endif
+ }
+
+ data = data->next; /* check next handle */
+ }
+ }
+
+ /* Add external file descriptions from poll-like struct curl_waitfd */
+ for(i = 0; i < extra_nfds; i++) {
+#ifdef USE_WINSOCK
+ long mask = 0;
+ if(extra_fds[i].events & CURL_WAIT_POLLIN)
+ mask |= FD_READ|FD_ACCEPT|FD_CLOSE;
+ if(extra_fds[i].events & CURL_WAIT_POLLPRI)
+ mask |= FD_OOB;
+ if(extra_fds[i].events & CURL_WAIT_POLLOUT) {
+ mask |= FD_WRITE|FD_CONNECT|FD_CLOSE;
+ send(extra_fds[i].fd, NULL, 0, 0); /* reset FD_WRITE */
+ }
+ if(WSAEventSelect(extra_fds[i].fd, multi->wsa_event, mask) != 0) {
+ if(ufds_malloc)
+ free(ufds);
+ return CURLM_INTERNAL_ERROR;
+ }
+#endif
+ ufds[nfds].fd = extra_fds[i].fd;
+ ufds[nfds].events = 0;
+ if(extra_fds[i].events & CURL_WAIT_POLLIN)
+ ufds[nfds].events |= POLLIN;
+ if(extra_fds[i].events & CURL_WAIT_POLLPRI)
+ ufds[nfds].events |= POLLPRI;
+ if(extra_fds[i].events & CURL_WAIT_POLLOUT)
+ ufds[nfds].events |= POLLOUT;
+ ++nfds;
+ }
+
+#ifdef ENABLE_WAKEUP
+#ifndef USE_WINSOCK
+ if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
+ ufds[nfds].fd = multi->wakeup_pair[0];
+ ufds[nfds].events = POLLIN;
+ ++nfds;
+ }
+#endif
+#endif
+
+#if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK)
+ if(nfds || use_wakeup) {
+#else
+ if(nfds) {
+#endif
+ int pollrc;
+#ifdef USE_WINSOCK
+ if(nfds)
+ pollrc = Curl_poll(ufds, nfds, 0); /* just pre-check with WinSock */
+ else
+ pollrc = 0;
+#else
+ pollrc = Curl_poll(ufds, nfds, timeout_ms); /* wait... */
+#endif
+ if(pollrc < 0)
+ return CURLM_UNRECOVERABLE_POLL;
+
+ if(pollrc > 0) {
+ retcode = pollrc;
+#ifdef USE_WINSOCK
+ }
+ else { /* now wait... if not ready during the pre-check (pollrc == 0) */
+ WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, timeout_ms, FALSE);
+ }
+ /* With WinSock, we have to run the following section unconditionally
+ to call WSAEventSelect(fd, event, 0) on all the sockets */
+ {
+#endif
+ /* copy revents results from the poll to the curl_multi_wait poll
+ struct, the bit values of the actual underlying poll() implementation
+ may not be the same as the ones in the public libcurl API! */
+ for(i = 0; i < extra_nfds; i++) {
+ unsigned r = ufds[curlfds + i].revents;
+ unsigned short mask = 0;
+#ifdef USE_WINSOCK
+ curl_socket_t s = extra_fds[i].fd;
+ wsa_events.lNetworkEvents = 0;
+ if(WSAEnumNetworkEvents(s, NULL, &wsa_events) == 0) {
+ if(wsa_events.lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE))
+ mask |= CURL_WAIT_POLLIN;
+ if(wsa_events.lNetworkEvents & (FD_WRITE|FD_CONNECT|FD_CLOSE))
+ mask |= CURL_WAIT_POLLOUT;
+ if(wsa_events.lNetworkEvents & FD_OOB)
+ mask |= CURL_WAIT_POLLPRI;
+ if(ret && !pollrc && wsa_events.lNetworkEvents)
+ retcode++;
+ }
+ WSAEventSelect(s, multi->wsa_event, 0);
+ if(!pollrc) {
+ extra_fds[i].revents = mask;
+ continue;
+ }
+#endif
+ if(r & POLLIN)
+ mask |= CURL_WAIT_POLLIN;
+ if(r & POLLOUT)
+ mask |= CURL_WAIT_POLLOUT;
+ if(r & POLLPRI)
+ mask |= CURL_WAIT_POLLPRI;
+ extra_fds[i].revents = mask;
+ }
+
+#ifdef USE_WINSOCK
+ /* Count up all our own sockets that had activity,
+ and remove them from the event. */
+ if(curlfds) {
+ data = multi->easyp;
+ while(data) {
+ bitmap = multi_getsock(data, sockbunch);
+
+ for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) {
+ if(bitmap & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))) {
+ wsa_events.lNetworkEvents = 0;
+ if(WSAEnumNetworkEvents(sockbunch[i], NULL, &wsa_events) == 0) {
+ if(ret && !pollrc && wsa_events.lNetworkEvents)
+ retcode++;
+ }
+ WSAEventSelect(sockbunch[i], multi->wsa_event, 0);
+ }
+ else {
+ /* break on entry not checked for being readable or writable */
+ break;
+ }
+ }
+
+ data = data->next;
+ }
+ }
+
+ WSAResetEvent(multi->wsa_event);
+#else
+#ifdef ENABLE_WAKEUP
+ if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
+ if(ufds[curlfds + extra_nfds].revents & POLLIN) {
+ char buf[64];
+ ssize_t nread;
+ while(1) {
+ /* the reading socket is non-blocking, try to read
+ data from it until it receives an error (except EINTR).
+ In normal cases it will get EAGAIN or EWOULDBLOCK
+ when there is no more data, breaking the loop. */
+ nread = wakeup_read(multi->wakeup_pair[0], buf, sizeof(buf));
+ if(nread <= 0) {
+ if(nread < 0 && EINTR == SOCKERRNO)
+ continue;
+ break;
+ }
+ }
+ /* do not count the wakeup socket into the returned value */
+ retcode--;
+ }
+ }
+#endif
+#endif
+ }
+ }
+
+ if(ufds_malloc)
+ free(ufds);
+ if(ret)
+ *ret = retcode;
+#if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK)
+ if(extrawait && !nfds && !use_wakeup) {
+#else
+ if(extrawait && !nfds) {
+#endif
+ long sleep_ms = 0;
+
+ /* Avoid busy-looping when there's nothing particular to wait for */
+ if(!curl_multi_timeout(multi, &sleep_ms) && sleep_ms) {
+ if(sleep_ms > timeout_ms)
+ sleep_ms = timeout_ms;
+ /* when there are no easy handles in the multi, this holds a -1
+ timeout */
+ else if(sleep_ms < 0)
+ sleep_ms = timeout_ms;
+ Curl_wait_ms(sleep_ms);
+ }
+ }
+
+ return CURLM_OK;
+}
+
+CURLMcode curl_multi_wait(struct Curl_multi *multi,
+ struct curl_waitfd extra_fds[],
+ unsigned int extra_nfds,
+ int timeout_ms,
+ int *ret)
+{
+ return multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, FALSE,
+ FALSE);
+}
+
+CURLMcode curl_multi_poll(struct Curl_multi *multi,
+ struct curl_waitfd extra_fds[],
+ unsigned int extra_nfds,
+ int timeout_ms,
+ int *ret)
+{
+ return multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, TRUE,
+ TRUE);
+}
+
+CURLMcode curl_multi_wakeup(struct Curl_multi *multi)
+{
+ /* this function is usually called from another thread,
+ it has to be careful only to access parts of the
+ Curl_multi struct that are constant */
+
+ /* GOOD_MULTI_HANDLE can be safely called */
+ if(!GOOD_MULTI_HANDLE(multi))
+ return CURLM_BAD_HANDLE;
+
+#ifdef ENABLE_WAKEUP
+#ifdef USE_WINSOCK
+ if(WSASetEvent(multi->wsa_event))
+ return CURLM_OK;
+#else
+ /* the wakeup_pair variable is only written during init and cleanup,
+ making it safe to access from another thread after the init part
+ and before cleanup */
+ if(multi->wakeup_pair[1] != CURL_SOCKET_BAD) {
+ char buf[1];
+ buf[0] = 1;
+ while(1) {
+ /* swrite() is not thread-safe in general, because concurrent calls
+ can have their messages interleaved, but in this case the content
+ of the messages does not matter, which makes it ok to call.
+
+ The write socket is set to non-blocking, this way this function
+ cannot block, making it safe to call even from the same thread
+ that will call curl_multi_wait(). If swrite() returns that it
+ would block, it's considered successful because it means that
+ previous calls to this function will wake up the poll(). */
+ if(wakeup_write(multi->wakeup_pair[1], buf, sizeof(buf)) < 0) {
+ int err = SOCKERRNO;
+ int return_success;
+#ifdef USE_WINSOCK
+ return_success = WSAEWOULDBLOCK == err;
+#else
+ if(EINTR == err)
+ continue;
+ return_success = EWOULDBLOCK == err || EAGAIN == err;
+#endif
+ if(!return_success)
+ return CURLM_WAKEUP_FAILURE;
+ }
+ return CURLM_OK;
+ }
+ }
+#endif
+#endif
+ return CURLM_WAKEUP_FAILURE;
+}
+
+/*
+ * multi_ischanged() is called
+ *
+ * Returns TRUE/FALSE whether the state is changed to trigger a CONNECT_PEND
+ * => CONNECT action.
+ *
+ * Set 'clear' to TRUE to have it also clear the state variable.
+ */
+static bool multi_ischanged(struct Curl_multi *multi, bool clear)
+{
+ bool retval = multi->recheckstate;
+ if(clear)
+ multi->recheckstate = FALSE;
+ return retval;
+}
+
+CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
+ struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLMcode rc;
+
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+
+ rc = curl_multi_add_handle(multi, data);
+ if(!rc) {
+ struct SingleRequest *k = &data->req;
+
+ /* pass in NULL for 'conn' here since we don't want to init the
+ connection, only this transfer */
+ Curl_init_do(data, NULL);
+
+ /* take this handle to the perform state right away */
+ multistate(data, MSTATE_PERFORMING);
+ Curl_attach_connection(data, conn);
+ k->keepon |= KEEP_RECV; /* setup to receive! */
+ }
+ return rc;
+}
+
+static CURLcode multi_do(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+
+ DEBUGASSERT(conn);
+ DEBUGASSERT(conn->handler);
+
+ if(conn->handler->do_it)
+ /* generic protocol-specific function pointer set in curl_connect() */
+ result = conn->handler->do_it(data, done);
+
+ return result;
+}
+
+/*
+ * multi_do_more() is called during the DO_MORE multi state. It is basically a
+ * second stage DO state which (wrongly) was introduced to support FTP's
+ * second connection.
+ *
+ * 'complete' can return 0 for incomplete, 1 for done and -1 for go back to
+ * DOING state there's more work to do!
+ */
+
+static CURLcode multi_do_more(struct Curl_easy *data, int *complete)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+
+ *complete = 0;
+
+ if(conn->handler->do_more)
+ result = conn->handler->do_more(data, complete);
+
+ return result;
+}
+
+/*
+ * Check whether a timeout occurred, and handle it if it did
+ */
+static bool multi_handle_timeout(struct Curl_easy *data,
+ struct curltime *now,
+ bool *stream_error,
+ CURLcode *result,
+ bool connect_timeout)
+{
+ timediff_t timeout_ms;
+ timeout_ms = Curl_timeleft(data, now, connect_timeout);
+
+ if(timeout_ms < 0) {
+ /* Handle timed out */
+ if(data->mstate == MSTATE_RESOLVING)
+ failf(data, "Resolving timed out after %" CURL_FORMAT_TIMEDIFF_T
+ " milliseconds",
+ Curl_timediff(*now, data->progress.t_startsingle));
+ else if(data->mstate == MSTATE_CONNECTING)
+ failf(data, "Connection timed out after %" CURL_FORMAT_TIMEDIFF_T
+ " milliseconds",
+ Curl_timediff(*now, data->progress.t_startsingle));
+ else {
+ struct SingleRequest *k = &data->req;
+ if(k->size != -1) {
+ failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
+ " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %"
+ CURL_FORMAT_CURL_OFF_T " bytes received",
+ Curl_timediff(*now, data->progress.t_startsingle),
+ k->bytecount, k->size);
+ }
+ else {
+ failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
+ " milliseconds with %" CURL_FORMAT_CURL_OFF_T
+ " bytes received",
+ Curl_timediff(*now, data->progress.t_startsingle),
+ k->bytecount);
+ }
+ }
+
+ /* Force connection closed if the connection has indeed been used */
+ if(data->mstate > MSTATE_DO) {
+ streamclose(data->conn, "Disconnected with pending data");
+ *stream_error = TRUE;
+ }
+ *result = CURLE_OPERATION_TIMEDOUT;
+ (void)multi_done(data, *result, TRUE);
+ }
+
+ return (timeout_ms < 0);
+}
+
+/*
+ * We are doing protocol-specific connecting and this is being called over and
+ * over from the multi interface until the connection phase is done on
+ * protocol layer.
+ */
+
+static CURLcode protocol_connecting(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+
+ if(conn && conn->handler->connecting) {
+ *done = FALSE;
+ result = conn->handler->connecting(data, done);
+ }
+ else
+ *done = TRUE;
+
+ return result;
+}
+
+/*
+ * We are DOING this is being called over and over from the multi interface
+ * until the DOING phase is done on protocol layer.
+ */
+
+static CURLcode protocol_doing(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+
+ if(conn && conn->handler->doing) {
+ *done = FALSE;
+ result = conn->handler->doing(data, done);
+ }
+ else
+ *done = TRUE;
+
+ return result;
+}
+
+/*
+ * We have discovered that the TCP connection has been successful, we can now
+ * proceed with some action.
+ *
+ */
+static CURLcode protocol_connect(struct Curl_easy *data,
+ bool *protocol_done)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ DEBUGASSERT(conn);
+ DEBUGASSERT(protocol_done);
+
+ *protocol_done = FALSE;
+
+ if(Curl_conn_is_connected(conn, FIRSTSOCKET)
+ && conn->bits.protoconnstart) {
+ /* We already are connected, get back. This may happen when the connect
+ worked fine in the first call, like when we connect to a local server
+ or proxy. Note that we don't know if the protocol is actually done.
+
+ Unless this protocol doesn't have any protocol-connect callback, as
+ then we know we're done. */
+ if(!conn->handler->connecting)
+ *protocol_done = TRUE;
+
+ return CURLE_OK;
+ }
+
+ if(!conn->bits.protoconnstart) {
+ if(conn->handler->connect_it) {
+ /* is there a protocol-specific connect() procedure? */
+
+ /* Call the protocol-specific connect function */
+ result = conn->handler->connect_it(data, protocol_done);
+ }
+ else
+ *protocol_done = TRUE;
+
+ /* it has started, possibly even completed but that knowledge isn't stored
+ in this bit! */
+ if(!result)
+ conn->bits.protoconnstart = TRUE;
+ }
+
+ return result; /* pass back status */
+}
+
+/*
+ * readrewind() rewinds the read stream. This is typically used for HTTP
+ * POST/PUT with multi-pass authentication when a sending was denied and a
+ * resend is necessary.
+ */
+static CURLcode readrewind(struct Curl_easy *data)
+{
+ struct connectdata *conn = data->conn;
+ curl_mimepart *mimepart = &data->set.mimepost;
+ DEBUGASSERT(conn);
+
+ data->state.rewindbeforesend = FALSE; /* we rewind now */
+
+ /* explicitly switch off sending data on this connection now since we are
+ about to restart a new transfer and thus we want to avoid inadvertently
+ sending more data on the existing connection until the next transfer
+ starts */
+ data->req.keepon &= ~KEEP_SEND;
+
+ /* We have sent away data. If not using CURLOPT_POSTFIELDS or
+ CURLOPT_HTTPPOST, call app to rewind
+ */
+ if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
+ struct HTTP *http = data->req.p.http;
+
+ if(http->sendit)
+ mimepart = http->sendit;
+ }
+ if(data->set.postfields ||
+ (data->state.httpreq == HTTPREQ_GET) ||
+ (data->state.httpreq == HTTPREQ_HEAD))
+ ; /* no need to rewind */
+ else if(data->state.httpreq == HTTPREQ_POST_MIME ||
+ data->state.httpreq == HTTPREQ_POST_FORM) {
+ CURLcode result = Curl_mime_rewind(mimepart);
+ if(result) {
+ failf(data, "Cannot rewind mime/post data");
+ return result;
+ }
+ }
+ else {
+ if(data->set.seek_func) {
+ int err;
+
+ Curl_set_in_callback(data, true);
+ err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET);
+ Curl_set_in_callback(data, false);
+ if(err) {
+ failf(data, "seek callback returned error %d", (int)err);
+ return CURLE_SEND_FAIL_REWIND;
+ }
+ }
+ else if(data->set.ioctl_func) {
+ curlioerr err;
+
+ Curl_set_in_callback(data, true);
+ err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD,
+ data->set.ioctl_client);
+ Curl_set_in_callback(data, false);
+ infof(data, "the ioctl callback returned %d", (int)err);
+
+ if(err) {
+ failf(data, "ioctl callback returned error %d", (int)err);
+ return CURLE_SEND_FAIL_REWIND;
+ }
+ }
+ else {
+ /* If no CURLOPT_READFUNCTION is used, we know that we operate on a
+ given FILE * stream and we can actually attempt to rewind that
+ ourselves with fseek() */
+ if(data->state.fread_func == (curl_read_callback)fread) {
+ if(-1 != fseek(data->state.in, 0, SEEK_SET))
+ /* successful rewind */
+ return CURLE_OK;
+ }
+
+ /* no callback set or failure above, makes us fail at once */
+ failf(data, "necessary data rewind wasn't possible");
+ return CURLE_SEND_FAIL_REWIND;
+ }
+ }
+ return CURLE_OK;
+}
+
+/*
+ * Curl_preconnect() is called immediately before a connect starts. When a
+ * redirect is followed, this is then called multiple times during a single
+ * transfer.
+ */
+CURLcode Curl_preconnect(struct Curl_easy *data)
+{
+ if(!data->state.buffer) {
+ data->state.buffer = malloc(data->set.buffer_size + 1);
+ if(!data->state.buffer)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ return CURLE_OK;
+}
+
+static void set_in_callback(struct Curl_multi *multi, bool value)
+{
+ multi->in_callback = value;
+}
+
+static CURLMcode multi_runsingle(struct Curl_multi *multi,
+ struct curltime *nowp,
+ struct Curl_easy *data)
+{
+ struct Curl_message *msg = NULL;
+ bool connected;
+ bool async;
+ bool protocol_connected = FALSE;
+ bool dophase_done = FALSE;
+ bool done = FALSE;
+ CURLMcode rc;
+ CURLcode result = CURLE_OK;
+ timediff_t recv_timeout_ms;
+ timediff_t send_timeout_ms;
+ int control;
+
+ if(!GOOD_EASY_HANDLE(data))
+ return CURLM_BAD_EASY_HANDLE;
+
+ if(multi->dead) {
+ /* a multi-level callback returned error before, meaning every individual
+ transfer now has failed */
+ result = CURLE_ABORTED_BY_CALLBACK;
+ Curl_posttransfer(data);
+ multi_done(data, result, FALSE);
+ multistate(data, MSTATE_COMPLETED);
+ }
+
+ do {
+ /* A "stream" here is a logical stream if the protocol can handle that
+ (HTTP/2), or the full connection for older protocols */
+ bool stream_error = FALSE;
+ rc = CURLM_OK;
+
+ if(multi_ischanged(multi, TRUE)) {
+ DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue"));
+ process_pending_handles(multi); /* multiplexed */
+ }
+
+ if(data->mstate > MSTATE_CONNECT &&
+ data->mstate < MSTATE_COMPLETED) {
+ /* Make sure we set the connection's current owner */
+ DEBUGASSERT(data->conn);
+ if(!data->conn)
+ return CURLM_INTERNAL_ERROR;
+ }
+
+ if(data->conn &&
+ (data->mstate >= MSTATE_CONNECT) &&
+ (data->mstate < MSTATE_COMPLETED)) {
+ /* Check for overall operation timeout here but defer handling the
+ * connection timeout to later, to allow for a connection to be set up
+ * in the window since we last checked timeout. This prevents us
+ * tearing down a completed connection in the case where we were slow
+ * to check the timeout (e.g. process descheduled during this loop).
+ * We set connect_timeout=FALSE to do this. */
+
+ /* we need to wait for the connect state as only then is the start time
+ stored, but we must not check already completed handles */
+ if(multi_handle_timeout(data, nowp, &stream_error, &result, FALSE)) {
+ /* Skip the statemachine and go directly to error handling section. */
+ goto statemachine_end;
+ }
+ }
+
+ switch(data->mstate) {
+ case MSTATE_INIT:
+ /* init this transfer. */
+ result = Curl_pretransfer(data);
+
+ if(!result) {
+ /* after init, go CONNECT */
+ multistate(data, MSTATE_CONNECT);
+ *nowp = Curl_pgrsTime(data, TIMER_STARTOP);
+ rc = CURLM_CALL_MULTI_PERFORM;
+ }
+ break;
+
+ case MSTATE_PENDING:
+ /* We will stay here until there is a connection available. Then
+ we try again in the MSTATE_CONNECT state. */
+ break;
+
+ case MSTATE_CONNECT:
+ /* Connect. We want to get a connection identifier filled in. */
+ /* init this transfer. */
+ result = Curl_preconnect(data);
+ if(result)
+ break;
+
+ *nowp = Curl_pgrsTime(data, TIMER_STARTSINGLE);
+ if(data->set.timeout)
+ Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT);
+
+ if(data->set.connecttimeout)
+ Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT);
+
+ result = Curl_connect(data, &async, &connected);
+ if(CURLE_NO_CONNECTION_AVAILABLE == result) {
+ /* There was no connection available. We will go to the pending
+ state and wait for an available connection. */
+ multistate(data, MSTATE_PENDING);
+
+ /* add this handle to the list of connect-pending handles */
+ Curl_llist_insert_next(&multi->pending, multi->pending.tail, data,
+ &data->connect_queue);
+ result = CURLE_OK;
+ break;
+ }
+ else if(data->state.previouslypending) {
+ /* this transfer comes from the pending queue so try move another */
+ infof(data, "Transfer was pending, now try another");
+ process_pending_handles(data->multi);
+ }
+
+ if(!result) {
+ if(async)
+ /* We're now waiting for an asynchronous name lookup */
+ multistate(data, MSTATE_RESOLVING);
+ else {
+ /* after the connect has been sent off, go WAITCONNECT unless the
+ protocol connect is already done and we can go directly to
+ WAITDO or DO! */
+ rc = CURLM_CALL_MULTI_PERFORM;
+
+ if(connected)
+ multistate(data, MSTATE_PROTOCONNECT);
+ else {
+ multistate(data, MSTATE_CONNECTING);
+ }
+ }
+ }
+ break;
+
+ case MSTATE_RESOLVING:
+ /* awaiting an asynch name resolve to complete */
+ {
+ struct Curl_dns_entry *dns = NULL;
+ struct connectdata *conn = data->conn;
+ const char *hostname;
+
+ DEBUGASSERT(conn);
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.httpproxy)
+ hostname = conn->http_proxy.host.name;
+ else
+#endif
+ if(conn->bits.conn_to_host)
+ hostname = conn->conn_to_host.name;
+ else
+ hostname = conn->host.name;
+
+ /* check if we have the name resolved by now */
+ dns = Curl_fetch_addr(data, hostname, (int)conn->port);
+
+ if(dns) {
+#ifdef CURLRES_ASYNCH
+ data->state.async.dns = dns;
+ data->state.async.done = TRUE;
+#endif
+ result = CURLE_OK;
+ infof(data, "Hostname '%s' was found in DNS cache", hostname);
+ }
+
+ if(!dns)
+ result = Curl_resolv_check(data, &dns);
+
+ /* Update sockets here, because the socket(s) may have been
+ closed and the application thus needs to be told, even if it
+ is likely that the same socket(s) will again be used further
+ down. If the name has not yet been resolved, it is likely
+ that new sockets have been opened in an attempt to contact
+ another resolver. */
+ rc = singlesocket(multi, data);
+ if(rc)
+ return rc;
+
+ if(dns) {
+ /* Perform the next step in the connection phase, and then move on
+ to the WAITCONNECT state */
+ result = Curl_once_resolved(data, &connected);
+
+ if(result)
+ /* if Curl_once_resolved() returns failure, the connection struct
+ is already freed and gone */
+ data->conn = NULL; /* no more connection */
+ else {
+ /* call again please so that we get the next socket setup */
+ rc = CURLM_CALL_MULTI_PERFORM;
+ if(connected)
+ multistate(data, MSTATE_PROTOCONNECT);
+ else {
+ multistate(data, MSTATE_CONNECTING);
+ }
+ }
+ }
+
+ if(result) {
+ /* failure detected */
+ stream_error = TRUE;
+ break;
+ }
+ }
+ break;
+
+#ifndef CURL_DISABLE_HTTP
+ case MSTATE_TUNNELING:
+ /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */
+ DEBUGASSERT(data->conn);
+ result = Curl_http_connect(data, &protocol_connected);
+#ifndef CURL_DISABLE_PROXY
+ if(data->conn->bits.proxy_connect_closed) {
+ rc = CURLM_CALL_MULTI_PERFORM;
+ /* connect back to proxy again */
+ result = CURLE_OK;
+ multi_done(data, CURLE_OK, FALSE);
+ multistate(data, MSTATE_CONNECT);
+ }
+ else
+#endif
+ if(!result) {
+ rc = CURLM_CALL_MULTI_PERFORM;
+ /* initiate protocol connect phase */
+ multistate(data, MSTATE_PROTOCONNECT);
+ }
+ else
+ stream_error = TRUE;
+ break;
+#endif
+
+ case MSTATE_CONNECTING:
+ /* awaiting a completion of an asynch TCP connect */
+ DEBUGASSERT(data->conn);
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected);
+ if(connected && !result) {
+ rc = CURLM_CALL_MULTI_PERFORM;
+ multistate(data, MSTATE_PROTOCONNECT);
+ }
+ else if(result) {
+ /* failure detected */
+ Curl_posttransfer(data);
+ multi_done(data, result, TRUE);
+ stream_error = TRUE;
+ break;
+ }
+ break;
+
+ case MSTATE_PROTOCONNECT:
+ if(data->state.rewindbeforesend)
+ result = readrewind(data);
+
+ if(!result && data->conn->bits.reuse) {
+ /* ftp seems to hang when protoconnect on reused connection
+ * since we handle PROTOCONNECT in general inside the filers, it
+ * seems wrong to restart this on a reused connection. */
+ multistate(data, MSTATE_DO);
+ rc = CURLM_CALL_MULTI_PERFORM;
+ break;
+ }
+ if(!result)
+ result = protocol_connect(data, &protocol_connected);
+ if(!result && !protocol_connected)
+ /* switch to waiting state */
+ multistate(data, MSTATE_PROTOCONNECTING);
+ else if(!result) {
+ /* protocol connect has completed, go WAITDO or DO */
+ multistate(data, MSTATE_DO);
+ rc = CURLM_CALL_MULTI_PERFORM;
+ }
+ else {
+ /* failure detected */
+ Curl_posttransfer(data);
+ multi_done(data, result, TRUE);
+ stream_error = TRUE;
+ }
+ break;
+
+ case MSTATE_PROTOCONNECTING:
+ /* protocol-specific connect phase */
+ result = protocol_connecting(data, &protocol_connected);
+ if(!result && protocol_connected) {
+ /* after the connect has completed, go WAITDO or DO */
+ multistate(data, MSTATE_DO);
+ rc = CURLM_CALL_MULTI_PERFORM;
+ }
+ else if(result) {
+ /* failure detected */
+ Curl_posttransfer(data);
+ multi_done(data, result, TRUE);
+ stream_error = TRUE;
+ }
+ break;
+
+ case MSTATE_DO:
+ if(data->set.fprereq) {
+ int prereq_rc;
+
+ /* call the prerequest callback function */
+ Curl_set_in_callback(data, true);
+ prereq_rc = data->set.fprereq(data->set.prereq_userp,
+ data->info.conn_primary_ip,
+ data->info.conn_local_ip,
+ data->info.conn_primary_port,
+ data->info.conn_local_port);
+ Curl_set_in_callback(data, false);
+ if(prereq_rc != CURL_PREREQFUNC_OK) {
+ failf(data, "operation aborted by pre-request callback");
+ /* failure in pre-request callback - don't do any other processing */
+ result = CURLE_ABORTED_BY_CALLBACK;
+ Curl_posttransfer(data);
+ multi_done(data, result, FALSE);
+ stream_error = TRUE;
+ break;
+ }
+ }
+
+ if(data->set.connect_only == 1) {
+ /* keep connection open for application to use the socket */
+ connkeep(data->conn, "CONNECT_ONLY");
+ multistate(data, MSTATE_DONE);
+ result = CURLE_OK;
+ rc = CURLM_CALL_MULTI_PERFORM;
+ }
+ else {
+ /* Perform the protocol's DO action */
+ result = multi_do(data, &dophase_done);
+
+ /* When multi_do() returns failure, data->conn might be NULL! */
+
+ if(!result) {
+ if(!dophase_done) {
+#ifndef CURL_DISABLE_FTP
+ /* some steps needed for wildcard matching */
+ if(data->state.wildcardmatch) {
+ struct WildcardData *wc = &data->wildcard;
+ if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) {
+ /* skip some states if it is important */
+ multi_done(data, CURLE_OK, FALSE);
+
+ /* if there's no connection left, skip the DONE state */
+ multistate(data, data->conn ?
+ MSTATE_DONE : MSTATE_COMPLETED);
+ rc = CURLM_CALL_MULTI_PERFORM;
+ break;
+ }
+ }
+#endif
+ /* DO was not completed in one function call, we must continue
+ DOING... */
+ multistate(data, MSTATE_DOING);
+ rc = CURLM_OK;
+ }
+
+ /* after DO, go DO_DONE... or DO_MORE */
+ else if(data->conn->bits.do_more) {
+ /* we're supposed to do more, but we need to sit down, relax
+ and wait a little while first */
+ multistate(data, MSTATE_DOING_MORE);
+ rc = CURLM_OK;
+ }
+ else {
+ /* we're done with the DO, now DID */
+ multistate(data, MSTATE_DID);
+ rc = CURLM_CALL_MULTI_PERFORM;
+ }
+ }
+ else if((CURLE_SEND_ERROR == result) &&
+ data->conn->bits.reuse) {
+ /*
+ * In this situation, a connection that we were trying to use
+ * may have unexpectedly died. If possible, send the connection
+ * back to the CONNECT phase so we can try again.
+ */
+ char *newurl = NULL;
+ followtype follow = FOLLOW_NONE;
+ CURLcode drc;
+
+ drc = Curl_retry_request(data, &newurl);
+ if(drc) {
+ /* a failure here pretty much implies an out of memory */
+ result = drc;
+ stream_error = TRUE;
+ }
+
+ Curl_posttransfer(data);
+ drc = multi_done(data, result, FALSE);
+
+ /* When set to retry the connection, we must go back to the CONNECT
+ * state */
+ if(newurl) {
+ if(!drc || (drc == CURLE_SEND_ERROR)) {
+ follow = FOLLOW_RETRY;
+ drc = Curl_follow(data, newurl, follow);
+ if(!drc) {
+ multistate(data, MSTATE_CONNECT);
+ rc = CURLM_CALL_MULTI_PERFORM;
+ result = CURLE_OK;
+ }
+ else {
+ /* Follow failed */
+ result = drc;
+ }
+ }
+ else {
+ /* done didn't return OK or SEND_ERROR */
+ result = drc;
+ }
+ }
+ else {
+ /* Have error handler disconnect conn if we can't retry */
+ stream_error = TRUE;
+ }
+ free(newurl);
+ }
+ else {
+ /* failure detected */
+ Curl_posttransfer(data);
+ if(data->conn)
+ multi_done(data, result, FALSE);
+ stream_error = TRUE;
+ }
+ }
+ break;
+
+ case MSTATE_DOING:
+ /* we continue DOING until the DO phase is complete */
+ DEBUGASSERT(data->conn);
+ result = protocol_doing(data, &dophase_done);
+ if(!result) {
+ if(dophase_done) {
+ /* after DO, go DO_DONE or DO_MORE */
+ multistate(data, data->conn->bits.do_more?
+ MSTATE_DOING_MORE : MSTATE_DID);
+ rc = CURLM_CALL_MULTI_PERFORM;
+ } /* dophase_done */
+ }
+ else {
+ /* failure detected */
+ Curl_posttransfer(data);
+ multi_done(data, result, FALSE);
+ stream_error = TRUE;
+ }
+ break;
+
+ case MSTATE_DOING_MORE:
+ /*
+ * When we are connected, DOING MORE and then go DID
+ */
+ DEBUGASSERT(data->conn);
+ result = multi_do_more(data, &control);
+
+ if(!result) {
+ if(control) {
+ /* if positive, advance to DO_DONE
+ if negative, go back to DOING */
+ multistate(data, control == 1?
+ MSTATE_DID : MSTATE_DOING);
+ rc = CURLM_CALL_MULTI_PERFORM;
+ }
+ else
+ /* stay in DO_MORE */
+ rc = CURLM_OK;
+ }
+ else {
+ /* failure detected */
+ Curl_posttransfer(data);
+ multi_done(data, result, FALSE);
+ stream_error = TRUE;
+ }
+ break;
+
+ case MSTATE_DID:
+ DEBUGASSERT(data->conn);
+ if(data->conn->bits.multiplex)
+ /* Check if we can move pending requests to send pipe */
+ process_pending_handles(multi); /* multiplexed */
+
+ /* Only perform the transfer if there's a good socket to work with.
+ Having both BAD is a signal to skip immediately to DONE */
+ if((data->conn->sockfd != CURL_SOCKET_BAD) ||
+ (data->conn->writesockfd != CURL_SOCKET_BAD))
+ multistate(data, MSTATE_PERFORMING);
+ else {
+#ifndef CURL_DISABLE_FTP
+ if(data->state.wildcardmatch &&
+ ((data->conn->handler->flags & PROTOPT_WILDCARD) == 0)) {
+ data->wildcard.state = CURLWC_DONE;
+ }
+#endif
+ multistate(data, MSTATE_DONE);
+ }
+ rc = CURLM_CALL_MULTI_PERFORM;
+ break;
+
+ case MSTATE_RATELIMITING: /* limit-rate exceeded in either direction */
+ DEBUGASSERT(data->conn);
+ /* if both rates are within spec, resume transfer */
+ if(Curl_pgrsUpdate(data))
+ result = CURLE_ABORTED_BY_CALLBACK;
+ else
+ result = Curl_speedcheck(data, *nowp);
+
+ if(result) {
+ if(!(data->conn->handler->flags & PROTOPT_DUAL) &&
+ result != CURLE_HTTP2_STREAM)
+ streamclose(data->conn, "Transfer returned error");
+
+ Curl_posttransfer(data);
+ multi_done(data, result, TRUE);
+ }
+ else {
+ send_timeout_ms = 0;
+ if(data->set.max_send_speed)
+ send_timeout_ms =
+ Curl_pgrsLimitWaitTime(data->progress.uploaded,
+ data->progress.ul_limit_size,
+ data->set.max_send_speed,
+ data->progress.ul_limit_start,
+ *nowp);
+
+ recv_timeout_ms = 0;
+ if(data->set.max_recv_speed)
+ recv_timeout_ms =
+ Curl_pgrsLimitWaitTime(data->progress.downloaded,
+ data->progress.dl_limit_size,
+ data->set.max_recv_speed,
+ data->progress.dl_limit_start,
+ *nowp);
+
+ if(!send_timeout_ms && !recv_timeout_ms) {
+ multistate(data, MSTATE_PERFORMING);
+ Curl_ratelimit(data, *nowp);
+ }
+ else if(send_timeout_ms >= recv_timeout_ms)
+ Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST);
+ else
+ Curl_expire(data, recv_timeout_ms, EXPIRE_TOOFAST);
+ }
+ break;
+
+ case MSTATE_PERFORMING:
+ {
+ char *newurl = NULL;
+ bool retry = FALSE;
+ bool comeback = FALSE;
+ DEBUGASSERT(data->state.buffer);
+ /* check if over send speed */
+ send_timeout_ms = 0;
+ if(data->set.max_send_speed)
+ send_timeout_ms = Curl_pgrsLimitWaitTime(data->progress.uploaded,
+ data->progress.ul_limit_size,
+ data->set.max_send_speed,
+ data->progress.ul_limit_start,
+ *nowp);
+
+ /* check if over recv speed */
+ recv_timeout_ms = 0;
+ if(data->set.max_recv_speed)
+ recv_timeout_ms = Curl_pgrsLimitWaitTime(data->progress.downloaded,
+ data->progress.dl_limit_size,
+ data->set.max_recv_speed,
+ data->progress.dl_limit_start,
+ *nowp);
+
+ if(send_timeout_ms || recv_timeout_ms) {
+ Curl_ratelimit(data, *nowp);
+ multistate(data, MSTATE_RATELIMITING);
+ if(send_timeout_ms >= recv_timeout_ms)
+ Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST);
+ else
+ Curl_expire(data, recv_timeout_ms, EXPIRE_TOOFAST);
+ break;
+ }
+
+ /* read/write data if it is ready to do so */
+ result = Curl_readwrite(data->conn, data, &done, &comeback);
+
+ if(done || (result == CURLE_RECV_ERROR)) {
+ /* If CURLE_RECV_ERROR happens early enough, we assume it was a race
+ * condition and the server closed the re-used connection exactly when
+ * we wanted to use it, so figure out if that is indeed the case.
+ */
+ CURLcode ret = Curl_retry_request(data, &newurl);
+ if(!ret)
+ retry = (newurl)?TRUE:FALSE;
+ else if(!result)
+ result = ret;
+
+ if(retry) {
+ /* if we are to retry, set the result to OK and consider the
+ request as done */
+ result = CURLE_OK;
+ done = TRUE;
+ }
+ }
+ else if((CURLE_HTTP2_STREAM == result) &&
+ Curl_h2_http_1_1_error(data)) {
+ CURLcode ret = Curl_retry_request(data, &newurl);
+
+ if(!ret) {
+ infof(data, "Downgrades to HTTP/1.1");
+ streamclose(data->conn, "Disconnect HTTP/2 for HTTP/1");
+ data->state.httpwant = CURL_HTTP_VERSION_1_1;
+ /* clear the error message bit too as we ignore the one we got */
+ data->state.errorbuf = FALSE;
+ if(!newurl)
+ /* typically for HTTP_1_1_REQUIRED error on first flight */
+ newurl = strdup(data->state.url);
+ /* if we are to retry, set the result to OK and consider the request
+ as done */
+ retry = TRUE;
+ result = CURLE_OK;
+ done = TRUE;
+ }
+ else
+ result = ret;
+ }
+
+ if(result) {
+ /*
+ * The transfer phase returned error, we mark the connection to get
+ * closed to prevent being re-used. This is because we can't possibly
+ * know if the connection is in a good shape or not now. Unless it is
+ * a protocol which uses two "channels" like FTP, as then the error
+ * happened in the data connection.
+ */
+
+ if(!(data->conn->handler->flags & PROTOPT_DUAL) &&
+ result != CURLE_HTTP2_STREAM)
+ streamclose(data->conn, "Transfer returned error");
+
+ Curl_posttransfer(data);
+ multi_done(data, result, TRUE);
+ }
+ else if(done) {
+
+ /* call this even if the readwrite function returned error */
+ Curl_posttransfer(data);
+
+ /* When we follow redirects or is set to retry the connection, we must
+ to go back to the CONNECT state */
+ if(data->req.newurl || retry) {
+ followtype follow = FOLLOW_NONE;
+ if(!retry) {
+ /* if the URL is a follow-location and not just a retried request
+ then figure out the URL here */
+ free(newurl);
+ newurl = data->req.newurl;
+ data->req.newurl = NULL;
+ follow = FOLLOW_REDIR;
+ }
+ else
+ follow = FOLLOW_RETRY;
+ (void)multi_done(data, CURLE_OK, FALSE);
+ /* multi_done() might return CURLE_GOT_NOTHING */
+ result = Curl_follow(data, newurl, follow);
+ if(!result) {
+ multistate(data, MSTATE_CONNECT);
+ rc = CURLM_CALL_MULTI_PERFORM;
+ }
+ free(newurl);
+ }
+ else {
+ /* after the transfer is done, go DONE */
+
+ /* but first check to see if we got a location info even though we're
+ not following redirects */
+ if(data->req.location) {
+ free(newurl);
+ newurl = data->req.location;
+ data->req.location = NULL;
+ result = Curl_follow(data, newurl, FOLLOW_FAKE);
+ free(newurl);
+ if(result) {
+ stream_error = TRUE;
+ result = multi_done(data, result, TRUE);
+ }
+ }
+
+ if(!result) {
+ multistate(data, MSTATE_DONE);
+ rc = CURLM_CALL_MULTI_PERFORM;
+ }
+ }
+ }
+ else if(comeback) {
+ /* This avoids CURLM_CALL_MULTI_PERFORM so that a very fast transfer
+ won't get stuck on this transfer at the expense of other concurrent
+ transfers */
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+ rc = CURLM_OK;
+ }
+ break;
+ }
+
+ case MSTATE_DONE:
+ /* this state is highly transient, so run another loop after this */
+ rc = CURLM_CALL_MULTI_PERFORM;
+
+ if(data->conn) {
+ CURLcode res;
+
+ if(data->conn->bits.multiplex)
+ /* Check if we can move pending requests to connection */
+ process_pending_handles(multi); /* multiplexing */
+
+ /* post-transfer command */
+ res = multi_done(data, result, FALSE);
+
+ /* allow a previously set error code take precedence */
+ if(!result)
+ result = res;
+ }
+
+#ifndef CURL_DISABLE_FTP
+ if(data->state.wildcardmatch) {
+ if(data->wildcard.state != CURLWC_DONE) {
+ /* if a wildcard is set and we are not ending -> lets start again
+ with MSTATE_INIT */
+ multistate(data, MSTATE_INIT);
+ break;
+ }
+ }
+#endif
+ /* after we have DONE what we're supposed to do, go COMPLETED, and
+ it doesn't matter what the multi_done() returned! */
+ multistate(data, MSTATE_COMPLETED);
+ break;
+
+ case MSTATE_COMPLETED:
+ break;
+
+ case MSTATE_MSGSENT:
+ data->result = result;
+ return CURLM_OK; /* do nothing */
+
+ default:
+ return CURLM_INTERNAL_ERROR;
+ }
+
+ if(data->conn &&
+ data->mstate >= MSTATE_CONNECT &&
+ data->mstate < MSTATE_DO &&
+ rc != CURLM_CALL_MULTI_PERFORM &&
+ !multi_ischanged(multi, false)) {
+ /* We now handle stream timeouts if and only if this will be the last
+ * loop iteration. We only check this on the last iteration to ensure
+ * that if we know we have additional work to do immediately
+ * (i.e. CURLM_CALL_MULTI_PERFORM == TRUE) then we should do that before
+ * declaring the connection timed out as we may almost have a completed
+ * connection. */
+ multi_handle_timeout(data, nowp, &stream_error, &result, TRUE);
+ }
+
+ statemachine_end:
+
+ if(data->mstate < MSTATE_COMPLETED) {
+ if(result) {
+ /*
+ * If an error was returned, and we aren't in completed state now,
+ * then we go to completed and consider this transfer aborted.
+ */
+
+ /* NOTE: no attempt to disconnect connections must be made
+ in the case blocks above - cleanup happens only here */
+
+ /* Check if we can move pending requests to send pipe */
+ process_pending_handles(multi); /* connection */
+
+ if(data->conn) {
+ if(stream_error) {
+ /* Don't attempt to send data over a connection that timed out */
+ bool dead_connection = result == CURLE_OPERATION_TIMEDOUT;
+ struct connectdata *conn = data->conn;
+
+ /* This is where we make sure that the conn pointer is reset.
+ We don't have to do this in every case block above where a
+ failure is detected */
+ Curl_detach_connection(data);
+
+ /* remove connection from cache */
+ Curl_conncache_remove_conn(data, conn, TRUE);
+
+ /* disconnect properly */
+ Curl_disconnect(data, conn, dead_connection);
+ }
+ }
+ else if(data->mstate == MSTATE_CONNECT) {
+ /* Curl_connect() failed */
+ (void)Curl_posttransfer(data);
+ }
+
+ multistate(data, MSTATE_COMPLETED);
+ rc = CURLM_CALL_MULTI_PERFORM;
+ }
+ /* if there's still a connection to use, call the progress function */
+ else if(data->conn && Curl_pgrsUpdate(data)) {
+ /* aborted due to progress callback return code must close the
+ connection */
+ result = CURLE_ABORTED_BY_CALLBACK;
+ streamclose(data->conn, "Aborted by callback");
+
+ /* if not yet in DONE state, go there, otherwise COMPLETED */
+ multistate(data, (data->mstate < MSTATE_DONE)?
+ MSTATE_DONE: MSTATE_COMPLETED);
+ rc = CURLM_CALL_MULTI_PERFORM;
+ }
+ }
+
+ if(MSTATE_COMPLETED == data->mstate) {
+ if(data->set.fmultidone) {
+ /* signal via callback instead */
+ data->set.fmultidone(data, result);
+ }
+ else {
+ /* now fill in the Curl_message with this info */
+ msg = &data->msg;
+
+ msg->extmsg.msg = CURLMSG_DONE;
+ msg->extmsg.easy_handle = data;
+ msg->extmsg.data.result = result;
+
+ rc = multi_addmsg(multi, msg);
+ DEBUGASSERT(!data->conn);
+ }
+ multistate(data, MSTATE_MSGSENT);
+ }
+ } while((rc == CURLM_CALL_MULTI_PERFORM) || multi_ischanged(multi, FALSE));
+
+ data->result = result;
+ return rc;
+}
+
+
+CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles)
+{
+ struct Curl_easy *data;
+ CURLMcode returncode = CURLM_OK;
+ struct Curl_tree *t;
+ struct curltime now = Curl_now();
+
+ if(!GOOD_MULTI_HANDLE(multi))
+ return CURLM_BAD_HANDLE;
+
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+
+ data = multi->easyp;
+ while(data) {
+ CURLMcode result;
+ SIGPIPE_VARIABLE(pipe_st);
+
+ sigpipe_ignore(data, &pipe_st);
+ result = multi_runsingle(multi, &now, data);
+ sigpipe_restore(&pipe_st);
+
+ if(result)
+ returncode = result;
+
+ data = data->next; /* operate on next handle */
+ }
+
+ /*
+ * Simply remove all expired timers from the splay since handles are dealt
+ * with unconditionally by this function and curl_multi_timeout() requires
+ * that already passed/handled expire times are removed from the splay.
+ *
+ * It is important that the 'now' value is set at the entry of this function
+ * and not for the current time as it may have ticked a little while since
+ * then and then we risk this loop to remove timers that actually have not
+ * been handled!
+ */
+ do {
+ multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
+ if(t)
+ /* the removed may have another timeout in queue */
+ (void)add_next_timeout(now, multi, t->payload);
+
+ } while(t);
+
+ *running_handles = multi->num_alive;
+
+ if(CURLM_OK >= returncode)
+ returncode = Curl_update_timer(multi);
+
+ return returncode;
+}
+
+CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
+{
+ struct Curl_easy *data;
+ struct Curl_easy *nextdata;
+
+ if(GOOD_MULTI_HANDLE(multi)) {
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+
+ multi->magic = 0; /* not good anymore */
+
+ /* First remove all remaining easy handles */
+ data = multi->easyp;
+ while(data) {
+ nextdata = data->next;
+ if(!data->state.done && data->conn)
+ /* if DONE was never called for this handle */
+ (void)multi_done(data, CURLE_OK, TRUE);
+ if(data->dns.hostcachetype == HCACHE_MULTI) {
+ /* clear out the usage of the shared DNS cache */
+ Curl_hostcache_clean(data, data->dns.hostcache);
+ data->dns.hostcache = NULL;
+ data->dns.hostcachetype = HCACHE_NONE;
+ }
+
+ /* Clear the pointer to the connection cache */
+ data->state.conn_cache = NULL;
+ data->multi = NULL; /* clear the association */
+
+#ifdef USE_LIBPSL
+ if(data->psl == &multi->psl)
+ data->psl = NULL;
+#endif
+
+ data = nextdata;
+ }
+
+ /* Close all the connections in the connection cache */
+ Curl_conncache_close_all_connections(&multi->conn_cache);
+
+ sockhash_destroy(&multi->sockhash);
+ Curl_conncache_destroy(&multi->conn_cache);
+ Curl_llist_destroy(&multi->msglist, NULL);
+ Curl_llist_destroy(&multi->pending, NULL);
+
+ Curl_hash_destroy(&multi->hostcache);
+ Curl_psl_destroy(&multi->psl);
+
+#ifdef USE_WINSOCK
+ WSACloseEvent(multi->wsa_event);
+#else
+#ifdef ENABLE_WAKEUP
+ wakeup_close(multi->wakeup_pair[0]);
+ wakeup_close(multi->wakeup_pair[1]);
+#endif
+#endif
+
+#ifdef USE_SSL
+ Curl_free_multi_ssl_backend_data(multi->ssl_backend_data);
+#endif
+
+ free(multi);
+
+ return CURLM_OK;
+ }
+ return CURLM_BAD_HANDLE;
+}
+
+/*
+ * curl_multi_info_read()
+ *
+ * This function is the primary way for a multi/multi_socket application to
+ * figure out if a transfer has ended. We MUST make this function as fast as
+ * possible as it will be polled frequently and we MUST NOT scan any lists in
+ * here to figure out things. We must scale fine to thousands of handles and
+ * beyond. The current design is fully O(1).
+ */
+
+CURLMsg *curl_multi_info_read(struct Curl_multi *multi, int *msgs_in_queue)
+{
+ struct Curl_message *msg;
+
+ *msgs_in_queue = 0; /* default to none */
+
+ if(GOOD_MULTI_HANDLE(multi) &&
+ !multi->in_callback &&
+ Curl_llist_count(&multi->msglist)) {
+ /* there is one or more messages in the list */
+ struct Curl_llist_element *e;
+
+ /* extract the head of the list to return */
+ e = multi->msglist.head;
+
+ msg = e->ptr;
+
+ /* remove the extracted entry */
+ Curl_llist_remove(&multi->msglist, e, NULL);
+
+ *msgs_in_queue = curlx_uztosi(Curl_llist_count(&multi->msglist));
+
+ return &msg->extmsg;
+ }
+ return NULL;
+}
+
+/*
+ * singlesocket() checks what sockets we deal with and their "action state"
+ * and if we have a different state in any of those sockets from last time we
+ * call the callback accordingly.
+ */
+static CURLMcode singlesocket(struct Curl_multi *multi,
+ struct Curl_easy *data)
+{
+ curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
+ int i;
+ struct Curl_sh_entry *entry;
+ curl_socket_t s;
+ int num;
+ unsigned int curraction;
+ unsigned char actions[MAX_SOCKSPEREASYHANDLE];
+ int rc;
+
+ for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++)
+ socks[i] = CURL_SOCKET_BAD;
+
+ /* Fill in the 'current' struct with the state as it is now: what sockets to
+ supervise and for what actions */
+ curraction = multi_getsock(data, socks);
+
+ /* We have 0 .. N sockets already and we get to know about the 0 .. M
+ sockets we should have from now on. Detect the differences, remove no
+ longer supervised ones and add new ones */
+
+ /* walk over the sockets we got right now */
+ for(i = 0; (i< MAX_SOCKSPEREASYHANDLE) &&
+ (curraction & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i)));
+ i++) {
+ unsigned char action = CURL_POLL_NONE;
+ unsigned char prevaction = 0;
+ int comboaction;
+ bool sincebefore = FALSE;
+
+ s = socks[i];
+
+ /* get it from the hash */
+ entry = sh_getentry(&multi->sockhash, s);
+
+ if(curraction & GETSOCK_READSOCK(i))
+ action |= CURL_POLL_IN;
+ if(curraction & GETSOCK_WRITESOCK(i))
+ action |= CURL_POLL_OUT;
+
+ actions[i] = action;
+ if(entry) {
+ /* check if new for this transfer */
+ int j;
+ for(j = 0; j< data->numsocks; j++) {
+ if(s == data->sockets[j]) {
+ prevaction = data->actions[j];
+ sincebefore = TRUE;
+ break;
+ }
+ }
+ }
+ else {
+ /* this is a socket we didn't have before, add it to the hash! */
+ entry = sh_addentry(&multi->sockhash, s);
+ if(!entry)
+ /* fatal */
+ return CURLM_OUT_OF_MEMORY;
+ }
+ if(sincebefore && (prevaction != action)) {
+ /* Socket was used already, but different action now */
+ if(prevaction & CURL_POLL_IN)
+ entry->readers--;
+ if(prevaction & CURL_POLL_OUT)
+ entry->writers--;
+ if(action & CURL_POLL_IN)
+ entry->readers++;
+ if(action & CURL_POLL_OUT)
+ entry->writers++;
+ }
+ else if(!sincebefore) {
+ /* a new user */
+ entry->users++;
+ if(action & CURL_POLL_IN)
+ entry->readers++;
+ if(action & CURL_POLL_OUT)
+ entry->writers++;
+
+ /* add 'data' to the transfer hash on this socket! */
+ if(!Curl_hash_add(&entry->transfers, (char *)&data, /* hash key */
+ sizeof(struct Curl_easy *), data)) {
+ Curl_hash_destroy(&entry->transfers);
+ return CURLM_OUT_OF_MEMORY;
+ }
+ }
+
+ comboaction = (entry->writers? CURL_POLL_OUT : 0) |
+ (entry->readers ? CURL_POLL_IN : 0);
+
+ /* socket existed before and has the same action set as before */
+ if(sincebefore && ((int)entry->action == comboaction))
+ /* same, continue */
+ continue;
+
+ if(multi->socket_cb) {
+ set_in_callback(multi, TRUE);
+ rc = multi->socket_cb(data, s, comboaction, multi->socket_userp,
+ entry->socketp);
+ set_in_callback(multi, FALSE);
+ if(rc == -1) {
+ multi->dead = TRUE;
+ return CURLM_ABORTED_BY_CALLBACK;
+ }
+ }
+
+ entry->action = comboaction; /* store the current action state */
+ }
+
+ num = i; /* number of sockets */
+
+ /* when we've walked over all the sockets we should have right now, we must
+ make sure to detect sockets that are removed */
+ for(i = 0; i< data->numsocks; i++) {
+ int j;
+ bool stillused = FALSE;
+ s = data->sockets[i];
+ for(j = 0; j < num; j++) {
+ if(s == socks[j]) {
+ /* this is still supervised */
+ stillused = TRUE;
+ break;
+ }
+ }
+ if(stillused)
+ continue;
+
+ entry = sh_getentry(&multi->sockhash, s);
+ /* if this is NULL here, the socket has been closed and notified so
+ already by Curl_multi_closed() */
+ if(entry) {
+ unsigned char oldactions = data->actions[i];
+ /* this socket has been removed. Decrease user count */
+ entry->users--;
+ if(oldactions & CURL_POLL_OUT)
+ entry->writers--;
+ if(oldactions & CURL_POLL_IN)
+ entry->readers--;
+ if(!entry->users) {
+ if(multi->socket_cb) {
+ set_in_callback(multi, TRUE);
+ rc = multi->socket_cb(data, s, CURL_POLL_REMOVE,
+ multi->socket_userp, entry->socketp);
+ set_in_callback(multi, FALSE);
+ if(rc == -1) {
+ multi->dead = TRUE;
+ return CURLM_ABORTED_BY_CALLBACK;
+ }
+ }
+ sh_delentry(entry, &multi->sockhash, s);
+ }
+ else {
+ /* still users, but remove this handle as a user of this socket */
+ if(Curl_hash_delete(&entry->transfers, (char *)&data,
+ sizeof(struct Curl_easy *))) {
+ DEBUGASSERT(NULL);
+ }
+ }
+ }
+ } /* for loop over numsocks */
+
+ memcpy(data->sockets, socks, num*sizeof(curl_socket_t));
+ memcpy(data->actions, actions, num*sizeof(char));
+ data->numsocks = num;
+ return CURLM_OK;
+}
+
+CURLcode Curl_updatesocket(struct Curl_easy *data)
+{
+ if(singlesocket(data->multi, data))
+ return CURLE_ABORTED_BY_CALLBACK;
+ return CURLE_OK;
+}
+
+
+/*
+ * Curl_multi_closed()
+ *
+ * Used by the connect code to tell the multi_socket code that one of the
+ * sockets we were using is about to be closed. This function will then
+ * remove it from the sockethash for this handle to make the multi_socket API
+ * behave properly, especially for the case when libcurl will create another
+ * socket again and it gets the same file descriptor number.
+ */
+
+void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s)
+{
+ if(data) {
+ /* if there's still an easy handle associated with this connection */
+ struct Curl_multi *multi = data->multi;
+ if(multi) {
+ /* this is set if this connection is part of a handle that is added to
+ a multi handle, and only then this is necessary */
+ struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
+
+ if(entry) {
+ int rc = 0;
+ if(multi->socket_cb) {
+ set_in_callback(multi, TRUE);
+ rc = multi->socket_cb(data, s, CURL_POLL_REMOVE,
+ multi->socket_userp, entry->socketp);
+ set_in_callback(multi, FALSE);
+ }
+
+ /* now remove it from the socket hash */
+ sh_delentry(entry, &multi->sockhash, s);
+ if(rc == -1)
+ /* This just marks the multi handle as "dead" without returning an
+ error code primarily because this function is used from many
+ places where propagating an error back is tricky. */
+ multi->dead = TRUE;
+ }
+ }
+ }
+}
+
+/*
+ * add_next_timeout()
+ *
+ * Each Curl_easy has a list of timeouts. The add_next_timeout() is called
+ * when it has just been removed from the splay tree because the timeout has
+ * expired. This function is then to advance in the list to pick the next
+ * timeout to use (skip the already expired ones) and add this node back to
+ * the splay tree again.
+ *
+ * The splay tree only has each sessionhandle as a single node and the nearest
+ * timeout is used to sort it on.
+ */
+static CURLMcode add_next_timeout(struct curltime now,
+ struct Curl_multi *multi,
+ struct Curl_easy *d)
+{
+ struct curltime *tv = &d->state.expiretime;
+ struct Curl_llist *list = &d->state.timeoutlist;
+ struct Curl_llist_element *e;
+ struct time_node *node = NULL;
+
+ /* move over the timeout list for this specific handle and remove all
+ timeouts that are now passed tense and store the next pending
+ timeout in *tv */
+ for(e = list->head; e;) {
+ struct Curl_llist_element *n = e->next;
+ timediff_t diff;
+ node = (struct time_node *)e->ptr;
+ diff = Curl_timediff(node->time, now);
+ if(diff <= 0)
+ /* remove outdated entry */
+ Curl_llist_remove(list, e, NULL);
+ else
+ /* the list is sorted so get out on the first mismatch */
+ break;
+ e = n;
+ }
+ e = list->head;
+ if(!e) {
+ /* clear the expire times within the handles that we remove from the
+ splay tree */
+ tv->tv_sec = 0;
+ tv->tv_usec = 0;
+ }
+ else {
+ /* copy the first entry to 'tv' */
+ memcpy(tv, &node->time, sizeof(*tv));
+
+ /* Insert this node again into the splay. Keep the timer in the list in
+ case we need to recompute future timers. */
+ multi->timetree = Curl_splayinsert(*tv, multi->timetree,
+ &d->state.timenode);
+ }
+ return CURLM_OK;
+}
+
+static CURLMcode multi_socket(struct Curl_multi *multi,
+ bool checkall,
+ curl_socket_t s,
+ int ev_bitmask,
+ int *running_handles)
+{
+ CURLMcode result = CURLM_OK;
+ struct Curl_easy *data = NULL;
+ struct Curl_tree *t;
+ struct curltime now = Curl_now();
+
+ if(checkall) {
+ /* *perform() deals with running_handles on its own */
+ result = curl_multi_perform(multi, running_handles);
+
+ /* walk through each easy handle and do the socket state change magic
+ and callbacks */
+ if(result != CURLM_BAD_HANDLE) {
+ data = multi->easyp;
+ while(data && !result) {
+ result = singlesocket(multi, data);
+ data = data->next;
+ }
+ }
+
+ /* or should we fall-through and do the timer-based stuff? */
+ return result;
+ }
+ if(s != CURL_SOCKET_TIMEOUT) {
+ struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
+
+ if(!entry)
+ /* Unmatched socket, we can't act on it but we ignore this fact. In
+ real-world tests it has been proved that libevent can in fact give
+ the application actions even though the socket was just previously
+ asked to get removed, so thus we better survive stray socket actions
+ and just move on. */
+ ;
+ else {
+ struct Curl_hash_iterator iter;
+ struct Curl_hash_element *he;
+
+ /* the socket can be shared by many transfers, iterate */
+ Curl_hash_start_iterate(&entry->transfers, &iter);
+ for(he = Curl_hash_next_element(&iter); he;
+ he = Curl_hash_next_element(&iter)) {
+ data = (struct Curl_easy *)he->ptr;
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->magic == CURLEASY_MAGIC_NUMBER);
+
+ if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK))
+ /* set socket event bitmask if they're not locked */
+ data->conn->cselect_bits = ev_bitmask;
+
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+ }
+
+ /* Now we fall-through and do the timer-based stuff, since we don't want
+ to force the user to have to deal with timeouts as long as at least
+ one connection in fact has traffic. */
+
+ data = NULL; /* set data to NULL again to avoid calling
+ multi_runsingle() in case there's no need to */
+ now = Curl_now(); /* get a newer time since the multi_runsingle() loop
+ may have taken some time */
+ }
+ }
+ else {
+ /* Asked to run due to time-out. Clear the 'lastcall' variable to force
+ Curl_update_timer() to trigger a callback to the app again even if the
+ same timeout is still the one to run after this call. That handles the
+ case when the application asks libcurl to run the timeout
+ prematurely. */
+ memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall));
+ }
+
+ /*
+ * The loop following here will go on as long as there are expire-times left
+ * to process in the splay and 'data' will be re-assigned for every expired
+ * handle we deal with.
+ */
+ do {
+ /* the first loop lap 'data' can be NULL */
+ if(data) {
+ SIGPIPE_VARIABLE(pipe_st);
+
+ sigpipe_ignore(data, &pipe_st);
+ result = multi_runsingle(multi, &now, data);
+ sigpipe_restore(&pipe_st);
+
+ if(CURLM_OK >= result) {
+ /* get the socket(s) and check if the state has been changed since
+ last */
+ result = singlesocket(multi, data);
+ if(result)
+ return result;
+ }
+ }
+
+ /* Check if there's one (more) expired timer to deal with! This function
+ extracts a matching node if there is one */
+
+ multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
+ if(t) {
+ data = t->payload; /* assign this for next loop */
+ (void)add_next_timeout(now, multi, t->payload);
+ }
+
+ } while(t);
+
+ *running_handles = multi->num_alive;
+ return result;
+}
+
+#undef curl_multi_setopt
+CURLMcode curl_multi_setopt(struct Curl_multi *multi,
+ CURLMoption option, ...)
+{
+ CURLMcode res = CURLM_OK;
+ va_list param;
+
+ if(!GOOD_MULTI_HANDLE(multi))
+ return CURLM_BAD_HANDLE;
+
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+
+ va_start(param, option);
+
+ switch(option) {
+ case CURLMOPT_SOCKETFUNCTION:
+ multi->socket_cb = va_arg(param, curl_socket_callback);
+ break;
+ case CURLMOPT_SOCKETDATA:
+ multi->socket_userp = va_arg(param, void *);
+ break;
+ case CURLMOPT_PUSHFUNCTION:
+ multi->push_cb = va_arg(param, curl_push_callback);
+ break;
+ case CURLMOPT_PUSHDATA:
+ multi->push_userp = va_arg(param, void *);
+ break;
+ case CURLMOPT_PIPELINING:
+ multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX;
+ break;
+ case CURLMOPT_TIMERFUNCTION:
+ multi->timer_cb = va_arg(param, curl_multi_timer_callback);
+ break;
+ case CURLMOPT_TIMERDATA:
+ multi->timer_userp = va_arg(param, void *);
+ break;
+ case CURLMOPT_MAXCONNECTS:
+ multi->maxconnects = va_arg(param, long);
+ break;
+ case CURLMOPT_MAX_HOST_CONNECTIONS:
+ multi->max_host_connections = va_arg(param, long);
+ break;
+ case CURLMOPT_MAX_TOTAL_CONNECTIONS:
+ multi->max_total_connections = va_arg(param, long);
+ break;
+ /* options formerly used for pipelining */
+ case CURLMOPT_MAX_PIPELINE_LENGTH:
+ break;
+ case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE:
+ break;
+ case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE:
+ break;
+ case CURLMOPT_PIPELINING_SITE_BL:
+ break;
+ case CURLMOPT_PIPELINING_SERVER_BL:
+ break;
+ case CURLMOPT_MAX_CONCURRENT_STREAMS:
+ {
+ long streams = va_arg(param, long);
+ if(streams < 1)
+ streams = 100;
+ multi->max_concurrent_streams = curlx_sltoui(streams);
+ }
+ break;
+ default:
+ res = CURLM_UNKNOWN_OPTION;
+ break;
+ }
+ va_end(param);
+ return res;
+}
+
+/* we define curl_multi_socket() in the public multi.h header */
+#undef curl_multi_socket
+
+CURLMcode curl_multi_socket(struct Curl_multi *multi, curl_socket_t s,
+ int *running_handles)
+{
+ CURLMcode result;
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+ result = multi_socket(multi, FALSE, s, 0, running_handles);
+ if(CURLM_OK >= result)
+ result = Curl_update_timer(multi);
+ return result;
+}
+
+CURLMcode curl_multi_socket_action(struct Curl_multi *multi, curl_socket_t s,
+ int ev_bitmask, int *running_handles)
+{
+ CURLMcode result;
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+ result = multi_socket(multi, FALSE, s, ev_bitmask, running_handles);
+ if(CURLM_OK >= result)
+ result = Curl_update_timer(multi);
+ return result;
+}
+
+CURLMcode curl_multi_socket_all(struct Curl_multi *multi, int *running_handles)
+{
+ CURLMcode result;
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+ result = multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles);
+ if(CURLM_OK >= result)
+ result = Curl_update_timer(multi);
+ return result;
+}
+
+static CURLMcode multi_timeout(struct Curl_multi *multi,
+ long *timeout_ms)
+{
+ static const struct curltime tv_zero = {0, 0};
+
+ if(multi->dead) {
+ *timeout_ms = 0;
+ return CURLM_OK;
+ }
+
+ if(multi->timetree) {
+ /* we have a tree of expire times */
+ struct curltime now = Curl_now();
+
+ /* splay the lowest to the bottom */
+ multi->timetree = Curl_splay(tv_zero, multi->timetree);
+
+ if(Curl_splaycomparekeys(multi->timetree->key, now) > 0) {
+ /* some time left before expiration */
+ timediff_t diff = Curl_timediff(multi->timetree->key, now);
+ if(diff <= 0)
+ /*
+ * Since we only provide millisecond resolution on the returned value
+ * and the diff might be less than one millisecond here, we don't
+ * return zero as that may cause short bursts of busyloops on fast
+ * processors while the diff is still present but less than one
+ * millisecond! instead we return 1 until the time is ripe.
+ */
+ *timeout_ms = 1;
+ else
+ /* this should be safe even on 64 bit archs, as we don't use that
+ overly long timeouts */
+ *timeout_ms = (long)diff;
+ }
+ else
+ /* 0 means immediately */
+ *timeout_ms = 0;
+ }
+ else
+ *timeout_ms = -1;
+
+ return CURLM_OK;
+}
+
+CURLMcode curl_multi_timeout(struct Curl_multi *multi,
+ long *timeout_ms)
+{
+ /* First, make some basic checks that the CURLM handle is a good handle */
+ if(!GOOD_MULTI_HANDLE(multi))
+ return CURLM_BAD_HANDLE;
+
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+
+ return multi_timeout(multi, timeout_ms);
+}
+
+/*
+ * Tell the application it should update its timers, if it subscribes to the
+ * update timer callback.
+ */
+CURLMcode Curl_update_timer(struct Curl_multi *multi)
+{
+ long timeout_ms;
+ int rc;
+
+ if(!multi->timer_cb || multi->dead)
+ return CURLM_OK;
+ if(multi_timeout(multi, &timeout_ms)) {
+ return CURLM_OK;
+ }
+ if(timeout_ms < 0) {
+ static const struct curltime none = {0, 0};
+ if(Curl_splaycomparekeys(none, multi->timer_lastcall)) {
+ multi->timer_lastcall = none;
+ /* there's no timeout now but there was one previously, tell the app to
+ disable it */
+ set_in_callback(multi, TRUE);
+ rc = multi->timer_cb(multi, -1, multi->timer_userp);
+ set_in_callback(multi, FALSE);
+ if(rc == -1) {
+ multi->dead = TRUE;
+ return CURLM_ABORTED_BY_CALLBACK;
+ }
+ return CURLM_OK;
+ }
+ return CURLM_OK;
+ }
+
+ /* When multi_timeout() is done, multi->timetree points to the node with the
+ * timeout we got the (relative) time-out time for. We can thus easily check
+ * if this is the same (fixed) time as we got in a previous call and then
+ * avoid calling the callback again. */
+ if(Curl_splaycomparekeys(multi->timetree->key, multi->timer_lastcall) == 0)
+ return CURLM_OK;
+
+ multi->timer_lastcall = multi->timetree->key;
+
+ set_in_callback(multi, TRUE);
+ rc = multi->timer_cb(multi, timeout_ms, multi->timer_userp);
+ set_in_callback(multi, FALSE);
+ if(rc == -1) {
+ multi->dead = TRUE;
+ return CURLM_ABORTED_BY_CALLBACK;
+ }
+ return CURLM_OK;
+}
+
+/*
+ * multi_deltimeout()
+ *
+ * Remove a given timestamp from the list of timeouts.
+ */
+static void
+multi_deltimeout(struct Curl_easy *data, expire_id eid)
+{
+ struct Curl_llist_element *e;
+ struct Curl_llist *timeoutlist = &data->state.timeoutlist;
+ /* find and remove the specific node from the list */
+ for(e = timeoutlist->head; e; e = e->next) {
+ struct time_node *n = (struct time_node *)e->ptr;
+ if(n->eid == eid) {
+ Curl_llist_remove(timeoutlist, e, NULL);
+ return;
+ }
+ }
+}
+
+/*
+ * multi_addtimeout()
+ *
+ * Add a timestamp to the list of timeouts. Keep the list sorted so that head
+ * of list is always the timeout nearest in time.
+ *
+ */
+static CURLMcode
+multi_addtimeout(struct Curl_easy *data,
+ struct curltime *stamp,
+ expire_id eid)
+{
+ struct Curl_llist_element *e;
+ struct time_node *node;
+ struct Curl_llist_element *prev = NULL;
+ size_t n;
+ struct Curl_llist *timeoutlist = &data->state.timeoutlist;
+
+ node = &data->state.expires[eid];
+
+ /* copy the timestamp and id */
+ memcpy(&node->time, stamp, sizeof(*stamp));
+ node->eid = eid; /* also marks it as in use */
+
+ n = Curl_llist_count(timeoutlist);
+ if(n) {
+ /* find the correct spot in the list */
+ for(e = timeoutlist->head; e; e = e->next) {
+ struct time_node *check = (struct time_node *)e->ptr;
+ timediff_t diff = Curl_timediff(check->time, node->time);
+ if(diff > 0)
+ break;
+ prev = e;
+ }
+
+ }
+ /* else
+ this is the first timeout on the list */
+
+ Curl_llist_insert_next(timeoutlist, prev, node, &node->list);
+ return CURLM_OK;
+}
+
+/*
+ * Curl_expire()
+ *
+ * given a number of milliseconds from now to use to set the 'act before
+ * this'-time for the transfer, to be extracted by curl_multi_timeout()
+ *
+ * The timeout will be added to a queue of timeouts if it defines a moment in
+ * time that is later than the current head of queue.
+ *
+ * Expire replaces a former timeout using the same id if already set.
+ */
+void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id)
+{
+ struct Curl_multi *multi = data->multi;
+ struct curltime *nowp = &data->state.expiretime;
+ struct curltime set;
+
+ /* this is only interesting while there is still an associated multi struct
+ remaining! */
+ if(!multi)
+ return;
+
+ DEBUGASSERT(id < EXPIRE_LAST);
+
+ set = Curl_now();
+ set.tv_sec += (time_t)(milli/1000); /* might be a 64 to 32 bit conversion */
+ set.tv_usec += (unsigned int)(milli%1000)*1000;
+
+ if(set.tv_usec >= 1000000) {
+ set.tv_sec++;
+ set.tv_usec -= 1000000;
+ }
+
+ /* Remove any timer with the same id just in case. */
+ multi_deltimeout(data, id);
+
+ /* Add it to the timer list. It must stay in the list until it has expired
+ in case we need to recompute the minimum timer later. */
+ multi_addtimeout(data, &set, id);
+
+ if(nowp->tv_sec || nowp->tv_usec) {
+ /* This means that the struct is added as a node in the splay tree.
+ Compare if the new time is earlier, and only remove-old/add-new if it
+ is. */
+ timediff_t diff = Curl_timediff(set, *nowp);
+ int rc;
+
+ if(diff > 0) {
+ /* The current splay tree entry is sooner than this new expiry time.
+ We don't need to update our splay tree entry. */
+ return;
+ }
+
+ /* Since this is an updated time, we must remove the previous entry from
+ the splay tree first and then re-add the new value */
+ rc = Curl_splayremove(multi->timetree, &data->state.timenode,
+ &multi->timetree);
+ if(rc)
+ infof(data, "Internal error removing splay node = %d", rc);
+ }
+
+ /* Indicate that we are in the splay tree and insert the new timer expiry
+ value since it is our local minimum. */
+ *nowp = set;
+ data->state.timenode.payload = data;
+ multi->timetree = Curl_splayinsert(*nowp, multi->timetree,
+ &data->state.timenode);
+}
+
+/*
+ * Curl_expire_done()
+ *
+ * Removes the expire timer. Marks it as done.
+ *
+ */
+void Curl_expire_done(struct Curl_easy *data, expire_id id)
+{
+ /* remove the timer, if there */
+ multi_deltimeout(data, id);
+}
+
+/*
+ * Curl_expire_clear()
+ *
+ * Clear ALL timeout values for this handle.
+ */
+void Curl_expire_clear(struct Curl_easy *data)
+{
+ struct Curl_multi *multi = data->multi;
+ struct curltime *nowp = &data->state.expiretime;
+
+ /* this is only interesting while there is still an associated multi struct
+ remaining! */
+ if(!multi)
+ return;
+
+ if(nowp->tv_sec || nowp->tv_usec) {
+ /* Since this is an cleared time, we must remove the previous entry from
+ the splay tree */
+ struct Curl_llist *list = &data->state.timeoutlist;
+ int rc;
+
+ rc = Curl_splayremove(multi->timetree, &data->state.timenode,
+ &multi->timetree);
+ if(rc)
+ infof(data, "Internal error clearing splay node = %d", rc);
+
+ /* flush the timeout list too */
+ while(list->size > 0) {
+ Curl_llist_remove(list, list->tail, NULL);
+ }
+
+#ifdef DEBUGBUILD
+ infof(data, "Expire cleared (transfer %p)", data);
+#endif
+ nowp->tv_sec = 0;
+ nowp->tv_usec = 0;
+ }
+}
+
+
+
+
+CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s,
+ void *hashp)
+{
+ struct Curl_sh_entry *there = NULL;
+
+ there = sh_getentry(&multi->sockhash, s);
+
+ if(!there)
+ return CURLM_BAD_SOCKET;
+
+ there->socketp = hashp;
+
+ return CURLM_OK;
+}
+
+size_t Curl_multi_max_host_connections(struct Curl_multi *multi)
+{
+ return multi ? multi->max_host_connections : 0;
+}
+
+size_t Curl_multi_max_total_connections(struct Curl_multi *multi)
+{
+ return multi ? multi->max_total_connections : 0;
+}
+
+/*
+ * When information about a connection has appeared, call this!
+ */
+
+void Curl_multiuse_state(struct Curl_easy *data,
+ int bundlestate) /* use BUNDLE_* defines */
+{
+ struct connectdata *conn;
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->multi);
+ conn = data->conn;
+ DEBUGASSERT(conn);
+ DEBUGASSERT(conn->bundle);
+
+ conn->bundle->multiuse = bundlestate;
+ process_pending_handles(data->multi);
+}
+
+static void process_pending_handles(struct Curl_multi *multi)
+{
+ struct Curl_llist_element *e = multi->pending.head;
+ if(e) {
+ struct Curl_easy *data = e->ptr;
+
+ DEBUGASSERT(data->mstate == MSTATE_PENDING);
+
+ multistate(data, MSTATE_CONNECT);
+
+ /* Remove this node from the list */
+ Curl_llist_remove(&multi->pending, e, NULL);
+
+ /* Make sure that the handle will be processed soonish. */
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+
+ /* mark this as having been in the pending queue */
+ data->state.previouslypending = TRUE;
+ }
+}
+
+void Curl_set_in_callback(struct Curl_easy *data, bool value)
+{
+ /* might get called when there is no data pointer! */
+ if(data) {
+ if(data->multi_easy)
+ data->multi_easy->in_callback = value;
+ else if(data->multi)
+ data->multi->in_callback = value;
+ }
+}
+
+bool Curl_is_in_callback(struct Curl_easy *easy)
+{
+ return ((easy->multi && easy->multi->in_callback) ||
+ (easy->multi_easy && easy->multi_easy->in_callback));
+}
+
+#ifdef DEBUGBUILD
+void Curl_multi_dump(struct Curl_multi *multi)
+{
+ struct Curl_easy *data;
+ int i;
+ fprintf(stderr, "* Multi status: %d handles, %d alive\n",
+ multi->num_easy, multi->num_alive);
+ for(data = multi->easyp; data; data = data->next) {
+ if(data->mstate < MSTATE_COMPLETED) {
+ /* only display handles that are not completed */
+ fprintf(stderr, "handle %p, state %s, %d sockets\n",
+ (void *)data,
+ statename[data->mstate], data->numsocks);
+ for(i = 0; i < data->numsocks; i++) {
+ curl_socket_t s = data->sockets[i];
+ struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
+
+ fprintf(stderr, "%d ", (int)s);
+ if(!entry) {
+ fprintf(stderr, "INTERNAL CONFUSION\n");
+ continue;
+ }
+ fprintf(stderr, "[%s %s] ",
+ (entry->action&CURL_POLL_IN)?"RECVING":"",
+ (entry->action&CURL_POLL_OUT)?"SENDING":"");
+ }
+ if(data->numsocks)
+ fprintf(stderr, "\n");
+ }
+ }
+}
+#endif
+
+unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi)
+{
+ DEBUGASSERT(multi);
+ return multi->max_concurrent_streams;
+}
diff --git a/libs/libcurl/src/multihandle.h b/libs/libcurl/src/multihandle.h index a997784ea3..c923879b16 100644 --- a/libs/libcurl/src/multihandle.h +++ b/libs/libcurl/src/multihandle.h @@ -1,167 +1,175 @@ -#ifndef HEADER_CURL_MULTIHANDLE_H -#define HEADER_CURL_MULTIHANDLE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "llist.h" -#include "hash.h" -#include "conncache.h" -#include "psl.h" -#include "socketpair.h" - -struct connectdata; - -struct Curl_message { - struct Curl_llist_element list; - /* the 'CURLMsg' is the part that is visible to the external user */ - struct CURLMsg extmsg; -}; - -/* NOTE: if you add a state here, add the name to the statename[] array as - well! -*/ -typedef enum { - MSTATE_INIT, /* 0 - start in this state */ - MSTATE_PENDING, /* 1 - no connections, waiting for one */ - MSTATE_CONNECT, /* 2 - resolve/connect has been sent off */ - MSTATE_RESOLVING, /* 3 - awaiting the resolve to finalize */ - MSTATE_CONNECTING, /* 4 - awaiting the TCP connect to finalize */ - MSTATE_TUNNELING, /* 5 - awaiting HTTPS proxy SSL initialization to - complete and/or proxy CONNECT to finalize */ - MSTATE_PROTOCONNECT, /* 6 - initiate protocol connect procedure */ - MSTATE_PROTOCONNECTING, /* 7 - completing the protocol-specific connect - phase */ - MSTATE_DO, /* 8 - start send off the request (part 1) */ - MSTATE_DOING, /* 9 - sending off the request (part 1) */ - MSTATE_DOING_MORE, /* 10 - send off the request (part 2) */ - MSTATE_DID, /* 11 - done sending off request */ - MSTATE_PERFORMING, /* 12 - transfer data */ - MSTATE_RATELIMITING, /* 13 - wait because limit-rate exceeded */ - MSTATE_DONE, /* 14 - post data transfer operation */ - MSTATE_COMPLETED, /* 15 - operation complete */ - MSTATE_MSGSENT, /* 16 - the operation complete message is sent */ - MSTATE_LAST /* 17 - not a true state, never use this */ -} CURLMstate; - -/* we support N sockets per easy handle. Set the corresponding bit to what - action we should wait for */ -#define MAX_SOCKSPEREASYHANDLE 5 -#define GETSOCK_READABLE (0x00ff) -#define GETSOCK_WRITABLE (0xff00) - -#define CURLPIPE_ANY (CURLPIPE_MULTIPLEX) - -#if !defined(CURL_DISABLE_SOCKETPAIR) -#define ENABLE_WAKEUP -#endif - -/* value for MAXIMUM CONCURRENT STREAMS upper limit */ -#define INITIAL_MAX_CONCURRENT_STREAMS ((1U << 31) - 1) - -/* This is the struct known as CURLM on the outside */ -struct Curl_multi { - /* First a simple identifier to easier detect if a user mix up - this multi handle with an easy handle. Set this to CURL_MULTI_HANDLE. */ - unsigned int magic; - - /* We have a doubly-linked list with easy handles */ - struct Curl_easy *easyp; - struct Curl_easy *easylp; /* last node */ - - int num_easy; /* amount of entries in the linked list above. */ - int num_alive; /* amount of easy handles that are added but have not yet - reached COMPLETE state */ - - struct Curl_llist msglist; /* a list of messages from completed transfers */ - - struct Curl_llist pending; /* Curl_easys that are in the - MSTATE_PENDING state */ - - /* callback function and user data pointer for the *socket() API */ - curl_socket_callback socket_cb; - void *socket_userp; - - /* callback function and user data pointer for server push */ - curl_push_callback push_cb; - void *push_userp; - - /* Hostname cache */ - struct Curl_hash hostcache; - -#ifdef USE_LIBPSL - /* PSL cache. */ - struct PslCache psl; -#endif - - /* timetree points to the splay-tree of time nodes to figure out expire - times of all currently set timers */ - struct Curl_tree *timetree; - - /* 'sockhash' is the lookup hash for socket descriptor => easy handles (note - the pluralis form, there can be more than one easy handle waiting on the - same actual socket) */ - struct Curl_hash sockhash; - - /* Shared connection cache (bundles)*/ - struct conncache conn_cache; - - long maxconnects; /* if >0, a fixed limit of the maximum number of entries - we're allowed to grow the connection cache to */ - - long max_host_connections; /* if >0, a fixed limit of the maximum number - of connections per host */ - - long max_total_connections; /* if >0, a fixed limit of the maximum number - of connections in total */ - - /* timer callback and user data pointer for the *socket() API */ - curl_multi_timer_callback timer_cb; - void *timer_userp; - struct curltime timer_lastcall; /* the fixed time for the timeout for the - previous callback */ - unsigned int max_concurrent_streams; - -#ifdef USE_WINSOCK - WSAEVENT wsa_event; /* winsock event used for waits */ -#else -#ifdef ENABLE_WAKEUP - curl_socket_t wakeup_pair[2]; /* socketpair() used for wakeup - 0 is used for read, 1 is used for write */ -#endif -#endif -#define IPV6_UNKNOWN 0 -#define IPV6_DEAD 1 -#define IPV6_WORKS 2 - unsigned char ipv6_up; /* IPV6_* defined */ - bool multiplexing; /* multiplexing wanted */ - bool recheckstate; /* see Curl_multi_connchanged */ - bool in_callback; /* true while executing a callback */ -#ifdef USE_OPENSSL - bool ssl_seeded; -#endif - bool dead; /* a callback returned error, everything needs to crash and - burn */ -}; - -#endif /* HEADER_CURL_MULTIHANDLE_H */ +#ifndef HEADER_CURL_MULTIHANDLE_H
+#define HEADER_CURL_MULTIHANDLE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "llist.h"
+#include "hash.h"
+#include "conncache.h"
+#include "psl.h"
+#include "socketpair.h"
+
+struct connectdata;
+
+struct Curl_message {
+ struct Curl_llist_element list;
+ /* the 'CURLMsg' is the part that is visible to the external user */
+ struct CURLMsg extmsg;
+};
+
+/* NOTE: if you add a state here, add the name to the statename[] array as
+ well!
+*/
+typedef enum {
+ MSTATE_INIT, /* 0 - start in this state */
+ MSTATE_PENDING, /* 1 - no connections, waiting for one */
+ MSTATE_CONNECT, /* 2 - resolve/connect has been sent off */
+ MSTATE_RESOLVING, /* 3 - awaiting the resolve to finalize */
+ MSTATE_CONNECTING, /* 4 - awaiting the TCP connect to finalize */
+ MSTATE_TUNNELING, /* 5 - awaiting HTTPS proxy SSL initialization to
+ complete and/or proxy CONNECT to finalize */
+ MSTATE_PROTOCONNECT, /* 6 - initiate protocol connect procedure */
+ MSTATE_PROTOCONNECTING, /* 7 - completing the protocol-specific connect
+ phase */
+ MSTATE_DO, /* 8 - start send off the request (part 1) */
+ MSTATE_DOING, /* 9 - sending off the request (part 1) */
+ MSTATE_DOING_MORE, /* 10 - send off the request (part 2) */
+ MSTATE_DID, /* 11 - done sending off request */
+ MSTATE_PERFORMING, /* 12 - transfer data */
+ MSTATE_RATELIMITING, /* 13 - wait because limit-rate exceeded */
+ MSTATE_DONE, /* 14 - post data transfer operation */
+ MSTATE_COMPLETED, /* 15 - operation complete */
+ MSTATE_MSGSENT, /* 16 - the operation complete message is sent */
+ MSTATE_LAST /* 17 - not a true state, never use this */
+} CURLMstate;
+
+/* we support N sockets per easy handle. Set the corresponding bit to what
+ action we should wait for */
+#define MAX_SOCKSPEREASYHANDLE 5
+#define GETSOCK_READABLE (0x00ff)
+#define GETSOCK_WRITABLE (0xff00)
+
+#define CURLPIPE_ANY (CURLPIPE_MULTIPLEX)
+
+#if !defined(CURL_DISABLE_SOCKETPAIR)
+#define ENABLE_WAKEUP
+#endif
+
+/* value for MAXIMUM CONCURRENT STREAMS upper limit */
+#define INITIAL_MAX_CONCURRENT_STREAMS ((1U << 31) - 1)
+
+/* Curl_multi SSL backend-specific data; declared differently by each SSL
+ backend */
+struct multi_ssl_backend_data;
+
+/* This is the struct known as CURLM on the outside */
+struct Curl_multi {
+ /* First a simple identifier to easier detect if a user mix up
+ this multi handle with an easy handle. Set this to CURL_MULTI_HANDLE. */
+ unsigned int magic;
+
+ /* We have a doubly-linked list with easy handles */
+ struct Curl_easy *easyp;
+ struct Curl_easy *easylp; /* last node */
+
+ int num_easy; /* amount of entries in the linked list above. */
+ int num_alive; /* amount of easy handles that are added but have not yet
+ reached COMPLETE state */
+
+ struct Curl_llist msglist; /* a list of messages from completed transfers */
+
+ struct Curl_llist pending; /* Curl_easys that are in the
+ MSTATE_PENDING state */
+
+ /* callback function and user data pointer for the *socket() API */
+ curl_socket_callback socket_cb;
+ void *socket_userp;
+
+ /* callback function and user data pointer for server push */
+ curl_push_callback push_cb;
+ void *push_userp;
+
+ /* Hostname cache */
+ struct Curl_hash hostcache;
+
+#ifdef USE_LIBPSL
+ /* PSL cache. */
+ struct PslCache psl;
+#endif
+
+ /* timetree points to the splay-tree of time nodes to figure out expire
+ times of all currently set timers */
+ struct Curl_tree *timetree;
+
+#if defined(USE_SSL)
+ struct multi_ssl_backend_data *ssl_backend_data;
+#endif
+
+ /* 'sockhash' is the lookup hash for socket descriptor => easy handles (note
+ the pluralis form, there can be more than one easy handle waiting on the
+ same actual socket) */
+ struct Curl_hash sockhash;
+
+ /* Shared connection cache (bundles)*/
+ struct conncache conn_cache;
+
+ long maxconnects; /* if >0, a fixed limit of the maximum number of entries
+ we're allowed to grow the connection cache to */
+
+ long max_host_connections; /* if >0, a fixed limit of the maximum number
+ of connections per host */
+
+ long max_total_connections; /* if >0, a fixed limit of the maximum number
+ of connections in total */
+
+ /* timer callback and user data pointer for the *socket() API */
+ curl_multi_timer_callback timer_cb;
+ void *timer_userp;
+ struct curltime timer_lastcall; /* the fixed time for the timeout for the
+ previous callback */
+ unsigned int max_concurrent_streams;
+
+#ifdef USE_WINSOCK
+ WSAEVENT wsa_event; /* winsock event used for waits */
+#else
+#ifdef ENABLE_WAKEUP
+ curl_socket_t wakeup_pair[2]; /* socketpair() used for wakeup
+ 0 is used for read, 1 is used for write */
+#endif
+#endif
+#define IPV6_UNKNOWN 0
+#define IPV6_DEAD 1
+#define IPV6_WORKS 2
+ unsigned char ipv6_up; /* IPV6_* defined */
+ bool multiplexing; /* multiplexing wanted */
+ bool recheckstate; /* see Curl_multi_connchanged */
+ bool in_callback; /* true while executing a callback */
+#ifdef USE_OPENSSL
+ bool ssl_seeded;
+#endif
+ bool dead; /* a callback returned error, everything needs to crash and
+ burn */
+};
+
+#endif /* HEADER_CURL_MULTIHANDLE_H */
diff --git a/libs/libcurl/src/multiif.h b/libs/libcurl/src/multiif.h index 0cb9d4f7f2..0ffe5b45fb 100644 --- a/libs/libcurl/src/multiif.h +++ b/libs/libcurl/src/multiif.h @@ -1,101 +1,101 @@ -#ifndef HEADER_CURL_MULTIIF_H -#define HEADER_CURL_MULTIIF_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - -/* - * Prototypes for library-wide functions provided by multi.c - */ - -CURLcode Curl_updatesocket(struct Curl_easy *data); -void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id); -void Curl_expire_clear(struct Curl_easy *data); -void Curl_expire_done(struct Curl_easy *data, expire_id id); -CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT; -void Curl_attach_connection(struct Curl_easy *data, - struct connectdata *conn); -void Curl_detach_connection(struct Curl_easy *data); -bool Curl_multiplex_wanted(const struct Curl_multi *multi); -void Curl_set_in_callback(struct Curl_easy *data, bool value); -bool Curl_is_in_callback(struct Curl_easy *easy); -CURLcode Curl_preconnect(struct Curl_easy *data); - -/* Internal version of curl_multi_init() accepts size parameters for the - socket, connection and dns hashes */ -struct Curl_multi *Curl_multi_handle(int hashsize, int chashsize, - int dnssize); - -/* the write bits start at bit 16 for the *getsock() bitmap */ -#define GETSOCK_WRITEBITSTART 16 - -#define GETSOCK_BLANK 0 /* no bits set */ - -/* set the bit for the given sock number to make the bitmap for writable */ -#define GETSOCK_WRITESOCK(x) (1 << (GETSOCK_WRITEBITSTART + (x))) - -/* set the bit for the given sock number to make the bitmap for readable */ -#define GETSOCK_READSOCK(x) (1 << (x)) - -#ifdef DEBUGBUILD - /* - * Curl_multi_dump is not a stable public function, this is only meant to - * allow easier tracking of the internal handle's state and what sockets - * they use. Only for research and development DEBUGBUILD enabled builds. - */ -void Curl_multi_dump(struct Curl_multi *multi); -#endif - -/* Return the value of the CURLMOPT_MAX_HOST_CONNECTIONS option */ -size_t Curl_multi_max_host_connections(struct Curl_multi *multi); - -/* Return the value of the CURLMOPT_MAX_TOTAL_CONNECTIONS option */ -size_t Curl_multi_max_total_connections(struct Curl_multi *multi); - -void Curl_multiuse_state(struct Curl_easy *data, - int bundlestate); /* use BUNDLE_* defines */ - -/* - * Curl_multi_closed() - * - * Used by the connect code to tell the multi_socket code that one of the - * sockets we were using is about to be closed. This function will then - * remove it from the sockethash for this handle to make the multi_socket API - * behave properly, especially for the case when libcurl will create another - * socket again and it gets the same file descriptor number. - */ - -void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s); - -/* - * Add a handle and move it into PERFORM state at once. For pushed streams. - */ -CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, - struct Curl_easy *data, - struct connectdata *conn); - - -/* Return the value of the CURLMOPT_MAX_CONCURRENT_STREAMS option */ -unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi); - -#endif /* HEADER_CURL_MULTIIF_H */ +#ifndef HEADER_CURL_MULTIIF_H
+#define HEADER_CURL_MULTIIF_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+/*
+ * Prototypes for library-wide functions provided by multi.c
+ */
+
+CURLcode Curl_updatesocket(struct Curl_easy *data);
+void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id);
+void Curl_expire_clear(struct Curl_easy *data);
+void Curl_expire_done(struct Curl_easy *data, expire_id id);
+CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT;
+void Curl_attach_connection(struct Curl_easy *data,
+ struct connectdata *conn);
+void Curl_detach_connection(struct Curl_easy *data);
+bool Curl_multiplex_wanted(const struct Curl_multi *multi);
+void Curl_set_in_callback(struct Curl_easy *data, bool value);
+bool Curl_is_in_callback(struct Curl_easy *easy);
+CURLcode Curl_preconnect(struct Curl_easy *data);
+
+/* Internal version of curl_multi_init() accepts size parameters for the
+ socket, connection and dns hashes */
+struct Curl_multi *Curl_multi_handle(int hashsize, int chashsize,
+ int dnssize);
+
+/* the write bits start at bit 16 for the *getsock() bitmap */
+#define GETSOCK_WRITEBITSTART 16
+
+#define GETSOCK_BLANK 0 /* no bits set */
+
+/* set the bit for the given sock number to make the bitmap for writable */
+#define GETSOCK_WRITESOCK(x) (1 << (GETSOCK_WRITEBITSTART + (x)))
+
+/* set the bit for the given sock number to make the bitmap for readable */
+#define GETSOCK_READSOCK(x) (1 << (x))
+
+#ifdef DEBUGBUILD
+ /*
+ * Curl_multi_dump is not a stable public function, this is only meant to
+ * allow easier tracking of the internal handle's state and what sockets
+ * they use. Only for research and development DEBUGBUILD enabled builds.
+ */
+void Curl_multi_dump(struct Curl_multi *multi);
+#endif
+
+/* Return the value of the CURLMOPT_MAX_HOST_CONNECTIONS option */
+size_t Curl_multi_max_host_connections(struct Curl_multi *multi);
+
+/* Return the value of the CURLMOPT_MAX_TOTAL_CONNECTIONS option */
+size_t Curl_multi_max_total_connections(struct Curl_multi *multi);
+
+void Curl_multiuse_state(struct Curl_easy *data,
+ int bundlestate); /* use BUNDLE_* defines */
+
+/*
+ * Curl_multi_closed()
+ *
+ * Used by the connect code to tell the multi_socket code that one of the
+ * sockets we were using is about to be closed. This function will then
+ * remove it from the sockethash for this handle to make the multi_socket API
+ * behave properly, especially for the case when libcurl will create another
+ * socket again and it gets the same file descriptor number.
+ */
+
+void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s);
+
+/*
+ * Add a handle and move it into PERFORM state at once. For pushed streams.
+ */
+CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
+ struct Curl_easy *data,
+ struct connectdata *conn);
+
+
+/* Return the value of the CURLMOPT_MAX_CONCURRENT_STREAMS option */
+unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi);
+
+#endif /* HEADER_CURL_MULTIIF_H */
diff --git a/libs/libcurl/src/netrc.c b/libs/libcurl/src/netrc.c index 4461b8492f..aabdb437b9 100644 --- a/libs/libcurl/src/netrc.c +++ b/libs/libcurl/src/netrc.c @@ -1,349 +1,349 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_NETRC - -#ifdef HAVE_PWD_H -#include <pwd.h> -#endif - -#include <curl/curl.h> -#include "netrc.h" -#include "strtok.h" -#include "strcase.h" -#include "curl_get_line.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* Get user and password from .netrc when given a machine name */ - -enum host_lookup_state { - NOTHING, - HOSTFOUND, /* the 'machine' keyword was found */ - HOSTVALID, /* this is "our" machine! */ - MACDEF -}; - -#define NETRC_FILE_MISSING 1 -#define NETRC_FAILED -1 -#define NETRC_SUCCESS 0 - -/* - * Returns zero on success. - */ -static int parsenetrc(const char *host, - char **loginp, - char **passwordp, - char *netrcfile) -{ - FILE *file; - int retcode = NETRC_FILE_MISSING; - char *login = *loginp; - char *password = *passwordp; - bool specific_login = (login && *login != 0); - bool login_alloc = FALSE; - bool password_alloc = FALSE; - enum host_lookup_state state = NOTHING; - - char state_login = 0; /* Found a login keyword */ - char state_password = 0; /* Found a password keyword */ - int state_our_login = TRUE; /* With specific_login, found *our* login - name (or login-less line) */ - - DEBUGASSERT(netrcfile); - - file = fopen(netrcfile, FOPEN_READTEXT); - if(file) { - bool done = FALSE; - char netrcbuffer[4096]; - int netrcbuffsize = (int)sizeof(netrcbuffer); - - while(!done && Curl_get_line(netrcbuffer, netrcbuffsize, file)) { - char *tok; - char *tok_end; - bool quoted; - if(state == MACDEF) { - if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r')) - state = NOTHING; - else - continue; - } - tok = netrcbuffer; - while(tok) { - while(ISBLANK(*tok)) - tok++; - /* tok is first non-space letter */ - if(!*tok || (*tok == '#')) - /* end of line or the rest is a comment */ - break; - - /* leading double-quote means quoted string */ - quoted = (*tok == '\"'); - - tok_end = tok; - if(!quoted) { - while(!ISSPACE(*tok_end)) - tok_end++; - *tok_end = 0; - } - else { - bool escape = FALSE; - bool endquote = FALSE; - char *store = tok; - tok_end++; /* pass the leading quote */ - while(*tok_end) { - char s = *tok_end; - if(escape) { - escape = FALSE; - switch(s) { - case 'n': - s = '\n'; - break; - case 'r': - s = '\r'; - break; - case 't': - s = '\t'; - break; - } - } - else if(s == '\\') { - escape = TRUE; - tok_end++; - continue; - } - else if(s == '\"') { - tok_end++; /* pass the ending quote */ - endquote = TRUE; - break; - } - *store++ = s; - tok_end++; - } - *store = 0; - if(escape || !endquote) { - /* bad syntax, get out */ - retcode = NETRC_FAILED; - goto out; - } - } - - if((login && *login) && (password && *password)) { - done = TRUE; - break; - } - - switch(state) { - case NOTHING: - if(strcasecompare("macdef", tok)) { - /* Define a macro. A macro is defined with the specified name; its - contents begin with the next .netrc line and continue until a - null line (consecutive new-line characters) is encountered. */ - state = MACDEF; - } - else if(strcasecompare("machine", tok)) { - /* the next tok is the machine name, this is in itself the - delimiter that starts the stuff entered for this machine, - after this we need to search for 'login' and - 'password'. */ - state = HOSTFOUND; - } - else if(strcasecompare("default", tok)) { - state = HOSTVALID; - retcode = NETRC_SUCCESS; /* we did find our host */ - } - break; - case MACDEF: - if(!strlen(tok)) { - state = NOTHING; - } - break; - case HOSTFOUND: - if(strcasecompare(host, tok)) { - /* and yes, this is our host! */ - state = HOSTVALID; - retcode = NETRC_SUCCESS; /* we did find our host */ - } - else - /* not our host */ - state = NOTHING; - break; - case HOSTVALID: - /* we are now parsing sub-keywords concerning "our" host */ - if(state_login) { - if(specific_login) { - state_our_login = !Curl_timestrcmp(login, tok); - } - else if(!login || Curl_timestrcmp(login, tok)) { - if(login_alloc) { - free(login); - login_alloc = FALSE; - } - login = strdup(tok); - if(!login) { - retcode = NETRC_FAILED; /* allocation failed */ - goto out; - } - login_alloc = TRUE; - } - state_login = 0; - } - else if(state_password) { - if((state_our_login || !specific_login) - && (!password || Curl_timestrcmp(password, tok))) { - if(password_alloc) { - free(password); - password_alloc = FALSE; - } - password = strdup(tok); - if(!password) { - retcode = NETRC_FAILED; /* allocation failed */ - goto out; - } - password_alloc = TRUE; - } - state_password = 0; - } - else if(strcasecompare("login", tok)) - state_login = 1; - else if(strcasecompare("password", tok)) - state_password = 1; - else if(strcasecompare("machine", tok)) { - /* ok, there's machine here go => */ - state = HOSTFOUND; - state_our_login = FALSE; - } - break; - } /* switch (state) */ - tok = ++tok_end; - } - } /* while Curl_get_line() */ - - out: - if(!retcode) { - /* success */ - if(login_alloc) { - if(*loginp) - free(*loginp); - *loginp = login; - } - if(password_alloc) { - if(*passwordp) - free(*passwordp); - *passwordp = password; - } - } - else { - if(login_alloc) - free(login); - if(password_alloc) - free(password); - } - fclose(file); - } - - return retcode; -} - -/* - * @unittest: 1304 - * - * *loginp and *passwordp MUST be allocated if they aren't NULL when passed - * in. - */ -int Curl_parsenetrc(const char *host, char **loginp, char **passwordp, - char *netrcfile) -{ - int retcode = 1; - char *filealloc = NULL; - - if(!netrcfile) { -#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID) - char pwbuf[1024]; -#endif - char *home = NULL; - char *homea = curl_getenv("HOME"); /* portable environment reader */ - if(homea) { - home = homea; -#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID) - } - else { - struct passwd pw, *pw_res; - if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res) - && pw_res) { - home = pw.pw_dir; - } -#elif defined(HAVE_GETPWUID) && defined(HAVE_GETEUID) - } - else { - struct passwd *pw; - pw = getpwuid(geteuid()); - if(pw) { - home = pw->pw_dir; - } -#elif defined(_WIN32) - } - else { - homea = curl_getenv("USERPROFILE"); - if(homea) { - home = homea; - } -#endif - } - - if(!home) - return retcode; /* no home directory found (or possibly out of - memory) */ - - filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR); - if(!filealloc) { - free(homea); - return -1; - } - retcode = parsenetrc(host, loginp, passwordp, filealloc); - free(filealloc); -#ifdef WIN32 - if(retcode == NETRC_FILE_MISSING) { - /* fallback to the old-style "_netrc" file */ - filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR); - if(!filealloc) { - free(homea); - return -1; - } - retcode = parsenetrc(host, loginp, passwordp, filealloc); - free(filealloc); - } -#endif - free(homea); - } - else - retcode = parsenetrc(host, loginp, passwordp, netrcfile); - return retcode; -} - -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_NETRC
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#include <curl/curl.h>
+#include "netrc.h"
+#include "strtok.h"
+#include "strcase.h"
+#include "curl_get_line.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* Get user and password from .netrc when given a machine name */
+
+enum host_lookup_state {
+ NOTHING,
+ HOSTFOUND, /* the 'machine' keyword was found */
+ HOSTVALID, /* this is "our" machine! */
+ MACDEF
+};
+
+#define NETRC_FILE_MISSING 1
+#define NETRC_FAILED -1
+#define NETRC_SUCCESS 0
+
+/*
+ * Returns zero on success.
+ */
+static int parsenetrc(const char *host,
+ char **loginp,
+ char **passwordp,
+ char *netrcfile)
+{
+ FILE *file;
+ int retcode = NETRC_FILE_MISSING;
+ char *login = *loginp;
+ char *password = *passwordp;
+ bool specific_login = (login && *login != 0);
+ bool login_alloc = FALSE;
+ bool password_alloc = FALSE;
+ enum host_lookup_state state = NOTHING;
+
+ char state_login = 0; /* Found a login keyword */
+ char state_password = 0; /* Found a password keyword */
+ int state_our_login = TRUE; /* With specific_login, found *our* login
+ name (or login-less line) */
+
+ DEBUGASSERT(netrcfile);
+
+ file = fopen(netrcfile, FOPEN_READTEXT);
+ if(file) {
+ bool done = FALSE;
+ char netrcbuffer[4096];
+ int netrcbuffsize = (int)sizeof(netrcbuffer);
+
+ while(!done && Curl_get_line(netrcbuffer, netrcbuffsize, file)) {
+ char *tok;
+ char *tok_end;
+ bool quoted;
+ if(state == MACDEF) {
+ if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r'))
+ state = NOTHING;
+ else
+ continue;
+ }
+ tok = netrcbuffer;
+ while(tok) {
+ while(ISBLANK(*tok))
+ tok++;
+ /* tok is first non-space letter */
+ if(!*tok || (*tok == '#'))
+ /* end of line or the rest is a comment */
+ break;
+
+ /* leading double-quote means quoted string */
+ quoted = (*tok == '\"');
+
+ tok_end = tok;
+ if(!quoted) {
+ while(!ISSPACE(*tok_end))
+ tok_end++;
+ *tok_end = 0;
+ }
+ else {
+ bool escape = FALSE;
+ bool endquote = FALSE;
+ char *store = tok;
+ tok_end++; /* pass the leading quote */
+ while(*tok_end) {
+ char s = *tok_end;
+ if(escape) {
+ escape = FALSE;
+ switch(s) {
+ case 'n':
+ s = '\n';
+ break;
+ case 'r':
+ s = '\r';
+ break;
+ case 't':
+ s = '\t';
+ break;
+ }
+ }
+ else if(s == '\\') {
+ escape = TRUE;
+ tok_end++;
+ continue;
+ }
+ else if(s == '\"') {
+ tok_end++; /* pass the ending quote */
+ endquote = TRUE;
+ break;
+ }
+ *store++ = s;
+ tok_end++;
+ }
+ *store = 0;
+ if(escape || !endquote) {
+ /* bad syntax, get out */
+ retcode = NETRC_FAILED;
+ goto out;
+ }
+ }
+
+ if((login && *login) && (password && *password)) {
+ done = TRUE;
+ break;
+ }
+
+ switch(state) {
+ case NOTHING:
+ if(strcasecompare("macdef", tok)) {
+ /* Define a macro. A macro is defined with the specified name; its
+ contents begin with the next .netrc line and continue until a
+ null line (consecutive new-line characters) is encountered. */
+ state = MACDEF;
+ }
+ else if(strcasecompare("machine", tok)) {
+ /* the next tok is the machine name, this is in itself the
+ delimiter that starts the stuff entered for this machine,
+ after this we need to search for 'login' and
+ 'password'. */
+ state = HOSTFOUND;
+ }
+ else if(strcasecompare("default", tok)) {
+ state = HOSTVALID;
+ retcode = NETRC_SUCCESS; /* we did find our host */
+ }
+ break;
+ case MACDEF:
+ if(!strlen(tok)) {
+ state = NOTHING;
+ }
+ break;
+ case HOSTFOUND:
+ if(strcasecompare(host, tok)) {
+ /* and yes, this is our host! */
+ state = HOSTVALID;
+ retcode = NETRC_SUCCESS; /* we did find our host */
+ }
+ else
+ /* not our host */
+ state = NOTHING;
+ break;
+ case HOSTVALID:
+ /* we are now parsing sub-keywords concerning "our" host */
+ if(state_login) {
+ if(specific_login) {
+ state_our_login = !Curl_timestrcmp(login, tok);
+ }
+ else if(!login || Curl_timestrcmp(login, tok)) {
+ if(login_alloc) {
+ free(login);
+ login_alloc = FALSE;
+ }
+ login = strdup(tok);
+ if(!login) {
+ retcode = NETRC_FAILED; /* allocation failed */
+ goto out;
+ }
+ login_alloc = TRUE;
+ }
+ state_login = 0;
+ }
+ else if(state_password) {
+ if((state_our_login || !specific_login)
+ && (!password || Curl_timestrcmp(password, tok))) {
+ if(password_alloc) {
+ free(password);
+ password_alloc = FALSE;
+ }
+ password = strdup(tok);
+ if(!password) {
+ retcode = NETRC_FAILED; /* allocation failed */
+ goto out;
+ }
+ password_alloc = TRUE;
+ }
+ state_password = 0;
+ }
+ else if(strcasecompare("login", tok))
+ state_login = 1;
+ else if(strcasecompare("password", tok))
+ state_password = 1;
+ else if(strcasecompare("machine", tok)) {
+ /* ok, there's machine here go => */
+ state = HOSTFOUND;
+ state_our_login = FALSE;
+ }
+ break;
+ } /* switch (state) */
+ tok = ++tok_end;
+ }
+ } /* while Curl_get_line() */
+
+ out:
+ if(!retcode) {
+ /* success */
+ if(login_alloc) {
+ if(*loginp)
+ free(*loginp);
+ *loginp = login;
+ }
+ if(password_alloc) {
+ if(*passwordp)
+ free(*passwordp);
+ *passwordp = password;
+ }
+ }
+ else {
+ if(login_alloc)
+ free(login);
+ if(password_alloc)
+ free(password);
+ }
+ fclose(file);
+ }
+
+ return retcode;
+}
+
+/*
+ * @unittest: 1304
+ *
+ * *loginp and *passwordp MUST be allocated if they aren't NULL when passed
+ * in.
+ */
+int Curl_parsenetrc(const char *host, char **loginp, char **passwordp,
+ char *netrcfile)
+{
+ int retcode = 1;
+ char *filealloc = NULL;
+
+ if(!netrcfile) {
+#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
+ char pwbuf[1024];
+#endif
+ char *home = NULL;
+ char *homea = curl_getenv("HOME"); /* portable environment reader */
+ if(homea) {
+ home = homea;
+#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
+ }
+ else {
+ struct passwd pw, *pw_res;
+ if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res)
+ && pw_res) {
+ home = pw.pw_dir;
+ }
+#elif defined(HAVE_GETPWUID) && defined(HAVE_GETEUID)
+ }
+ else {
+ struct passwd *pw;
+ pw = getpwuid(geteuid());
+ if(pw) {
+ home = pw->pw_dir;
+ }
+#elif defined(_WIN32)
+ }
+ else {
+ homea = curl_getenv("USERPROFILE");
+ if(homea) {
+ home = homea;
+ }
+#endif
+ }
+
+ if(!home)
+ return retcode; /* no home directory found (or possibly out of
+ memory) */
+
+ filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR);
+ if(!filealloc) {
+ free(homea);
+ return -1;
+ }
+ retcode = parsenetrc(host, loginp, passwordp, filealloc);
+ free(filealloc);
+#ifdef WIN32
+ if(retcode == NETRC_FILE_MISSING) {
+ /* fallback to the old-style "_netrc" file */
+ filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR);
+ if(!filealloc) {
+ free(homea);
+ return -1;
+ }
+ retcode = parsenetrc(host, loginp, passwordp, filealloc);
+ free(filealloc);
+ }
+#endif
+ free(homea);
+ }
+ else
+ retcode = parsenetrc(host, loginp, passwordp, netrcfile);
+ return retcode;
+}
+
+#endif
diff --git a/libs/libcurl/src/netrc.h b/libs/libcurl/src/netrc.h index 53d0056721..0736f9a6b2 100644 --- a/libs/libcurl/src/netrc.h +++ b/libs/libcurl/src/netrc.h @@ -1,43 +1,43 @@ -#ifndef HEADER_CURL_NETRC_H -#define HEADER_CURL_NETRC_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_NETRC - -/* returns -1 on failure, 0 if the host is found, 1 is the host isn't found */ -int Curl_parsenetrc(const char *host, char **loginp, - char **passwordp, char *filename); - /* Assume: (*passwordp)[0]=0, host[0] != 0. - * If (*loginp)[0] = 0, search for login and password within a machine - * section in the netrc. - * If (*loginp)[0] != 0, search for password within machine and login. - */ -#else -/* disabled */ -#define Curl_parsenetrc(a,b,c,d,e,f) 1 -#endif - -#endif /* HEADER_CURL_NETRC_H */ +#ifndef HEADER_CURL_NETRC_H
+#define HEADER_CURL_NETRC_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_NETRC
+
+/* returns -1 on failure, 0 if the host is found, 1 is the host isn't found */
+int Curl_parsenetrc(const char *host, char **loginp,
+ char **passwordp, char *filename);
+ /* Assume: (*passwordp)[0]=0, host[0] != 0.
+ * If (*loginp)[0] = 0, search for login and password within a machine
+ * section in the netrc.
+ * If (*loginp)[0] != 0, search for password within machine and login.
+ */
+#else
+/* disabled */
+#define Curl_parsenetrc(a,b,c,d,e,f) 1
+#endif
+
+#endif /* HEADER_CURL_NETRC_H */
diff --git a/libs/libcurl/src/nonblock.c b/libs/libcurl/src/nonblock.c index ce73af31c1..fe7f49ef9e 100644 --- a/libs/libcurl/src/nonblock.c +++ b/libs/libcurl/src/nonblock.c @@ -1,87 +1,84 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_SYS_IOCTL_H -#include <sys/ioctl.h> -#endif -#ifdef HAVE_FCNTL_H -#include <fcntl.h> -#endif - -#if (defined(HAVE_IOCTL_FIONBIO) && defined(NETWARE)) -#include <sys/filio.h> -#endif -#ifdef __VMS -#include <in.h> -#include <inet.h> -#endif - -#include "nonblock.h" - -/* - * curlx_nonblock() set the given socket to either blocking or non-blocking - * mode based on the 'nonblock' boolean argument. This function is highly - * portable. - */ -int curlx_nonblock(curl_socket_t sockfd, /* operate on this */ - int nonblock /* TRUE or FALSE */) -{ -#if defined(HAVE_FCNTL_O_NONBLOCK) - /* most recent unix versions */ - int flags; - flags = sfcntl(sockfd, F_GETFL, 0); - if(nonblock) - return sfcntl(sockfd, F_SETFL, flags | O_NONBLOCK); - return sfcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK)); - -#elif defined(HAVE_IOCTL_FIONBIO) - - /* older unix versions */ - int flags = nonblock ? 1 : 0; - return ioctl(sockfd, FIONBIO, &flags); - -#elif defined(HAVE_IOCTLSOCKET_FIONBIO) - - /* Windows */ - unsigned long flags = nonblock ? 1UL : 0UL; - return ioctlsocket(sockfd, FIONBIO, &flags); - -#elif defined(HAVE_IOCTLSOCKET_CAMEL_FIONBIO) - - /* Amiga */ - long flags = nonblock ? 1L : 0L; - return IoctlSocket(sockfd, FIONBIO, (char *)&flags); - -#elif defined(HAVE_SETSOCKOPT_SO_NONBLOCK) - - /* Orbis OS */ - long b = nonblock ? 1L : 0L; - return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); - -#else -# error "no non-blocking method was found/used/set" -#endif -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#include "nonblock.h"
+
+/*
+ * curlx_nonblock() set the given socket to either blocking or non-blocking
+ * mode based on the 'nonblock' boolean argument. This function is highly
+ * portable.
+ */
+int curlx_nonblock(curl_socket_t sockfd, /* operate on this */
+ int nonblock /* TRUE or FALSE */)
+{
+#if defined(HAVE_FCNTL_O_NONBLOCK)
+ /* most recent unix versions */
+ int flags;
+ flags = sfcntl(sockfd, F_GETFL, 0);
+ if(nonblock)
+ return sfcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
+ return sfcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK));
+
+#elif defined(HAVE_IOCTL_FIONBIO)
+
+ /* older unix versions */
+ int flags = nonblock ? 1 : 0;
+ return ioctl(sockfd, FIONBIO, &flags);
+
+#elif defined(HAVE_IOCTLSOCKET_FIONBIO)
+
+ /* Windows */
+ unsigned long flags = nonblock ? 1UL : 0UL;
+ return ioctlsocket(sockfd, FIONBIO, &flags);
+
+#elif defined(HAVE_IOCTLSOCKET_CAMEL_FIONBIO)
+
+ /* Amiga */
+ long flags = nonblock ? 1L : 0L;
+ return IoctlSocket(sockfd, FIONBIO, (char *)&flags);
+
+#elif defined(HAVE_SETSOCKOPT_SO_NONBLOCK)
+
+ /* Orbis OS */
+ long b = nonblock ? 1L : 0L;
+ return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b));
+
+#else
+# error "no non-blocking method was found/used/set"
+#endif
+}
diff --git a/libs/libcurl/src/nonblock.h b/libs/libcurl/src/nonblock.h index a42f443a49..862534fd5f 100644 --- a/libs/libcurl/src/nonblock.h +++ b/libs/libcurl/src/nonblock.h @@ -1,32 +1,32 @@ -#ifndef HEADER_CURL_NONBLOCK_H -#define HEADER_CURL_NONBLOCK_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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> /* for curl_socket_t */ - -int curlx_nonblock(curl_socket_t sockfd, /* operate on this */ - int nonblock /* TRUE or FALSE */); - -#endif /* HEADER_CURL_NONBLOCK_H */ +#ifndef HEADER_CURL_NONBLOCK_H
+#define HEADER_CURL_NONBLOCK_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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> /* for curl_socket_t */
+
+int curlx_nonblock(curl_socket_t sockfd, /* operate on this */
+ int nonblock /* TRUE or FALSE */);
+
+#endif /* HEADER_CURL_NONBLOCK_H */
diff --git a/libs/libcurl/src/noproxy.c b/libs/libcurl/src/noproxy.c index 81f1e09934..5b591fcb40 100644 --- a/libs/libcurl/src/noproxy.c +++ b/libs/libcurl/src/noproxy.c @@ -1,222 +1,255 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "inet_pton.h" -#include "strcase.h" -#include "noproxy.h" - -#ifdef HAVE_NETINET_IN_H -#include <netinet/in.h> -#endif - -/* - * Curl_cidr4_match() returns TRUE if the given IPv4 address is within the - * specified CIDR address range. - */ -UNITTEST bool Curl_cidr4_match(const char *ipv4, /* 1.2.3.4 address */ - const char *network, /* 1.2.3.4 address */ - unsigned int bits) -{ - unsigned int address = 0; - unsigned int check = 0; - - if(bits > 32) - /* strange input */ - return FALSE; - - if(1 != Curl_inet_pton(AF_INET, ipv4, &address)) - return FALSE; - if(1 != Curl_inet_pton(AF_INET, network, &check)) - return FALSE; - - if(bits && (bits != 32)) { - unsigned int mask = 0xffffffff << (32 - bits); - unsigned int haddr = htonl(address); - unsigned int hcheck = htonl(check); -#if 0 - fprintf(stderr, "Host %s (%x) network %s (%x) bits %u mask %x => %x\n", - ipv4, haddr, network, hcheck, bits, mask, - (haddr ^ hcheck) & mask); -#endif - if((haddr ^ hcheck) & mask) - return FALSE; - return TRUE; - } - return (address == check); -} - -UNITTEST bool Curl_cidr6_match(const char *ipv6, - const char *network, - unsigned int bits) -{ -#ifdef ENABLE_IPV6 - int bytes; - int rest; - unsigned char address[16]; - unsigned char check[16]; - - if(!bits) - bits = 128; - - bytes = bits/8; - rest = bits & 0x07; - if(1 != Curl_inet_pton(AF_INET6, ipv6, address)) - return FALSE; - if(1 != Curl_inet_pton(AF_INET6, network, check)) - return FALSE; - if((bytes > 16) || ((bytes == 16) && rest)) - return FALSE; - if(bytes && memcmp(address, check, bytes)) - return FALSE; - if(rest && !((address[bytes] ^ check[bytes]) & (0xff << (8 - rest)))) - return FALSE; - - return TRUE; -#else - (void)ipv6; - (void)network; - (void)bits; - return FALSE; -#endif -} - -enum nametype { - TYPE_HOST, - TYPE_IPV4, - TYPE_IPV6 -}; - -/**************************************************************** -* Checks if the host is in the noproxy list. returns TRUE if it matches and -* therefore the proxy should NOT be used. -****************************************************************/ -bool Curl_check_noproxy(const char *name, const char *no_proxy) -{ - /* no_proxy=domain1.dom,host.domain2.dom - * (a comma-separated list of hosts which should - * not be proxied, or an asterisk to override - * all proxy variables) - */ - if(no_proxy && no_proxy[0]) { - const char *p = no_proxy; - size_t namelen; - enum nametype type = TYPE_HOST; - char hostip[128]; - if(!strcmp("*", no_proxy)) - return TRUE; - - /* NO_PROXY was specified and it wasn't just an asterisk */ - - if(name[0] == '[') { - char *endptr; - /* IPv6 numerical address */ - endptr = strchr(name, ']'); - if(!endptr) - return FALSE; - name++; - namelen = endptr - name; - if(namelen >= sizeof(hostip)) - return FALSE; - memcpy(hostip, name, namelen); - hostip[namelen] = 0; - name = hostip; - type = TYPE_IPV6; - } - else { - unsigned int address; - if(1 == Curl_inet_pton(AF_INET, name, &address)) - type = TYPE_IPV4; - namelen = strlen(name); - } - - while(*p) { - const char *token; - size_t tokenlen = 0; - bool match = FALSE; - - /* pass blanks */ - while(*p && ISBLANK(*p)) - p++; - - token = p; - /* pass over the pattern */ - while(*p && !ISBLANK(*p) && (*p != ',')) { - p++; - tokenlen++; - } - - if(tokenlen) { - switch(type) { - case TYPE_HOST: - if(*token == '.') { - ++token; - --tokenlen; - /* tailmatch */ - match = (tokenlen <= namelen) && - strncasecompare(token, name + (namelen - tokenlen), namelen); - } - else - match = (tokenlen == namelen) && - strncasecompare(token, name, namelen); - break; - case TYPE_IPV4: - /* FALLTHROUGH */ - case TYPE_IPV6: { - const char *check = token; - char *slash = strchr(check, '/'); - unsigned int bits = 0; - char checkip[128]; - /* if the slash is part of this token, use it */ - if(slash && (slash < &check[tokenlen])) { - bits = atoi(slash + 1); - /* copy the check name to a temp buffer */ - if(tokenlen >= sizeof(checkip)) - break; - memcpy(checkip, check, tokenlen); - checkip[ slash - check ] = 0; - check = checkip; - } - if(type == TYPE_IPV6) - match = Curl_cidr6_match(name, check, bits); - else - match = Curl_cidr4_match(name, check, bits); - break; - } - } - if(match) - return TRUE; - } /* if(tokenlen) */ - while(*p == ',') - p++; - } /* while(*p) */ - } /* NO_PROXY was specified and it wasn't just an asterisk */ - - return FALSE; -} - -#endif /* CURL_DISABLE_PROXY */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "inet_pton.h"
+#include "strcase.h"
+#include "noproxy.h"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+/*
+ * Curl_cidr4_match() returns TRUE if the given IPv4 address is within the
+ * specified CIDR address range.
+ */
+UNITTEST bool Curl_cidr4_match(const char *ipv4, /* 1.2.3.4 address */
+ const char *network, /* 1.2.3.4 address */
+ unsigned int bits)
+{
+ unsigned int address = 0;
+ unsigned int check = 0;
+
+ if(bits > 32)
+ /* strange input */
+ return FALSE;
+
+ if(1 != Curl_inet_pton(AF_INET, ipv4, &address))
+ return FALSE;
+ if(1 != Curl_inet_pton(AF_INET, network, &check))
+ return FALSE;
+
+ if(bits && (bits != 32)) {
+ unsigned int mask = 0xffffffff << (32 - bits);
+ unsigned int haddr = htonl(address);
+ unsigned int hcheck = htonl(check);
+#if 0
+ fprintf(stderr, "Host %s (%x) network %s (%x) bits %u mask %x => %x\n",
+ ipv4, haddr, network, hcheck, bits, mask,
+ (haddr ^ hcheck) & mask);
+#endif
+ if((haddr ^ hcheck) & mask)
+ return FALSE;
+ return TRUE;
+ }
+ return (address == check);
+}
+
+UNITTEST bool Curl_cidr6_match(const char *ipv6,
+ const char *network,
+ unsigned int bits)
+{
+#ifdef ENABLE_IPV6
+ int bytes;
+ int rest;
+ unsigned char address[16];
+ unsigned char check[16];
+
+ if(!bits)
+ bits = 128;
+
+ bytes = bits/8;
+ rest = bits & 0x07;
+ if(1 != Curl_inet_pton(AF_INET6, ipv6, address))
+ return FALSE;
+ if(1 != Curl_inet_pton(AF_INET6, network, check))
+ return FALSE;
+ if((bytes > 16) || ((bytes == 16) && rest))
+ return FALSE;
+ if(bytes && memcmp(address, check, bytes))
+ return FALSE;
+ if(rest && !((address[bytes] ^ check[bytes]) & (0xff << (8 - rest))))
+ return FALSE;
+
+ return TRUE;
+#else
+ (void)ipv6;
+ (void)network;
+ (void)bits;
+ return FALSE;
+#endif
+}
+
+enum nametype {
+ TYPE_HOST,
+ TYPE_IPV4,
+ TYPE_IPV6
+};
+
+/****************************************************************
+* Checks if the host is in the noproxy list. returns TRUE if it matches and
+* therefore the proxy should NOT be used.
+****************************************************************/
+bool Curl_check_noproxy(const char *name, const char *no_proxy)
+{
+ /*
+ * If we don't have a hostname at all, like for example with a FILE
+ * transfer, we have nothing to interrogate the noproxy list with.
+ */
+ if(!name || name[0] == '\0')
+ return FALSE;
+
+ /* no_proxy=domain1.dom,host.domain2.dom
+ * (a comma-separated list of hosts which should
+ * not be proxied, or an asterisk to override
+ * all proxy variables)
+ */
+ if(no_proxy && no_proxy[0]) {
+ const char *p = no_proxy;
+ size_t namelen;
+ enum nametype type = TYPE_HOST;
+ char hostip[128];
+ if(!strcmp("*", no_proxy))
+ return TRUE;
+
+ /* NO_PROXY was specified and it wasn't just an asterisk */
+
+ if(name[0] == '[') {
+ char *endptr;
+ /* IPv6 numerical address */
+ endptr = strchr(name, ']');
+ if(!endptr)
+ return FALSE;
+ name++;
+ namelen = endptr - name;
+ if(namelen >= sizeof(hostip))
+ return FALSE;
+ memcpy(hostip, name, namelen);
+ hostip[namelen] = 0;
+ name = hostip;
+ type = TYPE_IPV6;
+ }
+ else {
+ unsigned int address;
+ namelen = strlen(name);
+ if(1 == Curl_inet_pton(AF_INET, name, &address))
+ type = TYPE_IPV4;
+ else {
+ /* ignore trailing dots in the host name */
+ if(name[namelen - 1] == '.')
+ namelen--;
+ }
+ }
+
+ while(*p) {
+ const char *token;
+ size_t tokenlen = 0;
+ bool match = FALSE;
+
+ /* pass blanks */
+ while(*p && ISBLANK(*p))
+ p++;
+
+ token = p;
+ /* pass over the pattern */
+ while(*p && !ISBLANK(*p) && (*p != ',')) {
+ p++;
+ tokenlen++;
+ }
+
+ if(tokenlen) {
+ switch(type) {
+ case TYPE_HOST:
+ /* ignore trailing dots in the token to check */
+ if(token[tokenlen - 1] == '.')
+ tokenlen--;
+
+ if(tokenlen && (*token == '.')) {
+ /* ignore leading token dot as well */
+ token++;
+ tokenlen--;
+ }
+ /* A: example.com matches 'example.com'
+ B: www.example.com matches 'example.com'
+ C: nonexample.com DOES NOT match 'example.com'
+ */
+ if(tokenlen == namelen)
+ /* case A, exact match */
+ match = strncasecompare(token, name, namelen);
+ else if(tokenlen < namelen) {
+ /* case B, tailmatch domain */
+ match = (name[namelen - tokenlen - 1] == '.') &&
+ strncasecompare(token, name + (namelen - tokenlen),
+ tokenlen);
+ }
+ /* case C passes through, not a match */
+ break;
+ case TYPE_IPV4:
+ /* FALLTHROUGH */
+ case TYPE_IPV6: {
+ const char *check = token;
+ char *slash;
+ unsigned int bits = 0;
+ char checkip[128];
+ if(tokenlen >= sizeof(checkip))
+ /* this cannot match */
+ break;
+ /* copy the check name to a temp buffer */
+ memcpy(checkip, check, tokenlen);
+ checkip[tokenlen] = 0;
+ check = checkip;
+
+ slash = strchr(check, '/');
+ /* if the slash is part of this token, use it */
+ if(slash) {
+ bits = atoi(slash + 1);
+ *slash = 0; /* null terminate there */
+ }
+ if(type == TYPE_IPV6)
+ match = Curl_cidr6_match(name, check, bits);
+ else
+ match = Curl_cidr4_match(name, check, bits);
+ break;
+ }
+ }
+ if(match)
+ return TRUE;
+ } /* if(tokenlen) */
+ while(*p == ',')
+ p++;
+ } /* while(*p) */
+ } /* NO_PROXY was specified and it wasn't just an asterisk */
+
+ return FALSE;
+}
+
+#endif /* CURL_DISABLE_PROXY */
diff --git a/libs/libcurl/src/noproxy.h b/libs/libcurl/src/noproxy.h index 8800a21276..d57a7a9b27 100644 --- a/libs/libcurl/src/noproxy.h +++ b/libs/libcurl/src/noproxy.h @@ -1,44 +1,44 @@ -#ifndef HEADER_CURL_NOPROXY_H -#define HEADER_CURL_NOPROXY_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - -#ifdef DEBUGBUILD - -UNITTEST bool Curl_cidr4_match(const char *ipv4, /* 1.2.3.4 address */ - const char *network, /* 1.2.3.4 address */ - unsigned int bits); -UNITTEST bool Curl_cidr6_match(const char *ipv6, - const char *network, - unsigned int bits); -#endif - -bool Curl_check_noproxy(const char *name, const char *no_proxy); - -#endif - -#endif /* HEADER_CURL_NOPROXY_H */ +#ifndef HEADER_CURL_NOPROXY_H
+#define HEADER_CURL_NOPROXY_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+
+#ifdef DEBUGBUILD
+
+UNITTEST bool Curl_cidr4_match(const char *ipv4, /* 1.2.3.4 address */
+ const char *network, /* 1.2.3.4 address */
+ unsigned int bits);
+UNITTEST bool Curl_cidr6_match(const char *ipv6,
+ const char *network,
+ unsigned int bits);
+#endif
+
+bool Curl_check_noproxy(const char *name, const char *no_proxy);
+
+#endif
+
+#endif /* HEADER_CURL_NOPROXY_H */
diff --git a/libs/libcurl/src/openldap.c b/libs/libcurl/src/openldap.c index 3a93b6728d..61527cf3a4 100644 --- a/libs/libcurl/src/openldap.c +++ b/libs/libcurl/src/openldap.c @@ -1,1211 +1,1211 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2011 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2010, Howard Chu, <hyc@openldap.org> - * - * 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_LDAP) && defined(USE_OPENLDAP) - -/* - * Notice that USE_OPENLDAP is only a source code selection switch. When - * libcurl is built with USE_OPENLDAP defined the libcurl source code that - * gets compiled is the code from openldap.c, otherwise the code that gets - * compiled is the code from ldap.c. - * - * When USE_OPENLDAP is defined a recent version of the OpenLDAP library - * might be required for compilation and runtime. In order to use ancient - * OpenLDAP library versions, USE_OPENLDAP shall not be defined. - */ - -#include <ldap.h> - -#include "urldata.h" -#include <curl/curl.h> -#include "sendf.h" -#include "vtls/vtls.h" -#include "transfer.h" -#include "curl_ldap.h" -#include "curl_base64.h" -#include "connect.h" -#include "curl_sasl.h" -#include "strcase.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* - * Uncommenting this will enable the built-in debug logging of the openldap - * library. The debug log level can be set using the CURL_OPENLDAP_TRACE - * environment variable. The debug output is written to stderr. - * - * The library supports the following debug flags: - * LDAP_DEBUG_NONE 0x0000 - * LDAP_DEBUG_TRACE 0x0001 - * LDAP_DEBUG_CONSTRUCT 0x0002 - * LDAP_DEBUG_DESTROY 0x0004 - * LDAP_DEBUG_PARAMETER 0x0008 - * LDAP_DEBUG_ANY 0xffff - * - * For example, use CURL_OPENLDAP_TRACE=0 for no debug, - * CURL_OPENLDAP_TRACE=2 for LDAP_DEBUG_CONSTRUCT messages only, - * CURL_OPENLDAP_TRACE=65535 for all debug message levels. - */ -/* #define CURL_OPENLDAP_DEBUG */ - -/* Machine states. */ -typedef enum { - OLDAP_STOP, /* Do nothing state, stops the state machine */ - OLDAP_SSL, /* Performing SSL handshake. */ - OLDAP_STARTTLS, /* STARTTLS request sent. */ - OLDAP_TLS, /* Performing TLS handshake. */ - OLDAP_MECHS, /* Get SASL authentication mechanisms. */ - OLDAP_SASL, /* SASL binding reply. */ - OLDAP_BIND, /* Simple bind reply. */ - OLDAP_BINDV2, /* Simple bind reply in protocol version 2. */ - OLDAP_LAST /* Never used */ -} ldapstate; - -#ifndef _LDAP_PVT_H -extern int ldap_pvt_url_scheme2proto(const char *); -extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, - LDAP **ld); -#endif - -static CURLcode oldap_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode oldap_do(struct Curl_easy *data, bool *done); -static CURLcode oldap_done(struct Curl_easy *data, CURLcode, bool); -static CURLcode oldap_connect(struct Curl_easy *data, bool *done); -static CURLcode oldap_connecting(struct Curl_easy *data, bool *done); -static CURLcode oldap_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead); - -static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech, - const struct bufref *initresp); -static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech, - const struct bufref *resp); -static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech); -static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out); - -static Curl_recv oldap_recv; - -/* - * LDAP protocol handler. - */ - -const struct Curl_handler Curl_handler_ldap = { - "LDAP", /* scheme */ - oldap_setup_connection, /* setup_connection */ - oldap_do, /* do_it */ - oldap_done, /* done */ - ZERO_NULL, /* do_more */ - oldap_connect, /* connect_it */ - oldap_connecting, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - oldap_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_LDAP, /* defport */ - CURLPROTO_LDAP, /* protocol */ - CURLPROTO_LDAP, /* family */ - PROTOPT_NONE /* flags */ -}; - -#ifdef USE_SSL -/* - * LDAPS protocol handler. - */ - -const struct Curl_handler Curl_handler_ldaps = { - "LDAPS", /* scheme */ - oldap_setup_connection, /* setup_connection */ - oldap_do, /* do_it */ - oldap_done, /* done */ - ZERO_NULL, /* do_more */ - oldap_connect, /* connect_it */ - oldap_connecting, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - oldap_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_LDAPS, /* defport */ - CURLPROTO_LDAPS, /* protocol */ - CURLPROTO_LDAP, /* family */ - PROTOPT_SSL /* flags */ -}; -#endif - -/* SASL parameters for the ldap protocol */ -static const struct SASLproto saslldap = { - "ldap", /* The service name */ - oldap_perform_auth, /* Send authentication command */ - oldap_continue_auth, /* Send authentication continuation */ - oldap_cancel_auth, /* Send authentication cancellation */ - oldap_get_message, /* Get SASL response message */ - 0, /* Maximum initial response length (no max) */ - LDAP_SASL_BIND_IN_PROGRESS, /* Code received when continuation is expected */ - LDAP_SUCCESS, /* Code to receive upon authentication success */ - SASL_AUTH_NONE, /* Default mechanisms */ - 0 /* Configuration flags */ -}; - -struct ldapconninfo { - struct SASL sasl; /* SASL-related parameters */ - LDAP *ld; /* Openldap connection handle. */ - Curl_recv *recv; /* For stacking SSL handler */ - Curl_send *send; - struct berval *servercred; /* SASL data from server. */ - ldapstate state; /* Current machine state. */ - int proto; /* LDAP_PROTO_TCP/LDAP_PROTO_UDP/LDAP_PROTO_IPC */ - int msgid; /* Current message id. */ -}; - -struct ldapreqinfo { - int msgid; - int nument; -}; - -/* - * state() - * - * This is the ONLY way to change LDAP state! - */ -static void state(struct Curl_easy *data, ldapstate newstate) -{ - struct ldapconninfo *ldapc = data->conn->proto.ldapc; - -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - /* for debug purposes */ - static const char * const names[] = { - "STOP", - "SSL", - "STARTTLS", - "TLS", - "MECHS", - "SASL", - "BIND", - "BINDV2", - /* LAST */ - }; - - if(ldapc->state != newstate) - infof(data, "LDAP %p state change from %s to %s", - (void *)ldapc, names[ldapc->state], names[newstate]); -#endif - - ldapc->state = newstate; -} - -/* Map some particular LDAP error codes to CURLcode values. */ -static CURLcode oldap_map_error(int rc, CURLcode result) -{ - switch(rc) { - case LDAP_NO_MEMORY: - result = CURLE_OUT_OF_MEMORY; - break; - case LDAP_INVALID_CREDENTIALS: - result = CURLE_LOGIN_DENIED; - break; - case LDAP_PROTOCOL_ERROR: - result = CURLE_UNSUPPORTED_PROTOCOL; - break; - case LDAP_INSUFFICIENT_ACCESS: - result = CURLE_REMOTE_ACCESS_DENIED; - break; - } - return result; -} - -static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp) -{ - CURLcode result = CURLE_OK; - int rc = LDAP_URL_ERR_BADURL; - static const char * const url_errs[] = { - "success", - "out of memory", - "bad parameter", - "unrecognized scheme", - "unbalanced delimiter", - "bad URL", - "bad host or port", - "bad or missing attributes", - "bad or missing scope", - "bad or missing filter", - "bad or missing extensions" - }; - - *ludp = NULL; - if(!data->state.up.user && !data->state.up.password && - !data->state.up.options) - rc = ldap_url_parse(data->state.url, ludp); - if(rc != LDAP_URL_SUCCESS) { - const char *msg = "url parsing problem"; - - result = rc == LDAP_URL_ERR_MEM? CURLE_OUT_OF_MEMORY: CURLE_URL_MALFORMAT; - rc -= LDAP_URL_SUCCESS; - if((size_t) rc < sizeof(url_errs) / sizeof(url_errs[0])) - msg = url_errs[rc]; - failf(data, "LDAP local: %s", msg); - } - return result; -} - -/* Parse the login options. */ -static CURLcode oldap_parse_login_options(struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - struct ldapconninfo *li = conn->proto.ldapc; - const char *ptr = conn->options; - - while(!result && ptr && *ptr) { - const char *key = ptr; - const char *value; - - while(*ptr && *ptr != '=') - ptr++; - - value = ptr + 1; - - while(*ptr && *ptr != ';') - ptr++; - - if(checkprefix("AUTH=", key)) - result = Curl_sasl_parse_url_auth_option(&li->sasl, value, ptr - value); - else - result = CURLE_SETOPT_OPTION_SYNTAX; - - if(*ptr == ';') - ptr++; - } - - return result == CURLE_URL_MALFORMAT? CURLE_SETOPT_OPTION_SYNTAX: result; -} - -static CURLcode oldap_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result; - LDAPURLDesc *lud; - struct ldapconninfo *li; - - /* Early URL syntax check. */ - result = oldap_url_parse(data, &lud); - ldap_free_urldesc(lud); - - if(!result) { - li = calloc(1, sizeof(struct ldapconninfo)); - if(!li) - result = CURLE_OUT_OF_MEMORY; - else { - li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme); - conn->proto.ldapc = li; - connkeep(conn, "OpenLDAP default"); - - /* Initialize the SASL storage */ - Curl_sasl_init(&li->sasl, data, &saslldap); - - /* Clear the TLS upgraded flag */ - conn->bits.tls_upgraded = FALSE; - - result = oldap_parse_login_options(conn); - } - } - - return result; -} - -/* - * Get the SASL authentication challenge from the server credential buffer. - */ -static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out) -{ - struct berval *servercred = data->conn->proto.ldapc->servercred; - - if(!servercred || !servercred->bv_val) - return CURLE_WEIRD_SERVER_REPLY; - Curl_bufref_set(out, servercred->bv_val, servercred->bv_len, NULL); - return CURLE_OK; -} - -/* - * Sends an initial SASL bind request to the server. - */ -static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech, - const struct bufref *initresp) -{ - struct connectdata *conn = data->conn; - struct ldapconninfo *li = conn->proto.ldapc; - CURLcode result = CURLE_OK; - struct berval cred; - struct berval *pcred = &cred; - int rc; - - cred.bv_val = (char *) Curl_bufref_ptr(initresp); - cred.bv_len = Curl_bufref_len(initresp); - if(!cred.bv_val) - pcred = NULL; - rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid); - if(rc != LDAP_SUCCESS) - result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND); - return result; -} - -/* - * Sends SASL continuation. - */ -static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech, - const struct bufref *resp) -{ - struct connectdata *conn = data->conn; - struct ldapconninfo *li = conn->proto.ldapc; - CURLcode result = CURLE_OK; - struct berval cred; - struct berval *pcred = &cred; - int rc; - - cred.bv_val = (char *) Curl_bufref_ptr(resp); - cred.bv_len = Curl_bufref_len(resp); - if(!cred.bv_val) - pcred = NULL; - rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid); - if(rc != LDAP_SUCCESS) - result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND); - return result; -} - -/* - * Sends SASL bind cancellation. - */ -static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech) -{ - struct ldapconninfo *li = data->conn->proto.ldapc; - CURLcode result = CURLE_OK; - int rc = ldap_sasl_bind(li->ld, NULL, LDAP_SASL_NULL, NULL, NULL, NULL, - &li->msgid); - - (void)mech; - if(rc != LDAP_SUCCESS) - result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND); - return result; -} - -/* Starts LDAP simple bind. */ -static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct ldapconninfo *li = conn->proto.ldapc; - char *binddn = NULL; - struct berval passwd; - int rc; - - passwd.bv_val = NULL; - passwd.bv_len = 0; - - if(data->state.aptr.user) { - binddn = conn->user; - passwd.bv_val = conn->passwd; - passwd.bv_len = strlen(passwd.bv_val); - } - - rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd, - NULL, NULL, &li->msgid); - if(rc == LDAP_SUCCESS) - state(data, newstate); - else - result = oldap_map_error(rc, - data->state.aptr.user? - CURLE_LOGIN_DENIED: CURLE_LDAP_CANNOT_BIND); - return result; -} - -/* Query the supported SASL authentication mechanisms. */ -static CURLcode oldap_perform_mechs(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct ldapconninfo *li = data->conn->proto.ldapc; - int rc; - static const char * const supportedSASLMechanisms[] = { - "supportedSASLMechanisms", - NULL - }; - - rc = ldap_search_ext(li->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)", - (char **) supportedSASLMechanisms, 0, - NULL, NULL, NULL, 0, &li->msgid); - if(rc == LDAP_SUCCESS) - state(data, OLDAP_MECHS); - else - result = oldap_map_error(rc, CURLE_LOGIN_DENIED); - return result; -} - -/* Starts SASL bind. */ -static CURLcode oldap_perform_sasl(struct Curl_easy *data) -{ - saslprogress progress = SASL_IDLE; - struct ldapconninfo *li = data->conn->proto.ldapc; - CURLcode result = Curl_sasl_start(&li->sasl, data, TRUE, &progress); - - state(data, OLDAP_SASL); - if(!result && progress != SASL_INPROGRESS) - result = CURLE_LOGIN_DENIED; - return result; -} - -#ifdef USE_SSL -static Sockbuf_IO ldapsb_tls; - -static bool ssl_installed(struct connectdata *conn) -{ - return conn->proto.ldapc->recv != NULL; -} - -static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct ldapconninfo *li = conn->proto.ldapc; - bool ssldone = 0; - - result = Curl_ssl_connect_nonblocking(data, conn, FALSE, - FIRSTSOCKET, &ssldone); - if(!result) { - state(data, newstate); - - if(ssldone) { - Sockbuf *sb; - - /* Install the libcurl SSL handlers into the sockbuf. */ - ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb); - ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data); - li->recv = conn->recv[FIRSTSOCKET]; - li->send = conn->send[FIRSTSOCKET]; - } - } - - return result; -} - -/* Send the STARTTLS request */ -static CURLcode oldap_perform_starttls(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct ldapconninfo *li = data->conn->proto.ldapc; - int rc = ldap_start_tls(li->ld, NULL, NULL, &li->msgid); - - if(rc == LDAP_SUCCESS) - state(data, OLDAP_STARTTLS); - else - result = oldap_map_error(rc, CURLE_USE_SSL_FAILED); - return result; -} -#endif - -static CURLcode oldap_connect(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - struct ldapconninfo *li = conn->proto.ldapc; - static const int version = LDAP_VERSION3; - int rc; - char *hosturl; -#ifdef CURL_OPENLDAP_DEBUG - static int do_trace = -1; -#endif - - (void)done; - - hosturl = aprintf("ldap%s://%s:%d", - conn->handler->flags & PROTOPT_SSL? "s": "", - conn->host.name, conn->remote_port); - if(!hosturl) - return CURLE_OUT_OF_MEMORY; - - rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld); - if(rc) { - failf(data, "LDAP local: Cannot connect to %s, %s", - hosturl, ldap_err2string(rc)); - free(hosturl); - return CURLE_COULDNT_CONNECT; - } - - free(hosturl); - -#ifdef CURL_OPENLDAP_DEBUG - if(do_trace < 0) { - const char *env = getenv("CURL_OPENLDAP_TRACE"); - do_trace = (env && strtol(env, NULL, 10) > 0); - } - if(do_trace) - ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace); -#endif - - /* Try version 3 first. */ - ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version); - - /* Do not chase referrals. */ - ldap_set_option(li->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); - -#ifdef USE_SSL - if(conn->handler->flags & PROTOPT_SSL) - return oldap_ssl_connect(data, OLDAP_SSL); - - if(data->set.use_ssl) { - CURLcode result = oldap_perform_starttls(data); - - if(!result || data->set.use_ssl != CURLUSESSL_TRY) - return result; - } -#endif - - if(li->sasl.prefmech != SASL_AUTH_NONE) - return oldap_perform_mechs(data); - - /* Force bind even if anonymous bind is not needed in protocol version 3 - to detect missing version 3 support. */ - return oldap_perform_bind(data, OLDAP_BIND); -} - -/* Handle the supported SASL mechanisms query response */ -static CURLcode oldap_state_mechs_resp(struct Curl_easy *data, - LDAPMessage *msg, int code) -{ - struct connectdata *conn = data->conn; - struct ldapconninfo *li = conn->proto.ldapc; - int rc; - BerElement *ber = NULL; - CURLcode result = CURLE_OK; - struct berval bv, *bvals; - - switch(ldap_msgtype(msg)) { - case LDAP_RES_SEARCH_ENTRY: - /* Got a list of supported SASL mechanisms. */ - if(code != LDAP_SUCCESS && code != LDAP_NO_RESULTS_RETURNED) - return CURLE_LOGIN_DENIED; - - rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv); - if(rc < 0) - return oldap_map_error(rc, CURLE_BAD_CONTENT_ENCODING); - for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals); - rc == LDAP_SUCCESS; - rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) { - int i; - - if(!bv.bv_val) - break; - - if(bvals) { - for(i = 0; bvals[i].bv_val; i++) { - size_t llen; - unsigned short mech = Curl_sasl_decode_mech((char *) bvals[i].bv_val, - bvals[i].bv_len, &llen); - if(bvals[i].bv_len == llen) - li->sasl.authmechs |= mech; - } - ber_memfree(bvals); - } - } - ber_free(ber, 0); - break; - - case LDAP_RES_SEARCH_RESULT: - switch(code) { - case LDAP_SIZELIMIT_EXCEEDED: - infof(data, "Too many authentication mechanisms\n"); - /* FALLTHROUGH */ - case LDAP_SUCCESS: - case LDAP_NO_RESULTS_RETURNED: - if(Curl_sasl_can_authenticate(&li->sasl, data)) - result = oldap_perform_sasl(data); - else - result = CURLE_LOGIN_DENIED; - break; - default: - result = oldap_map_error(code, CURLE_LOGIN_DENIED); - break; - } - break; - default: - break; - } - return result; -} - -/* Handle a SASL bind response. */ -static CURLcode oldap_state_sasl_resp(struct Curl_easy *data, - LDAPMessage *msg, int code) -{ - struct connectdata *conn = data->conn; - struct ldapconninfo *li = conn->proto.ldapc; - CURLcode result = CURLE_OK; - saslprogress progress; - int rc; - - li->servercred = NULL; - rc = ldap_parse_sasl_bind_result(li->ld, msg, &li->servercred, 0); - if(rc != LDAP_SUCCESS) { - failf(data, "LDAP local: sasl ldap_parse_result %s", ldap_err2string(rc)); - result = oldap_map_error(rc, CURLE_LOGIN_DENIED); - } - else { - result = Curl_sasl_continue(&li->sasl, data, code, &progress); - if(!result && progress != SASL_INPROGRESS) - state(data, OLDAP_STOP); - } - - if(li->servercred) - ber_bvfree(li->servercred); - return result; -} - -/* Handle a simple bind response. */ -static CURLcode oldap_state_bind_resp(struct Curl_easy *data, LDAPMessage *msg, - int code) -{ - struct connectdata *conn = data->conn; - struct ldapconninfo *li = conn->proto.ldapc; - CURLcode result = CURLE_OK; - struct berval *bv = NULL; - int rc; - - if(code != LDAP_SUCCESS) - return oldap_map_error(code, CURLE_LDAP_CANNOT_BIND); - - rc = ldap_parse_sasl_bind_result(li->ld, msg, &bv, 0); - if(rc != LDAP_SUCCESS) { - failf(data, "LDAP local: bind ldap_parse_sasl_bind_result %s", - ldap_err2string(rc)); - result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND); - } - else - state(data, OLDAP_STOP); - - if(bv) - ber_bvfree(bv); - return result; -} - -static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct ldapconninfo *li = conn->proto.ldapc; - LDAPMessage *msg = NULL; - struct timeval tv = {0, 0}; - int code = LDAP_SUCCESS; - int rc; - - if(li->state != OLDAP_SSL && li->state != OLDAP_TLS) { - /* Get response to last command. */ - rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, &tv, &msg); - switch(rc) { - case 0: /* Timed out. */ - return CURLE_OK; - case LDAP_RES_SEARCH_ENTRY: - case LDAP_RES_SEARCH_REFERENCE: - break; - default: - li->msgid = 0; /* Nothing to abandon upon error. */ - if(rc < 0) { - failf(data, "LDAP local: connecting ldap_result %s", - ldap_err2string(rc)); - return oldap_map_error(rc, CURLE_COULDNT_CONNECT); - } - break; - } - - /* Get error code from message. */ - rc = ldap_parse_result(li->ld, msg, &code, NULL, NULL, NULL, NULL, 0); - if(rc) - code = rc; - else { - /* store the latest code for later retrieval */ - data->info.httpcode = code; - } - - /* If protocol version 3 is not supported, fallback to version 2. */ - if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2 && -#ifdef USE_SSL - (ssl_installed(conn) || data->set.use_ssl <= CURLUSESSL_TRY) && -#endif - li->sasl.prefmech == SASL_AUTH_NONE) { - static const int version = LDAP_VERSION2; - - ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version); - ldap_msgfree(msg); - return oldap_perform_bind(data, OLDAP_BINDV2); - } - } - - /* Handle response message according to current state. */ - switch(li->state) { - -#ifdef USE_SSL - case OLDAP_SSL: - result = oldap_ssl_connect(data, OLDAP_SSL); - if(!result && ssl_installed(conn)) { - if(li->sasl.prefmech != SASL_AUTH_NONE) - result = oldap_perform_mechs(data); - else - result = oldap_perform_bind(data, OLDAP_BIND); - } - break; - case OLDAP_STARTTLS: - if(code != LDAP_SUCCESS) { - if(data->set.use_ssl != CURLUSESSL_TRY) - result = oldap_map_error(code, CURLE_USE_SSL_FAILED); - else if(li->sasl.prefmech != SASL_AUTH_NONE) - result = oldap_perform_mechs(data); - else - result = oldap_perform_bind(data, OLDAP_BIND); - break; - } - /* FALLTHROUGH */ - case OLDAP_TLS: - result = oldap_ssl_connect(data, OLDAP_TLS); - if(result && data->set.use_ssl != CURLUSESSL_TRY) - result = oldap_map_error(code, CURLE_USE_SSL_FAILED); - else if(ssl_installed(conn)) { - conn->bits.tls_upgraded = TRUE; - if(li->sasl.prefmech != SASL_AUTH_NONE) - result = oldap_perform_mechs(data); - else if(data->state.aptr.user) - result = oldap_perform_bind(data, OLDAP_BIND); - else { - state(data, OLDAP_STOP); /* Version 3 supported: no bind required */ - result = CURLE_OK; - } - } - break; -#endif - - case OLDAP_MECHS: - result = oldap_state_mechs_resp(data, msg, code); - break; - case OLDAP_SASL: - result = oldap_state_sasl_resp(data, msg, code); - break; - case OLDAP_BIND: - case OLDAP_BINDV2: - result = oldap_state_bind_resp(data, msg, code); - break; - default: - /* internal error */ - result = CURLE_COULDNT_CONNECT; - break; - } - - ldap_msgfree(msg); - - *done = li->state == OLDAP_STOP; - if(*done) - conn->recv[FIRSTSOCKET] = oldap_recv; - - if(result && li->msgid) { - ldap_abandon_ext(li->ld, li->msgid, NULL, NULL); - li->msgid = 0; - } - return result; -} - -static CURLcode oldap_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection) -{ - struct ldapconninfo *li = conn->proto.ldapc; - (void) dead_connection; -#ifndef USE_SSL - (void)data; -#endif - - if(li) { - if(li->ld) { -#ifdef USE_SSL - if(ssl_installed(conn)) { - Sockbuf *sb; - ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb); - ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data); - } -#endif - ldap_unbind_ext(li->ld, NULL, NULL); - li->ld = NULL; - } - Curl_sasl_cleanup(conn, li->sasl.authused); - conn->proto.ldapc = NULL; - free(li); - } - return CURLE_OK; -} - -static CURLcode oldap_do(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - struct ldapconninfo *li = conn->proto.ldapc; - struct ldapreqinfo *lr; - CURLcode result; - int rc; - LDAPURLDesc *lud; - int msgid; - - connkeep(conn, "OpenLDAP do"); - - infof(data, "LDAP local: %s", data->state.url); - - result = oldap_url_parse(data, &lud); - if(!result) { - rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope, - lud->lud_filter, lud->lud_attrs, 0, - NULL, NULL, NULL, 0, &msgid); - ldap_free_urldesc(lud); - if(rc != LDAP_SUCCESS) { - failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc)); - result = CURLE_LDAP_SEARCH_FAILED; - } - else { - lr = calloc(1, sizeof(struct ldapreqinfo)); - if(!lr) { - ldap_abandon_ext(li->ld, msgid, NULL, NULL); - result = CURLE_OUT_OF_MEMORY; - } - else { - lr->msgid = msgid; - data->req.p.ldap = lr; - Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); - *done = TRUE; - } - } - } - return result; -} - -static CURLcode oldap_done(struct Curl_easy *data, CURLcode res, - bool premature) -{ - struct connectdata *conn = data->conn; - struct ldapreqinfo *lr = data->req.p.ldap; - - (void)res; - (void)premature; - - if(lr) { - /* if there was a search in progress, abandon it */ - if(lr->msgid) { - struct ldapconninfo *li = conn->proto.ldapc; - ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL); - lr->msgid = 0; - } - data->req.p.ldap = NULL; - free(lr); - } - - return CURLE_OK; -} - -static CURLcode client_write(struct Curl_easy *data, - const char *prefix, size_t plen, - const char *value, size_t len, - const char *suffix, size_t slen) -{ - CURLcode result = CURLE_OK; - - if(prefix) { - /* If we have a zero-length value and the prefix ends with a space - separator, drop the latter. */ - if(!len && plen && prefix[plen - 1] == ' ') - plen--; - result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, plen); - if(!result) - data->req.bytecount += plen; - } - if(!result && value) { - result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) value, len); - if(!result) - data->req.bytecount += len; - } - if(!result && suffix) { - result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, slen); - if(!result) - data->req.bytecount += slen; - } - return result; -} - -static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf, - size_t len, CURLcode *err) -{ - struct connectdata *conn = data->conn; - struct ldapconninfo *li = conn->proto.ldapc; - struct ldapreqinfo *lr = data->req.p.ldap; - int rc; - LDAPMessage *msg = NULL; - BerElement *ber = NULL; - struct timeval tv = {0, 0}; - struct berval bv, *bvals; - int binary = 0; - CURLcode result = CURLE_AGAIN; - int code; - char *info = NULL; - - (void)len; - (void)buf; - (void)sockindex; - - rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_ONE, &tv, &msg); - if(rc < 0) { - failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc)); - result = CURLE_RECV_ERROR; - } - - *err = result; - - /* error or timed out */ - if(!msg) - return -1; - - result = CURLE_OK; - - switch(ldap_msgtype(msg)) { - case LDAP_RES_SEARCH_RESULT: - lr->msgid = 0; - rc = ldap_parse_result(li->ld, msg, &code, NULL, &info, NULL, NULL, 0); - if(rc) { - failf(data, "LDAP local: search ldap_parse_result %s", - ldap_err2string(rc)); - result = CURLE_LDAP_SEARCH_FAILED; - break; - } - - /* store the latest code for later retrieval */ - data->info.httpcode = code; - - switch(code) { - case LDAP_SIZELIMIT_EXCEEDED: - infof(data, "There are more than %d entries", lr->nument); - /* FALLTHROUGH */ - case LDAP_SUCCESS: - data->req.size = data->req.bytecount; - break; - default: - failf(data, "LDAP remote: search failed %s %s", ldap_err2string(code), - info ? info : ""); - result = CURLE_LDAP_SEARCH_FAILED; - break; - } - if(info) - ldap_memfree(info); - break; - case LDAP_RES_SEARCH_ENTRY: - lr->nument++; - rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv); - if(rc < 0) { - result = CURLE_RECV_ERROR; - break; - } - - result = client_write(data, STRCONST("DN: "), bv.bv_val, bv.bv_len, - STRCONST("\n")); - if(result) - break; - - for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals); - rc == LDAP_SUCCESS; - rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) { - int i; - - if(!bv.bv_val) - break; - - if(!bvals) { - result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len, - STRCONST(":\n")); - if(result) - break; - continue; - } - - binary = bv.bv_len > 7 && - !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7); - - for(i = 0; bvals[i].bv_val != NULL; i++) { - int binval = 0; - - result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len, - STRCONST(":")); - if(result) - break; - - if(!binary) { - /* check for leading or trailing whitespace */ - if(ISBLANK(bvals[i].bv_val[0]) || - ISBLANK(bvals[i].bv_val[bvals[i].bv_len - 1])) - binval = 1; - else { - /* check for unprintable characters */ - unsigned int j; - for(j = 0; j < bvals[i].bv_len; j++) - if(!ISPRINT(bvals[i].bv_val[j])) { - binval = 1; - break; - } - } - } - if(binary || binval) { - char *val_b64 = NULL; - size_t val_b64_sz = 0; - - /* Binary value, encode to base64. */ - if(bvals[i].bv_len) - result = Curl_base64_encode(bvals[i].bv_val, bvals[i].bv_len, - &val_b64, &val_b64_sz); - if(!result) - result = client_write(data, STRCONST(": "), val_b64, val_b64_sz, - STRCONST("\n")); - free(val_b64); - } - else - result = client_write(data, STRCONST(" "), - bvals[i].bv_val, bvals[i].bv_len, - STRCONST("\n")); - if(result) - break; - } - - ber_memfree(bvals); - bvals = NULL; - if(!result) - result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0); - if(result) - break; - } - - ber_free(ber, 0); - - if(!result) - result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0); - if(!result) - result = CURLE_AGAIN; - break; - } - - ldap_msgfree(msg); - *err = result; - return result? -1: 0; -} - -#ifdef USE_SSL -static int -ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg) -{ - sbiod->sbiod_pvt = arg; - return 0; -} - -static int -ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod) -{ - sbiod->sbiod_pvt = NULL; - return 0; -} - -/* We don't need to do anything because libcurl does it already */ -static int -ldapsb_tls_close(Sockbuf_IO_Desc *sbiod) -{ - (void)sbiod; - return 0; -} - -static int -ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg) -{ - (void)arg; - if(opt == LBER_SB_OPT_DATA_READY) { - struct Curl_easy *data = sbiod->sbiod_pvt; - return Curl_ssl_data_pending(data->conn, FIRSTSOCKET); - } - return 0; -} - -static ber_slen_t -ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) -{ - struct Curl_easy *data = sbiod->sbiod_pvt; - ber_slen_t ret = 0; - if(data) { - struct connectdata *conn = data->conn; - if(conn) { - struct ldapconninfo *li = conn->proto.ldapc; - CURLcode err = CURLE_RECV_ERROR; - - ret = (li->recv)(data, FIRSTSOCKET, buf, len, &err); - if(ret < 0 && err == CURLE_AGAIN) { - SET_SOCKERRNO(EWOULDBLOCK); - } - } - } - return ret; -} - -static ber_slen_t -ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) -{ - struct Curl_easy *data = sbiod->sbiod_pvt; - ber_slen_t ret = 0; - if(data) { - struct connectdata *conn = data->conn; - if(conn) { - struct ldapconninfo *li = conn->proto.ldapc; - CURLcode err = CURLE_SEND_ERROR; - ret = (li->send)(data, FIRSTSOCKET, buf, len, &err); - if(ret < 0 && err == CURLE_AGAIN) { - SET_SOCKERRNO(EWOULDBLOCK); - } - } - } - return ret; -} - -static Sockbuf_IO ldapsb_tls = -{ - ldapsb_tls_setup, - ldapsb_tls_remove, - ldapsb_tls_ctrl, - ldapsb_tls_read, - ldapsb_tls_write, - ldapsb_tls_close -}; -#endif /* USE_SSL */ - -#endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2011 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2010, Howard Chu, <hyc@openldap.org>
+ *
+ * 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_LDAP) && defined(USE_OPENLDAP)
+
+/*
+ * Notice that USE_OPENLDAP is only a source code selection switch. When
+ * libcurl is built with USE_OPENLDAP defined the libcurl source code that
+ * gets compiled is the code from openldap.c, otherwise the code that gets
+ * compiled is the code from ldap.c.
+ *
+ * When USE_OPENLDAP is defined a recent version of the OpenLDAP library
+ * might be required for compilation and runtime. In order to use ancient
+ * OpenLDAP library versions, USE_OPENLDAP shall not be defined.
+ */
+
+#include <ldap.h>
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "sendf.h"
+#include "vtls/vtls.h"
+#include "transfer.h"
+#include "curl_ldap.h"
+#include "curl_base64.h"
+#include "cfilters.h"
+#include "connect.h"
+#include "curl_sasl.h"
+#include "strcase.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * Uncommenting this will enable the built-in debug logging of the openldap
+ * library. The debug log level can be set using the CURL_OPENLDAP_TRACE
+ * environment variable. The debug output is written to stderr.
+ *
+ * The library supports the following debug flags:
+ * LDAP_DEBUG_NONE 0x0000
+ * LDAP_DEBUG_TRACE 0x0001
+ * LDAP_DEBUG_CONSTRUCT 0x0002
+ * LDAP_DEBUG_DESTROY 0x0004
+ * LDAP_DEBUG_PARAMETER 0x0008
+ * LDAP_DEBUG_ANY 0xffff
+ *
+ * For example, use CURL_OPENLDAP_TRACE=0 for no debug,
+ * CURL_OPENLDAP_TRACE=2 for LDAP_DEBUG_CONSTRUCT messages only,
+ * CURL_OPENLDAP_TRACE=65535 for all debug message levels.
+ */
+/* #define CURL_OPENLDAP_DEBUG */
+
+/* Machine states. */
+typedef enum {
+ OLDAP_STOP, /* Do nothing state, stops the state machine */
+ OLDAP_SSL, /* Performing SSL handshake. */
+ OLDAP_STARTTLS, /* STARTTLS request sent. */
+ OLDAP_TLS, /* Performing TLS handshake. */
+ OLDAP_MECHS, /* Get SASL authentication mechanisms. */
+ OLDAP_SASL, /* SASL binding reply. */
+ OLDAP_BIND, /* Simple bind reply. */
+ OLDAP_BINDV2, /* Simple bind reply in protocol version 2. */
+ OLDAP_LAST /* Never used */
+} ldapstate;
+
+#ifndef _LDAP_PVT_H
+extern int ldap_pvt_url_scheme2proto(const char *);
+extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url,
+ LDAP **ld);
+#endif
+
+static CURLcode oldap_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn);
+static CURLcode oldap_do(struct Curl_easy *data, bool *done);
+static CURLcode oldap_done(struct Curl_easy *data, CURLcode, bool);
+static CURLcode oldap_connect(struct Curl_easy *data, bool *done);
+static CURLcode oldap_connecting(struct Curl_easy *data, bool *done);
+static CURLcode oldap_disconnect(struct Curl_easy *data,
+ struct connectdata *conn, bool dead);
+
+static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech,
+ const struct bufref *initresp);
+static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech,
+ const struct bufref *resp);
+static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech);
+static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out);
+
+static Curl_recv oldap_recv;
+
+/*
+ * LDAP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_ldap = {
+ "LDAP", /* scheme */
+ oldap_setup_connection, /* setup_connection */
+ oldap_do, /* do_it */
+ oldap_done, /* done */
+ ZERO_NULL, /* do_more */
+ oldap_connect, /* connect_it */
+ oldap_connecting, /* connecting */
+ ZERO_NULL, /* doing */
+ ZERO_NULL, /* proto_getsock */
+ ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ oldap_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_LDAP, /* defport */
+ CURLPROTO_LDAP, /* protocol */
+ CURLPROTO_LDAP, /* family */
+ PROTOPT_NONE /* flags */
+};
+
+#ifdef USE_SSL
+/*
+ * LDAPS protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_ldaps = {
+ "LDAPS", /* scheme */
+ oldap_setup_connection, /* setup_connection */
+ oldap_do, /* do_it */
+ oldap_done, /* done */
+ ZERO_NULL, /* do_more */
+ oldap_connect, /* connect_it */
+ oldap_connecting, /* connecting */
+ ZERO_NULL, /* doing */
+ ZERO_NULL, /* proto_getsock */
+ ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ oldap_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_LDAPS, /* defport */
+ CURLPROTO_LDAPS, /* protocol */
+ CURLPROTO_LDAP, /* family */
+ PROTOPT_SSL /* flags */
+};
+#endif
+
+/* SASL parameters for the ldap protocol */
+static const struct SASLproto saslldap = {
+ "ldap", /* The service name */
+ oldap_perform_auth, /* Send authentication command */
+ oldap_continue_auth, /* Send authentication continuation */
+ oldap_cancel_auth, /* Send authentication cancellation */
+ oldap_get_message, /* Get SASL response message */
+ 0, /* Maximum initial response length (no max) */
+ LDAP_SASL_BIND_IN_PROGRESS, /* Code received when continuation is expected */
+ LDAP_SUCCESS, /* Code to receive upon authentication success */
+ SASL_AUTH_NONE, /* Default mechanisms */
+ 0 /* Configuration flags */
+};
+
+struct ldapconninfo {
+ struct SASL sasl; /* SASL-related parameters */
+ LDAP *ld; /* Openldap connection handle. */
+ Curl_recv *recv; /* For stacking SSL handler */
+ Curl_send *send;
+ struct berval *servercred; /* SASL data from server. */
+ ldapstate state; /* Current machine state. */
+ int proto; /* LDAP_PROTO_TCP/LDAP_PROTO_UDP/LDAP_PROTO_IPC */
+ int msgid; /* Current message id. */
+};
+
+struct ldapreqinfo {
+ int msgid;
+ int nument;
+};
+
+/*
+ * state()
+ *
+ * This is the ONLY way to change LDAP state!
+ */
+static void state(struct Curl_easy *data, ldapstate newstate)
+{
+ struct ldapconninfo *ldapc = data->conn->proto.ldapc;
+
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ /* for debug purposes */
+ static const char * const names[] = {
+ "STOP",
+ "SSL",
+ "STARTTLS",
+ "TLS",
+ "MECHS",
+ "SASL",
+ "BIND",
+ "BINDV2",
+ /* LAST */
+ };
+
+ if(ldapc->state != newstate)
+ infof(data, "LDAP %p state change from %s to %s",
+ (void *)ldapc, names[ldapc->state], names[newstate]);
+#endif
+
+ ldapc->state = newstate;
+}
+
+/* Map some particular LDAP error codes to CURLcode values. */
+static CURLcode oldap_map_error(int rc, CURLcode result)
+{
+ switch(rc) {
+ case LDAP_NO_MEMORY:
+ result = CURLE_OUT_OF_MEMORY;
+ break;
+ case LDAP_INVALID_CREDENTIALS:
+ result = CURLE_LOGIN_DENIED;
+ break;
+ case LDAP_PROTOCOL_ERROR:
+ result = CURLE_UNSUPPORTED_PROTOCOL;
+ break;
+ case LDAP_INSUFFICIENT_ACCESS:
+ result = CURLE_REMOTE_ACCESS_DENIED;
+ break;
+ }
+ return result;
+}
+
+static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp)
+{
+ CURLcode result = CURLE_OK;
+ int rc = LDAP_URL_ERR_BADURL;
+ static const char * const url_errs[] = {
+ "success",
+ "out of memory",
+ "bad parameter",
+ "unrecognized scheme",
+ "unbalanced delimiter",
+ "bad URL",
+ "bad host or port",
+ "bad or missing attributes",
+ "bad or missing scope",
+ "bad or missing filter",
+ "bad or missing extensions"
+ };
+
+ *ludp = NULL;
+ if(!data->state.up.user && !data->state.up.password &&
+ !data->state.up.options)
+ rc = ldap_url_parse(data->state.url, ludp);
+ if(rc != LDAP_URL_SUCCESS) {
+ const char *msg = "url parsing problem";
+
+ result = rc == LDAP_URL_ERR_MEM? CURLE_OUT_OF_MEMORY: CURLE_URL_MALFORMAT;
+ rc -= LDAP_URL_SUCCESS;
+ if((size_t) rc < sizeof(url_errs) / sizeof(url_errs[0]))
+ msg = url_errs[rc];
+ failf(data, "LDAP local: %s", msg);
+ }
+ return result;
+}
+
+/* Parse the login options. */
+static CURLcode oldap_parse_login_options(struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct ldapconninfo *li = conn->proto.ldapc;
+ const char *ptr = conn->options;
+
+ while(!result && ptr && *ptr) {
+ const char *key = ptr;
+ const char *value;
+
+ while(*ptr && *ptr != '=')
+ ptr++;
+
+ value = ptr + 1;
+
+ while(*ptr && *ptr != ';')
+ ptr++;
+
+ if(checkprefix("AUTH=", key))
+ result = Curl_sasl_parse_url_auth_option(&li->sasl, value, ptr - value);
+ else
+ result = CURLE_SETOPT_OPTION_SYNTAX;
+
+ if(*ptr == ';')
+ ptr++;
+ }
+
+ return result == CURLE_URL_MALFORMAT? CURLE_SETOPT_OPTION_SYNTAX: result;
+}
+
+static CURLcode oldap_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result;
+ LDAPURLDesc *lud;
+ struct ldapconninfo *li;
+
+ /* Early URL syntax check. */
+ result = oldap_url_parse(data, &lud);
+ ldap_free_urldesc(lud);
+
+ if(!result) {
+ li = calloc(1, sizeof(struct ldapconninfo));
+ if(!li)
+ result = CURLE_OUT_OF_MEMORY;
+ else {
+ li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme);
+ conn->proto.ldapc = li;
+ connkeep(conn, "OpenLDAP default");
+
+ /* Initialize the SASL storage */
+ Curl_sasl_init(&li->sasl, data, &saslldap);
+
+ /* Clear the TLS upgraded flag */
+ conn->bits.tls_upgraded = FALSE;
+
+ result = oldap_parse_login_options(conn);
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Get the SASL authentication challenge from the server credential buffer.
+ */
+static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out)
+{
+ struct berval *servercred = data->conn->proto.ldapc->servercred;
+
+ if(!servercred || !servercred->bv_val)
+ return CURLE_WEIRD_SERVER_REPLY;
+ Curl_bufref_set(out, servercred->bv_val, servercred->bv_len, NULL);
+ return CURLE_OK;
+}
+
+/*
+ * Sends an initial SASL bind request to the server.
+ */
+static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech,
+ const struct bufref *initresp)
+{
+ struct connectdata *conn = data->conn;
+ struct ldapconninfo *li = conn->proto.ldapc;
+ CURLcode result = CURLE_OK;
+ struct berval cred;
+ struct berval *pcred = &cred;
+ int rc;
+
+ cred.bv_val = (char *) Curl_bufref_ptr(initresp);
+ cred.bv_len = Curl_bufref_len(initresp);
+ if(!cred.bv_val)
+ pcred = NULL;
+ rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid);
+ if(rc != LDAP_SUCCESS)
+ result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
+ return result;
+}
+
+/*
+ * Sends SASL continuation.
+ */
+static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech,
+ const struct bufref *resp)
+{
+ struct connectdata *conn = data->conn;
+ struct ldapconninfo *li = conn->proto.ldapc;
+ CURLcode result = CURLE_OK;
+ struct berval cred;
+ struct berval *pcred = &cred;
+ int rc;
+
+ cred.bv_val = (char *) Curl_bufref_ptr(resp);
+ cred.bv_len = Curl_bufref_len(resp);
+ if(!cred.bv_val)
+ pcred = NULL;
+ rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid);
+ if(rc != LDAP_SUCCESS)
+ result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
+ return result;
+}
+
+/*
+ * Sends SASL bind cancellation.
+ */
+static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech)
+{
+ struct ldapconninfo *li = data->conn->proto.ldapc;
+ CURLcode result = CURLE_OK;
+ int rc = ldap_sasl_bind(li->ld, NULL, LDAP_SASL_NULL, NULL, NULL, NULL,
+ &li->msgid);
+
+ (void)mech;
+ if(rc != LDAP_SUCCESS)
+ result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
+ return result;
+}
+
+/* Starts LDAP simple bind. */
+static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct ldapconninfo *li = conn->proto.ldapc;
+ char *binddn = NULL;
+ struct berval passwd;
+ int rc;
+
+ passwd.bv_val = NULL;
+ passwd.bv_len = 0;
+
+ if(data->state.aptr.user) {
+ binddn = conn->user;
+ passwd.bv_val = conn->passwd;
+ passwd.bv_len = strlen(passwd.bv_val);
+ }
+
+ rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
+ NULL, NULL, &li->msgid);
+ if(rc == LDAP_SUCCESS)
+ state(data, newstate);
+ else
+ result = oldap_map_error(rc,
+ data->state.aptr.user?
+ CURLE_LOGIN_DENIED: CURLE_LDAP_CANNOT_BIND);
+ return result;
+}
+
+/* Query the supported SASL authentication mechanisms. */
+static CURLcode oldap_perform_mechs(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct ldapconninfo *li = data->conn->proto.ldapc;
+ int rc;
+ static const char * const supportedSASLMechanisms[] = {
+ "supportedSASLMechanisms",
+ NULL
+ };
+
+ rc = ldap_search_ext(li->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
+ (char **) supportedSASLMechanisms, 0,
+ NULL, NULL, NULL, 0, &li->msgid);
+ if(rc == LDAP_SUCCESS)
+ state(data, OLDAP_MECHS);
+ else
+ result = oldap_map_error(rc, CURLE_LOGIN_DENIED);
+ return result;
+}
+
+/* Starts SASL bind. */
+static CURLcode oldap_perform_sasl(struct Curl_easy *data)
+{
+ saslprogress progress = SASL_IDLE;
+ struct ldapconninfo *li = data->conn->proto.ldapc;
+ CURLcode result = Curl_sasl_start(&li->sasl, data, TRUE, &progress);
+
+ state(data, OLDAP_SASL);
+ if(!result && progress != SASL_INPROGRESS)
+ result = CURLE_LOGIN_DENIED;
+ return result;
+}
+
+#ifdef USE_SSL
+static Sockbuf_IO ldapsb_tls;
+
+static bool ssl_installed(struct connectdata *conn)
+{
+ return conn->proto.ldapc->recv != NULL;
+}
+
+static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct ldapconninfo *li = conn->proto.ldapc;
+ bool ssldone = 0;
+
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
+ if(!result) {
+ state(data, newstate);
+
+ if(ssldone) {
+ Sockbuf *sb;
+
+ /* Install the libcurl SSL handlers into the sockbuf. */
+ ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
+ ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
+ li->recv = conn->recv[FIRSTSOCKET];
+ li->send = conn->send[FIRSTSOCKET];
+ }
+ }
+
+ return result;
+}
+
+/* Send the STARTTLS request */
+static CURLcode oldap_perform_starttls(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct ldapconninfo *li = data->conn->proto.ldapc;
+ int rc = ldap_start_tls(li->ld, NULL, NULL, &li->msgid);
+
+ if(rc == LDAP_SUCCESS)
+ state(data, OLDAP_STARTTLS);
+ else
+ result = oldap_map_error(rc, CURLE_USE_SSL_FAILED);
+ return result;
+}
+#endif
+
+static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
+{
+ struct connectdata *conn = data->conn;
+ struct ldapconninfo *li = conn->proto.ldapc;
+ static const int version = LDAP_VERSION3;
+ int rc;
+ char *hosturl;
+#ifdef CURL_OPENLDAP_DEBUG
+ static int do_trace = -1;
+#endif
+
+ (void)done;
+
+ hosturl = aprintf("ldap%s://%s:%d",
+ conn->handler->flags & PROTOPT_SSL? "s": "",
+ conn->host.name, conn->remote_port);
+ if(!hosturl)
+ return CURLE_OUT_OF_MEMORY;
+
+ rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld);
+ if(rc) {
+ failf(data, "LDAP local: Cannot connect to %s, %s",
+ hosturl, ldap_err2string(rc));
+ free(hosturl);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ free(hosturl);
+
+#ifdef CURL_OPENLDAP_DEBUG
+ if(do_trace < 0) {
+ const char *env = getenv("CURL_OPENLDAP_TRACE");
+ do_trace = (env && strtol(env, NULL, 10) > 0);
+ }
+ if(do_trace)
+ ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace);
+#endif
+
+ /* Try version 3 first. */
+ ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
+
+ /* Do not chase referrals. */
+ ldap_set_option(li->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
+
+#ifdef USE_SSL
+ if(conn->handler->flags & PROTOPT_SSL)
+ return oldap_ssl_connect(data, OLDAP_SSL);
+
+ if(data->set.use_ssl) {
+ CURLcode result = oldap_perform_starttls(data);
+
+ if(!result || data->set.use_ssl != CURLUSESSL_TRY)
+ return result;
+ }
+#endif
+
+ if(li->sasl.prefmech != SASL_AUTH_NONE)
+ return oldap_perform_mechs(data);
+
+ /* Force bind even if anonymous bind is not needed in protocol version 3
+ to detect missing version 3 support. */
+ return oldap_perform_bind(data, OLDAP_BIND);
+}
+
+/* Handle the supported SASL mechanisms query response */
+static CURLcode oldap_state_mechs_resp(struct Curl_easy *data,
+ LDAPMessage *msg, int code)
+{
+ struct connectdata *conn = data->conn;
+ struct ldapconninfo *li = conn->proto.ldapc;
+ int rc;
+ BerElement *ber = NULL;
+ CURLcode result = CURLE_OK;
+ struct berval bv, *bvals;
+
+ switch(ldap_msgtype(msg)) {
+ case LDAP_RES_SEARCH_ENTRY:
+ /* Got a list of supported SASL mechanisms. */
+ if(code != LDAP_SUCCESS && code != LDAP_NO_RESULTS_RETURNED)
+ return CURLE_LOGIN_DENIED;
+
+ rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
+ if(rc < 0)
+ return oldap_map_error(rc, CURLE_BAD_CONTENT_ENCODING);
+ for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
+ rc == LDAP_SUCCESS;
+ rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
+ int i;
+
+ if(!bv.bv_val)
+ break;
+
+ if(bvals) {
+ for(i = 0; bvals[i].bv_val; i++) {
+ size_t llen;
+ unsigned short mech = Curl_sasl_decode_mech((char *) bvals[i].bv_val,
+ bvals[i].bv_len, &llen);
+ if(bvals[i].bv_len == llen)
+ li->sasl.authmechs |= mech;
+ }
+ ber_memfree(bvals);
+ }
+ }
+ ber_free(ber, 0);
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ switch(code) {
+ case LDAP_SIZELIMIT_EXCEEDED:
+ infof(data, "Too many authentication mechanisms\n");
+ /* FALLTHROUGH */
+ case LDAP_SUCCESS:
+ case LDAP_NO_RESULTS_RETURNED:
+ if(Curl_sasl_can_authenticate(&li->sasl, data))
+ result = oldap_perform_sasl(data);
+ else
+ result = CURLE_LOGIN_DENIED;
+ break;
+ default:
+ result = oldap_map_error(code, CURLE_LOGIN_DENIED);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+/* Handle a SASL bind response. */
+static CURLcode oldap_state_sasl_resp(struct Curl_easy *data,
+ LDAPMessage *msg, int code)
+{
+ struct connectdata *conn = data->conn;
+ struct ldapconninfo *li = conn->proto.ldapc;
+ CURLcode result = CURLE_OK;
+ saslprogress progress;
+ int rc;
+
+ li->servercred = NULL;
+ rc = ldap_parse_sasl_bind_result(li->ld, msg, &li->servercred, 0);
+ if(rc != LDAP_SUCCESS) {
+ failf(data, "LDAP local: sasl ldap_parse_result %s", ldap_err2string(rc));
+ result = oldap_map_error(rc, CURLE_LOGIN_DENIED);
+ }
+ else {
+ result = Curl_sasl_continue(&li->sasl, data, code, &progress);
+ if(!result && progress != SASL_INPROGRESS)
+ state(data, OLDAP_STOP);
+ }
+
+ if(li->servercred)
+ ber_bvfree(li->servercred);
+ return result;
+}
+
+/* Handle a simple bind response. */
+static CURLcode oldap_state_bind_resp(struct Curl_easy *data, LDAPMessage *msg,
+ int code)
+{
+ struct connectdata *conn = data->conn;
+ struct ldapconninfo *li = conn->proto.ldapc;
+ CURLcode result = CURLE_OK;
+ struct berval *bv = NULL;
+ int rc;
+
+ if(code != LDAP_SUCCESS)
+ return oldap_map_error(code, CURLE_LDAP_CANNOT_BIND);
+
+ rc = ldap_parse_sasl_bind_result(li->ld, msg, &bv, 0);
+ if(rc != LDAP_SUCCESS) {
+ failf(data, "LDAP local: bind ldap_parse_sasl_bind_result %s",
+ ldap_err2string(rc));
+ result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
+ }
+ else
+ state(data, OLDAP_STOP);
+
+ if(bv)
+ ber_bvfree(bv);
+ return result;
+}
+
+static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct ldapconninfo *li = conn->proto.ldapc;
+ LDAPMessage *msg = NULL;
+ struct timeval tv = {0, 0};
+ int code = LDAP_SUCCESS;
+ int rc;
+
+ if(li->state != OLDAP_SSL && li->state != OLDAP_TLS) {
+ /* Get response to last command. */
+ rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, &tv, &msg);
+ switch(rc) {
+ case 0: /* Timed out. */
+ return CURLE_OK;
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ break;
+ default:
+ li->msgid = 0; /* Nothing to abandon upon error. */
+ if(rc < 0) {
+ failf(data, "LDAP local: connecting ldap_result %s",
+ ldap_err2string(rc));
+ return oldap_map_error(rc, CURLE_COULDNT_CONNECT);
+ }
+ break;
+ }
+
+ /* Get error code from message. */
+ rc = ldap_parse_result(li->ld, msg, &code, NULL, NULL, NULL, NULL, 0);
+ if(rc)
+ code = rc;
+ else {
+ /* store the latest code for later retrieval */
+ data->info.httpcode = code;
+ }
+
+ /* If protocol version 3 is not supported, fallback to version 2. */
+ if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2 &&
+#ifdef USE_SSL
+ (ssl_installed(conn) || data->set.use_ssl <= CURLUSESSL_TRY) &&
+#endif
+ li->sasl.prefmech == SASL_AUTH_NONE) {
+ static const int version = LDAP_VERSION2;
+
+ ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
+ ldap_msgfree(msg);
+ return oldap_perform_bind(data, OLDAP_BINDV2);
+ }
+ }
+
+ /* Handle response message according to current state. */
+ switch(li->state) {
+
+#ifdef USE_SSL
+ case OLDAP_SSL:
+ result = oldap_ssl_connect(data, OLDAP_SSL);
+ if(!result && ssl_installed(conn)) {
+ if(li->sasl.prefmech != SASL_AUTH_NONE)
+ result = oldap_perform_mechs(data);
+ else
+ result = oldap_perform_bind(data, OLDAP_BIND);
+ }
+ break;
+ case OLDAP_STARTTLS:
+ if(code != LDAP_SUCCESS) {
+ if(data->set.use_ssl != CURLUSESSL_TRY)
+ result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
+ else if(li->sasl.prefmech != SASL_AUTH_NONE)
+ result = oldap_perform_mechs(data);
+ else
+ result = oldap_perform_bind(data, OLDAP_BIND);
+ break;
+ }
+ /* FALLTHROUGH */
+ case OLDAP_TLS:
+ result = oldap_ssl_connect(data, OLDAP_TLS);
+ if(result && data->set.use_ssl != CURLUSESSL_TRY)
+ result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
+ else if(ssl_installed(conn)) {
+ conn->bits.tls_upgraded = TRUE;
+ if(li->sasl.prefmech != SASL_AUTH_NONE)
+ result = oldap_perform_mechs(data);
+ else if(data->state.aptr.user)
+ result = oldap_perform_bind(data, OLDAP_BIND);
+ else {
+ state(data, OLDAP_STOP); /* Version 3 supported: no bind required */
+ result = CURLE_OK;
+ }
+ }
+ break;
+#endif
+
+ case OLDAP_MECHS:
+ result = oldap_state_mechs_resp(data, msg, code);
+ break;
+ case OLDAP_SASL:
+ result = oldap_state_sasl_resp(data, msg, code);
+ break;
+ case OLDAP_BIND:
+ case OLDAP_BINDV2:
+ result = oldap_state_bind_resp(data, msg, code);
+ break;
+ default:
+ /* internal error */
+ result = CURLE_COULDNT_CONNECT;
+ break;
+ }
+
+ ldap_msgfree(msg);
+
+ *done = li->state == OLDAP_STOP;
+ if(*done)
+ conn->recv[FIRSTSOCKET] = oldap_recv;
+
+ if(result && li->msgid) {
+ ldap_abandon_ext(li->ld, li->msgid, NULL, NULL);
+ li->msgid = 0;
+ }
+ return result;
+}
+
+static CURLcode oldap_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection)
+{
+ struct ldapconninfo *li = conn->proto.ldapc;
+ (void) dead_connection;
+#ifndef USE_SSL
+ (void)data;
+#endif
+
+ if(li) {
+ if(li->ld) {
+#ifdef USE_SSL
+ if(ssl_installed(conn)) {
+ Sockbuf *sb;
+ ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
+ ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
+ }
+#endif
+ ldap_unbind_ext(li->ld, NULL, NULL);
+ li->ld = NULL;
+ }
+ Curl_sasl_cleanup(conn, li->sasl.authused);
+ conn->proto.ldapc = NULL;
+ free(li);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode oldap_do(struct Curl_easy *data, bool *done)
+{
+ struct connectdata *conn = data->conn;
+ struct ldapconninfo *li = conn->proto.ldapc;
+ struct ldapreqinfo *lr;
+ CURLcode result;
+ int rc;
+ LDAPURLDesc *lud;
+ int msgid;
+
+ connkeep(conn, "OpenLDAP do");
+
+ infof(data, "LDAP local: %s", data->state.url);
+
+ result = oldap_url_parse(data, &lud);
+ if(!result) {
+ rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope,
+ lud->lud_filter, lud->lud_attrs, 0,
+ NULL, NULL, NULL, 0, &msgid);
+ ldap_free_urldesc(lud);
+ if(rc != LDAP_SUCCESS) {
+ failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
+ result = CURLE_LDAP_SEARCH_FAILED;
+ }
+ else {
+ lr = calloc(1, sizeof(struct ldapreqinfo));
+ if(!lr) {
+ ldap_abandon_ext(li->ld, msgid, NULL, NULL);
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ else {
+ lr->msgid = msgid;
+ data->req.p.ldap = lr;
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
+ *done = TRUE;
+ }
+ }
+ }
+ return result;
+}
+
+static CURLcode oldap_done(struct Curl_easy *data, CURLcode res,
+ bool premature)
+{
+ struct connectdata *conn = data->conn;
+ struct ldapreqinfo *lr = data->req.p.ldap;
+
+ (void)res;
+ (void)premature;
+
+ if(lr) {
+ /* if there was a search in progress, abandon it */
+ if(lr->msgid) {
+ struct ldapconninfo *li = conn->proto.ldapc;
+ ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
+ lr->msgid = 0;
+ }
+ data->req.p.ldap = NULL;
+ free(lr);
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode client_write(struct Curl_easy *data,
+ const char *prefix, size_t plen,
+ const char *value, size_t len,
+ const char *suffix, size_t slen)
+{
+ CURLcode result = CURLE_OK;
+
+ if(prefix) {
+ /* If we have a zero-length value and the prefix ends with a space
+ separator, drop the latter. */
+ if(!len && plen && prefix[plen - 1] == ' ')
+ plen--;
+ result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, plen);
+ if(!result)
+ data->req.bytecount += plen;
+ }
+ if(!result && value) {
+ result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) value, len);
+ if(!result)
+ data->req.bytecount += len;
+ }
+ if(!result && suffix) {
+ result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, slen);
+ if(!result)
+ data->req.bytecount += slen;
+ }
+ return result;
+}
+
+static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
+ size_t len, CURLcode *err)
+{
+ struct connectdata *conn = data->conn;
+ struct ldapconninfo *li = conn->proto.ldapc;
+ struct ldapreqinfo *lr = data->req.p.ldap;
+ int rc;
+ LDAPMessage *msg = NULL;
+ BerElement *ber = NULL;
+ struct timeval tv = {0, 0};
+ struct berval bv, *bvals;
+ int binary = 0;
+ CURLcode result = CURLE_AGAIN;
+ int code;
+ char *info = NULL;
+
+ (void)len;
+ (void)buf;
+ (void)sockindex;
+
+ rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_ONE, &tv, &msg);
+ if(rc < 0) {
+ failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
+ result = CURLE_RECV_ERROR;
+ }
+
+ *err = result;
+
+ /* error or timed out */
+ if(!msg)
+ return -1;
+
+ result = CURLE_OK;
+
+ switch(ldap_msgtype(msg)) {
+ case LDAP_RES_SEARCH_RESULT:
+ lr->msgid = 0;
+ rc = ldap_parse_result(li->ld, msg, &code, NULL, &info, NULL, NULL, 0);
+ if(rc) {
+ failf(data, "LDAP local: search ldap_parse_result %s",
+ ldap_err2string(rc));
+ result = CURLE_LDAP_SEARCH_FAILED;
+ break;
+ }
+
+ /* store the latest code for later retrieval */
+ data->info.httpcode = code;
+
+ switch(code) {
+ case LDAP_SIZELIMIT_EXCEEDED:
+ infof(data, "There are more than %d entries", lr->nument);
+ /* FALLTHROUGH */
+ case LDAP_SUCCESS:
+ data->req.size = data->req.bytecount;
+ break;
+ default:
+ failf(data, "LDAP remote: search failed %s %s", ldap_err2string(code),
+ info ? info : "");
+ result = CURLE_LDAP_SEARCH_FAILED;
+ break;
+ }
+ if(info)
+ ldap_memfree(info);
+ break;
+ case LDAP_RES_SEARCH_ENTRY:
+ lr->nument++;
+ rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
+ if(rc < 0) {
+ result = CURLE_RECV_ERROR;
+ break;
+ }
+
+ result = client_write(data, STRCONST("DN: "), bv.bv_val, bv.bv_len,
+ STRCONST("\n"));
+ if(result)
+ break;
+
+ for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
+ rc == LDAP_SUCCESS;
+ rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
+ int i;
+
+ if(!bv.bv_val)
+ break;
+
+ if(!bvals) {
+ result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
+ STRCONST(":\n"));
+ if(result)
+ break;
+ continue;
+ }
+
+ binary = bv.bv_len > 7 &&
+ !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7);
+
+ for(i = 0; bvals[i].bv_val != NULL; i++) {
+ int binval = 0;
+
+ result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
+ STRCONST(":"));
+ if(result)
+ break;
+
+ if(!binary) {
+ /* check for leading or trailing whitespace */
+ if(ISBLANK(bvals[i].bv_val[0]) ||
+ ISBLANK(bvals[i].bv_val[bvals[i].bv_len - 1]))
+ binval = 1;
+ else {
+ /* check for unprintable characters */
+ unsigned int j;
+ for(j = 0; j < bvals[i].bv_len; j++)
+ if(!ISPRINT(bvals[i].bv_val[j])) {
+ binval = 1;
+ break;
+ }
+ }
+ }
+ if(binary || binval) {
+ char *val_b64 = NULL;
+ size_t val_b64_sz = 0;
+
+ /* Binary value, encode to base64. */
+ if(bvals[i].bv_len)
+ result = Curl_base64_encode(bvals[i].bv_val, bvals[i].bv_len,
+ &val_b64, &val_b64_sz);
+ if(!result)
+ result = client_write(data, STRCONST(": "), val_b64, val_b64_sz,
+ STRCONST("\n"));
+ free(val_b64);
+ }
+ else
+ result = client_write(data, STRCONST(" "),
+ bvals[i].bv_val, bvals[i].bv_len,
+ STRCONST("\n"));
+ if(result)
+ break;
+ }
+
+ ber_memfree(bvals);
+ bvals = NULL;
+ if(!result)
+ result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
+ if(result)
+ break;
+ }
+
+ ber_free(ber, 0);
+
+ if(!result)
+ result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
+ if(!result)
+ result = CURLE_AGAIN;
+ break;
+ }
+
+ ldap_msgfree(msg);
+ *err = result;
+ return result? -1: 0;
+}
+
+#ifdef USE_SSL
+static int
+ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg)
+{
+ sbiod->sbiod_pvt = arg;
+ return 0;
+}
+
+static int
+ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod)
+{
+ sbiod->sbiod_pvt = NULL;
+ return 0;
+}
+
+/* We don't need to do anything because libcurl does it already */
+static int
+ldapsb_tls_close(Sockbuf_IO_Desc *sbiod)
+{
+ (void)sbiod;
+ return 0;
+}
+
+static int
+ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
+{
+ (void)arg;
+ if(opt == LBER_SB_OPT_DATA_READY) {
+ struct Curl_easy *data = sbiod->sbiod_pvt;
+ return Curl_conn_data_pending(data, FIRSTSOCKET);
+ }
+ return 0;
+}
+
+static ber_slen_t
+ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+ struct Curl_easy *data = sbiod->sbiod_pvt;
+ ber_slen_t ret = 0;
+ if(data) {
+ struct connectdata *conn = data->conn;
+ if(conn) {
+ struct ldapconninfo *li = conn->proto.ldapc;
+ CURLcode err = CURLE_RECV_ERROR;
+
+ ret = (li->recv)(data, FIRSTSOCKET, buf, len, &err);
+ if(ret < 0 && err == CURLE_AGAIN) {
+ SET_SOCKERRNO(EWOULDBLOCK);
+ }
+ }
+ }
+ return ret;
+}
+
+static ber_slen_t
+ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+ struct Curl_easy *data = sbiod->sbiod_pvt;
+ ber_slen_t ret = 0;
+ if(data) {
+ struct connectdata *conn = data->conn;
+ if(conn) {
+ struct ldapconninfo *li = conn->proto.ldapc;
+ CURLcode err = CURLE_SEND_ERROR;
+ ret = (li->send)(data, FIRSTSOCKET, buf, len, &err);
+ if(ret < 0 && err == CURLE_AGAIN) {
+ SET_SOCKERRNO(EWOULDBLOCK);
+ }
+ }
+ }
+ return ret;
+}
+
+static Sockbuf_IO ldapsb_tls =
+{
+ ldapsb_tls_setup,
+ ldapsb_tls_remove,
+ ldapsb_tls_ctrl,
+ ldapsb_tls_read,
+ ldapsb_tls_write,
+ ldapsb_tls_close
+};
+#endif /* USE_SSL */
+
+#endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */
diff --git a/libs/libcurl/src/parsedate.c b/libs/libcurl/src/parsedate.c index 5ed88195fb..4b6c1171d6 100644 --- a/libs/libcurl/src/parsedate.c +++ b/libs/libcurl/src/parsedate.c @@ -1,603 +1,603 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ -/* - A brief summary of the date string formats this parser groks: - - RFC 2616 3.3.1 - - Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 - Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 - Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format - - we support dates without week day name: - - 06 Nov 1994 08:49:37 GMT - 06-Nov-94 08:49:37 GMT - Nov 6 08:49:37 1994 - - without the time zone: - - 06 Nov 1994 08:49:37 - 06-Nov-94 08:49:37 - - weird order: - - 1994 Nov 6 08:49:37 (GNU date fails) - GMT 08:49:37 06-Nov-94 Sunday - 94 6 Nov 08:49:37 (GNU date fails) - - time left out: - - 1994 Nov 6 - 06-Nov-94 - Sun Nov 6 94 - - unusual separators: - - 1994.Nov.6 - Sun/Nov/6/94/GMT - - commonly used time zone names: - - Sun, 06 Nov 1994 08:49:37 CET - 06 Nov 1994 08:49:37 EST - - time zones specified using RFC822 style: - - Sun, 12 Sep 2004 15:05:58 -0700 - Sat, 11 Sep 2004 21:32:11 +0200 - - compact numerical date strings: - - 20040912 15:05:58 -0700 - 20040911 +0200 - -*/ - -#include "curl_setup.h" - -#include <limits.h> - -#include <curl/curl.h> -#include "strcase.h" -#include "warnless.h" -#include "parsedate.h" - -/* - * parsedate() - * - * Returns: - * - * PARSEDATE_OK - a fine conversion - * PARSEDATE_FAIL - failed to convert - * PARSEDATE_LATER - time overflow at the far end of time_t - * PARSEDATE_SOONER - time underflow at the low end of time_t - */ - -static int parsedate(const char *date, time_t *output); - -#define PARSEDATE_OK 0 -#define PARSEDATE_FAIL -1 -#define PARSEDATE_LATER 1 -#define PARSEDATE_SOONER 2 - -#if !defined(CURL_DISABLE_PARSEDATE) || !defined(CURL_DISABLE_FTP) || \ - !defined(CURL_DISABLE_FILE) -/* These names are also used by FTP and FILE code */ -const char * const Curl_wkday[] = -{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; -const char * const Curl_month[]= -{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; -#endif - -#ifndef CURL_DISABLE_PARSEDATE -static const char * const weekday[] = -{ "Monday", "Tuesday", "Wednesday", "Thursday", - "Friday", "Saturday", "Sunday" }; - -struct tzinfo { - char name[5]; - int offset; /* +/- in minutes */ -}; - -/* Here's a bunch of frequently used time zone names. These were supported - by the old getdate parser. */ -#define tDAYZONE -60 /* offset for daylight savings time */ -static const struct tzinfo tz[]= { - {"GMT", 0}, /* Greenwich Mean */ - {"UT", 0}, /* Universal Time */ - {"UTC", 0}, /* Universal (Coordinated) */ - {"WET", 0}, /* Western European */ - {"BST", 0 tDAYZONE}, /* British Summer */ - {"WAT", 60}, /* West Africa */ - {"AST", 240}, /* Atlantic Standard */ - {"ADT", 240 tDAYZONE}, /* Atlantic Daylight */ - {"EST", 300}, /* Eastern Standard */ - {"EDT", 300 tDAYZONE}, /* Eastern Daylight */ - {"CST", 360}, /* Central Standard */ - {"CDT", 360 tDAYZONE}, /* Central Daylight */ - {"MST", 420}, /* Mountain Standard */ - {"MDT", 420 tDAYZONE}, /* Mountain Daylight */ - {"PST", 480}, /* Pacific Standard */ - {"PDT", 480 tDAYZONE}, /* Pacific Daylight */ - {"YST", 540}, /* Yukon Standard */ - {"YDT", 540 tDAYZONE}, /* Yukon Daylight */ - {"HST", 600}, /* Hawaii Standard */ - {"HDT", 600 tDAYZONE}, /* Hawaii Daylight */ - {"CAT", 600}, /* Central Alaska */ - {"AHST", 600}, /* Alaska-Hawaii Standard */ - {"NT", 660}, /* Nome */ - {"IDLW", 720}, /* International Date Line West */ - {"CET", -60}, /* Central European */ - {"MET", -60}, /* Middle European */ - {"MEWT", -60}, /* Middle European Winter */ - {"MEST", -60 tDAYZONE}, /* Middle European Summer */ - {"CEST", -60 tDAYZONE}, /* Central European Summer */ - {"MESZ", -60 tDAYZONE}, /* Middle European Summer */ - {"FWT", -60}, /* French Winter */ - {"FST", -60 tDAYZONE}, /* French Summer */ - {"EET", -120}, /* Eastern Europe, USSR Zone 1 */ - {"WAST", -420}, /* West Australian Standard */ - {"WADT", -420 tDAYZONE}, /* West Australian Daylight */ - {"CCT", -480}, /* China Coast, USSR Zone 7 */ - {"JST", -540}, /* Japan Standard, USSR Zone 8 */ - {"EAST", -600}, /* Eastern Australian Standard */ - {"EADT", -600 tDAYZONE}, /* Eastern Australian Daylight */ - {"GST", -600}, /* Guam Standard, USSR Zone 9 */ - {"NZT", -720}, /* New Zealand */ - {"NZST", -720}, /* New Zealand Standard */ - {"NZDT", -720 tDAYZONE}, /* New Zealand Daylight */ - {"IDLE", -720}, /* International Date Line East */ - /* Next up: Military timezone names. RFC822 allowed these, but (as noted in - RFC 1123) had their signs wrong. Here we use the correct signs to match - actual military usage. - */ - {"A", 1 * 60}, /* Alpha */ - {"B", 2 * 60}, /* Bravo */ - {"C", 3 * 60}, /* Charlie */ - {"D", 4 * 60}, /* Delta */ - {"E", 5 * 60}, /* Echo */ - {"F", 6 * 60}, /* Foxtrot */ - {"G", 7 * 60}, /* Golf */ - {"H", 8 * 60}, /* Hotel */ - {"I", 9 * 60}, /* India */ - /* "J", Juliet is not used as a timezone, to indicate the observer's local - time */ - {"K", 10 * 60}, /* Kilo */ - {"L", 11 * 60}, /* Lima */ - {"M", 12 * 60}, /* Mike */ - {"N", -1 * 60}, /* November */ - {"O", -2 * 60}, /* Oscar */ - {"P", -3 * 60}, /* Papa */ - {"Q", -4 * 60}, /* Quebec */ - {"R", -5 * 60}, /* Romeo */ - {"S", -6 * 60}, /* Sierra */ - {"T", -7 * 60}, /* Tango */ - {"U", -8 * 60}, /* Uniform */ - {"V", -9 * 60}, /* Victor */ - {"W", -10 * 60}, /* Whiskey */ - {"X", -11 * 60}, /* X-ray */ - {"Y", -12 * 60}, /* Yankee */ - {"Z", 0}, /* Zulu, zero meridian, a.k.a. UTC */ -}; - -/* returns: - -1 no day - 0 monday - 6 sunday -*/ - -static int checkday(const char *check, size_t len) -{ - int i; - const char * const *what; - bool found = FALSE; - if(len > 3) - what = &weekday[0]; - else - what = &Curl_wkday[0]; - for(i = 0; i<7; i++) { - if(strcasecompare(check, what[0])) { - found = TRUE; - break; - } - what++; - } - return found?i:-1; -} - -static int checkmonth(const char *check) -{ - int i; - const char * const *what; - bool found = FALSE; - - what = &Curl_month[0]; - for(i = 0; i<12; i++) { - if(strcasecompare(check, what[0])) { - found = TRUE; - break; - } - what++; - } - return found?i:-1; /* return the offset or -1, no real offset is -1 */ -} - -/* return the time zone offset between GMT and the input one, in number - of seconds or -1 if the timezone wasn't found/legal */ - -static int checktz(const char *check) -{ - unsigned int i; - const struct tzinfo *what; - bool found = FALSE; - - what = tz; - for(i = 0; i< sizeof(tz)/sizeof(tz[0]); i++) { - if(strcasecompare(check, what->name)) { - found = TRUE; - break; - } - what++; - } - return found?what->offset*60:-1; -} - -static void skip(const char **date) -{ - /* skip everything that aren't letters or digits */ - while(**date && !ISALNUM(**date)) - (*date)++; -} - -enum assume { - DATE_MDAY, - DATE_YEAR, - DATE_TIME -}; - -/* - * time2epoch: time stamp to seconds since epoch in GMT time zone. Similar to - * mktime but for GMT only. - */ -static time_t time2epoch(int sec, int min, int hour, - int mday, int mon, int year) -{ - static const int month_days_cumulative [12] = - { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; - int leap_days = year - (mon <= 1); - leap_days = ((leap_days / 4) - (leap_days / 100) + (leap_days / 400) - - (1969 / 4) + (1969 / 100) - (1969 / 400)); - return ((((time_t) (year - 1970) * 365 - + leap_days + month_days_cumulative[mon] + mday - 1) * 24 - + hour) * 60 + min) * 60 + sec; -} - -/* - * parsedate() - * - * Returns: - * - * PARSEDATE_OK - a fine conversion - * PARSEDATE_FAIL - failed to convert - * PARSEDATE_LATER - time overflow at the far end of time_t - * PARSEDATE_SOONER - time underflow at the low end of time_t - */ - -static int parsedate(const char *date, time_t *output) -{ - time_t t = 0; - int wdaynum = -1; /* day of the week number, 0-6 (mon-sun) */ - int monnum = -1; /* month of the year number, 0-11 */ - int mdaynum = -1; /* day of month, 1 - 31 */ - int hournum = -1; - int minnum = -1; - int secnum = -1; - int yearnum = -1; - int tzoff = -1; - enum assume dignext = DATE_MDAY; - const char *indate = date; /* save the original pointer */ - int part = 0; /* max 6 parts */ - - while(*date && (part < 6)) { - bool found = FALSE; - - skip(&date); - - if(ISALPHA(*date)) { - /* a name coming up */ - char buf[32]=""; - size_t len; - if(sscanf(date, "%31[ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz]", buf)) - len = strlen(buf); - else - len = 0; - - if(wdaynum == -1) { - wdaynum = checkday(buf, len); - if(wdaynum != -1) - found = TRUE; - } - if(!found && (monnum == -1)) { - monnum = checkmonth(buf); - if(monnum != -1) - found = TRUE; - } - - if(!found && (tzoff == -1)) { - /* this just must be a time zone string */ - tzoff = checktz(buf); - if(tzoff != -1) - found = TRUE; - } - - if(!found) - return PARSEDATE_FAIL; /* bad string */ - - date += len; - } - else if(ISDIGIT(*date)) { - /* a digit */ - int val; - char *end; - int len = 0; - if((secnum == -1) && - (3 == sscanf(date, "%02d:%02d:%02d%n", - &hournum, &minnum, &secnum, &len))) { - /* time stamp! */ - date += len; - } - else if((secnum == -1) && - (2 == sscanf(date, "%02d:%02d%n", &hournum, &minnum, &len))) { - /* time stamp without seconds */ - date += len; - secnum = 0; - } - else { - long lval; - int error; - int old_errno; - - old_errno = errno; - errno = 0; - lval = strtol(date, &end, 10); - error = errno; - if(errno != old_errno) - errno = old_errno; - - if(error) - return PARSEDATE_FAIL; - -#if LONG_MAX != INT_MAX - if((lval > (long)INT_MAX) || (lval < (long)INT_MIN)) - return PARSEDATE_FAIL; -#endif - - val = curlx_sltosi(lval); - - if((tzoff == -1) && - ((end - date) == 4) && - (val <= 1400) && - (indate< date) && - ((date[-1] == '+' || date[-1] == '-'))) { - /* four digits and a value less than or equal to 1400 (to take into - account all sorts of funny time zone diffs) and it is preceded - with a plus or minus. This is a time zone indication. 1400 is - picked since +1300 is frequently used and +1400 is mentioned as - an edge number in the document "ISO C 200X Proposal: Timezone - Functions" at http://david.tribble.com/text/c0xtimezone.html If - anyone has a more authoritative source for the exact maximum time - zone offsets, please speak up! */ - found = TRUE; - tzoff = (val/100 * 60 + val%100)*60; - - /* the + and - prefix indicates the local time compared to GMT, - this we need their reversed math to get what we want */ - tzoff = date[-1]=='+'?-tzoff:tzoff; - } - - if(((end - date) == 8) && - (yearnum == -1) && - (monnum == -1) && - (mdaynum == -1)) { - /* 8 digits, no year, month or day yet. This is YYYYMMDD */ - found = TRUE; - yearnum = val/10000; - monnum = (val%10000)/100-1; /* month is 0 - 11 */ - mdaynum = val%100; - } - - if(!found && (dignext == DATE_MDAY) && (mdaynum == -1)) { - if((val > 0) && (val<32)) { - mdaynum = val; - found = TRUE; - } - dignext = DATE_YEAR; - } - - if(!found && (dignext == DATE_YEAR) && (yearnum == -1)) { - yearnum = val; - found = TRUE; - if(yearnum < 100) { - if(yearnum > 70) - yearnum += 1900; - else - yearnum += 2000; - } - if(mdaynum == -1) - dignext = DATE_MDAY; - } - - if(!found) - return PARSEDATE_FAIL; - - date = end; - } - } - - part++; - } - - if(-1 == secnum) - secnum = minnum = hournum = 0; /* no time, make it zero */ - - if((-1 == mdaynum) || - (-1 == monnum) || - (-1 == yearnum)) - /* lacks vital info, fail */ - return PARSEDATE_FAIL; - -#ifdef HAVE_TIME_T_UNSIGNED - if(yearnum < 1970) { - /* only positive numbers cannot return earlier */ - *output = TIME_T_MIN; - return PARSEDATE_SOONER; - } -#endif - -#if (SIZEOF_TIME_T < 5) - -#ifdef HAVE_TIME_T_UNSIGNED - /* an unsigned 32 bit time_t can only hold dates to 2106 */ - if(yearnum > 2105) { - *output = TIME_T_MAX; - return PARSEDATE_LATER; - } -#else - /* a signed 32 bit time_t can only hold dates to the beginning of 2038 */ - if(yearnum > 2037) { - *output = TIME_T_MAX; - return PARSEDATE_LATER; - } - if(yearnum < 1903) { - *output = TIME_T_MIN; - return PARSEDATE_SOONER; - } -#endif - -#else - /* The Gregorian calendar was introduced 1582 */ - if(yearnum < 1583) - return PARSEDATE_FAIL; -#endif - - if((mdaynum > 31) || (monnum > 11) || - (hournum > 23) || (minnum > 59) || (secnum > 60)) - return PARSEDATE_FAIL; /* clearly an illegal date */ - - /* time2epoch() returns a time_t. time_t is often 32 bits, sometimes even on - architectures that feature 64 bit 'long' but ultimately time_t is the - correct data type to use. - */ - t = time2epoch(secnum, minnum, hournum, mdaynum, monnum, yearnum); - - /* Add the time zone diff between local time zone and GMT. */ - if(tzoff == -1) - tzoff = 0; - - if((tzoff > 0) && (t > TIME_T_MAX - tzoff)) { - *output = TIME_T_MAX; - return PARSEDATE_LATER; /* time_t overflow */ - } - - t += tzoff; - - *output = t; - - return PARSEDATE_OK; -} -#else -/* disabled */ -static int parsedate(const char *date, time_t *output) -{ - (void)date; - *output = 0; - return PARSEDATE_OK; /* a lie */ -} -#endif - -time_t curl_getdate(const char *p, const time_t *now) -{ - time_t parsed = -1; - int rc = parsedate(p, &parsed); - (void)now; /* legacy argument from the past that we ignore */ - - if(rc == PARSEDATE_OK) { - if(parsed == -1) - /* avoid returning -1 for a working scenario */ - parsed++; - return parsed; - } - /* everything else is fail */ - return -1; -} - -/* Curl_getdate_capped() differs from curl_getdate() in that this will return - TIME_T_MAX in case the parsed time value was too big, instead of an - error. */ - -time_t Curl_getdate_capped(const char *p) -{ - time_t parsed = -1; - int rc = parsedate(p, &parsed); - - switch(rc) { - case PARSEDATE_OK: - if(parsed == -1) - /* avoid returning -1 for a working scenario */ - parsed++; - return parsed; - case PARSEDATE_LATER: - /* this returns the maximum time value */ - return parsed; - default: - return -1; /* everything else is fail */ - } - /* UNREACHABLE */ -} - -/* - * Curl_gmtime() is a gmtime() replacement for portability. Do not use the - * gmtime_r() or gmtime() functions anywhere else but here. - * - */ - -CURLcode Curl_gmtime(time_t intime, struct tm *store) -{ - const struct tm *tm; -#ifdef HAVE_GMTIME_R - /* thread-safe version */ - tm = (struct tm *)gmtime_r(&intime, store); -#else - /* !checksrc! disable BANNEDFUNC 1 */ - tm = gmtime(&intime); - if(tm) - *store = *tm; /* copy the pointed struct to the local copy */ -#endif - - if(!tm) - return CURLE_BAD_FUNCTION_ARGUMENT; - return CURLE_OK; -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+/*
+ A brief summary of the date string formats this parser groks:
+
+ RFC 2616 3.3.1
+
+ Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
+ Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+ Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+
+ we support dates without week day name:
+
+ 06 Nov 1994 08:49:37 GMT
+ 06-Nov-94 08:49:37 GMT
+ Nov 6 08:49:37 1994
+
+ without the time zone:
+
+ 06 Nov 1994 08:49:37
+ 06-Nov-94 08:49:37
+
+ weird order:
+
+ 1994 Nov 6 08:49:37 (GNU date fails)
+ GMT 08:49:37 06-Nov-94 Sunday
+ 94 6 Nov 08:49:37 (GNU date fails)
+
+ time left out:
+
+ 1994 Nov 6
+ 06-Nov-94
+ Sun Nov 6 94
+
+ unusual separators:
+
+ 1994.Nov.6
+ Sun/Nov/6/94/GMT
+
+ commonly used time zone names:
+
+ Sun, 06 Nov 1994 08:49:37 CET
+ 06 Nov 1994 08:49:37 EST
+
+ time zones specified using RFC822 style:
+
+ Sun, 12 Sep 2004 15:05:58 -0700
+ Sat, 11 Sep 2004 21:32:11 +0200
+
+ compact numerical date strings:
+
+ 20040912 15:05:58 -0700
+ 20040911 +0200
+
+*/
+
+#include "curl_setup.h"
+
+#include <limits.h>
+
+#include <curl/curl.h>
+#include "strcase.h"
+#include "warnless.h"
+#include "parsedate.h"
+
+/*
+ * parsedate()
+ *
+ * Returns:
+ *
+ * PARSEDATE_OK - a fine conversion
+ * PARSEDATE_FAIL - failed to convert
+ * PARSEDATE_LATER - time overflow at the far end of time_t
+ * PARSEDATE_SOONER - time underflow at the low end of time_t
+ */
+
+static int parsedate(const char *date, time_t *output);
+
+#define PARSEDATE_OK 0
+#define PARSEDATE_FAIL -1
+#define PARSEDATE_LATER 1
+#define PARSEDATE_SOONER 2
+
+#if !defined(CURL_DISABLE_PARSEDATE) || !defined(CURL_DISABLE_FTP) || \
+ !defined(CURL_DISABLE_FILE)
+/* These names are also used by FTP and FILE code */
+const char * const Curl_wkday[] =
+{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
+const char * const Curl_month[]=
+{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+#endif
+
+#ifndef CURL_DISABLE_PARSEDATE
+static const char * const weekday[] =
+{ "Monday", "Tuesday", "Wednesday", "Thursday",
+ "Friday", "Saturday", "Sunday" };
+
+struct tzinfo {
+ char name[5];
+ int offset; /* +/- in minutes */
+};
+
+/* Here's a bunch of frequently used time zone names. These were supported
+ by the old getdate parser. */
+#define tDAYZONE -60 /* offset for daylight savings time */
+static const struct tzinfo tz[]= {
+ {"GMT", 0}, /* Greenwich Mean */
+ {"UT", 0}, /* Universal Time */
+ {"UTC", 0}, /* Universal (Coordinated) */
+ {"WET", 0}, /* Western European */
+ {"BST", 0 tDAYZONE}, /* British Summer */
+ {"WAT", 60}, /* West Africa */
+ {"AST", 240}, /* Atlantic Standard */
+ {"ADT", 240 tDAYZONE}, /* Atlantic Daylight */
+ {"EST", 300}, /* Eastern Standard */
+ {"EDT", 300 tDAYZONE}, /* Eastern Daylight */
+ {"CST", 360}, /* Central Standard */
+ {"CDT", 360 tDAYZONE}, /* Central Daylight */
+ {"MST", 420}, /* Mountain Standard */
+ {"MDT", 420 tDAYZONE}, /* Mountain Daylight */
+ {"PST", 480}, /* Pacific Standard */
+ {"PDT", 480 tDAYZONE}, /* Pacific Daylight */
+ {"YST", 540}, /* Yukon Standard */
+ {"YDT", 540 tDAYZONE}, /* Yukon Daylight */
+ {"HST", 600}, /* Hawaii Standard */
+ {"HDT", 600 tDAYZONE}, /* Hawaii Daylight */
+ {"CAT", 600}, /* Central Alaska */
+ {"AHST", 600}, /* Alaska-Hawaii Standard */
+ {"NT", 660}, /* Nome */
+ {"IDLW", 720}, /* International Date Line West */
+ {"CET", -60}, /* Central European */
+ {"MET", -60}, /* Middle European */
+ {"MEWT", -60}, /* Middle European Winter */
+ {"MEST", -60 tDAYZONE}, /* Middle European Summer */
+ {"CEST", -60 tDAYZONE}, /* Central European Summer */
+ {"MESZ", -60 tDAYZONE}, /* Middle European Summer */
+ {"FWT", -60}, /* French Winter */
+ {"FST", -60 tDAYZONE}, /* French Summer */
+ {"EET", -120}, /* Eastern Europe, USSR Zone 1 */
+ {"WAST", -420}, /* West Australian Standard */
+ {"WADT", -420 tDAYZONE}, /* West Australian Daylight */
+ {"CCT", -480}, /* China Coast, USSR Zone 7 */
+ {"JST", -540}, /* Japan Standard, USSR Zone 8 */
+ {"EAST", -600}, /* Eastern Australian Standard */
+ {"EADT", -600 tDAYZONE}, /* Eastern Australian Daylight */
+ {"GST", -600}, /* Guam Standard, USSR Zone 9 */
+ {"NZT", -720}, /* New Zealand */
+ {"NZST", -720}, /* New Zealand Standard */
+ {"NZDT", -720 tDAYZONE}, /* New Zealand Daylight */
+ {"IDLE", -720}, /* International Date Line East */
+ /* Next up: Military timezone names. RFC822 allowed these, but (as noted in
+ RFC 1123) had their signs wrong. Here we use the correct signs to match
+ actual military usage.
+ */
+ {"A", 1 * 60}, /* Alpha */
+ {"B", 2 * 60}, /* Bravo */
+ {"C", 3 * 60}, /* Charlie */
+ {"D", 4 * 60}, /* Delta */
+ {"E", 5 * 60}, /* Echo */
+ {"F", 6 * 60}, /* Foxtrot */
+ {"G", 7 * 60}, /* Golf */
+ {"H", 8 * 60}, /* Hotel */
+ {"I", 9 * 60}, /* India */
+ /* "J", Juliet is not used as a timezone, to indicate the observer's local
+ time */
+ {"K", 10 * 60}, /* Kilo */
+ {"L", 11 * 60}, /* Lima */
+ {"M", 12 * 60}, /* Mike */
+ {"N", -1 * 60}, /* November */
+ {"O", -2 * 60}, /* Oscar */
+ {"P", -3 * 60}, /* Papa */
+ {"Q", -4 * 60}, /* Quebec */
+ {"R", -5 * 60}, /* Romeo */
+ {"S", -6 * 60}, /* Sierra */
+ {"T", -7 * 60}, /* Tango */
+ {"U", -8 * 60}, /* Uniform */
+ {"V", -9 * 60}, /* Victor */
+ {"W", -10 * 60}, /* Whiskey */
+ {"X", -11 * 60}, /* X-ray */
+ {"Y", -12 * 60}, /* Yankee */
+ {"Z", 0}, /* Zulu, zero meridian, a.k.a. UTC */
+};
+
+/* returns:
+ -1 no day
+ 0 monday - 6 sunday
+*/
+
+static int checkday(const char *check, size_t len)
+{
+ int i;
+ const char * const *what;
+ bool found = FALSE;
+ if(len > 3)
+ what = &weekday[0];
+ else
+ what = &Curl_wkday[0];
+ for(i = 0; i<7; i++) {
+ if(strcasecompare(check, what[0])) {
+ found = TRUE;
+ break;
+ }
+ what++;
+ }
+ return found?i:-1;
+}
+
+static int checkmonth(const char *check)
+{
+ int i;
+ const char * const *what;
+ bool found = FALSE;
+
+ what = &Curl_month[0];
+ for(i = 0; i<12; i++) {
+ if(strcasecompare(check, what[0])) {
+ found = TRUE;
+ break;
+ }
+ what++;
+ }
+ return found?i:-1; /* return the offset or -1, no real offset is -1 */
+}
+
+/* return the time zone offset between GMT and the input one, in number
+ of seconds or -1 if the timezone wasn't found/legal */
+
+static int checktz(const char *check)
+{
+ unsigned int i;
+ const struct tzinfo *what;
+ bool found = FALSE;
+
+ what = tz;
+ for(i = 0; i< sizeof(tz)/sizeof(tz[0]); i++) {
+ if(strcasecompare(check, what->name)) {
+ found = TRUE;
+ break;
+ }
+ what++;
+ }
+ return found?what->offset*60:-1;
+}
+
+static void skip(const char **date)
+{
+ /* skip everything that aren't letters or digits */
+ while(**date && !ISALNUM(**date))
+ (*date)++;
+}
+
+enum assume {
+ DATE_MDAY,
+ DATE_YEAR,
+ DATE_TIME
+};
+
+/*
+ * time2epoch: time stamp to seconds since epoch in GMT time zone. Similar to
+ * mktime but for GMT only.
+ */
+static time_t time2epoch(int sec, int min, int hour,
+ int mday, int mon, int year)
+{
+ static const int month_days_cumulative [12] =
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+ int leap_days = year - (mon <= 1);
+ leap_days = ((leap_days / 4) - (leap_days / 100) + (leap_days / 400)
+ - (1969 / 4) + (1969 / 100) - (1969 / 400));
+ return ((((time_t) (year - 1970) * 365
+ + leap_days + month_days_cumulative[mon] + mday - 1) * 24
+ + hour) * 60 + min) * 60 + sec;
+}
+
+/*
+ * parsedate()
+ *
+ * Returns:
+ *
+ * PARSEDATE_OK - a fine conversion
+ * PARSEDATE_FAIL - failed to convert
+ * PARSEDATE_LATER - time overflow at the far end of time_t
+ * PARSEDATE_SOONER - time underflow at the low end of time_t
+ */
+
+static int parsedate(const char *date, time_t *output)
+{
+ time_t t = 0;
+ int wdaynum = -1; /* day of the week number, 0-6 (mon-sun) */
+ int monnum = -1; /* month of the year number, 0-11 */
+ int mdaynum = -1; /* day of month, 1 - 31 */
+ int hournum = -1;
+ int minnum = -1;
+ int secnum = -1;
+ int yearnum = -1;
+ int tzoff = -1;
+ enum assume dignext = DATE_MDAY;
+ const char *indate = date; /* save the original pointer */
+ int part = 0; /* max 6 parts */
+
+ while(*date && (part < 6)) {
+ bool found = FALSE;
+
+ skip(&date);
+
+ if(ISALPHA(*date)) {
+ /* a name coming up */
+ char buf[32]="";
+ size_t len;
+ if(sscanf(date, "%31[ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz]", buf))
+ len = strlen(buf);
+ else
+ len = 0;
+
+ if(wdaynum == -1) {
+ wdaynum = checkday(buf, len);
+ if(wdaynum != -1)
+ found = TRUE;
+ }
+ if(!found && (monnum == -1)) {
+ monnum = checkmonth(buf);
+ if(monnum != -1)
+ found = TRUE;
+ }
+
+ if(!found && (tzoff == -1)) {
+ /* this just must be a time zone string */
+ tzoff = checktz(buf);
+ if(tzoff != -1)
+ found = TRUE;
+ }
+
+ if(!found)
+ return PARSEDATE_FAIL; /* bad string */
+
+ date += len;
+ }
+ else if(ISDIGIT(*date)) {
+ /* a digit */
+ int val;
+ char *end;
+ int len = 0;
+ if((secnum == -1) &&
+ (3 == sscanf(date, "%02d:%02d:%02d%n",
+ &hournum, &minnum, &secnum, &len))) {
+ /* time stamp! */
+ date += len;
+ }
+ else if((secnum == -1) &&
+ (2 == sscanf(date, "%02d:%02d%n", &hournum, &minnum, &len))) {
+ /* time stamp without seconds */
+ date += len;
+ secnum = 0;
+ }
+ else {
+ long lval;
+ int error;
+ int old_errno;
+
+ old_errno = errno;
+ errno = 0;
+ lval = strtol(date, &end, 10);
+ error = errno;
+ if(errno != old_errno)
+ errno = old_errno;
+
+ if(error)
+ return PARSEDATE_FAIL;
+
+#if LONG_MAX != INT_MAX
+ if((lval > (long)INT_MAX) || (lval < (long)INT_MIN))
+ return PARSEDATE_FAIL;
+#endif
+
+ val = curlx_sltosi(lval);
+
+ if((tzoff == -1) &&
+ ((end - date) == 4) &&
+ (val <= 1400) &&
+ (indate< date) &&
+ ((date[-1] == '+' || date[-1] == '-'))) {
+ /* four digits and a value less than or equal to 1400 (to take into
+ account all sorts of funny time zone diffs) and it is preceded
+ with a plus or minus. This is a time zone indication. 1400 is
+ picked since +1300 is frequently used and +1400 is mentioned as
+ an edge number in the document "ISO C 200X Proposal: Timezone
+ Functions" at http://david.tribble.com/text/c0xtimezone.html If
+ anyone has a more authoritative source for the exact maximum time
+ zone offsets, please speak up! */
+ found = TRUE;
+ tzoff = (val/100 * 60 + val%100)*60;
+
+ /* the + and - prefix indicates the local time compared to GMT,
+ this we need their reversed math to get what we want */
+ tzoff = date[-1]=='+'?-tzoff:tzoff;
+ }
+
+ if(((end - date) == 8) &&
+ (yearnum == -1) &&
+ (monnum == -1) &&
+ (mdaynum == -1)) {
+ /* 8 digits, no year, month or day yet. This is YYYYMMDD */
+ found = TRUE;
+ yearnum = val/10000;
+ monnum = (val%10000)/100-1; /* month is 0 - 11 */
+ mdaynum = val%100;
+ }
+
+ if(!found && (dignext == DATE_MDAY) && (mdaynum == -1)) {
+ if((val > 0) && (val<32)) {
+ mdaynum = val;
+ found = TRUE;
+ }
+ dignext = DATE_YEAR;
+ }
+
+ if(!found && (dignext == DATE_YEAR) && (yearnum == -1)) {
+ yearnum = val;
+ found = TRUE;
+ if(yearnum < 100) {
+ if(yearnum > 70)
+ yearnum += 1900;
+ else
+ yearnum += 2000;
+ }
+ if(mdaynum == -1)
+ dignext = DATE_MDAY;
+ }
+
+ if(!found)
+ return PARSEDATE_FAIL;
+
+ date = end;
+ }
+ }
+
+ part++;
+ }
+
+ if(-1 == secnum)
+ secnum = minnum = hournum = 0; /* no time, make it zero */
+
+ if((-1 == mdaynum) ||
+ (-1 == monnum) ||
+ (-1 == yearnum))
+ /* lacks vital info, fail */
+ return PARSEDATE_FAIL;
+
+#ifdef HAVE_TIME_T_UNSIGNED
+ if(yearnum < 1970) {
+ /* only positive numbers cannot return earlier */
+ *output = TIME_T_MIN;
+ return PARSEDATE_SOONER;
+ }
+#endif
+
+#if (SIZEOF_TIME_T < 5)
+
+#ifdef HAVE_TIME_T_UNSIGNED
+ /* an unsigned 32 bit time_t can only hold dates to 2106 */
+ if(yearnum > 2105) {
+ *output = TIME_T_MAX;
+ return PARSEDATE_LATER;
+ }
+#else
+ /* a signed 32 bit time_t can only hold dates to the beginning of 2038 */
+ if(yearnum > 2037) {
+ *output = TIME_T_MAX;
+ return PARSEDATE_LATER;
+ }
+ if(yearnum < 1903) {
+ *output = TIME_T_MIN;
+ return PARSEDATE_SOONER;
+ }
+#endif
+
+#else
+ /* The Gregorian calendar was introduced 1582 */
+ if(yearnum < 1583)
+ return PARSEDATE_FAIL;
+#endif
+
+ if((mdaynum > 31) || (monnum > 11) ||
+ (hournum > 23) || (minnum > 59) || (secnum > 60))
+ return PARSEDATE_FAIL; /* clearly an illegal date */
+
+ /* time2epoch() returns a time_t. time_t is often 32 bits, sometimes even on
+ architectures that feature 64 bit 'long' but ultimately time_t is the
+ correct data type to use.
+ */
+ t = time2epoch(secnum, minnum, hournum, mdaynum, monnum, yearnum);
+
+ /* Add the time zone diff between local time zone and GMT. */
+ if(tzoff == -1)
+ tzoff = 0;
+
+ if((tzoff > 0) && (t > TIME_T_MAX - tzoff)) {
+ *output = TIME_T_MAX;
+ return PARSEDATE_LATER; /* time_t overflow */
+ }
+
+ t += tzoff;
+
+ *output = t;
+
+ return PARSEDATE_OK;
+}
+#else
+/* disabled */
+static int parsedate(const char *date, time_t *output)
+{
+ (void)date;
+ *output = 0;
+ return PARSEDATE_OK; /* a lie */
+}
+#endif
+
+time_t curl_getdate(const char *p, const time_t *now)
+{
+ time_t parsed = -1;
+ int rc = parsedate(p, &parsed);
+ (void)now; /* legacy argument from the past that we ignore */
+
+ if(rc == PARSEDATE_OK) {
+ if(parsed == -1)
+ /* avoid returning -1 for a working scenario */
+ parsed++;
+ return parsed;
+ }
+ /* everything else is fail */
+ return -1;
+}
+
+/* Curl_getdate_capped() differs from curl_getdate() in that this will return
+ TIME_T_MAX in case the parsed time value was too big, instead of an
+ error. */
+
+time_t Curl_getdate_capped(const char *p)
+{
+ time_t parsed = -1;
+ int rc = parsedate(p, &parsed);
+
+ switch(rc) {
+ case PARSEDATE_OK:
+ if(parsed == -1)
+ /* avoid returning -1 for a working scenario */
+ parsed++;
+ return parsed;
+ case PARSEDATE_LATER:
+ /* this returns the maximum time value */
+ return parsed;
+ default:
+ return -1; /* everything else is fail */
+ }
+ /* UNREACHABLE */
+}
+
+/*
+ * Curl_gmtime() is a gmtime() replacement for portability. Do not use the
+ * gmtime_r() or gmtime() functions anywhere else but here.
+ *
+ */
+
+CURLcode Curl_gmtime(time_t intime, struct tm *store)
+{
+ const struct tm *tm;
+#ifdef HAVE_GMTIME_R
+ /* thread-safe version */
+ tm = (struct tm *)gmtime_r(&intime, store);
+#else
+ /* !checksrc! disable BANNEDFUNC 1 */
+ tm = gmtime(&intime);
+ if(tm)
+ *store = *tm; /* copy the pointed struct to the local copy */
+#endif
+
+ if(!tm)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ return CURLE_OK;
+}
diff --git a/libs/libcurl/src/parsedate.h b/libs/libcurl/src/parsedate.h index 4e4347754d..ee892d49e8 100644 --- a/libs/libcurl/src/parsedate.h +++ b/libs/libcurl/src/parsedate.h @@ -1,38 +1,38 @@ -#ifndef HEADER_CURL_PARSEDATE_H -#define HEADER_CURL_PARSEDATE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - -extern const char * const Curl_wkday[7]; -extern const char * const Curl_month[12]; - -CURLcode Curl_gmtime(time_t intime, struct tm *store); - -/* Curl_getdate_capped() differs from curl_getdate() in that this will return - TIME_T_MAX in case the parsed time value was too big, instead of an - error. */ - -time_t Curl_getdate_capped(const char *p); - -#endif /* HEADER_CURL_PARSEDATE_H */ +#ifndef HEADER_CURL_PARSEDATE_H
+#define HEADER_CURL_PARSEDATE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+extern const char * const Curl_wkday[7];
+extern const char * const Curl_month[12];
+
+CURLcode Curl_gmtime(time_t intime, struct tm *store);
+
+/* Curl_getdate_capped() differs from curl_getdate() in that this will return
+ TIME_T_MAX in case the parsed time value was too big, instead of an
+ error. */
+
+time_t Curl_getdate_capped(const char *p);
+
+#endif /* HEADER_CURL_PARSEDATE_H */
diff --git a/libs/libcurl/src/pingpong.c b/libs/libcurl/src/pingpong.c index d4e6be98c4..d7e130cc61 100644 --- a/libs/libcurl/src/pingpong.c +++ b/libs/libcurl/src/pingpong.c @@ -1,502 +1,503 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - * 'pingpong' is for generic back-and-forth support functions used by FTP, - * IMAP, POP3, SMTP and whatever more that likes them. - * - ***************************************************************************/ - -#include "curl_setup.h" - -#include "urldata.h" -#include "sendf.h" -#include "select.h" -#include "progress.h" -#include "speedcheck.h" -#include "pingpong.h" -#include "multiif.h" -#include "vtls/vtls.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#ifdef USE_PINGPONG - -/* Returns timeout in ms. 0 or negative number means the timeout has already - triggered */ -timediff_t Curl_pp_state_timeout(struct Curl_easy *data, - struct pingpong *pp, bool disconnecting) -{ - struct connectdata *conn = data->conn; - timediff_t timeout_ms; /* in milliseconds */ - timediff_t response_time = (data->set.server_response_timeout)? - data->set.server_response_timeout: pp->response_time; - - /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine - remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is - supposed to govern the response for any given server response, not for - the time from connect to the given server response. */ - - /* Without a requested timeout, we only wait 'response_time' seconds for the - full response to arrive before we bail out */ - timeout_ms = response_time - - Curl_timediff(Curl_now(), pp->response); /* spent time */ - - if(data->set.timeout && !disconnecting) { - /* if timeout is requested, find out how much remaining time we have */ - timediff_t timeout2_ms = data->set.timeout - /* timeout time */ - Curl_timediff(Curl_now(), conn->now); /* spent time */ - - /* pick the lowest number */ - timeout_ms = CURLMIN(timeout_ms, timeout2_ms); - } - - return timeout_ms; -} - -/* - * Curl_pp_statemach() - */ -CURLcode Curl_pp_statemach(struct Curl_easy *data, - struct pingpong *pp, bool block, - bool disconnecting) -{ - struct connectdata *conn = data->conn; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; - int rc; - timediff_t interval_ms; - timediff_t timeout_ms = Curl_pp_state_timeout(data, pp, disconnecting); - CURLcode result = CURLE_OK; - - if(timeout_ms <= 0) { - failf(data, "server response timeout"); - return CURLE_OPERATION_TIMEDOUT; /* already too little time */ - } - - if(block) { - interval_ms = 1000; /* use 1 second timeout intervals */ - if(timeout_ms < interval_ms) - interval_ms = timeout_ms; - } - else - interval_ms = 0; /* immediate */ - - if(Curl_ssl_data_pending(conn, FIRSTSOCKET)) - rc = 1; - else if(Curl_pp_moredata(pp)) - /* We are receiving and there is data in the cache so just read it */ - rc = 1; - else if(!pp->sendleft && Curl_ssl_data_pending(conn, FIRSTSOCKET)) - /* We are receiving and there is data ready in the SSL library */ - rc = 1; - else - rc = Curl_socket_check(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */ - CURL_SOCKET_BAD, - pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */ - interval_ms); - - if(block) { - /* if we didn't wait, we don't have to spend time on this now */ - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - else - result = Curl_speedcheck(data, Curl_now()); - - if(result) - return result; - } - - if(rc == -1) { - failf(data, "select/poll error"); - result = CURLE_OUT_OF_MEMORY; - } - else if(rc) - result = pp->statemachine(data, data->conn); - - return result; -} - -/* initialize stuff to prepare for reading a fresh new response */ -void Curl_pp_init(struct Curl_easy *data, struct pingpong *pp) -{ - DEBUGASSERT(data); - pp->nread_resp = 0; - pp->linestart_resp = data->state.buffer; - pp->pending_resp = TRUE; - pp->response = Curl_now(); /* start response time-out now! */ -} - -/* setup for the coming transfer */ -void Curl_pp_setup(struct pingpong *pp) -{ - Curl_dyn_init(&pp->sendbuf, DYN_PINGPPONG_CMD); -} - -/*********************************************************************** - * - * Curl_pp_vsendf() - * - * Send the formatted string as a command to a pingpong server. Note that - * the string should not have any CRLF appended, as this function will - * append the necessary things itself. - * - * made to never block - */ -CURLcode Curl_pp_vsendf(struct Curl_easy *data, - struct pingpong *pp, - const char *fmt, - va_list args) -{ - ssize_t bytes_written = 0; - size_t write_len; - char *s; - CURLcode result; - struct connectdata *conn = data->conn; - -#ifdef HAVE_GSSAPI - enum protection_level data_sec; -#endif - - DEBUGASSERT(pp->sendleft == 0); - DEBUGASSERT(pp->sendsize == 0); - DEBUGASSERT(pp->sendthis == NULL); - - if(!conn) - /* can't send without a connection! */ - return CURLE_SEND_ERROR; - - Curl_dyn_reset(&pp->sendbuf); - result = Curl_dyn_vaddf(&pp->sendbuf, fmt, args); - if(result) - return result; - - /* append CRLF */ - result = Curl_dyn_addn(&pp->sendbuf, "\r\n", 2); - if(result) - return result; - - write_len = Curl_dyn_len(&pp->sendbuf); - s = Curl_dyn_ptr(&pp->sendbuf); - Curl_pp_init(data, pp); - -#ifdef HAVE_GSSAPI - conn->data_prot = PROT_CMD; -#endif - result = Curl_write(data, conn->sock[FIRSTSOCKET], s, write_len, - &bytes_written); - if(result) - return result; -#ifdef HAVE_GSSAPI - data_sec = conn->data_prot; - DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST); - conn->data_prot = data_sec; -#endif - - Curl_debug(data, CURLINFO_HEADER_OUT, s, (size_t)bytes_written); - - if(bytes_written != (ssize_t)write_len) { - /* the whole chunk was not sent, keep it around and adjust sizes */ - pp->sendthis = s; - pp->sendsize = write_len; - pp->sendleft = write_len - bytes_written; - } - else { - pp->sendthis = NULL; - pp->sendleft = pp->sendsize = 0; - pp->response = Curl_now(); - } - - return CURLE_OK; -} - - -/*********************************************************************** - * - * Curl_pp_sendf() - * - * Send the formatted string as a command to a pingpong server. Note that - * the string should not have any CRLF appended, as this function will - * append the necessary things itself. - * - * made to never block - */ -CURLcode Curl_pp_sendf(struct Curl_easy *data, struct pingpong *pp, - const char *fmt, ...) -{ - CURLcode result; - va_list ap; - va_start(ap, fmt); - - result = Curl_pp_vsendf(data, pp, fmt, ap); - - va_end(ap); - - return result; -} - -/* - * Curl_pp_readresp() - * - * Reads a piece of a server response. - */ -CURLcode Curl_pp_readresp(struct Curl_easy *data, - curl_socket_t sockfd, - struct pingpong *pp, - int *code, /* return the server code if done */ - size_t *size) /* size of the response */ -{ - ssize_t perline; /* count bytes per line */ - bool keepon = TRUE; - ssize_t gotbytes; - char *ptr; - struct connectdata *conn = data->conn; - char * const buf = data->state.buffer; - CURLcode result = CURLE_OK; - - *code = 0; /* 0 for errors or not done */ - *size = 0; - - ptr = buf + pp->nread_resp; - - /* number of bytes in the current line, so far */ - perline = (ssize_t)(ptr-pp->linestart_resp); - - while((pp->nread_resp < (size_t)data->set.buffer_size) && - (keepon && !result)) { - - if(pp->cache) { - /* we had data in the "cache", copy that instead of doing an actual - * read - * - * pp->cache_size is cast to ssize_t here. This should be safe, because - * it would have been populated with something of size int to begin - * with, even though its datatype may be larger than an int. - */ - if((ptr + pp->cache_size) > (buf + data->set.buffer_size + 1)) { - failf(data, "cached response data too big to handle"); - return CURLE_WEIRD_SERVER_REPLY; - } - memcpy(ptr, pp->cache, pp->cache_size); - gotbytes = (ssize_t)pp->cache_size; - free(pp->cache); /* free the cache */ - pp->cache = NULL; /* clear the pointer */ - pp->cache_size = 0; /* zero the size just in case */ - } - else { -#ifdef HAVE_GSSAPI - enum protection_level prot = conn->data_prot; - conn->data_prot = PROT_CLEAR; -#endif - DEBUGASSERT((ptr + data->set.buffer_size - pp->nread_resp) <= - (buf + data->set.buffer_size + 1)); - result = Curl_read(data, sockfd, ptr, - data->set.buffer_size - pp->nread_resp, - &gotbytes); -#ifdef HAVE_GSSAPI - DEBUGASSERT(prot > PROT_NONE && prot < PROT_LAST); - conn->data_prot = prot; -#endif - if(result == CURLE_AGAIN) - return CURLE_OK; /* return */ - - if(result) - /* Set outer result variable to this error. */ - keepon = FALSE; - } - - if(!keepon) - ; - else if(gotbytes <= 0) { - keepon = FALSE; - result = CURLE_RECV_ERROR; - failf(data, "response reading failed (errno: %d)", SOCKERRNO); - } - else { - /* we got a whole chunk of data, which can be anything from one - * byte to a set of lines and possible just a piece of the last - * line */ - ssize_t i; - ssize_t clipamount = 0; - bool restart = FALSE; - - data->req.headerbytecount += (long)gotbytes; - - pp->nread_resp += gotbytes; - for(i = 0; i < gotbytes; ptr++, i++) { - perline++; - if(*ptr == '\n') { - /* a newline is CRLF in pp-talk, so the CR is ignored as - the line isn't really terminated until the LF comes */ - - /* output debug output if that is requested */ -#ifdef HAVE_GSSAPI - if(!conn->sec_complete) -#endif - Curl_debug(data, CURLINFO_HEADER_IN, - pp->linestart_resp, (size_t)perline); - - /* - * We pass all response-lines to the callback function registered - * for "headers". The response lines can be seen as a kind of - * headers. - */ - result = Curl_client_write(data, CLIENTWRITE_HEADER, - pp->linestart_resp, perline); - if(result) - return result; - - if(pp->endofresp(data, conn, pp->linestart_resp, perline, code)) { - /* This is the end of the last line, copy the last line to the - start of the buffer and null-terminate, for old times sake */ - size_t n = ptr - pp->linestart_resp; - memmove(buf, pp->linestart_resp, n); - buf[n] = 0; /* null-terminate */ - keepon = FALSE; - pp->linestart_resp = ptr + 1; /* advance pointer */ - i++; /* skip this before getting out */ - - *size = pp->nread_resp; /* size of the response */ - pp->nread_resp = 0; /* restart */ - break; - } - perline = 0; /* line starts over here */ - pp->linestart_resp = ptr + 1; - } - } - - if(!keepon && (i != gotbytes)) { - /* We found the end of the response lines, but we didn't parse the - full chunk of data we have read from the server. We therefore need - to store the rest of the data to be checked on the next invoke as - it may actually contain another end of response already! */ - clipamount = gotbytes - i; - restart = TRUE; - DEBUGF(infof(data, "Curl_pp_readresp_ %d bytes of trailing " - "server response left", - (int)clipamount)); - } - else if(keepon) { - - if((perline == gotbytes) && - (gotbytes > (ssize_t)data->set.buffer_size/2)) { - /* We got an excessive line without newlines and we need to deal - with it. We keep the first bytes of the line then we throw - away the rest. */ - infof(data, "Excessive server response line length received, " - "%zd bytes. Stripping", gotbytes); - restart = TRUE; - - /* we keep 40 bytes since all our pingpong protocols are only - interested in the first piece */ - clipamount = 40; - } - else if(pp->nread_resp > (size_t)data->set.buffer_size/2) { - /* We got a large chunk of data and there's potentially still - trailing data to take care of, so we put any such part in the - "cache", clear the buffer to make space and restart. */ - clipamount = perline; - restart = TRUE; - } - } - else if(i == gotbytes) - restart = TRUE; - - if(clipamount) { - pp->cache_size = clipamount; - pp->cache = malloc(pp->cache_size); - if(pp->cache) - memcpy(pp->cache, pp->linestart_resp, pp->cache_size); - else - return CURLE_OUT_OF_MEMORY; - } - if(restart) { - /* now reset a few variables to start over nicely from the start of - the big buffer */ - pp->nread_resp = 0; /* start over from scratch in the buffer */ - ptr = pp->linestart_resp = buf; - perline = 0; - } - - } /* there was data */ - - } /* while there's buffer left and loop is requested */ - - pp->pending_resp = FALSE; - - return result; -} - -int Curl_pp_getsock(struct Curl_easy *data, - struct pingpong *pp, curl_socket_t *socks) -{ - struct connectdata *conn = data->conn; - socks[0] = conn->sock[FIRSTSOCKET]; - - if(pp->sendleft) { - /* write mode */ - return GETSOCK_WRITESOCK(0); - } - - /* read mode */ - return GETSOCK_READSOCK(0); -} - -CURLcode Curl_pp_flushsend(struct Curl_easy *data, - struct pingpong *pp) -{ - /* we have a piece of a command still left to send */ - struct connectdata *conn = data->conn; - ssize_t written; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; - CURLcode result = Curl_write(data, sock, pp->sendthis + pp->sendsize - - pp->sendleft, pp->sendleft, &written); - if(result) - return result; - - if(written != (ssize_t)pp->sendleft) { - /* only a fraction was sent */ - pp->sendleft -= written; - } - else { - pp->sendthis = NULL; - pp->sendleft = pp->sendsize = 0; - pp->response = Curl_now(); - } - return CURLE_OK; -} - -CURLcode Curl_pp_disconnect(struct pingpong *pp) -{ - Curl_dyn_free(&pp->sendbuf); - Curl_safefree(pp->cache); - return CURLE_OK; -} - -bool Curl_pp_moredata(struct pingpong *pp) -{ - return (!pp->sendleft && pp->cache && pp->nread_resp < pp->cache_size) ? - TRUE : FALSE; -} - -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ * 'pingpong' is for generic back-and-forth support functions used by FTP,
+ * IMAP, POP3, SMTP and whatever more that likes them.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include "urldata.h"
+#include "cfilters.h"
+#include "sendf.h"
+#include "select.h"
+#include "progress.h"
+#include "speedcheck.h"
+#include "pingpong.h"
+#include "multiif.h"
+#include "vtls/vtls.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#ifdef USE_PINGPONG
+
+/* Returns timeout in ms. 0 or negative number means the timeout has already
+ triggered */
+timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
+ struct pingpong *pp, bool disconnecting)
+{
+ struct connectdata *conn = data->conn;
+ timediff_t timeout_ms; /* in milliseconds */
+ timediff_t response_time = (data->set.server_response_timeout)?
+ data->set.server_response_timeout: pp->response_time;
+
+ /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine
+ remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is
+ supposed to govern the response for any given server response, not for
+ the time from connect to the given server response. */
+
+ /* Without a requested timeout, we only wait 'response_time' seconds for the
+ full response to arrive before we bail out */
+ timeout_ms = response_time -
+ Curl_timediff(Curl_now(), pp->response); /* spent time */
+
+ if(data->set.timeout && !disconnecting) {
+ /* if timeout is requested, find out how much remaining time we have */
+ timediff_t timeout2_ms = data->set.timeout - /* timeout time */
+ Curl_timediff(Curl_now(), conn->now); /* spent time */
+
+ /* pick the lowest number */
+ timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
+ }
+
+ return timeout_ms;
+}
+
+/*
+ * Curl_pp_statemach()
+ */
+CURLcode Curl_pp_statemach(struct Curl_easy *data,
+ struct pingpong *pp, bool block,
+ bool disconnecting)
+{
+ struct connectdata *conn = data->conn;
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ int rc;
+ timediff_t interval_ms;
+ timediff_t timeout_ms = Curl_pp_state_timeout(data, pp, disconnecting);
+ CURLcode result = CURLE_OK;
+
+ if(timeout_ms <= 0) {
+ failf(data, "server response timeout");
+ return CURLE_OPERATION_TIMEDOUT; /* already too little time */
+ }
+
+ if(block) {
+ interval_ms = 1000; /* use 1 second timeout intervals */
+ if(timeout_ms < interval_ms)
+ interval_ms = timeout_ms;
+ }
+ else
+ interval_ms = 0; /* immediate */
+
+ if(Curl_conn_data_pending(data, FIRSTSOCKET))
+ rc = 1;
+ else if(Curl_pp_moredata(pp))
+ /* We are receiving and there is data in the cache so just read it */
+ rc = 1;
+ else if(!pp->sendleft && Curl_conn_data_pending(data, FIRSTSOCKET))
+ /* We are receiving and there is data ready in the SSL library */
+ rc = 1;
+ else
+ rc = Curl_socket_check(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
+ CURL_SOCKET_BAD,
+ pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
+ interval_ms);
+
+ if(block) {
+ /* if we didn't wait, we don't have to spend time on this now */
+ if(Curl_pgrsUpdate(data))
+ result = CURLE_ABORTED_BY_CALLBACK;
+ else
+ result = Curl_speedcheck(data, Curl_now());
+
+ if(result)
+ return result;
+ }
+
+ if(rc == -1) {
+ failf(data, "select/poll error");
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ else if(rc)
+ result = pp->statemachine(data, data->conn);
+
+ return result;
+}
+
+/* initialize stuff to prepare for reading a fresh new response */
+void Curl_pp_init(struct Curl_easy *data, struct pingpong *pp)
+{
+ DEBUGASSERT(data);
+ pp->nread_resp = 0;
+ pp->linestart_resp = data->state.buffer;
+ pp->pending_resp = TRUE;
+ pp->response = Curl_now(); /* start response time-out now! */
+}
+
+/* setup for the coming transfer */
+void Curl_pp_setup(struct pingpong *pp)
+{
+ Curl_dyn_init(&pp->sendbuf, DYN_PINGPPONG_CMD);
+}
+
+/***********************************************************************
+ *
+ * Curl_pp_vsendf()
+ *
+ * Send the formatted string as a command to a pingpong server. Note that
+ * the string should not have any CRLF appended, as this function will
+ * append the necessary things itself.
+ *
+ * made to never block
+ */
+CURLcode Curl_pp_vsendf(struct Curl_easy *data,
+ struct pingpong *pp,
+ const char *fmt,
+ va_list args)
+{
+ ssize_t bytes_written = 0;
+ size_t write_len;
+ char *s;
+ CURLcode result;
+ struct connectdata *conn = data->conn;
+
+#ifdef HAVE_GSSAPI
+ enum protection_level data_sec;
+#endif
+
+ DEBUGASSERT(pp->sendleft == 0);
+ DEBUGASSERT(pp->sendsize == 0);
+ DEBUGASSERT(pp->sendthis == NULL);
+
+ if(!conn)
+ /* can't send without a connection! */
+ return CURLE_SEND_ERROR;
+
+ Curl_dyn_reset(&pp->sendbuf);
+ result = Curl_dyn_vaddf(&pp->sendbuf, fmt, args);
+ if(result)
+ return result;
+
+ /* append CRLF */
+ result = Curl_dyn_addn(&pp->sendbuf, "\r\n", 2);
+ if(result)
+ return result;
+
+ write_len = Curl_dyn_len(&pp->sendbuf);
+ s = Curl_dyn_ptr(&pp->sendbuf);
+ Curl_pp_init(data, pp);
+
+#ifdef HAVE_GSSAPI
+ conn->data_prot = PROT_CMD;
+#endif
+ result = Curl_write(data, conn->sock[FIRSTSOCKET], s, write_len,
+ &bytes_written);
+ if(result)
+ return result;
+#ifdef HAVE_GSSAPI
+ data_sec = conn->data_prot;
+ DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
+ conn->data_prot = data_sec;
+#endif
+
+ Curl_debug(data, CURLINFO_HEADER_OUT, s, (size_t)bytes_written);
+
+ if(bytes_written != (ssize_t)write_len) {
+ /* the whole chunk was not sent, keep it around and adjust sizes */
+ pp->sendthis = s;
+ pp->sendsize = write_len;
+ pp->sendleft = write_len - bytes_written;
+ }
+ else {
+ pp->sendthis = NULL;
+ pp->sendleft = pp->sendsize = 0;
+ pp->response = Curl_now();
+ }
+
+ return CURLE_OK;
+}
+
+
+/***********************************************************************
+ *
+ * Curl_pp_sendf()
+ *
+ * Send the formatted string as a command to a pingpong server. Note that
+ * the string should not have any CRLF appended, as this function will
+ * append the necessary things itself.
+ *
+ * made to never block
+ */
+CURLcode Curl_pp_sendf(struct Curl_easy *data, struct pingpong *pp,
+ const char *fmt, ...)
+{
+ CURLcode result;
+ va_list ap;
+ va_start(ap, fmt);
+
+ result = Curl_pp_vsendf(data, pp, fmt, ap);
+
+ va_end(ap);
+
+ return result;
+}
+
+/*
+ * Curl_pp_readresp()
+ *
+ * Reads a piece of a server response.
+ */
+CURLcode Curl_pp_readresp(struct Curl_easy *data,
+ curl_socket_t sockfd,
+ struct pingpong *pp,
+ int *code, /* return the server code if done */
+ size_t *size) /* size of the response */
+{
+ ssize_t perline; /* count bytes per line */
+ bool keepon = TRUE;
+ ssize_t gotbytes;
+ char *ptr;
+ struct connectdata *conn = data->conn;
+ char * const buf = data->state.buffer;
+ CURLcode result = CURLE_OK;
+
+ *code = 0; /* 0 for errors or not done */
+ *size = 0;
+
+ ptr = buf + pp->nread_resp;
+
+ /* number of bytes in the current line, so far */
+ perline = (ssize_t)(ptr-pp->linestart_resp);
+
+ while((pp->nread_resp < (size_t)data->set.buffer_size) &&
+ (keepon && !result)) {
+
+ if(pp->cache) {
+ /* we had data in the "cache", copy that instead of doing an actual
+ * read
+ *
+ * pp->cache_size is cast to ssize_t here. This should be safe, because
+ * it would have been populated with something of size int to begin
+ * with, even though its datatype may be larger than an int.
+ */
+ if((ptr + pp->cache_size) > (buf + data->set.buffer_size + 1)) {
+ failf(data, "cached response data too big to handle");
+ return CURLE_WEIRD_SERVER_REPLY;
+ }
+ memcpy(ptr, pp->cache, pp->cache_size);
+ gotbytes = (ssize_t)pp->cache_size;
+ free(pp->cache); /* free the cache */
+ pp->cache = NULL; /* clear the pointer */
+ pp->cache_size = 0; /* zero the size just in case */
+ }
+ else {
+#ifdef HAVE_GSSAPI
+ enum protection_level prot = conn->data_prot;
+ conn->data_prot = PROT_CLEAR;
+#endif
+ DEBUGASSERT((ptr + data->set.buffer_size - pp->nread_resp) <=
+ (buf + data->set.buffer_size + 1));
+ result = Curl_read(data, sockfd, ptr,
+ data->set.buffer_size - pp->nread_resp,
+ &gotbytes);
+#ifdef HAVE_GSSAPI
+ DEBUGASSERT(prot > PROT_NONE && prot < PROT_LAST);
+ conn->data_prot = prot;
+#endif
+ if(result == CURLE_AGAIN)
+ return CURLE_OK; /* return */
+
+ if(result)
+ /* Set outer result variable to this error. */
+ keepon = FALSE;
+ }
+
+ if(!keepon)
+ ;
+ else if(gotbytes <= 0) {
+ keepon = FALSE;
+ result = CURLE_RECV_ERROR;
+ failf(data, "response reading failed (errno: %d)", SOCKERRNO);
+ }
+ else {
+ /* we got a whole chunk of data, which can be anything from one
+ * byte to a set of lines and possible just a piece of the last
+ * line */
+ ssize_t i;
+ ssize_t clipamount = 0;
+ bool restart = FALSE;
+
+ data->req.headerbytecount += (long)gotbytes;
+
+ pp->nread_resp += gotbytes;
+ for(i = 0; i < gotbytes; ptr++, i++) {
+ perline++;
+ if(*ptr == '\n') {
+ /* a newline is CRLF in pp-talk, so the CR is ignored as
+ the line isn't really terminated until the LF comes */
+
+ /* output debug output if that is requested */
+#ifdef HAVE_GSSAPI
+ if(!conn->sec_complete)
+#endif
+ Curl_debug(data, CURLINFO_HEADER_IN,
+ pp->linestart_resp, (size_t)perline);
+
+ /*
+ * We pass all response-lines to the callback function registered
+ * for "headers". The response lines can be seen as a kind of
+ * headers.
+ */
+ result = Curl_client_write(data, CLIENTWRITE_HEADER,
+ pp->linestart_resp, perline);
+ if(result)
+ return result;
+
+ if(pp->endofresp(data, conn, pp->linestart_resp, perline, code)) {
+ /* This is the end of the last line, copy the last line to the
+ start of the buffer and null-terminate, for old times sake */
+ size_t n = ptr - pp->linestart_resp;
+ memmove(buf, pp->linestart_resp, n);
+ buf[n] = 0; /* null-terminate */
+ keepon = FALSE;
+ pp->linestart_resp = ptr + 1; /* advance pointer */
+ i++; /* skip this before getting out */
+
+ *size = pp->nread_resp; /* size of the response */
+ pp->nread_resp = 0; /* restart */
+ break;
+ }
+ perline = 0; /* line starts over here */
+ pp->linestart_resp = ptr + 1;
+ }
+ }
+
+ if(!keepon && (i != gotbytes)) {
+ /* We found the end of the response lines, but we didn't parse the
+ full chunk of data we have read from the server. We therefore need
+ to store the rest of the data to be checked on the next invoke as
+ it may actually contain another end of response already! */
+ clipamount = gotbytes - i;
+ restart = TRUE;
+ DEBUGF(infof(data, "Curl_pp_readresp_ %d bytes of trailing "
+ "server response left",
+ (int)clipamount));
+ }
+ else if(keepon) {
+
+ if((perline == gotbytes) &&
+ (gotbytes > (ssize_t)data->set.buffer_size/2)) {
+ /* We got an excessive line without newlines and we need to deal
+ with it. We keep the first bytes of the line then we throw
+ away the rest. */
+ infof(data, "Excessive server response line length received, "
+ "%zd bytes. Stripping", gotbytes);
+ restart = TRUE;
+
+ /* we keep 40 bytes since all our pingpong protocols are only
+ interested in the first piece */
+ clipamount = 40;
+ }
+ else if(pp->nread_resp > (size_t)data->set.buffer_size/2) {
+ /* We got a large chunk of data and there's potentially still
+ trailing data to take care of, so we put any such part in the
+ "cache", clear the buffer to make space and restart. */
+ clipamount = perline;
+ restart = TRUE;
+ }
+ }
+ else if(i == gotbytes)
+ restart = TRUE;
+
+ if(clipamount) {
+ pp->cache_size = clipamount;
+ pp->cache = malloc(pp->cache_size);
+ if(pp->cache)
+ memcpy(pp->cache, pp->linestart_resp, pp->cache_size);
+ else
+ return CURLE_OUT_OF_MEMORY;
+ }
+ if(restart) {
+ /* now reset a few variables to start over nicely from the start of
+ the big buffer */
+ pp->nread_resp = 0; /* start over from scratch in the buffer */
+ ptr = pp->linestart_resp = buf;
+ perline = 0;
+ }
+
+ } /* there was data */
+
+ } /* while there's buffer left and loop is requested */
+
+ pp->pending_resp = FALSE;
+
+ return result;
+}
+
+int Curl_pp_getsock(struct Curl_easy *data,
+ struct pingpong *pp, curl_socket_t *socks)
+{
+ struct connectdata *conn = data->conn;
+ socks[0] = conn->sock[FIRSTSOCKET];
+
+ if(pp->sendleft) {
+ /* write mode */
+ return GETSOCK_WRITESOCK(0);
+ }
+
+ /* read mode */
+ return GETSOCK_READSOCK(0);
+}
+
+CURLcode Curl_pp_flushsend(struct Curl_easy *data,
+ struct pingpong *pp)
+{
+ /* we have a piece of a command still left to send */
+ struct connectdata *conn = data->conn;
+ ssize_t written;
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ CURLcode result = Curl_write(data, sock, pp->sendthis + pp->sendsize -
+ pp->sendleft, pp->sendleft, &written);
+ if(result)
+ return result;
+
+ if(written != (ssize_t)pp->sendleft) {
+ /* only a fraction was sent */
+ pp->sendleft -= written;
+ }
+ else {
+ pp->sendthis = NULL;
+ pp->sendleft = pp->sendsize = 0;
+ pp->response = Curl_now();
+ }
+ return CURLE_OK;
+}
+
+CURLcode Curl_pp_disconnect(struct pingpong *pp)
+{
+ Curl_dyn_free(&pp->sendbuf);
+ Curl_safefree(pp->cache);
+ return CURLE_OK;
+}
+
+bool Curl_pp_moredata(struct pingpong *pp)
+{
+ return (!pp->sendleft && pp->cache && pp->nread_resp < pp->cache_size) ?
+ TRUE : FALSE;
+}
+
+#endif
diff --git a/libs/libcurl/src/pingpong.h b/libs/libcurl/src/pingpong.h index cefae073a6..e610835d60 100644 --- a/libs/libcurl/src/pingpong.h +++ b/libs/libcurl/src/pingpong.h @@ -1,164 +1,164 @@ -#ifndef HEADER_CURL_PINGPONG_H -#define HEADER_CURL_PINGPONG_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_IMAP) || !defined(CURL_DISABLE_FTP) || \ - !defined(CURL_DISABLE_POP3) || !defined(CURL_DISABLE_SMTP) -#define USE_PINGPONG -#endif - -/* forward-declaration, this is defined in urldata.h */ -struct connectdata; - -typedef enum { - PPTRANSFER_BODY, /* yes do transfer a body */ - PPTRANSFER_INFO, /* do still go through to get info/headers */ - PPTRANSFER_NONE /* don't get anything and don't get info */ -} curl_pp_transfer; - -/* - * 'pingpong' is the generic struct used for protocols doing server<->client - * conversations in a back-and-forth style such as FTP, IMAP, POP3, SMTP etc. - * - * It holds response cache and non-blocking sending data. - */ -struct pingpong { - char *cache; /* data cache between getresponse()-calls */ - size_t cache_size; /* size of cache in bytes */ - size_t nread_resp; /* number of bytes currently read of a server response */ - char *linestart_resp; /* line start pointer for the server response - reader function */ - bool pending_resp; /* set TRUE when a server response is pending or in - progress, and is cleared once the last response is - read */ - char *sendthis; /* allocated pointer to a buffer that is to be sent to the - server */ - size_t sendleft; /* number of bytes left to send from the sendthis buffer */ - size_t sendsize; /* total size of the sendthis buffer */ - struct curltime response; /* set to Curl_now() when a command has been sent - off, used to time-out response reading */ - timediff_t response_time; /* When no timeout is given, this is the amount of - milliseconds we await for a server response. */ - struct dynbuf sendbuf; - - /* Function pointers the protocols MUST implement and provide for the - pingpong layer to function */ - - CURLcode (*statemachine)(struct Curl_easy *data, struct connectdata *conn); - bool (*endofresp)(struct Curl_easy *data, struct connectdata *conn, - char *ptr, size_t len, int *code); -}; - -#define PINGPONG_SETUP(pp,s,e) \ - do { \ - pp->response_time = RESP_TIMEOUT; \ - pp->statemachine = s; \ - pp->endofresp = e; \ - } while(0) - -/* - * Curl_pp_statemach() - * - * called repeatedly until done. Set 'wait' to make it wait a while on the - * socket if there's no traffic. - */ -CURLcode Curl_pp_statemach(struct Curl_easy *data, struct pingpong *pp, - bool block, bool disconnecting); - -/* initialize stuff to prepare for reading a fresh new response */ -void Curl_pp_init(struct Curl_easy *data, struct pingpong *pp); - -/* setup for the transfer */ -void Curl_pp_setup(struct pingpong *pp); - -/* Returns timeout in ms. 0 or negative number means the timeout has already - triggered */ -timediff_t Curl_pp_state_timeout(struct Curl_easy *data, - struct pingpong *pp, bool disconnecting); - - -/*********************************************************************** - * - * Curl_pp_sendf() - * - * Send the formatted string as a command to a pingpong server. Note that - * the string should not have any CRLF appended, as this function will - * append the necessary things itself. - * - * made to never block - */ -CURLcode Curl_pp_sendf(struct Curl_easy *data, - struct pingpong *pp, - const char *fmt, ...); - -/*********************************************************************** - * - * Curl_pp_vsendf() - * - * Send the formatted string as a command to a pingpong server. Note that - * the string should not have any CRLF appended, as this function will - * append the necessary things itself. - * - * made to never block - */ -CURLcode Curl_pp_vsendf(struct Curl_easy *data, - struct pingpong *pp, - const char *fmt, - va_list args); - -/* - * Curl_pp_readresp() - * - * Reads a piece of a server response. - */ -CURLcode Curl_pp_readresp(struct Curl_easy *data, - curl_socket_t sockfd, - struct pingpong *pp, - int *code, /* return the server code if done */ - size_t *size); /* size of the response */ - - -CURLcode Curl_pp_flushsend(struct Curl_easy *data, - struct pingpong *pp); - -/* call this when a pingpong connection is disconnected */ -CURLcode Curl_pp_disconnect(struct pingpong *pp); - -int Curl_pp_getsock(struct Curl_easy *data, struct pingpong *pp, - curl_socket_t *socks); - - -/*********************************************************************** - * - * Curl_pp_moredata() - * - * Returns whether there are still more data in the cache and so a call - * to Curl_pp_readresp() will not block. - */ -bool Curl_pp_moredata(struct pingpong *pp); - -#endif /* HEADER_CURL_PINGPONG_H */ +#ifndef HEADER_CURL_PINGPONG_H
+#define HEADER_CURL_PINGPONG_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_IMAP) || !defined(CURL_DISABLE_FTP) || \
+ !defined(CURL_DISABLE_POP3) || !defined(CURL_DISABLE_SMTP)
+#define USE_PINGPONG
+#endif
+
+/* forward-declaration, this is defined in urldata.h */
+struct connectdata;
+
+typedef enum {
+ PPTRANSFER_BODY, /* yes do transfer a body */
+ PPTRANSFER_INFO, /* do still go through to get info/headers */
+ PPTRANSFER_NONE /* don't get anything and don't get info */
+} curl_pp_transfer;
+
+/*
+ * 'pingpong' is the generic struct used for protocols doing server<->client
+ * conversations in a back-and-forth style such as FTP, IMAP, POP3, SMTP etc.
+ *
+ * It holds response cache and non-blocking sending data.
+ */
+struct pingpong {
+ char *cache; /* data cache between getresponse()-calls */
+ size_t cache_size; /* size of cache in bytes */
+ size_t nread_resp; /* number of bytes currently read of a server response */
+ char *linestart_resp; /* line start pointer for the server response
+ reader function */
+ bool pending_resp; /* set TRUE when a server response is pending or in
+ progress, and is cleared once the last response is
+ read */
+ char *sendthis; /* allocated pointer to a buffer that is to be sent to the
+ server */
+ size_t sendleft; /* number of bytes left to send from the sendthis buffer */
+ size_t sendsize; /* total size of the sendthis buffer */
+ struct curltime response; /* set to Curl_now() when a command has been sent
+ off, used to time-out response reading */
+ timediff_t response_time; /* When no timeout is given, this is the amount of
+ milliseconds we await for a server response. */
+ struct dynbuf sendbuf;
+
+ /* Function pointers the protocols MUST implement and provide for the
+ pingpong layer to function */
+
+ CURLcode (*statemachine)(struct Curl_easy *data, struct connectdata *conn);
+ bool (*endofresp)(struct Curl_easy *data, struct connectdata *conn,
+ char *ptr, size_t len, int *code);
+};
+
+#define PINGPONG_SETUP(pp,s,e) \
+ do { \
+ pp->response_time = RESP_TIMEOUT; \
+ pp->statemachine = s; \
+ pp->endofresp = e; \
+ } while(0)
+
+/*
+ * Curl_pp_statemach()
+ *
+ * called repeatedly until done. Set 'wait' to make it wait a while on the
+ * socket if there's no traffic.
+ */
+CURLcode Curl_pp_statemach(struct Curl_easy *data, struct pingpong *pp,
+ bool block, bool disconnecting);
+
+/* initialize stuff to prepare for reading a fresh new response */
+void Curl_pp_init(struct Curl_easy *data, struct pingpong *pp);
+
+/* setup for the transfer */
+void Curl_pp_setup(struct pingpong *pp);
+
+/* Returns timeout in ms. 0 or negative number means the timeout has already
+ triggered */
+timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
+ struct pingpong *pp, bool disconnecting);
+
+
+/***********************************************************************
+ *
+ * Curl_pp_sendf()
+ *
+ * Send the formatted string as a command to a pingpong server. Note that
+ * the string should not have any CRLF appended, as this function will
+ * append the necessary things itself.
+ *
+ * made to never block
+ */
+CURLcode Curl_pp_sendf(struct Curl_easy *data,
+ struct pingpong *pp,
+ const char *fmt, ...);
+
+/***********************************************************************
+ *
+ * Curl_pp_vsendf()
+ *
+ * Send the formatted string as a command to a pingpong server. Note that
+ * the string should not have any CRLF appended, as this function will
+ * append the necessary things itself.
+ *
+ * made to never block
+ */
+CURLcode Curl_pp_vsendf(struct Curl_easy *data,
+ struct pingpong *pp,
+ const char *fmt,
+ va_list args);
+
+/*
+ * Curl_pp_readresp()
+ *
+ * Reads a piece of a server response.
+ */
+CURLcode Curl_pp_readresp(struct Curl_easy *data,
+ curl_socket_t sockfd,
+ struct pingpong *pp,
+ int *code, /* return the server code if done */
+ size_t *size); /* size of the response */
+
+
+CURLcode Curl_pp_flushsend(struct Curl_easy *data,
+ struct pingpong *pp);
+
+/* call this when a pingpong connection is disconnected */
+CURLcode Curl_pp_disconnect(struct pingpong *pp);
+
+int Curl_pp_getsock(struct Curl_easy *data, struct pingpong *pp,
+ curl_socket_t *socks);
+
+
+/***********************************************************************
+ *
+ * Curl_pp_moredata()
+ *
+ * Returns whether there are still more data in the cache and so a call
+ * to Curl_pp_readresp() will not block.
+ */
+bool Curl_pp_moredata(struct pingpong *pp);
+
+#endif /* HEADER_CURL_PINGPONG_H */
diff --git a/libs/libcurl/src/pop3.c b/libs/libcurl/src/pop3.c index 3151a3f56a..c76c3e1b91 100644 --- a/libs/libcurl/src/pop3.c +++ b/libs/libcurl/src/pop3.c @@ -1,1586 +1,1586 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - * RFC1734 POP3 Authentication - * RFC1939 POP3 protocol - * RFC2195 CRAM-MD5 authentication - * RFC2384 POP URL Scheme - * RFC2449 POP3 Extension Mechanism - * RFC2595 Using TLS with IMAP, POP3 and ACAP - * RFC2831 DIGEST-MD5 authentication - * RFC4422 Simple Authentication and Security Layer (SASL) - * RFC4616 PLAIN authentication - * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism - * RFC5034 POP3 SASL Authentication Mechanism - * RFC6749 OAuth 2.0 Authorization Framework - * RFC8314 Use of TLS for Email Submission and Access - * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt> - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifndef CURL_DISABLE_POP3 - -#ifdef HAVE_NETINET_IN_H -#include <netinet/in.h> -#endif -#ifdef HAVE_ARPA_INET_H -#include <arpa/inet.h> -#endif -#ifdef HAVE_UTSNAME_H -#include <sys/utsname.h> -#endif -#ifdef HAVE_NETDB_H -#include <netdb.h> -#endif -#ifdef __VMS -#include <in.h> -#include <inet.h> -#endif - -#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) -#undef in_addr_t -#define in_addr_t unsigned long -#endif - -#include <curl/curl.h> -#include "urldata.h" -#include "sendf.h" -#include "hostip.h" -#include "progress.h" -#include "transfer.h" -#include "escape.h" -#include "http.h" /* for HTTP proxy tunnel stuff */ -#include "socks.h" -#include "pop3.h" -#include "strtoofft.h" -#include "strcase.h" -#include "vtls/vtls.h" -#include "connect.h" -#include "select.h" -#include "multiif.h" -#include "url.h" -#include "bufref.h" -#include "curl_sasl.h" -#include "curl_md5.h" -#include "warnless.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* Local API functions */ -static CURLcode pop3_regular_transfer(struct Curl_easy *data, bool *done); -static CURLcode pop3_do(struct Curl_easy *data, bool *done); -static CURLcode pop3_done(struct Curl_easy *data, CURLcode status, - bool premature); -static CURLcode pop3_connect(struct Curl_easy *data, bool *done); -static CURLcode pop3_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead); -static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done); -static int pop3_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks); -static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done); -static CURLcode pop3_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode pop3_parse_url_options(struct connectdata *conn); -static CURLcode pop3_parse_url_path(struct Curl_easy *data); -static CURLcode pop3_parse_custom_request(struct Curl_easy *data); -static CURLcode pop3_perform_auth(struct Curl_easy *data, const char *mech, - const struct bufref *initresp); -static CURLcode pop3_continue_auth(struct Curl_easy *data, const char *mech, - const struct bufref *resp); -static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech); -static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out); - -/* - * POP3 protocol handler. - */ - -const struct Curl_handler Curl_handler_pop3 = { - "POP3", /* scheme */ - pop3_setup_connection, /* setup_connection */ - pop3_do, /* do_it */ - pop3_done, /* done */ - ZERO_NULL, /* do_more */ - pop3_connect, /* connect_it */ - pop3_multi_statemach, /* connecting */ - pop3_doing, /* doing */ - pop3_getsock, /* proto_getsock */ - pop3_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - pop3_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_POP3, /* defport */ - CURLPROTO_POP3, /* protocol */ - CURLPROTO_POP3, /* family */ - PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */ - PROTOPT_URLOPTIONS -}; - -#ifdef USE_SSL -/* - * POP3S protocol handler. - */ - -const struct Curl_handler Curl_handler_pop3s = { - "POP3S", /* scheme */ - pop3_setup_connection, /* setup_connection */ - pop3_do, /* do_it */ - pop3_done, /* done */ - ZERO_NULL, /* do_more */ - pop3_connect, /* connect_it */ - pop3_multi_statemach, /* connecting */ - pop3_doing, /* doing */ - pop3_getsock, /* proto_getsock */ - pop3_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - pop3_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_POP3S, /* defport */ - CURLPROTO_POP3S, /* protocol */ - CURLPROTO_POP3, /* family */ - PROTOPT_CLOSEACTION | PROTOPT_SSL - | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */ -}; -#endif - -/* SASL parameters for the pop3 protocol */ -static const struct SASLproto saslpop3 = { - "pop", /* The service name */ - pop3_perform_auth, /* Send authentication command */ - pop3_continue_auth, /* Send authentication continuation */ - pop3_cancel_auth, /* Send authentication cancellation */ - pop3_get_message, /* Get SASL response message */ - 255 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */ - '*', /* Code received when continuation is expected */ - '+', /* Code to receive upon authentication success */ - SASL_AUTH_DEFAULT, /* Default mechanisms */ - SASL_FLAG_BASE64 /* Configuration flags */ -}; - -#ifdef USE_SSL -static void pop3_to_pop3s(struct connectdata *conn) -{ - /* Change the connection handler */ - conn->handler = &Curl_handler_pop3s; - - /* Set the connection's upgraded to TLS flag */ - conn->bits.tls_upgraded = TRUE; -} -#else -#define pop3_to_pop3s(x) Curl_nop_stmt -#endif - -/*********************************************************************** - * - * pop3_endofresp() - * - * Checks for an ending POP3 status code at the start of the given string, but - * also detects the APOP timestamp from the server greeting and various - * capabilities from the CAPA response including the supported authentication - * types and allowed SASL mechanisms. - */ -static bool pop3_endofresp(struct Curl_easy *data, struct connectdata *conn, - char *line, size_t len, int *resp) -{ - struct pop3_conn *pop3c = &conn->proto.pop3c; - (void)data; - - /* Do we have an error response? */ - if(len >= 4 && !memcmp("-ERR", line, 4)) { - *resp = '-'; - - return TRUE; - } - - /* Are we processing CAPA command responses? */ - if(pop3c->state == POP3_CAPA) { - /* Do we have the terminating line? */ - if(len >= 1 && line[0] == '.') - /* Treat the response as a success */ - *resp = '+'; - else - /* Treat the response as an untagged continuation */ - *resp = '*'; - - return TRUE; - } - - /* Do we have a success response? */ - if(len >= 3 && !memcmp("+OK", line, 3)) { - *resp = '+'; - - return TRUE; - } - - /* Do we have a continuation response? */ - if(len >= 1 && line[0] == '+') { - *resp = '*'; - - return TRUE; - } - - return FALSE; /* Nothing for us */ -} - -/*********************************************************************** - * - * pop3_get_message() - * - * Gets the authentication message from the response buffer. - */ -static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out) -{ - char *message = data->state.buffer; - size_t len = strlen(message); - - if(len > 2) { - /* Find the start of the message */ - len -= 2; - for(message += 2; *message == ' ' || *message == '\t'; message++, len--) - ; - - /* Find the end of the message */ - while(len--) - if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && - message[len] != '\t') - break; - - /* Terminate the message */ - message[++len] = '\0'; - Curl_bufref_set(out, message, len, NULL); - } - else - /* junk input => zero length output */ - Curl_bufref_set(out, "", 0, NULL); - - return CURLE_OK; -} - -/*********************************************************************** - * - * state() - * - * This is the ONLY way to change POP3 state! - */ -static void state(struct Curl_easy *data, pop3state newstate) -{ - struct pop3_conn *pop3c = &data->conn->proto.pop3c; -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - /* for debug purposes */ - static const char * const names[] = { - "STOP", - "SERVERGREET", - "CAPA", - "STARTTLS", - "UPGRADETLS", - "AUTH", - "APOP", - "USER", - "PASS", - "COMMAND", - "QUIT", - /* LAST */ - }; - - if(pop3c->state != newstate) - infof(data, "POP3 %p state change from %s to %s", - (void *)pop3c, names[pop3c->state], names[newstate]); -#endif - - pop3c->state = newstate; -} - -/*********************************************************************** - * - * pop3_perform_capa() - * - * Sends the CAPA command in order to obtain a list of server side supported - * capabilities. - */ -static CURLcode pop3_perform_capa(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - struct pop3_conn *pop3c = &conn->proto.pop3c; - - pop3c->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */ - pop3c->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */ - pop3c->tls_supported = FALSE; /* Clear the TLS capability */ - - /* Send the CAPA command */ - result = Curl_pp_sendf(data, &pop3c->pp, "%s", "CAPA"); - - if(!result) - state(data, POP3_CAPA); - - return result; -} - -/*********************************************************************** - * - * pop3_perform_starttls() - * - * Sends the STLS command to start the upgrade to TLS. - */ -static CURLcode pop3_perform_starttls(struct Curl_easy *data, - struct connectdata *conn) -{ - /* Send the STLS command */ - CURLcode result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", "STLS"); - - if(!result) - state(data, POP3_STARTTLS); - - return result; -} - -/*********************************************************************** - * - * pop3_perform_upgrade_tls() - * - * Performs the upgrade to TLS. - */ -static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data, - struct connectdata *conn) -{ - /* Start the SSL connection */ - struct pop3_conn *pop3c = &conn->proto.pop3c; - CURLcode result = - Curl_ssl_connect_nonblocking(data, conn, FALSE, FIRSTSOCKET, - &pop3c->ssldone); - - if(!result) { - if(pop3c->state != POP3_UPGRADETLS) - state(data, POP3_UPGRADETLS); - - if(pop3c->ssldone) { - pop3_to_pop3s(conn); - result = pop3_perform_capa(data, conn); - } - } - - return result; -} - -/*********************************************************************** - * - * pop3_perform_user() - * - * Sends a clear text USER command to authenticate with. - */ -static CURLcode pop3_perform_user(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - - /* Check we have a username and password to authenticate with and end the - connect phase if we don't */ - if(!data->state.aptr.user) { - state(data, POP3_STOP); - - return result; - } - - /* Send the USER command */ - result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "USER %s", - conn->user ? conn->user : ""); - if(!result) - state(data, POP3_USER); - - return result; -} - -#ifndef CURL_DISABLE_CRYPTO_AUTH -/*********************************************************************** - * - * pop3_perform_apop() - * - * Sends an APOP command to authenticate with. - */ -static CURLcode pop3_perform_apop(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - struct pop3_conn *pop3c = &conn->proto.pop3c; - size_t i; - struct MD5_context *ctxt; - unsigned char digest[MD5_DIGEST_LEN]; - char secret[2 * MD5_DIGEST_LEN + 1]; - - /* Check we have a username and password to authenticate with and end the - connect phase if we don't */ - if(!data->state.aptr.user) { - state(data, POP3_STOP); - - return result; - } - - /* Create the digest */ - ctxt = Curl_MD5_init(Curl_DIGEST_MD5); - if(!ctxt) - return CURLE_OUT_OF_MEMORY; - - Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp, - curlx_uztoui(strlen(pop3c->apoptimestamp))); - - Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd, - curlx_uztoui(strlen(conn->passwd))); - - /* Finalise the digest */ - Curl_MD5_final(ctxt, digest); - - /* Convert the calculated 16 octet digest into a 32 byte hex string */ - for(i = 0; i < MD5_DIGEST_LEN; i++) - msnprintf(&secret[2 * i], 3, "%02x", digest[i]); - - result = Curl_pp_sendf(data, &pop3c->pp, "APOP %s %s", conn->user, secret); - - if(!result) - state(data, POP3_APOP); - - return result; -} -#endif - -/*********************************************************************** - * - * pop3_perform_auth() - * - * Sends an AUTH command allowing the client to login with the given SASL - * authentication mechanism. - */ -static CURLcode pop3_perform_auth(struct Curl_easy *data, - const char *mech, - const struct bufref *initresp) -{ - CURLcode result = CURLE_OK; - struct pop3_conn *pop3c = &data->conn->proto.pop3c; - const char *ir = (const char *) Curl_bufref_ptr(initresp); - - if(ir) { /* AUTH <mech> ...<crlf> */ - /* Send the AUTH command with the initial response */ - result = Curl_pp_sendf(data, &pop3c->pp, "AUTH %s %s", mech, ir); - } - else { - /* Send the AUTH command */ - result = Curl_pp_sendf(data, &pop3c->pp, "AUTH %s", mech); - } - - return result; -} - -/*********************************************************************** - * - * pop3_continue_auth() - * - * Sends SASL continuation data. - */ -static CURLcode pop3_continue_auth(struct Curl_easy *data, - const char *mech, - const struct bufref *resp) -{ - struct pop3_conn *pop3c = &data->conn->proto.pop3c; - - (void)mech; - - return Curl_pp_sendf(data, &pop3c->pp, - "%s", (const char *) Curl_bufref_ptr(resp)); -} - -/*********************************************************************** - * - * pop3_cancel_auth() - * - * Sends SASL cancellation. - */ -static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech) -{ - struct pop3_conn *pop3c = &data->conn->proto.pop3c; - - (void)mech; - - return Curl_pp_sendf(data, &pop3c->pp, "*"); -} - -/*********************************************************************** - * - * pop3_perform_authentication() - * - * Initiates the authentication sequence, with the appropriate SASL - * authentication mechanism, falling back to APOP and clear text should a - * common mechanism not be available between the client and server. - */ -static CURLcode pop3_perform_authentication(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - struct pop3_conn *pop3c = &conn->proto.pop3c; - saslprogress progress = SASL_IDLE; - - /* Check we have enough data to authenticate with and end the - connect phase if we don't */ - if(!Curl_sasl_can_authenticate(&pop3c->sasl, data)) { - state(data, POP3_STOP); - return result; - } - - if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_SASL) { - /* Calculate the SASL login details */ - result = Curl_sasl_start(&pop3c->sasl, data, FALSE, &progress); - - if(!result) - if(progress == SASL_INPROGRESS) - state(data, POP3_AUTH); - } - - if(!result && progress == SASL_IDLE) { -#ifndef CURL_DISABLE_CRYPTO_AUTH - if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP) - /* Perform APOP authentication */ - result = pop3_perform_apop(data, conn); - else -#endif - if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT) - /* Perform clear text authentication */ - result = pop3_perform_user(data, conn); - else { - /* Other mechanisms not supported */ - infof(data, "No known authentication mechanisms supported"); - result = CURLE_LOGIN_DENIED; - } - } - - return result; -} - -/*********************************************************************** - * - * pop3_perform_command() - * - * Sends a POP3 based command. - */ -static CURLcode pop3_perform_command(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct POP3 *pop3 = data->req.p.pop3; - const char *command = NULL; - - /* Calculate the default command */ - if(pop3->id[0] == '\0' || data->set.list_only) { - command = "LIST"; - - if(pop3->id[0] != '\0') - /* Message specific LIST so skip the BODY transfer */ - pop3->transfer = PPTRANSFER_INFO; - } - else - command = "RETR"; - - /* Send the command */ - if(pop3->id[0] != '\0') - result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s %s", - (pop3->custom && pop3->custom[0] != '\0' ? - pop3->custom : command), pop3->id); - else - result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", - (pop3->custom && pop3->custom[0] != '\0' ? - pop3->custom : command)); - - if(!result) - state(data, POP3_COMMAND); - - return result; -} - -/*********************************************************************** - * - * pop3_perform_quit() - * - * Performs the quit action prior to sclose() be called. - */ -static CURLcode pop3_perform_quit(struct Curl_easy *data, - struct connectdata *conn) -{ - /* Send the QUIT command */ - CURLcode result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", "QUIT"); - - if(!result) - state(data, POP3_QUIT); - - return result; -} - -/* For the initial server greeting */ -static CURLcode pop3_state_servergreet_resp(struct Curl_easy *data, - int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct pop3_conn *pop3c = &conn->proto.pop3c; - const char *line = data->state.buffer; - size_t len = strlen(line); - - (void)instate; /* no use for this yet */ - - if(pop3code != '+') { - failf(data, "Got unexpected pop3-server response"); - result = CURLE_WEIRD_SERVER_REPLY; - } - else { - /* Does the server support APOP authentication? */ - if(len >= 4 && line[len - 2] == '>') { - /* Look for the APOP timestamp */ - size_t i; - for(i = 3; i < len - 2; ++i) { - if(line[i] == '<') { - /* Calculate the length of the timestamp */ - size_t timestamplen = len - 1 - i; - char *at; - if(!timestamplen) - break; - - /* Allocate some memory for the timestamp */ - pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1); - - if(!pop3c->apoptimestamp) - break; - - /* Copy the timestamp */ - memcpy(pop3c->apoptimestamp, line + i, timestamplen); - pop3c->apoptimestamp[timestamplen] = '\0'; - - /* If the timestamp does not contain '@' it is not (as required by - RFC-1939) conformant to the RFC-822 message id syntax, and we - therefore do not use APOP authentication. */ - at = strchr(pop3c->apoptimestamp, '@'); - if(!at) - Curl_safefree(pop3c->apoptimestamp); - else - /* Store the APOP capability */ - pop3c->authtypes |= POP3_TYPE_APOP; - break; - } - } - } - - result = pop3_perform_capa(data, conn); - } - - return result; -} - -/* For CAPA responses */ -static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct pop3_conn *pop3c = &conn->proto.pop3c; - const char *line = data->state.buffer; - size_t len = strlen(line); - - (void)instate; /* no use for this yet */ - - /* Do we have a untagged continuation response? */ - if(pop3code == '*') { - /* Does the server support the STLS capability? */ - if(len >= 4 && !memcmp(line, "STLS", 4)) - pop3c->tls_supported = TRUE; - - /* Does the server support clear text authentication? */ - else if(len >= 4 && !memcmp(line, "USER", 4)) - pop3c->authtypes |= POP3_TYPE_CLEARTEXT; - - /* Does the server support SASL based authentication? */ - else if(len >= 5 && !memcmp(line, "SASL ", 5)) { - pop3c->authtypes |= POP3_TYPE_SASL; - - /* Advance past the SASL keyword */ - line += 5; - len -= 5; - - /* Loop through the data line */ - for(;;) { - size_t llen; - size_t wordlen; - unsigned short mechbit; - - while(len && - (*line == ' ' || *line == '\t' || - *line == '\r' || *line == '\n')) { - - line++; - len--; - } - - if(!len) - break; - - /* Extract the word */ - for(wordlen = 0; wordlen < len && line[wordlen] != ' ' && - line[wordlen] != '\t' && line[wordlen] != '\r' && - line[wordlen] != '\n';) - wordlen++; - - /* Test the word for a matching authentication mechanism */ - mechbit = Curl_sasl_decode_mech(line, wordlen, &llen); - if(mechbit && llen == wordlen) - pop3c->sasl.authmechs |= mechbit; - - line += wordlen; - len -= wordlen; - } - } - } - else { - /* Clear text is supported when CAPA isn't recognised */ - if(pop3code != '+') - pop3c->authtypes |= POP3_TYPE_CLEARTEXT; - - if(!data->set.use_ssl || conn->ssl[FIRSTSOCKET].use) - result = pop3_perform_authentication(data, conn); - else if(pop3code == '+' && pop3c->tls_supported) - /* Switch to TLS connection now */ - result = pop3_perform_starttls(data, conn); - else if(data->set.use_ssl <= CURLUSESSL_TRY) - /* Fallback and carry on with authentication */ - result = pop3_perform_authentication(data, conn); - else { - failf(data, "STLS not supported."); - result = CURLE_USE_SSL_FAILED; - } - } - - return result; -} - -/* For STARTTLS responses */ -static CURLcode pop3_state_starttls_resp(struct Curl_easy *data, - struct connectdata *conn, - int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - (void)instate; /* no use for this yet */ - - /* Pipelining in response is forbidden. */ - if(data->conn->proto.pop3c.pp.cache_size) - return CURLE_WEIRD_SERVER_REPLY; - - if(pop3code != '+') { - if(data->set.use_ssl != CURLUSESSL_TRY) { - failf(data, "STARTTLS denied"); - result = CURLE_USE_SSL_FAILED; - } - else - result = pop3_perform_authentication(data, conn); - } - else - result = pop3_perform_upgrade_tls(data, conn); - - return result; -} - -/* For SASL authentication responses */ -static CURLcode pop3_state_auth_resp(struct Curl_easy *data, - int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct pop3_conn *pop3c = &conn->proto.pop3c; - saslprogress progress; - - (void)instate; /* no use for this yet */ - - result = Curl_sasl_continue(&pop3c->sasl, data, pop3code, &progress); - if(!result) - switch(progress) { - case SASL_DONE: - state(data, POP3_STOP); /* Authenticated */ - break; - case SASL_IDLE: /* No mechanism left after cancellation */ -#ifndef CURL_DISABLE_CRYPTO_AUTH - if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP) - /* Perform APOP authentication */ - result = pop3_perform_apop(data, conn); - else -#endif - if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT) - /* Perform clear text authentication */ - result = pop3_perform_user(data, conn); - else { - failf(data, "Authentication cancelled"); - result = CURLE_LOGIN_DENIED; - } - break; - default: - break; - } - - return result; -} - -#ifndef CURL_DISABLE_CRYPTO_AUTH -/* For APOP responses */ -static CURLcode pop3_state_apop_resp(struct Curl_easy *data, int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - (void)instate; /* no use for this yet */ - - if(pop3code != '+') { - failf(data, "Authentication failed: %d", pop3code); - result = CURLE_LOGIN_DENIED; - } - else - /* End of connect phase */ - state(data, POP3_STOP); - - return result; -} -#endif - -/* For USER responses */ -static CURLcode pop3_state_user_resp(struct Curl_easy *data, int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - (void)instate; /* no use for this yet */ - - if(pop3code != '+') { - failf(data, "Access denied. %c", pop3code); - result = CURLE_LOGIN_DENIED; - } - else - /* Send the PASS command */ - result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "PASS %s", - conn->passwd ? conn->passwd : ""); - if(!result) - state(data, POP3_PASS); - - return result; -} - -/* For PASS responses */ -static CURLcode pop3_state_pass_resp(struct Curl_easy *data, int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - (void)instate; /* no use for this yet */ - - if(pop3code != '+') { - failf(data, "Access denied. %c", pop3code); - result = CURLE_LOGIN_DENIED; - } - else - /* End of connect phase */ - state(data, POP3_STOP); - - return result; -} - -/* For command responses */ -static CURLcode pop3_state_command_resp(struct Curl_easy *data, - int pop3code, - pop3state instate) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct POP3 *pop3 = data->req.p.pop3; - struct pop3_conn *pop3c = &conn->proto.pop3c; - struct pingpong *pp = &pop3c->pp; - - (void)instate; /* no use for this yet */ - - if(pop3code != '+') { - state(data, POP3_STOP); - return CURLE_WEIRD_SERVER_REPLY; - } - - /* This 'OK' line ends with a CR LF pair which is the two first bytes of the - EOB string so count this is two matching bytes. This is necessary to make - the code detect the EOB if the only data than comes now is %2e CR LF like - when there is no body to return. */ - pop3c->eob = 2; - - /* But since this initial CR LF pair is not part of the actual body, we set - the strip counter here so that these bytes won't be delivered. */ - pop3c->strip = 2; - - if(pop3->transfer == PPTRANSFER_BODY) { - /* POP3 download */ - Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); - - if(pp->cache) { - /* The header "cache" contains a bunch of data that is actually body - content so send it as such. Note that there may even be additional - "headers" after the body */ - - if(!data->set.opt_no_body) { - result = Curl_pop3_write(data, pp->cache, pp->cache_size); - if(result) - return result; - } - - /* Free the cache */ - Curl_safefree(pp->cache); - - /* Reset the cache size */ - pp->cache_size = 0; - } - } - - /* End of DO phase */ - state(data, POP3_STOP); - - return result; -} - -static CURLcode pop3_statemachine(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; - int pop3code; - struct pop3_conn *pop3c = &conn->proto.pop3c; - struct pingpong *pp = &pop3c->pp; - size_t nread = 0; - (void)data; - - /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */ - if(pop3c->state == POP3_UPGRADETLS) - return pop3_perform_upgrade_tls(data, conn); - - /* Flush any data that needs to be sent */ - if(pp->sendleft) - return Curl_pp_flushsend(data, pp); - - do { - /* Read the response from the server */ - result = Curl_pp_readresp(data, sock, pp, &pop3code, &nread); - if(result) - return result; - - if(!pop3code) - break; - - /* We have now received a full POP3 server response */ - switch(pop3c->state) { - case POP3_SERVERGREET: - result = pop3_state_servergreet_resp(data, pop3code, pop3c->state); - break; - - case POP3_CAPA: - result = pop3_state_capa_resp(data, pop3code, pop3c->state); - break; - - case POP3_STARTTLS: - result = pop3_state_starttls_resp(data, conn, pop3code, pop3c->state); - break; - - case POP3_AUTH: - result = pop3_state_auth_resp(data, pop3code, pop3c->state); - break; - -#ifndef CURL_DISABLE_CRYPTO_AUTH - case POP3_APOP: - result = pop3_state_apop_resp(data, pop3code, pop3c->state); - break; -#endif - - case POP3_USER: - result = pop3_state_user_resp(data, pop3code, pop3c->state); - break; - - case POP3_PASS: - result = pop3_state_pass_resp(data, pop3code, pop3c->state); - break; - - case POP3_COMMAND: - result = pop3_state_command_resp(data, pop3code, pop3c->state); - break; - - case POP3_QUIT: - state(data, POP3_STOP); - break; - - default: - /* internal error */ - state(data, POP3_STOP); - break; - } - } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp)); - - return result; -} - -/* Called repeatedly until done from multi.c */ -static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct pop3_conn *pop3c = &conn->proto.pop3c; - - if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) { - result = Curl_ssl_connect_nonblocking(data, conn, FALSE, - FIRSTSOCKET, &pop3c->ssldone); - if(result || !pop3c->ssldone) - return result; - } - - result = Curl_pp_statemach(data, &pop3c->pp, FALSE, FALSE); - *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE; - - return result; -} - -static CURLcode pop3_block_statemach(struct Curl_easy *data, - struct connectdata *conn, - bool disconnecting) -{ - CURLcode result = CURLE_OK; - struct pop3_conn *pop3c = &conn->proto.pop3c; - - while(pop3c->state != POP3_STOP && !result) - result = Curl_pp_statemach(data, &pop3c->pp, TRUE, disconnecting); - - return result; -} - -/* Allocate and initialize the POP3 struct for the current Curl_easy if - required */ -static CURLcode pop3_init(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct POP3 *pop3; - - pop3 = data->req.p.pop3 = calloc(sizeof(struct POP3), 1); - if(!pop3) - result = CURLE_OUT_OF_MEMORY; - - return result; -} - -/* For the POP3 "protocol connect" and "doing" phases only */ -static int pop3_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks) -{ - return Curl_pp_getsock(data, &conn->proto.pop3c.pp, socks); -} - -/*********************************************************************** - * - * pop3_connect() - * - * This function should do everything that is to be considered a part of the - * connection phase. - * - * The variable 'done' points to will be TRUE if the protocol-layer connect - * phase is done when this function returns, or FALSE if not. - */ -static CURLcode pop3_connect(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct pop3_conn *pop3c = &conn->proto.pop3c; - struct pingpong *pp = &pop3c->pp; - - *done = FALSE; /* default to not done yet */ - - /* We always support persistent connections in POP3 */ - connkeep(conn, "POP3 default"); - - PINGPONG_SETUP(pp, pop3_statemachine, pop3_endofresp); - - /* Set the default preferred authentication type and mechanism */ - pop3c->preftype = POP3_TYPE_ANY; - Curl_sasl_init(&pop3c->sasl, data, &saslpop3); - - /* Initialise the pingpong layer */ - Curl_pp_setup(pp); - Curl_pp_init(data, pp); - - /* Parse the URL options */ - result = pop3_parse_url_options(conn); - if(result) - return result; - - /* Start off waiting for the server greeting response */ - state(data, POP3_SERVERGREET); - - result = pop3_multi_statemach(data, done); - - return result; -} - -/*********************************************************************** - * - * pop3_done() - * - * The DONE function. This does what needs to be done after a single DO has - * performed. - * - * Input argument is already checked for validity. - */ -static CURLcode pop3_done(struct Curl_easy *data, CURLcode status, - bool premature) -{ - CURLcode result = CURLE_OK; - struct POP3 *pop3 = data->req.p.pop3; - - (void)premature; - - if(!pop3) - return CURLE_OK; - - if(status) { - connclose(data->conn, "POP3 done with bad status"); - result = status; /* use the already set error code */ - } - - /* Cleanup our per-request based variables */ - Curl_safefree(pop3->id); - Curl_safefree(pop3->custom); - - /* Clear the transfer mode for the next request */ - pop3->transfer = PPTRANSFER_BODY; - - return result; -} - -/*********************************************************************** - * - * pop3_perform() - * - * This is the actual DO function for POP3. Get a message/listing according to - * the options previously setup. - */ -static CURLcode pop3_perform(struct Curl_easy *data, bool *connected, - bool *dophase_done) -{ - /* This is POP3 and no proxy */ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct POP3 *pop3 = data->req.p.pop3; - - DEBUGF(infof(data, "DO phase starts")); - - if(data->set.opt_no_body) { - /* Requested no body means no transfer */ - pop3->transfer = PPTRANSFER_INFO; - } - - *dophase_done = FALSE; /* not done yet */ - - /* Start the first command in the DO phase */ - result = pop3_perform_command(data); - if(result) - return result; - - /* Run the state-machine */ - result = pop3_multi_statemach(data, dophase_done); - *connected = conn->bits.tcpconnect[FIRSTSOCKET]; - - if(*dophase_done) - DEBUGF(infof(data, "DO phase is complete")); - - return result; -} - -/*********************************************************************** - * - * pop3_do() - * - * This function is registered as 'curl_do' function. It decodes the path - * parts etc as a wrapper to the actual DO function (pop3_perform). - * - * The input argument is already checked for validity. - */ -static CURLcode pop3_do(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - *done = FALSE; /* default to false */ - - /* Parse the URL path */ - result = pop3_parse_url_path(data); - if(result) - return result; - - /* Parse the custom request */ - result = pop3_parse_custom_request(data); - if(result) - return result; - - result = pop3_regular_transfer(data, done); - - return result; -} - -/*********************************************************************** - * - * pop3_disconnect() - * - * Disconnect from an POP3 server. Cleanup protocol-specific per-connection - * resources. BLOCKING. - */ -static CURLcode pop3_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead_connection) -{ - struct pop3_conn *pop3c = &conn->proto.pop3c; - (void)data; - - /* We cannot send quit unconditionally. If this connection is stale or - bad in any way, sending quit and waiting around here will make the - disconnect wait in vain and cause more problems than we need to. */ - - if(!dead_connection && conn->bits.protoconnstart) { - if(!pop3_perform_quit(data, conn)) - (void)pop3_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */ - } - - /* Disconnect from the server */ - Curl_pp_disconnect(&pop3c->pp); - - /* Cleanup the SASL module */ - Curl_sasl_cleanup(conn, pop3c->sasl.authused); - - /* Cleanup our connection based variables */ - Curl_safefree(pop3c->apoptimestamp); - - return CURLE_OK; -} - -/* Call this when the DO phase has completed */ -static CURLcode pop3_dophase_done(struct Curl_easy *data, bool connected) -{ - (void)data; - (void)connected; - - return CURLE_OK; -} - -/* Called from multi.c while DOing */ -static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done) -{ - CURLcode result = pop3_multi_statemach(data, dophase_done); - - if(result) - DEBUGF(infof(data, "DO phase failed")); - else if(*dophase_done) { - result = pop3_dophase_done(data, FALSE /* not connected */); - - DEBUGF(infof(data, "DO phase is complete")); - } - - return result; -} - -/*********************************************************************** - * - * pop3_regular_transfer() - * - * The input argument is already checked for validity. - * - * Performs all commands done before a regular transfer between a local and a - * remote host. - */ -static CURLcode pop3_regular_transfer(struct Curl_easy *data, - bool *dophase_done) -{ - CURLcode result = CURLE_OK; - bool connected = FALSE; - - /* Make sure size is unknown at this point */ - data->req.size = -1; - - /* Set the progress data */ - Curl_pgrsSetUploadCounter(data, 0); - Curl_pgrsSetDownloadCounter(data, 0); - Curl_pgrsSetUploadSize(data, -1); - Curl_pgrsSetDownloadSize(data, -1); - - /* Carry out the perform */ - result = pop3_perform(data, &connected, dophase_done); - - /* Perform post DO phase operations if necessary */ - if(!result && *dophase_done) - result = pop3_dophase_done(data, connected); - - return result; -} - -static CURLcode pop3_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - /* Initialise the POP3 layer */ - CURLcode result = pop3_init(data); - if(result) - return result; - - /* Clear the TLS upgraded flag */ - conn->bits.tls_upgraded = FALSE; - - return CURLE_OK; -} - -/*********************************************************************** - * - * pop3_parse_url_options() - * - * Parse the URL login options. - */ -static CURLcode pop3_parse_url_options(struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - struct pop3_conn *pop3c = &conn->proto.pop3c; - const char *ptr = conn->options; - - while(!result && ptr && *ptr) { - const char *key = ptr; - const char *value; - - while(*ptr && *ptr != '=') - ptr++; - - value = ptr + 1; - - while(*ptr && *ptr != ';') - ptr++; - - if(strncasecompare(key, "AUTH=", 5)) { - result = Curl_sasl_parse_url_auth_option(&pop3c->sasl, - value, ptr - value); - - if(result && strncasecompare(value, "+APOP", ptr - value)) { - pop3c->preftype = POP3_TYPE_APOP; - pop3c->sasl.prefmech = SASL_AUTH_NONE; - result = CURLE_OK; - } - } - else - result = CURLE_URL_MALFORMAT; - - if(*ptr == ';') - ptr++; - } - - if(pop3c->preftype != POP3_TYPE_APOP) - switch(pop3c->sasl.prefmech) { - case SASL_AUTH_NONE: - pop3c->preftype = POP3_TYPE_NONE; - break; - case SASL_AUTH_DEFAULT: - pop3c->preftype = POP3_TYPE_ANY; - break; - default: - pop3c->preftype = POP3_TYPE_SASL; - break; - } - - return result; -} - -/*********************************************************************** - * - * pop3_parse_url_path() - * - * Parse the URL path into separate path components. - */ -static CURLcode pop3_parse_url_path(struct Curl_easy *data) -{ - /* The POP3 struct is already initialised in pop3_connect() */ - struct POP3 *pop3 = data->req.p.pop3; - const char *path = &data->state.up.path[1]; /* skip leading path */ - - /* URL decode the path for the message ID */ - return Curl_urldecode(path, 0, &pop3->id, NULL, REJECT_CTRL); -} - -/*********************************************************************** - * - * pop3_parse_custom_request() - * - * Parse the custom request. - */ -static CURLcode pop3_parse_custom_request(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct POP3 *pop3 = data->req.p.pop3; - const char *custom = data->set.str[STRING_CUSTOMREQUEST]; - - /* URL decode the custom request */ - if(custom) - result = Curl_urldecode(custom, 0, &pop3->custom, NULL, REJECT_CTRL); - - return result; -} - -/*********************************************************************** - * - * Curl_pop3_write() - * - * This function scans the body after the end-of-body and writes everything - * until the end is found. - */ -CURLcode Curl_pop3_write(struct Curl_easy *data, char *str, size_t nread) -{ - /* This code could be made into a special function in the handler struct */ - CURLcode result = CURLE_OK; - struct SingleRequest *k = &data->req; - struct connectdata *conn = data->conn; - struct pop3_conn *pop3c = &conn->proto.pop3c; - bool strip_dot = FALSE; - size_t last = 0; - size_t i; - - /* Search through the buffer looking for the end-of-body marker which is - 5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches - the eob so the server will have prefixed it with an extra dot which we - need to strip out. Additionally the marker could of course be spread out - over 5 different data chunks. */ - for(i = 0; i < nread; i++) { - size_t prev = pop3c->eob; - - switch(str[i]) { - case 0x0d: - if(pop3c->eob == 0) { - pop3c->eob++; - - if(i) { - /* Write out the body part that didn't match */ - result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last], - i - last); - - if(result) - return result; - - last = i; - } - } - else if(pop3c->eob == 3) - pop3c->eob++; - else - /* If the character match wasn't at position 0 or 3 then restart the - pattern matching */ - pop3c->eob = 1; - break; - - case 0x0a: - if(pop3c->eob == 1 || pop3c->eob == 4) - pop3c->eob++; - else - /* If the character match wasn't at position 1 or 4 then start the - search again */ - pop3c->eob = 0; - break; - - case 0x2e: - if(pop3c->eob == 2) - pop3c->eob++; - else if(pop3c->eob == 3) { - /* We have an extra dot after the CRLF which we need to strip off */ - strip_dot = TRUE; - pop3c->eob = 0; - } - else - /* If the character match wasn't at position 2 then start the search - again */ - pop3c->eob = 0; - break; - - default: - pop3c->eob = 0; - break; - } - - /* Did we have a partial match which has subsequently failed? */ - if(prev && prev >= pop3c->eob) { - /* Strip can only be non-zero for the very first mismatch after CRLF - and then both prev and strip are equal and nothing will be output - below */ - while(prev && pop3c->strip) { - prev--; - pop3c->strip--; - } - - if(prev) { - /* If the partial match was the CRLF and dot then only write the CRLF - as the server would have inserted the dot */ - if(strip_dot && prev - 1 > 0) { - result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB, - prev - 1); - } - else if(!strip_dot) { - result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB, - prev); - } - else { - result = CURLE_OK; - } - - if(result) - return result; - - last = i; - strip_dot = FALSE; - } - } - } - - if(pop3c->eob == POP3_EOB_LEN) { - /* We have a full match so the transfer is done, however we must transfer - the CRLF at the start of the EOB as this is considered to be part of the - message as per RFC-1939, sect. 3 */ - result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB, 2); - - k->keepon &= ~KEEP_RECV; - pop3c->eob = 0; - - return result; - } - - if(pop3c->eob) - /* While EOB is matching nothing should be output */ - return CURLE_OK; - - if(nread - last) { - result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last], - nread - last); - } - - return result; -} - -#endif /* CURL_DISABLE_POP3 */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ * RFC1734 POP3 Authentication
+ * RFC1939 POP3 protocol
+ * RFC2195 CRAM-MD5 authentication
+ * RFC2384 POP URL Scheme
+ * RFC2449 POP3 Extension Mechanism
+ * RFC2595 Using TLS with IMAP, POP3 and ACAP
+ * RFC2831 DIGEST-MD5 authentication
+ * RFC4422 Simple Authentication and Security Layer (SASL)
+ * RFC4616 PLAIN authentication
+ * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
+ * RFC5034 POP3 SASL Authentication Mechanism
+ * RFC6749 OAuth 2.0 Authorization Framework
+ * RFC8314 Use of TLS for Email Submission and Access
+ * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_POP3
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#include <curl/curl.h>
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "progress.h"
+#include "transfer.h"
+#include "escape.h"
+#include "http.h" /* for HTTP proxy tunnel stuff */
+#include "socks.h"
+#include "pop3.h"
+#include "strtoofft.h"
+#include "strcase.h"
+#include "vtls/vtls.h"
+#include "cfilters.h"
+#include "connect.h"
+#include "select.h"
+#include "multiif.h"
+#include "url.h"
+#include "bufref.h"
+#include "curl_sasl.h"
+#include "curl_md5.h"
+#include "warnless.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* Local API functions */
+static CURLcode pop3_regular_transfer(struct Curl_easy *data, bool *done);
+static CURLcode pop3_do(struct Curl_easy *data, bool *done);
+static CURLcode pop3_done(struct Curl_easy *data, CURLcode status,
+ bool premature);
+static CURLcode pop3_connect(struct Curl_easy *data, bool *done);
+static CURLcode pop3_disconnect(struct Curl_easy *data,
+ struct connectdata *conn, bool dead);
+static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done);
+static int pop3_getsock(struct Curl_easy *data,
+ struct connectdata *conn, curl_socket_t *socks);
+static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done);
+static CURLcode pop3_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn);
+static CURLcode pop3_parse_url_options(struct connectdata *conn);
+static CURLcode pop3_parse_url_path(struct Curl_easy *data);
+static CURLcode pop3_parse_custom_request(struct Curl_easy *data);
+static CURLcode pop3_perform_auth(struct Curl_easy *data, const char *mech,
+ const struct bufref *initresp);
+static CURLcode pop3_continue_auth(struct Curl_easy *data, const char *mech,
+ const struct bufref *resp);
+static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech);
+static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out);
+
+/*
+ * POP3 protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_pop3 = {
+ "POP3", /* scheme */
+ pop3_setup_connection, /* setup_connection */
+ pop3_do, /* do_it */
+ pop3_done, /* done */
+ ZERO_NULL, /* do_more */
+ pop3_connect, /* connect_it */
+ pop3_multi_statemach, /* connecting */
+ pop3_doing, /* doing */
+ pop3_getsock, /* proto_getsock */
+ pop3_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ pop3_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_POP3, /* defport */
+ CURLPROTO_POP3, /* protocol */
+ CURLPROTO_POP3, /* family */
+ PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
+ PROTOPT_URLOPTIONS
+};
+
+#ifdef USE_SSL
+/*
+ * POP3S protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_pop3s = {
+ "POP3S", /* scheme */
+ pop3_setup_connection, /* setup_connection */
+ pop3_do, /* do_it */
+ pop3_done, /* done */
+ ZERO_NULL, /* do_more */
+ pop3_connect, /* connect_it */
+ pop3_multi_statemach, /* connecting */
+ pop3_doing, /* doing */
+ pop3_getsock, /* proto_getsock */
+ pop3_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ pop3_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_POP3S, /* defport */
+ CURLPROTO_POP3S, /* protocol */
+ CURLPROTO_POP3, /* family */
+ PROTOPT_CLOSEACTION | PROTOPT_SSL
+ | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
+};
+#endif
+
+/* SASL parameters for the pop3 protocol */
+static const struct SASLproto saslpop3 = {
+ "pop", /* The service name */
+ pop3_perform_auth, /* Send authentication command */
+ pop3_continue_auth, /* Send authentication continuation */
+ pop3_cancel_auth, /* Send authentication cancellation */
+ pop3_get_message, /* Get SASL response message */
+ 255 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */
+ '*', /* Code received when continuation is expected */
+ '+', /* Code to receive upon authentication success */
+ SASL_AUTH_DEFAULT, /* Default mechanisms */
+ SASL_FLAG_BASE64 /* Configuration flags */
+};
+
+#ifdef USE_SSL
+static void pop3_to_pop3s(struct connectdata *conn)
+{
+ /* Change the connection handler */
+ conn->handler = &Curl_handler_pop3s;
+
+ /* Set the connection's upgraded to TLS flag */
+ conn->bits.tls_upgraded = TRUE;
+}
+#else
+#define pop3_to_pop3s(x) Curl_nop_stmt
+#endif
+
+/***********************************************************************
+ *
+ * pop3_endofresp()
+ *
+ * Checks for an ending POP3 status code at the start of the given string, but
+ * also detects the APOP timestamp from the server greeting and various
+ * capabilities from the CAPA response including the supported authentication
+ * types and allowed SASL mechanisms.
+ */
+static bool pop3_endofresp(struct Curl_easy *data, struct connectdata *conn,
+ char *line, size_t len, int *resp)
+{
+ struct pop3_conn *pop3c = &conn->proto.pop3c;
+ (void)data;
+
+ /* Do we have an error response? */
+ if(len >= 4 && !memcmp("-ERR", line, 4)) {
+ *resp = '-';
+
+ return TRUE;
+ }
+
+ /* Are we processing CAPA command responses? */
+ if(pop3c->state == POP3_CAPA) {
+ /* Do we have the terminating line? */
+ if(len >= 1 && line[0] == '.')
+ /* Treat the response as a success */
+ *resp = '+';
+ else
+ /* Treat the response as an untagged continuation */
+ *resp = '*';
+
+ return TRUE;
+ }
+
+ /* Do we have a success response? */
+ if(len >= 3 && !memcmp("+OK", line, 3)) {
+ *resp = '+';
+
+ return TRUE;
+ }
+
+ /* Do we have a continuation response? */
+ if(len >= 1 && line[0] == '+') {
+ *resp = '*';
+
+ return TRUE;
+ }
+
+ return FALSE; /* Nothing for us */
+}
+
+/***********************************************************************
+ *
+ * pop3_get_message()
+ *
+ * Gets the authentication message from the response buffer.
+ */
+static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out)
+{
+ char *message = data->state.buffer;
+ size_t len = strlen(message);
+
+ if(len > 2) {
+ /* Find the start of the message */
+ len -= 2;
+ for(message += 2; *message == ' ' || *message == '\t'; message++, len--)
+ ;
+
+ /* Find the end of the message */
+ while(len--)
+ if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
+ message[len] != '\t')
+ break;
+
+ /* Terminate the message */
+ message[++len] = '\0';
+ Curl_bufref_set(out, message, len, NULL);
+ }
+ else
+ /* junk input => zero length output */
+ Curl_bufref_set(out, "", 0, NULL);
+
+ return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * state()
+ *
+ * This is the ONLY way to change POP3 state!
+ */
+static void state(struct Curl_easy *data, pop3state newstate)
+{
+ struct pop3_conn *pop3c = &data->conn->proto.pop3c;
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ /* for debug purposes */
+ static const char * const names[] = {
+ "STOP",
+ "SERVERGREET",
+ "CAPA",
+ "STARTTLS",
+ "UPGRADETLS",
+ "AUTH",
+ "APOP",
+ "USER",
+ "PASS",
+ "COMMAND",
+ "QUIT",
+ /* LAST */
+ };
+
+ if(pop3c->state != newstate)
+ infof(data, "POP3 %p state change from %s to %s",
+ (void *)pop3c, names[pop3c->state], names[newstate]);
+#endif
+
+ pop3c->state = newstate;
+}
+
+/***********************************************************************
+ *
+ * pop3_perform_capa()
+ *
+ * Sends the CAPA command in order to obtain a list of server side supported
+ * capabilities.
+ */
+static CURLcode pop3_perform_capa(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct pop3_conn *pop3c = &conn->proto.pop3c;
+
+ pop3c->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
+ pop3c->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */
+ pop3c->tls_supported = FALSE; /* Clear the TLS capability */
+
+ /* Send the CAPA command */
+ result = Curl_pp_sendf(data, &pop3c->pp, "%s", "CAPA");
+
+ if(!result)
+ state(data, POP3_CAPA);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * pop3_perform_starttls()
+ *
+ * Sends the STLS command to start the upgrade to TLS.
+ */
+static CURLcode pop3_perform_starttls(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ /* Send the STLS command */
+ CURLcode result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", "STLS");
+
+ if(!result)
+ state(data, POP3_STARTTLS);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * pop3_perform_upgrade_tls()
+ *
+ * Performs the upgrade to TLS.
+ */
+static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ /* Start the SSL connection */
+ struct pop3_conn *pop3c = &conn->proto.pop3c;
+ CURLcode result;
+
+ if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) {
+ result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
+ if(result)
+ goto out;
+ }
+
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &pop3c->ssldone);
+
+ if(!result) {
+ if(pop3c->state != POP3_UPGRADETLS)
+ state(data, POP3_UPGRADETLS);
+
+ if(pop3c->ssldone) {
+ pop3_to_pop3s(conn);
+ result = pop3_perform_capa(data, conn);
+ }
+ }
+out:
+ return result;
+}
+
+/***********************************************************************
+ *
+ * pop3_perform_user()
+ *
+ * Sends a clear text USER command to authenticate with.
+ */
+static CURLcode pop3_perform_user(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+
+ /* Check we have a username and password to authenticate with and end the
+ connect phase if we don't */
+ if(!data->state.aptr.user) {
+ state(data, POP3_STOP);
+
+ return result;
+ }
+
+ /* Send the USER command */
+ result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "USER %s",
+ conn->user ? conn->user : "");
+ if(!result)
+ state(data, POP3_USER);
+
+ return result;
+}
+
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+/***********************************************************************
+ *
+ * pop3_perform_apop()
+ *
+ * Sends an APOP command to authenticate with.
+ */
+static CURLcode pop3_perform_apop(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct pop3_conn *pop3c = &conn->proto.pop3c;
+ size_t i;
+ struct MD5_context *ctxt;
+ unsigned char digest[MD5_DIGEST_LEN];
+ char secret[2 * MD5_DIGEST_LEN + 1];
+
+ /* Check we have a username and password to authenticate with and end the
+ connect phase if we don't */
+ if(!data->state.aptr.user) {
+ state(data, POP3_STOP);
+
+ return result;
+ }
+
+ /* Create the digest */
+ ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
+ if(!ctxt)
+ return CURLE_OUT_OF_MEMORY;
+
+ Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp,
+ curlx_uztoui(strlen(pop3c->apoptimestamp)));
+
+ Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd,
+ curlx_uztoui(strlen(conn->passwd)));
+
+ /* Finalise the digest */
+ Curl_MD5_final(ctxt, digest);
+
+ /* Convert the calculated 16 octet digest into a 32 byte hex string */
+ for(i = 0; i < MD5_DIGEST_LEN; i++)
+ msnprintf(&secret[2 * i], 3, "%02x", digest[i]);
+
+ result = Curl_pp_sendf(data, &pop3c->pp, "APOP %s %s", conn->user, secret);
+
+ if(!result)
+ state(data, POP3_APOP);
+
+ return result;
+}
+#endif
+
+/***********************************************************************
+ *
+ * pop3_perform_auth()
+ *
+ * Sends an AUTH command allowing the client to login with the given SASL
+ * authentication mechanism.
+ */
+static CURLcode pop3_perform_auth(struct Curl_easy *data,
+ const char *mech,
+ const struct bufref *initresp)
+{
+ CURLcode result = CURLE_OK;
+ struct pop3_conn *pop3c = &data->conn->proto.pop3c;
+ const char *ir = (const char *) Curl_bufref_ptr(initresp);
+
+ if(ir) { /* AUTH <mech> ...<crlf> */
+ /* Send the AUTH command with the initial response */
+ result = Curl_pp_sendf(data, &pop3c->pp, "AUTH %s %s", mech, ir);
+ }
+ else {
+ /* Send the AUTH command */
+ result = Curl_pp_sendf(data, &pop3c->pp, "AUTH %s", mech);
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * pop3_continue_auth()
+ *
+ * Sends SASL continuation data.
+ */
+static CURLcode pop3_continue_auth(struct Curl_easy *data,
+ const char *mech,
+ const struct bufref *resp)
+{
+ struct pop3_conn *pop3c = &data->conn->proto.pop3c;
+
+ (void)mech;
+
+ return Curl_pp_sendf(data, &pop3c->pp,
+ "%s", (const char *) Curl_bufref_ptr(resp));
+}
+
+/***********************************************************************
+ *
+ * pop3_cancel_auth()
+ *
+ * Sends SASL cancellation.
+ */
+static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech)
+{
+ struct pop3_conn *pop3c = &data->conn->proto.pop3c;
+
+ (void)mech;
+
+ return Curl_pp_sendf(data, &pop3c->pp, "*");
+}
+
+/***********************************************************************
+ *
+ * pop3_perform_authentication()
+ *
+ * Initiates the authentication sequence, with the appropriate SASL
+ * authentication mechanism, falling back to APOP and clear text should a
+ * common mechanism not be available between the client and server.
+ */
+static CURLcode pop3_perform_authentication(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct pop3_conn *pop3c = &conn->proto.pop3c;
+ saslprogress progress = SASL_IDLE;
+
+ /* Check we have enough data to authenticate with and end the
+ connect phase if we don't */
+ if(!Curl_sasl_can_authenticate(&pop3c->sasl, data)) {
+ state(data, POP3_STOP);
+ return result;
+ }
+
+ if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_SASL) {
+ /* Calculate the SASL login details */
+ result = Curl_sasl_start(&pop3c->sasl, data, FALSE, &progress);
+
+ if(!result)
+ if(progress == SASL_INPROGRESS)
+ state(data, POP3_AUTH);
+ }
+
+ if(!result && progress == SASL_IDLE) {
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+ if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
+ /* Perform APOP authentication */
+ result = pop3_perform_apop(data, conn);
+ else
+#endif
+ if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
+ /* Perform clear text authentication */
+ result = pop3_perform_user(data, conn);
+ else {
+ /* Other mechanisms not supported */
+ infof(data, "No known authentication mechanisms supported");
+ result = CURLE_LOGIN_DENIED;
+ }
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * pop3_perform_command()
+ *
+ * Sends a POP3 based command.
+ */
+static CURLcode pop3_perform_command(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct POP3 *pop3 = data->req.p.pop3;
+ const char *command = NULL;
+
+ /* Calculate the default command */
+ if(pop3->id[0] == '\0' || data->set.list_only) {
+ command = "LIST";
+
+ if(pop3->id[0] != '\0')
+ /* Message specific LIST so skip the BODY transfer */
+ pop3->transfer = PPTRANSFER_INFO;
+ }
+ else
+ command = "RETR";
+
+ /* Send the command */
+ if(pop3->id[0] != '\0')
+ result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s %s",
+ (pop3->custom && pop3->custom[0] != '\0' ?
+ pop3->custom : command), pop3->id);
+ else
+ result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s",
+ (pop3->custom && pop3->custom[0] != '\0' ?
+ pop3->custom : command));
+
+ if(!result)
+ state(data, POP3_COMMAND);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * pop3_perform_quit()
+ *
+ * Performs the quit action prior to sclose() be called.
+ */
+static CURLcode pop3_perform_quit(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ /* Send the QUIT command */
+ CURLcode result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", "QUIT");
+
+ if(!result)
+ state(data, POP3_QUIT);
+
+ return result;
+}
+
+/* For the initial server greeting */
+static CURLcode pop3_state_servergreet_resp(struct Curl_easy *data,
+ int pop3code,
+ pop3state instate)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct pop3_conn *pop3c = &conn->proto.pop3c;
+ const char *line = data->state.buffer;
+ size_t len = strlen(line);
+
+ (void)instate; /* no use for this yet */
+
+ if(pop3code != '+') {
+ failf(data, "Got unexpected pop3-server response");
+ result = CURLE_WEIRD_SERVER_REPLY;
+ }
+ else {
+ /* Does the server support APOP authentication? */
+ if(len >= 4 && line[len - 2] == '>') {
+ /* Look for the APOP timestamp */
+ size_t i;
+ for(i = 3; i < len - 2; ++i) {
+ if(line[i] == '<') {
+ /* Calculate the length of the timestamp */
+ size_t timestamplen = len - 1 - i;
+ char *at;
+ if(!timestamplen)
+ break;
+
+ /* Allocate some memory for the timestamp */
+ pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1);
+
+ if(!pop3c->apoptimestamp)
+ break;
+
+ /* Copy the timestamp */
+ memcpy(pop3c->apoptimestamp, line + i, timestamplen);
+ pop3c->apoptimestamp[timestamplen] = '\0';
+
+ /* If the timestamp does not contain '@' it is not (as required by
+ RFC-1939) conformant to the RFC-822 message id syntax, and we
+ therefore do not use APOP authentication. */
+ at = strchr(pop3c->apoptimestamp, '@');
+ if(!at)
+ Curl_safefree(pop3c->apoptimestamp);
+ else
+ /* Store the APOP capability */
+ pop3c->authtypes |= POP3_TYPE_APOP;
+ break;
+ }
+ }
+ }
+
+ result = pop3_perform_capa(data, conn);
+ }
+
+ return result;
+}
+
+/* For CAPA responses */
+static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code,
+ pop3state instate)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct pop3_conn *pop3c = &conn->proto.pop3c;
+ const char *line = data->state.buffer;
+ size_t len = strlen(line);
+
+ (void)instate; /* no use for this yet */
+
+ /* Do we have a untagged continuation response? */
+ if(pop3code == '*') {
+ /* Does the server support the STLS capability? */
+ if(len >= 4 && !memcmp(line, "STLS", 4))
+ pop3c->tls_supported = TRUE;
+
+ /* Does the server support clear text authentication? */
+ else if(len >= 4 && !memcmp(line, "USER", 4))
+ pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
+
+ /* Does the server support SASL based authentication? */
+ else if(len >= 5 && !memcmp(line, "SASL ", 5)) {
+ pop3c->authtypes |= POP3_TYPE_SASL;
+
+ /* Advance past the SASL keyword */
+ line += 5;
+ len -= 5;
+
+ /* Loop through the data line */
+ for(;;) {
+ size_t llen;
+ size_t wordlen;
+ unsigned short mechbit;
+
+ while(len &&
+ (*line == ' ' || *line == '\t' ||
+ *line == '\r' || *line == '\n')) {
+
+ line++;
+ len--;
+ }
+
+ if(!len)
+ break;
+
+ /* Extract the word */
+ for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
+ line[wordlen] != '\t' && line[wordlen] != '\r' &&
+ line[wordlen] != '\n';)
+ wordlen++;
+
+ /* Test the word for a matching authentication mechanism */
+ mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
+ if(mechbit && llen == wordlen)
+ pop3c->sasl.authmechs |= mechbit;
+
+ line += wordlen;
+ len -= wordlen;
+ }
+ }
+ }
+ else {
+ /* Clear text is supported when CAPA isn't recognised */
+ if(pop3code != '+')
+ pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
+
+ if(!data->set.use_ssl || Curl_conn_is_ssl(data, FIRSTSOCKET))
+ result = pop3_perform_authentication(data, conn);
+ else if(pop3code == '+' && pop3c->tls_supported)
+ /* Switch to TLS connection now */
+ result = pop3_perform_starttls(data, conn);
+ else if(data->set.use_ssl <= CURLUSESSL_TRY)
+ /* Fallback and carry on with authentication */
+ result = pop3_perform_authentication(data, conn);
+ else {
+ failf(data, "STLS not supported.");
+ result = CURLE_USE_SSL_FAILED;
+ }
+ }
+
+ return result;
+}
+
+/* For STARTTLS responses */
+static CURLcode pop3_state_starttls_resp(struct Curl_easy *data,
+ struct connectdata *conn,
+ int pop3code,
+ pop3state instate)
+{
+ CURLcode result = CURLE_OK;
+ (void)instate; /* no use for this yet */
+
+ /* Pipelining in response is forbidden. */
+ if(data->conn->proto.pop3c.pp.cache_size)
+ return CURLE_WEIRD_SERVER_REPLY;
+
+ if(pop3code != '+') {
+ if(data->set.use_ssl != CURLUSESSL_TRY) {
+ failf(data, "STARTTLS denied");
+ result = CURLE_USE_SSL_FAILED;
+ }
+ else
+ result = pop3_perform_authentication(data, conn);
+ }
+ else
+ result = pop3_perform_upgrade_tls(data, conn);
+
+ return result;
+}
+
+/* For SASL authentication responses */
+static CURLcode pop3_state_auth_resp(struct Curl_easy *data,
+ int pop3code,
+ pop3state instate)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct pop3_conn *pop3c = &conn->proto.pop3c;
+ saslprogress progress;
+
+ (void)instate; /* no use for this yet */
+
+ result = Curl_sasl_continue(&pop3c->sasl, data, pop3code, &progress);
+ if(!result)
+ switch(progress) {
+ case SASL_DONE:
+ state(data, POP3_STOP); /* Authenticated */
+ break;
+ case SASL_IDLE: /* No mechanism left after cancellation */
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+ if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
+ /* Perform APOP authentication */
+ result = pop3_perform_apop(data, conn);
+ else
+#endif
+ if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
+ /* Perform clear text authentication */
+ result = pop3_perform_user(data, conn);
+ else {
+ failf(data, "Authentication cancelled");
+ result = CURLE_LOGIN_DENIED;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+/* For APOP responses */
+static CURLcode pop3_state_apop_resp(struct Curl_easy *data, int pop3code,
+ pop3state instate)
+{
+ CURLcode result = CURLE_OK;
+ (void)instate; /* no use for this yet */
+
+ if(pop3code != '+') {
+ failf(data, "Authentication failed: %d", pop3code);
+ result = CURLE_LOGIN_DENIED;
+ }
+ else
+ /* End of connect phase */
+ state(data, POP3_STOP);
+
+ return result;
+}
+#endif
+
+/* For USER responses */
+static CURLcode pop3_state_user_resp(struct Curl_easy *data, int pop3code,
+ pop3state instate)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ (void)instate; /* no use for this yet */
+
+ if(pop3code != '+') {
+ failf(data, "Access denied. %c", pop3code);
+ result = CURLE_LOGIN_DENIED;
+ }
+ else
+ /* Send the PASS command */
+ result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "PASS %s",
+ conn->passwd ? conn->passwd : "");
+ if(!result)
+ state(data, POP3_PASS);
+
+ return result;
+}
+
+/* For PASS responses */
+static CURLcode pop3_state_pass_resp(struct Curl_easy *data, int pop3code,
+ pop3state instate)
+{
+ CURLcode result = CURLE_OK;
+ (void)instate; /* no use for this yet */
+
+ if(pop3code != '+') {
+ failf(data, "Access denied. %c", pop3code);
+ result = CURLE_LOGIN_DENIED;
+ }
+ else
+ /* End of connect phase */
+ state(data, POP3_STOP);
+
+ return result;
+}
+
+/* For command responses */
+static CURLcode pop3_state_command_resp(struct Curl_easy *data,
+ int pop3code,
+ pop3state instate)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct POP3 *pop3 = data->req.p.pop3;
+ struct pop3_conn *pop3c = &conn->proto.pop3c;
+ struct pingpong *pp = &pop3c->pp;
+
+ (void)instate; /* no use for this yet */
+
+ if(pop3code != '+') {
+ state(data, POP3_STOP);
+ return CURLE_WEIRD_SERVER_REPLY;
+ }
+
+ /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
+ EOB string so count this is two matching bytes. This is necessary to make
+ the code detect the EOB if the only data than comes now is %2e CR LF like
+ when there is no body to return. */
+ pop3c->eob = 2;
+
+ /* But since this initial CR LF pair is not part of the actual body, we set
+ the strip counter here so that these bytes won't be delivered. */
+ pop3c->strip = 2;
+
+ if(pop3->transfer == PPTRANSFER_BODY) {
+ /* POP3 download */
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
+
+ if(pp->cache) {
+ /* The header "cache" contains a bunch of data that is actually body
+ content so send it as such. Note that there may even be additional
+ "headers" after the body */
+
+ if(!data->req.no_body) {
+ result = Curl_pop3_write(data, pp->cache, pp->cache_size);
+ if(result)
+ return result;
+ }
+
+ /* Free the cache */
+ Curl_safefree(pp->cache);
+
+ /* Reset the cache size */
+ pp->cache_size = 0;
+ }
+ }
+
+ /* End of DO phase */
+ state(data, POP3_STOP);
+
+ return result;
+}
+
+static CURLcode pop3_statemachine(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ int pop3code;
+ struct pop3_conn *pop3c = &conn->proto.pop3c;
+ struct pingpong *pp = &pop3c->pp;
+ size_t nread = 0;
+ (void)data;
+
+ /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
+ if(pop3c->state == POP3_UPGRADETLS)
+ return pop3_perform_upgrade_tls(data, conn);
+
+ /* Flush any data that needs to be sent */
+ if(pp->sendleft)
+ return Curl_pp_flushsend(data, pp);
+
+ do {
+ /* Read the response from the server */
+ result = Curl_pp_readresp(data, sock, pp, &pop3code, &nread);
+ if(result)
+ return result;
+
+ if(!pop3code)
+ break;
+
+ /* We have now received a full POP3 server response */
+ switch(pop3c->state) {
+ case POP3_SERVERGREET:
+ result = pop3_state_servergreet_resp(data, pop3code, pop3c->state);
+ break;
+
+ case POP3_CAPA:
+ result = pop3_state_capa_resp(data, pop3code, pop3c->state);
+ break;
+
+ case POP3_STARTTLS:
+ result = pop3_state_starttls_resp(data, conn, pop3code, pop3c->state);
+ break;
+
+ case POP3_AUTH:
+ result = pop3_state_auth_resp(data, pop3code, pop3c->state);
+ break;
+
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+ case POP3_APOP:
+ result = pop3_state_apop_resp(data, pop3code, pop3c->state);
+ break;
+#endif
+
+ case POP3_USER:
+ result = pop3_state_user_resp(data, pop3code, pop3c->state);
+ break;
+
+ case POP3_PASS:
+ result = pop3_state_pass_resp(data, pop3code, pop3c->state);
+ break;
+
+ case POP3_COMMAND:
+ result = pop3_state_command_resp(data, pop3code, pop3c->state);
+ break;
+
+ case POP3_QUIT:
+ state(data, POP3_STOP);
+ break;
+
+ default:
+ /* internal error */
+ state(data, POP3_STOP);
+ break;
+ }
+ } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp));
+
+ return result;
+}
+
+/* Called repeatedly until done from multi.c */
+static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct pop3_conn *pop3c = &conn->proto.pop3c;
+
+ if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) {
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &pop3c->ssldone);
+ if(result || !pop3c->ssldone)
+ return result;
+ }
+
+ result = Curl_pp_statemach(data, &pop3c->pp, FALSE, FALSE);
+ *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
+
+ return result;
+}
+
+static CURLcode pop3_block_statemach(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool disconnecting)
+{
+ CURLcode result = CURLE_OK;
+ struct pop3_conn *pop3c = &conn->proto.pop3c;
+
+ while(pop3c->state != POP3_STOP && !result)
+ result = Curl_pp_statemach(data, &pop3c->pp, TRUE, disconnecting);
+
+ return result;
+}
+
+/* Allocate and initialize the POP3 struct for the current Curl_easy if
+ required */
+static CURLcode pop3_init(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct POP3 *pop3;
+
+ pop3 = data->req.p.pop3 = calloc(sizeof(struct POP3), 1);
+ if(!pop3)
+ result = CURLE_OUT_OF_MEMORY;
+
+ return result;
+}
+
+/* For the POP3 "protocol connect" and "doing" phases only */
+static int pop3_getsock(struct Curl_easy *data,
+ struct connectdata *conn, curl_socket_t *socks)
+{
+ return Curl_pp_getsock(data, &conn->proto.pop3c.pp, socks);
+}
+
+/***********************************************************************
+ *
+ * pop3_connect()
+ *
+ * This function should do everything that is to be considered a part of the
+ * connection phase.
+ *
+ * The variable 'done' points to will be TRUE if the protocol-layer connect
+ * phase is done when this function returns, or FALSE if not.
+ */
+static CURLcode pop3_connect(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct pop3_conn *pop3c = &conn->proto.pop3c;
+ struct pingpong *pp = &pop3c->pp;
+
+ *done = FALSE; /* default to not done yet */
+
+ /* We always support persistent connections in POP3 */
+ connkeep(conn, "POP3 default");
+
+ PINGPONG_SETUP(pp, pop3_statemachine, pop3_endofresp);
+
+ /* Set the default preferred authentication type and mechanism */
+ pop3c->preftype = POP3_TYPE_ANY;
+ Curl_sasl_init(&pop3c->sasl, data, &saslpop3);
+
+ /* Initialise the pingpong layer */
+ Curl_pp_setup(pp);
+ Curl_pp_init(data, pp);
+
+ /* Parse the URL options */
+ result = pop3_parse_url_options(conn);
+ if(result)
+ return result;
+
+ /* Start off waiting for the server greeting response */
+ state(data, POP3_SERVERGREET);
+
+ result = pop3_multi_statemach(data, done);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * pop3_done()
+ *
+ * The DONE function. This does what needs to be done after a single DO has
+ * performed.
+ *
+ * Input argument is already checked for validity.
+ */
+static CURLcode pop3_done(struct Curl_easy *data, CURLcode status,
+ bool premature)
+{
+ CURLcode result = CURLE_OK;
+ struct POP3 *pop3 = data->req.p.pop3;
+
+ (void)premature;
+
+ if(!pop3)
+ return CURLE_OK;
+
+ if(status) {
+ connclose(data->conn, "POP3 done with bad status");
+ result = status; /* use the already set error code */
+ }
+
+ /* Cleanup our per-request based variables */
+ Curl_safefree(pop3->id);
+ Curl_safefree(pop3->custom);
+
+ /* Clear the transfer mode for the next request */
+ pop3->transfer = PPTRANSFER_BODY;
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * pop3_perform()
+ *
+ * This is the actual DO function for POP3. Get a message/listing according to
+ * the options previously setup.
+ */
+static CURLcode pop3_perform(struct Curl_easy *data, bool *connected,
+ bool *dophase_done)
+{
+ /* This is POP3 and no proxy */
+ CURLcode result = CURLE_OK;
+ struct POP3 *pop3 = data->req.p.pop3;
+
+ DEBUGF(infof(data, "DO phase starts"));
+
+ if(data->req.no_body) {
+ /* Requested no body means no transfer */
+ pop3->transfer = PPTRANSFER_INFO;
+ }
+
+ *dophase_done = FALSE; /* not done yet */
+
+ /* Start the first command in the DO phase */
+ result = pop3_perform_command(data);
+ if(result)
+ return result;
+
+ /* Run the state-machine */
+ result = pop3_multi_statemach(data, dophase_done);
+ *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
+
+ if(*dophase_done)
+ DEBUGF(infof(data, "DO phase is complete"));
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * pop3_do()
+ *
+ * This function is registered as 'curl_do' function. It decodes the path
+ * parts etc as a wrapper to the actual DO function (pop3_perform).
+ *
+ * The input argument is already checked for validity.
+ */
+static CURLcode pop3_do(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ *done = FALSE; /* default to false */
+
+ /* Parse the URL path */
+ result = pop3_parse_url_path(data);
+ if(result)
+ return result;
+
+ /* Parse the custom request */
+ result = pop3_parse_custom_request(data);
+ if(result)
+ return result;
+
+ result = pop3_regular_transfer(data, done);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * pop3_disconnect()
+ *
+ * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
+ * resources. BLOCKING.
+ */
+static CURLcode pop3_disconnect(struct Curl_easy *data,
+ struct connectdata *conn, bool dead_connection)
+{
+ struct pop3_conn *pop3c = &conn->proto.pop3c;
+ (void)data;
+
+ /* We cannot send quit unconditionally. If this connection is stale or
+ bad in any way, sending quit and waiting around here will make the
+ disconnect wait in vain and cause more problems than we need to. */
+
+ if(!dead_connection && conn->bits.protoconnstart) {
+ if(!pop3_perform_quit(data, conn))
+ (void)pop3_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */
+ }
+
+ /* Disconnect from the server */
+ Curl_pp_disconnect(&pop3c->pp);
+
+ /* Cleanup the SASL module */
+ Curl_sasl_cleanup(conn, pop3c->sasl.authused);
+
+ /* Cleanup our connection based variables */
+ Curl_safefree(pop3c->apoptimestamp);
+
+ return CURLE_OK;
+}
+
+/* Call this when the DO phase has completed */
+static CURLcode pop3_dophase_done(struct Curl_easy *data, bool connected)
+{
+ (void)data;
+ (void)connected;
+
+ return CURLE_OK;
+}
+
+/* Called from multi.c while DOing */
+static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done)
+{
+ CURLcode result = pop3_multi_statemach(data, dophase_done);
+
+ if(result)
+ DEBUGF(infof(data, "DO phase failed"));
+ else if(*dophase_done) {
+ result = pop3_dophase_done(data, FALSE /* not connected */);
+
+ DEBUGF(infof(data, "DO phase is complete"));
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * pop3_regular_transfer()
+ *
+ * The input argument is already checked for validity.
+ *
+ * Performs all commands done before a regular transfer between a local and a
+ * remote host.
+ */
+static CURLcode pop3_regular_transfer(struct Curl_easy *data,
+ bool *dophase_done)
+{
+ CURLcode result = CURLE_OK;
+ bool connected = FALSE;
+
+ /* Make sure size is unknown at this point */
+ data->req.size = -1;
+
+ /* Set the progress data */
+ Curl_pgrsSetUploadCounter(data, 0);
+ Curl_pgrsSetDownloadCounter(data, 0);
+ Curl_pgrsSetUploadSize(data, -1);
+ Curl_pgrsSetDownloadSize(data, -1);
+
+ /* Carry out the perform */
+ result = pop3_perform(data, &connected, dophase_done);
+
+ /* Perform post DO phase operations if necessary */
+ if(!result && *dophase_done)
+ result = pop3_dophase_done(data, connected);
+
+ return result;
+}
+
+static CURLcode pop3_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ /* Initialise the POP3 layer */
+ CURLcode result = pop3_init(data);
+ if(result)
+ return result;
+
+ /* Clear the TLS upgraded flag */
+ conn->bits.tls_upgraded = FALSE;
+
+ return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * pop3_parse_url_options()
+ *
+ * Parse the URL login options.
+ */
+static CURLcode pop3_parse_url_options(struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct pop3_conn *pop3c = &conn->proto.pop3c;
+ const char *ptr = conn->options;
+
+ while(!result && ptr && *ptr) {
+ const char *key = ptr;
+ const char *value;
+
+ while(*ptr && *ptr != '=')
+ ptr++;
+
+ value = ptr + 1;
+
+ while(*ptr && *ptr != ';')
+ ptr++;
+
+ if(strncasecompare(key, "AUTH=", 5)) {
+ result = Curl_sasl_parse_url_auth_option(&pop3c->sasl,
+ value, ptr - value);
+
+ if(result && strncasecompare(value, "+APOP", ptr - value)) {
+ pop3c->preftype = POP3_TYPE_APOP;
+ pop3c->sasl.prefmech = SASL_AUTH_NONE;
+ result = CURLE_OK;
+ }
+ }
+ else
+ result = CURLE_URL_MALFORMAT;
+
+ if(*ptr == ';')
+ ptr++;
+ }
+
+ if(pop3c->preftype != POP3_TYPE_APOP)
+ switch(pop3c->sasl.prefmech) {
+ case SASL_AUTH_NONE:
+ pop3c->preftype = POP3_TYPE_NONE;
+ break;
+ case SASL_AUTH_DEFAULT:
+ pop3c->preftype = POP3_TYPE_ANY;
+ break;
+ default:
+ pop3c->preftype = POP3_TYPE_SASL;
+ break;
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * pop3_parse_url_path()
+ *
+ * Parse the URL path into separate path components.
+ */
+static CURLcode pop3_parse_url_path(struct Curl_easy *data)
+{
+ /* The POP3 struct is already initialised in pop3_connect() */
+ struct POP3 *pop3 = data->req.p.pop3;
+ const char *path = &data->state.up.path[1]; /* skip leading path */
+
+ /* URL decode the path for the message ID */
+ return Curl_urldecode(path, 0, &pop3->id, NULL, REJECT_CTRL);
+}
+
+/***********************************************************************
+ *
+ * pop3_parse_custom_request()
+ *
+ * Parse the custom request.
+ */
+static CURLcode pop3_parse_custom_request(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct POP3 *pop3 = data->req.p.pop3;
+ const char *custom = data->set.str[STRING_CUSTOMREQUEST];
+
+ /* URL decode the custom request */
+ if(custom)
+ result = Curl_urldecode(custom, 0, &pop3->custom, NULL, REJECT_CTRL);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * Curl_pop3_write()
+ *
+ * This function scans the body after the end-of-body and writes everything
+ * until the end is found.
+ */
+CURLcode Curl_pop3_write(struct Curl_easy *data, char *str, size_t nread)
+{
+ /* This code could be made into a special function in the handler struct */
+ CURLcode result = CURLE_OK;
+ struct SingleRequest *k = &data->req;
+ struct connectdata *conn = data->conn;
+ struct pop3_conn *pop3c = &conn->proto.pop3c;
+ bool strip_dot = FALSE;
+ size_t last = 0;
+ size_t i;
+
+ /* Search through the buffer looking for the end-of-body marker which is
+ 5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
+ the eob so the server will have prefixed it with an extra dot which we
+ need to strip out. Additionally the marker could of course be spread out
+ over 5 different data chunks. */
+ for(i = 0; i < nread; i++) {
+ size_t prev = pop3c->eob;
+
+ switch(str[i]) {
+ case 0x0d:
+ if(pop3c->eob == 0) {
+ pop3c->eob++;
+
+ if(i) {
+ /* Write out the body part that didn't match */
+ result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last],
+ i - last);
+
+ if(result)
+ return result;
+
+ last = i;
+ }
+ }
+ else if(pop3c->eob == 3)
+ pop3c->eob++;
+ else
+ /* If the character match wasn't at position 0 or 3 then restart the
+ pattern matching */
+ pop3c->eob = 1;
+ break;
+
+ case 0x0a:
+ if(pop3c->eob == 1 || pop3c->eob == 4)
+ pop3c->eob++;
+ else
+ /* If the character match wasn't at position 1 or 4 then start the
+ search again */
+ pop3c->eob = 0;
+ break;
+
+ case 0x2e:
+ if(pop3c->eob == 2)
+ pop3c->eob++;
+ else if(pop3c->eob == 3) {
+ /* We have an extra dot after the CRLF which we need to strip off */
+ strip_dot = TRUE;
+ pop3c->eob = 0;
+ }
+ else
+ /* If the character match wasn't at position 2 then start the search
+ again */
+ pop3c->eob = 0;
+ break;
+
+ default:
+ pop3c->eob = 0;
+ break;
+ }
+
+ /* Did we have a partial match which has subsequently failed? */
+ if(prev && prev >= pop3c->eob) {
+ /* Strip can only be non-zero for the very first mismatch after CRLF
+ and then both prev and strip are equal and nothing will be output
+ below */
+ while(prev && pop3c->strip) {
+ prev--;
+ pop3c->strip--;
+ }
+
+ if(prev) {
+ /* If the partial match was the CRLF and dot then only write the CRLF
+ as the server would have inserted the dot */
+ if(strip_dot && prev - 1 > 0) {
+ result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB,
+ prev - 1);
+ }
+ else if(!strip_dot) {
+ result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB,
+ prev);
+ }
+ else {
+ result = CURLE_OK;
+ }
+
+ if(result)
+ return result;
+
+ last = i;
+ strip_dot = FALSE;
+ }
+ }
+ }
+
+ if(pop3c->eob == POP3_EOB_LEN) {
+ /* We have a full match so the transfer is done, however we must transfer
+ the CRLF at the start of the EOB as this is considered to be part of the
+ message as per RFC-1939, sect. 3 */
+ result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
+
+ k->keepon &= ~KEEP_RECV;
+ pop3c->eob = 0;
+
+ return result;
+ }
+
+ if(pop3c->eob)
+ /* While EOB is matching nothing should be output */
+ return CURLE_OK;
+
+ if(nread - last) {
+ result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last],
+ nread - last);
+ }
+
+ return result;
+}
+
+#endif /* CURL_DISABLE_POP3 */
diff --git a/libs/libcurl/src/pop3.h b/libs/libcurl/src/pop3.h index bb0645f9bf..08356b088a 100644 --- a/libs/libcurl/src/pop3.h +++ b/libs/libcurl/src/pop3.h @@ -1,97 +1,97 @@ -#ifndef HEADER_CURL_POP3_H -#define HEADER_CURL_POP3_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2009 - 2022, 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 "pingpong.h" -#include "curl_sasl.h" - -/**************************************************************************** - * POP3 unique setup - ***************************************************************************/ -typedef enum { - POP3_STOP, /* do nothing state, stops the state machine */ - POP3_SERVERGREET, /* waiting for the initial greeting immediately after - a connect */ - POP3_CAPA, - POP3_STARTTLS, - POP3_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS - (multi mode only) */ - POP3_AUTH, - POP3_APOP, - POP3_USER, - POP3_PASS, - POP3_COMMAND, - POP3_QUIT, - POP3_LAST /* never used */ -} pop3state; - -/* This POP3 struct is used in the Curl_easy. All POP3 data that is - connection-oriented must be in pop3_conn to properly deal with the fact that - perhaps the Curl_easy is changed between the times the connection is - used. */ -struct POP3 { - curl_pp_transfer transfer; - char *id; /* Message ID */ - char *custom; /* Custom Request */ -}; - -/* pop3_conn is used for struct connection-oriented data in the connectdata - struct */ -struct pop3_conn { - struct pingpong pp; - pop3state state; /* Always use pop3.c:state() to change state! */ - bool ssldone; /* Is connect() over SSL done? */ - bool tls_supported; /* StartTLS capability supported by server */ - size_t eob; /* Number of bytes of the EOB (End Of Body) that - have been received so far */ - size_t strip; /* Number of bytes from the start to ignore as - non-body */ - struct SASL sasl; /* SASL-related storage */ - unsigned int authtypes; /* Accepted authentication types */ - unsigned int preftype; /* Preferred authentication type */ - char *apoptimestamp; /* APOP timestamp from the server greeting */ -}; - -extern const struct Curl_handler Curl_handler_pop3; -extern const struct Curl_handler Curl_handler_pop3s; - -/* Authentication type flags */ -#define POP3_TYPE_CLEARTEXT (1 << 0) -#define POP3_TYPE_APOP (1 << 1) -#define POP3_TYPE_SASL (1 << 2) - -/* Authentication type values */ -#define POP3_TYPE_NONE 0 -#define POP3_TYPE_ANY ~0U - -/* This is the 5-bytes End-Of-Body marker for POP3 */ -#define POP3_EOB "\x0d\x0a\x2e\x0d\x0a" -#define POP3_EOB_LEN 5 - -/* This function scans the body after the end-of-body and writes everything - * until the end is found */ -CURLcode Curl_pop3_write(struct Curl_easy *data, char *str, size_t nread); - -#endif /* HEADER_CURL_POP3_H */ +#ifndef HEADER_CURL_POP3_H
+#define HEADER_CURL_POP3_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2009 - 2022, 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 "pingpong.h"
+#include "curl_sasl.h"
+
+/****************************************************************************
+ * POP3 unique setup
+ ***************************************************************************/
+typedef enum {
+ POP3_STOP, /* do nothing state, stops the state machine */
+ POP3_SERVERGREET, /* waiting for the initial greeting immediately after
+ a connect */
+ POP3_CAPA,
+ POP3_STARTTLS,
+ POP3_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS
+ (multi mode only) */
+ POP3_AUTH,
+ POP3_APOP,
+ POP3_USER,
+ POP3_PASS,
+ POP3_COMMAND,
+ POP3_QUIT,
+ POP3_LAST /* never used */
+} pop3state;
+
+/* This POP3 struct is used in the Curl_easy. All POP3 data that is
+ connection-oriented must be in pop3_conn to properly deal with the fact that
+ perhaps the Curl_easy is changed between the times the connection is
+ used. */
+struct POP3 {
+ curl_pp_transfer transfer;
+ char *id; /* Message ID */
+ char *custom; /* Custom Request */
+};
+
+/* pop3_conn is used for struct connection-oriented data in the connectdata
+ struct */
+struct pop3_conn {
+ struct pingpong pp;
+ pop3state state; /* Always use pop3.c:state() to change state! */
+ bool ssldone; /* Is connect() over SSL done? */
+ bool tls_supported; /* StartTLS capability supported by server */
+ size_t eob; /* Number of bytes of the EOB (End Of Body) that
+ have been received so far */
+ size_t strip; /* Number of bytes from the start to ignore as
+ non-body */
+ struct SASL sasl; /* SASL-related storage */
+ unsigned int authtypes; /* Accepted authentication types */
+ unsigned int preftype; /* Preferred authentication type */
+ char *apoptimestamp; /* APOP timestamp from the server greeting */
+};
+
+extern const struct Curl_handler Curl_handler_pop3;
+extern const struct Curl_handler Curl_handler_pop3s;
+
+/* Authentication type flags */
+#define POP3_TYPE_CLEARTEXT (1 << 0)
+#define POP3_TYPE_APOP (1 << 1)
+#define POP3_TYPE_SASL (1 << 2)
+
+/* Authentication type values */
+#define POP3_TYPE_NONE 0
+#define POP3_TYPE_ANY ~0U
+
+/* This is the 5-bytes End-Of-Body marker for POP3 */
+#define POP3_EOB "\x0d\x0a\x2e\x0d\x0a"
+#define POP3_EOB_LEN 5
+
+/* This function scans the body after the end-of-body and writes everything
+ * until the end is found */
+CURLcode Curl_pop3_write(struct Curl_easy *data, char *str, size_t nread);
+
+#endif /* HEADER_CURL_POP3_H */
diff --git a/libs/libcurl/src/progress.c b/libs/libcurl/src/progress.c index 4a1e1daa81..86d37ec7af 100644 --- a/libs/libcurl/src/progress.c +++ b/libs/libcurl/src/progress.c @@ -1,621 +1,621 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "sendf.h" -#include "multiif.h" -#include "progress.h" -#include "timeval.h" -#include "curl_printf.h" - -/* check rate limits within this many recent milliseconds, at minimum. */ -#define MIN_RATE_LIMIT_PERIOD 3000 - -#ifndef CURL_DISABLE_PROGRESS_METER -/* Provide a string that is 2 + 1 + 2 + 1 + 2 = 8 letters long (plus the zero - byte) */ -static void time2str(char *r, curl_off_t seconds) -{ - curl_off_t h; - if(seconds <= 0) { - strcpy(r, "--:--:--"); - return; - } - h = seconds / CURL_OFF_T_C(3600); - if(h <= CURL_OFF_T_C(99)) { - curl_off_t m = (seconds - (h*CURL_OFF_T_C(3600))) / CURL_OFF_T_C(60); - curl_off_t s = (seconds - (h*CURL_OFF_T_C(3600))) - (m*CURL_OFF_T_C(60)); - msnprintf(r, 9, "%2" CURL_FORMAT_CURL_OFF_T ":%02" CURL_FORMAT_CURL_OFF_T - ":%02" CURL_FORMAT_CURL_OFF_T, h, m, s); - } - else { - /* this equals to more than 99 hours, switch to a more suitable output - format to fit within the limits. */ - curl_off_t d = seconds / CURL_OFF_T_C(86400); - h = (seconds - (d*CURL_OFF_T_C(86400))) / CURL_OFF_T_C(3600); - if(d <= CURL_OFF_T_C(999)) - msnprintf(r, 9, "%3" CURL_FORMAT_CURL_OFF_T - "d %02" CURL_FORMAT_CURL_OFF_T "h", d, h); - else - msnprintf(r, 9, "%7" CURL_FORMAT_CURL_OFF_T "d", d); - } -} - -/* The point of this function would be to return a string of the input data, - but never longer than 5 columns (+ one zero byte). - Add suffix k, M, G when suitable... */ -static char *max5data(curl_off_t bytes, char *max5) -{ -#define ONE_KILOBYTE CURL_OFF_T_C(1024) -#define ONE_MEGABYTE (CURL_OFF_T_C(1024) * ONE_KILOBYTE) -#define ONE_GIGABYTE (CURL_OFF_T_C(1024) * ONE_MEGABYTE) -#define ONE_TERABYTE (CURL_OFF_T_C(1024) * ONE_GIGABYTE) -#define ONE_PETABYTE (CURL_OFF_T_C(1024) * ONE_TERABYTE) - - if(bytes < CURL_OFF_T_C(100000)) - msnprintf(max5, 6, "%5" CURL_FORMAT_CURL_OFF_T, bytes); - - else if(bytes < CURL_OFF_T_C(10000) * ONE_KILOBYTE) - msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "k", bytes/ONE_KILOBYTE); - - else if(bytes < CURL_OFF_T_C(100) * ONE_MEGABYTE) - /* 'XX.XM' is good as long as we're less than 100 megs */ - msnprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0" - CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE, - (bytes%ONE_MEGABYTE) / (ONE_MEGABYTE/CURL_OFF_T_C(10)) ); - -#if (SIZEOF_CURL_OFF_T > 4) - - else if(bytes < CURL_OFF_T_C(10000) * ONE_MEGABYTE) - /* 'XXXXM' is good until we're at 10000MB or above */ - msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE); - - else if(bytes < CURL_OFF_T_C(100) * ONE_GIGABYTE) - /* 10000 MB - 100 GB, we show it as XX.XG */ - msnprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0" - CURL_FORMAT_CURL_OFF_T "G", bytes/ONE_GIGABYTE, - (bytes%ONE_GIGABYTE) / (ONE_GIGABYTE/CURL_OFF_T_C(10)) ); - - else if(bytes < CURL_OFF_T_C(10000) * ONE_GIGABYTE) - /* up to 10000GB, display without decimal: XXXXG */ - msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "G", bytes/ONE_GIGABYTE); - - else if(bytes < CURL_OFF_T_C(10000) * ONE_TERABYTE) - /* up to 10000TB, display without decimal: XXXXT */ - msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "T", bytes/ONE_TERABYTE); - - else - /* up to 10000PB, display without decimal: XXXXP */ - msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "P", bytes/ONE_PETABYTE); - - /* 16384 petabytes (16 exabytes) is the maximum a 64 bit unsigned number - can hold, but our data type is signed so 8192PB will be the maximum. */ - -#else - - else - msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE); - -#endif - - return max5; -} -#endif - -/* - - New proposed interface, 9th of February 2000: - - pgrsStartNow() - sets start time - pgrsSetDownloadSize(x) - known expected download size - pgrsSetUploadSize(x) - known expected upload size - pgrsSetDownloadCounter() - amount of data currently downloaded - pgrsSetUploadCounter() - amount of data currently uploaded - pgrsUpdate() - show progress - pgrsDone() - transfer complete - -*/ - -int Curl_pgrsDone(struct Curl_easy *data) -{ - int rc; - data->progress.lastshow = 0; - rc = Curl_pgrsUpdate(data); /* the final (forced) update */ - if(rc) - return rc; - - if(!(data->progress.flags & PGRS_HIDE) && - !data->progress.callback) - /* only output if we don't use a progress callback and we're not - * hidden */ - fprintf(data->set.err, "\n"); - - data->progress.speeder_c = 0; /* reset the progress meter display */ - return 0; -} - -/* reset the known transfer sizes */ -void Curl_pgrsResetTransferSizes(struct Curl_easy *data) -{ - Curl_pgrsSetDownloadSize(data, -1); - Curl_pgrsSetUploadSize(data, -1); -} - -/* - * - * Curl_pgrsTime(). Store the current time at the given label. This fetches a - * fresh "now" and returns it. - * - * @unittest: 1399 - */ -struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer) -{ - struct curltime now = Curl_now(); - timediff_t *delta = NULL; - - switch(timer) { - default: - case TIMER_NONE: - /* mistake filter */ - break; - case TIMER_STARTOP: - /* This is set at the start of a transfer */ - data->progress.t_startop = now; - break; - case TIMER_STARTSINGLE: - /* This is set at the start of each single fetch */ - data->progress.t_startsingle = now; - data->progress.is_t_startransfer_set = false; - break; - case TIMER_STARTACCEPT: - data->progress.t_acceptdata = now; - break; - case TIMER_NAMELOOKUP: - delta = &data->progress.t_nslookup; - break; - case TIMER_CONNECT: - delta = &data->progress.t_connect; - break; - case TIMER_APPCONNECT: - delta = &data->progress.t_appconnect; - break; - case TIMER_PRETRANSFER: - delta = &data->progress.t_pretransfer; - break; - case TIMER_STARTTRANSFER: - delta = &data->progress.t_starttransfer; - /* prevent updating t_starttransfer unless: - * 1) this is the first time we're setting t_starttransfer - * 2) a redirect has occurred since the last time t_starttransfer was set - * This prevents repeated invocations of the function from incorrectly - * changing the t_starttransfer time. - */ - if(data->progress.is_t_startransfer_set) { - return now; - } - else { - data->progress.is_t_startransfer_set = true; - break; - } - case TIMER_POSTRANSFER: - /* this is the normal end-of-transfer thing */ - break; - case TIMER_REDIRECT: - data->progress.t_redirect = Curl_timediff_us(now, data->progress.start); - break; - } - if(delta) { - timediff_t us = Curl_timediff_us(now, data->progress.t_startsingle); - if(us < 1) - us = 1; /* make sure at least one microsecond passed */ - *delta += us; - } - return now; -} - -void Curl_pgrsStartNow(struct Curl_easy *data) -{ - data->progress.speeder_c = 0; /* reset the progress meter display */ - data->progress.start = Curl_now(); - data->progress.is_t_startransfer_set = false; - data->progress.ul_limit_start = data->progress.start; - data->progress.dl_limit_start = data->progress.start; - data->progress.ul_limit_size = 0; - data->progress.dl_limit_size = 0; - data->progress.downloaded = 0; - data->progress.uploaded = 0; - /* clear all bits except HIDE and HEADERS_OUT */ - data->progress.flags &= PGRS_HIDE|PGRS_HEADERS_OUT; - Curl_ratelimit(data, data->progress.start); -} - -/* - * This is used to handle speed limits, calculating how many milliseconds to - * wait until we're back under the speed limit, if needed. - * - * The way it works is by having a "starting point" (time & amount of data - * transferred by then) used in the speed computation, to be used instead of - * the start of the transfer. This starting point is regularly moved as - * transfer goes on, to keep getting accurate values (instead of average over - * the entire transfer). - * - * This function takes the current amount of data transferred, the amount at - * the starting point, the limit (in bytes/s), the time of the starting point - * and the current time. - * - * Returns 0 if no waiting is needed or when no waiting is needed but the - * starting point should be reset (to current); or the number of milliseconds - * to wait to get back under the speed limit. - */ -timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize, - curl_off_t startsize, - curl_off_t limit, - struct curltime start, - struct curltime now) -{ - curl_off_t size = cursize - startsize; - timediff_t minimum; - timediff_t actual; - - if(!limit || !size) - return 0; - - /* - * 'minimum' is the number of milliseconds 'size' should take to download to - * stay below 'limit'. - */ - if(size < CURL_OFF_T_MAX/1000) - minimum = (timediff_t) (CURL_OFF_T_C(1000) * size / limit); - else { - minimum = (timediff_t) (size / limit); - if(minimum < TIMEDIFF_T_MAX/1000) - minimum *= 1000; - else - minimum = TIMEDIFF_T_MAX; - } - - /* - * 'actual' is the time in milliseconds it took to actually download the - * last 'size' bytes. - */ - actual = Curl_timediff(now, start); - if(actual < minimum) { - /* if it downloaded the data faster than the limit, make it wait the - difference */ - return (minimum - actual); - } - - return 0; -} - -/* - * Set the number of downloaded bytes so far. - */ -void Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size) -{ - data->progress.downloaded = size; -} - -/* - * Update the timestamp and sizestamp to use for rate limit calculations. - */ -void Curl_ratelimit(struct Curl_easy *data, struct curltime now) -{ - /* don't set a new stamp unless the time since last update is long enough */ - if(data->set.max_recv_speed) { - if(Curl_timediff(now, data->progress.dl_limit_start) >= - MIN_RATE_LIMIT_PERIOD) { - data->progress.dl_limit_start = now; - data->progress.dl_limit_size = data->progress.downloaded; - } - } - if(data->set.max_send_speed) { - if(Curl_timediff(now, data->progress.ul_limit_start) >= - MIN_RATE_LIMIT_PERIOD) { - data->progress.ul_limit_start = now; - data->progress.ul_limit_size = data->progress.uploaded; - } - } -} - -/* - * Set the number of uploaded bytes so far. - */ -void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size) -{ - data->progress.uploaded = size; -} - -void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size) -{ - if(size >= 0) { - data->progress.size_dl = size; - data->progress.flags |= PGRS_DL_SIZE_KNOWN; - } - else { - data->progress.size_dl = 0; - data->progress.flags &= ~PGRS_DL_SIZE_KNOWN; - } -} - -void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size) -{ - if(size >= 0) { - data->progress.size_ul = size; - data->progress.flags |= PGRS_UL_SIZE_KNOWN; - } - else { - data->progress.size_ul = 0; - data->progress.flags &= ~PGRS_UL_SIZE_KNOWN; - } -} - -/* returns the average speed in bytes / second */ -static curl_off_t trspeed(curl_off_t size, /* number of bytes */ - curl_off_t us) /* microseconds */ -{ - if(us < 1) - return size * 1000000; - else if(size < CURL_OFF_T_MAX/1000000) - return (size * 1000000) / us; - else if(us >= 1000000) - return size / (us / 1000000); - else - return CURL_OFF_T_MAX; -} - -/* returns TRUE if it's time to show the progress meter */ -static bool progress_calc(struct Curl_easy *data, struct curltime now) -{ - bool timetoshow = FALSE; - struct Progress * const p = &data->progress; - - /* The time spent so far (from the start) in microseconds */ - p->timespent = Curl_timediff_us(now, p->start); - p->dlspeed = trspeed(p->downloaded, p->timespent); - p->ulspeed = trspeed(p->uploaded, p->timespent); - - /* Calculations done at most once a second, unless end is reached */ - if(p->lastshow != now.tv_sec) { - int countindex; /* amount of seconds stored in the speeder array */ - int nowindex = p->speeder_c% CURR_TIME; - p->lastshow = now.tv_sec; - timetoshow = TRUE; - - /* Let's do the "current speed" thing, with the dl + ul speeds - combined. Store the speed at entry 'nowindex'. */ - p->speeder[ nowindex ] = p->downloaded + p->uploaded; - - /* remember the exact time for this moment */ - p->speeder_time [ nowindex ] = now; - - /* advance our speeder_c counter, which is increased every time we get - here and we expect it to never wrap as 2^32 is a lot of seconds! */ - p->speeder_c++; - - /* figure out how many index entries of data we have stored in our speeder - array. With N_ENTRIES filled in, we have about N_ENTRIES-1 seconds of - transfer. Imagine, after one second we have filled in two entries, - after two seconds we've filled in three entries etc. */ - countindex = ((p->speeder_c >= CURR_TIME)? CURR_TIME:p->speeder_c) - 1; - - /* first of all, we don't do this if there's no counted seconds yet */ - if(countindex) { - int checkindex; - timediff_t span_ms; - curl_off_t amount; - - /* Get the index position to compare with the 'nowindex' position. - Get the oldest entry possible. While we have less than CURR_TIME - entries, the first entry will remain the oldest. */ - checkindex = (p->speeder_c >= CURR_TIME)? p->speeder_c%CURR_TIME:0; - - /* Figure out the exact time for the time span */ - span_ms = Curl_timediff(now, p->speeder_time[checkindex]); - if(0 == span_ms) - span_ms = 1; /* at least one millisecond MUST have passed */ - - /* Calculate the average speed the last 'span_ms' milliseconds */ - amount = p->speeder[nowindex]- p->speeder[checkindex]; - - if(amount > CURL_OFF_T_C(4294967) /* 0xffffffff/1000 */) - /* the 'amount' value is bigger than would fit in 32 bits if - multiplied with 1000, so we use the double math for this */ - p->current_speed = (curl_off_t) - ((double)amount/((double)span_ms/1000.0)); - else - /* the 'amount' value is small enough to fit within 32 bits even - when multiplied with 1000 */ - p->current_speed = amount*CURL_OFF_T_C(1000)/span_ms; - } - else - /* the first second we use the average */ - p->current_speed = p->ulspeed + p->dlspeed; - - } /* Calculations end */ - return timetoshow; -} - -#ifndef CURL_DISABLE_PROGRESS_METER -static void progress_meter(struct Curl_easy *data) -{ - char max5[6][10]; - curl_off_t dlpercen = 0; - curl_off_t ulpercen = 0; - curl_off_t total_percen = 0; - curl_off_t total_transfer; - curl_off_t total_expected_transfer; - char time_left[10]; - char time_total[10]; - char time_spent[10]; - curl_off_t ulestimate = 0; - curl_off_t dlestimate = 0; - curl_off_t total_estimate; - curl_off_t timespent = - (curl_off_t)data->progress.timespent/1000000; /* seconds */ - - if(!(data->progress.flags & PGRS_HEADERS_OUT)) { - if(data->state.resume_from) { - fprintf(data->set.err, - "** Resuming transfer from byte position %" - CURL_FORMAT_CURL_OFF_T "\n", data->state.resume_from); - } - fprintf(data->set.err, - " %% Total %% Received %% Xferd Average Speed " - "Time Time Time Current\n" - " Dload Upload " - "Total Spent Left Speed\n"); - data->progress.flags |= PGRS_HEADERS_OUT; /* headers are shown */ - } - - /* Figure out the estimated time of arrival for the upload */ - if((data->progress.flags & PGRS_UL_SIZE_KNOWN) && - (data->progress.ulspeed > CURL_OFF_T_C(0))) { - ulestimate = data->progress.size_ul / data->progress.ulspeed; - - if(data->progress.size_ul > CURL_OFF_T_C(10000)) - ulpercen = data->progress.uploaded / - (data->progress.size_ul/CURL_OFF_T_C(100)); - else if(data->progress.size_ul > CURL_OFF_T_C(0)) - ulpercen = (data->progress.uploaded*100) / - data->progress.size_ul; - } - - /* ... and the download */ - if((data->progress.flags & PGRS_DL_SIZE_KNOWN) && - (data->progress.dlspeed > CURL_OFF_T_C(0))) { - dlestimate = data->progress.size_dl / data->progress.dlspeed; - - if(data->progress.size_dl > CURL_OFF_T_C(10000)) - dlpercen = data->progress.downloaded / - (data->progress.size_dl/CURL_OFF_T_C(100)); - else if(data->progress.size_dl > CURL_OFF_T_C(0)) - dlpercen = (data->progress.downloaded*100) / - data->progress.size_dl; - } - - /* Now figure out which of them is slower and use that one for the - total estimate! */ - total_estimate = ulestimate>dlestimate?ulestimate:dlestimate; - - /* create the three time strings */ - time2str(time_left, total_estimate > 0?(total_estimate - timespent):0); - time2str(time_total, total_estimate); - time2str(time_spent, timespent); - - /* Get the total amount of data expected to get transferred */ - total_expected_transfer = - ((data->progress.flags & PGRS_UL_SIZE_KNOWN)? - data->progress.size_ul:data->progress.uploaded)+ - ((data->progress.flags & PGRS_DL_SIZE_KNOWN)? - data->progress.size_dl:data->progress.downloaded); - - /* We have transferred this much so far */ - total_transfer = data->progress.downloaded + data->progress.uploaded; - - /* Get the percentage of data transferred so far */ - if(total_expected_transfer > CURL_OFF_T_C(10000)) - total_percen = total_transfer / - (total_expected_transfer/CURL_OFF_T_C(100)); - else if(total_expected_transfer > CURL_OFF_T_C(0)) - total_percen = (total_transfer*100) / total_expected_transfer; - - fprintf(data->set.err, - "\r" - "%3" CURL_FORMAT_CURL_OFF_T " %s " - "%3" CURL_FORMAT_CURL_OFF_T " %s " - "%3" CURL_FORMAT_CURL_OFF_T " %s %s %s %s %s %s %s", - total_percen, /* 3 letters */ /* total % */ - max5data(total_expected_transfer, max5[2]), /* total size */ - dlpercen, /* 3 letters */ /* rcvd % */ - max5data(data->progress.downloaded, max5[0]), /* rcvd size */ - ulpercen, /* 3 letters */ /* xfer % */ - max5data(data->progress.uploaded, max5[1]), /* xfer size */ - max5data(data->progress.dlspeed, max5[3]), /* avrg dl speed */ - max5data(data->progress.ulspeed, max5[4]), /* avrg ul speed */ - time_total, /* 8 letters */ /* total time */ - time_spent, /* 8 letters */ /* time spent */ - time_left, /* 8 letters */ /* time left */ - max5data(data->progress.current_speed, max5[5]) - ); - - /* we flush the output stream to make it appear as soon as possible */ - fflush(data->set.err); -} -#else - /* progress bar disabled */ -#define progress_meter(x) Curl_nop_stmt -#endif - - -/* - * Curl_pgrsUpdate() returns 0 for success or the value returned by the - * progress callback! - */ -int Curl_pgrsUpdate(struct Curl_easy *data) -{ - struct curltime now = Curl_now(); /* what time is it */ - bool showprogress = progress_calc(data, now); - if(!(data->progress.flags & PGRS_HIDE)) { - if(data->set.fxferinfo) { - int result; - /* There's a callback set, call that */ - Curl_set_in_callback(data, true); - result = data->set.fxferinfo(data->set.progress_client, - data->progress.size_dl, - data->progress.downloaded, - data->progress.size_ul, - data->progress.uploaded); - Curl_set_in_callback(data, false); - if(result != CURL_PROGRESSFUNC_CONTINUE) { - if(result) - failf(data, "Callback aborted"); - return result; - } - } - else if(data->set.fprogress) { - int result; - /* The older deprecated callback is set, call that */ - Curl_set_in_callback(data, true); - result = data->set.fprogress(data->set.progress_client, - (double)data->progress.size_dl, - (double)data->progress.downloaded, - (double)data->progress.size_ul, - (double)data->progress.uploaded); - Curl_set_in_callback(data, false); - if(result != CURL_PROGRESSFUNC_CONTINUE) { - if(result) - failf(data, "Callback aborted"); - return result; - } - } - - if(showprogress) - progress_meter(data); - } - - return 0; -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "sendf.h"
+#include "multiif.h"
+#include "progress.h"
+#include "timeval.h"
+#include "curl_printf.h"
+
+/* check rate limits within this many recent milliseconds, at minimum. */
+#define MIN_RATE_LIMIT_PERIOD 3000
+
+#ifndef CURL_DISABLE_PROGRESS_METER
+/* Provide a string that is 2 + 1 + 2 + 1 + 2 = 8 letters long (plus the zero
+ byte) */
+static void time2str(char *r, curl_off_t seconds)
+{
+ curl_off_t h;
+ if(seconds <= 0) {
+ strcpy(r, "--:--:--");
+ return;
+ }
+ h = seconds / CURL_OFF_T_C(3600);
+ if(h <= CURL_OFF_T_C(99)) {
+ curl_off_t m = (seconds - (h*CURL_OFF_T_C(3600))) / CURL_OFF_T_C(60);
+ curl_off_t s = (seconds - (h*CURL_OFF_T_C(3600))) - (m*CURL_OFF_T_C(60));
+ msnprintf(r, 9, "%2" CURL_FORMAT_CURL_OFF_T ":%02" CURL_FORMAT_CURL_OFF_T
+ ":%02" CURL_FORMAT_CURL_OFF_T, h, m, s);
+ }
+ else {
+ /* this equals to more than 99 hours, switch to a more suitable output
+ format to fit within the limits. */
+ curl_off_t d = seconds / CURL_OFF_T_C(86400);
+ h = (seconds - (d*CURL_OFF_T_C(86400))) / CURL_OFF_T_C(3600);
+ if(d <= CURL_OFF_T_C(999))
+ msnprintf(r, 9, "%3" CURL_FORMAT_CURL_OFF_T
+ "d %02" CURL_FORMAT_CURL_OFF_T "h", d, h);
+ else
+ msnprintf(r, 9, "%7" CURL_FORMAT_CURL_OFF_T "d", d);
+ }
+}
+
+/* The point of this function would be to return a string of the input data,
+ but never longer than 5 columns (+ one zero byte).
+ Add suffix k, M, G when suitable... */
+static char *max5data(curl_off_t bytes, char *max5)
+{
+#define ONE_KILOBYTE CURL_OFF_T_C(1024)
+#define ONE_MEGABYTE (CURL_OFF_T_C(1024) * ONE_KILOBYTE)
+#define ONE_GIGABYTE (CURL_OFF_T_C(1024) * ONE_MEGABYTE)
+#define ONE_TERABYTE (CURL_OFF_T_C(1024) * ONE_GIGABYTE)
+#define ONE_PETABYTE (CURL_OFF_T_C(1024) * ONE_TERABYTE)
+
+ if(bytes < CURL_OFF_T_C(100000))
+ msnprintf(max5, 6, "%5" CURL_FORMAT_CURL_OFF_T, bytes);
+
+ else if(bytes < CURL_OFF_T_C(10000) * ONE_KILOBYTE)
+ msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "k", bytes/ONE_KILOBYTE);
+
+ else if(bytes < CURL_OFF_T_C(100) * ONE_MEGABYTE)
+ /* 'XX.XM' is good as long as we're less than 100 megs */
+ msnprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0"
+ CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE,
+ (bytes%ONE_MEGABYTE) / (ONE_MEGABYTE/CURL_OFF_T_C(10)) );
+
+#if (SIZEOF_CURL_OFF_T > 4)
+
+ else if(bytes < CURL_OFF_T_C(10000) * ONE_MEGABYTE)
+ /* 'XXXXM' is good until we're at 10000MB or above */
+ msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE);
+
+ else if(bytes < CURL_OFF_T_C(100) * ONE_GIGABYTE)
+ /* 10000 MB - 100 GB, we show it as XX.XG */
+ msnprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0"
+ CURL_FORMAT_CURL_OFF_T "G", bytes/ONE_GIGABYTE,
+ (bytes%ONE_GIGABYTE) / (ONE_GIGABYTE/CURL_OFF_T_C(10)) );
+
+ else if(bytes < CURL_OFF_T_C(10000) * ONE_GIGABYTE)
+ /* up to 10000GB, display without decimal: XXXXG */
+ msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "G", bytes/ONE_GIGABYTE);
+
+ else if(bytes < CURL_OFF_T_C(10000) * ONE_TERABYTE)
+ /* up to 10000TB, display without decimal: XXXXT */
+ msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "T", bytes/ONE_TERABYTE);
+
+ else
+ /* up to 10000PB, display without decimal: XXXXP */
+ msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "P", bytes/ONE_PETABYTE);
+
+ /* 16384 petabytes (16 exabytes) is the maximum a 64 bit unsigned number
+ can hold, but our data type is signed so 8192PB will be the maximum. */
+
+#else
+
+ else
+ msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE);
+
+#endif
+
+ return max5;
+}
+#endif
+
+/*
+
+ New proposed interface, 9th of February 2000:
+
+ pgrsStartNow() - sets start time
+ pgrsSetDownloadSize(x) - known expected download size
+ pgrsSetUploadSize(x) - known expected upload size
+ pgrsSetDownloadCounter() - amount of data currently downloaded
+ pgrsSetUploadCounter() - amount of data currently uploaded
+ pgrsUpdate() - show progress
+ pgrsDone() - transfer complete
+
+*/
+
+int Curl_pgrsDone(struct Curl_easy *data)
+{
+ int rc;
+ data->progress.lastshow = 0;
+ rc = Curl_pgrsUpdate(data); /* the final (forced) update */
+ if(rc)
+ return rc;
+
+ if(!(data->progress.flags & PGRS_HIDE) &&
+ !data->progress.callback)
+ /* only output if we don't use a progress callback and we're not
+ * hidden */
+ fprintf(data->set.err, "\n");
+
+ data->progress.speeder_c = 0; /* reset the progress meter display */
+ return 0;
+}
+
+/* reset the known transfer sizes */
+void Curl_pgrsResetTransferSizes(struct Curl_easy *data)
+{
+ Curl_pgrsSetDownloadSize(data, -1);
+ Curl_pgrsSetUploadSize(data, -1);
+}
+
+/*
+ *
+ * Curl_pgrsTime(). Store the current time at the given label. This fetches a
+ * fresh "now" and returns it.
+ *
+ * @unittest: 1399
+ */
+struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer)
+{
+ struct curltime now = Curl_now();
+ timediff_t *delta = NULL;
+
+ switch(timer) {
+ default:
+ case TIMER_NONE:
+ /* mistake filter */
+ break;
+ case TIMER_STARTOP:
+ /* This is set at the start of a transfer */
+ data->progress.t_startop = now;
+ break;
+ case TIMER_STARTSINGLE:
+ /* This is set at the start of each single fetch */
+ data->progress.t_startsingle = now;
+ data->progress.is_t_startransfer_set = false;
+ break;
+ case TIMER_STARTACCEPT:
+ data->progress.t_acceptdata = now;
+ break;
+ case TIMER_NAMELOOKUP:
+ delta = &data->progress.t_nslookup;
+ break;
+ case TIMER_CONNECT:
+ delta = &data->progress.t_connect;
+ break;
+ case TIMER_APPCONNECT:
+ delta = &data->progress.t_appconnect;
+ break;
+ case TIMER_PRETRANSFER:
+ delta = &data->progress.t_pretransfer;
+ break;
+ case TIMER_STARTTRANSFER:
+ delta = &data->progress.t_starttransfer;
+ /* prevent updating t_starttransfer unless:
+ * 1) this is the first time we're setting t_starttransfer
+ * 2) a redirect has occurred since the last time t_starttransfer was set
+ * This prevents repeated invocations of the function from incorrectly
+ * changing the t_starttransfer time.
+ */
+ if(data->progress.is_t_startransfer_set) {
+ return now;
+ }
+ else {
+ data->progress.is_t_startransfer_set = true;
+ break;
+ }
+ case TIMER_POSTRANSFER:
+ /* this is the normal end-of-transfer thing */
+ break;
+ case TIMER_REDIRECT:
+ data->progress.t_redirect = Curl_timediff_us(now, data->progress.start);
+ break;
+ }
+ if(delta) {
+ timediff_t us = Curl_timediff_us(now, data->progress.t_startsingle);
+ if(us < 1)
+ us = 1; /* make sure at least one microsecond passed */
+ *delta += us;
+ }
+ return now;
+}
+
+void Curl_pgrsStartNow(struct Curl_easy *data)
+{
+ data->progress.speeder_c = 0; /* reset the progress meter display */
+ data->progress.start = Curl_now();
+ data->progress.is_t_startransfer_set = false;
+ data->progress.ul_limit_start = data->progress.start;
+ data->progress.dl_limit_start = data->progress.start;
+ data->progress.ul_limit_size = 0;
+ data->progress.dl_limit_size = 0;
+ data->progress.downloaded = 0;
+ data->progress.uploaded = 0;
+ /* clear all bits except HIDE and HEADERS_OUT */
+ data->progress.flags &= PGRS_HIDE|PGRS_HEADERS_OUT;
+ Curl_ratelimit(data, data->progress.start);
+}
+
+/*
+ * This is used to handle speed limits, calculating how many milliseconds to
+ * wait until we're back under the speed limit, if needed.
+ *
+ * The way it works is by having a "starting point" (time & amount of data
+ * transferred by then) used in the speed computation, to be used instead of
+ * the start of the transfer. This starting point is regularly moved as
+ * transfer goes on, to keep getting accurate values (instead of average over
+ * the entire transfer).
+ *
+ * This function takes the current amount of data transferred, the amount at
+ * the starting point, the limit (in bytes/s), the time of the starting point
+ * and the current time.
+ *
+ * Returns 0 if no waiting is needed or when no waiting is needed but the
+ * starting point should be reset (to current); or the number of milliseconds
+ * to wait to get back under the speed limit.
+ */
+timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize,
+ curl_off_t startsize,
+ curl_off_t limit,
+ struct curltime start,
+ struct curltime now)
+{
+ curl_off_t size = cursize - startsize;
+ timediff_t minimum;
+ timediff_t actual;
+
+ if(!limit || !size)
+ return 0;
+
+ /*
+ * 'minimum' is the number of milliseconds 'size' should take to download to
+ * stay below 'limit'.
+ */
+ if(size < CURL_OFF_T_MAX/1000)
+ minimum = (timediff_t) (CURL_OFF_T_C(1000) * size / limit);
+ else {
+ minimum = (timediff_t) (size / limit);
+ if(minimum < TIMEDIFF_T_MAX/1000)
+ minimum *= 1000;
+ else
+ minimum = TIMEDIFF_T_MAX;
+ }
+
+ /*
+ * 'actual' is the time in milliseconds it took to actually download the
+ * last 'size' bytes.
+ */
+ actual = Curl_timediff(now, start);
+ if(actual < minimum) {
+ /* if it downloaded the data faster than the limit, make it wait the
+ difference */
+ return (minimum - actual);
+ }
+
+ return 0;
+}
+
+/*
+ * Set the number of downloaded bytes so far.
+ */
+void Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size)
+{
+ data->progress.downloaded = size;
+}
+
+/*
+ * Update the timestamp and sizestamp to use for rate limit calculations.
+ */
+void Curl_ratelimit(struct Curl_easy *data, struct curltime now)
+{
+ /* don't set a new stamp unless the time since last update is long enough */
+ if(data->set.max_recv_speed) {
+ if(Curl_timediff(now, data->progress.dl_limit_start) >=
+ MIN_RATE_LIMIT_PERIOD) {
+ data->progress.dl_limit_start = now;
+ data->progress.dl_limit_size = data->progress.downloaded;
+ }
+ }
+ if(data->set.max_send_speed) {
+ if(Curl_timediff(now, data->progress.ul_limit_start) >=
+ MIN_RATE_LIMIT_PERIOD) {
+ data->progress.ul_limit_start = now;
+ data->progress.ul_limit_size = data->progress.uploaded;
+ }
+ }
+}
+
+/*
+ * Set the number of uploaded bytes so far.
+ */
+void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size)
+{
+ data->progress.uploaded = size;
+}
+
+void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size)
+{
+ if(size >= 0) {
+ data->progress.size_dl = size;
+ data->progress.flags |= PGRS_DL_SIZE_KNOWN;
+ }
+ else {
+ data->progress.size_dl = 0;
+ data->progress.flags &= ~PGRS_DL_SIZE_KNOWN;
+ }
+}
+
+void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size)
+{
+ if(size >= 0) {
+ data->progress.size_ul = size;
+ data->progress.flags |= PGRS_UL_SIZE_KNOWN;
+ }
+ else {
+ data->progress.size_ul = 0;
+ data->progress.flags &= ~PGRS_UL_SIZE_KNOWN;
+ }
+}
+
+/* returns the average speed in bytes / second */
+static curl_off_t trspeed(curl_off_t size, /* number of bytes */
+ curl_off_t us) /* microseconds */
+{
+ if(us < 1)
+ return size * 1000000;
+ else if(size < CURL_OFF_T_MAX/1000000)
+ return (size * 1000000) / us;
+ else if(us >= 1000000)
+ return size / (us / 1000000);
+ else
+ return CURL_OFF_T_MAX;
+}
+
+/* returns TRUE if it's time to show the progress meter */
+static bool progress_calc(struct Curl_easy *data, struct curltime now)
+{
+ bool timetoshow = FALSE;
+ struct Progress * const p = &data->progress;
+
+ /* The time spent so far (from the start) in microseconds */
+ p->timespent = Curl_timediff_us(now, p->start);
+ p->dlspeed = trspeed(p->downloaded, p->timespent);
+ p->ulspeed = trspeed(p->uploaded, p->timespent);
+
+ /* Calculations done at most once a second, unless end is reached */
+ if(p->lastshow != now.tv_sec) {
+ int countindex; /* amount of seconds stored in the speeder array */
+ int nowindex = p->speeder_c% CURR_TIME;
+ p->lastshow = now.tv_sec;
+ timetoshow = TRUE;
+
+ /* Let's do the "current speed" thing, with the dl + ul speeds
+ combined. Store the speed at entry 'nowindex'. */
+ p->speeder[ nowindex ] = p->downloaded + p->uploaded;
+
+ /* remember the exact time for this moment */
+ p->speeder_time [ nowindex ] = now;
+
+ /* advance our speeder_c counter, which is increased every time we get
+ here and we expect it to never wrap as 2^32 is a lot of seconds! */
+ p->speeder_c++;
+
+ /* figure out how many index entries of data we have stored in our speeder
+ array. With N_ENTRIES filled in, we have about N_ENTRIES-1 seconds of
+ transfer. Imagine, after one second we have filled in two entries,
+ after two seconds we've filled in three entries etc. */
+ countindex = ((p->speeder_c >= CURR_TIME)? CURR_TIME:p->speeder_c) - 1;
+
+ /* first of all, we don't do this if there's no counted seconds yet */
+ if(countindex) {
+ int checkindex;
+ timediff_t span_ms;
+ curl_off_t amount;
+
+ /* Get the index position to compare with the 'nowindex' position.
+ Get the oldest entry possible. While we have less than CURR_TIME
+ entries, the first entry will remain the oldest. */
+ checkindex = (p->speeder_c >= CURR_TIME)? p->speeder_c%CURR_TIME:0;
+
+ /* Figure out the exact time for the time span */
+ span_ms = Curl_timediff(now, p->speeder_time[checkindex]);
+ if(0 == span_ms)
+ span_ms = 1; /* at least one millisecond MUST have passed */
+
+ /* Calculate the average speed the last 'span_ms' milliseconds */
+ amount = p->speeder[nowindex]- p->speeder[checkindex];
+
+ if(amount > CURL_OFF_T_C(4294967) /* 0xffffffff/1000 */)
+ /* the 'amount' value is bigger than would fit in 32 bits if
+ multiplied with 1000, so we use the double math for this */
+ p->current_speed = (curl_off_t)
+ ((double)amount/((double)span_ms/1000.0));
+ else
+ /* the 'amount' value is small enough to fit within 32 bits even
+ when multiplied with 1000 */
+ p->current_speed = amount*CURL_OFF_T_C(1000)/span_ms;
+ }
+ else
+ /* the first second we use the average */
+ p->current_speed = p->ulspeed + p->dlspeed;
+
+ } /* Calculations end */
+ return timetoshow;
+}
+
+#ifndef CURL_DISABLE_PROGRESS_METER
+static void progress_meter(struct Curl_easy *data)
+{
+ char max5[6][10];
+ curl_off_t dlpercen = 0;
+ curl_off_t ulpercen = 0;
+ curl_off_t total_percen = 0;
+ curl_off_t total_transfer;
+ curl_off_t total_expected_transfer;
+ char time_left[10];
+ char time_total[10];
+ char time_spent[10];
+ curl_off_t ulestimate = 0;
+ curl_off_t dlestimate = 0;
+ curl_off_t total_estimate;
+ curl_off_t timespent =
+ (curl_off_t)data->progress.timespent/1000000; /* seconds */
+
+ if(!(data->progress.flags & PGRS_HEADERS_OUT)) {
+ if(data->state.resume_from) {
+ fprintf(data->set.err,
+ "** Resuming transfer from byte position %"
+ CURL_FORMAT_CURL_OFF_T "\n", data->state.resume_from);
+ }
+ fprintf(data->set.err,
+ " %% Total %% Received %% Xferd Average Speed "
+ "Time Time Time Current\n"
+ " Dload Upload "
+ "Total Spent Left Speed\n");
+ data->progress.flags |= PGRS_HEADERS_OUT; /* headers are shown */
+ }
+
+ /* Figure out the estimated time of arrival for the upload */
+ if((data->progress.flags & PGRS_UL_SIZE_KNOWN) &&
+ (data->progress.ulspeed > CURL_OFF_T_C(0))) {
+ ulestimate = data->progress.size_ul / data->progress.ulspeed;
+
+ if(data->progress.size_ul > CURL_OFF_T_C(10000))
+ ulpercen = data->progress.uploaded /
+ (data->progress.size_ul/CURL_OFF_T_C(100));
+ else if(data->progress.size_ul > CURL_OFF_T_C(0))
+ ulpercen = (data->progress.uploaded*100) /
+ data->progress.size_ul;
+ }
+
+ /* ... and the download */
+ if((data->progress.flags & PGRS_DL_SIZE_KNOWN) &&
+ (data->progress.dlspeed > CURL_OFF_T_C(0))) {
+ dlestimate = data->progress.size_dl / data->progress.dlspeed;
+
+ if(data->progress.size_dl > CURL_OFF_T_C(10000))
+ dlpercen = data->progress.downloaded /
+ (data->progress.size_dl/CURL_OFF_T_C(100));
+ else if(data->progress.size_dl > CURL_OFF_T_C(0))
+ dlpercen = (data->progress.downloaded*100) /
+ data->progress.size_dl;
+ }
+
+ /* Now figure out which of them is slower and use that one for the
+ total estimate! */
+ total_estimate = ulestimate>dlestimate?ulestimate:dlestimate;
+
+ /* create the three time strings */
+ time2str(time_left, total_estimate > 0?(total_estimate - timespent):0);
+ time2str(time_total, total_estimate);
+ time2str(time_spent, timespent);
+
+ /* Get the total amount of data expected to get transferred */
+ total_expected_transfer =
+ ((data->progress.flags & PGRS_UL_SIZE_KNOWN)?
+ data->progress.size_ul:data->progress.uploaded)+
+ ((data->progress.flags & PGRS_DL_SIZE_KNOWN)?
+ data->progress.size_dl:data->progress.downloaded);
+
+ /* We have transferred this much so far */
+ total_transfer = data->progress.downloaded + data->progress.uploaded;
+
+ /* Get the percentage of data transferred so far */
+ if(total_expected_transfer > CURL_OFF_T_C(10000))
+ total_percen = total_transfer /
+ (total_expected_transfer/CURL_OFF_T_C(100));
+ else if(total_expected_transfer > CURL_OFF_T_C(0))
+ total_percen = (total_transfer*100) / total_expected_transfer;
+
+ fprintf(data->set.err,
+ "\r"
+ "%3" CURL_FORMAT_CURL_OFF_T " %s "
+ "%3" CURL_FORMAT_CURL_OFF_T " %s "
+ "%3" CURL_FORMAT_CURL_OFF_T " %s %s %s %s %s %s %s",
+ total_percen, /* 3 letters */ /* total % */
+ max5data(total_expected_transfer, max5[2]), /* total size */
+ dlpercen, /* 3 letters */ /* rcvd % */
+ max5data(data->progress.downloaded, max5[0]), /* rcvd size */
+ ulpercen, /* 3 letters */ /* xfer % */
+ max5data(data->progress.uploaded, max5[1]), /* xfer size */
+ max5data(data->progress.dlspeed, max5[3]), /* avrg dl speed */
+ max5data(data->progress.ulspeed, max5[4]), /* avrg ul speed */
+ time_total, /* 8 letters */ /* total time */
+ time_spent, /* 8 letters */ /* time spent */
+ time_left, /* 8 letters */ /* time left */
+ max5data(data->progress.current_speed, max5[5])
+ );
+
+ /* we flush the output stream to make it appear as soon as possible */
+ fflush(data->set.err);
+}
+#else
+ /* progress bar disabled */
+#define progress_meter(x) Curl_nop_stmt
+#endif
+
+
+/*
+ * Curl_pgrsUpdate() returns 0 for success or the value returned by the
+ * progress callback!
+ */
+int Curl_pgrsUpdate(struct Curl_easy *data)
+{
+ struct curltime now = Curl_now(); /* what time is it */
+ bool showprogress = progress_calc(data, now);
+ if(!(data->progress.flags & PGRS_HIDE)) {
+ if(data->set.fxferinfo) {
+ int result;
+ /* There's a callback set, call that */
+ Curl_set_in_callback(data, true);
+ result = data->set.fxferinfo(data->set.progress_client,
+ data->progress.size_dl,
+ data->progress.downloaded,
+ data->progress.size_ul,
+ data->progress.uploaded);
+ Curl_set_in_callback(data, false);
+ if(result != CURL_PROGRESSFUNC_CONTINUE) {
+ if(result)
+ failf(data, "Callback aborted");
+ return result;
+ }
+ }
+ else if(data->set.fprogress) {
+ int result;
+ /* The older deprecated callback is set, call that */
+ Curl_set_in_callback(data, true);
+ result = data->set.fprogress(data->set.progress_client,
+ (double)data->progress.size_dl,
+ (double)data->progress.downloaded,
+ (double)data->progress.size_ul,
+ (double)data->progress.uploaded);
+ Curl_set_in_callback(data, false);
+ if(result != CURL_PROGRESSFUNC_CONTINUE) {
+ if(result)
+ failf(data, "Callback aborted");
+ return result;
+ }
+ }
+
+ if(showprogress)
+ progress_meter(data);
+ }
+
+ return 0;
+}
diff --git a/libs/libcurl/src/progress.h b/libs/libcurl/src/progress.h index a129315147..d116fcfb97 100644 --- a/libs/libcurl/src/progress.h +++ b/libs/libcurl/src/progress.h @@ -1,66 +1,66 @@ -#ifndef HEADER_CURL_PROGRESS_H -#define HEADER_CURL_PROGRESS_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "timeval.h" - - -typedef enum { - TIMER_NONE, - TIMER_STARTOP, - TIMER_STARTSINGLE, - TIMER_NAMELOOKUP, - TIMER_CONNECT, - TIMER_APPCONNECT, - TIMER_PRETRANSFER, - TIMER_STARTTRANSFER, - TIMER_POSTRANSFER, - TIMER_STARTACCEPT, - TIMER_REDIRECT, - TIMER_LAST /* must be last */ -} timerid; - -int Curl_pgrsDone(struct Curl_easy *data); -void Curl_pgrsStartNow(struct Curl_easy *data); -void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size); -void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size); -void Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size); -void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size); -void Curl_ratelimit(struct Curl_easy *data, struct curltime now); -int Curl_pgrsUpdate(struct Curl_easy *data); -void Curl_pgrsResetTransferSizes(struct Curl_easy *data); -struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer); -timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize, - curl_off_t startsize, - curl_off_t limit, - struct curltime start, - struct curltime now); - -#define PGRS_HIDE (1<<4) -#define PGRS_UL_SIZE_KNOWN (1<<5) -#define PGRS_DL_SIZE_KNOWN (1<<6) -#define PGRS_HEADERS_OUT (1<<7) /* set when the headers have been written */ - -#endif /* HEADER_CURL_PROGRESS_H */ +#ifndef HEADER_CURL_PROGRESS_H
+#define HEADER_CURL_PROGRESS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "timeval.h"
+
+
+typedef enum {
+ TIMER_NONE,
+ TIMER_STARTOP,
+ TIMER_STARTSINGLE,
+ TIMER_NAMELOOKUP,
+ TIMER_CONNECT,
+ TIMER_APPCONNECT,
+ TIMER_PRETRANSFER,
+ TIMER_STARTTRANSFER,
+ TIMER_POSTRANSFER,
+ TIMER_STARTACCEPT,
+ TIMER_REDIRECT,
+ TIMER_LAST /* must be last */
+} timerid;
+
+int Curl_pgrsDone(struct Curl_easy *data);
+void Curl_pgrsStartNow(struct Curl_easy *data);
+void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size);
+void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size);
+void Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size);
+void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size);
+void Curl_ratelimit(struct Curl_easy *data, struct curltime now);
+int Curl_pgrsUpdate(struct Curl_easy *data);
+void Curl_pgrsResetTransferSizes(struct Curl_easy *data);
+struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer);
+timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize,
+ curl_off_t startsize,
+ curl_off_t limit,
+ struct curltime start,
+ struct curltime now);
+
+#define PGRS_HIDE (1<<4)
+#define PGRS_UL_SIZE_KNOWN (1<<5)
+#define PGRS_DL_SIZE_KNOWN (1<<6)
+#define PGRS_HEADERS_OUT (1<<7) /* set when the headers have been written */
+
+#endif /* HEADER_CURL_PROGRESS_H */
diff --git a/libs/libcurl/src/psl.c b/libs/libcurl/src/psl.c index 60c98a4ca4..d005a81bf1 100644 --- a/libs/libcurl/src/psl.c +++ b/libs/libcurl/src/psl.c @@ -1,113 +1,113 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_LIBPSL - -#include "psl.h" -#include "share.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -void Curl_psl_destroy(struct PslCache *pslcache) -{ - if(pslcache->psl) { - if(pslcache->dynamic) - psl_free((psl_ctx_t *) pslcache->psl); - pslcache->psl = NULL; - pslcache->dynamic = FALSE; - } -} - -static time_t now_seconds(void) -{ - struct curltime now = Curl_now(); - - return now.tv_sec; -} - -const psl_ctx_t *Curl_psl_use(struct Curl_easy *easy) -{ - struct PslCache *pslcache = easy->psl; - const psl_ctx_t *psl; - time_t now; - - if(!pslcache) - return NULL; - - Curl_share_lock(easy, CURL_LOCK_DATA_PSL, CURL_LOCK_ACCESS_SHARED); - now = now_seconds(); - if(!pslcache->psl || pslcache->expires <= now) { - /* Let a chance to other threads to do the job: avoids deadlock. */ - Curl_share_unlock(easy, CURL_LOCK_DATA_PSL); - - /* Update cache: this needs an exclusive lock. */ - Curl_share_lock(easy, CURL_LOCK_DATA_PSL, CURL_LOCK_ACCESS_SINGLE); - - /* Recheck in case another thread did the job. */ - now = now_seconds(); - if(!pslcache->psl || pslcache->expires <= now) { - bool dynamic = FALSE; - time_t expires = TIME_T_MAX; - -#if defined(PSL_VERSION_NUMBER) && PSL_VERSION_NUMBER >= 0x001000 - psl = psl_latest(NULL); - dynamic = psl != NULL; - /* Take care of possible time computation overflow. */ - expires = now < TIME_T_MAX - PSL_TTL? now + PSL_TTL: TIME_T_MAX; - - /* Only get the built-in PSL if we do not already have the "latest". */ - if(!psl && !pslcache->dynamic) -#endif - - psl = psl_builtin(); - - if(psl) { - Curl_psl_destroy(pslcache); - pslcache->psl = psl; - pslcache->dynamic = dynamic; - pslcache->expires = expires; - } - } - Curl_share_unlock(easy, CURL_LOCK_DATA_PSL); /* Release exclusive lock. */ - Curl_share_lock(easy, CURL_LOCK_DATA_PSL, CURL_LOCK_ACCESS_SHARED); - } - psl = pslcache->psl; - if(!psl) - Curl_share_unlock(easy, CURL_LOCK_DATA_PSL); - return psl; -} - -void Curl_psl_release(struct Curl_easy *easy) -{ - Curl_share_unlock(easy, CURL_LOCK_DATA_PSL); -} - -#endif /* USE_LIBPSL */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_LIBPSL
+
+#include "psl.h"
+#include "share.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+void Curl_psl_destroy(struct PslCache *pslcache)
+{
+ if(pslcache->psl) {
+ if(pslcache->dynamic)
+ psl_free((psl_ctx_t *) pslcache->psl);
+ pslcache->psl = NULL;
+ pslcache->dynamic = FALSE;
+ }
+}
+
+static time_t now_seconds(void)
+{
+ struct curltime now = Curl_now();
+
+ return now.tv_sec;
+}
+
+const psl_ctx_t *Curl_psl_use(struct Curl_easy *easy)
+{
+ struct PslCache *pslcache = easy->psl;
+ const psl_ctx_t *psl;
+ time_t now;
+
+ if(!pslcache)
+ return NULL;
+
+ Curl_share_lock(easy, CURL_LOCK_DATA_PSL, CURL_LOCK_ACCESS_SHARED);
+ now = now_seconds();
+ if(!pslcache->psl || pslcache->expires <= now) {
+ /* Let a chance to other threads to do the job: avoids deadlock. */
+ Curl_share_unlock(easy, CURL_LOCK_DATA_PSL);
+
+ /* Update cache: this needs an exclusive lock. */
+ Curl_share_lock(easy, CURL_LOCK_DATA_PSL, CURL_LOCK_ACCESS_SINGLE);
+
+ /* Recheck in case another thread did the job. */
+ now = now_seconds();
+ if(!pslcache->psl || pslcache->expires <= now) {
+ bool dynamic = FALSE;
+ time_t expires = TIME_T_MAX;
+
+#if defined(PSL_VERSION_NUMBER) && PSL_VERSION_NUMBER >= 0x001000
+ psl = psl_latest(NULL);
+ dynamic = psl != NULL;
+ /* Take care of possible time computation overflow. */
+ expires = now < TIME_T_MAX - PSL_TTL? now + PSL_TTL: TIME_T_MAX;
+
+ /* Only get the built-in PSL if we do not already have the "latest". */
+ if(!psl && !pslcache->dynamic)
+#endif
+
+ psl = psl_builtin();
+
+ if(psl) {
+ Curl_psl_destroy(pslcache);
+ pslcache->psl = psl;
+ pslcache->dynamic = dynamic;
+ pslcache->expires = expires;
+ }
+ }
+ Curl_share_unlock(easy, CURL_LOCK_DATA_PSL); /* Release exclusive lock. */
+ Curl_share_lock(easy, CURL_LOCK_DATA_PSL, CURL_LOCK_ACCESS_SHARED);
+ }
+ psl = pslcache->psl;
+ if(!psl)
+ Curl_share_unlock(easy, CURL_LOCK_DATA_PSL);
+ return psl;
+}
+
+void Curl_psl_release(struct Curl_easy *easy)
+{
+ Curl_share_unlock(easy, CURL_LOCK_DATA_PSL);
+}
+
+#endif /* USE_LIBPSL */
diff --git a/libs/libcurl/src/psl.h b/libs/libcurl/src/psl.h index 34f0a5cae8..8f0a88adfc 100644 --- a/libs/libcurl/src/psl.h +++ b/libs/libcurl/src/psl.h @@ -1,49 +1,49 @@ -#ifndef HEADER_PSL_H -#define HEADER_PSL_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - -#ifdef USE_LIBPSL -#include <libpsl.h> - -#define PSL_TTL (72 * 3600) /* PSL time to live before a refresh. */ - -struct PslCache { - const psl_ctx_t *psl; /* The PSL. */ - time_t expires; /* Time this PSL life expires. */ - bool dynamic; /* PSL should be released when no longer needed. */ -}; - -const psl_ctx_t *Curl_psl_use(struct Curl_easy *easy); -void Curl_psl_release(struct Curl_easy *easy); -void Curl_psl_destroy(struct PslCache *pslcache); - -#else - -#define Curl_psl_use(easy) NULL -#define Curl_psl_release(easy) -#define Curl_psl_destroy(pslcache) - -#endif /* USE_LIBPSL */ -#endif /* HEADER_PSL_H */ +#ifndef HEADER_PSL_H
+#define HEADER_PSL_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+#ifdef USE_LIBPSL
+#include <libpsl.h>
+
+#define PSL_TTL (72 * 3600) /* PSL time to live before a refresh. */
+
+struct PslCache {
+ const psl_ctx_t *psl; /* The PSL. */
+ time_t expires; /* Time this PSL life expires. */
+ bool dynamic; /* PSL should be released when no longer needed. */
+};
+
+const psl_ctx_t *Curl_psl_use(struct Curl_easy *easy);
+void Curl_psl_release(struct Curl_easy *easy);
+void Curl_psl_destroy(struct PslCache *pslcache);
+
+#else
+
+#define Curl_psl_use(easy) NULL
+#define Curl_psl_release(easy)
+#define Curl_psl_destroy(pslcache)
+
+#endif /* USE_LIBPSL */
+#endif /* HEADER_PSL_H */
diff --git a/libs/libcurl/src/quic.h b/libs/libcurl/src/quic.h index b35774735e..f281bb7f72 100644 --- a/libs/libcurl/src/quic.h +++ b/libs/libcurl/src/quic.h @@ -1,68 +1,68 @@ -#ifndef HEADER_CURL_QUIC_H -#define HEADER_CURL_QUIC_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 ENABLE_QUIC -#ifdef USE_NGTCP2 -#include "vquic/ngtcp2.h" -#endif -#ifdef USE_QUICHE -#include "vquic/quiche.h" -#endif -#ifdef USE_MSH3 -#include "vquic/msh3.h" -#endif - -#include "urldata.h" - -/* functions provided by the specific backends */ -CURLcode Curl_quic_connect(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t sockfd, - int sockindex, - const struct sockaddr *addr, - socklen_t addrlen); -CURLcode Curl_quic_is_connected(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool *connected); -void Curl_quic_ver(char *p, size_t len); -CURLcode Curl_quic_done_sending(struct Curl_easy *data); -void Curl_quic_done(struct Curl_easy *data, bool premature); -bool Curl_quic_data_pending(const struct Curl_easy *data); -void Curl_quic_disconnect(struct Curl_easy *data, - struct connectdata *conn, int tempindex); -CURLcode Curl_quic_idle(struct Curl_easy *data); - -#else /* ENABLE_QUIC */ -#define Curl_quic_done_sending(x) -#define Curl_quic_done(x,y) -#define Curl_quic_data_pending(x) -#define Curl_quic_disconnect(x,y,z) -#endif /* !ENABLE_QUIC */ - -#endif /* HEADER_CURL_QUIC_H */ +#ifndef HEADER_CURL_QUIC_H
+#define HEADER_CURL_QUIC_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 ENABLE_QUIC
+#ifdef USE_NGTCP2
+#include "vquic/ngtcp2.h"
+#endif
+#ifdef USE_QUICHE
+#include "vquic/quiche.h"
+#endif
+#ifdef USE_MSH3
+#include "vquic/msh3.h"
+#endif
+
+#include "urldata.h"
+
+/* functions provided by the specific backends */
+CURLcode Curl_quic_connect(struct Curl_easy *data,
+ struct connectdata *conn,
+ curl_socket_t sockfd,
+ int sockindex,
+ const struct sockaddr *addr,
+ socklen_t addrlen);
+CURLcode Curl_quic_is_connected(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ bool *connected);
+void Curl_quic_ver(char *p, size_t len);
+CURLcode Curl_quic_done_sending(struct Curl_easy *data);
+void Curl_quic_done(struct Curl_easy *data, bool premature);
+bool Curl_quic_data_pending(const struct Curl_easy *data);
+void Curl_quic_disconnect(struct Curl_easy *data,
+ struct connectdata *conn, int tempindex);
+CURLcode Curl_quic_idle(struct Curl_easy *data);
+
+#else /* ENABLE_QUIC */
+#define Curl_quic_done_sending(x)
+#define Curl_quic_done(x,y)
+#define Curl_quic_data_pending(x)
+#define Curl_quic_disconnect(x,y,z)
+#endif /* !ENABLE_QUIC */
+
+#endif /* HEADER_CURL_QUIC_H */
diff --git a/libs/libcurl/src/rand.c b/libs/libcurl/src/rand.c index 2e7e7e8238..0931badbe7 100644 --- a/libs/libcurl/src/rand.c +++ b/libs/libcurl/src/rand.c @@ -1,255 +1,259 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_FCNTL_H -#include <fcntl.h> -#endif - -#include <curl/curl.h> -#include "vtls/vtls.h" -#include "sendf.h" -#include "rand.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#ifdef WIN32 - -#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) -# define HAVE_MINGW_ORIGINAL -#endif - -#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 && \ - !defined(HAVE_MINGW_ORIGINAL) -# define HAVE_WIN_BCRYPTGENRANDOM -# include <bcrypt.h> -# ifdef _MSC_VER -# pragma comment(lib, "bcrypt.lib") -# endif -# ifndef BCRYPT_USE_SYSTEM_PREFERRED_RNG -# define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002 -# endif -# ifndef STATUS_SUCCESS -# define STATUS_SUCCESS ((NTSTATUS)0x00000000L) -# endif -#elif defined(USE_WIN32_CRYPTO) -# include <wincrypt.h> -# ifdef _MSC_VER -# pragma comment(lib, "advapi32.lib") -# endif -#endif - -CURLcode Curl_win32_random(unsigned char *entropy, size_t length) -{ - memset(entropy, 0, length); - -#if defined(HAVE_WIN_BCRYPTGENRANDOM) - if(BCryptGenRandom(NULL, entropy, (ULONG)length, - BCRYPT_USE_SYSTEM_PREFERRED_RNG) != STATUS_SUCCESS) - return CURLE_FAILED_INIT; - - return CURLE_OK; -#elif defined(USE_WIN32_CRYPTO) - { - HCRYPTPROV hCryptProv = 0; - - if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) - return CURLE_FAILED_INIT; - - if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) { - CryptReleaseContext(hCryptProv, 0UL); - return CURLE_FAILED_INIT; - } - - CryptReleaseContext(hCryptProv, 0UL); - } - return CURLE_OK; -#else - return CURLE_NOT_BUILT_IN; -#endif -} -#endif - -static CURLcode randit(struct Curl_easy *data, unsigned int *rnd) -{ - unsigned int r; - CURLcode result = CURLE_OK; - static unsigned int randseed; - static bool seeded = FALSE; - -#ifdef CURLDEBUG - char *force_entropy = getenv("CURL_ENTROPY"); - if(force_entropy) { - if(!seeded) { - unsigned int seed = 0; - size_t elen = strlen(force_entropy); - size_t clen = sizeof(seed); - size_t min = elen < clen ? elen : clen; - memcpy((char *)&seed, force_entropy, min); - randseed = ntohl(seed); - seeded = TRUE; - } - else - randseed++; - *rnd = randseed; - return CURLE_OK; - } -#endif - - /* data may be NULL! */ - result = Curl_ssl_random(data, (unsigned char *)rnd, sizeof(*rnd)); - if(result != CURLE_NOT_BUILT_IN) - /* only if there is no random function in the TLS backend do the non crypto - version, otherwise return result */ - return result; - - /* ---- non-cryptographic version following ---- */ - -#ifdef WIN32 - if(!seeded) { - result = Curl_win32_random((unsigned char *)rnd, sizeof(*rnd)); - if(result != CURLE_NOT_BUILT_IN) - return result; - } -#endif - -#if defined(RANDOM_FILE) && !defined(WIN32) - if(!seeded) { - /* if there's a random file to read a seed from, use it */ - int fd = open(RANDOM_FILE, O_RDONLY); - if(fd > -1) { - /* read random data into the randseed variable */ - ssize_t nread = read(fd, &randseed, sizeof(randseed)); - if(nread == sizeof(randseed)) - seeded = TRUE; - close(fd); - } - } -#endif - - if(!seeded) { - struct curltime now = Curl_now(); - infof(data, "WARNING: using weak random seed"); - randseed += (unsigned int)now.tv_usec + (unsigned int)now.tv_sec; - randseed = randseed * 1103515245 + 12345; - randseed = randseed * 1103515245 + 12345; - randseed = randseed * 1103515245 + 12345; - seeded = TRUE; - } - - /* Return an unsigned 32-bit pseudo-random number. */ - r = randseed = randseed * 1103515245 + 12345; - *rnd = (r << 16) | ((r >> 16) & 0xFFFF); - return CURLE_OK; -} - -/* - * Curl_rand() stores 'num' number of random unsigned integers in the buffer - * 'rndptr' points to. - * - * If libcurl is built without TLS support or with a TLS backend that lacks a - * proper random API (rustls, Gskit or mbedTLS), this function will use "weak" - * random. - * - * When built *with* TLS support and a backend that offers strong random, it - * will return error if it cannot provide strong random values. - * - * NOTE: 'data' may be passed in as NULL when coming from external API without - * easy handle! - * - */ - -CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num) -{ - CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT; - - DEBUGASSERT(num > 0); - - while(num) { - unsigned int r; - size_t left = num < sizeof(unsigned int) ? num : sizeof(unsigned int); - - result = randit(data, &r); - if(result) - return result; - - while(left) { - *rnd++ = (unsigned char)(r & 0xFF); - r >>= 8; - --num; - --left; - } - } - - return result; -} - -/* - * Curl_rand_hex() fills the 'rnd' buffer with a given 'num' size with random - * hexadecimal digits PLUS a null-terminating byte. It must be an odd number - * size. - */ - -CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd, - size_t num) -{ - CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT; - const char *hex = "0123456789abcdef"; - unsigned char buffer[128]; - unsigned char *bufp = buffer; - DEBUGASSERT(num > 1); - -#ifdef __clang_analyzer__ - /* This silences a scan-build warning about accessing this buffer with - uninitialized memory. */ - memset(buffer, 0, sizeof(buffer)); -#endif - - if((num/2 >= sizeof(buffer)) || !(num&1)) - /* make sure it fits in the local buffer and that it is an odd number! */ - return CURLE_BAD_FUNCTION_ARGUMENT; - - num--; /* save one for null-termination */ - - result = Curl_rand(data, buffer, num/2); - if(result) - return result; - - while(num) { - /* clang-tidy warns on this line without this comment: */ - /* NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult) */ - *rnd++ = hex[(*bufp & 0xF0)>>4]; - *rnd++ = hex[*bufp & 0x0F]; - bufp++; - num -= 2; - } - *rnd = 0; - - return result; -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include <curl/curl.h>
+#include "vtls/vtls.h"
+#include "sendf.h"
+#include "timeval.h"
+#include "rand.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#ifdef WIN32
+
+#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
+# define HAVE_MINGW_ORIGINAL
+#endif
+
+#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 && \
+ !defined(HAVE_MINGW_ORIGINAL)
+# define HAVE_WIN_BCRYPTGENRANDOM
+# include <bcrypt.h>
+# ifdef _MSC_VER
+# pragma comment(lib, "bcrypt.lib")
+# endif
+# ifndef BCRYPT_USE_SYSTEM_PREFERRED_RNG
+# define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002
+# endif
+# ifndef STATUS_SUCCESS
+# define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
+# endif
+#elif defined(USE_WIN32_CRYPTO)
+# include <wincrypt.h>
+# ifdef _MSC_VER
+# pragma comment(lib, "advapi32.lib")
+# endif
+#endif
+
+CURLcode Curl_win32_random(unsigned char *entropy, size_t length)
+{
+ memset(entropy, 0, length);
+
+#if defined(HAVE_WIN_BCRYPTGENRANDOM)
+ if(BCryptGenRandom(NULL, entropy, (ULONG)length,
+ BCRYPT_USE_SYSTEM_PREFERRED_RNG) != STATUS_SUCCESS)
+ return CURLE_FAILED_INIT;
+
+ return CURLE_OK;
+#elif defined(USE_WIN32_CRYPTO)
+ {
+ HCRYPTPROV hCryptProv = 0;
+
+ if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
+ return CURLE_FAILED_INIT;
+
+ if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) {
+ CryptReleaseContext(hCryptProv, 0UL);
+ return CURLE_FAILED_INIT;
+ }
+
+ CryptReleaseContext(hCryptProv, 0UL);
+ }
+ return CURLE_OK;
+#else
+ return CURLE_NOT_BUILT_IN;
+#endif
+}
+#endif
+
+static CURLcode randit(struct Curl_easy *data, unsigned int *rnd)
+{
+ unsigned int r;
+ CURLcode result = CURLE_OK;
+ static unsigned int randseed;
+ static bool seeded = FALSE;
+
+#ifdef CURLDEBUG
+ char *force_entropy = getenv("CURL_ENTROPY");
+ if(force_entropy) {
+ if(!seeded) {
+ unsigned int seed = 0;
+ size_t elen = strlen(force_entropy);
+ size_t clen = sizeof(seed);
+ size_t min = elen < clen ? elen : clen;
+ memcpy((char *)&seed, force_entropy, min);
+ randseed = ntohl(seed);
+ seeded = TRUE;
+ }
+ else
+ randseed++;
+ *rnd = randseed;
+ return CURLE_OK;
+ }
+#endif
+
+ /* data may be NULL! */
+ result = Curl_ssl_random(data, (unsigned char *)rnd, sizeof(*rnd));
+ if(result != CURLE_NOT_BUILT_IN)
+ /* only if there is no random function in the TLS backend do the non crypto
+ version, otherwise return result */
+ return result;
+
+ /* ---- non-cryptographic version following ---- */
+
+#ifdef WIN32
+ if(!seeded) {
+ result = Curl_win32_random((unsigned char *)rnd, sizeof(*rnd));
+ if(result != CURLE_NOT_BUILT_IN)
+ return result;
+ }
+#endif
+
+#if defined(RANDOM_FILE) && !defined(WIN32)
+ if(!seeded) {
+ /* if there's a random file to read a seed from, use it */
+ int fd = open(RANDOM_FILE, O_RDONLY);
+ if(fd > -1) {
+ /* read random data into the randseed variable */
+ ssize_t nread = read(fd, &randseed, sizeof(randseed));
+ if(nread == sizeof(randseed))
+ seeded = TRUE;
+ close(fd);
+ }
+ }
+#endif
+
+ if(!seeded) {
+ struct curltime now = Curl_now();
+ infof(data, "WARNING: using weak random seed");
+ randseed += (unsigned int)now.tv_usec + (unsigned int)now.tv_sec;
+ randseed = randseed * 1103515245 + 12345;
+ randseed = randseed * 1103515245 + 12345;
+ randseed = randseed * 1103515245 + 12345;
+ seeded = TRUE;
+ }
+
+ /* Return an unsigned 32-bit pseudo-random number. */
+ r = randseed = randseed * 1103515245 + 12345;
+ *rnd = (r << 16) | ((r >> 16) & 0xFFFF);
+ return CURLE_OK;
+}
+
+/*
+ * Curl_rand() stores 'num' number of random unsigned integers in the buffer
+ * 'rndptr' points to.
+ *
+ * If libcurl is built without TLS support or with a TLS backend that lacks a
+ * proper random API (rustls, Gskit or mbedTLS), this function will use "weak"
+ * random.
+ *
+ * When built *with* TLS support and a backend that offers strong random, it
+ * will return error if it cannot provide strong random values.
+ *
+ * NOTE: 'data' may be passed in as NULL when coming from external API without
+ * easy handle!
+ *
+ */
+
+CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num)
+{
+ CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT;
+
+ DEBUGASSERT(num > 0);
+
+ while(num) {
+ unsigned int r;
+ size_t left = num < sizeof(unsigned int) ? num : sizeof(unsigned int);
+
+ result = randit(data, &r);
+ if(result)
+ return result;
+
+ while(left) {
+ *rnd++ = (unsigned char)(r & 0xFF);
+ r >>= 8;
+ --num;
+ --left;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Curl_rand_hex() fills the 'rnd' buffer with a given 'num' size with random
+ * hexadecimal digits PLUS a null-terminating byte. It must be an odd number
+ * size.
+ */
+
+CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd,
+ size_t num)
+{
+ CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT;
+ const char *hex = "0123456789abcdef";
+ unsigned char buffer[128];
+ unsigned char *bufp = buffer;
+ DEBUGASSERT(num > 1);
+
+#ifdef __clang_analyzer__
+ /* This silences a scan-build warning about accessing this buffer with
+ uninitialized memory. */
+ memset(buffer, 0, sizeof(buffer));
+#endif
+
+ if((num/2 >= sizeof(buffer)) || !(num&1))
+ /* make sure it fits in the local buffer and that it is an odd number! */
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ num--; /* save one for null-termination */
+
+ result = Curl_rand(data, buffer, num/2);
+ if(result)
+ return result;
+
+ while(num) {
+ /* clang-tidy warns on this line without this comment: */
+ /* NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult) */
+ *rnd++ = hex[(*bufp & 0xF0)>>4];
+ *rnd++ = hex[*bufp & 0x0F];
+ bufp++;
+ num -= 2;
+ }
+ *rnd = 0;
+
+ return result;
+}
diff --git a/libs/libcurl/src/rand.h b/libs/libcurl/src/rand.h index 30fc29615a..5d1256a268 100644 --- a/libs/libcurl/src/rand.h +++ b/libs/libcurl/src/rand.h @@ -1,57 +1,57 @@ -#ifndef HEADER_CURL_RAND_H -#define HEADER_CURL_RAND_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - -/* - * Curl_rand() stores 'num' number of random unsigned characters in the buffer - * 'rnd' points to. - * - * If libcurl is built without TLS support or with a TLS backend that lacks a - * proper random API (Gskit or mbedTLS), this function will use "weak" random. - * - * When built *with* TLS support and a backend that offers strong random, it - * will return error if it cannot provide strong random values. - * - * NOTE: 'data' may be passed in as NULL when coming from external API without - * easy handle! - * - */ -CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num); - -/* - * Curl_rand_hex() fills the 'rnd' buffer with a given 'num' size with random - * hexadecimal digits PLUS a null-terminating byte. It must be an odd number - * size. - */ -CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd, - size_t num); - -#ifdef WIN32 -/* Random generator shared between the Schannel vtls and Curl_rand*() - functions */ -CURLcode Curl_win32_random(unsigned char *entropy, size_t length); -#endif - -#endif /* HEADER_CURL_RAND_H */ +#ifndef HEADER_CURL_RAND_H
+#define HEADER_CURL_RAND_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+/*
+ * Curl_rand() stores 'num' number of random unsigned characters in the buffer
+ * 'rnd' points to.
+ *
+ * If libcurl is built without TLS support or with a TLS backend that lacks a
+ * proper random API (Gskit or mbedTLS), this function will use "weak" random.
+ *
+ * When built *with* TLS support and a backend that offers strong random, it
+ * will return error if it cannot provide strong random values.
+ *
+ * NOTE: 'data' may be passed in as NULL when coming from external API without
+ * easy handle!
+ *
+ */
+CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num);
+
+/*
+ * Curl_rand_hex() fills the 'rnd' buffer with a given 'num' size with random
+ * hexadecimal digits PLUS a null-terminating byte. It must be an odd number
+ * size.
+ */
+CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd,
+ size_t num);
+
+#ifdef WIN32
+/* Random generator shared between the Schannel vtls and Curl_rand*()
+ functions */
+CURLcode Curl_win32_random(unsigned char *entropy, size_t length);
+#endif
+
+#endif /* HEADER_CURL_RAND_H */
diff --git a/libs/libcurl/src/rename.c b/libs/libcurl/src/rename.c index cfb3699fb7..f76d301658 100644 --- a/libs/libcurl/src/rename.c +++ b/libs/libcurl/src/rename.c @@ -1,73 +1,73 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2020 - 2022, 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 "rename.h" - -#include "curl_setup.h" - -#if (!defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_COOKIES)) || \ - !defined(CURL_DISABLE_ALTSVC) - -#include "curl_multibyte.h" -#include "timeval.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* return 0 on success, 1 on error */ -int Curl_rename(const char *oldpath, const char *newpath) -{ -#ifdef WIN32 - /* rename() on Windows doesn't overwrite, so we can't use it here. - MoveFileEx() will overwrite and is usually atomic, however it fails - when there are open handles to the file. */ - const int max_wait_ms = 1000; - struct curltime start = Curl_now(); - TCHAR *tchar_oldpath = curlx_convert_UTF8_to_tchar((char *)oldpath); - TCHAR *tchar_newpath = curlx_convert_UTF8_to_tchar((char *)newpath); - for(;;) { - timediff_t diff; - if(MoveFileEx(tchar_oldpath, tchar_newpath, MOVEFILE_REPLACE_EXISTING)) { - curlx_unicodefree(tchar_oldpath); - curlx_unicodefree(tchar_newpath); - break; - } - diff = Curl_timediff(Curl_now(), start); - if(diff < 0 || diff > max_wait_ms) { - curlx_unicodefree(tchar_oldpath); - curlx_unicodefree(tchar_newpath); - return 1; - } - Sleep(1); - } -#else - if(rename(oldpath, newpath)) - return 1; -#endif - return 0; -} - -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2020 - 2022, 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 "rename.h"
+
+#include "curl_setup.h"
+
+#if (!defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_COOKIES)) || \
+ !defined(CURL_DISABLE_ALTSVC)
+
+#include "curl_multibyte.h"
+#include "timeval.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* return 0 on success, 1 on error */
+int Curl_rename(const char *oldpath, const char *newpath)
+{
+#ifdef WIN32
+ /* rename() on Windows doesn't overwrite, so we can't use it here.
+ MoveFileEx() will overwrite and is usually atomic, however it fails
+ when there are open handles to the file. */
+ const int max_wait_ms = 1000;
+ struct curltime start = Curl_now();
+ TCHAR *tchar_oldpath = curlx_convert_UTF8_to_tchar((char *)oldpath);
+ TCHAR *tchar_newpath = curlx_convert_UTF8_to_tchar((char *)newpath);
+ for(;;) {
+ timediff_t diff;
+ if(MoveFileEx(tchar_oldpath, tchar_newpath, MOVEFILE_REPLACE_EXISTING)) {
+ curlx_unicodefree(tchar_oldpath);
+ curlx_unicodefree(tchar_newpath);
+ break;
+ }
+ diff = Curl_timediff(Curl_now(), start);
+ if(diff < 0 || diff > max_wait_ms) {
+ curlx_unicodefree(tchar_oldpath);
+ curlx_unicodefree(tchar_newpath);
+ return 1;
+ }
+ Sleep(1);
+ }
+#else
+ if(rename(oldpath, newpath))
+ return 1;
+#endif
+ return 0;
+}
+
+#endif
diff --git a/libs/libcurl/src/rename.h b/libs/libcurl/src/rename.h index 9958e2cd23..b3efa089b5 100644 --- a/libs/libcurl/src/rename.h +++ b/libs/libcurl/src/rename.h @@ -1,29 +1,29 @@ -#ifndef HEADER_CURL_RENAME_H -#define HEADER_CURL_RENAME_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2020 - 2022, 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 - * - ***************************************************************************/ - -int Curl_rename(const char *oldpath, const char *newpath); - -#endif /* HEADER_CURL_RENAME_H */ +#ifndef HEADER_CURL_RENAME_H
+#define HEADER_CURL_RENAME_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2020 - 2022, 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
+ *
+ ***************************************************************************/
+
+int Curl_rename(const char *oldpath, const char *newpath);
+
+#endif /* HEADER_CURL_RENAME_H */
diff --git a/libs/libcurl/src/rtsp.c b/libs/libcurl/src/rtsp.c index 6d3bf97e6c..27f26fe298 100644 --- a/libs/libcurl/src/rtsp.c +++ b/libs/libcurl/src/rtsp.c @@ -1,842 +1,854 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_RTSP) && !defined(USE_HYPER) - -#include "urldata.h" -#include <curl/curl.h> -#include "transfer.h" -#include "sendf.h" -#include "multiif.h" -#include "http.h" -#include "url.h" -#include "progress.h" -#include "rtsp.h" -#include "strcase.h" -#include "select.h" -#include "connect.h" -#include "strdup.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#define RTP_PKT_CHANNEL(p) ((int)((unsigned char)((p)[1]))) - -#define RTP_PKT_LENGTH(p) ((((int)((unsigned char)((p)[2]))) << 8) | \ - ((int)((unsigned char)((p)[3])))) - -/* protocol-specific functions set up to be called by the main engine */ -static CURLcode rtsp_do(struct Curl_easy *data, bool *done); -static CURLcode rtsp_done(struct Curl_easy *data, CURLcode, bool premature); -static CURLcode rtsp_connect(struct Curl_easy *data, bool *done); -static CURLcode rtsp_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead); -static int rtsp_getsock_do(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks); - -/* - * Parse and write out any available RTP data. - * - * nread: amount of data left after k->str. will be modified if RTP - * data is parsed and k->str is moved up - * readmore: whether or not the RTP parser needs more data right away - */ -static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data, - struct connectdata *conn, - ssize_t *nread, - bool *readmore); - -static CURLcode rtsp_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static unsigned int rtsp_conncheck(struct Curl_easy *data, - struct connectdata *check, - unsigned int checks_to_perform); - -/* this returns the socket to wait for in the DO and DOING state for the multi - interface and then we're always _sending_ a request and thus we wait for - the single socket to become writable only */ -static int rtsp_getsock_do(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t *socks) -{ - /* write mode */ - (void)data; - socks[0] = conn->sock[FIRSTSOCKET]; - return GETSOCK_WRITESOCK(0); -} - -static -CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len); - - -/* - * RTSP handler interface. - */ -const struct Curl_handler Curl_handler_rtsp = { - "RTSP", /* scheme */ - rtsp_setup_connection, /* setup_connection */ - rtsp_do, /* do_it */ - rtsp_done, /* done */ - ZERO_NULL, /* do_more */ - rtsp_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - rtsp_getsock_do, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - rtsp_disconnect, /* disconnect */ - rtsp_rtp_readwrite, /* readwrite */ - rtsp_conncheck, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_RTSP, /* defport */ - CURLPROTO_RTSP, /* protocol */ - CURLPROTO_RTSP, /* family */ - PROTOPT_NONE /* flags */ -}; - - -static CURLcode rtsp_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - struct RTSP *rtsp; - (void)conn; - - data->req.p.rtsp = rtsp = calloc(1, sizeof(struct RTSP)); - if(!rtsp) - return CURLE_OUT_OF_MEMORY; - - return CURLE_OK; -} - - -/* - * The server may send us RTP data at any point, and RTSPREQ_RECEIVE does not - * want to block the application forever while receiving a stream. Therefore, - * we cannot assume that an RTSP socket is dead just because it is readable. - * - * Instead, if it is readable, run Curl_connalive() to peek at the socket - * and distinguish between closed and data. - */ -static bool rtsp_connisdead(struct connectdata *check) -{ - int sval; - bool ret_val = TRUE; - - sval = SOCKET_READABLE(check->sock[FIRSTSOCKET], 0); - if(sval == 0) { - /* timeout */ - ret_val = FALSE; - } - else if(sval & CURL_CSELECT_ERR) { - /* socket is in an error state */ - ret_val = TRUE; - } - else if(sval & CURL_CSELECT_IN) { - /* readable with no error. could still be closed */ - ret_val = !Curl_connalive(check); - } - - return ret_val; -} - -/* - * Function to check on various aspects of a connection. - */ -static unsigned int rtsp_conncheck(struct Curl_easy *data, - struct connectdata *conn, - unsigned int checks_to_perform) -{ - unsigned int ret_val = CONNRESULT_NONE; - (void)data; - - if(checks_to_perform & CONNCHECK_ISDEAD) { - if(rtsp_connisdead(conn)) - ret_val |= CONNRESULT_DEAD; - } - - return ret_val; -} - - -static CURLcode rtsp_connect(struct Curl_easy *data, bool *done) -{ - CURLcode httpStatus; - - httpStatus = Curl_http_connect(data, done); - - /* Initialize the CSeq if not already done */ - if(data->state.rtsp_next_client_CSeq == 0) - data->state.rtsp_next_client_CSeq = 1; - if(data->state.rtsp_next_server_CSeq == 0) - data->state.rtsp_next_server_CSeq = 1; - - data->conn->proto.rtspc.rtp_channel = -1; - - return httpStatus; -} - -static CURLcode rtsp_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead) -{ - (void) dead; - (void) data; - Curl_safefree(conn->proto.rtspc.rtp_buf); - return CURLE_OK; -} - - -static CURLcode rtsp_done(struct Curl_easy *data, - CURLcode status, bool premature) -{ - struct RTSP *rtsp = data->req.p.rtsp; - CURLcode httpStatus; - - /* Bypass HTTP empty-reply checks on receive */ - if(data->set.rtspreq == RTSPREQ_RECEIVE) - premature = TRUE; - - httpStatus = Curl_http_done(data, status, premature); - - if(rtsp && !status && !httpStatus) { - /* Check the sequence numbers */ - long CSeq_sent = rtsp->CSeq_sent; - long CSeq_recv = rtsp->CSeq_recv; - if((data->set.rtspreq != RTSPREQ_RECEIVE) && (CSeq_sent != CSeq_recv)) { - failf(data, - "The CSeq of this request %ld did not match the response %ld", - CSeq_sent, CSeq_recv); - return CURLE_RTSP_CSEQ_ERROR; - } - if(data->set.rtspreq == RTSPREQ_RECEIVE && - (data->conn->proto.rtspc.rtp_channel == -1)) { - infof(data, "Got an RTP Receive with a CSeq of %ld", CSeq_recv); - } - } - - return httpStatus; -} - -static CURLcode rtsp_do(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - CURLcode result = CURLE_OK; - Curl_RtspReq rtspreq = data->set.rtspreq; - struct RTSP *rtsp = data->req.p.rtsp; - struct dynbuf req_buffer; - curl_off_t postsize = 0; /* for ANNOUNCE and SET_PARAMETER */ - curl_off_t putsize = 0; /* for ANNOUNCE and SET_PARAMETER */ - - const char *p_request = NULL; - const char *p_session_id = NULL; - const char *p_accept = NULL; - const char *p_accept_encoding = NULL; - const char *p_range = NULL; - const char *p_referrer = NULL; - const char *p_stream_uri = NULL; - const char *p_transport = NULL; - const char *p_uagent = NULL; - const char *p_proxyuserpwd = NULL; - const char *p_userpwd = NULL; - - *done = TRUE; - - rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq; - rtsp->CSeq_recv = 0; - - /* Setup the 'p_request' pointer to the proper p_request string - * Since all RTSP requests are included here, there is no need to - * support custom requests like HTTP. - **/ - data->set.opt_no_body = TRUE; /* most requests don't contain a body */ - switch(rtspreq) { - default: - failf(data, "Got invalid RTSP request"); - return CURLE_BAD_FUNCTION_ARGUMENT; - case RTSPREQ_OPTIONS: - p_request = "OPTIONS"; - break; - case RTSPREQ_DESCRIBE: - p_request = "DESCRIBE"; - data->set.opt_no_body = FALSE; - break; - case RTSPREQ_ANNOUNCE: - p_request = "ANNOUNCE"; - break; - case RTSPREQ_SETUP: - p_request = "SETUP"; - break; - case RTSPREQ_PLAY: - p_request = "PLAY"; - break; - case RTSPREQ_PAUSE: - p_request = "PAUSE"; - break; - case RTSPREQ_TEARDOWN: - p_request = "TEARDOWN"; - break; - case RTSPREQ_GET_PARAMETER: - /* GET_PARAMETER's no_body status is determined later */ - p_request = "GET_PARAMETER"; - data->set.opt_no_body = FALSE; - break; - case RTSPREQ_SET_PARAMETER: - p_request = "SET_PARAMETER"; - break; - case RTSPREQ_RECORD: - p_request = "RECORD"; - break; - case RTSPREQ_RECEIVE: - p_request = ""; - /* Treat interleaved RTP as body*/ - data->set.opt_no_body = FALSE; - break; - case RTSPREQ_LAST: - failf(data, "Got invalid RTSP request: RTSPREQ_LAST"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - if(rtspreq == RTSPREQ_RECEIVE) { - Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1); - - return result; - } - - p_session_id = data->set.str[STRING_RTSP_SESSION_ID]; - if(!p_session_id && - (rtspreq & ~(RTSPREQ_OPTIONS | RTSPREQ_DESCRIBE | RTSPREQ_SETUP))) { - failf(data, "Refusing to issue an RTSP request [%s] without a session ID.", - p_request); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - /* Stream URI. Default to server '*' if not specified */ - if(data->set.str[STRING_RTSP_STREAM_URI]) { - p_stream_uri = data->set.str[STRING_RTSP_STREAM_URI]; - } - else { - p_stream_uri = "*"; - } - - /* Transport Header for SETUP requests */ - p_transport = Curl_checkheaders(data, STRCONST("Transport")); - if(rtspreq == RTSPREQ_SETUP && !p_transport) { - /* New Transport: setting? */ - if(data->set.str[STRING_RTSP_TRANSPORT]) { - Curl_safefree(data->state.aptr.rtsp_transport); - - data->state.aptr.rtsp_transport = - aprintf("Transport: %s\r\n", - data->set.str[STRING_RTSP_TRANSPORT]); - if(!data->state.aptr.rtsp_transport) - return CURLE_OUT_OF_MEMORY; - } - else { - failf(data, - "Refusing to issue an RTSP SETUP without a Transport: header."); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - p_transport = data->state.aptr.rtsp_transport; - } - - /* Accept Headers for DESCRIBE requests */ - if(rtspreq == RTSPREQ_DESCRIBE) { - /* Accept Header */ - p_accept = Curl_checkheaders(data, STRCONST("Accept"))? - NULL:"Accept: application/sdp\r\n"; - - /* Accept-Encoding header */ - if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && - data->set.str[STRING_ENCODING]) { - Curl_safefree(data->state.aptr.accept_encoding); - data->state.aptr.accept_encoding = - aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]); - - if(!data->state.aptr.accept_encoding) - return CURLE_OUT_OF_MEMORY; - - p_accept_encoding = data->state.aptr.accept_encoding; - } - } - - /* The User-Agent string might have been allocated in url.c already, because - it might have been used in the proxy connect, but if we have got a header - with the user-agent string specified, we erase the previously made string - here. */ - if(Curl_checkheaders(data, STRCONST("User-Agent")) && - data->state.aptr.uagent) { - Curl_safefree(data->state.aptr.uagent); - data->state.aptr.uagent = NULL; - } - else if(!Curl_checkheaders(data, STRCONST("User-Agent")) && - data->set.str[STRING_USERAGENT]) { - p_uagent = data->state.aptr.uagent; - } - - /* setup the authentication headers */ - result = Curl_http_output_auth(data, conn, p_request, HTTPREQ_GET, - p_stream_uri, FALSE); - if(result) - return result; - - p_proxyuserpwd = data->state.aptr.proxyuserpwd; - p_userpwd = data->state.aptr.userpwd; - - /* Referrer */ - Curl_safefree(data->state.aptr.ref); - if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) - data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer); - else - data->state.aptr.ref = NULL; - - p_referrer = data->state.aptr.ref; - - /* - * Range Header - * Only applies to PLAY, PAUSE, RECORD - * - * Go ahead and use the Range stuff supplied for HTTP - */ - if(data->state.use_range && - (rtspreq & (RTSPREQ_PLAY | RTSPREQ_PAUSE | RTSPREQ_RECORD))) { - - /* Check to see if there is a range set in the custom headers */ - if(!Curl_checkheaders(data, STRCONST("Range")) && data->state.range) { - Curl_safefree(data->state.aptr.rangeline); - data->state.aptr.rangeline = aprintf("Range: %s\r\n", data->state.range); - p_range = data->state.aptr.rangeline; - } - } - - /* - * Sanity check the custom headers - */ - if(Curl_checkheaders(data, STRCONST("CSeq"))) { - failf(data, "CSeq cannot be set as a custom header."); - return CURLE_RTSP_CSEQ_ERROR; - } - if(Curl_checkheaders(data, STRCONST("Session"))) { - failf(data, "Session ID cannot be set as a custom header."); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - /* Initialize a dynamic send buffer */ - Curl_dyn_init(&req_buffer, DYN_RTSP_REQ_HEADER); - - result = - Curl_dyn_addf(&req_buffer, - "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */ - "CSeq: %ld\r\n", /* CSeq */ - p_request, p_stream_uri, rtsp->CSeq_sent); - if(result) - return result; - - /* - * Rather than do a normal alloc line, keep the session_id unformatted - * to make comparison easier - */ - if(p_session_id) { - result = Curl_dyn_addf(&req_buffer, "Session: %s\r\n", p_session_id); - if(result) - return result; - } - - /* - * Shared HTTP-like options - */ - result = Curl_dyn_addf(&req_buffer, - "%s" /* transport */ - "%s" /* accept */ - "%s" /* accept-encoding */ - "%s" /* range */ - "%s" /* referrer */ - "%s" /* user-agent */ - "%s" /* proxyuserpwd */ - "%s" /* userpwd */ - , - p_transport ? p_transport : "", - p_accept ? p_accept : "", - p_accept_encoding ? p_accept_encoding : "", - p_range ? p_range : "", - p_referrer ? p_referrer : "", - p_uagent ? p_uagent : "", - p_proxyuserpwd ? p_proxyuserpwd : "", - p_userpwd ? p_userpwd : ""); - - /* - * Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM - * with basic and digest, it will be freed anyway by the next request - */ - Curl_safefree(data->state.aptr.userpwd); - data->state.aptr.userpwd = NULL; - - if(result) - return result; - - if((rtspreq == RTSPREQ_SETUP) || (rtspreq == RTSPREQ_DESCRIBE)) { - result = Curl_add_timecondition(data, &req_buffer); - if(result) - return result; - } - - result = Curl_add_custom_headers(data, FALSE, &req_buffer); - if(result) - return result; - - if(rtspreq == RTSPREQ_ANNOUNCE || - rtspreq == RTSPREQ_SET_PARAMETER || - rtspreq == RTSPREQ_GET_PARAMETER) { - - if(data->set.upload) { - putsize = data->state.infilesize; - data->state.httpreq = HTTPREQ_PUT; - - } - else { - postsize = (data->state.infilesize != -1)? - data->state.infilesize: - (data->set.postfields? (curl_off_t)strlen(data->set.postfields):0); - data->state.httpreq = HTTPREQ_POST; - } - - if(putsize > 0 || postsize > 0) { - /* As stated in the http comments, it is probably not wise to - * actually set a custom Content-Length in the headers */ - if(!Curl_checkheaders(data, STRCONST("Content-Length"))) { - result = - Curl_dyn_addf(&req_buffer, - "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n", - (data->set.upload ? putsize : postsize)); - if(result) - return result; - } - - if(rtspreq == RTSPREQ_SET_PARAMETER || - rtspreq == RTSPREQ_GET_PARAMETER) { - if(!Curl_checkheaders(data, STRCONST("Content-Type"))) { - result = Curl_dyn_addn(&req_buffer, - STRCONST("Content-Type: " - "text/parameters\r\n")); - if(result) - return result; - } - } - - if(rtspreq == RTSPREQ_ANNOUNCE) { - if(!Curl_checkheaders(data, STRCONST("Content-Type"))) { - result = Curl_dyn_addn(&req_buffer, - STRCONST("Content-Type: " - "application/sdp\r\n")); - if(result) - return result; - } - } - - data->state.expect100header = FALSE; /* RTSP posts are simple/small */ - } - else if(rtspreq == RTSPREQ_GET_PARAMETER) { - /* Check for an empty GET_PARAMETER (heartbeat) request */ - data->state.httpreq = HTTPREQ_HEAD; - data->set.opt_no_body = TRUE; - } - } - - /* RTSP never allows chunked transfer */ - data->req.forbidchunk = TRUE; - /* Finish the request buffer */ - result = Curl_dyn_addn(&req_buffer, STRCONST("\r\n")); - if(result) - return result; - - if(postsize > 0) { - result = Curl_dyn_addn(&req_buffer, data->set.postfields, - (size_t)postsize); - if(result) - return result; - } - - /* issue the request */ - result = Curl_buffer_send(&req_buffer, data, - &data->info.request_size, 0, FIRSTSOCKET); - if(result) { - failf(data, "Failed sending RTSP request"); - return result; - } - - Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, putsize?FIRSTSOCKET:-1); - - /* Increment the CSeq on success */ - data->state.rtsp_next_client_CSeq++; - - if(data->req.writebytecount) { - /* if a request-body has been sent off, we make sure this progress is - noted properly */ - Curl_pgrsSetUploadCounter(data, data->req.writebytecount); - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - } - - return result; -} - - -static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data, - struct connectdata *conn, - ssize_t *nread, - bool *readmore) { - struct SingleRequest *k = &data->req; - struct rtsp_conn *rtspc = &(conn->proto.rtspc); - - char *rtp; /* moving pointer to rtp data */ - ssize_t rtp_dataleft; /* how much data left to parse in this round */ - char *scratch; - CURLcode result; - - if(rtspc->rtp_buf) { - /* There was some leftover data the last time. Merge buffers */ - char *newptr = Curl_saferealloc(rtspc->rtp_buf, - rtspc->rtp_bufsize + *nread); - if(!newptr) { - rtspc->rtp_buf = NULL; - rtspc->rtp_bufsize = 0; - return CURLE_OUT_OF_MEMORY; - } - rtspc->rtp_buf = newptr; - memcpy(rtspc->rtp_buf + rtspc->rtp_bufsize, k->str, *nread); - rtspc->rtp_bufsize += *nread; - rtp = rtspc->rtp_buf; - rtp_dataleft = rtspc->rtp_bufsize; - } - else { - /* Just parse the request buffer directly */ - rtp = k->str; - rtp_dataleft = *nread; - } - - while((rtp_dataleft > 0) && - (rtp[0] == '$')) { - if(rtp_dataleft > 4) { - int rtp_length; - - /* Parse the header */ - /* The channel identifier immediately follows and is 1 byte */ - rtspc->rtp_channel = RTP_PKT_CHANNEL(rtp); - - /* The length is two bytes */ - rtp_length = RTP_PKT_LENGTH(rtp); - - if(rtp_dataleft < rtp_length + 4) { - /* Need more - incomplete payload*/ - *readmore = TRUE; - break; - } - /* We have the full RTP interleaved packet - * Write out the header including the leading '$' */ - DEBUGF(infof(data, "RTP write channel %d rtp_length %d", - rtspc->rtp_channel, rtp_length)); - result = rtp_client_write(data, &rtp[0], rtp_length + 4); - if(result) { - failf(data, "Got an error writing an RTP packet"); - *readmore = FALSE; - Curl_safefree(rtspc->rtp_buf); - rtspc->rtp_buf = NULL; - rtspc->rtp_bufsize = 0; - return result; - } - - /* Move forward in the buffer */ - rtp_dataleft -= rtp_length + 4; - rtp += rtp_length + 4; - - if(data->set.rtspreq == RTSPREQ_RECEIVE) { - /* If we are in a passive receive, give control back - * to the app as often as we can. - */ - k->keepon &= ~KEEP_RECV; - } - } - else { - /* Need more - incomplete header */ - *readmore = TRUE; - break; - } - } - - if(rtp_dataleft && rtp[0] == '$') { - DEBUGF(infof(data, "RTP Rewinding %zd %s", rtp_dataleft, - *readmore ? "(READMORE)" : "")); - - /* Store the incomplete RTP packet for a "rewind" */ - scratch = malloc(rtp_dataleft); - if(!scratch) { - Curl_safefree(rtspc->rtp_buf); - rtspc->rtp_buf = NULL; - rtspc->rtp_bufsize = 0; - return CURLE_OUT_OF_MEMORY; - } - memcpy(scratch, rtp, rtp_dataleft); - Curl_safefree(rtspc->rtp_buf); - rtspc->rtp_buf = scratch; - rtspc->rtp_bufsize = rtp_dataleft; - - /* As far as the transfer is concerned, this data is consumed */ - *nread = 0; - return CURLE_OK; - } - /* Fix up k->str to point just after the last RTP packet */ - k->str += *nread - rtp_dataleft; - - /* either all of the data has been read or... - * rtp now points at the next byte to parse - */ - if(rtp_dataleft > 0) - DEBUGASSERT(k->str[0] == rtp[0]); - - DEBUGASSERT(rtp_dataleft <= *nread); /* sanity check */ - - *nread = rtp_dataleft; - - /* If we get here, we have finished with the leftover/merge buffer */ - Curl_safefree(rtspc->rtp_buf); - rtspc->rtp_buf = NULL; - rtspc->rtp_bufsize = 0; - - return CURLE_OK; -} - -static -CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len) -{ - size_t wrote; - curl_write_callback writeit; - void *user_ptr; - - if(len == 0) { - failf(data, "Cannot write a 0 size RTP packet."); - return CURLE_WRITE_ERROR; - } - - /* If the user has configured CURLOPT_INTERLEAVEFUNCTION then use that - function and any configured CURLOPT_INTERLEAVEDATA to write out the RTP - data. Otherwise, use the CURLOPT_WRITEFUNCTION with the CURLOPT_WRITEDATA - pointer to write out the RTP data. */ - if(data->set.fwrite_rtp) { - writeit = data->set.fwrite_rtp; - user_ptr = data->set.rtp_out; - } - else { - writeit = data->set.fwrite_func; - user_ptr = data->set.out; - } - - Curl_set_in_callback(data, true); - wrote = writeit(ptr, 1, len, user_ptr); - Curl_set_in_callback(data, false); - - if(CURL_WRITEFUNC_PAUSE == wrote) { - failf(data, "Cannot pause RTP"); - return CURLE_WRITE_ERROR; - } - - if(wrote != len) { - failf(data, "Failed writing RTP data"); - return CURLE_WRITE_ERROR; - } - - return CURLE_OK; -} - -CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header) -{ - long CSeq = 0; - - if(checkprefix("CSeq:", header)) { - /* Store the received CSeq. Match is verified in rtsp_done */ - int nc = sscanf(&header[4], ": %ld", &CSeq); - if(nc == 1) { - struct RTSP *rtsp = data->req.p.rtsp; - rtsp->CSeq_recv = CSeq; /* mark the request */ - data->state.rtsp_CSeq_recv = CSeq; /* update the handle */ - } - else { - failf(data, "Unable to read the CSeq header: [%s]", header); - return CURLE_RTSP_CSEQ_ERROR; - } - } - else if(checkprefix("Session:", header)) { - char *start; - char *end; - size_t idlen; - - /* Find the first non-space letter */ - start = header + 8; - while(*start && ISBLANK(*start)) - start++; - - if(!*start) { - failf(data, "Got a blank Session ID"); - return CURLE_RTSP_SESSION_ERROR; - } - - /* Find the end of Session ID - * - * Allow any non whitespace content, up to the field separator or end of - * line. RFC 2326 isn't 100% clear on the session ID and for example - * gstreamer does url-encoded session ID's not covered by the standard. - */ - end = start; - while(*end && *end != ';' && !ISSPACE(*end)) - end++; - idlen = end - start; - - if(data->set.str[STRING_RTSP_SESSION_ID]) { - - /* If the Session ID is set, then compare */ - if(strlen(data->set.str[STRING_RTSP_SESSION_ID]) != idlen || - strncmp(start, data->set.str[STRING_RTSP_SESSION_ID], idlen) != 0) { - failf(data, "Got RTSP Session ID Line [%s], but wanted ID [%s]", - start, data->set.str[STRING_RTSP_SESSION_ID]); - return CURLE_RTSP_SESSION_ERROR; - } - } - else { - /* If the Session ID is not set, and we find it in a response, then set - * it. - */ - - /* Copy the id substring into a new buffer */ - data->set.str[STRING_RTSP_SESSION_ID] = malloc(idlen + 1); - if(!data->set.str[STRING_RTSP_SESSION_ID]) - return CURLE_OUT_OF_MEMORY; - memcpy(data->set.str[STRING_RTSP_SESSION_ID], start, idlen); - (data->set.str[STRING_RTSP_SESSION_ID])[idlen] = '\0'; - } - } - return CURLE_OK; -} - -#endif /* CURL_DISABLE_RTSP or using Hyper */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_RTSP) && !defined(USE_HYPER)
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "transfer.h"
+#include "sendf.h"
+#include "multiif.h"
+#include "http.h"
+#include "url.h"
+#include "progress.h"
+#include "rtsp.h"
+#include "strcase.h"
+#include "select.h"
+#include "connect.h"
+#include "strdup.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define RTP_PKT_CHANNEL(p) ((int)((unsigned char)((p)[1])))
+
+#define RTP_PKT_LENGTH(p) ((((int)((unsigned char)((p)[2]))) << 8) | \
+ ((int)((unsigned char)((p)[3]))))
+
+/* protocol-specific functions set up to be called by the main engine */
+static CURLcode rtsp_do(struct Curl_easy *data, bool *done);
+static CURLcode rtsp_done(struct Curl_easy *data, CURLcode, bool premature);
+static CURLcode rtsp_connect(struct Curl_easy *data, bool *done);
+static CURLcode rtsp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn, bool dead);
+static int rtsp_getsock_do(struct Curl_easy *data,
+ struct connectdata *conn, curl_socket_t *socks);
+
+/*
+ * Parse and write out any available RTP data.
+ *
+ * nread: amount of data left after k->str. will be modified if RTP
+ * data is parsed and k->str is moved up
+ * readmore: whether or not the RTP parser needs more data right away
+ */
+static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
+ struct connectdata *conn,
+ ssize_t *nread,
+ bool *readmore);
+
+static CURLcode rtsp_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn);
+static unsigned int rtsp_conncheck(struct Curl_easy *data,
+ struct connectdata *check,
+ unsigned int checks_to_perform);
+
+/* this returns the socket to wait for in the DO and DOING state for the multi
+ interface and then we're always _sending_ a request and thus we wait for
+ the single socket to become writable only */
+static int rtsp_getsock_do(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t *socks)
+{
+ /* write mode */
+ (void)data;
+ socks[0] = conn->sock[FIRSTSOCKET];
+ return GETSOCK_WRITESOCK(0);
+}
+
+static
+CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len);
+
+
+/*
+ * RTSP handler interface.
+ */
+const struct Curl_handler Curl_handler_rtsp = {
+ "RTSP", /* scheme */
+ rtsp_setup_connection, /* setup_connection */
+ rtsp_do, /* do_it */
+ rtsp_done, /* done */
+ ZERO_NULL, /* do_more */
+ rtsp_connect, /* connect_it */
+ ZERO_NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ ZERO_NULL, /* proto_getsock */
+ rtsp_getsock_do, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ rtsp_disconnect, /* disconnect */
+ rtsp_rtp_readwrite, /* readwrite */
+ rtsp_conncheck, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_RTSP, /* defport */
+ CURLPROTO_RTSP, /* protocol */
+ CURLPROTO_RTSP, /* family */
+ PROTOPT_NONE /* flags */
+};
+
+
+static CURLcode rtsp_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ struct RTSP *rtsp;
+ (void)conn;
+
+ data->req.p.rtsp = rtsp = calloc(1, sizeof(struct RTSP));
+ if(!rtsp)
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_OK;
+}
+
+
+/*
+ * The server may send us RTP data at any point, and RTSPREQ_RECEIVE does not
+ * want to block the application forever while receiving a stream. Therefore,
+ * we cannot assume that an RTSP socket is dead just because it is readable.
+ *
+ * Instead, if it is readable, run Curl_connalive() to peek at the socket
+ * and distinguish between closed and data.
+ */
+static bool rtsp_connisdead(struct Curl_easy *data, struct connectdata *check)
+{
+ int sval;
+ bool ret_val = TRUE;
+
+ sval = SOCKET_READABLE(check->sock[FIRSTSOCKET], 0);
+ if(sval == 0) {
+ /* timeout */
+ ret_val = FALSE;
+ }
+ else if(sval & CURL_CSELECT_ERR) {
+ /* socket is in an error state */
+ ret_val = TRUE;
+ }
+ else if(sval & CURL_CSELECT_IN) {
+ /* readable with no error. could still be closed */
+ ret_val = !Curl_connalive(data, check);
+ }
+
+ return ret_val;
+}
+
+/*
+ * Function to check on various aspects of a connection.
+ */
+static unsigned int rtsp_conncheck(struct Curl_easy *data,
+ struct connectdata *conn,
+ unsigned int checks_to_perform)
+{
+ unsigned int ret_val = CONNRESULT_NONE;
+ (void)data;
+
+ if(checks_to_perform & CONNCHECK_ISDEAD) {
+ if(rtsp_connisdead(data, conn))
+ ret_val |= CONNRESULT_DEAD;
+ }
+
+ return ret_val;
+}
+
+
+static CURLcode rtsp_connect(struct Curl_easy *data, bool *done)
+{
+ CURLcode httpStatus;
+
+ httpStatus = Curl_http_connect(data, done);
+
+ /* Initialize the CSeq if not already done */
+ if(data->state.rtsp_next_client_CSeq == 0)
+ data->state.rtsp_next_client_CSeq = 1;
+ if(data->state.rtsp_next_server_CSeq == 0)
+ data->state.rtsp_next_server_CSeq = 1;
+
+ data->conn->proto.rtspc.rtp_channel = -1;
+
+ return httpStatus;
+}
+
+static CURLcode rtsp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn, bool dead)
+{
+ (void) dead;
+ (void) data;
+ Curl_safefree(conn->proto.rtspc.rtp_buf);
+ return CURLE_OK;
+}
+
+
+static CURLcode rtsp_done(struct Curl_easy *data,
+ CURLcode status, bool premature)
+{
+ struct RTSP *rtsp = data->req.p.rtsp;
+ CURLcode httpStatus;
+
+ /* Bypass HTTP empty-reply checks on receive */
+ if(data->set.rtspreq == RTSPREQ_RECEIVE)
+ premature = TRUE;
+
+ httpStatus = Curl_http_done(data, status, premature);
+
+ if(rtsp && !status && !httpStatus) {
+ /* Check the sequence numbers */
+ long CSeq_sent = rtsp->CSeq_sent;
+ long CSeq_recv = rtsp->CSeq_recv;
+ if((data->set.rtspreq != RTSPREQ_RECEIVE) && (CSeq_sent != CSeq_recv)) {
+ failf(data,
+ "The CSeq of this request %ld did not match the response %ld",
+ CSeq_sent, CSeq_recv);
+ return CURLE_RTSP_CSEQ_ERROR;
+ }
+ if(data->set.rtspreq == RTSPREQ_RECEIVE &&
+ (data->conn->proto.rtspc.rtp_channel == -1)) {
+ infof(data, "Got an RTP Receive with a CSeq of %ld", CSeq_recv);
+ }
+ }
+
+ return httpStatus;
+}
+
+static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
+{
+ struct connectdata *conn = data->conn;
+ CURLcode result = CURLE_OK;
+ Curl_RtspReq rtspreq = data->set.rtspreq;
+ struct RTSP *rtsp = data->req.p.rtsp;
+ struct dynbuf req_buffer;
+ curl_off_t postsize = 0; /* for ANNOUNCE and SET_PARAMETER */
+ curl_off_t putsize = 0; /* for ANNOUNCE and SET_PARAMETER */
+
+ const char *p_request = NULL;
+ const char *p_session_id = NULL;
+ const char *p_accept = NULL;
+ const char *p_accept_encoding = NULL;
+ const char *p_range = NULL;
+ const char *p_referrer = NULL;
+ const char *p_stream_uri = NULL;
+ const char *p_transport = NULL;
+ const char *p_uagent = NULL;
+ const char *p_proxyuserpwd = NULL;
+ const char *p_userpwd = NULL;
+
+ *done = TRUE;
+
+ rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq;
+ rtsp->CSeq_recv = 0;
+
+ /* Setup the first_* fields to allow auth details get sent
+ to this origin */
+
+ if(!data->state.first_host) {
+ data->state.first_host = strdup(conn->host.name);
+ if(!data->state.first_host)
+ return CURLE_OUT_OF_MEMORY;
+
+ data->state.first_remote_port = conn->remote_port;
+ data->state.first_remote_protocol = conn->handler->protocol;
+ }
+
+ /* Setup the 'p_request' pointer to the proper p_request string
+ * Since all RTSP requests are included here, there is no need to
+ * support custom requests like HTTP.
+ **/
+ data->req.no_body = TRUE; /* most requests don't contain a body */
+ switch(rtspreq) {
+ default:
+ failf(data, "Got invalid RTSP request");
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ case RTSPREQ_OPTIONS:
+ p_request = "OPTIONS";
+ break;
+ case RTSPREQ_DESCRIBE:
+ p_request = "DESCRIBE";
+ data->req.no_body = FALSE;
+ break;
+ case RTSPREQ_ANNOUNCE:
+ p_request = "ANNOUNCE";
+ break;
+ case RTSPREQ_SETUP:
+ p_request = "SETUP";
+ break;
+ case RTSPREQ_PLAY:
+ p_request = "PLAY";
+ break;
+ case RTSPREQ_PAUSE:
+ p_request = "PAUSE";
+ break;
+ case RTSPREQ_TEARDOWN:
+ p_request = "TEARDOWN";
+ break;
+ case RTSPREQ_GET_PARAMETER:
+ /* GET_PARAMETER's no_body status is determined later */
+ p_request = "GET_PARAMETER";
+ data->req.no_body = FALSE;
+ break;
+ case RTSPREQ_SET_PARAMETER:
+ p_request = "SET_PARAMETER";
+ break;
+ case RTSPREQ_RECORD:
+ p_request = "RECORD";
+ break;
+ case RTSPREQ_RECEIVE:
+ p_request = "";
+ /* Treat interleaved RTP as body */
+ data->req.no_body = FALSE;
+ break;
+ case RTSPREQ_LAST:
+ failf(data, "Got invalid RTSP request: RTSPREQ_LAST");
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+
+ if(rtspreq == RTSPREQ_RECEIVE) {
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1);
+
+ return result;
+ }
+
+ p_session_id = data->set.str[STRING_RTSP_SESSION_ID];
+ if(!p_session_id &&
+ (rtspreq & ~(RTSPREQ_OPTIONS | RTSPREQ_DESCRIBE | RTSPREQ_SETUP))) {
+ failf(data, "Refusing to issue an RTSP request [%s] without a session ID.",
+ p_request);
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+
+ /* Stream URI. Default to server '*' if not specified */
+ if(data->set.str[STRING_RTSP_STREAM_URI]) {
+ p_stream_uri = data->set.str[STRING_RTSP_STREAM_URI];
+ }
+ else {
+ p_stream_uri = "*";
+ }
+
+ /* Transport Header for SETUP requests */
+ p_transport = Curl_checkheaders(data, STRCONST("Transport"));
+ if(rtspreq == RTSPREQ_SETUP && !p_transport) {
+ /* New Transport: setting? */
+ if(data->set.str[STRING_RTSP_TRANSPORT]) {
+ Curl_safefree(data->state.aptr.rtsp_transport);
+
+ data->state.aptr.rtsp_transport =
+ aprintf("Transport: %s\r\n",
+ data->set.str[STRING_RTSP_TRANSPORT]);
+ if(!data->state.aptr.rtsp_transport)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else {
+ failf(data,
+ "Refusing to issue an RTSP SETUP without a Transport: header.");
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+
+ p_transport = data->state.aptr.rtsp_transport;
+ }
+
+ /* Accept Headers for DESCRIBE requests */
+ if(rtspreq == RTSPREQ_DESCRIBE) {
+ /* Accept Header */
+ p_accept = Curl_checkheaders(data, STRCONST("Accept"))?
+ NULL:"Accept: application/sdp\r\n";
+
+ /* Accept-Encoding header */
+ if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
+ data->set.str[STRING_ENCODING]) {
+ Curl_safefree(data->state.aptr.accept_encoding);
+ data->state.aptr.accept_encoding =
+ aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
+
+ if(!data->state.aptr.accept_encoding)
+ return CURLE_OUT_OF_MEMORY;
+
+ p_accept_encoding = data->state.aptr.accept_encoding;
+ }
+ }
+
+ /* The User-Agent string might have been allocated in url.c already, because
+ it might have been used in the proxy connect, but if we have got a header
+ with the user-agent string specified, we erase the previously made string
+ here. */
+ if(Curl_checkheaders(data, STRCONST("User-Agent")) &&
+ data->state.aptr.uagent) {
+ Curl_safefree(data->state.aptr.uagent);
+ data->state.aptr.uagent = NULL;
+ }
+ else if(!Curl_checkheaders(data, STRCONST("User-Agent")) &&
+ data->set.str[STRING_USERAGENT]) {
+ p_uagent = data->state.aptr.uagent;
+ }
+
+ /* setup the authentication headers */
+ result = Curl_http_output_auth(data, conn, p_request, HTTPREQ_GET,
+ p_stream_uri, FALSE);
+ if(result)
+ return result;
+
+ p_proxyuserpwd = data->state.aptr.proxyuserpwd;
+ p_userpwd = data->state.aptr.userpwd;
+
+ /* Referrer */
+ Curl_safefree(data->state.aptr.ref);
+ if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer")))
+ data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer);
+ else
+ data->state.aptr.ref = NULL;
+
+ p_referrer = data->state.aptr.ref;
+
+ /*
+ * Range Header
+ * Only applies to PLAY, PAUSE, RECORD
+ *
+ * Go ahead and use the Range stuff supplied for HTTP
+ */
+ if(data->state.use_range &&
+ (rtspreq & (RTSPREQ_PLAY | RTSPREQ_PAUSE | RTSPREQ_RECORD))) {
+
+ /* Check to see if there is a range set in the custom headers */
+ if(!Curl_checkheaders(data, STRCONST("Range")) && data->state.range) {
+ Curl_safefree(data->state.aptr.rangeline);
+ data->state.aptr.rangeline = aprintf("Range: %s\r\n", data->state.range);
+ p_range = data->state.aptr.rangeline;
+ }
+ }
+
+ /*
+ * Sanity check the custom headers
+ */
+ if(Curl_checkheaders(data, STRCONST("CSeq"))) {
+ failf(data, "CSeq cannot be set as a custom header.");
+ return CURLE_RTSP_CSEQ_ERROR;
+ }
+ if(Curl_checkheaders(data, STRCONST("Session"))) {
+ failf(data, "Session ID cannot be set as a custom header.");
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+
+ /* Initialize a dynamic send buffer */
+ Curl_dyn_init(&req_buffer, DYN_RTSP_REQ_HEADER);
+
+ result =
+ Curl_dyn_addf(&req_buffer,
+ "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
+ "CSeq: %ld\r\n", /* CSeq */
+ p_request, p_stream_uri, rtsp->CSeq_sent);
+ if(result)
+ return result;
+
+ /*
+ * Rather than do a normal alloc line, keep the session_id unformatted
+ * to make comparison easier
+ */
+ if(p_session_id) {
+ result = Curl_dyn_addf(&req_buffer, "Session: %s\r\n", p_session_id);
+ if(result)
+ return result;
+ }
+
+ /*
+ * Shared HTTP-like options
+ */
+ result = Curl_dyn_addf(&req_buffer,
+ "%s" /* transport */
+ "%s" /* accept */
+ "%s" /* accept-encoding */
+ "%s" /* range */
+ "%s" /* referrer */
+ "%s" /* user-agent */
+ "%s" /* proxyuserpwd */
+ "%s" /* userpwd */
+ ,
+ p_transport ? p_transport : "",
+ p_accept ? p_accept : "",
+ p_accept_encoding ? p_accept_encoding : "",
+ p_range ? p_range : "",
+ p_referrer ? p_referrer : "",
+ p_uagent ? p_uagent : "",
+ p_proxyuserpwd ? p_proxyuserpwd : "",
+ p_userpwd ? p_userpwd : "");
+
+ /*
+ * Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM
+ * with basic and digest, it will be freed anyway by the next request
+ */
+ Curl_safefree(data->state.aptr.userpwd);
+ data->state.aptr.userpwd = NULL;
+
+ if(result)
+ return result;
+
+ if((rtspreq == RTSPREQ_SETUP) || (rtspreq == RTSPREQ_DESCRIBE)) {
+ result = Curl_add_timecondition(data, &req_buffer);
+ if(result)
+ return result;
+ }
+
+ result = Curl_add_custom_headers(data, FALSE, &req_buffer);
+ if(result)
+ return result;
+
+ if(rtspreq == RTSPREQ_ANNOUNCE ||
+ rtspreq == RTSPREQ_SET_PARAMETER ||
+ rtspreq == RTSPREQ_GET_PARAMETER) {
+
+ if(data->set.upload) {
+ putsize = data->state.infilesize;
+ data->state.httpreq = HTTPREQ_PUT;
+
+ }
+ else {
+ postsize = (data->state.infilesize != -1)?
+ data->state.infilesize:
+ (data->set.postfields? (curl_off_t)strlen(data->set.postfields):0);
+ data->state.httpreq = HTTPREQ_POST;
+ }
+
+ if(putsize > 0 || postsize > 0) {
+ /* As stated in the http comments, it is probably not wise to
+ * actually set a custom Content-Length in the headers */
+ if(!Curl_checkheaders(data, STRCONST("Content-Length"))) {
+ result =
+ Curl_dyn_addf(&req_buffer,
+ "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n",
+ (data->set.upload ? putsize : postsize));
+ if(result)
+ return result;
+ }
+
+ if(rtspreq == RTSPREQ_SET_PARAMETER ||
+ rtspreq == RTSPREQ_GET_PARAMETER) {
+ if(!Curl_checkheaders(data, STRCONST("Content-Type"))) {
+ result = Curl_dyn_addn(&req_buffer,
+ STRCONST("Content-Type: "
+ "text/parameters\r\n"));
+ if(result)
+ return result;
+ }
+ }
+
+ if(rtspreq == RTSPREQ_ANNOUNCE) {
+ if(!Curl_checkheaders(data, STRCONST("Content-Type"))) {
+ result = Curl_dyn_addn(&req_buffer,
+ STRCONST("Content-Type: "
+ "application/sdp\r\n"));
+ if(result)
+ return result;
+ }
+ }
+
+ data->state.expect100header = FALSE; /* RTSP posts are simple/small */
+ }
+ else if(rtspreq == RTSPREQ_GET_PARAMETER) {
+ /* Check for an empty GET_PARAMETER (heartbeat) request */
+ data->state.httpreq = HTTPREQ_HEAD;
+ data->req.no_body = TRUE;
+ }
+ }
+
+ /* RTSP never allows chunked transfer */
+ data->req.forbidchunk = TRUE;
+ /* Finish the request buffer */
+ result = Curl_dyn_addn(&req_buffer, STRCONST("\r\n"));
+ if(result)
+ return result;
+
+ if(postsize > 0) {
+ result = Curl_dyn_addn(&req_buffer, data->set.postfields,
+ (size_t)postsize);
+ if(result)
+ return result;
+ }
+
+ /* issue the request */
+ result = Curl_buffer_send(&req_buffer, data,
+ &data->info.request_size, 0, FIRSTSOCKET);
+ if(result) {
+ failf(data, "Failed sending RTSP request");
+ return result;
+ }
+
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, putsize?FIRSTSOCKET:-1);
+
+ /* Increment the CSeq on success */
+ data->state.rtsp_next_client_CSeq++;
+
+ if(data->req.writebytecount) {
+ /* if a request-body has been sent off, we make sure this progress is
+ noted properly */
+ Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
+ if(Curl_pgrsUpdate(data))
+ result = CURLE_ABORTED_BY_CALLBACK;
+ }
+
+ return result;
+}
+
+
+static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
+ struct connectdata *conn,
+ ssize_t *nread,
+ bool *readmore) {
+ struct SingleRequest *k = &data->req;
+ struct rtsp_conn *rtspc = &(conn->proto.rtspc);
+
+ char *rtp; /* moving pointer to rtp data */
+ ssize_t rtp_dataleft; /* how much data left to parse in this round */
+ char *scratch;
+ CURLcode result;
+
+ if(rtspc->rtp_buf) {
+ /* There was some leftover data the last time. Merge buffers */
+ char *newptr = Curl_saferealloc(rtspc->rtp_buf,
+ rtspc->rtp_bufsize + *nread);
+ if(!newptr) {
+ rtspc->rtp_buf = NULL;
+ rtspc->rtp_bufsize = 0;
+ return CURLE_OUT_OF_MEMORY;
+ }
+ rtspc->rtp_buf = newptr;
+ memcpy(rtspc->rtp_buf + rtspc->rtp_bufsize, k->str, *nread);
+ rtspc->rtp_bufsize += *nread;
+ rtp = rtspc->rtp_buf;
+ rtp_dataleft = rtspc->rtp_bufsize;
+ }
+ else {
+ /* Just parse the request buffer directly */
+ rtp = k->str;
+ rtp_dataleft = *nread;
+ }
+
+ while((rtp_dataleft > 0) &&
+ (rtp[0] == '$')) {
+ if(rtp_dataleft > 4) {
+ int rtp_length;
+
+ /* Parse the header */
+ /* The channel identifier immediately follows and is 1 byte */
+ rtspc->rtp_channel = RTP_PKT_CHANNEL(rtp);
+
+ /* The length is two bytes */
+ rtp_length = RTP_PKT_LENGTH(rtp);
+
+ if(rtp_dataleft < rtp_length + 4) {
+ /* Need more - incomplete payload */
+ *readmore = TRUE;
+ break;
+ }
+ /* We have the full RTP interleaved packet
+ * Write out the header including the leading '$' */
+ DEBUGF(infof(data, "RTP write channel %d rtp_length %d",
+ rtspc->rtp_channel, rtp_length));
+ result = rtp_client_write(data, &rtp[0], rtp_length + 4);
+ if(result) {
+ failf(data, "Got an error writing an RTP packet");
+ *readmore = FALSE;
+ Curl_safefree(rtspc->rtp_buf);
+ rtspc->rtp_buf = NULL;
+ rtspc->rtp_bufsize = 0;
+ return result;
+ }
+
+ /* Move forward in the buffer */
+ rtp_dataleft -= rtp_length + 4;
+ rtp += rtp_length + 4;
+
+ if(data->set.rtspreq == RTSPREQ_RECEIVE) {
+ /* If we are in a passive receive, give control back
+ * to the app as often as we can.
+ */
+ k->keepon &= ~KEEP_RECV;
+ }
+ }
+ else {
+ /* Need more - incomplete header */
+ *readmore = TRUE;
+ break;
+ }
+ }
+
+ if(rtp_dataleft && rtp[0] == '$') {
+ DEBUGF(infof(data, "RTP Rewinding %zd %s", rtp_dataleft,
+ *readmore ? "(READMORE)" : ""));
+
+ /* Store the incomplete RTP packet for a "rewind" */
+ scratch = malloc(rtp_dataleft);
+ if(!scratch) {
+ Curl_safefree(rtspc->rtp_buf);
+ rtspc->rtp_buf = NULL;
+ rtspc->rtp_bufsize = 0;
+ return CURLE_OUT_OF_MEMORY;
+ }
+ memcpy(scratch, rtp, rtp_dataleft);
+ Curl_safefree(rtspc->rtp_buf);
+ rtspc->rtp_buf = scratch;
+ rtspc->rtp_bufsize = rtp_dataleft;
+
+ /* As far as the transfer is concerned, this data is consumed */
+ *nread = 0;
+ return CURLE_OK;
+ }
+ /* Fix up k->str to point just after the last RTP packet */
+ k->str += *nread - rtp_dataleft;
+
+ /* either all of the data has been read or...
+ * rtp now points at the next byte to parse
+ */
+ if(rtp_dataleft > 0)
+ DEBUGASSERT(k->str[0] == rtp[0]);
+
+ DEBUGASSERT(rtp_dataleft <= *nread); /* sanity check */
+
+ *nread = rtp_dataleft;
+
+ /* If we get here, we have finished with the leftover/merge buffer */
+ Curl_safefree(rtspc->rtp_buf);
+ rtspc->rtp_buf = NULL;
+ rtspc->rtp_bufsize = 0;
+
+ return CURLE_OK;
+}
+
+static
+CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len)
+{
+ size_t wrote;
+ curl_write_callback writeit;
+ void *user_ptr;
+
+ if(len == 0) {
+ failf(data, "Cannot write a 0 size RTP packet.");
+ return CURLE_WRITE_ERROR;
+ }
+
+ /* If the user has configured CURLOPT_INTERLEAVEFUNCTION then use that
+ function and any configured CURLOPT_INTERLEAVEDATA to write out the RTP
+ data. Otherwise, use the CURLOPT_WRITEFUNCTION with the CURLOPT_WRITEDATA
+ pointer to write out the RTP data. */
+ if(data->set.fwrite_rtp) {
+ writeit = data->set.fwrite_rtp;
+ user_ptr = data->set.rtp_out;
+ }
+ else {
+ writeit = data->set.fwrite_func;
+ user_ptr = data->set.out;
+ }
+
+ Curl_set_in_callback(data, true);
+ wrote = writeit(ptr, 1, len, user_ptr);
+ Curl_set_in_callback(data, false);
+
+ if(CURL_WRITEFUNC_PAUSE == wrote) {
+ failf(data, "Cannot pause RTP");
+ return CURLE_WRITE_ERROR;
+ }
+
+ if(wrote != len) {
+ failf(data, "Failed writing RTP data");
+ return CURLE_WRITE_ERROR;
+ }
+
+ return CURLE_OK;
+}
+
+CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header)
+{
+ long CSeq = 0;
+
+ if(checkprefix("CSeq:", header)) {
+ /* Store the received CSeq. Match is verified in rtsp_done */
+ int nc = sscanf(&header[4], ": %ld", &CSeq);
+ if(nc == 1) {
+ struct RTSP *rtsp = data->req.p.rtsp;
+ rtsp->CSeq_recv = CSeq; /* mark the request */
+ data->state.rtsp_CSeq_recv = CSeq; /* update the handle */
+ }
+ else {
+ failf(data, "Unable to read the CSeq header: [%s]", header);
+ return CURLE_RTSP_CSEQ_ERROR;
+ }
+ }
+ else if(checkprefix("Session:", header)) {
+ char *start;
+ char *end;
+ size_t idlen;
+
+ /* Find the first non-space letter */
+ start = header + 8;
+ while(*start && ISBLANK(*start))
+ start++;
+
+ if(!*start) {
+ failf(data, "Got a blank Session ID");
+ return CURLE_RTSP_SESSION_ERROR;
+ }
+
+ /* Find the end of Session ID
+ *
+ * Allow any non whitespace content, up to the field separator or end of
+ * line. RFC 2326 isn't 100% clear on the session ID and for example
+ * gstreamer does url-encoded session ID's not covered by the standard.
+ */
+ end = start;
+ while(*end && *end != ';' && !ISSPACE(*end))
+ end++;
+ idlen = end - start;
+
+ if(data->set.str[STRING_RTSP_SESSION_ID]) {
+
+ /* If the Session ID is set, then compare */
+ if(strlen(data->set.str[STRING_RTSP_SESSION_ID]) != idlen ||
+ strncmp(start, data->set.str[STRING_RTSP_SESSION_ID], idlen) != 0) {
+ failf(data, "Got RTSP Session ID Line [%s], but wanted ID [%s]",
+ start, data->set.str[STRING_RTSP_SESSION_ID]);
+ return CURLE_RTSP_SESSION_ERROR;
+ }
+ }
+ else {
+ /* If the Session ID is not set, and we find it in a response, then set
+ * it.
+ */
+
+ /* Copy the id substring into a new buffer */
+ data->set.str[STRING_RTSP_SESSION_ID] = malloc(idlen + 1);
+ if(!data->set.str[STRING_RTSP_SESSION_ID])
+ return CURLE_OUT_OF_MEMORY;
+ memcpy(data->set.str[STRING_RTSP_SESSION_ID], start, idlen);
+ (data->set.str[STRING_RTSP_SESSION_ID])[idlen] = '\0';
+ }
+ }
+ return CURLE_OK;
+}
+
+#endif /* CURL_DISABLE_RTSP or using Hyper */
diff --git a/libs/libcurl/src/rtsp.h b/libs/libcurl/src/rtsp.h index 377c828605..fa216ea464 100644 --- a/libs/libcurl/src/rtsp.h +++ b/libs/libcurl/src/rtsp.h @@ -1,72 +1,72 @@ -#ifndef HEADER_CURL_RTSP_H -#define HEADER_CURL_RTSP_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ -#ifdef USE_HYPER -#define CURL_DISABLE_RTSP 1 -#endif - -#ifndef CURL_DISABLE_RTSP - -extern const struct Curl_handler Curl_handler_rtsp; - -CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header); - -#else -/* disabled */ -#define Curl_rtsp_parseheader(x,y) CURLE_NOT_BUILT_IN - -#endif /* CURL_DISABLE_RTSP */ - -/* - * RTSP Connection data - * - * Currently, only used for tracking incomplete RTP data reads - */ -struct rtsp_conn { - char *rtp_buf; - ssize_t rtp_bufsize; - int rtp_channel; -}; - -/**************************************************************************** - * RTSP unique setup - ***************************************************************************/ -struct RTSP { - /* - * http_wrapper MUST be the first element of this structure for the wrap - * logic to work. In this way, we get a cheap polymorphism because - * &(data->state.proto.rtsp) == &(data->state.proto.http) per the C spec - * - * HTTP functions can safely treat this as an HTTP struct, but RTSP aware - * functions can also index into the later elements. - */ - struct HTTP http_wrapper; /*wrap HTTP to do the heavy lifting */ - - long CSeq_sent; /* CSeq of this request */ - long CSeq_recv; /* CSeq received */ -}; - - -#endif /* HEADER_CURL_RTSP_H */ +#ifndef HEADER_CURL_RTSP_H
+#define HEADER_CURL_RTSP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+#ifdef USE_HYPER
+#define CURL_DISABLE_RTSP 1
+#endif
+
+#ifndef CURL_DISABLE_RTSP
+
+extern const struct Curl_handler Curl_handler_rtsp;
+
+CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header);
+
+#else
+/* disabled */
+#define Curl_rtsp_parseheader(x,y) CURLE_NOT_BUILT_IN
+
+#endif /* CURL_DISABLE_RTSP */
+
+/*
+ * RTSP Connection data
+ *
+ * Currently, only used for tracking incomplete RTP data reads
+ */
+struct rtsp_conn {
+ char *rtp_buf;
+ ssize_t rtp_bufsize;
+ int rtp_channel;
+};
+
+/****************************************************************************
+ * RTSP unique setup
+ ***************************************************************************/
+struct RTSP {
+ /*
+ * http_wrapper MUST be the first element of this structure for the wrap
+ * logic to work. In this way, we get a cheap polymorphism because
+ * &(data->state.proto.rtsp) == &(data->state.proto.http) per the C spec
+ *
+ * HTTP functions can safely treat this as an HTTP struct, but RTSP aware
+ * functions can also index into the later elements.
+ */
+ struct HTTP http_wrapper; /* wrap HTTP to do the heavy lifting */
+
+ long CSeq_sent; /* CSeq of this request */
+ long CSeq_recv; /* CSeq received */
+};
+
+
+#endif /* HEADER_CURL_RTSP_H */
diff --git a/libs/libcurl/src/select.c b/libs/libcurl/src/select.c index 2ac0746772..c581c25ac6 100644 --- a/libs/libcurl/src/select.c +++ b/libs/libcurl/src/select.c @@ -1,398 +1,398 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 <limits.h> - -#ifdef HAVE_SYS_SELECT_H -#include <sys/select.h> -#elif defined(HAVE_UNISTD_H) -#include <unistd.h> -#endif - -#if !defined(HAVE_SELECT) && !defined(HAVE_POLL_FINE) -#error "We can't compile without select() or poll() support." -#endif - -#ifdef MSDOS -#include <dos.h> /* delay() */ -#endif - -#include <curl/curl.h> - -#include "urldata.h" -#include "connect.h" -#include "select.h" -#include "timediff.h" -#include "warnless.h" - -/* - * Internal function used for waiting a specific amount of ms - * in Curl_socket_check() and Curl_poll() when no file descriptor - * is provided to wait on, just being used to delay execution. - * WinSock select() and poll() timeout mechanisms need a valid - * socket descriptor in a not null file descriptor set to work. - * Waiting indefinitely with this function is not allowed, a - * zero or negative timeout value will return immediately. - * Timeout resolution, accuracy, as well as maximum supported - * value is system dependent, neither factor is a critical issue - * for the intended use of this function in the library. - * - * Return values: - * -1 = system call error, invalid timeout value, or interrupted - * 0 = specified timeout has elapsed - */ -int Curl_wait_ms(timediff_t timeout_ms) -{ - int r = 0; - - if(!timeout_ms) - return 0; - if(timeout_ms < 0) { - SET_SOCKERRNO(EINVAL); - return -1; - } -#if defined(MSDOS) - delay(timeout_ms); -#elif defined(WIN32) - /* prevent overflow, timeout_ms is typecast to ULONG/DWORD. */ -#if TIMEDIFF_T_MAX >= ULONG_MAX - if(timeout_ms >= ULONG_MAX) - timeout_ms = ULONG_MAX-1; - /* don't use ULONG_MAX, because that is equal to INFINITE */ -#endif - Sleep((ULONG)timeout_ms); -#else -#if defined(HAVE_POLL_FINE) - /* prevent overflow, timeout_ms is typecast to int. */ -#if TIMEDIFF_T_MAX > INT_MAX - if(timeout_ms > INT_MAX) - timeout_ms = INT_MAX; -#endif - r = poll(NULL, 0, (int)timeout_ms); -#else - { - struct timeval pending_tv; - r = select(0, NULL, NULL, NULL, curlx_mstotv(&pending_tv, timeout_ms)); - } -#endif /* HAVE_POLL_FINE */ -#endif /* USE_WINSOCK */ - if(r) - r = -1; - return r; -} - -#ifndef HAVE_POLL_FINE -/* - * This is a wrapper around select() to aid in Windows compatibility. - * A negative timeout value makes this function wait indefinitely, - * unless no valid file descriptor is given, when this happens the - * negative timeout is ignored and the function times out immediately. - * - * Return values: - * -1 = system call error or fd >= FD_SETSIZE - * 0 = timeout - * N = number of signalled file descriptors - */ -static int our_select(curl_socket_t maxfd, /* highest socket number */ - fd_set *fds_read, /* sockets ready for reading */ - fd_set *fds_write, /* sockets ready for writing */ - fd_set *fds_err, /* sockets with errors */ - timediff_t timeout_ms) /* milliseconds to wait */ -{ - struct timeval pending_tv; - struct timeval *ptimeout; - -#ifdef USE_WINSOCK - /* WinSock select() can't handle zero events. See the comment below. */ - if((!fds_read || fds_read->fd_count == 0) && - (!fds_write || fds_write->fd_count == 0) && - (!fds_err || fds_err->fd_count == 0)) { - /* no sockets, just wait */ - return Curl_wait_ms(timeout_ms); - } -#endif - - ptimeout = curlx_mstotv(&pending_tv, timeout_ms); - -#ifdef USE_WINSOCK - /* WinSock select() must not be called with an fd_set that contains zero - fd flags, or it will return WSAEINVAL. But, it also can't be called - with no fd_sets at all! From the documentation: - - Any two of the parameters, readfds, writefds, or exceptfds, can be - given as null. At least one must be non-null, and any non-null - descriptor set must contain at least one handle to a socket. - - It is unclear why WinSock doesn't just handle this for us instead of - calling this an error. Luckily, with WinSock, we can _also_ ask how - many bits are set on an fd_set. So, let's just check it beforehand. - */ - return select((int)maxfd + 1, - fds_read && fds_read->fd_count ? fds_read : NULL, - fds_write && fds_write->fd_count ? fds_write : NULL, - fds_err && fds_err->fd_count ? fds_err : NULL, ptimeout); -#else - return select((int)maxfd + 1, fds_read, fds_write, fds_err, ptimeout); -#endif -} - -#endif - -/* - * Wait for read or write events on a set of file descriptors. It uses poll() - * when a fine poll() is available, in order to avoid limits with FD_SETSIZE, - * otherwise select() is used. An error is returned if select() is being used - * and a file descriptor is too large for FD_SETSIZE. - * - * A negative timeout value makes this function wait indefinitely, - * unless no valid file descriptor is given, when this happens the - * negative timeout is ignored and the function times out immediately. - * - * Return values: - * -1 = system call error or fd >= FD_SETSIZE - * 0 = timeout - * [bitmask] = action as described below - * - * CURL_CSELECT_IN - first socket is readable - * CURL_CSELECT_IN2 - second socket is readable - * CURL_CSELECT_OUT - write socket is writable - * CURL_CSELECT_ERR - an error condition occurred - */ -int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */ - curl_socket_t readfd1, - curl_socket_t writefd, /* socket to write to */ - timediff_t timeout_ms) /* milliseconds to wait */ -{ - struct pollfd pfd[3]; - int num; - int r; - - if((readfd0 == CURL_SOCKET_BAD) && (readfd1 == CURL_SOCKET_BAD) && - (writefd == CURL_SOCKET_BAD)) { - /* no sockets, just wait */ - return Curl_wait_ms(timeout_ms); - } - - /* Avoid initial timestamp, avoid Curl_now() call, when elapsed - time in this function does not need to be measured. This happens - when function is called with a zero timeout or a negative timeout - value indicating a blocking call should be performed. */ - - num = 0; - if(readfd0 != CURL_SOCKET_BAD) { - pfd[num].fd = readfd0; - pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI; - pfd[num].revents = 0; - num++; - } - if(readfd1 != CURL_SOCKET_BAD) { - pfd[num].fd = readfd1; - pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI; - pfd[num].revents = 0; - num++; - } - if(writefd != CURL_SOCKET_BAD) { - pfd[num].fd = writefd; - pfd[num].events = POLLWRNORM|POLLOUT|POLLPRI; - pfd[num].revents = 0; - num++; - } - - r = Curl_poll(pfd, num, timeout_ms); - if(r <= 0) - return r; - - r = 0; - num = 0; - if(readfd0 != CURL_SOCKET_BAD) { - if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP)) - r |= CURL_CSELECT_IN; - if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL)) - r |= CURL_CSELECT_ERR; - num++; - } - if(readfd1 != CURL_SOCKET_BAD) { - if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP)) - r |= CURL_CSELECT_IN2; - if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL)) - r |= CURL_CSELECT_ERR; - num++; - } - if(writefd != CURL_SOCKET_BAD) { - if(pfd[num].revents & (POLLWRNORM|POLLOUT)) - r |= CURL_CSELECT_OUT; - if(pfd[num].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL)) - r |= CURL_CSELECT_ERR; - } - - return r; -} - -/* - * This is a wrapper around poll(). If poll() does not exist, then - * select() is used instead. An error is returned if select() is - * being used and a file descriptor is too large for FD_SETSIZE. - * A negative timeout value makes this function wait indefinitely, - * unless no valid file descriptor is given, when this happens the - * negative timeout is ignored and the function times out immediately. - * - * Return values: - * -1 = system call error or fd >= FD_SETSIZE - * 0 = timeout - * N = number of structures with non zero revent fields - */ -int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms) -{ -#ifdef HAVE_POLL_FINE - int pending_ms; -#else - fd_set fds_read; - fd_set fds_write; - fd_set fds_err; - curl_socket_t maxfd; -#endif - bool fds_none = TRUE; - unsigned int i; - int r; - - if(ufds) { - for(i = 0; i < nfds; i++) { - if(ufds[i].fd != CURL_SOCKET_BAD) { - fds_none = FALSE; - break; - } - } - } - if(fds_none) { - /* no sockets, just wait */ - return Curl_wait_ms(timeout_ms); - } - - /* Avoid initial timestamp, avoid Curl_now() call, when elapsed - time in this function does not need to be measured. This happens - when function is called with a zero timeout or a negative timeout - value indicating a blocking call should be performed. */ - -#ifdef HAVE_POLL_FINE - - /* prevent overflow, timeout_ms is typecast to int. */ -#if TIMEDIFF_T_MAX > INT_MAX - if(timeout_ms > INT_MAX) - timeout_ms = INT_MAX; -#endif - if(timeout_ms > 0) - pending_ms = (int)timeout_ms; - else if(timeout_ms < 0) - pending_ms = -1; - else - pending_ms = 0; - r = poll(ufds, nfds, pending_ms); - if(r <= 0) { - if((r == -1) && (SOCKERRNO == EINTR)) - /* make EINTR from select or poll not a "lethal" error */ - r = 0; - return r; - } - - for(i = 0; i < nfds; i++) { - if(ufds[i].fd == CURL_SOCKET_BAD) - continue; - if(ufds[i].revents & POLLHUP) - ufds[i].revents |= POLLIN; - if(ufds[i].revents & POLLERR) - ufds[i].revents |= POLLIN|POLLOUT; - } - -#else /* HAVE_POLL_FINE */ - - FD_ZERO(&fds_read); - FD_ZERO(&fds_write); - FD_ZERO(&fds_err); - maxfd = (curl_socket_t)-1; - - for(i = 0; i < nfds; i++) { - ufds[i].revents = 0; - if(ufds[i].fd == CURL_SOCKET_BAD) - continue; - VERIFY_SOCK(ufds[i].fd); - if(ufds[i].events & (POLLIN|POLLOUT|POLLPRI| - POLLRDNORM|POLLWRNORM|POLLRDBAND)) { - if(ufds[i].fd > maxfd) - maxfd = ufds[i].fd; - if(ufds[i].events & (POLLRDNORM|POLLIN)) - FD_SET(ufds[i].fd, &fds_read); - if(ufds[i].events & (POLLWRNORM|POLLOUT)) - FD_SET(ufds[i].fd, &fds_write); - if(ufds[i].events & (POLLRDBAND|POLLPRI)) - FD_SET(ufds[i].fd, &fds_err); - } - } - - /* - Note also that WinSock ignores the first argument, so we don't worry - about the fact that maxfd is computed incorrectly with WinSock (since - curl_socket_t is unsigned in such cases and thus -1 is the largest - value). - */ - r = our_select(maxfd, &fds_read, &fds_write, &fds_err, timeout_ms); - if(r <= 0) { - if((r == -1) && (SOCKERRNO == EINTR)) - /* make EINTR from select or poll not a "lethal" error */ - r = 0; - return r; - } - - r = 0; - for(i = 0; i < nfds; i++) { - ufds[i].revents = 0; - if(ufds[i].fd == CURL_SOCKET_BAD) - continue; - if(FD_ISSET(ufds[i].fd, &fds_read)) { - if(ufds[i].events & POLLRDNORM) - ufds[i].revents |= POLLRDNORM; - if(ufds[i].events & POLLIN) - ufds[i].revents |= POLLIN; - } - if(FD_ISSET(ufds[i].fd, &fds_write)) { - if(ufds[i].events & POLLWRNORM) - ufds[i].revents |= POLLWRNORM; - if(ufds[i].events & POLLOUT) - ufds[i].revents |= POLLOUT; - } - if(FD_ISSET(ufds[i].fd, &fds_err)) { - if(ufds[i].events & POLLRDBAND) - ufds[i].revents |= POLLRDBAND; - if(ufds[i].events & POLLPRI) - ufds[i].revents |= POLLPRI; - } - if(ufds[i].revents) - r++; - } - -#endif /* HAVE_POLL_FINE */ - - return r; -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 <limits.h>
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#elif defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#if !defined(HAVE_SELECT) && !defined(HAVE_POLL_FINE)
+#error "We can't compile without select() or poll() support."
+#endif
+
+#ifdef MSDOS
+#include <dos.h> /* delay() */
+#endif
+
+#include <curl/curl.h>
+
+#include "urldata.h"
+#include "connect.h"
+#include "select.h"
+#include "timediff.h"
+#include "warnless.h"
+
+/*
+ * Internal function used for waiting a specific amount of ms
+ * in Curl_socket_check() and Curl_poll() when no file descriptor
+ * is provided to wait on, just being used to delay execution.
+ * WinSock select() and poll() timeout mechanisms need a valid
+ * socket descriptor in a not null file descriptor set to work.
+ * Waiting indefinitely with this function is not allowed, a
+ * zero or negative timeout value will return immediately.
+ * Timeout resolution, accuracy, as well as maximum supported
+ * value is system dependent, neither factor is a critical issue
+ * for the intended use of this function in the library.
+ *
+ * Return values:
+ * -1 = system call error, invalid timeout value, or interrupted
+ * 0 = specified timeout has elapsed
+ */
+int Curl_wait_ms(timediff_t timeout_ms)
+{
+ int r = 0;
+
+ if(!timeout_ms)
+ return 0;
+ if(timeout_ms < 0) {
+ SET_SOCKERRNO(EINVAL);
+ return -1;
+ }
+#if defined(MSDOS)
+ delay(timeout_ms);
+#elif defined(WIN32)
+ /* prevent overflow, timeout_ms is typecast to ULONG/DWORD. */
+#if TIMEDIFF_T_MAX >= ULONG_MAX
+ if(timeout_ms >= ULONG_MAX)
+ timeout_ms = ULONG_MAX-1;
+ /* don't use ULONG_MAX, because that is equal to INFINITE */
+#endif
+ Sleep((ULONG)timeout_ms);
+#else
+#if defined(HAVE_POLL_FINE)
+ /* prevent overflow, timeout_ms is typecast to int. */
+#if TIMEDIFF_T_MAX > INT_MAX
+ if(timeout_ms > INT_MAX)
+ timeout_ms = INT_MAX;
+#endif
+ r = poll(NULL, 0, (int)timeout_ms);
+#else
+ {
+ struct timeval pending_tv;
+ r = select(0, NULL, NULL, NULL, curlx_mstotv(&pending_tv, timeout_ms));
+ }
+#endif /* HAVE_POLL_FINE */
+#endif /* USE_WINSOCK */
+ if(r)
+ r = -1;
+ return r;
+}
+
+#ifndef HAVE_POLL_FINE
+/*
+ * This is a wrapper around select() to aid in Windows compatibility.
+ * A negative timeout value makes this function wait indefinitely,
+ * unless no valid file descriptor is given, when this happens the
+ * negative timeout is ignored and the function times out immediately.
+ *
+ * Return values:
+ * -1 = system call error or fd >= FD_SETSIZE
+ * 0 = timeout
+ * N = number of signalled file descriptors
+ */
+static int our_select(curl_socket_t maxfd, /* highest socket number */
+ fd_set *fds_read, /* sockets ready for reading */
+ fd_set *fds_write, /* sockets ready for writing */
+ fd_set *fds_err, /* sockets with errors */
+ timediff_t timeout_ms) /* milliseconds to wait */
+{
+ struct timeval pending_tv;
+ struct timeval *ptimeout;
+
+#ifdef USE_WINSOCK
+ /* WinSock select() can't handle zero events. See the comment below. */
+ if((!fds_read || fds_read->fd_count == 0) &&
+ (!fds_write || fds_write->fd_count == 0) &&
+ (!fds_err || fds_err->fd_count == 0)) {
+ /* no sockets, just wait */
+ return Curl_wait_ms(timeout_ms);
+ }
+#endif
+
+ ptimeout = curlx_mstotv(&pending_tv, timeout_ms);
+
+#ifdef USE_WINSOCK
+ /* WinSock select() must not be called with an fd_set that contains zero
+ fd flags, or it will return WSAEINVAL. But, it also can't be called
+ with no fd_sets at all! From the documentation:
+
+ Any two of the parameters, readfds, writefds, or exceptfds, can be
+ given as null. At least one must be non-null, and any non-null
+ descriptor set must contain at least one handle to a socket.
+
+ It is unclear why WinSock doesn't just handle this for us instead of
+ calling this an error. Luckily, with WinSock, we can _also_ ask how
+ many bits are set on an fd_set. So, let's just check it beforehand.
+ */
+ return select((int)maxfd + 1,
+ fds_read && fds_read->fd_count ? fds_read : NULL,
+ fds_write && fds_write->fd_count ? fds_write : NULL,
+ fds_err && fds_err->fd_count ? fds_err : NULL, ptimeout);
+#else
+ return select((int)maxfd + 1, fds_read, fds_write, fds_err, ptimeout);
+#endif
+}
+
+#endif
+
+/*
+ * Wait for read or write events on a set of file descriptors. It uses poll()
+ * when a fine poll() is available, in order to avoid limits with FD_SETSIZE,
+ * otherwise select() is used. An error is returned if select() is being used
+ * and a file descriptor is too large for FD_SETSIZE.
+ *
+ * A negative timeout value makes this function wait indefinitely,
+ * unless no valid file descriptor is given, when this happens the
+ * negative timeout is ignored and the function times out immediately.
+ *
+ * Return values:
+ * -1 = system call error or fd >= FD_SETSIZE
+ * 0 = timeout
+ * [bitmask] = action as described below
+ *
+ * CURL_CSELECT_IN - first socket is readable
+ * CURL_CSELECT_IN2 - second socket is readable
+ * CURL_CSELECT_OUT - write socket is writable
+ * CURL_CSELECT_ERR - an error condition occurred
+ */
+int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */
+ curl_socket_t readfd1,
+ curl_socket_t writefd, /* socket to write to */
+ timediff_t timeout_ms) /* milliseconds to wait */
+{
+ struct pollfd pfd[3];
+ int num;
+ int r;
+
+ if((readfd0 == CURL_SOCKET_BAD) && (readfd1 == CURL_SOCKET_BAD) &&
+ (writefd == CURL_SOCKET_BAD)) {
+ /* no sockets, just wait */
+ return Curl_wait_ms(timeout_ms);
+ }
+
+ /* Avoid initial timestamp, avoid Curl_now() call, when elapsed
+ time in this function does not need to be measured. This happens
+ when function is called with a zero timeout or a negative timeout
+ value indicating a blocking call should be performed. */
+
+ num = 0;
+ if(readfd0 != CURL_SOCKET_BAD) {
+ pfd[num].fd = readfd0;
+ pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
+ pfd[num].revents = 0;
+ num++;
+ }
+ if(readfd1 != CURL_SOCKET_BAD) {
+ pfd[num].fd = readfd1;
+ pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
+ pfd[num].revents = 0;
+ num++;
+ }
+ if(writefd != CURL_SOCKET_BAD) {
+ pfd[num].fd = writefd;
+ pfd[num].events = POLLWRNORM|POLLOUT|POLLPRI;
+ pfd[num].revents = 0;
+ num++;
+ }
+
+ r = Curl_poll(pfd, num, timeout_ms);
+ if(r <= 0)
+ return r;
+
+ r = 0;
+ num = 0;
+ if(readfd0 != CURL_SOCKET_BAD) {
+ if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP))
+ r |= CURL_CSELECT_IN;
+ if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL))
+ r |= CURL_CSELECT_ERR;
+ num++;
+ }
+ if(readfd1 != CURL_SOCKET_BAD) {
+ if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP))
+ r |= CURL_CSELECT_IN2;
+ if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL))
+ r |= CURL_CSELECT_ERR;
+ num++;
+ }
+ if(writefd != CURL_SOCKET_BAD) {
+ if(pfd[num].revents & (POLLWRNORM|POLLOUT))
+ r |= CURL_CSELECT_OUT;
+ if(pfd[num].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL))
+ r |= CURL_CSELECT_ERR;
+ }
+
+ return r;
+}
+
+/*
+ * This is a wrapper around poll(). If poll() does not exist, then
+ * select() is used instead. An error is returned if select() is
+ * being used and a file descriptor is too large for FD_SETSIZE.
+ * A negative timeout value makes this function wait indefinitely,
+ * unless no valid file descriptor is given, when this happens the
+ * negative timeout is ignored and the function times out immediately.
+ *
+ * Return values:
+ * -1 = system call error or fd >= FD_SETSIZE
+ * 0 = timeout
+ * N = number of structures with non zero revent fields
+ */
+int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms)
+{
+#ifdef HAVE_POLL_FINE
+ int pending_ms;
+#else
+ fd_set fds_read;
+ fd_set fds_write;
+ fd_set fds_err;
+ curl_socket_t maxfd;
+#endif
+ bool fds_none = TRUE;
+ unsigned int i;
+ int r;
+
+ if(ufds) {
+ for(i = 0; i < nfds; i++) {
+ if(ufds[i].fd != CURL_SOCKET_BAD) {
+ fds_none = FALSE;
+ break;
+ }
+ }
+ }
+ if(fds_none) {
+ /* no sockets, just wait */
+ return Curl_wait_ms(timeout_ms);
+ }
+
+ /* Avoid initial timestamp, avoid Curl_now() call, when elapsed
+ time in this function does not need to be measured. This happens
+ when function is called with a zero timeout or a negative timeout
+ value indicating a blocking call should be performed. */
+
+#ifdef HAVE_POLL_FINE
+
+ /* prevent overflow, timeout_ms is typecast to int. */
+#if TIMEDIFF_T_MAX > INT_MAX
+ if(timeout_ms > INT_MAX)
+ timeout_ms = INT_MAX;
+#endif
+ if(timeout_ms > 0)
+ pending_ms = (int)timeout_ms;
+ else if(timeout_ms < 0)
+ pending_ms = -1;
+ else
+ pending_ms = 0;
+ r = poll(ufds, nfds, pending_ms);
+ if(r <= 0) {
+ if((r == -1) && (SOCKERRNO == EINTR))
+ /* make EINTR from select or poll not a "lethal" error */
+ r = 0;
+ return r;
+ }
+
+ for(i = 0; i < nfds; i++) {
+ if(ufds[i].fd == CURL_SOCKET_BAD)
+ continue;
+ if(ufds[i].revents & POLLHUP)
+ ufds[i].revents |= POLLIN;
+ if(ufds[i].revents & POLLERR)
+ ufds[i].revents |= POLLIN|POLLOUT;
+ }
+
+#else /* HAVE_POLL_FINE */
+
+ FD_ZERO(&fds_read);
+ FD_ZERO(&fds_write);
+ FD_ZERO(&fds_err);
+ maxfd = (curl_socket_t)-1;
+
+ for(i = 0; i < nfds; i++) {
+ ufds[i].revents = 0;
+ if(ufds[i].fd == CURL_SOCKET_BAD)
+ continue;
+ VERIFY_SOCK(ufds[i].fd);
+ if(ufds[i].events & (POLLIN|POLLOUT|POLLPRI|
+ POLLRDNORM|POLLWRNORM|POLLRDBAND)) {
+ if(ufds[i].fd > maxfd)
+ maxfd = ufds[i].fd;
+ if(ufds[i].events & (POLLRDNORM|POLLIN))
+ FD_SET(ufds[i].fd, &fds_read);
+ if(ufds[i].events & (POLLWRNORM|POLLOUT))
+ FD_SET(ufds[i].fd, &fds_write);
+ if(ufds[i].events & (POLLRDBAND|POLLPRI))
+ FD_SET(ufds[i].fd, &fds_err);
+ }
+ }
+
+ /*
+ Note also that WinSock ignores the first argument, so we don't worry
+ about the fact that maxfd is computed incorrectly with WinSock (since
+ curl_socket_t is unsigned in such cases and thus -1 is the largest
+ value).
+ */
+ r = our_select(maxfd, &fds_read, &fds_write, &fds_err, timeout_ms);
+ if(r <= 0) {
+ if((r == -1) && (SOCKERRNO == EINTR))
+ /* make EINTR from select or poll not a "lethal" error */
+ r = 0;
+ return r;
+ }
+
+ r = 0;
+ for(i = 0; i < nfds; i++) {
+ ufds[i].revents = 0;
+ if(ufds[i].fd == CURL_SOCKET_BAD)
+ continue;
+ if(FD_ISSET(ufds[i].fd, &fds_read)) {
+ if(ufds[i].events & POLLRDNORM)
+ ufds[i].revents |= POLLRDNORM;
+ if(ufds[i].events & POLLIN)
+ ufds[i].revents |= POLLIN;
+ }
+ if(FD_ISSET(ufds[i].fd, &fds_write)) {
+ if(ufds[i].events & POLLWRNORM)
+ ufds[i].revents |= POLLWRNORM;
+ if(ufds[i].events & POLLOUT)
+ ufds[i].revents |= POLLOUT;
+ }
+ if(FD_ISSET(ufds[i].fd, &fds_err)) {
+ if(ufds[i].events & POLLRDBAND)
+ ufds[i].revents |= POLLRDBAND;
+ if(ufds[i].events & POLLPRI)
+ ufds[i].revents |= POLLPRI;
+ }
+ if(ufds[i].revents)
+ r++;
+ }
+
+#endif /* HAVE_POLL_FINE */
+
+ return r;
+}
diff --git a/libs/libcurl/src/select.h b/libs/libcurl/src/select.h index f2cf8bbd9f..439b936ae1 100644 --- a/libs/libcurl/src/select.h +++ b/libs/libcurl/src/select.h @@ -1,114 +1,114 @@ -#ifndef HEADER_CURL_SELECT_H -#define HEADER_CURL_SELECT_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_POLL_H -#include <poll.h> -#elif defined(HAVE_SYS_POLL_H) -#include <sys/poll.h> -#endif - -/* - * Definition of pollfd struct and constants for platforms lacking them. - */ - -#if !defined(HAVE_SYS_POLL_H) && \ - !defined(HAVE_POLL_H) && \ - !defined(POLLIN) - -#define POLLIN 0x01 -#define POLLPRI 0x02 -#define POLLOUT 0x04 -#define POLLERR 0x08 -#define POLLHUP 0x10 -#define POLLNVAL 0x20 - -struct pollfd -{ - curl_socket_t fd; - short events; - short revents; -}; - -#endif - -#ifndef POLLRDNORM -#define POLLRDNORM POLLIN -#endif - -#ifndef POLLWRNORM -#define POLLWRNORM POLLOUT -#endif - -#ifndef POLLRDBAND -#define POLLRDBAND POLLPRI -#endif - -/* there are three CSELECT defines that are defined in the public header that - are exposed to users, but this *IN2 bit is only ever used internally and - therefore defined here */ -#define CURL_CSELECT_IN2 (CURL_CSELECT_ERR << 1) - -int Curl_socket_check(curl_socket_t readfd, curl_socket_t readfd2, - curl_socket_t writefd, - timediff_t timeout_ms); -#define SOCKET_READABLE(x,z) \ - Curl_socket_check(x, CURL_SOCKET_BAD, CURL_SOCKET_BAD, z) -#define SOCKET_WRITABLE(x,z) \ - Curl_socket_check(CURL_SOCKET_BAD, CURL_SOCKET_BAD, x, z) - -int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms); -int Curl_wait_ms(timediff_t timeout_ms); - -/* - With Winsock the valid range is [0..INVALID_SOCKET-1] according to - https://docs.microsoft.com/en-us/windows/win32/winsock/socket-data-type-2 -*/ -#ifdef USE_WINSOCK -#define VALID_SOCK(s) ((s) < INVALID_SOCKET) -#define FDSET_SOCK(x) 1 -#define VERIFY_SOCK(x) do { \ - if(!VALID_SOCK(x)) { \ - SET_SOCKERRNO(WSAEINVAL); \ - return -1; \ - } \ -} while(0) -#else -#define VALID_SOCK(s) ((s) >= 0) - -/* If the socket is small enough to get set or read from an fdset */ -#define FDSET_SOCK(s) ((s) < FD_SETSIZE) - -#define VERIFY_SOCK(x) do { \ - if(!VALID_SOCK(x) || !FDSET_SOCK(x)) { \ - SET_SOCKERRNO(EINVAL); \ - return -1; \ - } \ - } while(0) -#endif - -#endif /* HEADER_CURL_SELECT_H */ +#ifndef HEADER_CURL_SELECT_H
+#define HEADER_CURL_SELECT_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_POLL_H
+#include <poll.h>
+#elif defined(HAVE_SYS_POLL_H)
+#include <sys/poll.h>
+#endif
+
+/*
+ * Definition of pollfd struct and constants for platforms lacking them.
+ */
+
+#if !defined(HAVE_SYS_POLL_H) && \
+ !defined(HAVE_POLL_H) && \
+ !defined(POLLIN)
+
+#define POLLIN 0x01
+#define POLLPRI 0x02
+#define POLLOUT 0x04
+#define POLLERR 0x08
+#define POLLHUP 0x10
+#define POLLNVAL 0x20
+
+struct pollfd
+{
+ curl_socket_t fd;
+ short events;
+ short revents;
+};
+
+#endif
+
+#ifndef POLLRDNORM
+#define POLLRDNORM POLLIN
+#endif
+
+#ifndef POLLWRNORM
+#define POLLWRNORM POLLOUT
+#endif
+
+#ifndef POLLRDBAND
+#define POLLRDBAND POLLPRI
+#endif
+
+/* there are three CSELECT defines that are defined in the public header that
+ are exposed to users, but this *IN2 bit is only ever used internally and
+ therefore defined here */
+#define CURL_CSELECT_IN2 (CURL_CSELECT_ERR << 1)
+
+int Curl_socket_check(curl_socket_t readfd, curl_socket_t readfd2,
+ curl_socket_t writefd,
+ timediff_t timeout_ms);
+#define SOCKET_READABLE(x,z) \
+ Curl_socket_check(x, CURL_SOCKET_BAD, CURL_SOCKET_BAD, z)
+#define SOCKET_WRITABLE(x,z) \
+ Curl_socket_check(CURL_SOCKET_BAD, CURL_SOCKET_BAD, x, z)
+
+int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms);
+int Curl_wait_ms(timediff_t timeout_ms);
+
+/*
+ With Winsock the valid range is [0..INVALID_SOCKET-1] according to
+ https://docs.microsoft.com/en-us/windows/win32/winsock/socket-data-type-2
+*/
+#ifdef USE_WINSOCK
+#define VALID_SOCK(s) ((s) < INVALID_SOCKET)
+#define FDSET_SOCK(x) 1
+#define VERIFY_SOCK(x) do { \
+ if(!VALID_SOCK(x)) { \
+ SET_SOCKERRNO(WSAEINVAL); \
+ return -1; \
+ } \
+} while(0)
+#else
+#define VALID_SOCK(s) ((s) >= 0)
+
+/* If the socket is small enough to get set or read from an fdset */
+#define FDSET_SOCK(s) ((s) < FD_SETSIZE)
+
+#define VERIFY_SOCK(x) do { \
+ if(!VALID_SOCK(x) || !FDSET_SOCK(x)) { \
+ SET_SOCKERRNO(EINVAL); \
+ return -1; \
+ } \
+ } while(0)
+#endif
+
+#endif /* HEADER_CURL_SELECT_H */
diff --git a/libs/libcurl/src/sendf.c b/libs/libcurl/src/sendf.c index d26b7e7cd7..dabb1078bf 100644 --- a/libs/libcurl/src/sendf.c +++ b/libs/libcurl/src/sendf.c @@ -1,756 +1,781 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_LINUX_TCP_H -#include <linux/tcp.h> -#elif defined(HAVE_NETINET_TCP_H) -#include <netinet/tcp.h> -#endif - -#include <curl/curl.h> - -#include "urldata.h" -#include "sendf.h" -#include "connect.h" -#include "vtls/vtls.h" -#include "vssh/ssh.h" -#include "easyif.h" -#include "multiif.h" -#include "strerror.h" -#include "select.h" -#include "strdup.h" -#include "http2.h" -#include "headers.h" -#include "ws.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#if defined(CURL_DO_LINEEND_CONV) && !defined(CURL_DISABLE_FTP) -/* - * convert_lineends() changes CRLF (\r\n) end-of-line markers to a single LF - * (\n), with special processing for CRLF sequences that are split between two - * blocks of data. Remaining, bare CRs are changed to LFs. The possibly new - * size of the data is returned. - */ -static size_t convert_lineends(struct Curl_easy *data, - char *startPtr, size_t size) -{ - char *inPtr, *outPtr; - - /* sanity check */ - if(!startPtr || (size < 1)) { - return size; - } - - if(data->state.prev_block_had_trailing_cr) { - /* The previous block of incoming data - had a trailing CR, which was turned into a LF. */ - if(*startPtr == '\n') { - /* This block of incoming data starts with the - previous block's LF so get rid of it */ - memmove(startPtr, startPtr + 1, size-1); - size--; - /* and it wasn't a bare CR but a CRLF conversion instead */ - data->state.crlf_conversions++; - } - data->state.prev_block_had_trailing_cr = FALSE; /* reset the flag */ - } - - /* find 1st CR, if any */ - inPtr = outPtr = memchr(startPtr, '\r', size); - if(inPtr) { - /* at least one CR, now look for CRLF */ - while(inPtr < (startPtr + size-1)) { - /* note that it's size-1, so we'll never look past the last byte */ - if(memcmp(inPtr, "\r\n", 2) == 0) { - /* CRLF found, bump past the CR and copy the NL */ - inPtr++; - *outPtr = *inPtr; - /* keep track of how many CRLFs we converted */ - data->state.crlf_conversions++; - } - else { - if(*inPtr == '\r') { - /* lone CR, move LF instead */ - *outPtr = '\n'; - } - else { - /* not a CRLF nor a CR, just copy whatever it is */ - *outPtr = *inPtr; - } - } - outPtr++; - inPtr++; - } /* end of while loop */ - - if(inPtr < startPtr + size) { - /* handle last byte */ - if(*inPtr == '\r') { - /* deal with a CR at the end of the buffer */ - *outPtr = '\n'; /* copy a NL instead */ - /* note that a CRLF might be split across two blocks */ - data->state.prev_block_had_trailing_cr = TRUE; - } - else { - /* copy last byte */ - *outPtr = *inPtr; - } - outPtr++; - } - if(outPtr < startPtr + size) - /* tidy up by null terminating the now shorter data */ - *outPtr = '\0'; - - return (outPtr - startPtr); - } - return size; -} -#endif /* CURL_DO_LINEEND_CONV && !CURL_DISABLE_FTP */ - -#ifdef USE_RECV_BEFORE_SEND_WORKAROUND -bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex) -{ - struct postponed_data * const psnd = &(conn->postponed[sockindex]); - return psnd->buffer && psnd->allocated_size && - psnd->recv_size > psnd->recv_processed; -} - -static CURLcode pre_receive_plain(struct Curl_easy *data, - struct connectdata *conn, int num) -{ - const curl_socket_t sockfd = conn->sock[num]; - struct postponed_data * const psnd = &(conn->postponed[num]); - size_t bytestorecv = psnd->allocated_size - psnd->recv_size; - /* WinSock will destroy unread received data if send() is - failed. - To avoid lossage of received data, recv() must be - performed before every send() if any incoming data is - available. However, skip this, if buffer is already full. */ - if((conn->handler->protocol&PROTO_FAMILY_HTTP) != 0 && - conn->recv[num] == Curl_recv_plain && - (!psnd->buffer || bytestorecv)) { - const int readymask = Curl_socket_check(sockfd, CURL_SOCKET_BAD, - CURL_SOCKET_BAD, 0); - if(readymask != -1 && (readymask & CURL_CSELECT_IN) != 0) { - /* Have some incoming data */ - if(!psnd->buffer) { - /* Use buffer double default size for intermediate buffer */ - psnd->allocated_size = 2 * data->set.buffer_size; - psnd->buffer = malloc(psnd->allocated_size); - if(!psnd->buffer) - return CURLE_OUT_OF_MEMORY; - psnd->recv_size = 0; - psnd->recv_processed = 0; -#ifdef DEBUGBUILD - psnd->bindsock = sockfd; /* Used only for DEBUGASSERT */ -#endif /* DEBUGBUILD */ - bytestorecv = psnd->allocated_size; - } - if(psnd->buffer) { - ssize_t recvedbytes; - DEBUGASSERT(psnd->bindsock == sockfd); - recvedbytes = sread(sockfd, psnd->buffer + psnd->recv_size, - bytestorecv); - if(recvedbytes > 0) - psnd->recv_size += recvedbytes; - } - else - psnd->allocated_size = 0; - } - } - return CURLE_OK; -} - -static ssize_t get_pre_recved(struct connectdata *conn, int num, char *buf, - size_t len) -{ - struct postponed_data * const psnd = &(conn->postponed[num]); - size_t copysize; - if(!psnd->buffer) - return 0; - - DEBUGASSERT(psnd->allocated_size > 0); - DEBUGASSERT(psnd->recv_size <= psnd->allocated_size); - DEBUGASSERT(psnd->recv_processed <= psnd->recv_size); - /* Check and process data that already received and storied in internal - intermediate buffer */ - if(psnd->recv_size > psnd->recv_processed) { - DEBUGASSERT(psnd->bindsock == conn->sock[num]); - copysize = CURLMIN(len, psnd->recv_size - psnd->recv_processed); - memcpy(buf, psnd->buffer + psnd->recv_processed, copysize); - psnd->recv_processed += copysize; - } - else - copysize = 0; /* buffer was allocated, but nothing was received */ - - /* Free intermediate buffer if it has no unprocessed data */ - if(psnd->recv_processed == psnd->recv_size) { - free(psnd->buffer); - psnd->buffer = NULL; - psnd->allocated_size = 0; - psnd->recv_size = 0; - psnd->recv_processed = 0; -#ifdef DEBUGBUILD - psnd->bindsock = CURL_SOCKET_BAD; -#endif /* DEBUGBUILD */ - } - return (ssize_t)copysize; -} -#else /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ -/* Use "do-nothing" macros instead of functions when workaround not used */ -bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex) -{ - (void)conn; - (void)sockindex; - return false; -} -#define pre_receive_plain(d,c,n) CURLE_OK -#define get_pre_recved(c,n,b,l) 0 -#endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ - -/* Curl_infof() is for info message along the way */ -#define MAXINFO 2048 - -void Curl_infof(struct Curl_easy *data, const char *fmt, ...) -{ - DEBUGASSERT(!strchr(fmt, '\n')); - if(data && data->set.verbose) { - va_list ap; - int len; - char buffer[MAXINFO + 2]; - va_start(ap, fmt); - len = mvsnprintf(buffer, MAXINFO, fmt, ap); - va_end(ap); - buffer[len++] = '\n'; - buffer[len] = '\0'; - Curl_debug(data, CURLINFO_TEXT, buffer, len); - } -} - -/* 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; - int len; - char error[CURL_ERROR_SIZE + 2]; - va_start(ap, fmt); - len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap); - - if(data->set.errorbuffer && !data->state.errorbuf) { - strcpy(data->set.errorbuffer, error); - data->state.errorbuf = TRUE; /* wrote error string */ - } - error[len++] = '\n'; - error[len] = '\0'; - Curl_debug(data, CURLINFO_TEXT, error, len); - va_end(ap); - } -} - -/* - * Curl_write() is an internal write function that sends data to the - * server. Works with plain sockets, SCP, SSL or kerberos. - * - * If the write would block (CURLE_AGAIN), we return CURLE_OK and - * (*written == 0). Otherwise we return regular CURLcode value. - */ -CURLcode Curl_write(struct Curl_easy *data, - curl_socket_t sockfd, - const void *mem, - size_t len, - ssize_t *written) -{ - ssize_t bytes_written; - CURLcode result = CURLE_OK; - struct connectdata *conn; - int num; - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - conn = data->conn; - num = (sockfd == conn->sock[SECONDARYSOCKET]); - -#ifdef CURLDEBUG - { - /* Allow debug builds to override this logic to force short sends - */ - char *p = getenv("CURL_SMALLSENDS"); - if(p) { - size_t altsize = (size_t)strtoul(p, NULL, 10); - if(altsize) - len = CURLMIN(len, altsize); - } - } -#endif - bytes_written = conn->send[num](data, num, mem, len, &result); - - *written = bytes_written; - if(bytes_written >= 0) - /* we completely ignore the curlcode value when subzero is not returned */ - return CURLE_OK; - - /* handle CURLE_AGAIN or a send failure */ - switch(result) { - case CURLE_AGAIN: - *written = 0; - return CURLE_OK; - - case CURLE_OK: - /* general send failure */ - return CURLE_SEND_ERROR; - - default: - /* we got a specific curlcode, forward it */ - return result; - } -} - -ssize_t Curl_send_plain(struct Curl_easy *data, int num, - const void *mem, size_t len, CURLcode *code) -{ - struct connectdata *conn; - curl_socket_t sockfd; - ssize_t bytes_written; - - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - conn = data->conn; - sockfd = conn->sock[num]; - /* WinSock will destroy unread received data if send() is - failed. - To avoid lossage of received data, recv() must be - performed before every send() if any incoming data is - available. */ - if(pre_receive_plain(data, conn, num)) { - *code = CURLE_OUT_OF_MEMORY; - return -1; - } - -#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */ - if(conn->bits.tcp_fastopen) { - bytes_written = sendto(sockfd, mem, len, MSG_FASTOPEN, - conn->ip_addr->ai_addr, conn->ip_addr->ai_addrlen); - conn->bits.tcp_fastopen = FALSE; - } - else -#endif - bytes_written = swrite(sockfd, mem, len); - - *code = CURLE_OK; - if(-1 == bytes_written) { - int err = SOCKERRNO; - - if( -#ifdef WSAEWOULDBLOCK - /* This is how Windows does it */ - (WSAEWOULDBLOCK == err) -#else - /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned - due to its inability to send off data without blocking. We therefore - treat both error codes the same here */ - (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err) || - (EINPROGRESS == err) -#endif - ) { - /* this is just a case of EWOULDBLOCK */ - bytes_written = 0; - *code = CURLE_AGAIN; - } - else { - char buffer[STRERROR_LEN]; - failf(data, "Send failure: %s", - Curl_strerror(err, buffer, sizeof(buffer))); - data->state.os_errno = err; - *code = CURLE_SEND_ERROR; - } - } - return bytes_written; -} - -/* - * Curl_write_plain() is an internal write function that sends data to the - * server using plain sockets only. Otherwise meant to have the exact same - * proto as Curl_write() - */ -CURLcode Curl_write_plain(struct Curl_easy *data, - curl_socket_t sockfd, - const void *mem, - size_t len, - ssize_t *written) -{ - CURLcode result; - struct connectdata *conn = data->conn; - int num; - DEBUGASSERT(conn); - num = (sockfd == conn->sock[SECONDARYSOCKET]); - - *written = Curl_send_plain(data, num, mem, len, &result); - - return result; -} - -ssize_t Curl_recv_plain(struct Curl_easy *data, int num, char *buf, - size_t len, CURLcode *code) -{ - struct connectdata *conn; - curl_socket_t sockfd; - ssize_t nread; - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - conn = data->conn; - sockfd = conn->sock[num]; - /* Check and return data that already received and storied in internal - intermediate buffer */ - nread = get_pre_recved(conn, num, buf, len); - if(nread > 0) { - *code = CURLE_OK; - return nread; - } - - nread = sread(sockfd, buf, len); - - *code = CURLE_OK; - if(-1 == nread) { - int err = SOCKERRNO; - - if( -#ifdef WSAEWOULDBLOCK - /* This is how Windows does it */ - (WSAEWOULDBLOCK == err) -#else - /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned - due to its inability to send off data without blocking. We therefore - treat both error codes the same here */ - (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err) -#endif - ) { - /* this is just a case of EWOULDBLOCK */ - *code = CURLE_AGAIN; - } - else { - char buffer[STRERROR_LEN]; - failf(data, "Recv failure: %s", - Curl_strerror(err, buffer, sizeof(buffer))); - data->state.os_errno = err; - *code = CURLE_RECV_ERROR; - } - } - return nread; -} - -static CURLcode pausewrite(struct Curl_easy *data, - int type, /* what type of data */ - const char *ptr, - size_t len) -{ - /* signalled to pause sending on this connection, but since we have data - we want to send we need to dup it to save a copy for when the sending - is again enabled */ - struct SingleRequest *k = &data->req; - struct UrlState *s = &data->state; - unsigned int i; - bool newtype = TRUE; - - /* If this transfers over HTTP/2, pause the stream! */ - Curl_http2_stream_pause(data, TRUE); - - if(s->tempcount) { - for(i = 0; i< s->tempcount; i++) { - if(s->tempwrite[i].type == type) { - /* data for this type exists */ - newtype = FALSE; - break; - } - } - DEBUGASSERT(i < 3); - if(i >= 3) - /* There are more types to store than what fits: very bad */ - return CURLE_OUT_OF_MEMORY; - } - else - i = 0; - - if(newtype) { - /* store this information in the state struct for later use */ - Curl_dyn_init(&s->tempwrite[i].b, DYN_PAUSE_BUFFER); - s->tempwrite[i].type = type; - s->tempcount++; - } - - if(Curl_dyn_addn(&s->tempwrite[i].b, (unsigned char *)ptr, len)) - return CURLE_OUT_OF_MEMORY; - - /* mark the connection as RECV paused */ - k->keepon |= KEEP_RECV_PAUSE; - - return CURLE_OK; -} - - -/* chop_write() writes chunks of data not larger than CURL_MAX_WRITE_SIZE via - * client write callback(s) and takes care of pause requests from the - * callbacks. - */ -static CURLcode chop_write(struct Curl_easy *data, - int type, - char *optr, - size_t olen) -{ - struct connectdata *conn = data->conn; - curl_write_callback writeheader = NULL; - curl_write_callback writebody = NULL; - char *ptr = optr; - size_t len = olen; - void *writebody_ptr = data->set.out; - - if(!len) - return CURLE_OK; - - /* If reading is paused, append this data to the already held data for this - type. */ - if(data->req.keepon & KEEP_RECV_PAUSE) - return pausewrite(data, type, ptr, len); - - /* Determine the callback(s) to use. */ - if(type & CLIENTWRITE_BODY) { -#ifdef USE_WEBSOCKETS - if(conn->handler->protocol & (CURLPROTO_WS|CURLPROTO_WSS)) { - struct HTTP *ws = data->req.p.http; - writebody = Curl_ws_writecb; - ws->ws.data = data; - writebody_ptr = ws; - } - else -#endif - writebody = data->set.fwrite_func; - } - if((type & CLIENTWRITE_HEADER) && - (data->set.fwrite_header || data->set.writeheader)) { - /* - * Write headers to the same callback or to the especially setup - * header callback function (added after version 7.7.1). - */ - writeheader = - data->set.fwrite_header? data->set.fwrite_header: data->set.fwrite_func; - } - - /* Chop data, write chunks. */ - while(len) { - size_t chunklen = len <= CURL_MAX_WRITE_SIZE? len: CURL_MAX_WRITE_SIZE; - - if(writebody) { - size_t wrote; - Curl_set_in_callback(data, true); - wrote = writebody(ptr, 1, chunklen, writebody_ptr); - Curl_set_in_callback(data, false); - - if(CURL_WRITEFUNC_PAUSE == wrote) { - if(conn->handler->flags & PROTOPT_NONETWORK) { - /* Protocols that work without network cannot be paused. This is - actually only FILE:// just now, and it can't pause since the - transfer isn't done using the "normal" procedure. */ - failf(data, "Write callback asked for PAUSE when not supported"); - return CURLE_WRITE_ERROR; - } - return pausewrite(data, type, ptr, len); - } - if(wrote != chunklen) { - failf(data, "Failure writing output to destination"); - return CURLE_WRITE_ERROR; - } - } - - ptr += chunklen; - len -= chunklen; - } - -#ifndef CURL_DISABLE_HTTP - /* HTTP header, but not status-line */ - if((conn->handler->protocol & PROTO_FAMILY_HTTP) && - (type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS) ) { - unsigned char htype = (unsigned char) - (type & CLIENTWRITE_CONNECT ? CURLH_CONNECT : - (type & CLIENTWRITE_1XX ? CURLH_1XX : - (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER : - CURLH_HEADER))); - CURLcode result = Curl_headers_push(data, optr, htype); - if(result) - return result; - } -#endif - - if(writeheader) { - size_t wrote; - - Curl_set_in_callback(data, true); - wrote = writeheader(optr, 1, olen, data->set.writeheader); - Curl_set_in_callback(data, false); - - if(CURL_WRITEFUNC_PAUSE == wrote) - /* here we pass in the HEADER bit only since if this was body as well - then it was passed already and clearly that didn't trigger the - pause, so this is saved for later with the HEADER bit only */ - return pausewrite(data, CLIENTWRITE_HEADER | - (type & (CLIENTWRITE_STATUS|CLIENTWRITE_CONNECT| - CLIENTWRITE_1XX|CLIENTWRITE_TRAILER)), - optr, olen); - if(wrote != olen) { - failf(data, "Failed writing header"); - return CURLE_WRITE_ERROR; - } - } - - return CURLE_OK; -} - - -/* Curl_client_write() sends data to the write callback(s) - - The bit pattern defines to what "streams" to write to. Body and/or header. - The defines are in sendf.h of course. - - If CURL_DO_LINEEND_CONV is enabled, data is converted IN PLACE to the - local character encoding. This is a problem and should be changed in - the future to leave the original data alone. - */ -CURLcode Curl_client_write(struct Curl_easy *data, - int type, - char *ptr, - size_t len) -{ -#if !defined(CURL_DISABLE_FTP) && defined(CURL_DO_LINEEND_CONV) - /* FTP data may need conversion. */ - if((type & CLIENTWRITE_BODY) && - (data->conn->handler->protocol & PROTO_FAMILY_FTP) && - data->conn->proto.ftpc.transfertype == 'A') { - /* convert end-of-line markers */ - len = convert_lineends(data, ptr, len); - } -#endif - return chop_write(data, type, ptr, len); -} - -CURLcode Curl_read_plain(curl_socket_t sockfd, - char *buf, - size_t bytesfromsocket, - ssize_t *n) -{ - ssize_t nread = sread(sockfd, buf, bytesfromsocket); - - if(-1 == nread) { - const int err = SOCKERRNO; - const bool return_error = -#ifdef USE_WINSOCK - WSAEWOULDBLOCK == err -#else - EWOULDBLOCK == err || EAGAIN == err || EINTR == err -#endif - ; - *n = 0; /* no data returned */ - if(return_error) - return CURLE_AGAIN; - return CURLE_RECV_ERROR; - } - - *n = nread; - return CURLE_OK; -} - -/* - * Internal read-from-socket function. This is meant to deal with plain - * sockets, SSL sockets and kerberos sockets. - * - * Returns a regular CURLcode value. - */ -CURLcode Curl_read(struct Curl_easy *data, /* transfer */ - curl_socket_t sockfd, /* read from this socket */ - char *buf, /* store read data here */ - size_t sizerequested, /* max amount to read */ - ssize_t *n) /* amount bytes read */ -{ - CURLcode result = CURLE_RECV_ERROR; - ssize_t nread = 0; - size_t bytesfromsocket = 0; - char *buffertofill = NULL; - struct connectdata *conn = data->conn; - - /* Set 'num' to 0 or 1, depending on which socket that has been sent here. - If it is the second socket, we set num to 1. Otherwise to 0. This lets - us use the correct ssl handle. */ - int num = (sockfd == conn->sock[SECONDARYSOCKET]); - - *n = 0; /* reset amount to zero */ - - bytesfromsocket = CURLMIN(sizerequested, (size_t)data->set.buffer_size); - buffertofill = buf; - - nread = conn->recv[num](data, num, buffertofill, bytesfromsocket, &result); - if(nread < 0) - return result; - - *n += nread; - - return CURLE_OK; -} - -/* return 0 on success */ -void Curl_debug(struct Curl_easy *data, curl_infotype type, - char *ptr, size_t size) -{ - if(data->set.verbose) { - static const char s_infotype[CURLINFO_END][3] = { - "* ", "< ", "> ", "{ ", "} ", "{ ", "} " }; - if(data->set.fdebug) { - bool inCallback = Curl_is_in_callback(data); - Curl_set_in_callback(data, true); - (void)(*data->set.fdebug)(data, type, 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: - fwrite(s_infotype[type], 2, 1, data->set.err); - fwrite(ptr, size, 1, data->set.err); - break; - default: /* nada */ - break; - } - } - } -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_LINUX_TCP_H
+#include <linux/tcp.h>
+#elif defined(HAVE_NETINET_TCP_H)
+#include <netinet/tcp.h>
+#endif
+
+#include <curl/curl.h>
+
+#include "urldata.h"
+#include "sendf.h"
+#include "cfilters.h"
+#include "connect.h"
+#include "vtls/vtls.h"
+#include "vssh/ssh.h"
+#include "easyif.h"
+#include "multiif.h"
+#include "strerror.h"
+#include "select.h"
+#include "strdup.h"
+#include "http2.h"
+#include "headers.h"
+#include "ws.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#if defined(CURL_DO_LINEEND_CONV) && !defined(CURL_DISABLE_FTP)
+/*
+ * convert_lineends() changes CRLF (\r\n) end-of-line markers to a single LF
+ * (\n), with special processing for CRLF sequences that are split between two
+ * blocks of data. Remaining, bare CRs are changed to LFs. The possibly new
+ * size of the data is returned.
+ */
+static size_t convert_lineends(struct Curl_easy *data,
+ char *startPtr, size_t size)
+{
+ char *inPtr, *outPtr;
+
+ /* sanity check */
+ if(!startPtr || (size < 1)) {
+ return size;
+ }
+
+ if(data->state.prev_block_had_trailing_cr) {
+ /* The previous block of incoming data
+ had a trailing CR, which was turned into a LF. */
+ if(*startPtr == '\n') {
+ /* This block of incoming data starts with the
+ previous block's LF so get rid of it */
+ memmove(startPtr, startPtr + 1, size-1);
+ size--;
+ /* and it wasn't a bare CR but a CRLF conversion instead */
+ data->state.crlf_conversions++;
+ }
+ data->state.prev_block_had_trailing_cr = FALSE; /* reset the flag */
+ }
+
+ /* find 1st CR, if any */
+ inPtr = outPtr = memchr(startPtr, '\r', size);
+ if(inPtr) {
+ /* at least one CR, now look for CRLF */
+ while(inPtr < (startPtr + size-1)) {
+ /* note that it's size-1, so we'll never look past the last byte */
+ if(memcmp(inPtr, "\r\n", 2) == 0) {
+ /* CRLF found, bump past the CR and copy the NL */
+ inPtr++;
+ *outPtr = *inPtr;
+ /* keep track of how many CRLFs we converted */
+ data->state.crlf_conversions++;
+ }
+ else {
+ if(*inPtr == '\r') {
+ /* lone CR, move LF instead */
+ *outPtr = '\n';
+ }
+ else {
+ /* not a CRLF nor a CR, just copy whatever it is */
+ *outPtr = *inPtr;
+ }
+ }
+ outPtr++;
+ inPtr++;
+ } /* end of while loop */
+
+ if(inPtr < startPtr + size) {
+ /* handle last byte */
+ if(*inPtr == '\r') {
+ /* deal with a CR at the end of the buffer */
+ *outPtr = '\n'; /* copy a NL instead */
+ /* note that a CRLF might be split across two blocks */
+ data->state.prev_block_had_trailing_cr = TRUE;
+ }
+ else {
+ /* copy last byte */
+ *outPtr = *inPtr;
+ }
+ outPtr++;
+ }
+ if(outPtr < startPtr + size)
+ /* tidy up by null terminating the now shorter data */
+ *outPtr = '\0';
+
+ return (outPtr - startPtr);
+ }
+ return size;
+}
+#endif /* CURL_DO_LINEEND_CONV && !CURL_DISABLE_FTP */
+
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex)
+{
+ struct postponed_data * const psnd = &(conn->postponed[sockindex]);
+ return psnd->buffer && psnd->allocated_size &&
+ psnd->recv_size > psnd->recv_processed;
+}
+
+static CURLcode pre_receive_plain(struct Curl_easy *data,
+ struct connectdata *conn, int num)
+{
+ const curl_socket_t sockfd = conn->sock[num];
+ struct postponed_data * const psnd = &(conn->postponed[num]);
+ size_t bytestorecv = psnd->allocated_size - psnd->recv_size;
+ ssize_t recvedbytes;
+
+ /* WinSock will destroy unread received data if send() is
+ failed.
+ To avoid lossage of received data, recv() must be
+ performed before every send() if any incoming data is
+ available. However, skip this, if buffer is already full. */
+ if((conn->handler->protocol&PROTO_FAMILY_HTTP) != 0 &&
+ conn->recv[num] == Curl_conn_recv &&
+ (!psnd->buffer || bytestorecv)) {
+ const int readymask = Curl_socket_check(sockfd, CURL_SOCKET_BAD,
+ CURL_SOCKET_BAD, 0);
+ if(readymask != -1 && (readymask & CURL_CSELECT_IN) != 0) {
+ /* Have some incoming data */
+ if(!psnd->buffer) {
+ /* Use buffer double default size for intermediate buffer */
+ psnd->allocated_size = 2 * data->set.buffer_size;
+ psnd->buffer = malloc(psnd->allocated_size);
+ if(!psnd->buffer)
+ return CURLE_OUT_OF_MEMORY;
+ psnd->recv_size = 0;
+ psnd->recv_processed = 0;
+#ifdef DEBUGBUILD
+ psnd->bindsock = sockfd; /* Used only for DEBUGASSERT */
+#endif /* DEBUGBUILD */
+ bytestorecv = psnd->allocated_size;
+ }
+
+ DEBUGASSERT(psnd->bindsock == sockfd);
+ recvedbytes = sread(sockfd, psnd->buffer + psnd->recv_size,
+ bytestorecv);
+ if(recvedbytes > 0)
+ psnd->recv_size += recvedbytes;
+ }
+ }
+ return CURLE_OK;
+}
+
+static ssize_t get_pre_recved(struct connectdata *conn, int num, char *buf,
+ size_t len)
+{
+ struct postponed_data * const psnd = &(conn->postponed[num]);
+ size_t copysize;
+ if(!psnd->buffer)
+ return 0;
+
+ DEBUGASSERT(psnd->allocated_size > 0);
+ DEBUGASSERT(psnd->recv_size <= psnd->allocated_size);
+ DEBUGASSERT(psnd->recv_processed <= psnd->recv_size);
+ /* Check and process data that already received and storied in internal
+ intermediate buffer */
+ if(psnd->recv_size > psnd->recv_processed) {
+ DEBUGASSERT(psnd->bindsock == conn->sock[num]);
+ copysize = CURLMIN(len, psnd->recv_size - psnd->recv_processed);
+ memcpy(buf, psnd->buffer + psnd->recv_processed, copysize);
+ psnd->recv_processed += copysize;
+ }
+ else
+ copysize = 0; /* buffer was allocated, but nothing was received */
+
+ /* Free intermediate buffer if it has no unprocessed data */
+ if(psnd->recv_processed == psnd->recv_size) {
+ free(psnd->buffer);
+ psnd->buffer = NULL;
+ psnd->allocated_size = 0;
+ psnd->recv_size = 0;
+ psnd->recv_processed = 0;
+#ifdef DEBUGBUILD
+ psnd->bindsock = CURL_SOCKET_BAD;
+#endif /* DEBUGBUILD */
+ }
+ return (ssize_t)copysize;
+}
+#else /* ! USE_RECV_BEFORE_SEND_WORKAROUND */
+/* Use "do-nothing" macros instead of functions when workaround not used */
+bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex)
+{
+ (void)conn;
+ (void)sockindex;
+ return false;
+}
+#define pre_receive_plain(d,c,n) CURLE_OK
+#define get_pre_recved(c,n,b,l) 0
+#endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */
+
+/* Curl_infof() is for info message along the way */
+#define MAXINFO 2048
+
+void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
+{
+ DEBUGASSERT(!strchr(fmt, '\n'));
+ if(data && data->set.verbose) {
+ va_list ap;
+ int len;
+ char buffer[MAXINFO + 2];
+ va_start(ap, fmt);
+ len = mvsnprintf(buffer, MAXINFO, fmt, ap);
+ va_end(ap);
+ buffer[len++] = '\n';
+ buffer[len] = '\0';
+ Curl_debug(data, CURLINFO_TEXT, buffer, len);
+ }
+}
+
+/* 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;
+ int len;
+ char error[CURL_ERROR_SIZE + 2];
+ va_start(ap, fmt);
+ len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
+
+ if(data->set.errorbuffer && !data->state.errorbuf) {
+ strcpy(data->set.errorbuffer, error);
+ data->state.errorbuf = TRUE; /* wrote error string */
+ }
+ error[len++] = '\n';
+ error[len] = '\0';
+ Curl_debug(data, CURLINFO_TEXT, error, len);
+ va_end(ap);
+ }
+}
+
+/*
+ * Curl_write() is an internal write function that sends data to the
+ * server. Works with plain sockets, SCP, SSL or kerberos.
+ *
+ * If the write would block (CURLE_AGAIN), we return CURLE_OK and
+ * (*written == 0). Otherwise we return regular CURLcode value.
+ */
+CURLcode Curl_write(struct Curl_easy *data,
+ curl_socket_t sockfd,
+ const void *mem,
+ size_t len,
+ ssize_t *written)
+{
+ ssize_t bytes_written;
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn;
+ int num;
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->conn);
+ conn = data->conn;
+ num = (sockfd == conn->sock[SECONDARYSOCKET]);
+
+#ifdef CURLDEBUG
+ {
+ /* Allow debug builds to override this logic to force short sends
+ */
+ char *p = getenv("CURL_SMALLSENDS");
+ if(p) {
+ size_t altsize = (size_t)strtoul(p, NULL, 10);
+ if(altsize)
+ len = CURLMIN(len, altsize);
+ }
+ }
+#endif
+ bytes_written = conn->send[num](data, num, mem, len, &result);
+
+ *written = bytes_written;
+ if(bytes_written >= 0)
+ /* we completely ignore the curlcode value when subzero is not returned */
+ return CURLE_OK;
+
+ /* handle CURLE_AGAIN or a send failure */
+ switch(result) {
+ case CURLE_AGAIN:
+ *written = 0;
+ return CURLE_OK;
+
+ case CURLE_OK:
+ /* general send failure */
+ return CURLE_SEND_ERROR;
+
+ default:
+ /* we got a specific curlcode, forward it */
+ return result;
+ }
+}
+
+/* Curl_send_plain sends raw data without a size restriction on 'len'. */
+ssize_t Curl_send_plain(struct Curl_easy *data, int num,
+ const void *mem, size_t len, CURLcode *code)
+{
+ struct connectdata *conn;
+ curl_socket_t sockfd;
+ ssize_t bytes_written;
+
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->conn);
+ conn = data->conn;
+ sockfd = conn->sock[num];
+ /* WinSock will destroy unread received data if send() is
+ failed.
+ To avoid lossage of received data, recv() must be
+ performed before every send() if any incoming data is
+ available. */
+ if(pre_receive_plain(data, conn, num)) {
+ *code = CURLE_OUT_OF_MEMORY;
+ return -1;
+ }
+
+#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */
+ if(conn->bits.tcp_fastopen) {
+ bytes_written = sendto(sockfd, mem, len, MSG_FASTOPEN,
+ conn->ip_addr->ai_addr, conn->ip_addr->ai_addrlen);
+ conn->bits.tcp_fastopen = FALSE;
+ }
+ else
+#endif
+ bytes_written = swrite(sockfd, mem, len);
+
+ *code = CURLE_OK;
+ if(-1 == bytes_written) {
+ int err = SOCKERRNO;
+
+ if(
+#ifdef WSAEWOULDBLOCK
+ /* This is how Windows does it */
+ (WSAEWOULDBLOCK == err)
+#else
+ /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
+ due to its inability to send off data without blocking. We therefore
+ treat both error codes the same here */
+ (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err) ||
+ (EINPROGRESS == err)
+#endif
+ ) {
+ /* this is just a case of EWOULDBLOCK */
+ *code = CURLE_AGAIN;
+ }
+ else {
+ char buffer[STRERROR_LEN];
+ failf(data, "Send failure: %s",
+ Curl_strerror(err, buffer, sizeof(buffer)));
+ data->state.os_errno = err;
+ *code = CURLE_SEND_ERROR;
+ }
+ }
+ return bytes_written;
+}
+
+/*
+ * Curl_write_plain() is an internal write function that sends data to the
+ * server using plain sockets only. Otherwise meant to have the exact same
+ * proto as Curl_write().
+ *
+ * This function wraps Curl_send_plain(). The only difference besides the
+ * prototype is '*written' (bytes written) is set to 0 on error.
+ * 'sockfd' must be one of the connection's two main sockets and the value of
+ * 'len' must not be changed.
+ */
+CURLcode Curl_write_plain(struct Curl_easy *data,
+ curl_socket_t sockfd,
+ const void *mem,
+ size_t len,
+ ssize_t *written)
+{
+ CURLcode result;
+ struct connectdata *conn = data->conn;
+ int num;
+ DEBUGASSERT(conn);
+ DEBUGASSERT(sockfd == conn->sock[FIRSTSOCKET] ||
+ sockfd == conn->sock[SECONDARYSOCKET]);
+ if(sockfd != conn->sock[FIRSTSOCKET] &&
+ sockfd != conn->sock[SECONDARYSOCKET])
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ num = (sockfd == conn->sock[SECONDARYSOCKET]);
+
+ *written = Curl_send_plain(data, num, mem, len, &result);
+ if(*written == -1)
+ *written = 0;
+
+ return result;
+}
+
+/* Curl_recv_plain receives raw data without a size restriction on 'len'. */
+ssize_t Curl_recv_plain(struct Curl_easy *data, int num, char *buf,
+ size_t len, CURLcode *code)
+{
+ struct connectdata *conn;
+ curl_socket_t sockfd;
+ ssize_t nread;
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->conn);
+ conn = data->conn;
+ sockfd = conn->sock[num];
+ /* Check and return data that already received and storied in internal
+ intermediate buffer */
+ nread = get_pre_recved(conn, num, buf, len);
+ if(nread > 0) {
+ *code = CURLE_OK;
+ return nread;
+ }
+
+ nread = sread(sockfd, buf, len);
+
+ *code = CURLE_OK;
+ if(-1 == nread) {
+ int err = SOCKERRNO;
+
+ if(
+#ifdef WSAEWOULDBLOCK
+ /* This is how Windows does it */
+ (WSAEWOULDBLOCK == err)
+#else
+ /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
+ due to its inability to send off data without blocking. We therefore
+ treat both error codes the same here */
+ (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err)
+#endif
+ ) {
+ /* this is just a case of EWOULDBLOCK */
+ *code = CURLE_AGAIN;
+ }
+ else {
+ char buffer[STRERROR_LEN];
+ failf(data, "Recv failure: %s",
+ Curl_strerror(err, buffer, sizeof(buffer)));
+ data->state.os_errno = err;
+ *code = CURLE_RECV_ERROR;
+ }
+ }
+ return nread;
+}
+
+static CURLcode pausewrite(struct Curl_easy *data,
+ int type, /* what type of data */
+ const char *ptr,
+ size_t len)
+{
+ /* signalled to pause sending on this connection, but since we have data
+ we want to send we need to dup it to save a copy for when the sending
+ is again enabled */
+ struct SingleRequest *k = &data->req;
+ struct UrlState *s = &data->state;
+ unsigned int i;
+ bool newtype = TRUE;
+
+ /* If this transfers over HTTP/2, pause the stream! */
+ Curl_http2_stream_pause(data, TRUE);
+
+ if(s->tempcount) {
+ for(i = 0; i< s->tempcount; i++) {
+ if(s->tempwrite[i].type == type) {
+ /* data for this type exists */
+ newtype = FALSE;
+ break;
+ }
+ }
+ DEBUGASSERT(i < 3);
+ if(i >= 3)
+ /* There are more types to store than what fits: very bad */
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else
+ i = 0;
+
+ if(newtype) {
+ /* store this information in the state struct for later use */
+ Curl_dyn_init(&s->tempwrite[i].b, DYN_PAUSE_BUFFER);
+ s->tempwrite[i].type = type;
+ s->tempcount++;
+ }
+
+ if(Curl_dyn_addn(&s->tempwrite[i].b, (unsigned char *)ptr, len))
+ return CURLE_OUT_OF_MEMORY;
+
+ /* mark the connection as RECV paused */
+ k->keepon |= KEEP_RECV_PAUSE;
+
+ return CURLE_OK;
+}
+
+
+/* chop_write() writes chunks of data not larger than CURL_MAX_WRITE_SIZE via
+ * client write callback(s) and takes care of pause requests from the
+ * callbacks.
+ */
+static CURLcode chop_write(struct Curl_easy *data,
+ int type,
+ char *optr,
+ size_t olen)
+{
+ struct connectdata *conn = data->conn;
+ curl_write_callback writeheader = NULL;
+ curl_write_callback writebody = NULL;
+ char *ptr = optr;
+ size_t len = olen;
+ void *writebody_ptr = data->set.out;
+
+ if(!len)
+ return CURLE_OK;
+
+ /* If reading is paused, append this data to the already held data for this
+ type. */
+ if(data->req.keepon & KEEP_RECV_PAUSE)
+ return pausewrite(data, type, ptr, len);
+
+ /* Determine the callback(s) to use. */
+ if(type & CLIENTWRITE_BODY) {
+#ifdef USE_WEBSOCKETS
+ if(conn->handler->protocol & (CURLPROTO_WS|CURLPROTO_WSS)) {
+ struct HTTP *ws = data->req.p.http;
+ writebody = Curl_ws_writecb;
+ ws->ws.data = data;
+ writebody_ptr = ws;
+ }
+ else
+#endif
+ writebody = data->set.fwrite_func;
+ }
+ if((type & CLIENTWRITE_HEADER) &&
+ (data->set.fwrite_header || data->set.writeheader)) {
+ /*
+ * Write headers to the same callback or to the especially setup
+ * header callback function (added after version 7.7.1).
+ */
+ writeheader =
+ data->set.fwrite_header? data->set.fwrite_header: data->set.fwrite_func;
+ }
+
+ /* Chop data, write chunks. */
+ while(len) {
+ size_t chunklen = len <= CURL_MAX_WRITE_SIZE? len: CURL_MAX_WRITE_SIZE;
+
+ if(writebody) {
+ size_t wrote;
+ Curl_set_in_callback(data, true);
+ wrote = writebody(ptr, 1, chunklen, writebody_ptr);
+ Curl_set_in_callback(data, false);
+
+ if(CURL_WRITEFUNC_PAUSE == wrote) {
+ if(conn->handler->flags & PROTOPT_NONETWORK) {
+ /* Protocols that work without network cannot be paused. This is
+ actually only FILE:// just now, and it can't pause since the
+ transfer isn't done using the "normal" procedure. */
+ failf(data, "Write callback asked for PAUSE when not supported");
+ return CURLE_WRITE_ERROR;
+ }
+ return pausewrite(data, type, ptr, len);
+ }
+ if(wrote != chunklen) {
+ failf(data, "Failure writing output to destination");
+ return CURLE_WRITE_ERROR;
+ }
+ }
+
+ ptr += chunklen;
+ len -= chunklen;
+ }
+
+#ifndef CURL_DISABLE_HTTP
+ /* HTTP header, but not status-line */
+ if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
+ (type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS) ) {
+ unsigned char htype = (unsigned char)
+ (type & CLIENTWRITE_CONNECT ? CURLH_CONNECT :
+ (type & CLIENTWRITE_1XX ? CURLH_1XX :
+ (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER :
+ CURLH_HEADER)));
+ CURLcode result = Curl_headers_push(data, optr, htype);
+ if(result)
+ return result;
+ }
+#endif
+
+ if(writeheader) {
+ size_t wrote;
+
+ Curl_set_in_callback(data, true);
+ wrote = writeheader(optr, 1, olen, data->set.writeheader);
+ Curl_set_in_callback(data, false);
+
+ if(CURL_WRITEFUNC_PAUSE == wrote)
+ /* here we pass in the HEADER bit only since if this was body as well
+ then it was passed already and clearly that didn't trigger the
+ pause, so this is saved for later with the HEADER bit only */
+ return pausewrite(data, CLIENTWRITE_HEADER |
+ (type & (CLIENTWRITE_STATUS|CLIENTWRITE_CONNECT|
+ CLIENTWRITE_1XX|CLIENTWRITE_TRAILER)),
+ optr, olen);
+ if(wrote != olen) {
+ failf(data, "Failed writing header");
+ return CURLE_WRITE_ERROR;
+ }
+ }
+
+ return CURLE_OK;
+}
+
+
+/* Curl_client_write() sends data to the write callback(s)
+
+ The bit pattern defines to what "streams" to write to. Body and/or header.
+ The defines are in sendf.h of course.
+
+ If CURL_DO_LINEEND_CONV is enabled, data is converted IN PLACE to the
+ local character encoding. This is a problem and should be changed in
+ the future to leave the original data alone.
+ */
+CURLcode Curl_client_write(struct Curl_easy *data,
+ int type,
+ char *ptr,
+ size_t len)
+{
+#if !defined(CURL_DISABLE_FTP) && defined(CURL_DO_LINEEND_CONV)
+ /* FTP data may need conversion. */
+ if((type & CLIENTWRITE_BODY) &&
+ (data->conn->handler->protocol & PROTO_FAMILY_FTP) &&
+ data->conn->proto.ftpc.transfertype == 'A') {
+ /* convert end-of-line markers */
+ len = convert_lineends(data, ptr, len);
+ }
+#endif
+ return chop_write(data, type, ptr, len);
+}
+
+/*
+ * Curl_read_plain() is an internal read function that reads data from the
+ * server using plain sockets only. Otherwise meant to have the exact same
+ * proto as Curl_read().
+ *
+ * This function wraps Curl_recv_plain(). The only difference besides the
+ * prototype is '*n' (bytes read) is set to 0 on error.
+ * 'sockfd' must be one of the connection's two main sockets and the value of
+ * 'sizerequested' must not be changed.
+ */
+CURLcode Curl_read_plain(struct Curl_easy *data, /* transfer */
+ curl_socket_t sockfd, /* read from this socket */
+ char *buf, /* store read data here */
+ size_t sizerequested, /* max amount to read */
+ ssize_t *n) /* amount bytes read */
+{
+ CURLcode result;
+ struct connectdata *conn = data->conn;
+ int num;
+ DEBUGASSERT(conn);
+ DEBUGASSERT(sockfd == conn->sock[FIRSTSOCKET] ||
+ sockfd == conn->sock[SECONDARYSOCKET]);
+ if(sockfd != conn->sock[FIRSTSOCKET] &&
+ sockfd != conn->sock[SECONDARYSOCKET])
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ num = (sockfd == conn->sock[SECONDARYSOCKET]);
+
+ *n = Curl_recv_plain(data, num, buf, sizerequested, &result);
+ if(*n == -1)
+ *n = 0;
+
+ return result;
+}
+
+/*
+ * Internal read-from-socket function. This is meant to deal with plain
+ * sockets, SSL sockets and kerberos sockets.
+ *
+ * Returns a regular CURLcode value.
+ */
+CURLcode Curl_read(struct Curl_easy *data, /* transfer */
+ curl_socket_t sockfd, /* read from this socket */
+ char *buf, /* store read data here */
+ size_t sizerequested, /* max amount to read */
+ ssize_t *n) /* amount bytes read */
+{
+ CURLcode result = CURLE_RECV_ERROR;
+ ssize_t nread = 0;
+ size_t bytesfromsocket = 0;
+ char *buffertofill = NULL;
+ struct connectdata *conn = data->conn;
+
+ /* Set 'num' to 0 or 1, depending on which socket that has been sent here.
+ If it is the second socket, we set num to 1. Otherwise to 0. This lets
+ us use the correct ssl handle. */
+ int num = (sockfd == conn->sock[SECONDARYSOCKET]);
+
+ *n = 0; /* reset amount to zero */
+
+ bytesfromsocket = CURLMIN(sizerequested, (size_t)data->set.buffer_size);
+ buffertofill = buf;
+
+ nread = conn->recv[num](data, num, buffertofill, bytesfromsocket, &result);
+ if(nread < 0)
+ goto out;
+
+ *n += nread;
+ result = CURLE_OK;
+out:
+ /* DEBUGF(infof(data, "Curl_read(handle=%p) -> %d, nread=%ld",
+ data, result, nread)); */
+ return result;
+}
+
+/* return 0 on success */
+void Curl_debug(struct Curl_easy *data, curl_infotype type,
+ char *ptr, size_t size)
+{
+ if(data->set.verbose) {
+ static const char s_infotype[CURLINFO_END][3] = {
+ "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
+ if(data->set.fdebug) {
+ bool inCallback = Curl_is_in_callback(data);
+ Curl_set_in_callback(data, true);
+ (void)(*data->set.fdebug)(data, type, 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:
+ fwrite(s_infotype[type], 2, 1, data->set.err);
+ fwrite(ptr, size, 1, data->set.err);
+ break;
+ default: /* nada */
+ break;
+ }
+ }
+ }
+}
diff --git a/libs/libcurl/src/sendf.h b/libs/libcurl/src/sendf.h index 7c4c1280a0..4d14129698 100644 --- a/libs/libcurl/src/sendf.h +++ b/libs/libcurl/src/sendf.h @@ -1,96 +1,97 @@ -#ifndef HEADER_CURL_SENDF_H -#define HEADER_CURL_SENDF_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -void Curl_infof(struct Curl_easy *, const char *fmt, ...); -void Curl_failf(struct Curl_easy *, const char *fmt, ...); - -#if defined(CURL_DISABLE_VERBOSE_STRINGS) - -#if defined(HAVE_VARIADIC_MACROS_C99) -#define infof(...) Curl_nop_stmt -#elif defined(HAVE_VARIADIC_MACROS_GCC) -#define infof(x...) Curl_nop_stmt -#else -#error "missing VARIADIC macro define, fix and rebuild!" -#endif - -#else /* CURL_DISABLE_VERBOSE_STRINGS */ - -#define infof Curl_infof - -#endif /* CURL_DISABLE_VERBOSE_STRINGS */ - -#define failf Curl_failf - -#define CLIENTWRITE_BODY (1<<0) -#define CLIENTWRITE_HEADER (1<<1) -#define CLIENTWRITE_STATUS (1<<2) /* the first "header" is the status line */ -#define CLIENTWRITE_CONNECT (1<<3) /* a CONNECT response */ -#define CLIENTWRITE_1XX (1<<4) /* a 1xx response */ -#define CLIENTWRITE_TRAILER (1<<5) /* a trailer header */ -#define CLIENTWRITE_BOTH (CLIENTWRITE_BODY|CLIENTWRITE_HEADER) - -CURLcode Curl_client_write(struct Curl_easy *data, int type, char *ptr, - size_t len) WARN_UNUSED_RESULT; - -bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex); - -/* internal read-function, does plain socket only */ -CURLcode Curl_read_plain(curl_socket_t sockfd, - char *buf, - size_t bytesfromsocket, - ssize_t *n); - -ssize_t Curl_recv_plain(struct Curl_easy *data, int num, char *buf, - size_t len, CURLcode *code); -ssize_t Curl_send_plain(struct Curl_easy *data, int num, - const void *mem, size_t len, CURLcode *code); - -/* internal read-function, does plain socket, SSL and krb4 */ -CURLcode Curl_read(struct Curl_easy *data, curl_socket_t sockfd, - char *buf, size_t buffersize, - ssize_t *n); - -/* internal write-function, does plain socket, SSL, SCP, SFTP and krb4 */ -CURLcode Curl_write(struct Curl_easy *data, - curl_socket_t sockfd, - const void *mem, size_t len, - ssize_t *written); - -/* internal write-function, does plain sockets ONLY */ -CURLcode Curl_write_plain(struct Curl_easy *data, - curl_socket_t sockfd, - const void *mem, size_t len, - ssize_t *written); - -/* the function used to output verbose information */ -void Curl_debug(struct Curl_easy *data, curl_infotype type, - char *ptr, size_t size); - - -#endif /* HEADER_CURL_SENDF_H */ +#ifndef HEADER_CURL_SENDF_H
+#define HEADER_CURL_SENDF_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+void Curl_infof(struct Curl_easy *, const char *fmt, ...);
+void Curl_failf(struct Curl_easy *, const char *fmt, ...);
+
+#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+
+#if defined(HAVE_VARIADIC_MACROS_C99)
+#define infof(...) Curl_nop_stmt
+#elif defined(HAVE_VARIADIC_MACROS_GCC)
+#define infof(x...) Curl_nop_stmt
+#else
+#error "missing VARIADIC macro define, fix and rebuild!"
+#endif
+
+#else /* CURL_DISABLE_VERBOSE_STRINGS */
+
+#define infof Curl_infof
+
+#endif /* CURL_DISABLE_VERBOSE_STRINGS */
+
+#define failf Curl_failf
+
+#define CLIENTWRITE_BODY (1<<0)
+#define CLIENTWRITE_HEADER (1<<1)
+#define CLIENTWRITE_STATUS (1<<2) /* the first "header" is the status line */
+#define CLIENTWRITE_CONNECT (1<<3) /* a CONNECT response */
+#define CLIENTWRITE_1XX (1<<4) /* a 1xx response */
+#define CLIENTWRITE_TRAILER (1<<5) /* a trailer header */
+#define CLIENTWRITE_BOTH (CLIENTWRITE_BODY|CLIENTWRITE_HEADER)
+
+CURLcode Curl_client_write(struct Curl_easy *data, int type, char *ptr,
+ size_t len) WARN_UNUSED_RESULT;
+
+bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex);
+
+/* internal read-function, does plain socket only */
+CURLcode Curl_read_plain(struct Curl_easy *data,
+ curl_socket_t sockfd,
+ char *buf,
+ size_t sizerequested,
+ ssize_t *n);
+
+ssize_t Curl_recv_plain(struct Curl_easy *data, int num, char *buf,
+ size_t len, CURLcode *code);
+ssize_t Curl_send_plain(struct Curl_easy *data, int num,
+ const void *mem, size_t len, CURLcode *code);
+
+/* internal read-function, does plain socket, SSL and krb4 */
+CURLcode Curl_read(struct Curl_easy *data, curl_socket_t sockfd,
+ char *buf, size_t buffersize,
+ ssize_t *n);
+
+/* internal write-function, does plain socket, SSL, SCP, SFTP and krb4 */
+CURLcode Curl_write(struct Curl_easy *data,
+ curl_socket_t sockfd,
+ const void *mem, size_t len,
+ ssize_t *written);
+
+/* internal write-function, does plain sockets ONLY */
+CURLcode Curl_write_plain(struct Curl_easy *data,
+ curl_socket_t sockfd,
+ const void *mem, size_t len,
+ ssize_t *written);
+
+/* the function used to output verbose information */
+void Curl_debug(struct Curl_easy *data, curl_infotype type,
+ char *ptr, size_t size);
+
+
+#endif /* HEADER_CURL_SENDF_H */
diff --git a/libs/libcurl/src/setopt.c b/libs/libcurl/src/setopt.c index 5b5975485c..7c99b7c0f5 100644 --- a/libs/libcurl/src/setopt.c +++ b/libs/libcurl/src/setopt.c @@ -1,3142 +1,3154 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 <limits.h> - -#ifdef HAVE_NETINET_IN_H -#include <netinet/in.h> -#endif - -#ifdef HAVE_LINUX_TCP_H -#include <linux/tcp.h> -#elif defined(HAVE_NETINET_TCP_H) -#include <netinet/tcp.h> -#endif - -#include "urldata.h" -#include "url.h" -#include "progress.h" -#include "content_encoding.h" -#include "strcase.h" -#include "share.h" -#include "vtls/vtls.h" -#include "warnless.h" -#include "sendf.h" -#include "http2.h" -#include "setopt.h" -#include "multiif.h" -#include "altsvc.h" -#include "hsts.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -CURLcode Curl_setstropt(char **charp, const char *s) -{ - /* Release the previous storage at `charp' and replace by a dynamic storage - copy of `s'. Return CURLE_OK or CURLE_OUT_OF_MEMORY. */ - - Curl_safefree(*charp); - - if(s) { - if(strlen(s) > CURL_MAX_INPUT_LENGTH) - return CURLE_BAD_FUNCTION_ARGUMENT; - - *charp = strdup(s); - if(!*charp) - return CURLE_OUT_OF_MEMORY; - } - - return CURLE_OK; -} - -CURLcode Curl_setblobopt(struct curl_blob **blobp, - const struct curl_blob *blob) -{ - /* free the previous storage at `blobp' and replace by a dynamic storage - copy of blob. If CURL_BLOB_COPY is set, the data is copied. */ - - Curl_safefree(*blobp); - - if(blob) { - struct curl_blob *nblob; - if(blob->len > CURL_MAX_INPUT_LENGTH) - return CURLE_BAD_FUNCTION_ARGUMENT; - nblob = (struct curl_blob *) - malloc(sizeof(struct curl_blob) + - ((blob->flags & CURL_BLOB_COPY) ? blob->len : 0)); - if(!nblob) - return CURLE_OUT_OF_MEMORY; - *nblob = *blob; - if(blob->flags & CURL_BLOB_COPY) { - /* put the data after the blob struct in memory */ - nblob->data = (char *)nblob + sizeof(struct curl_blob); - memcpy(nblob->data, blob->data, blob->len); - } - - *blobp = nblob; - return CURLE_OK; - } - - return CURLE_OK; -} - -static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp) -{ - CURLcode result = CURLE_OK; - char *user = NULL; - char *passwd = NULL; - - /* Parse the login details if specified. It not then we treat NULL as a hint - to clear the existing data */ - if(option) { - result = Curl_parse_login_details(option, strlen(option), - (userp ? &user : NULL), - (passwdp ? &passwd : NULL), - NULL); - } - - if(!result) { - /* Store the username part of option if required */ - if(userp) { - if(!user && option && option[0] == ':') { - /* Allocate an empty string instead of returning NULL as user name */ - user = strdup(""); - if(!user) - result = CURLE_OUT_OF_MEMORY; - } - - Curl_safefree(*userp); - *userp = user; - } - - /* Store the password part of option if required */ - if(passwdp) { - Curl_safefree(*passwdp); - *passwdp = passwd; - } - } - - return result; -} - -#define C_SSLVERSION_VALUE(x) (x & 0xffff) -#define C_SSLVERSION_MAX_VALUE(x) (x & 0xffff0000) - -static CURLcode protocol2num(const char *str, curl_prot_t *val) -{ - if(!str) - return CURLE_BAD_FUNCTION_ARGUMENT; - - if(curl_strequal(str, "all")) { - *val = ~(curl_prot_t) 0; - return CURLE_OK; - } - - *val = 0; - - do { - const char *token = str; - size_t tlen; - - str = strchr(str, ','); - tlen = str? (size_t) (str - token): strlen(token); - if(tlen) { - const struct Curl_handler *h = Curl_builtin_scheme(token, tlen); - - if(!h) - return CURLE_UNSUPPORTED_PROTOCOL; - - *val |= h->protocol; - } - } while(str++); - - if(!*val) - /* no protocol listed */ - return CURLE_BAD_FUNCTION_ARGUMENT; - return CURLE_OK; -} - -/* - * Do not make Curl_vsetopt() static: it is called from - * packages/OS400/ccsidcurl.c. - */ -CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) -{ - char *argptr; - CURLcode result = CURLE_OK; - long arg; - unsigned long uarg; - curl_off_t bigsize; - - switch(option) { - case CURLOPT_DNS_CACHE_TIMEOUT: - arg = va_arg(param, long); - if(arg < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - else if(arg > INT_MAX) - arg = INT_MAX; - - data->set.dns_cache_timeout = (int)arg; - break; - case CURLOPT_DNS_USE_GLOBAL_CACHE: - /* deprecated */ - break; - case CURLOPT_SSL_CIPHER_LIST: - /* set a list of cipher we want to use in the SSL connection */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER_LIST], - va_arg(param, char *)); - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSL_CIPHER_LIST: - /* set a list of cipher we want to use in the SSL connection for proxy */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER_LIST_PROXY], - va_arg(param, char *)); - break; -#endif - case CURLOPT_TLS13_CIPHERS: - if(Curl_ssl_tls13_ciphersuites()) { - /* set preferred list of TLS 1.3 cipher suites */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER13_LIST], - va_arg(param, char *)); - } - else - return CURLE_NOT_BUILT_IN; - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_TLS13_CIPHERS: - if(Curl_ssl_tls13_ciphersuites()) { - /* set preferred list of TLS 1.3 cipher suites for proxy */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER13_LIST_PROXY], - va_arg(param, char *)); - } - else - return CURLE_NOT_BUILT_IN; - break; -#endif - case CURLOPT_RANDOM_FILE: - break; - case CURLOPT_EGDSOCKET: - break; - case CURLOPT_MAXCONNECTS: - /* - * Set the absolute number of maximum simultaneous alive connection that - * libcurl is allowed to have. - */ - arg = va_arg(param, long); - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.maxconnects = arg; - break; - case CURLOPT_FORBID_REUSE: - /* - * When this transfer is done, it must not be left to be reused by a - * subsequent transfer but shall be closed immediately. - */ - data->set.reuse_forbid = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_FRESH_CONNECT: - /* - * This transfer shall not use a previously cached connection but - * should be made with a fresh new connect! - */ - data->set.reuse_fresh = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_VERBOSE: - /* - * Verbose means infof() calls that give a lot of information about - * the connection and transfer procedures as well as internal choices. - */ - data->set.verbose = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_HEADER: - /* - * Set to include the header in the general data output stream. - */ - data->set.include_header = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_NOPROGRESS: - /* - * Shut off the internal supported progress meter - */ - data->set.hide_progress = (0 != va_arg(param, long)) ? TRUE : FALSE; - if(data->set.hide_progress) - data->progress.flags |= PGRS_HIDE; - else - data->progress.flags &= ~PGRS_HIDE; - break; - case CURLOPT_NOBODY: - /* - * Do not include the body part in the output data stream. - */ - data->set.opt_no_body = (0 != va_arg(param, long)) ? TRUE : FALSE; -#ifndef CURL_DISABLE_HTTP - if(data->set.opt_no_body) - /* in HTTP lingo, no body means using the HEAD request... */ - data->set.method = HTTPREQ_HEAD; - else if(data->set.method == HTTPREQ_HEAD) - data->set.method = HTTPREQ_GET; -#endif - break; - case CURLOPT_FAILONERROR: - /* - * Don't output the >=400 error code HTML-page, but instead only - * return error. - */ - data->set.http_fail_on_error = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_KEEP_SENDING_ON_ERROR: - data->set.http_keep_sending_on_error = (0 != va_arg(param, long)) ? - TRUE : FALSE; - break; - case CURLOPT_UPLOAD: - case CURLOPT_PUT: - /* - * We want to sent data to the remote host. If this is HTTP, that equals - * using the PUT request. - */ - data->set.upload = (0 != va_arg(param, long)) ? TRUE : FALSE; - if(data->set.upload) { - /* If this is HTTP, PUT is what's needed to "upload" */ - data->set.method = HTTPREQ_PUT; - data->set.opt_no_body = FALSE; /* this is implied */ - } - else - /* In HTTP, the opposite of upload is GET (unless NOBODY is true as - then this can be changed to HEAD later on) */ - data->set.method = HTTPREQ_GET; - break; - case CURLOPT_REQUEST_TARGET: - result = Curl_setstropt(&data->set.str[STRING_TARGET], - va_arg(param, char *)); - break; - case CURLOPT_FILETIME: - /* - * Try to get the file time of the remote document. The time will - * later (possibly) become available using curl_easy_getinfo(). - */ - data->set.get_filetime = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_SERVER_RESPONSE_TIMEOUT: - /* - * Option that specifies how quickly a server response must be obtained - * before it is considered failure. For pingpong protocols. - */ - arg = va_arg(param, long); - if((arg >= 0) && (arg <= (INT_MAX/1000))) - data->set.server_response_timeout = (unsigned int)arg * 1000; - else - return CURLE_BAD_FUNCTION_ARGUMENT; - break; -#ifndef CURL_DISABLE_TFTP - case CURLOPT_TFTP_NO_OPTIONS: - /* - * Option that prevents libcurl from sending TFTP option requests to the - * server. - */ - data->set.tftp_no_options = va_arg(param, long) != 0; - break; - case CURLOPT_TFTP_BLKSIZE: - /* - * TFTP option that specifies the block size to use for data transmission. - */ - arg = va_arg(param, long); - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.tftp_blksize = arg; - break; -#endif -#ifndef CURL_DISABLE_NETRC - case CURLOPT_NETRC: - /* - * Parse the $HOME/.netrc file - */ - arg = va_arg(param, long); - if((arg < CURL_NETRC_IGNORED) || (arg >= CURL_NETRC_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.use_netrc = (unsigned char)arg; - break; - case CURLOPT_NETRC_FILE: - /* - * Use this file instead of the $HOME/.netrc file - */ - result = Curl_setstropt(&data->set.str[STRING_NETRC_FILE], - va_arg(param, char *)); - break; -#endif - case CURLOPT_TRANSFERTEXT: - /* - * This option was previously named 'FTPASCII'. Renamed to work with - * more protocols than merely FTP. - * - * Transfer using ASCII (instead of BINARY). - */ - data->set.prefer_ascii = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_TIMECONDITION: - /* - * Set HTTP time condition. This must be one of the defines in the - * curl/curl.h header file. - */ - arg = va_arg(param, long); - if((arg < CURL_TIMECOND_NONE) || (arg >= CURL_TIMECOND_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.timecondition = (curl_TimeCond)arg; - break; - case CURLOPT_TIMEVALUE: - /* - * This is the value to compare with the remote document with the - * method set with CURLOPT_TIMECONDITION - */ - data->set.timevalue = (time_t)va_arg(param, long); - break; - - case CURLOPT_TIMEVALUE_LARGE: - /* - * This is the value to compare with the remote document with the - * method set with CURLOPT_TIMECONDITION - */ - data->set.timevalue = (time_t)va_arg(param, curl_off_t); - break; - - case CURLOPT_SSLVERSION: -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSLVERSION: -#endif - /* - * Set explicit SSL version to try to connect with, as some SSL - * implementations are lame. - */ -#ifdef USE_SSL - { - long version, version_max; - struct ssl_primary_config *primary = &data->set.ssl.primary; -#ifndef CURL_DISABLE_PROXY - if(option != CURLOPT_SSLVERSION) - primary = &data->set.proxy_ssl.primary; -#endif - - arg = va_arg(param, long); - - version = C_SSLVERSION_VALUE(arg); - version_max = C_SSLVERSION_MAX_VALUE(arg); - - if(version < CURL_SSLVERSION_DEFAULT || - version == CURL_SSLVERSION_SSLv2 || - version == CURL_SSLVERSION_SSLv3 || - version >= CURL_SSLVERSION_LAST || - version_max < CURL_SSLVERSION_MAX_NONE || - version_max >= CURL_SSLVERSION_MAX_LAST) - return CURLE_BAD_FUNCTION_ARGUMENT; - - primary->version = version; - primary->version_max = version_max; - } -#else - result = CURLE_NOT_BUILT_IN; -#endif - break; - - /* MQTT "borrows" some of the HTTP options */ -#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MQTT) - case CURLOPT_COPYPOSTFIELDS: - /* - * A string with POST data. Makes curl HTTP POST. Even if it is NULL. - * If needed, CURLOPT_POSTFIELDSIZE must have been set prior to - * CURLOPT_COPYPOSTFIELDS and not altered later. - */ - argptr = va_arg(param, char *); - - if(!argptr || data->set.postfieldsize == -1) - result = Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], argptr); - else { - /* - * Check that requested length does not overflow the size_t type. - */ - - if((data->set.postfieldsize < 0) || - ((sizeof(curl_off_t) != sizeof(size_t)) && - (data->set.postfieldsize > (curl_off_t)((size_t)-1)))) - result = CURLE_OUT_OF_MEMORY; - else { - char *p; - - (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); - - /* Allocate even when size == 0. This satisfies the need of possible - later address compare to detect the COPYPOSTFIELDS mode, and - to mark that postfields is used rather than read function or - form data. - */ - p = malloc((size_t)(data->set.postfieldsize? - data->set.postfieldsize:1)); - - if(!p) - result = CURLE_OUT_OF_MEMORY; - else { - if(data->set.postfieldsize) - memcpy(p, argptr, (size_t)data->set.postfieldsize); - - data->set.str[STRING_COPYPOSTFIELDS] = p; - } - } - } - - data->set.postfields = data->set.str[STRING_COPYPOSTFIELDS]; - data->set.method = HTTPREQ_POST; - break; - - case CURLOPT_POSTFIELDS: - /* - * Like above, but use static data instead of copying it. - */ - data->set.postfields = va_arg(param, void *); - /* Release old copied data. */ - (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); - data->set.method = HTTPREQ_POST; - break; - - case CURLOPT_POSTFIELDSIZE: - /* - * The size of the POSTFIELD data to prevent libcurl to do strlen() to - * figure it out. Enables binary posts. - */ - bigsize = va_arg(param, long); - if(bigsize < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - - if(data->set.postfieldsize < bigsize && - data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) { - /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ - (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); - data->set.postfields = NULL; - } - - data->set.postfieldsize = bigsize; - break; - - case CURLOPT_POSTFIELDSIZE_LARGE: - /* - * The size of the POSTFIELD data to prevent libcurl to do strlen() to - * figure it out. Enables binary posts. - */ - bigsize = va_arg(param, curl_off_t); - if(bigsize < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - - if(data->set.postfieldsize < bigsize && - data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) { - /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ - (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); - data->set.postfields = NULL; - } - - data->set.postfieldsize = bigsize; - break; -#endif -#ifndef CURL_DISABLE_HTTP - case CURLOPT_AUTOREFERER: - /* - * Switch on automatic referer that gets set if curl follows locations. - */ - data->set.http_auto_referer = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_ACCEPT_ENCODING: - /* - * String to use at the value of Accept-Encoding header. - * - * If the encoding is set to "" we use an Accept-Encoding header that - * encompasses all the encodings we support. - * If the encoding is set to NULL we don't send an Accept-Encoding header - * and ignore an received Content-Encoding header. - * - */ - argptr = va_arg(param, char *); - if(argptr && !*argptr) { - argptr = Curl_all_content_encodings(); - if(!argptr) - result = CURLE_OUT_OF_MEMORY; - else { - result = Curl_setstropt(&data->set.str[STRING_ENCODING], argptr); - free(argptr); - } - } - else - result = Curl_setstropt(&data->set.str[STRING_ENCODING], argptr); - break; - - case CURLOPT_TRANSFER_ENCODING: - data->set.http_transfer_encoding = (0 != va_arg(param, long)) ? - TRUE : FALSE; - break; - - case CURLOPT_FOLLOWLOCATION: - /* - * Follow Location: header hints on a HTTP-server. - */ - data->set.http_follow_location = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_UNRESTRICTED_AUTH: - /* - * Send authentication (user+password) when following locations, even when - * hostname changed. - */ - data->set.allow_auth_to_other_hosts = - (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_MAXREDIRS: - /* - * The maximum amount of hops you allow curl to follow Location: - * headers. This should mostly be used to detect never-ending loops. - */ - arg = va_arg(param, long); - if(arg < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.maxredirs = arg; - break; - - case CURLOPT_POSTREDIR: - /* - * Set the behavior of POST when redirecting - * CURL_REDIR_GET_ALL - POST is changed to GET after 301 and 302 - * CURL_REDIR_POST_301 - POST is kept as POST after 301 - * CURL_REDIR_POST_302 - POST is kept as POST after 302 - * CURL_REDIR_POST_303 - POST is kept as POST after 303 - * CURL_REDIR_POST_ALL - POST is kept as POST after 301, 302 and 303 - * other - POST is kept as POST after 301 and 302 - */ - arg = va_arg(param, long); - if(arg < CURL_REDIR_GET_ALL) - /* no return error on too high numbers since the bitmask could be - extended in a future */ - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.keep_post = arg & CURL_REDIR_POST_ALL; - break; - - case CURLOPT_POST: - /* Does this option serve a purpose anymore? Yes it does, when - CURLOPT_POSTFIELDS isn't used and the POST data is read off the - callback! */ - if(va_arg(param, long)) { - data->set.method = HTTPREQ_POST; - data->set.opt_no_body = FALSE; /* this is implied */ - } - else - data->set.method = HTTPREQ_GET; - data->set.upload = FALSE; - break; - -#ifndef CURL_DISABLE_MIME - case CURLOPT_HTTPPOST: - /* - * Set to make us do HTTP POST - */ - data->set.httppost = va_arg(param, struct curl_httppost *); - data->set.method = HTTPREQ_POST_FORM; - data->set.opt_no_body = FALSE; /* this is implied */ - break; -#endif - - case CURLOPT_AWS_SIGV4: - /* - * String that is merged to some authentication - * parameters are used by the algorithm. - */ - result = Curl_setstropt(&data->set.str[STRING_AWS_SIGV4], - va_arg(param, char *)); - /* - * Basic been set by default it need to be unset here - */ - if(data->set.str[STRING_AWS_SIGV4]) - data->set.httpauth = CURLAUTH_AWS_SIGV4; - break; - - case CURLOPT_REFERER: - /* - * String to set in the HTTP Referer: field. - */ - if(data->state.referer_alloc) { - Curl_safefree(data->state.referer); - data->state.referer_alloc = FALSE; - } - result = Curl_setstropt(&data->set.str[STRING_SET_REFERER], - va_arg(param, char *)); - data->state.referer = data->set.str[STRING_SET_REFERER]; - break; - - case CURLOPT_USERAGENT: - /* - * String to use in the HTTP User-Agent field - */ - result = Curl_setstropt(&data->set.str[STRING_USERAGENT], - va_arg(param, char *)); - break; - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXYHEADER: - /* - * Set a list with proxy headers to use (or replace internals with) - * - * Since CURLOPT_HTTPHEADER was the only way to set HTTP headers for a - * long time we remain doing it this way until CURLOPT_PROXYHEADER is - * used. As soon as this option has been used, if set to anything but - * NULL, custom headers for proxies are only picked from this list. - * - * Set this option to NULL to restore the previous behavior. - */ - data->set.proxyheaders = va_arg(param, struct curl_slist *); - break; -#endif - case CURLOPT_HEADEROPT: - /* - * Set header option. - */ - arg = va_arg(param, long); - data->set.sep_headers = (bool)((arg & CURLHEADER_SEPARATE)? TRUE: FALSE); - break; - - case CURLOPT_HTTP200ALIASES: - /* - * Set a list of aliases for HTTP 200 in response header - */ - data->set.http200aliases = va_arg(param, struct curl_slist *); - break; - -#if !defined(CURL_DISABLE_COOKIES) - case CURLOPT_COOKIE: - /* - * Cookie string to send to the remote server in the request. - */ - result = Curl_setstropt(&data->set.str[STRING_COOKIE], - va_arg(param, char *)); - break; - - case CURLOPT_COOKIEFILE: - /* - * Set cookie file to read and parse. Can be used multiple times. - */ - argptr = (char *)va_arg(param, void *); - if(argptr) { - struct curl_slist *cl; - /* general protection against mistakes and abuse */ - if(strlen(argptr) > CURL_MAX_INPUT_LENGTH) - return CURLE_BAD_FUNCTION_ARGUMENT; - /* append the cookie file name to the list of file names, and deal with - them later */ - cl = curl_slist_append(data->state.cookielist, argptr); - if(!cl) { - curl_slist_free_all(data->state.cookielist); - data->state.cookielist = NULL; - return CURLE_OUT_OF_MEMORY; - } - data->state.cookielist = cl; /* store the list for later use */ - } - else { - /* clear the list of cookie files */ - curl_slist_free_all(data->state.cookielist); - data->state.cookielist = NULL; - - if(!data->share || !data->share->cookies) { - /* throw away all existing cookies if this isn't a shared cookie - container */ - Curl_cookie_clearall(data->cookies); - Curl_cookie_cleanup(data->cookies); - } - /* disable the cookie engine */ - data->cookies = NULL; - } - break; - - case CURLOPT_COOKIEJAR: - /* - * Set cookie file name to dump all cookies to when we're done. - */ - { - struct CookieInfo *newcookies; - result = Curl_setstropt(&data->set.str[STRING_COOKIEJAR], - va_arg(param, char *)); - - /* - * Activate the cookie parser. This may or may not already - * have been made. - */ - newcookies = Curl_cookie_init(data, NULL, data->cookies, - data->set.cookiesession); - if(!newcookies) - result = CURLE_OUT_OF_MEMORY; - data->cookies = newcookies; - } - break; - - case CURLOPT_COOKIESESSION: - /* - * Set this option to TRUE to start a new "cookie session". It will - * prevent the forthcoming read-cookies-from-file actions to accept - * cookies that are marked as being session cookies, as they belong to a - * previous session. - * - * In the original Netscape cookie spec, "session cookies" are cookies - * with no expire date set. RFC2109 describes the same action if no - * 'Max-Age' is set and RFC2965 includes the RFC2109 description and adds - * a 'Discard' action that can enforce the discard even for cookies that - * have a Max-Age. - * - * We run mostly with the original cookie spec, as hardly anyone implements - * anything else. - */ - data->set.cookiesession = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_COOKIELIST: - argptr = va_arg(param, char *); - - if(!argptr) - break; - - if(strcasecompare(argptr, "ALL")) { - /* clear all cookies */ - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - Curl_cookie_clearall(data->cookies); - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - } - else if(strcasecompare(argptr, "SESS")) { - /* clear session cookies */ - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - Curl_cookie_clearsess(data->cookies); - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - } - else if(strcasecompare(argptr, "FLUSH")) { - /* flush cookies to file, takes care of the locking */ - Curl_flush_cookies(data, FALSE); - } - else if(strcasecompare(argptr, "RELOAD")) { - /* reload cookies from file */ - Curl_cookie_loadfiles(data); - break; - } - else { - if(!data->cookies) - /* if cookie engine was not running, activate it */ - data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE); - - /* general protection against mistakes and abuse */ - if(strlen(argptr) > CURL_MAX_INPUT_LENGTH) - return CURLE_BAD_FUNCTION_ARGUMENT; - argptr = strdup(argptr); - if(!argptr || !data->cookies) { - result = CURLE_OUT_OF_MEMORY; - free(argptr); - } - else { - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - - if(checkprefix("Set-Cookie:", argptr)) - /* HTTP Header format line */ - Curl_cookie_add(data, data->cookies, TRUE, FALSE, argptr + 11, NULL, - NULL, TRUE); - - else - /* Netscape format line */ - Curl_cookie_add(data, data->cookies, FALSE, FALSE, argptr, NULL, - NULL, TRUE); - - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - free(argptr); - } - } - - break; -#endif /* !CURL_DISABLE_COOKIES */ - - case CURLOPT_HTTPGET: - /* - * Set to force us do HTTP GET - */ - if(va_arg(param, long)) { - data->set.method = HTTPREQ_GET; - data->set.upload = FALSE; /* switch off upload */ - data->set.opt_no_body = FALSE; /* this is implied */ - } - break; - - case CURLOPT_HTTP_VERSION: - /* - * This sets a requested HTTP version to be used. The value is one of - * the listed enums in curl/curl.h. - */ - arg = va_arg(param, long); - if(arg < CURL_HTTP_VERSION_NONE) - return CURLE_BAD_FUNCTION_ARGUMENT; -#ifdef ENABLE_QUIC - if(arg == CURL_HTTP_VERSION_3) - ; - else -#endif -#ifndef USE_HTTP2 - if(arg >= CURL_HTTP_VERSION_2) - return CURLE_UNSUPPORTED_PROTOCOL; -#else - if(arg >= CURL_HTTP_VERSION_LAST) - return CURLE_UNSUPPORTED_PROTOCOL; - if(arg == CURL_HTTP_VERSION_NONE) - arg = CURL_HTTP_VERSION_2TLS; -#endif - data->set.httpwant = (unsigned char)arg; - break; - - case CURLOPT_EXPECT_100_TIMEOUT_MS: - /* - * Time to wait for a response to a HTTP request containing an - * Expect: 100-continue header before sending the data anyway. - */ - arg = va_arg(param, long); - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.expect_100_timeout = arg; - break; - - case CURLOPT_HTTP09_ALLOWED: - arg = va_arg(param, unsigned long); - if(arg > 1L) - return CURLE_BAD_FUNCTION_ARGUMENT; -#ifdef USE_HYPER - /* Hyper does not support HTTP/0.9 */ - if(arg) - return CURLE_BAD_FUNCTION_ARGUMENT; -#else - data->set.http09_allowed = arg ? TRUE : FALSE; -#endif - break; -#endif /* CURL_DISABLE_HTTP */ - -#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \ - !defined(CURL_DISABLE_IMAP) -# if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MIME) - case CURLOPT_HTTPHEADER: - /* - * Set a list with HTTP headers to use (or replace internals with) - */ - data->set.headers = va_arg(param, struct curl_slist *); - break; -# endif - -# ifndef CURL_DISABLE_MIME - case CURLOPT_MIMEPOST: - /* - * Set to make us do MIME POST - */ - result = Curl_mime_set_subparts(&data->set.mimepost, - va_arg(param, curl_mime *), FALSE); - if(!result) { - data->set.method = HTTPREQ_POST_MIME; - data->set.opt_no_body = FALSE; /* this is implied */ - } - break; - - case CURLOPT_MIME_OPTIONS: - data->set.mime_options = (unsigned int)va_arg(param, long); - break; -# endif -#endif - - case CURLOPT_HTTPAUTH: - /* - * Set HTTP Authentication type BITMASK. - */ - { - int bitcheck; - bool authbits; - unsigned long auth = va_arg(param, unsigned long); - - if(auth == CURLAUTH_NONE) { - data->set.httpauth = auth; - break; - } - - /* the DIGEST_IE bit is only used to set a special marker, for all the - rest we need to handle it as normal DIGEST */ - data->state.authhost.iestyle = - (bool)((auth & CURLAUTH_DIGEST_IE) ? TRUE : FALSE); - - if(auth & CURLAUTH_DIGEST_IE) { - auth |= CURLAUTH_DIGEST; /* set standard digest bit */ - auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */ - } - - /* switch off bits we can't support */ -#ifndef USE_NTLM - auth &= ~CURLAUTH_NTLM; /* no NTLM support */ - auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */ -#elif !defined(NTLM_WB_ENABLED) - auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */ -#endif -#ifndef USE_SPNEGO - auth &= ~CURLAUTH_NEGOTIATE; /* no Negotiate (SPNEGO) auth without - GSS-API or SSPI */ -#endif - - /* check if any auth bit lower than CURLAUTH_ONLY is still set */ - bitcheck = 0; - authbits = FALSE; - while(bitcheck < 31) { - if(auth & (1UL << bitcheck++)) { - authbits = TRUE; - break; - } - } - if(!authbits) - return CURLE_NOT_BUILT_IN; /* no supported types left! */ - - data->set.httpauth = auth; - } - break; - - case CURLOPT_CUSTOMREQUEST: - /* - * Set a custom string to use as request - */ - result = Curl_setstropt(&data->set.str[STRING_CUSTOMREQUEST], - va_arg(param, char *)); - - /* we don't set - data->set.method = HTTPREQ_CUSTOM; - here, we continue as if we were using the already set type - and this just changes the actual request keyword */ - break; - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_HTTPPROXYTUNNEL: - /* - * Tunnel operations through the proxy instead of normal proxy use - */ - data->set.tunnel_thru_httpproxy = (0 != va_arg(param, long)) ? - TRUE : FALSE; - break; - - case CURLOPT_PROXYPORT: - /* - * Explicitly set HTTP proxy port number. - */ - arg = va_arg(param, long); - if((arg < 0) || (arg > 65535)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.proxyport = arg; - break; - - case CURLOPT_PROXYAUTH: - /* - * Set HTTP Authentication type BITMASK. - */ - { - int bitcheck; - bool authbits; - unsigned long auth = va_arg(param, unsigned long); - - if(auth == CURLAUTH_NONE) { - data->set.proxyauth = auth; - break; - } - - /* the DIGEST_IE bit is only used to set a special marker, for all the - rest we need to handle it as normal DIGEST */ - data->state.authproxy.iestyle = - (bool)((auth & CURLAUTH_DIGEST_IE) ? TRUE : FALSE); - - if(auth & CURLAUTH_DIGEST_IE) { - auth |= CURLAUTH_DIGEST; /* set standard digest bit */ - auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */ - } - /* switch off bits we can't support */ -#ifndef USE_NTLM - auth &= ~CURLAUTH_NTLM; /* no NTLM support */ - auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */ -#elif !defined(NTLM_WB_ENABLED) - auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */ -#endif -#ifndef USE_SPNEGO - auth &= ~CURLAUTH_NEGOTIATE; /* no Negotiate (SPNEGO) auth without - GSS-API or SSPI */ -#endif - - /* check if any auth bit lower than CURLAUTH_ONLY is still set */ - bitcheck = 0; - authbits = FALSE; - while(bitcheck < 31) { - if(auth & (1UL << bitcheck++)) { - authbits = TRUE; - break; - } - } - if(!authbits) - return CURLE_NOT_BUILT_IN; /* no supported types left! */ - - data->set.proxyauth = auth; - } - break; - - case CURLOPT_PROXY: - /* - * Set proxy server:port to use as proxy. - * - * If the proxy is set to "" (and CURLOPT_SOCKS_PROXY is set to "" or NULL) - * we explicitly say that we don't want to use a proxy - * (even though there might be environment variables saying so). - * - * Setting it to NULL, means no proxy but allows the environment variables - * to decide for us (if CURLOPT_SOCKS_PROXY setting it to NULL). - */ - result = Curl_setstropt(&data->set.str[STRING_PROXY], - va_arg(param, char *)); - break; - - case CURLOPT_PRE_PROXY: - /* - * Set proxy server:port to use as SOCKS proxy. - * - * If the proxy is set to "" or NULL we explicitly say that we don't want - * to use the socks proxy. - */ - result = Curl_setstropt(&data->set.str[STRING_PRE_PROXY], - va_arg(param, char *)); - break; - - case CURLOPT_PROXYTYPE: - /* - * Set proxy type. HTTP/HTTP_1_0/SOCKS4/SOCKS4a/SOCKS5/SOCKS5_HOSTNAME - */ - arg = va_arg(param, long); - if((arg < CURLPROXY_HTTP) || (arg > CURLPROXY_SOCKS5_HOSTNAME)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.proxytype = (curl_proxytype)arg; - break; - - case CURLOPT_PROXY_TRANSFER_MODE: - /* - * set transfer mode (;type=<a|i>) when doing FTP via an HTTP proxy - */ - switch(va_arg(param, long)) { - case 0: - data->set.proxy_transfer_mode = FALSE; - break; - case 1: - data->set.proxy_transfer_mode = TRUE; - break; - default: - /* reserve other values for future use */ - result = CURLE_BAD_FUNCTION_ARGUMENT; - break; - } - break; - - case CURLOPT_SOCKS5_AUTH: - data->set.socks5auth = va_arg(param, unsigned long); - if(data->set.socks5auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI)) - result = CURLE_NOT_BUILT_IN; - break; -#endif /* CURL_DISABLE_PROXY */ - -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - case CURLOPT_SOCKS5_GSSAPI_NEC: - /* - * Set flag for NEC SOCK5 support - */ - data->set.socks5_gssapi_nec = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; -#endif -#ifndef CURL_DISABLE_PROXY - case CURLOPT_SOCKS5_GSSAPI_SERVICE: - case CURLOPT_PROXY_SERVICE_NAME: - /* - * Set proxy authentication service name for Kerberos 5 and SPNEGO - */ - result = Curl_setstropt(&data->set.str[STRING_PROXY_SERVICE_NAME], - va_arg(param, char *)); - break; -#endif - case CURLOPT_SERVICE_NAME: - /* - * Set authentication service name for DIGEST-MD5, Kerberos 5 and SPNEGO - */ - result = Curl_setstropt(&data->set.str[STRING_SERVICE_NAME], - va_arg(param, char *)); - break; - - case CURLOPT_HEADERDATA: - /* - * Custom pointer to pass the header write callback function - */ - data->set.writeheader = (void *)va_arg(param, void *); - break; - case CURLOPT_ERRORBUFFER: - /* - * Error buffer provided by the caller to get the human readable - * error string in. - */ - data->set.errorbuffer = va_arg(param, char *); - break; - case CURLOPT_WRITEDATA: - /* - * FILE pointer to write to. Or possibly - * used as argument to the write callback. - */ - data->set.out = va_arg(param, void *); - break; - - case CURLOPT_DIRLISTONLY: - /* - * An option that changes the command to one that asks for a list only, no - * file info details. Used for FTP, POP3 and SFTP. - */ - data->set.list_only = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_APPEND: - /* - * We want to upload and append to an existing file. Used for FTP and - * SFTP. - */ - data->set.remote_append = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - -#ifndef CURL_DISABLE_FTP - case CURLOPT_FTP_FILEMETHOD: - /* - * How do access files over FTP. - */ - arg = va_arg(param, long); - if((arg < CURLFTPMETHOD_DEFAULT) || (arg >= CURLFTPMETHOD_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.ftp_filemethod = (curl_ftpfile)arg; - break; - case CURLOPT_FTPPORT: - /* - * Use FTP PORT, this also specifies which IP address to use - */ - result = Curl_setstropt(&data->set.str[STRING_FTPPORT], - va_arg(param, char *)); - data->set.ftp_use_port = (data->set.str[STRING_FTPPORT]) ? TRUE : FALSE; - break; - - case CURLOPT_FTP_USE_EPRT: - data->set.ftp_use_eprt = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_FTP_USE_EPSV: - data->set.ftp_use_epsv = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_FTP_USE_PRET: - data->set.ftp_use_pret = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_FTP_SSL_CCC: - arg = va_arg(param, long); - if((arg < CURLFTPSSL_CCC_NONE) || (arg >= CURLFTPSSL_CCC_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.ftp_ccc = (curl_ftpccc)arg; - break; - - case CURLOPT_FTP_SKIP_PASV_IP: - /* - * Enable or disable FTP_SKIP_PASV_IP, which will disable/enable the - * bypass of the IP address in PASV responses. - */ - data->set.ftp_skip_ip = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_FTP_ACCOUNT: - result = Curl_setstropt(&data->set.str[STRING_FTP_ACCOUNT], - va_arg(param, char *)); - break; - - case CURLOPT_FTP_ALTERNATIVE_TO_USER: - result = Curl_setstropt(&data->set.str[STRING_FTP_ALTERNATIVE_TO_USER], - va_arg(param, char *)); - break; - - case CURLOPT_FTPSSLAUTH: - /* - * Set a specific auth for FTP-SSL transfers. - */ - arg = va_arg(param, long); - if((arg < CURLFTPAUTH_DEFAULT) || (arg >= CURLFTPAUTH_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.ftpsslauth = (curl_ftpauth)arg; - break; - case CURLOPT_KRBLEVEL: - /* - * A string that defines the kerberos security level. - */ - result = Curl_setstropt(&data->set.str[STRING_KRB_LEVEL], - va_arg(param, char *)); - data->set.krb = (data->set.str[STRING_KRB_LEVEL]) ? TRUE : FALSE; - break; -#endif - case CURLOPT_FTP_CREATE_MISSING_DIRS: - /* - * An FTP/SFTP option that modifies an upload to create missing - * directories on the server. - */ - arg = va_arg(param, long); - /* reserve other values for future use */ - if((arg < CURLFTP_CREATE_DIR_NONE) || - (arg > CURLFTP_CREATE_DIR_RETRY)) - result = CURLE_BAD_FUNCTION_ARGUMENT; - else - data->set.ftp_create_missing_dirs = (unsigned char)arg; - break; - case CURLOPT_READDATA: - /* - * FILE pointer to read the file to be uploaded from. Or possibly - * used as argument to the read callback. - */ - data->set.in_set = va_arg(param, void *); - break; - case CURLOPT_INFILESIZE: - /* - * If known, this should inform curl about the file size of the - * to-be-uploaded file. - */ - arg = va_arg(param, long); - if(arg < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.filesize = arg; - break; - case CURLOPT_INFILESIZE_LARGE: - /* - * If known, this should inform curl about the file size of the - * to-be-uploaded file. - */ - bigsize = va_arg(param, curl_off_t); - if(bigsize < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.filesize = bigsize; - break; - case CURLOPT_LOW_SPEED_LIMIT: - /* - * The low speed limit that if transfers are below this for - * CURLOPT_LOW_SPEED_TIME, the transfer is aborted. - */ - arg = va_arg(param, long); - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.low_speed_limit = arg; - break; - case CURLOPT_MAX_SEND_SPEED_LARGE: - /* - * When transfer uploads are faster then CURLOPT_MAX_SEND_SPEED_LARGE - * bytes per second the transfer is throttled.. - */ - bigsize = va_arg(param, curl_off_t); - if(bigsize < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.max_send_speed = bigsize; - break; - case CURLOPT_MAX_RECV_SPEED_LARGE: - /* - * When receiving data faster than CURLOPT_MAX_RECV_SPEED_LARGE bytes per - * second the transfer is throttled.. - */ - bigsize = va_arg(param, curl_off_t); - if(bigsize < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.max_recv_speed = bigsize; - break; - case CURLOPT_LOW_SPEED_TIME: - /* - * The low speed time that if transfers are below the set - * CURLOPT_LOW_SPEED_LIMIT during this time, the transfer is aborted. - */ - arg = va_arg(param, long); - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.low_speed_time = arg; - break; - case CURLOPT_CURLU: - /* - * pass CURLU to set URL - */ - data->set.uh = va_arg(param, CURLU *); - break; - case CURLOPT_URL: - /* - * The URL to fetch. - */ - if(data->state.url_alloc) { - /* the already set URL is allocated, free it first! */ - Curl_safefree(data->state.url); - data->state.url_alloc = FALSE; - } - result = Curl_setstropt(&data->set.str[STRING_SET_URL], - va_arg(param, char *)); - data->state.url = data->set.str[STRING_SET_URL]; - break; - case CURLOPT_PORT: - /* - * The port number to use when getting the URL. 0 disables it. - */ - arg = va_arg(param, long); - if((arg < 0) || (arg > 65535)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.use_port = (unsigned short)arg; - break; - case CURLOPT_TIMEOUT: - /* - * The maximum time you allow curl to use for a single transfer - * operation. - */ - arg = va_arg(param, long); - if((arg >= 0) && (arg <= (INT_MAX/1000))) - data->set.timeout = (unsigned int)arg * 1000; - else - return CURLE_BAD_FUNCTION_ARGUMENT; - break; - - case CURLOPT_TIMEOUT_MS: - uarg = va_arg(param, unsigned long); - if(uarg >= UINT_MAX) - uarg = UINT_MAX; - data->set.timeout = (unsigned int)uarg; - break; - - case CURLOPT_CONNECTTIMEOUT: - /* - * The maximum time you allow curl to use to connect. - */ - arg = va_arg(param, long); - if((arg >= 0) && (arg <= (INT_MAX/1000))) - data->set.connecttimeout = (unsigned int)arg * 1000; - else - return CURLE_BAD_FUNCTION_ARGUMENT; - break; - - case CURLOPT_CONNECTTIMEOUT_MS: - uarg = va_arg(param, unsigned long); - if(uarg >= UINT_MAX) - uarg = UINT_MAX; - data->set.connecttimeout = (unsigned int)uarg; - break; - -#ifndef CURL_DISABLE_FTP - case CURLOPT_ACCEPTTIMEOUT_MS: - /* - * The maximum time for curl to wait for FTP server connect - */ - uarg = va_arg(param, unsigned long); - if(uarg >= UINT_MAX) - uarg = UINT_MAX; - data->set.accepttimeout = (unsigned int)uarg; - break; -#endif - - case CURLOPT_USERPWD: - /* - * user:password to use in the operation - */ - result = setstropt_userpwd(va_arg(param, char *), - &data->set.str[STRING_USERNAME], - &data->set.str[STRING_PASSWORD]); - break; - - case CURLOPT_USERNAME: - /* - * authentication user name to use in the operation - */ - result = Curl_setstropt(&data->set.str[STRING_USERNAME], - va_arg(param, char *)); - break; - case CURLOPT_PASSWORD: - /* - * authentication password to use in the operation - */ - result = Curl_setstropt(&data->set.str[STRING_PASSWORD], - va_arg(param, char *)); - break; - - case CURLOPT_LOGIN_OPTIONS: - /* - * authentication options to use in the operation - */ - result = Curl_setstropt(&data->set.str[STRING_OPTIONS], - va_arg(param, char *)); - break; - - case CURLOPT_XOAUTH2_BEARER: - /* - * OAuth 2.0 bearer token to use in the operation - */ - result = Curl_setstropt(&data->set.str[STRING_BEARER], - va_arg(param, char *)); - break; - - case CURLOPT_POSTQUOTE: - /* - * List of RAW FTP commands to use after a transfer - */ - data->set.postquote = va_arg(param, struct curl_slist *); - break; - case CURLOPT_PREQUOTE: - /* - * List of RAW FTP commands to use prior to RETR (Wesley Laxton) - */ - data->set.prequote = va_arg(param, struct curl_slist *); - break; - case CURLOPT_QUOTE: - /* - * List of RAW FTP commands to use before a transfer - */ - data->set.quote = va_arg(param, struct curl_slist *); - break; - case CURLOPT_RESOLVE: - /* - * List of HOST:PORT:[addresses] strings to populate the DNS cache with - * Entries added this way will remain in the cache until explicitly - * removed or the handle is cleaned up. - * - * Prefix the HOST with plus sign (+) to have the entry expire just like - * automatically added entries. - * - * Prefix the HOST with dash (-) to _remove_ the entry from the cache. - * - * This API can remove any entry from the DNS cache, but only entries - * that aren't actually in use right now will be pruned immediately. - */ - data->set.resolve = va_arg(param, struct curl_slist *); - data->state.resolve = data->set.resolve; - break; - case CURLOPT_PROGRESSFUNCTION: - /* - * Progress callback function - */ - data->set.fprogress = va_arg(param, curl_progress_callback); - if(data->set.fprogress) - data->progress.callback = TRUE; /* no longer internal */ - else - data->progress.callback = FALSE; /* NULL enforces internal */ - break; - - case CURLOPT_XFERINFOFUNCTION: - /* - * Transfer info callback function - */ - data->set.fxferinfo = va_arg(param, curl_xferinfo_callback); - if(data->set.fxferinfo) - data->progress.callback = TRUE; /* no longer internal */ - else - data->progress.callback = FALSE; /* NULL enforces internal */ - - break; - - case CURLOPT_PROGRESSDATA: - /* - * Custom client data to pass to the progress callback - */ - data->set.progress_client = va_arg(param, void *); - break; - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXYUSERPWD: - /* - * user:password needed to use the proxy - */ - result = setstropt_userpwd(va_arg(param, char *), - &data->set.str[STRING_PROXYUSERNAME], - &data->set.str[STRING_PROXYPASSWORD]); - break; - case CURLOPT_PROXYUSERNAME: - /* - * authentication user name to use in the operation - */ - result = Curl_setstropt(&data->set.str[STRING_PROXYUSERNAME], - va_arg(param, char *)); - break; - case CURLOPT_PROXYPASSWORD: - /* - * authentication password to use in the operation - */ - result = Curl_setstropt(&data->set.str[STRING_PROXYPASSWORD], - va_arg(param, char *)); - break; - case CURLOPT_NOPROXY: - /* - * proxy exception list - */ - result = Curl_setstropt(&data->set.str[STRING_NOPROXY], - va_arg(param, char *)); - break; -#endif - - case CURLOPT_RANGE: - /* - * What range of the file you want to transfer - */ - result = Curl_setstropt(&data->set.str[STRING_SET_RANGE], - va_arg(param, char *)); - break; - case CURLOPT_RESUME_FROM: - /* - * Resume transfer at the given file position - */ - arg = va_arg(param, long); - if(arg < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.set_resume_from = arg; - break; - case CURLOPT_RESUME_FROM_LARGE: - /* - * Resume transfer at the given file position - */ - bigsize = va_arg(param, curl_off_t); - if(bigsize < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.set_resume_from = bigsize; - break; - case CURLOPT_DEBUGFUNCTION: - /* - * stderr write callback. - */ - data->set.fdebug = va_arg(param, curl_debug_callback); - /* - * if the callback provided is NULL, it'll use the default callback - */ - break; - case CURLOPT_DEBUGDATA: - /* - * Set to a void * that should receive all error writes. This - * defaults to CURLOPT_STDERR for normal operations. - */ - data->set.debugdata = va_arg(param, void *); - break; - case CURLOPT_STDERR: - /* - * Set to a FILE * that should receive all error writes. This - * defaults to stderr for normal operations. - */ - data->set.err = va_arg(param, FILE *); - if(!data->set.err) - data->set.err = stderr; - break; - case CURLOPT_HEADERFUNCTION: - /* - * Set header write callback - */ - data->set.fwrite_header = va_arg(param, curl_write_callback); - break; - case CURLOPT_WRITEFUNCTION: - /* - * Set data write callback - */ - data->set.fwrite_func = va_arg(param, curl_write_callback); - if(!data->set.fwrite_func) - /* When set to NULL, reset to our internal default function */ - data->set.fwrite_func = (curl_write_callback)fwrite; - break; - case CURLOPT_READFUNCTION: - /* - * Read data callback - */ - data->set.fread_func_set = va_arg(param, curl_read_callback); - if(!data->set.fread_func_set) { - data->set.is_fread_set = 0; - /* When set to NULL, reset to our internal default function */ - data->set.fread_func_set = (curl_read_callback)fread; - } - else - data->set.is_fread_set = 1; - break; - case CURLOPT_SEEKFUNCTION: - /* - * Seek callback. Might be NULL. - */ - data->set.seek_func = va_arg(param, curl_seek_callback); - break; - case CURLOPT_SEEKDATA: - /* - * Seek control callback. Might be NULL. - */ - data->set.seek_client = va_arg(param, void *); - break; - case CURLOPT_IOCTLFUNCTION: - /* - * I/O control callback. Might be NULL. - */ - data->set.ioctl_func = va_arg(param, curl_ioctl_callback); - break; - case CURLOPT_IOCTLDATA: - /* - * I/O control data pointer. Might be NULL. - */ - data->set.ioctl_client = va_arg(param, void *); - break; - case CURLOPT_SSLCERT: - /* - * String that holds file name of the SSL certificate to use - */ - result = Curl_setstropt(&data->set.str[STRING_CERT], - va_arg(param, char *)); - break; - case CURLOPT_SSLCERT_BLOB: - /* - * Blob that holds file content of the SSL certificate to use - */ - result = Curl_setblobopt(&data->set.blobs[BLOB_CERT], - va_arg(param, struct curl_blob *)); - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSLCERT: - /* - * String that holds file name of the SSL certificate to use for proxy - */ - result = Curl_setstropt(&data->set.str[STRING_CERT_PROXY], - va_arg(param, char *)); - break; - case CURLOPT_PROXY_SSLCERT_BLOB: - /* - * Blob that holds file content of the SSL certificate to use for proxy - */ - result = Curl_setblobopt(&data->set.blobs[BLOB_CERT_PROXY], - va_arg(param, struct curl_blob *)); - break; -#endif - case CURLOPT_SSLCERTTYPE: - /* - * String that holds file type of the SSL certificate to use - */ - result = Curl_setstropt(&data->set.str[STRING_CERT_TYPE], - va_arg(param, char *)); - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSLCERTTYPE: - /* - * String that holds file type of the SSL certificate to use for proxy - */ - result = Curl_setstropt(&data->set.str[STRING_CERT_TYPE_PROXY], - va_arg(param, char *)); - break; -#endif - case CURLOPT_SSLKEY: - /* - * String that holds file name of the SSL key to use - */ - result = Curl_setstropt(&data->set.str[STRING_KEY], - va_arg(param, char *)); - break; - case CURLOPT_SSLKEY_BLOB: - /* - * Blob that holds file content of the SSL key to use - */ - result = Curl_setblobopt(&data->set.blobs[BLOB_KEY], - va_arg(param, struct curl_blob *)); - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSLKEY: - /* - * String that holds file name of the SSL key to use for proxy - */ - result = Curl_setstropt(&data->set.str[STRING_KEY_PROXY], - va_arg(param, char *)); - break; - case CURLOPT_PROXY_SSLKEY_BLOB: - /* - * Blob that holds file content of the SSL key to use for proxy - */ - result = Curl_setblobopt(&data->set.blobs[BLOB_KEY_PROXY], - va_arg(param, struct curl_blob *)); - break; -#endif - case CURLOPT_SSLKEYTYPE: - /* - * String that holds file type of the SSL key to use - */ - result = Curl_setstropt(&data->set.str[STRING_KEY_TYPE], - va_arg(param, char *)); - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSLKEYTYPE: - /* - * String that holds file type of the SSL key to use for proxy - */ - result = Curl_setstropt(&data->set.str[STRING_KEY_TYPE_PROXY], - va_arg(param, char *)); - break; -#endif - case CURLOPT_KEYPASSWD: - /* - * String that holds the SSL or SSH private key password. - */ - result = Curl_setstropt(&data->set.str[STRING_KEY_PASSWD], - va_arg(param, char *)); - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_KEYPASSWD: - /* - * String that holds the SSL private key password for proxy. - */ - result = Curl_setstropt(&data->set.str[STRING_KEY_PASSWD_PROXY], - va_arg(param, char *)); - break; -#endif - case CURLOPT_SSLENGINE: - /* - * String that holds the SSL crypto engine. - */ - argptr = va_arg(param, char *); - if(argptr && argptr[0]) { - result = Curl_setstropt(&data->set.str[STRING_SSL_ENGINE], argptr); - if(!result) { - result = Curl_ssl_set_engine(data, argptr); - } - } - break; - - case CURLOPT_SSLENGINE_DEFAULT: - /* - * flag to set engine as default. - */ - Curl_setstropt(&data->set.str[STRING_SSL_ENGINE], NULL); - result = Curl_ssl_set_engine_default(data); - break; - case CURLOPT_CRLF: - /* - * Kludgy option to enable CRLF conversions. Subject for removal. - */ - data->set.crlf = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_HAPROXYPROTOCOL: - /* - * Set to send the HAProxy Proxy Protocol header - */ - data->set.haproxyprotocol = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; -#endif - case CURLOPT_INTERFACE: - /* - * Set what interface or address/hostname to bind the socket to when - * performing an operation and thus what from-IP your connection will use. - */ - result = Curl_setstropt(&data->set.str[STRING_DEVICE], - va_arg(param, char *)); - break; - case CURLOPT_LOCALPORT: - /* - * Set what local port to bind the socket to when performing an operation. - */ - arg = va_arg(param, long); - if((arg < 0) || (arg > 65535)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.localport = curlx_sltous(arg); - break; - case CURLOPT_LOCALPORTRANGE: - /* - * Set number of local ports to try, starting with CURLOPT_LOCALPORT. - */ - arg = va_arg(param, long); - if((arg < 0) || (arg > 65535)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.localportrange = curlx_sltosi(arg); - break; - case CURLOPT_GSSAPI_DELEGATION: - /* - * GSS-API credential delegation bitmask - */ - arg = va_arg(param, long); - if(arg < CURLGSSAPI_DELEGATION_NONE) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.gssapi_delegation = arg; - break; - case CURLOPT_SSL_VERIFYPEER: - /* - * Enable peer SSL verifying. - */ - data->set.ssl.primary.verifypeer = (0 != va_arg(param, long)) ? - TRUE : FALSE; - - /* Update the current connection ssl_config. */ - if(data->conn) { - data->conn->ssl_config.verifypeer = - data->set.ssl.primary.verifypeer; - } - break; -#ifndef CURL_DISABLE_DOH - case CURLOPT_DOH_SSL_VERIFYPEER: - /* - * Enable peer SSL verifying for DoH. - */ - data->set.doh_verifypeer = (0 != va_arg(param, long)) ? - TRUE : FALSE; - break; -#endif -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSL_VERIFYPEER: - /* - * Enable peer SSL verifying for proxy. - */ - data->set.proxy_ssl.primary.verifypeer = - (0 != va_arg(param, long))?TRUE:FALSE; - - /* Update the current connection proxy_ssl_config. */ - if(data->conn) { - data->conn->proxy_ssl_config.verifypeer = - data->set.proxy_ssl.primary.verifypeer; - } - break; -#endif - case CURLOPT_SSL_VERIFYHOST: - /* - * Enable verification of the host name in the peer certificate - */ - arg = va_arg(param, long); - - /* Obviously people are not reading documentation and too many thought - this argument took a boolean when it wasn't and misused it. - Treat 1 and 2 the same */ - data->set.ssl.primary.verifyhost = (bool)((arg & 3) ? TRUE : FALSE); - - /* Update the current connection ssl_config. */ - if(data->conn) { - data->conn->ssl_config.verifyhost = - data->set.ssl.primary.verifyhost; - } - break; -#ifndef CURL_DISABLE_DOH - case CURLOPT_DOH_SSL_VERIFYHOST: - /* - * Enable verification of the host name in the peer certificate for DoH - */ - arg = va_arg(param, long); - - /* Treat both 1 and 2 as TRUE */ - data->set.doh_verifyhost = (bool)((arg & 3) ? TRUE : FALSE); - break; -#endif -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSL_VERIFYHOST: - /* - * Enable verification of the host name in the peer certificate for proxy - */ - arg = va_arg(param, long); - - /* Treat both 1 and 2 as TRUE */ - data->set.proxy_ssl.primary.verifyhost = (bool)((arg & 3)?TRUE:FALSE); - - /* Update the current connection proxy_ssl_config. */ - if(data->conn) { - data->conn->proxy_ssl_config.verifyhost = - data->set.proxy_ssl.primary.verifyhost; - } - break; -#endif - case CURLOPT_SSL_VERIFYSTATUS: - /* - * Enable certificate status verifying. - */ - if(!Curl_ssl_cert_status_request()) { - result = CURLE_NOT_BUILT_IN; - break; - } - - data->set.ssl.primary.verifystatus = (0 != va_arg(param, long)) ? - TRUE : FALSE; - - /* Update the current connection ssl_config. */ - if(data->conn) { - data->conn->ssl_config.verifystatus = - data->set.ssl.primary.verifystatus; - } - break; -#ifndef CURL_DISABLE_DOH - case CURLOPT_DOH_SSL_VERIFYSTATUS: - /* - * Enable certificate status verifying for DoH. - */ - if(!Curl_ssl_cert_status_request()) { - result = CURLE_NOT_BUILT_IN; - break; - } - - data->set.doh_verifystatus = (0 != va_arg(param, long)) ? - TRUE : FALSE; - break; -#endif - case CURLOPT_SSL_CTX_FUNCTION: - /* - * Set a SSL_CTX callback - */ -#ifdef USE_SSL - if(Curl_ssl->supports & SSLSUPP_SSL_CTX) - data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback); - else -#endif - result = CURLE_NOT_BUILT_IN; - break; - case CURLOPT_SSL_CTX_DATA: - /* - * Set a SSL_CTX callback parameter pointer - */ -#ifdef USE_SSL - if(Curl_ssl->supports & SSLSUPP_SSL_CTX) - data->set.ssl.fsslctxp = va_arg(param, void *); - else -#endif - result = CURLE_NOT_BUILT_IN; - break; - case CURLOPT_SSL_FALSESTART: - /* - * Enable TLS false start. - */ - if(!Curl_ssl_false_start()) { - result = CURLE_NOT_BUILT_IN; - break; - } - - data->set.ssl.falsestart = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_CERTINFO: -#ifdef USE_SSL - if(Curl_ssl->supports & SSLSUPP_CERTINFO) - data->set.ssl.certinfo = (0 != va_arg(param, long)) ? TRUE : FALSE; - else -#endif - result = CURLE_NOT_BUILT_IN; - break; - case CURLOPT_PINNEDPUBLICKEY: - /* - * Set pinned public key for SSL connection. - * Specify file name of the public key in DER format. - */ -#ifdef USE_SSL - if(Curl_ssl->supports & SSLSUPP_PINNEDPUBKEY) - result = Curl_setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY], - va_arg(param, char *)); - else -#endif - result = CURLE_NOT_BUILT_IN; - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_PINNEDPUBLICKEY: - /* - * Set pinned public key for SSL connection. - * Specify file name of the public key in DER format. - */ -#ifdef USE_SSL - if(Curl_ssl->supports & SSLSUPP_PINNEDPUBKEY) - result = Curl_setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY], - va_arg(param, char *)); - else -#endif - result = CURLE_NOT_BUILT_IN; - break; -#endif - case CURLOPT_CAINFO: - /* - * Set CA info for SSL connection. Specify file name of the CA certificate - */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CAFILE], - va_arg(param, char *)); - break; - case CURLOPT_CAINFO_BLOB: - /* - * Blob that holds CA info for SSL connection. - * Specify entire PEM of the CA certificate - */ -#ifdef USE_SSL - if(Curl_ssl->supports & SSLSUPP_CAINFO_BLOB) - result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO], - va_arg(param, struct curl_blob *)); - else -#endif - return CURLE_NOT_BUILT_IN; - - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_CAINFO: - /* - * Set CA info SSL connection for proxy. Specify file name of the - * CA certificate - */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CAFILE_PROXY], - va_arg(param, char *)); - break; - case CURLOPT_PROXY_CAINFO_BLOB: - /* - * Blob that holds CA info for SSL connection proxy. - * Specify entire PEM of the CA certificate - */ -#ifdef USE_SSL - if(Curl_ssl->supports & SSLSUPP_CAINFO_BLOB) - result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO_PROXY], - va_arg(param, struct curl_blob *)); - else -#endif - return CURLE_NOT_BUILT_IN; - break; -#endif - case CURLOPT_CAPATH: - /* - * Set CA path info for SSL connection. Specify directory name of the CA - * certificates which have been prepared using openssl c_rehash utility. - */ -#ifdef USE_SSL - if(Curl_ssl->supports & SSLSUPP_CA_PATH) - /* This does not work on windows. */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CAPATH], - va_arg(param, char *)); - else -#endif - result = CURLE_NOT_BUILT_IN; - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_CAPATH: - /* - * Set CA path info for SSL connection proxy. Specify directory name of the - * CA certificates which have been prepared using openssl c_rehash utility. - */ -#ifdef USE_SSL - if(Curl_ssl->supports & SSLSUPP_CA_PATH) - /* This does not work on windows. */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CAPATH_PROXY], - va_arg(param, char *)); - else -#endif - result = CURLE_NOT_BUILT_IN; - break; -#endif - case CURLOPT_CRLFILE: - /* - * Set CRL file info for SSL connection. Specify file name of the CRL - * to check certificates revocation - */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CRLFILE], - va_arg(param, char *)); - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_CRLFILE: - /* - * Set CRL file info for SSL connection for proxy. Specify file name of the - * CRL to check certificates revocation - */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CRLFILE_PROXY], - va_arg(param, char *)); - break; -#endif - case CURLOPT_ISSUERCERT: - /* - * Set Issuer certificate file - * to check certificates issuer - */ - result = Curl_setstropt(&data->set.str[STRING_SSL_ISSUERCERT], - va_arg(param, char *)); - break; - case CURLOPT_ISSUERCERT_BLOB: - /* - * Blob that holds Issuer certificate to check certificates issuer - */ - result = Curl_setblobopt(&data->set.blobs[BLOB_SSL_ISSUERCERT], - va_arg(param, struct curl_blob *)); - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_ISSUERCERT: - /* - * Set Issuer certificate file - * to check certificates issuer - */ - result = Curl_setstropt(&data->set.str[STRING_SSL_ISSUERCERT_PROXY], - va_arg(param, char *)); - break; - case CURLOPT_PROXY_ISSUERCERT_BLOB: - /* - * Blob that holds Issuer certificate to check certificates issuer - */ - result = Curl_setblobopt(&data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY], - va_arg(param, struct curl_blob *)); - break; -#endif -#ifndef CURL_DISABLE_TELNET - case CURLOPT_TELNETOPTIONS: - /* - * Set a linked list of telnet options - */ - data->set.telnet_options = va_arg(param, struct curl_slist *); - break; -#endif - case CURLOPT_BUFFERSIZE: - /* - * The application kindly asks for a differently sized receive buffer. - * If it seems reasonable, we'll use it. - */ - if(data->state.buffer) - return CURLE_BAD_FUNCTION_ARGUMENT; - - arg = va_arg(param, long); - - if(arg > READBUFFER_MAX) - arg = READBUFFER_MAX; - else if(arg < 1) - arg = READBUFFER_SIZE; - else if(arg < READBUFFER_MIN) - arg = READBUFFER_MIN; - - data->set.buffer_size = (int)arg; - break; - - case CURLOPT_UPLOAD_BUFFERSIZE: - /* - * The application kindly asks for a differently sized upload buffer. - * Cap it to sensible. - */ - arg = va_arg(param, long); - - if(arg > UPLOADBUFFER_MAX) - arg = UPLOADBUFFER_MAX; - else if(arg < UPLOADBUFFER_MIN) - arg = UPLOADBUFFER_MIN; - - data->set.upload_buffer_size = (unsigned int)arg; - Curl_safefree(data->state.ulbuf); /* force a realloc next opportunity */ - break; - - case CURLOPT_NOSIGNAL: - /* - * The application asks not to set any signal() or alarm() handlers, - * even when using a timeout. - */ - data->set.no_signal = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_SHARE: - { - struct Curl_share *set; - set = va_arg(param, struct Curl_share *); - - /* disconnect from old share, if any */ - if(data->share) { - Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); - - if(data->dns.hostcachetype == HCACHE_SHARED) { - data->dns.hostcache = NULL; - data->dns.hostcachetype = HCACHE_NONE; - } - -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - if(data->share->cookies == data->cookies) - data->cookies = NULL; -#endif - - if(data->share->sslsession == data->state.session) - data->state.session = NULL; - -#ifdef USE_LIBPSL - if(data->psl == &data->share->psl) - data->psl = data->multi? &data->multi->psl: NULL; -#endif - - data->share->dirty--; - - Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); - data->share = NULL; - } - - if(GOOD_SHARE_HANDLE(set)) - /* use new share if it set */ - data->share = set; - if(data->share) { - - Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); - - data->share->dirty++; - - if(data->share->specifier & (1<< CURL_LOCK_DATA_DNS)) { - /* use shared host cache */ - data->dns.hostcache = &data->share->hostcache; - data->dns.hostcachetype = HCACHE_SHARED; - } -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - if(data->share->cookies) { - /* use shared cookie list, first free own one if any */ - Curl_cookie_cleanup(data->cookies); - /* enable cookies since we now use a share that uses cookies! */ - data->cookies = data->share->cookies; - } -#endif /* CURL_DISABLE_HTTP */ - if(data->share->sslsession) { - data->set.general_ssl.max_ssl_sessions = data->share->max_ssl_sessions; - data->state.session = data->share->sslsession; - } -#ifdef USE_LIBPSL - if(data->share->specifier & (1 << CURL_LOCK_DATA_PSL)) - data->psl = &data->share->psl; -#endif - - Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); - } - /* check for host cache not needed, - * it will be done by curl_easy_perform */ - } - break; - - case CURLOPT_PRIVATE: - /* - * Set private data pointer. - */ - data->set.private_data = va_arg(param, void *); - break; - - case CURLOPT_MAXFILESIZE: - /* - * Set the maximum size of a file to download. - */ - arg = va_arg(param, long); - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.max_filesize = arg; - break; - -#ifdef USE_SSL - case CURLOPT_USE_SSL: - /* - * Make transfers attempt to use SSL/TLS. - */ - arg = va_arg(param, long); - if((arg < CURLUSESSL_NONE) || (arg >= CURLUSESSL_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.use_ssl = (curl_usessl)arg; - break; - - case CURLOPT_SSL_OPTIONS: - arg = va_arg(param, long); - data->set.ssl.primary.ssl_options = (unsigned char)(arg & 0xff); - data->set.ssl.enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST); - data->set.ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE); - data->set.ssl.no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN); - data->set.ssl.revoke_best_effort = !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT); - data->set.ssl.native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA); - data->set.ssl.auto_client_cert = !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT); - /* If a setting is added here it should also be added in dohprobe() - which sets its own CURLOPT_SSL_OPTIONS based on these settings. */ - break; - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSL_OPTIONS: - arg = va_arg(param, long); - data->set.proxy_ssl.primary.ssl_options = (unsigned char)(arg & 0xff); - data->set.proxy_ssl.enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST); - data->set.proxy_ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE); - data->set.proxy_ssl.no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN); - data->set.proxy_ssl.revoke_best_effort = - !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT); - data->set.proxy_ssl.native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA); - data->set.proxy_ssl.auto_client_cert = - !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT); - break; -#endif - - case CURLOPT_SSL_EC_CURVES: - /* - * Set accepted curves in SSL connection setup. - * Specify colon-delimited list of curve algorithm names. - */ - result = Curl_setstropt(&data->set.str[STRING_SSL_EC_CURVES], - va_arg(param, char *)); - break; -#endif - case CURLOPT_IPRESOLVE: - arg = va_arg(param, long); - if((arg < CURL_IPRESOLVE_WHATEVER) || (arg > CURL_IPRESOLVE_V6)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.ipver = (unsigned char) arg; - break; - - case CURLOPT_MAXFILESIZE_LARGE: - /* - * Set the maximum size of a file to download. - */ - bigsize = va_arg(param, curl_off_t); - if(bigsize < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.max_filesize = bigsize; - break; - - case CURLOPT_TCP_NODELAY: - /* - * Enable or disable TCP_NODELAY, which will disable/enable the Nagle - * algorithm - */ - data->set.tcp_nodelay = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_IGNORE_CONTENT_LENGTH: - data->set.ignorecl = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_CONNECT_ONLY: - /* - * No data transfer. - * (1) - only do connection - * (2) - do first get request but get no content - */ - arg = va_arg(param, long); - if(arg > 2) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.connect_only = (unsigned char)arg; - break; - - case CURLOPT_SOCKOPTFUNCTION: - /* - * socket callback function: called after socket() but before connect() - */ - data->set.fsockopt = va_arg(param, curl_sockopt_callback); - break; - - case CURLOPT_SOCKOPTDATA: - /* - * socket callback data pointer. Might be NULL. - */ - data->set.sockopt_client = va_arg(param, void *); - break; - - case CURLOPT_OPENSOCKETFUNCTION: - /* - * open/create socket callback function: called instead of socket(), - * before connect() - */ - data->set.fopensocket = va_arg(param, curl_opensocket_callback); - break; - - case CURLOPT_OPENSOCKETDATA: - /* - * socket callback data pointer. Might be NULL. - */ - data->set.opensocket_client = va_arg(param, void *); - break; - - case CURLOPT_CLOSESOCKETFUNCTION: - /* - * close socket callback function: called instead of close() - * when shutting down a connection - */ - data->set.fclosesocket = va_arg(param, curl_closesocket_callback); - break; - - case CURLOPT_RESOLVER_START_FUNCTION: - /* - * resolver start callback function: called before a new resolver request - * is started - */ - data->set.resolver_start = va_arg(param, curl_resolver_start_callback); - break; - - case CURLOPT_RESOLVER_START_DATA: - /* - * resolver start callback data pointer. Might be NULL. - */ - data->set.resolver_start_client = va_arg(param, void *); - break; - - case CURLOPT_CLOSESOCKETDATA: - /* - * socket callback data pointer. Might be NULL. - */ - data->set.closesocket_client = va_arg(param, void *); - break; - - case CURLOPT_SSL_SESSIONID_CACHE: - data->set.ssl.primary.sessionid = (0 != va_arg(param, long)) ? - TRUE : FALSE; -#ifndef CURL_DISABLE_PROXY - data->set.proxy_ssl.primary.sessionid = data->set.ssl.primary.sessionid; -#endif - break; - -#ifdef USE_SSH - /* we only include SSH options if explicitly built to support SSH */ - case CURLOPT_SSH_AUTH_TYPES: - data->set.ssh_auth_types = (unsigned int)va_arg(param, long); - break; - - case CURLOPT_SSH_PUBLIC_KEYFILE: - /* - * Use this file instead of the $HOME/.ssh/id_dsa.pub file - */ - result = Curl_setstropt(&data->set.str[STRING_SSH_PUBLIC_KEY], - va_arg(param, char *)); - break; - - case CURLOPT_SSH_PRIVATE_KEYFILE: - /* - * Use this file instead of the $HOME/.ssh/id_dsa file - */ - result = Curl_setstropt(&data->set.str[STRING_SSH_PRIVATE_KEY], - va_arg(param, char *)); - break; - case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: - /* - * Option to allow for the MD5 of the host public key to be checked - * for validation purposes. - */ - result = Curl_setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5], - va_arg(param, char *)); - break; - - case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256: - /* - * Option to allow for the SHA256 of the host public key to be checked - * for validation purposes. - */ - result = Curl_setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256], - va_arg(param, char *)); - break; - - case CURLOPT_SSH_KNOWNHOSTS: - /* - * Store the file name to read known hosts from. - */ - result = Curl_setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS], - va_arg(param, char *)); - break; -#ifdef USE_LIBSSH2 - case CURLOPT_SSH_HOSTKEYFUNCTION: - /* the callback to check the hostkey without the knownhost file */ - data->set.ssh_hostkeyfunc = va_arg(param, curl_sshhostkeycallback); - break; - - case CURLOPT_SSH_HOSTKEYDATA: - /* - * Custom client data to pass to the SSH keyfunc callback - */ - data->set.ssh_hostkeyfunc_userp = va_arg(param, void *); - break; -#endif - case CURLOPT_SSH_KEYFUNCTION: - /* setting to NULL is fine since the ssh.c functions themselves will - then revert to use the internal default */ - data->set.ssh_keyfunc = va_arg(param, curl_sshkeycallback); - break; - - case CURLOPT_SSH_KEYDATA: - /* - * Custom client data to pass to the SSH keyfunc callback - */ - data->set.ssh_keyfunc_userp = va_arg(param, void *); - break; - - case CURLOPT_SSH_COMPRESSION: - data->set.ssh_compression = (0 != va_arg(param, long))?TRUE:FALSE; - break; -#endif /* USE_SSH */ - - case CURLOPT_HTTP_TRANSFER_DECODING: - /* - * disable libcurl transfer encoding is used - */ -#ifndef USE_HYPER - data->set.http_te_skip = (0 == va_arg(param, long)) ? TRUE : FALSE; - break; -#else - return CURLE_NOT_BUILT_IN; /* hyper doesn't support */ -#endif - - case CURLOPT_HTTP_CONTENT_DECODING: - /* - * raw data passed to the application when content encoding is used - */ - data->set.http_ce_skip = (0 == va_arg(param, long)) ? TRUE : FALSE; - break; - -#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) - case CURLOPT_NEW_FILE_PERMS: - /* - * Uses these permissions instead of 0644 - */ - arg = va_arg(param, long); - if((arg < 0) || (arg > 0777)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.new_file_perms = (unsigned int)arg; - break; - - case CURLOPT_NEW_DIRECTORY_PERMS: - /* - * Uses these permissions instead of 0755 - */ - arg = va_arg(param, long); - if((arg < 0) || (arg > 0777)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.new_directory_perms = (unsigned int)arg; - break; -#endif - -#ifdef ENABLE_IPV6 - case CURLOPT_ADDRESS_SCOPE: - /* - * Use this scope id when using IPv6 - * We always get longs when passed plain numericals so we should check - * that the value fits into an unsigned 32 bit integer. - */ - uarg = va_arg(param, unsigned long); -#if SIZEOF_LONG > 4 - if(uarg > UINT_MAX) - return CURLE_BAD_FUNCTION_ARGUMENT; -#endif - data->set.scope_id = (unsigned int)uarg; - break; -#endif - - case CURLOPT_PROTOCOLS: - /* set the bitmask for the protocols that are allowed to be used for the - transfer, which thus helps the app which takes URLs from users or other - external inputs and want to restrict what protocol(s) to deal - with. Defaults to CURLPROTO_ALL. */ - data->set.allowed_protocols = (curl_prot_t)va_arg(param, long); - break; - - case CURLOPT_REDIR_PROTOCOLS: - /* set the bitmask for the protocols that libcurl is allowed to follow to, - as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs - to be set in both bitmasks to be allowed to get redirected to. */ - data->set.redir_protocols = (curl_prot_t)va_arg(param, long); - break; - - case CURLOPT_PROTOCOLS_STR: { - curl_prot_t prot; - argptr = va_arg(param, char *); - result = protocol2num(argptr, &prot); - if(result) - return result; - data->set.allowed_protocols = prot; - break; - } - - case CURLOPT_REDIR_PROTOCOLS_STR: { - curl_prot_t prot; - argptr = va_arg(param, char *); - result = protocol2num(argptr, &prot); - if(result) - return result; - data->set.redir_protocols = prot; - break; - } - - case CURLOPT_DEFAULT_PROTOCOL: - /* Set the protocol to use when the URL doesn't include any protocol */ - result = Curl_setstropt(&data->set.str[STRING_DEFAULT_PROTOCOL], - va_arg(param, char *)); - break; -#ifndef CURL_DISABLE_SMTP - case CURLOPT_MAIL_FROM: - /* Set the SMTP mail originator */ - result = Curl_setstropt(&data->set.str[STRING_MAIL_FROM], - va_arg(param, char *)); - break; - - case CURLOPT_MAIL_AUTH: - /* Set the SMTP auth originator */ - result = Curl_setstropt(&data->set.str[STRING_MAIL_AUTH], - va_arg(param, char *)); - break; - - case CURLOPT_MAIL_RCPT: - /* Set the list of mail recipients */ - data->set.mail_rcpt = va_arg(param, struct curl_slist *); - break; - case CURLOPT_MAIL_RCPT_ALLLOWFAILS: - /* allow RCPT TO command to fail for some recipients */ - data->set.mail_rcpt_allowfails = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; -#endif - - case CURLOPT_SASL_AUTHZID: - /* Authorization identity (identity to act as) */ - result = Curl_setstropt(&data->set.str[STRING_SASL_AUTHZID], - va_arg(param, char *)); - break; - - case CURLOPT_SASL_IR: - /* Enable/disable SASL initial response */ - data->set.sasl_ir = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; -#ifndef CURL_DISABLE_RTSP - case CURLOPT_RTSP_REQUEST: - { - /* - * Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...) - * Would this be better if the RTSPREQ_* were just moved into here? - */ - long in_rtspreq = va_arg(param, long); - Curl_RtspReq rtspreq = RTSPREQ_NONE; - switch(in_rtspreq) { - case CURL_RTSPREQ_OPTIONS: - rtspreq = RTSPREQ_OPTIONS; - break; - - case CURL_RTSPREQ_DESCRIBE: - rtspreq = RTSPREQ_DESCRIBE; - break; - - case CURL_RTSPREQ_ANNOUNCE: - rtspreq = RTSPREQ_ANNOUNCE; - break; - - case CURL_RTSPREQ_SETUP: - rtspreq = RTSPREQ_SETUP; - break; - - case CURL_RTSPREQ_PLAY: - rtspreq = RTSPREQ_PLAY; - break; - - case CURL_RTSPREQ_PAUSE: - rtspreq = RTSPREQ_PAUSE; - break; - - case CURL_RTSPREQ_TEARDOWN: - rtspreq = RTSPREQ_TEARDOWN; - break; - - case CURL_RTSPREQ_GET_PARAMETER: - rtspreq = RTSPREQ_GET_PARAMETER; - break; - - case CURL_RTSPREQ_SET_PARAMETER: - rtspreq = RTSPREQ_SET_PARAMETER; - break; - - case CURL_RTSPREQ_RECORD: - rtspreq = RTSPREQ_RECORD; - break; - - case CURL_RTSPREQ_RECEIVE: - rtspreq = RTSPREQ_RECEIVE; - break; - default: - rtspreq = RTSPREQ_NONE; - } - - data->set.rtspreq = rtspreq; - break; - } - - - case CURLOPT_RTSP_SESSION_ID: - /* - * Set the RTSP Session ID manually. Useful if the application is - * resuming a previously established RTSP session - */ - result = Curl_setstropt(&data->set.str[STRING_RTSP_SESSION_ID], - va_arg(param, char *)); - break; - - case CURLOPT_RTSP_STREAM_URI: - /* - * Set the Stream URI for the RTSP request. Unless the request is - * for generic server options, the application will need to set this. - */ - result = Curl_setstropt(&data->set.str[STRING_RTSP_STREAM_URI], - va_arg(param, char *)); - break; - - case CURLOPT_RTSP_TRANSPORT: - /* - * The content of the Transport: header for the RTSP request - */ - result = Curl_setstropt(&data->set.str[STRING_RTSP_TRANSPORT], - va_arg(param, char *)); - break; - - case CURLOPT_RTSP_CLIENT_CSEQ: - /* - * Set the CSEQ number to issue for the next RTSP request. Useful if the - * application is resuming a previously broken connection. The CSEQ - * will increment from this new number henceforth. - */ - data->state.rtsp_next_client_CSeq = va_arg(param, long); - break; - - case CURLOPT_RTSP_SERVER_CSEQ: - /* Same as the above, but for server-initiated requests */ - data->state.rtsp_next_server_CSeq = va_arg(param, long); - break; - - case CURLOPT_INTERLEAVEDATA: - data->set.rtp_out = va_arg(param, void *); - break; - case CURLOPT_INTERLEAVEFUNCTION: - /* Set the user defined RTP write function */ - data->set.fwrite_rtp = va_arg(param, curl_write_callback); - break; -#endif -#ifndef CURL_DISABLE_FTP - case CURLOPT_WILDCARDMATCH: - data->set.wildcard_enabled = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_CHUNK_BGN_FUNCTION: - data->set.chunk_bgn = va_arg(param, curl_chunk_bgn_callback); - break; - case CURLOPT_CHUNK_END_FUNCTION: - data->set.chunk_end = va_arg(param, curl_chunk_end_callback); - break; - case CURLOPT_FNMATCH_FUNCTION: - data->set.fnmatch = va_arg(param, curl_fnmatch_callback); - break; - case CURLOPT_CHUNK_DATA: - data->wildcard.customptr = va_arg(param, void *); - break; - case CURLOPT_FNMATCH_DATA: - data->set.fnmatch_data = va_arg(param, void *); - break; -#endif -#ifdef USE_TLS_SRP - case CURLOPT_TLSAUTH_USERNAME: - result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME], - va_arg(param, char *)); - if(data->set.str[STRING_TLSAUTH_USERNAME] && - !data->set.ssl.primary.authtype) - data->set.ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_TLSAUTH_USERNAME: - result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME_PROXY], - va_arg(param, char *)); - if(data->set.str[STRING_TLSAUTH_USERNAME_PROXY] && - !data->set.proxy_ssl.primary.authtype) - data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default to - SRP */ - break; -#endif - case CURLOPT_TLSAUTH_PASSWORD: - result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD], - va_arg(param, char *)); - if(data->set.str[STRING_TLSAUTH_USERNAME] && - !data->set.ssl.primary.authtype) - data->set.ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default */ - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_TLSAUTH_PASSWORD: - result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD_PROXY], - va_arg(param, char *)); - if(data->set.str[STRING_TLSAUTH_USERNAME_PROXY] && - !data->set.proxy_ssl.primary.authtype) - data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default */ - break; -#endif - case CURLOPT_TLSAUTH_TYPE: - argptr = va_arg(param, char *); - if(!argptr || - strncasecompare(argptr, "SRP", strlen("SRP"))) - data->set.ssl.primary.authtype = CURL_TLSAUTH_SRP; - else - data->set.ssl.primary.authtype = CURL_TLSAUTH_NONE; - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_TLSAUTH_TYPE: - argptr = va_arg(param, char *); - if(!argptr || - strncasecompare(argptr, "SRP", strlen("SRP"))) - data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_SRP; - else - data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_NONE; - break; -#endif -#endif -#ifdef USE_ARES - case CURLOPT_DNS_SERVERS: - result = Curl_setstropt(&data->set.str[STRING_DNS_SERVERS], - va_arg(param, char *)); - if(result) - return result; - result = Curl_set_dns_servers(data, data->set.str[STRING_DNS_SERVERS]); - break; - case CURLOPT_DNS_INTERFACE: - result = Curl_setstropt(&data->set.str[STRING_DNS_INTERFACE], - va_arg(param, char *)); - if(result) - return result; - result = Curl_set_dns_interface(data, data->set.str[STRING_DNS_INTERFACE]); - break; - case CURLOPT_DNS_LOCAL_IP4: - result = Curl_setstropt(&data->set.str[STRING_DNS_LOCAL_IP4], - va_arg(param, char *)); - if(result) - return result; - result = Curl_set_dns_local_ip4(data, data->set.str[STRING_DNS_LOCAL_IP4]); - break; - case CURLOPT_DNS_LOCAL_IP6: - result = Curl_setstropt(&data->set.str[STRING_DNS_LOCAL_IP6], - va_arg(param, char *)); - if(result) - return result; - result = Curl_set_dns_local_ip6(data, data->set.str[STRING_DNS_LOCAL_IP6]); - break; -#endif - case CURLOPT_TCP_KEEPALIVE: - data->set.tcp_keepalive = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_TCP_KEEPIDLE: - arg = va_arg(param, long); - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - else if(arg > INT_MAX) - arg = INT_MAX; - data->set.tcp_keepidle = (int)arg; - break; - case CURLOPT_TCP_KEEPINTVL: - arg = va_arg(param, long); - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - else if(arg > INT_MAX) - arg = INT_MAX; - data->set.tcp_keepintvl = (int)arg; - break; - case CURLOPT_TCP_FASTOPEN: -#if defined(CONNECT_DATA_IDEMPOTENT) || defined(MSG_FASTOPEN) || \ - defined(TCP_FASTOPEN_CONNECT) - data->set.tcp_fastopen = (0 != va_arg(param, long))?TRUE:FALSE; -#else - result = CURLE_NOT_BUILT_IN; -#endif - break; - case CURLOPT_SSL_ENABLE_NPN: - break; - case CURLOPT_SSL_ENABLE_ALPN: - data->set.ssl_enable_alpn = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; -#ifdef USE_UNIX_SOCKETS - case CURLOPT_UNIX_SOCKET_PATH: - data->set.abstract_unix_socket = FALSE; - result = Curl_setstropt(&data->set.str[STRING_UNIX_SOCKET_PATH], - va_arg(param, char *)); - break; - case CURLOPT_ABSTRACT_UNIX_SOCKET: - data->set.abstract_unix_socket = TRUE; - result = Curl_setstropt(&data->set.str[STRING_UNIX_SOCKET_PATH], - va_arg(param, char *)); - break; -#endif - - case CURLOPT_PATH_AS_IS: - data->set.path_as_is = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_PIPEWAIT: - data->set.pipewait = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - case CURLOPT_STREAM_WEIGHT: -#ifndef USE_NGHTTP2 - return CURLE_NOT_BUILT_IN; -#else - arg = va_arg(param, long); - if((arg >= 1) && (arg <= 256)) - data->set.stream_weight = (int)arg; - break; -#endif - case CURLOPT_STREAM_DEPENDS: - case CURLOPT_STREAM_DEPENDS_E: - { -#ifndef USE_NGHTTP2 - return CURLE_NOT_BUILT_IN; -#else - struct Curl_easy *dep = va_arg(param, struct Curl_easy *); - if(!dep || GOOD_EASY_HANDLE(dep)) { - if(data->set.stream_depends_on) { - Curl_http2_remove_child(data->set.stream_depends_on, data); - } - Curl_http2_add_child(dep, data, (option == CURLOPT_STREAM_DEPENDS_E)); - } - break; -#endif - } - case CURLOPT_CONNECT_TO: - data->set.connect_to = va_arg(param, struct curl_slist *); - break; - case CURLOPT_SUPPRESS_CONNECT_HEADERS: - data->set.suppress_connect_headers = (0 != va_arg(param, long))?TRUE:FALSE; - break; - case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS: - uarg = va_arg(param, unsigned long); - if(uarg >= UINT_MAX) - uarg = UINT_MAX; - data->set.happy_eyeballs_timeout = (unsigned int)uarg; - break; -#ifndef CURL_DISABLE_SHUFFLE_DNS - case CURLOPT_DNS_SHUFFLE_ADDRESSES: - data->set.dns_shuffle_addresses = (0 != va_arg(param, long)) ? TRUE:FALSE; - break; -#endif - case CURLOPT_DISALLOW_USERNAME_IN_URL: - data->set.disallow_username_in_url = - (0 != va_arg(param, long)) ? TRUE : FALSE; - break; -#ifndef CURL_DISABLE_DOH - case CURLOPT_DOH_URL: - result = Curl_setstropt(&data->set.str[STRING_DOH], - va_arg(param, char *)); - data->set.doh = data->set.str[STRING_DOH]?TRUE:FALSE; - break; -#endif - case CURLOPT_UPKEEP_INTERVAL_MS: - arg = va_arg(param, long); - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.upkeep_interval_ms = arg; - break; - case CURLOPT_MAXAGE_CONN: - arg = va_arg(param, long); - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.maxage_conn = arg; - break; - case CURLOPT_MAXLIFETIME_CONN: - arg = va_arg(param, long); - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.maxlifetime_conn = arg; - break; - case CURLOPT_TRAILERFUNCTION: -#ifndef CURL_DISABLE_HTTP - data->set.trailer_callback = va_arg(param, curl_trailer_callback); -#endif - break; - case CURLOPT_TRAILERDATA: -#ifndef CURL_DISABLE_HTTP - data->set.trailer_data = va_arg(param, void *); -#endif - break; -#ifndef CURL_DISABLE_HSTS - case CURLOPT_HSTSREADFUNCTION: - data->set.hsts_read = va_arg(param, curl_hstsread_callback); - break; - case CURLOPT_HSTSREADDATA: - data->set.hsts_read_userp = va_arg(param, void *); - break; - case CURLOPT_HSTSWRITEFUNCTION: - data->set.hsts_write = va_arg(param, curl_hstswrite_callback); - break; - case CURLOPT_HSTSWRITEDATA: - data->set.hsts_write_userp = va_arg(param, void *); - break; - case CURLOPT_HSTS: - if(!data->hsts) { - data->hsts = Curl_hsts_init(); - if(!data->hsts) - return CURLE_OUT_OF_MEMORY; - } - argptr = va_arg(param, char *); - result = Curl_setstropt(&data->set.str[STRING_HSTS], argptr); - if(result) - return result; - if(argptr) - (void)Curl_hsts_loadfile(data, data->hsts, argptr); - break; - case CURLOPT_HSTS_CTRL: - arg = va_arg(param, long); - if(arg & CURLHSTS_ENABLE) { - if(!data->hsts) { - data->hsts = Curl_hsts_init(); - if(!data->hsts) - return CURLE_OUT_OF_MEMORY; - } - } - else - Curl_hsts_cleanup(&data->hsts); - break; -#endif -#ifndef CURL_DISABLE_ALTSVC - case CURLOPT_ALTSVC: - if(!data->asi) { - data->asi = Curl_altsvc_init(); - if(!data->asi) - return CURLE_OUT_OF_MEMORY; - } - argptr = va_arg(param, char *); - result = Curl_setstropt(&data->set.str[STRING_ALTSVC], argptr); - if(result) - return result; - if(argptr) - (void)Curl_altsvc_load(data->asi, argptr); - break; - case CURLOPT_ALTSVC_CTRL: - if(!data->asi) { - data->asi = Curl_altsvc_init(); - if(!data->asi) - return CURLE_OUT_OF_MEMORY; - } - arg = va_arg(param, long); - result = Curl_altsvc_ctrl(data->asi, arg); - if(result) - return result; - break; -#endif - case CURLOPT_PREREQFUNCTION: - data->set.fprereq = va_arg(param, curl_prereq_callback); - break; - case CURLOPT_PREREQDATA: - data->set.prereq_userp = va_arg(param, void *); - break; -#ifdef USE_WEBSOCKETS - case CURLOPT_WS_OPTIONS: { - bool raw; - arg = va_arg(param, long); - raw = (arg & CURLWS_RAW_MODE); - data->set.ws_raw_mode = raw; - break; - } -#endif - default: - /* unknown tag and its companion, just ignore: */ - result = CURLE_UNKNOWN_OPTION; - break; - } - - return result; -} - -/* - * curl_easy_setopt() is the external interface for setting options on an - * easy handle. - * - * NOTE: This is one of few API functions that are allowed to be called from - * within a callback. - */ - -#undef curl_easy_setopt -CURLcode curl_easy_setopt(struct Curl_easy *data, CURLoption tag, ...) -{ - va_list arg; - CURLcode result; - - if(!data) - return CURLE_BAD_FUNCTION_ARGUMENT; - - va_start(arg, tag); - - result = Curl_vsetopt(data, tag, arg); - - va_end(arg); - return result; -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 <limits.h>
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_LINUX_TCP_H
+#include <linux/tcp.h>
+#elif defined(HAVE_NETINET_TCP_H)
+#include <netinet/tcp.h>
+#endif
+
+#include "urldata.h"
+#include "url.h"
+#include "progress.h"
+#include "content_encoding.h"
+#include "strcase.h"
+#include "share.h"
+#include "vtls/vtls.h"
+#include "warnless.h"
+#include "sendf.h"
+#include "http2.h"
+#include "setopt.h"
+#include "multiif.h"
+#include "altsvc.h"
+#include "hsts.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+CURLcode Curl_setstropt(char **charp, const char *s)
+{
+ /* Release the previous storage at `charp' and replace by a dynamic storage
+ copy of `s'. Return CURLE_OK or CURLE_OUT_OF_MEMORY. */
+
+ Curl_safefree(*charp);
+
+ if(s) {
+ if(strlen(s) > CURL_MAX_INPUT_LENGTH)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ *charp = strdup(s);
+ if(!*charp)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ return CURLE_OK;
+}
+
+CURLcode Curl_setblobopt(struct curl_blob **blobp,
+ const struct curl_blob *blob)
+{
+ /* free the previous storage at `blobp' and replace by a dynamic storage
+ copy of blob. If CURL_BLOB_COPY is set, the data is copied. */
+
+ Curl_safefree(*blobp);
+
+ if(blob) {
+ struct curl_blob *nblob;
+ if(blob->len > CURL_MAX_INPUT_LENGTH)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ nblob = (struct curl_blob *)
+ malloc(sizeof(struct curl_blob) +
+ ((blob->flags & CURL_BLOB_COPY) ? blob->len : 0));
+ if(!nblob)
+ return CURLE_OUT_OF_MEMORY;
+ *nblob = *blob;
+ if(blob->flags & CURL_BLOB_COPY) {
+ /* put the data after the blob struct in memory */
+ nblob->data = (char *)nblob + sizeof(struct curl_blob);
+ memcpy(nblob->data, blob->data, blob->len);
+ }
+
+ *blobp = nblob;
+ return CURLE_OK;
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp)
+{
+ CURLcode result = CURLE_OK;
+ char *user = NULL;
+ char *passwd = NULL;
+
+ /* Parse the login details if specified. It not then we treat NULL as a hint
+ to clear the existing data */
+ if(option) {
+ result = Curl_parse_login_details(option, strlen(option),
+ (userp ? &user : NULL),
+ (passwdp ? &passwd : NULL),
+ NULL);
+ }
+
+ if(!result) {
+ /* Store the username part of option if required */
+ if(userp) {
+ if(!user && option && option[0] == ':') {
+ /* Allocate an empty string instead of returning NULL as user name */
+ user = strdup("");
+ if(!user)
+ result = CURLE_OUT_OF_MEMORY;
+ }
+
+ Curl_safefree(*userp);
+ *userp = user;
+ }
+
+ /* Store the password part of option if required */
+ if(passwdp) {
+ Curl_safefree(*passwdp);
+ *passwdp = passwd;
+ }
+ }
+
+ return result;
+}
+
+#define C_SSLVERSION_VALUE(x) (x & 0xffff)
+#define C_SSLVERSION_MAX_VALUE(x) (x & 0xffff0000)
+
+static CURLcode protocol2num(const char *str, curl_prot_t *val)
+{
+ if(!str)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ if(curl_strequal(str, "all")) {
+ *val = ~(curl_prot_t) 0;
+ return CURLE_OK;
+ }
+
+ *val = 0;
+
+ do {
+ const char *token = str;
+ size_t tlen;
+
+ str = strchr(str, ',');
+ tlen = str? (size_t) (str - token): strlen(token);
+ if(tlen) {
+ const struct Curl_handler *h = Curl_builtin_scheme(token, tlen);
+
+ if(!h)
+ return CURLE_UNSUPPORTED_PROTOCOL;
+
+ *val |= h->protocol;
+ }
+ } while(str++);
+
+ if(!*val)
+ /* no protocol listed */
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ return CURLE_OK;
+}
+
+/*
+ * Do not make Curl_vsetopt() static: it is called from
+ * packages/OS400/ccsidcurl.c.
+ */
+CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
+{
+ char *argptr;
+ CURLcode result = CURLE_OK;
+ long arg;
+ unsigned long uarg;
+ curl_off_t bigsize;
+
+ switch(option) {
+ case CURLOPT_DNS_CACHE_TIMEOUT:
+ arg = va_arg(param, long);
+ if(arg < -1)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ else if(arg > INT_MAX)
+ arg = INT_MAX;
+
+ data->set.dns_cache_timeout = (int)arg;
+ break;
+ case CURLOPT_CA_CACHE_TIMEOUT:
+ arg = va_arg(param, long);
+ if(arg < -1)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ else if(arg > INT_MAX)
+ arg = INT_MAX;
+
+ data->set.general_ssl.ca_cache_timeout = (int)arg;
+ break;
+ case CURLOPT_DNS_USE_GLOBAL_CACHE:
+ /* deprecated */
+ break;
+ case CURLOPT_SSL_CIPHER_LIST:
+ /* set a list of cipher we want to use in the SSL connection */
+ result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER_LIST],
+ va_arg(param, char *));
+ break;
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXY_SSL_CIPHER_LIST:
+ /* set a list of cipher we want to use in the SSL connection for proxy */
+ result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER_LIST_PROXY],
+ va_arg(param, char *));
+ break;
+#endif
+ case CURLOPT_TLS13_CIPHERS:
+ if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) {
+ /* set preferred list of TLS 1.3 cipher suites */
+ result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER13_LIST],
+ va_arg(param, char *));
+ }
+ else
+ return CURLE_NOT_BUILT_IN;
+ break;
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXY_TLS13_CIPHERS:
+ if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) {
+ /* set preferred list of TLS 1.3 cipher suites for proxy */
+ result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER13_LIST_PROXY],
+ va_arg(param, char *));
+ }
+ else
+ return CURLE_NOT_BUILT_IN;
+ break;
+#endif
+ case CURLOPT_RANDOM_FILE:
+ break;
+ case CURLOPT_EGDSOCKET:
+ break;
+ case CURLOPT_MAXCONNECTS:
+ /*
+ * Set the absolute number of maximum simultaneous alive connection that
+ * libcurl is allowed to have.
+ */
+ arg = va_arg(param, long);
+ if(arg < 0)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.maxconnects = arg;
+ break;
+ case CURLOPT_FORBID_REUSE:
+ /*
+ * When this transfer is done, it must not be left to be reused by a
+ * subsequent transfer but shall be closed immediately.
+ */
+ data->set.reuse_forbid = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+ case CURLOPT_FRESH_CONNECT:
+ /*
+ * This transfer shall not use a previously cached connection but
+ * should be made with a fresh new connect!
+ */
+ data->set.reuse_fresh = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+ case CURLOPT_VERBOSE:
+ /*
+ * Verbose means infof() calls that give a lot of information about
+ * the connection and transfer procedures as well as internal choices.
+ */
+ data->set.verbose = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+ case CURLOPT_HEADER:
+ /*
+ * Set to include the header in the general data output stream.
+ */
+ data->set.include_header = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+ case CURLOPT_NOPROGRESS:
+ /*
+ * Shut off the internal supported progress meter
+ */
+ data->set.hide_progress = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ if(data->set.hide_progress)
+ data->progress.flags |= PGRS_HIDE;
+ else
+ data->progress.flags &= ~PGRS_HIDE;
+ break;
+ case CURLOPT_NOBODY:
+ /*
+ * Do not include the body part in the output data stream.
+ */
+ data->set.opt_no_body = (0 != va_arg(param, long)) ? TRUE : FALSE;
+#ifndef CURL_DISABLE_HTTP
+ if(data->set.opt_no_body)
+ /* in HTTP lingo, no body means using the HEAD request... */
+ data->set.method = HTTPREQ_HEAD;
+ else if(data->set.method == HTTPREQ_HEAD)
+ data->set.method = HTTPREQ_GET;
+#endif
+ break;
+ case CURLOPT_FAILONERROR:
+ /*
+ * Don't output the >=400 error code HTML-page, but instead only
+ * return error.
+ */
+ data->set.http_fail_on_error = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+ case CURLOPT_KEEP_SENDING_ON_ERROR:
+ data->set.http_keep_sending_on_error = (0 != va_arg(param, long)) ?
+ TRUE : FALSE;
+ break;
+ case CURLOPT_UPLOAD:
+ case CURLOPT_PUT:
+ /*
+ * We want to sent data to the remote host. If this is HTTP, that equals
+ * using the PUT request.
+ */
+ data->set.upload = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ if(data->set.upload) {
+ /* If this is HTTP, PUT is what's needed to "upload" */
+ data->set.method = HTTPREQ_PUT;
+ data->set.opt_no_body = FALSE; /* this is implied */
+ }
+ else
+ /* In HTTP, the opposite of upload is GET (unless NOBODY is true as
+ then this can be changed to HEAD later on) */
+ data->set.method = HTTPREQ_GET;
+ break;
+ case CURLOPT_REQUEST_TARGET:
+ result = Curl_setstropt(&data->set.str[STRING_TARGET],
+ va_arg(param, char *));
+ break;
+ case CURLOPT_FILETIME:
+ /*
+ * Try to get the file time of the remote document. The time will
+ * later (possibly) become available using curl_easy_getinfo().
+ */
+ data->set.get_filetime = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+ case CURLOPT_SERVER_RESPONSE_TIMEOUT:
+ /*
+ * Option that specifies how quickly a server response must be obtained
+ * before it is considered failure. For pingpong protocols.
+ */
+ arg = va_arg(param, long);
+ if((arg >= 0) && (arg <= (INT_MAX/1000)))
+ data->set.server_response_timeout = (unsigned int)arg * 1000;
+ else
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ break;
+#ifndef CURL_DISABLE_TFTP
+ case CURLOPT_TFTP_NO_OPTIONS:
+ /*
+ * Option that prevents libcurl from sending TFTP option requests to the
+ * server.
+ */
+ data->set.tftp_no_options = va_arg(param, long) != 0;
+ break;
+ case CURLOPT_TFTP_BLKSIZE:
+ /*
+ * TFTP option that specifies the block size to use for data transmission.
+ */
+ arg = va_arg(param, long);
+ if(arg < 0)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.tftp_blksize = arg;
+ break;
+#endif
+#ifndef CURL_DISABLE_NETRC
+ case CURLOPT_NETRC:
+ /*
+ * Parse the $HOME/.netrc file
+ */
+ arg = va_arg(param, long);
+ if((arg < CURL_NETRC_IGNORED) || (arg >= CURL_NETRC_LAST))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.use_netrc = (unsigned char)arg;
+ break;
+ case CURLOPT_NETRC_FILE:
+ /*
+ * Use this file instead of the $HOME/.netrc file
+ */
+ result = Curl_setstropt(&data->set.str[STRING_NETRC_FILE],
+ va_arg(param, char *));
+ break;
+#endif
+ case CURLOPT_TRANSFERTEXT:
+ /*
+ * This option was previously named 'FTPASCII'. Renamed to work with
+ * more protocols than merely FTP.
+ *
+ * Transfer using ASCII (instead of BINARY).
+ */
+ data->set.prefer_ascii = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+ case CURLOPT_TIMECONDITION:
+ /*
+ * Set HTTP time condition. This must be one of the defines in the
+ * curl/curl.h header file.
+ */
+ arg = va_arg(param, long);
+ if((arg < CURL_TIMECOND_NONE) || (arg >= CURL_TIMECOND_LAST))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.timecondition = (unsigned char)(curl_TimeCond)arg;
+ break;
+ case CURLOPT_TIMEVALUE:
+ /*
+ * This is the value to compare with the remote document with the
+ * method set with CURLOPT_TIMECONDITION
+ */
+ data->set.timevalue = (time_t)va_arg(param, long);
+ break;
+
+ case CURLOPT_TIMEVALUE_LARGE:
+ /*
+ * This is the value to compare with the remote document with the
+ * method set with CURLOPT_TIMECONDITION
+ */
+ data->set.timevalue = (time_t)va_arg(param, curl_off_t);
+ break;
+
+ case CURLOPT_SSLVERSION:
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXY_SSLVERSION:
+#endif
+ /*
+ * Set explicit SSL version to try to connect with, as some SSL
+ * implementations are lame.
+ */
+#ifdef USE_SSL
+ {
+ long version, version_max;
+ struct ssl_primary_config *primary = &data->set.ssl.primary;
+#ifndef CURL_DISABLE_PROXY
+ if(option != CURLOPT_SSLVERSION)
+ primary = &data->set.proxy_ssl.primary;
+#endif
+
+ arg = va_arg(param, long);
+
+ version = C_SSLVERSION_VALUE(arg);
+ version_max = C_SSLVERSION_MAX_VALUE(arg);
+
+ if(version < CURL_SSLVERSION_DEFAULT ||
+ version == CURL_SSLVERSION_SSLv2 ||
+ version == CURL_SSLVERSION_SSLv3 ||
+ version >= CURL_SSLVERSION_LAST ||
+ version_max < CURL_SSLVERSION_MAX_NONE ||
+ version_max >= CURL_SSLVERSION_MAX_LAST)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ primary->version = version;
+ primary->version_max = version_max;
+ }
+#else
+ result = CURLE_NOT_BUILT_IN;
+#endif
+ break;
+
+ /* MQTT "borrows" some of the HTTP options */
+#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MQTT)
+ case CURLOPT_COPYPOSTFIELDS:
+ /*
+ * A string with POST data. Makes curl HTTP POST. Even if it is NULL.
+ * If needed, CURLOPT_POSTFIELDSIZE must have been set prior to
+ * CURLOPT_COPYPOSTFIELDS and not altered later.
+ */
+ argptr = va_arg(param, char *);
+
+ if(!argptr || data->set.postfieldsize == -1)
+ result = Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], argptr);
+ else {
+ /*
+ * Check that requested length does not overflow the size_t type.
+ */
+
+ if((data->set.postfieldsize < 0) ||
+ ((sizeof(curl_off_t) != sizeof(size_t)) &&
+ (data->set.postfieldsize > (curl_off_t)((size_t)-1))))
+ result = CURLE_OUT_OF_MEMORY;
+ else {
+ char *p;
+
+ (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
+
+ /* Allocate even when size == 0. This satisfies the need of possible
+ later address compare to detect the COPYPOSTFIELDS mode, and
+ to mark that postfields is used rather than read function or
+ form data.
+ */
+ p = malloc((size_t)(data->set.postfieldsize?
+ data->set.postfieldsize:1));
+
+ if(!p)
+ result = CURLE_OUT_OF_MEMORY;
+ else {
+ if(data->set.postfieldsize)
+ memcpy(p, argptr, (size_t)data->set.postfieldsize);
+
+ data->set.str[STRING_COPYPOSTFIELDS] = p;
+ }
+ }
+ }
+
+ data->set.postfields = data->set.str[STRING_COPYPOSTFIELDS];
+ data->set.method = HTTPREQ_POST;
+ break;
+
+ case CURLOPT_POSTFIELDS:
+ /*
+ * Like above, but use static data instead of copying it.
+ */
+ data->set.postfields = va_arg(param, void *);
+ /* Release old copied data. */
+ (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
+ data->set.method = HTTPREQ_POST;
+ break;
+
+ case CURLOPT_POSTFIELDSIZE:
+ /*
+ * The size of the POSTFIELD data to prevent libcurl to do strlen() to
+ * figure it out. Enables binary posts.
+ */
+ bigsize = va_arg(param, long);
+ if(bigsize < -1)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ if(data->set.postfieldsize < bigsize &&
+ data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) {
+ /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */
+ (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
+ data->set.postfields = NULL;
+ }
+
+ data->set.postfieldsize = bigsize;
+ break;
+
+ case CURLOPT_POSTFIELDSIZE_LARGE:
+ /*
+ * The size of the POSTFIELD data to prevent libcurl to do strlen() to
+ * figure it out. Enables binary posts.
+ */
+ bigsize = va_arg(param, curl_off_t);
+ if(bigsize < -1)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ if(data->set.postfieldsize < bigsize &&
+ data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) {
+ /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */
+ (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
+ data->set.postfields = NULL;
+ }
+
+ data->set.postfieldsize = bigsize;
+ break;
+#endif
+#ifndef CURL_DISABLE_HTTP
+ case CURLOPT_AUTOREFERER:
+ /*
+ * Switch on automatic referer that gets set if curl follows locations.
+ */
+ data->set.http_auto_referer = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+
+ case CURLOPT_ACCEPT_ENCODING:
+ /*
+ * String to use at the value of Accept-Encoding header.
+ *
+ * If the encoding is set to "" we use an Accept-Encoding header that
+ * encompasses all the encodings we support.
+ * If the encoding is set to NULL we don't send an Accept-Encoding header
+ * and ignore an received Content-Encoding header.
+ *
+ */
+ argptr = va_arg(param, char *);
+ if(argptr && !*argptr) {
+ argptr = Curl_all_content_encodings();
+ if(!argptr)
+ result = CURLE_OUT_OF_MEMORY;
+ else {
+ result = Curl_setstropt(&data->set.str[STRING_ENCODING], argptr);
+ free(argptr);
+ }
+ }
+ else
+ result = Curl_setstropt(&data->set.str[STRING_ENCODING], argptr);
+ break;
+
+ case CURLOPT_TRANSFER_ENCODING:
+ data->set.http_transfer_encoding = (0 != va_arg(param, long)) ?
+ TRUE : FALSE;
+ break;
+
+ case CURLOPT_FOLLOWLOCATION:
+ /*
+ * Follow Location: header hints on an HTTP-server.
+ */
+ data->set.http_follow_location = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+
+ case CURLOPT_UNRESTRICTED_AUTH:
+ /*
+ * Send authentication (user+password) when following locations, even when
+ * hostname changed.
+ */
+ data->set.allow_auth_to_other_hosts =
+ (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+
+ case CURLOPT_MAXREDIRS:
+ /*
+ * The maximum amount of hops you allow curl to follow Location:
+ * headers. This should mostly be used to detect never-ending loops.
+ */
+ arg = va_arg(param, long);
+ if(arg < -1)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.maxredirs = arg;
+ break;
+
+ case CURLOPT_POSTREDIR:
+ /*
+ * Set the behavior of POST when redirecting
+ * CURL_REDIR_GET_ALL - POST is changed to GET after 301 and 302
+ * CURL_REDIR_POST_301 - POST is kept as POST after 301
+ * CURL_REDIR_POST_302 - POST is kept as POST after 302
+ * CURL_REDIR_POST_303 - POST is kept as POST after 303
+ * CURL_REDIR_POST_ALL - POST is kept as POST after 301, 302 and 303
+ * other - POST is kept as POST after 301 and 302
+ */
+ arg = va_arg(param, long);
+ if(arg < CURL_REDIR_GET_ALL)
+ /* no return error on too high numbers since the bitmask could be
+ extended in a future */
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.keep_post = arg & CURL_REDIR_POST_ALL;
+ break;
+
+ case CURLOPT_POST:
+ /* Does this option serve a purpose anymore? Yes it does, when
+ CURLOPT_POSTFIELDS isn't used and the POST data is read off the
+ callback! */
+ if(va_arg(param, long)) {
+ data->set.method = HTTPREQ_POST;
+ data->set.opt_no_body = FALSE; /* this is implied */
+ }
+ else
+ data->set.method = HTTPREQ_GET;
+ data->set.upload = FALSE;
+ break;
+
+#ifndef CURL_DISABLE_MIME
+ case CURLOPT_HTTPPOST:
+ /*
+ * Set to make us do HTTP POST
+ */
+ data->set.httppost = va_arg(param, struct curl_httppost *);
+ data->set.method = HTTPREQ_POST_FORM;
+ data->set.opt_no_body = FALSE; /* this is implied */
+ break;
+#endif
+
+ case CURLOPT_AWS_SIGV4:
+ /*
+ * String that is merged to some authentication
+ * parameters are used by the algorithm.
+ */
+ result = Curl_setstropt(&data->set.str[STRING_AWS_SIGV4],
+ va_arg(param, char *));
+ /*
+ * Basic been set by default it need to be unset here
+ */
+ if(data->set.str[STRING_AWS_SIGV4])
+ data->set.httpauth = CURLAUTH_AWS_SIGV4;
+ break;
+
+ case CURLOPT_REFERER:
+ /*
+ * String to set in the HTTP Referer: field.
+ */
+ if(data->state.referer_alloc) {
+ Curl_safefree(data->state.referer);
+ data->state.referer_alloc = FALSE;
+ }
+ result = Curl_setstropt(&data->set.str[STRING_SET_REFERER],
+ va_arg(param, char *));
+ data->state.referer = data->set.str[STRING_SET_REFERER];
+ break;
+
+ case CURLOPT_USERAGENT:
+ /*
+ * String to use in the HTTP User-Agent field
+ */
+ result = Curl_setstropt(&data->set.str[STRING_USERAGENT],
+ va_arg(param, char *));
+ break;
+
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXYHEADER:
+ /*
+ * Set a list with proxy headers to use (or replace internals with)
+ *
+ * Since CURLOPT_HTTPHEADER was the only way to set HTTP headers for a
+ * long time we remain doing it this way until CURLOPT_PROXYHEADER is
+ * used. As soon as this option has been used, if set to anything but
+ * NULL, custom headers for proxies are only picked from this list.
+ *
+ * Set this option to NULL to restore the previous behavior.
+ */
+ data->set.proxyheaders = va_arg(param, struct curl_slist *);
+ break;
+#endif
+ case CURLOPT_HEADEROPT:
+ /*
+ * Set header option.
+ */
+ arg = va_arg(param, long);
+ data->set.sep_headers = (bool)((arg & CURLHEADER_SEPARATE)? TRUE: FALSE);
+ break;
+
+ case CURLOPT_HTTP200ALIASES:
+ /*
+ * Set a list of aliases for HTTP 200 in response header
+ */
+ data->set.http200aliases = va_arg(param, struct curl_slist *);
+ break;
+
+#if !defined(CURL_DISABLE_COOKIES)
+ case CURLOPT_COOKIE:
+ /*
+ * Cookie string to send to the remote server in the request.
+ */
+ result = Curl_setstropt(&data->set.str[STRING_COOKIE],
+ va_arg(param, char *));
+ break;
+
+ case CURLOPT_COOKIEFILE:
+ /*
+ * Set cookie file to read and parse. Can be used multiple times.
+ */
+ argptr = (char *)va_arg(param, void *);
+ if(argptr) {
+ struct curl_slist *cl;
+ /* general protection against mistakes and abuse */
+ if(strlen(argptr) > CURL_MAX_INPUT_LENGTH)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ /* append the cookie file name to the list of file names, and deal with
+ them later */
+ cl = curl_slist_append(data->state.cookielist, argptr);
+ if(!cl) {
+ curl_slist_free_all(data->state.cookielist);
+ data->state.cookielist = NULL;
+ return CURLE_OUT_OF_MEMORY;
+ }
+ data->state.cookielist = cl; /* store the list for later use */
+ }
+ else {
+ /* clear the list of cookie files */
+ curl_slist_free_all(data->state.cookielist);
+ data->state.cookielist = NULL;
+
+ if(!data->share || !data->share->cookies) {
+ /* throw away all existing cookies if this isn't a shared cookie
+ container */
+ Curl_cookie_clearall(data->cookies);
+ Curl_cookie_cleanup(data->cookies);
+ }
+ /* disable the cookie engine */
+ data->cookies = NULL;
+ }
+ break;
+
+ case CURLOPT_COOKIEJAR:
+ /*
+ * Set cookie file name to dump all cookies to when we're done.
+ */
+ {
+ struct CookieInfo *newcookies;
+ result = Curl_setstropt(&data->set.str[STRING_COOKIEJAR],
+ va_arg(param, char *));
+
+ /*
+ * Activate the cookie parser. This may or may not already
+ * have been made.
+ */
+ newcookies = Curl_cookie_init(data, NULL, data->cookies,
+ data->set.cookiesession);
+ if(!newcookies)
+ result = CURLE_OUT_OF_MEMORY;
+ data->cookies = newcookies;
+ }
+ break;
+
+ case CURLOPT_COOKIESESSION:
+ /*
+ * Set this option to TRUE to start a new "cookie session". It will
+ * prevent the forthcoming read-cookies-from-file actions to accept
+ * cookies that are marked as being session cookies, as they belong to a
+ * previous session.
+ *
+ * In the original Netscape cookie spec, "session cookies" are cookies
+ * with no expire date set. RFC2109 describes the same action if no
+ * 'Max-Age' is set and RFC2965 includes the RFC2109 description and adds
+ * a 'Discard' action that can enforce the discard even for cookies that
+ * have a Max-Age.
+ *
+ * We run mostly with the original cookie spec, as hardly anyone implements
+ * anything else.
+ */
+ data->set.cookiesession = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+
+ case CURLOPT_COOKIELIST:
+ argptr = va_arg(param, char *);
+
+ if(!argptr)
+ break;
+
+ if(strcasecompare(argptr, "ALL")) {
+ /* clear all cookies */
+ Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
+ Curl_cookie_clearall(data->cookies);
+ Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
+ }
+ else if(strcasecompare(argptr, "SESS")) {
+ /* clear session cookies */
+ Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
+ Curl_cookie_clearsess(data->cookies);
+ Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
+ }
+ else if(strcasecompare(argptr, "FLUSH")) {
+ /* flush cookies to file, takes care of the locking */
+ Curl_flush_cookies(data, FALSE);
+ }
+ else if(strcasecompare(argptr, "RELOAD")) {
+ /* reload cookies from file */
+ Curl_cookie_loadfiles(data);
+ break;
+ }
+ else {
+ if(!data->cookies)
+ /* if cookie engine was not running, activate it */
+ data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE);
+
+ /* general protection against mistakes and abuse */
+ if(strlen(argptr) > CURL_MAX_INPUT_LENGTH)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ argptr = strdup(argptr);
+ if(!argptr || !data->cookies) {
+ result = CURLE_OUT_OF_MEMORY;
+ free(argptr);
+ }
+ else {
+ Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
+
+ if(checkprefix("Set-Cookie:", argptr))
+ /* HTTP Header format line */
+ Curl_cookie_add(data, data->cookies, TRUE, FALSE, argptr + 11, NULL,
+ NULL, TRUE);
+
+ else
+ /* Netscape format line */
+ Curl_cookie_add(data, data->cookies, FALSE, FALSE, argptr, NULL,
+ NULL, TRUE);
+
+ Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
+ free(argptr);
+ }
+ }
+
+ break;
+#endif /* !CURL_DISABLE_COOKIES */
+
+ case CURLOPT_HTTPGET:
+ /*
+ * Set to force us do HTTP GET
+ */
+ if(va_arg(param, long)) {
+ data->set.method = HTTPREQ_GET;
+ data->set.upload = FALSE; /* switch off upload */
+ data->set.opt_no_body = FALSE; /* this is implied */
+ }
+ break;
+
+ case CURLOPT_HTTP_VERSION:
+ /*
+ * This sets a requested HTTP version to be used. The value is one of
+ * the listed enums in curl/curl.h.
+ */
+ arg = va_arg(param, long);
+ if(arg < CURL_HTTP_VERSION_NONE)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+#ifdef ENABLE_QUIC
+ if(arg == CURL_HTTP_VERSION_3)
+ ;
+ else
+#endif
+#ifndef USE_HTTP2
+ if(arg >= CURL_HTTP_VERSION_2)
+ return CURLE_UNSUPPORTED_PROTOCOL;
+#else
+ if(arg >= CURL_HTTP_VERSION_LAST)
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ if(arg == CURL_HTTP_VERSION_NONE)
+ arg = CURL_HTTP_VERSION_2TLS;
+#endif
+ data->set.httpwant = (unsigned char)arg;
+ break;
+
+ case CURLOPT_EXPECT_100_TIMEOUT_MS:
+ /*
+ * Time to wait for a response to an HTTP request containing an
+ * Expect: 100-continue header before sending the data anyway.
+ */
+ arg = va_arg(param, long);
+ if(arg < 0)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.expect_100_timeout = arg;
+ break;
+
+ case CURLOPT_HTTP09_ALLOWED:
+ arg = va_arg(param, unsigned long);
+ if(arg > 1L)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+#ifdef USE_HYPER
+ /* Hyper does not support HTTP/0.9 */
+ if(arg)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+#else
+ data->set.http09_allowed = arg ? TRUE : FALSE;
+#endif
+ break;
+#endif /* CURL_DISABLE_HTTP */
+
+#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \
+ !defined(CURL_DISABLE_IMAP)
+# if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MIME)
+ case CURLOPT_HTTPHEADER:
+ /*
+ * Set a list with HTTP headers to use (or replace internals with)
+ */
+ data->set.headers = va_arg(param, struct curl_slist *);
+ break;
+# endif
+
+# ifndef CURL_DISABLE_MIME
+ case CURLOPT_MIMEPOST:
+ /*
+ * Set to make us do MIME POST
+ */
+ result = Curl_mime_set_subparts(&data->set.mimepost,
+ va_arg(param, curl_mime *), FALSE);
+ if(!result) {
+ data->set.method = HTTPREQ_POST_MIME;
+ data->set.opt_no_body = FALSE; /* this is implied */
+ }
+ break;
+
+ case CURLOPT_MIME_OPTIONS:
+ data->set.mime_options = (unsigned int)va_arg(param, long);
+ break;
+# endif
+#endif
+
+ case CURLOPT_HTTPAUTH:
+ /*
+ * Set HTTP Authentication type BITMASK.
+ */
+ {
+ int bitcheck;
+ bool authbits;
+ unsigned long auth = va_arg(param, unsigned long);
+
+ if(auth == CURLAUTH_NONE) {
+ data->set.httpauth = auth;
+ break;
+ }
+
+ /* the DIGEST_IE bit is only used to set a special marker, for all the
+ rest we need to handle it as normal DIGEST */
+ data->state.authhost.iestyle =
+ (bool)((auth & CURLAUTH_DIGEST_IE) ? TRUE : FALSE);
+
+ if(auth & CURLAUTH_DIGEST_IE) {
+ auth |= CURLAUTH_DIGEST; /* set standard digest bit */
+ auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */
+ }
+
+ /* switch off bits we can't support */
+#ifndef USE_NTLM
+ auth &= ~CURLAUTH_NTLM; /* no NTLM support */
+ auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */
+#elif !defined(NTLM_WB_ENABLED)
+ auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */
+#endif
+#ifndef USE_SPNEGO
+ auth &= ~CURLAUTH_NEGOTIATE; /* no Negotiate (SPNEGO) auth without
+ GSS-API or SSPI */
+#endif
+
+ /* check if any auth bit lower than CURLAUTH_ONLY is still set */
+ bitcheck = 0;
+ authbits = FALSE;
+ while(bitcheck < 31) {
+ if(auth & (1UL << bitcheck++)) {
+ authbits = TRUE;
+ break;
+ }
+ }
+ if(!authbits)
+ return CURLE_NOT_BUILT_IN; /* no supported types left! */
+
+ data->set.httpauth = auth;
+ }
+ break;
+
+ case CURLOPT_CUSTOMREQUEST:
+ /*
+ * Set a custom string to use as request
+ */
+ result = Curl_setstropt(&data->set.str[STRING_CUSTOMREQUEST],
+ va_arg(param, char *));
+
+ /* we don't set
+ data->set.method = HTTPREQ_CUSTOM;
+ here, we continue as if we were using the already set type
+ and this just changes the actual request keyword */
+ break;
+
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_HTTPPROXYTUNNEL:
+ /*
+ * Tunnel operations through the proxy instead of normal proxy use
+ */
+ data->set.tunnel_thru_httpproxy = (0 != va_arg(param, long)) ?
+ TRUE : FALSE;
+ break;
+
+ case CURLOPT_PROXYPORT:
+ /*
+ * Explicitly set HTTP proxy port number.
+ */
+ arg = va_arg(param, long);
+ if((arg < 0) || (arg > 65535))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.proxyport = (unsigned short)arg;
+ break;
+
+ case CURLOPT_PROXYAUTH:
+ /*
+ * Set HTTP Authentication type BITMASK.
+ */
+ {
+ int bitcheck;
+ bool authbits;
+ unsigned long auth = va_arg(param, unsigned long);
+
+ if(auth == CURLAUTH_NONE) {
+ data->set.proxyauth = auth;
+ break;
+ }
+
+ /* the DIGEST_IE bit is only used to set a special marker, for all the
+ rest we need to handle it as normal DIGEST */
+ data->state.authproxy.iestyle =
+ (bool)((auth & CURLAUTH_DIGEST_IE) ? TRUE : FALSE);
+
+ if(auth & CURLAUTH_DIGEST_IE) {
+ auth |= CURLAUTH_DIGEST; /* set standard digest bit */
+ auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */
+ }
+ /* switch off bits we can't support */
+#ifndef USE_NTLM
+ auth &= ~CURLAUTH_NTLM; /* no NTLM support */
+ auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */
+#elif !defined(NTLM_WB_ENABLED)
+ auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */
+#endif
+#ifndef USE_SPNEGO
+ auth &= ~CURLAUTH_NEGOTIATE; /* no Negotiate (SPNEGO) auth without
+ GSS-API or SSPI */
+#endif
+
+ /* check if any auth bit lower than CURLAUTH_ONLY is still set */
+ bitcheck = 0;
+ authbits = FALSE;
+ while(bitcheck < 31) {
+ if(auth & (1UL << bitcheck++)) {
+ authbits = TRUE;
+ break;
+ }
+ }
+ if(!authbits)
+ return CURLE_NOT_BUILT_IN; /* no supported types left! */
+
+ data->set.proxyauth = auth;
+ }
+ break;
+
+ case CURLOPT_PROXY:
+ /*
+ * Set proxy server:port to use as proxy.
+ *
+ * If the proxy is set to "" (and CURLOPT_SOCKS_PROXY is set to "" or NULL)
+ * we explicitly say that we don't want to use a proxy
+ * (even though there might be environment variables saying so).
+ *
+ * Setting it to NULL, means no proxy but allows the environment variables
+ * to decide for us (if CURLOPT_SOCKS_PROXY setting it to NULL).
+ */
+ result = Curl_setstropt(&data->set.str[STRING_PROXY],
+ va_arg(param, char *));
+ break;
+
+ case CURLOPT_PRE_PROXY:
+ /*
+ * Set proxy server:port to use as SOCKS proxy.
+ *
+ * If the proxy is set to "" or NULL we explicitly say that we don't want
+ * to use the socks proxy.
+ */
+ result = Curl_setstropt(&data->set.str[STRING_PRE_PROXY],
+ va_arg(param, char *));
+ break;
+
+ case CURLOPT_PROXYTYPE:
+ /*
+ * Set proxy type. HTTP/HTTP_1_0/SOCKS4/SOCKS4a/SOCKS5/SOCKS5_HOSTNAME
+ */
+ arg = va_arg(param, long);
+ if((arg < CURLPROXY_HTTP) || (arg > CURLPROXY_SOCKS5_HOSTNAME))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.proxytype = (unsigned char)(curl_proxytype)arg;
+ break;
+
+ case CURLOPT_PROXY_TRANSFER_MODE:
+ /*
+ * set transfer mode (;type=<a|i>) when doing FTP via an HTTP proxy
+ */
+ switch(va_arg(param, long)) {
+ case 0:
+ data->set.proxy_transfer_mode = FALSE;
+ break;
+ case 1:
+ data->set.proxy_transfer_mode = TRUE;
+ break;
+ default:
+ /* reserve other values for future use */
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ break;
+ }
+ break;
+
+ case CURLOPT_SOCKS5_AUTH:
+ data->set.socks5auth = (unsigned char)va_arg(param, unsigned long);
+ if(data->set.socks5auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
+ result = CURLE_NOT_BUILT_IN;
+ break;
+#endif /* CURL_DISABLE_PROXY */
+
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+ case CURLOPT_SOCKS5_GSSAPI_NEC:
+ /*
+ * Set flag for NEC SOCK5 support
+ */
+ data->set.socks5_gssapi_nec = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+#endif
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_SOCKS5_GSSAPI_SERVICE:
+ case CURLOPT_PROXY_SERVICE_NAME:
+ /*
+ * Set proxy authentication service name for Kerberos 5 and SPNEGO
+ */
+ result = Curl_setstropt(&data->set.str[STRING_PROXY_SERVICE_NAME],
+ va_arg(param, char *));
+ break;
+#endif
+ case CURLOPT_SERVICE_NAME:
+ /*
+ * Set authentication service name for DIGEST-MD5, Kerberos 5 and SPNEGO
+ */
+ result = Curl_setstropt(&data->set.str[STRING_SERVICE_NAME],
+ va_arg(param, char *));
+ break;
+
+ case CURLOPT_HEADERDATA:
+ /*
+ * Custom pointer to pass the header write callback function
+ */
+ data->set.writeheader = (void *)va_arg(param, void *);
+ break;
+ case CURLOPT_ERRORBUFFER:
+ /*
+ * Error buffer provided by the caller to get the human readable
+ * error string in.
+ */
+ data->set.errorbuffer = va_arg(param, char *);
+ break;
+ case CURLOPT_WRITEDATA:
+ /*
+ * FILE pointer to write to. Or possibly
+ * used as argument to the write callback.
+ */
+ data->set.out = va_arg(param, void *);
+ break;
+
+ case CURLOPT_DIRLISTONLY:
+ /*
+ * An option that changes the command to one that asks for a list only, no
+ * file info details. Used for FTP, POP3 and SFTP.
+ */
+ data->set.list_only = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+
+ case CURLOPT_APPEND:
+ /*
+ * We want to upload and append to an existing file. Used for FTP and
+ * SFTP.
+ */
+ data->set.remote_append = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+
+#ifndef CURL_DISABLE_FTP
+ case CURLOPT_FTP_FILEMETHOD:
+ /*
+ * How do access files over FTP.
+ */
+ arg = va_arg(param, long);
+ if((arg < CURLFTPMETHOD_DEFAULT) || (arg >= CURLFTPMETHOD_LAST))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.ftp_filemethod = (unsigned char)(curl_ftpfile)arg;
+ break;
+ case CURLOPT_FTPPORT:
+ /*
+ * Use FTP PORT, this also specifies which IP address to use
+ */
+ result = Curl_setstropt(&data->set.str[STRING_FTPPORT],
+ va_arg(param, char *));
+ data->set.ftp_use_port = (data->set.str[STRING_FTPPORT]) ? TRUE : FALSE;
+ break;
+
+ case CURLOPT_FTP_USE_EPRT:
+ data->set.ftp_use_eprt = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+
+ case CURLOPT_FTP_USE_EPSV:
+ data->set.ftp_use_epsv = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+
+ case CURLOPT_FTP_USE_PRET:
+ data->set.ftp_use_pret = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+
+ case CURLOPT_FTP_SSL_CCC:
+ arg = va_arg(param, long);
+ if((arg < CURLFTPSSL_CCC_NONE) || (arg >= CURLFTPSSL_CCC_LAST))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.ftp_ccc = (unsigned char)(curl_ftpccc)arg;
+ break;
+
+ case CURLOPT_FTP_SKIP_PASV_IP:
+ /*
+ * Enable or disable FTP_SKIP_PASV_IP, which will disable/enable the
+ * bypass of the IP address in PASV responses.
+ */
+ data->set.ftp_skip_ip = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+
+ case CURLOPT_FTP_ACCOUNT:
+ result = Curl_setstropt(&data->set.str[STRING_FTP_ACCOUNT],
+ va_arg(param, char *));
+ break;
+
+ case CURLOPT_FTP_ALTERNATIVE_TO_USER:
+ result = Curl_setstropt(&data->set.str[STRING_FTP_ALTERNATIVE_TO_USER],
+ va_arg(param, char *));
+ break;
+
+ case CURLOPT_FTPSSLAUTH:
+ /*
+ * Set a specific auth for FTP-SSL transfers.
+ */
+ arg = va_arg(param, long);
+ if((arg < CURLFTPAUTH_DEFAULT) || (arg >= CURLFTPAUTH_LAST))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.ftpsslauth = (unsigned char)(curl_ftpauth)arg;
+ break;
+ case CURLOPT_KRBLEVEL:
+ /*
+ * A string that defines the kerberos security level.
+ */
+ result = Curl_setstropt(&data->set.str[STRING_KRB_LEVEL],
+ va_arg(param, char *));
+ data->set.krb = (data->set.str[STRING_KRB_LEVEL]) ? TRUE : FALSE;
+ break;
+#endif
+ case CURLOPT_FTP_CREATE_MISSING_DIRS:
+ /*
+ * An FTP/SFTP option that modifies an upload to create missing
+ * directories on the server.
+ */
+ arg = va_arg(param, long);
+ /* reserve other values for future use */
+ if((arg < CURLFTP_CREATE_DIR_NONE) ||
+ (arg > CURLFTP_CREATE_DIR_RETRY))
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ else
+ data->set.ftp_create_missing_dirs = (unsigned char)arg;
+ break;
+ case CURLOPT_READDATA:
+ /*
+ * FILE pointer to read the file to be uploaded from. Or possibly
+ * used as argument to the read callback.
+ */
+ data->set.in_set = va_arg(param, void *);
+ break;
+ case CURLOPT_INFILESIZE:
+ /*
+ * If known, this should inform curl about the file size of the
+ * to-be-uploaded file.
+ */
+ arg = va_arg(param, long);
+ if(arg < -1)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.filesize = arg;
+ break;
+ case CURLOPT_INFILESIZE_LARGE:
+ /*
+ * If known, this should inform curl about the file size of the
+ * to-be-uploaded file.
+ */
+ bigsize = va_arg(param, curl_off_t);
+ if(bigsize < -1)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.filesize = bigsize;
+ break;
+ case CURLOPT_LOW_SPEED_LIMIT:
+ /*
+ * The low speed limit that if transfers are below this for
+ * CURLOPT_LOW_SPEED_TIME, the transfer is aborted.
+ */
+ arg = va_arg(param, long);
+ if(arg < 0)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.low_speed_limit = arg;
+ break;
+ case CURLOPT_MAX_SEND_SPEED_LARGE:
+ /*
+ * When transfer uploads are faster then CURLOPT_MAX_SEND_SPEED_LARGE
+ * bytes per second the transfer is throttled..
+ */
+ bigsize = va_arg(param, curl_off_t);
+ if(bigsize < 0)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.max_send_speed = bigsize;
+ break;
+ case CURLOPT_MAX_RECV_SPEED_LARGE:
+ /*
+ * When receiving data faster than CURLOPT_MAX_RECV_SPEED_LARGE bytes per
+ * second the transfer is throttled..
+ */
+ bigsize = va_arg(param, curl_off_t);
+ if(bigsize < 0)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.max_recv_speed = bigsize;
+ break;
+ case CURLOPT_LOW_SPEED_TIME:
+ /*
+ * The low speed time that if transfers are below the set
+ * CURLOPT_LOW_SPEED_LIMIT during this time, the transfer is aborted.
+ */
+ arg = va_arg(param, long);
+ if(arg < 0)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.low_speed_time = arg;
+ break;
+ case CURLOPT_CURLU:
+ /*
+ * pass CURLU to set URL
+ */
+ data->set.uh = va_arg(param, CURLU *);
+ break;
+ case CURLOPT_URL:
+ /*
+ * The URL to fetch.
+ */
+ if(data->state.url_alloc) {
+ /* the already set URL is allocated, free it first! */
+ Curl_safefree(data->state.url);
+ data->state.url_alloc = FALSE;
+ }
+ result = Curl_setstropt(&data->set.str[STRING_SET_URL],
+ va_arg(param, char *));
+ data->state.url = data->set.str[STRING_SET_URL];
+ break;
+ case CURLOPT_PORT:
+ /*
+ * The port number to use when getting the URL. 0 disables it.
+ */
+ arg = va_arg(param, long);
+ if((arg < 0) || (arg > 65535))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.use_port = (unsigned short)arg;
+ break;
+ case CURLOPT_TIMEOUT:
+ /*
+ * The maximum time you allow curl to use for a single transfer
+ * operation.
+ */
+ arg = va_arg(param, long);
+ if((arg >= 0) && (arg <= (INT_MAX/1000)))
+ data->set.timeout = (unsigned int)arg * 1000;
+ else
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ break;
+
+ case CURLOPT_TIMEOUT_MS:
+ uarg = va_arg(param, unsigned long);
+ if(uarg >= UINT_MAX)
+ uarg = UINT_MAX;
+ data->set.timeout = (unsigned int)uarg;
+ break;
+
+ case CURLOPT_CONNECTTIMEOUT:
+ /*
+ * The maximum time you allow curl to use to connect.
+ */
+ arg = va_arg(param, long);
+ if((arg >= 0) && (arg <= (INT_MAX/1000)))
+ data->set.connecttimeout = (unsigned int)arg * 1000;
+ else
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ break;
+
+ case CURLOPT_CONNECTTIMEOUT_MS:
+ uarg = va_arg(param, unsigned long);
+ if(uarg >= UINT_MAX)
+ uarg = UINT_MAX;
+ data->set.connecttimeout = (unsigned int)uarg;
+ break;
+
+#ifndef CURL_DISABLE_FTP
+ case CURLOPT_ACCEPTTIMEOUT_MS:
+ /*
+ * The maximum time for curl to wait for FTP server connect
+ */
+ uarg = va_arg(param, unsigned long);
+ if(uarg >= UINT_MAX)
+ uarg = UINT_MAX;
+ data->set.accepttimeout = (unsigned int)uarg;
+ break;
+#endif
+
+ case CURLOPT_USERPWD:
+ /*
+ * user:password to use in the operation
+ */
+ result = setstropt_userpwd(va_arg(param, char *),
+ &data->set.str[STRING_USERNAME],
+ &data->set.str[STRING_PASSWORD]);
+ break;
+
+ case CURLOPT_USERNAME:
+ /*
+ * authentication user name to use in the operation
+ */
+ result = Curl_setstropt(&data->set.str[STRING_USERNAME],
+ va_arg(param, char *));
+ break;
+ case CURLOPT_PASSWORD:
+ /*
+ * authentication password to use in the operation
+ */
+ result = Curl_setstropt(&data->set.str[STRING_PASSWORD],
+ va_arg(param, char *));
+ break;
+
+ case CURLOPT_LOGIN_OPTIONS:
+ /*
+ * authentication options to use in the operation
+ */
+ result = Curl_setstropt(&data->set.str[STRING_OPTIONS],
+ va_arg(param, char *));
+ break;
+
+ case CURLOPT_XOAUTH2_BEARER:
+ /*
+ * OAuth 2.0 bearer token to use in the operation
+ */
+ result = Curl_setstropt(&data->set.str[STRING_BEARER],
+ va_arg(param, char *));
+ break;
+
+ case CURLOPT_POSTQUOTE:
+ /*
+ * List of RAW FTP commands to use after a transfer
+ */
+ data->set.postquote = va_arg(param, struct curl_slist *);
+ break;
+ case CURLOPT_PREQUOTE:
+ /*
+ * List of RAW FTP commands to use prior to RETR (Wesley Laxton)
+ */
+ data->set.prequote = va_arg(param, struct curl_slist *);
+ break;
+ case CURLOPT_QUOTE:
+ /*
+ * List of RAW FTP commands to use before a transfer
+ */
+ data->set.quote = va_arg(param, struct curl_slist *);
+ break;
+ case CURLOPT_RESOLVE:
+ /*
+ * List of HOST:PORT:[addresses] strings to populate the DNS cache with
+ * Entries added this way will remain in the cache until explicitly
+ * removed or the handle is cleaned up.
+ *
+ * Prefix the HOST with plus sign (+) to have the entry expire just like
+ * automatically added entries.
+ *
+ * Prefix the HOST with dash (-) to _remove_ the entry from the cache.
+ *
+ * This API can remove any entry from the DNS cache, but only entries
+ * that aren't actually in use right now will be pruned immediately.
+ */
+ data->set.resolve = va_arg(param, struct curl_slist *);
+ data->state.resolve = data->set.resolve;
+ break;
+ case CURLOPT_PROGRESSFUNCTION:
+ /*
+ * Progress callback function
+ */
+ data->set.fprogress = va_arg(param, curl_progress_callback);
+ if(data->set.fprogress)
+ data->progress.callback = TRUE; /* no longer internal */
+ else
+ data->progress.callback = FALSE; /* NULL enforces internal */
+ break;
+
+ case CURLOPT_XFERINFOFUNCTION:
+ /*
+ * Transfer info callback function
+ */
+ data->set.fxferinfo = va_arg(param, curl_xferinfo_callback);
+ if(data->set.fxferinfo)
+ data->progress.callback = TRUE; /* no longer internal */
+ else
+ data->progress.callback = FALSE; /* NULL enforces internal */
+
+ break;
+
+ case CURLOPT_PROGRESSDATA:
+ /*
+ * Custom client data to pass to the progress callback
+ */
+ data->set.progress_client = va_arg(param, void *);
+ break;
+
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXYUSERPWD:
+ /*
+ * user:password needed to use the proxy
+ */
+ result = setstropt_userpwd(va_arg(param, char *),
+ &data->set.str[STRING_PROXYUSERNAME],
+ &data->set.str[STRING_PROXYPASSWORD]);
+ break;
+ case CURLOPT_PROXYUSERNAME:
+ /*
+ * authentication user name to use in the operation
+ */
+ result = Curl_setstropt(&data->set.str[STRING_PROXYUSERNAME],
+ va_arg(param, char *));
+ break;
+ case CURLOPT_PROXYPASSWORD:
+ /*
+ * authentication password to use in the operation
+ */
+ result = Curl_setstropt(&data->set.str[STRING_PROXYPASSWORD],
+ va_arg(param, char *));
+ break;
+ case CURLOPT_NOPROXY:
+ /*
+ * proxy exception list
+ */
+ result = Curl_setstropt(&data->set.str[STRING_NOPROXY],
+ va_arg(param, char *));
+ break;
+#endif
+
+ case CURLOPT_RANGE:
+ /*
+ * What range of the file you want to transfer
+ */
+ result = Curl_setstropt(&data->set.str[STRING_SET_RANGE],
+ va_arg(param, char *));
+ break;
+ case CURLOPT_RESUME_FROM:
+ /*
+ * Resume transfer at the given file position
+ */
+ arg = va_arg(param, long);
+ if(arg < -1)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.set_resume_from = arg;
+ break;
+ case CURLOPT_RESUME_FROM_LARGE:
+ /*
+ * Resume transfer at the given file position
+ */
+ bigsize = va_arg(param, curl_off_t);
+ if(bigsize < -1)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.set_resume_from = bigsize;
+ break;
+ case CURLOPT_DEBUGFUNCTION:
+ /*
+ * stderr write callback.
+ */
+ data->set.fdebug = va_arg(param, curl_debug_callback);
+ /*
+ * if the callback provided is NULL, it'll use the default callback
+ */
+ break;
+ case CURLOPT_DEBUGDATA:
+ /*
+ * Set to a void * that should receive all error writes. This
+ * defaults to CURLOPT_STDERR for normal operations.
+ */
+ data->set.debugdata = va_arg(param, void *);
+ break;
+ case CURLOPT_STDERR:
+ /*
+ * Set to a FILE * that should receive all error writes. This
+ * defaults to stderr for normal operations.
+ */
+ data->set.err = va_arg(param, FILE *);
+ if(!data->set.err)
+ data->set.err = stderr;
+ break;
+ case CURLOPT_HEADERFUNCTION:
+ /*
+ * Set header write callback
+ */
+ data->set.fwrite_header = va_arg(param, curl_write_callback);
+ break;
+ case CURLOPT_WRITEFUNCTION:
+ /*
+ * Set data write callback
+ */
+ data->set.fwrite_func = va_arg(param, curl_write_callback);
+ if(!data->set.fwrite_func)
+ /* When set to NULL, reset to our internal default function */
+ data->set.fwrite_func = (curl_write_callback)fwrite;
+ break;
+ case CURLOPT_READFUNCTION:
+ /*
+ * Read data callback
+ */
+ data->set.fread_func_set = va_arg(param, curl_read_callback);
+ if(!data->set.fread_func_set) {
+ data->set.is_fread_set = 0;
+ /* When set to NULL, reset to our internal default function */
+ data->set.fread_func_set = (curl_read_callback)fread;
+ }
+ else
+ data->set.is_fread_set = 1;
+ break;
+ case CURLOPT_SEEKFUNCTION:
+ /*
+ * Seek callback. Might be NULL.
+ */
+ data->set.seek_func = va_arg(param, curl_seek_callback);
+ break;
+ case CURLOPT_SEEKDATA:
+ /*
+ * Seek control callback. Might be NULL.
+ */
+ data->set.seek_client = va_arg(param, void *);
+ break;
+ case CURLOPT_IOCTLFUNCTION:
+ /*
+ * I/O control callback. Might be NULL.
+ */
+ data->set.ioctl_func = va_arg(param, curl_ioctl_callback);
+ break;
+ case CURLOPT_IOCTLDATA:
+ /*
+ * I/O control data pointer. Might be NULL.
+ */
+ data->set.ioctl_client = va_arg(param, void *);
+ break;
+ case CURLOPT_SSLCERT:
+ /*
+ * String that holds file name of the SSL certificate to use
+ */
+ result = Curl_setstropt(&data->set.str[STRING_CERT],
+ va_arg(param, char *));
+ break;
+ case CURLOPT_SSLCERT_BLOB:
+ /*
+ * Blob that holds file content of the SSL certificate to use
+ */
+ result = Curl_setblobopt(&data->set.blobs[BLOB_CERT],
+ va_arg(param, struct curl_blob *));
+ break;
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXY_SSLCERT:
+ /*
+ * String that holds file name of the SSL certificate to use for proxy
+ */
+ result = Curl_setstropt(&data->set.str[STRING_CERT_PROXY],
+ va_arg(param, char *));
+ break;
+ case CURLOPT_PROXY_SSLCERT_BLOB:
+ /*
+ * Blob that holds file content of the SSL certificate to use for proxy
+ */
+ result = Curl_setblobopt(&data->set.blobs[BLOB_CERT_PROXY],
+ va_arg(param, struct curl_blob *));
+ break;
+#endif
+ case CURLOPT_SSLCERTTYPE:
+ /*
+ * String that holds file type of the SSL certificate to use
+ */
+ result = Curl_setstropt(&data->set.str[STRING_CERT_TYPE],
+ va_arg(param, char *));
+ break;
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXY_SSLCERTTYPE:
+ /*
+ * String that holds file type of the SSL certificate to use for proxy
+ */
+ result = Curl_setstropt(&data->set.str[STRING_CERT_TYPE_PROXY],
+ va_arg(param, char *));
+ break;
+#endif
+ case CURLOPT_SSLKEY:
+ /*
+ * String that holds file name of the SSL key to use
+ */
+ result = Curl_setstropt(&data->set.str[STRING_KEY],
+ va_arg(param, char *));
+ break;
+ case CURLOPT_SSLKEY_BLOB:
+ /*
+ * Blob that holds file content of the SSL key to use
+ */
+ result = Curl_setblobopt(&data->set.blobs[BLOB_KEY],
+ va_arg(param, struct curl_blob *));
+ break;
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXY_SSLKEY:
+ /*
+ * String that holds file name of the SSL key to use for proxy
+ */
+ result = Curl_setstropt(&data->set.str[STRING_KEY_PROXY],
+ va_arg(param, char *));
+ break;
+ case CURLOPT_PROXY_SSLKEY_BLOB:
+ /*
+ * Blob that holds file content of the SSL key to use for proxy
+ */
+ result = Curl_setblobopt(&data->set.blobs[BLOB_KEY_PROXY],
+ va_arg(param, struct curl_blob *));
+ break;
+#endif
+ case CURLOPT_SSLKEYTYPE:
+ /*
+ * String that holds file type of the SSL key to use
+ */
+ result = Curl_setstropt(&data->set.str[STRING_KEY_TYPE],
+ va_arg(param, char *));
+ break;
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXY_SSLKEYTYPE:
+ /*
+ * String that holds file type of the SSL key to use for proxy
+ */
+ result = Curl_setstropt(&data->set.str[STRING_KEY_TYPE_PROXY],
+ va_arg(param, char *));
+ break;
+#endif
+ case CURLOPT_KEYPASSWD:
+ /*
+ * String that holds the SSL or SSH private key password.
+ */
+ result = Curl_setstropt(&data->set.str[STRING_KEY_PASSWD],
+ va_arg(param, char *));
+ break;
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXY_KEYPASSWD:
+ /*
+ * String that holds the SSL private key password for proxy.
+ */
+ result = Curl_setstropt(&data->set.str[STRING_KEY_PASSWD_PROXY],
+ va_arg(param, char *));
+ break;
+#endif
+ case CURLOPT_SSLENGINE:
+ /*
+ * String that holds the SSL crypto engine.
+ */
+ argptr = va_arg(param, char *);
+ if(argptr && argptr[0]) {
+ result = Curl_setstropt(&data->set.str[STRING_SSL_ENGINE], argptr);
+ if(!result) {
+ result = Curl_ssl_set_engine(data, argptr);
+ }
+ }
+ break;
+
+ case CURLOPT_SSLENGINE_DEFAULT:
+ /*
+ * flag to set engine as default.
+ */
+ Curl_setstropt(&data->set.str[STRING_SSL_ENGINE], NULL);
+ result = Curl_ssl_set_engine_default(data);
+ break;
+ case CURLOPT_CRLF:
+ /*
+ * Kludgy option to enable CRLF conversions. Subject for removal.
+ */
+ data->set.crlf = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_HAPROXYPROTOCOL:
+ /*
+ * Set to send the HAProxy Proxy Protocol header
+ */
+ data->set.haproxyprotocol = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+#endif
+ case CURLOPT_INTERFACE:
+ /*
+ * Set what interface or address/hostname to bind the socket to when
+ * performing an operation and thus what from-IP your connection will use.
+ */
+ result = Curl_setstropt(&data->set.str[STRING_DEVICE],
+ va_arg(param, char *));
+ break;
+ case CURLOPT_LOCALPORT:
+ /*
+ * Set what local port to bind the socket to when performing an operation.
+ */
+ arg = va_arg(param, long);
+ if((arg < 0) || (arg > 65535))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.localport = curlx_sltous(arg);
+ break;
+ case CURLOPT_LOCALPORTRANGE:
+ /*
+ * Set number of local ports to try, starting with CURLOPT_LOCALPORT.
+ */
+ arg = va_arg(param, long);
+ if((arg < 0) || (arg > 65535))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.localportrange = curlx_sltosi(arg);
+ break;
+ case CURLOPT_GSSAPI_DELEGATION:
+ /*
+ * GSS-API credential delegation bitmask
+ */
+ arg = va_arg(param, long);
+ if(arg < CURLGSSAPI_DELEGATION_NONE)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.gssapi_delegation = arg;
+ break;
+ case CURLOPT_SSL_VERIFYPEER:
+ /*
+ * Enable peer SSL verifying.
+ */
+ data->set.ssl.primary.verifypeer = (0 != va_arg(param, long)) ?
+ TRUE : FALSE;
+
+ /* Update the current connection ssl_config. */
+ if(data->conn) {
+ data->conn->ssl_config.verifypeer =
+ data->set.ssl.primary.verifypeer;
+ }
+ break;
+#ifndef CURL_DISABLE_DOH
+ case CURLOPT_DOH_SSL_VERIFYPEER:
+ /*
+ * Enable peer SSL verifying for DoH.
+ */
+ data->set.doh_verifypeer = (0 != va_arg(param, long)) ?
+ TRUE : FALSE;
+ break;
+#endif
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXY_SSL_VERIFYPEER:
+ /*
+ * Enable peer SSL verifying for proxy.
+ */
+ data->set.proxy_ssl.primary.verifypeer =
+ (0 != va_arg(param, long))?TRUE:FALSE;
+
+ /* Update the current connection proxy_ssl_config. */
+ if(data->conn) {
+ data->conn->proxy_ssl_config.verifypeer =
+ data->set.proxy_ssl.primary.verifypeer;
+ }
+ break;
+#endif
+ case CURLOPT_SSL_VERIFYHOST:
+ /*
+ * Enable verification of the host name in the peer certificate
+ */
+ arg = va_arg(param, long);
+
+ /* Obviously people are not reading documentation and too many thought
+ this argument took a boolean when it wasn't and misused it.
+ Treat 1 and 2 the same */
+ data->set.ssl.primary.verifyhost = (bool)((arg & 3) ? TRUE : FALSE);
+
+ /* Update the current connection ssl_config. */
+ if(data->conn) {
+ data->conn->ssl_config.verifyhost =
+ data->set.ssl.primary.verifyhost;
+ }
+ break;
+#ifndef CURL_DISABLE_DOH
+ case CURLOPT_DOH_SSL_VERIFYHOST:
+ /*
+ * Enable verification of the host name in the peer certificate for DoH
+ */
+ arg = va_arg(param, long);
+
+ /* Treat both 1 and 2 as TRUE */
+ data->set.doh_verifyhost = (bool)((arg & 3) ? TRUE : FALSE);
+ break;
+#endif
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXY_SSL_VERIFYHOST:
+ /*
+ * Enable verification of the host name in the peer certificate for proxy
+ */
+ arg = va_arg(param, long);
+
+ /* Treat both 1 and 2 as TRUE */
+ data->set.proxy_ssl.primary.verifyhost = (bool)((arg & 3)?TRUE:FALSE);
+
+ /* Update the current connection proxy_ssl_config. */
+ if(data->conn) {
+ data->conn->proxy_ssl_config.verifyhost =
+ data->set.proxy_ssl.primary.verifyhost;
+ }
+ break;
+#endif
+ case CURLOPT_SSL_VERIFYSTATUS:
+ /*
+ * Enable certificate status verifying.
+ */
+ if(!Curl_ssl_cert_status_request()) {
+ result = CURLE_NOT_BUILT_IN;
+ break;
+ }
+
+ data->set.ssl.primary.verifystatus = (0 != va_arg(param, long)) ?
+ TRUE : FALSE;
+
+ /* Update the current connection ssl_config. */
+ if(data->conn) {
+ data->conn->ssl_config.verifystatus =
+ data->set.ssl.primary.verifystatus;
+ }
+ break;
+#ifndef CURL_DISABLE_DOH
+ case CURLOPT_DOH_SSL_VERIFYSTATUS:
+ /*
+ * Enable certificate status verifying for DoH.
+ */
+ if(!Curl_ssl_cert_status_request()) {
+ result = CURLE_NOT_BUILT_IN;
+ break;
+ }
+
+ data->set.doh_verifystatus = (0 != va_arg(param, long)) ?
+ TRUE : FALSE;
+ break;
+#endif
+ case CURLOPT_SSL_CTX_FUNCTION:
+ /*
+ * Set a SSL_CTX callback
+ */
+#ifdef USE_SSL
+ if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX))
+ data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback);
+ else
+#endif
+ result = CURLE_NOT_BUILT_IN;
+ break;
+ case CURLOPT_SSL_CTX_DATA:
+ /*
+ * Set a SSL_CTX callback parameter pointer
+ */
+#ifdef USE_SSL
+ if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX))
+ data->set.ssl.fsslctxp = va_arg(param, void *);
+ else
+#endif
+ result = CURLE_NOT_BUILT_IN;
+ break;
+ case CURLOPT_SSL_FALSESTART:
+ /*
+ * Enable TLS false start.
+ */
+ if(!Curl_ssl_false_start(data)) {
+ result = CURLE_NOT_BUILT_IN;
+ break;
+ }
+
+ data->set.ssl.falsestart = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+ case CURLOPT_CERTINFO:
+#ifdef USE_SSL
+ if(Curl_ssl_supports(data, SSLSUPP_CERTINFO))
+ data->set.ssl.certinfo = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ else
+#endif
+ result = CURLE_NOT_BUILT_IN;
+ break;
+ case CURLOPT_PINNEDPUBLICKEY:
+ /*
+ * Set pinned public key for SSL connection.
+ * Specify file name of the public key in DER format.
+ */
+#ifdef USE_SSL
+ if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY))
+ result = Curl_setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY],
+ va_arg(param, char *));
+ else
+#endif
+ result = CURLE_NOT_BUILT_IN;
+ break;
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXY_PINNEDPUBLICKEY:
+ /*
+ * Set pinned public key for SSL connection.
+ * Specify file name of the public key in DER format.
+ */
+#ifdef USE_SSL
+ if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY))
+ result = Curl_setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY],
+ va_arg(param, char *));
+ else
+#endif
+ result = CURLE_NOT_BUILT_IN;
+ break;
+#endif
+ case CURLOPT_CAINFO:
+ /*
+ * Set CA info for SSL connection. Specify file name of the CA certificate
+ */
+ result = Curl_setstropt(&data->set.str[STRING_SSL_CAFILE],
+ va_arg(param, char *));
+ break;
+ case CURLOPT_CAINFO_BLOB:
+ /*
+ * Blob that holds CA info for SSL connection.
+ * Specify entire PEM of the CA certificate
+ */
+#ifdef USE_SSL
+ if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB))
+ result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO],
+ va_arg(param, struct curl_blob *));
+ else
+#endif
+ return CURLE_NOT_BUILT_IN;
+
+ break;
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXY_CAINFO:
+ /*
+ * Set CA info SSL connection for proxy. Specify file name of the
+ * CA certificate
+ */
+ result = Curl_setstropt(&data->set.str[STRING_SSL_CAFILE_PROXY],
+ va_arg(param, char *));
+ break;
+ case CURLOPT_PROXY_CAINFO_BLOB:
+ /*
+ * Blob that holds CA info for SSL connection proxy.
+ * Specify entire PEM of the CA certificate
+ */
+#ifdef USE_SSL
+ if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB))
+ result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO_PROXY],
+ va_arg(param, struct curl_blob *));
+ else
+#endif
+ return CURLE_NOT_BUILT_IN;
+ break;
+#endif
+ case CURLOPT_CAPATH:
+ /*
+ * Set CA path info for SSL connection. Specify directory name of the CA
+ * certificates which have been prepared using openssl c_rehash utility.
+ */
+#ifdef USE_SSL
+ if(Curl_ssl_supports(data, SSLSUPP_CA_PATH))
+ /* This does not work on windows. */
+ result = Curl_setstropt(&data->set.str[STRING_SSL_CAPATH],
+ va_arg(param, char *));
+ else
+#endif
+ result = CURLE_NOT_BUILT_IN;
+ break;
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXY_CAPATH:
+ /*
+ * Set CA path info for SSL connection proxy. Specify directory name of the
+ * CA certificates which have been prepared using openssl c_rehash utility.
+ */
+#ifdef USE_SSL
+ if(Curl_ssl_supports(data, SSLSUPP_CA_PATH))
+ /* This does not work on windows. */
+ result = Curl_setstropt(&data->set.str[STRING_SSL_CAPATH_PROXY],
+ va_arg(param, char *));
+ else
+#endif
+ result = CURLE_NOT_BUILT_IN;
+ break;
+#endif
+ case CURLOPT_CRLFILE:
+ /*
+ * Set CRL file info for SSL connection. Specify file name of the CRL
+ * to check certificates revocation
+ */
+ result = Curl_setstropt(&data->set.str[STRING_SSL_CRLFILE],
+ va_arg(param, char *));
+ break;
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXY_CRLFILE:
+ /*
+ * Set CRL file info for SSL connection for proxy. Specify file name of the
+ * CRL to check certificates revocation
+ */
+ result = Curl_setstropt(&data->set.str[STRING_SSL_CRLFILE_PROXY],
+ va_arg(param, char *));
+ break;
+#endif
+ case CURLOPT_ISSUERCERT:
+ /*
+ * Set Issuer certificate file
+ * to check certificates issuer
+ */
+ result = Curl_setstropt(&data->set.str[STRING_SSL_ISSUERCERT],
+ va_arg(param, char *));
+ break;
+ case CURLOPT_ISSUERCERT_BLOB:
+ /*
+ * Blob that holds Issuer certificate to check certificates issuer
+ */
+ result = Curl_setblobopt(&data->set.blobs[BLOB_SSL_ISSUERCERT],
+ va_arg(param, struct curl_blob *));
+ break;
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXY_ISSUERCERT:
+ /*
+ * Set Issuer certificate file
+ * to check certificates issuer
+ */
+ result = Curl_setstropt(&data->set.str[STRING_SSL_ISSUERCERT_PROXY],
+ va_arg(param, char *));
+ break;
+ case CURLOPT_PROXY_ISSUERCERT_BLOB:
+ /*
+ * Blob that holds Issuer certificate to check certificates issuer
+ */
+ result = Curl_setblobopt(&data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY],
+ va_arg(param, struct curl_blob *));
+ break;
+#endif
+#ifndef CURL_DISABLE_TELNET
+ case CURLOPT_TELNETOPTIONS:
+ /*
+ * Set a linked list of telnet options
+ */
+ data->set.telnet_options = va_arg(param, struct curl_slist *);
+ break;
+#endif
+ case CURLOPT_BUFFERSIZE:
+ /*
+ * The application kindly asks for a differently sized receive buffer.
+ * If it seems reasonable, we'll use it.
+ */
+ if(data->state.buffer)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ arg = va_arg(param, long);
+
+ if(arg > READBUFFER_MAX)
+ arg = READBUFFER_MAX;
+ else if(arg < 1)
+ arg = READBUFFER_SIZE;
+ else if(arg < READBUFFER_MIN)
+ arg = READBUFFER_MIN;
+
+ data->set.buffer_size = (unsigned int)arg;
+ break;
+
+ case CURLOPT_UPLOAD_BUFFERSIZE:
+ /*
+ * The application kindly asks for a differently sized upload buffer.
+ * Cap it to sensible.
+ */
+ arg = va_arg(param, long);
+
+ if(arg > UPLOADBUFFER_MAX)
+ arg = UPLOADBUFFER_MAX;
+ else if(arg < UPLOADBUFFER_MIN)
+ arg = UPLOADBUFFER_MIN;
+
+ data->set.upload_buffer_size = (unsigned int)arg;
+ Curl_safefree(data->state.ulbuf); /* force a realloc next opportunity */
+ break;
+
+ case CURLOPT_NOSIGNAL:
+ /*
+ * The application asks not to set any signal() or alarm() handlers,
+ * even when using a timeout.
+ */
+ data->set.no_signal = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+
+ case CURLOPT_SHARE:
+ {
+ struct Curl_share *set;
+ set = va_arg(param, struct Curl_share *);
+
+ /* disconnect from old share, if any */
+ if(data->share) {
+ Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
+
+ if(data->dns.hostcachetype == HCACHE_SHARED) {
+ data->dns.hostcache = NULL;
+ data->dns.hostcachetype = HCACHE_NONE;
+ }
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+ if(data->share->cookies == data->cookies)
+ data->cookies = NULL;
+#endif
+
+ if(data->share->sslsession == data->state.session)
+ data->state.session = NULL;
+
+#ifdef USE_LIBPSL
+ if(data->psl == &data->share->psl)
+ data->psl = data->multi? &data->multi->psl: NULL;
+#endif
+
+ data->share->dirty--;
+
+ Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
+ data->share = NULL;
+ }
+
+ if(GOOD_SHARE_HANDLE(set))
+ /* use new share if it set */
+ data->share = set;
+ if(data->share) {
+
+ Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
+
+ data->share->dirty++;
+
+ if(data->share->specifier & (1<< CURL_LOCK_DATA_DNS)) {
+ /* use shared host cache */
+ data->dns.hostcache = &data->share->hostcache;
+ data->dns.hostcachetype = HCACHE_SHARED;
+ }
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+ if(data->share->cookies) {
+ /* use shared cookie list, first free own one if any */
+ Curl_cookie_cleanup(data->cookies);
+ /* enable cookies since we now use a share that uses cookies! */
+ data->cookies = data->share->cookies;
+ }
+#endif /* CURL_DISABLE_HTTP */
+ if(data->share->sslsession) {
+ data->set.general_ssl.max_ssl_sessions = data->share->max_ssl_sessions;
+ data->state.session = data->share->sslsession;
+ }
+#ifdef USE_LIBPSL
+ if(data->share->specifier & (1 << CURL_LOCK_DATA_PSL))
+ data->psl = &data->share->psl;
+#endif
+
+ Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
+ }
+ /* check for host cache not needed,
+ * it will be done by curl_easy_perform */
+ }
+ break;
+
+ case CURLOPT_PRIVATE:
+ /*
+ * Set private data pointer.
+ */
+ data->set.private_data = va_arg(param, void *);
+ break;
+
+ case CURLOPT_MAXFILESIZE:
+ /*
+ * Set the maximum size of a file to download.
+ */
+ arg = va_arg(param, long);
+ if(arg < 0)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.max_filesize = arg;
+ break;
+
+#ifdef USE_SSL
+ case CURLOPT_USE_SSL:
+ /*
+ * Make transfers attempt to use SSL/TLS.
+ */
+ arg = va_arg(param, long);
+ if((arg < CURLUSESSL_NONE) || (arg >= CURLUSESSL_LAST))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.use_ssl = (curl_usessl)arg;
+ break;
+
+ case CURLOPT_SSL_OPTIONS:
+ arg = va_arg(param, long);
+ data->set.ssl.primary.ssl_options = (unsigned char)(arg & 0xff);
+ data->set.ssl.enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST);
+ data->set.ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE);
+ data->set.ssl.no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN);
+ data->set.ssl.revoke_best_effort = !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT);
+ data->set.ssl.native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA);
+ data->set.ssl.auto_client_cert = !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT);
+ /* If a setting is added here it should also be added in dohprobe()
+ which sets its own CURLOPT_SSL_OPTIONS based on these settings. */
+ break;
+
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXY_SSL_OPTIONS:
+ arg = va_arg(param, long);
+ data->set.proxy_ssl.primary.ssl_options = (unsigned char)(arg & 0xff);
+ data->set.proxy_ssl.enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST);
+ data->set.proxy_ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE);
+ data->set.proxy_ssl.no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN);
+ data->set.proxy_ssl.revoke_best_effort =
+ !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT);
+ data->set.proxy_ssl.native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA);
+ data->set.proxy_ssl.auto_client_cert =
+ !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT);
+ break;
+#endif
+
+ case CURLOPT_SSL_EC_CURVES:
+ /*
+ * Set accepted curves in SSL connection setup.
+ * Specify colon-delimited list of curve algorithm names.
+ */
+ result = Curl_setstropt(&data->set.str[STRING_SSL_EC_CURVES],
+ va_arg(param, char *));
+ break;
+#endif
+ case CURLOPT_IPRESOLVE:
+ arg = va_arg(param, long);
+ if((arg < CURL_IPRESOLVE_WHATEVER) || (arg > CURL_IPRESOLVE_V6))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.ipver = (unsigned char) arg;
+ break;
+
+ case CURLOPT_MAXFILESIZE_LARGE:
+ /*
+ * Set the maximum size of a file to download.
+ */
+ bigsize = va_arg(param, curl_off_t);
+ if(bigsize < 0)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.max_filesize = bigsize;
+ break;
+
+ case CURLOPT_TCP_NODELAY:
+ /*
+ * Enable or disable TCP_NODELAY, which will disable/enable the Nagle
+ * algorithm
+ */
+ data->set.tcp_nodelay = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+
+ case CURLOPT_IGNORE_CONTENT_LENGTH:
+ data->set.ignorecl = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+
+ case CURLOPT_CONNECT_ONLY:
+ /*
+ * No data transfer.
+ * (1) - only do connection
+ * (2) - do first get request but get no content
+ */
+ arg = va_arg(param, long);
+ if(arg > 2)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.connect_only = (unsigned char)arg;
+ break;
+
+ case CURLOPT_SOCKOPTFUNCTION:
+ /*
+ * socket callback function: called after socket() but before connect()
+ */
+ data->set.fsockopt = va_arg(param, curl_sockopt_callback);
+ break;
+
+ case CURLOPT_SOCKOPTDATA:
+ /*
+ * socket callback data pointer. Might be NULL.
+ */
+ data->set.sockopt_client = va_arg(param, void *);
+ break;
+
+ case CURLOPT_OPENSOCKETFUNCTION:
+ /*
+ * open/create socket callback function: called instead of socket(),
+ * before connect()
+ */
+ data->set.fopensocket = va_arg(param, curl_opensocket_callback);
+ break;
+
+ case CURLOPT_OPENSOCKETDATA:
+ /*
+ * socket callback data pointer. Might be NULL.
+ */
+ data->set.opensocket_client = va_arg(param, void *);
+ break;
+
+ case CURLOPT_CLOSESOCKETFUNCTION:
+ /*
+ * close socket callback function: called instead of close()
+ * when shutting down a connection
+ */
+ data->set.fclosesocket = va_arg(param, curl_closesocket_callback);
+ break;
+
+ case CURLOPT_RESOLVER_START_FUNCTION:
+ /*
+ * resolver start callback function: called before a new resolver request
+ * is started
+ */
+ data->set.resolver_start = va_arg(param, curl_resolver_start_callback);
+ break;
+
+ case CURLOPT_RESOLVER_START_DATA:
+ /*
+ * resolver start callback data pointer. Might be NULL.
+ */
+ data->set.resolver_start_client = va_arg(param, void *);
+ break;
+
+ case CURLOPT_CLOSESOCKETDATA:
+ /*
+ * socket callback data pointer. Might be NULL.
+ */
+ data->set.closesocket_client = va_arg(param, void *);
+ break;
+
+ case CURLOPT_SSL_SESSIONID_CACHE:
+ data->set.ssl.primary.sessionid = (0 != va_arg(param, long)) ?
+ TRUE : FALSE;
+#ifndef CURL_DISABLE_PROXY
+ data->set.proxy_ssl.primary.sessionid = data->set.ssl.primary.sessionid;
+#endif
+ break;
+
+#ifdef USE_SSH
+ /* we only include SSH options if explicitly built to support SSH */
+ case CURLOPT_SSH_AUTH_TYPES:
+ data->set.ssh_auth_types = (unsigned int)va_arg(param, long);
+ break;
+
+ case CURLOPT_SSH_PUBLIC_KEYFILE:
+ /*
+ * Use this file instead of the $HOME/.ssh/id_dsa.pub file
+ */
+ result = Curl_setstropt(&data->set.str[STRING_SSH_PUBLIC_KEY],
+ va_arg(param, char *));
+ break;
+
+ case CURLOPT_SSH_PRIVATE_KEYFILE:
+ /*
+ * Use this file instead of the $HOME/.ssh/id_dsa file
+ */
+ result = Curl_setstropt(&data->set.str[STRING_SSH_PRIVATE_KEY],
+ va_arg(param, char *));
+ break;
+ case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5:
+ /*
+ * Option to allow for the MD5 of the host public key to be checked
+ * for validation purposes.
+ */
+ result = Curl_setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5],
+ va_arg(param, char *));
+ break;
+
+ case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256:
+ /*
+ * Option to allow for the SHA256 of the host public key to be checked
+ * for validation purposes.
+ */
+ result = Curl_setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256],
+ va_arg(param, char *));
+ break;
+
+ case CURLOPT_SSH_KNOWNHOSTS:
+ /*
+ * Store the file name to read known hosts from.
+ */
+ result = Curl_setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS],
+ va_arg(param, char *));
+ break;
+#ifdef USE_LIBSSH2
+ case CURLOPT_SSH_HOSTKEYFUNCTION:
+ /* the callback to check the hostkey without the knownhost file */
+ data->set.ssh_hostkeyfunc = va_arg(param, curl_sshhostkeycallback);
+ break;
+
+ case CURLOPT_SSH_HOSTKEYDATA:
+ /*
+ * Custom client data to pass to the SSH keyfunc callback
+ */
+ data->set.ssh_hostkeyfunc_userp = va_arg(param, void *);
+ break;
+#endif
+ case CURLOPT_SSH_KEYFUNCTION:
+ /* setting to NULL is fine since the ssh.c functions themselves will
+ then revert to use the internal default */
+ data->set.ssh_keyfunc = va_arg(param, curl_sshkeycallback);
+ break;
+
+ case CURLOPT_SSH_KEYDATA:
+ /*
+ * Custom client data to pass to the SSH keyfunc callback
+ */
+ data->set.ssh_keyfunc_userp = va_arg(param, void *);
+ break;
+
+ case CURLOPT_SSH_COMPRESSION:
+ data->set.ssh_compression = (0 != va_arg(param, long))?TRUE:FALSE;
+ break;
+#endif /* USE_SSH */
+
+ case CURLOPT_HTTP_TRANSFER_DECODING:
+ /*
+ * disable libcurl transfer encoding is used
+ */
+#ifndef USE_HYPER
+ data->set.http_te_skip = (0 == va_arg(param, long)) ? TRUE : FALSE;
+ break;
+#else
+ return CURLE_NOT_BUILT_IN; /* hyper doesn't support */
+#endif
+
+ case CURLOPT_HTTP_CONTENT_DECODING:
+ /*
+ * raw data passed to the application when content encoding is used
+ */
+ data->set.http_ce_skip = (0 == va_arg(param, long)) ? TRUE : FALSE;
+ break;
+
+#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH)
+ case CURLOPT_NEW_FILE_PERMS:
+ /*
+ * Uses these permissions instead of 0644
+ */
+ arg = va_arg(param, long);
+ if((arg < 0) || (arg > 0777))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.new_file_perms = (unsigned int)arg;
+ break;
+
+ case CURLOPT_NEW_DIRECTORY_PERMS:
+ /*
+ * Uses these permissions instead of 0755
+ */
+ arg = va_arg(param, long);
+ if((arg < 0) || (arg > 0777))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.new_directory_perms = (unsigned int)arg;
+ break;
+#endif
+
+#ifdef ENABLE_IPV6
+ case CURLOPT_ADDRESS_SCOPE:
+ /*
+ * Use this scope id when using IPv6
+ * We always get longs when passed plain numericals so we should check
+ * that the value fits into an unsigned 32 bit integer.
+ */
+ uarg = va_arg(param, unsigned long);
+#if SIZEOF_LONG > 4
+ if(uarg > UINT_MAX)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+#endif
+ data->set.scope_id = (unsigned int)uarg;
+ break;
+#endif
+
+ case CURLOPT_PROTOCOLS:
+ /* set the bitmask for the protocols that are allowed to be used for the
+ transfer, which thus helps the app which takes URLs from users or other
+ external inputs and want to restrict what protocol(s) to deal
+ with. Defaults to CURLPROTO_ALL. */
+ data->set.allowed_protocols = (curl_prot_t)va_arg(param, long);
+ break;
+
+ case CURLOPT_REDIR_PROTOCOLS:
+ /* set the bitmask for the protocols that libcurl is allowed to follow to,
+ as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs
+ to be set in both bitmasks to be allowed to get redirected to. */
+ data->set.redir_protocols = (curl_prot_t)va_arg(param, long);
+ break;
+
+ case CURLOPT_PROTOCOLS_STR: {
+ curl_prot_t prot;
+ argptr = va_arg(param, char *);
+ result = protocol2num(argptr, &prot);
+ if(result)
+ return result;
+ data->set.allowed_protocols = prot;
+ break;
+ }
+
+ case CURLOPT_REDIR_PROTOCOLS_STR: {
+ curl_prot_t prot;
+ argptr = va_arg(param, char *);
+ result = protocol2num(argptr, &prot);
+ if(result)
+ return result;
+ data->set.redir_protocols = prot;
+ break;
+ }
+
+ case CURLOPT_DEFAULT_PROTOCOL:
+ /* Set the protocol to use when the URL doesn't include any protocol */
+ result = Curl_setstropt(&data->set.str[STRING_DEFAULT_PROTOCOL],
+ va_arg(param, char *));
+ break;
+#ifndef CURL_DISABLE_SMTP
+ case CURLOPT_MAIL_FROM:
+ /* Set the SMTP mail originator */
+ result = Curl_setstropt(&data->set.str[STRING_MAIL_FROM],
+ va_arg(param, char *));
+ break;
+
+ case CURLOPT_MAIL_AUTH:
+ /* Set the SMTP auth originator */
+ result = Curl_setstropt(&data->set.str[STRING_MAIL_AUTH],
+ va_arg(param, char *));
+ break;
+
+ case CURLOPT_MAIL_RCPT:
+ /* Set the list of mail recipients */
+ data->set.mail_rcpt = va_arg(param, struct curl_slist *);
+ break;
+ case CURLOPT_MAIL_RCPT_ALLLOWFAILS:
+ /* allow RCPT TO command to fail for some recipients */
+ data->set.mail_rcpt_allowfails = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+#endif
+
+ case CURLOPT_SASL_AUTHZID:
+ /* Authorization identity (identity to act as) */
+ result = Curl_setstropt(&data->set.str[STRING_SASL_AUTHZID],
+ va_arg(param, char *));
+ break;
+
+ case CURLOPT_SASL_IR:
+ /* Enable/disable SASL initial response */
+ data->set.sasl_ir = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+#ifndef CURL_DISABLE_RTSP
+ case CURLOPT_RTSP_REQUEST:
+ {
+ /*
+ * Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...)
+ * Would this be better if the RTSPREQ_* were just moved into here?
+ */
+ long in_rtspreq = va_arg(param, long);
+ Curl_RtspReq rtspreq = RTSPREQ_NONE;
+ switch(in_rtspreq) {
+ case CURL_RTSPREQ_OPTIONS:
+ rtspreq = RTSPREQ_OPTIONS;
+ break;
+
+ case CURL_RTSPREQ_DESCRIBE:
+ rtspreq = RTSPREQ_DESCRIBE;
+ break;
+
+ case CURL_RTSPREQ_ANNOUNCE:
+ rtspreq = RTSPREQ_ANNOUNCE;
+ break;
+
+ case CURL_RTSPREQ_SETUP:
+ rtspreq = RTSPREQ_SETUP;
+ break;
+
+ case CURL_RTSPREQ_PLAY:
+ rtspreq = RTSPREQ_PLAY;
+ break;
+
+ case CURL_RTSPREQ_PAUSE:
+ rtspreq = RTSPREQ_PAUSE;
+ break;
+
+ case CURL_RTSPREQ_TEARDOWN:
+ rtspreq = RTSPREQ_TEARDOWN;
+ break;
+
+ case CURL_RTSPREQ_GET_PARAMETER:
+ rtspreq = RTSPREQ_GET_PARAMETER;
+ break;
+
+ case CURL_RTSPREQ_SET_PARAMETER:
+ rtspreq = RTSPREQ_SET_PARAMETER;
+ break;
+
+ case CURL_RTSPREQ_RECORD:
+ rtspreq = RTSPREQ_RECORD;
+ break;
+
+ case CURL_RTSPREQ_RECEIVE:
+ rtspreq = RTSPREQ_RECEIVE;
+ break;
+ default:
+ rtspreq = RTSPREQ_NONE;
+ }
+
+ data->set.rtspreq = rtspreq;
+ break;
+ }
+
+
+ case CURLOPT_RTSP_SESSION_ID:
+ /*
+ * Set the RTSP Session ID manually. Useful if the application is
+ * resuming a previously established RTSP session
+ */
+ result = Curl_setstropt(&data->set.str[STRING_RTSP_SESSION_ID],
+ va_arg(param, char *));
+ break;
+
+ case CURLOPT_RTSP_STREAM_URI:
+ /*
+ * Set the Stream URI for the RTSP request. Unless the request is
+ * for generic server options, the application will need to set this.
+ */
+ result = Curl_setstropt(&data->set.str[STRING_RTSP_STREAM_URI],
+ va_arg(param, char *));
+ break;
+
+ case CURLOPT_RTSP_TRANSPORT:
+ /*
+ * The content of the Transport: header for the RTSP request
+ */
+ result = Curl_setstropt(&data->set.str[STRING_RTSP_TRANSPORT],
+ va_arg(param, char *));
+ break;
+
+ case CURLOPT_RTSP_CLIENT_CSEQ:
+ /*
+ * Set the CSEQ number to issue for the next RTSP request. Useful if the
+ * application is resuming a previously broken connection. The CSEQ
+ * will increment from this new number henceforth.
+ */
+ data->state.rtsp_next_client_CSeq = va_arg(param, long);
+ break;
+
+ case CURLOPT_RTSP_SERVER_CSEQ:
+ /* Same as the above, but for server-initiated requests */
+ data->state.rtsp_next_server_CSeq = va_arg(param, long);
+ break;
+
+ case CURLOPT_INTERLEAVEDATA:
+ data->set.rtp_out = va_arg(param, void *);
+ break;
+ case CURLOPT_INTERLEAVEFUNCTION:
+ /* Set the user defined RTP write function */
+ data->set.fwrite_rtp = va_arg(param, curl_write_callback);
+ break;
+#endif
+#ifndef CURL_DISABLE_FTP
+ case CURLOPT_WILDCARDMATCH:
+ data->set.wildcard_enabled = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+ case CURLOPT_CHUNK_BGN_FUNCTION:
+ data->set.chunk_bgn = va_arg(param, curl_chunk_bgn_callback);
+ break;
+ case CURLOPT_CHUNK_END_FUNCTION:
+ data->set.chunk_end = va_arg(param, curl_chunk_end_callback);
+ break;
+ case CURLOPT_FNMATCH_FUNCTION:
+ data->set.fnmatch = va_arg(param, curl_fnmatch_callback);
+ break;
+ case CURLOPT_CHUNK_DATA:
+ data->wildcard.customptr = va_arg(param, void *);
+ break;
+ case CURLOPT_FNMATCH_DATA:
+ data->set.fnmatch_data = va_arg(param, void *);
+ break;
+#endif
+#ifdef USE_TLS_SRP
+ case CURLOPT_TLSAUTH_USERNAME:
+ result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME],
+ va_arg(param, char *));
+ if(data->set.str[STRING_TLSAUTH_USERNAME] &&
+ !data->set.ssl.primary.authtype)
+ data->set.ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default to SRP */
+ break;
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXY_TLSAUTH_USERNAME:
+ result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME_PROXY],
+ va_arg(param, char *));
+ if(data->set.str[STRING_TLSAUTH_USERNAME_PROXY] &&
+ !data->set.proxy_ssl.primary.authtype)
+ data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default to
+ SRP */
+ break;
+#endif
+ case CURLOPT_TLSAUTH_PASSWORD:
+ result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD],
+ va_arg(param, char *));
+ if(data->set.str[STRING_TLSAUTH_USERNAME] &&
+ !data->set.ssl.primary.authtype)
+ data->set.ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default */
+ break;
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXY_TLSAUTH_PASSWORD:
+ result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD_PROXY],
+ va_arg(param, char *));
+ if(data->set.str[STRING_TLSAUTH_USERNAME_PROXY] &&
+ !data->set.proxy_ssl.primary.authtype)
+ data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default */
+ break;
+#endif
+ case CURLOPT_TLSAUTH_TYPE:
+ argptr = va_arg(param, char *);
+ if(!argptr ||
+ strncasecompare(argptr, "SRP", strlen("SRP")))
+ data->set.ssl.primary.authtype = CURL_TLSAUTH_SRP;
+ else
+ data->set.ssl.primary.authtype = CURL_TLSAUTH_NONE;
+ break;
+#ifndef CURL_DISABLE_PROXY
+ case CURLOPT_PROXY_TLSAUTH_TYPE:
+ argptr = va_arg(param, char *);
+ if(!argptr ||
+ strncasecompare(argptr, "SRP", strlen("SRP")))
+ data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_SRP;
+ else
+ data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_NONE;
+ break;
+#endif
+#endif
+#ifdef USE_ARES
+ case CURLOPT_DNS_SERVERS:
+ result = Curl_setstropt(&data->set.str[STRING_DNS_SERVERS],
+ va_arg(param, char *));
+ if(result)
+ return result;
+ result = Curl_set_dns_servers(data, data->set.str[STRING_DNS_SERVERS]);
+ break;
+ case CURLOPT_DNS_INTERFACE:
+ result = Curl_setstropt(&data->set.str[STRING_DNS_INTERFACE],
+ va_arg(param, char *));
+ if(result)
+ return result;
+ result = Curl_set_dns_interface(data, data->set.str[STRING_DNS_INTERFACE]);
+ break;
+ case CURLOPT_DNS_LOCAL_IP4:
+ result = Curl_setstropt(&data->set.str[STRING_DNS_LOCAL_IP4],
+ va_arg(param, char *));
+ if(result)
+ return result;
+ result = Curl_set_dns_local_ip4(data, data->set.str[STRING_DNS_LOCAL_IP4]);
+ break;
+ case CURLOPT_DNS_LOCAL_IP6:
+ result = Curl_setstropt(&data->set.str[STRING_DNS_LOCAL_IP6],
+ va_arg(param, char *));
+ if(result)
+ return result;
+ result = Curl_set_dns_local_ip6(data, data->set.str[STRING_DNS_LOCAL_IP6]);
+ break;
+#endif
+ case CURLOPT_TCP_KEEPALIVE:
+ data->set.tcp_keepalive = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+ case CURLOPT_TCP_KEEPIDLE:
+ arg = va_arg(param, long);
+ if(arg < 0)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ else if(arg > INT_MAX)
+ arg = INT_MAX;
+ data->set.tcp_keepidle = (int)arg;
+ break;
+ case CURLOPT_TCP_KEEPINTVL:
+ arg = va_arg(param, long);
+ if(arg < 0)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ else if(arg > INT_MAX)
+ arg = INT_MAX;
+ data->set.tcp_keepintvl = (int)arg;
+ break;
+ case CURLOPT_TCP_FASTOPEN:
+#if defined(CONNECT_DATA_IDEMPOTENT) || defined(MSG_FASTOPEN) || \
+ defined(TCP_FASTOPEN_CONNECT)
+ data->set.tcp_fastopen = (0 != va_arg(param, long))?TRUE:FALSE;
+#else
+ result = CURLE_NOT_BUILT_IN;
+#endif
+ break;
+ case CURLOPT_SSL_ENABLE_NPN:
+ break;
+ case CURLOPT_SSL_ENABLE_ALPN:
+ data->set.ssl_enable_alpn = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+#ifdef USE_UNIX_SOCKETS
+ case CURLOPT_UNIX_SOCKET_PATH:
+ data->set.abstract_unix_socket = FALSE;
+ result = Curl_setstropt(&data->set.str[STRING_UNIX_SOCKET_PATH],
+ va_arg(param, char *));
+ break;
+ case CURLOPT_ABSTRACT_UNIX_SOCKET:
+ data->set.abstract_unix_socket = TRUE;
+ result = Curl_setstropt(&data->set.str[STRING_UNIX_SOCKET_PATH],
+ va_arg(param, char *));
+ break;
+#endif
+
+ case CURLOPT_PATH_AS_IS:
+ data->set.path_as_is = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+ case CURLOPT_PIPEWAIT:
+ data->set.pipewait = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+ case CURLOPT_STREAM_WEIGHT:
+#ifndef USE_NGHTTP2
+ return CURLE_NOT_BUILT_IN;
+#else
+ arg = va_arg(param, long);
+ if((arg >= 1) && (arg <= 256))
+ data->set.stream_weight = (int)arg;
+ break;
+#endif
+ case CURLOPT_STREAM_DEPENDS:
+ case CURLOPT_STREAM_DEPENDS_E:
+ {
+#ifndef USE_NGHTTP2
+ return CURLE_NOT_BUILT_IN;
+#else
+ struct Curl_easy *dep = va_arg(param, struct Curl_easy *);
+ if(!dep || GOOD_EASY_HANDLE(dep)) {
+ if(data->set.stream_depends_on) {
+ Curl_http2_remove_child(data->set.stream_depends_on, data);
+ }
+ Curl_http2_add_child(dep, data, (option == CURLOPT_STREAM_DEPENDS_E));
+ }
+ break;
+#endif
+ }
+ case CURLOPT_CONNECT_TO:
+ data->set.connect_to = va_arg(param, struct curl_slist *);
+ break;
+ case CURLOPT_SUPPRESS_CONNECT_HEADERS:
+ data->set.suppress_connect_headers = (0 != va_arg(param, long))?TRUE:FALSE;
+ break;
+ case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS:
+ uarg = va_arg(param, unsigned long);
+ if(uarg >= UINT_MAX)
+ uarg = UINT_MAX;
+ data->set.happy_eyeballs_timeout = (unsigned int)uarg;
+ break;
+#ifndef CURL_DISABLE_SHUFFLE_DNS
+ case CURLOPT_DNS_SHUFFLE_ADDRESSES:
+ data->set.dns_shuffle_addresses = (0 != va_arg(param, long)) ? TRUE:FALSE;
+ break;
+#endif
+ case CURLOPT_DISALLOW_USERNAME_IN_URL:
+ data->set.disallow_username_in_url =
+ (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+#ifndef CURL_DISABLE_DOH
+ case CURLOPT_DOH_URL:
+ result = Curl_setstropt(&data->set.str[STRING_DOH],
+ va_arg(param, char *));
+ data->set.doh = data->set.str[STRING_DOH]?TRUE:FALSE;
+ break;
+#endif
+ case CURLOPT_UPKEEP_INTERVAL_MS:
+ arg = va_arg(param, long);
+ if(arg < 0)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.upkeep_interval_ms = arg;
+ break;
+ case CURLOPT_MAXAGE_CONN:
+ arg = va_arg(param, long);
+ if(arg < 0)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.maxage_conn = arg;
+ break;
+ case CURLOPT_MAXLIFETIME_CONN:
+ arg = va_arg(param, long);
+ if(arg < 0)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.maxlifetime_conn = arg;
+ break;
+ case CURLOPT_TRAILERFUNCTION:
+#ifndef CURL_DISABLE_HTTP
+ data->set.trailer_callback = va_arg(param, curl_trailer_callback);
+#endif
+ break;
+ case CURLOPT_TRAILERDATA:
+#ifndef CURL_DISABLE_HTTP
+ data->set.trailer_data = va_arg(param, void *);
+#endif
+ break;
+#ifndef CURL_DISABLE_HSTS
+ case CURLOPT_HSTSREADFUNCTION:
+ data->set.hsts_read = va_arg(param, curl_hstsread_callback);
+ break;
+ case CURLOPT_HSTSREADDATA:
+ data->set.hsts_read_userp = va_arg(param, void *);
+ break;
+ case CURLOPT_HSTSWRITEFUNCTION:
+ data->set.hsts_write = va_arg(param, curl_hstswrite_callback);
+ break;
+ case CURLOPT_HSTSWRITEDATA:
+ data->set.hsts_write_userp = va_arg(param, void *);
+ break;
+ case CURLOPT_HSTS:
+ if(!data->hsts) {
+ data->hsts = Curl_hsts_init();
+ if(!data->hsts)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ argptr = va_arg(param, char *);
+ result = Curl_setstropt(&data->set.str[STRING_HSTS], argptr);
+ if(result)
+ return result;
+ if(argptr)
+ (void)Curl_hsts_loadfile(data, data->hsts, argptr);
+ break;
+ case CURLOPT_HSTS_CTRL:
+ arg = va_arg(param, long);
+ if(arg & CURLHSTS_ENABLE) {
+ if(!data->hsts) {
+ data->hsts = Curl_hsts_init();
+ if(!data->hsts)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ else
+ Curl_hsts_cleanup(&data->hsts);
+ break;
+#endif
+#ifndef CURL_DISABLE_ALTSVC
+ case CURLOPT_ALTSVC:
+ if(!data->asi) {
+ data->asi = Curl_altsvc_init();
+ if(!data->asi)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ argptr = va_arg(param, char *);
+ result = Curl_setstropt(&data->set.str[STRING_ALTSVC], argptr);
+ if(result)
+ return result;
+ if(argptr)
+ (void)Curl_altsvc_load(data->asi, argptr);
+ break;
+ case CURLOPT_ALTSVC_CTRL:
+ if(!data->asi) {
+ data->asi = Curl_altsvc_init();
+ if(!data->asi)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ arg = va_arg(param, long);
+ result = Curl_altsvc_ctrl(data->asi, arg);
+ if(result)
+ return result;
+ break;
+#endif
+ case CURLOPT_PREREQFUNCTION:
+ data->set.fprereq = va_arg(param, curl_prereq_callback);
+ break;
+ case CURLOPT_PREREQDATA:
+ data->set.prereq_userp = va_arg(param, void *);
+ break;
+#ifdef USE_WEBSOCKETS
+ case CURLOPT_WS_OPTIONS: {
+ bool raw;
+ arg = va_arg(param, long);
+ raw = (arg & CURLWS_RAW_MODE);
+ data->set.ws_raw_mode = raw;
+ break;
+ }
+#endif
+ case CURLOPT_QUICK_EXIT:
+ data->set.quick_exit = (0 != va_arg(param, long)) ? 1L:0L;
+ break;
+ default:
+ /* unknown tag and its companion, just ignore: */
+ result = CURLE_UNKNOWN_OPTION;
+ break;
+ }
+
+ return result;
+}
+
+/*
+ * curl_easy_setopt() is the external interface for setting options on an
+ * easy handle.
+ *
+ * NOTE: This is one of few API functions that are allowed to be called from
+ * within a callback.
+ */
+
+#undef curl_easy_setopt
+CURLcode curl_easy_setopt(struct Curl_easy *data, CURLoption tag, ...)
+{
+ va_list arg;
+ CURLcode result;
+
+ if(!data)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ va_start(arg, tag);
+
+ result = Curl_vsetopt(data, tag, arg);
+
+ va_end(arg);
+ return result;
+}
diff --git a/libs/libcurl/src/setopt.h b/libs/libcurl/src/setopt.h index ffc77a71dc..2c229e2990 100644 --- a/libs/libcurl/src/setopt.h +++ b/libs/libcurl/src/setopt.h @@ -1,32 +1,32 @@ -#ifndef HEADER_CURL_SETOPT_H -#define HEADER_CURL_SETOPT_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 Curl_setstropt(char **charp, const char *s); -CURLcode Curl_setblobopt(struct curl_blob **blobp, - const struct curl_blob *blob); -CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list arg); - -#endif /* HEADER_CURL_SETOPT_H */ +#ifndef HEADER_CURL_SETOPT_H
+#define HEADER_CURL_SETOPT_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 Curl_setstropt(char **charp, const char *s);
+CURLcode Curl_setblobopt(struct curl_blob **blobp,
+ const struct curl_blob *blob);
+CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list arg);
+
+#endif /* HEADER_CURL_SETOPT_H */
diff --git a/libs/libcurl/src/setup-os400.h b/libs/libcurl/src/setup-os400.h index 6023ca20de..2ae5ea04f1 100644 --- a/libs/libcurl/src/setup-os400.h +++ b/libs/libcurl/src/setup-os400.h @@ -1,229 +1,229 @@ -#ifndef HEADER_CURL_SETUP_OS400_H -#define HEADER_CURL_SETUP_OS400_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - - -/* OS/400 netdb.h does not define NI_MAXHOST. */ -#define NI_MAXHOST 1025 - -/* OS/400 netdb.h does not define NI_MAXSERV. */ -#define NI_MAXSERV 32 - -/* No OS/400 header file defines u_int32_t. */ -typedef unsigned long u_int32_t; - - -/* System API wrapper prototypes & definitions to support ASCII parameters. */ - -#include <sys/socket.h> -#include <netdb.h> -#include <gskssl.h> -#include <qsoasync.h> -#include <gssapi.h> - -extern int Curl_getaddrinfo_a(const char *nodename, - const char *servname, - const struct addrinfo *hints, - struct addrinfo **res); -#define getaddrinfo Curl_getaddrinfo_a - - -extern int Curl_getnameinfo_a(const struct sockaddr *sa, - curl_socklen_t salen, - char *nodename, curl_socklen_t nodenamelen, - char *servname, curl_socklen_t servnamelen, - int flags); -#define getnameinfo Curl_getnameinfo_a - - -/* GSKit wrappers. */ - -extern int Curl_gsk_environment_open(gsk_handle * my_env_handle); -#define gsk_environment_open Curl_gsk_environment_open - -extern int Curl_gsk_secure_soc_open(gsk_handle my_env_handle, - gsk_handle * my_session_handle); -#define gsk_secure_soc_open Curl_gsk_secure_soc_open - -extern int Curl_gsk_environment_close(gsk_handle * my_env_handle); -#define gsk_environment_close Curl_gsk_environment_close - -extern int Curl_gsk_secure_soc_close(gsk_handle * my_session_handle); -#define gsk_secure_soc_close Curl_gsk_secure_soc_close - -extern int Curl_gsk_environment_init(gsk_handle my_env_handle); -#define gsk_environment_init Curl_gsk_environment_init - -extern int Curl_gsk_secure_soc_init(gsk_handle my_session_handle); -#define gsk_secure_soc_init Curl_gsk_secure_soc_init - -extern int Curl_gsk_attribute_set_buffer_a(gsk_handle my_gsk_handle, - GSK_BUF_ID bufID, - const char *buffer, - int bufSize); -#define gsk_attribute_set_buffer Curl_gsk_attribute_set_buffer_a - -extern int Curl_gsk_attribute_set_enum(gsk_handle my_gsk_handle, - GSK_ENUM_ID enumID, - GSK_ENUM_VALUE enumValue); -#define gsk_attribute_set_enum Curl_gsk_attribute_set_enum - -extern int Curl_gsk_attribute_set_numeric_value(gsk_handle my_gsk_handle, - GSK_NUM_ID numID, - int numValue); -#define gsk_attribute_set_numeric_value Curl_gsk_attribute_set_numeric_value - -extern int Curl_gsk_attribute_set_callback(gsk_handle my_gsk_handle, - GSK_CALLBACK_ID callBackID, - void *callBackAreaPtr); -#define gsk_attribute_set_callback Curl_gsk_attribute_set_callback - -extern int Curl_gsk_attribute_get_buffer_a(gsk_handle my_gsk_handle, - GSK_BUF_ID bufID, - const char **buffer, - int *bufSize); -#define gsk_attribute_get_buffer Curl_gsk_attribute_get_buffer_a - -extern int Curl_gsk_attribute_get_enum(gsk_handle my_gsk_handle, - GSK_ENUM_ID enumID, - GSK_ENUM_VALUE *enumValue); -#define gsk_attribute_get_enum Curl_gsk_attribute_get_enum - -extern int Curl_gsk_attribute_get_numeric_value(gsk_handle my_gsk_handle, - GSK_NUM_ID numID, - int *numValue); -#define gsk_attribute_get_numeric_value Curl_gsk_attribute_get_numeric_value - -extern int Curl_gsk_attribute_get_cert_info(gsk_handle my_gsk_handle, - GSK_CERT_ID certID, - const gsk_cert_data_elem **certDataElem, - int *certDataElementCount); -#define gsk_attribute_get_cert_info Curl_gsk_attribute_get_cert_info - -extern int Curl_gsk_secure_soc_misc(gsk_handle my_session_handle, - GSK_MISC_ID miscID); -#define gsk_secure_soc_misc Curl_gsk_secure_soc_misc - -extern int Curl_gsk_secure_soc_read(gsk_handle my_session_handle, - char *readBuffer, - int readBufSize, int *amtRead); -#define gsk_secure_soc_read Curl_gsk_secure_soc_read - -extern int Curl_gsk_secure_soc_write(gsk_handle my_session_handle, - char *writeBuffer, - int writeBufSize, int *amtWritten); -#define gsk_secure_soc_write Curl_gsk_secure_soc_write - -extern const char * Curl_gsk_strerror_a(int gsk_return_value); -#define gsk_strerror Curl_gsk_strerror_a - -extern int Curl_gsk_secure_soc_startInit(gsk_handle my_session_handle, - int IOCompletionPort, - Qso_OverlappedIO_t * communicationsArea); -#define gsk_secure_soc_startInit Curl_gsk_secure_soc_startInit - - -/* GSSAPI wrappers. */ - -extern OM_uint32 Curl_gss_import_name_a(OM_uint32 * minor_status, - gss_buffer_t in_name, - gss_OID in_name_type, - gss_name_t * out_name); -#define gss_import_name Curl_gss_import_name_a - - -extern OM_uint32 Curl_gss_display_status_a(OM_uint32 * minor_status, - OM_uint32 status_value, - int status_type, gss_OID mech_type, - gss_msg_ctx_t * message_context, - gss_buffer_t status_string); -#define gss_display_status Curl_gss_display_status_a - - -extern OM_uint32 Curl_gss_init_sec_context_a(OM_uint32 * minor_status, - gss_cred_id_t cred_handle, - gss_ctx_id_t * context_handle, - gss_name_t target_name, - gss_OID mech_type, - gss_flags_t req_flags, - OM_uint32 time_req, - gss_channel_bindings_t - input_chan_bindings, - gss_buffer_t input_token, - gss_OID * actual_mech_type, - gss_buffer_t output_token, - gss_flags_t * ret_flags, - OM_uint32 * time_rec); -#define gss_init_sec_context Curl_gss_init_sec_context_a - - -extern OM_uint32 Curl_gss_delete_sec_context_a(OM_uint32 * minor_status, - gss_ctx_id_t * context_handle, - gss_buffer_t output_token); -#define gss_delete_sec_context Curl_gss_delete_sec_context_a - - -/* LDAP wrappers. */ - -#define BerValue struct berval - -#define ldap_url_parse ldap_url_parse_utf8 -#define ldap_init Curl_ldap_init_a -#define ldap_simple_bind_s Curl_ldap_simple_bind_s_a -#define ldap_search_s Curl_ldap_search_s_a -#define ldap_get_values_len Curl_ldap_get_values_len_a -#define ldap_err2string Curl_ldap_err2string_a -#define ldap_get_dn Curl_ldap_get_dn_a -#define ldap_first_attribute Curl_ldap_first_attribute_a -#define ldap_next_attribute Curl_ldap_next_attribute_a - -/* Some socket functions must be wrapped to process textual addresses - like AF_UNIX. */ - -extern int Curl_os400_connect(int sd, struct sockaddr *destaddr, int addrlen); -extern int Curl_os400_bind(int sd, struct sockaddr *localaddr, int addrlen); -extern int Curl_os400_sendto(int sd, char *buffer, int buflen, int flags, - struct sockaddr *dstaddr, int addrlen); -extern int Curl_os400_recvfrom(int sd, char *buffer, int buflen, int flags, - struct sockaddr *fromaddr, int *addrlen); -extern int Curl_os400_getpeername(int sd, struct sockaddr *addr, int *addrlen); -extern int Curl_os400_getsockname(int sd, struct sockaddr *addr, int *addrlen); - -#define connect Curl_os400_connect -#define bind Curl_os400_bind -#define sendto Curl_os400_sendto -#define recvfrom Curl_os400_recvfrom -#define getpeername Curl_os400_getpeername -#define getsockname Curl_os400_getsockname - -#ifdef HAVE_LIBZ -#define zlibVersion Curl_os400_zlibVersion -#define inflateInit_ Curl_os400_inflateInit_ -#define inflateInit2_ Curl_os400_inflateInit2_ -#define inflate Curl_os400_inflate -#define inflateEnd Curl_os400_inflateEnd -#endif - -#endif /* HEADER_CURL_SETUP_OS400_H */ +#ifndef HEADER_CURL_SETUP_OS400_H
+#define HEADER_CURL_SETUP_OS400_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+
+/* OS/400 netdb.h does not define NI_MAXHOST. */
+#define NI_MAXHOST 1025
+
+/* OS/400 netdb.h does not define NI_MAXSERV. */
+#define NI_MAXSERV 32
+
+/* No OS/400 header file defines u_int32_t. */
+typedef unsigned long u_int32_t;
+
+
+/* System API wrapper prototypes & definitions to support ASCII parameters. */
+
+#include <sys/socket.h>
+#include <netdb.h>
+#include <gskssl.h>
+#include <qsoasync.h>
+#include <gssapi.h>
+
+extern int Curl_getaddrinfo_a(const char *nodename,
+ const char *servname,
+ const struct addrinfo *hints,
+ struct addrinfo **res);
+#define getaddrinfo Curl_getaddrinfo_a
+
+/* Note socklen_t must be used as this is declared before curl_socklen_t */
+extern int Curl_getnameinfo_a(const struct sockaddr *sa,
+ socklen_t salen,
+ char *nodename, socklen_t nodenamelen,
+ char *servname, socklen_t servnamelen,
+ int flags);
+#define getnameinfo Curl_getnameinfo_a
+
+
+/* GSKit wrappers. */
+
+extern int Curl_gsk_environment_open(gsk_handle * my_env_handle);
+#define gsk_environment_open Curl_gsk_environment_open
+
+extern int Curl_gsk_secure_soc_open(gsk_handle my_env_handle,
+ gsk_handle * my_session_handle);
+#define gsk_secure_soc_open Curl_gsk_secure_soc_open
+
+extern int Curl_gsk_environment_close(gsk_handle * my_env_handle);
+#define gsk_environment_close Curl_gsk_environment_close
+
+extern int Curl_gsk_secure_soc_close(gsk_handle * my_session_handle);
+#define gsk_secure_soc_close Curl_gsk_secure_soc_close
+
+extern int Curl_gsk_environment_init(gsk_handle my_env_handle);
+#define gsk_environment_init Curl_gsk_environment_init
+
+extern int Curl_gsk_secure_soc_init(gsk_handle my_session_handle);
+#define gsk_secure_soc_init Curl_gsk_secure_soc_init
+
+extern int Curl_gsk_attribute_set_buffer_a(gsk_handle my_gsk_handle,
+ GSK_BUF_ID bufID,
+ const char *buffer,
+ int bufSize);
+#define gsk_attribute_set_buffer Curl_gsk_attribute_set_buffer_a
+
+extern int Curl_gsk_attribute_set_enum(gsk_handle my_gsk_handle,
+ GSK_ENUM_ID enumID,
+ GSK_ENUM_VALUE enumValue);
+#define gsk_attribute_set_enum Curl_gsk_attribute_set_enum
+
+extern int Curl_gsk_attribute_set_numeric_value(gsk_handle my_gsk_handle,
+ GSK_NUM_ID numID,
+ int numValue);
+#define gsk_attribute_set_numeric_value Curl_gsk_attribute_set_numeric_value
+
+extern int Curl_gsk_attribute_set_callback(gsk_handle my_gsk_handle,
+ GSK_CALLBACK_ID callBackID,
+ void *callBackAreaPtr);
+#define gsk_attribute_set_callback Curl_gsk_attribute_set_callback
+
+extern int Curl_gsk_attribute_get_buffer_a(gsk_handle my_gsk_handle,
+ GSK_BUF_ID bufID,
+ const char **buffer,
+ int *bufSize);
+#define gsk_attribute_get_buffer Curl_gsk_attribute_get_buffer_a
+
+extern int Curl_gsk_attribute_get_enum(gsk_handle my_gsk_handle,
+ GSK_ENUM_ID enumID,
+ GSK_ENUM_VALUE *enumValue);
+#define gsk_attribute_get_enum Curl_gsk_attribute_get_enum
+
+extern int Curl_gsk_attribute_get_numeric_value(gsk_handle my_gsk_handle,
+ GSK_NUM_ID numID,
+ int *numValue);
+#define gsk_attribute_get_numeric_value Curl_gsk_attribute_get_numeric_value
+
+extern int Curl_gsk_attribute_get_cert_info(gsk_handle my_gsk_handle,
+ GSK_CERT_ID certID,
+ const gsk_cert_data_elem **certDataElem,
+ int *certDataElementCount);
+#define gsk_attribute_get_cert_info Curl_gsk_attribute_get_cert_info
+
+extern int Curl_gsk_secure_soc_misc(gsk_handle my_session_handle,
+ GSK_MISC_ID miscID);
+#define gsk_secure_soc_misc Curl_gsk_secure_soc_misc
+
+extern int Curl_gsk_secure_soc_read(gsk_handle my_session_handle,
+ char *readBuffer,
+ int readBufSize, int *amtRead);
+#define gsk_secure_soc_read Curl_gsk_secure_soc_read
+
+extern int Curl_gsk_secure_soc_write(gsk_handle my_session_handle,
+ char *writeBuffer,
+ int writeBufSize, int *amtWritten);
+#define gsk_secure_soc_write Curl_gsk_secure_soc_write
+
+extern const char * Curl_gsk_strerror_a(int gsk_return_value);
+#define gsk_strerror Curl_gsk_strerror_a
+
+extern int Curl_gsk_secure_soc_startInit(gsk_handle my_session_handle,
+ int IOCompletionPort,
+ Qso_OverlappedIO_t * communicationsArea);
+#define gsk_secure_soc_startInit Curl_gsk_secure_soc_startInit
+
+
+/* GSSAPI wrappers. */
+
+extern OM_uint32 Curl_gss_import_name_a(OM_uint32 * minor_status,
+ gss_buffer_t in_name,
+ gss_OID in_name_type,
+ gss_name_t * out_name);
+#define gss_import_name Curl_gss_import_name_a
+
+
+extern OM_uint32 Curl_gss_display_status_a(OM_uint32 * minor_status,
+ OM_uint32 status_value,
+ int status_type, gss_OID mech_type,
+ gss_msg_ctx_t * message_context,
+ gss_buffer_t status_string);
+#define gss_display_status Curl_gss_display_status_a
+
+
+extern OM_uint32 Curl_gss_init_sec_context_a(OM_uint32 * minor_status,
+ gss_cred_id_t cred_handle,
+ gss_ctx_id_t * context_handle,
+ gss_name_t target_name,
+ gss_OID mech_type,
+ gss_flags_t req_flags,
+ OM_uint32 time_req,
+ gss_channel_bindings_t
+ input_chan_bindings,
+ gss_buffer_t input_token,
+ gss_OID * actual_mech_type,
+ gss_buffer_t output_token,
+ gss_flags_t * ret_flags,
+ OM_uint32 * time_rec);
+#define gss_init_sec_context Curl_gss_init_sec_context_a
+
+
+extern OM_uint32 Curl_gss_delete_sec_context_a(OM_uint32 * minor_status,
+ gss_ctx_id_t * context_handle,
+ gss_buffer_t output_token);
+#define gss_delete_sec_context Curl_gss_delete_sec_context_a
+
+
+/* LDAP wrappers. */
+
+#define BerValue struct berval
+
+#define ldap_url_parse ldap_url_parse_utf8
+#define ldap_init Curl_ldap_init_a
+#define ldap_simple_bind_s Curl_ldap_simple_bind_s_a
+#define ldap_search_s Curl_ldap_search_s_a
+#define ldap_get_values_len Curl_ldap_get_values_len_a
+#define ldap_err2string Curl_ldap_err2string_a
+#define ldap_get_dn Curl_ldap_get_dn_a
+#define ldap_first_attribute Curl_ldap_first_attribute_a
+#define ldap_next_attribute Curl_ldap_next_attribute_a
+
+/* Some socket functions must be wrapped to process textual addresses
+ like AF_UNIX. */
+
+extern int Curl_os400_connect(int sd, struct sockaddr *destaddr, int addrlen);
+extern int Curl_os400_bind(int sd, struct sockaddr *localaddr, int addrlen);
+extern int Curl_os400_sendto(int sd, char *buffer, int buflen, int flags,
+ struct sockaddr *dstaddr, int addrlen);
+extern int Curl_os400_recvfrom(int sd, char *buffer, int buflen, int flags,
+ struct sockaddr *fromaddr, int *addrlen);
+extern int Curl_os400_getpeername(int sd, struct sockaddr *addr, int *addrlen);
+extern int Curl_os400_getsockname(int sd, struct sockaddr *addr, int *addrlen);
+
+#define connect Curl_os400_connect
+#define bind Curl_os400_bind
+#define sendto Curl_os400_sendto
+#define recvfrom Curl_os400_recvfrom
+#define getpeername Curl_os400_getpeername
+#define getsockname Curl_os400_getsockname
+
+#ifdef HAVE_LIBZ
+#define zlibVersion Curl_os400_zlibVersion
+#define inflateInit_ Curl_os400_inflateInit_
+#define inflateInit2_ Curl_os400_inflateInit2_
+#define inflate Curl_os400_inflate
+#define inflateEnd Curl_os400_inflateEnd
+#endif
+
+#endif /* HEADER_CURL_SETUP_OS400_H */
diff --git a/libs/libcurl/src/setup-vms.h b/libs/libcurl/src/setup-vms.h index b570683d78..880c0260fc 100644 --- a/libs/libcurl/src/setup-vms.h +++ b/libs/libcurl/src/setup-vms.h @@ -1,445 +1,445 @@ -#ifndef HEADER_CURL_SETUP_VMS_H -#define HEADER_CURL_SETUP_VMS_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - -/* */ -/* JEM, 12/30/12, VMS now generates config.h, so only define wrappers for */ -/* getenv(), getpwuid() and provide is_vms_shell() */ -/* Also need upper case symbols for system services, and */ -/* OpenSSL, and some Kerberos image */ - -#ifdef __DECC -#pragma message save -#pragma message disable dollarid -#endif - -/* Hide the stuff we are overriding */ -#define getenv decc_getenv -#ifdef __DECC -# if __INITIAL_POINTER_SIZE != 64 -# define getpwuid decc_getpwuid -# endif -#endif -#include <stdlib.h> -char *decc$getenv(const char *__name); -#include <pwd.h> - -#include <string.h> -#include <unixlib.h> - -#undef getenv -#undef getpwuid -#define getenv vms_getenv -#define getpwuid vms_getpwuid - -/* VAX needs these in upper case when compiling exact case */ -#define sys$assign SYS$ASSIGN -#define sys$dassgn SYS$DASSGN -#define sys$qiow SYS$QIOW - -#ifdef __DECC -# if __INITIAL_POINTER_SIZE -# pragma __pointer_size __save -# endif -#endif - -#if __USE_LONG_GID_T -# define decc_getpwuid DECC$__LONG_GID_GETPWUID -#else -# if __INITIAL_POINTER_SIZE -# define decc_getpwuid decc$__32_getpwuid -# else -# define decc_getpwuid decc$getpwuid -# endif -#endif - - struct passwd *decc_getpwuid(uid_t uid); - -#ifdef __DECC -# if __INITIAL_POINTER_SIZE == 32 -/* Translate the path, but only if the path is a VMS file specification */ -/* The translation is usually only needed for older versions of VMS */ -static char *vms_translate_path(const char *path) -{ - char *unix_path; - char *test_str; - - /* See if the result is in VMS format, if not, we are done */ - /* Assume that this is a PATH, not just some data */ - test_str = strpbrk(path, ":[<^"); - if(!test_str) { - return (char *)path; - } - - unix_path = decc$translate_vms(path); - - if((int)unix_path <= 0) { - /* We can not translate it, so return the original string */ - return (char *)path; - } -} -# else - /* VMS translate path is actually not needed on the current 64 bit */ - /* VMS platforms, so instead of figuring out the pointer settings */ - /* Change it to a noop */ -# define vms_translate_path(__path) __path -# endif -#endif - -#ifdef __DECC -# if __INITIAL_POINTER_SIZE -# pragma __pointer_size __restore -# endif -#endif - -static char *vms_getenv(const char *envvar) -{ - char *result; - char *vms_path; - - /* first use the DECC getenv() function */ - result = decc$getenv(envvar); - if(!result) { - return result; - } - - vms_path = result; - result = vms_translate_path(vms_path); - - /* note that if you backport this to use VAX C RTL, that the VAX C RTL */ - /* may do a malloc(2048) for each call to getenv(), so you will need */ - /* to add a free(vms_path) */ - /* Do not do a free() for DEC C RTL builds, which should be used for */ - /* VMS 5.5-2 and later, even if using GCC */ - - return result; -} - - -static struct passwd vms_passwd_cache; - -static struct passwd *vms_getpwuid(uid_t uid) -{ - struct passwd *my_passwd; - -/* Hack needed to support 64 bit builds, decc_getpwnam is 32 bit only */ -#ifdef __DECC -# if __INITIAL_POINTER_SIZE - __char_ptr32 unix_path; -# else - char *unix_path; -# endif -#else - char *unix_path; -#endif - - my_passwd = decc_getpwuid(uid); - if(!my_passwd) { - return my_passwd; - } - - unix_path = vms_translate_path(my_passwd->pw_dir); - - if((long)unix_path <= 0) { - /* We can not translate it, so return the original string */ - return my_passwd; - } - - /* If no changes needed just return it */ - if(unix_path == my_passwd->pw_dir) { - return my_passwd; - } - - /* Need to copy the structure returned */ - /* Since curl is only using pw_dir, no need to fix up */ - /* the pw_shell when running under Bash */ - vms_passwd_cache.pw_name = my_passwd->pw_name; - vms_passwd_cache.pw_uid = my_passwd->pw_uid; - vms_passwd_cache.pw_gid = my_passwd->pw_uid; - vms_passwd_cache.pw_dir = unix_path; - vms_passwd_cache.pw_shell = my_passwd->pw_shell; - - return &vms_passwd_cache; -} - -#ifdef __DECC -#pragma message restore -#endif - -/* Bug - VMS OpenSSL and Kerberos universal symbols are in uppercase only */ -/* VMS libraries should have universal symbols in exact and uppercase */ - -#define ASN1_INTEGER_get ASN1_INTEGER_GET -#define ASN1_STRING_data ASN1_STRING_DATA -#define ASN1_STRING_length ASN1_STRING_LENGTH -#define ASN1_STRING_print ASN1_STRING_PRINT -#define ASN1_STRING_to_UTF8 ASN1_STRING_TO_UTF8 -#define ASN1_STRING_type ASN1_STRING_TYPE -#define BIO_ctrl BIO_CTRL -#define BIO_free BIO_FREE -#define BIO_new BIO_NEW -#define BIO_s_mem BIO_S_MEM -#define BN_bn2bin BN_BN2BIN -#define BN_num_bits BN_NUM_BITS -#define CRYPTO_cleanup_all_ex_data CRYPTO_CLEANUP_ALL_EX_DATA -#define CRYPTO_free CRYPTO_FREE -#define CRYPTO_malloc CRYPTO_MALLOC -#define CONF_modules_load_file CONF_MODULES_LOAD_FILE -#ifdef __VAX -# ifdef VMS_OLD_SSL - /* Ancient OpenSSL on VAX/VMS missing this constant */ -# define CONF_MFLAGS_IGNORE_MISSING_FILE 0x10 -# undef CONF_modules_load_file - static int CONF_modules_load_file(const char *filename, - const char *appname, - unsigned long flags) { - return 1; - } -# endif -#endif -#define DES_ecb_encrypt DES_ECB_ENCRYPT -#define DES_set_key DES_SET_KEY -#define DES_set_odd_parity DES_SET_ODD_PARITY -#define ENGINE_ctrl ENGINE_CTRL -#define ENGINE_ctrl_cmd ENGINE_CTRL_CMD -#define ENGINE_finish ENGINE_FINISH -#define ENGINE_free ENGINE_FREE -#define ENGINE_get_first ENGINE_GET_FIRST -#define ENGINE_get_id ENGINE_GET_ID -#define ENGINE_get_next ENGINE_GET_NEXT -#define ENGINE_init ENGINE_INIT -#define ENGINE_load_builtin_engines ENGINE_LOAD_BUILTIN_ENGINES -#define ENGINE_load_private_key ENGINE_LOAD_PRIVATE_KEY -#define ENGINE_set_default ENGINE_SET_DEFAULT -#define ERR_clear_error ERR_CLEAR_ERROR -#define ERR_error_string ERR_ERROR_STRING -#define ERR_error_string_n ERR_ERROR_STRING_N -#define ERR_free_strings ERR_FREE_STRINGS -#define ERR_get_error ERR_GET_ERROR -#define ERR_peek_error ERR_PEEK_ERROR -#define ERR_remove_state ERR_REMOVE_STATE -#define EVP_PKEY_copy_parameters EVP_PKEY_COPY_PARAMETERS -#define EVP_PKEY_free EVP_PKEY_FREE -#define EVP_cleanup EVP_CLEANUP -#define GENERAL_NAMES_free GENERAL_NAMES_FREE -#define i2d_X509_PUBKEY I2D_X509_PUBKEY -#define MD4_Final MD4_FINAL -#define MD4_Init MD4_INIT -#define MD4_Update MD4_UPDATE -#define MD5_Final MD5_FINAL -#define MD5_Init MD5_INIT -#define MD5_Update MD5_UPDATE -#define OPENSSL_add_all_algo_noconf OPENSSL_ADD_ALL_ALGO_NOCONF -#ifndef __VAX -#define OPENSSL_load_builtin_modules OPENSSL_LOAD_BUILTIN_MODULES -#endif -#define PEM_read_X509 PEM_READ_X509 -#define PEM_write_bio_X509 PEM_WRITE_BIO_X509 -#define PKCS12_PBE_add PKCS12_PBE_ADD -#define PKCS12_free PKCS12_FREE -#define PKCS12_parse PKCS12_PARSE -#define RAND_add RAND_ADD -#define RAND_bytes RAND_BYTES -#define RAND_egd RAND_EGD -#define RAND_file_name RAND_FILE_NAME -#define RAND_load_file RAND_LOAD_FILE -#define RAND_status RAND_STATUS -#define SSL_CIPHER_get_name SSL_CIPHER_GET_NAME -#define SSL_CTX_add_client_CA SSL_CTX_ADD_CLIENT_CA -#define SSL_CTX_callback_ctrl SSL_CTX_CALLBACK_CTRL -#define SSL_CTX_check_private_key SSL_CTX_CHECK_PRIVATE_KEY -#define SSL_CTX_ctrl SSL_CTX_CTRL -#define SSL_CTX_free SSL_CTX_FREE -#define SSL_CTX_get_cert_store SSL_CTX_GET_CERT_STORE -#define SSL_CTX_load_verify_locations SSL_CTX_LOAD_VERIFY_LOCATIONS -#define SSL_CTX_new SSL_CTX_NEW -#define SSL_CTX_set_cipher_list SSL_CTX_SET_CIPHER_LIST -#define SSL_CTX_set_def_passwd_cb_ud SSL_CTX_SET_DEF_PASSWD_CB_UD -#define SSL_CTX_set_default_passwd_cb SSL_CTX_SET_DEFAULT_PASSWD_CB -#define SSL_CTX_set_msg_callback SSL_CTX_SET_MSG_CALLBACK -#define SSL_CTX_set_verify SSL_CTX_SET_VERIFY -#define SSL_CTX_use_PrivateKey SSL_CTX_USE_PRIVATEKEY -#define SSL_CTX_use_PrivateKey_file SSL_CTX_USE_PRIVATEKEY_FILE -#define SSL_CTX_use_cert_chain_file SSL_CTX_USE_CERT_CHAIN_FILE -#define SSL_CTX_use_certificate SSL_CTX_USE_CERTIFICATE -#define SSL_CTX_use_certificate_file SSL_CTX_USE_CERTIFICATE_FILE -#define SSL_SESSION_free SSL_SESSION_FREE -#define SSL_connect SSL_CONNECT -#define SSL_free SSL_FREE -#define SSL_get1_session SSL_GET1_SESSION -#define SSL_get_certificate SSL_GET_CERTIFICATE -#define SSL_get_current_cipher SSL_GET_CURRENT_CIPHER -#define SSL_get_error SSL_GET_ERROR -#define SSL_get_peer_cert_chain SSL_GET_PEER_CERT_CHAIN -#define SSL_get_peer_certificate SSL_GET_PEER_CERTIFICATE -#define SSL_get_privatekey SSL_GET_PRIVATEKEY -#define SSL_get_session SSL_GET_SESSION -#define SSL_get_shutdown SSL_GET_SHUTDOWN -#define SSL_get_verify_result SSL_GET_VERIFY_RESULT -#define SSL_library_init SSL_LIBRARY_INIT -#define SSL_load_error_strings SSL_LOAD_ERROR_STRINGS -#define SSL_new SSL_NEW -#define SSL_peek SSL_PEEK -#define SSL_pending SSL_PENDING -#define SSL_read SSL_READ -#define SSL_set_connect_state SSL_SET_CONNECT_STATE -#define SSL_set_fd SSL_SET_FD -#define SSL_set_session SSL_SET_SESSION -#define SSL_shutdown SSL_SHUTDOWN -#define SSL_version SSL_VERSION -#define SSL_write SSL_WRITE -#define SSLeay SSLEAY -#define SSLv23_client_method SSLV23_CLIENT_METHOD -#define SSLv3_client_method SSLV3_CLIENT_METHOD -#define TLSv1_client_method TLSV1_CLIENT_METHOD -#define UI_create_method UI_CREATE_METHOD -#define UI_destroy_method UI_DESTROY_METHOD -#define UI_get0_user_data UI_GET0_USER_DATA -#define UI_get_input_flags UI_GET_INPUT_FLAGS -#define UI_get_string_type UI_GET_STRING_TYPE -#define UI_create_method UI_CREATE_METHOD -#define UI_destroy_method UI_DESTROY_METHOD -#define UI_method_get_closer UI_METHOD_GET_CLOSER -#define UI_method_get_opener UI_METHOD_GET_OPENER -#define UI_method_get_reader UI_METHOD_GET_READER -#define UI_method_get_writer UI_METHOD_GET_WRITER -#define UI_method_set_closer UI_METHOD_SET_CLOSER -#define UI_method_set_opener UI_METHOD_SET_OPENER -#define UI_method_set_reader UI_METHOD_SET_READER -#define UI_method_set_writer UI_METHOD_SET_WRITER -#define UI_OpenSSL UI_OPENSSL -#define UI_set_result UI_SET_RESULT -#define X509V3_EXT_print X509V3_EXT_PRINT -#define X509_EXTENSION_get_critical X509_EXTENSION_GET_CRITICAL -#define X509_EXTENSION_get_data X509_EXTENSION_GET_DATA -#define X509_EXTENSION_get_object X509_EXTENSION_GET_OBJECT -#define X509_LOOKUP_file X509_LOOKUP_FILE -#define X509_NAME_ENTRY_get_data X509_NAME_ENTRY_GET_DATA -#define X509_NAME_get_entry X509_NAME_GET_ENTRY -#define X509_NAME_get_index_by_NID X509_NAME_GET_INDEX_BY_NID -#define X509_NAME_print_ex X509_NAME_PRINT_EX -#define X509_STORE_CTX_get_current_cert X509_STORE_CTX_GET_CURRENT_CERT -#define X509_STORE_add_lookup X509_STORE_ADD_LOOKUP -#define X509_STORE_set_flags X509_STORE_SET_FLAGS -#define X509_check_issued X509_CHECK_ISSUED -#define X509_free X509_FREE -#define X509_get_ext_d2i X509_GET_EXT_D2I -#define X509_get_issuer_name X509_GET_ISSUER_NAME -#define X509_get_pubkey X509_GET_PUBKEY -#define X509_get_serialNumber X509_GET_SERIALNUMBER -#define X509_get_subject_name X509_GET_SUBJECT_NAME -#define X509_load_crl_file X509_LOAD_CRL_FILE -#define X509_verify_cert_error_string X509_VERIFY_CERT_ERROR_STRING -#define d2i_PKCS12_fp D2I_PKCS12_FP -#define i2t_ASN1_OBJECT I2T_ASN1_OBJECT -#define sk_num SK_NUM -#define sk_pop SK_POP -#define sk_pop_free SK_POP_FREE -#define sk_value SK_VALUE -#ifdef __VAX -#define OPENSSL_NO_SHA256 -#endif -#define SHA256_Final SHA256_FINAL -#define SHA256_Init SHA256_INIT -#define SHA256_Update SHA256_UPDATE - -#define USE_UPPERCASE_GSSAPI 1 -#define gss_seal GSS_SEAL -#define gss_unseal GSS_UNSEAL - -#define USE_UPPERCASE_KRBAPI 1 - -/* AI_NUMERICHOST needed for IP V6 support in Curl */ -#ifdef HAVE_NETDB_H -#include <netdb.h> -#ifndef AI_NUMERICHOST -#ifdef ENABLE_IPV6 -#undef ENABLE_IPV6 -#endif -#endif -#endif - -/* VAX symbols are always in uppercase */ -#ifdef __VAX -#define inflate INFLATE -#define inflateEnd INFLATEEND -#define inflateInit2_ INFLATEINIT2_ -#define inflateInit_ INFLATEINIT_ -#define zlibVersion ZLIBVERSION -#endif - -/* Older VAX OpenSSL port defines these as Macros */ -/* Need to include the headers first and then redefine */ -/* that way a newer port will also work if some one has one */ -#ifdef __VAX - -# if (OPENSSL_VERSION_NUMBER < 0x00907001L) -# define des_set_odd_parity DES_SET_ODD_PARITY -# define des_set_key DES_SET_KEY -# define des_ecb_encrypt DES_ECB_ENCRYPT - -# endif -# include <openssl/evp.h> -# ifndef OpenSSL_add_all_algorithms -# define OpenSSL_add_all_algorithms OPENSSL_ADD_ALL_ALGORITHMS - void OPENSSL_ADD_ALL_ALGORITHMS(void); -# endif - - /* Curl defines these to lower case and VAX needs them in upper case */ - /* So we need static routines */ -# if (OPENSSL_VERSION_NUMBER < 0x00907001L) - -# undef des_set_odd_parity -# undef DES_set_odd_parity -# undef des_set_key -# undef DES_set_key -# undef des_ecb_encrypt -# undef DES_ecb_encrypt - - static void des_set_odd_parity(des_cblock *key) { - DES_SET_ODD_PARITY(key); - } - - static int des_set_key(const_des_cblock *key, - des_key_schedule schedule) { - return DES_SET_KEY(key, schedule); - } - - static void des_ecb_encrypt(const_des_cblock *input, - des_cblock *output, - des_key_schedule ks, int enc) { - DES_ECB_ENCRYPT(input, output, ks, enc); - } -#endif -/* Need this to stop a macro redefinition error */ -#if OPENSSL_VERSION_NUMBER < 0x00907000L -# ifdef X509_STORE_set_flags -# undef X509_STORE_set_flags -# define X509_STORE_set_flags(x,y) Curl_nop_stmt -# endif -#endif -#endif - -#endif /* HEADER_CURL_SETUP_VMS_H */ +#ifndef HEADER_CURL_SETUP_VMS_H
+#define HEADER_CURL_SETUP_VMS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+/* */
+/* JEM, 12/30/12, VMS now generates config.h, so only define wrappers for */
+/* getenv(), getpwuid() and provide is_vms_shell() */
+/* Also need upper case symbols for system services, and */
+/* OpenSSL, and some Kerberos image */
+
+#ifdef __DECC
+#pragma message save
+#pragma message disable dollarid
+#endif
+
+/* Hide the stuff we are overriding */
+#define getenv decc_getenv
+#ifdef __DECC
+# if __INITIAL_POINTER_SIZE != 64
+# define getpwuid decc_getpwuid
+# endif
+#endif
+#include <stdlib.h>
+char *decc$getenv(const char *__name);
+#include <pwd.h>
+
+#include <string.h>
+#include <unixlib.h>
+
+#undef getenv
+#undef getpwuid
+#define getenv vms_getenv
+#define getpwuid vms_getpwuid
+
+/* VAX needs these in upper case when compiling exact case */
+#define sys$assign SYS$ASSIGN
+#define sys$dassgn SYS$DASSGN
+#define sys$qiow SYS$QIOW
+
+#ifdef __DECC
+# if __INITIAL_POINTER_SIZE
+# pragma __pointer_size __save
+# endif
+#endif
+
+#if __USE_LONG_GID_T
+# define decc_getpwuid DECC$__LONG_GID_GETPWUID
+#else
+# if __INITIAL_POINTER_SIZE
+# define decc_getpwuid decc$__32_getpwuid
+# else
+# define decc_getpwuid decc$getpwuid
+# endif
+#endif
+
+ struct passwd *decc_getpwuid(uid_t uid);
+
+#ifdef __DECC
+# if __INITIAL_POINTER_SIZE == 32
+/* Translate the path, but only if the path is a VMS file specification */
+/* The translation is usually only needed for older versions of VMS */
+static char *vms_translate_path(const char *path)
+{
+ char *unix_path;
+ char *test_str;
+
+ /* See if the result is in VMS format, if not, we are done */
+ /* Assume that this is a PATH, not just some data */
+ test_str = strpbrk(path, ":[<^");
+ if(!test_str) {
+ return (char *)path;
+ }
+
+ unix_path = decc$translate_vms(path);
+
+ if((int)unix_path <= 0) {
+ /* We can not translate it, so return the original string */
+ return (char *)path;
+ }
+}
+# else
+ /* VMS translate path is actually not needed on the current 64 bit */
+ /* VMS platforms, so instead of figuring out the pointer settings */
+ /* Change it to a noop */
+# define vms_translate_path(__path) __path
+# endif
+#endif
+
+#ifdef __DECC
+# if __INITIAL_POINTER_SIZE
+# pragma __pointer_size __restore
+# endif
+#endif
+
+static char *vms_getenv(const char *envvar)
+{
+ char *result;
+ char *vms_path;
+
+ /* first use the DECC getenv() function */
+ result = decc$getenv(envvar);
+ if(!result) {
+ return result;
+ }
+
+ vms_path = result;
+ result = vms_translate_path(vms_path);
+
+ /* note that if you backport this to use VAX C RTL, that the VAX C RTL */
+ /* may do a malloc(2048) for each call to getenv(), so you will need */
+ /* to add a free(vms_path) */
+ /* Do not do a free() for DEC C RTL builds, which should be used for */
+ /* VMS 5.5-2 and later, even if using GCC */
+
+ return result;
+}
+
+
+static struct passwd vms_passwd_cache;
+
+static struct passwd *vms_getpwuid(uid_t uid)
+{
+ struct passwd *my_passwd;
+
+/* Hack needed to support 64 bit builds, decc_getpwnam is 32 bit only */
+#ifdef __DECC
+# if __INITIAL_POINTER_SIZE
+ __char_ptr32 unix_path;
+# else
+ char *unix_path;
+# endif
+#else
+ char *unix_path;
+#endif
+
+ my_passwd = decc_getpwuid(uid);
+ if(!my_passwd) {
+ return my_passwd;
+ }
+
+ unix_path = vms_translate_path(my_passwd->pw_dir);
+
+ if((long)unix_path <= 0) {
+ /* We can not translate it, so return the original string */
+ return my_passwd;
+ }
+
+ /* If no changes needed just return it */
+ if(unix_path == my_passwd->pw_dir) {
+ return my_passwd;
+ }
+
+ /* Need to copy the structure returned */
+ /* Since curl is only using pw_dir, no need to fix up */
+ /* the pw_shell when running under Bash */
+ vms_passwd_cache.pw_name = my_passwd->pw_name;
+ vms_passwd_cache.pw_uid = my_passwd->pw_uid;
+ vms_passwd_cache.pw_gid = my_passwd->pw_uid;
+ vms_passwd_cache.pw_dir = unix_path;
+ vms_passwd_cache.pw_shell = my_passwd->pw_shell;
+
+ return &vms_passwd_cache;
+}
+
+#ifdef __DECC
+#pragma message restore
+#endif
+
+/* Bug - VMS OpenSSL and Kerberos universal symbols are in uppercase only */
+/* VMS libraries should have universal symbols in exact and uppercase */
+
+#define ASN1_INTEGER_get ASN1_INTEGER_GET
+#define ASN1_STRING_data ASN1_STRING_DATA
+#define ASN1_STRING_length ASN1_STRING_LENGTH
+#define ASN1_STRING_print ASN1_STRING_PRINT
+#define ASN1_STRING_to_UTF8 ASN1_STRING_TO_UTF8
+#define ASN1_STRING_type ASN1_STRING_TYPE
+#define BIO_ctrl BIO_CTRL
+#define BIO_free BIO_FREE
+#define BIO_new BIO_NEW
+#define BIO_s_mem BIO_S_MEM
+#define BN_bn2bin BN_BN2BIN
+#define BN_num_bits BN_NUM_BITS
+#define CRYPTO_cleanup_all_ex_data CRYPTO_CLEANUP_ALL_EX_DATA
+#define CRYPTO_free CRYPTO_FREE
+#define CRYPTO_malloc CRYPTO_MALLOC
+#define CONF_modules_load_file CONF_MODULES_LOAD_FILE
+#ifdef __VAX
+# ifdef VMS_OLD_SSL
+ /* Ancient OpenSSL on VAX/VMS missing this constant */
+# define CONF_MFLAGS_IGNORE_MISSING_FILE 0x10
+# undef CONF_modules_load_file
+ static int CONF_modules_load_file(const char *filename,
+ const char *appname,
+ unsigned long flags) {
+ return 1;
+ }
+# endif
+#endif
+#define DES_ecb_encrypt DES_ECB_ENCRYPT
+#define DES_set_key DES_SET_KEY
+#define DES_set_odd_parity DES_SET_ODD_PARITY
+#define ENGINE_ctrl ENGINE_CTRL
+#define ENGINE_ctrl_cmd ENGINE_CTRL_CMD
+#define ENGINE_finish ENGINE_FINISH
+#define ENGINE_free ENGINE_FREE
+#define ENGINE_get_first ENGINE_GET_FIRST
+#define ENGINE_get_id ENGINE_GET_ID
+#define ENGINE_get_next ENGINE_GET_NEXT
+#define ENGINE_init ENGINE_INIT
+#define ENGINE_load_builtin_engines ENGINE_LOAD_BUILTIN_ENGINES
+#define ENGINE_load_private_key ENGINE_LOAD_PRIVATE_KEY
+#define ENGINE_set_default ENGINE_SET_DEFAULT
+#define ERR_clear_error ERR_CLEAR_ERROR
+#define ERR_error_string ERR_ERROR_STRING
+#define ERR_error_string_n ERR_ERROR_STRING_N
+#define ERR_free_strings ERR_FREE_STRINGS
+#define ERR_get_error ERR_GET_ERROR
+#define ERR_peek_error ERR_PEEK_ERROR
+#define ERR_remove_state ERR_REMOVE_STATE
+#define EVP_PKEY_copy_parameters EVP_PKEY_COPY_PARAMETERS
+#define EVP_PKEY_free EVP_PKEY_FREE
+#define EVP_cleanup EVP_CLEANUP
+#define GENERAL_NAMES_free GENERAL_NAMES_FREE
+#define i2d_X509_PUBKEY I2D_X509_PUBKEY
+#define MD4_Final MD4_FINAL
+#define MD4_Init MD4_INIT
+#define MD4_Update MD4_UPDATE
+#define MD5_Final MD5_FINAL
+#define MD5_Init MD5_INIT
+#define MD5_Update MD5_UPDATE
+#define OPENSSL_add_all_algo_noconf OPENSSL_ADD_ALL_ALGO_NOCONF
+#ifndef __VAX
+#define OPENSSL_load_builtin_modules OPENSSL_LOAD_BUILTIN_MODULES
+#endif
+#define PEM_read_X509 PEM_READ_X509
+#define PEM_write_bio_X509 PEM_WRITE_BIO_X509
+#define PKCS12_PBE_add PKCS12_PBE_ADD
+#define PKCS12_free PKCS12_FREE
+#define PKCS12_parse PKCS12_PARSE
+#define RAND_add RAND_ADD
+#define RAND_bytes RAND_BYTES
+#define RAND_egd RAND_EGD
+#define RAND_file_name RAND_FILE_NAME
+#define RAND_load_file RAND_LOAD_FILE
+#define RAND_status RAND_STATUS
+#define SSL_CIPHER_get_name SSL_CIPHER_GET_NAME
+#define SSL_CTX_add_client_CA SSL_CTX_ADD_CLIENT_CA
+#define SSL_CTX_callback_ctrl SSL_CTX_CALLBACK_CTRL
+#define SSL_CTX_check_private_key SSL_CTX_CHECK_PRIVATE_KEY
+#define SSL_CTX_ctrl SSL_CTX_CTRL
+#define SSL_CTX_free SSL_CTX_FREE
+#define SSL_CTX_get_cert_store SSL_CTX_GET_CERT_STORE
+#define SSL_CTX_load_verify_locations SSL_CTX_LOAD_VERIFY_LOCATIONS
+#define SSL_CTX_new SSL_CTX_NEW
+#define SSL_CTX_set_cipher_list SSL_CTX_SET_CIPHER_LIST
+#define SSL_CTX_set_def_passwd_cb_ud SSL_CTX_SET_DEF_PASSWD_CB_UD
+#define SSL_CTX_set_default_passwd_cb SSL_CTX_SET_DEFAULT_PASSWD_CB
+#define SSL_CTX_set_msg_callback SSL_CTX_SET_MSG_CALLBACK
+#define SSL_CTX_set_verify SSL_CTX_SET_VERIFY
+#define SSL_CTX_use_PrivateKey SSL_CTX_USE_PRIVATEKEY
+#define SSL_CTX_use_PrivateKey_file SSL_CTX_USE_PRIVATEKEY_FILE
+#define SSL_CTX_use_cert_chain_file SSL_CTX_USE_CERT_CHAIN_FILE
+#define SSL_CTX_use_certificate SSL_CTX_USE_CERTIFICATE
+#define SSL_CTX_use_certificate_file SSL_CTX_USE_CERTIFICATE_FILE
+#define SSL_SESSION_free SSL_SESSION_FREE
+#define SSL_connect SSL_CONNECT
+#define SSL_free SSL_FREE
+#define SSL_get1_session SSL_GET1_SESSION
+#define SSL_get_certificate SSL_GET_CERTIFICATE
+#define SSL_get_current_cipher SSL_GET_CURRENT_CIPHER
+#define SSL_get_error SSL_GET_ERROR
+#define SSL_get_peer_cert_chain SSL_GET_PEER_CERT_CHAIN
+#define SSL_get_peer_certificate SSL_GET_PEER_CERTIFICATE
+#define SSL_get_privatekey SSL_GET_PRIVATEKEY
+#define SSL_get_session SSL_GET_SESSION
+#define SSL_get_shutdown SSL_GET_SHUTDOWN
+#define SSL_get_verify_result SSL_GET_VERIFY_RESULT
+#define SSL_library_init SSL_LIBRARY_INIT
+#define SSL_load_error_strings SSL_LOAD_ERROR_STRINGS
+#define SSL_new SSL_NEW
+#define SSL_peek SSL_PEEK
+#define SSL_pending SSL_PENDING
+#define SSL_read SSL_READ
+#define SSL_set_connect_state SSL_SET_CONNECT_STATE
+#define SSL_set_fd SSL_SET_FD
+#define SSL_set_session SSL_SET_SESSION
+#define SSL_shutdown SSL_SHUTDOWN
+#define SSL_version SSL_VERSION
+#define SSL_write SSL_WRITE
+#define SSLeay SSLEAY
+#define SSLv23_client_method SSLV23_CLIENT_METHOD
+#define SSLv3_client_method SSLV3_CLIENT_METHOD
+#define TLSv1_client_method TLSV1_CLIENT_METHOD
+#define UI_create_method UI_CREATE_METHOD
+#define UI_destroy_method UI_DESTROY_METHOD
+#define UI_get0_user_data UI_GET0_USER_DATA
+#define UI_get_input_flags UI_GET_INPUT_FLAGS
+#define UI_get_string_type UI_GET_STRING_TYPE
+#define UI_create_method UI_CREATE_METHOD
+#define UI_destroy_method UI_DESTROY_METHOD
+#define UI_method_get_closer UI_METHOD_GET_CLOSER
+#define UI_method_get_opener UI_METHOD_GET_OPENER
+#define UI_method_get_reader UI_METHOD_GET_READER
+#define UI_method_get_writer UI_METHOD_GET_WRITER
+#define UI_method_set_closer UI_METHOD_SET_CLOSER
+#define UI_method_set_opener UI_METHOD_SET_OPENER
+#define UI_method_set_reader UI_METHOD_SET_READER
+#define UI_method_set_writer UI_METHOD_SET_WRITER
+#define UI_OpenSSL UI_OPENSSL
+#define UI_set_result UI_SET_RESULT
+#define X509V3_EXT_print X509V3_EXT_PRINT
+#define X509_EXTENSION_get_critical X509_EXTENSION_GET_CRITICAL
+#define X509_EXTENSION_get_data X509_EXTENSION_GET_DATA
+#define X509_EXTENSION_get_object X509_EXTENSION_GET_OBJECT
+#define X509_LOOKUP_file X509_LOOKUP_FILE
+#define X509_NAME_ENTRY_get_data X509_NAME_ENTRY_GET_DATA
+#define X509_NAME_get_entry X509_NAME_GET_ENTRY
+#define X509_NAME_get_index_by_NID X509_NAME_GET_INDEX_BY_NID
+#define X509_NAME_print_ex X509_NAME_PRINT_EX
+#define X509_STORE_CTX_get_current_cert X509_STORE_CTX_GET_CURRENT_CERT
+#define X509_STORE_add_lookup X509_STORE_ADD_LOOKUP
+#define X509_STORE_set_flags X509_STORE_SET_FLAGS
+#define X509_check_issued X509_CHECK_ISSUED
+#define X509_free X509_FREE
+#define X509_get_ext_d2i X509_GET_EXT_D2I
+#define X509_get_issuer_name X509_GET_ISSUER_NAME
+#define X509_get_pubkey X509_GET_PUBKEY
+#define X509_get_serialNumber X509_GET_SERIALNUMBER
+#define X509_get_subject_name X509_GET_SUBJECT_NAME
+#define X509_load_crl_file X509_LOAD_CRL_FILE
+#define X509_verify_cert_error_string X509_VERIFY_CERT_ERROR_STRING
+#define d2i_PKCS12_fp D2I_PKCS12_FP
+#define i2t_ASN1_OBJECT I2T_ASN1_OBJECT
+#define sk_num SK_NUM
+#define sk_pop SK_POP
+#define sk_pop_free SK_POP_FREE
+#define sk_value SK_VALUE
+#ifdef __VAX
+#define OPENSSL_NO_SHA256
+#endif
+#define SHA256_Final SHA256_FINAL
+#define SHA256_Init SHA256_INIT
+#define SHA256_Update SHA256_UPDATE
+
+#define USE_UPPERCASE_GSSAPI 1
+#define gss_seal GSS_SEAL
+#define gss_unseal GSS_UNSEAL
+
+#define USE_UPPERCASE_KRBAPI 1
+
+/* AI_NUMERICHOST needed for IP V6 support in Curl */
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#ifndef AI_NUMERICHOST
+#ifdef ENABLE_IPV6
+#undef ENABLE_IPV6
+#endif
+#endif
+#endif
+
+/* VAX symbols are always in uppercase */
+#ifdef __VAX
+#define inflate INFLATE
+#define inflateEnd INFLATEEND
+#define inflateInit2_ INFLATEINIT2_
+#define inflateInit_ INFLATEINIT_
+#define zlibVersion ZLIBVERSION
+#endif
+
+/* Older VAX OpenSSL port defines these as Macros */
+/* Need to include the headers first and then redefine */
+/* that way a newer port will also work if some one has one */
+#ifdef __VAX
+
+# if (OPENSSL_VERSION_NUMBER < 0x00907001L)
+# define des_set_odd_parity DES_SET_ODD_PARITY
+# define des_set_key DES_SET_KEY
+# define des_ecb_encrypt DES_ECB_ENCRYPT
+
+# endif
+# include <openssl/evp.h>
+# ifndef OpenSSL_add_all_algorithms
+# define OpenSSL_add_all_algorithms OPENSSL_ADD_ALL_ALGORITHMS
+ void OPENSSL_ADD_ALL_ALGORITHMS(void);
+# endif
+
+ /* Curl defines these to lower case and VAX needs them in upper case */
+ /* So we need static routines */
+# if (OPENSSL_VERSION_NUMBER < 0x00907001L)
+
+# undef des_set_odd_parity
+# undef DES_set_odd_parity
+# undef des_set_key
+# undef DES_set_key
+# undef des_ecb_encrypt
+# undef DES_ecb_encrypt
+
+ static void des_set_odd_parity(des_cblock *key) {
+ DES_SET_ODD_PARITY(key);
+ }
+
+ static int des_set_key(const_des_cblock *key,
+ des_key_schedule schedule) {
+ return DES_SET_KEY(key, schedule);
+ }
+
+ static void des_ecb_encrypt(const_des_cblock *input,
+ des_cblock *output,
+ des_key_schedule ks, int enc) {
+ DES_ECB_ENCRYPT(input, output, ks, enc);
+ }
+#endif
+/* Need this to stop a macro redefinition error */
+#if OPENSSL_VERSION_NUMBER < 0x00907000L
+# ifdef X509_STORE_set_flags
+# undef X509_STORE_set_flags
+# define X509_STORE_set_flags(x,y) Curl_nop_stmt
+# endif
+#endif
+#endif
+
+#endif /* HEADER_CURL_SETUP_VMS_H */
diff --git a/libs/libcurl/src/setup-win32.h b/libs/libcurl/src/setup-win32.h index bc5f8efc3c..1f5b1257dd 100644 --- a/libs/libcurl/src/setup-win32.h +++ b/libs/libcurl/src/setup-win32.h @@ -1,127 +1,127 @@ -#ifndef HEADER_CURL_SETUP_WIN32_H -#define HEADER_CURL_SETUP_WIN32_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 header files for windows builds before redefining anything. - * Use this preprocessor block only to include or exclude windows.h, - * winsock2.h or ws2tcpip.h. Any other windows thing belongs - * to any other further and independent block. Under Cygwin things work - * just as under linux (e.g. <sys/socket.h>) and the winsock headers should - * never be included when __CYGWIN__ is defined. configure script takes - * care of this, not defining HAVE_WINDOWS_H, HAVE_WINSOCK2_H, - * neither HAVE_WS2TCPIP_H when __CYGWIN__ is defined. - */ - -#ifdef HAVE_WINDOWS_H -# if defined(UNICODE) && !defined(_UNICODE) -# error "UNICODE is defined but _UNICODE is not defined" -# endif -# if defined(_UNICODE) && !defined(UNICODE) -# error "_UNICODE is defined but UNICODE is not defined" -# endif -/* - * Don't include unneeded stuff in Windows headers to avoid compiler - * warnings and macro clashes. - * Make sure to define this macro before including any Windows headers. - */ -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif -# ifndef NOGDI -# define NOGDI -# endif -# include <winerror.h> -# include <windows.h> -# ifdef HAVE_WINSOCK2_H -# include <winsock2.h> -# ifdef HAVE_WS2TCPIP_H -# include <ws2tcpip.h> -# endif -# endif -# include <tchar.h> -# ifdef UNICODE - typedef wchar_t *(*curl_wcsdup_callback)(const wchar_t *str); -# endif -#endif - -/* - * Define USE_WINSOCK to 2 if we have and use WINSOCK2 API, else - * undefine USE_WINSOCK. - */ - -#undef USE_WINSOCK - -#ifdef HAVE_WINSOCK2_H -# define USE_WINSOCK 2 -#endif - -/* - * Define _WIN32_WINNT_[OS] symbols because not all Windows build systems have - * those symbols to compare against, and even those that do may be missing - * newer symbols. - */ - -#ifndef _WIN32_WINNT_NT4 -#define _WIN32_WINNT_NT4 0x0400 /* Windows NT 4.0 */ -#endif -#ifndef _WIN32_WINNT_WIN2K -#define _WIN32_WINNT_WIN2K 0x0500 /* Windows 2000 */ -#endif -#ifndef _WIN32_WINNT_WINXP -#define _WIN32_WINNT_WINXP 0x0501 /* Windows XP */ -#endif -#ifndef _WIN32_WINNT_WS03 -#define _WIN32_WINNT_WS03 0x0502 /* Windows Server 2003 */ -#endif -#ifndef _WIN32_WINNT_WIN6 -#define _WIN32_WINNT_WIN6 0x0600 /* Windows Vista */ -#endif -#ifndef _WIN32_WINNT_VISTA -#define _WIN32_WINNT_VISTA 0x0600 /* Windows Vista */ -#endif -#ifndef _WIN32_WINNT_WS08 -#define _WIN32_WINNT_WS08 0x0600 /* Windows Server 2008 */ -#endif -#ifndef _WIN32_WINNT_LONGHORN -#define _WIN32_WINNT_LONGHORN 0x0600 /* Windows Vista */ -#endif -#ifndef _WIN32_WINNT_WIN7 -#define _WIN32_WINNT_WIN7 0x0601 /* Windows 7 */ -#endif -#ifndef _WIN32_WINNT_WIN8 -#define _WIN32_WINNT_WIN8 0x0602 /* Windows 8 */ -#endif -#ifndef _WIN32_WINNT_WINBLUE -#define _WIN32_WINNT_WINBLUE 0x0603 /* Windows 8.1 */ -#endif -#ifndef _WIN32_WINNT_WINTHRESHOLD -#define _WIN32_WINNT_WINTHRESHOLD 0x0A00 /* Windows 10 */ -#endif -#ifndef _WIN32_WINNT_WIN10 -#define _WIN32_WINNT_WIN10 0x0A00 /* Windows 10 */ -#endif - -#endif /* HEADER_CURL_SETUP_WIN32_H */ +#ifndef HEADER_CURL_SETUP_WIN32_H
+#define HEADER_CURL_SETUP_WIN32_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 header files for windows builds before redefining anything.
+ * Use this preprocessor block only to include or exclude windows.h,
+ * winsock2.h or ws2tcpip.h. Any other windows thing belongs
+ * to any other further and independent block. Under Cygwin things work
+ * just as under linux (e.g. <sys/socket.h>) and the winsock headers should
+ * never be included when __CYGWIN__ is defined. configure script takes
+ * care of this, not defining HAVE_WINDOWS_H, HAVE_WINSOCK2_H,
+ * neither HAVE_WS2TCPIP_H when __CYGWIN__ is defined.
+ */
+
+#ifdef HAVE_WINDOWS_H
+# if defined(UNICODE) && !defined(_UNICODE)
+# error "UNICODE is defined but _UNICODE is not defined"
+# endif
+# if defined(_UNICODE) && !defined(UNICODE)
+# error "_UNICODE is defined but UNICODE is not defined"
+# endif
+/*
+ * Don't include unneeded stuff in Windows headers to avoid compiler
+ * warnings and macro clashes.
+ * Make sure to define this macro before including any Windows headers.
+ */
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# ifndef NOGDI
+# define NOGDI
+# endif
+# include <winerror.h>
+# include <windows.h>
+# ifdef HAVE_WINSOCK2_H
+# include <winsock2.h>
+# ifdef HAVE_WS2TCPIP_H
+# include <ws2tcpip.h>
+# endif
+# endif
+# include <tchar.h>
+# ifdef UNICODE
+ typedef wchar_t *(*curl_wcsdup_callback)(const wchar_t *str);
+# endif
+#endif
+
+/*
+ * Define USE_WINSOCK to 2 if we have and use WINSOCK2 API, else
+ * undefine USE_WINSOCK.
+ */
+
+#undef USE_WINSOCK
+
+#ifdef HAVE_WINSOCK2_H
+# define USE_WINSOCK 2
+#endif
+
+/*
+ * Define _WIN32_WINNT_[OS] symbols because not all Windows build systems have
+ * those symbols to compare against, and even those that do may be missing
+ * newer symbols.
+ */
+
+#ifndef _WIN32_WINNT_NT4
+#define _WIN32_WINNT_NT4 0x0400 /* Windows NT 4.0 */
+#endif
+#ifndef _WIN32_WINNT_WIN2K
+#define _WIN32_WINNT_WIN2K 0x0500 /* Windows 2000 */
+#endif
+#ifndef _WIN32_WINNT_WINXP
+#define _WIN32_WINNT_WINXP 0x0501 /* Windows XP */
+#endif
+#ifndef _WIN32_WINNT_WS03
+#define _WIN32_WINNT_WS03 0x0502 /* Windows Server 2003 */
+#endif
+#ifndef _WIN32_WINNT_WIN6
+#define _WIN32_WINNT_WIN6 0x0600 /* Windows Vista */
+#endif
+#ifndef _WIN32_WINNT_VISTA
+#define _WIN32_WINNT_VISTA 0x0600 /* Windows Vista */
+#endif
+#ifndef _WIN32_WINNT_WS08
+#define _WIN32_WINNT_WS08 0x0600 /* Windows Server 2008 */
+#endif
+#ifndef _WIN32_WINNT_LONGHORN
+#define _WIN32_WINNT_LONGHORN 0x0600 /* Windows Vista */
+#endif
+#ifndef _WIN32_WINNT_WIN7
+#define _WIN32_WINNT_WIN7 0x0601 /* Windows 7 */
+#endif
+#ifndef _WIN32_WINNT_WIN8
+#define _WIN32_WINNT_WIN8 0x0602 /* Windows 8 */
+#endif
+#ifndef _WIN32_WINNT_WINBLUE
+#define _WIN32_WINNT_WINBLUE 0x0603 /* Windows 8.1 */
+#endif
+#ifndef _WIN32_WINNT_WINTHRESHOLD
+#define _WIN32_WINNT_WINTHRESHOLD 0x0A00 /* Windows 10 */
+#endif
+#ifndef _WIN32_WINNT_WIN10
+#define _WIN32_WINNT_WIN10 0x0A00 /* Windows 10 */
+#endif
+
+#endif /* HEADER_CURL_SETUP_WIN32_H */
diff --git a/libs/libcurl/src/sha256.c b/libs/libcurl/src/sha256.c index 60720f5b13..1340cf0a96 100644 --- a/libs/libcurl/src/sha256.c +++ b/libs/libcurl/src/sha256.c @@ -1,549 +1,538 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2017, Florin Petriuc, <petriuc.florin@gmail.com> - * Copyright (C) 2018 - 2022, 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_CRYPTO_AUTH - -#include "warnless.h" -#include "curl_sha256.h" -#include "curl_hmac.h" - -#ifdef USE_WOLFSSL -#include <wolfssl/options.h> -#ifndef NO_SHA256 -#define USE_OPENSSL_SHA256 -#endif -#endif - -#if defined(USE_OPENSSL) - -#include <openssl/opensslv.h> - -#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) -#define USE_OPENSSL_SHA256 -#endif - -#endif /* USE_OPENSSL */ - -#ifdef USE_MBEDTLS -#include <mbedtls/version.h> - -#if(MBEDTLS_VERSION_NUMBER >= 0x02070000) && \ - (MBEDTLS_VERSION_NUMBER < 0x03000000) - #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS -#endif -#endif /* USE_MBEDTLS */ - -/* Please keep the SSL backend-specific #if branches in this order: - * - * 1. USE_OPENSSL - * 2. USE_GNUTLS - * 3. USE_MBEDTLS - * 4. USE_COMMON_CRYPTO - * 5. 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. - */ - -#if defined(USE_OPENSSL_SHA256) - -/* When OpenSSL or wolfSSL is available is available we use their - * SHA256-functions. - */ -#if defined(USE_OPENSSL) -#include <openssl/evp.h> -#elif defined(USE_WOLFSSL) -#include <wolfssl/openssl/evp.h> -#endif - -#include "curl_memory.h" - -/* The last #include file should be: */ -#include "memdebug.h" - -struct sha256_ctx { - EVP_MD_CTX *openssl_ctx; -}; -typedef struct sha256_ctx my_sha256_ctx; - -static CURLcode my_sha256_init(my_sha256_ctx *ctx) -{ - ctx->openssl_ctx = EVP_MD_CTX_create(); - if(!ctx->openssl_ctx) - return CURLE_OUT_OF_MEMORY; - - EVP_DigestInit_ex(ctx->openssl_ctx, EVP_sha256(), NULL); - return CURLE_OK; -} - -static void my_sha256_update(my_sha256_ctx *ctx, - const unsigned char *data, - unsigned int length) -{ - EVP_DigestUpdate(ctx->openssl_ctx, data, length); -} - -static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) -{ - EVP_DigestFinal_ex(ctx->openssl_ctx, digest, NULL); - EVP_MD_CTX_destroy(ctx->openssl_ctx); -} - -#elif defined(USE_GNUTLS) - -#include <nettle/sha.h> - -#include "curl_memory.h" - -/* The last #include file should be: */ -#include "memdebug.h" - -typedef struct sha256_ctx my_sha256_ctx; - -static CURLcode my_sha256_init(my_sha256_ctx *ctx) -{ - sha256_init(ctx); - return CURLE_OK; -} - -static void my_sha256_update(my_sha256_ctx *ctx, - const unsigned char *data, - unsigned int length) -{ - sha256_update(ctx, length, data); -} - -static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) -{ - sha256_digest(ctx, SHA256_DIGEST_SIZE, digest); -} - -#elif defined(USE_MBEDTLS) - -#include <mbedtls/sha256.h> - -#include "curl_memory.h" - -/* The last #include file should be: */ -#include "memdebug.h" - -typedef mbedtls_sha256_context my_sha256_ctx; - -static CURLcode my_sha256_init(my_sha256_ctx *ctx) -{ -#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) - (void) mbedtls_sha256_starts(ctx, 0); -#else - (void) mbedtls_sha256_starts_ret(ctx, 0); -#endif - return CURLE_OK; -} - -static void my_sha256_update(my_sha256_ctx *ctx, - const unsigned char *data, - unsigned int length) -{ -#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) - (void) mbedtls_sha256_update(ctx, data, length); -#else - (void) mbedtls_sha256_update_ret(ctx, data, length); -#endif -} - -static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) -{ -#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) - (void) mbedtls_sha256_finish(ctx, digest); -#else - (void) mbedtls_sha256_finish_ret(ctx, digest); -#endif -} - -#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ - (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040)) || \ - (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ - (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000)) - -#include <CommonCrypto/CommonDigest.h> - -#include "curl_memory.h" - -/* The last #include file should be: */ -#include "memdebug.h" - -typedef CC_SHA256_CTX my_sha256_ctx; - -static CURLcode my_sha256_init(my_sha256_ctx *ctx) -{ - (void) CC_SHA256_Init(ctx); - return CURLE_OK; -} - -static void my_sha256_update(my_sha256_ctx *ctx, - const unsigned char *data, - unsigned int length) -{ - (void) CC_SHA256_Update(ctx, data, length); -} - -static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) -{ - (void) CC_SHA256_Final(digest, ctx); -} - -#elif defined(USE_WIN32_CRYPTO) - -#include <wincrypt.h> - -struct sha256_ctx { - HCRYPTPROV hCryptProv; - HCRYPTHASH hHash; -}; -typedef struct sha256_ctx my_sha256_ctx; - -#if !defined(CALG_SHA_256) -#define CALG_SHA_256 0x0000800c -#endif - -static CURLcode my_sha256_init(my_sha256_ctx *ctx) -{ - if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_AES, - CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { - CryptCreateHash(ctx->hCryptProv, CALG_SHA_256, 0, 0, &ctx->hHash); - } - - return CURLE_OK; -} - -static void my_sha256_update(my_sha256_ctx *ctx, - const unsigned char *data, - unsigned int length) -{ - CryptHashData(ctx->hHash, (unsigned char *) data, length, 0); -} - -static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) -{ - unsigned long length = 0; - - CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0); - if(length == SHA256_DIGEST_LENGTH) - CryptGetHashParam(ctx->hHash, HP_HASHVAL, digest, &length, 0); - - if(ctx->hHash) - CryptDestroyHash(ctx->hHash); - - if(ctx->hCryptProv) - CryptReleaseContext(ctx->hCryptProv, 0); -} - -#else - -/* When no other crypto library is available we use this code segment */ - -/* This is based on SHA256 implementation in LibTomCrypt that was released into - * public domain by Tom St Denis. */ - -#define WPA_GET_BE32(a) ((((unsigned long)(a)[0]) << 24) | \ - (((unsigned long)(a)[1]) << 16) | \ - (((unsigned long)(a)[2]) << 8) | \ - ((unsigned long)(a)[3])) -#define WPA_PUT_BE32(a, val) \ -do { \ - (a)[0] = (unsigned char)((((unsigned long) (val)) >> 24) & 0xff); \ - (a)[1] = (unsigned char)((((unsigned long) (val)) >> 16) & 0xff); \ - (a)[2] = (unsigned char)((((unsigned long) (val)) >> 8) & 0xff); \ - (a)[3] = (unsigned char)(((unsigned long) (val)) & 0xff); \ -} while(0) - -#ifdef HAVE_LONGLONG -#define WPA_PUT_BE64(a, val) \ -do { \ - (a)[0] = (unsigned char)(((unsigned long long)(val)) >> 56); \ - (a)[1] = (unsigned char)(((unsigned long long)(val)) >> 48); \ - (a)[2] = (unsigned char)(((unsigned long long)(val)) >> 40); \ - (a)[3] = (unsigned char)(((unsigned long long)(val)) >> 32); \ - (a)[4] = (unsigned char)(((unsigned long long)(val)) >> 24); \ - (a)[5] = (unsigned char)(((unsigned long long)(val)) >> 16); \ - (a)[6] = (unsigned char)(((unsigned long long)(val)) >> 8); \ - (a)[7] = (unsigned char)(((unsigned long long)(val)) & 0xff); \ -} while(0) -#else -#define WPA_PUT_BE64(a, val) \ -do { \ - (a)[0] = (unsigned char)(((unsigned __int64)(val)) >> 56); \ - (a)[1] = (unsigned char)(((unsigned __int64)(val)) >> 48); \ - (a)[2] = (unsigned char)(((unsigned __int64)(val)) >> 40); \ - (a)[3] = (unsigned char)(((unsigned __int64)(val)) >> 32); \ - (a)[4] = (unsigned char)(((unsigned __int64)(val)) >> 24); \ - (a)[5] = (unsigned char)(((unsigned __int64)(val)) >> 16); \ - (a)[6] = (unsigned char)(((unsigned __int64)(val)) >> 8); \ - (a)[7] = (unsigned char)(((unsigned __int64)(val)) & 0xff); \ -} while(0) -#endif - -struct sha256_state { -#ifdef HAVE_LONGLONG - unsigned long long length; -#else - unsigned __int64 length; -#endif - unsigned long state[8], curlen; - unsigned char buf[64]; -}; -typedef struct sha256_state my_sha256_ctx; - -/* The K array */ -static const unsigned long K[64] = { - 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, - 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, - 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, - 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, - 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, - 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, - 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, - 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, - 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, - 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, - 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, - 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, - 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL -}; - -/* Various logical functions */ -#define RORc(x, y) \ -(((((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)((y) & 31)) | \ - ((unsigned long)(x) << (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL) -#define Ch(x,y,z) (z ^ (x & (y ^ z))) -#define Maj(x,y,z) (((x | y) & z) | (x & y)) -#define S(x, n) RORc((x), (n)) -#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) -#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) -#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) -#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) -#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) - -/* Compress 512-bits */ -static int sha256_compress(struct sha256_state *md, - unsigned char *buf) -{ - unsigned long S[8], W[64]; - int i; - - /* Copy state into S */ - for(i = 0; i < 8; i++) { - S[i] = md->state[i]; - } - /* copy the state into 512-bits into W[0..15] */ - for(i = 0; i < 16; i++) - W[i] = WPA_GET_BE32(buf + (4 * i)); - /* fill W[16..63] */ - for(i = 16; i < 64; i++) { - W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + - W[i - 16]; - } - - /* Compress */ -#define RND(a,b,c,d,e,f,g,h,i) \ - do { \ - unsigned long t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ - unsigned long t1 = Sigma0(a) + Maj(a, b, c); \ - d += t0; \ - h = t0 + t1; \ - } while(0) - - for(i = 0; i < 64; ++i) { - unsigned long t; - RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i); - t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; - S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; - } - - /* Feedback */ - for(i = 0; i < 8; i++) { - md->state[i] = md->state[i] + S[i]; - } - - return 0; -} - -/* Initialize the hash state */ -static CURLcode my_sha256_init(struct sha256_state *md) -{ - md->curlen = 0; - md->length = 0; - md->state[0] = 0x6A09E667UL; - md->state[1] = 0xBB67AE85UL; - md->state[2] = 0x3C6EF372UL; - md->state[3] = 0xA54FF53AUL; - md->state[4] = 0x510E527FUL; - md->state[5] = 0x9B05688CUL; - md->state[6] = 0x1F83D9ABUL; - md->state[7] = 0x5BE0CD19UL; - - return CURLE_OK; -} - -/* - Process a block of memory though the hash - @param md The hash state - @param in The data to hash - @param inlen The length of the data (octets) - @return 0 if successful -*/ -static int my_sha256_update(struct sha256_state *md, - const unsigned char *in, - unsigned long inlen) -{ - unsigned long n; - -#define block_size 64 - if(md->curlen > sizeof(md->buf)) - return -1; - while(inlen > 0) { - if(md->curlen == 0 && inlen >= block_size) { - if(sha256_compress(md, (unsigned char *)in) < 0) - return -1; - md->length += block_size * 8; - in += block_size; - inlen -= block_size; - } - else { - n = CURLMIN(inlen, (block_size - md->curlen)); - memcpy(md->buf + md->curlen, in, n); - md->curlen += n; - in += n; - inlen -= n; - if(md->curlen == block_size) { - if(sha256_compress(md, md->buf) < 0) - return -1; - md->length += 8 * block_size; - md->curlen = 0; - } - } - } - - return 0; -} - -/* - Terminate the hash to get the digest - @param md The hash state - @param out [out] The destination of the hash (32 bytes) - @return 0 if successful -*/ -static int my_sha256_final(unsigned char *out, - struct sha256_state *md) -{ - int i; - - if(md->curlen >= sizeof(md->buf)) - return -1; - - /* Increase the length of the message */ - md->length += md->curlen * 8; - - /* Append the '1' bit */ - md->buf[md->curlen++] = (unsigned char)0x80; - - /* If the length is currently above 56 bytes we append zeros - * then compress. Then we can fall back to padding zeros and length - * encoding like normal. - */ - if(md->curlen > 56) { - while(md->curlen < 64) { - md->buf[md->curlen++] = (unsigned char)0; - } - sha256_compress(md, md->buf); - md->curlen = 0; - } - - /* Pad up to 56 bytes of zeroes */ - while(md->curlen < 56) { - md->buf[md->curlen++] = (unsigned char)0; - } - - /* Store length */ - WPA_PUT_BE64(md->buf + 56, md->length); - sha256_compress(md, md->buf); - - /* Copy output */ - for(i = 0; i < 8; i++) - WPA_PUT_BE32(out + (4 * i), md->state[i]); - - return 0; -} - -#endif /* CRYPTO LIBS */ - -/* - * Curl_sha256it() - * - * Generates a SHA256 hash for the given input data. - * - * Parameters: - * - * output [in/out] - The output buffer. - * input [in] - The input data. - * length [in] - The input length. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_sha256it(unsigned char *output, const unsigned char *input, - const size_t length) -{ - CURLcode result; - my_sha256_ctx ctx; - - result = my_sha256_init(&ctx); - if(!result) { - my_sha256_update(&ctx, input, curlx_uztoui(length)); - my_sha256_final(output, &ctx); - } - return result; -} - - -const struct HMAC_params Curl_HMAC_SHA256[] = { - { - /* Hash initialization function. */ - CURLX_FUNCTION_CAST(HMAC_hinit_func, my_sha256_init), - /* Hash update function. */ - CURLX_FUNCTION_CAST(HMAC_hupdate_func, my_sha256_update), - /* Hash computation end function. */ - CURLX_FUNCTION_CAST(HMAC_hfinal_func, my_sha256_final), - /* Size of hash context structure. */ - sizeof(my_sha256_ctx), - /* Maximum key length. */ - 64, - /* Result size. */ - 32 - } -}; - - -#endif /* CURL_DISABLE_CRYPTO_AUTH */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2017, Florin Petriuc, <petriuc.florin@gmail.com>
+ * Copyright (C) 2018 - 2022, 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_CRYPTO_AUTH
+
+#include "warnless.h"
+#include "curl_sha256.h"
+#include "curl_hmac.h"
+
+#ifdef USE_WOLFSSL
+#include <wolfssl/options.h>
+#ifndef NO_SHA256
+#define USE_OPENSSL_SHA256
+#endif
+#endif
+
+#if defined(USE_OPENSSL)
+
+#include <openssl/opensslv.h>
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL)
+#define USE_OPENSSL_SHA256
+#endif
+
+#endif /* USE_OPENSSL */
+
+#ifdef USE_MBEDTLS
+#include <mbedtls/version.h>
+
+#if(MBEDTLS_VERSION_NUMBER >= 0x02070000) && \
+ (MBEDTLS_VERSION_NUMBER < 0x03000000)
+ #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS
+#endif
+#endif /* USE_MBEDTLS */
+
+#if defined(USE_OPENSSL_SHA256)
+
+/* When OpenSSL or wolfSSL is available is available we use their
+ * SHA256-functions.
+ */
+#if defined(USE_OPENSSL)
+#include <openssl/evp.h>
+#elif defined(USE_WOLFSSL)
+#include <wolfssl/openssl/evp.h>
+#endif
+
+#elif defined(USE_GNUTLS)
+#include <nettle/sha.h>
+#elif defined(USE_MBEDTLS)
+#include <mbedtls/sha256.h>
+#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \
+ (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040)) || \
+ (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \
+ (__IPHONE_OS_VERSION_MAX_ALLOWED >= 20000))
+#include <CommonCrypto/CommonDigest.h>
+#define AN_APPLE_OS
+#elif defined(USE_WIN32_CRYPTO)
+#include <wincrypt.h>
+#endif
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* Please keep the SSL backend-specific #if branches in this order:
+ *
+ * 1. USE_OPENSSL
+ * 2. USE_GNUTLS
+ * 3. USE_MBEDTLS
+ * 4. USE_COMMON_CRYPTO
+ * 5. 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.
+ */
+
+#if defined(USE_OPENSSL_SHA256)
+
+struct sha256_ctx {
+ EVP_MD_CTX *openssl_ctx;
+};
+typedef struct sha256_ctx my_sha256_ctx;
+
+static CURLcode my_sha256_init(my_sha256_ctx *ctx)
+{
+ ctx->openssl_ctx = EVP_MD_CTX_create();
+ if(!ctx->openssl_ctx)
+ return CURLE_OUT_OF_MEMORY;
+
+ EVP_DigestInit_ex(ctx->openssl_ctx, EVP_sha256(), NULL);
+ return CURLE_OK;
+}
+
+static void my_sha256_update(my_sha256_ctx *ctx,
+ const unsigned char *data,
+ unsigned int length)
+{
+ EVP_DigestUpdate(ctx->openssl_ctx, data, length);
+}
+
+static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx)
+{
+ EVP_DigestFinal_ex(ctx->openssl_ctx, digest, NULL);
+ EVP_MD_CTX_destroy(ctx->openssl_ctx);
+}
+
+#elif defined(USE_GNUTLS)
+
+typedef struct sha256_ctx my_sha256_ctx;
+
+static CURLcode my_sha256_init(my_sha256_ctx *ctx)
+{
+ sha256_init(ctx);
+ return CURLE_OK;
+}
+
+static void my_sha256_update(my_sha256_ctx *ctx,
+ const unsigned char *data,
+ unsigned int length)
+{
+ sha256_update(ctx, length, data);
+}
+
+static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx)
+{
+ sha256_digest(ctx, SHA256_DIGEST_SIZE, digest);
+}
+
+#elif defined(USE_MBEDTLS)
+
+typedef mbedtls_sha256_context my_sha256_ctx;
+
+static CURLcode my_sha256_init(my_sha256_ctx *ctx)
+{
+#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
+ (void) mbedtls_sha256_starts(ctx, 0);
+#else
+ (void) mbedtls_sha256_starts_ret(ctx, 0);
+#endif
+ return CURLE_OK;
+}
+
+static void my_sha256_update(my_sha256_ctx *ctx,
+ const unsigned char *data,
+ unsigned int length)
+{
+#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
+ (void) mbedtls_sha256_update(ctx, data, length);
+#else
+ (void) mbedtls_sha256_update_ret(ctx, data, length);
+#endif
+}
+
+static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx)
+{
+#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
+ (void) mbedtls_sha256_finish(ctx, digest);
+#else
+ (void) mbedtls_sha256_finish_ret(ctx, digest);
+#endif
+}
+
+#elif defined(AN_APPLE_OS)
+typedef CC_SHA256_CTX my_sha256_ctx;
+
+static CURLcode my_sha256_init(my_sha256_ctx *ctx)
+{
+ (void) CC_SHA256_Init(ctx);
+ return CURLE_OK;
+}
+
+static void my_sha256_update(my_sha256_ctx *ctx,
+ const unsigned char *data,
+ unsigned int length)
+{
+ (void) CC_SHA256_Update(ctx, data, length);
+}
+
+static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx)
+{
+ (void) CC_SHA256_Final(digest, ctx);
+}
+
+#elif defined(USE_WIN32_CRYPTO)
+
+struct sha256_ctx {
+ HCRYPTPROV hCryptProv;
+ HCRYPTHASH hHash;
+};
+typedef struct sha256_ctx my_sha256_ctx;
+
+#if !defined(CALG_SHA_256)
+#define CALG_SHA_256 0x0000800c
+#endif
+
+static CURLcode my_sha256_init(my_sha256_ctx *ctx)
+{
+ if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_AES,
+ CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
+ CryptCreateHash(ctx->hCryptProv, CALG_SHA_256, 0, 0, &ctx->hHash);
+ }
+
+ return CURLE_OK;
+}
+
+static void my_sha256_update(my_sha256_ctx *ctx,
+ const unsigned char *data,
+ unsigned int length)
+{
+ CryptHashData(ctx->hHash, (unsigned char *) data, length, 0);
+}
+
+static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx)
+{
+ unsigned long length = 0;
+
+ CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0);
+ if(length == SHA256_DIGEST_LENGTH)
+ CryptGetHashParam(ctx->hHash, HP_HASHVAL, digest, &length, 0);
+
+ if(ctx->hHash)
+ CryptDestroyHash(ctx->hHash);
+
+ if(ctx->hCryptProv)
+ CryptReleaseContext(ctx->hCryptProv, 0);
+}
+
+#else
+
+/* When no other crypto library is available we use this code segment */
+
+/* This is based on SHA256 implementation in LibTomCrypt that was released into
+ * public domain by Tom St Denis. */
+
+#define WPA_GET_BE32(a) ((((unsigned long)(a)[0]) << 24) | \
+ (((unsigned long)(a)[1]) << 16) | \
+ (((unsigned long)(a)[2]) << 8) | \
+ ((unsigned long)(a)[3]))
+#define WPA_PUT_BE32(a, val) \
+do { \
+ (a)[0] = (unsigned char)((((unsigned long) (val)) >> 24) & 0xff); \
+ (a)[1] = (unsigned char)((((unsigned long) (val)) >> 16) & 0xff); \
+ (a)[2] = (unsigned char)((((unsigned long) (val)) >> 8) & 0xff); \
+ (a)[3] = (unsigned char)(((unsigned long) (val)) & 0xff); \
+} while(0)
+
+#ifdef HAVE_LONGLONG
+#define WPA_PUT_BE64(a, val) \
+do { \
+ (a)[0] = (unsigned char)(((unsigned long long)(val)) >> 56); \
+ (a)[1] = (unsigned char)(((unsigned long long)(val)) >> 48); \
+ (a)[2] = (unsigned char)(((unsigned long long)(val)) >> 40); \
+ (a)[3] = (unsigned char)(((unsigned long long)(val)) >> 32); \
+ (a)[4] = (unsigned char)(((unsigned long long)(val)) >> 24); \
+ (a)[5] = (unsigned char)(((unsigned long long)(val)) >> 16); \
+ (a)[6] = (unsigned char)(((unsigned long long)(val)) >> 8); \
+ (a)[7] = (unsigned char)(((unsigned long long)(val)) & 0xff); \
+} while(0)
+#else
+#define WPA_PUT_BE64(a, val) \
+do { \
+ (a)[0] = (unsigned char)(((unsigned __int64)(val)) >> 56); \
+ (a)[1] = (unsigned char)(((unsigned __int64)(val)) >> 48); \
+ (a)[2] = (unsigned char)(((unsigned __int64)(val)) >> 40); \
+ (a)[3] = (unsigned char)(((unsigned __int64)(val)) >> 32); \
+ (a)[4] = (unsigned char)(((unsigned __int64)(val)) >> 24); \
+ (a)[5] = (unsigned char)(((unsigned __int64)(val)) >> 16); \
+ (a)[6] = (unsigned char)(((unsigned __int64)(val)) >> 8); \
+ (a)[7] = (unsigned char)(((unsigned __int64)(val)) & 0xff); \
+} while(0)
+#endif
+
+struct sha256_state {
+#ifdef HAVE_LONGLONG
+ unsigned long long length;
+#else
+ unsigned __int64 length;
+#endif
+ unsigned long state[8], curlen;
+ unsigned char buf[64];
+};
+typedef struct sha256_state my_sha256_ctx;
+
+/* The K array */
+static const unsigned long K[64] = {
+ 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
+ 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
+ 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
+ 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+ 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
+ 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
+ 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
+ 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+ 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
+ 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
+ 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
+ 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+ 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+/* Various logical functions */
+#define RORc(x, y) \
+(((((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)((y) & 31)) | \
+ ((unsigned long)(x) << (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL)
+#define Ch(x,y,z) (z ^ (x & (y ^ z)))
+#define Maj(x,y,z) (((x | y) & z) | (x & y))
+#define S(x, n) RORc((x), (n))
+#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n))
+#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22))
+#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25))
+#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3))
+#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10))
+
+/* Compress 512-bits */
+static int sha256_compress(struct sha256_state *md,
+ unsigned char *buf)
+{
+ unsigned long S[8], W[64];
+ int i;
+
+ /* Copy state into S */
+ for(i = 0; i < 8; i++) {
+ S[i] = md->state[i];
+ }
+ /* copy the state into 512-bits into W[0..15] */
+ for(i = 0; i < 16; i++)
+ W[i] = WPA_GET_BE32(buf + (4 * i));
+ /* fill W[16..63] */
+ for(i = 16; i < 64; i++) {
+ W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) +
+ W[i - 16];
+ }
+
+ /* Compress */
+#define RND(a,b,c,d,e,f,g,h,i) \
+ do { \
+ unsigned long t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \
+ unsigned long t1 = Sigma0(a) + Maj(a, b, c); \
+ d += t0; \
+ h = t0 + t1; \
+ } while(0)
+
+ for(i = 0; i < 64; ++i) {
+ unsigned long t;
+ RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i);
+ t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4];
+ S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t;
+ }
+
+ /* Feedback */
+ for(i = 0; i < 8; i++) {
+ md->state[i] = md->state[i] + S[i];
+ }
+
+ return 0;
+}
+
+/* Initialize the hash state */
+static CURLcode my_sha256_init(struct sha256_state *md)
+{
+ md->curlen = 0;
+ md->length = 0;
+ md->state[0] = 0x6A09E667UL;
+ md->state[1] = 0xBB67AE85UL;
+ md->state[2] = 0x3C6EF372UL;
+ md->state[3] = 0xA54FF53AUL;
+ md->state[4] = 0x510E527FUL;
+ md->state[5] = 0x9B05688CUL;
+ md->state[6] = 0x1F83D9ABUL;
+ md->state[7] = 0x5BE0CD19UL;
+
+ return CURLE_OK;
+}
+
+/*
+ Process a block of memory though the hash
+ @param md The hash state
+ @param in The data to hash
+ @param inlen The length of the data (octets)
+ @return 0 if successful
+*/
+static int my_sha256_update(struct sha256_state *md,
+ const unsigned char *in,
+ unsigned long inlen)
+{
+ unsigned long n;
+
+#define block_size 64
+ if(md->curlen > sizeof(md->buf))
+ return -1;
+ while(inlen > 0) {
+ if(md->curlen == 0 && inlen >= block_size) {
+ if(sha256_compress(md, (unsigned char *)in) < 0)
+ return -1;
+ md->length += block_size * 8;
+ in += block_size;
+ inlen -= block_size;
+ }
+ else {
+ n = CURLMIN(inlen, (block_size - md->curlen));
+ memcpy(md->buf + md->curlen, in, n);
+ md->curlen += n;
+ in += n;
+ inlen -= n;
+ if(md->curlen == block_size) {
+ if(sha256_compress(md, md->buf) < 0)
+ return -1;
+ md->length += 8 * block_size;
+ md->curlen = 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ Terminate the hash to get the digest
+ @param md The hash state
+ @param out [out] The destination of the hash (32 bytes)
+ @return 0 if successful
+*/
+static int my_sha256_final(unsigned char *out,
+ struct sha256_state *md)
+{
+ int i;
+
+ if(md->curlen >= sizeof(md->buf))
+ return -1;
+
+ /* Increase the length of the message */
+ md->length += md->curlen * 8;
+
+ /* Append the '1' bit */
+ md->buf[md->curlen++] = (unsigned char)0x80;
+
+ /* If the length is currently above 56 bytes we append zeros
+ * then compress. Then we can fall back to padding zeros and length
+ * encoding like normal.
+ */
+ if(md->curlen > 56) {
+ while(md->curlen < 64) {
+ md->buf[md->curlen++] = (unsigned char)0;
+ }
+ sha256_compress(md, md->buf);
+ md->curlen = 0;
+ }
+
+ /* Pad up to 56 bytes of zeroes */
+ while(md->curlen < 56) {
+ md->buf[md->curlen++] = (unsigned char)0;
+ }
+
+ /* Store length */
+ WPA_PUT_BE64(md->buf + 56, md->length);
+ sha256_compress(md, md->buf);
+
+ /* Copy output */
+ for(i = 0; i < 8; i++)
+ WPA_PUT_BE32(out + (4 * i), md->state[i]);
+
+ return 0;
+}
+
+#endif /* CRYPTO LIBS */
+
+/*
+ * Curl_sha256it()
+ *
+ * Generates a SHA256 hash for the given input data.
+ *
+ * Parameters:
+ *
+ * output [in/out] - The output buffer.
+ * input [in] - The input data.
+ * length [in] - The input length.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_sha256it(unsigned char *output, const unsigned char *input,
+ const size_t length)
+{
+ CURLcode result;
+ my_sha256_ctx ctx;
+
+ result = my_sha256_init(&ctx);
+ if(!result) {
+ my_sha256_update(&ctx, input, curlx_uztoui(length));
+ my_sha256_final(output, &ctx);
+ }
+ return result;
+}
+
+
+const struct HMAC_params Curl_HMAC_SHA256[] = {
+ {
+ /* Hash initialization function. */
+ CURLX_FUNCTION_CAST(HMAC_hinit_func, my_sha256_init),
+ /* Hash update function. */
+ CURLX_FUNCTION_CAST(HMAC_hupdate_func, my_sha256_update),
+ /* Hash computation end function. */
+ CURLX_FUNCTION_CAST(HMAC_hfinal_func, my_sha256_final),
+ /* Size of hash context structure. */
+ sizeof(my_sha256_ctx),
+ /* Maximum key length. */
+ 64,
+ /* Result size. */
+ 32
+ }
+};
+
+
+#endif /* CURL_DISABLE_CRYPTO_AUTH */
diff --git a/libs/libcurl/src/share.c b/libs/libcurl/src/share.c index 1a083e72a0..119dfa4398 100644 --- a/libs/libcurl/src/share.c +++ b/libs/libcurl/src/share.c @@ -1,262 +1,262 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "share.h" -#include "psl.h" -#include "vtls/vtls.h" -#include "curl_memory.h" - -/* The last #include file should be: */ -#include "memdebug.h" - -struct Curl_share * -curl_share_init(void) -{ - struct Curl_share *share = calloc(1, sizeof(struct Curl_share)); - if(share) { - share->magic = CURL_GOOD_SHARE; - share->specifier |= (1<<CURL_LOCK_DATA_SHARE); - Curl_init_dnscache(&share->hostcache, 23); - } - - return share; -} - -#undef curl_share_setopt -CURLSHcode -curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...) -{ - va_list param; - int type; - curl_lock_function lockfunc; - curl_unlock_function unlockfunc; - void *ptr; - CURLSHcode res = CURLSHE_OK; - - if(!GOOD_SHARE_HANDLE(share)) - return CURLSHE_INVALID; - - if(share->dirty) - /* don't allow setting options while one or more handles are already - using this share */ - return CURLSHE_IN_USE; - - va_start(param, option); - - switch(option) { - case CURLSHOPT_SHARE: - /* this is a type this share will share */ - type = va_arg(param, int); - - switch(type) { - case CURL_LOCK_DATA_DNS: - break; - - case CURL_LOCK_DATA_COOKIE: -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - if(!share->cookies) { - share->cookies = Curl_cookie_init(NULL, NULL, NULL, TRUE); - if(!share->cookies) - res = CURLSHE_NOMEM; - } -#else /* CURL_DISABLE_HTTP */ - res = CURLSHE_NOT_BUILT_IN; -#endif - break; - - case CURL_LOCK_DATA_SSL_SESSION: -#ifdef USE_SSL - if(!share->sslsession) { - share->max_ssl_sessions = 8; - share->sslsession = calloc(share->max_ssl_sessions, - sizeof(struct Curl_ssl_session)); - share->sessionage = 0; - if(!share->sslsession) - res = CURLSHE_NOMEM; - } -#else - res = CURLSHE_NOT_BUILT_IN; -#endif - break; - - case CURL_LOCK_DATA_CONNECT: - if(Curl_conncache_init(&share->conn_cache, 103)) - res = CURLSHE_NOMEM; - break; - - case CURL_LOCK_DATA_PSL: -#ifndef USE_LIBPSL - res = CURLSHE_NOT_BUILT_IN; -#endif - break; - - default: - res = CURLSHE_BAD_OPTION; - } - if(!res) - share->specifier |= (1<<type); - break; - - case CURLSHOPT_UNSHARE: - /* this is a type this share will no longer share */ - type = va_arg(param, int); - share->specifier &= ~(1<<type); - switch(type) { - case CURL_LOCK_DATA_DNS: - break; - - case CURL_LOCK_DATA_COOKIE: -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - if(share->cookies) { - Curl_cookie_cleanup(share->cookies); - share->cookies = NULL; - } -#else /* CURL_DISABLE_HTTP */ - res = CURLSHE_NOT_BUILT_IN; -#endif - break; - - case CURL_LOCK_DATA_SSL_SESSION: -#ifdef USE_SSL - Curl_safefree(share->sslsession); -#else - res = CURLSHE_NOT_BUILT_IN; -#endif - break; - - case CURL_LOCK_DATA_CONNECT: - break; - - default: - res = CURLSHE_BAD_OPTION; - break; - } - break; - - case CURLSHOPT_LOCKFUNC: - lockfunc = va_arg(param, curl_lock_function); - share->lockfunc = lockfunc; - break; - - case CURLSHOPT_UNLOCKFUNC: - unlockfunc = va_arg(param, curl_unlock_function); - share->unlockfunc = unlockfunc; - break; - - case CURLSHOPT_USERDATA: - ptr = va_arg(param, void *); - share->clientdata = ptr; - break; - - default: - res = CURLSHE_BAD_OPTION; - break; - } - - va_end(param); - - return res; -} - -CURLSHcode -curl_share_cleanup(struct Curl_share *share) -{ - if(!GOOD_SHARE_HANDLE(share)) - return CURLSHE_INVALID; - - if(share->lockfunc) - share->lockfunc(NULL, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE, - share->clientdata); - - if(share->dirty) { - if(share->unlockfunc) - share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata); - return CURLSHE_IN_USE; - } - - Curl_conncache_close_all_connections(&share->conn_cache); - Curl_conncache_destroy(&share->conn_cache); - Curl_hash_destroy(&share->hostcache); - -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - Curl_cookie_cleanup(share->cookies); -#endif - -#ifdef USE_SSL - if(share->sslsession) { - size_t i; - for(i = 0; i < share->max_ssl_sessions; i++) - Curl_ssl_kill_session(&(share->sslsession[i])); - free(share->sslsession); - } -#endif - - Curl_psl_destroy(&share->psl); - - if(share->unlockfunc) - share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata); - share->magic = 0; - free(share); - - return CURLSHE_OK; -} - - -CURLSHcode -Curl_share_lock(struct Curl_easy *data, curl_lock_data type, - curl_lock_access accesstype) -{ - struct Curl_share *share = data->share; - - if(!share) - return CURLSHE_INVALID; - - if(share->specifier & (1<<type)) { - if(share->lockfunc) /* only call this if set! */ - share->lockfunc(data, type, accesstype, share->clientdata); - } - /* else if we don't share this, pretend successful lock */ - - return CURLSHE_OK; -} - -CURLSHcode -Curl_share_unlock(struct Curl_easy *data, curl_lock_data type) -{ - struct Curl_share *share = data->share; - - if(!share) - return CURLSHE_INVALID; - - if(share->specifier & (1<<type)) { - if(share->unlockfunc) /* only call this if set! */ - share->unlockfunc (data, type, share->clientdata); - } - - return CURLSHE_OK; -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "share.h"
+#include "psl.h"
+#include "vtls/vtls.h"
+#include "curl_memory.h"
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+struct Curl_share *
+curl_share_init(void)
+{
+ struct Curl_share *share = calloc(1, sizeof(struct Curl_share));
+ if(share) {
+ share->magic = CURL_GOOD_SHARE;
+ share->specifier |= (1<<CURL_LOCK_DATA_SHARE);
+ Curl_init_dnscache(&share->hostcache, 23);
+ }
+
+ return share;
+}
+
+#undef curl_share_setopt
+CURLSHcode
+curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...)
+{
+ va_list param;
+ int type;
+ curl_lock_function lockfunc;
+ curl_unlock_function unlockfunc;
+ void *ptr;
+ CURLSHcode res = CURLSHE_OK;
+
+ if(!GOOD_SHARE_HANDLE(share))
+ return CURLSHE_INVALID;
+
+ if(share->dirty)
+ /* don't allow setting options while one or more handles are already
+ using this share */
+ return CURLSHE_IN_USE;
+
+ va_start(param, option);
+
+ switch(option) {
+ case CURLSHOPT_SHARE:
+ /* this is a type this share will share */
+ type = va_arg(param, int);
+
+ switch(type) {
+ case CURL_LOCK_DATA_DNS:
+ break;
+
+ case CURL_LOCK_DATA_COOKIE:
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+ if(!share->cookies) {
+ share->cookies = Curl_cookie_init(NULL, NULL, NULL, TRUE);
+ if(!share->cookies)
+ res = CURLSHE_NOMEM;
+ }
+#else /* CURL_DISABLE_HTTP */
+ res = CURLSHE_NOT_BUILT_IN;
+#endif
+ break;
+
+ case CURL_LOCK_DATA_SSL_SESSION:
+#ifdef USE_SSL
+ if(!share->sslsession) {
+ share->max_ssl_sessions = 8;
+ share->sslsession = calloc(share->max_ssl_sessions,
+ sizeof(struct Curl_ssl_session));
+ share->sessionage = 0;
+ if(!share->sslsession)
+ res = CURLSHE_NOMEM;
+ }
+#else
+ res = CURLSHE_NOT_BUILT_IN;
+#endif
+ break;
+
+ case CURL_LOCK_DATA_CONNECT:
+ if(Curl_conncache_init(&share->conn_cache, 103))
+ res = CURLSHE_NOMEM;
+ break;
+
+ case CURL_LOCK_DATA_PSL:
+#ifndef USE_LIBPSL
+ res = CURLSHE_NOT_BUILT_IN;
+#endif
+ break;
+
+ default:
+ res = CURLSHE_BAD_OPTION;
+ }
+ if(!res)
+ share->specifier |= (1<<type);
+ break;
+
+ case CURLSHOPT_UNSHARE:
+ /* this is a type this share will no longer share */
+ type = va_arg(param, int);
+ share->specifier &= ~(1<<type);
+ switch(type) {
+ case CURL_LOCK_DATA_DNS:
+ break;
+
+ case CURL_LOCK_DATA_COOKIE:
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+ if(share->cookies) {
+ Curl_cookie_cleanup(share->cookies);
+ share->cookies = NULL;
+ }
+#else /* CURL_DISABLE_HTTP */
+ res = CURLSHE_NOT_BUILT_IN;
+#endif
+ break;
+
+ case CURL_LOCK_DATA_SSL_SESSION:
+#ifdef USE_SSL
+ Curl_safefree(share->sslsession);
+#else
+ res = CURLSHE_NOT_BUILT_IN;
+#endif
+ break;
+
+ case CURL_LOCK_DATA_CONNECT:
+ break;
+
+ default:
+ res = CURLSHE_BAD_OPTION;
+ break;
+ }
+ break;
+
+ case CURLSHOPT_LOCKFUNC:
+ lockfunc = va_arg(param, curl_lock_function);
+ share->lockfunc = lockfunc;
+ break;
+
+ case CURLSHOPT_UNLOCKFUNC:
+ unlockfunc = va_arg(param, curl_unlock_function);
+ share->unlockfunc = unlockfunc;
+ break;
+
+ case CURLSHOPT_USERDATA:
+ ptr = va_arg(param, void *);
+ share->clientdata = ptr;
+ break;
+
+ default:
+ res = CURLSHE_BAD_OPTION;
+ break;
+ }
+
+ va_end(param);
+
+ return res;
+}
+
+CURLSHcode
+curl_share_cleanup(struct Curl_share *share)
+{
+ if(!GOOD_SHARE_HANDLE(share))
+ return CURLSHE_INVALID;
+
+ if(share->lockfunc)
+ share->lockfunc(NULL, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE,
+ share->clientdata);
+
+ if(share->dirty) {
+ if(share->unlockfunc)
+ share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata);
+ return CURLSHE_IN_USE;
+ }
+
+ Curl_conncache_close_all_connections(&share->conn_cache);
+ Curl_conncache_destroy(&share->conn_cache);
+ Curl_hash_destroy(&share->hostcache);
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+ Curl_cookie_cleanup(share->cookies);
+#endif
+
+#ifdef USE_SSL
+ if(share->sslsession) {
+ size_t i;
+ for(i = 0; i < share->max_ssl_sessions; i++)
+ Curl_ssl_kill_session(&(share->sslsession[i]));
+ free(share->sslsession);
+ }
+#endif
+
+ Curl_psl_destroy(&share->psl);
+
+ if(share->unlockfunc)
+ share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata);
+ share->magic = 0;
+ free(share);
+
+ return CURLSHE_OK;
+}
+
+
+CURLSHcode
+Curl_share_lock(struct Curl_easy *data, curl_lock_data type,
+ curl_lock_access accesstype)
+{
+ struct Curl_share *share = data->share;
+
+ if(!share)
+ return CURLSHE_INVALID;
+
+ if(share->specifier & (1<<type)) {
+ if(share->lockfunc) /* only call this if set! */
+ share->lockfunc(data, type, accesstype, share->clientdata);
+ }
+ /* else if we don't share this, pretend successful lock */
+
+ return CURLSHE_OK;
+}
+
+CURLSHcode
+Curl_share_unlock(struct Curl_easy *data, curl_lock_data type)
+{
+ struct Curl_share *share = data->share;
+
+ if(!share)
+ return CURLSHE_INVALID;
+
+ if(share->specifier & (1<<type)) {
+ if(share->unlockfunc) /* only call this if set! */
+ share->unlockfunc (data, type, share->clientdata);
+ }
+
+ return CURLSHE_OK;
+}
diff --git a/libs/libcurl/src/share.h b/libs/libcurl/src/share.h index 32be41691a..5bcdce0215 100644 --- a/libs/libcurl/src/share.h +++ b/libs/libcurl/src/share.h @@ -1,72 +1,72 @@ -#ifndef HEADER_CURL_SHARE_H -#define HEADER_CURL_SHARE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "cookie.h" -#include "psl.h" -#include "urldata.h" -#include "conncache.h" - -/* SalfordC says "A structure member may not be volatile". Hence: - */ -#ifdef __SALFORDC__ -#define CURL_VOLATILE -#else -#define CURL_VOLATILE volatile -#endif - -#define CURL_GOOD_SHARE 0x7e117a1e -#define GOOD_SHARE_HANDLE(x) ((x) && (x)->magic == CURL_GOOD_SHARE) - -/* this struct is libcurl-private, don't export details */ -struct Curl_share { - unsigned int magic; /* CURL_GOOD_SHARE */ - unsigned int specifier; - CURL_VOLATILE unsigned int dirty; - - curl_lock_function lockfunc; - curl_unlock_function unlockfunc; - void *clientdata; - struct conncache conn_cache; - struct Curl_hash hostcache; -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - struct CookieInfo *cookies; -#endif -#ifdef USE_LIBPSL - struct PslCache psl; -#endif - - struct Curl_ssl_session *sslsession; - size_t max_ssl_sessions; - long sessionage; -}; - -CURLSHcode Curl_share_lock(struct Curl_easy *, curl_lock_data, - curl_lock_access); -CURLSHcode Curl_share_unlock(struct Curl_easy *, curl_lock_data); - -#endif /* HEADER_CURL_SHARE_H */ +#ifndef HEADER_CURL_SHARE_H
+#define HEADER_CURL_SHARE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "cookie.h"
+#include "psl.h"
+#include "urldata.h"
+#include "conncache.h"
+
+/* SalfordC says "A structure member may not be volatile". Hence:
+ */
+#ifdef __SALFORDC__
+#define CURL_VOLATILE
+#else
+#define CURL_VOLATILE volatile
+#endif
+
+#define CURL_GOOD_SHARE 0x7e117a1e
+#define GOOD_SHARE_HANDLE(x) ((x) && (x)->magic == CURL_GOOD_SHARE)
+
+/* this struct is libcurl-private, don't export details */
+struct Curl_share {
+ unsigned int magic; /* CURL_GOOD_SHARE */
+ unsigned int specifier;
+ CURL_VOLATILE unsigned int dirty;
+
+ curl_lock_function lockfunc;
+ curl_unlock_function unlockfunc;
+ void *clientdata;
+ struct conncache conn_cache;
+ struct Curl_hash hostcache;
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+ struct CookieInfo *cookies;
+#endif
+#ifdef USE_LIBPSL
+ struct PslCache psl;
+#endif
+
+ struct Curl_ssl_session *sslsession;
+ size_t max_ssl_sessions;
+ long sessionage;
+};
+
+CURLSHcode Curl_share_lock(struct Curl_easy *, curl_lock_data,
+ curl_lock_access);
+CURLSHcode Curl_share_unlock(struct Curl_easy *, curl_lock_data);
+
+#endif /* HEADER_CURL_SHARE_H */
diff --git a/libs/libcurl/src/sigpipe.h b/libs/libcurl/src/sigpipe.h index d12b31764d..9159280976 100644 --- a/libs/libcurl/src/sigpipe.h +++ b/libs/libcurl/src/sigpipe.h @@ -1,81 +1,81 @@ -#ifndef HEADER_CURL_SIGPIPE_H -#define HEADER_CURL_SIGPIPE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_SIGNAL_H) && defined(HAVE_SIGACTION) && \ - (defined(USE_OPENSSL) || defined(USE_MBEDTLS) || defined(USE_WOLFSSL)) -#include <signal.h> - -struct sigpipe_ignore { - struct sigaction old_pipe_act; - bool no_signal; -}; - -#define SIGPIPE_VARIABLE(x) struct sigpipe_ignore x - -/* - * sigpipe_ignore() makes sure we ignore SIGPIPE while running libcurl - * internals, and then sigpipe_restore() will restore the situation when we - * return from libcurl again. - */ -static void sigpipe_ignore(struct Curl_easy *data, - struct sigpipe_ignore *ig) -{ - /* get a local copy of no_signal because the Curl_easy might not be - around when we restore */ - ig->no_signal = data->set.no_signal; - if(!data->set.no_signal) { - struct sigaction action; - /* first, extract the existing situation */ - memset(&ig->old_pipe_act, 0, sizeof(struct sigaction)); - sigaction(SIGPIPE, NULL, &ig->old_pipe_act); - action = ig->old_pipe_act; - /* ignore this signal */ - action.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &action, NULL); - } -} - -/* - * sigpipe_restore() puts back the outside world's opinion of signal handler - * and SIGPIPE handling. It MUST only be called after a corresponding - * sigpipe_ignore() was used. - */ -static void sigpipe_restore(struct sigpipe_ignore *ig) -{ - if(!ig->no_signal) - /* restore the outside state */ - sigaction(SIGPIPE, &ig->old_pipe_act, NULL); -} - -#else -/* for systems without sigaction */ -#define sigpipe_ignore(x,y) Curl_nop_stmt -#define sigpipe_restore(x) Curl_nop_stmt -#define SIGPIPE_VARIABLE(x) -#endif - -#endif /* HEADER_CURL_SIGPIPE_H */ +#ifndef HEADER_CURL_SIGPIPE_H
+#define HEADER_CURL_SIGPIPE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_SIGNAL_H) && defined(HAVE_SIGACTION) && \
+ (defined(USE_OPENSSL) || defined(USE_MBEDTLS) || defined(USE_WOLFSSL))
+#include <signal.h>
+
+struct sigpipe_ignore {
+ struct sigaction old_pipe_act;
+ bool no_signal;
+};
+
+#define SIGPIPE_VARIABLE(x) struct sigpipe_ignore x
+
+/*
+ * sigpipe_ignore() makes sure we ignore SIGPIPE while running libcurl
+ * internals, and then sigpipe_restore() will restore the situation when we
+ * return from libcurl again.
+ */
+static void sigpipe_ignore(struct Curl_easy *data,
+ struct sigpipe_ignore *ig)
+{
+ /* get a local copy of no_signal because the Curl_easy might not be
+ around when we restore */
+ ig->no_signal = data->set.no_signal;
+ if(!data->set.no_signal) {
+ struct sigaction action;
+ /* first, extract the existing situation */
+ memset(&ig->old_pipe_act, 0, sizeof(struct sigaction));
+ sigaction(SIGPIPE, NULL, &ig->old_pipe_act);
+ action = ig->old_pipe_act;
+ /* ignore this signal */
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &action, NULL);
+ }
+}
+
+/*
+ * sigpipe_restore() puts back the outside world's opinion of signal handler
+ * and SIGPIPE handling. It MUST only be called after a corresponding
+ * sigpipe_ignore() was used.
+ */
+static void sigpipe_restore(struct sigpipe_ignore *ig)
+{
+ if(!ig->no_signal)
+ /* restore the outside state */
+ sigaction(SIGPIPE, &ig->old_pipe_act, NULL);
+}
+
+#else
+/* for systems without sigaction */
+#define sigpipe_ignore(x,y) Curl_nop_stmt
+#define sigpipe_restore(x) Curl_nop_stmt
+#define SIGPIPE_VARIABLE(x)
+#endif
+
+#endif /* HEADER_CURL_SIGPIPE_H */
diff --git a/libs/libcurl/src/slist.c b/libs/libcurl/src/slist.c index 6c80722c77..da79c89afe 100644 --- a/libs/libcurl/src/slist.c +++ b/libs/libcurl/src/slist.c @@ -1,146 +1,146 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "slist.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* returns last node in linked list */ -static struct curl_slist *slist_get_last(struct curl_slist *list) -{ - struct curl_slist *item; - - /* if caller passed us a NULL, return now */ - if(!list) - return NULL; - - /* loop through to find the last item */ - item = list; - while(item->next) { - item = item->next; - } - return item; -} - -/* - * Curl_slist_append_nodup() appends a string to the linked list. Rather than - * copying the string in dynamic storage, it takes its ownership. The string - * should have been malloc()ated. Curl_slist_append_nodup always returns - * the address of the first record, so that you can use this function as an - * initialization function as well as an append function. - * If an error occurs, NULL is returned and the string argument is NOT - * released. - */ -struct curl_slist *Curl_slist_append_nodup(struct curl_slist *list, char *data) -{ - struct curl_slist *last; - struct curl_slist *new_item; - - DEBUGASSERT(data); - - new_item = malloc(sizeof(struct curl_slist)); - if(!new_item) - return NULL; - - new_item->next = NULL; - new_item->data = data; - - /* if this is the first item, then new_item *is* the list */ - if(!list) - return new_item; - - last = slist_get_last(list); - last->next = new_item; - return list; -} - -/* - * curl_slist_append() appends a string to the linked list. It always returns - * the address of the first record, so that you can use this function as an - * initialization function as well as an append function. If you find this - * bothersome, then simply create a separate _init function and call it - * appropriately from within the program. - */ -struct curl_slist *curl_slist_append(struct curl_slist *list, - const char *data) -{ - char *dupdata = strdup(data); - - if(!dupdata) - return NULL; - - list = Curl_slist_append_nodup(list, dupdata); - if(!list) - free(dupdata); - - return list; -} - -/* - * Curl_slist_duplicate() duplicates a linked list. It always returns the - * address of the first record of the cloned list or NULL in case of an - * error (or if the input list was NULL). - */ -struct curl_slist *Curl_slist_duplicate(struct curl_slist *inlist) -{ - struct curl_slist *outlist = NULL; - struct curl_slist *tmp; - - while(inlist) { - tmp = curl_slist_append(outlist, inlist->data); - - if(!tmp) { - curl_slist_free_all(outlist); - return NULL; - } - - outlist = tmp; - inlist = inlist->next; - } - return outlist; -} - -/* be nice and clean up resources */ -void curl_slist_free_all(struct curl_slist *list) -{ - struct curl_slist *next; - struct curl_slist *item; - - if(!list) - return; - - item = list; - do { - next = item->next; - Curl_safefree(item->data); - free(item); - item = next; - } while(next); -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "slist.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* returns last node in linked list */
+static struct curl_slist *slist_get_last(struct curl_slist *list)
+{
+ struct curl_slist *item;
+
+ /* if caller passed us a NULL, return now */
+ if(!list)
+ return NULL;
+
+ /* loop through to find the last item */
+ item = list;
+ while(item->next) {
+ item = item->next;
+ }
+ return item;
+}
+
+/*
+ * Curl_slist_append_nodup() appends a string to the linked list. Rather than
+ * copying the string in dynamic storage, it takes its ownership. The string
+ * should have been malloc()ated. Curl_slist_append_nodup always returns
+ * the address of the first record, so that you can use this function as an
+ * initialization function as well as an append function.
+ * If an error occurs, NULL is returned and the string argument is NOT
+ * released.
+ */
+struct curl_slist *Curl_slist_append_nodup(struct curl_slist *list, char *data)
+{
+ struct curl_slist *last;
+ struct curl_slist *new_item;
+
+ DEBUGASSERT(data);
+
+ new_item = malloc(sizeof(struct curl_slist));
+ if(!new_item)
+ return NULL;
+
+ new_item->next = NULL;
+ new_item->data = data;
+
+ /* if this is the first item, then new_item *is* the list */
+ if(!list)
+ return new_item;
+
+ last = slist_get_last(list);
+ last->next = new_item;
+ return list;
+}
+
+/*
+ * curl_slist_append() appends a string to the linked list. It always returns
+ * the address of the first record, so that you can use this function as an
+ * initialization function as well as an append function. If you find this
+ * bothersome, then simply create a separate _init function and call it
+ * appropriately from within the program.
+ */
+struct curl_slist *curl_slist_append(struct curl_slist *list,
+ const char *data)
+{
+ char *dupdata = strdup(data);
+
+ if(!dupdata)
+ return NULL;
+
+ list = Curl_slist_append_nodup(list, dupdata);
+ if(!list)
+ free(dupdata);
+
+ return list;
+}
+
+/*
+ * Curl_slist_duplicate() duplicates a linked list. It always returns the
+ * address of the first record of the cloned list or NULL in case of an
+ * error (or if the input list was NULL).
+ */
+struct curl_slist *Curl_slist_duplicate(struct curl_slist *inlist)
+{
+ struct curl_slist *outlist = NULL;
+ struct curl_slist *tmp;
+
+ while(inlist) {
+ tmp = curl_slist_append(outlist, inlist->data);
+
+ if(!tmp) {
+ curl_slist_free_all(outlist);
+ return NULL;
+ }
+
+ outlist = tmp;
+ inlist = inlist->next;
+ }
+ return outlist;
+}
+
+/* be nice and clean up resources */
+void curl_slist_free_all(struct curl_slist *list)
+{
+ struct curl_slist *next;
+ struct curl_slist *item;
+
+ if(!list)
+ return;
+
+ item = list;
+ do {
+ next = item->next;
+ Curl_safefree(item->data);
+ free(item);
+ item = next;
+ } while(next);
+}
diff --git a/libs/libcurl/src/slist.h b/libs/libcurl/src/slist.h index 4e5834c90a..3e015655ea 100644 --- a/libs/libcurl/src/slist.h +++ b/libs/libcurl/src/slist.h @@ -1,41 +1,41 @@ -#ifndef HEADER_CURL_SLIST_H -#define HEADER_CURL_SLIST_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - -/* - * Curl_slist_duplicate() duplicates a linked list. It always returns the - * address of the first record of the cloned list or NULL in case of an - * error (or if the input list was NULL). - */ -struct curl_slist *Curl_slist_duplicate(struct curl_slist *inlist); - -/* - * Curl_slist_append_nodup() takes ownership of the given string and appends - * it to the list. - */ -struct curl_slist *Curl_slist_append_nodup(struct curl_slist *list, - char *data); - -#endif /* HEADER_CURL_SLIST_H */ +#ifndef HEADER_CURL_SLIST_H
+#define HEADER_CURL_SLIST_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+/*
+ * Curl_slist_duplicate() duplicates a linked list. It always returns the
+ * address of the first record of the cloned list or NULL in case of an
+ * error (or if the input list was NULL).
+ */
+struct curl_slist *Curl_slist_duplicate(struct curl_slist *inlist);
+
+/*
+ * Curl_slist_append_nodup() takes ownership of the given string and appends
+ * it to the list.
+ */
+struct curl_slist *Curl_slist_append_nodup(struct curl_slist *list,
+ char *data);
+
+#endif /* HEADER_CURL_SLIST_H */
diff --git a/libs/libcurl/src/smb.c b/libs/libcurl/src/smb.c index a62e858143..2084ed26c9 100644 --- a/libs/libcurl/src/smb.c +++ b/libs/libcurl/src/smb.c @@ -1,1027 +1,1012 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2016 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2014, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies - * - * 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_SMB) && defined(USE_CURL_NTLM_CORE) && \ - (SIZEOF_CURL_OFF_T > 4) - -#define BUILDING_CURL_SMB_C - -#ifdef HAVE_PROCESS_H -#include <process.h> -#ifdef CURL_WINDOWS_APP -#define getpid GetCurrentProcessId -#elif defined(WIN32) -#define getpid _getpid -#endif -#endif - -#include "smb.h" -#include "urldata.h" -#include "sendf.h" -#include "multiif.h" -#include "connect.h" -#include "progress.h" -#include "transfer.h" -#include "vtls/vtls.h" -#include "curl_ntlm_core.h" -#include "escape.h" -#include "curl_endian.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* Local API functions */ -static CURLcode smb_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode smb_connect(struct Curl_easy *data, bool *done); -static CURLcode smb_connection_state(struct Curl_easy *data, bool *done); -static CURLcode smb_do(struct Curl_easy *data, bool *done); -static CURLcode smb_request_state(struct Curl_easy *data, bool *done); -static CURLcode smb_done(struct Curl_easy *data, CURLcode status, - bool premature); -static CURLcode smb_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead); -static int smb_getsock(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t *socks); -static CURLcode smb_parse_url_path(struct Curl_easy *data, - struct connectdata *conn); - -/* - * SMB handler interface - */ -const struct Curl_handler Curl_handler_smb = { - "SMB", /* scheme */ - smb_setup_connection, /* setup_connection */ - smb_do, /* do_it */ - smb_done, /* done */ - ZERO_NULL, /* do_more */ - smb_connect, /* connect_it */ - smb_connection_state, /* connecting */ - smb_request_state, /* doing */ - smb_getsock, /* proto_getsock */ - smb_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - smb_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_SMB, /* defport */ - CURLPROTO_SMB, /* protocol */ - CURLPROTO_SMB, /* family */ - PROTOPT_NONE /* flags */ -}; - -#ifdef USE_SSL -/* - * SMBS handler interface - */ -const struct Curl_handler Curl_handler_smbs = { - "SMBS", /* scheme */ - smb_setup_connection, /* setup_connection */ - smb_do, /* do_it */ - smb_done, /* done */ - ZERO_NULL, /* do_more */ - smb_connect, /* connect_it */ - smb_connection_state, /* connecting */ - smb_request_state, /* doing */ - smb_getsock, /* proto_getsock */ - smb_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - smb_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_SMBS, /* defport */ - CURLPROTO_SMBS, /* protocol */ - CURLPROTO_SMB, /* family */ - PROTOPT_SSL /* flags */ -}; -#endif - -#define MAX_PAYLOAD_SIZE 0x8000 -#define MAX_MESSAGE_SIZE (MAX_PAYLOAD_SIZE + 0x1000) -#define CLIENTNAME "curl" -#define SERVICENAME "?????" - -/* Append a string to an SMB message */ -#define MSGCAT(str) \ - do { \ - strcpy(p, (str)); \ - p += strlen(str); \ - } while(0) - -/* Append a null-terminated string to an SMB message */ -#define MSGCATNULL(str) \ - do { \ - strcpy(p, (str)); \ - p += strlen(str) + 1; \ - } while(0) - -/* SMB is mostly little endian */ -#if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \ - defined(__OS400__) -static unsigned short smb_swap16(unsigned short x) -{ - return (unsigned short) ((x << 8) | ((x >> 8) & 0xff)); -} - -static unsigned int smb_swap32(unsigned int x) -{ - return (x << 24) | ((x << 8) & 0xff0000) | ((x >> 8) & 0xff00) | - ((x >> 24) & 0xff); -} - -static curl_off_t smb_swap64(curl_off_t x) -{ - return ((curl_off_t) smb_swap32((unsigned int) x) << 32) | - smb_swap32((unsigned int) (x >> 32)); -} - -#else -# define smb_swap16(x) (x) -# define smb_swap32(x) (x) -# define smb_swap64(x) (x) -#endif - -/* SMB request state */ -enum smb_req_state { - SMB_REQUESTING, - SMB_TREE_CONNECT, - SMB_OPEN, - SMB_DOWNLOAD, - SMB_UPLOAD, - SMB_CLOSE, - SMB_TREE_DISCONNECT, - SMB_DONE -}; - -/* SMB request data */ -struct smb_request { - enum smb_req_state state; - char *path; - unsigned short tid; /* Even if we connect to the same tree as another */ - unsigned short fid; /* request, the tid will be different */ - CURLcode result; -}; - -static void conn_state(struct Curl_easy *data, enum smb_conn_state newstate) -{ - struct smb_conn *smbc = &data->conn->proto.smbc; -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - /* For debug purposes */ - static const char * const names[] = { - "SMB_NOT_CONNECTED", - "SMB_CONNECTING", - "SMB_NEGOTIATE", - "SMB_SETUP", - "SMB_CONNECTED", - /* LAST */ - }; - - if(smbc->state != newstate) - infof(data, "SMB conn %p state change from %s to %s", - (void *)smbc, names[smbc->state], names[newstate]); -#endif - - smbc->state = newstate; -} - -static void request_state(struct Curl_easy *data, - enum smb_req_state newstate) -{ - struct smb_request *req = data->req.p.smb; -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - /* For debug purposes */ - static const char * const names[] = { - "SMB_REQUESTING", - "SMB_TREE_CONNECT", - "SMB_OPEN", - "SMB_DOWNLOAD", - "SMB_UPLOAD", - "SMB_CLOSE", - "SMB_TREE_DISCONNECT", - "SMB_DONE", - /* LAST */ - }; - - if(req->state != newstate) - infof(data, "SMB request %p state change from %s to %s", - (void *)req, names[req->state], names[newstate]); -#endif - - req->state = newstate; -} - -/* this should setup things in the connection, not in the easy - handle */ -static CURLcode smb_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - struct smb_request *req; - - /* Initialize the request state */ - data->req.p.smb = req = calloc(1, sizeof(struct smb_request)); - if(!req) - return CURLE_OUT_OF_MEMORY; - - /* Parse the URL path */ - return smb_parse_url_path(data, conn); -} - -static CURLcode smb_connect(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - struct smb_conn *smbc = &conn->proto.smbc; - char *slash; - - (void) done; - - /* Check we have a username and password to authenticate with */ - if(!data->state.aptr.user) - return CURLE_LOGIN_DENIED; - - /* Initialize the connection state */ - smbc->state = SMB_CONNECTING; - smbc->recv_buf = malloc(MAX_MESSAGE_SIZE); - if(!smbc->recv_buf) - return CURLE_OUT_OF_MEMORY; - - /* Multiple requests are allowed with this connection */ - connkeep(conn, "SMB default"); - - /* Parse the username, domain, and password */ - slash = strchr(conn->user, '/'); - if(!slash) - slash = strchr(conn->user, '\\'); - - if(slash) { - smbc->user = slash + 1; - smbc->domain = strdup(conn->user); - if(!smbc->domain) - return CURLE_OUT_OF_MEMORY; - smbc->domain[slash - conn->user] = 0; - } - else { - smbc->user = conn->user; - smbc->domain = strdup(conn->host.name); - if(!smbc->domain) - return CURLE_OUT_OF_MEMORY; - } - - return CURLE_OK; -} - -static CURLcode smb_recv_message(struct Curl_easy *data, void **msg) -{ - struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; - struct smb_conn *smbc = &conn->proto.smbc; - char *buf = smbc->recv_buf; - ssize_t bytes_read; - size_t nbt_size; - size_t msg_size; - size_t len = MAX_MESSAGE_SIZE - smbc->got; - CURLcode result; - - result = Curl_read(data, sockfd, buf + smbc->got, len, &bytes_read); - if(result) - return result; - - if(!bytes_read) - return CURLE_OK; - - smbc->got += bytes_read; - - /* Check for a 32-bit nbt header */ - if(smbc->got < sizeof(unsigned int)) - return CURLE_OK; - - nbt_size = Curl_read16_be((const unsigned char *) - (buf + sizeof(unsigned short))) + - sizeof(unsigned int); - if(smbc->got < nbt_size) - return CURLE_OK; - - msg_size = sizeof(struct smb_header); - if(nbt_size >= msg_size + 1) { - /* Add the word count */ - msg_size += 1 + ((unsigned char) buf[msg_size]) * sizeof(unsigned short); - if(nbt_size >= msg_size + sizeof(unsigned short)) { - /* Add the byte count */ - msg_size += sizeof(unsigned short) + - Curl_read16_le((const unsigned char *)&buf[msg_size]); - if(nbt_size < msg_size) - return CURLE_READ_ERROR; - } - } - - *msg = buf; - - return CURLE_OK; -} - -static void smb_pop_message(struct connectdata *conn) -{ - struct smb_conn *smbc = &conn->proto.smbc; - - smbc->got = 0; -} - -static void smb_format_message(struct Curl_easy *data, struct smb_header *h, - unsigned char cmd, size_t len) -{ - struct connectdata *conn = data->conn; - struct smb_conn *smbc = &conn->proto.smbc; - struct smb_request *req = data->req.p.smb; - unsigned int pid; - - memset(h, 0, sizeof(*h)); - h->nbt_length = htons((unsigned short) (sizeof(*h) - sizeof(unsigned int) + - len)); - memcpy((char *)h->magic, "\xffSMB", 4); - h->command = cmd; - h->flags = SMB_FLAGS_CANONICAL_PATHNAMES | SMB_FLAGS_CASELESS_PATHNAMES; - h->flags2 = smb_swap16(SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_KNOWS_LONG_NAME); - h->uid = smb_swap16(smbc->uid); - h->tid = smb_swap16(req->tid); - pid = getpid(); - h->pid_high = smb_swap16((unsigned short)(pid >> 16)); - h->pid = smb_swap16((unsigned short) pid); -} - -static CURLcode smb_send(struct Curl_easy *data, ssize_t len, - size_t upload_size) -{ - struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; - struct smb_conn *smbc = &conn->proto.smbc; - ssize_t bytes_written; - CURLcode result; - - result = Curl_write(data, sockfd, data->state.ulbuf, - len, &bytes_written); - if(result) - return result; - - if(bytes_written != len) { - smbc->send_size = len; - smbc->sent = bytes_written; - } - - smbc->upload_size = upload_size; - - return CURLE_OK; -} - -static CURLcode smb_flush(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; - struct smb_conn *smbc = &conn->proto.smbc; - ssize_t bytes_written; - ssize_t len = smbc->send_size - smbc->sent; - CURLcode result; - - if(!smbc->send_size) - return CURLE_OK; - - result = Curl_write(data, sockfd, - data->state.ulbuf + smbc->sent, - len, &bytes_written); - if(result) - return result; - - if(bytes_written != len) - smbc->sent += bytes_written; - else - smbc->send_size = 0; - - return CURLE_OK; -} - -static CURLcode smb_send_message(struct Curl_easy *data, unsigned char cmd, - const void *msg, size_t msg_len) -{ - CURLcode result = Curl_get_upload_buffer(data); - if(result) - return result; - smb_format_message(data, (struct smb_header *)data->state.ulbuf, - cmd, msg_len); - memcpy(data->state.ulbuf + sizeof(struct smb_header), - msg, msg_len); - - return smb_send(data, sizeof(struct smb_header) + msg_len, 0); -} - -static CURLcode smb_send_negotiate(struct Curl_easy *data) -{ - const char *msg = "\x00\x0c\x00\x02NT LM 0.12"; - - return smb_send_message(data, SMB_COM_NEGOTIATE, msg, 15); -} - -static CURLcode smb_send_setup(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - struct smb_conn *smbc = &conn->proto.smbc; - struct smb_setup msg; - char *p = msg.bytes; - unsigned char lm_hash[21]; - unsigned char lm[24]; - unsigned char nt_hash[21]; - unsigned char nt[24]; - - size_t byte_count = sizeof(lm) + sizeof(nt); - byte_count += strlen(smbc->user) + strlen(smbc->domain); - byte_count += strlen(OS) + strlen(CLIENTNAME) + 4; /* 4 null chars */ - if(byte_count > sizeof(msg.bytes)) - return CURLE_FILESIZE_EXCEEDED; - - Curl_ntlm_core_mk_lm_hash(conn->passwd, lm_hash); - Curl_ntlm_core_lm_resp(lm_hash, smbc->challenge, lm); - Curl_ntlm_core_mk_nt_hash(conn->passwd, nt_hash); - Curl_ntlm_core_lm_resp(nt_hash, smbc->challenge, nt); - - memset(&msg, 0, sizeof(msg)); - msg.word_count = SMB_WC_SETUP_ANDX; - msg.andx.command = SMB_COM_NO_ANDX_COMMAND; - msg.max_buffer_size = smb_swap16(MAX_MESSAGE_SIZE); - msg.max_mpx_count = smb_swap16(1); - msg.vc_number = smb_swap16(1); - msg.session_key = smb_swap32(smbc->session_key); - msg.capabilities = smb_swap32(SMB_CAP_LARGE_FILES); - msg.lengths[0] = smb_swap16(sizeof(lm)); - msg.lengths[1] = smb_swap16(sizeof(nt)); - memcpy(p, lm, sizeof(lm)); - p += sizeof(lm); - memcpy(p, nt, sizeof(nt)); - p += sizeof(nt); - MSGCATNULL(smbc->user); - MSGCATNULL(smbc->domain); - MSGCATNULL(OS); - MSGCATNULL(CLIENTNAME); - byte_count = p - msg.bytes; - msg.byte_count = smb_swap16((unsigned short)byte_count); - - return smb_send_message(data, SMB_COM_SETUP_ANDX, &msg, - sizeof(msg) - sizeof(msg.bytes) + byte_count); -} - -static CURLcode smb_send_tree_connect(struct Curl_easy *data) -{ - struct smb_tree_connect msg; - struct connectdata *conn = data->conn; - struct smb_conn *smbc = &conn->proto.smbc; - char *p = msg.bytes; - - size_t byte_count = strlen(conn->host.name) + strlen(smbc->share); - byte_count += strlen(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */ - if(byte_count > sizeof(msg.bytes)) - return CURLE_FILESIZE_EXCEEDED; - - memset(&msg, 0, sizeof(msg)); - msg.word_count = SMB_WC_TREE_CONNECT_ANDX; - msg.andx.command = SMB_COM_NO_ANDX_COMMAND; - msg.pw_len = 0; - MSGCAT("\\\\"); - MSGCAT(conn->host.name); - MSGCAT("\\"); - MSGCATNULL(smbc->share); - MSGCATNULL(SERVICENAME); /* Match any type of service */ - byte_count = p - msg.bytes; - msg.byte_count = smb_swap16((unsigned short)byte_count); - - return smb_send_message(data, SMB_COM_TREE_CONNECT_ANDX, &msg, - sizeof(msg) - sizeof(msg.bytes) + byte_count); -} - -static CURLcode smb_send_open(struct Curl_easy *data) -{ - struct smb_request *req = data->req.p.smb; - struct smb_nt_create msg; - size_t byte_count; - - if((strlen(req->path) + 1) > sizeof(msg.bytes)) - return CURLE_FILESIZE_EXCEEDED; - - memset(&msg, 0, sizeof(msg)); - msg.word_count = SMB_WC_NT_CREATE_ANDX; - msg.andx.command = SMB_COM_NO_ANDX_COMMAND; - byte_count = strlen(req->path); - msg.name_length = smb_swap16((unsigned short)byte_count); - msg.share_access = smb_swap32(SMB_FILE_SHARE_ALL); - if(data->set.upload) { - msg.access = smb_swap32(SMB_GENERIC_READ | SMB_GENERIC_WRITE); - msg.create_disposition = smb_swap32(SMB_FILE_OVERWRITE_IF); - } - else { - msg.access = smb_swap32(SMB_GENERIC_READ); - msg.create_disposition = smb_swap32(SMB_FILE_OPEN); - } - msg.byte_count = smb_swap16((unsigned short) ++byte_count); - strcpy(msg.bytes, req->path); - - return smb_send_message(data, SMB_COM_NT_CREATE_ANDX, &msg, - sizeof(msg) - sizeof(msg.bytes) + byte_count); -} - -static CURLcode smb_send_close(struct Curl_easy *data) -{ - struct smb_request *req = data->req.p.smb; - struct smb_close msg; - - memset(&msg, 0, sizeof(msg)); - msg.word_count = SMB_WC_CLOSE; - msg.fid = smb_swap16(req->fid); - - return smb_send_message(data, SMB_COM_CLOSE, &msg, sizeof(msg)); -} - -static CURLcode smb_send_tree_disconnect(struct Curl_easy *data) -{ - struct smb_tree_disconnect msg; - - memset(&msg, 0, sizeof(msg)); - - return smb_send_message(data, SMB_COM_TREE_DISCONNECT, &msg, sizeof(msg)); -} - -static CURLcode smb_send_read(struct Curl_easy *data) -{ - struct smb_request *req = data->req.p.smb; - curl_off_t offset = data->req.offset; - struct smb_read msg; - - memset(&msg, 0, sizeof(msg)); - msg.word_count = SMB_WC_READ_ANDX; - msg.andx.command = SMB_COM_NO_ANDX_COMMAND; - msg.fid = smb_swap16(req->fid); - msg.offset = smb_swap32((unsigned int) offset); - msg.offset_high = smb_swap32((unsigned int) (offset >> 32)); - msg.min_bytes = smb_swap16(MAX_PAYLOAD_SIZE); - msg.max_bytes = smb_swap16(MAX_PAYLOAD_SIZE); - - return smb_send_message(data, SMB_COM_READ_ANDX, &msg, sizeof(msg)); -} - -static CURLcode smb_send_write(struct Curl_easy *data) -{ - struct smb_write *msg; - struct smb_request *req = data->req.p.smb; - curl_off_t offset = data->req.offset; - curl_off_t upload_size = data->req.size - data->req.bytecount; - CURLcode result = Curl_get_upload_buffer(data); - if(result) - return result; - msg = (struct smb_write *)data->state.ulbuf; - - if(upload_size >= MAX_PAYLOAD_SIZE - 1) /* There is one byte of padding */ - upload_size = MAX_PAYLOAD_SIZE - 1; - - memset(msg, 0, sizeof(*msg)); - msg->word_count = SMB_WC_WRITE_ANDX; - msg->andx.command = SMB_COM_NO_ANDX_COMMAND; - msg->fid = smb_swap16(req->fid); - msg->offset = smb_swap32((unsigned int) offset); - msg->offset_high = smb_swap32((unsigned int) (offset >> 32)); - msg->data_length = smb_swap16((unsigned short) upload_size); - msg->data_offset = smb_swap16(sizeof(*msg) - sizeof(unsigned int)); - msg->byte_count = smb_swap16((unsigned short) (upload_size + 1)); - - smb_format_message(data, &msg->h, SMB_COM_WRITE_ANDX, - sizeof(*msg) - sizeof(msg->h) + (size_t) upload_size); - - return smb_send(data, sizeof(*msg), (size_t) upload_size); -} - -static CURLcode smb_send_and_recv(struct Curl_easy *data, void **msg) -{ - struct connectdata *conn = data->conn; - struct smb_conn *smbc = &conn->proto.smbc; - CURLcode result; - *msg = NULL; /* if it returns early */ - - /* Check if there is data in the transfer buffer */ - if(!smbc->send_size && smbc->upload_size) { - size_t nread = smbc->upload_size > (size_t)data->set.upload_buffer_size ? - (size_t)data->set.upload_buffer_size : smbc->upload_size; - data->req.upload_fromhere = data->state.ulbuf; - result = Curl_fillreadbuffer(data, nread, &nread); - if(result && result != CURLE_AGAIN) - return result; - if(!nread) - return CURLE_OK; - - smbc->upload_size -= nread; - smbc->send_size = nread; - smbc->sent = 0; - } - - /* Check if there is data to send */ - if(smbc->send_size) { - result = smb_flush(data); - if(result) - return result; - } - - /* Check if there is still data to be sent */ - if(smbc->send_size || smbc->upload_size) - return CURLE_AGAIN; - - return smb_recv_message(data, msg); -} - -static CURLcode smb_connection_state(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - struct smb_conn *smbc = &conn->proto.smbc; - struct smb_negotiate_response *nrsp; - struct smb_header *h; - CURLcode result; - void *msg = NULL; - - if(smbc->state == SMB_CONNECTING) { -#ifdef USE_SSL - if((conn->handler->flags & PROTOPT_SSL)) { - bool ssl_done = FALSE; - result = Curl_ssl_connect_nonblocking(data, conn, FALSE, - FIRSTSOCKET, &ssl_done); - if(result && result != CURLE_AGAIN) - return result; - if(!ssl_done) - return CURLE_OK; - } -#endif - - result = smb_send_negotiate(data); - if(result) { - connclose(conn, "SMB: failed to send negotiate message"); - return result; - } - - conn_state(data, SMB_NEGOTIATE); - } - - /* Send the previous message and check for a response */ - result = smb_send_and_recv(data, &msg); - if(result && result != CURLE_AGAIN) { - connclose(conn, "SMB: failed to communicate"); - return result; - } - - if(!msg) - return CURLE_OK; - - h = msg; - - switch(smbc->state) { - case SMB_NEGOTIATE: - if((smbc->got < sizeof(*nrsp) + sizeof(smbc->challenge) - 1) || - h->status) { - connclose(conn, "SMB: negotiation failed"); - return CURLE_COULDNT_CONNECT; - } - nrsp = msg; - memcpy(smbc->challenge, nrsp->bytes, sizeof(smbc->challenge)); - smbc->session_key = smb_swap32(nrsp->session_key); - result = smb_send_setup(data); - if(result) { - connclose(conn, "SMB: failed to send setup message"); - return result; - } - conn_state(data, SMB_SETUP); - break; - - case SMB_SETUP: - if(h->status) { - connclose(conn, "SMB: authentication failed"); - return CURLE_LOGIN_DENIED; - } - smbc->uid = smb_swap16(h->uid); - conn_state(data, SMB_CONNECTED); - *done = true; - break; - - default: - smb_pop_message(conn); - return CURLE_OK; /* ignore */ - } - - smb_pop_message(conn); - - return CURLE_OK; -} - -/* - * Convert a timestamp from the Windows world (100 nsec units from 1 Jan 1601) - * to Posix time. Cap the output to fit within a time_t. - */ -static void get_posix_time(time_t *out, curl_off_t timestamp) -{ - timestamp -= 116444736000000000; - timestamp /= 10000000; -#if SIZEOF_TIME_T < SIZEOF_CURL_OFF_T - if(timestamp > TIME_T_MAX) - *out = TIME_T_MAX; - else if(timestamp < TIME_T_MIN) - *out = TIME_T_MIN; - else -#endif - *out = (time_t) timestamp; -} - -static CURLcode smb_request_state(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - struct smb_request *req = data->req.p.smb; - struct smb_header *h; - struct smb_conn *smbc = &conn->proto.smbc; - enum smb_req_state next_state = SMB_DONE; - unsigned short len; - unsigned short off; - CURLcode result; - void *msg = NULL; - const struct smb_nt_create_response *smb_m; - - /* Start the request */ - if(req->state == SMB_REQUESTING) { - result = smb_send_tree_connect(data); - if(result) { - connclose(conn, "SMB: failed to send tree connect message"); - return result; - } - - request_state(data, SMB_TREE_CONNECT); - } - - /* Send the previous message and check for a response */ - result = smb_send_and_recv(data, &msg); - if(result && result != CURLE_AGAIN) { - connclose(conn, "SMB: failed to communicate"); - return result; - } - - if(!msg) - return CURLE_OK; - - h = msg; - - switch(req->state) { - case SMB_TREE_CONNECT: - if(h->status) { - req->result = CURLE_REMOTE_FILE_NOT_FOUND; - if(h->status == smb_swap32(SMB_ERR_NOACCESS)) - req->result = CURLE_REMOTE_ACCESS_DENIED; - break; - } - req->tid = smb_swap16(h->tid); - next_state = SMB_OPEN; - break; - - case SMB_OPEN: - if(h->status || smbc->got < sizeof(struct smb_nt_create_response)) { - req->result = CURLE_REMOTE_FILE_NOT_FOUND; - if(h->status == smb_swap32(SMB_ERR_NOACCESS)) - req->result = CURLE_REMOTE_ACCESS_DENIED; - next_state = SMB_TREE_DISCONNECT; - break; - } - smb_m = (const struct smb_nt_create_response*) msg; - req->fid = smb_swap16(smb_m->fid); - data->req.offset = 0; - if(data->set.upload) { - data->req.size = data->state.infilesize; - Curl_pgrsSetUploadSize(data, data->req.size); - next_state = SMB_UPLOAD; - } - else { - smb_m = (const struct smb_nt_create_response*) msg; - data->req.size = smb_swap64(smb_m->end_of_file); - if(data->req.size < 0) { - req->result = CURLE_WEIRD_SERVER_REPLY; - next_state = SMB_CLOSE; - } - else { - Curl_pgrsSetDownloadSize(data, data->req.size); - if(data->set.get_filetime) - get_posix_time(&data->info.filetime, smb_m->last_change_time); - next_state = SMB_DOWNLOAD; - } - } - break; - - case SMB_DOWNLOAD: - if(h->status || smbc->got < sizeof(struct smb_header) + 14) { - req->result = CURLE_RECV_ERROR; - next_state = SMB_CLOSE; - break; - } - len = Curl_read16_le(((const unsigned char *) msg) + - sizeof(struct smb_header) + 11); - off = Curl_read16_le(((const unsigned char *) msg) + - sizeof(struct smb_header) + 13); - if(len > 0) { - if(off + sizeof(unsigned int) + len > smbc->got) { - failf(data, "Invalid input packet"); - result = CURLE_RECV_ERROR; - } - else - result = Curl_client_write(data, CLIENTWRITE_BODY, - (char *)msg + off + sizeof(unsigned int), - len); - if(result) { - req->result = result; - next_state = SMB_CLOSE; - break; - } - } - data->req.bytecount += len; - data->req.offset += len; - Curl_pgrsSetDownloadCounter(data, data->req.bytecount); - next_state = (len < MAX_PAYLOAD_SIZE) ? SMB_CLOSE : SMB_DOWNLOAD; - break; - - case SMB_UPLOAD: - if(h->status || smbc->got < sizeof(struct smb_header) + 6) { - req->result = CURLE_UPLOAD_FAILED; - next_state = SMB_CLOSE; - break; - } - len = Curl_read16_le(((const unsigned char *) msg) + - sizeof(struct smb_header) + 5); - data->req.bytecount += len; - data->req.offset += len; - Curl_pgrsSetUploadCounter(data, data->req.bytecount); - if(data->req.bytecount >= data->req.size) - next_state = SMB_CLOSE; - else - next_state = SMB_UPLOAD; - break; - - case SMB_CLOSE: - /* We don't care if the close failed, proceed to tree disconnect anyway */ - next_state = SMB_TREE_DISCONNECT; - break; - - case SMB_TREE_DISCONNECT: - next_state = SMB_DONE; - break; - - default: - smb_pop_message(conn); - return CURLE_OK; /* ignore */ - } - - smb_pop_message(conn); - - switch(next_state) { - case SMB_OPEN: - result = smb_send_open(data); - break; - - case SMB_DOWNLOAD: - result = smb_send_read(data); - break; - - case SMB_UPLOAD: - result = smb_send_write(data); - break; - - case SMB_CLOSE: - result = smb_send_close(data); - break; - - case SMB_TREE_DISCONNECT: - result = smb_send_tree_disconnect(data); - break; - - case SMB_DONE: - result = req->result; - *done = true; - break; - - default: - break; - } - - if(result) { - connclose(conn, "SMB: failed to send message"); - return result; - } - - request_state(data, next_state); - - return CURLE_OK; -} - -static CURLcode smb_done(struct Curl_easy *data, CURLcode status, - bool premature) -{ - (void) premature; - Curl_safefree(data->req.p.smb); - return status; -} - -static CURLcode smb_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead) -{ - struct smb_conn *smbc = &conn->proto.smbc; - (void) dead; - (void) data; - Curl_safefree(smbc->share); - Curl_safefree(smbc->domain); - Curl_safefree(smbc->recv_buf); - return CURLE_OK; -} - -static int smb_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks) -{ - (void)data; - socks[0] = conn->sock[FIRSTSOCKET]; - return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0); -} - -static CURLcode smb_do(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - struct smb_conn *smbc = &conn->proto.smbc; - - *done = FALSE; - if(smbc->share) { - return CURLE_OK; - } - return CURLE_URL_MALFORMAT; -} - -static CURLcode smb_parse_url_path(struct Curl_easy *data, - struct connectdata *conn) -{ - struct smb_request *req = data->req.p.smb; - struct smb_conn *smbc = &conn->proto.smbc; - char *path; - char *slash; - - /* URL decode the path */ - CURLcode result = Curl_urldecode(data->state.up.path, 0, &path, NULL, - REJECT_CTRL); - if(result) - return result; - - /* Parse the path for the share */ - smbc->share = strdup((*path == '/' || *path == '\\') ? path + 1 : path); - free(path); - if(!smbc->share) - return CURLE_OUT_OF_MEMORY; - - slash = strchr(smbc->share, '/'); - if(!slash) - slash = strchr(smbc->share, '\\'); - - /* The share must be present */ - if(!slash) { - Curl_safefree(smbc->share); - return CURLE_URL_MALFORMAT; - } - - /* Parse the path for the file path converting any forward slashes into - backslashes */ - *slash++ = 0; - req->path = slash; - - for(; *slash; slash++) { - if(*slash == '/') - *slash = '\\'; - } - return CURLE_OK; -} - -#endif /* CURL_DISABLE_SMB && USE_CURL_NTLM_CORE && - SIZEOF_CURL_OFF_T > 4 */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2016 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2014, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies
+ *
+ * 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_SMB) && defined(USE_CURL_NTLM_CORE) && \
+ (SIZEOF_CURL_OFF_T > 4)
+
+#define BUILDING_CURL_SMB_C
+
+#ifdef WIN32
+#define getpid GetCurrentProcessId
+#endif
+
+#include "smb.h"
+#include "urldata.h"
+#include "sendf.h"
+#include "multiif.h"
+#include "cfilters.h"
+#include "connect.h"
+#include "progress.h"
+#include "transfer.h"
+#include "vtls/vtls.h"
+#include "curl_ntlm_core.h"
+#include "escape.h"
+#include "curl_endian.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* Local API functions */
+static CURLcode smb_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn);
+static CURLcode smb_connect(struct Curl_easy *data, bool *done);
+static CURLcode smb_connection_state(struct Curl_easy *data, bool *done);
+static CURLcode smb_do(struct Curl_easy *data, bool *done);
+static CURLcode smb_request_state(struct Curl_easy *data, bool *done);
+static CURLcode smb_disconnect(struct Curl_easy *data,
+ struct connectdata *conn, bool dead);
+static int smb_getsock(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t *socks);
+static CURLcode smb_parse_url_path(struct Curl_easy *data,
+ struct connectdata *conn);
+
+/*
+ * SMB handler interface
+ */
+const struct Curl_handler Curl_handler_smb = {
+ "SMB", /* scheme */
+ smb_setup_connection, /* setup_connection */
+ smb_do, /* do_it */
+ ZERO_NULL, /* done */
+ ZERO_NULL, /* do_more */
+ smb_connect, /* connect_it */
+ smb_connection_state, /* connecting */
+ smb_request_state, /* doing */
+ smb_getsock, /* proto_getsock */
+ smb_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ smb_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_SMB, /* defport */
+ CURLPROTO_SMB, /* protocol */
+ CURLPROTO_SMB, /* family */
+ PROTOPT_NONE /* flags */
+};
+
+#ifdef USE_SSL
+/*
+ * SMBS handler interface
+ */
+const struct Curl_handler Curl_handler_smbs = {
+ "SMBS", /* scheme */
+ smb_setup_connection, /* setup_connection */
+ smb_do, /* do_it */
+ ZERO_NULL, /* done */
+ ZERO_NULL, /* do_more */
+ smb_connect, /* connect_it */
+ smb_connection_state, /* connecting */
+ smb_request_state, /* doing */
+ smb_getsock, /* proto_getsock */
+ smb_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ smb_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_SMBS, /* defport */
+ CURLPROTO_SMBS, /* protocol */
+ CURLPROTO_SMB, /* family */
+ PROTOPT_SSL /* flags */
+};
+#endif
+
+#define MAX_PAYLOAD_SIZE 0x8000
+#define MAX_MESSAGE_SIZE (MAX_PAYLOAD_SIZE + 0x1000)
+#define CLIENTNAME "curl"
+#define SERVICENAME "?????"
+
+/* Append a string to an SMB message */
+#define MSGCAT(str) \
+ do { \
+ strcpy(p, (str)); \
+ p += strlen(str); \
+ } while(0)
+
+/* Append a null-terminated string to an SMB message */
+#define MSGCATNULL(str) \
+ do { \
+ strcpy(p, (str)); \
+ p += strlen(str) + 1; \
+ } while(0)
+
+/* SMB is mostly little endian */
+#if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \
+ defined(__OS400__)
+static unsigned short smb_swap16(unsigned short x)
+{
+ return (unsigned short) ((x << 8) | ((x >> 8) & 0xff));
+}
+
+static unsigned int smb_swap32(unsigned int x)
+{
+ return (x << 24) | ((x << 8) & 0xff0000) | ((x >> 8) & 0xff00) |
+ ((x >> 24) & 0xff);
+}
+
+static curl_off_t smb_swap64(curl_off_t x)
+{
+ return ((curl_off_t) smb_swap32((unsigned int) x) << 32) |
+ smb_swap32((unsigned int) (x >> 32));
+}
+
+#else
+# define smb_swap16(x) (x)
+# define smb_swap32(x) (x)
+# define smb_swap64(x) (x)
+#endif
+
+/* SMB request state */
+enum smb_req_state {
+ SMB_REQUESTING,
+ SMB_TREE_CONNECT,
+ SMB_OPEN,
+ SMB_DOWNLOAD,
+ SMB_UPLOAD,
+ SMB_CLOSE,
+ SMB_TREE_DISCONNECT,
+ SMB_DONE
+};
+
+/* SMB request data */
+struct smb_request {
+ enum smb_req_state state;
+ char *path;
+ unsigned short tid; /* Even if we connect to the same tree as another */
+ unsigned short fid; /* request, the tid will be different */
+ CURLcode result;
+};
+
+static void conn_state(struct Curl_easy *data, enum smb_conn_state newstate)
+{
+ struct smb_conn *smbc = &data->conn->proto.smbc;
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ /* For debug purposes */
+ static const char * const names[] = {
+ "SMB_NOT_CONNECTED",
+ "SMB_CONNECTING",
+ "SMB_NEGOTIATE",
+ "SMB_SETUP",
+ "SMB_CONNECTED",
+ /* LAST */
+ };
+
+ if(smbc->state != newstate)
+ infof(data, "SMB conn %p state change from %s to %s",
+ (void *)smbc, names[smbc->state], names[newstate]);
+#endif
+
+ smbc->state = newstate;
+}
+
+static void request_state(struct Curl_easy *data,
+ enum smb_req_state newstate)
+{
+ struct smb_request *req = data->req.p.smb;
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ /* For debug purposes */
+ static const char * const names[] = {
+ "SMB_REQUESTING",
+ "SMB_TREE_CONNECT",
+ "SMB_OPEN",
+ "SMB_DOWNLOAD",
+ "SMB_UPLOAD",
+ "SMB_CLOSE",
+ "SMB_TREE_DISCONNECT",
+ "SMB_DONE",
+ /* LAST */
+ };
+
+ if(req->state != newstate)
+ infof(data, "SMB request %p state change from %s to %s",
+ (void *)req, names[req->state], names[newstate]);
+#endif
+
+ req->state = newstate;
+}
+
+/* this should setup things in the connection, not in the easy
+ handle */
+static CURLcode smb_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ struct smb_request *req;
+
+ /* Initialize the request state */
+ data->req.p.smb = req = calloc(1, sizeof(struct smb_request));
+ if(!req)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Parse the URL path */
+ return smb_parse_url_path(data, conn);
+}
+
+static CURLcode smb_connect(struct Curl_easy *data, bool *done)
+{
+ struct connectdata *conn = data->conn;
+ struct smb_conn *smbc = &conn->proto.smbc;
+ char *slash;
+
+ (void) done;
+
+ /* Check we have a username and password to authenticate with */
+ if(!data->state.aptr.user)
+ return CURLE_LOGIN_DENIED;
+
+ /* Initialize the connection state */
+ smbc->state = SMB_CONNECTING;
+ smbc->recv_buf = malloc(MAX_MESSAGE_SIZE);
+ if(!smbc->recv_buf)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Multiple requests are allowed with this connection */
+ connkeep(conn, "SMB default");
+
+ /* Parse the username, domain, and password */
+ slash = strchr(conn->user, '/');
+ if(!slash)
+ slash = strchr(conn->user, '\\');
+
+ if(slash) {
+ smbc->user = slash + 1;
+ smbc->domain = strdup(conn->user);
+ if(!smbc->domain)
+ return CURLE_OUT_OF_MEMORY;
+ smbc->domain[slash - conn->user] = 0;
+ }
+ else {
+ smbc->user = conn->user;
+ smbc->domain = strdup(conn->host.name);
+ if(!smbc->domain)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode smb_recv_message(struct Curl_easy *data, void **msg)
+{
+ struct connectdata *conn = data->conn;
+ curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+ struct smb_conn *smbc = &conn->proto.smbc;
+ char *buf = smbc->recv_buf;
+ ssize_t bytes_read;
+ size_t nbt_size;
+ size_t msg_size;
+ size_t len = MAX_MESSAGE_SIZE - smbc->got;
+ CURLcode result;
+
+ result = Curl_read(data, sockfd, buf + smbc->got, len, &bytes_read);
+ if(result)
+ return result;
+
+ if(!bytes_read)
+ return CURLE_OK;
+
+ smbc->got += bytes_read;
+
+ /* Check for a 32-bit nbt header */
+ if(smbc->got < sizeof(unsigned int))
+ return CURLE_OK;
+
+ nbt_size = Curl_read16_be((const unsigned char *)
+ (buf + sizeof(unsigned short))) +
+ sizeof(unsigned int);
+ if(smbc->got < nbt_size)
+ return CURLE_OK;
+
+ msg_size = sizeof(struct smb_header);
+ if(nbt_size >= msg_size + 1) {
+ /* Add the word count */
+ msg_size += 1 + ((unsigned char) buf[msg_size]) * sizeof(unsigned short);
+ if(nbt_size >= msg_size + sizeof(unsigned short)) {
+ /* Add the byte count */
+ msg_size += sizeof(unsigned short) +
+ Curl_read16_le((const unsigned char *)&buf[msg_size]);
+ if(nbt_size < msg_size)
+ return CURLE_READ_ERROR;
+ }
+ }
+
+ *msg = buf;
+
+ return CURLE_OK;
+}
+
+static void smb_pop_message(struct connectdata *conn)
+{
+ struct smb_conn *smbc = &conn->proto.smbc;
+
+ smbc->got = 0;
+}
+
+static void smb_format_message(struct Curl_easy *data, struct smb_header *h,
+ unsigned char cmd, size_t len)
+{
+ struct connectdata *conn = data->conn;
+ struct smb_conn *smbc = &conn->proto.smbc;
+ struct smb_request *req = data->req.p.smb;
+ unsigned int pid;
+
+ memset(h, 0, sizeof(*h));
+ h->nbt_length = htons((unsigned short) (sizeof(*h) - sizeof(unsigned int) +
+ len));
+ memcpy((char *)h->magic, "\xffSMB", 4);
+ h->command = cmd;
+ h->flags = SMB_FLAGS_CANONICAL_PATHNAMES | SMB_FLAGS_CASELESS_PATHNAMES;
+ h->flags2 = smb_swap16(SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_KNOWS_LONG_NAME);
+ h->uid = smb_swap16(smbc->uid);
+ h->tid = smb_swap16(req->tid);
+ pid = getpid();
+ h->pid_high = smb_swap16((unsigned short)(pid >> 16));
+ h->pid = smb_swap16((unsigned short) pid);
+}
+
+static CURLcode smb_send(struct Curl_easy *data, ssize_t len,
+ size_t upload_size)
+{
+ struct connectdata *conn = data->conn;
+ curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+ struct smb_conn *smbc = &conn->proto.smbc;
+ ssize_t bytes_written;
+ CURLcode result;
+
+ result = Curl_write(data, sockfd, data->state.ulbuf,
+ len, &bytes_written);
+ if(result)
+ return result;
+
+ if(bytes_written != len) {
+ smbc->send_size = len;
+ smbc->sent = bytes_written;
+ }
+
+ smbc->upload_size = upload_size;
+
+ return CURLE_OK;
+}
+
+static CURLcode smb_flush(struct Curl_easy *data)
+{
+ struct connectdata *conn = data->conn;
+ curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+ struct smb_conn *smbc = &conn->proto.smbc;
+ ssize_t bytes_written;
+ ssize_t len = smbc->send_size - smbc->sent;
+ CURLcode result;
+
+ if(!smbc->send_size)
+ return CURLE_OK;
+
+ result = Curl_write(data, sockfd,
+ data->state.ulbuf + smbc->sent,
+ len, &bytes_written);
+ if(result)
+ return result;
+
+ if(bytes_written != len)
+ smbc->sent += bytes_written;
+ else
+ smbc->send_size = 0;
+
+ return CURLE_OK;
+}
+
+static CURLcode smb_send_message(struct Curl_easy *data, unsigned char cmd,
+ const void *msg, size_t msg_len)
+{
+ CURLcode result = Curl_get_upload_buffer(data);
+ if(result)
+ return result;
+ smb_format_message(data, (struct smb_header *)data->state.ulbuf,
+ cmd, msg_len);
+ memcpy(data->state.ulbuf + sizeof(struct smb_header),
+ msg, msg_len);
+
+ return smb_send(data, sizeof(struct smb_header) + msg_len, 0);
+}
+
+static CURLcode smb_send_negotiate(struct Curl_easy *data)
+{
+ const char *msg = "\x00\x0c\x00\x02NT LM 0.12";
+
+ return smb_send_message(data, SMB_COM_NEGOTIATE, msg, 15);
+}
+
+static CURLcode smb_send_setup(struct Curl_easy *data)
+{
+ struct connectdata *conn = data->conn;
+ struct smb_conn *smbc = &conn->proto.smbc;
+ struct smb_setup msg;
+ char *p = msg.bytes;
+ unsigned char lm_hash[21];
+ unsigned char lm[24];
+ unsigned char nt_hash[21];
+ unsigned char nt[24];
+
+ size_t byte_count = sizeof(lm) + sizeof(nt);
+ byte_count += strlen(smbc->user) + strlen(smbc->domain);
+ byte_count += strlen(OS) + strlen(CLIENTNAME) + 4; /* 4 null chars */
+ if(byte_count > sizeof(msg.bytes))
+ return CURLE_FILESIZE_EXCEEDED;
+
+ Curl_ntlm_core_mk_lm_hash(conn->passwd, lm_hash);
+ Curl_ntlm_core_lm_resp(lm_hash, smbc->challenge, lm);
+ Curl_ntlm_core_mk_nt_hash(conn->passwd, nt_hash);
+ Curl_ntlm_core_lm_resp(nt_hash, smbc->challenge, nt);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.word_count = SMB_WC_SETUP_ANDX;
+ msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
+ msg.max_buffer_size = smb_swap16(MAX_MESSAGE_SIZE);
+ msg.max_mpx_count = smb_swap16(1);
+ msg.vc_number = smb_swap16(1);
+ msg.session_key = smb_swap32(smbc->session_key);
+ msg.capabilities = smb_swap32(SMB_CAP_LARGE_FILES);
+ msg.lengths[0] = smb_swap16(sizeof(lm));
+ msg.lengths[1] = smb_swap16(sizeof(nt));
+ memcpy(p, lm, sizeof(lm));
+ p += sizeof(lm);
+ memcpy(p, nt, sizeof(nt));
+ p += sizeof(nt);
+ MSGCATNULL(smbc->user);
+ MSGCATNULL(smbc->domain);
+ MSGCATNULL(OS);
+ MSGCATNULL(CLIENTNAME);
+ byte_count = p - msg.bytes;
+ msg.byte_count = smb_swap16((unsigned short)byte_count);
+
+ return smb_send_message(data, SMB_COM_SETUP_ANDX, &msg,
+ sizeof(msg) - sizeof(msg.bytes) + byte_count);
+}
+
+static CURLcode smb_send_tree_connect(struct Curl_easy *data)
+{
+ struct smb_tree_connect msg;
+ struct connectdata *conn = data->conn;
+ struct smb_conn *smbc = &conn->proto.smbc;
+ char *p = msg.bytes;
+
+ size_t byte_count = strlen(conn->host.name) + strlen(smbc->share);
+ byte_count += strlen(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */
+ if(byte_count > sizeof(msg.bytes))
+ return CURLE_FILESIZE_EXCEEDED;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.word_count = SMB_WC_TREE_CONNECT_ANDX;
+ msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
+ msg.pw_len = 0;
+ MSGCAT("\\\\");
+ MSGCAT(conn->host.name);
+ MSGCAT("\\");
+ MSGCATNULL(smbc->share);
+ MSGCATNULL(SERVICENAME); /* Match any type of service */
+ byte_count = p - msg.bytes;
+ msg.byte_count = smb_swap16((unsigned short)byte_count);
+
+ return smb_send_message(data, SMB_COM_TREE_CONNECT_ANDX, &msg,
+ sizeof(msg) - sizeof(msg.bytes) + byte_count);
+}
+
+static CURLcode smb_send_open(struct Curl_easy *data)
+{
+ struct smb_request *req = data->req.p.smb;
+ struct smb_nt_create msg;
+ size_t byte_count;
+
+ if((strlen(req->path) + 1) > sizeof(msg.bytes))
+ return CURLE_FILESIZE_EXCEEDED;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.word_count = SMB_WC_NT_CREATE_ANDX;
+ msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
+ byte_count = strlen(req->path);
+ msg.name_length = smb_swap16((unsigned short)byte_count);
+ msg.share_access = smb_swap32(SMB_FILE_SHARE_ALL);
+ if(data->set.upload) {
+ msg.access = smb_swap32(SMB_GENERIC_READ | SMB_GENERIC_WRITE);
+ msg.create_disposition = smb_swap32(SMB_FILE_OVERWRITE_IF);
+ }
+ else {
+ msg.access = smb_swap32(SMB_GENERIC_READ);
+ msg.create_disposition = smb_swap32(SMB_FILE_OPEN);
+ }
+ msg.byte_count = smb_swap16((unsigned short) ++byte_count);
+ strcpy(msg.bytes, req->path);
+
+ return smb_send_message(data, SMB_COM_NT_CREATE_ANDX, &msg,
+ sizeof(msg) - sizeof(msg.bytes) + byte_count);
+}
+
+static CURLcode smb_send_close(struct Curl_easy *data)
+{
+ struct smb_request *req = data->req.p.smb;
+ struct smb_close msg;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.word_count = SMB_WC_CLOSE;
+ msg.fid = smb_swap16(req->fid);
+
+ return smb_send_message(data, SMB_COM_CLOSE, &msg, sizeof(msg));
+}
+
+static CURLcode smb_send_tree_disconnect(struct Curl_easy *data)
+{
+ struct smb_tree_disconnect msg;
+
+ memset(&msg, 0, sizeof(msg));
+
+ return smb_send_message(data, SMB_COM_TREE_DISCONNECT, &msg, sizeof(msg));
+}
+
+static CURLcode smb_send_read(struct Curl_easy *data)
+{
+ struct smb_request *req = data->req.p.smb;
+ curl_off_t offset = data->req.offset;
+ struct smb_read msg;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.word_count = SMB_WC_READ_ANDX;
+ msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
+ msg.fid = smb_swap16(req->fid);
+ msg.offset = smb_swap32((unsigned int) offset);
+ msg.offset_high = smb_swap32((unsigned int) (offset >> 32));
+ msg.min_bytes = smb_swap16(MAX_PAYLOAD_SIZE);
+ msg.max_bytes = smb_swap16(MAX_PAYLOAD_SIZE);
+
+ return smb_send_message(data, SMB_COM_READ_ANDX, &msg, sizeof(msg));
+}
+
+static CURLcode smb_send_write(struct Curl_easy *data)
+{
+ struct smb_write *msg;
+ struct smb_request *req = data->req.p.smb;
+ curl_off_t offset = data->req.offset;
+ curl_off_t upload_size = data->req.size - data->req.bytecount;
+ CURLcode result = Curl_get_upload_buffer(data);
+ if(result)
+ return result;
+ msg = (struct smb_write *)data->state.ulbuf;
+
+ if(upload_size >= MAX_PAYLOAD_SIZE - 1) /* There is one byte of padding */
+ upload_size = MAX_PAYLOAD_SIZE - 1;
+
+ memset(msg, 0, sizeof(*msg));
+ msg->word_count = SMB_WC_WRITE_ANDX;
+ msg->andx.command = SMB_COM_NO_ANDX_COMMAND;
+ msg->fid = smb_swap16(req->fid);
+ msg->offset = smb_swap32((unsigned int) offset);
+ msg->offset_high = smb_swap32((unsigned int) (offset >> 32));
+ msg->data_length = smb_swap16((unsigned short) upload_size);
+ msg->data_offset = smb_swap16(sizeof(*msg) - sizeof(unsigned int));
+ msg->byte_count = smb_swap16((unsigned short) (upload_size + 1));
+
+ smb_format_message(data, &msg->h, SMB_COM_WRITE_ANDX,
+ sizeof(*msg) - sizeof(msg->h) + (size_t) upload_size);
+
+ return smb_send(data, sizeof(*msg), (size_t) upload_size);
+}
+
+static CURLcode smb_send_and_recv(struct Curl_easy *data, void **msg)
+{
+ struct connectdata *conn = data->conn;
+ struct smb_conn *smbc = &conn->proto.smbc;
+ CURLcode result;
+ *msg = NULL; /* if it returns early */
+
+ /* Check if there is data in the transfer buffer */
+ if(!smbc->send_size && smbc->upload_size) {
+ size_t nread = smbc->upload_size > (size_t)data->set.upload_buffer_size ?
+ (size_t)data->set.upload_buffer_size : smbc->upload_size;
+ data->req.upload_fromhere = data->state.ulbuf;
+ result = Curl_fillreadbuffer(data, nread, &nread);
+ if(result && result != CURLE_AGAIN)
+ return result;
+ if(!nread)
+ return CURLE_OK;
+
+ smbc->upload_size -= nread;
+ smbc->send_size = nread;
+ smbc->sent = 0;
+ }
+
+ /* Check if there is data to send */
+ if(smbc->send_size) {
+ result = smb_flush(data);
+ if(result)
+ return result;
+ }
+
+ /* Check if there is still data to be sent */
+ if(smbc->send_size || smbc->upload_size)
+ return CURLE_AGAIN;
+
+ return smb_recv_message(data, msg);
+}
+
+static CURLcode smb_connection_state(struct Curl_easy *data, bool *done)
+{
+ struct connectdata *conn = data->conn;
+ struct smb_conn *smbc = &conn->proto.smbc;
+ struct smb_negotiate_response *nrsp;
+ struct smb_header *h;
+ CURLcode result;
+ void *msg = NULL;
+
+ if(smbc->state == SMB_CONNECTING) {
+#ifdef USE_SSL
+ if((conn->handler->flags & PROTOPT_SSL)) {
+ bool ssl_done = FALSE;
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssl_done);
+ if(result && result != CURLE_AGAIN)
+ return result;
+ if(!ssl_done)
+ return CURLE_OK;
+ }
+#endif
+
+ result = smb_send_negotiate(data);
+ if(result) {
+ connclose(conn, "SMB: failed to send negotiate message");
+ return result;
+ }
+
+ conn_state(data, SMB_NEGOTIATE);
+ }
+
+ /* Send the previous message and check for a response */
+ result = smb_send_and_recv(data, &msg);
+ if(result && result != CURLE_AGAIN) {
+ connclose(conn, "SMB: failed to communicate");
+ return result;
+ }
+
+ if(!msg)
+ return CURLE_OK;
+
+ h = msg;
+
+ switch(smbc->state) {
+ case SMB_NEGOTIATE:
+ if((smbc->got < sizeof(*nrsp) + sizeof(smbc->challenge) - 1) ||
+ h->status) {
+ connclose(conn, "SMB: negotiation failed");
+ return CURLE_COULDNT_CONNECT;
+ }
+ nrsp = msg;
+ memcpy(smbc->challenge, nrsp->bytes, sizeof(smbc->challenge));
+ smbc->session_key = smb_swap32(nrsp->session_key);
+ result = smb_send_setup(data);
+ if(result) {
+ connclose(conn, "SMB: failed to send setup message");
+ return result;
+ }
+ conn_state(data, SMB_SETUP);
+ break;
+
+ case SMB_SETUP:
+ if(h->status) {
+ connclose(conn, "SMB: authentication failed");
+ return CURLE_LOGIN_DENIED;
+ }
+ smbc->uid = smb_swap16(h->uid);
+ conn_state(data, SMB_CONNECTED);
+ *done = true;
+ break;
+
+ default:
+ smb_pop_message(conn);
+ return CURLE_OK; /* ignore */
+ }
+
+ smb_pop_message(conn);
+
+ return CURLE_OK;
+}
+
+/*
+ * Convert a timestamp from the Windows world (100 nsec units from 1 Jan 1601)
+ * to Posix time. Cap the output to fit within a time_t.
+ */
+static void get_posix_time(time_t *out, curl_off_t timestamp)
+{
+ timestamp -= 116444736000000000;
+ timestamp /= 10000000;
+#if SIZEOF_TIME_T < SIZEOF_CURL_OFF_T
+ if(timestamp > TIME_T_MAX)
+ *out = TIME_T_MAX;
+ else if(timestamp < TIME_T_MIN)
+ *out = TIME_T_MIN;
+ else
+#endif
+ *out = (time_t) timestamp;
+}
+
+static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
+{
+ struct connectdata *conn = data->conn;
+ struct smb_request *req = data->req.p.smb;
+ struct smb_header *h;
+ struct smb_conn *smbc = &conn->proto.smbc;
+ enum smb_req_state next_state = SMB_DONE;
+ unsigned short len;
+ unsigned short off;
+ CURLcode result;
+ void *msg = NULL;
+ const struct smb_nt_create_response *smb_m;
+
+ /* Start the request */
+ if(req->state == SMB_REQUESTING) {
+ result = smb_send_tree_connect(data);
+ if(result) {
+ connclose(conn, "SMB: failed to send tree connect message");
+ return result;
+ }
+
+ request_state(data, SMB_TREE_CONNECT);
+ }
+
+ /* Send the previous message and check for a response */
+ result = smb_send_and_recv(data, &msg);
+ if(result && result != CURLE_AGAIN) {
+ connclose(conn, "SMB: failed to communicate");
+ return result;
+ }
+
+ if(!msg)
+ return CURLE_OK;
+
+ h = msg;
+
+ switch(req->state) {
+ case SMB_TREE_CONNECT:
+ if(h->status) {
+ req->result = CURLE_REMOTE_FILE_NOT_FOUND;
+ if(h->status == smb_swap32(SMB_ERR_NOACCESS))
+ req->result = CURLE_REMOTE_ACCESS_DENIED;
+ break;
+ }
+ req->tid = smb_swap16(h->tid);
+ next_state = SMB_OPEN;
+ break;
+
+ case SMB_OPEN:
+ if(h->status || smbc->got < sizeof(struct smb_nt_create_response)) {
+ req->result = CURLE_REMOTE_FILE_NOT_FOUND;
+ if(h->status == smb_swap32(SMB_ERR_NOACCESS))
+ req->result = CURLE_REMOTE_ACCESS_DENIED;
+ next_state = SMB_TREE_DISCONNECT;
+ break;
+ }
+ smb_m = (const struct smb_nt_create_response*) msg;
+ req->fid = smb_swap16(smb_m->fid);
+ data->req.offset = 0;
+ if(data->set.upload) {
+ data->req.size = data->state.infilesize;
+ Curl_pgrsSetUploadSize(data, data->req.size);
+ next_state = SMB_UPLOAD;
+ }
+ else {
+ smb_m = (const struct smb_nt_create_response*) msg;
+ data->req.size = smb_swap64(smb_m->end_of_file);
+ if(data->req.size < 0) {
+ req->result = CURLE_WEIRD_SERVER_REPLY;
+ next_state = SMB_CLOSE;
+ }
+ else {
+ Curl_pgrsSetDownloadSize(data, data->req.size);
+ if(data->set.get_filetime)
+ get_posix_time(&data->info.filetime, smb_m->last_change_time);
+ next_state = SMB_DOWNLOAD;
+ }
+ }
+ break;
+
+ case SMB_DOWNLOAD:
+ if(h->status || smbc->got < sizeof(struct smb_header) + 14) {
+ req->result = CURLE_RECV_ERROR;
+ next_state = SMB_CLOSE;
+ break;
+ }
+ len = Curl_read16_le(((const unsigned char *) msg) +
+ sizeof(struct smb_header) + 11);
+ off = Curl_read16_le(((const unsigned char *) msg) +
+ sizeof(struct smb_header) + 13);
+ if(len > 0) {
+ if(off + sizeof(unsigned int) + len > smbc->got) {
+ failf(data, "Invalid input packet");
+ result = CURLE_RECV_ERROR;
+ }
+ else
+ result = Curl_client_write(data, CLIENTWRITE_BODY,
+ (char *)msg + off + sizeof(unsigned int),
+ len);
+ if(result) {
+ req->result = result;
+ next_state = SMB_CLOSE;
+ break;
+ }
+ }
+ data->req.bytecount += len;
+ data->req.offset += len;
+ Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
+ next_state = (len < MAX_PAYLOAD_SIZE) ? SMB_CLOSE : SMB_DOWNLOAD;
+ break;
+
+ case SMB_UPLOAD:
+ if(h->status || smbc->got < sizeof(struct smb_header) + 6) {
+ req->result = CURLE_UPLOAD_FAILED;
+ next_state = SMB_CLOSE;
+ break;
+ }
+ len = Curl_read16_le(((const unsigned char *) msg) +
+ sizeof(struct smb_header) + 5);
+ data->req.bytecount += len;
+ data->req.offset += len;
+ Curl_pgrsSetUploadCounter(data, data->req.bytecount);
+ if(data->req.bytecount >= data->req.size)
+ next_state = SMB_CLOSE;
+ else
+ next_state = SMB_UPLOAD;
+ break;
+
+ case SMB_CLOSE:
+ /* We don't care if the close failed, proceed to tree disconnect anyway */
+ next_state = SMB_TREE_DISCONNECT;
+ break;
+
+ case SMB_TREE_DISCONNECT:
+ next_state = SMB_DONE;
+ break;
+
+ default:
+ smb_pop_message(conn);
+ return CURLE_OK; /* ignore */
+ }
+
+ smb_pop_message(conn);
+
+ switch(next_state) {
+ case SMB_OPEN:
+ result = smb_send_open(data);
+ break;
+
+ case SMB_DOWNLOAD:
+ result = smb_send_read(data);
+ break;
+
+ case SMB_UPLOAD:
+ result = smb_send_write(data);
+ break;
+
+ case SMB_CLOSE:
+ result = smb_send_close(data);
+ break;
+
+ case SMB_TREE_DISCONNECT:
+ result = smb_send_tree_disconnect(data);
+ break;
+
+ case SMB_DONE:
+ result = req->result;
+ *done = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if(result) {
+ connclose(conn, "SMB: failed to send message");
+ return result;
+ }
+
+ request_state(data, next_state);
+
+ return CURLE_OK;
+}
+
+static CURLcode smb_disconnect(struct Curl_easy *data,
+ struct connectdata *conn, bool dead)
+{
+ struct smb_conn *smbc = &conn->proto.smbc;
+ (void) dead;
+ (void) data;
+ Curl_safefree(smbc->share);
+ Curl_safefree(smbc->domain);
+ Curl_safefree(smbc->recv_buf);
+ return CURLE_OK;
+}
+
+static int smb_getsock(struct Curl_easy *data,
+ struct connectdata *conn, curl_socket_t *socks)
+{
+ (void)data;
+ socks[0] = conn->sock[FIRSTSOCKET];
+ return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0);
+}
+
+static CURLcode smb_do(struct Curl_easy *data, bool *done)
+{
+ struct connectdata *conn = data->conn;
+ struct smb_conn *smbc = &conn->proto.smbc;
+
+ *done = FALSE;
+ if(smbc->share) {
+ return CURLE_OK;
+ }
+ return CURLE_URL_MALFORMAT;
+}
+
+static CURLcode smb_parse_url_path(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ struct smb_request *req = data->req.p.smb;
+ struct smb_conn *smbc = &conn->proto.smbc;
+ char *path;
+ char *slash;
+
+ /* URL decode the path */
+ CURLcode result = Curl_urldecode(data->state.up.path, 0, &path, NULL,
+ REJECT_CTRL);
+ if(result)
+ return result;
+
+ /* Parse the path for the share */
+ smbc->share = strdup((*path == '/' || *path == '\\') ? path + 1 : path);
+ free(path);
+ if(!smbc->share)
+ return CURLE_OUT_OF_MEMORY;
+
+ slash = strchr(smbc->share, '/');
+ if(!slash)
+ slash = strchr(smbc->share, '\\');
+
+ /* The share must be present */
+ if(!slash) {
+ Curl_safefree(smbc->share);
+ return CURLE_URL_MALFORMAT;
+ }
+
+ /* Parse the path for the file path converting any forward slashes into
+ backslashes */
+ *slash++ = 0;
+ req->path = slash;
+
+ for(; *slash; slash++) {
+ if(*slash == '/')
+ *slash = '\\';
+ }
+ return CURLE_OK;
+}
+
+#endif /* CURL_DISABLE_SMB && USE_CURL_NTLM_CORE &&
+ SIZEOF_CURL_OFF_T > 4 */
diff --git a/libs/libcurl/src/smb.h b/libs/libcurl/src/smb.h index 919f3ac142..f2d4e70f9c 100644 --- a/libs/libcurl/src/smb.h +++ b/libs/libcurl/src/smb.h @@ -1,257 +1,257 @@ -#ifndef HEADER_CURL_SMB_H -#define HEADER_CURL_SMB_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2018, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies - * Copyright (C) 2018 - 2022, 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 - * - ***************************************************************************/ - -enum smb_conn_state { - SMB_NOT_CONNECTED = 0, - SMB_CONNECTING, - SMB_NEGOTIATE, - SMB_SETUP, - SMB_CONNECTED -}; - -struct smb_conn { - enum smb_conn_state state; - char *user; - char *domain; - char *share; - unsigned char challenge[8]; - unsigned int session_key; - unsigned short uid; - char *recv_buf; - size_t upload_size; - size_t send_size; - size_t sent; - size_t got; -}; - -/* - * Definitions for SMB protocol data structures - */ -#ifdef BUILDING_CURL_SMB_C - -#if defined(_MSC_VER) || defined(__ILEC400__) -# define PACK -# pragma pack(push) -# pragma pack(1) -#elif defined(__GNUC__) -# define PACK __attribute__((packed)) -#else -# define PACK -#endif - -#define SMB_COM_CLOSE 0x04 -#define SMB_COM_READ_ANDX 0x2e -#define SMB_COM_WRITE_ANDX 0x2f -#define SMB_COM_TREE_DISCONNECT 0x71 -#define SMB_COM_NEGOTIATE 0x72 -#define SMB_COM_SETUP_ANDX 0x73 -#define SMB_COM_TREE_CONNECT_ANDX 0x75 -#define SMB_COM_NT_CREATE_ANDX 0xa2 -#define SMB_COM_NO_ANDX_COMMAND 0xff - -#define SMB_WC_CLOSE 0x03 -#define SMB_WC_READ_ANDX 0x0c -#define SMB_WC_WRITE_ANDX 0x0e -#define SMB_WC_SETUP_ANDX 0x0d -#define SMB_WC_TREE_CONNECT_ANDX 0x04 -#define SMB_WC_NT_CREATE_ANDX 0x18 - -#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10 -#define SMB_FLAGS_CASELESS_PATHNAMES 0x08 -#define SMB_FLAGS2_UNICODE_STRINGS 0x8000 -#define SMB_FLAGS2_IS_LONG_NAME 0x0040 -#define SMB_FLAGS2_KNOWS_LONG_NAME 0x0001 - -#define SMB_CAP_LARGE_FILES 0x08 -#define SMB_GENERIC_WRITE 0x40000000 -#define SMB_GENERIC_READ 0x80000000 -#define SMB_FILE_SHARE_ALL 0x07 -#define SMB_FILE_OPEN 0x01 -#define SMB_FILE_OVERWRITE_IF 0x05 - -#define SMB_ERR_NOACCESS 0x00050001 - -struct smb_header { - unsigned char nbt_type; - unsigned char nbt_flags; - unsigned short nbt_length; - unsigned char magic[4]; - unsigned char command; - unsigned int status; - unsigned char flags; - unsigned short flags2; - unsigned short pid_high; - unsigned char signature[8]; - unsigned short pad; - unsigned short tid; - unsigned short pid; - unsigned short uid; - unsigned short mid; -} PACK; - -struct smb_negotiate_response { - struct smb_header h; - unsigned char word_count; - unsigned short dialect_index; - unsigned char security_mode; - unsigned short max_mpx_count; - unsigned short max_number_vcs; - unsigned int max_buffer_size; - unsigned int max_raw_size; - unsigned int session_key; - unsigned int capabilities; - unsigned int system_time_low; - unsigned int system_time_high; - unsigned short server_time_zone; - unsigned char encryption_key_length; - unsigned short byte_count; - char bytes[1]; -} PACK; - -struct andx { - unsigned char command; - unsigned char pad; - unsigned short offset; -} PACK; - -struct smb_setup { - unsigned char word_count; - struct andx andx; - unsigned short max_buffer_size; - unsigned short max_mpx_count; - unsigned short vc_number; - unsigned int session_key; - unsigned short lengths[2]; - unsigned int pad; - unsigned int capabilities; - unsigned short byte_count; - char bytes[1024]; -} PACK; - -struct smb_tree_connect { - unsigned char word_count; - struct andx andx; - unsigned short flags; - unsigned short pw_len; - unsigned short byte_count; - char bytes[1024]; -} PACK; - -struct smb_nt_create { - unsigned char word_count; - struct andx andx; - unsigned char pad; - unsigned short name_length; - unsigned int flags; - unsigned int root_fid; - unsigned int access; - curl_off_t allocation_size; - unsigned int ext_file_attributes; - unsigned int share_access; - unsigned int create_disposition; - unsigned int create_options; - unsigned int impersonation_level; - unsigned char security_flags; - unsigned short byte_count; - char bytes[1024]; -} PACK; - -struct smb_nt_create_response { - struct smb_header h; - unsigned char word_count; - struct andx andx; - unsigned char op_lock_level; - unsigned short fid; - unsigned int create_disposition; - - curl_off_t create_time; - curl_off_t last_access_time; - curl_off_t last_write_time; - curl_off_t last_change_time; - unsigned int ext_file_attributes; - curl_off_t allocation_size; - curl_off_t end_of_file; -} PACK; - -struct smb_read { - unsigned char word_count; - struct andx andx; - unsigned short fid; - unsigned int offset; - unsigned short max_bytes; - unsigned short min_bytes; - unsigned int timeout; - unsigned short remaining; - unsigned int offset_high; - unsigned short byte_count; -} PACK; - -struct smb_write { - struct smb_header h; - unsigned char word_count; - struct andx andx; - unsigned short fid; - unsigned int offset; - unsigned int timeout; - unsigned short write_mode; - unsigned short remaining; - unsigned short pad; - unsigned short data_length; - unsigned short data_offset; - unsigned int offset_high; - unsigned short byte_count; - unsigned char pad2; -} PACK; - -struct smb_close { - unsigned char word_count; - unsigned short fid; - unsigned int last_mtime; - unsigned short byte_count; -} PACK; - -struct smb_tree_disconnect { - unsigned char word_count; - unsigned short byte_count; -} PACK; - -#if defined(_MSC_VER) || defined(__ILEC400__) -# pragma pack(pop) -#endif - -#endif /* BUILDING_CURL_SMB_C */ - -#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \ - (SIZEOF_CURL_OFF_T > 4) - -extern const struct Curl_handler Curl_handler_smb; -extern const struct Curl_handler Curl_handler_smbs; - -#endif /* CURL_DISABLE_SMB && USE_CURL_NTLM_CORE && - SIZEOF_CURL_OFF_T > 4 */ - -#endif /* HEADER_CURL_SMB_H */ +#ifndef HEADER_CURL_SMB_H
+#define HEADER_CURL_SMB_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2018, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies
+ * Copyright (C) 2018 - 2022, 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
+ *
+ ***************************************************************************/
+
+enum smb_conn_state {
+ SMB_NOT_CONNECTED = 0,
+ SMB_CONNECTING,
+ SMB_NEGOTIATE,
+ SMB_SETUP,
+ SMB_CONNECTED
+};
+
+struct smb_conn {
+ enum smb_conn_state state;
+ char *user;
+ char *domain;
+ char *share;
+ unsigned char challenge[8];
+ unsigned int session_key;
+ unsigned short uid;
+ char *recv_buf;
+ size_t upload_size;
+ size_t send_size;
+ size_t sent;
+ size_t got;
+};
+
+/*
+ * Definitions for SMB protocol data structures
+ */
+#ifdef BUILDING_CURL_SMB_C
+
+#if defined(_MSC_VER) || defined(__ILEC400__)
+# define PACK
+# pragma pack(push)
+# pragma pack(1)
+#elif defined(__GNUC__)
+# define PACK __attribute__((packed))
+#else
+# define PACK
+#endif
+
+#define SMB_COM_CLOSE 0x04
+#define SMB_COM_READ_ANDX 0x2e
+#define SMB_COM_WRITE_ANDX 0x2f
+#define SMB_COM_TREE_DISCONNECT 0x71
+#define SMB_COM_NEGOTIATE 0x72
+#define SMB_COM_SETUP_ANDX 0x73
+#define SMB_COM_TREE_CONNECT_ANDX 0x75
+#define SMB_COM_NT_CREATE_ANDX 0xa2
+#define SMB_COM_NO_ANDX_COMMAND 0xff
+
+#define SMB_WC_CLOSE 0x03
+#define SMB_WC_READ_ANDX 0x0c
+#define SMB_WC_WRITE_ANDX 0x0e
+#define SMB_WC_SETUP_ANDX 0x0d
+#define SMB_WC_TREE_CONNECT_ANDX 0x04
+#define SMB_WC_NT_CREATE_ANDX 0x18
+
+#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10
+#define SMB_FLAGS_CASELESS_PATHNAMES 0x08
+#define SMB_FLAGS2_UNICODE_STRINGS 0x8000
+#define SMB_FLAGS2_IS_LONG_NAME 0x0040
+#define SMB_FLAGS2_KNOWS_LONG_NAME 0x0001
+
+#define SMB_CAP_LARGE_FILES 0x08
+#define SMB_GENERIC_WRITE 0x40000000
+#define SMB_GENERIC_READ 0x80000000
+#define SMB_FILE_SHARE_ALL 0x07
+#define SMB_FILE_OPEN 0x01
+#define SMB_FILE_OVERWRITE_IF 0x05
+
+#define SMB_ERR_NOACCESS 0x00050001
+
+struct smb_header {
+ unsigned char nbt_type;
+ unsigned char nbt_flags;
+ unsigned short nbt_length;
+ unsigned char magic[4];
+ unsigned char command;
+ unsigned int status;
+ unsigned char flags;
+ unsigned short flags2;
+ unsigned short pid_high;
+ unsigned char signature[8];
+ unsigned short pad;
+ unsigned short tid;
+ unsigned short pid;
+ unsigned short uid;
+ unsigned short mid;
+} PACK;
+
+struct smb_negotiate_response {
+ struct smb_header h;
+ unsigned char word_count;
+ unsigned short dialect_index;
+ unsigned char security_mode;
+ unsigned short max_mpx_count;
+ unsigned short max_number_vcs;
+ unsigned int max_buffer_size;
+ unsigned int max_raw_size;
+ unsigned int session_key;
+ unsigned int capabilities;
+ unsigned int system_time_low;
+ unsigned int system_time_high;
+ unsigned short server_time_zone;
+ unsigned char encryption_key_length;
+ unsigned short byte_count;
+ char bytes[1];
+} PACK;
+
+struct andx {
+ unsigned char command;
+ unsigned char pad;
+ unsigned short offset;
+} PACK;
+
+struct smb_setup {
+ unsigned char word_count;
+ struct andx andx;
+ unsigned short max_buffer_size;
+ unsigned short max_mpx_count;
+ unsigned short vc_number;
+ unsigned int session_key;
+ unsigned short lengths[2];
+ unsigned int pad;
+ unsigned int capabilities;
+ unsigned short byte_count;
+ char bytes[1024];
+} PACK;
+
+struct smb_tree_connect {
+ unsigned char word_count;
+ struct andx andx;
+ unsigned short flags;
+ unsigned short pw_len;
+ unsigned short byte_count;
+ char bytes[1024];
+} PACK;
+
+struct smb_nt_create {
+ unsigned char word_count;
+ struct andx andx;
+ unsigned char pad;
+ unsigned short name_length;
+ unsigned int flags;
+ unsigned int root_fid;
+ unsigned int access;
+ curl_off_t allocation_size;
+ unsigned int ext_file_attributes;
+ unsigned int share_access;
+ unsigned int create_disposition;
+ unsigned int create_options;
+ unsigned int impersonation_level;
+ unsigned char security_flags;
+ unsigned short byte_count;
+ char bytes[1024];
+} PACK;
+
+struct smb_nt_create_response {
+ struct smb_header h;
+ unsigned char word_count;
+ struct andx andx;
+ unsigned char op_lock_level;
+ unsigned short fid;
+ unsigned int create_disposition;
+
+ curl_off_t create_time;
+ curl_off_t last_access_time;
+ curl_off_t last_write_time;
+ curl_off_t last_change_time;
+ unsigned int ext_file_attributes;
+ curl_off_t allocation_size;
+ curl_off_t end_of_file;
+} PACK;
+
+struct smb_read {
+ unsigned char word_count;
+ struct andx andx;
+ unsigned short fid;
+ unsigned int offset;
+ unsigned short max_bytes;
+ unsigned short min_bytes;
+ unsigned int timeout;
+ unsigned short remaining;
+ unsigned int offset_high;
+ unsigned short byte_count;
+} PACK;
+
+struct smb_write {
+ struct smb_header h;
+ unsigned char word_count;
+ struct andx andx;
+ unsigned short fid;
+ unsigned int offset;
+ unsigned int timeout;
+ unsigned short write_mode;
+ unsigned short remaining;
+ unsigned short pad;
+ unsigned short data_length;
+ unsigned short data_offset;
+ unsigned int offset_high;
+ unsigned short byte_count;
+ unsigned char pad2;
+} PACK;
+
+struct smb_close {
+ unsigned char word_count;
+ unsigned short fid;
+ unsigned int last_mtime;
+ unsigned short byte_count;
+} PACK;
+
+struct smb_tree_disconnect {
+ unsigned char word_count;
+ unsigned short byte_count;
+} PACK;
+
+#if defined(_MSC_VER) || defined(__ILEC400__)
+# pragma pack(pop)
+#endif
+
+#endif /* BUILDING_CURL_SMB_C */
+
+#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \
+ (SIZEOF_CURL_OFF_T > 4)
+
+extern const struct Curl_handler Curl_handler_smb;
+extern const struct Curl_handler Curl_handler_smbs;
+
+#endif /* CURL_DISABLE_SMB && USE_CURL_NTLM_CORE &&
+ SIZEOF_CURL_OFF_T > 4 */
+
+#endif /* HEADER_CURL_SMB_H */
diff --git a/libs/libcurl/src/smtp.c b/libs/libcurl/src/smtp.c index 6ebb41af66..54b8c03a56 100644 --- a/libs/libcurl/src/smtp.c +++ b/libs/libcurl/src/smtp.c @@ -1,1927 +1,1928 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - * RFC1870 SMTP Service Extension for Message Size - * RFC2195 CRAM-MD5 authentication - * RFC2831 DIGEST-MD5 authentication - * RFC3207 SMTP over TLS - * RFC4422 Simple Authentication and Security Layer (SASL) - * RFC4616 PLAIN authentication - * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism - * RFC4954 SMTP Authentication - * RFC5321 SMTP protocol - * RFC5890 Internationalized Domain Names for Applications (IDNA) - * RFC6531 SMTP Extension for Internationalized Email - * RFC6532 Internationalized Email Headers - * RFC6749 OAuth 2.0 Authorization Framework - * RFC8314 Use of TLS for Email Submission and Access - * Draft SMTP URL Interface <draft-earhart-url-smtp-00.txt> - * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt> - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifndef CURL_DISABLE_SMTP - -#ifdef HAVE_NETINET_IN_H -#include <netinet/in.h> -#endif -#ifdef HAVE_ARPA_INET_H -#include <arpa/inet.h> -#endif -#ifdef HAVE_UTSNAME_H -#include <sys/utsname.h> -#endif -#ifdef HAVE_NETDB_H -#include <netdb.h> -#endif -#ifdef __VMS -#include <in.h> -#include <inet.h> -#endif - -#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) -#undef in_addr_t -#define in_addr_t unsigned long -#endif - -#include <curl/curl.h> -#include "urldata.h" -#include "sendf.h" -#include "hostip.h" -#include "progress.h" -#include "transfer.h" -#include "escape.h" -#include "http.h" /* for HTTP proxy tunnel stuff */ -#include "mime.h" -#include "socks.h" -#include "smtp.h" -#include "strtoofft.h" -#include "strcase.h" -#include "vtls/vtls.h" -#include "connect.h" -#include "select.h" -#include "multiif.h" -#include "url.h" -#include "curl_gethostname.h" -#include "bufref.h" -#include "curl_sasl.h" -#include "warnless.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* Local API functions */ -static CURLcode smtp_regular_transfer(struct Curl_easy *data, bool *done); -static CURLcode smtp_do(struct Curl_easy *data, bool *done); -static CURLcode smtp_done(struct Curl_easy *data, CURLcode status, - bool premature); -static CURLcode smtp_connect(struct Curl_easy *data, bool *done); -static CURLcode smtp_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead); -static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done); -static int smtp_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks); -static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done); -static CURLcode smtp_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode smtp_parse_url_options(struct connectdata *conn); -static CURLcode smtp_parse_url_path(struct Curl_easy *data); -static CURLcode smtp_parse_custom_request(struct Curl_easy *data); -static CURLcode smtp_parse_address(struct Curl_easy *data, const char *fqma, - char **address, struct hostname *host); -static CURLcode smtp_perform_auth(struct Curl_easy *data, const char *mech, - const struct bufref *initresp); -static CURLcode smtp_continue_auth(struct Curl_easy *data, const char *mech, - const struct bufref *resp); -static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech); -static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out); - -/* - * SMTP protocol handler. - */ - -const struct Curl_handler Curl_handler_smtp = { - "SMTP", /* scheme */ - smtp_setup_connection, /* setup_connection */ - smtp_do, /* do_it */ - smtp_done, /* done */ - ZERO_NULL, /* do_more */ - smtp_connect, /* connect_it */ - smtp_multi_statemach, /* connecting */ - smtp_doing, /* doing */ - smtp_getsock, /* proto_getsock */ - smtp_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - smtp_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_SMTP, /* defport */ - CURLPROTO_SMTP, /* protocol */ - CURLPROTO_SMTP, /* family */ - PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */ - PROTOPT_URLOPTIONS -}; - -#ifdef USE_SSL -/* - * SMTPS protocol handler. - */ - -const struct Curl_handler Curl_handler_smtps = { - "SMTPS", /* scheme */ - smtp_setup_connection, /* setup_connection */ - smtp_do, /* do_it */ - smtp_done, /* done */ - ZERO_NULL, /* do_more */ - smtp_connect, /* connect_it */ - smtp_multi_statemach, /* connecting */ - smtp_doing, /* doing */ - smtp_getsock, /* proto_getsock */ - smtp_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - smtp_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_SMTPS, /* defport */ - CURLPROTO_SMTPS, /* protocol */ - CURLPROTO_SMTP, /* family */ - PROTOPT_CLOSEACTION | PROTOPT_SSL - | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */ -}; -#endif - -/* SASL parameters for the smtp protocol */ -static const struct SASLproto saslsmtp = { - "smtp", /* The service name */ - smtp_perform_auth, /* Send authentication command */ - smtp_continue_auth, /* Send authentication continuation */ - smtp_cancel_auth, /* Cancel authentication */ - smtp_get_message, /* Get SASL response message */ - 512 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */ - 334, /* Code received when continuation is expected */ - 235, /* Code to receive upon authentication success */ - SASL_AUTH_DEFAULT, /* Default mechanisms */ - SASL_FLAG_BASE64 /* Configuration flags */ -}; - -#ifdef USE_SSL -static void smtp_to_smtps(struct connectdata *conn) -{ - /* Change the connection handler */ - conn->handler = &Curl_handler_smtps; - - /* Set the connection's upgraded to TLS flag */ - conn->bits.tls_upgraded = TRUE; -} -#else -#define smtp_to_smtps(x) Curl_nop_stmt -#endif - -/*********************************************************************** - * - * smtp_endofresp() - * - * Checks for an ending SMTP status code at the start of the given string, but - * also detects various capabilities from the EHLO response including the - * supported authentication mechanisms. - */ -static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn, - char *line, size_t len, int *resp) -{ - struct smtp_conn *smtpc = &conn->proto.smtpc; - bool result = FALSE; - (void)data; - - /* Nothing for us */ - if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2])) - return FALSE; - - /* Do we have a command response? This should be the response code followed - by a space and optionally some text as per RFC-5321 and as outlined in - Section 4. Examples of RFC-4954 but some email servers ignore this and - only send the response code instead as per Section 4.2. */ - if(line[3] == ' ' || len == 5) { - char tmpline[6]; - - result = TRUE; - memset(tmpline, '\0', sizeof(tmpline)); - memcpy(tmpline, line, (len == 5 ? 5 : 3)); - *resp = curlx_sltosi(strtol(tmpline, NULL, 10)); - - /* Make sure real server never sends internal value */ - if(*resp == 1) - *resp = 0; - } - /* Do we have a multiline (continuation) response? */ - else if(line[3] == '-' && - (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) { - result = TRUE; - *resp = 1; /* Internal response code */ - } - - return result; -} - -/*********************************************************************** - * - * smtp_get_message() - * - * Gets the authentication message from the response buffer. - */ -static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out) -{ - char *message = data->state.buffer; - size_t len = strlen(message); - - if(len > 4) { - /* Find the start of the message */ - len -= 4; - for(message += 4; *message == ' ' || *message == '\t'; message++, len--) - ; - - /* Find the end of the message */ - while(len--) - if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && - message[len] != '\t') - break; - - /* Terminate the message */ - message[++len] = '\0'; - Curl_bufref_set(out, message, len, NULL); - } - else - /* junk input => zero length output */ - Curl_bufref_set(out, "", 0, NULL); - - return CURLE_OK; -} - -/*********************************************************************** - * - * state() - * - * This is the ONLY way to change SMTP state! - */ -static void state(struct Curl_easy *data, smtpstate newstate) -{ - struct smtp_conn *smtpc = &data->conn->proto.smtpc; -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - /* for debug purposes */ - static const char * const names[] = { - "STOP", - "SERVERGREET", - "EHLO", - "HELO", - "STARTTLS", - "UPGRADETLS", - "AUTH", - "COMMAND", - "MAIL", - "RCPT", - "DATA", - "POSTDATA", - "QUIT", - /* LAST */ - }; - - if(smtpc->state != newstate) - infof(data, "SMTP %p state change from %s to %s", - (void *)smtpc, names[smtpc->state], names[newstate]); -#endif - - smtpc->state = newstate; -} - -/*********************************************************************** - * - * smtp_perform_ehlo() - * - * Sends the EHLO command to not only initialise communication with the ESMTP - * server but to also obtain a list of server side supported capabilities. - */ -static CURLcode smtp_perform_ehlo(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct smtp_conn *smtpc = &conn->proto.smtpc; - - smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */ - smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism - used for esmtp connections */ - smtpc->tls_supported = FALSE; /* Clear the TLS capability */ - smtpc->auth_supported = FALSE; /* Clear the AUTH capability */ - - /* Send the EHLO command */ - result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain); - - if(!result) - state(data, SMTP_EHLO); - - return result; -} - -/*********************************************************************** - * - * smtp_perform_helo() - * - * Sends the HELO command to initialise communication with the SMTP server. - */ -static CURLcode smtp_perform_helo(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - struct smtp_conn *smtpc = &conn->proto.smtpc; - - smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used - in smtp connections */ - - /* Send the HELO command */ - result = Curl_pp_sendf(data, &smtpc->pp, "HELO %s", smtpc->domain); - - if(!result) - state(data, SMTP_HELO); - - return result; -} - -/*********************************************************************** - * - * smtp_perform_starttls() - * - * Sends the STLS command to start the upgrade to TLS. - */ -static CURLcode smtp_perform_starttls(struct Curl_easy *data, - struct connectdata *conn) -{ - /* Send the STARTTLS command */ - CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, - "%s", "STARTTLS"); - - if(!result) - state(data, SMTP_STARTTLS); - - return result; -} - -/*********************************************************************** - * - * smtp_perform_upgrade_tls() - * - * Performs the upgrade to TLS. - */ -static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data) -{ - /* Start the SSL connection */ - struct connectdata *conn = data->conn; - struct smtp_conn *smtpc = &conn->proto.smtpc; - CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FALSE, - FIRSTSOCKET, - &smtpc->ssldone); - - if(!result) { - if(smtpc->state != SMTP_UPGRADETLS) - state(data, SMTP_UPGRADETLS); - - if(smtpc->ssldone) { - smtp_to_smtps(conn); - result = smtp_perform_ehlo(data); - } - } - - return result; -} - -/*********************************************************************** - * - * smtp_perform_auth() - * - * Sends an AUTH command allowing the client to login with the given SASL - * authentication mechanism. - */ -static CURLcode smtp_perform_auth(struct Curl_easy *data, - const char *mech, - const struct bufref *initresp) -{ - CURLcode result = CURLE_OK; - struct smtp_conn *smtpc = &data->conn->proto.smtpc; - const char *ir = (const char *) Curl_bufref_ptr(initresp); - - if(ir) { /* AUTH <mech> ...<crlf> */ - /* Send the AUTH command with the initial response */ - result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s", mech, ir); - } - else { - /* Send the AUTH command */ - result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s", mech); - } - - return result; -} - -/*********************************************************************** - * - * smtp_continue_auth() - * - * Sends SASL continuation data. - */ -static CURLcode smtp_continue_auth(struct Curl_easy *data, - const char *mech, - const struct bufref *resp) -{ - struct smtp_conn *smtpc = &data->conn->proto.smtpc; - - (void)mech; - - return Curl_pp_sendf(data, &smtpc->pp, - "%s", (const char *) Curl_bufref_ptr(resp)); -} - -/*********************************************************************** - * - * smtp_cancel_auth() - * - * Sends SASL cancellation. - */ -static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech) -{ - struct smtp_conn *smtpc = &data->conn->proto.smtpc; - - (void)mech; - - return Curl_pp_sendf(data, &smtpc->pp, "*"); -} - -/*********************************************************************** - * - * smtp_perform_authentication() - * - * Initiates the authentication sequence, with the appropriate SASL - * authentication mechanism. - */ -static CURLcode smtp_perform_authentication(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct smtp_conn *smtpc = &conn->proto.smtpc; - saslprogress progress; - - /* Check we have enough data to authenticate with, and the - server supports authentication, and end the connect phase if not */ - if(!smtpc->auth_supported || - !Curl_sasl_can_authenticate(&smtpc->sasl, data)) { - state(data, SMTP_STOP); - return result; - } - - /* Calculate the SASL login details */ - result = Curl_sasl_start(&smtpc->sasl, data, FALSE, &progress); - - if(!result) { - if(progress == SASL_INPROGRESS) - state(data, SMTP_AUTH); - else { - /* Other mechanisms not supported */ - infof(data, "No known authentication mechanisms supported"); - result = CURLE_LOGIN_DENIED; - } - } - - return result; -} - -/*********************************************************************** - * - * smtp_perform_command() - * - * Sends a SMTP based command. - */ -static CURLcode smtp_perform_command(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct SMTP *smtp = data->req.p.smtp; - - if(smtp->rcpt) { - /* We notify the server we are sending UTF-8 data if a) it supports the - SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in - either the local address or host name parts. This is regardless of - whether the host name is encoded using IDN ACE */ - bool utf8 = FALSE; - - if((!smtp->custom) || (!smtp->custom[0])) { - char *address = NULL; - struct hostname host = { NULL, NULL, NULL, NULL }; - - /* Parse the mailbox to verify into the local address and host name - parts, converting the host name to an IDN A-label if necessary */ - result = smtp_parse_address(data, smtp->rcpt->data, - &address, &host); - if(result) - return result; - - /* Establish whether we should report SMTPUTF8 to the server for this - mailbox as per RFC-6531 sect. 3.1 point 6 */ - utf8 = (conn->proto.smtpc.utf8_supported) && - ((host.encalloc) || (!Curl_is_ASCII_name(address)) || - (!Curl_is_ASCII_name(host.name))); - - /* Send the VRFY command (Note: The host name part may be absent when the - host is a local system) */ - result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "VRFY %s%s%s%s", - address, - host.name ? "@" : "", - host.name ? host.name : "", - utf8 ? " SMTPUTF8" : ""); - - Curl_free_idnconverted_hostname(&host); - free(address); - } - else { - /* Establish whether we should report that we support SMTPUTF8 for EXPN - commands to the server as per RFC-6531 sect. 3.1 point 6 */ - utf8 = (conn->proto.smtpc.utf8_supported) && - (!strcmp(smtp->custom, "EXPN")); - - /* Send the custom recipient based command such as the EXPN command */ - result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, - "%s %s%s", smtp->custom, - smtp->rcpt->data, - utf8 ? " SMTPUTF8" : ""); - } - } - else - /* Send the non-recipient based command such as HELP */ - result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", - smtp->custom && smtp->custom[0] != '\0' ? - smtp->custom : "HELP"); - - if(!result) - state(data, SMTP_COMMAND); - - return result; -} - -/*********************************************************************** - * - * smtp_perform_mail() - * - * Sends an MAIL command to initiate the upload of a message. - */ -static CURLcode smtp_perform_mail(struct Curl_easy *data) -{ - char *from = NULL; - char *auth = NULL; - char *size = NULL; - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - - /* We notify the server we are sending UTF-8 data if a) it supports the - SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in - either the local address or host name parts. This is regardless of - whether the host name is encoded using IDN ACE */ - bool utf8 = FALSE; - - /* Calculate the FROM parameter */ - if(data->set.str[STRING_MAIL_FROM]) { - char *address = NULL; - struct hostname host = { NULL, NULL, NULL, NULL }; - - /* Parse the FROM mailbox into the local address and host name parts, - converting the host name to an IDN A-label if necessary */ - result = smtp_parse_address(data, data->set.str[STRING_MAIL_FROM], - &address, &host); - if(result) - return result; - - /* Establish whether we should report SMTPUTF8 to the server for this - mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */ - utf8 = (conn->proto.smtpc.utf8_supported) && - ((host.encalloc) || (!Curl_is_ASCII_name(address)) || - (!Curl_is_ASCII_name(host.name))); - - if(host.name) { - from = aprintf("<%s@%s>", address, host.name); - - Curl_free_idnconverted_hostname(&host); - } - else - /* An invalid mailbox was provided but we'll simply let the server worry - about that and reply with a 501 error */ - from = aprintf("<%s>", address); - - free(address); - } - else - /* Null reverse-path, RFC-5321, sect. 3.6.3 */ - from = strdup("<>"); - - if(!from) - return CURLE_OUT_OF_MEMORY; - - /* Calculate the optional AUTH parameter */ - if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) { - if(data->set.str[STRING_MAIL_AUTH][0] != '\0') { - char *address = NULL; - struct hostname host = { NULL, NULL, NULL, NULL }; - - /* Parse the AUTH mailbox into the local address and host name parts, - converting the host name to an IDN A-label if necessary */ - result = smtp_parse_address(data, data->set.str[STRING_MAIL_AUTH], - &address, &host); - if(result) { - free(from); - return result; - } - - /* Establish whether we should report SMTPUTF8 to the server for this - mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */ - if((!utf8) && (conn->proto.smtpc.utf8_supported) && - ((host.encalloc) || (!Curl_is_ASCII_name(address)) || - (!Curl_is_ASCII_name(host.name)))) - utf8 = TRUE; - - if(host.name) { - auth = aprintf("<%s@%s>", address, host.name); - - Curl_free_idnconverted_hostname(&host); - } - else - /* An invalid mailbox was provided but we'll simply let the server - worry about it */ - auth = aprintf("<%s>", address); - - free(address); - } - else - /* Empty AUTH, RFC-2554, sect. 5 */ - auth = strdup("<>"); - - if(!auth) { - free(from); - - return CURLE_OUT_OF_MEMORY; - } - } - - /* Prepare the mime data if some. */ - if(data->set.mimepost.kind != MIMEKIND_NONE) { - /* Use the whole structure as data. */ - data->set.mimepost.flags &= ~MIME_BODY_ONLY; - - /* Add external headers and mime version. */ - curl_mime_headers(&data->set.mimepost, data->set.headers, 0); - result = Curl_mime_prepare_headers(&data->set.mimepost, NULL, - NULL, MIMESTRATEGY_MAIL); - - if(!result) - if(!Curl_checkheaders(data, STRCONST("Mime-Version"))) - result = Curl_mime_add_header(&data->set.mimepost.curlheaders, - "Mime-Version: 1.0"); - - /* Make sure we will read the entire mime structure. */ - if(!result) - result = Curl_mime_rewind(&data->set.mimepost); - - if(result) { - free(from); - free(auth); - - return result; - } - - data->state.infilesize = Curl_mime_size(&data->set.mimepost); - - /* Read from mime structure. */ - data->state.fread_func = (curl_read_callback) Curl_mime_read; - data->state.in = (void *) &data->set.mimepost; - } - - /* Calculate the optional SIZE parameter */ - if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) { - size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize); - - if(!size) { - free(from); - free(auth); - - return CURLE_OUT_OF_MEMORY; - } - } - - /* If the mailboxes in the FROM and AUTH parameters don't include a UTF-8 - based address then quickly scan through the recipient list and check if - any there do, as we need to correctly identify our support for SMTPUTF8 - in the envelope, as per RFC-6531 sect. 3.4 */ - if(conn->proto.smtpc.utf8_supported && !utf8) { - struct SMTP *smtp = data->req.p.smtp; - struct curl_slist *rcpt = smtp->rcpt; - - while(rcpt && !utf8) { - /* Does the host name contain non-ASCII characters? */ - if(!Curl_is_ASCII_name(rcpt->data)) - utf8 = TRUE; - - rcpt = rcpt->next; - } - } - - /* Send the MAIL command */ - result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, - "MAIL FROM:%s%s%s%s%s%s", - from, /* Mandatory */ - auth ? " AUTH=" : "", /* Optional on AUTH support */ - auth ? auth : "", /* */ - size ? " SIZE=" : "", /* Optional on SIZE support */ - size ? size : "", /* */ - utf8 ? " SMTPUTF8" /* Internationalised mailbox */ - : ""); /* included in our envelope */ - - free(from); - free(auth); - free(size); - - if(!result) - state(data, SMTP_MAIL); - - return result; -} - -/*********************************************************************** - * - * smtp_perform_rcpt_to() - * - * Sends a RCPT TO command for a given recipient as part of the message upload - * process. - */ -static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct SMTP *smtp = data->req.p.smtp; - char *address = NULL; - struct hostname host = { NULL, NULL, NULL, NULL }; - - /* Parse the recipient mailbox into the local address and host name parts, - converting the host name to an IDN A-label if necessary */ - result = smtp_parse_address(data, smtp->rcpt->data, - &address, &host); - if(result) - return result; - - /* Send the RCPT TO command */ - if(host.name) - result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s@%s>", - address, host.name); - else - /* An invalid mailbox was provided but we'll simply let the server worry - about that and reply with a 501 error */ - result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s>", - address); - - Curl_free_idnconverted_hostname(&host); - free(address); - - if(!result) - state(data, SMTP_RCPT); - - return result; -} - -/*********************************************************************** - * - * smtp_perform_quit() - * - * Performs the quit action prior to sclose() being called. - */ -static CURLcode smtp_perform_quit(struct Curl_easy *data, - struct connectdata *conn) -{ - /* Send the QUIT command */ - CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "QUIT"); - - if(!result) - state(data, SMTP_QUIT); - - return result; -} - -/* For the initial server greeting */ -static CURLcode smtp_state_servergreet_resp(struct Curl_easy *data, - int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - (void)instate; /* no use for this yet */ - - if(smtpcode/100 != 2) { - failf(data, "Got unexpected smtp-server response: %d", smtpcode); - result = CURLE_WEIRD_SERVER_REPLY; - } - else - result = smtp_perform_ehlo(data); - - return result; -} - -/* For STARTTLS responses */ -static CURLcode smtp_state_starttls_resp(struct Curl_easy *data, - int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - (void)instate; /* no use for this yet */ - - /* Pipelining in response is forbidden. */ - if(data->conn->proto.smtpc.pp.cache_size) - return CURLE_WEIRD_SERVER_REPLY; - - if(smtpcode != 220) { - if(data->set.use_ssl != CURLUSESSL_TRY) { - failf(data, "STARTTLS denied, code %d", smtpcode); - result = CURLE_USE_SSL_FAILED; - } - else - result = smtp_perform_authentication(data); - } - else - result = smtp_perform_upgrade_tls(data); - - return result; -} - -/* For EHLO responses */ -static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data, - struct connectdata *conn, int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - struct smtp_conn *smtpc = &conn->proto.smtpc; - const char *line = data->state.buffer; - size_t len = strlen(line); - - (void)instate; /* no use for this yet */ - - if(smtpcode/100 != 2 && smtpcode != 1) { - if(data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) - result = smtp_perform_helo(data, conn); - else { - failf(data, "Remote access denied: %d", smtpcode); - result = CURLE_REMOTE_ACCESS_DENIED; - } - } - else if(len >= 4) { - line += 4; - len -= 4; - - /* Does the server support the STARTTLS capability? */ - if(len >= 8 && !memcmp(line, "STARTTLS", 8)) - smtpc->tls_supported = TRUE; - - /* Does the server support the SIZE capability? */ - else if(len >= 4 && !memcmp(line, "SIZE", 4)) - smtpc->size_supported = TRUE; - - /* Does the server support the UTF-8 capability? */ - else if(len >= 8 && !memcmp(line, "SMTPUTF8", 8)) - smtpc->utf8_supported = TRUE; - - /* Does the server support authentication? */ - else if(len >= 5 && !memcmp(line, "AUTH ", 5)) { - smtpc->auth_supported = TRUE; - - /* Advance past the AUTH keyword */ - line += 5; - len -= 5; - - /* Loop through the data line */ - for(;;) { - size_t llen; - size_t wordlen; - unsigned short mechbit; - - while(len && - (*line == ' ' || *line == '\t' || - *line == '\r' || *line == '\n')) { - - line++; - len--; - } - - if(!len) - break; - - /* Extract the word */ - for(wordlen = 0; wordlen < len && line[wordlen] != ' ' && - line[wordlen] != '\t' && line[wordlen] != '\r' && - line[wordlen] != '\n';) - wordlen++; - - /* Test the word for a matching authentication mechanism */ - mechbit = Curl_sasl_decode_mech(line, wordlen, &llen); - if(mechbit && llen == wordlen) - smtpc->sasl.authmechs |= mechbit; - - line += wordlen; - len -= wordlen; - } - } - - if(smtpcode != 1) { - if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { - /* We don't have a SSL/TLS connection yet, but SSL is requested */ - if(smtpc->tls_supported) - /* Switch to TLS connection now */ - result = smtp_perform_starttls(data, conn); - else if(data->set.use_ssl == CURLUSESSL_TRY) - /* Fallback and carry on with authentication */ - result = smtp_perform_authentication(data); - else { - failf(data, "STARTTLS not supported."); - result = CURLE_USE_SSL_FAILED; - } - } - else - result = smtp_perform_authentication(data); - } - } - else { - failf(data, "Unexpectedly short EHLO response"); - result = CURLE_WEIRD_SERVER_REPLY; - } - - return result; -} - -/* For HELO responses */ -static CURLcode smtp_state_helo_resp(struct Curl_easy *data, int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - (void)instate; /* no use for this yet */ - - if(smtpcode/100 != 2) { - failf(data, "Remote access denied: %d", smtpcode); - result = CURLE_REMOTE_ACCESS_DENIED; - } - else - /* End of connect phase */ - state(data, SMTP_STOP); - - return result; -} - -/* For SASL authentication responses */ -static CURLcode smtp_state_auth_resp(struct Curl_easy *data, - int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct smtp_conn *smtpc = &conn->proto.smtpc; - saslprogress progress; - - (void)instate; /* no use for this yet */ - - result = Curl_sasl_continue(&smtpc->sasl, data, smtpcode, &progress); - if(!result) - switch(progress) { - case SASL_DONE: - state(data, SMTP_STOP); /* Authenticated */ - break; - case SASL_IDLE: /* No mechanism left after cancellation */ - failf(data, "Authentication cancelled"); - result = CURLE_LOGIN_DENIED; - break; - default: - break; - } - - return result; -} - -/* For command responses */ -static CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - struct SMTP *smtp = data->req.p.smtp; - char *line = data->state.buffer; - size_t len = strlen(line); - - (void)instate; /* no use for this yet */ - - if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) || - (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) { - failf(data, "Command failed: %d", smtpcode); - result = CURLE_WEIRD_SERVER_REPLY; - } - else { - /* Temporarily add the LF character back and send as body to the client */ - if(!data->set.opt_no_body) { - line[len] = '\n'; - result = Curl_client_write(data, CLIENTWRITE_BODY, line, len + 1); - line[len] = '\0'; - } - - if(smtpcode != 1) { - if(smtp->rcpt) { - smtp->rcpt = smtp->rcpt->next; - - if(smtp->rcpt) { - /* Send the next command */ - result = smtp_perform_command(data); - } - else - /* End of DO phase */ - state(data, SMTP_STOP); - } - else - /* End of DO phase */ - state(data, SMTP_STOP); - } - } - - return result; -} - -/* For MAIL responses */ -static CURLcode smtp_state_mail_resp(struct Curl_easy *data, int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - (void)instate; /* no use for this yet */ - - if(smtpcode/100 != 2) { - failf(data, "MAIL failed: %d", smtpcode); - result = CURLE_SEND_ERROR; - } - else - /* Start the RCPT TO command */ - result = smtp_perform_rcpt_to(data); - - return result; -} - -/* For RCPT responses */ -static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data, - struct connectdata *conn, int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - struct SMTP *smtp = data->req.p.smtp; - bool is_smtp_err = FALSE; - bool is_smtp_blocking_err = FALSE; - - (void)instate; /* no use for this yet */ - - is_smtp_err = (smtpcode/100 != 2) ? TRUE : FALSE; - - /* If there's multiple RCPT TO to be issued, it's possible to ignore errors - and proceed with only the valid addresses. */ - is_smtp_blocking_err = - (is_smtp_err && !data->set.mail_rcpt_allowfails) ? TRUE : FALSE; - - if(is_smtp_err) { - /* Remembering the last failure which we can report if all "RCPT TO" have - failed and we cannot proceed. */ - smtp->rcpt_last_error = smtpcode; - - if(is_smtp_blocking_err) { - failf(data, "RCPT failed: %d", smtpcode); - result = CURLE_SEND_ERROR; - } - } - else { - /* Some RCPT TO commands have succeeded. */ - smtp->rcpt_had_ok = TRUE; - } - - if(!is_smtp_blocking_err) { - smtp->rcpt = smtp->rcpt->next; - - if(smtp->rcpt) - /* Send the next RCPT TO command */ - result = smtp_perform_rcpt_to(data); - else { - /* We weren't able to issue a successful RCPT TO command while going - over recipients (potentially multiple). Sending back last error. */ - if(!smtp->rcpt_had_ok) { - failf(data, "RCPT failed: %d (last error)", smtp->rcpt_last_error); - result = CURLE_SEND_ERROR; - } - else { - /* Send the DATA command */ - result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "DATA"); - - if(!result) - state(data, SMTP_DATA); - } - } - } - - return result; -} - -/* For DATA response */ -static CURLcode smtp_state_data_resp(struct Curl_easy *data, int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - (void)instate; /* no use for this yet */ - - if(smtpcode != 354) { - failf(data, "DATA failed: %d", smtpcode); - result = CURLE_SEND_ERROR; - } - else { - /* Set the progress upload size */ - Curl_pgrsSetUploadSize(data, data->state.infilesize); - - /* SMTP upload */ - Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); - - /* End of DO phase */ - state(data, SMTP_STOP); - } - - return result; -} - -/* For POSTDATA responses, which are received after the entire DATA - part has been sent to the server */ -static CURLcode smtp_state_postdata_resp(struct Curl_easy *data, - int smtpcode, - smtpstate instate) -{ - CURLcode result = CURLE_OK; - - (void)instate; /* no use for this yet */ - - if(smtpcode != 250) - result = CURLE_WEIRD_SERVER_REPLY; - - /* End of DONE phase */ - state(data, SMTP_STOP); - - return result; -} - -static CURLcode smtp_statemachine(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; - int smtpcode; - struct smtp_conn *smtpc = &conn->proto.smtpc; - struct pingpong *pp = &smtpc->pp; - size_t nread = 0; - - /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */ - if(smtpc->state == SMTP_UPGRADETLS) - return smtp_perform_upgrade_tls(data); - - /* Flush any data that needs to be sent */ - if(pp->sendleft) - return Curl_pp_flushsend(data, pp); - - do { - /* Read the response from the server */ - result = Curl_pp_readresp(data, sock, pp, &smtpcode, &nread); - if(result) - return result; - - /* Store the latest response for later retrieval if necessary */ - if(smtpc->state != SMTP_QUIT && smtpcode != 1) - data->info.httpcode = smtpcode; - - if(!smtpcode) - break; - - /* We have now received a full SMTP server response */ - switch(smtpc->state) { - case SMTP_SERVERGREET: - result = smtp_state_servergreet_resp(data, smtpcode, smtpc->state); - break; - - case SMTP_EHLO: - result = smtp_state_ehlo_resp(data, conn, smtpcode, smtpc->state); - break; - - case SMTP_HELO: - result = smtp_state_helo_resp(data, smtpcode, smtpc->state); - break; - - case SMTP_STARTTLS: - result = smtp_state_starttls_resp(data, smtpcode, smtpc->state); - break; - - case SMTP_AUTH: - result = smtp_state_auth_resp(data, smtpcode, smtpc->state); - break; - - case SMTP_COMMAND: - result = smtp_state_command_resp(data, smtpcode, smtpc->state); - break; - - case SMTP_MAIL: - result = smtp_state_mail_resp(data, smtpcode, smtpc->state); - break; - - case SMTP_RCPT: - result = smtp_state_rcpt_resp(data, conn, smtpcode, smtpc->state); - break; - - case SMTP_DATA: - result = smtp_state_data_resp(data, smtpcode, smtpc->state); - break; - - case SMTP_POSTDATA: - result = smtp_state_postdata_resp(data, smtpcode, smtpc->state); - break; - - case SMTP_QUIT: - /* fallthrough, just stop! */ - default: - /* internal error */ - state(data, SMTP_STOP); - break; - } - } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp)); - - return result; -} - -/* Called repeatedly until done from multi.c */ -static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct smtp_conn *smtpc = &conn->proto.smtpc; - - if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) { - result = Curl_ssl_connect_nonblocking(data, conn, FALSE, - FIRSTSOCKET, &smtpc->ssldone); - if(result || !smtpc->ssldone) - return result; - } - - result = Curl_pp_statemach(data, &smtpc->pp, FALSE, FALSE); - *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE; - - return result; -} - -static CURLcode smtp_block_statemach(struct Curl_easy *data, - struct connectdata *conn, - bool disconnecting) -{ - CURLcode result = CURLE_OK; - struct smtp_conn *smtpc = &conn->proto.smtpc; - - while(smtpc->state != SMTP_STOP && !result) - result = Curl_pp_statemach(data, &smtpc->pp, TRUE, disconnecting); - - return result; -} - -/* Allocate and initialize the SMTP struct for the current Curl_easy if - required */ -static CURLcode smtp_init(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct SMTP *smtp; - - smtp = data->req.p.smtp = calloc(sizeof(struct SMTP), 1); - if(!smtp) - result = CURLE_OUT_OF_MEMORY; - - return result; -} - -/* For the SMTP "protocol connect" and "doing" phases only */ -static int smtp_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks) -{ - return Curl_pp_getsock(data, &conn->proto.smtpc.pp, socks); -} - -/*********************************************************************** - * - * smtp_connect() - * - * This function should do everything that is to be considered a part of - * the connection phase. - * - * The variable pointed to by 'done' will be TRUE if the protocol-layer - * connect phase is done when this function returns, or FALSE if not. - */ -static CURLcode smtp_connect(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct smtp_conn *smtpc = &conn->proto.smtpc; - struct pingpong *pp = &smtpc->pp; - - *done = FALSE; /* default to not done yet */ - - /* We always support persistent connections in SMTP */ - connkeep(conn, "SMTP default"); - - PINGPONG_SETUP(pp, smtp_statemachine, smtp_endofresp); - - /* Initialize the SASL storage */ - Curl_sasl_init(&smtpc->sasl, data, &saslsmtp); - - /* Initialise the pingpong layer */ - Curl_pp_setup(pp); - Curl_pp_init(data, pp); - - /* Parse the URL options */ - result = smtp_parse_url_options(conn); - if(result) - return result; - - /* Parse the URL path */ - result = smtp_parse_url_path(data); - if(result) - return result; - - /* Start off waiting for the server greeting response */ - state(data, SMTP_SERVERGREET); - - result = smtp_multi_statemach(data, done); - - return result; -} - -/*********************************************************************** - * - * smtp_done() - * - * The DONE function. This does what needs to be done after a single DO has - * performed. - * - * Input argument is already checked for validity. - */ -static CURLcode smtp_done(struct Curl_easy *data, CURLcode status, - bool premature) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct SMTP *smtp = data->req.p.smtp; - struct pingpong *pp = &conn->proto.smtpc.pp; - char *eob; - ssize_t len; - ssize_t bytes_written; - - (void)premature; - - if(!smtp) - return CURLE_OK; - - /* Cleanup our per-request based variables */ - Curl_safefree(smtp->custom); - - if(status) { - connclose(conn, "SMTP done with bad status"); /* marked for closure */ - result = status; /* use the already set error code */ - } - else if(!data->set.connect_only && data->set.mail_rcpt && - (data->set.upload || data->set.mimepost.kind)) { - /* Calculate the EOB taking into account any terminating CRLF from the - previous line of the email or the CRLF of the DATA command when there - is "no mail data". RFC-5321, sect. 4.1.1.4. - - Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to - fail when using a different pointer following a previous write, that - returned CURLE_AGAIN, we duplicate the EOB now rather than when the - bytes written doesn't equal len. */ - if(smtp->trailing_crlf || !data->state.infilesize) { - eob = strdup(&SMTP_EOB[2]); - len = SMTP_EOB_LEN - 2; - } - else { - eob = strdup(SMTP_EOB); - len = SMTP_EOB_LEN; - } - - if(!eob) - return CURLE_OUT_OF_MEMORY; - - /* Send the end of block data */ - result = Curl_write(data, conn->writesockfd, eob, len, &bytes_written); - if(result) { - free(eob); - return result; - } - - if(bytes_written != len) { - /* The whole chunk was not sent so keep it around and adjust the - pingpong structure accordingly */ - pp->sendthis = eob; - pp->sendsize = len; - pp->sendleft = len - bytes_written; - } - else { - /* Successfully sent so adjust the response timeout relative to now */ - pp->response = Curl_now(); - - free(eob); - } - - state(data, SMTP_POSTDATA); - - /* Run the state-machine */ - result = smtp_block_statemach(data, conn, FALSE); - } - - /* Clear the transfer mode for the next request */ - smtp->transfer = PPTRANSFER_BODY; - - return result; -} - -/*********************************************************************** - * - * smtp_perform() - * - * This is the actual DO function for SMTP. Transfer a mail, send a command - * or get some data according to the options previously setup. - */ -static CURLcode smtp_perform(struct Curl_easy *data, bool *connected, - bool *dophase_done) -{ - /* This is SMTP and no proxy */ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct SMTP *smtp = data->req.p.smtp; - - DEBUGF(infof(data, "DO phase starts")); - - if(data->set.opt_no_body) { - /* Requested no body means no transfer */ - smtp->transfer = PPTRANSFER_INFO; - } - - *dophase_done = FALSE; /* not done yet */ - - /* Store the first recipient (or NULL if not specified) */ - smtp->rcpt = data->set.mail_rcpt; - - /* Track of whether we've successfully sent at least one RCPT TO command */ - smtp->rcpt_had_ok = FALSE; - - /* Track of the last error we've received by sending RCPT TO command */ - smtp->rcpt_last_error = 0; - - /* Initial data character is the first character in line: it is implicitly - preceded by a virtual CRLF. */ - smtp->trailing_crlf = TRUE; - smtp->eob = 2; - - /* Start the first command in the DO phase */ - if((data->set.upload || data->set.mimepost.kind) && data->set.mail_rcpt) - /* MAIL transfer */ - result = smtp_perform_mail(data); - else - /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */ - result = smtp_perform_command(data); - - if(result) - return result; - - /* Run the state-machine */ - result = smtp_multi_statemach(data, dophase_done); - - *connected = conn->bits.tcpconnect[FIRSTSOCKET]; - - if(*dophase_done) - DEBUGF(infof(data, "DO phase is complete")); - - return result; -} - -/*********************************************************************** - * - * smtp_do() - * - * This function is registered as 'curl_do' function. It decodes the path - * parts etc as a wrapper to the actual DO function (smtp_perform). - * - * The input argument is already checked for validity. - */ -static CURLcode smtp_do(struct Curl_easy *data, bool *done) -{ - CURLcode result = CURLE_OK; - *done = FALSE; /* default to false */ - - /* Parse the custom request */ - result = smtp_parse_custom_request(data); - if(result) - return result; - - result = smtp_regular_transfer(data, done); - - return result; -} - -/*********************************************************************** - * - * smtp_disconnect() - * - * Disconnect from an SMTP server. Cleanup protocol-specific per-connection - * resources. BLOCKING. - */ -static CURLcode smtp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection) -{ - struct smtp_conn *smtpc = &conn->proto.smtpc; - (void)data; - - /* We cannot send quit unconditionally. If this connection is stale or - bad in any way, sending quit and waiting around here will make the - disconnect wait in vain and cause more problems than we need to. */ - - if(!dead_connection && conn->bits.protoconnstart) { - if(!smtp_perform_quit(data, conn)) - (void)smtp_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */ - } - - /* Disconnect from the server */ - Curl_pp_disconnect(&smtpc->pp); - - /* Cleanup the SASL module */ - Curl_sasl_cleanup(conn, smtpc->sasl.authused); - - /* Cleanup our connection based variables */ - Curl_safefree(smtpc->domain); - - return CURLE_OK; -} - -/* Call this when the DO phase has completed */ -static CURLcode smtp_dophase_done(struct Curl_easy *data, bool connected) -{ - struct SMTP *smtp = data->req.p.smtp; - - (void)connected; - - if(smtp->transfer != PPTRANSFER_BODY) - /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); - - return CURLE_OK; -} - -/* Called from multi.c while DOing */ -static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done) -{ - CURLcode result = smtp_multi_statemach(data, dophase_done); - - if(result) - DEBUGF(infof(data, "DO phase failed")); - else if(*dophase_done) { - result = smtp_dophase_done(data, FALSE /* not connected */); - - DEBUGF(infof(data, "DO phase is complete")); - } - - return result; -} - -/*********************************************************************** - * - * smtp_regular_transfer() - * - * The input argument is already checked for validity. - * - * Performs all commands done before a regular transfer between a local and a - * remote host. - */ -static CURLcode smtp_regular_transfer(struct Curl_easy *data, - bool *dophase_done) -{ - CURLcode result = CURLE_OK; - bool connected = FALSE; - - /* Make sure size is unknown at this point */ - data->req.size = -1; - - /* Set the progress data */ - Curl_pgrsSetUploadCounter(data, 0); - Curl_pgrsSetDownloadCounter(data, 0); - Curl_pgrsSetUploadSize(data, -1); - Curl_pgrsSetDownloadSize(data, -1); - - /* Carry out the perform */ - result = smtp_perform(data, &connected, dophase_done); - - /* Perform post DO phase operations if necessary */ - if(!result && *dophase_done) - result = smtp_dophase_done(data, connected); - - return result; -} - -static CURLcode smtp_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result; - - /* Clear the TLS upgraded flag */ - conn->bits.tls_upgraded = FALSE; - - /* Initialise the SMTP layer */ - result = smtp_init(data); - if(result) - return result; - - return CURLE_OK; -} - -/*********************************************************************** - * - * smtp_parse_url_options() - * - * Parse the URL login options. - */ -static CURLcode smtp_parse_url_options(struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - struct smtp_conn *smtpc = &conn->proto.smtpc; - const char *ptr = conn->options; - - while(!result && ptr && *ptr) { - const char *key = ptr; - const char *value; - - while(*ptr && *ptr != '=') - ptr++; - - value = ptr + 1; - - while(*ptr && *ptr != ';') - ptr++; - - if(strncasecompare(key, "AUTH=", 5)) - result = Curl_sasl_parse_url_auth_option(&smtpc->sasl, - value, ptr - value); - else - result = CURLE_URL_MALFORMAT; - - if(*ptr == ';') - ptr++; - } - - return result; -} - -/*********************************************************************** - * - * smtp_parse_url_path() - * - * Parse the URL path into separate path components. - */ -static CURLcode smtp_parse_url_path(struct Curl_easy *data) -{ - /* The SMTP struct is already initialised in smtp_connect() */ - struct connectdata *conn = data->conn; - struct smtp_conn *smtpc = &conn->proto.smtpc; - const char *path = &data->state.up.path[1]; /* skip leading path */ - char localhost[HOSTNAME_MAX + 1]; - - /* Calculate the path if necessary */ - if(!*path) { - if(!Curl_gethostname(localhost, sizeof(localhost))) - path = localhost; - else - path = "localhost"; - } - - /* URL decode the path and use it as the domain in our EHLO */ - return Curl_urldecode(path, 0, &smtpc->domain, NULL, REJECT_CTRL); -} - -/*********************************************************************** - * - * smtp_parse_custom_request() - * - * Parse the custom request. - */ -static CURLcode smtp_parse_custom_request(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct SMTP *smtp = data->req.p.smtp; - const char *custom = data->set.str[STRING_CUSTOMREQUEST]; - - /* URL decode the custom request */ - if(custom) - result = Curl_urldecode(custom, 0, &smtp->custom, NULL, REJECT_CTRL); - - return result; -} - -/*********************************************************************** - * - * smtp_parse_address() - * - * Parse the fully qualified mailbox address into a local address part and the - * host name, converting the host name to an IDN A-label, as per RFC-5890, if - * necessary. - * - * Parameters: - * - * conn [in] - The connection handle. - * fqma [in] - The fully qualified mailbox address (which may or - * may not contain UTF-8 characters). - * address [in/out] - A new allocated buffer which holds the local - * address part of the mailbox. This buffer must be - * free'ed by the caller. - * host [in/out] - The host name structure that holds the original, - * and optionally encoded, host name. - * Curl_free_idnconverted_hostname() must be called - * once the caller has finished with the structure. - * - * Returns CURLE_OK on success. - * - * Notes: - * - * Should a UTF-8 host name require conversion to IDN ACE and we cannot honor - * that conversion then we shall return success. This allow the caller to send - * the data to the server as a U-label (as per RFC-6531 sect. 3.2). - * - * If an mailbox '@' separator cannot be located then the mailbox is considered - * to be either a local mailbox or an invalid mailbox (depending on what the - * calling function deems it to be) then the input will simply be returned in - * the address part with the host name being NULL. - */ -static CURLcode smtp_parse_address(struct Curl_easy *data, const char *fqma, - char **address, struct hostname *host) -{ - CURLcode result = CURLE_OK; - size_t length; - - /* Duplicate the fully qualified email address so we can manipulate it, - ensuring it doesn't contain the delimiters if specified */ - char *dup = strdup(fqma[0] == '<' ? fqma + 1 : fqma); - if(!dup) - return CURLE_OUT_OF_MEMORY; - - length = strlen(dup); - if(length) { - if(dup[length - 1] == '>') - dup[length - 1] = '\0'; - } - - /* Extract the host name from the address (if we can) */ - host->name = strpbrk(dup, "@"); - if(host->name) { - *host->name = '\0'; - host->name = host->name + 1; - - /* Attempt to convert the host name to IDN ACE */ - (void) Curl_idnconvert_hostname(data, host); - - /* If Curl_idnconvert_hostname() fails then we shall attempt to continue - and send the host name using UTF-8 rather than as 7-bit ACE (which is - our preference) */ - } - - /* Extract the local address from the mailbox */ - *address = dup; - - return result; -} - -CURLcode Curl_smtp_escape_eob(struct Curl_easy *data, - const ssize_t nread, - const ssize_t offset) -{ - /* When sending a SMTP payload we must detect CRLF. sequences making sure - they are sent as CRLF.. instead, as a . on the beginning of a line will - be deleted by the server when not part of an EOB terminator and a - genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of - data by the server - */ - ssize_t i; - ssize_t si; - struct SMTP *smtp = data->req.p.smtp; - char *scratch = data->state.scratch; - char *newscratch = NULL; - char *oldscratch = NULL; - size_t eob_sent; - - /* Do we need to allocate a scratch buffer? */ - if(!scratch || data->set.crlf) { - oldscratch = scratch; - - scratch = newscratch = malloc(2 * data->set.upload_buffer_size); - if(!newscratch) { - failf(data, "Failed to alloc scratch buffer"); - - return CURLE_OUT_OF_MEMORY; - } - } - DEBUGASSERT((size_t)data->set.upload_buffer_size >= (size_t)nread); - - /* Have we already sent part of the EOB? */ - eob_sent = smtp->eob; - - /* This loop can be improved by some kind of Boyer-Moore style of - approach but that is saved for later... */ - if(offset) - memcpy(scratch, data->req.upload_fromhere, offset); - for(i = offset, si = offset; i < nread; i++) { - if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) { - smtp->eob++; - - /* Is the EOB potentially the terminating CRLF? */ - if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob) - smtp->trailing_crlf = TRUE; - else - smtp->trailing_crlf = FALSE; - } - else if(smtp->eob) { - /* A previous substring matched so output that first */ - memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent); - si += smtp->eob - eob_sent; - - /* Then compare the first byte */ - if(SMTP_EOB[0] == data->req.upload_fromhere[i]) - smtp->eob = 1; - else - smtp->eob = 0; - - eob_sent = 0; - - /* Reset the trailing CRLF flag as there was more data */ - smtp->trailing_crlf = FALSE; - } - - /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */ - if(SMTP_EOB_FIND_LEN == smtp->eob) { - /* Copy the replacement data to the target buffer */ - memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent], - SMTP_EOB_REPL_LEN - eob_sent); - si += SMTP_EOB_REPL_LEN - eob_sent; - smtp->eob = 0; - eob_sent = 0; - } - else if(!smtp->eob) - scratch[si++] = data->req.upload_fromhere[i]; - } - - if(smtp->eob - eob_sent) { - /* A substring matched before processing ended so output that now */ - memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent); - si += smtp->eob - eob_sent; - } - - /* Only use the new buffer if we replaced something */ - if(si != nread) { - /* Upload from the new (replaced) buffer instead */ - data->req.upload_fromhere = scratch; - - /* Save the buffer so it can be freed later */ - data->state.scratch = scratch; - - /* Free the old scratch buffer */ - free(oldscratch); - - /* Set the new amount too */ - data->req.upload_present = si; - } - else - free(newscratch); - - return CURLE_OK; -} - -#endif /* CURL_DISABLE_SMTP */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ * RFC1870 SMTP Service Extension for Message Size
+ * RFC2195 CRAM-MD5 authentication
+ * RFC2831 DIGEST-MD5 authentication
+ * RFC3207 SMTP over TLS
+ * RFC4422 Simple Authentication and Security Layer (SASL)
+ * RFC4616 PLAIN authentication
+ * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
+ * RFC4954 SMTP Authentication
+ * RFC5321 SMTP protocol
+ * RFC5890 Internationalized Domain Names for Applications (IDNA)
+ * RFC6531 SMTP Extension for Internationalized Email
+ * RFC6532 Internationalized Email Headers
+ * RFC6749 OAuth 2.0 Authorization Framework
+ * RFC8314 Use of TLS for Email Submission and Access
+ * Draft SMTP URL Interface <draft-earhart-url-smtp-00.txt>
+ * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_SMTP
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#include <curl/curl.h>
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "progress.h"
+#include "transfer.h"
+#include "escape.h"
+#include "http.h" /* for HTTP proxy tunnel stuff */
+#include "mime.h"
+#include "socks.h"
+#include "smtp.h"
+#include "strtoofft.h"
+#include "strcase.h"
+#include "vtls/vtls.h"
+#include "cfilters.h"
+#include "connect.h"
+#include "select.h"
+#include "multiif.h"
+#include "url.h"
+#include "curl_gethostname.h"
+#include "bufref.h"
+#include "curl_sasl.h"
+#include "warnless.h"
+#include "idn.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* Local API functions */
+static CURLcode smtp_regular_transfer(struct Curl_easy *data, bool *done);
+static CURLcode smtp_do(struct Curl_easy *data, bool *done);
+static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
+ bool premature);
+static CURLcode smtp_connect(struct Curl_easy *data, bool *done);
+static CURLcode smtp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn, bool dead);
+static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done);
+static int smtp_getsock(struct Curl_easy *data,
+ struct connectdata *conn, curl_socket_t *socks);
+static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done);
+static CURLcode smtp_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn);
+static CURLcode smtp_parse_url_options(struct connectdata *conn);
+static CURLcode smtp_parse_url_path(struct Curl_easy *data);
+static CURLcode smtp_parse_custom_request(struct Curl_easy *data);
+static CURLcode smtp_parse_address(const char *fqma,
+ char **address, struct hostname *host);
+static CURLcode smtp_perform_auth(struct Curl_easy *data, const char *mech,
+ const struct bufref *initresp);
+static CURLcode smtp_continue_auth(struct Curl_easy *data, const char *mech,
+ const struct bufref *resp);
+static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech);
+static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out);
+
+/*
+ * SMTP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_smtp = {
+ "SMTP", /* scheme */
+ smtp_setup_connection, /* setup_connection */
+ smtp_do, /* do_it */
+ smtp_done, /* done */
+ ZERO_NULL, /* do_more */
+ smtp_connect, /* connect_it */
+ smtp_multi_statemach, /* connecting */
+ smtp_doing, /* doing */
+ smtp_getsock, /* proto_getsock */
+ smtp_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ smtp_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_SMTP, /* defport */
+ CURLPROTO_SMTP, /* protocol */
+ CURLPROTO_SMTP, /* family */
+ PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
+ PROTOPT_URLOPTIONS
+};
+
+#ifdef USE_SSL
+/*
+ * SMTPS protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_smtps = {
+ "SMTPS", /* scheme */
+ smtp_setup_connection, /* setup_connection */
+ smtp_do, /* do_it */
+ smtp_done, /* done */
+ ZERO_NULL, /* do_more */
+ smtp_connect, /* connect_it */
+ smtp_multi_statemach, /* connecting */
+ smtp_doing, /* doing */
+ smtp_getsock, /* proto_getsock */
+ smtp_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ smtp_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_SMTPS, /* defport */
+ CURLPROTO_SMTPS, /* protocol */
+ CURLPROTO_SMTP, /* family */
+ PROTOPT_CLOSEACTION | PROTOPT_SSL
+ | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
+};
+#endif
+
+/* SASL parameters for the smtp protocol */
+static const struct SASLproto saslsmtp = {
+ "smtp", /* The service name */
+ smtp_perform_auth, /* Send authentication command */
+ smtp_continue_auth, /* Send authentication continuation */
+ smtp_cancel_auth, /* Cancel authentication */
+ smtp_get_message, /* Get SASL response message */
+ 512 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */
+ 334, /* Code received when continuation is expected */
+ 235, /* Code to receive upon authentication success */
+ SASL_AUTH_DEFAULT, /* Default mechanisms */
+ SASL_FLAG_BASE64 /* Configuration flags */
+};
+
+#ifdef USE_SSL
+static void smtp_to_smtps(struct connectdata *conn)
+{
+ /* Change the connection handler */
+ conn->handler = &Curl_handler_smtps;
+
+ /* Set the connection's upgraded to TLS flag */
+ conn->bits.tls_upgraded = TRUE;
+}
+#else
+#define smtp_to_smtps(x) Curl_nop_stmt
+#endif
+
+/***********************************************************************
+ *
+ * smtp_endofresp()
+ *
+ * Checks for an ending SMTP status code at the start of the given string, but
+ * also detects various capabilities from the EHLO response including the
+ * supported authentication mechanisms.
+ */
+static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn,
+ char *line, size_t len, int *resp)
+{
+ struct smtp_conn *smtpc = &conn->proto.smtpc;
+ bool result = FALSE;
+ (void)data;
+
+ /* Nothing for us */
+ if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
+ return FALSE;
+
+ /* Do we have a command response? This should be the response code followed
+ by a space and optionally some text as per RFC-5321 and as outlined in
+ Section 4. Examples of RFC-4954 but some email servers ignore this and
+ only send the response code instead as per Section 4.2. */
+ if(line[3] == ' ' || len == 5) {
+ char tmpline[6];
+
+ result = TRUE;
+ memset(tmpline, '\0', sizeof(tmpline));
+ memcpy(tmpline, line, (len == 5 ? 5 : 3));
+ *resp = curlx_sltosi(strtol(tmpline, NULL, 10));
+
+ /* Make sure real server never sends internal value */
+ if(*resp == 1)
+ *resp = 0;
+ }
+ /* Do we have a multiline (continuation) response? */
+ else if(line[3] == '-' &&
+ (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
+ result = TRUE;
+ *resp = 1; /* Internal response code */
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_get_message()
+ *
+ * Gets the authentication message from the response buffer.
+ */
+static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out)
+{
+ char *message = data->state.buffer;
+ size_t len = strlen(message);
+
+ if(len > 4) {
+ /* Find the start of the message */
+ len -= 4;
+ for(message += 4; *message == ' ' || *message == '\t'; message++, len--)
+ ;
+
+ /* Find the end of the message */
+ while(len--)
+ if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
+ message[len] != '\t')
+ break;
+
+ /* Terminate the message */
+ message[++len] = '\0';
+ Curl_bufref_set(out, message, len, NULL);
+ }
+ else
+ /* junk input => zero length output */
+ Curl_bufref_set(out, "", 0, NULL);
+
+ return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * state()
+ *
+ * This is the ONLY way to change SMTP state!
+ */
+static void state(struct Curl_easy *data, smtpstate newstate)
+{
+ struct smtp_conn *smtpc = &data->conn->proto.smtpc;
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ /* for debug purposes */
+ static const char * const names[] = {
+ "STOP",
+ "SERVERGREET",
+ "EHLO",
+ "HELO",
+ "STARTTLS",
+ "UPGRADETLS",
+ "AUTH",
+ "COMMAND",
+ "MAIL",
+ "RCPT",
+ "DATA",
+ "POSTDATA",
+ "QUIT",
+ /* LAST */
+ };
+
+ if(smtpc->state != newstate)
+ infof(data, "SMTP %p state change from %s to %s",
+ (void *)smtpc, names[smtpc->state], names[newstate]);
+#endif
+
+ smtpc->state = newstate;
+}
+
+/***********************************************************************
+ *
+ * smtp_perform_ehlo()
+ *
+ * Sends the EHLO command to not only initialise communication with the ESMTP
+ * server but to also obtain a list of server side supported capabilities.
+ */
+static CURLcode smtp_perform_ehlo(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct smtp_conn *smtpc = &conn->proto.smtpc;
+
+ smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */
+ smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism
+ used for esmtp connections */
+ smtpc->tls_supported = FALSE; /* Clear the TLS capability */
+ smtpc->auth_supported = FALSE; /* Clear the AUTH capability */
+
+ /* Send the EHLO command */
+ result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain);
+
+ if(!result)
+ state(data, SMTP_EHLO);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_perform_helo()
+ *
+ * Sends the HELO command to initialise communication with the SMTP server.
+ */
+static CURLcode smtp_perform_helo(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct smtp_conn *smtpc = &conn->proto.smtpc;
+
+ smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used
+ in smtp connections */
+
+ /* Send the HELO command */
+ result = Curl_pp_sendf(data, &smtpc->pp, "HELO %s", smtpc->domain);
+
+ if(!result)
+ state(data, SMTP_HELO);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_perform_starttls()
+ *
+ * Sends the STLS command to start the upgrade to TLS.
+ */
+static CURLcode smtp_perform_starttls(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ /* Send the STARTTLS command */
+ CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
+ "%s", "STARTTLS");
+
+ if(!result)
+ state(data, SMTP_STARTTLS);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_perform_upgrade_tls()
+ *
+ * Performs the upgrade to TLS.
+ */
+static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data)
+{
+ /* Start the SSL connection */
+ struct connectdata *conn = data->conn;
+ struct smtp_conn *smtpc = &conn->proto.smtpc;
+ CURLcode result;
+
+ if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) {
+ result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
+ if(result)
+ goto out;
+ }
+
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &smtpc->ssldone);
+ if(!result) {
+ if(smtpc->state != SMTP_UPGRADETLS)
+ state(data, SMTP_UPGRADETLS);
+
+ if(smtpc->ssldone) {
+ smtp_to_smtps(conn);
+ result = smtp_perform_ehlo(data);
+ }
+ }
+out:
+ return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_perform_auth()
+ *
+ * Sends an AUTH command allowing the client to login with the given SASL
+ * authentication mechanism.
+ */
+static CURLcode smtp_perform_auth(struct Curl_easy *data,
+ const char *mech,
+ const struct bufref *initresp)
+{
+ CURLcode result = CURLE_OK;
+ struct smtp_conn *smtpc = &data->conn->proto.smtpc;
+ const char *ir = (const char *) Curl_bufref_ptr(initresp);
+
+ if(ir) { /* AUTH <mech> ...<crlf> */
+ /* Send the AUTH command with the initial response */
+ result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s", mech, ir);
+ }
+ else {
+ /* Send the AUTH command */
+ result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s", mech);
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_continue_auth()
+ *
+ * Sends SASL continuation data.
+ */
+static CURLcode smtp_continue_auth(struct Curl_easy *data,
+ const char *mech,
+ const struct bufref *resp)
+{
+ struct smtp_conn *smtpc = &data->conn->proto.smtpc;
+
+ (void)mech;
+
+ return Curl_pp_sendf(data, &smtpc->pp,
+ "%s", (const char *) Curl_bufref_ptr(resp));
+}
+
+/***********************************************************************
+ *
+ * smtp_cancel_auth()
+ *
+ * Sends SASL cancellation.
+ */
+static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech)
+{
+ struct smtp_conn *smtpc = &data->conn->proto.smtpc;
+
+ (void)mech;
+
+ return Curl_pp_sendf(data, &smtpc->pp, "*");
+}
+
+/***********************************************************************
+ *
+ * smtp_perform_authentication()
+ *
+ * Initiates the authentication sequence, with the appropriate SASL
+ * authentication mechanism.
+ */
+static CURLcode smtp_perform_authentication(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct smtp_conn *smtpc = &conn->proto.smtpc;
+ saslprogress progress;
+
+ /* Check we have enough data to authenticate with, and the
+ server supports authentication, and end the connect phase if not */
+ if(!smtpc->auth_supported ||
+ !Curl_sasl_can_authenticate(&smtpc->sasl, data)) {
+ state(data, SMTP_STOP);
+ return result;
+ }
+
+ /* Calculate the SASL login details */
+ result = Curl_sasl_start(&smtpc->sasl, data, FALSE, &progress);
+
+ if(!result) {
+ if(progress == SASL_INPROGRESS)
+ state(data, SMTP_AUTH);
+ else {
+ /* Other mechanisms not supported */
+ infof(data, "No known authentication mechanisms supported");
+ result = CURLE_LOGIN_DENIED;
+ }
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_perform_command()
+ *
+ * Sends a SMTP based command.
+ */
+static CURLcode smtp_perform_command(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct SMTP *smtp = data->req.p.smtp;
+
+ if(smtp->rcpt) {
+ /* We notify the server we are sending UTF-8 data if a) it supports the
+ SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in
+ either the local address or host name parts. This is regardless of
+ whether the host name is encoded using IDN ACE */
+ bool utf8 = FALSE;
+
+ if((!smtp->custom) || (!smtp->custom[0])) {
+ char *address = NULL;
+ struct hostname host = { NULL, NULL, NULL, NULL };
+
+ /* Parse the mailbox to verify into the local address and host name
+ parts, converting the host name to an IDN A-label if necessary */
+ result = smtp_parse_address(smtp->rcpt->data,
+ &address, &host);
+ if(result)
+ return result;
+
+ /* Establish whether we should report SMTPUTF8 to the server for this
+ mailbox as per RFC-6531 sect. 3.1 point 6 */
+ utf8 = (conn->proto.smtpc.utf8_supported) &&
+ ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
+ (!Curl_is_ASCII_name(host.name)));
+
+ /* Send the VRFY command (Note: The host name part may be absent when the
+ host is a local system) */
+ result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "VRFY %s%s%s%s",
+ address,
+ host.name ? "@" : "",
+ host.name ? host.name : "",
+ utf8 ? " SMTPUTF8" : "");
+
+ Curl_free_idnconverted_hostname(&host);
+ free(address);
+ }
+ else {
+ /* Establish whether we should report that we support SMTPUTF8 for EXPN
+ commands to the server as per RFC-6531 sect. 3.1 point 6 */
+ utf8 = (conn->proto.smtpc.utf8_supported) &&
+ (!strcmp(smtp->custom, "EXPN"));
+
+ /* Send the custom recipient based command such as the EXPN command */
+ result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
+ "%s %s%s", smtp->custom,
+ smtp->rcpt->data,
+ utf8 ? " SMTPUTF8" : "");
+ }
+ }
+ else
+ /* Send the non-recipient based command such as HELP */
+ result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s",
+ smtp->custom && smtp->custom[0] != '\0' ?
+ smtp->custom : "HELP");
+
+ if(!result)
+ state(data, SMTP_COMMAND);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_perform_mail()
+ *
+ * Sends an MAIL command to initiate the upload of a message.
+ */
+static CURLcode smtp_perform_mail(struct Curl_easy *data)
+{
+ char *from = NULL;
+ char *auth = NULL;
+ char *size = NULL;
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+
+ /* We notify the server we are sending UTF-8 data if a) it supports the
+ SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in
+ either the local address or host name parts. This is regardless of
+ whether the host name is encoded using IDN ACE */
+ bool utf8 = FALSE;
+
+ /* Calculate the FROM parameter */
+ if(data->set.str[STRING_MAIL_FROM]) {
+ char *address = NULL;
+ struct hostname host = { NULL, NULL, NULL, NULL };
+
+ /* Parse the FROM mailbox into the local address and host name parts,
+ converting the host name to an IDN A-label if necessary */
+ result = smtp_parse_address(data->set.str[STRING_MAIL_FROM],
+ &address, &host);
+ if(result)
+ return result;
+
+ /* Establish whether we should report SMTPUTF8 to the server for this
+ mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
+ utf8 = (conn->proto.smtpc.utf8_supported) &&
+ ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
+ (!Curl_is_ASCII_name(host.name)));
+
+ if(host.name) {
+ from = aprintf("<%s@%s>", address, host.name);
+
+ Curl_free_idnconverted_hostname(&host);
+ }
+ else
+ /* An invalid mailbox was provided but we'll simply let the server worry
+ about that and reply with a 501 error */
+ from = aprintf("<%s>", address);
+
+ free(address);
+ }
+ else
+ /* Null reverse-path, RFC-5321, sect. 3.6.3 */
+ from = strdup("<>");
+
+ if(!from)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Calculate the optional AUTH parameter */
+ if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) {
+ if(data->set.str[STRING_MAIL_AUTH][0] != '\0') {
+ char *address = NULL;
+ struct hostname host = { NULL, NULL, NULL, NULL };
+
+ /* Parse the AUTH mailbox into the local address and host name parts,
+ converting the host name to an IDN A-label if necessary */
+ result = smtp_parse_address(data->set.str[STRING_MAIL_AUTH],
+ &address, &host);
+ if(result) {
+ free(from);
+ return result;
+ }
+
+ /* Establish whether we should report SMTPUTF8 to the server for this
+ mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
+ if((!utf8) && (conn->proto.smtpc.utf8_supported) &&
+ ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
+ (!Curl_is_ASCII_name(host.name))))
+ utf8 = TRUE;
+
+ if(host.name) {
+ auth = aprintf("<%s@%s>", address, host.name);
+
+ Curl_free_idnconverted_hostname(&host);
+ }
+ else
+ /* An invalid mailbox was provided but we'll simply let the server
+ worry about it */
+ auth = aprintf("<%s>", address);
+
+ free(address);
+ }
+ else
+ /* Empty AUTH, RFC-2554, sect. 5 */
+ auth = strdup("<>");
+
+ if(!auth) {
+ free(from);
+
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+
+ /* Prepare the mime data if some. */
+ if(data->set.mimepost.kind != MIMEKIND_NONE) {
+ /* Use the whole structure as data. */
+ data->set.mimepost.flags &= ~MIME_BODY_ONLY;
+
+ /* Add external headers and mime version. */
+ curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
+ result = Curl_mime_prepare_headers(data, &data->set.mimepost, NULL,
+ NULL, MIMESTRATEGY_MAIL);
+
+ if(!result)
+ if(!Curl_checkheaders(data, STRCONST("Mime-Version")))
+ result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
+ "Mime-Version: 1.0");
+
+ /* Make sure we will read the entire mime structure. */
+ if(!result)
+ result = Curl_mime_rewind(&data->set.mimepost);
+
+ if(result) {
+ free(from);
+ free(auth);
+
+ return result;
+ }
+
+ data->state.infilesize = Curl_mime_size(&data->set.mimepost);
+
+ /* Read from mime structure. */
+ data->state.fread_func = (curl_read_callback) Curl_mime_read;
+ data->state.in = (void *) &data->set.mimepost;
+ }
+
+ /* Calculate the optional SIZE parameter */
+ if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) {
+ size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
+
+ if(!size) {
+ free(from);
+ free(auth);
+
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+
+ /* If the mailboxes in the FROM and AUTH parameters don't include a UTF-8
+ based address then quickly scan through the recipient list and check if
+ any there do, as we need to correctly identify our support for SMTPUTF8
+ in the envelope, as per RFC-6531 sect. 3.4 */
+ if(conn->proto.smtpc.utf8_supported && !utf8) {
+ struct SMTP *smtp = data->req.p.smtp;
+ struct curl_slist *rcpt = smtp->rcpt;
+
+ while(rcpt && !utf8) {
+ /* Does the host name contain non-ASCII characters? */
+ if(!Curl_is_ASCII_name(rcpt->data))
+ utf8 = TRUE;
+
+ rcpt = rcpt->next;
+ }
+ }
+
+ /* Send the MAIL command */
+ result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
+ "MAIL FROM:%s%s%s%s%s%s",
+ from, /* Mandatory */
+ auth ? " AUTH=" : "", /* Optional on AUTH support */
+ auth ? auth : "", /* */
+ size ? " SIZE=" : "", /* Optional on SIZE support */
+ size ? size : "", /* */
+ utf8 ? " SMTPUTF8" /* Internationalised mailbox */
+ : ""); /* included in our envelope */
+
+ free(from);
+ free(auth);
+ free(size);
+
+ if(!result)
+ state(data, SMTP_MAIL);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_perform_rcpt_to()
+ *
+ * Sends a RCPT TO command for a given recipient as part of the message upload
+ * process.
+ */
+static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct SMTP *smtp = data->req.p.smtp;
+ char *address = NULL;
+ struct hostname host = { NULL, NULL, NULL, NULL };
+
+ /* Parse the recipient mailbox into the local address and host name parts,
+ converting the host name to an IDN A-label if necessary */
+ result = smtp_parse_address(smtp->rcpt->data,
+ &address, &host);
+ if(result)
+ return result;
+
+ /* Send the RCPT TO command */
+ if(host.name)
+ result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s@%s>",
+ address, host.name);
+ else
+ /* An invalid mailbox was provided but we'll simply let the server worry
+ about that and reply with a 501 error */
+ result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s>",
+ address);
+
+ Curl_free_idnconverted_hostname(&host);
+ free(address);
+
+ if(!result)
+ state(data, SMTP_RCPT);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_perform_quit()
+ *
+ * Performs the quit action prior to sclose() being called.
+ */
+static CURLcode smtp_perform_quit(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ /* Send the QUIT command */
+ CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "QUIT");
+
+ if(!result)
+ state(data, SMTP_QUIT);
+
+ return result;
+}
+
+/* For the initial server greeting */
+static CURLcode smtp_state_servergreet_resp(struct Curl_easy *data,
+ int smtpcode,
+ smtpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ (void)instate; /* no use for this yet */
+
+ if(smtpcode/100 != 2) {
+ failf(data, "Got unexpected smtp-server response: %d", smtpcode);
+ result = CURLE_WEIRD_SERVER_REPLY;
+ }
+ else
+ result = smtp_perform_ehlo(data);
+
+ return result;
+}
+
+/* For STARTTLS responses */
+static CURLcode smtp_state_starttls_resp(struct Curl_easy *data,
+ int smtpcode,
+ smtpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ (void)instate; /* no use for this yet */
+
+ /* Pipelining in response is forbidden. */
+ if(data->conn->proto.smtpc.pp.cache_size)
+ return CURLE_WEIRD_SERVER_REPLY;
+
+ if(smtpcode != 220) {
+ if(data->set.use_ssl != CURLUSESSL_TRY) {
+ failf(data, "STARTTLS denied, code %d", smtpcode);
+ result = CURLE_USE_SSL_FAILED;
+ }
+ else
+ result = smtp_perform_authentication(data);
+ }
+ else
+ result = smtp_perform_upgrade_tls(data);
+
+ return result;
+}
+
+/* For EHLO responses */
+static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
+ struct connectdata *conn, int smtpcode,
+ smtpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct smtp_conn *smtpc = &conn->proto.smtpc;
+ const char *line = data->state.buffer;
+ size_t len = strlen(line);
+
+ (void)instate; /* no use for this yet */
+
+ if(smtpcode/100 != 2 && smtpcode != 1) {
+ if(data->set.use_ssl <= CURLUSESSL_TRY
+ || Curl_conn_is_ssl(data, FIRSTSOCKET))
+ result = smtp_perform_helo(data, conn);
+ else {
+ failf(data, "Remote access denied: %d", smtpcode);
+ result = CURLE_REMOTE_ACCESS_DENIED;
+ }
+ }
+ else if(len >= 4) {
+ line += 4;
+ len -= 4;
+
+ /* Does the server support the STARTTLS capability? */
+ if(len >= 8 && !memcmp(line, "STARTTLS", 8))
+ smtpc->tls_supported = TRUE;
+
+ /* Does the server support the SIZE capability? */
+ else if(len >= 4 && !memcmp(line, "SIZE", 4))
+ smtpc->size_supported = TRUE;
+
+ /* Does the server support the UTF-8 capability? */
+ else if(len >= 8 && !memcmp(line, "SMTPUTF8", 8))
+ smtpc->utf8_supported = TRUE;
+
+ /* Does the server support authentication? */
+ else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
+ smtpc->auth_supported = TRUE;
+
+ /* Advance past the AUTH keyword */
+ line += 5;
+ len -= 5;
+
+ /* Loop through the data line */
+ for(;;) {
+ size_t llen;
+ size_t wordlen;
+ unsigned short mechbit;
+
+ while(len &&
+ (*line == ' ' || *line == '\t' ||
+ *line == '\r' || *line == '\n')) {
+
+ line++;
+ len--;
+ }
+
+ if(!len)
+ break;
+
+ /* Extract the word */
+ for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
+ line[wordlen] != '\t' && line[wordlen] != '\r' &&
+ line[wordlen] != '\n';)
+ wordlen++;
+
+ /* Test the word for a matching authentication mechanism */
+ mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
+ if(mechbit && llen == wordlen)
+ smtpc->sasl.authmechs |= mechbit;
+
+ line += wordlen;
+ len -= wordlen;
+ }
+ }
+
+ if(smtpcode != 1) {
+ if(data->set.use_ssl && !Curl_conn_is_ssl(data, FIRSTSOCKET)) {
+ /* We don't have a SSL/TLS connection yet, but SSL is requested */
+ if(smtpc->tls_supported)
+ /* Switch to TLS connection now */
+ result = smtp_perform_starttls(data, conn);
+ else if(data->set.use_ssl == CURLUSESSL_TRY)
+ /* Fallback and carry on with authentication */
+ result = smtp_perform_authentication(data);
+ else {
+ failf(data, "STARTTLS not supported.");
+ result = CURLE_USE_SSL_FAILED;
+ }
+ }
+ else
+ result = smtp_perform_authentication(data);
+ }
+ }
+ else {
+ failf(data, "Unexpectedly short EHLO response");
+ result = CURLE_WEIRD_SERVER_REPLY;
+ }
+
+ return result;
+}
+
+/* For HELO responses */
+static CURLcode smtp_state_helo_resp(struct Curl_easy *data, int smtpcode,
+ smtpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ (void)instate; /* no use for this yet */
+
+ if(smtpcode/100 != 2) {
+ failf(data, "Remote access denied: %d", smtpcode);
+ result = CURLE_REMOTE_ACCESS_DENIED;
+ }
+ else
+ /* End of connect phase */
+ state(data, SMTP_STOP);
+
+ return result;
+}
+
+/* For SASL authentication responses */
+static CURLcode smtp_state_auth_resp(struct Curl_easy *data,
+ int smtpcode,
+ smtpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct smtp_conn *smtpc = &conn->proto.smtpc;
+ saslprogress progress;
+
+ (void)instate; /* no use for this yet */
+
+ result = Curl_sasl_continue(&smtpc->sasl, data, smtpcode, &progress);
+ if(!result)
+ switch(progress) {
+ case SASL_DONE:
+ state(data, SMTP_STOP); /* Authenticated */
+ break;
+ case SASL_IDLE: /* No mechanism left after cancellation */
+ failf(data, "Authentication cancelled");
+ result = CURLE_LOGIN_DENIED;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+/* For command responses */
+static CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode,
+ smtpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct SMTP *smtp = data->req.p.smtp;
+ char *line = data->state.buffer;
+ size_t len = strlen(line);
+
+ (void)instate; /* no use for this yet */
+
+ if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
+ (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
+ failf(data, "Command failed: %d", smtpcode);
+ result = CURLE_WEIRD_SERVER_REPLY;
+ }
+ else {
+ /* Temporarily add the LF character back and send as body to the client */
+ if(!data->req.no_body) {
+ line[len] = '\n';
+ result = Curl_client_write(data, CLIENTWRITE_BODY, line, len + 1);
+ line[len] = '\0';
+ }
+
+ if(smtpcode != 1) {
+ if(smtp->rcpt) {
+ smtp->rcpt = smtp->rcpt->next;
+
+ if(smtp->rcpt) {
+ /* Send the next command */
+ result = smtp_perform_command(data);
+ }
+ else
+ /* End of DO phase */
+ state(data, SMTP_STOP);
+ }
+ else
+ /* End of DO phase */
+ state(data, SMTP_STOP);
+ }
+ }
+
+ return result;
+}
+
+/* For MAIL responses */
+static CURLcode smtp_state_mail_resp(struct Curl_easy *data, int smtpcode,
+ smtpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ (void)instate; /* no use for this yet */
+
+ if(smtpcode/100 != 2) {
+ failf(data, "MAIL failed: %d", smtpcode);
+ result = CURLE_SEND_ERROR;
+ }
+ else
+ /* Start the RCPT TO command */
+ result = smtp_perform_rcpt_to(data);
+
+ return result;
+}
+
+/* For RCPT responses */
+static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data,
+ struct connectdata *conn, int smtpcode,
+ smtpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ struct SMTP *smtp = data->req.p.smtp;
+ bool is_smtp_err = FALSE;
+ bool is_smtp_blocking_err = FALSE;
+
+ (void)instate; /* no use for this yet */
+
+ is_smtp_err = (smtpcode/100 != 2) ? TRUE : FALSE;
+
+ /* If there's multiple RCPT TO to be issued, it's possible to ignore errors
+ and proceed with only the valid addresses. */
+ is_smtp_blocking_err =
+ (is_smtp_err && !data->set.mail_rcpt_allowfails) ? TRUE : FALSE;
+
+ if(is_smtp_err) {
+ /* Remembering the last failure which we can report if all "RCPT TO" have
+ failed and we cannot proceed. */
+ smtp->rcpt_last_error = smtpcode;
+
+ if(is_smtp_blocking_err) {
+ failf(data, "RCPT failed: %d", smtpcode);
+ result = CURLE_SEND_ERROR;
+ }
+ }
+ else {
+ /* Some RCPT TO commands have succeeded. */
+ smtp->rcpt_had_ok = TRUE;
+ }
+
+ if(!is_smtp_blocking_err) {
+ smtp->rcpt = smtp->rcpt->next;
+
+ if(smtp->rcpt)
+ /* Send the next RCPT TO command */
+ result = smtp_perform_rcpt_to(data);
+ else {
+ /* We weren't able to issue a successful RCPT TO command while going
+ over recipients (potentially multiple). Sending back last error. */
+ if(!smtp->rcpt_had_ok) {
+ failf(data, "RCPT failed: %d (last error)", smtp->rcpt_last_error);
+ result = CURLE_SEND_ERROR;
+ }
+ else {
+ /* Send the DATA command */
+ result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "DATA");
+
+ if(!result)
+ state(data, SMTP_DATA);
+ }
+ }
+ }
+
+ return result;
+}
+
+/* For DATA response */
+static CURLcode smtp_state_data_resp(struct Curl_easy *data, int smtpcode,
+ smtpstate instate)
+{
+ CURLcode result = CURLE_OK;
+ (void)instate; /* no use for this yet */
+
+ if(smtpcode != 354) {
+ failf(data, "DATA failed: %d", smtpcode);
+ result = CURLE_SEND_ERROR;
+ }
+ else {
+ /* Set the progress upload size */
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+
+ /* SMTP upload */
+ Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
+
+ /* End of DO phase */
+ state(data, SMTP_STOP);
+ }
+
+ return result;
+}
+
+/* For POSTDATA responses, which are received after the entire DATA
+ part has been sent to the server */
+static CURLcode smtp_state_postdata_resp(struct Curl_easy *data,
+ int smtpcode,
+ smtpstate instate)
+{
+ CURLcode result = CURLE_OK;
+
+ (void)instate; /* no use for this yet */
+
+ if(smtpcode != 250)
+ result = CURLE_WEIRD_SERVER_REPLY;
+
+ /* End of DONE phase */
+ state(data, SMTP_STOP);
+
+ return result;
+}
+
+static CURLcode smtp_statemachine(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ int smtpcode;
+ struct smtp_conn *smtpc = &conn->proto.smtpc;
+ struct pingpong *pp = &smtpc->pp;
+ size_t nread = 0;
+
+ /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
+ if(smtpc->state == SMTP_UPGRADETLS)
+ return smtp_perform_upgrade_tls(data);
+
+ /* Flush any data that needs to be sent */
+ if(pp->sendleft)
+ return Curl_pp_flushsend(data, pp);
+
+ do {
+ /* Read the response from the server */
+ result = Curl_pp_readresp(data, sock, pp, &smtpcode, &nread);
+ if(result)
+ return result;
+
+ /* Store the latest response for later retrieval if necessary */
+ if(smtpc->state != SMTP_QUIT && smtpcode != 1)
+ data->info.httpcode = smtpcode;
+
+ if(!smtpcode)
+ break;
+
+ /* We have now received a full SMTP server response */
+ switch(smtpc->state) {
+ case SMTP_SERVERGREET:
+ result = smtp_state_servergreet_resp(data, smtpcode, smtpc->state);
+ break;
+
+ case SMTP_EHLO:
+ result = smtp_state_ehlo_resp(data, conn, smtpcode, smtpc->state);
+ break;
+
+ case SMTP_HELO:
+ result = smtp_state_helo_resp(data, smtpcode, smtpc->state);
+ break;
+
+ case SMTP_STARTTLS:
+ result = smtp_state_starttls_resp(data, smtpcode, smtpc->state);
+ break;
+
+ case SMTP_AUTH:
+ result = smtp_state_auth_resp(data, smtpcode, smtpc->state);
+ break;
+
+ case SMTP_COMMAND:
+ result = smtp_state_command_resp(data, smtpcode, smtpc->state);
+ break;
+
+ case SMTP_MAIL:
+ result = smtp_state_mail_resp(data, smtpcode, smtpc->state);
+ break;
+
+ case SMTP_RCPT:
+ result = smtp_state_rcpt_resp(data, conn, smtpcode, smtpc->state);
+ break;
+
+ case SMTP_DATA:
+ result = smtp_state_data_resp(data, smtpcode, smtpc->state);
+ break;
+
+ case SMTP_POSTDATA:
+ result = smtp_state_postdata_resp(data, smtpcode, smtpc->state);
+ break;
+
+ case SMTP_QUIT:
+ /* fallthrough, just stop! */
+ default:
+ /* internal error */
+ state(data, SMTP_STOP);
+ break;
+ }
+ } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
+
+ return result;
+}
+
+/* Called repeatedly until done from multi.c */
+static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct smtp_conn *smtpc = &conn->proto.smtpc;
+
+ if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &smtpc->ssldone);
+ if(result || !smtpc->ssldone)
+ return result;
+ }
+
+ result = Curl_pp_statemach(data, &smtpc->pp, FALSE, FALSE);
+ *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
+
+ return result;
+}
+
+static CURLcode smtp_block_statemach(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool disconnecting)
+{
+ CURLcode result = CURLE_OK;
+ struct smtp_conn *smtpc = &conn->proto.smtpc;
+
+ while(smtpc->state != SMTP_STOP && !result)
+ result = Curl_pp_statemach(data, &smtpc->pp, TRUE, disconnecting);
+
+ return result;
+}
+
+/* Allocate and initialize the SMTP struct for the current Curl_easy if
+ required */
+static CURLcode smtp_init(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct SMTP *smtp;
+
+ smtp = data->req.p.smtp = calloc(sizeof(struct SMTP), 1);
+ if(!smtp)
+ result = CURLE_OUT_OF_MEMORY;
+
+ return result;
+}
+
+/* For the SMTP "protocol connect" and "doing" phases only */
+static int smtp_getsock(struct Curl_easy *data,
+ struct connectdata *conn, curl_socket_t *socks)
+{
+ return Curl_pp_getsock(data, &conn->proto.smtpc.pp, socks);
+}
+
+/***********************************************************************
+ *
+ * smtp_connect()
+ *
+ * This function should do everything that is to be considered a part of
+ * the connection phase.
+ *
+ * The variable pointed to by 'done' will be TRUE if the protocol-layer
+ * connect phase is done when this function returns, or FALSE if not.
+ */
+static CURLcode smtp_connect(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct smtp_conn *smtpc = &conn->proto.smtpc;
+ struct pingpong *pp = &smtpc->pp;
+
+ *done = FALSE; /* default to not done yet */
+
+ /* We always support persistent connections in SMTP */
+ connkeep(conn, "SMTP default");
+
+ PINGPONG_SETUP(pp, smtp_statemachine, smtp_endofresp);
+
+ /* Initialize the SASL storage */
+ Curl_sasl_init(&smtpc->sasl, data, &saslsmtp);
+
+ /* Initialise the pingpong layer */
+ Curl_pp_setup(pp);
+ Curl_pp_init(data, pp);
+
+ /* Parse the URL options */
+ result = smtp_parse_url_options(conn);
+ if(result)
+ return result;
+
+ /* Parse the URL path */
+ result = smtp_parse_url_path(data);
+ if(result)
+ return result;
+
+ /* Start off waiting for the server greeting response */
+ state(data, SMTP_SERVERGREET);
+
+ result = smtp_multi_statemach(data, done);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_done()
+ *
+ * The DONE function. This does what needs to be done after a single DO has
+ * performed.
+ *
+ * Input argument is already checked for validity.
+ */
+static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
+ bool premature)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct SMTP *smtp = data->req.p.smtp;
+ struct pingpong *pp = &conn->proto.smtpc.pp;
+ char *eob;
+ ssize_t len;
+ ssize_t bytes_written;
+
+ (void)premature;
+
+ if(!smtp)
+ return CURLE_OK;
+
+ /* Cleanup our per-request based variables */
+ Curl_safefree(smtp->custom);
+
+ if(status) {
+ connclose(conn, "SMTP done with bad status"); /* marked for closure */
+ result = status; /* use the already set error code */
+ }
+ else if(!data->set.connect_only && data->set.mail_rcpt &&
+ (data->set.upload || data->set.mimepost.kind)) {
+ /* Calculate the EOB taking into account any terminating CRLF from the
+ previous line of the email or the CRLF of the DATA command when there
+ is "no mail data". RFC-5321, sect. 4.1.1.4.
+
+ Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to
+ fail when using a different pointer following a previous write, that
+ returned CURLE_AGAIN, we duplicate the EOB now rather than when the
+ bytes written doesn't equal len. */
+ if(smtp->trailing_crlf || !data->state.infilesize) {
+ eob = strdup(&SMTP_EOB[2]);
+ len = SMTP_EOB_LEN - 2;
+ }
+ else {
+ eob = strdup(SMTP_EOB);
+ len = SMTP_EOB_LEN;
+ }
+
+ if(!eob)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Send the end of block data */
+ result = Curl_write(data, conn->writesockfd, eob, len, &bytes_written);
+ if(result) {
+ free(eob);
+ return result;
+ }
+
+ if(bytes_written != len) {
+ /* The whole chunk was not sent so keep it around and adjust the
+ pingpong structure accordingly */
+ pp->sendthis = eob;
+ pp->sendsize = len;
+ pp->sendleft = len - bytes_written;
+ }
+ else {
+ /* Successfully sent so adjust the response timeout relative to now */
+ pp->response = Curl_now();
+
+ free(eob);
+ }
+
+ state(data, SMTP_POSTDATA);
+
+ /* Run the state-machine */
+ result = smtp_block_statemach(data, conn, FALSE);
+ }
+
+ /* Clear the transfer mode for the next request */
+ smtp->transfer = PPTRANSFER_BODY;
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_perform()
+ *
+ * This is the actual DO function for SMTP. Transfer a mail, send a command
+ * or get some data according to the options previously setup.
+ */
+static CURLcode smtp_perform(struct Curl_easy *data, bool *connected,
+ bool *dophase_done)
+{
+ /* This is SMTP and no proxy */
+ CURLcode result = CURLE_OK;
+ struct SMTP *smtp = data->req.p.smtp;
+
+ DEBUGF(infof(data, "DO phase starts"));
+
+ if(data->req.no_body) {
+ /* Requested no body means no transfer */
+ smtp->transfer = PPTRANSFER_INFO;
+ }
+
+ *dophase_done = FALSE; /* not done yet */
+
+ /* Store the first recipient (or NULL if not specified) */
+ smtp->rcpt = data->set.mail_rcpt;
+
+ /* Track of whether we've successfully sent at least one RCPT TO command */
+ smtp->rcpt_had_ok = FALSE;
+
+ /* Track of the last error we've received by sending RCPT TO command */
+ smtp->rcpt_last_error = 0;
+
+ /* Initial data character is the first character in line: it is implicitly
+ preceded by a virtual CRLF. */
+ smtp->trailing_crlf = TRUE;
+ smtp->eob = 2;
+
+ /* Start the first command in the DO phase */
+ if((data->set.upload || data->set.mimepost.kind) && data->set.mail_rcpt)
+ /* MAIL transfer */
+ result = smtp_perform_mail(data);
+ else
+ /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
+ result = smtp_perform_command(data);
+
+ if(result)
+ return result;
+
+ /* Run the state-machine */
+ result = smtp_multi_statemach(data, dophase_done);
+
+ *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
+
+ if(*dophase_done)
+ DEBUGF(infof(data, "DO phase is complete"));
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_do()
+ *
+ * This function is registered as 'curl_do' function. It decodes the path
+ * parts etc as a wrapper to the actual DO function (smtp_perform).
+ *
+ * The input argument is already checked for validity.
+ */
+static CURLcode smtp_do(struct Curl_easy *data, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ *done = FALSE; /* default to false */
+
+ /* Parse the custom request */
+ result = smtp_parse_custom_request(data);
+ if(result)
+ return result;
+
+ result = smtp_regular_transfer(data, done);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_disconnect()
+ *
+ * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
+ * resources. BLOCKING.
+ */
+static CURLcode smtp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection)
+{
+ struct smtp_conn *smtpc = &conn->proto.smtpc;
+ (void)data;
+
+ /* We cannot send quit unconditionally. If this connection is stale or
+ bad in any way, sending quit and waiting around here will make the
+ disconnect wait in vain and cause more problems than we need to. */
+
+ if(!dead_connection && conn->bits.protoconnstart) {
+ if(!smtp_perform_quit(data, conn))
+ (void)smtp_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */
+ }
+
+ /* Disconnect from the server */
+ Curl_pp_disconnect(&smtpc->pp);
+
+ /* Cleanup the SASL module */
+ Curl_sasl_cleanup(conn, smtpc->sasl.authused);
+
+ /* Cleanup our connection based variables */
+ Curl_safefree(smtpc->domain);
+
+ return CURLE_OK;
+}
+
+/* Call this when the DO phase has completed */
+static CURLcode smtp_dophase_done(struct Curl_easy *data, bool connected)
+{
+ struct SMTP *smtp = data->req.p.smtp;
+
+ (void)connected;
+
+ if(smtp->transfer != PPTRANSFER_BODY)
+ /* no data to transfer */
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
+
+ return CURLE_OK;
+}
+
+/* Called from multi.c while DOing */
+static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done)
+{
+ CURLcode result = smtp_multi_statemach(data, dophase_done);
+
+ if(result)
+ DEBUGF(infof(data, "DO phase failed"));
+ else if(*dophase_done) {
+ result = smtp_dophase_done(data, FALSE /* not connected */);
+
+ DEBUGF(infof(data, "DO phase is complete"));
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_regular_transfer()
+ *
+ * The input argument is already checked for validity.
+ *
+ * Performs all commands done before a regular transfer between a local and a
+ * remote host.
+ */
+static CURLcode smtp_regular_transfer(struct Curl_easy *data,
+ bool *dophase_done)
+{
+ CURLcode result = CURLE_OK;
+ bool connected = FALSE;
+
+ /* Make sure size is unknown at this point */
+ data->req.size = -1;
+
+ /* Set the progress data */
+ Curl_pgrsSetUploadCounter(data, 0);
+ Curl_pgrsSetDownloadCounter(data, 0);
+ Curl_pgrsSetUploadSize(data, -1);
+ Curl_pgrsSetDownloadSize(data, -1);
+
+ /* Carry out the perform */
+ result = smtp_perform(data, &connected, dophase_done);
+
+ /* Perform post DO phase operations if necessary */
+ if(!result && *dophase_done)
+ result = smtp_dophase_done(data, connected);
+
+ return result;
+}
+
+static CURLcode smtp_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result;
+
+ /* Clear the TLS upgraded flag */
+ conn->bits.tls_upgraded = FALSE;
+
+ /* Initialise the SMTP layer */
+ result = smtp_init(data);
+ if(result)
+ return result;
+
+ return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * smtp_parse_url_options()
+ *
+ * Parse the URL login options.
+ */
+static CURLcode smtp_parse_url_options(struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct smtp_conn *smtpc = &conn->proto.smtpc;
+ const char *ptr = conn->options;
+
+ while(!result && ptr && *ptr) {
+ const char *key = ptr;
+ const char *value;
+
+ while(*ptr && *ptr != '=')
+ ptr++;
+
+ value = ptr + 1;
+
+ while(*ptr && *ptr != ';')
+ ptr++;
+
+ if(strncasecompare(key, "AUTH=", 5))
+ result = Curl_sasl_parse_url_auth_option(&smtpc->sasl,
+ value, ptr - value);
+ else
+ result = CURLE_URL_MALFORMAT;
+
+ if(*ptr == ';')
+ ptr++;
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_parse_url_path()
+ *
+ * Parse the URL path into separate path components.
+ */
+static CURLcode smtp_parse_url_path(struct Curl_easy *data)
+{
+ /* The SMTP struct is already initialised in smtp_connect() */
+ struct connectdata *conn = data->conn;
+ struct smtp_conn *smtpc = &conn->proto.smtpc;
+ const char *path = &data->state.up.path[1]; /* skip leading path */
+ char localhost[HOSTNAME_MAX + 1];
+
+ /* Calculate the path if necessary */
+ if(!*path) {
+ if(!Curl_gethostname(localhost, sizeof(localhost)))
+ path = localhost;
+ else
+ path = "localhost";
+ }
+
+ /* URL decode the path and use it as the domain in our EHLO */
+ return Curl_urldecode(path, 0, &smtpc->domain, NULL, REJECT_CTRL);
+}
+
+/***********************************************************************
+ *
+ * smtp_parse_custom_request()
+ *
+ * Parse the custom request.
+ */
+static CURLcode smtp_parse_custom_request(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct SMTP *smtp = data->req.p.smtp;
+ const char *custom = data->set.str[STRING_CUSTOMREQUEST];
+
+ /* URL decode the custom request */
+ if(custom)
+ result = Curl_urldecode(custom, 0, &smtp->custom, NULL, REJECT_CTRL);
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_parse_address()
+ *
+ * Parse the fully qualified mailbox address into a local address part and the
+ * host name, converting the host name to an IDN A-label, as per RFC-5890, if
+ * necessary.
+ *
+ * Parameters:
+ *
+ * conn [in] - The connection handle.
+ * fqma [in] - The fully qualified mailbox address (which may or
+ * may not contain UTF-8 characters).
+ * address [in/out] - A new allocated buffer which holds the local
+ * address part of the mailbox. This buffer must be
+ * free'ed by the caller.
+ * host [in/out] - The host name structure that holds the original,
+ * and optionally encoded, host name.
+ * Curl_free_idnconverted_hostname() must be called
+ * once the caller has finished with the structure.
+ *
+ * Returns CURLE_OK on success.
+ *
+ * Notes:
+ *
+ * Should a UTF-8 host name require conversion to IDN ACE and we cannot honor
+ * that conversion then we shall return success. This allow the caller to send
+ * the data to the server as a U-label (as per RFC-6531 sect. 3.2).
+ *
+ * If an mailbox '@' separator cannot be located then the mailbox is considered
+ * to be either a local mailbox or an invalid mailbox (depending on what the
+ * calling function deems it to be) then the input will simply be returned in
+ * the address part with the host name being NULL.
+ */
+static CURLcode smtp_parse_address(const char *fqma, char **address,
+ struct hostname *host)
+{
+ CURLcode result = CURLE_OK;
+ size_t length;
+
+ /* Duplicate the fully qualified email address so we can manipulate it,
+ ensuring it doesn't contain the delimiters if specified */
+ char *dup = strdup(fqma[0] == '<' ? fqma + 1 : fqma);
+ if(!dup)
+ return CURLE_OUT_OF_MEMORY;
+
+ length = strlen(dup);
+ if(length) {
+ if(dup[length - 1] == '>')
+ dup[length - 1] = '\0';
+ }
+
+ /* Extract the host name from the address (if we can) */
+ host->name = strpbrk(dup, "@");
+ if(host->name) {
+ *host->name = '\0';
+ host->name = host->name + 1;
+
+ /* Attempt to convert the host name to IDN ACE */
+ (void) Curl_idnconvert_hostname(host);
+
+ /* If Curl_idnconvert_hostname() fails then we shall attempt to continue
+ and send the host name using UTF-8 rather than as 7-bit ACE (which is
+ our preference) */
+ }
+
+ /* Extract the local address from the mailbox */
+ *address = dup;
+
+ return result;
+}
+
+CURLcode Curl_smtp_escape_eob(struct Curl_easy *data,
+ const ssize_t nread,
+ const ssize_t offset)
+{
+ /* When sending a SMTP payload we must detect CRLF. sequences making sure
+ they are sent as CRLF.. instead, as a . on the beginning of a line will
+ be deleted by the server when not part of an EOB terminator and a
+ genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
+ data by the server
+ */
+ ssize_t i;
+ ssize_t si;
+ struct SMTP *smtp = data->req.p.smtp;
+ char *scratch = data->state.scratch;
+ char *newscratch = NULL;
+ char *oldscratch = NULL;
+ size_t eob_sent;
+
+ /* Do we need to allocate a scratch buffer? */
+ if(!scratch || data->set.crlf) {
+ oldscratch = scratch;
+
+ scratch = newscratch = malloc(2 * data->set.upload_buffer_size);
+ if(!newscratch) {
+ failf(data, "Failed to alloc scratch buffer");
+
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ DEBUGASSERT((size_t)data->set.upload_buffer_size >= (size_t)nread);
+
+ /* Have we already sent part of the EOB? */
+ eob_sent = smtp->eob;
+
+ /* This loop can be improved by some kind of Boyer-Moore style of
+ approach but that is saved for later... */
+ if(offset)
+ memcpy(scratch, data->req.upload_fromhere, offset);
+ for(i = offset, si = offset; i < nread; i++) {
+ if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
+ smtp->eob++;
+
+ /* Is the EOB potentially the terminating CRLF? */
+ if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
+ smtp->trailing_crlf = TRUE;
+ else
+ smtp->trailing_crlf = FALSE;
+ }
+ else if(smtp->eob) {
+ /* A previous substring matched so output that first */
+ memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
+ si += smtp->eob - eob_sent;
+
+ /* Then compare the first byte */
+ if(SMTP_EOB[0] == data->req.upload_fromhere[i])
+ smtp->eob = 1;
+ else
+ smtp->eob = 0;
+
+ eob_sent = 0;
+
+ /* Reset the trailing CRLF flag as there was more data */
+ smtp->trailing_crlf = FALSE;
+ }
+
+ /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
+ if(SMTP_EOB_FIND_LEN == smtp->eob) {
+ /* Copy the replacement data to the target buffer */
+ memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent],
+ SMTP_EOB_REPL_LEN - eob_sent);
+ si += SMTP_EOB_REPL_LEN - eob_sent;
+ smtp->eob = 0;
+ eob_sent = 0;
+ }
+ else if(!smtp->eob)
+ scratch[si++] = data->req.upload_fromhere[i];
+ }
+
+ if(smtp->eob - eob_sent) {
+ /* A substring matched before processing ended so output that now */
+ memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
+ si += smtp->eob - eob_sent;
+ }
+
+ /* Only use the new buffer if we replaced something */
+ if(si != nread) {
+ /* Upload from the new (replaced) buffer instead */
+ data->req.upload_fromhere = scratch;
+
+ /* Save the buffer so it can be freed later */
+ data->state.scratch = scratch;
+
+ /* Free the old scratch buffer */
+ free(oldscratch);
+
+ /* Set the new amount too */
+ data->req.upload_present = si;
+ }
+ else
+ free(newscratch);
+
+ return CURLE_OK;
+}
+
+#endif /* CURL_DISABLE_SMTP */
diff --git a/libs/libcurl/src/smtp.h b/libs/libcurl/src/smtp.h index 24c5589e43..87d9319b5d 100644 --- a/libs/libcurl/src/smtp.h +++ b/libs/libcurl/src/smtp.h @@ -1,100 +1,100 @@ -#ifndef HEADER_CURL_SMTP_H -#define HEADER_CURL_SMTP_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2009 - 2022, 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 "pingpong.h" -#include "curl_sasl.h" - -/**************************************************************************** - * SMTP unique setup - ***************************************************************************/ -typedef enum { - SMTP_STOP, /* do nothing state, stops the state machine */ - SMTP_SERVERGREET, /* waiting for the initial greeting immediately after - a connect */ - SMTP_EHLO, - SMTP_HELO, - SMTP_STARTTLS, - SMTP_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS - (multi mode only) */ - SMTP_AUTH, - SMTP_COMMAND, /* VRFY, EXPN, NOOP, RSET and HELP */ - SMTP_MAIL, /* MAIL FROM */ - SMTP_RCPT, /* RCPT TO */ - SMTP_DATA, - SMTP_POSTDATA, - SMTP_QUIT, - SMTP_LAST /* never used */ -} smtpstate; - -/* This SMTP struct is used in the Curl_easy. All SMTP data that is - connection-oriented must be in smtp_conn to properly deal with the fact that - perhaps the Curl_easy is changed between the times the connection is - used. */ -struct SMTP { - curl_pp_transfer transfer; - char *custom; /* Custom Request */ - struct curl_slist *rcpt; /* Recipient list */ - bool rcpt_had_ok; /* Whether any of RCPT TO commands (depends on - total number of recipients) succeeded so far */ - bool trailing_crlf; /* Specifies if the trailing CRLF is present */ - int rcpt_last_error; /* The last error received for RCPT TO command */ - size_t eob; /* Number of bytes of the EOB (End Of Body) that - have been received so far */ -}; - -/* smtp_conn is used for struct connection-oriented data in the connectdata - struct */ -struct smtp_conn { - struct pingpong pp; - smtpstate state; /* Always use smtp.c:state() to change state! */ - bool ssldone; /* Is connect() over SSL done? */ - char *domain; /* Client address/name to send in the EHLO */ - struct SASL sasl; /* SASL-related storage */ - bool tls_supported; /* StartTLS capability supported by server */ - bool size_supported; /* If server supports SIZE extension according to - RFC 1870 */ - bool utf8_supported; /* If server supports SMTPUTF8 extension according - to RFC 6531 */ - bool auth_supported; /* AUTH capability supported by server */ -}; - -extern const struct Curl_handler Curl_handler_smtp; -extern const struct Curl_handler Curl_handler_smtps; - -/* this is the 5-bytes End-Of-Body marker for SMTP */ -#define SMTP_EOB "\x0d\x0a\x2e\x0d\x0a" -#define SMTP_EOB_LEN 5 -#define SMTP_EOB_FIND_LEN 3 - -/* if found in data, replace it with this string instead */ -#define SMTP_EOB_REPL "\x0d\x0a\x2e\x2e" -#define SMTP_EOB_REPL_LEN 4 - -CURLcode Curl_smtp_escape_eob(struct Curl_easy *data, - const ssize_t nread, - const ssize_t offset); - -#endif /* HEADER_CURL_SMTP_H */ +#ifndef HEADER_CURL_SMTP_H
+#define HEADER_CURL_SMTP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2009 - 2022, 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 "pingpong.h"
+#include "curl_sasl.h"
+
+/****************************************************************************
+ * SMTP unique setup
+ ***************************************************************************/
+typedef enum {
+ SMTP_STOP, /* do nothing state, stops the state machine */
+ SMTP_SERVERGREET, /* waiting for the initial greeting immediately after
+ a connect */
+ SMTP_EHLO,
+ SMTP_HELO,
+ SMTP_STARTTLS,
+ SMTP_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS
+ (multi mode only) */
+ SMTP_AUTH,
+ SMTP_COMMAND, /* VRFY, EXPN, NOOP, RSET and HELP */
+ SMTP_MAIL, /* MAIL FROM */
+ SMTP_RCPT, /* RCPT TO */
+ SMTP_DATA,
+ SMTP_POSTDATA,
+ SMTP_QUIT,
+ SMTP_LAST /* never used */
+} smtpstate;
+
+/* This SMTP struct is used in the Curl_easy. All SMTP data that is
+ connection-oriented must be in smtp_conn to properly deal with the fact that
+ perhaps the Curl_easy is changed between the times the connection is
+ used. */
+struct SMTP {
+ curl_pp_transfer transfer;
+ char *custom; /* Custom Request */
+ struct curl_slist *rcpt; /* Recipient list */
+ bool rcpt_had_ok; /* Whether any of RCPT TO commands (depends on
+ total number of recipients) succeeded so far */
+ bool trailing_crlf; /* Specifies if the trailing CRLF is present */
+ int rcpt_last_error; /* The last error received for RCPT TO command */
+ size_t eob; /* Number of bytes of the EOB (End Of Body) that
+ have been received so far */
+};
+
+/* smtp_conn is used for struct connection-oriented data in the connectdata
+ struct */
+struct smtp_conn {
+ struct pingpong pp;
+ smtpstate state; /* Always use smtp.c:state() to change state! */
+ bool ssldone; /* Is connect() over SSL done? */
+ char *domain; /* Client address/name to send in the EHLO */
+ struct SASL sasl; /* SASL-related storage */
+ bool tls_supported; /* StartTLS capability supported by server */
+ bool size_supported; /* If server supports SIZE extension according to
+ RFC 1870 */
+ bool utf8_supported; /* If server supports SMTPUTF8 extension according
+ to RFC 6531 */
+ bool auth_supported; /* AUTH capability supported by server */
+};
+
+extern const struct Curl_handler Curl_handler_smtp;
+extern const struct Curl_handler Curl_handler_smtps;
+
+/* this is the 5-bytes End-Of-Body marker for SMTP */
+#define SMTP_EOB "\x0d\x0a\x2e\x0d\x0a"
+#define SMTP_EOB_LEN 5
+#define SMTP_EOB_FIND_LEN 3
+
+/* if found in data, replace it with this string instead */
+#define SMTP_EOB_REPL "\x0d\x0a\x2e\x2e"
+#define SMTP_EOB_REPL_LEN 4
+
+CURLcode Curl_smtp_escape_eob(struct Curl_easy *data,
+ const ssize_t nread,
+ const ssize_t offset);
+
+#endif /* HEADER_CURL_SMTP_H */
diff --git a/libs/libcurl/src/sockaddr.h b/libs/libcurl/src/sockaddr.h index 77ec833ee0..ba497b4079 100644 --- a/libs/libcurl/src/sockaddr.h +++ b/libs/libcurl/src/sockaddr.h @@ -1,44 +1,44 @@ -#ifndef HEADER_CURL_SOCKADDR_H -#define HEADER_CURL_SOCKADDR_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_sockaddr_storage { - union { - struct sockaddr sa; - struct sockaddr_in sa_in; -#ifdef ENABLE_IPV6 - struct sockaddr_in6 sa_in6; -#endif -#ifdef HAVE_STRUCT_SOCKADDR_STORAGE - struct sockaddr_storage sa_stor; -#else - char cbuf[256]; /* this should be big enough to fit a lot */ -#endif - } buffer; -}; - -#endif /* HEADER_CURL_SOCKADDR_H */ +#ifndef HEADER_CURL_SOCKADDR_H
+#define HEADER_CURL_SOCKADDR_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_sockaddr_storage {
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sa_in;
+#ifdef ENABLE_IPV6
+ struct sockaddr_in6 sa_in6;
+#endif
+#ifdef HAVE_STRUCT_SOCKADDR_STORAGE
+ struct sockaddr_storage sa_stor;
+#else
+ char cbuf[256]; /* this should be big enough to fit a lot */
+#endif
+ } buffer;
+};
+
+#endif /* HEADER_CURL_SOCKADDR_H */
diff --git a/libs/libcurl/src/socketpair.c b/libs/libcurl/src/socketpair.c index 0f8798f087..c559e1a3e5 100644 --- a/libs/libcurl/src/socketpair.c +++ b/libs/libcurl/src/socketpair.c @@ -1,139 +1,139 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2019 - 2022, 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" - -#if !defined(HAVE_SOCKETPAIR) && !defined(CURL_DISABLE_SOCKETPAIR) -#ifdef WIN32 -/* - * This is a socketpair() implementation for Windows. - */ -#include <string.h> -#include <winsock2.h> -#include <ws2tcpip.h> -#include <windows.h> -#include <io.h> -#else -#ifdef HAVE_NETDB_H -#include <netdb.h> -#endif -#ifdef HAVE_NETINET_IN_H -#include <netinet/in.h> /* IPPROTO_TCP */ -#endif -#ifdef HAVE_ARPA_INET_H -#include <arpa/inet.h> -#endif -#ifndef INADDR_LOOPBACK -#define INADDR_LOOPBACK 0x7f000001 -#endif /* !INADDR_LOOPBACK */ -#endif /* !WIN32 */ - -#include "nonblock.h" /* for curlx_nonblock */ -#include "timeval.h" /* needed before select.h */ -#include "select.h" /* for Curl_poll */ - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -int Curl_socketpair(int domain, int type, int protocol, - curl_socket_t socks[2]) -{ - union { - struct sockaddr_in inaddr; - struct sockaddr addr; - } a, a2; - curl_socket_t listener; - curl_socklen_t addrlen = sizeof(a.inaddr); - int reuse = 1; - struct pollfd pfd[1]; - (void)domain; - (void)type; - (void)protocol; - - listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if(listener == CURL_SOCKET_BAD) - return -1; - - memset(&a, 0, sizeof(a)); - a.inaddr.sin_family = AF_INET; - a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - a.inaddr.sin_port = 0; - - socks[0] = socks[1] = CURL_SOCKET_BAD; - - if(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, - (char *)&reuse, (curl_socklen_t)sizeof(reuse)) == -1) - goto error; - if(bind(listener, &a.addr, sizeof(a.inaddr)) == -1) - goto error; - if(getsockname(listener, &a.addr, &addrlen) == -1 || - addrlen < (int)sizeof(a.inaddr)) - goto error; - if(listen(listener, 1) == -1) - goto error; - socks[0] = socket(AF_INET, SOCK_STREAM, 0); - if(socks[0] == CURL_SOCKET_BAD) - goto error; - if(connect(socks[0], &a.addr, sizeof(a.inaddr)) == -1) - goto error; - - /* use non-blocking accept to make sure we don't block forever */ - if(curlx_nonblock(listener, TRUE) < 0) - goto error; - pfd[0].fd = listener; - pfd[0].events = POLLIN; - pfd[0].revents = 0; - (void)Curl_poll(pfd, 1, 10*1000); /* 10 seconds */ - socks[1] = accept(listener, NULL, NULL); - if(socks[1] == CURL_SOCKET_BAD) - goto error; - - /* verify that nothing else connected */ - addrlen = sizeof(a.inaddr); - if(getsockname(socks[0], &a.addr, &addrlen) == -1 || - addrlen < (int)sizeof(a.inaddr)) - goto error; - addrlen = sizeof(a2.inaddr); - if(getpeername(socks[1], &a2.addr, &addrlen) == -1 || - addrlen < (int)sizeof(a2.inaddr)) - goto error; - if(a.inaddr.sin_family != a2.inaddr.sin_family || - a.inaddr.sin_addr.s_addr != a2.inaddr.sin_addr.s_addr || - a.inaddr.sin_port != a2.inaddr.sin_port) - goto error; - - sclose(listener); - return 0; - - error: - sclose(listener); - sclose(socks[0]); - sclose(socks[1]); - return -1; -} - -#endif /* ! HAVE_SOCKETPAIR */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2019 - 2022, 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"
+
+#if !defined(HAVE_SOCKETPAIR) && !defined(CURL_DISABLE_SOCKETPAIR)
+#ifdef WIN32
+/*
+ * This is a socketpair() implementation for Windows.
+ */
+#include <string.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <windows.h>
+#include <io.h>
+#else
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h> /* IPPROTO_TCP */
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK 0x7f000001
+#endif /* !INADDR_LOOPBACK */
+#endif /* !WIN32 */
+
+#include "nonblock.h" /* for curlx_nonblock */
+#include "timeval.h" /* needed before select.h */
+#include "select.h" /* for Curl_poll */
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+int Curl_socketpair(int domain, int type, int protocol,
+ curl_socket_t socks[2])
+{
+ union {
+ struct sockaddr_in inaddr;
+ struct sockaddr addr;
+ } a, a2;
+ curl_socket_t listener;
+ curl_socklen_t addrlen = sizeof(a.inaddr);
+ int reuse = 1;
+ struct pollfd pfd[1];
+ (void)domain;
+ (void)type;
+ (void)protocol;
+
+ listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if(listener == CURL_SOCKET_BAD)
+ return -1;
+
+ memset(&a, 0, sizeof(a));
+ a.inaddr.sin_family = AF_INET;
+ a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ a.inaddr.sin_port = 0;
+
+ socks[0] = socks[1] = CURL_SOCKET_BAD;
+
+ if(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&reuse, (curl_socklen_t)sizeof(reuse)) == -1)
+ goto error;
+ if(bind(listener, &a.addr, sizeof(a.inaddr)) == -1)
+ goto error;
+ if(getsockname(listener, &a.addr, &addrlen) == -1 ||
+ addrlen < (int)sizeof(a.inaddr))
+ goto error;
+ if(listen(listener, 1) == -1)
+ goto error;
+ socks[0] = socket(AF_INET, SOCK_STREAM, 0);
+ if(socks[0] == CURL_SOCKET_BAD)
+ goto error;
+ if(connect(socks[0], &a.addr, sizeof(a.inaddr)) == -1)
+ goto error;
+
+ /* use non-blocking accept to make sure we don't block forever */
+ if(curlx_nonblock(listener, TRUE) < 0)
+ goto error;
+ pfd[0].fd = listener;
+ pfd[0].events = POLLIN;
+ pfd[0].revents = 0;
+ (void)Curl_poll(pfd, 1, 10*1000); /* 10 seconds */
+ socks[1] = accept(listener, NULL, NULL);
+ if(socks[1] == CURL_SOCKET_BAD)
+ goto error;
+
+ /* verify that nothing else connected */
+ addrlen = sizeof(a.inaddr);
+ if(getsockname(socks[0], &a.addr, &addrlen) == -1 ||
+ addrlen < (int)sizeof(a.inaddr))
+ goto error;
+ addrlen = sizeof(a2.inaddr);
+ if(getpeername(socks[1], &a2.addr, &addrlen) == -1 ||
+ addrlen < (int)sizeof(a2.inaddr))
+ goto error;
+ if(a.inaddr.sin_family != a2.inaddr.sin_family ||
+ a.inaddr.sin_addr.s_addr != a2.inaddr.sin_addr.s_addr ||
+ a.inaddr.sin_port != a2.inaddr.sin_port)
+ goto error;
+
+ sclose(listener);
+ return 0;
+
+ error:
+ sclose(listener);
+ sclose(socks[0]);
+ sclose(socks[1]);
+ return -1;
+}
+
+#endif /* ! HAVE_SOCKETPAIR */
diff --git a/libs/libcurl/src/socketpair.h b/libs/libcurl/src/socketpair.h index de70df673a..17dba73414 100644 --- a/libs/libcurl/src/socketpair.h +++ b/libs/libcurl/src/socketpair.h @@ -1,37 +1,37 @@ -#ifndef HEADER_CURL_SOCKETPAIR_H -#define HEADER_CURL_SOCKETPAIR_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2019 - 2022, 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 HAVE_SOCKETPAIR -#include <curl/curl.h> - -int Curl_socketpair(int domain, int type, int protocol, - curl_socket_t socks[2]); -#else -#define Curl_socketpair(a,b,c,d) socketpair(a,b,c,d) -#endif - -#endif /* HEADER_CURL_SOCKETPAIR_H */ +#ifndef HEADER_CURL_SOCKETPAIR_H
+#define HEADER_CURL_SOCKETPAIR_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2019 - 2022, 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 HAVE_SOCKETPAIR
+#include <curl/curl.h>
+
+int Curl_socketpair(int domain, int type, int protocol,
+ curl_socket_t socks[2]);
+#else
+#define Curl_socketpair(a,b,c,d) socketpair(a,b,c,d)
+#endif
+
+#endif /* HEADER_CURL_SOCKETPAIR_H */
diff --git a/libs/libcurl/src/socks.c b/libs/libcurl/src/socks.c index 52c29880a6..4c85a33c35 100644 --- a/libs/libcurl/src/socks.c +++ b/libs/libcurl/src/socks.c @@ -1,1058 +1,1261 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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) - -#ifdef HAVE_NETINET_IN_H -#include <netinet/in.h> -#endif -#ifdef HAVE_ARPA_INET_H -#include <arpa/inet.h> -#endif - -#include "urldata.h" -#include "sendf.h" -#include "select.h" -#include "connect.h" -#include "timeval.h" -#include "socks.h" -#include "multiif.h" /* for getsock macros */ -#include "inet_pton.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) -/* - * Helper read-from-socket functions. Does the same as Curl_read() but it - * blocks until all bytes amount of buffersize will be read. No more, no less. - * - * This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions. - */ -int Curl_blockread_all(struct Curl_easy *data, /* transfer */ - curl_socket_t sockfd, /* read from this socket */ - char *buf, /* store read data here */ - ssize_t buffersize, /* max amount to read */ - ssize_t *n) /* amount bytes read */ -{ - ssize_t nread = 0; - ssize_t allread = 0; - int result; - *n = 0; - for(;;) { - timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); - if(timeout_ms < 0) { - /* we already got the timeout */ - result = CURLE_OPERATION_TIMEDOUT; - break; - } - if(!timeout_ms) - timeout_ms = TIMEDIFF_T_MAX; - if(SOCKET_READABLE(sockfd, timeout_ms) <= 0) { - result = ~CURLE_OK; - break; - } - result = Curl_read_plain(sockfd, buf, buffersize, &nread); - if(CURLE_AGAIN == result) - continue; - if(result) - break; - - if(buffersize == nread) { - allread += nread; - *n = allread; - result = CURLE_OK; - break; - } - if(!nread) { - result = ~CURLE_OK; - break; - } - - buffersize -= nread; - buf += nread; - allread += nread; - } - return result; -} -#endif - -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) -#define DEBUG_AND_VERBOSE -#define sxstate(x,y) socksstate(x,y, __LINE__) -#else -#define sxstate(x,y) socksstate(x,y) -#endif - -/* always use this function to change state, to make debugging easier */ -static void socksstate(struct Curl_easy *data, - enum connect_t state -#ifdef DEBUG_AND_VERBOSE - , int lineno -#endif -) -{ - struct connectdata *conn = data->conn; - enum connect_t oldstate = conn->cnnct.state; -#ifdef DEBUG_AND_VERBOSE - /* synced with the state list in urldata.h */ - static const char * const statename[] = { - "INIT", - "SOCKS_INIT", - "SOCKS_SEND", - "SOCKS_READ_INIT", - "SOCKS_READ", - "GSSAPI_INIT", - "AUTH_INIT", - "AUTH_SEND", - "AUTH_READ", - "REQ_INIT", - "RESOLVING", - "RESOLVED", - "RESOLVE_REMOTE", - "REQ_SEND", - "REQ_SENDING", - "REQ_READ", - "REQ_READ_MORE", - "DONE" - }; -#endif - - if(oldstate == state) - /* don't bother when the new state is the same as the old state */ - return; - - conn->cnnct.state = state; - -#ifdef DEBUG_AND_VERBOSE - infof(data, - "SXSTATE: %s => %s conn %p; line %d", - statename[oldstate], statename[conn->cnnct.state], conn, - lineno); -#endif -} - -int Curl_SOCKS_getsock(struct connectdata *conn, curl_socket_t *sock, - int sockindex) -{ - int rc = 0; - sock[0] = conn->sock[sockindex]; - switch(conn->cnnct.state) { - case CONNECT_RESOLVING: - case CONNECT_SOCKS_READ: - case CONNECT_AUTH_READ: - case CONNECT_REQ_READ: - case CONNECT_REQ_READ_MORE: - rc = GETSOCK_READSOCK(0); - break; - default: - rc = GETSOCK_WRITESOCK(0); - break; - } - return rc; -} - -/* -* This function logs in to a SOCKS4 proxy and sends the specifics to the final -* destination server. -* -* Reference : -* https://www.openssh.com/txt/socks4.protocol -* -* Note : -* Set protocol4a=true for "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)" -* Nonsupport "Identification Protocol (RFC1413)" -*/ -CURLproxycode Curl_SOCKS4(const char *proxy_user, - const char *hostname, - int remote_port, - int sockindex, - struct Curl_easy *data, - bool *done) -{ - struct connectdata *conn = data->conn; - const bool protocol4a = - (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE; - unsigned char *socksreq = (unsigned char *)data->state.buffer; - CURLcode result; - curl_socket_t sockfd = conn->sock[sockindex]; - struct connstate *sx = &conn->cnnct; - struct Curl_dns_entry *dns = NULL; - ssize_t actualread; - ssize_t written; - - /* make sure that the buffer is at least 600 bytes */ - DEBUGASSERT(READBUFFER_MIN >= 600); - - if(!SOCKS_STATE(sx->state) && !*done) - sxstate(data, CONNECT_SOCKS_INIT); - - switch(sx->state) { - case CONNECT_SOCKS_INIT: - /* SOCKS4 can only do IPv4, insist! */ - conn->ip_version = CURL_IPRESOLVE_V4; - if(conn->bits.httpproxy) - infof(data, "SOCKS4%s: connecting to HTTP proxy %s port %d", - protocol4a ? "a" : "", hostname, remote_port); - - infof(data, "SOCKS4 communication to %s:%d", hostname, remote_port); - - /* - * Compose socks4 request - * - * Request format - * - * +----+----+----+----+----+----+----+----+----+----+....+----+ - * | VN | CD | DSTPORT | DSTIP | USERID |NULL| - * +----+----+----+----+----+----+----+----+----+----+....+----+ - * # of bytes: 1 1 2 4 variable 1 - */ - - socksreq[0] = 4; /* version (SOCKS4) */ - socksreq[1] = 1; /* connect */ - socksreq[2] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */ - socksreq[3] = (unsigned char)(remote_port & 0xff); /* PORT LSB */ - - /* DNS resolve only for SOCKS4, not SOCKS4a */ - if(!protocol4a) { - enum resolve_t rc = - Curl_resolv(data, hostname, remote_port, FALSE, &dns); - - if(rc == CURLRESOLV_ERROR) - return CURLPX_RESOLVE_HOST; - else if(rc == CURLRESOLV_PENDING) { - sxstate(data, CONNECT_RESOLVING); - infof(data, "SOCKS4 non-blocking resolve of %s", hostname); - return CURLPX_OK; - } - sxstate(data, CONNECT_RESOLVED); - goto CONNECT_RESOLVED; - } - - /* socks4a doesn't resolve anything locally */ - sxstate(data, CONNECT_REQ_INIT); - goto CONNECT_REQ_INIT; - - case CONNECT_RESOLVING: - /* check if we have the name resolved by now */ - dns = Curl_fetch_addr(data, hostname, (int)conn->port); - - if(dns) { -#ifdef CURLRES_ASYNCH - data->state.async.dns = dns; - data->state.async.done = TRUE; -#endif - infof(data, "Hostname '%s' was found", hostname); - sxstate(data, CONNECT_RESOLVED); - } - else { - result = Curl_resolv_check(data, &dns); - if(!dns) { - if(result) - return CURLPX_RESOLVE_HOST; - return CURLPX_OK; - } - } - /* FALLTHROUGH */ - CONNECT_RESOLVED: - case CONNECT_RESOLVED: { - struct Curl_addrinfo *hp = NULL; - /* - * We cannot use 'hostent' as a struct that Curl_resolv() returns. It - * returns a Curl_addrinfo pointer that may not always look the same. - */ - if(dns) { - hp = dns->addr; - - /* scan for the first IPv4 address */ - while(hp && (hp->ai_family != AF_INET)) - hp = hp->ai_next; - - if(hp) { - struct sockaddr_in *saddr_in; - char buf[64]; - Curl_printable_address(hp, buf, sizeof(buf)); - - saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr; - socksreq[4] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[0]; - socksreq[5] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[1]; - socksreq[6] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[2]; - socksreq[7] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[3]; - - infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)", buf); - - Curl_resolv_unlock(data, dns); /* not used anymore from now on */ - } - else - failf(data, "SOCKS4 connection to %s not supported", hostname); - } - else - failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", - hostname); - - if(!hp) - return CURLPX_RESOLVE_HOST; - } - /* FALLTHROUGH */ - CONNECT_REQ_INIT: - case CONNECT_REQ_INIT: - /* - * This is currently not supporting "Identification Protocol (RFC1413)". - */ - socksreq[8] = 0; /* ensure empty userid is NUL-terminated */ - if(proxy_user) { - size_t plen = strlen(proxy_user); - if(plen >= (size_t)data->set.buffer_size - 8) { - failf(data, "Too long SOCKS proxy user name, can't use"); - return CURLPX_LONG_USER; - } - /* copy the proxy name WITH trailing zero */ - memcpy(socksreq + 8, proxy_user, plen + 1); - } - - /* - * Make connection - */ - { - size_t packetsize = 9 + - strlen((char *)socksreq + 8); /* size including NUL */ - - /* If SOCKS4a, set special invalid IP address 0.0.0.x */ - if(protocol4a) { - size_t hostnamelen = 0; - socksreq[4] = 0; - socksreq[5] = 0; - socksreq[6] = 0; - socksreq[7] = 1; - /* append hostname */ - hostnamelen = strlen(hostname) + 1; /* length including NUL */ - if(hostnamelen <= 255) - strcpy((char *)socksreq + packetsize, hostname); - else { - failf(data, "SOCKS4: too long host name"); - return CURLPX_LONG_HOSTNAME; - } - packetsize += hostnamelen; - } - sx->outp = socksreq; - sx->outstanding = packetsize; - sxstate(data, CONNECT_REQ_SENDING); - } - /* FALLTHROUGH */ - case CONNECT_REQ_SENDING: - /* Send request */ - result = Curl_write_plain(data, sockfd, (char *)sx->outp, - sx->outstanding, &written); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Failed to send SOCKS4 connect request."); - return CURLPX_SEND_CONNECT; - } - if(written != sx->outstanding) { - /* not done, remain in state */ - sx->outstanding -= written; - sx->outp += written; - return CURLPX_OK; - } - - /* done sending! */ - sx->outstanding = 8; /* receive data size */ - sx->outp = socksreq; - sxstate(data, CONNECT_SOCKS_READ); - - /* FALLTHROUGH */ - case CONNECT_SOCKS_READ: - /* Receive response */ - result = Curl_read_plain(sockfd, (char *)sx->outp, - sx->outstanding, &actualread); - if(result && (CURLE_AGAIN != result)) { - failf(data, "SOCKS4: Failed receiving connect request ack: %s", - curl_easy_strerror(result)); - return CURLPX_RECV_CONNECT; - } - else if(!result && !actualread) { - /* connection closed */ - failf(data, "connection to proxy closed"); - return CURLPX_CLOSED; - } - else if(actualread != sx->outstanding) { - /* remain in reading state */ - sx->outstanding -= actualread; - sx->outp += actualread; - return CURLPX_OK; - } - sxstate(data, CONNECT_DONE); - break; - default: /* lots of unused states in SOCKS4 */ - break; - } - - /* - * Response format - * - * +----+----+----+----+----+----+----+----+ - * | VN | CD | DSTPORT | DSTIP | - * +----+----+----+----+----+----+----+----+ - * # of bytes: 1 1 2 4 - * - * VN is the version of the reply code and should be 0. CD is the result - * code with one of the following values: - * - * 90: request granted - * 91: request rejected or failed - * 92: request rejected because SOCKS server cannot connect to - * identd on the client - * 93: request rejected because the client program and identd - * report different user-ids - */ - - /* wrong version ? */ - if(socksreq[0]) { - failf(data, - "SOCKS4 reply has wrong version, version should be 0."); - return CURLPX_BAD_VERSION; - } - - /* Result */ - switch(socksreq[1]) { - case 90: - infof(data, "SOCKS4%s request granted.", protocol4a?"a":""); - break; - case 91: - failf(data, - "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" - ", request rejected or failed.", - socksreq[4], socksreq[5], socksreq[6], socksreq[7], - (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), - (unsigned char)socksreq[1]); - return CURLPX_REQUEST_FAILED; - case 92: - failf(data, - "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" - ", request rejected because SOCKS server cannot connect to " - "identd on the client.", - socksreq[4], socksreq[5], socksreq[6], socksreq[7], - (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), - (unsigned char)socksreq[1]); - return CURLPX_IDENTD; - case 93: - failf(data, - "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" - ", request rejected because the client program and identd " - "report different user-ids.", - socksreq[4], socksreq[5], socksreq[6], socksreq[7], - (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), - (unsigned char)socksreq[1]); - return CURLPX_IDENTD_DIFFER; - default: - failf(data, - "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" - ", Unknown.", - socksreq[4], socksreq[5], socksreq[6], socksreq[7], - (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), - (unsigned char)socksreq[1]); - return CURLPX_UNKNOWN_FAIL; - } - - *done = TRUE; - return CURLPX_OK; /* Proxy was successful! */ -} - -/* - * This function logs in to a SOCKS5 proxy and sends the specifics to the final - * destination server. - */ -CURLproxycode Curl_SOCKS5(const char *proxy_user, - const char *proxy_password, - const char *hostname, - int remote_port, - int sockindex, - struct Curl_easy *data, - bool *done) -{ - /* - According to the RFC1928, section "6. Replies". This is what a SOCK5 - replies: - - +----+-----+-------+------+----------+----------+ - |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | - +----+-----+-------+------+----------+----------+ - | 1 | 1 | X'00' | 1 | Variable | 2 | - +----+-----+-------+------+----------+----------+ - - Where: - - o VER protocol version: X'05' - o REP Reply field: - o X'00' succeeded - */ - struct connectdata *conn = data->conn; - unsigned char *socksreq = (unsigned char *)data->state.buffer; - char dest[256] = "unknown"; /* printable hostname:port */ - int idx; - ssize_t actualread; - ssize_t written; - CURLcode result; - curl_socket_t sockfd = conn->sock[sockindex]; - bool socks5_resolve_local = - (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE; - const size_t hostname_len = strlen(hostname); - ssize_t len = 0; - const unsigned long auth = data->set.socks5auth; - bool allow_gssapi = FALSE; - struct connstate *sx = &conn->cnnct; - struct Curl_dns_entry *dns = NULL; - - if(!SOCKS_STATE(sx->state) && !*done) - sxstate(data, CONNECT_SOCKS_INIT); - - switch(sx->state) { - case CONNECT_SOCKS_INIT: - if(conn->bits.httpproxy) - infof(data, "SOCKS5: connecting to HTTP proxy %s port %d", - hostname, remote_port); - - /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */ - if(!socks5_resolve_local && hostname_len > 255) { - infof(data, "SOCKS5: server resolving disabled for hostnames of " - "length > 255 [actual len=%zu]", hostname_len); - socks5_resolve_local = TRUE; - } - - if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI)) - infof(data, - "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %lu", - auth); - if(!(auth & CURLAUTH_BASIC)) - /* disable username/password auth */ - proxy_user = NULL; -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - if(auth & CURLAUTH_GSSAPI) - allow_gssapi = TRUE; -#endif - - idx = 0; - socksreq[idx++] = 5; /* version */ - idx++; /* number of authentication methods */ - socksreq[idx++] = 0; /* no authentication */ - if(allow_gssapi) - socksreq[idx++] = 1; /* GSS-API */ - if(proxy_user) - socksreq[idx++] = 2; /* username/password */ - /* write the number of authentication methods */ - socksreq[1] = (unsigned char) (idx - 2); - - result = Curl_write_plain(data, sockfd, (char *)socksreq, idx, &written); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Unable to send initial SOCKS5 request."); - return CURLPX_SEND_CONNECT; - } - if(written != idx) { - sxstate(data, CONNECT_SOCKS_SEND); - sx->outstanding = idx - written; - sx->outp = &socksreq[written]; - return CURLPX_OK; - } - sxstate(data, CONNECT_SOCKS_READ); - goto CONNECT_SOCKS_READ_INIT; - case CONNECT_SOCKS_SEND: - result = Curl_write_plain(data, sockfd, (char *)sx->outp, - sx->outstanding, &written); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Unable to send initial SOCKS5 request."); - return CURLPX_SEND_CONNECT; - } - if(written != sx->outstanding) { - /* not done, remain in state */ - sx->outstanding -= written; - sx->outp += written; - return CURLPX_OK; - } - /* FALLTHROUGH */ - CONNECT_SOCKS_READ_INIT: - case CONNECT_SOCKS_READ_INIT: - sx->outstanding = 2; /* expect two bytes */ - sx->outp = socksreq; /* store it here */ - /* FALLTHROUGH */ - case CONNECT_SOCKS_READ: - result = Curl_read_plain(sockfd, (char *)sx->outp, - sx->outstanding, &actualread); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Unable to receive initial SOCKS5 response."); - return CURLPX_RECV_CONNECT; - } - else if(!result && !actualread) { - /* connection closed */ - failf(data, "Connection to proxy closed"); - return CURLPX_CLOSED; - } - else if(actualread != sx->outstanding) { - /* remain in reading state */ - sx->outstanding -= actualread; - sx->outp += actualread; - return CURLPX_OK; - } - else if(socksreq[0] != 5) { - failf(data, "Received invalid version in initial SOCKS5 response."); - return CURLPX_BAD_VERSION; - } - else if(socksreq[1] == 0) { - /* DONE! No authentication needed. Send request. */ - sxstate(data, CONNECT_REQ_INIT); - goto CONNECT_REQ_INIT; - } - else if(socksreq[1] == 2) { - /* regular name + password authentication */ - sxstate(data, CONNECT_AUTH_INIT); - goto CONNECT_AUTH_INIT; - } -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - else if(allow_gssapi && (socksreq[1] == 1)) { - sxstate(data, CONNECT_GSSAPI_INIT); - result = Curl_SOCKS5_gssapi_negotiate(sockindex, data); - if(result) { - failf(data, "Unable to negotiate SOCKS5 GSS-API context."); - return CURLPX_GSSAPI; - } - } -#endif - else { - /* error */ - if(!allow_gssapi && (socksreq[1] == 1)) { - failf(data, - "SOCKS5 GSSAPI per-message authentication is not supported."); - return CURLPX_GSSAPI_PERMSG; - } - else if(socksreq[1] == 255) { - failf(data, "No authentication method was acceptable."); - return CURLPX_NO_AUTH; - } - } - failf(data, - "Undocumented SOCKS5 mode attempted to be used by server."); - return CURLPX_UNKNOWN_MODE; -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - case CONNECT_GSSAPI_INIT: - /* GSSAPI stuff done non-blocking */ - break; -#endif - - default: /* do nothing! */ - break; - - CONNECT_AUTH_INIT: - case CONNECT_AUTH_INIT: { - /* Needs user name and password */ - size_t proxy_user_len, proxy_password_len; - if(proxy_user && proxy_password) { - proxy_user_len = strlen(proxy_user); - proxy_password_len = strlen(proxy_password); - } - else { - proxy_user_len = 0; - proxy_password_len = 0; - } - - /* username/password request looks like - * +----+------+----------+------+----------+ - * |VER | ULEN | UNAME | PLEN | PASSWD | - * +----+------+----------+------+----------+ - * | 1 | 1 | 1 to 255 | 1 | 1 to 255 | - * +----+------+----------+------+----------+ - */ - len = 0; - socksreq[len++] = 1; /* username/pw subnegotiation version */ - socksreq[len++] = (unsigned char) proxy_user_len; - if(proxy_user && proxy_user_len) { - /* the length must fit in a single byte */ - if(proxy_user_len >= 255) { - failf(data, "Excessive user name length for proxy auth"); - return CURLPX_LONG_USER; - } - memcpy(socksreq + len, proxy_user, proxy_user_len); - } - len += proxy_user_len; - socksreq[len++] = (unsigned char) proxy_password_len; - if(proxy_password && proxy_password_len) { - /* the length must fit in a single byte */ - if(proxy_password_len > 255) { - failf(data, "Excessive password length for proxy auth"); - return CURLPX_LONG_PASSWD; - } - memcpy(socksreq + len, proxy_password, proxy_password_len); - } - len += proxy_password_len; - sxstate(data, CONNECT_AUTH_SEND); - sx->outstanding = len; - sx->outp = socksreq; - } - /* FALLTHROUGH */ - case CONNECT_AUTH_SEND: - result = Curl_write_plain(data, sockfd, (char *)sx->outp, - sx->outstanding, &written); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Failed to send SOCKS5 sub-negotiation request."); - return CURLPX_SEND_AUTH; - } - if(sx->outstanding != written) { - /* remain in state */ - sx->outstanding -= written; - sx->outp += written; - return CURLPX_OK; - } - sx->outp = socksreq; - sx->outstanding = 2; - sxstate(data, CONNECT_AUTH_READ); - /* FALLTHROUGH */ - case CONNECT_AUTH_READ: - result = Curl_read_plain(sockfd, (char *)sx->outp, - sx->outstanding, &actualread); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Unable to receive SOCKS5 sub-negotiation response."); - return CURLPX_RECV_AUTH; - } - else if(!result && !actualread) { - /* connection closed */ - failf(data, "connection to proxy closed"); - return CURLPX_CLOSED; - } - else if(actualread != sx->outstanding) { - /* remain in state */ - sx->outstanding -= actualread; - sx->outp += actualread; - return CURLPX_OK; - } - /* ignore the first (VER) byte */ - else if(socksreq[1]) { /* status */ - failf(data, "User was rejected by the SOCKS5 server (%d %d).", - socksreq[0], socksreq[1]); - return CURLPX_USER_REJECTED; - } - - /* Everything is good so far, user was authenticated! */ - sxstate(data, CONNECT_REQ_INIT); - /* FALLTHROUGH */ - CONNECT_REQ_INIT: - case CONNECT_REQ_INIT: - if(socks5_resolve_local) { - enum resolve_t rc = Curl_resolv(data, hostname, remote_port, - FALSE, &dns); - - if(rc == CURLRESOLV_ERROR) - return CURLPX_RESOLVE_HOST; - - if(rc == CURLRESOLV_PENDING) { - sxstate(data, CONNECT_RESOLVING); - return CURLPX_OK; - } - sxstate(data, CONNECT_RESOLVED); - goto CONNECT_RESOLVED; - } - goto CONNECT_RESOLVE_REMOTE; - - case CONNECT_RESOLVING: - /* check if we have the name resolved by now */ - dns = Curl_fetch_addr(data, hostname, remote_port); - - if(dns) { -#ifdef CURLRES_ASYNCH - data->state.async.dns = dns; - data->state.async.done = TRUE; -#endif - infof(data, "SOCKS5: hostname '%s' found", hostname); - } - - if(!dns) { - result = Curl_resolv_check(data, &dns); - if(!dns) { - if(result) - return CURLPX_RESOLVE_HOST; - return CURLPX_OK; - } - } - /* FALLTHROUGH */ - CONNECT_RESOLVED: - case CONNECT_RESOLVED: { - struct Curl_addrinfo *hp = NULL; - size_t destlen; - if(dns) - hp = dns->addr; - if(!hp) { - failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", - hostname); - return CURLPX_RESOLVE_HOST; - } - - Curl_printable_address(hp, dest, sizeof(dest)); - destlen = strlen(dest); - msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", remote_port); - - len = 0; - socksreq[len++] = 5; /* version (SOCKS5) */ - socksreq[len++] = 1; /* connect */ - socksreq[len++] = 0; /* must be zero */ - if(hp->ai_family == AF_INET) { - int i; - struct sockaddr_in *saddr_in; - socksreq[len++] = 1; /* ATYP: IPv4 = 1 */ - - saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr; - for(i = 0; i < 4; i++) { - socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i]; - } - - infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)", dest); - } -#ifdef ENABLE_IPV6 - else if(hp->ai_family == AF_INET6) { - int i; - struct sockaddr_in6 *saddr_in6; - socksreq[len++] = 4; /* ATYP: IPv6 = 4 */ - - saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr; - for(i = 0; i < 16; i++) { - socksreq[len++] = - ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i]; - } - - infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)", dest); - } -#endif - else { - hp = NULL; /* fail! */ - failf(data, "SOCKS5 connection to %s not supported", dest); - } - - Curl_resolv_unlock(data, dns); /* not used anymore from now on */ - goto CONNECT_REQ_SEND; - } - CONNECT_RESOLVE_REMOTE: - case CONNECT_RESOLVE_REMOTE: - /* Authentication is complete, now specify destination to the proxy */ - len = 0; - socksreq[len++] = 5; /* version (SOCKS5) */ - socksreq[len++] = 1; /* connect */ - socksreq[len++] = 0; /* must be zero */ - - if(!socks5_resolve_local) { - /* ATYP: domain name = 3, - IPv6 == 4, - IPv4 == 1 */ - unsigned char ip4[4]; -#ifdef ENABLE_IPV6 - if(conn->bits.ipv6_ip) { - char ip6[16]; - if(1 != Curl_inet_pton(AF_INET6, hostname, ip6)) - return CURLPX_BAD_ADDRESS_TYPE; - socksreq[len++] = 4; - memcpy(&socksreq[len], ip6, sizeof(ip6)); - len += sizeof(ip6); - } - else -#endif - if(1 == Curl_inet_pton(AF_INET, hostname, ip4)) { - socksreq[len++] = 1; - memcpy(&socksreq[len], ip4, sizeof(ip4)); - len += sizeof(ip4); - } - else { - socksreq[len++] = 3; - socksreq[len++] = (char) hostname_len; /* one byte address length */ - memcpy(&socksreq[len], hostname, hostname_len); /* address w/o NULL */ - len += hostname_len; - } - infof(data, "SOCKS5 connect to %s:%d (remotely resolved)", - hostname, remote_port); - } - /* FALLTHROUGH */ - - CONNECT_REQ_SEND: - case CONNECT_REQ_SEND: - /* PORT MSB */ - socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff); - /* PORT LSB */ - socksreq[len++] = (unsigned char)(remote_port & 0xff); - -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - if(conn->socks5_gssapi_enctype) { - failf(data, "SOCKS5 GSS-API protection not yet implemented."); - return CURLPX_GSSAPI_PROTECTION; - } -#endif - sx->outp = socksreq; - sx->outstanding = len; - sxstate(data, CONNECT_REQ_SENDING); - /* FALLTHROUGH */ - case CONNECT_REQ_SENDING: - result = Curl_write_plain(data, sockfd, (char *)sx->outp, - sx->outstanding, &written); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Failed to send SOCKS5 connect request."); - return CURLPX_SEND_REQUEST; - } - if(sx->outstanding != written) { - /* remain in state */ - sx->outstanding -= written; - sx->outp += written; - return CURLPX_OK; - } -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - if(conn->socks5_gssapi_enctype) { - failf(data, "SOCKS5 GSS-API protection not yet implemented."); - return CURLPX_GSSAPI_PROTECTION; - } -#endif - sx->outstanding = 10; /* minimum packet size is 10 */ - sx->outp = socksreq; - sxstate(data, CONNECT_REQ_READ); - /* FALLTHROUGH */ - case CONNECT_REQ_READ: - result = Curl_read_plain(sockfd, (char *)sx->outp, - sx->outstanding, &actualread); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Failed to receive SOCKS5 connect request ack."); - return CURLPX_RECV_REQACK; - } - else if(!result && !actualread) { - /* connection closed */ - failf(data, "connection to proxy closed"); - return CURLPX_CLOSED; - } - else if(actualread != sx->outstanding) { - /* remain in state */ - sx->outstanding -= actualread; - sx->outp += actualread; - return CURLPX_OK; - } - - if(socksreq[0] != 5) { /* version */ - failf(data, - "SOCKS5 reply has wrong version, version should be 5."); - return CURLPX_BAD_VERSION; - } - else if(socksreq[1]) { /* Anything besides 0 is an error */ - CURLproxycode rc = CURLPX_REPLY_UNASSIGNED; - int code = socksreq[1]; - failf(data, "Can't complete SOCKS5 connection to %s. (%d)", - hostname, (unsigned char)socksreq[1]); - if(code < 9) { - /* RFC 1928 section 6 lists: */ - static const CURLproxycode lookup[] = { - CURLPX_OK, - CURLPX_REPLY_GENERAL_SERVER_FAILURE, - CURLPX_REPLY_NOT_ALLOWED, - CURLPX_REPLY_NETWORK_UNREACHABLE, - CURLPX_REPLY_HOST_UNREACHABLE, - CURLPX_REPLY_CONNECTION_REFUSED, - CURLPX_REPLY_TTL_EXPIRED, - CURLPX_REPLY_COMMAND_NOT_SUPPORTED, - CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED, - }; - rc = lookup[code]; - } - return rc; - } - - /* Fix: in general, returned BND.ADDR is variable length parameter by RFC - 1928, so the reply packet should be read until the end to avoid errors - at subsequent protocol level. - - +----+-----+-------+------+----------+----------+ - |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | - +----+-----+-------+------+----------+----------+ - | 1 | 1 | X'00' | 1 | Variable | 2 | - +----+-----+-------+------+----------+----------+ - - ATYP: - o IP v4 address: X'01', BND.ADDR = 4 byte - o domain name: X'03', BND.ADDR = [ 1 byte length, string ] - o IP v6 address: X'04', BND.ADDR = 16 byte - */ - - /* Calculate real packet size */ - if(socksreq[3] == 3) { - /* domain name */ - int addrlen = (int) socksreq[4]; - len = 5 + addrlen + 2; - } - else if(socksreq[3] == 4) { - /* IPv6 */ - len = 4 + 16 + 2; - } - else if(socksreq[3] == 1) { - len = 4 + 4 + 2; - } - else { - failf(data, "SOCKS5 reply has wrong address type."); - return CURLPX_BAD_ADDRESS_TYPE; - } - - /* At this point we already read first 10 bytes */ -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - if(!conn->socks5_gssapi_enctype) { - /* decrypt_gssapi_blockread already read the whole packet */ -#endif - if(len > 10) { - sx->outstanding = len - 10; /* get the rest */ - sx->outp = &socksreq[10]; - sxstate(data, CONNECT_REQ_READ_MORE); - } - else { - sxstate(data, CONNECT_DONE); - break; - } -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - } -#endif - /* FALLTHROUGH */ - case CONNECT_REQ_READ_MORE: - result = Curl_read_plain(sockfd, (char *)sx->outp, - sx->outstanding, &actualread); - if(result && (CURLE_AGAIN != result)) { - failf(data, "Failed to receive SOCKS5 connect request ack."); - return CURLPX_RECV_ADDRESS; - } - else if(!result && !actualread) { - /* connection closed */ - failf(data, "connection to proxy closed"); - return CURLPX_CLOSED; - } - else if(actualread != sx->outstanding) { - /* remain in state */ - sx->outstanding -= actualread; - sx->outp += actualread; - return CURLPX_OK; - } - sxstate(data, CONNECT_DONE); - } - infof(data, "SOCKS5 request granted."); - - *done = TRUE; - return CURLPX_OK; /* Proxy was successful! */ -} - -#endif /* CURL_DISABLE_PROXY */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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)
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "select.h"
+#include "cfilters.h"
+#include "connect.h"
+#include "timeval.h"
+#include "socks.h"
+#include "multiif.h" /* for getsock macros */
+#include "inet_pton.h"
+#include "url.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* for the (SOCKS) connect state machine */
+enum connect_t {
+ CONNECT_INIT,
+ CONNECT_SOCKS_INIT, /* 1 */
+ CONNECT_SOCKS_SEND, /* 2 waiting to send more first data */
+ CONNECT_SOCKS_READ_INIT, /* 3 set up read */
+ CONNECT_SOCKS_READ, /* 4 read server response */
+ CONNECT_GSSAPI_INIT, /* 5 */
+ CONNECT_AUTH_INIT, /* 6 setup outgoing auth buffer */
+ CONNECT_AUTH_SEND, /* 7 send auth */
+ CONNECT_AUTH_READ, /* 8 read auth response */
+ CONNECT_REQ_INIT, /* 9 init SOCKS "request" */
+ CONNECT_RESOLVING, /* 10 */
+ CONNECT_RESOLVED, /* 11 */
+ CONNECT_RESOLVE_REMOTE, /* 12 */
+ CONNECT_REQ_SEND, /* 13 */
+ CONNECT_REQ_SENDING, /* 14 */
+ CONNECT_REQ_READ, /* 15 */
+ CONNECT_REQ_READ_MORE, /* 16 */
+ CONNECT_DONE /* 17 connected fine to the remote or the SOCKS proxy */
+};
+
+struct socks_state {
+ enum connect_t state;
+ ssize_t outstanding; /* send this many bytes more */
+ unsigned char *outp; /* send from this pointer */
+
+ const char *hostname;
+ int remote_port;
+ const char *proxy_user;
+ const char *proxy_password;
+};
+
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+/*
+ * Helper read-from-socket functions. Does the same as Curl_read() but it
+ * blocks until all bytes amount of buffersize will be read. No more, no less.
+ *
+ * This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions.
+ */
+int Curl_blockread_all(struct Curl_easy *data, /* transfer */
+ curl_socket_t sockfd, /* read from this socket */
+ char *buf, /* store read data here */
+ ssize_t buffersize, /* max amount to read */
+ ssize_t *n) /* amount bytes read */
+{
+ ssize_t nread = 0;
+ ssize_t allread = 0;
+ int result;
+ *n = 0;
+ for(;;) {
+ timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+ if(timeout_ms < 0) {
+ /* we already got the timeout */
+ result = CURLE_OPERATION_TIMEDOUT;
+ break;
+ }
+ if(!timeout_ms)
+ timeout_ms = TIMEDIFF_T_MAX;
+ if(SOCKET_READABLE(sockfd, timeout_ms) <= 0) {
+ result = ~CURLE_OK;
+ break;
+ }
+ result = Curl_read_plain(data, sockfd, buf, buffersize, &nread);
+ if(CURLE_AGAIN == result)
+ continue;
+ if(result)
+ break;
+
+ if(buffersize == nread) {
+ allread += nread;
+ *n = allread;
+ result = CURLE_OK;
+ break;
+ }
+ if(!nread) {
+ result = ~CURLE_OK;
+ break;
+ }
+
+ buffersize -= nread;
+ buf += nread;
+ allread += nread;
+ }
+ return result;
+}
+#endif
+
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+#define DEBUG_AND_VERBOSE
+#define sxstate(x,d,y) socksstate(x,d,y, __LINE__)
+#else
+#define sxstate(x,d,y) socksstate(x,d,y)
+#endif
+
+/* always use this function to change state, to make debugging easier */
+static void socksstate(struct socks_state *sx, struct Curl_easy *data,
+ enum connect_t state
+#ifdef DEBUG_AND_VERBOSE
+ , int lineno
+#endif
+)
+{
+ enum connect_t oldstate = sx->state;
+#ifdef DEBUG_AND_VERBOSE
+ /* synced with the state list in urldata.h */
+ static const char * const statename[] = {
+ "INIT",
+ "SOCKS_INIT",
+ "SOCKS_SEND",
+ "SOCKS_READ_INIT",
+ "SOCKS_READ",
+ "GSSAPI_INIT",
+ "AUTH_INIT",
+ "AUTH_SEND",
+ "AUTH_READ",
+ "REQ_INIT",
+ "RESOLVING",
+ "RESOLVED",
+ "RESOLVE_REMOTE",
+ "REQ_SEND",
+ "REQ_SENDING",
+ "REQ_READ",
+ "REQ_READ_MORE",
+ "DONE"
+ };
+#endif
+
+ (void)data;
+ if(oldstate == state)
+ /* don't bother when the new state is the same as the old state */
+ return;
+
+ sx->state = state;
+
+#ifdef DEBUG_AND_VERBOSE
+ infof(data,
+ "SXSTATE: %s => %s; line %d",
+ statename[oldstate], statename[sx->state],
+ lineno);
+#endif
+}
+
+/*
+* This function logs in to a SOCKS4 proxy and sends the specifics to the final
+* destination server.
+*
+* Reference :
+* https://www.openssh.com/txt/socks4.protocol
+*
+* Note :
+* Set protocol4a=true for "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
+* Nonsupport "Identification Protocol (RFC1413)"
+*/
+static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
+ struct socks_state *sx,
+ struct Curl_easy *data)
+{
+ struct connectdata *conn = cf->conn;
+ const bool protocol4a =
+ (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE;
+ unsigned char *socksreq = (unsigned char *)data->state.buffer;
+ CURLcode result;
+ curl_socket_t sockfd = conn->sock[cf->sockindex];
+ struct Curl_dns_entry *dns = NULL;
+ ssize_t actualread;
+ ssize_t written;
+
+ /* make sure that the buffer is at least 600 bytes */
+ DEBUGASSERT(READBUFFER_MIN >= 600);
+
+ switch(sx->state) {
+ case CONNECT_SOCKS_INIT:
+ /* SOCKS4 can only do IPv4, insist! */
+ conn->ip_version = CURL_IPRESOLVE_V4;
+ if(conn->bits.httpproxy)
+ infof(data, "SOCKS4%s: connecting to HTTP proxy %s port %d",
+ protocol4a ? "a" : "", sx->hostname, sx->remote_port);
+
+ infof(data, "SOCKS4 communication to %s:%d",
+ sx->hostname, sx->remote_port);
+
+ /*
+ * Compose socks4 request
+ *
+ * Request format
+ *
+ * +----+----+----+----+----+----+----+----+----+----+....+----+
+ * | VN | CD | DSTPORT | DSTIP | USERID |NULL|
+ * +----+----+----+----+----+----+----+----+----+----+....+----+
+ * # of bytes: 1 1 2 4 variable 1
+ */
+
+ socksreq[0] = 4; /* version (SOCKS4) */
+ socksreq[1] = 1; /* connect */
+ socksreq[2] = (unsigned char)((sx->remote_port >> 8) & 0xff); /* MSB */
+ socksreq[3] = (unsigned char)(sx->remote_port & 0xff); /* LSB */
+
+ /* DNS resolve only for SOCKS4, not SOCKS4a */
+ if(!protocol4a) {
+ enum resolve_t rc =
+ Curl_resolv(data, sx->hostname, sx->remote_port, FALSE, &dns);
+
+ if(rc == CURLRESOLV_ERROR)
+ return CURLPX_RESOLVE_HOST;
+ else if(rc == CURLRESOLV_PENDING) {
+ sxstate(sx, data, CONNECT_RESOLVING);
+ infof(data, "SOCKS4 non-blocking resolve of %s", sx->hostname);
+ return CURLPX_OK;
+ }
+ sxstate(sx, data, CONNECT_RESOLVED);
+ goto CONNECT_RESOLVED;
+ }
+
+ /* socks4a doesn't resolve anything locally */
+ sxstate(sx, data, CONNECT_REQ_INIT);
+ goto CONNECT_REQ_INIT;
+
+ case CONNECT_RESOLVING:
+ /* check if we have the name resolved by now */
+ dns = Curl_fetch_addr(data, sx->hostname, (int)conn->port);
+
+ if(dns) {
+#ifdef CURLRES_ASYNCH
+ data->state.async.dns = dns;
+ data->state.async.done = TRUE;
+#endif
+ infof(data, "Hostname '%s' was found", sx->hostname);
+ sxstate(sx, data, CONNECT_RESOLVED);
+ }
+ else {
+ result = Curl_resolv_check(data, &dns);
+ if(!dns) {
+ if(result)
+ return CURLPX_RESOLVE_HOST;
+ return CURLPX_OK;
+ }
+ }
+ /* FALLTHROUGH */
+ CONNECT_RESOLVED:
+ case CONNECT_RESOLVED: {
+ struct Curl_addrinfo *hp = NULL;
+ /*
+ * We cannot use 'hostent' as a struct that Curl_resolv() returns. It
+ * returns a Curl_addrinfo pointer that may not always look the same.
+ */
+ if(dns) {
+ hp = dns->addr;
+
+ /* scan for the first IPv4 address */
+ while(hp && (hp->ai_family != AF_INET))
+ hp = hp->ai_next;
+
+ if(hp) {
+ struct sockaddr_in *saddr_in;
+ char buf[64];
+ Curl_printable_address(hp, buf, sizeof(buf));
+
+ saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
+ socksreq[4] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[0];
+ socksreq[5] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[1];
+ socksreq[6] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[2];
+ socksreq[7] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[3];
+
+ infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)", buf);
+
+ Curl_resolv_unlock(data, dns); /* not used anymore from now on */
+ }
+ else
+ failf(data, "SOCKS4 connection to %s not supported", sx->hostname);
+ }
+ else
+ failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
+ sx->hostname);
+
+ if(!hp)
+ return CURLPX_RESOLVE_HOST;
+ }
+ /* FALLTHROUGH */
+ CONNECT_REQ_INIT:
+ case CONNECT_REQ_INIT:
+ /*
+ * This is currently not supporting "Identification Protocol (RFC1413)".
+ */
+ socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
+ if(sx->proxy_user) {
+ size_t plen = strlen(sx->proxy_user);
+ if(plen >= (size_t)data->set.buffer_size - 8) {
+ failf(data, "Too long SOCKS proxy user name, can't use");
+ return CURLPX_LONG_USER;
+ }
+ /* copy the proxy name WITH trailing zero */
+ memcpy(socksreq + 8, sx->proxy_user, plen + 1);
+ }
+
+ /*
+ * Make connection
+ */
+ {
+ size_t packetsize = 9 +
+ strlen((char *)socksreq + 8); /* size including NUL */
+
+ /* If SOCKS4a, set special invalid IP address 0.0.0.x */
+ if(protocol4a) {
+ size_t hostnamelen = 0;
+ socksreq[4] = 0;
+ socksreq[5] = 0;
+ socksreq[6] = 0;
+ socksreq[7] = 1;
+ /* append hostname */
+ hostnamelen = strlen(sx->hostname) + 1; /* length including NUL */
+ if(hostnamelen <= 255)
+ strcpy((char *)socksreq + packetsize, sx->hostname);
+ else {
+ failf(data, "SOCKS4: too long host name");
+ return CURLPX_LONG_HOSTNAME;
+ }
+ packetsize += hostnamelen;
+ }
+ sx->outp = socksreq;
+ sx->outstanding = packetsize;
+ sxstate(sx, data, CONNECT_REQ_SENDING);
+ }
+ /* FALLTHROUGH */
+ case CONNECT_REQ_SENDING:
+ /* Send request */
+ result = Curl_write_plain(data, sockfd, (char *)sx->outp,
+ sx->outstanding, &written);
+ if(result && (CURLE_AGAIN != result)) {
+ failf(data, "Failed to send SOCKS4 connect request.");
+ return CURLPX_SEND_CONNECT;
+ }
+ if(written != sx->outstanding) {
+ /* not done, remain in state */
+ sx->outstanding -= written;
+ sx->outp += written;
+ return CURLPX_OK;
+ }
+
+ /* done sending! */
+ sx->outstanding = 8; /* receive data size */
+ sx->outp = socksreq;
+ sxstate(sx, data, CONNECT_SOCKS_READ);
+
+ /* FALLTHROUGH */
+ case CONNECT_SOCKS_READ:
+ /* Receive response */
+ result = Curl_read_plain(data, sockfd, (char *)sx->outp,
+ sx->outstanding, &actualread);
+ if(result && (CURLE_AGAIN != result)) {
+ failf(data, "SOCKS4: Failed receiving connect request ack: %s",
+ curl_easy_strerror(result));
+ return CURLPX_RECV_CONNECT;
+ }
+ else if(!result && !actualread) {
+ /* connection closed */
+ failf(data, "connection to proxy closed");
+ return CURLPX_CLOSED;
+ }
+ else if(actualread != sx->outstanding) {
+ /* remain in reading state */
+ sx->outstanding -= actualread;
+ sx->outp += actualread;
+ return CURLPX_OK;
+ }
+ sxstate(sx, data, CONNECT_DONE);
+ break;
+ default: /* lots of unused states in SOCKS4 */
+ break;
+ }
+
+ /*
+ * Response format
+ *
+ * +----+----+----+----+----+----+----+----+
+ * | VN | CD | DSTPORT | DSTIP |
+ * +----+----+----+----+----+----+----+----+
+ * # of bytes: 1 1 2 4
+ *
+ * VN is the version of the reply code and should be 0. CD is the result
+ * code with one of the following values:
+ *
+ * 90: request granted
+ * 91: request rejected or failed
+ * 92: request rejected because SOCKS server cannot connect to
+ * identd on the client
+ * 93: request rejected because the client program and identd
+ * report different user-ids
+ */
+
+ /* wrong version ? */
+ if(socksreq[0]) {
+ failf(data,
+ "SOCKS4 reply has wrong version, version should be 0.");
+ return CURLPX_BAD_VERSION;
+ }
+
+ /* Result */
+ switch(socksreq[1]) {
+ case 90:
+ infof(data, "SOCKS4%s request granted.", protocol4a?"a":"");
+ break;
+ case 91:
+ failf(data,
+ "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
+ ", request rejected or failed.",
+ socksreq[4], socksreq[5], socksreq[6], socksreq[7],
+ (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
+ (unsigned char)socksreq[1]);
+ return CURLPX_REQUEST_FAILED;
+ case 92:
+ failf(data,
+ "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
+ ", request rejected because SOCKS server cannot connect to "
+ "identd on the client.",
+ socksreq[4], socksreq[5], socksreq[6], socksreq[7],
+ (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
+ (unsigned char)socksreq[1]);
+ return CURLPX_IDENTD;
+ case 93:
+ failf(data,
+ "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
+ ", request rejected because the client program and identd "
+ "report different user-ids.",
+ socksreq[4], socksreq[5], socksreq[6], socksreq[7],
+ (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
+ (unsigned char)socksreq[1]);
+ return CURLPX_IDENTD_DIFFER;
+ default:
+ failf(data,
+ "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
+ ", Unknown.",
+ socksreq[4], socksreq[5], socksreq[6], socksreq[7],
+ (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
+ (unsigned char)socksreq[1]);
+ return CURLPX_UNKNOWN_FAIL;
+ }
+
+ return CURLPX_OK; /* Proxy was successful! */
+}
+
+/*
+ * This function logs in to a SOCKS5 proxy and sends the specifics to the final
+ * destination server.
+ */
+static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
+ struct socks_state *sx,
+ struct Curl_easy *data)
+{
+ /*
+ According to the RFC1928, section "6. Replies". This is what a SOCK5
+ replies:
+
+ +----+-----+-------+------+----------+----------+
+ |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
+ +----+-----+-------+------+----------+----------+
+ | 1 | 1 | X'00' | 1 | Variable | 2 |
+ +----+-----+-------+------+----------+----------+
+
+ Where:
+
+ o VER protocol version: X'05'
+ o REP Reply field:
+ o X'00' succeeded
+ */
+ struct connectdata *conn = cf->conn;
+ unsigned char *socksreq = (unsigned char *)data->state.buffer;
+ char dest[256] = "unknown"; /* printable hostname:port */
+ int idx;
+ ssize_t actualread;
+ ssize_t written;
+ CURLcode result;
+ curl_socket_t sockfd = conn->sock[cf->sockindex];
+ bool socks5_resolve_local =
+ (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE;
+ const size_t hostname_len = strlen(sx->hostname);
+ ssize_t len = 0;
+ const unsigned char auth = data->set.socks5auth;
+ bool allow_gssapi = FALSE;
+ struct Curl_dns_entry *dns = NULL;
+
+ DEBUGASSERT(auth & (CURLAUTH_BASIC | CURLAUTH_GSSAPI));
+ switch(sx->state) {
+ case CONNECT_SOCKS_INIT:
+ if(conn->bits.httpproxy)
+ infof(data, "SOCKS5: connecting to HTTP proxy %s port %d",
+ sx->hostname, sx->remote_port);
+
+ /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
+ if(!socks5_resolve_local && hostname_len > 255) {
+ infof(data, "SOCKS5: server resolving disabled for hostnames of "
+ "length > 255 [actual len=%zu]", hostname_len);
+ socks5_resolve_local = TRUE;
+ }
+
+ if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
+ infof(data,
+ "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %u",
+ auth);
+ if(!(auth & CURLAUTH_BASIC))
+ /* disable username/password auth */
+ sx->proxy_user = NULL;
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+ if(auth & CURLAUTH_GSSAPI)
+ allow_gssapi = TRUE;
+#endif
+
+ idx = 0;
+ socksreq[idx++] = 5; /* version */
+ idx++; /* number of authentication methods */
+ socksreq[idx++] = 0; /* no authentication */
+ if(allow_gssapi)
+ socksreq[idx++] = 1; /* GSS-API */
+ if(sx->proxy_user)
+ socksreq[idx++] = 2; /* username/password */
+ /* write the number of authentication methods */
+ socksreq[1] = (unsigned char) (idx - 2);
+
+ result = Curl_write_plain(data, sockfd, socksreq, idx, &written);
+ if(result && (CURLE_AGAIN != result)) {
+ failf(data, "Unable to send initial SOCKS5 request.");
+ return CURLPX_SEND_CONNECT;
+ }
+ if(written != idx) {
+ sxstate(sx, data, CONNECT_SOCKS_SEND);
+ sx->outstanding = idx - written;
+ sx->outp = &socksreq[written];
+ return CURLPX_OK;
+ }
+ sxstate(sx, data, CONNECT_SOCKS_READ);
+ goto CONNECT_SOCKS_READ_INIT;
+ case CONNECT_SOCKS_SEND:
+ result = Curl_write_plain(data, sockfd, (char *)sx->outp,
+ sx->outstanding, &written);
+ if(result && (CURLE_AGAIN != result)) {
+ failf(data, "Unable to send initial SOCKS5 request.");
+ return CURLPX_SEND_CONNECT;
+ }
+ if(written != sx->outstanding) {
+ /* not done, remain in state */
+ sx->outstanding -= written;
+ sx->outp += written;
+ return CURLPX_OK;
+ }
+ /* FALLTHROUGH */
+ CONNECT_SOCKS_READ_INIT:
+ case CONNECT_SOCKS_READ_INIT:
+ sx->outstanding = 2; /* expect two bytes */
+ sx->outp = socksreq; /* store it here */
+ /* FALLTHROUGH */
+ case CONNECT_SOCKS_READ:
+ result = Curl_read_plain(data, sockfd, (char *)sx->outp,
+ sx->outstanding, &actualread);
+ if(result && (CURLE_AGAIN != result)) {
+ failf(data, "Unable to receive initial SOCKS5 response.");
+ return CURLPX_RECV_CONNECT;
+ }
+ else if(!result && !actualread) {
+ /* connection closed */
+ failf(data, "Connection to proxy closed");
+ return CURLPX_CLOSED;
+ }
+ else if(actualread != sx->outstanding) {
+ /* remain in reading state */
+ sx->outstanding -= actualread;
+ sx->outp += actualread;
+ return CURLPX_OK;
+ }
+ else if(socksreq[0] != 5) {
+ failf(data, "Received invalid version in initial SOCKS5 response.");
+ return CURLPX_BAD_VERSION;
+ }
+ else if(socksreq[1] == 0) {
+ /* DONE! No authentication needed. Send request. */
+ sxstate(sx, data, CONNECT_REQ_INIT);
+ goto CONNECT_REQ_INIT;
+ }
+ else if(socksreq[1] == 2) {
+ /* regular name + password authentication */
+ sxstate(sx, data, CONNECT_AUTH_INIT);
+ goto CONNECT_AUTH_INIT;
+ }
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+ else if(allow_gssapi && (socksreq[1] == 1)) {
+ sxstate(sx, data, CONNECT_GSSAPI_INIT);
+ result = Curl_SOCKS5_gssapi_negotiate(cf->sockindex, data);
+ if(result) {
+ failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
+ return CURLPX_GSSAPI;
+ }
+ }
+#endif
+ else {
+ /* error */
+ if(!allow_gssapi && (socksreq[1] == 1)) {
+ failf(data,
+ "SOCKS5 GSSAPI per-message authentication is not supported.");
+ return CURLPX_GSSAPI_PERMSG;
+ }
+ else if(socksreq[1] == 255) {
+ failf(data, "No authentication method was acceptable.");
+ return CURLPX_NO_AUTH;
+ }
+ }
+ failf(data,
+ "Undocumented SOCKS5 mode attempted to be used by server.");
+ return CURLPX_UNKNOWN_MODE;
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+ case CONNECT_GSSAPI_INIT:
+ /* GSSAPI stuff done non-blocking */
+ break;
+#endif
+
+ default: /* do nothing! */
+ break;
+
+ CONNECT_AUTH_INIT:
+ case CONNECT_AUTH_INIT: {
+ /* Needs user name and password */
+ size_t proxy_user_len, proxy_password_len;
+ if(sx->proxy_user && sx->proxy_password) {
+ proxy_user_len = strlen(sx->proxy_user);
+ proxy_password_len = strlen(sx->proxy_password);
+ }
+ else {
+ proxy_user_len = 0;
+ proxy_password_len = 0;
+ }
+
+ /* username/password request looks like
+ * +----+------+----------+------+----------+
+ * |VER | ULEN | UNAME | PLEN | PASSWD |
+ * +----+------+----------+------+----------+
+ * | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
+ * +----+------+----------+------+----------+
+ */
+ len = 0;
+ socksreq[len++] = 1; /* username/pw subnegotiation version */
+ socksreq[len++] = (unsigned char) proxy_user_len;
+ if(sx->proxy_user && proxy_user_len) {
+ /* the length must fit in a single byte */
+ if(proxy_user_len > 255) {
+ failf(data, "Excessive user name length for proxy auth");
+ return CURLPX_LONG_USER;
+ }
+ memcpy(socksreq + len, sx->proxy_user, proxy_user_len);
+ }
+ len += proxy_user_len;
+ socksreq[len++] = (unsigned char) proxy_password_len;
+ if(sx->proxy_password && proxy_password_len) {
+ /* the length must fit in a single byte */
+ if(proxy_password_len > 255) {
+ failf(data, "Excessive password length for proxy auth");
+ return CURLPX_LONG_PASSWD;
+ }
+ memcpy(socksreq + len, sx->proxy_password, proxy_password_len);
+ }
+ len += proxy_password_len;
+ sxstate(sx, data, CONNECT_AUTH_SEND);
+ sx->outstanding = len;
+ sx->outp = socksreq;
+ }
+ /* FALLTHROUGH */
+ case CONNECT_AUTH_SEND:
+ result = Curl_write_plain(data, sockfd, sx->outp,
+ sx->outstanding, &written);
+ if(result && (CURLE_AGAIN != result)) {
+ failf(data, "Failed to send SOCKS5 sub-negotiation request.");
+ return CURLPX_SEND_AUTH;
+ }
+ if(sx->outstanding != written) {
+ /* remain in state */
+ sx->outstanding -= written;
+ sx->outp += written;
+ return CURLPX_OK;
+ }
+ sx->outp = socksreq;
+ sx->outstanding = 2;
+ sxstate(sx, data, CONNECT_AUTH_READ);
+ /* FALLTHROUGH */
+ case CONNECT_AUTH_READ:
+ result = Curl_read_plain(data, sockfd, (char *)sx->outp,
+ sx->outstanding, &actualread);
+ if(result && (CURLE_AGAIN != result)) {
+ failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
+ return CURLPX_RECV_AUTH;
+ }
+ else if(!result && !actualread) {
+ /* connection closed */
+ failf(data, "connection to proxy closed");
+ return CURLPX_CLOSED;
+ }
+ else if(actualread != sx->outstanding) {
+ /* remain in state */
+ sx->outstanding -= actualread;
+ sx->outp += actualread;
+ return CURLPX_OK;
+ }
+ /* ignore the first (VER) byte */
+ else if(socksreq[1]) { /* status */
+ failf(data, "User was rejected by the SOCKS5 server (%d %d).",
+ socksreq[0], socksreq[1]);
+ return CURLPX_USER_REJECTED;
+ }
+
+ /* Everything is good so far, user was authenticated! */
+ sxstate(sx, data, CONNECT_REQ_INIT);
+ /* FALLTHROUGH */
+ CONNECT_REQ_INIT:
+ case CONNECT_REQ_INIT:
+ if(socks5_resolve_local) {
+ enum resolve_t rc = Curl_resolv(data, sx->hostname, sx->remote_port,
+ FALSE, &dns);
+
+ if(rc == CURLRESOLV_ERROR)
+ return CURLPX_RESOLVE_HOST;
+
+ if(rc == CURLRESOLV_PENDING) {
+ sxstate(sx, data, CONNECT_RESOLVING);
+ return CURLPX_OK;
+ }
+ sxstate(sx, data, CONNECT_RESOLVED);
+ goto CONNECT_RESOLVED;
+ }
+ goto CONNECT_RESOLVE_REMOTE;
+
+ case CONNECT_RESOLVING:
+ /* check if we have the name resolved by now */
+ dns = Curl_fetch_addr(data, sx->hostname, sx->remote_port);
+
+ if(dns) {
+#ifdef CURLRES_ASYNCH
+ data->state.async.dns = dns;
+ data->state.async.done = TRUE;
+#endif
+ infof(data, "SOCKS5: hostname '%s' found", sx->hostname);
+ }
+
+ if(!dns) {
+ result = Curl_resolv_check(data, &dns);
+ if(!dns) {
+ if(result)
+ return CURLPX_RESOLVE_HOST;
+ return CURLPX_OK;
+ }
+ }
+ /* FALLTHROUGH */
+ CONNECT_RESOLVED:
+ case CONNECT_RESOLVED: {
+ struct Curl_addrinfo *hp = NULL;
+ size_t destlen;
+ if(dns)
+ hp = dns->addr;
+ if(!hp) {
+ failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
+ sx->hostname);
+ return CURLPX_RESOLVE_HOST;
+ }
+
+ Curl_printable_address(hp, dest, sizeof(dest));
+ destlen = strlen(dest);
+ msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", sx->remote_port);
+
+ len = 0;
+ socksreq[len++] = 5; /* version (SOCKS5) */
+ socksreq[len++] = 1; /* connect */
+ socksreq[len++] = 0; /* must be zero */
+ if(hp->ai_family == AF_INET) {
+ int i;
+ struct sockaddr_in *saddr_in;
+ socksreq[len++] = 1; /* ATYP: IPv4 = 1 */
+
+ saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
+ for(i = 0; i < 4; i++) {
+ socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i];
+ }
+
+ infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)", dest);
+ }
+#ifdef ENABLE_IPV6
+ else if(hp->ai_family == AF_INET6) {
+ int i;
+ struct sockaddr_in6 *saddr_in6;
+ socksreq[len++] = 4; /* ATYP: IPv6 = 4 */
+
+ saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr;
+ for(i = 0; i < 16; i++) {
+ socksreq[len++] =
+ ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i];
+ }
+
+ infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)", dest);
+ }
+#endif
+ else {
+ hp = NULL; /* fail! */
+ failf(data, "SOCKS5 connection to %s not supported", dest);
+ }
+
+ Curl_resolv_unlock(data, dns); /* not used anymore from now on */
+ goto CONNECT_REQ_SEND;
+ }
+ CONNECT_RESOLVE_REMOTE:
+ case CONNECT_RESOLVE_REMOTE:
+ /* Authentication is complete, now specify destination to the proxy */
+ len = 0;
+ socksreq[len++] = 5; /* version (SOCKS5) */
+ socksreq[len++] = 1; /* connect */
+ socksreq[len++] = 0; /* must be zero */
+
+ if(!socks5_resolve_local) {
+ /* ATYP: domain name = 3,
+ IPv6 == 4,
+ IPv4 == 1 */
+ unsigned char ip4[4];
+#ifdef ENABLE_IPV6
+ if(conn->bits.ipv6_ip) {
+ char ip6[16];
+ if(1 != Curl_inet_pton(AF_INET6, sx->hostname, ip6))
+ return CURLPX_BAD_ADDRESS_TYPE;
+ socksreq[len++] = 4;
+ memcpy(&socksreq[len], ip6, sizeof(ip6));
+ len += sizeof(ip6);
+ }
+ else
+#endif
+ if(1 == Curl_inet_pton(AF_INET, sx->hostname, ip4)) {
+ socksreq[len++] = 1;
+ memcpy(&socksreq[len], ip4, sizeof(ip4));
+ len += sizeof(ip4);
+ }
+ else {
+ socksreq[len++] = 3;
+ socksreq[len++] = (char) hostname_len; /* one byte address length */
+ memcpy(&socksreq[len], sx->hostname, hostname_len); /* w/o NULL */
+ len += hostname_len;
+ }
+ infof(data, "SOCKS5 connect to %s:%d (remotely resolved)",
+ sx->hostname, sx->remote_port);
+ }
+ /* FALLTHROUGH */
+
+ CONNECT_REQ_SEND:
+ case CONNECT_REQ_SEND:
+ /* PORT MSB */
+ socksreq[len++] = (unsigned char)((sx->remote_port >> 8) & 0xff);
+ /* PORT LSB */
+ socksreq[len++] = (unsigned char)(sx->remote_port & 0xff);
+
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+ if(conn->socks5_gssapi_enctype) {
+ failf(data, "SOCKS5 GSS-API protection not yet implemented.");
+ return CURLPX_GSSAPI_PROTECTION;
+ }
+#endif
+ sx->outp = socksreq;
+ sx->outstanding = len;
+ sxstate(sx, data, CONNECT_REQ_SENDING);
+ /* FALLTHROUGH */
+ case CONNECT_REQ_SENDING:
+ result = Curl_write_plain(data, sockfd, (char *)sx->outp,
+ sx->outstanding, &written);
+ if(result && (CURLE_AGAIN != result)) {
+ failf(data, "Failed to send SOCKS5 connect request.");
+ return CURLPX_SEND_REQUEST;
+ }
+ if(sx->outstanding != written) {
+ /* remain in state */
+ sx->outstanding -= written;
+ sx->outp += written;
+ return CURLPX_OK;
+ }
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+ if(conn->socks5_gssapi_enctype) {
+ failf(data, "SOCKS5 GSS-API protection not yet implemented.");
+ return CURLPX_GSSAPI_PROTECTION;
+ }
+#endif
+ sx->outstanding = 10; /* minimum packet size is 10 */
+ sx->outp = socksreq;
+ sxstate(sx, data, CONNECT_REQ_READ);
+ /* FALLTHROUGH */
+ case CONNECT_REQ_READ:
+ result = Curl_read_plain(data, sockfd, (char *)sx->outp,
+ sx->outstanding, &actualread);
+ if(result && (CURLE_AGAIN != result)) {
+ failf(data, "Failed to receive SOCKS5 connect request ack.");
+ return CURLPX_RECV_REQACK;
+ }
+ else if(!result && !actualread) {
+ /* connection closed */
+ failf(data, "connection to proxy closed");
+ return CURLPX_CLOSED;
+ }
+ else if(actualread != sx->outstanding) {
+ /* remain in state */
+ sx->outstanding -= actualread;
+ sx->outp += actualread;
+ return CURLPX_OK;
+ }
+
+ if(socksreq[0] != 5) { /* version */
+ failf(data,
+ "SOCKS5 reply has wrong version, version should be 5.");
+ return CURLPX_BAD_VERSION;
+ }
+ else if(socksreq[1]) { /* Anything besides 0 is an error */
+ CURLproxycode rc = CURLPX_REPLY_UNASSIGNED;
+ int code = socksreq[1];
+ failf(data, "Can't complete SOCKS5 connection to %s. (%d)",
+ sx->hostname, (unsigned char)socksreq[1]);
+ if(code < 9) {
+ /* RFC 1928 section 6 lists: */
+ static const CURLproxycode lookup[] = {
+ CURLPX_OK,
+ CURLPX_REPLY_GENERAL_SERVER_FAILURE,
+ CURLPX_REPLY_NOT_ALLOWED,
+ CURLPX_REPLY_NETWORK_UNREACHABLE,
+ CURLPX_REPLY_HOST_UNREACHABLE,
+ CURLPX_REPLY_CONNECTION_REFUSED,
+ CURLPX_REPLY_TTL_EXPIRED,
+ CURLPX_REPLY_COMMAND_NOT_SUPPORTED,
+ CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED,
+ };
+ rc = lookup[code];
+ }
+ return rc;
+ }
+
+ /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
+ 1928, so the reply packet should be read until the end to avoid errors
+ at subsequent protocol level.
+
+ +----+-----+-------+------+----------+----------+
+ |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
+ +----+-----+-------+------+----------+----------+
+ | 1 | 1 | X'00' | 1 | Variable | 2 |
+ +----+-----+-------+------+----------+----------+
+
+ ATYP:
+ o IP v4 address: X'01', BND.ADDR = 4 byte
+ o domain name: X'03', BND.ADDR = [ 1 byte length, string ]
+ o IP v6 address: X'04', BND.ADDR = 16 byte
+ */
+
+ /* Calculate real packet size */
+ if(socksreq[3] == 3) {
+ /* domain name */
+ int addrlen = (int) socksreq[4];
+ len = 5 + addrlen + 2;
+ }
+ else if(socksreq[3] == 4) {
+ /* IPv6 */
+ len = 4 + 16 + 2;
+ }
+ else if(socksreq[3] == 1) {
+ len = 4 + 4 + 2;
+ }
+ else {
+ failf(data, "SOCKS5 reply has wrong address type.");
+ return CURLPX_BAD_ADDRESS_TYPE;
+ }
+
+ /* At this point we already read first 10 bytes */
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+ if(!conn->socks5_gssapi_enctype) {
+ /* decrypt_gssapi_blockread already read the whole packet */
+#endif
+ if(len > 10) {
+ sx->outstanding = len - 10; /* get the rest */
+ sx->outp = &socksreq[10];
+ sxstate(sx, data, CONNECT_REQ_READ_MORE);
+ }
+ else {
+ sxstate(sx, data, CONNECT_DONE);
+ break;
+ }
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+ }
+#endif
+ /* FALLTHROUGH */
+ case CONNECT_REQ_READ_MORE:
+ result = Curl_read_plain(data, sockfd, (char *)sx->outp,
+ sx->outstanding, &actualread);
+ if(result && (CURLE_AGAIN != result)) {
+ failf(data, "Failed to receive SOCKS5 connect request ack.");
+ return CURLPX_RECV_ADDRESS;
+ }
+ else if(!result && !actualread) {
+ /* connection closed */
+ failf(data, "connection to proxy closed");
+ return CURLPX_CLOSED;
+ }
+ else if(actualread != sx->outstanding) {
+ /* remain in state */
+ sx->outstanding -= actualread;
+ sx->outp += actualread;
+ return CURLPX_OK;
+ }
+ sxstate(sx, data, CONNECT_DONE);
+ }
+ infof(data, "SOCKS5 request granted.");
+
+ return CURLPX_OK; /* Proxy was successful! */
+}
+
+static CURLcode connect_SOCKS(struct Curl_cfilter *cf,
+ struct socks_state *sxstate,
+ struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ CURLproxycode pxresult = CURLPX_OK;
+ struct connectdata *conn = cf->conn;
+
+ switch(conn->socks_proxy.proxytype) {
+ case CURLPROXY_SOCKS5:
+ case CURLPROXY_SOCKS5_HOSTNAME:
+ pxresult = do_SOCKS5(cf, sxstate, data);
+ break;
+
+ case CURLPROXY_SOCKS4:
+ case CURLPROXY_SOCKS4A:
+ pxresult = do_SOCKS4(cf, sxstate, data);
+ break;
+
+ default:
+ failf(data, "unknown proxytype option given");
+ result = CURLE_COULDNT_CONNECT;
+ } /* switch proxytype */
+ if(pxresult) {
+ result = CURLE_PROXY;
+ data->info.pxcode = pxresult;
+ }
+
+ return result;
+}
+
+static void socks_proxy_cf_free(struct Curl_cfilter *cf)
+{
+ struct socks_state *sxstate = cf->ctx;
+ if(sxstate) {
+ free(sxstate);
+ cf->ctx = NULL;
+ }
+}
+
+/* After a TCP connection to the proxy has been verified, this function does
+ the next magic steps. If 'done' isn't set TRUE, it is not done yet and
+ must be called again.
+
+ Note: this function's sub-functions call failf()
+
+*/
+static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ CURLcode result;
+ struct connectdata *conn = cf->conn;
+ int sockindex = cf->sockindex;
+ struct socks_state *sx = cf->ctx;
+
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ result = cf->next->cft->connect(cf->next, data, blocking, done);
+ if(result || !*done)
+ return result;
+
+ if(!sx) {
+ sx = calloc(sizeof(*sx), 1);
+ if(!sx)
+ return CURLE_OUT_OF_MEMORY;
+ cf->ctx = sx;
+ }
+
+ if(sx->state == CONNECT_INIT) {
+ /* for the secondary socket (FTP), use the "connect to host"
+ * but ignore the "connect to port" (use the secondary port)
+ */
+ sxstate(sx, data, CONNECT_SOCKS_INIT);
+ sx->hostname =
+ conn->bits.httpproxy ?
+ conn->http_proxy.host.name :
+ conn->bits.conn_to_host ?
+ conn->conn_to_host.name :
+ sockindex == SECONDARYSOCKET ?
+ conn->secondaryhostname : conn->host.name;
+ sx->remote_port =
+ conn->bits.httpproxy ? (int)conn->http_proxy.port :
+ sockindex == SECONDARYSOCKET ? conn->secondary_port :
+ conn->bits.conn_to_port ? conn->conn_to_port :
+ conn->remote_port;
+ sx->proxy_user = conn->socks_proxy.user;
+ sx->proxy_password = conn->socks_proxy.passwd;
+ }
+
+ result = connect_SOCKS(cf, sx, data);
+ if(!result && sx->state == CONNECT_DONE) {
+ cf->connected = TRUE;
+ Curl_updateconninfo(data, conn, conn->sock[cf->sockindex]);
+ Curl_verboseconnect(data, conn);
+ socks_proxy_cf_free(cf);
+ }
+
+ *done = cf->connected;
+ return result;
+}
+
+static int socks_cf_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ struct socks_state *sx = cf->ctx;
+ int fds;
+
+ fds = cf->next->cft->get_select_socks(cf->next, data, socks);
+ if(!fds && cf->next->connected && !cf->connected && sx) {
+ /* If we are not connected, the filter below is and has nothing
+ * to wait on, we determine what to wait for. */
+ socks[0] = cf->conn->sock[cf->sockindex];
+ switch(sx->state) {
+ case CONNECT_RESOLVING:
+ case CONNECT_SOCKS_READ:
+ case CONNECT_AUTH_READ:
+ case CONNECT_REQ_READ:
+ case CONNECT_REQ_READ_MORE:
+ fds = GETSOCK_READSOCK(0);
+ break;
+ default:
+ fds = GETSOCK_WRITESOCK(0);
+ break;
+ }
+ }
+ return fds;
+}
+
+static void socks_proxy_cf_close(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+
+ DEBUGASSERT(cf->next);
+ cf->connected = FALSE;
+ socks_proxy_cf_free(cf);
+ cf->next->cft->close(cf->next, data);
+}
+
+static void socks_proxy_cf_destroy(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ (void)data;
+ socks_proxy_cf_free(cf);
+}
+
+static void socks_proxy_cf_detach_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ (void)data;
+ socks_proxy_cf_free(cf);
+}
+
+static void socks_cf_get_host(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char **phost,
+ const char **pdisplay_host,
+ int *pport)
+{
+ (void)data;
+ if(!cf->connected) {
+ *phost = cf->conn->socks_proxy.host.name;
+ *pdisplay_host = cf->conn->http_proxy.host.dispname;
+ *pport = (int)cf->conn->socks_proxy.port;
+ }
+ else {
+ cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
+ }
+}
+
+static const struct Curl_cftype cft_socks_proxy = {
+ "SOCKS-PROXYY",
+ CF_TYPE_IP_CONNECT,
+ socks_proxy_cf_destroy,
+ Curl_cf_def_setup,
+ socks_proxy_cf_connect,
+ socks_proxy_cf_close,
+ socks_cf_get_host,
+ socks_cf_get_select_socks,
+ Curl_cf_def_data_pending,
+ Curl_cf_def_send,
+ Curl_cf_def_recv,
+ Curl_cf_def_attach_data,
+ socks_proxy_cf_detach_data,
+};
+
+CURLcode Curl_conn_socks_proxy_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
+
+ result = Curl_cf_create(&cf, &cft_socks_proxy, NULL);
+ if(!result)
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+ return result;
+}
+
+#endif /* CURL_DISABLE_PROXY */
diff --git a/libs/libcurl/src/socks.h b/libs/libcurl/src/socks.h index ff83aa5614..7b4e074718 100644 --- a/libs/libcurl/src/socks.h +++ b/libs/libcurl/src/socks.h @@ -1,82 +1,60 @@ -#ifndef HEADER_CURL_SOCKS_H -#define HEADER_CURL_SOCKS_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 CURL_DISABLE_PROXY -#define Curl_SOCKS4(a,b,c,d,e) CURLE_NOT_BUILT_IN -#define Curl_SOCKS5(a,b,c,d,e,f) CURLE_NOT_BUILT_IN -#define Curl_SOCKS_getsock(x,y,z) 0 -#else -/* - * Helper read-from-socket functions. Does the same as Curl_read() but it - * blocks until all bytes amount of buffersize will be read. No more, no less. - * - * This is STUPID BLOCKING behavior - */ -int Curl_blockread_all(struct Curl_easy *data, - curl_socket_t sockfd, - char *buf, - ssize_t buffersize, - ssize_t *n); - -int Curl_SOCKS_getsock(struct connectdata *conn, - curl_socket_t *sock, - int sockindex); -/* - * This function logs in to a SOCKS4(a) proxy and sends the specifics to the - * final destination server. - */ -CURLproxycode Curl_SOCKS4(const char *proxy_name, - const char *hostname, - int remote_port, - int sockindex, - struct Curl_easy *data, - bool *done); - -/* - * This function logs in to a SOCKS5 proxy and sends the specifics to the - * final destination server. - */ -CURLproxycode Curl_SOCKS5(const char *proxy_name, - const char *proxy_password, - const char *hostname, - int remote_port, - int sockindex, - struct Curl_easy *data, - bool *done); - -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) -/* - * This function handles the SOCKS5 GSS-API negotiation and initialization - */ -CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, - struct Curl_easy *data); -#endif - -#endif /* CURL_DISABLE_PROXY */ - -#endif /* HEADER_CURL_SOCKS_H */ +#ifndef HEADER_CURL_SOCKS_H
+#define HEADER_CURL_SOCKS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 CURL_DISABLE_PROXY
+#define Curl_SOCKS4(a,b,c,d,e) CURLE_NOT_BUILT_IN
+#define Curl_SOCKS5(a,b,c,d,e,f) CURLE_NOT_BUILT_IN
+#define Curl_SOCKS_getsock(x,y,z) 0
+#else
+/*
+ * Helper read-from-socket functions. Does the same as Curl_read() but it
+ * blocks until all bytes amount of buffersize will be read. No more, no less.
+ *
+ * This is STUPID BLOCKING behavior
+ */
+int Curl_blockread_all(struct Curl_easy *data,
+ curl_socket_t sockfd,
+ char *buf,
+ ssize_t buffersize,
+ ssize_t *n);
+
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+/*
+ * This function handles the SOCKS5 GSS-API negotiation and initialization
+ */
+CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
+ struct Curl_easy *data);
+#endif
+
+CURLcode Curl_conn_socks_proxy_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex);
+
+#endif /* CURL_DISABLE_PROXY */
+
+#endif /* HEADER_CURL_SOCKS_H */
diff --git a/libs/libcurl/src/socks_gssapi.c b/libs/libcurl/src/socks_gssapi.c index f14099febf..6f611dd38e 100644 --- a/libs/libcurl/src/socks_gssapi.c +++ b/libs/libcurl/src/socks_gssapi.c @@ -1,534 +1,534 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2012, Markus Moeller, <markus_moeller@compuserve.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" - -#if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_PROXY) - -#include "curl_gssapi.h" -#include "urldata.h" -#include "sendf.h" -#include "connect.h" -#include "timeval.h" -#include "socks.h" -#include "warnless.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -static gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; - -/* - * Helper GSS-API error functions. - */ -static int check_gss_err(struct Curl_easy *data, - OM_uint32 major_status, - OM_uint32 minor_status, - const char *function) -{ - if(GSS_ERROR(major_status)) { - OM_uint32 maj_stat, min_stat; - OM_uint32 msg_ctx = 0; - gss_buffer_desc status_string = GSS_C_EMPTY_BUFFER; - char buf[1024]; - size_t len; - - len = 0; - msg_ctx = 0; - while(!msg_ctx) { - /* convert major status code (GSS-API error) to text */ - maj_stat = gss_display_status(&min_stat, major_status, - GSS_C_GSS_CODE, - GSS_C_NULL_OID, - &msg_ctx, &status_string); - if(maj_stat == GSS_S_COMPLETE) { - if(sizeof(buf) > len + status_string.length + 1) { - strcpy(buf + len, (char *) status_string.value); - len += status_string.length; - } - gss_release_buffer(&min_stat, &status_string); - break; - } - gss_release_buffer(&min_stat, &status_string); - } - if(sizeof(buf) > len + 3) { - strcpy(buf + len, ".\n"); - len += 2; - } - msg_ctx = 0; - while(!msg_ctx) { - /* convert minor status code (underlying routine error) to text */ - maj_stat = gss_display_status(&min_stat, minor_status, - GSS_C_MECH_CODE, - GSS_C_NULL_OID, - &msg_ctx, &status_string); - if(maj_stat == GSS_S_COMPLETE) { - if(sizeof(buf) > len + status_string.length) - strcpy(buf + len, (char *) status_string.value); - gss_release_buffer(&min_stat, &status_string); - break; - } - gss_release_buffer(&min_stat, &status_string); - } - failf(data, "GSS-API error: %s failed: %s", function, buf); - return 1; - } - - return 0; -} - -CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, - struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - curl_socket_t sock = conn->sock[sockindex]; - CURLcode code; - ssize_t actualread; - ssize_t written; - int result; - OM_uint32 gss_major_status, gss_minor_status, gss_status; - OM_uint32 gss_ret_flags; - int gss_conf_state, gss_enc; - gss_buffer_desc service = GSS_C_EMPTY_BUFFER; - gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc gss_recv_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc gss_w_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc *gss_token = GSS_C_NO_BUFFER; - gss_name_t server = GSS_C_NO_NAME; - gss_name_t gss_client_name = GSS_C_NO_NAME; - unsigned short us_length; - char *user = NULL; - unsigned char socksreq[4]; /* room for GSS-API exchange header only */ - const char *serviceptr = data->set.str[STRING_PROXY_SERVICE_NAME] ? - data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd"; - const size_t serviceptr_length = strlen(serviceptr); - - /* GSS-API request looks like - * +----+------+-----+----------------+ - * |VER | MTYP | LEN | TOKEN | - * +----+------+----------------------+ - * | 1 | 1 | 2 | up to 2^16 - 1 | - * +----+------+-----+----------------+ - */ - - /* prepare service name */ - if(strchr(serviceptr, '/')) { - service.length = serviceptr_length; - service.value = malloc(service.length); - if(!service.value) - return CURLE_OUT_OF_MEMORY; - memcpy(service.value, serviceptr, service.length); - - gss_major_status = gss_import_name(&gss_minor_status, &service, - (gss_OID) GSS_C_NULL_OID, &server); - } - else { - service.value = malloc(serviceptr_length + - strlen(conn->socks_proxy.host.name) + 2); - if(!service.value) - return CURLE_OUT_OF_MEMORY; - service.length = serviceptr_length + - strlen(conn->socks_proxy.host.name) + 1; - msnprintf(service.value, service.length + 1, "%s@%s", - serviceptr, conn->socks_proxy.host.name); - - gss_major_status = gss_import_name(&gss_minor_status, &service, - GSS_C_NT_HOSTBASED_SERVICE, &server); - } - - gss_release_buffer(&gss_status, &service); /* clear allocated memory */ - - if(check_gss_err(data, gss_major_status, - gss_minor_status, "gss_import_name()")) { - failf(data, "Failed to create service name."); - gss_release_name(&gss_status, &server); - return CURLE_COULDNT_CONNECT; - } - - (void)curlx_nonblock(sock, FALSE); - - /* As long as we need to keep sending some context info, and there's no */ - /* errors, keep sending it... */ - for(;;) { - gss_major_status = Curl_gss_init_sec_context(data, - &gss_minor_status, - &gss_context, - server, - &Curl_krb5_mech_oid, - NULL, - gss_token, - &gss_send_token, - TRUE, - &gss_ret_flags); - - if(gss_token != GSS_C_NO_BUFFER) - gss_release_buffer(&gss_status, &gss_recv_token); - if(check_gss_err(data, gss_major_status, - gss_minor_status, "gss_init_sec_context")) { - gss_release_name(&gss_status, &server); - gss_release_buffer(&gss_status, &gss_recv_token); - gss_release_buffer(&gss_status, &gss_send_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); - failf(data, "Failed to initial GSS-API token."); - return CURLE_COULDNT_CONNECT; - } - - if(gss_send_token.length) { - socksreq[0] = 1; /* GSS-API subnegotiation version */ - socksreq[1] = 1; /* authentication message type */ - us_length = htons((short)gss_send_token.length); - memcpy(socksreq + 2, &us_length, sizeof(short)); - - code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written); - if(code || (4 != written)) { - failf(data, "Failed to send GSS-API authentication request."); - gss_release_name(&gss_status, &server); - gss_release_buffer(&gss_status, &gss_recv_token); - gss_release_buffer(&gss_status, &gss_send_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); - return CURLE_COULDNT_CONNECT; - } - - code = Curl_write_plain(data, sock, (char *)gss_send_token.value, - gss_send_token.length, &written); - - if(code || ((ssize_t)gss_send_token.length != written)) { - failf(data, "Failed to send GSS-API authentication token."); - gss_release_name(&gss_status, &server); - gss_release_buffer(&gss_status, &gss_recv_token); - gss_release_buffer(&gss_status, &gss_send_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); - return CURLE_COULDNT_CONNECT; - } - - } - - gss_release_buffer(&gss_status, &gss_send_token); - gss_release_buffer(&gss_status, &gss_recv_token); - if(gss_major_status != GSS_S_CONTINUE_NEEDED) - break; - - /* analyse response */ - - /* GSS-API response looks like - * +----+------+-----+----------------+ - * |VER | MTYP | LEN | TOKEN | - * +----+------+----------------------+ - * | 1 | 1 | 2 | up to 2^16 - 1 | - * +----+------+-----+----------------+ - */ - - result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread); - if(result || (actualread != 4)) { - failf(data, "Failed to receive GSS-API authentication response."); - gss_release_name(&gss_status, &server); - gss_delete_sec_context(&gss_status, &gss_context, NULL); - return CURLE_COULDNT_CONNECT; - } - - /* ignore the first (VER) byte */ - if(socksreq[1] == 255) { /* status / message type */ - failf(data, "User was rejected by the SOCKS5 server (%d %d).", - socksreq[0], socksreq[1]); - gss_release_name(&gss_status, &server); - gss_delete_sec_context(&gss_status, &gss_context, NULL); - return CURLE_COULDNT_CONNECT; - } - - if(socksreq[1] != 1) { /* status / message type */ - failf(data, "Invalid GSS-API authentication response type (%d %d).", - socksreq[0], socksreq[1]); - gss_release_name(&gss_status, &server); - gss_delete_sec_context(&gss_status, &gss_context, NULL); - return CURLE_COULDNT_CONNECT; - } - - memcpy(&us_length, socksreq + 2, sizeof(short)); - us_length = ntohs(us_length); - - gss_recv_token.length = us_length; - gss_recv_token.value = malloc(us_length); - if(!gss_recv_token.value) { - failf(data, - "Could not allocate memory for GSS-API authentication " - "response token."); - gss_release_name(&gss_status, &server); - gss_delete_sec_context(&gss_status, &gss_context, NULL); - return CURLE_OUT_OF_MEMORY; - } - - result = Curl_blockread_all(data, sock, (char *)gss_recv_token.value, - gss_recv_token.length, &actualread); - - if(result || (actualread != us_length)) { - failf(data, "Failed to receive GSS-API authentication token."); - gss_release_name(&gss_status, &server); - gss_release_buffer(&gss_status, &gss_recv_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); - return CURLE_COULDNT_CONNECT; - } - - gss_token = &gss_recv_token; - } - - gss_release_name(&gss_status, &server); - - /* Everything is good so far, user was authenticated! */ - gss_major_status = gss_inquire_context(&gss_minor_status, gss_context, - &gss_client_name, NULL, NULL, NULL, - NULL, NULL, NULL); - if(check_gss_err(data, gss_major_status, - gss_minor_status, "gss_inquire_context")) { - gss_delete_sec_context(&gss_status, &gss_context, NULL); - gss_release_name(&gss_status, &gss_client_name); - failf(data, "Failed to determine user name."); - return CURLE_COULDNT_CONNECT; - } - gss_major_status = gss_display_name(&gss_minor_status, gss_client_name, - &gss_send_token, NULL); - if(check_gss_err(data, gss_major_status, - gss_minor_status, "gss_display_name")) { - gss_delete_sec_context(&gss_status, &gss_context, NULL); - gss_release_name(&gss_status, &gss_client_name); - gss_release_buffer(&gss_status, &gss_send_token); - failf(data, "Failed to determine user name."); - return CURLE_COULDNT_CONNECT; - } - user = malloc(gss_send_token.length + 1); - if(!user) { - gss_delete_sec_context(&gss_status, &gss_context, NULL); - gss_release_name(&gss_status, &gss_client_name); - gss_release_buffer(&gss_status, &gss_send_token); - return CURLE_OUT_OF_MEMORY; - } - - memcpy(user, gss_send_token.value, gss_send_token.length); - user[gss_send_token.length] = '\0'; - gss_release_name(&gss_status, &gss_client_name); - gss_release_buffer(&gss_status, &gss_send_token); - infof(data, "SOCKS5 server authenticated user %s with GSS-API.",user); - free(user); - user = NULL; - - /* Do encryption */ - socksreq[0] = 1; /* GSS-API subnegotiation version */ - socksreq[1] = 2; /* encryption message type */ - - gss_enc = 0; /* no data protection */ - /* do confidentiality protection if supported */ - if(gss_ret_flags & GSS_C_CONF_FLAG) - gss_enc = 2; - /* else do integrity protection */ - else if(gss_ret_flags & GSS_C_INTEG_FLAG) - gss_enc = 1; - - infof(data, "SOCKS5 server supports GSS-API %s data protection.", - (gss_enc == 0)?"no":((gss_enc==1)?"integrity":"confidentiality")); - /* force for the moment to no data protection */ - gss_enc = 0; - /* - * Sending the encryption type in clear seems wrong. It should be - * protected with gss_seal()/gss_wrap(). See RFC1961 extract below - * The NEC reference implementations on which this is based is - * therefore at fault - * - * +------+------+------+.......................+ - * + ver | mtyp | len | token | - * +------+------+------+.......................+ - * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets | - * +------+------+------+.......................+ - * - * Where: - * - * - "ver" is the protocol version number, here 1 to represent the - * first version of the SOCKS/GSS-API protocol - * - * - "mtyp" is the message type, here 2 to represent a protection - * -level negotiation message - * - * - "len" is the length of the "token" field in octets - * - * - "token" is the GSS-API encapsulated protection level - * - * The token is produced by encapsulating an octet containing the - * required protection level using gss_seal()/gss_wrap() with conf_req - * set to FALSE. The token is verified using gss_unseal()/ - * gss_unwrap(). - * - */ - if(data->set.socks5_gssapi_nec) { - us_length = htons((short)1); - memcpy(socksreq + 2, &us_length, sizeof(short)); - } - else { - gss_send_token.length = 1; - gss_send_token.value = malloc(1); - if(!gss_send_token.value) { - gss_delete_sec_context(&gss_status, &gss_context, NULL); - return CURLE_OUT_OF_MEMORY; - } - memcpy(gss_send_token.value, &gss_enc, 1); - - gss_major_status = gss_wrap(&gss_minor_status, gss_context, 0, - GSS_C_QOP_DEFAULT, &gss_send_token, - &gss_conf_state, &gss_w_token); - - if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_wrap")) { - gss_release_buffer(&gss_status, &gss_send_token); - gss_release_buffer(&gss_status, &gss_w_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); - failf(data, "Failed to wrap GSS-API encryption value into token."); - return CURLE_COULDNT_CONNECT; - } - gss_release_buffer(&gss_status, &gss_send_token); - - us_length = htons((short)gss_w_token.length); - memcpy(socksreq + 2, &us_length, sizeof(short)); - } - - code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written); - if(code || (4 != written)) { - failf(data, "Failed to send GSS-API encryption request."); - gss_release_buffer(&gss_status, &gss_w_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); - return CURLE_COULDNT_CONNECT; - } - - if(data->set.socks5_gssapi_nec) { - memcpy(socksreq, &gss_enc, 1); - code = Curl_write_plain(data, sock, socksreq, 1, &written); - if(code || ( 1 != written)) { - failf(data, "Failed to send GSS-API encryption type."); - gss_delete_sec_context(&gss_status, &gss_context, NULL); - return CURLE_COULDNT_CONNECT; - } - } - else { - code = Curl_write_plain(data, sock, (char *)gss_w_token.value, - gss_w_token.length, &written); - if(code || ((ssize_t)gss_w_token.length != written)) { - failf(data, "Failed to send GSS-API encryption type."); - gss_release_buffer(&gss_status, &gss_w_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); - return CURLE_COULDNT_CONNECT; - } - gss_release_buffer(&gss_status, &gss_w_token); - } - - result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread); - if(result || (actualread != 4)) { - failf(data, "Failed to receive GSS-API encryption response."); - gss_delete_sec_context(&gss_status, &gss_context, NULL); - return CURLE_COULDNT_CONNECT; - } - - /* ignore the first (VER) byte */ - if(socksreq[1] == 255) { /* status / message type */ - failf(data, "User was rejected by the SOCKS5 server (%d %d).", - socksreq[0], socksreq[1]); - gss_delete_sec_context(&gss_status, &gss_context, NULL); - return CURLE_COULDNT_CONNECT; - } - - if(socksreq[1] != 2) { /* status / message type */ - failf(data, "Invalid GSS-API encryption response type (%d %d).", - socksreq[0], socksreq[1]); - gss_delete_sec_context(&gss_status, &gss_context, NULL); - return CURLE_COULDNT_CONNECT; - } - - memcpy(&us_length, socksreq + 2, sizeof(short)); - us_length = ntohs(us_length); - - gss_recv_token.length = us_length; - gss_recv_token.value = malloc(gss_recv_token.length); - if(!gss_recv_token.value) { - gss_delete_sec_context(&gss_status, &gss_context, NULL); - return CURLE_OUT_OF_MEMORY; - } - result = Curl_blockread_all(data, sock, (char *)gss_recv_token.value, - gss_recv_token.length, &actualread); - - if(result || (actualread != us_length)) { - failf(data, "Failed to receive GSS-API encryptrion type."); - gss_release_buffer(&gss_status, &gss_recv_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); - return CURLE_COULDNT_CONNECT; - } - - if(!data->set.socks5_gssapi_nec) { - gss_major_status = gss_unwrap(&gss_minor_status, gss_context, - &gss_recv_token, &gss_w_token, - 0, GSS_C_QOP_DEFAULT); - - if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_unwrap")) { - gss_release_buffer(&gss_status, &gss_recv_token); - gss_release_buffer(&gss_status, &gss_w_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); - failf(data, "Failed to unwrap GSS-API encryption value into token."); - return CURLE_COULDNT_CONNECT; - } - gss_release_buffer(&gss_status, &gss_recv_token); - - if(gss_w_token.length != 1) { - failf(data, "Invalid GSS-API encryption response length (%zu).", - gss_w_token.length); - gss_release_buffer(&gss_status, &gss_w_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); - return CURLE_COULDNT_CONNECT; - } - - memcpy(socksreq, gss_w_token.value, gss_w_token.length); - gss_release_buffer(&gss_status, &gss_w_token); - } - else { - if(gss_recv_token.length != 1) { - failf(data, "Invalid GSS-API encryption response length (%zu).", - gss_recv_token.length); - gss_release_buffer(&gss_status, &gss_recv_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); - return CURLE_COULDNT_CONNECT; - } - - memcpy(socksreq, gss_recv_token.value, gss_recv_token.length); - gss_release_buffer(&gss_status, &gss_recv_token); - } - - (void)curlx_nonblock(sock, TRUE); - - infof(data, "SOCKS5 access with%s protection granted.", - (socksreq[0] == 0)?"out GSS-API data": - ((socksreq[0] == 1)?" GSS-API integrity":" GSS-API confidentiality")); - - conn->socks5_gssapi_enctype = socksreq[0]; - if(socksreq[0] == 0) - gss_delete_sec_context(&gss_status, &gss_context, NULL); - - return CURLE_OK; -} - -#endif /* HAVE_GSSAPI && !CURL_DISABLE_PROXY */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012, Markus Moeller, <markus_moeller@compuserve.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"
+
+#if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_PROXY)
+
+#include "curl_gssapi.h"
+#include "urldata.h"
+#include "sendf.h"
+#include "connect.h"
+#include "timeval.h"
+#include "socks.h"
+#include "warnless.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+static gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
+
+/*
+ * Helper GSS-API error functions.
+ */
+static int check_gss_err(struct Curl_easy *data,
+ OM_uint32 major_status,
+ OM_uint32 minor_status,
+ const char *function)
+{
+ if(GSS_ERROR(major_status)) {
+ OM_uint32 maj_stat, min_stat;
+ OM_uint32 msg_ctx = 0;
+ gss_buffer_desc status_string = GSS_C_EMPTY_BUFFER;
+ char buf[1024];
+ size_t len;
+
+ len = 0;
+ msg_ctx = 0;
+ while(!msg_ctx) {
+ /* convert major status code (GSS-API error) to text */
+ maj_stat = gss_display_status(&min_stat, major_status,
+ GSS_C_GSS_CODE,
+ GSS_C_NULL_OID,
+ &msg_ctx, &status_string);
+ if(maj_stat == GSS_S_COMPLETE) {
+ if(sizeof(buf) > len + status_string.length + 1) {
+ strcpy(buf + len, (char *) status_string.value);
+ len += status_string.length;
+ }
+ gss_release_buffer(&min_stat, &status_string);
+ break;
+ }
+ gss_release_buffer(&min_stat, &status_string);
+ }
+ if(sizeof(buf) > len + 3) {
+ strcpy(buf + len, ".\n");
+ len += 2;
+ }
+ msg_ctx = 0;
+ while(!msg_ctx) {
+ /* convert minor status code (underlying routine error) to text */
+ maj_stat = gss_display_status(&min_stat, minor_status,
+ GSS_C_MECH_CODE,
+ GSS_C_NULL_OID,
+ &msg_ctx, &status_string);
+ if(maj_stat == GSS_S_COMPLETE) {
+ if(sizeof(buf) > len + status_string.length)
+ strcpy(buf + len, (char *) status_string.value);
+ gss_release_buffer(&min_stat, &status_string);
+ break;
+ }
+ gss_release_buffer(&min_stat, &status_string);
+ }
+ failf(data, "GSS-API error: %s failed: %s", function, buf);
+ return 1;
+ }
+
+ return 0;
+}
+
+CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
+ struct Curl_easy *data)
+{
+ struct connectdata *conn = data->conn;
+ curl_socket_t sock = conn->sock[sockindex];
+ CURLcode code;
+ ssize_t actualread;
+ ssize_t written;
+ int result;
+ OM_uint32 gss_major_status, gss_minor_status, gss_status;
+ OM_uint32 gss_ret_flags;
+ int gss_conf_state, gss_enc;
+ gss_buffer_desc service = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc gss_recv_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc gss_w_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc *gss_token = GSS_C_NO_BUFFER;
+ gss_name_t server = GSS_C_NO_NAME;
+ gss_name_t gss_client_name = GSS_C_NO_NAME;
+ unsigned short us_length;
+ char *user = NULL;
+ unsigned char socksreq[4]; /* room for GSS-API exchange header only */
+ const char *serviceptr = data->set.str[STRING_PROXY_SERVICE_NAME] ?
+ data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd";
+ const size_t serviceptr_length = strlen(serviceptr);
+
+ /* GSS-API request looks like
+ * +----+------+-----+----------------+
+ * |VER | MTYP | LEN | TOKEN |
+ * +----+------+----------------------+
+ * | 1 | 1 | 2 | up to 2^16 - 1 |
+ * +----+------+-----+----------------+
+ */
+
+ /* prepare service name */
+ if(strchr(serviceptr, '/')) {
+ service.length = serviceptr_length;
+ service.value = malloc(service.length);
+ if(!service.value)
+ return CURLE_OUT_OF_MEMORY;
+ memcpy(service.value, serviceptr, service.length);
+
+ gss_major_status = gss_import_name(&gss_minor_status, &service,
+ (gss_OID) GSS_C_NULL_OID, &server);
+ }
+ else {
+ service.value = malloc(serviceptr_length +
+ strlen(conn->socks_proxy.host.name) + 2);
+ if(!service.value)
+ return CURLE_OUT_OF_MEMORY;
+ service.length = serviceptr_length +
+ strlen(conn->socks_proxy.host.name) + 1;
+ msnprintf(service.value, service.length + 1, "%s@%s",
+ serviceptr, conn->socks_proxy.host.name);
+
+ gss_major_status = gss_import_name(&gss_minor_status, &service,
+ GSS_C_NT_HOSTBASED_SERVICE, &server);
+ }
+
+ gss_release_buffer(&gss_status, &service); /* clear allocated memory */
+
+ if(check_gss_err(data, gss_major_status,
+ gss_minor_status, "gss_import_name()")) {
+ failf(data, "Failed to create service name.");
+ gss_release_name(&gss_status, &server);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ (void)curlx_nonblock(sock, FALSE);
+
+ /* As long as we need to keep sending some context info, and there's no */
+ /* errors, keep sending it... */
+ for(;;) {
+ gss_major_status = Curl_gss_init_sec_context(data,
+ &gss_minor_status,
+ &gss_context,
+ server,
+ &Curl_krb5_mech_oid,
+ NULL,
+ gss_token,
+ &gss_send_token,
+ TRUE,
+ &gss_ret_flags);
+
+ if(gss_token != GSS_C_NO_BUFFER)
+ gss_release_buffer(&gss_status, &gss_recv_token);
+ if(check_gss_err(data, gss_major_status,
+ gss_minor_status, "gss_init_sec_context")) {
+ gss_release_name(&gss_status, &server);
+ gss_release_buffer(&gss_status, &gss_recv_token);
+ gss_release_buffer(&gss_status, &gss_send_token);
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ failf(data, "Failed to initial GSS-API token.");
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ if(gss_send_token.length) {
+ socksreq[0] = 1; /* GSS-API subnegotiation version */
+ socksreq[1] = 1; /* authentication message type */
+ us_length = htons((short)gss_send_token.length);
+ memcpy(socksreq + 2, &us_length, sizeof(short));
+
+ code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written);
+ if(code || (4 != written)) {
+ failf(data, "Failed to send GSS-API authentication request.");
+ gss_release_name(&gss_status, &server);
+ gss_release_buffer(&gss_status, &gss_recv_token);
+ gss_release_buffer(&gss_status, &gss_send_token);
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ code = Curl_write_plain(data, sock, (char *)gss_send_token.value,
+ gss_send_token.length, &written);
+
+ if(code || ((ssize_t)gss_send_token.length != written)) {
+ failf(data, "Failed to send GSS-API authentication token.");
+ gss_release_name(&gss_status, &server);
+ gss_release_buffer(&gss_status, &gss_recv_token);
+ gss_release_buffer(&gss_status, &gss_send_token);
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ }
+
+ gss_release_buffer(&gss_status, &gss_send_token);
+ gss_release_buffer(&gss_status, &gss_recv_token);
+ if(gss_major_status != GSS_S_CONTINUE_NEEDED)
+ break;
+
+ /* analyse response */
+
+ /* GSS-API response looks like
+ * +----+------+-----+----------------+
+ * |VER | MTYP | LEN | TOKEN |
+ * +----+------+----------------------+
+ * | 1 | 1 | 2 | up to 2^16 - 1 |
+ * +----+------+-----+----------------+
+ */
+
+ result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread);
+ if(result || (actualread != 4)) {
+ failf(data, "Failed to receive GSS-API authentication response.");
+ gss_release_name(&gss_status, &server);
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ /* ignore the first (VER) byte */
+ if(socksreq[1] == 255) { /* status / message type */
+ failf(data, "User was rejected by the SOCKS5 server (%d %d).",
+ socksreq[0], socksreq[1]);
+ gss_release_name(&gss_status, &server);
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ if(socksreq[1] != 1) { /* status / message type */
+ failf(data, "Invalid GSS-API authentication response type (%d %d).",
+ socksreq[0], socksreq[1]);
+ gss_release_name(&gss_status, &server);
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ memcpy(&us_length, socksreq + 2, sizeof(short));
+ us_length = ntohs(us_length);
+
+ gss_recv_token.length = us_length;
+ gss_recv_token.value = malloc(us_length);
+ if(!gss_recv_token.value) {
+ failf(data,
+ "Could not allocate memory for GSS-API authentication "
+ "response token.");
+ gss_release_name(&gss_status, &server);
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ result = Curl_blockread_all(data, sock, (char *)gss_recv_token.value,
+ gss_recv_token.length, &actualread);
+
+ if(result || (actualread != us_length)) {
+ failf(data, "Failed to receive GSS-API authentication token.");
+ gss_release_name(&gss_status, &server);
+ gss_release_buffer(&gss_status, &gss_recv_token);
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ gss_token = &gss_recv_token;
+ }
+
+ gss_release_name(&gss_status, &server);
+
+ /* Everything is good so far, user was authenticated! */
+ gss_major_status = gss_inquire_context(&gss_minor_status, gss_context,
+ &gss_client_name, NULL, NULL, NULL,
+ NULL, NULL, NULL);
+ if(check_gss_err(data, gss_major_status,
+ gss_minor_status, "gss_inquire_context")) {
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ gss_release_name(&gss_status, &gss_client_name);
+ failf(data, "Failed to determine user name.");
+ return CURLE_COULDNT_CONNECT;
+ }
+ gss_major_status = gss_display_name(&gss_minor_status, gss_client_name,
+ &gss_send_token, NULL);
+ if(check_gss_err(data, gss_major_status,
+ gss_minor_status, "gss_display_name")) {
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ gss_release_name(&gss_status, &gss_client_name);
+ gss_release_buffer(&gss_status, &gss_send_token);
+ failf(data, "Failed to determine user name.");
+ return CURLE_COULDNT_CONNECT;
+ }
+ user = malloc(gss_send_token.length + 1);
+ if(!user) {
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ gss_release_name(&gss_status, &gss_client_name);
+ gss_release_buffer(&gss_status, &gss_send_token);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ memcpy(user, gss_send_token.value, gss_send_token.length);
+ user[gss_send_token.length] = '\0';
+ gss_release_name(&gss_status, &gss_client_name);
+ gss_release_buffer(&gss_status, &gss_send_token);
+ infof(data, "SOCKS5 server authenticated user %s with GSS-API.",user);
+ free(user);
+ user = NULL;
+
+ /* Do encryption */
+ socksreq[0] = 1; /* GSS-API subnegotiation version */
+ socksreq[1] = 2; /* encryption message type */
+
+ gss_enc = 0; /* no data protection */
+ /* do confidentiality protection if supported */
+ if(gss_ret_flags & GSS_C_CONF_FLAG)
+ gss_enc = 2;
+ /* else do integrity protection */
+ else if(gss_ret_flags & GSS_C_INTEG_FLAG)
+ gss_enc = 1;
+
+ infof(data, "SOCKS5 server supports GSS-API %s data protection.",
+ (gss_enc == 0)?"no":((gss_enc==1)?"integrity":"confidentiality"));
+ /* force for the moment to no data protection */
+ gss_enc = 0;
+ /*
+ * Sending the encryption type in clear seems wrong. It should be
+ * protected with gss_seal()/gss_wrap(). See RFC1961 extract below
+ * The NEC reference implementations on which this is based is
+ * therefore at fault
+ *
+ * +------+------+------+.......................+
+ * + ver | mtyp | len | token |
+ * +------+------+------+.......................+
+ * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets |
+ * +------+------+------+.......................+
+ *
+ * Where:
+ *
+ * - "ver" is the protocol version number, here 1 to represent the
+ * first version of the SOCKS/GSS-API protocol
+ *
+ * - "mtyp" is the message type, here 2 to represent a protection
+ * -level negotiation message
+ *
+ * - "len" is the length of the "token" field in octets
+ *
+ * - "token" is the GSS-API encapsulated protection level
+ *
+ * The token is produced by encapsulating an octet containing the
+ * required protection level using gss_seal()/gss_wrap() with conf_req
+ * set to FALSE. The token is verified using gss_unseal()/
+ * gss_unwrap().
+ *
+ */
+ if(data->set.socks5_gssapi_nec) {
+ us_length = htons((short)1);
+ memcpy(socksreq + 2, &us_length, sizeof(short));
+ }
+ else {
+ gss_send_token.length = 1;
+ gss_send_token.value = malloc(1);
+ if(!gss_send_token.value) {
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ memcpy(gss_send_token.value, &gss_enc, 1);
+
+ gss_major_status = gss_wrap(&gss_minor_status, gss_context, 0,
+ GSS_C_QOP_DEFAULT, &gss_send_token,
+ &gss_conf_state, &gss_w_token);
+
+ if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_wrap")) {
+ gss_release_buffer(&gss_status, &gss_send_token);
+ gss_release_buffer(&gss_status, &gss_w_token);
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ failf(data, "Failed to wrap GSS-API encryption value into token.");
+ return CURLE_COULDNT_CONNECT;
+ }
+ gss_release_buffer(&gss_status, &gss_send_token);
+
+ us_length = htons((short)gss_w_token.length);
+ memcpy(socksreq + 2, &us_length, sizeof(short));
+ }
+
+ code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written);
+ if(code || (4 != written)) {
+ failf(data, "Failed to send GSS-API encryption request.");
+ gss_release_buffer(&gss_status, &gss_w_token);
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ if(data->set.socks5_gssapi_nec) {
+ memcpy(socksreq, &gss_enc, 1);
+ code = Curl_write_plain(data, sock, socksreq, 1, &written);
+ if(code || ( 1 != written)) {
+ failf(data, "Failed to send GSS-API encryption type.");
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ return CURLE_COULDNT_CONNECT;
+ }
+ }
+ else {
+ code = Curl_write_plain(data, sock, (char *)gss_w_token.value,
+ gss_w_token.length, &written);
+ if(code || ((ssize_t)gss_w_token.length != written)) {
+ failf(data, "Failed to send GSS-API encryption type.");
+ gss_release_buffer(&gss_status, &gss_w_token);
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ return CURLE_COULDNT_CONNECT;
+ }
+ gss_release_buffer(&gss_status, &gss_w_token);
+ }
+
+ result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread);
+ if(result || (actualread != 4)) {
+ failf(data, "Failed to receive GSS-API encryption response.");
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ /* ignore the first (VER) byte */
+ if(socksreq[1] == 255) { /* status / message type */
+ failf(data, "User was rejected by the SOCKS5 server (%d %d).",
+ socksreq[0], socksreq[1]);
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ if(socksreq[1] != 2) { /* status / message type */
+ failf(data, "Invalid GSS-API encryption response type (%d %d).",
+ socksreq[0], socksreq[1]);
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ memcpy(&us_length, socksreq + 2, sizeof(short));
+ us_length = ntohs(us_length);
+
+ gss_recv_token.length = us_length;
+ gss_recv_token.value = malloc(gss_recv_token.length);
+ if(!gss_recv_token.value) {
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ result = Curl_blockread_all(data, sock, (char *)gss_recv_token.value,
+ gss_recv_token.length, &actualread);
+
+ if(result || (actualread != us_length)) {
+ failf(data, "Failed to receive GSS-API encryptrion type.");
+ gss_release_buffer(&gss_status, &gss_recv_token);
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ if(!data->set.socks5_gssapi_nec) {
+ gss_major_status = gss_unwrap(&gss_minor_status, gss_context,
+ &gss_recv_token, &gss_w_token,
+ 0, GSS_C_QOP_DEFAULT);
+
+ if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_unwrap")) {
+ gss_release_buffer(&gss_status, &gss_recv_token);
+ gss_release_buffer(&gss_status, &gss_w_token);
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ failf(data, "Failed to unwrap GSS-API encryption value into token.");
+ return CURLE_COULDNT_CONNECT;
+ }
+ gss_release_buffer(&gss_status, &gss_recv_token);
+
+ if(gss_w_token.length != 1) {
+ failf(data, "Invalid GSS-API encryption response length (%zu).",
+ gss_w_token.length);
+ gss_release_buffer(&gss_status, &gss_w_token);
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ memcpy(socksreq, gss_w_token.value, gss_w_token.length);
+ gss_release_buffer(&gss_status, &gss_w_token);
+ }
+ else {
+ if(gss_recv_token.length != 1) {
+ failf(data, "Invalid GSS-API encryption response length (%zu).",
+ gss_recv_token.length);
+ gss_release_buffer(&gss_status, &gss_recv_token);
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ memcpy(socksreq, gss_recv_token.value, gss_recv_token.length);
+ gss_release_buffer(&gss_status, &gss_recv_token);
+ }
+
+ (void)curlx_nonblock(sock, TRUE);
+
+ infof(data, "SOCKS5 access with%s protection granted.",
+ (socksreq[0] == 0)?"out GSS-API data":
+ ((socksreq[0] == 1)?" GSS-API integrity":" GSS-API confidentiality"));
+
+ conn->socks5_gssapi_enctype = socksreq[0];
+ if(socksreq[0] == 0)
+ gss_delete_sec_context(&gss_status, &gss_context, NULL);
+
+ return CURLE_OK;
+}
+
+#endif /* HAVE_GSSAPI && !CURL_DISABLE_PROXY */
diff --git a/libs/libcurl/src/socks_sspi.c b/libs/libcurl/src/socks_sspi.c index 210a0dfbc5..6ed7e1b8ca 100644 --- a/libs/libcurl/src/socks_sspi.c +++ b/libs/libcurl/src/socks_sspi.c @@ -1,611 +1,611 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2012, 2011, Markus Moeller, <markus_moeller@compuserve.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" - -#if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_PROXY) - -#include "urldata.h" -#include "sendf.h" -#include "connect.h" -#include "strerror.h" -#include "timeval.h" -#include "socks.h" -#include "curl_sspi.h" -#include "curl_multibyte.h" -#include "warnless.h" -#include "strdup.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* - * Helper sspi error functions. - */ -static int check_sspi_err(struct Curl_easy *data, - SECURITY_STATUS status, - const char *function) -{ - if(status != SEC_E_OK && - status != SEC_I_COMPLETE_AND_CONTINUE && - status != SEC_I_COMPLETE_NEEDED && - status != SEC_I_CONTINUE_NEEDED) { - char buffer[STRERROR_LEN]; - failf(data, "SSPI error: %s failed: %s", function, - Curl_sspi_strerror(status, buffer, sizeof(buffer))); - return 1; - } - return 0; -} - -/* This is the SSPI-using version of this function */ -CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, - struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - curl_socket_t sock = conn->sock[sockindex]; - CURLcode code; - ssize_t actualread; - ssize_t written; - int result; - /* Needs GSS-API authentication */ - SECURITY_STATUS status; - unsigned long sspi_ret_flags = 0; - unsigned char gss_enc; - SecBuffer sspi_send_token, sspi_recv_token, sspi_w_token[3]; - SecBufferDesc input_desc, output_desc, wrap_desc; - SecPkgContext_Sizes sspi_sizes; - CredHandle cred_handle; - CtxtHandle sspi_context; - PCtxtHandle context_handle = NULL; - SecPkgCredentials_Names names; - TimeStamp expiry; - char *service_name = NULL; - unsigned short us_length; - unsigned long qop; - unsigned char socksreq[4]; /* room for GSS-API exchange header only */ - const char *service = data->set.str[STRING_PROXY_SERVICE_NAME] ? - data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd"; - const size_t service_length = strlen(service); - - /* GSS-API request looks like - * +----+------+-----+----------------+ - * |VER | MTYP | LEN | TOKEN | - * +----+------+----------------------+ - * | 1 | 1 | 2 | up to 2^16 - 1 | - * +----+------+-----+----------------+ - */ - - /* prepare service name */ - if(strchr(service, '/')) { - service_name = strdup(service); - if(!service_name) - return CURLE_OUT_OF_MEMORY; - } - else { - service_name = malloc(service_length + - strlen(conn->socks_proxy.host.name) + 2); - if(!service_name) - return CURLE_OUT_OF_MEMORY; - msnprintf(service_name, service_length + - strlen(conn->socks_proxy.host.name) + 2, "%s/%s", - service, conn->socks_proxy.host.name); - } - - input_desc.cBuffers = 1; - input_desc.pBuffers = &sspi_recv_token; - input_desc.ulVersion = SECBUFFER_VERSION; - - sspi_recv_token.BufferType = SECBUFFER_TOKEN; - sspi_recv_token.cbBuffer = 0; - sspi_recv_token.pvBuffer = NULL; - - output_desc.cBuffers = 1; - output_desc.pBuffers = &sspi_send_token; - output_desc.ulVersion = SECBUFFER_VERSION; - - sspi_send_token.BufferType = SECBUFFER_TOKEN; - sspi_send_token.cbBuffer = 0; - sspi_send_token.pvBuffer = NULL; - - wrap_desc.cBuffers = 3; - wrap_desc.pBuffers = sspi_w_token; - wrap_desc.ulVersion = SECBUFFER_VERSION; - - cred_handle.dwLower = 0; - cred_handle.dwUpper = 0; - - status = s_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *) TEXT("Kerberos"), - SECPKG_CRED_OUTBOUND, - NULL, - NULL, - NULL, - NULL, - &cred_handle, - &expiry); - - if(check_sspi_err(data, status, "AcquireCredentialsHandle")) { - failf(data, "Failed to acquire credentials."); - free(service_name); - s_pSecFn->FreeCredentialsHandle(&cred_handle); - return CURLE_COULDNT_CONNECT; - } - - (void)curlx_nonblock(sock, FALSE); - - /* As long as we need to keep sending some context info, and there's no */ - /* errors, keep sending it... */ - for(;;) { - TCHAR *sname; - - sname = curlx_convert_UTF8_to_tchar(service_name); - if(!sname) - return CURLE_OUT_OF_MEMORY; - - status = s_pSecFn->InitializeSecurityContext(&cred_handle, - context_handle, - sname, - ISC_REQ_MUTUAL_AUTH | - ISC_REQ_ALLOCATE_MEMORY | - ISC_REQ_CONFIDENTIALITY | - ISC_REQ_REPLAY_DETECT, - 0, - SECURITY_NATIVE_DREP, - &input_desc, - 0, - &sspi_context, - &output_desc, - &sspi_ret_flags, - &expiry); - - curlx_unicodefree(sname); - - if(sspi_recv_token.pvBuffer) { - s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); - sspi_recv_token.pvBuffer = NULL; - sspi_recv_token.cbBuffer = 0; - } - - if(check_sspi_err(data, status, "InitializeSecurityContext")) { - free(service_name); - s_pSecFn->FreeCredentialsHandle(&cred_handle); - s_pSecFn->DeleteSecurityContext(&sspi_context); - if(sspi_recv_token.pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); - failf(data, "Failed to initialise security context."); - return CURLE_COULDNT_CONNECT; - } - - if(sspi_send_token.cbBuffer) { - socksreq[0] = 1; /* GSS-API subnegotiation version */ - socksreq[1] = 1; /* authentication message type */ - us_length = htons((short)sspi_send_token.cbBuffer); - memcpy(socksreq + 2, &us_length, sizeof(short)); - - code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written); - if(code || (4 != written)) { - failf(data, "Failed to send SSPI authentication request."); - free(service_name); - if(sspi_send_token.pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); - if(sspi_recv_token.pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); - s_pSecFn->FreeCredentialsHandle(&cred_handle); - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_COULDNT_CONNECT; - } - - code = Curl_write_plain(data, sock, (char *)sspi_send_token.pvBuffer, - sspi_send_token.cbBuffer, &written); - if(code || (sspi_send_token.cbBuffer != (size_t)written)) { - failf(data, "Failed to send SSPI authentication token."); - free(service_name); - if(sspi_send_token.pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); - if(sspi_recv_token.pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); - s_pSecFn->FreeCredentialsHandle(&cred_handle); - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_COULDNT_CONNECT; - } - - } - - if(sspi_send_token.pvBuffer) { - s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); - sspi_send_token.pvBuffer = NULL; - } - sspi_send_token.cbBuffer = 0; - - if(sspi_recv_token.pvBuffer) { - s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); - sspi_recv_token.pvBuffer = NULL; - } - sspi_recv_token.cbBuffer = 0; - - if(status != SEC_I_CONTINUE_NEEDED) - break; - - /* analyse response */ - - /* GSS-API response looks like - * +----+------+-----+----------------+ - * |VER | MTYP | LEN | TOKEN | - * +----+------+----------------------+ - * | 1 | 1 | 2 | up to 2^16 - 1 | - * +----+------+-----+----------------+ - */ - - result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread); - if(result || (actualread != 4)) { - failf(data, "Failed to receive SSPI authentication response."); - free(service_name); - s_pSecFn->FreeCredentialsHandle(&cred_handle); - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_COULDNT_CONNECT; - } - - /* ignore the first (VER) byte */ - if(socksreq[1] == 255) { /* status / message type */ - failf(data, "User was rejected by the SOCKS5 server (%u %u).", - (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - free(service_name); - s_pSecFn->FreeCredentialsHandle(&cred_handle); - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_COULDNT_CONNECT; - } - - if(socksreq[1] != 1) { /* status / message type */ - failf(data, "Invalid SSPI authentication response type (%u %u).", - (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - free(service_name); - s_pSecFn->FreeCredentialsHandle(&cred_handle); - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_COULDNT_CONNECT; - } - - memcpy(&us_length, socksreq + 2, sizeof(short)); - us_length = ntohs(us_length); - - sspi_recv_token.cbBuffer = us_length; - sspi_recv_token.pvBuffer = malloc(us_length); - - if(!sspi_recv_token.pvBuffer) { - free(service_name); - s_pSecFn->FreeCredentialsHandle(&cred_handle); - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_OUT_OF_MEMORY; - } - result = Curl_blockread_all(data, sock, (char *)sspi_recv_token.pvBuffer, - sspi_recv_token.cbBuffer, &actualread); - - if(result || (actualread != us_length)) { - failf(data, "Failed to receive SSPI authentication token."); - free(service_name); - if(sspi_recv_token.pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); - s_pSecFn->FreeCredentialsHandle(&cred_handle); - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_COULDNT_CONNECT; - } - - context_handle = &sspi_context; - } - - free(service_name); - - /* Everything is good so far, user was authenticated! */ - status = s_pSecFn->QueryCredentialsAttributes(&cred_handle, - SECPKG_CRED_ATTR_NAMES, - &names); - s_pSecFn->FreeCredentialsHandle(&cred_handle); - if(check_sspi_err(data, status, "QueryCredentialAttributes")) { - s_pSecFn->DeleteSecurityContext(&sspi_context); - s_pSecFn->FreeContextBuffer(names.sUserName); - failf(data, "Failed to determine user name."); - return CURLE_COULDNT_CONNECT; - } - infof(data, "SOCKS5 server authenticated user %s with GSS-API.", - names.sUserName); - s_pSecFn->FreeContextBuffer(names.sUserName); - - /* Do encryption */ - socksreq[0] = 1; /* GSS-API subnegotiation version */ - socksreq[1] = 2; /* encryption message type */ - - gss_enc = 0; /* no data protection */ - /* do confidentiality protection if supported */ - if(sspi_ret_flags & ISC_REQ_CONFIDENTIALITY) - gss_enc = 2; - /* else do integrity protection */ - else if(sspi_ret_flags & ISC_REQ_INTEGRITY) - gss_enc = 1; - - infof(data, "SOCKS5 server supports GSS-API %s data protection.", - (gss_enc == 0)?"no":((gss_enc == 1)?"integrity":"confidentiality") ); - /* force to no data protection, avoid encryption/decryption for now */ - gss_enc = 0; - /* - * Sending the encryption type in clear seems wrong. It should be - * protected with gss_seal()/gss_wrap(). See RFC1961 extract below - * The NEC reference implementations on which this is based is - * therefore at fault - * - * +------+------+------+.......................+ - * + ver | mtyp | len | token | - * +------+------+------+.......................+ - * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets | - * +------+------+------+.......................+ - * - * Where: - * - * - "ver" is the protocol version number, here 1 to represent the - * first version of the SOCKS/GSS-API protocol - * - * - "mtyp" is the message type, here 2 to represent a protection - * -level negotiation message - * - * - "len" is the length of the "token" field in octets - * - * - "token" is the GSS-API encapsulated protection level - * - * The token is produced by encapsulating an octet containing the - * required protection level using gss_seal()/gss_wrap() with conf_req - * set to FALSE. The token is verified using gss_unseal()/ - * gss_unwrap(). - * - */ - - if(data->set.socks5_gssapi_nec) { - us_length = htons((short)1); - memcpy(socksreq + 2, &us_length, sizeof(short)); - } - else { - status = s_pSecFn->QueryContextAttributes(&sspi_context, - SECPKG_ATTR_SIZES, - &sspi_sizes); - if(check_sspi_err(data, status, "QueryContextAttributes")) { - s_pSecFn->DeleteSecurityContext(&sspi_context); - failf(data, "Failed to query security context attributes."); - return CURLE_COULDNT_CONNECT; - } - - sspi_w_token[0].cbBuffer = sspi_sizes.cbSecurityTrailer; - sspi_w_token[0].BufferType = SECBUFFER_TOKEN; - sspi_w_token[0].pvBuffer = malloc(sspi_sizes.cbSecurityTrailer); - - if(!sspi_w_token[0].pvBuffer) { - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_OUT_OF_MEMORY; - } - - sspi_w_token[1].cbBuffer = 1; - sspi_w_token[1].pvBuffer = malloc(1); - if(!sspi_w_token[1].pvBuffer) { - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_OUT_OF_MEMORY; - } - - memcpy(sspi_w_token[1].pvBuffer, &gss_enc, 1); - sspi_w_token[2].BufferType = SECBUFFER_PADDING; - sspi_w_token[2].cbBuffer = sspi_sizes.cbBlockSize; - sspi_w_token[2].pvBuffer = malloc(sspi_sizes.cbBlockSize); - if(!sspi_w_token[2].pvBuffer) { - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); - s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_OUT_OF_MEMORY; - } - status = s_pSecFn->EncryptMessage(&sspi_context, - KERB_WRAP_NO_ENCRYPT, - &wrap_desc, - 0); - if(check_sspi_err(data, status, "EncryptMessage")) { - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); - s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer); - s_pSecFn->DeleteSecurityContext(&sspi_context); - failf(data, "Failed to query security context attributes."); - return CURLE_COULDNT_CONNECT; - } - sspi_send_token.cbBuffer = sspi_w_token[0].cbBuffer - + sspi_w_token[1].cbBuffer - + sspi_w_token[2].cbBuffer; - sspi_send_token.pvBuffer = malloc(sspi_send_token.cbBuffer); - if(!sspi_send_token.pvBuffer) { - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); - s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer); - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_OUT_OF_MEMORY; - } - - memcpy(sspi_send_token.pvBuffer, sspi_w_token[0].pvBuffer, - sspi_w_token[0].cbBuffer); - memcpy((PUCHAR) sspi_send_token.pvBuffer +(int)sspi_w_token[0].cbBuffer, - sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer); - memcpy((PUCHAR) sspi_send_token.pvBuffer - + sspi_w_token[0].cbBuffer - + sspi_w_token[1].cbBuffer, - sspi_w_token[2].pvBuffer, sspi_w_token[2].cbBuffer); - - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); - sspi_w_token[0].pvBuffer = NULL; - sspi_w_token[0].cbBuffer = 0; - s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - sspi_w_token[1].pvBuffer = NULL; - sspi_w_token[1].cbBuffer = 0; - s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer); - sspi_w_token[2].pvBuffer = NULL; - sspi_w_token[2].cbBuffer = 0; - - us_length = htons((short)sspi_send_token.cbBuffer); - memcpy(socksreq + 2, &us_length, sizeof(short)); - } - - code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written); - if(code || (4 != written)) { - failf(data, "Failed to send SSPI encryption request."); - if(sspi_send_token.pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_COULDNT_CONNECT; - } - - if(data->set.socks5_gssapi_nec) { - memcpy(socksreq, &gss_enc, 1); - code = Curl_write_plain(data, sock, (char *)socksreq, 1, &written); - if(code || (1 != written)) { - failf(data, "Failed to send SSPI encryption type."); - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_COULDNT_CONNECT; - } - } - else { - code = Curl_write_plain(data, sock, (char *)sspi_send_token.pvBuffer, - sspi_send_token.cbBuffer, &written); - if(code || (sspi_send_token.cbBuffer != (size_t)written)) { - failf(data, "Failed to send SSPI encryption type."); - if(sspi_send_token.pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_COULDNT_CONNECT; - } - if(sspi_send_token.pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); - } - - result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread); - if(result || (actualread != 4)) { - failf(data, "Failed to receive SSPI encryption response."); - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_COULDNT_CONNECT; - } - - /* ignore the first (VER) byte */ - if(socksreq[1] == 255) { /* status / message type */ - failf(data, "User was rejected by the SOCKS5 server (%u %u).", - (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_COULDNT_CONNECT; - } - - if(socksreq[1] != 2) { /* status / message type */ - failf(data, "Invalid SSPI encryption response type (%u %u).", - (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_COULDNT_CONNECT; - } - - memcpy(&us_length, socksreq + 2, sizeof(short)); - us_length = ntohs(us_length); - - sspi_w_token[0].cbBuffer = us_length; - sspi_w_token[0].pvBuffer = malloc(us_length); - if(!sspi_w_token[0].pvBuffer) { - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_OUT_OF_MEMORY; - } - - result = Curl_blockread_all(data, sock, (char *)sspi_w_token[0].pvBuffer, - sspi_w_token[0].cbBuffer, &actualread); - - if(result || (actualread != us_length)) { - failf(data, "Failed to receive SSPI encryption type."); - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_COULDNT_CONNECT; - } - - - if(!data->set.socks5_gssapi_nec) { - wrap_desc.cBuffers = 2; - sspi_w_token[0].BufferType = SECBUFFER_STREAM; - sspi_w_token[1].BufferType = SECBUFFER_DATA; - sspi_w_token[1].cbBuffer = 0; - sspi_w_token[1].pvBuffer = NULL; - - status = s_pSecFn->DecryptMessage(&sspi_context, - &wrap_desc, - 0, - &qop); - - if(check_sspi_err(data, status, "DecryptMessage")) { - if(sspi_w_token[0].pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); - if(sspi_w_token[1].pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - s_pSecFn->DeleteSecurityContext(&sspi_context); - failf(data, "Failed to query security context attributes."); - return CURLE_COULDNT_CONNECT; - } - - if(sspi_w_token[1].cbBuffer != 1) { - failf(data, "Invalid SSPI encryption response length (%lu).", - (unsigned long)sspi_w_token[1].cbBuffer); - if(sspi_w_token[0].pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); - if(sspi_w_token[1].pvBuffer) - s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_COULDNT_CONNECT; - } - - memcpy(socksreq, sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer); - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); - s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - } - else { - if(sspi_w_token[0].cbBuffer != 1) { - failf(data, "Invalid SSPI encryption response length (%lu).", - (unsigned long)sspi_w_token[0].cbBuffer); - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); - s_pSecFn->DeleteSecurityContext(&sspi_context); - return CURLE_COULDNT_CONNECT; - } - memcpy(socksreq, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer); - s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); - } - (void)curlx_nonblock(sock, TRUE); - - infof(data, "SOCKS5 access with%s protection granted.", - (socksreq[0] == 0)?"out GSS-API data": - ((socksreq[0] == 1)?" GSS-API integrity":" GSS-API confidentiality")); - - /* For later use if encryption is required - conn->socks5_gssapi_enctype = socksreq[0]; - if(socksreq[0] != 0) - conn->socks5_sspi_context = sspi_context; - else { - s_pSecFn->DeleteSecurityContext(&sspi_context); - conn->socks5_sspi_context = sspi_context; - } - */ - return CURLE_OK; -} -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012, 2011, Markus Moeller, <markus_moeller@compuserve.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"
+
+#if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_PROXY)
+
+#include "urldata.h"
+#include "sendf.h"
+#include "connect.h"
+#include "strerror.h"
+#include "timeval.h"
+#include "socks.h"
+#include "curl_sspi.h"
+#include "curl_multibyte.h"
+#include "warnless.h"
+#include "strdup.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * Helper sspi error functions.
+ */
+static int check_sspi_err(struct Curl_easy *data,
+ SECURITY_STATUS status,
+ const char *function)
+{
+ if(status != SEC_E_OK &&
+ status != SEC_I_COMPLETE_AND_CONTINUE &&
+ status != SEC_I_COMPLETE_NEEDED &&
+ status != SEC_I_CONTINUE_NEEDED) {
+ char buffer[STRERROR_LEN];
+ failf(data, "SSPI error: %s failed: %s", function,
+ Curl_sspi_strerror(status, buffer, sizeof(buffer)));
+ return 1;
+ }
+ return 0;
+}
+
+/* This is the SSPI-using version of this function */
+CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
+ struct Curl_easy *data)
+{
+ struct connectdata *conn = data->conn;
+ curl_socket_t sock = conn->sock[sockindex];
+ CURLcode code;
+ ssize_t actualread;
+ ssize_t written;
+ int result;
+ /* Needs GSS-API authentication */
+ SECURITY_STATUS status;
+ unsigned long sspi_ret_flags = 0;
+ unsigned char gss_enc;
+ SecBuffer sspi_send_token, sspi_recv_token, sspi_w_token[3];
+ SecBufferDesc input_desc, output_desc, wrap_desc;
+ SecPkgContext_Sizes sspi_sizes;
+ CredHandle cred_handle;
+ CtxtHandle sspi_context;
+ PCtxtHandle context_handle = NULL;
+ SecPkgCredentials_Names names;
+ TimeStamp expiry;
+ char *service_name = NULL;
+ unsigned short us_length;
+ unsigned long qop;
+ unsigned char socksreq[4]; /* room for GSS-API exchange header only */
+ const char *service = data->set.str[STRING_PROXY_SERVICE_NAME] ?
+ data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd";
+ const size_t service_length = strlen(service);
+
+ /* GSS-API request looks like
+ * +----+------+-----+----------------+
+ * |VER | MTYP | LEN | TOKEN |
+ * +----+------+----------------------+
+ * | 1 | 1 | 2 | up to 2^16 - 1 |
+ * +----+------+-----+----------------+
+ */
+
+ /* prepare service name */
+ if(strchr(service, '/')) {
+ service_name = strdup(service);
+ if(!service_name)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else {
+ service_name = malloc(service_length +
+ strlen(conn->socks_proxy.host.name) + 2);
+ if(!service_name)
+ return CURLE_OUT_OF_MEMORY;
+ msnprintf(service_name, service_length +
+ strlen(conn->socks_proxy.host.name) + 2, "%s/%s",
+ service, conn->socks_proxy.host.name);
+ }
+
+ input_desc.cBuffers = 1;
+ input_desc.pBuffers = &sspi_recv_token;
+ input_desc.ulVersion = SECBUFFER_VERSION;
+
+ sspi_recv_token.BufferType = SECBUFFER_TOKEN;
+ sspi_recv_token.cbBuffer = 0;
+ sspi_recv_token.pvBuffer = NULL;
+
+ output_desc.cBuffers = 1;
+ output_desc.pBuffers = &sspi_send_token;
+ output_desc.ulVersion = SECBUFFER_VERSION;
+
+ sspi_send_token.BufferType = SECBUFFER_TOKEN;
+ sspi_send_token.cbBuffer = 0;
+ sspi_send_token.pvBuffer = NULL;
+
+ wrap_desc.cBuffers = 3;
+ wrap_desc.pBuffers = sspi_w_token;
+ wrap_desc.ulVersion = SECBUFFER_VERSION;
+
+ cred_handle.dwLower = 0;
+ cred_handle.dwUpper = 0;
+
+ status = s_pSecFn->AcquireCredentialsHandle(NULL,
+ (TCHAR *) TEXT("Kerberos"),
+ SECPKG_CRED_OUTBOUND,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &cred_handle,
+ &expiry);
+
+ if(check_sspi_err(data, status, "AcquireCredentialsHandle")) {
+ failf(data, "Failed to acquire credentials.");
+ free(service_name);
+ s_pSecFn->FreeCredentialsHandle(&cred_handle);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ (void)curlx_nonblock(sock, FALSE);
+
+ /* As long as we need to keep sending some context info, and there's no */
+ /* errors, keep sending it... */
+ for(;;) {
+ TCHAR *sname;
+
+ sname = curlx_convert_UTF8_to_tchar(service_name);
+ if(!sname)
+ return CURLE_OUT_OF_MEMORY;
+
+ status = s_pSecFn->InitializeSecurityContext(&cred_handle,
+ context_handle,
+ sname,
+ ISC_REQ_MUTUAL_AUTH |
+ ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_REQ_REPLAY_DETECT,
+ 0,
+ SECURITY_NATIVE_DREP,
+ &input_desc,
+ 0,
+ &sspi_context,
+ &output_desc,
+ &sspi_ret_flags,
+ &expiry);
+
+ curlx_unicodefree(sname);
+
+ if(sspi_recv_token.pvBuffer) {
+ s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
+ sspi_recv_token.pvBuffer = NULL;
+ sspi_recv_token.cbBuffer = 0;
+ }
+
+ if(check_sspi_err(data, status, "InitializeSecurityContext")) {
+ free(service_name);
+ s_pSecFn->FreeCredentialsHandle(&cred_handle);
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ if(sspi_recv_token.pvBuffer)
+ s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
+ failf(data, "Failed to initialise security context.");
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ if(sspi_send_token.cbBuffer) {
+ socksreq[0] = 1; /* GSS-API subnegotiation version */
+ socksreq[1] = 1; /* authentication message type */
+ us_length = htons((short)sspi_send_token.cbBuffer);
+ memcpy(socksreq + 2, &us_length, sizeof(short));
+
+ code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written);
+ if(code || (4 != written)) {
+ failf(data, "Failed to send SSPI authentication request.");
+ free(service_name);
+ if(sspi_send_token.pvBuffer)
+ s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
+ if(sspi_recv_token.pvBuffer)
+ s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
+ s_pSecFn->FreeCredentialsHandle(&cred_handle);
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ code = Curl_write_plain(data, sock, (char *)sspi_send_token.pvBuffer,
+ sspi_send_token.cbBuffer, &written);
+ if(code || (sspi_send_token.cbBuffer != (size_t)written)) {
+ failf(data, "Failed to send SSPI authentication token.");
+ free(service_name);
+ if(sspi_send_token.pvBuffer)
+ s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
+ if(sspi_recv_token.pvBuffer)
+ s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
+ s_pSecFn->FreeCredentialsHandle(&cred_handle);
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ }
+
+ if(sspi_send_token.pvBuffer) {
+ s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
+ sspi_send_token.pvBuffer = NULL;
+ }
+ sspi_send_token.cbBuffer = 0;
+
+ if(sspi_recv_token.pvBuffer) {
+ s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
+ sspi_recv_token.pvBuffer = NULL;
+ }
+ sspi_recv_token.cbBuffer = 0;
+
+ if(status != SEC_I_CONTINUE_NEEDED)
+ break;
+
+ /* analyse response */
+
+ /* GSS-API response looks like
+ * +----+------+-----+----------------+
+ * |VER | MTYP | LEN | TOKEN |
+ * +----+------+----------------------+
+ * | 1 | 1 | 2 | up to 2^16 - 1 |
+ * +----+------+-----+----------------+
+ */
+
+ result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread);
+ if(result || (actualread != 4)) {
+ failf(data, "Failed to receive SSPI authentication response.");
+ free(service_name);
+ s_pSecFn->FreeCredentialsHandle(&cred_handle);
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ /* ignore the first (VER) byte */
+ if(socksreq[1] == 255) { /* status / message type */
+ failf(data, "User was rejected by the SOCKS5 server (%u %u).",
+ (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
+ free(service_name);
+ s_pSecFn->FreeCredentialsHandle(&cred_handle);
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ if(socksreq[1] != 1) { /* status / message type */
+ failf(data, "Invalid SSPI authentication response type (%u %u).",
+ (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
+ free(service_name);
+ s_pSecFn->FreeCredentialsHandle(&cred_handle);
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ memcpy(&us_length, socksreq + 2, sizeof(short));
+ us_length = ntohs(us_length);
+
+ sspi_recv_token.cbBuffer = us_length;
+ sspi_recv_token.pvBuffer = malloc(us_length);
+
+ if(!sspi_recv_token.pvBuffer) {
+ free(service_name);
+ s_pSecFn->FreeCredentialsHandle(&cred_handle);
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ result = Curl_blockread_all(data, sock, (char *)sspi_recv_token.pvBuffer,
+ sspi_recv_token.cbBuffer, &actualread);
+
+ if(result || (actualread != us_length)) {
+ failf(data, "Failed to receive SSPI authentication token.");
+ free(service_name);
+ if(sspi_recv_token.pvBuffer)
+ s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
+ s_pSecFn->FreeCredentialsHandle(&cred_handle);
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ context_handle = &sspi_context;
+ }
+
+ free(service_name);
+
+ /* Everything is good so far, user was authenticated! */
+ status = s_pSecFn->QueryCredentialsAttributes(&cred_handle,
+ SECPKG_CRED_ATTR_NAMES,
+ &names);
+ s_pSecFn->FreeCredentialsHandle(&cred_handle);
+ if(check_sspi_err(data, status, "QueryCredentialAttributes")) {
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ s_pSecFn->FreeContextBuffer(names.sUserName);
+ failf(data, "Failed to determine user name.");
+ return CURLE_COULDNT_CONNECT;
+ }
+ infof(data, "SOCKS5 server authenticated user %s with GSS-API.",
+ names.sUserName);
+ s_pSecFn->FreeContextBuffer(names.sUserName);
+
+ /* Do encryption */
+ socksreq[0] = 1; /* GSS-API subnegotiation version */
+ socksreq[1] = 2; /* encryption message type */
+
+ gss_enc = 0; /* no data protection */
+ /* do confidentiality protection if supported */
+ if(sspi_ret_flags & ISC_REQ_CONFIDENTIALITY)
+ gss_enc = 2;
+ /* else do integrity protection */
+ else if(sspi_ret_flags & ISC_REQ_INTEGRITY)
+ gss_enc = 1;
+
+ infof(data, "SOCKS5 server supports GSS-API %s data protection.",
+ (gss_enc == 0)?"no":((gss_enc == 1)?"integrity":"confidentiality") );
+ /* force to no data protection, avoid encryption/decryption for now */
+ gss_enc = 0;
+ /*
+ * Sending the encryption type in clear seems wrong. It should be
+ * protected with gss_seal()/gss_wrap(). See RFC1961 extract below
+ * The NEC reference implementations on which this is based is
+ * therefore at fault
+ *
+ * +------+------+------+.......................+
+ * + ver | mtyp | len | token |
+ * +------+------+------+.......................+
+ * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets |
+ * +------+------+------+.......................+
+ *
+ * Where:
+ *
+ * - "ver" is the protocol version number, here 1 to represent the
+ * first version of the SOCKS/GSS-API protocol
+ *
+ * - "mtyp" is the message type, here 2 to represent a protection
+ * -level negotiation message
+ *
+ * - "len" is the length of the "token" field in octets
+ *
+ * - "token" is the GSS-API encapsulated protection level
+ *
+ * The token is produced by encapsulating an octet containing the
+ * required protection level using gss_seal()/gss_wrap() with conf_req
+ * set to FALSE. The token is verified using gss_unseal()/
+ * gss_unwrap().
+ *
+ */
+
+ if(data->set.socks5_gssapi_nec) {
+ us_length = htons((short)1);
+ memcpy(socksreq + 2, &us_length, sizeof(short));
+ }
+ else {
+ status = s_pSecFn->QueryContextAttributes(&sspi_context,
+ SECPKG_ATTR_SIZES,
+ &sspi_sizes);
+ if(check_sspi_err(data, status, "QueryContextAttributes")) {
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ failf(data, "Failed to query security context attributes.");
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ sspi_w_token[0].cbBuffer = sspi_sizes.cbSecurityTrailer;
+ sspi_w_token[0].BufferType = SECBUFFER_TOKEN;
+ sspi_w_token[0].pvBuffer = malloc(sspi_sizes.cbSecurityTrailer);
+
+ if(!sspi_w_token[0].pvBuffer) {
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ sspi_w_token[1].cbBuffer = 1;
+ sspi_w_token[1].pvBuffer = malloc(1);
+ if(!sspi_w_token[1].pvBuffer) {
+ s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ memcpy(sspi_w_token[1].pvBuffer, &gss_enc, 1);
+ sspi_w_token[2].BufferType = SECBUFFER_PADDING;
+ sspi_w_token[2].cbBuffer = sspi_sizes.cbBlockSize;
+ sspi_w_token[2].pvBuffer = malloc(sspi_sizes.cbBlockSize);
+ if(!sspi_w_token[2].pvBuffer) {
+ s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+ s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ status = s_pSecFn->EncryptMessage(&sspi_context,
+ KERB_WRAP_NO_ENCRYPT,
+ &wrap_desc,
+ 0);
+ if(check_sspi_err(data, status, "EncryptMessage")) {
+ s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+ s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
+ s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ failf(data, "Failed to query security context attributes.");
+ return CURLE_COULDNT_CONNECT;
+ }
+ sspi_send_token.cbBuffer = sspi_w_token[0].cbBuffer
+ + sspi_w_token[1].cbBuffer
+ + sspi_w_token[2].cbBuffer;
+ sspi_send_token.pvBuffer = malloc(sspi_send_token.cbBuffer);
+ if(!sspi_send_token.pvBuffer) {
+ s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+ s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
+ s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ memcpy(sspi_send_token.pvBuffer, sspi_w_token[0].pvBuffer,
+ sspi_w_token[0].cbBuffer);
+ memcpy((PUCHAR) sspi_send_token.pvBuffer +(int)sspi_w_token[0].cbBuffer,
+ sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer);
+ memcpy((PUCHAR) sspi_send_token.pvBuffer
+ + sspi_w_token[0].cbBuffer
+ + sspi_w_token[1].cbBuffer,
+ sspi_w_token[2].pvBuffer, sspi_w_token[2].cbBuffer);
+
+ s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+ sspi_w_token[0].pvBuffer = NULL;
+ sspi_w_token[0].cbBuffer = 0;
+ s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
+ sspi_w_token[1].pvBuffer = NULL;
+ sspi_w_token[1].cbBuffer = 0;
+ s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
+ sspi_w_token[2].pvBuffer = NULL;
+ sspi_w_token[2].cbBuffer = 0;
+
+ us_length = htons((short)sspi_send_token.cbBuffer);
+ memcpy(socksreq + 2, &us_length, sizeof(short));
+ }
+
+ code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written);
+ if(code || (4 != written)) {
+ failf(data, "Failed to send SSPI encryption request.");
+ if(sspi_send_token.pvBuffer)
+ s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ if(data->set.socks5_gssapi_nec) {
+ memcpy(socksreq, &gss_enc, 1);
+ code = Curl_write_plain(data, sock, (char *)socksreq, 1, &written);
+ if(code || (1 != written)) {
+ failf(data, "Failed to send SSPI encryption type.");
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_COULDNT_CONNECT;
+ }
+ }
+ else {
+ code = Curl_write_plain(data, sock, (char *)sspi_send_token.pvBuffer,
+ sspi_send_token.cbBuffer, &written);
+ if(code || (sspi_send_token.cbBuffer != (size_t)written)) {
+ failf(data, "Failed to send SSPI encryption type.");
+ if(sspi_send_token.pvBuffer)
+ s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_COULDNT_CONNECT;
+ }
+ if(sspi_send_token.pvBuffer)
+ s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
+ }
+
+ result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread);
+ if(result || (actualread != 4)) {
+ failf(data, "Failed to receive SSPI encryption response.");
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ /* ignore the first (VER) byte */
+ if(socksreq[1] == 255) { /* status / message type */
+ failf(data, "User was rejected by the SOCKS5 server (%u %u).",
+ (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ if(socksreq[1] != 2) { /* status / message type */
+ failf(data, "Invalid SSPI encryption response type (%u %u).",
+ (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ memcpy(&us_length, socksreq + 2, sizeof(short));
+ us_length = ntohs(us_length);
+
+ sspi_w_token[0].cbBuffer = us_length;
+ sspi_w_token[0].pvBuffer = malloc(us_length);
+ if(!sspi_w_token[0].pvBuffer) {
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ result = Curl_blockread_all(data, sock, (char *)sspi_w_token[0].pvBuffer,
+ sspi_w_token[0].cbBuffer, &actualread);
+
+ if(result || (actualread != us_length)) {
+ failf(data, "Failed to receive SSPI encryption type.");
+ s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+
+ if(!data->set.socks5_gssapi_nec) {
+ wrap_desc.cBuffers = 2;
+ sspi_w_token[0].BufferType = SECBUFFER_STREAM;
+ sspi_w_token[1].BufferType = SECBUFFER_DATA;
+ sspi_w_token[1].cbBuffer = 0;
+ sspi_w_token[1].pvBuffer = NULL;
+
+ status = s_pSecFn->DecryptMessage(&sspi_context,
+ &wrap_desc,
+ 0,
+ &qop);
+
+ if(check_sspi_err(data, status, "DecryptMessage")) {
+ if(sspi_w_token[0].pvBuffer)
+ s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+ if(sspi_w_token[1].pvBuffer)
+ s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ failf(data, "Failed to query security context attributes.");
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ if(sspi_w_token[1].cbBuffer != 1) {
+ failf(data, "Invalid SSPI encryption response length (%lu).",
+ (unsigned long)sspi_w_token[1].cbBuffer);
+ if(sspi_w_token[0].pvBuffer)
+ s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+ if(sspi_w_token[1].pvBuffer)
+ s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ memcpy(socksreq, sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer);
+ s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+ s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
+ }
+ else {
+ if(sspi_w_token[0].cbBuffer != 1) {
+ failf(data, "Invalid SSPI encryption response length (%lu).",
+ (unsigned long)sspi_w_token[0].cbBuffer);
+ s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ return CURLE_COULDNT_CONNECT;
+ }
+ memcpy(socksreq, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer);
+ s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+ }
+ (void)curlx_nonblock(sock, TRUE);
+
+ infof(data, "SOCKS5 access with%s protection granted.",
+ (socksreq[0] == 0)?"out GSS-API data":
+ ((socksreq[0] == 1)?" GSS-API integrity":" GSS-API confidentiality"));
+
+ /* For later use if encryption is required
+ conn->socks5_gssapi_enctype = socksreq[0];
+ if(socksreq[0] != 0)
+ conn->socks5_sspi_context = sspi_context;
+ else {
+ s_pSecFn->DeleteSecurityContext(&sspi_context);
+ conn->socks5_sspi_context = sspi_context;
+ }
+ */
+ return CURLE_OK;
+}
+#endif
diff --git a/libs/libcurl/src/speedcheck.c b/libs/libcurl/src/speedcheck.c index 3ddc43d2de..0f41f294f8 100644 --- a/libs/libcurl/src/speedcheck.c +++ b/libs/libcurl/src/speedcheck.c @@ -1,79 +1,79 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "sendf.h" -#include "multiif.h" -#include "speedcheck.h" - -void Curl_speedinit(struct Curl_easy *data) -{ - memset(&data->state.keeps_speed, 0, sizeof(struct curltime)); -} - -/* - * @unittest: 1606 - */ -CURLcode Curl_speedcheck(struct Curl_easy *data, - struct curltime now) -{ - if(data->req.keepon & KEEP_RECV_PAUSE) - /* A paused transfer is not qualified for speed checks */ - return CURLE_OK; - - if((data->progress.current_speed >= 0) && data->set.low_speed_time) { - if(data->progress.current_speed < data->set.low_speed_limit) { - if(!data->state.keeps_speed.tv_sec) - /* under the limit at this very moment */ - data->state.keeps_speed = now; - else { - /* how long has it been under the limit */ - timediff_t howlong = Curl_timediff(now, data->state.keeps_speed); - - if(howlong >= data->set.low_speed_time * 1000) { - /* too long */ - failf(data, - "Operation too slow. " - "Less than %ld bytes/sec transferred the last %ld seconds", - data->set.low_speed_limit, - data->set.low_speed_time); - return CURLE_OPERATION_TIMEDOUT; - } - } - } - else - /* faster right now */ - data->state.keeps_speed.tv_sec = 0; - } - - if(data->set.low_speed_limit) - /* if low speed limit is enabled, set the expire timer to make this - connection's speed get checked again in a second */ - Curl_expire(data, 1000, EXPIRE_SPEEDCHECK); - - return CURLE_OK; -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "sendf.h"
+#include "multiif.h"
+#include "speedcheck.h"
+
+void Curl_speedinit(struct Curl_easy *data)
+{
+ memset(&data->state.keeps_speed, 0, sizeof(struct curltime));
+}
+
+/*
+ * @unittest: 1606
+ */
+CURLcode Curl_speedcheck(struct Curl_easy *data,
+ struct curltime now)
+{
+ if(data->req.keepon & KEEP_RECV_PAUSE)
+ /* A paused transfer is not qualified for speed checks */
+ return CURLE_OK;
+
+ if((data->progress.current_speed >= 0) && data->set.low_speed_time) {
+ if(data->progress.current_speed < data->set.low_speed_limit) {
+ if(!data->state.keeps_speed.tv_sec)
+ /* under the limit at this very moment */
+ data->state.keeps_speed = now;
+ else {
+ /* how long has it been under the limit */
+ timediff_t howlong = Curl_timediff(now, data->state.keeps_speed);
+
+ if(howlong >= data->set.low_speed_time * 1000) {
+ /* too long */
+ failf(data,
+ "Operation too slow. "
+ "Less than %ld bytes/sec transferred the last %ld seconds",
+ data->set.low_speed_limit,
+ data->set.low_speed_time);
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+ }
+ }
+ else
+ /* faster right now */
+ data->state.keeps_speed.tv_sec = 0;
+ }
+
+ if(data->set.low_speed_limit)
+ /* if low speed limit is enabled, set the expire timer to make this
+ connection's speed get checked again in a second */
+ Curl_expire(data, 1000, EXPIRE_SPEEDCHECK);
+
+ return CURLE_OK;
+}
diff --git a/libs/libcurl/src/speedcheck.h b/libs/libcurl/src/speedcheck.h index cb44eb04ec..82923d0d45 100644 --- a/libs/libcurl/src/speedcheck.h +++ b/libs/libcurl/src/speedcheck.h @@ -1,35 +1,35 @@ -#ifndef HEADER_CURL_SPEEDCHECK_H -#define HEADER_CURL_SPEEDCHECK_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "timeval.h" - -void Curl_speedinit(struct Curl_easy *data); -CURLcode Curl_speedcheck(struct Curl_easy *data, - struct curltime now); - -#endif /* HEADER_CURL_SPEEDCHECK_H */ +#ifndef HEADER_CURL_SPEEDCHECK_H
+#define HEADER_CURL_SPEEDCHECK_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "timeval.h"
+
+void Curl_speedinit(struct Curl_easy *data);
+CURLcode Curl_speedcheck(struct Curl_easy *data,
+ struct curltime now);
+
+#endif /* HEADER_CURL_SPEEDCHECK_H */
diff --git a/libs/libcurl/src/splay.c b/libs/libcurl/src/splay.c index 33b44aa1c6..e013f78b63 100644 --- a/libs/libcurl/src/splay.c +++ b/libs/libcurl/src/splay.c @@ -1,278 +1,278 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1997 - 2022, 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 "splay.h" - -/* - * This macro compares two node keys i and j and returns: - * - * negative value: when i is smaller than j - * zero : when i is equal to j - * positive when : when i is larger than j - */ -#define compare(i,j) Curl_splaycomparekeys((i),(j)) - -/* - * Splay using the key i (which may or may not be in the tree.) The starting - * root is t. - */ -struct Curl_tree *Curl_splay(struct curltime i, - struct Curl_tree *t) -{ - struct Curl_tree N, *l, *r, *y; - - if(!t) - return t; - N.smaller = N.larger = NULL; - l = r = &N; - - for(;;) { - long comp = compare(i, t->key); - if(comp < 0) { - if(!t->smaller) - break; - if(compare(i, t->smaller->key) < 0) { - y = t->smaller; /* rotate smaller */ - t->smaller = y->larger; - y->larger = t; - t = y; - if(!t->smaller) - break; - } - r->smaller = t; /* link smaller */ - r = t; - t = t->smaller; - } - else if(comp > 0) { - if(!t->larger) - break; - if(compare(i, t->larger->key) > 0) { - y = t->larger; /* rotate larger */ - t->larger = y->smaller; - y->smaller = t; - t = y; - if(!t->larger) - break; - } - l->larger = t; /* link larger */ - l = t; - t = t->larger; - } - else - break; - } - - l->larger = t->smaller; /* assemble */ - r->smaller = t->larger; - t->smaller = N.larger; - t->larger = N.smaller; - - return t; -} - -/* Insert key i into the tree t. Return a pointer to the resulting tree or - * NULL if something went wrong. - * - * @unittest: 1309 - */ -struct Curl_tree *Curl_splayinsert(struct curltime i, - struct Curl_tree *t, - struct Curl_tree *node) -{ - static const struct curltime KEY_NOTUSED = { - ~0, -1 - }; /* will *NEVER* appear */ - - if(!node) - return t; - - if(t) { - t = Curl_splay(i, t); - if(compare(i, t->key) == 0) { - /* There already exists a node in the tree with the very same key. Build - a doubly-linked circular list of nodes. We add the new 'node' struct - to the end of this list. */ - - node->key = KEY_NOTUSED; /* we set the key in the sub node to NOTUSED - to quickly identify this node as a subnode */ - node->samen = t; - node->samep = t->samep; - t->samep->samen = node; - t->samep = node; - - return t; /* the root node always stays the same */ - } - } - - if(!t) { - node->smaller = node->larger = NULL; - } - else if(compare(i, t->key) < 0) { - node->smaller = t->smaller; - node->larger = t; - t->smaller = NULL; - - } - else { - node->larger = t->larger; - node->smaller = t; - t->larger = NULL; - } - node->key = i; - - /* no identical nodes (yet), we are the only one in the list of nodes */ - node->samen = node; - node->samep = node; - return node; -} - -/* Finds and deletes the best-fit node from the tree. Return a pointer to the - resulting tree. best-fit means the smallest node if it is not larger than - the key */ -struct Curl_tree *Curl_splaygetbest(struct curltime i, - struct Curl_tree *t, - struct Curl_tree **removed) -{ - static const struct curltime tv_zero = {0, 0}; - struct Curl_tree *x; - - if(!t) { - *removed = NULL; /* none removed since there was no root */ - return NULL; - } - - /* find smallest */ - t = Curl_splay(tv_zero, t); - if(compare(i, t->key) < 0) { - /* even the smallest is too big */ - *removed = NULL; - return t; - } - - /* FIRST! Check if there is a list with identical keys */ - x = t->samen; - if(x != t) { - /* there is, pick one from the list */ - - /* 'x' is the new root node */ - - x->key = t->key; - x->larger = t->larger; - x->smaller = t->smaller; - x->samep = t->samep; - t->samep->samen = x; - - *removed = t; - return x; /* new root */ - } - - /* we splayed the tree to the smallest element, there is no smaller */ - x = t->larger; - *removed = t; - - return x; -} - - -/* Deletes the very node we point out from the tree if it's there. Stores a - * pointer to the new resulting tree in 'newroot'. - * - * Returns zero on success and non-zero on errors! - * When returning error, it does not touch the 'newroot' pointer. - * - * NOTE: when the last node of the tree is removed, there's no tree left so - * 'newroot' will be made to point to NULL. - * - * @unittest: 1309 - */ -int Curl_splayremove(struct Curl_tree *t, - struct Curl_tree *removenode, - struct Curl_tree **newroot) -{ - static const struct curltime KEY_NOTUSED = { - ~0, -1 - }; /* will *NEVER* appear */ - struct Curl_tree *x; - - if(!t || !removenode) - return 1; - - if(compare(KEY_NOTUSED, removenode->key) == 0) { - /* Key set to NOTUSED means it is a subnode within a 'same' linked list - and thus we can unlink it easily. */ - if(removenode->samen == removenode) - /* A non-subnode should never be set to KEY_NOTUSED */ - return 3; - - removenode->samep->samen = removenode->samen; - removenode->samen->samep = removenode->samep; - - /* Ensures that double-remove gets caught. */ - removenode->samen = removenode; - - *newroot = t; /* return the same root */ - return 0; - } - - t = Curl_splay(removenode->key, t); - - /* First make sure that we got the same root node as the one we want - to remove, as otherwise we might be trying to remove a node that - isn't actually in the tree. - - We cannot just compare the keys here as a double remove in quick - succession of a node with key != KEY_NOTUSED && same != NULL - could return the same key but a different node. */ - if(t != removenode) - return 2; - - /* Check if there is a list with identical sizes, as then we're trying to - remove the root node of a list of nodes with identical keys. */ - x = t->samen; - if(x != t) { - /* 'x' is the new root node, we just make it use the root node's - smaller/larger links */ - - x->key = t->key; - x->larger = t->larger; - x->smaller = t->smaller; - x->samep = t->samep; - t->samep->samen = x; - } - else { - /* Remove the root node */ - if(!t->smaller) - x = t->larger; - else { - x = Curl_splay(removenode->key, t->smaller); - x->larger = t->larger; - } - } - - *newroot = x; /* store new root pointer */ - - return 0; -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1997 - 2022, 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 "splay.h"
+
+/*
+ * This macro compares two node keys i and j and returns:
+ *
+ * negative value: when i is smaller than j
+ * zero : when i is equal to j
+ * positive when : when i is larger than j
+ */
+#define compare(i,j) Curl_splaycomparekeys((i),(j))
+
+/*
+ * Splay using the key i (which may or may not be in the tree.) The starting
+ * root is t.
+ */
+struct Curl_tree *Curl_splay(struct curltime i,
+ struct Curl_tree *t)
+{
+ struct Curl_tree N, *l, *r, *y;
+
+ if(!t)
+ return t;
+ N.smaller = N.larger = NULL;
+ l = r = &N;
+
+ for(;;) {
+ long comp = compare(i, t->key);
+ if(comp < 0) {
+ if(!t->smaller)
+ break;
+ if(compare(i, t->smaller->key) < 0) {
+ y = t->smaller; /* rotate smaller */
+ t->smaller = y->larger;
+ y->larger = t;
+ t = y;
+ if(!t->smaller)
+ break;
+ }
+ r->smaller = t; /* link smaller */
+ r = t;
+ t = t->smaller;
+ }
+ else if(comp > 0) {
+ if(!t->larger)
+ break;
+ if(compare(i, t->larger->key) > 0) {
+ y = t->larger; /* rotate larger */
+ t->larger = y->smaller;
+ y->smaller = t;
+ t = y;
+ if(!t->larger)
+ break;
+ }
+ l->larger = t; /* link larger */
+ l = t;
+ t = t->larger;
+ }
+ else
+ break;
+ }
+
+ l->larger = t->smaller; /* assemble */
+ r->smaller = t->larger;
+ t->smaller = N.larger;
+ t->larger = N.smaller;
+
+ return t;
+}
+
+/* Insert key i into the tree t. Return a pointer to the resulting tree or
+ * NULL if something went wrong.
+ *
+ * @unittest: 1309
+ */
+struct Curl_tree *Curl_splayinsert(struct curltime i,
+ struct Curl_tree *t,
+ struct Curl_tree *node)
+{
+ static const struct curltime KEY_NOTUSED = {
+ ~0, -1
+ }; /* will *NEVER* appear */
+
+ if(!node)
+ return t;
+
+ if(t) {
+ t = Curl_splay(i, t);
+ if(compare(i, t->key) == 0) {
+ /* There already exists a node in the tree with the very same key. Build
+ a doubly-linked circular list of nodes. We add the new 'node' struct
+ to the end of this list. */
+
+ node->key = KEY_NOTUSED; /* we set the key in the sub node to NOTUSED
+ to quickly identify this node as a subnode */
+ node->samen = t;
+ node->samep = t->samep;
+ t->samep->samen = node;
+ t->samep = node;
+
+ return t; /* the root node always stays the same */
+ }
+ }
+
+ if(!t) {
+ node->smaller = node->larger = NULL;
+ }
+ else if(compare(i, t->key) < 0) {
+ node->smaller = t->smaller;
+ node->larger = t;
+ t->smaller = NULL;
+
+ }
+ else {
+ node->larger = t->larger;
+ node->smaller = t;
+ t->larger = NULL;
+ }
+ node->key = i;
+
+ /* no identical nodes (yet), we are the only one in the list of nodes */
+ node->samen = node;
+ node->samep = node;
+ return node;
+}
+
+/* Finds and deletes the best-fit node from the tree. Return a pointer to the
+ resulting tree. best-fit means the smallest node if it is not larger than
+ the key */
+struct Curl_tree *Curl_splaygetbest(struct curltime i,
+ struct Curl_tree *t,
+ struct Curl_tree **removed)
+{
+ static const struct curltime tv_zero = {0, 0};
+ struct Curl_tree *x;
+
+ if(!t) {
+ *removed = NULL; /* none removed since there was no root */
+ return NULL;
+ }
+
+ /* find smallest */
+ t = Curl_splay(tv_zero, t);
+ if(compare(i, t->key) < 0) {
+ /* even the smallest is too big */
+ *removed = NULL;
+ return t;
+ }
+
+ /* FIRST! Check if there is a list with identical keys */
+ x = t->samen;
+ if(x != t) {
+ /* there is, pick one from the list */
+
+ /* 'x' is the new root node */
+
+ x->key = t->key;
+ x->larger = t->larger;
+ x->smaller = t->smaller;
+ x->samep = t->samep;
+ t->samep->samen = x;
+
+ *removed = t;
+ return x; /* new root */
+ }
+
+ /* we splayed the tree to the smallest element, there is no smaller */
+ x = t->larger;
+ *removed = t;
+
+ return x;
+}
+
+
+/* Deletes the very node we point out from the tree if it's there. Stores a
+ * pointer to the new resulting tree in 'newroot'.
+ *
+ * Returns zero on success and non-zero on errors!
+ * When returning error, it does not touch the 'newroot' pointer.
+ *
+ * NOTE: when the last node of the tree is removed, there's no tree left so
+ * 'newroot' will be made to point to NULL.
+ *
+ * @unittest: 1309
+ */
+int Curl_splayremove(struct Curl_tree *t,
+ struct Curl_tree *removenode,
+ struct Curl_tree **newroot)
+{
+ static const struct curltime KEY_NOTUSED = {
+ ~0, -1
+ }; /* will *NEVER* appear */
+ struct Curl_tree *x;
+
+ if(!t || !removenode)
+ return 1;
+
+ if(compare(KEY_NOTUSED, removenode->key) == 0) {
+ /* Key set to NOTUSED means it is a subnode within a 'same' linked list
+ and thus we can unlink it easily. */
+ if(removenode->samen == removenode)
+ /* A non-subnode should never be set to KEY_NOTUSED */
+ return 3;
+
+ removenode->samep->samen = removenode->samen;
+ removenode->samen->samep = removenode->samep;
+
+ /* Ensures that double-remove gets caught. */
+ removenode->samen = removenode;
+
+ *newroot = t; /* return the same root */
+ return 0;
+ }
+
+ t = Curl_splay(removenode->key, t);
+
+ /* First make sure that we got the same root node as the one we want
+ to remove, as otherwise we might be trying to remove a node that
+ isn't actually in the tree.
+
+ We cannot just compare the keys here as a double remove in quick
+ succession of a node with key != KEY_NOTUSED && same != NULL
+ could return the same key but a different node. */
+ if(t != removenode)
+ return 2;
+
+ /* Check if there is a list with identical sizes, as then we're trying to
+ remove the root node of a list of nodes with identical keys. */
+ x = t->samen;
+ if(x != t) {
+ /* 'x' is the new root node, we just make it use the root node's
+ smaller/larger links */
+
+ x->key = t->key;
+ x->larger = t->larger;
+ x->smaller = t->smaller;
+ x->samep = t->samep;
+ t->samep->samen = x;
+ }
+ else {
+ /* Remove the root node */
+ if(!t->smaller)
+ x = t->larger;
+ else {
+ x = Curl_splay(removenode->key, t->smaller);
+ x->larger = t->larger;
+ }
+ }
+
+ *newroot = x; /* store new root pointer */
+
+ return 0;
+}
diff --git a/libs/libcurl/src/splay.h b/libs/libcurl/src/splay.h index 015e2ca52f..f25306a04e 100644 --- a/libs/libcurl/src/splay.h +++ b/libs/libcurl/src/splay.h @@ -1,58 +1,58 @@ -#ifndef HEADER_CURL_SPLAY_H -#define HEADER_CURL_SPLAY_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1997 - 2022, 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 "timeval.h" - -struct Curl_tree { - struct Curl_tree *smaller; /* smaller node */ - struct Curl_tree *larger; /* larger node */ - struct Curl_tree *samen; /* points to the next node with identical key */ - struct Curl_tree *samep; /* points to the prev node with identical key */ - struct curltime key; /* this node's "sort" key */ - void *payload; /* data the splay code doesn't care about */ -}; - -struct Curl_tree *Curl_splay(struct curltime i, - struct Curl_tree *t); - -struct Curl_tree *Curl_splayinsert(struct curltime key, - struct Curl_tree *t, - struct Curl_tree *newnode); - -struct Curl_tree *Curl_splaygetbest(struct curltime key, - struct Curl_tree *t, - struct Curl_tree **removed); - -int Curl_splayremove(struct Curl_tree *t, - struct Curl_tree *removenode, - struct Curl_tree **newroot); - -#define Curl_splaycomparekeys(i,j) ( ((i.tv_sec) < (j.tv_sec)) ? -1 : \ - ( ((i.tv_sec) > (j.tv_sec)) ? 1 : \ - ( ((i.tv_usec) < (j.tv_usec)) ? -1 : \ - ( ((i.tv_usec) > (j.tv_usec)) ? 1 : 0)))) - -#endif /* HEADER_CURL_SPLAY_H */ +#ifndef HEADER_CURL_SPLAY_H
+#define HEADER_CURL_SPLAY_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1997 - 2022, 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 "timeval.h"
+
+struct Curl_tree {
+ struct Curl_tree *smaller; /* smaller node */
+ struct Curl_tree *larger; /* larger node */
+ struct Curl_tree *samen; /* points to the next node with identical key */
+ struct Curl_tree *samep; /* points to the prev node with identical key */
+ struct curltime key; /* this node's "sort" key */
+ void *payload; /* data the splay code doesn't care about */
+};
+
+struct Curl_tree *Curl_splay(struct curltime i,
+ struct Curl_tree *t);
+
+struct Curl_tree *Curl_splayinsert(struct curltime key,
+ struct Curl_tree *t,
+ struct Curl_tree *newnode);
+
+struct Curl_tree *Curl_splaygetbest(struct curltime key,
+ struct Curl_tree *t,
+ struct Curl_tree **removed);
+
+int Curl_splayremove(struct Curl_tree *t,
+ struct Curl_tree *removenode,
+ struct Curl_tree **newroot);
+
+#define Curl_splaycomparekeys(i,j) ( ((i.tv_sec) < (j.tv_sec)) ? -1 : \
+ ( ((i.tv_sec) > (j.tv_sec)) ? 1 : \
+ ( ((i.tv_usec) < (j.tv_usec)) ? -1 : \
+ ( ((i.tv_usec) > (j.tv_usec)) ? 1 : 0))))
+
+#endif /* HEADER_CURL_SPLAY_H */
diff --git a/libs/libcurl/src/strcase.c b/libs/libcurl/src/strcase.c index 09d2a8a961..12c497dfd7 100644 --- a/libs/libcurl/src/strcase.c +++ b/libs/libcurl/src/strcase.c @@ -1,211 +1,204 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "strcase.h" - -/* Mapping table to go from lowercase to uppercase for plain ASCII.*/ -static const unsigned char touppermap[256] = { -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, 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, 52, 53, 54, 55, 56, 57, 58, 59, -60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, -79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 65, -66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, -85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, -150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, -166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, -182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, -198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, -214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, -230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, -246, 247, 248, 249, 250, 251, 252, 253, 254, 255 -}; - -/* Mapping table to go from uppercase to lowercase for plain ASCII.*/ -static const unsigned char tolowermap[256] = { -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, 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, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, -111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, -96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, -112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, -128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, -144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, -160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, -176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, -192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, -208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, -224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, -240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 -}; - - -/* Portable, consistent toupper. Do not use toupper() because its behavior is - altered by the current locale. */ -char Curl_raw_toupper(char in) -{ - return touppermap[(unsigned char) in]; -} - - -/* Portable, consistent tolower. Do not use tolower() because its behavior is - altered by the current locale. */ -char Curl_raw_tolower(char in) -{ - return tolowermap[(unsigned char) in]; -} - -/* - * Curl_strcasecompare() is for doing "raw" case insensitive strings. This is - * meant to be locale independent and only compare strings we know are safe - * for this. See - * https://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for some - * further explanation to why this function is necessary. - * - * @unittest: 1301 - */ - -int Curl_strcasecompare(const char *first, const char *second) -{ - while(*first && *second) { - if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second)) - /* get out of the loop as soon as they don't match */ - return 0; - first++; - second++; - } - /* If we're here either the strings are the same or the length is different. - We can just test if the "current" character is non-zero for one and zero - for the other. Note that the characters may not be exactly the same even - if they match, we only want to compare zero-ness. */ - return !*first == !*second; -} - -int Curl_safe_strcasecompare(const char *first, const char *second) -{ - if(first && second) - /* both pointers point to something then compare them */ - return Curl_strcasecompare(first, second); - - /* if both pointers are NULL then treat them as equal */ - return (NULL == first && NULL == second); -} - -/* - * @unittest: 1301 - */ -int Curl_strncasecompare(const char *first, const char *second, size_t max) -{ - while(*first && *second && max) { - if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second)) { - break; - } - max--; - first++; - second++; - } - if(0 == max) - return 1; /* they are equal this far */ - - return Curl_raw_toupper(*first) == Curl_raw_toupper(*second); -} - -/* Copy an upper case version of the string from src to dest. The - * strings may overlap. No more than n characters of the string are copied - * (including any NUL) and the destination string will NOT be - * NUL-terminated if that limit is reached. - */ -void Curl_strntoupper(char *dest, const char *src, size_t n) -{ - if(n < 1) - return; - - do { - *dest++ = Curl_raw_toupper(*src); - } while(*src++ && --n); -} - -/* Copy a lower case version of the string from src to dest. The - * strings may overlap. No more than n characters of the string are copied - * (including any NUL) and the destination string will NOT be - * NUL-terminated if that limit is reached. - */ -void Curl_strntolower(char *dest, const char *src, size_t n) -{ - if(n < 1) - return; - - do { - *dest++ = Curl_raw_tolower(*src); - } while(*src++ && --n); -} - -/* Compare case-sensitive NUL-terminated strings, taking care of possible - * null pointers. Return true if arguments match. - */ -bool Curl_safecmp(char *a, char *b) -{ - if(a && b) - return !strcmp(a, b); - return !a && !b; -} - -/* - * Curl_timestrcmp() returns 0 if the two strings are identical. The time this - * function spends is a function of the shortest string, not of the contents. - */ -int Curl_timestrcmp(const char *a, const char *b) -{ - int match = 0; - int i = 0; - - if(a && b) { - while(1) { - match |= a[i]^b[i]; - if(!a[i] || !b[i]) - break; - i++; - } - } - else - return a || b; - return match; -} - -/* --- public functions --- */ - -int curl_strequal(const char *first, const char *second) -{ - return Curl_strcasecompare(first, second); -} -int curl_strnequal(const char *first, const char *second, size_t max) -{ - return Curl_strncasecompare(first, second, max); -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "strcase.h"
+
+/* Mapping table to go from lowercase to uppercase for plain ASCII.*/
+static const unsigned char touppermap[256] = {
+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, 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, 52, 53, 54, 55, 56, 57, 58, 59,
+60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
+79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 65,
+66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
+85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
+134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165,
+166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181,
+182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,
+198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213,
+214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229,
+230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
+246, 247, 248, 249, 250, 251, 252, 253, 254, 255
+};
+
+/* Mapping table to go from uppercase to lowercase for plain ASCII.*/
+static const unsigned char tolowermap[256] = {
+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, 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, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
+111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95,
+96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
+144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
+176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
+192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
+208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
+224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
+};
+
+
+/* Portable, consistent toupper. Do not use toupper() because its behavior is
+ altered by the current locale. */
+char Curl_raw_toupper(char in)
+{
+ return touppermap[(unsigned char) in];
+}
+
+
+/* Portable, consistent tolower. Do not use tolower() because its behavior is
+ altered by the current locale. */
+char Curl_raw_tolower(char in)
+{
+ return tolowermap[(unsigned char) in];
+}
+
+/*
+ * curl_strequal() is for doing "raw" case insensitive strings. This is meant
+ * to be locale independent and only compare strings we know are safe for
+ * this. See https://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for
+ * further explanations as to why this function is necessary.
+ */
+
+static int casecompare(const char *first, const char *second)
+{
+ while(*first && *second) {
+ if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second))
+ /* get out of the loop as soon as they don't match */
+ return 0;
+ first++;
+ second++;
+ }
+ /* If we're here either the strings are the same or the length is different.
+ We can just test if the "current" character is non-zero for one and zero
+ for the other. Note that the characters may not be exactly the same even
+ if they match, we only want to compare zero-ness. */
+ return !*first == !*second;
+}
+
+/* --- public function --- */
+int curl_strequal(const char *first, const char *second)
+{
+ if(first && second)
+ /* both pointers point to something then compare them */
+ return casecompare(first, second);
+
+ /* if both pointers are NULL then treat them as equal */
+ return (NULL == first && NULL == second);
+}
+
+static int ncasecompare(const char *first, const char *second, size_t max)
+{
+ while(*first && *second && max) {
+ if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second))
+ return 0;
+ max--;
+ first++;
+ second++;
+ }
+ if(0 == max)
+ return 1; /* they are equal this far */
+
+ return Curl_raw_toupper(*first) == Curl_raw_toupper(*second);
+}
+
+/* --- public function --- */
+int curl_strnequal(const char *first, const char *second, size_t max)
+{
+ if(first && second)
+ /* both pointers point to something then compare them */
+ return ncasecompare(first, second, max);
+
+ /* if both pointers are NULL then treat them as equal if max is non-zero */
+ return (NULL == first && NULL == second && max);
+}
+/* Copy an upper case version of the string from src to dest. The
+ * strings may overlap. No more than n characters of the string are copied
+ * (including any NUL) and the destination string will NOT be
+ * NUL-terminated if that limit is reached.
+ */
+void Curl_strntoupper(char *dest, const char *src, size_t n)
+{
+ if(n < 1)
+ return;
+
+ do {
+ *dest++ = Curl_raw_toupper(*src);
+ } while(*src++ && --n);
+}
+
+/* Copy a lower case version of the string from src to dest. The
+ * strings may overlap. No more than n characters of the string are copied
+ * (including any NUL) and the destination string will NOT be
+ * NUL-terminated if that limit is reached.
+ */
+void Curl_strntolower(char *dest, const char *src, size_t n)
+{
+ if(n < 1)
+ return;
+
+ do {
+ *dest++ = Curl_raw_tolower(*src);
+ } while(*src++ && --n);
+}
+
+/* Compare case-sensitive NUL-terminated strings, taking care of possible
+ * null pointers. Return true if arguments match.
+ */
+bool Curl_safecmp(char *a, char *b)
+{
+ if(a && b)
+ return !strcmp(a, b);
+ return !a && !b;
+}
+
+/*
+ * Curl_timestrcmp() returns 0 if the two strings are identical. The time this
+ * function spends is a function of the shortest string, not of the contents.
+ */
+int Curl_timestrcmp(const char *a, const char *b)
+{
+ int match = 0;
+ int i = 0;
+
+ if(a && b) {
+ while(1) {
+ match |= a[i]^b[i];
+ if(!a[i] || !b[i])
+ break;
+ i++;
+ }
+ }
+ else
+ return a || b;
+ return match;
+}
diff --git a/libs/libcurl/src/strcase.h b/libs/libcurl/src/strcase.h index 65a575385d..e59e5ff44f 100644 --- a/libs/libcurl/src/strcase.h +++ b/libs/libcurl/src/strcase.h @@ -1,58 +1,54 @@ -#ifndef HEADER_CURL_STRCASE_H -#define HEADER_CURL_STRCASE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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> - -/* - * Only "raw" case insensitive strings. This is meant to be locale independent - * and only compare strings we know are safe for this. - * - * The function is capable of comparing a-z case insensitively. - * - * Result is 1 if text matches and 0 if not. - */ - -#define strcasecompare(a,b) Curl_strcasecompare(a,b) -#define strncasecompare(a,b,c) Curl_strncasecompare(a,b,c) - -int Curl_strcasecompare(const char *first, const char *second); -int Curl_safe_strcasecompare(const char *first, const char *second); -int Curl_strncasecompare(const char *first, const char *second, size_t max); - -char Curl_raw_toupper(char in); -char Curl_raw_tolower(char in); - -/* checkprefix() is a shorter version of the above, used when the first - argument is the string literal */ -#define checkprefix(a,b) curl_strnequal(b, STRCONST(a)) - -void Curl_strntoupper(char *dest, const char *src, size_t n); -void Curl_strntolower(char *dest, const char *src, size_t n); - -bool Curl_safecmp(char *a, char *b); -int Curl_timestrcmp(const char *first, const char *second); - -#endif /* HEADER_CURL_STRCASE_H */ +#ifndef HEADER_CURL_STRCASE_H
+#define HEADER_CURL_STRCASE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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>
+
+/*
+ * Only "raw" case insensitive strings. This is meant to be locale independent
+ * and only compare strings we know are safe for this.
+ *
+ * The function is capable of comparing a-z case insensitively.
+ *
+ * Result is 1 if text matches and 0 if not.
+ */
+
+#define strcasecompare(a,b) curl_strequal(a,b)
+#define strncasecompare(a,b,c) curl_strnequal(a,b,c)
+
+char Curl_raw_toupper(char in);
+char Curl_raw_tolower(char in);
+
+/* checkprefix() is a shorter version of the above, used when the first
+ argument is the string literal */
+#define checkprefix(a,b) curl_strnequal(b, STRCONST(a))
+
+void Curl_strntoupper(char *dest, const char *src, size_t n);
+void Curl_strntolower(char *dest, const char *src, size_t n);
+
+bool Curl_safecmp(char *a, char *b);
+int Curl_timestrcmp(const char *first, const char *second);
+
+#endif /* HEADER_CURL_STRCASE_H */
diff --git a/libs/libcurl/src/strdup.c b/libs/libcurl/src/strdup.c index ac22b6ddaf..f4a47d9ccd 100644 --- a/libs/libcurl/src/strdup.c +++ b/libs/libcurl/src/strdup.c @@ -1,123 +1,123 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 WIN32 -#include <wchar.h> -#endif - -#include "strdup.h" -#include "curl_memory.h" - -/* The last #include file should be: */ -#include "memdebug.h" - -#ifndef HAVE_STRDUP -char *curlx_strdup(const char *str) -{ - size_t len; - char *newstr; - - if(!str) - return (char *)NULL; - - len = strlen(str) + 1; - - newstr = malloc(len); - if(!newstr) - return (char *)NULL; - - memcpy(newstr, str, len); - return newstr; -} -#endif - -#ifdef WIN32 -/*************************************************************************** - * - * Curl_wcsdup(source) - * - * Copies the 'source' wchar string to a newly allocated buffer (that is - * returned). - * - * Returns the new pointer or NULL on failure. - * - ***************************************************************************/ -wchar_t *Curl_wcsdup(const wchar_t *src) -{ - size_t length = wcslen(src); - - if(length > (SIZE_T_MAX / sizeof(wchar_t)) - 1) - return (wchar_t *)NULL; /* integer overflow */ - - return (wchar_t *)Curl_memdup(src, (length + 1) * sizeof(wchar_t)); -} -#endif - -/*************************************************************************** - * - * Curl_memdup(source, length) - * - * Copies the 'source' data to a newly allocated buffer (that is - * returned). Copies 'length' bytes. - * - * Returns the new pointer or NULL on failure. - * - ***************************************************************************/ -void *Curl_memdup(const void *src, size_t length) -{ - void *buffer = malloc(length); - if(!buffer) - return NULL; /* fail */ - - memcpy(buffer, src, length); - - return buffer; -} - -/*************************************************************************** - * - * Curl_saferealloc(ptr, size) - * - * Does a normal realloc(), but will free the data pointer if the realloc - * fails. If 'size' is non-zero, it will free the data and return a failure. - * - * This convenience function is provided and used to help us avoid a common - * mistake pattern when we could pass in a zero, catch the NULL return and end - * up free'ing the memory twice. - * - * Returns the new pointer or NULL on failure. - * - ***************************************************************************/ -void *Curl_saferealloc(void *ptr, size_t size) -{ - void *datap = realloc(ptr, size); - if(size && !datap) - /* only free 'ptr' if size was non-zero */ - free(ptr); - return datap; -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 WIN32
+#include <wchar.h>
+#endif
+
+#include "strdup.h"
+#include "curl_memory.h"
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+#ifndef HAVE_STRDUP
+char *curlx_strdup(const char *str)
+{
+ size_t len;
+ char *newstr;
+
+ if(!str)
+ return (char *)NULL;
+
+ len = strlen(str) + 1;
+
+ newstr = malloc(len);
+ if(!newstr)
+ return (char *)NULL;
+
+ memcpy(newstr, str, len);
+ return newstr;
+}
+#endif
+
+#ifdef WIN32
+/***************************************************************************
+ *
+ * Curl_wcsdup(source)
+ *
+ * Copies the 'source' wchar string to a newly allocated buffer (that is
+ * returned).
+ *
+ * Returns the new pointer or NULL on failure.
+ *
+ ***************************************************************************/
+wchar_t *Curl_wcsdup(const wchar_t *src)
+{
+ size_t length = wcslen(src);
+
+ if(length > (SIZE_T_MAX / sizeof(wchar_t)) - 1)
+ return (wchar_t *)NULL; /* integer overflow */
+
+ return (wchar_t *)Curl_memdup(src, (length + 1) * sizeof(wchar_t));
+}
+#endif
+
+/***************************************************************************
+ *
+ * Curl_memdup(source, length)
+ *
+ * Copies the 'source' data to a newly allocated buffer (that is
+ * returned). Copies 'length' bytes.
+ *
+ * Returns the new pointer or NULL on failure.
+ *
+ ***************************************************************************/
+void *Curl_memdup(const void *src, size_t length)
+{
+ void *buffer = malloc(length);
+ if(!buffer)
+ return NULL; /* fail */
+
+ memcpy(buffer, src, length);
+
+ return buffer;
+}
+
+/***************************************************************************
+ *
+ * Curl_saferealloc(ptr, size)
+ *
+ * Does a normal realloc(), but will free the data pointer if the realloc
+ * fails. If 'size' is non-zero, it will free the data and return a failure.
+ *
+ * This convenience function is provided and used to help us avoid a common
+ * mistake pattern when we could pass in a zero, catch the NULL return and end
+ * up free'ing the memory twice.
+ *
+ * Returns the new pointer or NULL on failure.
+ *
+ ***************************************************************************/
+void *Curl_saferealloc(void *ptr, size_t size)
+{
+ void *datap = realloc(ptr, size);
+ if(size && !datap)
+ /* only free 'ptr' if size was non-zero */
+ free(ptr);
+ return datap;
+}
diff --git a/libs/libcurl/src/strdup.h b/libs/libcurl/src/strdup.h index fb46808b83..8687ec481c 100644 --- a/libs/libcurl/src/strdup.h +++ b/libs/libcurl/src/strdup.h @@ -1,37 +1,37 @@ -#ifndef HEADER_CURL_STRDUP_H -#define HEADER_CURL_STRDUP_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 HAVE_STRDUP -extern char *curlx_strdup(const char *str); -#endif -#ifdef WIN32 -wchar_t* Curl_wcsdup(const wchar_t* src); -#endif -void *Curl_memdup(const void *src, size_t buffer_length); -void *Curl_saferealloc(void *ptr, size_t size); - -#endif /* HEADER_CURL_STRDUP_H */ +#ifndef HEADER_CURL_STRDUP_H
+#define HEADER_CURL_STRDUP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 HAVE_STRDUP
+extern char *curlx_strdup(const char *str);
+#endif
+#ifdef WIN32
+wchar_t* Curl_wcsdup(const wchar_t* src);
+#endif
+void *Curl_memdup(const void *src, size_t buffer_length);
+void *Curl_saferealloc(void *ptr, size_t size);
+
+#endif /* HEADER_CURL_STRDUP_H */
diff --git a/libs/libcurl/src/strerror.c b/libs/libcurl/src/strerror.c index b9a51e26b9..dc7c312fc0 100644 --- a/libs/libcurl/src/strerror.c +++ b/libs/libcurl/src/strerror.c @@ -1,1109 +1,1109 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2004 - 2022, 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_STRERROR_R -# if (!defined(HAVE_POSIX_STRERROR_R) && \ - !defined(HAVE_GLIBC_STRERROR_R)) || \ - (defined(HAVE_POSIX_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R)) -# error "strerror_r MUST be either POSIX, glibc style" -# endif -#endif - -#include <curl/curl.h> - -#ifdef USE_LIBIDN2 -#include <idn2.h> -#endif - -#ifdef USE_WINDOWS_SSPI -#include "curl_sspi.h" -#endif - -#include "strerror.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#if defined(WIN32) || defined(_WIN32_WCE) -#define PRESERVE_WINDOWS_ERROR_CODE -#endif - -const char * -curl_easy_strerror(CURLcode error) -{ -#ifndef CURL_DISABLE_VERBOSE_STRINGS - switch(error) { - case CURLE_OK: - return "No error"; - - case CURLE_UNSUPPORTED_PROTOCOL: - return "Unsupported protocol"; - - case CURLE_FAILED_INIT: - return "Failed initialization"; - - case CURLE_URL_MALFORMAT: - return "URL using bad/illegal format or missing URL"; - - case CURLE_NOT_BUILT_IN: - return "A requested feature, protocol or option was not found built-in in" - " this libcurl due to a build-time decision."; - - case CURLE_COULDNT_RESOLVE_PROXY: - return "Couldn't resolve proxy name"; - - case CURLE_COULDNT_RESOLVE_HOST: - return "Couldn't resolve host name"; - - case CURLE_COULDNT_CONNECT: - return "Couldn't connect to server"; - - case CURLE_WEIRD_SERVER_REPLY: - return "Weird server reply"; - - case CURLE_REMOTE_ACCESS_DENIED: - return "Access denied to remote resource"; - - case CURLE_FTP_ACCEPT_FAILED: - return "FTP: The server failed to connect to data port"; - - case CURLE_FTP_ACCEPT_TIMEOUT: - return "FTP: Accepting server connect has timed out"; - - case CURLE_FTP_PRET_FAILED: - return "FTP: The server did not accept the PRET command."; - - case CURLE_FTP_WEIRD_PASS_REPLY: - return "FTP: unknown PASS reply"; - - case CURLE_FTP_WEIRD_PASV_REPLY: - return "FTP: unknown PASV reply"; - - case CURLE_FTP_WEIRD_227_FORMAT: - return "FTP: unknown 227 response format"; - - case CURLE_FTP_CANT_GET_HOST: - return "FTP: can't figure out the host in the PASV response"; - - case CURLE_HTTP2: - return "Error in the HTTP2 framing layer"; - - case CURLE_FTP_COULDNT_SET_TYPE: - return "FTP: couldn't set file type"; - - case CURLE_PARTIAL_FILE: - return "Transferred a partial file"; - - case CURLE_FTP_COULDNT_RETR_FILE: - return "FTP: couldn't retrieve (RETR failed) the specified file"; - - case CURLE_QUOTE_ERROR: - return "Quote command returned error"; - - case CURLE_HTTP_RETURNED_ERROR: - return "HTTP response code said error"; - - case CURLE_WRITE_ERROR: - return "Failed writing received data to disk/application"; - - case CURLE_UPLOAD_FAILED: - return "Upload failed (at start/before it took off)"; - - case CURLE_READ_ERROR: - return "Failed to open/read local data from file/application"; - - case CURLE_OUT_OF_MEMORY: - return "Out of memory"; - - case CURLE_OPERATION_TIMEDOUT: - return "Timeout was reached"; - - case CURLE_FTP_PORT_FAILED: - return "FTP: command PORT failed"; - - case CURLE_FTP_COULDNT_USE_REST: - return "FTP: command REST failed"; - - case CURLE_RANGE_ERROR: - return "Requested range was not delivered by the server"; - - case CURLE_HTTP_POST_ERROR: - return "Internal problem setting up the POST"; - - case CURLE_SSL_CONNECT_ERROR: - return "SSL connect error"; - - case CURLE_BAD_DOWNLOAD_RESUME: - return "Couldn't resume download"; - - case CURLE_FILE_COULDNT_READ_FILE: - return "Couldn't read a file:// file"; - - case CURLE_LDAP_CANNOT_BIND: - return "LDAP: cannot bind"; - - case CURLE_LDAP_SEARCH_FAILED: - return "LDAP: search failed"; - - case CURLE_FUNCTION_NOT_FOUND: - return "A required function in the library was not found"; - - case CURLE_ABORTED_BY_CALLBACK: - return "Operation was aborted by an application callback"; - - case CURLE_BAD_FUNCTION_ARGUMENT: - return "A libcurl function was given a bad argument"; - - case CURLE_INTERFACE_FAILED: - return "Failed binding local connection end"; - - case CURLE_TOO_MANY_REDIRECTS : - return "Number of redirects hit maximum amount"; - - case CURLE_UNKNOWN_OPTION: - return "An unknown option was passed in to libcurl"; - - case CURLE_SETOPT_OPTION_SYNTAX : - return "Malformed option provided in a setopt"; - - case CURLE_GOT_NOTHING: - return "Server returned nothing (no headers, no data)"; - - case CURLE_SSL_ENGINE_NOTFOUND: - return "SSL crypto engine not found"; - - case CURLE_SSL_ENGINE_SETFAILED: - return "Can not set SSL crypto engine as default"; - - case CURLE_SSL_ENGINE_INITFAILED: - return "Failed to initialise SSL crypto engine"; - - case CURLE_SEND_ERROR: - return "Failed sending data to the peer"; - - case CURLE_RECV_ERROR: - return "Failure when receiving data from the peer"; - - case CURLE_SSL_CERTPROBLEM: - return "Problem with the local SSL certificate"; - - case CURLE_SSL_CIPHER: - return "Couldn't use specified SSL cipher"; - - case CURLE_PEER_FAILED_VERIFICATION: - return "SSL peer certificate or SSH remote key was not OK"; - - case CURLE_SSL_CACERT_BADFILE: - return "Problem with the SSL CA cert (path? access rights?)"; - - case CURLE_BAD_CONTENT_ENCODING: - return "Unrecognized or bad HTTP Content or Transfer-Encoding"; - - case CURLE_FILESIZE_EXCEEDED: - return "Maximum file size exceeded"; - - case CURLE_USE_SSL_FAILED: - return "Requested SSL level failed"; - - case CURLE_SSL_SHUTDOWN_FAILED: - return "Failed to shut down the SSL connection"; - - case CURLE_SSL_CRL_BADFILE: - return "Failed to load CRL file (path? access rights?, format?)"; - - case CURLE_SSL_ISSUER_ERROR: - return "Issuer check against peer certificate failed"; - - case CURLE_SEND_FAIL_REWIND: - return "Send failed since rewinding of the data stream failed"; - - case CURLE_LOGIN_DENIED: - return "Login denied"; - - case CURLE_TFTP_NOTFOUND: - return "TFTP: File Not Found"; - - case CURLE_TFTP_PERM: - return "TFTP: Access Violation"; - - case CURLE_REMOTE_DISK_FULL: - return "Disk full or allocation exceeded"; - - case CURLE_TFTP_ILLEGAL: - return "TFTP: Illegal operation"; - - case CURLE_TFTP_UNKNOWNID: - return "TFTP: Unknown transfer ID"; - - case CURLE_REMOTE_FILE_EXISTS: - return "Remote file already exists"; - - case CURLE_TFTP_NOSUCHUSER: - return "TFTP: No such user"; - - case CURLE_REMOTE_FILE_NOT_FOUND: - return "Remote file not found"; - - case CURLE_SSH: - return "Error in the SSH layer"; - - case CURLE_AGAIN: - return "Socket not ready for send/recv"; - - case CURLE_RTSP_CSEQ_ERROR: - return "RTSP CSeq mismatch or invalid CSeq"; - - case CURLE_RTSP_SESSION_ERROR: - return "RTSP session error"; - - case CURLE_FTP_BAD_FILE_LIST: - return "Unable to parse FTP file list"; - - case CURLE_CHUNK_FAILED: - return "Chunk callback failed"; - - case CURLE_NO_CONNECTION_AVAILABLE: - return "The max connection limit is reached"; - - case CURLE_SSL_PINNEDPUBKEYNOTMATCH: - return "SSL public key does not match pinned public key"; - - case CURLE_SSL_INVALIDCERTSTATUS: - return "SSL server certificate status verification FAILED"; - - case CURLE_HTTP2_STREAM: - return "Stream error in the HTTP/2 framing layer"; - - case CURLE_RECURSIVE_API_CALL: - return "API function called from within callback"; - - case CURLE_AUTH_ERROR: - return "An authentication function returned an error"; - - case CURLE_HTTP3: - return "HTTP/3 error"; - - case CURLE_QUIC_CONNECT_ERROR: - return "QUIC connection error"; - - case CURLE_PROXY: - return "proxy handshake error"; - - case CURLE_SSL_CLIENTCERT: - return "SSL Client Certificate required"; - - case CURLE_UNRECOVERABLE_POLL: - return "Unrecoverable error in select/poll"; - - /* error codes not used by current libcurl */ - case CURLE_OBSOLETE20: - case CURLE_OBSOLETE24: - case CURLE_OBSOLETE29: - case CURLE_OBSOLETE32: - case CURLE_OBSOLETE40: - case CURLE_OBSOLETE44: - case CURLE_OBSOLETE46: - case CURLE_OBSOLETE50: - case CURLE_OBSOLETE51: - case CURLE_OBSOLETE57: - case CURLE_OBSOLETE62: - case CURLE_OBSOLETE75: - case CURLE_OBSOLETE76: - case CURL_LAST: - break; - } - /* - * By using a switch, gcc -Wall will complain about enum values - * which do not appear, helping keep this function up-to-date. - * By using gcc -Wall -Werror, you can't forget. - * - * A table would not have the same benefit. Most compilers will - * generate code very similar to a table in any case, so there - * is little performance gain from a table. And something is broken - * for the user's application, anyways, so does it matter how fast - * it _doesn't_ work? - * - * The line number for the error will be near this comment, which - * is why it is here, and not at the start of the switch. - */ - return "Unknown error"; -#else - if(!error) - return "No error"; - else - return "Error"; -#endif -} - -const char * -curl_multi_strerror(CURLMcode error) -{ -#ifndef CURL_DISABLE_VERBOSE_STRINGS - switch(error) { - case CURLM_CALL_MULTI_PERFORM: - return "Please call curl_multi_perform() soon"; - - case CURLM_OK: - return "No error"; - - case CURLM_BAD_HANDLE: - return "Invalid multi handle"; - - case CURLM_BAD_EASY_HANDLE: - return "Invalid easy handle"; - - case CURLM_OUT_OF_MEMORY: - return "Out of memory"; - - case CURLM_INTERNAL_ERROR: - return "Internal error"; - - case CURLM_BAD_SOCKET: - return "Invalid socket argument"; - - case CURLM_UNKNOWN_OPTION: - return "Unknown option"; - - case CURLM_ADDED_ALREADY: - return "The easy handle is already added to a multi handle"; - - case CURLM_RECURSIVE_API_CALL: - return "API function called from within callback"; - - case CURLM_WAKEUP_FAILURE: - return "Wakeup is unavailable or failed"; - - case CURLM_BAD_FUNCTION_ARGUMENT: - return "A libcurl function was given a bad argument"; - - case CURLM_ABORTED_BY_CALLBACK: - return "Operation was aborted by an application callback"; - - case CURLM_UNRECOVERABLE_POLL: - return "Unrecoverable error in select/poll"; - - case CURLM_LAST: - break; - } - - return "Unknown error"; -#else - if(error == CURLM_OK) - return "No error"; - else - return "Error"; -#endif -} - -const char * -curl_share_strerror(CURLSHcode error) -{ -#ifndef CURL_DISABLE_VERBOSE_STRINGS - switch(error) { - case CURLSHE_OK: - return "No error"; - - case CURLSHE_BAD_OPTION: - return "Unknown share option"; - - case CURLSHE_IN_USE: - return "Share currently in use"; - - case CURLSHE_INVALID: - return "Invalid share handle"; - - case CURLSHE_NOMEM: - return "Out of memory"; - - case CURLSHE_NOT_BUILT_IN: - return "Feature not enabled in this library"; - - case CURLSHE_LAST: - break; - } - - return "CURLSHcode unknown"; -#else - if(error == CURLSHE_OK) - return "No error"; - else - return "Error"; -#endif -} - -const char * -curl_url_strerror(CURLUcode error) -{ -#ifndef CURL_DISABLE_VERBOSE_STRINGS - switch(error) { - case CURLUE_OK: - return "No error"; - - case CURLUE_BAD_HANDLE: - return "An invalid CURLU pointer was passed as argument"; - - case CURLUE_BAD_PARTPOINTER: - return "An invalid 'part' argument was passed as argument"; - - case CURLUE_MALFORMED_INPUT: - return "Malformed input to a URL function"; - - case CURLUE_BAD_PORT_NUMBER: - return "Port number was not a decimal number between 0 and 65535"; - - case CURLUE_UNSUPPORTED_SCHEME: - return "Unsupported URL scheme"; - - case CURLUE_URLDECODE: - return "URL decode error, most likely because of rubbish in the input"; - - case CURLUE_OUT_OF_MEMORY: - return "A memory function failed"; - - case CURLUE_USER_NOT_ALLOWED: - return "Credentials was passed in the URL when prohibited"; - - case CURLUE_UNKNOWN_PART: - return "An unknown part ID was passed to a URL API function"; - - case CURLUE_NO_SCHEME: - return "No scheme part in the URL"; - - case CURLUE_NO_USER: - return "No user part in the URL"; - - case CURLUE_NO_PASSWORD: - return "No password part in the URL"; - - case CURLUE_NO_OPTIONS: - return "No options part in the URL"; - - case CURLUE_NO_HOST: - return "No host part in the URL"; - - case CURLUE_NO_PORT: - return "No port part in the URL"; - - case CURLUE_NO_QUERY: - return "No query part in the URL"; - - case CURLUE_NO_FRAGMENT: - return "No fragment part in the URL"; - - case CURLUE_NO_ZONEID: - return "No zoneid part in the URL"; - - case CURLUE_BAD_LOGIN: - return "Bad login part"; - - case CURLUE_BAD_IPV6: - return "Bad IPv6 address"; - - case CURLUE_BAD_HOSTNAME: - return "Bad hostname"; - - case CURLUE_BAD_FILE_URL: - return "Bad file:// URL"; - - case CURLUE_BAD_SLASHES: - return "Unsupported number of slashes following scheme"; - - case CURLUE_BAD_SCHEME: - return "Bad scheme"; - - case CURLUE_BAD_PATH: - return "Bad path"; - - case CURLUE_BAD_FRAGMENT: - return "Bad fragment"; - - case CURLUE_BAD_QUERY: - return "Bad query"; - - case CURLUE_BAD_PASSWORD: - return "Bad password"; - - case CURLUE_BAD_USER: - return "Bad user"; - - case CURLUE_LAST: - break; - } - - return "CURLUcode unknown"; -#else - if(error == CURLUE_OK) - return "No error"; - else - return "Error"; -#endif -} - -#ifdef USE_WINSOCK -/* This is a helper function for Curl_strerror that converts Winsock error - * codes (WSAGetLastError) to error messages. - * Returns NULL if no error message was found for error code. - */ -static const char * -get_winsock_error (int err, char *buf, size_t len) -{ -#ifndef CURL_DISABLE_VERBOSE_STRINGS - const char *p; -#endif - - if(!len) - return NULL; - - *buf = '\0'; - -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)err; - return NULL; -#else - switch(err) { - case WSAEINTR: - p = "Call interrupted"; - break; - case WSAEBADF: - p = "Bad file"; - break; - case WSAEACCES: - p = "Bad access"; - break; - case WSAEFAULT: - p = "Bad argument"; - break; - case WSAEINVAL: - p = "Invalid arguments"; - break; - case WSAEMFILE: - p = "Out of file descriptors"; - break; - case WSAEWOULDBLOCK: - p = "Call would block"; - break; - case WSAEINPROGRESS: - case WSAEALREADY: - p = "Blocking call in progress"; - break; - case WSAENOTSOCK: - p = "Descriptor is not a socket"; - break; - case WSAEDESTADDRREQ: - p = "Need destination address"; - break; - case WSAEMSGSIZE: - p = "Bad message size"; - break; - case WSAEPROTOTYPE: - p = "Bad protocol"; - break; - case WSAENOPROTOOPT: - p = "Protocol option is unsupported"; - break; - case WSAEPROTONOSUPPORT: - p = "Protocol is unsupported"; - break; - case WSAESOCKTNOSUPPORT: - p = "Socket is unsupported"; - break; - case WSAEOPNOTSUPP: - p = "Operation not supported"; - break; - case WSAEAFNOSUPPORT: - p = "Address family not supported"; - break; - case WSAEPFNOSUPPORT: - p = "Protocol family not supported"; - break; - case WSAEADDRINUSE: - p = "Address already in use"; - break; - case WSAEADDRNOTAVAIL: - p = "Address not available"; - break; - case WSAENETDOWN: - p = "Network down"; - break; - case WSAENETUNREACH: - p = "Network unreachable"; - break; - case WSAENETRESET: - p = "Network has been reset"; - break; - case WSAECONNABORTED: - p = "Connection was aborted"; - break; - case WSAECONNRESET: - p = "Connection was reset"; - break; - case WSAENOBUFS: - p = "No buffer space"; - break; - case WSAEISCONN: - p = "Socket is already connected"; - break; - case WSAENOTCONN: - p = "Socket is not connected"; - break; - case WSAESHUTDOWN: - p = "Socket has been shut down"; - break; - case WSAETOOMANYREFS: - p = "Too many references"; - break; - case WSAETIMEDOUT: - p = "Timed out"; - break; - case WSAECONNREFUSED: - p = "Connection refused"; - break; - case WSAELOOP: - p = "Loop??"; - break; - case WSAENAMETOOLONG: - p = "Name too long"; - break; - case WSAEHOSTDOWN: - p = "Host down"; - break; - case WSAEHOSTUNREACH: - p = "Host unreachable"; - break; - case WSAENOTEMPTY: - p = "Not empty"; - break; - case WSAEPROCLIM: - p = "Process limit reached"; - break; - case WSAEUSERS: - p = "Too many users"; - break; - case WSAEDQUOT: - p = "Bad quota"; - break; - case WSAESTALE: - p = "Something is stale"; - break; - case WSAEREMOTE: - p = "Remote error"; - break; -#ifdef WSAEDISCON /* missing in SalfordC! */ - case WSAEDISCON: - p = "Disconnected"; - break; -#endif - /* Extended Winsock errors */ - case WSASYSNOTREADY: - p = "Winsock library is not ready"; - break; - case WSANOTINITIALISED: - p = "Winsock library not initialised"; - break; - case WSAVERNOTSUPPORTED: - p = "Winsock version not supported"; - break; - - /* getXbyY() errors (already handled in herrmsg): - * Authoritative Answer: Host not found */ - case WSAHOST_NOT_FOUND: - p = "Host not found"; - break; - - /* Non-Authoritative: Host not found, or SERVERFAIL */ - case WSATRY_AGAIN: - p = "Host not found, try again"; - break; - - /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */ - case WSANO_RECOVERY: - p = "Unrecoverable error in call to nameserver"; - break; - - /* Valid name, no data record of requested type */ - case WSANO_DATA: - p = "No data record of requested type"; - break; - - default: - return NULL; - } - strncpy(buf, p, len); - buf [len-1] = '\0'; - return buf; -#endif -} -#endif /* USE_WINSOCK */ - -#if defined(WIN32) || defined(_WIN32_WCE) -/* This is a helper function for Curl_strerror that converts Windows API error - * codes (GetLastError) to error messages. - * Returns NULL if no error message was found for error code. - */ -static const char * -get_winapi_error(int err, char *buf, size_t buflen) -{ - char *p; - wchar_t wbuf[256]; - - if(!buflen) - return NULL; - - *buf = '\0'; - *wbuf = L'\0'; - - /* We return the local codepage version of the error string because if it is - output to the user's terminal it will likely be with functions which - expect the local codepage (eg fprintf, failf, infof). - FormatMessageW -> wcstombs is used for Windows CE compatibility. */ - if(FormatMessageW((FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS), NULL, err, - LANG_NEUTRAL, wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL)) { - size_t written = wcstombs(buf, wbuf, buflen - 1); - if(written != (size_t)-1) - buf[written] = '\0'; - else - *buf = '\0'; - } - - /* Truncate multiple lines */ - p = strchr(buf, '\n'); - if(p) { - if(p > buf && *(p-1) == '\r') - *(p-1) = '\0'; - else - *p = '\0'; - } - - return (*buf ? buf : NULL); -} -#endif /* WIN32 || _WIN32_WCE */ - -/* - * Our thread-safe and smart strerror() replacement. - * - * The 'err' argument passed in to this function MUST be a true errno number - * as reported on this system. We do no range checking on the number before - * we pass it to the "number-to-message" conversion function and there might - * be systems that don't do proper range checking in there themselves. - * - * We don't do range checking (on systems other than Windows) since there is - * no good reliable and portable way to do it. - * - * On Windows different types of error codes overlap. This function has an - * order of preference when trying to match error codes: - * CRT (errno), Winsock (WSAGetLastError), Windows API (GetLastError). - * - * It may be more correct to call one of the variant functions instead: - * Call Curl_sspi_strerror if the error code is definitely Windows SSPI. - * Call Curl_winapi_strerror if the error code is definitely Windows API. - */ -const char *Curl_strerror(int err, char *buf, size_t buflen) -{ -#ifdef PRESERVE_WINDOWS_ERROR_CODE - DWORD old_win_err = GetLastError(); -#endif - int old_errno = errno; - char *p; - size_t max; - - if(!buflen) - return NULL; - -#ifndef WIN32 - DEBUGASSERT(err >= 0); -#endif - - max = buflen - 1; - *buf = '\0'; - -#if defined(WIN32) || defined(_WIN32_WCE) -#if defined(WIN32) - /* 'sys_nerr' is the maximum errno number, it is not widely portable */ - if(err >= 0 && err < sys_nerr) - strncpy(buf, sys_errlist[err], max); - else -#endif - { - if( -#ifdef USE_WINSOCK - !get_winsock_error(err, buf, max) && -#endif - !get_winapi_error((DWORD)err, buf, max)) - msnprintf(buf, max, "Unknown error %d (%#x)", err, err); - } -#else /* not Windows coming up */ - -#if defined(HAVE_STRERROR_R) && defined(HAVE_POSIX_STRERROR_R) - /* - * The POSIX-style strerror_r() may set errno to ERANGE if insufficient - * storage is supplied via 'strerrbuf' and 'buflen' to hold the generated - * message string, or EINVAL if 'errnum' is not a valid error number. - */ - if(0 != strerror_r(err, buf, max)) { - if('\0' == buf[0]) - msnprintf(buf, max, "Unknown error %d", err); - } -#elif defined(HAVE_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R) - /* - * The glibc-style strerror_r() only *might* use the buffer we pass to - * the function, but it always returns the error message as a pointer, - * so we must copy that string unconditionally (if non-NULL). - */ - { - char buffer[256]; - char *msg = strerror_r(err, buffer, sizeof(buffer)); - if(msg) - strncpy(buf, msg, max); - else - msnprintf(buf, max, "Unknown error %d", err); - } -#else - { - /* !checksrc! disable STRERROR 1 */ - const char *msg = strerror(err); - if(msg) - strncpy(buf, msg, max); - else - msnprintf(buf, max, "Unknown error %d", err); - } -#endif - -#endif /* end of not Windows */ - - buf[max] = '\0'; /* make sure the string is null-terminated */ - - /* strip trailing '\r\n' or '\n'. */ - p = strrchr(buf, '\n'); - if(p && (p - buf) >= 2) - *p = '\0'; - p = strrchr(buf, '\r'); - if(p && (p - buf) >= 1) - *p = '\0'; - - if(errno != old_errno) - errno = old_errno; - -#ifdef PRESERVE_WINDOWS_ERROR_CODE - if(old_win_err != GetLastError()) - SetLastError(old_win_err); -#endif - - return buf; -} - -/* - * Curl_winapi_strerror: - * Variant of Curl_strerror if the error code is definitely Windows API. - */ -#if defined(WIN32) || defined(_WIN32_WCE) -const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen) -{ -#ifdef PRESERVE_WINDOWS_ERROR_CODE - DWORD old_win_err = GetLastError(); -#endif - int old_errno = errno; - - if(!buflen) - return NULL; - - *buf = '\0'; - -#ifndef CURL_DISABLE_VERBOSE_STRINGS - if(!get_winapi_error(err, buf, buflen)) { - msnprintf(buf, buflen, "Unknown error %u (0x%08X)", err, err); - } -#else - { - const char *txt = (err == ERROR_SUCCESS) ? "No error" : "Error"; - strncpy(buf, txt, buflen); - buf[buflen - 1] = '\0'; - } -#endif - - if(errno != old_errno) - errno = old_errno; - -#ifdef PRESERVE_WINDOWS_ERROR_CODE - if(old_win_err != GetLastError()) - SetLastError(old_win_err); -#endif - - return buf; -} -#endif /* WIN32 || _WIN32_WCE */ - -#ifdef USE_WINDOWS_SSPI -/* - * Curl_sspi_strerror: - * Variant of Curl_strerror if the error code is definitely Windows SSPI. - */ -const char *Curl_sspi_strerror(int err, char *buf, size_t buflen) -{ -#ifdef PRESERVE_WINDOWS_ERROR_CODE - DWORD old_win_err = GetLastError(); -#endif - int old_errno = errno; - const char *txt; - - if(!buflen) - return NULL; - - *buf = '\0'; - -#ifndef CURL_DISABLE_VERBOSE_STRINGS - - switch(err) { - case SEC_E_OK: - txt = "No error"; - break; -#define SEC2TXT(sec) case sec: txt = #sec; break - SEC2TXT(CRYPT_E_REVOKED); - SEC2TXT(SEC_E_ALGORITHM_MISMATCH); - SEC2TXT(SEC_E_BAD_BINDINGS); - SEC2TXT(SEC_E_BAD_PKGID); - SEC2TXT(SEC_E_BUFFER_TOO_SMALL); - SEC2TXT(SEC_E_CANNOT_INSTALL); - SEC2TXT(SEC_E_CANNOT_PACK); - SEC2TXT(SEC_E_CERT_EXPIRED); - SEC2TXT(SEC_E_CERT_UNKNOWN); - SEC2TXT(SEC_E_CERT_WRONG_USAGE); - SEC2TXT(SEC_E_CONTEXT_EXPIRED); - SEC2TXT(SEC_E_CROSSREALM_DELEGATION_FAILURE); - SEC2TXT(SEC_E_CRYPTO_SYSTEM_INVALID); - SEC2TXT(SEC_E_DECRYPT_FAILURE); - SEC2TXT(SEC_E_DELEGATION_POLICY); - SEC2TXT(SEC_E_DELEGATION_REQUIRED); - SEC2TXT(SEC_E_DOWNGRADE_DETECTED); - SEC2TXT(SEC_E_ENCRYPT_FAILURE); - SEC2TXT(SEC_E_ILLEGAL_MESSAGE); - SEC2TXT(SEC_E_INCOMPLETE_CREDENTIALS); - SEC2TXT(SEC_E_INCOMPLETE_MESSAGE); - SEC2TXT(SEC_E_INSUFFICIENT_MEMORY); - SEC2TXT(SEC_E_INTERNAL_ERROR); - SEC2TXT(SEC_E_INVALID_HANDLE); - SEC2TXT(SEC_E_INVALID_PARAMETER); - SEC2TXT(SEC_E_INVALID_TOKEN); - SEC2TXT(SEC_E_ISSUING_CA_UNTRUSTED); - SEC2TXT(SEC_E_ISSUING_CA_UNTRUSTED_KDC); - SEC2TXT(SEC_E_KDC_CERT_EXPIRED); - SEC2TXT(SEC_E_KDC_CERT_REVOKED); - SEC2TXT(SEC_E_KDC_INVALID_REQUEST); - SEC2TXT(SEC_E_KDC_UNABLE_TO_REFER); - SEC2TXT(SEC_E_KDC_UNKNOWN_ETYPE); - SEC2TXT(SEC_E_LOGON_DENIED); - SEC2TXT(SEC_E_MAX_REFERRALS_EXCEEDED); - SEC2TXT(SEC_E_MESSAGE_ALTERED); - SEC2TXT(SEC_E_MULTIPLE_ACCOUNTS); - SEC2TXT(SEC_E_MUST_BE_KDC); - SEC2TXT(SEC_E_NOT_OWNER); - SEC2TXT(SEC_E_NO_AUTHENTICATING_AUTHORITY); - SEC2TXT(SEC_E_NO_CREDENTIALS); - SEC2TXT(SEC_E_NO_IMPERSONATION); - SEC2TXT(SEC_E_NO_IP_ADDRESSES); - SEC2TXT(SEC_E_NO_KERB_KEY); - SEC2TXT(SEC_E_NO_PA_DATA); - SEC2TXT(SEC_E_NO_S4U_PROT_SUPPORT); - SEC2TXT(SEC_E_NO_TGT_REPLY); - SEC2TXT(SEC_E_OUT_OF_SEQUENCE); - SEC2TXT(SEC_E_PKINIT_CLIENT_FAILURE); - SEC2TXT(SEC_E_PKINIT_NAME_MISMATCH); - SEC2TXT(SEC_E_POLICY_NLTM_ONLY); - SEC2TXT(SEC_E_QOP_NOT_SUPPORTED); - SEC2TXT(SEC_E_REVOCATION_OFFLINE_C); - SEC2TXT(SEC_E_REVOCATION_OFFLINE_KDC); - SEC2TXT(SEC_E_SECPKG_NOT_FOUND); - SEC2TXT(SEC_E_SECURITY_QOS_FAILED); - SEC2TXT(SEC_E_SHUTDOWN_IN_PROGRESS); - SEC2TXT(SEC_E_SMARTCARD_CERT_EXPIRED); - SEC2TXT(SEC_E_SMARTCARD_CERT_REVOKED); - SEC2TXT(SEC_E_SMARTCARD_LOGON_REQUIRED); - SEC2TXT(SEC_E_STRONG_CRYPTO_NOT_SUPPORTED); - SEC2TXT(SEC_E_TARGET_UNKNOWN); - SEC2TXT(SEC_E_TIME_SKEW); - SEC2TXT(SEC_E_TOO_MANY_PRINCIPALS); - SEC2TXT(SEC_E_UNFINISHED_CONTEXT_DELETED); - SEC2TXT(SEC_E_UNKNOWN_CREDENTIALS); - SEC2TXT(SEC_E_UNSUPPORTED_FUNCTION); - SEC2TXT(SEC_E_UNSUPPORTED_PREAUTH); - SEC2TXT(SEC_E_UNTRUSTED_ROOT); - SEC2TXT(SEC_E_WRONG_CREDENTIAL_HANDLE); - SEC2TXT(SEC_E_WRONG_PRINCIPAL); - SEC2TXT(SEC_I_COMPLETE_AND_CONTINUE); - SEC2TXT(SEC_I_COMPLETE_NEEDED); - SEC2TXT(SEC_I_CONTEXT_EXPIRED); - SEC2TXT(SEC_I_CONTINUE_NEEDED); - SEC2TXT(SEC_I_INCOMPLETE_CREDENTIALS); - SEC2TXT(SEC_I_LOCAL_LOGON); - SEC2TXT(SEC_I_NO_LSA_CONTEXT); - SEC2TXT(SEC_I_RENEGOTIATE); - SEC2TXT(SEC_I_SIGNATURE_NEEDED); - default: - txt = "Unknown error"; - } - - if(err == SEC_E_ILLEGAL_MESSAGE) { - msnprintf(buf, buflen, - "SEC_E_ILLEGAL_MESSAGE (0x%08X) - This error usually occurs " - "when a fatal SSL/TLS alert is received (e.g. handshake failed)." - " More detail may be available in the Windows System event log.", - err); - } - else { - char txtbuf[80]; - char msgbuf[256]; - - msnprintf(txtbuf, sizeof(txtbuf), "%s (0x%08X)", txt, err); - - if(get_winapi_error(err, msgbuf, sizeof(msgbuf))) - msnprintf(buf, buflen, "%s - %s", txtbuf, msgbuf); - else { - strncpy(buf, txtbuf, buflen); - buf[buflen - 1] = '\0'; - } - } - -#else - if(err == SEC_E_OK) - txt = "No error"; - else - txt = "Error"; - strncpy(buf, txt, buflen); - buf[buflen - 1] = '\0'; -#endif - - if(errno != old_errno) - errno = old_errno; - -#ifdef PRESERVE_WINDOWS_ERROR_CODE - if(old_win_err != GetLastError()) - SetLastError(old_win_err); -#endif - - return buf; -} -#endif /* USE_WINDOWS_SSPI */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2004 - 2022, 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_STRERROR_R
+# if (!defined(HAVE_POSIX_STRERROR_R) && \
+ !defined(HAVE_GLIBC_STRERROR_R)) || \
+ (defined(HAVE_POSIX_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R))
+# error "strerror_r MUST be either POSIX, glibc style"
+# endif
+#endif
+
+#include <curl/curl.h>
+
+#ifdef USE_LIBIDN2
+#include <idn2.h>
+#endif
+
+#ifdef USE_WINDOWS_SSPI
+#include "curl_sspi.h"
+#endif
+
+#include "strerror.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#if defined(WIN32) || defined(_WIN32_WCE)
+#define PRESERVE_WINDOWS_ERROR_CODE
+#endif
+
+const char *
+curl_easy_strerror(CURLcode error)
+{
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ switch(error) {
+ case CURLE_OK:
+ return "No error";
+
+ case CURLE_UNSUPPORTED_PROTOCOL:
+ return "Unsupported protocol";
+
+ case CURLE_FAILED_INIT:
+ return "Failed initialization";
+
+ case CURLE_URL_MALFORMAT:
+ return "URL using bad/illegal format or missing URL";
+
+ case CURLE_NOT_BUILT_IN:
+ return "A requested feature, protocol or option was not found built-in in"
+ " this libcurl due to a build-time decision.";
+
+ case CURLE_COULDNT_RESOLVE_PROXY:
+ return "Couldn't resolve proxy name";
+
+ case CURLE_COULDNT_RESOLVE_HOST:
+ return "Couldn't resolve host name";
+
+ case CURLE_COULDNT_CONNECT:
+ return "Couldn't connect to server";
+
+ case CURLE_WEIRD_SERVER_REPLY:
+ return "Weird server reply";
+
+ case CURLE_REMOTE_ACCESS_DENIED:
+ return "Access denied to remote resource";
+
+ case CURLE_FTP_ACCEPT_FAILED:
+ return "FTP: The server failed to connect to data port";
+
+ case CURLE_FTP_ACCEPT_TIMEOUT:
+ return "FTP: Accepting server connect has timed out";
+
+ case CURLE_FTP_PRET_FAILED:
+ return "FTP: The server did not accept the PRET command.";
+
+ case CURLE_FTP_WEIRD_PASS_REPLY:
+ return "FTP: unknown PASS reply";
+
+ case CURLE_FTP_WEIRD_PASV_REPLY:
+ return "FTP: unknown PASV reply";
+
+ case CURLE_FTP_WEIRD_227_FORMAT:
+ return "FTP: unknown 227 response format";
+
+ case CURLE_FTP_CANT_GET_HOST:
+ return "FTP: can't figure out the host in the PASV response";
+
+ case CURLE_HTTP2:
+ return "Error in the HTTP2 framing layer";
+
+ case CURLE_FTP_COULDNT_SET_TYPE:
+ return "FTP: couldn't set file type";
+
+ case CURLE_PARTIAL_FILE:
+ return "Transferred a partial file";
+
+ case CURLE_FTP_COULDNT_RETR_FILE:
+ return "FTP: couldn't retrieve (RETR failed) the specified file";
+
+ case CURLE_QUOTE_ERROR:
+ return "Quote command returned error";
+
+ case CURLE_HTTP_RETURNED_ERROR:
+ return "HTTP response code said error";
+
+ case CURLE_WRITE_ERROR:
+ return "Failed writing received data to disk/application";
+
+ case CURLE_UPLOAD_FAILED:
+ return "Upload failed (at start/before it took off)";
+
+ case CURLE_READ_ERROR:
+ return "Failed to open/read local data from file/application";
+
+ case CURLE_OUT_OF_MEMORY:
+ return "Out of memory";
+
+ case CURLE_OPERATION_TIMEDOUT:
+ return "Timeout was reached";
+
+ case CURLE_FTP_PORT_FAILED:
+ return "FTP: command PORT failed";
+
+ case CURLE_FTP_COULDNT_USE_REST:
+ return "FTP: command REST failed";
+
+ case CURLE_RANGE_ERROR:
+ return "Requested range was not delivered by the server";
+
+ case CURLE_HTTP_POST_ERROR:
+ return "Internal problem setting up the POST";
+
+ case CURLE_SSL_CONNECT_ERROR:
+ return "SSL connect error";
+
+ case CURLE_BAD_DOWNLOAD_RESUME:
+ return "Couldn't resume download";
+
+ case CURLE_FILE_COULDNT_READ_FILE:
+ return "Couldn't read a file:// file";
+
+ case CURLE_LDAP_CANNOT_BIND:
+ return "LDAP: cannot bind";
+
+ case CURLE_LDAP_SEARCH_FAILED:
+ return "LDAP: search failed";
+
+ case CURLE_FUNCTION_NOT_FOUND:
+ return "A required function in the library was not found";
+
+ case CURLE_ABORTED_BY_CALLBACK:
+ return "Operation was aborted by an application callback";
+
+ case CURLE_BAD_FUNCTION_ARGUMENT:
+ return "A libcurl function was given a bad argument";
+
+ case CURLE_INTERFACE_FAILED:
+ return "Failed binding local connection end";
+
+ case CURLE_TOO_MANY_REDIRECTS :
+ return "Number of redirects hit maximum amount";
+
+ case CURLE_UNKNOWN_OPTION:
+ return "An unknown option was passed in to libcurl";
+
+ case CURLE_SETOPT_OPTION_SYNTAX :
+ return "Malformed option provided in a setopt";
+
+ case CURLE_GOT_NOTHING:
+ return "Server returned nothing (no headers, no data)";
+
+ case CURLE_SSL_ENGINE_NOTFOUND:
+ return "SSL crypto engine not found";
+
+ case CURLE_SSL_ENGINE_SETFAILED:
+ return "Can not set SSL crypto engine as default";
+
+ case CURLE_SSL_ENGINE_INITFAILED:
+ return "Failed to initialise SSL crypto engine";
+
+ case CURLE_SEND_ERROR:
+ return "Failed sending data to the peer";
+
+ case CURLE_RECV_ERROR:
+ return "Failure when receiving data from the peer";
+
+ case CURLE_SSL_CERTPROBLEM:
+ return "Problem with the local SSL certificate";
+
+ case CURLE_SSL_CIPHER:
+ return "Couldn't use specified SSL cipher";
+
+ case CURLE_PEER_FAILED_VERIFICATION:
+ return "SSL peer certificate or SSH remote key was not OK";
+
+ case CURLE_SSL_CACERT_BADFILE:
+ return "Problem with the SSL CA cert (path? access rights?)";
+
+ case CURLE_BAD_CONTENT_ENCODING:
+ return "Unrecognized or bad HTTP Content or Transfer-Encoding";
+
+ case CURLE_FILESIZE_EXCEEDED:
+ return "Maximum file size exceeded";
+
+ case CURLE_USE_SSL_FAILED:
+ return "Requested SSL level failed";
+
+ case CURLE_SSL_SHUTDOWN_FAILED:
+ return "Failed to shut down the SSL connection";
+
+ case CURLE_SSL_CRL_BADFILE:
+ return "Failed to load CRL file (path? access rights?, format?)";
+
+ case CURLE_SSL_ISSUER_ERROR:
+ return "Issuer check against peer certificate failed";
+
+ case CURLE_SEND_FAIL_REWIND:
+ return "Send failed since rewinding of the data stream failed";
+
+ case CURLE_LOGIN_DENIED:
+ return "Login denied";
+
+ case CURLE_TFTP_NOTFOUND:
+ return "TFTP: File Not Found";
+
+ case CURLE_TFTP_PERM:
+ return "TFTP: Access Violation";
+
+ case CURLE_REMOTE_DISK_FULL:
+ return "Disk full or allocation exceeded";
+
+ case CURLE_TFTP_ILLEGAL:
+ return "TFTP: Illegal operation";
+
+ case CURLE_TFTP_UNKNOWNID:
+ return "TFTP: Unknown transfer ID";
+
+ case CURLE_REMOTE_FILE_EXISTS:
+ return "Remote file already exists";
+
+ case CURLE_TFTP_NOSUCHUSER:
+ return "TFTP: No such user";
+
+ case CURLE_REMOTE_FILE_NOT_FOUND:
+ return "Remote file not found";
+
+ case CURLE_SSH:
+ return "Error in the SSH layer";
+
+ case CURLE_AGAIN:
+ return "Socket not ready for send/recv";
+
+ case CURLE_RTSP_CSEQ_ERROR:
+ return "RTSP CSeq mismatch or invalid CSeq";
+
+ case CURLE_RTSP_SESSION_ERROR:
+ return "RTSP session error";
+
+ case CURLE_FTP_BAD_FILE_LIST:
+ return "Unable to parse FTP file list";
+
+ case CURLE_CHUNK_FAILED:
+ return "Chunk callback failed";
+
+ case CURLE_NO_CONNECTION_AVAILABLE:
+ return "The max connection limit is reached";
+
+ case CURLE_SSL_PINNEDPUBKEYNOTMATCH:
+ return "SSL public key does not match pinned public key";
+
+ case CURLE_SSL_INVALIDCERTSTATUS:
+ return "SSL server certificate status verification FAILED";
+
+ case CURLE_HTTP2_STREAM:
+ return "Stream error in the HTTP/2 framing layer";
+
+ case CURLE_RECURSIVE_API_CALL:
+ return "API function called from within callback";
+
+ case CURLE_AUTH_ERROR:
+ return "An authentication function returned an error";
+
+ case CURLE_HTTP3:
+ return "HTTP/3 error";
+
+ case CURLE_QUIC_CONNECT_ERROR:
+ return "QUIC connection error";
+
+ case CURLE_PROXY:
+ return "proxy handshake error";
+
+ case CURLE_SSL_CLIENTCERT:
+ return "SSL Client Certificate required";
+
+ case CURLE_UNRECOVERABLE_POLL:
+ return "Unrecoverable error in select/poll";
+
+ /* error codes not used by current libcurl */
+ case CURLE_OBSOLETE20:
+ case CURLE_OBSOLETE24:
+ case CURLE_OBSOLETE29:
+ case CURLE_OBSOLETE32:
+ case CURLE_OBSOLETE40:
+ case CURLE_OBSOLETE44:
+ case CURLE_OBSOLETE46:
+ case CURLE_OBSOLETE50:
+ case CURLE_OBSOLETE51:
+ case CURLE_OBSOLETE57:
+ case CURLE_OBSOLETE62:
+ case CURLE_OBSOLETE75:
+ case CURLE_OBSOLETE76:
+ case CURL_LAST:
+ break;
+ }
+ /*
+ * By using a switch, gcc -Wall will complain about enum values
+ * which do not appear, helping keep this function up-to-date.
+ * By using gcc -Wall -Werror, you can't forget.
+ *
+ * A table would not have the same benefit. Most compilers will
+ * generate code very similar to a table in any case, so there
+ * is little performance gain from a table. And something is broken
+ * for the user's application, anyways, so does it matter how fast
+ * it _doesn't_ work?
+ *
+ * The line number for the error will be near this comment, which
+ * is why it is here, and not at the start of the switch.
+ */
+ return "Unknown error";
+#else
+ if(!error)
+ return "No error";
+ else
+ return "Error";
+#endif
+}
+
+const char *
+curl_multi_strerror(CURLMcode error)
+{
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ switch(error) {
+ case CURLM_CALL_MULTI_PERFORM:
+ return "Please call curl_multi_perform() soon";
+
+ case CURLM_OK:
+ return "No error";
+
+ case CURLM_BAD_HANDLE:
+ return "Invalid multi handle";
+
+ case CURLM_BAD_EASY_HANDLE:
+ return "Invalid easy handle";
+
+ case CURLM_OUT_OF_MEMORY:
+ return "Out of memory";
+
+ case CURLM_INTERNAL_ERROR:
+ return "Internal error";
+
+ case CURLM_BAD_SOCKET:
+ return "Invalid socket argument";
+
+ case CURLM_UNKNOWN_OPTION:
+ return "Unknown option";
+
+ case CURLM_ADDED_ALREADY:
+ return "The easy handle is already added to a multi handle";
+
+ case CURLM_RECURSIVE_API_CALL:
+ return "API function called from within callback";
+
+ case CURLM_WAKEUP_FAILURE:
+ return "Wakeup is unavailable or failed";
+
+ case CURLM_BAD_FUNCTION_ARGUMENT:
+ return "A libcurl function was given a bad argument";
+
+ case CURLM_ABORTED_BY_CALLBACK:
+ return "Operation was aborted by an application callback";
+
+ case CURLM_UNRECOVERABLE_POLL:
+ return "Unrecoverable error in select/poll";
+
+ case CURLM_LAST:
+ break;
+ }
+
+ return "Unknown error";
+#else
+ if(error == CURLM_OK)
+ return "No error";
+ else
+ return "Error";
+#endif
+}
+
+const char *
+curl_share_strerror(CURLSHcode error)
+{
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ switch(error) {
+ case CURLSHE_OK:
+ return "No error";
+
+ case CURLSHE_BAD_OPTION:
+ return "Unknown share option";
+
+ case CURLSHE_IN_USE:
+ return "Share currently in use";
+
+ case CURLSHE_INVALID:
+ return "Invalid share handle";
+
+ case CURLSHE_NOMEM:
+ return "Out of memory";
+
+ case CURLSHE_NOT_BUILT_IN:
+ return "Feature not enabled in this library";
+
+ case CURLSHE_LAST:
+ break;
+ }
+
+ return "CURLSHcode unknown";
+#else
+ if(error == CURLSHE_OK)
+ return "No error";
+ else
+ return "Error";
+#endif
+}
+
+const char *
+curl_url_strerror(CURLUcode error)
+{
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ switch(error) {
+ case CURLUE_OK:
+ return "No error";
+
+ case CURLUE_BAD_HANDLE:
+ return "An invalid CURLU pointer was passed as argument";
+
+ case CURLUE_BAD_PARTPOINTER:
+ return "An invalid 'part' argument was passed as argument";
+
+ case CURLUE_MALFORMED_INPUT:
+ return "Malformed input to a URL function";
+
+ case CURLUE_BAD_PORT_NUMBER:
+ return "Port number was not a decimal number between 0 and 65535";
+
+ case CURLUE_UNSUPPORTED_SCHEME:
+ return "Unsupported URL scheme";
+
+ case CURLUE_URLDECODE:
+ return "URL decode error, most likely because of rubbish in the input";
+
+ case CURLUE_OUT_OF_MEMORY:
+ return "A memory function failed";
+
+ case CURLUE_USER_NOT_ALLOWED:
+ return "Credentials was passed in the URL when prohibited";
+
+ case CURLUE_UNKNOWN_PART:
+ return "An unknown part ID was passed to a URL API function";
+
+ case CURLUE_NO_SCHEME:
+ return "No scheme part in the URL";
+
+ case CURLUE_NO_USER:
+ return "No user part in the URL";
+
+ case CURLUE_NO_PASSWORD:
+ return "No password part in the URL";
+
+ case CURLUE_NO_OPTIONS:
+ return "No options part in the URL";
+
+ case CURLUE_NO_HOST:
+ return "No host part in the URL";
+
+ case CURLUE_NO_PORT:
+ return "No port part in the URL";
+
+ case CURLUE_NO_QUERY:
+ return "No query part in the URL";
+
+ case CURLUE_NO_FRAGMENT:
+ return "No fragment part in the URL";
+
+ case CURLUE_NO_ZONEID:
+ return "No zoneid part in the URL";
+
+ case CURLUE_BAD_LOGIN:
+ return "Bad login part";
+
+ case CURLUE_BAD_IPV6:
+ return "Bad IPv6 address";
+
+ case CURLUE_BAD_HOSTNAME:
+ return "Bad hostname";
+
+ case CURLUE_BAD_FILE_URL:
+ return "Bad file:// URL";
+
+ case CURLUE_BAD_SLASHES:
+ return "Unsupported number of slashes following scheme";
+
+ case CURLUE_BAD_SCHEME:
+ return "Bad scheme";
+
+ case CURLUE_BAD_PATH:
+ return "Bad path";
+
+ case CURLUE_BAD_FRAGMENT:
+ return "Bad fragment";
+
+ case CURLUE_BAD_QUERY:
+ return "Bad query";
+
+ case CURLUE_BAD_PASSWORD:
+ return "Bad password";
+
+ case CURLUE_BAD_USER:
+ return "Bad user";
+
+ case CURLUE_LAST:
+ break;
+ }
+
+ return "CURLUcode unknown";
+#else
+ if(error == CURLUE_OK)
+ return "No error";
+ else
+ return "Error";
+#endif
+}
+
+#ifdef USE_WINSOCK
+/* This is a helper function for Curl_strerror that converts Winsock error
+ * codes (WSAGetLastError) to error messages.
+ * Returns NULL if no error message was found for error code.
+ */
+static const char *
+get_winsock_error (int err, char *buf, size_t len)
+{
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ const char *p;
+#endif
+
+ if(!len)
+ return NULL;
+
+ *buf = '\0';
+
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+ (void)err;
+ return NULL;
+#else
+ switch(err) {
+ case WSAEINTR:
+ p = "Call interrupted";
+ break;
+ case WSAEBADF:
+ p = "Bad file";
+ break;
+ case WSAEACCES:
+ p = "Bad access";
+ break;
+ case WSAEFAULT:
+ p = "Bad argument";
+ break;
+ case WSAEINVAL:
+ p = "Invalid arguments";
+ break;
+ case WSAEMFILE:
+ p = "Out of file descriptors";
+ break;
+ case WSAEWOULDBLOCK:
+ p = "Call would block";
+ break;
+ case WSAEINPROGRESS:
+ case WSAEALREADY:
+ p = "Blocking call in progress";
+ break;
+ case WSAENOTSOCK:
+ p = "Descriptor is not a socket";
+ break;
+ case WSAEDESTADDRREQ:
+ p = "Need destination address";
+ break;
+ case WSAEMSGSIZE:
+ p = "Bad message size";
+ break;
+ case WSAEPROTOTYPE:
+ p = "Bad protocol";
+ break;
+ case WSAENOPROTOOPT:
+ p = "Protocol option is unsupported";
+ break;
+ case WSAEPROTONOSUPPORT:
+ p = "Protocol is unsupported";
+ break;
+ case WSAESOCKTNOSUPPORT:
+ p = "Socket is unsupported";
+ break;
+ case WSAEOPNOTSUPP:
+ p = "Operation not supported";
+ break;
+ case WSAEAFNOSUPPORT:
+ p = "Address family not supported";
+ break;
+ case WSAEPFNOSUPPORT:
+ p = "Protocol family not supported";
+ break;
+ case WSAEADDRINUSE:
+ p = "Address already in use";
+ break;
+ case WSAEADDRNOTAVAIL:
+ p = "Address not available";
+ break;
+ case WSAENETDOWN:
+ p = "Network down";
+ break;
+ case WSAENETUNREACH:
+ p = "Network unreachable";
+ break;
+ case WSAENETRESET:
+ p = "Network has been reset";
+ break;
+ case WSAECONNABORTED:
+ p = "Connection was aborted";
+ break;
+ case WSAECONNRESET:
+ p = "Connection was reset";
+ break;
+ case WSAENOBUFS:
+ p = "No buffer space";
+ break;
+ case WSAEISCONN:
+ p = "Socket is already connected";
+ break;
+ case WSAENOTCONN:
+ p = "Socket is not connected";
+ break;
+ case WSAESHUTDOWN:
+ p = "Socket has been shut down";
+ break;
+ case WSAETOOMANYREFS:
+ p = "Too many references";
+ break;
+ case WSAETIMEDOUT:
+ p = "Timed out";
+ break;
+ case WSAECONNREFUSED:
+ p = "Connection refused";
+ break;
+ case WSAELOOP:
+ p = "Loop??";
+ break;
+ case WSAENAMETOOLONG:
+ p = "Name too long";
+ break;
+ case WSAEHOSTDOWN:
+ p = "Host down";
+ break;
+ case WSAEHOSTUNREACH:
+ p = "Host unreachable";
+ break;
+ case WSAENOTEMPTY:
+ p = "Not empty";
+ break;
+ case WSAEPROCLIM:
+ p = "Process limit reached";
+ break;
+ case WSAEUSERS:
+ p = "Too many users";
+ break;
+ case WSAEDQUOT:
+ p = "Bad quota";
+ break;
+ case WSAESTALE:
+ p = "Something is stale";
+ break;
+ case WSAEREMOTE:
+ p = "Remote error";
+ break;
+#ifdef WSAEDISCON /* missing in SalfordC! */
+ case WSAEDISCON:
+ p = "Disconnected";
+ break;
+#endif
+ /* Extended Winsock errors */
+ case WSASYSNOTREADY:
+ p = "Winsock library is not ready";
+ break;
+ case WSANOTINITIALISED:
+ p = "Winsock library not initialised";
+ break;
+ case WSAVERNOTSUPPORTED:
+ p = "Winsock version not supported";
+ break;
+
+ /* getXbyY() errors (already handled in herrmsg):
+ * Authoritative Answer: Host not found */
+ case WSAHOST_NOT_FOUND:
+ p = "Host not found";
+ break;
+
+ /* Non-Authoritative: Host not found, or SERVERFAIL */
+ case WSATRY_AGAIN:
+ p = "Host not found, try again";
+ break;
+
+ /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */
+ case WSANO_RECOVERY:
+ p = "Unrecoverable error in call to nameserver";
+ break;
+
+ /* Valid name, no data record of requested type */
+ case WSANO_DATA:
+ p = "No data record of requested type";
+ break;
+
+ default:
+ return NULL;
+ }
+ strncpy(buf, p, len);
+ buf [len-1] = '\0';
+ return buf;
+#endif
+}
+#endif /* USE_WINSOCK */
+
+#if defined(WIN32) || defined(_WIN32_WCE)
+/* This is a helper function for Curl_strerror that converts Windows API error
+ * codes (GetLastError) to error messages.
+ * Returns NULL if no error message was found for error code.
+ */
+static const char *
+get_winapi_error(int err, char *buf, size_t buflen)
+{
+ char *p;
+ wchar_t wbuf[256];
+
+ if(!buflen)
+ return NULL;
+
+ *buf = '\0';
+ *wbuf = L'\0';
+
+ /* We return the local codepage version of the error string because if it is
+ output to the user's terminal it will likely be with functions which
+ expect the local codepage (eg fprintf, failf, infof).
+ FormatMessageW -> wcstombs is used for Windows CE compatibility. */
+ if(FormatMessageW((FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS), NULL, err,
+ LANG_NEUTRAL, wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL)) {
+ size_t written = wcstombs(buf, wbuf, buflen - 1);
+ if(written != (size_t)-1)
+ buf[written] = '\0';
+ else
+ *buf = '\0';
+ }
+
+ /* Truncate multiple lines */
+ p = strchr(buf, '\n');
+ if(p) {
+ if(p > buf && *(p-1) == '\r')
+ *(p-1) = '\0';
+ else
+ *p = '\0';
+ }
+
+ return (*buf ? buf : NULL);
+}
+#endif /* WIN32 || _WIN32_WCE */
+
+/*
+ * Our thread-safe and smart strerror() replacement.
+ *
+ * The 'err' argument passed in to this function MUST be a true errno number
+ * as reported on this system. We do no range checking on the number before
+ * we pass it to the "number-to-message" conversion function and there might
+ * be systems that don't do proper range checking in there themselves.
+ *
+ * We don't do range checking (on systems other than Windows) since there is
+ * no good reliable and portable way to do it.
+ *
+ * On Windows different types of error codes overlap. This function has an
+ * order of preference when trying to match error codes:
+ * CRT (errno), Winsock (WSAGetLastError), Windows API (GetLastError).
+ *
+ * It may be more correct to call one of the variant functions instead:
+ * Call Curl_sspi_strerror if the error code is definitely Windows SSPI.
+ * Call Curl_winapi_strerror if the error code is definitely Windows API.
+ */
+const char *Curl_strerror(int err, char *buf, size_t buflen)
+{
+#ifdef PRESERVE_WINDOWS_ERROR_CODE
+ DWORD old_win_err = GetLastError();
+#endif
+ int old_errno = errno;
+ char *p;
+ size_t max;
+
+ if(!buflen)
+ return NULL;
+
+#ifndef WIN32
+ DEBUGASSERT(err >= 0);
+#endif
+
+ max = buflen - 1;
+ *buf = '\0';
+
+#if defined(WIN32) || defined(_WIN32_WCE)
+#if defined(WIN32)
+ /* 'sys_nerr' is the maximum errno number, it is not widely portable */
+ if(err >= 0 && err < sys_nerr)
+ strncpy(buf, sys_errlist[err], max);
+ else
+#endif
+ {
+ if(
+#ifdef USE_WINSOCK
+ !get_winsock_error(err, buf, max) &&
+#endif
+ !get_winapi_error((DWORD)err, buf, max))
+ msnprintf(buf, max, "Unknown error %d (%#x)", err, err);
+ }
+#else /* not Windows coming up */
+
+#if defined(HAVE_STRERROR_R) && defined(HAVE_POSIX_STRERROR_R)
+ /*
+ * The POSIX-style strerror_r() may set errno to ERANGE if insufficient
+ * storage is supplied via 'strerrbuf' and 'buflen' to hold the generated
+ * message string, or EINVAL if 'errnum' is not a valid error number.
+ */
+ if(0 != strerror_r(err, buf, max)) {
+ if('\0' == buf[0])
+ msnprintf(buf, max, "Unknown error %d", err);
+ }
+#elif defined(HAVE_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R)
+ /*
+ * The glibc-style strerror_r() only *might* use the buffer we pass to
+ * the function, but it always returns the error message as a pointer,
+ * so we must copy that string unconditionally (if non-NULL).
+ */
+ {
+ char buffer[256];
+ char *msg = strerror_r(err, buffer, sizeof(buffer));
+ if(msg)
+ strncpy(buf, msg, max);
+ else
+ msnprintf(buf, max, "Unknown error %d", err);
+ }
+#else
+ {
+ /* !checksrc! disable STRERROR 1 */
+ const char *msg = strerror(err);
+ if(msg)
+ strncpy(buf, msg, max);
+ else
+ msnprintf(buf, max, "Unknown error %d", err);
+ }
+#endif
+
+#endif /* end of not Windows */
+
+ buf[max] = '\0'; /* make sure the string is null-terminated */
+
+ /* strip trailing '\r\n' or '\n'. */
+ p = strrchr(buf, '\n');
+ if(p && (p - buf) >= 2)
+ *p = '\0';
+ p = strrchr(buf, '\r');
+ if(p && (p - buf) >= 1)
+ *p = '\0';
+
+ if(errno != old_errno)
+ errno = old_errno;
+
+#ifdef PRESERVE_WINDOWS_ERROR_CODE
+ if(old_win_err != GetLastError())
+ SetLastError(old_win_err);
+#endif
+
+ return buf;
+}
+
+/*
+ * Curl_winapi_strerror:
+ * Variant of Curl_strerror if the error code is definitely Windows API.
+ */
+#if defined(WIN32) || defined(_WIN32_WCE)
+const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen)
+{
+#ifdef PRESERVE_WINDOWS_ERROR_CODE
+ DWORD old_win_err = GetLastError();
+#endif
+ int old_errno = errno;
+
+ if(!buflen)
+ return NULL;
+
+ *buf = '\0';
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ if(!get_winapi_error(err, buf, buflen)) {
+ msnprintf(buf, buflen, "Unknown error %u (0x%08X)", err, err);
+ }
+#else
+ {
+ const char *txt = (err == ERROR_SUCCESS) ? "No error" : "Error";
+ strncpy(buf, txt, buflen);
+ buf[buflen - 1] = '\0';
+ }
+#endif
+
+ if(errno != old_errno)
+ errno = old_errno;
+
+#ifdef PRESERVE_WINDOWS_ERROR_CODE
+ if(old_win_err != GetLastError())
+ SetLastError(old_win_err);
+#endif
+
+ return buf;
+}
+#endif /* WIN32 || _WIN32_WCE */
+
+#ifdef USE_WINDOWS_SSPI
+/*
+ * Curl_sspi_strerror:
+ * Variant of Curl_strerror if the error code is definitely Windows SSPI.
+ */
+const char *Curl_sspi_strerror(int err, char *buf, size_t buflen)
+{
+#ifdef PRESERVE_WINDOWS_ERROR_CODE
+ DWORD old_win_err = GetLastError();
+#endif
+ int old_errno = errno;
+ const char *txt;
+
+ if(!buflen)
+ return NULL;
+
+ *buf = '\0';
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+
+ switch(err) {
+ case SEC_E_OK:
+ txt = "No error";
+ break;
+#define SEC2TXT(sec) case sec: txt = #sec; break
+ SEC2TXT(CRYPT_E_REVOKED);
+ SEC2TXT(SEC_E_ALGORITHM_MISMATCH);
+ SEC2TXT(SEC_E_BAD_BINDINGS);
+ SEC2TXT(SEC_E_BAD_PKGID);
+ SEC2TXT(SEC_E_BUFFER_TOO_SMALL);
+ SEC2TXT(SEC_E_CANNOT_INSTALL);
+ SEC2TXT(SEC_E_CANNOT_PACK);
+ SEC2TXT(SEC_E_CERT_EXPIRED);
+ SEC2TXT(SEC_E_CERT_UNKNOWN);
+ SEC2TXT(SEC_E_CERT_WRONG_USAGE);
+ SEC2TXT(SEC_E_CONTEXT_EXPIRED);
+ SEC2TXT(SEC_E_CROSSREALM_DELEGATION_FAILURE);
+ SEC2TXT(SEC_E_CRYPTO_SYSTEM_INVALID);
+ SEC2TXT(SEC_E_DECRYPT_FAILURE);
+ SEC2TXT(SEC_E_DELEGATION_POLICY);
+ SEC2TXT(SEC_E_DELEGATION_REQUIRED);
+ SEC2TXT(SEC_E_DOWNGRADE_DETECTED);
+ SEC2TXT(SEC_E_ENCRYPT_FAILURE);
+ SEC2TXT(SEC_E_ILLEGAL_MESSAGE);
+ SEC2TXT(SEC_E_INCOMPLETE_CREDENTIALS);
+ SEC2TXT(SEC_E_INCOMPLETE_MESSAGE);
+ SEC2TXT(SEC_E_INSUFFICIENT_MEMORY);
+ SEC2TXT(SEC_E_INTERNAL_ERROR);
+ SEC2TXT(SEC_E_INVALID_HANDLE);
+ SEC2TXT(SEC_E_INVALID_PARAMETER);
+ SEC2TXT(SEC_E_INVALID_TOKEN);
+ SEC2TXT(SEC_E_ISSUING_CA_UNTRUSTED);
+ SEC2TXT(SEC_E_ISSUING_CA_UNTRUSTED_KDC);
+ SEC2TXT(SEC_E_KDC_CERT_EXPIRED);
+ SEC2TXT(SEC_E_KDC_CERT_REVOKED);
+ SEC2TXT(SEC_E_KDC_INVALID_REQUEST);
+ SEC2TXT(SEC_E_KDC_UNABLE_TO_REFER);
+ SEC2TXT(SEC_E_KDC_UNKNOWN_ETYPE);
+ SEC2TXT(SEC_E_LOGON_DENIED);
+ SEC2TXT(SEC_E_MAX_REFERRALS_EXCEEDED);
+ SEC2TXT(SEC_E_MESSAGE_ALTERED);
+ SEC2TXT(SEC_E_MULTIPLE_ACCOUNTS);
+ SEC2TXT(SEC_E_MUST_BE_KDC);
+ SEC2TXT(SEC_E_NOT_OWNER);
+ SEC2TXT(SEC_E_NO_AUTHENTICATING_AUTHORITY);
+ SEC2TXT(SEC_E_NO_CREDENTIALS);
+ SEC2TXT(SEC_E_NO_IMPERSONATION);
+ SEC2TXT(SEC_E_NO_IP_ADDRESSES);
+ SEC2TXT(SEC_E_NO_KERB_KEY);
+ SEC2TXT(SEC_E_NO_PA_DATA);
+ SEC2TXT(SEC_E_NO_S4U_PROT_SUPPORT);
+ SEC2TXT(SEC_E_NO_TGT_REPLY);
+ SEC2TXT(SEC_E_OUT_OF_SEQUENCE);
+ SEC2TXT(SEC_E_PKINIT_CLIENT_FAILURE);
+ SEC2TXT(SEC_E_PKINIT_NAME_MISMATCH);
+ SEC2TXT(SEC_E_POLICY_NLTM_ONLY);
+ SEC2TXT(SEC_E_QOP_NOT_SUPPORTED);
+ SEC2TXT(SEC_E_REVOCATION_OFFLINE_C);
+ SEC2TXT(SEC_E_REVOCATION_OFFLINE_KDC);
+ SEC2TXT(SEC_E_SECPKG_NOT_FOUND);
+ SEC2TXT(SEC_E_SECURITY_QOS_FAILED);
+ SEC2TXT(SEC_E_SHUTDOWN_IN_PROGRESS);
+ SEC2TXT(SEC_E_SMARTCARD_CERT_EXPIRED);
+ SEC2TXT(SEC_E_SMARTCARD_CERT_REVOKED);
+ SEC2TXT(SEC_E_SMARTCARD_LOGON_REQUIRED);
+ SEC2TXT(SEC_E_STRONG_CRYPTO_NOT_SUPPORTED);
+ SEC2TXT(SEC_E_TARGET_UNKNOWN);
+ SEC2TXT(SEC_E_TIME_SKEW);
+ SEC2TXT(SEC_E_TOO_MANY_PRINCIPALS);
+ SEC2TXT(SEC_E_UNFINISHED_CONTEXT_DELETED);
+ SEC2TXT(SEC_E_UNKNOWN_CREDENTIALS);
+ SEC2TXT(SEC_E_UNSUPPORTED_FUNCTION);
+ SEC2TXT(SEC_E_UNSUPPORTED_PREAUTH);
+ SEC2TXT(SEC_E_UNTRUSTED_ROOT);
+ SEC2TXT(SEC_E_WRONG_CREDENTIAL_HANDLE);
+ SEC2TXT(SEC_E_WRONG_PRINCIPAL);
+ SEC2TXT(SEC_I_COMPLETE_AND_CONTINUE);
+ SEC2TXT(SEC_I_COMPLETE_NEEDED);
+ SEC2TXT(SEC_I_CONTEXT_EXPIRED);
+ SEC2TXT(SEC_I_CONTINUE_NEEDED);
+ SEC2TXT(SEC_I_INCOMPLETE_CREDENTIALS);
+ SEC2TXT(SEC_I_LOCAL_LOGON);
+ SEC2TXT(SEC_I_NO_LSA_CONTEXT);
+ SEC2TXT(SEC_I_RENEGOTIATE);
+ SEC2TXT(SEC_I_SIGNATURE_NEEDED);
+ default:
+ txt = "Unknown error";
+ }
+
+ if(err == SEC_E_ILLEGAL_MESSAGE) {
+ msnprintf(buf, buflen,
+ "SEC_E_ILLEGAL_MESSAGE (0x%08X) - This error usually occurs "
+ "when a fatal SSL/TLS alert is received (e.g. handshake failed)."
+ " More detail may be available in the Windows System event log.",
+ err);
+ }
+ else {
+ char txtbuf[80];
+ char msgbuf[256];
+
+ msnprintf(txtbuf, sizeof(txtbuf), "%s (0x%08X)", txt, err);
+
+ if(get_winapi_error(err, msgbuf, sizeof(msgbuf)))
+ msnprintf(buf, buflen, "%s - %s", txtbuf, msgbuf);
+ else {
+ strncpy(buf, txtbuf, buflen);
+ buf[buflen - 1] = '\0';
+ }
+ }
+
+#else
+ if(err == SEC_E_OK)
+ txt = "No error";
+ else
+ txt = "Error";
+ strncpy(buf, txt, buflen);
+ buf[buflen - 1] = '\0';
+#endif
+
+ if(errno != old_errno)
+ errno = old_errno;
+
+#ifdef PRESERVE_WINDOWS_ERROR_CODE
+ if(old_win_err != GetLastError())
+ SetLastError(old_win_err);
+#endif
+
+ return buf;
+}
+#endif /* USE_WINDOWS_SSPI */
diff --git a/libs/libcurl/src/strerror.h b/libs/libcurl/src/strerror.h index 658f16c10e..730084212b 100644 --- a/libs/libcurl/src/strerror.h +++ b/libs/libcurl/src/strerror.h @@ -1,39 +1,39 @@ -#ifndef HEADER_CURL_STRERROR_H -#define HEADER_CURL_STRERROR_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "urldata.h" - -#define STRERROR_LEN 256 /* a suitable length */ - -const char *Curl_strerror(int err, char *buf, size_t buflen); -#if defined(WIN32) || defined(_WIN32_WCE) -const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen); -#endif -#ifdef USE_WINDOWS_SSPI -const char *Curl_sspi_strerror(int err, char *buf, size_t buflen); -#endif - -#endif /* HEADER_CURL_STRERROR_H */ +#ifndef HEADER_CURL_STRERROR_H
+#define HEADER_CURL_STRERROR_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "urldata.h"
+
+#define STRERROR_LEN 256 /* a suitable length */
+
+const char *Curl_strerror(int err, char *buf, size_t buflen);
+#if defined(WIN32) || defined(_WIN32_WCE)
+const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen);
+#endif
+#ifdef USE_WINDOWS_SSPI
+const char *Curl_sspi_strerror(int err, char *buf, size_t buflen);
+#endif
+
+#endif /* HEADER_CURL_STRERROR_H */
diff --git a/libs/libcurl/src/strtok.c b/libs/libcurl/src/strtok.c index 6120bcc28e..73d738b0a6 100644 --- a/libs/libcurl/src/strtok.c +++ b/libs/libcurl/src/strtok.c @@ -1,68 +1,68 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 HAVE_STRTOK_R -#include <stddef.h> - -#include "strtok.h" - -char * -Curl_strtok_r(char *ptr, const char *sep, char **end) -{ - if(!ptr) - /* we got NULL input so then we get our last position instead */ - ptr = *end; - - /* pass all letters that are including in the separator string */ - while(*ptr && strchr(sep, *ptr)) - ++ptr; - - if(*ptr) { - /* so this is where the next piece of string starts */ - char *start = ptr; - - /* set the end pointer to the first byte after the start */ - *end = start + 1; - - /* scan through the string to find where it ends, it ends on a - null byte or a character that exists in the separator string */ - while(**end && !strchr(sep, **end)) - ++*end; - - if(**end) { - /* the end is not a null byte */ - **end = '\0'; /* null-terminate it! */ - ++*end; /* advance the last pointer to beyond the null byte */ - } - - return start; /* return the position where the string starts */ - } - - /* we ended up on a null byte, there are no more strings to find! */ - return NULL; -} - -#endif /* this was only compiled if strtok_r wasn't present */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 HAVE_STRTOK_R
+#include <stddef.h>
+
+#include "strtok.h"
+
+char *
+Curl_strtok_r(char *ptr, const char *sep, char **end)
+{
+ if(!ptr)
+ /* we got NULL input so then we get our last position instead */
+ ptr = *end;
+
+ /* pass all letters that are including in the separator string */
+ while(*ptr && strchr(sep, *ptr))
+ ++ptr;
+
+ if(*ptr) {
+ /* so this is where the next piece of string starts */
+ char *start = ptr;
+
+ /* set the end pointer to the first byte after the start */
+ *end = start + 1;
+
+ /* scan through the string to find where it ends, it ends on a
+ null byte or a character that exists in the separator string */
+ while(**end && !strchr(sep, **end))
+ ++*end;
+
+ if(**end) {
+ /* the end is not a null byte */
+ **end = '\0'; /* null-terminate it! */
+ ++*end; /* advance the last pointer to beyond the null byte */
+ }
+
+ return start; /* return the position where the string starts */
+ }
+
+ /* we ended up on a null byte, there are no more strings to find! */
+ return NULL;
+}
+
+#endif /* this was only compiled if strtok_r wasn't present */
diff --git a/libs/libcurl/src/strtok.h b/libs/libcurl/src/strtok.h index 641a3daed8..b06be08744 100644 --- a/libs/libcurl/src/strtok.h +++ b/libs/libcurl/src/strtok.h @@ -1,36 +1,36 @@ -#ifndef HEADER_CURL_STRTOK_H -#define HEADER_CURL_STRTOK_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#include "curl_setup.h" -#include <stddef.h> - -#ifndef HAVE_STRTOK_R -char *Curl_strtok_r(char *s, const char *delim, char **last); -#define strtok_r Curl_strtok_r -#else -#include <string.h> -#endif - -#endif /* HEADER_CURL_STRTOK_H */ +#ifndef HEADER_CURL_STRTOK_H
+#define HEADER_CURL_STRTOK_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+#include <stddef.h>
+
+#ifndef HAVE_STRTOK_R
+char *Curl_strtok_r(char *s, const char *delim, char **last);
+#define strtok_r Curl_strtok_r
+#else
+#include <string.h>
+#endif
+
+#endif /* HEADER_CURL_STRTOK_H */
diff --git a/libs/libcurl/src/strtoofft.c b/libs/libcurl/src/strtoofft.c index 30deb8c05b..cc39b84566 100644 --- a/libs/libcurl/src/strtoofft.c +++ b/libs/libcurl/src/strtoofft.c @@ -1,244 +1,245 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 <errno.h> -#include "curl_setup.h" - -#include "strtoofft.h" - -/* - * NOTE: - * - * In the ISO C standard (IEEE Std 1003.1), there is a strtoimax() function we - * could use in case strtoll() doesn't exist... See - * https://www.opengroup.org/onlinepubs/009695399/functions/strtoimax.html - */ - -#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) -# ifdef HAVE_STRTOLL -# define strtooff strtoll -# else -# if defined(_MSC_VER) && (_MSC_VER >= 1300) && (_INTEGRAL_MAX_BITS >= 64) -# if defined(_SAL_VERSION) - _Check_return_ _CRTIMP __int64 __cdecl _strtoi64( - _In_z_ const char *_String, - _Out_opt_ _Deref_post_z_ char **_EndPtr, _In_ int _Radix); -# else - _CRTIMP __int64 __cdecl _strtoi64(const char *_String, - char **_EndPtr, int _Radix); -# endif -# define strtooff _strtoi64 -# else -# define PRIVATE_STRTOOFF 1 -# endif -# endif -#else -# define strtooff strtol -#endif - -#ifdef PRIVATE_STRTOOFF - -/* Range tests can be used for alphanum decoding if characters are consecutive, - like in ASCII. Else an array is scanned. Determine this condition now. */ - -#if('9' - '0') != 9 || ('Z' - 'A') != 25 || ('z' - 'a') != 25 - -#define NO_RANGE_TEST - -static const char valchars[] = - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; -#endif - -static int get_char(char c, int base); - -/** - * Custom version of the strtooff function. This extracts a curl_off_t - * value from the given input string and returns it. - */ -static curl_off_t strtooff(const char *nptr, char **endptr, int base) -{ - char *end; - int is_negative = 0; - int overflow; - int i; - curl_off_t value = 0; - curl_off_t newval; - - /* Skip leading whitespace. */ - end = (char *)nptr; - while(ISBLANK(end[0])) { - end++; - } - - /* Handle the sign, if any. */ - if(end[0] == '-') { - is_negative = 1; - end++; - } - else if(end[0] == '+') { - end++; - } - else if(end[0] == '\0') { - /* We had nothing but perhaps some whitespace -- there was no number. */ - if(endptr) { - *endptr = end; - } - return 0; - } - - /* Handle special beginnings, if present and allowed. */ - if(end[0] == '0' && end[1] == 'x') { - if(base == 16 || base == 0) { - end += 2; - base = 16; - } - } - else if(end[0] == '0') { - if(base == 8 || base == 0) { - end++; - base = 8; - } - } - - /* Matching strtol, if the base is 0 and it doesn't look like - * the number is octal or hex, we assume it's base 10. - */ - if(base == 0) { - base = 10; - } - - /* Loop handling digits. */ - value = 0; - overflow = 0; - for(i = get_char(end[0], base); - i != -1; - end++, i = get_char(end[0], base)) { - newval = base * value + i; - if(newval < value) { - /* We've overflowed. */ - overflow = 1; - break; - } - else - value = newval; - } - - if(!overflow) { - if(is_negative) { - /* Fix the sign. */ - value *= -1; - } - } - else { - if(is_negative) - value = CURL_OFF_T_MIN; - else - value = CURL_OFF_T_MAX; - - errno = ERANGE; - } - - if(endptr) - *endptr = end; - - return value; -} - -/** - * Returns the value of c in the given base, or -1 if c cannot - * be interpreted properly in that base (i.e., is out of range, - * is a null, etc.). - * - * @param c the character to interpret according to base - * @param base the base in which to interpret c - * - * @return the value of c in base, or -1 if c isn't in range - */ -static int get_char(char c, int base) -{ -#ifndef NO_RANGE_TEST - int value = -1; - if(c <= '9' && c >= '0') { - value = c - '0'; - } - else if(c <= 'Z' && c >= 'A') { - value = c - 'A' + 10; - } - else if(c <= 'z' && c >= 'a') { - value = c - 'a' + 10; - } -#else - const char *cp; - int value; - - cp = memchr(valchars, c, 10 + 26 + 26); - - if(!cp) - return -1; - - value = cp - valchars; - - if(value >= 10 + 26) - value -= 26; /* Lowercase. */ -#endif - - if(value >= base) { - value = -1; - } - - return value; -} -#endif /* Only present if we need strtoll, but don't have it. */ - -/* - * Parse a *positive* up to 64 bit number written in ascii. - */ -CURLofft curlx_strtoofft(const char *str, char **endp, int base, - curl_off_t *num) -{ - char *end; - curl_off_t number; - errno = 0; - *num = 0; /* clear by default */ - - while(*str && ISBLANK(*str)) - str++; - if(('-' == *str) || (ISSPACE(*str))) { - if(endp) - *endp = (char *)str; /* didn't actually move */ - return CURL_OFFT_INVAL; /* nothing parsed */ - } - number = strtooff(str, &end, base); - if(endp) - *endp = end; - if(errno == ERANGE) - /* overflow/underflow */ - return CURL_OFFT_FLOW; - else if(str == end) - /* nothing parsed */ - return CURL_OFFT_INVAL; - - *num = number; - return CURL_OFFT_OK; -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 <errno.h>
+#include "curl_setup.h"
+
+#include "strtoofft.h"
+
+/*
+ * NOTE:
+ *
+ * In the ISO C standard (IEEE Std 1003.1), there is a strtoimax() function we
+ * could use in case strtoll() doesn't exist... See
+ * https://www.opengroup.org/onlinepubs/009695399/functions/strtoimax.html
+ */
+
+#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
+# ifdef HAVE_STRTOLL
+# define strtooff strtoll
+# else
+# if defined(_MSC_VER) && (_MSC_VER >= 1300) && (_INTEGRAL_MAX_BITS >= 64)
+# if defined(_SAL_VERSION)
+ _Check_return_ _CRTIMP __int64 __cdecl _strtoi64(
+ _In_z_ const char *_String,
+ _Out_opt_ _Deref_post_z_ char **_EndPtr, _In_ int _Radix);
+# else
+ _CRTIMP __int64 __cdecl _strtoi64(const char *_String,
+ char **_EndPtr, int _Radix);
+# endif
+# define strtooff _strtoi64
+# else
+# define PRIVATE_STRTOOFF 1
+# endif
+# endif
+#else
+# define strtooff strtol
+#endif
+
+#ifdef PRIVATE_STRTOOFF
+
+/* Range tests can be used for alphanum decoding if characters are consecutive,
+ like in ASCII. Else an array is scanned. Determine this condition now. */
+
+#if('9' - '0') != 9 || ('Z' - 'A') != 25 || ('z' - 'a') != 25
+
+#define NO_RANGE_TEST
+
+static const char valchars[] =
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+#endif
+
+static int get_char(char c, int base);
+
+/**
+ * Custom version of the strtooff function. This extracts a curl_off_t
+ * value from the given input string and returns it.
+ */
+static curl_off_t strtooff(const char *nptr, char **endptr, int base)
+{
+ char *end;
+ int is_negative = 0;
+ int overflow;
+ int i;
+ curl_off_t value = 0;
+ curl_off_t newval;
+
+ /* Skip leading whitespace. */
+ end = (char *)nptr;
+ while(ISBLANK(end[0])) {
+ end++;
+ }
+
+ /* Handle the sign, if any. */
+ if(end[0] == '-') {
+ is_negative = 1;
+ end++;
+ }
+ else if(end[0] == '+') {
+ end++;
+ }
+ else if(end[0] == '\0') {
+ /* We had nothing but perhaps some whitespace -- there was no number. */
+ if(endptr) {
+ *endptr = end;
+ }
+ return 0;
+ }
+
+ /* Handle special beginnings, if present and allowed. */
+ if(end[0] == '0' && end[1] == 'x') {
+ if(base == 16 || base == 0) {
+ end += 2;
+ base = 16;
+ }
+ }
+ else if(end[0] == '0') {
+ if(base == 8 || base == 0) {
+ end++;
+ base = 8;
+ }
+ }
+
+ /* Matching strtol, if the base is 0 and it doesn't look like
+ * the number is octal or hex, we assume it's base 10.
+ */
+ if(base == 0) {
+ base = 10;
+ }
+
+ /* Loop handling digits. */
+ value = 0;
+ overflow = 0;
+ for(i = get_char(end[0], base);
+ i != -1;
+ end++, i = get_char(end[0], base)) {
+ newval = base * value + i;
+ if(newval < value) {
+ /* We've overflowed. */
+ overflow = 1;
+ break;
+ }
+ else
+ value = newval;
+ }
+
+ if(!overflow) {
+ if(is_negative) {
+ /* Fix the sign. */
+ value *= -1;
+ }
+ }
+ else {
+ if(is_negative)
+ value = CURL_OFF_T_MIN;
+ else
+ value = CURL_OFF_T_MAX;
+
+ errno = ERANGE;
+ }
+
+ if(endptr)
+ *endptr = end;
+
+ return value;
+}
+
+/**
+ * Returns the value of c in the given base, or -1 if c cannot
+ * be interpreted properly in that base (i.e., is out of range,
+ * is a null, etc.).
+ *
+ * @param c the character to interpret according to base
+ * @param base the base in which to interpret c
+ *
+ * @return the value of c in base, or -1 if c isn't in range
+ */
+static int get_char(char c, int base)
+{
+#ifndef NO_RANGE_TEST
+ int value = -1;
+ if(c <= '9' && c >= '0') {
+ value = c - '0';
+ }
+ else if(c <= 'Z' && c >= 'A') {
+ value = c - 'A' + 10;
+ }
+ else if(c <= 'z' && c >= 'a') {
+ value = c - 'a' + 10;
+ }
+#else
+ const char *cp;
+ int value;
+
+ cp = memchr(valchars, c, 10 + 26 + 26);
+
+ if(!cp)
+ return -1;
+
+ value = cp - valchars;
+
+ if(value >= 10 + 26)
+ value -= 26; /* Lowercase. */
+#endif
+
+ if(value >= base) {
+ value = -1;
+ }
+
+ return value;
+}
+#endif /* Only present if we need strtoll, but don't have it. */
+
+/*
+ * Parse a *positive* up to 64 bit number written in ascii.
+ */
+CURLofft curlx_strtoofft(const char *str, char **endp, int base,
+ curl_off_t *num)
+{
+ char *end;
+ curl_off_t number;
+ errno = 0;
+ *num = 0; /* clear by default */
+ DEBUGASSERT(base); /* starting now, avoid base zero */
+
+ while(*str && ISBLANK(*str))
+ str++;
+ if(('-' == *str) || (ISSPACE(*str))) {
+ if(endp)
+ *endp = (char *)str; /* didn't actually move */
+ return CURL_OFFT_INVAL; /* nothing parsed */
+ }
+ number = strtooff(str, &end, base);
+ if(endp)
+ *endp = end;
+ if(errno == ERANGE)
+ /* overflow/underflow */
+ return CURL_OFFT_FLOW;
+ else if(str == end)
+ /* nothing parsed */
+ return CURL_OFFT_INVAL;
+
+ *num = number;
+ return CURL_OFFT_OK;
+}
diff --git a/libs/libcurl/src/strtoofft.h b/libs/libcurl/src/strtoofft.h index 311dae4403..5f5dc1ac68 100644 --- a/libs/libcurl/src/strtoofft.h +++ b/libs/libcurl/src/strtoofft.h @@ -1,54 +1,54 @@ -#ifndef HEADER_CURL_STRTOOFFT_H -#define HEADER_CURL_STRTOOFFT_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -/* - * Determine which string to integral data type conversion function we use - * to implement string conversion to our curl_off_t integral data type. - * - * Notice that curl_off_t might be 64 or 32 bit wide, and that it might use - * an underlying data type which might be 'long', 'int64_t', 'long long' or - * '__int64' and more remotely other data types. - * - * On systems where the size of curl_off_t is greater than the size of 'long' - * the conversion function to use is strtoll() if it is available, otherwise, - * we emulate its functionality with our own clone. - * - * On systems where the size of curl_off_t is smaller or equal than the size - * of 'long' the conversion function to use is strtol(). - */ - -typedef enum { - CURL_OFFT_OK, /* parsed fine */ - CURL_OFFT_FLOW, /* over or underflow */ - CURL_OFFT_INVAL /* nothing was parsed */ -} CURLofft; - -CURLofft curlx_strtoofft(const char *str, char **endp, int base, - curl_off_t *num); - -#endif /* HEADER_CURL_STRTOOFFT_H */ +#ifndef HEADER_CURL_STRTOOFFT_H
+#define HEADER_CURL_STRTOOFFT_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+/*
+ * Determine which string to integral data type conversion function we use
+ * to implement string conversion to our curl_off_t integral data type.
+ *
+ * Notice that curl_off_t might be 64 or 32 bit wide, and that it might use
+ * an underlying data type which might be 'long', 'int64_t', 'long long' or
+ * '__int64' and more remotely other data types.
+ *
+ * On systems where the size of curl_off_t is greater than the size of 'long'
+ * the conversion function to use is strtoll() if it is available, otherwise,
+ * we emulate its functionality with our own clone.
+ *
+ * On systems where the size of curl_off_t is smaller or equal than the size
+ * of 'long' the conversion function to use is strtol().
+ */
+
+typedef enum {
+ CURL_OFFT_OK, /* parsed fine */
+ CURL_OFFT_FLOW, /* over or underflow */
+ CURL_OFFT_INVAL /* nothing was parsed */
+} CURLofft;
+
+CURLofft curlx_strtoofft(const char *str, char **endp, int base,
+ curl_off_t *num);
+
+#endif /* HEADER_CURL_STRTOOFFT_H */
diff --git a/libs/libcurl/src/system_win32.c b/libs/libcurl/src/system_win32.c index bede9c7dcd..e18c020bea 100644 --- a/libs/libcurl/src/system_win32.c +++ b/libs/libcurl/src/system_win32.c @@ -1,241 +1,241 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.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" - -#if defined(WIN32) - -#include <curl/curl.h> -#include "system_win32.h" -#include "version_win32.h" -#include "curl_sspi.h" -#include "warnless.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -LARGE_INTEGER Curl_freq; -bool Curl_isVistaOrGreater; - -/* Handle of iphlpapp.dll */ -static HMODULE s_hIpHlpApiDll = NULL; - -/* Pointer to the if_nametoindex function */ -IF_NAMETOINDEX_FN Curl_if_nametoindex = NULL; - -/* Curl_win32_init() performs win32 global initialization */ -CURLcode Curl_win32_init(long flags) -{ - /* CURL_GLOBAL_WIN32 controls the *optional* part of the initialization which - is just for Winsock at the moment. Any required win32 initialization - should take place after this block. */ - if(flags & CURL_GLOBAL_WIN32) { -#ifdef USE_WINSOCK - WORD wVersionRequested; - WSADATA wsaData; - int res; - - wVersionRequested = MAKEWORD(2, 2); - res = WSAStartup(wVersionRequested, &wsaData); - - if(res) - /* Tell the user that we couldn't find a usable */ - /* winsock.dll. */ - return CURLE_FAILED_INIT; - - /* Confirm that the Windows Sockets DLL supports what we need.*/ - /* Note that if the DLL supports versions greater */ - /* than wVersionRequested, it will still return */ - /* wVersionRequested in wVersion. wHighVersion contains the */ - /* highest supported version. */ - - if(LOBYTE(wsaData.wVersion) != LOBYTE(wVersionRequested) || - HIBYTE(wsaData.wVersion) != HIBYTE(wVersionRequested) ) { - /* Tell the user that we couldn't find a usable */ - - /* winsock.dll. */ - WSACleanup(); - return CURLE_FAILED_INIT; - } - /* The Windows Sockets DLL is acceptable. Proceed. */ -#elif defined(USE_LWIPSOCK) - lwip_init(); -#endif - } /* CURL_GLOBAL_WIN32 */ - -#ifdef USE_WINDOWS_SSPI - { - CURLcode result = Curl_sspi_global_init(); - if(result) - return result; - } -#endif - - s_hIpHlpApiDll = Curl_load_library(TEXT("iphlpapi.dll")); - if(s_hIpHlpApiDll) { - /* Get the address of the if_nametoindex function */ - IF_NAMETOINDEX_FN pIfNameToIndex = - CURLX_FUNCTION_CAST(IF_NAMETOINDEX_FN, - (GetProcAddress(s_hIpHlpApiDll, "if_nametoindex"))); - - if(pIfNameToIndex) - Curl_if_nametoindex = pIfNameToIndex; - } - - /* curlx_verify_windows_version must be called during init at least once - because it has its own initialization routine. */ - if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL)) { - Curl_isVistaOrGreater = TRUE; - } - else - Curl_isVistaOrGreater = FALSE; - - QueryPerformanceFrequency(&Curl_freq); - return CURLE_OK; -} - -/* Curl_win32_cleanup() is the opposite of Curl_win32_init() */ -void Curl_win32_cleanup(long init_flags) -{ - if(s_hIpHlpApiDll) { - FreeLibrary(s_hIpHlpApiDll); - s_hIpHlpApiDll = NULL; - Curl_if_nametoindex = NULL; - } - -#ifdef USE_WINDOWS_SSPI - Curl_sspi_global_cleanup(); -#endif - - if(init_flags & CURL_GLOBAL_WIN32) { -#ifdef USE_WINSOCK - WSACleanup(); -#endif - } -} - -#if !defined(LOAD_WITH_ALTERED_SEARCH_PATH) -#define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008 -#endif - -#if !defined(LOAD_LIBRARY_SEARCH_SYSTEM32) -#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 -#endif - -/* We use our own typedef here since some headers might lack these */ -typedef HMODULE (APIENTRY *LOADLIBRARYEX_FN)(LPCTSTR, HANDLE, DWORD); - -/* See function definitions in winbase.h */ -#ifdef UNICODE -# ifdef _WIN32_WCE -# define LOADLIBARYEX L"LoadLibraryExW" -# else -# define LOADLIBARYEX "LoadLibraryExW" -# endif -#else -# define LOADLIBARYEX "LoadLibraryExA" -#endif - -/* - * Curl_load_library() - * - * This is used to dynamically load DLLs using the most secure method available - * for the version of Windows that we are running on. - * - * Parameters: - * - * filename [in] - The filename or full path of the DLL to load. If only the - * filename is passed then the DLL will be loaded from the - * Windows system directory. - * - * Returns the handle of the module on success; otherwise NULL. - */ -HMODULE Curl_load_library(LPCTSTR filename) -{ -#ifndef CURL_WINDOWS_APP - HMODULE hModule = NULL; - LOADLIBRARYEX_FN pLoadLibraryEx = NULL; - - /* Get a handle to kernel32 so we can access it's functions at runtime */ - HMODULE hKernel32 = GetModuleHandle(TEXT("kernel32")); - if(!hKernel32) - return NULL; - - /* Attempt to find LoadLibraryEx() which is only available on Windows 2000 - and above */ - pLoadLibraryEx = - CURLX_FUNCTION_CAST(LOADLIBRARYEX_FN, - (GetProcAddress(hKernel32, LOADLIBARYEX))); - - /* Detect if there's already a path in the filename and load the library if - there is. Note: Both back slashes and forward slashes have been supported - since the earlier days of DOS at an API level although they are not - supported by command prompt */ - if(_tcspbrk(filename, TEXT("\\/"))) { - /** !checksrc! disable BANNEDFUNC 1 **/ - hModule = pLoadLibraryEx ? - pLoadLibraryEx(filename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) : - LoadLibrary(filename); - } - /* Detect if KB2533623 is installed, as LOAD_LIBRARY_SEARCH_SYSTEM32 is only - supported on Windows Vista, Windows Server 2008, Windows 7 and Windows - Server 2008 R2 with this patch or natively on Windows 8 and above */ - else if(pLoadLibraryEx && GetProcAddress(hKernel32, "AddDllDirectory")) { - /* Load the DLL from the Windows system directory */ - hModule = pLoadLibraryEx(filename, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); - } - else { - /* Attempt to get the Windows system path */ - UINT systemdirlen = GetSystemDirectory(NULL, 0); - if(systemdirlen) { - /* Allocate space for the full DLL path (Room for the null terminator - is included in systemdirlen) */ - size_t filenamelen = _tcslen(filename); - TCHAR *path = malloc(sizeof(TCHAR) * (systemdirlen + 1 + filenamelen)); - if(path && GetSystemDirectory(path, systemdirlen)) { - /* Calculate the full DLL path */ - _tcscpy(path + _tcslen(path), TEXT("\\")); - _tcscpy(path + _tcslen(path), filename); - - /* Load the DLL from the Windows system directory */ - /** !checksrc! disable BANNEDFUNC 1 **/ - hModule = pLoadLibraryEx ? - pLoadLibraryEx(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) : - LoadLibrary(path); - - } - free(path); - } - } - return hModule; -#else - /* the Universal Windows Platform (UWP) can't do this */ - (void)filename; - return NULL; -#endif -} - -#endif /* WIN32 */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.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"
+
+#if defined(WIN32)
+
+#include <curl/curl.h>
+#include "system_win32.h"
+#include "version_win32.h"
+#include "curl_sspi.h"
+#include "warnless.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+LARGE_INTEGER Curl_freq;
+bool Curl_isVistaOrGreater;
+
+/* Handle of iphlpapp.dll */
+static HMODULE s_hIpHlpApiDll = NULL;
+
+/* Pointer to the if_nametoindex function */
+IF_NAMETOINDEX_FN Curl_if_nametoindex = NULL;
+
+/* Curl_win32_init() performs win32 global initialization */
+CURLcode Curl_win32_init(long flags)
+{
+ /* CURL_GLOBAL_WIN32 controls the *optional* part of the initialization which
+ is just for Winsock at the moment. Any required win32 initialization
+ should take place after this block. */
+ if(flags & CURL_GLOBAL_WIN32) {
+#ifdef USE_WINSOCK
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ int res;
+
+ wVersionRequested = MAKEWORD(2, 2);
+ res = WSAStartup(wVersionRequested, &wsaData);
+
+ if(res)
+ /* Tell the user that we couldn't find a usable */
+ /* winsock.dll. */
+ return CURLE_FAILED_INIT;
+
+ /* Confirm that the Windows Sockets DLL supports what we need.*/
+ /* Note that if the DLL supports versions greater */
+ /* than wVersionRequested, it will still return */
+ /* wVersionRequested in wVersion. wHighVersion contains the */
+ /* highest supported version. */
+
+ if(LOBYTE(wsaData.wVersion) != LOBYTE(wVersionRequested) ||
+ HIBYTE(wsaData.wVersion) != HIBYTE(wVersionRequested) ) {
+ /* Tell the user that we couldn't find a usable */
+
+ /* winsock.dll. */
+ WSACleanup();
+ return CURLE_FAILED_INIT;
+ }
+ /* The Windows Sockets DLL is acceptable. Proceed. */
+#elif defined(USE_LWIPSOCK)
+ lwip_init();
+#endif
+ } /* CURL_GLOBAL_WIN32 */
+
+#ifdef USE_WINDOWS_SSPI
+ {
+ CURLcode result = Curl_sspi_global_init();
+ if(result)
+ return result;
+ }
+#endif
+
+ s_hIpHlpApiDll = Curl_load_library(TEXT("iphlpapi.dll"));
+ if(s_hIpHlpApiDll) {
+ /* Get the address of the if_nametoindex function */
+ IF_NAMETOINDEX_FN pIfNameToIndex =
+ CURLX_FUNCTION_CAST(IF_NAMETOINDEX_FN,
+ (GetProcAddress(s_hIpHlpApiDll, "if_nametoindex")));
+
+ if(pIfNameToIndex)
+ Curl_if_nametoindex = pIfNameToIndex;
+ }
+
+ /* curlx_verify_windows_version must be called during init at least once
+ because it has its own initialization routine. */
+ if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL)) {
+ Curl_isVistaOrGreater = TRUE;
+ }
+ else
+ Curl_isVistaOrGreater = FALSE;
+
+ QueryPerformanceFrequency(&Curl_freq);
+ return CURLE_OK;
+}
+
+/* Curl_win32_cleanup() is the opposite of Curl_win32_init() */
+void Curl_win32_cleanup(long init_flags)
+{
+ if(s_hIpHlpApiDll) {
+ FreeLibrary(s_hIpHlpApiDll);
+ s_hIpHlpApiDll = NULL;
+ Curl_if_nametoindex = NULL;
+ }
+
+#ifdef USE_WINDOWS_SSPI
+ Curl_sspi_global_cleanup();
+#endif
+
+ if(init_flags & CURL_GLOBAL_WIN32) {
+#ifdef USE_WINSOCK
+ WSACleanup();
+#endif
+ }
+}
+
+#if !defined(LOAD_WITH_ALTERED_SEARCH_PATH)
+#define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008
+#endif
+
+#if !defined(LOAD_LIBRARY_SEARCH_SYSTEM32)
+#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800
+#endif
+
+/* We use our own typedef here since some headers might lack these */
+typedef HMODULE (APIENTRY *LOADLIBRARYEX_FN)(LPCTSTR, HANDLE, DWORD);
+
+/* See function definitions in winbase.h */
+#ifdef UNICODE
+# ifdef _WIN32_WCE
+# define LOADLIBARYEX L"LoadLibraryExW"
+# else
+# define LOADLIBARYEX "LoadLibraryExW"
+# endif
+#else
+# define LOADLIBARYEX "LoadLibraryExA"
+#endif
+
+/*
+ * Curl_load_library()
+ *
+ * This is used to dynamically load DLLs using the most secure method available
+ * for the version of Windows that we are running on.
+ *
+ * Parameters:
+ *
+ * filename [in] - The filename or full path of the DLL to load. If only the
+ * filename is passed then the DLL will be loaded from the
+ * Windows system directory.
+ *
+ * Returns the handle of the module on success; otherwise NULL.
+ */
+HMODULE Curl_load_library(LPCTSTR filename)
+{
+#ifndef CURL_WINDOWS_APP
+ HMODULE hModule = NULL;
+ LOADLIBRARYEX_FN pLoadLibraryEx = NULL;
+
+ /* Get a handle to kernel32 so we can access it's functions at runtime */
+ HMODULE hKernel32 = GetModuleHandle(TEXT("kernel32"));
+ if(!hKernel32)
+ return NULL;
+
+ /* Attempt to find LoadLibraryEx() which is only available on Windows 2000
+ and above */
+ pLoadLibraryEx =
+ CURLX_FUNCTION_CAST(LOADLIBRARYEX_FN,
+ (GetProcAddress(hKernel32, LOADLIBARYEX)));
+
+ /* Detect if there's already a path in the filename and load the library if
+ there is. Note: Both back slashes and forward slashes have been supported
+ since the earlier days of DOS at an API level although they are not
+ supported by command prompt */
+ if(_tcspbrk(filename, TEXT("\\/"))) {
+ /** !checksrc! disable BANNEDFUNC 1 **/
+ hModule = pLoadLibraryEx ?
+ pLoadLibraryEx(filename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) :
+ LoadLibrary(filename);
+ }
+ /* Detect if KB2533623 is installed, as LOAD_LIBRARY_SEARCH_SYSTEM32 is only
+ supported on Windows Vista, Windows Server 2008, Windows 7 and Windows
+ Server 2008 R2 with this patch or natively on Windows 8 and above */
+ else if(pLoadLibraryEx && GetProcAddress(hKernel32, "AddDllDirectory")) {
+ /* Load the DLL from the Windows system directory */
+ hModule = pLoadLibraryEx(filename, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+ }
+ else {
+ /* Attempt to get the Windows system path */
+ UINT systemdirlen = GetSystemDirectory(NULL, 0);
+ if(systemdirlen) {
+ /* Allocate space for the full DLL path (Room for the null terminator
+ is included in systemdirlen) */
+ size_t filenamelen = _tcslen(filename);
+ TCHAR *path = malloc(sizeof(TCHAR) * (systemdirlen + 1 + filenamelen));
+ if(path && GetSystemDirectory(path, systemdirlen)) {
+ /* Calculate the full DLL path */
+ _tcscpy(path + _tcslen(path), TEXT("\\"));
+ _tcscpy(path + _tcslen(path), filename);
+
+ /* Load the DLL from the Windows system directory */
+ /** !checksrc! disable BANNEDFUNC 1 **/
+ hModule = pLoadLibraryEx ?
+ pLoadLibraryEx(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) :
+ LoadLibrary(path);
+
+ }
+ free(path);
+ }
+ }
+ return hModule;
+#else
+ /* the Universal Windows Platform (UWP) can't do this */
+ (void)filename;
+ return NULL;
+#endif
+}
+
+#endif /* WIN32 */
diff --git a/libs/libcurl/src/system_win32.h b/libs/libcurl/src/system_win32.h index 167804e3c5..b2101c6306 100644 --- a/libs/libcurl/src/system_win32.h +++ b/libs/libcurl/src/system_win32.h @@ -1,48 +1,48 @@ -#ifndef HEADER_CURL_SYSTEM_WIN32_H -#define HEADER_CURL_SYSTEM_WIN32_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.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" - -#if defined(WIN32) - -extern LARGE_INTEGER Curl_freq; -extern bool Curl_isVistaOrGreater; - -CURLcode Curl_win32_init(long flags); -void Curl_win32_cleanup(long init_flags); - -/* We use our own typedef here since some headers might lack this */ -typedef unsigned int(WINAPI *IF_NAMETOINDEX_FN)(const char *); - -/* This is used instead of if_nametoindex if available on Windows */ -extern IF_NAMETOINDEX_FN Curl_if_nametoindex; - -/* This is used to dynamically load DLLs */ -HMODULE Curl_load_library(LPCTSTR filename); - -#endif /* WIN32 */ - -#endif /* HEADER_CURL_SYSTEM_WIN32_H */ +#ifndef HEADER_CURL_SYSTEM_WIN32_H
+#define HEADER_CURL_SYSTEM_WIN32_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.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"
+
+#if defined(WIN32)
+
+extern LARGE_INTEGER Curl_freq;
+extern bool Curl_isVistaOrGreater;
+
+CURLcode Curl_win32_init(long flags);
+void Curl_win32_cleanup(long init_flags);
+
+/* We use our own typedef here since some headers might lack this */
+typedef unsigned int(WINAPI *IF_NAMETOINDEX_FN)(const char *);
+
+/* This is used instead of if_nametoindex if available on Windows */
+extern IF_NAMETOINDEX_FN Curl_if_nametoindex;
+
+/* This is used to dynamically load DLLs */
+HMODULE Curl_load_library(LPCTSTR filename);
+
+#endif /* WIN32 */
+
+#endif /* HEADER_CURL_SYSTEM_WIN32_H */
diff --git a/libs/libcurl/src/telnet.c b/libs/libcurl/src/telnet.c index 923c7f82bd..f012ffec1c 100644 --- a/libs/libcurl/src/telnet.c +++ b/libs/libcurl/src/telnet.c @@ -1,1591 +1,1597 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_TELNET - -#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 HAVE_NET_IF_H -#include <net/if.h> -#endif -#ifdef HAVE_SYS_IOCTL_H -#include <sys/ioctl.h> -#endif - -#ifdef HAVE_SYS_PARAM_H -#include <sys/param.h> -#endif - -#include "urldata.h" -#include <curl/curl.h> -#include "transfer.h" -#include "sendf.h" -#include "telnet.h" -#include "connect.h" -#include "progress.h" -#include "system_win32.h" -#include "arpa_telnet.h" -#include "select.h" -#include "strcase.h" -#include "warnless.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#define SUBBUFSIZE 512 - -#define CURL_SB_CLEAR(x) x->subpointer = x->subbuffer -#define CURL_SB_TERM(x) \ - do { \ - x->subend = x->subpointer; \ - CURL_SB_CLEAR(x); \ - } while(0) -#define CURL_SB_ACCUM(x,c) \ - do { \ - if(x->subpointer < (x->subbuffer + sizeof(x->subbuffer))) \ - *x->subpointer++ = (c); \ - } while(0) - -#define CURL_SB_GET(x) ((*x->subpointer++)&0xff) -#define CURL_SB_LEN(x) (x->subend - x->subpointer) - -/* For posterity: -#define CURL_SB_PEEK(x) ((*x->subpointer)&0xff) -#define CURL_SB_EOF(x) (x->subpointer >= x->subend) */ - -#ifdef CURL_DISABLE_VERBOSE_STRINGS -#define printoption(a,b,c,d) Curl_nop_stmt -#endif - -static -CURLcode telrcv(struct Curl_easy *data, - const unsigned char *inbuf, /* Data received from socket */ - ssize_t count); /* Number of bytes received */ - -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static void printoption(struct Curl_easy *data, - const char *direction, - int cmd, int option); -#endif - -static void negotiate(struct Curl_easy *data); -static void send_negotiation(struct Curl_easy *data, int cmd, int option); -static void set_local_option(struct Curl_easy *data, - int option, int newstate); -static void set_remote_option(struct Curl_easy *data, - int option, int newstate); - -static void printsub(struct Curl_easy *data, - int direction, unsigned char *pointer, - size_t length); -static void suboption(struct Curl_easy *data); -static void sendsuboption(struct Curl_easy *data, int option); - -static CURLcode telnet_do(struct Curl_easy *data, bool *done); -static CURLcode telnet_done(struct Curl_easy *data, - CURLcode, bool premature); -static CURLcode send_telnet_data(struct Curl_easy *data, - char *buffer, ssize_t nread); - -/* For negotiation compliant to RFC 1143 */ -#define CURL_NO 0 -#define CURL_YES 1 -#define CURL_WANTYES 2 -#define CURL_WANTNO 3 - -#define CURL_EMPTY 0 -#define CURL_OPPOSITE 1 - -/* - * Telnet receiver states for fsm - */ -typedef enum -{ - CURL_TS_DATA = 0, - CURL_TS_IAC, - CURL_TS_WILL, - CURL_TS_WONT, - CURL_TS_DO, - CURL_TS_DONT, - CURL_TS_CR, - CURL_TS_SB, /* sub-option collection */ - CURL_TS_SE /* looking for sub-option end */ -} TelnetReceive; - -struct TELNET { - int please_negotiate; - int already_negotiated; - int us[256]; - int usq[256]; - int us_preferred[256]; - int him[256]; - int himq[256]; - int him_preferred[256]; - int subnegotiation[256]; - char subopt_ttype[32]; /* Set with suboption TTYPE */ - char subopt_xdisploc[128]; /* Set with suboption XDISPLOC */ - unsigned short subopt_wsx; /* Set with suboption NAWS */ - unsigned short subopt_wsy; /* Set with suboption NAWS */ - TelnetReceive telrcv_state; - struct curl_slist *telnet_vars; /* Environment variables */ - - /* suboptions */ - unsigned char subbuffer[SUBBUFSIZE]; - unsigned char *subpointer, *subend; /* buffer for sub-options */ -}; - - -/* - * TELNET protocol handler. - */ - -const struct Curl_handler Curl_handler_telnet = { - "TELNET", /* scheme */ - ZERO_NULL, /* setup_connection */ - telnet_do, /* do_it */ - telnet_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_TELNET, /* defport */ - CURLPROTO_TELNET, /* protocol */ - CURLPROTO_TELNET, /* family */ - PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */ -}; - - -static -CURLcode init_telnet(struct Curl_easy *data) -{ - struct TELNET *tn; - - tn = calloc(1, sizeof(struct TELNET)); - if(!tn) - return CURLE_OUT_OF_MEMORY; - - data->req.p.telnet = tn; /* make us known */ - - tn->telrcv_state = CURL_TS_DATA; - - /* Init suboptions */ - CURL_SB_CLEAR(tn); - - /* Set the options we want by default */ - tn->us_preferred[CURL_TELOPT_SGA] = CURL_YES; - tn->him_preferred[CURL_TELOPT_SGA] = CURL_YES; - - /* To be compliant with previous releases of libcurl - we enable this option by default. This behavior - can be changed thanks to the "BINARY" option in - CURLOPT_TELNETOPTIONS - */ - tn->us_preferred[CURL_TELOPT_BINARY] = CURL_YES; - tn->him_preferred[CURL_TELOPT_BINARY] = CURL_YES; - - /* We must allow the server to echo what we sent - but it is not necessary to request the server - to do so (it might forces the server to close - the connection). Hence, we ignore ECHO in the - negotiate function - */ - tn->him_preferred[CURL_TELOPT_ECHO] = CURL_YES; - - /* Set the subnegotiation fields to send information - just after negotiation passed (do/will) - - Default values are (0,0) initialized by calloc. - According to the RFC1013 it is valid: - A value equal to zero is acceptable for the width (or height), - and means that no character width (or height) is being sent. - In this case, the width (or height) that will be assumed by the - Telnet server is operating system specific (it will probably be - based upon the terminal type information that may have been sent - using the TERMINAL TYPE Telnet option). */ - tn->subnegotiation[CURL_TELOPT_NAWS] = CURL_YES; - return CURLE_OK; -} - -static void negotiate(struct Curl_easy *data) -{ - int i; - struct TELNET *tn = data->req.p.telnet; - - for(i = 0; i < CURL_NTELOPTS; i++) { - if(i == CURL_TELOPT_ECHO) - continue; - - if(tn->us_preferred[i] == CURL_YES) - set_local_option(data, i, CURL_YES); - - if(tn->him_preferred[i] == CURL_YES) - set_remote_option(data, i, CURL_YES); - } -} - -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static void printoption(struct Curl_easy *data, - const char *direction, int cmd, int option) -{ - if(data->set.verbose) { - if(cmd == CURL_IAC) { - if(CURL_TELCMD_OK(option)) - infof(data, "%s IAC %s", direction, CURL_TELCMD(option)); - else - infof(data, "%s IAC %d", direction, option); - } - else { - const char *fmt = (cmd == CURL_WILL) ? "WILL" : - (cmd == CURL_WONT) ? "WONT" : - (cmd == CURL_DO) ? "DO" : - (cmd == CURL_DONT) ? "DONT" : 0; - if(fmt) { - const char *opt; - if(CURL_TELOPT_OK(option)) - opt = CURL_TELOPT(option); - else if(option == CURL_TELOPT_EXOPL) - opt = "EXOPL"; - else - opt = NULL; - - if(opt) - infof(data, "%s %s %s", direction, fmt, opt); - else - infof(data, "%s %s %d", direction, fmt, option); - } - else - infof(data, "%s %d %d", direction, cmd, option); - } - } -} -#endif - -static void send_negotiation(struct Curl_easy *data, int cmd, int option) -{ - unsigned char buf[3]; - ssize_t bytes_written; - struct connectdata *conn = data->conn; - - buf[0] = CURL_IAC; - buf[1] = (unsigned char)cmd; - buf[2] = (unsigned char)option; - - bytes_written = swrite(conn->sock[FIRSTSOCKET], buf, 3); - if(bytes_written < 0) { - int err = SOCKERRNO; - failf(data,"Sending data failed (%d)",err); - } - - printoption(data, "SENT", cmd, option); -} - -static -void set_remote_option(struct Curl_easy *data, int option, int newstate) -{ - struct TELNET *tn = data->req.p.telnet; - if(newstate == CURL_YES) { - switch(tn->him[option]) { - case CURL_NO: - tn->him[option] = CURL_WANTYES; - send_negotiation(data, CURL_DO, option); - break; - - case CURL_YES: - /* Already enabled */ - break; - - case CURL_WANTNO: - switch(tn->himq[option]) { - case CURL_EMPTY: - /* Already negotiating for CURL_YES, queue the request */ - tn->himq[option] = CURL_OPPOSITE; - break; - case CURL_OPPOSITE: - /* Error: already queued an enable request */ - break; - } - break; - - case CURL_WANTYES: - switch(tn->himq[option]) { - case CURL_EMPTY: - /* Error: already negotiating for enable */ - break; - case CURL_OPPOSITE: - tn->himq[option] = CURL_EMPTY; - break; - } - break; - } - } - else { /* NO */ - switch(tn->him[option]) { - case CURL_NO: - /* Already disabled */ - break; - - case CURL_YES: - tn->him[option] = CURL_WANTNO; - send_negotiation(data, CURL_DONT, option); - break; - - case CURL_WANTNO: - switch(tn->himq[option]) { - case CURL_EMPTY: - /* Already negotiating for NO */ - break; - case CURL_OPPOSITE: - tn->himq[option] = CURL_EMPTY; - break; - } - break; - - case CURL_WANTYES: - switch(tn->himq[option]) { - case CURL_EMPTY: - tn->himq[option] = CURL_OPPOSITE; - break; - case CURL_OPPOSITE: - break; - } - break; - } - } -} - -static -void rec_will(struct Curl_easy *data, int option) -{ - struct TELNET *tn = data->req.p.telnet; - switch(tn->him[option]) { - case CURL_NO: - if(tn->him_preferred[option] == CURL_YES) { - tn->him[option] = CURL_YES; - send_negotiation(data, CURL_DO, option); - } - else - send_negotiation(data, CURL_DONT, option); - - break; - - case CURL_YES: - /* Already enabled */ - break; - - case CURL_WANTNO: - switch(tn->himq[option]) { - case CURL_EMPTY: - /* Error: DONT answered by WILL */ - tn->him[option] = CURL_NO; - break; - case CURL_OPPOSITE: - /* Error: DONT answered by WILL */ - tn->him[option] = CURL_YES; - tn->himq[option] = CURL_EMPTY; - break; - } - break; - - case CURL_WANTYES: - switch(tn->himq[option]) { - case CURL_EMPTY: - tn->him[option] = CURL_YES; - break; - case CURL_OPPOSITE: - tn->him[option] = CURL_WANTNO; - tn->himq[option] = CURL_EMPTY; - send_negotiation(data, CURL_DONT, option); - break; - } - break; - } -} - -static -void rec_wont(struct Curl_easy *data, int option) -{ - struct TELNET *tn = data->req.p.telnet; - switch(tn->him[option]) { - case CURL_NO: - /* Already disabled */ - break; - - case CURL_YES: - tn->him[option] = CURL_NO; - send_negotiation(data, CURL_DONT, option); - break; - - case CURL_WANTNO: - switch(tn->himq[option]) { - case CURL_EMPTY: - tn->him[option] = CURL_NO; - break; - - case CURL_OPPOSITE: - tn->him[option] = CURL_WANTYES; - tn->himq[option] = CURL_EMPTY; - send_negotiation(data, CURL_DO, option); - break; - } - break; - - case CURL_WANTYES: - switch(tn->himq[option]) { - case CURL_EMPTY: - tn->him[option] = CURL_NO; - break; - case CURL_OPPOSITE: - tn->him[option] = CURL_NO; - tn->himq[option] = CURL_EMPTY; - break; - } - break; - } -} - -static void -set_local_option(struct Curl_easy *data, int option, int newstate) -{ - struct TELNET *tn = data->req.p.telnet; - if(newstate == CURL_YES) { - switch(tn->us[option]) { - case CURL_NO: - tn->us[option] = CURL_WANTYES; - send_negotiation(data, CURL_WILL, option); - break; - - case CURL_YES: - /* Already enabled */ - break; - - case CURL_WANTNO: - switch(tn->usq[option]) { - case CURL_EMPTY: - /* Already negotiating for CURL_YES, queue the request */ - tn->usq[option] = CURL_OPPOSITE; - break; - case CURL_OPPOSITE: - /* Error: already queued an enable request */ - break; - } - break; - - case CURL_WANTYES: - switch(tn->usq[option]) { - case CURL_EMPTY: - /* Error: already negotiating for enable */ - break; - case CURL_OPPOSITE: - tn->usq[option] = CURL_EMPTY; - break; - } - break; - } - } - else { /* NO */ - switch(tn->us[option]) { - case CURL_NO: - /* Already disabled */ - break; - - case CURL_YES: - tn->us[option] = CURL_WANTNO; - send_negotiation(data, CURL_WONT, option); - break; - - case CURL_WANTNO: - switch(tn->usq[option]) { - case CURL_EMPTY: - /* Already negotiating for NO */ - break; - case CURL_OPPOSITE: - tn->usq[option] = CURL_EMPTY; - break; - } - break; - - case CURL_WANTYES: - switch(tn->usq[option]) { - case CURL_EMPTY: - tn->usq[option] = CURL_OPPOSITE; - break; - case CURL_OPPOSITE: - break; - } - break; - } - } -} - -static -void rec_do(struct Curl_easy *data, int option) -{ - struct TELNET *tn = data->req.p.telnet; - switch(tn->us[option]) { - case CURL_NO: - if(tn->us_preferred[option] == CURL_YES) { - tn->us[option] = CURL_YES; - send_negotiation(data, CURL_WILL, option); - if(tn->subnegotiation[option] == CURL_YES) - /* transmission of data option */ - sendsuboption(data, option); - } - else if(tn->subnegotiation[option] == CURL_YES) { - /* send information to achieve this option*/ - tn->us[option] = CURL_YES; - send_negotiation(data, CURL_WILL, option); - sendsuboption(data, option); - } - else - send_negotiation(data, CURL_WONT, option); - break; - - case CURL_YES: - /* Already enabled */ - break; - - case CURL_WANTNO: - switch(tn->usq[option]) { - case CURL_EMPTY: - /* Error: DONT answered by WILL */ - tn->us[option] = CURL_NO; - break; - case CURL_OPPOSITE: - /* Error: DONT answered by WILL */ - tn->us[option] = CURL_YES; - tn->usq[option] = CURL_EMPTY; - break; - } - break; - - case CURL_WANTYES: - switch(tn->usq[option]) { - case CURL_EMPTY: - tn->us[option] = CURL_YES; - if(tn->subnegotiation[option] == CURL_YES) { - /* transmission of data option */ - sendsuboption(data, option); - } - break; - case CURL_OPPOSITE: - tn->us[option] = CURL_WANTNO; - tn->himq[option] = CURL_EMPTY; - send_negotiation(data, CURL_WONT, option); - break; - } - break; - } -} - -static -void rec_dont(struct Curl_easy *data, int option) -{ - struct TELNET *tn = data->req.p.telnet; - switch(tn->us[option]) { - case CURL_NO: - /* Already disabled */ - break; - - case CURL_YES: - tn->us[option] = CURL_NO; - send_negotiation(data, CURL_WONT, option); - break; - - case CURL_WANTNO: - switch(tn->usq[option]) { - case CURL_EMPTY: - tn->us[option] = CURL_NO; - break; - - case CURL_OPPOSITE: - tn->us[option] = CURL_WANTYES; - tn->usq[option] = CURL_EMPTY; - send_negotiation(data, CURL_WILL, option); - break; - } - break; - - case CURL_WANTYES: - switch(tn->usq[option]) { - case CURL_EMPTY: - tn->us[option] = CURL_NO; - break; - case CURL_OPPOSITE: - tn->us[option] = CURL_NO; - tn->usq[option] = CURL_EMPTY; - break; - } - break; - } -} - - -static void printsub(struct Curl_easy *data, - int direction, /* '<' or '>' */ - unsigned char *pointer, /* where suboption data is */ - size_t length) /* length of suboption data */ -{ - if(data->set.verbose) { - unsigned int i = 0; - if(direction) { - infof(data, "%s IAC SB ", (direction == '<')? "RCVD":"SENT"); - if(length >= 3) { - int j; - - i = pointer[length-2]; - j = pointer[length-1]; - - if(i != CURL_IAC || j != CURL_SE) { - infof(data, "(terminated by "); - if(CURL_TELOPT_OK(i)) - infof(data, "%s ", CURL_TELOPT(i)); - else if(CURL_TELCMD_OK(i)) - infof(data, "%s ", CURL_TELCMD(i)); - else - infof(data, "%u ", i); - if(CURL_TELOPT_OK(j)) - infof(data, "%s", CURL_TELOPT(j)); - else if(CURL_TELCMD_OK(j)) - infof(data, "%s", CURL_TELCMD(j)); - else - infof(data, "%d", j); - infof(data, ", not IAC SE) "); - } - } - length -= 2; - } - if(length < 1) { - infof(data, "(Empty suboption?)"); - return; - } - - if(CURL_TELOPT_OK(pointer[0])) { - switch(pointer[0]) { - case CURL_TELOPT_TTYPE: - case CURL_TELOPT_XDISPLOC: - case CURL_TELOPT_NEW_ENVIRON: - case CURL_TELOPT_NAWS: - infof(data, "%s", CURL_TELOPT(pointer[0])); - break; - default: - infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0])); - break; - } - } - else - infof(data, "%d (unknown)", pointer[i]); - - switch(pointer[0]) { - case CURL_TELOPT_NAWS: - if(length > 4) - infof(data, "Width: %d ; Height: %d", (pointer[1]<<8) | pointer[2], - (pointer[3]<<8) | pointer[4]); - break; - default: - switch(pointer[1]) { - case CURL_TELQUAL_IS: - infof(data, " IS"); - break; - case CURL_TELQUAL_SEND: - infof(data, " SEND"); - break; - case CURL_TELQUAL_INFO: - infof(data, " INFO/REPLY"); - break; - case CURL_TELQUAL_NAME: - infof(data, " NAME"); - break; - } - - switch(pointer[0]) { - case CURL_TELOPT_TTYPE: - case CURL_TELOPT_XDISPLOC: - pointer[length] = 0; - infof(data, " \"%s\"", &pointer[2]); - break; - case CURL_TELOPT_NEW_ENVIRON: - if(pointer[1] == CURL_TELQUAL_IS) { - infof(data, " "); - for(i = 3; i < length; i++) { - switch(pointer[i]) { - case CURL_NEW_ENV_VAR: - infof(data, ", "); - break; - case CURL_NEW_ENV_VALUE: - infof(data, " = "); - break; - default: - infof(data, "%c", pointer[i]); - break; - } - } - } - break; - default: - for(i = 2; i < length; i++) - infof(data, " %.2x", pointer[i]); - break; - } - } - } -} - -static CURLcode check_telnet_options(struct Curl_easy *data) -{ - struct curl_slist *head; - struct curl_slist *beg; - char option_keyword[128] = ""; - char option_arg[256] = ""; - struct TELNET *tn = data->req.p.telnet; - struct connectdata *conn = data->conn; - CURLcode result = CURLE_OK; - int binary_option; - - /* Add the user name as an environment variable if it - was given on the command line */ - if(data->state.aptr.user) { - msnprintf(option_arg, sizeof(option_arg), "USER,%s", conn->user); - beg = curl_slist_append(tn->telnet_vars, option_arg); - if(!beg) { - curl_slist_free_all(tn->telnet_vars); - tn->telnet_vars = NULL; - return CURLE_OUT_OF_MEMORY; - } - tn->telnet_vars = beg; - tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES; - } - - for(head = data->set.telnet_options; head; head = head->next) { - if(sscanf(head->data, "%127[^= ]%*[ =]%255s", - option_keyword, option_arg) == 2) { - - /* Terminal type */ - if(strcasecompare(option_keyword, "TTYPE")) { - strncpy(tn->subopt_ttype, option_arg, 31); - tn->subopt_ttype[31] = 0; /* String termination */ - tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES; - continue; - } - - /* Display variable */ - if(strcasecompare(option_keyword, "XDISPLOC")) { - strncpy(tn->subopt_xdisploc, option_arg, 127); - tn->subopt_xdisploc[127] = 0; /* String termination */ - tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES; - continue; - } - - /* Environment variable */ - if(strcasecompare(option_keyword, "NEW_ENV")) { - beg = curl_slist_append(tn->telnet_vars, option_arg); - if(!beg) { - result = CURLE_OUT_OF_MEMORY; - break; - } - tn->telnet_vars = beg; - tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES; - continue; - } - - /* Window Size */ - if(strcasecompare(option_keyword, "WS")) { - if(sscanf(option_arg, "%hu%*[xX]%hu", - &tn->subopt_wsx, &tn->subopt_wsy) == 2) - tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES; - else { - failf(data, "Syntax error in telnet option: %s", head->data); - result = CURLE_SETOPT_OPTION_SYNTAX; - break; - } - continue; - } - - /* To take care or not of the 8th bit in data exchange */ - if(strcasecompare(option_keyword, "BINARY")) { - binary_option = atoi(option_arg); - if(binary_option != 1) { - tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO; - tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO; - } - continue; - } - - failf(data, "Unknown telnet option %s", head->data); - result = CURLE_UNKNOWN_OPTION; - break; - } - failf(data, "Syntax error in telnet option: %s", head->data); - result = CURLE_SETOPT_OPTION_SYNTAX; - break; - } - - if(result) { - curl_slist_free_all(tn->telnet_vars); - tn->telnet_vars = NULL; - } - - return result; -} - -/* - * suboption() - * - * Look at the sub-option buffer, and try to be helpful to the other - * side. - */ - -static void suboption(struct Curl_easy *data) -{ - struct curl_slist *v; - unsigned char temp[2048]; - ssize_t bytes_written; - size_t len; - int err; - char varname[128] = ""; - char varval[128] = ""; - struct TELNET *tn = data->req.p.telnet; - struct connectdata *conn = data->conn; - - printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn) + 2); - switch(CURL_SB_GET(tn)) { - case CURL_TELOPT_TTYPE: - len = strlen(tn->subopt_ttype) + 4 + 2; - msnprintf((char *)temp, sizeof(temp), - "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_TTYPE, - CURL_TELQUAL_IS, tn->subopt_ttype, CURL_IAC, CURL_SE); - bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); - if(bytes_written < 0) { - err = SOCKERRNO; - failf(data,"Sending data failed (%d)",err); - } - printsub(data, '>', &temp[2], len-2); - break; - case CURL_TELOPT_XDISPLOC: - len = strlen(tn->subopt_xdisploc) + 4 + 2; - msnprintf((char *)temp, sizeof(temp), - "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_XDISPLOC, - CURL_TELQUAL_IS, tn->subopt_xdisploc, CURL_IAC, CURL_SE); - bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); - if(bytes_written < 0) { - err = SOCKERRNO; - failf(data,"Sending data failed (%d)",err); - } - printsub(data, '>', &temp[2], len-2); - break; - case CURL_TELOPT_NEW_ENVIRON: - msnprintf((char *)temp, sizeof(temp), - "%c%c%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_NEW_ENVIRON, - CURL_TELQUAL_IS); - len = 4; - - for(v = tn->telnet_vars; v; v = v->next) { - size_t tmplen = (strlen(v->data) + 1); - /* Add the variable only if it fits */ - if(len + tmplen < (int)sizeof(temp)-6) { - int rv; - char sep[2] = ""; - varval[0] = 0; - rv = sscanf(v->data, "%127[^,]%1[,]%127s", varname, sep, varval); - if(rv == 1) - len += msnprintf((char *)&temp[len], sizeof(temp) - len, - "%c%s", CURL_NEW_ENV_VAR, varname); - else if(rv >= 2) - len += msnprintf((char *)&temp[len], sizeof(temp) - len, - "%c%s%c%s", CURL_NEW_ENV_VAR, varname, - CURL_NEW_ENV_VALUE, varval); - } - } - msnprintf((char *)&temp[len], sizeof(temp) - len, - "%c%c", CURL_IAC, CURL_SE); - len += 2; - bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); - if(bytes_written < 0) { - err = SOCKERRNO; - failf(data,"Sending data failed (%d)",err); - } - printsub(data, '>', &temp[2], len-2); - break; - } - return; -} - - -/* - * sendsuboption() - * - * Send suboption information to the server side. - */ - -static void sendsuboption(struct Curl_easy *data, int option) -{ - ssize_t bytes_written; - int err; - unsigned short x, y; - unsigned char *uc1, *uc2; - struct TELNET *tn = data->req.p.telnet; - struct connectdata *conn = data->conn; - - switch(option) { - case CURL_TELOPT_NAWS: - /* We prepare data to be sent */ - CURL_SB_CLEAR(tn); - CURL_SB_ACCUM(tn, CURL_IAC); - CURL_SB_ACCUM(tn, CURL_SB); - CURL_SB_ACCUM(tn, CURL_TELOPT_NAWS); - /* We must deal either with little or big endian processors */ - /* Window size must be sent according to the 'network order' */ - x = htons(tn->subopt_wsx); - y = htons(tn->subopt_wsy); - uc1 = (unsigned char *)&x; - uc2 = (unsigned char *)&y; - CURL_SB_ACCUM(tn, uc1[0]); - CURL_SB_ACCUM(tn, uc1[1]); - CURL_SB_ACCUM(tn, uc2[0]); - CURL_SB_ACCUM(tn, uc2[1]); - - CURL_SB_ACCUM(tn, CURL_IAC); - CURL_SB_ACCUM(tn, CURL_SE); - CURL_SB_TERM(tn); - /* data suboption is now ready */ - - printsub(data, '>', (unsigned char *)tn->subbuffer + 2, - CURL_SB_LEN(tn)-2); - - /* we send the header of the suboption... */ - bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer, 3); - if(bytes_written < 0) { - err = SOCKERRNO; - failf(data, "Sending data failed (%d)", err); - } - /* ... then the window size with the send_telnet_data() function - to deal with 0xFF cases ... */ - send_telnet_data(data, (char *)tn->subbuffer + 3, 4); - /* ... and the footer */ - bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer + 7, 2); - if(bytes_written < 0) { - err = SOCKERRNO; - failf(data, "Sending data failed (%d)", err); - } - break; - } -} - - -static -CURLcode telrcv(struct Curl_easy *data, - const unsigned char *inbuf, /* Data received from socket */ - ssize_t count) /* Number of bytes received */ -{ - unsigned char c; - CURLcode result; - int in = 0; - int startwrite = -1; - struct TELNET *tn = data->req.p.telnet; - -#define startskipping() \ - if(startwrite >= 0) { \ - result = Curl_client_write(data, \ - CLIENTWRITE_BODY, \ - (char *)&inbuf[startwrite], \ - in-startwrite); \ - if(result) \ - return result; \ - } \ - startwrite = -1 - -#define writebyte() \ - if(startwrite < 0) \ - startwrite = in - -#define bufferflush() startskipping() - - while(count--) { - c = inbuf[in]; - - switch(tn->telrcv_state) { - case CURL_TS_CR: - tn->telrcv_state = CURL_TS_DATA; - if(c == '\0') { - startskipping(); - break; /* Ignore \0 after CR */ - } - writebyte(); - break; - - case CURL_TS_DATA: - if(c == CURL_IAC) { - tn->telrcv_state = CURL_TS_IAC; - startskipping(); - break; - } - else if(c == '\r') - tn->telrcv_state = CURL_TS_CR; - writebyte(); - break; - - case CURL_TS_IAC: - process_iac: - DEBUGASSERT(startwrite < 0); - switch(c) { - case CURL_WILL: - tn->telrcv_state = CURL_TS_WILL; - break; - case CURL_WONT: - tn->telrcv_state = CURL_TS_WONT; - break; - case CURL_DO: - tn->telrcv_state = CURL_TS_DO; - break; - case CURL_DONT: - tn->telrcv_state = CURL_TS_DONT; - break; - case CURL_SB: - CURL_SB_CLEAR(tn); - tn->telrcv_state = CURL_TS_SB; - break; - case CURL_IAC: - tn->telrcv_state = CURL_TS_DATA; - writebyte(); - break; - case CURL_DM: - case CURL_NOP: - case CURL_GA: - default: - tn->telrcv_state = CURL_TS_DATA; - printoption(data, "RCVD", CURL_IAC, c); - break; - } - break; - - case CURL_TS_WILL: - printoption(data, "RCVD", CURL_WILL, c); - tn->please_negotiate = 1; - rec_will(data, c); - tn->telrcv_state = CURL_TS_DATA; - break; - - case CURL_TS_WONT: - printoption(data, "RCVD", CURL_WONT, c); - tn->please_negotiate = 1; - rec_wont(data, c); - tn->telrcv_state = CURL_TS_DATA; - break; - - case CURL_TS_DO: - printoption(data, "RCVD", CURL_DO, c); - tn->please_negotiate = 1; - rec_do(data, c); - tn->telrcv_state = CURL_TS_DATA; - break; - - case CURL_TS_DONT: - printoption(data, "RCVD", CURL_DONT, c); - tn->please_negotiate = 1; - rec_dont(data, c); - tn->telrcv_state = CURL_TS_DATA; - break; - - case CURL_TS_SB: - if(c == CURL_IAC) - tn->telrcv_state = CURL_TS_SE; - else - CURL_SB_ACCUM(tn, c); - break; - - case CURL_TS_SE: - if(c != CURL_SE) { - if(c != CURL_IAC) { - /* - * This is an error. We only expect to get "IAC IAC" or "IAC SE". - * Several things may have happened. An IAC was not doubled, the - * IAC SE was left off, or another option got inserted into the - * suboption are all possibilities. If we assume that the IAC was - * not doubled, and really the IAC SE was left off, we could get - * into an infinite loop here. So, instead, we terminate the - * suboption, and process the partial suboption if we can. - */ - CURL_SB_ACCUM(tn, CURL_IAC); - CURL_SB_ACCUM(tn, c); - tn->subpointer -= 2; - CURL_SB_TERM(tn); - - printoption(data, "In SUBOPTION processing, RCVD", CURL_IAC, c); - suboption(data); /* handle sub-option */ - tn->telrcv_state = CURL_TS_IAC; - goto process_iac; - } - CURL_SB_ACCUM(tn, c); - tn->telrcv_state = CURL_TS_SB; - } - else { - CURL_SB_ACCUM(tn, CURL_IAC); - CURL_SB_ACCUM(tn, CURL_SE); - tn->subpointer -= 2; - CURL_SB_TERM(tn); - suboption(data); /* handle sub-option */ - tn->telrcv_state = CURL_TS_DATA; - } - break; - } - ++in; - } - bufferflush(); - return CURLE_OK; -} - -/* Escape and send a telnet data block */ -static CURLcode send_telnet_data(struct Curl_easy *data, - char *buffer, ssize_t nread) -{ - ssize_t escapes, i, outlen; - unsigned char *outbuf = NULL; - CURLcode result = CURLE_OK; - ssize_t bytes_written, total_written; - struct connectdata *conn = data->conn; - - /* Determine size of new buffer after escaping */ - escapes = 0; - for(i = 0; i < nread; i++) - if((unsigned char)buffer[i] == CURL_IAC) - escapes++; - outlen = nread + escapes; - - if(outlen == nread) - outbuf = (unsigned char *)buffer; - else { - ssize_t j; - outbuf = malloc(nread + escapes + 1); - if(!outbuf) - return CURLE_OUT_OF_MEMORY; - - j = 0; - for(i = 0; i < nread; i++) { - outbuf[j++] = buffer[i]; - if((unsigned char)buffer[i] == CURL_IAC) - outbuf[j++] = CURL_IAC; - } - outbuf[j] = '\0'; - } - - total_written = 0; - while(!result && total_written < outlen) { - /* Make sure socket is writable to avoid EWOULDBLOCK condition */ - struct pollfd pfd[1]; - pfd[0].fd = conn->sock[FIRSTSOCKET]; - pfd[0].events = POLLOUT; - switch(Curl_poll(pfd, 1, -1)) { - case -1: /* error, abort writing */ - case 0: /* timeout (will never happen) */ - result = CURLE_SEND_ERROR; - break; - default: /* write! */ - bytes_written = 0; - result = Curl_write(data, conn->sock[FIRSTSOCKET], - outbuf + total_written, - outlen - total_written, - &bytes_written); - total_written += bytes_written; - break; - } - } - - /* Free malloc copy if escaped */ - if(outbuf != (unsigned char *)buffer) - free(outbuf); - - return result; -} - -static CURLcode telnet_done(struct Curl_easy *data, - CURLcode status, bool premature) -{ - struct TELNET *tn = data->req.p.telnet; - (void)status; /* unused */ - (void)premature; /* not used */ - - if(!tn) - return CURLE_OK; - - curl_slist_free_all(tn->telnet_vars); - tn->telnet_vars = NULL; - - Curl_safefree(data->req.p.telnet); - - return CURLE_OK; -} - -static CURLcode telnet_do(struct Curl_easy *data, bool *done) -{ - CURLcode result; - struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; -#ifdef USE_WINSOCK - WSAEVENT event_handle; - WSANETWORKEVENTS events; - HANDLE stdin_handle; - HANDLE objs[2]; - DWORD obj_count; - DWORD wait_timeout; - DWORD readfile_read; - int err; -#else - timediff_t interval_ms; - struct pollfd pfd[2]; - int poll_cnt; - curl_off_t total_dl = 0; - curl_off_t total_ul = 0; -#endif - ssize_t nread; - struct curltime now; - bool keepon = TRUE; - char *buf = data->state.buffer; - struct TELNET *tn; - - *done = TRUE; /* unconditionally */ - - result = init_telnet(data); - if(result) - return result; - - tn = data->req.p.telnet; - - result = check_telnet_options(data); - if(result) - return result; - -#ifdef USE_WINSOCK - /* We want to wait for both stdin and the socket. Since - ** the select() function in winsock only works on sockets - ** we have to use the WaitForMultipleObjects() call. - */ - - /* First, create a sockets event object */ - event_handle = WSACreateEvent(); - if(event_handle == WSA_INVALID_EVENT) { - failf(data, "WSACreateEvent failed (%d)", SOCKERRNO); - return CURLE_FAILED_INIT; - } - - /* Tell winsock what events we want to listen to */ - if(WSAEventSelect(sockfd, event_handle, FD_READ|FD_CLOSE) == SOCKET_ERROR) { - WSACloseEvent(event_handle); - return CURLE_OK; - } - - /* The get the Windows file handle for stdin */ - stdin_handle = GetStdHandle(STD_INPUT_HANDLE); - - /* Create the list of objects to wait for */ - objs[0] = event_handle; - objs[1] = stdin_handle; - - /* If stdin_handle is a pipe, use PeekNamedPipe() method to check it, - else use the old WaitForMultipleObjects() way */ - if(GetFileType(stdin_handle) == FILE_TYPE_PIPE || - data->set.is_fread_set) { - /* Don't wait for stdin_handle, just wait for event_handle */ - obj_count = 1; - /* Check stdin_handle per 100 milliseconds */ - wait_timeout = 100; - } - else { - obj_count = 2; - wait_timeout = 1000; - } - - /* Keep on listening and act on events */ - while(keepon) { - const DWORD buf_size = (DWORD)data->set.buffer_size; - DWORD waitret = WaitForMultipleObjects(obj_count, objs, - FALSE, wait_timeout); - switch(waitret) { - - case WAIT_TIMEOUT: - { - for(;;) { - if(data->set.is_fread_set) { - size_t n; - /* read from user-supplied method */ - n = data->state.fread_func(buf, 1, buf_size, data->state.in); - if(n == CURL_READFUNC_ABORT) { - keepon = FALSE; - result = CURLE_READ_ERROR; - break; - } - - if(n == CURL_READFUNC_PAUSE) - break; - - if(n == 0) /* no bytes */ - break; - - /* fall through with number of bytes read */ - readfile_read = (DWORD)n; - } - else { - /* read from stdin */ - if(!PeekNamedPipe(stdin_handle, NULL, 0, NULL, - &readfile_read, NULL)) { - keepon = FALSE; - result = CURLE_READ_ERROR; - break; - } - - if(!readfile_read) - break; - - if(!ReadFile(stdin_handle, buf, buf_size, - &readfile_read, NULL)) { - keepon = FALSE; - result = CURLE_READ_ERROR; - break; - } - } - - result = send_telnet_data(data, buf, readfile_read); - if(result) { - keepon = FALSE; - break; - } - } - } - break; - - case WAIT_OBJECT_0 + 1: - { - if(!ReadFile(stdin_handle, buf, buf_size, - &readfile_read, NULL)) { - keepon = FALSE; - result = CURLE_READ_ERROR; - break; - } - - result = send_telnet_data(data, buf, readfile_read); - if(result) { - keepon = FALSE; - break; - } - } - break; - - case WAIT_OBJECT_0: - { - events.lNetworkEvents = 0; - if(WSAEnumNetworkEvents(sockfd, event_handle, &events) == SOCKET_ERROR) { - err = SOCKERRNO; - if(err != EINPROGRESS) { - infof(data, "WSAEnumNetworkEvents failed (%d)", err); - keepon = FALSE; - result = CURLE_READ_ERROR; - } - break; - } - if(events.lNetworkEvents & FD_READ) { - /* read data from network */ - result = Curl_read(data, sockfd, buf, data->set.buffer_size, &nread); - /* read would've blocked. Loop again */ - if(result == CURLE_AGAIN) - break; - /* returned not-zero, this an error */ - else if(result) { - keepon = FALSE; - break; - } - /* returned zero but actually received 0 or less here, - the server closed the connection and we bail out */ - else if(nread <= 0) { - keepon = FALSE; - break; - } - - result = telrcv(data, (unsigned char *) buf, nread); - if(result) { - keepon = FALSE; - break; - } - - /* Negotiate if the peer has started negotiating, - otherwise don't. We don't want to speak telnet with - non-telnet servers, like POP or SMTP. */ - if(tn->please_negotiate && !tn->already_negotiated) { - negotiate(data); - tn->already_negotiated = 1; - } - } - if(events.lNetworkEvents & FD_CLOSE) { - keepon = FALSE; - } - } - break; - - } - - if(data->set.timeout) { - now = Curl_now(); - if(Curl_timediff(now, conn->created) >= data->set.timeout) { - failf(data, "Time-out"); - result = CURLE_OPERATION_TIMEDOUT; - keepon = FALSE; - } - } - } - - /* We called WSACreateEvent, so call WSACloseEvent */ - if(!WSACloseEvent(event_handle)) { - infof(data, "WSACloseEvent failed (%d)", SOCKERRNO); - } -#else - pfd[0].fd = sockfd; - pfd[0].events = POLLIN; - - if(data->set.is_fread_set) { - poll_cnt = 1; - interval_ms = 100; /* poll user-supplied read function */ - } - else { - /* really using fread, so infile is a FILE* */ - pfd[1].fd = fileno((FILE *)data->state.in); - pfd[1].events = POLLIN; - poll_cnt = 2; - interval_ms = 1 * 1000; - } - - while(keepon) { - switch(Curl_poll(pfd, poll_cnt, interval_ms)) { - case -1: /* error, stop reading */ - keepon = FALSE; - continue; - case 0: /* timeout */ - pfd[0].revents = 0; - pfd[1].revents = 0; - /* FALLTHROUGH */ - default: /* read! */ - if(pfd[0].revents & POLLIN) { - /* read data from network */ - result = Curl_read(data, sockfd, buf, data->set.buffer_size, &nread); - /* read would've blocked. Loop again */ - if(result == CURLE_AGAIN) - break; - /* returned not-zero, this an error */ - if(result) { - keepon = FALSE; - break; - } - /* returned zero but actually received 0 or less here, - the server closed the connection and we bail out */ - else if(nread <= 0) { - keepon = FALSE; - break; - } - - total_dl += nread; - Curl_pgrsSetDownloadCounter(data, total_dl); - result = telrcv(data, (unsigned char *)buf, nread); - if(result) { - keepon = FALSE; - break; - } - - /* Negotiate if the peer has started negotiating, - otherwise don't. We don't want to speak telnet with - non-telnet servers, like POP or SMTP. */ - if(tn->please_negotiate && !tn->already_negotiated) { - negotiate(data); - tn->already_negotiated = 1; - } - } - - nread = 0; - if(poll_cnt == 2) { - if(pfd[1].revents & POLLIN) { /* read from in file */ - nread = read(pfd[1].fd, buf, data->set.buffer_size); - } - } - else { - /* read from user-supplied method */ - nread = (int)data->state.fread_func(buf, 1, data->set.buffer_size, - data->state.in); - if(nread == CURL_READFUNC_ABORT) { - keepon = FALSE; - break; - } - if(nread == CURL_READFUNC_PAUSE) - break; - } - - if(nread > 0) { - result = send_telnet_data(data, buf, nread); - if(result) { - keepon = FALSE; - break; - } - total_ul += nread; - Curl_pgrsSetUploadCounter(data, total_ul); - } - else if(nread < 0) - keepon = FALSE; - - break; - } /* poll switch statement */ - - if(data->set.timeout) { - now = Curl_now(); - if(Curl_timediff(now, conn->created) >= data->set.timeout) { - failf(data, "Time-out"); - result = CURLE_OPERATION_TIMEDOUT; - keepon = FALSE; - } - } - - if(Curl_pgrsUpdate(data)) { - result = CURLE_ABORTED_BY_CALLBACK; - break; - } - } -#endif - /* mark this as "no further transfer wanted" */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); - - return result; -} -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_TELNET
+
+#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 HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "transfer.h"
+#include "sendf.h"
+#include "telnet.h"
+#include "connect.h"
+#include "progress.h"
+#include "system_win32.h"
+#include "arpa_telnet.h"
+#include "select.h"
+#include "strcase.h"
+#include "warnless.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define SUBBUFSIZE 512
+
+#define CURL_SB_CLEAR(x) x->subpointer = x->subbuffer
+#define CURL_SB_TERM(x) \
+ do { \
+ x->subend = x->subpointer; \
+ CURL_SB_CLEAR(x); \
+ } while(0)
+#define CURL_SB_ACCUM(x,c) \
+ do { \
+ if(x->subpointer < (x->subbuffer + sizeof(x->subbuffer))) \
+ *x->subpointer++ = (c); \
+ } while(0)
+
+#define CURL_SB_GET(x) ((*x->subpointer++)&0xff)
+#define CURL_SB_LEN(x) (x->subend - x->subpointer)
+
+/* For posterity:
+#define CURL_SB_PEEK(x) ((*x->subpointer)&0xff)
+#define CURL_SB_EOF(x) (x->subpointer >= x->subend) */
+
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+#define printoption(a,b,c,d) Curl_nop_stmt
+#endif
+
+static
+CURLcode telrcv(struct Curl_easy *data,
+ const unsigned char *inbuf, /* Data received from socket */
+ ssize_t count); /* Number of bytes received */
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static void printoption(struct Curl_easy *data,
+ const char *direction,
+ int cmd, int option);
+#endif
+
+static void negotiate(struct Curl_easy *data);
+static void send_negotiation(struct Curl_easy *data, int cmd, int option);
+static void set_local_option(struct Curl_easy *data,
+ int option, int newstate);
+static void set_remote_option(struct Curl_easy *data,
+ int option, int newstate);
+
+static void printsub(struct Curl_easy *data,
+ int direction, unsigned char *pointer,
+ size_t length);
+static void suboption(struct Curl_easy *data);
+static void sendsuboption(struct Curl_easy *data, int option);
+
+static CURLcode telnet_do(struct Curl_easy *data, bool *done);
+static CURLcode telnet_done(struct Curl_easy *data,
+ CURLcode, bool premature);
+static CURLcode send_telnet_data(struct Curl_easy *data,
+ char *buffer, ssize_t nread);
+
+/* For negotiation compliant to RFC 1143 */
+#define CURL_NO 0
+#define CURL_YES 1
+#define CURL_WANTYES 2
+#define CURL_WANTNO 3
+
+#define CURL_EMPTY 0
+#define CURL_OPPOSITE 1
+
+/*
+ * Telnet receiver states for fsm
+ */
+typedef enum
+{
+ CURL_TS_DATA = 0,
+ CURL_TS_IAC,
+ CURL_TS_WILL,
+ CURL_TS_WONT,
+ CURL_TS_DO,
+ CURL_TS_DONT,
+ CURL_TS_CR,
+ CURL_TS_SB, /* sub-option collection */
+ CURL_TS_SE /* looking for sub-option end */
+} TelnetReceive;
+
+struct TELNET {
+ int please_negotiate;
+ int already_negotiated;
+ int us[256];
+ int usq[256];
+ int us_preferred[256];
+ int him[256];
+ int himq[256];
+ int him_preferred[256];
+ int subnegotiation[256];
+ char subopt_ttype[32]; /* Set with suboption TTYPE */
+ char subopt_xdisploc[128]; /* Set with suboption XDISPLOC */
+ unsigned short subopt_wsx; /* Set with suboption NAWS */
+ unsigned short subopt_wsy; /* Set with suboption NAWS */
+ TelnetReceive telrcv_state;
+ struct curl_slist *telnet_vars; /* Environment variables */
+
+ /* suboptions */
+ unsigned char subbuffer[SUBBUFSIZE];
+ unsigned char *subpointer, *subend; /* buffer for sub-options */
+};
+
+
+/*
+ * TELNET protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_telnet = {
+ "TELNET", /* scheme */
+ ZERO_NULL, /* setup_connection */
+ telnet_do, /* do_it */
+ telnet_done, /* done */
+ ZERO_NULL, /* do_more */
+ ZERO_NULL, /* connect_it */
+ ZERO_NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ ZERO_NULL, /* proto_getsock */
+ ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ ZERO_NULL, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_TELNET, /* defport */
+ CURLPROTO_TELNET, /* protocol */
+ CURLPROTO_TELNET, /* family */
+ PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */
+};
+
+
+static
+CURLcode init_telnet(struct Curl_easy *data)
+{
+ struct TELNET *tn;
+
+ tn = calloc(1, sizeof(struct TELNET));
+ if(!tn)
+ return CURLE_OUT_OF_MEMORY;
+
+ data->req.p.telnet = tn; /* make us known */
+
+ tn->telrcv_state = CURL_TS_DATA;
+
+ /* Init suboptions */
+ CURL_SB_CLEAR(tn);
+
+ /* Set the options we want by default */
+ tn->us_preferred[CURL_TELOPT_SGA] = CURL_YES;
+ tn->him_preferred[CURL_TELOPT_SGA] = CURL_YES;
+
+ /* To be compliant with previous releases of libcurl
+ we enable this option by default. This behavior
+ can be changed thanks to the "BINARY" option in
+ CURLOPT_TELNETOPTIONS
+ */
+ tn->us_preferred[CURL_TELOPT_BINARY] = CURL_YES;
+ tn->him_preferred[CURL_TELOPT_BINARY] = CURL_YES;
+
+ /* We must allow the server to echo what we sent
+ but it is not necessary to request the server
+ to do so (it might forces the server to close
+ the connection). Hence, we ignore ECHO in the
+ negotiate function
+ */
+ tn->him_preferred[CURL_TELOPT_ECHO] = CURL_YES;
+
+ /* Set the subnegotiation fields to send information
+ just after negotiation passed (do/will)
+
+ Default values are (0,0) initialized by calloc.
+ According to the RFC1013 it is valid:
+ A value equal to zero is acceptable for the width (or height),
+ and means that no character width (or height) is being sent.
+ In this case, the width (or height) that will be assumed by the
+ Telnet server is operating system specific (it will probably be
+ based upon the terminal type information that may have been sent
+ using the TERMINAL TYPE Telnet option). */
+ tn->subnegotiation[CURL_TELOPT_NAWS] = CURL_YES;
+ return CURLE_OK;
+}
+
+static void negotiate(struct Curl_easy *data)
+{
+ int i;
+ struct TELNET *tn = data->req.p.telnet;
+
+ for(i = 0; i < CURL_NTELOPTS; i++) {
+ if(i == CURL_TELOPT_ECHO)
+ continue;
+
+ if(tn->us_preferred[i] == CURL_YES)
+ set_local_option(data, i, CURL_YES);
+
+ if(tn->him_preferred[i] == CURL_YES)
+ set_remote_option(data, i, CURL_YES);
+ }
+}
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static void printoption(struct Curl_easy *data,
+ const char *direction, int cmd, int option)
+{
+ if(data->set.verbose) {
+ if(cmd == CURL_IAC) {
+ if(CURL_TELCMD_OK(option))
+ infof(data, "%s IAC %s", direction, CURL_TELCMD(option));
+ else
+ infof(data, "%s IAC %d", direction, option);
+ }
+ else {
+ const char *fmt = (cmd == CURL_WILL) ? "WILL" :
+ (cmd == CURL_WONT) ? "WONT" :
+ (cmd == CURL_DO) ? "DO" :
+ (cmd == CURL_DONT) ? "DONT" : 0;
+ if(fmt) {
+ const char *opt;
+ if(CURL_TELOPT_OK(option))
+ opt = CURL_TELOPT(option);
+ else if(option == CURL_TELOPT_EXOPL)
+ opt = "EXOPL";
+ else
+ opt = NULL;
+
+ if(opt)
+ infof(data, "%s %s %s", direction, fmt, opt);
+ else
+ infof(data, "%s %s %d", direction, fmt, option);
+ }
+ else
+ infof(data, "%s %d %d", direction, cmd, option);
+ }
+ }
+}
+#endif
+
+static void send_negotiation(struct Curl_easy *data, int cmd, int option)
+{
+ unsigned char buf[3];
+ ssize_t bytes_written;
+ struct connectdata *conn = data->conn;
+
+ buf[0] = CURL_IAC;
+ buf[1] = (unsigned char)cmd;
+ buf[2] = (unsigned char)option;
+
+ bytes_written = swrite(conn->sock[FIRSTSOCKET], buf, 3);
+ if(bytes_written < 0) {
+ int err = SOCKERRNO;
+ failf(data,"Sending data failed (%d)",err);
+ }
+
+ printoption(data, "SENT", cmd, option);
+}
+
+static
+void set_remote_option(struct Curl_easy *data, int option, int newstate)
+{
+ struct TELNET *tn = data->req.p.telnet;
+ if(newstate == CURL_YES) {
+ switch(tn->him[option]) {
+ case CURL_NO:
+ tn->him[option] = CURL_WANTYES;
+ send_negotiation(data, CURL_DO, option);
+ break;
+
+ case CURL_YES:
+ /* Already enabled */
+ break;
+
+ case CURL_WANTNO:
+ switch(tn->himq[option]) {
+ case CURL_EMPTY:
+ /* Already negotiating for CURL_YES, queue the request */
+ tn->himq[option] = CURL_OPPOSITE;
+ break;
+ case CURL_OPPOSITE:
+ /* Error: already queued an enable request */
+ break;
+ }
+ break;
+
+ case CURL_WANTYES:
+ switch(tn->himq[option]) {
+ case CURL_EMPTY:
+ /* Error: already negotiating for enable */
+ break;
+ case CURL_OPPOSITE:
+ tn->himq[option] = CURL_EMPTY;
+ break;
+ }
+ break;
+ }
+ }
+ else { /* NO */
+ switch(tn->him[option]) {
+ case CURL_NO:
+ /* Already disabled */
+ break;
+
+ case CURL_YES:
+ tn->him[option] = CURL_WANTNO;
+ send_negotiation(data, CURL_DONT, option);
+ break;
+
+ case CURL_WANTNO:
+ switch(tn->himq[option]) {
+ case CURL_EMPTY:
+ /* Already negotiating for NO */
+ break;
+ case CURL_OPPOSITE:
+ tn->himq[option] = CURL_EMPTY;
+ break;
+ }
+ break;
+
+ case CURL_WANTYES:
+ switch(tn->himq[option]) {
+ case CURL_EMPTY:
+ tn->himq[option] = CURL_OPPOSITE;
+ break;
+ case CURL_OPPOSITE:
+ break;
+ }
+ break;
+ }
+ }
+}
+
+static
+void rec_will(struct Curl_easy *data, int option)
+{
+ struct TELNET *tn = data->req.p.telnet;
+ switch(tn->him[option]) {
+ case CURL_NO:
+ if(tn->him_preferred[option] == CURL_YES) {
+ tn->him[option] = CURL_YES;
+ send_negotiation(data, CURL_DO, option);
+ }
+ else
+ send_negotiation(data, CURL_DONT, option);
+
+ break;
+
+ case CURL_YES:
+ /* Already enabled */
+ break;
+
+ case CURL_WANTNO:
+ switch(tn->himq[option]) {
+ case CURL_EMPTY:
+ /* Error: DONT answered by WILL */
+ tn->him[option] = CURL_NO;
+ break;
+ case CURL_OPPOSITE:
+ /* Error: DONT answered by WILL */
+ tn->him[option] = CURL_YES;
+ tn->himq[option] = CURL_EMPTY;
+ break;
+ }
+ break;
+
+ case CURL_WANTYES:
+ switch(tn->himq[option]) {
+ case CURL_EMPTY:
+ tn->him[option] = CURL_YES;
+ break;
+ case CURL_OPPOSITE:
+ tn->him[option] = CURL_WANTNO;
+ tn->himq[option] = CURL_EMPTY;
+ send_negotiation(data, CURL_DONT, option);
+ break;
+ }
+ break;
+ }
+}
+
+static
+void rec_wont(struct Curl_easy *data, int option)
+{
+ struct TELNET *tn = data->req.p.telnet;
+ switch(tn->him[option]) {
+ case CURL_NO:
+ /* Already disabled */
+ break;
+
+ case CURL_YES:
+ tn->him[option] = CURL_NO;
+ send_negotiation(data, CURL_DONT, option);
+ break;
+
+ case CURL_WANTNO:
+ switch(tn->himq[option]) {
+ case CURL_EMPTY:
+ tn->him[option] = CURL_NO;
+ break;
+
+ case CURL_OPPOSITE:
+ tn->him[option] = CURL_WANTYES;
+ tn->himq[option] = CURL_EMPTY;
+ send_negotiation(data, CURL_DO, option);
+ break;
+ }
+ break;
+
+ case CURL_WANTYES:
+ switch(tn->himq[option]) {
+ case CURL_EMPTY:
+ tn->him[option] = CURL_NO;
+ break;
+ case CURL_OPPOSITE:
+ tn->him[option] = CURL_NO;
+ tn->himq[option] = CURL_EMPTY;
+ break;
+ }
+ break;
+ }
+}
+
+static void
+set_local_option(struct Curl_easy *data, int option, int newstate)
+{
+ struct TELNET *tn = data->req.p.telnet;
+ if(newstate == CURL_YES) {
+ switch(tn->us[option]) {
+ case CURL_NO:
+ tn->us[option] = CURL_WANTYES;
+ send_negotiation(data, CURL_WILL, option);
+ break;
+
+ case CURL_YES:
+ /* Already enabled */
+ break;
+
+ case CURL_WANTNO:
+ switch(tn->usq[option]) {
+ case CURL_EMPTY:
+ /* Already negotiating for CURL_YES, queue the request */
+ tn->usq[option] = CURL_OPPOSITE;
+ break;
+ case CURL_OPPOSITE:
+ /* Error: already queued an enable request */
+ break;
+ }
+ break;
+
+ case CURL_WANTYES:
+ switch(tn->usq[option]) {
+ case CURL_EMPTY:
+ /* Error: already negotiating for enable */
+ break;
+ case CURL_OPPOSITE:
+ tn->usq[option] = CURL_EMPTY;
+ break;
+ }
+ break;
+ }
+ }
+ else { /* NO */
+ switch(tn->us[option]) {
+ case CURL_NO:
+ /* Already disabled */
+ break;
+
+ case CURL_YES:
+ tn->us[option] = CURL_WANTNO;
+ send_negotiation(data, CURL_WONT, option);
+ break;
+
+ case CURL_WANTNO:
+ switch(tn->usq[option]) {
+ case CURL_EMPTY:
+ /* Already negotiating for NO */
+ break;
+ case CURL_OPPOSITE:
+ tn->usq[option] = CURL_EMPTY;
+ break;
+ }
+ break;
+
+ case CURL_WANTYES:
+ switch(tn->usq[option]) {
+ case CURL_EMPTY:
+ tn->usq[option] = CURL_OPPOSITE;
+ break;
+ case CURL_OPPOSITE:
+ break;
+ }
+ break;
+ }
+ }
+}
+
+static
+void rec_do(struct Curl_easy *data, int option)
+{
+ struct TELNET *tn = data->req.p.telnet;
+ switch(tn->us[option]) {
+ case CURL_NO:
+ if(tn->us_preferred[option] == CURL_YES) {
+ tn->us[option] = CURL_YES;
+ send_negotiation(data, CURL_WILL, option);
+ if(tn->subnegotiation[option] == CURL_YES)
+ /* transmission of data option */
+ sendsuboption(data, option);
+ }
+ else if(tn->subnegotiation[option] == CURL_YES) {
+ /* send information to achieve this option */
+ tn->us[option] = CURL_YES;
+ send_negotiation(data, CURL_WILL, option);
+ sendsuboption(data, option);
+ }
+ else
+ send_negotiation(data, CURL_WONT, option);
+ break;
+
+ case CURL_YES:
+ /* Already enabled */
+ break;
+
+ case CURL_WANTNO:
+ switch(tn->usq[option]) {
+ case CURL_EMPTY:
+ /* Error: DONT answered by WILL */
+ tn->us[option] = CURL_NO;
+ break;
+ case CURL_OPPOSITE:
+ /* Error: DONT answered by WILL */
+ tn->us[option] = CURL_YES;
+ tn->usq[option] = CURL_EMPTY;
+ break;
+ }
+ break;
+
+ case CURL_WANTYES:
+ switch(tn->usq[option]) {
+ case CURL_EMPTY:
+ tn->us[option] = CURL_YES;
+ if(tn->subnegotiation[option] == CURL_YES) {
+ /* transmission of data option */
+ sendsuboption(data, option);
+ }
+ break;
+ case CURL_OPPOSITE:
+ tn->us[option] = CURL_WANTNO;
+ tn->himq[option] = CURL_EMPTY;
+ send_negotiation(data, CURL_WONT, option);
+ break;
+ }
+ break;
+ }
+}
+
+static
+void rec_dont(struct Curl_easy *data, int option)
+{
+ struct TELNET *tn = data->req.p.telnet;
+ switch(tn->us[option]) {
+ case CURL_NO:
+ /* Already disabled */
+ break;
+
+ case CURL_YES:
+ tn->us[option] = CURL_NO;
+ send_negotiation(data, CURL_WONT, option);
+ break;
+
+ case CURL_WANTNO:
+ switch(tn->usq[option]) {
+ case CURL_EMPTY:
+ tn->us[option] = CURL_NO;
+ break;
+
+ case CURL_OPPOSITE:
+ tn->us[option] = CURL_WANTYES;
+ tn->usq[option] = CURL_EMPTY;
+ send_negotiation(data, CURL_WILL, option);
+ break;
+ }
+ break;
+
+ case CURL_WANTYES:
+ switch(tn->usq[option]) {
+ case CURL_EMPTY:
+ tn->us[option] = CURL_NO;
+ break;
+ case CURL_OPPOSITE:
+ tn->us[option] = CURL_NO;
+ tn->usq[option] = CURL_EMPTY;
+ break;
+ }
+ break;
+ }
+}
+
+
+static void printsub(struct Curl_easy *data,
+ int direction, /* '<' or '>' */
+ unsigned char *pointer, /* where suboption data is */
+ size_t length) /* length of suboption data */
+{
+ if(data->set.verbose) {
+ unsigned int i = 0;
+ if(direction) {
+ infof(data, "%s IAC SB ", (direction == '<')? "RCVD":"SENT");
+ if(length >= 3) {
+ int j;
+
+ i = pointer[length-2];
+ j = pointer[length-1];
+
+ if(i != CURL_IAC || j != CURL_SE) {
+ infof(data, "(terminated by ");
+ if(CURL_TELOPT_OK(i))
+ infof(data, "%s ", CURL_TELOPT(i));
+ else if(CURL_TELCMD_OK(i))
+ infof(data, "%s ", CURL_TELCMD(i));
+ else
+ infof(data, "%u ", i);
+ if(CURL_TELOPT_OK(j))
+ infof(data, "%s", CURL_TELOPT(j));
+ else if(CURL_TELCMD_OK(j))
+ infof(data, "%s", CURL_TELCMD(j));
+ else
+ infof(data, "%d", j);
+ infof(data, ", not IAC SE) ");
+ }
+ }
+ length -= 2;
+ }
+ if(length < 1) {
+ infof(data, "(Empty suboption?)");
+ return;
+ }
+
+ if(CURL_TELOPT_OK(pointer[0])) {
+ switch(pointer[0]) {
+ case CURL_TELOPT_TTYPE:
+ case CURL_TELOPT_XDISPLOC:
+ case CURL_TELOPT_NEW_ENVIRON:
+ case CURL_TELOPT_NAWS:
+ infof(data, "%s", CURL_TELOPT(pointer[0]));
+ break;
+ default:
+ infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0]));
+ break;
+ }
+ }
+ else
+ infof(data, "%d (unknown)", pointer[i]);
+
+ switch(pointer[0]) {
+ case CURL_TELOPT_NAWS:
+ if(length > 4)
+ infof(data, "Width: %d ; Height: %d", (pointer[1]<<8) | pointer[2],
+ (pointer[3]<<8) | pointer[4]);
+ break;
+ default:
+ switch(pointer[1]) {
+ case CURL_TELQUAL_IS:
+ infof(data, " IS");
+ break;
+ case CURL_TELQUAL_SEND:
+ infof(data, " SEND");
+ break;
+ case CURL_TELQUAL_INFO:
+ infof(data, " INFO/REPLY");
+ break;
+ case CURL_TELQUAL_NAME:
+ infof(data, " NAME");
+ break;
+ }
+
+ switch(pointer[0]) {
+ case CURL_TELOPT_TTYPE:
+ case CURL_TELOPT_XDISPLOC:
+ pointer[length] = 0;
+ infof(data, " \"%s\"", &pointer[2]);
+ break;
+ case CURL_TELOPT_NEW_ENVIRON:
+ if(pointer[1] == CURL_TELQUAL_IS) {
+ infof(data, " ");
+ for(i = 3; i < length; i++) {
+ switch(pointer[i]) {
+ case CURL_NEW_ENV_VAR:
+ infof(data, ", ");
+ break;
+ case CURL_NEW_ENV_VALUE:
+ infof(data, " = ");
+ break;
+ default:
+ infof(data, "%c", pointer[i]);
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ for(i = 2; i < length; i++)
+ infof(data, " %.2x", pointer[i]);
+ break;
+ }
+ }
+ }
+}
+
+static CURLcode check_telnet_options(struct Curl_easy *data)
+{
+ struct curl_slist *head;
+ struct curl_slist *beg;
+ char option_keyword[128] = "";
+ char option_arg[256] = "";
+ struct TELNET *tn = data->req.p.telnet;
+ struct connectdata *conn = data->conn;
+ CURLcode result = CURLE_OK;
+ int binary_option;
+
+ /* Add the user name as an environment variable if it
+ was given on the command line */
+ if(data->state.aptr.user) {
+ msnprintf(option_arg, sizeof(option_arg), "USER,%s", conn->user);
+ beg = curl_slist_append(tn->telnet_vars, option_arg);
+ if(!beg) {
+ curl_slist_free_all(tn->telnet_vars);
+ tn->telnet_vars = NULL;
+ return CURLE_OUT_OF_MEMORY;
+ }
+ tn->telnet_vars = beg;
+ tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
+ }
+
+ for(head = data->set.telnet_options; head; head = head->next) {
+ if(sscanf(head->data, "%127[^= ]%*[ =]%255s",
+ option_keyword, option_arg) == 2) {
+
+ /* Terminal type */
+ if(strcasecompare(option_keyword, "TTYPE")) {
+ strncpy(tn->subopt_ttype, option_arg, 31);
+ tn->subopt_ttype[31] = 0; /* String termination */
+ tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES;
+ continue;
+ }
+
+ /* Display variable */
+ if(strcasecompare(option_keyword, "XDISPLOC")) {
+ strncpy(tn->subopt_xdisploc, option_arg, 127);
+ tn->subopt_xdisploc[127] = 0; /* String termination */
+ tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES;
+ continue;
+ }
+
+ /* Environment variable */
+ if(strcasecompare(option_keyword, "NEW_ENV")) {
+ beg = curl_slist_append(tn->telnet_vars, option_arg);
+ if(!beg) {
+ result = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+ tn->telnet_vars = beg;
+ tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
+ continue;
+ }
+
+ /* Window Size */
+ if(strcasecompare(option_keyword, "WS")) {
+ if(sscanf(option_arg, "%hu%*[xX]%hu",
+ &tn->subopt_wsx, &tn->subopt_wsy) == 2)
+ tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES;
+ else {
+ failf(data, "Syntax error in telnet option: %s", head->data);
+ result = CURLE_SETOPT_OPTION_SYNTAX;
+ break;
+ }
+ continue;
+ }
+
+ /* To take care or not of the 8th bit in data exchange */
+ if(strcasecompare(option_keyword, "BINARY")) {
+ binary_option = atoi(option_arg);
+ if(binary_option != 1) {
+ tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO;
+ tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO;
+ }
+ continue;
+ }
+
+ failf(data, "Unknown telnet option %s", head->data);
+ result = CURLE_UNKNOWN_OPTION;
+ break;
+ }
+ failf(data, "Syntax error in telnet option: %s", head->data);
+ result = CURLE_SETOPT_OPTION_SYNTAX;
+ break;
+ }
+
+ if(result) {
+ curl_slist_free_all(tn->telnet_vars);
+ tn->telnet_vars = NULL;
+ }
+
+ return result;
+}
+
+/*
+ * suboption()
+ *
+ * Look at the sub-option buffer, and try to be helpful to the other
+ * side.
+ */
+
+static void suboption(struct Curl_easy *data)
+{
+ struct curl_slist *v;
+ unsigned char temp[2048];
+ ssize_t bytes_written;
+ size_t len;
+ int err;
+ char varname[128] = "";
+ char varval[128] = "";
+ struct TELNET *tn = data->req.p.telnet;
+ struct connectdata *conn = data->conn;
+
+ printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn) + 2);
+ switch(CURL_SB_GET(tn)) {
+ case CURL_TELOPT_TTYPE:
+ len = strlen(tn->subopt_ttype) + 4 + 2;
+ msnprintf((char *)temp, sizeof(temp),
+ "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_TTYPE,
+ CURL_TELQUAL_IS, tn->subopt_ttype, CURL_IAC, CURL_SE);
+ bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
+ if(bytes_written < 0) {
+ err = SOCKERRNO;
+ failf(data,"Sending data failed (%d)",err);
+ }
+ printsub(data, '>', &temp[2], len-2);
+ break;
+ case CURL_TELOPT_XDISPLOC:
+ len = strlen(tn->subopt_xdisploc) + 4 + 2;
+ msnprintf((char *)temp, sizeof(temp),
+ "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_XDISPLOC,
+ CURL_TELQUAL_IS, tn->subopt_xdisploc, CURL_IAC, CURL_SE);
+ bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
+ if(bytes_written < 0) {
+ err = SOCKERRNO;
+ failf(data,"Sending data failed (%d)",err);
+ }
+ printsub(data, '>', &temp[2], len-2);
+ break;
+ case CURL_TELOPT_NEW_ENVIRON:
+ msnprintf((char *)temp, sizeof(temp),
+ "%c%c%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_NEW_ENVIRON,
+ CURL_TELQUAL_IS);
+ len = 4;
+
+ for(v = tn->telnet_vars; v; v = v->next) {
+ size_t tmplen = (strlen(v->data) + 1);
+ /* Add the variable only if it fits */
+ if(len + tmplen < (int)sizeof(temp)-6) {
+ int rv;
+ char sep[2] = "";
+ varval[0] = 0;
+ rv = sscanf(v->data, "%127[^,]%1[,]%127s", varname, sep, varval);
+ if(rv == 1)
+ len += msnprintf((char *)&temp[len], sizeof(temp) - len,
+ "%c%s", CURL_NEW_ENV_VAR, varname);
+ else if(rv >= 2)
+ len += msnprintf((char *)&temp[len], sizeof(temp) - len,
+ "%c%s%c%s", CURL_NEW_ENV_VAR, varname,
+ CURL_NEW_ENV_VALUE, varval);
+ }
+ }
+ msnprintf((char *)&temp[len], sizeof(temp) - len,
+ "%c%c", CURL_IAC, CURL_SE);
+ len += 2;
+ bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
+ if(bytes_written < 0) {
+ err = SOCKERRNO;
+ failf(data,"Sending data failed (%d)",err);
+ }
+ printsub(data, '>', &temp[2], len-2);
+ break;
+ }
+ return;
+}
+
+
+/*
+ * sendsuboption()
+ *
+ * Send suboption information to the server side.
+ */
+
+static void sendsuboption(struct Curl_easy *data, int option)
+{
+ ssize_t bytes_written;
+ int err;
+ unsigned short x, y;
+ unsigned char *uc1, *uc2;
+ struct TELNET *tn = data->req.p.telnet;
+ struct connectdata *conn = data->conn;
+
+ switch(option) {
+ case CURL_TELOPT_NAWS:
+ /* We prepare data to be sent */
+ CURL_SB_CLEAR(tn);
+ CURL_SB_ACCUM(tn, CURL_IAC);
+ CURL_SB_ACCUM(tn, CURL_SB);
+ CURL_SB_ACCUM(tn, CURL_TELOPT_NAWS);
+ /* We must deal either with little or big endian processors */
+ /* Window size must be sent according to the 'network order' */
+ x = htons(tn->subopt_wsx);
+ y = htons(tn->subopt_wsy);
+ uc1 = (unsigned char *)&x;
+ uc2 = (unsigned char *)&y;
+ CURL_SB_ACCUM(tn, uc1[0]);
+ CURL_SB_ACCUM(tn, uc1[1]);
+ CURL_SB_ACCUM(tn, uc2[0]);
+ CURL_SB_ACCUM(tn, uc2[1]);
+
+ CURL_SB_ACCUM(tn, CURL_IAC);
+ CURL_SB_ACCUM(tn, CURL_SE);
+ CURL_SB_TERM(tn);
+ /* data suboption is now ready */
+
+ printsub(data, '>', (unsigned char *)tn->subbuffer + 2,
+ CURL_SB_LEN(tn)-2);
+
+ /* we send the header of the suboption... */
+ bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer, 3);
+ if(bytes_written < 0) {
+ err = SOCKERRNO;
+ failf(data, "Sending data failed (%d)", err);
+ }
+ /* ... then the window size with the send_telnet_data() function
+ to deal with 0xFF cases ... */
+ send_telnet_data(data, (char *)tn->subbuffer + 3, 4);
+ /* ... and the footer */
+ bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer + 7, 2);
+ if(bytes_written < 0) {
+ err = SOCKERRNO;
+ failf(data, "Sending data failed (%d)", err);
+ }
+ break;
+ }
+}
+
+
+static
+CURLcode telrcv(struct Curl_easy *data,
+ const unsigned char *inbuf, /* Data received from socket */
+ ssize_t count) /* Number of bytes received */
+{
+ unsigned char c;
+ CURLcode result;
+ int in = 0;
+ int startwrite = -1;
+ struct TELNET *tn = data->req.p.telnet;
+
+#define startskipping() \
+ if(startwrite >= 0) { \
+ result = Curl_client_write(data, \
+ CLIENTWRITE_BODY, \
+ (char *)&inbuf[startwrite], \
+ in-startwrite); \
+ if(result) \
+ return result; \
+ } \
+ startwrite = -1
+
+#define writebyte() \
+ if(startwrite < 0) \
+ startwrite = in
+
+#define bufferflush() startskipping()
+
+ while(count--) {
+ c = inbuf[in];
+
+ switch(tn->telrcv_state) {
+ case CURL_TS_CR:
+ tn->telrcv_state = CURL_TS_DATA;
+ if(c == '\0') {
+ startskipping();
+ break; /* Ignore \0 after CR */
+ }
+ writebyte();
+ break;
+
+ case CURL_TS_DATA:
+ if(c == CURL_IAC) {
+ tn->telrcv_state = CURL_TS_IAC;
+ startskipping();
+ break;
+ }
+ else if(c == '\r')
+ tn->telrcv_state = CURL_TS_CR;
+ writebyte();
+ break;
+
+ case CURL_TS_IAC:
+ process_iac:
+ DEBUGASSERT(startwrite < 0);
+ switch(c) {
+ case CURL_WILL:
+ tn->telrcv_state = CURL_TS_WILL;
+ break;
+ case CURL_WONT:
+ tn->telrcv_state = CURL_TS_WONT;
+ break;
+ case CURL_DO:
+ tn->telrcv_state = CURL_TS_DO;
+ break;
+ case CURL_DONT:
+ tn->telrcv_state = CURL_TS_DONT;
+ break;
+ case CURL_SB:
+ CURL_SB_CLEAR(tn);
+ tn->telrcv_state = CURL_TS_SB;
+ break;
+ case CURL_IAC:
+ tn->telrcv_state = CURL_TS_DATA;
+ writebyte();
+ break;
+ case CURL_DM:
+ case CURL_NOP:
+ case CURL_GA:
+ default:
+ tn->telrcv_state = CURL_TS_DATA;
+ printoption(data, "RCVD", CURL_IAC, c);
+ break;
+ }
+ break;
+
+ case CURL_TS_WILL:
+ printoption(data, "RCVD", CURL_WILL, c);
+ tn->please_negotiate = 1;
+ rec_will(data, c);
+ tn->telrcv_state = CURL_TS_DATA;
+ break;
+
+ case CURL_TS_WONT:
+ printoption(data, "RCVD", CURL_WONT, c);
+ tn->please_negotiate = 1;
+ rec_wont(data, c);
+ tn->telrcv_state = CURL_TS_DATA;
+ break;
+
+ case CURL_TS_DO:
+ printoption(data, "RCVD", CURL_DO, c);
+ tn->please_negotiate = 1;
+ rec_do(data, c);
+ tn->telrcv_state = CURL_TS_DATA;
+ break;
+
+ case CURL_TS_DONT:
+ printoption(data, "RCVD", CURL_DONT, c);
+ tn->please_negotiate = 1;
+ rec_dont(data, c);
+ tn->telrcv_state = CURL_TS_DATA;
+ break;
+
+ case CURL_TS_SB:
+ if(c == CURL_IAC)
+ tn->telrcv_state = CURL_TS_SE;
+ else
+ CURL_SB_ACCUM(tn, c);
+ break;
+
+ case CURL_TS_SE:
+ if(c != CURL_SE) {
+ if(c != CURL_IAC) {
+ /*
+ * This is an error. We only expect to get "IAC IAC" or "IAC SE".
+ * Several things may have happened. An IAC was not doubled, the
+ * IAC SE was left off, or another option got inserted into the
+ * suboption are all possibilities. If we assume that the IAC was
+ * not doubled, and really the IAC SE was left off, we could get
+ * into an infinite loop here. So, instead, we terminate the
+ * suboption, and process the partial suboption if we can.
+ */
+ CURL_SB_ACCUM(tn, CURL_IAC);
+ CURL_SB_ACCUM(tn, c);
+ tn->subpointer -= 2;
+ CURL_SB_TERM(tn);
+
+ printoption(data, "In SUBOPTION processing, RCVD", CURL_IAC, c);
+ suboption(data); /* handle sub-option */
+ tn->telrcv_state = CURL_TS_IAC;
+ goto process_iac;
+ }
+ CURL_SB_ACCUM(tn, c);
+ tn->telrcv_state = CURL_TS_SB;
+ }
+ else {
+ CURL_SB_ACCUM(tn, CURL_IAC);
+ CURL_SB_ACCUM(tn, CURL_SE);
+ tn->subpointer -= 2;
+ CURL_SB_TERM(tn);
+ suboption(data); /* handle sub-option */
+ tn->telrcv_state = CURL_TS_DATA;
+ }
+ break;
+ }
+ ++in;
+ }
+ bufferflush();
+ return CURLE_OK;
+}
+
+/* Escape and send a telnet data block */
+static CURLcode send_telnet_data(struct Curl_easy *data,
+ char *buffer, ssize_t nread)
+{
+ ssize_t escapes, i, outlen;
+ unsigned char *outbuf = NULL;
+ CURLcode result = CURLE_OK;
+ ssize_t bytes_written, total_written;
+ struct connectdata *conn = data->conn;
+
+ /* Determine size of new buffer after escaping */
+ escapes = 0;
+ for(i = 0; i < nread; i++)
+ if((unsigned char)buffer[i] == CURL_IAC)
+ escapes++;
+ outlen = nread + escapes;
+
+ if(outlen == nread)
+ outbuf = (unsigned char *)buffer;
+ else {
+ ssize_t j;
+ outbuf = malloc(nread + escapes + 1);
+ if(!outbuf)
+ return CURLE_OUT_OF_MEMORY;
+
+ j = 0;
+ for(i = 0; i < nread; i++) {
+ outbuf[j++] = (unsigned char)buffer[i];
+ if((unsigned char)buffer[i] == CURL_IAC)
+ outbuf[j++] = CURL_IAC;
+ }
+ outbuf[j] = '\0';
+ }
+
+ total_written = 0;
+ while(!result && total_written < outlen) {
+ /* Make sure socket is writable to avoid EWOULDBLOCK condition */
+ struct pollfd pfd[1];
+ pfd[0].fd = conn->sock[FIRSTSOCKET];
+ pfd[0].events = POLLOUT;
+ switch(Curl_poll(pfd, 1, -1)) {
+ case -1: /* error, abort writing */
+ case 0: /* timeout (will never happen) */
+ result = CURLE_SEND_ERROR;
+ break;
+ default: /* write! */
+ bytes_written = 0;
+ result = Curl_write(data, conn->sock[FIRSTSOCKET],
+ outbuf + total_written,
+ outlen - total_written,
+ &bytes_written);
+ total_written += bytes_written;
+ break;
+ }
+ }
+
+ /* Free malloc copy if escaped */
+ if(outbuf != (unsigned char *)buffer)
+ free(outbuf);
+
+ return result;
+}
+
+static CURLcode telnet_done(struct Curl_easy *data,
+ CURLcode status, bool premature)
+{
+ struct TELNET *tn = data->req.p.telnet;
+ (void)status; /* unused */
+ (void)premature; /* not used */
+
+ if(!tn)
+ return CURLE_OK;
+
+ curl_slist_free_all(tn->telnet_vars);
+ tn->telnet_vars = NULL;
+ return CURLE_OK;
+}
+
+static CURLcode telnet_do(struct Curl_easy *data, bool *done)
+{
+ CURLcode result;
+ struct connectdata *conn = data->conn;
+ curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+#ifdef USE_WINSOCK
+ WSAEVENT event_handle;
+ WSANETWORKEVENTS events;
+ HANDLE stdin_handle;
+ HANDLE objs[2];
+ DWORD obj_count;
+ DWORD wait_timeout;
+ DWORD readfile_read;
+ int err;
+#else
+ timediff_t interval_ms;
+ struct pollfd pfd[2];
+ int poll_cnt;
+ curl_off_t total_dl = 0;
+ curl_off_t total_ul = 0;
+#endif
+ ssize_t nread;
+ struct curltime now;
+ bool keepon = TRUE;
+ char *buf = data->state.buffer;
+ struct TELNET *tn;
+
+ *done = TRUE; /* unconditionally */
+
+ result = init_telnet(data);
+ if(result)
+ return result;
+
+ tn = data->req.p.telnet;
+
+ result = check_telnet_options(data);
+ if(result)
+ return result;
+
+#ifdef USE_WINSOCK
+ /* We want to wait for both stdin and the socket. Since
+ ** the select() function in winsock only works on sockets
+ ** we have to use the WaitForMultipleObjects() call.
+ */
+
+ /* First, create a sockets event object */
+ event_handle = WSACreateEvent();
+ if(event_handle == WSA_INVALID_EVENT) {
+ failf(data, "WSACreateEvent failed (%d)", SOCKERRNO);
+ return CURLE_FAILED_INIT;
+ }
+
+ /* Tell winsock what events we want to listen to */
+ if(WSAEventSelect(sockfd, event_handle, FD_READ|FD_CLOSE) == SOCKET_ERROR) {
+ WSACloseEvent(event_handle);
+ return CURLE_OK;
+ }
+
+ /* The get the Windows file handle for stdin */
+ stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
+
+ /* Create the list of objects to wait for */
+ objs[0] = event_handle;
+ objs[1] = stdin_handle;
+
+ /* If stdin_handle is a pipe, use PeekNamedPipe() method to check it,
+ else use the old WaitForMultipleObjects() way */
+ if(GetFileType(stdin_handle) == FILE_TYPE_PIPE ||
+ data->set.is_fread_set) {
+ /* Don't wait for stdin_handle, just wait for event_handle */
+ obj_count = 1;
+ /* Check stdin_handle per 100 milliseconds */
+ wait_timeout = 100;
+ }
+ else {
+ obj_count = 2;
+ wait_timeout = 1000;
+ }
+
+ /* Keep on listening and act on events */
+ while(keepon) {
+ const DWORD buf_size = (DWORD)data->set.buffer_size;
+ DWORD waitret = WaitForMultipleObjects(obj_count, objs,
+ FALSE, wait_timeout);
+ switch(waitret) {
+
+ case WAIT_TIMEOUT:
+ {
+ for(;;) {
+ if(data->set.is_fread_set) {
+ size_t n;
+ /* read from user-supplied method */
+ n = data->state.fread_func(buf, 1, buf_size, data->state.in);
+ if(n == CURL_READFUNC_ABORT) {
+ keepon = FALSE;
+ result = CURLE_READ_ERROR;
+ break;
+ }
+
+ if(n == CURL_READFUNC_PAUSE)
+ break;
+
+ if(n == 0) /* no bytes */
+ break;
+
+ /* fall through with number of bytes read */
+ readfile_read = (DWORD)n;
+ }
+ else {
+ /* read from stdin */
+ if(!PeekNamedPipe(stdin_handle, NULL, 0, NULL,
+ &readfile_read, NULL)) {
+ keepon = FALSE;
+ result = CURLE_READ_ERROR;
+ break;
+ }
+
+ if(!readfile_read)
+ break;
+
+ if(!ReadFile(stdin_handle, buf, buf_size,
+ &readfile_read, NULL)) {
+ keepon = FALSE;
+ result = CURLE_READ_ERROR;
+ break;
+ }
+ }
+
+ result = send_telnet_data(data, buf, readfile_read);
+ if(result) {
+ keepon = FALSE;
+ break;
+ }
+ }
+ }
+ break;
+
+ case WAIT_OBJECT_0 + 1:
+ {
+ if(!ReadFile(stdin_handle, buf, buf_size,
+ &readfile_read, NULL)) {
+ keepon = FALSE;
+ result = CURLE_READ_ERROR;
+ break;
+ }
+
+ result = send_telnet_data(data, buf, readfile_read);
+ if(result) {
+ keepon = FALSE;
+ break;
+ }
+ }
+ break;
+
+ case WAIT_OBJECT_0:
+ {
+ events.lNetworkEvents = 0;
+ if(WSAEnumNetworkEvents(sockfd, event_handle, &events) == SOCKET_ERROR) {
+ err = SOCKERRNO;
+ if(err != EINPROGRESS) {
+ infof(data, "WSAEnumNetworkEvents failed (%d)", err);
+ keepon = FALSE;
+ result = CURLE_READ_ERROR;
+ }
+ break;
+ }
+ if(events.lNetworkEvents & FD_READ) {
+ /* read data from network */
+ result = Curl_read(data, sockfd, buf, data->set.buffer_size, &nread);
+ /* read would've blocked. Loop again */
+ if(result == CURLE_AGAIN)
+ break;
+ /* returned not-zero, this an error */
+ else if(result) {
+ keepon = FALSE;
+ break;
+ }
+ /* returned zero but actually received 0 or less here,
+ the server closed the connection and we bail out */
+ else if(nread <= 0) {
+ keepon = FALSE;
+ break;
+ }
+
+ result = telrcv(data, (unsigned char *) buf, nread);
+ if(result) {
+ keepon = FALSE;
+ break;
+ }
+
+ /* Negotiate if the peer has started negotiating,
+ otherwise don't. We don't want to speak telnet with
+ non-telnet servers, like POP or SMTP. */
+ if(tn->please_negotiate && !tn->already_negotiated) {
+ negotiate(data);
+ tn->already_negotiated = 1;
+ }
+ }
+ if(events.lNetworkEvents & FD_CLOSE) {
+ keepon = FALSE;
+ }
+ }
+ break;
+
+ }
+
+ if(data->set.timeout) {
+ now = Curl_now();
+ if(Curl_timediff(now, conn->created) >= data->set.timeout) {
+ failf(data, "Time-out");
+ result = CURLE_OPERATION_TIMEDOUT;
+ keepon = FALSE;
+ }
+ }
+ }
+
+ /* We called WSACreateEvent, so call WSACloseEvent */
+ if(!WSACloseEvent(event_handle)) {
+ infof(data, "WSACloseEvent failed (%d)", SOCKERRNO);
+ }
+#else
+ pfd[0].fd = sockfd;
+ pfd[0].events = POLLIN;
+
+ if(data->set.is_fread_set) {
+ poll_cnt = 1;
+ interval_ms = 100; /* poll user-supplied read function */
+ }
+ else {
+ /* really using fread, so infile is a FILE* */
+ pfd[1].fd = fileno((FILE *)data->state.in);
+ pfd[1].events = POLLIN;
+ poll_cnt = 2;
+ interval_ms = 1 * 1000;
+ }
+
+ while(keepon) {
+ DEBUGF(infof(data, "telnet_do(handle=%p), poll %d fds", data, poll_cnt));
+ switch(Curl_poll(pfd, poll_cnt, interval_ms)) {
+ case -1: /* error, stop reading */
+ keepon = FALSE;
+ continue;
+ case 0: /* timeout */
+ pfd[0].revents = 0;
+ pfd[1].revents = 0;
+ /* FALLTHROUGH */
+ default: /* read! */
+ if(pfd[0].revents & POLLIN) {
+ /* read data from network */
+ result = Curl_read(data, sockfd, buf, data->set.buffer_size, &nread);
+ /* read would've blocked. Loop again */
+ if(result == CURLE_AGAIN)
+ break;
+ /* returned not-zero, this an error */
+ if(result) {
+ keepon = FALSE;
+ /* TODO: in test 1452, macOS sees a ECONNRESET sometimes?
+ * Is this the telnet test server not shutting down the socket
+ * in a clean way? Seems to be timing related, happens more
+ * on slow debug build */
+ if(data->state.os_errno == ECONNRESET) {
+ DEBUGF(infof(data, "telnet_do(handle=%p), unexpected ECONNRESET"
+ " on recv", data));
+ }
+ break;
+ }
+ /* returned zero but actually received 0 or less here,
+ the server closed the connection and we bail out */
+ else if(nread <= 0) {
+ keepon = FALSE;
+ break;
+ }
+
+ total_dl += nread;
+ Curl_pgrsSetDownloadCounter(data, total_dl);
+ result = telrcv(data, (unsigned char *)buf, nread);
+ if(result) {
+ keepon = FALSE;
+ break;
+ }
+
+ /* Negotiate if the peer has started negotiating,
+ otherwise don't. We don't want to speak telnet with
+ non-telnet servers, like POP or SMTP. */
+ if(tn->please_negotiate && !tn->already_negotiated) {
+ negotiate(data);
+ tn->already_negotiated = 1;
+ }
+ }
+
+ nread = 0;
+ if(poll_cnt == 2) {
+ if(pfd[1].revents & POLLIN) { /* read from in file */
+ nread = read(pfd[1].fd, buf, data->set.buffer_size);
+ }
+ }
+ else {
+ /* read from user-supplied method */
+ nread = (int)data->state.fread_func(buf, 1, data->set.buffer_size,
+ data->state.in);
+ if(nread == CURL_READFUNC_ABORT) {
+ keepon = FALSE;
+ break;
+ }
+ if(nread == CURL_READFUNC_PAUSE)
+ break;
+ }
+
+ if(nread > 0) {
+ result = send_telnet_data(data, buf, nread);
+ if(result) {
+ keepon = FALSE;
+ break;
+ }
+ total_ul += nread;
+ Curl_pgrsSetUploadCounter(data, total_ul);
+ }
+ else if(nread < 0)
+ keepon = FALSE;
+
+ break;
+ } /* poll switch statement */
+
+ if(data->set.timeout) {
+ now = Curl_now();
+ if(Curl_timediff(now, conn->created) >= data->set.timeout) {
+ failf(data, "Time-out");
+ result = CURLE_OPERATION_TIMEDOUT;
+ keepon = FALSE;
+ }
+ }
+
+ if(Curl_pgrsUpdate(data)) {
+ result = CURLE_ABORTED_BY_CALLBACK;
+ break;
+ }
+ }
+#endif
+ /* mark this as "no further transfer wanted" */
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
+
+ return result;
+}
+#endif
diff --git a/libs/libcurl/src/telnet.h b/libs/libcurl/src/telnet.h index 6dd99b48dc..fa75355dbf 100644 --- a/libs/libcurl/src/telnet.h +++ b/libs/libcurl/src/telnet.h @@ -1,30 +1,30 @@ -#ifndef HEADER_CURL_TELNET_H -#define HEADER_CURL_TELNET_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 -extern const struct Curl_handler Curl_handler_telnet; -#endif - -#endif /* HEADER_CURL_TELNET_H */ +#ifndef HEADER_CURL_TELNET_H
+#define HEADER_CURL_TELNET_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+extern const struct Curl_handler Curl_handler_telnet;
+#endif
+
+#endif /* HEADER_CURL_TELNET_H */
diff --git a/libs/libcurl/src/tftp.c b/libs/libcurl/src/tftp.c index 9e6d9490ed..900797d87f 100644 --- a/libs/libcurl/src/tftp.c +++ b/libs/libcurl/src/tftp.c @@ -1,1411 +1,1411 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_TFTP - -#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 HAVE_NET_IF_H -#include <net/if.h> -#endif -#ifdef HAVE_SYS_IOCTL_H -#include <sys/ioctl.h> -#endif - -#ifdef HAVE_SYS_PARAM_H -#include <sys/param.h> -#endif - -#include "urldata.h" -#include <curl/curl.h> -#include "transfer.h" -#include "sendf.h" -#include "tftp.h" -#include "progress.h" -#include "connect.h" -#include "strerror.h" -#include "sockaddr.h" /* required for Curl_sockaddr_storage */ -#include "multiif.h" -#include "url.h" -#include "strcase.h" -#include "speedcheck.h" -#include "select.h" -#include "escape.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* RFC2348 allows the block size to be negotiated */ -#define TFTP_BLKSIZE_DEFAULT 512 -#define TFTP_BLKSIZE_MIN 8 -#define TFTP_BLKSIZE_MAX 65464 -#define TFTP_OPTION_BLKSIZE "blksize" - -/* from RFC2349: */ -#define TFTP_OPTION_TSIZE "tsize" -#define TFTP_OPTION_INTERVAL "timeout" - -typedef enum { - TFTP_MODE_NETASCII = 0, - TFTP_MODE_OCTET -} tftp_mode_t; - -typedef enum { - TFTP_STATE_START = 0, - TFTP_STATE_RX, - TFTP_STATE_TX, - TFTP_STATE_FIN -} tftp_state_t; - -typedef enum { - TFTP_EVENT_NONE = -1, - TFTP_EVENT_INIT = 0, - TFTP_EVENT_RRQ = 1, - TFTP_EVENT_WRQ = 2, - TFTP_EVENT_DATA = 3, - TFTP_EVENT_ACK = 4, - TFTP_EVENT_ERROR = 5, - TFTP_EVENT_OACK = 6, - TFTP_EVENT_TIMEOUT -} tftp_event_t; - -typedef enum { - TFTP_ERR_UNDEF = 0, - TFTP_ERR_NOTFOUND, - TFTP_ERR_PERM, - TFTP_ERR_DISKFULL, - TFTP_ERR_ILLEGAL, - TFTP_ERR_UNKNOWNID, - TFTP_ERR_EXISTS, - TFTP_ERR_NOSUCHUSER, /* This will never be triggered by this code */ - - /* The remaining error codes are internal to curl */ - TFTP_ERR_NONE = -100, - TFTP_ERR_TIMEOUT, - TFTP_ERR_NORESPONSE -} tftp_error_t; - -struct tftp_packet { - unsigned char *data; -}; - -struct tftp_state_data { - tftp_state_t state; - tftp_mode_t mode; - tftp_error_t error; - tftp_event_t event; - struct Curl_easy *data; - curl_socket_t sockfd; - int retries; - int retry_time; - int retry_max; - time_t rx_time; - struct Curl_sockaddr_storage local_addr; - struct Curl_sockaddr_storage remote_addr; - curl_socklen_t remote_addrlen; - int rbytes; - int sbytes; - int blksize; - int requested_blksize; - unsigned short block; - struct tftp_packet rpacket; - struct tftp_packet spacket; -}; - - -/* Forward declarations */ -static CURLcode tftp_rx(struct tftp_state_data *state, tftp_event_t event); -static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event); -static CURLcode tftp_connect(struct Curl_easy *data, bool *done); -static CURLcode tftp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection); -static CURLcode tftp_do(struct Curl_easy *data, bool *done); -static CURLcode tftp_done(struct Curl_easy *data, - CURLcode, bool premature); -static CURLcode tftp_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done); -static CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done); -static int tftp_getsock(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t *socks); -static CURLcode tftp_translate_code(tftp_error_t error); - - -/* - * TFTP protocol handler. - */ - -const struct Curl_handler Curl_handler_tftp = { - "TFTP", /* scheme */ - tftp_setup_connection, /* setup_connection */ - tftp_do, /* do_it */ - tftp_done, /* done */ - ZERO_NULL, /* do_more */ - tftp_connect, /* connect_it */ - tftp_multi_statemach, /* connecting */ - tftp_doing, /* doing */ - tftp_getsock, /* proto_getsock */ - tftp_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - tftp_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_TFTP, /* defport */ - CURLPROTO_TFTP, /* protocol */ - CURLPROTO_TFTP, /* family */ - PROTOPT_NOTCPPROXY | PROTOPT_NOURLQUERY /* flags */ -}; - -/********************************************************** - * - * tftp_set_timeouts - - * - * Set timeouts based on state machine state. - * Use user provided connect timeouts until DATA or ACK - * packet is received, then use user-provided transfer timeouts - * - * - **********************************************************/ -static CURLcode tftp_set_timeouts(struct tftp_state_data *state) -{ - time_t maxtime, timeout; - timediff_t timeout_ms; - bool start = (state->state == TFTP_STATE_START) ? TRUE : FALSE; - - /* Compute drop-dead time */ - timeout_ms = Curl_timeleft(state->data, NULL, start); - - if(timeout_ms < 0) { - /* time-out, bail out, go home */ - failf(state->data, "Connection time-out"); - return CURLE_OPERATION_TIMEDOUT; - } - - if(timeout_ms > 0) - maxtime = (time_t)(timeout_ms + 500) / 1000; - else - maxtime = 3600; /* use for calculating block timeouts */ - - /* Set per-block timeout to total */ - timeout = maxtime; - - /* Average reposting an ACK after 5 seconds */ - state->retry_max = (int)timeout/5; - - /* But bound the total number */ - if(state->retry_max<3) - state->retry_max = 3; - - if(state->retry_max>50) - state->retry_max = 50; - - /* Compute the re-ACK interval to suit the timeout */ - state->retry_time = (int)(timeout/state->retry_max); - if(state->retry_time<1) - state->retry_time = 1; - - infof(state->data, - "set timeouts for state %d; Total % " CURL_FORMAT_CURL_OFF_T - ", retry %d maxtry %d", - (int)state->state, timeout_ms, state->retry_time, state->retry_max); - - /* init RX time */ - time(&state->rx_time); - - return CURLE_OK; -} - -/********************************************************** - * - * tftp_set_send_first - * - * Event handler for the START state - * - **********************************************************/ - -static void setpacketevent(struct tftp_packet *packet, unsigned short num) -{ - packet->data[0] = (unsigned char)(num >> 8); - packet->data[1] = (unsigned char)(num & 0xff); -} - - -static void setpacketblock(struct tftp_packet *packet, unsigned short num) -{ - packet->data[2] = (unsigned char)(num >> 8); - packet->data[3] = (unsigned char)(num & 0xff); -} - -static unsigned short getrpacketevent(const struct tftp_packet *packet) -{ - return (unsigned short)((packet->data[0] << 8) | packet->data[1]); -} - -static unsigned short getrpacketblock(const struct tftp_packet *packet) -{ - return (unsigned short)((packet->data[2] << 8) | packet->data[3]); -} - -static size_t tftp_strnlen(const char *string, size_t maxlen) -{ - const char *end = memchr(string, '\0', maxlen); - return end ? (size_t) (end - string) : maxlen; -} - -static const char *tftp_option_get(const char *buf, size_t len, - const char **option, const char **value) -{ - size_t loc; - - loc = tftp_strnlen(buf, len); - loc++; /* NULL term */ - - if(loc >= len) - return NULL; - *option = buf; - - loc += tftp_strnlen(buf + loc, len-loc); - loc++; /* NULL term */ - - if(loc > len) - return NULL; - *value = &buf[strlen(*option) + 1]; - - return &buf[loc]; -} - -static CURLcode tftp_parse_option_ack(struct tftp_state_data *state, - const char *ptr, int len) -{ - const char *tmp = ptr; - struct Curl_easy *data = state->data; - - /* if OACK doesn't contain blksize option, the default (512) must be used */ - state->blksize = TFTP_BLKSIZE_DEFAULT; - - while(tmp < ptr + len) { - const char *option, *value; - - tmp = tftp_option_get(tmp, ptr + len - tmp, &option, &value); - if(!tmp) { - failf(data, "Malformed ACK packet, rejecting"); - return CURLE_TFTP_ILLEGAL; - } - - infof(data, "got option=(%s) value=(%s)", option, value); - - if(checkprefix(TFTP_OPTION_BLKSIZE, option)) { - long blksize; - - blksize = strtol(value, NULL, 10); - - if(!blksize) { - failf(data, "invalid blocksize value in OACK packet"); - return CURLE_TFTP_ILLEGAL; - } - if(blksize > TFTP_BLKSIZE_MAX) { - failf(data, "%s (%d)", "blksize is larger than max supported", - TFTP_BLKSIZE_MAX); - return CURLE_TFTP_ILLEGAL; - } - else if(blksize < TFTP_BLKSIZE_MIN) { - failf(data, "%s (%d)", "blksize is smaller than min supported", - TFTP_BLKSIZE_MIN); - return CURLE_TFTP_ILLEGAL; - } - else if(blksize > state->requested_blksize) { - /* could realloc pkt buffers here, but the spec doesn't call out - * support for the server requesting a bigger blksize than the client - * requests */ - failf(data, "%s (%ld)", - "server requested blksize larger than allocated", blksize); - return CURLE_TFTP_ILLEGAL; - } - - state->blksize = (int)blksize; - infof(data, "%s (%d) %s (%d)", "blksize parsed from OACK", - state->blksize, "requested", state->requested_blksize); - } - else if(checkprefix(TFTP_OPTION_TSIZE, option)) { - long tsize = 0; - - tsize = strtol(value, NULL, 10); - infof(data, "%s (%ld)", "tsize parsed from OACK", tsize); - - /* tsize should be ignored on upload: Who cares about the size of the - remote file? */ - if(!data->set.upload) { - if(!tsize) { - failf(data, "invalid tsize -:%s:- value in OACK packet", value); - return CURLE_TFTP_ILLEGAL; - } - Curl_pgrsSetDownloadSize(data, tsize); - } - } - } - - return CURLE_OK; -} - -static CURLcode tftp_option_add(struct tftp_state_data *state, size_t *csize, - char *buf, const char *option) -{ - if(( strlen(option) + *csize + 1) > (size_t)state->blksize) - return CURLE_TFTP_ILLEGAL; - strcpy(buf, option); - *csize += strlen(option) + 1; - return CURLE_OK; -} - -static CURLcode tftp_connect_for_tx(struct tftp_state_data *state, - tftp_event_t event) -{ - CURLcode result; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - struct Curl_easy *data = state->data; - - infof(data, "%s", "Connected for transmit"); -#endif - state->state = TFTP_STATE_TX; - result = tftp_set_timeouts(state); - if(result) - return result; - return tftp_tx(state, event); -} - -static CURLcode tftp_connect_for_rx(struct tftp_state_data *state, - tftp_event_t event) -{ - CURLcode result; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - struct Curl_easy *data = state->data; - - infof(data, "%s", "Connected for receive"); -#endif - state->state = TFTP_STATE_RX; - result = tftp_set_timeouts(state); - if(result) - return result; - return tftp_rx(state, event); -} - -static CURLcode tftp_send_first(struct tftp_state_data *state, - tftp_event_t event) -{ - size_t sbytes; - ssize_t senddata; - const char *mode = "octet"; - char *filename; - struct Curl_easy *data = state->data; - CURLcode result = CURLE_OK; - - /* Set ascii mode if -B flag was used */ - if(data->state.prefer_ascii) - mode = "netascii"; - - switch(event) { - - case TFTP_EVENT_INIT: /* Send the first packet out */ - case TFTP_EVENT_TIMEOUT: /* Resend the first packet out */ - /* Increment the retry counter, quit if over the limit */ - state->retries++; - if(state->retries>state->retry_max) { - state->error = TFTP_ERR_NORESPONSE; - state->state = TFTP_STATE_FIN; - return result; - } - - if(data->set.upload) { - /* If we are uploading, send an WRQ */ - setpacketevent(&state->spacket, TFTP_EVENT_WRQ); - state->data->req.upload_fromhere = - (char *)state->spacket.data + 4; - if(data->state.infilesize != -1) - Curl_pgrsSetUploadSize(data, data->state.infilesize); - } - else { - /* If we are downloading, send an RRQ */ - setpacketevent(&state->spacket, TFTP_EVENT_RRQ); - } - /* As RFC3617 describes the separator slash is not actually part of the - file name so we skip the always-present first letter of the path - string. */ - result = Curl_urldecode(&state->data->state.up.path[1], 0, - &filename, NULL, REJECT_ZERO); - if(result) - return result; - - if(strlen(filename) > (state->blksize - strlen(mode) - 4)) { - failf(data, "TFTP file name too long"); - free(filename); - return CURLE_TFTP_ILLEGAL; /* too long file name field */ - } - - msnprintf((char *)state->spacket.data + 2, - state->blksize, - "%s%c%s%c", filename, '\0', mode, '\0'); - sbytes = 4 + strlen(filename) + strlen(mode); - - /* optional addition of TFTP options */ - if(!data->set.tftp_no_options) { - char buf[64]; - /* add tsize option */ - if(data->set.upload && (data->state.infilesize != -1)) - msnprintf(buf, sizeof(buf), "%" CURL_FORMAT_CURL_OFF_T, - data->state.infilesize); - else - strcpy(buf, "0"); /* the destination is large enough */ - - result = tftp_option_add(state, &sbytes, - (char *)state->spacket.data + sbytes, - TFTP_OPTION_TSIZE); - if(result == CURLE_OK) - result = tftp_option_add(state, &sbytes, - (char *)state->spacket.data + sbytes, buf); - - /* add blksize option */ - msnprintf(buf, sizeof(buf), "%d", state->requested_blksize); - if(result == CURLE_OK) - result = tftp_option_add(state, &sbytes, - (char *)state->spacket.data + sbytes, - TFTP_OPTION_BLKSIZE); - if(result == CURLE_OK) - result = tftp_option_add(state, &sbytes, - (char *)state->spacket.data + sbytes, buf); - - /* add timeout option */ - msnprintf(buf, sizeof(buf), "%d", state->retry_time); - if(result == CURLE_OK) - result = tftp_option_add(state, &sbytes, - (char *)state->spacket.data + sbytes, - TFTP_OPTION_INTERVAL); - if(result == CURLE_OK) - result = tftp_option_add(state, &sbytes, - (char *)state->spacket.data + sbytes, buf); - - if(result != CURLE_OK) { - failf(data, "TFTP buffer too small for options"); - free(filename); - return CURLE_TFTP_ILLEGAL; - } - } - - /* the typecase for the 3rd argument is mostly for systems that do - not have a size_t argument, like older unixes that want an 'int' */ - senddata = sendto(state->sockfd, (void *)state->spacket.data, - (SEND_TYPE_ARG3)sbytes, 0, - data->conn->ip_addr->ai_addr, - data->conn->ip_addr->ai_addrlen); - if(senddata != (ssize_t)sbytes) { - char buffer[STRERROR_LEN]; - failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - } - free(filename); - break; - - case TFTP_EVENT_OACK: - if(data->set.upload) { - result = tftp_connect_for_tx(state, event); - } - else { - result = tftp_connect_for_rx(state, event); - } - break; - - case TFTP_EVENT_ACK: /* Connected for transmit */ - result = tftp_connect_for_tx(state, event); - break; - - case TFTP_EVENT_DATA: /* Connected for receive */ - result = tftp_connect_for_rx(state, event); - break; - - case TFTP_EVENT_ERROR: - state->state = TFTP_STATE_FIN; - break; - - default: - failf(state->data, "tftp_send_first: internal error"); - break; - } - - return result; -} - -/* the next blocknum is x + 1 but it needs to wrap at an unsigned 16bit - boundary */ -#define NEXT_BLOCKNUM(x) (((x) + 1)&0xffff) - -/********************************************************** - * - * tftp_rx - * - * Event handler for the RX state - * - **********************************************************/ -static CURLcode tftp_rx(struct tftp_state_data *state, - tftp_event_t event) -{ - ssize_t sbytes; - int rblock; - struct Curl_easy *data = state->data; - char buffer[STRERROR_LEN]; - - switch(event) { - - case TFTP_EVENT_DATA: - /* Is this the block we expect? */ - rblock = getrpacketblock(&state->rpacket); - if(NEXT_BLOCKNUM(state->block) == rblock) { - /* This is the expected block. Reset counters and ACK it. */ - state->retries = 0; - } - else if(state->block == rblock) { - /* This is the last recently received block again. Log it and ACK it - again. */ - infof(data, "Received last DATA packet block %d again.", rblock); - } - else { - /* totally unexpected, just log it */ - infof(data, - "Received unexpected DATA packet block %d, expecting block %d", - rblock, NEXT_BLOCKNUM(state->block)); - break; - } - - /* ACK this block. */ - state->block = (unsigned short)rblock; - setpacketevent(&state->spacket, TFTP_EVENT_ACK); - setpacketblock(&state->spacket, state->block); - sbytes = sendto(state->sockfd, (void *)state->spacket.data, - 4, SEND_4TH_ARG, - (struct sockaddr *)&state->remote_addr, - state->remote_addrlen); - if(sbytes < 0) { - failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - return CURLE_SEND_ERROR; - } - - /* Check if completed (That is, a less than full packet is received) */ - if(state->rbytes < (ssize_t)state->blksize + 4) { - state->state = TFTP_STATE_FIN; - } - else { - state->state = TFTP_STATE_RX; - } - time(&state->rx_time); - break; - - case TFTP_EVENT_OACK: - /* ACK option acknowledgement so we can move on to data */ - state->block = 0; - state->retries = 0; - setpacketevent(&state->spacket, TFTP_EVENT_ACK); - setpacketblock(&state->spacket, state->block); - sbytes = sendto(state->sockfd, (void *)state->spacket.data, - 4, SEND_4TH_ARG, - (struct sockaddr *)&state->remote_addr, - state->remote_addrlen); - if(sbytes < 0) { - failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - return CURLE_SEND_ERROR; - } - - /* we're ready to RX data */ - state->state = TFTP_STATE_RX; - time(&state->rx_time); - break; - - case TFTP_EVENT_TIMEOUT: - /* Increment the retry count and fail if over the limit */ - state->retries++; - infof(data, - "Timeout waiting for block %d ACK. Retries = %d", - NEXT_BLOCKNUM(state->block), state->retries); - if(state->retries > state->retry_max) { - state->error = TFTP_ERR_TIMEOUT; - state->state = TFTP_STATE_FIN; - } - else { - /* Resend the previous ACK */ - sbytes = sendto(state->sockfd, (void *)state->spacket.data, - 4, SEND_4TH_ARG, - (struct sockaddr *)&state->remote_addr, - state->remote_addrlen); - if(sbytes<0) { - failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - return CURLE_SEND_ERROR; - } - } - break; - - case TFTP_EVENT_ERROR: - setpacketevent(&state->spacket, TFTP_EVENT_ERROR); - setpacketblock(&state->spacket, state->block); - (void)sendto(state->sockfd, (void *)state->spacket.data, - 4, SEND_4TH_ARG, - (struct sockaddr *)&state->remote_addr, - state->remote_addrlen); - /* don't bother with the return code, but if the socket is still up we - * should be a good TFTP client and let the server know we're done */ - state->state = TFTP_STATE_FIN; - break; - - default: - failf(data, "%s", "tftp_rx: internal error"); - return CURLE_TFTP_ILLEGAL; /* not really the perfect return code for - this */ - } - return CURLE_OK; -} - -/********************************************************** - * - * tftp_tx - * - * Event handler for the TX state - * - **********************************************************/ -static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event) -{ - struct Curl_easy *data = state->data; - ssize_t sbytes; - CURLcode result = CURLE_OK; - struct SingleRequest *k = &data->req; - size_t cb; /* Bytes currently read */ - char buffer[STRERROR_LEN]; - - switch(event) { - - case TFTP_EVENT_ACK: - case TFTP_EVENT_OACK: - if(event == TFTP_EVENT_ACK) { - /* Ack the packet */ - int rblock = getrpacketblock(&state->rpacket); - - if(rblock != state->block && - /* There's a bug in tftpd-hpa that causes it to send us an ack for - * 65535 when the block number wraps to 0. So when we're expecting - * 0, also accept 65535. See - * https://www.syslinux.org/archives/2010-September/015612.html - * */ - !(state->block == 0 && rblock == 65535)) { - /* This isn't the expected block. Log it and up the retry counter */ - infof(data, "Received ACK for block %d, expecting %d", - rblock, state->block); - state->retries++; - /* Bail out if over the maximum */ - if(state->retries>state->retry_max) { - failf(data, "tftp_tx: giving up waiting for block %d ack", - state->block); - result = CURLE_SEND_ERROR; - } - else { - /* Re-send the data packet */ - sbytes = sendto(state->sockfd, (void *)state->spacket.data, - 4 + state->sbytes, SEND_4TH_ARG, - (struct sockaddr *)&state->remote_addr, - state->remote_addrlen); - /* Check all sbytes were sent */ - if(sbytes<0) { - failf(data, "%s", Curl_strerror(SOCKERRNO, - buffer, sizeof(buffer))); - result = CURLE_SEND_ERROR; - } - } - - return result; - } - /* This is the expected packet. Reset the counters and send the next - block */ - time(&state->rx_time); - state->block++; - } - else - state->block = 1; /* first data block is 1 when using OACK */ - - state->retries = 0; - setpacketevent(&state->spacket, TFTP_EVENT_DATA); - setpacketblock(&state->spacket, state->block); - if(state->block > 1 && state->sbytes < state->blksize) { - state->state = TFTP_STATE_FIN; - return CURLE_OK; - } - - /* TFTP considers data block size < 512 bytes as an end of session. So - * in some cases we must wait for additional data to build full (512 bytes) - * data block. - * */ - state->sbytes = 0; - state->data->req.upload_fromhere = (char *)state->spacket.data + 4; - do { - result = Curl_fillreadbuffer(data, state->blksize - state->sbytes, &cb); - if(result) - return result; - state->sbytes += (int)cb; - state->data->req.upload_fromhere += cb; - } while(state->sbytes < state->blksize && cb); - - sbytes = sendto(state->sockfd, (void *) state->spacket.data, - 4 + state->sbytes, SEND_4TH_ARG, - (struct sockaddr *)&state->remote_addr, - state->remote_addrlen); - /* Check all sbytes were sent */ - if(sbytes<0) { - failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - return CURLE_SEND_ERROR; - } - /* Update the progress meter */ - k->writebytecount += state->sbytes; - Curl_pgrsSetUploadCounter(data, k->writebytecount); - break; - - case TFTP_EVENT_TIMEOUT: - /* Increment the retry counter and log the timeout */ - state->retries++; - infof(data, "Timeout waiting for block %d ACK. " - " Retries = %d", NEXT_BLOCKNUM(state->block), state->retries); - /* Decide if we've had enough */ - if(state->retries > state->retry_max) { - state->error = TFTP_ERR_TIMEOUT; - state->state = TFTP_STATE_FIN; - } - else { - /* Re-send the data packet */ - sbytes = sendto(state->sockfd, (void *)state->spacket.data, - 4 + state->sbytes, SEND_4TH_ARG, - (struct sockaddr *)&state->remote_addr, - state->remote_addrlen); - /* Check all sbytes were sent */ - if(sbytes<0) { - failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - return CURLE_SEND_ERROR; - } - /* since this was a re-send, we remain at the still byte position */ - Curl_pgrsSetUploadCounter(data, k->writebytecount); - } - break; - - case TFTP_EVENT_ERROR: - state->state = TFTP_STATE_FIN; - setpacketevent(&state->spacket, TFTP_EVENT_ERROR); - setpacketblock(&state->spacket, state->block); - (void)sendto(state->sockfd, (void *)state->spacket.data, 4, SEND_4TH_ARG, - (struct sockaddr *)&state->remote_addr, - state->remote_addrlen); - /* don't bother with the return code, but if the socket is still up we - * should be a good TFTP client and let the server know we're done */ - state->state = TFTP_STATE_FIN; - break; - - default: - failf(data, "tftp_tx: internal error, event: %i", (int)(event)); - break; - } - - return result; -} - -/********************************************************** - * - * tftp_translate_code - * - * Translate internal error codes to CURL error codes - * - **********************************************************/ -static CURLcode tftp_translate_code(tftp_error_t error) -{ - CURLcode result = CURLE_OK; - - if(error != TFTP_ERR_NONE) { - switch(error) { - case TFTP_ERR_NOTFOUND: - result = CURLE_TFTP_NOTFOUND; - break; - case TFTP_ERR_PERM: - result = CURLE_TFTP_PERM; - break; - case TFTP_ERR_DISKFULL: - result = CURLE_REMOTE_DISK_FULL; - break; - case TFTP_ERR_UNDEF: - case TFTP_ERR_ILLEGAL: - result = CURLE_TFTP_ILLEGAL; - break; - case TFTP_ERR_UNKNOWNID: - result = CURLE_TFTP_UNKNOWNID; - break; - case TFTP_ERR_EXISTS: - result = CURLE_REMOTE_FILE_EXISTS; - break; - case TFTP_ERR_NOSUCHUSER: - result = CURLE_TFTP_NOSUCHUSER; - break; - case TFTP_ERR_TIMEOUT: - result = CURLE_OPERATION_TIMEDOUT; - break; - case TFTP_ERR_NORESPONSE: - result = CURLE_COULDNT_CONNECT; - break; - default: - result = CURLE_ABORTED_BY_CALLBACK; - break; - } - } - else - result = CURLE_OK; - - return result; -} - -/********************************************************** - * - * tftp_state_machine - * - * The tftp state machine event dispatcher - * - **********************************************************/ -static CURLcode tftp_state_machine(struct tftp_state_data *state, - tftp_event_t event) -{ - CURLcode result = CURLE_OK; - struct Curl_easy *data = state->data; - - switch(state->state) { - case TFTP_STATE_START: - DEBUGF(infof(data, "TFTP_STATE_START")); - result = tftp_send_first(state, event); - break; - case TFTP_STATE_RX: - DEBUGF(infof(data, "TFTP_STATE_RX")); - result = tftp_rx(state, event); - break; - case TFTP_STATE_TX: - DEBUGF(infof(data, "TFTP_STATE_TX")); - result = tftp_tx(state, event); - break; - case TFTP_STATE_FIN: - infof(data, "%s", "TFTP finished"); - break; - default: - DEBUGF(infof(data, "STATE: %d", state->state)); - failf(data, "%s", "Internal state machine error"); - result = CURLE_TFTP_ILLEGAL; - break; - } - - return result; -} - -/********************************************************** - * - * tftp_disconnect - * - * The disconnect callback - * - **********************************************************/ -static CURLcode tftp_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead_connection) -{ - struct tftp_state_data *state = conn->proto.tftpc; - (void) data; - (void) dead_connection; - - /* done, free dynamically allocated pkt buffers */ - if(state) { - Curl_safefree(state->rpacket.data); - Curl_safefree(state->spacket.data); - free(state); - } - - return CURLE_OK; -} - -/********************************************************** - * - * tftp_connect - * - * The connect callback - * - **********************************************************/ -static CURLcode tftp_connect(struct Curl_easy *data, bool *done) -{ - struct tftp_state_data *state; - int blksize; - int need_blksize; - struct connectdata *conn = data->conn; - - blksize = TFTP_BLKSIZE_DEFAULT; - - state = conn->proto.tftpc = calloc(1, sizeof(struct tftp_state_data)); - if(!state) - return CURLE_OUT_OF_MEMORY; - - /* alloc pkt buffers based on specified blksize */ - if(data->set.tftp_blksize) { - blksize = (int)data->set.tftp_blksize; - if(blksize > TFTP_BLKSIZE_MAX || blksize < TFTP_BLKSIZE_MIN) - return CURLE_TFTP_ILLEGAL; - } - - need_blksize = blksize; - /* default size is the fallback when no OACK is received */ - if(need_blksize < TFTP_BLKSIZE_DEFAULT) - need_blksize = TFTP_BLKSIZE_DEFAULT; - - if(!state->rpacket.data) { - state->rpacket.data = calloc(1, need_blksize + 2 + 2); - - if(!state->rpacket.data) - return CURLE_OUT_OF_MEMORY; - } - - if(!state->spacket.data) { - state->spacket.data = calloc(1, need_blksize + 2 + 2); - - if(!state->spacket.data) - return CURLE_OUT_OF_MEMORY; - } - - /* we don't keep TFTP connections up basically because there's none or very - * little gain for UDP */ - connclose(conn, "TFTP"); - - state->data = data; - state->sockfd = conn->sock[FIRSTSOCKET]; - state->state = TFTP_STATE_START; - state->error = TFTP_ERR_NONE; - state->blksize = TFTP_BLKSIZE_DEFAULT; /* Unless updated by OACK response */ - state->requested_blksize = blksize; - - ((struct sockaddr *)&state->local_addr)->sa_family = - (CURL_SA_FAMILY_T)(conn->ip_addr->ai_family); - - tftp_set_timeouts(state); - - if(!conn->bits.bound) { - /* If not already bound, bind to any interface, random UDP port. If it is - * reused or a custom local port was desired, this has already been done! - * - * We once used the size of the local_addr struct as the third argument - * for bind() to better work with IPv6 or whatever size the struct could - * have, but we learned that at least Tru64, AIX and IRIX *requires* the - * size of that argument to match the exact size of a 'sockaddr_in' struct - * when running IPv4-only. - * - * Therefore we use the size from the address we connected to, which we - * assume uses the same IP version and thus hopefully this works for both - * IPv4 and IPv6... - */ - int rc = bind(state->sockfd, (struct sockaddr *)&state->local_addr, - conn->ip_addr->ai_addrlen); - if(rc) { - char buffer[STRERROR_LEN]; - failf(data, "bind() failed; %s", - Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - return CURLE_COULDNT_CONNECT; - } - conn->bits.bound = TRUE; - } - - Curl_pgrsStartNow(data); - - *done = TRUE; - - return CURLE_OK; -} - -/********************************************************** - * - * tftp_done - * - * The done callback - * - **********************************************************/ -static CURLcode tftp_done(struct Curl_easy *data, CURLcode status, - bool premature) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct tftp_state_data *state = conn->proto.tftpc; - - (void)status; /* unused */ - (void)premature; /* not used */ - - if(Curl_pgrsDone(data)) - return CURLE_ABORTED_BY_CALLBACK; - - /* If we have encountered an error */ - if(state) - result = tftp_translate_code(state->error); - - return result; -} - -/********************************************************** - * - * tftp_getsock - * - * The getsock callback - * - **********************************************************/ -static int tftp_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks) -{ - (void)data; - socks[0] = conn->sock[FIRSTSOCKET]; - return GETSOCK_READSOCK(0); -} - -/********************************************************** - * - * tftp_receive_packet - * - * Called once select fires and data is ready on the socket - * - **********************************************************/ -static CURLcode tftp_receive_packet(struct Curl_easy *data) -{ - struct Curl_sockaddr_storage fromaddr; - curl_socklen_t fromlen; - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct tftp_state_data *state = conn->proto.tftpc; - struct SingleRequest *k = &data->req; - - /* Receive the packet */ - fromlen = sizeof(fromaddr); - state->rbytes = (int)recvfrom(state->sockfd, - (void *)state->rpacket.data, - state->blksize + 4, - 0, - (struct sockaddr *)&fromaddr, - &fromlen); - if(state->remote_addrlen == 0) { - memcpy(&state->remote_addr, &fromaddr, fromlen); - state->remote_addrlen = fromlen; - } - - /* Sanity check packet length */ - if(state->rbytes < 4) { - failf(data, "Received too short packet"); - /* Not a timeout, but how best to handle it? */ - state->event = TFTP_EVENT_TIMEOUT; - } - else { - /* The event is given by the TFTP packet time */ - unsigned short event = getrpacketevent(&state->rpacket); - state->event = (tftp_event_t)event; - - switch(state->event) { - case TFTP_EVENT_DATA: - /* Don't pass to the client empty or retransmitted packets */ - if(state->rbytes > 4 && - (NEXT_BLOCKNUM(state->block) == getrpacketblock(&state->rpacket))) { - result = Curl_client_write(data, CLIENTWRITE_BODY, - (char *)state->rpacket.data + 4, - state->rbytes-4); - if(result) { - tftp_state_machine(state, TFTP_EVENT_ERROR); - return result; - } - k->bytecount += state->rbytes-4; - Curl_pgrsSetDownloadCounter(data, (curl_off_t) k->bytecount); - } - break; - case TFTP_EVENT_ERROR: - { - unsigned short error = getrpacketblock(&state->rpacket); - char *str = (char *)state->rpacket.data + 4; - size_t strn = state->rbytes - 4; - state->error = (tftp_error_t)error; - if(tftp_strnlen(str, strn) < strn) - infof(data, "TFTP error: %s", str); - break; - } - case TFTP_EVENT_ACK: - break; - case TFTP_EVENT_OACK: - result = tftp_parse_option_ack(state, - (const char *)state->rpacket.data + 2, - state->rbytes-2); - if(result) - return result; - break; - case TFTP_EVENT_RRQ: - case TFTP_EVENT_WRQ: - default: - failf(data, "%s", "Internal error: Unexpected packet"); - break; - } - - /* Update the progress meter */ - if(Curl_pgrsUpdate(data)) { - tftp_state_machine(state, TFTP_EVENT_ERROR); - return CURLE_ABORTED_BY_CALLBACK; - } - } - return result; -} - -/********************************************************** - * - * tftp_state_timeout - * - * Check if timeouts have been reached - * - **********************************************************/ -static timediff_t tftp_state_timeout(struct Curl_easy *data, - tftp_event_t *event) -{ - time_t current; - struct connectdata *conn = data->conn; - struct tftp_state_data *state = conn->proto.tftpc; - timediff_t timeout_ms; - - if(event) - *event = TFTP_EVENT_NONE; - - timeout_ms = Curl_timeleft(state->data, NULL, - (state->state == TFTP_STATE_START)); - if(timeout_ms < 0) { - state->error = TFTP_ERR_TIMEOUT; - state->state = TFTP_STATE_FIN; - return 0; - } - time(¤t); - if(current > state->rx_time + state->retry_time) { - if(event) - *event = TFTP_EVENT_TIMEOUT; - time(&state->rx_time); /* update even though we received nothing */ - } - - return timeout_ms; -} - -/********************************************************** - * - * tftp_multi_statemach - * - * Handle single RX socket event and return - * - **********************************************************/ -static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done) -{ - tftp_event_t event; - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct tftp_state_data *state = conn->proto.tftpc; - timediff_t timeout_ms = tftp_state_timeout(data, &event); - - *done = FALSE; - - if(timeout_ms < 0) { - failf(data, "TFTP response timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - if(event != TFTP_EVENT_NONE) { - result = tftp_state_machine(state, event); - if(result) - return result; - *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE; - if(*done) - /* Tell curl we're done */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); - } - else { - /* no timeouts to handle, check our socket */ - int rc = SOCKET_READABLE(state->sockfd, 0); - - if(rc == -1) { - /* bail out */ - int error = SOCKERRNO; - char buffer[STRERROR_LEN]; - failf(data, "%s", Curl_strerror(error, buffer, sizeof(buffer))); - state->event = TFTP_EVENT_ERROR; - } - else if(rc) { - result = tftp_receive_packet(data); - if(result) - return result; - result = tftp_state_machine(state, state->event); - if(result) - return result; - *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE; - if(*done) - /* Tell curl we're done */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); - } - /* if rc == 0, then select() timed out */ - } - - return result; -} - -/********************************************************** - * - * tftp_doing - * - * Called from multi.c while DOing - * - **********************************************************/ -static CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done) -{ - CURLcode result; - result = tftp_multi_statemach(data, dophase_done); - - if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); - } - else if(!result) { - /* The multi code doesn't have this logic for the DOING state so we - provide it for TFTP since it may do the entire transfer in this - state. */ - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - else - result = Curl_speedcheck(data, Curl_now()); - } - return result; -} - -/********************************************************** - * - * tftp_perform - * - * Entry point for transfer from tftp_do, starts state mach - * - **********************************************************/ -static CURLcode tftp_perform(struct Curl_easy *data, bool *dophase_done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct tftp_state_data *state = conn->proto.tftpc; - - *dophase_done = FALSE; - - result = tftp_state_machine(state, TFTP_EVENT_INIT); - - if((state->state == TFTP_STATE_FIN) || result) - return result; - - tftp_multi_statemach(data, dophase_done); - - if(*dophase_done) - DEBUGF(infof(data, "DO phase is complete")); - - return result; -} - - -/********************************************************** - * - * tftp_do - * - * The do callback - * - * This callback initiates the TFTP transfer - * - **********************************************************/ - -static CURLcode tftp_do(struct Curl_easy *data, bool *done) -{ - struct tftp_state_data *state; - CURLcode result; - struct connectdata *conn = data->conn; - - *done = FALSE; - - if(!conn->proto.tftpc) { - result = tftp_connect(data, done); - if(result) - return result; - } - - state = conn->proto.tftpc; - if(!state) - return CURLE_TFTP_ILLEGAL; - - result = tftp_perform(data, done); - - /* If tftp_perform() returned an error, use that for return code. If it - was OK, see if tftp_translate_code() has an error. */ - if(!result) - /* If we have encountered an internal tftp error, translate it. */ - result = tftp_translate_code(state->error); - - return result; -} - -static CURLcode tftp_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - char *type; - - conn->transport = TRNSPRT_UDP; - - /* TFTP URLs support an extension like ";mode=<typecode>" that - * we'll try to get now! */ - type = strstr(data->state.up.path, ";mode="); - - if(!type) - type = strstr(conn->host.rawalloc, ";mode="); - - if(type) { - char command; - *type = 0; /* it was in the middle of the hostname */ - command = Curl_raw_toupper(type[6]); - - switch(command) { - case 'A': /* ASCII mode */ - case 'N': /* NETASCII mode */ - data->state.prefer_ascii = TRUE; - break; - - case 'O': /* octet mode */ - case 'I': /* binary mode */ - default: - /* switch off ASCII */ - data->state.prefer_ascii = FALSE; - break; - } - } - - return CURLE_OK; -} -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_TFTP
+
+#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 HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "transfer.h"
+#include "sendf.h"
+#include "tftp.h"
+#include "progress.h"
+#include "connect.h"
+#include "strerror.h"
+#include "sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "multiif.h"
+#include "url.h"
+#include "strcase.h"
+#include "speedcheck.h"
+#include "select.h"
+#include "escape.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* RFC2348 allows the block size to be negotiated */
+#define TFTP_BLKSIZE_DEFAULT 512
+#define TFTP_BLKSIZE_MIN 8
+#define TFTP_BLKSIZE_MAX 65464
+#define TFTP_OPTION_BLKSIZE "blksize"
+
+/* from RFC2349: */
+#define TFTP_OPTION_TSIZE "tsize"
+#define TFTP_OPTION_INTERVAL "timeout"
+
+typedef enum {
+ TFTP_MODE_NETASCII = 0,
+ TFTP_MODE_OCTET
+} tftp_mode_t;
+
+typedef enum {
+ TFTP_STATE_START = 0,
+ TFTP_STATE_RX,
+ TFTP_STATE_TX,
+ TFTP_STATE_FIN
+} tftp_state_t;
+
+typedef enum {
+ TFTP_EVENT_NONE = -1,
+ TFTP_EVENT_INIT = 0,
+ TFTP_EVENT_RRQ = 1,
+ TFTP_EVENT_WRQ = 2,
+ TFTP_EVENT_DATA = 3,
+ TFTP_EVENT_ACK = 4,
+ TFTP_EVENT_ERROR = 5,
+ TFTP_EVENT_OACK = 6,
+ TFTP_EVENT_TIMEOUT
+} tftp_event_t;
+
+typedef enum {
+ TFTP_ERR_UNDEF = 0,
+ TFTP_ERR_NOTFOUND,
+ TFTP_ERR_PERM,
+ TFTP_ERR_DISKFULL,
+ TFTP_ERR_ILLEGAL,
+ TFTP_ERR_UNKNOWNID,
+ TFTP_ERR_EXISTS,
+ TFTP_ERR_NOSUCHUSER, /* This will never be triggered by this code */
+
+ /* The remaining error codes are internal to curl */
+ TFTP_ERR_NONE = -100,
+ TFTP_ERR_TIMEOUT,
+ TFTP_ERR_NORESPONSE
+} tftp_error_t;
+
+struct tftp_packet {
+ unsigned char *data;
+};
+
+struct tftp_state_data {
+ tftp_state_t state;
+ tftp_mode_t mode;
+ tftp_error_t error;
+ tftp_event_t event;
+ struct Curl_easy *data;
+ curl_socket_t sockfd;
+ int retries;
+ int retry_time;
+ int retry_max;
+ time_t rx_time;
+ struct Curl_sockaddr_storage local_addr;
+ struct Curl_sockaddr_storage remote_addr;
+ curl_socklen_t remote_addrlen;
+ int rbytes;
+ int sbytes;
+ int blksize;
+ int requested_blksize;
+ unsigned short block;
+ struct tftp_packet rpacket;
+ struct tftp_packet spacket;
+};
+
+
+/* Forward declarations */
+static CURLcode tftp_rx(struct tftp_state_data *state, tftp_event_t event);
+static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event);
+static CURLcode tftp_connect(struct Curl_easy *data, bool *done);
+static CURLcode tftp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection);
+static CURLcode tftp_do(struct Curl_easy *data, bool *done);
+static CURLcode tftp_done(struct Curl_easy *data,
+ CURLcode, bool premature);
+static CURLcode tftp_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn);
+static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done);
+static CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done);
+static int tftp_getsock(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t *socks);
+static CURLcode tftp_translate_code(tftp_error_t error);
+
+
+/*
+ * TFTP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_tftp = {
+ "TFTP", /* scheme */
+ tftp_setup_connection, /* setup_connection */
+ tftp_do, /* do_it */
+ tftp_done, /* done */
+ ZERO_NULL, /* do_more */
+ tftp_connect, /* connect_it */
+ tftp_multi_statemach, /* connecting */
+ tftp_doing, /* doing */
+ tftp_getsock, /* proto_getsock */
+ tftp_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ tftp_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_TFTP, /* defport */
+ CURLPROTO_TFTP, /* protocol */
+ CURLPROTO_TFTP, /* family */
+ PROTOPT_NOTCPPROXY | PROTOPT_NOURLQUERY /* flags */
+};
+
+/**********************************************************
+ *
+ * tftp_set_timeouts -
+ *
+ * Set timeouts based on state machine state.
+ * Use user provided connect timeouts until DATA or ACK
+ * packet is received, then use user-provided transfer timeouts
+ *
+ *
+ **********************************************************/
+static CURLcode tftp_set_timeouts(struct tftp_state_data *state)
+{
+ time_t maxtime, timeout;
+ timediff_t timeout_ms;
+ bool start = (state->state == TFTP_STATE_START) ? TRUE : FALSE;
+
+ /* Compute drop-dead time */
+ timeout_ms = Curl_timeleft(state->data, NULL, start);
+
+ if(timeout_ms < 0) {
+ /* time-out, bail out, go home */
+ failf(state->data, "Connection time-out");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
+ if(timeout_ms > 0)
+ maxtime = (time_t)(timeout_ms + 500) / 1000;
+ else
+ maxtime = 3600; /* use for calculating block timeouts */
+
+ /* Set per-block timeout to total */
+ timeout = maxtime;
+
+ /* Average reposting an ACK after 5 seconds */
+ state->retry_max = (int)timeout/5;
+
+ /* But bound the total number */
+ if(state->retry_max<3)
+ state->retry_max = 3;
+
+ if(state->retry_max>50)
+ state->retry_max = 50;
+
+ /* Compute the re-ACK interval to suit the timeout */
+ state->retry_time = (int)(timeout/state->retry_max);
+ if(state->retry_time<1)
+ state->retry_time = 1;
+
+ infof(state->data,
+ "set timeouts for state %d; Total % " CURL_FORMAT_CURL_OFF_T
+ ", retry %d maxtry %d",
+ (int)state->state, timeout_ms, state->retry_time, state->retry_max);
+
+ /* init RX time */
+ time(&state->rx_time);
+
+ return CURLE_OK;
+}
+
+/**********************************************************
+ *
+ * tftp_set_send_first
+ *
+ * Event handler for the START state
+ *
+ **********************************************************/
+
+static void setpacketevent(struct tftp_packet *packet, unsigned short num)
+{
+ packet->data[0] = (unsigned char)(num >> 8);
+ packet->data[1] = (unsigned char)(num & 0xff);
+}
+
+
+static void setpacketblock(struct tftp_packet *packet, unsigned short num)
+{
+ packet->data[2] = (unsigned char)(num >> 8);
+ packet->data[3] = (unsigned char)(num & 0xff);
+}
+
+static unsigned short getrpacketevent(const struct tftp_packet *packet)
+{
+ return (unsigned short)((packet->data[0] << 8) | packet->data[1]);
+}
+
+static unsigned short getrpacketblock(const struct tftp_packet *packet)
+{
+ return (unsigned short)((packet->data[2] << 8) | packet->data[3]);
+}
+
+static size_t tftp_strnlen(const char *string, size_t maxlen)
+{
+ const char *end = memchr(string, '\0', maxlen);
+ return end ? (size_t) (end - string) : maxlen;
+}
+
+static const char *tftp_option_get(const char *buf, size_t len,
+ const char **option, const char **value)
+{
+ size_t loc;
+
+ loc = tftp_strnlen(buf, len);
+ loc++; /* NULL term */
+
+ if(loc >= len)
+ return NULL;
+ *option = buf;
+
+ loc += tftp_strnlen(buf + loc, len-loc);
+ loc++; /* NULL term */
+
+ if(loc > len)
+ return NULL;
+ *value = &buf[strlen(*option) + 1];
+
+ return &buf[loc];
+}
+
+static CURLcode tftp_parse_option_ack(struct tftp_state_data *state,
+ const char *ptr, int len)
+{
+ const char *tmp = ptr;
+ struct Curl_easy *data = state->data;
+
+ /* if OACK doesn't contain blksize option, the default (512) must be used */
+ state->blksize = TFTP_BLKSIZE_DEFAULT;
+
+ while(tmp < ptr + len) {
+ const char *option, *value;
+
+ tmp = tftp_option_get(tmp, ptr + len - tmp, &option, &value);
+ if(!tmp) {
+ failf(data, "Malformed ACK packet, rejecting");
+ return CURLE_TFTP_ILLEGAL;
+ }
+
+ infof(data, "got option=(%s) value=(%s)", option, value);
+
+ if(checkprefix(TFTP_OPTION_BLKSIZE, option)) {
+ long blksize;
+
+ blksize = strtol(value, NULL, 10);
+
+ if(!blksize) {
+ failf(data, "invalid blocksize value in OACK packet");
+ return CURLE_TFTP_ILLEGAL;
+ }
+ if(blksize > TFTP_BLKSIZE_MAX) {
+ failf(data, "%s (%d)", "blksize is larger than max supported",
+ TFTP_BLKSIZE_MAX);
+ return CURLE_TFTP_ILLEGAL;
+ }
+ else if(blksize < TFTP_BLKSIZE_MIN) {
+ failf(data, "%s (%d)", "blksize is smaller than min supported",
+ TFTP_BLKSIZE_MIN);
+ return CURLE_TFTP_ILLEGAL;
+ }
+ else if(blksize > state->requested_blksize) {
+ /* could realloc pkt buffers here, but the spec doesn't call out
+ * support for the server requesting a bigger blksize than the client
+ * requests */
+ failf(data, "%s (%ld)",
+ "server requested blksize larger than allocated", blksize);
+ return CURLE_TFTP_ILLEGAL;
+ }
+
+ state->blksize = (int)blksize;
+ infof(data, "%s (%d) %s (%d)", "blksize parsed from OACK",
+ state->blksize, "requested", state->requested_blksize);
+ }
+ else if(checkprefix(TFTP_OPTION_TSIZE, option)) {
+ long tsize = 0;
+
+ tsize = strtol(value, NULL, 10);
+ infof(data, "%s (%ld)", "tsize parsed from OACK", tsize);
+
+ /* tsize should be ignored on upload: Who cares about the size of the
+ remote file? */
+ if(!data->set.upload) {
+ if(!tsize) {
+ failf(data, "invalid tsize -:%s:- value in OACK packet", value);
+ return CURLE_TFTP_ILLEGAL;
+ }
+ Curl_pgrsSetDownloadSize(data, tsize);
+ }
+ }
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode tftp_option_add(struct tftp_state_data *state, size_t *csize,
+ char *buf, const char *option)
+{
+ if(( strlen(option) + *csize + 1) > (size_t)state->blksize)
+ return CURLE_TFTP_ILLEGAL;
+ strcpy(buf, option);
+ *csize += strlen(option) + 1;
+ return CURLE_OK;
+}
+
+static CURLcode tftp_connect_for_tx(struct tftp_state_data *state,
+ tftp_event_t event)
+{
+ CURLcode result;
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ struct Curl_easy *data = state->data;
+
+ infof(data, "%s", "Connected for transmit");
+#endif
+ state->state = TFTP_STATE_TX;
+ result = tftp_set_timeouts(state);
+ if(result)
+ return result;
+ return tftp_tx(state, event);
+}
+
+static CURLcode tftp_connect_for_rx(struct tftp_state_data *state,
+ tftp_event_t event)
+{
+ CURLcode result;
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ struct Curl_easy *data = state->data;
+
+ infof(data, "%s", "Connected for receive");
+#endif
+ state->state = TFTP_STATE_RX;
+ result = tftp_set_timeouts(state);
+ if(result)
+ return result;
+ return tftp_rx(state, event);
+}
+
+static CURLcode tftp_send_first(struct tftp_state_data *state,
+ tftp_event_t event)
+{
+ size_t sbytes;
+ ssize_t senddata;
+ const char *mode = "octet";
+ char *filename;
+ struct Curl_easy *data = state->data;
+ CURLcode result = CURLE_OK;
+
+ /* Set ascii mode if -B flag was used */
+ if(data->state.prefer_ascii)
+ mode = "netascii";
+
+ switch(event) {
+
+ case TFTP_EVENT_INIT: /* Send the first packet out */
+ case TFTP_EVENT_TIMEOUT: /* Resend the first packet out */
+ /* Increment the retry counter, quit if over the limit */
+ state->retries++;
+ if(state->retries>state->retry_max) {
+ state->error = TFTP_ERR_NORESPONSE;
+ state->state = TFTP_STATE_FIN;
+ return result;
+ }
+
+ if(data->set.upload) {
+ /* If we are uploading, send an WRQ */
+ setpacketevent(&state->spacket, TFTP_EVENT_WRQ);
+ state->data->req.upload_fromhere =
+ (char *)state->spacket.data + 4;
+ if(data->state.infilesize != -1)
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+ }
+ else {
+ /* If we are downloading, send an RRQ */
+ setpacketevent(&state->spacket, TFTP_EVENT_RRQ);
+ }
+ /* As RFC3617 describes the separator slash is not actually part of the
+ file name so we skip the always-present first letter of the path
+ string. */
+ result = Curl_urldecode(&state->data->state.up.path[1], 0,
+ &filename, NULL, REJECT_ZERO);
+ if(result)
+ return result;
+
+ if(strlen(filename) > (state->blksize - strlen(mode) - 4)) {
+ failf(data, "TFTP file name too long");
+ free(filename);
+ return CURLE_TFTP_ILLEGAL; /* too long file name field */
+ }
+
+ msnprintf((char *)state->spacket.data + 2,
+ state->blksize,
+ "%s%c%s%c", filename, '\0', mode, '\0');
+ sbytes = 4 + strlen(filename) + strlen(mode);
+
+ /* optional addition of TFTP options */
+ if(!data->set.tftp_no_options) {
+ char buf[64];
+ /* add tsize option */
+ if(data->set.upload && (data->state.infilesize != -1))
+ msnprintf(buf, sizeof(buf), "%" CURL_FORMAT_CURL_OFF_T,
+ data->state.infilesize);
+ else
+ strcpy(buf, "0"); /* the destination is large enough */
+
+ result = tftp_option_add(state, &sbytes,
+ (char *)state->spacket.data + sbytes,
+ TFTP_OPTION_TSIZE);
+ if(result == CURLE_OK)
+ result = tftp_option_add(state, &sbytes,
+ (char *)state->spacket.data + sbytes, buf);
+
+ /* add blksize option */
+ msnprintf(buf, sizeof(buf), "%d", state->requested_blksize);
+ if(result == CURLE_OK)
+ result = tftp_option_add(state, &sbytes,
+ (char *)state->spacket.data + sbytes,
+ TFTP_OPTION_BLKSIZE);
+ if(result == CURLE_OK)
+ result = tftp_option_add(state, &sbytes,
+ (char *)state->spacket.data + sbytes, buf);
+
+ /* add timeout option */
+ msnprintf(buf, sizeof(buf), "%d", state->retry_time);
+ if(result == CURLE_OK)
+ result = tftp_option_add(state, &sbytes,
+ (char *)state->spacket.data + sbytes,
+ TFTP_OPTION_INTERVAL);
+ if(result == CURLE_OK)
+ result = tftp_option_add(state, &sbytes,
+ (char *)state->spacket.data + sbytes, buf);
+
+ if(result != CURLE_OK) {
+ failf(data, "TFTP buffer too small for options");
+ free(filename);
+ return CURLE_TFTP_ILLEGAL;
+ }
+ }
+
+ /* the typecase for the 3rd argument is mostly for systems that do
+ not have a size_t argument, like older unixes that want an 'int' */
+ senddata = sendto(state->sockfd, (void *)state->spacket.data,
+ (SEND_TYPE_ARG3)sbytes, 0,
+ data->conn->ip_addr->ai_addr,
+ data->conn->ip_addr->ai_addrlen);
+ if(senddata != (ssize_t)sbytes) {
+ char buffer[STRERROR_LEN];
+ failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+ }
+ free(filename);
+ break;
+
+ case TFTP_EVENT_OACK:
+ if(data->set.upload) {
+ result = tftp_connect_for_tx(state, event);
+ }
+ else {
+ result = tftp_connect_for_rx(state, event);
+ }
+ break;
+
+ case TFTP_EVENT_ACK: /* Connected for transmit */
+ result = tftp_connect_for_tx(state, event);
+ break;
+
+ case TFTP_EVENT_DATA: /* Connected for receive */
+ result = tftp_connect_for_rx(state, event);
+ break;
+
+ case TFTP_EVENT_ERROR:
+ state->state = TFTP_STATE_FIN;
+ break;
+
+ default:
+ failf(state->data, "tftp_send_first: internal error");
+ break;
+ }
+
+ return result;
+}
+
+/* the next blocknum is x + 1 but it needs to wrap at an unsigned 16bit
+ boundary */
+#define NEXT_BLOCKNUM(x) (((x) + 1)&0xffff)
+
+/**********************************************************
+ *
+ * tftp_rx
+ *
+ * Event handler for the RX state
+ *
+ **********************************************************/
+static CURLcode tftp_rx(struct tftp_state_data *state,
+ tftp_event_t event)
+{
+ ssize_t sbytes;
+ int rblock;
+ struct Curl_easy *data = state->data;
+ char buffer[STRERROR_LEN];
+
+ switch(event) {
+
+ case TFTP_EVENT_DATA:
+ /* Is this the block we expect? */
+ rblock = getrpacketblock(&state->rpacket);
+ if(NEXT_BLOCKNUM(state->block) == rblock) {
+ /* This is the expected block. Reset counters and ACK it. */
+ state->retries = 0;
+ }
+ else if(state->block == rblock) {
+ /* This is the last recently received block again. Log it and ACK it
+ again. */
+ infof(data, "Received last DATA packet block %d again.", rblock);
+ }
+ else {
+ /* totally unexpected, just log it */
+ infof(data,
+ "Received unexpected DATA packet block %d, expecting block %d",
+ rblock, NEXT_BLOCKNUM(state->block));
+ break;
+ }
+
+ /* ACK this block. */
+ state->block = (unsigned short)rblock;
+ setpacketevent(&state->spacket, TFTP_EVENT_ACK);
+ setpacketblock(&state->spacket, state->block);
+ sbytes = sendto(state->sockfd, (void *)state->spacket.data,
+ 4, SEND_4TH_ARG,
+ (struct sockaddr *)&state->remote_addr,
+ state->remote_addrlen);
+ if(sbytes < 0) {
+ failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+ return CURLE_SEND_ERROR;
+ }
+
+ /* Check if completed (That is, a less than full packet is received) */
+ if(state->rbytes < (ssize_t)state->blksize + 4) {
+ state->state = TFTP_STATE_FIN;
+ }
+ else {
+ state->state = TFTP_STATE_RX;
+ }
+ time(&state->rx_time);
+ break;
+
+ case TFTP_EVENT_OACK:
+ /* ACK option acknowledgement so we can move on to data */
+ state->block = 0;
+ state->retries = 0;
+ setpacketevent(&state->spacket, TFTP_EVENT_ACK);
+ setpacketblock(&state->spacket, state->block);
+ sbytes = sendto(state->sockfd, (void *)state->spacket.data,
+ 4, SEND_4TH_ARG,
+ (struct sockaddr *)&state->remote_addr,
+ state->remote_addrlen);
+ if(sbytes < 0) {
+ failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+ return CURLE_SEND_ERROR;
+ }
+
+ /* we're ready to RX data */
+ state->state = TFTP_STATE_RX;
+ time(&state->rx_time);
+ break;
+
+ case TFTP_EVENT_TIMEOUT:
+ /* Increment the retry count and fail if over the limit */
+ state->retries++;
+ infof(data,
+ "Timeout waiting for block %d ACK. Retries = %d",
+ NEXT_BLOCKNUM(state->block), state->retries);
+ if(state->retries > state->retry_max) {
+ state->error = TFTP_ERR_TIMEOUT;
+ state->state = TFTP_STATE_FIN;
+ }
+ else {
+ /* Resend the previous ACK */
+ sbytes = sendto(state->sockfd, (void *)state->spacket.data,
+ 4, SEND_4TH_ARG,
+ (struct sockaddr *)&state->remote_addr,
+ state->remote_addrlen);
+ if(sbytes<0) {
+ failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+ return CURLE_SEND_ERROR;
+ }
+ }
+ break;
+
+ case TFTP_EVENT_ERROR:
+ setpacketevent(&state->spacket, TFTP_EVENT_ERROR);
+ setpacketblock(&state->spacket, state->block);
+ (void)sendto(state->sockfd, (void *)state->spacket.data,
+ 4, SEND_4TH_ARG,
+ (struct sockaddr *)&state->remote_addr,
+ state->remote_addrlen);
+ /* don't bother with the return code, but if the socket is still up we
+ * should be a good TFTP client and let the server know we're done */
+ state->state = TFTP_STATE_FIN;
+ break;
+
+ default:
+ failf(data, "%s", "tftp_rx: internal error");
+ return CURLE_TFTP_ILLEGAL; /* not really the perfect return code for
+ this */
+ }
+ return CURLE_OK;
+}
+
+/**********************************************************
+ *
+ * tftp_tx
+ *
+ * Event handler for the TX state
+ *
+ **********************************************************/
+static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event)
+{
+ struct Curl_easy *data = state->data;
+ ssize_t sbytes;
+ CURLcode result = CURLE_OK;
+ struct SingleRequest *k = &data->req;
+ size_t cb; /* Bytes currently read */
+ char buffer[STRERROR_LEN];
+
+ switch(event) {
+
+ case TFTP_EVENT_ACK:
+ case TFTP_EVENT_OACK:
+ if(event == TFTP_EVENT_ACK) {
+ /* Ack the packet */
+ int rblock = getrpacketblock(&state->rpacket);
+
+ if(rblock != state->block &&
+ /* There's a bug in tftpd-hpa that causes it to send us an ack for
+ * 65535 when the block number wraps to 0. So when we're expecting
+ * 0, also accept 65535. See
+ * https://www.syslinux.org/archives/2010-September/015612.html
+ * */
+ !(state->block == 0 && rblock == 65535)) {
+ /* This isn't the expected block. Log it and up the retry counter */
+ infof(data, "Received ACK for block %d, expecting %d",
+ rblock, state->block);
+ state->retries++;
+ /* Bail out if over the maximum */
+ if(state->retries>state->retry_max) {
+ failf(data, "tftp_tx: giving up waiting for block %d ack",
+ state->block);
+ result = CURLE_SEND_ERROR;
+ }
+ else {
+ /* Re-send the data packet */
+ sbytes = sendto(state->sockfd, (void *)state->spacket.data,
+ 4 + state->sbytes, SEND_4TH_ARG,
+ (struct sockaddr *)&state->remote_addr,
+ state->remote_addrlen);
+ /* Check all sbytes were sent */
+ if(sbytes<0) {
+ failf(data, "%s", Curl_strerror(SOCKERRNO,
+ buffer, sizeof(buffer)));
+ result = CURLE_SEND_ERROR;
+ }
+ }
+
+ return result;
+ }
+ /* This is the expected packet. Reset the counters and send the next
+ block */
+ time(&state->rx_time);
+ state->block++;
+ }
+ else
+ state->block = 1; /* first data block is 1 when using OACK */
+
+ state->retries = 0;
+ setpacketevent(&state->spacket, TFTP_EVENT_DATA);
+ setpacketblock(&state->spacket, state->block);
+ if(state->block > 1 && state->sbytes < state->blksize) {
+ state->state = TFTP_STATE_FIN;
+ return CURLE_OK;
+ }
+
+ /* TFTP considers data block size < 512 bytes as an end of session. So
+ * in some cases we must wait for additional data to build full (512 bytes)
+ * data block.
+ * */
+ state->sbytes = 0;
+ state->data->req.upload_fromhere = (char *)state->spacket.data + 4;
+ do {
+ result = Curl_fillreadbuffer(data, state->blksize - state->sbytes, &cb);
+ if(result)
+ return result;
+ state->sbytes += (int)cb;
+ state->data->req.upload_fromhere += cb;
+ } while(state->sbytes < state->blksize && cb);
+
+ sbytes = sendto(state->sockfd, (void *) state->spacket.data,
+ 4 + state->sbytes, SEND_4TH_ARG,
+ (struct sockaddr *)&state->remote_addr,
+ state->remote_addrlen);
+ /* Check all sbytes were sent */
+ if(sbytes<0) {
+ failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+ return CURLE_SEND_ERROR;
+ }
+ /* Update the progress meter */
+ k->writebytecount += state->sbytes;
+ Curl_pgrsSetUploadCounter(data, k->writebytecount);
+ break;
+
+ case TFTP_EVENT_TIMEOUT:
+ /* Increment the retry counter and log the timeout */
+ state->retries++;
+ infof(data, "Timeout waiting for block %d ACK. "
+ " Retries = %d", NEXT_BLOCKNUM(state->block), state->retries);
+ /* Decide if we've had enough */
+ if(state->retries > state->retry_max) {
+ state->error = TFTP_ERR_TIMEOUT;
+ state->state = TFTP_STATE_FIN;
+ }
+ else {
+ /* Re-send the data packet */
+ sbytes = sendto(state->sockfd, (void *)state->spacket.data,
+ 4 + state->sbytes, SEND_4TH_ARG,
+ (struct sockaddr *)&state->remote_addr,
+ state->remote_addrlen);
+ /* Check all sbytes were sent */
+ if(sbytes<0) {
+ failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+ return CURLE_SEND_ERROR;
+ }
+ /* since this was a re-send, we remain at the still byte position */
+ Curl_pgrsSetUploadCounter(data, k->writebytecount);
+ }
+ break;
+
+ case TFTP_EVENT_ERROR:
+ state->state = TFTP_STATE_FIN;
+ setpacketevent(&state->spacket, TFTP_EVENT_ERROR);
+ setpacketblock(&state->spacket, state->block);
+ (void)sendto(state->sockfd, (void *)state->spacket.data, 4, SEND_4TH_ARG,
+ (struct sockaddr *)&state->remote_addr,
+ state->remote_addrlen);
+ /* don't bother with the return code, but if the socket is still up we
+ * should be a good TFTP client and let the server know we're done */
+ state->state = TFTP_STATE_FIN;
+ break;
+
+ default:
+ failf(data, "tftp_tx: internal error, event: %i", (int)(event));
+ break;
+ }
+
+ return result;
+}
+
+/**********************************************************
+ *
+ * tftp_translate_code
+ *
+ * Translate internal error codes to CURL error codes
+ *
+ **********************************************************/
+static CURLcode tftp_translate_code(tftp_error_t error)
+{
+ CURLcode result = CURLE_OK;
+
+ if(error != TFTP_ERR_NONE) {
+ switch(error) {
+ case TFTP_ERR_NOTFOUND:
+ result = CURLE_TFTP_NOTFOUND;
+ break;
+ case TFTP_ERR_PERM:
+ result = CURLE_TFTP_PERM;
+ break;
+ case TFTP_ERR_DISKFULL:
+ result = CURLE_REMOTE_DISK_FULL;
+ break;
+ case TFTP_ERR_UNDEF:
+ case TFTP_ERR_ILLEGAL:
+ result = CURLE_TFTP_ILLEGAL;
+ break;
+ case TFTP_ERR_UNKNOWNID:
+ result = CURLE_TFTP_UNKNOWNID;
+ break;
+ case TFTP_ERR_EXISTS:
+ result = CURLE_REMOTE_FILE_EXISTS;
+ break;
+ case TFTP_ERR_NOSUCHUSER:
+ result = CURLE_TFTP_NOSUCHUSER;
+ break;
+ case TFTP_ERR_TIMEOUT:
+ result = CURLE_OPERATION_TIMEDOUT;
+ break;
+ case TFTP_ERR_NORESPONSE:
+ result = CURLE_COULDNT_CONNECT;
+ break;
+ default:
+ result = CURLE_ABORTED_BY_CALLBACK;
+ break;
+ }
+ }
+ else
+ result = CURLE_OK;
+
+ return result;
+}
+
+/**********************************************************
+ *
+ * tftp_state_machine
+ *
+ * The tftp state machine event dispatcher
+ *
+ **********************************************************/
+static CURLcode tftp_state_machine(struct tftp_state_data *state,
+ tftp_event_t event)
+{
+ CURLcode result = CURLE_OK;
+ struct Curl_easy *data = state->data;
+
+ switch(state->state) {
+ case TFTP_STATE_START:
+ DEBUGF(infof(data, "TFTP_STATE_START"));
+ result = tftp_send_first(state, event);
+ break;
+ case TFTP_STATE_RX:
+ DEBUGF(infof(data, "TFTP_STATE_RX"));
+ result = tftp_rx(state, event);
+ break;
+ case TFTP_STATE_TX:
+ DEBUGF(infof(data, "TFTP_STATE_TX"));
+ result = tftp_tx(state, event);
+ break;
+ case TFTP_STATE_FIN:
+ infof(data, "%s", "TFTP finished");
+ break;
+ default:
+ DEBUGF(infof(data, "STATE: %d", state->state));
+ failf(data, "%s", "Internal state machine error");
+ result = CURLE_TFTP_ILLEGAL;
+ break;
+ }
+
+ return result;
+}
+
+/**********************************************************
+ *
+ * tftp_disconnect
+ *
+ * The disconnect callback
+ *
+ **********************************************************/
+static CURLcode tftp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn, bool dead_connection)
+{
+ struct tftp_state_data *state = conn->proto.tftpc;
+ (void) data;
+ (void) dead_connection;
+
+ /* done, free dynamically allocated pkt buffers */
+ if(state) {
+ Curl_safefree(state->rpacket.data);
+ Curl_safefree(state->spacket.data);
+ free(state);
+ }
+
+ return CURLE_OK;
+}
+
+/**********************************************************
+ *
+ * tftp_connect
+ *
+ * The connect callback
+ *
+ **********************************************************/
+static CURLcode tftp_connect(struct Curl_easy *data, bool *done)
+{
+ struct tftp_state_data *state;
+ int blksize;
+ int need_blksize;
+ struct connectdata *conn = data->conn;
+
+ blksize = TFTP_BLKSIZE_DEFAULT;
+
+ state = conn->proto.tftpc = calloc(1, sizeof(struct tftp_state_data));
+ if(!state)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* alloc pkt buffers based on specified blksize */
+ if(data->set.tftp_blksize) {
+ blksize = (int)data->set.tftp_blksize;
+ if(blksize > TFTP_BLKSIZE_MAX || blksize < TFTP_BLKSIZE_MIN)
+ return CURLE_TFTP_ILLEGAL;
+ }
+
+ need_blksize = blksize;
+ /* default size is the fallback when no OACK is received */
+ if(need_blksize < TFTP_BLKSIZE_DEFAULT)
+ need_blksize = TFTP_BLKSIZE_DEFAULT;
+
+ if(!state->rpacket.data) {
+ state->rpacket.data = calloc(1, need_blksize + 2 + 2);
+
+ if(!state->rpacket.data)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(!state->spacket.data) {
+ state->spacket.data = calloc(1, need_blksize + 2 + 2);
+
+ if(!state->spacket.data)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* we don't keep TFTP connections up basically because there's none or very
+ * little gain for UDP */
+ connclose(conn, "TFTP");
+
+ state->data = data;
+ state->sockfd = conn->sock[FIRSTSOCKET];
+ state->state = TFTP_STATE_START;
+ state->error = TFTP_ERR_NONE;
+ state->blksize = TFTP_BLKSIZE_DEFAULT; /* Unless updated by OACK response */
+ state->requested_blksize = blksize;
+
+ ((struct sockaddr *)&state->local_addr)->sa_family =
+ (CURL_SA_FAMILY_T)(conn->ip_addr->ai_family);
+
+ tftp_set_timeouts(state);
+
+ if(!conn->bits.bound) {
+ /* If not already bound, bind to any interface, random UDP port. If it is
+ * reused or a custom local port was desired, this has already been done!
+ *
+ * We once used the size of the local_addr struct as the third argument
+ * for bind() to better work with IPv6 or whatever size the struct could
+ * have, but we learned that at least Tru64, AIX and IRIX *requires* the
+ * size of that argument to match the exact size of a 'sockaddr_in' struct
+ * when running IPv4-only.
+ *
+ * Therefore we use the size from the address we connected to, which we
+ * assume uses the same IP version and thus hopefully this works for both
+ * IPv4 and IPv6...
+ */
+ int rc = bind(state->sockfd, (struct sockaddr *)&state->local_addr,
+ conn->ip_addr->ai_addrlen);
+ if(rc) {
+ char buffer[STRERROR_LEN];
+ failf(data, "bind() failed; %s",
+ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+ return CURLE_COULDNT_CONNECT;
+ }
+ conn->bits.bound = TRUE;
+ }
+
+ Curl_pgrsStartNow(data);
+
+ *done = TRUE;
+
+ return CURLE_OK;
+}
+
+/**********************************************************
+ *
+ * tftp_done
+ *
+ * The done callback
+ *
+ **********************************************************/
+static CURLcode tftp_done(struct Curl_easy *data, CURLcode status,
+ bool premature)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct tftp_state_data *state = conn->proto.tftpc;
+
+ (void)status; /* unused */
+ (void)premature; /* not used */
+
+ if(Curl_pgrsDone(data))
+ return CURLE_ABORTED_BY_CALLBACK;
+
+ /* If we have encountered an error */
+ if(state)
+ result = tftp_translate_code(state->error);
+
+ return result;
+}
+
+/**********************************************************
+ *
+ * tftp_getsock
+ *
+ * The getsock callback
+ *
+ **********************************************************/
+static int tftp_getsock(struct Curl_easy *data,
+ struct connectdata *conn, curl_socket_t *socks)
+{
+ (void)data;
+ socks[0] = conn->sock[FIRSTSOCKET];
+ return GETSOCK_READSOCK(0);
+}
+
+/**********************************************************
+ *
+ * tftp_receive_packet
+ *
+ * Called once select fires and data is ready on the socket
+ *
+ **********************************************************/
+static CURLcode tftp_receive_packet(struct Curl_easy *data)
+{
+ struct Curl_sockaddr_storage fromaddr;
+ curl_socklen_t fromlen;
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct tftp_state_data *state = conn->proto.tftpc;
+ struct SingleRequest *k = &data->req;
+
+ /* Receive the packet */
+ fromlen = sizeof(fromaddr);
+ state->rbytes = (int)recvfrom(state->sockfd,
+ (void *)state->rpacket.data,
+ state->blksize + 4,
+ 0,
+ (struct sockaddr *)&fromaddr,
+ &fromlen);
+ if(state->remote_addrlen == 0) {
+ memcpy(&state->remote_addr, &fromaddr, fromlen);
+ state->remote_addrlen = fromlen;
+ }
+
+ /* Sanity check packet length */
+ if(state->rbytes < 4) {
+ failf(data, "Received too short packet");
+ /* Not a timeout, but how best to handle it? */
+ state->event = TFTP_EVENT_TIMEOUT;
+ }
+ else {
+ /* The event is given by the TFTP packet time */
+ unsigned short event = getrpacketevent(&state->rpacket);
+ state->event = (tftp_event_t)event;
+
+ switch(state->event) {
+ case TFTP_EVENT_DATA:
+ /* Don't pass to the client empty or retransmitted packets */
+ if(state->rbytes > 4 &&
+ (NEXT_BLOCKNUM(state->block) == getrpacketblock(&state->rpacket))) {
+ result = Curl_client_write(data, CLIENTWRITE_BODY,
+ (char *)state->rpacket.data + 4,
+ state->rbytes-4);
+ if(result) {
+ tftp_state_machine(state, TFTP_EVENT_ERROR);
+ return result;
+ }
+ k->bytecount += state->rbytes-4;
+ Curl_pgrsSetDownloadCounter(data, (curl_off_t) k->bytecount);
+ }
+ break;
+ case TFTP_EVENT_ERROR:
+ {
+ unsigned short error = getrpacketblock(&state->rpacket);
+ char *str = (char *)state->rpacket.data + 4;
+ size_t strn = state->rbytes - 4;
+ state->error = (tftp_error_t)error;
+ if(tftp_strnlen(str, strn) < strn)
+ infof(data, "TFTP error: %s", str);
+ break;
+ }
+ case TFTP_EVENT_ACK:
+ break;
+ case TFTP_EVENT_OACK:
+ result = tftp_parse_option_ack(state,
+ (const char *)state->rpacket.data + 2,
+ state->rbytes-2);
+ if(result)
+ return result;
+ break;
+ case TFTP_EVENT_RRQ:
+ case TFTP_EVENT_WRQ:
+ default:
+ failf(data, "%s", "Internal error: Unexpected packet");
+ break;
+ }
+
+ /* Update the progress meter */
+ if(Curl_pgrsUpdate(data)) {
+ tftp_state_machine(state, TFTP_EVENT_ERROR);
+ return CURLE_ABORTED_BY_CALLBACK;
+ }
+ }
+ return result;
+}
+
+/**********************************************************
+ *
+ * tftp_state_timeout
+ *
+ * Check if timeouts have been reached
+ *
+ **********************************************************/
+static timediff_t tftp_state_timeout(struct Curl_easy *data,
+ tftp_event_t *event)
+{
+ time_t current;
+ struct connectdata *conn = data->conn;
+ struct tftp_state_data *state = conn->proto.tftpc;
+ timediff_t timeout_ms;
+
+ if(event)
+ *event = TFTP_EVENT_NONE;
+
+ timeout_ms = Curl_timeleft(state->data, NULL,
+ (state->state == TFTP_STATE_START));
+ if(timeout_ms < 0) {
+ state->error = TFTP_ERR_TIMEOUT;
+ state->state = TFTP_STATE_FIN;
+ return 0;
+ }
+ time(¤t);
+ if(current > state->rx_time + state->retry_time) {
+ if(event)
+ *event = TFTP_EVENT_TIMEOUT;
+ time(&state->rx_time); /* update even though we received nothing */
+ }
+
+ return timeout_ms;
+}
+
+/**********************************************************
+ *
+ * tftp_multi_statemach
+ *
+ * Handle single RX socket event and return
+ *
+ **********************************************************/
+static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done)
+{
+ tftp_event_t event;
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct tftp_state_data *state = conn->proto.tftpc;
+ timediff_t timeout_ms = tftp_state_timeout(data, &event);
+
+ *done = FALSE;
+
+ if(timeout_ms < 0) {
+ failf(data, "TFTP response timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+ if(event != TFTP_EVENT_NONE) {
+ result = tftp_state_machine(state, event);
+ if(result)
+ return result;
+ *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE;
+ if(*done)
+ /* Tell curl we're done */
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
+ }
+ else {
+ /* no timeouts to handle, check our socket */
+ int rc = SOCKET_READABLE(state->sockfd, 0);
+
+ if(rc == -1) {
+ /* bail out */
+ int error = SOCKERRNO;
+ char buffer[STRERROR_LEN];
+ failf(data, "%s", Curl_strerror(error, buffer, sizeof(buffer)));
+ state->event = TFTP_EVENT_ERROR;
+ }
+ else if(rc) {
+ result = tftp_receive_packet(data);
+ if(result)
+ return result;
+ result = tftp_state_machine(state, state->event);
+ if(result)
+ return result;
+ *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE;
+ if(*done)
+ /* Tell curl we're done */
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
+ }
+ /* if rc == 0, then select() timed out */
+ }
+
+ return result;
+}
+
+/**********************************************************
+ *
+ * tftp_doing
+ *
+ * Called from multi.c while DOing
+ *
+ **********************************************************/
+static CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done)
+{
+ CURLcode result;
+ result = tftp_multi_statemach(data, dophase_done);
+
+ if(*dophase_done) {
+ DEBUGF(infof(data, "DO phase is complete"));
+ }
+ else if(!result) {
+ /* The multi code doesn't have this logic for the DOING state so we
+ provide it for TFTP since it may do the entire transfer in this
+ state. */
+ if(Curl_pgrsUpdate(data))
+ result = CURLE_ABORTED_BY_CALLBACK;
+ else
+ result = Curl_speedcheck(data, Curl_now());
+ }
+ return result;
+}
+
+/**********************************************************
+ *
+ * tftp_perform
+ *
+ * Entry point for transfer from tftp_do, starts state mach
+ *
+ **********************************************************/
+static CURLcode tftp_perform(struct Curl_easy *data, bool *dophase_done)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct tftp_state_data *state = conn->proto.tftpc;
+
+ *dophase_done = FALSE;
+
+ result = tftp_state_machine(state, TFTP_EVENT_INIT);
+
+ if((state->state == TFTP_STATE_FIN) || result)
+ return result;
+
+ tftp_multi_statemach(data, dophase_done);
+
+ if(*dophase_done)
+ DEBUGF(infof(data, "DO phase is complete"));
+
+ return result;
+}
+
+
+/**********************************************************
+ *
+ * tftp_do
+ *
+ * The do callback
+ *
+ * This callback initiates the TFTP transfer
+ *
+ **********************************************************/
+
+static CURLcode tftp_do(struct Curl_easy *data, bool *done)
+{
+ struct tftp_state_data *state;
+ CURLcode result;
+ struct connectdata *conn = data->conn;
+
+ *done = FALSE;
+
+ if(!conn->proto.tftpc) {
+ result = tftp_connect(data, done);
+ if(result)
+ return result;
+ }
+
+ state = conn->proto.tftpc;
+ if(!state)
+ return CURLE_TFTP_ILLEGAL;
+
+ result = tftp_perform(data, done);
+
+ /* If tftp_perform() returned an error, use that for return code. If it
+ was OK, see if tftp_translate_code() has an error. */
+ if(!result)
+ /* If we have encountered an internal tftp error, translate it. */
+ result = tftp_translate_code(state->error);
+
+ return result;
+}
+
+static CURLcode tftp_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ char *type;
+
+ conn->transport = TRNSPRT_UDP;
+
+ /* TFTP URLs support an extension like ";mode=<typecode>" that
+ * we'll try to get now! */
+ type = strstr(data->state.up.path, ";mode=");
+
+ if(!type)
+ type = strstr(conn->host.rawalloc, ";mode=");
+
+ if(type) {
+ char command;
+ *type = 0; /* it was in the middle of the hostname */
+ command = Curl_raw_toupper(type[6]);
+
+ switch(command) {
+ case 'A': /* ASCII mode */
+ case 'N': /* NETASCII mode */
+ data->state.prefer_ascii = TRUE;
+ break;
+
+ case 'O': /* octet mode */
+ case 'I': /* binary mode */
+ default:
+ /* switch off ASCII */
+ data->state.prefer_ascii = FALSE;
+ break;
+ }
+ }
+
+ return CURLE_OK;
+}
+#endif
diff --git a/libs/libcurl/src/tftp.h b/libs/libcurl/src/tftp.h index 3f1fda6382..ee5fe9bbb9 100644 --- a/libs/libcurl/src/tftp.h +++ b/libs/libcurl/src/tftp.h @@ -1,30 +1,30 @@ -#ifndef HEADER_CURL_TFTP_H -#define HEADER_CURL_TFTP_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_TFTP -extern const struct Curl_handler Curl_handler_tftp; -#endif - -#endif /* HEADER_CURL_TFTP_H */ +#ifndef HEADER_CURL_TFTP_H
+#define HEADER_CURL_TFTP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_TFTP
+extern const struct Curl_handler Curl_handler_tftp;
+#endif
+
+#endif /* HEADER_CURL_TFTP_H */
diff --git a/libs/libcurl/src/timediff.c b/libs/libcurl/src/timediff.c index c5893187dd..e8cc0fd4f2 100644 --- a/libs/libcurl/src/timediff.c +++ b/libs/libcurl/src/timediff.c @@ -1,88 +1,88 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "timediff.h" - -#include <limits.h> - -/* - * Converts number of milliseconds into a timeval structure. - * - * Return values: - * NULL IF tv is NULL or ms < 0 (eg. no timeout -> blocking select) - * tv with 0 in both fields IF ms == 0 (eg. 0ms timeout -> polling select) - * tv with converted fields IF ms > 0 (eg. >0ms timeout -> waiting select) - */ -struct timeval *curlx_mstotv(struct timeval *tv, timediff_t ms) -{ - if(!tv) - return NULL; - - if(ms < 0) - return NULL; - - if(ms > 0) { - timediff_t tv_sec = ms / 1000; - timediff_t tv_usec = (ms % 1000) * 1000; /* max=999999 */ -#ifdef HAVE_SUSECONDS_T -#if TIMEDIFF_T_MAX > TIME_T_MAX - /* tv_sec overflow check in case time_t is signed */ - if(tv_sec > TIME_T_MAX) - tv_sec = TIME_T_MAX; -#endif - tv->tv_sec = (time_t)tv_sec; - tv->tv_usec = (suseconds_t)tv_usec; -#elif defined(WIN32) /* maybe also others in the future */ -#if TIMEDIFF_T_MAX > LONG_MAX - /* tv_sec overflow check on Windows there we know it is long */ - if(tv_sec > LONG_MAX) - tv_sec = LONG_MAX; -#endif - tv->tv_sec = (long)tv_sec; - tv->tv_usec = (long)tv_usec; -#else -#if TIMEDIFF_T_MAX > INT_MAX - /* tv_sec overflow check in case time_t is signed */ - if(tv_sec > INT_MAX) - tv_sec = INT_MAX; -#endif - tv->tv_sec = (int)tv_sec; - tv->tv_usec = (int)tv_usec; -#endif - } - else { - tv->tv_sec = 0; - tv->tv_usec = 0; - } - - return tv; -} - -/* - * Converts a timeval structure into number of milliseconds. - */ -timediff_t curlx_tvtoms(struct timeval *tv) -{ - return (tv->tv_sec*1000) + (timediff_t)(((double)tv->tv_usec)/1000.0); -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "timediff.h"
+
+#include <limits.h>
+
+/*
+ * Converts number of milliseconds into a timeval structure.
+ *
+ * Return values:
+ * NULL IF tv is NULL or ms < 0 (eg. no timeout -> blocking select)
+ * tv with 0 in both fields IF ms == 0 (eg. 0ms timeout -> polling select)
+ * tv with converted fields IF ms > 0 (eg. >0ms timeout -> waiting select)
+ */
+struct timeval *curlx_mstotv(struct timeval *tv, timediff_t ms)
+{
+ if(!tv)
+ return NULL;
+
+ if(ms < 0)
+ return NULL;
+
+ if(ms > 0) {
+ timediff_t tv_sec = ms / 1000;
+ timediff_t tv_usec = (ms % 1000) * 1000; /* max=999999 */
+#ifdef HAVE_SUSECONDS_T
+#if TIMEDIFF_T_MAX > TIME_T_MAX
+ /* tv_sec overflow check in case time_t is signed */
+ if(tv_sec > TIME_T_MAX)
+ tv_sec = TIME_T_MAX;
+#endif
+ tv->tv_sec = (time_t)tv_sec;
+ tv->tv_usec = (suseconds_t)tv_usec;
+#elif defined(WIN32) /* maybe also others in the future */
+#if TIMEDIFF_T_MAX > LONG_MAX
+ /* tv_sec overflow check on Windows there we know it is long */
+ if(tv_sec > LONG_MAX)
+ tv_sec = LONG_MAX;
+#endif
+ tv->tv_sec = (long)tv_sec;
+ tv->tv_usec = (long)tv_usec;
+#else
+#if TIMEDIFF_T_MAX > INT_MAX
+ /* tv_sec overflow check in case time_t is signed */
+ if(tv_sec > INT_MAX)
+ tv_sec = INT_MAX;
+#endif
+ tv->tv_sec = (int)tv_sec;
+ tv->tv_usec = (int)tv_usec;
+#endif
+ }
+ else {
+ tv->tv_sec = 0;
+ tv->tv_usec = 0;
+ }
+
+ return tv;
+}
+
+/*
+ * Converts a timeval structure into number of milliseconds.
+ */
+timediff_t curlx_tvtoms(struct timeval *tv)
+{
+ return (tv->tv_sec*1000) + (timediff_t)(((double)tv->tv_usec)/1000.0);
+}
diff --git a/libs/libcurl/src/timediff.h b/libs/libcurl/src/timediff.h index 90e547457f..d98de76b88 100644 --- a/libs/libcurl/src/timediff.h +++ b/libs/libcurl/src/timediff.h @@ -1,52 +1,52 @@ -#ifndef HEADER_CURL_TIMEDIFF_H -#define HEADER_CURL_TIMEDIFF_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -/* Use a larger type even for 32 bit time_t systems so that we can keep - microsecond accuracy in it */ -typedef curl_off_t timediff_t; -#define CURL_FORMAT_TIMEDIFF_T CURL_FORMAT_CURL_OFF_T - -#define TIMEDIFF_T_MAX CURL_OFF_T_MAX -#define TIMEDIFF_T_MIN CURL_OFF_T_MIN - -/* - * Converts number of milliseconds into a timeval structure. - * - * Return values: - * NULL IF tv is NULL or ms < 0 (eg. no timeout -> blocking select) - * tv with 0 in both fields IF ms == 0 (eg. 0ms timeout -> polling select) - * tv with converted fields IF ms > 0 (eg. >0ms timeout -> waiting select) - */ -struct timeval *curlx_mstotv(struct timeval *tv, timediff_t ms); - -/* - * Converts a timeval structure into number of milliseconds. - */ -timediff_t curlx_tvtoms(struct timeval *tv); - -#endif /* HEADER_CURL_TIMEDIFF_H */ +#ifndef HEADER_CURL_TIMEDIFF_H
+#define HEADER_CURL_TIMEDIFF_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+/* Use a larger type even for 32 bit time_t systems so that we can keep
+ microsecond accuracy in it */
+typedef curl_off_t timediff_t;
+#define CURL_FORMAT_TIMEDIFF_T CURL_FORMAT_CURL_OFF_T
+
+#define TIMEDIFF_T_MAX CURL_OFF_T_MAX
+#define TIMEDIFF_T_MIN CURL_OFF_T_MIN
+
+/*
+ * Converts number of milliseconds into a timeval structure.
+ *
+ * Return values:
+ * NULL IF tv is NULL or ms < 0 (eg. no timeout -> blocking select)
+ * tv with 0 in both fields IF ms == 0 (eg. 0ms timeout -> polling select)
+ * tv with converted fields IF ms > 0 (eg. >0ms timeout -> waiting select)
+ */
+struct timeval *curlx_mstotv(struct timeval *tv, timediff_t ms);
+
+/*
+ * Converts a timeval structure into number of milliseconds.
+ */
+timediff_t curlx_tvtoms(struct timeval *tv);
+
+#endif /* HEADER_CURL_TIMEDIFF_H */
diff --git a/libs/libcurl/src/timeval.c b/libs/libcurl/src/timeval.c index 647d7b0fc5..fdd90d2a2d 100644 --- a/libs/libcurl/src/timeval.c +++ b/libs/libcurl/src/timeval.c @@ -1,210 +1,210 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "timeval.h" - -#if defined(WIN32) && !defined(MSDOS) - -/* set in win32_init() */ -extern LARGE_INTEGER Curl_freq; -extern bool Curl_isVistaOrGreater; - -/* In case of bug fix this function has a counterpart in tool_util.c */ -struct curltime Curl_now(void) -{ - struct curltime now; - if(Curl_isVistaOrGreater) { /* QPC timer might have issues pre-Vista */ - LARGE_INTEGER count; - QueryPerformanceCounter(&count); - now.tv_sec = (time_t)(count.QuadPart / Curl_freq.QuadPart); - now.tv_usec = (int)((count.QuadPart % Curl_freq.QuadPart) * 1000000 / - Curl_freq.QuadPart); - } - else { - /* Disable /analyze warning that GetTickCount64 is preferred */ -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable:28159) -#endif - DWORD milliseconds = GetTickCount(); -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - - now.tv_sec = milliseconds / 1000; - now.tv_usec = (milliseconds % 1000) * 1000; - } - return now; -} - -#elif defined(HAVE_CLOCK_GETTIME_MONOTONIC) - -struct curltime Curl_now(void) -{ - /* - ** clock_gettime() is granted to be increased monotonically when the - ** monotonic clock is queried. Time starting point is unspecified, it - ** could be the system start-up time, the Epoch, or something else, - ** in any case the time starting point does not change once that the - ** system has started up. - */ -#ifdef HAVE_GETTIMEOFDAY - struct timeval now; -#endif - struct curltime cnow; - struct timespec tsnow; - - /* - ** clock_gettime() may be defined by Apple's SDK as weak symbol thus - ** code compiles but fails during run-time if clock_gettime() is - ** called on unsupported OS version. - */ -#if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \ - (HAVE_BUILTIN_AVAILABLE == 1) - bool have_clock_gettime = FALSE; - if(__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *)) - have_clock_gettime = TRUE; -#endif - - if( -#if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \ - (HAVE_BUILTIN_AVAILABLE == 1) - have_clock_gettime && -#endif - (0 == clock_gettime(CLOCK_MONOTONIC, &tsnow))) { - cnow.tv_sec = tsnow.tv_sec; - cnow.tv_usec = (unsigned int)(tsnow.tv_nsec / 1000); - } - /* - ** Even when the configure process has truly detected monotonic clock - ** availability, it might happen that it is not actually available at - ** run-time. When this occurs simply fallback to other time source. - */ -#ifdef HAVE_GETTIMEOFDAY - else { - (void)gettimeofday(&now, NULL); - cnow.tv_sec = now.tv_sec; - cnow.tv_usec = (unsigned int)now.tv_usec; - } -#else - else { - cnow.tv_sec = time(NULL); - cnow.tv_usec = 0; - } -#endif - return cnow; -} - -#elif defined(HAVE_MACH_ABSOLUTE_TIME) - -#include <stdint.h> -#include <mach/mach_time.h> - -struct curltime Curl_now(void) -{ - /* - ** Monotonic timer on Mac OS is provided by mach_absolute_time(), which - ** returns time in Mach "absolute time units," which are platform-dependent. - ** To convert to nanoseconds, one must use conversion factors specified by - ** mach_timebase_info(). - */ - static mach_timebase_info_data_t timebase; - struct curltime cnow; - uint64_t usecs; - - if(0 == timebase.denom) - (void) mach_timebase_info(&timebase); - - usecs = mach_absolute_time(); - usecs *= timebase.numer; - usecs /= timebase.denom; - usecs /= 1000; - - cnow.tv_sec = usecs / 1000000; - cnow.tv_usec = (int)(usecs % 1000000); - - return cnow; -} - -#elif defined(HAVE_GETTIMEOFDAY) - -struct curltime Curl_now(void) -{ - /* - ** gettimeofday() is not granted to be increased monotonically, due to - ** clock drifting and external source time synchronization it can jump - ** forward or backward in time. - */ - struct timeval now; - struct curltime ret; - (void)gettimeofday(&now, NULL); - ret.tv_sec = now.tv_sec; - ret.tv_usec = (int)now.tv_usec; - return ret; -} - -#else - -struct curltime Curl_now(void) -{ - /* - ** time() returns the value of time in seconds since the Epoch. - */ - struct curltime now; - now.tv_sec = time(NULL); - now.tv_usec = 0; - return now; -} - -#endif - -/* - * Returns: time difference in number of milliseconds. For too large diffs it - * returns max value. - * - * @unittest: 1323 - */ -timediff_t Curl_timediff(struct curltime newer, struct curltime older) -{ - timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec; - if(diff >= (TIMEDIFF_T_MAX/1000)) - return TIMEDIFF_T_MAX; - else if(diff <= (TIMEDIFF_T_MIN/1000)) - return TIMEDIFF_T_MIN; - return diff * 1000 + (newer.tv_usec-older.tv_usec)/1000; -} - -/* - * Returns: time difference in number of microseconds. For too large diffs it - * returns max value. - */ -timediff_t Curl_timediff_us(struct curltime newer, struct curltime older) -{ - timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec; - if(diff >= (TIMEDIFF_T_MAX/1000000)) - return TIMEDIFF_T_MAX; - else if(diff <= (TIMEDIFF_T_MIN/1000000)) - return TIMEDIFF_T_MIN; - return diff * 1000000 + newer.tv_usec-older.tv_usec; -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "timeval.h"
+
+#if defined(WIN32) && !defined(MSDOS)
+
+/* set in win32_init() */
+extern LARGE_INTEGER Curl_freq;
+extern bool Curl_isVistaOrGreater;
+
+/* In case of bug fix this function has a counterpart in tool_util.c */
+struct curltime Curl_now(void)
+{
+ struct curltime now;
+ if(Curl_isVistaOrGreater) { /* QPC timer might have issues pre-Vista */
+ LARGE_INTEGER count;
+ QueryPerformanceCounter(&count);
+ now.tv_sec = (time_t)(count.QuadPart / Curl_freq.QuadPart);
+ now.tv_usec = (int)((count.QuadPart % Curl_freq.QuadPart) * 1000000 /
+ Curl_freq.QuadPart);
+ }
+ else {
+ /* Disable /analyze warning that GetTickCount64 is preferred */
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:28159)
+#endif
+ DWORD milliseconds = GetTickCount();
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+ now.tv_sec = milliseconds / 1000;
+ now.tv_usec = (milliseconds % 1000) * 1000;
+ }
+ return now;
+}
+
+#elif defined(HAVE_CLOCK_GETTIME_MONOTONIC)
+
+struct curltime Curl_now(void)
+{
+ /*
+ ** clock_gettime() is granted to be increased monotonically when the
+ ** monotonic clock is queried. Time starting point is unspecified, it
+ ** could be the system start-up time, the Epoch, or something else,
+ ** in any case the time starting point does not change once that the
+ ** system has started up.
+ */
+#ifdef HAVE_GETTIMEOFDAY
+ struct timeval now;
+#endif
+ struct curltime cnow;
+ struct timespec tsnow;
+
+ /*
+ ** clock_gettime() may be defined by Apple's SDK as weak symbol thus
+ ** code compiles but fails during run-time if clock_gettime() is
+ ** called on unsupported OS version.
+ */
+#if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \
+ (HAVE_BUILTIN_AVAILABLE == 1)
+ bool have_clock_gettime = FALSE;
+ if(__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *))
+ have_clock_gettime = TRUE;
+#endif
+
+ if(
+#if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \
+ (HAVE_BUILTIN_AVAILABLE == 1)
+ have_clock_gettime &&
+#endif
+ (0 == clock_gettime(CLOCK_MONOTONIC, &tsnow))) {
+ cnow.tv_sec = tsnow.tv_sec;
+ cnow.tv_usec = (unsigned int)(tsnow.tv_nsec / 1000);
+ }
+ /*
+ ** Even when the configure process has truly detected monotonic clock
+ ** availability, it might happen that it is not actually available at
+ ** run-time. When this occurs simply fallback to other time source.
+ */
+#ifdef HAVE_GETTIMEOFDAY
+ else {
+ (void)gettimeofday(&now, NULL);
+ cnow.tv_sec = now.tv_sec;
+ cnow.tv_usec = (unsigned int)now.tv_usec;
+ }
+#else
+ else {
+ cnow.tv_sec = time(NULL);
+ cnow.tv_usec = 0;
+ }
+#endif
+ return cnow;
+}
+
+#elif defined(HAVE_MACH_ABSOLUTE_TIME)
+
+#include <stdint.h>
+#include <mach/mach_time.h>
+
+struct curltime Curl_now(void)
+{
+ /*
+ ** Monotonic timer on Mac OS is provided by mach_absolute_time(), which
+ ** returns time in Mach "absolute time units," which are platform-dependent.
+ ** To convert to nanoseconds, one must use conversion factors specified by
+ ** mach_timebase_info().
+ */
+ static mach_timebase_info_data_t timebase;
+ struct curltime cnow;
+ uint64_t usecs;
+
+ if(0 == timebase.denom)
+ (void) mach_timebase_info(&timebase);
+
+ usecs = mach_absolute_time();
+ usecs *= timebase.numer;
+ usecs /= timebase.denom;
+ usecs /= 1000;
+
+ cnow.tv_sec = usecs / 1000000;
+ cnow.tv_usec = (int)(usecs % 1000000);
+
+ return cnow;
+}
+
+#elif defined(HAVE_GETTIMEOFDAY)
+
+struct curltime Curl_now(void)
+{
+ /*
+ ** gettimeofday() is not granted to be increased monotonically, due to
+ ** clock drifting and external source time synchronization it can jump
+ ** forward or backward in time.
+ */
+ struct timeval now;
+ struct curltime ret;
+ (void)gettimeofday(&now, NULL);
+ ret.tv_sec = now.tv_sec;
+ ret.tv_usec = (int)now.tv_usec;
+ return ret;
+}
+
+#else
+
+struct curltime Curl_now(void)
+{
+ /*
+ ** time() returns the value of time in seconds since the Epoch.
+ */
+ struct curltime now;
+ now.tv_sec = time(NULL);
+ now.tv_usec = 0;
+ return now;
+}
+
+#endif
+
+/*
+ * Returns: time difference in number of milliseconds. For too large diffs it
+ * returns max value.
+ *
+ * @unittest: 1323
+ */
+timediff_t Curl_timediff(struct curltime newer, struct curltime older)
+{
+ timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec;
+ if(diff >= (TIMEDIFF_T_MAX/1000))
+ return TIMEDIFF_T_MAX;
+ else if(diff <= (TIMEDIFF_T_MIN/1000))
+ return TIMEDIFF_T_MIN;
+ return diff * 1000 + (newer.tv_usec-older.tv_usec)/1000;
+}
+
+/*
+ * Returns: time difference in number of microseconds. For too large diffs it
+ * returns max value.
+ */
+timediff_t Curl_timediff_us(struct curltime newer, struct curltime older)
+{
+ timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec;
+ if(diff >= (TIMEDIFF_T_MAX/1000000))
+ return TIMEDIFF_T_MAX;
+ else if(diff <= (TIMEDIFF_T_MIN/1000000))
+ return TIMEDIFF_T_MIN;
+ return diff * 1000000 + newer.tv_usec-older.tv_usec;
+}
diff --git a/libs/libcurl/src/timeval.h b/libs/libcurl/src/timeval.h index 8d4fef4e17..1523aee2f5 100644 --- a/libs/libcurl/src/timeval.h +++ b/libs/libcurl/src/timeval.h @@ -1,54 +1,54 @@ -#ifndef HEADER_CURL_TIMEVAL_H -#define HEADER_CURL_TIMEVAL_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "timediff.h" - -struct curltime { - time_t tv_sec; /* seconds */ - int tv_usec; /* microseconds */ -}; - -struct curltime Curl_now(void); - -/* - * Make sure that the first argument (t1) is the more recent time and t2 is - * the older time, as otherwise you get a weird negative time-diff back... - * - * Returns: the time difference in number of milliseconds. - */ -timediff_t Curl_timediff(struct curltime t1, struct curltime t2); - -/* - * Make sure that the first argument (t1) is the more recent time and t2 is - * the older time, as otherwise you get a weird negative time-diff back... - * - * Returns: the time difference in number of microseconds. - */ -timediff_t Curl_timediff_us(struct curltime newer, struct curltime older); - -#endif /* HEADER_CURL_TIMEVAL_H */ +#ifndef HEADER_CURL_TIMEVAL_H
+#define HEADER_CURL_TIMEVAL_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "timediff.h"
+
+struct curltime {
+ time_t tv_sec; /* seconds */
+ int tv_usec; /* microseconds */
+};
+
+struct curltime Curl_now(void);
+
+/*
+ * Make sure that the first argument (t1) is the more recent time and t2 is
+ * the older time, as otherwise you get a weird negative time-diff back...
+ *
+ * Returns: the time difference in number of milliseconds.
+ */
+timediff_t Curl_timediff(struct curltime t1, struct curltime t2);
+
+/*
+ * Make sure that the first argument (t1) is the more recent time and t2 is
+ * the older time, as otherwise you get a weird negative time-diff back...
+ *
+ * Returns: the time difference in number of microseconds.
+ */
+timediff_t Curl_timediff_us(struct curltime newer, struct curltime older);
+
+#endif /* HEADER_CURL_TIMEVAL_H */
diff --git a/libs/libcurl/src/transfer.c b/libs/libcurl/src/transfer.c index 441da73429..9468de477f 100644 --- a/libs/libcurl/src/transfer.c +++ b/libs/libcurl/src/transfer.c @@ -1,2020 +1,1942 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "strtoofft.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 HAVE_NET_IF_H -#include <net/if.h> -#endif -#ifdef HAVE_SYS_IOCTL_H -#include <sys/ioctl.h> -#endif -#ifdef HAVE_SIGNAL_H -#include <signal.h> -#endif - -#ifdef HAVE_SYS_PARAM_H -#include <sys/param.h> -#endif - -#ifdef HAVE_SYS_SELECT_H -#include <sys/select.h> -#elif defined(HAVE_UNISTD_H) -#include <unistd.h> -#endif - -#ifndef HAVE_SOCKET -#error "We can't compile without socket() support!" -#endif - -#include "urldata.h" -#include <curl/curl.h> -#include "netrc.h" - -#include "content_encoding.h" -#include "hostip.h" -#include "transfer.h" -#include "sendf.h" -#include "speedcheck.h" -#include "progress.h" -#include "http.h" -#include "url.h" -#include "getinfo.h" -#include "vtls/vtls.h" -#include "select.h" -#include "multiif.h" -#include "connect.h" -#include "http2.h" -#include "mime.h" -#include "strcase.h" -#include "urlapi-int.h" -#include "hsts.h" -#include "setopt.h" -#include "headers.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \ - !defined(CURL_DISABLE_IMAP) -/* - * checkheaders() checks the linked list of custom headers for a - * particular header (prefix). Provide the prefix without colon! - * - * Returns a pointer to the first matching header or NULL if none matched. - */ -char *Curl_checkheaders(const struct Curl_easy *data, - const char *thisheader, - const size_t thislen) -{ - struct curl_slist *head; - DEBUGASSERT(thislen); - DEBUGASSERT(thisheader[thislen-1] != ':'); - - for(head = data->set.headers; head; head = head->next) { - if(strncasecompare(head->data, thisheader, thislen) && - Curl_headersep(head->data[thislen]) ) - return head->data; - } - - return NULL; -} -#endif - -CURLcode Curl_get_upload_buffer(struct Curl_easy *data) -{ - if(!data->state.ulbuf) { - data->state.ulbuf = malloc(data->set.upload_buffer_size); - if(!data->state.ulbuf) - return CURLE_OUT_OF_MEMORY; - } - return CURLE_OK; -} - -#ifndef CURL_DISABLE_HTTP -/* - * This function will be called to loop through the trailers buffer - * until no more data is available for sending. - */ -static size_t trailers_read(char *buffer, size_t size, size_t nitems, - void *raw) -{ - struct Curl_easy *data = (struct Curl_easy *)raw; - struct dynbuf *trailers_buf = &data->state.trailers_buf; - size_t bytes_left = Curl_dyn_len(trailers_buf) - - data->state.trailers_bytes_sent; - size_t to_copy = (size*nitems < bytes_left) ? size*nitems : bytes_left; - if(to_copy) { - memcpy(buffer, - Curl_dyn_ptr(trailers_buf) + data->state.trailers_bytes_sent, - to_copy); - data->state.trailers_bytes_sent += to_copy; - } - return to_copy; -} - -static size_t trailers_left(void *raw) -{ - struct Curl_easy *data = (struct Curl_easy *)raw; - struct dynbuf *trailers_buf = &data->state.trailers_buf; - return Curl_dyn_len(trailers_buf) - data->state.trailers_bytes_sent; -} -#endif - -/* - * This function will call the read callback to fill our buffer with data - * to upload. - */ -CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes, - size_t *nreadp) -{ - size_t buffersize = bytes; - size_t nread; - - curl_read_callback readfunc = NULL; - void *extra_data = NULL; - -#ifndef CURL_DISABLE_HTTP - if(data->state.trailers_state == TRAILERS_INITIALIZED) { - struct curl_slist *trailers = NULL; - CURLcode result; - int trailers_ret_code; - - /* at this point we already verified that the callback exists - so we compile and store the trailers buffer, then proceed */ - infof(data, - "Moving trailers state machine from initialized to sending."); - data->state.trailers_state = TRAILERS_SENDING; - Curl_dyn_init(&data->state.trailers_buf, DYN_TRAILERS); - - data->state.trailers_bytes_sent = 0; - Curl_set_in_callback(data, true); - trailers_ret_code = data->set.trailer_callback(&trailers, - data->set.trailer_data); - Curl_set_in_callback(data, false); - if(trailers_ret_code == CURL_TRAILERFUNC_OK) { - result = Curl_http_compile_trailers(trailers, &data->state.trailers_buf, - data); - } - else { - failf(data, "operation aborted by trailing headers callback"); - *nreadp = 0; - result = CURLE_ABORTED_BY_CALLBACK; - } - if(result) { - Curl_dyn_free(&data->state.trailers_buf); - curl_slist_free_all(trailers); - return result; - } - infof(data, "Successfully compiled trailers."); - curl_slist_free_all(trailers); - } -#endif - -#ifndef CURL_DISABLE_HTTP - /* if we are transmitting trailing data, we don't need to write - a chunk size so we skip this */ - if(data->req.upload_chunky && - data->state.trailers_state == TRAILERS_NONE) { - /* if chunked Transfer-Encoding */ - buffersize -= (8 + 2 + 2); /* 32bit hex + CRLF + CRLF */ - data->req.upload_fromhere += (8 + 2); /* 32bit hex + CRLF */ - } - - if(data->state.trailers_state == TRAILERS_SENDING) { - /* if we're here then that means that we already sent the last empty chunk - but we didn't send a final CR LF, so we sent 0 CR LF. We then start - pulling trailing data until we have no more at which point we - simply return to the previous point in the state machine as if - nothing happened. - */ - readfunc = trailers_read; - extra_data = (void *)data; - } - else -#endif - { - readfunc = data->state.fread_func; - extra_data = data->state.in; - } - - Curl_set_in_callback(data, true); - nread = readfunc(data->req.upload_fromhere, 1, - buffersize, extra_data); - Curl_set_in_callback(data, false); - - if(nread == CURL_READFUNC_ABORT) { - failf(data, "operation aborted by callback"); - *nreadp = 0; - return CURLE_ABORTED_BY_CALLBACK; - } - if(nread == CURL_READFUNC_PAUSE) { - struct SingleRequest *k = &data->req; - - if(data->conn->handler->flags & PROTOPT_NONETWORK) { - /* protocols that work without network cannot be paused. This is - actually only FILE:// just now, and it can't pause since the transfer - isn't done using the "normal" procedure. */ - failf(data, "Read callback asked for PAUSE when not supported"); - return CURLE_READ_ERROR; - } - - /* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */ - k->keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */ - if(data->req.upload_chunky) { - /* Back out the preallocation done above */ - data->req.upload_fromhere -= (8 + 2); - } - *nreadp = 0; - - return CURLE_OK; /* nothing was read */ - } - else if(nread > buffersize) { - /* the read function returned a too large value */ - *nreadp = 0; - failf(data, "read function returned funny value"); - return CURLE_READ_ERROR; - } - -#ifndef CURL_DISABLE_HTTP - if(!data->req.forbidchunk && data->req.upload_chunky) { - /* if chunked Transfer-Encoding - * build chunk: - * - * <HEX SIZE> CRLF - * <DATA> CRLF - */ - /* On non-ASCII platforms the <DATA> may or may not be - translated based on state.prefer_ascii while the protocol - portion must always be translated to the network encoding. - To further complicate matters, line end conversion might be - done later on, so we need to prevent CRLFs from becoming - CRCRLFs if that's the case. To do this we use bare LFs - here, knowing they'll become CRLFs later on. - */ - - bool added_crlf = FALSE; - int hexlen = 0; - const char *endofline_native; - const char *endofline_network; - - if( -#ifdef CURL_DO_LINEEND_CONV - (data->state.prefer_ascii) || -#endif - (data->set.crlf)) { - /* \n will become \r\n later on */ - endofline_native = "\n"; - endofline_network = "\x0a"; - } - else { - endofline_native = "\r\n"; - endofline_network = "\x0d\x0a"; - } - - /* if we're not handling trailing data, proceed as usual */ - if(data->state.trailers_state != TRAILERS_SENDING) { - char hexbuffer[11] = ""; - hexlen = msnprintf(hexbuffer, sizeof(hexbuffer), - "%zx%s", nread, endofline_native); - - /* move buffer pointer */ - data->req.upload_fromhere -= hexlen; - nread += hexlen; - - /* copy the prefix to the buffer, leaving out the NUL */ - memcpy(data->req.upload_fromhere, hexbuffer, hexlen); - - /* always append ASCII CRLF to the data unless - we have a valid trailer callback */ - if((nread-hexlen) == 0 && - data->set.trailer_callback != NULL && - data->state.trailers_state == TRAILERS_NONE) { - data->state.trailers_state = TRAILERS_INITIALIZED; - } - else { - memcpy(data->req.upload_fromhere + nread, - endofline_network, - strlen(endofline_network)); - added_crlf = TRUE; - } - } - - if(data->state.trailers_state == TRAILERS_SENDING && - !trailers_left(data)) { - Curl_dyn_free(&data->state.trailers_buf); - data->state.trailers_state = TRAILERS_DONE; - data->set.trailer_data = NULL; - data->set.trailer_callback = NULL; - /* mark the transfer as done */ - data->req.upload_done = TRUE; - infof(data, "Signaling end of chunked upload after trailers."); - } - else - if((nread - hexlen) == 0 && - data->state.trailers_state != TRAILERS_INITIALIZED) { - /* mark this as done once this chunk is transferred */ - data->req.upload_done = TRUE; - infof(data, - "Signaling end of chunked upload via terminating chunk."); - } - - if(added_crlf) - nread += strlen(endofline_network); /* for the added end of line */ - } -#endif - - *nreadp = nread; - - return CURLE_OK; -} - - -/* - * Curl_readrewind() rewinds the read stream. This is typically used for HTTP - * POST/PUT with multi-pass authentication when a sending was denied and a - * resend is necessary. - */ -CURLcode Curl_readrewind(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - curl_mimepart *mimepart = &data->set.mimepost; - - conn->bits.rewindaftersend = FALSE; /* we rewind now */ - - /* explicitly switch off sending data on this connection now since we are - about to restart a new transfer and thus we want to avoid inadvertently - sending more data on the existing connection until the next transfer - starts */ - data->req.keepon &= ~KEEP_SEND; - - /* We have sent away data. If not using CURLOPT_POSTFIELDS or - CURLOPT_HTTPPOST, call app to rewind - */ - if(conn->handler->protocol & PROTO_FAMILY_HTTP) { - struct HTTP *http = data->req.p.http; - - if(http->sendit) - mimepart = http->sendit; - } - if(data->set.postfields) - ; /* do nothing */ - else if(data->state.httpreq == HTTPREQ_POST_MIME || - data->state.httpreq == HTTPREQ_POST_FORM) { - CURLcode result = Curl_mime_rewind(mimepart); - if(result) { - failf(data, "Cannot rewind mime/post data"); - return result; - } - } - else { - if(data->set.seek_func) { - int err; - - Curl_set_in_callback(data, true); - err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET); - Curl_set_in_callback(data, false); - if(err) { - failf(data, "seek callback returned error %d", (int)err); - return CURLE_SEND_FAIL_REWIND; - } - } - else if(data->set.ioctl_func) { - curlioerr err; - - Curl_set_in_callback(data, true); - err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD, - data->set.ioctl_client); - Curl_set_in_callback(data, false); - infof(data, "the ioctl callback returned %d", (int)err); - - if(err) { - failf(data, "ioctl callback returned error %d", (int)err); - return CURLE_SEND_FAIL_REWIND; - } - } - else { - /* If no CURLOPT_READFUNCTION is used, we know that we operate on a - given FILE * stream and we can actually attempt to rewind that - ourselves with fseek() */ - if(data->state.fread_func == (curl_read_callback)fread) { - if(-1 != fseek(data->state.in, 0, SEEK_SET)) - /* successful rewind */ - return CURLE_OK; - } - - /* no callback set or failure above, makes us fail at once */ - failf(data, "necessary data rewind wasn't possible"); - return CURLE_SEND_FAIL_REWIND; - } - } - return CURLE_OK; -} - -static int data_pending(const struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - -#ifdef ENABLE_QUIC - if(conn->transport == TRNSPRT_QUIC) - return Curl_quic_data_pending(data); -#endif - - if(conn->handler->protocol&PROTO_FAMILY_FTP) - return Curl_ssl_data_pending(conn, SECONDARYSOCKET); - - /* in the case of libssh2, we can never be really sure that we have emptied - its internal buffers so we MUST always try until we get EAGAIN back */ - return conn->handler->protocol&(CURLPROTO_SCP|CURLPROTO_SFTP) || -#ifdef USE_NGHTTP2 - /* For HTTP/2, we may read up everything including response body - with header fields in Curl_http_readwrite_headers. If no - content-length is provided, curl waits for the connection - close, which we emulate it using conn->proto.httpc.closed = - TRUE. The thing is if we read everything, then http2_recv won't - be called and we cannot signal the HTTP/2 stream has closed. As - a workaround, we return nonzero here to call http2_recv. */ - ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion >= 20) || -#endif - Curl_ssl_data_pending(conn, FIRSTSOCKET); -} - -/* - * Check to see if CURLOPT_TIMECONDITION was met by comparing the time of the - * remote document with the time provided by CURLOPT_TIMEVAL - */ -bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc) -{ - if((timeofdoc == 0) || (data->set.timevalue == 0)) - return TRUE; - - switch(data->set.timecondition) { - case CURL_TIMECOND_IFMODSINCE: - default: - if(timeofdoc <= data->set.timevalue) { - infof(data, - "The requested document is not new enough"); - data->info.timecond = TRUE; - return FALSE; - } - break; - case CURL_TIMECOND_IFUNMODSINCE: - if(timeofdoc >= data->set.timevalue) { - infof(data, - "The requested document is not old enough"); - data->info.timecond = TRUE; - return FALSE; - } - break; - } - - return TRUE; -} - -/* - * Go ahead and do a read if we have a readable socket or if - * the stream was rewound (in which case we have data in a - * buffer) - * - * return '*comeback' TRUE if we didn't properly drain the socket so this - * function should get called again without select() or similar in between! - */ -static CURLcode readwrite_data(struct Curl_easy *data, - struct connectdata *conn, - struct SingleRequest *k, - int *didwhat, bool *done, - bool *comeback) -{ - CURLcode result = CURLE_OK; - ssize_t nread; /* number of bytes read */ - size_t excess = 0; /* excess bytes read */ - bool readmore = FALSE; /* used by RTP to signal for more data */ - int maxloops = 100; - char *buf = data->state.buffer; - DEBUGASSERT(buf); - - *done = FALSE; - *comeback = FALSE; - - /* This is where we loop until we have read everything there is to - read or we get a CURLE_AGAIN */ - do { - bool is_empty_data = FALSE; - size_t buffersize = data->set.buffer_size; - size_t bytestoread = buffersize; -#ifdef USE_NGHTTP2 - bool is_http2 = ((conn->handler->protocol & PROTO_FAMILY_HTTP) && - (conn->httpversion == 20)); -#endif - bool is_http3 = -#ifdef ENABLE_QUIC - ((conn->handler->protocol & PROTO_FAMILY_HTTP) && - (conn->httpversion == 30)); -#else - FALSE; -#endif - - if( -#ifdef USE_NGHTTP2 - /* For HTTP/2, read data without caring about the content length. This - is safe because body in HTTP/2 is always segmented thanks to its - framing layer. Meanwhile, we have to call Curl_read to ensure that - http2_handle_stream_close is called when we read all incoming bytes - for a particular stream. */ - !is_http2 && -#endif - !is_http3 && /* Same reason mentioned above. */ - k->size != -1 && !k->header) { - /* make sure we don't read too much */ - curl_off_t totalleft = k->size - k->bytecount; - if(totalleft < (curl_off_t)bytestoread) - bytestoread = (size_t)totalleft; - } - - if(bytestoread) { - /* receive data from the network! */ - result = Curl_read(data, conn->sockfd, buf, bytestoread, &nread); - - /* read would've blocked */ - if(CURLE_AGAIN == result) - break; /* get out of loop */ - - if(result>0) - return result; - } - else { - /* read nothing but since we wanted nothing we consider this an OK - situation to proceed from */ - DEBUGF(infof(data, "readwrite_data: we're done")); - nread = 0; - } - - if(!k->bytecount) { - Curl_pgrsTime(data, TIMER_STARTTRANSFER); - if(k->exp100 > EXP100_SEND_DATA) - /* set time stamp to compare with when waiting for the 100 */ - k->start100 = Curl_now(); - } - - *didwhat |= KEEP_RECV; - /* indicates data of zero size, i.e. empty file */ - is_empty_data = ((nread == 0) && (k->bodywrites == 0)) ? TRUE : FALSE; - - if(0 < nread || is_empty_data) { - buf[nread] = 0; - } - else { - /* if we receive 0 or less here, either the http2 stream is closed or the - server closed the connection and we bail out from this! */ -#ifdef USE_NGHTTP2 - if(is_http2 && !nread) - DEBUGF(infof(data, "nread == 0, stream closed, bailing")); - else -#endif - if(is_http3 && !nread) - DEBUGF(infof(data, "nread == 0, stream closed, bailing")); - else - DEBUGF(infof(data, "nread <= 0, server closed connection, bailing")); - k->keepon &= ~KEEP_RECV; - break; - } - - /* Default buffer to use when we write the buffer, it may be changed - in the flow below before the actual storing is done. */ - k->str = buf; - - if(conn->handler->readwrite) { - result = conn->handler->readwrite(data, conn, &nread, &readmore); - if(result) - return result; - if(readmore) - break; - } - -#ifndef CURL_DISABLE_HTTP - /* Since this is a two-state thing, we check if we are parsing - headers at the moment or not. */ - if(k->header) { - /* we are in parse-the-header-mode */ - bool stop_reading = FALSE; - result = Curl_http_readwrite_headers(data, conn, &nread, &stop_reading); - if(result) - return result; - - if(conn->handler->readwrite && - (k->maxdownload <= 0 && nread > 0)) { - result = conn->handler->readwrite(data, conn, &nread, &readmore); - if(result) - return result; - if(readmore) - break; - } - - if(stop_reading) { - /* We've stopped dealing with input, get out of the do-while loop */ - - if(nread > 0) { - infof(data, - "Excess found:" - " excess = %zd" - " url = %s (zero-length body)", - nread, data->state.up.path); - } - - break; - } - } -#endif /* CURL_DISABLE_HTTP */ - - - /* This is not an 'else if' since it may be a rest from the header - parsing, where the beginning of the buffer is headers and the end - is non-headers. */ - if(!k->header && (nread > 0 || is_empty_data)) { - - if(data->set.opt_no_body) { - /* data arrives although we want none, bail out */ - streamclose(conn, "ignoring body"); - *done = TRUE; - return CURLE_WEIRD_SERVER_REPLY; - } - -#ifndef CURL_DISABLE_HTTP - if(0 == k->bodywrites && !is_empty_data) { - /* These checks are only made the first time we are about to - write a piece of the body */ - if(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) { - /* HTTP-only checks */ - result = Curl_http_firstwrite(data, conn, done); - if(result || *done) - return result; - } - } /* this is the first time we write a body part */ -#endif /* CURL_DISABLE_HTTP */ - - k->bodywrites++; - - /* pass data to the debug function before it gets "dechunked" */ - if(data->set.verbose) { - if(k->badheader) { - Curl_debug(data, CURLINFO_DATA_IN, - Curl_dyn_ptr(&data->state.headerb), - Curl_dyn_len(&data->state.headerb)); - if(k->badheader == HEADER_PARTHEADER) - Curl_debug(data, CURLINFO_DATA_IN, - k->str, (size_t)nread); - } - else - Curl_debug(data, CURLINFO_DATA_IN, - k->str, (size_t)nread); - } - -#ifndef CURL_DISABLE_HTTP - if(k->chunk) { - /* - * Here comes a chunked transfer flying and we need to decode this - * properly. While the name says read, this function both reads - * and writes away the data. The returned 'nread' holds the number - * of actual data it wrote to the client. - */ - CURLcode extra; - CHUNKcode res = - Curl_httpchunk_read(data, k->str, nread, &nread, &extra); - - if(CHUNKE_OK < res) { - if(CHUNKE_PASSTHRU_ERROR == res) { - failf(data, "Failed reading the chunked-encoded stream"); - return extra; - } - failf(data, "%s in chunked-encoding", Curl_chunked_strerror(res)); - return CURLE_RECV_ERROR; - } - if(CHUNKE_STOP == res) { - /* we're done reading chunks! */ - k->keepon &= ~KEEP_RECV; /* read no more */ - - /* N number of bytes at the end of the str buffer that weren't - written to the client. */ - if(conn->chunk.datasize) { - infof(data, "Leftovers after chunking: % " - CURL_FORMAT_CURL_OFF_T "u bytes", - conn->chunk.datasize); - } - } - /* If it returned OK, we just keep going */ - } -#endif /* CURL_DISABLE_HTTP */ - - /* Account for body content stored in the header buffer */ - if((k->badheader == HEADER_PARTHEADER) && !k->ignorebody) { - size_t headlen = Curl_dyn_len(&data->state.headerb); - DEBUGF(infof(data, "Increasing bytecount by %zu", headlen)); - k->bytecount += headlen; - } - - if((-1 != k->maxdownload) && - (k->bytecount + nread >= k->maxdownload)) { - - excess = (size_t)(k->bytecount + nread - k->maxdownload); - if(excess > 0 && !k->ignorebody) { - infof(data, - "Excess found in a read:" - " excess = %zu" - ", size = %" CURL_FORMAT_CURL_OFF_T - ", maxdownload = %" CURL_FORMAT_CURL_OFF_T - ", bytecount = %" CURL_FORMAT_CURL_OFF_T, - excess, k->size, k->maxdownload, k->bytecount); - connclose(conn, "excess found in a read"); - } - - nread = (ssize_t) (k->maxdownload - k->bytecount); - if(nread < 0) /* this should be unusual */ - nread = 0; - - /* HTTP/3 over QUIC should keep reading until QUIC connection - is closed. In contrast to HTTP/2 which can stop reading - from TCP connection, HTTP/3 over QUIC needs ACK from server - to ensure stream closure. It should keep reading. */ - if(!is_http3) { - k->keepon &= ~KEEP_RECV; /* we're done reading */ - } - } - - k->bytecount += nread; - - Curl_pgrsSetDownloadCounter(data, k->bytecount); - - if(!k->chunk && (nread || k->badheader || is_empty_data)) { - /* If this is chunky transfer, it was already written */ - - if(k->badheader && !k->ignorebody) { - /* we parsed a piece of data wrongly assuming it was a header - and now we output it as body instead */ - size_t headlen = Curl_dyn_len(&data->state.headerb); - - /* Don't let excess data pollute body writes */ - if(k->maxdownload == -1 || (curl_off_t)headlen <= k->maxdownload) - result = Curl_client_write(data, CLIENTWRITE_BODY, - Curl_dyn_ptr(&data->state.headerb), - headlen); - else - result = Curl_client_write(data, CLIENTWRITE_BODY, - Curl_dyn_ptr(&data->state.headerb), - (size_t)k->maxdownload); - - if(result) - return result; - } - if(k->badheader < HEADER_ALLBAD) { - /* This switch handles various content encodings. If there's an - error here, be sure to check over the almost identical code - in http_chunks.c. - Make sure that ALL_CONTENT_ENCODINGS contains all the - encodings handled here. */ - if(data->set.http_ce_skip || !k->writer_stack) { - if(!k->ignorebody && nread) { -#ifndef CURL_DISABLE_POP3 - if(conn->handler->protocol & PROTO_FAMILY_POP3) - result = Curl_pop3_write(data, k->str, nread); - else -#endif /* CURL_DISABLE_POP3 */ - result = Curl_client_write(data, CLIENTWRITE_BODY, k->str, - nread); - } - } - else if(!k->ignorebody && nread) - result = Curl_unencode_write(data, k->writer_stack, k->str, nread); - } - k->badheader = HEADER_NORMAL; /* taken care of now */ - - if(result) - return result; - } - - } /* if(!header and data to read) */ - - if(conn->handler->readwrite && excess) { - /* Parse the excess data */ - k->str += nread; - - if(&k->str[excess] > &buf[data->set.buffer_size]) { - /* the excess amount was too excessive(!), make sure - it doesn't read out of buffer */ - excess = &buf[data->set.buffer_size] - k->str; - } - nread = (ssize_t)excess; - - result = conn->handler->readwrite(data, conn, &nread, &readmore); - if(result) - return result; - - if(readmore) - k->keepon |= KEEP_RECV; /* we're not done reading */ - break; - } - - if(is_empty_data) { - /* if we received nothing, the server closed the connection and we - are done */ - k->keepon &= ~KEEP_RECV; - } - - if(k->keepon & KEEP_RECV_PAUSE) { - /* this is a paused transfer */ - break; - } - - } while(data_pending(data) && maxloops--); - - if(maxloops <= 0) { - /* we mark it as read-again-please */ - conn->cselect_bits = CURL_CSELECT_IN; - *comeback = TRUE; - } - - if(((k->keepon & (KEEP_RECV|KEEP_SEND)) == KEEP_SEND) && - conn->bits.close) { - /* When we've read the entire thing and the close bit is set, the server - may now close the connection. If there's now any kind of sending going - on from our side, we need to stop that immediately. */ - infof(data, "we are done reading and this is set to close, stop send"); - k->keepon &= ~KEEP_SEND; /* no writing anymore either */ - } - - return CURLE_OK; -} - -CURLcode Curl_done_sending(struct Curl_easy *data, - struct SingleRequest *k) -{ - struct connectdata *conn = data->conn; - k->keepon &= ~KEEP_SEND; /* we're done writing */ - - /* These functions should be moved into the handler struct! */ - Curl_http2_done_sending(data, conn); - Curl_quic_done_sending(data); - - if(conn->bits.rewindaftersend) { - CURLcode result = Curl_readrewind(data); - if(result) - return result; - } - return CURLE_OK; -} - -#if defined(WIN32) && defined(USE_WINSOCK) -#ifndef SIO_IDEAL_SEND_BACKLOG_QUERY -#define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B -#endif - -static void win_update_buffer_size(curl_socket_t sockfd) -{ - int result; - ULONG ideal; - DWORD ideallen; - result = WSAIoctl(sockfd, SIO_IDEAL_SEND_BACKLOG_QUERY, 0, 0, - &ideal, sizeof(ideal), &ideallen, 0, 0); - if(result == 0) { - setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, - (const char *)&ideal, sizeof(ideal)); - } -} -#else -#define win_update_buffer_size(x) -#endif - -#define curl_upload_refill_watermark(data) \ - ((ssize_t)((data)->set.upload_buffer_size >> 5)) - -/* - * Send data to upload to the server, when the socket is writable. - */ -static CURLcode readwrite_upload(struct Curl_easy *data, - struct connectdata *conn, - int *didwhat) -{ - ssize_t i, si; - ssize_t bytes_written; - CURLcode result; - ssize_t nread; /* number of bytes read */ - bool sending_http_headers = FALSE; - struct SingleRequest *k = &data->req; - - if((k->bytecount == 0) && (k->writebytecount == 0)) - Curl_pgrsTime(data, TIMER_STARTTRANSFER); - - *didwhat |= KEEP_SEND; - - do { - curl_off_t nbody; - ssize_t offset = 0; - - if(0 != k->upload_present && - k->upload_present < curl_upload_refill_watermark(data) && - !k->upload_chunky &&/*(variable sized chunked header; append not safe)*/ - !k->upload_done && /*!(k->upload_done once k->upload_present sent)*/ - !(k->writebytecount + k->upload_present - k->pendingheader == - data->state.infilesize)) { - offset = k->upload_present; - } - - /* only read more data if there's no upload data already - present in the upload buffer, or if appending to upload buffer */ - if(0 == k->upload_present || offset) { - result = Curl_get_upload_buffer(data); - if(result) - return result; - if(offset && k->upload_fromhere != data->state.ulbuf) - memmove(data->state.ulbuf, k->upload_fromhere, offset); - /* init the "upload from here" pointer */ - k->upload_fromhere = data->state.ulbuf; - - if(!k->upload_done) { - /* HTTP pollution, this should be written nicer to become more - protocol agnostic. */ - size_t fillcount; - struct HTTP *http = k->p.http; - - if((k->exp100 == EXP100_SENDING_REQUEST) && - (http->sending == HTTPSEND_BODY)) { - /* If this call is to send body data, we must take some action: - We have sent off the full HTTP 1.1 request, and we shall now - go into the Expect: 100 state and await such a header */ - k->exp100 = EXP100_AWAITING_CONTINUE; /* wait for the header */ - k->keepon &= ~KEEP_SEND; /* disable writing */ - k->start100 = Curl_now(); /* timeout count starts now */ - *didwhat &= ~KEEP_SEND; /* we didn't write anything actually */ - /* set a timeout for the multi interface */ - Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT); - break; - } - - if(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) { - if(http->sending == HTTPSEND_REQUEST) - /* We're sending the HTTP request headers, not the data. - Remember that so we don't change the line endings. */ - sending_http_headers = TRUE; - else - sending_http_headers = FALSE; - } - - k->upload_fromhere += offset; - result = Curl_fillreadbuffer(data, data->set.upload_buffer_size-offset, - &fillcount); - k->upload_fromhere -= offset; - if(result) - return result; - - nread = offset + fillcount; - } - else - nread = 0; /* we're done uploading/reading */ - - if(!nread && (k->keepon & KEEP_SEND_PAUSE)) { - /* this is a paused transfer */ - break; - } - if(nread <= 0) { - result = Curl_done_sending(data, k); - if(result) - return result; - break; - } - - /* store number of bytes available for upload */ - k->upload_present = nread; - - /* convert LF to CRLF if so asked */ - if((!sending_http_headers) && ( -#ifdef CURL_DO_LINEEND_CONV - /* always convert if we're FTPing in ASCII mode */ - (data->state.prefer_ascii) || -#endif - (data->set.crlf))) { - /* Do we need to allocate a scratch buffer? */ - if(!data->state.scratch) { - data->state.scratch = malloc(2 * data->set.upload_buffer_size); - if(!data->state.scratch) { - failf(data, "Failed to alloc scratch buffer"); - - return CURLE_OUT_OF_MEMORY; - } - } - - /* - * ASCII/EBCDIC Note: This is presumably a text (not binary) - * transfer so the data should already be in ASCII. - * That means the hex values for ASCII CR (0x0d) & LF (0x0a) - * must be used instead of the escape sequences \r & \n. - */ - if(offset) - memcpy(data->state.scratch, k->upload_fromhere, offset); - for(i = offset, si = offset; i < nread; i++, si++) { - if(k->upload_fromhere[i] == 0x0a) { - data->state.scratch[si++] = 0x0d; - data->state.scratch[si] = 0x0a; - if(!data->set.crlf) { - /* we're here only because FTP is in ASCII mode... - bump infilesize for the LF we just added */ - if(data->state.infilesize != -1) - data->state.infilesize++; - } - } - else - data->state.scratch[si] = k->upload_fromhere[i]; - } - - if(si != nread) { - /* only perform the special operation if we really did replace - anything */ - nread = si; - - /* upload from the new (replaced) buffer instead */ - k->upload_fromhere = data->state.scratch; - - /* set the new amount too */ - k->upload_present = nread; - } - } - -#ifndef CURL_DISABLE_SMTP - if(conn->handler->protocol & PROTO_FAMILY_SMTP) { - result = Curl_smtp_escape_eob(data, nread, offset); - if(result) - return result; - } -#endif /* CURL_DISABLE_SMTP */ - } /* if 0 == k->upload_present or appended to upload buffer */ - else { - /* We have a partial buffer left from a previous "round". Use - that instead of reading more data */ - } - - /* write to socket (send away data) */ - result = Curl_write(data, - conn->writesockfd, /* socket to send to */ - k->upload_fromhere, /* buffer pointer */ - k->upload_present, /* buffer size */ - &bytes_written); /* actually sent */ - if(result) - return result; - - win_update_buffer_size(conn->writesockfd); - - if(k->pendingheader) { - /* parts of what was sent was header */ - curl_off_t n = CURLMIN(k->pendingheader, bytes_written); - /* show the data before we change the pointer upload_fromhere */ - Curl_debug(data, CURLINFO_HEADER_OUT, k->upload_fromhere, (size_t)n); - k->pendingheader -= n; - nbody = bytes_written - n; /* size of the written body part */ - } - else - nbody = bytes_written; - - if(nbody) { - /* show the data before we change the pointer upload_fromhere */ - Curl_debug(data, CURLINFO_DATA_OUT, - &k->upload_fromhere[bytes_written - nbody], - (size_t)nbody); - - k->writebytecount += nbody; - Curl_pgrsSetUploadCounter(data, k->writebytecount); - } - - if((!k->upload_chunky || k->forbidchunk) && - (k->writebytecount == data->state.infilesize)) { - /* we have sent all data we were supposed to */ - k->upload_done = TRUE; - infof(data, "We are completely uploaded and fine"); - } - - if(k->upload_present != bytes_written) { - /* we only wrote a part of the buffer (if anything), deal with it! */ - - /* store the amount of bytes left in the buffer to write */ - k->upload_present -= bytes_written; - - /* advance the pointer where to find the buffer when the next send - is to happen */ - k->upload_fromhere += bytes_written; - } - else { - /* we've uploaded that buffer now */ - result = Curl_get_upload_buffer(data); - if(result) - return result; - k->upload_fromhere = data->state.ulbuf; - k->upload_present = 0; /* no more bytes left */ - - if(k->upload_done) { - result = Curl_done_sending(data, k); - if(result) - return result; - } - } - - - } while(0); /* just to break out from! */ - - return CURLE_OK; -} - -/* - * Curl_readwrite() is the low-level function to be called when data is to - * be read and written to/from the connection. - * - * return '*comeback' TRUE if we didn't properly drain the socket so this - * function should get called again without select() or similar in between! - */ -CURLcode Curl_readwrite(struct connectdata *conn, - struct Curl_easy *data, - bool *done, - bool *comeback) -{ - struct SingleRequest *k = &data->req; - CURLcode result; - int didwhat = 0; - - curl_socket_t fd_read; - curl_socket_t fd_write; - int select_res = conn->cselect_bits; - - conn->cselect_bits = 0; - - /* only use the proper socket if the *_HOLD bit is not set simultaneously as - then we are in rate limiting state in that transfer direction */ - - if((k->keepon & KEEP_RECVBITS) == KEEP_RECV) - fd_read = conn->sockfd; - else - fd_read = CURL_SOCKET_BAD; - - if((k->keepon & KEEP_SENDBITS) == KEEP_SEND) - fd_write = conn->writesockfd; - else - fd_write = CURL_SOCKET_BAD; - -#if defined(USE_HTTP2) || defined(USE_HTTP3) - if(data->state.drain) { - select_res |= CURL_CSELECT_IN; - DEBUGF(infof(data, "Curl_readwrite: forcibly told to drain data")); - } -#endif - - if(!select_res) /* Call for select()/poll() only, if read/write/error - status is not known. */ - select_res = Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write, 0); - - if(select_res == CURL_CSELECT_ERR) { - failf(data, "select/poll returned error"); - return CURLE_SEND_ERROR; - } - -#ifdef USE_HYPER - if(conn->datastream) { - result = conn->datastream(data, conn, &didwhat, done, select_res); - if(result || *done) - return result; - } - else { -#endif - /* We go ahead and do a read if we have a readable socket or if - the stream was rewound (in which case we have data in a - buffer) */ - if((k->keepon & KEEP_RECV) && (select_res & CURL_CSELECT_IN)) { - result = readwrite_data(data, conn, k, &didwhat, done, comeback); - if(result || *done) - return result; - } - - /* If we still have writing to do, we check if we have a writable socket. */ - if((k->keepon & KEEP_SEND) && (select_res & CURL_CSELECT_OUT)) { - /* write */ - - result = readwrite_upload(data, conn, &didwhat); - if(result) - return result; - } -#ifdef USE_HYPER - } -#endif - - k->now = Curl_now(); - if(!didwhat) { - /* no read no write, this is a timeout? */ - if(k->exp100 == EXP100_AWAITING_CONTINUE) { - /* This should allow some time for the header to arrive, but only a - very short time as otherwise it'll be too much wasted time too - often. */ - - /* Quoting RFC2616, section "8.2.3 Use of the 100 (Continue) Status": - - Therefore, when a client sends this header field to an origin server - (possibly via a proxy) from which it has never seen a 100 (Continue) - status, the client SHOULD NOT wait for an indefinite period before - sending the request body. - - */ - - timediff_t ms = Curl_timediff(k->now, k->start100); - if(ms >= data->set.expect_100_timeout) { - /* we've waited long enough, continue anyway */ - k->exp100 = EXP100_SEND_DATA; - k->keepon |= KEEP_SEND; - Curl_expire_done(data, EXPIRE_100_TIMEOUT); - infof(data, "Done waiting for 100-continue"); - } - } - -#ifdef ENABLE_QUIC - if(conn->transport == TRNSPRT_QUIC) { - result = Curl_quic_idle(data); - if(result) - return result; - } -#endif - } - - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - else - result = Curl_speedcheck(data, k->now); - if(result) - return result; - - if(k->keepon) { - if(0 > Curl_timeleft(data, &k->now, FALSE)) { - if(k->size != -1) { - failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T - " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %" - CURL_FORMAT_CURL_OFF_T " bytes received", - Curl_timediff(k->now, data->progress.t_startsingle), - k->bytecount, k->size); - } - else { - failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T - " milliseconds with %" CURL_FORMAT_CURL_OFF_T " bytes received", - Curl_timediff(k->now, data->progress.t_startsingle), - k->bytecount); - } - return CURLE_OPERATION_TIMEDOUT; - } - } - else { - /* - * The transfer has been performed. Just make some general checks before - * returning. - */ - - if(!(data->set.opt_no_body) && (k->size != -1) && - (k->bytecount != k->size) && -#ifdef CURL_DO_LINEEND_CONV - /* Most FTP servers don't adjust their file SIZE response for CRLFs, - so we'll check to see if the discrepancy can be explained - by the number of CRLFs we've changed to LFs. - */ - (k->bytecount != (k->size + data->state.crlf_conversions)) && -#endif /* CURL_DO_LINEEND_CONV */ - !k->newurl) { - failf(data, "transfer closed with %" CURL_FORMAT_CURL_OFF_T - " bytes remaining to read", k->size - k->bytecount); - return CURLE_PARTIAL_FILE; - } - if(!(data->set.opt_no_body) && k->chunk && - (conn->chunk.state != CHUNK_STOP)) { - /* - * In chunked mode, return an error if the connection is closed prior to - * the empty (terminating) chunk is read. - * - * The condition above used to check for - * conn->proto.http->chunk.datasize != 0 which is true after reading - * *any* chunk, not just the empty chunk. - * - */ - failf(data, "transfer closed with outstanding read data remaining"); - return CURLE_PARTIAL_FILE; - } - if(Curl_pgrsUpdate(data)) - return CURLE_ABORTED_BY_CALLBACK; - } - - /* Now update the "done" boolean we return */ - *done = (0 == (k->keepon&(KEEP_RECV|KEEP_SEND| - KEEP_RECV_PAUSE|KEEP_SEND_PAUSE))) ? TRUE : FALSE; - - return CURLE_OK; -} - -/* - * Curl_single_getsock() gets called by the multi interface code when the app - * has requested to get the sockets for the current connection. This function - * will then be called once for every connection that the multi interface - * keeps track of. This function will only be called for connections that are - * in the proper state to have this information available. - */ -int Curl_single_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *sock) -{ - int bitmap = GETSOCK_BLANK; - unsigned sockindex = 0; - - if(conn->handler->perform_getsock) - return conn->handler->perform_getsock(data, conn, sock); - - /* don't include HOLD and PAUSE connections */ - if((data->req.keepon & KEEP_RECVBITS) == KEEP_RECV) { - - DEBUGASSERT(conn->sockfd != CURL_SOCKET_BAD); - - bitmap |= GETSOCK_READSOCK(sockindex); - sock[sockindex] = conn->sockfd; - } - - /* don't include HOLD and PAUSE connections */ - if((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) { - - if((conn->sockfd != conn->writesockfd) || - bitmap == GETSOCK_BLANK) { - /* only if they are not the same socket and we have a readable - one, we increase index */ - if(bitmap != GETSOCK_BLANK) - sockindex++; /* increase index if we need two entries */ - - DEBUGASSERT(conn->writesockfd != CURL_SOCKET_BAD); - - sock[sockindex] = conn->writesockfd; - } - - bitmap |= GETSOCK_WRITESOCK(sockindex); - } - - return bitmap; -} - -/* Curl_init_CONNECT() gets called each time the handle switches to CONNECT - which means this gets called once for each subsequent redirect etc */ -void Curl_init_CONNECT(struct Curl_easy *data) -{ - data->state.fread_func = data->set.fread_func_set; - data->state.in = data->set.in_set; -} - -/* - * Curl_pretransfer() is called immediately before a transfer starts, and only - * once for one transfer no matter if it has redirects or do multi-pass - * authentication etc. - */ -CURLcode Curl_pretransfer(struct Curl_easy *data) -{ - CURLcode result; - - if(!data->state.url && !data->set.uh) { - /* we can't do anything without URL */ - failf(data, "No URL set"); - return CURLE_URL_MALFORMAT; - } - - /* since the URL may have been redirected in a previous use of this handle */ - if(data->state.url_alloc) { - /* the already set URL is allocated, free it first! */ - Curl_safefree(data->state.url); - data->state.url_alloc = FALSE; - } - - if(!data->state.url && data->set.uh) { - CURLUcode uc; - free(data->set.str[STRING_SET_URL]); - uc = curl_url_get(data->set.uh, - CURLUPART_URL, &data->set.str[STRING_SET_URL], 0); - if(uc) { - failf(data, "No URL set"); - return CURLE_URL_MALFORMAT; - } - } - - data->state.prefer_ascii = data->set.prefer_ascii; - data->state.list_only = data->set.list_only; - data->state.httpreq = data->set.method; - data->state.url = data->set.str[STRING_SET_URL]; - - /* Init the SSL session ID cache here. We do it here since we want to do it - after the *_setopt() calls (that could specify the size of the cache) but - before any transfer takes place. */ - result = Curl_ssl_initsessions(data, data->set.general_ssl.max_ssl_sessions); - if(result) - return result; - - data->state.requests = 0; - data->state.followlocation = 0; /* reset the location-follow counter */ - data->state.this_is_a_follow = FALSE; /* reset this */ - data->state.errorbuf = FALSE; /* no error has occurred */ - data->state.httpwant = data->set.httpwant; - data->state.httpversion = 0; - data->state.authproblem = FALSE; - data->state.authhost.want = data->set.httpauth; - data->state.authproxy.want = data->set.proxyauth; - Curl_safefree(data->info.wouldredirect); - - if(data->state.httpreq == HTTPREQ_PUT) - data->state.infilesize = data->set.filesize; - else if((data->state.httpreq != HTTPREQ_GET) && - (data->state.httpreq != HTTPREQ_HEAD)) { - data->state.infilesize = data->set.postfieldsize; - if(data->set.postfields && (data->state.infilesize == -1)) - data->state.infilesize = (curl_off_t)strlen(data->set.postfields); - } - else - data->state.infilesize = 0; - -#ifndef CURL_DISABLE_COOKIES - /* If there is a list of cookie files to read, do it now! */ - if(data->state.cookielist) - Curl_cookie_loadfiles(data); -#endif - /* If there is a list of host pairs to deal with */ - if(data->state.resolve) - result = Curl_loadhostpairs(data); - - if(!result) { - /* Allow data->set.use_port to set which port to use. This needs to be - * disabled for example when we follow Location: headers to URLs using - * different ports! */ - data->state.allow_port = TRUE; - -#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL) - /************************************************************* - * Tell signal handler to ignore SIGPIPE - *************************************************************/ - if(!data->set.no_signal) - data->state.prev_signal = signal(SIGPIPE, SIG_IGN); -#endif - - Curl_initinfo(data); /* reset session-specific information "variables" */ - Curl_pgrsResetTransferSizes(data); - Curl_pgrsStartNow(data); - - /* In case the handle is re-used and an authentication method was picked - in the session we need to make sure we only use the one(s) we now - consider to be fine */ - data->state.authhost.picked &= data->state.authhost.want; - data->state.authproxy.picked &= data->state.authproxy.want; - -#ifndef CURL_DISABLE_FTP - data->state.wildcardmatch = data->set.wildcard_enabled; - if(data->state.wildcardmatch) { - struct WildcardData *wc = &data->wildcard; - if(wc->state < CURLWC_INIT) { - result = Curl_wildcard_init(wc); /* init wildcard structures */ - if(result) - return CURLE_OUT_OF_MEMORY; - } - } -#endif - Curl_http2_init_state(&data->state); - result = Curl_hsts_loadcb(data, data->hsts); - } - - /* - * Set user-agent. Used for HTTP, but since we can attempt to tunnel - * basically anything through a http proxy we can't limit this based on - * protocol. - */ - if(data->set.str[STRING_USERAGENT]) { - Curl_safefree(data->state.aptr.uagent); - data->state.aptr.uagent = - aprintf("User-Agent: %s\r\n", data->set.str[STRING_USERAGENT]); - if(!data->state.aptr.uagent) - return CURLE_OUT_OF_MEMORY; - } - - if(!result) - result = Curl_setstropt(&data->state.aptr.user, - data->set.str[STRING_USERNAME]); - if(!result) - result = Curl_setstropt(&data->state.aptr.passwd, - data->set.str[STRING_PASSWORD]); - if(!result) - result = Curl_setstropt(&data->state.aptr.proxyuser, - data->set.str[STRING_PROXYUSERNAME]); - if(!result) - result = Curl_setstropt(&data->state.aptr.proxypasswd, - data->set.str[STRING_PROXYPASSWORD]); - - data->req.headerbytecount = 0; - Curl_headers_cleanup(data); - return result; -} - -/* - * Curl_posttransfer() is called immediately after a transfer ends - */ -CURLcode Curl_posttransfer(struct Curl_easy *data) -{ -#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL) - /* restore the signal handler for SIGPIPE before we get back */ - if(!data->set.no_signal) - signal(SIGPIPE, data->state.prev_signal); -#else - (void)data; /* unused parameter */ -#endif - - return CURLE_OK; -} - -/* - * Curl_follow() handles the URL redirect magic. Pass in the 'newurl' string - * as given by the remote server and set up the new URL to request. - * - * This function DOES NOT FREE the given url. - */ -CURLcode Curl_follow(struct Curl_easy *data, - char *newurl, /* the Location: string */ - followtype type) /* see transfer.h */ -{ -#ifdef CURL_DISABLE_HTTP - (void)data; - (void)newurl; - (void)type; - /* Location: following will not happen when HTTP is disabled */ - return CURLE_TOO_MANY_REDIRECTS; -#else - - /* Location: redirect */ - bool disallowport = FALSE; - bool reachedmax = FALSE; - CURLUcode uc; - - DEBUGASSERT(type != FOLLOW_NONE); - - if(type != FOLLOW_FAKE) - data->state.requests++; /* count all real follows */ - if(type == FOLLOW_REDIR) { - if((data->set.maxredirs != -1) && - (data->state.followlocation >= data->set.maxredirs)) { - reachedmax = TRUE; - type = FOLLOW_FAKE; /* switch to fake to store the would-be-redirected - to URL */ - } - else { - /* mark the next request as a followed location: */ - data->state.this_is_a_follow = TRUE; - - data->state.followlocation++; /* count location-followers */ - - if(data->set.http_auto_referer) { - CURLU *u; - char *referer = NULL; - - /* We are asked to automatically set the previous URL as the referer - when we get the next URL. We pick the ->url field, which may or may - not be 100% correct */ - - if(data->state.referer_alloc) { - Curl_safefree(data->state.referer); - data->state.referer_alloc = FALSE; - } - - /* Make a copy of the URL without credentials and fragment */ - u = curl_url(); - if(!u) - return CURLE_OUT_OF_MEMORY; - - uc = curl_url_set(u, CURLUPART_URL, data->state.url, 0); - if(!uc) - uc = curl_url_set(u, CURLUPART_FRAGMENT, NULL, 0); - if(!uc) - uc = curl_url_set(u, CURLUPART_USER, NULL, 0); - if(!uc) - uc = curl_url_set(u, CURLUPART_PASSWORD, NULL, 0); - if(!uc) - uc = curl_url_get(u, CURLUPART_URL, &referer, 0); - - curl_url_cleanup(u); - - if(uc || !referer) - return CURLE_OUT_OF_MEMORY; - - data->state.referer = referer; - data->state.referer_alloc = TRUE; /* yes, free this later */ - } - } - } - - if((type != FOLLOW_RETRY) && - (data->req.httpcode != 401) && (data->req.httpcode != 407) && - Curl_is_absolute_url(newurl, NULL, 0, FALSE)) - /* If this is not redirect due to a 401 or 407 response and an absolute - URL: don't allow a custom port number */ - disallowport = TRUE; - - DEBUGASSERT(data->state.uh); - uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl, - (type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME : - ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) | - CURLU_ALLOW_SPACE | - (data->set.path_as_is ? CURLU_PATH_AS_IS : 0)); - if(uc) { - if(type != FOLLOW_FAKE) { - failf(data, "The redirect target URL could not be parsed: %s", - curl_url_strerror(uc)); - return Curl_uc_to_curlcode(uc); - } - - /* the URL could not be parsed for some reason, but since this is FAKE - mode, just duplicate the field as-is */ - newurl = strdup(newurl); - if(!newurl) - return CURLE_OUT_OF_MEMORY; - } - else { - uc = curl_url_get(data->state.uh, CURLUPART_URL, &newurl, 0); - if(uc) - return Curl_uc_to_curlcode(uc); - - /* Clear auth if this redirects to a different port number or protocol, - unless permitted */ - if(!data->set.allow_auth_to_other_hosts && (type != FOLLOW_FAKE)) { - char *portnum; - int port; - bool clear = FALSE; - - if(data->set.use_port && data->state.allow_port) - /* a custom port is used */ - port = (int)data->set.use_port; - else { - uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum, - CURLU_DEFAULT_PORT); - if(uc) { - free(newurl); - return Curl_uc_to_curlcode(uc); - } - port = atoi(portnum); - free(portnum); - } - if(port != data->info.conn_remote_port) { - infof(data, "Clear auth, redirects to port from %u to %u", - data->info.conn_remote_port, port); - clear = TRUE; - } - else { - char *scheme; - const struct Curl_handler *p; - uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0); - if(uc) { - free(newurl); - return Curl_uc_to_curlcode(uc); - } - - p = Curl_builtin_scheme(scheme, CURL_ZERO_TERMINATED); - if(p && (p->protocol != data->info.conn_protocol)) { - infof(data, "Clear auth, redirects scheme from %s to %s", - data->info.conn_scheme, scheme); - clear = TRUE; - } - free(scheme); - } - if(clear) { - Curl_safefree(data->state.aptr.user); - Curl_safefree(data->state.aptr.passwd); - } - } - } - - if(type == FOLLOW_FAKE) { - /* we're only figuring out the new url if we would've followed locations - but now we're done so we can get out! */ - data->info.wouldredirect = newurl; - - if(reachedmax) { - failf(data, "Maximum (%ld) redirects followed", data->set.maxredirs); - return CURLE_TOO_MANY_REDIRECTS; - } - return CURLE_OK; - } - - if(disallowport) - data->state.allow_port = FALSE; - - if(data->state.url_alloc) - Curl_safefree(data->state.url); - - data->state.url = newurl; - data->state.url_alloc = TRUE; - - infof(data, "Issue another request to this URL: '%s'", data->state.url); - - /* - * We get here when the HTTP code is 300-399 (and 401). We need to perform - * differently based on exactly what return code there was. - * - * News from 7.10.6: we can also get here on a 401 or 407, in case we act on - * a HTTP (proxy-) authentication scheme other than Basic. - */ - switch(data->info.httpcode) { - /* 401 - Act on a WWW-Authenticate, we keep on moving and do the - Authorization: XXXX header in the HTTP request code snippet */ - /* 407 - Act on a Proxy-Authenticate, we keep on moving and do the - Proxy-Authorization: XXXX header in the HTTP request code snippet */ - /* 300 - Multiple Choices */ - /* 306 - Not used */ - /* 307 - Temporary Redirect */ - default: /* for all above (and the unknown ones) */ - /* Some codes are explicitly mentioned since I've checked RFC2616 and they - * seem to be OK to POST to. - */ - break; - case 301: /* Moved Permanently */ - /* (quote from RFC7231, section 6.4.2) - * - * Note: For historical reasons, a user agent MAY change the request - * method from POST to GET for the subsequent request. If this - * behavior is undesired, the 307 (Temporary Redirect) status code - * can be used instead. - * - * ---- - * - * Many webservers expect this, so these servers often answers to a POST - * request with an error page. To be sure that libcurl gets the page that - * most user agents would get, libcurl has to force GET. - * - * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and - * can be overridden with CURLOPT_POSTREDIR. - */ - if((data->state.httpreq == HTTPREQ_POST - || data->state.httpreq == HTTPREQ_POST_FORM - || data->state.httpreq == HTTPREQ_POST_MIME) - && !(data->set.keep_post & CURL_REDIR_POST_301)) { - infof(data, "Switch from POST to GET"); - data->state.httpreq = HTTPREQ_GET; - } - break; - case 302: /* Found */ - /* (quote from RFC7231, section 6.4.3) - * - * Note: For historical reasons, a user agent MAY change the request - * method from POST to GET for the subsequent request. If this - * behavior is undesired, the 307 (Temporary Redirect) status code - * can be used instead. - * - * ---- - * - * Many webservers expect this, so these servers often answers to a POST - * request with an error page. To be sure that libcurl gets the page that - * most user agents would get, libcurl has to force GET. - * - * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and - * can be overridden with CURLOPT_POSTREDIR. - */ - if((data->state.httpreq == HTTPREQ_POST - || data->state.httpreq == HTTPREQ_POST_FORM - || data->state.httpreq == HTTPREQ_POST_MIME) - && !(data->set.keep_post & CURL_REDIR_POST_302)) { - infof(data, "Switch from POST to GET"); - data->state.httpreq = HTTPREQ_GET; - } - break; - - case 303: /* See Other */ - /* 'See Other' location is not the resource but a substitute for the - * resource. In this case we switch the method to GET/HEAD, unless the - * method is POST and the user specified to keep it as POST. - * https://github.com/curl/curl/issues/5237#issuecomment-614641049 - */ - if(data->state.httpreq != HTTPREQ_GET && - ((data->state.httpreq != HTTPREQ_POST && - data->state.httpreq != HTTPREQ_POST_FORM && - data->state.httpreq != HTTPREQ_POST_MIME) || - !(data->set.keep_post & CURL_REDIR_POST_303))) { - data->state.httpreq = HTTPREQ_GET; - data->set.upload = false; - infof(data, "Switch to %s", - data->set.opt_no_body?"HEAD":"GET"); - } - break; - case 304: /* Not Modified */ - /* 304 means we did a conditional request and it was "Not modified". - * We shouldn't get any Location: header in this response! - */ - break; - case 305: /* Use Proxy */ - /* (quote from RFC2616, section 10.3.6): - * "The requested resource MUST be accessed through the proxy given - * by the Location field. The Location field gives the URI of the - * proxy. The recipient is expected to repeat this single request - * via the proxy. 305 responses MUST only be generated by origin - * servers." - */ - break; - } - Curl_pgrsTime(data, TIMER_REDIRECT); - Curl_pgrsResetTransferSizes(data); - - return CURLE_OK; -#endif /* CURL_DISABLE_HTTP */ -} - -/* Returns CURLE_OK *and* sets '*url' if a request retry is wanted. - - NOTE: that the *url is malloc()ed. */ -CURLcode Curl_retry_request(struct Curl_easy *data, char **url) -{ - struct connectdata *conn = data->conn; - bool retry = FALSE; - *url = NULL; - - /* if we're talking upload, we can't do the checks below, unless the protocol - is HTTP as when uploading over HTTP we will still get a response */ - if(data->set.upload && - !(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP))) - return CURLE_OK; - - if((data->req.bytecount + data->req.headerbytecount == 0) && - conn->bits.reuse && - (!data->set.opt_no_body || (conn->handler->protocol & PROTO_FAMILY_HTTP)) -#ifndef CURL_DISABLE_RTSP - && (data->set.rtspreq != RTSPREQ_RECEIVE) -#endif - ) - /* We got no data, we attempted to re-use a connection. For HTTP this - can be a retry so we try again regardless if we expected a body. - For other protocols we only try again only if we expected a body. - - This might happen if the connection was left alive when we were - done using it before, but that was closed when we wanted to read from - it again. Bad luck. Retry the same request on a fresh connect! */ - retry = TRUE; - else if(data->state.refused_stream && - (data->req.bytecount + data->req.headerbytecount == 0) ) { - /* This was sent on a refused stream, safe to rerun. A refused stream - error can typically only happen on HTTP/2 level if the stream is safe - to issue again, but the nghttp2 API can deliver the message to other - streams as well, which is why this adds the check the data counters - too. */ - infof(data, "REFUSED_STREAM, retrying a fresh connect"); - data->state.refused_stream = FALSE; /* clear again */ - retry = TRUE; - } - if(retry) { -#define CONN_MAX_RETRIES 5 - if(data->state.retrycount++ >= CONN_MAX_RETRIES) { - failf(data, "Connection died, tried %d times before giving up", - CONN_MAX_RETRIES); - data->state.retrycount = 0; - return CURLE_SEND_ERROR; - } - infof(data, "Connection died, retrying a fresh connect (retry count: %d)", - data->state.retrycount); - *url = strdup(data->state.url); - if(!*url) - return CURLE_OUT_OF_MEMORY; - - connclose(conn, "retry"); /* close this connection */ - conn->bits.retry = TRUE; /* mark this as a connection we're about - to retry. Marking it this way should - prevent i.e HTTP transfers to return - error just because nothing has been - transferred! */ - - - if(conn->handler->protocol&PROTO_FAMILY_HTTP) { - if(data->req.writebytecount) { - CURLcode result = Curl_readrewind(data); - if(result) { - Curl_safefree(*url); - return result; - } - } - } - } - return CURLE_OK; -} - -/* - * Curl_setup_transfer() is called to setup some basic properties for the - * upcoming transfer. - */ -void -Curl_setup_transfer( - struct Curl_easy *data, /* transfer */ - int sockindex, /* socket index to read from or -1 */ - curl_off_t size, /* -1 if unknown at this point */ - bool getheader, /* TRUE if header parsing is wanted */ - int writesockindex /* socket index to write to, it may very well be - the same we read from. -1 disables */ - ) -{ - struct SingleRequest *k = &data->req; - struct connectdata *conn = data->conn; - struct HTTP *http = data->req.p.http; - bool httpsending; - - DEBUGASSERT(conn != NULL); - DEBUGASSERT((sockindex <= 1) && (sockindex >= -1)); - - httpsending = ((conn->handler->protocol&PROTO_FAMILY_HTTP) && - (http->sending == HTTPSEND_REQUEST)); - - if(conn->bits.multiplex || conn->httpversion == 20 || httpsending) { - /* when multiplexing, the read/write sockets need to be the same! */ - conn->sockfd = sockindex == -1 ? - ((writesockindex == -1 ? CURL_SOCKET_BAD : conn->sock[writesockindex])) : - conn->sock[sockindex]; - conn->writesockfd = conn->sockfd; - if(httpsending) - /* special and very HTTP-specific */ - writesockindex = FIRSTSOCKET; - } - else { - conn->sockfd = sockindex == -1 ? - CURL_SOCKET_BAD : conn->sock[sockindex]; - conn->writesockfd = writesockindex == -1 ? - CURL_SOCKET_BAD:conn->sock[writesockindex]; - } - k->getheader = getheader; - - k->size = size; - - /* The code sequence below is placed in this function just because all - necessary input is not always known in do_complete() as this function may - be called after that */ - - if(!k->getheader) { - k->header = FALSE; - if(size > 0) - Curl_pgrsSetDownloadSize(data, size); - } - /* we want header and/or body, if neither then don't do this! */ - if(k->getheader || !data->set.opt_no_body) { - - if(sockindex != -1) - k->keepon |= KEEP_RECV; - - if(writesockindex != -1) { - /* HTTP 1.1 magic: - - Even if we require a 100-return code before uploading data, we might - need to write data before that since the REQUEST may not have been - finished sent off just yet. - - Thus, we must check if the request has been sent before we set the - state info where we wait for the 100-return code - */ - if((data->state.expect100header) && - (conn->handler->protocol&PROTO_FAMILY_HTTP) && - (http->sending == HTTPSEND_BODY)) { - /* wait with write until we either got 100-continue or a timeout */ - k->exp100 = EXP100_AWAITING_CONTINUE; - k->start100 = Curl_now(); - - /* Set a timeout for the multi interface. Add the inaccuracy margin so - that we don't fire slightly too early and get denied to run. */ - Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT); - } - else { - if(data->state.expect100header) - /* when we've sent off the rest of the headers, we must await a - 100-continue but first finish sending the request */ - k->exp100 = EXP100_SENDING_REQUEST; - - /* enable the write bit when we're not waiting for continue */ - k->keepon |= KEEP_SEND; - } - } /* if(writesockindex != -1) */ - } /* if(k->getheader || !data->set.opt_no_body) */ - -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "strtoofft.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 HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#elif defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#ifndef HAVE_SOCKET
+#error "We can't compile without socket() support!"
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "netrc.h"
+
+#include "content_encoding.h"
+#include "hostip.h"
+#include "cfilters.h"
+#include "transfer.h"
+#include "sendf.h"
+#include "speedcheck.h"
+#include "progress.h"
+#include "http.h"
+#include "url.h"
+#include "getinfo.h"
+#include "vtls/vtls.h"
+#include "select.h"
+#include "multiif.h"
+#include "connect.h"
+#include "http2.h"
+#include "mime.h"
+#include "strcase.h"
+#include "urlapi-int.h"
+#include "hsts.h"
+#include "setopt.h"
+#include "headers.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \
+ !defined(CURL_DISABLE_IMAP)
+/*
+ * checkheaders() checks the linked list of custom headers for a
+ * particular header (prefix). Provide the prefix without colon!
+ *
+ * Returns a pointer to the first matching header or NULL if none matched.
+ */
+char *Curl_checkheaders(const struct Curl_easy *data,
+ const char *thisheader,
+ const size_t thislen)
+{
+ struct curl_slist *head;
+ DEBUGASSERT(thislen);
+ DEBUGASSERT(thisheader[thislen-1] != ':');
+
+ for(head = data->set.headers; head; head = head->next) {
+ if(strncasecompare(head->data, thisheader, thislen) &&
+ Curl_headersep(head->data[thislen]) )
+ return head->data;
+ }
+
+ return NULL;
+}
+#endif
+
+CURLcode Curl_get_upload_buffer(struct Curl_easy *data)
+{
+ if(!data->state.ulbuf) {
+ data->state.ulbuf = malloc(data->set.upload_buffer_size);
+ if(!data->state.ulbuf)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ return CURLE_OK;
+}
+
+#ifndef CURL_DISABLE_HTTP
+/*
+ * This function will be called to loop through the trailers buffer
+ * until no more data is available for sending.
+ */
+static size_t trailers_read(char *buffer, size_t size, size_t nitems,
+ void *raw)
+{
+ struct Curl_easy *data = (struct Curl_easy *)raw;
+ struct dynbuf *trailers_buf = &data->state.trailers_buf;
+ size_t bytes_left = Curl_dyn_len(trailers_buf) -
+ data->state.trailers_bytes_sent;
+ size_t to_copy = (size*nitems < bytes_left) ? size*nitems : bytes_left;
+ if(to_copy) {
+ memcpy(buffer,
+ Curl_dyn_ptr(trailers_buf) + data->state.trailers_bytes_sent,
+ to_copy);
+ data->state.trailers_bytes_sent += to_copy;
+ }
+ return to_copy;
+}
+
+static size_t trailers_left(void *raw)
+{
+ struct Curl_easy *data = (struct Curl_easy *)raw;
+ struct dynbuf *trailers_buf = &data->state.trailers_buf;
+ return Curl_dyn_len(trailers_buf) - data->state.trailers_bytes_sent;
+}
+#endif
+
+/*
+ * This function will call the read callback to fill our buffer with data
+ * to upload.
+ */
+CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
+ size_t *nreadp)
+{
+ size_t buffersize = bytes;
+ size_t nread;
+
+ curl_read_callback readfunc = NULL;
+ void *extra_data = NULL;
+
+#ifndef CURL_DISABLE_HTTP
+ if(data->state.trailers_state == TRAILERS_INITIALIZED) {
+ struct curl_slist *trailers = NULL;
+ CURLcode result;
+ int trailers_ret_code;
+
+ /* at this point we already verified that the callback exists
+ so we compile and store the trailers buffer, then proceed */
+ infof(data,
+ "Moving trailers state machine from initialized to sending.");
+ data->state.trailers_state = TRAILERS_SENDING;
+ Curl_dyn_init(&data->state.trailers_buf, DYN_TRAILERS);
+
+ data->state.trailers_bytes_sent = 0;
+ Curl_set_in_callback(data, true);
+ trailers_ret_code = data->set.trailer_callback(&trailers,
+ data->set.trailer_data);
+ Curl_set_in_callback(data, false);
+ if(trailers_ret_code == CURL_TRAILERFUNC_OK) {
+ result = Curl_http_compile_trailers(trailers, &data->state.trailers_buf,
+ data);
+ }
+ else {
+ failf(data, "operation aborted by trailing headers callback");
+ *nreadp = 0;
+ result = CURLE_ABORTED_BY_CALLBACK;
+ }
+ if(result) {
+ Curl_dyn_free(&data->state.trailers_buf);
+ curl_slist_free_all(trailers);
+ return result;
+ }
+ infof(data, "Successfully compiled trailers.");
+ curl_slist_free_all(trailers);
+ }
+#endif
+
+#ifndef CURL_DISABLE_HTTP
+ /* if we are transmitting trailing data, we don't need to write
+ a chunk size so we skip this */
+ if(data->req.upload_chunky &&
+ data->state.trailers_state == TRAILERS_NONE) {
+ /* if chunked Transfer-Encoding */
+ buffersize -= (8 + 2 + 2); /* 32bit hex + CRLF + CRLF */
+ data->req.upload_fromhere += (8 + 2); /* 32bit hex + CRLF */
+ }
+
+ if(data->state.trailers_state == TRAILERS_SENDING) {
+ /* if we're here then that means that we already sent the last empty chunk
+ but we didn't send a final CR LF, so we sent 0 CR LF. We then start
+ pulling trailing data until we have no more at which point we
+ simply return to the previous point in the state machine as if
+ nothing happened.
+ */
+ readfunc = trailers_read;
+ extra_data = (void *)data;
+ }
+ else
+#endif
+ {
+ readfunc = data->state.fread_func;
+ extra_data = data->state.in;
+ }
+
+ Curl_set_in_callback(data, true);
+ nread = readfunc(data->req.upload_fromhere, 1,
+ buffersize, extra_data);
+ Curl_set_in_callback(data, false);
+
+ if(nread == CURL_READFUNC_ABORT) {
+ failf(data, "operation aborted by callback");
+ *nreadp = 0;
+ return CURLE_ABORTED_BY_CALLBACK;
+ }
+ if(nread == CURL_READFUNC_PAUSE) {
+ struct SingleRequest *k = &data->req;
+
+ if(data->conn->handler->flags & PROTOPT_NONETWORK) {
+ /* protocols that work without network cannot be paused. This is
+ actually only FILE:// just now, and it can't pause since the transfer
+ isn't done using the "normal" procedure. */
+ failf(data, "Read callback asked for PAUSE when not supported");
+ return CURLE_READ_ERROR;
+ }
+
+ /* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */
+ k->keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */
+ if(data->req.upload_chunky) {
+ /* Back out the preallocation done above */
+ data->req.upload_fromhere -= (8 + 2);
+ }
+ *nreadp = 0;
+
+ return CURLE_OK; /* nothing was read */
+ }
+ else if(nread > buffersize) {
+ /* the read function returned a too large value */
+ *nreadp = 0;
+ failf(data, "read function returned funny value");
+ return CURLE_READ_ERROR;
+ }
+
+#ifndef CURL_DISABLE_HTTP
+ if(!data->req.forbidchunk && data->req.upload_chunky) {
+ /* if chunked Transfer-Encoding
+ * build chunk:
+ *
+ * <HEX SIZE> CRLF
+ * <DATA> CRLF
+ */
+ /* On non-ASCII platforms the <DATA> may or may not be
+ translated based on state.prefer_ascii while the protocol
+ portion must always be translated to the network encoding.
+ To further complicate matters, line end conversion might be
+ done later on, so we need to prevent CRLFs from becoming
+ CRCRLFs if that's the case. To do this we use bare LFs
+ here, knowing they'll become CRLFs later on.
+ */
+
+ bool added_crlf = FALSE;
+ int hexlen = 0;
+ const char *endofline_native;
+ const char *endofline_network;
+
+ if(
+#ifdef CURL_DO_LINEEND_CONV
+ (data->state.prefer_ascii) ||
+#endif
+ (data->set.crlf)) {
+ /* \n will become \r\n later on */
+ endofline_native = "\n";
+ endofline_network = "\x0a";
+ }
+ else {
+ endofline_native = "\r\n";
+ endofline_network = "\x0d\x0a";
+ }
+
+ /* if we're not handling trailing data, proceed as usual */
+ if(data->state.trailers_state != TRAILERS_SENDING) {
+ char hexbuffer[11] = "";
+ hexlen = msnprintf(hexbuffer, sizeof(hexbuffer),
+ "%zx%s", nread, endofline_native);
+
+ /* move buffer pointer */
+ data->req.upload_fromhere -= hexlen;
+ nread += hexlen;
+
+ /* copy the prefix to the buffer, leaving out the NUL */
+ memcpy(data->req.upload_fromhere, hexbuffer, hexlen);
+
+ /* always append ASCII CRLF to the data unless
+ we have a valid trailer callback */
+ if((nread-hexlen) == 0 &&
+ data->set.trailer_callback != NULL &&
+ data->state.trailers_state == TRAILERS_NONE) {
+ data->state.trailers_state = TRAILERS_INITIALIZED;
+ }
+ else {
+ memcpy(data->req.upload_fromhere + nread,
+ endofline_network,
+ strlen(endofline_network));
+ added_crlf = TRUE;
+ }
+ }
+
+ if(data->state.trailers_state == TRAILERS_SENDING &&
+ !trailers_left(data)) {
+ Curl_dyn_free(&data->state.trailers_buf);
+ data->state.trailers_state = TRAILERS_DONE;
+ data->set.trailer_data = NULL;
+ data->set.trailer_callback = NULL;
+ /* mark the transfer as done */
+ data->req.upload_done = TRUE;
+ infof(data, "Signaling end of chunked upload after trailers.");
+ }
+ else
+ if((nread - hexlen) == 0 &&
+ data->state.trailers_state != TRAILERS_INITIALIZED) {
+ /* mark this as done once this chunk is transferred */
+ data->req.upload_done = TRUE;
+ infof(data,
+ "Signaling end of chunked upload via terminating chunk.");
+ }
+
+ if(added_crlf)
+ nread += strlen(endofline_network); /* for the added end of line */
+ }
+#endif
+
+ *nreadp = nread;
+
+ return CURLE_OK;
+}
+
+static int data_pending(struct Curl_easy *data)
+{
+ struct connectdata *conn = data->conn;
+
+#ifdef ENABLE_QUIC
+ if(conn->transport == TRNSPRT_QUIC)
+ return Curl_quic_data_pending(data);
+#endif
+
+ if(conn->handler->protocol&PROTO_FAMILY_FTP)
+ return Curl_conn_data_pending(data, SECONDARYSOCKET);
+
+ /* in the case of libssh2, we can never be really sure that we have emptied
+ its internal buffers so we MUST always try until we get EAGAIN back */
+ return conn->handler->protocol&(CURLPROTO_SCP|CURLPROTO_SFTP) ||
+#ifdef USE_NGHTTP2
+ /* For HTTP/2, we may read up everything including response body
+ with header fields in Curl_http_readwrite_headers. If no
+ content-length is provided, curl waits for the connection
+ close, which we emulate it using conn->proto.httpc.closed =
+ TRUE. The thing is if we read everything, then http2_recv won't
+ be called and we cannot signal the HTTP/2 stream has closed. As
+ a workaround, we return nonzero here to call http2_recv. */
+ ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion >= 20) ||
+#endif
+ Curl_conn_data_pending(data, FIRSTSOCKET);
+}
+
+/*
+ * Check to see if CURLOPT_TIMECONDITION was met by comparing the time of the
+ * remote document with the time provided by CURLOPT_TIMEVAL
+ */
+bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc)
+{
+ if((timeofdoc == 0) || (data->set.timevalue == 0))
+ return TRUE;
+
+ switch(data->set.timecondition) {
+ case CURL_TIMECOND_IFMODSINCE:
+ default:
+ if(timeofdoc <= data->set.timevalue) {
+ infof(data,
+ "The requested document is not new enough");
+ data->info.timecond = TRUE;
+ return FALSE;
+ }
+ break;
+ case CURL_TIMECOND_IFUNMODSINCE:
+ if(timeofdoc >= data->set.timevalue) {
+ infof(data,
+ "The requested document is not old enough");
+ data->info.timecond = TRUE;
+ return FALSE;
+ }
+ break;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Go ahead and do a read if we have a readable socket or if
+ * the stream was rewound (in which case we have data in a
+ * buffer)
+ *
+ * return '*comeback' TRUE if we didn't properly drain the socket so this
+ * function should get called again without select() or similar in between!
+ */
+static CURLcode readwrite_data(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct SingleRequest *k,
+ int *didwhat, bool *done,
+ bool *comeback)
+{
+ CURLcode result = CURLE_OK;
+ ssize_t nread; /* number of bytes read */
+ size_t excess = 0; /* excess bytes read */
+ bool readmore = FALSE; /* used by RTP to signal for more data */
+ int maxloops = 100;
+ char *buf = data->state.buffer;
+ DEBUGASSERT(buf);
+
+ *done = FALSE;
+ *comeback = FALSE;
+
+ /* This is where we loop until we have read everything there is to
+ read or we get a CURLE_AGAIN */
+ do {
+ bool is_empty_data = FALSE;
+ size_t buffersize = data->set.buffer_size;
+ size_t bytestoread = buffersize;
+#ifdef USE_NGHTTP2
+ bool is_http2 = ((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
+ (conn->httpversion == 20));
+#endif
+ bool is_http3 =
+#ifdef ENABLE_QUIC
+ ((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
+ (conn->httpversion == 30));
+#else
+ FALSE;
+#endif
+
+ if(
+#ifdef USE_NGHTTP2
+ /* For HTTP/2, read data without caring about the content length. This
+ is safe because body in HTTP/2 is always segmented thanks to its
+ framing layer. Meanwhile, we have to call Curl_read to ensure that
+ http2_handle_stream_close is called when we read all incoming bytes
+ for a particular stream. */
+ !is_http2 &&
+#endif
+ !is_http3 && /* Same reason mentioned above. */
+ k->size != -1 && !k->header) {
+ /* make sure we don't read too much */
+ curl_off_t totalleft = k->size - k->bytecount;
+ if(totalleft < (curl_off_t)bytestoread)
+ bytestoread = (size_t)totalleft;
+ }
+
+ if(bytestoread) {
+ /* receive data from the network! */
+ result = Curl_read(data, conn->sockfd, buf, bytestoread, &nread);
+
+ /* read would've blocked */
+ if(CURLE_AGAIN == result) {
+ result = CURLE_OK;
+ break; /* get out of loop */
+ }
+
+ if(result>0)
+ goto out;
+ }
+ else {
+ /* read nothing but since we wanted nothing we consider this an OK
+ situation to proceed from */
+ DEBUGF(infof(data, "readwrite_data: we're done"));
+ nread = 0;
+ }
+
+ if(!k->bytecount) {
+ Curl_pgrsTime(data, TIMER_STARTTRANSFER);
+ if(k->exp100 > EXP100_SEND_DATA)
+ /* set time stamp to compare with when waiting for the 100 */
+ k->start100 = Curl_now();
+ }
+
+ *didwhat |= KEEP_RECV;
+ /* indicates data of zero size, i.e. empty file */
+ is_empty_data = ((nread == 0) && (k->bodywrites == 0)) ? TRUE : FALSE;
+
+ if(0 < nread || is_empty_data) {
+ buf[nread] = 0;
+ }
+ else {
+ /* if we receive 0 or less here, either the http2 stream is closed or the
+ server closed the connection and we bail out from this! */
+#ifdef USE_NGHTTP2
+ if(is_http2 && !nread)
+ DEBUGF(infof(data, "nread == 0, stream closed, bailing"));
+ else
+#endif
+ if(is_http3 && !nread)
+ DEBUGF(infof(data, "nread == 0, stream closed, bailing"));
+ else
+ DEBUGF(infof(data, "nread <= 0, server closed connection, bailing"));
+ k->keepon &= ~KEEP_RECV;
+ break;
+ }
+
+ /* Default buffer to use when we write the buffer, it may be changed
+ in the flow below before the actual storing is done. */
+ k->str = buf;
+
+ if(conn->handler->readwrite) {
+ result = conn->handler->readwrite(data, conn, &nread, &readmore);
+ if(result)
+ goto out;
+ if(readmore)
+ break;
+ }
+
+#ifndef CURL_DISABLE_HTTP
+ /* Since this is a two-state thing, we check if we are parsing
+ headers at the moment or not. */
+ if(k->header) {
+ /* we are in parse-the-header-mode */
+ bool stop_reading = FALSE;
+ result = Curl_http_readwrite_headers(data, conn, &nread, &stop_reading);
+ if(result)
+ goto out;
+
+ if(conn->handler->readwrite &&
+ (k->maxdownload <= 0 && nread > 0)) {
+ result = conn->handler->readwrite(data, conn, &nread, &readmore);
+ if(result)
+ goto out;
+ if(readmore)
+ break;
+ }
+
+ if(stop_reading) {
+ /* We've stopped dealing with input, get out of the do-while loop */
+
+ if(nread > 0) {
+ infof(data,
+ "Excess found:"
+ " excess = %zd"
+ " url = %s (zero-length body)",
+ nread, data->state.up.path);
+ }
+
+ break;
+ }
+ }
+#endif /* CURL_DISABLE_HTTP */
+
+
+ /* This is not an 'else if' since it may be a rest from the header
+ parsing, where the beginning of the buffer is headers and the end
+ is non-headers. */
+ if(!k->header && (nread > 0 || is_empty_data)) {
+
+ if(data->req.no_body) {
+ /* data arrives although we want none, bail out */
+ streamclose(conn, "ignoring body");
+ *done = TRUE;
+ result = CURLE_WEIRD_SERVER_REPLY;
+ goto out;
+ }
+
+#ifndef CURL_DISABLE_HTTP
+ if(0 == k->bodywrites && !is_empty_data) {
+ /* These checks are only made the first time we are about to
+ write a piece of the body */
+ if(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) {
+ /* HTTP-only checks */
+ result = Curl_http_firstwrite(data, conn, done);
+ if(result || *done)
+ goto out;
+ }
+ } /* this is the first time we write a body part */
+#endif /* CURL_DISABLE_HTTP */
+
+ k->bodywrites++;
+
+ /* pass data to the debug function before it gets "dechunked" */
+ if(data->set.verbose) {
+ if(k->badheader) {
+ Curl_debug(data, CURLINFO_DATA_IN,
+ Curl_dyn_ptr(&data->state.headerb),
+ Curl_dyn_len(&data->state.headerb));
+ if(k->badheader == HEADER_PARTHEADER)
+ Curl_debug(data, CURLINFO_DATA_IN,
+ k->str, (size_t)nread);
+ }
+ else
+ Curl_debug(data, CURLINFO_DATA_IN,
+ k->str, (size_t)nread);
+ }
+
+#ifndef CURL_DISABLE_HTTP
+ if(k->chunk) {
+ /*
+ * Here comes a chunked transfer flying and we need to decode this
+ * properly. While the name says read, this function both reads
+ * and writes away the data. The returned 'nread' holds the number
+ * of actual data it wrote to the client.
+ */
+ CURLcode extra;
+ CHUNKcode res =
+ Curl_httpchunk_read(data, k->str, nread, &nread, &extra);
+
+ if(CHUNKE_OK < res) {
+ if(CHUNKE_PASSTHRU_ERROR == res) {
+ failf(data, "Failed reading the chunked-encoded stream");
+ result = extra;
+ goto out;
+ }
+ failf(data, "%s in chunked-encoding", Curl_chunked_strerror(res));
+ result = CURLE_RECV_ERROR;
+ goto out;
+ }
+ if(CHUNKE_STOP == res) {
+ /* we're done reading chunks! */
+ k->keepon &= ~KEEP_RECV; /* read no more */
+
+ /* N number of bytes at the end of the str buffer that weren't
+ written to the client. */
+ if(conn->chunk.datasize) {
+ infof(data, "Leftovers after chunking: % "
+ CURL_FORMAT_CURL_OFF_T "u bytes",
+ conn->chunk.datasize);
+ }
+ }
+ /* If it returned OK, we just keep going */
+ }
+#endif /* CURL_DISABLE_HTTP */
+
+ /* Account for body content stored in the header buffer */
+ if((k->badheader == HEADER_PARTHEADER) && !k->ignorebody) {
+ size_t headlen = Curl_dyn_len(&data->state.headerb);
+ DEBUGF(infof(data, "Increasing bytecount by %zu", headlen));
+ k->bytecount += headlen;
+ }
+
+ if((-1 != k->maxdownload) &&
+ (k->bytecount + nread >= k->maxdownload)) {
+
+ excess = (size_t)(k->bytecount + nread - k->maxdownload);
+ if(excess > 0 && !k->ignorebody) {
+ infof(data,
+ "Excess found in a read:"
+ " excess = %zu"
+ ", size = %" CURL_FORMAT_CURL_OFF_T
+ ", maxdownload = %" CURL_FORMAT_CURL_OFF_T
+ ", bytecount = %" CURL_FORMAT_CURL_OFF_T,
+ excess, k->size, k->maxdownload, k->bytecount);
+ connclose(conn, "excess found in a read");
+ }
+
+ nread = (ssize_t) (k->maxdownload - k->bytecount);
+ if(nread < 0) /* this should be unusual */
+ nread = 0;
+
+ /* HTTP/3 over QUIC should keep reading until QUIC connection
+ is closed. In contrast to HTTP/2 which can stop reading
+ from TCP connection, HTTP/3 over QUIC needs ACK from server
+ to ensure stream closure. It should keep reading. */
+ if(!is_http3) {
+ k->keepon &= ~KEEP_RECV; /* we're done reading */
+ }
+ }
+
+ k->bytecount += nread;
+
+ Curl_pgrsSetDownloadCounter(data, k->bytecount);
+
+ if(!k->chunk && (nread || k->badheader || is_empty_data)) {
+ /* If this is chunky transfer, it was already written */
+
+ if(k->badheader && !k->ignorebody) {
+ /* we parsed a piece of data wrongly assuming it was a header
+ and now we output it as body instead */
+ size_t headlen = Curl_dyn_len(&data->state.headerb);
+
+ /* Don't let excess data pollute body writes */
+ if(k->maxdownload == -1 || (curl_off_t)headlen <= k->maxdownload)
+ result = Curl_client_write(data, CLIENTWRITE_BODY,
+ Curl_dyn_ptr(&data->state.headerb),
+ headlen);
+ else
+ result = Curl_client_write(data, CLIENTWRITE_BODY,
+ Curl_dyn_ptr(&data->state.headerb),
+ (size_t)k->maxdownload);
+
+ if(result)
+ goto out;
+ }
+ if(k->badheader < HEADER_ALLBAD) {
+ /* This switch handles various content encodings. If there's an
+ error here, be sure to check over the almost identical code
+ in http_chunks.c.
+ Make sure that ALL_CONTENT_ENCODINGS contains all the
+ encodings handled here. */
+ if(data->set.http_ce_skip || !k->writer_stack) {
+ if(!k->ignorebody && nread) {
+#ifndef CURL_DISABLE_POP3
+ if(conn->handler->protocol & PROTO_FAMILY_POP3)
+ result = Curl_pop3_write(data, k->str, nread);
+ else
+#endif /* CURL_DISABLE_POP3 */
+ result = Curl_client_write(data, CLIENTWRITE_BODY, k->str,
+ nread);
+ }
+ }
+ else if(!k->ignorebody && nread)
+ result = Curl_unencode_write(data, k->writer_stack, k->str, nread);
+ }
+ k->badheader = HEADER_NORMAL; /* taken care of now */
+
+ if(result)
+ goto out;
+ }
+
+ } /* if(!header and data to read) */
+
+ if(conn->handler->readwrite && excess) {
+ /* Parse the excess data */
+ k->str += nread;
+
+ if(&k->str[excess] > &buf[data->set.buffer_size]) {
+ /* the excess amount was too excessive(!), make sure
+ it doesn't read out of buffer */
+ excess = &buf[data->set.buffer_size] - k->str;
+ }
+ nread = (ssize_t)excess;
+
+ result = conn->handler->readwrite(data, conn, &nread, &readmore);
+ if(result)
+ goto out;
+
+ if(readmore)
+ k->keepon |= KEEP_RECV; /* we're not done reading */
+ break;
+ }
+
+ if(is_empty_data) {
+ /* if we received nothing, the server closed the connection and we
+ are done */
+ k->keepon &= ~KEEP_RECV;
+ }
+
+ if(k->keepon & KEEP_RECV_PAUSE) {
+ /* this is a paused transfer */
+ break;
+ }
+
+ } while(data_pending(data) && maxloops--);
+
+ if(maxloops <= 0) {
+ /* we mark it as read-again-please */
+ conn->cselect_bits = CURL_CSELECT_IN;
+ *comeback = TRUE;
+ }
+
+ if(((k->keepon & (KEEP_RECV|KEEP_SEND)) == KEEP_SEND) &&
+ conn->bits.close) {
+ /* When we've read the entire thing and the close bit is set, the server
+ may now close the connection. If there's now any kind of sending going
+ on from our side, we need to stop that immediately. */
+ infof(data, "we are done reading and this is set to close, stop send");
+ k->keepon &= ~KEEP_SEND; /* no writing anymore either */
+ }
+
+out:
+ DEBUGF(infof(data, "readwrite_data(handle=%p) -> %d", data, result));
+ return result;
+}
+
+CURLcode Curl_done_sending(struct Curl_easy *data,
+ struct SingleRequest *k)
+{
+ struct connectdata *conn = data->conn;
+ k->keepon &= ~KEEP_SEND; /* we're done writing */
+
+ /* These functions should be moved into the handler struct! */
+ Curl_http2_done_sending(data, conn);
+ Curl_quic_done_sending(data);
+
+ return CURLE_OK;
+}
+
+#if defined(WIN32) && defined(USE_WINSOCK)
+#ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
+#define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B
+#endif
+
+static void win_update_buffer_size(curl_socket_t sockfd)
+{
+ int result;
+ ULONG ideal;
+ DWORD ideallen;
+ result = WSAIoctl(sockfd, SIO_IDEAL_SEND_BACKLOG_QUERY, 0, 0,
+ &ideal, sizeof(ideal), &ideallen, 0, 0);
+ if(result == 0) {
+ setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF,
+ (const char *)&ideal, sizeof(ideal));
+ }
+}
+#else
+#define win_update_buffer_size(x)
+#endif
+
+#define curl_upload_refill_watermark(data) \
+ ((ssize_t)((data)->set.upload_buffer_size >> 5))
+
+/*
+ * Send data to upload to the server, when the socket is writable.
+ */
+static CURLcode readwrite_upload(struct Curl_easy *data,
+ struct connectdata *conn,
+ int *didwhat)
+{
+ ssize_t i, si;
+ ssize_t bytes_written;
+ CURLcode result;
+ ssize_t nread; /* number of bytes read */
+ bool sending_http_headers = FALSE;
+ struct SingleRequest *k = &data->req;
+
+ if((k->bytecount == 0) && (k->writebytecount == 0))
+ Curl_pgrsTime(data, TIMER_STARTTRANSFER);
+
+ *didwhat |= KEEP_SEND;
+
+ do {
+ curl_off_t nbody;
+ ssize_t offset = 0;
+
+ if(0 != k->upload_present &&
+ k->upload_present < curl_upload_refill_watermark(data) &&
+ !k->upload_chunky &&/*(variable sized chunked header; append not safe)*/
+ !k->upload_done && /*!(k->upload_done once k->upload_present sent)*/
+ !(k->writebytecount + k->upload_present - k->pendingheader ==
+ data->state.infilesize)) {
+ offset = k->upload_present;
+ }
+
+ /* only read more data if there's no upload data already
+ present in the upload buffer, or if appending to upload buffer */
+ if(0 == k->upload_present || offset) {
+ result = Curl_get_upload_buffer(data);
+ if(result)
+ return result;
+ if(offset && k->upload_fromhere != data->state.ulbuf)
+ memmove(data->state.ulbuf, k->upload_fromhere, offset);
+ /* init the "upload from here" pointer */
+ k->upload_fromhere = data->state.ulbuf;
+
+ if(!k->upload_done) {
+ /* HTTP pollution, this should be written nicer to become more
+ protocol agnostic. */
+ size_t fillcount;
+ struct HTTP *http = k->p.http;
+
+ if((k->exp100 == EXP100_SENDING_REQUEST) &&
+ (http->sending == HTTPSEND_BODY)) {
+ /* If this call is to send body data, we must take some action:
+ We have sent off the full HTTP 1.1 request, and we shall now
+ go into the Expect: 100 state and await such a header */
+ k->exp100 = EXP100_AWAITING_CONTINUE; /* wait for the header */
+ k->keepon &= ~KEEP_SEND; /* disable writing */
+ k->start100 = Curl_now(); /* timeout count starts now */
+ *didwhat &= ~KEEP_SEND; /* we didn't write anything actually */
+ /* set a timeout for the multi interface */
+ Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
+ break;
+ }
+
+ if(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) {
+ if(http->sending == HTTPSEND_REQUEST)
+ /* We're sending the HTTP request headers, not the data.
+ Remember that so we don't change the line endings. */
+ sending_http_headers = TRUE;
+ else
+ sending_http_headers = FALSE;
+ }
+
+ k->upload_fromhere += offset;
+ result = Curl_fillreadbuffer(data, data->set.upload_buffer_size-offset,
+ &fillcount);
+ k->upload_fromhere -= offset;
+ if(result)
+ return result;
+
+ nread = offset + fillcount;
+ }
+ else
+ nread = 0; /* we're done uploading/reading */
+
+ if(!nread && (k->keepon & KEEP_SEND_PAUSE)) {
+ /* this is a paused transfer */
+ break;
+ }
+ if(nread <= 0) {
+ result = Curl_done_sending(data, k);
+ if(result)
+ return result;
+ break;
+ }
+
+ /* store number of bytes available for upload */
+ k->upload_present = nread;
+
+ /* convert LF to CRLF if so asked */
+ if((!sending_http_headers) && (
+#ifdef CURL_DO_LINEEND_CONV
+ /* always convert if we're FTPing in ASCII mode */
+ (data->state.prefer_ascii) ||
+#endif
+ (data->set.crlf))) {
+ /* Do we need to allocate a scratch buffer? */
+ if(!data->state.scratch) {
+ data->state.scratch = malloc(2 * data->set.upload_buffer_size);
+ if(!data->state.scratch) {
+ failf(data, "Failed to alloc scratch buffer");
+
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+
+ /*
+ * ASCII/EBCDIC Note: This is presumably a text (not binary)
+ * transfer so the data should already be in ASCII.
+ * That means the hex values for ASCII CR (0x0d) & LF (0x0a)
+ * must be used instead of the escape sequences \r & \n.
+ */
+ if(offset)
+ memcpy(data->state.scratch, k->upload_fromhere, offset);
+ for(i = offset, si = offset; i < nread; i++, si++) {
+ if(k->upload_fromhere[i] == 0x0a) {
+ data->state.scratch[si++] = 0x0d;
+ data->state.scratch[si] = 0x0a;
+ if(!data->set.crlf) {
+ /* we're here only because FTP is in ASCII mode...
+ bump infilesize for the LF we just added */
+ if(data->state.infilesize != -1)
+ data->state.infilesize++;
+ }
+ }
+ else
+ data->state.scratch[si] = k->upload_fromhere[i];
+ }
+
+ if(si != nread) {
+ /* only perform the special operation if we really did replace
+ anything */
+ nread = si;
+
+ /* upload from the new (replaced) buffer instead */
+ k->upload_fromhere = data->state.scratch;
+
+ /* set the new amount too */
+ k->upload_present = nread;
+ }
+ }
+
+#ifndef CURL_DISABLE_SMTP
+ if(conn->handler->protocol & PROTO_FAMILY_SMTP) {
+ result = Curl_smtp_escape_eob(data, nread, offset);
+ if(result)
+ return result;
+ }
+#endif /* CURL_DISABLE_SMTP */
+ } /* if 0 == k->upload_present or appended to upload buffer */
+ else {
+ /* We have a partial buffer left from a previous "round". Use
+ that instead of reading more data */
+ }
+
+ /* write to socket (send away data) */
+ result = Curl_write(data,
+ conn->writesockfd, /* socket to send to */
+ k->upload_fromhere, /* buffer pointer */
+ k->upload_present, /* buffer size */
+ &bytes_written); /* actually sent */
+ if(result)
+ return result;
+
+ win_update_buffer_size(conn->writesockfd);
+
+ if(k->pendingheader) {
+ /* parts of what was sent was header */
+ curl_off_t n = CURLMIN(k->pendingheader, bytes_written);
+ /* show the data before we change the pointer upload_fromhere */
+ Curl_debug(data, CURLINFO_HEADER_OUT, k->upload_fromhere, (size_t)n);
+ k->pendingheader -= n;
+ nbody = bytes_written - n; /* size of the written body part */
+ }
+ else
+ nbody = bytes_written;
+
+ if(nbody) {
+ /* show the data before we change the pointer upload_fromhere */
+ Curl_debug(data, CURLINFO_DATA_OUT,
+ &k->upload_fromhere[bytes_written - nbody],
+ (size_t)nbody);
+
+ k->writebytecount += nbody;
+ Curl_pgrsSetUploadCounter(data, k->writebytecount);
+ }
+
+ if((!k->upload_chunky || k->forbidchunk) &&
+ (k->writebytecount == data->state.infilesize)) {
+ /* we have sent all data we were supposed to */
+ k->upload_done = TRUE;
+ infof(data, "We are completely uploaded and fine");
+ }
+
+ if(k->upload_present != bytes_written) {
+ /* we only wrote a part of the buffer (if anything), deal with it! */
+
+ /* store the amount of bytes left in the buffer to write */
+ k->upload_present -= bytes_written;
+
+ /* advance the pointer where to find the buffer when the next send
+ is to happen */
+ k->upload_fromhere += bytes_written;
+ }
+ else {
+ /* we've uploaded that buffer now */
+ result = Curl_get_upload_buffer(data);
+ if(result)
+ return result;
+ k->upload_fromhere = data->state.ulbuf;
+ k->upload_present = 0; /* no more bytes left */
+
+ if(k->upload_done) {
+ result = Curl_done_sending(data, k);
+ if(result)
+ return result;
+ }
+ }
+
+
+ } while(0); /* just to break out from! */
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_readwrite() is the low-level function to be called when data is to
+ * be read and written to/from the connection.
+ *
+ * return '*comeback' TRUE if we didn't properly drain the socket so this
+ * function should get called again without select() or similar in between!
+ */
+CURLcode Curl_readwrite(struct connectdata *conn,
+ struct Curl_easy *data,
+ bool *done,
+ bool *comeback)
+{
+ struct SingleRequest *k = &data->req;
+ CURLcode result;
+ int didwhat = 0;
+
+ curl_socket_t fd_read;
+ curl_socket_t fd_write;
+ int select_res = conn->cselect_bits;
+
+ conn->cselect_bits = 0;
+
+ /* only use the proper socket if the *_HOLD bit is not set simultaneously as
+ then we are in rate limiting state in that transfer direction */
+
+ if((k->keepon & KEEP_RECVBITS) == KEEP_RECV)
+ fd_read = conn->sockfd;
+ else
+ fd_read = CURL_SOCKET_BAD;
+
+ if((k->keepon & KEEP_SENDBITS) == KEEP_SEND)
+ fd_write = conn->writesockfd;
+ else
+ fd_write = CURL_SOCKET_BAD;
+
+#if defined(USE_HTTP2) || defined(USE_HTTP3)
+ if(data->state.drain) {
+ select_res |= CURL_CSELECT_IN;
+ DEBUGF(infof(data, "Curl_readwrite: forcibly told to drain data"));
+ }
+#endif
+
+ if(!select_res) /* Call for select()/poll() only, if read/write/error
+ status is not known. */
+ select_res = Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write, 0);
+
+ if(select_res == CURL_CSELECT_ERR) {
+ failf(data, "select/poll returned error");
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
+
+#ifdef USE_HYPER
+ if(conn->datastream) {
+ result = conn->datastream(data, conn, &didwhat, done, select_res);
+ if(result || *done)
+ goto out;
+ }
+ else {
+#endif
+ /* We go ahead and do a read if we have a readable socket or if
+ the stream was rewound (in which case we have data in a
+ buffer) */
+ if((k->keepon & KEEP_RECV) && (select_res & CURL_CSELECT_IN)) {
+ result = readwrite_data(data, conn, k, &didwhat, done, comeback);
+ if(result || *done)
+ goto out;
+ }
+
+ /* If we still have writing to do, we check if we have a writable socket. */
+ if((k->keepon & KEEP_SEND) && (select_res & CURL_CSELECT_OUT)) {
+ /* write */
+
+ result = readwrite_upload(data, conn, &didwhat);
+ if(result)
+ goto out;
+ }
+#ifdef USE_HYPER
+ }
+#endif
+
+ k->now = Curl_now();
+ if(!didwhat) {
+ /* no read no write, this is a timeout? */
+ if(k->exp100 == EXP100_AWAITING_CONTINUE) {
+ /* This should allow some time for the header to arrive, but only a
+ very short time as otherwise it'll be too much wasted time too
+ often. */
+
+ /* Quoting RFC2616, section "8.2.3 Use of the 100 (Continue) Status":
+
+ Therefore, when a client sends this header field to an origin server
+ (possibly via a proxy) from which it has never seen a 100 (Continue)
+ status, the client SHOULD NOT wait for an indefinite period before
+ sending the request body.
+
+ */
+
+ timediff_t ms = Curl_timediff(k->now, k->start100);
+ if(ms >= data->set.expect_100_timeout) {
+ /* we've waited long enough, continue anyway */
+ k->exp100 = EXP100_SEND_DATA;
+ k->keepon |= KEEP_SEND;
+ Curl_expire_done(data, EXPIRE_100_TIMEOUT);
+ infof(data, "Done waiting for 100-continue");
+ }
+ }
+
+#ifdef ENABLE_QUIC
+ if(conn->transport == TRNSPRT_QUIC) {
+ result = Curl_quic_idle(data);
+ if(result)
+ goto out;
+ }
+#endif
+ }
+
+ if(Curl_pgrsUpdate(data))
+ result = CURLE_ABORTED_BY_CALLBACK;
+ else
+ result = Curl_speedcheck(data, k->now);
+ if(result)
+ goto out;
+
+ if(k->keepon) {
+ if(0 > Curl_timeleft(data, &k->now, FALSE)) {
+ if(k->size != -1) {
+ failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
+ " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %"
+ CURL_FORMAT_CURL_OFF_T " bytes received",
+ Curl_timediff(k->now, data->progress.t_startsingle),
+ k->bytecount, k->size);
+ }
+ else {
+ failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
+ " milliseconds with %" CURL_FORMAT_CURL_OFF_T " bytes received",
+ Curl_timediff(k->now, data->progress.t_startsingle),
+ k->bytecount);
+ }
+ result = CURLE_OPERATION_TIMEDOUT;
+ goto out;
+ }
+ }
+ else {
+ /*
+ * The transfer has been performed. Just make some general checks before
+ * returning.
+ */
+
+ if(!(data->req.no_body) && (k->size != -1) &&
+ (k->bytecount != k->size) &&
+#ifdef CURL_DO_LINEEND_CONV
+ /* Most FTP servers don't adjust their file SIZE response for CRLFs,
+ so we'll check to see if the discrepancy can be explained
+ by the number of CRLFs we've changed to LFs.
+ */
+ (k->bytecount != (k->size + data->state.crlf_conversions)) &&
+#endif /* CURL_DO_LINEEND_CONV */
+ !k->newurl) {
+ failf(data, "transfer closed with %" CURL_FORMAT_CURL_OFF_T
+ " bytes remaining to read", k->size - k->bytecount);
+ result = CURLE_PARTIAL_FILE;
+ goto out;
+ }
+ if(!(data->req.no_body) && k->chunk &&
+ (conn->chunk.state != CHUNK_STOP)) {
+ /*
+ * In chunked mode, return an error if the connection is closed prior to
+ * the empty (terminating) chunk is read.
+ *
+ * The condition above used to check for
+ * conn->proto.http->chunk.datasize != 0 which is true after reading
+ * *any* chunk, not just the empty chunk.
+ *
+ */
+ failf(data, "transfer closed with outstanding read data remaining");
+ result = CURLE_PARTIAL_FILE;
+ goto out;
+ }
+ if(Curl_pgrsUpdate(data)) {
+ result = CURLE_ABORTED_BY_CALLBACK;
+ goto out;
+ }
+ }
+
+ /* Now update the "done" boolean we return */
+ *done = (0 == (k->keepon&(KEEP_RECV|KEEP_SEND|
+ KEEP_RECV_PAUSE|KEEP_SEND_PAUSE))) ? TRUE : FALSE;
+ result = CURLE_OK;
+out:
+ DEBUGF(infof(data, "Curl_readwrite(handle=%p) -> %d", data, result));
+ return result;
+}
+
+/*
+ * Curl_single_getsock() gets called by the multi interface code when the app
+ * has requested to get the sockets for the current connection. This function
+ * will then be called once for every connection that the multi interface
+ * keeps track of. This function will only be called for connections that are
+ * in the proper state to have this information available.
+ */
+int Curl_single_getsock(struct Curl_easy *data,
+ struct connectdata *conn,
+ curl_socket_t *sock)
+{
+ int bitmap = GETSOCK_BLANK;
+ unsigned sockindex = 0;
+
+ if(conn->handler->perform_getsock)
+ return conn->handler->perform_getsock(data, conn, sock);
+
+ /* don't include HOLD and PAUSE connections */
+ if((data->req.keepon & KEEP_RECVBITS) == KEEP_RECV) {
+
+ DEBUGASSERT(conn->sockfd != CURL_SOCKET_BAD);
+
+ bitmap |= GETSOCK_READSOCK(sockindex);
+ sock[sockindex] = conn->sockfd;
+ }
+
+ /* don't include HOLD and PAUSE connections */
+ if((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) {
+ if((conn->sockfd != conn->writesockfd) ||
+ bitmap == GETSOCK_BLANK) {
+ /* only if they are not the same socket and we have a readable
+ one, we increase index */
+ if(bitmap != GETSOCK_BLANK)
+ sockindex++; /* increase index if we need two entries */
+
+ DEBUGASSERT(conn->writesockfd != CURL_SOCKET_BAD);
+
+ sock[sockindex] = conn->writesockfd;
+ }
+
+ bitmap |= GETSOCK_WRITESOCK(sockindex);
+ }
+
+ return bitmap;
+}
+
+/* Curl_init_CONNECT() gets called each time the handle switches to CONNECT
+ which means this gets called once for each subsequent redirect etc */
+void Curl_init_CONNECT(struct Curl_easy *data)
+{
+ data->state.fread_func = data->set.fread_func_set;
+ data->state.in = data->set.in_set;
+}
+
+/*
+ * Curl_pretransfer() is called immediately before a transfer starts, and only
+ * once for one transfer no matter if it has redirects or do multi-pass
+ * authentication etc.
+ */
+CURLcode Curl_pretransfer(struct Curl_easy *data)
+{
+ CURLcode result;
+
+ if(!data->state.url && !data->set.uh) {
+ /* we can't do anything without URL */
+ failf(data, "No URL set");
+ return CURLE_URL_MALFORMAT;
+ }
+
+ /* since the URL may have been redirected in a previous use of this handle */
+ if(data->state.url_alloc) {
+ /* the already set URL is allocated, free it first! */
+ Curl_safefree(data->state.url);
+ data->state.url_alloc = FALSE;
+ }
+
+ if(!data->state.url && data->set.uh) {
+ CURLUcode uc;
+ free(data->set.str[STRING_SET_URL]);
+ uc = curl_url_get(data->set.uh,
+ CURLUPART_URL, &data->set.str[STRING_SET_URL], 0);
+ if(uc) {
+ failf(data, "No URL set");
+ return CURLE_URL_MALFORMAT;
+ }
+ }
+
+ data->state.prefer_ascii = data->set.prefer_ascii;
+ data->state.list_only = data->set.list_only;
+ data->state.httpreq = data->set.method;
+ data->state.url = data->set.str[STRING_SET_URL];
+
+ /* Init the SSL session ID cache here. We do it here since we want to do it
+ after the *_setopt() calls (that could specify the size of the cache) but
+ before any transfer takes place. */
+ result = Curl_ssl_initsessions(data, data->set.general_ssl.max_ssl_sessions);
+ if(result)
+ return result;
+
+ data->state.requests = 0;
+ data->state.followlocation = 0; /* reset the location-follow counter */
+ data->state.this_is_a_follow = FALSE; /* reset this */
+ data->state.errorbuf = FALSE; /* no error has occurred */
+ data->state.httpwant = data->set.httpwant;
+ data->state.httpversion = 0;
+ data->state.authproblem = FALSE;
+ data->state.authhost.want = data->set.httpauth;
+ data->state.authproxy.want = data->set.proxyauth;
+ Curl_safefree(data->info.wouldredirect);
+
+ if(data->state.httpreq == HTTPREQ_PUT)
+ data->state.infilesize = data->set.filesize;
+ else if((data->state.httpreq != HTTPREQ_GET) &&
+ (data->state.httpreq != HTTPREQ_HEAD)) {
+ data->state.infilesize = data->set.postfieldsize;
+ if(data->set.postfields && (data->state.infilesize == -1))
+ data->state.infilesize = (curl_off_t)strlen(data->set.postfields);
+ }
+ else
+ data->state.infilesize = 0;
+
+#ifndef CURL_DISABLE_COOKIES
+ /* If there is a list of cookie files to read, do it now! */
+ if(data->state.cookielist)
+ Curl_cookie_loadfiles(data);
+#endif
+ /* If there is a list of host pairs to deal with */
+ if(data->state.resolve)
+ result = Curl_loadhostpairs(data);
+
+ if(!result) {
+ /* Allow data->set.use_port to set which port to use. This needs to be
+ * disabled for example when we follow Location: headers to URLs using
+ * different ports! */
+ data->state.allow_port = TRUE;
+
+#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL)
+ /*************************************************************
+ * Tell signal handler to ignore SIGPIPE
+ *************************************************************/
+ if(!data->set.no_signal)
+ data->state.prev_signal = signal(SIGPIPE, SIG_IGN);
+#endif
+
+ Curl_initinfo(data); /* reset session-specific information "variables" */
+ Curl_pgrsResetTransferSizes(data);
+ Curl_pgrsStartNow(data);
+
+ /* In case the handle is re-used and an authentication method was picked
+ in the session we need to make sure we only use the one(s) we now
+ consider to be fine */
+ data->state.authhost.picked &= data->state.authhost.want;
+ data->state.authproxy.picked &= data->state.authproxy.want;
+
+#ifndef CURL_DISABLE_FTP
+ data->state.wildcardmatch = data->set.wildcard_enabled;
+ if(data->state.wildcardmatch) {
+ struct WildcardData *wc = &data->wildcard;
+ if(wc->state < CURLWC_INIT) {
+ result = Curl_wildcard_init(wc); /* init wildcard structures */
+ if(result)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+#endif
+ Curl_http2_init_state(&data->state);
+ result = Curl_hsts_loadcb(data, data->hsts);
+ }
+
+ /*
+ * Set user-agent. Used for HTTP, but since we can attempt to tunnel
+ * basically anything through an HTTP proxy we can't limit this based on
+ * protocol.
+ */
+ if(data->set.str[STRING_USERAGENT]) {
+ Curl_safefree(data->state.aptr.uagent);
+ data->state.aptr.uagent =
+ aprintf("User-Agent: %s\r\n", data->set.str[STRING_USERAGENT]);
+ if(!data->state.aptr.uagent)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(!result)
+ result = Curl_setstropt(&data->state.aptr.user,
+ data->set.str[STRING_USERNAME]);
+ if(!result)
+ result = Curl_setstropt(&data->state.aptr.passwd,
+ data->set.str[STRING_PASSWORD]);
+ if(!result)
+ result = Curl_setstropt(&data->state.aptr.proxyuser,
+ data->set.str[STRING_PROXYUSERNAME]);
+ if(!result)
+ result = Curl_setstropt(&data->state.aptr.proxypasswd,
+ data->set.str[STRING_PROXYPASSWORD]);
+
+ data->req.headerbytecount = 0;
+ Curl_headers_cleanup(data);
+ return result;
+}
+
+/*
+ * Curl_posttransfer() is called immediately after a transfer ends
+ */
+CURLcode Curl_posttransfer(struct Curl_easy *data)
+{
+#if defined(HAVE_SIGNAL) && defined(SIGPIPE) && !defined(HAVE_MSG_NOSIGNAL)
+ /* restore the signal handler for SIGPIPE before we get back */
+ if(!data->set.no_signal)
+ signal(SIGPIPE, data->state.prev_signal);
+#else
+ (void)data; /* unused parameter */
+#endif
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_follow() handles the URL redirect magic. Pass in the 'newurl' string
+ * as given by the remote server and set up the new URL to request.
+ *
+ * This function DOES NOT FREE the given url.
+ */
+CURLcode Curl_follow(struct Curl_easy *data,
+ char *newurl, /* the Location: string */
+ followtype type) /* see transfer.h */
+{
+#ifdef CURL_DISABLE_HTTP
+ (void)data;
+ (void)newurl;
+ (void)type;
+ /* Location: following will not happen when HTTP is disabled */
+ return CURLE_TOO_MANY_REDIRECTS;
+#else
+
+ /* Location: redirect */
+ bool disallowport = FALSE;
+ bool reachedmax = FALSE;
+ CURLUcode uc;
+
+ DEBUGASSERT(type != FOLLOW_NONE);
+
+ if(type != FOLLOW_FAKE)
+ data->state.requests++; /* count all real follows */
+ if(type == FOLLOW_REDIR) {
+ if((data->set.maxredirs != -1) &&
+ (data->state.followlocation >= data->set.maxredirs)) {
+ reachedmax = TRUE;
+ type = FOLLOW_FAKE; /* switch to fake to store the would-be-redirected
+ to URL */
+ }
+ else {
+ data->state.followlocation++; /* count redirect-followings, including
+ auth reloads */
+
+ if(data->set.http_auto_referer) {
+ CURLU *u;
+ char *referer = NULL;
+
+ /* We are asked to automatically set the previous URL as the referer
+ when we get the next URL. We pick the ->url field, which may or may
+ not be 100% correct */
+
+ if(data->state.referer_alloc) {
+ Curl_safefree(data->state.referer);
+ data->state.referer_alloc = FALSE;
+ }
+
+ /* Make a copy of the URL without credentials and fragment */
+ u = curl_url();
+ if(!u)
+ return CURLE_OUT_OF_MEMORY;
+
+ uc = curl_url_set(u, CURLUPART_URL, data->state.url, 0);
+ if(!uc)
+ uc = curl_url_set(u, CURLUPART_FRAGMENT, NULL, 0);
+ if(!uc)
+ uc = curl_url_set(u, CURLUPART_USER, NULL, 0);
+ if(!uc)
+ uc = curl_url_set(u, CURLUPART_PASSWORD, NULL, 0);
+ if(!uc)
+ uc = curl_url_get(u, CURLUPART_URL, &referer, 0);
+
+ curl_url_cleanup(u);
+
+ if(uc || !referer)
+ return CURLE_OUT_OF_MEMORY;
+
+ data->state.referer = referer;
+ data->state.referer_alloc = TRUE; /* yes, free this later */
+ }
+ }
+ }
+
+ if((type != FOLLOW_RETRY) &&
+ (data->req.httpcode != 401) && (data->req.httpcode != 407) &&
+ Curl_is_absolute_url(newurl, NULL, 0, FALSE))
+ /* If this is not redirect due to a 401 or 407 response and an absolute
+ URL: don't allow a custom port number */
+ disallowport = TRUE;
+
+ DEBUGASSERT(data->state.uh);
+ uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl,
+ (type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME :
+ ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) |
+ CURLU_ALLOW_SPACE |
+ (data->set.path_as_is ? CURLU_PATH_AS_IS : 0));
+ if(uc) {
+ if(type != FOLLOW_FAKE) {
+ failf(data, "The redirect target URL could not be parsed: %s",
+ curl_url_strerror(uc));
+ return Curl_uc_to_curlcode(uc);
+ }
+
+ /* the URL could not be parsed for some reason, but since this is FAKE
+ mode, just duplicate the field as-is */
+ newurl = strdup(newurl);
+ if(!newurl)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else {
+ uc = curl_url_get(data->state.uh, CURLUPART_URL, &newurl, 0);
+ if(uc)
+ return Curl_uc_to_curlcode(uc);
+
+ /* Clear auth if this redirects to a different port number or protocol,
+ unless permitted */
+ if(!data->set.allow_auth_to_other_hosts && (type != FOLLOW_FAKE)) {
+ char *portnum;
+ int port;
+ bool clear = FALSE;
+
+ if(data->set.use_port && data->state.allow_port)
+ /* a custom port is used */
+ port = (int)data->set.use_port;
+ else {
+ uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum,
+ CURLU_DEFAULT_PORT);
+ if(uc) {
+ free(newurl);
+ return Curl_uc_to_curlcode(uc);
+ }
+ port = atoi(portnum);
+ free(portnum);
+ }
+ if(port != data->info.conn_remote_port) {
+ infof(data, "Clear auth, redirects to port from %u to %u",
+ data->info.conn_remote_port, port);
+ clear = TRUE;
+ }
+ else {
+ char *scheme;
+ const struct Curl_handler *p;
+ uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0);
+ if(uc) {
+ free(newurl);
+ return Curl_uc_to_curlcode(uc);
+ }
+
+ p = Curl_builtin_scheme(scheme, CURL_ZERO_TERMINATED);
+ if(p && (p->protocol != data->info.conn_protocol)) {
+ infof(data, "Clear auth, redirects scheme from %s to %s",
+ data->info.conn_scheme, scheme);
+ clear = TRUE;
+ }
+ free(scheme);
+ }
+ if(clear) {
+ Curl_safefree(data->state.aptr.user);
+ Curl_safefree(data->state.aptr.passwd);
+ }
+ }
+ }
+
+ if(type == FOLLOW_FAKE) {
+ /* we're only figuring out the new url if we would've followed locations
+ but now we're done so we can get out! */
+ data->info.wouldredirect = newurl;
+
+ if(reachedmax) {
+ failf(data, "Maximum (%ld) redirects followed", data->set.maxredirs);
+ return CURLE_TOO_MANY_REDIRECTS;
+ }
+ return CURLE_OK;
+ }
+
+ if(disallowport)
+ data->state.allow_port = FALSE;
+
+ if(data->state.url_alloc)
+ Curl_safefree(data->state.url);
+
+ data->state.url = newurl;
+ data->state.url_alloc = TRUE;
+
+ infof(data, "Issue another request to this URL: '%s'", data->state.url);
+
+ /*
+ * We get here when the HTTP code is 300-399 (and 401). We need to perform
+ * differently based on exactly what return code there was.
+ *
+ * News from 7.10.6: we can also get here on a 401 or 407, in case we act on
+ * an HTTP (proxy-) authentication scheme other than Basic.
+ */
+ switch(data->info.httpcode) {
+ /* 401 - Act on a WWW-Authenticate, we keep on moving and do the
+ Authorization: XXXX header in the HTTP request code snippet */
+ /* 407 - Act on a Proxy-Authenticate, we keep on moving and do the
+ Proxy-Authorization: XXXX header in the HTTP request code snippet */
+ /* 300 - Multiple Choices */
+ /* 306 - Not used */
+ /* 307 - Temporary Redirect */
+ default: /* for all above (and the unknown ones) */
+ /* Some codes are explicitly mentioned since I've checked RFC2616 and they
+ * seem to be OK to POST to.
+ */
+ break;
+ case 301: /* Moved Permanently */
+ /* (quote from RFC7231, section 6.4.2)
+ *
+ * Note: For historical reasons, a user agent MAY change the request
+ * method from POST to GET for the subsequent request. If this
+ * behavior is undesired, the 307 (Temporary Redirect) status code
+ * can be used instead.
+ *
+ * ----
+ *
+ * Many webservers expect this, so these servers often answers to a POST
+ * request with an error page. To be sure that libcurl gets the page that
+ * most user agents would get, libcurl has to force GET.
+ *
+ * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and
+ * can be overridden with CURLOPT_POSTREDIR.
+ */
+ if((data->state.httpreq == HTTPREQ_POST
+ || data->state.httpreq == HTTPREQ_POST_FORM
+ || data->state.httpreq == HTTPREQ_POST_MIME)
+ && !(data->set.keep_post & CURL_REDIR_POST_301)) {
+ infof(data, "Switch from POST to GET");
+ data->state.httpreq = HTTPREQ_GET;
+ }
+ break;
+ case 302: /* Found */
+ /* (quote from RFC7231, section 6.4.3)
+ *
+ * Note: For historical reasons, a user agent MAY change the request
+ * method from POST to GET for the subsequent request. If this
+ * behavior is undesired, the 307 (Temporary Redirect) status code
+ * can be used instead.
+ *
+ * ----
+ *
+ * Many webservers expect this, so these servers often answers to a POST
+ * request with an error page. To be sure that libcurl gets the page that
+ * most user agents would get, libcurl has to force GET.
+ *
+ * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and
+ * can be overridden with CURLOPT_POSTREDIR.
+ */
+ if((data->state.httpreq == HTTPREQ_POST
+ || data->state.httpreq == HTTPREQ_POST_FORM
+ || data->state.httpreq == HTTPREQ_POST_MIME)
+ && !(data->set.keep_post & CURL_REDIR_POST_302)) {
+ infof(data, "Switch from POST to GET");
+ data->state.httpreq = HTTPREQ_GET;
+ }
+ break;
+
+ case 303: /* See Other */
+ /* 'See Other' location is not the resource but a substitute for the
+ * resource. In this case we switch the method to GET/HEAD, unless the
+ * method is POST and the user specified to keep it as POST.
+ * https://github.com/curl/curl/issues/5237#issuecomment-614641049
+ */
+ if(data->state.httpreq != HTTPREQ_GET &&
+ ((data->state.httpreq != HTTPREQ_POST &&
+ data->state.httpreq != HTTPREQ_POST_FORM &&
+ data->state.httpreq != HTTPREQ_POST_MIME) ||
+ !(data->set.keep_post & CURL_REDIR_POST_303))) {
+ data->state.httpreq = HTTPREQ_GET;
+ data->set.upload = false;
+ infof(data, "Switch to %s",
+ data->req.no_body?"HEAD":"GET");
+ }
+ break;
+ case 304: /* Not Modified */
+ /* 304 means we did a conditional request and it was "Not modified".
+ * We shouldn't get any Location: header in this response!
+ */
+ break;
+ case 305: /* Use Proxy */
+ /* (quote from RFC2616, section 10.3.6):
+ * "The requested resource MUST be accessed through the proxy given
+ * by the Location field. The Location field gives the URI of the
+ * proxy. The recipient is expected to repeat this single request
+ * via the proxy. 305 responses MUST only be generated by origin
+ * servers."
+ */
+ break;
+ }
+ Curl_pgrsTime(data, TIMER_REDIRECT);
+ Curl_pgrsResetTransferSizes(data);
+
+ return CURLE_OK;
+#endif /* CURL_DISABLE_HTTP */
+}
+
+/* Returns CURLE_OK *and* sets '*url' if a request retry is wanted.
+
+ NOTE: that the *url is malloc()ed. */
+CURLcode Curl_retry_request(struct Curl_easy *data, char **url)
+{
+ struct connectdata *conn = data->conn;
+ bool retry = FALSE;
+ *url = NULL;
+
+ /* if we're talking upload, we can't do the checks below, unless the protocol
+ is HTTP as when uploading over HTTP we will still get a response */
+ if(data->set.upload &&
+ !(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)))
+ return CURLE_OK;
+
+ if((data->req.bytecount + data->req.headerbytecount == 0) &&
+ conn->bits.reuse &&
+ (!data->req.no_body || (conn->handler->protocol & PROTO_FAMILY_HTTP))
+#ifndef CURL_DISABLE_RTSP
+ && (data->set.rtspreq != RTSPREQ_RECEIVE)
+#endif
+ )
+ /* We got no data, we attempted to re-use a connection. For HTTP this
+ can be a retry so we try again regardless if we expected a body.
+ For other protocols we only try again only if we expected a body.
+
+ This might happen if the connection was left alive when we were
+ done using it before, but that was closed when we wanted to read from
+ it again. Bad luck. Retry the same request on a fresh connect! */
+ retry = TRUE;
+ else if(data->state.refused_stream &&
+ (data->req.bytecount + data->req.headerbytecount == 0) ) {
+ /* This was sent on a refused stream, safe to rerun. A refused stream
+ error can typically only happen on HTTP/2 level if the stream is safe
+ to issue again, but the nghttp2 API can deliver the message to other
+ streams as well, which is why this adds the check the data counters
+ too. */
+ infof(data, "REFUSED_STREAM, retrying a fresh connect");
+ data->state.refused_stream = FALSE; /* clear again */
+ retry = TRUE;
+ }
+ if(retry) {
+#define CONN_MAX_RETRIES 5
+ if(data->state.retrycount++ >= CONN_MAX_RETRIES) {
+ failf(data, "Connection died, tried %d times before giving up",
+ CONN_MAX_RETRIES);
+ data->state.retrycount = 0;
+ return CURLE_SEND_ERROR;
+ }
+ infof(data, "Connection died, retrying a fresh connect (retry count: %d)",
+ data->state.retrycount);
+ *url = strdup(data->state.url);
+ if(!*url)
+ return CURLE_OUT_OF_MEMORY;
+
+ connclose(conn, "retry"); /* close this connection */
+ conn->bits.retry = TRUE; /* mark this as a connection we're about
+ to retry. Marking it this way should
+ prevent i.e HTTP transfers to return
+ error just because nothing has been
+ transferred! */
+
+
+ if((conn->handler->protocol&PROTO_FAMILY_HTTP) &&
+ data->req.writebytecount) {
+ data->state.rewindbeforesend = TRUE;
+ infof(data, "state.rewindbeforesend = TRUE");
+ }
+ }
+ return CURLE_OK;
+}
+
+/*
+ * Curl_setup_transfer() is called to setup some basic properties for the
+ * upcoming transfer.
+ */
+void
+Curl_setup_transfer(
+ struct Curl_easy *data, /* transfer */
+ int sockindex, /* socket index to read from or -1 */
+ curl_off_t size, /* -1 if unknown at this point */
+ bool getheader, /* TRUE if header parsing is wanted */
+ int writesockindex /* socket index to write to, it may very well be
+ the same we read from. -1 disables */
+ )
+{
+ struct SingleRequest *k = &data->req;
+ struct connectdata *conn = data->conn;
+ struct HTTP *http = data->req.p.http;
+ bool httpsending;
+
+ DEBUGASSERT(conn != NULL);
+ DEBUGASSERT((sockindex <= 1) && (sockindex >= -1));
+
+ httpsending = ((conn->handler->protocol&PROTO_FAMILY_HTTP) &&
+ (http->sending == HTTPSEND_REQUEST));
+
+ if(conn->bits.multiplex || conn->httpversion == 20 || httpsending) {
+ /* when multiplexing, the read/write sockets need to be the same! */
+ conn->sockfd = sockindex == -1 ?
+ ((writesockindex == -1 ? CURL_SOCKET_BAD : conn->sock[writesockindex])) :
+ conn->sock[sockindex];
+ conn->writesockfd = conn->sockfd;
+ if(httpsending)
+ /* special and very HTTP-specific */
+ writesockindex = FIRSTSOCKET;
+ }
+ else {
+ conn->sockfd = sockindex == -1 ?
+ CURL_SOCKET_BAD : conn->sock[sockindex];
+ conn->writesockfd = writesockindex == -1 ?
+ CURL_SOCKET_BAD:conn->sock[writesockindex];
+ }
+ k->getheader = getheader;
+
+ k->size = size;
+
+ /* The code sequence below is placed in this function just because all
+ necessary input is not always known in do_complete() as this function may
+ be called after that */
+
+ if(!k->getheader) {
+ k->header = FALSE;
+ if(size > 0)
+ Curl_pgrsSetDownloadSize(data, size);
+ }
+ /* we want header and/or body, if neither then don't do this! */
+ if(k->getheader || !data->req.no_body) {
+
+ if(sockindex != -1)
+ k->keepon |= KEEP_RECV;
+
+ if(writesockindex != -1) {
+ /* HTTP 1.1 magic:
+
+ Even if we require a 100-return code before uploading data, we might
+ need to write data before that since the REQUEST may not have been
+ finished sent off just yet.
+
+ Thus, we must check if the request has been sent before we set the
+ state info where we wait for the 100-return code
+ */
+ if((data->state.expect100header) &&
+ (conn->handler->protocol&PROTO_FAMILY_HTTP) &&
+ (http->sending == HTTPSEND_BODY)) {
+ /* wait with write until we either got 100-continue or a timeout */
+ k->exp100 = EXP100_AWAITING_CONTINUE;
+ k->start100 = Curl_now();
+
+ /* Set a timeout for the multi interface. Add the inaccuracy margin so
+ that we don't fire slightly too early and get denied to run. */
+ Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
+ }
+ else {
+ if(data->state.expect100header)
+ /* when we've sent off the rest of the headers, we must await a
+ 100-continue but first finish sending the request */
+ k->exp100 = EXP100_SENDING_REQUEST;
+
+ /* enable the write bit when we're not waiting for continue */
+ k->keepon |= KEEP_SEND;
+ }
+ } /* if(writesockindex != -1) */
+ } /* if(k->getheader || !data->req.no_body) */
+
+}
diff --git a/libs/libcurl/src/transfer.h b/libs/libcurl/src/transfer.h index 65fe68e812..2b2913cb09 100644 --- a/libs/libcurl/src/transfer.h +++ b/libs/libcurl/src/transfer.h @@ -1,74 +1,73 @@ -#ifndef HEADER_CURL_TRANSFER_H -#define HEADER_CURL_TRANSFER_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_headersep(x) ((((x)==':') || ((x)==';'))) -char *Curl_checkheaders(const struct Curl_easy *data, - const char *thisheader, - const size_t thislen); - -void Curl_init_CONNECT(struct Curl_easy *data); - -CURLcode Curl_pretransfer(struct Curl_easy *data); -CURLcode Curl_posttransfer(struct Curl_easy *data); - -typedef enum { - FOLLOW_NONE, /* not used within the function, just a placeholder to - allow initing to this */ - FOLLOW_FAKE, /* only records stuff, not actually following */ - FOLLOW_RETRY, /* set if this is a request retry as opposed to a real - redirect following */ - FOLLOW_REDIR /* a full true redirect */ -} followtype; - -CURLcode Curl_follow(struct Curl_easy *data, char *newurl, - followtype type); -CURLcode Curl_readwrite(struct connectdata *conn, - struct Curl_easy *data, bool *done, - bool *comeback); -int Curl_single_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks); -CURLcode Curl_readrewind(struct Curl_easy *data); -CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes, - size_t *nreadp); -CURLcode Curl_retry_request(struct Curl_easy *data, char **url); -bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc); -CURLcode Curl_get_upload_buffer(struct Curl_easy *data); - -CURLcode Curl_done_sending(struct Curl_easy *data, - struct SingleRequest *k); - -/* This sets up a forthcoming transfer */ -void -Curl_setup_transfer (struct Curl_easy *data, - int sockindex, /* socket index to read from or -1 */ - curl_off_t size, /* -1 if unknown at this point */ - bool getheader, /* TRUE if header parsing is wanted */ - int writesockindex /* socket index to write to. May be - the same we read from. -1 - disables */ - ); - -#endif /* HEADER_CURL_TRANSFER_H */ +#ifndef HEADER_CURL_TRANSFER_H
+#define HEADER_CURL_TRANSFER_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_headersep(x) ((((x)==':') || ((x)==';')))
+char *Curl_checkheaders(const struct Curl_easy *data,
+ const char *thisheader,
+ const size_t thislen);
+
+void Curl_init_CONNECT(struct Curl_easy *data);
+
+CURLcode Curl_pretransfer(struct Curl_easy *data);
+CURLcode Curl_posttransfer(struct Curl_easy *data);
+
+typedef enum {
+ FOLLOW_NONE, /* not used within the function, just a placeholder to
+ allow initing to this */
+ FOLLOW_FAKE, /* only records stuff, not actually following */
+ FOLLOW_RETRY, /* set if this is a request retry as opposed to a real
+ redirect following */
+ FOLLOW_REDIR /* a full true redirect */
+} followtype;
+
+CURLcode Curl_follow(struct Curl_easy *data, char *newurl,
+ followtype type);
+CURLcode Curl_readwrite(struct connectdata *conn,
+ struct Curl_easy *data, bool *done,
+ bool *comeback);
+int Curl_single_getsock(struct Curl_easy *data,
+ struct connectdata *conn, curl_socket_t *socks);
+CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
+ size_t *nreadp);
+CURLcode Curl_retry_request(struct Curl_easy *data, char **url);
+bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc);
+CURLcode Curl_get_upload_buffer(struct Curl_easy *data);
+
+CURLcode Curl_done_sending(struct Curl_easy *data,
+ struct SingleRequest *k);
+
+/* This sets up a forthcoming transfer */
+void
+Curl_setup_transfer (struct Curl_easy *data,
+ int sockindex, /* socket index to read from or -1 */
+ curl_off_t size, /* -1 if unknown at this point */
+ bool getheader, /* TRUE if header parsing is wanted */
+ int writesockindex /* socket index to write to. May be
+ the same we read from. -1
+ disables */
+ );
+
+#endif /* HEADER_CURL_TRANSFER_H */
diff --git a/libs/libcurl/src/url.c b/libs/libcurl/src/url.c index be5ffca2d8..45a6f5d871 100644 --- a/libs/libcurl/src/url.c +++ b/libs/libcurl/src/url.c @@ -1,4212 +1,4020 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 HAVE_NET_IF_H -#include <net/if.h> -#endif -#ifdef HAVE_IPHLPAPI_H -#include <Iphlpapi.h> -#endif -#ifdef HAVE_SYS_IOCTL_H -#include <sys/ioctl.h> -#endif -#ifdef HAVE_SYS_PARAM_H -#include <sys/param.h> -#endif - -#ifdef __VMS -#include <in.h> -#include <inet.h> -#endif - -#ifdef HAVE_SYS_UN_H -#include <sys/un.h> -#endif - -#ifndef HAVE_SOCKET -#error "We can't compile without socket() support!" -#endif - -#include <limits.h> - -#ifdef USE_LIBIDN2 -#include <idn2.h> - -#if defined(WIN32) && defined(UNICODE) -#define IDN2_LOOKUP(name, host, flags) \ - idn2_lookup_u8((const uint8_t *)name, (uint8_t **)host, flags) -#else -#define IDN2_LOOKUP(name, host, flags) \ - idn2_lookup_ul((const char *)name, (char **)host, flags) -#endif - -#elif defined(USE_WIN32_IDN) -/* prototype for Curl_win32_idn_to_ascii() */ -bool Curl_win32_idn_to_ascii(const char *in, char **out); -#endif /* USE_LIBIDN2 */ - -#include "doh.h" -#include "urldata.h" -#include "netrc.h" - -#include "formdata.h" -#include "mime.h" -#include "vtls/vtls.h" -#include "hostip.h" -#include "transfer.h" -#include "sendf.h" -#include "progress.h" -#include "cookie.h" -#include "strcase.h" -#include "strerror.h" -#include "escape.h" -#include "strtok.h" -#include "share.h" -#include "content_encoding.h" -#include "http_digest.h" -#include "http_negotiate.h" -#include "select.h" -#include "multiif.h" -#include "easyif.h" -#include "speedcheck.h" -#include "warnless.h" -#include "getinfo.h" -#include "urlapi-int.h" -#include "system_win32.h" -#include "hsts.h" -#include "noproxy.h" - -/* And now for the protocols */ -#include "ftp.h" -#include "dict.h" -#include "telnet.h" -#include "tftp.h" -#include "http.h" -#include "http2.h" -#include "file.h" -#include "curl_ldap.h" -#include "vssh/ssh.h" -#include "imap.h" -#include "url.h" -#include "connect.h" -#include "inet_ntop.h" -#include "http_ntlm.h" -#include "curl_rtmp.h" -#include "gopher.h" -#include "mqtt.h" -#include "http_proxy.h" -#include "conncache.h" -#include "multihandle.h" -#include "strdup.h" -#include "setopt.h" -#include "altsvc.h" -#include "dynbuf.h" -#include "headers.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -static void conn_free(struct connectdata *conn); - -/* Some parts of the code (e.g. chunked encoding) assume this buffer has at - * more than just a few bytes to play with. Don't let it become too small or - * bad things will happen. - */ -#if READBUFFER_SIZE < READBUFFER_MIN -# error READBUFFER_SIZE is too small -#endif - -#ifdef USE_UNIX_SOCKETS -#define UNIX_SOCKET_PREFIX "localhost" -#endif - -/* Reject URLs exceeding this length */ -#define MAX_URL_LEN 0xffff - -/* -* get_protocol_family() -* -* This is used to return the protocol family for a given protocol. -* -* Parameters: -* -* 'h' [in] - struct Curl_handler pointer. -* -* Returns the family as a single bit protocol identifier. -*/ -static curl_prot_t get_protocol_family(const struct Curl_handler *h) -{ - DEBUGASSERT(h); - DEBUGASSERT(h->family); - return h->family; -} - - -/* - * Protocol table. Schemes (roughly) in 2019 popularity order: - * - * HTTPS, HTTP, FTP, FTPS, SFTP, FILE, SCP, SMTP, LDAP, IMAPS, TELNET, IMAP, - * LDAPS, SMTPS, TFTP, SMB, POP3, GOPHER POP3S, RTSP, RTMP, SMBS, DICT - */ -static const struct Curl_handler * const protocols[] = { - -#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) - &Curl_handler_https, -#endif - -#ifndef CURL_DISABLE_HTTP - &Curl_handler_http, -#endif - -#ifdef USE_WEBSOCKETS -#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) - &Curl_handler_wss, -#endif - -#ifndef CURL_DISABLE_HTTP - &Curl_handler_ws, -#endif -#endif - -#ifndef CURL_DISABLE_FTP - &Curl_handler_ftp, -#endif - -#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP) - &Curl_handler_ftps, -#endif - -#if defined(USE_SSH) - &Curl_handler_sftp, -#endif - -#ifndef CURL_DISABLE_FILE - &Curl_handler_file, -#endif - -#if defined(USE_SSH) && !defined(USE_WOLFSSH) - &Curl_handler_scp, -#endif - -#ifndef CURL_DISABLE_SMTP - &Curl_handler_smtp, -#ifdef USE_SSL - &Curl_handler_smtps, -#endif -#endif - -#ifndef CURL_DISABLE_LDAP - &Curl_handler_ldap, -#if !defined(CURL_DISABLE_LDAPS) && \ - ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \ - (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL))) - &Curl_handler_ldaps, -#endif -#endif - -#ifndef CURL_DISABLE_IMAP - &Curl_handler_imap, -#ifdef USE_SSL - &Curl_handler_imaps, -#endif -#endif - -#ifndef CURL_DISABLE_TELNET - &Curl_handler_telnet, -#endif - -#ifndef CURL_DISABLE_TFTP - &Curl_handler_tftp, -#endif - -#ifndef CURL_DISABLE_POP3 - &Curl_handler_pop3, -#ifdef USE_SSL - &Curl_handler_pop3s, -#endif -#endif - -#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \ - (SIZEOF_CURL_OFF_T > 4) - &Curl_handler_smb, -#ifdef USE_SSL - &Curl_handler_smbs, -#endif -#endif - -#ifndef CURL_DISABLE_RTSP - &Curl_handler_rtsp, -#endif - -#ifndef CURL_DISABLE_MQTT - &Curl_handler_mqtt, -#endif - -#ifndef CURL_DISABLE_GOPHER - &Curl_handler_gopher, -#ifdef USE_SSL - &Curl_handler_gophers, -#endif -#endif - -#ifdef USE_LIBRTMP - &Curl_handler_rtmp, - &Curl_handler_rtmpt, - &Curl_handler_rtmpe, - &Curl_handler_rtmpte, - &Curl_handler_rtmps, - &Curl_handler_rtmpts, -#endif - -#ifndef CURL_DISABLE_DICT - &Curl_handler_dict, -#endif - - (struct Curl_handler *) NULL -}; - -/* - * Dummy handler for undefined protocol schemes. - */ - -static const struct Curl_handler Curl_handler_dummy = { - "<no protocol>", /* scheme */ - ZERO_NULL, /* setup_connection */ - ZERO_NULL, /* do_it */ - ZERO_NULL, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_getsock */ - ZERO_NULL, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ZERO_NULL, /* perform_getsock */ - ZERO_NULL, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - 0, /* defport */ - 0, /* protocol */ - 0, /* family */ - PROTOPT_NONE /* flags */ -}; - -void Curl_freeset(struct Curl_easy *data) -{ - /* Free all dynamic strings stored in the data->set substructure. */ - enum dupstring i; - enum dupblob j; - - for(i = (enum dupstring)0; i < STRING_LAST; i++) { - Curl_safefree(data->set.str[i]); - } - - for(j = (enum dupblob)0; j < BLOB_LAST; j++) { - Curl_safefree(data->set.blobs[j]); - } - - if(data->state.referer_alloc) { - Curl_safefree(data->state.referer); - data->state.referer_alloc = FALSE; - } - data->state.referer = NULL; - if(data->state.url_alloc) { - Curl_safefree(data->state.url); - data->state.url_alloc = FALSE; - } - data->state.url = NULL; - - Curl_mime_cleanpart(&data->set.mimepost); -} - -/* free the URL pieces */ -static void up_free(struct Curl_easy *data) -{ - struct urlpieces *up = &data->state.up; - Curl_safefree(up->scheme); - Curl_safefree(up->hostname); - Curl_safefree(up->port); - Curl_safefree(up->user); - Curl_safefree(up->password); - Curl_safefree(up->options); - Curl_safefree(up->path); - Curl_safefree(up->query); - curl_url_cleanup(data->state.uh); - data->state.uh = NULL; -} - -/* - * This is the internal function curl_easy_cleanup() calls. This should - * cleanup and free all resources associated with this sessionhandle. - * - * We ignore SIGPIPE when this is called from curl_easy_cleanup. - */ - -CURLcode Curl_close(struct Curl_easy **datap) -{ - struct Curl_multi *m; - struct Curl_easy *data; - - if(!datap || !*datap) - return CURLE_OK; - - data = *datap; - *datap = NULL; - - Curl_expire_clear(data); /* shut off timers */ - - /* Detach connection if any is left. This should not be normal, but can be - the case for example with CONNECT_ONLY + recv/send (test 556) */ - Curl_detach_connection(data); - m = data->multi; - if(m) - /* This handle is still part of a multi handle, take care of this first - and detach this handle from there. */ - curl_multi_remove_handle(data->multi, data); - - if(data->multi_easy) { - /* when curl_easy_perform() is used, it creates its own multi handle to - use and this is the one */ - curl_multi_cleanup(data->multi_easy); - data->multi_easy = NULL; - } - - /* Destroy the timeout list that is held in the easy handle. It is - /normally/ done by curl_multi_remove_handle() but this is "just in - case" */ - Curl_llist_destroy(&data->state.timeoutlist, NULL); - - data->magic = 0; /* force a clear AFTER the possibly enforced removal from - the multi handle, since that function uses the magic - field! */ - - if(data->state.rangestringalloc) - free(data->state.range); - - /* freed here just in case DONE wasn't called */ - Curl_free_request_state(data); - - /* Close down all open SSL info and sessions */ - Curl_ssl_close_all(data); - Curl_safefree(data->state.first_host); - Curl_safefree(data->state.scratch); - Curl_ssl_free_certinfo(data); - - /* Cleanup possible redirect junk */ - free(data->req.newurl); - data->req.newurl = NULL; - - if(data->state.referer_alloc) { - Curl_safefree(data->state.referer); - data->state.referer_alloc = FALSE; - } - data->state.referer = NULL; - - up_free(data); - Curl_safefree(data->state.buffer); - Curl_dyn_free(&data->state.headerb); - Curl_safefree(data->state.ulbuf); - Curl_flush_cookies(data, TRUE); - Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]); - Curl_altsvc_cleanup(&data->asi); - Curl_hsts_save(data, data->hsts, data->set.str[STRING_HSTS]); - Curl_hsts_cleanup(&data->hsts); -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) - Curl_http_auth_cleanup_digest(data); -#endif - Curl_safefree(data->info.contenttype); - Curl_safefree(data->info.wouldredirect); - - /* this destroys the channel and we cannot use it anymore after this */ - Curl_resolver_cancel(data); - Curl_resolver_cleanup(data->state.async.resolver); - - Curl_http2_cleanup_dependencies(data); - - /* No longer a dirty share, if it exists */ - if(data->share) { - Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); - data->share->dirty--; - Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); - } - - Curl_safefree(data->state.aptr.proxyuserpwd); - Curl_safefree(data->state.aptr.uagent); - Curl_safefree(data->state.aptr.userpwd); - Curl_safefree(data->state.aptr.accept_encoding); - Curl_safefree(data->state.aptr.te); - Curl_safefree(data->state.aptr.rangeline); - Curl_safefree(data->state.aptr.ref); - Curl_safefree(data->state.aptr.host); - Curl_safefree(data->state.aptr.cookiehost); - Curl_safefree(data->state.aptr.rtsp_transport); - Curl_safefree(data->state.aptr.user); - Curl_safefree(data->state.aptr.passwd); - Curl_safefree(data->state.aptr.proxyuser); - Curl_safefree(data->state.aptr.proxypasswd); - -#ifndef CURL_DISABLE_DOH - if(data->req.doh) { - Curl_dyn_free(&data->req.doh->probe[0].serverdoh); - Curl_dyn_free(&data->req.doh->probe[1].serverdoh); - curl_slist_free_all(data->req.doh->headers); - Curl_safefree(data->req.doh); - } -#endif - - /* destruct wildcard structures if it is needed */ - Curl_wildcard_dtor(&data->wildcard); - Curl_freeset(data); - Curl_headers_cleanup(data); - free(data); - return CURLE_OK; -} - -/* - * Initialize the UserDefined fields within a Curl_easy. - * This may be safely called on a new or existing Curl_easy. - */ -CURLcode Curl_init_userdefined(struct Curl_easy *data) -{ - struct UserDefined *set = &data->set; - CURLcode result = CURLE_OK; - - set->out = stdout; /* default output to stdout */ - set->in_set = stdin; /* default input from stdin */ - set->err = stderr; /* default stderr to stderr */ - - /* use fwrite as default function to store output */ - set->fwrite_func = (curl_write_callback)fwrite; - - /* use fread as default function to read input */ - set->fread_func_set = (curl_read_callback)fread; - set->is_fread_set = 0; - - set->seek_func = ZERO_NULL; - set->seek_client = ZERO_NULL; - - set->filesize = -1; /* we don't know the size */ - set->postfieldsize = -1; /* unknown size */ - set->maxredirs = -1; /* allow any amount by default */ - - set->method = HTTPREQ_GET; /* Default HTTP request */ -#ifndef CURL_DISABLE_RTSP - set->rtspreq = RTSPREQ_OPTIONS; /* Default RTSP request */ -#endif -#ifndef CURL_DISABLE_FTP - set->ftp_use_epsv = TRUE; /* FTP defaults to EPSV operations */ - set->ftp_use_eprt = TRUE; /* FTP defaults to EPRT operations */ - set->ftp_use_pret = FALSE; /* mainly useful for drftpd servers */ - set->ftp_filemethod = FTPFILE_MULTICWD; - set->ftp_skip_ip = TRUE; /* skip PASV IP by default */ -#endif - set->dns_cache_timeout = 60; /* Timeout every 60 seconds by default */ - - /* Set the default size of the SSL session ID cache */ - set->general_ssl.max_ssl_sessions = 5; - - set->proxyport = 0; - set->proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */ - set->httpauth = CURLAUTH_BASIC; /* defaults to basic */ - -#ifndef CURL_DISABLE_PROXY - set->proxyauth = CURLAUTH_BASIC; /* defaults to basic */ - /* SOCKS5 proxy auth defaults to username/password + GSS-API */ - set->socks5auth = CURLAUTH_BASIC | CURLAUTH_GSSAPI; -#endif - - /* make libcurl quiet by default: */ - set->hide_progress = TRUE; /* CURLOPT_NOPROGRESS changes these */ - - Curl_mime_initpart(&set->mimepost, data); - - /* - * libcurl 7.10 introduced SSL verification *by default*! This needs to be - * switched off unless wanted. - */ -#ifndef CURL_DISABLE_DOH - set->doh_verifyhost = TRUE; - set->doh_verifypeer = TRUE; -#endif - set->ssl.primary.verifypeer = TRUE; - set->ssl.primary.verifyhost = TRUE; -#ifdef USE_TLS_SRP - set->ssl.primary.authtype = CURL_TLSAUTH_NONE; -#endif - /* defaults to any auth type */ - set->ssh_auth_types = CURLSSH_AUTH_DEFAULT; - set->ssl.primary.sessionid = TRUE; /* session ID caching enabled by - default */ -#ifndef CURL_DISABLE_PROXY - set->proxy_ssl = set->ssl; -#endif - - set->new_file_perms = 0644; /* Default permissions */ - set->new_directory_perms = 0755; /* Default permissions */ - set->allowed_protocols = (curl_prot_t) CURLPROTO_ALL; - set->redir_protocols = CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | - CURLPROTO_FTPS; - -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - /* - * disallow unprotected protection negotiation NEC reference implementation - * seem not to follow rfc1961 section 4.3/4.4 - */ - set->socks5_gssapi_nec = FALSE; -#endif - - /* Set the default CA cert bundle/path detected/specified at build time. - * - * If Schannel is the selected SSL backend then these locations are - * ignored. We allow setting CA location for schannel only when explicitly - * specified by the user via CURLOPT_CAINFO / --cacert. - */ - if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { -#if defined(CURL_CA_BUNDLE) - result = Curl_setstropt(&set->str[STRING_SSL_CAFILE], CURL_CA_BUNDLE); - if(result) - return result; - - result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_PROXY], - CURL_CA_BUNDLE); - if(result) - return result; -#endif -#if defined(CURL_CA_PATH) - result = Curl_setstropt(&set->str[STRING_SSL_CAPATH], CURL_CA_PATH); - if(result) - return result; - - result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_PROXY], CURL_CA_PATH); - if(result) - return result; -#endif - } - -#ifndef CURL_DISABLE_FTP - set->wildcard_enabled = FALSE; - set->chunk_bgn = ZERO_NULL; - set->chunk_end = ZERO_NULL; - set->fnmatch = ZERO_NULL; -#endif - set->tcp_keepalive = FALSE; - set->tcp_keepintvl = 60; - set->tcp_keepidle = 60; - set->tcp_fastopen = FALSE; - set->tcp_nodelay = TRUE; - set->ssl_enable_alpn = TRUE; - set->expect_100_timeout = 1000L; /* Wait for a second by default. */ - set->sep_headers = TRUE; /* separated header lists by default */ - set->buffer_size = READBUFFER_SIZE; - set->upload_buffer_size = UPLOADBUFFER_DEFAULT; - set->happy_eyeballs_timeout = CURL_HET_DEFAULT; - set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT; - set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ - set->maxage_conn = 118; - set->maxlifetime_conn = 0; - set->http09_allowed = FALSE; - set->httpwant = -#ifdef USE_HTTP2 - CURL_HTTP_VERSION_2TLS -#else - CURL_HTTP_VERSION_1_1 -#endif - ; - Curl_http2_init_userset(set); - return result; -} - -/** - * Curl_open() - * - * @param curl is a pointer to a sessionhandle pointer that gets set by this - * function. - * @return CURLcode - */ - -CURLcode Curl_open(struct Curl_easy **curl) -{ - CURLcode result; - struct Curl_easy *data; - - /* Very simple start-up: alloc the struct, init it with zeroes and return */ - data = calloc(1, sizeof(struct Curl_easy)); - if(!data) { - /* this is a very serious error */ - DEBUGF(fprintf(stderr, "Error: calloc of Curl_easy failed\n")); - return CURLE_OUT_OF_MEMORY; - } - - data->magic = CURLEASY_MAGIC_NUMBER; - - result = Curl_resolver_init(data, &data->state.async.resolver); - if(result) { - DEBUGF(fprintf(stderr, "Error: resolver_init failed\n")); - free(data); - return result; - } - - result = Curl_init_userdefined(data); - if(!result) { - Curl_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER); - Curl_initinfo(data); - - /* most recent connection is not yet defined */ - data->state.lastconnect_id = -1; - - data->progress.flags |= PGRS_HIDE; - data->state.current_speed = -1; /* init to negative == impossible */ - } - - if(result) { - Curl_resolver_cleanup(data->state.async.resolver); - Curl_dyn_free(&data->state.headerb); - Curl_freeset(data); - free(data); - data = NULL; - } - else - *curl = data; - - return result; -} - -#ifdef USE_RECV_BEFORE_SEND_WORKAROUND -static void conn_reset_postponed_data(struct connectdata *conn, int num) -{ - struct postponed_data * const psnd = &(conn->postponed[num]); - if(psnd->buffer) { - DEBUGASSERT(psnd->allocated_size > 0); - DEBUGASSERT(psnd->recv_size <= psnd->allocated_size); - DEBUGASSERT(psnd->recv_size ? - (psnd->recv_processed < psnd->recv_size) : - (psnd->recv_processed == 0)); - DEBUGASSERT(psnd->bindsock != CURL_SOCKET_BAD); - free(psnd->buffer); - psnd->buffer = NULL; - psnd->allocated_size = 0; - psnd->recv_size = 0; - psnd->recv_processed = 0; -#ifdef DEBUGBUILD - psnd->bindsock = CURL_SOCKET_BAD; /* used only for DEBUGASSERT */ -#endif /* DEBUGBUILD */ - } - else { - DEBUGASSERT(psnd->allocated_size == 0); - DEBUGASSERT(psnd->recv_size == 0); - DEBUGASSERT(psnd->recv_processed == 0); - DEBUGASSERT(psnd->bindsock == CURL_SOCKET_BAD); - } -} - -static void conn_reset_all_postponed_data(struct connectdata *conn) -{ - conn_reset_postponed_data(conn, 0); - conn_reset_postponed_data(conn, 1); -} -#else /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ -/* Use "do-nothing" macro instead of function when workaround not used */ -#define conn_reset_all_postponed_data(c) do {} while(0) -#endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ - - -static void conn_shutdown(struct Curl_easy *data, struct connectdata *conn) -{ - DEBUGASSERT(conn); - DEBUGASSERT(data); - infof(data, "Closing connection %ld", conn->connection_id); - - /* possible left-overs from the async name resolvers */ - Curl_resolver_cancel(data); - - /* close the SSL stuff before we close any sockets since they will/may - write to the sockets */ - Curl_ssl_close(data, conn, FIRSTSOCKET); -#ifndef CURL_DISABLE_FTP - Curl_ssl_close(data, conn, SECONDARYSOCKET); -#endif - - /* close possibly still open sockets */ - if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) - Curl_closesocket(data, conn, conn->sock[SECONDARYSOCKET]); - if(CURL_SOCKET_BAD != conn->sock[FIRSTSOCKET]) - Curl_closesocket(data, conn, conn->sock[FIRSTSOCKET]); - if(CURL_SOCKET_BAD != conn->tempsock[0]) - Curl_closesocket(data, conn, conn->tempsock[0]); - if(CURL_SOCKET_BAD != conn->tempsock[1]) - Curl_closesocket(data, conn, conn->tempsock[1]); -} - -static void conn_free(struct connectdata *conn) -{ - DEBUGASSERT(conn); - - Curl_free_idnconverted_hostname(&conn->host); - Curl_free_idnconverted_hostname(&conn->conn_to_host); -#ifndef CURL_DISABLE_PROXY - Curl_free_idnconverted_hostname(&conn->http_proxy.host); - Curl_free_idnconverted_hostname(&conn->socks_proxy.host); - Curl_safefree(conn->http_proxy.user); - Curl_safefree(conn->socks_proxy.user); - Curl_safefree(conn->http_proxy.passwd); - Curl_safefree(conn->socks_proxy.passwd); - Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */ - Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */ - Curl_free_primary_ssl_config(&conn->proxy_ssl_config); -#endif - Curl_safefree(conn->user); - Curl_safefree(conn->passwd); - Curl_safefree(conn->sasl_authzid); - Curl_safefree(conn->options); - Curl_safefree(conn->oauth_bearer); - Curl_dyn_free(&conn->trailer); - Curl_safefree(conn->host.rawalloc); /* host name buffer */ - Curl_safefree(conn->conn_to_host.rawalloc); /* host name buffer */ - Curl_safefree(conn->hostname_resolve); - Curl_safefree(conn->secondaryhostname); - Curl_safefree(conn->connect_state); - - conn_reset_all_postponed_data(conn); - Curl_llist_destroy(&conn->easyq, NULL); - Curl_safefree(conn->localdev); - Curl_free_primary_ssl_config(&conn->ssl_config); - -#ifdef USE_UNIX_SOCKETS - Curl_safefree(conn->unix_domain_socket); -#endif - -#ifdef USE_SSL - Curl_safefree(conn->ssl_extra); -#endif - free(conn); /* free all the connection oriented data */ -} - -/* - * Disconnects the given connection. Note the connection may not be the - * primary connection, like when freeing room in the connection cache or - * killing of a dead old connection. - * - * A connection needs an easy handle when closing down. We support this passed - * in separately since the connection to get closed here is often already - * disassociated from an easy handle. - * - * This function MUST NOT reset state in the Curl_easy struct if that - * isn't strictly bound to the life-time of *this* particular connection. - * - */ - -void Curl_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead_connection) -{ - /* there must be a connection to close */ - DEBUGASSERT(conn); - - /* it must be removed from the connection cache */ - DEBUGASSERT(!conn->bundle); - - /* there must be an associated transfer */ - DEBUGASSERT(data); - - /* the transfer must be detached from the connection */ - DEBUGASSERT(!data->conn); - - /* - * If this connection isn't marked to force-close, leave it open if there - * are other users of it - */ - if(CONN_INUSE(conn) && !dead_connection) { - DEBUGF(infof(data, "Curl_disconnect when inuse: %zu", CONN_INUSE(conn))); - return; - } - - if(conn->dns_entry) { - Curl_resolv_unlock(data, conn->dns_entry); - conn->dns_entry = NULL; - } - - /* Cleanup NTLM connection-related data */ - Curl_http_auth_cleanup_ntlm(conn); - - /* Cleanup NEGOTIATE connection-related data */ - Curl_http_auth_cleanup_negotiate(conn); - - if(conn->connect_only) - /* treat the connection as dead in CONNECT_ONLY situations */ - dead_connection = TRUE; - - /* temporarily attach the connection to this transfer handle for the - disconnect and shutdown */ - Curl_attach_connection(data, conn); - - if(conn->handler->disconnect) - /* This is set if protocol-specific cleanups should be made */ - conn->handler->disconnect(data, conn, dead_connection); - - conn_shutdown(data, conn); - - /* detach it again */ - Curl_detach_connection(data); - - conn_free(conn); -} - -/* - * This function should return TRUE if the socket is to be assumed to - * be dead. Most commonly this happens when the server has closed the - * connection due to inactivity. - */ -static bool SocketIsDead(curl_socket_t sock) -{ - int sval; - bool ret_val = TRUE; - - sval = SOCKET_READABLE(sock, 0); - if(sval == 0) - /* timeout */ - ret_val = FALSE; - - return ret_val; -} - -/* - * IsMultiplexingPossible() - * - * Return a bitmask with the available multiplexing options for the given - * requested connection. - */ -static int IsMultiplexingPossible(const struct Curl_easy *handle, - const struct connectdata *conn) -{ - int avail = 0; - - /* If a HTTP protocol and multiplexing is enabled */ - if((conn->handler->protocol & PROTO_FAMILY_HTTP) && - (!conn->bits.protoconnstart || !conn->bits.close)) { - - if(Curl_multiplex_wanted(handle->multi) && - (handle->state.httpwant >= CURL_HTTP_VERSION_2)) - /* allows HTTP/2 */ - avail |= CURLPIPE_MULTIPLEX; - } - return avail; -} - -#ifndef CURL_DISABLE_PROXY -static bool -proxy_info_matches(const struct proxy_info *data, - const struct proxy_info *needle) -{ - if((data->proxytype == needle->proxytype) && - (data->port == needle->port) && - Curl_safe_strcasecompare(data->host.name, needle->host.name)) - return TRUE; - - return FALSE; -} - -static bool -socks_proxy_info_matches(const struct proxy_info *data, - const struct proxy_info *needle) -{ - if(!proxy_info_matches(data, needle)) - return FALSE; - - /* the user information is case-sensitive - or at least it is not defined as case-insensitive - see https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1 */ - - /* curl_strequal does a case insensitive comparison, - so do not use it here! */ - if(Curl_timestrcmp(data->user, needle->user) || - Curl_timestrcmp(data->passwd, needle->passwd)) - return FALSE; - return TRUE; -} -#else -/* disabled, won't get called */ -#define proxy_info_matches(x,y) FALSE -#define socks_proxy_info_matches(x,y) FALSE -#endif - -/* A connection has to have been idle for a shorter time than 'maxage_conn' - (the success rate is just too low after this), or created less than - 'maxlifetime_conn' ago, to be subject for reuse. */ - -static bool conn_maxage(struct Curl_easy *data, - struct connectdata *conn, - struct curltime now) -{ - timediff_t idletime, lifetime; - - idletime = Curl_timediff(now, conn->lastused); - idletime /= 1000; /* integer seconds is fine */ - - if(idletime > data->set.maxage_conn) { - infof(data, "Too old connection (%ld seconds idle), disconnect it", - idletime); - return TRUE; - } - - lifetime = Curl_timediff(now, conn->created); - lifetime /= 1000; /* integer seconds is fine */ - - if(data->set.maxlifetime_conn && lifetime > data->set.maxlifetime_conn) { - infof(data, - "Too old connection (%ld seconds since creation), disconnect it", - lifetime); - return TRUE; - } - - - return FALSE; -} - -/* - * This function checks if the given connection is dead and extracts it from - * the connection cache if so. - * - * When this is called as a Curl_conncache_foreach() callback, the connection - * cache lock is held! - * - * Returns TRUE if the connection was dead and extracted. - */ -static bool extract_if_dead(struct connectdata *conn, - struct Curl_easy *data) -{ - if(!CONN_INUSE(conn)) { - /* The check for a dead socket makes sense only if the connection isn't in - use */ - bool dead; - struct curltime now = Curl_now(); - if(conn_maxage(data, conn, now)) { - /* avoid check if already too old */ - dead = TRUE; - } - else if(conn->handler->connection_check) { - /* The protocol has a special method for checking the state of the - connection. Use it to check if the connection is dead. */ - unsigned int state; - - /* briefly attach the connection to this transfer for the purpose of - checking it */ - Curl_attach_connection(data, conn); - - state = conn->handler->connection_check(data, conn, CONNCHECK_ISDEAD); - dead = (state & CONNRESULT_DEAD); - /* detach the connection again */ - Curl_detach_connection(data); - - } - else { - /* Use the general method for determining the death of a connection */ - dead = SocketIsDead(conn->sock[FIRSTSOCKET]); - } - - if(dead) { - infof(data, "Connection %ld seems to be dead", conn->connection_id); - Curl_conncache_remove_conn(data, conn, FALSE); - return TRUE; - } - } - return FALSE; -} - -struct prunedead { - struct Curl_easy *data; - struct connectdata *extracted; -}; - -/* - * Wrapper to use extract_if_dead() function in Curl_conncache_foreach() - * - */ -static int call_extract_if_dead(struct Curl_easy *data, - struct connectdata *conn, void *param) -{ - struct prunedead *p = (struct prunedead *)param; - if(extract_if_dead(conn, data)) { - /* stop the iteration here, pass back the connection that was extracted */ - p->extracted = conn; - return 1; - } - return 0; /* continue iteration */ -} - -/* - * This function scans the connection cache 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. - */ -static void prune_dead_connections(struct Curl_easy *data) -{ - struct curltime now = Curl_now(); - timediff_t elapsed; - - DEBUGASSERT(!data->conn); /* no connection */ - CONNCACHE_LOCK(data); - elapsed = - Curl_timediff(now, data->state.conn_cache->last_cleanup); - CONNCACHE_UNLOCK(data); - - if(elapsed >= 1000L) { - struct prunedead prune; - prune.data = data; - prune.extracted = NULL; - while(Curl_conncache_foreach(data, data->state.conn_cache, &prune, - call_extract_if_dead)) { - /* unlocked */ - - /* remove connection from cache */ - Curl_conncache_remove_conn(data, prune.extracted, TRUE); - - /* disconnect it */ - Curl_disconnect(data, prune.extracted, TRUE); - } - CONNCACHE_LOCK(data); - data->state.conn_cache->last_cleanup = now; - CONNCACHE_UNLOCK(data); - } -} - -#ifdef USE_SSH -static bool ssh_config_matches(struct connectdata *one, - struct connectdata *two) -{ - return (Curl_safecmp(one->proto.sshc.rsa, two->proto.sshc.rsa) && - Curl_safecmp(one->proto.sshc.rsa_pub, two->proto.sshc.rsa_pub)); -} -#else -#define ssh_config_matches(x,y) FALSE -#endif - -/* - * Given one filled in connection struct (named needle), this function should - * detect if there already is one that has all the significant details - * exactly the same and thus should be used instead. - * - * If there is a match, this function returns TRUE - and has marked the - * connection as 'in-use'. It must later be called with ConnectionDone() to - * return back to 'idle' (unused) state. - * - * The force_reuse flag is set if the connection must be used. - */ -static bool -ConnectionExists(struct Curl_easy *data, - struct connectdata *needle, - struct connectdata **usethis, - bool *force_reuse, - bool *waitpipe) -{ - struct connectdata *check; - struct connectdata *chosen = 0; - bool foundPendingCandidate = FALSE; - bool canmultiplex = IsMultiplexingPossible(data, needle); - struct connectbundle *bundle; - -#ifdef USE_NTLM - bool wantNTLMhttp = ((data->state.authhost.want & - (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) && - (needle->handler->protocol & PROTO_FAMILY_HTTP)); -#ifndef CURL_DISABLE_PROXY - bool wantProxyNTLMhttp = (needle->bits.proxy_user_passwd && - ((data->state.authproxy.want & - (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) && - (needle->handler->protocol & PROTO_FAMILY_HTTP))); -#else - bool wantProxyNTLMhttp = FALSE; -#endif -#endif - - *force_reuse = FALSE; - *waitpipe = FALSE; - - /* Look up the bundle with all the connections to this particular host. - Locks the connection cache, beware of early returns! */ - bundle = Curl_conncache_find_bundle(data, needle, data->state.conn_cache); - if(bundle) { - /* Max pipe length is zero (unlimited) for multiplexed connections */ - struct Curl_llist_element *curr; - - infof(data, "Found bundle for host: %p [%s]", - (void *)bundle, (bundle->multiuse == BUNDLE_MULTIPLEX ? - "can multiplex" : "serially")); - - /* We can't multiplex if we don't know anything about the server */ - if(canmultiplex) { - if(bundle->multiuse == BUNDLE_UNKNOWN) { - if(data->set.pipewait) { - infof(data, "Server doesn't support multiplex yet, wait"); - *waitpipe = TRUE; - CONNCACHE_UNLOCK(data); - return FALSE; /* no re-use */ - } - - infof(data, "Server doesn't support multiplex (yet)"); - canmultiplex = FALSE; - } - if((bundle->multiuse == BUNDLE_MULTIPLEX) && - !Curl_multiplex_wanted(data->multi)) { - infof(data, "Could multiplex, but not asked to"); - canmultiplex = FALSE; - } - if(bundle->multiuse == BUNDLE_NO_MULTIUSE) { - infof(data, "Can not multiplex, even if we wanted to"); - canmultiplex = FALSE; - } - } - - curr = bundle->conn_list.head; - while(curr) { - bool match = FALSE; - size_t multiplexed = 0; - - /* - * Note that if we use a HTTP proxy in normal mode (no tunneling), we - * check connections to that proxy and not to the actual remote server. - */ - check = curr->ptr; - curr = curr->next; - - if(check->connect_only || check->bits.close) - /* connect-only or to-be-closed connections will not be reused */ - continue; - - if(extract_if_dead(check, data)) { - /* disconnect it */ - Curl_disconnect(data, check, TRUE); - continue; - } - - if(data->set.ipver != CURL_IPRESOLVE_WHATEVER - && data->set.ipver != check->ip_version) { - /* skip because the connection is not via the requested IP version */ - continue; - } - - if(bundle->multiuse == BUNDLE_MULTIPLEX) - multiplexed = CONN_INUSE(check); - - if(!canmultiplex) { - if(multiplexed) { - /* can only happen within multi handles, and means that another easy - handle is using this connection */ - continue; - } - - if(Curl_resolver_asynch()) { - /* primary_ip[0] is NUL only if the resolving of the name hasn't - completed yet and until then we don't re-use this connection */ - if(!check->primary_ip[0]) { - infof(data, - "Connection #%ld is still name resolving, can't reuse", - check->connection_id); - continue; - } - } - - if(check->sock[FIRSTSOCKET] == CURL_SOCKET_BAD) { - foundPendingCandidate = TRUE; - /* Don't pick a connection that hasn't connected yet */ - infof(data, "Connection #%ld isn't open enough, can't reuse", - check->connection_id); - continue; - } - } - -#ifdef USE_UNIX_SOCKETS - if(needle->unix_domain_socket) { - if(!check->unix_domain_socket) - continue; - if(strcmp(needle->unix_domain_socket, check->unix_domain_socket)) - continue; - if(needle->bits.abstract_unix_socket != - check->bits.abstract_unix_socket) - continue; - } - else if(check->unix_domain_socket) - continue; -#endif - - if((needle->handler->flags&PROTOPT_SSL) != - (check->handler->flags&PROTOPT_SSL)) - /* don't do mixed SSL and non-SSL connections */ - if(get_protocol_family(check->handler) != - needle->handler->protocol || !check->bits.tls_upgraded) - /* except protocols that have been upgraded via TLS */ - continue; - -#ifndef CURL_DISABLE_PROXY - if(needle->bits.httpproxy != check->bits.httpproxy || - needle->bits.socksproxy != check->bits.socksproxy) - continue; - - if(needle->bits.socksproxy && - !socks_proxy_info_matches(&needle->socks_proxy, - &check->socks_proxy)) - continue; -#endif - if(needle->bits.conn_to_host != check->bits.conn_to_host) - /* don't mix connections that use the "connect to host" feature and - * connections that don't use this feature */ - continue; - - if(needle->bits.conn_to_port != check->bits.conn_to_port) - /* don't mix connections that use the "connect to port" feature and - * connections that don't use this feature */ - continue; - -#ifndef CURL_DISABLE_PROXY - if(needle->bits.httpproxy) { - if(!proxy_info_matches(&needle->http_proxy, &check->http_proxy)) - continue; - - if(needle->bits.tunnel_proxy != check->bits.tunnel_proxy) - continue; - - if(needle->http_proxy.proxytype == CURLPROXY_HTTPS) { - /* use https proxy */ - if(needle->handler->flags&PROTOPT_SSL) { - /* use double layer ssl */ - if(!Curl_ssl_config_matches(&needle->proxy_ssl_config, - &check->proxy_ssl_config)) - continue; - if(check->proxy_ssl[FIRSTSOCKET].state != ssl_connection_complete) - continue; - } - - if(!Curl_ssl_config_matches(&needle->ssl_config, - &check->ssl_config)) - continue; - if(check->ssl[FIRSTSOCKET].state != ssl_connection_complete) - continue; - } - } -#endif - - if(!canmultiplex && CONN_INUSE(check)) - /* this request can't be multiplexed but the checked connection is - already in use so we skip it */ - continue; - - if(CONN_INUSE(check)) { - /* Subject for multiplex use if 'checks' belongs to the same multi - handle as 'data' is. */ - struct Curl_llist_element *e = check->easyq.head; - struct Curl_easy *entry = e->ptr; - if(entry->multi != data->multi) - continue; - } - - if(needle->localdev || needle->localport) { - /* If we are bound to a specific local end (IP+port), we must not - re-use a random other one, although if we didn't ask for a - particular one we can reuse one that was bound. - - This comparison is a bit rough and too strict. Since the input - parameters can be specified in numerous ways and still end up the - same it would take a lot of processing to make it really accurate. - Instead, this matching will assume that re-uses of bound connections - will most likely also re-use the exact same binding parameters and - missing out a few edge cases shouldn't hurt anyone very much. - */ - if((check->localport != needle->localport) || - (check->localportrange != needle->localportrange) || - (needle->localdev && - (!check->localdev || strcmp(check->localdev, needle->localdev)))) - continue; - } - - if(!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) { - /* This protocol requires credentials per connection, - so verify that we're using the same name and password as well */ - if(Curl_timestrcmp(needle->user, check->user) || - Curl_timestrcmp(needle->passwd, check->passwd) || - Curl_timestrcmp(needle->sasl_authzid, check->sasl_authzid) || - Curl_timestrcmp(needle->oauth_bearer, check->oauth_bearer)) { - /* one of them was different */ - continue; - } - } - - /* If multiplexing isn't enabled on the h2 connection and h1 is - explicitly requested, handle it: */ - if((needle->handler->protocol & PROTO_FAMILY_HTTP) && - (check->httpversion >= 20) && - (data->state.httpwant < CURL_HTTP_VERSION_2_0)) - continue; - - if(get_protocol_family(needle->handler) == PROTO_FAMILY_SSH) { - if(!ssh_config_matches(needle, check)) - continue; - } - - if((needle->handler->flags&PROTOPT_SSL) -#ifndef CURL_DISABLE_PROXY - || !needle->bits.httpproxy || needle->bits.tunnel_proxy -#endif - ) { - /* The requested connection does not use a HTTP proxy or it uses SSL or - it is a non-SSL protocol tunneled or it is a non-SSL protocol which - is allowed to be upgraded via TLS */ - - if((strcasecompare(needle->handler->scheme, check->handler->scheme) || - (get_protocol_family(check->handler) == - needle->handler->protocol && check->bits.tls_upgraded)) && - (!needle->bits.conn_to_host || strcasecompare( - needle->conn_to_host.name, check->conn_to_host.name)) && - (!needle->bits.conn_to_port || - needle->conn_to_port == check->conn_to_port) && - strcasecompare(needle->host.name, check->host.name) && - needle->remote_port == check->remote_port) { - /* The schemes match or the protocol family is the same and the - previous connection was TLS upgraded, and the hostname and host - port match */ - if(needle->handler->flags & PROTOPT_SSL) { - /* This is a SSL connection so verify that we're using the same - SSL options as well */ - if(!Curl_ssl_config_matches(&needle->ssl_config, - &check->ssl_config)) { - DEBUGF(infof(data, - "Connection #%ld has different SSL parameters, " - "can't reuse", - check->connection_id)); - continue; - } - if(check->ssl[FIRSTSOCKET].state != ssl_connection_complete) { - foundPendingCandidate = TRUE; - DEBUGF(infof(data, - "Connection #%ld has not started SSL connect, " - "can't reuse", - check->connection_id)); - continue; - } - } - match = TRUE; - } - } - else { - /* The requested connection is using the same HTTP proxy in normal - mode (no tunneling) */ - match = TRUE; - } - - if(match) { -#if defined(USE_NTLM) - /* If we are looking for an HTTP+NTLM connection, check if this is - already authenticating with the right credentials. If not, keep - looking so that we can reuse NTLM connections if - possible. (Especially we must not reuse the same connection if - partway through a handshake!) */ - if(wantNTLMhttp) { - if(Curl_timestrcmp(needle->user, check->user) || - Curl_timestrcmp(needle->passwd, check->passwd)) { - - /* we prefer a credential match, but this is at least a connection - that can be reused and "upgraded" to NTLM */ - if(check->http_ntlm_state == NTLMSTATE_NONE) - chosen = check; - continue; - } - } - else if(check->http_ntlm_state != NTLMSTATE_NONE) { - /* Connection is using NTLM auth but we don't want NTLM */ - continue; - } - -#ifndef CURL_DISABLE_PROXY - /* Same for Proxy NTLM authentication */ - if(wantProxyNTLMhttp) { - /* Both check->http_proxy.user and check->http_proxy.passwd can be - * NULL */ - if(!check->http_proxy.user || !check->http_proxy.passwd) - continue; - - if(Curl_timestrcmp(needle->http_proxy.user, - check->http_proxy.user) || - Curl_timestrcmp(needle->http_proxy.passwd, - check->http_proxy.passwd)) - continue; - } - else if(check->proxy_ntlm_state != NTLMSTATE_NONE) { - /* Proxy connection is using NTLM auth but we don't want NTLM */ - continue; - } -#endif - if(wantNTLMhttp || wantProxyNTLMhttp) { - /* Credentials are already checked, we can use this connection */ - chosen = check; - - if((wantNTLMhttp && - (check->http_ntlm_state != NTLMSTATE_NONE)) || - (wantProxyNTLMhttp && - (check->proxy_ntlm_state != NTLMSTATE_NONE))) { - /* We must use this connection, no other */ - *force_reuse = TRUE; - break; - } - - /* Continue look up for a better connection */ - continue; - } -#endif - if(canmultiplex) { - /* We can multiplex if we want to. Let's continue looking for - the optimal connection to use. */ - - if(!multiplexed) { - /* We have the optimal connection. Let's stop looking. */ - chosen = check; - break; - } - -#ifdef USE_NGHTTP2 - /* If multiplexed, make sure we don't go over concurrency limit */ - if(check->bits.multiplex) { - /* Multiplexed connections can only be HTTP/2 for now */ - struct http_conn *httpc = &check->proto.httpc; - if(multiplexed >= httpc->settings.max_concurrent_streams) { - infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)", - multiplexed); - continue; - } - else if(multiplexed >= - Curl_multi_max_concurrent_streams(data->multi)) { - infof(data, "client side MAX_CONCURRENT_STREAMS reached" - ", skip (%zu)", - multiplexed); - continue; - } - } -#endif - /* When not multiplexed, we have a match here! */ - chosen = check; - infof(data, "Multiplexed connection found"); - break; - } - else { - /* We have found a connection. Let's stop searching. */ - chosen = check; - break; - } - } - } - } - - if(chosen) { - /* mark it as used before releasing the lock */ - Curl_attach_connection(data, chosen); - CONNCACHE_UNLOCK(data); - *usethis = chosen; - return TRUE; /* yes, we found one to use! */ - } - CONNCACHE_UNLOCK(data); - - if(foundPendingCandidate && data->set.pipewait) { - infof(data, - "Found pending candidate for reuse and CURLOPT_PIPEWAIT is set"); - *waitpipe = TRUE; - } - - return FALSE; /* no matching connecting exists */ -} - -/* - * verboseconnect() displays verbose information after a connect - */ -#ifndef CURL_DISABLE_VERBOSE_STRINGS -void Curl_verboseconnect(struct Curl_easy *data, - struct connectdata *conn) -{ - if(data->set.verbose) - infof(data, "Connected to %s (%s) port %u (#%ld)", -#ifndef CURL_DISABLE_PROXY - conn->bits.socksproxy ? conn->socks_proxy.host.dispname : - conn->bits.httpproxy ? conn->http_proxy.host.dispname : -#endif - conn->bits.conn_to_host ? conn->conn_to_host.dispname : - conn->host.dispname, - conn->primary_ip, conn->port, conn->connection_id); -} -#endif - -/* - * Helpers for IDNA conversions. - */ -bool Curl_is_ASCII_name(const char *hostname) -{ - /* get an UNSIGNED local version of the pointer */ - const unsigned char *ch = (const unsigned char *)hostname; - - if(!hostname) /* bad input, consider it ASCII! */ - return TRUE; - - while(*ch) { - if(*ch++ & 0x80) - return FALSE; - } - return TRUE; -} - -/* - * Perform any necessary IDN conversion of hostname - */ -CURLcode Curl_idnconvert_hostname(struct Curl_easy *data, - struct hostname *host) -{ -#ifndef USE_LIBIDN2 - (void)data; - (void)data; -#elif defined(CURL_DISABLE_VERBOSE_STRINGS) - (void)data; -#endif - - /* set the name we use to display the host name */ - host->dispname = host->name; - - /* Check name for non-ASCII and convert hostname to ACE form if we can */ - if(!Curl_is_ASCII_name(host->name)) { -#ifdef USE_LIBIDN2 - if(idn2_check_version(IDN2_VERSION)) { - char *ace_hostname = NULL; -#if IDN2_VERSION_NUMBER >= 0x00140000 - /* IDN2_NFC_INPUT: Normalize input string using normalization form C. - IDN2_NONTRANSITIONAL: Perform Unicode TR46 non-transitional - processing. */ - int flags = IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL; -#else - int flags = IDN2_NFC_INPUT; -#endif - int rc = IDN2_LOOKUP(host->name, &ace_hostname, flags); - if(rc != IDN2_OK) - /* fallback to TR46 Transitional mode for better IDNA2003 - compatibility */ - rc = IDN2_LOOKUP(host->name, &ace_hostname, - IDN2_TRANSITIONAL); - if(rc == IDN2_OK) { - host->encalloc = (char *)ace_hostname; - /* change the name pointer to point to the encoded hostname */ - host->name = host->encalloc; - } - else { - failf(data, "Failed to convert %s to ACE; %s", host->name, - idn2_strerror(rc)); - return CURLE_URL_MALFORMAT; - } - } -#elif defined(USE_WIN32_IDN) - char *ace_hostname = NULL; - - if(Curl_win32_idn_to_ascii(host->name, &ace_hostname)) { - host->encalloc = ace_hostname; - /* change the name pointer to point to the encoded hostname */ - host->name = host->encalloc; - } - else { - char buffer[STRERROR_LEN]; - failf(data, "Failed to convert %s to ACE; %s", host->name, - Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - return CURLE_URL_MALFORMAT; - } -#else - infof(data, "IDN support not present, can't parse Unicode domains"); -#endif - } - return CURLE_OK; -} - -/* - * Frees data allocated by idnconvert_hostname() - */ -void Curl_free_idnconverted_hostname(struct hostname *host) -{ -#if defined(USE_LIBIDN2) - if(host->encalloc) { - idn2_free(host->encalloc); /* must be freed with idn2_free() since this was - allocated by libidn */ - host->encalloc = NULL; - } -#elif defined(USE_WIN32_IDN) - free(host->encalloc); /* must be freed with free() since this was - allocated by Curl_win32_idn_to_ascii */ - host->encalloc = NULL; -#else - (void)host; -#endif -} - -/* - * Allocate and initialize a new connectdata object. - */ -static struct connectdata *allocate_conn(struct Curl_easy *data) -{ - struct connectdata *conn = calloc(1, sizeof(struct connectdata)); - if(!conn) - return NULL; - -#ifdef USE_SSL - /* The SSL backend-specific data (ssl_backend_data) objects are allocated as - a separate array to ensure suitable alignment. - Note that these backend pointers can be swapped by vtls (eg ssl backend - data becomes proxy backend data). */ - { - size_t onesize = Curl_ssl->sizeof_ssl_backend_data; - size_t totalsize = onesize; - char *ssl; - -#ifndef CURL_DISABLE_FTP - totalsize *= 2; -#endif -#ifndef CURL_DISABLE_PROXY - totalsize *= 2; -#endif - - ssl = calloc(1, totalsize); - if(!ssl) { - free(conn); - return NULL; - } - conn->ssl_extra = ssl; - conn->ssl[FIRSTSOCKET].backend = (void *)ssl; -#ifndef CURL_DISABLE_FTP - ssl += onesize; - conn->ssl[SECONDARYSOCKET].backend = (void *)ssl; -#endif -#ifndef CURL_DISABLE_PROXY - ssl += onesize; - conn->proxy_ssl[FIRSTSOCKET].backend = (void *)ssl; -#ifndef CURL_DISABLE_FTP - ssl += onesize; - conn->proxy_ssl[SECONDARYSOCKET].backend = (void *)ssl; -#endif -#endif - } -#endif - - conn->handler = &Curl_handler_dummy; /* Be sure we have a handler defined - already from start to avoid NULL - situations and checks */ - - /* and we setup a few fields in case we end up actually using this struct */ - - conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ - conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ - conn->tempsock[0] = CURL_SOCKET_BAD; /* no file descriptor */ - conn->tempsock[1] = CURL_SOCKET_BAD; /* no file descriptor */ - conn->connection_id = -1; /* no ID */ - conn->port = -1; /* unknown at this point */ - conn->remote_port = -1; /* unknown at this point */ -#if defined(USE_RECV_BEFORE_SEND_WORKAROUND) && defined(DEBUGBUILD) - conn->postponed[0].bindsock = CURL_SOCKET_BAD; /* no file descriptor */ - conn->postponed[1].bindsock = CURL_SOCKET_BAD; /* no file descriptor */ -#endif /* USE_RECV_BEFORE_SEND_WORKAROUND && DEBUGBUILD */ - - /* Default protocol-independent behavior doesn't support persistent - connections, so we set this to force-close. Protocols that support - this need to set this to FALSE in their "curl_do" functions. */ - connclose(conn, "Default to force-close"); - - /* Store creation time to help future close decision making */ - conn->created = Curl_now(); - - /* Store current time to give a baseline to keepalive connection times. */ - conn->keepalive = Curl_now(); - -#ifndef CURL_DISABLE_PROXY - conn->http_proxy.proxytype = data->set.proxytype; - conn->socks_proxy.proxytype = CURLPROXY_SOCKS4; - - /* note that these two proxy bits are now just on what looks to be - requested, they may be altered down the road */ - conn->bits.proxy = (data->set.str[STRING_PROXY] && - *data->set.str[STRING_PROXY]) ? TRUE : FALSE; - conn->bits.httpproxy = (conn->bits.proxy && - (conn->http_proxy.proxytype == CURLPROXY_HTTP || - conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0 || - conn->http_proxy.proxytype == CURLPROXY_HTTPS)) ? - TRUE : FALSE; - conn->bits.socksproxy = (conn->bits.proxy && - !conn->bits.httpproxy) ? TRUE : FALSE; - - if(data->set.str[STRING_PRE_PROXY] && *data->set.str[STRING_PRE_PROXY]) { - conn->bits.proxy = TRUE; - conn->bits.socksproxy = TRUE; - } - - conn->bits.proxy_user_passwd = - (data->state.aptr.proxyuser) ? TRUE : FALSE; - conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy; -#endif /* CURL_DISABLE_PROXY */ - -#ifndef CURL_DISABLE_FTP - conn->bits.ftp_use_epsv = data->set.ftp_use_epsv; - conn->bits.ftp_use_eprt = data->set.ftp_use_eprt; -#endif - conn->ssl_config.verifystatus = data->set.ssl.primary.verifystatus; - conn->ssl_config.verifypeer = data->set.ssl.primary.verifypeer; - conn->ssl_config.verifyhost = data->set.ssl.primary.verifyhost; - conn->ssl_config.ssl_options = data->set.ssl.primary.ssl_options; -#ifndef CURL_DISABLE_PROXY - conn->proxy_ssl_config.verifystatus = - data->set.proxy_ssl.primary.verifystatus; - conn->proxy_ssl_config.verifypeer = data->set.proxy_ssl.primary.verifypeer; - conn->proxy_ssl_config.verifyhost = data->set.proxy_ssl.primary.verifyhost; - conn->proxy_ssl_config.ssl_options = data->set.proxy_ssl.primary.ssl_options; -#endif - conn->ip_version = data->set.ipver; - conn->connect_only = data->set.connect_only; - conn->transport = TRNSPRT_TCP; /* most of them are TCP streams */ - -#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \ - defined(NTLM_WB_ENABLED) - conn->ntlm.ntlm_auth_hlpr_socket = CURL_SOCKET_BAD; - conn->proxyntlm.ntlm_auth_hlpr_socket = CURL_SOCKET_BAD; -#endif - - /* Initialize the easy handle list */ - Curl_llist_init(&conn->easyq, NULL); - -#ifdef HAVE_GSSAPI - conn->data_prot = PROT_CLEAR; -#endif - - /* Store the local bind parameters that will be used for this connection */ - if(data->set.str[STRING_DEVICE]) { - conn->localdev = strdup(data->set.str[STRING_DEVICE]); - if(!conn->localdev) - goto error; - } - conn->localportrange = data->set.localportrange; - conn->localport = data->set.localport; - - /* the close socket stuff needs to be copied to the connection struct as - it may live on without (this specific) Curl_easy */ - conn->fclosesocket = data->set.fclosesocket; - conn->closesocket_client = data->set.closesocket_client; - conn->lastused = Curl_now(); /* used now */ - - return conn; - error: - - Curl_llist_destroy(&conn->easyq, NULL); - free(conn->localdev); -#ifdef USE_SSL - free(conn->ssl_extra); -#endif - free(conn); - return NULL; -} - -/* returns the handler if the given scheme is built-in */ -const struct Curl_handler *Curl_builtin_scheme(const char *scheme, - size_t schemelen) -{ - const struct Curl_handler * const *pp; - const struct Curl_handler *p; - /* Scan protocol handler table and match against 'scheme'. The handler may - be changed later when the protocol specific setup function is called. */ - if(schemelen == CURL_ZERO_TERMINATED) - schemelen = strlen(scheme); - for(pp = protocols; (p = *pp) != NULL; pp++) - if(strncasecompare(p->scheme, scheme, schemelen) && !p->scheme[schemelen]) - /* Protocol found in table. */ - return p; - return NULL; /* not found */ -} - - -static CURLcode findprotocol(struct Curl_easy *data, - struct connectdata *conn, - const char *protostr) -{ - const struct Curl_handler *p = Curl_builtin_scheme(protostr, - CURL_ZERO_TERMINATED); - - if(p && /* Protocol found in table. Check if allowed */ - (data->set.allowed_protocols & p->protocol)) { - - /* it is allowed for "normal" request, now do an extra check if this is - the result of a redirect */ - if(data->state.this_is_a_follow && - !(data->set.redir_protocols & p->protocol)) - /* nope, get out */ - ; - else { - /* Perform setup complement if some. */ - conn->handler = conn->given = p; - - /* 'port' and 'remote_port' are set in setup_connection_internals() */ - return CURLE_OK; - } - } - - /* The protocol was not found in the table, but we don't have to assign it - to anything since it is already assigned to a dummy-struct in the - create_conn() function when the connectdata struct is allocated. */ - failf(data, "Protocol \"%s\" not supported or disabled in " LIBCURL_NAME, - protostr); - - return CURLE_UNSUPPORTED_PROTOCOL; -} - - -CURLcode Curl_uc_to_curlcode(CURLUcode uc) -{ - switch(uc) { - default: - return CURLE_URL_MALFORMAT; - case CURLUE_UNSUPPORTED_SCHEME: - return CURLE_UNSUPPORTED_PROTOCOL; - case CURLUE_OUT_OF_MEMORY: - return CURLE_OUT_OF_MEMORY; - case CURLUE_USER_NOT_ALLOWED: - return CURLE_LOGIN_DENIED; - } -} - -#ifdef ENABLE_IPV6 -/* - * If the URL was set with an IPv6 numerical address with a zone id part, set - * the scope_id based on that! - */ - -static void zonefrom_url(CURLU *uh, struct Curl_easy *data, - struct connectdata *conn) -{ - char *zoneid; - CURLUcode uc = curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0); -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif - - if(!uc && zoneid) { - char *endp; - unsigned long scope = strtoul(zoneid, &endp, 10); - if(!*endp && (scope < UINT_MAX)) - /* A plain number, use it directly as a scope id. */ - conn->scope_id = (unsigned int)scope; -#if defined(HAVE_IF_NAMETOINDEX) - else { -#elif defined(WIN32) - else if(Curl_if_nametoindex) { -#endif - -#if defined(HAVE_IF_NAMETOINDEX) || defined(WIN32) - /* Zone identifier is not numeric */ - unsigned int scopeidx = 0; -#if defined(WIN32) - scopeidx = Curl_if_nametoindex(zoneid); -#else - scopeidx = if_nametoindex(zoneid); -#endif - if(!scopeidx) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS - char buffer[STRERROR_LEN]; - infof(data, "Invalid zoneid: %s; %s", zoneid, - Curl_strerror(errno, buffer, sizeof(buffer))); -#endif - } - else - conn->scope_id = scopeidx; - } -#endif /* HAVE_IF_NAMETOINDEX || WIN32 */ - - free(zoneid); - } -} -#else -#define zonefrom_url(a,b,c) Curl_nop_stmt -#endif - -/* - * Parse URL and fill in the relevant members of the connection struct. - */ -static CURLcode parseurlandfillconn(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result; - CURLU *uh; - CURLUcode uc; - char *hostname; - bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow); - - up_free(data); /* cleanup previous leftovers first */ - - /* parse the URL */ - if(use_set_uh) { - uh = data->state.uh = curl_url_dup(data->set.uh); - } - else { - uh = data->state.uh = curl_url(); - } - - if(!uh) - return CURLE_OUT_OF_MEMORY; - - if(data->set.str[STRING_DEFAULT_PROTOCOL] && - !Curl_is_absolute_url(data->state.url, NULL, 0, TRUE)) { - char *url = aprintf("%s://%s", data->set.str[STRING_DEFAULT_PROTOCOL], - data->state.url); - if(!url) - return CURLE_OUT_OF_MEMORY; - if(data->state.url_alloc) - free(data->state.url); - data->state.url = url; - data->state.url_alloc = TRUE; - } - - if(!use_set_uh) { - char *newurl; - uc = curl_url_set(uh, CURLUPART_URL, data->state.url, - CURLU_GUESS_SCHEME | - CURLU_NON_SUPPORT_SCHEME | - (data->set.disallow_username_in_url ? - CURLU_DISALLOW_USER : 0) | - (data->set.path_as_is ? CURLU_PATH_AS_IS : 0)); - if(uc) { - DEBUGF(infof(data, "curl_url_set rejected %s: %s", data->state.url, - curl_url_strerror(uc))); - return Curl_uc_to_curlcode(uc); - } - - /* after it was parsed, get the generated normalized version */ - uc = curl_url_get(uh, CURLUPART_URL, &newurl, 0); - if(uc) - return Curl_uc_to_curlcode(uc); - if(data->state.url_alloc) - free(data->state.url); - data->state.url = newurl; - data->state.url_alloc = TRUE; - } - - uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0); - if(uc) - return Curl_uc_to_curlcode(uc); - - uc = curl_url_get(uh, CURLUPART_HOST, &data->state.up.hostname, 0); - if(uc) { - if(!strcasecompare("file", data->state.up.scheme)) - return CURLE_OUT_OF_MEMORY; - } - else if(strlen(data->state.up.hostname) > MAX_URL_LEN) { - failf(data, "Too long host name (maximum is %d)", MAX_URL_LEN); - return CURLE_URL_MALFORMAT; - } - hostname = data->state.up.hostname; - - if(hostname && hostname[0] == '[') { - /* This looks like an IPv6 address literal. See if there is an address - scope. */ - size_t hlen; - conn->bits.ipv6_ip = TRUE; - /* cut off the brackets! */ - hostname++; - hlen = strlen(hostname); - hostname[hlen - 1] = 0; - - zonefrom_url(uh, data, conn); - } - - /* make sure the connect struct gets its own copy of the host name */ - conn->host.rawalloc = strdup(hostname ? hostname : ""); - if(!conn->host.rawalloc) - return CURLE_OUT_OF_MEMORY; - conn->host.name = conn->host.rawalloc; - - /************************************************************* - * IDN-convert the hostnames - *************************************************************/ - result = Curl_idnconvert_hostname(data, &conn->host); - if(result) - return result; - if(conn->bits.conn_to_host) { - result = Curl_idnconvert_hostname(data, &conn->conn_to_host); - if(result) - return result; - } -#ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy) { - result = Curl_idnconvert_hostname(data, &conn->http_proxy.host); - if(result) - return result; - } - if(conn->bits.socksproxy) { - result = Curl_idnconvert_hostname(data, &conn->socks_proxy.host); - if(result) - return result; - } -#endif - -#ifndef CURL_DISABLE_HSTS - /* HSTS upgrade */ - if(data->hsts && strcasecompare("http", data->state.up.scheme)) { - /* This MUST use the IDN decoded name */ - if(Curl_hsts(data->hsts, conn->host.name, TRUE)) { - char *url; - Curl_safefree(data->state.up.scheme); - uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0); - if(uc) - return Curl_uc_to_curlcode(uc); - if(data->state.url_alloc) - Curl_safefree(data->state.url); - /* after update, get the updated version */ - uc = curl_url_get(uh, CURLUPART_URL, &url, 0); - if(uc) - return Curl_uc_to_curlcode(uc); - uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0); - if(uc) { - free(url); - return Curl_uc_to_curlcode(uc); - } - data->state.url = url; - data->state.url_alloc = TRUE; - infof(data, "Switched from HTTP to HTTPS due to HSTS => %s", - data->state.url); - } - } -#endif - - result = findprotocol(data, conn, data->state.up.scheme); - if(result) - return result; - - /* - * User name and password set with their own options override the - * credentials possibly set in the URL. - */ - if(!data->state.aptr.passwd) { - uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0); - if(!uc) { - char *decoded; - result = Curl_urldecode(data->state.up.password, 0, &decoded, NULL, - conn->handler->flags&PROTOPT_USERPWDCTRL ? - REJECT_ZERO : REJECT_CTRL); - if(result) - return result; - conn->passwd = decoded; - result = Curl_setstropt(&data->state.aptr.passwd, decoded); - if(result) - return result; - } - else if(uc != CURLUE_NO_PASSWORD) - return Curl_uc_to_curlcode(uc); - } - - if(!data->set.str[STRING_USERNAME]) { - /* we don't use the URL API's URL decoder option here since it rejects - control codes and we want to allow them for some schemes in the user - and password fields */ - uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, 0); - if(!uc) { - char *decoded; - result = Curl_urldecode(data->state.up.user, 0, &decoded, NULL, - conn->handler->flags&PROTOPT_USERPWDCTRL ? - REJECT_ZERO : REJECT_CTRL); - if(result) - return result; - conn->user = decoded; - result = Curl_setstropt(&data->state.aptr.user, decoded); - } - else if(uc != CURLUE_NO_USER) - return Curl_uc_to_curlcode(uc); - else if(data->state.aptr.passwd) { - /* no user was set but a password, set a blank user */ - result = Curl_setstropt(&data->state.aptr.user, ""); - } - if(result) - return result; - } - - uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options, - CURLU_URLDECODE); - if(!uc) { - conn->options = strdup(data->state.up.options); - if(!conn->options) - return CURLE_OUT_OF_MEMORY; - } - else if(uc != CURLUE_NO_OPTIONS) - return Curl_uc_to_curlcode(uc); - - uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path, - CURLU_URLENCODE); - if(uc) - return Curl_uc_to_curlcode(uc); - - uc = curl_url_get(uh, CURLUPART_PORT, &data->state.up.port, - CURLU_DEFAULT_PORT); - if(uc) { - if(!strcasecompare("file", data->state.up.scheme)) - return CURLE_OUT_OF_MEMORY; - } - else { - unsigned long port = strtoul(data->state.up.port, NULL, 10); - conn->port = conn->remote_port = - (data->set.use_port && data->state.allow_port) ? - data->set.use_port : curlx_ultous(port); - } - - (void)curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0); - -#ifdef ENABLE_IPV6 - if(data->set.scope_id) - /* Override any scope that was set above. */ - conn->scope_id = data->set.scope_id; -#endif - - return CURLE_OK; -} - - -/* - * If we're doing a resumed transfer, we need to setup our stuff - * properly. - */ -static CURLcode setup_range(struct Curl_easy *data) -{ - struct UrlState *s = &data->state; - s->resume_from = data->set.set_resume_from; - if(s->resume_from || data->set.str[STRING_SET_RANGE]) { - if(s->rangestringalloc) - free(s->range); - - if(s->resume_from) - s->range = aprintf("%" CURL_FORMAT_CURL_OFF_T "-", s->resume_from); - else - s->range = strdup(data->set.str[STRING_SET_RANGE]); - - s->rangestringalloc = (s->range) ? TRUE : FALSE; - - if(!s->range) - return CURLE_OUT_OF_MEMORY; - - /* tell ourselves to fetch this range */ - s->use_range = TRUE; /* enable range download */ - } - else - s->use_range = FALSE; /* disable range download */ - - return CURLE_OK; -} - - -/* - * setup_connection_internals() - - * - * Setup connection internals specific to the requested protocol in the - * Curl_easy. This is inited and setup before the connection is made but - * is about the particular protocol that is to be used. - * - * This MUST get called after proxy magic has been figured out. - */ -static CURLcode setup_connection_internals(struct Curl_easy *data, - struct connectdata *conn) -{ - const struct Curl_handler *p; - CURLcode result; - - /* Perform setup complement if some. */ - p = conn->handler; - - if(p->setup_connection) { - result = (*p->setup_connection)(data, conn); - - if(result) - return result; - - p = conn->handler; /* May have changed. */ - } - - if(conn->port < 0) - /* we check for -1 here since if proxy was detected already, this - was very likely already set to the proxy port */ - conn->port = p->defport; - - return CURLE_OK; -} - -/* - * Curl_free_request_state() should free temp data that was allocated in the - * Curl_easy for this single request. - */ - -void Curl_free_request_state(struct Curl_easy *data) -{ - Curl_safefree(data->req.p.http); - Curl_safefree(data->req.newurl); - -#ifndef CURL_DISABLE_DOH - if(data->req.doh) { - Curl_close(&data->req.doh->probe[0].easy); - Curl_close(&data->req.doh->probe[1].easy); - } -#endif -} - - -#ifndef CURL_DISABLE_PROXY - -#ifndef CURL_DISABLE_HTTP -/**************************************************************** -* Detect what (if any) proxy to use. Remember that this selects a host -* name and is not limited to HTTP proxies only. -* The returned pointer must be freed by the caller (unless NULL) -****************************************************************/ -static char *detect_proxy(struct Curl_easy *data, - struct connectdata *conn) -{ - char *proxy = NULL; - - /* If proxy was not specified, we check for default proxy environment - * variables, to enable i.e Lynx compliance: - * - * http_proxy=http://some.server.dom:port/ - * https_proxy=http://some.server.dom:port/ - * ftp_proxy=http://some.server.dom:port/ - * no_proxy=domain1.dom,host.domain2.dom - * (a comma-separated list of hosts which should - * not be proxied, or an asterisk to override - * all proxy variables) - * all_proxy=http://some.server.dom:port/ - * (seems to exist for the CERN www lib. Probably - * the first to check for.) - * - * For compatibility, the all-uppercase versions of these variables are - * checked if the lowercase versions don't exist. - */ - char proxy_env[128]; - const char *protop = conn->handler->scheme; - char *envp = proxy_env; - char *prox; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif - - /* Now, build <protocol>_proxy and check for such a one to use */ - while(*protop) - *envp++ = Curl_raw_tolower(*protop++); - - /* append _proxy */ - strcpy(envp, "_proxy"); - - /* read the protocol proxy: */ - prox = curl_getenv(proxy_env); - - /* - * We don't try the uppercase version of HTTP_PROXY because of - * security reasons: - * - * When curl is used in a webserver application - * environment (cgi or php), this environment variable can - * be controlled by the web server user by setting the - * http header 'Proxy:' to some value. - * - * This can cause 'internal' http/ftp requests to be - * arbitrarily redirected by any external attacker. - */ - if(!prox && !strcasecompare("http_proxy", proxy_env)) { - /* There was no lowercase variable, try the uppercase version: */ - Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env)); - prox = curl_getenv(proxy_env); - } - - envp = proxy_env; - if(prox) { - proxy = prox; /* use this */ - } - else { - envp = (char *)"all_proxy"; - proxy = curl_getenv(envp); /* default proxy to use */ - if(!proxy) { - envp = (char *)"ALL_PROXY"; - proxy = curl_getenv(envp); - } - } - if(proxy) - infof(data, "Uses proxy env variable %s == '%s'", envp, proxy); - - return proxy; -} -#endif /* CURL_DISABLE_HTTP */ - -/* - * If this is supposed to use a proxy, we need to figure out the proxy - * host name, so that we can re-use an existing connection - * that may exist registered to the same proxy host. - */ -static CURLcode parse_proxy(struct Curl_easy *data, - struct connectdata *conn, char *proxy, - curl_proxytype proxytype) -{ - char *portptr = NULL; - int port = -1; - char *proxyuser = NULL; - char *proxypasswd = NULL; - char *host = NULL; - bool sockstype; - CURLUcode uc; - struct proxy_info *proxyinfo; - CURLU *uhp = curl_url(); - CURLcode result = CURLE_OK; - char *scheme = NULL; -#ifdef USE_UNIX_SOCKETS - char *path = NULL; - bool is_unix_proxy = FALSE; -#endif - - - if(!uhp) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - /* When parsing the proxy, allowing non-supported schemes since we have - these made up ones for proxies. Guess scheme for URLs without it. */ - uc = curl_url_set(uhp, CURLUPART_URL, proxy, - CURLU_NON_SUPPORT_SCHEME|CURLU_GUESS_SCHEME); - if(!uc) { - /* parsed okay as a URL */ - uc = curl_url_get(uhp, CURLUPART_SCHEME, &scheme, 0); - if(uc) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - if(strcasecompare("https", scheme)) - proxytype = CURLPROXY_HTTPS; - else if(strcasecompare("socks5h", scheme)) - proxytype = CURLPROXY_SOCKS5_HOSTNAME; - else if(strcasecompare("socks5", scheme)) - proxytype = CURLPROXY_SOCKS5; - else if(strcasecompare("socks4a", scheme)) - proxytype = CURLPROXY_SOCKS4A; - else if(strcasecompare("socks4", scheme) || - strcasecompare("socks", scheme)) - proxytype = CURLPROXY_SOCKS4; - else if(strcasecompare("http", scheme)) - ; /* leave it as HTTP or HTTP/1.0 */ - else { - /* Any other xxx:// reject! */ - failf(data, "Unsupported proxy scheme for \'%s\'", proxy); - result = CURLE_COULDNT_CONNECT; - goto error; - } - } - else { - failf(data, "Unsupported proxy syntax in \'%s\'", proxy); - result = CURLE_COULDNT_RESOLVE_PROXY; - goto error; - } - -#ifdef USE_SSL - if(!(Curl_ssl->supports & SSLSUPP_HTTPS_PROXY)) -#endif - if(proxytype == CURLPROXY_HTTPS) { - failf(data, "Unsupported proxy \'%s\', libcurl is built without the " - "HTTPS-proxy support.", proxy); - result = CURLE_NOT_BUILT_IN; - goto error; - } - - sockstype = - proxytype == CURLPROXY_SOCKS5_HOSTNAME || - proxytype == CURLPROXY_SOCKS5 || - proxytype == CURLPROXY_SOCKS4A || - proxytype == CURLPROXY_SOCKS4; - - proxyinfo = sockstype ? &conn->socks_proxy : &conn->http_proxy; - proxyinfo->proxytype = proxytype; - - /* Is there a username and password given in this proxy url? */ - uc = curl_url_get(uhp, CURLUPART_USER, &proxyuser, CURLU_URLDECODE); - if(uc && (uc != CURLUE_NO_USER)) - goto error; - uc = curl_url_get(uhp, CURLUPART_PASSWORD, &proxypasswd, CURLU_URLDECODE); - if(uc && (uc != CURLUE_NO_PASSWORD)) - goto error; - - if(proxyuser || proxypasswd) { - Curl_safefree(proxyinfo->user); - proxyinfo->user = proxyuser; - result = Curl_setstropt(&data->state.aptr.proxyuser, proxyuser); - proxyuser = NULL; - if(result) - goto error; - Curl_safefree(proxyinfo->passwd); - if(!proxypasswd) { - proxypasswd = strdup(""); - if(!proxypasswd) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - } - proxyinfo->passwd = proxypasswd; - result = Curl_setstropt(&data->state.aptr.proxypasswd, proxypasswd); - proxypasswd = NULL; - if(result) - goto error; - conn->bits.proxy_user_passwd = TRUE; /* enable it */ - } - - (void)curl_url_get(uhp, CURLUPART_PORT, &portptr, 0); - - if(portptr) { - port = (int)strtol(portptr, NULL, 10); - free(portptr); - } - else { - if(data->set.proxyport) - /* None given in the proxy string, then get the default one if it is - given */ - port = (int)data->set.proxyport; - else { - if(proxytype == CURLPROXY_HTTPS) - port = CURL_DEFAULT_HTTPS_PROXY_PORT; - else - port = CURL_DEFAULT_PROXY_PORT; - } - } - if(port >= 0) { - proxyinfo->port = port; - if(conn->port < 0 || sockstype || !conn->socks_proxy.host.rawalloc) - conn->port = port; - } - - /* now, clone the proxy host name */ - uc = curl_url_get(uhp, CURLUPART_HOST, &host, CURLU_URLDECODE); - if(uc) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } -#ifdef USE_UNIX_SOCKETS - if(sockstype && strcasecompare(UNIX_SOCKET_PREFIX, host)) { - uc = curl_url_get(uhp, CURLUPART_PATH, &path, CURLU_URLDECODE); - if(uc) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - /* path will be "/", if no path was was found */ - if(strcmp("/", path)) { - is_unix_proxy = TRUE; - free(host); - host = aprintf(UNIX_SOCKET_PREFIX"%s", path); - if(!host) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - Curl_safefree(proxyinfo->host.rawalloc); - proxyinfo->host.rawalloc = host; - proxyinfo->host.name = host; - host = NULL; - } - } - - if(!is_unix_proxy) { -#endif - Curl_safefree(proxyinfo->host.rawalloc); - proxyinfo->host.rawalloc = host; - if(host[0] == '[') { - /* this is a numerical IPv6, strip off the brackets */ - size_t len = strlen(host); - host[len-1] = 0; /* clear the trailing bracket */ - host++; - zonefrom_url(uhp, data, conn); - } - proxyinfo->host.name = host; - host = NULL; -#ifdef USE_UNIX_SOCKETS - } -#endif - - error: - free(proxyuser); - free(proxypasswd); - free(host); - free(scheme); -#ifdef USE_UNIX_SOCKETS - free(path); -#endif - curl_url_cleanup(uhp); - return result; -} - -/* - * Extract the user and password from the authentication string - */ -static CURLcode parse_proxy_auth(struct Curl_easy *data, - struct connectdata *conn) -{ - const char *proxyuser = data->state.aptr.proxyuser ? - data->state.aptr.proxyuser : ""; - const char *proxypasswd = data->state.aptr.proxypasswd ? - data->state.aptr.proxypasswd : ""; - CURLcode result = CURLE_OK; - - if(proxyuser) { - result = Curl_urldecode(proxyuser, 0, &conn->http_proxy.user, NULL, - REJECT_ZERO); - if(!result) - result = Curl_setstropt(&data->state.aptr.proxyuser, - conn->http_proxy.user); - } - if(!result && proxypasswd) { - result = Curl_urldecode(proxypasswd, 0, &conn->http_proxy.passwd, - NULL, REJECT_ZERO); - if(!result) - result = Curl_setstropt(&data->state.aptr.proxypasswd, - conn->http_proxy.passwd); - } - return result; -} - -/* create_conn helper to parse and init proxy values. to be called after unix - socket init but before any proxy vars are evaluated. */ -static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, - struct connectdata *conn) -{ - char *proxy = NULL; - char *socksproxy = NULL; - char *no_proxy = NULL; - CURLcode result = CURLE_OK; - - /************************************************************* - * Extract the user and password from the authentication string - *************************************************************/ - if(conn->bits.proxy_user_passwd) { - result = parse_proxy_auth(data, conn); - if(result) - goto out; - } - - /************************************************************* - * Detect what (if any) proxy to use - *************************************************************/ - if(data->set.str[STRING_PROXY]) { - proxy = strdup(data->set.str[STRING_PROXY]); - /* if global proxy is set, this is it */ - if(!proxy) { - failf(data, "memory shortage"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - - if(data->set.str[STRING_PRE_PROXY]) { - socksproxy = strdup(data->set.str[STRING_PRE_PROXY]); - /* if global socks proxy is set, this is it */ - if(!socksproxy) { - failf(data, "memory shortage"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - - if(!data->set.str[STRING_NOPROXY]) { - const char *p = "no_proxy"; - no_proxy = curl_getenv(p); - if(!no_proxy) { - p = "NO_PROXY"; - no_proxy = curl_getenv(p); - } - if(no_proxy) { - infof(data, "Uses proxy env variable %s == '%s'", p, no_proxy); - } - } - - if(Curl_check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ? - data->set.str[STRING_NOPROXY] : no_proxy)) { - Curl_safefree(proxy); - Curl_safefree(socksproxy); - } -#ifndef CURL_DISABLE_HTTP - else if(!proxy && !socksproxy) - /* if the host is not in the noproxy list, detect proxy. */ - proxy = detect_proxy(data, conn); -#endif /* CURL_DISABLE_HTTP */ - - Curl_safefree(no_proxy); - -#ifdef USE_UNIX_SOCKETS - /* For the time being do not mix proxy and unix domain sockets. See #1274 */ - if(proxy && conn->unix_domain_socket) { - free(proxy); - proxy = NULL; - } -#endif - - if(proxy && (!*proxy || (conn->handler->flags & PROTOPT_NONETWORK))) { - free(proxy); /* Don't bother with an empty proxy string or if the - protocol doesn't work with network */ - proxy = NULL; - } - if(socksproxy && (!*socksproxy || - (conn->handler->flags & PROTOPT_NONETWORK))) { - free(socksproxy); /* Don't bother with an empty socks proxy string or if - the protocol doesn't work with network */ - socksproxy = NULL; - } - - /*********************************************************************** - * If this is supposed to use a proxy, we need to figure out the proxy host - * name, proxy type and port number, so that we can re-use an existing - * connection that may exist registered to the same proxy host. - ***********************************************************************/ - if(proxy || socksproxy) { - curl_proxytype ptype = (curl_proxytype)conn->http_proxy.proxytype; - if(proxy) { - result = parse_proxy(data, conn, proxy, ptype); - Curl_safefree(proxy); /* parse_proxy copies the proxy string */ - if(result) - goto out; - } - - if(socksproxy) { - result = parse_proxy(data, conn, socksproxy, ptype); - /* parse_proxy copies the socks proxy string */ - Curl_safefree(socksproxy); - if(result) - goto out; - } - - if(conn->http_proxy.host.rawalloc) { -#ifdef CURL_DISABLE_HTTP - /* asking for a HTTP proxy is a bit funny when HTTP is disabled... */ - result = CURLE_UNSUPPORTED_PROTOCOL; - goto out; -#else - /* force this connection's protocol to become HTTP if compatible */ - if(!(conn->handler->protocol & PROTO_FAMILY_HTTP)) { - if((conn->handler->flags & PROTOPT_PROXY_AS_HTTP) && - !conn->bits.tunnel_proxy) - conn->handler = &Curl_handler_http; - else - /* if not converting to HTTP over the proxy, enforce tunneling */ - conn->bits.tunnel_proxy = TRUE; - } - conn->bits.httpproxy = TRUE; -#endif - } - else { - conn->bits.httpproxy = FALSE; /* not a HTTP proxy */ - conn->bits.tunnel_proxy = FALSE; /* no tunneling if not HTTP */ - } - - if(conn->socks_proxy.host.rawalloc) { - if(!conn->http_proxy.host.rawalloc) { - /* once a socks proxy */ - if(!conn->socks_proxy.user) { - conn->socks_proxy.user = conn->http_proxy.user; - conn->http_proxy.user = NULL; - Curl_safefree(conn->socks_proxy.passwd); - conn->socks_proxy.passwd = conn->http_proxy.passwd; - conn->http_proxy.passwd = NULL; - } - } - conn->bits.socksproxy = TRUE; - } - else - conn->bits.socksproxy = FALSE; /* not a socks proxy */ - } - else { - conn->bits.socksproxy = FALSE; - conn->bits.httpproxy = FALSE; - } - conn->bits.proxy = conn->bits.httpproxy || conn->bits.socksproxy; - - if(!conn->bits.proxy) { - /* we aren't using the proxy after all... */ - conn->bits.proxy = FALSE; - conn->bits.httpproxy = FALSE; - conn->bits.socksproxy = FALSE; - conn->bits.proxy_user_passwd = FALSE; - conn->bits.tunnel_proxy = FALSE; - /* CURLPROXY_HTTPS does not have its own flag in conn->bits, yet we need - to signal that CURLPROXY_HTTPS is not used for this connection */ - conn->http_proxy.proxytype = CURLPROXY_HTTP; - } - -out: - - free(socksproxy); - free(proxy); - return result; -} -#endif /* CURL_DISABLE_PROXY */ - -/* - * Curl_parse_login_details() - * - * This is used to parse a login string for user name, password and options in - * the following formats: - * - * user - * user:password - * user:password;options - * user;options - * user;options:password - * :password - * :password;options - * ;options - * ;options:password - * - * Parameters: - * - * login [in] - The login string. - * len [in] - The length of the login string. - * userp [in/out] - The address where a pointer to newly allocated memory - * holding the user will be stored upon completion. - * passwdp [in/out] - The address where a pointer to newly allocated memory - * holding the password will be stored upon completion. - * optionsp [in/out] - The address where a pointer to newly allocated memory - * holding the options will be stored upon completion. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_parse_login_details(const char *login, const size_t len, - char **userp, char **passwdp, - char **optionsp) -{ - CURLcode result = CURLE_OK; - char *ubuf = NULL; - char *pbuf = NULL; - char *obuf = NULL; - const char *psep = NULL; - const char *osep = NULL; - size_t ulen; - size_t plen; - size_t olen; - - /* the input length check is because this is called directly from setopt - and isn't going through the regular string length check */ - size_t llen = strlen(login); - if(llen > CURL_MAX_INPUT_LENGTH) - return CURLE_BAD_FUNCTION_ARGUMENT; - - /* Attempt to find the password separator */ - if(passwdp) { - psep = strchr(login, ':'); - - /* Within the constraint of the login string */ - if(psep >= login + len) - psep = NULL; - } - - /* Attempt to find the options separator */ - if(optionsp) { - osep = strchr(login, ';'); - - /* Within the constraint of the login string */ - if(osep >= login + len) - osep = NULL; - } - - /* Calculate the portion lengths */ - ulen = (psep ? - (size_t)(osep && psep > osep ? osep - login : psep - login) : - (osep ? (size_t)(osep - login) : len)); - plen = (psep ? - (osep && osep > psep ? (size_t)(osep - psep) : - (size_t)(login + len - psep)) - 1 : 0); - olen = (osep ? - (psep && psep > osep ? (size_t)(psep - osep) : - (size_t)(login + len - osep)) - 1 : 0); - - /* Allocate the user portion buffer, which can be zero length */ - if(userp) { - ubuf = malloc(ulen + 1); - if(!ubuf) - result = CURLE_OUT_OF_MEMORY; - } - - /* Allocate the password portion buffer */ - if(!result && passwdp && psep) { - pbuf = malloc(plen + 1); - if(!pbuf) { - free(ubuf); - result = CURLE_OUT_OF_MEMORY; - } - } - - /* Allocate the options portion buffer */ - if(!result && optionsp && olen) { - obuf = malloc(olen + 1); - if(!obuf) { - free(pbuf); - free(ubuf); - result = CURLE_OUT_OF_MEMORY; - } - } - - if(!result) { - /* Store the user portion if necessary */ - if(ubuf) { - memcpy(ubuf, login, ulen); - ubuf[ulen] = '\0'; - Curl_safefree(*userp); - *userp = ubuf; - } - - /* Store the password portion if necessary */ - if(pbuf) { - memcpy(pbuf, psep + 1, plen); - pbuf[plen] = '\0'; - Curl_safefree(*passwdp); - *passwdp = pbuf; - } - - /* Store the options portion if necessary */ - if(obuf) { - memcpy(obuf, osep + 1, olen); - obuf[olen] = '\0'; - Curl_safefree(*optionsp); - *optionsp = obuf; - } - } - - return result; -} - -/************************************************************* - * Figure out the remote port number and fix it in the URL - * - * No matter if we use a proxy or not, we have to figure out the remote - * port number of various reasons. - * - * The port number embedded in the URL is replaced, if necessary. - *************************************************************/ -static CURLcode parse_remote_port(struct Curl_easy *data, - struct connectdata *conn) -{ - - if(data->set.use_port && data->state.allow_port) { - /* if set, we use this instead of the port possibly given in the URL */ - char portbuf[16]; - CURLUcode uc; - conn->remote_port = data->set.use_port; - msnprintf(portbuf, sizeof(portbuf), "%d", conn->remote_port); - uc = curl_url_set(data->state.uh, CURLUPART_PORT, portbuf, 0); - if(uc) - return CURLE_OUT_OF_MEMORY; - } - - return CURLE_OK; -} - -/* - * Override the login details from the URL with that in the CURLOPT_USERPWD - * option or a .netrc file, if applicable. - */ -static CURLcode override_login(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLUcode uc; - char **userp = &conn->user; - char **passwdp = &conn->passwd; - char **optionsp = &conn->options; - - if(data->set.str[STRING_OPTIONS]) { - free(*optionsp); - *optionsp = strdup(data->set.str[STRING_OPTIONS]); - if(!*optionsp) - return CURLE_OUT_OF_MEMORY; - } - -#ifndef CURL_DISABLE_NETRC - if(data->set.use_netrc == CURL_NETRC_REQUIRED) { - Curl_safefree(*userp); - Curl_safefree(*passwdp); - } - conn->bits.netrc = FALSE; - if(data->set.use_netrc && !data->set.str[STRING_USERNAME]) { - int ret; - bool url_provided = FALSE; - - if(data->state.aptr.user) { - /* there was a user name in the URL. Use the URL decoded version */ - userp = &data->state.aptr.user; - url_provided = TRUE; - } - - ret = Curl_parsenetrc(conn->host.name, - userp, passwdp, - data->set.str[STRING_NETRC_FILE]); - if(ret > 0) { - infof(data, "Couldn't find host %s in the %s file; using defaults", - conn->host.name, data->set.str[STRING_NETRC_FILE]); - } - else if(ret < 0) { - failf(data, ".netrc parser error"); - return CURLE_READ_ERROR; - } - else { - /* set bits.netrc TRUE to remember that we got the name from a .netrc - file, so that it is safe to use even if we followed a Location: to a - different host or similar. */ - conn->bits.netrc = TRUE; - } - if(url_provided) { - Curl_safefree(conn->user); - conn->user = strdup(*userp); - if(!conn->user) - return CURLE_OUT_OF_MEMORY; - } - /* no user was set but a password, set a blank user */ - if(userp && !*userp && *passwdp) { - *userp = strdup(""); - if(!*userp) - return CURLE_OUT_OF_MEMORY; - } - } -#endif - - /* for updated strings, we update them in the URL */ - if(*userp) { - CURLcode result; - if(data->state.aptr.user != *userp) { - /* nothing to do then */ - result = Curl_setstropt(&data->state.aptr.user, *userp); - if(result) - return result; - } - } - if(data->state.aptr.user) { - uc = curl_url_set(data->state.uh, CURLUPART_USER, data->state.aptr.user, - CURLU_URLENCODE); - if(uc) - return Curl_uc_to_curlcode(uc); - if(!*userp) { - *userp = strdup(data->state.aptr.user); - if(!*userp) - return CURLE_OUT_OF_MEMORY; - } - } - if(*passwdp) { - CURLcode result = Curl_setstropt(&data->state.aptr.passwd, *passwdp); - if(result) - return result; - } - if(data->state.aptr.passwd) { - uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, - data->state.aptr.passwd, CURLU_URLENCODE); - if(uc) - return Curl_uc_to_curlcode(uc); - if(!*passwdp) { - *passwdp = strdup(data->state.aptr.passwd); - if(!*passwdp) - return CURLE_OUT_OF_MEMORY; - } - } - - return CURLE_OK; -} - -/* - * Set the login details so they're available in the connection - */ -static CURLcode set_login(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - const char *setuser = CURL_DEFAULT_USER; - const char *setpasswd = CURL_DEFAULT_PASSWORD; - - /* If our protocol needs a password and we have none, use the defaults */ - if((conn->handler->flags & PROTOPT_NEEDSPWD) && !data->state.aptr.user) - ; - else { - setuser = ""; - setpasswd = ""; - } - /* Store the default user */ - if(!conn->user) { - conn->user = strdup(setuser); - if(!conn->user) - return CURLE_OUT_OF_MEMORY; - } - - /* Store the default password */ - if(!conn->passwd) { - conn->passwd = strdup(setpasswd); - if(!conn->passwd) - result = CURLE_OUT_OF_MEMORY; - } - - return result; -} - -/* - * Parses a "host:port" string to connect to. - * The hostname and the port may be empty; in this case, NULL is returned for - * the hostname and -1 for the port. - */ -static CURLcode parse_connect_to_host_port(struct Curl_easy *data, - const char *host, - char **hostname_result, - int *port_result) -{ - char *host_dup; - char *hostptr; - char *host_portno; - char *portptr; - int port = -1; - CURLcode result = CURLE_OK; - -#if defined(CURL_DISABLE_VERBOSE_STRINGS) - (void) data; -#endif - - *hostname_result = NULL; - *port_result = -1; - - if(!host || !*host) - return CURLE_OK; - - host_dup = strdup(host); - if(!host_dup) - return CURLE_OUT_OF_MEMORY; - - hostptr = host_dup; - - /* start scanning for port number at this point */ - portptr = hostptr; - - /* detect and extract RFC6874-style IPv6-addresses */ - if(*hostptr == '[') { -#ifdef ENABLE_IPV6 - char *ptr = ++hostptr; /* advance beyond the initial bracket */ - while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '.'))) - ptr++; - if(*ptr == '%') { - /* There might be a zone identifier */ - if(strncmp("%25", ptr, 3)) - infof(data, "Please URL encode %% as %%25, see RFC 6874."); - ptr++; - /* Allow unreserved characters as defined in RFC 3986 */ - while(*ptr && (ISALPHA(*ptr) || ISXDIGIT(*ptr) || (*ptr == '-') || - (*ptr == '.') || (*ptr == '_') || (*ptr == '~'))) - ptr++; - } - if(*ptr == ']') - /* yeps, it ended nicely with a bracket as well */ - *ptr++ = '\0'; - else - infof(data, "Invalid IPv6 address format"); - portptr = ptr; - /* Note that if this didn't end with a bracket, we still advanced the - * hostptr first, but I can't see anything wrong with that as no host - * name nor a numeric can legally start with a bracket. - */ -#else - failf(data, "Use of IPv6 in *_CONNECT_TO without IPv6 support built-in"); - result = CURLE_NOT_BUILT_IN; - goto error; -#endif - } - - /* Get port number off server.com:1080 */ - host_portno = strchr(portptr, ':'); - if(host_portno) { - char *endp = NULL; - *host_portno = '\0'; /* cut off number from host name */ - host_portno++; - if(*host_portno) { - long portparse = strtol(host_portno, &endp, 10); - if((endp && *endp) || (portparse < 0) || (portparse > 65535)) { - failf(data, "No valid port number in connect to host string (%s)", - host_portno); - result = CURLE_SETOPT_OPTION_SYNTAX; - goto error; - } - else - port = (int)portparse; /* we know it will fit */ - } - } - - /* now, clone the cleaned host name */ - if(hostptr) { - *hostname_result = strdup(hostptr); - if(!*hostname_result) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - } - - *port_result = port; - - error: - free(host_dup); - return result; -} - -/* - * Parses one "connect to" string in the form: - * "HOST:PORT:CONNECT-TO-HOST:CONNECT-TO-PORT". - */ -static CURLcode parse_connect_to_string(struct Curl_easy *data, - struct connectdata *conn, - const char *conn_to_host, - char **host_result, - int *port_result) -{ - CURLcode result = CURLE_OK; - const char *ptr = conn_to_host; - int host_match = FALSE; - int port_match = FALSE; - - *host_result = NULL; - *port_result = -1; - - if(*ptr == ':') { - /* an empty hostname always matches */ - host_match = TRUE; - ptr++; - } - else { - /* check whether the URL's hostname matches */ - size_t hostname_to_match_len; - char *hostname_to_match = aprintf("%s%s%s", - conn->bits.ipv6_ip ? "[" : "", - conn->host.name, - conn->bits.ipv6_ip ? "]" : ""); - if(!hostname_to_match) - return CURLE_OUT_OF_MEMORY; - hostname_to_match_len = strlen(hostname_to_match); - host_match = strncasecompare(ptr, hostname_to_match, - hostname_to_match_len); - free(hostname_to_match); - ptr += hostname_to_match_len; - - host_match = host_match && *ptr == ':'; - ptr++; - } - - if(host_match) { - if(*ptr == ':') { - /* an empty port always matches */ - port_match = TRUE; - ptr++; - } - else { - /* check whether the URL's port matches */ - char *ptr_next = strchr(ptr, ':'); - if(ptr_next) { - char *endp = NULL; - long port_to_match = strtol(ptr, &endp, 10); - if((endp == ptr_next) && (port_to_match == conn->remote_port)) { - port_match = TRUE; - ptr = ptr_next + 1; - } - } - } - } - - if(host_match && port_match) { - /* parse the hostname and port to connect to */ - result = parse_connect_to_host_port(data, ptr, host_result, port_result); - } - - return result; -} - -/* - * Processes all strings in the "connect to" slist, and uses the "connect - * to host" and "connect to port" of the first string that matches. - */ -static CURLcode parse_connect_to_slist(struct Curl_easy *data, - struct connectdata *conn, - struct curl_slist *conn_to_host) -{ - CURLcode result = CURLE_OK; - char *host = NULL; - int port = -1; - - while(conn_to_host && !host && port == -1) { - result = parse_connect_to_string(data, conn, conn_to_host->data, - &host, &port); - if(result) - return result; - - if(host && *host) { - conn->conn_to_host.rawalloc = host; - conn->conn_to_host.name = host; - conn->bits.conn_to_host = TRUE; - - infof(data, "Connecting to hostname: %s", host); - } - else { - /* no "connect to host" */ - conn->bits.conn_to_host = FALSE; - Curl_safefree(host); - } - - if(port >= 0) { - conn->conn_to_port = port; - conn->bits.conn_to_port = TRUE; - infof(data, "Connecting to port: %d", port); - } - else { - /* no "connect to port" */ - conn->bits.conn_to_port = FALSE; - port = -1; - } - - conn_to_host = conn_to_host->next; - } - -#ifndef CURL_DISABLE_ALTSVC - if(data->asi && !host && (port == -1) && - ((conn->handler->protocol == CURLPROTO_HTTPS) || -#ifdef CURLDEBUG - /* allow debug builds to circumvent the HTTPS restriction */ - getenv("CURL_ALTSVC_HTTP") -#else - 0 -#endif - )) { - /* no connect_to match, try alt-svc! */ - enum alpnid srcalpnid; - bool hit; - struct altsvc *as; - const int allowed_versions = ( ALPN_h1 -#ifdef USE_HTTP2 - | ALPN_h2 -#endif -#ifdef ENABLE_QUIC - | ALPN_h3 -#endif - ) & data->asi->flags; - - host = conn->host.rawalloc; -#ifdef USE_HTTP2 - /* with h2 support, check that first */ - srcalpnid = ALPN_h2; - hit = Curl_altsvc_lookup(data->asi, - srcalpnid, host, conn->remote_port, /* from */ - &as /* to */, - allowed_versions); - if(!hit) -#endif - { - srcalpnid = ALPN_h1; - hit = Curl_altsvc_lookup(data->asi, - srcalpnid, host, conn->remote_port, /* from */ - &as /* to */, - allowed_versions); - } - if(hit) { - char *hostd = strdup((char *)as->dst.host); - if(!hostd) - return CURLE_OUT_OF_MEMORY; - conn->conn_to_host.rawalloc = hostd; - conn->conn_to_host.name = hostd; - conn->bits.conn_to_host = TRUE; - conn->conn_to_port = as->dst.port; - conn->bits.conn_to_port = TRUE; - conn->bits.altused = TRUE; - infof(data, "Alt-svc connecting from [%s]%s:%d to [%s]%s:%d", - Curl_alpnid2str(srcalpnid), host, conn->remote_port, - Curl_alpnid2str(as->dst.alpnid), hostd, as->dst.port); - if(srcalpnid != as->dst.alpnid) { - /* protocol version switch */ - switch(as->dst.alpnid) { - case ALPN_h1: - conn->httpversion = 11; - break; - case ALPN_h2: - conn->httpversion = 20; - break; - case ALPN_h3: - conn->transport = TRNSPRT_QUIC; - conn->httpversion = 30; - break; - default: /* shouldn't be possible */ - break; - } - } - } - } -#endif - - return result; -} - -#ifdef USE_UNIX_SOCKETS -static CURLcode resolve_unix(struct Curl_easy *data, - struct connectdata *conn, - char *unix_path) -{ - struct Curl_dns_entry *hostaddr = NULL; - bool longpath = FALSE; - - DEBUGASSERT(unix_path); - DEBUGASSERT(conn->dns_entry == NULL); - - /* Unix domain sockets are local. The host gets ignored, just use the - * specified domain socket address. Do not cache "DNS entries". There is - * no DNS involved and we already have the filesystem path available. */ - hostaddr = calloc(1, sizeof(struct Curl_dns_entry)); - if(!hostaddr) - return CURLE_OUT_OF_MEMORY; - - hostaddr->addr = Curl_unix2addr(unix_path, &longpath, - conn->bits.abstract_unix_socket); - if(!hostaddr->addr) { - if(longpath) - /* Long paths are not supported for now */ - failf(data, "Unix socket path too long: '%s'", unix_path); - free(hostaddr); - return longpath ? CURLE_COULDNT_RESOLVE_HOST : CURLE_OUT_OF_MEMORY; - } - - hostaddr->inuse++; - conn->dns_entry = hostaddr; - return CURLE_OK; -} -#endif - -#ifndef CURL_DISABLE_PROXY -static CURLcode resolve_proxy(struct Curl_easy *data, - struct connectdata *conn, - bool *async) -{ - struct Curl_dns_entry *hostaddr = NULL; - struct hostname *host; - timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); - int rc; - - DEBUGASSERT(conn->dns_entry == NULL); - - host = conn->bits.socksproxy ? &conn->socks_proxy.host : - &conn->http_proxy.host; - - conn->hostname_resolve = strdup(host->name); - if(!conn->hostname_resolve) - return CURLE_OUT_OF_MEMORY; - - rc = Curl_resolv_timeout(data, conn->hostname_resolve, (int)conn->port, - &hostaddr, timeout_ms); - conn->dns_entry = hostaddr; - if(rc == CURLRESOLV_PENDING) - *async = TRUE; - else if(rc == CURLRESOLV_TIMEDOUT) - return CURLE_OPERATION_TIMEDOUT; - else if(!hostaddr) { - failf(data, "Couldn't resolve proxy '%s'", host->dispname); - return CURLE_COULDNT_RESOLVE_PROXY; - } - - return CURLE_OK; -} -#endif - -static CURLcode resolve_host(struct Curl_easy *data, - struct connectdata *conn, - bool *async) -{ - struct Curl_dns_entry *hostaddr = NULL; - struct hostname *connhost; - timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); - int rc; - - DEBUGASSERT(conn->dns_entry == NULL); - - connhost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host; - - /* If not connecting via a proxy, extract the port from the URL, if it is - * there, thus overriding any defaults that might have been set above. */ - conn->port = conn->bits.conn_to_port ? conn->conn_to_port : - conn->remote_port; - - /* Resolve target host right on */ - conn->hostname_resolve = strdup(connhost->name); - if(!conn->hostname_resolve) - return CURLE_OUT_OF_MEMORY; - - rc = Curl_resolv_timeout(data, conn->hostname_resolve, (int)conn->port, - &hostaddr, timeout_ms); - conn->dns_entry = hostaddr; - if(rc == CURLRESOLV_PENDING) - *async = TRUE; - else if(rc == CURLRESOLV_TIMEDOUT) { - failf(data, "Failed to resolve host '%s' with timeout after %ld ms", - connhost->dispname, - Curl_timediff(Curl_now(), data->progress.t_startsingle)); - return CURLE_OPERATION_TIMEDOUT; - } - else if(!hostaddr) { - failf(data, "Could not resolve host: %s", connhost->dispname); - return CURLE_COULDNT_RESOLVE_HOST; - } - - return CURLE_OK; -} - -/* Perform a fresh resolve */ -static CURLcode resolve_fresh(struct Curl_easy *data, - struct connectdata *conn, - bool *async) -{ -#ifdef USE_UNIX_SOCKETS - char *unix_path = conn->unix_domain_socket; - -#ifndef CURL_DISABLE_PROXY - if(!unix_path && conn->socks_proxy.host.name && - !strncmp(UNIX_SOCKET_PREFIX"/", - conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX))) - unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1; -#endif - - if(unix_path) { - conn->transport = TRNSPRT_UNIX; - return resolve_unix(data, conn, unix_path); - } -#endif - -#ifndef CURL_DISABLE_PROXY - if(CONN_IS_PROXIED(conn)) - return resolve_proxy(data, conn, async); -#endif - - return resolve_host(data, conn, async); -} - -/************************************************************* - * Resolve the address of the server or proxy - *************************************************************/ -static CURLcode resolve_server(struct Curl_easy *data, - struct connectdata *conn, - bool *async) -{ - DEBUGASSERT(conn); - DEBUGASSERT(data); - - /* Resolve the name of the server or proxy */ - if(conn->bits.reuse) { - /* We're reusing the connection - no need to resolve anything, and - idnconvert_hostname() was called already in create_conn() for the re-use - case. */ - *async = FALSE; - return CURLE_OK; - } - - return resolve_fresh(data, conn, async); -} - -/* - * Cleanup the connection just allocated before we can move along and use the - * previously existing one. All relevant data is copied over and old_conn is - * ready for freeing once this function returns. - */ -static void reuse_conn(struct Curl_easy *data, - struct connectdata *old_conn, - struct connectdata *conn) -{ - /* 'local_ip' and 'local_port' get filled with local's numerical - ip address and port number whenever an outgoing connection is - **established** from the primary socket to a remote address. */ - char local_ip[MAX_IPADR_LEN] = ""; - int local_port = -1; - - /* get the user+password information from the old_conn struct since it may - * be new for this request even when we re-use an existing connection */ - if(old_conn->user) { - /* use the new user name and password though */ - Curl_safefree(conn->user); - Curl_safefree(conn->passwd); - conn->user = old_conn->user; - conn->passwd = old_conn->passwd; - old_conn->user = NULL; - old_conn->passwd = NULL; - } - -#ifndef CURL_DISABLE_PROXY - conn->bits.proxy_user_passwd = old_conn->bits.proxy_user_passwd; - if(conn->bits.proxy_user_passwd) { - /* use the new proxy user name and proxy password though */ - Curl_safefree(conn->http_proxy.user); - Curl_safefree(conn->socks_proxy.user); - Curl_safefree(conn->http_proxy.passwd); - Curl_safefree(conn->socks_proxy.passwd); - conn->http_proxy.user = old_conn->http_proxy.user; - conn->socks_proxy.user = old_conn->socks_proxy.user; - conn->http_proxy.passwd = old_conn->http_proxy.passwd; - conn->socks_proxy.passwd = old_conn->socks_proxy.passwd; - old_conn->http_proxy.user = NULL; - old_conn->socks_proxy.user = NULL; - old_conn->http_proxy.passwd = NULL; - old_conn->socks_proxy.passwd = NULL; - } -#endif - - Curl_free_idnconverted_hostname(&conn->host); - Curl_free_idnconverted_hostname(&conn->conn_to_host); - Curl_safefree(conn->host.rawalloc); - Curl_safefree(conn->conn_to_host.rawalloc); - conn->host = old_conn->host; - old_conn->host.rawalloc = NULL; - old_conn->host.encalloc = NULL; - conn->conn_to_host = old_conn->conn_to_host; - old_conn->conn_to_host.rawalloc = NULL; - conn->conn_to_port = old_conn->conn_to_port; - conn->remote_port = old_conn->remote_port; - Curl_safefree(conn->hostname_resolve); - - conn->hostname_resolve = old_conn->hostname_resolve; - old_conn->hostname_resolve = NULL; - - /* persist connection info in session handle */ - if(conn->transport == TRNSPRT_TCP) { - Curl_conninfo_local(data, conn->sock[FIRSTSOCKET], - local_ip, &local_port); - } - Curl_persistconninfo(data, conn, local_ip, local_port); - - conn_reset_all_postponed_data(old_conn); /* free buffers */ - - /* re-use init */ - conn->bits.reuse = TRUE; /* yes, we're re-using here */ - - conn_free(old_conn); -} - -/** - * create_conn() sets up a new connectdata struct, or re-uses an already - * existing one, and resolves host name. - * - * if this function returns CURLE_OK and *async is set to TRUE, the resolve - * response will be coming asynchronously. If *async is FALSE, the name is - * already resolved. - * - * @param data The sessionhandle pointer - * @param in_connect is set to the next connection data pointer - * @param async is set TRUE when an async DNS resolution is pending - * @see Curl_setup_conn() - * - */ - -static CURLcode create_conn(struct Curl_easy *data, - struct connectdata **in_connect, - bool *async) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn; - struct connectdata *conn_temp = NULL; - bool reuse; - bool connections_available = TRUE; - bool force_reuse = FALSE; - bool waitpipe = FALSE; - size_t max_host_connections = Curl_multi_max_host_connections(data->multi); - size_t max_total_connections = Curl_multi_max_total_connections(data->multi); - - *async = FALSE; - *in_connect = NULL; - - /************************************************************* - * Check input data - *************************************************************/ - if(!data->state.url) { - result = CURLE_URL_MALFORMAT; - goto out; - } - - /* First, split up the current URL in parts so that we can use the - parts for checking against the already present connections. In order - to not have to modify everything at once, we allocate a temporary - connection data struct and fill in for comparison purposes. */ - conn = allocate_conn(data); - - if(!conn) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - /* We must set the return variable as soon as possible, so that our - parent can cleanup any possible allocs we may have done before - any failure */ - *in_connect = conn; - - result = parseurlandfillconn(data, conn); - if(result) - goto out; - - if(data->set.str[STRING_SASL_AUTHZID]) { - conn->sasl_authzid = strdup(data->set.str[STRING_SASL_AUTHZID]); - if(!conn->sasl_authzid) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - - if(data->set.str[STRING_BEARER]) { - conn->oauth_bearer = strdup(data->set.str[STRING_BEARER]); - if(!conn->oauth_bearer) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - -#ifdef USE_UNIX_SOCKETS - if(data->set.str[STRING_UNIX_SOCKET_PATH]) { - conn->unix_domain_socket = strdup(data->set.str[STRING_UNIX_SOCKET_PATH]); - if(!conn->unix_domain_socket) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - conn->bits.abstract_unix_socket = data->set.abstract_unix_socket; - } -#endif - - /* After the unix socket init but before the proxy vars are used, parse and - initialize the proxy vars */ -#ifndef CURL_DISABLE_PROXY - result = create_conn_helper_init_proxy(data, conn); - if(result) - goto out; - - /************************************************************* - * If the protocol is using SSL and HTTP proxy is used, we set - * the tunnel_proxy bit. - *************************************************************/ - if((conn->given->flags&PROTOPT_SSL) && conn->bits.httpproxy) - conn->bits.tunnel_proxy = TRUE; -#endif - - /************************************************************* - * Figure out the remote port number and fix it in the URL - *************************************************************/ - result = parse_remote_port(data, conn); - if(result) - goto out; - - /* Check for overridden login details and set them accordingly so that - they are known when protocol->setup_connection is called! */ - result = override_login(data, conn); - if(result) - goto out; - - result = set_login(data, conn); /* default credentials */ - if(result) - goto out; - - /************************************************************* - * Process the "connect to" linked list of hostname/port mappings. - * Do this after the remote port number has been fixed in the URL. - *************************************************************/ - result = parse_connect_to_slist(data, conn, data->set.connect_to); - if(result) - goto out; - - - /************************************************************* - * Check whether the host and the "connect to host" are equal. - * Do this after the hostnames have been IDN-converted. - *************************************************************/ - if(conn->bits.conn_to_host && - strcasecompare(conn->conn_to_host.name, conn->host.name)) { - conn->bits.conn_to_host = FALSE; - } - - /************************************************************* - * Check whether the port and the "connect to port" are equal. - * Do this after the remote port number has been fixed in the URL. - *************************************************************/ - if(conn->bits.conn_to_port && conn->conn_to_port == conn->remote_port) { - conn->bits.conn_to_port = FALSE; - } - -#ifndef CURL_DISABLE_PROXY - /************************************************************* - * If the "connect to" feature is used with an HTTP proxy, - * we set the tunnel_proxy bit. - *************************************************************/ - if((conn->bits.conn_to_host || conn->bits.conn_to_port) && - conn->bits.httpproxy) - conn->bits.tunnel_proxy = TRUE; -#endif - - /************************************************************* - * Setup internals depending on protocol. Needs to be done after - * we figured out what/if proxy to use. - *************************************************************/ - result = setup_connection_internals(data, conn); - if(result) - goto out; - - conn->recv[FIRSTSOCKET] = Curl_recv_plain; - conn->send[FIRSTSOCKET] = Curl_send_plain; - conn->recv[SECONDARYSOCKET] = Curl_recv_plain; - conn->send[SECONDARYSOCKET] = Curl_send_plain; - - conn->bits.tcp_fastopen = data->set.tcp_fastopen; - - /*********************************************************************** - * file: is a special case in that it doesn't need a network connection - ***********************************************************************/ -#ifndef CURL_DISABLE_FILE - if(conn->handler->flags & PROTOPT_NONETWORK) { - bool done; - /* this is supposed to be the connect function so we better at least check - that the file is present here! */ - DEBUGASSERT(conn->handler->connect_it); - Curl_persistconninfo(data, conn, NULL, -1); - result = conn->handler->connect_it(data, &done); - - /* Setup a "faked" transfer that'll do nothing */ - if(!result) { - conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; /* we are "connected */ - - Curl_attach_connection(data, conn); - result = Curl_conncache_add_conn(data); - if(result) - goto out; - - /* - * Setup whatever necessary for a resumed transfer - */ - result = setup_range(data); - if(result) { - DEBUGASSERT(conn->handler->done); - /* we ignore the return code for the protocol-specific DONE */ - (void)conn->handler->done(data, result, FALSE); - goto out; - } - Curl_setup_transfer(data, -1, -1, FALSE, -1); - } - - /* since we skip do_init() */ - Curl_init_do(data, conn); - - goto out; - } -#endif - - /* Get a cloned copy of the SSL config situation stored in the - connection struct. But to get this going nicely, we must first make - sure that the strings in the master copy are pointing to the correct - strings in the session handle strings array! - - Keep in mind that the pointers in the master copy are pointing to strings - that will be freed as part of the Curl_easy struct, but all cloned - copies will be separately allocated. - */ - data->set.ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH]; - data->set.ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE]; - data->set.ssl.primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT]; - data->set.ssl.primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT]; - data->set.ssl.primary.cipher_list = - data->set.str[STRING_SSL_CIPHER_LIST]; - data->set.ssl.primary.cipher_list13 = - data->set.str[STRING_SSL_CIPHER13_LIST]; - data->set.ssl.primary.pinned_key = - data->set.str[STRING_SSL_PINNEDPUBLICKEY]; - data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT]; - data->set.ssl.primary.ca_info_blob = data->set.blobs[BLOB_CAINFO]; - data->set.ssl.primary.curves = data->set.str[STRING_SSL_EC_CURVES]; - -#ifndef CURL_DISABLE_PROXY - data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY]; - data->set.proxy_ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY]; - data->set.proxy_ssl.primary.cipher_list = - data->set.str[STRING_SSL_CIPHER_LIST_PROXY]; - data->set.proxy_ssl.primary.cipher_list13 = - data->set.str[STRING_SSL_CIPHER13_LIST_PROXY]; - data->set.proxy_ssl.primary.pinned_key = - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]; - data->set.proxy_ssl.primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY]; - data->set.proxy_ssl.primary.ca_info_blob = - data->set.blobs[BLOB_CAINFO_PROXY]; - data->set.proxy_ssl.primary.issuercert = - data->set.str[STRING_SSL_ISSUERCERT_PROXY]; - data->set.proxy_ssl.primary.issuercert_blob = - data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY]; - data->set.proxy_ssl.primary.CRLfile = - data->set.str[STRING_SSL_CRLFILE_PROXY]; - data->set.proxy_ssl.cert_type = data->set.str[STRING_CERT_TYPE_PROXY]; - data->set.proxy_ssl.key = data->set.str[STRING_KEY_PROXY]; - data->set.proxy_ssl.key_type = data->set.str[STRING_KEY_TYPE_PROXY]; - data->set.proxy_ssl.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY]; - data->set.proxy_ssl.primary.clientcert = data->set.str[STRING_CERT_PROXY]; - data->set.proxy_ssl.key_blob = data->set.blobs[BLOB_KEY_PROXY]; -#endif - data->set.ssl.primary.CRLfile = data->set.str[STRING_SSL_CRLFILE]; - data->set.ssl.cert_type = data->set.str[STRING_CERT_TYPE]; - data->set.ssl.key = data->set.str[STRING_KEY]; - data->set.ssl.key_type = data->set.str[STRING_KEY_TYPE]; - data->set.ssl.key_passwd = data->set.str[STRING_KEY_PASSWD]; - data->set.ssl.primary.clientcert = data->set.str[STRING_CERT]; -#ifdef USE_TLS_SRP - data->set.ssl.primary.username = data->set.str[STRING_TLSAUTH_USERNAME]; - data->set.ssl.primary.password = data->set.str[STRING_TLSAUTH_PASSWORD]; -#ifndef CURL_DISABLE_PROXY - data->set.proxy_ssl.primary.username = - data->set.str[STRING_TLSAUTH_USERNAME_PROXY]; - data->set.proxy_ssl.primary.password = - data->set.str[STRING_TLSAUTH_PASSWORD_PROXY]; -#endif -#endif - data->set.ssl.key_blob = data->set.blobs[BLOB_KEY]; - - if(!Curl_clone_primary_ssl_config(&data->set.ssl.primary, - &conn->ssl_config)) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - -#ifndef CURL_DISABLE_PROXY - if(!Curl_clone_primary_ssl_config(&data->set.proxy_ssl.primary, - &conn->proxy_ssl_config)) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } -#endif - - prune_dead_connections(data); - - /************************************************************* - * Check the current list of connections to see if we can - * re-use an already existing one or if we have to create a - * new one. - *************************************************************/ - - DEBUGASSERT(conn->user); - DEBUGASSERT(conn->passwd); - - /* reuse_fresh is TRUE if we are told to use a new connection by force, but - we only acknowledge this option if this is not a re-used connection - already (which happens due to follow-location or during a HTTP - authentication phase). CONNECT_ONLY transfers also refuse reuse. */ - if((data->set.reuse_fresh && !data->state.this_is_a_follow) || - data->set.connect_only) - reuse = FALSE; - else - reuse = ConnectionExists(data, conn, &conn_temp, &force_reuse, &waitpipe); - - if(reuse) { - /* - * We already have a connection for this, we got the former connection in - * the conn_temp variable and thus we need to cleanup the one we just - * allocated before we can move along and use the previously existing one. - */ - reuse_conn(data, conn, conn_temp); - conn = conn_temp; - *in_connect = conn; - -#ifndef CURL_DISABLE_PROXY - infof(data, "Re-using existing connection #%ld with %s %s", - conn->connection_id, - conn->bits.proxy?"proxy":"host", - conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname : - conn->http_proxy.host.name ? conn->http_proxy.host.dispname : - conn->host.dispname); -#else - infof(data, "Re-using existing connection #%ld with host %s", - conn->connection_id, conn->host.dispname); -#endif - } - else { - /* We have decided that we want a new connection. However, we may not - be able to do that if we have reached the limit of how many - connections we are allowed to open. */ - - if(conn->handler->flags & PROTOPT_ALPN) { - /* The protocol wants it, so set the bits if enabled in the easy handle - (default) */ - if(data->set.ssl_enable_alpn) - conn->bits.tls_enable_alpn = TRUE; - } - - if(waitpipe) - /* There is a connection that *might* become usable for multiplexing - "soon", and we wait for that */ - connections_available = FALSE; - else { - /* this gets a lock on the conncache */ - struct connectbundle *bundle = - Curl_conncache_find_bundle(data, conn, data->state.conn_cache); - - if(max_host_connections > 0 && bundle && - (bundle->num_connections >= max_host_connections)) { - struct connectdata *conn_candidate; - - /* The bundle is full. Extract the oldest connection. */ - conn_candidate = Curl_conncache_extract_bundle(data, bundle); - CONNCACHE_UNLOCK(data); - - if(conn_candidate) - Curl_disconnect(data, conn_candidate, FALSE); - else { - infof(data, "No more connections allowed to host: %zu", - max_host_connections); - connections_available = FALSE; - } - } - else - CONNCACHE_UNLOCK(data); - - } - - if(connections_available && - (max_total_connections > 0) && - (Curl_conncache_size(data) >= max_total_connections)) { - struct connectdata *conn_candidate; - - /* The cache is full. Let's see if we can kill a connection. */ - conn_candidate = Curl_conncache_extract_oldest(data); - if(conn_candidate) - Curl_disconnect(data, conn_candidate, FALSE); - else { - infof(data, "No connections available in cache"); - connections_available = FALSE; - } - } - - if(!connections_available) { - infof(data, "No connections available."); - - conn_free(conn); - *in_connect = NULL; - - result = CURLE_NO_CONNECTION_AVAILABLE; - goto out; - } - else { - /* - * This is a brand new connection, so let's store it in the connection - * cache of ours! - */ - Curl_attach_connection(data, conn); - result = Curl_conncache_add_conn(data); - if(result) - goto out; - } - -#if defined(USE_NTLM) - /* If NTLM is requested in a part of this connection, make sure we don't - assume the state is fine as this is a fresh connection and NTLM is - connection based. */ - if((data->state.authhost.picked & (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) && - data->state.authhost.done) { - infof(data, "NTLM picked AND auth done set, clear picked"); - data->state.authhost.picked = CURLAUTH_NONE; - data->state.authhost.done = FALSE; - } - - if((data->state.authproxy.picked & (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) && - data->state.authproxy.done) { - infof(data, "NTLM-proxy picked AND auth done set, clear picked"); - data->state.authproxy.picked = CURLAUTH_NONE; - data->state.authproxy.done = FALSE; - } -#endif - } - - /* Setup and init stuff before DO starts, in preparing for the transfer. */ - Curl_init_do(data, conn); - - /* - * Setup whatever necessary for a resumed transfer - */ - result = setup_range(data); - if(result) - goto out; - - /* Continue connectdata initialization here. */ - - /* - * Inherit the proper values from the urldata struct AFTER we have arranged - * the persistent connection stuff - */ - conn->seek_func = data->set.seek_func; - conn->seek_client = data->set.seek_client; - - /************************************************************* - * Resolve the address of the server or proxy - *************************************************************/ - result = resolve_server(data, conn, async); - -out: - return result; -} - -/* Curl_setup_conn() is called after the name resolve initiated in - * create_conn() is all done. - * - * Curl_setup_conn() also handles reused connections - */ -CURLcode Curl_setup_conn(struct Curl_easy *data, - bool *protocol_done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - - Curl_pgrsTime(data, TIMER_NAMELOOKUP); - - if(conn->handler->flags & PROTOPT_NONETWORK) { - /* nothing to setup when not using a network */ - *protocol_done = TRUE; - return result; - } - *protocol_done = FALSE; /* default to not done */ - -#ifndef CURL_DISABLE_PROXY - /* set proxy_connect_closed to false unconditionally already here since it - is used strictly to provide extra information to a parent function in the - case of proxy CONNECT failures and we must make sure we don't have it - lingering set from a previous invoke */ - conn->bits.proxy_connect_closed = FALSE; -#endif - -#ifdef CURL_DO_LINEEND_CONV - data->state.crlf_conversions = 0; /* reset CRLF conversion counter */ -#endif /* CURL_DO_LINEEND_CONV */ - - /* set start time here for timeout purposes in the connect procedure, it - is later set again for the progress meter purpose */ - conn->now = Curl_now(); - - if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) { - conn->bits.tcpconnect[FIRSTSOCKET] = FALSE; - result = Curl_connecthost(data, conn, conn->dns_entry); - if(result) - return result; - } - else { - Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */ - if(conn->ssl[FIRSTSOCKET].use || - (conn->handler->protocol & PROTO_FAMILY_SSH)) - Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */ - conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; - *protocol_done = TRUE; - Curl_updateconninfo(data, conn, conn->sock[FIRSTSOCKET]); - Curl_verboseconnect(data, conn); - } - - conn->now = Curl_now(); /* time this *after* the connect is done, we set - this here perhaps a second time */ - return result; -} - -CURLcode Curl_connect(struct Curl_easy *data, - bool *asyncp, - bool *protocol_done) -{ - CURLcode result; - struct connectdata *conn; - - *asyncp = FALSE; /* assume synchronous resolves by default */ - - /* init the single-transfer specific data */ - Curl_free_request_state(data); - memset(&data->req, 0, sizeof(struct SingleRequest)); - data->req.size = data->req.maxdownload = -1; - - /* call the stuff that needs to be called */ - result = create_conn(data, &conn, asyncp); - - if(!result) { - if(CONN_INUSE(conn) > 1) - /* multiplexed */ - *protocol_done = TRUE; - else if(!*asyncp) { - /* DNS resolution is done: that's either because this is a reused - connection, in which case DNS was unnecessary, or because DNS - really did finish already (synch resolver/fast async resolve) */ - result = Curl_setup_conn(data, protocol_done); - } - } - - if(result == CURLE_NO_CONNECTION_AVAILABLE) { - return result; - } - else if(result && conn) { - /* We're not allowed to return failure with memory left allocated in the - connectdata struct, free those here */ - Curl_detach_connection(data); - Curl_conncache_remove_conn(data, conn, TRUE); - Curl_disconnect(data, conn, TRUE); - } - - return result; -} - -/* - * Curl_init_do() inits the readwrite session. This is inited each time (in - * the DO function before the protocol-specific DO functions are invoked) for - * a transfer, sometimes multiple times on the same Curl_easy. Make sure - * nothing in here depends on stuff that are setup dynamically for the - * transfer. - * - * Allow this function to get called with 'conn' set to NULL. - */ - -CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) -{ - struct SingleRequest *k = &data->req; - - /* if this is a pushed stream, we need this: */ - CURLcode result = Curl_preconnect(data); - if(result) - return result; - - if(conn) { - conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to - use */ - /* if the protocol used doesn't support wildcards, switch it off */ - if(data->state.wildcardmatch && - !(conn->handler->flags & PROTOPT_WILDCARD)) - data->state.wildcardmatch = FALSE; - } - - data->state.done = FALSE; /* *_done() is not called yet */ - data->state.expect100header = FALSE; - - if(data->set.opt_no_body) - /* in HTTP lingo, no body means using the HEAD request... */ - data->state.httpreq = HTTPREQ_HEAD; - - k->start = Curl_now(); /* start time */ - k->now = k->start; /* current time is now */ - k->header = TRUE; /* assume header */ - k->bytecount = 0; - k->ignorebody = FALSE; - - Curl_speedinit(data); - Curl_pgrsSetUploadCounter(data, 0); - Curl_pgrsSetDownloadCounter(data, 0); - - return CURLE_OK; -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_IPHLPAPI_H
+#include <Iphlpapi.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+#ifndef HAVE_SOCKET
+#error "We can't compile without socket() support!"
+#endif
+
+#include <limits.h>
+
+#include "doh.h"
+#include "urldata.h"
+#include "netrc.h"
+#include "formdata.h"
+#include "mime.h"
+#include "vtls/vtls.h"
+#include "hostip.h"
+#include "transfer.h"
+#include "sendf.h"
+#include "progress.h"
+#include "cookie.h"
+#include "strcase.h"
+#include "strerror.h"
+#include "escape.h"
+#include "strtok.h"
+#include "share.h"
+#include "content_encoding.h"
+#include "http_digest.h"
+#include "http_negotiate.h"
+#include "select.h"
+#include "multiif.h"
+#include "easyif.h"
+#include "speedcheck.h"
+#include "warnless.h"
+#include "getinfo.h"
+#include "urlapi-int.h"
+#include "system_win32.h"
+#include "hsts.h"
+#include "noproxy.h"
+#include "cfilters.h"
+#include "idn.h"
+
+/* And now for the protocols */
+#include "ftp.h"
+#include "dict.h"
+#include "telnet.h"
+#include "tftp.h"
+#include "http.h"
+#include "http2.h"
+#include "file.h"
+#include "curl_ldap.h"
+#include "vssh/ssh.h"
+#include "imap.h"
+#include "url.h"
+#include "connect.h"
+#include "inet_ntop.h"
+#include "http_ntlm.h"
+#include "curl_rtmp.h"
+#include "gopher.h"
+#include "mqtt.h"
+#include "http_proxy.h"
+#include "conncache.h"
+#include "multihandle.h"
+#include "strdup.h"
+#include "setopt.h"
+#include "altsvc.h"
+#include "dynbuf.h"
+#include "headers.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
+
+static void conn_free(struct Curl_easy *data, struct connectdata *conn);
+
+/* Some parts of the code (e.g. chunked encoding) assume this buffer has at
+ * more than just a few bytes to play with. Don't let it become too small or
+ * bad things will happen.
+ */
+#if READBUFFER_SIZE < READBUFFER_MIN
+# error READBUFFER_SIZE is too small
+#endif
+
+#ifdef USE_UNIX_SOCKETS
+#define UNIX_SOCKET_PREFIX "localhost"
+#endif
+
+/* Reject URLs exceeding this length */
+#define MAX_URL_LEN 0xffff
+
+/*
+* get_protocol_family()
+*
+* This is used to return the protocol family for a given protocol.
+*
+* Parameters:
+*
+* 'h' [in] - struct Curl_handler pointer.
+*
+* Returns the family as a single bit protocol identifier.
+*/
+static curl_prot_t get_protocol_family(const struct Curl_handler *h)
+{
+ DEBUGASSERT(h);
+ DEBUGASSERT(h->family);
+ return h->family;
+}
+
+
+/*
+ * Protocol table. Schemes (roughly) in 2019 popularity order:
+ *
+ * HTTPS, HTTP, FTP, FTPS, SFTP, FILE, SCP, SMTP, LDAP, IMAPS, TELNET, IMAP,
+ * LDAPS, SMTPS, TFTP, SMB, POP3, GOPHER POP3S, RTSP, RTMP, SMBS, DICT
+ */
+static const struct Curl_handler * const protocols[] = {
+
+#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)
+ &Curl_handler_https,
+#endif
+
+#ifndef CURL_DISABLE_HTTP
+ &Curl_handler_http,
+#endif
+
+#ifdef USE_WEBSOCKETS
+#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)
+ &Curl_handler_wss,
+#endif
+
+#ifndef CURL_DISABLE_HTTP
+ &Curl_handler_ws,
+#endif
+#endif
+
+#ifndef CURL_DISABLE_FTP
+ &Curl_handler_ftp,
+#endif
+
+#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP)
+ &Curl_handler_ftps,
+#endif
+
+#if defined(USE_SSH)
+ &Curl_handler_sftp,
+#endif
+
+#ifndef CURL_DISABLE_FILE
+ &Curl_handler_file,
+#endif
+
+#if defined(USE_SSH) && !defined(USE_WOLFSSH)
+ &Curl_handler_scp,
+#endif
+
+#ifndef CURL_DISABLE_SMTP
+ &Curl_handler_smtp,
+#ifdef USE_SSL
+ &Curl_handler_smtps,
+#endif
+#endif
+
+#ifndef CURL_DISABLE_LDAP
+ &Curl_handler_ldap,
+#if !defined(CURL_DISABLE_LDAPS) && \
+ ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \
+ (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL)))
+ &Curl_handler_ldaps,
+#endif
+#endif
+
+#ifndef CURL_DISABLE_IMAP
+ &Curl_handler_imap,
+#ifdef USE_SSL
+ &Curl_handler_imaps,
+#endif
+#endif
+
+#ifndef CURL_DISABLE_TELNET
+ &Curl_handler_telnet,
+#endif
+
+#ifndef CURL_DISABLE_TFTP
+ &Curl_handler_tftp,
+#endif
+
+#ifndef CURL_DISABLE_POP3
+ &Curl_handler_pop3,
+#ifdef USE_SSL
+ &Curl_handler_pop3s,
+#endif
+#endif
+
+#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \
+ (SIZEOF_CURL_OFF_T > 4)
+ &Curl_handler_smb,
+#ifdef USE_SSL
+ &Curl_handler_smbs,
+#endif
+#endif
+
+#ifndef CURL_DISABLE_RTSP
+ &Curl_handler_rtsp,
+#endif
+
+#ifndef CURL_DISABLE_MQTT
+ &Curl_handler_mqtt,
+#endif
+
+#ifndef CURL_DISABLE_GOPHER
+ &Curl_handler_gopher,
+#ifdef USE_SSL
+ &Curl_handler_gophers,
+#endif
+#endif
+
+#ifdef USE_LIBRTMP
+ &Curl_handler_rtmp,
+ &Curl_handler_rtmpt,
+ &Curl_handler_rtmpe,
+ &Curl_handler_rtmpte,
+ &Curl_handler_rtmps,
+ &Curl_handler_rtmpts,
+#endif
+
+#ifndef CURL_DISABLE_DICT
+ &Curl_handler_dict,
+#endif
+
+ (struct Curl_handler *) NULL
+};
+
+/*
+ * Dummy handler for undefined protocol schemes.
+ */
+
+static const struct Curl_handler Curl_handler_dummy = {
+ "<no protocol>", /* scheme */
+ ZERO_NULL, /* setup_connection */
+ ZERO_NULL, /* do_it */
+ ZERO_NULL, /* done */
+ ZERO_NULL, /* do_more */
+ ZERO_NULL, /* connect_it */
+ ZERO_NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ ZERO_NULL, /* proto_getsock */
+ ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ ZERO_NULL, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ 0, /* defport */
+ 0, /* protocol */
+ 0, /* family */
+ PROTOPT_NONE /* flags */
+};
+
+void Curl_freeset(struct Curl_easy *data)
+{
+ /* Free all dynamic strings stored in the data->set substructure. */
+ enum dupstring i;
+ enum dupblob j;
+
+ for(i = (enum dupstring)0; i < STRING_LAST; i++) {
+ Curl_safefree(data->set.str[i]);
+ }
+
+ for(j = (enum dupblob)0; j < BLOB_LAST; j++) {
+ Curl_safefree(data->set.blobs[j]);
+ }
+
+ if(data->state.referer_alloc) {
+ Curl_safefree(data->state.referer);
+ data->state.referer_alloc = FALSE;
+ }
+ data->state.referer = NULL;
+ if(data->state.url_alloc) {
+ Curl_safefree(data->state.url);
+ data->state.url_alloc = FALSE;
+ }
+ data->state.url = NULL;
+
+ Curl_mime_cleanpart(&data->set.mimepost);
+}
+
+/* free the URL pieces */
+static void up_free(struct Curl_easy *data)
+{
+ struct urlpieces *up = &data->state.up;
+ Curl_safefree(up->scheme);
+ Curl_safefree(up->hostname);
+ Curl_safefree(up->port);
+ Curl_safefree(up->user);
+ Curl_safefree(up->password);
+ Curl_safefree(up->options);
+ Curl_safefree(up->path);
+ Curl_safefree(up->query);
+ curl_url_cleanup(data->state.uh);
+ data->state.uh = NULL;
+}
+
+/*
+ * This is the internal function curl_easy_cleanup() calls. This should
+ * cleanup and free all resources associated with this sessionhandle.
+ *
+ * We ignore SIGPIPE when this is called from curl_easy_cleanup.
+ */
+
+CURLcode Curl_close(struct Curl_easy **datap)
+{
+ struct Curl_multi *m;
+ struct Curl_easy *data;
+
+ if(!datap || !*datap)
+ return CURLE_OK;
+
+ data = *datap;
+ *datap = NULL;
+
+ Curl_expire_clear(data); /* shut off timers */
+
+ /* Detach connection if any is left. This should not be normal, but can be
+ the case for example with CONNECT_ONLY + recv/send (test 556) */
+ Curl_detach_connection(data);
+ m = data->multi;
+ if(m)
+ /* This handle is still part of a multi handle, take care of this first
+ and detach this handle from there. */
+ curl_multi_remove_handle(data->multi, data);
+
+ if(data->multi_easy) {
+ /* when curl_easy_perform() is used, it creates its own multi handle to
+ use and this is the one */
+ curl_multi_cleanup(data->multi_easy);
+ data->multi_easy = NULL;
+ }
+
+ /* Destroy the timeout list that is held in the easy handle. It is
+ /normally/ done by curl_multi_remove_handle() but this is "just in
+ case" */
+ Curl_llist_destroy(&data->state.timeoutlist, NULL);
+
+ data->magic = 0; /* force a clear AFTER the possibly enforced removal from
+ the multi handle, since that function uses the magic
+ field! */
+
+ if(data->state.rangestringalloc)
+ free(data->state.range);
+
+ /* freed here just in case DONE wasn't called */
+ Curl_free_request_state(data);
+
+ /* Close down all open SSL info and sessions */
+ Curl_ssl_close_all(data);
+ Curl_safefree(data->state.first_host);
+ Curl_safefree(data->state.scratch);
+ Curl_ssl_free_certinfo(data);
+
+ /* Cleanup possible redirect junk */
+ free(data->req.newurl);
+ data->req.newurl = NULL;
+
+ if(data->state.referer_alloc) {
+ Curl_safefree(data->state.referer);
+ data->state.referer_alloc = FALSE;
+ }
+ data->state.referer = NULL;
+
+ up_free(data);
+ Curl_safefree(data->state.buffer);
+ Curl_dyn_free(&data->state.headerb);
+ Curl_safefree(data->state.ulbuf);
+ Curl_flush_cookies(data, TRUE);
+ Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]);
+ Curl_altsvc_cleanup(&data->asi);
+ Curl_hsts_save(data, data->hsts, data->set.str[STRING_HSTS]);
+ Curl_hsts_cleanup(&data->hsts);
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+ Curl_http_auth_cleanup_digest(data);
+#endif
+ Curl_safefree(data->info.contenttype);
+ Curl_safefree(data->info.wouldredirect);
+
+ /* this destroys the channel and we cannot use it anymore after this */
+ Curl_resolver_cancel(data);
+ Curl_resolver_cleanup(data->state.async.resolver);
+
+ Curl_http2_cleanup_dependencies(data);
+
+ /* No longer a dirty share, if it exists */
+ if(data->share) {
+ Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
+ data->share->dirty--;
+ Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
+ }
+
+ Curl_safefree(data->state.aptr.proxyuserpwd);
+ Curl_safefree(data->state.aptr.uagent);
+ Curl_safefree(data->state.aptr.userpwd);
+ Curl_safefree(data->state.aptr.accept_encoding);
+ Curl_safefree(data->state.aptr.te);
+ Curl_safefree(data->state.aptr.rangeline);
+ Curl_safefree(data->state.aptr.ref);
+ Curl_safefree(data->state.aptr.host);
+ Curl_safefree(data->state.aptr.cookiehost);
+ Curl_safefree(data->state.aptr.rtsp_transport);
+ Curl_safefree(data->state.aptr.user);
+ Curl_safefree(data->state.aptr.passwd);
+ Curl_safefree(data->state.aptr.proxyuser);
+ Curl_safefree(data->state.aptr.proxypasswd);
+
+#ifndef CURL_DISABLE_DOH
+ if(data->req.doh) {
+ Curl_dyn_free(&data->req.doh->probe[0].serverdoh);
+ Curl_dyn_free(&data->req.doh->probe[1].serverdoh);
+ curl_slist_free_all(data->req.doh->headers);
+ Curl_safefree(data->req.doh);
+ }
+#endif
+
+ /* destruct wildcard structures if it is needed */
+ Curl_wildcard_dtor(&data->wildcard);
+ Curl_freeset(data);
+ Curl_headers_cleanup(data);
+ free(data);
+ return CURLE_OK;
+}
+
+/*
+ * Initialize the UserDefined fields within a Curl_easy.
+ * This may be safely called on a new or existing Curl_easy.
+ */
+CURLcode Curl_init_userdefined(struct Curl_easy *data)
+{
+ struct UserDefined *set = &data->set;
+ CURLcode result = CURLE_OK;
+
+ set->out = stdout; /* default output to stdout */
+ set->in_set = stdin; /* default input from stdin */
+ set->err = stderr; /* default stderr to stderr */
+
+ /* use fwrite as default function to store output */
+ set->fwrite_func = (curl_write_callback)fwrite;
+
+ /* use fread as default function to read input */
+ set->fread_func_set = (curl_read_callback)fread;
+ set->is_fread_set = 0;
+
+ set->seek_func = ZERO_NULL;
+ set->seek_client = ZERO_NULL;
+
+ set->filesize = -1; /* we don't know the size */
+ set->postfieldsize = -1; /* unknown size */
+ set->maxredirs = -1; /* allow any amount by default */
+
+ set->method = HTTPREQ_GET; /* Default HTTP request */
+#ifndef CURL_DISABLE_RTSP
+ set->rtspreq = RTSPREQ_OPTIONS; /* Default RTSP request */
+#endif
+#ifndef CURL_DISABLE_FTP
+ set->ftp_use_epsv = TRUE; /* FTP defaults to EPSV operations */
+ set->ftp_use_eprt = TRUE; /* FTP defaults to EPRT operations */
+ set->ftp_use_pret = FALSE; /* mainly useful for drftpd servers */
+ set->ftp_filemethod = FTPFILE_MULTICWD;
+ set->ftp_skip_ip = TRUE; /* skip PASV IP by default */
+#endif
+ set->dns_cache_timeout = 60; /* Timeout every 60 seconds by default */
+
+ /* Set the default size of the SSL session ID cache */
+ set->general_ssl.max_ssl_sessions = 5;
+ /* Timeout every 24 hours by default */
+ set->general_ssl.ca_cache_timeout = 24 * 60 * 60;
+
+ set->proxyport = 0;
+ set->proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */
+ set->httpauth = CURLAUTH_BASIC; /* defaults to basic */
+
+#ifndef CURL_DISABLE_PROXY
+ set->proxyauth = CURLAUTH_BASIC; /* defaults to basic */
+ /* SOCKS5 proxy auth defaults to username/password + GSS-API */
+ set->socks5auth = CURLAUTH_BASIC | CURLAUTH_GSSAPI;
+#endif
+
+ /* make libcurl quiet by default: */
+ set->hide_progress = TRUE; /* CURLOPT_NOPROGRESS changes these */
+
+ Curl_mime_initpart(&set->mimepost);
+
+ /*
+ * libcurl 7.10 introduced SSL verification *by default*! This needs to be
+ * switched off unless wanted.
+ */
+#ifndef CURL_DISABLE_DOH
+ set->doh_verifyhost = TRUE;
+ set->doh_verifypeer = TRUE;
+#endif
+ set->ssl.primary.verifypeer = TRUE;
+ set->ssl.primary.verifyhost = TRUE;
+#ifdef USE_TLS_SRP
+ set->ssl.primary.authtype = CURL_TLSAUTH_NONE;
+#endif
+ /* defaults to any auth type */
+ set->ssh_auth_types = CURLSSH_AUTH_DEFAULT;
+ set->ssl.primary.sessionid = TRUE; /* session ID caching enabled by
+ default */
+#ifndef CURL_DISABLE_PROXY
+ set->proxy_ssl = set->ssl;
+#endif
+
+ set->new_file_perms = 0644; /* Default permissions */
+ set->new_directory_perms = 0755; /* Default permissions */
+ set->allowed_protocols = (curl_prot_t) CURLPROTO_ALL;
+ set->redir_protocols = CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP |
+ CURLPROTO_FTPS;
+
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+ /*
+ * disallow unprotected protection negotiation NEC reference implementation
+ * seem not to follow rfc1961 section 4.3/4.4
+ */
+ set->socks5_gssapi_nec = FALSE;
+#endif
+
+ /* Set the default CA cert bundle/path detected/specified at build time.
+ *
+ * If Schannel is the selected SSL backend then these locations are
+ * ignored. We allow setting CA location for schannel only when explicitly
+ * specified by the user via CURLOPT_CAINFO / --cacert.
+ */
+ if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) {
+#if defined(CURL_CA_BUNDLE)
+ result = Curl_setstropt(&set->str[STRING_SSL_CAFILE], CURL_CA_BUNDLE);
+ if(result)
+ return result;
+
+ result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_PROXY],
+ CURL_CA_BUNDLE);
+ if(result)
+ return result;
+#endif
+#if defined(CURL_CA_PATH)
+ result = Curl_setstropt(&set->str[STRING_SSL_CAPATH], CURL_CA_PATH);
+ if(result)
+ return result;
+
+ result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_PROXY], CURL_CA_PATH);
+ if(result)
+ return result;
+#endif
+ }
+
+#ifndef CURL_DISABLE_FTP
+ set->wildcard_enabled = FALSE;
+ set->chunk_bgn = ZERO_NULL;
+ set->chunk_end = ZERO_NULL;
+ set->fnmatch = ZERO_NULL;
+#endif
+ set->tcp_keepalive = FALSE;
+ set->tcp_keepintvl = 60;
+ set->tcp_keepidle = 60;
+ set->tcp_fastopen = FALSE;
+ set->tcp_nodelay = TRUE;
+ set->ssl_enable_alpn = TRUE;
+ set->expect_100_timeout = 1000L; /* Wait for a second by default. */
+ set->sep_headers = TRUE; /* separated header lists by default */
+ set->buffer_size = READBUFFER_SIZE;
+ set->upload_buffer_size = UPLOADBUFFER_DEFAULT;
+ set->happy_eyeballs_timeout = CURL_HET_DEFAULT;
+ set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT;
+ set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */
+ set->maxage_conn = 118;
+ set->maxlifetime_conn = 0;
+ set->http09_allowed = FALSE;
+ set->httpwant =
+#ifdef USE_HTTP2
+ CURL_HTTP_VERSION_2TLS
+#else
+ CURL_HTTP_VERSION_1_1
+#endif
+ ;
+ Curl_http2_init_userset(set);
+ set->quick_exit = 0L;
+ return result;
+}
+
+/**
+ * Curl_open()
+ *
+ * @param curl is a pointer to a sessionhandle pointer that gets set by this
+ * function.
+ * @return CURLcode
+ */
+
+CURLcode Curl_open(struct Curl_easy **curl)
+{
+ CURLcode result;
+ struct Curl_easy *data;
+
+ /* Very simple start-up: alloc the struct, init it with zeroes and return */
+ data = calloc(1, sizeof(struct Curl_easy));
+ if(!data) {
+ /* this is a very serious error */
+ DEBUGF(fprintf(stderr, "Error: calloc of Curl_easy failed\n"));
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ data->magic = CURLEASY_MAGIC_NUMBER;
+
+ result = Curl_resolver_init(data, &data->state.async.resolver);
+ if(result) {
+ DEBUGF(fprintf(stderr, "Error: resolver_init failed\n"));
+ free(data);
+ return result;
+ }
+
+ result = Curl_init_userdefined(data);
+ if(!result) {
+ Curl_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER);
+ Curl_initinfo(data);
+
+ /* most recent connection is not yet defined */
+ data->state.lastconnect_id = -1;
+
+ data->progress.flags |= PGRS_HIDE;
+ data->state.current_speed = -1; /* init to negative == impossible */
+ }
+
+ if(result) {
+ Curl_resolver_cleanup(data->state.async.resolver);
+ Curl_dyn_free(&data->state.headerb);
+ Curl_freeset(data);
+ free(data);
+ data = NULL;
+ }
+ else
+ *curl = data;
+
+ return result;
+}
+
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+static void conn_reset_postponed_data(struct connectdata *conn, int num)
+{
+ struct postponed_data * const psnd = &(conn->postponed[num]);
+ if(psnd->buffer) {
+ DEBUGASSERT(psnd->allocated_size > 0);
+ DEBUGASSERT(psnd->recv_size <= psnd->allocated_size);
+ DEBUGASSERT(psnd->recv_size ?
+ (psnd->recv_processed < psnd->recv_size) :
+ (psnd->recv_processed == 0));
+ DEBUGASSERT(psnd->bindsock != CURL_SOCKET_BAD);
+ free(psnd->buffer);
+ psnd->buffer = NULL;
+ psnd->allocated_size = 0;
+ psnd->recv_size = 0;
+ psnd->recv_processed = 0;
+#ifdef DEBUGBUILD
+ psnd->bindsock = CURL_SOCKET_BAD; /* used only for DEBUGASSERT */
+#endif /* DEBUGBUILD */
+ }
+ else {
+ DEBUGASSERT(psnd->allocated_size == 0);
+ DEBUGASSERT(psnd->recv_size == 0);
+ DEBUGASSERT(psnd->recv_processed == 0);
+ DEBUGASSERT(psnd->bindsock == CURL_SOCKET_BAD);
+ }
+}
+
+static void conn_reset_all_postponed_data(struct connectdata *conn)
+{
+ conn_reset_postponed_data(conn, 0);
+ conn_reset_postponed_data(conn, 1);
+}
+#else /* ! USE_RECV_BEFORE_SEND_WORKAROUND */
+/* Use "do-nothing" macro instead of function when workaround not used */
+#define conn_reset_all_postponed_data(c) do {} while(0)
+#endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */
+
+
+static void conn_shutdown(struct Curl_easy *data)
+{
+ DEBUGASSERT(data);
+ infof(data, "Closing connection %ld", data->conn->connection_id);
+
+ /* possible left-overs from the async name resolvers */
+ Curl_resolver_cancel(data);
+
+ Curl_conn_close(data, SECONDARYSOCKET);
+ Curl_conn_close(data, FIRSTSOCKET);
+}
+
+static void conn_free(struct Curl_easy *data, struct connectdata *conn)
+{
+ size_t i;
+
+ DEBUGASSERT(conn);
+
+ for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
+ Curl_conn_cf_discard_all(data, conn, (int)i);
+ }
+
+ Curl_free_idnconverted_hostname(&conn->host);
+ Curl_free_idnconverted_hostname(&conn->conn_to_host);
+#ifndef CURL_DISABLE_PROXY
+ Curl_free_idnconverted_hostname(&conn->http_proxy.host);
+ Curl_free_idnconverted_hostname(&conn->socks_proxy.host);
+ Curl_safefree(conn->http_proxy.user);
+ Curl_safefree(conn->socks_proxy.user);
+ Curl_safefree(conn->http_proxy.passwd);
+ Curl_safefree(conn->socks_proxy.passwd);
+ Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */
+ Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */
+ Curl_free_primary_ssl_config(&conn->proxy_ssl_config);
+#endif
+ Curl_safefree(conn->user);
+ Curl_safefree(conn->passwd);
+ Curl_safefree(conn->sasl_authzid);
+ Curl_safefree(conn->options);
+ Curl_safefree(conn->oauth_bearer);
+ Curl_dyn_free(&conn->trailer);
+ Curl_safefree(conn->host.rawalloc); /* host name buffer */
+ Curl_safefree(conn->conn_to_host.rawalloc); /* host name buffer */
+ Curl_safefree(conn->hostname_resolve);
+ Curl_safefree(conn->secondaryhostname);
+
+ conn_reset_all_postponed_data(conn);
+ Curl_llist_destroy(&conn->easyq, NULL);
+ Curl_safefree(conn->localdev);
+ Curl_free_primary_ssl_config(&conn->ssl_config);
+
+#ifdef USE_UNIX_SOCKETS
+ Curl_safefree(conn->unix_domain_socket);
+#endif
+
+ free(conn); /* free all the connection oriented data */
+}
+
+/*
+ * Disconnects the given connection. Note the connection may not be the
+ * primary connection, like when freeing room in the connection cache or
+ * killing of a dead old connection.
+ *
+ * A connection needs an easy handle when closing down. We support this passed
+ * in separately since the connection to get closed here is often already
+ * disassociated from an easy handle.
+ *
+ * This function MUST NOT reset state in the Curl_easy struct if that
+ * isn't strictly bound to the life-time of *this* particular connection.
+ *
+ */
+
+void Curl_disconnect(struct Curl_easy *data,
+ struct connectdata *conn, bool dead_connection)
+{
+ /* there must be a connection to close */
+ DEBUGASSERT(conn);
+
+ /* it must be removed from the connection cache */
+ DEBUGASSERT(!conn->bundle);
+
+ /* there must be an associated transfer */
+ DEBUGASSERT(data);
+
+ /* the transfer must be detached from the connection */
+ DEBUGASSERT(!data->conn);
+
+ DEBUGF(infof(data, "Curl_disconnect(conn #%ld, dead=%d)",
+ conn->connection_id, dead_connection));
+ /*
+ * If this connection isn't marked to force-close, leave it open if there
+ * are other users of it
+ */
+ if(CONN_INUSE(conn) && !dead_connection) {
+ DEBUGF(infof(data, "Curl_disconnect when inuse: %zu", CONN_INUSE(conn)));
+ return;
+ }
+
+ if(conn->dns_entry) {
+ Curl_resolv_unlock(data, conn->dns_entry);
+ conn->dns_entry = NULL;
+ }
+
+ /* Cleanup NTLM connection-related data */
+ Curl_http_auth_cleanup_ntlm(conn);
+
+ /* Cleanup NEGOTIATE connection-related data */
+ Curl_http_auth_cleanup_negotiate(conn);
+
+ if(conn->connect_only)
+ /* treat the connection as dead in CONNECT_ONLY situations */
+ dead_connection = TRUE;
+
+ /* temporarily attach the connection to this transfer handle for the
+ disconnect and shutdown */
+ Curl_attach_connection(data, conn);
+
+ if(conn->handler->disconnect)
+ /* This is set if protocol-specific cleanups should be made */
+ conn->handler->disconnect(data, conn, dead_connection);
+
+ conn_shutdown(data);
+
+ /* detach it again */
+ Curl_detach_connection(data);
+
+ conn_free(data, conn);
+}
+
+/*
+ * This function should return TRUE if the socket is to be assumed to
+ * be dead. Most commonly this happens when the server has closed the
+ * connection due to inactivity.
+ */
+static bool SocketIsDead(curl_socket_t sock)
+{
+ int sval;
+ bool ret_val = TRUE;
+
+ sval = SOCKET_READABLE(sock, 0);
+ if(sval == 0)
+ /* timeout */
+ ret_val = FALSE;
+
+ return ret_val;
+}
+
+/*
+ * IsMultiplexingPossible()
+ *
+ * Return a bitmask with the available multiplexing options for the given
+ * requested connection.
+ */
+static int IsMultiplexingPossible(const struct Curl_easy *handle,
+ const struct connectdata *conn)
+{
+ int avail = 0;
+
+ /* If an HTTP protocol and multiplexing is enabled */
+ if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
+ (!conn->bits.protoconnstart || !conn->bits.close)) {
+
+ if(Curl_multiplex_wanted(handle->multi) &&
+ (handle->state.httpwant >= CURL_HTTP_VERSION_2))
+ /* allows HTTP/2 */
+ avail |= CURLPIPE_MULTIPLEX;
+ }
+ return avail;
+}
+
+#ifndef CURL_DISABLE_PROXY
+static bool
+proxy_info_matches(const struct proxy_info *data,
+ const struct proxy_info *needle)
+{
+ if((data->proxytype == needle->proxytype) &&
+ (data->port == needle->port) &&
+ strcasecompare(data->host.name, needle->host.name))
+ return TRUE;
+
+ return FALSE;
+}
+
+static bool
+socks_proxy_info_matches(const struct proxy_info *data,
+ const struct proxy_info *needle)
+{
+ if(!proxy_info_matches(data, needle))
+ return FALSE;
+
+ /* the user information is case-sensitive
+ or at least it is not defined as case-insensitive
+ see https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1 */
+
+ /* curl_strequal does a case insensitive comparison,
+ so do not use it here! */
+ if(Curl_timestrcmp(data->user, needle->user) ||
+ Curl_timestrcmp(data->passwd, needle->passwd))
+ return FALSE;
+ return TRUE;
+}
+#else
+/* disabled, won't get called */
+#define proxy_info_matches(x,y) FALSE
+#define socks_proxy_info_matches(x,y) FALSE
+#endif
+
+/* A connection has to have been idle for a shorter time than 'maxage_conn'
+ (the success rate is just too low after this), or created less than
+ 'maxlifetime_conn' ago, to be subject for reuse. */
+
+static bool conn_maxage(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct curltime now)
+{
+ timediff_t idletime, lifetime;
+
+ idletime = Curl_timediff(now, conn->lastused);
+ idletime /= 1000; /* integer seconds is fine */
+
+ if(idletime > data->set.maxage_conn) {
+ infof(data, "Too old connection (%ld seconds idle), disconnect it",
+ idletime);
+ return TRUE;
+ }
+
+ lifetime = Curl_timediff(now, conn->created);
+ lifetime /= 1000; /* integer seconds is fine */
+
+ if(data->set.maxlifetime_conn && lifetime > data->set.maxlifetime_conn) {
+ infof(data,
+ "Too old connection (%ld seconds since creation), disconnect it",
+ lifetime);
+ return TRUE;
+ }
+
+
+ return FALSE;
+}
+
+/*
+ * This function checks if the given connection is dead and extracts it from
+ * the connection cache if so.
+ *
+ * When this is called as a Curl_conncache_foreach() callback, the connection
+ * cache lock is held!
+ *
+ * Returns TRUE if the connection was dead and extracted.
+ */
+static bool extract_if_dead(struct connectdata *conn,
+ struct Curl_easy *data)
+{
+ if(!CONN_INUSE(conn)) {
+ /* The check for a dead socket makes sense only if the connection isn't in
+ use */
+ bool dead;
+ struct curltime now = Curl_now();
+ if(conn_maxage(data, conn, now)) {
+ /* avoid check if already too old */
+ dead = TRUE;
+ }
+ else if(conn->handler->connection_check) {
+ /* The protocol has a special method for checking the state of the
+ connection. Use it to check if the connection is dead. */
+ unsigned int state;
+
+ /* briefly attach the connection to this transfer for the purpose of
+ checking it */
+ Curl_attach_connection(data, conn);
+
+ state = conn->handler->connection_check(data, conn, CONNCHECK_ISDEAD);
+ dead = (state & CONNRESULT_DEAD);
+ /* detach the connection again */
+ Curl_detach_connection(data);
+
+ }
+ else {
+ /* Use the general method for determining the death of a connection */
+ dead = SocketIsDead(conn->sock[FIRSTSOCKET]);
+ }
+
+ if(dead) {
+ infof(data, "Connection %ld seems to be dead", conn->connection_id);
+ Curl_conncache_remove_conn(data, conn, FALSE);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+struct prunedead {
+ struct Curl_easy *data;
+ struct connectdata *extracted;
+};
+
+/*
+ * Wrapper to use extract_if_dead() function in Curl_conncache_foreach()
+ *
+ */
+static int call_extract_if_dead(struct Curl_easy *data,
+ struct connectdata *conn, void *param)
+{
+ struct prunedead *p = (struct prunedead *)param;
+ if(extract_if_dead(conn, data)) {
+ /* stop the iteration here, pass back the connection that was extracted */
+ p->extracted = conn;
+ return 1;
+ }
+ return 0; /* continue iteration */
+}
+
+/*
+ * This function scans the connection cache 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.
+ */
+static void prune_dead_connections(struct Curl_easy *data)
+{
+ struct curltime now = Curl_now();
+ timediff_t elapsed;
+
+ DEBUGASSERT(!data->conn); /* no connection */
+ CONNCACHE_LOCK(data);
+ elapsed =
+ Curl_timediff(now, data->state.conn_cache->last_cleanup);
+ CONNCACHE_UNLOCK(data);
+
+ if(elapsed >= 1000L) {
+ struct prunedead prune;
+ prune.data = data;
+ prune.extracted = NULL;
+ while(Curl_conncache_foreach(data, data->state.conn_cache, &prune,
+ call_extract_if_dead)) {
+ /* unlocked */
+
+ /* remove connection from cache */
+ Curl_conncache_remove_conn(data, prune.extracted, TRUE);
+
+ /* disconnect it */
+ Curl_disconnect(data, prune.extracted, TRUE);
+ }
+ CONNCACHE_LOCK(data);
+ data->state.conn_cache->last_cleanup = now;
+ CONNCACHE_UNLOCK(data);
+ }
+}
+
+#ifdef USE_SSH
+static bool ssh_config_matches(struct connectdata *one,
+ struct connectdata *two)
+{
+ return (Curl_safecmp(one->proto.sshc.rsa, two->proto.sshc.rsa) &&
+ Curl_safecmp(one->proto.sshc.rsa_pub, two->proto.sshc.rsa_pub));
+}
+#else
+#define ssh_config_matches(x,y) FALSE
+#endif
+
+/*
+ * Given one filled in connection struct (named needle), this function should
+ * detect if there already is one that has all the significant details
+ * exactly the same and thus should be used instead.
+ *
+ * If there is a match, this function returns TRUE - and has marked the
+ * connection as 'in-use'. It must later be called with ConnectionDone() to
+ * return back to 'idle' (unused) state.
+ *
+ * The force_reuse flag is set if the connection must be used.
+ */
+static bool
+ConnectionExists(struct Curl_easy *data,
+ struct connectdata *needle,
+ struct connectdata **usethis,
+ bool *force_reuse,
+ bool *waitpipe)
+{
+ struct connectdata *check;
+ struct connectdata *chosen = 0;
+ bool foundPendingCandidate = FALSE;
+ bool canmultiplex = IsMultiplexingPossible(data, needle);
+ struct connectbundle *bundle;
+
+#ifdef USE_NTLM
+ bool wantNTLMhttp = ((data->state.authhost.want &
+ (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) &&
+ (needle->handler->protocol & PROTO_FAMILY_HTTP));
+#ifndef CURL_DISABLE_PROXY
+ bool wantProxyNTLMhttp = (needle->bits.proxy_user_passwd &&
+ ((data->state.authproxy.want &
+ (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) &&
+ (needle->handler->protocol & PROTO_FAMILY_HTTP)));
+#else
+ bool wantProxyNTLMhttp = FALSE;
+#endif
+#endif
+
+ *force_reuse = FALSE;
+ *waitpipe = FALSE;
+
+ /* Look up the bundle with all the connections to this particular host.
+ Locks the connection cache, beware of early returns! */
+ bundle = Curl_conncache_find_bundle(data, needle, data->state.conn_cache);
+ if(bundle) {
+ /* Max pipe length is zero (unlimited) for multiplexed connections */
+ struct Curl_llist_element *curr;
+
+ infof(data, "Found bundle for host: %p [%s]",
+ (void *)bundle, (bundle->multiuse == BUNDLE_MULTIPLEX ?
+ "can multiplex" : "serially"));
+
+ /* We can't multiplex if we don't know anything about the server */
+ if(canmultiplex) {
+ if(bundle->multiuse == BUNDLE_UNKNOWN) {
+ if(data->set.pipewait) {
+ infof(data, "Server doesn't support multiplex yet, wait");
+ *waitpipe = TRUE;
+ CONNCACHE_UNLOCK(data);
+ return FALSE; /* no re-use */
+ }
+
+ infof(data, "Server doesn't support multiplex (yet)");
+ canmultiplex = FALSE;
+ }
+ if((bundle->multiuse == BUNDLE_MULTIPLEX) &&
+ !Curl_multiplex_wanted(data->multi)) {
+ infof(data, "Could multiplex, but not asked to");
+ canmultiplex = FALSE;
+ }
+ if(bundle->multiuse == BUNDLE_NO_MULTIUSE) {
+ infof(data, "Can not multiplex, even if we wanted to");
+ canmultiplex = FALSE;
+ }
+ }
+
+ curr = bundle->conn_list.head;
+ while(curr) {
+ bool match = FALSE;
+ size_t multiplexed = 0;
+
+ /*
+ * Note that if we use an HTTP proxy in normal mode (no tunneling), we
+ * check connections to that proxy and not to the actual remote server.
+ */
+ check = curr->ptr;
+ curr = curr->next;
+
+ if(check->connect_only || check->bits.close)
+ /* connect-only or to-be-closed connections will not be reused */
+ continue;
+
+ if(extract_if_dead(check, data)) {
+ /* disconnect it */
+ Curl_disconnect(data, check, TRUE);
+ continue;
+ }
+
+ if(data->set.ipver != CURL_IPRESOLVE_WHATEVER
+ && data->set.ipver != check->ip_version) {
+ /* skip because the connection is not via the requested IP version */
+ continue;
+ }
+
+ if(bundle->multiuse == BUNDLE_MULTIPLEX)
+ multiplexed = CONN_INUSE(check);
+
+ if(!canmultiplex) {
+ if(multiplexed) {
+ /* can only happen within multi handles, and means that another easy
+ handle is using this connection */
+ continue;
+ }
+
+ if(Curl_resolver_asynch()) {
+ /* primary_ip[0] is NUL only if the resolving of the name hasn't
+ completed yet and until then we don't re-use this connection */
+ if(!check->primary_ip[0]) {
+ infof(data,
+ "Connection #%ld is still name resolving, can't reuse",
+ check->connection_id);
+ continue;
+ }
+ }
+
+ if(!Curl_conn_is_connected(check, FIRSTSOCKET)) {
+ foundPendingCandidate = TRUE;
+ /* Don't pick a connection that hasn't connected yet */
+ infof(data, "Connection #%ld isn't open enough, can't reuse",
+ check->connection_id);
+ continue;
+ }
+ }
+
+#ifdef USE_UNIX_SOCKETS
+ if(needle->unix_domain_socket) {
+ if(!check->unix_domain_socket)
+ continue;
+ if(strcmp(needle->unix_domain_socket, check->unix_domain_socket))
+ continue;
+ if(needle->bits.abstract_unix_socket !=
+ check->bits.abstract_unix_socket)
+ continue;
+ }
+ else if(check->unix_domain_socket)
+ continue;
+#endif
+
+ if((needle->handler->flags&PROTOPT_SSL) !=
+ (check->handler->flags&PROTOPT_SSL))
+ /* don't do mixed SSL and non-SSL connections */
+ if(get_protocol_family(check->handler) !=
+ needle->handler->protocol || !check->bits.tls_upgraded)
+ /* except protocols that have been upgraded via TLS */
+ continue;
+
+#ifndef CURL_DISABLE_PROXY
+ if(needle->bits.httpproxy != check->bits.httpproxy ||
+ needle->bits.socksproxy != check->bits.socksproxy)
+ continue;
+
+ if(needle->bits.socksproxy &&
+ !socks_proxy_info_matches(&needle->socks_proxy,
+ &check->socks_proxy))
+ continue;
+#endif
+ if(needle->bits.conn_to_host != check->bits.conn_to_host)
+ /* don't mix connections that use the "connect to host" feature and
+ * connections that don't use this feature */
+ continue;
+
+ if(needle->bits.conn_to_port != check->bits.conn_to_port)
+ /* don't mix connections that use the "connect to port" feature and
+ * connections that don't use this feature */
+ continue;
+
+#ifndef CURL_DISABLE_PROXY
+ if(needle->bits.httpproxy) {
+ if(!proxy_info_matches(&needle->http_proxy, &check->http_proxy))
+ continue;
+
+ if(needle->bits.tunnel_proxy != check->bits.tunnel_proxy)
+ continue;
+
+ if(needle->http_proxy.proxytype == CURLPROXY_HTTPS) {
+ /* use https proxy */
+ if(needle->handler->flags&PROTOPT_SSL) {
+ /* use double layer ssl */
+ if(!Curl_ssl_config_matches(&needle->proxy_ssl_config,
+ &check->proxy_ssl_config))
+ continue;
+ }
+
+ if(!Curl_ssl_config_matches(&needle->ssl_config,
+ &check->ssl_config))
+ continue;
+ }
+ }
+#endif
+
+ if(!canmultiplex && CONN_INUSE(check))
+ /* this request can't be multiplexed but the checked connection is
+ already in use so we skip it */
+ continue;
+
+ if(CONN_INUSE(check)) {
+ /* Subject for multiplex use if 'checks' belongs to the same multi
+ handle as 'data' is. */
+ struct Curl_llist_element *e = check->easyq.head;
+ struct Curl_easy *entry = e->ptr;
+ if(entry->multi != data->multi)
+ continue;
+ }
+
+ if(needle->localdev || needle->localport) {
+ /* If we are bound to a specific local end (IP+port), we must not
+ re-use a random other one, although if we didn't ask for a
+ particular one we can reuse one that was bound.
+
+ This comparison is a bit rough and too strict. Since the input
+ parameters can be specified in numerous ways and still end up the
+ same it would take a lot of processing to make it really accurate.
+ Instead, this matching will assume that re-uses of bound connections
+ will most likely also re-use the exact same binding parameters and
+ missing out a few edge cases shouldn't hurt anyone very much.
+ */
+ if((check->localport != needle->localport) ||
+ (check->localportrange != needle->localportrange) ||
+ (needle->localdev &&
+ (!check->localdev || strcmp(check->localdev, needle->localdev))))
+ continue;
+ }
+
+ if(!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) {
+ /* This protocol requires credentials per connection,
+ so verify that we're using the same name and password as well */
+ if(Curl_timestrcmp(needle->user, check->user) ||
+ Curl_timestrcmp(needle->passwd, check->passwd) ||
+ Curl_timestrcmp(needle->sasl_authzid, check->sasl_authzid) ||
+ Curl_timestrcmp(needle->oauth_bearer, check->oauth_bearer)) {
+ /* one of them was different */
+ continue;
+ }
+ }
+
+ /* If multiplexing isn't enabled on the h2 connection and h1 is
+ explicitly requested, handle it: */
+ if((needle->handler->protocol & PROTO_FAMILY_HTTP) &&
+ (check->httpversion >= 20) &&
+ (data->state.httpwant < CURL_HTTP_VERSION_2_0))
+ continue;
+
+ if(get_protocol_family(needle->handler) == PROTO_FAMILY_SSH) {
+ if(!ssh_config_matches(needle, check))
+ continue;
+ }
+
+ if((needle->handler->flags&PROTOPT_SSL)
+#ifndef CURL_DISABLE_PROXY
+ || !needle->bits.httpproxy || needle->bits.tunnel_proxy
+#endif
+ ) {
+ /* The requested connection does not use an HTTP proxy or it uses SSL
+ or it is a non-SSL protocol tunneled or it is a non-SSL protocol
+ which is allowed to be upgraded via TLS */
+
+ if((strcasecompare(needle->handler->scheme, check->handler->scheme) ||
+ (get_protocol_family(check->handler) ==
+ needle->handler->protocol && check->bits.tls_upgraded)) &&
+ (!needle->bits.conn_to_host || strcasecompare(
+ needle->conn_to_host.name, check->conn_to_host.name)) &&
+ (!needle->bits.conn_to_port ||
+ needle->conn_to_port == check->conn_to_port) &&
+ strcasecompare(needle->host.name, check->host.name) &&
+ needle->remote_port == check->remote_port) {
+ /* The schemes match or the protocol family is the same and the
+ previous connection was TLS upgraded, and the hostname and host
+ port match */
+ if(needle->handler->flags & PROTOPT_SSL) {
+ /* This is a SSL connection so verify that we're using the same
+ SSL options as well */
+ if(!Curl_ssl_config_matches(&needle->ssl_config,
+ &check->ssl_config)) {
+ DEBUGF(infof(data,
+ "Connection #%ld has different SSL parameters, "
+ "can't reuse",
+ check->connection_id));
+ continue;
+ }
+ }
+ match = TRUE;
+ }
+ }
+ else {
+ /* The requested connection is using the same HTTP proxy in normal
+ mode (no tunneling) */
+ match = TRUE;
+ }
+
+ if(match) {
+#if defined(USE_NTLM)
+ /* If we are looking for an HTTP+NTLM connection, check if this is
+ already authenticating with the right credentials. If not, keep
+ looking so that we can reuse NTLM connections if
+ possible. (Especially we must not reuse the same connection if
+ partway through a handshake!) */
+ if(wantNTLMhttp) {
+ if(Curl_timestrcmp(needle->user, check->user) ||
+ Curl_timestrcmp(needle->passwd, check->passwd)) {
+
+ /* we prefer a credential match, but this is at least a connection
+ that can be reused and "upgraded" to NTLM */
+ if(check->http_ntlm_state == NTLMSTATE_NONE)
+ chosen = check;
+ continue;
+ }
+ }
+ else if(check->http_ntlm_state != NTLMSTATE_NONE) {
+ /* Connection is using NTLM auth but we don't want NTLM */
+ continue;
+ }
+
+#ifndef CURL_DISABLE_PROXY
+ /* Same for Proxy NTLM authentication */
+ if(wantProxyNTLMhttp) {
+ /* Both check->http_proxy.user and check->http_proxy.passwd can be
+ * NULL */
+ if(!check->http_proxy.user || !check->http_proxy.passwd)
+ continue;
+
+ if(Curl_timestrcmp(needle->http_proxy.user,
+ check->http_proxy.user) ||
+ Curl_timestrcmp(needle->http_proxy.passwd,
+ check->http_proxy.passwd))
+ continue;
+ }
+ else if(check->proxy_ntlm_state != NTLMSTATE_NONE) {
+ /* Proxy connection is using NTLM auth but we don't want NTLM */
+ continue;
+ }
+#endif
+ if(wantNTLMhttp || wantProxyNTLMhttp) {
+ /* Credentials are already checked, we can use this connection */
+ chosen = check;
+
+ if((wantNTLMhttp &&
+ (check->http_ntlm_state != NTLMSTATE_NONE)) ||
+ (wantProxyNTLMhttp &&
+ (check->proxy_ntlm_state != NTLMSTATE_NONE))) {
+ /* We must use this connection, no other */
+ *force_reuse = TRUE;
+ break;
+ }
+
+ /* Continue look up for a better connection */
+ continue;
+ }
+#endif
+ if(canmultiplex) {
+ /* We can multiplex if we want to. Let's continue looking for
+ the optimal connection to use. */
+
+ if(!multiplexed) {
+ /* We have the optimal connection. Let's stop looking. */
+ chosen = check;
+ break;
+ }
+
+#ifdef USE_NGHTTP2
+ /* If multiplexed, make sure we don't go over concurrency limit */
+ if(check->bits.multiplex) {
+ /* Multiplexed connections can only be HTTP/2 for now */
+ struct http_conn *httpc = &check->proto.httpc;
+ if(multiplexed >= httpc->settings.max_concurrent_streams) {
+ infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)",
+ multiplexed);
+ continue;
+ }
+ else if(multiplexed >=
+ Curl_multi_max_concurrent_streams(data->multi)) {
+ infof(data, "client side MAX_CONCURRENT_STREAMS reached"
+ ", skip (%zu)",
+ multiplexed);
+ continue;
+ }
+ }
+#endif
+ /* When not multiplexed, we have a match here! */
+ chosen = check;
+ infof(data, "Multiplexed connection found");
+ break;
+ }
+ else {
+ /* We have found a connection. Let's stop searching. */
+ chosen = check;
+ break;
+ }
+ }
+ }
+ }
+
+ if(chosen) {
+ /* mark it as used before releasing the lock */
+ Curl_attach_connection(data, chosen);
+ CONNCACHE_UNLOCK(data);
+ *usethis = chosen;
+ return TRUE; /* yes, we found one to use! */
+ }
+ CONNCACHE_UNLOCK(data);
+
+ if(foundPendingCandidate && data->set.pipewait) {
+ infof(data,
+ "Found pending candidate for reuse and CURLOPT_PIPEWAIT is set");
+ *waitpipe = TRUE;
+ }
+
+ return FALSE; /* no matching connecting exists */
+}
+
+/*
+ * verboseconnect() displays verbose information after a connect
+ */
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+void Curl_verboseconnect(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ if(data->set.verbose)
+ infof(data, "Connected to %s (%s) port %u (#%ld)",
+#ifndef CURL_DISABLE_PROXY
+ conn->bits.socksproxy ? conn->socks_proxy.host.dispname :
+ conn->bits.httpproxy ? conn->http_proxy.host.dispname :
+#endif
+ conn->bits.conn_to_host ? conn->conn_to_host.dispname :
+ conn->host.dispname,
+ conn->primary_ip, conn->port, conn->connection_id);
+}
+#endif
+
+/*
+ * Allocate and initialize a new connectdata object.
+ */
+static struct connectdata *allocate_conn(struct Curl_easy *data)
+{
+ struct connectdata *conn = calloc(1, sizeof(struct connectdata));
+ if(!conn)
+ return NULL;
+
+ conn->handler = &Curl_handler_dummy; /* Be sure we have a handler defined
+ already from start to avoid NULL
+ situations and checks */
+
+ /* and we setup a few fields in case we end up actually using this struct */
+
+ conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
+ conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
+ conn->tempsock[0] = CURL_SOCKET_BAD; /* no file descriptor */
+ conn->tempsock[1] = CURL_SOCKET_BAD; /* no file descriptor */
+ conn->connection_id = -1; /* no ID */
+ conn->port = -1; /* unknown at this point */
+ conn->remote_port = -1; /* unknown at this point */
+#if defined(USE_RECV_BEFORE_SEND_WORKAROUND) && defined(DEBUGBUILD)
+ conn->postponed[0].bindsock = CURL_SOCKET_BAD; /* no file descriptor */
+ conn->postponed[1].bindsock = CURL_SOCKET_BAD; /* no file descriptor */
+#endif /* USE_RECV_BEFORE_SEND_WORKAROUND && DEBUGBUILD */
+
+ /* Default protocol-independent behavior doesn't support persistent
+ connections, so we set this to force-close. Protocols that support
+ this need to set this to FALSE in their "curl_do" functions. */
+ connclose(conn, "Default to force-close");
+
+ /* Store creation time to help future close decision making */
+ conn->created = Curl_now();
+
+ /* Store current time to give a baseline to keepalive connection times. */
+ conn->keepalive = Curl_now();
+
+#ifndef CURL_DISABLE_PROXY
+ conn->http_proxy.proxytype = data->set.proxytype;
+ conn->socks_proxy.proxytype = CURLPROXY_SOCKS4;
+
+ /* note that these two proxy bits are now just on what looks to be
+ requested, they may be altered down the road */
+ conn->bits.proxy = (data->set.str[STRING_PROXY] &&
+ *data->set.str[STRING_PROXY]) ? TRUE : FALSE;
+ conn->bits.httpproxy = (conn->bits.proxy &&
+ (conn->http_proxy.proxytype == CURLPROXY_HTTP ||
+ conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0 ||
+ conn->http_proxy.proxytype == CURLPROXY_HTTPS)) ?
+ TRUE : FALSE;
+ conn->bits.socksproxy = (conn->bits.proxy &&
+ !conn->bits.httpproxy) ? TRUE : FALSE;
+
+ if(data->set.str[STRING_PRE_PROXY] && *data->set.str[STRING_PRE_PROXY]) {
+ conn->bits.proxy = TRUE;
+ conn->bits.socksproxy = TRUE;
+ }
+
+ conn->bits.proxy_user_passwd =
+ (data->state.aptr.proxyuser) ? TRUE : FALSE;
+ conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy;
+#endif /* CURL_DISABLE_PROXY */
+
+#ifndef CURL_DISABLE_FTP
+ conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
+ conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
+#endif
+ conn->ssl_config.verifystatus = data->set.ssl.primary.verifystatus;
+ conn->ssl_config.verifypeer = data->set.ssl.primary.verifypeer;
+ conn->ssl_config.verifyhost = data->set.ssl.primary.verifyhost;
+ conn->ssl_config.ssl_options = data->set.ssl.primary.ssl_options;
+#ifndef CURL_DISABLE_PROXY
+ conn->proxy_ssl_config.verifystatus =
+ data->set.proxy_ssl.primary.verifystatus;
+ conn->proxy_ssl_config.verifypeer = data->set.proxy_ssl.primary.verifypeer;
+ conn->proxy_ssl_config.verifyhost = data->set.proxy_ssl.primary.verifyhost;
+ conn->proxy_ssl_config.ssl_options = data->set.proxy_ssl.primary.ssl_options;
+#endif
+ conn->ip_version = data->set.ipver;
+ conn->connect_only = data->set.connect_only;
+ conn->transport = TRNSPRT_TCP; /* most of them are TCP streams */
+
+#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \
+ defined(NTLM_WB_ENABLED)
+ conn->ntlm.ntlm_auth_hlpr_socket = CURL_SOCKET_BAD;
+ conn->proxyntlm.ntlm_auth_hlpr_socket = CURL_SOCKET_BAD;
+#endif
+
+ /* Initialize the easy handle list */
+ Curl_llist_init(&conn->easyq, NULL);
+
+#ifdef HAVE_GSSAPI
+ conn->data_prot = PROT_CLEAR;
+#endif
+
+ /* Store the local bind parameters that will be used for this connection */
+ if(data->set.str[STRING_DEVICE]) {
+ conn->localdev = strdup(data->set.str[STRING_DEVICE]);
+ if(!conn->localdev)
+ goto error;
+ }
+ conn->localportrange = data->set.localportrange;
+ conn->localport = data->set.localport;
+
+ /* the close socket stuff needs to be copied to the connection struct as
+ it may live on without (this specific) Curl_easy */
+ conn->fclosesocket = data->set.fclosesocket;
+ conn->closesocket_client = data->set.closesocket_client;
+ conn->lastused = Curl_now(); /* used now */
+
+ return conn;
+ error:
+
+ Curl_llist_destroy(&conn->easyq, NULL);
+ free(conn->localdev);
+ free(conn);
+ return NULL;
+}
+
+/* returns the handler if the given scheme is built-in */
+const struct Curl_handler *Curl_builtin_scheme(const char *scheme,
+ size_t schemelen)
+{
+ const struct Curl_handler * const *pp;
+ const struct Curl_handler *p;
+ /* Scan protocol handler table and match against 'scheme'. The handler may
+ be changed later when the protocol specific setup function is called. */
+ if(schemelen == CURL_ZERO_TERMINATED)
+ schemelen = strlen(scheme);
+ for(pp = protocols; (p = *pp) != NULL; pp++)
+ if(strncasecompare(p->scheme, scheme, schemelen) && !p->scheme[schemelen])
+ /* Protocol found in table. */
+ return p;
+ return NULL; /* not found */
+}
+
+
+static CURLcode findprotocol(struct Curl_easy *data,
+ struct connectdata *conn,
+ const char *protostr)
+{
+ const struct Curl_handler *p = Curl_builtin_scheme(protostr,
+ CURL_ZERO_TERMINATED);
+
+ if(p && /* Protocol found in table. Check if allowed */
+ (data->set.allowed_protocols & p->protocol)) {
+
+ /* it is allowed for "normal" request, now do an extra check if this is
+ the result of a redirect */
+ if(data->state.this_is_a_follow &&
+ !(data->set.redir_protocols & p->protocol))
+ /* nope, get out */
+ ;
+ else {
+ /* Perform setup complement if some. */
+ conn->handler = conn->given = p;
+
+ /* 'port' and 'remote_port' are set in setup_connection_internals() */
+ return CURLE_OK;
+ }
+ }
+
+ /* The protocol was not found in the table, but we don't have to assign it
+ to anything since it is already assigned to a dummy-struct in the
+ create_conn() function when the connectdata struct is allocated. */
+ failf(data, "Protocol \"%s\" not supported or disabled in " LIBCURL_NAME,
+ protostr);
+
+ return CURLE_UNSUPPORTED_PROTOCOL;
+}
+
+
+CURLcode Curl_uc_to_curlcode(CURLUcode uc)
+{
+ switch(uc) {
+ default:
+ return CURLE_URL_MALFORMAT;
+ case CURLUE_UNSUPPORTED_SCHEME:
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ case CURLUE_OUT_OF_MEMORY:
+ return CURLE_OUT_OF_MEMORY;
+ case CURLUE_USER_NOT_ALLOWED:
+ return CURLE_LOGIN_DENIED;
+ }
+}
+
+#ifdef ENABLE_IPV6
+/*
+ * If the URL was set with an IPv6 numerical address with a zone id part, set
+ * the scope_id based on that!
+ */
+
+static void zonefrom_url(CURLU *uh, struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ char *zoneid;
+ CURLUcode uc = curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0);
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+ (void)data;
+#endif
+
+ if(!uc && zoneid) {
+ char *endp;
+ unsigned long scope = strtoul(zoneid, &endp, 10);
+ if(!*endp && (scope < UINT_MAX))
+ /* A plain number, use it directly as a scope id. */
+ conn->scope_id = (unsigned int)scope;
+#if defined(HAVE_IF_NAMETOINDEX)
+ else {
+#elif defined(WIN32)
+ else if(Curl_if_nametoindex) {
+#endif
+
+#if defined(HAVE_IF_NAMETOINDEX) || defined(WIN32)
+ /* Zone identifier is not numeric */
+ unsigned int scopeidx = 0;
+#if defined(WIN32)
+ scopeidx = Curl_if_nametoindex(zoneid);
+#else
+ scopeidx = if_nametoindex(zoneid);
+#endif
+ if(!scopeidx) {
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ char buffer[STRERROR_LEN];
+ infof(data, "Invalid zoneid: %s; %s", zoneid,
+ Curl_strerror(errno, buffer, sizeof(buffer)));
+#endif
+ }
+ else
+ conn->scope_id = scopeidx;
+ }
+#endif /* HAVE_IF_NAMETOINDEX || WIN32 */
+
+ free(zoneid);
+ }
+}
+#else
+#define zonefrom_url(a,b,c) Curl_nop_stmt
+#endif
+
+/*
+ * Parse URL and fill in the relevant members of the connection struct.
+ */
+static CURLcode parseurlandfillconn(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result;
+ CURLU *uh;
+ CURLUcode uc;
+ char *hostname;
+ bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow);
+
+ up_free(data); /* cleanup previous leftovers first */
+
+ /* parse the URL */
+ if(use_set_uh) {
+ uh = data->state.uh = curl_url_dup(data->set.uh);
+ }
+ else {
+ uh = data->state.uh = curl_url();
+ }
+
+ if(!uh)
+ return CURLE_OUT_OF_MEMORY;
+
+ if(data->set.str[STRING_DEFAULT_PROTOCOL] &&
+ !Curl_is_absolute_url(data->state.url, NULL, 0, TRUE)) {
+ char *url = aprintf("%s://%s", data->set.str[STRING_DEFAULT_PROTOCOL],
+ data->state.url);
+ if(!url)
+ return CURLE_OUT_OF_MEMORY;
+ if(data->state.url_alloc)
+ free(data->state.url);
+ data->state.url = url;
+ data->state.url_alloc = TRUE;
+ }
+
+ if(!use_set_uh) {
+ char *newurl;
+ uc = curl_url_set(uh, CURLUPART_URL, data->state.url,
+ CURLU_GUESS_SCHEME |
+ CURLU_NON_SUPPORT_SCHEME |
+ (data->set.disallow_username_in_url ?
+ CURLU_DISALLOW_USER : 0) |
+ (data->set.path_as_is ? CURLU_PATH_AS_IS : 0));
+ if(uc) {
+ DEBUGF(infof(data, "curl_url_set rejected %s: %s", data->state.url,
+ curl_url_strerror(uc)));
+ return Curl_uc_to_curlcode(uc);
+ }
+
+ /* after it was parsed, get the generated normalized version */
+ uc = curl_url_get(uh, CURLUPART_URL, &newurl, 0);
+ if(uc)
+ return Curl_uc_to_curlcode(uc);
+ if(data->state.url_alloc)
+ free(data->state.url);
+ data->state.url = newurl;
+ data->state.url_alloc = TRUE;
+ }
+
+ uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0);
+ if(uc)
+ return Curl_uc_to_curlcode(uc);
+
+ uc = curl_url_get(uh, CURLUPART_HOST, &data->state.up.hostname, 0);
+ if(uc) {
+ if(!strcasecompare("file", data->state.up.scheme))
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else if(strlen(data->state.up.hostname) > MAX_URL_LEN) {
+ failf(data, "Too long host name (maximum is %d)", MAX_URL_LEN);
+ return CURLE_URL_MALFORMAT;
+ }
+ hostname = data->state.up.hostname;
+
+ if(hostname && hostname[0] == '[') {
+ /* This looks like an IPv6 address literal. See if there is an address
+ scope. */
+ size_t hlen;
+ conn->bits.ipv6_ip = TRUE;
+ /* cut off the brackets! */
+ hostname++;
+ hlen = strlen(hostname);
+ hostname[hlen - 1] = 0;
+
+ zonefrom_url(uh, data, conn);
+ }
+
+ /* make sure the connect struct gets its own copy of the host name */
+ conn->host.rawalloc = strdup(hostname ? hostname : "");
+ if(!conn->host.rawalloc)
+ return CURLE_OUT_OF_MEMORY;
+ conn->host.name = conn->host.rawalloc;
+
+ /*************************************************************
+ * IDN-convert the hostnames
+ *************************************************************/
+ result = Curl_idnconvert_hostname(&conn->host);
+ if(result)
+ return result;
+ if(conn->bits.conn_to_host) {
+ result = Curl_idnconvert_hostname(&conn->conn_to_host);
+ if(result)
+ return result;
+ }
+
+#ifndef CURL_DISABLE_HSTS
+ /* HSTS upgrade */
+ if(data->hsts && strcasecompare("http", data->state.up.scheme)) {
+ /* This MUST use the IDN decoded name */
+ if(Curl_hsts(data->hsts, conn->host.name, TRUE)) {
+ char *url;
+ Curl_safefree(data->state.up.scheme);
+ uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0);
+ if(uc)
+ return Curl_uc_to_curlcode(uc);
+ if(data->state.url_alloc)
+ Curl_safefree(data->state.url);
+ /* after update, get the updated version */
+ uc = curl_url_get(uh, CURLUPART_URL, &url, 0);
+ if(uc)
+ return Curl_uc_to_curlcode(uc);
+ uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0);
+ if(uc) {
+ free(url);
+ return Curl_uc_to_curlcode(uc);
+ }
+ data->state.url = url;
+ data->state.url_alloc = TRUE;
+ infof(data, "Switched from HTTP to HTTPS due to HSTS => %s",
+ data->state.url);
+ }
+ }
+#endif
+
+ result = findprotocol(data, conn, data->state.up.scheme);
+ if(result)
+ return result;
+
+ /*
+ * User name and password set with their own options override the
+ * credentials possibly set in the URL.
+ */
+ if(!data->state.aptr.passwd) {
+ uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0);
+ if(!uc) {
+ char *decoded;
+ result = Curl_urldecode(data->state.up.password, 0, &decoded, NULL,
+ conn->handler->flags&PROTOPT_USERPWDCTRL ?
+ REJECT_ZERO : REJECT_CTRL);
+ if(result)
+ return result;
+ conn->passwd = decoded;
+ result = Curl_setstropt(&data->state.aptr.passwd, decoded);
+ if(result)
+ return result;
+ }
+ else if(uc != CURLUE_NO_PASSWORD)
+ return Curl_uc_to_curlcode(uc);
+ }
+
+ if(!data->set.str[STRING_USERNAME]) {
+ /* we don't use the URL API's URL decoder option here since it rejects
+ control codes and we want to allow them for some schemes in the user
+ and password fields */
+ uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, 0);
+ if(!uc) {
+ char *decoded;
+ result = Curl_urldecode(data->state.up.user, 0, &decoded, NULL,
+ conn->handler->flags&PROTOPT_USERPWDCTRL ?
+ REJECT_ZERO : REJECT_CTRL);
+ if(result)
+ return result;
+ conn->user = decoded;
+ result = Curl_setstropt(&data->state.aptr.user, decoded);
+ }
+ else if(uc != CURLUE_NO_USER)
+ return Curl_uc_to_curlcode(uc);
+ else if(data->state.aptr.passwd) {
+ /* no user was set but a password, set a blank user */
+ result = Curl_setstropt(&data->state.aptr.user, "");
+ }
+ if(result)
+ return result;
+ }
+
+ uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options,
+ CURLU_URLDECODE);
+ if(!uc) {
+ conn->options = strdup(data->state.up.options);
+ if(!conn->options)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else if(uc != CURLUE_NO_OPTIONS)
+ return Curl_uc_to_curlcode(uc);
+
+ uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path,
+ CURLU_URLENCODE);
+ if(uc)
+ return Curl_uc_to_curlcode(uc);
+
+ uc = curl_url_get(uh, CURLUPART_PORT, &data->state.up.port,
+ CURLU_DEFAULT_PORT);
+ if(uc) {
+ if(!strcasecompare("file", data->state.up.scheme))
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else {
+ unsigned long port = strtoul(data->state.up.port, NULL, 10);
+ conn->port = conn->remote_port =
+ (data->set.use_port && data->state.allow_port) ?
+ data->set.use_port : curlx_ultous(port);
+ }
+
+ (void)curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0);
+
+#ifdef ENABLE_IPV6
+ if(data->set.scope_id)
+ /* Override any scope that was set above. */
+ conn->scope_id = data->set.scope_id;
+#endif
+
+ return CURLE_OK;
+}
+
+
+/*
+ * If we're doing a resumed transfer, we need to setup our stuff
+ * properly.
+ */
+static CURLcode setup_range(struct Curl_easy *data)
+{
+ struct UrlState *s = &data->state;
+ s->resume_from = data->set.set_resume_from;
+ if(s->resume_from || data->set.str[STRING_SET_RANGE]) {
+ if(s->rangestringalloc)
+ free(s->range);
+
+ if(s->resume_from)
+ s->range = aprintf("%" CURL_FORMAT_CURL_OFF_T "-", s->resume_from);
+ else
+ s->range = strdup(data->set.str[STRING_SET_RANGE]);
+
+ s->rangestringalloc = (s->range) ? TRUE : FALSE;
+
+ if(!s->range)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* tell ourselves to fetch this range */
+ s->use_range = TRUE; /* enable range download */
+ }
+ else
+ s->use_range = FALSE; /* disable range download */
+
+ return CURLE_OK;
+}
+
+
+/*
+ * setup_connection_internals() -
+ *
+ * Setup connection internals specific to the requested protocol in the
+ * Curl_easy. This is inited and setup before the connection is made but
+ * is about the particular protocol that is to be used.
+ *
+ * This MUST get called after proxy magic has been figured out.
+ */
+static CURLcode setup_connection_internals(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ const struct Curl_handler *p;
+ CURLcode result;
+
+ /* Perform setup complement if some. */
+ p = conn->handler;
+
+ if(p->setup_connection) {
+ result = (*p->setup_connection)(data, conn);
+
+ if(result)
+ return result;
+
+ p = conn->handler; /* May have changed. */
+ }
+
+ if(conn->port < 0)
+ /* we check for -1 here since if proxy was detected already, this
+ was very likely already set to the proxy port */
+ conn->port = p->defport;
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_free_request_state() should free temp data that was allocated in the
+ * Curl_easy for this single request.
+ */
+
+void Curl_free_request_state(struct Curl_easy *data)
+{
+ Curl_safefree(data->req.p.http);
+ Curl_safefree(data->req.newurl);
+
+#ifndef CURL_DISABLE_DOH
+ if(data->req.doh) {
+ Curl_close(&data->req.doh->probe[0].easy);
+ Curl_close(&data->req.doh->probe[1].easy);
+ }
+#endif
+}
+
+
+#ifndef CURL_DISABLE_PROXY
+
+#ifndef CURL_DISABLE_HTTP
+/****************************************************************
+* Detect what (if any) proxy to use. Remember that this selects a host
+* name and is not limited to HTTP proxies only.
+* The returned pointer must be freed by the caller (unless NULL)
+****************************************************************/
+static char *detect_proxy(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ char *proxy = NULL;
+
+ /* If proxy was not specified, we check for default proxy environment
+ * variables, to enable i.e Lynx compliance:
+ *
+ * http_proxy=http://some.server.dom:port/
+ * https_proxy=http://some.server.dom:port/
+ * ftp_proxy=http://some.server.dom:port/
+ * no_proxy=domain1.dom,host.domain2.dom
+ * (a comma-separated list of hosts which should
+ * not be proxied, or an asterisk to override
+ * all proxy variables)
+ * all_proxy=http://some.server.dom:port/
+ * (seems to exist for the CERN www lib. Probably
+ * the first to check for.)
+ *
+ * For compatibility, the all-uppercase versions of these variables are
+ * checked if the lowercase versions don't exist.
+ */
+ char proxy_env[128];
+ const char *protop = conn->handler->scheme;
+ char *envp = proxy_env;
+ char *prox;
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+ (void)data;
+#endif
+
+ /* Now, build <protocol>_proxy and check for such a one to use */
+ while(*protop)
+ *envp++ = Curl_raw_tolower(*protop++);
+
+ /* append _proxy */
+ strcpy(envp, "_proxy");
+
+ /* read the protocol proxy: */
+ prox = curl_getenv(proxy_env);
+
+ /*
+ * We don't try the uppercase version of HTTP_PROXY because of
+ * security reasons:
+ *
+ * When curl is used in a webserver application
+ * environment (cgi or php), this environment variable can
+ * be controlled by the web server user by setting the
+ * http header 'Proxy:' to some value.
+ *
+ * This can cause 'internal' http/ftp requests to be
+ * arbitrarily redirected by any external attacker.
+ */
+ if(!prox && !strcasecompare("http_proxy", proxy_env)) {
+ /* There was no lowercase variable, try the uppercase version: */
+ Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env));
+ prox = curl_getenv(proxy_env);
+ }
+
+ envp = proxy_env;
+ if(prox) {
+ proxy = prox; /* use this */
+ }
+ else {
+ envp = (char *)"all_proxy";
+ proxy = curl_getenv(envp); /* default proxy to use */
+ if(!proxy) {
+ envp = (char *)"ALL_PROXY";
+ proxy = curl_getenv(envp);
+ }
+ }
+ if(proxy)
+ infof(data, "Uses proxy env variable %s == '%s'", envp, proxy);
+
+ return proxy;
+}
+#endif /* CURL_DISABLE_HTTP */
+
+/*
+ * If this is supposed to use a proxy, we need to figure out the proxy
+ * host name, so that we can re-use an existing connection
+ * that may exist registered to the same proxy host.
+ */
+static CURLcode parse_proxy(struct Curl_easy *data,
+ struct connectdata *conn, char *proxy,
+ curl_proxytype proxytype)
+{
+ char *portptr = NULL;
+ int port = -1;
+ char *proxyuser = NULL;
+ char *proxypasswd = NULL;
+ char *host = NULL;
+ bool sockstype;
+ CURLUcode uc;
+ struct proxy_info *proxyinfo;
+ CURLU *uhp = curl_url();
+ CURLcode result = CURLE_OK;
+ char *scheme = NULL;
+#ifdef USE_UNIX_SOCKETS
+ char *path = NULL;
+ bool is_unix_proxy = FALSE;
+#endif
+
+
+ if(!uhp) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ /* When parsing the proxy, allowing non-supported schemes since we have
+ these made up ones for proxies. Guess scheme for URLs without it. */
+ uc = curl_url_set(uhp, CURLUPART_URL, proxy,
+ CURLU_NON_SUPPORT_SCHEME|CURLU_GUESS_SCHEME);
+ if(!uc) {
+ /* parsed okay as a URL */
+ uc = curl_url_get(uhp, CURLUPART_SCHEME, &scheme, 0);
+ if(uc) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ if(strcasecompare("https", scheme))
+ proxytype = CURLPROXY_HTTPS;
+ else if(strcasecompare("socks5h", scheme))
+ proxytype = CURLPROXY_SOCKS5_HOSTNAME;
+ else if(strcasecompare("socks5", scheme))
+ proxytype = CURLPROXY_SOCKS5;
+ else if(strcasecompare("socks4a", scheme))
+ proxytype = CURLPROXY_SOCKS4A;
+ else if(strcasecompare("socks4", scheme) ||
+ strcasecompare("socks", scheme))
+ proxytype = CURLPROXY_SOCKS4;
+ else if(strcasecompare("http", scheme))
+ ; /* leave it as HTTP or HTTP/1.0 */
+ else {
+ /* Any other xxx:// reject! */
+ failf(data, "Unsupported proxy scheme for \'%s\'", proxy);
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
+ }
+ }
+ else {
+ failf(data, "Unsupported proxy syntax in \'%s\'", proxy);
+ result = CURLE_COULDNT_RESOLVE_PROXY;
+ goto error;
+ }
+
+#ifdef USE_SSL
+ if(!Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY))
+#endif
+ if(proxytype == CURLPROXY_HTTPS) {
+ failf(data, "Unsupported proxy \'%s\', libcurl is built without the "
+ "HTTPS-proxy support.", proxy);
+ result = CURLE_NOT_BUILT_IN;
+ goto error;
+ }
+
+ sockstype =
+ proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
+ proxytype == CURLPROXY_SOCKS5 ||
+ proxytype == CURLPROXY_SOCKS4A ||
+ proxytype == CURLPROXY_SOCKS4;
+
+ proxyinfo = sockstype ? &conn->socks_proxy : &conn->http_proxy;
+ proxyinfo->proxytype = (unsigned char)proxytype;
+
+ /* Is there a username and password given in this proxy url? */
+ uc = curl_url_get(uhp, CURLUPART_USER, &proxyuser, CURLU_URLDECODE);
+ if(uc && (uc != CURLUE_NO_USER))
+ goto error;
+ uc = curl_url_get(uhp, CURLUPART_PASSWORD, &proxypasswd, CURLU_URLDECODE);
+ if(uc && (uc != CURLUE_NO_PASSWORD))
+ goto error;
+
+ if(proxyuser || proxypasswd) {
+ Curl_safefree(proxyinfo->user);
+ proxyinfo->user = proxyuser;
+ result = Curl_setstropt(&data->state.aptr.proxyuser, proxyuser);
+ proxyuser = NULL;
+ if(result)
+ goto error;
+ Curl_safefree(proxyinfo->passwd);
+ if(!proxypasswd) {
+ proxypasswd = strdup("");
+ if(!proxypasswd) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ }
+ proxyinfo->passwd = proxypasswd;
+ result = Curl_setstropt(&data->state.aptr.proxypasswd, proxypasswd);
+ proxypasswd = NULL;
+ if(result)
+ goto error;
+ conn->bits.proxy_user_passwd = TRUE; /* enable it */
+ }
+
+ (void)curl_url_get(uhp, CURLUPART_PORT, &portptr, 0);
+
+ if(portptr) {
+ port = (int)strtol(portptr, NULL, 10);
+ free(portptr);
+ }
+ else {
+ if(data->set.proxyport)
+ /* None given in the proxy string, then get the default one if it is
+ given */
+ port = (int)data->set.proxyport;
+ else {
+ if(proxytype == CURLPROXY_HTTPS)
+ port = CURL_DEFAULT_HTTPS_PROXY_PORT;
+ else
+ port = CURL_DEFAULT_PROXY_PORT;
+ }
+ }
+ if(port >= 0) {
+ proxyinfo->port = port;
+ if(conn->port < 0 || sockstype || !conn->socks_proxy.host.rawalloc)
+ conn->port = port;
+ }
+
+ /* now, clone the proxy host name */
+ uc = curl_url_get(uhp, CURLUPART_HOST, &host, CURLU_URLDECODE);
+ if(uc) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+#ifdef USE_UNIX_SOCKETS
+ if(sockstype && strcasecompare(UNIX_SOCKET_PREFIX, host)) {
+ uc = curl_url_get(uhp, CURLUPART_PATH, &path, CURLU_URLDECODE);
+ if(uc) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ /* path will be "/", if no path was was found */
+ if(strcmp("/", path)) {
+ is_unix_proxy = TRUE;
+ free(host);
+ host = aprintf(UNIX_SOCKET_PREFIX"%s", path);
+ if(!host) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ Curl_safefree(proxyinfo->host.rawalloc);
+ proxyinfo->host.rawalloc = host;
+ proxyinfo->host.name = host;
+ host = NULL;
+ }
+ }
+
+ if(!is_unix_proxy) {
+#endif
+ Curl_safefree(proxyinfo->host.rawalloc);
+ proxyinfo->host.rawalloc = host;
+ if(host[0] == '[') {
+ /* this is a numerical IPv6, strip off the brackets */
+ size_t len = strlen(host);
+ host[len-1] = 0; /* clear the trailing bracket */
+ host++;
+ zonefrom_url(uhp, data, conn);
+ }
+ proxyinfo->host.name = host;
+ host = NULL;
+#ifdef USE_UNIX_SOCKETS
+ }
+#endif
+
+ error:
+ free(proxyuser);
+ free(proxypasswd);
+ free(host);
+ free(scheme);
+#ifdef USE_UNIX_SOCKETS
+ free(path);
+#endif
+ curl_url_cleanup(uhp);
+ return result;
+}
+
+/*
+ * Extract the user and password from the authentication string
+ */
+static CURLcode parse_proxy_auth(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ const char *proxyuser = data->state.aptr.proxyuser ?
+ data->state.aptr.proxyuser : "";
+ const char *proxypasswd = data->state.aptr.proxypasswd ?
+ data->state.aptr.proxypasswd : "";
+ CURLcode result = CURLE_OK;
+
+ if(proxyuser) {
+ result = Curl_urldecode(proxyuser, 0, &conn->http_proxy.user, NULL,
+ REJECT_ZERO);
+ if(!result)
+ result = Curl_setstropt(&data->state.aptr.proxyuser,
+ conn->http_proxy.user);
+ }
+ if(!result && proxypasswd) {
+ result = Curl_urldecode(proxypasswd, 0, &conn->http_proxy.passwd,
+ NULL, REJECT_ZERO);
+ if(!result)
+ result = Curl_setstropt(&data->state.aptr.proxypasswd,
+ conn->http_proxy.passwd);
+ }
+ return result;
+}
+
+/* create_conn helper to parse and init proxy values. to be called after unix
+ socket init but before any proxy vars are evaluated. */
+static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ char *proxy = NULL;
+ char *socksproxy = NULL;
+ char *no_proxy = NULL;
+ CURLcode result = CURLE_OK;
+
+ /*************************************************************
+ * Extract the user and password from the authentication string
+ *************************************************************/
+ if(conn->bits.proxy_user_passwd) {
+ result = parse_proxy_auth(data, conn);
+ if(result)
+ goto out;
+ }
+
+ /*************************************************************
+ * Detect what (if any) proxy to use
+ *************************************************************/
+ if(data->set.str[STRING_PROXY]) {
+ proxy = strdup(data->set.str[STRING_PROXY]);
+ /* if global proxy is set, this is it */
+ if(!proxy) {
+ failf(data, "memory shortage");
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ }
+
+ if(data->set.str[STRING_PRE_PROXY]) {
+ socksproxy = strdup(data->set.str[STRING_PRE_PROXY]);
+ /* if global socks proxy is set, this is it */
+ if(!socksproxy) {
+ failf(data, "memory shortage");
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ }
+
+ if(!data->set.str[STRING_NOPROXY]) {
+ const char *p = "no_proxy";
+ no_proxy = curl_getenv(p);
+ if(!no_proxy) {
+ p = "NO_PROXY";
+ no_proxy = curl_getenv(p);
+ }
+ if(no_proxy) {
+ infof(data, "Uses proxy env variable %s == '%s'", p, no_proxy);
+ }
+ }
+
+ if(Curl_check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ?
+ data->set.str[STRING_NOPROXY] : no_proxy)) {
+ Curl_safefree(proxy);
+ Curl_safefree(socksproxy);
+ }
+#ifndef CURL_DISABLE_HTTP
+ else if(!proxy && !socksproxy)
+ /* if the host is not in the noproxy list, detect proxy. */
+ proxy = detect_proxy(data, conn);
+#endif /* CURL_DISABLE_HTTP */
+
+ Curl_safefree(no_proxy);
+
+#ifdef USE_UNIX_SOCKETS
+ /* For the time being do not mix proxy and unix domain sockets. See #1274 */
+ if(proxy && conn->unix_domain_socket) {
+ free(proxy);
+ proxy = NULL;
+ }
+#endif
+
+ if(proxy && (!*proxy || (conn->handler->flags & PROTOPT_NONETWORK))) {
+ free(proxy); /* Don't bother with an empty proxy string or if the
+ protocol doesn't work with network */
+ proxy = NULL;
+ }
+ if(socksproxy && (!*socksproxy ||
+ (conn->handler->flags & PROTOPT_NONETWORK))) {
+ free(socksproxy); /* Don't bother with an empty socks proxy string or if
+ the protocol doesn't work with network */
+ socksproxy = NULL;
+ }
+
+ /***********************************************************************
+ * If this is supposed to use a proxy, we need to figure out the proxy host
+ * name, proxy type and port number, so that we can re-use an existing
+ * connection that may exist registered to the same proxy host.
+ ***********************************************************************/
+ if(proxy || socksproxy) {
+ curl_proxytype ptype = (curl_proxytype)conn->http_proxy.proxytype;
+ if(proxy) {
+ result = parse_proxy(data, conn, proxy, ptype);
+ Curl_safefree(proxy); /* parse_proxy copies the proxy string */
+ if(result)
+ goto out;
+ }
+
+ if(socksproxy) {
+ result = parse_proxy(data, conn, socksproxy, ptype);
+ /* parse_proxy copies the socks proxy string */
+ Curl_safefree(socksproxy);
+ if(result)
+ goto out;
+ }
+
+ if(conn->http_proxy.host.rawalloc) {
+#ifdef CURL_DISABLE_HTTP
+ /* asking for an HTTP proxy is a bit funny when HTTP is disabled... */
+ result = CURLE_UNSUPPORTED_PROTOCOL;
+ goto out;
+#else
+ /* force this connection's protocol to become HTTP if compatible */
+ if(!(conn->handler->protocol & PROTO_FAMILY_HTTP)) {
+ if((conn->handler->flags & PROTOPT_PROXY_AS_HTTP) &&
+ !conn->bits.tunnel_proxy)
+ conn->handler = &Curl_handler_http;
+ else
+ /* if not converting to HTTP over the proxy, enforce tunneling */
+ conn->bits.tunnel_proxy = TRUE;
+ }
+ conn->bits.httpproxy = TRUE;
+#endif
+ }
+ else {
+ conn->bits.httpproxy = FALSE; /* not an HTTP proxy */
+ conn->bits.tunnel_proxy = FALSE; /* no tunneling if not HTTP */
+ }
+
+ if(conn->socks_proxy.host.rawalloc) {
+ if(!conn->http_proxy.host.rawalloc) {
+ /* once a socks proxy */
+ if(!conn->socks_proxy.user) {
+ conn->socks_proxy.user = conn->http_proxy.user;
+ conn->http_proxy.user = NULL;
+ Curl_safefree(conn->socks_proxy.passwd);
+ conn->socks_proxy.passwd = conn->http_proxy.passwd;
+ conn->http_proxy.passwd = NULL;
+ }
+ }
+ conn->bits.socksproxy = TRUE;
+ }
+ else
+ conn->bits.socksproxy = FALSE; /* not a socks proxy */
+ }
+ else {
+ conn->bits.socksproxy = FALSE;
+ conn->bits.httpproxy = FALSE;
+ }
+ conn->bits.proxy = conn->bits.httpproxy || conn->bits.socksproxy;
+
+ if(!conn->bits.proxy) {
+ /* we aren't using the proxy after all... */
+ conn->bits.proxy = FALSE;
+ conn->bits.httpproxy = FALSE;
+ conn->bits.socksproxy = FALSE;
+ conn->bits.proxy_user_passwd = FALSE;
+ conn->bits.tunnel_proxy = FALSE;
+ /* CURLPROXY_HTTPS does not have its own flag in conn->bits, yet we need
+ to signal that CURLPROXY_HTTPS is not used for this connection */
+ conn->http_proxy.proxytype = CURLPROXY_HTTP;
+ }
+
+out:
+
+ free(socksproxy);
+ free(proxy);
+ return result;
+}
+#endif /* CURL_DISABLE_PROXY */
+
+/*
+ * Curl_parse_login_details()
+ *
+ * This is used to parse a login string for user name, password and options in
+ * the following formats:
+ *
+ * user
+ * user:password
+ * user:password;options
+ * user;options
+ * user;options:password
+ * :password
+ * :password;options
+ * ;options
+ * ;options:password
+ *
+ * Parameters:
+ *
+ * login [in] - The login string.
+ * len [in] - The length of the login string.
+ * userp [in/out] - The address where a pointer to newly allocated memory
+ * holding the user will be stored upon completion.
+ * passwdp [in/out] - The address where a pointer to newly allocated memory
+ * holding the password will be stored upon completion.
+ * optionsp [in/out] - The address where a pointer to newly allocated memory
+ * holding the options will be stored upon completion.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_parse_login_details(const char *login, const size_t len,
+ char **userp, char **passwdp,
+ char **optionsp)
+{
+ CURLcode result = CURLE_OK;
+ char *ubuf = NULL;
+ char *pbuf = NULL;
+ char *obuf = NULL;
+ const char *psep = NULL;
+ const char *osep = NULL;
+ size_t ulen;
+ size_t plen;
+ size_t olen;
+
+ /* the input length check is because this is called directly from setopt
+ and isn't going through the regular string length check */
+ size_t llen = strlen(login);
+ if(llen > CURL_MAX_INPUT_LENGTH)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ /* Attempt to find the password separator */
+ if(passwdp) {
+ psep = strchr(login, ':');
+
+ /* Within the constraint of the login string */
+ if(psep >= login + len)
+ psep = NULL;
+ }
+
+ /* Attempt to find the options separator */
+ if(optionsp) {
+ osep = strchr(login, ';');
+
+ /* Within the constraint of the login string */
+ if(osep >= login + len)
+ osep = NULL;
+ }
+
+ /* Calculate the portion lengths */
+ ulen = (psep ?
+ (size_t)(osep && psep > osep ? osep - login : psep - login) :
+ (osep ? (size_t)(osep - login) : len));
+ plen = (psep ?
+ (osep && osep > psep ? (size_t)(osep - psep) :
+ (size_t)(login + len - psep)) - 1 : 0);
+ olen = (osep ?
+ (psep && psep > osep ? (size_t)(psep - osep) :
+ (size_t)(login + len - osep)) - 1 : 0);
+
+ /* Allocate the user portion buffer, which can be zero length */
+ if(userp) {
+ ubuf = malloc(ulen + 1);
+ if(!ubuf)
+ result = CURLE_OUT_OF_MEMORY;
+ }
+
+ /* Allocate the password portion buffer */
+ if(!result && passwdp && psep) {
+ pbuf = malloc(plen + 1);
+ if(!pbuf) {
+ free(ubuf);
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ }
+
+ /* Allocate the options portion buffer */
+ if(!result && optionsp && olen) {
+ obuf = malloc(olen + 1);
+ if(!obuf) {
+ free(pbuf);
+ free(ubuf);
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ }
+
+ if(!result) {
+ /* Store the user portion if necessary */
+ if(ubuf) {
+ memcpy(ubuf, login, ulen);
+ ubuf[ulen] = '\0';
+ Curl_safefree(*userp);
+ *userp = ubuf;
+ }
+
+ /* Store the password portion if necessary */
+ if(pbuf) {
+ memcpy(pbuf, psep + 1, plen);
+ pbuf[plen] = '\0';
+ Curl_safefree(*passwdp);
+ *passwdp = pbuf;
+ }
+
+ /* Store the options portion if necessary */
+ if(obuf) {
+ memcpy(obuf, osep + 1, olen);
+ obuf[olen] = '\0';
+ Curl_safefree(*optionsp);
+ *optionsp = obuf;
+ }
+ }
+
+ return result;
+}
+
+/*************************************************************
+ * Figure out the remote port number and fix it in the URL
+ *
+ * No matter if we use a proxy or not, we have to figure out the remote
+ * port number of various reasons.
+ *
+ * The port number embedded in the URL is replaced, if necessary.
+ *************************************************************/
+static CURLcode parse_remote_port(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+
+ if(data->set.use_port && data->state.allow_port) {
+ /* if set, we use this instead of the port possibly given in the URL */
+ char portbuf[16];
+ CURLUcode uc;
+ conn->remote_port = data->set.use_port;
+ msnprintf(portbuf, sizeof(portbuf), "%d", conn->remote_port);
+ uc = curl_url_set(data->state.uh, CURLUPART_PORT, portbuf, 0);
+ if(uc)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Override the login details from the URL with that in the CURLOPT_USERPWD
+ * option or a .netrc file, if applicable.
+ */
+static CURLcode override_login(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLUcode uc;
+ char **userp = &conn->user;
+ char **passwdp = &conn->passwd;
+ char **optionsp = &conn->options;
+
+ if(data->set.str[STRING_OPTIONS]) {
+ free(*optionsp);
+ *optionsp = strdup(data->set.str[STRING_OPTIONS]);
+ if(!*optionsp)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+#ifndef CURL_DISABLE_NETRC
+ if(data->set.use_netrc == CURL_NETRC_REQUIRED) {
+ Curl_safefree(*userp);
+ Curl_safefree(*passwdp);
+ }
+ conn->bits.netrc = FALSE;
+ if(data->set.use_netrc && !data->set.str[STRING_USERNAME]) {
+ int ret;
+ bool url_provided = FALSE;
+
+ if(data->state.aptr.user) {
+ /* there was a user name in the URL. Use the URL decoded version */
+ userp = &data->state.aptr.user;
+ url_provided = TRUE;
+ }
+
+ ret = Curl_parsenetrc(conn->host.name,
+ userp, passwdp,
+ data->set.str[STRING_NETRC_FILE]);
+ if(ret > 0) {
+ infof(data, "Couldn't find host %s in the %s file; using defaults",
+ conn->host.name, data->set.str[STRING_NETRC_FILE]);
+ }
+ else if(ret < 0) {
+ failf(data, ".netrc parser error");
+ return CURLE_READ_ERROR;
+ }
+ else {
+ /* set bits.netrc TRUE to remember that we got the name from a .netrc
+ file, so that it is safe to use even if we followed a Location: to a
+ different host or similar. */
+ conn->bits.netrc = TRUE;
+ }
+ if(url_provided) {
+ Curl_safefree(conn->user);
+ conn->user = strdup(*userp);
+ if(!conn->user)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ /* no user was set but a password, set a blank user */
+ if(userp && !*userp && *passwdp) {
+ *userp = strdup("");
+ if(!*userp)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+#endif
+
+ /* for updated strings, we update them in the URL */
+ if(*userp) {
+ CURLcode result;
+ if(data->state.aptr.user != *userp) {
+ /* nothing to do then */
+ result = Curl_setstropt(&data->state.aptr.user, *userp);
+ if(result)
+ return result;
+ }
+ }
+ if(data->state.aptr.user) {
+ uc = curl_url_set(data->state.uh, CURLUPART_USER, data->state.aptr.user,
+ CURLU_URLENCODE);
+ if(uc)
+ return Curl_uc_to_curlcode(uc);
+ if(!*userp) {
+ *userp = strdup(data->state.aptr.user);
+ if(!*userp)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ if(*passwdp) {
+ CURLcode result = Curl_setstropt(&data->state.aptr.passwd, *passwdp);
+ if(result)
+ return result;
+ }
+ if(data->state.aptr.passwd) {
+ uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD,
+ data->state.aptr.passwd, CURLU_URLENCODE);
+ if(uc)
+ return Curl_uc_to_curlcode(uc);
+ if(!*passwdp) {
+ *passwdp = strdup(data->state.aptr.passwd);
+ if(!*passwdp)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Set the login details so they're available in the connection
+ */
+static CURLcode set_login(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ const char *setuser = CURL_DEFAULT_USER;
+ const char *setpasswd = CURL_DEFAULT_PASSWORD;
+
+ /* If our protocol needs a password and we have none, use the defaults */
+ if((conn->handler->flags & PROTOPT_NEEDSPWD) && !data->state.aptr.user)
+ ;
+ else {
+ setuser = "";
+ setpasswd = "";
+ }
+ /* Store the default user */
+ if(!conn->user) {
+ conn->user = strdup(setuser);
+ if(!conn->user)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* Store the default password */
+ if(!conn->passwd) {
+ conn->passwd = strdup(setpasswd);
+ if(!conn->passwd)
+ result = CURLE_OUT_OF_MEMORY;
+ }
+
+ return result;
+}
+
+/*
+ * Parses a "host:port" string to connect to.
+ * The hostname and the port may be empty; in this case, NULL is returned for
+ * the hostname and -1 for the port.
+ */
+static CURLcode parse_connect_to_host_port(struct Curl_easy *data,
+ const char *host,
+ char **hostname_result,
+ int *port_result)
+{
+ char *host_dup;
+ char *hostptr;
+ char *host_portno;
+ char *portptr;
+ int port = -1;
+ CURLcode result = CURLE_OK;
+
+#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+ (void) data;
+#endif
+
+ *hostname_result = NULL;
+ *port_result = -1;
+
+ if(!host || !*host)
+ return CURLE_OK;
+
+ host_dup = strdup(host);
+ if(!host_dup)
+ return CURLE_OUT_OF_MEMORY;
+
+ hostptr = host_dup;
+
+ /* start scanning for port number at this point */
+ portptr = hostptr;
+
+ /* detect and extract RFC6874-style IPv6-addresses */
+ if(*hostptr == '[') {
+#ifdef ENABLE_IPV6
+ char *ptr = ++hostptr; /* advance beyond the initial bracket */
+ while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '.')))
+ ptr++;
+ if(*ptr == '%') {
+ /* There might be a zone identifier */
+ if(strncmp("%25", ptr, 3))
+ infof(data, "Please URL encode %% as %%25, see RFC 6874.");
+ ptr++;
+ /* Allow unreserved characters as defined in RFC 3986 */
+ while(*ptr && (ISALPHA(*ptr) || ISXDIGIT(*ptr) || (*ptr == '-') ||
+ (*ptr == '.') || (*ptr == '_') || (*ptr == '~')))
+ ptr++;
+ }
+ if(*ptr == ']')
+ /* yeps, it ended nicely with a bracket as well */
+ *ptr++ = '\0';
+ else
+ infof(data, "Invalid IPv6 address format");
+ portptr = ptr;
+ /* Note that if this didn't end with a bracket, we still advanced the
+ * hostptr first, but I can't see anything wrong with that as no host
+ * name nor a numeric can legally start with a bracket.
+ */
+#else
+ failf(data, "Use of IPv6 in *_CONNECT_TO without IPv6 support built-in");
+ result = CURLE_NOT_BUILT_IN;
+ goto error;
+#endif
+ }
+
+ /* Get port number off server.com:1080 */
+ host_portno = strchr(portptr, ':');
+ if(host_portno) {
+ char *endp = NULL;
+ *host_portno = '\0'; /* cut off number from host name */
+ host_portno++;
+ if(*host_portno) {
+ long portparse = strtol(host_portno, &endp, 10);
+ if((endp && *endp) || (portparse < 0) || (portparse > 65535)) {
+ failf(data, "No valid port number in connect to host string (%s)",
+ host_portno);
+ result = CURLE_SETOPT_OPTION_SYNTAX;
+ goto error;
+ }
+ else
+ port = (int)portparse; /* we know it will fit */
+ }
+ }
+
+ /* now, clone the cleaned host name */
+ if(hostptr) {
+ *hostname_result = strdup(hostptr);
+ if(!*hostname_result) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ }
+
+ *port_result = port;
+
+ error:
+ free(host_dup);
+ return result;
+}
+
+/*
+ * Parses one "connect to" string in the form:
+ * "HOST:PORT:CONNECT-TO-HOST:CONNECT-TO-PORT".
+ */
+static CURLcode parse_connect_to_string(struct Curl_easy *data,
+ struct connectdata *conn,
+ const char *conn_to_host,
+ char **host_result,
+ int *port_result)
+{
+ CURLcode result = CURLE_OK;
+ const char *ptr = conn_to_host;
+ int host_match = FALSE;
+ int port_match = FALSE;
+
+ *host_result = NULL;
+ *port_result = -1;
+
+ if(*ptr == ':') {
+ /* an empty hostname always matches */
+ host_match = TRUE;
+ ptr++;
+ }
+ else {
+ /* check whether the URL's hostname matches */
+ size_t hostname_to_match_len;
+ char *hostname_to_match = aprintf("%s%s%s",
+ conn->bits.ipv6_ip ? "[" : "",
+ conn->host.name,
+ conn->bits.ipv6_ip ? "]" : "");
+ if(!hostname_to_match)
+ return CURLE_OUT_OF_MEMORY;
+ hostname_to_match_len = strlen(hostname_to_match);
+ host_match = strncasecompare(ptr, hostname_to_match,
+ hostname_to_match_len);
+ free(hostname_to_match);
+ ptr += hostname_to_match_len;
+
+ host_match = host_match && *ptr == ':';
+ ptr++;
+ }
+
+ if(host_match) {
+ if(*ptr == ':') {
+ /* an empty port always matches */
+ port_match = TRUE;
+ ptr++;
+ }
+ else {
+ /* check whether the URL's port matches */
+ char *ptr_next = strchr(ptr, ':');
+ if(ptr_next) {
+ char *endp = NULL;
+ long port_to_match = strtol(ptr, &endp, 10);
+ if((endp == ptr_next) && (port_to_match == conn->remote_port)) {
+ port_match = TRUE;
+ ptr = ptr_next + 1;
+ }
+ }
+ }
+ }
+
+ if(host_match && port_match) {
+ /* parse the hostname and port to connect to */
+ result = parse_connect_to_host_port(data, ptr, host_result, port_result);
+ }
+
+ return result;
+}
+
+/*
+ * Processes all strings in the "connect to" slist, and uses the "connect
+ * to host" and "connect to port" of the first string that matches.
+ */
+static CURLcode parse_connect_to_slist(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct curl_slist *conn_to_host)
+{
+ CURLcode result = CURLE_OK;
+ char *host = NULL;
+ int port = -1;
+
+ while(conn_to_host && !host && port == -1) {
+ result = parse_connect_to_string(data, conn, conn_to_host->data,
+ &host, &port);
+ if(result)
+ return result;
+
+ if(host && *host) {
+ conn->conn_to_host.rawalloc = host;
+ conn->conn_to_host.name = host;
+ conn->bits.conn_to_host = TRUE;
+
+ infof(data, "Connecting to hostname: %s", host);
+ }
+ else {
+ /* no "connect to host" */
+ conn->bits.conn_to_host = FALSE;
+ Curl_safefree(host);
+ }
+
+ if(port >= 0) {
+ conn->conn_to_port = port;
+ conn->bits.conn_to_port = TRUE;
+ infof(data, "Connecting to port: %d", port);
+ }
+ else {
+ /* no "connect to port" */
+ conn->bits.conn_to_port = FALSE;
+ port = -1;
+ }
+
+ conn_to_host = conn_to_host->next;
+ }
+
+#ifndef CURL_DISABLE_ALTSVC
+ if(data->asi && !host && (port == -1) &&
+ ((conn->handler->protocol == CURLPROTO_HTTPS) ||
+#ifdef CURLDEBUG
+ /* allow debug builds to circumvent the HTTPS restriction */
+ getenv("CURL_ALTSVC_HTTP")
+#else
+ 0
+#endif
+ )) {
+ /* no connect_to match, try alt-svc! */
+ enum alpnid srcalpnid;
+ bool hit;
+ struct altsvc *as;
+ const int allowed_versions = ( ALPN_h1
+#ifdef USE_HTTP2
+ | ALPN_h2
+#endif
+#ifdef ENABLE_QUIC
+ | ALPN_h3
+#endif
+ ) & data->asi->flags;
+
+ host = conn->host.rawalloc;
+#ifdef USE_HTTP2
+ /* with h2 support, check that first */
+ srcalpnid = ALPN_h2;
+ hit = Curl_altsvc_lookup(data->asi,
+ srcalpnid, host, conn->remote_port, /* from */
+ &as /* to */,
+ allowed_versions);
+ if(!hit)
+#endif
+ {
+ srcalpnid = ALPN_h1;
+ hit = Curl_altsvc_lookup(data->asi,
+ srcalpnid, host, conn->remote_port, /* from */
+ &as /* to */,
+ allowed_versions);
+ }
+ if(hit) {
+ char *hostd = strdup((char *)as->dst.host);
+ if(!hostd)
+ return CURLE_OUT_OF_MEMORY;
+ conn->conn_to_host.rawalloc = hostd;
+ conn->conn_to_host.name = hostd;
+ conn->bits.conn_to_host = TRUE;
+ conn->conn_to_port = as->dst.port;
+ conn->bits.conn_to_port = TRUE;
+ conn->bits.altused = TRUE;
+ infof(data, "Alt-svc connecting from [%s]%s:%d to [%s]%s:%d",
+ Curl_alpnid2str(srcalpnid), host, conn->remote_port,
+ Curl_alpnid2str(as->dst.alpnid), hostd, as->dst.port);
+ if(srcalpnid != as->dst.alpnid) {
+ /* protocol version switch */
+ switch(as->dst.alpnid) {
+ case ALPN_h1:
+ conn->httpversion = 11;
+ break;
+ case ALPN_h2:
+ conn->httpversion = 20;
+ break;
+ case ALPN_h3:
+ conn->transport = TRNSPRT_QUIC;
+ conn->httpversion = 30;
+ break;
+ default: /* shouldn't be possible */
+ break;
+ }
+ }
+ }
+ }
+#endif
+
+ return result;
+}
+
+#ifdef USE_UNIX_SOCKETS
+static CURLcode resolve_unix(struct Curl_easy *data,
+ struct connectdata *conn,
+ char *unix_path)
+{
+ struct Curl_dns_entry *hostaddr = NULL;
+ bool longpath = FALSE;
+
+ DEBUGASSERT(unix_path);
+ DEBUGASSERT(conn->dns_entry == NULL);
+
+ /* Unix domain sockets are local. The host gets ignored, just use the
+ * specified domain socket address. Do not cache "DNS entries". There is
+ * no DNS involved and we already have the filesystem path available. */
+ hostaddr = calloc(1, sizeof(struct Curl_dns_entry));
+ if(!hostaddr)
+ return CURLE_OUT_OF_MEMORY;
+
+ hostaddr->addr = Curl_unix2addr(unix_path, &longpath,
+ conn->bits.abstract_unix_socket);
+ if(!hostaddr->addr) {
+ if(longpath)
+ /* Long paths are not supported for now */
+ failf(data, "Unix socket path too long: '%s'", unix_path);
+ free(hostaddr);
+ return longpath ? CURLE_COULDNT_RESOLVE_HOST : CURLE_OUT_OF_MEMORY;
+ }
+
+ hostaddr->inuse++;
+ conn->dns_entry = hostaddr;
+ return CURLE_OK;
+}
+#endif
+
+#ifndef CURL_DISABLE_PROXY
+static CURLcode resolve_proxy(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool *async)
+{
+ struct Curl_dns_entry *hostaddr = NULL;
+ struct hostname *host;
+ timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+ int rc;
+
+ DEBUGASSERT(conn->dns_entry == NULL);
+
+ host = conn->bits.socksproxy ? &conn->socks_proxy.host :
+ &conn->http_proxy.host;
+
+ conn->hostname_resolve = strdup(host->name);
+ if(!conn->hostname_resolve)
+ return CURLE_OUT_OF_MEMORY;
+
+ rc = Curl_resolv_timeout(data, conn->hostname_resolve, (int)conn->port,
+ &hostaddr, timeout_ms);
+ conn->dns_entry = hostaddr;
+ if(rc == CURLRESOLV_PENDING)
+ *async = TRUE;
+ else if(rc == CURLRESOLV_TIMEDOUT)
+ return CURLE_OPERATION_TIMEDOUT;
+ else if(!hostaddr) {
+ failf(data, "Couldn't resolve proxy '%s'", host->dispname);
+ return CURLE_COULDNT_RESOLVE_PROXY;
+ }
+
+ return CURLE_OK;
+}
+#endif
+
+static CURLcode resolve_host(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool *async)
+{
+ struct Curl_dns_entry *hostaddr = NULL;
+ struct hostname *connhost;
+ timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+ int rc;
+
+ DEBUGASSERT(conn->dns_entry == NULL);
+
+ connhost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host;
+
+ /* If not connecting via a proxy, extract the port from the URL, if it is
+ * there, thus overriding any defaults that might have been set above. */
+ conn->port = conn->bits.conn_to_port ? conn->conn_to_port :
+ conn->remote_port;
+
+ /* Resolve target host right on */
+ conn->hostname_resolve = strdup(connhost->name);
+ if(!conn->hostname_resolve)
+ return CURLE_OUT_OF_MEMORY;
+
+ rc = Curl_resolv_timeout(data, conn->hostname_resolve, (int)conn->port,
+ &hostaddr, timeout_ms);
+ conn->dns_entry = hostaddr;
+ if(rc == CURLRESOLV_PENDING)
+ *async = TRUE;
+ else if(rc == CURLRESOLV_TIMEDOUT) {
+ failf(data, "Failed to resolve host '%s' with timeout after %ld ms",
+ connhost->dispname,
+ Curl_timediff(Curl_now(), data->progress.t_startsingle));
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+ else if(!hostaddr) {
+ failf(data, "Could not resolve host: %s", connhost->dispname);
+ return CURLE_COULDNT_RESOLVE_HOST;
+ }
+
+ return CURLE_OK;
+}
+
+/* Perform a fresh resolve */
+static CURLcode resolve_fresh(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool *async)
+{
+#ifdef USE_UNIX_SOCKETS
+ char *unix_path = conn->unix_domain_socket;
+
+#ifndef CURL_DISABLE_PROXY
+ if(!unix_path && conn->socks_proxy.host.name &&
+ !strncmp(UNIX_SOCKET_PREFIX"/",
+ conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX)))
+ unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1;
+#endif
+
+ if(unix_path) {
+ conn->transport = TRNSPRT_UNIX;
+ return resolve_unix(data, conn, unix_path);
+ }
+#endif
+
+#ifndef CURL_DISABLE_PROXY
+ if(CONN_IS_PROXIED(conn))
+ return resolve_proxy(data, conn, async);
+#endif
+
+ return resolve_host(data, conn, async);
+}
+
+/*************************************************************
+ * Resolve the address of the server or proxy
+ *************************************************************/
+static CURLcode resolve_server(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool *async)
+{
+ DEBUGASSERT(conn);
+ DEBUGASSERT(data);
+
+ /* Resolve the name of the server or proxy */
+ if(conn->bits.reuse) {
+ /* We're reusing the connection - no need to resolve anything, and
+ idnconvert_hostname() was called already in create_conn() for the re-use
+ case. */
+ *async = FALSE;
+ return CURLE_OK;
+ }
+
+ return resolve_fresh(data, conn, async);
+}
+
+/*
+ * Cleanup the connection `temp`, just allocated for `data`, before using the
+ * previously `existing` one for `data`. All relevant info is copied over
+ * and `temp` is freed.
+ */
+static void reuse_conn(struct Curl_easy *data,
+ struct connectdata *temp,
+ struct connectdata *existing)
+{
+ /* 'local_ip' and 'local_port' get filled with local's numerical
+ ip address and port number whenever an outgoing connection is
+ **established** from the primary socket to a remote address. */
+ char local_ip[MAX_IPADR_LEN] = "";
+ int local_port = -1;
+
+ /* get the user+password information from the temp struct since it may
+ * be new for this request even when we re-use an existing connection */
+ if(temp->user) {
+ /* use the new user name and password though */
+ Curl_safefree(existing->user);
+ Curl_safefree(existing->passwd);
+ existing->user = temp->user;
+ existing->passwd = temp->passwd;
+ temp->user = NULL;
+ temp->passwd = NULL;
+ }
+
+#ifndef CURL_DISABLE_PROXY
+ existing->bits.proxy_user_passwd = temp->bits.proxy_user_passwd;
+ if(existing->bits.proxy_user_passwd) {
+ /* use the new proxy user name and proxy password though */
+ Curl_safefree(existing->http_proxy.user);
+ Curl_safefree(existing->socks_proxy.user);
+ Curl_safefree(existing->http_proxy.passwd);
+ Curl_safefree(existing->socks_proxy.passwd);
+ existing->http_proxy.user = temp->http_proxy.user;
+ existing->socks_proxy.user = temp->socks_proxy.user;
+ existing->http_proxy.passwd = temp->http_proxy.passwd;
+ existing->socks_proxy.passwd = temp->socks_proxy.passwd;
+ temp->http_proxy.user = NULL;
+ temp->socks_proxy.user = NULL;
+ temp->http_proxy.passwd = NULL;
+ temp->socks_proxy.passwd = NULL;
+ }
+#endif
+
+ Curl_free_idnconverted_hostname(&existing->host);
+ Curl_free_idnconverted_hostname(&existing->conn_to_host);
+ Curl_safefree(existing->host.rawalloc);
+ Curl_safefree(existing->conn_to_host.rawalloc);
+ existing->host = temp->host;
+ temp->host.rawalloc = NULL;
+ temp->host.encalloc = NULL;
+ existing->conn_to_host = temp->conn_to_host;
+ temp->conn_to_host.rawalloc = NULL;
+ existing->conn_to_port = temp->conn_to_port;
+ existing->remote_port = temp->remote_port;
+ Curl_safefree(existing->hostname_resolve);
+
+ existing->hostname_resolve = temp->hostname_resolve;
+ temp->hostname_resolve = NULL;
+
+ /* persist connection info in session handle */
+ if(existing->transport == TRNSPRT_TCP) {
+ Curl_conninfo_local(data, existing->sock[FIRSTSOCKET],
+ local_ip, &local_port);
+ }
+ Curl_persistconninfo(data, existing, local_ip, local_port);
+
+ conn_reset_all_postponed_data(temp); /* free buffers */
+
+ /* re-use init */
+ existing->bits.reuse = TRUE; /* yes, we're re-using here */
+
+ conn_free(data, temp);
+}
+
+/**
+ * create_conn() sets up a new connectdata struct, or re-uses an already
+ * existing one, and resolves host name.
+ *
+ * if this function returns CURLE_OK and *async is set to TRUE, the resolve
+ * response will be coming asynchronously. If *async is FALSE, the name is
+ * already resolved.
+ *
+ * @param data The sessionhandle pointer
+ * @param in_connect is set to the next connection data pointer
+ * @param async is set TRUE when an async DNS resolution is pending
+ * @see Curl_setup_conn()
+ *
+ */
+
+static CURLcode create_conn(struct Curl_easy *data,
+ struct connectdata **in_connect,
+ bool *async)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn;
+ struct connectdata *existing = NULL;
+ bool reuse;
+ bool connections_available = TRUE;
+ bool force_reuse = FALSE;
+ bool waitpipe = FALSE;
+ size_t max_host_connections = Curl_multi_max_host_connections(data->multi);
+ size_t max_total_connections = Curl_multi_max_total_connections(data->multi);
+
+ *async = FALSE;
+ *in_connect = NULL;
+
+ /*************************************************************
+ * Check input data
+ *************************************************************/
+ if(!data->state.url) {
+ result = CURLE_URL_MALFORMAT;
+ goto out;
+ }
+
+ /* First, split up the current URL in parts so that we can use the
+ parts for checking against the already present connections. In order
+ to not have to modify everything at once, we allocate a temporary
+ connection data struct and fill in for comparison purposes. */
+ conn = allocate_conn(data);
+
+ if(!conn) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ /* We must set the return variable as soon as possible, so that our
+ parent can cleanup any possible allocs we may have done before
+ any failure */
+ *in_connect = conn;
+
+ result = parseurlandfillconn(data, conn);
+ if(result)
+ goto out;
+
+ if(data->set.str[STRING_SASL_AUTHZID]) {
+ conn->sasl_authzid = strdup(data->set.str[STRING_SASL_AUTHZID]);
+ if(!conn->sasl_authzid) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ }
+
+ if(data->set.str[STRING_BEARER]) {
+ conn->oauth_bearer = strdup(data->set.str[STRING_BEARER]);
+ if(!conn->oauth_bearer) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ }
+
+#ifdef USE_UNIX_SOCKETS
+ if(data->set.str[STRING_UNIX_SOCKET_PATH]) {
+ conn->unix_domain_socket = strdup(data->set.str[STRING_UNIX_SOCKET_PATH]);
+ if(!conn->unix_domain_socket) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ conn->bits.abstract_unix_socket = data->set.abstract_unix_socket;
+ }
+#endif
+
+ /* After the unix socket init but before the proxy vars are used, parse and
+ initialize the proxy vars */
+#ifndef CURL_DISABLE_PROXY
+ result = create_conn_helper_init_proxy(data, conn);
+ if(result)
+ goto out;
+
+ /*************************************************************
+ * If the protocol is using SSL and HTTP proxy is used, we set
+ * the tunnel_proxy bit.
+ *************************************************************/
+ if((conn->given->flags&PROTOPT_SSL) && conn->bits.httpproxy)
+ conn->bits.tunnel_proxy = TRUE;
+#endif
+
+ /*************************************************************
+ * Figure out the remote port number and fix it in the URL
+ *************************************************************/
+ result = parse_remote_port(data, conn);
+ if(result)
+ goto out;
+
+ /* Check for overridden login details and set them accordingly so that
+ they are known when protocol->setup_connection is called! */
+ result = override_login(data, conn);
+ if(result)
+ goto out;
+
+ result = set_login(data, conn); /* default credentials */
+ if(result)
+ goto out;
+
+ /*************************************************************
+ * Process the "connect to" linked list of hostname/port mappings.
+ * Do this after the remote port number has been fixed in the URL.
+ *************************************************************/
+ result = parse_connect_to_slist(data, conn, data->set.connect_to);
+ if(result)
+ goto out;
+
+ /*************************************************************
+ * IDN-convert the proxy hostnames
+ *************************************************************/
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.httpproxy) {
+ result = Curl_idnconvert_hostname(&conn->http_proxy.host);
+ if(result)
+ return result;
+ }
+ if(conn->bits.socksproxy) {
+ result = Curl_idnconvert_hostname(&conn->socks_proxy.host);
+ if(result)
+ return result;
+ }
+#endif
+
+ /*************************************************************
+ * Check whether the host and the "connect to host" are equal.
+ * Do this after the hostnames have been IDN-converted.
+ *************************************************************/
+ if(conn->bits.conn_to_host &&
+ strcasecompare(conn->conn_to_host.name, conn->host.name)) {
+ conn->bits.conn_to_host = FALSE;
+ }
+
+ /*************************************************************
+ * Check whether the port and the "connect to port" are equal.
+ * Do this after the remote port number has been fixed in the URL.
+ *************************************************************/
+ if(conn->bits.conn_to_port && conn->conn_to_port == conn->remote_port) {
+ conn->bits.conn_to_port = FALSE;
+ }
+
+#ifndef CURL_DISABLE_PROXY
+ /*************************************************************
+ * If the "connect to" feature is used with an HTTP proxy,
+ * we set the tunnel_proxy bit.
+ *************************************************************/
+ if((conn->bits.conn_to_host || conn->bits.conn_to_port) &&
+ conn->bits.httpproxy)
+ conn->bits.tunnel_proxy = TRUE;
+#endif
+
+ /*************************************************************
+ * Setup internals depending on protocol. Needs to be done after
+ * we figured out what/if proxy to use.
+ *************************************************************/
+ result = setup_connection_internals(data, conn);
+ if(result)
+ goto out;
+
+ /***********************************************************************
+ * file: is a special case in that it doesn't need a network connection
+ ***********************************************************************/
+#ifndef CURL_DISABLE_FILE
+ if(conn->handler->flags & PROTOPT_NONETWORK) {
+ bool done;
+ /* this is supposed to be the connect function so we better at least check
+ that the file is present here! */
+ DEBUGASSERT(conn->handler->connect_it);
+ Curl_persistconninfo(data, conn, NULL, -1);
+ result = conn->handler->connect_it(data, &done);
+
+ /* Setup a "faked" transfer that'll do nothing */
+ if(!result) {
+ Curl_attach_connection(data, conn);
+ result = Curl_conncache_add_conn(data);
+ if(result)
+ goto out;
+
+ /*
+ * Setup whatever necessary for a resumed transfer
+ */
+ result = setup_range(data);
+ if(result) {
+ DEBUGASSERT(conn->handler->done);
+ /* we ignore the return code for the protocol-specific DONE */
+ (void)conn->handler->done(data, result, FALSE);
+ goto out;
+ }
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
+ }
+
+ /* since we skip do_init() */
+ Curl_init_do(data, conn);
+
+ goto out;
+ }
+#endif
+
+ /* Setup filter for network connections */
+ conn->recv[FIRSTSOCKET] = Curl_conn_recv;
+ conn->send[FIRSTSOCKET] = Curl_conn_send;
+ conn->recv[SECONDARYSOCKET] = Curl_conn_recv;
+ conn->send[SECONDARYSOCKET] = Curl_conn_send;
+ conn->bits.tcp_fastopen = data->set.tcp_fastopen;
+
+ /* Get a cloned copy of the SSL config situation stored in the
+ connection struct. But to get this going nicely, we must first make
+ sure that the strings in the master copy are pointing to the correct
+ strings in the session handle strings array!
+
+ Keep in mind that the pointers in the master copy are pointing to strings
+ that will be freed as part of the Curl_easy struct, but all cloned
+ copies will be separately allocated.
+ */
+ data->set.ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH];
+ data->set.ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE];
+ data->set.ssl.primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT];
+ data->set.ssl.primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT];
+ data->set.ssl.primary.cipher_list =
+ data->set.str[STRING_SSL_CIPHER_LIST];
+ data->set.ssl.primary.cipher_list13 =
+ data->set.str[STRING_SSL_CIPHER13_LIST];
+ data->set.ssl.primary.pinned_key =
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY];
+ data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT];
+ data->set.ssl.primary.ca_info_blob = data->set.blobs[BLOB_CAINFO];
+ data->set.ssl.primary.curves = data->set.str[STRING_SSL_EC_CURVES];
+
+#ifndef CURL_DISABLE_PROXY
+ data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY];
+ data->set.proxy_ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY];
+ data->set.proxy_ssl.primary.cipher_list =
+ data->set.str[STRING_SSL_CIPHER_LIST_PROXY];
+ data->set.proxy_ssl.primary.cipher_list13 =
+ data->set.str[STRING_SSL_CIPHER13_LIST_PROXY];
+ data->set.proxy_ssl.primary.pinned_key =
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY];
+ data->set.proxy_ssl.primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY];
+ data->set.proxy_ssl.primary.ca_info_blob =
+ data->set.blobs[BLOB_CAINFO_PROXY];
+ data->set.proxy_ssl.primary.issuercert =
+ data->set.str[STRING_SSL_ISSUERCERT_PROXY];
+ data->set.proxy_ssl.primary.issuercert_blob =
+ data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY];
+ data->set.proxy_ssl.primary.CRLfile =
+ data->set.str[STRING_SSL_CRLFILE_PROXY];
+ data->set.proxy_ssl.cert_type = data->set.str[STRING_CERT_TYPE_PROXY];
+ data->set.proxy_ssl.key = data->set.str[STRING_KEY_PROXY];
+ data->set.proxy_ssl.key_type = data->set.str[STRING_KEY_TYPE_PROXY];
+ data->set.proxy_ssl.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY];
+ data->set.proxy_ssl.primary.clientcert = data->set.str[STRING_CERT_PROXY];
+ data->set.proxy_ssl.key_blob = data->set.blobs[BLOB_KEY_PROXY];
+#endif
+ data->set.ssl.primary.CRLfile = data->set.str[STRING_SSL_CRLFILE];
+ data->set.ssl.cert_type = data->set.str[STRING_CERT_TYPE];
+ data->set.ssl.key = data->set.str[STRING_KEY];
+ data->set.ssl.key_type = data->set.str[STRING_KEY_TYPE];
+ data->set.ssl.key_passwd = data->set.str[STRING_KEY_PASSWD];
+ data->set.ssl.primary.clientcert = data->set.str[STRING_CERT];
+#ifdef USE_TLS_SRP
+ data->set.ssl.primary.username = data->set.str[STRING_TLSAUTH_USERNAME];
+ data->set.ssl.primary.password = data->set.str[STRING_TLSAUTH_PASSWORD];
+#ifndef CURL_DISABLE_PROXY
+ data->set.proxy_ssl.primary.username =
+ data->set.str[STRING_TLSAUTH_USERNAME_PROXY];
+ data->set.proxy_ssl.primary.password =
+ data->set.str[STRING_TLSAUTH_PASSWORD_PROXY];
+#endif
+#endif
+ data->set.ssl.key_blob = data->set.blobs[BLOB_KEY];
+
+ if(!Curl_clone_primary_ssl_config(&data->set.ssl.primary,
+ &conn->ssl_config)) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+#ifndef CURL_DISABLE_PROXY
+ if(!Curl_clone_primary_ssl_config(&data->set.proxy_ssl.primary,
+ &conn->proxy_ssl_config)) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+#endif
+
+ prune_dead_connections(data);
+
+ /*************************************************************
+ * Check the current list of connections to see if we can
+ * re-use an already existing one or if we have to create a
+ * new one.
+ *************************************************************/
+
+ DEBUGASSERT(conn->user);
+ DEBUGASSERT(conn->passwd);
+
+ /* reuse_fresh is TRUE if we are told to use a new connection by force, but
+ we only acknowledge this option if this is not a re-used connection
+ already (which happens due to follow-location or during an HTTP
+ authentication phase). CONNECT_ONLY transfers also refuse reuse. */
+ if((data->set.reuse_fresh && !data->state.followlocation) ||
+ data->set.connect_only)
+ reuse = FALSE;
+ else
+ reuse = ConnectionExists(data, conn, &existing, &force_reuse, &waitpipe);
+
+ if(reuse) {
+ /*
+ * We already have a connection for this, we got the former connection in
+ * `existing` and thus we need to cleanup the one we just
+ * allocated before we can move along and use `existing`.
+ */
+ reuse_conn(data, conn, existing);
+ conn = existing;
+ *in_connect = conn;
+
+#ifndef CURL_DISABLE_PROXY
+ infof(data, "Re-using existing connection #%ld with %s %s",
+ conn->connection_id,
+ conn->bits.proxy?"proxy":"host",
+ conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname :
+ conn->http_proxy.host.name ? conn->http_proxy.host.dispname :
+ conn->host.dispname);
+#else
+ infof(data, "Re-using existing connection #%ld with host %s",
+ conn->connection_id, conn->host.dispname);
+#endif
+ }
+ else {
+ /* We have decided that we want a new connection. However, we may not
+ be able to do that if we have reached the limit of how many
+ connections we are allowed to open. */
+
+ if(conn->handler->flags & PROTOPT_ALPN) {
+ /* The protocol wants it, so set the bits if enabled in the easy handle
+ (default) */
+ if(data->set.ssl_enable_alpn)
+ conn->bits.tls_enable_alpn = TRUE;
+ }
+
+ if(waitpipe)
+ /* There is a connection that *might* become usable for multiplexing
+ "soon", and we wait for that */
+ connections_available = FALSE;
+ else {
+ /* this gets a lock on the conncache */
+ struct connectbundle *bundle =
+ Curl_conncache_find_bundle(data, conn, data->state.conn_cache);
+
+ if(max_host_connections > 0 && bundle &&
+ (bundle->num_connections >= max_host_connections)) {
+ struct connectdata *conn_candidate;
+
+ /* The bundle is full. Extract the oldest connection. */
+ conn_candidate = Curl_conncache_extract_bundle(data, bundle);
+ CONNCACHE_UNLOCK(data);
+
+ if(conn_candidate)
+ Curl_disconnect(data, conn_candidate, FALSE);
+ else {
+ infof(data, "No more connections allowed to host: %zu",
+ max_host_connections);
+ connections_available = FALSE;
+ }
+ }
+ else
+ CONNCACHE_UNLOCK(data);
+
+ }
+
+ if(connections_available &&
+ (max_total_connections > 0) &&
+ (Curl_conncache_size(data) >= max_total_connections)) {
+ struct connectdata *conn_candidate;
+
+ /* The cache is full. Let's see if we can kill a connection. */
+ conn_candidate = Curl_conncache_extract_oldest(data);
+ if(conn_candidate)
+ Curl_disconnect(data, conn_candidate, FALSE);
+ else {
+ infof(data, "No connections available in cache");
+ connections_available = FALSE;
+ }
+ }
+
+ if(!connections_available) {
+ infof(data, "No connections available.");
+
+ conn_free(data, conn);
+ *in_connect = NULL;
+
+ result = CURLE_NO_CONNECTION_AVAILABLE;
+ goto out;
+ }
+ else {
+ /*
+ * This is a brand new connection, so let's store it in the connection
+ * cache of ours!
+ */
+ Curl_attach_connection(data, conn);
+ result = Curl_conncache_add_conn(data);
+ if(result)
+ goto out;
+ }
+
+#if defined(USE_NTLM)
+ /* If NTLM is requested in a part of this connection, make sure we don't
+ assume the state is fine as this is a fresh connection and NTLM is
+ connection based. */
+ if((data->state.authhost.picked & (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) &&
+ data->state.authhost.done) {
+ infof(data, "NTLM picked AND auth done set, clear picked");
+ data->state.authhost.picked = CURLAUTH_NONE;
+ data->state.authhost.done = FALSE;
+ }
+
+ if((data->state.authproxy.picked & (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) &&
+ data->state.authproxy.done) {
+ infof(data, "NTLM-proxy picked AND auth done set, clear picked");
+ data->state.authproxy.picked = CURLAUTH_NONE;
+ data->state.authproxy.done = FALSE;
+ }
+#endif
+ }
+
+ /* Setup and init stuff before DO starts, in preparing for the transfer. */
+ Curl_init_do(data, conn);
+
+ /*
+ * Setup whatever necessary for a resumed transfer
+ */
+ result = setup_range(data);
+ if(result)
+ goto out;
+
+ /* Continue connectdata initialization here. */
+
+ /*
+ * Inherit the proper values from the urldata struct AFTER we have arranged
+ * the persistent connection stuff
+ */
+ conn->seek_func = data->set.seek_func;
+ conn->seek_client = data->set.seek_client;
+
+ /*************************************************************
+ * Resolve the address of the server or proxy
+ *************************************************************/
+ result = resolve_server(data, conn, async);
+
+out:
+ return result;
+}
+
+/* Curl_setup_conn() is called after the name resolve initiated in
+ * create_conn() is all done.
+ *
+ * Curl_setup_conn() also handles reused connections
+ */
+CURLcode Curl_setup_conn(struct Curl_easy *data,
+ bool *protocol_done)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+
+ Curl_pgrsTime(data, TIMER_NAMELOOKUP);
+
+ if(conn->handler->flags & PROTOPT_NONETWORK) {
+ /* nothing to setup when not using a network */
+ *protocol_done = TRUE;
+ return result;
+ }
+
+#ifndef CURL_DISABLE_PROXY
+ /* set proxy_connect_closed to false unconditionally already here since it
+ is used strictly to provide extra information to a parent function in the
+ case of proxy CONNECT failures and we must make sure we don't have it
+ lingering set from a previous invoke */
+ conn->bits.proxy_connect_closed = FALSE;
+#endif
+
+#ifdef CURL_DO_LINEEND_CONV
+ data->state.crlf_conversions = 0; /* reset CRLF conversion counter */
+#endif /* CURL_DO_LINEEND_CONV */
+
+ /* set start time here for timeout purposes in the connect procedure, it
+ is later set again for the progress meter purpose */
+ conn->now = Curl_now();
+ if(!conn->bits.reuse)
+ result = Curl_conn_setup(data, conn, FIRSTSOCKET, conn->dns_entry,
+ CURL_CF_SSL_DEFAULT);
+ /* not sure we need this flag to be passed around any more */
+ *protocol_done = FALSE;
+ return result;
+}
+
+CURLcode Curl_connect(struct Curl_easy *data,
+ bool *asyncp,
+ bool *protocol_done)
+{
+ CURLcode result;
+ struct connectdata *conn;
+
+ *asyncp = FALSE; /* assume synchronous resolves by default */
+
+ /* init the single-transfer specific data */
+ Curl_free_request_state(data);
+ memset(&data->req, 0, sizeof(struct SingleRequest));
+ data->req.size = data->req.maxdownload = -1;
+ data->req.no_body = data->set.opt_no_body;
+
+ /* call the stuff that needs to be called */
+ result = create_conn(data, &conn, asyncp);
+
+ if(!result) {
+ if(CONN_INUSE(conn) > 1)
+ /* multiplexed */
+ *protocol_done = TRUE;
+ else if(!*asyncp) {
+ /* DNS resolution is done: that's either because this is a reused
+ connection, in which case DNS was unnecessary, or because DNS
+ really did finish already (synch resolver/fast async resolve) */
+ result = Curl_setup_conn(data, protocol_done);
+ }
+ }
+
+ if(result == CURLE_NO_CONNECTION_AVAILABLE) {
+ return result;
+ }
+ else if(result && conn) {
+ /* We're not allowed to return failure with memory left allocated in the
+ connectdata struct, free those here */
+ Curl_detach_connection(data);
+ Curl_conncache_remove_conn(data, conn, TRUE);
+ Curl_disconnect(data, conn, TRUE);
+ }
+
+ return result;
+}
+
+/*
+ * Curl_init_do() inits the readwrite session. This is inited each time (in
+ * the DO function before the protocol-specific DO functions are invoked) for
+ * a transfer, sometimes multiple times on the same Curl_easy. Make sure
+ * nothing in here depends on stuff that are setup dynamically for the
+ * transfer.
+ *
+ * Allow this function to get called with 'conn' set to NULL.
+ */
+
+CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn)
+{
+ struct SingleRequest *k = &data->req;
+
+ /* if this is a pushed stream, we need this: */
+ CURLcode result = Curl_preconnect(data);
+ if(result)
+ return result;
+
+ if(conn) {
+ conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to
+ use */
+ /* if the protocol used doesn't support wildcards, switch it off */
+ if(data->state.wildcardmatch &&
+ !(conn->handler->flags & PROTOPT_WILDCARD))
+ data->state.wildcardmatch = FALSE;
+ }
+
+ data->state.done = FALSE; /* *_done() is not called yet */
+ data->state.expect100header = FALSE;
+
+ if(data->req.no_body)
+ /* in HTTP lingo, no body means using the HEAD request... */
+ data->state.httpreq = HTTPREQ_HEAD;
+
+ k->start = Curl_now(); /* start time */
+ k->now = k->start; /* current time is now */
+ k->header = TRUE; /* assume header */
+ k->bytecount = 0;
+ k->ignorebody = FALSE;
+
+ Curl_speedinit(data);
+ Curl_pgrsSetUploadCounter(data, 0);
+ Curl_pgrsSetDownloadCounter(data, 0);
+
+ return CURLE_OK;
+}
diff --git a/libs/libcurl/src/url.h b/libs/libcurl/src/url.h index ba4270d523..49364c8224 100644 --- a/libs/libcurl/src/url.h +++ b/libs/libcurl/src/url.h @@ -1,84 +1,62 @@ -#ifndef HEADER_CURL_URL_H -#define HEADER_CURL_URL_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -/* - * Prototypes for library-wide functions provided by url.c - */ - -CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn); -CURLcode Curl_open(struct Curl_easy **curl); -CURLcode Curl_init_userdefined(struct Curl_easy *data); - -void Curl_freeset(struct Curl_easy *data); -CURLcode Curl_uc_to_curlcode(CURLUcode uc); -CURLcode Curl_close(struct Curl_easy **datap); /* opposite of curl_open() */ -CURLcode Curl_connect(struct Curl_easy *, bool *async, bool *protocol_connect); -void Curl_disconnect(struct Curl_easy *data, - struct connectdata *, bool dead_connection); -CURLcode Curl_setup_conn(struct Curl_easy *data, - bool *protocol_done); -void Curl_free_request_state(struct Curl_easy *data); -CURLcode Curl_parse_login_details(const char *login, const size_t len, - char **userptr, char **passwdptr, - char **optionsptr); - -const struct Curl_handler *Curl_builtin_scheme(const char *scheme, - size_t schemelen); - -bool Curl_is_ASCII_name(const char *hostname); -CURLcode Curl_idnconvert_hostname(struct Curl_easy *data, - struct hostname *host); -void Curl_free_idnconverted_hostname(struct hostname *host); - -#define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */ -#define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless - specified */ - -#ifdef CURL_DISABLE_VERBOSE_STRINGS -#define Curl_verboseconnect(x,y) Curl_nop_stmt -#else -void Curl_verboseconnect(struct Curl_easy *data, struct connectdata *conn); -#endif - -#ifdef CURL_DISABLE_PROXY -#define CONNECT_PROXY_SSL() FALSE -#else - -#define CONNECT_PROXY_SSL()\ - (conn->http_proxy.proxytype == CURLPROXY_HTTPS &&\ - !conn->bits.proxy_ssl_connected[sockindex]) - -#define CONNECT_FIRSTSOCKET_PROXY_SSL()\ - (conn->http_proxy.proxytype == CURLPROXY_HTTPS &&\ - !conn->bits.proxy_ssl_connected[FIRSTSOCKET]) - -#define CONNECT_SECONDARYSOCKET_PROXY_SSL()\ - (conn->http_proxy.proxytype == CURLPROXY_HTTPS &&\ - !conn->bits.proxy_ssl_connected[SECONDARYSOCKET]) -#endif /* !CURL_DISABLE_PROXY */ - -#endif /* HEADER_CURL_URL_H */ +#ifndef HEADER_CURL_URL_H
+#define HEADER_CURL_URL_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+/*
+ * Prototypes for library-wide functions provided by url.c
+ */
+
+CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn);
+CURLcode Curl_open(struct Curl_easy **curl);
+CURLcode Curl_init_userdefined(struct Curl_easy *data);
+
+void Curl_freeset(struct Curl_easy *data);
+CURLcode Curl_uc_to_curlcode(CURLUcode uc);
+CURLcode Curl_close(struct Curl_easy **datap); /* opposite of curl_open() */
+CURLcode Curl_connect(struct Curl_easy *, bool *async, bool *protocol_connect);
+void Curl_disconnect(struct Curl_easy *data,
+ struct connectdata *, bool dead_connection);
+CURLcode Curl_setup_conn(struct Curl_easy *data,
+ bool *protocol_done);
+void Curl_free_request_state(struct Curl_easy *data);
+CURLcode Curl_parse_login_details(const char *login, const size_t len,
+ char **userptr, char **passwdptr,
+ char **optionsptr);
+
+const struct Curl_handler *Curl_builtin_scheme(const char *scheme,
+ size_t schemelen);
+
+#define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */
+#define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless
+ specified */
+
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+#define Curl_verboseconnect(x,y) Curl_nop_stmt
+#else
+void Curl_verboseconnect(struct Curl_easy *data, struct connectdata *conn);
+#endif
+
+#endif /* HEADER_CURL_URL_H */
diff --git a/libs/libcurl/src/urlapi-int.h b/libs/libcurl/src/urlapi-int.h index 43a83ef6e4..ac14e8e242 100644 --- a/libs/libcurl/src/urlapi-int.h +++ b/libs/libcurl/src/urlapi-int.h @@ -1,36 +1,36 @@ -#ifndef HEADER_CURL_URLAPI_INT_H -#define HEADER_CURL_URLAPI_INT_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, - bool guess_scheme); - -#ifdef DEBUGBUILD -CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, - bool has_scheme); -#endif - -#endif /* HEADER_CURL_URLAPI_INT_H */ +#ifndef HEADER_CURL_URLAPI_INT_H
+#define HEADER_CURL_URLAPI_INT_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen,
+ bool guess_scheme);
+
+#ifdef DEBUGBUILD
+CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
+ bool has_scheme);
+#endif
+
+#endif /* HEADER_CURL_URLAPI_INT_H */
diff --git a/libs/libcurl/src/urlapi.c b/libs/libcurl/src/urlapi.c index 7dac81c85c..203fbc4149 100644 --- a/libs/libcurl/src/urlapi.c +++ b/libs/libcurl/src/urlapi.c @@ -1,1886 +1,1886 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "urlapi-int.h" -#include "strcase.h" -#include "url.h" -#include "escape.h" -#include "curl_ctype.h" -#include "inet_pton.h" -#include "inet_ntop.h" -#include "strdup.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - - /* MSDOS/Windows style drive prefix, eg c: in c:foo */ -#define STARTS_WITH_DRIVE_PREFIX(str) \ - ((('a' <= str[0] && str[0] <= 'z') || \ - ('A' <= str[0] && str[0] <= 'Z')) && \ - (str[1] == ':')) - - /* MSDOS/Windows style drive prefix, optionally with - * a '|' instead of ':', followed by a slash or NUL */ -#define STARTS_WITH_URL_DRIVE_PREFIX(str) \ - ((('a' <= (str)[0] && (str)[0] <= 'z') || \ - ('A' <= (str)[0] && (str)[0] <= 'Z')) && \ - ((str)[1] == ':' || (str)[1] == '|') && \ - ((str)[2] == '/' || (str)[2] == '\\' || (str)[2] == 0)) - -/* scheme is not URL encoded, the longest libcurl supported ones are... */ -#define MAX_SCHEME_LEN 40 - -/* Internal representation of CURLU. Point to URL-encoded strings. */ -struct Curl_URL { - char *scheme; - char *user; - char *password; - char *options; /* IMAP only? */ - char *host; - char *zoneid; /* for numerical IPv6 addresses */ - char *port; - char *path; - char *query; - char *fragment; - long portnum; /* the numerical version */ -}; - -#define DEFAULT_SCHEME "https" - -static void free_urlhandle(struct Curl_URL *u) -{ - free(u->scheme); - free(u->user); - free(u->password); - free(u->options); - free(u->host); - free(u->zoneid); - free(u->port); - free(u->path); - free(u->query); - free(u->fragment); -} - -/* - * Find the separator at the end of the host name, or the '?' in cases like - * http://www.url.com?id=2380 - */ -static const char *find_host_sep(const char *url) -{ - const char *sep; - const char *query; - - /* Find the start of the hostname */ - sep = strstr(url, "//"); - if(!sep) - sep = url; - else - sep += 2; - - query = strchr(sep, '?'); - sep = strchr(sep, '/'); - - if(!sep) - sep = url + strlen(url); - - if(!query) - query = url + strlen(url); - - return sep < query ? sep : query; -} - -/* - * Decide in an encoding-independent manner whether a character in a URL must - * be escaped. This is used in urlencode_str(). - */ -static bool urlchar_needs_escaping(int c) -{ - return !(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c)); -} - -/* urlencode_str() writes data into an output dynbuf and URL-encodes the - * spaces in the source URL accordingly. - * - * URL encoding should be skipped for host names, otherwise IDN resolution - * will fail. - */ -static CURLUcode urlencode_str(struct dynbuf *o, const char *url, - size_t len, bool relative, - bool query) -{ - /* we must add this with whitespace-replacing */ - bool left = !query; - const unsigned char *iptr; - const unsigned char *host_sep = (const unsigned char *) url; - - if(!relative) - host_sep = (const unsigned char *) find_host_sep(url); - - for(iptr = (unsigned char *)url; /* read from here */ - len; iptr++, len--) { - - if(iptr < host_sep) { - if(Curl_dyn_addn(o, iptr, 1)) - return CURLUE_OUT_OF_MEMORY; - continue; - } - - if(*iptr == ' ') { - if(left) { - if(Curl_dyn_addn(o, "%20", 3)) - return CURLUE_OUT_OF_MEMORY; - } - else { - if(Curl_dyn_addn(o, "+", 1)) - return CURLUE_OUT_OF_MEMORY; - } - continue; - } - - if(*iptr == '?') - left = FALSE; - - if(urlchar_needs_escaping(*iptr)) { - if(Curl_dyn_addf(o, "%%%02x", *iptr)) - return CURLUE_OUT_OF_MEMORY; - } - else { - if(Curl_dyn_addn(o, iptr, 1)) - return CURLUE_OUT_OF_MEMORY; - } - } - - return CURLUE_OK; -} - -/* - * Returns the length of the scheme if the given URL is absolute (as opposed - * to relative). Stores the scheme in the buffer if TRUE and 'buf' is - * non-NULL. The buflen must be larger than MAX_SCHEME_LEN if buf is set. - * - * If 'guess_scheme' is TRUE, it means the URL might be provided without - * scheme. - */ -size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, - bool guess_scheme) -{ - int i; - DEBUGASSERT(!buf || (buflen > MAX_SCHEME_LEN)); - (void)buflen; /* only used in debug-builds */ - if(buf) - buf[0] = 0; /* always leave a defined value in buf */ -#ifdef WIN32 - if(guess_scheme && STARTS_WITH_DRIVE_PREFIX(url)) - return 0; -#endif - for(i = 0; i < MAX_SCHEME_LEN; ++i) { - char s = url[i]; - if(s && (ISALNUM(s) || (s == '+') || (s == '-') || (s == '.') )) { - /* RFC 3986 3.1 explains: - scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) - */ - } - else { - break; - } - } - if(i && (url[i] == ':') && ((url[i + 1] == '/') || !guess_scheme)) { - /* If this does not guess scheme, the scheme always ends with the colon so - that this also detects data: URLs etc. In guessing mode, data: could - be the host name "data" with a specified port number. */ - - /* the length of the scheme is the name part only */ - size_t len = i; - if(buf) { - buf[i] = 0; - while(i--) { - buf[i] = Curl_raw_tolower(url[i]); - } - } - return len; - } - return 0; -} - -/* - * Concatenate a relative URL to a base URL making it absolute. - * URL-encodes any spaces. - * The returned pointer must be freed by the caller unless NULL - * (returns NULL on out of memory). - * - * Note that this function destroys the 'base' string. - */ -static char *concat_url(char *base, const char *relurl) -{ - /*** - TRY to append this new path to the old URL - to the right of the host part. Oh crap, this is doomed to cause - problems in the future... - */ - struct dynbuf newest; - char *protsep; - char *pathsep; - bool host_changed = FALSE; - const char *useurl = relurl; - - /* protsep points to the start of the host name */ - protsep = strstr(base, "//"); - if(!protsep) - protsep = base; - else - protsep += 2; /* pass the slashes */ - - if('/' != relurl[0]) { - int level = 0; - - /* First we need to find out if there's a ?-letter in the URL, - and cut it and the right-side of that off */ - pathsep = strchr(protsep, '?'); - if(pathsep) - *pathsep = 0; - - /* we have a relative path to append to the last slash if there's one - available, or if the new URL is just a query string (starts with a - '?') we append the new one at the end of the entire currently worked - out URL */ - if(useurl[0] != '?') { - pathsep = strrchr(protsep, '/'); - if(pathsep) - *pathsep = 0; - } - - /* Check if there's any slash after the host name, and if so, remember - that position instead */ - pathsep = strchr(protsep, '/'); - if(pathsep) - protsep = pathsep + 1; - else - protsep = NULL; - - /* now deal with one "./" or any amount of "../" in the newurl - and act accordingly */ - - if((useurl[0] == '.') && (useurl[1] == '/')) - useurl += 2; /* just skip the "./" */ - - while((useurl[0] == '.') && - (useurl[1] == '.') && - (useurl[2] == '/')) { - level++; - useurl += 3; /* pass the "../" */ - } - - if(protsep) { - while(level--) { - /* cut off one more level from the right of the original URL */ - pathsep = strrchr(protsep, '/'); - if(pathsep) - *pathsep = 0; - else { - *protsep = 0; - break; - } - } - } - } - else { - /* We got a new absolute path for this server */ - - if(relurl[1] == '/') { - /* the new URL starts with //, just keep the protocol part from the - original one */ - *protsep = 0; - useurl = &relurl[2]; /* we keep the slashes from the original, so we - skip the new ones */ - host_changed = TRUE; - } - else { - /* cut off the original URL from the first slash, or deal with URLs - without slash */ - pathsep = strchr(protsep, '/'); - if(pathsep) { - /* When people use badly formatted URLs, such as - "http://www.url.com?dir=/home/daniel" we must not use the first - slash, if there's a ?-letter before it! */ - char *sep = strchr(protsep, '?'); - if(sep && (sep < pathsep)) - pathsep = sep; - *pathsep = 0; - } - else { - /* There was no slash. Now, since we might be operating on a badly - formatted URL, such as "http://www.url.com?id=2380" which doesn't - use a slash separator as it is supposed to, we need to check for a - ?-letter as well! */ - pathsep = strchr(protsep, '?'); - if(pathsep) - *pathsep = 0; - } - } - } - - Curl_dyn_init(&newest, CURL_MAX_INPUT_LENGTH); - - /* copy over the root url part */ - if(Curl_dyn_add(&newest, base)) - return NULL; - - /* check if we need to append a slash */ - if(('/' == useurl[0]) || (protsep && !*protsep) || ('?' == useurl[0])) - ; - else { - if(Curl_dyn_addn(&newest, "/", 1)) - return NULL; - } - - /* then append the new piece on the right side */ - urlencode_str(&newest, useurl, strlen(useurl), !host_changed, FALSE); - - return Curl_dyn_ptr(&newest); -} - -/* scan for byte values < 31 or 127 */ -static bool junkscan(const char *part, unsigned int flags) -{ - if(part) { - static const char badbytes[]={ - /* */ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x7f, 0x00 /* null-terminate */ - }; - size_t n = strlen(part); - size_t nfine = strcspn(part, badbytes); - if(nfine != n) - /* since we don't know which part is scanned, return a generic error - code */ - return TRUE; - if(!(flags & CURLU_ALLOW_SPACE) && strchr(part, ' ')) - return TRUE; - } - return FALSE; -} - -/* - * parse_hostname_login() - * - * Parse the login details (user name, password and options) from the URL and - * strip them out of the host name - * - */ -static CURLUcode parse_hostname_login(struct Curl_URL *u, - struct dynbuf *host, - unsigned int flags) -{ - CURLUcode result = CURLUE_OK; - CURLcode ccode; - char *userp = NULL; - char *passwdp = NULL; - char *optionsp = NULL; - const struct Curl_handler *h = NULL; - - /* At this point, we assume all the other special cases have been taken - * care of, so the host is at most - * - * [user[:password][;options]]@]hostname - * - * We need somewhere to put the embedded details, so do that first. - */ - - char *login = Curl_dyn_ptr(host); - char *ptr; - - DEBUGASSERT(login); - - ptr = strchr(login, '@'); - if(!ptr) - goto out; - - /* We will now try to extract the - * possible login information in a string like: - * ftp://user:password@ftp.my.site:8021/README */ - ptr++; - - /* if this is a known scheme, get some details */ - if(u->scheme) - h = Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED); - - /* We could use the login information in the URL so extract it. Only parse - options if the handler says we should. Note that 'h' might be NULL! */ - ccode = Curl_parse_login_details(login, ptr - login - 1, - &userp, &passwdp, - (h && (h->flags & PROTOPT_URLOPTIONS)) ? - &optionsp:NULL); - if(ccode) { - result = CURLUE_BAD_LOGIN; - goto out; - } - - if(userp) { - if(flags & CURLU_DISALLOW_USER) { - /* Option DISALLOW_USER is set and url contains username. */ - result = CURLUE_USER_NOT_ALLOWED; - goto out; - } - if(junkscan(userp, flags)) { - result = CURLUE_BAD_USER; - goto out; - } - u->user = userp; - } - - if(passwdp) { - if(junkscan(passwdp, flags)) { - result = CURLUE_BAD_PASSWORD; - goto out; - } - u->password = passwdp; - } - - if(optionsp) { - if(junkscan(optionsp, flags)) { - result = CURLUE_BAD_LOGIN; - goto out; - } - u->options = optionsp; - } - - /* move the name to the start of the host buffer */ - if(Curl_dyn_tail(host, strlen(ptr))) - return CURLUE_OUT_OF_MEMORY; - - return CURLUE_OK; - out: - - free(userp); - free(passwdp); - free(optionsp); - u->user = NULL; - u->password = NULL; - u->options = NULL; - - return result; -} - -UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, - bool has_scheme) -{ - char *portptr = NULL; - char endbracket; - int len; - char *hostname = Curl_dyn_ptr(host); - /* - * Find the end of an IPv6 address, either on the ']' ending bracket or - * a percent-encoded zone index. - */ - if(1 == sscanf(hostname, "[%*45[0123456789abcdefABCDEF:.]%c%n", - &endbracket, &len)) { - if(']' == endbracket) - portptr = &hostname[len]; - else if('%' == endbracket) { - int zonelen = len; - if(1 == sscanf(hostname + zonelen, "%*[^]]%c%n", &endbracket, &len)) { - if(']' != endbracket) - return CURLUE_BAD_IPV6; - portptr = &hostname[--zonelen + len + 1]; - } - else - return CURLUE_BAD_IPV6; - } - else - return CURLUE_BAD_IPV6; - - /* this is a RFC2732-style specified IP-address */ - if(portptr && *portptr) { - if(*portptr != ':') - return CURLUE_BAD_IPV6; - } - else - portptr = NULL; - } - else - portptr = strchr(hostname, ':'); - - if(portptr) { - char *rest; - long port; - char portbuf[7]; - size_t keep = portptr - hostname; - - /* Browser behavior adaptation. If there's a colon with no digits after, - just cut off the name there which makes us ignore the colon and just - use the default port. Firefox, Chrome and Safari all do that. - - Don't do it if the URL has no scheme, to make something that looks like - a scheme not work! - */ - Curl_dyn_setlen(host, keep); - portptr++; - if(!*portptr) - return has_scheme ? CURLUE_OK : CURLUE_BAD_PORT_NUMBER; - - if(!ISDIGIT(*portptr)) - return CURLUE_BAD_PORT_NUMBER; - - port = strtol(portptr, &rest, 10); /* Port number must be decimal */ - - if(port > 0xffff) - return CURLUE_BAD_PORT_NUMBER; - - if(rest[0]) - return CURLUE_BAD_PORT_NUMBER; - - *rest = 0; - /* generate a new port number string to get rid of leading zeroes etc */ - msnprintf(portbuf, sizeof(portbuf), "%ld", port); - u->portnum = port; - u->port = strdup(portbuf); - if(!u->port) - return CURLUE_OUT_OF_MEMORY; - } - - return CURLUE_OK; -} - -static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, - size_t hlen) /* length of hostname */ -{ - size_t len; - DEBUGASSERT(hostname); - - if(!hostname[0]) - return CURLUE_NO_HOST; - else if(hostname[0] == '[') { - const char *l = "0123456789abcdefABCDEF:."; - if(hlen < 4) /* '[::]' is the shortest possible valid string */ - return CURLUE_BAD_IPV6; - hostname++; - hlen -= 2; - - if(hostname[hlen] != ']') - return CURLUE_BAD_IPV6; - - /* only valid letters are ok */ - len = strspn(hostname, l); - if(hlen != len) { - hlen = len; - if(hostname[len] == '%') { - /* this could now be '%[zone id]' */ - char zoneid[16]; - int i = 0; - char *h = &hostname[len + 1]; - /* pass '25' if present and is a url encoded percent sign */ - if(!strncmp(h, "25", 2) && h[2] && (h[2] != ']')) - h += 2; - while(*h && (*h != ']') && (i < 15)) - zoneid[i++] = *h++; - if(!i || (']' != *h)) - /* impossible to reach? */ - return CURLUE_MALFORMED_INPUT; - zoneid[i] = 0; - u->zoneid = strdup(zoneid); - if(!u->zoneid) - return CURLUE_OUT_OF_MEMORY; - hostname[len] = ']'; /* insert end bracket */ - hostname[len + 1] = 0; /* terminate the hostname */ - } - else - return CURLUE_BAD_IPV6; - /* hostname is fine */ - } -#ifdef ENABLE_IPV6 - { - char dest[16]; /* fits a binary IPv6 address */ - char norm[MAX_IPADR_LEN]; - hostname[hlen] = 0; /* end the address there */ - if(1 != Curl_inet_pton(AF_INET6, hostname, dest)) - return CURLUE_BAD_IPV6; - - /* check if it can be done shorter */ - if(Curl_inet_ntop(AF_INET6, dest, norm, sizeof(norm)) && - (strlen(norm) < hlen)) { - strcpy(hostname, norm); - hlen = strlen(norm); - hostname[hlen + 1] = 0; - } - hostname[hlen] = ']'; /* restore ending bracket */ - } -#endif - } - else { - /* letters from the second string are not ok */ - len = strcspn(hostname, " \r\n\t/:#?!@{}[]\\$\'\"^`*<>=;,"); - if(hlen != len) - /* hostname with bad content */ - return CURLUE_BAD_HOSTNAME; - } - return CURLUE_OK; -} - -#define HOSTNAME_END(x) (((x) == '/') || ((x) == '?') || ((x) == '#')) - -/* - * Handle partial IPv4 numerical addresses and different bases, like - * '16843009', '0x7f', '0x7f.1' '0177.1.1.1' etc. - * - * If the given input string is syntactically wrong or any part for example is - * too big, this function returns FALSE and doesn't create any output. - * - * Output the "normalized" version of that input string in plain quad decimal - * integers and return TRUE. - */ -static bool ipv4_normalize(const char *hostname, char *outp, size_t olen) -{ - bool done = FALSE; - int n = 0; - const char *c = hostname; - unsigned long parts[4] = {0, 0, 0, 0}; - - while(!done) { - char *endp; - unsigned long l; - if((*c < '0') || (*c > '9')) - /* most importantly this doesn't allow a leading plus or minus */ - return FALSE; - l = strtoul(c, &endp, 0); - - /* overflow or nothing parsed at all */ - if(((l == ULONG_MAX) && (errno == ERANGE)) || (endp == c)) - return FALSE; - -#if SIZEOF_LONG > 4 - /* a value larger than 32 bits */ - if(l > UINT_MAX) - return FALSE; -#endif - - parts[n] = l; - c = endp; - - switch (*c) { - case '.' : - if(n == 3) - return FALSE; - n++; - c++; - break; - - case '\0': - done = TRUE; - break; - - default: - return FALSE; - } - } - - /* this is deemed a valid IPv4 numerical address */ - - switch(n) { - case 0: /* a -- 32 bits */ - msnprintf(outp, olen, "%u.%u.%u.%u", - parts[0] >> 24, (parts[0] >> 16) & 0xff, - (parts[0] >> 8) & 0xff, parts[0] & 0xff); - break; - case 1: /* a.b -- 8.24 bits */ - if((parts[0] > 0xff) || (parts[1] > 0xffffff)) - return FALSE; - msnprintf(outp, olen, "%u.%u.%u.%u", - parts[0], (parts[1] >> 16) & 0xff, - (parts[1] >> 8) & 0xff, parts[1] & 0xff); - break; - case 2: /* a.b.c -- 8.8.16 bits */ - if((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xffff)) - return FALSE; - msnprintf(outp, olen, "%u.%u.%u.%u", - parts[0], parts[1], (parts[2] >> 8) & 0xff, - parts[2] & 0xff); - break; - case 3: /* a.b.c.d -- 8.8.8.8 bits */ - if((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff) || - (parts[3] > 0xff)) - return FALSE; - msnprintf(outp, olen, "%u.%u.%u.%u", - parts[0], parts[1], parts[2], parts[3]); - break; - } - return TRUE; -} - -/* if necessary, replace the host content with a URL decoded version */ -static CURLUcode decode_host(struct dynbuf *host) -{ - char *per = NULL; - const char *hostname = Curl_dyn_ptr(host); - if(hostname[0] == '[') - /* only decode if not an ipv6 numerical */ - return CURLUE_OK; - per = strchr(hostname, '%'); - if(!per) - /* nothing to decode */ - return CURLUE_OK; - else { - /* encoded */ - size_t dlen; - char *decoded; - CURLcode result = Curl_urldecode(hostname, 0, &decoded, &dlen, - REJECT_CTRL); - if(result) - return CURLUE_BAD_HOSTNAME; - Curl_dyn_reset(host); - result = Curl_dyn_addn(host, decoded, dlen); - free(decoded); - if(result) - return CURLUE_OUT_OF_MEMORY; - } - - return CURLUE_OK; -} - -/* - * "Remove Dot Segments" - * https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4 - */ - -/* - * dedotdotify() - * @unittest: 1395 - * - * This function gets a null-terminated path with dot and dotdot sequences - * passed in and strips them off according to the rules in RFC 3986 section - * 5.2.4. - * - * The function handles a query part ('?' + stuff) appended but it expects - * that fragments ('#' + stuff) have already been cut off. - * - * RETURNS - * - * an allocated dedotdotified output string - */ -UNITTEST char *dedotdotify(const char *input, size_t clen); -UNITTEST char *dedotdotify(const char *input, size_t clen) -{ - char *out = malloc(clen + 1); - char *outptr; - const char *orginput = input; - char *queryp; - if(!out) - return NULL; /* out of memory */ - - *out = 0; /* null-terminates, for inputs like "./" */ - outptr = out; - - if(!*input) - /* zero length input string, return that */ - return out; - - /* - * To handle query-parts properly, we must find it and remove it during the - * dotdot-operation and then append it again at the end to the output - * string. - */ - queryp = strchr(input, '?'); - - do { - bool dotdot = TRUE; - if(*input == '.') { - /* A. If the input buffer begins with a prefix of "../" or "./", then - remove that prefix from the input buffer; otherwise, */ - - if(!strncmp("./", input, 2)) { - input += 2; - clen -= 2; - } - else if(!strncmp("../", input, 3)) { - input += 3; - clen -= 3; - } - /* D. if the input buffer consists only of "." or "..", then remove - that from the input buffer; otherwise, */ - - else if(!strcmp(".", input) || !strcmp("..", input) || - !strncmp(".?", input, 2) || !strncmp("..?", input, 3)) { - *out = 0; - break; - } - else - dotdot = FALSE; - } - else if(*input == '/') { - /* B. if the input buffer begins with a prefix of "/./" or "/.", where - "." is a complete path segment, then replace that prefix with "/" in - the input buffer; otherwise, */ - if(!strncmp("/./", input, 3)) { - input += 2; - clen -= 2; - } - else if(!strcmp("/.", input) || !strncmp("/.?", input, 3)) { - *outptr++ = '/'; - *outptr = 0; - break; - } - - /* C. if the input buffer begins with a prefix of "/../" or "/..", - where ".." is a complete path segment, then replace that prefix with - "/" in the input buffer and remove the last segment and its - preceding "/" (if any) from the output buffer; otherwise, */ - - else if(!strncmp("/../", input, 4)) { - input += 3; - clen -= 3; - /* remove the last segment from the output buffer */ - while(outptr > out) { - outptr--; - if(*outptr == '/') - break; - } - *outptr = 0; /* null-terminate where it stops */ - } - else if(!strcmp("/..", input) || !strncmp("/..?", input, 4)) { - /* remove the last segment from the output buffer */ - while(outptr > out) { - outptr--; - if(*outptr == '/') - break; - } - *outptr++ = '/'; - *outptr = 0; /* null-terminate where it stops */ - break; - } - else - dotdot = FALSE; - } - else - dotdot = FALSE; - - if(!dotdot) { - /* E. move the first path segment in the input buffer to the end of - the output buffer, including the initial "/" character (if any) and - any subsequent characters up to, but not including, the next "/" - character or the end of the input buffer. */ - - do { - *outptr++ = *input++; - clen--; - } while(*input && (*input != '/') && (*input != '?')); - *outptr = 0; - } - - /* continue until end of input string OR, if there is a terminating - query part, stop there */ - } while(*input && (!queryp || (input < queryp))); - - if(queryp) { - size_t qlen; - /* There was a query part, append that to the output. */ - size_t oindex = queryp - orginput; - qlen = strlen(&orginput[oindex]); - memcpy(outptr, &orginput[oindex], qlen + 1); /* include zero byte */ - } - - return out; -} - -static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) -{ - const char *path; - size_t pathlen; - bool uncpath = FALSE; - char *query = NULL; - char *fragment = NULL; - char schemebuf[MAX_SCHEME_LEN + 1]; - const char *schemep = NULL; - size_t schemelen = 0; - size_t urllen; - CURLUcode result = CURLUE_OK; - size_t fraglen = 0; - struct dynbuf host; - - DEBUGASSERT(url); - - Curl_dyn_init(&host, CURL_MAX_INPUT_LENGTH); - - /************************************************************* - * Parse the URL. - ************************************************************/ - /* allocate scratch area */ - urllen = strlen(url); - if(urllen > CURL_MAX_INPUT_LENGTH) { - /* excessive input length */ - result = CURLUE_MALFORMED_INPUT; - goto fail; - } - - schemelen = Curl_is_absolute_url(url, schemebuf, sizeof(schemebuf), - flags & (CURLU_GUESS_SCHEME| - CURLU_DEFAULT_SCHEME)); - - /* handle the file: scheme */ - if(schemelen && !strcmp(schemebuf, "file")) { - if(urllen <= 6) { - /* file:/ is not enough to actually be a complete file: URL */ - result = CURLUE_BAD_FILE_URL; - goto fail; - } - - /* path has been allocated large enough to hold this */ - path = (char *)&url[5]; - - schemep = u->scheme = strdup("file"); - if(!u->scheme) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - - /* Extra handling URLs with an authority component (i.e. that start with - * "file://") - * - * We allow omitted hostname (e.g. file:/<path>) -- valid according to - * RFC 8089, but not the (current) WHAT-WG URL spec. - */ - if(path[0] == '/' && path[1] == '/') { - /* swallow the two slashes */ - const char *ptr = &path[2]; - - /* - * According to RFC 8089, a file: URL can be reliably dereferenced if: - * - * o it has no/blank hostname, or - * - * o the hostname matches "localhost" (case-insensitively), or - * - * o the hostname is a FQDN that resolves to this machine, or - * - * o it is an UNC String transformed to an URI (Windows only, RFC 8089 - * Appendix E.3). - * - * For brevity, we only consider URLs with empty, "localhost", or - * "127.0.0.1" hostnames as local, otherwise as an UNC String. - * - * Additionally, there is an exception for URLs with a Windows drive - * letter in the authority (which was accidentally omitted from RFC 8089 - * Appendix E, but believe me, it was meant to be there. --MK) - */ - if(ptr[0] != '/' && !STARTS_WITH_URL_DRIVE_PREFIX(ptr)) { - /* the URL includes a host name, it must match "localhost" or - "127.0.0.1" to be valid */ - if(checkprefix("localhost/", ptr) || - checkprefix("127.0.0.1/", ptr)) { - ptr += 9; /* now points to the slash after the host */ - } - else { -#if defined(WIN32) - size_t len; - - /* the host name, NetBIOS computer name, can not contain disallowed - chars, and the delimiting slash character must be appended to the - host name */ - path = strpbrk(ptr, "/\\:*?\"<>|"); - if(!path || *path != '/') { - result = CURLUE_BAD_FILE_URL; - goto fail; - } - - len = path - ptr; - if(len) { - if(Curl_dyn_addn(&host, ptr, len)) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - uncpath = TRUE; - } - - ptr -= 2; /* now points to the // before the host in UNC */ -#else - /* Invalid file://hostname/, expected localhost or 127.0.0.1 or - none */ - result = CURLUE_BAD_FILE_URL; - goto fail; -#endif - } - } - - path = ptr; - } - - if(!uncpath) - /* no host for file: URLs by default */ - Curl_dyn_reset(&host); - -#if !defined(MSDOS) && !defined(WIN32) && !defined(__CYGWIN__) - /* Don't allow Windows drive letters when not in Windows. - * This catches both "file:/c:" and "file:c:" */ - if(('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) || - STARTS_WITH_URL_DRIVE_PREFIX(path)) { - /* File drive letters are only accepted in MSDOS/Windows */ - result = CURLUE_BAD_FILE_URL; - goto fail; - } -#else - /* If the path starts with a slash and a drive letter, ditch the slash */ - if('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) { - /* This cannot be done with strcpy, as the memory chunks overlap! */ - path++; - } -#endif - - } - else { - /* clear path */ - const char *p; - const char *hostp; - size_t len; - - if(schemelen) { - int i = 0; - p = &url[schemelen + 1]; - while(p && (*p == '/') && (i < 4)) { - p++; - i++; - } - - schemep = schemebuf; - if(!Curl_builtin_scheme(schemep, CURL_ZERO_TERMINATED) && - !(flags & CURLU_NON_SUPPORT_SCHEME)) { - result = CURLUE_UNSUPPORTED_SCHEME; - goto fail; - } - - if((i < 1) || (i>3)) { - /* less than one or more than three slashes */ - result = CURLUE_BAD_SLASHES; - goto fail; - } - if(junkscan(schemep, flags)) { - result = CURLUE_BAD_SCHEME; - goto fail; - } - } - else { - /* no scheme! */ - - if(!(flags & (CURLU_DEFAULT_SCHEME|CURLU_GUESS_SCHEME))) { - result = CURLUE_BAD_SCHEME; - goto fail; - } - if(flags & CURLU_DEFAULT_SCHEME) - schemep = DEFAULT_SCHEME; - - /* - * The URL was badly formatted, let's try without scheme specified. - */ - p = url; - } - hostp = p; /* host name starts here */ - - /* find the end of the host name + port number */ - while(*p && !HOSTNAME_END(*p)) - p++; - - len = p - hostp; - if(len) { - if(Curl_dyn_addn(&host, hostp, len)) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - } - else { - if(!(flags & CURLU_NO_AUTHORITY)) { - result = CURLUE_NO_HOST; - goto fail; - } - } - - path = (char *)p; - - if(schemep) { - u->scheme = strdup(schemep); - if(!u->scheme) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - } - } - - fragment = strchr(path, '#'); - if(fragment) { - fraglen = strlen(fragment); - if(fraglen > 1) { - /* skip the leading '#' in the copy but include the terminating null */ - u->fragment = Curl_memdup(fragment + 1, fraglen); - if(!u->fragment) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - - if(junkscan(u->fragment, flags)) { - result = CURLUE_BAD_FRAGMENT; - goto fail; - } - } - } - - query = strchr(path, '?'); - if(query && (!fragment || (query < fragment))) { - size_t qlen = strlen(query) - fraglen; /* includes '?' */ - pathlen = strlen(path) - qlen - fraglen; - if(qlen > 1) { - if(qlen && (flags & CURLU_URLENCODE)) { - struct dynbuf enc; - Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - /* skip the leading question mark */ - if(urlencode_str(&enc, query + 1, qlen - 1, TRUE, TRUE)) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - u->query = Curl_dyn_ptr(&enc); - } - else { - u->query = Curl_memdup(query + 1, qlen); - if(!u->query) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - u->query[qlen - 1] = 0; - } - - if(junkscan(u->query, flags)) { - result = CURLUE_BAD_QUERY; - goto fail; - } - } - else { - /* single byte query */ - u->query = strdup(""); - if(!u->query) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - } - } - else - pathlen = strlen(path) - fraglen; - - if(pathlen && (flags & CURLU_URLENCODE)) { - struct dynbuf enc; - Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - if(urlencode_str(&enc, path, pathlen, TRUE, FALSE)) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - pathlen = Curl_dyn_len(&enc); - path = u->path = Curl_dyn_ptr(&enc); - } - - if(!pathlen) { - /* there is no path left, unset */ - path = NULL; - } - else { - if(!u->path) { - u->path = Curl_memdup(path, pathlen + 1); - if(!u->path) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - u->path[pathlen] = 0; - path = u->path; - } - else if(flags & CURLU_URLENCODE) - /* it might have encoded more than just the path so cut it */ - u->path[pathlen] = 0; - - if(junkscan(u->path, flags)) { - result = CURLUE_BAD_PATH; - goto fail; - } - - if(!(flags & CURLU_PATH_AS_IS)) { - /* remove ../ and ./ sequences according to RFC3986 */ - char *newp = dedotdotify((char *)path, pathlen); - if(!newp) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - free(u->path); - u->path = newp; - } - } - - if(Curl_dyn_len(&host)) { - char normalized_ipv4[sizeof("255.255.255.255") + 1]; - - /* - * Parse the login details and strip them out of the host name. - */ - result = parse_hostname_login(u, &host, flags); - if(!result) - result = Curl_parse_port(u, &host, schemelen); - if(result) - goto fail; - - if(junkscan(Curl_dyn_ptr(&host), flags)) { - result = CURLUE_BAD_HOSTNAME; - goto fail; - } - - if(ipv4_normalize(Curl_dyn_ptr(&host), - normalized_ipv4, sizeof(normalized_ipv4))) { - Curl_dyn_reset(&host); - if(Curl_dyn_add(&host, normalized_ipv4)) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - } - else { - result = decode_host(&host); - if(!result) - result = hostname_check(u, Curl_dyn_ptr(&host), Curl_dyn_len(&host)); - if(result) - goto fail; - } - - if((flags & CURLU_GUESS_SCHEME) && !schemep) { - const char *hostname = Curl_dyn_ptr(&host); - /* legacy curl-style guess based on host name */ - if(checkprefix("ftp.", hostname)) - schemep = "ftp"; - else if(checkprefix("dict.", hostname)) - schemep = "dict"; - else if(checkprefix("ldap.", hostname)) - schemep = "ldap"; - else if(checkprefix("imap.", hostname)) - schemep = "imap"; - else if(checkprefix("smtp.", hostname)) - schemep = "smtp"; - else if(checkprefix("pop3.", hostname)) - schemep = "pop3"; - else - schemep = "http"; - - u->scheme = strdup(schemep); - if(!u->scheme) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - } - } - else if(flags & CURLU_NO_AUTHORITY) { - /* allowed to be empty. */ - if(Curl_dyn_add(&host, "")) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - } - - u->host = Curl_dyn_ptr(&host); - - return result; - fail: - Curl_dyn_free(&host); - free_urlhandle(u); - return result; -} - -/* - * Parse the URL and, if successful, replace everything in the Curl_URL struct. - */ -static CURLUcode parseurl_and_replace(const char *url, CURLU *u, - unsigned int flags) -{ - CURLUcode result; - CURLU tmpurl; - memset(&tmpurl, 0, sizeof(tmpurl)); - result = parseurl(url, &tmpurl, flags); - if(!result) { - free_urlhandle(u); - *u = tmpurl; - } - return result; -} - -/* - */ -CURLU *curl_url(void) -{ - return calloc(sizeof(struct Curl_URL), 1); -} - -void curl_url_cleanup(CURLU *u) -{ - if(u) { - free_urlhandle(u); - free(u); - } -} - -#define DUP(dest, src, name) \ - do { \ - if(src->name) { \ - dest->name = strdup(src->name); \ - if(!dest->name) \ - goto fail; \ - } \ - } while(0) - -CURLU *curl_url_dup(CURLU *in) -{ - struct Curl_URL *u = calloc(sizeof(struct Curl_URL), 1); - if(u) { - DUP(u, in, scheme); - DUP(u, in, user); - DUP(u, in, password); - DUP(u, in, options); - DUP(u, in, host); - DUP(u, in, port); - DUP(u, in, path); - DUP(u, in, query); - DUP(u, in, fragment); - u->portnum = in->portnum; - } - return u; - fail: - curl_url_cleanup(u); - return NULL; -} - -CURLUcode curl_url_get(CURLU *u, CURLUPart what, - char **part, unsigned int flags) -{ - char *ptr; - CURLUcode ifmissing = CURLUE_UNKNOWN_PART; - char portbuf[7]; - bool urldecode = (flags & CURLU_URLDECODE)?1:0; - bool urlencode = (flags & CURLU_URLENCODE)?1:0; - bool plusdecode = FALSE; - (void)flags; - if(!u) - return CURLUE_BAD_HANDLE; - if(!part) - return CURLUE_BAD_PARTPOINTER; - *part = NULL; - - switch(what) { - case CURLUPART_SCHEME: - ptr = u->scheme; - ifmissing = CURLUE_NO_SCHEME; - urldecode = FALSE; /* never for schemes */ - break; - case CURLUPART_USER: - ptr = u->user; - ifmissing = CURLUE_NO_USER; - break; - case CURLUPART_PASSWORD: - ptr = u->password; - ifmissing = CURLUE_NO_PASSWORD; - break; - case CURLUPART_OPTIONS: - ptr = u->options; - ifmissing = CURLUE_NO_OPTIONS; - break; - case CURLUPART_HOST: - ptr = u->host; - ifmissing = CURLUE_NO_HOST; - break; - case CURLUPART_ZONEID: - ptr = u->zoneid; - ifmissing = CURLUE_NO_ZONEID; - break; - case CURLUPART_PORT: - ptr = u->port; - ifmissing = CURLUE_NO_PORT; - urldecode = FALSE; /* never for port */ - if(!ptr && (flags & CURLU_DEFAULT_PORT) && u->scheme) { - /* there's no stored port number, but asked to deliver - a default one for the scheme */ - const struct Curl_handler *h = - Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED); - if(h) { - msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); - ptr = portbuf; - } - } - else if(ptr && u->scheme) { - /* there is a stored port number, but ask to inhibit if - it matches the default one for the scheme */ - const struct Curl_handler *h = - Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED); - if(h && (h->defport == u->portnum) && - (flags & CURLU_NO_DEFAULT_PORT)) - ptr = NULL; - } - break; - case CURLUPART_PATH: - ptr = u->path; - if(!ptr) { - ptr = u->path = strdup("/"); - if(!u->path) - return CURLUE_OUT_OF_MEMORY; - } - break; - case CURLUPART_QUERY: - ptr = u->query; - ifmissing = CURLUE_NO_QUERY; - plusdecode = urldecode; - break; - case CURLUPART_FRAGMENT: - ptr = u->fragment; - ifmissing = CURLUE_NO_FRAGMENT; - break; - case CURLUPART_URL: { - char *url; - char *scheme; - char *options = u->options; - char *port = u->port; - char *allochost = NULL; - if(u->scheme && strcasecompare("file", u->scheme)) { - url = aprintf("file://%s%s%s", - u->path, - u->fragment? "#": "", - u->fragment? u->fragment : ""); - } - else if(!u->host) - return CURLUE_NO_HOST; - else { - const struct Curl_handler *h = NULL; - if(u->scheme) - scheme = u->scheme; - else if(flags & CURLU_DEFAULT_SCHEME) - scheme = (char *) DEFAULT_SCHEME; - else - return CURLUE_NO_SCHEME; - - h = Curl_builtin_scheme(scheme, CURL_ZERO_TERMINATED); - if(!port && (flags & CURLU_DEFAULT_PORT)) { - /* there's no stored port number, but asked to deliver - a default one for the scheme */ - if(h) { - msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); - port = portbuf; - } - } - else if(port) { - /* there is a stored port number, but asked to inhibit if it matches - the default one for the scheme */ - if(h && (h->defport == u->portnum) && - (flags & CURLU_NO_DEFAULT_PORT)) - port = NULL; - } - - if(h && !(h->flags & PROTOPT_URLOPTIONS)) - options = NULL; - - if(u->host[0] == '[') { - if(u->zoneid) { - /* make it '[ host %25 zoneid ]' */ - struct dynbuf enc; - size_t hostlen = strlen(u->host); - Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - if(Curl_dyn_addf(&enc, "%.*s%%25%s]", (int)hostlen - 1, u->host, - u->zoneid)) - return CURLUE_OUT_OF_MEMORY; - allochost = Curl_dyn_ptr(&enc); - } - } - else if(urlencode) { - allochost = curl_easy_escape(NULL, u->host, 0); - if(!allochost) - return CURLUE_OUT_OF_MEMORY; - } - else { - /* only encode '%' in output host name */ - char *host = u->host; - bool percent = FALSE; - /* first, count number of percents present in the name */ - while(*host) { - if(*host == '%') { - percent = TRUE; - break; - } - host++; - } - /* if there were percent(s), encode the host name */ - if(percent) { - struct dynbuf enc; - CURLcode result; - Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - host = u->host; - while(*host) { - if(*host == '%') - result = Curl_dyn_addn(&enc, "%25", 3); - else - result = Curl_dyn_addn(&enc, host, 1); - if(result) - return CURLUE_OUT_OF_MEMORY; - host++; - } - free(u->host); - u->host = Curl_dyn_ptr(&enc); - } - } - - url = aprintf("%s://%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", - scheme, - u->user ? u->user : "", - u->password ? ":": "", - u->password ? u->password : "", - options ? ";" : "", - options ? options : "", - (u->user || u->password || options) ? "@": "", - allochost ? allochost : u->host, - port ? ":": "", - port ? port : "", - (u->path && (u->path[0] != '/')) ? "/": "", - u->path ? u->path : "/", - (u->query && u->query[0]) ? "?": "", - (u->query && u->query[0]) ? u->query : "", - u->fragment? "#": "", - u->fragment? u->fragment : ""); - free(allochost); - } - if(!url) - return CURLUE_OUT_OF_MEMORY; - *part = url; - return CURLUE_OK; - } - default: - ptr = NULL; - break; - } - if(ptr) { - size_t partlen = strlen(ptr); - size_t i = 0; - *part = Curl_memdup(ptr, partlen + 1); - if(!*part) - return CURLUE_OUT_OF_MEMORY; - if(plusdecode) { - /* convert + to space */ - char *plus = *part; - for(i = 0; i < partlen; ++plus, i++) { - if(*plus == '+') - *plus = ' '; - } - } - if(urldecode) { - char *decoded; - size_t dlen; - /* this unconditional rejection of control bytes is documented - API behavior */ - CURLcode res = Curl_urldecode(*part, 0, &decoded, &dlen, REJECT_CTRL); - free(*part); - if(res) { - *part = NULL; - return CURLUE_URLDECODE; - } - *part = decoded; - partlen = dlen; - } - if(urlencode) { - struct dynbuf enc; - Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - if(urlencode_str(&enc, *part, partlen, TRUE, - what == CURLUPART_QUERY)) - return CURLUE_OUT_OF_MEMORY; - free(*part); - *part = Curl_dyn_ptr(&enc); - } - - return CURLUE_OK; - } - else - return ifmissing; -} - -CURLUcode curl_url_set(CURLU *u, CURLUPart what, - const char *part, unsigned int flags) -{ - char **storep = NULL; - long port = 0; - bool urlencode = (flags & CURLU_URLENCODE)? 1 : 0; - bool plusencode = FALSE; - bool urlskipslash = FALSE; - bool appendquery = FALSE; - bool equalsencode = FALSE; - - if(!u) - return CURLUE_BAD_HANDLE; - if(!part) { - /* setting a part to NULL clears it */ - switch(what) { - case CURLUPART_URL: - break; - case CURLUPART_SCHEME: - storep = &u->scheme; - break; - case CURLUPART_USER: - storep = &u->user; - break; - case CURLUPART_PASSWORD: - storep = &u->password; - break; - case CURLUPART_OPTIONS: - storep = &u->options; - break; - case CURLUPART_HOST: - storep = &u->host; - break; - case CURLUPART_ZONEID: - storep = &u->zoneid; - break; - case CURLUPART_PORT: - u->portnum = 0; - storep = &u->port; - break; - case CURLUPART_PATH: - storep = &u->path; - break; - case CURLUPART_QUERY: - storep = &u->query; - break; - case CURLUPART_FRAGMENT: - storep = &u->fragment; - break; - default: - return CURLUE_UNKNOWN_PART; - } - if(storep && *storep) { - Curl_safefree(*storep); - } - else if(!storep) { - free_urlhandle(u); - memset(u, 0, sizeof(struct Curl_URL)); - } - return CURLUE_OK; - } - - switch(what) { - case CURLUPART_SCHEME: - if(strlen(part) > MAX_SCHEME_LEN) - /* too long */ - return CURLUE_BAD_SCHEME; - if(!(flags & CURLU_NON_SUPPORT_SCHEME) && - /* verify that it is a fine scheme */ - !Curl_builtin_scheme(part, CURL_ZERO_TERMINATED)) - return CURLUE_UNSUPPORTED_SCHEME; - storep = &u->scheme; - urlencode = FALSE; /* never */ - break; - case CURLUPART_USER: - storep = &u->user; - break; - case CURLUPART_PASSWORD: - storep = &u->password; - break; - case CURLUPART_OPTIONS: - storep = &u->options; - break; - case CURLUPART_HOST: { - size_t len = strcspn(part, " \r\n"); - if(strlen(part) != len) - /* hostname with bad content */ - return CURLUE_BAD_HOSTNAME; - storep = &u->host; - Curl_safefree(u->zoneid); - break; - } - case CURLUPART_ZONEID: - storep = &u->zoneid; - break; - case CURLUPART_PORT: - { - char *endp; - urlencode = FALSE; /* never */ - port = strtol(part, &endp, 10); /* Port number must be decimal */ - if((port <= 0) || (port > 0xffff)) - return CURLUE_BAD_PORT_NUMBER; - if(*endp) - /* weirdly provided number, not good! */ - return CURLUE_BAD_PORT_NUMBER; - storep = &u->port; - } - break; - case CURLUPART_PATH: - urlskipslash = TRUE; - storep = &u->path; - break; - case CURLUPART_QUERY: - plusencode = urlencode; - appendquery = (flags & CURLU_APPENDQUERY)?1:0; - equalsencode = appendquery; - storep = &u->query; - break; - case CURLUPART_FRAGMENT: - storep = &u->fragment; - break; - case CURLUPART_URL: { - /* - * Allow a new URL to replace the existing (if any) contents. - * - * If the existing contents is enough for a URL, allow a relative URL to - * replace it. - */ - CURLUcode result; - char *oldurl; - char *redired_url; - - /* if the new thing is absolute or the old one is not - * (we could not get an absolute url in 'oldurl'), - * then replace the existing with the new. */ - if(Curl_is_absolute_url(part, NULL, 0, - flags & (CURLU_GUESS_SCHEME| - CURLU_DEFAULT_SCHEME)) - || curl_url_get(u, CURLUPART_URL, &oldurl, flags)) { - return parseurl_and_replace(part, u, flags); - } - - /* apply the relative part to create a new URL - * and replace the existing one with it. */ - redired_url = concat_url(oldurl, part); - free(oldurl); - if(!redired_url) - return CURLUE_OUT_OF_MEMORY; - - result = parseurl_and_replace(redired_url, u, flags); - free(redired_url); - return result; - } - default: - return CURLUE_UNKNOWN_PART; - } - DEBUGASSERT(storep); - { - const char *newp = part; - size_t nalloc = strlen(part); - - if(nalloc > CURL_MAX_INPUT_LENGTH) - /* excessive input length */ - return CURLUE_MALFORMED_INPUT; - - if(urlencode) { - const unsigned char *i; - struct dynbuf enc; - - Curl_dyn_init(&enc, nalloc * 3 + 1); - - for(i = (const unsigned char *)part; *i; i++) { - CURLcode result; - if((*i == ' ') && plusencode) { - result = Curl_dyn_addn(&enc, "+", 1); - if(result) - return CURLUE_OUT_OF_MEMORY; - } - else if(Curl_isunreserved(*i) || - ((*i == '/') && urlskipslash) || - ((*i == '=') && equalsencode)) { - if((*i == '=') && equalsencode) - /* only skip the first equals sign */ - equalsencode = FALSE; - result = Curl_dyn_addn(&enc, i, 1); - if(result) - return CURLUE_OUT_OF_MEMORY; - } - else { - result = Curl_dyn_addf(&enc, "%%%02x", *i); - if(result) - return CURLUE_OUT_OF_MEMORY; - } - } - newp = Curl_dyn_ptr(&enc); - } - else { - char *p; - newp = strdup(part); - if(!newp) - return CURLUE_OUT_OF_MEMORY; - p = (char *)newp; - while(*p) { - /* make sure percent encoded are lower case */ - if((*p == '%') && ISXDIGIT(p[1]) && ISXDIGIT(p[2]) && - (ISUPPER(p[1]) || ISUPPER(p[2]))) { - p[1] = Curl_raw_tolower(p[1]); - p[2] = Curl_raw_tolower(p[2]); - p += 3; - } - else - p++; - } - } - - if(appendquery) { - /* Append the 'newp' string onto the old query. Add a '&' separator if - none is present at the end of the existing query already */ - - size_t querylen = u->query ? strlen(u->query) : 0; - bool addamperand = querylen && (u->query[querylen -1] != '&'); - if(querylen) { - struct dynbuf enc; - Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - - if(Curl_dyn_addn(&enc, u->query, querylen)) /* add original query */ - goto nomem; - - if(addamperand) { - if(Curl_dyn_addn(&enc, "&", 1)) - goto nomem; - } - if(Curl_dyn_add(&enc, newp)) - goto nomem; - free((char *)newp); - free(*storep); - *storep = Curl_dyn_ptr(&enc); - return CURLUE_OK; - nomem: - free((char *)newp); - return CURLUE_OUT_OF_MEMORY; - } - } - - if(what == CURLUPART_HOST) { - size_t n = strlen(newp); - if(!n && (flags & CURLU_NO_AUTHORITY)) { - /* Skip hostname check, it's allowed to be empty. */ - } - else { - if(hostname_check(u, (char *)newp, n)) { - free((char *)newp); - return CURLUE_BAD_HOSTNAME; - } - } - } - - free(*storep); - *storep = (char *)newp; - } - /* set after the string, to make it not assigned if the allocation above - fails */ - if(port) - u->portnum = port; - return CURLUE_OK; -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "urlapi-int.h"
+#include "strcase.h"
+#include "url.h"
+#include "escape.h"
+#include "curl_ctype.h"
+#include "inet_pton.h"
+#include "inet_ntop.h"
+#include "strdup.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+ /* MSDOS/Windows style drive prefix, eg c: in c:foo */
+#define STARTS_WITH_DRIVE_PREFIX(str) \
+ ((('a' <= str[0] && str[0] <= 'z') || \
+ ('A' <= str[0] && str[0] <= 'Z')) && \
+ (str[1] == ':'))
+
+ /* MSDOS/Windows style drive prefix, optionally with
+ * a '|' instead of ':', followed by a slash or NUL */
+#define STARTS_WITH_URL_DRIVE_PREFIX(str) \
+ ((('a' <= (str)[0] && (str)[0] <= 'z') || \
+ ('A' <= (str)[0] && (str)[0] <= 'Z')) && \
+ ((str)[1] == ':' || (str)[1] == '|') && \
+ ((str)[2] == '/' || (str)[2] == '\\' || (str)[2] == 0))
+
+/* scheme is not URL encoded, the longest libcurl supported ones are... */
+#define MAX_SCHEME_LEN 40
+
+/* Internal representation of CURLU. Point to URL-encoded strings. */
+struct Curl_URL {
+ char *scheme;
+ char *user;
+ char *password;
+ char *options; /* IMAP only? */
+ char *host;
+ char *zoneid; /* for numerical IPv6 addresses */
+ char *port;
+ char *path;
+ char *query;
+ char *fragment;
+ long portnum; /* the numerical version */
+};
+
+#define DEFAULT_SCHEME "https"
+
+static void free_urlhandle(struct Curl_URL *u)
+{
+ free(u->scheme);
+ free(u->user);
+ free(u->password);
+ free(u->options);
+ free(u->host);
+ free(u->zoneid);
+ free(u->port);
+ free(u->path);
+ free(u->query);
+ free(u->fragment);
+}
+
+/*
+ * Find the separator at the end of the host name, or the '?' in cases like
+ * http://www.url.com?id=2380
+ */
+static const char *find_host_sep(const char *url)
+{
+ const char *sep;
+ const char *query;
+
+ /* Find the start of the hostname */
+ sep = strstr(url, "//");
+ if(!sep)
+ sep = url;
+ else
+ sep += 2;
+
+ query = strchr(sep, '?');
+ sep = strchr(sep, '/');
+
+ if(!sep)
+ sep = url + strlen(url);
+
+ if(!query)
+ query = url + strlen(url);
+
+ return sep < query ? sep : query;
+}
+
+/*
+ * Decide in an encoding-independent manner whether a character in a URL must
+ * be escaped. This is used in urlencode_str().
+ */
+static bool urlchar_needs_escaping(int c)
+{
+ return !(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c));
+}
+
+/* urlencode_str() writes data into an output dynbuf and URL-encodes the
+ * spaces in the source URL accordingly.
+ *
+ * URL encoding should be skipped for host names, otherwise IDN resolution
+ * will fail.
+ */
+static CURLUcode urlencode_str(struct dynbuf *o, const char *url,
+ size_t len, bool relative,
+ bool query)
+{
+ /* we must add this with whitespace-replacing */
+ bool left = !query;
+ const unsigned char *iptr;
+ const unsigned char *host_sep = (const unsigned char *) url;
+
+ if(!relative)
+ host_sep = (const unsigned char *) find_host_sep(url);
+
+ for(iptr = (unsigned char *)url; /* read from here */
+ len; iptr++, len--) {
+
+ if(iptr < host_sep) {
+ if(Curl_dyn_addn(o, iptr, 1))
+ return CURLUE_OUT_OF_MEMORY;
+ continue;
+ }
+
+ if(*iptr == ' ') {
+ if(left) {
+ if(Curl_dyn_addn(o, "%20", 3))
+ return CURLUE_OUT_OF_MEMORY;
+ }
+ else {
+ if(Curl_dyn_addn(o, "+", 1))
+ return CURLUE_OUT_OF_MEMORY;
+ }
+ continue;
+ }
+
+ if(*iptr == '?')
+ left = FALSE;
+
+ if(urlchar_needs_escaping(*iptr)) {
+ if(Curl_dyn_addf(o, "%%%02x", *iptr))
+ return CURLUE_OUT_OF_MEMORY;
+ }
+ else {
+ if(Curl_dyn_addn(o, iptr, 1))
+ return CURLUE_OUT_OF_MEMORY;
+ }
+ }
+
+ return CURLUE_OK;
+}
+
+/*
+ * Returns the length of the scheme if the given URL is absolute (as opposed
+ * to relative). Stores the scheme in the buffer if TRUE and 'buf' is
+ * non-NULL. The buflen must be larger than MAX_SCHEME_LEN if buf is set.
+ *
+ * If 'guess_scheme' is TRUE, it means the URL might be provided without
+ * scheme.
+ */
+size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen,
+ bool guess_scheme)
+{
+ int i;
+ DEBUGASSERT(!buf || (buflen > MAX_SCHEME_LEN));
+ (void)buflen; /* only used in debug-builds */
+ if(buf)
+ buf[0] = 0; /* always leave a defined value in buf */
+#ifdef WIN32
+ if(guess_scheme && STARTS_WITH_DRIVE_PREFIX(url))
+ return 0;
+#endif
+ for(i = 0; i < MAX_SCHEME_LEN; ++i) {
+ char s = url[i];
+ if(s && (ISALNUM(s) || (s == '+') || (s == '-') || (s == '.') )) {
+ /* RFC 3986 3.1 explains:
+ scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+ */
+ }
+ else {
+ break;
+ }
+ }
+ if(i && (url[i] == ':') && ((url[i + 1] == '/') || !guess_scheme)) {
+ /* If this does not guess scheme, the scheme always ends with the colon so
+ that this also detects data: URLs etc. In guessing mode, data: could
+ be the host name "data" with a specified port number. */
+
+ /* the length of the scheme is the name part only */
+ size_t len = i;
+ if(buf) {
+ buf[i] = 0;
+ while(i--) {
+ buf[i] = Curl_raw_tolower(url[i]);
+ }
+ }
+ return len;
+ }
+ return 0;
+}
+
+/*
+ * Concatenate a relative URL to a base URL making it absolute.
+ * URL-encodes any spaces.
+ * The returned pointer must be freed by the caller unless NULL
+ * (returns NULL on out of memory).
+ *
+ * Note that this function destroys the 'base' string.
+ */
+static char *concat_url(char *base, const char *relurl)
+{
+ /***
+ TRY to append this new path to the old URL
+ to the right of the host part. Oh crap, this is doomed to cause
+ problems in the future...
+ */
+ struct dynbuf newest;
+ char *protsep;
+ char *pathsep;
+ bool host_changed = FALSE;
+ const char *useurl = relurl;
+
+ /* protsep points to the start of the host name */
+ protsep = strstr(base, "//");
+ if(!protsep)
+ protsep = base;
+ else
+ protsep += 2; /* pass the slashes */
+
+ if('/' != relurl[0]) {
+ int level = 0;
+
+ /* First we need to find out if there's a ?-letter in the URL,
+ and cut it and the right-side of that off */
+ pathsep = strchr(protsep, '?');
+ if(pathsep)
+ *pathsep = 0;
+
+ /* we have a relative path to append to the last slash if there's one
+ available, or if the new URL is just a query string (starts with a
+ '?') we append the new one at the end of the entire currently worked
+ out URL */
+ if(useurl[0] != '?') {
+ pathsep = strrchr(protsep, '/');
+ if(pathsep)
+ *pathsep = 0;
+ }
+
+ /* Check if there's any slash after the host name, and if so, remember
+ that position instead */
+ pathsep = strchr(protsep, '/');
+ if(pathsep)
+ protsep = pathsep + 1;
+ else
+ protsep = NULL;
+
+ /* now deal with one "./" or any amount of "../" in the newurl
+ and act accordingly */
+
+ if((useurl[0] == '.') && (useurl[1] == '/'))
+ useurl += 2; /* just skip the "./" */
+
+ while((useurl[0] == '.') &&
+ (useurl[1] == '.') &&
+ (useurl[2] == '/')) {
+ level++;
+ useurl += 3; /* pass the "../" */
+ }
+
+ if(protsep) {
+ while(level--) {
+ /* cut off one more level from the right of the original URL */
+ pathsep = strrchr(protsep, '/');
+ if(pathsep)
+ *pathsep = 0;
+ else {
+ *protsep = 0;
+ break;
+ }
+ }
+ }
+ }
+ else {
+ /* We got a new absolute path for this server */
+
+ if(relurl[1] == '/') {
+ /* the new URL starts with //, just keep the protocol part from the
+ original one */
+ *protsep = 0;
+ useurl = &relurl[2]; /* we keep the slashes from the original, so we
+ skip the new ones */
+ host_changed = TRUE;
+ }
+ else {
+ /* cut off the original URL from the first slash, or deal with URLs
+ without slash */
+ pathsep = strchr(protsep, '/');
+ if(pathsep) {
+ /* When people use badly formatted URLs, such as
+ "http://www.url.com?dir=/home/daniel" we must not use the first
+ slash, if there's a ?-letter before it! */
+ char *sep = strchr(protsep, '?');
+ if(sep && (sep < pathsep))
+ pathsep = sep;
+ *pathsep = 0;
+ }
+ else {
+ /* There was no slash. Now, since we might be operating on a badly
+ formatted URL, such as "http://www.url.com?id=2380" which doesn't
+ use a slash separator as it is supposed to, we need to check for a
+ ?-letter as well! */
+ pathsep = strchr(protsep, '?');
+ if(pathsep)
+ *pathsep = 0;
+ }
+ }
+ }
+
+ Curl_dyn_init(&newest, CURL_MAX_INPUT_LENGTH);
+
+ /* copy over the root url part */
+ if(Curl_dyn_add(&newest, base))
+ return NULL;
+
+ /* check if we need to append a slash */
+ if(('/' == useurl[0]) || (protsep && !*protsep) || ('?' == useurl[0]))
+ ;
+ else {
+ if(Curl_dyn_addn(&newest, "/", 1))
+ return NULL;
+ }
+
+ /* then append the new piece on the right side */
+ urlencode_str(&newest, useurl, strlen(useurl), !host_changed, FALSE);
+
+ return Curl_dyn_ptr(&newest);
+}
+
+/* scan for byte values < 31 or 127 */
+static bool junkscan(const char *part, unsigned int flags)
+{
+ if(part) {
+ static const char badbytes[]={
+ /* */ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x7f, 0x00 /* null-terminate */
+ };
+ size_t n = strlen(part);
+ size_t nfine = strcspn(part, badbytes);
+ if(nfine != n)
+ /* since we don't know which part is scanned, return a generic error
+ code */
+ return TRUE;
+ if(!(flags & CURLU_ALLOW_SPACE) && strchr(part, ' '))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * parse_hostname_login()
+ *
+ * Parse the login details (user name, password and options) from the URL and
+ * strip them out of the host name
+ *
+ */
+static CURLUcode parse_hostname_login(struct Curl_URL *u,
+ struct dynbuf *host,
+ unsigned int flags)
+{
+ CURLUcode result = CURLUE_OK;
+ CURLcode ccode;
+ char *userp = NULL;
+ char *passwdp = NULL;
+ char *optionsp = NULL;
+ const struct Curl_handler *h = NULL;
+
+ /* At this point, we assume all the other special cases have been taken
+ * care of, so the host is at most
+ *
+ * [user[:password][;options]]@]hostname
+ *
+ * We need somewhere to put the embedded details, so do that first.
+ */
+
+ char *login = Curl_dyn_ptr(host);
+ char *ptr;
+
+ DEBUGASSERT(login);
+
+ ptr = strchr(login, '@');
+ if(!ptr)
+ goto out;
+
+ /* We will now try to extract the
+ * possible login information in a string like:
+ * ftp://user:password@ftp.my.site:8021/README */
+ ptr++;
+
+ /* if this is a known scheme, get some details */
+ if(u->scheme)
+ h = Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED);
+
+ /* We could use the login information in the URL so extract it. Only parse
+ options if the handler says we should. Note that 'h' might be NULL! */
+ ccode = Curl_parse_login_details(login, ptr - login - 1,
+ &userp, &passwdp,
+ (h && (h->flags & PROTOPT_URLOPTIONS)) ?
+ &optionsp:NULL);
+ if(ccode) {
+ result = CURLUE_BAD_LOGIN;
+ goto out;
+ }
+
+ if(userp) {
+ if(flags & CURLU_DISALLOW_USER) {
+ /* Option DISALLOW_USER is set and url contains username. */
+ result = CURLUE_USER_NOT_ALLOWED;
+ goto out;
+ }
+ if(junkscan(userp, flags)) {
+ result = CURLUE_BAD_USER;
+ goto out;
+ }
+ u->user = userp;
+ }
+
+ if(passwdp) {
+ if(junkscan(passwdp, flags)) {
+ result = CURLUE_BAD_PASSWORD;
+ goto out;
+ }
+ u->password = passwdp;
+ }
+
+ if(optionsp) {
+ if(junkscan(optionsp, flags)) {
+ result = CURLUE_BAD_LOGIN;
+ goto out;
+ }
+ u->options = optionsp;
+ }
+
+ /* move the name to the start of the host buffer */
+ if(Curl_dyn_tail(host, strlen(ptr)))
+ return CURLUE_OUT_OF_MEMORY;
+
+ return CURLUE_OK;
+ out:
+
+ free(userp);
+ free(passwdp);
+ free(optionsp);
+ u->user = NULL;
+ u->password = NULL;
+ u->options = NULL;
+
+ return result;
+}
+
+UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
+ bool has_scheme)
+{
+ char *portptr = NULL;
+ char endbracket;
+ int len;
+ char *hostname = Curl_dyn_ptr(host);
+ /*
+ * Find the end of an IPv6 address, either on the ']' ending bracket or
+ * a percent-encoded zone index.
+ */
+ if(1 == sscanf(hostname, "[%*45[0123456789abcdefABCDEF:.]%c%n",
+ &endbracket, &len)) {
+ if(']' == endbracket)
+ portptr = &hostname[len];
+ else if('%' == endbracket) {
+ int zonelen = len;
+ if(1 == sscanf(hostname + zonelen, "%*[^]]%c%n", &endbracket, &len)) {
+ if(']' != endbracket)
+ return CURLUE_BAD_IPV6;
+ portptr = &hostname[--zonelen + len + 1];
+ }
+ else
+ return CURLUE_BAD_IPV6;
+ }
+ else
+ return CURLUE_BAD_IPV6;
+
+ /* this is a RFC2732-style specified IP-address */
+ if(portptr && *portptr) {
+ if(*portptr != ':')
+ return CURLUE_BAD_IPV6;
+ }
+ else
+ portptr = NULL;
+ }
+ else
+ portptr = strchr(hostname, ':');
+
+ if(portptr) {
+ char *rest;
+ long port;
+ char portbuf[7];
+ size_t keep = portptr - hostname;
+
+ /* Browser behavior adaptation. If there's a colon with no digits after,
+ just cut off the name there which makes us ignore the colon and just
+ use the default port. Firefox, Chrome and Safari all do that.
+
+ Don't do it if the URL has no scheme, to make something that looks like
+ a scheme not work!
+ */
+ Curl_dyn_setlen(host, keep);
+ portptr++;
+ if(!*portptr)
+ return has_scheme ? CURLUE_OK : CURLUE_BAD_PORT_NUMBER;
+
+ if(!ISDIGIT(*portptr))
+ return CURLUE_BAD_PORT_NUMBER;
+
+ port = strtol(portptr, &rest, 10); /* Port number must be decimal */
+
+ if(port > 0xffff)
+ return CURLUE_BAD_PORT_NUMBER;
+
+ if(rest[0])
+ return CURLUE_BAD_PORT_NUMBER;
+
+ *rest = 0;
+ /* generate a new port number string to get rid of leading zeroes etc */
+ msnprintf(portbuf, sizeof(portbuf), "%ld", port);
+ u->portnum = port;
+ u->port = strdup(portbuf);
+ if(!u->port)
+ return CURLUE_OUT_OF_MEMORY;
+ }
+
+ return CURLUE_OK;
+}
+
+static CURLUcode hostname_check(struct Curl_URL *u, char *hostname,
+ size_t hlen) /* length of hostname */
+{
+ size_t len;
+ DEBUGASSERT(hostname);
+
+ if(!hostname[0])
+ return CURLUE_NO_HOST;
+ else if(hostname[0] == '[') {
+ const char *l = "0123456789abcdefABCDEF:.";
+ if(hlen < 4) /* '[::]' is the shortest possible valid string */
+ return CURLUE_BAD_IPV6;
+ hostname++;
+ hlen -= 2;
+
+ if(hostname[hlen] != ']')
+ return CURLUE_BAD_IPV6;
+
+ /* only valid letters are ok */
+ len = strspn(hostname, l);
+ if(hlen != len) {
+ hlen = len;
+ if(hostname[len] == '%') {
+ /* this could now be '%[zone id]' */
+ char zoneid[16];
+ int i = 0;
+ char *h = &hostname[len + 1];
+ /* pass '25' if present and is a url encoded percent sign */
+ if(!strncmp(h, "25", 2) && h[2] && (h[2] != ']'))
+ h += 2;
+ while(*h && (*h != ']') && (i < 15))
+ zoneid[i++] = *h++;
+ if(!i || (']' != *h))
+ /* impossible to reach? */
+ return CURLUE_MALFORMED_INPUT;
+ zoneid[i] = 0;
+ u->zoneid = strdup(zoneid);
+ if(!u->zoneid)
+ return CURLUE_OUT_OF_MEMORY;
+ hostname[len] = ']'; /* insert end bracket */
+ hostname[len + 1] = 0; /* terminate the hostname */
+ }
+ else
+ return CURLUE_BAD_IPV6;
+ /* hostname is fine */
+ }
+#ifdef ENABLE_IPV6
+ {
+ char dest[16]; /* fits a binary IPv6 address */
+ char norm[MAX_IPADR_LEN];
+ hostname[hlen] = 0; /* end the address there */
+ if(1 != Curl_inet_pton(AF_INET6, hostname, dest))
+ return CURLUE_BAD_IPV6;
+
+ /* check if it can be done shorter */
+ if(Curl_inet_ntop(AF_INET6, dest, norm, sizeof(norm)) &&
+ (strlen(norm) < hlen)) {
+ strcpy(hostname, norm);
+ hlen = strlen(norm);
+ hostname[hlen + 1] = 0;
+ }
+ hostname[hlen] = ']'; /* restore ending bracket */
+ }
+#endif
+ }
+ else {
+ /* letters from the second string are not ok */
+ len = strcspn(hostname, " \r\n\t/:#?!@{}[]\\$\'\"^`*<>=;,+&()");
+ if(hlen != len)
+ /* hostname with bad content */
+ return CURLUE_BAD_HOSTNAME;
+ }
+ return CURLUE_OK;
+}
+
+#define HOSTNAME_END(x) (((x) == '/') || ((x) == '?') || ((x) == '#'))
+
+/*
+ * Handle partial IPv4 numerical addresses and different bases, like
+ * '16843009', '0x7f', '0x7f.1' '0177.1.1.1' etc.
+ *
+ * If the given input string is syntactically wrong or any part for example is
+ * too big, this function returns FALSE and doesn't create any output.
+ *
+ * Output the "normalized" version of that input string in plain quad decimal
+ * integers and return TRUE.
+ */
+static bool ipv4_normalize(const char *hostname, char *outp, size_t olen)
+{
+ bool done = FALSE;
+ int n = 0;
+ const char *c = hostname;
+ unsigned long parts[4] = {0, 0, 0, 0};
+
+ while(!done) {
+ char *endp;
+ unsigned long l;
+ if((*c < '0') || (*c > '9'))
+ /* most importantly this doesn't allow a leading plus or minus */
+ return FALSE;
+ l = strtoul(c, &endp, 0);
+
+ /* overflow or nothing parsed at all */
+ if(((l == ULONG_MAX) && (errno == ERANGE)) || (endp == c))
+ return FALSE;
+
+#if SIZEOF_LONG > 4
+ /* a value larger than 32 bits */
+ if(l > UINT_MAX)
+ return FALSE;
+#endif
+
+ parts[n] = l;
+ c = endp;
+
+ switch (*c) {
+ case '.' :
+ if(n == 3)
+ return FALSE;
+ n++;
+ c++;
+ break;
+
+ case '\0':
+ done = TRUE;
+ break;
+
+ default:
+ return FALSE;
+ }
+ }
+
+ /* this is deemed a valid IPv4 numerical address */
+
+ switch(n) {
+ case 0: /* a -- 32 bits */
+ msnprintf(outp, olen, "%u.%u.%u.%u",
+ parts[0] >> 24, (parts[0] >> 16) & 0xff,
+ (parts[0] >> 8) & 0xff, parts[0] & 0xff);
+ break;
+ case 1: /* a.b -- 8.24 bits */
+ if((parts[0] > 0xff) || (parts[1] > 0xffffff))
+ return FALSE;
+ msnprintf(outp, olen, "%u.%u.%u.%u",
+ parts[0], (parts[1] >> 16) & 0xff,
+ (parts[1] >> 8) & 0xff, parts[1] & 0xff);
+ break;
+ case 2: /* a.b.c -- 8.8.16 bits */
+ if((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xffff))
+ return FALSE;
+ msnprintf(outp, olen, "%u.%u.%u.%u",
+ parts[0], parts[1], (parts[2] >> 8) & 0xff,
+ parts[2] & 0xff);
+ break;
+ case 3: /* a.b.c.d -- 8.8.8.8 bits */
+ if((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff) ||
+ (parts[3] > 0xff))
+ return FALSE;
+ msnprintf(outp, olen, "%u.%u.%u.%u",
+ parts[0], parts[1], parts[2], parts[3]);
+ break;
+ }
+ return TRUE;
+}
+
+/* if necessary, replace the host content with a URL decoded version */
+static CURLUcode decode_host(struct dynbuf *host)
+{
+ char *per = NULL;
+ const char *hostname = Curl_dyn_ptr(host);
+ if(hostname[0] == '[')
+ /* only decode if not an ipv6 numerical */
+ return CURLUE_OK;
+ per = strchr(hostname, '%');
+ if(!per)
+ /* nothing to decode */
+ return CURLUE_OK;
+ else {
+ /* encoded */
+ size_t dlen;
+ char *decoded;
+ CURLcode result = Curl_urldecode(hostname, 0, &decoded, &dlen,
+ REJECT_CTRL);
+ if(result)
+ return CURLUE_BAD_HOSTNAME;
+ Curl_dyn_reset(host);
+ result = Curl_dyn_addn(host, decoded, dlen);
+ free(decoded);
+ if(result)
+ return CURLUE_OUT_OF_MEMORY;
+ }
+
+ return CURLUE_OK;
+}
+
+/*
+ * "Remove Dot Segments"
+ * https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4
+ */
+
+/*
+ * dedotdotify()
+ * @unittest: 1395
+ *
+ * This function gets a null-terminated path with dot and dotdot sequences
+ * passed in and strips them off according to the rules in RFC 3986 section
+ * 5.2.4.
+ *
+ * The function handles a query part ('?' + stuff) appended but it expects
+ * that fragments ('#' + stuff) have already been cut off.
+ *
+ * RETURNS
+ *
+ * an allocated dedotdotified output string
+ */
+UNITTEST char *dedotdotify(const char *input, size_t clen);
+UNITTEST char *dedotdotify(const char *input, size_t clen)
+{
+ char *out = malloc(clen + 1);
+ char *outptr;
+ const char *orginput = input;
+ char *queryp;
+ if(!out)
+ return NULL; /* out of memory */
+
+ *out = 0; /* null-terminates, for inputs like "./" */
+ outptr = out;
+
+ if(!*input)
+ /* zero length input string, return that */
+ return out;
+
+ /*
+ * To handle query-parts properly, we must find it and remove it during the
+ * dotdot-operation and then append it again at the end to the output
+ * string.
+ */
+ queryp = strchr(input, '?');
+
+ do {
+ bool dotdot = TRUE;
+ if(*input == '.') {
+ /* A. If the input buffer begins with a prefix of "../" or "./", then
+ remove that prefix from the input buffer; otherwise, */
+
+ if(!strncmp("./", input, 2)) {
+ input += 2;
+ clen -= 2;
+ }
+ else if(!strncmp("../", input, 3)) {
+ input += 3;
+ clen -= 3;
+ }
+ /* D. if the input buffer consists only of "." or "..", then remove
+ that from the input buffer; otherwise, */
+
+ else if(!strcmp(".", input) || !strcmp("..", input) ||
+ !strncmp(".?", input, 2) || !strncmp("..?", input, 3)) {
+ *out = 0;
+ break;
+ }
+ else
+ dotdot = FALSE;
+ }
+ else if(*input == '/') {
+ /* B. if the input buffer begins with a prefix of "/./" or "/.", where
+ "." is a complete path segment, then replace that prefix with "/" in
+ the input buffer; otherwise, */
+ if(!strncmp("/./", input, 3)) {
+ input += 2;
+ clen -= 2;
+ }
+ else if(!strcmp("/.", input) || !strncmp("/.?", input, 3)) {
+ *outptr++ = '/';
+ *outptr = 0;
+ break;
+ }
+
+ /* C. if the input buffer begins with a prefix of "/../" or "/..",
+ where ".." is a complete path segment, then replace that prefix with
+ "/" in the input buffer and remove the last segment and its
+ preceding "/" (if any) from the output buffer; otherwise, */
+
+ else if(!strncmp("/../", input, 4)) {
+ input += 3;
+ clen -= 3;
+ /* remove the last segment from the output buffer */
+ while(outptr > out) {
+ outptr--;
+ if(*outptr == '/')
+ break;
+ }
+ *outptr = 0; /* null-terminate where it stops */
+ }
+ else if(!strcmp("/..", input) || !strncmp("/..?", input, 4)) {
+ /* remove the last segment from the output buffer */
+ while(outptr > out) {
+ outptr--;
+ if(*outptr == '/')
+ break;
+ }
+ *outptr++ = '/';
+ *outptr = 0; /* null-terminate where it stops */
+ break;
+ }
+ else
+ dotdot = FALSE;
+ }
+ else
+ dotdot = FALSE;
+
+ if(!dotdot) {
+ /* E. move the first path segment in the input buffer to the end of
+ the output buffer, including the initial "/" character (if any) and
+ any subsequent characters up to, but not including, the next "/"
+ character or the end of the input buffer. */
+
+ do {
+ *outptr++ = *input++;
+ clen--;
+ } while(*input && (*input != '/') && (*input != '?'));
+ *outptr = 0;
+ }
+
+ /* continue until end of input string OR, if there is a terminating
+ query part, stop there */
+ } while(*input && (!queryp || (input < queryp)));
+
+ if(queryp) {
+ size_t qlen;
+ /* There was a query part, append that to the output. */
+ size_t oindex = queryp - orginput;
+ qlen = strlen(&orginput[oindex]);
+ memcpy(outptr, &orginput[oindex], qlen + 1); /* include zero byte */
+ }
+
+ return out;
+}
+
+static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
+{
+ const char *path;
+ size_t pathlen;
+ bool uncpath = FALSE;
+ char *query = NULL;
+ char *fragment = NULL;
+ char schemebuf[MAX_SCHEME_LEN + 1];
+ const char *schemep = NULL;
+ size_t schemelen = 0;
+ size_t urllen;
+ CURLUcode result = CURLUE_OK;
+ size_t fraglen = 0;
+ struct dynbuf host;
+
+ DEBUGASSERT(url);
+
+ Curl_dyn_init(&host, CURL_MAX_INPUT_LENGTH);
+
+ /*************************************************************
+ * Parse the URL.
+ ************************************************************/
+ /* allocate scratch area */
+ urllen = strlen(url);
+ if(urllen > CURL_MAX_INPUT_LENGTH) {
+ /* excessive input length */
+ result = CURLUE_MALFORMED_INPUT;
+ goto fail;
+ }
+
+ schemelen = Curl_is_absolute_url(url, schemebuf, sizeof(schemebuf),
+ flags & (CURLU_GUESS_SCHEME|
+ CURLU_DEFAULT_SCHEME));
+
+ /* handle the file: scheme */
+ if(schemelen && !strcmp(schemebuf, "file")) {
+ if(urllen <= 6) {
+ /* file:/ is not enough to actually be a complete file: URL */
+ result = CURLUE_BAD_FILE_URL;
+ goto fail;
+ }
+
+ /* path has been allocated large enough to hold this */
+ path = (char *)&url[5];
+
+ schemep = u->scheme = strdup("file");
+ if(!u->scheme) {
+ result = CURLUE_OUT_OF_MEMORY;
+ goto fail;
+ }
+
+ /* Extra handling URLs with an authority component (i.e. that start with
+ * "file://")
+ *
+ * We allow omitted hostname (e.g. file:/<path>) -- valid according to
+ * RFC 8089, but not the (current) WHAT-WG URL spec.
+ */
+ if(path[0] == '/' && path[1] == '/') {
+ /* swallow the two slashes */
+ const char *ptr = &path[2];
+
+ /*
+ * According to RFC 8089, a file: URL can be reliably dereferenced if:
+ *
+ * o it has no/blank hostname, or
+ *
+ * o the hostname matches "localhost" (case-insensitively), or
+ *
+ * o the hostname is a FQDN that resolves to this machine, or
+ *
+ * o it is an UNC String transformed to an URI (Windows only, RFC 8089
+ * Appendix E.3).
+ *
+ * For brevity, we only consider URLs with empty, "localhost", or
+ * "127.0.0.1" hostnames as local, otherwise as an UNC String.
+ *
+ * Additionally, there is an exception for URLs with a Windows drive
+ * letter in the authority (which was accidentally omitted from RFC 8089
+ * Appendix E, but believe me, it was meant to be there. --MK)
+ */
+ if(ptr[0] != '/' && !STARTS_WITH_URL_DRIVE_PREFIX(ptr)) {
+ /* the URL includes a host name, it must match "localhost" or
+ "127.0.0.1" to be valid */
+ if(checkprefix("localhost/", ptr) ||
+ checkprefix("127.0.0.1/", ptr)) {
+ ptr += 9; /* now points to the slash after the host */
+ }
+ else {
+#if defined(WIN32)
+ size_t len;
+
+ /* the host name, NetBIOS computer name, can not contain disallowed
+ chars, and the delimiting slash character must be appended to the
+ host name */
+ path = strpbrk(ptr, "/\\:*?\"<>|");
+ if(!path || *path != '/') {
+ result = CURLUE_BAD_FILE_URL;
+ goto fail;
+ }
+
+ len = path - ptr;
+ if(len) {
+ if(Curl_dyn_addn(&host, ptr, len)) {
+ result = CURLUE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ uncpath = TRUE;
+ }
+
+ ptr -= 2; /* now points to the // before the host in UNC */
+#else
+ /* Invalid file://hostname/, expected localhost or 127.0.0.1 or
+ none */
+ result = CURLUE_BAD_FILE_URL;
+ goto fail;
+#endif
+ }
+ }
+
+ path = ptr;
+ }
+
+ if(!uncpath)
+ /* no host for file: URLs by default */
+ Curl_dyn_reset(&host);
+
+#if !defined(MSDOS) && !defined(WIN32) && !defined(__CYGWIN__)
+ /* Don't allow Windows drive letters when not in Windows.
+ * This catches both "file:/c:" and "file:c:" */
+ if(('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) ||
+ STARTS_WITH_URL_DRIVE_PREFIX(path)) {
+ /* File drive letters are only accepted in MSDOS/Windows */
+ result = CURLUE_BAD_FILE_URL;
+ goto fail;
+ }
+#else
+ /* If the path starts with a slash and a drive letter, ditch the slash */
+ if('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) {
+ /* This cannot be done with strcpy, as the memory chunks overlap! */
+ path++;
+ }
+#endif
+
+ }
+ else {
+ /* clear path */
+ const char *p;
+ const char *hostp;
+ size_t len;
+
+ if(schemelen) {
+ int i = 0;
+ p = &url[schemelen + 1];
+ while(p && (*p == '/') && (i < 4)) {
+ p++;
+ i++;
+ }
+
+ schemep = schemebuf;
+ if(!Curl_builtin_scheme(schemep, CURL_ZERO_TERMINATED) &&
+ !(flags & CURLU_NON_SUPPORT_SCHEME)) {
+ result = CURLUE_UNSUPPORTED_SCHEME;
+ goto fail;
+ }
+
+ if((i < 1) || (i>3)) {
+ /* less than one or more than three slashes */
+ result = CURLUE_BAD_SLASHES;
+ goto fail;
+ }
+ if(junkscan(schemep, flags)) {
+ result = CURLUE_BAD_SCHEME;
+ goto fail;
+ }
+ }
+ else {
+ /* no scheme! */
+
+ if(!(flags & (CURLU_DEFAULT_SCHEME|CURLU_GUESS_SCHEME))) {
+ result = CURLUE_BAD_SCHEME;
+ goto fail;
+ }
+ if(flags & CURLU_DEFAULT_SCHEME)
+ schemep = DEFAULT_SCHEME;
+
+ /*
+ * The URL was badly formatted, let's try without scheme specified.
+ */
+ p = url;
+ }
+ hostp = p; /* host name starts here */
+
+ /* find the end of the host name + port number */
+ while(*p && !HOSTNAME_END(*p))
+ p++;
+
+ len = p - hostp;
+ if(len) {
+ if(Curl_dyn_addn(&host, hostp, len)) {
+ result = CURLUE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ }
+ else {
+ if(!(flags & CURLU_NO_AUTHORITY)) {
+ result = CURLUE_NO_HOST;
+ goto fail;
+ }
+ }
+
+ path = (char *)p;
+
+ if(schemep) {
+ u->scheme = strdup(schemep);
+ if(!u->scheme) {
+ result = CURLUE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ }
+ }
+
+ fragment = strchr(path, '#');
+ if(fragment) {
+ fraglen = strlen(fragment);
+ if(fraglen > 1) {
+ /* skip the leading '#' in the copy but include the terminating null */
+ u->fragment = Curl_memdup(fragment + 1, fraglen);
+ if(!u->fragment) {
+ result = CURLUE_OUT_OF_MEMORY;
+ goto fail;
+ }
+
+ if(junkscan(u->fragment, flags)) {
+ result = CURLUE_BAD_FRAGMENT;
+ goto fail;
+ }
+ }
+ }
+
+ query = strchr(path, '?');
+ if(query && (!fragment || (query < fragment))) {
+ size_t qlen = strlen(query) - fraglen; /* includes '?' */
+ pathlen = strlen(path) - qlen - fraglen;
+ if(qlen > 1) {
+ if(qlen && (flags & CURLU_URLENCODE)) {
+ struct dynbuf enc;
+ Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+ /* skip the leading question mark */
+ if(urlencode_str(&enc, query + 1, qlen - 1, TRUE, TRUE)) {
+ result = CURLUE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ u->query = Curl_dyn_ptr(&enc);
+ }
+ else {
+ u->query = Curl_memdup(query + 1, qlen);
+ if(!u->query) {
+ result = CURLUE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ u->query[qlen - 1] = 0;
+ }
+
+ if(junkscan(u->query, flags)) {
+ result = CURLUE_BAD_QUERY;
+ goto fail;
+ }
+ }
+ else {
+ /* single byte query */
+ u->query = strdup("");
+ if(!u->query) {
+ result = CURLUE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ }
+ }
+ else
+ pathlen = strlen(path) - fraglen;
+
+ if(pathlen && (flags & CURLU_URLENCODE)) {
+ struct dynbuf enc;
+ Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+ if(urlencode_str(&enc, path, pathlen, TRUE, FALSE)) {
+ result = CURLUE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ pathlen = Curl_dyn_len(&enc);
+ path = u->path = Curl_dyn_ptr(&enc);
+ }
+
+ if(!pathlen) {
+ /* there is no path left, unset */
+ path = NULL;
+ }
+ else {
+ if(!u->path) {
+ u->path = Curl_memdup(path, pathlen + 1);
+ if(!u->path) {
+ result = CURLUE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ u->path[pathlen] = 0;
+ path = u->path;
+ }
+ else if(flags & CURLU_URLENCODE)
+ /* it might have encoded more than just the path so cut it */
+ u->path[pathlen] = 0;
+
+ if(junkscan(u->path, flags)) {
+ result = CURLUE_BAD_PATH;
+ goto fail;
+ }
+
+ if(!(flags & CURLU_PATH_AS_IS)) {
+ /* remove ../ and ./ sequences according to RFC3986 */
+ char *newp = dedotdotify((char *)path, pathlen);
+ if(!newp) {
+ result = CURLUE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ free(u->path);
+ u->path = newp;
+ }
+ }
+
+ if(Curl_dyn_len(&host)) {
+ char normalized_ipv4[sizeof("255.255.255.255") + 1];
+
+ /*
+ * Parse the login details and strip them out of the host name.
+ */
+ result = parse_hostname_login(u, &host, flags);
+ if(!result)
+ result = Curl_parse_port(u, &host, schemelen);
+ if(result)
+ goto fail;
+
+ if(junkscan(Curl_dyn_ptr(&host), flags)) {
+ result = CURLUE_BAD_HOSTNAME;
+ goto fail;
+ }
+
+ if(ipv4_normalize(Curl_dyn_ptr(&host),
+ normalized_ipv4, sizeof(normalized_ipv4))) {
+ Curl_dyn_reset(&host);
+ if(Curl_dyn_add(&host, normalized_ipv4)) {
+ result = CURLUE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ }
+ else {
+ result = decode_host(&host);
+ if(!result)
+ result = hostname_check(u, Curl_dyn_ptr(&host), Curl_dyn_len(&host));
+ if(result)
+ goto fail;
+ }
+
+ if((flags & CURLU_GUESS_SCHEME) && !schemep) {
+ const char *hostname = Curl_dyn_ptr(&host);
+ /* legacy curl-style guess based on host name */
+ if(checkprefix("ftp.", hostname))
+ schemep = "ftp";
+ else if(checkprefix("dict.", hostname))
+ schemep = "dict";
+ else if(checkprefix("ldap.", hostname))
+ schemep = "ldap";
+ else if(checkprefix("imap.", hostname))
+ schemep = "imap";
+ else if(checkprefix("smtp.", hostname))
+ schemep = "smtp";
+ else if(checkprefix("pop3.", hostname))
+ schemep = "pop3";
+ else
+ schemep = "http";
+
+ u->scheme = strdup(schemep);
+ if(!u->scheme) {
+ result = CURLUE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ }
+ }
+ else if(flags & CURLU_NO_AUTHORITY) {
+ /* allowed to be empty. */
+ if(Curl_dyn_add(&host, "")) {
+ result = CURLUE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ }
+
+ u->host = Curl_dyn_ptr(&host);
+
+ return result;
+ fail:
+ Curl_dyn_free(&host);
+ free_urlhandle(u);
+ return result;
+}
+
+/*
+ * Parse the URL and, if successful, replace everything in the Curl_URL struct.
+ */
+static CURLUcode parseurl_and_replace(const char *url, CURLU *u,
+ unsigned int flags)
+{
+ CURLUcode result;
+ CURLU tmpurl;
+ memset(&tmpurl, 0, sizeof(tmpurl));
+ result = parseurl(url, &tmpurl, flags);
+ if(!result) {
+ free_urlhandle(u);
+ *u = tmpurl;
+ }
+ return result;
+}
+
+/*
+ */
+CURLU *curl_url(void)
+{
+ return calloc(sizeof(struct Curl_URL), 1);
+}
+
+void curl_url_cleanup(CURLU *u)
+{
+ if(u) {
+ free_urlhandle(u);
+ free(u);
+ }
+}
+
+#define DUP(dest, src, name) \
+ do { \
+ if(src->name) { \
+ dest->name = strdup(src->name); \
+ if(!dest->name) \
+ goto fail; \
+ } \
+ } while(0)
+
+CURLU *curl_url_dup(CURLU *in)
+{
+ struct Curl_URL *u = calloc(sizeof(struct Curl_URL), 1);
+ if(u) {
+ DUP(u, in, scheme);
+ DUP(u, in, user);
+ DUP(u, in, password);
+ DUP(u, in, options);
+ DUP(u, in, host);
+ DUP(u, in, port);
+ DUP(u, in, path);
+ DUP(u, in, query);
+ DUP(u, in, fragment);
+ u->portnum = in->portnum;
+ }
+ return u;
+ fail:
+ curl_url_cleanup(u);
+ return NULL;
+}
+
+CURLUcode curl_url_get(CURLU *u, CURLUPart what,
+ char **part, unsigned int flags)
+{
+ char *ptr;
+ CURLUcode ifmissing = CURLUE_UNKNOWN_PART;
+ char portbuf[7];
+ bool urldecode = (flags & CURLU_URLDECODE)?1:0;
+ bool urlencode = (flags & CURLU_URLENCODE)?1:0;
+ bool plusdecode = FALSE;
+ (void)flags;
+ if(!u)
+ return CURLUE_BAD_HANDLE;
+ if(!part)
+ return CURLUE_BAD_PARTPOINTER;
+ *part = NULL;
+
+ switch(what) {
+ case CURLUPART_SCHEME:
+ ptr = u->scheme;
+ ifmissing = CURLUE_NO_SCHEME;
+ urldecode = FALSE; /* never for schemes */
+ break;
+ case CURLUPART_USER:
+ ptr = u->user;
+ ifmissing = CURLUE_NO_USER;
+ break;
+ case CURLUPART_PASSWORD:
+ ptr = u->password;
+ ifmissing = CURLUE_NO_PASSWORD;
+ break;
+ case CURLUPART_OPTIONS:
+ ptr = u->options;
+ ifmissing = CURLUE_NO_OPTIONS;
+ break;
+ case CURLUPART_HOST:
+ ptr = u->host;
+ ifmissing = CURLUE_NO_HOST;
+ break;
+ case CURLUPART_ZONEID:
+ ptr = u->zoneid;
+ ifmissing = CURLUE_NO_ZONEID;
+ break;
+ case CURLUPART_PORT:
+ ptr = u->port;
+ ifmissing = CURLUE_NO_PORT;
+ urldecode = FALSE; /* never for port */
+ if(!ptr && (flags & CURLU_DEFAULT_PORT) && u->scheme) {
+ /* there's no stored port number, but asked to deliver
+ a default one for the scheme */
+ const struct Curl_handler *h =
+ Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED);
+ if(h) {
+ msnprintf(portbuf, sizeof(portbuf), "%u", h->defport);
+ ptr = portbuf;
+ }
+ }
+ else if(ptr && u->scheme) {
+ /* there is a stored port number, but ask to inhibit if
+ it matches the default one for the scheme */
+ const struct Curl_handler *h =
+ Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED);
+ if(h && (h->defport == u->portnum) &&
+ (flags & CURLU_NO_DEFAULT_PORT))
+ ptr = NULL;
+ }
+ break;
+ case CURLUPART_PATH:
+ ptr = u->path;
+ if(!ptr) {
+ ptr = u->path = strdup("/");
+ if(!u->path)
+ return CURLUE_OUT_OF_MEMORY;
+ }
+ break;
+ case CURLUPART_QUERY:
+ ptr = u->query;
+ ifmissing = CURLUE_NO_QUERY;
+ plusdecode = urldecode;
+ break;
+ case CURLUPART_FRAGMENT:
+ ptr = u->fragment;
+ ifmissing = CURLUE_NO_FRAGMENT;
+ break;
+ case CURLUPART_URL: {
+ char *url;
+ char *scheme;
+ char *options = u->options;
+ char *port = u->port;
+ char *allochost = NULL;
+ if(u->scheme && strcasecompare("file", u->scheme)) {
+ url = aprintf("file://%s%s%s",
+ u->path,
+ u->fragment? "#": "",
+ u->fragment? u->fragment : "");
+ }
+ else if(!u->host)
+ return CURLUE_NO_HOST;
+ else {
+ const struct Curl_handler *h = NULL;
+ if(u->scheme)
+ scheme = u->scheme;
+ else if(flags & CURLU_DEFAULT_SCHEME)
+ scheme = (char *) DEFAULT_SCHEME;
+ else
+ return CURLUE_NO_SCHEME;
+
+ h = Curl_builtin_scheme(scheme, CURL_ZERO_TERMINATED);
+ if(!port && (flags & CURLU_DEFAULT_PORT)) {
+ /* there's no stored port number, but asked to deliver
+ a default one for the scheme */
+ if(h) {
+ msnprintf(portbuf, sizeof(portbuf), "%u", h->defport);
+ port = portbuf;
+ }
+ }
+ else if(port) {
+ /* there is a stored port number, but asked to inhibit if it matches
+ the default one for the scheme */
+ if(h && (h->defport == u->portnum) &&
+ (flags & CURLU_NO_DEFAULT_PORT))
+ port = NULL;
+ }
+
+ if(h && !(h->flags & PROTOPT_URLOPTIONS))
+ options = NULL;
+
+ if(u->host[0] == '[') {
+ if(u->zoneid) {
+ /* make it '[ host %25 zoneid ]' */
+ struct dynbuf enc;
+ size_t hostlen = strlen(u->host);
+ Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+ if(Curl_dyn_addf(&enc, "%.*s%%25%s]", (int)hostlen - 1, u->host,
+ u->zoneid))
+ return CURLUE_OUT_OF_MEMORY;
+ allochost = Curl_dyn_ptr(&enc);
+ }
+ }
+ else if(urlencode) {
+ allochost = curl_easy_escape(NULL, u->host, 0);
+ if(!allochost)
+ return CURLUE_OUT_OF_MEMORY;
+ }
+ else {
+ /* only encode '%' in output host name */
+ char *host = u->host;
+ bool percent = FALSE;
+ /* first, count number of percents present in the name */
+ while(*host) {
+ if(*host == '%') {
+ percent = TRUE;
+ break;
+ }
+ host++;
+ }
+ /* if there were percent(s), encode the host name */
+ if(percent) {
+ struct dynbuf enc;
+ CURLcode result;
+ Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+ host = u->host;
+ while(*host) {
+ if(*host == '%')
+ result = Curl_dyn_addn(&enc, "%25", 3);
+ else
+ result = Curl_dyn_addn(&enc, host, 1);
+ if(result)
+ return CURLUE_OUT_OF_MEMORY;
+ host++;
+ }
+ free(u->host);
+ u->host = Curl_dyn_ptr(&enc);
+ }
+ }
+
+ url = aprintf("%s://%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ scheme,
+ u->user ? u->user : "",
+ u->password ? ":": "",
+ u->password ? u->password : "",
+ options ? ";" : "",
+ options ? options : "",
+ (u->user || u->password || options) ? "@": "",
+ allochost ? allochost : u->host,
+ port ? ":": "",
+ port ? port : "",
+ (u->path && (u->path[0] != '/')) ? "/": "",
+ u->path ? u->path : "/",
+ (u->query && u->query[0]) ? "?": "",
+ (u->query && u->query[0]) ? u->query : "",
+ u->fragment? "#": "",
+ u->fragment? u->fragment : "");
+ free(allochost);
+ }
+ if(!url)
+ return CURLUE_OUT_OF_MEMORY;
+ *part = url;
+ return CURLUE_OK;
+ }
+ default:
+ ptr = NULL;
+ break;
+ }
+ if(ptr) {
+ size_t partlen = strlen(ptr);
+ size_t i = 0;
+ *part = Curl_memdup(ptr, partlen + 1);
+ if(!*part)
+ return CURLUE_OUT_OF_MEMORY;
+ if(plusdecode) {
+ /* convert + to space */
+ char *plus = *part;
+ for(i = 0; i < partlen; ++plus, i++) {
+ if(*plus == '+')
+ *plus = ' ';
+ }
+ }
+ if(urldecode) {
+ char *decoded;
+ size_t dlen;
+ /* this unconditional rejection of control bytes is documented
+ API behavior */
+ CURLcode res = Curl_urldecode(*part, 0, &decoded, &dlen, REJECT_CTRL);
+ free(*part);
+ if(res) {
+ *part = NULL;
+ return CURLUE_URLDECODE;
+ }
+ *part = decoded;
+ partlen = dlen;
+ }
+ if(urlencode) {
+ struct dynbuf enc;
+ Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+ if(urlencode_str(&enc, *part, partlen, TRUE,
+ what == CURLUPART_QUERY))
+ return CURLUE_OUT_OF_MEMORY;
+ free(*part);
+ *part = Curl_dyn_ptr(&enc);
+ }
+
+ return CURLUE_OK;
+ }
+ else
+ return ifmissing;
+}
+
+CURLUcode curl_url_set(CURLU *u, CURLUPart what,
+ const char *part, unsigned int flags)
+{
+ char **storep = NULL;
+ long port = 0;
+ bool urlencode = (flags & CURLU_URLENCODE)? 1 : 0;
+ bool plusencode = FALSE;
+ bool urlskipslash = FALSE;
+ bool appendquery = FALSE;
+ bool equalsencode = FALSE;
+
+ if(!u)
+ return CURLUE_BAD_HANDLE;
+ if(!part) {
+ /* setting a part to NULL clears it */
+ switch(what) {
+ case CURLUPART_URL:
+ break;
+ case CURLUPART_SCHEME:
+ storep = &u->scheme;
+ break;
+ case CURLUPART_USER:
+ storep = &u->user;
+ break;
+ case CURLUPART_PASSWORD:
+ storep = &u->password;
+ break;
+ case CURLUPART_OPTIONS:
+ storep = &u->options;
+ break;
+ case CURLUPART_HOST:
+ storep = &u->host;
+ break;
+ case CURLUPART_ZONEID:
+ storep = &u->zoneid;
+ break;
+ case CURLUPART_PORT:
+ u->portnum = 0;
+ storep = &u->port;
+ break;
+ case CURLUPART_PATH:
+ storep = &u->path;
+ break;
+ case CURLUPART_QUERY:
+ storep = &u->query;
+ break;
+ case CURLUPART_FRAGMENT:
+ storep = &u->fragment;
+ break;
+ default:
+ return CURLUE_UNKNOWN_PART;
+ }
+ if(storep && *storep) {
+ Curl_safefree(*storep);
+ }
+ else if(!storep) {
+ free_urlhandle(u);
+ memset(u, 0, sizeof(struct Curl_URL));
+ }
+ return CURLUE_OK;
+ }
+
+ switch(what) {
+ case CURLUPART_SCHEME:
+ if(strlen(part) > MAX_SCHEME_LEN)
+ /* too long */
+ return CURLUE_BAD_SCHEME;
+ if(!(flags & CURLU_NON_SUPPORT_SCHEME) &&
+ /* verify that it is a fine scheme */
+ !Curl_builtin_scheme(part, CURL_ZERO_TERMINATED))
+ return CURLUE_UNSUPPORTED_SCHEME;
+ storep = &u->scheme;
+ urlencode = FALSE; /* never */
+ break;
+ case CURLUPART_USER:
+ storep = &u->user;
+ break;
+ case CURLUPART_PASSWORD:
+ storep = &u->password;
+ break;
+ case CURLUPART_OPTIONS:
+ storep = &u->options;
+ break;
+ case CURLUPART_HOST: {
+ size_t len = strcspn(part, " \r\n");
+ if(strlen(part) != len)
+ /* hostname with bad content */
+ return CURLUE_BAD_HOSTNAME;
+ storep = &u->host;
+ Curl_safefree(u->zoneid);
+ break;
+ }
+ case CURLUPART_ZONEID:
+ storep = &u->zoneid;
+ break;
+ case CURLUPART_PORT:
+ {
+ char *endp;
+ urlencode = FALSE; /* never */
+ port = strtol(part, &endp, 10); /* Port number must be decimal */
+ if((port <= 0) || (port > 0xffff))
+ return CURLUE_BAD_PORT_NUMBER;
+ if(*endp)
+ /* weirdly provided number, not good! */
+ return CURLUE_BAD_PORT_NUMBER;
+ storep = &u->port;
+ }
+ break;
+ case CURLUPART_PATH:
+ urlskipslash = TRUE;
+ storep = &u->path;
+ break;
+ case CURLUPART_QUERY:
+ plusencode = urlencode;
+ appendquery = (flags & CURLU_APPENDQUERY)?1:0;
+ equalsencode = appendquery;
+ storep = &u->query;
+ break;
+ case CURLUPART_FRAGMENT:
+ storep = &u->fragment;
+ break;
+ case CURLUPART_URL: {
+ /*
+ * Allow a new URL to replace the existing (if any) contents.
+ *
+ * If the existing contents is enough for a URL, allow a relative URL to
+ * replace it.
+ */
+ CURLUcode result;
+ char *oldurl;
+ char *redired_url;
+
+ /* if the new thing is absolute or the old one is not
+ * (we could not get an absolute url in 'oldurl'),
+ * then replace the existing with the new. */
+ if(Curl_is_absolute_url(part, NULL, 0,
+ flags & (CURLU_GUESS_SCHEME|
+ CURLU_DEFAULT_SCHEME))
+ || curl_url_get(u, CURLUPART_URL, &oldurl, flags)) {
+ return parseurl_and_replace(part, u, flags);
+ }
+
+ /* apply the relative part to create a new URL
+ * and replace the existing one with it. */
+ redired_url = concat_url(oldurl, part);
+ free(oldurl);
+ if(!redired_url)
+ return CURLUE_OUT_OF_MEMORY;
+
+ result = parseurl_and_replace(redired_url, u, flags);
+ free(redired_url);
+ return result;
+ }
+ default:
+ return CURLUE_UNKNOWN_PART;
+ }
+ DEBUGASSERT(storep);
+ {
+ const char *newp = part;
+ size_t nalloc = strlen(part);
+
+ if(nalloc > CURL_MAX_INPUT_LENGTH)
+ /* excessive input length */
+ return CURLUE_MALFORMED_INPUT;
+
+ if(urlencode) {
+ const unsigned char *i;
+ struct dynbuf enc;
+
+ Curl_dyn_init(&enc, nalloc * 3 + 1);
+
+ for(i = (const unsigned char *)part; *i; i++) {
+ CURLcode result;
+ if((*i == ' ') && plusencode) {
+ result = Curl_dyn_addn(&enc, "+", 1);
+ if(result)
+ return CURLUE_OUT_OF_MEMORY;
+ }
+ else if(Curl_isunreserved(*i) ||
+ ((*i == '/') && urlskipslash) ||
+ ((*i == '=') && equalsencode)) {
+ if((*i == '=') && equalsencode)
+ /* only skip the first equals sign */
+ equalsencode = FALSE;
+ result = Curl_dyn_addn(&enc, i, 1);
+ if(result)
+ return CURLUE_OUT_OF_MEMORY;
+ }
+ else {
+ result = Curl_dyn_addf(&enc, "%%%02x", *i);
+ if(result)
+ return CURLUE_OUT_OF_MEMORY;
+ }
+ }
+ newp = Curl_dyn_ptr(&enc);
+ }
+ else {
+ char *p;
+ newp = strdup(part);
+ if(!newp)
+ return CURLUE_OUT_OF_MEMORY;
+ p = (char *)newp;
+ while(*p) {
+ /* make sure percent encoded are lower case */
+ if((*p == '%') && ISXDIGIT(p[1]) && ISXDIGIT(p[2]) &&
+ (ISUPPER(p[1]) || ISUPPER(p[2]))) {
+ p[1] = Curl_raw_tolower(p[1]);
+ p[2] = Curl_raw_tolower(p[2]);
+ p += 3;
+ }
+ else
+ p++;
+ }
+ }
+
+ if(appendquery) {
+ /* Append the 'newp' string onto the old query. Add a '&' separator if
+ none is present at the end of the existing query already */
+
+ size_t querylen = u->query ? strlen(u->query) : 0;
+ bool addamperand = querylen && (u->query[querylen -1] != '&');
+ if(querylen) {
+ struct dynbuf enc;
+ Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+
+ if(Curl_dyn_addn(&enc, u->query, querylen)) /* add original query */
+ goto nomem;
+
+ if(addamperand) {
+ if(Curl_dyn_addn(&enc, "&", 1))
+ goto nomem;
+ }
+ if(Curl_dyn_add(&enc, newp))
+ goto nomem;
+ free((char *)newp);
+ free(*storep);
+ *storep = Curl_dyn_ptr(&enc);
+ return CURLUE_OK;
+ nomem:
+ free((char *)newp);
+ return CURLUE_OUT_OF_MEMORY;
+ }
+ }
+
+ if(what == CURLUPART_HOST) {
+ size_t n = strlen(newp);
+ if(!n && (flags & CURLU_NO_AUTHORITY)) {
+ /* Skip hostname check, it's allowed to be empty. */
+ }
+ else {
+ if(hostname_check(u, (char *)newp, n)) {
+ free((char *)newp);
+ return CURLUE_BAD_HOSTNAME;
+ }
+ }
+ }
+
+ free(*storep);
+ *storep = (char *)newp;
+ }
+ /* set after the string, to make it not assigned if the allocation above
+ fails */
+ if(port)
+ u->portnum = port;
+ return CURLUE_OK;
+}
diff --git a/libs/libcurl/src/urldata.h b/libs/libcurl/src/urldata.h index 1d430b5e88..7f90ecf529 100644 --- a/libs/libcurl/src/urldata.h +++ b/libs/libcurl/src/urldata.h @@ -1,2018 +1,1984 @@ -#ifndef HEADER_CURL_URLDATA_H -#define HEADER_CURL_URLDATA_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 for lib internal stuff */ - -#include "curl_setup.h" - -#define PORT_FTP 21 -#define PORT_FTPS 990 -#define PORT_TELNET 23 -#define PORT_HTTP 80 -#define PORT_HTTPS 443 -#define PORT_DICT 2628 -#define PORT_LDAP 389 -#define PORT_LDAPS 636 -#define PORT_TFTP 69 -#define PORT_SSH 22 -#define PORT_IMAP 143 -#define PORT_IMAPS 993 -#define PORT_POP3 110 -#define PORT_POP3S 995 -#define PORT_SMB 445 -#define PORT_SMBS 445 -#define PORT_SMTP 25 -#define PORT_SMTPS 465 /* sometimes called SSMTP */ -#define PORT_RTSP 554 -#define PORT_RTMP 1935 -#define PORT_RTMPT PORT_HTTP -#define PORT_RTMPS PORT_HTTPS -#define PORT_GOPHER 70 -#define PORT_MQTT 1883 - -#ifdef USE_WEBSOCKETS -/* CURLPROTO_GOPHERS (29) is the highest publicly used protocol bit number, - * the rest are internal information. If we use higher bits we only do this on - * platforms that have a >= 64 bit type and then we use such a type for the - * protocol fields in the protocol handler. - */ -#define CURLPROTO_WS (1<<30) -#define CURLPROTO_WSS ((curl_prot_t)1<<31) -#else -#define CURLPROTO_WS 0 -#define CURLPROTO_WSS 0 -#endif - -/* This should be undefined once we need bit 32 or higher */ -#define PROTO_TYPE_SMALL - -#ifndef PROTO_TYPE_SMALL -typedef curl_off_t curl_prot_t; -#else -typedef unsigned int curl_prot_t; -#endif - -/* This mask is for all the old protocols that are provided and defined in the - public header and shall exclude protocols added since which are not exposed - in the API */ -#define CURLPROTO_MASK (0x3ffffff) - -#define DICT_MATCH "/MATCH:" -#define DICT_MATCH2 "/M:" -#define DICT_MATCH3 "/FIND:" -#define DICT_DEFINE "/DEFINE:" -#define DICT_DEFINE2 "/D:" -#define DICT_DEFINE3 "/LOOKUP:" - -#define CURL_DEFAULT_USER "anonymous" -#define CURL_DEFAULT_PASSWORD "ftp@example.com" - -/* Convenience defines for checking protocols or their SSL based version. Each - protocol handler should only ever have a single CURLPROTO_ in its protocol - field. */ -#define PROTO_FAMILY_HTTP (CURLPROTO_HTTP|CURLPROTO_HTTPS|CURLPROTO_WS| \ - CURLPROTO_WSS) -#define PROTO_FAMILY_FTP (CURLPROTO_FTP|CURLPROTO_FTPS) -#define PROTO_FAMILY_POP3 (CURLPROTO_POP3|CURLPROTO_POP3S) -#define PROTO_FAMILY_SMB (CURLPROTO_SMB|CURLPROTO_SMBS) -#define PROTO_FAMILY_SMTP (CURLPROTO_SMTP|CURLPROTO_SMTPS) -#define PROTO_FAMILY_SSH (CURLPROTO_SCP|CURLPROTO_SFTP) - -#define DEFAULT_CONNCACHE_SIZE 5 - -/* length of longest IPv6 address string including the trailing null */ -#define MAX_IPADR_LEN sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") - -/* Default FTP/IMAP etc response timeout in milliseconds */ -#define RESP_TIMEOUT (120*1000) - -/* Max string input length is a precaution against abuse and to detect junk - input easier and better. */ -#define CURL_MAX_INPUT_LENGTH 8000000 - -#include "cookie.h" -#include "psl.h" -#include "formdata.h" - -#ifdef HAVE_NETINET_IN_H -#include <netinet/in.h> -#endif -#ifdef HAVE_NETINET_IN6_H -#include <netinet/in6.h> -#endif - -#include "timeval.h" - -#include <curl/curl.h> - -#include "http_chunks.h" /* for the structs and enum stuff */ -#include "hostip.h" -#include "hash.h" -#include "splay.h" -#include "dynbuf.h" - -/* return the count of bytes sent, or -1 on error */ -typedef ssize_t (Curl_send)(struct Curl_easy *data, /* transfer */ - int sockindex, /* socketindex */ - const void *buf, /* data to write */ - size_t len, /* max amount to write */ - CURLcode *err); /* error to return */ - -/* return the count of bytes read, or -1 on error */ -typedef ssize_t (Curl_recv)(struct Curl_easy *data, /* transfer */ - int sockindex, /* socketindex */ - char *buf, /* store data here */ - size_t len, /* max amount to read */ - CURLcode *err); /* error to return */ - -#ifdef USE_HYPER -typedef CURLcode (*Curl_datastream)(struct Curl_easy *data, - struct connectdata *conn, - int *didwhat, - bool *done, - int select_res); -#endif - -#include "mime.h" -#include "imap.h" -#include "pop3.h" -#include "smtp.h" -#include "ftp.h" -#include "file.h" -#include "vssh/ssh.h" -#include "http.h" -#include "rtsp.h" -#include "smb.h" -#include "mqtt.h" -#include "wildcard.h" -#include "multihandle.h" -#include "quic.h" -#include "c-hyper.h" - -#ifdef HAVE_GSSAPI -# ifdef HAVE_GSSGNU -# include <gss.h> -# elif defined HAVE_GSSAPI_GSSAPI_H -# include <gssapi/gssapi.h> -# else -# include <gssapi.h> -# endif -# ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H -# include <gssapi/gssapi_generic.h> -# endif -#endif - -#ifdef USE_LIBSSH2 -#include <libssh2.h> -#include <libssh2_sftp.h> -#endif /* USE_LIBSSH2 */ - -#define READBUFFER_SIZE CURL_MAX_WRITE_SIZE -#define READBUFFER_MAX CURL_MAX_READ_SIZE -#define READBUFFER_MIN 1024 - -/* The default upload buffer size, should not be smaller than - CURL_MAX_WRITE_SIZE, as it needs to hold a full buffer as could be sent in - a write callback. - - The size was 16KB for many years but was bumped to 64KB because it makes - libcurl able to do significantly faster uploads in some circumstances. Even - larger buffers can help further, but this is deemed a fair memory/speed - compromise. */ -#define UPLOADBUFFER_DEFAULT 65536 -#define UPLOADBUFFER_MAX (2*1024*1024) -#define UPLOADBUFFER_MIN CURL_MAX_WRITE_SIZE - -#define CURLEASY_MAGIC_NUMBER 0xc0dedbadU -#define GOOD_EASY_HANDLE(x) \ - ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) - -#ifdef HAVE_GSSAPI -/* Types needed for krb5-ftp connections */ -struct krb5buffer { - void *data; - size_t size; - size_t index; - BIT(eof_flag); -}; - -enum protection_level { - PROT_NONE, /* first in list */ - PROT_CLEAR, - PROT_SAFE, - PROT_CONFIDENTIAL, - PROT_PRIVATE, - PROT_CMD, - PROT_LAST /* last in list */ -}; -#endif - -/* enum for the nonblocking SSL connection state machine */ -typedef enum { - ssl_connect_1, - ssl_connect_2, - ssl_connect_2_reading, - ssl_connect_2_writing, - ssl_connect_3, - ssl_connect_done -} ssl_connect_state; - -typedef enum { - ssl_connection_none, - ssl_connection_negotiating, - ssl_connection_complete -} ssl_connection_state; - -/* SSL backend-specific data; declared differently by each SSL backend */ -struct ssl_backend_data; - -/* struct for data related to each SSL connection */ -struct ssl_connect_data { - ssl_connection_state state; - ssl_connect_state connecting_state; -#if defined(USE_SSL) - struct ssl_backend_data *backend; -#endif - /* Use ssl encrypted communications TRUE/FALSE. The library is not - necessarily using ssl at the moment but at least asked to or means to use - it. See 'state' for the exact current state of the connection. */ - BIT(use); -}; - -struct ssl_primary_config { - long version; /* what version the client wants to use */ - long version_max; /* max supported version the client wants to use*/ - char *CApath; /* certificate dir (doesn't work on windows) */ - char *CAfile; /* certificate to verify peer against */ - char *issuercert; /* optional issuer certificate filename */ - char *clientcert; - char *cipher_list; /* list of ciphers to use */ - char *cipher_list13; /* list of TLS 1.3 cipher suites to use */ - char *pinned_key; - char *CRLfile; /* CRL to check certificate revocation */ - struct curl_blob *cert_blob; - struct curl_blob *ca_info_blob; - struct curl_blob *issuercert_blob; -#ifdef USE_TLS_SRP - char *username; /* TLS username (for, e.g., SRP) */ - char *password; /* TLS password (for, e.g., SRP) */ - enum CURL_TLSAUTH authtype; /* TLS authentication type (default SRP) */ -#endif - char *curves; /* list of curves to use */ - unsigned char ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */ - BIT(verifypeer); /* set TRUE if this is desired */ - BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */ - BIT(verifystatus); /* set TRUE if certificate status must be checked */ - BIT(sessionid); /* cache session IDs or not */ -}; - -struct ssl_config_data { - struct ssl_primary_config primary; - long certverifyresult; /* result from the certificate verification */ - curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */ - void *fsslctxp; /* parameter for call back */ - char *cert_type; /* format for certificate (default: PEM)*/ - char *key; /* private key file name */ - struct curl_blob *key_blob; - char *key_type; /* format for private key (default: PEM) */ - char *key_passwd; /* plain text private key password */ - BIT(certinfo); /* gather lots of certificate info */ - BIT(falsestart); - BIT(enable_beast); /* allow this flaw for interoperability's sake*/ - BIT(no_revoke); /* disable SSL certificate revocation checks */ - BIT(no_partialchain); /* don't accept partial certificate chains */ - BIT(revoke_best_effort); /* ignore SSL revocation offline/missing revocation - list errors */ - BIT(native_ca_store); /* use the native ca store of operating system */ - BIT(auto_client_cert); /* automatically locate and use a client - certificate for authentication (Schannel) */ -}; - -struct ssl_general_config { - size_t max_ssl_sessions; /* SSL session id cache size */ -}; - -/* information stored about one single SSL session */ -struct Curl_ssl_session { - char *name; /* host name for which this ID was used */ - char *conn_to_host; /* host name for the connection (may be NULL) */ - const char *scheme; /* protocol scheme used */ - void *sessionid; /* as returned from the SSL layer */ - size_t idsize; /* if known, otherwise 0 */ - long age; /* just a number, the higher the more recent */ - int remote_port; /* remote port */ - int conn_to_port; /* remote port for the connection (may be -1) */ - struct ssl_primary_config ssl_config; /* setup for this session */ -}; - -#ifdef USE_WINDOWS_SSPI -#include "curl_sspi.h" -#endif - -/* Struct used for Digest challenge-response authentication */ -struct digestdata { -#if defined(USE_WINDOWS_SSPI) - BYTE *input_token; - size_t input_token_len; - CtxtHandle *http_context; - /* copy of user/passwd used to make the identity for http_context. - either may be NULL. */ - char *user; - char *passwd; -#else - char *nonce; - char *cnonce; - char *realm; - char *opaque; - char *qop; - char *algorithm; - int nc; /* nonce count */ - unsigned char algo; - BIT(stale); /* set true for re-negotiation */ - BIT(userhash); -#endif -}; - -typedef enum { - NTLMSTATE_NONE, - NTLMSTATE_TYPE1, - NTLMSTATE_TYPE2, - NTLMSTATE_TYPE3, - NTLMSTATE_LAST -} curlntlm; - -typedef enum { - GSS_AUTHNONE, - GSS_AUTHRECV, - GSS_AUTHSENT, - GSS_AUTHDONE, - GSS_AUTHSUCC -} curlnegotiate; - -/* Struct used for GSSAPI (Kerberos V5) authentication */ -#if defined(USE_KERBEROS5) -struct kerberos5data { -#if defined(USE_WINDOWS_SSPI) - CredHandle *credentials; - CtxtHandle *context; - TCHAR *spn; - SEC_WINNT_AUTH_IDENTITY identity; - SEC_WINNT_AUTH_IDENTITY *p_identity; - size_t token_max; - BYTE *output_token; -#else - gss_ctx_id_t context; - gss_name_t spn; -#endif -}; -#endif - -/* Struct used for SCRAM-SHA-1 authentication */ -#ifdef USE_GSASL -#include <gsasl.h> -struct gsasldata { - Gsasl *ctx; - Gsasl_session *client; -}; -#endif - -/* Struct used for NTLM challenge-response authentication */ -#if defined(USE_NTLM) -struct ntlmdata { -#ifdef USE_WINDOWS_SSPI -/* The sslContext is used for the Schannel bindings. The - * api is available on the Windows 7 SDK and later. - */ -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS - CtxtHandle *sslContext; -#endif - CredHandle *credentials; - CtxtHandle *context; - SEC_WINNT_AUTH_IDENTITY identity; - SEC_WINNT_AUTH_IDENTITY *p_identity; - size_t token_max; - BYTE *output_token; - BYTE *input_token; - size_t input_token_len; - TCHAR *spn; -#else - unsigned int flags; - unsigned char nonce[8]; - unsigned int target_info_len; - void *target_info; /* TargetInfo received in the ntlm type-2 message */ - -#if defined(NTLM_WB_ENABLED) - /* used for communication with Samba's winbind daemon helper ntlm_auth */ - curl_socket_t ntlm_auth_hlpr_socket; - pid_t ntlm_auth_hlpr_pid; - char *challenge; /* The received base64 encoded ntlm type-2 message */ - char *response; /* The generated base64 ntlm type-1/type-3 message */ -#endif -#endif -}; -#endif - -/* Struct used for Negotiate (SPNEGO) authentication */ -#ifdef USE_SPNEGO -struct negotiatedata { -#ifdef HAVE_GSSAPI - OM_uint32 status; - gss_ctx_id_t context; - gss_name_t spn; - gss_buffer_desc output_token; -#else -#ifdef USE_WINDOWS_SSPI -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS - CtxtHandle *sslContext; -#endif - DWORD status; - CredHandle *credentials; - CtxtHandle *context; - SEC_WINNT_AUTH_IDENTITY identity; - SEC_WINNT_AUTH_IDENTITY *p_identity; - TCHAR *spn; - size_t token_max; - BYTE *output_token; - size_t output_token_length; -#endif -#endif - BIT(noauthpersist); - BIT(havenoauthpersist); - BIT(havenegdata); - BIT(havemultiplerequests); -}; -#endif - -#ifdef CURL_DISABLE_PROXY -#define CONN_IS_PROXIED(x) 0 -#else -#define CONN_IS_PROXIED(x) x->bits.proxy -#endif - -/* - * Boolean values that concerns this connection. - */ -struct ConnectBits { - bool tcpconnect[2]; /* the TCP layer (or similar) is connected, this is set - the first time on the first connect function call */ -#ifndef CURL_DISABLE_PROXY - bool proxy_ssl_connected[2]; /* TRUE when SSL initialization for HTTPS proxy - is complete */ - BIT(httpproxy); /* if set, this transfer is done through a http proxy */ - BIT(socksproxy); /* if set, this transfer is done through a socks proxy */ - BIT(proxy_user_passwd); /* user+password for the proxy? */ - BIT(tunnel_proxy); /* if CONNECT is used to "tunnel" through the proxy. - This is implicit when SSL-protocols are used through - proxies, but can also be enabled explicitly by - apps */ - BIT(proxy_connect_closed); /* TRUE if a proxy disconnected the connection - in a CONNECT request with auth, so that - libcurl should reconnect and continue. */ - BIT(proxy); /* if set, this transfer is done through a proxy - any type */ -#endif - /* always modify bits.close with the connclose() and connkeep() macros! */ - BIT(close); /* if set, we close the connection after this request */ - BIT(reuse); /* if set, this is a re-used connection */ - BIT(altused); /* this is an alt-svc "redirect" */ - BIT(conn_to_host); /* if set, this connection has a "connect to host" - that overrides the host in the URL */ - BIT(conn_to_port); /* if set, this connection has a "connect to port" - that overrides the port in the URL (remote port) */ - BIT(ipv6_ip); /* we communicate with a remote site specified with pure IPv6 - IP address */ - BIT(ipv6); /* we communicate with a site using an IPv6 address */ - BIT(do_more); /* this is set TRUE if the ->curl_do_more() function is - supposed to be called, after ->curl_do() */ - BIT(protoconnstart);/* the protocol layer has STARTED its operation after - the TCP layer connect */ - BIT(retry); /* this connection is about to get closed and then - re-attempted at another connection. */ - BIT(authneg); /* TRUE when the auth phase has started, which means - that we are creating a request with an auth header, - but it is not the final request in the auth - negotiation. */ - BIT(rewindaftersend);/* TRUE when the sending couldn't be stopped even - though it will be discarded. When the whole send - operation is done, we must call the data rewind - callback. */ -#ifndef CURL_DISABLE_FTP - BIT(ftp_use_epsv); /* As set with CURLOPT_FTP_USE_EPSV, but if we find out - EPSV doesn't work we disable it for the forthcoming - requests */ - BIT(ftp_use_eprt); /* As set with CURLOPT_FTP_USE_EPRT, but if we find out - EPRT doesn't work we disable it for the forthcoming - requests */ - BIT(ftp_use_data_ssl); /* Enabled SSL for the data connection */ - BIT(ftp_use_control_ssl); /* Enabled SSL for the control connection */ -#endif -#ifndef CURL_DISABLE_NETRC - BIT(netrc); /* name+password provided by netrc */ -#endif - BIT(bound); /* set true if bind() has already been done on this socket/ - connection */ - BIT(multiplex); /* connection is multiplexed */ - BIT(tcp_fastopen); /* use TCP Fast Open */ - BIT(tls_enable_alpn); /* TLS ALPN extension? */ -#ifndef CURL_DISABLE_DOH - BIT(doh); -#endif -#ifdef USE_UNIX_SOCKETS - BIT(abstract_unix_socket); -#endif - BIT(tls_upgraded); - BIT(sock_accepted); /* TRUE if the SECONDARYSOCKET was created with - accept() */ - BIT(parallel_connect); /* set TRUE when a parallel connect attempt has - started (happy eyeballs) */ -}; - -struct hostname { - char *rawalloc; /* allocated "raw" version of the name */ - char *encalloc; /* allocated IDN-encoded version of the name */ - char *name; /* name to use internally, might be encoded, might be raw */ - const char *dispname; /* name to display, as 'name' might be encoded */ -}; - -/* - * Flags on the keepon member of the Curl_transfer_keeper - */ - -#define KEEP_NONE 0 -#define KEEP_RECV (1<<0) /* there is or may be data to read */ -#define KEEP_SEND (1<<1) /* there is or may be data to write */ -#define KEEP_RECV_HOLD (1<<2) /* when set, no reading should be done but there - might still be data to read */ -#define KEEP_SEND_HOLD (1<<3) /* when set, no writing should be done but there - might still be data to write */ -#define KEEP_RECV_PAUSE (1<<4) /* reading is paused */ -#define KEEP_SEND_PAUSE (1<<5) /* writing is paused */ - -#define KEEP_RECVBITS (KEEP_RECV | KEEP_RECV_HOLD | KEEP_RECV_PAUSE) -#define KEEP_SENDBITS (KEEP_SEND | KEEP_SEND_HOLD | KEEP_SEND_PAUSE) - -#if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH) -#define USE_CURL_ASYNC -struct Curl_async { - char *hostname; - struct Curl_dns_entry *dns; - struct thread_data *tdata; - void *resolver; /* resolver state, if it is used in the URL state - - ares_channel e.g. */ - int port; - int status; /* if done is TRUE, this is the status from the callback */ - BIT(done); /* set TRUE when the lookup is complete */ -}; - -#endif - -#define FIRSTSOCKET 0 -#define SECONDARYSOCKET 1 - -enum expect100 { - EXP100_SEND_DATA, /* enough waiting, just send the body now */ - EXP100_AWAITING_CONTINUE, /* waiting for the 100 Continue header */ - EXP100_SENDING_REQUEST, /* still sending the request but will wait for - the 100 header once done with the request */ - EXP100_FAILED /* used on 417 Expectation Failed */ -}; - -enum upgrade101 { - UPGR101_INIT, /* default state */ - UPGR101_WS, /* upgrade to WebSockets requested */ - UPGR101_H2, /* upgrade to HTTP/2 requested */ - UPGR101_RECEIVED, /* 101 response received */ - UPGR101_WORKING /* talking upgraded protocol */ -}; - -enum doh_slots { - /* Explicit values for first two symbols so as to match hard-coded - * constants in existing code - */ - DOH_PROBE_SLOT_IPADDR_V4 = 0, /* make 'V4' stand out for readability */ - DOH_PROBE_SLOT_IPADDR_V6 = 1, /* 'V6' likewise */ - - /* Space here for (possibly build-specific) additional slot definitions */ - - /* for example */ - /* #ifdef WANT_DOH_FOOBAR_TXT */ - /* DOH_PROBE_SLOT_FOOBAR_TXT, */ - /* #endif */ - - /* AFTER all slot definitions, establish how many we have */ - DOH_PROBE_SLOTS -}; - -/* - * Request specific data in the easy handle (Curl_easy). Previously, - * these members were on the connectdata struct but since a conn struct may - * now be shared between different Curl_easys, we store connection-specific - * data here. This struct only keeps stuff that's interesting for *this* - * request, as it will be cleared between multiple ones - */ -struct SingleRequest { - curl_off_t size; /* -1 if unknown at this point */ - curl_off_t maxdownload; /* in bytes, the maximum amount of data to fetch, - -1 means unlimited */ - curl_off_t bytecount; /* total number of bytes read */ - curl_off_t writebytecount; /* number of bytes written */ - - curl_off_t headerbytecount; /* only count received headers */ - curl_off_t deductheadercount; /* this amount of bytes doesn't count when we - check if anything has been transferred at - the end of a connection. We use this - counter to make only a 100 reply (without a - following second response code) result in a - CURLE_GOT_NOTHING error code */ - - curl_off_t pendingheader; /* this many bytes left to send is actually - header and not body */ - struct curltime start; /* transfer started at this time */ - struct curltime now; /* current time */ - enum { - HEADER_NORMAL, /* no bad header at all */ - HEADER_PARTHEADER, /* part of the chunk is a bad header, the rest - is normal data */ - HEADER_ALLBAD /* all was believed to be header */ - } badheader; /* the header was deemed bad and will be - written as body */ - int headerline; /* counts header lines to better track the - first one */ - char *str; /* within buf */ - curl_off_t offset; /* possible resume offset read from the - Content-Range: header */ - int httpcode; /* error code from the 'HTTP/1.? XXX' or - 'RTSP/1.? XXX' line */ - int keepon; - struct curltime start100; /* time stamp to wait for the 100 code from */ - enum expect100 exp100; /* expect 100 continue state */ - enum upgrade101 upgr101; /* 101 upgrade state */ - - /* Content unencoding stack. See sec 3.5, RFC2616. */ - struct contenc_writer *writer_stack; - time_t timeofdoc; - long bodywrites; - char *location; /* This points to an allocated version of the Location: - header data */ - char *newurl; /* Set to the new URL to use when a redirect or a retry is - wanted */ - - /* 'upload_present' is used to keep a byte counter of how much data there is - still left in the buffer, aimed for upload. */ - ssize_t upload_present; - - /* 'upload_fromhere' is used as a read-pointer when we uploaded parts of a - buffer, so the next read should read from where this pointer points to, - and the 'upload_present' contains the number of bytes available at this - position */ - char *upload_fromhere; - - /* Allocated protocol-specific data. Each protocol handler makes sure this - points to data it needs. */ - union { - struct FILEPROTO *file; - struct FTP *ftp; - struct HTTP *http; - struct IMAP *imap; - struct ldapreqinfo *ldap; - struct MQTT *mqtt; - struct POP3 *pop3; - struct RTSP *rtsp; - struct smb_request *smb; - struct SMTP *smtp; - struct SSHPROTO *ssh; - struct TELNET *telnet; - } p; -#ifndef CURL_DISABLE_DOH - struct dohdata *doh; /* DoH specific data for this request */ -#endif - unsigned char setcookies; - BIT(header); /* incoming data has HTTP header */ - BIT(content_range); /* set TRUE if Content-Range: was found */ - BIT(upload_done); /* set to TRUE when doing chunked transfer-encoding - upload and we're uploading the last chunk */ - BIT(ignorebody); /* we read a response-body but we ignore it! */ - BIT(http_bodyless); /* HTTP response status code is between 100 and 199, - 204 or 304 */ - BIT(chunk); /* if set, this is a chunked transfer-encoding */ - BIT(ignore_cl); /* ignore content-length */ - BIT(upload_chunky); /* set TRUE if we are doing chunked transfer-encoding - on upload */ - BIT(getheader); /* TRUE if header parsing is wanted */ - BIT(forbidchunk); /* used only to explicitly forbid chunk-upload for - specific upload buffers. See readmoredata() in http.c - for details. */ -}; - -/* - * Specific protocol handler. - */ - -struct Curl_handler { - const char *scheme; /* URL scheme name. */ - - /* Complement to setup_connection_internals(). This is done before the - transfer "owns" the connection. */ - CURLcode (*setup_connection)(struct Curl_easy *data, - struct connectdata *conn); - - /* These two functions MUST be set to be protocol dependent */ - CURLcode (*do_it)(struct Curl_easy *data, bool *done); - CURLcode (*done)(struct Curl_easy *, CURLcode, bool); - - /* If the curl_do() function is better made in two halves, this - * curl_do_more() function will be called afterwards, if set. For example - * for doing the FTP stuff after the PASV/PORT command. - */ - CURLcode (*do_more)(struct Curl_easy *, int *); - - /* This function *MAY* be set to a protocol-dependent function that is run - * after the connect() and everything is done, as a step in the connection. - * The 'done' pointer points to a bool that should be set to TRUE if the - * function completes before return. If it doesn't complete, the caller - * should call the curl_connecting() function until it is. - */ - CURLcode (*connect_it)(struct Curl_easy *data, bool *done); - - /* See above. */ - CURLcode (*connecting)(struct Curl_easy *data, bool *done); - CURLcode (*doing)(struct Curl_easy *data, bool *done); - - /* Called from the multi interface during the PROTOCONNECT phase, and it - should then return a proper fd set */ - int (*proto_getsock)(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks); - - /* Called from the multi interface during the DOING phase, and it should - then return a proper fd set */ - int (*doing_getsock)(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks); - - /* Called from the multi interface during the DO_MORE phase, and it should - then return a proper fd set */ - int (*domore_getsock)(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks); - - /* Called from the multi interface during the DO_DONE, PERFORM and - WAITPERFORM phases, and it should then return a proper fd set. Not setting - this will make libcurl use the generic default one. */ - int (*perform_getsock)(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks); - - /* This function *MAY* be set to a protocol-dependent function that is run - * by the curl_disconnect(), as a step in the disconnection. If the handler - * is called because the connection has been considered dead, - * dead_connection is set to TRUE. The connection is already disassociated - * from the transfer here. - */ - CURLcode (*disconnect)(struct Curl_easy *, struct connectdata *, - bool dead_connection); - - /* If used, this function gets called from transfer.c:readwrite_data() to - allow the protocol to do extra reads/writes */ - CURLcode (*readwrite)(struct Curl_easy *data, struct connectdata *conn, - ssize_t *nread, bool *readmore); - - /* This function can perform various checks on the connection. See - CONNCHECK_* for more information about the checks that can be performed, - and CONNRESULT_* for the results that can be returned. */ - unsigned int (*connection_check)(struct Curl_easy *data, - struct connectdata *conn, - unsigned int checks_to_perform); - - /* attach() attaches this transfer to this connection */ - void (*attach)(struct Curl_easy *data, struct connectdata *conn); - - int defport; /* Default port. */ - curl_prot_t protocol; /* See CURLPROTO_* - this needs to be the single - specific protocol bit */ - curl_prot_t family; /* single bit for protocol family; basically the - non-TLS name of the protocol this is */ - unsigned int flags; /* Extra particular characteristics, see PROTOPT_* */ - -}; - -#define PROTOPT_NONE 0 /* nothing extra */ -#define PROTOPT_SSL (1<<0) /* uses SSL */ -#define PROTOPT_DUAL (1<<1) /* this protocol uses two connections */ -#define PROTOPT_CLOSEACTION (1<<2) /* need action before socket close */ -/* some protocols will have to call the underlying functions without regard to - what exact state the socket signals. IE even if the socket says "readable", - the send function might need to be called while uploading, or vice versa. -*/ -#define PROTOPT_DIRLOCK (1<<3) -#define PROTOPT_NONETWORK (1<<4) /* protocol doesn't use the network! */ -#define PROTOPT_NEEDSPWD (1<<5) /* needs a password, and if none is set it - gets a default */ -#define PROTOPT_NOURLQUERY (1<<6) /* protocol can't handle - url query strings (?foo=bar) ! */ -#define PROTOPT_CREDSPERREQUEST (1<<7) /* requires login credentials per - request instead of per connection */ -#define PROTOPT_ALPN (1<<8) /* set ALPN for this */ -#define PROTOPT_STREAM (1<<9) /* a protocol with individual logical streams */ -#define PROTOPT_URLOPTIONS (1<<10) /* allow options part in the userinfo field - of the URL */ -#define PROTOPT_PROXY_AS_HTTP (1<<11) /* allow this non-HTTP scheme over a - HTTP proxy as HTTP proxies may know - this protocol and act as a gateway */ -#define PROTOPT_WILDCARD (1<<12) /* protocol supports wildcard matching */ -#define PROTOPT_USERPWDCTRL (1<<13) /* Allow "control bytes" (< 32 ascii) in - user name and password */ -#define PROTOPT_NOTCPPROXY (1<<14) /* this protocol can't proxy over TCP */ - -#define CONNCHECK_NONE 0 /* No checks */ -#define CONNCHECK_ISDEAD (1<<0) /* Check if the connection is dead. */ -#define CONNCHECK_KEEPALIVE (1<<1) /* Perform any keepalive function. */ - -#define CONNRESULT_NONE 0 /* No extra information. */ -#define CONNRESULT_DEAD (1<<0) /* The connection is dead. */ - -#ifdef USE_RECV_BEFORE_SEND_WORKAROUND -struct postponed_data { - char *buffer; /* Temporal store for received data during - sending, must be freed */ - size_t allocated_size; /* Size of temporal store */ - size_t recv_size; /* Size of received data during sending */ - size_t recv_processed; /* Size of processed part of postponed data */ -#ifdef DEBUGBUILD - curl_socket_t bindsock;/* Structure must be bound to specific socket, - used only for DEBUGASSERT */ -#endif /* DEBUGBUILD */ -}; -#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */ - -struct proxy_info { - struct hostname host; - long port; - unsigned char proxytype; /* curl_proxytype: what kind of proxy that is in - use */ - char *user; /* proxy user name string, allocated */ - char *passwd; /* proxy password string, allocated */ -}; - -struct ldapconninfo; -struct http_connect_state; - -/* for the (SOCKS) connect state machine */ -enum connect_t { - CONNECT_INIT, - CONNECT_SOCKS_INIT, /* 1 */ - CONNECT_SOCKS_SEND, /* 2 waiting to send more first data */ - CONNECT_SOCKS_READ_INIT, /* 3 set up read */ - CONNECT_SOCKS_READ, /* 4 read server response */ - CONNECT_GSSAPI_INIT, /* 5 */ - CONNECT_AUTH_INIT, /* 6 setup outgoing auth buffer */ - CONNECT_AUTH_SEND, /* 7 send auth */ - CONNECT_AUTH_READ, /* 8 read auth response */ - CONNECT_REQ_INIT, /* 9 init SOCKS "request" */ - CONNECT_RESOLVING, /* 10 */ - CONNECT_RESOLVED, /* 11 */ - CONNECT_RESOLVE_REMOTE, /* 12 */ - CONNECT_REQ_SEND, /* 13 */ - CONNECT_REQ_SENDING, /* 14 */ - CONNECT_REQ_READ, /* 15 */ - CONNECT_REQ_READ_MORE, /* 16 */ - CONNECT_DONE /* 17 connected fine to the remote or the SOCKS proxy */ -}; - -#define SOCKS_STATE(x) (((x) >= CONNECT_SOCKS_INIT) && \ - ((x) < CONNECT_DONE)) - -struct connstate { - enum connect_t state; - ssize_t outstanding; /* send this many bytes more */ - unsigned char *outp; /* send from this pointer */ -}; - -#define TRNSPRT_TCP 3 -#define TRNSPRT_UDP 4 -#define TRNSPRT_QUIC 5 -#define TRNSPRT_UNIX 6 - -/* - * The connectdata struct contains all fields and variables that should be - * unique for an entire connection. - */ -struct connectdata { - struct connstate cnnct; - struct Curl_llist_element bundle_node; /* conncache */ - - /* chunk is for HTTP chunked encoding, but is in the general connectdata - struct only because we can do just about any protocol through a HTTP proxy - and a HTTP proxy may in fact respond using chunked encoding */ - struct Curl_chunker chunk; - - curl_closesocket_callback fclosesocket; /* function closing the socket(s) */ - void *closesocket_client; - - /* This is used by the connection cache logic. If this returns TRUE, this - handle is still used by one or more easy handles and can only used by any - other easy handle without careful consideration (== only for - multiplexing) and it cannot be used by another multi handle! */ -#define CONN_INUSE(c) ((c)->easyq.size) - - /**** Fields set when inited and not modified again */ - long connection_id; /* Contains a unique number to make it easier to - track the connections in the log output */ - - /* 'dns_entry' is the particular host we use. This points to an entry in the - DNS cache and it will not get pruned while locked. It gets unlocked in - multi_done(). This entry will be NULL if the connection is re-used as then - there is no name resolve done. */ - struct Curl_dns_entry *dns_entry; - - /* 'ip_addr' is the particular IP we connected to. It points to a struct - within the DNS cache, so this pointer is only valid as long as the DNS - cache entry remains locked. It gets unlocked in multi_done() */ - struct Curl_addrinfo *ip_addr; - struct Curl_addrinfo *tempaddr[2]; /* for happy eyeballs */ - -#ifdef ENABLE_QUIC - struct quicsocket hequic[2]; /* two, for happy eyeballs! */ - struct quicsocket *quic; -#endif - - struct hostname host; - char *hostname_resolve; /* host name to resolve to address, allocated */ - char *secondaryhostname; /* secondary socket host name (ftp) */ - struct hostname conn_to_host; /* the host to connect to. valid only if - bits.conn_to_host is set */ -#ifndef CURL_DISABLE_PROXY - struct proxy_info socks_proxy; - struct proxy_info http_proxy; -#endif - /* 'primary_ip' and 'primary_port' get filled with peer's numerical - ip address and port number whenever an outgoing connection is - *attempted* from the primary socket to a remote address. When more - than one address is tried for a connection these will hold data - for the last attempt. When the connection is actually established - these are updated with data which comes directly from the socket. */ - - char primary_ip[MAX_IPADR_LEN]; - char *user; /* user name string, allocated */ - char *passwd; /* password string, allocated */ - char *options; /* options string, allocated */ - char *sasl_authzid; /* authorization identity string, allocated */ - char *oauth_bearer; /* OAUTH2 bearer, allocated */ - struct curltime now; /* "current" time */ - struct curltime created; /* creation time */ - struct curltime lastused; /* when returned to the connection cache */ - curl_socket_t sock[2]; /* two sockets, the second is used for the data - transfer when doing FTP */ - curl_socket_t tempsock[2]; /* temporary sockets for happy eyeballs */ - int tempfamily[2]; /* family used for the temp sockets */ - Curl_recv *recv[2]; - Curl_send *send[2]; - -#ifdef USE_RECV_BEFORE_SEND_WORKAROUND - struct postponed_data postponed[2]; /* two buffers for two sockets */ -#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */ - struct ssl_connect_data ssl[2]; /* this is for ssl-stuff */ -#ifndef CURL_DISABLE_PROXY - struct ssl_connect_data proxy_ssl[2]; /* this is for proxy ssl-stuff */ -#endif -#ifdef USE_SSL - void *ssl_extra; /* separately allocated backend-specific data */ -#endif - struct ssl_primary_config ssl_config; -#ifndef CURL_DISABLE_PROXY - struct ssl_primary_config proxy_ssl_config; -#endif - struct ConnectBits bits; /* various state-flags for this connection */ - - /* connecttime: when connect() is called on the current IP address. Used to - be able to track when to move on to try next IP - but only when the multi - interface is used. */ - struct curltime connecttime; - - /* The field below gets set in Curl_connecthost */ - /* how long time in milliseconds to spend on trying to connect to each IP - address, per family */ - timediff_t timeoutms_per_addr[2]; - - const struct Curl_handler *handler; /* Connection's protocol handler */ - const struct Curl_handler *given; /* The protocol first given */ - - /* Protocols can use a custom keepalive mechanism to keep connections alive. - This allows those protocols to track the last time the keepalive mechanism - was used on this connection. */ - struct curltime keepalive; - - /**** curl_get() phase fields */ - - curl_socket_t sockfd; /* socket to read from or CURL_SOCKET_BAD */ - curl_socket_t writesockfd; /* socket to write to, it may very - well be the same we read from. - CURL_SOCKET_BAD disables */ - -#ifdef HAVE_GSSAPI - BIT(sec_complete); /* if Kerberos is enabled for this connection */ - unsigned char command_prot; /* enum protection_level */ - unsigned char data_prot; /* enum protection_level */ - unsigned char request_data_prot; /* enum protection_level */ - size_t buffer_size; - struct krb5buffer in_buffer; - void *app_data; - const struct Curl_sec_client_mech *mech; - struct sockaddr_in local_addr; -#endif - -#if defined(USE_KERBEROS5) /* Consider moving some of the above GSS-API */ - struct kerberos5data krb5; /* variables into the structure definition, */ -#endif /* however, some of them are ftp specific. */ - - struct Curl_llist easyq; /* List of easy handles using this connection */ - curl_seek_callback seek_func; /* function that seeks the input */ - void *seek_client; /* pointer to pass to the seek() above */ - - /*************** Request - specific items ************/ -#if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS) - CtxtHandle *sslContext; -#endif - -#ifdef USE_GSASL - struct gsasldata gsasl; -#endif - -#if defined(USE_NTLM) - curlntlm http_ntlm_state; - curlntlm proxy_ntlm_state; - - struct ntlmdata ntlm; /* NTLM differs from other authentication schemes - because it authenticates connections, not - single requests! */ - struct ntlmdata proxyntlm; /* NTLM data for proxy */ -#endif - -#ifdef USE_SPNEGO - curlnegotiate http_negotiate_state; - curlnegotiate proxy_negotiate_state; - - struct negotiatedata negotiate; /* state data for host Negotiate auth */ - struct negotiatedata proxyneg; /* state data for proxy Negotiate auth */ -#endif - - /* for chunked-encoded trailer */ - struct dynbuf trailer; - - union { -#ifndef CURL_DISABLE_FTP - struct ftp_conn ftpc; -#endif -#ifndef CURL_DISABLE_HTTP - struct http_conn httpc; -#endif -#ifdef USE_SSH - struct ssh_conn sshc; -#endif -#ifndef CURL_DISABLE_TFTP - struct tftp_state_data *tftpc; -#endif -#ifndef CURL_DISABLE_IMAP - struct imap_conn imapc; -#endif -#ifndef CURL_DISABLE_POP3 - struct pop3_conn pop3c; -#endif -#ifndef CURL_DISABLE_SMTP - struct smtp_conn smtpc; -#endif -#ifndef CURL_DISABLE_RTSP - struct rtsp_conn rtspc; -#endif -#ifndef CURL_DISABLE_SMB - struct smb_conn smbc; -#endif - void *rtmp; - struct ldapconninfo *ldapc; -#ifndef CURL_DISABLE_MQTT - struct mqtt_conn mqtt; -#endif - } proto; - - struct http_connect_state *connect_state; /* for HTTP CONNECT */ - struct connectbundle *bundle; /* The bundle we are member of */ -#ifdef USE_UNIX_SOCKETS - char *unix_domain_socket; -#endif -#ifdef USE_HYPER - /* if set, an alternative data transfer function */ - Curl_datastream datastream; -#endif - /* When this connection is created, store the conditions for the local end - bind. This is stored before the actual bind and before any connection is - made and will serve the purpose of being used for comparison reasons so - that subsequent bound-requested connections aren't accidentally re-using - wrong connections. */ - char *localdev; - int localportrange; - int cselect_bits; /* bitmask of socket events */ - int waitfor; /* current READ/WRITE bits to wait for */ -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - int socks5_gssapi_enctype; -#endif - /* The field below gets set in Curl_connecthost */ - int num_addr; /* number of addresses to try to connect to */ - int port; /* which port to use locally - to connect to */ - int remote_port; /* the remote port, not the proxy port! */ - int conn_to_port; /* the remote port to connect to. valid only if - bits.conn_to_port is set */ -#ifdef ENABLE_IPV6 - unsigned int scope_id; /* Scope id for IPv6 */ -#endif - unsigned short localport; - unsigned short secondary_port; /* secondary socket remote port to connect to - (ftp) */ - unsigned char alpn; /* APLN TLS negotiated protocol, a CURL_HTTP_VERSION* - value */ - unsigned char transport; /* one of the TRNSPRT_* defines */ - unsigned char ip_version; /* copied from the Curl_easy at creation time */ - unsigned char httpversion; /* the HTTP version*10 reported by the server */ - unsigned char connect_only; -}; - -/* The end of connectdata. */ - -/* - * Struct to keep statistical and informational data. - * All variables in this struct must be initialized/reset in Curl_initinfo(). - */ -struct PureInfo { - int httpcode; /* Recent HTTP, FTP, RTSP or SMTP response code */ - int httpproxycode; /* response code from proxy when received separate */ - int httpversion; /* the http version number X.Y = X*10+Y */ - time_t filetime; /* If requested, this is might get set. Set to -1 if the - time was unretrievable. */ - curl_off_t header_size; /* size of read header(s) in bytes */ - curl_off_t request_size; /* the amount of bytes sent in the request(s) */ - unsigned long proxyauthavail; /* what proxy auth types were announced */ - unsigned long httpauthavail; /* what host auth types were announced */ - long numconnects; /* how many new connection did libcurl created */ - char *contenttype; /* the content type of the object */ - char *wouldredirect; /* URL this would've been redirected to if asked to */ - curl_off_t retry_after; /* info from Retry-After: header */ - - /* PureInfo members 'conn_primary_ip', 'conn_primary_port', 'conn_local_ip' - and, 'conn_local_port' are copied over from the connectdata struct in - order to allow curl_easy_getinfo() to return this information even when - the session handle is no longer associated with a connection, and also - allow curl_easy_reset() to clear this information from the session handle - without disturbing information which is still alive, and that might be - reused, in the connection cache. */ - - char conn_primary_ip[MAX_IPADR_LEN]; - int conn_primary_port; /* this is the destination port to the connection, - which might have been a proxy */ - int conn_remote_port; /* this is the "remote port", which is the port - number of the used URL, independent of proxy or - not */ - char conn_local_ip[MAX_IPADR_LEN]; - int conn_local_port; - const char *conn_scheme; - unsigned int conn_protocol; - struct curl_certinfo certs; /* info about the certs, only populated in - OpenSSL, GnuTLS, Schannel, NSS and GSKit - builds. Asked for with CURLOPT_CERTINFO - / CURLINFO_CERTINFO */ - CURLproxycode pxcode; - BIT(timecond); /* set to TRUE if the time condition didn't match, which - thus made the document NOT get fetched */ -}; - - -struct Progress { - time_t lastshow; /* time() of the last displayed progress meter or NULL to - force redraw at next call */ - curl_off_t size_dl; /* total expected size */ - curl_off_t size_ul; /* total expected size */ - curl_off_t downloaded; /* transferred so far */ - curl_off_t uploaded; /* transferred so far */ - - curl_off_t current_speed; /* uses the currently fastest transfer */ - - int width; /* screen width at download start */ - int flags; /* see progress.h */ - - timediff_t timespent; - - curl_off_t dlspeed; - curl_off_t ulspeed; - - timediff_t t_nslookup; - timediff_t t_connect; - timediff_t t_appconnect; - timediff_t t_pretransfer; - timediff_t t_starttransfer; - timediff_t t_redirect; - - struct curltime start; - struct curltime t_startsingle; - struct curltime t_startop; - struct curltime t_acceptdata; - - - /* upload speed limit */ - struct curltime ul_limit_start; - curl_off_t ul_limit_size; - /* download speed limit */ - struct curltime dl_limit_start; - curl_off_t dl_limit_size; - -#define CURR_TIME (5 + 1) /* 6 entries for 5 seconds */ - - curl_off_t speeder[ CURR_TIME ]; - struct curltime speeder_time[ CURR_TIME ]; - int speeder_c; - BIT(callback); /* set when progress callback is used */ - BIT(is_t_startransfer_set); -}; - -typedef enum { - RTSPREQ_NONE, /* first in list */ - RTSPREQ_OPTIONS, - RTSPREQ_DESCRIBE, - RTSPREQ_ANNOUNCE, - RTSPREQ_SETUP, - RTSPREQ_PLAY, - RTSPREQ_PAUSE, - RTSPREQ_TEARDOWN, - RTSPREQ_GET_PARAMETER, - RTSPREQ_SET_PARAMETER, - RTSPREQ_RECORD, - RTSPREQ_RECEIVE, - RTSPREQ_LAST /* last in list */ -} Curl_RtspReq; - -struct auth { - unsigned long want; /* Bitmask set to the authentication methods wanted by - app (with CURLOPT_HTTPAUTH or CURLOPT_PROXYAUTH). */ - unsigned long picked; - unsigned long avail; /* Bitmask for what the server reports to support for - this resource */ - BIT(done); /* TRUE when the auth phase is done and ready to do the - actual request */ - BIT(multipass); /* TRUE if this is not yet authenticated but within the - auth multipass negotiation */ - BIT(iestyle); /* TRUE if digest should be done IE-style or FALSE if it - should be RFC compliant */ -}; - -struct Curl_http2_dep { - struct Curl_http2_dep *next; - struct Curl_easy *data; -}; - -/* - * This struct is for holding data that was attempted to get sent to the user's - * callback but is held due to pausing. One instance per type (BOTH, HEADER, - * BODY). - */ -struct tempbuf { - struct dynbuf b; - int type; /* type of the 'tempwrite' buffer as a bitmask that is used with - Curl_client_write() */ -}; - -/* Timers */ -typedef enum { - EXPIRE_100_TIMEOUT, - EXPIRE_ASYNC_NAME, - EXPIRE_CONNECTTIMEOUT, - EXPIRE_DNS_PER_NAME, /* family1 */ - EXPIRE_DNS_PER_NAME2, /* family2 */ - EXPIRE_HAPPY_EYEBALLS_DNS, /* See asyn-ares.c */ - EXPIRE_HAPPY_EYEBALLS, - EXPIRE_MULTI_PENDING, - EXPIRE_RUN_NOW, - EXPIRE_SPEEDCHECK, - EXPIRE_TIMEOUT, - EXPIRE_TOOFAST, - EXPIRE_QUIC, - EXPIRE_FTP_ACCEPT, - EXPIRE_LAST /* not an actual timer, used as a marker only */ -} expire_id; - - -typedef enum { - TRAILERS_NONE, - TRAILERS_INITIALIZED, - TRAILERS_SENDING, - TRAILERS_DONE -} trailers_state; - - -/* - * One instance for each timeout an easy handle can set. - */ -struct time_node { - struct Curl_llist_element list; - struct curltime time; - expire_id eid; -}; - -/* individual pieces of the URL */ -struct urlpieces { - char *scheme; - char *hostname; - char *port; - char *user; - char *password; - char *options; - char *path; - char *query; -}; - -struct UrlState { - /* Points to the connection cache */ - struct conncache *conn_cache; - /* buffers to store authentication data in, as parsed from input options */ - struct curltime keeps_speed; /* for the progress meter really */ - - long lastconnect_id; /* The last connection, -1 if undefined */ - struct dynbuf headerb; /* buffer to store headers in */ - - char *buffer; /* download buffer */ - char *ulbuf; /* allocated upload buffer or NULL */ - curl_off_t current_speed; /* the ProgressShow() function sets this, - bytes / second */ - - /* host name, port number and protocol of the first (not followed) request. - if set, this should be the host name that we will sent authorization to, - no else. Used to make Location: following not keep sending user+password. - This is strdup()ed data. */ - char *first_host; - int first_remote_port; - curl_prot_t first_remote_protocol; - - int retrycount; /* number of retries on a new connection */ - struct Curl_ssl_session *session; /* array of 'max_ssl_sessions' size */ - long sessionage; /* number of the most recent session */ - struct tempbuf tempwrite[3]; /* BOTH, HEADER, BODY */ - unsigned int tempcount; /* number of entries in use in tempwrite, 0 - 3 */ - int os_errno; /* filled in with errno whenever an error occurs */ - char *scratch; /* huge buffer[set.buffer_size*2] for upload CRLF replacing */ - long followlocation; /* redirect counter */ - int requests; /* request counter: redirects + authentication retakes */ -#ifdef HAVE_SIGNAL - /* storage for the previous bag^H^H^HSIGPIPE signal handler :-) */ - void (*prev_signal)(int sig); -#endif -#ifndef CURL_DISABLE_CRYPTO_AUTH - struct digestdata digest; /* state data for host Digest auth */ - struct digestdata proxydigest; /* state data for proxy Digest auth */ -#endif - struct auth authhost; /* auth details for host */ - struct auth authproxy; /* auth details for proxy */ -#ifdef USE_CURL_ASYNC - struct Curl_async async; /* asynchronous name resolver data */ -#endif - -#if defined(USE_OPENSSL) - /* void instead of ENGINE to avoid bleeding OpenSSL into this header */ - void *engine; -#endif /* USE_OPENSSL */ - struct curltime expiretime; /* set this with Curl_expire() only */ - struct Curl_tree timenode; /* for the splay stuff */ - struct Curl_llist timeoutlist; /* list of pending timeouts */ - struct time_node expires[EXPIRE_LAST]; /* nodes for each expire type */ - - /* a place to store the most recently set (S)FTP entrypath */ - char *most_recent_ftp_entrypath; - unsigned char httpwant; /* when non-zero, a specific HTTP version requested - to be used in the library's request(s) */ - unsigned char httpversion; /* the lowest HTTP version*10 reported by any - server involved in this request */ - -#if !defined(WIN32) && !defined(MSDOS) && !defined(__EMX__) -/* do FTP line-end conversions on most platforms */ -#define CURL_DO_LINEEND_CONV - /* for FTP downloads: track CRLF sequences that span blocks */ - BIT(prev_block_had_trailing_cr); - /* for FTP downloads: how many CRLFs did we converted to LFs? */ - curl_off_t crlf_conversions; -#endif - char *range; /* range, if used. See README for detailed specification on - this syntax. */ - curl_off_t resume_from; /* continue [ftp] transfer from here */ - -#ifndef CURL_DISABLE_RTSP - /* This RTSP state information survives requests and connections */ - long rtsp_next_client_CSeq; /* the session's next client CSeq */ - long rtsp_next_server_CSeq; /* the session's next server CSeq */ - long rtsp_CSeq_recv; /* most recent CSeq received */ -#endif - - curl_off_t infilesize; /* size of file to upload, -1 means unknown. - Copied from set.filesize at start of operation */ -#if defined(USE_HTTP2) || defined(USE_HTTP3) - size_t drain; /* Increased when this stream has data to read, even if its - socket is not necessarily is readable. Decreased when - checked. */ -#endif - - curl_read_callback fread_func; /* read callback/function */ - void *in; /* CURLOPT_READDATA */ -#ifdef USE_HTTP2 - struct Curl_easy *stream_depends_on; - int stream_weight; -#endif - CURLU *uh; /* URL handle for the current parsed URL */ - struct urlpieces up; - unsigned char httpreq; /* Curl_HttpReq; what kind of HTTP request (if any) - is this */ - char *url; /* work URL, copied from UserDefined */ - char *referer; /* referer string */ -#ifndef CURL_DISABLE_COOKIES - struct curl_slist *cookielist; /* list of cookie files set by - curl_easy_setopt(COOKIEFILE) calls */ -#endif - struct curl_slist *resolve; /* set to point to the set.resolve list when - this should be dealt with in pretransfer */ -#ifndef CURL_DISABLE_HTTP - size_t trailers_bytes_sent; - struct dynbuf trailers_buf; /* a buffer containing the compiled trailing - headers */ - struct Curl_llist httphdrs; /* received headers */ - struct curl_header headerout; /* for external purposes */ - struct Curl_header_store *prevhead; /* the latest added header */ - trailers_state trailers_state; /* whether we are sending trailers - and what stage are we at */ -#endif -#ifdef USE_HYPER - bool hconnect; /* set if a CONNECT request */ - CURLcode hresult; /* used to pass return codes back from hyper callbacks */ -#endif - - /* Dynamically allocated strings, MUST be freed before this struct is - killed. */ - struct dynamically_allocated_data { - char *proxyuserpwd; - char *uagent; - char *accept_encoding; - char *userpwd; - char *rangeline; - char *ref; - char *host; - char *cookiehost; - char *rtsp_transport; - char *te; /* TE: request header */ - - /* transfer credentials */ - char *user; - char *passwd; - char *proxyuser; - char *proxypasswd; - } aptr; - -#ifdef CURLDEBUG - BIT(conncache_lock); -#endif - /* when curl_easy_perform() is called, the multi handle is "owned" by - the easy handle so curl_easy_cleanup() on such an easy handle will - also close the multi handle! */ - BIT(multi_owned_by_easy); - - BIT(this_is_a_follow); /* this is a followed Location: request */ - BIT(refused_stream); /* this was refused, try again */ - BIT(errorbuf); /* Set to TRUE if the error buffer is already filled in. - This must be set to FALSE every time _easy_perform() is - called. */ - BIT(allow_port); /* Is set.use_port allowed to take effect or not. This - is always set TRUE when curl_easy_perform() is called. */ - BIT(authproblem); /* TRUE if there's some problem authenticating */ - /* set after initial USER failure, to prevent an authentication loop */ - BIT(wildcardmatch); /* enable wildcard matching */ - BIT(expect100header); /* TRUE if we added Expect: 100-continue */ - BIT(disableexpect); /* TRUE if Expect: is disabled due to a previous - 417 response */ - BIT(use_range); - BIT(rangestringalloc); /* the range string is malloc()'ed */ - BIT(done); /* set to FALSE when Curl_init_do() is called and set to TRUE - when multi_done() is called, to prevent multi_done() to get - invoked twice when the multi interface is used. */ - BIT(stream_depends_e); /* set or don't set the Exclusive bit */ - BIT(previouslypending); /* this transfer WAS in the multi->pending queue */ - BIT(cookie_engine); - BIT(prefer_ascii); /* ASCII rather than binary */ - BIT(list_only); /* list directory contents */ - BIT(url_alloc); /* URL string is malloc()'ed */ - BIT(referer_alloc); /* referer string is malloc()ed */ - BIT(wildcard_resolve); /* Set to true if any resolve change is a wildcard */ -}; - -/* - * This 'UserDefined' struct must only contain data that is set once to go - * for many (perhaps) independent connections. Values that are generated or - * calculated internally for the "session handle" MUST be defined within the - * 'struct UrlState' instead. The only exceptions MUST note the changes in - * the 'DynamicStatic' struct. - * Character pointer fields point to dynamic storage, unless otherwise stated. - */ - -struct Curl_multi; /* declared and used only in multi.c */ - -/* - * This enumeration MUST not use conditional directives (#ifdefs), new - * null terminated strings MUST be added to the enumeration immediately - * before STRING_LASTZEROTERMINATED, binary fields immediately before - * STRING_LAST. When doing so, ensure that the packages/OS400/chkstring.c - * test is updated and applicable changes for EBCDIC to ASCII conversion - * are catered for in curl_easy_setopt_ccsid() - */ -enum dupstring { - STRING_CERT, /* client certificate file name */ - STRING_CERT_PROXY, /* client certificate file name */ - STRING_CERT_TYPE, /* format for certificate (default: PEM)*/ - STRING_CERT_TYPE_PROXY, /* format for certificate (default: PEM)*/ - STRING_COOKIE, /* HTTP cookie string to send */ - STRING_COOKIEJAR, /* dump all cookies to this file */ - STRING_CUSTOMREQUEST, /* HTTP/FTP/RTSP request/method to use */ - STRING_DEFAULT_PROTOCOL, /* Protocol to use when the URL doesn't specify */ - STRING_DEVICE, /* local network interface/address to use */ - STRING_ENCODING, /* Accept-Encoding string */ - STRING_FTP_ACCOUNT, /* ftp account data */ - STRING_FTP_ALTERNATIVE_TO_USER, /* command to send if USER/PASS fails */ - STRING_FTPPORT, /* port to send with the FTP PORT command */ - STRING_KEY, /* private key file name */ - STRING_KEY_PROXY, /* private key file name */ - STRING_KEY_PASSWD, /* plain text private key password */ - STRING_KEY_PASSWD_PROXY, /* plain text private key password */ - STRING_KEY_TYPE, /* format for private key (default: PEM) */ - STRING_KEY_TYPE_PROXY, /* format for private key (default: PEM) */ - STRING_KRB_LEVEL, /* krb security level */ - STRING_NETRC_FILE, /* if not NULL, use this instead of trying to find - $HOME/.netrc */ - STRING_PROXY, /* proxy to use */ - STRING_PRE_PROXY, /* pre socks proxy to use */ - STRING_SET_RANGE, /* range, if used */ - STRING_SET_REFERER, /* custom string for the HTTP referer field */ - STRING_SET_URL, /* what original URL to work on */ - STRING_SSL_CAPATH, /* CA directory name (doesn't work on windows) */ - STRING_SSL_CAPATH_PROXY, /* CA directory name (doesn't work on windows) */ - STRING_SSL_CAFILE, /* certificate file to verify peer against */ - STRING_SSL_CAFILE_PROXY, /* certificate file to verify peer against */ - STRING_SSL_PINNEDPUBLICKEY, /* public key file to verify peer against */ - STRING_SSL_PINNEDPUBLICKEY_PROXY, /* public key file to verify proxy */ - STRING_SSL_CIPHER_LIST, /* list of ciphers to use */ - STRING_SSL_CIPHER_LIST_PROXY, /* list of ciphers to use */ - STRING_SSL_CIPHER13_LIST, /* list of TLS 1.3 ciphers to use */ - STRING_SSL_CIPHER13_LIST_PROXY, /* list of TLS 1.3 ciphers to use */ - STRING_USERAGENT, /* User-Agent string */ - STRING_SSL_CRLFILE, /* crl file to check certificate */ - STRING_SSL_CRLFILE_PROXY, /* crl file to check certificate */ - STRING_SSL_ISSUERCERT, /* issuer cert file to check certificate */ - STRING_SSL_ISSUERCERT_PROXY, /* issuer cert file to check certificate */ - STRING_SSL_ENGINE, /* name of ssl engine */ - STRING_USERNAME, /* <username>, if used */ - STRING_PASSWORD, /* <password>, if used */ - STRING_OPTIONS, /* <options>, if used */ - STRING_PROXYUSERNAME, /* Proxy <username>, if used */ - STRING_PROXYPASSWORD, /* Proxy <password>, if used */ - STRING_NOPROXY, /* List of hosts which should not use the proxy, if - used */ - STRING_RTSP_SESSION_ID, /* Session ID to use */ - STRING_RTSP_STREAM_URI, /* Stream URI for this request */ - STRING_RTSP_TRANSPORT, /* Transport for this session */ - STRING_SSH_PRIVATE_KEY, /* path to the private key file for auth */ - STRING_SSH_PUBLIC_KEY, /* path to the public key file for auth */ - STRING_SSH_HOST_PUBLIC_KEY_MD5, /* md5 of host public key in ascii hex */ - STRING_SSH_HOST_PUBLIC_KEY_SHA256, /* sha256 of host public key in base64 */ - STRING_SSH_KNOWNHOSTS, /* file name of knownhosts file */ - STRING_PROXY_SERVICE_NAME, /* Proxy service name */ - STRING_SERVICE_NAME, /* Service name */ - STRING_MAIL_FROM, - STRING_MAIL_AUTH, - STRING_TLSAUTH_USERNAME, /* TLS auth <username> */ - STRING_TLSAUTH_USERNAME_PROXY, /* TLS auth <username> */ - STRING_TLSAUTH_PASSWORD, /* TLS auth <password> */ - STRING_TLSAUTH_PASSWORD_PROXY, /* TLS auth <password> */ - STRING_BEARER, /* <bearer>, if used */ - STRING_UNIX_SOCKET_PATH, /* path to Unix socket, if used */ - STRING_TARGET, /* CURLOPT_REQUEST_TARGET */ - STRING_DOH, /* CURLOPT_DOH_URL */ - STRING_ALTSVC, /* CURLOPT_ALTSVC */ - STRING_HSTS, /* CURLOPT_HSTS */ - STRING_SASL_AUTHZID, /* CURLOPT_SASL_AUTHZID */ - STRING_DNS_SERVERS, - STRING_DNS_INTERFACE, - STRING_DNS_LOCAL_IP4, - STRING_DNS_LOCAL_IP6, - STRING_SSL_EC_CURVES, - - /* -- end of null-terminated strings -- */ - - STRING_LASTZEROTERMINATED, - - /* -- below this are pointers to binary data that cannot be strdup'ed. --- */ - - STRING_COPYPOSTFIELDS, /* if POST, set the fields' values here */ - - STRING_AWS_SIGV4, /* Parameters for V4 signature */ - - STRING_LAST /* not used, just an end-of-list marker */ -}; - -enum dupblob { - BLOB_CERT, - BLOB_CERT_PROXY, - BLOB_KEY, - BLOB_KEY_PROXY, - BLOB_SSL_ISSUERCERT, - BLOB_SSL_ISSUERCERT_PROXY, - BLOB_CAINFO, - BLOB_CAINFO_PROXY, - BLOB_LAST -}; - -/* callback that gets called when this easy handle is completed within a multi - handle. Only used for internally created transfers, like for example - DoH. */ -typedef int (*multidone_func)(struct Curl_easy *easy, CURLcode result); - -struct UserDefined { - FILE *err; /* the stderr user data goes here */ - void *debugdata; /* the data that will be passed to fdebug */ - char *errorbuffer; /* (Static) store failure messages in here */ - long proxyport; /* If non-zero, use this port number by default. If the - proxy string features a ":[port]" that one will override - this. */ - void *out; /* CURLOPT_WRITEDATA */ - void *in_set; /* CURLOPT_READDATA */ - void *writeheader; /* write the header to this if non-NULL */ - unsigned short use_port; /* which port to use (when not using default) */ - unsigned long httpauth; /* kind of HTTP authentication to use (bitmask) */ - unsigned long proxyauth; /* kind of proxy authentication to use (bitmask) */ -#ifndef CURL_DISABLE_PROXY - unsigned long socks5auth;/* kind of SOCKS5 authentication to use (bitmask) */ -#endif - long maxredirs; /* maximum no. of http(s) redirects to follow, set to -1 - for infinity */ - - void *postfields; /* if POST, set the fields' values here */ - curl_seek_callback seek_func; /* function that seeks the input */ - curl_off_t postfieldsize; /* if POST, this might have a size to use instead - of strlen(), and then the data *may* be binary - (contain zero bytes) */ - unsigned short localport; /* local port number to bind to */ - int localportrange; /* number of additional port numbers to test in case the - 'localport' one can't be bind()ed */ - curl_write_callback fwrite_func; /* function that stores the output */ - curl_write_callback fwrite_header; /* function that stores headers */ - curl_write_callback fwrite_rtp; /* function that stores interleaved RTP */ - curl_read_callback fread_func_set; /* function that reads the input */ - curl_progress_callback fprogress; /* OLD and deprecated progress callback */ - curl_xferinfo_callback fxferinfo; /* progress callback */ - curl_debug_callback fdebug; /* function that write informational data */ - curl_ioctl_callback ioctl_func; /* function for I/O control */ - curl_sockopt_callback fsockopt; /* function for setting socket options */ - void *sockopt_client; /* pointer to pass to the socket options callback */ - curl_opensocket_callback fopensocket; /* function for checking/translating - the address and opening the - socket */ - void *opensocket_client; - curl_closesocket_callback fclosesocket; /* function for closing the - socket */ - void *closesocket_client; - curl_prereq_callback fprereq; /* pre-initial request callback */ - void *prereq_userp; /* pre-initial request user data */ - - void *seek_client; /* pointer to pass to the seek callback */ -#ifndef CURL_DISABLE_HSTS - curl_hstsread_callback hsts_read; - void *hsts_read_userp; - curl_hstswrite_callback hsts_write; - void *hsts_write_userp; -#endif - void *progress_client; /* pointer to pass to the progress callback */ - void *ioctl_client; /* pointer to pass to the ioctl callback */ - unsigned int timeout; /* ms, 0 means no timeout */ - unsigned int connecttimeout; /* ms, 0 means no timeout */ - unsigned int happy_eyeballs_timeout; /* ms, 0 is a valid value */ - unsigned int server_response_timeout; /* ms, 0 means no timeout */ - long maxage_conn; /* in seconds, max idle time to allow a connection that - is to be reused */ - long maxlifetime_conn; /* in seconds, max time since creation to allow a - connection that is to be reused */ -#ifndef CURL_DISABLE_TFTP - long tftp_blksize; /* in bytes, 0 means use default */ -#endif - curl_off_t filesize; /* size of file to upload, -1 means unknown */ - long low_speed_limit; /* bytes/second */ - long low_speed_time; /* number of seconds */ - curl_off_t max_send_speed; /* high speed limit in bytes/second for upload */ - curl_off_t max_recv_speed; /* high speed limit in bytes/second for - download */ - curl_off_t set_resume_from; /* continue [ftp] transfer from here */ - struct curl_slist *headers; /* linked list of extra headers */ - struct curl_slist *proxyheaders; /* linked list of extra CONNECT headers */ - struct curl_httppost *httppost; /* linked list of old POST data */ - curl_mimepart mimepost; /* MIME/POST data. */ - struct curl_slist *quote; /* after connection is established */ - struct curl_slist *postquote; /* after the transfer */ - struct curl_slist *prequote; /* before the transfer, after type */ - struct curl_slist *source_quote; /* 3rd party quote */ - struct curl_slist *source_prequote; /* in 3rd party transfer mode - before - the transfer on source host */ - struct curl_slist *source_postquote; /* in 3rd party transfer mode - after - the transfer on source host */ -#ifndef CURL_DISABLE_TELNET - struct curl_slist *telnet_options; /* linked list of telnet options */ -#endif - struct curl_slist *resolve; /* list of names to add/remove from - DNS cache */ - struct curl_slist *connect_to; /* list of host:port mappings to override - the hostname and port to connect to */ - time_t timevalue; /* what time to compare with */ - unsigned char timecondition; /* kind of time comparison: curl_TimeCond */ - unsigned char proxytype; /* what kind of proxy: curl_proxytype */ - unsigned char method; /* what kind of HTTP request: Curl_HttpReq */ - unsigned char httpwant; /* when non-zero, a specific HTTP version requested - to be used in the library's request(s) */ - struct ssl_config_data ssl; /* user defined SSL stuff */ -#ifndef CURL_DISABLE_PROXY - struct ssl_config_data proxy_ssl; /* user defined SSL stuff for proxy */ -#endif - struct ssl_general_config general_ssl; /* general user defined SSL stuff */ - int dns_cache_timeout; /* DNS cache timeout (seconds) */ - unsigned int buffer_size; /* size of receive buffer to use */ - unsigned int upload_buffer_size; /* size of upload buffer to use, - keep it >= CURL_MAX_WRITE_SIZE */ - void *private_data; /* application-private data */ - struct curl_slist *http200aliases; /* linked list of aliases for http200 */ - unsigned char ipver; /* the CURL_IPRESOLVE_* defines in the public header - file 0 - whatever, 1 - v2, 2 - v6 */ - curl_off_t max_filesize; /* Maximum file size to download */ -#ifndef CURL_DISABLE_FTP - unsigned char ftp_filemethod; /* how to get to a file: curl_ftpfile */ - unsigned char ftpsslauth; /* what AUTH XXX to try: curl_ftpauth */ - unsigned char ftp_ccc; /* FTP CCC options: curl_ftpccc */ - unsigned int accepttimeout; /* in milliseconds, 0 means no timeout */ -#endif - /* Desppie the name ftp_create_missing_dirs is for FTP(S) and SFTP - 1 - create directories that don't exist - 2 - the same but also allow MKD to fail once - */ - unsigned char ftp_create_missing_dirs; -#ifdef USE_LIBSSH2 - curl_sshhostkeycallback ssh_hostkeyfunc; /* hostkey check callback */ - void *ssh_hostkeyfunc_userp; /* custom pointer to callback */ -#endif - - curl_sshkeycallback ssh_keyfunc; /* key matching callback */ - void *ssh_keyfunc_userp; /* custom pointer to callback */ -#ifndef CURL_DISABLE_NETRC - unsigned char use_netrc; /* enum CURL_NETRC_OPTION values */ -#endif - curl_usessl use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or - IMAP or POP3 or others! */ - unsigned int new_file_perms; /* when creating remote files */ - unsigned int new_directory_perms; /* when creating remote dirs */ - int ssh_auth_types; /* allowed SSH auth types */ - char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */ - struct curl_blob *blobs[BLOB_LAST]; -#ifdef ENABLE_IPV6 - unsigned int scope_id; /* Scope id for IPv6 */ -#endif - curl_prot_t allowed_protocols; - curl_prot_t redir_protocols; - unsigned int mime_options; /* Mime option flags. */ - -#ifndef CURL_DISABLE_RTSP - void *rtp_out; /* write RTP to this if non-NULL */ - /* Common RTSP header options */ - Curl_RtspReq rtspreq; /* RTSP request type */ -#endif -#ifndef CURL_DISABLE_FTP - curl_chunk_bgn_callback chunk_bgn; /* called before part of transfer - starts */ - curl_chunk_end_callback chunk_end; /* called after part transferring - stopped */ - curl_fnmatch_callback fnmatch; /* callback to decide which file corresponds - to pattern (e.g. if WILDCARDMATCH is on) */ - void *fnmatch_data; -#endif - long gssapi_delegation; /* GSS-API credential delegation, see the - documentation of CURLOPT_GSSAPI_DELEGATION */ - - int tcp_keepidle; /* seconds in idle before sending keepalive probe */ - int tcp_keepintvl; /* seconds between TCP keepalive probes */ - - size_t maxconnects; /* Max idle connections in the connection cache */ - - long expect_100_timeout; /* in milliseconds */ -#ifdef USE_HTTP2 - struct Curl_easy *stream_depends_on; - int stream_weight; - struct Curl_http2_dep *stream_dependents; -#endif - curl_resolver_start_callback resolver_start; /* optional callback called - before resolver start */ - void *resolver_start_client; /* pointer to pass to resolver start callback */ - long upkeep_interval_ms; /* Time between calls for connection upkeep. */ - multidone_func fmultidone; -#ifndef CURL_DISABLE_DOH - struct Curl_easy *dohfor; /* this is a DoH request for that transfer */ -#endif - CURLU *uh; /* URL handle for the current parsed URL */ - void *trailer_data; /* pointer to pass to trailer data callback */ - curl_trailer_callback trailer_callback; /* trailing data callback */ - char keep_post; /* keep POSTs as POSTs after a 30x request; each - bit represents a request, from 301 to 303 */ -#ifndef CURL_DISABLE_SMTP - struct curl_slist *mail_rcpt; /* linked list of mail recipients */ - BIT(mail_rcpt_allowfails); /* allow RCPT TO command to fail for some - recipients */ -#endif - unsigned char connect_only; /* make connection/request, then let - application use the socket */ - BIT(is_fread_set); /* has read callback been set to non-NULL? */ -#ifndef CURL_DISABLE_TFTP - BIT(tftp_no_options); /* do not send TFTP options requests */ -#endif - BIT(sep_headers); /* handle host and proxy headers separately */ - BIT(cookiesession); /* new cookie session? */ - BIT(crlf); /* convert crlf on ftp upload(?) */ - BIT(ssh_compression); /* enable SSH compression */ - -/* Here follows boolean settings that define how to behave during - this session. They are STATIC, set by libcurl users or at least initially - and they don't change during operations. */ - BIT(get_filetime); /* get the time and get of the remote file */ - BIT(tunnel_thru_httpproxy); /* use CONNECT through a HTTP proxy */ - BIT(prefer_ascii); /* ASCII rather than binary */ - BIT(remote_append); /* append, not overwrite, on upload */ - BIT(list_only); /* list directory */ -#ifndef CURL_DISABLE_FTP - BIT(ftp_use_port); /* use the FTP PORT command */ - BIT(ftp_use_epsv); /* if EPSV is to be attempted or not */ - BIT(ftp_use_eprt); /* if EPRT is to be attempted or not */ - BIT(ftp_use_pret); /* if PRET is to be used before PASV or not */ - BIT(ftp_skip_ip); /* skip the IP address the FTP server passes on to - us */ - BIT(wildcard_enabled); /* enable wildcard matching */ -#endif - BIT(hide_progress); /* don't use the progress meter */ - BIT(http_fail_on_error); /* fail on HTTP error codes >= 400 */ - BIT(http_keep_sending_on_error); /* for HTTP status codes >= 300 */ - BIT(http_follow_location); /* follow HTTP redirects */ - BIT(http_transfer_encoding); /* request compressed HTTP transfer-encoding */ - BIT(allow_auth_to_other_hosts); - BIT(include_header); /* include received protocol headers in data output */ - BIT(http_set_referer); /* is a custom referer used */ - BIT(http_auto_referer); /* set "correct" referer when following - location: */ - BIT(opt_no_body); /* as set with CURLOPT_NOBODY */ - BIT(upload); /* upload request */ - BIT(verbose); /* output verbosity */ - BIT(krb); /* Kerberos connection requested */ - BIT(reuse_forbid); /* forbidden to be reused, close after use */ - BIT(reuse_fresh); /* do not re-use an existing connection */ - BIT(no_signal); /* do not use any signal/alarm handler */ - BIT(tcp_nodelay); /* whether to enable TCP_NODELAY or not */ - BIT(ignorecl); /* ignore content length */ - BIT(http_te_skip); /* pass the raw body data to the user, even when - transfer-encoded (chunked, compressed) */ - BIT(http_ce_skip); /* pass the raw body data to the user, even when - content-encoded (chunked, compressed) */ - BIT(proxy_transfer_mode); /* set transfer mode (;type=<a|i>) when doing - FTP via an HTTP proxy */ -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - BIT(socks5_gssapi_nec); /* Flag to support NEC SOCKS5 server */ -#endif - BIT(sasl_ir); /* Enable/disable SASL initial response */ - BIT(tcp_keepalive); /* use TCP keepalives */ - BIT(tcp_fastopen); /* use TCP Fast Open */ - BIT(ssl_enable_alpn);/* TLS ALPN extension? */ - BIT(path_as_is); /* allow dotdots? */ - BIT(pipewait); /* wait for multiplex status before starting a new - connection */ - BIT(suppress_connect_headers); /* suppress proxy CONNECT response headers - from user callbacks */ - BIT(dns_shuffle_addresses); /* whether to shuffle addresses before use */ - BIT(stream_depends_e); /* set or don't set the Exclusive bit */ - BIT(haproxyprotocol); /* whether to send HAProxy PROXY protocol v1 - header */ - BIT(abstract_unix_socket); - BIT(disallow_username_in_url); /* disallow username in url */ -#ifndef CURL_DISABLE_DOH - BIT(doh); /* DNS-over-HTTPS enabled */ - BIT(doh_verifypeer); /* DoH certificate peer verification */ - BIT(doh_verifyhost); /* DoH certificate hostname verification */ - BIT(doh_verifystatus); /* DoH certificate status verification */ -#endif - BIT(http09_allowed); /* allow HTTP/0.9 responses */ -#ifdef USE_WEBSOCKETS - BIT(ws_raw_mode); -#endif -}; - -struct Names { - struct Curl_hash *hostcache; - enum { - HCACHE_NONE, /* not pointing to anything */ - HCACHE_MULTI, /* points to a shared one in the multi handle */ - HCACHE_SHARED /* points to a shared one in a shared object */ - } hostcachetype; -}; - -/* - * The 'connectdata' struct MUST have all the connection oriented stuff as we - * may have several simultaneous connections and connection structs in memory. - * - * The 'struct UserDefined' must only contain data that is set once to go for - * many (perhaps) independent connections. Values that are generated or - * calculated internally for the "session handle" must be defined within the - * 'struct UrlState' instead. - */ - -struct Curl_easy { - /* First a simple identifier to easier detect if a user mix up this easy - handle with a multi handle. Set this to CURLEASY_MAGIC_NUMBER */ - unsigned int magic; - - /* first, two fields for the linked list of these */ - struct Curl_easy *next; - struct Curl_easy *prev; - - struct connectdata *conn; - struct Curl_llist_element connect_queue; - struct Curl_llist_element conn_queue; /* list per connectdata */ - - CURLMstate mstate; /* the handle's state */ - CURLcode result; /* previous result */ - - struct Curl_message msg; /* A single posted message. */ - - /* Array with the plain socket numbers this handle takes care of, in no - particular order. Note that all sockets are added to the sockhash, where - the state etc are also kept. This array is mostly used to detect when a - socket is to be removed from the hash. See singlesocket(). */ - curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE]; - unsigned char actions[MAX_SOCKSPEREASYHANDLE]; /* action for each socket in - sockets[] */ - int numsocks; - - struct Names dns; - struct Curl_multi *multi; /* if non-NULL, points to the multi handle - struct to which this "belongs" when used by - the multi interface */ - struct Curl_multi *multi_easy; /* if non-NULL, points to the multi handle - struct to which this "belongs" when used - by the easy interface */ - struct Curl_share *share; /* Share, handles global variable mutexing */ -#ifdef USE_LIBPSL - struct PslCache *psl; /* The associated PSL cache. */ -#endif - struct SingleRequest req; /* Request-specific data */ - struct UserDefined set; /* values set by the libcurl user */ -#ifndef CURL_DISABLE_COOKIES - struct CookieInfo *cookies; /* the cookies, read from files and servers. - NOTE that the 'cookie' field in the - UserDefined struct defines if the "engine" - is to be used or not. */ -#endif -#ifndef CURL_DISABLE_HSTS - struct hsts *hsts; -#endif -#ifndef CURL_DISABLE_ALTSVC - struct altsvcinfo *asi; /* the alt-svc cache */ -#endif - struct Progress progress; /* for all the progress meter data */ - struct UrlState state; /* struct for fields used for state info and - other dynamic purposes */ -#ifndef CURL_DISABLE_FTP - struct WildcardData wildcard; /* wildcard download state info */ -#endif - struct PureInfo info; /* stats, reports and info data */ - struct curl_tlssessioninfo tsi; /* Information about the TLS session, only - valid after a client has asked for it */ -#ifdef USE_HYPER - struct hyptransfer hyp; -#endif -}; - -#define LIBCURL_NAME "libcurl" - -#endif /* HEADER_CURL_URLDATA_H */ +#ifndef HEADER_CURL_URLDATA_H
+#define HEADER_CURL_URLDATA_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 for lib internal stuff */
+
+#include "curl_setup.h"
+
+#define PORT_FTP 21
+#define PORT_FTPS 990
+#define PORT_TELNET 23
+#define PORT_HTTP 80
+#define PORT_HTTPS 443
+#define PORT_DICT 2628
+#define PORT_LDAP 389
+#define PORT_LDAPS 636
+#define PORT_TFTP 69
+#define PORT_SSH 22
+#define PORT_IMAP 143
+#define PORT_IMAPS 993
+#define PORT_POP3 110
+#define PORT_POP3S 995
+#define PORT_SMB 445
+#define PORT_SMBS 445
+#define PORT_SMTP 25
+#define PORT_SMTPS 465 /* sometimes called SSMTP */
+#define PORT_RTSP 554
+#define PORT_RTMP 1935
+#define PORT_RTMPT PORT_HTTP
+#define PORT_RTMPS PORT_HTTPS
+#define PORT_GOPHER 70
+#define PORT_MQTT 1883
+
+#ifdef USE_WEBSOCKETS
+/* CURLPROTO_GOPHERS (29) is the highest publicly used protocol bit number,
+ * the rest are internal information. If we use higher bits we only do this on
+ * platforms that have a >= 64 bit type and then we use such a type for the
+ * protocol fields in the protocol handler.
+ */
+#define CURLPROTO_WS (1<<30)
+#define CURLPROTO_WSS ((curl_prot_t)1<<31)
+#else
+#define CURLPROTO_WS 0
+#define CURLPROTO_WSS 0
+#endif
+
+/* This should be undefined once we need bit 32 or higher */
+#define PROTO_TYPE_SMALL
+
+#ifndef PROTO_TYPE_SMALL
+typedef curl_off_t curl_prot_t;
+#else
+typedef unsigned int curl_prot_t;
+#endif
+
+/* This mask is for all the old protocols that are provided and defined in the
+ public header and shall exclude protocols added since which are not exposed
+ in the API */
+#define CURLPROTO_MASK (0x3ffffff)
+
+#define DICT_MATCH "/MATCH:"
+#define DICT_MATCH2 "/M:"
+#define DICT_MATCH3 "/FIND:"
+#define DICT_DEFINE "/DEFINE:"
+#define DICT_DEFINE2 "/D:"
+#define DICT_DEFINE3 "/LOOKUP:"
+
+#define CURL_DEFAULT_USER "anonymous"
+#define CURL_DEFAULT_PASSWORD "ftp@example.com"
+
+/* Convenience defines for checking protocols or their SSL based version. Each
+ protocol handler should only ever have a single CURLPROTO_ in its protocol
+ field. */
+#define PROTO_FAMILY_HTTP (CURLPROTO_HTTP|CURLPROTO_HTTPS|CURLPROTO_WS| \
+ CURLPROTO_WSS)
+#define PROTO_FAMILY_FTP (CURLPROTO_FTP|CURLPROTO_FTPS)
+#define PROTO_FAMILY_POP3 (CURLPROTO_POP3|CURLPROTO_POP3S)
+#define PROTO_FAMILY_SMB (CURLPROTO_SMB|CURLPROTO_SMBS)
+#define PROTO_FAMILY_SMTP (CURLPROTO_SMTP|CURLPROTO_SMTPS)
+#define PROTO_FAMILY_SSH (CURLPROTO_SCP|CURLPROTO_SFTP)
+
+#define DEFAULT_CONNCACHE_SIZE 5
+
+/* length of longest IPv6 address string including the trailing null */
+#define MAX_IPADR_LEN sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")
+
+/* Default FTP/IMAP etc response timeout in milliseconds */
+#define RESP_TIMEOUT (120*1000)
+
+/* Max string input length is a precaution against abuse and to detect junk
+ input easier and better. */
+#define CURL_MAX_INPUT_LENGTH 8000000
+
+/* Macros intended for DEBUGF logging, use like:
+ * DEBUGF(infof(data, CFMSG(cf, "this filter %s rocks"), "very much"));
+ * and it will output:
+ * [CONN-1-0][CF-SSL] this filter very much rocks
+ * on connection #1 with sockindex 0 for filter of type "SSL". */
+#define DMSG(d,msg) \
+ "[CONN-%ld] "msg, (d)->conn->connection_id
+#define DMSGI(d,i,msg) \
+ "[CONN-%ld-%d] "msg, (d)->conn->connection_id, (i)
+#define CMSG(c,msg) \
+ "[CONN-%ld] "msg, (conn)->connection_id
+#define CMSGI(c,i,msg) \
+ "[CONN-%ld-%d] "msg, (conn)->connection_id, (i)
+#define CFMSG(cf,msg) \
+ "[CONN-%ld-%d][CF-%s] "msg, (cf)->conn->connection_id, \
+ (cf)->sockindex, (cf)->cft->name
+
+
+#include "cookie.h"
+#include "psl.h"
+#include "formdata.h"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif
+
+#include "timeval.h"
+
+#include <curl/curl.h>
+
+#include "http_chunks.h" /* for the structs and enum stuff */
+#include "hostip.h"
+#include "hash.h"
+#include "splay.h"
+#include "dynbuf.h"
+
+/* return the count of bytes sent, or -1 on error */
+typedef ssize_t (Curl_send)(struct Curl_easy *data, /* transfer */
+ int sockindex, /* socketindex */
+ const void *buf, /* data to write */
+ size_t len, /* max amount to write */
+ CURLcode *err); /* error to return */
+
+/* return the count of bytes read, or -1 on error */
+typedef ssize_t (Curl_recv)(struct Curl_easy *data, /* transfer */
+ int sockindex, /* socketindex */
+ char *buf, /* store data here */
+ size_t len, /* max amount to read */
+ CURLcode *err); /* error to return */
+
+#ifdef USE_HYPER
+typedef CURLcode (*Curl_datastream)(struct Curl_easy *data,
+ struct connectdata *conn,
+ int *didwhat,
+ bool *done,
+ int select_res);
+#endif
+
+#include "mime.h"
+#include "imap.h"
+#include "pop3.h"
+#include "smtp.h"
+#include "ftp.h"
+#include "file.h"
+#include "vssh/ssh.h"
+#include "http.h"
+#include "rtsp.h"
+#include "smb.h"
+#include "mqtt.h"
+#include "wildcard.h"
+#include "multihandle.h"
+#include "quic.h"
+#include "c-hyper.h"
+
+#ifdef HAVE_GSSAPI
+# ifdef HAVE_GSSGNU
+# include <gss.h>
+# elif defined HAVE_GSSAPI_GSSAPI_H
+# include <gssapi/gssapi.h>
+# else
+# include <gssapi.h>
+# endif
+# ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H
+# include <gssapi/gssapi_generic.h>
+# endif
+#endif
+
+#ifdef USE_LIBSSH2
+#include <libssh2.h>
+#include <libssh2_sftp.h>
+#endif /* USE_LIBSSH2 */
+
+#define READBUFFER_SIZE CURL_MAX_WRITE_SIZE
+#define READBUFFER_MAX CURL_MAX_READ_SIZE
+#define READBUFFER_MIN 1024
+
+/* The default upload buffer size, should not be smaller than
+ CURL_MAX_WRITE_SIZE, as it needs to hold a full buffer as could be sent in
+ a write callback.
+
+ The size was 16KB for many years but was bumped to 64KB because it makes
+ libcurl able to do significantly faster uploads in some circumstances. Even
+ larger buffers can help further, but this is deemed a fair memory/speed
+ compromise. */
+#define UPLOADBUFFER_DEFAULT 65536
+#define UPLOADBUFFER_MAX (2*1024*1024)
+#define UPLOADBUFFER_MIN CURL_MAX_WRITE_SIZE
+
+#define CURLEASY_MAGIC_NUMBER 0xc0dedbadU
+#define GOOD_EASY_HANDLE(x) \
+ ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER))
+
+#ifdef HAVE_GSSAPI
+/* Types needed for krb5-ftp connections */
+struct krb5buffer {
+ void *data;
+ size_t size;
+ size_t index;
+ BIT(eof_flag);
+};
+
+enum protection_level {
+ PROT_NONE, /* first in list */
+ PROT_CLEAR,
+ PROT_SAFE,
+ PROT_CONFIDENTIAL,
+ PROT_PRIVATE,
+ PROT_CMD,
+ PROT_LAST /* last in list */
+};
+#endif
+
+/* enum for the nonblocking SSL connection state machine */
+typedef enum {
+ ssl_connect_1,
+ ssl_connect_2,
+ ssl_connect_2_reading,
+ ssl_connect_2_writing,
+ ssl_connect_3,
+ ssl_connect_done
+} ssl_connect_state;
+
+typedef enum {
+ ssl_connection_none,
+ ssl_connection_negotiating,
+ ssl_connection_complete
+} ssl_connection_state;
+
+/* SSL backend-specific data; declared differently by each SSL backend */
+struct ssl_backend_data;
+
+struct ssl_primary_config {
+ long version; /* what version the client wants to use */
+ long version_max; /* max supported version the client wants to use */
+ char *CApath; /* certificate dir (doesn't work on windows) */
+ char *CAfile; /* certificate to verify peer against */
+ char *issuercert; /* optional issuer certificate filename */
+ char *clientcert;
+ char *cipher_list; /* list of ciphers to use */
+ char *cipher_list13; /* list of TLS 1.3 cipher suites to use */
+ char *pinned_key;
+ char *CRLfile; /* CRL to check certificate revocation */
+ struct curl_blob *cert_blob;
+ struct curl_blob *ca_info_blob;
+ struct curl_blob *issuercert_blob;
+#ifdef USE_TLS_SRP
+ char *username; /* TLS username (for, e.g., SRP) */
+ char *password; /* TLS password (for, e.g., SRP) */
+ enum CURL_TLSAUTH authtype; /* TLS authentication type (default SRP) */
+#endif
+ char *curves; /* list of curves to use */
+ unsigned char ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */
+ BIT(verifypeer); /* set TRUE if this is desired */
+ BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */
+ BIT(verifystatus); /* set TRUE if certificate status must be checked */
+ BIT(sessionid); /* cache session IDs or not */
+};
+
+struct ssl_config_data {
+ struct ssl_primary_config primary;
+ long certverifyresult; /* result from the certificate verification */
+ curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */
+ void *fsslctxp; /* parameter for call back */
+ char *cert_type; /* format for certificate (default: PEM)*/
+ char *key; /* private key file name */
+ struct curl_blob *key_blob;
+ char *key_type; /* format for private key (default: PEM) */
+ char *key_passwd; /* plain text private key password */
+ BIT(certinfo); /* gather lots of certificate info */
+ BIT(falsestart);
+ BIT(enable_beast); /* allow this flaw for interoperability's sake */
+ BIT(no_revoke); /* disable SSL certificate revocation checks */
+ BIT(no_partialchain); /* don't accept partial certificate chains */
+ BIT(revoke_best_effort); /* ignore SSL revocation offline/missing revocation
+ list errors */
+ BIT(native_ca_store); /* use the native ca store of operating system */
+ BIT(auto_client_cert); /* automatically locate and use a client
+ certificate for authentication (Schannel) */
+};
+
+struct ssl_general_config {
+ size_t max_ssl_sessions; /* SSL session id cache size */
+ int ca_cache_timeout; /* Certificate store cache timeout (seconds) */
+};
+
+/* information stored about one single SSL session */
+struct Curl_ssl_session {
+ char *name; /* host name for which this ID was used */
+ char *conn_to_host; /* host name for the connection (may be NULL) */
+ const char *scheme; /* protocol scheme used */
+ void *sessionid; /* as returned from the SSL layer */
+ size_t idsize; /* if known, otherwise 0 */
+ long age; /* just a number, the higher the more recent */
+ int remote_port; /* remote port */
+ int conn_to_port; /* remote port for the connection (may be -1) */
+ struct ssl_primary_config ssl_config; /* setup for this session */
+};
+
+#ifdef USE_WINDOWS_SSPI
+#include "curl_sspi.h"
+#endif
+
+/* Struct used for Digest challenge-response authentication */
+struct digestdata {
+#if defined(USE_WINDOWS_SSPI)
+ BYTE *input_token;
+ size_t input_token_len;
+ CtxtHandle *http_context;
+ /* copy of user/passwd used to make the identity for http_context.
+ either may be NULL. */
+ char *user;
+ char *passwd;
+#else
+ char *nonce;
+ char *cnonce;
+ char *realm;
+ char *opaque;
+ char *qop;
+ char *algorithm;
+ int nc; /* nonce count */
+ unsigned char algo;
+ BIT(stale); /* set true for re-negotiation */
+ BIT(userhash);
+#endif
+};
+
+typedef enum {
+ NTLMSTATE_NONE,
+ NTLMSTATE_TYPE1,
+ NTLMSTATE_TYPE2,
+ NTLMSTATE_TYPE3,
+ NTLMSTATE_LAST
+} curlntlm;
+
+typedef enum {
+ GSS_AUTHNONE,
+ GSS_AUTHRECV,
+ GSS_AUTHSENT,
+ GSS_AUTHDONE,
+ GSS_AUTHSUCC
+} curlnegotiate;
+
+/* Struct used for GSSAPI (Kerberos V5) authentication */
+#if defined(USE_KERBEROS5)
+struct kerberos5data {
+#if defined(USE_WINDOWS_SSPI)
+ CredHandle *credentials;
+ CtxtHandle *context;
+ TCHAR *spn;
+ SEC_WINNT_AUTH_IDENTITY identity;
+ SEC_WINNT_AUTH_IDENTITY *p_identity;
+ size_t token_max;
+ BYTE *output_token;
+#else
+ gss_ctx_id_t context;
+ gss_name_t spn;
+#endif
+};
+#endif
+
+/* Struct used for SCRAM-SHA-1 authentication */
+#ifdef USE_GSASL
+#include <gsasl.h>
+struct gsasldata {
+ Gsasl *ctx;
+ Gsasl_session *client;
+};
+#endif
+
+/* Struct used for NTLM challenge-response authentication */
+#if defined(USE_NTLM)
+struct ntlmdata {
+#ifdef USE_WINDOWS_SSPI
+/* The sslContext is used for the Schannel bindings. The
+ * api is available on the Windows 7 SDK and later.
+ */
+#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
+ CtxtHandle *sslContext;
+#endif
+ CredHandle *credentials;
+ CtxtHandle *context;
+ SEC_WINNT_AUTH_IDENTITY identity;
+ SEC_WINNT_AUTH_IDENTITY *p_identity;
+ size_t token_max;
+ BYTE *output_token;
+ BYTE *input_token;
+ size_t input_token_len;
+ TCHAR *spn;
+#else
+ unsigned int flags;
+ unsigned char nonce[8];
+ unsigned int target_info_len;
+ void *target_info; /* TargetInfo received in the ntlm type-2 message */
+
+#if defined(NTLM_WB_ENABLED)
+ /* used for communication with Samba's winbind daemon helper ntlm_auth */
+ curl_socket_t ntlm_auth_hlpr_socket;
+ pid_t ntlm_auth_hlpr_pid;
+ char *challenge; /* The received base64 encoded ntlm type-2 message */
+ char *response; /* The generated base64 ntlm type-1/type-3 message */
+#endif
+#endif
+};
+#endif
+
+/* Struct used for Negotiate (SPNEGO) authentication */
+#ifdef USE_SPNEGO
+struct negotiatedata {
+#ifdef HAVE_GSSAPI
+ OM_uint32 status;
+ gss_ctx_id_t context;
+ gss_name_t spn;
+ gss_buffer_desc output_token;
+#else
+#ifdef USE_WINDOWS_SSPI
+#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
+ CtxtHandle *sslContext;
+#endif
+ DWORD status;
+ CredHandle *credentials;
+ CtxtHandle *context;
+ SEC_WINNT_AUTH_IDENTITY identity;
+ SEC_WINNT_AUTH_IDENTITY *p_identity;
+ TCHAR *spn;
+ size_t token_max;
+ BYTE *output_token;
+ size_t output_token_length;
+#endif
+#endif
+ BIT(noauthpersist);
+ BIT(havenoauthpersist);
+ BIT(havenegdata);
+ BIT(havemultiplerequests);
+};
+#endif
+
+#ifdef CURL_DISABLE_PROXY
+#define CONN_IS_PROXIED(x) 0
+#else
+#define CONN_IS_PROXIED(x) x->bits.proxy
+#endif
+
+/*
+ * Boolean values that concerns this connection.
+ */
+struct ConnectBits {
+#ifndef CURL_DISABLE_PROXY
+ BIT(httpproxy); /* if set, this transfer is done through an HTTP proxy */
+ BIT(socksproxy); /* if set, this transfer is done through a socks proxy */
+ BIT(proxy_user_passwd); /* user+password for the proxy? */
+ BIT(tunnel_proxy); /* if CONNECT is used to "tunnel" through the proxy.
+ This is implicit when SSL-protocols are used through
+ proxies, but can also be enabled explicitly by
+ apps */
+ BIT(proxy_connect_closed); /* TRUE if a proxy disconnected the connection
+ in a CONNECT request with auth, so that
+ libcurl should reconnect and continue. */
+ BIT(proxy); /* if set, this transfer is done through a proxy - any type */
+#endif
+ /* always modify bits.close with the connclose() and connkeep() macros! */
+ BIT(close); /* if set, we close the connection after this request */
+ BIT(reuse); /* if set, this is a re-used connection */
+ BIT(altused); /* this is an alt-svc "redirect" */
+ BIT(conn_to_host); /* if set, this connection has a "connect to host"
+ that overrides the host in the URL */
+ BIT(conn_to_port); /* if set, this connection has a "connect to port"
+ that overrides the port in the URL (remote port) */
+ BIT(ipv6_ip); /* we communicate with a remote site specified with pure IPv6
+ IP address */
+ BIT(ipv6); /* we communicate with a site using an IPv6 address */
+ BIT(do_more); /* this is set TRUE if the ->curl_do_more() function is
+ supposed to be called, after ->curl_do() */
+ BIT(protoconnstart);/* the protocol layer has STARTED its operation after
+ the TCP layer connect */
+ BIT(retry); /* this connection is about to get closed and then
+ re-attempted at another connection. */
+ BIT(authneg); /* TRUE when the auth phase has started, which means
+ that we are creating a request with an auth header,
+ but it is not the final request in the auth
+ negotiation. */
+#ifndef CURL_DISABLE_FTP
+ BIT(ftp_use_epsv); /* As set with CURLOPT_FTP_USE_EPSV, but if we find out
+ EPSV doesn't work we disable it for the forthcoming
+ requests */
+ BIT(ftp_use_eprt); /* As set with CURLOPT_FTP_USE_EPRT, but if we find out
+ EPRT doesn't work we disable it for the forthcoming
+ requests */
+ BIT(ftp_use_data_ssl); /* Enabled SSL for the data connection */
+ BIT(ftp_use_control_ssl); /* Enabled SSL for the control connection */
+#endif
+#ifndef CURL_DISABLE_NETRC
+ BIT(netrc); /* name+password provided by netrc */
+#endif
+ BIT(bound); /* set true if bind() has already been done on this socket/
+ connection */
+ BIT(multiplex); /* connection is multiplexed */
+ BIT(tcp_fastopen); /* use TCP Fast Open */
+ BIT(tls_enable_alpn); /* TLS ALPN extension? */
+#ifndef CURL_DISABLE_DOH
+ BIT(doh);
+#endif
+#ifdef USE_UNIX_SOCKETS
+ BIT(abstract_unix_socket);
+#endif
+ BIT(tls_upgraded);
+ BIT(sock_accepted); /* TRUE if the SECONDARYSOCKET was created with
+ accept() */
+ BIT(parallel_connect); /* set TRUE when a parallel connect attempt has
+ started (happy eyeballs) */
+};
+
+struct hostname {
+ char *rawalloc; /* allocated "raw" version of the name */
+ char *encalloc; /* allocated IDN-encoded version of the name */
+ char *name; /* name to use internally, might be encoded, might be raw */
+ const char *dispname; /* name to display, as 'name' might be encoded */
+};
+
+/*
+ * Flags on the keepon member of the Curl_transfer_keeper
+ */
+
+#define KEEP_NONE 0
+#define KEEP_RECV (1<<0) /* there is or may be data to read */
+#define KEEP_SEND (1<<1) /* there is or may be data to write */
+#define KEEP_RECV_HOLD (1<<2) /* when set, no reading should be done but there
+ might still be data to read */
+#define KEEP_SEND_HOLD (1<<3) /* when set, no writing should be done but there
+ might still be data to write */
+#define KEEP_RECV_PAUSE (1<<4) /* reading is paused */
+#define KEEP_SEND_PAUSE (1<<5) /* writing is paused */
+
+#define KEEP_RECVBITS (KEEP_RECV | KEEP_RECV_HOLD | KEEP_RECV_PAUSE)
+#define KEEP_SENDBITS (KEEP_SEND | KEEP_SEND_HOLD | KEEP_SEND_PAUSE)
+
+#if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH)
+#define USE_CURL_ASYNC
+struct Curl_async {
+ char *hostname;
+ struct Curl_dns_entry *dns;
+ struct thread_data *tdata;
+ void *resolver; /* resolver state, if it is used in the URL state -
+ ares_channel e.g. */
+ int port;
+ int status; /* if done is TRUE, this is the status from the callback */
+ BIT(done); /* set TRUE when the lookup is complete */
+};
+
+#endif
+
+#define FIRSTSOCKET 0
+#define SECONDARYSOCKET 1
+
+enum expect100 {
+ EXP100_SEND_DATA, /* enough waiting, just send the body now */
+ EXP100_AWAITING_CONTINUE, /* waiting for the 100 Continue header */
+ EXP100_SENDING_REQUEST, /* still sending the request but will wait for
+ the 100 header once done with the request */
+ EXP100_FAILED /* used on 417 Expectation Failed */
+};
+
+enum upgrade101 {
+ UPGR101_INIT, /* default state */
+ UPGR101_WS, /* upgrade to WebSockets requested */
+ UPGR101_H2, /* upgrade to HTTP/2 requested */
+ UPGR101_RECEIVED, /* 101 response received */
+ UPGR101_WORKING /* talking upgraded protocol */
+};
+
+enum doh_slots {
+ /* Explicit values for first two symbols so as to match hard-coded
+ * constants in existing code
+ */
+ DOH_PROBE_SLOT_IPADDR_V4 = 0, /* make 'V4' stand out for readability */
+ DOH_PROBE_SLOT_IPADDR_V6 = 1, /* 'V6' likewise */
+
+ /* Space here for (possibly build-specific) additional slot definitions */
+
+ /* for example */
+ /* #ifdef WANT_DOH_FOOBAR_TXT */
+ /* DOH_PROBE_SLOT_FOOBAR_TXT, */
+ /* #endif */
+
+ /* AFTER all slot definitions, establish how many we have */
+ DOH_PROBE_SLOTS
+};
+
+/*
+ * Request specific data in the easy handle (Curl_easy). Previously,
+ * these members were on the connectdata struct but since a conn struct may
+ * now be shared between different Curl_easys, we store connection-specific
+ * data here. This struct only keeps stuff that's interesting for *this*
+ * request, as it will be cleared between multiple ones
+ */
+struct SingleRequest {
+ curl_off_t size; /* -1 if unknown at this point */
+ curl_off_t maxdownload; /* in bytes, the maximum amount of data to fetch,
+ -1 means unlimited */
+ curl_off_t bytecount; /* total number of bytes read */
+ curl_off_t writebytecount; /* number of bytes written */
+
+ curl_off_t headerbytecount; /* only count received headers */
+ curl_off_t deductheadercount; /* this amount of bytes doesn't count when we
+ check if anything has been transferred at
+ the end of a connection. We use this
+ counter to make only a 100 reply (without a
+ following second response code) result in a
+ CURLE_GOT_NOTHING error code */
+
+ curl_off_t pendingheader; /* this many bytes left to send is actually
+ header and not body */
+ struct curltime start; /* transfer started at this time */
+ struct curltime now; /* current time */
+ enum {
+ HEADER_NORMAL, /* no bad header at all */
+ HEADER_PARTHEADER, /* part of the chunk is a bad header, the rest
+ is normal data */
+ HEADER_ALLBAD /* all was believed to be header */
+ } badheader; /* the header was deemed bad and will be
+ written as body */
+ int headerline; /* counts header lines to better track the
+ first one */
+ char *str; /* within buf */
+ curl_off_t offset; /* possible resume offset read from the
+ Content-Range: header */
+ int httpcode; /* error code from the 'HTTP/1.? XXX' or
+ 'RTSP/1.? XXX' line */
+ int keepon;
+ struct curltime start100; /* time stamp to wait for the 100 code from */
+ enum expect100 exp100; /* expect 100 continue state */
+ enum upgrade101 upgr101; /* 101 upgrade state */
+
+ /* Content unencoding stack. See sec 3.5, RFC2616. */
+ struct contenc_writer *writer_stack;
+ time_t timeofdoc;
+ long bodywrites;
+ char *location; /* This points to an allocated version of the Location:
+ header data */
+ char *newurl; /* Set to the new URL to use when a redirect or a retry is
+ wanted */
+
+ /* 'upload_present' is used to keep a byte counter of how much data there is
+ still left in the buffer, aimed for upload. */
+ ssize_t upload_present;
+
+ /* 'upload_fromhere' is used as a read-pointer when we uploaded parts of a
+ buffer, so the next read should read from where this pointer points to,
+ and the 'upload_present' contains the number of bytes available at this
+ position */
+ char *upload_fromhere;
+
+ /* Allocated protocol-specific data. Each protocol handler makes sure this
+ points to data it needs. */
+ union {
+ struct FILEPROTO *file;
+ struct FTP *ftp;
+ struct HTTP *http;
+ struct IMAP *imap;
+ struct ldapreqinfo *ldap;
+ struct MQTT *mqtt;
+ struct POP3 *pop3;
+ struct RTSP *rtsp;
+ struct smb_request *smb;
+ struct SMTP *smtp;
+ struct SSHPROTO *ssh;
+ struct TELNET *telnet;
+ } p;
+#ifndef CURL_DISABLE_DOH
+ struct dohdata *doh; /* DoH specific data for this request */
+#endif
+ unsigned char setcookies;
+ BIT(header); /* incoming data has HTTP header */
+ BIT(content_range); /* set TRUE if Content-Range: was found */
+ BIT(upload_done); /* set to TRUE when doing chunked transfer-encoding
+ upload and we're uploading the last chunk */
+ BIT(ignorebody); /* we read a response-body but we ignore it! */
+ BIT(http_bodyless); /* HTTP response status code is between 100 and 199,
+ 204 or 304 */
+ BIT(chunk); /* if set, this is a chunked transfer-encoding */
+ BIT(ignore_cl); /* ignore content-length */
+ BIT(upload_chunky); /* set TRUE if we are doing chunked transfer-encoding
+ on upload */
+ BIT(getheader); /* TRUE if header parsing is wanted */
+ BIT(forbidchunk); /* used only to explicitly forbid chunk-upload for
+ specific upload buffers. See readmoredata() in http.c
+ for details. */
+ BIT(no_body); /* the response has no body */
+};
+
+/*
+ * Specific protocol handler.
+ */
+
+struct Curl_handler {
+ const char *scheme; /* URL scheme name. */
+
+ /* Complement to setup_connection_internals(). This is done before the
+ transfer "owns" the connection. */
+ CURLcode (*setup_connection)(struct Curl_easy *data,
+ struct connectdata *conn);
+
+ /* These two functions MUST be set to be protocol dependent */
+ CURLcode (*do_it)(struct Curl_easy *data, bool *done);
+ CURLcode (*done)(struct Curl_easy *, CURLcode, bool);
+
+ /* If the curl_do() function is better made in two halves, this
+ * curl_do_more() function will be called afterwards, if set. For example
+ * for doing the FTP stuff after the PASV/PORT command.
+ */
+ CURLcode (*do_more)(struct Curl_easy *, int *);
+
+ /* This function *MAY* be set to a protocol-dependent function that is run
+ * after the connect() and everything is done, as a step in the connection.
+ * The 'done' pointer points to a bool that should be set to TRUE if the
+ * function completes before return. If it doesn't complete, the caller
+ * should call the curl_connecting() function until it is.
+ */
+ CURLcode (*connect_it)(struct Curl_easy *data, bool *done);
+
+ /* See above. */
+ CURLcode (*connecting)(struct Curl_easy *data, bool *done);
+ CURLcode (*doing)(struct Curl_easy *data, bool *done);
+
+ /* Called from the multi interface during the PROTOCONNECT phase, and it
+ should then return a proper fd set */
+ int (*proto_getsock)(struct Curl_easy *data,
+ struct connectdata *conn, curl_socket_t *socks);
+
+ /* Called from the multi interface during the DOING phase, and it should
+ then return a proper fd set */
+ int (*doing_getsock)(struct Curl_easy *data,
+ struct connectdata *conn, curl_socket_t *socks);
+
+ /* Called from the multi interface during the DO_MORE phase, and it should
+ then return a proper fd set */
+ int (*domore_getsock)(struct Curl_easy *data,
+ struct connectdata *conn, curl_socket_t *socks);
+
+ /* Called from the multi interface during the DO_DONE, PERFORM and
+ WAITPERFORM phases, and it should then return a proper fd set. Not setting
+ this will make libcurl use the generic default one. */
+ int (*perform_getsock)(struct Curl_easy *data,
+ struct connectdata *conn, curl_socket_t *socks);
+
+ /* This function *MAY* be set to a protocol-dependent function that is run
+ * by the curl_disconnect(), as a step in the disconnection. If the handler
+ * is called because the connection has been considered dead,
+ * dead_connection is set to TRUE. The connection is already disassociated
+ * from the transfer here.
+ */
+ CURLcode (*disconnect)(struct Curl_easy *, struct connectdata *,
+ bool dead_connection);
+
+ /* If used, this function gets called from transfer.c:readwrite_data() to
+ allow the protocol to do extra reads/writes */
+ CURLcode (*readwrite)(struct Curl_easy *data, struct connectdata *conn,
+ ssize_t *nread, bool *readmore);
+
+ /* This function can perform various checks on the connection. See
+ CONNCHECK_* for more information about the checks that can be performed,
+ and CONNRESULT_* for the results that can be returned. */
+ unsigned int (*connection_check)(struct Curl_easy *data,
+ struct connectdata *conn,
+ unsigned int checks_to_perform);
+
+ /* attach() attaches this transfer to this connection */
+ void (*attach)(struct Curl_easy *data, struct connectdata *conn);
+
+ int defport; /* Default port. */
+ curl_prot_t protocol; /* See CURLPROTO_* - this needs to be the single
+ specific protocol bit */
+ curl_prot_t family; /* single bit for protocol family; basically the
+ non-TLS name of the protocol this is */
+ unsigned int flags; /* Extra particular characteristics, see PROTOPT_* */
+
+};
+
+#define PROTOPT_NONE 0 /* nothing extra */
+#define PROTOPT_SSL (1<<0) /* uses SSL */
+#define PROTOPT_DUAL (1<<1) /* this protocol uses two connections */
+#define PROTOPT_CLOSEACTION (1<<2) /* need action before socket close */
+/* some protocols will have to call the underlying functions without regard to
+ what exact state the socket signals. IE even if the socket says "readable",
+ the send function might need to be called while uploading, or vice versa.
+*/
+#define PROTOPT_DIRLOCK (1<<3)
+#define PROTOPT_NONETWORK (1<<4) /* protocol doesn't use the network! */
+#define PROTOPT_NEEDSPWD (1<<5) /* needs a password, and if none is set it
+ gets a default */
+#define PROTOPT_NOURLQUERY (1<<6) /* protocol can't handle
+ url query strings (?foo=bar) ! */
+#define PROTOPT_CREDSPERREQUEST (1<<7) /* requires login credentials per
+ request instead of per connection */
+#define PROTOPT_ALPN (1<<8) /* set ALPN for this */
+#define PROTOPT_STREAM (1<<9) /* a protocol with individual logical streams */
+#define PROTOPT_URLOPTIONS (1<<10) /* allow options part in the userinfo field
+ of the URL */
+#define PROTOPT_PROXY_AS_HTTP (1<<11) /* allow this non-HTTP scheme over a
+ HTTP proxy as HTTP proxies may know
+ this protocol and act as a gateway */
+#define PROTOPT_WILDCARD (1<<12) /* protocol supports wildcard matching */
+#define PROTOPT_USERPWDCTRL (1<<13) /* Allow "control bytes" (< 32 ascii) in
+ user name and password */
+#define PROTOPT_NOTCPPROXY (1<<14) /* this protocol can't proxy over TCP */
+
+#define CONNCHECK_NONE 0 /* No checks */
+#define CONNCHECK_ISDEAD (1<<0) /* Check if the connection is dead. */
+#define CONNCHECK_KEEPALIVE (1<<1) /* Perform any keepalive function. */
+
+#define CONNRESULT_NONE 0 /* No extra information. */
+#define CONNRESULT_DEAD (1<<0) /* The connection is dead. */
+
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+struct postponed_data {
+ char *buffer; /* Temporal store for received data during
+ sending, must be freed */
+ size_t allocated_size; /* Size of temporal store */
+ size_t recv_size; /* Size of received data during sending */
+ size_t recv_processed; /* Size of processed part of postponed data */
+#ifdef DEBUGBUILD
+ curl_socket_t bindsock;/* Structure must be bound to specific socket,
+ used only for DEBUGASSERT */
+#endif /* DEBUGBUILD */
+};
+#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */
+
+struct proxy_info {
+ struct hostname host;
+ int port;
+ unsigned char proxytype; /* curl_proxytype: what kind of proxy that is in
+ use */
+ char *user; /* proxy user name string, allocated */
+ char *passwd; /* proxy password string, allocated */
+};
+
+struct ldapconninfo;
+
+#define TRNSPRT_TCP 3
+#define TRNSPRT_UDP 4
+#define TRNSPRT_QUIC 5
+#define TRNSPRT_UNIX 6
+
+/*
+ * The connectdata struct contains all fields and variables that should be
+ * unique for an entire connection.
+ */
+struct connectdata {
+ struct Curl_llist_element bundle_node; /* conncache */
+
+ /* chunk is for HTTP chunked encoding, but is in the general connectdata
+ struct only because we can do just about any protocol through an HTTP
+ proxy and an HTTP proxy may in fact respond using chunked encoding */
+ struct Curl_chunker chunk;
+
+ curl_closesocket_callback fclosesocket; /* function closing the socket(s) */
+ void *closesocket_client;
+
+ /* This is used by the connection cache logic. If this returns TRUE, this
+ handle is still used by one or more easy handles and can only used by any
+ other easy handle without careful consideration (== only for
+ multiplexing) and it cannot be used by another multi handle! */
+#define CONN_INUSE(c) ((c)->easyq.size)
+
+ /**** Fields set when inited and not modified again */
+ long connection_id; /* Contains a unique number to make it easier to
+ track the connections in the log output */
+
+ /* 'dns_entry' is the particular host we use. This points to an entry in the
+ DNS cache and it will not get pruned while locked. It gets unlocked in
+ multi_done(). This entry will be NULL if the connection is re-used as then
+ there is no name resolve done. */
+ struct Curl_dns_entry *dns_entry;
+
+ /* 'ip_addr' is the particular IP we connected to. It points to a struct
+ within the DNS cache, so this pointer is only valid as long as the DNS
+ cache entry remains locked. It gets unlocked in multi_done() */
+ struct Curl_addrinfo *ip_addr;
+ struct Curl_addrinfo *tempaddr[2]; /* for happy eyeballs */
+
+#ifdef ENABLE_QUIC
+ struct quicsocket hequic[2]; /* two, for happy eyeballs! */
+ struct quicsocket *quic;
+#endif
+
+ struct hostname host;
+ char *hostname_resolve; /* host name to resolve to address, allocated */
+ char *secondaryhostname; /* secondary socket host name (ftp) */
+ struct hostname conn_to_host; /* the host to connect to. valid only if
+ bits.conn_to_host is set */
+#ifndef CURL_DISABLE_PROXY
+ struct proxy_info socks_proxy;
+ struct proxy_info http_proxy;
+#endif
+ /* 'primary_ip' and 'primary_port' get filled with peer's numerical
+ ip address and port number whenever an outgoing connection is
+ *attempted* from the primary socket to a remote address. When more
+ than one address is tried for a connection these will hold data
+ for the last attempt. When the connection is actually established
+ these are updated with data which comes directly from the socket. */
+
+ char primary_ip[MAX_IPADR_LEN];
+ char *user; /* user name string, allocated */
+ char *passwd; /* password string, allocated */
+ char *options; /* options string, allocated */
+ char *sasl_authzid; /* authorization identity string, allocated */
+ char *oauth_bearer; /* OAUTH2 bearer, allocated */
+ struct curltime now; /* "current" time */
+ struct curltime created; /* creation time */
+ struct curltime lastused; /* when returned to the connection cache */
+ curl_socket_t sock[2]; /* two sockets, the second is used for the data
+ transfer when doing FTP */
+ curl_socket_t tempsock[2]; /* temporary sockets for happy eyeballs */
+ int tempfamily[2]; /* family used for the temp sockets */
+ Curl_recv *recv[2];
+ Curl_send *send[2];
+ struct Curl_cfilter *cfilter[2]; /* connection filters */
+
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+ struct postponed_data postponed[2]; /* two buffers for two sockets */
+#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */
+ struct ssl_primary_config ssl_config;
+#ifndef CURL_DISABLE_PROXY
+ struct ssl_primary_config proxy_ssl_config;
+#endif
+ struct ConnectBits bits; /* various state-flags for this connection */
+
+ /* connecttime: when connect() is called on the current IP address. Used to
+ be able to track when to move on to try next IP - but only when the multi
+ interface is used. */
+ struct curltime connecttime;
+
+ /* The field below gets set in Curl_connecthost */
+ /* how long time in milliseconds to spend on trying to connect to each IP
+ address, per family */
+ timediff_t timeoutms_per_addr[2];
+
+ const struct Curl_handler *handler; /* Connection's protocol handler */
+ const struct Curl_handler *given; /* The protocol first given */
+
+ /* Protocols can use a custom keepalive mechanism to keep connections alive.
+ This allows those protocols to track the last time the keepalive mechanism
+ was used on this connection. */
+ struct curltime keepalive;
+
+ /**** curl_get() phase fields */
+
+ curl_socket_t sockfd; /* socket to read from or CURL_SOCKET_BAD */
+ curl_socket_t writesockfd; /* socket to write to, it may very
+ well be the same we read from.
+ CURL_SOCKET_BAD disables */
+
+#ifdef HAVE_GSSAPI
+ BIT(sec_complete); /* if Kerberos is enabled for this connection */
+ unsigned char command_prot; /* enum protection_level */
+ unsigned char data_prot; /* enum protection_level */
+ unsigned char request_data_prot; /* enum protection_level */
+ size_t buffer_size;
+ struct krb5buffer in_buffer;
+ void *app_data;
+ const struct Curl_sec_client_mech *mech;
+ struct sockaddr_in local_addr;
+#endif
+
+#if defined(USE_KERBEROS5) /* Consider moving some of the above GSS-API */
+ struct kerberos5data krb5; /* variables into the structure definition, */
+#endif /* however, some of them are ftp specific. */
+
+ struct Curl_llist easyq; /* List of easy handles using this connection */
+ curl_seek_callback seek_func; /* function that seeks the input */
+ void *seek_client; /* pointer to pass to the seek() above */
+
+ /*************** Request - specific items ************/
+#if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS)
+ CtxtHandle *sslContext;
+#endif
+
+#ifdef USE_GSASL
+ struct gsasldata gsasl;
+#endif
+
+#if defined(USE_NTLM)
+ curlntlm http_ntlm_state;
+ curlntlm proxy_ntlm_state;
+
+ struct ntlmdata ntlm; /* NTLM differs from other authentication schemes
+ because it authenticates connections, not
+ single requests! */
+ struct ntlmdata proxyntlm; /* NTLM data for proxy */
+#endif
+
+#ifdef USE_SPNEGO
+ curlnegotiate http_negotiate_state;
+ curlnegotiate proxy_negotiate_state;
+
+ struct negotiatedata negotiate; /* state data for host Negotiate auth */
+ struct negotiatedata proxyneg; /* state data for proxy Negotiate auth */
+#endif
+
+ /* for chunked-encoded trailer */
+ struct dynbuf trailer;
+
+ union {
+#ifndef CURL_DISABLE_FTP
+ struct ftp_conn ftpc;
+#endif
+#ifndef CURL_DISABLE_HTTP
+ struct http_conn httpc;
+#endif
+#ifdef USE_SSH
+ struct ssh_conn sshc;
+#endif
+#ifndef CURL_DISABLE_TFTP
+ struct tftp_state_data *tftpc;
+#endif
+#ifndef CURL_DISABLE_IMAP
+ struct imap_conn imapc;
+#endif
+#ifndef CURL_DISABLE_POP3
+ struct pop3_conn pop3c;
+#endif
+#ifndef CURL_DISABLE_SMTP
+ struct smtp_conn smtpc;
+#endif
+#ifndef CURL_DISABLE_RTSP
+ struct rtsp_conn rtspc;
+#endif
+#ifndef CURL_DISABLE_SMB
+ struct smb_conn smbc;
+#endif
+ void *rtmp;
+ struct ldapconninfo *ldapc;
+#ifndef CURL_DISABLE_MQTT
+ struct mqtt_conn mqtt;
+#endif
+ } proto;
+
+ struct connectbundle *bundle; /* The bundle we are member of */
+#ifdef USE_UNIX_SOCKETS
+ char *unix_domain_socket;
+#endif
+#ifdef USE_HYPER
+ /* if set, an alternative data transfer function */
+ Curl_datastream datastream;
+#endif
+ /* When this connection is created, store the conditions for the local end
+ bind. This is stored before the actual bind and before any connection is
+ made and will serve the purpose of being used for comparison reasons so
+ that subsequent bound-requested connections aren't accidentally re-using
+ wrong connections. */
+ char *localdev;
+ int localportrange;
+ int cselect_bits; /* bitmask of socket events */
+ int waitfor; /* current READ/WRITE bits to wait for */
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+ int socks5_gssapi_enctype;
+#endif
+ /* The field below gets set in Curl_connecthost */
+ int num_addr; /* number of addresses to try to connect to */
+ int port; /* which port to use locally - to connect to */
+ int remote_port; /* the remote port, not the proxy port! */
+ int conn_to_port; /* the remote port to connect to. valid only if
+ bits.conn_to_port is set */
+#ifdef ENABLE_IPV6
+ unsigned int scope_id; /* Scope id for IPv6 */
+#endif
+ unsigned short localport;
+ unsigned short secondary_port; /* secondary socket remote port to connect to
+ (ftp) */
+ unsigned char alpn; /* APLN TLS negotiated protocol, a CURL_HTTP_VERSION*
+ value */
+ unsigned char transport; /* one of the TRNSPRT_* defines */
+ unsigned char ip_version; /* copied from the Curl_easy at creation time */
+ unsigned char httpversion; /* the HTTP version*10 reported by the server */
+ unsigned char connect_only;
+};
+
+/* The end of connectdata. */
+
+/*
+ * Struct to keep statistical and informational data.
+ * All variables in this struct must be initialized/reset in Curl_initinfo().
+ */
+struct PureInfo {
+ int httpcode; /* Recent HTTP, FTP, RTSP or SMTP response code */
+ int httpproxycode; /* response code from proxy when received separate */
+ int httpversion; /* the http version number X.Y = X*10+Y */
+ time_t filetime; /* If requested, this is might get set. Set to -1 if the
+ time was unretrievable. */
+ curl_off_t header_size; /* size of read header(s) in bytes */
+ curl_off_t request_size; /* the amount of bytes sent in the request(s) */
+ unsigned long proxyauthavail; /* what proxy auth types were announced */
+ unsigned long httpauthavail; /* what host auth types were announced */
+ long numconnects; /* how many new connection did libcurl created */
+ char *contenttype; /* the content type of the object */
+ char *wouldredirect; /* URL this would've been redirected to if asked to */
+ curl_off_t retry_after; /* info from Retry-After: header */
+
+ /* PureInfo members 'conn_primary_ip', 'conn_primary_port', 'conn_local_ip'
+ and, 'conn_local_port' are copied over from the connectdata struct in
+ order to allow curl_easy_getinfo() to return this information even when
+ the session handle is no longer associated with a connection, and also
+ allow curl_easy_reset() to clear this information from the session handle
+ without disturbing information which is still alive, and that might be
+ reused, in the connection cache. */
+
+ char conn_primary_ip[MAX_IPADR_LEN];
+ int conn_primary_port; /* this is the destination port to the connection,
+ which might have been a proxy */
+ int conn_remote_port; /* this is the "remote port", which is the port
+ number of the used URL, independent of proxy or
+ not */
+ char conn_local_ip[MAX_IPADR_LEN];
+ int conn_local_port;
+ const char *conn_scheme;
+ unsigned int conn_protocol;
+ struct curl_certinfo certs; /* info about the certs, only populated in
+ OpenSSL, GnuTLS, Schannel, NSS and GSKit
+ builds. Asked for with CURLOPT_CERTINFO
+ / CURLINFO_CERTINFO */
+ CURLproxycode pxcode;
+ BIT(timecond); /* set to TRUE if the time condition didn't match, which
+ thus made the document NOT get fetched */
+};
+
+
+struct Progress {
+ time_t lastshow; /* time() of the last displayed progress meter or NULL to
+ force redraw at next call */
+ curl_off_t size_dl; /* total expected size */
+ curl_off_t size_ul; /* total expected size */
+ curl_off_t downloaded; /* transferred so far */
+ curl_off_t uploaded; /* transferred so far */
+
+ curl_off_t current_speed; /* uses the currently fastest transfer */
+
+ int width; /* screen width at download start */
+ int flags; /* see progress.h */
+
+ timediff_t timespent;
+
+ curl_off_t dlspeed;
+ curl_off_t ulspeed;
+
+ timediff_t t_nslookup;
+ timediff_t t_connect;
+ timediff_t t_appconnect;
+ timediff_t t_pretransfer;
+ timediff_t t_starttransfer;
+ timediff_t t_redirect;
+
+ struct curltime start;
+ struct curltime t_startsingle;
+ struct curltime t_startop;
+ struct curltime t_acceptdata;
+
+
+ /* upload speed limit */
+ struct curltime ul_limit_start;
+ curl_off_t ul_limit_size;
+ /* download speed limit */
+ struct curltime dl_limit_start;
+ curl_off_t dl_limit_size;
+
+#define CURR_TIME (5 + 1) /* 6 entries for 5 seconds */
+
+ curl_off_t speeder[ CURR_TIME ];
+ struct curltime speeder_time[ CURR_TIME ];
+ int speeder_c;
+ BIT(callback); /* set when progress callback is used */
+ BIT(is_t_startransfer_set);
+};
+
+typedef enum {
+ RTSPREQ_NONE, /* first in list */
+ RTSPREQ_OPTIONS,
+ RTSPREQ_DESCRIBE,
+ RTSPREQ_ANNOUNCE,
+ RTSPREQ_SETUP,
+ RTSPREQ_PLAY,
+ RTSPREQ_PAUSE,
+ RTSPREQ_TEARDOWN,
+ RTSPREQ_GET_PARAMETER,
+ RTSPREQ_SET_PARAMETER,
+ RTSPREQ_RECORD,
+ RTSPREQ_RECEIVE,
+ RTSPREQ_LAST /* last in list */
+} Curl_RtspReq;
+
+struct auth {
+ unsigned long want; /* Bitmask set to the authentication methods wanted by
+ app (with CURLOPT_HTTPAUTH or CURLOPT_PROXYAUTH). */
+ unsigned long picked;
+ unsigned long avail; /* Bitmask for what the server reports to support for
+ this resource */
+ BIT(done); /* TRUE when the auth phase is done and ready to do the
+ actual request */
+ BIT(multipass); /* TRUE if this is not yet authenticated but within the
+ auth multipass negotiation */
+ BIT(iestyle); /* TRUE if digest should be done IE-style or FALSE if it
+ should be RFC compliant */
+};
+
+struct Curl_http2_dep {
+ struct Curl_http2_dep *next;
+ struct Curl_easy *data;
+};
+
+/*
+ * This struct is for holding data that was attempted to get sent to the user's
+ * callback but is held due to pausing. One instance per type (BOTH, HEADER,
+ * BODY).
+ */
+struct tempbuf {
+ struct dynbuf b;
+ int type; /* type of the 'tempwrite' buffer as a bitmask that is used with
+ Curl_client_write() */
+};
+
+/* Timers */
+typedef enum {
+ EXPIRE_100_TIMEOUT,
+ EXPIRE_ASYNC_NAME,
+ EXPIRE_CONNECTTIMEOUT,
+ EXPIRE_DNS_PER_NAME, /* family1 */
+ EXPIRE_DNS_PER_NAME2, /* family2 */
+ EXPIRE_HAPPY_EYEBALLS_DNS, /* See asyn-ares.c */
+ EXPIRE_HAPPY_EYEBALLS,
+ EXPIRE_MULTI_PENDING,
+ EXPIRE_RUN_NOW,
+ EXPIRE_SPEEDCHECK,
+ EXPIRE_TIMEOUT,
+ EXPIRE_TOOFAST,
+ EXPIRE_QUIC,
+ EXPIRE_FTP_ACCEPT,
+ EXPIRE_LAST /* not an actual timer, used as a marker only */
+} expire_id;
+
+
+typedef enum {
+ TRAILERS_NONE,
+ TRAILERS_INITIALIZED,
+ TRAILERS_SENDING,
+ TRAILERS_DONE
+} trailers_state;
+
+
+/*
+ * One instance for each timeout an easy handle can set.
+ */
+struct time_node {
+ struct Curl_llist_element list;
+ struct curltime time;
+ expire_id eid;
+};
+
+/* individual pieces of the URL */
+struct urlpieces {
+ char *scheme;
+ char *hostname;
+ char *port;
+ char *user;
+ char *password;
+ char *options;
+ char *path;
+ char *query;
+};
+
+struct UrlState {
+ /* Points to the connection cache */
+ struct conncache *conn_cache;
+ /* buffers to store authentication data in, as parsed from input options */
+ struct curltime keeps_speed; /* for the progress meter really */
+
+ long lastconnect_id; /* The last connection, -1 if undefined */
+ struct dynbuf headerb; /* buffer to store headers in */
+
+ char *buffer; /* download buffer */
+ char *ulbuf; /* allocated upload buffer or NULL */
+ curl_off_t current_speed; /* the ProgressShow() function sets this,
+ bytes / second */
+
+ /* host name, port number and protocol of the first (not followed) request.
+ if set, this should be the host name that we will sent authorization to,
+ no else. Used to make Location: following not keep sending user+password.
+ This is strdup()ed data. */
+ char *first_host;
+ int first_remote_port;
+ curl_prot_t first_remote_protocol;
+
+ int retrycount; /* number of retries on a new connection */
+ struct Curl_ssl_session *session; /* array of 'max_ssl_sessions' size */
+ long sessionage; /* number of the most recent session */
+ struct tempbuf tempwrite[3]; /* BOTH, HEADER, BODY */
+ unsigned int tempcount; /* number of entries in use in tempwrite, 0 - 3 */
+ int os_errno; /* filled in with errno whenever an error occurs */
+ char *scratch; /* huge buffer[set.buffer_size*2] for upload CRLF replacing */
+ long followlocation; /* redirect counter */
+ int requests; /* request counter: redirects + authentication retakes */
+#ifdef HAVE_SIGNAL
+ /* storage for the previous bag^H^H^HSIGPIPE signal handler :-) */
+ void (*prev_signal)(int sig);
+#endif
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+ struct digestdata digest; /* state data for host Digest auth */
+ struct digestdata proxydigest; /* state data for proxy Digest auth */
+#endif
+ struct auth authhost; /* auth details for host */
+ struct auth authproxy; /* auth details for proxy */
+#ifdef USE_CURL_ASYNC
+ struct Curl_async async; /* asynchronous name resolver data */
+#endif
+
+#if defined(USE_OPENSSL)
+ /* void instead of ENGINE to avoid bleeding OpenSSL into this header */
+ void *engine;
+#endif /* USE_OPENSSL */
+ struct curltime expiretime; /* set this with Curl_expire() only */
+ struct Curl_tree timenode; /* for the splay stuff */
+ struct Curl_llist timeoutlist; /* list of pending timeouts */
+ struct time_node expires[EXPIRE_LAST]; /* nodes for each expire type */
+
+ /* a place to store the most recently set (S)FTP entrypath */
+ char *most_recent_ftp_entrypath;
+ unsigned char httpwant; /* when non-zero, a specific HTTP version requested
+ to be used in the library's request(s) */
+ unsigned char httpversion; /* the lowest HTTP version*10 reported by any
+ server involved in this request */
+
+#if !defined(WIN32) && !defined(MSDOS) && !defined(__EMX__)
+/* do FTP line-end conversions on most platforms */
+#define CURL_DO_LINEEND_CONV
+ /* for FTP downloads: track CRLF sequences that span blocks */
+ BIT(prev_block_had_trailing_cr);
+ /* for FTP downloads: how many CRLFs did we converted to LFs? */
+ curl_off_t crlf_conversions;
+#endif
+ char *range; /* range, if used. See README for detailed specification on
+ this syntax. */
+ curl_off_t resume_from; /* continue [ftp] transfer from here */
+
+#ifndef CURL_DISABLE_RTSP
+ /* This RTSP state information survives requests and connections */
+ long rtsp_next_client_CSeq; /* the session's next client CSeq */
+ long rtsp_next_server_CSeq; /* the session's next server CSeq */
+ long rtsp_CSeq_recv; /* most recent CSeq received */
+#endif
+
+ curl_off_t infilesize; /* size of file to upload, -1 means unknown.
+ Copied from set.filesize at start of operation */
+#if defined(USE_HTTP2) || defined(USE_HTTP3)
+ size_t drain; /* Increased when this stream has data to read, even if its
+ socket is not necessarily is readable. Decreased when
+ checked. */
+#endif
+
+ curl_read_callback fread_func; /* read callback/function */
+ void *in; /* CURLOPT_READDATA */
+#ifdef USE_HTTP2
+ struct Curl_easy *stream_depends_on;
+ int stream_weight;
+#endif
+ CURLU *uh; /* URL handle for the current parsed URL */
+ struct urlpieces up;
+ unsigned char httpreq; /* Curl_HttpReq; what kind of HTTP request (if any)
+ is this */
+ char *url; /* work URL, copied from UserDefined */
+ char *referer; /* referer string */
+#ifndef CURL_DISABLE_COOKIES
+ struct curl_slist *cookielist; /* list of cookie files set by
+ curl_easy_setopt(COOKIEFILE) calls */
+#endif
+ struct curl_slist *resolve; /* set to point to the set.resolve list when
+ this should be dealt with in pretransfer */
+#ifndef CURL_DISABLE_HTTP
+ size_t trailers_bytes_sent;
+ struct dynbuf trailers_buf; /* a buffer containing the compiled trailing
+ headers */
+ struct Curl_llist httphdrs; /* received headers */
+ struct curl_header headerout; /* for external purposes */
+ struct Curl_header_store *prevhead; /* the latest added header */
+ trailers_state trailers_state; /* whether we are sending trailers
+ and what stage are we at */
+#endif
+#ifdef USE_HYPER
+ bool hconnect; /* set if a CONNECT request */
+ CURLcode hresult; /* used to pass return codes back from hyper callbacks */
+#endif
+
+ /* Dynamically allocated strings, MUST be freed before this struct is
+ killed. */
+ struct dynamically_allocated_data {
+ char *proxyuserpwd;
+ char *uagent;
+ char *accept_encoding;
+ char *userpwd;
+ char *rangeline;
+ char *ref;
+ char *host;
+ char *cookiehost;
+ char *rtsp_transport;
+ char *te; /* TE: request header */
+
+ /* transfer credentials */
+ char *user;
+ char *passwd;
+ char *proxyuser;
+ char *proxypasswd;
+ } aptr;
+
+#ifdef CURLDEBUG
+ BIT(conncache_lock);
+#endif
+ /* when curl_easy_perform() is called, the multi handle is "owned" by
+ the easy handle so curl_easy_cleanup() on such an easy handle will
+ also close the multi handle! */
+ BIT(multi_owned_by_easy);
+
+ BIT(this_is_a_follow); /* this is a followed Location: request */
+ BIT(refused_stream); /* this was refused, try again */
+ BIT(errorbuf); /* Set to TRUE if the error buffer is already filled in.
+ This must be set to FALSE every time _easy_perform() is
+ called. */
+ BIT(allow_port); /* Is set.use_port allowed to take effect or not. This
+ is always set TRUE when curl_easy_perform() is called. */
+ BIT(authproblem); /* TRUE if there's some problem authenticating */
+ /* set after initial USER failure, to prevent an authentication loop */
+ BIT(wildcardmatch); /* enable wildcard matching */
+ BIT(expect100header); /* TRUE if we added Expect: 100-continue */
+ BIT(disableexpect); /* TRUE if Expect: is disabled due to a previous
+ 417 response */
+ BIT(use_range);
+ BIT(rangestringalloc); /* the range string is malloc()'ed */
+ BIT(done); /* set to FALSE when Curl_init_do() is called and set to TRUE
+ when multi_done() is called, to prevent multi_done() to get
+ invoked twice when the multi interface is used. */
+ BIT(stream_depends_e); /* set or don't set the Exclusive bit */
+ BIT(previouslypending); /* this transfer WAS in the multi->pending queue */
+ BIT(cookie_engine);
+ BIT(prefer_ascii); /* ASCII rather than binary */
+ BIT(list_only); /* list directory contents */
+ BIT(url_alloc); /* URL string is malloc()'ed */
+ BIT(referer_alloc); /* referer string is malloc()ed */
+ BIT(wildcard_resolve); /* Set to true if any resolve change is a wildcard */
+ BIT(rewindbeforesend);/* TRUE when the sending couldn't be stopped even
+ though it will be discarded. We must call the data
+ rewind callback before trying to send again. */
+};
+
+/*
+ * This 'UserDefined' struct must only contain data that is set once to go
+ * for many (perhaps) independent connections. Values that are generated or
+ * calculated internally for the "session handle" MUST be defined within the
+ * 'struct UrlState' instead. The only exceptions MUST note the changes in
+ * the 'DynamicStatic' struct.
+ * Character pointer fields point to dynamic storage, unless otherwise stated.
+ */
+
+struct Curl_multi; /* declared in multihandle.c */
+
+/*
+ * This enumeration MUST not use conditional directives (#ifdefs), new
+ * null terminated strings MUST be added to the enumeration immediately
+ * before STRING_LASTZEROTERMINATED, binary fields immediately before
+ * STRING_LAST. When doing so, ensure that the packages/OS400/chkstring.c
+ * test is updated and applicable changes for EBCDIC to ASCII conversion
+ * are catered for in curl_easy_setopt_ccsid()
+ */
+enum dupstring {
+ STRING_CERT, /* client certificate file name */
+ STRING_CERT_PROXY, /* client certificate file name */
+ STRING_CERT_TYPE, /* format for certificate (default: PEM)*/
+ STRING_CERT_TYPE_PROXY, /* format for certificate (default: PEM)*/
+ STRING_COOKIE, /* HTTP cookie string to send */
+ STRING_COOKIEJAR, /* dump all cookies to this file */
+ STRING_CUSTOMREQUEST, /* HTTP/FTP/RTSP request/method to use */
+ STRING_DEFAULT_PROTOCOL, /* Protocol to use when the URL doesn't specify */
+ STRING_DEVICE, /* local network interface/address to use */
+ STRING_ENCODING, /* Accept-Encoding string */
+ STRING_FTP_ACCOUNT, /* ftp account data */
+ STRING_FTP_ALTERNATIVE_TO_USER, /* command to send if USER/PASS fails */
+ STRING_FTPPORT, /* port to send with the FTP PORT command */
+ STRING_KEY, /* private key file name */
+ STRING_KEY_PROXY, /* private key file name */
+ STRING_KEY_PASSWD, /* plain text private key password */
+ STRING_KEY_PASSWD_PROXY, /* plain text private key password */
+ STRING_KEY_TYPE, /* format for private key (default: PEM) */
+ STRING_KEY_TYPE_PROXY, /* format for private key (default: PEM) */
+ STRING_KRB_LEVEL, /* krb security level */
+ STRING_NETRC_FILE, /* if not NULL, use this instead of trying to find
+ $HOME/.netrc */
+ STRING_PROXY, /* proxy to use */
+ STRING_PRE_PROXY, /* pre socks proxy to use */
+ STRING_SET_RANGE, /* range, if used */
+ STRING_SET_REFERER, /* custom string for the HTTP referer field */
+ STRING_SET_URL, /* what original URL to work on */
+ STRING_SSL_CAPATH, /* CA directory name (doesn't work on windows) */
+ STRING_SSL_CAPATH_PROXY, /* CA directory name (doesn't work on windows) */
+ STRING_SSL_CAFILE, /* certificate file to verify peer against */
+ STRING_SSL_CAFILE_PROXY, /* certificate file to verify peer against */
+ STRING_SSL_PINNEDPUBLICKEY, /* public key file to verify peer against */
+ STRING_SSL_PINNEDPUBLICKEY_PROXY, /* public key file to verify proxy */
+ STRING_SSL_CIPHER_LIST, /* list of ciphers to use */
+ STRING_SSL_CIPHER_LIST_PROXY, /* list of ciphers to use */
+ STRING_SSL_CIPHER13_LIST, /* list of TLS 1.3 ciphers to use */
+ STRING_SSL_CIPHER13_LIST_PROXY, /* list of TLS 1.3 ciphers to use */
+ STRING_USERAGENT, /* User-Agent string */
+ STRING_SSL_CRLFILE, /* crl file to check certificate */
+ STRING_SSL_CRLFILE_PROXY, /* crl file to check certificate */
+ STRING_SSL_ISSUERCERT, /* issuer cert file to check certificate */
+ STRING_SSL_ISSUERCERT_PROXY, /* issuer cert file to check certificate */
+ STRING_SSL_ENGINE, /* name of ssl engine */
+ STRING_USERNAME, /* <username>, if used */
+ STRING_PASSWORD, /* <password>, if used */
+ STRING_OPTIONS, /* <options>, if used */
+ STRING_PROXYUSERNAME, /* Proxy <username>, if used */
+ STRING_PROXYPASSWORD, /* Proxy <password>, if used */
+ STRING_NOPROXY, /* List of hosts which should not use the proxy, if
+ used */
+ STRING_RTSP_SESSION_ID, /* Session ID to use */
+ STRING_RTSP_STREAM_URI, /* Stream URI for this request */
+ STRING_RTSP_TRANSPORT, /* Transport for this session */
+ STRING_SSH_PRIVATE_KEY, /* path to the private key file for auth */
+ STRING_SSH_PUBLIC_KEY, /* path to the public key file for auth */
+ STRING_SSH_HOST_PUBLIC_KEY_MD5, /* md5 of host public key in ascii hex */
+ STRING_SSH_HOST_PUBLIC_KEY_SHA256, /* sha256 of host public key in base64 */
+ STRING_SSH_KNOWNHOSTS, /* file name of knownhosts file */
+ STRING_PROXY_SERVICE_NAME, /* Proxy service name */
+ STRING_SERVICE_NAME, /* Service name */
+ STRING_MAIL_FROM,
+ STRING_MAIL_AUTH,
+ STRING_TLSAUTH_USERNAME, /* TLS auth <username> */
+ STRING_TLSAUTH_USERNAME_PROXY, /* TLS auth <username> */
+ STRING_TLSAUTH_PASSWORD, /* TLS auth <password> */
+ STRING_TLSAUTH_PASSWORD_PROXY, /* TLS auth <password> */
+ STRING_BEARER, /* <bearer>, if used */
+ STRING_UNIX_SOCKET_PATH, /* path to Unix socket, if used */
+ STRING_TARGET, /* CURLOPT_REQUEST_TARGET */
+ STRING_DOH, /* CURLOPT_DOH_URL */
+ STRING_ALTSVC, /* CURLOPT_ALTSVC */
+ STRING_HSTS, /* CURLOPT_HSTS */
+ STRING_SASL_AUTHZID, /* CURLOPT_SASL_AUTHZID */
+ STRING_DNS_SERVERS,
+ STRING_DNS_INTERFACE,
+ STRING_DNS_LOCAL_IP4,
+ STRING_DNS_LOCAL_IP6,
+ STRING_SSL_EC_CURVES,
+
+ /* -- end of null-terminated strings -- */
+
+ STRING_LASTZEROTERMINATED,
+
+ /* -- below this are pointers to binary data that cannot be strdup'ed. --- */
+
+ STRING_COPYPOSTFIELDS, /* if POST, set the fields' values here */
+
+ STRING_AWS_SIGV4, /* Parameters for V4 signature */
+
+ STRING_LAST /* not used, just an end-of-list marker */
+};
+
+enum dupblob {
+ BLOB_CERT,
+ BLOB_CERT_PROXY,
+ BLOB_KEY,
+ BLOB_KEY_PROXY,
+ BLOB_SSL_ISSUERCERT,
+ BLOB_SSL_ISSUERCERT_PROXY,
+ BLOB_CAINFO,
+ BLOB_CAINFO_PROXY,
+ BLOB_LAST
+};
+
+/* callback that gets called when this easy handle is completed within a multi
+ handle. Only used for internally created transfers, like for example
+ DoH. */
+typedef int (*multidone_func)(struct Curl_easy *easy, CURLcode result);
+
+struct UserDefined {
+ FILE *err; /* the stderr user data goes here */
+ void *debugdata; /* the data that will be passed to fdebug */
+ char *errorbuffer; /* (Static) store failure messages in here */
+ void *out; /* CURLOPT_WRITEDATA */
+ void *in_set; /* CURLOPT_READDATA */
+ void *writeheader; /* write the header to this if non-NULL */
+ unsigned short proxyport; /* If non-zero, use this port number by
+ default. If the proxy string features a
+ ":[port]" that one will override this. */
+ unsigned short use_port; /* which port to use (when not using default) */
+ unsigned long httpauth; /* kind of HTTP authentication to use (bitmask) */
+ unsigned long proxyauth; /* kind of proxy authentication to use (bitmask) */
+#ifndef CURL_DISABLE_PROXY
+ unsigned char socks5auth;/* kind of SOCKS5 authentication to use (bitmask) */
+#endif
+ long maxredirs; /* maximum no. of http(s) redirects to follow, set to -1
+ for infinity */
+
+ void *postfields; /* if POST, set the fields' values here */
+ curl_seek_callback seek_func; /* function that seeks the input */
+ curl_off_t postfieldsize; /* if POST, this might have a size to use instead
+ of strlen(), and then the data *may* be binary
+ (contain zero bytes) */
+ unsigned short localport; /* local port number to bind to */
+ int localportrange; /* number of additional port numbers to test in case the
+ 'localport' one can't be bind()ed */
+ curl_write_callback fwrite_func; /* function that stores the output */
+ curl_write_callback fwrite_header; /* function that stores headers */
+ curl_write_callback fwrite_rtp; /* function that stores interleaved RTP */
+ curl_read_callback fread_func_set; /* function that reads the input */
+ curl_progress_callback fprogress; /* OLD and deprecated progress callback */
+ curl_xferinfo_callback fxferinfo; /* progress callback */
+ curl_debug_callback fdebug; /* function that write informational data */
+ curl_ioctl_callback ioctl_func; /* function for I/O control */
+ curl_sockopt_callback fsockopt; /* function for setting socket options */
+ void *sockopt_client; /* pointer to pass to the socket options callback */
+ curl_opensocket_callback fopensocket; /* function for checking/translating
+ the address and opening the
+ socket */
+ void *opensocket_client;
+ curl_closesocket_callback fclosesocket; /* function for closing the
+ socket */
+ void *closesocket_client;
+ curl_prereq_callback fprereq; /* pre-initial request callback */
+ void *prereq_userp; /* pre-initial request user data */
+
+ void *seek_client; /* pointer to pass to the seek callback */
+#ifndef CURL_DISABLE_HSTS
+ curl_hstsread_callback hsts_read;
+ void *hsts_read_userp;
+ curl_hstswrite_callback hsts_write;
+ void *hsts_write_userp;
+#endif
+ void *progress_client; /* pointer to pass to the progress callback */
+ void *ioctl_client; /* pointer to pass to the ioctl callback */
+ unsigned int timeout; /* ms, 0 means no timeout */
+ unsigned int connecttimeout; /* ms, 0 means no timeout */
+ unsigned int happy_eyeballs_timeout; /* ms, 0 is a valid value */
+ unsigned int server_response_timeout; /* ms, 0 means no timeout */
+ long maxage_conn; /* in seconds, max idle time to allow a connection that
+ is to be reused */
+ long maxlifetime_conn; /* in seconds, max time since creation to allow a
+ connection that is to be reused */
+#ifndef CURL_DISABLE_TFTP
+ long tftp_blksize; /* in bytes, 0 means use default */
+#endif
+ curl_off_t filesize; /* size of file to upload, -1 means unknown */
+ long low_speed_limit; /* bytes/second */
+ long low_speed_time; /* number of seconds */
+ curl_off_t max_send_speed; /* high speed limit in bytes/second for upload */
+ curl_off_t max_recv_speed; /* high speed limit in bytes/second for
+ download */
+ curl_off_t set_resume_from; /* continue [ftp] transfer from here */
+ struct curl_slist *headers; /* linked list of extra headers */
+ struct curl_slist *proxyheaders; /* linked list of extra CONNECT headers */
+ struct curl_httppost *httppost; /* linked list of old POST data */
+ curl_mimepart mimepost; /* MIME/POST data. */
+ struct curl_slist *quote; /* after connection is established */
+ struct curl_slist *postquote; /* after the transfer */
+ struct curl_slist *prequote; /* before the transfer, after type */
+ struct curl_slist *source_quote; /* 3rd party quote */
+ struct curl_slist *source_prequote; /* in 3rd party transfer mode - before
+ the transfer on source host */
+ struct curl_slist *source_postquote; /* in 3rd party transfer mode - after
+ the transfer on source host */
+#ifndef CURL_DISABLE_TELNET
+ struct curl_slist *telnet_options; /* linked list of telnet options */
+#endif
+ struct curl_slist *resolve; /* list of names to add/remove from
+ DNS cache */
+ struct curl_slist *connect_to; /* list of host:port mappings to override
+ the hostname and port to connect to */
+ time_t timevalue; /* what time to compare with */
+ unsigned char timecondition; /* kind of time comparison: curl_TimeCond */
+ unsigned char proxytype; /* what kind of proxy: curl_proxytype */
+ unsigned char method; /* what kind of HTTP request: Curl_HttpReq */
+ unsigned char httpwant; /* when non-zero, a specific HTTP version requested
+ to be used in the library's request(s) */
+ struct ssl_config_data ssl; /* user defined SSL stuff */
+#ifndef CURL_DISABLE_PROXY
+ struct ssl_config_data proxy_ssl; /* user defined SSL stuff for proxy */
+#endif
+ struct ssl_general_config general_ssl; /* general user defined SSL stuff */
+ int dns_cache_timeout; /* DNS cache timeout (seconds) */
+ unsigned int buffer_size; /* size of receive buffer to use */
+ unsigned int upload_buffer_size; /* size of upload buffer to use,
+ keep it >= CURL_MAX_WRITE_SIZE */
+ void *private_data; /* application-private data */
+ struct curl_slist *http200aliases; /* linked list of aliases for http200 */
+ unsigned char ipver; /* the CURL_IPRESOLVE_* defines in the public header
+ file 0 - whatever, 1 - v2, 2 - v6 */
+ curl_off_t max_filesize; /* Maximum file size to download */
+#ifndef CURL_DISABLE_FTP
+ unsigned char ftp_filemethod; /* how to get to a file: curl_ftpfile */
+ unsigned char ftpsslauth; /* what AUTH XXX to try: curl_ftpauth */
+ unsigned char ftp_ccc; /* FTP CCC options: curl_ftpccc */
+ unsigned int accepttimeout; /* in milliseconds, 0 means no timeout */
+#endif
+ /* Desppie the name ftp_create_missing_dirs is for FTP(S) and SFTP
+ 1 - create directories that don't exist
+ 2 - the same but also allow MKD to fail once
+ */
+ unsigned char ftp_create_missing_dirs;
+#ifdef USE_LIBSSH2
+ curl_sshhostkeycallback ssh_hostkeyfunc; /* hostkey check callback */
+ void *ssh_hostkeyfunc_userp; /* custom pointer to callback */
+#endif
+
+ curl_sshkeycallback ssh_keyfunc; /* key matching callback */
+ void *ssh_keyfunc_userp; /* custom pointer to callback */
+#ifndef CURL_DISABLE_NETRC
+ unsigned char use_netrc; /* enum CURL_NETRC_OPTION values */
+#endif
+ curl_usessl use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or
+ IMAP or POP3 or others! */
+ unsigned int new_file_perms; /* when creating remote files */
+ unsigned int new_directory_perms; /* when creating remote dirs */
+ int ssh_auth_types; /* allowed SSH auth types */
+ char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */
+ struct curl_blob *blobs[BLOB_LAST];
+#ifdef ENABLE_IPV6
+ unsigned int scope_id; /* Scope id for IPv6 */
+#endif
+ curl_prot_t allowed_protocols;
+ curl_prot_t redir_protocols;
+ unsigned int mime_options; /* Mime option flags. */
+
+#ifndef CURL_DISABLE_RTSP
+ void *rtp_out; /* write RTP to this if non-NULL */
+ /* Common RTSP header options */
+ Curl_RtspReq rtspreq; /* RTSP request type */
+#endif
+#ifndef CURL_DISABLE_FTP
+ curl_chunk_bgn_callback chunk_bgn; /* called before part of transfer
+ starts */
+ curl_chunk_end_callback chunk_end; /* called after part transferring
+ stopped */
+ curl_fnmatch_callback fnmatch; /* callback to decide which file corresponds
+ to pattern (e.g. if WILDCARDMATCH is on) */
+ void *fnmatch_data;
+#endif
+ long gssapi_delegation; /* GSS-API credential delegation, see the
+ documentation of CURLOPT_GSSAPI_DELEGATION */
+
+ int tcp_keepidle; /* seconds in idle before sending keepalive probe */
+ int tcp_keepintvl; /* seconds between TCP keepalive probes */
+
+ size_t maxconnects; /* Max idle connections in the connection cache */
+
+ long expect_100_timeout; /* in milliseconds */
+#ifdef USE_HTTP2
+ struct Curl_easy *stream_depends_on;
+ int stream_weight;
+ struct Curl_http2_dep *stream_dependents;
+#endif
+ curl_resolver_start_callback resolver_start; /* optional callback called
+ before resolver start */
+ void *resolver_start_client; /* pointer to pass to resolver start callback */
+ long upkeep_interval_ms; /* Time between calls for connection upkeep. */
+ multidone_func fmultidone;
+#ifndef CURL_DISABLE_DOH
+ struct Curl_easy *dohfor; /* this is a DoH request for that transfer */
+#endif
+ CURLU *uh; /* URL handle for the current parsed URL */
+ void *trailer_data; /* pointer to pass to trailer data callback */
+ curl_trailer_callback trailer_callback; /* trailing data callback */
+ char keep_post; /* keep POSTs as POSTs after a 30x request; each
+ bit represents a request, from 301 to 303 */
+#ifndef CURL_DISABLE_SMTP
+ struct curl_slist *mail_rcpt; /* linked list of mail recipients */
+ BIT(mail_rcpt_allowfails); /* allow RCPT TO command to fail for some
+ recipients */
+#endif
+ unsigned char connect_only; /* make connection/request, then let
+ application use the socket */
+ BIT(is_fread_set); /* has read callback been set to non-NULL? */
+#ifndef CURL_DISABLE_TFTP
+ BIT(tftp_no_options); /* do not send TFTP options requests */
+#endif
+ BIT(sep_headers); /* handle host and proxy headers separately */
+ BIT(cookiesession); /* new cookie session? */
+ BIT(crlf); /* convert crlf on ftp upload(?) */
+ BIT(ssh_compression); /* enable SSH compression */
+
+/* Here follows boolean settings that define how to behave during
+ this session. They are STATIC, set by libcurl users or at least initially
+ and they don't change during operations. */
+ BIT(quick_exit); /* set 1L when it is okay to leak things (like
+ threads), as we're about to exit() anyway and
+ don't want lengthy cleanups to delay termination,
+ e.g. after a DNS timeout */
+ BIT(get_filetime); /* get the time and get of the remote file */
+ BIT(tunnel_thru_httpproxy); /* use CONNECT through an HTTP proxy */
+ BIT(prefer_ascii); /* ASCII rather than binary */
+ BIT(remote_append); /* append, not overwrite, on upload */
+ BIT(list_only); /* list directory */
+#ifndef CURL_DISABLE_FTP
+ BIT(ftp_use_port); /* use the FTP PORT command */
+ BIT(ftp_use_epsv); /* if EPSV is to be attempted or not */
+ BIT(ftp_use_eprt); /* if EPRT is to be attempted or not */
+ BIT(ftp_use_pret); /* if PRET is to be used before PASV or not */
+ BIT(ftp_skip_ip); /* skip the IP address the FTP server passes on to
+ us */
+ BIT(wildcard_enabled); /* enable wildcard matching */
+#endif
+ BIT(hide_progress); /* don't use the progress meter */
+ BIT(http_fail_on_error); /* fail on HTTP error codes >= 400 */
+ BIT(http_keep_sending_on_error); /* for HTTP status codes >= 300 */
+ BIT(http_follow_location); /* follow HTTP redirects */
+ BIT(http_transfer_encoding); /* request compressed HTTP transfer-encoding */
+ BIT(allow_auth_to_other_hosts);
+ BIT(include_header); /* include received protocol headers in data output */
+ BIT(http_set_referer); /* is a custom referer used */
+ BIT(http_auto_referer); /* set "correct" referer when following
+ location: */
+ BIT(opt_no_body); /* as set with CURLOPT_NOBODY */
+ BIT(upload); /* upload request */
+ BIT(verbose); /* output verbosity */
+ BIT(krb); /* Kerberos connection requested */
+ BIT(reuse_forbid); /* forbidden to be reused, close after use */
+ BIT(reuse_fresh); /* do not re-use an existing connection */
+ BIT(no_signal); /* do not use any signal/alarm handler */
+ BIT(tcp_nodelay); /* whether to enable TCP_NODELAY or not */
+ BIT(ignorecl); /* ignore content length */
+ BIT(http_te_skip); /* pass the raw body data to the user, even when
+ transfer-encoded (chunked, compressed) */
+ BIT(http_ce_skip); /* pass the raw body data to the user, even when
+ content-encoded (chunked, compressed) */
+ BIT(proxy_transfer_mode); /* set transfer mode (;type=<a|i>) when doing
+ FTP via an HTTP proxy */
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+ BIT(socks5_gssapi_nec); /* Flag to support NEC SOCKS5 server */
+#endif
+ BIT(sasl_ir); /* Enable/disable SASL initial response */
+ BIT(tcp_keepalive); /* use TCP keepalives */
+ BIT(tcp_fastopen); /* use TCP Fast Open */
+ BIT(ssl_enable_alpn);/* TLS ALPN extension? */
+ BIT(path_as_is); /* allow dotdots? */
+ BIT(pipewait); /* wait for multiplex status before starting a new
+ connection */
+ BIT(suppress_connect_headers); /* suppress proxy CONNECT response headers
+ from user callbacks */
+ BIT(dns_shuffle_addresses); /* whether to shuffle addresses before use */
+ BIT(stream_depends_e); /* set or don't set the Exclusive bit */
+ BIT(haproxyprotocol); /* whether to send HAProxy PROXY protocol v1
+ header */
+ BIT(abstract_unix_socket);
+ BIT(disallow_username_in_url); /* disallow username in url */
+#ifndef CURL_DISABLE_DOH
+ BIT(doh); /* DNS-over-HTTPS enabled */
+ BIT(doh_verifypeer); /* DoH certificate peer verification */
+ BIT(doh_verifyhost); /* DoH certificate hostname verification */
+ BIT(doh_verifystatus); /* DoH certificate status verification */
+#endif
+ BIT(http09_allowed); /* allow HTTP/0.9 responses */
+#ifdef USE_WEBSOCKETS
+ BIT(ws_raw_mode);
+#endif
+};
+
+struct Names {
+ struct Curl_hash *hostcache;
+ enum {
+ HCACHE_NONE, /* not pointing to anything */
+ HCACHE_MULTI, /* points to a shared one in the multi handle */
+ HCACHE_SHARED /* points to a shared one in a shared object */
+ } hostcachetype;
+};
+
+/*
+ * The 'connectdata' struct MUST have all the connection oriented stuff as we
+ * may have several simultaneous connections and connection structs in memory.
+ *
+ * The 'struct UserDefined' must only contain data that is set once to go for
+ * many (perhaps) independent connections. Values that are generated or
+ * calculated internally for the "session handle" must be defined within the
+ * 'struct UrlState' instead.
+ */
+
+struct Curl_easy {
+ /* First a simple identifier to easier detect if a user mix up this easy
+ handle with a multi handle. Set this to CURLEASY_MAGIC_NUMBER */
+ unsigned int magic;
+
+ /* first, two fields for the linked list of these */
+ struct Curl_easy *next;
+ struct Curl_easy *prev;
+
+ struct connectdata *conn;
+ struct Curl_llist_element connect_queue;
+ struct Curl_llist_element conn_queue; /* list per connectdata */
+
+ CURLMstate mstate; /* the handle's state */
+ CURLcode result; /* previous result */
+
+ struct Curl_message msg; /* A single posted message. */
+
+ /* Array with the plain socket numbers this handle takes care of, in no
+ particular order. Note that all sockets are added to the sockhash, where
+ the state etc are also kept. This array is mostly used to detect when a
+ socket is to be removed from the hash. See singlesocket(). */
+ curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE];
+ unsigned char actions[MAX_SOCKSPEREASYHANDLE]; /* action for each socket in
+ sockets[] */
+ int numsocks;
+
+ struct Names dns;
+ struct Curl_multi *multi; /* if non-NULL, points to the multi handle
+ struct to which this "belongs" when used by
+ the multi interface */
+ struct Curl_multi *multi_easy; /* if non-NULL, points to the multi handle
+ struct to which this "belongs" when used
+ by the easy interface */
+ struct Curl_share *share; /* Share, handles global variable mutexing */
+#ifdef USE_LIBPSL
+ struct PslCache *psl; /* The associated PSL cache. */
+#endif
+ struct SingleRequest req; /* Request-specific data */
+ struct UserDefined set; /* values set by the libcurl user */
+#ifndef CURL_DISABLE_COOKIES
+ struct CookieInfo *cookies; /* the cookies, read from files and servers.
+ NOTE that the 'cookie' field in the
+ UserDefined struct defines if the "engine"
+ is to be used or not. */
+#endif
+#ifndef CURL_DISABLE_HSTS
+ struct hsts *hsts;
+#endif
+#ifndef CURL_DISABLE_ALTSVC
+ struct altsvcinfo *asi; /* the alt-svc cache */
+#endif
+ struct Progress progress; /* for all the progress meter data */
+ struct UrlState state; /* struct for fields used for state info and
+ other dynamic purposes */
+#ifndef CURL_DISABLE_FTP
+ struct WildcardData wildcard; /* wildcard download state info */
+#endif
+ struct PureInfo info; /* stats, reports and info data */
+ struct curl_tlssessioninfo tsi; /* Information about the TLS session, only
+ valid after a client has asked for it */
+#ifdef USE_HYPER
+ struct hyptransfer hyp;
+#endif
+};
+
+#define LIBCURL_NAME "libcurl"
+
+#endif /* HEADER_CURL_URLDATA_H */
diff --git a/libs/libcurl/src/vauth/cleartext.c b/libs/libcurl/src/vauth/cleartext.c index b82b171467..f1db19adbf 100644 --- a/libs/libcurl/src/vauth/cleartext.c +++ b/libs/libcurl/src/vauth/cleartext.c @@ -1,138 +1,138 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - * RFC4616 PLAIN authentication - * 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) - -#include <curl/curl.h> -#include "urldata.h" - -#include "vauth/vauth.h" -#include "curl_md5.h" -#include "warnless.h" -#include "strtok.h" -#include "sendf.h" -#include "curl_printf.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* - * Curl_auth_create_plain_message() - * - * This is used to generate an already encoded PLAIN message ready - * for sending to the recipient. - * - * Parameters: - * - * authzid [in] - The authorization identity. - * authcid [in] - The authentication identity. - * passwd [in] - The password. - * out [out] - The result storage. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_create_plain_message(const char *authzid, - const char *authcid, - const char *passwd, - struct bufref *out) -{ - char *plainauth; - size_t plainlen; - size_t zlen; - size_t clen; - size_t plen; - - zlen = (authzid == NULL ? 0 : strlen(authzid)); - clen = strlen(authcid); - plen = strlen(passwd); - - /* Compute binary message length. Check for overflows. */ - if((zlen > SIZE_T_MAX/4) || (clen > SIZE_T_MAX/4) || - (plen > (SIZE_T_MAX/2 - 2))) - return CURLE_OUT_OF_MEMORY; - plainlen = zlen + clen + plen + 2; - - plainauth = malloc(plainlen + 1); - if(!plainauth) - return CURLE_OUT_OF_MEMORY; - - /* Calculate the reply */ - if(zlen) - memcpy(plainauth, authzid, zlen); - plainauth[zlen] = '\0'; - memcpy(plainauth + zlen + 1, authcid, clen); - plainauth[zlen + clen + 1] = '\0'; - memcpy(plainauth + zlen + clen + 2, passwd, plen); - plainauth[plainlen] = '\0'; - Curl_bufref_set(out, plainauth, plainlen, curl_free); - return CURLE_OK; -} - -/* - * Curl_auth_create_login_message() - * - * This is used to generate an already encoded LOGIN message containing the - * user name or password ready for sending to the recipient. - * - * Parameters: - * - * valuep [in] - The user name or user's password. - * out [out] - The result storage. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_create_login_message(const char *valuep, struct bufref *out) -{ - Curl_bufref_set(out, valuep, strlen(valuep), NULL); - return CURLE_OK; -} - -/* - * Curl_auth_create_external_message() - * - * This is used to generate an already encoded EXTERNAL message containing - * the user name ready for sending to the recipient. - * - * Parameters: - * - * user [in] - The user name. - * out [out] - The result storage. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_create_external_message(const char *user, - struct bufref *out) -{ - /* This is the same formatting as the login message */ - return Curl_auth_create_login_message(user, out); -} - -#endif /* if no users */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ * RFC4616 PLAIN authentication
+ * 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)
+
+#include <curl/curl.h>
+#include "urldata.h"
+
+#include "vauth/vauth.h"
+#include "curl_md5.h"
+#include "warnless.h"
+#include "strtok.h"
+#include "sendf.h"
+#include "curl_printf.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * Curl_auth_create_plain_message()
+ *
+ * This is used to generate an already encoded PLAIN message ready
+ * for sending to the recipient.
+ *
+ * Parameters:
+ *
+ * authzid [in] - The authorization identity.
+ * authcid [in] - The authentication identity.
+ * passwd [in] - The password.
+ * out [out] - The result storage.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_plain_message(const char *authzid,
+ const char *authcid,
+ const char *passwd,
+ struct bufref *out)
+{
+ char *plainauth;
+ size_t plainlen;
+ size_t zlen;
+ size_t clen;
+ size_t plen;
+
+ zlen = (authzid == NULL ? 0 : strlen(authzid));
+ clen = strlen(authcid);
+ plen = strlen(passwd);
+
+ /* Compute binary message length. Check for overflows. */
+ if((zlen > SIZE_T_MAX/4) || (clen > SIZE_T_MAX/4) ||
+ (plen > (SIZE_T_MAX/2 - 2)))
+ return CURLE_OUT_OF_MEMORY;
+ plainlen = zlen + clen + plen + 2;
+
+ plainauth = malloc(plainlen + 1);
+ if(!plainauth)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Calculate the reply */
+ if(zlen)
+ memcpy(plainauth, authzid, zlen);
+ plainauth[zlen] = '\0';
+ memcpy(plainauth + zlen + 1, authcid, clen);
+ plainauth[zlen + clen + 1] = '\0';
+ memcpy(plainauth + zlen + clen + 2, passwd, plen);
+ plainauth[plainlen] = '\0';
+ Curl_bufref_set(out, plainauth, plainlen, curl_free);
+ return CURLE_OK;
+}
+
+/*
+ * Curl_auth_create_login_message()
+ *
+ * This is used to generate an already encoded LOGIN message containing the
+ * user name or password ready for sending to the recipient.
+ *
+ * Parameters:
+ *
+ * valuep [in] - The user name or user's password.
+ * out [out] - The result storage.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_login_message(const char *valuep, struct bufref *out)
+{
+ Curl_bufref_set(out, valuep, strlen(valuep), NULL);
+ return CURLE_OK;
+}
+
+/*
+ * Curl_auth_create_external_message()
+ *
+ * This is used to generate an already encoded EXTERNAL message containing
+ * the user name ready for sending to the recipient.
+ *
+ * Parameters:
+ *
+ * user [in] - The user name.
+ * out [out] - The result storage.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_external_message(const char *user,
+ struct bufref *out)
+{
+ /* This is the same formatting as the login message */
+ return Curl_auth_create_login_message(user, out);
+}
+
+#endif /* if no users */
diff --git a/libs/libcurl/src/vauth/cram.c b/libs/libcurl/src/vauth/cram.c index 475d31b8d7..9c0c743f37 100644 --- a/libs/libcurl/src/vauth/cram.c +++ b/libs/libcurl/src/vauth/cram.c @@ -1,97 +1,97 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if !defined(CURL_DISABLE_CRYPTO_AUTH) - -#include <curl/curl.h> -#include "urldata.h" - -#include "vauth/vauth.h" -#include "curl_hmac.h" -#include "curl_md5.h" -#include "warnless.h" -#include "curl_printf.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - - -/* - * Curl_auth_create_cram_md5_message() - * - * This is used to generate a CRAM-MD5 response message ready for sending to - * the recipient. - * - * Parameters: - * - * chlg [in] - The challenge. - * userp [in] - The user name. - * passwdp [in] - The user's password. - * out [out] - The result storage. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, - const char *userp, - const char *passwdp, - struct bufref *out) -{ - struct HMAC_context *ctxt; - unsigned char digest[MD5_DIGEST_LEN]; - char *response; - - /* Compute the digest using the password as the key */ - ctxt = Curl_HMAC_init(Curl_HMAC_MD5, - (const unsigned char *) passwdp, - curlx_uztoui(strlen(passwdp))); - if(!ctxt) - return CURLE_OUT_OF_MEMORY; - - /* Update the digest with the given challenge */ - if(Curl_bufref_len(chlg)) - Curl_HMAC_update(ctxt, Curl_bufref_ptr(chlg), - curlx_uztoui(Curl_bufref_len(chlg))); - - /* Finalise the digest */ - Curl_HMAC_final(ctxt, digest); - - /* Generate the response */ - response = aprintf( - "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - userp, digest[0], digest[1], digest[2], digest[3], digest[4], - digest[5], digest[6], digest[7], digest[8], digest[9], digest[10], - digest[11], digest[12], digest[13], digest[14], digest[15]); - if(!response) - return CURLE_OUT_OF_MEMORY; - - Curl_bufref_set(out, response, strlen(response), curl_free); - return CURLE_OK; -} - -#endif /* !CURL_DISABLE_CRYPTO_AUTH */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_CRYPTO_AUTH)
+
+#include <curl/curl.h>
+#include "urldata.h"
+
+#include "vauth/vauth.h"
+#include "curl_hmac.h"
+#include "curl_md5.h"
+#include "warnless.h"
+#include "curl_printf.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+/*
+ * Curl_auth_create_cram_md5_message()
+ *
+ * This is used to generate a CRAM-MD5 response message ready for sending to
+ * the recipient.
+ *
+ * Parameters:
+ *
+ * chlg [in] - The challenge.
+ * userp [in] - The user name.
+ * passwdp [in] - The user's password.
+ * out [out] - The result storage.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg,
+ const char *userp,
+ const char *passwdp,
+ struct bufref *out)
+{
+ struct HMAC_context *ctxt;
+ unsigned char digest[MD5_DIGEST_LEN];
+ char *response;
+
+ /* Compute the digest using the password as the key */
+ ctxt = Curl_HMAC_init(Curl_HMAC_MD5,
+ (const unsigned char *) passwdp,
+ curlx_uztoui(strlen(passwdp)));
+ if(!ctxt)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Update the digest with the given challenge */
+ if(Curl_bufref_len(chlg))
+ Curl_HMAC_update(ctxt, Curl_bufref_ptr(chlg),
+ curlx_uztoui(Curl_bufref_len(chlg)));
+
+ /* Finalise the digest */
+ Curl_HMAC_final(ctxt, digest);
+
+ /* Generate the response */
+ response = aprintf(
+ "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ userp, digest[0], digest[1], digest[2], digest[3], digest[4],
+ digest[5], digest[6], digest[7], digest[8], digest[9], digest[10],
+ digest[11], digest[12], digest[13], digest[14], digest[15]);
+ if(!response)
+ return CURLE_OUT_OF_MEMORY;
+
+ Curl_bufref_set(out, response, strlen(response), curl_free);
+ return CURLE_OK;
+}
+
+#endif /* !CURL_DISABLE_CRYPTO_AUTH */
diff --git a/libs/libcurl/src/vauth/digest.c b/libs/libcurl/src/vauth/digest.c index f945e8b6c9..995aeadc78 100644 --- a/libs/libcurl/src/vauth/digest.c +++ b/libs/libcurl/src/vauth/digest.c @@ -1,994 +1,994 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - * RFC2831 DIGEST-MD5 authentication - * RFC7616 DIGEST-SHA256, DIGEST-SHA512-256 authentication - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if !defined(CURL_DISABLE_CRYPTO_AUTH) - -#include <curl/curl.h> - -#include "vauth/vauth.h" -#include "vauth/digest.h" -#include "urldata.h" -#include "curl_base64.h" -#include "curl_hmac.h" -#include "curl_md5.h" -#include "curl_sha256.h" -#include "vtls/vtls.h" -#include "warnless.h" -#include "strtok.h" -#include "strcase.h" -#include "curl_printf.h" -#include "rand.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -#define SESSION_ALGO 1 /* for algos with this bit set */ - -#define ALGO_MD5 0 -#define ALGO_MD5SESS (ALGO_MD5 | SESSION_ALGO) -#define ALGO_SHA256 2 -#define ALGO_SHA256SESS (ALGO_SHA256 | SESSION_ALGO) -#define ALGO_SHA512_256 4 -#define ALGO_SHA512_256SESS (ALGO_SHA512_256 | SESSION_ALGO) - -#if !defined(USE_WINDOWS_SSPI) -#define DIGEST_QOP_VALUE_AUTH (1 << 0) -#define DIGEST_QOP_VALUE_AUTH_INT (1 << 1) -#define DIGEST_QOP_VALUE_AUTH_CONF (1 << 2) - -#define DIGEST_QOP_VALUE_STRING_AUTH "auth" -#define DIGEST_QOP_VALUE_STRING_AUTH_INT "auth-int" -#define DIGEST_QOP_VALUE_STRING_AUTH_CONF "auth-conf" -#endif - -bool Curl_auth_digest_get_pair(const char *str, char *value, char *content, - const char **endptr) -{ - int c; - bool starts_with_quote = FALSE; - bool escape = FALSE; - - for(c = DIGEST_MAX_VALUE_LENGTH - 1; (*str && (*str != '=') && c--);) - *value++ = *str++; - *value = 0; - - if('=' != *str++) - /* eek, no match */ - return FALSE; - - if('\"' == *str) { - /* This starts with a quote so it must end with one as well! */ - str++; - starts_with_quote = TRUE; - } - - for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) { - if(!escape) { - switch(*str) { - case '\\': - if(starts_with_quote) { - /* the start of an escaped quote */ - escape = TRUE; - continue; - } - break; - - case ',': - if(!starts_with_quote) { - /* This signals the end of the content if we didn't get a starting - quote and then we do "sloppy" parsing */ - c = 0; /* the end */ - continue; - } - break; - - case '\r': - case '\n': - /* end of string */ - if(starts_with_quote) - return FALSE; /* No closing quote */ - c = 0; - continue; - - case '\"': - if(starts_with_quote) { - /* end of string */ - c = 0; - continue; - } - else - return FALSE; - break; - } - } - - escape = FALSE; - *content++ = *str; - } - if(escape) - return FALSE; /* No character after backslash */ - - *content = 0; - *endptr = str; - - return TRUE; -} - -#if !defined(USE_WINDOWS_SSPI) -/* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/ -static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */ - unsigned char *dest) /* 33 bytes */ -{ - int i; - for(i = 0; i < 16; i++) - msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]); -} - -/* Convert sha256 chunk to RFC7616 -suitable ascii string*/ -static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */ - unsigned char *dest) /* 65 bytes */ -{ - int i; - for(i = 0; i < 32; i++) - msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]); -} - -/* Perform quoted-string escaping as described in RFC2616 and its errata */ -static char *auth_digest_string_quoted(const char *source) -{ - char *dest; - const char *s = source; - size_t n = 1; /* null terminator */ - - /* Calculate size needed */ - while(*s) { - ++n; - if(*s == '"' || *s == '\\') { - ++n; - } - ++s; - } - - dest = malloc(n); - if(dest) { - char *d = dest; - s = source; - while(*s) { - if(*s == '"' || *s == '\\') { - *d++ = '\\'; - } - *d++ = *s++; - } - *d = 0; - } - - return dest; -} - -/* Retrieves the value for a corresponding key from the challenge string - * returns TRUE if the key could be found, FALSE if it does not exists - */ -static bool auth_digest_get_key_value(const char *chlg, - const char *key, - char *value, - size_t max_val_len, - char end_char) -{ - char *find_pos; - size_t i; - - find_pos = strstr(chlg, key); - if(!find_pos) - return FALSE; - - find_pos += strlen(key); - - for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i) - value[i] = *find_pos++; - value[i] = '\0'; - - return TRUE; -} - -static CURLcode auth_digest_get_qop_values(const char *options, int *value) -{ - char *tmp; - char *token; - char *tok_buf = NULL; - - /* Initialise the output */ - *value = 0; - - /* Tokenise the list of qop values. Use a temporary clone of the buffer since - strtok_r() ruins it. */ - tmp = strdup(options); - if(!tmp) - return CURLE_OUT_OF_MEMORY; - - token = strtok_r(tmp, ",", &tok_buf); - while(token) { - if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) - *value |= DIGEST_QOP_VALUE_AUTH; - else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) - *value |= DIGEST_QOP_VALUE_AUTH_INT; - else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_CONF)) - *value |= DIGEST_QOP_VALUE_AUTH_CONF; - - token = strtok_r(NULL, ",", &tok_buf); - } - - free(tmp); - - return CURLE_OK; -} - -/* - * auth_decode_digest_md5_message() - * - * This is used internally to decode an already encoded DIGEST-MD5 challenge - * message into the separate attributes. - * - * Parameters: - * - * chlgref [in] - The challenge message. - * nonce [in/out] - The buffer where the nonce will be stored. - * nlen [in] - The length of the nonce buffer. - * realm [in/out] - The buffer where the realm will be stored. - * rlen [in] - The length of the realm buffer. - * alg [in/out] - The buffer where the algorithm will be stored. - * alen [in] - The length of the algorithm buffer. - * qop [in/out] - The buffer where the qop-options will be stored. - * qlen [in] - The length of the qop buffer. - * - * Returns CURLE_OK on success. - */ -static CURLcode auth_decode_digest_md5_message(const struct bufref *chlgref, - char *nonce, size_t nlen, - char *realm, size_t rlen, - char *alg, size_t alen, - char *qop, size_t qlen) -{ - const char *chlg = (const char *) Curl_bufref_ptr(chlgref); - - /* Ensure we have a valid challenge message */ - if(!Curl_bufref_len(chlgref)) - return CURLE_BAD_CONTENT_ENCODING; - - /* Retrieve nonce string from the challenge */ - if(!auth_digest_get_key_value(chlg, "nonce=\"", nonce, nlen, '\"')) - return CURLE_BAD_CONTENT_ENCODING; - - /* Retrieve realm string from the challenge */ - if(!auth_digest_get_key_value(chlg, "realm=\"", realm, rlen, '\"')) { - /* Challenge does not have a realm, set empty string [RFC2831] page 6 */ - strcpy(realm, ""); - } - - /* Retrieve algorithm string from the challenge */ - if(!auth_digest_get_key_value(chlg, "algorithm=", alg, alen, ',')) - return CURLE_BAD_CONTENT_ENCODING; - - /* Retrieve qop-options string from the challenge */ - if(!auth_digest_get_key_value(chlg, "qop=\"", qop, qlen, '\"')) - return CURLE_BAD_CONTENT_ENCODING; - - return CURLE_OK; -} - -/* - * Curl_auth_is_digest_supported() - * - * This is used to evaluate if DIGEST is supported. - * - * Parameters: None - * - * Returns TRUE as DIGEST as handled by libcurl. - */ -bool Curl_auth_is_digest_supported(void) -{ - return TRUE; -} - -/* - * Curl_auth_create_digest_md5_message() - * - * This is used to generate an already encoded DIGEST-MD5 response message - * ready for sending to the recipient. - * - * Parameters: - * - * data [in] - The session handle. - * chlg [in] - The challenge message. - * userp [in] - The user name. - * passwdp [in] - The user's password. - * service [in] - The service type such as http, smtp, pop or imap. - * out [out] - The result storage. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, - const struct bufref *chlg, - const char *userp, - const char *passwdp, - const char *service, - struct bufref *out) -{ - size_t i; - struct MD5_context *ctxt; - char *response = NULL; - unsigned char digest[MD5_DIGEST_LEN]; - char HA1_hex[2 * MD5_DIGEST_LEN + 1]; - char HA2_hex[2 * MD5_DIGEST_LEN + 1]; - char resp_hash_hex[2 * MD5_DIGEST_LEN + 1]; - char nonce[64]; - char realm[128]; - char algorithm[64]; - char qop_options[64]; - int qop_values; - char cnonce[33]; - char nonceCount[] = "00000001"; - char method[] = "AUTHENTICATE"; - char qop[] = DIGEST_QOP_VALUE_STRING_AUTH; - char *spn = NULL; - - /* Decode the challenge message */ - CURLcode result = auth_decode_digest_md5_message(chlg, - nonce, sizeof(nonce), - realm, sizeof(realm), - algorithm, - sizeof(algorithm), - qop_options, - sizeof(qop_options)); - if(result) - return result; - - /* We only support md5 sessions */ - if(strcmp(algorithm, "md5-sess") != 0) - return CURLE_BAD_CONTENT_ENCODING; - - /* Get the qop-values from the qop-options */ - result = auth_digest_get_qop_values(qop_options, &qop_values); - if(result) - return result; - - /* We only support auth quality-of-protection */ - if(!(qop_values & DIGEST_QOP_VALUE_AUTH)) - return CURLE_BAD_CONTENT_ENCODING; - - /* Generate 32 random hex chars, 32 bytes + 1 null-termination */ - result = Curl_rand_hex(data, (unsigned char *)cnonce, sizeof(cnonce)); - if(result) - return result; - - /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */ - ctxt = Curl_MD5_init(Curl_DIGEST_MD5); - if(!ctxt) - return CURLE_OUT_OF_MEMORY; - - Curl_MD5_update(ctxt, (const unsigned char *) userp, - curlx_uztoui(strlen(userp))); - Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); - Curl_MD5_update(ctxt, (const unsigned char *) realm, - curlx_uztoui(strlen(realm))); - Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); - Curl_MD5_update(ctxt, (const unsigned char *) passwdp, - curlx_uztoui(strlen(passwdp))); - Curl_MD5_final(ctxt, digest); - - ctxt = Curl_MD5_init(Curl_DIGEST_MD5); - if(!ctxt) - return CURLE_OUT_OF_MEMORY; - - Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN); - Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); - Curl_MD5_update(ctxt, (const unsigned char *) nonce, - curlx_uztoui(strlen(nonce))); - Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); - Curl_MD5_update(ctxt, (const unsigned char *) cnonce, - curlx_uztoui(strlen(cnonce))); - Curl_MD5_final(ctxt, digest); - - /* Convert calculated 16 octet hex into 32 bytes string */ - for(i = 0; i < MD5_DIGEST_LEN; i++) - msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]); - - /* Generate our SPN */ - spn = Curl_auth_build_spn(service, realm, NULL); - if(!spn) - return CURLE_OUT_OF_MEMORY; - - /* Calculate H(A2) */ - ctxt = Curl_MD5_init(Curl_DIGEST_MD5); - if(!ctxt) { - free(spn); - - return CURLE_OUT_OF_MEMORY; - } - - Curl_MD5_update(ctxt, (const unsigned char *) method, - curlx_uztoui(strlen(method))); - Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); - Curl_MD5_update(ctxt, (const unsigned char *) spn, - curlx_uztoui(strlen(spn))); - Curl_MD5_final(ctxt, digest); - - for(i = 0; i < MD5_DIGEST_LEN; i++) - msnprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]); - - /* Now calculate the response hash */ - ctxt = Curl_MD5_init(Curl_DIGEST_MD5); - if(!ctxt) { - free(spn); - - return CURLE_OUT_OF_MEMORY; - } - - Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN); - Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); - Curl_MD5_update(ctxt, (const unsigned char *) nonce, - curlx_uztoui(strlen(nonce))); - Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); - - Curl_MD5_update(ctxt, (const unsigned char *) nonceCount, - curlx_uztoui(strlen(nonceCount))); - Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); - Curl_MD5_update(ctxt, (const unsigned char *) cnonce, - curlx_uztoui(strlen(cnonce))); - Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); - Curl_MD5_update(ctxt, (const unsigned char *) qop, - curlx_uztoui(strlen(qop))); - Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); - - Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN); - Curl_MD5_final(ctxt, digest); - - for(i = 0; i < MD5_DIGEST_LEN; i++) - msnprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]); - - /* Generate the response */ - response = aprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\"," - "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s," - "qop=%s", - userp, realm, nonce, - cnonce, nonceCount, spn, resp_hash_hex, qop); - free(spn); - if(!response) - return CURLE_OUT_OF_MEMORY; - - /* Return the response. */ - Curl_bufref_set(out, response, strlen(response), curl_free); - return result; -} - -/* - * Curl_auth_decode_digest_http_message() - * - * This is used to decode a HTTP DIGEST challenge message into the separate - * attributes. - * - * Parameters: - * - * chlg [in] - The challenge message. - * digest [in/out] - The digest data struct being used and modified. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_decode_digest_http_message(const char *chlg, - struct digestdata *digest) -{ - bool before = FALSE; /* got a nonce before */ - bool foundAuth = FALSE; - bool foundAuthInt = FALSE; - char *token = NULL; - char *tmp = NULL; - - /* If we already have received a nonce, keep that in mind */ - if(digest->nonce) - before = TRUE; - - /* Clean up any former leftovers and initialise to defaults */ - Curl_auth_digest_cleanup(digest); - - for(;;) { - char value[DIGEST_MAX_VALUE_LENGTH]; - char content[DIGEST_MAX_CONTENT_LENGTH]; - - /* Pass all additional spaces here */ - while(*chlg && ISBLANK(*chlg)) - chlg++; - - /* Extract a value=content pair */ - if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) { - if(strcasecompare(value, "nonce")) { - free(digest->nonce); - digest->nonce = strdup(content); - if(!digest->nonce) - return CURLE_OUT_OF_MEMORY; - } - else if(strcasecompare(value, "stale")) { - if(strcasecompare(content, "true")) { - digest->stale = TRUE; - digest->nc = 1; /* we make a new nonce now */ - } - } - else if(strcasecompare(value, "realm")) { - free(digest->realm); - digest->realm = strdup(content); - if(!digest->realm) - return CURLE_OUT_OF_MEMORY; - } - else if(strcasecompare(value, "opaque")) { - free(digest->opaque); - digest->opaque = strdup(content); - if(!digest->opaque) - return CURLE_OUT_OF_MEMORY; - } - else if(strcasecompare(value, "qop")) { - char *tok_buf = NULL; - /* Tokenize the list and choose auth if possible, use a temporary - clone of the buffer since strtok_r() ruins it */ - tmp = strdup(content); - if(!tmp) - return CURLE_OUT_OF_MEMORY; - - token = strtok_r(tmp, ",", &tok_buf); - while(token) { - /* Pass additional spaces here */ - while(*token && ISBLANK(*token)) - token++; - if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) { - foundAuth = TRUE; - } - else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) { - foundAuthInt = TRUE; - } - token = strtok_r(NULL, ",", &tok_buf); - } - - free(tmp); - - /* Select only auth or auth-int. Otherwise, ignore */ - if(foundAuth) { - free(digest->qop); - digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH); - if(!digest->qop) - return CURLE_OUT_OF_MEMORY; - } - else if(foundAuthInt) { - free(digest->qop); - digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH_INT); - if(!digest->qop) - return CURLE_OUT_OF_MEMORY; - } - } - else if(strcasecompare(value, "algorithm")) { - free(digest->algorithm); - digest->algorithm = strdup(content); - if(!digest->algorithm) - return CURLE_OUT_OF_MEMORY; - - if(strcasecompare(content, "MD5-sess")) - digest->algo = ALGO_MD5SESS; - else if(strcasecompare(content, "MD5")) - digest->algo = ALGO_MD5; - else if(strcasecompare(content, "SHA-256")) - digest->algo = ALGO_SHA256; - else if(strcasecompare(content, "SHA-256-SESS")) - digest->algo = ALGO_SHA256SESS; - else if(strcasecompare(content, "SHA-512-256")) - digest->algo = ALGO_SHA512_256; - else if(strcasecompare(content, "SHA-512-256-SESS")) - digest->algo = ALGO_SHA512_256SESS; - else - return CURLE_BAD_CONTENT_ENCODING; - } - else if(strcasecompare(value, "userhash")) { - if(strcasecompare(content, "true")) { - digest->userhash = TRUE; - } - } - else { - /* Unknown specifier, ignore it! */ - } - } - else - break; /* We're done here */ - - /* Pass all additional spaces here */ - while(*chlg && ISBLANK(*chlg)) - chlg++; - - /* Allow the list to be comma-separated */ - if(',' == *chlg) - chlg++; - } - - /* We had a nonce since before, and we got another one now without - 'stale=true'. This means we provided bad credentials in the previous - request */ - if(before && !digest->stale) - return CURLE_BAD_CONTENT_ENCODING; - - /* We got this header without a nonce, that's a bad Digest line! */ - if(!digest->nonce) - return CURLE_BAD_CONTENT_ENCODING; - - /* "<algo>-sess" protocol versions require "auth" or "auth-int" qop */ - if(!digest->qop && (digest->algo & SESSION_ALGO)) - return CURLE_BAD_CONTENT_ENCODING; - - return CURLE_OK; -} - -/* - * auth_create_digest_http_message() - * - * This is used to generate a HTTP DIGEST response message ready for sending - * to the recipient. - * - * Parameters: - * - * data [in] - The session handle. - * userp [in] - The user name. - * passwdp [in] - The user's password. - * request [in] - The HTTP request. - * uripath [in] - The path of the HTTP uri. - * digest [in/out] - The digest data struct being used and modified. - * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. - * outlen [out] - The length of the output message. - * - * Returns CURLE_OK on success. - */ -static CURLcode auth_create_digest_http_message( - struct Curl_easy *data, - const char *userp, - const char *passwdp, - const unsigned char *request, - const unsigned char *uripath, - struct digestdata *digest, - char **outptr, size_t *outlen, - void (*convert_to_ascii)(unsigned char *, unsigned char *), - CURLcode (*hash)(unsigned char *, const unsigned char *, - const size_t)) -{ - CURLcode result; - unsigned char hashbuf[32]; /* 32 bytes/256 bits */ - unsigned char request_digest[65]; - unsigned char ha1[65]; /* 64 digits and 1 zero byte */ - unsigned char ha2[65]; /* 64 digits and 1 zero byte */ - char userh[65]; - char *cnonce = NULL; - size_t cnonce_sz = 0; - char *userp_quoted; - char *realm_quoted; - char *nonce_quoted; - char *response = NULL; - char *hashthis = NULL; - char *tmp = NULL; - - if(!digest->nc) - digest->nc = 1; - - if(!digest->cnonce) { - char cnoncebuf[33]; - result = Curl_rand_hex(data, (unsigned char *)cnoncebuf, - sizeof(cnoncebuf)); - if(result) - return result; - - result = Curl_base64_encode(cnoncebuf, strlen(cnoncebuf), - &cnonce, &cnonce_sz); - if(result) - return result; - - digest->cnonce = cnonce; - } - - if(digest->userhash) { - hashthis = aprintf("%s:%s", userp, digest->realm ? digest->realm : ""); - if(!hashthis) - return CURLE_OUT_OF_MEMORY; - - hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); - free(hashthis); - convert_to_ascii(hashbuf, (unsigned char *)userh); - } - - /* - If the algorithm is "MD5" or unspecified (which then defaults to MD5): - - A1 = unq(username-value) ":" unq(realm-value) ":" passwd - - If the algorithm is "MD5-sess" then: - - A1 = H(unq(username-value) ":" unq(realm-value) ":" passwd) ":" - unq(nonce-value) ":" unq(cnonce-value) - */ - - hashthis = aprintf("%s:%s:%s", userp, digest->realm ? digest->realm : "", - passwdp); - if(!hashthis) - return CURLE_OUT_OF_MEMORY; - - hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); - free(hashthis); - convert_to_ascii(hashbuf, ha1); - - if(digest->algo & SESSION_ALGO) { - /* nonce and cnonce are OUTSIDE the hash */ - tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce); - if(!tmp) - return CURLE_OUT_OF_MEMORY; - - hash(hashbuf, (unsigned char *) tmp, strlen(tmp)); - free(tmp); - convert_to_ascii(hashbuf, ha1); - } - - /* - If the "qop" directive's value is "auth" or is unspecified, then A2 is: - - A2 = Method ":" digest-uri-value - - If the "qop" value is "auth-int", then A2 is: - - A2 = Method ":" digest-uri-value ":" H(entity-body) - - (The "Method" value is the HTTP request method as specified in section - 5.1.1 of RFC 2616) - */ - - hashthis = aprintf("%s:%s", request, uripath); - if(!hashthis) - return CURLE_OUT_OF_MEMORY; - - if(digest->qop && strcasecompare(digest->qop, "auth-int")) { - /* We don't support auth-int for PUT or POST */ - char hashed[65]; - char *hashthis2; - - hash(hashbuf, (const unsigned char *)"", 0); - convert_to_ascii(hashbuf, (unsigned char *)hashed); - - hashthis2 = aprintf("%s:%s", hashthis, hashed); - free(hashthis); - hashthis = hashthis2; - } - - if(!hashthis) - return CURLE_OUT_OF_MEMORY; - - hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); - free(hashthis); - convert_to_ascii(hashbuf, ha2); - - if(digest->qop) { - hashthis = aprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce, digest->nc, - digest->cnonce, digest->qop, ha2); - } - else { - hashthis = aprintf("%s:%s:%s", ha1, digest->nonce, ha2); - } - - if(!hashthis) - return CURLE_OUT_OF_MEMORY; - - hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); - free(hashthis); - convert_to_ascii(hashbuf, request_digest); - - /* For test case 64 (snooped from a Mozilla 1.3a request) - - Authorization: Digest username="testuser", realm="testrealm", \ - nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca" - - Digest parameters are all quoted strings. Username which is provided by - the user will need double quotes and backslashes within it escaped. - realm, nonce, and opaque will need backslashes as well as they were - de-escaped when copied from request header. cnonce is generated with - web-safe characters. uri is already percent encoded. nc is 8 hex - characters. algorithm and qop with standard values only contain web-safe - characters. - */ - userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp); - if(!userp_quoted) - return CURLE_OUT_OF_MEMORY; - if(digest->realm) - realm_quoted = auth_digest_string_quoted(digest->realm); - else { - realm_quoted = malloc(1); - if(realm_quoted) - realm_quoted[0] = 0; - } - if(!realm_quoted) { - free(userp_quoted); - return CURLE_OUT_OF_MEMORY; - } - nonce_quoted = auth_digest_string_quoted(digest->nonce); - if(!nonce_quoted) { - free(realm_quoted); - free(userp_quoted); - return CURLE_OUT_OF_MEMORY; - } - - if(digest->qop) { - response = aprintf("username=\"%s\", " - "realm=\"%s\", " - "nonce=\"%s\", " - "uri=\"%s\", " - "cnonce=\"%s\", " - "nc=%08x, " - "qop=%s, " - "response=\"%s\"", - userp_quoted, - realm_quoted, - nonce_quoted, - uripath, - digest->cnonce, - digest->nc, - digest->qop, - request_digest); - - /* Increment nonce-count to use another nc value for the next request */ - digest->nc++; - } - else { - response = aprintf("username=\"%s\", " - "realm=\"%s\", " - "nonce=\"%s\", " - "uri=\"%s\", " - "response=\"%s\"", - userp_quoted, - realm_quoted, - nonce_quoted, - uripath, - request_digest); - } - free(nonce_quoted); - free(realm_quoted); - free(userp_quoted); - if(!response) - return CURLE_OUT_OF_MEMORY; - - /* Add the optional fields */ - if(digest->opaque) { - char *opaque_quoted; - /* Append the opaque */ - opaque_quoted = auth_digest_string_quoted(digest->opaque); - if(!opaque_quoted) { - free(response); - return CURLE_OUT_OF_MEMORY; - } - tmp = aprintf("%s, opaque=\"%s\"", response, opaque_quoted); - free(response); - free(opaque_quoted); - if(!tmp) - return CURLE_OUT_OF_MEMORY; - - response = tmp; - } - - if(digest->algorithm) { - /* Append the algorithm */ - tmp = aprintf("%s, algorithm=%s", response, digest->algorithm); - free(response); - if(!tmp) - return CURLE_OUT_OF_MEMORY; - - response = tmp; - } - - if(digest->userhash) { - /* Append the userhash */ - tmp = aprintf("%s, userhash=true", response); - free(response); - if(!tmp) - return CURLE_OUT_OF_MEMORY; - - response = tmp; - } - - /* Return the output */ - *outptr = response; - *outlen = strlen(response); - - return CURLE_OK; -} - -/* - * Curl_auth_create_digest_http_message() - * - * This is used to generate a HTTP DIGEST response message ready for sending - * to the recipient. - * - * Parameters: - * - * data [in] - The session handle. - * userp [in] - The user name. - * passwdp [in] - The user's password. - * request [in] - The HTTP request. - * uripath [in] - The path of the HTTP uri. - * digest [in/out] - The digest data struct being used and modified. - * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. - * outlen [out] - The length of the output message. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const unsigned char *request, - const unsigned char *uripath, - struct digestdata *digest, - char **outptr, size_t *outlen) -{ - if(digest->algo <= ALGO_MD5SESS) - return auth_create_digest_http_message(data, userp, passwdp, - request, uripath, digest, - outptr, outlen, - auth_digest_md5_to_ascii, - Curl_md5it); - DEBUGASSERT(digest->algo <= ALGO_SHA512_256SESS); - return auth_create_digest_http_message(data, userp, passwdp, - request, uripath, digest, - outptr, outlen, - auth_digest_sha256_to_ascii, - Curl_sha256it); -} - -/* - * Curl_auth_digest_cleanup() - * - * This is used to clean up the digest specific data. - * - * Parameters: - * - * digest [in/out] - The digest data struct being cleaned up. - * - */ -void Curl_auth_digest_cleanup(struct digestdata *digest) -{ - Curl_safefree(digest->nonce); - Curl_safefree(digest->cnonce); - Curl_safefree(digest->realm); - Curl_safefree(digest->opaque); - Curl_safefree(digest->qop); - Curl_safefree(digest->algorithm); - - digest->nc = 0; - digest->algo = ALGO_MD5; /* default algorithm */ - digest->stale = FALSE; /* default means normal, not stale */ - digest->userhash = FALSE; -} -#endif /* !USE_WINDOWS_SSPI */ - -#endif /* CURL_DISABLE_CRYPTO_AUTH */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ * RFC2831 DIGEST-MD5 authentication
+ * RFC7616 DIGEST-SHA256, DIGEST-SHA512-256 authentication
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_CRYPTO_AUTH)
+
+#include <curl/curl.h>
+
+#include "vauth/vauth.h"
+#include "vauth/digest.h"
+#include "urldata.h"
+#include "curl_base64.h"
+#include "curl_hmac.h"
+#include "curl_md5.h"
+#include "curl_sha256.h"
+#include "vtls/vtls.h"
+#include "warnless.h"
+#include "strtok.h"
+#include "strcase.h"
+#include "curl_printf.h"
+#include "rand.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define SESSION_ALGO 1 /* for algos with this bit set */
+
+#define ALGO_MD5 0
+#define ALGO_MD5SESS (ALGO_MD5 | SESSION_ALGO)
+#define ALGO_SHA256 2
+#define ALGO_SHA256SESS (ALGO_SHA256 | SESSION_ALGO)
+#define ALGO_SHA512_256 4
+#define ALGO_SHA512_256SESS (ALGO_SHA512_256 | SESSION_ALGO)
+
+#if !defined(USE_WINDOWS_SSPI)
+#define DIGEST_QOP_VALUE_AUTH (1 << 0)
+#define DIGEST_QOP_VALUE_AUTH_INT (1 << 1)
+#define DIGEST_QOP_VALUE_AUTH_CONF (1 << 2)
+
+#define DIGEST_QOP_VALUE_STRING_AUTH "auth"
+#define DIGEST_QOP_VALUE_STRING_AUTH_INT "auth-int"
+#define DIGEST_QOP_VALUE_STRING_AUTH_CONF "auth-conf"
+#endif
+
+bool Curl_auth_digest_get_pair(const char *str, char *value, char *content,
+ const char **endptr)
+{
+ int c;
+ bool starts_with_quote = FALSE;
+ bool escape = FALSE;
+
+ for(c = DIGEST_MAX_VALUE_LENGTH - 1; (*str && (*str != '=') && c--);)
+ *value++ = *str++;
+ *value = 0;
+
+ if('=' != *str++)
+ /* eek, no match */
+ return FALSE;
+
+ if('\"' == *str) {
+ /* This starts with a quote so it must end with one as well! */
+ str++;
+ starts_with_quote = TRUE;
+ }
+
+ for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) {
+ if(!escape) {
+ switch(*str) {
+ case '\\':
+ if(starts_with_quote) {
+ /* the start of an escaped quote */
+ escape = TRUE;
+ continue;
+ }
+ break;
+
+ case ',':
+ if(!starts_with_quote) {
+ /* This signals the end of the content if we didn't get a starting
+ quote and then we do "sloppy" parsing */
+ c = 0; /* the end */
+ continue;
+ }
+ break;
+
+ case '\r':
+ case '\n':
+ /* end of string */
+ if(starts_with_quote)
+ return FALSE; /* No closing quote */
+ c = 0;
+ continue;
+
+ case '\"':
+ if(starts_with_quote) {
+ /* end of string */
+ c = 0;
+ continue;
+ }
+ else
+ return FALSE;
+ break;
+ }
+ }
+
+ escape = FALSE;
+ *content++ = *str;
+ }
+ if(escape)
+ return FALSE; /* No character after backslash */
+
+ *content = 0;
+ *endptr = str;
+
+ return TRUE;
+}
+
+#if !defined(USE_WINDOWS_SSPI)
+/* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string */
+static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */
+ unsigned char *dest) /* 33 bytes */
+{
+ int i;
+ for(i = 0; i < 16; i++)
+ msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
+}
+
+/* Convert sha256 chunk to RFC7616 -suitable ascii string */
+static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */
+ unsigned char *dest) /* 65 bytes */
+{
+ int i;
+ for(i = 0; i < 32; i++)
+ msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
+}
+
+/* Perform quoted-string escaping as described in RFC2616 and its errata */
+static char *auth_digest_string_quoted(const char *source)
+{
+ char *dest;
+ const char *s = source;
+ size_t n = 1; /* null terminator */
+
+ /* Calculate size needed */
+ while(*s) {
+ ++n;
+ if(*s == '"' || *s == '\\') {
+ ++n;
+ }
+ ++s;
+ }
+
+ dest = malloc(n);
+ if(dest) {
+ char *d = dest;
+ s = source;
+ while(*s) {
+ if(*s == '"' || *s == '\\') {
+ *d++ = '\\';
+ }
+ *d++ = *s++;
+ }
+ *d = '\0';
+ }
+
+ return dest;
+}
+
+/* Retrieves the value for a corresponding key from the challenge string
+ * returns TRUE if the key could be found, FALSE if it does not exists
+ */
+static bool auth_digest_get_key_value(const char *chlg,
+ const char *key,
+ char *value,
+ size_t max_val_len,
+ char end_char)
+{
+ char *find_pos;
+ size_t i;
+
+ find_pos = strstr(chlg, key);
+ if(!find_pos)
+ return FALSE;
+
+ find_pos += strlen(key);
+
+ for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i)
+ value[i] = *find_pos++;
+ value[i] = '\0';
+
+ return TRUE;
+}
+
+static CURLcode auth_digest_get_qop_values(const char *options, int *value)
+{
+ char *tmp;
+ char *token;
+ char *tok_buf = NULL;
+
+ /* Initialise the output */
+ *value = 0;
+
+ /* Tokenise the list of qop values. Use a temporary clone of the buffer since
+ strtok_r() ruins it. */
+ tmp = strdup(options);
+ if(!tmp)
+ return CURLE_OUT_OF_MEMORY;
+
+ token = strtok_r(tmp, ",", &tok_buf);
+ while(token) {
+ if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH))
+ *value |= DIGEST_QOP_VALUE_AUTH;
+ else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT))
+ *value |= DIGEST_QOP_VALUE_AUTH_INT;
+ else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_CONF))
+ *value |= DIGEST_QOP_VALUE_AUTH_CONF;
+
+ token = strtok_r(NULL, ",", &tok_buf);
+ }
+
+ free(tmp);
+
+ return CURLE_OK;
+}
+
+/*
+ * auth_decode_digest_md5_message()
+ *
+ * This is used internally to decode an already encoded DIGEST-MD5 challenge
+ * message into the separate attributes.
+ *
+ * Parameters:
+ *
+ * chlgref [in] - The challenge message.
+ * nonce [in/out] - The buffer where the nonce will be stored.
+ * nlen [in] - The length of the nonce buffer.
+ * realm [in/out] - The buffer where the realm will be stored.
+ * rlen [in] - The length of the realm buffer.
+ * alg [in/out] - The buffer where the algorithm will be stored.
+ * alen [in] - The length of the algorithm buffer.
+ * qop [in/out] - The buffer where the qop-options will be stored.
+ * qlen [in] - The length of the qop buffer.
+ *
+ * Returns CURLE_OK on success.
+ */
+static CURLcode auth_decode_digest_md5_message(const struct bufref *chlgref,
+ char *nonce, size_t nlen,
+ char *realm, size_t rlen,
+ char *alg, size_t alen,
+ char *qop, size_t qlen)
+{
+ const char *chlg = (const char *) Curl_bufref_ptr(chlgref);
+
+ /* Ensure we have a valid challenge message */
+ if(!Curl_bufref_len(chlgref))
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ /* Retrieve nonce string from the challenge */
+ if(!auth_digest_get_key_value(chlg, "nonce=\"", nonce, nlen, '\"'))
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ /* Retrieve realm string from the challenge */
+ if(!auth_digest_get_key_value(chlg, "realm=\"", realm, rlen, '\"')) {
+ /* Challenge does not have a realm, set empty string [RFC2831] page 6 */
+ strcpy(realm, "");
+ }
+
+ /* Retrieve algorithm string from the challenge */
+ if(!auth_digest_get_key_value(chlg, "algorithm=", alg, alen, ','))
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ /* Retrieve qop-options string from the challenge */
+ if(!auth_digest_get_key_value(chlg, "qop=\"", qop, qlen, '\"'))
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_auth_is_digest_supported()
+ *
+ * This is used to evaluate if DIGEST is supported.
+ *
+ * Parameters: None
+ *
+ * Returns TRUE as DIGEST as handled by libcurl.
+ */
+bool Curl_auth_is_digest_supported(void)
+{
+ return TRUE;
+}
+
+/*
+ * Curl_auth_create_digest_md5_message()
+ *
+ * This is used to generate an already encoded DIGEST-MD5 response message
+ * ready for sending to the recipient.
+ *
+ * Parameters:
+ *
+ * data [in] - The session handle.
+ * chlg [in] - The challenge message.
+ * userp [in] - The user name.
+ * passwdp [in] - The user's password.
+ * service [in] - The service type such as http, smtp, pop or imap.
+ * out [out] - The result storage.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
+ const struct bufref *chlg,
+ const char *userp,
+ const char *passwdp,
+ const char *service,
+ struct bufref *out)
+{
+ size_t i;
+ struct MD5_context *ctxt;
+ char *response = NULL;
+ unsigned char digest[MD5_DIGEST_LEN];
+ char HA1_hex[2 * MD5_DIGEST_LEN + 1];
+ char HA2_hex[2 * MD5_DIGEST_LEN + 1];
+ char resp_hash_hex[2 * MD5_DIGEST_LEN + 1];
+ char nonce[64];
+ char realm[128];
+ char algorithm[64];
+ char qop_options[64];
+ int qop_values;
+ char cnonce[33];
+ char nonceCount[] = "00000001";
+ char method[] = "AUTHENTICATE";
+ char qop[] = DIGEST_QOP_VALUE_STRING_AUTH;
+ char *spn = NULL;
+
+ /* Decode the challenge message */
+ CURLcode result = auth_decode_digest_md5_message(chlg,
+ nonce, sizeof(nonce),
+ realm, sizeof(realm),
+ algorithm,
+ sizeof(algorithm),
+ qop_options,
+ sizeof(qop_options));
+ if(result)
+ return result;
+
+ /* We only support md5 sessions */
+ if(strcmp(algorithm, "md5-sess") != 0)
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ /* Get the qop-values from the qop-options */
+ result = auth_digest_get_qop_values(qop_options, &qop_values);
+ if(result)
+ return result;
+
+ /* We only support auth quality-of-protection */
+ if(!(qop_values & DIGEST_QOP_VALUE_AUTH))
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ /* Generate 32 random hex chars, 32 bytes + 1 null-termination */
+ result = Curl_rand_hex(data, (unsigned char *)cnonce, sizeof(cnonce));
+ if(result)
+ return result;
+
+ /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */
+ ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
+ if(!ctxt)
+ return CURLE_OUT_OF_MEMORY;
+
+ Curl_MD5_update(ctxt, (const unsigned char *) userp,
+ curlx_uztoui(strlen(userp)));
+ Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
+ Curl_MD5_update(ctxt, (const unsigned char *) realm,
+ curlx_uztoui(strlen(realm)));
+ Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
+ Curl_MD5_update(ctxt, (const unsigned char *) passwdp,
+ curlx_uztoui(strlen(passwdp)));
+ Curl_MD5_final(ctxt, digest);
+
+ ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
+ if(!ctxt)
+ return CURLE_OUT_OF_MEMORY;
+
+ Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN);
+ Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
+ Curl_MD5_update(ctxt, (const unsigned char *) nonce,
+ curlx_uztoui(strlen(nonce)));
+ Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
+ Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
+ curlx_uztoui(strlen(cnonce)));
+ Curl_MD5_final(ctxt, digest);
+
+ /* Convert calculated 16 octet hex into 32 bytes string */
+ for(i = 0; i < MD5_DIGEST_LEN; i++)
+ msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
+
+ /* Generate our SPN */
+ spn = Curl_auth_build_spn(service, realm, NULL);
+ if(!spn)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Calculate H(A2) */
+ ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
+ if(!ctxt) {
+ free(spn);
+
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ Curl_MD5_update(ctxt, (const unsigned char *) method,
+ curlx_uztoui(strlen(method)));
+ Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
+ Curl_MD5_update(ctxt, (const unsigned char *) spn,
+ curlx_uztoui(strlen(spn)));
+ Curl_MD5_final(ctxt, digest);
+
+ for(i = 0; i < MD5_DIGEST_LEN; i++)
+ msnprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]);
+
+ /* Now calculate the response hash */
+ ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
+ if(!ctxt) {
+ free(spn);
+
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN);
+ Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
+ Curl_MD5_update(ctxt, (const unsigned char *) nonce,
+ curlx_uztoui(strlen(nonce)));
+ Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
+
+ Curl_MD5_update(ctxt, (const unsigned char *) nonceCount,
+ curlx_uztoui(strlen(nonceCount)));
+ Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
+ Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
+ curlx_uztoui(strlen(cnonce)));
+ Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
+ Curl_MD5_update(ctxt, (const unsigned char *) qop,
+ curlx_uztoui(strlen(qop)));
+ Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
+
+ Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN);
+ Curl_MD5_final(ctxt, digest);
+
+ for(i = 0; i < MD5_DIGEST_LEN; i++)
+ msnprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
+
+ /* Generate the response */
+ response = aprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\","
+ "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s,"
+ "qop=%s",
+ userp, realm, nonce,
+ cnonce, nonceCount, spn, resp_hash_hex, qop);
+ free(spn);
+ if(!response)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Return the response. */
+ Curl_bufref_set(out, response, strlen(response), curl_free);
+ return result;
+}
+
+/*
+ * Curl_auth_decode_digest_http_message()
+ *
+ * This is used to decode an HTTP DIGEST challenge message into the separate
+ * attributes.
+ *
+ * Parameters:
+ *
+ * chlg [in] - The challenge message.
+ * digest [in/out] - The digest data struct being used and modified.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
+ struct digestdata *digest)
+{
+ bool before = FALSE; /* got a nonce before */
+ bool foundAuth = FALSE;
+ bool foundAuthInt = FALSE;
+ char *token = NULL;
+ char *tmp = NULL;
+
+ /* If we already have received a nonce, keep that in mind */
+ if(digest->nonce)
+ before = TRUE;
+
+ /* Clean up any former leftovers and initialise to defaults */
+ Curl_auth_digest_cleanup(digest);
+
+ for(;;) {
+ char value[DIGEST_MAX_VALUE_LENGTH];
+ char content[DIGEST_MAX_CONTENT_LENGTH];
+
+ /* Pass all additional spaces here */
+ while(*chlg && ISBLANK(*chlg))
+ chlg++;
+
+ /* Extract a value=content pair */
+ if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) {
+ if(strcasecompare(value, "nonce")) {
+ free(digest->nonce);
+ digest->nonce = strdup(content);
+ if(!digest->nonce)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else if(strcasecompare(value, "stale")) {
+ if(strcasecompare(content, "true")) {
+ digest->stale = TRUE;
+ digest->nc = 1; /* we make a new nonce now */
+ }
+ }
+ else if(strcasecompare(value, "realm")) {
+ free(digest->realm);
+ digest->realm = strdup(content);
+ if(!digest->realm)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else if(strcasecompare(value, "opaque")) {
+ free(digest->opaque);
+ digest->opaque = strdup(content);
+ if(!digest->opaque)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else if(strcasecompare(value, "qop")) {
+ char *tok_buf = NULL;
+ /* Tokenize the list and choose auth if possible, use a temporary
+ clone of the buffer since strtok_r() ruins it */
+ tmp = strdup(content);
+ if(!tmp)
+ return CURLE_OUT_OF_MEMORY;
+
+ token = strtok_r(tmp, ",", &tok_buf);
+ while(token) {
+ /* Pass additional spaces here */
+ while(*token && ISBLANK(*token))
+ token++;
+ if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) {
+ foundAuth = TRUE;
+ }
+ else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) {
+ foundAuthInt = TRUE;
+ }
+ token = strtok_r(NULL, ",", &tok_buf);
+ }
+
+ free(tmp);
+
+ /* Select only auth or auth-int. Otherwise, ignore */
+ if(foundAuth) {
+ free(digest->qop);
+ digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH);
+ if(!digest->qop)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else if(foundAuthInt) {
+ free(digest->qop);
+ digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH_INT);
+ if(!digest->qop)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ else if(strcasecompare(value, "algorithm")) {
+ free(digest->algorithm);
+ digest->algorithm = strdup(content);
+ if(!digest->algorithm)
+ return CURLE_OUT_OF_MEMORY;
+
+ if(strcasecompare(content, "MD5-sess"))
+ digest->algo = ALGO_MD5SESS;
+ else if(strcasecompare(content, "MD5"))
+ digest->algo = ALGO_MD5;
+ else if(strcasecompare(content, "SHA-256"))
+ digest->algo = ALGO_SHA256;
+ else if(strcasecompare(content, "SHA-256-SESS"))
+ digest->algo = ALGO_SHA256SESS;
+ else if(strcasecompare(content, "SHA-512-256"))
+ digest->algo = ALGO_SHA512_256;
+ else if(strcasecompare(content, "SHA-512-256-SESS"))
+ digest->algo = ALGO_SHA512_256SESS;
+ else
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+ else if(strcasecompare(value, "userhash")) {
+ if(strcasecompare(content, "true")) {
+ digest->userhash = TRUE;
+ }
+ }
+ else {
+ /* Unknown specifier, ignore it! */
+ }
+ }
+ else
+ break; /* We're done here */
+
+ /* Pass all additional spaces here */
+ while(*chlg && ISBLANK(*chlg))
+ chlg++;
+
+ /* Allow the list to be comma-separated */
+ if(',' == *chlg)
+ chlg++;
+ }
+
+ /* We had a nonce since before, and we got another one now without
+ 'stale=true'. This means we provided bad credentials in the previous
+ request */
+ if(before && !digest->stale)
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ /* We got this header without a nonce, that's a bad Digest line! */
+ if(!digest->nonce)
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ /* "<algo>-sess" protocol versions require "auth" or "auth-int" qop */
+ if(!digest->qop && (digest->algo & SESSION_ALGO))
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ return CURLE_OK;
+}
+
+/*
+ * auth_create_digest_http_message()
+ *
+ * This is used to generate an HTTP DIGEST response message ready for sending
+ * to the recipient.
+ *
+ * Parameters:
+ *
+ * data [in] - The session handle.
+ * userp [in] - The user name.
+ * passwdp [in] - The user's password.
+ * request [in] - The HTTP request.
+ * uripath [in] - The path of the HTTP uri.
+ * digest [in/out] - The digest data struct being used and modified.
+ * outptr [in/out] - The address where a pointer to newly allocated memory
+ * holding the result will be stored upon completion.
+ * outlen [out] - The length of the output message.
+ *
+ * Returns CURLE_OK on success.
+ */
+static CURLcode auth_create_digest_http_message(
+ struct Curl_easy *data,
+ const char *userp,
+ const char *passwdp,
+ const unsigned char *request,
+ const unsigned char *uripath,
+ struct digestdata *digest,
+ char **outptr, size_t *outlen,
+ void (*convert_to_ascii)(unsigned char *, unsigned char *),
+ CURLcode (*hash)(unsigned char *, const unsigned char *,
+ const size_t))
+{
+ CURLcode result;
+ unsigned char hashbuf[32]; /* 32 bytes/256 bits */
+ unsigned char request_digest[65];
+ unsigned char ha1[65]; /* 64 digits and 1 zero byte */
+ unsigned char ha2[65]; /* 64 digits and 1 zero byte */
+ char userh[65];
+ char *cnonce = NULL;
+ size_t cnonce_sz = 0;
+ char *userp_quoted;
+ char *realm_quoted;
+ char *nonce_quoted;
+ char *response = NULL;
+ char *hashthis = NULL;
+ char *tmp = NULL;
+
+ if(!digest->nc)
+ digest->nc = 1;
+
+ if(!digest->cnonce) {
+ char cnoncebuf[33];
+ result = Curl_rand_hex(data, (unsigned char *)cnoncebuf,
+ sizeof(cnoncebuf));
+ if(result)
+ return result;
+
+ result = Curl_base64_encode(cnoncebuf, strlen(cnoncebuf),
+ &cnonce, &cnonce_sz);
+ if(result)
+ return result;
+
+ digest->cnonce = cnonce;
+ }
+
+ if(digest->userhash) {
+ hashthis = aprintf("%s:%s", userp, digest->realm ? digest->realm : "");
+ if(!hashthis)
+ return CURLE_OUT_OF_MEMORY;
+
+ hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
+ free(hashthis);
+ convert_to_ascii(hashbuf, (unsigned char *)userh);
+ }
+
+ /*
+ If the algorithm is "MD5" or unspecified (which then defaults to MD5):
+
+ A1 = unq(username-value) ":" unq(realm-value) ":" passwd
+
+ If the algorithm is "MD5-sess" then:
+
+ A1 = H(unq(username-value) ":" unq(realm-value) ":" passwd) ":"
+ unq(nonce-value) ":" unq(cnonce-value)
+ */
+
+ hashthis = aprintf("%s:%s:%s", userp, digest->realm ? digest->realm : "",
+ passwdp);
+ if(!hashthis)
+ return CURLE_OUT_OF_MEMORY;
+
+ hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
+ free(hashthis);
+ convert_to_ascii(hashbuf, ha1);
+
+ if(digest->algo & SESSION_ALGO) {
+ /* nonce and cnonce are OUTSIDE the hash */
+ tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce);
+ if(!tmp)
+ return CURLE_OUT_OF_MEMORY;
+
+ hash(hashbuf, (unsigned char *) tmp, strlen(tmp));
+ free(tmp);
+ convert_to_ascii(hashbuf, ha1);
+ }
+
+ /*
+ If the "qop" directive's value is "auth" or is unspecified, then A2 is:
+
+ A2 = Method ":" digest-uri-value
+
+ If the "qop" value is "auth-int", then A2 is:
+
+ A2 = Method ":" digest-uri-value ":" H(entity-body)
+
+ (The "Method" value is the HTTP request method as specified in section
+ 5.1.1 of RFC 2616)
+ */
+
+ hashthis = aprintf("%s:%s", request, uripath);
+ if(!hashthis)
+ return CURLE_OUT_OF_MEMORY;
+
+ if(digest->qop && strcasecompare(digest->qop, "auth-int")) {
+ /* We don't support auth-int for PUT or POST */
+ char hashed[65];
+ char *hashthis2;
+
+ hash(hashbuf, (const unsigned char *)"", 0);
+ convert_to_ascii(hashbuf, (unsigned char *)hashed);
+
+ hashthis2 = aprintf("%s:%s", hashthis, hashed);
+ free(hashthis);
+ hashthis = hashthis2;
+ }
+
+ if(!hashthis)
+ return CURLE_OUT_OF_MEMORY;
+
+ hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
+ free(hashthis);
+ convert_to_ascii(hashbuf, ha2);
+
+ if(digest->qop) {
+ hashthis = aprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce, digest->nc,
+ digest->cnonce, digest->qop, ha2);
+ }
+ else {
+ hashthis = aprintf("%s:%s:%s", ha1, digest->nonce, ha2);
+ }
+
+ if(!hashthis)
+ return CURLE_OUT_OF_MEMORY;
+
+ hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
+ free(hashthis);
+ convert_to_ascii(hashbuf, request_digest);
+
+ /* For test case 64 (snooped from a Mozilla 1.3a request)
+
+ Authorization: Digest username="testuser", realm="testrealm", \
+ nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
+
+ Digest parameters are all quoted strings. Username which is provided by
+ the user will need double quotes and backslashes within it escaped.
+ realm, nonce, and opaque will need backslashes as well as they were
+ de-escaped when copied from request header. cnonce is generated with
+ web-safe characters. uri is already percent encoded. nc is 8 hex
+ characters. algorithm and qop with standard values only contain web-safe
+ characters.
+ */
+ userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp);
+ if(!userp_quoted)
+ return CURLE_OUT_OF_MEMORY;
+ if(digest->realm)
+ realm_quoted = auth_digest_string_quoted(digest->realm);
+ else {
+ realm_quoted = malloc(1);
+ if(realm_quoted)
+ realm_quoted[0] = 0;
+ }
+ if(!realm_quoted) {
+ free(userp_quoted);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ nonce_quoted = auth_digest_string_quoted(digest->nonce);
+ if(!nonce_quoted) {
+ free(realm_quoted);
+ free(userp_quoted);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(digest->qop) {
+ response = aprintf("username=\"%s\", "
+ "realm=\"%s\", "
+ "nonce=\"%s\", "
+ "uri=\"%s\", "
+ "cnonce=\"%s\", "
+ "nc=%08x, "
+ "qop=%s, "
+ "response=\"%s\"",
+ userp_quoted,
+ realm_quoted,
+ nonce_quoted,
+ uripath,
+ digest->cnonce,
+ digest->nc,
+ digest->qop,
+ request_digest);
+
+ /* Increment nonce-count to use another nc value for the next request */
+ digest->nc++;
+ }
+ else {
+ response = aprintf("username=\"%s\", "
+ "realm=\"%s\", "
+ "nonce=\"%s\", "
+ "uri=\"%s\", "
+ "response=\"%s\"",
+ userp_quoted,
+ realm_quoted,
+ nonce_quoted,
+ uripath,
+ request_digest);
+ }
+ free(nonce_quoted);
+ free(realm_quoted);
+ free(userp_quoted);
+ if(!response)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Add the optional fields */
+ if(digest->opaque) {
+ char *opaque_quoted;
+ /* Append the opaque */
+ opaque_quoted = auth_digest_string_quoted(digest->opaque);
+ if(!opaque_quoted) {
+ free(response);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ tmp = aprintf("%s, opaque=\"%s\"", response, opaque_quoted);
+ free(response);
+ free(opaque_quoted);
+ if(!tmp)
+ return CURLE_OUT_OF_MEMORY;
+
+ response = tmp;
+ }
+
+ if(digest->algorithm) {
+ /* Append the algorithm */
+ tmp = aprintf("%s, algorithm=%s", response, digest->algorithm);
+ free(response);
+ if(!tmp)
+ return CURLE_OUT_OF_MEMORY;
+
+ response = tmp;
+ }
+
+ if(digest->userhash) {
+ /* Append the userhash */
+ tmp = aprintf("%s, userhash=true", response);
+ free(response);
+ if(!tmp)
+ return CURLE_OUT_OF_MEMORY;
+
+ response = tmp;
+ }
+
+ /* Return the output */
+ *outptr = response;
+ *outlen = strlen(response);
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_auth_create_digest_http_message()
+ *
+ * This is used to generate an HTTP DIGEST response message ready for sending
+ * to the recipient.
+ *
+ * Parameters:
+ *
+ * data [in] - The session handle.
+ * userp [in] - The user name.
+ * passwdp [in] - The user's password.
+ * request [in] - The HTTP request.
+ * uripath [in] - The path of the HTTP uri.
+ * digest [in/out] - The digest data struct being used and modified.
+ * outptr [in/out] - The address where a pointer to newly allocated memory
+ * holding the result will be stored upon completion.
+ * outlen [out] - The length of the output message.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
+ const char *userp,
+ const char *passwdp,
+ const unsigned char *request,
+ const unsigned char *uripath,
+ struct digestdata *digest,
+ char **outptr, size_t *outlen)
+{
+ if(digest->algo <= ALGO_MD5SESS)
+ return auth_create_digest_http_message(data, userp, passwdp,
+ request, uripath, digest,
+ outptr, outlen,
+ auth_digest_md5_to_ascii,
+ Curl_md5it);
+ DEBUGASSERT(digest->algo <= ALGO_SHA512_256SESS);
+ return auth_create_digest_http_message(data, userp, passwdp,
+ request, uripath, digest,
+ outptr, outlen,
+ auth_digest_sha256_to_ascii,
+ Curl_sha256it);
+}
+
+/*
+ * Curl_auth_digest_cleanup()
+ *
+ * This is used to clean up the digest specific data.
+ *
+ * Parameters:
+ *
+ * digest [in/out] - The digest data struct being cleaned up.
+ *
+ */
+void Curl_auth_digest_cleanup(struct digestdata *digest)
+{
+ Curl_safefree(digest->nonce);
+ Curl_safefree(digest->cnonce);
+ Curl_safefree(digest->realm);
+ Curl_safefree(digest->opaque);
+ Curl_safefree(digest->qop);
+ Curl_safefree(digest->algorithm);
+
+ digest->nc = 0;
+ digest->algo = ALGO_MD5; /* default algorithm */
+ digest->stale = FALSE; /* default means normal, not stale */
+ digest->userhash = FALSE;
+}
+#endif /* !USE_WINDOWS_SSPI */
+
+#endif /* CURL_DISABLE_CRYPTO_AUTH */
diff --git a/libs/libcurl/src/vauth/digest.h b/libs/libcurl/src/vauth/digest.h index d785bdd91b..e994bb924c 100644 --- a/libs/libcurl/src/vauth/digest.h +++ b/libs/libcurl/src/vauth/digest.h @@ -1,40 +1,40 @@ -#ifndef HEADER_CURL_DIGEST_H -#define HEADER_CURL_DIGEST_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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> - -#if !defined(CURL_DISABLE_CRYPTO_AUTH) - -#define DIGEST_MAX_VALUE_LENGTH 256 -#define DIGEST_MAX_CONTENT_LENGTH 1024 - -/* This is used to extract the realm from a challenge message */ -bool Curl_auth_digest_get_pair(const char *str, char *value, char *content, - const char **endptr); - -#endif - -#endif /* HEADER_CURL_DIGEST_H */ +#ifndef HEADER_CURL_DIGEST_H
+#define HEADER_CURL_DIGEST_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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>
+
+#if !defined(CURL_DISABLE_CRYPTO_AUTH)
+
+#define DIGEST_MAX_VALUE_LENGTH 256
+#define DIGEST_MAX_CONTENT_LENGTH 1024
+
+/* This is used to extract the realm from a challenge message */
+bool Curl_auth_digest_get_pair(const char *str, char *value, char *content,
+ const char **endptr);
+
+#endif
+
+#endif /* HEADER_CURL_DIGEST_H */
diff --git a/libs/libcurl/src/vauth/digest_sspi.c b/libs/libcurl/src/vauth/digest_sspi.c index 89a9db52c7..0a071fe1e5 100644 --- a/libs/libcurl/src/vauth/digest_sspi.c +++ b/libs/libcurl/src/vauth/digest_sspi.c @@ -1,668 +1,668 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2014 - 2016, Steve Holme, <steve_holme@hotmail.com>. - * Copyright (C) 2015 - 2022, 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 - * - * RFC2831 DIGEST-MD5 authentication - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_CRYPTO_AUTH) - -#include <curl/curl.h> - -#include "vauth/vauth.h" -#include "vauth/digest.h" -#include "urldata.h" -#include "warnless.h" -#include "curl_multibyte.h" -#include "sendf.h" -#include "strdup.h" -#include "strcase.h" -#include "strerror.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* -* Curl_auth_is_digest_supported() -* -* This is used to evaluate if DIGEST is supported. -* -* Parameters: None -* -* Returns TRUE if DIGEST is supported by Windows SSPI. -*/ -bool Curl_auth_is_digest_supported(void) -{ - PSecPkgInfo SecurityPackage; - SECURITY_STATUS status; - - /* Query the security package for Digest */ - status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST), - &SecurityPackage); - - /* Release the package buffer as it is not required anymore */ - if(status == SEC_E_OK) { - s_pSecFn->FreeContextBuffer(SecurityPackage); - } - - return (status == SEC_E_OK ? TRUE : FALSE); -} - -/* - * Curl_auth_create_digest_md5_message() - * - * This is used to generate an already encoded DIGEST-MD5 response message - * ready for sending to the recipient. - * - * Parameters: - * - * data [in] - The session handle. - * chlg [in] - The challenge message. - * userp [in] - The user name in the format User or Domain\User. - * passwdp [in] - The user's password. - * service [in] - The service type such as http, smtp, pop or imap. - * out [out] - The result storage. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, - const struct bufref *chlg, - const char *userp, - const char *passwdp, - const char *service, - struct bufref *out) -{ - CURLcode result = CURLE_OK; - TCHAR *spn = NULL; - size_t token_max = 0; - unsigned char *output_token = NULL; - CredHandle credentials; - CtxtHandle context; - PSecPkgInfo SecurityPackage; - SEC_WINNT_AUTH_IDENTITY identity; - SEC_WINNT_AUTH_IDENTITY *p_identity; - SecBuffer chlg_buf; - SecBuffer resp_buf; - SecBufferDesc chlg_desc; - SecBufferDesc resp_desc; - SECURITY_STATUS status; - unsigned long attrs; - TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ - - /* Ensure we have a valid challenge message */ - if(!Curl_bufref_len(chlg)) { - infof(data, "DIGEST-MD5 handshake failure (empty challenge message)"); - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Query the security package for DigestSSP */ - status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST), - &SecurityPackage); - if(status != SEC_E_OK) { - failf(data, "SSPI: couldn't get auth info"); - return CURLE_AUTH_ERROR; - } - - token_max = SecurityPackage->cbMaxToken; - - /* Release the package buffer as it is not required anymore */ - s_pSecFn->FreeContextBuffer(SecurityPackage); - - /* Allocate our response buffer */ - output_token = malloc(token_max); - if(!output_token) - return CURLE_OUT_OF_MEMORY; - - /* Generate our SPN */ - spn = Curl_auth_build_spn(service, data->conn->host.name, NULL); - if(!spn) { - free(output_token); - return CURLE_OUT_OF_MEMORY; - } - - if(userp && *userp) { - /* Populate our identity structure */ - result = Curl_create_sspi_identity(userp, passwdp, &identity); - if(result) { - free(spn); - free(output_token); - return result; - } - - /* Allow proper cleanup of the identity structure */ - p_identity = &identity; - } - else - /* Use the current Windows user */ - p_identity = NULL; - - /* Acquire our credentials handle */ - status = s_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *) TEXT(SP_NAME_DIGEST), - SECPKG_CRED_OUTBOUND, NULL, - p_identity, NULL, NULL, - &credentials, &expiry); - - if(status != SEC_E_OK) { - Curl_sspi_free_identity(p_identity); - free(spn); - free(output_token); - return CURLE_LOGIN_DENIED; - } - - /* Setup the challenge "input" security buffer */ - chlg_desc.ulVersion = SECBUFFER_VERSION; - chlg_desc.cBuffers = 1; - chlg_desc.pBuffers = &chlg_buf; - chlg_buf.BufferType = SECBUFFER_TOKEN; - chlg_buf.pvBuffer = (void *) Curl_bufref_ptr(chlg); - chlg_buf.cbBuffer = curlx_uztoul(Curl_bufref_len(chlg)); - - /* Setup the response "output" security buffer */ - resp_desc.ulVersion = SECBUFFER_VERSION; - resp_desc.cBuffers = 1; - resp_desc.pBuffers = &resp_buf; - resp_buf.BufferType = SECBUFFER_TOKEN; - resp_buf.pvBuffer = output_token; - resp_buf.cbBuffer = curlx_uztoul(token_max); - - /* Generate our response message */ - status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, spn, - 0, 0, 0, &chlg_desc, 0, - &context, &resp_desc, &attrs, - &expiry); - - if(status == SEC_I_COMPLETE_NEEDED || - status == SEC_I_COMPLETE_AND_CONTINUE) - s_pSecFn->CompleteAuthToken(&credentials, &resp_desc); - else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) - char buffer[STRERROR_LEN]; -#endif - - s_pSecFn->FreeCredentialsHandle(&credentials); - Curl_sspi_free_identity(p_identity); - free(spn); - free(output_token); - - if(status == SEC_E_INSUFFICIENT_MEMORY) - return CURLE_OUT_OF_MEMORY; - - infof(data, "schannel: InitializeSecurityContext failed: %s", - Curl_sspi_strerror(status, buffer, sizeof(buffer))); - - return CURLE_AUTH_ERROR; - } - - /* Return the response. */ - Curl_bufref_set(out, output_token, resp_buf.cbBuffer, curl_free); - - /* Free our handles */ - s_pSecFn->DeleteSecurityContext(&context); - s_pSecFn->FreeCredentialsHandle(&credentials); - - /* Free the identity structure */ - Curl_sspi_free_identity(p_identity); - - /* Free the SPN */ - free(spn); - - return result; -} - -/* - * Curl_override_sspi_http_realm() - * - * This is used to populate the domain in a SSPI identity structure - * The realm is extracted from the challenge message and used as the - * domain if it is not already explicitly set. - * - * Parameters: - * - * chlg [in] - The challenge message. - * identity [in/out] - The identity structure. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_override_sspi_http_realm(const char *chlg, - SEC_WINNT_AUTH_IDENTITY *identity) -{ - xcharp_u domain, dup_domain; - - /* If domain is blank or unset, check challenge message for realm */ - if(!identity->Domain || !identity->DomainLength) { - for(;;) { - char value[DIGEST_MAX_VALUE_LENGTH]; - char content[DIGEST_MAX_CONTENT_LENGTH]; - - /* Pass all additional spaces here */ - while(*chlg && ISBLANK(*chlg)) - chlg++; - - /* Extract a value=content pair */ - if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) { - if(strcasecompare(value, "realm")) { - - /* Setup identity's domain and length */ - domain.tchar_ptr = curlx_convert_UTF8_to_tchar((char *) content); - if(!domain.tchar_ptr) - return CURLE_OUT_OF_MEMORY; - - dup_domain.tchar_ptr = _tcsdup(domain.tchar_ptr); - if(!dup_domain.tchar_ptr) { - curlx_unicodefree(domain.tchar_ptr); - return CURLE_OUT_OF_MEMORY; - } - - free(identity->Domain); - identity->Domain = dup_domain.tbyte_ptr; - identity->DomainLength = curlx_uztoul(_tcslen(dup_domain.tchar_ptr)); - dup_domain.tchar_ptr = NULL; - - curlx_unicodefree(domain.tchar_ptr); - } - else { - /* Unknown specifier, ignore it! */ - } - } - else - break; /* We're done here */ - - /* Pass all additional spaces here */ - while(*chlg && ISBLANK(*chlg)) - chlg++; - - /* Allow the list to be comma-separated */ - if(',' == *chlg) - chlg++; - } - } - - return CURLE_OK; -} - -/* - * Curl_auth_decode_digest_http_message() - * - * This is used to decode a HTTP DIGEST challenge message into the separate - * attributes. - * - * Parameters: - * - * chlg [in] - The challenge message. - * digest [in/out] - The digest data struct being used and modified. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_decode_digest_http_message(const char *chlg, - struct digestdata *digest) -{ - size_t chlglen = strlen(chlg); - - /* We had an input token before so if there's another one now that means we - provided bad credentials in the previous request or it's stale. */ - if(digest->input_token) { - bool stale = false; - const char *p = chlg; - - /* Check for the 'stale' directive */ - for(;;) { - char value[DIGEST_MAX_VALUE_LENGTH]; - char content[DIGEST_MAX_CONTENT_LENGTH]; - - while(*p && ISBLANK(*p)) - p++; - - if(!Curl_auth_digest_get_pair(p, value, content, &p)) - break; - - if(strcasecompare(value, "stale") && - strcasecompare(content, "true")) { - stale = true; - break; - } - - while(*p && ISBLANK(*p)) - p++; - - if(',' == *p) - p++; - } - - if(stale) - Curl_auth_digest_cleanup(digest); - else - return CURLE_LOGIN_DENIED; - } - - /* Store the challenge for use later */ - digest->input_token = (BYTE *) Curl_memdup(chlg, chlglen + 1); - if(!digest->input_token) - return CURLE_OUT_OF_MEMORY; - - digest->input_token_len = chlglen; - - return CURLE_OK; -} - -/* - * Curl_auth_create_digest_http_message() - * - * This is used to generate a HTTP DIGEST response message ready for sending - * to the recipient. - * - * Parameters: - * - * data [in] - The session handle. - * userp [in] - The user name in the format User or Domain\User. - * passwdp [in] - The user's password. - * request [in] - The HTTP request. - * uripath [in] - The path of the HTTP uri. - * digest [in/out] - The digest data struct being used and modified. - * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. - * outlen [out] - The length of the output message. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const unsigned char *request, - const unsigned char *uripath, - struct digestdata *digest, - char **outptr, size_t *outlen) -{ - size_t token_max; - char *resp; - BYTE *output_token; - size_t output_token_len = 0; - PSecPkgInfo SecurityPackage; - SecBuffer chlg_buf[5]; - SecBufferDesc chlg_desc; - SECURITY_STATUS status; - - (void) data; - - /* Query the security package for DigestSSP */ - status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST), - &SecurityPackage); - if(status != SEC_E_OK) { - failf(data, "SSPI: couldn't get auth info"); - return CURLE_AUTH_ERROR; - } - - token_max = SecurityPackage->cbMaxToken; - - /* Release the package buffer as it is not required anymore */ - s_pSecFn->FreeContextBuffer(SecurityPackage); - - /* Allocate the output buffer according to the max token size as indicated - by the security package */ - output_token = malloc(token_max); - if(!output_token) { - return CURLE_OUT_OF_MEMORY; - } - - /* If the user/passwd that was used to make the identity for http_context - has changed then delete that context. */ - if((userp && !digest->user) || (!userp && digest->user) || - (passwdp && !digest->passwd) || (!passwdp && digest->passwd) || - (userp && digest->user && Curl_timestrcmp(userp, digest->user)) || - (passwdp && digest->passwd && Curl_timestrcmp(passwdp, digest->passwd))) { - if(digest->http_context) { - s_pSecFn->DeleteSecurityContext(digest->http_context); - Curl_safefree(digest->http_context); - } - Curl_safefree(digest->user); - Curl_safefree(digest->passwd); - } - - if(digest->http_context) { - chlg_desc.ulVersion = SECBUFFER_VERSION; - chlg_desc.cBuffers = 5; - chlg_desc.pBuffers = chlg_buf; - chlg_buf[0].BufferType = SECBUFFER_TOKEN; - chlg_buf[0].pvBuffer = NULL; - chlg_buf[0].cbBuffer = 0; - chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS; - chlg_buf[1].pvBuffer = (void *) request; - chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request)); - chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS; - chlg_buf[2].pvBuffer = (void *) uripath; - chlg_buf[2].cbBuffer = curlx_uztoul(strlen((const char *) uripath)); - chlg_buf[3].BufferType = SECBUFFER_PKG_PARAMS; - chlg_buf[3].pvBuffer = NULL; - chlg_buf[3].cbBuffer = 0; - chlg_buf[4].BufferType = SECBUFFER_PADDING; - chlg_buf[4].pvBuffer = output_token; - chlg_buf[4].cbBuffer = curlx_uztoul(token_max); - - status = s_pSecFn->MakeSignature(digest->http_context, 0, &chlg_desc, 0); - if(status == SEC_E_OK) - output_token_len = chlg_buf[4].cbBuffer; - else { /* delete the context so a new one can be made */ - infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx", - (long)status); - s_pSecFn->DeleteSecurityContext(digest->http_context); - Curl_safefree(digest->http_context); - } - } - - if(!digest->http_context) { - CredHandle credentials; - SEC_WINNT_AUTH_IDENTITY identity; - SEC_WINNT_AUTH_IDENTITY *p_identity; - SecBuffer resp_buf; - SecBufferDesc resp_desc; - unsigned long attrs; - TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ - TCHAR *spn; - - /* free the copy of user/passwd used to make the previous identity */ - Curl_safefree(digest->user); - Curl_safefree(digest->passwd); - - if(userp && *userp) { - /* Populate our identity structure */ - if(Curl_create_sspi_identity(userp, passwdp, &identity)) { - free(output_token); - return CURLE_OUT_OF_MEMORY; - } - - /* Populate our identity domain */ - if(Curl_override_sspi_http_realm((const char *) digest->input_token, - &identity)) { - free(output_token); - return CURLE_OUT_OF_MEMORY; - } - - /* Allow proper cleanup of the identity structure */ - p_identity = &identity; - } - else - /* Use the current Windows user */ - p_identity = NULL; - - if(userp) { - digest->user = strdup(userp); - - if(!digest->user) { - free(output_token); - return CURLE_OUT_OF_MEMORY; - } - } - - if(passwdp) { - digest->passwd = strdup(passwdp); - - if(!digest->passwd) { - free(output_token); - Curl_safefree(digest->user); - return CURLE_OUT_OF_MEMORY; - } - } - - /* Acquire our credentials handle */ - status = s_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *) TEXT(SP_NAME_DIGEST), - SECPKG_CRED_OUTBOUND, NULL, - p_identity, NULL, NULL, - &credentials, &expiry); - if(status != SEC_E_OK) { - Curl_sspi_free_identity(p_identity); - free(output_token); - - return CURLE_LOGIN_DENIED; - } - - /* Setup the challenge "input" security buffer if present */ - chlg_desc.ulVersion = SECBUFFER_VERSION; - chlg_desc.cBuffers = 3; - chlg_desc.pBuffers = chlg_buf; - chlg_buf[0].BufferType = SECBUFFER_TOKEN; - chlg_buf[0].pvBuffer = digest->input_token; - chlg_buf[0].cbBuffer = curlx_uztoul(digest->input_token_len); - chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS; - chlg_buf[1].pvBuffer = (void *) request; - chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request)); - chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS; - chlg_buf[2].pvBuffer = NULL; - chlg_buf[2].cbBuffer = 0; - - /* Setup the response "output" security buffer */ - resp_desc.ulVersion = SECBUFFER_VERSION; - resp_desc.cBuffers = 1; - resp_desc.pBuffers = &resp_buf; - resp_buf.BufferType = SECBUFFER_TOKEN; - resp_buf.pvBuffer = output_token; - resp_buf.cbBuffer = curlx_uztoul(token_max); - - spn = curlx_convert_UTF8_to_tchar((char *) uripath); - if(!spn) { - s_pSecFn->FreeCredentialsHandle(&credentials); - - Curl_sspi_free_identity(p_identity); - free(output_token); - - return CURLE_OUT_OF_MEMORY; - } - - /* Allocate our new context handle */ - digest->http_context = calloc(1, sizeof(CtxtHandle)); - if(!digest->http_context) - return CURLE_OUT_OF_MEMORY; - - /* Generate our response message */ - status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, - spn, - ISC_REQ_USE_HTTP_STYLE, 0, 0, - &chlg_desc, 0, - digest->http_context, - &resp_desc, &attrs, &expiry); - curlx_unicodefree(spn); - - if(status == SEC_I_COMPLETE_NEEDED || - status == SEC_I_COMPLETE_AND_CONTINUE) - s_pSecFn->CompleteAuthToken(&credentials, &resp_desc); - else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { -#if !defined(CURL_DISABLE_VERBOSE_STRINGS) - char buffer[STRERROR_LEN]; -#endif - - s_pSecFn->FreeCredentialsHandle(&credentials); - - Curl_sspi_free_identity(p_identity); - free(output_token); - - Curl_safefree(digest->http_context); - - if(status == SEC_E_INSUFFICIENT_MEMORY) - return CURLE_OUT_OF_MEMORY; - - infof(data, "schannel: InitializeSecurityContext failed: %s", - Curl_sspi_strerror(status, buffer, sizeof(buffer))); - - return CURLE_AUTH_ERROR; - } - - output_token_len = resp_buf.cbBuffer; - - s_pSecFn->FreeCredentialsHandle(&credentials); - Curl_sspi_free_identity(p_identity); - } - - resp = malloc(output_token_len + 1); - if(!resp) { - free(output_token); - - return CURLE_OUT_OF_MEMORY; - } - - /* Copy the generated response */ - memcpy(resp, output_token, output_token_len); - resp[output_token_len] = 0; - - /* Return the response */ - *outptr = resp; - *outlen = output_token_len; - - /* Free the response buffer */ - free(output_token); - - return CURLE_OK; -} - -/* - * Curl_auth_digest_cleanup() - * - * This is used to clean up the digest specific data. - * - * Parameters: - * - * digest [in/out] - The digest data struct being cleaned up. - * - */ -void Curl_auth_digest_cleanup(struct digestdata *digest) -{ - /* Free the input token */ - Curl_safefree(digest->input_token); - - /* Reset any variables */ - digest->input_token_len = 0; - - /* Delete security context */ - if(digest->http_context) { - s_pSecFn->DeleteSecurityContext(digest->http_context); - Curl_safefree(digest->http_context); - } - - /* Free the copy of user/passwd used to make the identity for http_context */ - Curl_safefree(digest->user); - Curl_safefree(digest->passwd); -} - -#endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_CRYPTO_AUTH */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2014 - 2016, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) 2015 - 2022, 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
+ *
+ * RFC2831 DIGEST-MD5 authentication
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+
+#include <curl/curl.h>
+
+#include "vauth/vauth.h"
+#include "vauth/digest.h"
+#include "urldata.h"
+#include "warnless.h"
+#include "curl_multibyte.h"
+#include "sendf.h"
+#include "strdup.h"
+#include "strcase.h"
+#include "strerror.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+* Curl_auth_is_digest_supported()
+*
+* This is used to evaluate if DIGEST is supported.
+*
+* Parameters: None
+*
+* Returns TRUE if DIGEST is supported by Windows SSPI.
+*/
+bool Curl_auth_is_digest_supported(void)
+{
+ PSecPkgInfo SecurityPackage;
+ SECURITY_STATUS status;
+
+ /* Query the security package for Digest */
+ status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
+ &SecurityPackage);
+
+ /* Release the package buffer as it is not required anymore */
+ if(status == SEC_E_OK) {
+ s_pSecFn->FreeContextBuffer(SecurityPackage);
+ }
+
+ return (status == SEC_E_OK ? TRUE : FALSE);
+}
+
+/*
+ * Curl_auth_create_digest_md5_message()
+ *
+ * This is used to generate an already encoded DIGEST-MD5 response message
+ * ready for sending to the recipient.
+ *
+ * Parameters:
+ *
+ * data [in] - The session handle.
+ * chlg [in] - The challenge message.
+ * userp [in] - The user name in the format User or Domain\User.
+ * passwdp [in] - The user's password.
+ * service [in] - The service type such as http, smtp, pop or imap.
+ * out [out] - The result storage.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
+ const struct bufref *chlg,
+ const char *userp,
+ const char *passwdp,
+ const char *service,
+ struct bufref *out)
+{
+ CURLcode result = CURLE_OK;
+ TCHAR *spn = NULL;
+ size_t token_max = 0;
+ unsigned char *output_token = NULL;
+ CredHandle credentials;
+ CtxtHandle context;
+ PSecPkgInfo SecurityPackage;
+ SEC_WINNT_AUTH_IDENTITY identity;
+ SEC_WINNT_AUTH_IDENTITY *p_identity;
+ SecBuffer chlg_buf;
+ SecBuffer resp_buf;
+ SecBufferDesc chlg_desc;
+ SecBufferDesc resp_desc;
+ SECURITY_STATUS status;
+ unsigned long attrs;
+ TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
+
+ /* Ensure we have a valid challenge message */
+ if(!Curl_bufref_len(chlg)) {
+ infof(data, "DIGEST-MD5 handshake failure (empty challenge message)");
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+
+ /* Query the security package for DigestSSP */
+ status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
+ &SecurityPackage);
+ if(status != SEC_E_OK) {
+ failf(data, "SSPI: couldn't get auth info");
+ return CURLE_AUTH_ERROR;
+ }
+
+ token_max = SecurityPackage->cbMaxToken;
+
+ /* Release the package buffer as it is not required anymore */
+ s_pSecFn->FreeContextBuffer(SecurityPackage);
+
+ /* Allocate our response buffer */
+ output_token = malloc(token_max);
+ if(!output_token)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Generate our SPN */
+ spn = Curl_auth_build_spn(service, data->conn->host.name, NULL);
+ if(!spn) {
+ free(output_token);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(userp && *userp) {
+ /* Populate our identity structure */
+ result = Curl_create_sspi_identity(userp, passwdp, &identity);
+ if(result) {
+ free(spn);
+ free(output_token);
+ return result;
+ }
+
+ /* Allow proper cleanup of the identity structure */
+ p_identity = &identity;
+ }
+ else
+ /* Use the current Windows user */
+ p_identity = NULL;
+
+ /* Acquire our credentials handle */
+ status = s_pSecFn->AcquireCredentialsHandle(NULL,
+ (TCHAR *) TEXT(SP_NAME_DIGEST),
+ SECPKG_CRED_OUTBOUND, NULL,
+ p_identity, NULL, NULL,
+ &credentials, &expiry);
+
+ if(status != SEC_E_OK) {
+ Curl_sspi_free_identity(p_identity);
+ free(spn);
+ free(output_token);
+ return CURLE_LOGIN_DENIED;
+ }
+
+ /* Setup the challenge "input" security buffer */
+ chlg_desc.ulVersion = SECBUFFER_VERSION;
+ chlg_desc.cBuffers = 1;
+ chlg_desc.pBuffers = &chlg_buf;
+ chlg_buf.BufferType = SECBUFFER_TOKEN;
+ chlg_buf.pvBuffer = (void *) Curl_bufref_ptr(chlg);
+ chlg_buf.cbBuffer = curlx_uztoul(Curl_bufref_len(chlg));
+
+ /* Setup the response "output" security buffer */
+ resp_desc.ulVersion = SECBUFFER_VERSION;
+ resp_desc.cBuffers = 1;
+ resp_desc.pBuffers = &resp_buf;
+ resp_buf.BufferType = SECBUFFER_TOKEN;
+ resp_buf.pvBuffer = output_token;
+ resp_buf.cbBuffer = curlx_uztoul(token_max);
+
+ /* Generate our response message */
+ status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, spn,
+ 0, 0, 0, &chlg_desc, 0,
+ &context, &resp_desc, &attrs,
+ &expiry);
+
+ if(status == SEC_I_COMPLETE_NEEDED ||
+ status == SEC_I_COMPLETE_AND_CONTINUE)
+ s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
+ else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ char buffer[STRERROR_LEN];
+#endif
+
+ s_pSecFn->FreeCredentialsHandle(&credentials);
+ Curl_sspi_free_identity(p_identity);
+ free(spn);
+ free(output_token);
+
+ if(status == SEC_E_INSUFFICIENT_MEMORY)
+ return CURLE_OUT_OF_MEMORY;
+
+ infof(data, "schannel: InitializeSecurityContext failed: %s",
+ Curl_sspi_strerror(status, buffer, sizeof(buffer)));
+
+ return CURLE_AUTH_ERROR;
+ }
+
+ /* Return the response. */
+ Curl_bufref_set(out, output_token, resp_buf.cbBuffer, curl_free);
+
+ /* Free our handles */
+ s_pSecFn->DeleteSecurityContext(&context);
+ s_pSecFn->FreeCredentialsHandle(&credentials);
+
+ /* Free the identity structure */
+ Curl_sspi_free_identity(p_identity);
+
+ /* Free the SPN */
+ free(spn);
+
+ return result;
+}
+
+/*
+ * Curl_override_sspi_http_realm()
+ *
+ * This is used to populate the domain in a SSPI identity structure
+ * The realm is extracted from the challenge message and used as the
+ * domain if it is not already explicitly set.
+ *
+ * Parameters:
+ *
+ * chlg [in] - The challenge message.
+ * identity [in/out] - The identity structure.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_override_sspi_http_realm(const char *chlg,
+ SEC_WINNT_AUTH_IDENTITY *identity)
+{
+ xcharp_u domain, dup_domain;
+
+ /* If domain is blank or unset, check challenge message for realm */
+ if(!identity->Domain || !identity->DomainLength) {
+ for(;;) {
+ char value[DIGEST_MAX_VALUE_LENGTH];
+ char content[DIGEST_MAX_CONTENT_LENGTH];
+
+ /* Pass all additional spaces here */
+ while(*chlg && ISBLANK(*chlg))
+ chlg++;
+
+ /* Extract a value=content pair */
+ if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) {
+ if(strcasecompare(value, "realm")) {
+
+ /* Setup identity's domain and length */
+ domain.tchar_ptr = curlx_convert_UTF8_to_tchar((char *) content);
+ if(!domain.tchar_ptr)
+ return CURLE_OUT_OF_MEMORY;
+
+ dup_domain.tchar_ptr = _tcsdup(domain.tchar_ptr);
+ if(!dup_domain.tchar_ptr) {
+ curlx_unicodefree(domain.tchar_ptr);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ free(identity->Domain);
+ identity->Domain = dup_domain.tbyte_ptr;
+ identity->DomainLength = curlx_uztoul(_tcslen(dup_domain.tchar_ptr));
+ dup_domain.tchar_ptr = NULL;
+
+ curlx_unicodefree(domain.tchar_ptr);
+ }
+ else {
+ /* Unknown specifier, ignore it! */
+ }
+ }
+ else
+ break; /* We're done here */
+
+ /* Pass all additional spaces here */
+ while(*chlg && ISBLANK(*chlg))
+ chlg++;
+
+ /* Allow the list to be comma-separated */
+ if(',' == *chlg)
+ chlg++;
+ }
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_auth_decode_digest_http_message()
+ *
+ * This is used to decode an HTTP DIGEST challenge message into the separate
+ * attributes.
+ *
+ * Parameters:
+ *
+ * chlg [in] - The challenge message.
+ * digest [in/out] - The digest data struct being used and modified.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
+ struct digestdata *digest)
+{
+ size_t chlglen = strlen(chlg);
+
+ /* We had an input token before so if there's another one now that means we
+ provided bad credentials in the previous request or it's stale. */
+ if(digest->input_token) {
+ bool stale = false;
+ const char *p = chlg;
+
+ /* Check for the 'stale' directive */
+ for(;;) {
+ char value[DIGEST_MAX_VALUE_LENGTH];
+ char content[DIGEST_MAX_CONTENT_LENGTH];
+
+ while(*p && ISBLANK(*p))
+ p++;
+
+ if(!Curl_auth_digest_get_pair(p, value, content, &p))
+ break;
+
+ if(strcasecompare(value, "stale") &&
+ strcasecompare(content, "true")) {
+ stale = true;
+ break;
+ }
+
+ while(*p && ISBLANK(*p))
+ p++;
+
+ if(',' == *p)
+ p++;
+ }
+
+ if(stale)
+ Curl_auth_digest_cleanup(digest);
+ else
+ return CURLE_LOGIN_DENIED;
+ }
+
+ /* Store the challenge for use later */
+ digest->input_token = (BYTE *) Curl_memdup(chlg, chlglen + 1);
+ if(!digest->input_token)
+ return CURLE_OUT_OF_MEMORY;
+
+ digest->input_token_len = chlglen;
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_auth_create_digest_http_message()
+ *
+ * This is used to generate an HTTP DIGEST response message ready for sending
+ * to the recipient.
+ *
+ * Parameters:
+ *
+ * data [in] - The session handle.
+ * userp [in] - The user name in the format User or Domain\User.
+ * passwdp [in] - The user's password.
+ * request [in] - The HTTP request.
+ * uripath [in] - The path of the HTTP uri.
+ * digest [in/out] - The digest data struct being used and modified.
+ * outptr [in/out] - The address where a pointer to newly allocated memory
+ * holding the result will be stored upon completion.
+ * outlen [out] - The length of the output message.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
+ const char *userp,
+ const char *passwdp,
+ const unsigned char *request,
+ const unsigned char *uripath,
+ struct digestdata *digest,
+ char **outptr, size_t *outlen)
+{
+ size_t token_max;
+ char *resp;
+ BYTE *output_token;
+ size_t output_token_len = 0;
+ PSecPkgInfo SecurityPackage;
+ SecBuffer chlg_buf[5];
+ SecBufferDesc chlg_desc;
+ SECURITY_STATUS status;
+
+ (void) data;
+
+ /* Query the security package for DigestSSP */
+ status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
+ &SecurityPackage);
+ if(status != SEC_E_OK) {
+ failf(data, "SSPI: couldn't get auth info");
+ return CURLE_AUTH_ERROR;
+ }
+
+ token_max = SecurityPackage->cbMaxToken;
+
+ /* Release the package buffer as it is not required anymore */
+ s_pSecFn->FreeContextBuffer(SecurityPackage);
+
+ /* Allocate the output buffer according to the max token size as indicated
+ by the security package */
+ output_token = malloc(token_max);
+ if(!output_token) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* If the user/passwd that was used to make the identity for http_context
+ has changed then delete that context. */
+ if((userp && !digest->user) || (!userp && digest->user) ||
+ (passwdp && !digest->passwd) || (!passwdp && digest->passwd) ||
+ (userp && digest->user && Curl_timestrcmp(userp, digest->user)) ||
+ (passwdp && digest->passwd && Curl_timestrcmp(passwdp, digest->passwd))) {
+ if(digest->http_context) {
+ s_pSecFn->DeleteSecurityContext(digest->http_context);
+ Curl_safefree(digest->http_context);
+ }
+ Curl_safefree(digest->user);
+ Curl_safefree(digest->passwd);
+ }
+
+ if(digest->http_context) {
+ chlg_desc.ulVersion = SECBUFFER_VERSION;
+ chlg_desc.cBuffers = 5;
+ chlg_desc.pBuffers = chlg_buf;
+ chlg_buf[0].BufferType = SECBUFFER_TOKEN;
+ chlg_buf[0].pvBuffer = NULL;
+ chlg_buf[0].cbBuffer = 0;
+ chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
+ chlg_buf[1].pvBuffer = (void *) request;
+ chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request));
+ chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
+ chlg_buf[2].pvBuffer = (void *) uripath;
+ chlg_buf[2].cbBuffer = curlx_uztoul(strlen((const char *) uripath));
+ chlg_buf[3].BufferType = SECBUFFER_PKG_PARAMS;
+ chlg_buf[3].pvBuffer = NULL;
+ chlg_buf[3].cbBuffer = 0;
+ chlg_buf[4].BufferType = SECBUFFER_PADDING;
+ chlg_buf[4].pvBuffer = output_token;
+ chlg_buf[4].cbBuffer = curlx_uztoul(token_max);
+
+ status = s_pSecFn->MakeSignature(digest->http_context, 0, &chlg_desc, 0);
+ if(status == SEC_E_OK)
+ output_token_len = chlg_buf[4].cbBuffer;
+ else { /* delete the context so a new one can be made */
+ infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx",
+ (long)status);
+ s_pSecFn->DeleteSecurityContext(digest->http_context);
+ Curl_safefree(digest->http_context);
+ }
+ }
+
+ if(!digest->http_context) {
+ CredHandle credentials;
+ SEC_WINNT_AUTH_IDENTITY identity;
+ SEC_WINNT_AUTH_IDENTITY *p_identity;
+ SecBuffer resp_buf;
+ SecBufferDesc resp_desc;
+ unsigned long attrs;
+ TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
+ TCHAR *spn;
+
+ /* free the copy of user/passwd used to make the previous identity */
+ Curl_safefree(digest->user);
+ Curl_safefree(digest->passwd);
+
+ if(userp && *userp) {
+ /* Populate our identity structure */
+ if(Curl_create_sspi_identity(userp, passwdp, &identity)) {
+ free(output_token);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* Populate our identity domain */
+ if(Curl_override_sspi_http_realm((const char *) digest->input_token,
+ &identity)) {
+ free(output_token);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* Allow proper cleanup of the identity structure */
+ p_identity = &identity;
+ }
+ else
+ /* Use the current Windows user */
+ p_identity = NULL;
+
+ if(userp) {
+ digest->user = strdup(userp);
+
+ if(!digest->user) {
+ free(output_token);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+
+ if(passwdp) {
+ digest->passwd = strdup(passwdp);
+
+ if(!digest->passwd) {
+ free(output_token);
+ Curl_safefree(digest->user);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+
+ /* Acquire our credentials handle */
+ status = s_pSecFn->AcquireCredentialsHandle(NULL,
+ (TCHAR *) TEXT(SP_NAME_DIGEST),
+ SECPKG_CRED_OUTBOUND, NULL,
+ p_identity, NULL, NULL,
+ &credentials, &expiry);
+ if(status != SEC_E_OK) {
+ Curl_sspi_free_identity(p_identity);
+ free(output_token);
+
+ return CURLE_LOGIN_DENIED;
+ }
+
+ /* Setup the challenge "input" security buffer if present */
+ chlg_desc.ulVersion = SECBUFFER_VERSION;
+ chlg_desc.cBuffers = 3;
+ chlg_desc.pBuffers = chlg_buf;
+ chlg_buf[0].BufferType = SECBUFFER_TOKEN;
+ chlg_buf[0].pvBuffer = digest->input_token;
+ chlg_buf[0].cbBuffer = curlx_uztoul(digest->input_token_len);
+ chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
+ chlg_buf[1].pvBuffer = (void *) request;
+ chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request));
+ chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
+ chlg_buf[2].pvBuffer = NULL;
+ chlg_buf[2].cbBuffer = 0;
+
+ /* Setup the response "output" security buffer */
+ resp_desc.ulVersion = SECBUFFER_VERSION;
+ resp_desc.cBuffers = 1;
+ resp_desc.pBuffers = &resp_buf;
+ resp_buf.BufferType = SECBUFFER_TOKEN;
+ resp_buf.pvBuffer = output_token;
+ resp_buf.cbBuffer = curlx_uztoul(token_max);
+
+ spn = curlx_convert_UTF8_to_tchar((char *) uripath);
+ if(!spn) {
+ s_pSecFn->FreeCredentialsHandle(&credentials);
+
+ Curl_sspi_free_identity(p_identity);
+ free(output_token);
+
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* Allocate our new context handle */
+ digest->http_context = calloc(1, sizeof(CtxtHandle));
+ if(!digest->http_context)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Generate our response message */
+ status = s_pSecFn->InitializeSecurityContext(&credentials, NULL,
+ spn,
+ ISC_REQ_USE_HTTP_STYLE, 0, 0,
+ &chlg_desc, 0,
+ digest->http_context,
+ &resp_desc, &attrs, &expiry);
+ curlx_unicodefree(spn);
+
+ if(status == SEC_I_COMPLETE_NEEDED ||
+ status == SEC_I_COMPLETE_AND_CONTINUE)
+ s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
+ else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ char buffer[STRERROR_LEN];
+#endif
+
+ s_pSecFn->FreeCredentialsHandle(&credentials);
+
+ Curl_sspi_free_identity(p_identity);
+ free(output_token);
+
+ Curl_safefree(digest->http_context);
+
+ if(status == SEC_E_INSUFFICIENT_MEMORY)
+ return CURLE_OUT_OF_MEMORY;
+
+ infof(data, "schannel: InitializeSecurityContext failed: %s",
+ Curl_sspi_strerror(status, buffer, sizeof(buffer)));
+
+ return CURLE_AUTH_ERROR;
+ }
+
+ output_token_len = resp_buf.cbBuffer;
+
+ s_pSecFn->FreeCredentialsHandle(&credentials);
+ Curl_sspi_free_identity(p_identity);
+ }
+
+ resp = malloc(output_token_len + 1);
+ if(!resp) {
+ free(output_token);
+
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* Copy the generated response */
+ memcpy(resp, output_token, output_token_len);
+ resp[output_token_len] = 0;
+
+ /* Return the response */
+ *outptr = resp;
+ *outlen = output_token_len;
+
+ /* Free the response buffer */
+ free(output_token);
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_auth_digest_cleanup()
+ *
+ * This is used to clean up the digest specific data.
+ *
+ * Parameters:
+ *
+ * digest [in/out] - The digest data struct being cleaned up.
+ *
+ */
+void Curl_auth_digest_cleanup(struct digestdata *digest)
+{
+ /* Free the input token */
+ Curl_safefree(digest->input_token);
+
+ /* Reset any variables */
+ digest->input_token_len = 0;
+
+ /* Delete security context */
+ if(digest->http_context) {
+ s_pSecFn->DeleteSecurityContext(digest->http_context);
+ Curl_safefree(digest->http_context);
+ }
+
+ /* Free the copy of user/passwd used to make the identity for http_context */
+ Curl_safefree(digest->user);
+ Curl_safefree(digest->passwd);
+}
+
+#endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_CRYPTO_AUTH */
diff --git a/libs/libcurl/src/vauth/gsasl.c b/libs/libcurl/src/vauth/gsasl.c index a73c644434..df9db426e1 100644 --- a/libs/libcurl/src/vauth/gsasl.c +++ b/libs/libcurl/src/vauth/gsasl.c @@ -1,127 +1,127 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2020 - 2022, Simon Josefsson, <simon@josefsson.org>, 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 - * - * RFC5802 SCRAM-SHA-1 authentication - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifdef USE_GSASL - -#include <curl/curl.h> - -#include "vauth/vauth.h" -#include "urldata.h" -#include "sendf.h" - -#include <gsasl.h> - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, - const char *mech, - struct gsasldata *gsasl) -{ - int res; - - res = gsasl_init(&gsasl->ctx); - if(res != GSASL_OK) { - failf(data, "gsasl init: %s\n", gsasl_strerror(res)); - return FALSE; - } - - res = gsasl_client_start(gsasl->ctx, mech, &gsasl->client); - if(res != GSASL_OK) { - gsasl_done(gsasl->ctx); - return FALSE; - } - - return true; -} - -CURLcode Curl_auth_gsasl_start(struct Curl_easy *data, - const char *userp, - const char *passwdp, - struct gsasldata *gsasl) -{ -#if GSASL_VERSION_NUMBER >= 0x010b00 - int res; - res = -#endif - gsasl_property_set(gsasl->client, GSASL_AUTHID, userp); -#if GSASL_VERSION_NUMBER >= 0x010b00 - if(res != GSASL_OK) { - failf(data, "setting AUTHID failed: %s\n", gsasl_strerror(res)); - return CURLE_OUT_OF_MEMORY; - } -#endif - -#if GSASL_VERSION_NUMBER >= 0x010b00 - res = -#endif - gsasl_property_set(gsasl->client, GSASL_PASSWORD, passwdp); -#if GSASL_VERSION_NUMBER >= 0x010b00 - if(res != GSASL_OK) { - failf(data, "setting PASSWORD failed: %s\n", gsasl_strerror(res)); - return CURLE_OUT_OF_MEMORY; - } -#endif - - (void)data; - - return CURLE_OK; -} - -CURLcode Curl_auth_gsasl_token(struct Curl_easy *data, - const struct bufref *chlg, - struct gsasldata *gsasl, - struct bufref *out) -{ - int res; - char *response; - size_t outlen; - - res = gsasl_step(gsasl->client, - (const char *) Curl_bufref_ptr(chlg), Curl_bufref_len(chlg), - &response, &outlen); - if(res != GSASL_OK && res != GSASL_NEEDS_MORE) { - failf(data, "GSASL step: %s\n", gsasl_strerror(res)); - return CURLE_BAD_CONTENT_ENCODING; - } - - Curl_bufref_set(out, response, outlen, gsasl_free); - return CURLE_OK; -} - -void Curl_auth_gsasl_cleanup(struct gsasldata *gsasl) -{ - gsasl_finish(gsasl->client); - gsasl->client = NULL; - - gsasl_done(gsasl->ctx); - gsasl->ctx = NULL; -} -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2020 - 2022, Simon Josefsson, <simon@josefsson.org>, 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
+ *
+ * RFC5802 SCRAM-SHA-1 authentication
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_GSASL
+
+#include <curl/curl.h>
+
+#include "vauth/vauth.h"
+#include "urldata.h"
+#include "sendf.h"
+
+#include <gsasl.h>
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+bool Curl_auth_gsasl_is_supported(struct Curl_easy *data,
+ const char *mech,
+ struct gsasldata *gsasl)
+{
+ int res;
+
+ res = gsasl_init(&gsasl->ctx);
+ if(res != GSASL_OK) {
+ failf(data, "gsasl init: %s\n", gsasl_strerror(res));
+ return FALSE;
+ }
+
+ res = gsasl_client_start(gsasl->ctx, mech, &gsasl->client);
+ if(res != GSASL_OK) {
+ gsasl_done(gsasl->ctx);
+ return FALSE;
+ }
+
+ return true;
+}
+
+CURLcode Curl_auth_gsasl_start(struct Curl_easy *data,
+ const char *userp,
+ const char *passwdp,
+ struct gsasldata *gsasl)
+{
+#if GSASL_VERSION_NUMBER >= 0x010b00
+ int res;
+ res =
+#endif
+ gsasl_property_set(gsasl->client, GSASL_AUTHID, userp);
+#if GSASL_VERSION_NUMBER >= 0x010b00
+ if(res != GSASL_OK) {
+ failf(data, "setting AUTHID failed: %s\n", gsasl_strerror(res));
+ return CURLE_OUT_OF_MEMORY;
+ }
+#endif
+
+#if GSASL_VERSION_NUMBER >= 0x010b00
+ res =
+#endif
+ gsasl_property_set(gsasl->client, GSASL_PASSWORD, passwdp);
+#if GSASL_VERSION_NUMBER >= 0x010b00
+ if(res != GSASL_OK) {
+ failf(data, "setting PASSWORD failed: %s\n", gsasl_strerror(res));
+ return CURLE_OUT_OF_MEMORY;
+ }
+#endif
+
+ (void)data;
+
+ return CURLE_OK;
+}
+
+CURLcode Curl_auth_gsasl_token(struct Curl_easy *data,
+ const struct bufref *chlg,
+ struct gsasldata *gsasl,
+ struct bufref *out)
+{
+ int res;
+ char *response;
+ size_t outlen;
+
+ res = gsasl_step(gsasl->client,
+ (const char *) Curl_bufref_ptr(chlg), Curl_bufref_len(chlg),
+ &response, &outlen);
+ if(res != GSASL_OK && res != GSASL_NEEDS_MORE) {
+ failf(data, "GSASL step: %s\n", gsasl_strerror(res));
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+
+ Curl_bufref_set(out, response, outlen, gsasl_free);
+ return CURLE_OK;
+}
+
+void Curl_auth_gsasl_cleanup(struct gsasldata *gsasl)
+{
+ gsasl_finish(gsasl->client);
+ gsasl->client = NULL;
+
+ gsasl_done(gsasl->ctx);
+ gsasl->ctx = NULL;
+}
+#endif
diff --git a/libs/libcurl/src/vauth/krb5_gssapi.c b/libs/libcurl/src/vauth/krb5_gssapi.c index bac78049dc..c4decbd951 100644 --- a/libs/libcurl/src/vauth/krb5_gssapi.c +++ b/libs/libcurl/src/vauth/krb5_gssapi.c @@ -1,323 +1,323 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2014 - 2019, Steve Holme, <steve_holme@hotmail.com>. - * Copyright (C) 2015 - 2022, 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 - * - * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if defined(HAVE_GSSAPI) && defined(USE_KERBEROS5) - -#include <curl/curl.h> - -#include "vauth/vauth.h" -#include "curl_sasl.h" -#include "urldata.h" -#include "curl_gssapi.h" -#include "sendf.h" -#include "curl_printf.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* - * Curl_auth_is_gssapi_supported() - * - * This is used to evaluate if GSSAPI (Kerberos V5) is supported. - * - * Parameters: None - * - * Returns TRUE if Kerberos V5 is supported by the GSS-API library. - */ -bool Curl_auth_is_gssapi_supported(void) -{ - return TRUE; -} - -/* - * Curl_auth_create_gssapi_user_message() - * - * This is used to generate an already encoded GSSAPI (Kerberos V5) user token - * message ready for sending to the recipient. - * - * Parameters: - * - * data [in] - The session handle. - * userp [in] - The user name. - * passwdp [in] - The user's password. - * service [in] - The service type such as http, smtp, pop or imap. - * host [in[ - The host name. - * mutual_auth [in] - Flag specifying whether or not mutual authentication - * is enabled. - * chlg [in] - Optional challenge message. - * krb5 [in/out] - The Kerberos 5 data struct being used and modified. - * out [out] - The result storage. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const char *service, - const char *host, - const bool mutual_auth, - const struct bufref *chlg, - struct kerberos5data *krb5, - struct bufref *out) -{ - CURLcode result = CURLE_OK; - OM_uint32 major_status; - OM_uint32 minor_status; - OM_uint32 unused_status; - gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; - - (void) userp; - (void) passwdp; - - if(!krb5->spn) { - /* Generate our SPN */ - char *spn = Curl_auth_build_spn(service, NULL, host); - if(!spn) - return CURLE_OUT_OF_MEMORY; - - /* Populate the SPN structure */ - spn_token.value = spn; - spn_token.length = strlen(spn); - - /* Import the SPN */ - major_status = gss_import_name(&minor_status, &spn_token, - GSS_C_NT_HOSTBASED_SERVICE, &krb5->spn); - if(GSS_ERROR(major_status)) { - Curl_gss_log_error(data, "gss_import_name() failed: ", - major_status, minor_status); - - free(spn); - - return CURLE_AUTH_ERROR; - } - - free(spn); - } - - if(chlg) { - if(!Curl_bufref_len(chlg)) { - infof(data, "GSSAPI handshake failure (empty challenge message)"); - return CURLE_BAD_CONTENT_ENCODING; - } - input_token.value = (void *) Curl_bufref_ptr(chlg); - input_token.length = Curl_bufref_len(chlg); - } - - major_status = Curl_gss_init_sec_context(data, - &minor_status, - &krb5->context, - krb5->spn, - &Curl_krb5_mech_oid, - GSS_C_NO_CHANNEL_BINDINGS, - &input_token, - &output_token, - mutual_auth, - NULL); - - if(GSS_ERROR(major_status)) { - if(output_token.value) - gss_release_buffer(&unused_status, &output_token); - - Curl_gss_log_error(data, "gss_init_sec_context() failed: ", - major_status, minor_status); - - return CURLE_AUTH_ERROR; - } - - if(output_token.value && output_token.length) { - result = Curl_bufref_memdup(out, output_token.value, output_token.length); - gss_release_buffer(&unused_status, &output_token); - } - else - Curl_bufref_set(out, mutual_auth? "": NULL, 0, NULL); - - return result; -} - -/* - * Curl_auth_create_gssapi_security_message() - * - * This is used to generate an already encoded GSSAPI (Kerberos V5) security - * token message ready for sending to the recipient. - * - * Parameters: - * - * data [in] - The session handle. - * authzid [in] - The authorization identity if some. - * chlg [in] - Optional challenge message. - * krb5 [in/out] - The Kerberos 5 data struct being used and modified. - * out [out] - The result storage. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, - const char *authzid, - const struct bufref *chlg, - struct kerberos5data *krb5, - struct bufref *out) -{ - CURLcode result = CURLE_OK; - size_t messagelen = 0; - unsigned char *message = NULL; - OM_uint32 major_status; - OM_uint32 minor_status; - OM_uint32 unused_status; - gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; - unsigned char *indata; - gss_qop_t qop = GSS_C_QOP_DEFAULT; - unsigned int sec_layer = 0; - unsigned int max_size = 0; - - /* Ensure we have a valid challenge message */ - if(!Curl_bufref_len(chlg)) { - infof(data, "GSSAPI handshake failure (empty security message)"); - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Setup the challenge "input" security buffer */ - input_token.value = (void *) Curl_bufref_ptr(chlg); - input_token.length = Curl_bufref_len(chlg); - - /* Decrypt the inbound challenge and obtain the qop */ - major_status = gss_unwrap(&minor_status, krb5->context, &input_token, - &output_token, NULL, &qop); - if(GSS_ERROR(major_status)) { - Curl_gss_log_error(data, "gss_unwrap() failed: ", - major_status, minor_status); - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Not 4 octets long so fail as per RFC4752 Section 3.1 */ - if(output_token.length != 4) { - infof(data, "GSSAPI handshake failure (invalid security data)"); - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Extract the security layer and the maximum message size */ - indata = output_token.value; - sec_layer = indata[0]; - max_size = (indata[1] << 16) | (indata[2] << 8) | indata[3]; - - /* Free the challenge as it is not required anymore */ - gss_release_buffer(&unused_status, &output_token); - - /* Process the security layer */ - if(!(sec_layer & GSSAUTH_P_NONE)) { - infof(data, "GSSAPI handshake failure (invalid security layer)"); - - return CURLE_BAD_CONTENT_ENCODING; - } - sec_layer &= GSSAUTH_P_NONE; /* We do not support a security layer */ - - /* Process the maximum message size the server can receive */ - if(max_size > 0) { - /* The server has told us it supports a maximum receive buffer, however, as - we don't require one unless we are encrypting data, we tell the server - our receive buffer is zero. */ - max_size = 0; - } - - /* Allocate our message */ - messagelen = 4; - if(authzid) - messagelen += strlen(authzid); - message = malloc(messagelen); - if(!message) - return CURLE_OUT_OF_MEMORY; - - /* Populate the message with the security layer and client supported receive - message size. */ - message[0] = sec_layer & 0xFF; - message[1] = (max_size >> 16) & 0xFF; - message[2] = (max_size >> 8) & 0xFF; - message[3] = max_size & 0xFF; - - /* If given, append the authorization identity. */ - - if(authzid && *authzid) - memcpy(message + 4, authzid, messagelen - 4); - - /* Setup the "authentication data" security buffer */ - input_token.value = message; - input_token.length = messagelen; - - /* Encrypt the data */ - major_status = gss_wrap(&minor_status, krb5->context, 0, - GSS_C_QOP_DEFAULT, &input_token, NULL, - &output_token); - if(GSS_ERROR(major_status)) { - Curl_gss_log_error(data, "gss_wrap() failed: ", - major_status, minor_status); - free(message); - return CURLE_AUTH_ERROR; - } - - /* Return the response. */ - result = Curl_bufref_memdup(out, output_token.value, output_token.length); - /* Free the output buffer */ - gss_release_buffer(&unused_status, &output_token); - - /* Free the message buffer */ - free(message); - - return result; -} - -/* - * Curl_auth_cleanup_gssapi() - * - * This is used to clean up the GSSAPI (Kerberos V5) specific data. - * - * Parameters: - * - * krb5 [in/out] - The Kerberos 5 data struct being cleaned up. - * - */ -void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5) -{ - OM_uint32 minor_status; - - /* Free our security context */ - if(krb5->context != GSS_C_NO_CONTEXT) { - gss_delete_sec_context(&minor_status, &krb5->context, GSS_C_NO_BUFFER); - krb5->context = GSS_C_NO_CONTEXT; - } - - /* Free the SPN */ - if(krb5->spn != GSS_C_NO_NAME) { - gss_release_name(&minor_status, &krb5->spn); - krb5->spn = GSS_C_NO_NAME; - } -} - -#endif /* HAVE_GSSAPI && USE_KERBEROS5 */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2014 - 2019, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) 2015 - 2022, 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
+ *
+ * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if defined(HAVE_GSSAPI) && defined(USE_KERBEROS5)
+
+#include <curl/curl.h>
+
+#include "vauth/vauth.h"
+#include "curl_sasl.h"
+#include "urldata.h"
+#include "curl_gssapi.h"
+#include "sendf.h"
+#include "curl_printf.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * Curl_auth_is_gssapi_supported()
+ *
+ * This is used to evaluate if GSSAPI (Kerberos V5) is supported.
+ *
+ * Parameters: None
+ *
+ * Returns TRUE if Kerberos V5 is supported by the GSS-API library.
+ */
+bool Curl_auth_is_gssapi_supported(void)
+{
+ return TRUE;
+}
+
+/*
+ * Curl_auth_create_gssapi_user_message()
+ *
+ * This is used to generate an already encoded GSSAPI (Kerberos V5) user token
+ * message ready for sending to the recipient.
+ *
+ * Parameters:
+ *
+ * data [in] - The session handle.
+ * userp [in] - The user name.
+ * passwdp [in] - The user's password.
+ * service [in] - The service type such as http, smtp, pop or imap.
+ * host [in[ - The host name.
+ * mutual_auth [in] - Flag specifying whether or not mutual authentication
+ * is enabled.
+ * chlg [in] - Optional challenge message.
+ * krb5 [in/out] - The Kerberos 5 data struct being used and modified.
+ * out [out] - The result storage.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
+ const char *userp,
+ const char *passwdp,
+ const char *service,
+ const char *host,
+ const bool mutual_auth,
+ const struct bufref *chlg,
+ struct kerberos5data *krb5,
+ struct bufref *out)
+{
+ CURLcode result = CURLE_OK;
+ OM_uint32 major_status;
+ OM_uint32 minor_status;
+ OM_uint32 unused_status;
+ gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+
+ (void) userp;
+ (void) passwdp;
+
+ if(!krb5->spn) {
+ /* Generate our SPN */
+ char *spn = Curl_auth_build_spn(service, NULL, host);
+ if(!spn)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Populate the SPN structure */
+ spn_token.value = spn;
+ spn_token.length = strlen(spn);
+
+ /* Import the SPN */
+ major_status = gss_import_name(&minor_status, &spn_token,
+ GSS_C_NT_HOSTBASED_SERVICE, &krb5->spn);
+ if(GSS_ERROR(major_status)) {
+ Curl_gss_log_error(data, "gss_import_name() failed: ",
+ major_status, minor_status);
+
+ free(spn);
+
+ return CURLE_AUTH_ERROR;
+ }
+
+ free(spn);
+ }
+
+ if(chlg) {
+ if(!Curl_bufref_len(chlg)) {
+ infof(data, "GSSAPI handshake failure (empty challenge message)");
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+ input_token.value = (void *) Curl_bufref_ptr(chlg);
+ input_token.length = Curl_bufref_len(chlg);
+ }
+
+ major_status = Curl_gss_init_sec_context(data,
+ &minor_status,
+ &krb5->context,
+ krb5->spn,
+ &Curl_krb5_mech_oid,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &input_token,
+ &output_token,
+ mutual_auth,
+ NULL);
+
+ if(GSS_ERROR(major_status)) {
+ if(output_token.value)
+ gss_release_buffer(&unused_status, &output_token);
+
+ Curl_gss_log_error(data, "gss_init_sec_context() failed: ",
+ major_status, minor_status);
+
+ return CURLE_AUTH_ERROR;
+ }
+
+ if(output_token.value && output_token.length) {
+ result = Curl_bufref_memdup(out, output_token.value, output_token.length);
+ gss_release_buffer(&unused_status, &output_token);
+ }
+ else
+ Curl_bufref_set(out, mutual_auth? "": NULL, 0, NULL);
+
+ return result;
+}
+
+/*
+ * Curl_auth_create_gssapi_security_message()
+ *
+ * This is used to generate an already encoded GSSAPI (Kerberos V5) security
+ * token message ready for sending to the recipient.
+ *
+ * Parameters:
+ *
+ * data [in] - The session handle.
+ * authzid [in] - The authorization identity if some.
+ * chlg [in] - Optional challenge message.
+ * krb5 [in/out] - The Kerberos 5 data struct being used and modified.
+ * out [out] - The result storage.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
+ const char *authzid,
+ const struct bufref *chlg,
+ struct kerberos5data *krb5,
+ struct bufref *out)
+{
+ CURLcode result = CURLE_OK;
+ size_t messagelen = 0;
+ unsigned char *message = NULL;
+ OM_uint32 major_status;
+ OM_uint32 minor_status;
+ OM_uint32 unused_status;
+ gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+ unsigned char *indata;
+ gss_qop_t qop = GSS_C_QOP_DEFAULT;
+ unsigned int sec_layer = 0;
+ unsigned int max_size = 0;
+
+ /* Ensure we have a valid challenge message */
+ if(!Curl_bufref_len(chlg)) {
+ infof(data, "GSSAPI handshake failure (empty security message)");
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+
+ /* Setup the challenge "input" security buffer */
+ input_token.value = (void *) Curl_bufref_ptr(chlg);
+ input_token.length = Curl_bufref_len(chlg);
+
+ /* Decrypt the inbound challenge and obtain the qop */
+ major_status = gss_unwrap(&minor_status, krb5->context, &input_token,
+ &output_token, NULL, &qop);
+ if(GSS_ERROR(major_status)) {
+ Curl_gss_log_error(data, "gss_unwrap() failed: ",
+ major_status, minor_status);
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+
+ /* Not 4 octets long so fail as per RFC4752 Section 3.1 */
+ if(output_token.length != 4) {
+ infof(data, "GSSAPI handshake failure (invalid security data)");
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+
+ /* Extract the security layer and the maximum message size */
+ indata = output_token.value;
+ sec_layer = indata[0];
+ max_size = (indata[1] << 16) | (indata[2] << 8) | indata[3];
+
+ /* Free the challenge as it is not required anymore */
+ gss_release_buffer(&unused_status, &output_token);
+
+ /* Process the security layer */
+ if(!(sec_layer & GSSAUTH_P_NONE)) {
+ infof(data, "GSSAPI handshake failure (invalid security layer)");
+
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+ sec_layer &= GSSAUTH_P_NONE; /* We do not support a security layer */
+
+ /* Process the maximum message size the server can receive */
+ if(max_size > 0) {
+ /* The server has told us it supports a maximum receive buffer, however, as
+ we don't require one unless we are encrypting data, we tell the server
+ our receive buffer is zero. */
+ max_size = 0;
+ }
+
+ /* Allocate our message */
+ messagelen = 4;
+ if(authzid)
+ messagelen += strlen(authzid);
+ message = malloc(messagelen);
+ if(!message)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Populate the message with the security layer and client supported receive
+ message size. */
+ message[0] = sec_layer & 0xFF;
+ message[1] = (max_size >> 16) & 0xFF;
+ message[2] = (max_size >> 8) & 0xFF;
+ message[3] = max_size & 0xFF;
+
+ /* If given, append the authorization identity. */
+
+ if(authzid && *authzid)
+ memcpy(message + 4, authzid, messagelen - 4);
+
+ /* Setup the "authentication data" security buffer */
+ input_token.value = message;
+ input_token.length = messagelen;
+
+ /* Encrypt the data */
+ major_status = gss_wrap(&minor_status, krb5->context, 0,
+ GSS_C_QOP_DEFAULT, &input_token, NULL,
+ &output_token);
+ if(GSS_ERROR(major_status)) {
+ Curl_gss_log_error(data, "gss_wrap() failed: ",
+ major_status, minor_status);
+ free(message);
+ return CURLE_AUTH_ERROR;
+ }
+
+ /* Return the response. */
+ result = Curl_bufref_memdup(out, output_token.value, output_token.length);
+ /* Free the output buffer */
+ gss_release_buffer(&unused_status, &output_token);
+
+ /* Free the message buffer */
+ free(message);
+
+ return result;
+}
+
+/*
+ * Curl_auth_cleanup_gssapi()
+ *
+ * This is used to clean up the GSSAPI (Kerberos V5) specific data.
+ *
+ * Parameters:
+ *
+ * krb5 [in/out] - The Kerberos 5 data struct being cleaned up.
+ *
+ */
+void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5)
+{
+ OM_uint32 minor_status;
+
+ /* Free our security context */
+ if(krb5->context != GSS_C_NO_CONTEXT) {
+ gss_delete_sec_context(&minor_status, &krb5->context, GSS_C_NO_BUFFER);
+ krb5->context = GSS_C_NO_CONTEXT;
+ }
+
+ /* Free the SPN */
+ if(krb5->spn != GSS_C_NO_NAME) {
+ gss_release_name(&minor_status, &krb5->spn);
+ krb5->spn = GSS_C_NO_NAME;
+ }
+}
+
+#endif /* HAVE_GSSAPI && USE_KERBEROS5 */
diff --git a/libs/libcurl/src/vauth/krb5_sspi.c b/libs/libcurl/src/vauth/krb5_sspi.c index 895b4a1937..44add314be 100644 --- a/libs/libcurl/src/vauth/krb5_sspi.c +++ b/libs/libcurl/src/vauth/krb5_sspi.c @@ -1,474 +1,474 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2014 - 2022, Steve Holme, <steve_holme@hotmail.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 - * - * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if defined(USE_WINDOWS_SSPI) && defined(USE_KERBEROS5) - -#include <curl/curl.h> - -#include "vauth/vauth.h" -#include "urldata.h" -#include "warnless.h" -#include "curl_multibyte.h" -#include "sendf.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* - * Curl_auth_is_gssapi_supported() - * - * This is used to evaluate if GSSAPI (Kerberos V5) is supported. - * - * Parameters: None - * - * Returns TRUE if Kerberos V5 is supported by Windows SSPI. - */ -bool Curl_auth_is_gssapi_supported(void) -{ - PSecPkgInfo SecurityPackage; - SECURITY_STATUS status; - - /* Query the security package for Kerberos */ - status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) - TEXT(SP_NAME_KERBEROS), - &SecurityPackage); - - /* Release the package buffer as it is not required anymore */ - if(status == SEC_E_OK) { - s_pSecFn->FreeContextBuffer(SecurityPackage); - } - - return (status == SEC_E_OK ? TRUE : FALSE); -} - -/* - * Curl_auth_create_gssapi_user_message() - * - * This is used to generate an already encoded GSSAPI (Kerberos V5) user token - * message ready for sending to the recipient. - * - * Parameters: - * - * data [in] - The session handle. - * userp [in] - The user name in the format User or Domain\User. - * passwdp [in] - The user's password. - * service [in] - The service type such as http, smtp, pop or imap. - * host [in] - The host name. - * mutual_auth [in] - Flag specifying whether or not mutual authentication - * is enabled. - * chlg [in] - Optional challenge message. - * krb5 [in/out] - The Kerberos 5 data struct being used and modified. - * out [out] - The result storage. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const char *service, - const char *host, - const bool mutual_auth, - const struct bufref *chlg, - struct kerberos5data *krb5, - struct bufref *out) -{ - CURLcode result = CURLE_OK; - CtxtHandle context; - PSecPkgInfo SecurityPackage; - SecBuffer chlg_buf; - SecBuffer resp_buf; - SecBufferDesc chlg_desc; - SecBufferDesc resp_desc; - SECURITY_STATUS status; - unsigned long attrs; - TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ - - if(!krb5->spn) { - /* Generate our SPN */ - krb5->spn = Curl_auth_build_spn(service, host, NULL); - if(!krb5->spn) - return CURLE_OUT_OF_MEMORY; - } - - if(!krb5->output_token) { - /* Query the security package for Kerberos */ - status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) - TEXT(SP_NAME_KERBEROS), - &SecurityPackage); - if(status != SEC_E_OK) { - failf(data, "SSPI: couldn't get auth info"); - return CURLE_AUTH_ERROR; - } - - krb5->token_max = SecurityPackage->cbMaxToken; - - /* Release the package buffer as it is not required anymore */ - s_pSecFn->FreeContextBuffer(SecurityPackage); - - /* Allocate our response buffer */ - krb5->output_token = malloc(krb5->token_max); - if(!krb5->output_token) - return CURLE_OUT_OF_MEMORY; - } - - if(!krb5->credentials) { - /* Do we have credentials to use or are we using single sign-on? */ - if(userp && *userp) { - /* Populate our identity structure */ - result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity); - if(result) - return result; - - /* Allow proper cleanup of the identity structure */ - krb5->p_identity = &krb5->identity; - } - else - /* Use the current Windows user */ - krb5->p_identity = NULL; - - /* Allocate our credentials handle */ - krb5->credentials = calloc(1, sizeof(CredHandle)); - if(!krb5->credentials) - return CURLE_OUT_OF_MEMORY; - - /* Acquire our credentials handle */ - status = s_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *) - TEXT(SP_NAME_KERBEROS), - SECPKG_CRED_OUTBOUND, NULL, - krb5->p_identity, NULL, NULL, - krb5->credentials, &expiry); - if(status != SEC_E_OK) - return CURLE_LOGIN_DENIED; - - /* Allocate our new context handle */ - krb5->context = calloc(1, sizeof(CtxtHandle)); - if(!krb5->context) - return CURLE_OUT_OF_MEMORY; - } - - if(chlg) { - if(!Curl_bufref_len(chlg)) { - infof(data, "GSSAPI handshake failure (empty challenge message)"); - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Setup the challenge "input" security buffer */ - chlg_desc.ulVersion = SECBUFFER_VERSION; - chlg_desc.cBuffers = 1; - chlg_desc.pBuffers = &chlg_buf; - chlg_buf.BufferType = SECBUFFER_TOKEN; - chlg_buf.pvBuffer = (void *) Curl_bufref_ptr(chlg); - chlg_buf.cbBuffer = curlx_uztoul(Curl_bufref_len(chlg)); - } - - /* Setup the response "output" security buffer */ - resp_desc.ulVersion = SECBUFFER_VERSION; - resp_desc.cBuffers = 1; - resp_desc.pBuffers = &resp_buf; - resp_buf.BufferType = SECBUFFER_TOKEN; - resp_buf.pvBuffer = krb5->output_token; - resp_buf.cbBuffer = curlx_uztoul(krb5->token_max); - - /* Generate our challenge-response message */ - status = s_pSecFn->InitializeSecurityContext(krb5->credentials, - chlg ? krb5->context : NULL, - krb5->spn, - (mutual_auth ? - ISC_REQ_MUTUAL_AUTH : 0), - 0, SECURITY_NATIVE_DREP, - chlg ? &chlg_desc : NULL, 0, - &context, - &resp_desc, &attrs, - &expiry); - - if(status == SEC_E_INSUFFICIENT_MEMORY) - return CURLE_OUT_OF_MEMORY; - - if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) - return CURLE_AUTH_ERROR; - - if(memcmp(&context, krb5->context, sizeof(context))) { - s_pSecFn->DeleteSecurityContext(krb5->context); - - memcpy(krb5->context, &context, sizeof(context)); - } - - if(resp_buf.cbBuffer) { - result = Curl_bufref_memdup(out, resp_buf.pvBuffer, resp_buf.cbBuffer); - } - else if(mutual_auth) - Curl_bufref_set(out, "", 0, NULL); - else - Curl_bufref_set(out, NULL, 0, NULL); - - return result; -} - -/* - * Curl_auth_create_gssapi_security_message() - * - * This is used to generate an already encoded GSSAPI (Kerberos V5) security - * token message ready for sending to the recipient. - * - * Parameters: - * - * data [in] - The session handle. - * authzid [in] - The authorization identity if some. - * chlg [in] - The optional challenge message. - * krb5 [in/out] - The Kerberos 5 data struct being used and modified. - * out [out] - The result storage. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, - const char *authzid, - const struct bufref *chlg, - struct kerberos5data *krb5, - struct bufref *out) -{ - size_t offset = 0; - size_t messagelen = 0; - size_t appdatalen = 0; - unsigned char *trailer = NULL; - unsigned char *message = NULL; - unsigned char *padding = NULL; - unsigned char *appdata = NULL; - SecBuffer input_buf[2]; - SecBuffer wrap_buf[3]; - SecBufferDesc input_desc; - SecBufferDesc wrap_desc; - unsigned char *indata; - unsigned long qop = 0; - unsigned long sec_layer = 0; - unsigned long max_size = 0; - SecPkgContext_Sizes sizes; - SECURITY_STATUS status; - -#if defined(CURL_DISABLE_VERBOSE_STRINGS) - (void) data; -#endif - - /* Ensure we have a valid challenge message */ - if(!Curl_bufref_len(chlg)) { - infof(data, "GSSAPI handshake failure (empty security message)"); - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Get our response size information */ - status = s_pSecFn->QueryContextAttributes(krb5->context, - SECPKG_ATTR_SIZES, - &sizes); - - if(status == SEC_E_INSUFFICIENT_MEMORY) - return CURLE_OUT_OF_MEMORY; - - if(status != SEC_E_OK) - return CURLE_AUTH_ERROR; - - /* Setup the "input" security buffer */ - input_desc.ulVersion = SECBUFFER_VERSION; - input_desc.cBuffers = 2; - input_desc.pBuffers = input_buf; - input_buf[0].BufferType = SECBUFFER_STREAM; - input_buf[0].pvBuffer = (void *) Curl_bufref_ptr(chlg); - input_buf[0].cbBuffer = curlx_uztoul(Curl_bufref_len(chlg)); - input_buf[1].BufferType = SECBUFFER_DATA; - input_buf[1].pvBuffer = NULL; - input_buf[1].cbBuffer = 0; - - /* Decrypt the inbound challenge and obtain the qop */ - status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop); - if(status != SEC_E_OK) { - infof(data, "GSSAPI handshake failure (empty security message)"); - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Not 4 octets long so fail as per RFC4752 Section 3.1 */ - if(input_buf[1].cbBuffer != 4) { - infof(data, "GSSAPI handshake failure (invalid security data)"); - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Extract the security layer and the maximum message size */ - indata = input_buf[1].pvBuffer; - sec_layer = indata[0]; - max_size = (indata[1] << 16) | (indata[2] << 8) | indata[3]; - - /* Free the challenge as it is not required anymore */ - s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer); - - /* Process the security layer */ - if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) { - infof(data, "GSSAPI handshake failure (invalid security layer)"); - return CURLE_BAD_CONTENT_ENCODING; - } - sec_layer &= KERB_WRAP_NO_ENCRYPT; /* We do not support a security layer */ - - /* Process the maximum message size the server can receive */ - if(max_size > 0) { - /* The server has told us it supports a maximum receive buffer, however, as - we don't require one unless we are encrypting data, we tell the server - our receive buffer is zero. */ - max_size = 0; - } - - /* Allocate the trailer */ - trailer = malloc(sizes.cbSecurityTrailer); - if(!trailer) - return CURLE_OUT_OF_MEMORY; - - /* Allocate our message */ - messagelen = 4; - if(authzid) - messagelen += strlen(authzid); - message = malloc(messagelen); - if(!message) { - free(trailer); - - return CURLE_OUT_OF_MEMORY; - } - - /* Populate the message with the security layer and client supported receive - message size. */ - message[0] = sec_layer & 0xFF; - message[1] = (max_size >> 16) & 0xFF; - message[2] = (max_size >> 8) & 0xFF; - message[3] = max_size & 0xFF; - - /* If given, append the authorization identity. */ - - if(authzid && *authzid) - memcpy(message + 4, authzid, messagelen - 4); - - /* Allocate the padding */ - padding = malloc(sizes.cbBlockSize); - if(!padding) { - free(message); - free(trailer); - - return CURLE_OUT_OF_MEMORY; - } - - /* Setup the "authentication data" security buffer */ - wrap_desc.ulVersion = SECBUFFER_VERSION; - wrap_desc.cBuffers = 3; - wrap_desc.pBuffers = wrap_buf; - wrap_buf[0].BufferType = SECBUFFER_TOKEN; - wrap_buf[0].pvBuffer = trailer; - wrap_buf[0].cbBuffer = sizes.cbSecurityTrailer; - wrap_buf[1].BufferType = SECBUFFER_DATA; - wrap_buf[1].pvBuffer = message; - wrap_buf[1].cbBuffer = curlx_uztoul(messagelen); - wrap_buf[2].BufferType = SECBUFFER_PADDING; - wrap_buf[2].pvBuffer = padding; - wrap_buf[2].cbBuffer = sizes.cbBlockSize; - - /* Encrypt the data */ - status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT, - &wrap_desc, 0); - if(status != SEC_E_OK) { - free(padding); - free(message); - free(trailer); - - if(status == SEC_E_INSUFFICIENT_MEMORY) - return CURLE_OUT_OF_MEMORY; - - return CURLE_AUTH_ERROR; - } - - /* Allocate the encryption (wrap) buffer */ - appdatalen = wrap_buf[0].cbBuffer + wrap_buf[1].cbBuffer + - wrap_buf[2].cbBuffer; - appdata = malloc(appdatalen); - if(!appdata) { - free(padding); - free(message); - free(trailer); - - return CURLE_OUT_OF_MEMORY; - } - - /* Populate the encryption buffer */ - memcpy(appdata, wrap_buf[0].pvBuffer, wrap_buf[0].cbBuffer); - offset += wrap_buf[0].cbBuffer; - memcpy(appdata + offset, wrap_buf[1].pvBuffer, wrap_buf[1].cbBuffer); - offset += wrap_buf[1].cbBuffer; - memcpy(appdata + offset, wrap_buf[2].pvBuffer, wrap_buf[2].cbBuffer); - - /* Free all of our local buffers */ - free(padding); - free(message); - free(trailer); - - /* Return the response. */ - Curl_bufref_set(out, appdata, appdatalen, curl_free); - return CURLE_OK; -} - -/* - * Curl_auth_cleanup_gssapi() - * - * This is used to clean up the GSSAPI (Kerberos V5) specific data. - * - * Parameters: - * - * krb5 [in/out] - The Kerberos 5 data struct being cleaned up. - * - */ -void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5) -{ - /* Free our security context */ - if(krb5->context) { - s_pSecFn->DeleteSecurityContext(krb5->context); - free(krb5->context); - krb5->context = NULL; - } - - /* Free our credentials handle */ - if(krb5->credentials) { - s_pSecFn->FreeCredentialsHandle(krb5->credentials); - free(krb5->credentials); - krb5->credentials = NULL; - } - - /* Free our identity */ - Curl_sspi_free_identity(krb5->p_identity); - krb5->p_identity = NULL; - - /* Free the SPN and output token */ - Curl_safefree(krb5->spn); - Curl_safefree(krb5->output_token); - - /* Reset any variables */ - krb5->token_max = 0; -} - -#endif /* USE_WINDOWS_SSPI && USE_KERBEROS5*/ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2014 - 2022, Steve Holme, <steve_holme@hotmail.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
+ *
+ * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if defined(USE_WINDOWS_SSPI) && defined(USE_KERBEROS5)
+
+#include <curl/curl.h>
+
+#include "vauth/vauth.h"
+#include "urldata.h"
+#include "warnless.h"
+#include "curl_multibyte.h"
+#include "sendf.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * Curl_auth_is_gssapi_supported()
+ *
+ * This is used to evaluate if GSSAPI (Kerberos V5) is supported.
+ *
+ * Parameters: None
+ *
+ * Returns TRUE if Kerberos V5 is supported by Windows SSPI.
+ */
+bool Curl_auth_is_gssapi_supported(void)
+{
+ PSecPkgInfo SecurityPackage;
+ SECURITY_STATUS status;
+
+ /* Query the security package for Kerberos */
+ status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
+ TEXT(SP_NAME_KERBEROS),
+ &SecurityPackage);
+
+ /* Release the package buffer as it is not required anymore */
+ if(status == SEC_E_OK) {
+ s_pSecFn->FreeContextBuffer(SecurityPackage);
+ }
+
+ return (status == SEC_E_OK ? TRUE : FALSE);
+}
+
+/*
+ * Curl_auth_create_gssapi_user_message()
+ *
+ * This is used to generate an already encoded GSSAPI (Kerberos V5) user token
+ * message ready for sending to the recipient.
+ *
+ * Parameters:
+ *
+ * data [in] - The session handle.
+ * userp [in] - The user name in the format User or Domain\User.
+ * passwdp [in] - The user's password.
+ * service [in] - The service type such as http, smtp, pop or imap.
+ * host [in] - The host name.
+ * mutual_auth [in] - Flag specifying whether or not mutual authentication
+ * is enabled.
+ * chlg [in] - Optional challenge message.
+ * krb5 [in/out] - The Kerberos 5 data struct being used and modified.
+ * out [out] - The result storage.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
+ const char *userp,
+ const char *passwdp,
+ const char *service,
+ const char *host,
+ const bool mutual_auth,
+ const struct bufref *chlg,
+ struct kerberos5data *krb5,
+ struct bufref *out)
+{
+ CURLcode result = CURLE_OK;
+ CtxtHandle context;
+ PSecPkgInfo SecurityPackage;
+ SecBuffer chlg_buf;
+ SecBuffer resp_buf;
+ SecBufferDesc chlg_desc;
+ SecBufferDesc resp_desc;
+ SECURITY_STATUS status;
+ unsigned long attrs;
+ TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
+
+ if(!krb5->spn) {
+ /* Generate our SPN */
+ krb5->spn = Curl_auth_build_spn(service, host, NULL);
+ if(!krb5->spn)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(!krb5->output_token) {
+ /* Query the security package for Kerberos */
+ status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
+ TEXT(SP_NAME_KERBEROS),
+ &SecurityPackage);
+ if(status != SEC_E_OK) {
+ failf(data, "SSPI: couldn't get auth info");
+ return CURLE_AUTH_ERROR;
+ }
+
+ krb5->token_max = SecurityPackage->cbMaxToken;
+
+ /* Release the package buffer as it is not required anymore */
+ s_pSecFn->FreeContextBuffer(SecurityPackage);
+
+ /* Allocate our response buffer */
+ krb5->output_token = malloc(krb5->token_max);
+ if(!krb5->output_token)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(!krb5->credentials) {
+ /* Do we have credentials to use or are we using single sign-on? */
+ if(userp && *userp) {
+ /* Populate our identity structure */
+ result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity);
+ if(result)
+ return result;
+
+ /* Allow proper cleanup of the identity structure */
+ krb5->p_identity = &krb5->identity;
+ }
+ else
+ /* Use the current Windows user */
+ krb5->p_identity = NULL;
+
+ /* Allocate our credentials handle */
+ krb5->credentials = calloc(1, sizeof(CredHandle));
+ if(!krb5->credentials)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Acquire our credentials handle */
+ status = s_pSecFn->AcquireCredentialsHandle(NULL,
+ (TCHAR *)
+ TEXT(SP_NAME_KERBEROS),
+ SECPKG_CRED_OUTBOUND, NULL,
+ krb5->p_identity, NULL, NULL,
+ krb5->credentials, &expiry);
+ if(status != SEC_E_OK)
+ return CURLE_LOGIN_DENIED;
+
+ /* Allocate our new context handle */
+ krb5->context = calloc(1, sizeof(CtxtHandle));
+ if(!krb5->context)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(chlg) {
+ if(!Curl_bufref_len(chlg)) {
+ infof(data, "GSSAPI handshake failure (empty challenge message)");
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+
+ /* Setup the challenge "input" security buffer */
+ chlg_desc.ulVersion = SECBUFFER_VERSION;
+ chlg_desc.cBuffers = 1;
+ chlg_desc.pBuffers = &chlg_buf;
+ chlg_buf.BufferType = SECBUFFER_TOKEN;
+ chlg_buf.pvBuffer = (void *) Curl_bufref_ptr(chlg);
+ chlg_buf.cbBuffer = curlx_uztoul(Curl_bufref_len(chlg));
+ }
+
+ /* Setup the response "output" security buffer */
+ resp_desc.ulVersion = SECBUFFER_VERSION;
+ resp_desc.cBuffers = 1;
+ resp_desc.pBuffers = &resp_buf;
+ resp_buf.BufferType = SECBUFFER_TOKEN;
+ resp_buf.pvBuffer = krb5->output_token;
+ resp_buf.cbBuffer = curlx_uztoul(krb5->token_max);
+
+ /* Generate our challenge-response message */
+ status = s_pSecFn->InitializeSecurityContext(krb5->credentials,
+ chlg ? krb5->context : NULL,
+ krb5->spn,
+ (mutual_auth ?
+ ISC_REQ_MUTUAL_AUTH : 0),
+ 0, SECURITY_NATIVE_DREP,
+ chlg ? &chlg_desc : NULL, 0,
+ &context,
+ &resp_desc, &attrs,
+ &expiry);
+
+ if(status == SEC_E_INSUFFICIENT_MEMORY)
+ return CURLE_OUT_OF_MEMORY;
+
+ if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED)
+ return CURLE_AUTH_ERROR;
+
+ if(memcmp(&context, krb5->context, sizeof(context))) {
+ s_pSecFn->DeleteSecurityContext(krb5->context);
+
+ memcpy(krb5->context, &context, sizeof(context));
+ }
+
+ if(resp_buf.cbBuffer) {
+ result = Curl_bufref_memdup(out, resp_buf.pvBuffer, resp_buf.cbBuffer);
+ }
+ else if(mutual_auth)
+ Curl_bufref_set(out, "", 0, NULL);
+ else
+ Curl_bufref_set(out, NULL, 0, NULL);
+
+ return result;
+}
+
+/*
+ * Curl_auth_create_gssapi_security_message()
+ *
+ * This is used to generate an already encoded GSSAPI (Kerberos V5) security
+ * token message ready for sending to the recipient.
+ *
+ * Parameters:
+ *
+ * data [in] - The session handle.
+ * authzid [in] - The authorization identity if some.
+ * chlg [in] - The optional challenge message.
+ * krb5 [in/out] - The Kerberos 5 data struct being used and modified.
+ * out [out] - The result storage.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
+ const char *authzid,
+ const struct bufref *chlg,
+ struct kerberos5data *krb5,
+ struct bufref *out)
+{
+ size_t offset = 0;
+ size_t messagelen = 0;
+ size_t appdatalen = 0;
+ unsigned char *trailer = NULL;
+ unsigned char *message = NULL;
+ unsigned char *padding = NULL;
+ unsigned char *appdata = NULL;
+ SecBuffer input_buf[2];
+ SecBuffer wrap_buf[3];
+ SecBufferDesc input_desc;
+ SecBufferDesc wrap_desc;
+ unsigned char *indata;
+ unsigned long qop = 0;
+ unsigned long sec_layer = 0;
+ unsigned long max_size = 0;
+ SecPkgContext_Sizes sizes;
+ SECURITY_STATUS status;
+
+#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+ (void) data;
+#endif
+
+ /* Ensure we have a valid challenge message */
+ if(!Curl_bufref_len(chlg)) {
+ infof(data, "GSSAPI handshake failure (empty security message)");
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+
+ /* Get our response size information */
+ status = s_pSecFn->QueryContextAttributes(krb5->context,
+ SECPKG_ATTR_SIZES,
+ &sizes);
+
+ if(status == SEC_E_INSUFFICIENT_MEMORY)
+ return CURLE_OUT_OF_MEMORY;
+
+ if(status != SEC_E_OK)
+ return CURLE_AUTH_ERROR;
+
+ /* Setup the "input" security buffer */
+ input_desc.ulVersion = SECBUFFER_VERSION;
+ input_desc.cBuffers = 2;
+ input_desc.pBuffers = input_buf;
+ input_buf[0].BufferType = SECBUFFER_STREAM;
+ input_buf[0].pvBuffer = (void *) Curl_bufref_ptr(chlg);
+ input_buf[0].cbBuffer = curlx_uztoul(Curl_bufref_len(chlg));
+ input_buf[1].BufferType = SECBUFFER_DATA;
+ input_buf[1].pvBuffer = NULL;
+ input_buf[1].cbBuffer = 0;
+
+ /* Decrypt the inbound challenge and obtain the qop */
+ status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop);
+ if(status != SEC_E_OK) {
+ infof(data, "GSSAPI handshake failure (empty security message)");
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+
+ /* Not 4 octets long so fail as per RFC4752 Section 3.1 */
+ if(input_buf[1].cbBuffer != 4) {
+ infof(data, "GSSAPI handshake failure (invalid security data)");
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+
+ /* Extract the security layer and the maximum message size */
+ indata = input_buf[1].pvBuffer;
+ sec_layer = indata[0];
+ max_size = (indata[1] << 16) | (indata[2] << 8) | indata[3];
+
+ /* Free the challenge as it is not required anymore */
+ s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer);
+
+ /* Process the security layer */
+ if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) {
+ infof(data, "GSSAPI handshake failure (invalid security layer)");
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+ sec_layer &= KERB_WRAP_NO_ENCRYPT; /* We do not support a security layer */
+
+ /* Process the maximum message size the server can receive */
+ if(max_size > 0) {
+ /* The server has told us it supports a maximum receive buffer, however, as
+ we don't require one unless we are encrypting data, we tell the server
+ our receive buffer is zero. */
+ max_size = 0;
+ }
+
+ /* Allocate the trailer */
+ trailer = malloc(sizes.cbSecurityTrailer);
+ if(!trailer)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Allocate our message */
+ messagelen = 4;
+ if(authzid)
+ messagelen += strlen(authzid);
+ message = malloc(messagelen);
+ if(!message) {
+ free(trailer);
+
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* Populate the message with the security layer and client supported receive
+ message size. */
+ message[0] = sec_layer & 0xFF;
+ message[1] = (max_size >> 16) & 0xFF;
+ message[2] = (max_size >> 8) & 0xFF;
+ message[3] = max_size & 0xFF;
+
+ /* If given, append the authorization identity. */
+
+ if(authzid && *authzid)
+ memcpy(message + 4, authzid, messagelen - 4);
+
+ /* Allocate the padding */
+ padding = malloc(sizes.cbBlockSize);
+ if(!padding) {
+ free(message);
+ free(trailer);
+
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* Setup the "authentication data" security buffer */
+ wrap_desc.ulVersion = SECBUFFER_VERSION;
+ wrap_desc.cBuffers = 3;
+ wrap_desc.pBuffers = wrap_buf;
+ wrap_buf[0].BufferType = SECBUFFER_TOKEN;
+ wrap_buf[0].pvBuffer = trailer;
+ wrap_buf[0].cbBuffer = sizes.cbSecurityTrailer;
+ wrap_buf[1].BufferType = SECBUFFER_DATA;
+ wrap_buf[1].pvBuffer = message;
+ wrap_buf[1].cbBuffer = curlx_uztoul(messagelen);
+ wrap_buf[2].BufferType = SECBUFFER_PADDING;
+ wrap_buf[2].pvBuffer = padding;
+ wrap_buf[2].cbBuffer = sizes.cbBlockSize;
+
+ /* Encrypt the data */
+ status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT,
+ &wrap_desc, 0);
+ if(status != SEC_E_OK) {
+ free(padding);
+ free(message);
+ free(trailer);
+
+ if(status == SEC_E_INSUFFICIENT_MEMORY)
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_AUTH_ERROR;
+ }
+
+ /* Allocate the encryption (wrap) buffer */
+ appdatalen = wrap_buf[0].cbBuffer + wrap_buf[1].cbBuffer +
+ wrap_buf[2].cbBuffer;
+ appdata = malloc(appdatalen);
+ if(!appdata) {
+ free(padding);
+ free(message);
+ free(trailer);
+
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* Populate the encryption buffer */
+ memcpy(appdata, wrap_buf[0].pvBuffer, wrap_buf[0].cbBuffer);
+ offset += wrap_buf[0].cbBuffer;
+ memcpy(appdata + offset, wrap_buf[1].pvBuffer, wrap_buf[1].cbBuffer);
+ offset += wrap_buf[1].cbBuffer;
+ memcpy(appdata + offset, wrap_buf[2].pvBuffer, wrap_buf[2].cbBuffer);
+
+ /* Free all of our local buffers */
+ free(padding);
+ free(message);
+ free(trailer);
+
+ /* Return the response. */
+ Curl_bufref_set(out, appdata, appdatalen, curl_free);
+ return CURLE_OK;
+}
+
+/*
+ * Curl_auth_cleanup_gssapi()
+ *
+ * This is used to clean up the GSSAPI (Kerberos V5) specific data.
+ *
+ * Parameters:
+ *
+ * krb5 [in/out] - The Kerberos 5 data struct being cleaned up.
+ *
+ */
+void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5)
+{
+ /* Free our security context */
+ if(krb5->context) {
+ s_pSecFn->DeleteSecurityContext(krb5->context);
+ free(krb5->context);
+ krb5->context = NULL;
+ }
+
+ /* Free our credentials handle */
+ if(krb5->credentials) {
+ s_pSecFn->FreeCredentialsHandle(krb5->credentials);
+ free(krb5->credentials);
+ krb5->credentials = NULL;
+ }
+
+ /* Free our identity */
+ Curl_sspi_free_identity(krb5->p_identity);
+ krb5->p_identity = NULL;
+
+ /* Free the SPN and output token */
+ Curl_safefree(krb5->spn);
+ Curl_safefree(krb5->output_token);
+
+ /* Reset any variables */
+ krb5->token_max = 0;
+}
+
+#endif /* USE_WINDOWS_SSPI && USE_KERBEROS5 */
diff --git a/libs/libcurl/src/vauth/ntlm.c b/libs/libcurl/src/vauth/ntlm.c index c10fa6caaf..a93ccf6410 100644 --- a/libs/libcurl/src/vauth/ntlm.c +++ b/libs/libcurl/src/vauth/ntlm.c @@ -1,791 +1,789 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_NTLM) && !defined(USE_WINDOWS_SSPI) - -/* - * NTLM details: - * - * https://davenport.sourceforge.net/ntlm.html - * https://www.innovation.ch/java/ntlm.html - */ - -#define DEBUG_ME 0 - -#include "urldata.h" -#include "sendf.h" -#include "curl_ntlm_core.h" -#include "curl_gethostname.h" -#include "curl_multibyte.h" -#include "curl_md5.h" -#include "warnless.h" -#include "rand.h" -#include "vtls/vtls.h" - -/* SSL backend-specific #if branches in this file must be kept in the order - documented in curl_ntlm_core. */ -#if defined(NTLM_NEEDS_NSS_INIT) -#include "vtls/nssg.h" /* for Curl_nss_force_init() */ -#endif - -#define BUILDING_CURL_NTLM_MSGS_C -#include "vauth/vauth.h" -#include "vauth/ntlm.h" -#include "curl_endian.h" -#include "curl_printf.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* "NTLMSSP" signature is always in ASCII regardless of the platform */ -#define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50" - -/* The fixed host name we provide, in order to not leak our real local host - name. Copy the name used by Firefox. */ -#define NTLM_HOSTNAME "WORKSTATION" - -#if DEBUG_ME -# define DEBUG_OUT(x) x -static void ntlm_print_flags(FILE *handle, unsigned long flags) -{ - if(flags & NTLMFLAG_NEGOTIATE_UNICODE) - fprintf(handle, "NTLMFLAG_NEGOTIATE_UNICODE "); - if(flags & NTLMFLAG_NEGOTIATE_OEM) - fprintf(handle, "NTLMFLAG_NEGOTIATE_OEM "); - if(flags & NTLMFLAG_REQUEST_TARGET) - fprintf(handle, "NTLMFLAG_REQUEST_TARGET "); - if(flags & (1<<3)) - fprintf(handle, "NTLMFLAG_UNKNOWN_3 "); - if(flags & NTLMFLAG_NEGOTIATE_SIGN) - fprintf(handle, "NTLMFLAG_NEGOTIATE_SIGN "); - if(flags & NTLMFLAG_NEGOTIATE_SEAL) - fprintf(handle, "NTLMFLAG_NEGOTIATE_SEAL "); - if(flags & NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE) - fprintf(handle, "NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE "); - if(flags & NTLMFLAG_NEGOTIATE_LM_KEY) - fprintf(handle, "NTLMFLAG_NEGOTIATE_LM_KEY "); - if(flags & NTLMFLAG_NEGOTIATE_NETWARE) - fprintf(handle, "NTLMFLAG_NEGOTIATE_NETWARE "); - if(flags & NTLMFLAG_NEGOTIATE_NTLM_KEY) - fprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM_KEY "); - if(flags & (1<<10)) - fprintf(handle, "NTLMFLAG_UNKNOWN_10 "); - if(flags & NTLMFLAG_NEGOTIATE_ANONYMOUS) - fprintf(handle, "NTLMFLAG_NEGOTIATE_ANONYMOUS "); - if(flags & NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED) - fprintf(handle, "NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED "); - if(flags & NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED) - fprintf(handle, "NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED "); - if(flags & NTLMFLAG_NEGOTIATE_LOCAL_CALL) - fprintf(handle, "NTLMFLAG_NEGOTIATE_LOCAL_CALL "); - if(flags & NTLMFLAG_NEGOTIATE_ALWAYS_SIGN) - fprintf(handle, "NTLMFLAG_NEGOTIATE_ALWAYS_SIGN "); - if(flags & NTLMFLAG_TARGET_TYPE_DOMAIN) - fprintf(handle, "NTLMFLAG_TARGET_TYPE_DOMAIN "); - if(flags & NTLMFLAG_TARGET_TYPE_SERVER) - fprintf(handle, "NTLMFLAG_TARGET_TYPE_SERVER "); - if(flags & NTLMFLAG_TARGET_TYPE_SHARE) - fprintf(handle, "NTLMFLAG_TARGET_TYPE_SHARE "); - if(flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) - fprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM2_KEY "); - if(flags & NTLMFLAG_REQUEST_INIT_RESPONSE) - fprintf(handle, "NTLMFLAG_REQUEST_INIT_RESPONSE "); - if(flags & NTLMFLAG_REQUEST_ACCEPT_RESPONSE) - fprintf(handle, "NTLMFLAG_REQUEST_ACCEPT_RESPONSE "); - if(flags & NTLMFLAG_REQUEST_NONNT_SESSION_KEY) - fprintf(handle, "NTLMFLAG_REQUEST_NONNT_SESSION_KEY "); - if(flags & NTLMFLAG_NEGOTIATE_TARGET_INFO) - fprintf(handle, "NTLMFLAG_NEGOTIATE_TARGET_INFO "); - if(flags & (1<<24)) - fprintf(handle, "NTLMFLAG_UNKNOWN_24 "); - if(flags & (1<<25)) - fprintf(handle, "NTLMFLAG_UNKNOWN_25 "); - if(flags & (1<<26)) - fprintf(handle, "NTLMFLAG_UNKNOWN_26 "); - if(flags & (1<<27)) - fprintf(handle, "NTLMFLAG_UNKNOWN_27 "); - if(flags & (1<<28)) - fprintf(handle, "NTLMFLAG_UNKNOWN_28 "); - if(flags & NTLMFLAG_NEGOTIATE_128) - fprintf(handle, "NTLMFLAG_NEGOTIATE_128 "); - if(flags & NTLMFLAG_NEGOTIATE_KEY_EXCHANGE) - fprintf(handle, "NTLMFLAG_NEGOTIATE_KEY_EXCHANGE "); - if(flags & NTLMFLAG_NEGOTIATE_56) - fprintf(handle, "NTLMFLAG_NEGOTIATE_56 "); -} - -static void ntlm_print_hex(FILE *handle, const char *buf, size_t len) -{ - const char *p = buf; - - (void) handle; - - fprintf(stderr, "0x"); - while(len-- > 0) - fprintf(stderr, "%02.2x", (unsigned int)*p++); -} -#else -# define DEBUG_OUT(x) Curl_nop_stmt -#endif - -/* - * ntlm_decode_type2_target() - * - * This is used to decode the "target info" in the NTLM type-2 message - * received. - * - * Parameters: - * - * data [in] - The session handle. - * type2ref [in] - The type-2 message. - * ntlm [in/out] - The NTLM data struct being used and modified. - * - * Returns CURLE_OK on success. - */ -static CURLcode ntlm_decode_type2_target(struct Curl_easy *data, - const struct bufref *type2ref, - struct ntlmdata *ntlm) -{ - unsigned short target_info_len = 0; - unsigned int target_info_offset = 0; - const unsigned char *type2 = Curl_bufref_ptr(type2ref); - size_t type2len = Curl_bufref_len(type2ref); - -#if defined(CURL_DISABLE_VERBOSE_STRINGS) - (void) data; -#endif - - if(type2len >= 48) { - target_info_len = Curl_read16_le(&type2[40]); - target_info_offset = Curl_read32_le(&type2[44]); - if(target_info_len > 0) { - if((target_info_offset > type2len) || - (target_info_offset + target_info_len) > type2len || - target_info_offset < 48) { - infof(data, "NTLM handshake failure (bad type-2 message). " - "Target Info Offset Len is set incorrect by the peer"); - return CURLE_BAD_CONTENT_ENCODING; - } - - free(ntlm->target_info); /* replace any previous data */ - ntlm->target_info = malloc(target_info_len); - if(!ntlm->target_info) - return CURLE_OUT_OF_MEMORY; - - memcpy(ntlm->target_info, &type2[target_info_offset], target_info_len); - } - } - - ntlm->target_info_len = target_info_len; - - return CURLE_OK; -} - -/* - NTLM message structure notes: - - A 'short' is a 'network short', a little-endian 16-bit unsigned value. - - A 'long' is a 'network long', a little-endian, 32-bit unsigned value. - - A 'security buffer' represents a triplet used to point to a buffer, - consisting of two shorts and one long: - - 1. A 'short' containing the length of the buffer content in bytes. - 2. A 'short' containing the allocated space for the buffer in bytes. - 3. A 'long' containing the offset to the start of the buffer in bytes, - from the beginning of the NTLM message. -*/ - -/* - * Curl_auth_is_ntlm_supported() - * - * This is used to evaluate if NTLM is supported. - * - * Parameters: None - * - * Returns TRUE as NTLM as handled by libcurl. - */ -bool Curl_auth_is_ntlm_supported(void) -{ - return TRUE; -} - -/* - * Curl_auth_decode_ntlm_type2_message() - * - * This is used to decode an NTLM type-2 message. The raw NTLM message is - * checked * for validity before the appropriate data for creating a type-3 - * message is * written to the given NTLM data structure. - * - * Parameters: - * - * data [in] - The session handle. - * type2ref [in] - The type-2 message. - * ntlm [in/out] - The NTLM data struct being used and modified. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, - const struct bufref *type2ref, - struct ntlmdata *ntlm) -{ - static const char type2_marker[] = { 0x02, 0x00, 0x00, 0x00 }; - - /* NTLM type-2 message structure: - - Index Description Content - 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP" - (0x4e544c4d53535000) - 8 NTLM Message Type long (0x02000000) - 12 Target Name security buffer - 20 Flags long - 24 Challenge 8 bytes - (32) Context 8 bytes (two consecutive longs) (*) - (40) Target Information security buffer (*) - (48) OS Version Structure 8 bytes (*) - 32 (48) (56) Start of data block (*) - (*) -> Optional - */ - - CURLcode result = CURLE_OK; - const unsigned char *type2 = Curl_bufref_ptr(type2ref); - size_t type2len = Curl_bufref_len(type2ref); - -#if defined(NTLM_NEEDS_NSS_INIT) - /* Make sure the crypto backend is initialized */ - result = Curl_nss_force_init(data); - if(result) - return result; -#elif defined(CURL_DISABLE_VERBOSE_STRINGS) - (void)data; -#endif - - ntlm->flags = 0; - - if((type2len < 32) || - (memcmp(type2, NTLMSSP_SIGNATURE, 8) != 0) || - (memcmp(type2 + 8, type2_marker, sizeof(type2_marker)) != 0)) { - /* This was not a good enough type-2 message */ - infof(data, "NTLM handshake failure (bad type-2 message)"); - return CURLE_BAD_CONTENT_ENCODING; - } - - ntlm->flags = Curl_read32_le(&type2[20]); - memcpy(ntlm->nonce, &type2[24], 8); - - if(ntlm->flags & NTLMFLAG_NEGOTIATE_TARGET_INFO) { - result = ntlm_decode_type2_target(data, type2ref, ntlm); - if(result) { - infof(data, "NTLM handshake failure (bad type-2 message)"); - return result; - } - } - - DEBUG_OUT({ - fprintf(stderr, "**** TYPE2 header flags=0x%08.8lx ", ntlm->flags); - ntlm_print_flags(stderr, ntlm->flags); - fprintf(stderr, "\n nonce="); - ntlm_print_hex(stderr, (char *)ntlm->nonce, 8); - fprintf(stderr, "\n****\n"); - fprintf(stderr, "**** Header %s\n ", header); - }); - - return result; -} - -/* copy the source to the destination and fill in zeroes in every - other destination byte! */ -static void unicodecpy(unsigned char *dest, const char *src, size_t length) -{ - size_t i; - for(i = 0; i < length; i++) { - dest[2 * i] = (unsigned char)src[i]; - dest[2 * i + 1] = '\0'; - } -} - -/* - * Curl_auth_create_ntlm_type1_message() - * - * This is used to generate an NTLM type-1 message ready for sending to the - * recipient using the appropriate compile time crypto API. - * - * Parameters: - * - * data [in] - The session handle. - * userp [in] - The user name in the format User or Domain\User. - * passwdp [in] - The user's password. - * service [in] - The service type such as http, smtp, pop or imap. - * host [in] - The host name. - * ntlm [in/out] - The NTLM data struct being used and modified. - * out [out] - The result storage. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const char *service, - const char *hostname, - struct ntlmdata *ntlm, - struct bufref *out) -{ - /* NTLM type-1 message structure: - - Index Description Content - 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP" - (0x4e544c4d53535000) - 8 NTLM Message Type long (0x01000000) - 12 Flags long - (16) Supplied Domain security buffer (*) - (24) Supplied Workstation security buffer (*) - (32) OS Version Structure 8 bytes (*) - (32) (40) Start of data block (*) - (*) -> Optional - */ - - size_t size; - - char *ntlmbuf; - const char *host = ""; /* empty */ - const char *domain = ""; /* empty */ - size_t hostlen = 0; - size_t domlen = 0; - size_t hostoff = 0; - size_t domoff = hostoff + hostlen; /* This is 0: remember that host and - domain are empty */ - (void)data; - (void)userp; - (void)passwdp; - (void)service, - (void)hostname, - - /* Clean up any former leftovers and initialise to defaults */ - Curl_auth_cleanup_ntlm(ntlm); - - ntlmbuf = aprintf(NTLMSSP_SIGNATURE "%c" - "\x01%c%c%c" /* 32-bit type = 1 */ - "%c%c%c%c" /* 32-bit NTLM flag field */ - "%c%c" /* domain length */ - "%c%c" /* domain allocated space */ - "%c%c" /* domain name offset */ - "%c%c" /* 2 zeroes */ - "%c%c" /* host length */ - "%c%c" /* host allocated space */ - "%c%c" /* host name offset */ - "%c%c" /* 2 zeroes */ - "%s" /* host name */ - "%s", /* domain string */ - 0, /* trailing zero */ - 0, 0, 0, /* part of type-1 long */ - - LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM | - NTLMFLAG_REQUEST_TARGET | - NTLMFLAG_NEGOTIATE_NTLM_KEY | - NTLMFLAG_NEGOTIATE_NTLM2_KEY | - NTLMFLAG_NEGOTIATE_ALWAYS_SIGN), - SHORTPAIR(domlen), - SHORTPAIR(domlen), - SHORTPAIR(domoff), - 0, 0, - SHORTPAIR(hostlen), - SHORTPAIR(hostlen), - SHORTPAIR(hostoff), - 0, 0, - host, /* this is empty */ - domain /* this is empty */); - - if(!ntlmbuf) - return CURLE_OUT_OF_MEMORY; - - /* Initial packet length */ - size = 32 + hostlen + domlen; - - DEBUG_OUT({ - fprintf(stderr, "* TYPE1 header flags=0x%02.2x%02.2x%02.2x%02.2x " - "0x%08.8x ", - LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM | - NTLMFLAG_REQUEST_TARGET | - NTLMFLAG_NEGOTIATE_NTLM_KEY | - NTLMFLAG_NEGOTIATE_NTLM2_KEY | - NTLMFLAG_NEGOTIATE_ALWAYS_SIGN), - NTLMFLAG_NEGOTIATE_OEM | - NTLMFLAG_REQUEST_TARGET | - NTLMFLAG_NEGOTIATE_NTLM_KEY | - NTLMFLAG_NEGOTIATE_NTLM2_KEY | - NTLMFLAG_NEGOTIATE_ALWAYS_SIGN); - ntlm_print_flags(stderr, - NTLMFLAG_NEGOTIATE_OEM | - NTLMFLAG_REQUEST_TARGET | - NTLMFLAG_NEGOTIATE_NTLM_KEY | - NTLMFLAG_NEGOTIATE_NTLM2_KEY | - NTLMFLAG_NEGOTIATE_ALWAYS_SIGN); - fprintf(stderr, "\n****\n"); - }); - - Curl_bufref_set(out, ntlmbuf, size, curl_free); - return CURLE_OK; -} - -/* - * Curl_auth_create_ntlm_type3_message() - * - * This is used to generate an already encoded NTLM type-3 message ready for - * sending to the recipient using the appropriate compile time crypto API. - * - * Parameters: - * - * data [in] - The session handle. - * userp [in] - The user name in the format User or Domain\User. - * passwdp [in] - The user's password. - * ntlm [in/out] - The NTLM data struct being used and modified. - * out [out] - The result storage. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - struct ntlmdata *ntlm, - struct bufref *out) -{ - /* NTLM type-3 message structure: - - Index Description Content - 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP" - (0x4e544c4d53535000) - 8 NTLM Message Type long (0x03000000) - 12 LM/LMv2 Response security buffer - 20 NTLM/NTLMv2 Response security buffer - 28 Target Name security buffer - 36 User Name security buffer - 44 Workstation Name security buffer - (52) Session Key security buffer (*) - (60) Flags long (*) - (64) OS Version Structure 8 bytes (*) - 52 (64) (72) Start of data block - (*) -> Optional - */ - - CURLcode result = CURLE_OK; - size_t size; - unsigned char ntlmbuf[NTLM_BUFSIZE]; - int lmrespoff; - unsigned char lmresp[24]; /* fixed-size */ - int ntrespoff; - unsigned int ntresplen = 24; - unsigned char ntresp[24]; /* fixed-size */ - unsigned char *ptr_ntresp = &ntresp[0]; - unsigned char *ntlmv2resp = NULL; - bool unicode = (ntlm->flags & NTLMFLAG_NEGOTIATE_UNICODE) ? TRUE : FALSE; - char host[HOSTNAME_MAX + 1] = ""; - const char *user; - const char *domain = ""; - size_t hostoff = 0; - size_t useroff = 0; - size_t domoff = 0; - size_t hostlen = 0; - size_t userlen = 0; - size_t domlen = 0; - - user = strchr(userp, '\\'); - if(!user) - user = strchr(userp, '/'); - - if(user) { - domain = userp; - domlen = (user - domain); - user++; - } - else - user = userp; - - userlen = strlen(user); - -#ifndef NTLM_HOSTNAME - /* Get the machine's un-qualified host name as NTLM doesn't like the fully - qualified domain name */ - if(Curl_gethostname(host, sizeof(host))) { - infof(data, "gethostname() failed, continuing without"); - hostlen = 0; - } - else { - hostlen = strlen(host); - } -#else - (void)msnprintf(host, sizeof(host), "%s", NTLM_HOSTNAME); - hostlen = sizeof(NTLM_HOSTNAME)-1; -#endif - - if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) { - unsigned char ntbuffer[0x18]; - unsigned char entropy[8]; - unsigned char ntlmv2hash[0x18]; - - /* Full NTLM version 2 - Although this cannot be negotiated, it is used here if available, as - servers featuring extended security are likely supporting also - NTLMv2. */ - result = Curl_rand(data, entropy, 8); - if(result) - return result; - - result = Curl_ntlm_core_mk_nt_hash(passwdp, ntbuffer); - if(result) - return result; - - result = Curl_ntlm_core_mk_ntlmv2_hash(user, userlen, domain, domlen, - ntbuffer, ntlmv2hash); - if(result) - return result; - - /* LMv2 response */ - result = Curl_ntlm_core_mk_lmv2_resp(ntlmv2hash, entropy, - &ntlm->nonce[0], lmresp); - if(result) - return result; - - /* NTLMv2 response */ - result = Curl_ntlm_core_mk_ntlmv2_resp(ntlmv2hash, entropy, - ntlm, &ntlmv2resp, &ntresplen); - if(result) - return result; - - ptr_ntresp = ntlmv2resp; - } - else { - - unsigned char ntbuffer[0x18]; - unsigned char lmbuffer[0x18]; - - /* NTLM version 1 */ - - result = Curl_ntlm_core_mk_nt_hash(passwdp, ntbuffer); - if(result) - return result; - - Curl_ntlm_core_lm_resp(ntbuffer, &ntlm->nonce[0], ntresp); - - result = Curl_ntlm_core_mk_lm_hash(passwdp, lmbuffer); - if(result) - return result; - - Curl_ntlm_core_lm_resp(lmbuffer, &ntlm->nonce[0], lmresp); - ntlm->flags &= ~NTLMFLAG_NEGOTIATE_NTLM2_KEY; - - /* A safer but less compatible alternative is: - * Curl_ntlm_core_lm_resp(ntbuffer, &ntlm->nonce[0], lmresp); - * See https://davenport.sourceforge.net/ntlm.html#ntlmVersion2 */ - } - - if(unicode) { - domlen = domlen * 2; - userlen = userlen * 2; - hostlen = hostlen * 2; - } - - lmrespoff = 64; /* size of the message header */ - ntrespoff = lmrespoff + 0x18; - domoff = ntrespoff + ntresplen; - useroff = domoff + domlen; - hostoff = useroff + userlen; - - /* Create the big type-3 message binary blob */ - size = msnprintf((char *)ntlmbuf, NTLM_BUFSIZE, - NTLMSSP_SIGNATURE "%c" - "\x03%c%c%c" /* 32-bit type = 3 */ - - "%c%c" /* LanManager length */ - "%c%c" /* LanManager allocated space */ - "%c%c" /* LanManager offset */ - "%c%c" /* 2 zeroes */ - - "%c%c" /* NT-response length */ - "%c%c" /* NT-response allocated space */ - "%c%c" /* NT-response offset */ - "%c%c" /* 2 zeroes */ - - "%c%c" /* domain length */ - "%c%c" /* domain allocated space */ - "%c%c" /* domain name offset */ - "%c%c" /* 2 zeroes */ - - "%c%c" /* user length */ - "%c%c" /* user allocated space */ - "%c%c" /* user offset */ - "%c%c" /* 2 zeroes */ - - "%c%c" /* host length */ - "%c%c" /* host allocated space */ - "%c%c" /* host offset */ - "%c%c" /* 2 zeroes */ - - "%c%c" /* session key length (unknown purpose) */ - "%c%c" /* session key allocated space (unknown purpose) */ - "%c%c" /* session key offset (unknown purpose) */ - "%c%c" /* 2 zeroes */ - - "%c%c%c%c", /* flags */ - - /* domain string */ - /* user string */ - /* host string */ - /* LanManager response */ - /* NT response */ - - 0, /* null-termination */ - 0, 0, 0, /* type-3 long, the 24 upper bits */ - - SHORTPAIR(0x18), /* LanManager response length, twice */ - SHORTPAIR(0x18), - SHORTPAIR(lmrespoff), - 0x0, 0x0, - - SHORTPAIR(ntresplen), /* NT-response length, twice */ - SHORTPAIR(ntresplen), - SHORTPAIR(ntrespoff), - 0x0, 0x0, - - SHORTPAIR(domlen), - SHORTPAIR(domlen), - SHORTPAIR(domoff), - 0x0, 0x0, - - SHORTPAIR(userlen), - SHORTPAIR(userlen), - SHORTPAIR(useroff), - 0x0, 0x0, - - SHORTPAIR(hostlen), - SHORTPAIR(hostlen), - SHORTPAIR(hostoff), - 0x0, 0x0, - - 0x0, 0x0, - 0x0, 0x0, - 0x0, 0x0, - 0x0, 0x0, - - LONGQUARTET(ntlm->flags)); - - DEBUGASSERT(size == 64); - DEBUGASSERT(size == (size_t)lmrespoff); - - /* We append the binary hashes */ - if(size < (NTLM_BUFSIZE - 0x18)) { - memcpy(&ntlmbuf[size], lmresp, 0x18); - size += 0x18; - } - - DEBUG_OUT({ - fprintf(stderr, "**** TYPE3 header lmresp="); - ntlm_print_hex(stderr, (char *)&ntlmbuf[lmrespoff], 0x18); - }); - - /* ntresplen + size should not be risking an integer overflow here */ - if(ntresplen + size > sizeof(ntlmbuf)) { - failf(data, "incoming NTLM message too big"); - return CURLE_OUT_OF_MEMORY; - } - DEBUGASSERT(size == (size_t)ntrespoff); - memcpy(&ntlmbuf[size], ptr_ntresp, ntresplen); - size += ntresplen; - - DEBUG_OUT({ - fprintf(stderr, "\n ntresp="); - ntlm_print_hex(stderr, (char *)&ntlmbuf[ntrespoff], ntresplen); - }); - - free(ntlmv2resp);/* Free the dynamic buffer allocated for NTLMv2 */ - - DEBUG_OUT({ - fprintf(stderr, "\n flags=0x%02.2x%02.2x%02.2x%02.2x 0x%08.8x ", - LONGQUARTET(ntlm->flags), ntlm->flags); - ntlm_print_flags(stderr, ntlm->flags); - fprintf(stderr, "\n****\n"); - }); - - /* Make sure that the domain, user and host strings fit in the - buffer before we copy them there. */ - if(size + userlen + domlen + hostlen >= NTLM_BUFSIZE) { - failf(data, "user + domain + host name too big"); - return CURLE_OUT_OF_MEMORY; - } - - DEBUGASSERT(size == domoff); - if(unicode) - unicodecpy(&ntlmbuf[size], domain, domlen / 2); - else - memcpy(&ntlmbuf[size], domain, domlen); - - size += domlen; - - DEBUGASSERT(size == useroff); - if(unicode) - unicodecpy(&ntlmbuf[size], user, userlen / 2); - else - memcpy(&ntlmbuf[size], user, userlen); - - size += userlen; - - DEBUGASSERT(size == hostoff); - if(unicode) - unicodecpy(&ntlmbuf[size], host, hostlen / 2); - else - memcpy(&ntlmbuf[size], host, hostlen); - - size += hostlen; - - /* Return the binary blob. */ - result = Curl_bufref_memdup(out, ntlmbuf, size); - - Curl_auth_cleanup_ntlm(ntlm); - - return result; -} - -/* - * Curl_auth_cleanup_ntlm() - * - * This is used to clean up the NTLM specific data. - * - * Parameters: - * - * ntlm [in/out] - The NTLM data struct being cleaned up. - * - */ -void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm) -{ - /* Free the target info */ - Curl_safefree(ntlm->target_info); - - /* Reset any variables */ - ntlm->target_info_len = 0; -} - -#endif /* USE_NTLM && !USE_WINDOWS_SSPI */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_NTLM) && !defined(USE_WINDOWS_SSPI)
+
+/*
+ * NTLM details:
+ *
+ * https://davenport.sourceforge.net/ntlm.html
+ * https://www.innovation.ch/java/ntlm.html
+ */
+
+#define DEBUG_ME 0
+
+#include "urldata.h"
+#include "sendf.h"
+#include "curl_ntlm_core.h"
+#include "curl_gethostname.h"
+#include "curl_multibyte.h"
+#include "curl_md5.h"
+#include "warnless.h"
+#include "rand.h"
+#include "vtls/vtls.h"
+
+/* SSL backend-specific #if branches in this file must be kept in the order
+ documented in curl_ntlm_core. */
+#if defined(NTLM_NEEDS_NSS_INIT)
+#include "vtls/nssg.h" /* for Curl_nss_force_init() */
+#endif
+
+#define BUILDING_CURL_NTLM_MSGS_C
+#include "vauth/vauth.h"
+#include "vauth/ntlm.h"
+#include "curl_endian.h"
+#include "curl_printf.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* "NTLMSSP" signature is always in ASCII regardless of the platform */
+#define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50"
+
+/* The fixed host name we provide, in order to not leak our real local host
+ name. Copy the name used by Firefox. */
+#define NTLM_HOSTNAME "WORKSTATION"
+
+#if DEBUG_ME
+# define DEBUG_OUT(x) x
+static void ntlm_print_flags(FILE *handle, unsigned long flags)
+{
+ if(flags & NTLMFLAG_NEGOTIATE_UNICODE)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_UNICODE ");
+ if(flags & NTLMFLAG_NEGOTIATE_OEM)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_OEM ");
+ if(flags & NTLMFLAG_REQUEST_TARGET)
+ fprintf(handle, "NTLMFLAG_REQUEST_TARGET ");
+ if(flags & (1<<3))
+ fprintf(handle, "NTLMFLAG_UNKNOWN_3 ");
+ if(flags & NTLMFLAG_NEGOTIATE_SIGN)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_SIGN ");
+ if(flags & NTLMFLAG_NEGOTIATE_SEAL)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_SEAL ");
+ if(flags & NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE ");
+ if(flags & NTLMFLAG_NEGOTIATE_LM_KEY)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_LM_KEY ");
+ if(flags & NTLMFLAG_NEGOTIATE_NTLM_KEY)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM_KEY ");
+ if(flags & (1<<10))
+ fprintf(handle, "NTLMFLAG_UNKNOWN_10 ");
+ if(flags & NTLMFLAG_NEGOTIATE_ANONYMOUS)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_ANONYMOUS ");
+ if(flags & NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED ");
+ if(flags & NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED ");
+ if(flags & NTLMFLAG_NEGOTIATE_LOCAL_CALL)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_LOCAL_CALL ");
+ if(flags & NTLMFLAG_NEGOTIATE_ALWAYS_SIGN)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_ALWAYS_SIGN ");
+ if(flags & NTLMFLAG_TARGET_TYPE_DOMAIN)
+ fprintf(handle, "NTLMFLAG_TARGET_TYPE_DOMAIN ");
+ if(flags & NTLMFLAG_TARGET_TYPE_SERVER)
+ fprintf(handle, "NTLMFLAG_TARGET_TYPE_SERVER ");
+ if(flags & NTLMFLAG_TARGET_TYPE_SHARE)
+ fprintf(handle, "NTLMFLAG_TARGET_TYPE_SHARE ");
+ if(flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM2_KEY ");
+ if(flags & NTLMFLAG_REQUEST_INIT_RESPONSE)
+ fprintf(handle, "NTLMFLAG_REQUEST_INIT_RESPONSE ");
+ if(flags & NTLMFLAG_REQUEST_ACCEPT_RESPONSE)
+ fprintf(handle, "NTLMFLAG_REQUEST_ACCEPT_RESPONSE ");
+ if(flags & NTLMFLAG_REQUEST_NONNT_SESSION_KEY)
+ fprintf(handle, "NTLMFLAG_REQUEST_NONNT_SESSION_KEY ");
+ if(flags & NTLMFLAG_NEGOTIATE_TARGET_INFO)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_TARGET_INFO ");
+ if(flags & (1<<24))
+ fprintf(handle, "NTLMFLAG_UNKNOWN_24 ");
+ if(flags & (1<<25))
+ fprintf(handle, "NTLMFLAG_UNKNOWN_25 ");
+ if(flags & (1<<26))
+ fprintf(handle, "NTLMFLAG_UNKNOWN_26 ");
+ if(flags & (1<<27))
+ fprintf(handle, "NTLMFLAG_UNKNOWN_27 ");
+ if(flags & (1<<28))
+ fprintf(handle, "NTLMFLAG_UNKNOWN_28 ");
+ if(flags & NTLMFLAG_NEGOTIATE_128)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_128 ");
+ if(flags & NTLMFLAG_NEGOTIATE_KEY_EXCHANGE)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_KEY_EXCHANGE ");
+ if(flags & NTLMFLAG_NEGOTIATE_56)
+ fprintf(handle, "NTLMFLAG_NEGOTIATE_56 ");
+}
+
+static void ntlm_print_hex(FILE *handle, const char *buf, size_t len)
+{
+ const char *p = buf;
+
+ (void) handle;
+
+ fprintf(stderr, "0x");
+ while(len-- > 0)
+ fprintf(stderr, "%02.2x", (unsigned int)*p++);
+}
+#else
+# define DEBUG_OUT(x) Curl_nop_stmt
+#endif
+
+/*
+ * ntlm_decode_type2_target()
+ *
+ * This is used to decode the "target info" in the NTLM type-2 message
+ * received.
+ *
+ * Parameters:
+ *
+ * data [in] - The session handle.
+ * type2ref [in] - The type-2 message.
+ * ntlm [in/out] - The NTLM data struct being used and modified.
+ *
+ * Returns CURLE_OK on success.
+ */
+static CURLcode ntlm_decode_type2_target(struct Curl_easy *data,
+ const struct bufref *type2ref,
+ struct ntlmdata *ntlm)
+{
+ unsigned short target_info_len = 0;
+ unsigned int target_info_offset = 0;
+ const unsigned char *type2 = Curl_bufref_ptr(type2ref);
+ size_t type2len = Curl_bufref_len(type2ref);
+
+#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+ (void) data;
+#endif
+
+ if(type2len >= 48) {
+ target_info_len = Curl_read16_le(&type2[40]);
+ target_info_offset = Curl_read32_le(&type2[44]);
+ if(target_info_len > 0) {
+ if((target_info_offset > type2len) ||
+ (target_info_offset + target_info_len) > type2len ||
+ target_info_offset < 48) {
+ infof(data, "NTLM handshake failure (bad type-2 message). "
+ "Target Info Offset Len is set incorrect by the peer");
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+
+ free(ntlm->target_info); /* replace any previous data */
+ ntlm->target_info = malloc(target_info_len);
+ if(!ntlm->target_info)
+ return CURLE_OUT_OF_MEMORY;
+
+ memcpy(ntlm->target_info, &type2[target_info_offset], target_info_len);
+ }
+ }
+
+ ntlm->target_info_len = target_info_len;
+
+ return CURLE_OK;
+}
+
+/*
+ NTLM message structure notes:
+
+ A 'short' is a 'network short', a little-endian 16-bit unsigned value.
+
+ A 'long' is a 'network long', a little-endian, 32-bit unsigned value.
+
+ A 'security buffer' represents a triplet used to point to a buffer,
+ consisting of two shorts and one long:
+
+ 1. A 'short' containing the length of the buffer content in bytes.
+ 2. A 'short' containing the allocated space for the buffer in bytes.
+ 3. A 'long' containing the offset to the start of the buffer in bytes,
+ from the beginning of the NTLM message.
+*/
+
+/*
+ * Curl_auth_is_ntlm_supported()
+ *
+ * This is used to evaluate if NTLM is supported.
+ *
+ * Parameters: None
+ *
+ * Returns TRUE as NTLM as handled by libcurl.
+ */
+bool Curl_auth_is_ntlm_supported(void)
+{
+ return TRUE;
+}
+
+/*
+ * Curl_auth_decode_ntlm_type2_message()
+ *
+ * This is used to decode an NTLM type-2 message. The raw NTLM message is
+ * checked * for validity before the appropriate data for creating a type-3
+ * message is * written to the given NTLM data structure.
+ *
+ * Parameters:
+ *
+ * data [in] - The session handle.
+ * type2ref [in] - The type-2 message.
+ * ntlm [in/out] - The NTLM data struct being used and modified.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data,
+ const struct bufref *type2ref,
+ struct ntlmdata *ntlm)
+{
+ static const char type2_marker[] = { 0x02, 0x00, 0x00, 0x00 };
+
+ /* NTLM type-2 message structure:
+
+ Index Description Content
+ 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
+ (0x4e544c4d53535000)
+ 8 NTLM Message Type long (0x02000000)
+ 12 Target Name security buffer
+ 20 Flags long
+ 24 Challenge 8 bytes
+ (32) Context 8 bytes (two consecutive longs) (*)
+ (40) Target Information security buffer (*)
+ (48) OS Version Structure 8 bytes (*)
+ 32 (48) (56) Start of data block (*)
+ (*) -> Optional
+ */
+
+ CURLcode result = CURLE_OK;
+ const unsigned char *type2 = Curl_bufref_ptr(type2ref);
+ size_t type2len = Curl_bufref_len(type2ref);
+
+#if defined(NTLM_NEEDS_NSS_INIT)
+ /* Make sure the crypto backend is initialized */
+ result = Curl_nss_force_init(data);
+ if(result)
+ return result;
+#elif defined(CURL_DISABLE_VERBOSE_STRINGS)
+ (void)data;
+#endif
+
+ ntlm->flags = 0;
+
+ if((type2len < 32) ||
+ (memcmp(type2, NTLMSSP_SIGNATURE, 8) != 0) ||
+ (memcmp(type2 + 8, type2_marker, sizeof(type2_marker)) != 0)) {
+ /* This was not a good enough type-2 message */
+ infof(data, "NTLM handshake failure (bad type-2 message)");
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+
+ ntlm->flags = Curl_read32_le(&type2[20]);
+ memcpy(ntlm->nonce, &type2[24], 8);
+
+ if(ntlm->flags & NTLMFLAG_NEGOTIATE_TARGET_INFO) {
+ result = ntlm_decode_type2_target(data, type2ref, ntlm);
+ if(result) {
+ infof(data, "NTLM handshake failure (bad type-2 message)");
+ return result;
+ }
+ }
+
+ DEBUG_OUT({
+ fprintf(stderr, "**** TYPE2 header flags=0x%08.8lx ", ntlm->flags);
+ ntlm_print_flags(stderr, ntlm->flags);
+ fprintf(stderr, "\n nonce=");
+ ntlm_print_hex(stderr, (char *)ntlm->nonce, 8);
+ fprintf(stderr, "\n****\n");
+ fprintf(stderr, "**** Header %s\n ", header);
+ });
+
+ return result;
+}
+
+/* copy the source to the destination and fill in zeroes in every
+ other destination byte! */
+static void unicodecpy(unsigned char *dest, const char *src, size_t length)
+{
+ size_t i;
+ for(i = 0; i < length; i++) {
+ dest[2 * i] = (unsigned char)src[i];
+ dest[2 * i + 1] = '\0';
+ }
+}
+
+/*
+ * Curl_auth_create_ntlm_type1_message()
+ *
+ * This is used to generate an NTLM type-1 message ready for sending to the
+ * recipient using the appropriate compile time crypto API.
+ *
+ * Parameters:
+ *
+ * data [in] - The session handle.
+ * userp [in] - The user name in the format User or Domain\User.
+ * passwdp [in] - The user's password.
+ * service [in] - The service type such as http, smtp, pop or imap.
+ * host [in] - The host name.
+ * ntlm [in/out] - The NTLM data struct being used and modified.
+ * out [out] - The result storage.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
+ const char *userp,
+ const char *passwdp,
+ const char *service,
+ const char *hostname,
+ struct ntlmdata *ntlm,
+ struct bufref *out)
+{
+ /* NTLM type-1 message structure:
+
+ Index Description Content
+ 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
+ (0x4e544c4d53535000)
+ 8 NTLM Message Type long (0x01000000)
+ 12 Flags long
+ (16) Supplied Domain security buffer (*)
+ (24) Supplied Workstation security buffer (*)
+ (32) OS Version Structure 8 bytes (*)
+ (32) (40) Start of data block (*)
+ (*) -> Optional
+ */
+
+ size_t size;
+
+ char *ntlmbuf;
+ const char *host = ""; /* empty */
+ const char *domain = ""; /* empty */
+ size_t hostlen = 0;
+ size_t domlen = 0;
+ size_t hostoff = 0;
+ size_t domoff = hostoff + hostlen; /* This is 0: remember that host and
+ domain are empty */
+ (void)data;
+ (void)userp;
+ (void)passwdp;
+ (void)service,
+ (void)hostname,
+
+ /* Clean up any former leftovers and initialise to defaults */
+ Curl_auth_cleanup_ntlm(ntlm);
+
+ ntlmbuf = aprintf(NTLMSSP_SIGNATURE "%c"
+ "\x01%c%c%c" /* 32-bit type = 1 */
+ "%c%c%c%c" /* 32-bit NTLM flag field */
+ "%c%c" /* domain length */
+ "%c%c" /* domain allocated space */
+ "%c%c" /* domain name offset */
+ "%c%c" /* 2 zeroes */
+ "%c%c" /* host length */
+ "%c%c" /* host allocated space */
+ "%c%c" /* host name offset */
+ "%c%c" /* 2 zeroes */
+ "%s" /* host name */
+ "%s", /* domain string */
+ 0, /* trailing zero */
+ 0, 0, 0, /* part of type-1 long */
+
+ LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM |
+ NTLMFLAG_REQUEST_TARGET |
+ NTLMFLAG_NEGOTIATE_NTLM_KEY |
+ NTLMFLAG_NEGOTIATE_NTLM2_KEY |
+ NTLMFLAG_NEGOTIATE_ALWAYS_SIGN),
+ SHORTPAIR(domlen),
+ SHORTPAIR(domlen),
+ SHORTPAIR(domoff),
+ 0, 0,
+ SHORTPAIR(hostlen),
+ SHORTPAIR(hostlen),
+ SHORTPAIR(hostoff),
+ 0, 0,
+ host, /* this is empty */
+ domain /* this is empty */);
+
+ if(!ntlmbuf)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Initial packet length */
+ size = 32 + hostlen + domlen;
+
+ DEBUG_OUT({
+ fprintf(stderr, "* TYPE1 header flags=0x%02.2x%02.2x%02.2x%02.2x "
+ "0x%08.8x ",
+ LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM |
+ NTLMFLAG_REQUEST_TARGET |
+ NTLMFLAG_NEGOTIATE_NTLM_KEY |
+ NTLMFLAG_NEGOTIATE_NTLM2_KEY |
+ NTLMFLAG_NEGOTIATE_ALWAYS_SIGN),
+ NTLMFLAG_NEGOTIATE_OEM |
+ NTLMFLAG_REQUEST_TARGET |
+ NTLMFLAG_NEGOTIATE_NTLM_KEY |
+ NTLMFLAG_NEGOTIATE_NTLM2_KEY |
+ NTLMFLAG_NEGOTIATE_ALWAYS_SIGN);
+ ntlm_print_flags(stderr,
+ NTLMFLAG_NEGOTIATE_OEM |
+ NTLMFLAG_REQUEST_TARGET |
+ NTLMFLAG_NEGOTIATE_NTLM_KEY |
+ NTLMFLAG_NEGOTIATE_NTLM2_KEY |
+ NTLMFLAG_NEGOTIATE_ALWAYS_SIGN);
+ fprintf(stderr, "\n****\n");
+ });
+
+ Curl_bufref_set(out, ntlmbuf, size, curl_free);
+ return CURLE_OK;
+}
+
+/*
+ * Curl_auth_create_ntlm_type3_message()
+ *
+ * This is used to generate an already encoded NTLM type-3 message ready for
+ * sending to the recipient using the appropriate compile time crypto API.
+ *
+ * Parameters:
+ *
+ * data [in] - The session handle.
+ * userp [in] - The user name in the format User or Domain\User.
+ * passwdp [in] - The user's password.
+ * ntlm [in/out] - The NTLM data struct being used and modified.
+ * out [out] - The result storage.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
+ const char *userp,
+ const char *passwdp,
+ struct ntlmdata *ntlm,
+ struct bufref *out)
+{
+ /* NTLM type-3 message structure:
+
+ Index Description Content
+ 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
+ (0x4e544c4d53535000)
+ 8 NTLM Message Type long (0x03000000)
+ 12 LM/LMv2 Response security buffer
+ 20 NTLM/NTLMv2 Response security buffer
+ 28 Target Name security buffer
+ 36 User Name security buffer
+ 44 Workstation Name security buffer
+ (52) Session Key security buffer (*)
+ (60) Flags long (*)
+ (64) OS Version Structure 8 bytes (*)
+ 52 (64) (72) Start of data block
+ (*) -> Optional
+ */
+
+ CURLcode result = CURLE_OK;
+ size_t size;
+ unsigned char ntlmbuf[NTLM_BUFSIZE];
+ int lmrespoff;
+ unsigned char lmresp[24]; /* fixed-size */
+ int ntrespoff;
+ unsigned int ntresplen = 24;
+ unsigned char ntresp[24]; /* fixed-size */
+ unsigned char *ptr_ntresp = &ntresp[0];
+ unsigned char *ntlmv2resp = NULL;
+ bool unicode = (ntlm->flags & NTLMFLAG_NEGOTIATE_UNICODE) ? TRUE : FALSE;
+ char host[HOSTNAME_MAX + 1] = "";
+ const char *user;
+ const char *domain = "";
+ size_t hostoff = 0;
+ size_t useroff = 0;
+ size_t domoff = 0;
+ size_t hostlen = 0;
+ size_t userlen = 0;
+ size_t domlen = 0;
+
+ user = strchr(userp, '\\');
+ if(!user)
+ user = strchr(userp, '/');
+
+ if(user) {
+ domain = userp;
+ domlen = (user - domain);
+ user++;
+ }
+ else
+ user = userp;
+
+ userlen = strlen(user);
+
+#ifndef NTLM_HOSTNAME
+ /* Get the machine's un-qualified host name as NTLM doesn't like the fully
+ qualified domain name */
+ if(Curl_gethostname(host, sizeof(host))) {
+ infof(data, "gethostname() failed, continuing without");
+ hostlen = 0;
+ }
+ else {
+ hostlen = strlen(host);
+ }
+#else
+ (void)msnprintf(host, sizeof(host), "%s", NTLM_HOSTNAME);
+ hostlen = sizeof(NTLM_HOSTNAME)-1;
+#endif
+
+ if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) {
+ unsigned char ntbuffer[0x18];
+ unsigned char entropy[8];
+ unsigned char ntlmv2hash[0x18];
+
+ /* Full NTLM version 2
+ Although this cannot be negotiated, it is used here if available, as
+ servers featuring extended security are likely supporting also
+ NTLMv2. */
+ result = Curl_rand(data, entropy, 8);
+ if(result)
+ return result;
+
+ result = Curl_ntlm_core_mk_nt_hash(passwdp, ntbuffer);
+ if(result)
+ return result;
+
+ result = Curl_ntlm_core_mk_ntlmv2_hash(user, userlen, domain, domlen,
+ ntbuffer, ntlmv2hash);
+ if(result)
+ return result;
+
+ /* LMv2 response */
+ result = Curl_ntlm_core_mk_lmv2_resp(ntlmv2hash, entropy,
+ &ntlm->nonce[0], lmresp);
+ if(result)
+ return result;
+
+ /* NTLMv2 response */
+ result = Curl_ntlm_core_mk_ntlmv2_resp(ntlmv2hash, entropy,
+ ntlm, &ntlmv2resp, &ntresplen);
+ if(result)
+ return result;
+
+ ptr_ntresp = ntlmv2resp;
+ }
+ else {
+
+ unsigned char ntbuffer[0x18];
+ unsigned char lmbuffer[0x18];
+
+ /* NTLM version 1 */
+
+ result = Curl_ntlm_core_mk_nt_hash(passwdp, ntbuffer);
+ if(result)
+ return result;
+
+ Curl_ntlm_core_lm_resp(ntbuffer, &ntlm->nonce[0], ntresp);
+
+ result = Curl_ntlm_core_mk_lm_hash(passwdp, lmbuffer);
+ if(result)
+ return result;
+
+ Curl_ntlm_core_lm_resp(lmbuffer, &ntlm->nonce[0], lmresp);
+ ntlm->flags &= ~NTLMFLAG_NEGOTIATE_NTLM2_KEY;
+
+ /* A safer but less compatible alternative is:
+ * Curl_ntlm_core_lm_resp(ntbuffer, &ntlm->nonce[0], lmresp);
+ * See https://davenport.sourceforge.net/ntlm.html#ntlmVersion2 */
+ }
+
+ if(unicode) {
+ domlen = domlen * 2;
+ userlen = userlen * 2;
+ hostlen = hostlen * 2;
+ }
+
+ lmrespoff = 64; /* size of the message header */
+ ntrespoff = lmrespoff + 0x18;
+ domoff = ntrespoff + ntresplen;
+ useroff = domoff + domlen;
+ hostoff = useroff + userlen;
+
+ /* Create the big type-3 message binary blob */
+ size = msnprintf((char *)ntlmbuf, NTLM_BUFSIZE,
+ NTLMSSP_SIGNATURE "%c"
+ "\x03%c%c%c" /* 32-bit type = 3 */
+
+ "%c%c" /* LanManager length */
+ "%c%c" /* LanManager allocated space */
+ "%c%c" /* LanManager offset */
+ "%c%c" /* 2 zeroes */
+
+ "%c%c" /* NT-response length */
+ "%c%c" /* NT-response allocated space */
+ "%c%c" /* NT-response offset */
+ "%c%c" /* 2 zeroes */
+
+ "%c%c" /* domain length */
+ "%c%c" /* domain allocated space */
+ "%c%c" /* domain name offset */
+ "%c%c" /* 2 zeroes */
+
+ "%c%c" /* user length */
+ "%c%c" /* user allocated space */
+ "%c%c" /* user offset */
+ "%c%c" /* 2 zeroes */
+
+ "%c%c" /* host length */
+ "%c%c" /* host allocated space */
+ "%c%c" /* host offset */
+ "%c%c" /* 2 zeroes */
+
+ "%c%c" /* session key length (unknown purpose) */
+ "%c%c" /* session key allocated space (unknown purpose) */
+ "%c%c" /* session key offset (unknown purpose) */
+ "%c%c" /* 2 zeroes */
+
+ "%c%c%c%c", /* flags */
+
+ /* domain string */
+ /* user string */
+ /* host string */
+ /* LanManager response */
+ /* NT response */
+
+ 0, /* null-termination */
+ 0, 0, 0, /* type-3 long, the 24 upper bits */
+
+ SHORTPAIR(0x18), /* LanManager response length, twice */
+ SHORTPAIR(0x18),
+ SHORTPAIR(lmrespoff),
+ 0x0, 0x0,
+
+ SHORTPAIR(ntresplen), /* NT-response length, twice */
+ SHORTPAIR(ntresplen),
+ SHORTPAIR(ntrespoff),
+ 0x0, 0x0,
+
+ SHORTPAIR(domlen),
+ SHORTPAIR(domlen),
+ SHORTPAIR(domoff),
+ 0x0, 0x0,
+
+ SHORTPAIR(userlen),
+ SHORTPAIR(userlen),
+ SHORTPAIR(useroff),
+ 0x0, 0x0,
+
+ SHORTPAIR(hostlen),
+ SHORTPAIR(hostlen),
+ SHORTPAIR(hostoff),
+ 0x0, 0x0,
+
+ 0x0, 0x0,
+ 0x0, 0x0,
+ 0x0, 0x0,
+ 0x0, 0x0,
+
+ LONGQUARTET(ntlm->flags));
+
+ DEBUGASSERT(size == 64);
+ DEBUGASSERT(size == (size_t)lmrespoff);
+
+ /* We append the binary hashes */
+ if(size < (NTLM_BUFSIZE - 0x18)) {
+ memcpy(&ntlmbuf[size], lmresp, 0x18);
+ size += 0x18;
+ }
+
+ DEBUG_OUT({
+ fprintf(stderr, "**** TYPE3 header lmresp=");
+ ntlm_print_hex(stderr, (char *)&ntlmbuf[lmrespoff], 0x18);
+ });
+
+ /* ntresplen + size should not be risking an integer overflow here */
+ if(ntresplen + size > sizeof(ntlmbuf)) {
+ failf(data, "incoming NTLM message too big");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ DEBUGASSERT(size == (size_t)ntrespoff);
+ memcpy(&ntlmbuf[size], ptr_ntresp, ntresplen);
+ size += ntresplen;
+
+ DEBUG_OUT({
+ fprintf(stderr, "\n ntresp=");
+ ntlm_print_hex(stderr, (char *)&ntlmbuf[ntrespoff], ntresplen);
+ });
+
+ free(ntlmv2resp);/* Free the dynamic buffer allocated for NTLMv2 */
+
+ DEBUG_OUT({
+ fprintf(stderr, "\n flags=0x%02.2x%02.2x%02.2x%02.2x 0x%08.8x ",
+ LONGQUARTET(ntlm->flags), ntlm->flags);
+ ntlm_print_flags(stderr, ntlm->flags);
+ fprintf(stderr, "\n****\n");
+ });
+
+ /* Make sure that the domain, user and host strings fit in the
+ buffer before we copy them there. */
+ if(size + userlen + domlen + hostlen >= NTLM_BUFSIZE) {
+ failf(data, "user + domain + host name too big");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ DEBUGASSERT(size == domoff);
+ if(unicode)
+ unicodecpy(&ntlmbuf[size], domain, domlen / 2);
+ else
+ memcpy(&ntlmbuf[size], domain, domlen);
+
+ size += domlen;
+
+ DEBUGASSERT(size == useroff);
+ if(unicode)
+ unicodecpy(&ntlmbuf[size], user, userlen / 2);
+ else
+ memcpy(&ntlmbuf[size], user, userlen);
+
+ size += userlen;
+
+ DEBUGASSERT(size == hostoff);
+ if(unicode)
+ unicodecpy(&ntlmbuf[size], host, hostlen / 2);
+ else
+ memcpy(&ntlmbuf[size], host, hostlen);
+
+ size += hostlen;
+
+ /* Return the binary blob. */
+ result = Curl_bufref_memdup(out, ntlmbuf, size);
+
+ Curl_auth_cleanup_ntlm(ntlm);
+
+ return result;
+}
+
+/*
+ * Curl_auth_cleanup_ntlm()
+ *
+ * This is used to clean up the NTLM specific data.
+ *
+ * Parameters:
+ *
+ * ntlm [in/out] - The NTLM data struct being cleaned up.
+ *
+ */
+void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm)
+{
+ /* Free the target info */
+ Curl_safefree(ntlm->target_info);
+
+ /* Reset any variables */
+ ntlm->target_info_len = 0;
+}
+
+#endif /* USE_NTLM && !USE_WINDOWS_SSPI */
diff --git a/libs/libcurl/src/vauth/ntlm.h b/libs/libcurl/src/vauth/ntlm.h index 4dfda55453..2c8242c1a1 100644 --- a/libs/libcurl/src/vauth/ntlm.h +++ b/libs/libcurl/src/vauth/ntlm.h @@ -1,146 +1,143 @@ -#ifndef HEADER_VAUTH_NTLM_H -#define HEADER_VAUTH_NTLM_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_NTLM - -/* NTLM buffer fixed size, large enough for long user + host + domain */ -#define NTLM_BUFSIZE 1024 - -/* Stuff only required for curl_ntlm_msgs.c */ -#ifdef BUILDING_CURL_NTLM_MSGS_C - -/* Flag bits definitions based on - https://davenport.sourceforge.net/ntlm.html */ - -#define NTLMFLAG_NEGOTIATE_UNICODE (1<<0) -/* Indicates that Unicode strings are supported for use in security buffer - data. */ - -#define NTLMFLAG_NEGOTIATE_OEM (1<<1) -/* Indicates that OEM strings are supported for use in security buffer data. */ - -#define NTLMFLAG_REQUEST_TARGET (1<<2) -/* Requests that the server's authentication realm be included in the Type 2 - message. */ - -/* unknown (1<<3) */ -#define NTLMFLAG_NEGOTIATE_SIGN (1<<4) -/* Specifies that authenticated communication between the client and server - should carry a digital signature (message integrity). */ - -#define NTLMFLAG_NEGOTIATE_SEAL (1<<5) -/* Specifies that authenticated communication between the client and server - should be encrypted (message confidentiality). */ - -#define NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE (1<<6) -/* Indicates that datagram authentication is being used. */ - -#define NTLMFLAG_NEGOTIATE_LM_KEY (1<<7) -/* Indicates that the LAN Manager session key should be used for signing and - sealing authenticated communications. */ - -#define NTLMFLAG_NEGOTIATE_NETWARE (1<<8) -/* unknown purpose */ - -#define NTLMFLAG_NEGOTIATE_NTLM_KEY (1<<9) -/* Indicates that NTLM authentication is being used. */ - -/* unknown (1<<10) */ - -#define NTLMFLAG_NEGOTIATE_ANONYMOUS (1<<11) -/* Sent by the client in the Type 3 message to indicate that an anonymous - context has been established. This also affects the response fields. */ - -#define NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED (1<<12) -/* Sent by the client in the Type 1 message to indicate that a desired - authentication realm is included in the message. */ - -#define NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED (1<<13) -/* Sent by the client in the Type 1 message to indicate that the client - workstation's name is included in the message. */ - -#define NTLMFLAG_NEGOTIATE_LOCAL_CALL (1<<14) -/* Sent by the server to indicate that the server and client are on the same - machine. Implies that the client may use a pre-established local security - context rather than responding to the challenge. */ - -#define NTLMFLAG_NEGOTIATE_ALWAYS_SIGN (1<<15) -/* Indicates that authenticated communication between the client and server - should be signed with a "dummy" signature. */ - -#define NTLMFLAG_TARGET_TYPE_DOMAIN (1<<16) -/* Sent by the server in the Type 2 message to indicate that the target - authentication realm is a domain. */ - -#define NTLMFLAG_TARGET_TYPE_SERVER (1<<17) -/* Sent by the server in the Type 2 message to indicate that the target - authentication realm is a server. */ - -#define NTLMFLAG_TARGET_TYPE_SHARE (1<<18) -/* Sent by the server in the Type 2 message to indicate that the target - authentication realm is a share. Presumably, this is for share-level - authentication. Usage is unclear. */ - -#define NTLMFLAG_NEGOTIATE_NTLM2_KEY (1<<19) -/* Indicates that the NTLM2 signing and sealing scheme should be used for - protecting authenticated communications. */ - -#define NTLMFLAG_REQUEST_INIT_RESPONSE (1<<20) -/* unknown purpose */ - -#define NTLMFLAG_REQUEST_ACCEPT_RESPONSE (1<<21) -/* unknown purpose */ - -#define NTLMFLAG_REQUEST_NONNT_SESSION_KEY (1<<22) -/* unknown purpose */ - -#define NTLMFLAG_NEGOTIATE_TARGET_INFO (1<<23) -/* Sent by the server in the Type 2 message to indicate that it is including a - Target Information block in the message. */ - -/* unknown (1<24) */ -/* unknown (1<25) */ -/* unknown (1<26) */ -/* unknown (1<27) */ -/* unknown (1<28) */ - -#define NTLMFLAG_NEGOTIATE_128 (1<<29) -/* Indicates that 128-bit encryption is supported. */ - -#define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE (1<<30) -/* Indicates that the client will provide an encrypted master key in - the "Session Key" field of the Type 3 message. */ - -#define NTLMFLAG_NEGOTIATE_56 (1<<31) -/* Indicates that 56-bit encryption is supported. */ - -#endif /* BUILDING_CURL_NTLM_MSGS_C */ - -#endif /* USE_NTLM */ - -#endif /* HEADER_VAUTH_NTLM_H */ +#ifndef HEADER_VAUTH_NTLM_H
+#define HEADER_VAUTH_NTLM_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_NTLM
+
+/* NTLM buffer fixed size, large enough for long user + host + domain */
+#define NTLM_BUFSIZE 1024
+
+/* Stuff only required for curl_ntlm_msgs.c */
+#ifdef BUILDING_CURL_NTLM_MSGS_C
+
+/* Flag bits definitions based on
+ https://davenport.sourceforge.net/ntlm.html */
+
+#define NTLMFLAG_NEGOTIATE_UNICODE (1<<0)
+/* Indicates that Unicode strings are supported for use in security buffer
+ data. */
+
+#define NTLMFLAG_NEGOTIATE_OEM (1<<1)
+/* Indicates that OEM strings are supported for use in security buffer data. */
+
+#define NTLMFLAG_REQUEST_TARGET (1<<2)
+/* Requests that the server's authentication realm be included in the Type 2
+ message. */
+
+/* unknown (1<<3) */
+#define NTLMFLAG_NEGOTIATE_SIGN (1<<4)
+/* Specifies that authenticated communication between the client and server
+ should carry a digital signature (message integrity). */
+
+#define NTLMFLAG_NEGOTIATE_SEAL (1<<5)
+/* Specifies that authenticated communication between the client and server
+ should be encrypted (message confidentiality). */
+
+#define NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE (1<<6)
+/* Indicates that datagram authentication is being used. */
+
+#define NTLMFLAG_NEGOTIATE_LM_KEY (1<<7)
+/* Indicates that the LAN Manager session key should be used for signing and
+ sealing authenticated communications. */
+
+#define NTLMFLAG_NEGOTIATE_NTLM_KEY (1<<9)
+/* Indicates that NTLM authentication is being used. */
+
+/* unknown (1<<10) */
+
+#define NTLMFLAG_NEGOTIATE_ANONYMOUS (1<<11)
+/* Sent by the client in the Type 3 message to indicate that an anonymous
+ context has been established. This also affects the response fields. */
+
+#define NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED (1<<12)
+/* Sent by the client in the Type 1 message to indicate that a desired
+ authentication realm is included in the message. */
+
+#define NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED (1<<13)
+/* Sent by the client in the Type 1 message to indicate that the client
+ workstation's name is included in the message. */
+
+#define NTLMFLAG_NEGOTIATE_LOCAL_CALL (1<<14)
+/* Sent by the server to indicate that the server and client are on the same
+ machine. Implies that the client may use a pre-established local security
+ context rather than responding to the challenge. */
+
+#define NTLMFLAG_NEGOTIATE_ALWAYS_SIGN (1<<15)
+/* Indicates that authenticated communication between the client and server
+ should be signed with a "dummy" signature. */
+
+#define NTLMFLAG_TARGET_TYPE_DOMAIN (1<<16)
+/* Sent by the server in the Type 2 message to indicate that the target
+ authentication realm is a domain. */
+
+#define NTLMFLAG_TARGET_TYPE_SERVER (1<<17)
+/* Sent by the server in the Type 2 message to indicate that the target
+ authentication realm is a server. */
+
+#define NTLMFLAG_TARGET_TYPE_SHARE (1<<18)
+/* Sent by the server in the Type 2 message to indicate that the target
+ authentication realm is a share. Presumably, this is for share-level
+ authentication. Usage is unclear. */
+
+#define NTLMFLAG_NEGOTIATE_NTLM2_KEY (1<<19)
+/* Indicates that the NTLM2 signing and sealing scheme should be used for
+ protecting authenticated communications. */
+
+#define NTLMFLAG_REQUEST_INIT_RESPONSE (1<<20)
+/* unknown purpose */
+
+#define NTLMFLAG_REQUEST_ACCEPT_RESPONSE (1<<21)
+/* unknown purpose */
+
+#define NTLMFLAG_REQUEST_NONNT_SESSION_KEY (1<<22)
+/* unknown purpose */
+
+#define NTLMFLAG_NEGOTIATE_TARGET_INFO (1<<23)
+/* Sent by the server in the Type 2 message to indicate that it is including a
+ Target Information block in the message. */
+
+/* unknown (1<24) */
+/* unknown (1<25) */
+/* unknown (1<26) */
+/* unknown (1<27) */
+/* unknown (1<28) */
+
+#define NTLMFLAG_NEGOTIATE_128 (1<<29)
+/* Indicates that 128-bit encryption is supported. */
+
+#define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE (1<<30)
+/* Indicates that the client will provide an encrypted master key in
+ the "Session Key" field of the Type 3 message. */
+
+#define NTLMFLAG_NEGOTIATE_56 (1<<31)
+/* Indicates that 56-bit encryption is supported. */
+
+#endif /* BUILDING_CURL_NTLM_MSGS_C */
+
+#endif /* USE_NTLM */
+
+#endif /* HEADER_VAUTH_NTLM_H */
diff --git a/libs/libcurl/src/vauth/ntlm_sspi.c b/libs/libcurl/src/vauth/ntlm_sspi.c index 193576acaa..12f4e23b43 100644 --- a/libs/libcurl/src/vauth/ntlm_sspi.c +++ b/libs/libcurl/src/vauth/ntlm_sspi.c @@ -1,372 +1,372 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_WINDOWS_SSPI) && defined(USE_NTLM) - -#include <curl/curl.h> - -#include "vauth/vauth.h" -#include "urldata.h" -#include "curl_ntlm_core.h" -#include "warnless.h" -#include "curl_multibyte.h" -#include "sendf.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* - * Curl_auth_is_ntlm_supported() - * - * This is used to evaluate if NTLM is supported. - * - * Parameters: None - * - * Returns TRUE if NTLM is supported by Windows SSPI. - */ -bool Curl_auth_is_ntlm_supported(void) -{ - PSecPkgInfo SecurityPackage; - SECURITY_STATUS status; - - /* Query the security package for NTLM */ - status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM), - &SecurityPackage); - - /* Release the package buffer as it is not required anymore */ - if(status == SEC_E_OK) { - s_pSecFn->FreeContextBuffer(SecurityPackage); - } - - return (status == SEC_E_OK ? TRUE : FALSE); -} - -/* - * Curl_auth_create_ntlm_type1_message() - * - * This is used to generate an already encoded NTLM type-1 message ready for - * sending to the recipient. - * - * Parameters: - * - * data [in] - The session handle. - * userp [in] - The user name in the format User or Domain\User. - * passwdp [in] - The user's password. - * service [in] - The service type such as http, smtp, pop or imap. - * host [in] - The host name. - * ntlm [in/out] - The NTLM data struct being used and modified. - * out [out] - The result storage. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const char *service, - const char *host, - struct ntlmdata *ntlm, - struct bufref *out) -{ - PSecPkgInfo SecurityPackage; - SecBuffer type_1_buf; - SecBufferDesc type_1_desc; - SECURITY_STATUS status; - unsigned long attrs; - TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ - - /* Clean up any former leftovers and initialise to defaults */ - Curl_auth_cleanup_ntlm(ntlm); - - /* Query the security package for NTLM */ - status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM), - &SecurityPackage); - if(status != SEC_E_OK) { - failf(data, "SSPI: couldn't get auth info"); - return CURLE_AUTH_ERROR; - } - - ntlm->token_max = SecurityPackage->cbMaxToken; - - /* Release the package buffer as it is not required anymore */ - s_pSecFn->FreeContextBuffer(SecurityPackage); - - /* Allocate our output buffer */ - ntlm->output_token = malloc(ntlm->token_max); - if(!ntlm->output_token) - return CURLE_OUT_OF_MEMORY; - - if(userp && *userp) { - CURLcode result; - - /* Populate our identity structure */ - result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity); - if(result) - return result; - - /* Allow proper cleanup of the identity structure */ - ntlm->p_identity = &ntlm->identity; - } - else - /* Use the current Windows user */ - ntlm->p_identity = NULL; - - /* Allocate our credentials handle */ - ntlm->credentials = calloc(1, sizeof(CredHandle)); - if(!ntlm->credentials) - return CURLE_OUT_OF_MEMORY; - - /* Acquire our credentials handle */ - status = s_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *) TEXT(SP_NAME_NTLM), - SECPKG_CRED_OUTBOUND, NULL, - ntlm->p_identity, NULL, NULL, - ntlm->credentials, &expiry); - if(status != SEC_E_OK) - return CURLE_LOGIN_DENIED; - - /* Allocate our new context handle */ - ntlm->context = calloc(1, sizeof(CtxtHandle)); - if(!ntlm->context) - return CURLE_OUT_OF_MEMORY; - - ntlm->spn = Curl_auth_build_spn(service, host, NULL); - if(!ntlm->spn) - return CURLE_OUT_OF_MEMORY; - - /* Setup the type-1 "output" security buffer */ - type_1_desc.ulVersion = SECBUFFER_VERSION; - type_1_desc.cBuffers = 1; - type_1_desc.pBuffers = &type_1_buf; - type_1_buf.BufferType = SECBUFFER_TOKEN; - type_1_buf.pvBuffer = ntlm->output_token; - type_1_buf.cbBuffer = curlx_uztoul(ntlm->token_max); - - /* Generate our type-1 message */ - status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL, - ntlm->spn, - 0, 0, SECURITY_NETWORK_DREP, - NULL, 0, - ntlm->context, &type_1_desc, - &attrs, &expiry); - if(status == SEC_I_COMPLETE_NEEDED || - status == SEC_I_COMPLETE_AND_CONTINUE) - s_pSecFn->CompleteAuthToken(ntlm->context, &type_1_desc); - else if(status == SEC_E_INSUFFICIENT_MEMORY) - return CURLE_OUT_OF_MEMORY; - else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) - return CURLE_AUTH_ERROR; - - /* Return the response. */ - Curl_bufref_set(out, ntlm->output_token, type_1_buf.cbBuffer, NULL); - return CURLE_OK; -} - -/* - * Curl_auth_decode_ntlm_type2_message() - * - * This is used to decode an already encoded NTLM type-2 message. - * - * Parameters: - * - * data [in] - The session handle. - * type2 [in] - The type-2 message. - * ntlm [in/out] - The NTLM data struct being used and modified. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, - const struct bufref *type2, - struct ntlmdata *ntlm) -{ -#if defined(CURL_DISABLE_VERBOSE_STRINGS) - (void) data; -#endif - - /* Ensure we have a valid type-2 message */ - if(!Curl_bufref_len(type2)) { - infof(data, "NTLM handshake failure (empty type-2 message)"); - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Store the challenge for later use */ - ntlm->input_token = malloc(Curl_bufref_len(type2) + 1); - if(!ntlm->input_token) - return CURLE_OUT_OF_MEMORY; - memcpy(ntlm->input_token, Curl_bufref_ptr(type2), Curl_bufref_len(type2)); - ntlm->input_token[Curl_bufref_len(type2)] = '\0'; - ntlm->input_token_len = Curl_bufref_len(type2); - - return CURLE_OK; -} - -/* -* Curl_auth_create_ntlm_type3_message() - * Curl_auth_create_ntlm_type3_message() - * - * This is used to generate an already encoded NTLM type-3 message ready for - * sending to the recipient. - * - * Parameters: - * - * data [in] - The session handle. - * userp [in] - The user name in the format User or Domain\User. - * passwdp [in] - The user's password. - * ntlm [in/out] - The NTLM data struct being used and modified. - * out [out] - The result storage. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - struct ntlmdata *ntlm, - struct bufref *out) -{ - CURLcode result = CURLE_OK; - SecBuffer type_2_bufs[2]; - SecBuffer type_3_buf; - SecBufferDesc type_2_desc; - SecBufferDesc type_3_desc; - SECURITY_STATUS status; - unsigned long attrs; - TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ - -#if defined(CURL_DISABLE_VERBOSE_STRINGS) - (void) data; -#endif - (void) passwdp; - (void) userp; - - /* Setup the type-2 "input" security buffer */ - type_2_desc.ulVersion = SECBUFFER_VERSION; - type_2_desc.cBuffers = 1; - type_2_desc.pBuffers = &type_2_bufs[0]; - type_2_bufs[0].BufferType = SECBUFFER_TOKEN; - type_2_bufs[0].pvBuffer = ntlm->input_token; - type_2_bufs[0].cbBuffer = curlx_uztoul(ntlm->input_token_len); - -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS - /* ssl context comes from schannel. - * When extended protection is used in IIS server, - * we have to pass a second SecBuffer to the SecBufferDesc - * otherwise IIS will not pass the authentication (401 response). - * Minimum supported version is Windows 7. - * https://docs.microsoft.com/en-us/security-updates - * /SecurityAdvisories/2009/973811 - */ - if(ntlm->sslContext) { - SEC_CHANNEL_BINDINGS channelBindings; - SecPkgContext_Bindings pkgBindings; - pkgBindings.Bindings = &channelBindings; - status = s_pSecFn->QueryContextAttributes( - ntlm->sslContext, - SECPKG_ATTR_ENDPOINT_BINDINGS, - &pkgBindings - ); - if(status == SEC_E_OK) { - type_2_desc.cBuffers++; - type_2_bufs[1].BufferType = SECBUFFER_CHANNEL_BINDINGS; - type_2_bufs[1].cbBuffer = pkgBindings.BindingsLength; - type_2_bufs[1].pvBuffer = pkgBindings.Bindings; - } - } -#endif - - /* Setup the type-3 "output" security buffer */ - type_3_desc.ulVersion = SECBUFFER_VERSION; - type_3_desc.cBuffers = 1; - type_3_desc.pBuffers = &type_3_buf; - type_3_buf.BufferType = SECBUFFER_TOKEN; - type_3_buf.pvBuffer = ntlm->output_token; - type_3_buf.cbBuffer = curlx_uztoul(ntlm->token_max); - - /* Generate our type-3 message */ - status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, - ntlm->context, - ntlm->spn, - 0, 0, SECURITY_NETWORK_DREP, - &type_2_desc, - 0, ntlm->context, - &type_3_desc, - &attrs, &expiry); - if(status != SEC_E_OK) { - infof(data, "NTLM handshake failure (type-3 message): Status=%x", - status); - - if(status == SEC_E_INSUFFICIENT_MEMORY) - return CURLE_OUT_OF_MEMORY; - - return CURLE_AUTH_ERROR; - } - - /* Return the response. */ - result = Curl_bufref_memdup(out, ntlm->output_token, type_3_buf.cbBuffer); - Curl_auth_cleanup_ntlm(ntlm); - return result; -} - -/* - * Curl_auth_cleanup_ntlm() - * - * This is used to clean up the NTLM specific data. - * - * Parameters: - * - * ntlm [in/out] - The NTLM data struct being cleaned up. - * - */ -void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm) -{ - /* Free our security context */ - if(ntlm->context) { - s_pSecFn->DeleteSecurityContext(ntlm->context); - free(ntlm->context); - ntlm->context = NULL; - } - - /* Free our credentials handle */ - if(ntlm->credentials) { - s_pSecFn->FreeCredentialsHandle(ntlm->credentials); - free(ntlm->credentials); - ntlm->credentials = NULL; - } - - /* Free our identity */ - Curl_sspi_free_identity(ntlm->p_identity); - ntlm->p_identity = NULL; - - /* Free the input and output tokens */ - Curl_safefree(ntlm->input_token); - Curl_safefree(ntlm->output_token); - - /* Reset any variables */ - ntlm->token_max = 0; - - Curl_safefree(ntlm->spn); -} - -#endif /* USE_WINDOWS_SSPI && USE_NTLM */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_WINDOWS_SSPI) && defined(USE_NTLM)
+
+#include <curl/curl.h>
+
+#include "vauth/vauth.h"
+#include "urldata.h"
+#include "curl_ntlm_core.h"
+#include "warnless.h"
+#include "curl_multibyte.h"
+#include "sendf.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * Curl_auth_is_ntlm_supported()
+ *
+ * This is used to evaluate if NTLM is supported.
+ *
+ * Parameters: None
+ *
+ * Returns TRUE if NTLM is supported by Windows SSPI.
+ */
+bool Curl_auth_is_ntlm_supported(void)
+{
+ PSecPkgInfo SecurityPackage;
+ SECURITY_STATUS status;
+
+ /* Query the security package for NTLM */
+ status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
+ &SecurityPackage);
+
+ /* Release the package buffer as it is not required anymore */
+ if(status == SEC_E_OK) {
+ s_pSecFn->FreeContextBuffer(SecurityPackage);
+ }
+
+ return (status == SEC_E_OK ? TRUE : FALSE);
+}
+
+/*
+ * Curl_auth_create_ntlm_type1_message()
+ *
+ * This is used to generate an already encoded NTLM type-1 message ready for
+ * sending to the recipient.
+ *
+ * Parameters:
+ *
+ * data [in] - The session handle.
+ * userp [in] - The user name in the format User or Domain\User.
+ * passwdp [in] - The user's password.
+ * service [in] - The service type such as http, smtp, pop or imap.
+ * host [in] - The host name.
+ * ntlm [in/out] - The NTLM data struct being used and modified.
+ * out [out] - The result storage.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
+ const char *userp,
+ const char *passwdp,
+ const char *service,
+ const char *host,
+ struct ntlmdata *ntlm,
+ struct bufref *out)
+{
+ PSecPkgInfo SecurityPackage;
+ SecBuffer type_1_buf;
+ SecBufferDesc type_1_desc;
+ SECURITY_STATUS status;
+ unsigned long attrs;
+ TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
+
+ /* Clean up any former leftovers and initialise to defaults */
+ Curl_auth_cleanup_ntlm(ntlm);
+
+ /* Query the security package for NTLM */
+ status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
+ &SecurityPackage);
+ if(status != SEC_E_OK) {
+ failf(data, "SSPI: couldn't get auth info");
+ return CURLE_AUTH_ERROR;
+ }
+
+ ntlm->token_max = SecurityPackage->cbMaxToken;
+
+ /* Release the package buffer as it is not required anymore */
+ s_pSecFn->FreeContextBuffer(SecurityPackage);
+
+ /* Allocate our output buffer */
+ ntlm->output_token = malloc(ntlm->token_max);
+ if(!ntlm->output_token)
+ return CURLE_OUT_OF_MEMORY;
+
+ if(userp && *userp) {
+ CURLcode result;
+
+ /* Populate our identity structure */
+ result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity);
+ if(result)
+ return result;
+
+ /* Allow proper cleanup of the identity structure */
+ ntlm->p_identity = &ntlm->identity;
+ }
+ else
+ /* Use the current Windows user */
+ ntlm->p_identity = NULL;
+
+ /* Allocate our credentials handle */
+ ntlm->credentials = calloc(1, sizeof(CredHandle));
+ if(!ntlm->credentials)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Acquire our credentials handle */
+ status = s_pSecFn->AcquireCredentialsHandle(NULL,
+ (TCHAR *) TEXT(SP_NAME_NTLM),
+ SECPKG_CRED_OUTBOUND, NULL,
+ ntlm->p_identity, NULL, NULL,
+ ntlm->credentials, &expiry);
+ if(status != SEC_E_OK)
+ return CURLE_LOGIN_DENIED;
+
+ /* Allocate our new context handle */
+ ntlm->context = calloc(1, sizeof(CtxtHandle));
+ if(!ntlm->context)
+ return CURLE_OUT_OF_MEMORY;
+
+ ntlm->spn = Curl_auth_build_spn(service, host, NULL);
+ if(!ntlm->spn)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Setup the type-1 "output" security buffer */
+ type_1_desc.ulVersion = SECBUFFER_VERSION;
+ type_1_desc.cBuffers = 1;
+ type_1_desc.pBuffers = &type_1_buf;
+ type_1_buf.BufferType = SECBUFFER_TOKEN;
+ type_1_buf.pvBuffer = ntlm->output_token;
+ type_1_buf.cbBuffer = curlx_uztoul(ntlm->token_max);
+
+ /* Generate our type-1 message */
+ status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL,
+ ntlm->spn,
+ 0, 0, SECURITY_NETWORK_DREP,
+ NULL, 0,
+ ntlm->context, &type_1_desc,
+ &attrs, &expiry);
+ if(status == SEC_I_COMPLETE_NEEDED ||
+ status == SEC_I_COMPLETE_AND_CONTINUE)
+ s_pSecFn->CompleteAuthToken(ntlm->context, &type_1_desc);
+ else if(status == SEC_E_INSUFFICIENT_MEMORY)
+ return CURLE_OUT_OF_MEMORY;
+ else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED)
+ return CURLE_AUTH_ERROR;
+
+ /* Return the response. */
+ Curl_bufref_set(out, ntlm->output_token, type_1_buf.cbBuffer, NULL);
+ return CURLE_OK;
+}
+
+/*
+ * Curl_auth_decode_ntlm_type2_message()
+ *
+ * This is used to decode an already encoded NTLM type-2 message.
+ *
+ * Parameters:
+ *
+ * data [in] - The session handle.
+ * type2 [in] - The type-2 message.
+ * ntlm [in/out] - The NTLM data struct being used and modified.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data,
+ const struct bufref *type2,
+ struct ntlmdata *ntlm)
+{
+#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+ (void) data;
+#endif
+
+ /* Ensure we have a valid type-2 message */
+ if(!Curl_bufref_len(type2)) {
+ infof(data, "NTLM handshake failure (empty type-2 message)");
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+
+ /* Store the challenge for later use */
+ ntlm->input_token = malloc(Curl_bufref_len(type2) + 1);
+ if(!ntlm->input_token)
+ return CURLE_OUT_OF_MEMORY;
+ memcpy(ntlm->input_token, Curl_bufref_ptr(type2), Curl_bufref_len(type2));
+ ntlm->input_token[Curl_bufref_len(type2)] = '\0';
+ ntlm->input_token_len = Curl_bufref_len(type2);
+
+ return CURLE_OK;
+}
+
+/*
+* Curl_auth_create_ntlm_type3_message()
+ * Curl_auth_create_ntlm_type3_message()
+ *
+ * This is used to generate an already encoded NTLM type-3 message ready for
+ * sending to the recipient.
+ *
+ * Parameters:
+ *
+ * data [in] - The session handle.
+ * userp [in] - The user name in the format User or Domain\User.
+ * passwdp [in] - The user's password.
+ * ntlm [in/out] - The NTLM data struct being used and modified.
+ * out [out] - The result storage.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
+ const char *userp,
+ const char *passwdp,
+ struct ntlmdata *ntlm,
+ struct bufref *out)
+{
+ CURLcode result = CURLE_OK;
+ SecBuffer type_2_bufs[2];
+ SecBuffer type_3_buf;
+ SecBufferDesc type_2_desc;
+ SecBufferDesc type_3_desc;
+ SECURITY_STATUS status;
+ unsigned long attrs;
+ TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
+
+#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+ (void) data;
+#endif
+ (void) passwdp;
+ (void) userp;
+
+ /* Setup the type-2 "input" security buffer */
+ type_2_desc.ulVersion = SECBUFFER_VERSION;
+ type_2_desc.cBuffers = 1;
+ type_2_desc.pBuffers = &type_2_bufs[0];
+ type_2_bufs[0].BufferType = SECBUFFER_TOKEN;
+ type_2_bufs[0].pvBuffer = ntlm->input_token;
+ type_2_bufs[0].cbBuffer = curlx_uztoul(ntlm->input_token_len);
+
+#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
+ /* ssl context comes from schannel.
+ * When extended protection is used in IIS server,
+ * we have to pass a second SecBuffer to the SecBufferDesc
+ * otherwise IIS will not pass the authentication (401 response).
+ * Minimum supported version is Windows 7.
+ * https://docs.microsoft.com/en-us/security-updates
+ * /SecurityAdvisories/2009/973811
+ */
+ if(ntlm->sslContext) {
+ SEC_CHANNEL_BINDINGS channelBindings;
+ SecPkgContext_Bindings pkgBindings;
+ pkgBindings.Bindings = &channelBindings;
+ status = s_pSecFn->QueryContextAttributes(
+ ntlm->sslContext,
+ SECPKG_ATTR_ENDPOINT_BINDINGS,
+ &pkgBindings
+ );
+ if(status == SEC_E_OK) {
+ type_2_desc.cBuffers++;
+ type_2_bufs[1].BufferType = SECBUFFER_CHANNEL_BINDINGS;
+ type_2_bufs[1].cbBuffer = pkgBindings.BindingsLength;
+ type_2_bufs[1].pvBuffer = pkgBindings.Bindings;
+ }
+ }
+#endif
+
+ /* Setup the type-3 "output" security buffer */
+ type_3_desc.ulVersion = SECBUFFER_VERSION;
+ type_3_desc.cBuffers = 1;
+ type_3_desc.pBuffers = &type_3_buf;
+ type_3_buf.BufferType = SECBUFFER_TOKEN;
+ type_3_buf.pvBuffer = ntlm->output_token;
+ type_3_buf.cbBuffer = curlx_uztoul(ntlm->token_max);
+
+ /* Generate our type-3 message */
+ status = s_pSecFn->InitializeSecurityContext(ntlm->credentials,
+ ntlm->context,
+ ntlm->spn,
+ 0, 0, SECURITY_NETWORK_DREP,
+ &type_2_desc,
+ 0, ntlm->context,
+ &type_3_desc,
+ &attrs, &expiry);
+ if(status != SEC_E_OK) {
+ infof(data, "NTLM handshake failure (type-3 message): Status=%x",
+ status);
+
+ if(status == SEC_E_INSUFFICIENT_MEMORY)
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_AUTH_ERROR;
+ }
+
+ /* Return the response. */
+ result = Curl_bufref_memdup(out, ntlm->output_token, type_3_buf.cbBuffer);
+ Curl_auth_cleanup_ntlm(ntlm);
+ return result;
+}
+
+/*
+ * Curl_auth_cleanup_ntlm()
+ *
+ * This is used to clean up the NTLM specific data.
+ *
+ * Parameters:
+ *
+ * ntlm [in/out] - The NTLM data struct being cleaned up.
+ *
+ */
+void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm)
+{
+ /* Free our security context */
+ if(ntlm->context) {
+ s_pSecFn->DeleteSecurityContext(ntlm->context);
+ free(ntlm->context);
+ ntlm->context = NULL;
+ }
+
+ /* Free our credentials handle */
+ if(ntlm->credentials) {
+ s_pSecFn->FreeCredentialsHandle(ntlm->credentials);
+ free(ntlm->credentials);
+ ntlm->credentials = NULL;
+ }
+
+ /* Free our identity */
+ Curl_sspi_free_identity(ntlm->p_identity);
+ ntlm->p_identity = NULL;
+
+ /* Free the input and output tokens */
+ Curl_safefree(ntlm->input_token);
+ Curl_safefree(ntlm->output_token);
+
+ /* Reset any variables */
+ ntlm->token_max = 0;
+
+ Curl_safefree(ntlm->spn);
+}
+
+#endif /* USE_WINDOWS_SSPI && USE_NTLM */
diff --git a/libs/libcurl/src/vauth/oauth2.c b/libs/libcurl/src/vauth/oauth2.c index 1604b303e0..d424b26fdd 100644 --- a/libs/libcurl/src/vauth/oauth2.c +++ b/libs/libcurl/src/vauth/oauth2.c @@ -1,107 +1,107 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - * RFC6749 OAuth 2.0 Authorization Framework - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \ - !defined(CURL_DISABLE_POP3) - -#include <curl/curl.h> -#include "urldata.h" - -#include "vauth/vauth.h" -#include "warnless.h" -#include "curl_printf.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* - * Curl_auth_create_oauth_bearer_message() - * - * This is used to generate an OAuth 2.0 message ready for sending to the - * recipient. - * - * Parameters: - * - * user[in] - The user name. - * host[in] - The host name. - * port[in] - The port(when not Port 80). - * bearer[in] - The bearer token. - * out[out] - The result storage. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_create_oauth_bearer_message(const char *user, - const char *host, - const long port, - const char *bearer, - struct bufref *out) -{ - char *oauth; - - /* Generate the message */ - if(port == 0 || port == 80) - oauth = aprintf("n,a=%s,\1host=%s\1auth=Bearer %s\1\1", user, host, - bearer); - else - oauth = aprintf("n,a=%s,\1host=%s\1port=%ld\1auth=Bearer %s\1\1", user, - host, port, bearer); - if(!oauth) - return CURLE_OUT_OF_MEMORY; - - Curl_bufref_set(out, oauth, strlen(oauth), curl_free); - return CURLE_OK; -} - -/* - * Curl_auth_create_xoauth_bearer_message() - * - * This is used to generate a XOAuth 2.0 message ready for * sending to the - * recipient. - * - * Parameters: - * - * user[in] - The user name. - * bearer[in] - The bearer token. - * out[out] - The result storage. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_create_xoauth_bearer_message(const char *user, - const char *bearer, - struct bufref *out) -{ - /* Generate the message */ - char *xoauth = aprintf("user=%s\1auth=Bearer %s\1\1", user, bearer); - if(!xoauth) - return CURLE_OUT_OF_MEMORY; - - Curl_bufref_set(out, xoauth, strlen(xoauth), curl_free); - return CURLE_OK; -} -#endif /* disabled, no users */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ * RFC6749 OAuth 2.0 Authorization Framework
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \
+ !defined(CURL_DISABLE_POP3)
+
+#include <curl/curl.h>
+#include "urldata.h"
+
+#include "vauth/vauth.h"
+#include "warnless.h"
+#include "curl_printf.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * Curl_auth_create_oauth_bearer_message()
+ *
+ * This is used to generate an OAuth 2.0 message ready for sending to the
+ * recipient.
+ *
+ * Parameters:
+ *
+ * user[in] - The user name.
+ * host[in] - The host name.
+ * port[in] - The port(when not Port 80).
+ * bearer[in] - The bearer token.
+ * out[out] - The result storage.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_oauth_bearer_message(const char *user,
+ const char *host,
+ const long port,
+ const char *bearer,
+ struct bufref *out)
+{
+ char *oauth;
+
+ /* Generate the message */
+ if(port == 0 || port == 80)
+ oauth = aprintf("n,a=%s,\1host=%s\1auth=Bearer %s\1\1", user, host,
+ bearer);
+ else
+ oauth = aprintf("n,a=%s,\1host=%s\1port=%ld\1auth=Bearer %s\1\1", user,
+ host, port, bearer);
+ if(!oauth)
+ return CURLE_OUT_OF_MEMORY;
+
+ Curl_bufref_set(out, oauth, strlen(oauth), curl_free);
+ return CURLE_OK;
+}
+
+/*
+ * Curl_auth_create_xoauth_bearer_message()
+ *
+ * This is used to generate a XOAuth 2.0 message ready for * sending to the
+ * recipient.
+ *
+ * Parameters:
+ *
+ * user[in] - The user name.
+ * bearer[in] - The bearer token.
+ * out[out] - The result storage.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_xoauth_bearer_message(const char *user,
+ const char *bearer,
+ struct bufref *out)
+{
+ /* Generate the message */
+ char *xoauth = aprintf("user=%s\1auth=Bearer %s\1\1", user, bearer);
+ if(!xoauth)
+ return CURLE_OUT_OF_MEMORY;
+
+ Curl_bufref_set(out, xoauth, strlen(xoauth), curl_free);
+ return CURLE_OK;
+}
+#endif /* disabled, no users */
diff --git a/libs/libcurl/src/vauth/spnego_gssapi.c b/libs/libcurl/src/vauth/spnego_gssapi.c index 25dff967d1..7445b1bdbf 100644 --- a/libs/libcurl/src/vauth/spnego_gssapi.c +++ b/libs/libcurl/src/vauth/spnego_gssapi.c @@ -1,281 +1,281 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - * RFC4178 Simple and Protected GSS-API Negotiation Mechanism - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if defined(HAVE_GSSAPI) && defined(USE_SPNEGO) - -#include <curl/curl.h> - -#include "vauth/vauth.h" -#include "urldata.h" -#include "curl_base64.h" -#include "curl_gssapi.h" -#include "warnless.h" -#include "curl_multibyte.h" -#include "sendf.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* - * Curl_auth_is_spnego_supported() - * - * This is used to evaluate if SPNEGO (Negotiate) is supported. - * - * Parameters: None - * - * Returns TRUE if Negotiate supported by the GSS-API library. - */ -bool Curl_auth_is_spnego_supported(void) -{ - return TRUE; -} - -/* - * Curl_auth_decode_spnego_message() - * - * This is used to decode an already encoded SPNEGO (Negotiate) challenge - * message. - * - * Parameters: - * - * data [in] - The session handle. - * userp [in] - The user name in the format User or Domain\User. - * passwdp [in] - The user's password. - * service [in] - The service type such as http, smtp, pop or imap. - * host [in] - The host name. - * chlg64 [in] - The optional base64 encoded challenge message. - * nego [in/out] - The Negotiate data struct being used and modified. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, - const char *user, - const char *password, - const char *service, - const char *host, - const char *chlg64, - struct negotiatedata *nego) -{ - CURLcode result = CURLE_OK; - size_t chlglen = 0; - unsigned char *chlg = NULL; - OM_uint32 major_status; - OM_uint32 minor_status; - OM_uint32 unused_status; - gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; - - (void) user; - (void) password; - - if(nego->context && nego->status == GSS_S_COMPLETE) { - /* We finished successfully our part of authentication, but server - * rejected it (since we're again here). Exit with an error since we - * can't invent anything better */ - Curl_auth_cleanup_spnego(nego); - return CURLE_LOGIN_DENIED; - } - - if(!nego->spn) { - /* Generate our SPN */ - char *spn = Curl_auth_build_spn(service, NULL, host); - if(!spn) - return CURLE_OUT_OF_MEMORY; - - /* Populate the SPN structure */ - spn_token.value = spn; - spn_token.length = strlen(spn); - - /* Import the SPN */ - major_status = gss_import_name(&minor_status, &spn_token, - GSS_C_NT_HOSTBASED_SERVICE, - &nego->spn); - if(GSS_ERROR(major_status)) { - Curl_gss_log_error(data, "gss_import_name() failed: ", - major_status, minor_status); - - free(spn); - - return CURLE_AUTH_ERROR; - } - - free(spn); - } - - if(chlg64 && *chlg64) { - /* Decode the base-64 encoded challenge message */ - if(*chlg64 != '=') { - result = Curl_base64_decode(chlg64, &chlg, &chlglen); - if(result) - return result; - } - - /* Ensure we have a valid challenge message */ - if(!chlg) { - infof(data, "SPNEGO handshake failure (empty challenge message)"); - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Setup the challenge "input" security buffer */ - input_token.value = chlg; - input_token.length = chlglen; - } - - /* Generate our challenge-response message */ - major_status = Curl_gss_init_sec_context(data, - &minor_status, - &nego->context, - nego->spn, - &Curl_spnego_mech_oid, - GSS_C_NO_CHANNEL_BINDINGS, - &input_token, - &output_token, - TRUE, - NULL); - - /* Free the decoded challenge as it is not required anymore */ - Curl_safefree(input_token.value); - - nego->status = major_status; - if(GSS_ERROR(major_status)) { - if(output_token.value) - gss_release_buffer(&unused_status, &output_token); - - Curl_gss_log_error(data, "gss_init_sec_context() failed: ", - major_status, minor_status); - - return CURLE_AUTH_ERROR; - } - - if(!output_token.value || !output_token.length) { - if(output_token.value) - gss_release_buffer(&unused_status, &output_token); - - return CURLE_AUTH_ERROR; - } - - /* Free previous token */ - if(nego->output_token.length && nego->output_token.value) - gss_release_buffer(&unused_status, &nego->output_token); - - nego->output_token = output_token; - - return CURLE_OK; -} - -/* - * Curl_auth_create_spnego_message() - * - * This is used to generate an already encoded SPNEGO (Negotiate) response - * message ready for sending to the recipient. - * - * Parameters: - * - * data [in] - The session handle. - * nego [in/out] - The Negotiate data struct being used and modified. - * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. - * outlen [out] - The length of the output message. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego, - char **outptr, size_t *outlen) -{ - CURLcode result; - OM_uint32 minor_status; - - /* Base64 encode the already generated response */ - result = Curl_base64_encode(nego->output_token.value, - nego->output_token.length, - outptr, outlen); - - if(result) { - gss_release_buffer(&minor_status, &nego->output_token); - nego->output_token.value = NULL; - nego->output_token.length = 0; - - return result; - } - - if(!*outptr || !*outlen) { - gss_release_buffer(&minor_status, &nego->output_token); - nego->output_token.value = NULL; - nego->output_token.length = 0; - - return CURLE_REMOTE_ACCESS_DENIED; - } - - return CURLE_OK; -} - -/* - * Curl_auth_cleanup_spnego() - * - * This is used to clean up the SPNEGO (Negotiate) specific data. - * - * Parameters: - * - * nego [in/out] - The Negotiate data struct being cleaned up. - * - */ -void Curl_auth_cleanup_spnego(struct negotiatedata *nego) -{ - OM_uint32 minor_status; - - /* Free our security context */ - if(nego->context != GSS_C_NO_CONTEXT) { - gss_delete_sec_context(&minor_status, &nego->context, GSS_C_NO_BUFFER); - nego->context = GSS_C_NO_CONTEXT; - } - - /* Free the output token */ - if(nego->output_token.value) { - gss_release_buffer(&minor_status, &nego->output_token); - nego->output_token.value = NULL; - nego->output_token.length = 0; - - } - - /* Free the SPN */ - if(nego->spn != GSS_C_NO_NAME) { - gss_release_name(&minor_status, &nego->spn); - nego->spn = GSS_C_NO_NAME; - } - - /* Reset any variables */ - nego->status = 0; - nego->noauthpersist = FALSE; - nego->havenoauthpersist = FALSE; - nego->havenegdata = FALSE; - nego->havemultiplerequests = FALSE; -} - -#endif /* HAVE_GSSAPI && USE_SPNEGO */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ * RFC4178 Simple and Protected GSS-API Negotiation Mechanism
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if defined(HAVE_GSSAPI) && defined(USE_SPNEGO)
+
+#include <curl/curl.h>
+
+#include "vauth/vauth.h"
+#include "urldata.h"
+#include "curl_base64.h"
+#include "curl_gssapi.h"
+#include "warnless.h"
+#include "curl_multibyte.h"
+#include "sendf.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * Curl_auth_is_spnego_supported()
+ *
+ * This is used to evaluate if SPNEGO (Negotiate) is supported.
+ *
+ * Parameters: None
+ *
+ * Returns TRUE if Negotiate supported by the GSS-API library.
+ */
+bool Curl_auth_is_spnego_supported(void)
+{
+ return TRUE;
+}
+
+/*
+ * Curl_auth_decode_spnego_message()
+ *
+ * This is used to decode an already encoded SPNEGO (Negotiate) challenge
+ * message.
+ *
+ * Parameters:
+ *
+ * data [in] - The session handle.
+ * userp [in] - The user name in the format User or Domain\User.
+ * passwdp [in] - The user's password.
+ * service [in] - The service type such as http, smtp, pop or imap.
+ * host [in] - The host name.
+ * chlg64 [in] - The optional base64 encoded challenge message.
+ * nego [in/out] - The Negotiate data struct being used and modified.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
+ const char *user,
+ const char *password,
+ const char *service,
+ const char *host,
+ const char *chlg64,
+ struct negotiatedata *nego)
+{
+ CURLcode result = CURLE_OK;
+ size_t chlglen = 0;
+ unsigned char *chlg = NULL;
+ OM_uint32 major_status;
+ OM_uint32 minor_status;
+ OM_uint32 unused_status;
+ gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+
+ (void) user;
+ (void) password;
+
+ if(nego->context && nego->status == GSS_S_COMPLETE) {
+ /* We finished successfully our part of authentication, but server
+ * rejected it (since we're again here). Exit with an error since we
+ * can't invent anything better */
+ Curl_auth_cleanup_spnego(nego);
+ return CURLE_LOGIN_DENIED;
+ }
+
+ if(!nego->spn) {
+ /* Generate our SPN */
+ char *spn = Curl_auth_build_spn(service, NULL, host);
+ if(!spn)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Populate the SPN structure */
+ spn_token.value = spn;
+ spn_token.length = strlen(spn);
+
+ /* Import the SPN */
+ major_status = gss_import_name(&minor_status, &spn_token,
+ GSS_C_NT_HOSTBASED_SERVICE,
+ &nego->spn);
+ if(GSS_ERROR(major_status)) {
+ Curl_gss_log_error(data, "gss_import_name() failed: ",
+ major_status, minor_status);
+
+ free(spn);
+
+ return CURLE_AUTH_ERROR;
+ }
+
+ free(spn);
+ }
+
+ if(chlg64 && *chlg64) {
+ /* Decode the base-64 encoded challenge message */
+ if(*chlg64 != '=') {
+ result = Curl_base64_decode(chlg64, &chlg, &chlglen);
+ if(result)
+ return result;
+ }
+
+ /* Ensure we have a valid challenge message */
+ if(!chlg) {
+ infof(data, "SPNEGO handshake failure (empty challenge message)");
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+
+ /* Setup the challenge "input" security buffer */
+ input_token.value = chlg;
+ input_token.length = chlglen;
+ }
+
+ /* Generate our challenge-response message */
+ major_status = Curl_gss_init_sec_context(data,
+ &minor_status,
+ &nego->context,
+ nego->spn,
+ &Curl_spnego_mech_oid,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &input_token,
+ &output_token,
+ TRUE,
+ NULL);
+
+ /* Free the decoded challenge as it is not required anymore */
+ Curl_safefree(input_token.value);
+
+ nego->status = major_status;
+ if(GSS_ERROR(major_status)) {
+ if(output_token.value)
+ gss_release_buffer(&unused_status, &output_token);
+
+ Curl_gss_log_error(data, "gss_init_sec_context() failed: ",
+ major_status, minor_status);
+
+ return CURLE_AUTH_ERROR;
+ }
+
+ if(!output_token.value || !output_token.length) {
+ if(output_token.value)
+ gss_release_buffer(&unused_status, &output_token);
+
+ return CURLE_AUTH_ERROR;
+ }
+
+ /* Free previous token */
+ if(nego->output_token.length && nego->output_token.value)
+ gss_release_buffer(&unused_status, &nego->output_token);
+
+ nego->output_token = output_token;
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_auth_create_spnego_message()
+ *
+ * This is used to generate an already encoded SPNEGO (Negotiate) response
+ * message ready for sending to the recipient.
+ *
+ * Parameters:
+ *
+ * data [in] - The session handle.
+ * nego [in/out] - The Negotiate data struct being used and modified.
+ * outptr [in/out] - The address where a pointer to newly allocated memory
+ * holding the result will be stored upon completion.
+ * outlen [out] - The length of the output message.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego,
+ char **outptr, size_t *outlen)
+{
+ CURLcode result;
+ OM_uint32 minor_status;
+
+ /* Base64 encode the already generated response */
+ result = Curl_base64_encode(nego->output_token.value,
+ nego->output_token.length,
+ outptr, outlen);
+
+ if(result) {
+ gss_release_buffer(&minor_status, &nego->output_token);
+ nego->output_token.value = NULL;
+ nego->output_token.length = 0;
+
+ return result;
+ }
+
+ if(!*outptr || !*outlen) {
+ gss_release_buffer(&minor_status, &nego->output_token);
+ nego->output_token.value = NULL;
+ nego->output_token.length = 0;
+
+ return CURLE_REMOTE_ACCESS_DENIED;
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Curl_auth_cleanup_spnego()
+ *
+ * This is used to clean up the SPNEGO (Negotiate) specific data.
+ *
+ * Parameters:
+ *
+ * nego [in/out] - The Negotiate data struct being cleaned up.
+ *
+ */
+void Curl_auth_cleanup_spnego(struct negotiatedata *nego)
+{
+ OM_uint32 minor_status;
+
+ /* Free our security context */
+ if(nego->context != GSS_C_NO_CONTEXT) {
+ gss_delete_sec_context(&minor_status, &nego->context, GSS_C_NO_BUFFER);
+ nego->context = GSS_C_NO_CONTEXT;
+ }
+
+ /* Free the output token */
+ if(nego->output_token.value) {
+ gss_release_buffer(&minor_status, &nego->output_token);
+ nego->output_token.value = NULL;
+ nego->output_token.length = 0;
+
+ }
+
+ /* Free the SPN */
+ if(nego->spn != GSS_C_NO_NAME) {
+ gss_release_name(&minor_status, &nego->spn);
+ nego->spn = GSS_C_NO_NAME;
+ }
+
+ /* Reset any variables */
+ nego->status = 0;
+ nego->noauthpersist = FALSE;
+ nego->havenoauthpersist = FALSE;
+ nego->havenegdata = FALSE;
+ nego->havemultiplerequests = FALSE;
+}
+
+#endif /* HAVE_GSSAPI && USE_SPNEGO */
diff --git a/libs/libcurl/src/vauth/spnego_sspi.c b/libs/libcurl/src/vauth/spnego_sspi.c index d845caca6c..8efcd2693c 100644 --- a/libs/libcurl/src/vauth/spnego_sspi.c +++ b/libs/libcurl/src/vauth/spnego_sspi.c @@ -1,364 +1,364 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - * RFC4178 Simple and Protected GSS-API Negotiation Mechanism - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if defined(USE_WINDOWS_SSPI) && defined(USE_SPNEGO) - -#include <curl/curl.h> - -#include "vauth/vauth.h" -#include "urldata.h" -#include "curl_base64.h" -#include "warnless.h" -#include "curl_multibyte.h" -#include "sendf.h" -#include "strerror.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* - * Curl_auth_is_spnego_supported() - * - * This is used to evaluate if SPNEGO (Negotiate) is supported. - * - * Parameters: None - * - * Returns TRUE if Negotiate is supported by Windows SSPI. - */ -bool Curl_auth_is_spnego_supported(void) -{ - PSecPkgInfo SecurityPackage; - SECURITY_STATUS status; - - /* Query the security package for Negotiate */ - status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) - TEXT(SP_NAME_NEGOTIATE), - &SecurityPackage); - - /* Release the package buffer as it is not required anymore */ - if(status == SEC_E_OK) { - s_pSecFn->FreeContextBuffer(SecurityPackage); - } - - - return (status == SEC_E_OK ? TRUE : FALSE); -} - -/* - * Curl_auth_decode_spnego_message() - * - * This is used to decode an already encoded SPNEGO (Negotiate) challenge - * message. - * - * Parameters: - * - * data [in] - The session handle. - * user [in] - The user name in the format User or Domain\User. - * password [in] - The user's password. - * service [in] - The service type such as http, smtp, pop or imap. - * host [in] - The host name. - * chlg64 [in] - The optional base64 encoded challenge message. - * nego [in/out] - The Negotiate data struct being used and modified. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, - const char *user, - const char *password, - const char *service, - const char *host, - const char *chlg64, - struct negotiatedata *nego) -{ - CURLcode result = CURLE_OK; - size_t chlglen = 0; - unsigned char *chlg = NULL; - PSecPkgInfo SecurityPackage; - SecBuffer chlg_buf[2]; - SecBuffer resp_buf; - SecBufferDesc chlg_desc; - SecBufferDesc resp_desc; - unsigned long attrs; - TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ - -#if defined(CURL_DISABLE_VERBOSE_STRINGS) - (void) data; -#endif - - if(nego->context && nego->status == SEC_E_OK) { - /* We finished successfully our part of authentication, but server - * rejected it (since we're again here). Exit with an error since we - * can't invent anything better */ - Curl_auth_cleanup_spnego(nego); - return CURLE_LOGIN_DENIED; - } - - if(!nego->spn) { - /* Generate our SPN */ - nego->spn = Curl_auth_build_spn(service, host, NULL); - if(!nego->spn) - return CURLE_OUT_OF_MEMORY; - } - - if(!nego->output_token) { - /* Query the security package for Negotiate */ - nego->status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) - TEXT(SP_NAME_NEGOTIATE), - &SecurityPackage); - if(nego->status != SEC_E_OK) { - failf(data, "SSPI: couldn't get auth info"); - return CURLE_AUTH_ERROR; - } - - nego->token_max = SecurityPackage->cbMaxToken; - - /* Release the package buffer as it is not required anymore */ - s_pSecFn->FreeContextBuffer(SecurityPackage); - - /* Allocate our output buffer */ - nego->output_token = malloc(nego->token_max); - if(!nego->output_token) - return CURLE_OUT_OF_MEMORY; - } - - if(!nego->credentials) { - /* Do we have credentials to use or are we using single sign-on? */ - if(user && *user) { - /* Populate our identity structure */ - result = Curl_create_sspi_identity(user, password, &nego->identity); - if(result) - return result; - - /* Allow proper cleanup of the identity structure */ - nego->p_identity = &nego->identity; - } - else - /* Use the current Windows user */ - nego->p_identity = NULL; - - /* Allocate our credentials handle */ - nego->credentials = calloc(1, sizeof(CredHandle)); - if(!nego->credentials) - return CURLE_OUT_OF_MEMORY; - - /* Acquire our credentials handle */ - nego->status = - s_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *)TEXT(SP_NAME_NEGOTIATE), - SECPKG_CRED_OUTBOUND, NULL, - nego->p_identity, NULL, NULL, - nego->credentials, &expiry); - if(nego->status != SEC_E_OK) - return CURLE_AUTH_ERROR; - - /* Allocate our new context handle */ - nego->context = calloc(1, sizeof(CtxtHandle)); - if(!nego->context) - return CURLE_OUT_OF_MEMORY; - } - - if(chlg64 && *chlg64) { - /* Decode the base-64 encoded challenge message */ - if(*chlg64 != '=') { - result = Curl_base64_decode(chlg64, &chlg, &chlglen); - if(result) - return result; - } - - /* Ensure we have a valid challenge message */ - if(!chlg) { - infof(data, "SPNEGO handshake failure (empty challenge message)"); - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Setup the challenge "input" security buffer */ - chlg_desc.ulVersion = SECBUFFER_VERSION; - chlg_desc.cBuffers = 1; - chlg_desc.pBuffers = &chlg_buf[0]; - chlg_buf[0].BufferType = SECBUFFER_TOKEN; - chlg_buf[0].pvBuffer = chlg; - chlg_buf[0].cbBuffer = curlx_uztoul(chlglen); - -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS - /* ssl context comes from Schannel. - * When extended protection is used in IIS server, - * we have to pass a second SecBuffer to the SecBufferDesc - * otherwise IIS will not pass the authentication (401 response). - * Minimum supported version is Windows 7. - * https://docs.microsoft.com/en-us/security-updates - * /SecurityAdvisories/2009/973811 - */ - if(nego->sslContext) { - SEC_CHANNEL_BINDINGS channelBindings; - SecPkgContext_Bindings pkgBindings; - pkgBindings.Bindings = &channelBindings; - nego->status = s_pSecFn->QueryContextAttributes( - nego->sslContext, - SECPKG_ATTR_ENDPOINT_BINDINGS, - &pkgBindings - ); - if(nego->status == SEC_E_OK) { - chlg_desc.cBuffers++; - chlg_buf[1].BufferType = SECBUFFER_CHANNEL_BINDINGS; - chlg_buf[1].cbBuffer = pkgBindings.BindingsLength; - chlg_buf[1].pvBuffer = pkgBindings.Bindings; - } - } -#endif - } - - /* Setup the response "output" security buffer */ - resp_desc.ulVersion = SECBUFFER_VERSION; - resp_desc.cBuffers = 1; - resp_desc.pBuffers = &resp_buf; - resp_buf.BufferType = SECBUFFER_TOKEN; - resp_buf.pvBuffer = nego->output_token; - resp_buf.cbBuffer = curlx_uztoul(nego->token_max); - - /* Generate our challenge-response message */ - nego->status = s_pSecFn->InitializeSecurityContext(nego->credentials, - chlg ? nego->context : - NULL, - nego->spn, - ISC_REQ_CONFIDENTIALITY, - 0, SECURITY_NATIVE_DREP, - chlg ? &chlg_desc : NULL, - 0, nego->context, - &resp_desc, &attrs, - &expiry); - - /* Free the decoded challenge as it is not required anymore */ - free(chlg); - - if(GSS_ERROR(nego->status)) { - char buffer[STRERROR_LEN]; - failf(data, "InitializeSecurityContext failed: %s", - Curl_sspi_strerror(nego->status, buffer, sizeof(buffer))); - - if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY) - return CURLE_OUT_OF_MEMORY; - - return CURLE_AUTH_ERROR; - } - - if(nego->status == SEC_I_COMPLETE_NEEDED || - nego->status == SEC_I_COMPLETE_AND_CONTINUE) { - nego->status = s_pSecFn->CompleteAuthToken(nego->context, &resp_desc); - if(GSS_ERROR(nego->status)) { - char buffer[STRERROR_LEN]; - failf(data, "CompleteAuthToken failed: %s", - Curl_sspi_strerror(nego->status, buffer, sizeof(buffer))); - - if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY) - return CURLE_OUT_OF_MEMORY; - - return CURLE_AUTH_ERROR; - } - } - - nego->output_token_length = resp_buf.cbBuffer; - - return result; -} - -/* - * Curl_auth_create_spnego_message() - * - * This is used to generate an already encoded SPNEGO (Negotiate) response - * message ready for sending to the recipient. - * - * Parameters: - * - * data [in] - The session handle. - * nego [in/out] - The Negotiate data struct being used and modified. - * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. - * outlen [out] - The length of the output message. - * - * Returns CURLE_OK on success. - */ -CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego, - char **outptr, size_t *outlen) -{ - /* Base64 encode the already generated response */ - CURLcode result = Curl_base64_encode((const char *) nego->output_token, - nego->output_token_length, outptr, - outlen); - if(!result && (!*outptr || !*outlen)) { - free(*outptr); - result = CURLE_REMOTE_ACCESS_DENIED; - } - - return result; -} - -/* - * Curl_auth_cleanup_spnego() - * - * This is used to clean up the SPNEGO (Negotiate) specific data. - * - * Parameters: - * - * nego [in/out] - The Negotiate data struct being cleaned up. - * - */ -void Curl_auth_cleanup_spnego(struct negotiatedata *nego) -{ - /* Free our security context */ - if(nego->context) { - s_pSecFn->DeleteSecurityContext(nego->context); - free(nego->context); - nego->context = NULL; - } - - /* Free our credentials handle */ - if(nego->credentials) { - s_pSecFn->FreeCredentialsHandle(nego->credentials); - free(nego->credentials); - nego->credentials = NULL; - } - - /* Free our identity */ - Curl_sspi_free_identity(nego->p_identity); - nego->p_identity = NULL; - - /* Free the SPN and output token */ - Curl_safefree(nego->spn); - Curl_safefree(nego->output_token); - - /* Reset any variables */ - nego->status = 0; - nego->token_max = 0; - nego->noauthpersist = FALSE; - nego->havenoauthpersist = FALSE; - nego->havenegdata = FALSE; - nego->havemultiplerequests = FALSE; -} - -#endif /* USE_WINDOWS_SSPI && USE_SPNEGO */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ * RFC4178 Simple and Protected GSS-API Negotiation Mechanism
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if defined(USE_WINDOWS_SSPI) && defined(USE_SPNEGO)
+
+#include <curl/curl.h>
+
+#include "vauth/vauth.h"
+#include "urldata.h"
+#include "curl_base64.h"
+#include "warnless.h"
+#include "curl_multibyte.h"
+#include "sendf.h"
+#include "strerror.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * Curl_auth_is_spnego_supported()
+ *
+ * This is used to evaluate if SPNEGO (Negotiate) is supported.
+ *
+ * Parameters: None
+ *
+ * Returns TRUE if Negotiate is supported by Windows SSPI.
+ */
+bool Curl_auth_is_spnego_supported(void)
+{
+ PSecPkgInfo SecurityPackage;
+ SECURITY_STATUS status;
+
+ /* Query the security package for Negotiate */
+ status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
+ TEXT(SP_NAME_NEGOTIATE),
+ &SecurityPackage);
+
+ /* Release the package buffer as it is not required anymore */
+ if(status == SEC_E_OK) {
+ s_pSecFn->FreeContextBuffer(SecurityPackage);
+ }
+
+
+ return (status == SEC_E_OK ? TRUE : FALSE);
+}
+
+/*
+ * Curl_auth_decode_spnego_message()
+ *
+ * This is used to decode an already encoded SPNEGO (Negotiate) challenge
+ * message.
+ *
+ * Parameters:
+ *
+ * data [in] - The session handle.
+ * user [in] - The user name in the format User or Domain\User.
+ * password [in] - The user's password.
+ * service [in] - The service type such as http, smtp, pop or imap.
+ * host [in] - The host name.
+ * chlg64 [in] - The optional base64 encoded challenge message.
+ * nego [in/out] - The Negotiate data struct being used and modified.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
+ const char *user,
+ const char *password,
+ const char *service,
+ const char *host,
+ const char *chlg64,
+ struct negotiatedata *nego)
+{
+ CURLcode result = CURLE_OK;
+ size_t chlglen = 0;
+ unsigned char *chlg = NULL;
+ PSecPkgInfo SecurityPackage;
+ SecBuffer chlg_buf[2];
+ SecBuffer resp_buf;
+ SecBufferDesc chlg_desc;
+ SecBufferDesc resp_desc;
+ unsigned long attrs;
+ TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
+
+#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+ (void) data;
+#endif
+
+ if(nego->context && nego->status == SEC_E_OK) {
+ /* We finished successfully our part of authentication, but server
+ * rejected it (since we're again here). Exit with an error since we
+ * can't invent anything better */
+ Curl_auth_cleanup_spnego(nego);
+ return CURLE_LOGIN_DENIED;
+ }
+
+ if(!nego->spn) {
+ /* Generate our SPN */
+ nego->spn = Curl_auth_build_spn(service, host, NULL);
+ if(!nego->spn)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(!nego->output_token) {
+ /* Query the security package for Negotiate */
+ nego->status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
+ TEXT(SP_NAME_NEGOTIATE),
+ &SecurityPackage);
+ if(nego->status != SEC_E_OK) {
+ failf(data, "SSPI: couldn't get auth info");
+ return CURLE_AUTH_ERROR;
+ }
+
+ nego->token_max = SecurityPackage->cbMaxToken;
+
+ /* Release the package buffer as it is not required anymore */
+ s_pSecFn->FreeContextBuffer(SecurityPackage);
+
+ /* Allocate our output buffer */
+ nego->output_token = malloc(nego->token_max);
+ if(!nego->output_token)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(!nego->credentials) {
+ /* Do we have credentials to use or are we using single sign-on? */
+ if(user && *user) {
+ /* Populate our identity structure */
+ result = Curl_create_sspi_identity(user, password, &nego->identity);
+ if(result)
+ return result;
+
+ /* Allow proper cleanup of the identity structure */
+ nego->p_identity = &nego->identity;
+ }
+ else
+ /* Use the current Windows user */
+ nego->p_identity = NULL;
+
+ /* Allocate our credentials handle */
+ nego->credentials = calloc(1, sizeof(CredHandle));
+ if(!nego->credentials)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Acquire our credentials handle */
+ nego->status =
+ s_pSecFn->AcquireCredentialsHandle(NULL,
+ (TCHAR *)TEXT(SP_NAME_NEGOTIATE),
+ SECPKG_CRED_OUTBOUND, NULL,
+ nego->p_identity, NULL, NULL,
+ nego->credentials, &expiry);
+ if(nego->status != SEC_E_OK)
+ return CURLE_AUTH_ERROR;
+
+ /* Allocate our new context handle */
+ nego->context = calloc(1, sizeof(CtxtHandle));
+ if(!nego->context)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(chlg64 && *chlg64) {
+ /* Decode the base-64 encoded challenge message */
+ if(*chlg64 != '=') {
+ result = Curl_base64_decode(chlg64, &chlg, &chlglen);
+ if(result)
+ return result;
+ }
+
+ /* Ensure we have a valid challenge message */
+ if(!chlg) {
+ infof(data, "SPNEGO handshake failure (empty challenge message)");
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
+
+ /* Setup the challenge "input" security buffer */
+ chlg_desc.ulVersion = SECBUFFER_VERSION;
+ chlg_desc.cBuffers = 1;
+ chlg_desc.pBuffers = &chlg_buf[0];
+ chlg_buf[0].BufferType = SECBUFFER_TOKEN;
+ chlg_buf[0].pvBuffer = chlg;
+ chlg_buf[0].cbBuffer = curlx_uztoul(chlglen);
+
+#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
+ /* ssl context comes from Schannel.
+ * When extended protection is used in IIS server,
+ * we have to pass a second SecBuffer to the SecBufferDesc
+ * otherwise IIS will not pass the authentication (401 response).
+ * Minimum supported version is Windows 7.
+ * https://docs.microsoft.com/en-us/security-updates
+ * /SecurityAdvisories/2009/973811
+ */
+ if(nego->sslContext) {
+ SEC_CHANNEL_BINDINGS channelBindings;
+ SecPkgContext_Bindings pkgBindings;
+ pkgBindings.Bindings = &channelBindings;
+ nego->status = s_pSecFn->QueryContextAttributes(
+ nego->sslContext,
+ SECPKG_ATTR_ENDPOINT_BINDINGS,
+ &pkgBindings
+ );
+ if(nego->status == SEC_E_OK) {
+ chlg_desc.cBuffers++;
+ chlg_buf[1].BufferType = SECBUFFER_CHANNEL_BINDINGS;
+ chlg_buf[1].cbBuffer = pkgBindings.BindingsLength;
+ chlg_buf[1].pvBuffer = pkgBindings.Bindings;
+ }
+ }
+#endif
+ }
+
+ /* Setup the response "output" security buffer */
+ resp_desc.ulVersion = SECBUFFER_VERSION;
+ resp_desc.cBuffers = 1;
+ resp_desc.pBuffers = &resp_buf;
+ resp_buf.BufferType = SECBUFFER_TOKEN;
+ resp_buf.pvBuffer = nego->output_token;
+ resp_buf.cbBuffer = curlx_uztoul(nego->token_max);
+
+ /* Generate our challenge-response message */
+ nego->status = s_pSecFn->InitializeSecurityContext(nego->credentials,
+ chlg ? nego->context :
+ NULL,
+ nego->spn,
+ ISC_REQ_CONFIDENTIALITY,
+ 0, SECURITY_NATIVE_DREP,
+ chlg ? &chlg_desc : NULL,
+ 0, nego->context,
+ &resp_desc, &attrs,
+ &expiry);
+
+ /* Free the decoded challenge as it is not required anymore */
+ free(chlg);
+
+ if(GSS_ERROR(nego->status)) {
+ char buffer[STRERROR_LEN];
+ failf(data, "InitializeSecurityContext failed: %s",
+ Curl_sspi_strerror(nego->status, buffer, sizeof(buffer)));
+
+ if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY)
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_AUTH_ERROR;
+ }
+
+ if(nego->status == SEC_I_COMPLETE_NEEDED ||
+ nego->status == SEC_I_COMPLETE_AND_CONTINUE) {
+ nego->status = s_pSecFn->CompleteAuthToken(nego->context, &resp_desc);
+ if(GSS_ERROR(nego->status)) {
+ char buffer[STRERROR_LEN];
+ failf(data, "CompleteAuthToken failed: %s",
+ Curl_sspi_strerror(nego->status, buffer, sizeof(buffer)));
+
+ if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY)
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_AUTH_ERROR;
+ }
+ }
+
+ nego->output_token_length = resp_buf.cbBuffer;
+
+ return result;
+}
+
+/*
+ * Curl_auth_create_spnego_message()
+ *
+ * This is used to generate an already encoded SPNEGO (Negotiate) response
+ * message ready for sending to the recipient.
+ *
+ * Parameters:
+ *
+ * data [in] - The session handle.
+ * nego [in/out] - The Negotiate data struct being used and modified.
+ * outptr [in/out] - The address where a pointer to newly allocated memory
+ * holding the result will be stored upon completion.
+ * outlen [out] - The length of the output message.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego,
+ char **outptr, size_t *outlen)
+{
+ /* Base64 encode the already generated response */
+ CURLcode result = Curl_base64_encode((const char *) nego->output_token,
+ nego->output_token_length, outptr,
+ outlen);
+ if(!result && (!*outptr || !*outlen)) {
+ free(*outptr);
+ result = CURLE_REMOTE_ACCESS_DENIED;
+ }
+
+ return result;
+}
+
+/*
+ * Curl_auth_cleanup_spnego()
+ *
+ * This is used to clean up the SPNEGO (Negotiate) specific data.
+ *
+ * Parameters:
+ *
+ * nego [in/out] - The Negotiate data struct being cleaned up.
+ *
+ */
+void Curl_auth_cleanup_spnego(struct negotiatedata *nego)
+{
+ /* Free our security context */
+ if(nego->context) {
+ s_pSecFn->DeleteSecurityContext(nego->context);
+ free(nego->context);
+ nego->context = NULL;
+ }
+
+ /* Free our credentials handle */
+ if(nego->credentials) {
+ s_pSecFn->FreeCredentialsHandle(nego->credentials);
+ free(nego->credentials);
+ nego->credentials = NULL;
+ }
+
+ /* Free our identity */
+ Curl_sspi_free_identity(nego->p_identity);
+ nego->p_identity = NULL;
+
+ /* Free the SPN and output token */
+ Curl_safefree(nego->spn);
+ Curl_safefree(nego->output_token);
+
+ /* Reset any variables */
+ nego->status = 0;
+ nego->token_max = 0;
+ nego->noauthpersist = FALSE;
+ nego->havenoauthpersist = FALSE;
+ nego->havenegdata = FALSE;
+ nego->havemultiplerequests = FALSE;
+}
+
+#endif /* USE_WINDOWS_SSPI && USE_SPNEGO */
diff --git a/libs/libcurl/src/vauth/vauth.c b/libs/libcurl/src/vauth/vauth.c index 58fe05139d..8f83f60aa3 100644 --- a/libs/libcurl/src/vauth/vauth.c +++ b/libs/libcurl/src/vauth/vauth.c @@ -1,163 +1,163 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2014 - 2022, Steve Holme, <steve_holme@hotmail.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" - -#include <curl/curl.h> - -#include "vauth.h" -#include "urldata.h" -#include "strcase.h" -#include "curl_multibyte.h" -#include "curl_printf.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* - * Curl_auth_build_spn() - * - * This is used to build a SPN string in the following formats: - * - * service/host@realm (Not currently used) - * service/host (Not used by GSS-API) - * service@realm (Not used by Windows SSPI) - * - * Parameters: - * - * service [in] - The service type such as http, smtp, pop or imap. - * host [in] - The host name. - * realm [in] - The realm. - * - * Returns a pointer to the newly allocated SPN. - */ -#if !defined(USE_WINDOWS_SSPI) -char *Curl_auth_build_spn(const char *service, const char *host, - const char *realm) -{ - char *spn = NULL; - - /* Generate our SPN */ - if(host && realm) - spn = aprintf("%s/%s@%s", service, host, realm); - else if(host) - spn = aprintf("%s/%s", service, host); - else if(realm) - spn = aprintf("%s@%s", service, realm); - - /* Return our newly allocated SPN */ - return spn; -} -#else -TCHAR *Curl_auth_build_spn(const char *service, const char *host, - const char *realm) -{ - char *utf8_spn = NULL; - TCHAR *tchar_spn = NULL; - TCHAR *dupe_tchar_spn = NULL; - - (void) realm; - - /* Note: We could use DsMakeSPN() or DsClientMakeSpnForTargetServer() rather - than doing this ourselves but the first is only available in Windows XP - and Windows Server 2003 and the latter is only available in Windows 2000 - but not Windows95/98/ME or Windows NT4.0 unless the Active Directory - Client Extensions are installed. As such it is far simpler for us to - formulate the SPN instead. */ - - /* Generate our UTF8 based SPN */ - utf8_spn = aprintf("%s/%s", service, host); - if(!utf8_spn) - return NULL; - - /* Allocate and return a TCHAR based SPN. Since curlx_convert_UTF8_to_tchar - must be freed by curlx_unicodefree we'll dupe the result so that the - pointer this function returns can be normally free'd. */ - tchar_spn = curlx_convert_UTF8_to_tchar(utf8_spn); - free(utf8_spn); - if(!tchar_spn) - return NULL; - dupe_tchar_spn = _tcsdup(tchar_spn); - curlx_unicodefree(tchar_spn); - return dupe_tchar_spn; -} -#endif /* USE_WINDOWS_SSPI */ - -/* - * Curl_auth_user_contains_domain() - * - * This is used to test if the specified user contains a Windows domain name as - * follows: - * - * Domain\User (Down-level Logon Name) - * Domain/User (curl Down-level format - for compatibility with existing code) - * User@Domain (User Principal Name) - * - * Note: The user name may be empty when using a GSS-API library or Windows - * SSPI as the user and domain are either obtained from the credentials cache - * when using GSS-API or via the currently logged in user's credentials when - * using Windows SSPI. - * - * Parameters: - * - * user [in] - The user name. - * - * Returns TRUE on success; otherwise FALSE. - */ -bool Curl_auth_user_contains_domain(const char *user) -{ - bool valid = FALSE; - - if(user && *user) { - /* Check we have a domain name or UPN present */ - char *p = strpbrk(user, "\\/@"); - - valid = (p != NULL && p > user && p < user + strlen(user) - 1 ? TRUE : - FALSE); - } -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - else - /* User and domain are obtained from the GSS-API credentials cache or the - currently logged in user from Windows */ - valid = TRUE; -#endif - - return valid; -} - -/* - * Curl_auth_ollowed_to_host() tells if authentication, cookies or other - * "sensitive data" can (still) be sent to this host. - */ -bool Curl_auth_allowed_to_host(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - return (!data->state.this_is_a_follow || - data->set.allow_auth_to_other_hosts || - (data->state.first_host && - strcasecompare(data->state.first_host, conn->host.name) && - (data->state.first_remote_port == conn->remote_port) && - (data->state.first_remote_protocol == conn->handler->protocol))); -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2014 - 2022, Steve Holme, <steve_holme@hotmail.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"
+
+#include <curl/curl.h>
+
+#include "vauth.h"
+#include "urldata.h"
+#include "strcase.h"
+#include "curl_multibyte.h"
+#include "curl_printf.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * Curl_auth_build_spn()
+ *
+ * This is used to build a SPN string in the following formats:
+ *
+ * service/host@realm (Not currently used)
+ * service/host (Not used by GSS-API)
+ * service@realm (Not used by Windows SSPI)
+ *
+ * Parameters:
+ *
+ * service [in] - The service type such as http, smtp, pop or imap.
+ * host [in] - The host name.
+ * realm [in] - The realm.
+ *
+ * Returns a pointer to the newly allocated SPN.
+ */
+#if !defined(USE_WINDOWS_SSPI)
+char *Curl_auth_build_spn(const char *service, const char *host,
+ const char *realm)
+{
+ char *spn = NULL;
+
+ /* Generate our SPN */
+ if(host && realm)
+ spn = aprintf("%s/%s@%s", service, host, realm);
+ else if(host)
+ spn = aprintf("%s/%s", service, host);
+ else if(realm)
+ spn = aprintf("%s@%s", service, realm);
+
+ /* Return our newly allocated SPN */
+ return spn;
+}
+#else
+TCHAR *Curl_auth_build_spn(const char *service, const char *host,
+ const char *realm)
+{
+ char *utf8_spn = NULL;
+ TCHAR *tchar_spn = NULL;
+ TCHAR *dupe_tchar_spn = NULL;
+
+ (void) realm;
+
+ /* Note: We could use DsMakeSPN() or DsClientMakeSpnForTargetServer() rather
+ than doing this ourselves but the first is only available in Windows XP
+ and Windows Server 2003 and the latter is only available in Windows 2000
+ but not Windows95/98/ME or Windows NT4.0 unless the Active Directory
+ Client Extensions are installed. As such it is far simpler for us to
+ formulate the SPN instead. */
+
+ /* Generate our UTF8 based SPN */
+ utf8_spn = aprintf("%s/%s", service, host);
+ if(!utf8_spn)
+ return NULL;
+
+ /* Allocate and return a TCHAR based SPN. Since curlx_convert_UTF8_to_tchar
+ must be freed by curlx_unicodefree we'll dupe the result so that the
+ pointer this function returns can be normally free'd. */
+ tchar_spn = curlx_convert_UTF8_to_tchar(utf8_spn);
+ free(utf8_spn);
+ if(!tchar_spn)
+ return NULL;
+ dupe_tchar_spn = _tcsdup(tchar_spn);
+ curlx_unicodefree(tchar_spn);
+ return dupe_tchar_spn;
+}
+#endif /* USE_WINDOWS_SSPI */
+
+/*
+ * Curl_auth_user_contains_domain()
+ *
+ * This is used to test if the specified user contains a Windows domain name as
+ * follows:
+ *
+ * Domain\User (Down-level Logon Name)
+ * Domain/User (curl Down-level format - for compatibility with existing code)
+ * User@Domain (User Principal Name)
+ *
+ * Note: The user name may be empty when using a GSS-API library or Windows
+ * SSPI as the user and domain are either obtained from the credentials cache
+ * when using GSS-API or via the currently logged in user's credentials when
+ * using Windows SSPI.
+ *
+ * Parameters:
+ *
+ * user [in] - The user name.
+ *
+ * Returns TRUE on success; otherwise FALSE.
+ */
+bool Curl_auth_user_contains_domain(const char *user)
+{
+ bool valid = FALSE;
+
+ if(user && *user) {
+ /* Check we have a domain name or UPN present */
+ char *p = strpbrk(user, "\\/@");
+
+ valid = (p != NULL && p > user && p < user + strlen(user) - 1 ? TRUE :
+ FALSE);
+ }
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+ else
+ /* User and domain are obtained from the GSS-API credentials cache or the
+ currently logged in user from Windows */
+ valid = TRUE;
+#endif
+
+ return valid;
+}
+
+/*
+ * Curl_auth_ollowed_to_host() tells if authentication, cookies or other
+ * "sensitive data" can (still) be sent to this host.
+ */
+bool Curl_auth_allowed_to_host(struct Curl_easy *data)
+{
+ struct connectdata *conn = data->conn;
+ return (!data->state.this_is_a_follow ||
+ data->set.allow_auth_to_other_hosts ||
+ (data->state.first_host &&
+ strcasecompare(data->state.first_host, conn->host.name) &&
+ (data->state.first_remote_port == conn->remote_port) &&
+ (data->state.first_remote_protocol == conn->handler->protocol)));
+}
diff --git a/libs/libcurl/src/vauth/vauth.h b/libs/libcurl/src/vauth/vauth.h index af27f01dfb..a781fb9fcc 100644 --- a/libs/libcurl/src/vauth/vauth.h +++ b/libs/libcurl/src/vauth/vauth.h @@ -1,238 +1,238 @@ -#ifndef HEADER_CURL_VAUTH_H -#define HEADER_CURL_VAUTH_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2014 - 2022, Steve Holme, <steve_holme@hotmail.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/curl.h> - -#include "bufref.h" - -struct Curl_easy; - -#if !defined(CURL_DISABLE_CRYPTO_AUTH) -struct digestdata; -#endif - -#if defined(USE_NTLM) -struct ntlmdata; -#endif - -#if defined(USE_KERBEROS5) -struct kerberos5data; -#endif - -#if (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) && defined(USE_SPNEGO) -struct negotiatedata; -#endif - -#if defined(USE_GSASL) -struct gsasldata; -#endif - -#if defined(USE_WINDOWS_SSPI) -#define GSS_ERROR(status) ((status) & 0x80000000) -#endif - -/* - * Curl_auth_allowed_to_host() tells if authentication, cookies or other - * "sensitive data" can (still) be sent to this host. - */ -bool Curl_auth_allowed_to_host(struct Curl_easy *data); - -/* This is used to build a SPN string */ -#if !defined(USE_WINDOWS_SSPI) -char *Curl_auth_build_spn(const char *service, const char *host, - const char *realm); -#else -TCHAR *Curl_auth_build_spn(const char *service, const char *host, - const char *realm); -#endif - -/* This is used to test if the user contains a Windows domain name */ -bool Curl_auth_user_contains_domain(const char *user); - -/* This is used to generate a PLAIN cleartext message */ -CURLcode Curl_auth_create_plain_message(const char *authzid, - const char *authcid, - const char *passwd, - struct bufref *out); - -/* This is used to generate a LOGIN cleartext message */ -CURLcode Curl_auth_create_login_message(const char *value, - struct bufref *out); - -/* This is used to generate an EXTERNAL cleartext message */ -CURLcode Curl_auth_create_external_message(const char *user, - struct bufref *out); - -#if !defined(CURL_DISABLE_CRYPTO_AUTH) -/* This is used to generate a CRAM-MD5 response message */ -CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, - const char *userp, - const char *passwdp, - struct bufref *out); - -/* This is used to evaluate if DIGEST is supported */ -bool Curl_auth_is_digest_supported(void); - -/* This is used to generate a base64 encoded DIGEST-MD5 response message */ -CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, - const struct bufref *chlg, - const char *userp, - const char *passwdp, - const char *service, - struct bufref *out); - -/* This is used to decode a HTTP DIGEST challenge message */ -CURLcode Curl_auth_decode_digest_http_message(const char *chlg, - struct digestdata *digest); - -/* This is used to generate a HTTP DIGEST response message */ -CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const unsigned char *request, - const unsigned char *uri, - struct digestdata *digest, - char **outptr, size_t *outlen); - -/* This is used to clean up the digest specific data */ -void Curl_auth_digest_cleanup(struct digestdata *digest); -#endif /* !CURL_DISABLE_CRYPTO_AUTH */ - -#ifdef USE_GSASL -/* This is used to evaluate if MECH is supported by gsasl */ -bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, - const char *mech, - struct gsasldata *gsasl); -/* This is used to start a gsasl method */ -CURLcode Curl_auth_gsasl_start(struct Curl_easy *data, - const char *userp, - const char *passwdp, - struct gsasldata *gsasl); - -/* This is used to process and generate a new SASL token */ -CURLcode Curl_auth_gsasl_token(struct Curl_easy *data, - const struct bufref *chlg, - struct gsasldata *gsasl, - struct bufref *out); - -/* This is used to clean up the gsasl specific data */ -void Curl_auth_gsasl_cleanup(struct gsasldata *digest); -#endif - -#if defined(USE_NTLM) -/* This is used to evaluate if NTLM is supported */ -bool Curl_auth_is_ntlm_supported(void); - -/* This is used to generate a base64 encoded NTLM type-1 message */ -CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const char *service, - const char *host, - struct ntlmdata *ntlm, - struct bufref *out); - -/* This is used to decode a base64 encoded NTLM type-2 message */ -CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, - const struct bufref *type2, - struct ntlmdata *ntlm); - -/* This is used to generate a base64 encoded NTLM type-3 message */ -CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - struct ntlmdata *ntlm, - struct bufref *out); - -/* This is used to clean up the NTLM specific data */ -void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm); -#endif /* USE_NTLM */ - -/* This is used to generate a base64 encoded OAuth 2.0 message */ -CURLcode Curl_auth_create_oauth_bearer_message(const char *user, - const char *host, - const long port, - const char *bearer, - struct bufref *out); - -/* This is used to generate a base64 encoded XOAuth 2.0 message */ -CURLcode Curl_auth_create_xoauth_bearer_message(const char *user, - const char *bearer, - struct bufref *out); - -#if defined(USE_KERBEROS5) -/* This is used to evaluate if GSSAPI (Kerberos V5) is supported */ -bool Curl_auth_is_gssapi_supported(void); - -/* This is used to generate a base64 encoded GSSAPI (Kerberos V5) user token - message */ -CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const char *service, - const char *host, - const bool mutual, - const struct bufref *chlg, - struct kerberos5data *krb5, - struct bufref *out); - -/* This is used to generate a base64 encoded GSSAPI (Kerberos V5) security - token message */ -CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, - const char *authzid, - const struct bufref *chlg, - struct kerberos5data *krb5, - struct bufref *out); - -/* This is used to clean up the GSSAPI specific data */ -void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5); -#endif /* USE_KERBEROS5 */ - -#if defined(USE_SPNEGO) -/* This is used to evaluate if SPNEGO (Negotiate) is supported */ -bool Curl_auth_is_spnego_supported(void); - -/* This is used to decode a base64 encoded SPNEGO (Negotiate) challenge - message */ -CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, - const char *user, - const char *passwood, - const char *service, - const char *host, - const char *chlg64, - struct negotiatedata *nego); - -/* This is used to generate a base64 encoded SPNEGO (Negotiate) response - message */ -CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego, - char **outptr, size_t *outlen); - -/* This is used to clean up the SPNEGO specific data */ -void Curl_auth_cleanup_spnego(struct negotiatedata *nego); - -#endif /* USE_SPNEGO */ - -#endif /* HEADER_CURL_VAUTH_H */ +#ifndef HEADER_CURL_VAUTH_H
+#define HEADER_CURL_VAUTH_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2014 - 2022, Steve Holme, <steve_holme@hotmail.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/curl.h>
+
+#include "bufref.h"
+
+struct Curl_easy;
+
+#if !defined(CURL_DISABLE_CRYPTO_AUTH)
+struct digestdata;
+#endif
+
+#if defined(USE_NTLM)
+struct ntlmdata;
+#endif
+
+#if defined(USE_KERBEROS5)
+struct kerberos5data;
+#endif
+
+#if (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) && defined(USE_SPNEGO)
+struct negotiatedata;
+#endif
+
+#if defined(USE_GSASL)
+struct gsasldata;
+#endif
+
+#if defined(USE_WINDOWS_SSPI)
+#define GSS_ERROR(status) ((status) & 0x80000000)
+#endif
+
+/*
+ * Curl_auth_allowed_to_host() tells if authentication, cookies or other
+ * "sensitive data" can (still) be sent to this host.
+ */
+bool Curl_auth_allowed_to_host(struct Curl_easy *data);
+
+/* This is used to build a SPN string */
+#if !defined(USE_WINDOWS_SSPI)
+char *Curl_auth_build_spn(const char *service, const char *host,
+ const char *realm);
+#else
+TCHAR *Curl_auth_build_spn(const char *service, const char *host,
+ const char *realm);
+#endif
+
+/* This is used to test if the user contains a Windows domain name */
+bool Curl_auth_user_contains_domain(const char *user);
+
+/* This is used to generate a PLAIN cleartext message */
+CURLcode Curl_auth_create_plain_message(const char *authzid,
+ const char *authcid,
+ const char *passwd,
+ struct bufref *out);
+
+/* This is used to generate a LOGIN cleartext message */
+CURLcode Curl_auth_create_login_message(const char *value,
+ struct bufref *out);
+
+/* This is used to generate an EXTERNAL cleartext message */
+CURLcode Curl_auth_create_external_message(const char *user,
+ struct bufref *out);
+
+#if !defined(CURL_DISABLE_CRYPTO_AUTH)
+/* This is used to generate a CRAM-MD5 response message */
+CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg,
+ const char *userp,
+ const char *passwdp,
+ struct bufref *out);
+
+/* This is used to evaluate if DIGEST is supported */
+bool Curl_auth_is_digest_supported(void);
+
+/* This is used to generate a base64 encoded DIGEST-MD5 response message */
+CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
+ const struct bufref *chlg,
+ const char *userp,
+ const char *passwdp,
+ const char *service,
+ struct bufref *out);
+
+/* This is used to decode an HTTP DIGEST challenge message */
+CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
+ struct digestdata *digest);
+
+/* This is used to generate an HTTP DIGEST response message */
+CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
+ const char *userp,
+ const char *passwdp,
+ const unsigned char *request,
+ const unsigned char *uri,
+ struct digestdata *digest,
+ char **outptr, size_t *outlen);
+
+/* This is used to clean up the digest specific data */
+void Curl_auth_digest_cleanup(struct digestdata *digest);
+#endif /* !CURL_DISABLE_CRYPTO_AUTH */
+
+#ifdef USE_GSASL
+/* This is used to evaluate if MECH is supported by gsasl */
+bool Curl_auth_gsasl_is_supported(struct Curl_easy *data,
+ const char *mech,
+ struct gsasldata *gsasl);
+/* This is used to start a gsasl method */
+CURLcode Curl_auth_gsasl_start(struct Curl_easy *data,
+ const char *userp,
+ const char *passwdp,
+ struct gsasldata *gsasl);
+
+/* This is used to process and generate a new SASL token */
+CURLcode Curl_auth_gsasl_token(struct Curl_easy *data,
+ const struct bufref *chlg,
+ struct gsasldata *gsasl,
+ struct bufref *out);
+
+/* This is used to clean up the gsasl specific data */
+void Curl_auth_gsasl_cleanup(struct gsasldata *digest);
+#endif
+
+#if defined(USE_NTLM)
+/* This is used to evaluate if NTLM is supported */
+bool Curl_auth_is_ntlm_supported(void);
+
+/* This is used to generate a base64 encoded NTLM type-1 message */
+CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
+ const char *userp,
+ const char *passwdp,
+ const char *service,
+ const char *host,
+ struct ntlmdata *ntlm,
+ struct bufref *out);
+
+/* This is used to decode a base64 encoded NTLM type-2 message */
+CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data,
+ const struct bufref *type2,
+ struct ntlmdata *ntlm);
+
+/* This is used to generate a base64 encoded NTLM type-3 message */
+CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
+ const char *userp,
+ const char *passwdp,
+ struct ntlmdata *ntlm,
+ struct bufref *out);
+
+/* This is used to clean up the NTLM specific data */
+void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm);
+#endif /* USE_NTLM */
+
+/* This is used to generate a base64 encoded OAuth 2.0 message */
+CURLcode Curl_auth_create_oauth_bearer_message(const char *user,
+ const char *host,
+ const long port,
+ const char *bearer,
+ struct bufref *out);
+
+/* This is used to generate a base64 encoded XOAuth 2.0 message */
+CURLcode Curl_auth_create_xoauth_bearer_message(const char *user,
+ const char *bearer,
+ struct bufref *out);
+
+#if defined(USE_KERBEROS5)
+/* This is used to evaluate if GSSAPI (Kerberos V5) is supported */
+bool Curl_auth_is_gssapi_supported(void);
+
+/* This is used to generate a base64 encoded GSSAPI (Kerberos V5) user token
+ message */
+CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
+ const char *userp,
+ const char *passwdp,
+ const char *service,
+ const char *host,
+ const bool mutual,
+ const struct bufref *chlg,
+ struct kerberos5data *krb5,
+ struct bufref *out);
+
+/* This is used to generate a base64 encoded GSSAPI (Kerberos V5) security
+ token message */
+CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
+ const char *authzid,
+ const struct bufref *chlg,
+ struct kerberos5data *krb5,
+ struct bufref *out);
+
+/* This is used to clean up the GSSAPI specific data */
+void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5);
+#endif /* USE_KERBEROS5 */
+
+#if defined(USE_SPNEGO)
+/* This is used to evaluate if SPNEGO (Negotiate) is supported */
+bool Curl_auth_is_spnego_supported(void);
+
+/* This is used to decode a base64 encoded SPNEGO (Negotiate) challenge
+ message */
+CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
+ const char *user,
+ const char *passwood,
+ const char *service,
+ const char *host,
+ const char *chlg64,
+ struct negotiatedata *nego);
+
+/* This is used to generate a base64 encoded SPNEGO (Negotiate) response
+ message */
+CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego,
+ char **outptr, size_t *outlen);
+
+/* This is used to clean up the SPNEGO specific data */
+void Curl_auth_cleanup_spnego(struct negotiatedata *nego);
+
+#endif /* USE_SPNEGO */
+
+#endif /* HEADER_CURL_VAUTH_H */
diff --git a/libs/libcurl/src/version.c b/libs/libcurl/src/version.c index f71f49e09a..741cb2120b 100644 --- a/libs/libcurl/src/version.c +++ b/libs/libcurl/src/version.c @@ -1,602 +1,661 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "vtls/vtls.h" -#include "http2.h" -#include "vssh/ssh.h" -#include "quic.h" -#include "curl_printf.h" -#include "easy_lock.h" - -#ifdef USE_ARES -# if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \ - defined(WIN32) -# define CARES_STATICLIB -# endif -# include <ares.h> -#endif - -#ifdef USE_LIBIDN2 -#include <idn2.h> -#endif - -#ifdef USE_LIBPSL -#include <libpsl.h> -#endif - -#ifdef USE_LIBRTMP -#include <librtmp/rtmp.h> -#endif - -#ifdef HAVE_LIBZ -#include <zlib.h> -#endif - -#ifdef HAVE_BROTLI -#include <brotli/decode.h> -#endif - -#ifdef HAVE_ZSTD -#include <zstd.h> -#endif - -#ifdef USE_GSASL -#include <gsasl.h> -#endif - -#ifdef USE_OPENLDAP -#include <ldap.h> -#endif - -#ifdef HAVE_BROTLI -static void brotli_version(char *buf, size_t bufsz) -{ - uint32_t brotli_version = BrotliDecoderVersion(); - unsigned int major = brotli_version >> 24; - unsigned int minor = (brotli_version & 0x00FFFFFF) >> 12; - unsigned int patch = brotli_version & 0x00000FFF; - (void)msnprintf(buf, bufsz, "%u.%u.%u", major, minor, patch); -} -#endif - -#ifdef HAVE_ZSTD -static void zstd_version(char *buf, size_t bufsz) -{ - unsigned long zstd_version = (unsigned long)ZSTD_versionNumber(); - unsigned int major = (unsigned int)(zstd_version / (100 * 100)); - unsigned int minor = (unsigned int)((zstd_version - - (major * 100 * 100)) / 100); - unsigned int patch = (unsigned int)(zstd_version - - (major * 100 * 100) - (minor * 100)); - (void)msnprintf(buf, bufsz, "%u.%u.%u", major, minor, patch); -} -#endif - -/* - * curl_version() returns a pointer to a static buffer. - * - * It is implemented to work multi-threaded by making sure repeated invokes - * generate the exact same string and never write any temporary data like - * zeros in the data. - */ - -#define VERSION_PARTS 16 /* number of substrings we can concatenate */ - -char *curl_version(void) -{ - static char out[300]; - char *outp; - size_t outlen; - const char *src[VERSION_PARTS]; -#ifdef USE_SSL - char ssl_version[200]; -#endif -#ifdef HAVE_LIBZ - char z_version[40]; -#endif -#ifdef HAVE_BROTLI - char br_version[40] = "brotli/"; -#endif -#ifdef HAVE_ZSTD - char zst_version[40] = "zstd/"; -#endif -#ifdef USE_ARES - char cares_version[40]; -#endif -#if defined(USE_LIBIDN2) - char idn_version[40]; -#endif -#ifdef USE_LIBPSL - char psl_version[40]; -#endif -#ifdef USE_SSH - char ssh_version[40]; -#endif -#ifdef USE_NGHTTP2 - char h2_version[40]; -#endif -#ifdef ENABLE_QUIC - char h3_version[40]; -#endif -#ifdef USE_LIBRTMP - char rtmp_version[40]; -#endif -#ifdef USE_HYPER - char hyper_buf[30]; -#endif -#ifdef USE_GSASL - char gsasl_buf[30]; -#endif -#ifdef USE_OPENLDAP - char ldap_buf[30]; -#endif - int i = 0; - int j; - -#ifdef DEBUGBUILD - /* Override version string when environment variable CURL_VERSION is set */ - const char *debugversion = getenv("CURL_VERSION"); - if(debugversion) { - strncpy(out, debugversion, sizeof(out)-1); - out[sizeof(out)-1] = '\0'; - return out; - } -#endif - - src[i++] = LIBCURL_NAME "/" LIBCURL_VERSION; -#ifdef USE_SSL - Curl_ssl_version(ssl_version, sizeof(ssl_version)); - src[i++] = ssl_version; -#endif -#ifdef HAVE_LIBZ - msnprintf(z_version, sizeof(z_version), "zlib/%s", zlibVersion()); - src[i++] = z_version; -#endif -#ifdef HAVE_BROTLI - brotli_version(&br_version[7], sizeof(br_version) - 7); - src[i++] = br_version; -#endif -#ifdef HAVE_ZSTD - zstd_version(&zst_version[5], sizeof(zst_version) - 5); - src[i++] = zst_version; -#endif -#ifdef USE_ARES - msnprintf(cares_version, sizeof(cares_version), - "c-ares/%s", ares_version(NULL)); - src[i++] = cares_version; -#endif -#ifdef USE_LIBIDN2 - msnprintf(idn_version, sizeof(idn_version), - "libidn2/%s", idn2_check_version(NULL)); - src[i++] = idn_version; -#elif defined(USE_WIN32_IDN) - src[i++] = (char *)"WinIDN"; -#endif - -#ifdef USE_LIBPSL - msnprintf(psl_version, sizeof(psl_version), "libpsl/%s", psl_get_version()); - src[i++] = psl_version; -#endif - -#ifdef USE_SSH - Curl_ssh_version(ssh_version, sizeof(ssh_version)); - src[i++] = ssh_version; -#endif -#ifdef USE_NGHTTP2 - Curl_http2_ver(h2_version, sizeof(h2_version)); - src[i++] = h2_version; -#endif -#ifdef ENABLE_QUIC - Curl_quic_ver(h3_version, sizeof(h3_version)); - src[i++] = h3_version; -#endif -#ifdef USE_LIBRTMP - { - char suff[2]; - if(RTMP_LIB_VERSION & 0xff) { - suff[0] = (RTMP_LIB_VERSION & 0xff) + 'a' - 1; - suff[1] = '\0'; - } - else - suff[0] = '\0'; - - msnprintf(rtmp_version, sizeof(rtmp_version), "librtmp/%d.%d%s", - RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff, - suff); - src[i++] = rtmp_version; - } -#endif -#ifdef USE_HYPER - msnprintf(hyper_buf, sizeof(hyper_buf), "Hyper/%s", hyper_version()); - src[i++] = hyper_buf; -#endif -#ifdef USE_GSASL - msnprintf(gsasl_buf, sizeof(gsasl_buf), "libgsasl/%s", - gsasl_check_version(NULL)); - src[i++] = gsasl_buf; -#endif -#ifdef USE_OPENLDAP - { - LDAPAPIInfo api; - api.ldapai_info_version = LDAP_API_INFO_VERSION; - - if(ldap_get_option(NULL, LDAP_OPT_API_INFO, &api) == LDAP_OPT_SUCCESS) { - unsigned int patch = api.ldapai_vendor_version % 100; - unsigned int major = api.ldapai_vendor_version / 10000; - unsigned int minor = - ((api.ldapai_vendor_version - major * 10000) - patch) / 100; - msnprintf(ldap_buf, sizeof(ldap_buf), "%s/%u.%u.%u", - api.ldapai_vendor_name, major, minor, patch); - src[i++] = ldap_buf; - ldap_memfree(api.ldapai_vendor_name); - ber_memvfree((void **)api.ldapai_extensions); - } - } -#endif - - DEBUGASSERT(i <= VERSION_PARTS); - - outp = &out[0]; - outlen = sizeof(out); - for(j = 0; j < i; j++) { - size_t n = strlen(src[j]); - /* we need room for a space, the string and the final zero */ - if(outlen <= (n + 2)) - break; - if(j) { - /* prepend a space if not the first */ - *outp++ = ' '; - outlen--; - } - memcpy(outp, src[j], n); - outp += n; - outlen -= n; - } - *outp = 0; - - return out; -} - -/* data for curl_version_info - - Keep the list sorted alphabetically. It is also written so that each - protocol line has its own #if line to make things easier on the eye. - */ - -static const char * const protocols[] = { -#ifndef CURL_DISABLE_DICT - "dict", -#endif -#ifndef CURL_DISABLE_FILE - "file", -#endif -#ifndef CURL_DISABLE_FTP - "ftp", -#endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP) - "ftps", -#endif -#ifndef CURL_DISABLE_GOPHER - "gopher", -#endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_GOPHER) - "gophers", -#endif -#ifndef CURL_DISABLE_HTTP - "http", -#endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) - "https", -#endif -#ifndef CURL_DISABLE_IMAP - "imap", -#endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_IMAP) - "imaps", -#endif -#ifndef CURL_DISABLE_LDAP - "ldap", -#if !defined(CURL_DISABLE_LDAPS) && \ - ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \ - (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL))) - "ldaps", -#endif -#endif -#ifndef CURL_DISABLE_MQTT - "mqtt", -#endif -#ifndef CURL_DISABLE_POP3 - "pop3", -#endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_POP3) - "pop3s", -#endif -#ifdef USE_LIBRTMP - "rtmp", - "rtmpe", - "rtmps", - "rtmpt", - "rtmpte", - "rtmpts", -#endif -#ifndef CURL_DISABLE_RTSP - "rtsp", -#endif -#if defined(USE_SSH) && !defined(USE_WOLFSSH) - "scp", -#endif -#ifdef USE_SSH - "sftp", -#endif -#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \ - (SIZEOF_CURL_OFF_T > 4) - "smb", -# ifdef USE_SSL - "smbs", -# endif -#endif -#ifndef CURL_DISABLE_SMTP - "smtp", -#endif -#if defined(USE_SSL) && !defined(CURL_DISABLE_SMTP) - "smtps", -#endif -#ifndef CURL_DISABLE_TELNET - "telnet", -#endif -#ifndef CURL_DISABLE_TFTP - "tftp", -#endif -#ifdef USE_WEBSOCKETS - "ws", -#endif -#if defined(USE_SSL) && defined(USE_WEBSOCKETS) - "wss", -#endif - - NULL -}; - -static curl_version_info_data version_info = { - CURLVERSION_NOW, - LIBCURL_VERSION, - LIBCURL_VERSION_NUM, - OS, /* as found by configure or set by hand at build-time */ - 0 /* features is 0 by default */ -#ifdef ENABLE_IPV6 - | CURL_VERSION_IPV6 -#endif -#ifdef USE_SSL - | CURL_VERSION_SSL -#endif -#ifdef USE_NTLM - | CURL_VERSION_NTLM -#endif -#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \ - defined(NTLM_WB_ENABLED) - | CURL_VERSION_NTLM_WB -#endif -#ifdef USE_SPNEGO - | CURL_VERSION_SPNEGO -#endif -#ifdef USE_KERBEROS5 - | CURL_VERSION_KERBEROS5 -#endif -#ifdef HAVE_GSSAPI - | CURL_VERSION_GSSAPI -#endif -#ifdef USE_WINDOWS_SSPI - | CURL_VERSION_SSPI -#endif -#ifdef HAVE_LIBZ - | CURL_VERSION_LIBZ -#endif -#ifdef DEBUGBUILD - | CURL_VERSION_DEBUG -#endif -#ifdef CURLDEBUG - | CURL_VERSION_CURLDEBUG -#endif -#ifdef CURLRES_ASYNCH - | CURL_VERSION_ASYNCHDNS -#endif -#if (SIZEOF_CURL_OFF_T > 4) && \ - ( (SIZEOF_OFF_T > 4) || defined(USE_WIN32_LARGE_FILES) ) - | CURL_VERSION_LARGEFILE -#endif -#if defined(WIN32) && defined(UNICODE) && defined(_UNICODE) - | CURL_VERSION_UNICODE -#endif -#if defined(USE_TLS_SRP) - | CURL_VERSION_TLSAUTH_SRP -#endif -#if defined(USE_NGHTTP2) || defined(USE_HYPER) - | CURL_VERSION_HTTP2 -#endif -#if defined(ENABLE_QUIC) - | CURL_VERSION_HTTP3 -#endif -#if defined(USE_UNIX_SOCKETS) - | CURL_VERSION_UNIX_SOCKETS -#endif -#if defined(USE_LIBPSL) - | CURL_VERSION_PSL -#endif -#if defined(CURL_WITH_MULTI_SSL) - | CURL_VERSION_MULTI_SSL -#endif -#if defined(HAVE_BROTLI) - | CURL_VERSION_BROTLI -#endif -#if defined(HAVE_ZSTD) - | CURL_VERSION_ZSTD -#endif -#ifndef CURL_DISABLE_ALTSVC - | CURL_VERSION_ALTSVC -#endif -#ifndef CURL_DISABLE_HSTS - | CURL_VERSION_HSTS -#endif -#if defined(USE_GSASL) - | CURL_VERSION_GSASL -#endif -#if defined(GLOBAL_INIT_IS_THREADSAFE) - | CURL_VERSION_THREADSAFE -#endif - , - NULL, /* ssl_version */ - 0, /* ssl_version_num, this is kept at zero */ - NULL, /* zlib_version */ - protocols, - NULL, /* c-ares version */ - 0, /* c-ares version numerical */ - NULL, /* libidn version */ - 0, /* iconv version */ - NULL, /* ssh lib version */ - 0, /* brotli_ver_num */ - NULL, /* brotli version */ - 0, /* nghttp2 version number */ - NULL, /* nghttp2 version string */ - NULL, /* quic library string */ -#ifdef CURL_CA_BUNDLE - CURL_CA_BUNDLE, /* cainfo */ -#else - NULL, -#endif -#ifdef CURL_CA_PATH - CURL_CA_PATH, /* capath */ -#else - NULL, -#endif - 0, /* zstd_ver_num */ - NULL, /* zstd version */ - NULL, /* Hyper version */ - NULL /* gsasl version */ -}; - -curl_version_info_data *curl_version_info(CURLversion stamp) -{ -#if defined(USE_SSH) - static char ssh_buffer[80]; -#endif -#ifdef USE_SSL -#ifdef CURL_WITH_MULTI_SSL - static char ssl_buffer[200]; -#else - static char ssl_buffer[80]; -#endif -#endif -#ifdef HAVE_BROTLI - static char brotli_buffer[80]; -#endif -#ifdef HAVE_ZSTD - static char zstd_buffer[80]; -#endif - -#ifdef USE_SSL - Curl_ssl_version(ssl_buffer, sizeof(ssl_buffer)); - version_info.ssl_version = ssl_buffer; -#ifndef CURL_DISABLE_PROXY - if(Curl_ssl->supports & SSLSUPP_HTTPS_PROXY) - version_info.features |= CURL_VERSION_HTTPS_PROXY; - else - version_info.features &= ~CURL_VERSION_HTTPS_PROXY; -#endif -#endif - -#ifdef HAVE_LIBZ - version_info.libz_version = zlibVersion(); - /* libz left NULL if non-existing */ -#endif -#ifdef USE_ARES - { - int aresnum; - version_info.ares = ares_version(&aresnum); - version_info.ares_num = aresnum; - } -#endif -#ifdef USE_LIBIDN2 - /* This returns a version string if we use the given version or later, - otherwise it returns NULL */ - version_info.libidn = idn2_check_version(IDN2_VERSION); - if(version_info.libidn) - version_info.features |= CURL_VERSION_IDN; -#elif defined(USE_WIN32_IDN) - version_info.features |= CURL_VERSION_IDN; -#endif - -#if defined(USE_SSH) - Curl_ssh_version(ssh_buffer, sizeof(ssh_buffer)); - version_info.libssh_version = ssh_buffer; -#endif - -#ifdef HAVE_BROTLI - version_info.brotli_ver_num = BrotliDecoderVersion(); - brotli_version(brotli_buffer, sizeof(brotli_buffer)); - version_info.brotli_version = brotli_buffer; -#endif - -#ifdef HAVE_ZSTD - version_info.zstd_ver_num = (unsigned int)ZSTD_versionNumber(); - zstd_version(zstd_buffer, sizeof(zstd_buffer)); - version_info.zstd_version = zstd_buffer; -#endif - -#ifdef USE_NGHTTP2 - { - nghttp2_info *h2 = nghttp2_version(0); - version_info.nghttp2_ver_num = h2->version_num; - version_info.nghttp2_version = h2->version_str; - } -#endif - -#ifdef ENABLE_QUIC - { - static char quicbuffer[80]; - Curl_quic_ver(quicbuffer, sizeof(quicbuffer)); - version_info.quic_version = quicbuffer; - } -#endif - -#ifdef USE_HYPER - { - static char hyper_buffer[30]; - msnprintf(hyper_buffer, sizeof(hyper_buffer), "Hyper/%s", hyper_version()); - version_info.hyper_version = hyper_buffer; - } -#endif - -#ifdef USE_GSASL - { - version_info.gsasl_version = gsasl_check_version(NULL); - } -#endif - - (void)stamp; /* avoid compiler warnings, we don't use this */ - return &version_info; -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "vtls/vtls.h"
+#include "http2.h"
+#include "vssh/ssh.h"
+#include "quic.h"
+#include "curl_printf.h"
+#include "easy_lock.h"
+
+#ifdef USE_ARES
+# if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
+ defined(WIN32)
+# define CARES_STATICLIB
+# endif
+# include <ares.h>
+#endif
+
+#ifdef USE_LIBIDN2
+#include <idn2.h>
+#endif
+
+#ifdef USE_LIBPSL
+#include <libpsl.h>
+#endif
+
+#ifdef USE_LIBRTMP
+#include <librtmp/rtmp.h>
+#endif
+
+#ifdef HAVE_LIBZ
+#include <zlib.h>
+#endif
+
+#ifdef HAVE_BROTLI
+#include <brotli/decode.h>
+#endif
+
+#ifdef HAVE_ZSTD
+#include <zstd.h>
+#endif
+
+#ifdef USE_GSASL
+#include <gsasl.h>
+#endif
+
+#ifdef USE_OPENLDAP
+#include <ldap.h>
+#endif
+
+#ifdef HAVE_BROTLI
+static void brotli_version(char *buf, size_t bufsz)
+{
+ uint32_t brotli_version = BrotliDecoderVersion();
+ unsigned int major = brotli_version >> 24;
+ unsigned int minor = (brotli_version & 0x00FFFFFF) >> 12;
+ unsigned int patch = brotli_version & 0x00000FFF;
+ (void)msnprintf(buf, bufsz, "%u.%u.%u", major, minor, patch);
+}
+#endif
+
+#ifdef HAVE_ZSTD
+static void zstd_version(char *buf, size_t bufsz)
+{
+ unsigned long zstd_version = (unsigned long)ZSTD_versionNumber();
+ unsigned int major = (unsigned int)(zstd_version / (100 * 100));
+ unsigned int minor = (unsigned int)((zstd_version -
+ (major * 100 * 100)) / 100);
+ unsigned int patch = (unsigned int)(zstd_version -
+ (major * 100 * 100) - (minor * 100));
+ (void)msnprintf(buf, bufsz, "%u.%u.%u", major, minor, patch);
+}
+#endif
+
+/*
+ * curl_version() returns a pointer to a static buffer.
+ *
+ * It is implemented to work multi-threaded by making sure repeated invokes
+ * generate the exact same string and never write any temporary data like
+ * zeros in the data.
+ */
+
+#define VERSION_PARTS 16 /* number of substrings we can concatenate */
+
+char *curl_version(void)
+{
+ static char out[300];
+ char *outp;
+ size_t outlen;
+ const char *src[VERSION_PARTS];
+#ifdef USE_SSL
+ char ssl_version[200];
+#endif
+#ifdef HAVE_LIBZ
+ char z_version[40];
+#endif
+#ifdef HAVE_BROTLI
+ char br_version[40] = "brotli/";
+#endif
+#ifdef HAVE_ZSTD
+ char zst_version[40] = "zstd/";
+#endif
+#ifdef USE_ARES
+ char cares_version[40];
+#endif
+#if defined(USE_LIBIDN2)
+ char idn_version[40];
+#endif
+#ifdef USE_LIBPSL
+ char psl_version[40];
+#endif
+#ifdef USE_SSH
+ char ssh_version[40];
+#endif
+#ifdef USE_NGHTTP2
+ char h2_version[40];
+#endif
+#ifdef ENABLE_QUIC
+ char h3_version[40];
+#endif
+#ifdef USE_LIBRTMP
+ char rtmp_version[40];
+#endif
+#ifdef USE_HYPER
+ char hyper_buf[30];
+#endif
+#ifdef USE_GSASL
+ char gsasl_buf[30];
+#endif
+#ifdef USE_OPENLDAP
+ char ldap_buf[30];
+#endif
+ int i = 0;
+ int j;
+
+#ifdef DEBUGBUILD
+ /* Override version string when environment variable CURL_VERSION is set */
+ const char *debugversion = getenv("CURL_VERSION");
+ if(debugversion) {
+ strncpy(out, debugversion, sizeof(out)-1);
+ out[sizeof(out)-1] = '\0';
+ return out;
+ }
+#endif
+
+ src[i++] = LIBCURL_NAME "/" LIBCURL_VERSION;
+#ifdef USE_SSL
+ Curl_ssl_version(ssl_version, sizeof(ssl_version));
+ src[i++] = ssl_version;
+#endif
+#ifdef HAVE_LIBZ
+ msnprintf(z_version, sizeof(z_version), "zlib/%s", zlibVersion());
+ src[i++] = z_version;
+#endif
+#ifdef HAVE_BROTLI
+ brotli_version(&br_version[7], sizeof(br_version) - 7);
+ src[i++] = br_version;
+#endif
+#ifdef HAVE_ZSTD
+ zstd_version(&zst_version[5], sizeof(zst_version) - 5);
+ src[i++] = zst_version;
+#endif
+#ifdef USE_ARES
+ msnprintf(cares_version, sizeof(cares_version),
+ "c-ares/%s", ares_version(NULL));
+ src[i++] = cares_version;
+#endif
+#ifdef USE_LIBIDN2
+ msnprintf(idn_version, sizeof(idn_version),
+ "libidn2/%s", idn2_check_version(NULL));
+ src[i++] = idn_version;
+#elif defined(USE_WIN32_IDN)
+ src[i++] = (char *)"WinIDN";
+#endif
+
+#ifdef USE_LIBPSL
+ msnprintf(psl_version, sizeof(psl_version), "libpsl/%s", psl_get_version());
+ src[i++] = psl_version;
+#endif
+
+#ifdef USE_SSH
+ Curl_ssh_version(ssh_version, sizeof(ssh_version));
+ src[i++] = ssh_version;
+#endif
+#ifdef USE_NGHTTP2
+ Curl_http2_ver(h2_version, sizeof(h2_version));
+ src[i++] = h2_version;
+#endif
+#ifdef ENABLE_QUIC
+ Curl_quic_ver(h3_version, sizeof(h3_version));
+ src[i++] = h3_version;
+#endif
+#ifdef USE_LIBRTMP
+ {
+ char suff[2];
+ if(RTMP_LIB_VERSION & 0xff) {
+ suff[0] = (RTMP_LIB_VERSION & 0xff) + 'a' - 1;
+ suff[1] = '\0';
+ }
+ else
+ suff[0] = '\0';
+
+ msnprintf(rtmp_version, sizeof(rtmp_version), "librtmp/%d.%d%s",
+ RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff,
+ suff);
+ src[i++] = rtmp_version;
+ }
+#endif
+#ifdef USE_HYPER
+ msnprintf(hyper_buf, sizeof(hyper_buf), "Hyper/%s", hyper_version());
+ src[i++] = hyper_buf;
+#endif
+#ifdef USE_GSASL
+ msnprintf(gsasl_buf, sizeof(gsasl_buf), "libgsasl/%s",
+ gsasl_check_version(NULL));
+ src[i++] = gsasl_buf;
+#endif
+#ifdef USE_OPENLDAP
+ {
+ LDAPAPIInfo api;
+ api.ldapai_info_version = LDAP_API_INFO_VERSION;
+
+ if(ldap_get_option(NULL, LDAP_OPT_API_INFO, &api) == LDAP_OPT_SUCCESS) {
+ unsigned int patch = api.ldapai_vendor_version % 100;
+ unsigned int major = api.ldapai_vendor_version / 10000;
+ unsigned int minor =
+ ((api.ldapai_vendor_version - major * 10000) - patch) / 100;
+ msnprintf(ldap_buf, sizeof(ldap_buf), "%s/%u.%u.%u",
+ api.ldapai_vendor_name, major, minor, patch);
+ src[i++] = ldap_buf;
+ ldap_memfree(api.ldapai_vendor_name);
+ ber_memvfree((void **)api.ldapai_extensions);
+ }
+ }
+#endif
+
+ DEBUGASSERT(i <= VERSION_PARTS);
+
+ outp = &out[0];
+ outlen = sizeof(out);
+ for(j = 0; j < i; j++) {
+ size_t n = strlen(src[j]);
+ /* we need room for a space, the string and the final zero */
+ if(outlen <= (n + 2))
+ break;
+ if(j) {
+ /* prepend a space if not the first */
+ *outp++ = ' ';
+ outlen--;
+ }
+ memcpy(outp, src[j], n);
+ outp += n;
+ outlen -= n;
+ }
+ *outp = 0;
+
+ return out;
+}
+
+/* data for curl_version_info
+
+ Keep the list sorted alphabetically. It is also written so that each
+ protocol line has its own #if line to make things easier on the eye.
+ */
+
+static const char * const protocols[] = {
+#ifndef CURL_DISABLE_DICT
+ "dict",
+#endif
+#ifndef CURL_DISABLE_FILE
+ "file",
+#endif
+#ifndef CURL_DISABLE_FTP
+ "ftp",
+#endif
+#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP)
+ "ftps",
+#endif
+#ifndef CURL_DISABLE_GOPHER
+ "gopher",
+#endif
+#if defined(USE_SSL) && !defined(CURL_DISABLE_GOPHER)
+ "gophers",
+#endif
+#ifndef CURL_DISABLE_HTTP
+ "http",
+#endif
+#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)
+ "https",
+#endif
+#ifndef CURL_DISABLE_IMAP
+ "imap",
+#endif
+#if defined(USE_SSL) && !defined(CURL_DISABLE_IMAP)
+ "imaps",
+#endif
+#ifndef CURL_DISABLE_LDAP
+ "ldap",
+#if !defined(CURL_DISABLE_LDAPS) && \
+ ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \
+ (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL)))
+ "ldaps",
+#endif
+#endif
+#ifndef CURL_DISABLE_MQTT
+ "mqtt",
+#endif
+#ifndef CURL_DISABLE_POP3
+ "pop3",
+#endif
+#if defined(USE_SSL) && !defined(CURL_DISABLE_POP3)
+ "pop3s",
+#endif
+#ifdef USE_LIBRTMP
+ "rtmp",
+ "rtmpe",
+ "rtmps",
+ "rtmpt",
+ "rtmpte",
+ "rtmpts",
+#endif
+#ifndef CURL_DISABLE_RTSP
+ "rtsp",
+#endif
+#if defined(USE_SSH) && !defined(USE_WOLFSSH)
+ "scp",
+#endif
+#ifdef USE_SSH
+ "sftp",
+#endif
+#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \
+ (SIZEOF_CURL_OFF_T > 4)
+ "smb",
+# ifdef USE_SSL
+ "smbs",
+# endif
+#endif
+#ifndef CURL_DISABLE_SMTP
+ "smtp",
+#endif
+#if defined(USE_SSL) && !defined(CURL_DISABLE_SMTP)
+ "smtps",
+#endif
+#ifndef CURL_DISABLE_TELNET
+ "telnet",
+#endif
+#ifndef CURL_DISABLE_TFTP
+ "tftp",
+#endif
+#ifdef USE_WEBSOCKETS
+ "ws",
+#endif
+#if defined(USE_SSL) && defined(USE_WEBSOCKETS)
+ "wss",
+#endif
+
+ NULL
+};
+
+/*
+ * Feature presence run-time check functions.
+ *
+ * Warning: the value returned by these should not change between
+ * curl_global_init() and curl_global_cleanup() calls.
+ */
+
+#if defined(USE_LIBIDN2)
+static int idn_present(curl_version_info_data *info)
+{
+ return info->libidn != NULL;
+}
+#else
+#define idn_present NULL
+#endif
+
+#if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY)
+static int https_proxy_present(curl_version_info_data *info)
+{
+ (void) info;
+ return Curl_ssl_supports(NULL, SSLSUPP_HTTPS_PROXY);
+}
+#endif
+
+/*
+ * Features table.
+ *
+ * Keep the features alphabetically sorted.
+ * Use FEATURE() macro to define an entry: this allows documentation check.
+ */
+
+#define FEATURE(name, present, bitmask) {(name), (present), (bitmask)}
+
+struct feat {
+ const char *name;
+ int (*present)(curl_version_info_data *info);
+ int bitmask;
+};
+
+static const struct feat features_table[] = {
+#ifndef CURL_DISABLE_ALTSVC
+ FEATURE("alt-svc", NULL, CURL_VERSION_ALTSVC),
+#endif
+#ifdef CURLRES_ASYNCH
+ FEATURE("AsynchDNS", NULL, CURL_VERSION_ASYNCHDNS),
+#endif
+#ifdef HAVE_BROTLI
+ FEATURE("brotli", NULL, CURL_VERSION_BROTLI),
+#endif
+#ifdef DEBUGBUILD
+ FEATURE("Debug", NULL, CURL_VERSION_DEBUG),
+#endif
+#ifdef USE_GSASL
+ FEATURE("gsasl", NULL, CURL_VERSION_GSASL),
+#endif
+#ifdef HAVE_GSSAPI
+ FEATURE("GSS-API", NULL, CURL_VERSION_GSSAPI),
+#endif
+#ifndef CURL_DISABLE_HSTS
+ FEATURE("HSTS", NULL, CURL_VERSION_HSTS),
+#endif
+#if defined(USE_NGHTTP2) || defined(USE_HYPER)
+ FEATURE("HTTP2", NULL, CURL_VERSION_HTTP2),
+#endif
+#if defined(ENABLE_QUIC)
+ FEATURE("HTTP3", NULL, CURL_VERSION_HTTP3),
+#endif
+#if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY)
+ FEATURE("HTTPS-proxy", https_proxy_present, CURL_VERSION_HTTPS_PROXY),
+#endif
+#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN)
+ FEATURE("IDN", idn_present, CURL_VERSION_IDN),
+#endif
+#ifdef ENABLE_IPV6
+ FEATURE("IPv6", NULL, CURL_VERSION_IPV6),
+#endif
+#ifdef USE_KERBEROS5
+ FEATURE("Kerberos", NULL, CURL_VERSION_KERBEROS5),
+#endif
+#if (SIZEOF_CURL_OFF_T > 4) && \
+ ( (SIZEOF_OFF_T > 4) || defined(USE_WIN32_LARGE_FILES) )
+ FEATURE("Largefile", NULL, CURL_VERSION_LARGEFILE),
+#endif
+#ifdef HAVE_LIBZ
+ FEATURE("libz", NULL, CURL_VERSION_LIBZ),
+#endif
+#ifdef CURL_WITH_MULTI_SSL
+ FEATURE("MultiSSL", NULL, CURL_VERSION_MULTI_SSL),
+#endif
+#ifdef USE_NTLM
+ FEATURE("NTLM", NULL, CURL_VERSION_NTLM),
+#endif
+#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \
+ defined(NTLM_WB_ENABLED)
+ FEATURE("NTLM_WB", NULL, CURL_VERSION_NTLM_WB),
+#endif
+#if defined(USE_LIBPSL)
+ FEATURE("PSL", NULL, CURL_VERSION_PSL),
+#endif
+#ifdef USE_SPNEGO
+ FEATURE("SPNEGO", NULL, CURL_VERSION_SPNEGO),
+#endif
+#ifdef USE_SSL
+ FEATURE("SSL", NULL, CURL_VERSION_SSL),
+#endif
+#ifdef USE_WINDOWS_SSPI
+ FEATURE("SSPI", NULL, CURL_VERSION_SSPI),
+#endif
+#ifdef GLOBAL_INIT_IS_THREADSAFE
+ FEATURE("threadsafe", NULL, CURL_VERSION_THREADSAFE),
+#endif
+#ifdef USE_TLS_SRP
+ FEATURE("TLS-SRP", NULL, CURL_VERSION_TLSAUTH_SRP),
+#endif
+#ifdef CURLDEBUG
+ FEATURE("TrackMemory", NULL, CURL_VERSION_CURLDEBUG),
+#endif
+#if defined(WIN32) && defined(UNICODE) && defined(_UNICODE)
+ FEATURE("Unicode", NULL, CURL_VERSION_UNICODE),
+#endif
+#ifdef USE_UNIX_SOCKETS
+ FEATURE("UnixSockets", NULL, CURL_VERSION_UNIX_SOCKETS),
+#endif
+#ifdef HAVE_ZSTD
+ FEATURE("zstd", NULL, CURL_VERSION_ZSTD),
+#endif
+ {NULL, NULL, 0}
+};
+
+static const char *feature_names[sizeof(features_table) /
+ sizeof(features_table[0])] = {NULL};
+
+
+static curl_version_info_data version_info = {
+ CURLVERSION_NOW,
+ LIBCURL_VERSION,
+ LIBCURL_VERSION_NUM,
+ OS, /* as found by configure or set by hand at build-time */
+ 0, /* features bitmask is built at run-time */
+ NULL, /* ssl_version */
+ 0, /* ssl_version_num, this is kept at zero */
+ NULL, /* zlib_version */
+ protocols,
+ NULL, /* c-ares version */
+ 0, /* c-ares version numerical */
+ NULL, /* libidn version */
+ 0, /* iconv version */
+ NULL, /* ssh lib version */
+ 0, /* brotli_ver_num */
+ NULL, /* brotli version */
+ 0, /* nghttp2 version number */
+ NULL, /* nghttp2 version string */
+ NULL, /* quic library string */
+#ifdef CURL_CA_BUNDLE
+ CURL_CA_BUNDLE, /* cainfo */
+#else
+ NULL,
+#endif
+#ifdef CURL_CA_PATH
+ CURL_CA_PATH, /* capath */
+#else
+ NULL,
+#endif
+ 0, /* zstd_ver_num */
+ NULL, /* zstd version */
+ NULL, /* Hyper version */
+ NULL, /* gsasl version */
+ feature_names
+};
+
+curl_version_info_data *curl_version_info(CURLversion stamp)
+{
+ size_t n;
+ const struct feat *p;
+ int features = 0;
+
+#if defined(USE_SSH)
+ static char ssh_buffer[80];
+#endif
+#ifdef USE_SSL
+#ifdef CURL_WITH_MULTI_SSL
+ static char ssl_buffer[200];
+#else
+ static char ssl_buffer[80];
+#endif
+#endif
+#ifdef HAVE_BROTLI
+ static char brotli_buffer[80];
+#endif
+#ifdef HAVE_ZSTD
+ static char zstd_buffer[80];
+#endif
+
+ (void)stamp; /* avoid compiler warnings, we don't use this */
+
+#ifdef USE_SSL
+ Curl_ssl_version(ssl_buffer, sizeof(ssl_buffer));
+ version_info.ssl_version = ssl_buffer;
+#endif
+
+#ifdef HAVE_LIBZ
+ version_info.libz_version = zlibVersion();
+ /* libz left NULL if non-existing */
+#endif
+#ifdef USE_ARES
+ {
+ int aresnum;
+ version_info.ares = ares_version(&aresnum);
+ version_info.ares_num = aresnum;
+ }
+#endif
+#ifdef USE_LIBIDN2
+ /* This returns a version string if we use the given version or later,
+ otherwise it returns NULL */
+ version_info.libidn = idn2_check_version(IDN2_VERSION);
+#endif
+
+#if defined(USE_SSH)
+ Curl_ssh_version(ssh_buffer, sizeof(ssh_buffer));
+ version_info.libssh_version = ssh_buffer;
+#endif
+
+#ifdef HAVE_BROTLI
+ version_info.brotli_ver_num = BrotliDecoderVersion();
+ brotli_version(brotli_buffer, sizeof(brotli_buffer));
+ version_info.brotli_version = brotli_buffer;
+#endif
+
+#ifdef HAVE_ZSTD
+ version_info.zstd_ver_num = (unsigned int)ZSTD_versionNumber();
+ zstd_version(zstd_buffer, sizeof(zstd_buffer));
+ version_info.zstd_version = zstd_buffer;
+#endif
+
+#ifdef USE_NGHTTP2
+ {
+ nghttp2_info *h2 = nghttp2_version(0);
+ version_info.nghttp2_ver_num = h2->version_num;
+ version_info.nghttp2_version = h2->version_str;
+ }
+#endif
+
+#ifdef ENABLE_QUIC
+ {
+ static char quicbuffer[80];
+ Curl_quic_ver(quicbuffer, sizeof(quicbuffer));
+ version_info.quic_version = quicbuffer;
+ }
+#endif
+
+#ifdef USE_HYPER
+ {
+ static char hyper_buffer[30];
+ msnprintf(hyper_buffer, sizeof(hyper_buffer), "Hyper/%s", hyper_version());
+ version_info.hyper_version = hyper_buffer;
+ }
+#endif
+
+#ifdef USE_GSASL
+ {
+ version_info.gsasl_version = gsasl_check_version(NULL);
+ }
+#endif
+
+ /* Get available features, build bitmask and names array. */
+ n = 0;
+ for(p = features_table; p->name; p++)
+ if(!p->present || p->present(&version_info)) {
+ features |= p->bitmask;
+ feature_names[n++] = p->name;
+ }
+
+ feature_names[n] = NULL; /* Terminate array. */
+ version_info.features = features;
+
+ return &version_info;
+}
diff --git a/libs/libcurl/src/version_win32.c b/libs/libcurl/src/version_win32.c index e8f14f9dff..2b8788bc60 100644 --- a/libs/libcurl/src/version_win32.c +++ b/libs/libcurl/src/version_win32.c @@ -1,319 +1,319 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.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" - -#if defined(WIN32) - -#include <curl/curl.h> -#include "version_win32.h" -#include "warnless.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* This Unicode version struct works for VerifyVersionInfoW (OSVERSIONINFOEXW) - and RtlVerifyVersionInfo (RTLOSVERSIONINFOEXW) */ -struct OUR_OSVERSIONINFOEXW { - ULONG dwOSVersionInfoSize; - ULONG dwMajorVersion; - ULONG dwMinorVersion; - ULONG dwBuildNumber; - ULONG dwPlatformId; - WCHAR szCSDVersion[128]; - USHORT wServicePackMajor; - USHORT wServicePackMinor; - USHORT wSuiteMask; - UCHAR wProductType; - UCHAR wReserved; -}; - -/* - * curlx_verify_windows_version() - * - * This is used to verify if we are running on a specific windows version. - * - * Parameters: - * - * majorVersion [in] - The major version number. - * minorVersion [in] - The minor version number. - * buildVersion [in] - The build version number. If 0, this parameter is - * ignored. - * platform [in] - The optional platform identifier. - * condition [in] - The test condition used to specifier whether we are - * checking a version less then, equal to or greater than - * what is specified in the major and minor version - * numbers. - * - * Returns TRUE if matched; otherwise FALSE. - */ -bool curlx_verify_windows_version(const unsigned int majorVersion, - const unsigned int minorVersion, - const unsigned int buildVersion, - const PlatformIdentifier platform, - const VersionCondition condition) -{ - bool matched = FALSE; - -#if defined(CURL_WINDOWS_APP) - (void)buildVersion; - - /* We have no way to determine the Windows version from Windows apps, - so let's assume we're running on the target Windows version. */ - const WORD fullVersion = MAKEWORD(minorVersion, majorVersion); - const WORD targetVersion = (WORD)_WIN32_WINNT; - - switch(condition) { - case VERSION_LESS_THAN: - matched = targetVersion < fullVersion; - break; - - case VERSION_LESS_THAN_EQUAL: - matched = targetVersion <= fullVersion; - break; - - case VERSION_EQUAL: - matched = targetVersion == fullVersion; - break; - - case VERSION_GREATER_THAN_EQUAL: - matched = targetVersion >= fullVersion; - break; - - case VERSION_GREATER_THAN: - matched = targetVersion > fullVersion; - break; - } - - if(matched && (platform == PLATFORM_WINDOWS)) { - /* we're always running on PLATFORM_WINNT */ - matched = FALSE; - } -#elif !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_WIN2K) || \ - (_WIN32_WINNT < _WIN32_WINNT_WIN2K) - OSVERSIONINFO osver; - - memset(&osver, 0, sizeof(osver)); - osver.dwOSVersionInfoSize = sizeof(osver); - - /* Find out Windows version */ - if(GetVersionEx(&osver)) { - /* Verify the Operating System version number */ - switch(condition) { - case VERSION_LESS_THAN: - if(osver.dwMajorVersion < majorVersion || - (osver.dwMajorVersion == majorVersion && - osver.dwMinorVersion < minorVersion) || - (buildVersion != 0 && - (osver.dwMajorVersion == majorVersion && - osver.dwMinorVersion == minorVersion && - osver.dwBuildNumber < buildVersion))) - matched = TRUE; - break; - - case VERSION_LESS_THAN_EQUAL: - if(osver.dwMajorVersion < majorVersion || - (osver.dwMajorVersion == majorVersion && - osver.dwMinorVersion < minorVersion) || - (osver.dwMajorVersion == majorVersion && - osver.dwMinorVersion == minorVersion && - (buildVersion == 0 || - osver.dwBuildNumber <= buildVersion))) - matched = TRUE; - break; - - case VERSION_EQUAL: - if(osver.dwMajorVersion == majorVersion && - osver.dwMinorVersion == minorVersion && - (buildVersion == 0 || - osver.dwBuildNumber == buildVersion)) - matched = TRUE; - break; - - case VERSION_GREATER_THAN_EQUAL: - if(osver.dwMajorVersion > majorVersion || - (osver.dwMajorVersion == majorVersion && - osver.dwMinorVersion > minorVersion) || - (osver.dwMajorVersion == majorVersion && - osver.dwMinorVersion == minorVersion && - (buildVersion == 0 || - osver.dwBuildNumber >= buildVersion))) - matched = TRUE; - break; - - case VERSION_GREATER_THAN: - if(osver.dwMajorVersion > majorVersion || - (osver.dwMajorVersion == majorVersion && - osver.dwMinorVersion > minorVersion) || - (buildVersion != 0 && - (osver.dwMajorVersion == majorVersion && - osver.dwMinorVersion == minorVersion && - osver.dwBuildNumber > buildVersion))) - matched = TRUE; - break; - } - - /* Verify the platform identifier (if necessary) */ - if(matched) { - switch(platform) { - case PLATFORM_WINDOWS: - if(osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) - matched = FALSE; - break; - - case PLATFORM_WINNT: - if(osver.dwPlatformId != VER_PLATFORM_WIN32_NT) - matched = FALSE; - break; - - default: /* like platform == PLATFORM_DONT_CARE */ - break; - } - } - } -#else - ULONGLONG cm = 0; - struct OUR_OSVERSIONINFOEXW osver; - BYTE majorCondition; - BYTE minorCondition; - BYTE buildCondition; - BYTE spMajorCondition; - BYTE spMinorCondition; - DWORD dwTypeMask = VER_MAJORVERSION | VER_MINORVERSION | - VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR; - - typedef LONG (APIENTRY *RTLVERIFYVERSIONINFO_FN) - (struct OUR_OSVERSIONINFOEXW *, ULONG, ULONGLONG); - static RTLVERIFYVERSIONINFO_FN pRtlVerifyVersionInfo; - static bool onetime = true; /* safe because first call is during init */ - - if(onetime) { - pRtlVerifyVersionInfo = CURLX_FUNCTION_CAST(RTLVERIFYVERSIONINFO_FN, - (GetProcAddress(GetModuleHandleA("ntdll"), "RtlVerifyVersionInfo"))); - onetime = false; - } - - switch(condition) { - case VERSION_LESS_THAN: - majorCondition = VER_LESS; - minorCondition = VER_LESS; - buildCondition = VER_LESS; - spMajorCondition = VER_LESS_EQUAL; - spMinorCondition = VER_LESS_EQUAL; - break; - - case VERSION_LESS_THAN_EQUAL: - majorCondition = VER_LESS_EQUAL; - minorCondition = VER_LESS_EQUAL; - buildCondition = VER_LESS_EQUAL; - spMajorCondition = VER_LESS_EQUAL; - spMinorCondition = VER_LESS_EQUAL; - break; - - case VERSION_EQUAL: - majorCondition = VER_EQUAL; - minorCondition = VER_EQUAL; - buildCondition = VER_EQUAL; - spMajorCondition = VER_GREATER_EQUAL; - spMinorCondition = VER_GREATER_EQUAL; - break; - - case VERSION_GREATER_THAN_EQUAL: - majorCondition = VER_GREATER_EQUAL; - minorCondition = VER_GREATER_EQUAL; - buildCondition = VER_GREATER_EQUAL; - spMajorCondition = VER_GREATER_EQUAL; - spMinorCondition = VER_GREATER_EQUAL; - break; - - case VERSION_GREATER_THAN: - majorCondition = VER_GREATER; - minorCondition = VER_GREATER; - buildCondition = VER_GREATER; - spMajorCondition = VER_GREATER_EQUAL; - spMinorCondition = VER_GREATER_EQUAL; - break; - - default: - return FALSE; - } - - memset(&osver, 0, sizeof(osver)); - osver.dwOSVersionInfoSize = sizeof(osver); - osver.dwMajorVersion = majorVersion; - osver.dwMinorVersion = minorVersion; - osver.dwBuildNumber = buildVersion; - if(platform == PLATFORM_WINDOWS) - osver.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS; - else if(platform == PLATFORM_WINNT) - osver.dwPlatformId = VER_PLATFORM_WIN32_NT; - - cm = VerSetConditionMask(cm, VER_MAJORVERSION, majorCondition); - cm = VerSetConditionMask(cm, VER_MINORVERSION, minorCondition); - cm = VerSetConditionMask(cm, VER_SERVICEPACKMAJOR, spMajorCondition); - cm = VerSetConditionMask(cm, VER_SERVICEPACKMINOR, spMinorCondition); - - if(platform != PLATFORM_DONT_CARE) { - cm = VerSetConditionMask(cm, VER_PLATFORMID, VER_EQUAL); - dwTypeMask |= VER_PLATFORMID; - } - - /* Later versions of Windows have version functions that may not return the - real version of Windows unless the application is so manifested. We prefer - the real version always, so we use the Rtl variant of the function when - possible. Note though the function signatures have underlying fundamental - types that are the same, the return values are different. */ - if(pRtlVerifyVersionInfo) - matched = !pRtlVerifyVersionInfo(&osver, dwTypeMask, cm); - else - matched = !!VerifyVersionInfoW((OSVERSIONINFOEXW *)&osver, dwTypeMask, cm); - - /* Compare the build number separately. VerifyVersionInfo normally compares - major.minor in hierarchical order (eg 1.9 is less than 2.0) but does not - do the same for build (eg 1.9 build 222 is not less than 2.0 build 111). - Build comparison is only needed when build numbers are equal (eg 1.9 is - always less than 2.0 so build comparison is not needed). */ - if(matched && buildVersion && - (condition == VERSION_EQUAL || - ((condition == VERSION_GREATER_THAN_EQUAL || - condition == VERSION_LESS_THAN_EQUAL) && - curlx_verify_windows_version(majorVersion, minorVersion, 0, - platform, VERSION_EQUAL)))) { - - cm = VerSetConditionMask(0, VER_BUILDNUMBER, buildCondition); - dwTypeMask = VER_BUILDNUMBER; - if(pRtlVerifyVersionInfo) - matched = !pRtlVerifyVersionInfo(&osver, dwTypeMask, cm); - else - matched = !!VerifyVersionInfoW((OSVERSIONINFOEXW *)&osver, - dwTypeMask, cm); - } - -#endif - - return matched; -} - -#endif /* WIN32 */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.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"
+
+#if defined(WIN32)
+
+#include <curl/curl.h>
+#include "version_win32.h"
+#include "warnless.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* This Unicode version struct works for VerifyVersionInfoW (OSVERSIONINFOEXW)
+ and RtlVerifyVersionInfo (RTLOSVERSIONINFOEXW) */
+struct OUR_OSVERSIONINFOEXW {
+ ULONG dwOSVersionInfoSize;
+ ULONG dwMajorVersion;
+ ULONG dwMinorVersion;
+ ULONG dwBuildNumber;
+ ULONG dwPlatformId;
+ WCHAR szCSDVersion[128];
+ USHORT wServicePackMajor;
+ USHORT wServicePackMinor;
+ USHORT wSuiteMask;
+ UCHAR wProductType;
+ UCHAR wReserved;
+};
+
+/*
+ * curlx_verify_windows_version()
+ *
+ * This is used to verify if we are running on a specific windows version.
+ *
+ * Parameters:
+ *
+ * majorVersion [in] - The major version number.
+ * minorVersion [in] - The minor version number.
+ * buildVersion [in] - The build version number. If 0, this parameter is
+ * ignored.
+ * platform [in] - The optional platform identifier.
+ * condition [in] - The test condition used to specifier whether we are
+ * checking a version less then, equal to or greater than
+ * what is specified in the major and minor version
+ * numbers.
+ *
+ * Returns TRUE if matched; otherwise FALSE.
+ */
+bool curlx_verify_windows_version(const unsigned int majorVersion,
+ const unsigned int minorVersion,
+ const unsigned int buildVersion,
+ const PlatformIdentifier platform,
+ const VersionCondition condition)
+{
+ bool matched = FALSE;
+
+#if defined(CURL_WINDOWS_APP)
+ (void)buildVersion;
+
+ /* We have no way to determine the Windows version from Windows apps,
+ so let's assume we're running on the target Windows version. */
+ const WORD fullVersion = MAKEWORD(minorVersion, majorVersion);
+ const WORD targetVersion = (WORD)_WIN32_WINNT;
+
+ switch(condition) {
+ case VERSION_LESS_THAN:
+ matched = targetVersion < fullVersion;
+ break;
+
+ case VERSION_LESS_THAN_EQUAL:
+ matched = targetVersion <= fullVersion;
+ break;
+
+ case VERSION_EQUAL:
+ matched = targetVersion == fullVersion;
+ break;
+
+ case VERSION_GREATER_THAN_EQUAL:
+ matched = targetVersion >= fullVersion;
+ break;
+
+ case VERSION_GREATER_THAN:
+ matched = targetVersion > fullVersion;
+ break;
+ }
+
+ if(matched && (platform == PLATFORM_WINDOWS)) {
+ /* we're always running on PLATFORM_WINNT */
+ matched = FALSE;
+ }
+#elif !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_WIN2K) || \
+ (_WIN32_WINNT < _WIN32_WINNT_WIN2K)
+ OSVERSIONINFO osver;
+
+ memset(&osver, 0, sizeof(osver));
+ osver.dwOSVersionInfoSize = sizeof(osver);
+
+ /* Find out Windows version */
+ if(GetVersionEx(&osver)) {
+ /* Verify the Operating System version number */
+ switch(condition) {
+ case VERSION_LESS_THAN:
+ if(osver.dwMajorVersion < majorVersion ||
+ (osver.dwMajorVersion == majorVersion &&
+ osver.dwMinorVersion < minorVersion) ||
+ (buildVersion != 0 &&
+ (osver.dwMajorVersion == majorVersion &&
+ osver.dwMinorVersion == minorVersion &&
+ osver.dwBuildNumber < buildVersion)))
+ matched = TRUE;
+ break;
+
+ case VERSION_LESS_THAN_EQUAL:
+ if(osver.dwMajorVersion < majorVersion ||
+ (osver.dwMajorVersion == majorVersion &&
+ osver.dwMinorVersion < minorVersion) ||
+ (osver.dwMajorVersion == majorVersion &&
+ osver.dwMinorVersion == minorVersion &&
+ (buildVersion == 0 ||
+ osver.dwBuildNumber <= buildVersion)))
+ matched = TRUE;
+ break;
+
+ case VERSION_EQUAL:
+ if(osver.dwMajorVersion == majorVersion &&
+ osver.dwMinorVersion == minorVersion &&
+ (buildVersion == 0 ||
+ osver.dwBuildNumber == buildVersion))
+ matched = TRUE;
+ break;
+
+ case VERSION_GREATER_THAN_EQUAL:
+ if(osver.dwMajorVersion > majorVersion ||
+ (osver.dwMajorVersion == majorVersion &&
+ osver.dwMinorVersion > minorVersion) ||
+ (osver.dwMajorVersion == majorVersion &&
+ osver.dwMinorVersion == minorVersion &&
+ (buildVersion == 0 ||
+ osver.dwBuildNumber >= buildVersion)))
+ matched = TRUE;
+ break;
+
+ case VERSION_GREATER_THAN:
+ if(osver.dwMajorVersion > majorVersion ||
+ (osver.dwMajorVersion == majorVersion &&
+ osver.dwMinorVersion > minorVersion) ||
+ (buildVersion != 0 &&
+ (osver.dwMajorVersion == majorVersion &&
+ osver.dwMinorVersion == minorVersion &&
+ osver.dwBuildNumber > buildVersion)))
+ matched = TRUE;
+ break;
+ }
+
+ /* Verify the platform identifier (if necessary) */
+ if(matched) {
+ switch(platform) {
+ case PLATFORM_WINDOWS:
+ if(osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS)
+ matched = FALSE;
+ break;
+
+ case PLATFORM_WINNT:
+ if(osver.dwPlatformId != VER_PLATFORM_WIN32_NT)
+ matched = FALSE;
+ break;
+
+ default: /* like platform == PLATFORM_DONT_CARE */
+ break;
+ }
+ }
+ }
+#else
+ ULONGLONG cm = 0;
+ struct OUR_OSVERSIONINFOEXW osver;
+ BYTE majorCondition;
+ BYTE minorCondition;
+ BYTE buildCondition;
+ BYTE spMajorCondition;
+ BYTE spMinorCondition;
+ DWORD dwTypeMask = VER_MAJORVERSION | VER_MINORVERSION |
+ VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR;
+
+ typedef LONG (APIENTRY *RTLVERIFYVERSIONINFO_FN)
+ (struct OUR_OSVERSIONINFOEXW *, ULONG, ULONGLONG);
+ static RTLVERIFYVERSIONINFO_FN pRtlVerifyVersionInfo;
+ static bool onetime = true; /* safe because first call is during init */
+
+ if(onetime) {
+ pRtlVerifyVersionInfo = CURLX_FUNCTION_CAST(RTLVERIFYVERSIONINFO_FN,
+ (GetProcAddress(GetModuleHandleA("ntdll"), "RtlVerifyVersionInfo")));
+ onetime = false;
+ }
+
+ switch(condition) {
+ case VERSION_LESS_THAN:
+ majorCondition = VER_LESS;
+ minorCondition = VER_LESS;
+ buildCondition = VER_LESS;
+ spMajorCondition = VER_LESS_EQUAL;
+ spMinorCondition = VER_LESS_EQUAL;
+ break;
+
+ case VERSION_LESS_THAN_EQUAL:
+ majorCondition = VER_LESS_EQUAL;
+ minorCondition = VER_LESS_EQUAL;
+ buildCondition = VER_LESS_EQUAL;
+ spMajorCondition = VER_LESS_EQUAL;
+ spMinorCondition = VER_LESS_EQUAL;
+ break;
+
+ case VERSION_EQUAL:
+ majorCondition = VER_EQUAL;
+ minorCondition = VER_EQUAL;
+ buildCondition = VER_EQUAL;
+ spMajorCondition = VER_GREATER_EQUAL;
+ spMinorCondition = VER_GREATER_EQUAL;
+ break;
+
+ case VERSION_GREATER_THAN_EQUAL:
+ majorCondition = VER_GREATER_EQUAL;
+ minorCondition = VER_GREATER_EQUAL;
+ buildCondition = VER_GREATER_EQUAL;
+ spMajorCondition = VER_GREATER_EQUAL;
+ spMinorCondition = VER_GREATER_EQUAL;
+ break;
+
+ case VERSION_GREATER_THAN:
+ majorCondition = VER_GREATER;
+ minorCondition = VER_GREATER;
+ buildCondition = VER_GREATER;
+ spMajorCondition = VER_GREATER_EQUAL;
+ spMinorCondition = VER_GREATER_EQUAL;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ memset(&osver, 0, sizeof(osver));
+ osver.dwOSVersionInfoSize = sizeof(osver);
+ osver.dwMajorVersion = majorVersion;
+ osver.dwMinorVersion = minorVersion;
+ osver.dwBuildNumber = buildVersion;
+ if(platform == PLATFORM_WINDOWS)
+ osver.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS;
+ else if(platform == PLATFORM_WINNT)
+ osver.dwPlatformId = VER_PLATFORM_WIN32_NT;
+
+ cm = VerSetConditionMask(cm, VER_MAJORVERSION, majorCondition);
+ cm = VerSetConditionMask(cm, VER_MINORVERSION, minorCondition);
+ cm = VerSetConditionMask(cm, VER_SERVICEPACKMAJOR, spMajorCondition);
+ cm = VerSetConditionMask(cm, VER_SERVICEPACKMINOR, spMinorCondition);
+
+ if(platform != PLATFORM_DONT_CARE) {
+ cm = VerSetConditionMask(cm, VER_PLATFORMID, VER_EQUAL);
+ dwTypeMask |= VER_PLATFORMID;
+ }
+
+ /* Later versions of Windows have version functions that may not return the
+ real version of Windows unless the application is so manifested. We prefer
+ the real version always, so we use the Rtl variant of the function when
+ possible. Note though the function signatures have underlying fundamental
+ types that are the same, the return values are different. */
+ if(pRtlVerifyVersionInfo)
+ matched = !pRtlVerifyVersionInfo(&osver, dwTypeMask, cm);
+ else
+ matched = !!VerifyVersionInfoW((OSVERSIONINFOEXW *)&osver, dwTypeMask, cm);
+
+ /* Compare the build number separately. VerifyVersionInfo normally compares
+ major.minor in hierarchical order (eg 1.9 is less than 2.0) but does not
+ do the same for build (eg 1.9 build 222 is not less than 2.0 build 111).
+ Build comparison is only needed when build numbers are equal (eg 1.9 is
+ always less than 2.0 so build comparison is not needed). */
+ if(matched && buildVersion &&
+ (condition == VERSION_EQUAL ||
+ ((condition == VERSION_GREATER_THAN_EQUAL ||
+ condition == VERSION_LESS_THAN_EQUAL) &&
+ curlx_verify_windows_version(majorVersion, minorVersion, 0,
+ platform, VERSION_EQUAL)))) {
+
+ cm = VerSetConditionMask(0, VER_BUILDNUMBER, buildCondition);
+ dwTypeMask = VER_BUILDNUMBER;
+ if(pRtlVerifyVersionInfo)
+ matched = !pRtlVerifyVersionInfo(&osver, dwTypeMask, cm);
+ else
+ matched = !!VerifyVersionInfoW((OSVERSIONINFOEXW *)&osver,
+ dwTypeMask, cm);
+ }
+
+#endif
+
+ return matched;
+}
+
+#endif /* WIN32 */
diff --git a/libs/libcurl/src/version_win32.h b/libs/libcurl/src/version_win32.h index 7a9a6a14f1..c13598d449 100644 --- a/libs/libcurl/src/version_win32.h +++ b/libs/libcurl/src/version_win32.h @@ -1,56 +1,56 @@ -#ifndef HEADER_CURL_VERSION_WIN32_H -#define HEADER_CURL_VERSION_WIN32_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.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" - -#if defined(WIN32) - -/* Version condition */ -typedef enum { - VERSION_LESS_THAN, - VERSION_LESS_THAN_EQUAL, - VERSION_EQUAL, - VERSION_GREATER_THAN_EQUAL, - VERSION_GREATER_THAN -} VersionCondition; - -/* Platform identifier */ -typedef enum { - PLATFORM_DONT_CARE, - PLATFORM_WINDOWS, - PLATFORM_WINNT -} PlatformIdentifier; - -/* This is used to verify if we are running on a specific windows version */ -bool curlx_verify_windows_version(const unsigned int majorVersion, - const unsigned int minorVersion, - const unsigned int buildVersion, - const PlatformIdentifier platform, - const VersionCondition condition); - -#endif /* WIN32 */ - -#endif /* HEADER_CURL_VERSION_WIN32_H */ +#ifndef HEADER_CURL_VERSION_WIN32_H
+#define HEADER_CURL_VERSION_WIN32_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.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"
+
+#if defined(WIN32)
+
+/* Version condition */
+typedef enum {
+ VERSION_LESS_THAN,
+ VERSION_LESS_THAN_EQUAL,
+ VERSION_EQUAL,
+ VERSION_GREATER_THAN_EQUAL,
+ VERSION_GREATER_THAN
+} VersionCondition;
+
+/* Platform identifier */
+typedef enum {
+ PLATFORM_DONT_CARE,
+ PLATFORM_WINDOWS,
+ PLATFORM_WINNT
+} PlatformIdentifier;
+
+/* This is used to verify if we are running on a specific windows version */
+bool curlx_verify_windows_version(const unsigned int majorVersion,
+ const unsigned int minorVersion,
+ const unsigned int buildVersion,
+ const PlatformIdentifier platform,
+ const VersionCondition condition);
+
+#endif /* WIN32 */
+
+#endif /* HEADER_CURL_VERSION_WIN32_H */
diff --git a/libs/libcurl/src/vquic/msh3.c b/libs/libcurl/src/vquic/msh3.c index c3e58e726a..46d7699488 100644 --- a/libs/libcurl/src/vquic/msh3.c +++ b/libs/libcurl/src/vquic/msh3.c @@ -1,527 +1,527 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_MSH3 - -#include "urldata.h" -#include "timeval.h" -#include "multiif.h" -#include "sendf.h" -#include "connect.h" -#include "h2h3.h" -#include "msh3.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* #define DEBUG_HTTP3 1 */ -#ifdef DEBUG_HTTP3 -#define H3BUGF(x) x -#else -#define H3BUGF(x) do { } while(0) -#endif - -#define MSH3_REQ_INIT_BUF_LEN 8192 - -static CURLcode msh3_do_it(struct Curl_easy *data, bool *done); -static int msh3_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks); -static CURLcode msh3_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection); -static unsigned int msh3_conncheck(struct Curl_easy *data, - struct connectdata *conn, - unsigned int checks_to_perform); -static Curl_recv msh3_stream_recv; -static Curl_send msh3_stream_send; -static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request, - void *IfContext, - const MSH3_HEADER *Header); -static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request, - void *IfContext, uint32_t Length, - const uint8_t *Data); -static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext, - bool Aborted, uint64_t AbortError); -static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext); - -static const struct Curl_handler msh3_curl_handler_http3 = { - "HTTPS", /* scheme */ - ZERO_NULL, /* setup_connection */ - msh3_do_it, /* do_it */ - Curl_http_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - msh3_getsock, /* proto_getsock */ - msh3_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - msh3_getsock, /* perform_getsock */ - msh3_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - msh3_conncheck, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_HTTP, /* defport */ - CURLPROTO_HTTPS, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_SSL | PROTOPT_STREAM /* flags */ -}; - -static const MSH3_REQUEST_IF msh3_request_if = { - msh3_header_received, - msh3_data_received, - msh3_complete, - msh3_shutdown -}; - -void Curl_quic_ver(char *p, size_t len) -{ - uint32_t v[4]; - MsH3Version(v); - (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]); -} - -CURLcode Curl_quic_connect(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t sockfd, - int sockindex, - const struct sockaddr *addr, - socklen_t addrlen) -{ - struct quicsocket *qs = &conn->hequic[sockindex]; - bool insecure = !conn->ssl_config.verifypeer; - memset(qs, 0, sizeof(*qs)); - - (void)sockfd; - (void)addr; /* TODO - Pass address along */ - (void)addrlen; - - H3BUGF(infof(data, "creating new api/connection")); - - qs->api = MsH3ApiOpen(); - if(!qs->api) { - failf(data, "can't create msh3 api"); - return CURLE_FAILED_INIT; - } - - qs->conn = MsH3ConnectionOpen(qs->api, - conn->host.name, - (uint16_t)conn->remote_port, - insecure); - if(!qs->conn) { - failf(data, "can't create msh3 connection"); - if(qs->api) { - MsH3ApiClose(qs->api); - } - return CURLE_FAILED_INIT; - } - - return CURLE_OK; -} - -CURLcode Curl_quic_is_connected(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool *connected) -{ - struct quicsocket *qs = &conn->hequic[sockindex]; - MSH3_CONNECTION_STATE state; - - state = MsH3ConnectionGetState(qs->conn, false); - if(state == MSH3_CONN_HANDSHAKE_FAILED || state == MSH3_CONN_DISCONNECTED) { - failf(data, "failed to connect, state=%u", (uint32_t)state); - return CURLE_COULDNT_CONNECT; - } - - if(state == MSH3_CONN_CONNECTED) { - H3BUGF(infof(data, "connection connected")); - *connected = true; - conn->quic = qs; - conn->recv[sockindex] = msh3_stream_recv; - conn->send[sockindex] = msh3_stream_send; - conn->handler = &msh3_curl_handler_http3; - conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - conn->httpversion = 30; - conn->bundle->multiuse = BUNDLE_MULTIPLEX; - /* TODO - Clean up other happy-eyeballs connection(s)? */ - } - - return CURLE_OK; -} - -static int msh3_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks) -{ - struct HTTP *stream = data->req.p.http; - int bitmap = GETSOCK_BLANK; - - socks[0] = conn->sock[FIRSTSOCKET]; - - if(stream->recv_error) { - bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); - data->state.drain++; - } - else if(stream->recv_header_len || stream->recv_data_len) { - bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); - data->state.drain++; - } - - H3BUGF(infof(data, "msh3_getsock %u", (uint32_t)data->state.drain)); - - return bitmap; -} - -static CURLcode msh3_do_it(struct Curl_easy *data, bool *done) -{ - struct HTTP *stream = data->req.p.http; - H3BUGF(infof(data, "msh3_do_it")); - stream->recv_buf = malloc(MSH3_REQ_INIT_BUF_LEN); - if(!stream->recv_buf) { - return CURLE_OUT_OF_MEMORY; - } - stream->req = ZERO_NULL; - msh3_lock_initialize(&stream->recv_lock); - stream->recv_buf_alloc = MSH3_REQ_INIT_BUF_LEN; - stream->recv_header_len = 0; - stream->recv_header_complete = false; - stream->recv_data_len = 0; - stream->recv_data_complete = false; - stream->recv_error = CURLE_OK; - return Curl_http(data, done); -} - -static unsigned int msh3_conncheck(struct Curl_easy *data, - struct connectdata *conn, - unsigned int checks_to_perform) -{ - (void)data; - (void)conn; - (void)checks_to_perform; - H3BUGF(infof(data, "msh3_conncheck")); - return CONNRESULT_NONE; -} - -static void disconnect(struct quicsocket *qs) -{ - if(qs->conn) { - MsH3ConnectionClose(qs->conn); - qs->conn = ZERO_NULL; - } - if(qs->api) { - MsH3ApiClose(qs->api); - qs->api = ZERO_NULL; - } -} - -static CURLcode msh3_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead_connection) -{ - (void)data; - (void)dead_connection; - H3BUGF(infof(data, "disconnecting (msh3)")); - disconnect(conn->quic); - return CURLE_OK; -} - -void Curl_quic_disconnect(struct Curl_easy *data, struct connectdata *conn, - int tempindex) -{ - (void)data; - if(conn->transport == TRNSPRT_QUIC) { - H3BUGF(infof(data, "disconnecting QUIC index %u", tempindex)); - disconnect(&conn->hequic[tempindex]); - } -} - -/* Requires stream->recv_lock to be held */ -static bool msh3request_ensure_room(struct HTTP *stream, size_t len) -{ - uint8_t *new_recv_buf; - const size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len; - if(cur_recv_len + len > stream->recv_buf_alloc) { - size_t new_recv_buf_alloc_len = stream->recv_buf_alloc; - do { - new_recv_buf_alloc_len <<= 1; /* TODO - handle overflow */ - } while(cur_recv_len + len > new_recv_buf_alloc_len); - new_recv_buf = malloc(new_recv_buf_alloc_len); - if(!new_recv_buf) { - return false; - } - if(cur_recv_len) { - memcpy(new_recv_buf, stream->recv_buf, cur_recv_len); - } - stream->recv_buf_alloc = new_recv_buf_alloc_len; - free(stream->recv_buf); - stream->recv_buf = new_recv_buf; - } - return true; -} - -static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request, - void *IfContext, - const MSH3_HEADER *Header) -{ - struct HTTP *stream = IfContext; - size_t total_len; - (void)Request; - - if(stream->recv_header_complete) { - H3BUGF(printf("* ignoring header after data\n")); - return; - } - - msh3_lock_acquire(&stream->recv_lock); - - if((Header->NameLength == 7) && - !strncmp(H2H3_PSEUDO_STATUS, (char *)Header->Name, 7)) { - total_len = 9 + Header->ValueLength; - if(!msh3request_ensure_room(stream, total_len)) { - /* TODO - handle error */ - goto release_lock; - } - msnprintf((char *)stream->recv_buf + stream->recv_header_len, - stream->recv_buf_alloc - stream->recv_header_len, - "HTTP/3 %.*s\n", (int)Header->ValueLength, Header->Value); - } - else { - total_len = Header->NameLength + 4 + Header->ValueLength; - if(!msh3request_ensure_room(stream, total_len)) { - /* TODO - handle error */ - goto release_lock; - } - msnprintf((char *)stream->recv_buf + stream->recv_header_len, - stream->recv_buf_alloc - stream->recv_header_len, - "%.*s: %.*s\n", - (int)Header->NameLength, Header->Name, - (int)Header->ValueLength, Header->Value); - } - - stream->recv_header_len += total_len - 1; /* don't include null-terminator */ - -release_lock: - msh3_lock_release(&stream->recv_lock); -} - -static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request, - void *IfContext, uint32_t Length, - const uint8_t *Data) -{ - struct HTTP *stream = IfContext; - size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len; - (void)Request; - H3BUGF(printf("* msh3_data_received %u. %zu buffered, %zu allocated\n", - Length, cur_recv_len, stream->recv_buf_alloc)); - msh3_lock_acquire(&stream->recv_lock); - if(!stream->recv_header_complete) { - H3BUGF(printf("* Headers complete!\n")); - if(!msh3request_ensure_room(stream, 2)) { - /* TODO - handle error */ - goto release_lock; - } - stream->recv_buf[stream->recv_header_len++] = '\r'; - stream->recv_buf[stream->recv_header_len++] = '\n'; - stream->recv_header_complete = true; - cur_recv_len += 2; - } - if(!msh3request_ensure_room(stream, Length)) { - /* TODO - handle error */ - goto release_lock; - } - memcpy(stream->recv_buf + cur_recv_len, Data, Length); - stream->recv_data_len += (size_t)Length; -release_lock: - msh3_lock_release(&stream->recv_lock); -} - -static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext, - bool Aborted, uint64_t AbortError) -{ - struct HTTP *stream = IfContext; - (void)Request; - (void)AbortError; - H3BUGF(printf("* msh3_complete, aborted=%s\n", Aborted ? "true" : "false")); - msh3_lock_acquire(&stream->recv_lock); - if(Aborted) { - stream->recv_error = CURLE_HTTP3; /* TODO - how do we pass AbortError? */ - } - stream->recv_header_complete = true; - stream->recv_data_complete = true; - msh3_lock_release(&stream->recv_lock); -} - -static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext) -{ - struct HTTP *stream = IfContext; - (void)Request; - (void)stream; -} - -static ssize_t msh3_stream_send(struct Curl_easy *data, - int sockindex, - const void *mem, - size_t len, - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct HTTP *stream = data->req.p.http; - struct quicsocket *qs = conn->quic; - struct h2h3req *hreq; - - (void)sockindex; - /* Sizes must match for cast below to work" */ - DEBUGASSERT(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo)); - - H3BUGF(infof(data, "msh3_stream_send %zu", len)); - - if(!stream->req) { - *curlcode = Curl_pseudo_headers(data, mem, len, &hreq); - if(*curlcode) { - failf(data, "Curl_pseudo_headers failed"); - return -1; - } - H3BUGF(infof(data, "starting request with %zu headers", hreq->entries)); - stream->req = MsH3RequestOpen(qs->conn, &msh3_request_if, stream, - (MSH3_HEADER*)hreq->header, hreq->entries); - Curl_pseudo_free(hreq); - if(!stream->req) { - failf(data, "request open failed"); - *curlcode = CURLE_SEND_ERROR; - return -1; - } - *curlcode = CURLE_OK; - return len; - } - H3BUGF(infof(data, "send %zd body bytes on request %p", len, - (void *)stream->req)); - *curlcode = CURLE_SEND_ERROR; - return -1; -} - -static ssize_t msh3_stream_recv(struct Curl_easy *data, - int sockindex, - char *buf, - size_t buffersize, - CURLcode *curlcode) -{ - struct HTTP *stream = data->req.p.http; - size_t outsize = 0; - (void)sockindex; - H3BUGF(infof(data, "msh3_stream_recv %zu", buffersize)); - - if(stream->recv_error) { - failf(data, "request aborted"); - *curlcode = stream->recv_error; - return -1; - } - - msh3_lock_acquire(&stream->recv_lock); - - if(stream->recv_header_len) { - outsize = buffersize; - if(stream->recv_header_len < outsize) { - outsize = stream->recv_header_len; - } - memcpy(buf, stream->recv_buf, outsize); - if(outsize < stream->recv_header_len + stream->recv_data_len) { - memmove(stream->recv_buf, stream->recv_buf + outsize, - stream->recv_header_len + stream->recv_data_len - outsize); - } - stream->recv_header_len -= outsize; - H3BUGF(infof(data, "returned %zu bytes of headers", outsize)); - } - else if(stream->recv_data_len) { - outsize = buffersize; - if(stream->recv_data_len < outsize) { - outsize = stream->recv_data_len; - } - memcpy(buf, stream->recv_buf, outsize); - if(outsize < stream->recv_data_len) { - memmove(stream->recv_buf, stream->recv_buf + outsize, - stream->recv_data_len - outsize); - } - stream->recv_data_len -= outsize; - H3BUGF(infof(data, "returned %zu bytes of data", outsize)); - } - else if(stream->recv_data_complete) { - H3BUGF(infof(data, "receive complete")); - } - - msh3_lock_release(&stream->recv_lock); - - return (ssize_t)outsize; -} - -CURLcode Curl_quic_done_sending(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - H3BUGF(infof(data, "Curl_quic_done_sending")); - if(conn->handler == &msh3_curl_handler_http3) { - struct HTTP *stream = data->req.p.http; - stream->upload_done = TRUE; - } - - return CURLE_OK; -} - -void Curl_quic_done(struct Curl_easy *data, bool premature) -{ - struct HTTP *stream = data->req.p.http; - (void)premature; - H3BUGF(infof(data, "Curl_quic_done")); - if(stream) { - if(stream->recv_buf) { - Curl_safefree(stream->recv_buf); - msh3_lock_uninitialize(&stream->recv_lock); - } - if(stream->req) { - MsH3RequestClose(stream->req); - stream->req = ZERO_NULL; - } - } -} - -bool Curl_quic_data_pending(const struct Curl_easy *data) -{ - struct HTTP *stream = data->req.p.http; - H3BUGF(infof((struct Curl_easy *)data, "Curl_quic_data_pending")); - return stream->recv_header_len || stream->recv_data_len; -} - -/* - * Called from transfer.c:Curl_readwrite when neither HTTP level read - * nor write is performed. It is a good place to handle timer expiry - * for QUIC transport. - */ -CURLcode Curl_quic_idle(struct Curl_easy *data) -{ - (void)data; - H3BUGF(infof(data, "Curl_quic_idle")); - return CURLE_OK; -} - -#endif /* USE_MSH3 */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_MSH3
+
+#include "urldata.h"
+#include "timeval.h"
+#include "multiif.h"
+#include "sendf.h"
+#include "connect.h"
+#include "h2h3.h"
+#include "msh3.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* #define DEBUG_HTTP3 1 */
+#ifdef DEBUG_HTTP3
+#define H3BUGF(x) x
+#else
+#define H3BUGF(x) do { } while(0)
+#endif
+
+#define MSH3_REQ_INIT_BUF_LEN 8192
+
+static CURLcode msh3_do_it(struct Curl_easy *data, bool *done);
+static int msh3_getsock(struct Curl_easy *data,
+ struct connectdata *conn, curl_socket_t *socks);
+static CURLcode msh3_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection);
+static unsigned int msh3_conncheck(struct Curl_easy *data,
+ struct connectdata *conn,
+ unsigned int checks_to_perform);
+static Curl_recv msh3_stream_recv;
+static Curl_send msh3_stream_send;
+static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
+ void *IfContext,
+ const MSH3_HEADER *Header);
+static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
+ void *IfContext, uint32_t Length,
+ const uint8_t *Data);
+static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
+ bool Aborted, uint64_t AbortError);
+static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext);
+
+static const struct Curl_handler msh3_curl_handler_http3 = {
+ "HTTPS", /* scheme */
+ ZERO_NULL, /* setup_connection */
+ msh3_do_it, /* do_it */
+ Curl_http_done, /* done */
+ ZERO_NULL, /* do_more */
+ ZERO_NULL, /* connect_it */
+ ZERO_NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ msh3_getsock, /* proto_getsock */
+ msh3_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ msh3_getsock, /* perform_getsock */
+ msh3_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ msh3_conncheck, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_HTTP, /* defport */
+ CURLPROTO_HTTPS, /* protocol */
+ CURLPROTO_HTTP, /* family */
+ PROTOPT_SSL | PROTOPT_STREAM /* flags */
+};
+
+static const MSH3_REQUEST_IF msh3_request_if = {
+ msh3_header_received,
+ msh3_data_received,
+ msh3_complete,
+ msh3_shutdown
+};
+
+void Curl_quic_ver(char *p, size_t len)
+{
+ uint32_t v[4];
+ MsH3Version(v);
+ (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]);
+}
+
+CURLcode Curl_quic_connect(struct Curl_easy *data,
+ struct connectdata *conn,
+ curl_socket_t sockfd,
+ int sockindex,
+ const struct sockaddr *addr,
+ socklen_t addrlen)
+{
+ struct quicsocket *qs = &conn->hequic[sockindex];
+ bool insecure = !conn->ssl_config.verifypeer;
+ memset(qs, 0, sizeof(*qs));
+
+ (void)sockfd;
+ (void)addr; /* TODO - Pass address along */
+ (void)addrlen;
+
+ H3BUGF(infof(data, "creating new api/connection"));
+
+ qs->api = MsH3ApiOpen();
+ if(!qs->api) {
+ failf(data, "can't create msh3 api");
+ return CURLE_FAILED_INIT;
+ }
+
+ qs->conn = MsH3ConnectionOpen(qs->api,
+ conn->host.name,
+ (uint16_t)conn->remote_port,
+ insecure);
+ if(!qs->conn) {
+ failf(data, "can't create msh3 connection");
+ if(qs->api) {
+ MsH3ApiClose(qs->api);
+ }
+ return CURLE_FAILED_INIT;
+ }
+
+ return CURLE_OK;
+}
+
+CURLcode Curl_quic_is_connected(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ bool *connected)
+{
+ struct quicsocket *qs = &conn->hequic[sockindex];
+ MSH3_CONNECTION_STATE state;
+
+ state = MsH3ConnectionGetState(qs->conn, false);
+ if(state == MSH3_CONN_HANDSHAKE_FAILED || state == MSH3_CONN_DISCONNECTED) {
+ failf(data, "failed to connect, state=%u", (uint32_t)state);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ if(state == MSH3_CONN_CONNECTED) {
+ H3BUGF(infof(data, "connection connected"));
+ *connected = true;
+ conn->quic = qs;
+ conn->recv[sockindex] = msh3_stream_recv;
+ conn->send[sockindex] = msh3_stream_send;
+ conn->handler = &msh3_curl_handler_http3;
+ conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ conn->httpversion = 30;
+ conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+ /* TODO - Clean up other happy-eyeballs connection(s)? */
+ }
+
+ return CURLE_OK;
+}
+
+static int msh3_getsock(struct Curl_easy *data,
+ struct connectdata *conn, curl_socket_t *socks)
+{
+ struct HTTP *stream = data->req.p.http;
+ int bitmap = GETSOCK_BLANK;
+
+ socks[0] = conn->sock[FIRSTSOCKET];
+
+ if(stream->recv_error) {
+ bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
+ data->state.drain++;
+ }
+ else if(stream->recv_header_len || stream->recv_data_len) {
+ bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
+ data->state.drain++;
+ }
+
+ H3BUGF(infof(data, "msh3_getsock %u", (uint32_t)data->state.drain));
+
+ return bitmap;
+}
+
+static CURLcode msh3_do_it(struct Curl_easy *data, bool *done)
+{
+ struct HTTP *stream = data->req.p.http;
+ H3BUGF(infof(data, "msh3_do_it"));
+ stream->recv_buf = malloc(MSH3_REQ_INIT_BUF_LEN);
+ if(!stream->recv_buf) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+ stream->req = ZERO_NULL;
+ msh3_lock_initialize(&stream->recv_lock);
+ stream->recv_buf_alloc = MSH3_REQ_INIT_BUF_LEN;
+ stream->recv_header_len = 0;
+ stream->recv_header_complete = false;
+ stream->recv_data_len = 0;
+ stream->recv_data_complete = false;
+ stream->recv_error = CURLE_OK;
+ return Curl_http(data, done);
+}
+
+static unsigned int msh3_conncheck(struct Curl_easy *data,
+ struct connectdata *conn,
+ unsigned int checks_to_perform)
+{
+ (void)data;
+ (void)conn;
+ (void)checks_to_perform;
+ H3BUGF(infof(data, "msh3_conncheck"));
+ return CONNRESULT_NONE;
+}
+
+static void disconnect(struct quicsocket *qs)
+{
+ if(qs->conn) {
+ MsH3ConnectionClose(qs->conn);
+ qs->conn = ZERO_NULL;
+ }
+ if(qs->api) {
+ MsH3ApiClose(qs->api);
+ qs->api = ZERO_NULL;
+ }
+}
+
+static CURLcode msh3_disconnect(struct Curl_easy *data,
+ struct connectdata *conn, bool dead_connection)
+{
+ (void)data;
+ (void)dead_connection;
+ H3BUGF(infof(data, "disconnecting (msh3)"));
+ disconnect(conn->quic);
+ return CURLE_OK;
+}
+
+void Curl_quic_disconnect(struct Curl_easy *data, struct connectdata *conn,
+ int tempindex)
+{
+ (void)data;
+ if(conn->transport == TRNSPRT_QUIC) {
+ H3BUGF(infof(data, "disconnecting QUIC index %u", tempindex));
+ disconnect(&conn->hequic[tempindex]);
+ }
+}
+
+/* Requires stream->recv_lock to be held */
+static bool msh3request_ensure_room(struct HTTP *stream, size_t len)
+{
+ uint8_t *new_recv_buf;
+ const size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
+ if(cur_recv_len + len > stream->recv_buf_alloc) {
+ size_t new_recv_buf_alloc_len = stream->recv_buf_alloc;
+ do {
+ new_recv_buf_alloc_len <<= 1; /* TODO - handle overflow */
+ } while(cur_recv_len + len > new_recv_buf_alloc_len);
+ new_recv_buf = malloc(new_recv_buf_alloc_len);
+ if(!new_recv_buf) {
+ return false;
+ }
+ if(cur_recv_len) {
+ memcpy(new_recv_buf, stream->recv_buf, cur_recv_len);
+ }
+ stream->recv_buf_alloc = new_recv_buf_alloc_len;
+ free(stream->recv_buf);
+ stream->recv_buf = new_recv_buf;
+ }
+ return true;
+}
+
+static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
+ void *IfContext,
+ const MSH3_HEADER *Header)
+{
+ struct HTTP *stream = IfContext;
+ size_t total_len;
+ (void)Request;
+
+ if(stream->recv_header_complete) {
+ H3BUGF(printf("* ignoring header after data\n"));
+ return;
+ }
+
+ msh3_lock_acquire(&stream->recv_lock);
+
+ if((Header->NameLength == 7) &&
+ !strncmp(H2H3_PSEUDO_STATUS, (char *)Header->Name, 7)) {
+ total_len = 9 + Header->ValueLength;
+ if(!msh3request_ensure_room(stream, total_len)) {
+ /* TODO - handle error */
+ goto release_lock;
+ }
+ msnprintf((char *)stream->recv_buf + stream->recv_header_len,
+ stream->recv_buf_alloc - stream->recv_header_len,
+ "HTTP/3 %.*s\n", (int)Header->ValueLength, Header->Value);
+ }
+ else {
+ total_len = Header->NameLength + 4 + Header->ValueLength;
+ if(!msh3request_ensure_room(stream, total_len)) {
+ /* TODO - handle error */
+ goto release_lock;
+ }
+ msnprintf((char *)stream->recv_buf + stream->recv_header_len,
+ stream->recv_buf_alloc - stream->recv_header_len,
+ "%.*s: %.*s\n",
+ (int)Header->NameLength, Header->Name,
+ (int)Header->ValueLength, Header->Value);
+ }
+
+ stream->recv_header_len += total_len - 1; /* don't include null-terminator */
+
+release_lock:
+ msh3_lock_release(&stream->recv_lock);
+}
+
+static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
+ void *IfContext, uint32_t Length,
+ const uint8_t *Data)
+{
+ struct HTTP *stream = IfContext;
+ size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
+ (void)Request;
+ H3BUGF(printf("* msh3_data_received %u. %zu buffered, %zu allocated\n",
+ Length, cur_recv_len, stream->recv_buf_alloc));
+ msh3_lock_acquire(&stream->recv_lock);
+ if(!stream->recv_header_complete) {
+ H3BUGF(printf("* Headers complete!\n"));
+ if(!msh3request_ensure_room(stream, 2)) {
+ /* TODO - handle error */
+ goto release_lock;
+ }
+ stream->recv_buf[stream->recv_header_len++] = '\r';
+ stream->recv_buf[stream->recv_header_len++] = '\n';
+ stream->recv_header_complete = true;
+ cur_recv_len += 2;
+ }
+ if(!msh3request_ensure_room(stream, Length)) {
+ /* TODO - handle error */
+ goto release_lock;
+ }
+ memcpy(stream->recv_buf + cur_recv_len, Data, Length);
+ stream->recv_data_len += (size_t)Length;
+release_lock:
+ msh3_lock_release(&stream->recv_lock);
+}
+
+static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
+ bool Aborted, uint64_t AbortError)
+{
+ struct HTTP *stream = IfContext;
+ (void)Request;
+ (void)AbortError;
+ H3BUGF(printf("* msh3_complete, aborted=%s\n", Aborted ? "true" : "false"));
+ msh3_lock_acquire(&stream->recv_lock);
+ if(Aborted) {
+ stream->recv_error = CURLE_HTTP3; /* TODO - how do we pass AbortError? */
+ }
+ stream->recv_header_complete = true;
+ stream->recv_data_complete = true;
+ msh3_lock_release(&stream->recv_lock);
+}
+
+static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext)
+{
+ struct HTTP *stream = IfContext;
+ (void)Request;
+ (void)stream;
+}
+
+static ssize_t msh3_stream_send(struct Curl_easy *data,
+ int sockindex,
+ const void *mem,
+ size_t len,
+ CURLcode *curlcode)
+{
+ struct connectdata *conn = data->conn;
+ struct HTTP *stream = data->req.p.http;
+ struct quicsocket *qs = conn->quic;
+ struct h2h3req *hreq;
+
+ (void)sockindex;
+ /* Sizes must match for cast below to work" */
+ DEBUGASSERT(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo));
+
+ H3BUGF(infof(data, "msh3_stream_send %zu", len));
+
+ if(!stream->req) {
+ *curlcode = Curl_pseudo_headers(data, mem, len, &hreq);
+ if(*curlcode) {
+ failf(data, "Curl_pseudo_headers failed");
+ return -1;
+ }
+ H3BUGF(infof(data, "starting request with %zu headers", hreq->entries));
+ stream->req = MsH3RequestOpen(qs->conn, &msh3_request_if, stream,
+ (MSH3_HEADER*)hreq->header, hreq->entries);
+ Curl_pseudo_free(hreq);
+ if(!stream->req) {
+ failf(data, "request open failed");
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+ *curlcode = CURLE_OK;
+ return len;
+ }
+ H3BUGF(infof(data, "send %zd body bytes on request %p", len,
+ (void *)stream->req));
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+}
+
+static ssize_t msh3_stream_recv(struct Curl_easy *data,
+ int sockindex,
+ char *buf,
+ size_t buffersize,
+ CURLcode *curlcode)
+{
+ struct HTTP *stream = data->req.p.http;
+ size_t outsize = 0;
+ (void)sockindex;
+ H3BUGF(infof(data, "msh3_stream_recv %zu", buffersize));
+
+ if(stream->recv_error) {
+ failf(data, "request aborted");
+ *curlcode = stream->recv_error;
+ return -1;
+ }
+
+ msh3_lock_acquire(&stream->recv_lock);
+
+ if(stream->recv_header_len) {
+ outsize = buffersize;
+ if(stream->recv_header_len < outsize) {
+ outsize = stream->recv_header_len;
+ }
+ memcpy(buf, stream->recv_buf, outsize);
+ if(outsize < stream->recv_header_len + stream->recv_data_len) {
+ memmove(stream->recv_buf, stream->recv_buf + outsize,
+ stream->recv_header_len + stream->recv_data_len - outsize);
+ }
+ stream->recv_header_len -= outsize;
+ H3BUGF(infof(data, "returned %zu bytes of headers", outsize));
+ }
+ else if(stream->recv_data_len) {
+ outsize = buffersize;
+ if(stream->recv_data_len < outsize) {
+ outsize = stream->recv_data_len;
+ }
+ memcpy(buf, stream->recv_buf, outsize);
+ if(outsize < stream->recv_data_len) {
+ memmove(stream->recv_buf, stream->recv_buf + outsize,
+ stream->recv_data_len - outsize);
+ }
+ stream->recv_data_len -= outsize;
+ H3BUGF(infof(data, "returned %zu bytes of data", outsize));
+ }
+ else if(stream->recv_data_complete) {
+ H3BUGF(infof(data, "receive complete"));
+ }
+
+ msh3_lock_release(&stream->recv_lock);
+
+ return (ssize_t)outsize;
+}
+
+CURLcode Curl_quic_done_sending(struct Curl_easy *data)
+{
+ struct connectdata *conn = data->conn;
+ H3BUGF(infof(data, "Curl_quic_done_sending"));
+ if(conn->handler == &msh3_curl_handler_http3) {
+ struct HTTP *stream = data->req.p.http;
+ stream->upload_done = TRUE;
+ }
+
+ return CURLE_OK;
+}
+
+void Curl_quic_done(struct Curl_easy *data, bool premature)
+{
+ struct HTTP *stream = data->req.p.http;
+ (void)premature;
+ H3BUGF(infof(data, "Curl_quic_done"));
+ if(stream) {
+ if(stream->recv_buf) {
+ Curl_safefree(stream->recv_buf);
+ msh3_lock_uninitialize(&stream->recv_lock);
+ }
+ if(stream->req) {
+ MsH3RequestClose(stream->req);
+ stream->req = ZERO_NULL;
+ }
+ }
+}
+
+bool Curl_quic_data_pending(const struct Curl_easy *data)
+{
+ struct HTTP *stream = data->req.p.http;
+ H3BUGF(infof((struct Curl_easy *)data, "Curl_quic_data_pending"));
+ return stream->recv_header_len || stream->recv_data_len;
+}
+
+/*
+ * Called from transfer.c:Curl_readwrite when neither HTTP level read
+ * nor write is performed. It is a good place to handle timer expiry
+ * for QUIC transport.
+ */
+CURLcode Curl_quic_idle(struct Curl_easy *data)
+{
+ (void)data;
+ H3BUGF(infof(data, "Curl_quic_idle"));
+ return CURLE_OK;
+}
+
+#endif /* USE_MSH3 */
diff --git a/libs/libcurl/src/vquic/msh3.h b/libs/libcurl/src/vquic/msh3.h index ce884d92dd..06c2d79d31 100644 --- a/libs/libcurl/src/vquic/msh3.h +++ b/libs/libcurl/src/vquic/msh3.h @@ -1,40 +1,40 @@ -#ifndef HEADER_CURL_VQUIC_MSH3_H -#define HEADER_CURL_VQUIC_MSH3_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_MSH3 - -#include <msh3.h> - -struct quicsocket { - MSH3_API* api; - MSH3_CONNECTION* conn; -}; - -#endif /* USE_MSQUIC */ - -#endif /* HEADER_CURL_VQUIC_MSH3_H */ +#ifndef HEADER_CURL_VQUIC_MSH3_H
+#define HEADER_CURL_VQUIC_MSH3_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_MSH3
+
+#include <msh3.h>
+
+struct quicsocket {
+ MSH3_API* api;
+ MSH3_CONNECTION* conn;
+};
+
+#endif /* USE_MSQUIC */
+
+#endif /* HEADER_CURL_VQUIC_MSH3_H */
diff --git a/libs/libcurl/src/vquic/ngtcp2.c b/libs/libcurl/src/vquic/ngtcp2.c index 097cca44b0..fadcf70be0 100644 --- a/libs/libcurl/src/vquic/ngtcp2.c +++ b/libs/libcurl/src/vquic/ngtcp2.c @@ -1,2254 +1,2266 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_NGTCP2 -#include <ngtcp2/ngtcp2.h> -#include <nghttp3/nghttp3.h> -#ifdef USE_OPENSSL -#include <openssl/err.h> -#ifdef OPENSSL_IS_BORINGSSL -#include <ngtcp2/ngtcp2_crypto_boringssl.h> -#else -#include <ngtcp2/ngtcp2_crypto_openssl.h> -#endif -#include "vtls/openssl.h" -#elif defined(USE_GNUTLS) -#include <ngtcp2/ngtcp2_crypto_gnutls.h> -#include "vtls/gtls.h" -#elif defined(USE_WOLFSSL) -#include <ngtcp2/ngtcp2_crypto_wolfssl.h> -#include "vtls/wolfssl.h" -#endif -#include "urldata.h" -#include "sendf.h" -#include "strdup.h" -#include "rand.h" -#include "ngtcp2.h" -#include "multiif.h" -#include "strcase.h" -#include "connect.h" -#include "strerror.h" -#include "dynbuf.h" -#include "vquic.h" -#include "h2h3.h" -#include "vtls/keylog.h" -#include "vtls/vtls.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* #define DEBUG_NGTCP2 */ -#ifdef CURLDEBUG -#define DEBUG_HTTP3 -#endif -#ifdef DEBUG_HTTP3 -#define H3BUGF(x) x -#else -#define H3BUGF(x) do { } while(0) -#endif - -#define H3_ALPN_H3_29 "\x5h3-29" -#define H3_ALPN_H3 "\x2h3" - -/* - * This holds outgoing HTTP/3 stream data that is used by nghttp3 until acked. - * It is used as a circular buffer. Add new bytes at the end until it reaches - * the far end, then start over at index 0 again. - */ - -#define H3_SEND_SIZE (256*1024) -struct h3out { - uint8_t buf[H3_SEND_SIZE]; - size_t used; /* number of bytes used in the buffer */ - size_t windex; /* index in the buffer where to start writing the next - data block */ -}; - -#define QUIC_MAX_STREAMS (256*1024) -#define QUIC_MAX_DATA (1*1024*1024) -#define QUIC_IDLE_TIMEOUT (60*NGTCP2_SECONDS) - -#ifdef USE_OPENSSL -#define QUIC_CIPHERS \ - "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \ - "POLY1305_SHA256:TLS_AES_128_CCM_SHA256" -#define QUIC_GROUPS "P-256:X25519:P-384:P-521" -#elif defined(USE_GNUTLS) -#define QUIC_PRIORITY \ - "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \ - "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \ - "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \ - "%DISABLE_TLS13_COMPAT_MODE" -#elif defined(USE_WOLFSSL) -#define QUIC_CIPHERS \ - "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \ - "POLY1305_SHA256:TLS_AES_128_CCM_SHA256" -#define QUIC_GROUPS "P-256:P-384:P-521" -#endif - -/* ngtcp2 default congestion controller does not perform pacing. Limit - the maximum packet burst to MAX_PKT_BURST packets. */ -#define MAX_PKT_BURST 10 - -static CURLcode ng_process_ingress(struct Curl_easy *data, - curl_socket_t sockfd, - struct quicsocket *qs); -static CURLcode ng_flush_egress(struct Curl_easy *data, int sockfd, - struct quicsocket *qs); -static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, - uint64_t datalen, void *user_data, - void *stream_user_data); - -static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) -{ - struct quicsocket *qs = conn_ref->user_data; - return qs->qconn; -} - -static ngtcp2_tstamp timestamp(void) -{ - struct curltime ct = Curl_now(); - return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS; -} - -#ifdef DEBUG_NGTCP2 -static void quic_printf(void *user_data, const char *fmt, ...) -{ - va_list ap; - (void)user_data; /* TODO, use this to do infof() instead long-term */ - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - fprintf(stderr, "\n"); -} -#endif - -static void qlog_callback(void *user_data, uint32_t flags, - const void *data, size_t datalen) -{ - struct quicsocket *qs = (struct quicsocket *)user_data; - (void)flags; - if(qs->qlogfd != -1) { - ssize_t rc = write(qs->qlogfd, data, datalen); - if(rc == -1) { - /* on write error, stop further write attempts */ - close(qs->qlogfd); - qs->qlogfd = -1; - } - } - -} - -static void quic_settings(struct quicsocket *qs, - uint64_t stream_buffer_size) -{ - ngtcp2_settings *s = &qs->settings; - ngtcp2_transport_params *t = &qs->transport_params; - ngtcp2_settings_default(s); - ngtcp2_transport_params_default(t); -#ifdef DEBUG_NGTCP2 - s->log_printf = quic_printf; -#else - s->log_printf = NULL; -#endif - s->initial_ts = timestamp(); - t->initial_max_stream_data_bidi_local = stream_buffer_size; - t->initial_max_stream_data_bidi_remote = QUIC_MAX_STREAMS; - t->initial_max_stream_data_uni = QUIC_MAX_STREAMS; - t->initial_max_data = QUIC_MAX_DATA; - t->initial_max_streams_bidi = 1; - t->initial_max_streams_uni = 3; - t->max_idle_timeout = QUIC_IDLE_TIMEOUT; - if(qs->qlogfd != -1) { - s->qlog.write = qlog_callback; - } -} - -#ifdef USE_OPENSSL -static void keylog_callback(const SSL *ssl, const char *line) -{ - (void)ssl; - Curl_tls_keylog_write_line(line); -} -#elif defined(USE_GNUTLS) -static int keylog_callback(gnutls_session_t session, const char *label, - const gnutls_datum_t *secret) -{ - gnutls_datum_t crandom; - gnutls_datum_t srandom; - - gnutls_session_get_random(session, &crandom, &srandom); - if(crandom.size != 32) { - return -1; - } - - Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size); - return 0; -} -#elif defined(USE_WOLFSSL) -#if defined(HAVE_SECRET_CALLBACK) -static void keylog_callback(const WOLFSSL *ssl, const char *line) -{ - (void)ssl; - Curl_tls_keylog_write_line(line); -} -#endif -#endif - -static int init_ngh3_conn(struct quicsocket *qs); - -#ifdef USE_OPENSSL -static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); - -#ifdef OPENSSL_IS_BORINGSSL - if(ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) { - failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed"); - return NULL; - } -#else - if(ngtcp2_crypto_openssl_configure_client_context(ssl_ctx) != 0) { - failf(data, "ngtcp2_crypto_openssl_configure_client_context failed"); - return NULL; - } -#endif - - SSL_CTX_set_default_verify_paths(ssl_ctx); - -#ifdef OPENSSL_IS_BORINGSSL - if(SSL_CTX_set1_curves_list(ssl_ctx, QUIC_GROUPS) != 1) { - failf(data, "SSL_CTX_set1_curves_list failed"); - return NULL; - } -#else - if(SSL_CTX_set_ciphersuites(ssl_ctx, QUIC_CIPHERS) != 1) { - char error_buffer[256]; - ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); - failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer); - return NULL; - } - - if(SSL_CTX_set1_groups_list(ssl_ctx, QUIC_GROUPS) != 1) { - failf(data, "SSL_CTX_set1_groups_list failed"); - return NULL; - } -#endif - - /* Open the file if a TLS or QUIC backend has not done this before. */ - Curl_tls_keylog_open(); - if(Curl_tls_keylog_enabled()) { - SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); - } - - if(conn->ssl_config.verifypeer) { - const char * const ssl_cafile = conn->ssl_config.CAfile; - const char * const ssl_capath = conn->ssl_config.CApath; - - if(ssl_cafile || ssl_capath) { - SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); - /* tell OpenSSL where to find CA certificates that are used to verify - the server's certificate. */ - if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate verify locations:" - " CAfile: %s CApath: %s", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); - return NULL; - } - infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); - infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); - } -#ifdef CURL_CA_FALLBACK - else { - /* verifying the peer without any CA certificates won't work so - use openssl's built-in default as fallback */ - SSL_CTX_set_default_verify_paths(ssl_ctx); - } -#endif - } - return ssl_ctx; -} - -static CURLcode quic_set_client_cert(struct Curl_easy *data, - struct quicsocket *qs) -{ - struct connectdata *conn = data->conn; - SSL_CTX *ssl_ctx = qs->sslctx; - char *const ssl_cert = SSL_SET_OPTION(primary.clientcert); - const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob); - const char *const ssl_cert_type = SSL_SET_OPTION(cert_type); - - if(ssl_cert || ssl_cert_blob || ssl_cert_type) { - return Curl_ossl_set_client_cert( - data, ssl_ctx, ssl_cert, ssl_cert_blob, ssl_cert_type, - SSL_SET_OPTION(key), SSL_SET_OPTION(key_blob), - SSL_SET_OPTION(key_type), SSL_SET_OPTION(key_passwd)); - } - - return CURLE_OK; -} - -/** SSL callbacks ***/ - -static int quic_init_ssl(struct quicsocket *qs) -{ - const uint8_t *alpn = NULL; - size_t alpnlen = 0; - /* this will need some attention when HTTPS proxy over QUIC get fixed */ - const char * const hostname = qs->conn->host.name; - - DEBUGASSERT(!qs->ssl); - qs->ssl = SSL_new(qs->sslctx); - - SSL_set_app_data(qs->ssl, &qs->conn_ref); - SSL_set_connect_state(qs->ssl); - SSL_set_quic_use_legacy_codepoint(qs->ssl, 0); - - alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3; - alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1; - if(alpn) - SSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen); - - /* set SNI */ - SSL_set_tlsext_host_name(qs->ssl, hostname); - return 0; -} -#elif defined(USE_GNUTLS) -static int quic_init_ssl(struct quicsocket *qs) -{ - gnutls_datum_t alpn[2]; - /* this will need some attention when HTTPS proxy over QUIC get fixed */ - const char * const hostname = qs->conn->host.name; - int rc; - - DEBUGASSERT(!qs->ssl); - - gnutls_init(&qs->ssl, GNUTLS_CLIENT); - gnutls_session_set_ptr(qs->ssl, &qs->conn_ref); - - if(ngtcp2_crypto_gnutls_configure_client_session(qs->ssl) != 0) { - H3BUGF(fprintf(stderr, - "ngtcp2_crypto_gnutls_configure_client_session failed\n")); - return 1; - } - - rc = gnutls_priority_set_direct(qs->ssl, QUIC_PRIORITY, NULL); - if(rc < 0) { - H3BUGF(fprintf(stderr, "gnutls_priority_set_direct failed: %s\n", - gnutls_strerror(rc))); - return 1; - } - - /* Open the file if a TLS or QUIC backend has not done this before. */ - Curl_tls_keylog_open(); - if(Curl_tls_keylog_enabled()) { - gnutls_session_set_keylog_function(qs->ssl, keylog_callback); - } - - if(qs->cred) - gnutls_certificate_free_credentials(qs->cred); - - rc = gnutls_certificate_allocate_credentials(&qs->cred); - if(rc < 0) { - H3BUGF(fprintf(stderr, - "gnutls_certificate_allocate_credentials failed: %s\n", - gnutls_strerror(rc))); - return 1; - } - - rc = gnutls_certificate_set_x509_system_trust(qs->cred); - if(rc < 0) { - H3BUGF(fprintf(stderr, - "gnutls_certificate_set_x509_system_trust failed: %s\n", - gnutls_strerror(rc))); - return 1; - } - - rc = gnutls_credentials_set(qs->ssl, GNUTLS_CRD_CERTIFICATE, qs->cred); - if(rc < 0) { - H3BUGF(fprintf(stderr, "gnutls_credentials_set failed: %s\n", - gnutls_strerror(rc))); - return 1; - } - - /* strip the first byte (the length) from NGHTTP3_ALPN_H3 */ - alpn[0].data = (unsigned char *)H3_ALPN_H3_29 + 1; - alpn[0].size = sizeof(H3_ALPN_H3_29) - 2; - alpn[1].data = (unsigned char *)H3_ALPN_H3 + 1; - alpn[1].size = sizeof(H3_ALPN_H3) - 2; - - gnutls_alpn_set_protocols(qs->ssl, alpn, 2, GNUTLS_ALPN_MANDATORY); - - /* set SNI */ - gnutls_server_name_set(qs->ssl, GNUTLS_NAME_DNS, hostname, strlen(hostname)); - return 0; -} -#elif defined(USE_WOLFSSL) - -static WOLFSSL_CTX *quic_ssl_ctx(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - WOLFSSL_CTX *ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); - - if(ngtcp2_crypto_wolfssl_configure_client_context(ssl_ctx) != 0) { - failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed"); - return NULL; - } - - wolfSSL_CTX_set_default_verify_paths(ssl_ctx); - - if(wolfSSL_CTX_set_cipher_list(ssl_ctx, QUIC_CIPHERS) != 1) { - char error_buffer[256]; - ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); - failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer); - return NULL; - } - - if(wolfSSL_CTX_set1_groups_list(ssl_ctx, (char *)QUIC_GROUPS) != 1) { - failf(data, "SSL_CTX_set1_groups_list failed"); - return NULL; - } - - /* Open the file if a TLS or QUIC backend has not done this before. */ - Curl_tls_keylog_open(); - if(Curl_tls_keylog_enabled()) { -#if defined(HAVE_SECRET_CALLBACK) - wolfSSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); -#else - failf(data, "wolfSSL was built without keylog callback"); - return NULL; -#endif - } - - if(conn->ssl_config.verifypeer) { - const char * const ssl_cafile = conn->ssl_config.CAfile; - const char * const ssl_capath = conn->ssl_config.CApath; - - if(ssl_cafile || ssl_capath) { - wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); - /* tell wolfSSL where to find CA certificates that are used to verify - the server's certificate. */ - if(!wolfSSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate verify locations:" - " CAfile: %s CApath: %s", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); - return NULL; - } - infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); - infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); - } -#ifdef CURL_CA_FALLBACK - else { - /* verifying the peer without any CA certificates won't work so - use wolfssl's built-in default as fallback */ - wolfSSL_CTX_set_default_verify_paths(ssl_ctx); - } -#endif - } - else { - wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL); - } - - return ssl_ctx; -} - -/** SSL callbacks ***/ - -static int quic_init_ssl(struct quicsocket *qs) -{ - const uint8_t *alpn = NULL; - size_t alpnlen = 0; - /* this will need some attention when HTTPS proxy over QUIC get fixed */ - const char * const hostname = qs->conn->host.name; - - DEBUGASSERT(!qs->ssl); - qs->ssl = SSL_new(qs->sslctx); - - wolfSSL_set_app_data(qs->ssl, &qs->conn_ref); - wolfSSL_set_connect_state(qs->ssl); - wolfSSL_set_quic_use_legacy_codepoint(qs->ssl, 0); - - alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3; - alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1; - if(alpn) - wolfSSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen); - - /* set SNI */ - wolfSSL_UseSNI(qs->ssl, WOLFSSL_SNI_HOST_NAME, - hostname, (unsigned short)strlen(hostname)); - - return 0; -} -#endif /* defined(USE_WOLFSSL) */ - -static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data) -{ - (void)user_data; - (void)tconn; - return 0; -} - -static void extend_stream_window(ngtcp2_conn *tconn, - struct HTTP *stream) -{ - size_t thismuch = stream->unacked_window; - ngtcp2_conn_extend_max_stream_offset(tconn, stream->stream3_id, thismuch); - ngtcp2_conn_extend_max_offset(tconn, thismuch); - stream->unacked_window = 0; -} - - -static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, - int64_t stream_id, uint64_t offset, - const uint8_t *buf, size_t buflen, - void *user_data, void *stream_user_data) -{ - struct quicsocket *qs = (struct quicsocket *)user_data; - nghttp3_ssize nconsumed; - int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0; - (void)offset; - (void)stream_user_data; - - nconsumed = - nghttp3_conn_read_stream(qs->h3conn, stream_id, buf, buflen, fin); - if(nconsumed < 0) { - ngtcp2_connection_close_error_set_application_error( - &qs->last_error, nghttp3_err_infer_quic_app_error_code((int)nconsumed), - NULL, 0); - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - /* number of bytes inside buflen which consists of framing overhead - * including QPACK HEADERS. In other words, it does not consume payload of - * DATA frame. */ - ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed); - ngtcp2_conn_extend_max_offset(tconn, nconsumed); - - return 0; -} - -static int -cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id, - uint64_t offset, uint64_t datalen, void *user_data, - void *stream_user_data) -{ - struct quicsocket *qs = (struct quicsocket *)user_data; - int rv; - (void)stream_id; - (void)tconn; - (void)offset; - (void)datalen; - (void)stream_user_data; - - rv = nghttp3_conn_add_ack_offset(qs->h3conn, stream_id, datalen); - if(rv) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, - int64_t stream_id, uint64_t app_error_code, - void *user_data, void *stream_user_data) -{ - struct quicsocket *qs = (struct quicsocket *)user_data; - int rv; - (void)tconn; - (void)stream_user_data; - /* stream is closed... */ - - if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) { - app_error_code = NGHTTP3_H3_NO_ERROR; - } - - rv = nghttp3_conn_close_stream(qs->h3conn, stream_id, - app_error_code); - if(rv) { - ngtcp2_connection_close_error_set_application_error( - &qs->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0); - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, - uint64_t final_size, uint64_t app_error_code, - void *user_data, void *stream_user_data) -{ - struct quicsocket *qs = (struct quicsocket *)user_data; - int rv; - (void)tconn; - (void)final_size; - (void)app_error_code; - (void)stream_user_data; - - rv = nghttp3_conn_shutdown_stream_read(qs->h3conn, stream_id); - if(rv) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) -{ - struct quicsocket *qs = (struct quicsocket *)user_data; - int rv; - (void)tconn; - (void)app_error_code; - (void)stream_user_data; - - rv = nghttp3_conn_shutdown_stream_read(qs->h3conn, stream_id); - if(rv) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn, - uint64_t max_streams, - void *user_data) -{ - (void)tconn; - (void)max_streams; - (void)user_data; - - return 0; -} - -static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id, - uint64_t max_data, void *user_data, - void *stream_user_data) -{ - struct quicsocket *qs = (struct quicsocket *)user_data; - int rv; - (void)tconn; - (void)max_data; - (void)stream_user_data; - - rv = nghttp3_conn_unblock_stream(qs->h3conn, stream_id); - if(rv) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static void cb_rand(uint8_t *dest, size_t destlen, - const ngtcp2_rand_ctx *rand_ctx) -{ - CURLcode result; - (void)rand_ctx; - - result = Curl_rand(NULL, dest, destlen); - if(result) { - /* cb_rand is only used for non-cryptographic context. If Curl_rand - failed, just fill 0 and call it *random*. */ - memset(dest, 0, destlen); - } -} - -static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid, - uint8_t *token, size_t cidlen, - void *user_data) -{ - CURLcode result; - (void)tconn; - (void)user_data; - - result = Curl_rand(NULL, cid->data, cidlen); - if(result) - return NGTCP2_ERR_CALLBACK_FAILURE; - cid->datalen = cidlen; - - result = Curl_rand(NULL, token, NGTCP2_STATELESS_RESET_TOKENLEN); - if(result) - return NGTCP2_ERR_CALLBACK_FAILURE; - - return 0; -} - -static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_crypto_level level, - void *user_data) -{ - struct quicsocket *qs = (struct quicsocket *)user_data; - (void)tconn; - - if(level != NGTCP2_CRYPTO_LEVEL_APPLICATION) { - return 0; - } - - if(init_ngh3_conn(qs) != CURLE_OK) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static ngtcp2_callbacks ng_callbacks = { - ngtcp2_crypto_client_initial_cb, - NULL, /* recv_client_initial */ - ngtcp2_crypto_recv_crypto_data_cb, - cb_handshake_completed, - NULL, /* recv_version_negotiation */ - ngtcp2_crypto_encrypt_cb, - ngtcp2_crypto_decrypt_cb, - ngtcp2_crypto_hp_mask_cb, - cb_recv_stream_data, - cb_acked_stream_data_offset, - NULL, /* stream_open */ - cb_stream_close, - NULL, /* recv_stateless_reset */ - ngtcp2_crypto_recv_retry_cb, - cb_extend_max_local_streams_bidi, - NULL, /* extend_max_local_streams_uni */ - cb_rand, - cb_get_new_connection_id, - NULL, /* remove_connection_id */ - ngtcp2_crypto_update_key_cb, /* update_key */ - NULL, /* path_validation */ - NULL, /* select_preferred_addr */ - cb_stream_reset, - NULL, /* extend_max_remote_streams_bidi */ - NULL, /* extend_max_remote_streams_uni */ - cb_extend_max_stream_data, - NULL, /* dcid_status */ - NULL, /* handshake_confirmed */ - NULL, /* recv_new_token */ - ngtcp2_crypto_delete_crypto_aead_ctx_cb, - ngtcp2_crypto_delete_crypto_cipher_ctx_cb, - NULL, /* recv_datagram */ - NULL, /* ack_datagram */ - NULL, /* lost_datagram */ - ngtcp2_crypto_get_path_challenge_data_cb, - cb_stream_stop_sending, - NULL, /* version_negotiation */ - cb_recv_rx_key, - NULL, /* recv_tx_key */ - NULL, /* early_data_rejected */ -}; - -/* - * Might be called twice for happy eyeballs. - */ -CURLcode Curl_quic_connect(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t sockfd, - int sockindex, - const struct sockaddr *addr, - socklen_t addrlen) -{ - int rc; - int rv; - CURLcode result; - ngtcp2_path path; /* TODO: this must be initialized properly */ - struct quicsocket *qs = &conn->hequic[sockindex]; - char ipbuf[40]; - int port; - int qfd; - - if(qs->conn) - Curl_quic_disconnect(data, conn, sockindex); - qs->conn = conn; - - /* extract the used address as a string */ - if(!Curl_addr2string((struct sockaddr*)addr, addrlen, ipbuf, &port)) { - char buffer[STRERROR_LEN]; - failf(data, "ssrem inet_ntop() failed with errno %d: %s", - SOCKERRNO, Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - infof(data, "Connect socket %d over QUIC to %s:%d", - sockfd, ipbuf, port); - - qs->version = NGTCP2_PROTO_VER_MAX; -#ifdef USE_OPENSSL - qs->sslctx = quic_ssl_ctx(data); - if(!qs->sslctx) - return CURLE_QUIC_CONNECT_ERROR; - - result = quic_set_client_cert(data, qs); - if(result) - return result; -#elif defined(USE_WOLFSSL) - qs->sslctx = quic_ssl_ctx(data); - if(!qs->sslctx) - return CURLE_QUIC_CONNECT_ERROR; -#endif - - if(quic_init_ssl(qs)) - return CURLE_QUIC_CONNECT_ERROR; - - qs->dcid.datalen = NGTCP2_MAX_CIDLEN; - result = Curl_rand(data, qs->dcid.data, NGTCP2_MAX_CIDLEN); - if(result) - return result; - - qs->scid.datalen = NGTCP2_MAX_CIDLEN; - result = Curl_rand(data, qs->scid.data, NGTCP2_MAX_CIDLEN); - if(result) - return result; - - (void)Curl_qlogdir(data, qs->scid.data, NGTCP2_MAX_CIDLEN, &qfd); - qs->qlogfd = qfd; /* -1 if failure above */ - quic_settings(qs, data->set.buffer_size); - - qs->local_addrlen = sizeof(qs->local_addr); - rv = getsockname(sockfd, (struct sockaddr *)&qs->local_addr, - &qs->local_addrlen); - if(rv == -1) - return CURLE_QUIC_CONNECT_ERROR; - - ngtcp2_addr_init(&path.local, (struct sockaddr *)&qs->local_addr, - qs->local_addrlen); - ngtcp2_addr_init(&path.remote, addr, addrlen); - - rc = ngtcp2_conn_client_new(&qs->qconn, &qs->dcid, &qs->scid, &path, - NGTCP2_PROTO_VER_V1, &ng_callbacks, - &qs->settings, &qs->transport_params, NULL, qs); - if(rc) - return CURLE_QUIC_CONNECT_ERROR; - - ngtcp2_conn_set_tls_native_handle(qs->qconn, qs->ssl); - - ngtcp2_connection_close_error_default(&qs->last_error); - -#if defined(__linux__) && defined(UDP_SEGMENT) && defined(HAVE_SENDMSG) - qs->no_gso = FALSE; -#else - qs->no_gso = TRUE; -#endif - - qs->num_blocked_pkt = 0; - qs->num_blocked_pkt_sent = 0; - memset(&qs->blocked_pkt, 0, sizeof(qs->blocked_pkt)); - - qs->pktbuflen = NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE * MAX_PKT_BURST; - qs->pktbuf = malloc(qs->pktbuflen); - if(!qs->pktbuf) { - ngtcp2_conn_del(qs->qconn); - qs->qconn = NULL; - return CURLE_OUT_OF_MEMORY; - } - - qs->conn_ref.get_conn = get_conn; - qs->conn_ref.user_data = qs; - - return CURLE_OK; -} - -/* - * Store ngtcp2 version info in this buffer. - */ -void Curl_quic_ver(char *p, size_t len) -{ - const ngtcp2_info *ng2 = ngtcp2_version(0); - const nghttp3_info *ht3 = nghttp3_version(0); - (void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s", - ng2->version_str, ht3->version_str); -} - -static int ng_getsock(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t *socks) -{ - struct SingleRequest *k = &data->req; - int bitmap = GETSOCK_BLANK; - struct HTTP *stream = data->req.p.http; - struct quicsocket *qs = conn->quic; - - socks[0] = conn->sock[FIRSTSOCKET]; - - /* in a HTTP/2 connection we can basically always get a frame so we should - always be ready for one */ - bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); - - /* we're still uploading or the HTTP/2 layer wants to send data */ - if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND && - (!stream->h3out || stream->h3out->used < H3_SEND_SIZE) && - ngtcp2_conn_get_cwnd_left(qs->qconn) && - ngtcp2_conn_get_max_data_left(qs->qconn) && - nghttp3_conn_is_stream_writable(qs->h3conn, stream->stream3_id)) - bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); - - return bitmap; -} - -static void qs_disconnect(struct quicsocket *qs) -{ - char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; - ngtcp2_tstamp ts; - ngtcp2_ssize rc; - - if(!qs->conn) /* already closed */ - return; - ts = timestamp(); - rc = ngtcp2_conn_write_connection_close(qs->qconn, NULL, /* path */ - NULL, /* pkt_info */ - (uint8_t *)buffer, sizeof(buffer), - &qs->last_error, ts); - if(rc > 0) { - while((send(qs->conn->sock[FIRSTSOCKET], buffer, rc, 0) == -1) && - SOCKERRNO == EINTR); - } - - qs->conn = NULL; - if(qs->qlogfd != -1) { - close(qs->qlogfd); - qs->qlogfd = -1; - } - if(qs->ssl) -#ifdef USE_OPENSSL - SSL_free(qs->ssl); -#elif defined(USE_GNUTLS) - gnutls_deinit(qs->ssl); -#elif defined(USE_WOLFSSL) - wolfSSL_free(qs->ssl); -#endif - qs->ssl = NULL; -#ifdef USE_GNUTLS - if(qs->cred) { - gnutls_certificate_free_credentials(qs->cred); - qs->cred = NULL; - } -#endif - free(qs->pktbuf); - nghttp3_conn_del(qs->h3conn); - ngtcp2_conn_del(qs->qconn); -#ifdef USE_OPENSSL - SSL_CTX_free(qs->sslctx); -#elif defined(USE_WOLFSSL) - wolfSSL_CTX_free(qs->sslctx); -#endif -} - -void Curl_quic_disconnect(struct Curl_easy *data, - struct connectdata *conn, - int tempindex) -{ - (void)data; - if(conn->transport == TRNSPRT_QUIC) - qs_disconnect(&conn->hequic[tempindex]); -} - -static CURLcode ng_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection) -{ - (void)dead_connection; - Curl_quic_disconnect(data, conn, 0); - Curl_quic_disconnect(data, conn, 1); - return CURLE_OK; -} - -static unsigned int ng_conncheck(struct Curl_easy *data, - struct connectdata *conn, - unsigned int checks_to_perform) -{ - (void)data; - (void)conn; - (void)checks_to_perform; - return CONNRESULT_NONE; -} - -static const struct Curl_handler Curl_handler_http3 = { - "HTTPS", /* scheme */ - ZERO_NULL, /* setup_connection */ - Curl_http, /* do_it */ - Curl_http_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ng_getsock, /* proto_getsock */ - ng_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ng_getsock, /* perform_getsock */ - ng_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ng_conncheck, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_HTTP, /* defport */ - CURLPROTO_HTTPS, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_SSL | PROTOPT_STREAM /* flags */ -}; - -static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) -{ - struct Curl_easy *data = stream_user_data; - struct HTTP *stream = data->req.p.http; - (void)conn; - (void)stream_id; - (void)app_error_code; - (void)user_data; - H3BUGF(infof(data, "cb_h3_stream_close CALLED")); - - stream->closed = TRUE; - stream->error3 = app_error_code; - Curl_expire(data, 0, EXPIRE_QUIC); - /* make sure that ngh3_stream_recv is called again to complete the transfer - even if there are no more packets to be received from the server. */ - data->state.drain = 1; - return 0; -} - -/* - * write_data() copies data to the stream's receive buffer. If not enough - * space is available in the receive buffer, it copies the rest to the - * stream's overflow buffer. - */ -static CURLcode write_data(struct HTTP *stream, const void *mem, size_t memlen) -{ - CURLcode result = CURLE_OK; - const char *buf = mem; - size_t ncopy = memlen; - /* copy as much as possible to the receive buffer */ - if(stream->len) { - size_t len = CURLMIN(ncopy, stream->len); - memcpy(stream->mem, buf, len); - stream->len -= len; - stream->memlen += len; - stream->mem += len; - buf += len; - ncopy -= len; - } - /* copy the rest to the overflow buffer */ - if(ncopy) - result = Curl_dyn_addn(&stream->overflow, buf, ncopy); - return result; -} - -static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream_id, - const uint8_t *buf, size_t buflen, - void *user_data, void *stream_user_data) -{ - struct Curl_easy *data = stream_user_data; - struct HTTP *stream = data->req.p.http; - CURLcode result = CURLE_OK; - (void)conn; - - result = write_data(stream, buf, buflen); - if(result) { - return -1; - } - stream->unacked_window += buflen; - (void)stream_id; - (void)user_data; - return 0; -} - -static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id, - size_t consumed, void *user_data, - void *stream_user_data) -{ - struct quicsocket *qs = user_data; - (void)conn; - (void)stream_user_data; - (void)stream_id; - - ngtcp2_conn_extend_max_stream_offset(qs->qconn, stream_id, consumed); - ngtcp2_conn_extend_max_offset(qs->qconn, consumed); - return 0; -} - -/* Decode HTTP status code. Returns -1 if no valid status code was - decoded. (duplicate from http2.c) */ -static int decode_status_code(const uint8_t *value, size_t len) -{ - int i; - int res; - - if(len != 3) { - return -1; - } - - res = 0; - - for(i = 0; i < 3; ++i) { - char c = value[i]; - - if(c < '0' || c > '9') { - return -1; - } - - res *= 10; - res += c - '0'; - } - - return res; -} - -static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, - int fin, void *user_data, void *stream_user_data) -{ - struct Curl_easy *data = stream_user_data; - struct HTTP *stream = data->req.p.http; - CURLcode result = CURLE_OK; - (void)conn; - (void)stream_id; - (void)user_data; - (void)fin; - - /* add a CRLF only if we've received some headers */ - if(stream->firstheader) { - result = write_data(stream, "\r\n", 2); - if(result) { - return -1; - } - } - - if(stream->status_code / 100 != 1) { - stream->bodystarted = TRUE; - } - return 0; -} - -static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, - int32_t token, nghttp3_rcbuf *name, - nghttp3_rcbuf *value, uint8_t flags, - void *user_data, void *stream_user_data) -{ - nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); - nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); - struct Curl_easy *data = stream_user_data; - struct HTTP *stream = data->req.p.http; - CURLcode result = CURLE_OK; - (void)conn; - (void)stream_id; - (void)token; - (void)flags; - (void)user_data; - - if(token == NGHTTP3_QPACK_TOKEN__STATUS) { - char line[14]; /* status line is always 13 characters long */ - size_t ncopy; - stream->status_code = decode_status_code(h3val.base, h3val.len); - DEBUGASSERT(stream->status_code != -1); - ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", - stream->status_code); - result = write_data(stream, line, ncopy); - if(result) { - return -1; - } - } - else { - /* store as a HTTP1-style header */ - result = write_data(stream, h3name.base, h3name.len); - if(result) { - return -1; - } - result = write_data(stream, ": ", 2); - if(result) { - return -1; - } - result = write_data(stream, h3val.base, h3val.len); - if(result) { - return -1; - } - result = write_data(stream, "\r\n", 2); - if(result) { - return -1; - } - } - - stream->firstheader = TRUE; - return 0; -} - -static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) -{ - struct quicsocket *qs = user_data; - int rv; - (void)conn; - (void)stream_user_data; - - rv = ngtcp2_conn_shutdown_stream_read(qs->qconn, stream_id, app_error_code); - if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) { - struct quicsocket *qs = user_data; - int rv; - (void)conn; - (void)stream_user_data; - - rv = ngtcp2_conn_shutdown_stream_write(qs->qconn, stream_id, app_error_code); - if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static nghttp3_callbacks ngh3_callbacks = { - cb_h3_acked_stream_data, /* acked_stream_data */ - cb_h3_stream_close, - cb_h3_recv_data, - cb_h3_deferred_consume, - NULL, /* begin_headers */ - cb_h3_recv_header, - cb_h3_end_headers, - NULL, /* begin_trailers */ - cb_h3_recv_header, - NULL, /* end_trailers */ - cb_h3_stop_sending, - NULL, /* end_stream */ - cb_h3_reset_stream, - NULL /* shutdown */ -}; - -static int init_ngh3_conn(struct quicsocket *qs) -{ - CURLcode result; - int rc; - int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id; - - if(ngtcp2_conn_get_max_local_streams_uni(qs->qconn) < 3) { - return CURLE_QUIC_CONNECT_ERROR; - } - - nghttp3_settings_default(&qs->h3settings); - - rc = nghttp3_conn_client_new(&qs->h3conn, - &ngh3_callbacks, - &qs->h3settings, - nghttp3_mem_default(), - qs); - if(rc) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } - - rc = ngtcp2_conn_open_uni_stream(qs->qconn, &ctrl_stream_id, NULL); - if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto fail; - } - - rc = nghttp3_conn_bind_control_stream(qs->h3conn, ctrl_stream_id); - if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto fail; - } - - rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_enc_stream_id, NULL); - if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto fail; - } - - rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_dec_stream_id, NULL); - if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto fail; - } - - rc = nghttp3_conn_bind_qpack_streams(qs->h3conn, qpack_enc_stream_id, - qpack_dec_stream_id); - if(rc) { - result = CURLE_QUIC_CONNECT_ERROR; - goto fail; - } - - return CURLE_OK; - fail: - - return result; -} - -static Curl_recv ngh3_stream_recv; -static Curl_send ngh3_stream_send; - -static size_t drain_overflow_buffer(struct HTTP *stream) -{ - size_t overlen = Curl_dyn_len(&stream->overflow); - size_t ncopy = CURLMIN(overlen, stream->len); - if(ncopy > 0) { - memcpy(stream->mem, Curl_dyn_ptr(&stream->overflow), ncopy); - stream->len -= ncopy; - stream->mem += ncopy; - stream->memlen += ncopy; - if(ncopy != overlen) - /* make the buffer only keep the tail */ - (void)Curl_dyn_tail(&stream->overflow, overlen - ncopy); - else - Curl_dyn_reset(&stream->overflow); - } - return ncopy; -} - -/* incoming data frames on the h3 stream */ -static ssize_t ngh3_stream_recv(struct Curl_easy *data, - int sockindex, - char *buf, - size_t buffersize, - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[sockindex]; - struct HTTP *stream = data->req.p.http; - struct quicsocket *qs = conn->quic; - - if(!stream->memlen) { - /* remember where to store incoming data for this stream and how big the - buffer is */ - stream->mem = buf; - stream->len = buffersize; - } - /* else, there's data in the buffer already */ - - /* if there's data in the overflow buffer from a previous call, copy as much - as possible to the receive buffer before receiving more */ - drain_overflow_buffer(stream); - - if(ng_process_ingress(data, sockfd, qs)) { - *curlcode = CURLE_RECV_ERROR; - return -1; - } - if(ng_flush_egress(data, sockfd, qs)) { - *curlcode = CURLE_SEND_ERROR; - return -1; - } - - if(stream->memlen) { - ssize_t memlen = stream->memlen; - /* data arrived */ - *curlcode = CURLE_OK; - /* reset to allow more data to come */ - stream->memlen = 0; - stream->mem = buf; - stream->len = buffersize; - /* extend the stream window with the data we're consuming and send out - any additional packets to tell the server that we can receive more */ - extend_stream_window(qs->qconn, stream); - if(ng_flush_egress(data, sockfd, qs)) { - *curlcode = CURLE_SEND_ERROR; - return -1; - } - return memlen; - } - - if(stream->closed) { - if(stream->error3 != NGHTTP3_H3_NO_ERROR) { - failf(data, - "HTTP/3 stream %" PRId64 " was not closed cleanly: (err %" PRIu64 - ")", - stream->stream3_id, stream->error3); - *curlcode = CURLE_HTTP3; - return -1; - } - - if(!stream->bodystarted) { - failf(data, - "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting" - " all response header fields, treated as error", - stream->stream3_id); - *curlcode = CURLE_HTTP3; - return -1; - } - - *curlcode = CURLE_OK; - return 0; - } - - infof(data, "ngh3_stream_recv returns 0 bytes and EAGAIN"); - *curlcode = CURLE_AGAIN; - return -1; -} - -/* this amount of data has now been acked on this stream */ -static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, - uint64_t datalen, void *user_data, - void *stream_user_data) -{ - struct Curl_easy *data = stream_user_data; - struct HTTP *stream = data->req.p.http; - (void)user_data; - - if(!data->set.postfields) { - stream->h3out->used -= datalen; - H3BUGF(infof(data, - "cb_h3_acked_stream_data, %zd bytes, %zd left unacked", - datalen, stream->h3out->used)); - DEBUGASSERT(stream->h3out->used < H3_SEND_SIZE); - - if(stream->h3out->used == 0) { - int rv = nghttp3_conn_resume_stream(conn, stream_id); - if(rv) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - } - } - return 0; -} - -static nghttp3_ssize cb_h3_readfunction(nghttp3_conn *conn, int64_t stream_id, - nghttp3_vec *vec, size_t veccnt, - uint32_t *pflags, void *user_data, - void *stream_user_data) -{ - struct Curl_easy *data = stream_user_data; - size_t nread; - struct HTTP *stream = data->req.p.http; - (void)conn; - (void)stream_id; - (void)user_data; - (void)veccnt; - - if(data->set.postfields) { - vec[0].base = data->set.postfields; - vec[0].len = data->state.infilesize; - *pflags = NGHTTP3_DATA_FLAG_EOF; - return 1; - } - - if(stream->upload_len && H3_SEND_SIZE <= stream->h3out->used) { - return NGHTTP3_ERR_WOULDBLOCK; - } - - nread = CURLMIN(stream->upload_len, H3_SEND_SIZE - stream->h3out->used); - if(nread > 0) { - /* nghttp3 wants us to hold on to the data until it tells us it is okay to - delete it. Append the data at the end of the h3out buffer. Since we can - only return consecutive data, copy the amount that fits and the next - part comes in next invoke. */ - struct h3out *out = stream->h3out; - if(nread + out->windex > H3_SEND_SIZE) - nread = H3_SEND_SIZE - out->windex; - - memcpy(&out->buf[out->windex], stream->upload_mem, nread); - - /* that's the chunk we return to nghttp3 */ - vec[0].base = &out->buf[out->windex]; - vec[0].len = nread; - - out->windex += nread; - out->used += nread; - - if(out->windex == H3_SEND_SIZE) - out->windex = 0; /* wrap */ - stream->upload_mem += nread; - stream->upload_len -= nread; - if(data->state.infilesize != -1) { - stream->upload_left -= nread; - if(!stream->upload_left) - *pflags = NGHTTP3_DATA_FLAG_EOF; - } - H3BUGF(infof(data, "cb_h3_readfunction %zd bytes%s (at %zd unacked)", - nread, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"", - out->used)); - } - if(stream->upload_done && !stream->upload_len && - (stream->upload_left <= 0)) { - H3BUGF(infof(data, "cb_h3_readfunction sets EOF")); - *pflags = NGHTTP3_DATA_FLAG_EOF; - return nread ? 1 : 0; - } - else if(!nread) { - return NGHTTP3_ERR_WOULDBLOCK; - } - return 1; -} - -/* Index where :authority header field will appear in request header - field list. */ -#define AUTHORITY_DST_IDX 3 - -static CURLcode http_request(struct Curl_easy *data, const void *mem, - size_t len) -{ - struct connectdata *conn = data->conn; - struct HTTP *stream = data->req.p.http; - size_t nheader; - struct quicsocket *qs = conn->quic; - CURLcode result = CURLE_OK; - nghttp3_nv *nva = NULL; - int64_t stream3_id; - int rc; - struct h3out *h3out = NULL; - struct h2h3req *hreq = NULL; - - rc = ngtcp2_conn_open_bidi_stream(qs->qconn, &stream3_id, NULL); - if(rc) { - failf(data, "can get bidi streams"); - result = CURLE_SEND_ERROR; - goto fail; - } - - stream->stream3_id = stream3_id; - stream->h3req = TRUE; /* senf off! */ - Curl_dyn_init(&stream->overflow, CURL_MAX_READ_SIZE); - - result = Curl_pseudo_headers(data, mem, len, &hreq); - if(result) - goto fail; - nheader = hreq->entries; - - nva = malloc(sizeof(nghttp3_nv) * nheader); - if(!nva) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } - else { - unsigned int i; - for(i = 0; i < nheader; i++) { - nva[i].name = (unsigned char *)hreq->header[i].name; - nva[i].namelen = hreq->header[i].namelen; - nva[i].value = (unsigned char *)hreq->header[i].value; - nva[i].valuelen = hreq->header[i].valuelen; - nva[i].flags = NGHTTP3_NV_FLAG_NONE; - } - } - - switch(data->state.httpreq) { - case HTTPREQ_POST: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - case HTTPREQ_PUT: { - nghttp3_data_reader data_reader; - if(data->state.infilesize != -1) - stream->upload_left = data->state.infilesize; - else - /* data sending without specifying the data amount up front */ - stream->upload_left = -1; /* unknown, but not zero */ - - data_reader.read_data = cb_h3_readfunction; - - h3out = calloc(sizeof(struct h3out), 1); - if(!h3out) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } - stream->h3out = h3out; - - rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id, - nva, nheader, &data_reader, data); - if(rc) { - result = CURLE_SEND_ERROR; - goto fail; - } - break; - } - default: - stream->upload_left = 0; /* nothing left to send */ - rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id, - nva, nheader, NULL, data); - if(rc) { - result = CURLE_SEND_ERROR; - goto fail; - } - break; - } - - Curl_safefree(nva); - - infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)", - stream3_id, (void *)data); - - Curl_pseudo_free(hreq); - return CURLE_OK; - -fail: - free(nva); - Curl_pseudo_free(hreq); - return result; -} -static ssize_t ngh3_stream_send(struct Curl_easy *data, - int sockindex, - const void *mem, - size_t len, - CURLcode *curlcode) -{ - ssize_t sent = 0; - struct connectdata *conn = data->conn; - struct quicsocket *qs = conn->quic; - curl_socket_t sockfd = conn->sock[sockindex]; - struct HTTP *stream = data->req.p.http; - - if(stream->closed) { - *curlcode = CURLE_HTTP3; - return -1; - } - - if(!stream->h3req) { - CURLcode result = http_request(data, mem, len); - if(result) { - *curlcode = CURLE_SEND_ERROR; - return -1; - } - /* Assume that mem of length len only includes HTTP/1.1 style - header fields. In other words, it does not contain request - body. */ - sent = len; - } - else { - H3BUGF(infof(data, "ngh3_stream_send() wants to send %zd bytes", - len)); - if(!stream->upload_len) { - stream->upload_mem = mem; - stream->upload_len = len; - (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id); - } - else { - *curlcode = CURLE_AGAIN; - return -1; - } - } - - if(ng_flush_egress(data, sockfd, qs)) { - *curlcode = CURLE_SEND_ERROR; - return -1; - } - - /* Reset post upload buffer after resumed. */ - if(stream->upload_mem) { - if(data->set.postfields) { - sent = len; - } - else { - sent = len - stream->upload_len; - } - - stream->upload_mem = NULL; - stream->upload_len = 0; - - if(sent == 0) { - *curlcode = CURLE_AGAIN; - return -1; - } - } - - *curlcode = CURLE_OK; - return sent; -} - -static CURLcode ng_has_connected(struct Curl_easy *data, - struct connectdata *conn, int tempindex) -{ - CURLcode result = CURLE_OK; - conn->recv[FIRSTSOCKET] = ngh3_stream_recv; - conn->send[FIRSTSOCKET] = ngh3_stream_send; - conn->handler = &Curl_handler_http3; - conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - conn->httpversion = 30; - conn->bundle->multiuse = BUNDLE_MULTIPLEX; - conn->quic = &conn->hequic[tempindex]; - - if(conn->ssl_config.verifyhost) { -#ifdef USE_OPENSSL - X509 *server_cert; - server_cert = SSL_get_peer_certificate(conn->quic->ssl); - if(!server_cert) { - return CURLE_PEER_FAILED_VERIFICATION; - } - result = Curl_ossl_verifyhost(data, conn, server_cert); - X509_free(server_cert); - if(result) - return result; - infof(data, "Verified certificate just fine"); -#elif defined(USE_GNUTLS) - result = Curl_gtls_verifyserver(data, conn, conn->quic->ssl, FIRSTSOCKET); -#elif defined(USE_WOLFSSL) - char *snihost = Curl_ssl_snihost(data, SSL_HOST_NAME(), NULL); - if(!snihost || - (wolfSSL_check_domain_name(conn->quic->ssl, snihost) == SSL_FAILURE)) - return CURLE_PEER_FAILED_VERIFICATION; - infof(data, "Verified certificate just fine"); -#endif - } - else - infof(data, "Skipped certificate verification"); -#ifdef USE_OPENSSL - if(data->set.ssl.certinfo) - /* asked to gather certificate info */ - (void)Curl_ossl_certchain(data, conn->quic->ssl); -#endif - return result; -} - -/* - * There can be multiple connection attempts going on in parallel. - */ -CURLcode Curl_quic_is_connected(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool *done) -{ - CURLcode result; - struct quicsocket *qs = &conn->hequic[sockindex]; - curl_socket_t sockfd = conn->tempsock[sockindex]; - - result = ng_process_ingress(data, sockfd, qs); - if(result) - goto error; - - result = ng_flush_egress(data, sockfd, qs); - if(result) - goto error; - - if(ngtcp2_conn_get_handshake_completed(qs->qconn)) { - result = ng_has_connected(data, conn, sockindex); - if(!result) - *done = TRUE; - } - - return result; - error: - (void)qs_disconnect(qs); - return result; - -} - -static CURLcode ng_process_ingress(struct Curl_easy *data, - curl_socket_t sockfd, - struct quicsocket *qs) -{ - ssize_t recvd; - int rv; - uint8_t buf[65536]; - size_t bufsize = sizeof(buf); - struct sockaddr_storage remote_addr; - socklen_t remote_addrlen; - ngtcp2_path path; - ngtcp2_tstamp ts = timestamp(); - ngtcp2_pkt_info pi = { 0 }; - - for(;;) { - remote_addrlen = sizeof(remote_addr); - while((recvd = recvfrom(sockfd, (char *)buf, bufsize, 0, - (struct sockaddr *)&remote_addr, - &remote_addrlen)) == -1 && - SOCKERRNO == EINTR) - ; - if(recvd == -1) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) - break; - - failf(data, "ngtcp2: recvfrom() unexpectedly returned %zd", recvd); - return CURLE_RECV_ERROR; - } - - ngtcp2_addr_init(&path.local, (struct sockaddr *)&qs->local_addr, - qs->local_addrlen); - ngtcp2_addr_init(&path.remote, (struct sockaddr *)&remote_addr, - remote_addrlen); - - rv = ngtcp2_conn_read_pkt(qs->qconn, &path, &pi, buf, recvd, ts); - if(rv) { - if(!qs->last_error.error_code) { - if(rv == NGTCP2_ERR_CRYPTO) { - ngtcp2_connection_close_error_set_transport_error_tls_alert( - &qs->last_error, ngtcp2_conn_get_tls_alert(qs->qconn), NULL, 0); - } - else { - ngtcp2_connection_close_error_set_transport_error_liberr( - &qs->last_error, rv, NULL, 0); - } - } - - if(rv == NGTCP2_ERR_CRYPTO) - /* this is a "TLS problem", but a failed certificate verification - is a common reason for this */ - return CURLE_PEER_FAILED_VERIFICATION; - return CURLE_RECV_ERROR; - } - } - - return CURLE_OK; -} - -static CURLcode do_sendmsg(size_t *sent, struct Curl_easy *data, int sockfd, - struct quicsocket *qs, const uint8_t *pkt, - size_t pktlen, size_t gsolen); - -static CURLcode send_packet_no_gso(size_t *psent, struct Curl_easy *data, - int sockfd, struct quicsocket *qs, - const uint8_t *pkt, size_t pktlen, - size_t gsolen) -{ - const uint8_t *p, *end = pkt + pktlen; - size_t sent; - - *psent = 0; - - for(p = pkt; p < end; p += gsolen) { - size_t len = CURLMIN(gsolen, (size_t)(end - p)); - CURLcode curlcode = do_sendmsg(&sent, data, sockfd, qs, p, len, len); - if(curlcode != CURLE_OK) { - return curlcode; - } - *psent += sent; - } - - return CURLE_OK; -} - -static CURLcode do_sendmsg(size_t *psent, struct Curl_easy *data, int sockfd, - struct quicsocket *qs, const uint8_t *pkt, - size_t pktlen, size_t gsolen) -{ -#ifdef HAVE_SENDMSG - struct iovec msg_iov; - struct msghdr msg = {0}; - ssize_t sent; -#if defined(__linux__) && defined(UDP_SEGMENT) - uint8_t msg_ctrl[32]; - struct cmsghdr *cm; -#endif - - *psent = 0; - msg_iov.iov_base = (uint8_t *)pkt; - msg_iov.iov_len = pktlen; - msg.msg_iov = &msg_iov; - msg.msg_iovlen = 1; - -#if defined(__linux__) && defined(UDP_SEGMENT) - if(pktlen > gsolen) { - /* Only set this, when we need it. macOS, for example, - * does not seem to like a msg_control of length 0. */ - msg.msg_control = msg_ctrl; - assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(uint16_t))); - msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t)); - cm = CMSG_FIRSTHDR(&msg); - cm->cmsg_level = SOL_UDP; - cm->cmsg_type = UDP_SEGMENT; - cm->cmsg_len = CMSG_LEN(sizeof(uint16_t)); - *(uint16_t *)(void *)CMSG_DATA(cm) = gsolen & 0xffff; - } -#endif - - - while((sent = sendmsg(sockfd, &msg, 0)) == -1 && SOCKERRNO == EINTR) - ; - - if(sent == -1) { - switch(SOCKERRNO) { - case EAGAIN: -#if EAGAIN != EWOULDBLOCK - case EWOULDBLOCK: -#endif - return CURLE_AGAIN; - case EMSGSIZE: - /* UDP datagram is too large; caused by PMTUD. Just let it be lost. */ - break; - case EIO: - if(pktlen > gsolen) { - /* GSO failure */ - failf(data, "sendmsg() returned %zd (errno %d); disable GSO", sent, - SOCKERRNO); - qs->no_gso = TRUE; - return send_packet_no_gso(psent, data, sockfd, qs, pkt, pktlen, - gsolen); - } - /* FALLTHROUGH */ - default: - failf(data, "sendmsg() returned %zd (errno %d)", sent, SOCKERRNO); - return CURLE_SEND_ERROR; - } - } - else { - assert(pktlen == (size_t)sent); - } -#else - ssize_t sent; - (void)qs; - (void)gsolen; - - *psent = 0; - - while((sent = send(sockfd, (const char *)pkt, pktlen, 0)) == -1 && - SOCKERRNO == EINTR) - ; - - if(sent == -1) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { - return CURLE_AGAIN; - } - else { - failf(data, "send() returned %zd (errno %d)", sent, SOCKERRNO); - if(SOCKERRNO != EMSGSIZE) { - return CURLE_SEND_ERROR; - } - /* UDP datagram is too large; caused by PMTUD. Just let it be - lost. */ - } - } -#endif - - *psent = pktlen; - - return CURLE_OK; -} - -static CURLcode send_packet(size_t *psent, struct Curl_easy *data, int sockfd, - struct quicsocket *qs, const uint8_t *pkt, - size_t pktlen, size_t gsolen) -{ - if(qs->no_gso && pktlen > gsolen) { - return send_packet_no_gso(psent, data, sockfd, qs, pkt, pktlen, gsolen); - } - - return do_sendmsg(psent, data, sockfd, qs, pkt, pktlen, gsolen); -} - -static void push_blocked_pkt(struct quicsocket *qs, const uint8_t *pkt, - size_t pktlen, size_t gsolen) -{ - struct blocked_pkt *blkpkt; - - assert(qs->num_blocked_pkt < - sizeof(qs->blocked_pkt) / sizeof(qs->blocked_pkt[0])); - - blkpkt = &qs->blocked_pkt[qs->num_blocked_pkt++]; - - blkpkt->pkt = pkt; - blkpkt->pktlen = pktlen; - blkpkt->gsolen = gsolen; -} - -static CURLcode send_blocked_pkt(struct Curl_easy *data, int sockfd, - struct quicsocket *qs) -{ - size_t sent; - CURLcode curlcode; - struct blocked_pkt *blkpkt; - - for(; qs->num_blocked_pkt_sent < qs->num_blocked_pkt; - ++qs->num_blocked_pkt_sent) { - blkpkt = &qs->blocked_pkt[qs->num_blocked_pkt_sent]; - curlcode = send_packet(&sent, data, sockfd, qs, blkpkt->pkt, - blkpkt->pktlen, blkpkt->gsolen); - - if(curlcode) { - if(curlcode == CURLE_AGAIN) { - blkpkt->pkt += sent; - blkpkt->pktlen -= sent; - } - return curlcode; - } - } - - qs->num_blocked_pkt = 0; - qs->num_blocked_pkt_sent = 0; - - return CURLE_OK; -} - -static CURLcode ng_flush_egress(struct Curl_easy *data, - int sockfd, - struct quicsocket *qs) -{ - int rv; - size_t sent; - ngtcp2_ssize outlen; - uint8_t *outpos = qs->pktbuf; - size_t max_udp_payload_size = - ngtcp2_conn_get_max_tx_udp_payload_size(qs->qconn); - size_t path_max_udp_payload_size = - ngtcp2_conn_get_path_max_tx_udp_payload_size(qs->qconn); - size_t max_pktcnt = - CURLMIN(MAX_PKT_BURST, qs->pktbuflen / max_udp_payload_size); - size_t pktcnt = 0; - size_t gsolen; - ngtcp2_path_storage ps; - ngtcp2_tstamp ts = timestamp(); - ngtcp2_tstamp expiry; - ngtcp2_duration timeout; - int64_t stream_id; - nghttp3_ssize veccnt; - int fin; - nghttp3_vec vec[16]; - ngtcp2_ssize ndatalen; - uint32_t flags; - CURLcode curlcode; - - rv = ngtcp2_conn_handle_expiry(qs->qconn, ts); - if(rv) { - failf(data, "ngtcp2_conn_handle_expiry returned error: %s", - ngtcp2_strerror(rv)); - ngtcp2_connection_close_error_set_transport_error_liberr(&qs->last_error, - rv, NULL, 0); - return CURLE_SEND_ERROR; - } - - if(qs->num_blocked_pkt) { - curlcode = send_blocked_pkt(data, sockfd, qs); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return curlcode; - } - } - - ngtcp2_path_storage_zero(&ps); - - for(;;) { - veccnt = 0; - stream_id = -1; - fin = 0; - - if(qs->h3conn && ngtcp2_conn_get_max_data_left(qs->qconn)) { - veccnt = nghttp3_conn_writev_stream(qs->h3conn, &stream_id, &fin, vec, - sizeof(vec) / sizeof(vec[0])); - if(veccnt < 0) { - failf(data, "nghttp3_conn_writev_stream returned error: %s", - nghttp3_strerror((int)veccnt)); - ngtcp2_connection_close_error_set_application_error( - &qs->last_error, - nghttp3_err_infer_quic_app_error_code((int)veccnt), NULL, 0); - return CURLE_SEND_ERROR; - } - } - - flags = NGTCP2_WRITE_STREAM_FLAG_MORE | - (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0); - outlen = ngtcp2_conn_writev_stream(qs->qconn, &ps.path, NULL, outpos, - max_udp_payload_size, - &ndatalen, flags, stream_id, - (const ngtcp2_vec *)vec, veccnt, ts); - if(outlen == 0) { - if(outpos != qs->pktbuf) { - curlcode = send_packet(&sent, data, sockfd, qs, qs->pktbuf, - outpos - qs->pktbuf, gsolen); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { - push_blocked_pkt(qs, qs->pktbuf + sent, outpos - qs->pktbuf - sent, - gsolen); - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return curlcode; - } - } - - break; - } - if(outlen < 0) { - switch(outlen) { - case NGTCP2_ERR_STREAM_DATA_BLOCKED: - assert(ndatalen == -1); - nghttp3_conn_block_stream(qs->h3conn, stream_id); - continue; - case NGTCP2_ERR_STREAM_SHUT_WR: - assert(ndatalen == -1); - nghttp3_conn_shutdown_stream_write(qs->h3conn, stream_id); - continue; - case NGTCP2_ERR_WRITE_MORE: - assert(ndatalen >= 0); - rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ndatalen); - if(rv) { - failf(data, "nghttp3_conn_add_write_offset returned error: %s\n", - nghttp3_strerror(rv)); - return CURLE_SEND_ERROR; - } - continue; - default: - assert(ndatalen == -1); - failf(data, "ngtcp2_conn_writev_stream returned error: %s", - ngtcp2_strerror((int)outlen)); - ngtcp2_connection_close_error_set_transport_error_liberr( - &qs->last_error, (int)outlen, NULL, 0); - return CURLE_SEND_ERROR; - } - } - else if(ndatalen >= 0) { - rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ndatalen); - if(rv) { - failf(data, "nghttp3_conn_add_write_offset returned error: %s\n", - nghttp3_strerror(rv)); - return CURLE_SEND_ERROR; - } - } - - outpos += outlen; - - if(pktcnt == 0) { - gsolen = outlen; - } - else if((size_t)outlen > gsolen || - (gsolen > path_max_udp_payload_size && - (size_t)outlen != gsolen)) { - /* Packet larger than path_max_udp_payload_size is PMTUD probe - packet and it might not be sent because of EMSGSIZE. Send - them separately to minimize the loss. */ - curlcode = send_packet(&sent, data, sockfd, qs, qs->pktbuf, - outpos - outlen - qs->pktbuf, gsolen); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { - push_blocked_pkt(qs, qs->pktbuf + sent, - outpos - outlen - qs->pktbuf - sent, gsolen); - push_blocked_pkt(qs, outpos - outlen, outlen, outlen); - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return curlcode; - } - curlcode = send_packet(&sent, data, sockfd, qs, outpos - outlen, outlen, - outlen); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { - assert(0 == sent); - push_blocked_pkt(qs, outpos - outlen, outlen, outlen); - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return curlcode; - } - - pktcnt = 0; - outpos = qs->pktbuf; - continue; - } - - if(++pktcnt >= max_pktcnt || (size_t)outlen < gsolen) { - curlcode = send_packet(&sent, data, sockfd, qs, qs->pktbuf, - outpos - qs->pktbuf, gsolen); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { - push_blocked_pkt(qs, qs->pktbuf + sent, outpos - qs->pktbuf - sent, - gsolen); - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return curlcode; - } - - pktcnt = 0; - outpos = qs->pktbuf; - } - } - - expiry = ngtcp2_conn_get_expiry(qs->qconn); - if(expiry != UINT64_MAX) { - if(expiry <= ts) { - timeout = 0; - } - else { - timeout = expiry - ts; - if(timeout % NGTCP2_MILLISECONDS) { - timeout += NGTCP2_MILLISECONDS; - } - } - Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC); - } - - return CURLE_OK; -} - -/* - * Called from transfer.c:done_sending when we stop HTTP/3 uploading. - */ -CURLcode Curl_quic_done_sending(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - DEBUGASSERT(conn); - if(conn->handler == &Curl_handler_http3) { - /* only for HTTP/3 transfers */ - struct HTTP *stream = data->req.p.http; - struct quicsocket *qs = conn->quic; - stream->upload_done = TRUE; - (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id); - } - - return CURLE_OK; -} - -/* - * Called from http.c:Curl_http_done when a request completes. - */ -void Curl_quic_done(struct Curl_easy *data, bool premature) -{ - (void)premature; - if(data->conn->handler == &Curl_handler_http3) { - /* only for HTTP/3 transfers */ - struct HTTP *stream = data->req.p.http; - Curl_dyn_free(&stream->overflow); - free(stream->h3out); - } -} - -/* - * Called from transfer.c:data_pending to know if we should keep looping - * to receive more data from the connection. - */ -bool Curl_quic_data_pending(const struct Curl_easy *data) -{ - /* We may have received more data than we're able to hold in the receive - buffer and allocated an overflow buffer. Since it's possible that - there's no more data coming on the socket, we need to keep reading - until the overflow buffer is empty. */ - const struct HTTP *stream = data->req.p.http; - return Curl_dyn_len(&stream->overflow) > 0; -} - -/* - * Called from transfer.c:Curl_readwrite when neither HTTP level read - * nor write is performed. It is a good place to handle timer expiry - * for QUIC transport. - */ -CURLcode Curl_quic_idle(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; - struct quicsocket *qs = conn->quic; - - if(ngtcp2_conn_get_expiry(qs->qconn) > timestamp()) { - return CURLE_OK; - } - - if(ng_flush_egress(data, sockfd, qs)) { - return CURLE_SEND_ERROR; - } - - return CURLE_OK; -} - -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_NGTCP2
+#include <ngtcp2/ngtcp2.h>
+#include <nghttp3/nghttp3.h>
+
+#ifdef USE_OPENSSL
+#include <openssl/err.h>
+#ifdef OPENSSL_IS_BORINGSSL
+#include <ngtcp2/ngtcp2_crypto_boringssl.h>
+#else
+#include <ngtcp2/ngtcp2_crypto_openssl.h>
+#endif
+#include "vtls/openssl.h"
+#elif defined(USE_GNUTLS)
+#include <ngtcp2/ngtcp2_crypto_gnutls.h>
+#include "vtls/gtls.h"
+#elif defined(USE_WOLFSSL)
+#include <ngtcp2/ngtcp2_crypto_wolfssl.h>
+#include "vtls/wolfssl.h"
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "strdup.h"
+#include "rand.h"
+#include "ngtcp2.h"
+#include "multiif.h"
+#include "strcase.h"
+#include "cfilters.h"
+#include "connect.h"
+#include "strerror.h"
+#include "dynbuf.h"
+#include "vquic.h"
+#include "h2h3.h"
+#include "vtls/keylog.h"
+#include "vtls/vtls.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* #define DEBUG_NGTCP2 */
+#ifdef CURLDEBUG
+#define DEBUG_HTTP3
+#endif
+#ifdef DEBUG_HTTP3
+#define H3BUGF(x) x
+#else
+#define H3BUGF(x) do { } while(0)
+#endif
+
+#define H3_ALPN_H3_29 "\x5h3-29"
+#define H3_ALPN_H3 "\x2h3"
+
+/*
+ * This holds outgoing HTTP/3 stream data that is used by nghttp3 until acked.
+ * It is used as a circular buffer. Add new bytes at the end until it reaches
+ * the far end, then start over at index 0 again.
+ */
+
+#define H3_SEND_SIZE (256*1024)
+struct h3out {
+ uint8_t buf[H3_SEND_SIZE];
+ size_t used; /* number of bytes used in the buffer */
+ size_t windex; /* index in the buffer where to start writing the next
+ data block */
+};
+
+#define QUIC_MAX_STREAMS (256*1024)
+#define QUIC_MAX_DATA (1*1024*1024)
+#define QUIC_IDLE_TIMEOUT (60*NGTCP2_SECONDS)
+
+#ifdef USE_OPENSSL
+#define QUIC_CIPHERS \
+ "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
+ "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
+#define QUIC_GROUPS "P-256:X25519:P-384:P-521"
+#elif defined(USE_GNUTLS)
+#define QUIC_PRIORITY \
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
+ "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
+ "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
+ "%DISABLE_TLS13_COMPAT_MODE"
+#elif defined(USE_WOLFSSL)
+#define QUIC_CIPHERS \
+ "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
+ "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
+#define QUIC_GROUPS "P-256:P-384:P-521"
+#endif
+
+/* ngtcp2 default congestion controller does not perform pacing. Limit
+ the maximum packet burst to MAX_PKT_BURST packets. */
+#define MAX_PKT_BURST 10
+
+static CURLcode ng_process_ingress(struct Curl_easy *data,
+ curl_socket_t sockfd,
+ struct quicsocket *qs);
+static CURLcode ng_flush_egress(struct Curl_easy *data, int sockfd,
+ struct quicsocket *qs);
+static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t datalen, void *user_data,
+ void *stream_user_data);
+
+static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref)
+{
+ struct quicsocket *qs = conn_ref->user_data;
+ return qs->qconn;
+}
+
+static ngtcp2_tstamp timestamp(void)
+{
+ struct curltime ct = Curl_now();
+ return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS;
+}
+
+#ifdef DEBUG_NGTCP2
+static void quic_printf(void *user_data, const char *fmt, ...)
+{
+ va_list ap;
+ (void)user_data; /* TODO, use this to do infof() instead long-term */
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+#endif
+
+static void qlog_callback(void *user_data, uint32_t flags,
+ const void *data, size_t datalen)
+{
+ struct quicsocket *qs = (struct quicsocket *)user_data;
+ (void)flags;
+ if(qs->qlogfd != -1) {
+ ssize_t rc = write(qs->qlogfd, data, datalen);
+ if(rc == -1) {
+ /* on write error, stop further write attempts */
+ close(qs->qlogfd);
+ qs->qlogfd = -1;
+ }
+ }
+
+}
+
+static void quic_settings(struct quicsocket *qs,
+ uint64_t stream_buffer_size)
+{
+ ngtcp2_settings *s = &qs->settings;
+ ngtcp2_transport_params *t = &qs->transport_params;
+ ngtcp2_settings_default(s);
+ ngtcp2_transport_params_default(t);
+#ifdef DEBUG_NGTCP2
+ s->log_printf = quic_printf;
+#else
+ s->log_printf = NULL;
+#endif
+ s->initial_ts = timestamp();
+ t->initial_max_stream_data_bidi_local = stream_buffer_size;
+ t->initial_max_stream_data_bidi_remote = QUIC_MAX_STREAMS;
+ t->initial_max_stream_data_uni = QUIC_MAX_STREAMS;
+ t->initial_max_data = QUIC_MAX_DATA;
+ t->initial_max_streams_bidi = 1;
+ t->initial_max_streams_uni = 3;
+ t->max_idle_timeout = QUIC_IDLE_TIMEOUT;
+ if(qs->qlogfd != -1) {
+ s->qlog.write = qlog_callback;
+ }
+}
+
+#ifdef USE_OPENSSL
+static void keylog_callback(const SSL *ssl, const char *line)
+{
+ (void)ssl;
+ Curl_tls_keylog_write_line(line);
+}
+#elif defined(USE_GNUTLS)
+static int keylog_callback(gnutls_session_t session, const char *label,
+ const gnutls_datum_t *secret)
+{
+ gnutls_datum_t crandom;
+ gnutls_datum_t srandom;
+
+ gnutls_session_get_random(session, &crandom, &srandom);
+ if(crandom.size != 32) {
+ return -1;
+ }
+
+ Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size);
+ return 0;
+}
+#elif defined(USE_WOLFSSL)
+#if defined(HAVE_SECRET_CALLBACK)
+static void keylog_callback(const WOLFSSL *ssl, const char *line)
+{
+ (void)ssl;
+ Curl_tls_keylog_write_line(line);
+}
+#endif
+#endif
+
+static int init_ngh3_conn(struct quicsocket *qs);
+
+#ifdef USE_OPENSSL
+static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
+{
+ struct connectdata *conn = data->conn;
+ SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
+
+#ifdef OPENSSL_IS_BORINGSSL
+ if(ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) {
+ failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed");
+ return NULL;
+ }
+#else
+ if(ngtcp2_crypto_openssl_configure_client_context(ssl_ctx) != 0) {
+ failf(data, "ngtcp2_crypto_openssl_configure_client_context failed");
+ return NULL;
+ }
+#endif
+
+ SSL_CTX_set_default_verify_paths(ssl_ctx);
+
+#ifdef OPENSSL_IS_BORINGSSL
+ if(SSL_CTX_set1_curves_list(ssl_ctx, QUIC_GROUPS) != 1) {
+ failf(data, "SSL_CTX_set1_curves_list failed");
+ return NULL;
+ }
+#else
+ if(SSL_CTX_set_ciphersuites(ssl_ctx, QUIC_CIPHERS) != 1) {
+ char error_buffer[256];
+ ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
+ failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer);
+ return NULL;
+ }
+
+ if(SSL_CTX_set1_groups_list(ssl_ctx, QUIC_GROUPS) != 1) {
+ failf(data, "SSL_CTX_set1_groups_list failed");
+ return NULL;
+ }
+#endif
+
+ /* Open the file if a TLS or QUIC backend has not done this before. */
+ Curl_tls_keylog_open();
+ if(Curl_tls_keylog_enabled()) {
+ SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
+ }
+
+ if(conn->ssl_config.verifypeer) {
+ const char * const ssl_cafile = conn->ssl_config.CAfile;
+ const char * const ssl_capath = conn->ssl_config.CApath;
+
+ if(ssl_cafile || ssl_capath) {
+ SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
+ /* tell OpenSSL where to find CA certificates that are used to verify
+ the server's certificate. */
+ if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate verify locations:"
+ " CAfile: %s CApath: %s",
+ ssl_cafile ? ssl_cafile : "none",
+ ssl_capath ? ssl_capath : "none");
+ return NULL;
+ }
+ infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
+ infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
+ }
+#ifdef CURL_CA_FALLBACK
+ else {
+ /* verifying the peer without any CA certificates won't work so
+ use openssl's built-in default as fallback */
+ SSL_CTX_set_default_verify_paths(ssl_ctx);
+ }
+#endif
+ }
+ return ssl_ctx;
+}
+
+static CURLcode quic_set_client_cert(struct Curl_easy *data,
+ struct quicsocket *qs)
+{
+ SSL_CTX *ssl_ctx = qs->sslctx;
+ const struct ssl_config_data *ssl_config;
+
+ ssl_config = Curl_ssl_get_config(data, FIRSTSOCKET);
+ DEBUGASSERT(ssl_config);
+
+ if(ssl_config->primary.clientcert || ssl_config->primary.cert_blob
+ || ssl_config->cert_type) {
+ return Curl_ossl_set_client_cert(
+ data, ssl_ctx, ssl_config->primary.clientcert,
+ ssl_config->primary.cert_blob, ssl_config->cert_type,
+ ssl_config->key, ssl_config->key_blob,
+ ssl_config->key_type, ssl_config->key_passwd);
+ }
+
+ return CURLE_OK;
+}
+
+/** SSL callbacks ***/
+
+static CURLcode quic_init_ssl(struct quicsocket *qs,
+ struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ const uint8_t *alpn = NULL;
+ size_t alpnlen = 0;
+ /* this will need some attention when HTTPS proxy over QUIC get fixed */
+ const char * const hostname = qs->conn->host.name;
+
+ (void)data;
+ (void)conn;
+ DEBUGASSERT(!qs->ssl);
+ qs->ssl = SSL_new(qs->sslctx);
+
+ SSL_set_app_data(qs->ssl, &qs->conn_ref);
+ SSL_set_connect_state(qs->ssl);
+ SSL_set_quic_use_legacy_codepoint(qs->ssl, 0);
+
+ alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3;
+ alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1;
+ if(alpn)
+ SSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen);
+
+ /* set SNI */
+ SSL_set_tlsext_host_name(qs->ssl, hostname);
+ return CURLE_OK;
+}
+#elif defined(USE_GNUTLS)
+static CURLcode quic_init_ssl(struct quicsocket *qs,
+ struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result;
+ gnutls_datum_t alpn[2];
+ /* this will need some attention when HTTPS proxy over QUIC get fixed */
+ const char * const hostname = qs->conn->host.name;
+ long * const pverifyresult = &data->set.ssl.certverifyresult;
+ int rc;
+
+ DEBUGASSERT(qs->gtls == NULL);
+ qs->gtls = calloc(1, sizeof(*(qs->gtls)));
+ if(!qs->gtls)
+ return CURLE_OUT_OF_MEMORY;
+
+ result = gtls_client_init(data, &conn->ssl_config, &data->set.ssl,
+ hostname, qs->gtls, pverifyresult);
+ if(result)
+ return result;
+
+ gnutls_session_set_ptr(qs->gtls->session, &qs->conn_ref);
+
+ if(ngtcp2_crypto_gnutls_configure_client_session(qs->gtls->session) != 0) {
+ H3BUGF(fprintf(stderr,
+ "ngtcp2_crypto_gnutls_configure_client_session failed\n"));
+ return CURLE_QUIC_CONNECT_ERROR;
+ }
+
+ rc = gnutls_priority_set_direct(qs->gtls->session, QUIC_PRIORITY, NULL);
+ if(rc < 0) {
+ H3BUGF(fprintf(stderr, "gnutls_priority_set_direct failed: %s\n",
+ gnutls_strerror(rc)));
+ return CURLE_QUIC_CONNECT_ERROR;
+ }
+
+ /* Open the file if a TLS or QUIC backend has not done this before. */
+ Curl_tls_keylog_open();
+ if(Curl_tls_keylog_enabled()) {
+ gnutls_session_set_keylog_function(qs->gtls->session, keylog_callback);
+ }
+
+ /* strip the first byte (the length) from NGHTTP3_ALPN_H3 */
+ alpn[0].data = (unsigned char *)H3_ALPN_H3_29 + 1;
+ alpn[0].size = sizeof(H3_ALPN_H3_29) - 2;
+ alpn[1].data = (unsigned char *)H3_ALPN_H3 + 1;
+ alpn[1].size = sizeof(H3_ALPN_H3) - 2;
+
+ gnutls_alpn_set_protocols(qs->gtls->session, alpn, 2, GNUTLS_ALPN_MANDATORY);
+
+ return CURLE_OK;
+}
+#elif defined(USE_WOLFSSL)
+
+static WOLFSSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
+{
+ struct connectdata *conn = data->conn;
+ WOLFSSL_CTX *ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
+
+ if(ngtcp2_crypto_wolfssl_configure_client_context(ssl_ctx) != 0) {
+ failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed");
+ return NULL;
+ }
+
+ wolfSSL_CTX_set_default_verify_paths(ssl_ctx);
+
+ if(wolfSSL_CTX_set_cipher_list(ssl_ctx, QUIC_CIPHERS) != 1) {
+ char error_buffer[256];
+ ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
+ failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer);
+ return NULL;
+ }
+
+ if(wolfSSL_CTX_set1_groups_list(ssl_ctx, (char *)QUIC_GROUPS) != 1) {
+ failf(data, "SSL_CTX_set1_groups_list failed");
+ return NULL;
+ }
+
+ /* Open the file if a TLS or QUIC backend has not done this before. */
+ Curl_tls_keylog_open();
+ if(Curl_tls_keylog_enabled()) {
+#if defined(HAVE_SECRET_CALLBACK)
+ wolfSSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
+#else
+ failf(data, "wolfSSL was built without keylog callback");
+ return NULL;
+#endif
+ }
+
+ if(conn->ssl_config.verifypeer) {
+ const char * const ssl_cafile = conn->ssl_config.CAfile;
+ const char * const ssl_capath = conn->ssl_config.CApath;
+
+ if(ssl_cafile || ssl_capath) {
+ wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
+ /* tell wolfSSL where to find CA certificates that are used to verify
+ the server's certificate. */
+ if(!wolfSSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate verify locations:"
+ " CAfile: %s CApath: %s",
+ ssl_cafile ? ssl_cafile : "none",
+ ssl_capath ? ssl_capath : "none");
+ return NULL;
+ }
+ infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
+ infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
+ }
+#ifdef CURL_CA_FALLBACK
+ else {
+ /* verifying the peer without any CA certificates won't work so
+ use wolfssl's built-in default as fallback */
+ wolfSSL_CTX_set_default_verify_paths(ssl_ctx);
+ }
+#endif
+ }
+ else {
+ wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
+ }
+
+ return ssl_ctx;
+}
+
+/** SSL callbacks ***/
+
+static CURLcode quic_init_ssl(struct quicsocket *qs,
+ struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ const uint8_t *alpn = NULL;
+ size_t alpnlen = 0;
+ /* this will need some attention when HTTPS proxy over QUIC get fixed */
+ const char * const hostname = qs->conn->host.name;
+
+ (void)data;
+ (void)conn;
+ DEBUGASSERT(!qs->ssl);
+ qs->ssl = SSL_new(qs->sslctx);
+
+ wolfSSL_set_app_data(qs->ssl, &qs->conn_ref);
+ wolfSSL_set_connect_state(qs->ssl);
+ wolfSSL_set_quic_use_legacy_codepoint(qs->ssl, 0);
+
+ alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3;
+ alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1;
+ if(alpn)
+ wolfSSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen);
+
+ /* set SNI */
+ wolfSSL_UseSNI(qs->ssl, WOLFSSL_SNI_HOST_NAME,
+ hostname, (unsigned short)strlen(hostname));
+
+ return CURLE_OK;
+}
+#endif /* defined(USE_WOLFSSL) */
+
+static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data)
+{
+ (void)user_data;
+ (void)tconn;
+ return 0;
+}
+
+static void extend_stream_window(ngtcp2_conn *tconn,
+ struct HTTP *stream)
+{
+ size_t thismuch = stream->unacked_window;
+ ngtcp2_conn_extend_max_stream_offset(tconn, stream->stream3_id, thismuch);
+ ngtcp2_conn_extend_max_offset(tconn, thismuch);
+ stream->unacked_window = 0;
+}
+
+
+static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags,
+ int64_t stream_id, uint64_t offset,
+ const uint8_t *buf, size_t buflen,
+ void *user_data, void *stream_user_data)
+{
+ struct quicsocket *qs = (struct quicsocket *)user_data;
+ nghttp3_ssize nconsumed;
+ int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0;
+ (void)offset;
+ (void)stream_user_data;
+
+ nconsumed =
+ nghttp3_conn_read_stream(qs->h3conn, stream_id, buf, buflen, fin);
+ if(nconsumed < 0) {
+ ngtcp2_connection_close_error_set_application_error(
+ &qs->last_error, nghttp3_err_infer_quic_app_error_code((int)nconsumed),
+ NULL, 0);
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ /* number of bytes inside buflen which consists of framing overhead
+ * including QPACK HEADERS. In other words, it does not consume payload of
+ * DATA frame. */
+ ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed);
+ ngtcp2_conn_extend_max_offset(tconn, nconsumed);
+
+ return 0;
+}
+
+static int
+cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id,
+ uint64_t offset, uint64_t datalen, void *user_data,
+ void *stream_user_data)
+{
+ struct quicsocket *qs = (struct quicsocket *)user_data;
+ int rv;
+ (void)stream_id;
+ (void)tconn;
+ (void)offset;
+ (void)datalen;
+ (void)stream_user_data;
+
+ rv = nghttp3_conn_add_ack_offset(qs->h3conn, stream_id, datalen);
+ if(rv) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags,
+ int64_t stream_id, uint64_t app_error_code,
+ void *user_data, void *stream_user_data)
+{
+ struct quicsocket *qs = (struct quicsocket *)user_data;
+ int rv;
+ (void)tconn;
+ (void)stream_user_data;
+ /* stream is closed... */
+
+ if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) {
+ app_error_code = NGHTTP3_H3_NO_ERROR;
+ }
+
+ rv = nghttp3_conn_close_stream(qs->h3conn, stream_id,
+ app_error_code);
+ if(rv) {
+ ngtcp2_connection_close_error_set_application_error(
+ &qs->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0);
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id,
+ uint64_t final_size, uint64_t app_error_code,
+ void *user_data, void *stream_user_data)
+{
+ struct quicsocket *qs = (struct quicsocket *)user_data;
+ int rv;
+ (void)tconn;
+ (void)final_size;
+ (void)app_error_code;
+ (void)stream_user_data;
+
+ rv = nghttp3_conn_shutdown_stream_read(qs->h3conn, stream_id);
+ if(rv) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id,
+ uint64_t app_error_code, void *user_data,
+ void *stream_user_data)
+{
+ struct quicsocket *qs = (struct quicsocket *)user_data;
+ int rv;
+ (void)tconn;
+ (void)app_error_code;
+ (void)stream_user_data;
+
+ rv = nghttp3_conn_shutdown_stream_read(qs->h3conn, stream_id);
+ if(rv) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn,
+ uint64_t max_streams,
+ void *user_data)
+{
+ (void)tconn;
+ (void)max_streams;
+ (void)user_data;
+
+ return 0;
+}
+
+static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id,
+ uint64_t max_data, void *user_data,
+ void *stream_user_data)
+{
+ struct quicsocket *qs = (struct quicsocket *)user_data;
+ int rv;
+ (void)tconn;
+ (void)max_data;
+ (void)stream_user_data;
+
+ rv = nghttp3_conn_unblock_stream(qs->h3conn, stream_id);
+ if(rv) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static void cb_rand(uint8_t *dest, size_t destlen,
+ const ngtcp2_rand_ctx *rand_ctx)
+{
+ CURLcode result;
+ (void)rand_ctx;
+
+ result = Curl_rand(NULL, dest, destlen);
+ if(result) {
+ /* cb_rand is only used for non-cryptographic context. If Curl_rand
+ failed, just fill 0 and call it *random*. */
+ memset(dest, 0, destlen);
+ }
+}
+
+static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid,
+ uint8_t *token, size_t cidlen,
+ void *user_data)
+{
+ CURLcode result;
+ (void)tconn;
+ (void)user_data;
+
+ result = Curl_rand(NULL, cid->data, cidlen);
+ if(result)
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ cid->datalen = cidlen;
+
+ result = Curl_rand(NULL, token, NGTCP2_STATELESS_RESET_TOKENLEN);
+ if(result)
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+
+ return 0;
+}
+
+static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_crypto_level level,
+ void *user_data)
+{
+ struct quicsocket *qs = (struct quicsocket *)user_data;
+ (void)tconn;
+
+ if(level != NGTCP2_CRYPTO_LEVEL_APPLICATION) {
+ return 0;
+ }
+
+ if(init_ngh3_conn(qs) != CURLE_OK) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static ngtcp2_callbacks ng_callbacks = {
+ ngtcp2_crypto_client_initial_cb,
+ NULL, /* recv_client_initial */
+ ngtcp2_crypto_recv_crypto_data_cb,
+ cb_handshake_completed,
+ NULL, /* recv_version_negotiation */
+ ngtcp2_crypto_encrypt_cb,
+ ngtcp2_crypto_decrypt_cb,
+ ngtcp2_crypto_hp_mask_cb,
+ cb_recv_stream_data,
+ cb_acked_stream_data_offset,
+ NULL, /* stream_open */
+ cb_stream_close,
+ NULL, /* recv_stateless_reset */
+ ngtcp2_crypto_recv_retry_cb,
+ cb_extend_max_local_streams_bidi,
+ NULL, /* extend_max_local_streams_uni */
+ cb_rand,
+ cb_get_new_connection_id,
+ NULL, /* remove_connection_id */
+ ngtcp2_crypto_update_key_cb, /* update_key */
+ NULL, /* path_validation */
+ NULL, /* select_preferred_addr */
+ cb_stream_reset,
+ NULL, /* extend_max_remote_streams_bidi */
+ NULL, /* extend_max_remote_streams_uni */
+ cb_extend_max_stream_data,
+ NULL, /* dcid_status */
+ NULL, /* handshake_confirmed */
+ NULL, /* recv_new_token */
+ ngtcp2_crypto_delete_crypto_aead_ctx_cb,
+ ngtcp2_crypto_delete_crypto_cipher_ctx_cb,
+ NULL, /* recv_datagram */
+ NULL, /* ack_datagram */
+ NULL, /* lost_datagram */
+ ngtcp2_crypto_get_path_challenge_data_cb,
+ cb_stream_stop_sending,
+ NULL, /* version_negotiation */
+ cb_recv_rx_key,
+ NULL, /* recv_tx_key */
+ NULL, /* early_data_rejected */
+};
+
+/*
+ * Might be called twice for happy eyeballs.
+ */
+CURLcode Curl_quic_connect(struct Curl_easy *data,
+ struct connectdata *conn,
+ curl_socket_t sockfd,
+ int sockindex,
+ const struct sockaddr *addr,
+ socklen_t addrlen)
+{
+ int rc;
+ int rv;
+ CURLcode result;
+ ngtcp2_path path; /* TODO: this must be initialized properly */
+ struct quicsocket *qs = &conn->hequic[sockindex];
+ char ipbuf[40];
+ int port;
+ int qfd;
+
+ if(qs->conn)
+ Curl_quic_disconnect(data, conn, sockindex);
+ qs->conn = conn;
+
+ /* extract the used address as a string */
+ if(!Curl_addr2string((struct sockaddr*)addr, addrlen, ipbuf, &port)) {
+ char buffer[STRERROR_LEN];
+ failf(data, "ssrem inet_ntop() failed with errno %d: %s",
+ SOCKERRNO, Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+
+ infof(data, "Connect socket %d over QUIC to %s:%d",
+ sockfd, ipbuf, port);
+
+ qs->version = NGTCP2_PROTO_VER_MAX;
+#ifdef USE_OPENSSL
+ qs->sslctx = quic_ssl_ctx(data);
+ if(!qs->sslctx)
+ return CURLE_QUIC_CONNECT_ERROR;
+
+ result = quic_set_client_cert(data, qs);
+ if(result)
+ return result;
+#elif defined(USE_WOLFSSL)
+ qs->sslctx = quic_ssl_ctx(data);
+ if(!qs->sslctx)
+ return CURLE_QUIC_CONNECT_ERROR;
+#endif
+
+ result = quic_init_ssl(qs, data, conn);
+ if(result)
+ return result;
+
+ qs->dcid.datalen = NGTCP2_MAX_CIDLEN;
+ result = Curl_rand(data, qs->dcid.data, NGTCP2_MAX_CIDLEN);
+ if(result)
+ return result;
+
+ qs->scid.datalen = NGTCP2_MAX_CIDLEN;
+ result = Curl_rand(data, qs->scid.data, NGTCP2_MAX_CIDLEN);
+ if(result)
+ return result;
+
+ (void)Curl_qlogdir(data, qs->scid.data, NGTCP2_MAX_CIDLEN, &qfd);
+ qs->qlogfd = qfd; /* -1 if failure above */
+ quic_settings(qs, data->set.buffer_size);
+
+ qs->local_addrlen = sizeof(qs->local_addr);
+ rv = getsockname(sockfd, (struct sockaddr *)&qs->local_addr,
+ &qs->local_addrlen);
+ if(rv == -1)
+ return CURLE_QUIC_CONNECT_ERROR;
+
+ ngtcp2_addr_init(&path.local, (struct sockaddr *)&qs->local_addr,
+ qs->local_addrlen);
+ ngtcp2_addr_init(&path.remote, addr, addrlen);
+
+ rc = ngtcp2_conn_client_new(&qs->qconn, &qs->dcid, &qs->scid, &path,
+ NGTCP2_PROTO_VER_V1, &ng_callbacks,
+ &qs->settings, &qs->transport_params, NULL, qs);
+ if(rc)
+ return CURLE_QUIC_CONNECT_ERROR;
+
+#ifdef USE_GNUTLS
+ ngtcp2_conn_set_tls_native_handle(qs->qconn, qs->gtls->session);
+#else
+ ngtcp2_conn_set_tls_native_handle(qs->qconn, qs->ssl);
+#endif
+
+ ngtcp2_connection_close_error_default(&qs->last_error);
+
+#if defined(__linux__) && defined(UDP_SEGMENT) && defined(HAVE_SENDMSG)
+ qs->no_gso = FALSE;
+#else
+ qs->no_gso = TRUE;
+#endif
+
+ qs->num_blocked_pkt = 0;
+ qs->num_blocked_pkt_sent = 0;
+ memset(&qs->blocked_pkt, 0, sizeof(qs->blocked_pkt));
+
+ qs->pktbuflen = NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE * MAX_PKT_BURST;
+ qs->pktbuf = malloc(qs->pktbuflen);
+ if(!qs->pktbuf) {
+ ngtcp2_conn_del(qs->qconn);
+ qs->qconn = NULL;
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ qs->conn_ref.get_conn = get_conn;
+ qs->conn_ref.user_data = qs;
+
+ return CURLE_OK;
+}
+
+/*
+ * Store ngtcp2 version info in this buffer.
+ */
+void Curl_quic_ver(char *p, size_t len)
+{
+ const ngtcp2_info *ng2 = ngtcp2_version(0);
+ const nghttp3_info *ht3 = nghttp3_version(0);
+ (void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s",
+ ng2->version_str, ht3->version_str);
+}
+
+static int ng_getsock(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t *socks)
+{
+ struct SingleRequest *k = &data->req;
+ int bitmap = GETSOCK_BLANK;
+ struct HTTP *stream = data->req.p.http;
+ struct quicsocket *qs = conn->quic;
+
+ socks[0] = conn->sock[FIRSTSOCKET];
+
+ /* in an HTTP/2 connection we can basically always get a frame so we should
+ always be ready for one */
+ bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
+
+ /* we're still uploading or the HTTP/2 layer wants to send data */
+ if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND &&
+ (!stream->h3out || stream->h3out->used < H3_SEND_SIZE) &&
+ ngtcp2_conn_get_cwnd_left(qs->qconn) &&
+ ngtcp2_conn_get_max_data_left(qs->qconn) &&
+ nghttp3_conn_is_stream_writable(qs->h3conn, stream->stream3_id))
+ bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
+
+ return bitmap;
+}
+
+static void qs_disconnect(struct quicsocket *qs)
+{
+ char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE];
+ ngtcp2_tstamp ts;
+ ngtcp2_ssize rc;
+
+ if(!qs->conn) /* already closed */
+ return;
+ ts = timestamp();
+ rc = ngtcp2_conn_write_connection_close(qs->qconn, NULL, /* path */
+ NULL, /* pkt_info */
+ (uint8_t *)buffer, sizeof(buffer),
+ &qs->last_error, ts);
+ if(rc > 0) {
+ while((send(qs->conn->sock[FIRSTSOCKET], buffer, rc, 0) == -1) &&
+ SOCKERRNO == EINTR);
+ }
+
+ qs->conn = NULL;
+ if(qs->qlogfd != -1) {
+ close(qs->qlogfd);
+ qs->qlogfd = -1;
+ }
+#ifdef USE_OPENSSL
+ if(qs->ssl)
+ SSL_free(qs->ssl);
+ qs->ssl = NULL;
+ SSL_CTX_free(qs->sslctx);
+#elif defined(USE_GNUTLS)
+ if(qs->gtls) {
+ if(qs->gtls->cred)
+ gnutls_certificate_free_credentials(qs->gtls->cred);
+ if(qs->gtls->session)
+ gnutls_deinit(qs->gtls->session);
+ free(qs->gtls);
+ qs->gtls = NULL;
+ }
+#elif defined(USE_WOLFSSL)
+ if(qs->ssl)
+ wolfSSL_free(qs->ssl);
+ qs->ssl = NULL;
+ wolfSSL_CTX_free(qs->sslctx);
+#endif
+ free(qs->pktbuf);
+ nghttp3_conn_del(qs->h3conn);
+ ngtcp2_conn_del(qs->qconn);
+}
+
+void Curl_quic_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ int tempindex)
+{
+ (void)data;
+ if(conn->transport == TRNSPRT_QUIC)
+ qs_disconnect(&conn->hequic[tempindex]);
+}
+
+static CURLcode ng_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection)
+{
+ (void)dead_connection;
+ Curl_quic_disconnect(data, conn, 0);
+ Curl_quic_disconnect(data, conn, 1);
+ return CURLE_OK;
+}
+
+static unsigned int ng_conncheck(struct Curl_easy *data,
+ struct connectdata *conn,
+ unsigned int checks_to_perform)
+{
+ (void)data;
+ (void)conn;
+ (void)checks_to_perform;
+ return CONNRESULT_NONE;
+}
+
+static const struct Curl_handler Curl_handler_http3 = {
+ "HTTPS", /* scheme */
+ ZERO_NULL, /* setup_connection */
+ Curl_http, /* do_it */
+ Curl_http_done, /* done */
+ ZERO_NULL, /* do_more */
+ ZERO_NULL, /* connect_it */
+ ZERO_NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ ng_getsock, /* proto_getsock */
+ ng_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ng_getsock, /* perform_getsock */
+ ng_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ng_conncheck, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_HTTP, /* defport */
+ CURLPROTO_HTTPS, /* protocol */
+ CURLPROTO_HTTP, /* family */
+ PROTOPT_SSL | PROTOPT_STREAM /* flags */
+};
+
+static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_easy *data = stream_user_data;
+ struct HTTP *stream = data->req.p.http;
+ (void)conn;
+ (void)stream_id;
+ (void)app_error_code;
+ (void)user_data;
+ H3BUGF(infof(data, "cb_h3_stream_close CALLED"));
+
+ stream->closed = TRUE;
+ stream->error3 = app_error_code;
+ Curl_expire(data, 0, EXPIRE_QUIC);
+ /* make sure that ngh3_stream_recv is called again to complete the transfer
+ even if there are no more packets to be received from the server. */
+ data->state.drain = 1;
+ return 0;
+}
+
+/*
+ * write_data() copies data to the stream's receive buffer. If not enough
+ * space is available in the receive buffer, it copies the rest to the
+ * stream's overflow buffer.
+ */
+static CURLcode write_data(struct HTTP *stream, const void *mem, size_t memlen)
+{
+ CURLcode result = CURLE_OK;
+ const char *buf = mem;
+ size_t ncopy = memlen;
+ /* copy as much as possible to the receive buffer */
+ if(stream->len) {
+ size_t len = CURLMIN(ncopy, stream->len);
+ memcpy(stream->mem, buf, len);
+ stream->len -= len;
+ stream->memlen += len;
+ stream->mem += len;
+ buf += len;
+ ncopy -= len;
+ }
+ /* copy the rest to the overflow buffer */
+ if(ncopy)
+ result = Curl_dyn_addn(&stream->overflow, buf, ncopy);
+ return result;
+}
+
+static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream_id,
+ const uint8_t *buf, size_t buflen,
+ void *user_data, void *stream_user_data)
+{
+ struct Curl_easy *data = stream_user_data;
+ struct HTTP *stream = data->req.p.http;
+ CURLcode result = CURLE_OK;
+ (void)conn;
+
+ result = write_data(stream, buf, buflen);
+ if(result) {
+ return -1;
+ }
+ stream->unacked_window += buflen;
+ (void)stream_id;
+ (void)user_data;
+ return 0;
+}
+
+static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id,
+ size_t consumed, void *user_data,
+ void *stream_user_data)
+{
+ struct quicsocket *qs = user_data;
+ (void)conn;
+ (void)stream_user_data;
+ (void)stream_id;
+
+ ngtcp2_conn_extend_max_stream_offset(qs->qconn, stream_id, consumed);
+ ngtcp2_conn_extend_max_offset(qs->qconn, consumed);
+ return 0;
+}
+
+/* Decode HTTP status code. Returns -1 if no valid status code was
+ decoded. (duplicate from http2.c) */
+static int decode_status_code(const uint8_t *value, size_t len)
+{
+ int i;
+ int res;
+
+ if(len != 3) {
+ return -1;
+ }
+
+ res = 0;
+
+ for(i = 0; i < 3; ++i) {
+ char c = value[i];
+
+ if(c < '0' || c > '9') {
+ return -1;
+ }
+
+ res *= 10;
+ res += c - '0';
+ }
+
+ return res;
+}
+
+static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
+ int fin, void *user_data, void *stream_user_data)
+{
+ struct Curl_easy *data = stream_user_data;
+ struct HTTP *stream = data->req.p.http;
+ CURLcode result = CURLE_OK;
+ (void)conn;
+ (void)stream_id;
+ (void)user_data;
+ (void)fin;
+
+ /* add a CRLF only if we've received some headers */
+ if(stream->firstheader) {
+ result = write_data(stream, "\r\n", 2);
+ if(result) {
+ return -1;
+ }
+ }
+
+ if(stream->status_code / 100 != 1) {
+ stream->bodystarted = TRUE;
+ }
+ return 0;
+}
+
+static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
+ int32_t token, nghttp3_rcbuf *name,
+ nghttp3_rcbuf *value, uint8_t flags,
+ void *user_data, void *stream_user_data)
+{
+ nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name);
+ nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value);
+ struct Curl_easy *data = stream_user_data;
+ struct HTTP *stream = data->req.p.http;
+ CURLcode result = CURLE_OK;
+ (void)conn;
+ (void)stream_id;
+ (void)token;
+ (void)flags;
+ (void)user_data;
+
+ if(token == NGHTTP3_QPACK_TOKEN__STATUS) {
+ char line[14]; /* status line is always 13 characters long */
+ size_t ncopy;
+ stream->status_code = decode_status_code(h3val.base, h3val.len);
+ DEBUGASSERT(stream->status_code != -1);
+ ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
+ stream->status_code);
+ result = write_data(stream, line, ncopy);
+ if(result) {
+ return -1;
+ }
+ }
+ else {
+ /* store as an HTTP1-style header */
+ result = write_data(stream, h3name.base, h3name.len);
+ if(result) {
+ return -1;
+ }
+ result = write_data(stream, ": ", 2);
+ if(result) {
+ return -1;
+ }
+ result = write_data(stream, h3val.base, h3val.len);
+ if(result) {
+ return -1;
+ }
+ result = write_data(stream, "\r\n", 2);
+ if(result) {
+ return -1;
+ }
+ }
+
+ stream->firstheader = TRUE;
+ return 0;
+}
+
+static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code, void *user_data,
+ void *stream_user_data)
+{
+ struct quicsocket *qs = user_data;
+ int rv;
+ (void)conn;
+ (void)stream_user_data;
+
+ rv = ngtcp2_conn_shutdown_stream_read(qs->qconn, stream_id, app_error_code);
+ if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code, void *user_data,
+ void *stream_user_data) {
+ struct quicsocket *qs = user_data;
+ int rv;
+ (void)conn;
+ (void)stream_user_data;
+
+ rv = ngtcp2_conn_shutdown_stream_write(qs->qconn, stream_id, app_error_code);
+ if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static nghttp3_callbacks ngh3_callbacks = {
+ cb_h3_acked_stream_data, /* acked_stream_data */
+ cb_h3_stream_close,
+ cb_h3_recv_data,
+ cb_h3_deferred_consume,
+ NULL, /* begin_headers */
+ cb_h3_recv_header,
+ cb_h3_end_headers,
+ NULL, /* begin_trailers */
+ cb_h3_recv_header,
+ NULL, /* end_trailers */
+ cb_h3_stop_sending,
+ NULL, /* end_stream */
+ cb_h3_reset_stream,
+ NULL /* shutdown */
+};
+
+static int init_ngh3_conn(struct quicsocket *qs)
+{
+ CURLcode result;
+ int rc;
+ int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id;
+
+ if(ngtcp2_conn_get_max_local_streams_uni(qs->qconn) < 3) {
+ return CURLE_QUIC_CONNECT_ERROR;
+ }
+
+ nghttp3_settings_default(&qs->h3settings);
+
+ rc = nghttp3_conn_client_new(&qs->h3conn,
+ &ngh3_callbacks,
+ &qs->h3settings,
+ nghttp3_mem_default(),
+ qs);
+ if(rc) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+
+ rc = ngtcp2_conn_open_uni_stream(qs->qconn, &ctrl_stream_id, NULL);
+ if(rc) {
+ result = CURLE_QUIC_CONNECT_ERROR;
+ goto fail;
+ }
+
+ rc = nghttp3_conn_bind_control_stream(qs->h3conn, ctrl_stream_id);
+ if(rc) {
+ result = CURLE_QUIC_CONNECT_ERROR;
+ goto fail;
+ }
+
+ rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_enc_stream_id, NULL);
+ if(rc) {
+ result = CURLE_QUIC_CONNECT_ERROR;
+ goto fail;
+ }
+
+ rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_dec_stream_id, NULL);
+ if(rc) {
+ result = CURLE_QUIC_CONNECT_ERROR;
+ goto fail;
+ }
+
+ rc = nghttp3_conn_bind_qpack_streams(qs->h3conn, qpack_enc_stream_id,
+ qpack_dec_stream_id);
+ if(rc) {
+ result = CURLE_QUIC_CONNECT_ERROR;
+ goto fail;
+ }
+
+ return CURLE_OK;
+ fail:
+
+ return result;
+}
+
+static Curl_recv ngh3_stream_recv;
+static Curl_send ngh3_stream_send;
+
+static size_t drain_overflow_buffer(struct HTTP *stream)
+{
+ size_t overlen = Curl_dyn_len(&stream->overflow);
+ size_t ncopy = CURLMIN(overlen, stream->len);
+ if(ncopy > 0) {
+ memcpy(stream->mem, Curl_dyn_ptr(&stream->overflow), ncopy);
+ stream->len -= ncopy;
+ stream->mem += ncopy;
+ stream->memlen += ncopy;
+ if(ncopy != overlen)
+ /* make the buffer only keep the tail */
+ (void)Curl_dyn_tail(&stream->overflow, overlen - ncopy);
+ else
+ Curl_dyn_reset(&stream->overflow);
+ }
+ return ncopy;
+}
+
+/* incoming data frames on the h3 stream */
+static ssize_t ngh3_stream_recv(struct Curl_easy *data,
+ int sockindex,
+ char *buf,
+ size_t buffersize,
+ CURLcode *curlcode)
+{
+ struct connectdata *conn = data->conn;
+ curl_socket_t sockfd = conn->sock[sockindex];
+ struct HTTP *stream = data->req.p.http;
+ struct quicsocket *qs = conn->quic;
+
+ if(!stream->memlen) {
+ /* remember where to store incoming data for this stream and how big the
+ buffer is */
+ stream->mem = buf;
+ stream->len = buffersize;
+ }
+ /* else, there's data in the buffer already */
+
+ /* if there's data in the overflow buffer from a previous call, copy as much
+ as possible to the receive buffer before receiving more */
+ drain_overflow_buffer(stream);
+
+ if(ng_process_ingress(data, sockfd, qs)) {
+ *curlcode = CURLE_RECV_ERROR;
+ return -1;
+ }
+ if(ng_flush_egress(data, sockfd, qs)) {
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+
+ if(stream->memlen) {
+ ssize_t memlen = stream->memlen;
+ /* data arrived */
+ *curlcode = CURLE_OK;
+ /* reset to allow more data to come */
+ stream->memlen = 0;
+ stream->mem = buf;
+ stream->len = buffersize;
+ /* extend the stream window with the data we're consuming and send out
+ any additional packets to tell the server that we can receive more */
+ extend_stream_window(qs->qconn, stream);
+ if(ng_flush_egress(data, sockfd, qs)) {
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+ return memlen;
+ }
+
+ if(stream->closed) {
+ if(stream->error3 != NGHTTP3_H3_NO_ERROR) {
+ failf(data,
+ "HTTP/3 stream %" PRId64 " was not closed cleanly: (err %" PRIu64
+ ")",
+ stream->stream3_id, stream->error3);
+ *curlcode = CURLE_HTTP3;
+ return -1;
+ }
+
+ if(!stream->bodystarted) {
+ failf(data,
+ "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting"
+ " all response header fields, treated as error",
+ stream->stream3_id);
+ *curlcode = CURLE_HTTP3;
+ return -1;
+ }
+
+ *curlcode = CURLE_OK;
+ return 0;
+ }
+
+ infof(data, "ngh3_stream_recv returns 0 bytes and EAGAIN");
+ *curlcode = CURLE_AGAIN;
+ return -1;
+}
+
+/* this amount of data has now been acked on this stream */
+static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t datalen, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_easy *data = stream_user_data;
+ struct HTTP *stream = data->req.p.http;
+ (void)user_data;
+
+ if(!data->set.postfields) {
+ stream->h3out->used -= datalen;
+ H3BUGF(infof(data,
+ "cb_h3_acked_stream_data, %zd bytes, %zd left unacked",
+ datalen, stream->h3out->used));
+ DEBUGASSERT(stream->h3out->used < H3_SEND_SIZE);
+
+ if(stream->h3out->used == 0) {
+ int rv = nghttp3_conn_resume_stream(conn, stream_id);
+ if(rv) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ }
+ }
+ return 0;
+}
+
+static nghttp3_ssize cb_h3_readfunction(nghttp3_conn *conn, int64_t stream_id,
+ nghttp3_vec *vec, size_t veccnt,
+ uint32_t *pflags, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_easy *data = stream_user_data;
+ size_t nread;
+ struct HTTP *stream = data->req.p.http;
+ (void)conn;
+ (void)stream_id;
+ (void)user_data;
+ (void)veccnt;
+
+ if(data->set.postfields) {
+ vec[0].base = data->set.postfields;
+ vec[0].len = data->state.infilesize;
+ *pflags = NGHTTP3_DATA_FLAG_EOF;
+ return 1;
+ }
+
+ if(stream->upload_len && H3_SEND_SIZE <= stream->h3out->used) {
+ return NGHTTP3_ERR_WOULDBLOCK;
+ }
+
+ nread = CURLMIN(stream->upload_len, H3_SEND_SIZE - stream->h3out->used);
+ if(nread > 0) {
+ /* nghttp3 wants us to hold on to the data until it tells us it is okay to
+ delete it. Append the data at the end of the h3out buffer. Since we can
+ only return consecutive data, copy the amount that fits and the next
+ part comes in next invoke. */
+ struct h3out *out = stream->h3out;
+ if(nread + out->windex > H3_SEND_SIZE)
+ nread = H3_SEND_SIZE - out->windex;
+
+ memcpy(&out->buf[out->windex], stream->upload_mem, nread);
+
+ /* that's the chunk we return to nghttp3 */
+ vec[0].base = &out->buf[out->windex];
+ vec[0].len = nread;
+
+ out->windex += nread;
+ out->used += nread;
+
+ if(out->windex == H3_SEND_SIZE)
+ out->windex = 0; /* wrap */
+ stream->upload_mem += nread;
+ stream->upload_len -= nread;
+ if(data->state.infilesize != -1) {
+ stream->upload_left -= nread;
+ if(!stream->upload_left)
+ *pflags = NGHTTP3_DATA_FLAG_EOF;
+ }
+ H3BUGF(infof(data, "cb_h3_readfunction %zd bytes%s (at %zd unacked)",
+ nread, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
+ out->used));
+ }
+ if(stream->upload_done && !stream->upload_len &&
+ (stream->upload_left <= 0)) {
+ H3BUGF(infof(data, "cb_h3_readfunction sets EOF"));
+ *pflags = NGHTTP3_DATA_FLAG_EOF;
+ return nread ? 1 : 0;
+ }
+ else if(!nread) {
+ return NGHTTP3_ERR_WOULDBLOCK;
+ }
+ return 1;
+}
+
+/* Index where :authority header field will appear in request header
+ field list. */
+#define AUTHORITY_DST_IDX 3
+
+static CURLcode http_request(struct Curl_easy *data, const void *mem,
+ size_t len)
+{
+ struct connectdata *conn = data->conn;
+ struct HTTP *stream = data->req.p.http;
+ size_t nheader;
+ struct quicsocket *qs = conn->quic;
+ CURLcode result = CURLE_OK;
+ nghttp3_nv *nva = NULL;
+ int64_t stream3_id;
+ int rc;
+ struct h3out *h3out = NULL;
+ struct h2h3req *hreq = NULL;
+
+ rc = ngtcp2_conn_open_bidi_stream(qs->qconn, &stream3_id, NULL);
+ if(rc) {
+ failf(data, "can get bidi streams");
+ result = CURLE_SEND_ERROR;
+ goto fail;
+ }
+
+ stream->stream3_id = stream3_id;
+ stream->h3req = TRUE; /* senf off! */
+ Curl_dyn_init(&stream->overflow, CURL_MAX_READ_SIZE);
+
+ result = Curl_pseudo_headers(data, mem, len, &hreq);
+ if(result)
+ goto fail;
+ nheader = hreq->entries;
+
+ nva = malloc(sizeof(nghttp3_nv) * nheader);
+ if(!nva) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ else {
+ unsigned int i;
+ for(i = 0; i < nheader; i++) {
+ nva[i].name = (unsigned char *)hreq->header[i].name;
+ nva[i].namelen = hreq->header[i].namelen;
+ nva[i].value = (unsigned char *)hreq->header[i].value;
+ nva[i].valuelen = hreq->header[i].valuelen;
+ nva[i].flags = NGHTTP3_NV_FLAG_NONE;
+ }
+ }
+
+ switch(data->state.httpreq) {
+ case HTTPREQ_POST:
+ case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
+ case HTTPREQ_PUT: {
+ nghttp3_data_reader data_reader;
+ if(data->state.infilesize != -1)
+ stream->upload_left = data->state.infilesize;
+ else
+ /* data sending without specifying the data amount up front */
+ stream->upload_left = -1; /* unknown, but not zero */
+
+ data_reader.read_data = cb_h3_readfunction;
+
+ h3out = calloc(sizeof(struct h3out), 1);
+ if(!h3out) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ stream->h3out = h3out;
+
+ rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id,
+ nva, nheader, &data_reader, data);
+ if(rc) {
+ result = CURLE_SEND_ERROR;
+ goto fail;
+ }
+ break;
+ }
+ default:
+ stream->upload_left = 0; /* nothing left to send */
+ rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id,
+ nva, nheader, NULL, data);
+ if(rc) {
+ result = CURLE_SEND_ERROR;
+ goto fail;
+ }
+ break;
+ }
+
+ Curl_safefree(nva);
+
+ infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)",
+ stream3_id, (void *)data);
+
+ Curl_pseudo_free(hreq);
+ return CURLE_OK;
+
+fail:
+ free(nva);
+ Curl_pseudo_free(hreq);
+ return result;
+}
+static ssize_t ngh3_stream_send(struct Curl_easy *data,
+ int sockindex,
+ const void *mem,
+ size_t len,
+ CURLcode *curlcode)
+{
+ ssize_t sent = 0;
+ struct connectdata *conn = data->conn;
+ struct quicsocket *qs = conn->quic;
+ curl_socket_t sockfd = conn->sock[sockindex];
+ struct HTTP *stream = data->req.p.http;
+
+ if(stream->closed) {
+ *curlcode = CURLE_HTTP3;
+ return -1;
+ }
+
+ if(!stream->h3req) {
+ CURLcode result = http_request(data, mem, len);
+ if(result) {
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+ /* Assume that mem of length len only includes HTTP/1.1 style
+ header fields. In other words, it does not contain request
+ body. */
+ sent = len;
+ }
+ else {
+ H3BUGF(infof(data, "ngh3_stream_send() wants to send %zd bytes",
+ len));
+ if(!stream->upload_len) {
+ stream->upload_mem = mem;
+ stream->upload_len = len;
+ (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id);
+ }
+ else {
+ *curlcode = CURLE_AGAIN;
+ return -1;
+ }
+ }
+
+ if(ng_flush_egress(data, sockfd, qs)) {
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+
+ /* Reset post upload buffer after resumed. */
+ if(stream->upload_mem) {
+ if(data->set.postfields) {
+ sent = len;
+ }
+ else {
+ sent = len - stream->upload_len;
+ }
+
+ stream->upload_mem = NULL;
+ stream->upload_len = 0;
+
+ if(sent == 0) {
+ *curlcode = CURLE_AGAIN;
+ return -1;
+ }
+ }
+
+ *curlcode = CURLE_OK;
+ return sent;
+}
+
+static CURLcode ng_has_connected(struct Curl_easy *data,
+ struct connectdata *conn, int tempindex)
+{
+ CURLcode result = CURLE_OK;
+ const char *hostname, *disp_hostname;
+ int port;
+ char *snihost;
+
+ Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port);
+ snihost = Curl_ssl_snihost(data, hostname, NULL);
+ if(!snihost)
+ return CURLE_PEER_FAILED_VERIFICATION;
+
+ conn->recv[FIRSTSOCKET] = ngh3_stream_recv;
+ conn->send[FIRSTSOCKET] = ngh3_stream_send;
+ conn->handler = &Curl_handler_http3;
+ conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ conn->httpversion = 30;
+ conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+ conn->quic = &conn->hequic[tempindex];
+
+ if(conn->ssl_config.verifyhost) {
+#ifdef USE_OPENSSL
+ X509 *server_cert;
+ server_cert = SSL_get_peer_certificate(conn->quic->ssl);
+ if(!server_cert) {
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ result = Curl_ossl_verifyhost(data, conn, server_cert);
+ X509_free(server_cert);
+ if(result)
+ return result;
+#elif defined(USE_GNUTLS)
+ result = Curl_gtls_verifyserver(data, conn->quic->gtls->session,
+ &conn->ssl_config, &data->set.ssl,
+ hostname, disp_hostname,
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
+ if(result)
+ return result;
+#elif defined(USE_WOLFSSL)
+ if(wolfSSL_check_domain_name(conn->quic->ssl, snihost) == SSL_FAILURE)
+ return CURLE_PEER_FAILED_VERIFICATION;
+#endif
+ infof(data, "Verified certificate just fine");
+ }
+ else
+ infof(data, "Skipped certificate verification");
+#ifdef USE_OPENSSL
+ if(data->set.ssl.certinfo)
+ /* asked to gather certificate info */
+ (void)Curl_ossl_certchain(data, conn->quic->ssl);
+#endif
+ return result;
+}
+
+/*
+ * There can be multiple connection attempts going on in parallel.
+ */
+CURLcode Curl_quic_is_connected(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ bool *done)
+{
+ CURLcode result;
+ struct quicsocket *qs = &conn->hequic[sockindex];
+ curl_socket_t sockfd = conn->tempsock[sockindex];
+
+ result = ng_process_ingress(data, sockfd, qs);
+ if(result)
+ goto error;
+
+ result = ng_flush_egress(data, sockfd, qs);
+ if(result)
+ goto error;
+
+ if(ngtcp2_conn_get_handshake_completed(qs->qconn)) {
+ result = ng_has_connected(data, conn, sockindex);
+ if(!result)
+ *done = TRUE;
+ }
+
+ return result;
+ error:
+ (void)qs_disconnect(qs);
+ return result;
+
+}
+
+static CURLcode ng_process_ingress(struct Curl_easy *data,
+ curl_socket_t sockfd,
+ struct quicsocket *qs)
+{
+ ssize_t recvd;
+ int rv;
+ uint8_t buf[65536];
+ size_t bufsize = sizeof(buf);
+ struct sockaddr_storage remote_addr;
+ socklen_t remote_addrlen;
+ ngtcp2_path path;
+ ngtcp2_tstamp ts = timestamp();
+ ngtcp2_pkt_info pi = { 0 };
+
+ for(;;) {
+ remote_addrlen = sizeof(remote_addr);
+ while((recvd = recvfrom(sockfd, (char *)buf, bufsize, 0,
+ (struct sockaddr *)&remote_addr,
+ &remote_addrlen)) == -1 &&
+ SOCKERRNO == EINTR)
+ ;
+ if(recvd == -1) {
+ if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK)
+ break;
+
+ failf(data, "ngtcp2: recvfrom() unexpectedly returned %zd", recvd);
+ return CURLE_RECV_ERROR;
+ }
+
+ ngtcp2_addr_init(&path.local, (struct sockaddr *)&qs->local_addr,
+ qs->local_addrlen);
+ ngtcp2_addr_init(&path.remote, (struct sockaddr *)&remote_addr,
+ remote_addrlen);
+
+ rv = ngtcp2_conn_read_pkt(qs->qconn, &path, &pi, buf, recvd, ts);
+ if(rv) {
+ if(!qs->last_error.error_code) {
+ if(rv == NGTCP2_ERR_CRYPTO) {
+ ngtcp2_connection_close_error_set_transport_error_tls_alert(
+ &qs->last_error, ngtcp2_conn_get_tls_alert(qs->qconn), NULL, 0);
+ }
+ else {
+ ngtcp2_connection_close_error_set_transport_error_liberr(
+ &qs->last_error, rv, NULL, 0);
+ }
+ }
+
+ if(rv == NGTCP2_ERR_CRYPTO)
+ /* this is a "TLS problem", but a failed certificate verification
+ is a common reason for this */
+ return CURLE_PEER_FAILED_VERIFICATION;
+ return CURLE_RECV_ERROR;
+ }
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode do_sendmsg(size_t *sent, struct Curl_easy *data, int sockfd,
+ struct quicsocket *qs, const uint8_t *pkt,
+ size_t pktlen, size_t gsolen);
+
+static CURLcode send_packet_no_gso(size_t *psent, struct Curl_easy *data,
+ int sockfd, struct quicsocket *qs,
+ const uint8_t *pkt, size_t pktlen,
+ size_t gsolen)
+{
+ const uint8_t *p, *end = pkt + pktlen;
+ size_t sent;
+
+ *psent = 0;
+
+ for(p = pkt; p < end; p += gsolen) {
+ size_t len = CURLMIN(gsolen, (size_t)(end - p));
+ CURLcode curlcode = do_sendmsg(&sent, data, sockfd, qs, p, len, len);
+ if(curlcode != CURLE_OK) {
+ return curlcode;
+ }
+ *psent += sent;
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode do_sendmsg(size_t *psent, struct Curl_easy *data, int sockfd,
+ struct quicsocket *qs, const uint8_t *pkt,
+ size_t pktlen, size_t gsolen)
+{
+#ifdef HAVE_SENDMSG
+ struct iovec msg_iov;
+ struct msghdr msg = {0};
+ ssize_t sent;
+#if defined(__linux__) && defined(UDP_SEGMENT)
+ uint8_t msg_ctrl[32];
+ struct cmsghdr *cm;
+#endif
+
+ *psent = 0;
+ msg_iov.iov_base = (uint8_t *)pkt;
+ msg_iov.iov_len = pktlen;
+ msg.msg_iov = &msg_iov;
+ msg.msg_iovlen = 1;
+
+#if defined(__linux__) && defined(UDP_SEGMENT)
+ if(pktlen > gsolen) {
+ /* Only set this, when we need it. macOS, for example,
+ * does not seem to like a msg_control of length 0. */
+ msg.msg_control = msg_ctrl;
+ assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(uint16_t)));
+ msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t));
+ cm = CMSG_FIRSTHDR(&msg);
+ cm->cmsg_level = SOL_UDP;
+ cm->cmsg_type = UDP_SEGMENT;
+ cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
+ *(uint16_t *)(void *)CMSG_DATA(cm) = gsolen & 0xffff;
+ }
+#endif
+
+
+ while((sent = sendmsg(sockfd, &msg, 0)) == -1 && SOCKERRNO == EINTR)
+ ;
+
+ if(sent == -1) {
+ switch(SOCKERRNO) {
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ return CURLE_AGAIN;
+ case EMSGSIZE:
+ /* UDP datagram is too large; caused by PMTUD. Just let it be lost. */
+ break;
+ case EIO:
+ if(pktlen > gsolen) {
+ /* GSO failure */
+ failf(data, "sendmsg() returned %zd (errno %d); disable GSO", sent,
+ SOCKERRNO);
+ qs->no_gso = TRUE;
+ return send_packet_no_gso(psent, data, sockfd, qs, pkt, pktlen,
+ gsolen);
+ }
+ /* FALLTHROUGH */
+ default:
+ failf(data, "sendmsg() returned %zd (errno %d)", sent, SOCKERRNO);
+ return CURLE_SEND_ERROR;
+ }
+ }
+ else {
+ assert(pktlen == (size_t)sent);
+ }
+#else
+ ssize_t sent;
+ (void)qs;
+ (void)gsolen;
+
+ *psent = 0;
+
+ while((sent = send(sockfd, (const char *)pkt, pktlen, 0)) == -1 &&
+ SOCKERRNO == EINTR)
+ ;
+
+ if(sent == -1) {
+ if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
+ return CURLE_AGAIN;
+ }
+ else {
+ failf(data, "send() returned %zd (errno %d)", sent, SOCKERRNO);
+ if(SOCKERRNO != EMSGSIZE) {
+ return CURLE_SEND_ERROR;
+ }
+ /* UDP datagram is too large; caused by PMTUD. Just let it be
+ lost. */
+ }
+ }
+#endif
+
+ *psent = pktlen;
+
+ return CURLE_OK;
+}
+
+static CURLcode send_packet(size_t *psent, struct Curl_easy *data, int sockfd,
+ struct quicsocket *qs, const uint8_t *pkt,
+ size_t pktlen, size_t gsolen)
+{
+ if(qs->no_gso && pktlen > gsolen) {
+ return send_packet_no_gso(psent, data, sockfd, qs, pkt, pktlen, gsolen);
+ }
+
+ return do_sendmsg(psent, data, sockfd, qs, pkt, pktlen, gsolen);
+}
+
+static void push_blocked_pkt(struct quicsocket *qs, const uint8_t *pkt,
+ size_t pktlen, size_t gsolen)
+{
+ struct blocked_pkt *blkpkt;
+
+ assert(qs->num_blocked_pkt <
+ sizeof(qs->blocked_pkt) / sizeof(qs->blocked_pkt[0]));
+
+ blkpkt = &qs->blocked_pkt[qs->num_blocked_pkt++];
+
+ blkpkt->pkt = pkt;
+ blkpkt->pktlen = pktlen;
+ blkpkt->gsolen = gsolen;
+}
+
+static CURLcode send_blocked_pkt(struct Curl_easy *data, int sockfd,
+ struct quicsocket *qs)
+{
+ size_t sent;
+ CURLcode curlcode;
+ struct blocked_pkt *blkpkt;
+
+ for(; qs->num_blocked_pkt_sent < qs->num_blocked_pkt;
+ ++qs->num_blocked_pkt_sent) {
+ blkpkt = &qs->blocked_pkt[qs->num_blocked_pkt_sent];
+ curlcode = send_packet(&sent, data, sockfd, qs, blkpkt->pkt,
+ blkpkt->pktlen, blkpkt->gsolen);
+
+ if(curlcode) {
+ if(curlcode == CURLE_AGAIN) {
+ blkpkt->pkt += sent;
+ blkpkt->pktlen -= sent;
+ }
+ return curlcode;
+ }
+ }
+
+ qs->num_blocked_pkt = 0;
+ qs->num_blocked_pkt_sent = 0;
+
+ return CURLE_OK;
+}
+
+static CURLcode ng_flush_egress(struct Curl_easy *data,
+ int sockfd,
+ struct quicsocket *qs)
+{
+ int rv;
+ size_t sent;
+ ngtcp2_ssize outlen;
+ uint8_t *outpos = qs->pktbuf;
+ size_t max_udp_payload_size =
+ ngtcp2_conn_get_max_tx_udp_payload_size(qs->qconn);
+ size_t path_max_udp_payload_size =
+ ngtcp2_conn_get_path_max_tx_udp_payload_size(qs->qconn);
+ size_t max_pktcnt =
+ CURLMIN(MAX_PKT_BURST, qs->pktbuflen / max_udp_payload_size);
+ size_t pktcnt = 0;
+ size_t gsolen;
+ ngtcp2_path_storage ps;
+ ngtcp2_tstamp ts = timestamp();
+ ngtcp2_tstamp expiry;
+ ngtcp2_duration timeout;
+ int64_t stream_id;
+ nghttp3_ssize veccnt;
+ int fin;
+ nghttp3_vec vec[16];
+ ngtcp2_ssize ndatalen;
+ uint32_t flags;
+ CURLcode curlcode;
+
+ rv = ngtcp2_conn_handle_expiry(qs->qconn, ts);
+ if(rv) {
+ failf(data, "ngtcp2_conn_handle_expiry returned error: %s",
+ ngtcp2_strerror(rv));
+ ngtcp2_connection_close_error_set_transport_error_liberr(&qs->last_error,
+ rv, NULL, 0);
+ return CURLE_SEND_ERROR;
+ }
+
+ if(qs->num_blocked_pkt) {
+ curlcode = send_blocked_pkt(data, sockfd, qs);
+ if(curlcode) {
+ if(curlcode == CURLE_AGAIN) {
+ Curl_expire(data, 1, EXPIRE_QUIC);
+ return CURLE_OK;
+ }
+ return curlcode;
+ }
+ }
+
+ ngtcp2_path_storage_zero(&ps);
+
+ for(;;) {
+ veccnt = 0;
+ stream_id = -1;
+ fin = 0;
+
+ if(qs->h3conn && ngtcp2_conn_get_max_data_left(qs->qconn)) {
+ veccnt = nghttp3_conn_writev_stream(qs->h3conn, &stream_id, &fin, vec,
+ sizeof(vec) / sizeof(vec[0]));
+ if(veccnt < 0) {
+ failf(data, "nghttp3_conn_writev_stream returned error: %s",
+ nghttp3_strerror((int)veccnt));
+ ngtcp2_connection_close_error_set_application_error(
+ &qs->last_error,
+ nghttp3_err_infer_quic_app_error_code((int)veccnt), NULL, 0);
+ return CURLE_SEND_ERROR;
+ }
+ }
+
+ flags = NGTCP2_WRITE_STREAM_FLAG_MORE |
+ (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0);
+ outlen = ngtcp2_conn_writev_stream(qs->qconn, &ps.path, NULL, outpos,
+ max_udp_payload_size,
+ &ndatalen, flags, stream_id,
+ (const ngtcp2_vec *)vec, veccnt, ts);
+ if(outlen == 0) {
+ if(outpos != qs->pktbuf) {
+ curlcode = send_packet(&sent, data, sockfd, qs, qs->pktbuf,
+ outpos - qs->pktbuf, gsolen);
+ if(curlcode) {
+ if(curlcode == CURLE_AGAIN) {
+ push_blocked_pkt(qs, qs->pktbuf + sent, outpos - qs->pktbuf - sent,
+ gsolen);
+ Curl_expire(data, 1, EXPIRE_QUIC);
+ return CURLE_OK;
+ }
+ return curlcode;
+ }
+ }
+
+ break;
+ }
+ if(outlen < 0) {
+ switch(outlen) {
+ case NGTCP2_ERR_STREAM_DATA_BLOCKED:
+ assert(ndatalen == -1);
+ nghttp3_conn_block_stream(qs->h3conn, stream_id);
+ continue;
+ case NGTCP2_ERR_STREAM_SHUT_WR:
+ assert(ndatalen == -1);
+ nghttp3_conn_shutdown_stream_write(qs->h3conn, stream_id);
+ continue;
+ case NGTCP2_ERR_WRITE_MORE:
+ assert(ndatalen >= 0);
+ rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ndatalen);
+ if(rv) {
+ failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
+ nghttp3_strerror(rv));
+ return CURLE_SEND_ERROR;
+ }
+ continue;
+ default:
+ assert(ndatalen == -1);
+ failf(data, "ngtcp2_conn_writev_stream returned error: %s",
+ ngtcp2_strerror((int)outlen));
+ ngtcp2_connection_close_error_set_transport_error_liberr(
+ &qs->last_error, (int)outlen, NULL, 0);
+ return CURLE_SEND_ERROR;
+ }
+ }
+ else if(ndatalen >= 0) {
+ rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ndatalen);
+ if(rv) {
+ failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
+ nghttp3_strerror(rv));
+ return CURLE_SEND_ERROR;
+ }
+ }
+
+ outpos += outlen;
+
+ if(pktcnt == 0) {
+ gsolen = outlen;
+ }
+ else if((size_t)outlen > gsolen ||
+ (gsolen > path_max_udp_payload_size &&
+ (size_t)outlen != gsolen)) {
+ /* Packet larger than path_max_udp_payload_size is PMTUD probe
+ packet and it might not be sent because of EMSGSIZE. Send
+ them separately to minimize the loss. */
+ curlcode = send_packet(&sent, data, sockfd, qs, qs->pktbuf,
+ outpos - outlen - qs->pktbuf, gsolen);
+ if(curlcode) {
+ if(curlcode == CURLE_AGAIN) {
+ push_blocked_pkt(qs, qs->pktbuf + sent,
+ outpos - outlen - qs->pktbuf - sent, gsolen);
+ push_blocked_pkt(qs, outpos - outlen, outlen, outlen);
+ Curl_expire(data, 1, EXPIRE_QUIC);
+ return CURLE_OK;
+ }
+ return curlcode;
+ }
+ curlcode = send_packet(&sent, data, sockfd, qs, outpos - outlen, outlen,
+ outlen);
+ if(curlcode) {
+ if(curlcode == CURLE_AGAIN) {
+ assert(0 == sent);
+ push_blocked_pkt(qs, outpos - outlen, outlen, outlen);
+ Curl_expire(data, 1, EXPIRE_QUIC);
+ return CURLE_OK;
+ }
+ return curlcode;
+ }
+
+ pktcnt = 0;
+ outpos = qs->pktbuf;
+ continue;
+ }
+
+ if(++pktcnt >= max_pktcnt || (size_t)outlen < gsolen) {
+ curlcode = send_packet(&sent, data, sockfd, qs, qs->pktbuf,
+ outpos - qs->pktbuf, gsolen);
+ if(curlcode) {
+ if(curlcode == CURLE_AGAIN) {
+ push_blocked_pkt(qs, qs->pktbuf + sent, outpos - qs->pktbuf - sent,
+ gsolen);
+ Curl_expire(data, 1, EXPIRE_QUIC);
+ return CURLE_OK;
+ }
+ return curlcode;
+ }
+
+ pktcnt = 0;
+ outpos = qs->pktbuf;
+ }
+ }
+
+ expiry = ngtcp2_conn_get_expiry(qs->qconn);
+ if(expiry != UINT64_MAX) {
+ if(expiry <= ts) {
+ timeout = 0;
+ }
+ else {
+ timeout = expiry - ts;
+ if(timeout % NGTCP2_MILLISECONDS) {
+ timeout += NGTCP2_MILLISECONDS;
+ }
+ }
+ Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC);
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Called from transfer.c:done_sending when we stop HTTP/3 uploading.
+ */
+CURLcode Curl_quic_done_sending(struct Curl_easy *data)
+{
+ struct connectdata *conn = data->conn;
+ DEBUGASSERT(conn);
+ if(conn->handler == &Curl_handler_http3) {
+ /* only for HTTP/3 transfers */
+ struct HTTP *stream = data->req.p.http;
+ struct quicsocket *qs = conn->quic;
+ stream->upload_done = TRUE;
+ (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id);
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Called from http.c:Curl_http_done when a request completes.
+ */
+void Curl_quic_done(struct Curl_easy *data, bool premature)
+{
+ (void)premature;
+ if(data->conn->handler == &Curl_handler_http3) {
+ /* only for HTTP/3 transfers */
+ struct HTTP *stream = data->req.p.http;
+ Curl_dyn_free(&stream->overflow);
+ free(stream->h3out);
+ }
+}
+
+/*
+ * Called from transfer.c:data_pending to know if we should keep looping
+ * to receive more data from the connection.
+ */
+bool Curl_quic_data_pending(const struct Curl_easy *data)
+{
+ /* We may have received more data than we're able to hold in the receive
+ buffer and allocated an overflow buffer. Since it's possible that
+ there's no more data coming on the socket, we need to keep reading
+ until the overflow buffer is empty. */
+ const struct HTTP *stream = data->req.p.http;
+ return Curl_dyn_len(&stream->overflow) > 0;
+}
+
+/*
+ * Called from transfer.c:Curl_readwrite when neither HTTP level read
+ * nor write is performed. It is a good place to handle timer expiry
+ * for QUIC transport.
+ */
+CURLcode Curl_quic_idle(struct Curl_easy *data)
+{
+ struct connectdata *conn = data->conn;
+ curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+ struct quicsocket *qs = conn->quic;
+
+ if(ngtcp2_conn_get_expiry(qs->qconn) > timestamp()) {
+ return CURLE_OK;
+ }
+
+ if(ng_flush_egress(data, sockfd, qs)) {
+ return CURLE_SEND_ERROR;
+ }
+
+ return CURLE_OK;
+}
+
+#endif
diff --git a/libs/libcurl/src/vquic/ngtcp2.h b/libs/libcurl/src/vquic/ngtcp2.h index 6539f5fef3..41d88dbe9c 100644 --- a/libs/libcurl/src/vquic/ngtcp2.h +++ b/libs/libcurl/src/vquic/ngtcp2.h @@ -1,94 +1,93 @@ -#ifndef HEADER_CURL_VQUIC_NGTCP2_H -#define HEADER_CURL_VQUIC_NGTCP2_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_NGTCP2 - -#ifdef HAVE_NETINET_UDP_H -#include <netinet/udp.h> -#endif - -#include <ngtcp2/ngtcp2_crypto.h> -#include <nghttp3/nghttp3.h> -#ifdef USE_OPENSSL -#include <openssl/ssl.h> -#elif defined(USE_GNUTLS) -#include <gnutls/gnutls.h> -#elif defined(USE_WOLFSSL) -#include <wolfssl/options.h> -#include <wolfssl/ssl.h> -#include <wolfssl/quic.h> -#endif - -struct blocked_pkt { - const uint8_t *pkt; - size_t pktlen; - size_t gsolen; -}; - -struct quicsocket { - struct connectdata *conn; /* point back to the connection */ - ngtcp2_conn *qconn; - ngtcp2_cid dcid; - ngtcp2_cid scid; - uint32_t version; - ngtcp2_settings settings; - ngtcp2_transport_params transport_params; - ngtcp2_connection_close_error last_error; - ngtcp2_crypto_conn_ref conn_ref; -#ifdef USE_OPENSSL - SSL_CTX *sslctx; - SSL *ssl; -#elif defined(USE_GNUTLS) - gnutls_certificate_credentials_t cred; - gnutls_session_t ssl; -#elif defined(USE_WOLFSSL) - WOLFSSL_CTX *sslctx; - WOLFSSL *ssl; -#endif - struct sockaddr_storage local_addr; - socklen_t local_addrlen; - bool no_gso; - uint8_t *pktbuf; - size_t pktbuflen; - /* the number of entries in blocked_pkt */ - size_t num_blocked_pkt; - /* the number of processed entries in blocked_pkt */ - size_t num_blocked_pkt_sent; - /* the packets blocked by sendmsg (EAGAIN or EWOULDBLOCK) */ - struct blocked_pkt blocked_pkt[2]; - - nghttp3_conn *h3conn; - nghttp3_settings h3settings; - int qlogfd; -}; - -#include "urldata.h" - -#endif - -#endif /* HEADER_CURL_VQUIC_NGTCP2_H */ +#ifndef HEADER_CURL_VQUIC_NGTCP2_H
+#define HEADER_CURL_VQUIC_NGTCP2_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_NGTCP2
+
+#ifdef HAVE_NETINET_UDP_H
+#include <netinet/udp.h>
+#endif
+
+#include <ngtcp2/ngtcp2_crypto.h>
+#include <nghttp3/nghttp3.h>
+#ifdef USE_OPENSSL
+#include <openssl/ssl.h>
+#elif defined(USE_WOLFSSL)
+#include <wolfssl/options.h>
+#include <wolfssl/ssl.h>
+#include <wolfssl/quic.h>
+#endif
+
+struct gtls_instance;
+
+struct blocked_pkt {
+ const uint8_t *pkt;
+ size_t pktlen;
+ size_t gsolen;
+};
+
+struct quicsocket {
+ struct connectdata *conn; /* point back to the connection */
+ ngtcp2_conn *qconn;
+ ngtcp2_cid dcid;
+ ngtcp2_cid scid;
+ uint32_t version;
+ ngtcp2_settings settings;
+ ngtcp2_transport_params transport_params;
+ ngtcp2_connection_close_error last_error;
+ ngtcp2_crypto_conn_ref conn_ref;
+#ifdef USE_OPENSSL
+ SSL_CTX *sslctx;
+ SSL *ssl;
+#elif defined(USE_GNUTLS)
+ struct gtls_instance *gtls;
+#elif defined(USE_WOLFSSL)
+ WOLFSSL_CTX *sslctx;
+ WOLFSSL *ssl;
+#endif
+ struct sockaddr_storage local_addr;
+ socklen_t local_addrlen;
+ bool no_gso;
+ uint8_t *pktbuf;
+ size_t pktbuflen;
+ /* the number of entries in blocked_pkt */
+ size_t num_blocked_pkt;
+ /* the number of processed entries in blocked_pkt */
+ size_t num_blocked_pkt_sent;
+ /* the packets blocked by sendmsg (EAGAIN or EWOULDBLOCK) */
+ struct blocked_pkt blocked_pkt[2];
+
+ nghttp3_conn *h3conn;
+ nghttp3_settings h3settings;
+ int qlogfd;
+};
+
+#include "urldata.h"
+
+#endif
+
+#endif /* HEADER_CURL_VQUIC_NGTCP2_H */
diff --git a/libs/libcurl/src/vquic/quiche.c b/libs/libcurl/src/vquic/quiche.c index a52a7e8e21..ea231d6379 100644 --- a/libs/libcurl/src/vquic/quiche.c +++ b/libs/libcurl/src/vquic/quiche.c @@ -1,895 +1,892 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_QUICHE -#include <quiche.h> -#include <openssl/err.h> -#include <openssl/ssl.h> -#include "urldata.h" -#include "sendf.h" -#include "strdup.h" -#include "rand.h" -#include "quic.h" -#include "strcase.h" -#include "multiif.h" -#include "connect.h" -#include "strerror.h" -#include "vquic.h" -#include "transfer.h" -#include "h2h3.h" -#include "vtls/openssl.h" -#include "vtls/keylog.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#define DEBUG_HTTP3 -/* #define DEBUG_QUICHE */ -#ifdef DEBUG_HTTP3 -#define H3BUGF(x) x -#else -#define H3BUGF(x) do { } while(0) -#endif - -#define QUIC_MAX_STREAMS (256*1024) -#define QUIC_MAX_DATA (1*1024*1024) -#define QUIC_IDLE_TIMEOUT (60 * 1000) /* milliseconds */ - -static CURLcode process_ingress(struct Curl_easy *data, - curl_socket_t sockfd, - struct quicsocket *qs); - -static CURLcode flush_egress(struct Curl_easy *data, curl_socket_t sockfd, - struct quicsocket *qs); - -static CURLcode http_request(struct Curl_easy *data, const void *mem, - size_t len); -static Curl_recv h3_stream_recv; -static Curl_send h3_stream_send; - -static int quiche_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *socks) -{ - struct SingleRequest *k = &data->req; - int bitmap = GETSOCK_BLANK; - - socks[0] = conn->sock[FIRSTSOCKET]; - - /* in a HTTP/2 connection we can basically always get a frame so we should - always be ready for one */ - bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); - - /* we're still uploading or the HTTP/2 layer wants to send data */ - if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) - bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); - - return bitmap; -} - -static CURLcode qs_disconnect(struct Curl_easy *data, - struct quicsocket *qs) -{ - DEBUGASSERT(qs); - if(qs->conn) { - (void)quiche_conn_close(qs->conn, TRUE, 0, NULL, 0); - /* flushing the egress is not a failsafe way to deliver all the - outstanding packets, but we also don't want to get stuck here... */ - (void)flush_egress(data, qs->sockfd, qs); - quiche_conn_free(qs->conn); - qs->conn = NULL; - } - if(qs->h3config) - quiche_h3_config_free(qs->h3config); - if(qs->h3c) - quiche_h3_conn_free(qs->h3c); - if(qs->cfg) { - quiche_config_free(qs->cfg); - qs->cfg = NULL; - } - return CURLE_OK; -} - -static CURLcode quiche_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection) -{ - struct quicsocket *qs = conn->quic; - (void)dead_connection; - return qs_disconnect(data, qs); -} - -void Curl_quic_disconnect(struct Curl_easy *data, - struct connectdata *conn, - int tempindex) -{ - if(conn->transport == TRNSPRT_QUIC) - qs_disconnect(data, &conn->hequic[tempindex]); -} - -static unsigned int quiche_conncheck(struct Curl_easy *data, - struct connectdata *conn, - unsigned int checks_to_perform) -{ - (void)data; - (void)conn; - (void)checks_to_perform; - return CONNRESULT_NONE; -} - -static CURLcode quiche_do(struct Curl_easy *data, bool *done) -{ - struct HTTP *stream = data->req.p.http; - stream->h3req = FALSE; /* not sent */ - return Curl_http(data, done); -} - -static const struct Curl_handler Curl_handler_http3 = { - "HTTPS", /* scheme */ - ZERO_NULL, /* setup_connection */ - quiche_do, /* do_it */ - Curl_http_done, /* done */ - ZERO_NULL, /* do_more */ - ZERO_NULL, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - quiche_getsock, /* proto_getsock */ - quiche_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - quiche_getsock, /* perform_getsock */ - quiche_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - quiche_conncheck, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_HTTP, /* defport */ - CURLPROTO_HTTPS, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_SSL | PROTOPT_STREAM /* flags */ -}; - -#ifdef DEBUG_QUICHE -static void quiche_debug_log(const char *line, void *argp) -{ - (void)argp; - fprintf(stderr, "%s\n", line); -} -#endif - -static void keylog_callback(const SSL *ssl, const char *line) -{ - (void)ssl; - Curl_tls_keylog_write_line(line); -} - -static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data) -{ - SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); - - SSL_CTX_set_alpn_protos(ssl_ctx, - (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL, - sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1); - - SSL_CTX_set_default_verify_paths(ssl_ctx); - - /* Open the file if a TLS or QUIC backend has not done this before. */ - Curl_tls_keylog_open(); - if(Curl_tls_keylog_enabled()) { - SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); - } - - { - struct connectdata *conn = data->conn; - if(conn->ssl_config.verifypeer) { - const char * const ssl_cafile = conn->ssl_config.CAfile; - const char * const ssl_capath = conn->ssl_config.CApath; - if(ssl_cafile || ssl_capath) { - SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); - /* tell OpenSSL where to find CA certificates that are used to verify - the server's certificate. */ - if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate verify locations:" - " CAfile: %s CApath: %s", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); - return NULL; - } - infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); - infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); - } -#ifdef CURL_CA_FALLBACK - else { - /* verifying the peer without any CA certificates won't work so - use openssl's built-in default as fallback */ - SSL_CTX_set_default_verify_paths(ssl_ctx); - } -#endif - } - } - return ssl_ctx; -} - -static int quic_init_ssl(struct quicsocket *qs, struct connectdata *conn) -{ - /* this will need some attention when HTTPS proxy over QUIC get fixed */ - const char * const hostname = conn->host.name; - - DEBUGASSERT(!qs->ssl); - qs->ssl = SSL_new(qs->sslctx); - - SSL_set_app_data(qs->ssl, qs); - - /* set SNI */ - SSL_set_tlsext_host_name(qs->ssl, hostname); - return 0; -} - - -CURLcode Curl_quic_connect(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t sockfd, - int sockindex, - const struct sockaddr *addr, socklen_t addrlen) -{ - CURLcode result; - struct quicsocket *qs = &conn->hequic[sockindex]; - char ipbuf[40]; - int port; - int rv; - -#ifdef DEBUG_QUICHE - /* initialize debug log callback only once */ - static int debug_log_init = 0; - if(!debug_log_init) { - quiche_enable_debug_logging(quiche_debug_log, NULL); - debug_log_init = 1; - } -#endif - - (void)addr; - (void)addrlen; - - qs->sockfd = sockfd; - qs->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION); - if(!qs->cfg) { - failf(data, "can't create quiche config"); - return CURLE_FAILED_INIT; - } - - quiche_config_set_max_idle_timeout(qs->cfg, QUIC_IDLE_TIMEOUT); - quiche_config_set_initial_max_data(qs->cfg, QUIC_MAX_DATA); - quiche_config_set_initial_max_stream_data_bidi_local(qs->cfg, QUIC_MAX_DATA); - quiche_config_set_initial_max_stream_data_bidi_remote(qs->cfg, - QUIC_MAX_DATA); - quiche_config_set_initial_max_stream_data_uni(qs->cfg, QUIC_MAX_DATA); - quiche_config_set_initial_max_streams_bidi(qs->cfg, QUIC_MAX_STREAMS); - quiche_config_set_initial_max_streams_uni(qs->cfg, QUIC_MAX_STREAMS); - quiche_config_set_application_protos(qs->cfg, - (uint8_t *) - QUICHE_H3_APPLICATION_PROTOCOL, - sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - - 1); - - qs->sslctx = quic_ssl_ctx(data); - if(!qs->sslctx) - return CURLE_QUIC_CONNECT_ERROR; - - if(quic_init_ssl(qs, conn)) - return CURLE_QUIC_CONNECT_ERROR; - - result = Curl_rand(data, qs->scid, sizeof(qs->scid)); - if(result) - return result; - - qs->local_addrlen = sizeof(qs->local_addr); - rv = getsockname(sockfd, (struct sockaddr *)&qs->local_addr, - &qs->local_addrlen); - if(rv == -1) - return CURLE_QUIC_CONNECT_ERROR; - - qs->conn = quiche_conn_new_with_tls((const uint8_t *) qs->scid, - sizeof(qs->scid), NULL, 0, - (struct sockaddr *)&qs->local_addr, - qs->local_addrlen, addr, addrlen, - qs->cfg, qs->ssl, false); - if(!qs->conn) { - failf(data, "can't create quiche connection"); - return CURLE_OUT_OF_MEMORY; - } - - /* Known to not work on Windows */ -#if !defined(WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD) - { - int qfd; - (void)Curl_qlogdir(data, qs->scid, sizeof(qs->scid), &qfd); - if(qfd != -1) - quiche_conn_set_qlog_fd(qs->conn, qfd, - "qlog title", "curl qlog"); - } -#endif - - result = flush_egress(data, sockfd, qs); - if(result) - return result; - - /* extract the used address as a string */ - if(!Curl_addr2string((struct sockaddr*)addr, addrlen, ipbuf, &port)) { - char buffer[STRERROR_LEN]; - failf(data, "ssrem inet_ntop() failed with errno %d: %s", - SOCKERRNO, Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - infof(data, "Connect socket %d over QUIC to %s:%ld", - sockfd, ipbuf, port); - - Curl_persistconninfo(data, conn, NULL, -1); - - /* for connection reuse purposes: */ - conn->ssl[FIRSTSOCKET].state = ssl_connection_complete; - - { - unsigned char alpn_protocols[] = QUICHE_H3_APPLICATION_PROTOCOL; - unsigned alpn_len, offset = 0; - - /* Replace each ALPN length prefix by a comma. */ - while(offset < sizeof(alpn_protocols) - 1) { - alpn_len = alpn_protocols[offset]; - alpn_protocols[offset] = ','; - offset += 1 + alpn_len; - } - - infof(data, "Sent QUIC client Initial, ALPN: %s", - alpn_protocols + 1); - } - - return CURLE_OK; -} - -static CURLcode quiche_has_connected(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - int tempindex) -{ - CURLcode result; - struct quicsocket *qs = conn->quic = &conn->hequic[tempindex]; - - conn->recv[sockindex] = h3_stream_recv; - conn->send[sockindex] = h3_stream_send; - conn->handler = &Curl_handler_http3; - conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - conn->httpversion = 30; - conn->bundle->multiuse = BUNDLE_MULTIPLEX; - - if(conn->ssl_config.verifyhost) { - X509 *server_cert; - server_cert = SSL_get_peer_certificate(qs->ssl); - if(!server_cert) { - return CURLE_PEER_FAILED_VERIFICATION; - } - result = Curl_ossl_verifyhost(data, conn, server_cert); - X509_free(server_cert); - if(result) - return result; - infof(data, "Verified certificate just fine"); - } - else - infof(data, "Skipped certificate verification"); - - qs->h3config = quiche_h3_config_new(); - if(!qs->h3config) - return CURLE_OUT_OF_MEMORY; - - /* Create a new HTTP/3 connection on the QUIC connection. */ - qs->h3c = quiche_h3_conn_new_with_transport(qs->conn, qs->h3config); - if(!qs->h3c) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } - if(conn->hequic[1-tempindex].cfg) { - qs = &conn->hequic[1-tempindex]; - quiche_config_free(qs->cfg); - quiche_conn_free(qs->conn); - qs->cfg = NULL; - qs->conn = NULL; - } - if(data->set.ssl.certinfo) - /* asked to gather certificate info */ - (void)Curl_ossl_certchain(data, qs->ssl); - - return CURLE_OK; - fail: - quiche_h3_config_free(qs->h3config); - quiche_h3_conn_free(qs->h3c); - return result; -} - -/* - * This function gets polled to check if this QUIC connection has connected. - */ -CURLcode Curl_quic_is_connected(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool *done) -{ - CURLcode result; - struct quicsocket *qs = &conn->hequic[sockindex]; - curl_socket_t sockfd = conn->tempsock[sockindex]; - - result = process_ingress(data, sockfd, qs); - if(result) - goto error; - - result = flush_egress(data, sockfd, qs); - if(result) - goto error; - - if(quiche_conn_is_established(qs->conn)) { - *done = TRUE; - result = quiche_has_connected(data, conn, 0, sockindex); - DEBUGF(infof(data, "quiche established connection")); - } - - return result; - error: - qs_disconnect(data, qs); - return result; -} - -static CURLcode process_ingress(struct Curl_easy *data, int sockfd, - struct quicsocket *qs) -{ - ssize_t recvd; - uint8_t *buf = (uint8_t *)data->state.buffer; - size_t bufsize = data->set.buffer_size; - struct sockaddr_storage from; - socklen_t from_len; - quiche_recv_info recv_info; - - DEBUGASSERT(qs->conn); - - /* in case the timeout expired */ - quiche_conn_on_timeout(qs->conn); - - do { - from_len = sizeof(from); - - recvd = recvfrom(sockfd, buf, bufsize, 0, - (struct sockaddr *)&from, &from_len); - - if((recvd < 0) && ((SOCKERRNO == EAGAIN) || (SOCKERRNO == EWOULDBLOCK))) - break; - - if(recvd < 0) { - failf(data, "quiche: recvfrom() unexpectedly returned %zd " - "(errno: %d, socket %d)", recvd, SOCKERRNO, sockfd); - return CURLE_RECV_ERROR; - } - - recv_info.from = (struct sockaddr *) &from; - recv_info.from_len = from_len; - recv_info.to = (struct sockaddr *) &qs->local_addr; - recv_info.to_len = qs->local_addrlen; - - recvd = quiche_conn_recv(qs->conn, buf, recvd, &recv_info); - if(recvd == QUICHE_ERR_DONE) - break; - - if(recvd < 0) { - if(QUICHE_ERR_TLS_FAIL == recvd) { - long verify_ok = SSL_get_verify_result(qs->ssl); - if(verify_ok != X509_V_OK) { - failf(data, "SSL certificate problem: %s", - X509_verify_cert_error_string(verify_ok)); - - return CURLE_PEER_FAILED_VERIFICATION; - } - } - - failf(data, "quiche_conn_recv() == %zd", recvd); - - return CURLE_RECV_ERROR; - } - } while(1); - - return CURLE_OK; -} - -/* - * flush_egress drains the buffers and sends off data. - * Calls failf() on errors. - */ -static CURLcode flush_egress(struct Curl_easy *data, int sockfd, - struct quicsocket *qs) -{ - ssize_t sent; - uint8_t out[1200]; - int64_t timeout_ns; - quiche_send_info send_info; - - do { - sent = quiche_conn_send(qs->conn, out, sizeof(out), &send_info); - if(sent == QUICHE_ERR_DONE) - break; - - if(sent < 0) { - failf(data, "quiche_conn_send returned %zd", sent); - return CURLE_SEND_ERROR; - } - - sent = send(sockfd, out, sent, 0); - if(sent < 0) { - failf(data, "send() returned %zd", sent); - return CURLE_SEND_ERROR; - } - } while(1); - - /* time until the next timeout event, as nanoseconds. */ - timeout_ns = quiche_conn_timeout_as_nanos(qs->conn); - if(timeout_ns) - /* expire uses milliseconds */ - Curl_expire(data, (timeout_ns + 999999) / 1000000, EXPIRE_QUIC); - - return CURLE_OK; -} - -struct h3h1header { - char *dest; - size_t destlen; /* left to use */ - size_t nlen; /* used */ -}; - -static int cb_each_header(uint8_t *name, size_t name_len, - uint8_t *value, size_t value_len, - void *argp) -{ - struct h3h1header *headers = (struct h3h1header *)argp; - size_t olen = 0; - - if((name_len == 7) && !strncmp(H2H3_PSEUDO_STATUS, (char *)name, 7)) { - msnprintf(headers->dest, - headers->destlen, "HTTP/3 %.*s\n", - (int) value_len, value); - } - else if(!headers->nlen) { - return CURLE_HTTP3; - } - else { - msnprintf(headers->dest, - headers->destlen, "%.*s: %.*s\n", - (int)name_len, name, (int) value_len, value); - } - olen = strlen(headers->dest); - headers->destlen -= olen; - headers->nlen += olen; - headers->dest += olen; - return 0; -} - -static ssize_t h3_stream_recv(struct Curl_easy *data, - int sockindex, - char *buf, - size_t buffersize, - CURLcode *curlcode) -{ - ssize_t recvd = -1; - ssize_t rcode; - struct connectdata *conn = data->conn; - struct quicsocket *qs = conn->quic; - curl_socket_t sockfd = conn->sock[sockindex]; - quiche_h3_event *ev; - int rc; - struct h3h1header headers; - struct HTTP *stream = data->req.p.http; - headers.dest = buf; - headers.destlen = buffersize; - headers.nlen = 0; - - if(process_ingress(data, sockfd, qs)) { - infof(data, "h3_stream_recv returns on ingress"); - *curlcode = CURLE_RECV_ERROR; - return -1; - } - - if(qs->h3_recving) { - /* body receiving state */ - rcode = quiche_h3_recv_body(qs->h3c, qs->conn, stream->stream3_id, - (unsigned char *)buf, buffersize); - if(rcode <= 0) { - recvd = -1; - qs->h3_recving = FALSE; - /* fall through into the while loop below */ - } - else - recvd = rcode; - } - - while(recvd < 0) { - int64_t s = quiche_h3_conn_poll(qs->h3c, qs->conn, &ev); - if(s < 0) - /* nothing more to do */ - break; - - if(s != stream->stream3_id) { - /* another transfer, ignore for now */ - infof(data, "Got h3 for stream %u, expects %u", - s, stream->stream3_id); - continue; - } - - switch(quiche_h3_event_type(ev)) { - case QUICHE_H3_EVENT_HEADERS: - rc = quiche_h3_event_for_each_header(ev, cb_each_header, &headers); - if(rc) { - *curlcode = rc; - failf(data, "Error in HTTP/3 response header"); - break; - } - recvd = headers.nlen; - break; - case QUICHE_H3_EVENT_DATA: - if(!stream->firstbody) { - /* add a header-body separator CRLF */ - buf[0] = '\r'; - buf[1] = '\n'; - buf += 2; - buffersize -= 2; - stream->firstbody = TRUE; - recvd = 2; /* two bytes already */ - } - else - recvd = 0; - rcode = quiche_h3_recv_body(qs->h3c, qs->conn, s, (unsigned char *)buf, - buffersize); - if(rcode <= 0) { - recvd = -1; - break; - } - qs->h3_recving = TRUE; - recvd += rcode; - break; - - case QUICHE_H3_EVENT_RESET: - streamclose(conn, "Stream reset"); - *curlcode = CURLE_PARTIAL_FILE; - return -1; - - case QUICHE_H3_EVENT_FINISHED: - streamclose(conn, "End of stream"); - recvd = 0; /* end of stream */ - break; - default: - break; - } - - quiche_h3_event_free(ev); - } - if(flush_egress(data, sockfd, qs)) { - *curlcode = CURLE_SEND_ERROR; - return -1; - } - - *curlcode = (-1 == recvd)? CURLE_AGAIN : CURLE_OK; - if(recvd >= 0) - /* Get this called again to drain the event queue */ - Curl_expire(data, 0, EXPIRE_QUIC); - - data->state.drain = (recvd >= 0) ? 1 : 0; - return recvd; -} - -static ssize_t h3_stream_send(struct Curl_easy *data, - int sockindex, - const void *mem, - size_t len, - CURLcode *curlcode) -{ - ssize_t sent; - struct connectdata *conn = data->conn; - struct quicsocket *qs = conn->quic; - curl_socket_t sockfd = conn->sock[sockindex]; - struct HTTP *stream = data->req.p.http; - - if(!stream->h3req) { - CURLcode result = http_request(data, mem, len); - if(result) { - *curlcode = CURLE_SEND_ERROR; - return -1; - } - sent = len; - } - else { - sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id, - (uint8_t *)mem, len, FALSE); - if(sent == QUICHE_H3_ERR_DONE) { - sent = 0; - } - else if(sent < 0) { - *curlcode = CURLE_SEND_ERROR; - return -1; - } - } - - if(flush_egress(data, sockfd, qs)) { - *curlcode = CURLE_SEND_ERROR; - return -1; - } - - *curlcode = CURLE_OK; - return sent; -} - -/* - * Store quiche version info in this buffer. - */ -void Curl_quic_ver(char *p, size_t len) -{ - (void)msnprintf(p, len, "quiche/%s", quiche_version()); -} - -/* Index where :authority header field will appear in request header - field list. */ -#define AUTHORITY_DST_IDX 3 - -static CURLcode http_request(struct Curl_easy *data, const void *mem, - size_t len) -{ - struct connectdata *conn = data->conn; - struct HTTP *stream = data->req.p.http; - size_t nheader; - int64_t stream3_id; - quiche_h3_header *nva = NULL; - struct quicsocket *qs = conn->quic; - CURLcode result = CURLE_OK; - struct h2h3req *hreq = NULL; - - stream->h3req = TRUE; /* senf off! */ - - result = Curl_pseudo_headers(data, mem, len, &hreq); - if(result) - goto fail; - nheader = hreq->entries; - - nva = malloc(sizeof(quiche_h3_header) * nheader); - if(!nva) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } - else { - unsigned int i; - for(i = 0; i < nheader; i++) { - nva[i].name = (unsigned char *)hreq->header[i].name; - nva[i].name_len = hreq->header[i].namelen; - nva[i].value = (unsigned char *)hreq->header[i].value; - nva[i].value_len = hreq->header[i].valuelen; - } - } - - switch(data->state.httpreq) { - case HTTPREQ_POST: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - case HTTPREQ_PUT: - if(data->state.infilesize != -1) - stream->upload_left = data->state.infilesize; - else - /* data sending without specifying the data amount up front */ - stream->upload_left = -1; /* unknown, but not zero */ - - stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader, - stream->upload_left ? FALSE: TRUE); - if((stream3_id >= 0) && data->set.postfields) { - ssize_t sent = quiche_h3_send_body(qs->h3c, qs->conn, stream3_id, - (uint8_t *)data->set.postfields, - stream->upload_left, TRUE); - if(sent <= 0) { - failf(data, "quiche_h3_send_body failed"); - result = CURLE_SEND_ERROR; - } - stream->upload_left = 0; /* nothing left to send */ - } - break; - default: - stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader, - TRUE); - break; - } - - Curl_safefree(nva); - - if(stream3_id < 0) { - H3BUGF(infof(data, "quiche_h3_send_request returned %d", - stream3_id)); - result = CURLE_SEND_ERROR; - goto fail; - } - - infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)", - stream3_id, (void *)data); - stream->stream3_id = stream3_id; - - Curl_pseudo_free(hreq); - return CURLE_OK; - -fail: - free(nva); - Curl_pseudo_free(hreq); - return result; -} - -/* - * Called from transfer.c:done_sending when we stop HTTP/3 uploading. - */ -CURLcode Curl_quic_done_sending(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - DEBUGASSERT(conn); - if(conn->handler == &Curl_handler_http3) { - /* only for HTTP/3 transfers */ - ssize_t sent; - struct HTTP *stream = data->req.p.http; - struct quicsocket *qs = conn->quic; - stream->upload_done = TRUE; - sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id, - NULL, 0, TRUE); - if(sent < 0) - return CURLE_SEND_ERROR; - } - - return CURLE_OK; -} - -/* - * Called from http.c:Curl_http_done when a request completes. - */ -void Curl_quic_done(struct Curl_easy *data, bool premature) -{ - (void)data; - (void)premature; -} - -/* - * Called from transfer.c:data_pending to know if we should keep looping - * to receive more data from the connection. - */ -bool Curl_quic_data_pending(const struct Curl_easy *data) -{ - (void)data; - return FALSE; -} - -/* - * Called from transfer.c:Curl_readwrite when neither HTTP level read - * nor write is performed. It is a good place to handle timer expiry - * for QUIC transport. - */ -CURLcode Curl_quic_idle(struct Curl_easy *data) -{ - (void)data; - return CURLE_OK; -} - -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_QUICHE
+#include <quiche.h>
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#include "urldata.h"
+#include "sendf.h"
+#include "strdup.h"
+#include "rand.h"
+#include "quic.h"
+#include "strcase.h"
+#include "multiif.h"
+#include "connect.h"
+#include "strerror.h"
+#include "vquic.h"
+#include "transfer.h"
+#include "h2h3.h"
+#include "vtls/openssl.h"
+#include "vtls/keylog.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define DEBUG_HTTP3
+/* #define DEBUG_QUICHE */
+#ifdef DEBUG_HTTP3
+#define H3BUGF(x) x
+#else
+#define H3BUGF(x) do { } while(0)
+#endif
+
+#define QUIC_MAX_STREAMS (256*1024)
+#define QUIC_MAX_DATA (1*1024*1024)
+#define QUIC_IDLE_TIMEOUT (60 * 1000) /* milliseconds */
+
+static CURLcode process_ingress(struct Curl_easy *data,
+ curl_socket_t sockfd,
+ struct quicsocket *qs);
+
+static CURLcode flush_egress(struct Curl_easy *data, curl_socket_t sockfd,
+ struct quicsocket *qs);
+
+static CURLcode http_request(struct Curl_easy *data, const void *mem,
+ size_t len);
+static Curl_recv h3_stream_recv;
+static Curl_send h3_stream_send;
+
+static int quiche_getsock(struct Curl_easy *data,
+ struct connectdata *conn, curl_socket_t *socks)
+{
+ struct SingleRequest *k = &data->req;
+ int bitmap = GETSOCK_BLANK;
+
+ socks[0] = conn->sock[FIRSTSOCKET];
+
+ /* in an HTTP/2 connection we can basically always get a frame so we should
+ always be ready for one */
+ bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
+
+ /* we're still uploading or the HTTP/2 layer wants to send data */
+ if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND)
+ bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
+
+ return bitmap;
+}
+
+static CURLcode qs_disconnect(struct Curl_easy *data,
+ struct quicsocket *qs)
+{
+ DEBUGASSERT(qs);
+ if(qs->conn) {
+ (void)quiche_conn_close(qs->conn, TRUE, 0, NULL, 0);
+ /* flushing the egress is not a failsafe way to deliver all the
+ outstanding packets, but we also don't want to get stuck here... */
+ (void)flush_egress(data, qs->sockfd, qs);
+ quiche_conn_free(qs->conn);
+ qs->conn = NULL;
+ }
+ if(qs->h3config)
+ quiche_h3_config_free(qs->h3config);
+ if(qs->h3c)
+ quiche_h3_conn_free(qs->h3c);
+ if(qs->cfg) {
+ quiche_config_free(qs->cfg);
+ qs->cfg = NULL;
+ }
+ return CURLE_OK;
+}
+
+static CURLcode quiche_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection)
+{
+ struct quicsocket *qs = conn->quic;
+ (void)dead_connection;
+ return qs_disconnect(data, qs);
+}
+
+void Curl_quic_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ int tempindex)
+{
+ if(conn->transport == TRNSPRT_QUIC)
+ qs_disconnect(data, &conn->hequic[tempindex]);
+}
+
+static unsigned int quiche_conncheck(struct Curl_easy *data,
+ struct connectdata *conn,
+ unsigned int checks_to_perform)
+{
+ (void)data;
+ (void)conn;
+ (void)checks_to_perform;
+ return CONNRESULT_NONE;
+}
+
+static CURLcode quiche_do(struct Curl_easy *data, bool *done)
+{
+ struct HTTP *stream = data->req.p.http;
+ stream->h3req = FALSE; /* not sent */
+ return Curl_http(data, done);
+}
+
+static const struct Curl_handler Curl_handler_http3 = {
+ "HTTPS", /* scheme */
+ ZERO_NULL, /* setup_connection */
+ quiche_do, /* do_it */
+ Curl_http_done, /* done */
+ ZERO_NULL, /* do_more */
+ ZERO_NULL, /* connect_it */
+ ZERO_NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ quiche_getsock, /* proto_getsock */
+ quiche_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ quiche_getsock, /* perform_getsock */
+ quiche_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ quiche_conncheck, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_HTTP, /* defport */
+ CURLPROTO_HTTPS, /* protocol */
+ CURLPROTO_HTTP, /* family */
+ PROTOPT_SSL | PROTOPT_STREAM /* flags */
+};
+
+#ifdef DEBUG_QUICHE
+static void quiche_debug_log(const char *line, void *argp)
+{
+ (void)argp;
+ fprintf(stderr, "%s\n", line);
+}
+#endif
+
+static void keylog_callback(const SSL *ssl, const char *line)
+{
+ (void)ssl;
+ Curl_tls_keylog_write_line(line);
+}
+
+static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
+{
+ SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
+
+ SSL_CTX_set_alpn_protos(ssl_ctx,
+ (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL,
+ sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1);
+
+ SSL_CTX_set_default_verify_paths(ssl_ctx);
+
+ /* Open the file if a TLS or QUIC backend has not done this before. */
+ Curl_tls_keylog_open();
+ if(Curl_tls_keylog_enabled()) {
+ SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
+ }
+
+ {
+ struct connectdata *conn = data->conn;
+ if(conn->ssl_config.verifypeer) {
+ const char * const ssl_cafile = conn->ssl_config.CAfile;
+ const char * const ssl_capath = conn->ssl_config.CApath;
+ if(ssl_cafile || ssl_capath) {
+ SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
+ /* tell OpenSSL where to find CA certificates that are used to verify
+ the server's certificate. */
+ if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate verify locations:"
+ " CAfile: %s CApath: %s",
+ ssl_cafile ? ssl_cafile : "none",
+ ssl_capath ? ssl_capath : "none");
+ return NULL;
+ }
+ infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
+ infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
+ }
+#ifdef CURL_CA_FALLBACK
+ else {
+ /* verifying the peer without any CA certificates won't work so
+ use openssl's built-in default as fallback */
+ SSL_CTX_set_default_verify_paths(ssl_ctx);
+ }
+#endif
+ }
+ }
+ return ssl_ctx;
+}
+
+static int quic_init_ssl(struct quicsocket *qs, struct connectdata *conn)
+{
+ /* this will need some attention when HTTPS proxy over QUIC get fixed */
+ const char * const hostname = conn->host.name;
+
+ DEBUGASSERT(!qs->ssl);
+ qs->ssl = SSL_new(qs->sslctx);
+
+ SSL_set_app_data(qs->ssl, qs);
+
+ /* set SNI */
+ SSL_set_tlsext_host_name(qs->ssl, hostname);
+ return 0;
+}
+
+
+CURLcode Curl_quic_connect(struct Curl_easy *data,
+ struct connectdata *conn, curl_socket_t sockfd,
+ int sockindex,
+ const struct sockaddr *addr, socklen_t addrlen)
+{
+ CURLcode result;
+ struct quicsocket *qs = &conn->hequic[sockindex];
+ char ipbuf[40];
+ int port;
+ int rv;
+
+#ifdef DEBUG_QUICHE
+ /* initialize debug log callback only once */
+ static int debug_log_init = 0;
+ if(!debug_log_init) {
+ quiche_enable_debug_logging(quiche_debug_log, NULL);
+ debug_log_init = 1;
+ }
+#endif
+
+ (void)addr;
+ (void)addrlen;
+
+ qs->sockfd = sockfd;
+ qs->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION);
+ if(!qs->cfg) {
+ failf(data, "can't create quiche config");
+ return CURLE_FAILED_INIT;
+ }
+
+ quiche_config_set_max_idle_timeout(qs->cfg, QUIC_IDLE_TIMEOUT);
+ quiche_config_set_initial_max_data(qs->cfg, QUIC_MAX_DATA);
+ quiche_config_set_initial_max_stream_data_bidi_local(qs->cfg, QUIC_MAX_DATA);
+ quiche_config_set_initial_max_stream_data_bidi_remote(qs->cfg,
+ QUIC_MAX_DATA);
+ quiche_config_set_initial_max_stream_data_uni(qs->cfg, QUIC_MAX_DATA);
+ quiche_config_set_initial_max_streams_bidi(qs->cfg, QUIC_MAX_STREAMS);
+ quiche_config_set_initial_max_streams_uni(qs->cfg, QUIC_MAX_STREAMS);
+ quiche_config_set_application_protos(qs->cfg,
+ (uint8_t *)
+ QUICHE_H3_APPLICATION_PROTOCOL,
+ sizeof(QUICHE_H3_APPLICATION_PROTOCOL)
+ - 1);
+
+ qs->sslctx = quic_ssl_ctx(data);
+ if(!qs->sslctx)
+ return CURLE_QUIC_CONNECT_ERROR;
+
+ if(quic_init_ssl(qs, conn))
+ return CURLE_QUIC_CONNECT_ERROR;
+
+ result = Curl_rand(data, qs->scid, sizeof(qs->scid));
+ if(result)
+ return result;
+
+ qs->local_addrlen = sizeof(qs->local_addr);
+ rv = getsockname(sockfd, (struct sockaddr *)&qs->local_addr,
+ &qs->local_addrlen);
+ if(rv == -1)
+ return CURLE_QUIC_CONNECT_ERROR;
+
+ qs->conn = quiche_conn_new_with_tls((const uint8_t *) qs->scid,
+ sizeof(qs->scid), NULL, 0,
+ (struct sockaddr *)&qs->local_addr,
+ qs->local_addrlen, addr, addrlen,
+ qs->cfg, qs->ssl, false);
+ if(!qs->conn) {
+ failf(data, "can't create quiche connection");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* Known to not work on Windows */
+#if !defined(WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD)
+ {
+ int qfd;
+ (void)Curl_qlogdir(data, qs->scid, sizeof(qs->scid), &qfd);
+ if(qfd != -1)
+ quiche_conn_set_qlog_fd(qs->conn, qfd,
+ "qlog title", "curl qlog");
+ }
+#endif
+
+ result = flush_egress(data, sockfd, qs);
+ if(result)
+ return result;
+
+ /* extract the used address as a string */
+ if(!Curl_addr2string((struct sockaddr*)addr, addrlen, ipbuf, &port)) {
+ char buffer[STRERROR_LEN];
+ failf(data, "ssrem inet_ntop() failed with errno %d: %s",
+ SOCKERRNO, Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+
+ infof(data, "Connect socket %d over QUIC to %s:%ld",
+ sockfd, ipbuf, port);
+
+ Curl_persistconninfo(data, conn, NULL, -1);
+
+ {
+ unsigned char alpn_protocols[] = QUICHE_H3_APPLICATION_PROTOCOL;
+ unsigned alpn_len, offset = 0;
+
+ /* Replace each ALPN length prefix by a comma. */
+ while(offset < sizeof(alpn_protocols) - 1) {
+ alpn_len = alpn_protocols[offset];
+ alpn_protocols[offset] = ',';
+ offset += 1 + alpn_len;
+ }
+
+ infof(data, "Sent QUIC client Initial, ALPN: %s",
+ alpn_protocols + 1);
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode quiche_has_connected(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ int tempindex)
+{
+ CURLcode result;
+ struct quicsocket *qs = conn->quic = &conn->hequic[tempindex];
+
+ conn->recv[sockindex] = h3_stream_recv;
+ conn->send[sockindex] = h3_stream_send;
+ conn->handler = &Curl_handler_http3;
+ conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ conn->httpversion = 30;
+ conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+
+ if(conn->ssl_config.verifyhost) {
+ X509 *server_cert;
+ server_cert = SSL_get_peer_certificate(qs->ssl);
+ if(!server_cert) {
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ result = Curl_ossl_verifyhost(data, conn, server_cert);
+ X509_free(server_cert);
+ if(result)
+ return result;
+ infof(data, "Verified certificate just fine");
+ }
+ else
+ infof(data, "Skipped certificate verification");
+
+ qs->h3config = quiche_h3_config_new();
+ if(!qs->h3config)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Create a new HTTP/3 connection on the QUIC connection. */
+ qs->h3c = quiche_h3_conn_new_with_transport(qs->conn, qs->h3config);
+ if(!qs->h3c) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ if(conn->hequic[1-tempindex].cfg) {
+ qs = &conn->hequic[1-tempindex];
+ quiche_config_free(qs->cfg);
+ quiche_conn_free(qs->conn);
+ qs->cfg = NULL;
+ qs->conn = NULL;
+ }
+ if(data->set.ssl.certinfo)
+ /* asked to gather certificate info */
+ (void)Curl_ossl_certchain(data, qs->ssl);
+
+ return CURLE_OK;
+ fail:
+ quiche_h3_config_free(qs->h3config);
+ quiche_h3_conn_free(qs->h3c);
+ return result;
+}
+
+/*
+ * This function gets polled to check if this QUIC connection has connected.
+ */
+CURLcode Curl_quic_is_connected(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ bool *done)
+{
+ CURLcode result;
+ struct quicsocket *qs = &conn->hequic[sockindex];
+ curl_socket_t sockfd = conn->tempsock[sockindex];
+
+ result = process_ingress(data, sockfd, qs);
+ if(result)
+ goto error;
+
+ result = flush_egress(data, sockfd, qs);
+ if(result)
+ goto error;
+
+ if(quiche_conn_is_established(qs->conn)) {
+ *done = TRUE;
+ result = quiche_has_connected(data, conn, 0, sockindex);
+ DEBUGF(infof(data, "quiche established connection"));
+ }
+
+ return result;
+ error:
+ qs_disconnect(data, qs);
+ return result;
+}
+
+static CURLcode process_ingress(struct Curl_easy *data, int sockfd,
+ struct quicsocket *qs)
+{
+ ssize_t recvd;
+ uint8_t *buf = (uint8_t *)data->state.buffer;
+ size_t bufsize = data->set.buffer_size;
+ struct sockaddr_storage from;
+ socklen_t from_len;
+ quiche_recv_info recv_info;
+
+ DEBUGASSERT(qs->conn);
+
+ /* in case the timeout expired */
+ quiche_conn_on_timeout(qs->conn);
+
+ do {
+ from_len = sizeof(from);
+
+ recvd = recvfrom(sockfd, buf, bufsize, 0,
+ (struct sockaddr *)&from, &from_len);
+
+ if((recvd < 0) && ((SOCKERRNO == EAGAIN) || (SOCKERRNO == EWOULDBLOCK)))
+ break;
+
+ if(recvd < 0) {
+ failf(data, "quiche: recvfrom() unexpectedly returned %zd "
+ "(errno: %d, socket %d)", recvd, SOCKERRNO, sockfd);
+ return CURLE_RECV_ERROR;
+ }
+
+ recv_info.from = (struct sockaddr *) &from;
+ recv_info.from_len = from_len;
+ recv_info.to = (struct sockaddr *) &qs->local_addr;
+ recv_info.to_len = qs->local_addrlen;
+
+ recvd = quiche_conn_recv(qs->conn, buf, recvd, &recv_info);
+ if(recvd == QUICHE_ERR_DONE)
+ break;
+
+ if(recvd < 0) {
+ if(QUICHE_ERR_TLS_FAIL == recvd) {
+ long verify_ok = SSL_get_verify_result(qs->ssl);
+ if(verify_ok != X509_V_OK) {
+ failf(data, "SSL certificate problem: %s",
+ X509_verify_cert_error_string(verify_ok));
+
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ }
+
+ failf(data, "quiche_conn_recv() == %zd", recvd);
+
+ return CURLE_RECV_ERROR;
+ }
+ } while(1);
+
+ return CURLE_OK;
+}
+
+/*
+ * flush_egress drains the buffers and sends off data.
+ * Calls failf() on errors.
+ */
+static CURLcode flush_egress(struct Curl_easy *data, int sockfd,
+ struct quicsocket *qs)
+{
+ ssize_t sent;
+ uint8_t out[1200];
+ int64_t timeout_ns;
+ quiche_send_info send_info;
+
+ do {
+ sent = quiche_conn_send(qs->conn, out, sizeof(out), &send_info);
+ if(sent == QUICHE_ERR_DONE)
+ break;
+
+ if(sent < 0) {
+ failf(data, "quiche_conn_send returned %zd", sent);
+ return CURLE_SEND_ERROR;
+ }
+
+ sent = send(sockfd, out, sent, 0);
+ if(sent < 0) {
+ failf(data, "send() returned %zd", sent);
+ return CURLE_SEND_ERROR;
+ }
+ } while(1);
+
+ /* time until the next timeout event, as nanoseconds. */
+ timeout_ns = quiche_conn_timeout_as_nanos(qs->conn);
+ if(timeout_ns)
+ /* expire uses milliseconds */
+ Curl_expire(data, (timeout_ns + 999999) / 1000000, EXPIRE_QUIC);
+
+ return CURLE_OK;
+}
+
+struct h3h1header {
+ char *dest;
+ size_t destlen; /* left to use */
+ size_t nlen; /* used */
+};
+
+static int cb_each_header(uint8_t *name, size_t name_len,
+ uint8_t *value, size_t value_len,
+ void *argp)
+{
+ struct h3h1header *headers = (struct h3h1header *)argp;
+ size_t olen = 0;
+
+ if((name_len == 7) && !strncmp(H2H3_PSEUDO_STATUS, (char *)name, 7)) {
+ msnprintf(headers->dest,
+ headers->destlen, "HTTP/3 %.*s\n",
+ (int) value_len, value);
+ }
+ else if(!headers->nlen) {
+ return CURLE_HTTP3;
+ }
+ else {
+ msnprintf(headers->dest,
+ headers->destlen, "%.*s: %.*s\n",
+ (int)name_len, name, (int) value_len, value);
+ }
+ olen = strlen(headers->dest);
+ headers->destlen -= olen;
+ headers->nlen += olen;
+ headers->dest += olen;
+ return 0;
+}
+
+static ssize_t h3_stream_recv(struct Curl_easy *data,
+ int sockindex,
+ char *buf,
+ size_t buffersize,
+ CURLcode *curlcode)
+{
+ ssize_t recvd = -1;
+ ssize_t rcode;
+ struct connectdata *conn = data->conn;
+ struct quicsocket *qs = conn->quic;
+ curl_socket_t sockfd = conn->sock[sockindex];
+ quiche_h3_event *ev;
+ int rc;
+ struct h3h1header headers;
+ struct HTTP *stream = data->req.p.http;
+ headers.dest = buf;
+ headers.destlen = buffersize;
+ headers.nlen = 0;
+
+ if(process_ingress(data, sockfd, qs)) {
+ infof(data, "h3_stream_recv returns on ingress");
+ *curlcode = CURLE_RECV_ERROR;
+ return -1;
+ }
+
+ if(qs->h3_recving) {
+ /* body receiving state */
+ rcode = quiche_h3_recv_body(qs->h3c, qs->conn, stream->stream3_id,
+ (unsigned char *)buf, buffersize);
+ if(rcode <= 0) {
+ recvd = -1;
+ qs->h3_recving = FALSE;
+ /* fall through into the while loop below */
+ }
+ else
+ recvd = rcode;
+ }
+
+ while(recvd < 0) {
+ int64_t s = quiche_h3_conn_poll(qs->h3c, qs->conn, &ev);
+ if(s < 0)
+ /* nothing more to do */
+ break;
+
+ if(s != stream->stream3_id) {
+ /* another transfer, ignore for now */
+ infof(data, "Got h3 for stream %u, expects %u",
+ s, stream->stream3_id);
+ continue;
+ }
+
+ switch(quiche_h3_event_type(ev)) {
+ case QUICHE_H3_EVENT_HEADERS:
+ rc = quiche_h3_event_for_each_header(ev, cb_each_header, &headers);
+ if(rc) {
+ *curlcode = rc;
+ failf(data, "Error in HTTP/3 response header");
+ break;
+ }
+ recvd = headers.nlen;
+ break;
+ case QUICHE_H3_EVENT_DATA:
+ if(!stream->firstbody) {
+ /* add a header-body separator CRLF */
+ buf[0] = '\r';
+ buf[1] = '\n';
+ buf += 2;
+ buffersize -= 2;
+ stream->firstbody = TRUE;
+ recvd = 2; /* two bytes already */
+ }
+ else
+ recvd = 0;
+ rcode = quiche_h3_recv_body(qs->h3c, qs->conn, s, (unsigned char *)buf,
+ buffersize);
+ if(rcode <= 0) {
+ recvd = -1;
+ break;
+ }
+ qs->h3_recving = TRUE;
+ recvd += rcode;
+ break;
+
+ case QUICHE_H3_EVENT_RESET:
+ streamclose(conn, "Stream reset");
+ *curlcode = CURLE_PARTIAL_FILE;
+ return -1;
+
+ case QUICHE_H3_EVENT_FINISHED:
+ streamclose(conn, "End of stream");
+ recvd = 0; /* end of stream */
+ break;
+ default:
+ break;
+ }
+
+ quiche_h3_event_free(ev);
+ }
+ if(flush_egress(data, sockfd, qs)) {
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+
+ *curlcode = (-1 == recvd)? CURLE_AGAIN : CURLE_OK;
+ if(recvd >= 0)
+ /* Get this called again to drain the event queue */
+ Curl_expire(data, 0, EXPIRE_QUIC);
+
+ data->state.drain = (recvd >= 0) ? 1 : 0;
+ return recvd;
+}
+
+static ssize_t h3_stream_send(struct Curl_easy *data,
+ int sockindex,
+ const void *mem,
+ size_t len,
+ CURLcode *curlcode)
+{
+ ssize_t sent;
+ struct connectdata *conn = data->conn;
+ struct quicsocket *qs = conn->quic;
+ curl_socket_t sockfd = conn->sock[sockindex];
+ struct HTTP *stream = data->req.p.http;
+
+ if(!stream->h3req) {
+ CURLcode result = http_request(data, mem, len);
+ if(result) {
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+ sent = len;
+ }
+ else {
+ sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id,
+ (uint8_t *)mem, len, FALSE);
+ if(sent == QUICHE_H3_ERR_DONE) {
+ sent = 0;
+ }
+ else if(sent < 0) {
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+ }
+
+ if(flush_egress(data, sockfd, qs)) {
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+
+ *curlcode = CURLE_OK;
+ return sent;
+}
+
+/*
+ * Store quiche version info in this buffer.
+ */
+void Curl_quic_ver(char *p, size_t len)
+{
+ (void)msnprintf(p, len, "quiche/%s", quiche_version());
+}
+
+/* Index where :authority header field will appear in request header
+ field list. */
+#define AUTHORITY_DST_IDX 3
+
+static CURLcode http_request(struct Curl_easy *data, const void *mem,
+ size_t len)
+{
+ struct connectdata *conn = data->conn;
+ struct HTTP *stream = data->req.p.http;
+ size_t nheader;
+ int64_t stream3_id;
+ quiche_h3_header *nva = NULL;
+ struct quicsocket *qs = conn->quic;
+ CURLcode result = CURLE_OK;
+ struct h2h3req *hreq = NULL;
+
+ stream->h3req = TRUE; /* senf off! */
+
+ result = Curl_pseudo_headers(data, mem, len, &hreq);
+ if(result)
+ goto fail;
+ nheader = hreq->entries;
+
+ nva = malloc(sizeof(quiche_h3_header) * nheader);
+ if(!nva) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ else {
+ unsigned int i;
+ for(i = 0; i < nheader; i++) {
+ nva[i].name = (unsigned char *)hreq->header[i].name;
+ nva[i].name_len = hreq->header[i].namelen;
+ nva[i].value = (unsigned char *)hreq->header[i].value;
+ nva[i].value_len = hreq->header[i].valuelen;
+ }
+ }
+
+ switch(data->state.httpreq) {
+ case HTTPREQ_POST:
+ case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
+ case HTTPREQ_PUT:
+ if(data->state.infilesize != -1)
+ stream->upload_left = data->state.infilesize;
+ else
+ /* data sending without specifying the data amount up front */
+ stream->upload_left = -1; /* unknown, but not zero */
+
+ stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader,
+ stream->upload_left ? FALSE: TRUE);
+ if((stream3_id >= 0) && data->set.postfields) {
+ ssize_t sent = quiche_h3_send_body(qs->h3c, qs->conn, stream3_id,
+ (uint8_t *)data->set.postfields,
+ stream->upload_left, TRUE);
+ if(sent <= 0) {
+ failf(data, "quiche_h3_send_body failed");
+ result = CURLE_SEND_ERROR;
+ }
+ stream->upload_left = 0; /* nothing left to send */
+ }
+ break;
+ default:
+ stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader,
+ TRUE);
+ break;
+ }
+
+ Curl_safefree(nva);
+
+ if(stream3_id < 0) {
+ H3BUGF(infof(data, "quiche_h3_send_request returned %d",
+ stream3_id));
+ result = CURLE_SEND_ERROR;
+ goto fail;
+ }
+
+ infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)",
+ stream3_id, (void *)data);
+ stream->stream3_id = stream3_id;
+
+ Curl_pseudo_free(hreq);
+ return CURLE_OK;
+
+fail:
+ free(nva);
+ Curl_pseudo_free(hreq);
+ return result;
+}
+
+/*
+ * Called from transfer.c:done_sending when we stop HTTP/3 uploading.
+ */
+CURLcode Curl_quic_done_sending(struct Curl_easy *data)
+{
+ struct connectdata *conn = data->conn;
+ DEBUGASSERT(conn);
+ if(conn->handler == &Curl_handler_http3) {
+ /* only for HTTP/3 transfers */
+ ssize_t sent;
+ struct HTTP *stream = data->req.p.http;
+ struct quicsocket *qs = conn->quic;
+ stream->upload_done = TRUE;
+ sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id,
+ NULL, 0, TRUE);
+ if(sent < 0)
+ return CURLE_SEND_ERROR;
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Called from http.c:Curl_http_done when a request completes.
+ */
+void Curl_quic_done(struct Curl_easy *data, bool premature)
+{
+ (void)data;
+ (void)premature;
+}
+
+/*
+ * Called from transfer.c:data_pending to know if we should keep looping
+ * to receive more data from the connection.
+ */
+bool Curl_quic_data_pending(const struct Curl_easy *data)
+{
+ (void)data;
+ return FALSE;
+}
+
+/*
+ * Called from transfer.c:Curl_readwrite when neither HTTP level read
+ * nor write is performed. It is a good place to handle timer expiry
+ * for QUIC transport.
+ */
+CURLcode Curl_quic_idle(struct Curl_easy *data)
+{
+ (void)data;
+ return CURLE_OK;
+}
+
+#endif
diff --git a/libs/libcurl/src/vquic/quiche.h b/libs/libcurl/src/vquic/quiche.h index 2da65f5f48..79eb9bcff5 100644 --- a/libs/libcurl/src/vquic/quiche.h +++ b/libs/libcurl/src/vquic/quiche.h @@ -1,58 +1,58 @@ -#ifndef HEADER_CURL_VQUIC_QUICHE_H -#define HEADER_CURL_VQUIC_QUICHE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_QUICHE - -#include <quiche.h> -#include <openssl/ssl.h> - -struct quic_handshake { - char *buf; /* pointer to the buffer */ - size_t alloclen; /* size of allocation */ - size_t len; /* size of content in buffer */ - size_t nread; /* how many bytes have been read */ -}; - -struct quicsocket { - quiche_config *cfg; - quiche_conn *conn; - quiche_h3_conn *h3c; - quiche_h3_config *h3config; - uint8_t scid[QUICHE_MAX_CONN_ID_LEN]; - curl_socket_t sockfd; - uint32_t version; - SSL_CTX *sslctx; - SSL *ssl; - bool h3_recving; /* TRUE when in h3-body-reading state */ - struct sockaddr_storage local_addr; - socklen_t local_addrlen; -}; - -#endif - -#endif /* HEADER_CURL_VQUIC_QUICHE_H */ +#ifndef HEADER_CURL_VQUIC_QUICHE_H
+#define HEADER_CURL_VQUIC_QUICHE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_QUICHE
+
+#include <quiche.h>
+#include <openssl/ssl.h>
+
+struct quic_handshake {
+ char *buf; /* pointer to the buffer */
+ size_t alloclen; /* size of allocation */
+ size_t len; /* size of content in buffer */
+ size_t nread; /* how many bytes have been read */
+};
+
+struct quicsocket {
+ quiche_config *cfg;
+ quiche_conn *conn;
+ quiche_h3_conn *h3c;
+ quiche_h3_config *h3config;
+ uint8_t scid[QUICHE_MAX_CONN_ID_LEN];
+ curl_socket_t sockfd;
+ uint32_t version;
+ SSL_CTX *sslctx;
+ SSL *ssl;
+ bool h3_recving; /* TRUE when in h3-body-reading state */
+ struct sockaddr_storage local_addr;
+ socklen_t local_addrlen;
+};
+
+#endif
+
+#endif /* HEADER_CURL_VQUIC_QUICHE_H */
diff --git a/libs/libcurl/src/vquic/vquic.c b/libs/libcurl/src/vquic/vquic.c index e52a4f301d..0bb7a5253e 100644 --- a/libs/libcurl/src/vquic/vquic.c +++ b/libs/libcurl/src/vquic/vquic.c @@ -1,87 +1,87 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 ENABLE_QUIC - -#ifdef HAVE_FCNTL_H -#include <fcntl.h> -#endif -#include "urldata.h" -#include "dynbuf.h" -#include "curl_printf.h" -#include "vquic.h" - -#ifdef O_BINARY -#define QLOGMODE O_WRONLY|O_CREAT|O_BINARY -#else -#define QLOGMODE O_WRONLY|O_CREAT -#endif - -/* - * If the QLOGDIR environment variable is set, open and return a file - * descriptor to write the log to. - * - * This function returns error if something failed outside of failing to - * create the file. Open file success is deemed by seeing if the returned fd - * is != -1. - */ -CURLcode Curl_qlogdir(struct Curl_easy *data, - unsigned char *scid, - size_t scidlen, - int *qlogfdp) -{ - const char *qlog_dir = getenv("QLOGDIR"); - *qlogfdp = -1; - if(qlog_dir) { - struct dynbuf fname; - CURLcode result; - unsigned int i; - Curl_dyn_init(&fname, DYN_QLOG_NAME); - result = Curl_dyn_add(&fname, qlog_dir); - if(!result) - result = Curl_dyn_add(&fname, "/"); - for(i = 0; (i < scidlen) && !result; i++) { - char hex[3]; - msnprintf(hex, 3, "%02x", scid[i]); - result = Curl_dyn_add(&fname, hex); - } - if(!result) - result = Curl_dyn_add(&fname, ".sqlog"); - - if(!result) { - int qlogfd = open(Curl_dyn_ptr(&fname), QLOGMODE, - data->set.new_file_perms); - if(qlogfd != -1) - *qlogfdp = qlogfd; - } - Curl_dyn_free(&fname); - if(result) - return result; - } - - return CURLE_OK; -} -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 ENABLE_QUIC
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include "urldata.h"
+#include "dynbuf.h"
+#include "curl_printf.h"
+#include "vquic.h"
+
+#ifdef O_BINARY
+#define QLOGMODE O_WRONLY|O_CREAT|O_BINARY
+#else
+#define QLOGMODE O_WRONLY|O_CREAT
+#endif
+
+/*
+ * If the QLOGDIR environment variable is set, open and return a file
+ * descriptor to write the log to.
+ *
+ * This function returns error if something failed outside of failing to
+ * create the file. Open file success is deemed by seeing if the returned fd
+ * is != -1.
+ */
+CURLcode Curl_qlogdir(struct Curl_easy *data,
+ unsigned char *scid,
+ size_t scidlen,
+ int *qlogfdp)
+{
+ const char *qlog_dir = getenv("QLOGDIR");
+ *qlogfdp = -1;
+ if(qlog_dir) {
+ struct dynbuf fname;
+ CURLcode result;
+ unsigned int i;
+ Curl_dyn_init(&fname, DYN_QLOG_NAME);
+ result = Curl_dyn_add(&fname, qlog_dir);
+ if(!result)
+ result = Curl_dyn_add(&fname, "/");
+ for(i = 0; (i < scidlen) && !result; i++) {
+ char hex[3];
+ msnprintf(hex, 3, "%02x", scid[i]);
+ result = Curl_dyn_add(&fname, hex);
+ }
+ if(!result)
+ result = Curl_dyn_add(&fname, ".sqlog");
+
+ if(!result) {
+ int qlogfd = open(Curl_dyn_ptr(&fname), QLOGMODE,
+ data->set.new_file_perms);
+ if(qlogfd != -1)
+ *qlogfdp = qlogfd;
+ }
+ Curl_dyn_free(&fname);
+ if(result)
+ return result;
+ }
+
+ return CURLE_OK;
+}
+#endif
diff --git a/libs/libcurl/src/vquic/vquic.h b/libs/libcurl/src/vquic/vquic.h index 8f599a8f49..958c587f92 100644 --- a/libs/libcurl/src/vquic/vquic.h +++ b/libs/libcurl/src/vquic/vquic.h @@ -1,36 +1,36 @@ -#ifndef HEADER_CURL_VQUIC_QUIC_H -#define HEADER_CURL_VQUIC_QUIC_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 ENABLE_QUIC -CURLcode Curl_qlogdir(struct Curl_easy *data, - unsigned char *scid, - size_t scidlen, - int *qlogfdp); -#endif - -#endif /* HEADER_CURL_VQUIC_QUIC_H */ +#ifndef HEADER_CURL_VQUIC_QUIC_H
+#define HEADER_CURL_VQUIC_QUIC_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 ENABLE_QUIC
+CURLcode Curl_qlogdir(struct Curl_easy *data,
+ unsigned char *scid,
+ size_t scidlen,
+ int *qlogfdp);
+#endif
+
+#endif /* HEADER_CURL_VQUIC_QUIC_H */
diff --git a/libs/libcurl/src/vssh/libssh.c b/libs/libcurl/src/vssh/libssh.c index 0105e4079c..b067b601ea 100644 --- a/libs/libcurl/src/vssh/libssh.c +++ b/libs/libcurl/src/vssh/libssh.c @@ -1,2974 +1,2968 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2017 - 2022 Red Hat, Inc. - * - * Authors: Nikos Mavrogiannopoulos, Tomas Mraz, Stanislav Zidek, - * Robert Kolcun, Andreas Schneider - * - * 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_LIBSSH - -#include <limits.h> - -#include <libssh/libssh.h> -#include <libssh/sftp.h> - -#ifdef HAVE_NETINET_IN_H -#include <netinet/in.h> -#endif -#ifdef HAVE_ARPA_INET_H -#include <arpa/inet.h> -#endif -#ifdef HAVE_UTSNAME_H -#include <sys/utsname.h> -#endif -#ifdef HAVE_NETDB_H -#include <netdb.h> -#endif -#ifdef __VMS -#include <in.h> -#include <inet.h> -#endif - -#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) -#undef in_addr_t -#define in_addr_t unsigned long -#endif - -#include <curl/curl.h> -#include "urldata.h" -#include "sendf.h" -#include "hostip.h" -#include "progress.h" -#include "transfer.h" -#include "escape.h" -#include "http.h" /* for HTTP proxy tunnel stuff */ -#include "ssh.h" -#include "url.h" -#include "speedcheck.h" -#include "getinfo.h" -#include "strdup.h" -#include "strcase.h" -#include "vtls/vtls.h" -#include "connect.h" -#include "inet_ntop.h" -#include "parsedate.h" /* for the week day and month names */ -#include "sockaddr.h" /* required for Curl_sockaddr_storage */ -#include "strtoofft.h" -#include "multiif.h" -#include "select.h" -#include "warnless.h" -#include "curl_path.h" - -#ifdef HAVE_SYS_STAT_H -#include <sys/stat.h> -#endif -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif -#ifdef HAVE_FCNTL_H -#include <fcntl.h> -#endif - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* in 0.10.0 or later, ignore deprecated warnings */ -#if defined(__GNUC__) && \ - (LIBSSH_VERSION_MINOR >= 10) || \ - (LIBSSH_VERSION_MAJOR > 0) -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif - -/* A recent macro provided by libssh. Or make our own. */ -#ifndef SSH_STRING_FREE_CHAR -#define SSH_STRING_FREE_CHAR(x) \ - do { \ - if(x) { \ - ssh_string_free_char(x); \ - x = NULL; \ - } \ - } while(0) -#endif - -/* These stat values may not be the same as the user's S_IFMT / S_IFLNK */ -#ifndef SSH_S_IFMT -#define SSH_S_IFMT 00170000 -#endif -#ifndef SSH_S_IFLNK -#define SSH_S_IFLNK 0120000 -#endif - -/* Local functions: */ -static CURLcode myssh_connect(struct Curl_easy *data, bool *done); -static CURLcode myssh_multi_statemach(struct Curl_easy *data, - bool *done); -static CURLcode myssh_do_it(struct Curl_easy *data, bool *done); - -static CURLcode scp_done(struct Curl_easy *data, - CURLcode, bool premature); -static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done); -static CURLcode scp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection); - -static CURLcode sftp_done(struct Curl_easy *data, - CURLcode, bool premature); -static CURLcode sftp_doing(struct Curl_easy *data, - bool *dophase_done); -static CURLcode sftp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead); -static -CURLcode sftp_perform(struct Curl_easy *data, - bool *connected, - bool *dophase_done); - -static void sftp_quote(struct Curl_easy *data); -static void sftp_quote_stat(struct Curl_easy *data); -static int myssh_getsock(struct Curl_easy *data, - struct connectdata *conn, curl_socket_t *sock); - -static CURLcode myssh_setup_connection(struct Curl_easy *data, - struct connectdata *conn); - -/* - * SCP protocol handler. - */ - -const struct Curl_handler Curl_handler_scp = { - "SCP", /* scheme */ - myssh_setup_connection, /* setup_connection */ - myssh_do_it, /* do_it */ - scp_done, /* done */ - ZERO_NULL, /* do_more */ - myssh_connect, /* connect_it */ - myssh_multi_statemach, /* connecting */ - scp_doing, /* doing */ - myssh_getsock, /* proto_getsock */ - myssh_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - myssh_getsock, /* perform_getsock */ - scp_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_SSH, /* defport */ - CURLPROTO_SCP, /* protocol */ - CURLPROTO_SCP, /* family */ - PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */ -}; - -/* - * SFTP protocol handler. - */ - -const struct Curl_handler Curl_handler_sftp = { - "SFTP", /* scheme */ - myssh_setup_connection, /* setup_connection */ - myssh_do_it, /* do_it */ - sftp_done, /* done */ - ZERO_NULL, /* do_more */ - myssh_connect, /* connect_it */ - myssh_multi_statemach, /* connecting */ - sftp_doing, /* doing */ - myssh_getsock, /* proto_getsock */ - myssh_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - myssh_getsock, /* perform_getsock */ - sftp_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_SSH, /* defport */ - CURLPROTO_SFTP, /* protocol */ - CURLPROTO_SFTP, /* family */ - PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION - | PROTOPT_NOURLQUERY /* flags */ -}; - -static CURLcode sftp_error_to_CURLE(int err) -{ - switch(err) { - case SSH_FX_OK: - return CURLE_OK; - - case SSH_FX_NO_SUCH_FILE: - case SSH_FX_NO_SUCH_PATH: - return CURLE_REMOTE_FILE_NOT_FOUND; - - case SSH_FX_PERMISSION_DENIED: - case SSH_FX_WRITE_PROTECT: - return CURLE_REMOTE_ACCESS_DENIED; - - case SSH_FX_FILE_ALREADY_EXISTS: - return CURLE_REMOTE_FILE_EXISTS; - - default: - break; - } - - return CURLE_SSH; -} - -#ifndef DEBUGBUILD -#define state(x,y) mystate(x,y) -#else -#define state(x,y) mystate(x,y, __LINE__) -#endif - -/* - * SSH State machine related code - */ -/* This is the ONLY way to change SSH state! */ -static void mystate(struct Curl_easy *data, sshstate nowstate -#ifdef DEBUGBUILD - , int lineno -#endif - ) -{ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - /* for debug purposes */ - static const char *const names[] = { - "SSH_STOP", - "SSH_INIT", - "SSH_S_STARTUP", - "SSH_HOSTKEY", - "SSH_AUTHLIST", - "SSH_AUTH_PKEY_INIT", - "SSH_AUTH_PKEY", - "SSH_AUTH_PASS_INIT", - "SSH_AUTH_PASS", - "SSH_AUTH_AGENT_INIT", - "SSH_AUTH_AGENT_LIST", - "SSH_AUTH_AGENT", - "SSH_AUTH_HOST_INIT", - "SSH_AUTH_HOST", - "SSH_AUTH_KEY_INIT", - "SSH_AUTH_KEY", - "SSH_AUTH_GSSAPI", - "SSH_AUTH_DONE", - "SSH_SFTP_INIT", - "SSH_SFTP_REALPATH", - "SSH_SFTP_QUOTE_INIT", - "SSH_SFTP_POSTQUOTE_INIT", - "SSH_SFTP_QUOTE", - "SSH_SFTP_NEXT_QUOTE", - "SSH_SFTP_QUOTE_STAT", - "SSH_SFTP_QUOTE_SETSTAT", - "SSH_SFTP_QUOTE_SYMLINK", - "SSH_SFTP_QUOTE_MKDIR", - "SSH_SFTP_QUOTE_RENAME", - "SSH_SFTP_QUOTE_RMDIR", - "SSH_SFTP_QUOTE_UNLINK", - "SSH_SFTP_QUOTE_STATVFS", - "SSH_SFTP_GETINFO", - "SSH_SFTP_FILETIME", - "SSH_SFTP_TRANS_INIT", - "SSH_SFTP_UPLOAD_INIT", - "SSH_SFTP_CREATE_DIRS_INIT", - "SSH_SFTP_CREATE_DIRS", - "SSH_SFTP_CREATE_DIRS_MKDIR", - "SSH_SFTP_READDIR_INIT", - "SSH_SFTP_READDIR", - "SSH_SFTP_READDIR_LINK", - "SSH_SFTP_READDIR_BOTTOM", - "SSH_SFTP_READDIR_DONE", - "SSH_SFTP_DOWNLOAD_INIT", - "SSH_SFTP_DOWNLOAD_STAT", - "SSH_SFTP_CLOSE", - "SSH_SFTP_SHUTDOWN", - "SSH_SCP_TRANS_INIT", - "SSH_SCP_UPLOAD_INIT", - "SSH_SCP_DOWNLOAD_INIT", - "SSH_SCP_DOWNLOAD", - "SSH_SCP_DONE", - "SSH_SCP_SEND_EOF", - "SSH_SCP_WAIT_EOF", - "SSH_SCP_WAIT_CLOSE", - "SSH_SCP_CHANNEL_FREE", - "SSH_SESSION_DISCONNECT", - "SSH_SESSION_FREE", - "QUIT" - }; - - - if(sshc->state != nowstate) { - infof(data, "SSH %p state change from %s to %s (line %d)", - (void *) sshc, names[sshc->state], names[nowstate], - lineno); - } -#endif - - sshc->state = nowstate; -} - -/* Multiple options: - * 1. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] is set with an MD5 - * hash (90s style auth, not sure we should have it here) - * 2. data->set.ssh_keyfunc callback is set. Then we do trust on first - * use. We even save on knownhosts if CURLKHSTAT_FINE_ADD_TO_FILE - * is returned by it. - * 3. none of the above. We only accept if it is present on known hosts. - * - * Returns SSH_OK or SSH_ERROR. - */ -static int myssh_is_known(struct Curl_easy *data) -{ - int rc; - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - ssh_key pubkey; - size_t hlen; - unsigned char *hash = NULL; - char *found_base64 = NULL; - char *known_base64 = NULL; - int vstate; - enum curl_khmatch keymatch; - struct curl_khkey foundkey; - struct curl_khkey *knownkeyp = NULL; - curl_sshkeycallback func = - data->set.ssh_keyfunc; - -#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0) - struct ssh_knownhosts_entry *knownhostsentry = NULL; - struct curl_khkey knownkey; -#endif - -#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0) - rc = ssh_get_server_publickey(sshc->ssh_session, &pubkey); -#else - rc = ssh_get_publickey(sshc->ssh_session, &pubkey); -#endif - if(rc != SSH_OK) - return rc; - - if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) { - int i; - char md5buffer[33]; - const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]; - - rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, - &hash, &hlen); - if(rc != SSH_OK || hlen != 16) { - failf(data, - "Denied establishing ssh session: md5 fingerprint not available"); - goto cleanup; - } - - for(i = 0; i < 16; i++) - msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char)hash[i]); - - infof(data, "SSH MD5 fingerprint: %s", md5buffer); - - if(!strcasecompare(md5buffer, pubkey_md5)) { - failf(data, - "Denied establishing ssh session: mismatch md5 fingerprint. " - "Remote %s is not equal to %s", md5buffer, pubkey_md5); - rc = SSH_ERROR; - goto cleanup; - } - - rc = SSH_OK; - goto cleanup; - } - - if(data->set.ssl.primary.verifyhost != TRUE) { - rc = SSH_OK; - goto cleanup; - } - -#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0) - /* Get the known_key from the known hosts file */ - vstate = ssh_session_get_known_hosts_entry(sshc->ssh_session, - &knownhostsentry); - - /* Case an entry was found in a known hosts file */ - if(knownhostsentry) { - if(knownhostsentry->publickey) { - rc = ssh_pki_export_pubkey_base64(knownhostsentry->publickey, - &known_base64); - if(rc != SSH_OK) { - goto cleanup; - } - knownkey.key = known_base64; - knownkey.len = strlen(known_base64); - - switch(ssh_key_type(knownhostsentry->publickey)) { - case SSH_KEYTYPE_RSA: - knownkey.keytype = CURLKHTYPE_RSA; - break; - case SSH_KEYTYPE_RSA1: - knownkey.keytype = CURLKHTYPE_RSA1; - break; - case SSH_KEYTYPE_ECDSA: - case SSH_KEYTYPE_ECDSA_P256: - case SSH_KEYTYPE_ECDSA_P384: - case SSH_KEYTYPE_ECDSA_P521: - knownkey.keytype = CURLKHTYPE_ECDSA; - break; - case SSH_KEYTYPE_ED25519: - knownkey.keytype = CURLKHTYPE_ED25519; - break; - case SSH_KEYTYPE_DSS: - knownkey.keytype = CURLKHTYPE_DSS; - break; - default: - rc = SSH_ERROR; - goto cleanup; - } - knownkeyp = &knownkey; - } - } - - switch(vstate) { - case SSH_KNOWN_HOSTS_OK: - keymatch = CURLKHMATCH_OK; - break; - case SSH_KNOWN_HOSTS_OTHER: - /* fallthrough */ - case SSH_KNOWN_HOSTS_NOT_FOUND: - /* fallthrough */ - case SSH_KNOWN_HOSTS_UNKNOWN: - /* fallthrough */ - case SSH_KNOWN_HOSTS_ERROR: - keymatch = CURLKHMATCH_MISSING; - break; - default: - keymatch = CURLKHMATCH_MISMATCH; - break; - } - -#else - vstate = ssh_is_server_known(sshc->ssh_session); - switch(vstate) { - case SSH_SERVER_KNOWN_OK: - keymatch = CURLKHMATCH_OK; - break; - case SSH_SERVER_FILE_NOT_FOUND: - /* fallthrough */ - case SSH_SERVER_NOT_KNOWN: - keymatch = CURLKHMATCH_MISSING; - break; - default: - keymatch = CURLKHMATCH_MISMATCH; - break; - } -#endif - - if(func) { /* use callback to determine action */ - rc = ssh_pki_export_pubkey_base64(pubkey, &found_base64); - if(rc != SSH_OK) - goto cleanup; - - foundkey.key = found_base64; - foundkey.len = strlen(found_base64); - - switch(ssh_key_type(pubkey)) { - case SSH_KEYTYPE_RSA: - foundkey.keytype = CURLKHTYPE_RSA; - break; - case SSH_KEYTYPE_RSA1: - foundkey.keytype = CURLKHTYPE_RSA1; - break; - case SSH_KEYTYPE_ECDSA: -#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0) - case SSH_KEYTYPE_ECDSA_P256: - case SSH_KEYTYPE_ECDSA_P384: - case SSH_KEYTYPE_ECDSA_P521: -#endif - foundkey.keytype = CURLKHTYPE_ECDSA; - break; -#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,7,0) - case SSH_KEYTYPE_ED25519: - foundkey.keytype = CURLKHTYPE_ED25519; - break; -#endif - case SSH_KEYTYPE_DSS: - foundkey.keytype = CURLKHTYPE_DSS; - break; - default: - rc = SSH_ERROR; - goto cleanup; - } - - Curl_set_in_callback(data, true); - rc = func(data, knownkeyp, /* from the knownhosts file */ - &foundkey, /* from the remote host */ - keymatch, data->set.ssh_keyfunc_userp); - Curl_set_in_callback(data, false); - - switch(rc) { - case CURLKHSTAT_FINE_ADD_TO_FILE: -#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0) - rc = ssh_session_update_known_hosts(sshc->ssh_session); -#else - rc = ssh_write_knownhost(sshc->ssh_session); -#endif - if(rc != SSH_OK) { - goto cleanup; - } - break; - case CURLKHSTAT_FINE: - break; - default: /* REJECT/DEFER */ - rc = SSH_ERROR; - goto cleanup; - } - } - else { - if(keymatch != CURLKHMATCH_OK) { - rc = SSH_ERROR; - goto cleanup; - } - } - rc = SSH_OK; - -cleanup: - if(found_base64) { - (free)(found_base64); - } - if(known_base64) { - (free)(known_base64); - } - if(hash) - ssh_clean_pubkey_hash(&hash); - ssh_key_free(pubkey); -#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0) - if(knownhostsentry) { - ssh_knownhosts_entry_free(knownhostsentry); - } -#endif - return rc; -} - -#define MOVE_TO_ERROR_STATE(_r) do { \ - state(data, SSH_SESSION_DISCONNECT); \ - sshc->actualcode = _r; \ - rc = SSH_ERROR; \ - } while(0) - -#define MOVE_TO_SFTP_CLOSE_STATE() do { \ - state(data, SSH_SFTP_CLOSE); \ - sshc->actualcode = \ - sftp_error_to_CURLE(sftp_get_error(sshc->sftp_session)); \ - rc = SSH_ERROR; \ - } while(0) - -#define MOVE_TO_LAST_AUTH do { \ - if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) { \ - rc = SSH_OK; \ - state(data, SSH_AUTH_PASS_INIT); \ - } \ - else { \ - MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); \ - } \ - } while(0) - -#define MOVE_TO_TERTIARY_AUTH do { \ - if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { \ - rc = SSH_OK; \ - state(data, SSH_AUTH_KEY_INIT); \ - } \ - else { \ - MOVE_TO_LAST_AUTH; \ - } \ - } while(0) - -#define MOVE_TO_SECONDARY_AUTH do { \ - if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) { \ - rc = SSH_OK; \ - state(data, SSH_AUTH_GSSAPI); \ - } \ - else { \ - MOVE_TO_TERTIARY_AUTH; \ - } \ - } while(0) - -static -int myssh_auth_interactive(struct connectdata *conn) -{ - int rc; - struct ssh_conn *sshc = &conn->proto.sshc; - int nprompts; - -restart: - switch(sshc->kbd_state) { - case 0: - rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL); - if(rc == SSH_AUTH_AGAIN) - return SSH_AGAIN; - - if(rc != SSH_AUTH_INFO) - return SSH_ERROR; - - nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session); - if(nprompts != 1) - return SSH_ERROR; - - rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, conn->passwd); - if(rc < 0) - return SSH_ERROR; - - /* FALLTHROUGH */ - case 1: - sshc->kbd_state = 1; - - rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL); - if(rc == SSH_AUTH_AGAIN) - return SSH_AGAIN; - else if(rc == SSH_AUTH_SUCCESS) - rc = SSH_OK; - else if(rc == SSH_AUTH_INFO) { - nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session); - if(nprompts) - return SSH_ERROR; - - sshc->kbd_state = 2; - goto restart; - } - else - rc = SSH_ERROR; - break; - case 2: - sshc->kbd_state = 2; - - rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL); - if(rc == SSH_AUTH_AGAIN) - return SSH_AGAIN; - else if(rc == SSH_AUTH_SUCCESS) - rc = SSH_OK; - else - rc = SSH_ERROR; - - break; - default: - return SSH_ERROR; - } - - sshc->kbd_state = 0; - return rc; -} - -/* - * ssh_statemach_act() runs the SSH state machine as far as it can without - * blocking and without reaching the end. The data the pointer 'block' points - * to will be set to TRUE if the libssh function returns SSH_AGAIN - * meaning it wants to be called again when the socket is ready - */ -static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct SSHPROTO *protop = data->req.p.ssh; - struct ssh_conn *sshc = &conn->proto.sshc; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; - int rc = SSH_NO_ERROR, err; - char *new_readdir_line; - int seekerr = CURL_SEEKFUNC_OK; - const char *err_msg; - *block = 0; /* we're not blocking by default */ - - do { - - switch(sshc->state) { - case SSH_INIT: - sshc->secondCreateDirs = 0; - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_OK; - -#if 0 - ssh_set_log_level(SSH_LOG_PROTOCOL); -#endif - - /* Set libssh to non-blocking, since everything internally is - non-blocking */ - ssh_set_blocking(sshc->ssh_session, 0); - - state(data, SSH_S_STARTUP); - /* FALLTHROUGH */ - - case SSH_S_STARTUP: - rc = ssh_connect(sshc->ssh_session); - if(rc == SSH_AGAIN) - break; - - if(rc != SSH_OK) { - failf(data, "Failure establishing ssh session"); - MOVE_TO_ERROR_STATE(CURLE_FAILED_INIT); - break; - } - - state(data, SSH_HOSTKEY); - - /* FALLTHROUGH */ - case SSH_HOSTKEY: - - rc = myssh_is_known(data); - if(rc != SSH_OK) { - MOVE_TO_ERROR_STATE(CURLE_PEER_FAILED_VERIFICATION); - break; - } - - state(data, SSH_AUTHLIST); - /* FALLTHROUGH */ - case SSH_AUTHLIST:{ - sshc->authed = FALSE; - - rc = ssh_userauth_none(sshc->ssh_session, NULL); - if(rc == SSH_AUTH_AGAIN) { - rc = SSH_AGAIN; - break; - } - - if(rc == SSH_AUTH_SUCCESS) { - sshc->authed = TRUE; - infof(data, "Authenticated with none"); - state(data, SSH_AUTH_DONE); - break; - } - else if(rc == SSH_AUTH_ERROR) { - MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); - break; - } - - sshc->auth_methods = ssh_userauth_list(sshc->ssh_session, NULL); - if(sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) { - state(data, SSH_AUTH_PKEY_INIT); - infof(data, "Authentication using SSH public key file"); - } - else if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) { - state(data, SSH_AUTH_GSSAPI); - } - else if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { - state(data, SSH_AUTH_KEY_INIT); - } - else if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) { - state(data, SSH_AUTH_PASS_INIT); - } - else { /* unsupported authentication method */ - MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); - break; - } - - break; - } - case SSH_AUTH_PKEY_INIT: - if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY)) { - MOVE_TO_SECONDARY_AUTH; - break; - } - - /* Two choices, (1) private key was given on CMD, - * (2) use the "default" keys. */ - if(data->set.str[STRING_SSH_PRIVATE_KEY]) { - if(sshc->pubkey && !data->set.ssl.key_passwd) { - rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL, - sshc->pubkey); - if(rc == SSH_AUTH_AGAIN) { - rc = SSH_AGAIN; - break; - } - - if(rc != SSH_OK) { - MOVE_TO_SECONDARY_AUTH; - break; - } - } - - rc = ssh_pki_import_privkey_file(data-> - set.str[STRING_SSH_PRIVATE_KEY], - data->set.ssl.key_passwd, NULL, - NULL, &sshc->privkey); - if(rc != SSH_OK) { - failf(data, "Could not load private key file %s", - data->set.str[STRING_SSH_PRIVATE_KEY]); - MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); - break; - } - - state(data, SSH_AUTH_PKEY); - break; - - } - else { - rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL, - data->set.ssl.key_passwd); - if(rc == SSH_AUTH_AGAIN) { - rc = SSH_AGAIN; - break; - } - if(rc == SSH_AUTH_SUCCESS) { - rc = SSH_OK; - sshc->authed = TRUE; - infof(data, "Completed public key authentication"); - state(data, SSH_AUTH_DONE); - break; - } - - MOVE_TO_SECONDARY_AUTH; - } - break; - case SSH_AUTH_PKEY: - rc = ssh_userauth_publickey(sshc->ssh_session, NULL, sshc->privkey); - if(rc == SSH_AUTH_AGAIN) { - rc = SSH_AGAIN; - break; - } - - if(rc == SSH_AUTH_SUCCESS) { - sshc->authed = TRUE; - infof(data, "Completed public key authentication"); - state(data, SSH_AUTH_DONE); - break; - } - else { - infof(data, "Failed public key authentication (rc: %d)", rc); - MOVE_TO_SECONDARY_AUTH; - } - break; - - case SSH_AUTH_GSSAPI: - if(!(data->set.ssh_auth_types & CURLSSH_AUTH_GSSAPI)) { - MOVE_TO_TERTIARY_AUTH; - break; - } - - rc = ssh_userauth_gssapi(sshc->ssh_session); - if(rc == SSH_AUTH_AGAIN) { - rc = SSH_AGAIN; - break; - } - - if(rc == SSH_AUTH_SUCCESS) { - rc = SSH_OK; - sshc->authed = TRUE; - infof(data, "Completed gssapi authentication"); - state(data, SSH_AUTH_DONE); - break; - } - - MOVE_TO_TERTIARY_AUTH; - break; - - case SSH_AUTH_KEY_INIT: - if(data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) { - state(data, SSH_AUTH_KEY); - } - else { - MOVE_TO_LAST_AUTH; - } - break; - - case SSH_AUTH_KEY: - - /* Authentication failed. Continue with keyboard-interactive now. */ - rc = myssh_auth_interactive(conn); - if(rc == SSH_AGAIN) { - break; - } - if(rc == SSH_OK) { - sshc->authed = TRUE; - infof(data, "completed keyboard interactive authentication"); - } - state(data, SSH_AUTH_DONE); - break; - - case SSH_AUTH_PASS_INIT: - if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD)) { - /* Host key authentication is intentionally not implemented */ - MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); - break; - } - state(data, SSH_AUTH_PASS); - /* FALLTHROUGH */ - - case SSH_AUTH_PASS: - rc = ssh_userauth_password(sshc->ssh_session, NULL, conn->passwd); - if(rc == SSH_AUTH_AGAIN) { - rc = SSH_AGAIN; - break; - } - - if(rc == SSH_AUTH_SUCCESS) { - sshc->authed = TRUE; - infof(data, "Completed password authentication"); - state(data, SSH_AUTH_DONE); - } - else { - MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); - } - break; - - case SSH_AUTH_DONE: - if(!sshc->authed) { - failf(data, "Authentication failure"); - MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); - break; - } - - /* - * At this point we have an authenticated ssh session. - */ - infof(data, "Authentication complete"); - - Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */ - - conn->sockfd = sock; - conn->writesockfd = CURL_SOCKET_BAD; - - if(conn->handler->protocol == CURLPROTO_SFTP) { - state(data, SSH_SFTP_INIT); - break; - } - infof(data, "SSH CONNECT phase done"); - state(data, SSH_STOP); - break; - - case SSH_SFTP_INIT: - ssh_set_blocking(sshc->ssh_session, 1); - - sshc->sftp_session = sftp_new(sshc->ssh_session); - if(!sshc->sftp_session) { - failf(data, "Failure initializing sftp session: %s", - ssh_get_error(sshc->ssh_session)); - MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT); - break; - } - - rc = sftp_init(sshc->sftp_session); - if(rc != SSH_OK) { - failf(data, "Failure initializing sftp session: %s", - ssh_get_error(sshc->ssh_session)); - MOVE_TO_ERROR_STATE(sftp_error_to_CURLE(SSH_FX_FAILURE)); - break; - } - state(data, SSH_SFTP_REALPATH); - /* FALLTHROUGH */ - case SSH_SFTP_REALPATH: - /* - * Get the "home" directory - */ - sshc->homedir = sftp_canonicalize_path(sshc->sftp_session, "."); - if(!sshc->homedir) { - MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT); - break; - } - data->state.most_recent_ftp_entrypath = sshc->homedir; - - /* This is the last step in the SFTP connect phase. Do note that while - we get the homedir here, we get the "workingpath" in the DO action - since the homedir will remain the same between request but the - working path will not. */ - DEBUGF(infof(data, "SSH CONNECT phase done")); - state(data, SSH_STOP); - break; - - case SSH_SFTP_QUOTE_INIT: - result = Curl_getworkingpath(data, sshc->homedir, &protop->path); - if(result) { - sshc->actualcode = result; - state(data, SSH_STOP); - break; - } - - if(data->set.quote) { - infof(data, "Sending quote commands"); - sshc->quote_item = data->set.quote; - state(data, SSH_SFTP_QUOTE); - } - else { - state(data, SSH_SFTP_GETINFO); - } - break; - - case SSH_SFTP_POSTQUOTE_INIT: - if(data->set.postquote) { - infof(data, "Sending quote commands"); - sshc->quote_item = data->set.postquote; - state(data, SSH_SFTP_QUOTE); - } - else { - state(data, SSH_STOP); - } - break; - - case SSH_SFTP_QUOTE: - /* Send any quote commands */ - sftp_quote(data); - break; - - case SSH_SFTP_NEXT_QUOTE: - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - - sshc->quote_item = sshc->quote_item->next; - - if(sshc->quote_item) { - state(data, SSH_SFTP_QUOTE); - } - else { - if(sshc->nextstate != SSH_NO_STATE) { - state(data, sshc->nextstate); - sshc->nextstate = SSH_NO_STATE; - } - else { - state(data, SSH_SFTP_GETINFO); - } - } - break; - - case SSH_SFTP_QUOTE_STAT: - sftp_quote_stat(data); - break; - - case SSH_SFTP_QUOTE_SETSTAT: - rc = sftp_setstat(sshc->sftp_session, sshc->quote_path2, - sshc->quote_attrs); - if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Attempt to set SFTP stats failed: %s", - ssh_get_error(sshc->ssh_session)); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - /* sshc->actualcode = sftp_error_to_CURLE(err); - * we do not send the actual error; we return - * the error the libssh2 backend is returning */ - break; - } - state(data, SSH_SFTP_NEXT_QUOTE); - break; - - case SSH_SFTP_QUOTE_SYMLINK: - rc = sftp_symlink(sshc->sftp_session, sshc->quote_path2, - sshc->quote_path1); - if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "symlink command failed: %s", - ssh_get_error(sshc->ssh_session)); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - state(data, SSH_SFTP_NEXT_QUOTE); - break; - - case SSH_SFTP_QUOTE_MKDIR: - rc = sftp_mkdir(sshc->sftp_session, sshc->quote_path1, - (mode_t)data->set.new_directory_perms); - if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - failf(data, "mkdir command failed: %s", - ssh_get_error(sshc->ssh_session)); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - state(data, SSH_SFTP_NEXT_QUOTE); - break; - - case SSH_SFTP_QUOTE_RENAME: - rc = sftp_rename(sshc->sftp_session, sshc->quote_path1, - sshc->quote_path2); - if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "rename command failed: %s", - ssh_get_error(sshc->ssh_session)); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - state(data, SSH_SFTP_NEXT_QUOTE); - break; - - case SSH_SFTP_QUOTE_RMDIR: - rc = sftp_rmdir(sshc->sftp_session, sshc->quote_path1); - if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - failf(data, "rmdir command failed: %s", - ssh_get_error(sshc->ssh_session)); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - state(data, SSH_SFTP_NEXT_QUOTE); - break; - - case SSH_SFTP_QUOTE_UNLINK: - rc = sftp_unlink(sshc->sftp_session, sshc->quote_path1); - if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - failf(data, "rm command failed: %s", - ssh_get_error(sshc->ssh_session)); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - state(data, SSH_SFTP_NEXT_QUOTE); - break; - - case SSH_SFTP_QUOTE_STATVFS: - { - sftp_statvfs_t statvfs; - - statvfs = sftp_statvfs(sshc->sftp_session, sshc->quote_path1); - if(!statvfs && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - failf(data, "statvfs command failed: %s", - ssh_get_error(sshc->ssh_session)); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - else if(statvfs) { - char *tmp = aprintf("statvfs:\n" - "f_bsize: %llu\n" "f_frsize: %llu\n" - "f_blocks: %llu\n" "f_bfree: %llu\n" - "f_bavail: %llu\n" "f_files: %llu\n" - "f_ffree: %llu\n" "f_favail: %llu\n" - "f_fsid: %llu\n" "f_flag: %llu\n" - "f_namemax: %llu\n", - statvfs->f_bsize, statvfs->f_frsize, - statvfs->f_blocks, statvfs->f_bfree, - statvfs->f_bavail, statvfs->f_files, - statvfs->f_ffree, statvfs->f_favail, - statvfs->f_fsid, statvfs->f_flag, - statvfs->f_namemax); - sftp_statvfs_free(statvfs); - - if(!tmp) { - result = CURLE_OUT_OF_MEMORY; - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - break; - } - - result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); - free(tmp); - if(result) { - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - } - } - state(data, SSH_SFTP_NEXT_QUOTE); - break; - } - - case SSH_SFTP_GETINFO: - if(data->set.get_filetime) { - state(data, SSH_SFTP_FILETIME); - } - else { - state(data, SSH_SFTP_TRANS_INIT); - } - break; - - case SSH_SFTP_FILETIME: - { - sftp_attributes attrs; - - attrs = sftp_stat(sshc->sftp_session, protop->path); - if(attrs) { - data->info.filetime = attrs->mtime; - sftp_attributes_free(attrs); - } - - state(data, SSH_SFTP_TRANS_INIT); - break; - } - - case SSH_SFTP_TRANS_INIT: - if(data->set.upload) - state(data, SSH_SFTP_UPLOAD_INIT); - else { - if(protop->path[strlen(protop->path)-1] == '/') - state(data, SSH_SFTP_READDIR_INIT); - else - state(data, SSH_SFTP_DOWNLOAD_INIT); - } - break; - - case SSH_SFTP_UPLOAD_INIT: - { - int flags; - - if(data->state.resume_from) { - sftp_attributes attrs; - - if(data->state.resume_from < 0) { - attrs = sftp_stat(sshc->sftp_session, protop->path); - if(attrs) { - curl_off_t size = attrs->size; - if(size < 0) { - failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); - MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME); - break; - } - data->state.resume_from = attrs->size; - - sftp_attributes_free(attrs); - } - else { - data->state.resume_from = 0; - } - } - } - - if(data->set.remote_append) - /* Try to open for append, but create if nonexisting */ - flags = O_WRONLY|O_CREAT|O_APPEND; - else if(data->state.resume_from > 0) - /* If we have restart position then open for append */ - flags = O_WRONLY|O_APPEND; - else - /* Clear file before writing (normal behavior) */ - flags = O_WRONLY|O_CREAT|O_TRUNC; - - if(sshc->sftp_file) - sftp_close(sshc->sftp_file); - sshc->sftp_file = - sftp_open(sshc->sftp_session, protop->path, - flags, (mode_t)data->set.new_file_perms); - if(!sshc->sftp_file) { - err = sftp_get_error(sshc->sftp_session); - - if(((err == SSH_FX_NO_SUCH_FILE || err == SSH_FX_FAILURE || - err == SSH_FX_NO_SUCH_PATH)) && - (data->set.ftp_create_missing_dirs && - (strlen(protop->path) > 1))) { - /* try to create the path remotely */ - rc = 0; - sshc->secondCreateDirs = 1; - state(data, SSH_SFTP_CREATE_DIRS_INIT); - break; - } - else { - MOVE_TO_SFTP_CLOSE_STATE(); - break; - } - } - - /* If we have a restart point then we need to seek to the correct - position. */ - if(data->state.resume_from > 0) { - /* Let's read off the proper amount of bytes from the input. */ - if(conn->seek_func) { - Curl_set_in_callback(data, true); - seekerr = conn->seek_func(conn->seek_client, data->state.resume_from, - SEEK_SET); - Curl_set_in_callback(data, false); - } - - if(seekerr != CURL_SEEKFUNC_OK) { - curl_off_t passed = 0; - - if(seekerr != CURL_SEEKFUNC_CANTSEEK) { - failf(data, "Could not seek stream"); - return CURLE_FTP_COULDNT_USE_REST; - } - /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */ - do { - size_t readthisamountnow = - (data->state.resume_from - passed > data->set.buffer_size) ? - (size_t)data->set.buffer_size : - curlx_sotouz(data->state.resume_from - passed); - - size_t actuallyread = - data->state.fread_func(data->state.buffer, 1, - readthisamountnow, data->state.in); - - passed += actuallyread; - if((actuallyread == 0) || (actuallyread > readthisamountnow)) { - /* this checks for greater-than only to make sure that the - CURL_READFUNC_ABORT return code still aborts */ - failf(data, "Failed to read data"); - MOVE_TO_ERROR_STATE(CURLE_FTP_COULDNT_USE_REST); - break; - } - } while(passed < data->state.resume_from); - if(rc) - break; - } - - /* now, decrease the size of the read */ - if(data->state.infilesize > 0) { - data->state.infilesize -= data->state.resume_from; - data->req.size = data->state.infilesize; - Curl_pgrsSetUploadSize(data, data->state.infilesize); - } - - rc = sftp_seek64(sshc->sftp_file, data->state.resume_from); - if(rc) { - MOVE_TO_SFTP_CLOSE_STATE(); - break; - } - } - if(data->state.infilesize > 0) { - data->req.size = data->state.infilesize; - Curl_pgrsSetUploadSize(data, data->state.infilesize); - } - /* upload data */ - Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); - - /* not set by Curl_setup_transfer to preserve keepon bits */ - conn->sockfd = conn->writesockfd; - - /* store this original bitmask setup to use later on if we can't - figure out a "real" bitmask */ - sshc->orig_waitfor = data->req.keepon; - - /* we want to use the _sending_ function even when the socket turns - out readable as the underlying libssh sftp send function will deal - with both accordingly */ - conn->cselect_bits = CURL_CSELECT_OUT; - - /* since we don't really wait for anything at this point, we want the - state machine to move on as soon as possible so we set a very short - timeout here */ - Curl_expire(data, 0, EXPIRE_RUN_NOW); - - state(data, SSH_STOP); - break; - } - - case SSH_SFTP_CREATE_DIRS_INIT: - if(strlen(protop->path) > 1) { - sshc->slash_pos = protop->path + 1; /* ignore the leading '/' */ - state(data, SSH_SFTP_CREATE_DIRS); - } - else { - state(data, SSH_SFTP_UPLOAD_INIT); - } - break; - - case SSH_SFTP_CREATE_DIRS: - sshc->slash_pos = strchr(sshc->slash_pos, '/'); - if(sshc->slash_pos) { - *sshc->slash_pos = 0; - - infof(data, "Creating directory '%s'", protop->path); - state(data, SSH_SFTP_CREATE_DIRS_MKDIR); - break; - } - state(data, SSH_SFTP_UPLOAD_INIT); - break; - - case SSH_SFTP_CREATE_DIRS_MKDIR: - /* 'mode' - parameter is preliminary - default to 0644 */ - rc = sftp_mkdir(sshc->sftp_session, protop->path, - (mode_t)data->set.new_directory_perms); - *sshc->slash_pos = '/'; - ++sshc->slash_pos; - if(rc < 0) { - /* - * Abort if failure wasn't that the dir already exists or the - * permission was denied (creation might succeed further down the - * path) - retry on unspecific FAILURE also - */ - err = sftp_get_error(sshc->sftp_session); - if((err != SSH_FX_FILE_ALREADY_EXISTS) && - (err != SSH_FX_FAILURE) && - (err != SSH_FX_PERMISSION_DENIED)) { - MOVE_TO_SFTP_CLOSE_STATE(); - break; - } - rc = 0; /* clear rc and continue */ - } - state(data, SSH_SFTP_CREATE_DIRS); - break; - - case SSH_SFTP_READDIR_INIT: - Curl_pgrsSetDownloadSize(data, -1); - if(data->set.opt_no_body) { - state(data, SSH_STOP); - break; - } - - /* - * This is a directory that we are trying to get, so produce a directory - * listing - */ - sshc->sftp_dir = sftp_opendir(sshc->sftp_session, - protop->path); - if(!sshc->sftp_dir) { - failf(data, "Could not open directory for reading: %s", - ssh_get_error(sshc->ssh_session)); - MOVE_TO_SFTP_CLOSE_STATE(); - break; - } - state(data, SSH_SFTP_READDIR); - break; - - case SSH_SFTP_READDIR: - - if(sshc->readdir_attrs) - sftp_attributes_free(sshc->readdir_attrs); - - sshc->readdir_attrs = sftp_readdir(sshc->sftp_session, sshc->sftp_dir); - if(sshc->readdir_attrs) { - sshc->readdir_filename = sshc->readdir_attrs->name; - sshc->readdir_longentry = sshc->readdir_attrs->longname; - sshc->readdir_len = strlen(sshc->readdir_filename); - - if(data->set.list_only) { - char *tmpLine; - - tmpLine = aprintf("%s\n", sshc->readdir_filename); - if(!tmpLine) { - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; - break; - } - result = Curl_client_write(data, CLIENTWRITE_BODY, - tmpLine, sshc->readdir_len + 1); - free(tmpLine); - - if(result) { - state(data, SSH_STOP); - break; - } - /* since this counts what we send to the client, we include the - newline in this counter */ - data->req.bytecount += sshc->readdir_len + 1; - - /* output debug output if that is requested */ - Curl_debug(data, CURLINFO_DATA_OUT, (char *)sshc->readdir_filename, - sshc->readdir_len); - } - else { - sshc->readdir_currLen = strlen(sshc->readdir_longentry); - sshc->readdir_totalLen = 80 + sshc->readdir_currLen; - sshc->readdir_line = calloc(sshc->readdir_totalLen, 1); - if(!sshc->readdir_line) { - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; - break; - } - - memcpy(sshc->readdir_line, sshc->readdir_longentry, - sshc->readdir_currLen); - if((sshc->readdir_attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) && - ((sshc->readdir_attrs->permissions & SSH_S_IFMT) == - SSH_S_IFLNK)) { - sshc->readdir_linkPath = aprintf("%s%s", protop->path, - sshc->readdir_filename); - - if(!sshc->readdir_linkPath) { - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; - break; - } - - state(data, SSH_SFTP_READDIR_LINK); - break; - } - state(data, SSH_SFTP_READDIR_BOTTOM); - break; - } - } - else if(sftp_dir_eof(sshc->sftp_dir)) { - state(data, SSH_SFTP_READDIR_DONE); - break; - } - else { - failf(data, "Could not open remote file for reading: %s", - ssh_get_error(sshc->ssh_session)); - MOVE_TO_SFTP_CLOSE_STATE(); - break; - } - break; - - case SSH_SFTP_READDIR_LINK: - if(sshc->readdir_link_attrs) - sftp_attributes_free(sshc->readdir_link_attrs); - - sshc->readdir_link_attrs = sftp_lstat(sshc->sftp_session, - sshc->readdir_linkPath); - if(sshc->readdir_link_attrs == 0) { - failf(data, "Could not read symlink for reading: %s", - ssh_get_error(sshc->ssh_session)); - MOVE_TO_SFTP_CLOSE_STATE(); - break; - } - - if(!sshc->readdir_link_attrs->name) { - sshc->readdir_tmp = sftp_readlink(sshc->sftp_session, - sshc->readdir_linkPath); - if(!sshc->readdir_filename) - sshc->readdir_len = 0; - else - sshc->readdir_len = strlen(sshc->readdir_tmp); - sshc->readdir_longentry = NULL; - sshc->readdir_filename = sshc->readdir_tmp; - } - else { - sshc->readdir_len = strlen(sshc->readdir_link_attrs->name); - sshc->readdir_filename = sshc->readdir_link_attrs->name; - sshc->readdir_longentry = sshc->readdir_link_attrs->longname; - } - - Curl_safefree(sshc->readdir_linkPath); - - /* get room for the filename and extra output */ - sshc->readdir_totalLen += 4 + sshc->readdir_len; - new_readdir_line = Curl_saferealloc(sshc->readdir_line, - sshc->readdir_totalLen); - if(!new_readdir_line) { - sshc->readdir_line = NULL; - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; - break; - } - sshc->readdir_line = new_readdir_line; - - sshc->readdir_currLen += msnprintf(sshc->readdir_line + - sshc->readdir_currLen, - sshc->readdir_totalLen - - sshc->readdir_currLen, - " -> %s", - sshc->readdir_filename); - - sftp_attributes_free(sshc->readdir_link_attrs); - sshc->readdir_link_attrs = NULL; - sshc->readdir_filename = NULL; - sshc->readdir_longentry = NULL; - - state(data, SSH_SFTP_READDIR_BOTTOM); - /* FALLTHROUGH */ - case SSH_SFTP_READDIR_BOTTOM: - sshc->readdir_currLen += msnprintf(sshc->readdir_line + - sshc->readdir_currLen, - sshc->readdir_totalLen - - sshc->readdir_currLen, "\n"); - result = Curl_client_write(data, CLIENTWRITE_BODY, - sshc->readdir_line, - sshc->readdir_currLen); - - if(!result) { - /* output debug output if that is requested */ - Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line, - sshc->readdir_currLen); - data->req.bytecount += sshc->readdir_currLen; - } - Curl_safefree(sshc->readdir_line); - ssh_string_free_char(sshc->readdir_tmp); - sshc->readdir_tmp = NULL; - - if(result) { - state(data, SSH_STOP); - } - else - state(data, SSH_SFTP_READDIR); - break; - - case SSH_SFTP_READDIR_DONE: - sftp_closedir(sshc->sftp_dir); - sshc->sftp_dir = NULL; - - /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); - state(data, SSH_STOP); - break; - - case SSH_SFTP_DOWNLOAD_INIT: - /* - * Work on getting the specified file - */ - if(sshc->sftp_file) - sftp_close(sshc->sftp_file); - - sshc->sftp_file = sftp_open(sshc->sftp_session, protop->path, - O_RDONLY, (mode_t)data->set.new_file_perms); - if(!sshc->sftp_file) { - failf(data, "Could not open remote file for reading: %s", - ssh_get_error(sshc->ssh_session)); - - MOVE_TO_SFTP_CLOSE_STATE(); - break; - } - - state(data, SSH_SFTP_DOWNLOAD_STAT); - break; - - case SSH_SFTP_DOWNLOAD_STAT: - { - sftp_attributes attrs; - curl_off_t size; - - attrs = sftp_fstat(sshc->sftp_file); - if(!attrs || - !(attrs->flags & SSH_FILEXFER_ATTR_SIZE) || - (attrs->size == 0)) { - /* - * sftp_fstat didn't return an error, so maybe the server - * just doesn't support stat() - * OR the server doesn't return a file size with a stat() - * OR file size is 0 - */ - data->req.size = -1; - data->req.maxdownload = -1; - Curl_pgrsSetDownloadSize(data, -1); - size = 0; - } - else { - size = attrs->size; - - sftp_attributes_free(attrs); - - if(size < 0) { - failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); - return CURLE_BAD_DOWNLOAD_RESUME; - } - if(data->state.use_range) { - curl_off_t from, to; - char *ptr; - char *ptr2; - CURLofft to_t; - CURLofft from_t; - - from_t = curlx_strtoofft(data->state.range, &ptr, 0, &from); - if(from_t == CURL_OFFT_FLOW) { - return CURLE_RANGE_ERROR; - } - while(*ptr && (ISBLANK(*ptr) || (*ptr == '-'))) - ptr++; - to_t = curlx_strtoofft(ptr, &ptr2, 0, &to); - if(to_t == CURL_OFFT_FLOW) { - return CURLE_RANGE_ERROR; - } - if((to_t == CURL_OFFT_INVAL) /* no "to" value given */ - || (to >= size)) { - to = size - 1; - } - if(from_t) { - /* from is relative to end of file */ - from = size - to; - to = size - 1; - } - if(from > size) { - failf(data, "Offset (%" - CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" - CURL_FORMAT_CURL_OFF_T ")", from, size); - return CURLE_BAD_DOWNLOAD_RESUME; - } - if(from > to) { - from = to; - size = 0; - } - else { - size = to - from + 1; - } - - rc = sftp_seek64(sshc->sftp_file, from); - if(rc) { - MOVE_TO_SFTP_CLOSE_STATE(); - break; - } - } - data->req.size = size; - data->req.maxdownload = size; - Curl_pgrsSetDownloadSize(data, size); - } - - /* We can resume if we can seek to the resume position */ - if(data->state.resume_from) { - if(data->state.resume_from < 0) { - /* We're supposed to download the last abs(from) bytes */ - if((curl_off_t)size < -data->state.resume_from) { - failf(data, "Offset (%" - CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" - CURL_FORMAT_CURL_OFF_T ")", - data->state.resume_from, size); - return CURLE_BAD_DOWNLOAD_RESUME; - } - /* download from where? */ - data->state.resume_from += size; - } - else { - if((curl_off_t)size < data->state.resume_from) { - failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T - ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")", - data->state.resume_from, size); - return CURLE_BAD_DOWNLOAD_RESUME; - } - } - /* Now store the number of bytes we are expected to download */ - data->req.size = size - data->state.resume_from; - data->req.maxdownload = size - data->state.resume_from; - Curl_pgrsSetDownloadSize(data, - size - data->state.resume_from); - - rc = sftp_seek64(sshc->sftp_file, data->state.resume_from); - if(rc) { - MOVE_TO_SFTP_CLOSE_STATE(); - break; - } - } - } - - /* Setup the actual download */ - if(data->req.size == 0) { - /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); - infof(data, "File already completely downloaded"); - state(data, SSH_STOP); - break; - } - Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1); - - /* not set by Curl_setup_transfer to preserve keepon bits */ - conn->writesockfd = conn->sockfd; - - /* we want to use the _receiving_ function even when the socket turns - out writableable as the underlying libssh recv function will deal - with both accordingly */ - conn->cselect_bits = CURL_CSELECT_IN; - - if(result) { - /* this should never occur; the close state should be entered - at the time the error occurs */ - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = result; - } - else { - sshc->sftp_recv_state = 0; - state(data, SSH_STOP); - } - break; - - case SSH_SFTP_CLOSE: - if(sshc->sftp_file) { - sftp_close(sshc->sftp_file); - sshc->sftp_file = NULL; - } - Curl_safefree(protop->path); - - DEBUGF(infof(data, "SFTP DONE done")); - - /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT - After nextstate is executed, the control should come back to - SSH_SFTP_CLOSE to pass the correct result back */ - if(sshc->nextstate != SSH_NO_STATE && - sshc->nextstate != SSH_SFTP_CLOSE) { - state(data, sshc->nextstate); - sshc->nextstate = SSH_SFTP_CLOSE; - } - else { - state(data, SSH_STOP); - result = sshc->actualcode; - } - break; - - case SSH_SFTP_SHUTDOWN: - /* during times we get here due to a broken transfer and then the - sftp_handle might not have been taken down so make sure that is done - before we proceed */ - - if(sshc->sftp_file) { - sftp_close(sshc->sftp_file); - sshc->sftp_file = NULL; - } - - if(sshc->sftp_session) { - sftp_free(sshc->sftp_session); - sshc->sftp_session = NULL; - } - - SSH_STRING_FREE_CHAR(sshc->homedir); - data->state.most_recent_ftp_entrypath = NULL; - - state(data, SSH_SESSION_DISCONNECT); - break; - - case SSH_SCP_TRANS_INIT: - result = Curl_getworkingpath(data, sshc->homedir, &protop->path); - if(result) { - sshc->actualcode = result; - state(data, SSH_STOP); - break; - } - - /* Functions from the SCP subsystem cannot handle/return SSH_AGAIN */ - ssh_set_blocking(sshc->ssh_session, 1); - - if(data->set.upload) { - if(data->state.infilesize < 0) { - failf(data, "SCP requires a known file size for upload"); - sshc->actualcode = CURLE_UPLOAD_FAILED; - MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED); - break; - } - - sshc->scp_session = - ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, protop->path); - state(data, SSH_SCP_UPLOAD_INIT); - } - else { - sshc->scp_session = - ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, protop->path); - state(data, SSH_SCP_DOWNLOAD_INIT); - } - - if(!sshc->scp_session) { - err_msg = ssh_get_error(sshc->ssh_session); - failf(data, "%s", err_msg); - MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED); - } - - break; - - case SSH_SCP_UPLOAD_INIT: - - rc = ssh_scp_init(sshc->scp_session); - if(rc != SSH_OK) { - err_msg = ssh_get_error(sshc->ssh_session); - failf(data, "%s", err_msg); - MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED); - break; - } - - rc = ssh_scp_push_file(sshc->scp_session, protop->path, - data->state.infilesize, - (int)data->set.new_file_perms); - if(rc != SSH_OK) { - err_msg = ssh_get_error(sshc->ssh_session); - failf(data, "%s", err_msg); - MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED); - break; - } - - /* upload data */ - Curl_setup_transfer(data, -1, data->req.size, FALSE, FIRSTSOCKET); - - /* not set by Curl_setup_transfer to preserve keepon bits */ - conn->sockfd = conn->writesockfd; - - /* store this original bitmask setup to use later on if we can't - figure out a "real" bitmask */ - sshc->orig_waitfor = data->req.keepon; - - /* we want to use the _sending_ function even when the socket turns - out readable as the underlying libssh scp send function will deal - with both accordingly */ - conn->cselect_bits = CURL_CSELECT_OUT; - - state(data, SSH_STOP); - - break; - - case SSH_SCP_DOWNLOAD_INIT: - - rc = ssh_scp_init(sshc->scp_session); - if(rc != SSH_OK) { - err_msg = ssh_get_error(sshc->ssh_session); - failf(data, "%s", err_msg); - MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT); - break; - } - state(data, SSH_SCP_DOWNLOAD); - /* FALLTHROUGH */ - - case SSH_SCP_DOWNLOAD:{ - curl_off_t bytecount; - - rc = ssh_scp_pull_request(sshc->scp_session); - if(rc != SSH_SCP_REQUEST_NEWFILE) { - err_msg = ssh_get_error(sshc->ssh_session); - failf(data, "%s", err_msg); - MOVE_TO_ERROR_STATE(CURLE_REMOTE_FILE_NOT_FOUND); - break; - } - - /* download data */ - bytecount = ssh_scp_request_get_size(sshc->scp_session); - data->req.maxdownload = (curl_off_t) bytecount; - Curl_setup_transfer(data, FIRSTSOCKET, bytecount, FALSE, -1); - - /* not set by Curl_setup_transfer to preserve keepon bits */ - conn->writesockfd = conn->sockfd; - - /* we want to use the _receiving_ function even when the socket turns - out writableable as the underlying libssh recv function will deal - with both accordingly */ - conn->cselect_bits = CURL_CSELECT_IN; - - state(data, SSH_STOP); - break; - } - case SSH_SCP_DONE: - if(data->set.upload) - state(data, SSH_SCP_SEND_EOF); - else - state(data, SSH_SCP_CHANNEL_FREE); - break; - - case SSH_SCP_SEND_EOF: - if(sshc->scp_session) { - rc = ssh_scp_close(sshc->scp_session); - if(rc == SSH_AGAIN) { - /* Currently the ssh_scp_close handles waiting for EOF in - * blocking way. - */ - break; - } - if(rc != SSH_OK) { - infof(data, "Failed to close libssh scp channel: %s", - ssh_get_error(sshc->ssh_session)); - } - } - - state(data, SSH_SCP_CHANNEL_FREE); - break; - - case SSH_SCP_CHANNEL_FREE: - if(sshc->scp_session) { - ssh_scp_free(sshc->scp_session); - sshc->scp_session = NULL; - } - DEBUGF(infof(data, "SCP DONE phase complete")); - - ssh_set_blocking(sshc->ssh_session, 0); - - state(data, SSH_SESSION_DISCONNECT); - /* FALLTHROUGH */ - - case SSH_SESSION_DISCONNECT: - /* during weird times when we've been prematurely aborted, the channel - is still alive when we reach this state and we MUST kill the channel - properly first */ - if(sshc->scp_session) { - ssh_scp_free(sshc->scp_session); - sshc->scp_session = NULL; - } - - ssh_disconnect(sshc->ssh_session); - if(!ssh_version(SSH_VERSION_INT(0, 10, 0))) { - /* conn->sock[FIRSTSOCKET] is closed by ssh_disconnect behind our back, - explicitly mark it as closed with the memdebug macro. This libssh - bug is fixed in 0.10.0. */ - fake_sclose(conn->sock[FIRSTSOCKET]); - conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; - } - - SSH_STRING_FREE_CHAR(sshc->homedir); - data->state.most_recent_ftp_entrypath = NULL; - - state(data, SSH_SESSION_FREE); - /* FALLTHROUGH */ - case SSH_SESSION_FREE: - if(sshc->ssh_session) { - ssh_free(sshc->ssh_session); - sshc->ssh_session = NULL; - } - - /* worst-case scenario cleanup */ - - DEBUGASSERT(sshc->ssh_session == NULL); - DEBUGASSERT(sshc->scp_session == NULL); - - if(sshc->readdir_tmp) { - ssh_string_free_char(sshc->readdir_tmp); - sshc->readdir_tmp = NULL; - } - - if(sshc->quote_attrs) - sftp_attributes_free(sshc->quote_attrs); - - if(sshc->readdir_attrs) - sftp_attributes_free(sshc->readdir_attrs); - - if(sshc->readdir_link_attrs) - sftp_attributes_free(sshc->readdir_link_attrs); - - if(sshc->privkey) - ssh_key_free(sshc->privkey); - if(sshc->pubkey) - ssh_key_free(sshc->pubkey); - - Curl_safefree(sshc->rsa_pub); - Curl_safefree(sshc->rsa); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - Curl_safefree(sshc->readdir_line); - Curl_safefree(sshc->readdir_linkPath); - SSH_STRING_FREE_CHAR(sshc->homedir); - - /* the code we are about to return */ - result = sshc->actualcode; - - memset(sshc, 0, sizeof(struct ssh_conn)); - - connclose(conn, "SSH session free"); - sshc->state = SSH_SESSION_FREE; /* current */ - sshc->nextstate = SSH_NO_STATE; - state(data, SSH_STOP); - break; - - case SSH_QUIT: - /* fallthrough, just stop! */ - default: - /* internal error */ - sshc->nextstate = SSH_NO_STATE; - state(data, SSH_STOP); - break; - - } - } while(!rc && (sshc->state != SSH_STOP)); - - - if(rc == SSH_AGAIN) { - /* we would block, we need to wait for the socket to be ready (in the - right direction too)! */ - *block = TRUE; - } - - return result; -} - - -/* called by the multi interface to figure out what socket(s) to wait for and - for what actions in the DO_DONE, PERFORM and WAITPERFORM states */ -static int myssh_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *sock) -{ - int bitmap = GETSOCK_BLANK; - (void)data; - sock[0] = conn->sock[FIRSTSOCKET]; - - if(conn->waitfor & KEEP_RECV) - bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); - - if(conn->waitfor & KEEP_SEND) - bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); - - if(!conn->waitfor) - bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); - - return bitmap; -} - -static void myssh_block2waitfor(struct connectdata *conn, bool block) -{ - struct ssh_conn *sshc = &conn->proto.sshc; - - /* If it didn't block, or nothing was returned by ssh_get_poll_flags - * have the original set */ - conn->waitfor = sshc->orig_waitfor; - - if(block) { - int dir = ssh_get_poll_flags(sshc->ssh_session); - if(dir & SSH_READ_PENDING) { - /* translate the libssh define bits into our own bit defines */ - conn->waitfor = KEEP_RECV; - } - else if(dir & SSH_WRITE_PENDING) { - conn->waitfor = KEEP_SEND; - } - } -} - -/* called repeatedly until done from multi.c */ -static CURLcode myssh_multi_statemach(struct Curl_easy *data, - bool *done) -{ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - bool block; /* we store the status and use that to provide a ssh_getsock() - implementation */ - CURLcode result = myssh_statemach_act(data, &block); - - *done = (sshc->state == SSH_STOP) ? TRUE : FALSE; - myssh_block2waitfor(conn, block); - - return result; -} - -static CURLcode myssh_block_statemach(struct Curl_easy *data, - bool disconnect) -{ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - CURLcode result = CURLE_OK; - - while((sshc->state != SSH_STOP) && !result) { - bool block; - timediff_t left = 1000; - struct curltime now = Curl_now(); - - result = myssh_statemach_act(data, &block); - if(result) - break; - - if(!disconnect) { - if(Curl_pgrsUpdate(data)) - return CURLE_ABORTED_BY_CALLBACK; - - result = Curl_speedcheck(data, now); - if(result) - break; - - left = Curl_timeleft(data, NULL, FALSE); - if(left < 0) { - failf(data, "Operation timed out"); - return CURLE_OPERATION_TIMEDOUT; - } - } - - if(block) { - curl_socket_t fd_read = conn->sock[FIRSTSOCKET]; - /* wait for the socket to become ready */ - (void) Curl_socket_check(fd_read, CURL_SOCKET_BAD, - CURL_SOCKET_BAD, left > 1000 ? 1000 : left); - } - - } - - return result; -} - -/* - * SSH setup connection - */ -static CURLcode myssh_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - struct SSHPROTO *ssh; - (void)conn; - - data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO)); - if(!ssh) - return CURLE_OUT_OF_MEMORY; - - return CURLE_OK; -} - -static Curl_recv scp_recv, sftp_recv; -static Curl_send scp_send, sftp_send; - -/* - * Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to - * do protocol-specific actions at connect-time. - */ -static CURLcode myssh_connect(struct Curl_easy *data, bool *done) -{ - struct ssh_conn *ssh; - CURLcode result; - struct connectdata *conn = data->conn; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; - int rc; - - /* initialize per-handle data if not already */ - if(!data->req.p.ssh) - myssh_setup_connection(data, conn); - - /* We default to persistent connections. We set this already in this connect - function to make the re-use checks properly be able to check this bit. */ - connkeep(conn, "SSH default"); - - if(conn->handler->protocol & CURLPROTO_SCP) { - conn->recv[FIRSTSOCKET] = scp_recv; - conn->send[FIRSTSOCKET] = scp_send; - } - else { - conn->recv[FIRSTSOCKET] = sftp_recv; - conn->send[FIRSTSOCKET] = sftp_send; - } - - ssh = &conn->proto.sshc; - - ssh->ssh_session = ssh_new(); - if(!ssh->ssh_session) { - failf(data, "Failure initialising ssh session"); - return CURLE_FAILED_INIT; - } - - rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_HOST, conn->host.name); - if(rc != SSH_OK) { - failf(data, "Could not set remote host"); - return CURLE_FAILED_INIT; - } - - rc = ssh_options_parse_config(ssh->ssh_session, NULL); - if(rc != SSH_OK) { - infof(data, "Could not parse SSH configuration files"); - /* ignore */ - } - - rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_FD, &sock); - if(rc != SSH_OK) { - failf(data, "Could not set socket"); - return CURLE_FAILED_INIT; - } - - if(conn->user && conn->user[0] != '\0') { - infof(data, "User: %s", conn->user); - rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_USER, conn->user); - if(rc != SSH_OK) { - failf(data, "Could not set user"); - return CURLE_FAILED_INIT; - } - } - - if(data->set.str[STRING_SSH_KNOWNHOSTS]) { - infof(data, "Known hosts: %s", data->set.str[STRING_SSH_KNOWNHOSTS]); - rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_KNOWNHOSTS, - data->set.str[STRING_SSH_KNOWNHOSTS]); - if(rc != SSH_OK) { - failf(data, "Could not set known hosts file path"); - return CURLE_FAILED_INIT; - } - } - - if(conn->remote_port) { - rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_PORT, - &conn->remote_port); - if(rc != SSH_OK) { - failf(data, "Could not set remote port"); - return CURLE_FAILED_INIT; - } - } - - if(data->set.ssh_compression) { - rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_COMPRESSION, - "zlib,zlib@openssh.com,none"); - if(rc != SSH_OK) { - failf(data, "Could not set compression"); - return CURLE_FAILED_INIT; - } - } - - ssh->privkey = NULL; - ssh->pubkey = NULL; - - if(data->set.str[STRING_SSH_PUBLIC_KEY]) { - rc = ssh_pki_import_pubkey_file(data->set.str[STRING_SSH_PUBLIC_KEY], - &ssh->pubkey); - if(rc != SSH_OK) { - failf(data, "Could not load public key file"); - return CURLE_FAILED_INIT; - } - } - - /* we do not verify here, we do it at the state machine, - * after connection */ - - state(data, SSH_INIT); - - result = myssh_multi_statemach(data, done); - - return result; -} - -/* called from multi.c while DOing */ -static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done) -{ - CURLcode result; - - result = myssh_multi_statemach(data, dophase_done); - - if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); - } - return result; -} - -/* - *********************************************************************** - * - * scp_perform() - * - * This is the actual DO function for SCP. Get a file according to - * the options previously setup. - */ - -static -CURLcode scp_perform(struct Curl_easy *data, - bool *connected, bool *dophase_done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - - DEBUGF(infof(data, "DO phase starts")); - - *dophase_done = FALSE; /* not done yet */ - - /* start the first command in the DO phase */ - state(data, SSH_SCP_TRANS_INIT); - - result = myssh_multi_statemach(data, dophase_done); - - *connected = conn->bits.tcpconnect[FIRSTSOCKET]; - - if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); - } - - return result; -} - -static CURLcode myssh_do_it(struct Curl_easy *data, bool *done) -{ - CURLcode result; - bool connected = 0; - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - - *done = FALSE; /* default to false */ - - data->req.size = -1; /* make sure this is unknown at this point */ - - sshc->actualcode = CURLE_OK; /* reset error code */ - sshc->secondCreateDirs = 0; /* reset the create dir attempt state - variable */ - - Curl_pgrsSetUploadCounter(data, 0); - Curl_pgrsSetDownloadCounter(data, 0); - Curl_pgrsSetUploadSize(data, -1); - Curl_pgrsSetDownloadSize(data, -1); - - if(conn->handler->protocol & CURLPROTO_SCP) - result = scp_perform(data, &connected, done); - else - result = sftp_perform(data, &connected, done); - - return result; -} - -/* BLOCKING, but the function is using the state machine so the only reason - this is still blocking is that the multi interface code has no support for - disconnecting operations that takes a while */ -static CURLcode scp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection) -{ - CURLcode result = CURLE_OK; - struct ssh_conn *ssh = &conn->proto.sshc; - (void) dead_connection; - - if(ssh->ssh_session) { - /* only if there's a session still around to use! */ - - state(data, SSH_SESSION_DISCONNECT); - - result = myssh_block_statemach(data, TRUE); - } - - return result; -} - -/* generic done function for both SCP and SFTP called from their specific - done functions */ -static CURLcode myssh_done(struct Curl_easy *data, CURLcode status) -{ - CURLcode result = CURLE_OK; - struct SSHPROTO *protop = data->req.p.ssh; - - if(!status) { - /* run the state-machine */ - result = myssh_block_statemach(data, FALSE); - } - else - result = status; - - if(protop) - Curl_safefree(protop->path); - if(Curl_pgrsDone(data)) - return CURLE_ABORTED_BY_CALLBACK; - - data->req.keepon = 0; /* clear all bits */ - return result; -} - - -static CURLcode scp_done(struct Curl_easy *data, CURLcode status, - bool premature) -{ - (void) premature; /* not used */ - - if(!status) - state(data, SSH_SCP_DONE); - - return myssh_done(data, status); - -} - -static ssize_t scp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, CURLcode *err) -{ - int rc; - struct connectdata *conn = data->conn; - (void) sockindex; /* we only support SCP on the fixed known primary socket */ - (void) err; - - rc = ssh_scp_write(conn->proto.sshc.scp_session, mem, len); - -#if 0 - /* The following code is misleading, mostly added as wishful thinking - * that libssh at some point will implement non-blocking ssh_scp_write/read. - * Currently rc can only be number of bytes read or SSH_ERROR. */ - myssh_block2waitfor(conn, (rc == SSH_AGAIN) ? TRUE : FALSE); - - if(rc == SSH_AGAIN) { - *err = CURLE_AGAIN; - return 0; - } - else -#endif - if(rc != SSH_OK) { - *err = CURLE_SSH; - return -1; - } - - return len; -} - -static ssize_t scp_recv(struct Curl_easy *data, int sockindex, - char *mem, size_t len, CURLcode *err) -{ - ssize_t nread; - struct connectdata *conn = data->conn; - (void) err; - (void) sockindex; /* we only support SCP on the fixed known primary socket */ - - /* libssh returns int */ - nread = ssh_scp_read(conn->proto.sshc.scp_session, mem, len); - -#if 0 - /* The following code is misleading, mostly added as wishful thinking - * that libssh at some point will implement non-blocking ssh_scp_write/read. - * Currently rc can only be SSH_OK or SSH_ERROR. */ - - myssh_block2waitfor(conn, (nread == SSH_AGAIN) ? TRUE : FALSE); - if(nread == SSH_AGAIN) { - *err = CURLE_AGAIN; - nread = -1; - } -#endif - - return nread; -} - -/* - * =============== SFTP =============== - */ - -/* - *********************************************************************** - * - * sftp_perform() - * - * This is the actual DO function for SFTP. Get a file/directory according to - * the options previously setup. - */ - -static -CURLcode sftp_perform(struct Curl_easy *data, - bool *connected, - bool *dophase_done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - - DEBUGF(infof(data, "DO phase starts")); - - *dophase_done = FALSE; /* not done yet */ - - /* start the first command in the DO phase */ - state(data, SSH_SFTP_QUOTE_INIT); - - /* run the state-machine */ - result = myssh_multi_statemach(data, dophase_done); - - *connected = conn->bits.tcpconnect[FIRSTSOCKET]; - - if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); - } - - return result; -} - -/* called from multi.c while DOing */ -static CURLcode sftp_doing(struct Curl_easy *data, - bool *dophase_done) -{ - CURLcode result = myssh_multi_statemach(data, dophase_done); - if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); - } - return result; -} - -/* BLOCKING, but the function is using the state machine so the only reason - this is still blocking is that the multi interface code has no support for - disconnecting operations that takes a while */ -static CURLcode sftp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection) -{ - CURLcode result = CURLE_OK; - (void) dead_connection; - - DEBUGF(infof(data, "SSH DISCONNECT starts now")); - - if(conn->proto.sshc.ssh_session) { - /* only if there's a session still around to use! */ - state(data, SSH_SFTP_SHUTDOWN); - result = myssh_block_statemach(data, TRUE); - } - - DEBUGF(infof(data, "SSH DISCONNECT is done")); - - return result; - -} - -static CURLcode sftp_done(struct Curl_easy *data, CURLcode status, - bool premature) -{ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - - if(!status) { - /* Post quote commands are executed after the SFTP_CLOSE state to avoid - errors that could happen due to open file handles during POSTQUOTE - operation */ - if(!premature && data->set.postquote && !conn->bits.retry) - sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT; - state(data, SSH_SFTP_CLOSE); - } - return myssh_done(data, status); -} - -/* return number of sent bytes */ -static ssize_t sftp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, CURLcode *err) -{ - ssize_t nwrite; - struct connectdata *conn = data->conn; - (void)sockindex; - - nwrite = sftp_write(conn->proto.sshc.sftp_file, mem, len); - - myssh_block2waitfor(conn, FALSE); - -#if 0 /* not returned by libssh on write */ - if(nwrite == SSH_AGAIN) { - *err = CURLE_AGAIN; - nwrite = 0; - } - else -#endif - if(nwrite < 0) { - *err = CURLE_SSH; - nwrite = -1; - } - - return nwrite; -} - -/* - * Return number of received (decrypted) bytes - * or <0 on error - */ -static ssize_t sftp_recv(struct Curl_easy *data, int sockindex, - char *mem, size_t len, CURLcode *err) -{ - ssize_t nread; - struct connectdata *conn = data->conn; - (void)sockindex; - - DEBUGASSERT(len < CURL_MAX_READ_SIZE); - - switch(conn->proto.sshc.sftp_recv_state) { - case 0: - conn->proto.sshc.sftp_file_index = - sftp_async_read_begin(conn->proto.sshc.sftp_file, - (uint32_t)len); - if(conn->proto.sshc.sftp_file_index < 0) { - *err = CURLE_RECV_ERROR; - return -1; - } - - /* FALLTHROUGH */ - case 1: - conn->proto.sshc.sftp_recv_state = 1; - - nread = sftp_async_read(conn->proto.sshc.sftp_file, - mem, (uint32_t)len, - conn->proto.sshc.sftp_file_index); - - myssh_block2waitfor(conn, (nread == SSH_AGAIN)?TRUE:FALSE); - - if(nread == SSH_AGAIN) { - *err = CURLE_AGAIN; - return -1; - } - else if(nread < 0) { - *err = CURLE_RECV_ERROR; - return -1; - } - - conn->proto.sshc.sftp_recv_state = 0; - return nread; - - default: - /* we never reach here */ - return -1; - } -} - -static void sftp_quote(struct Curl_easy *data) -{ - const char *cp; - struct connectdata *conn = data->conn; - struct SSHPROTO *protop = data->req.p.ssh; - struct ssh_conn *sshc = &conn->proto.sshc; - CURLcode result; - - /* - * Support some of the "FTP" commands - */ - char *cmd = sshc->quote_item->data; - sshc->acceptfail = FALSE; - - /* if a command starts with an asterisk, which a legal SFTP command never - can, the command will be allowed to fail without it causing any - aborts or cancels etc. It will cause libcurl to act as if the command - is successful, whatever the server reponds. */ - - if(cmd[0] == '*') { - cmd++; - sshc->acceptfail = TRUE; - } - - if(strcasecompare("pwd", cmd)) { - /* output debug output if that is requested */ - char *tmp = aprintf("257 \"%s\" is current directory.\n", - protop->path); - if(!tmp) { - sshc->actualcode = CURLE_OUT_OF_MEMORY; - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - return; - } - Curl_debug(data, CURLINFO_HEADER_OUT, (char *) "PWD\n", 4); - Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp)); - - /* this sends an FTP-like "header" to the header callback so that the - current directory can be read very similar to how it is read when - using ordinary FTP. */ - result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); - free(tmp); - if(result) { - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - } - else - state(data, SSH_SFTP_NEXT_QUOTE); - return; - } - - /* - * the arguments following the command must be separated from the - * command with a space so we can check for it unconditionally - */ - cp = strchr(cmd, ' '); - if(!cp) { - failf(data, "Syntax error in SFTP command. Supply parameter(s)"); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - return; - } - - /* - * also, every command takes at least one argument so we get that - * first argument right now - */ - result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, "Syntax error: Bad first parameter"); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - return; - } - - /* - * SFTP is a binary protocol, so we don't send text commands - * to the server. Instead, we scan for commands used by - * OpenSSH's sftp program and call the appropriate libssh - * functions. - */ - if(strncasecompare(cmd, "chgrp ", 6) || - strncasecompare(cmd, "chmod ", 6) || - strncasecompare(cmd, "chown ", 6) || - strncasecompare(cmd, "atime ", 6) || - strncasecompare(cmd, "mtime ", 6)) { - /* attribute change */ - - /* sshc->quote_path1 contains the mode to set */ - /* get the destination */ - result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, "Syntax error in chgrp/chmod/chown/atime/mtime: " - "Bad second parameter"); - Curl_safefree(sshc->quote_path1); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - return; - } - sshc->quote_attrs = NULL; - state(data, SSH_SFTP_QUOTE_STAT); - return; - } - if(strncasecompare(cmd, "ln ", 3) || - strncasecompare(cmd, "symlink ", 8)) { - /* symbolic linking */ - /* sshc->quote_path1 is the source */ - /* get the destination */ - result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, "Syntax error in ln/symlink: Bad second parameter"); - Curl_safefree(sshc->quote_path1); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - return; - } - state(data, SSH_SFTP_QUOTE_SYMLINK); - return; - } - else if(strncasecompare(cmd, "mkdir ", 6)) { - /* create dir */ - state(data, SSH_SFTP_QUOTE_MKDIR); - return; - } - else if(strncasecompare(cmd, "rename ", 7)) { - /* rename file */ - /* first param is the source path */ - /* second param is the dest. path */ - result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, "Syntax error in rename: Bad second parameter"); - Curl_safefree(sshc->quote_path1); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - return; - } - state(data, SSH_SFTP_QUOTE_RENAME); - return; - } - else if(strncasecompare(cmd, "rmdir ", 6)) { - /* delete dir */ - state(data, SSH_SFTP_QUOTE_RMDIR); - return; - } - else if(strncasecompare(cmd, "rm ", 3)) { - state(data, SSH_SFTP_QUOTE_UNLINK); - return; - } -#ifdef HAS_STATVFS_SUPPORT - else if(strncasecompare(cmd, "statvfs ", 8)) { - state(data, SSH_SFTP_QUOTE_STATVFS); - return; - } -#endif - - failf(data, "Unknown SFTP command"); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; -} - -static void sftp_quote_stat(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - char *cmd = sshc->quote_item->data; - sshc->acceptfail = FALSE; - - /* if a command starts with an asterisk, which a legal SFTP command never - can, the command will be allowed to fail without it causing any - aborts or cancels etc. It will cause libcurl to act as if the command - is successful, whatever the server reponds. */ - - if(cmd[0] == '*') { - cmd++; - sshc->acceptfail = TRUE; - } - - /* We read the file attributes, store them in sshc->quote_attrs - * and modify them accordingly to command. Then we switch to - * QUOTE_SETSTAT state to write new ones. - */ - - if(sshc->quote_attrs) - sftp_attributes_free(sshc->quote_attrs); - sshc->quote_attrs = sftp_stat(sshc->sftp_session, sshc->quote_path2); - if(!sshc->quote_attrs) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Attempt to get SFTP stats failed: %d", - sftp_get_error(sshc->sftp_session)); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - return; - } - - /* Now set the new attributes... */ - if(strncasecompare(cmd, "chgrp", 5)) { - sshc->quote_attrs->gid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10); - if(sshc->quote_attrs->gid == 0 && !ISDIGIT(sshc->quote_path1[0]) && - !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Syntax error: chgrp gid not a number"); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - return; - } - sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID; - } - else if(strncasecompare(cmd, "chmod", 5)) { - mode_t perms; - perms = (mode_t)strtoul(sshc->quote_path1, NULL, 8); - /* permissions are octal */ - if(perms == 0 && !ISDIGIT(sshc->quote_path1[0])) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Syntax error: chmod permissions not a number"); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - return; - } - sshc->quote_attrs->permissions = perms; - sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_PERMISSIONS; - } - else if(strncasecompare(cmd, "chown", 5)) { - sshc->quote_attrs->uid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10); - if(sshc->quote_attrs->uid == 0 && !ISDIGIT(sshc->quote_path1[0]) && - !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Syntax error: chown uid not a number"); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - return; - } - sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID; - } - else if(strncasecompare(cmd, "atime", 5) || - strncasecompare(cmd, "mtime", 5)) { - time_t date = Curl_getdate_capped(sshc->quote_path1); - bool fail = FALSE; - if(date == -1) { - failf(data, "incorrect date format for %.*s", 5, cmd); - fail = TRUE; - } -#if SIZEOF_TIME_T > 4 - else if(date > 0xffffffff) { - failf(data, "date overflow"); - fail = TRUE; /* avoid setting a capped time */ - } -#endif - if(fail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - return; - } - if(strncasecompare(cmd, "atime", 5)) - sshc->quote_attrs->atime = (uint32_t)date; - else /* mtime */ - sshc->quote_attrs->mtime = (uint32_t)date; - - sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_ACMODTIME; - } - - /* Now send the completed structure... */ - state(data, SSH_SFTP_QUOTE_SETSTAT); - return; -} - -CURLcode Curl_ssh_init(void) -{ - if(ssh_init()) { - DEBUGF(fprintf(stderr, "Error: libssh_init failed\n")); - return CURLE_FAILED_INIT; - } - return CURLE_OK; -} - -void Curl_ssh_cleanup(void) -{ - (void)ssh_finalize(); -} - -void Curl_ssh_version(char *buffer, size_t buflen) -{ - (void)msnprintf(buffer, buflen, "libssh/%s", ssh_version(0)); -} - -#endif /* USE_LIBSSH */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2017 - 2022 Red Hat, Inc.
+ *
+ * Authors: Nikos Mavrogiannopoulos, Tomas Mraz, Stanislav Zidek,
+ * Robert Kolcun, Andreas Schneider
+ *
+ * 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_LIBSSH
+
+#include <limits.h>
+
+#include <libssh/libssh.h>
+#include <libssh/sftp.h>
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#include <curl/curl.h>
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "progress.h"
+#include "transfer.h"
+#include "escape.h"
+#include "http.h" /* for HTTP proxy tunnel stuff */
+#include "ssh.h"
+#include "url.h"
+#include "speedcheck.h"
+#include "getinfo.h"
+#include "strdup.h"
+#include "strcase.h"
+#include "vtls/vtls.h"
+#include "cfilters.h"
+#include "connect.h"
+#include "inet_ntop.h"
+#include "parsedate.h" /* for the week day and month names */
+#include "sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "strtoofft.h"
+#include "multiif.h"
+#include "select.h"
+#include "warnless.h"
+#include "curl_path.h"
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* in 0.10.0 or later, ignore deprecated warnings */
+#if defined(__GNUC__) && \
+ (LIBSSH_VERSION_MINOR >= 10) || \
+ (LIBSSH_VERSION_MAJOR > 0)
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+/* A recent macro provided by libssh. Or make our own. */
+#ifndef SSH_STRING_FREE_CHAR
+#define SSH_STRING_FREE_CHAR(x) \
+ do { \
+ if(x) { \
+ ssh_string_free_char(x); \
+ x = NULL; \
+ } \
+ } while(0)
+#endif
+
+/* These stat values may not be the same as the user's S_IFMT / S_IFLNK */
+#ifndef SSH_S_IFMT
+#define SSH_S_IFMT 00170000
+#endif
+#ifndef SSH_S_IFLNK
+#define SSH_S_IFLNK 0120000
+#endif
+
+/* Local functions: */
+static CURLcode myssh_connect(struct Curl_easy *data, bool *done);
+static CURLcode myssh_multi_statemach(struct Curl_easy *data,
+ bool *done);
+static CURLcode myssh_do_it(struct Curl_easy *data, bool *done);
+
+static CURLcode scp_done(struct Curl_easy *data,
+ CURLcode, bool premature);
+static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done);
+static CURLcode scp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection);
+
+static CURLcode sftp_done(struct Curl_easy *data,
+ CURLcode, bool premature);
+static CURLcode sftp_doing(struct Curl_easy *data,
+ bool *dophase_done);
+static CURLcode sftp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead);
+static
+CURLcode sftp_perform(struct Curl_easy *data,
+ bool *connected,
+ bool *dophase_done);
+
+static void sftp_quote(struct Curl_easy *data);
+static void sftp_quote_stat(struct Curl_easy *data);
+static int myssh_getsock(struct Curl_easy *data,
+ struct connectdata *conn, curl_socket_t *sock);
+
+static CURLcode myssh_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn);
+
+/*
+ * SCP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_scp = {
+ "SCP", /* scheme */
+ myssh_setup_connection, /* setup_connection */
+ myssh_do_it, /* do_it */
+ scp_done, /* done */
+ ZERO_NULL, /* do_more */
+ myssh_connect, /* connect_it */
+ myssh_multi_statemach, /* connecting */
+ scp_doing, /* doing */
+ myssh_getsock, /* proto_getsock */
+ myssh_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ myssh_getsock, /* perform_getsock */
+ scp_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_SSH, /* defport */
+ CURLPROTO_SCP, /* protocol */
+ CURLPROTO_SCP, /* family */
+ PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
+};
+
+/*
+ * SFTP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_sftp = {
+ "SFTP", /* scheme */
+ myssh_setup_connection, /* setup_connection */
+ myssh_do_it, /* do_it */
+ sftp_done, /* done */
+ ZERO_NULL, /* do_more */
+ myssh_connect, /* connect_it */
+ myssh_multi_statemach, /* connecting */
+ sftp_doing, /* doing */
+ myssh_getsock, /* proto_getsock */
+ myssh_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ myssh_getsock, /* perform_getsock */
+ sftp_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_SSH, /* defport */
+ CURLPROTO_SFTP, /* protocol */
+ CURLPROTO_SFTP, /* family */
+ PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
+ | PROTOPT_NOURLQUERY /* flags */
+};
+
+static CURLcode sftp_error_to_CURLE(int err)
+{
+ switch(err) {
+ case SSH_FX_OK:
+ return CURLE_OK;
+
+ case SSH_FX_NO_SUCH_FILE:
+ case SSH_FX_NO_SUCH_PATH:
+ return CURLE_REMOTE_FILE_NOT_FOUND;
+
+ case SSH_FX_PERMISSION_DENIED:
+ case SSH_FX_WRITE_PROTECT:
+ return CURLE_REMOTE_ACCESS_DENIED;
+
+ case SSH_FX_FILE_ALREADY_EXISTS:
+ return CURLE_REMOTE_FILE_EXISTS;
+
+ default:
+ break;
+ }
+
+ return CURLE_SSH;
+}
+
+#ifndef DEBUGBUILD
+#define state(x,y) mystate(x,y)
+#else
+#define state(x,y) mystate(x,y, __LINE__)
+#endif
+
+/*
+ * SSH State machine related code
+ */
+/* This is the ONLY way to change SSH state! */
+static void mystate(struct Curl_easy *data, sshstate nowstate
+#ifdef DEBUGBUILD
+ , int lineno
+#endif
+ )
+{
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ /* for debug purposes */
+ static const char *const names[] = {
+ "SSH_STOP",
+ "SSH_INIT",
+ "SSH_S_STARTUP",
+ "SSH_HOSTKEY",
+ "SSH_AUTHLIST",
+ "SSH_AUTH_PKEY_INIT",
+ "SSH_AUTH_PKEY",
+ "SSH_AUTH_PASS_INIT",
+ "SSH_AUTH_PASS",
+ "SSH_AUTH_AGENT_INIT",
+ "SSH_AUTH_AGENT_LIST",
+ "SSH_AUTH_AGENT",
+ "SSH_AUTH_HOST_INIT",
+ "SSH_AUTH_HOST",
+ "SSH_AUTH_KEY_INIT",
+ "SSH_AUTH_KEY",
+ "SSH_AUTH_GSSAPI",
+ "SSH_AUTH_DONE",
+ "SSH_SFTP_INIT",
+ "SSH_SFTP_REALPATH",
+ "SSH_SFTP_QUOTE_INIT",
+ "SSH_SFTP_POSTQUOTE_INIT",
+ "SSH_SFTP_QUOTE",
+ "SSH_SFTP_NEXT_QUOTE",
+ "SSH_SFTP_QUOTE_STAT",
+ "SSH_SFTP_QUOTE_SETSTAT",
+ "SSH_SFTP_QUOTE_SYMLINK",
+ "SSH_SFTP_QUOTE_MKDIR",
+ "SSH_SFTP_QUOTE_RENAME",
+ "SSH_SFTP_QUOTE_RMDIR",
+ "SSH_SFTP_QUOTE_UNLINK",
+ "SSH_SFTP_QUOTE_STATVFS",
+ "SSH_SFTP_GETINFO",
+ "SSH_SFTP_FILETIME",
+ "SSH_SFTP_TRANS_INIT",
+ "SSH_SFTP_UPLOAD_INIT",
+ "SSH_SFTP_CREATE_DIRS_INIT",
+ "SSH_SFTP_CREATE_DIRS",
+ "SSH_SFTP_CREATE_DIRS_MKDIR",
+ "SSH_SFTP_READDIR_INIT",
+ "SSH_SFTP_READDIR",
+ "SSH_SFTP_READDIR_LINK",
+ "SSH_SFTP_READDIR_BOTTOM",
+ "SSH_SFTP_READDIR_DONE",
+ "SSH_SFTP_DOWNLOAD_INIT",
+ "SSH_SFTP_DOWNLOAD_STAT",
+ "SSH_SFTP_CLOSE",
+ "SSH_SFTP_SHUTDOWN",
+ "SSH_SCP_TRANS_INIT",
+ "SSH_SCP_UPLOAD_INIT",
+ "SSH_SCP_DOWNLOAD_INIT",
+ "SSH_SCP_DOWNLOAD",
+ "SSH_SCP_DONE",
+ "SSH_SCP_SEND_EOF",
+ "SSH_SCP_WAIT_EOF",
+ "SSH_SCP_WAIT_CLOSE",
+ "SSH_SCP_CHANNEL_FREE",
+ "SSH_SESSION_DISCONNECT",
+ "SSH_SESSION_FREE",
+ "QUIT"
+ };
+
+
+ if(sshc->state != nowstate) {
+ infof(data, "SSH %p state change from %s to %s (line %d)",
+ (void *) sshc, names[sshc->state], names[nowstate],
+ lineno);
+ }
+#endif
+
+ sshc->state = nowstate;
+}
+
+/* Multiple options:
+ * 1. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] is set with an MD5
+ * hash (90s style auth, not sure we should have it here)
+ * 2. data->set.ssh_keyfunc callback is set. Then we do trust on first
+ * use. We even save on knownhosts if CURLKHSTAT_FINE_ADD_TO_FILE
+ * is returned by it.
+ * 3. none of the above. We only accept if it is present on known hosts.
+ *
+ * Returns SSH_OK or SSH_ERROR.
+ */
+static int myssh_is_known(struct Curl_easy *data)
+{
+ int rc;
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ ssh_key pubkey;
+ size_t hlen;
+ unsigned char *hash = NULL;
+ char *found_base64 = NULL;
+ char *known_base64 = NULL;
+ int vstate;
+ enum curl_khmatch keymatch;
+ struct curl_khkey foundkey;
+ struct curl_khkey *knownkeyp = NULL;
+ curl_sshkeycallback func =
+ data->set.ssh_keyfunc;
+
+#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
+ struct ssh_knownhosts_entry *knownhostsentry = NULL;
+ struct curl_khkey knownkey;
+#endif
+
+#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0)
+ rc = ssh_get_server_publickey(sshc->ssh_session, &pubkey);
+#else
+ rc = ssh_get_publickey(sshc->ssh_session, &pubkey);
+#endif
+ if(rc != SSH_OK)
+ return rc;
+
+ if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) {
+ int i;
+ char md5buffer[33];
+ const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5];
+
+ rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5,
+ &hash, &hlen);
+ if(rc != SSH_OK || hlen != 16) {
+ failf(data,
+ "Denied establishing ssh session: md5 fingerprint not available");
+ goto cleanup;
+ }
+
+ for(i = 0; i < 16; i++)
+ msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char)hash[i]);
+
+ infof(data, "SSH MD5 fingerprint: %s", md5buffer);
+
+ if(!strcasecompare(md5buffer, pubkey_md5)) {
+ failf(data,
+ "Denied establishing ssh session: mismatch md5 fingerprint. "
+ "Remote %s is not equal to %s", md5buffer, pubkey_md5);
+ rc = SSH_ERROR;
+ goto cleanup;
+ }
+
+ rc = SSH_OK;
+ goto cleanup;
+ }
+
+ if(data->set.ssl.primary.verifyhost != TRUE) {
+ rc = SSH_OK;
+ goto cleanup;
+ }
+
+#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
+ /* Get the known_key from the known hosts file */
+ vstate = ssh_session_get_known_hosts_entry(sshc->ssh_session,
+ &knownhostsentry);
+
+ /* Case an entry was found in a known hosts file */
+ if(knownhostsentry) {
+ if(knownhostsentry->publickey) {
+ rc = ssh_pki_export_pubkey_base64(knownhostsentry->publickey,
+ &known_base64);
+ if(rc != SSH_OK) {
+ goto cleanup;
+ }
+ knownkey.key = known_base64;
+ knownkey.len = strlen(known_base64);
+
+ switch(ssh_key_type(knownhostsentry->publickey)) {
+ case SSH_KEYTYPE_RSA:
+ knownkey.keytype = CURLKHTYPE_RSA;
+ break;
+ case SSH_KEYTYPE_RSA1:
+ knownkey.keytype = CURLKHTYPE_RSA1;
+ break;
+ case SSH_KEYTYPE_ECDSA:
+ case SSH_KEYTYPE_ECDSA_P256:
+ case SSH_KEYTYPE_ECDSA_P384:
+ case SSH_KEYTYPE_ECDSA_P521:
+ knownkey.keytype = CURLKHTYPE_ECDSA;
+ break;
+ case SSH_KEYTYPE_ED25519:
+ knownkey.keytype = CURLKHTYPE_ED25519;
+ break;
+ case SSH_KEYTYPE_DSS:
+ knownkey.keytype = CURLKHTYPE_DSS;
+ break;
+ default:
+ rc = SSH_ERROR;
+ goto cleanup;
+ }
+ knownkeyp = &knownkey;
+ }
+ }
+
+ switch(vstate) {
+ case SSH_KNOWN_HOSTS_OK:
+ keymatch = CURLKHMATCH_OK;
+ break;
+ case SSH_KNOWN_HOSTS_OTHER:
+ /* fallthrough */
+ case SSH_KNOWN_HOSTS_NOT_FOUND:
+ /* fallthrough */
+ case SSH_KNOWN_HOSTS_UNKNOWN:
+ /* fallthrough */
+ case SSH_KNOWN_HOSTS_ERROR:
+ keymatch = CURLKHMATCH_MISSING;
+ break;
+ default:
+ keymatch = CURLKHMATCH_MISMATCH;
+ break;
+ }
+
+#else
+ vstate = ssh_is_server_known(sshc->ssh_session);
+ switch(vstate) {
+ case SSH_SERVER_KNOWN_OK:
+ keymatch = CURLKHMATCH_OK;
+ break;
+ case SSH_SERVER_FILE_NOT_FOUND:
+ /* fallthrough */
+ case SSH_SERVER_NOT_KNOWN:
+ keymatch = CURLKHMATCH_MISSING;
+ break;
+ default:
+ keymatch = CURLKHMATCH_MISMATCH;
+ break;
+ }
+#endif
+
+ if(func) { /* use callback to determine action */
+ rc = ssh_pki_export_pubkey_base64(pubkey, &found_base64);
+ if(rc != SSH_OK)
+ goto cleanup;
+
+ foundkey.key = found_base64;
+ foundkey.len = strlen(found_base64);
+
+ switch(ssh_key_type(pubkey)) {
+ case SSH_KEYTYPE_RSA:
+ foundkey.keytype = CURLKHTYPE_RSA;
+ break;
+ case SSH_KEYTYPE_RSA1:
+ foundkey.keytype = CURLKHTYPE_RSA1;
+ break;
+ case SSH_KEYTYPE_ECDSA:
+#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
+ case SSH_KEYTYPE_ECDSA_P256:
+ case SSH_KEYTYPE_ECDSA_P384:
+ case SSH_KEYTYPE_ECDSA_P521:
+#endif
+ foundkey.keytype = CURLKHTYPE_ECDSA;
+ break;
+#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,7,0)
+ case SSH_KEYTYPE_ED25519:
+ foundkey.keytype = CURLKHTYPE_ED25519;
+ break;
+#endif
+ case SSH_KEYTYPE_DSS:
+ foundkey.keytype = CURLKHTYPE_DSS;
+ break;
+ default:
+ rc = SSH_ERROR;
+ goto cleanup;
+ }
+
+ Curl_set_in_callback(data, true);
+ rc = func(data, knownkeyp, /* from the knownhosts file */
+ &foundkey, /* from the remote host */
+ keymatch, data->set.ssh_keyfunc_userp);
+ Curl_set_in_callback(data, false);
+
+ switch(rc) {
+ case CURLKHSTAT_FINE_ADD_TO_FILE:
+#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0)
+ rc = ssh_session_update_known_hosts(sshc->ssh_session);
+#else
+ rc = ssh_write_knownhost(sshc->ssh_session);
+#endif
+ if(rc != SSH_OK) {
+ goto cleanup;
+ }
+ break;
+ case CURLKHSTAT_FINE:
+ break;
+ default: /* REJECT/DEFER */
+ rc = SSH_ERROR;
+ goto cleanup;
+ }
+ }
+ else {
+ if(keymatch != CURLKHMATCH_OK) {
+ rc = SSH_ERROR;
+ goto cleanup;
+ }
+ }
+ rc = SSH_OK;
+
+cleanup:
+ if(found_base64) {
+ (free)(found_base64);
+ }
+ if(known_base64) {
+ (free)(known_base64);
+ }
+ if(hash)
+ ssh_clean_pubkey_hash(&hash);
+ ssh_key_free(pubkey);
+#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
+ if(knownhostsentry) {
+ ssh_knownhosts_entry_free(knownhostsentry);
+ }
+#endif
+ return rc;
+}
+
+#define MOVE_TO_ERROR_STATE(_r) do { \
+ state(data, SSH_SESSION_DISCONNECT); \
+ sshc->actualcode = _r; \
+ rc = SSH_ERROR; \
+ } while(0)
+
+#define MOVE_TO_SFTP_CLOSE_STATE() do { \
+ state(data, SSH_SFTP_CLOSE); \
+ sshc->actualcode = \
+ sftp_error_to_CURLE(sftp_get_error(sshc->sftp_session)); \
+ rc = SSH_ERROR; \
+ } while(0)
+
+#define MOVE_TO_LAST_AUTH do { \
+ if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) { \
+ rc = SSH_OK; \
+ state(data, SSH_AUTH_PASS_INIT); \
+ } \
+ else { \
+ MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); \
+ } \
+ } while(0)
+
+#define MOVE_TO_TERTIARY_AUTH do { \
+ if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { \
+ rc = SSH_OK; \
+ state(data, SSH_AUTH_KEY_INIT); \
+ } \
+ else { \
+ MOVE_TO_LAST_AUTH; \
+ } \
+ } while(0)
+
+#define MOVE_TO_SECONDARY_AUTH do { \
+ if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) { \
+ rc = SSH_OK; \
+ state(data, SSH_AUTH_GSSAPI); \
+ } \
+ else { \
+ MOVE_TO_TERTIARY_AUTH; \
+ } \
+ } while(0)
+
+static
+int myssh_auth_interactive(struct connectdata *conn)
+{
+ int rc;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ int nprompts;
+
+restart:
+ switch(sshc->kbd_state) {
+ case 0:
+ rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL);
+ if(rc == SSH_AUTH_AGAIN)
+ return SSH_AGAIN;
+
+ if(rc != SSH_AUTH_INFO)
+ return SSH_ERROR;
+
+ nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session);
+ if(nprompts != 1)
+ return SSH_ERROR;
+
+ rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, conn->passwd);
+ if(rc < 0)
+ return SSH_ERROR;
+
+ /* FALLTHROUGH */
+ case 1:
+ sshc->kbd_state = 1;
+
+ rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL);
+ if(rc == SSH_AUTH_AGAIN)
+ return SSH_AGAIN;
+ else if(rc == SSH_AUTH_SUCCESS)
+ rc = SSH_OK;
+ else if(rc == SSH_AUTH_INFO) {
+ nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session);
+ if(nprompts)
+ return SSH_ERROR;
+
+ sshc->kbd_state = 2;
+ goto restart;
+ }
+ else
+ rc = SSH_ERROR;
+ break;
+ case 2:
+ sshc->kbd_state = 2;
+
+ rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL);
+ if(rc == SSH_AUTH_AGAIN)
+ return SSH_AGAIN;
+ else if(rc == SSH_AUTH_SUCCESS)
+ rc = SSH_OK;
+ else
+ rc = SSH_ERROR;
+
+ break;
+ default:
+ return SSH_ERROR;
+ }
+
+ sshc->kbd_state = 0;
+ return rc;
+}
+
+/*
+ * ssh_statemach_act() runs the SSH state machine as far as it can without
+ * blocking and without reaching the end. The data the pointer 'block' points
+ * to will be set to TRUE if the libssh function returns SSH_AGAIN
+ * meaning it wants to be called again when the socket is ready
+ */
+static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct SSHPROTO *protop = data->req.p.ssh;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ int rc = SSH_NO_ERROR, err;
+ char *new_readdir_line;
+ int seekerr = CURL_SEEKFUNC_OK;
+ const char *err_msg;
+ *block = 0; /* we're not blocking by default */
+
+ do {
+
+ switch(sshc->state) {
+ case SSH_INIT:
+ sshc->secondCreateDirs = 0;
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_OK;
+
+#if 0
+ ssh_set_log_level(SSH_LOG_PROTOCOL);
+#endif
+
+ /* Set libssh to non-blocking, since everything internally is
+ non-blocking */
+ ssh_set_blocking(sshc->ssh_session, 0);
+
+ state(data, SSH_S_STARTUP);
+ /* FALLTHROUGH */
+
+ case SSH_S_STARTUP:
+ rc = ssh_connect(sshc->ssh_session);
+ if(rc == SSH_AGAIN)
+ break;
+
+ if(rc != SSH_OK) {
+ failf(data, "Failure establishing ssh session");
+ MOVE_TO_ERROR_STATE(CURLE_FAILED_INIT);
+ break;
+ }
+
+ state(data, SSH_HOSTKEY);
+
+ /* FALLTHROUGH */
+ case SSH_HOSTKEY:
+
+ rc = myssh_is_known(data);
+ if(rc != SSH_OK) {
+ MOVE_TO_ERROR_STATE(CURLE_PEER_FAILED_VERIFICATION);
+ break;
+ }
+
+ state(data, SSH_AUTHLIST);
+ /* FALLTHROUGH */
+ case SSH_AUTHLIST:{
+ sshc->authed = FALSE;
+
+ rc = ssh_userauth_none(sshc->ssh_session, NULL);
+ if(rc == SSH_AUTH_AGAIN) {
+ rc = SSH_AGAIN;
+ break;
+ }
+
+ if(rc == SSH_AUTH_SUCCESS) {
+ sshc->authed = TRUE;
+ infof(data, "Authenticated with none");
+ state(data, SSH_AUTH_DONE);
+ break;
+ }
+ else if(rc == SSH_AUTH_ERROR) {
+ MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
+ break;
+ }
+
+ sshc->auth_methods = ssh_userauth_list(sshc->ssh_session, NULL);
+ if(sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) {
+ state(data, SSH_AUTH_PKEY_INIT);
+ infof(data, "Authentication using SSH public key file");
+ }
+ else if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) {
+ state(data, SSH_AUTH_GSSAPI);
+ }
+ else if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) {
+ state(data, SSH_AUTH_KEY_INIT);
+ }
+ else if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) {
+ state(data, SSH_AUTH_PASS_INIT);
+ }
+ else { /* unsupported authentication method */
+ MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
+ break;
+ }
+
+ break;
+ }
+ case SSH_AUTH_PKEY_INIT:
+ if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY)) {
+ MOVE_TO_SECONDARY_AUTH;
+ break;
+ }
+
+ /* Two choices, (1) private key was given on CMD,
+ * (2) use the "default" keys. */
+ if(data->set.str[STRING_SSH_PRIVATE_KEY]) {
+ if(sshc->pubkey && !data->set.ssl.key_passwd) {
+ rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL,
+ sshc->pubkey);
+ if(rc == SSH_AUTH_AGAIN) {
+ rc = SSH_AGAIN;
+ break;
+ }
+
+ if(rc != SSH_OK) {
+ MOVE_TO_SECONDARY_AUTH;
+ break;
+ }
+ }
+
+ rc = ssh_pki_import_privkey_file(data->
+ set.str[STRING_SSH_PRIVATE_KEY],
+ data->set.ssl.key_passwd, NULL,
+ NULL, &sshc->privkey);
+ if(rc != SSH_OK) {
+ failf(data, "Could not load private key file %s",
+ data->set.str[STRING_SSH_PRIVATE_KEY]);
+ MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
+ break;
+ }
+
+ state(data, SSH_AUTH_PKEY);
+ break;
+
+ }
+ else {
+ rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL,
+ data->set.ssl.key_passwd);
+ if(rc == SSH_AUTH_AGAIN) {
+ rc = SSH_AGAIN;
+ break;
+ }
+ if(rc == SSH_AUTH_SUCCESS) {
+ rc = SSH_OK;
+ sshc->authed = TRUE;
+ infof(data, "Completed public key authentication");
+ state(data, SSH_AUTH_DONE);
+ break;
+ }
+
+ MOVE_TO_SECONDARY_AUTH;
+ }
+ break;
+ case SSH_AUTH_PKEY:
+ rc = ssh_userauth_publickey(sshc->ssh_session, NULL, sshc->privkey);
+ if(rc == SSH_AUTH_AGAIN) {
+ rc = SSH_AGAIN;
+ break;
+ }
+
+ if(rc == SSH_AUTH_SUCCESS) {
+ sshc->authed = TRUE;
+ infof(data, "Completed public key authentication");
+ state(data, SSH_AUTH_DONE);
+ break;
+ }
+ else {
+ infof(data, "Failed public key authentication (rc: %d)", rc);
+ MOVE_TO_SECONDARY_AUTH;
+ }
+ break;
+
+ case SSH_AUTH_GSSAPI:
+ if(!(data->set.ssh_auth_types & CURLSSH_AUTH_GSSAPI)) {
+ MOVE_TO_TERTIARY_AUTH;
+ break;
+ }
+
+ rc = ssh_userauth_gssapi(sshc->ssh_session);
+ if(rc == SSH_AUTH_AGAIN) {
+ rc = SSH_AGAIN;
+ break;
+ }
+
+ if(rc == SSH_AUTH_SUCCESS) {
+ rc = SSH_OK;
+ sshc->authed = TRUE;
+ infof(data, "Completed gssapi authentication");
+ state(data, SSH_AUTH_DONE);
+ break;
+ }
+
+ MOVE_TO_TERTIARY_AUTH;
+ break;
+
+ case SSH_AUTH_KEY_INIT:
+ if(data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) {
+ state(data, SSH_AUTH_KEY);
+ }
+ else {
+ MOVE_TO_LAST_AUTH;
+ }
+ break;
+
+ case SSH_AUTH_KEY:
+
+ /* Authentication failed. Continue with keyboard-interactive now. */
+ rc = myssh_auth_interactive(conn);
+ if(rc == SSH_AGAIN) {
+ break;
+ }
+ if(rc == SSH_OK) {
+ sshc->authed = TRUE;
+ infof(data, "completed keyboard interactive authentication");
+ }
+ state(data, SSH_AUTH_DONE);
+ break;
+
+ case SSH_AUTH_PASS_INIT:
+ if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD)) {
+ /* Host key authentication is intentionally not implemented */
+ MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
+ break;
+ }
+ state(data, SSH_AUTH_PASS);
+ /* FALLTHROUGH */
+
+ case SSH_AUTH_PASS:
+ rc = ssh_userauth_password(sshc->ssh_session, NULL, conn->passwd);
+ if(rc == SSH_AUTH_AGAIN) {
+ rc = SSH_AGAIN;
+ break;
+ }
+
+ if(rc == SSH_AUTH_SUCCESS) {
+ sshc->authed = TRUE;
+ infof(data, "Completed password authentication");
+ state(data, SSH_AUTH_DONE);
+ }
+ else {
+ MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
+ }
+ break;
+
+ case SSH_AUTH_DONE:
+ if(!sshc->authed) {
+ failf(data, "Authentication failure");
+ MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
+ break;
+ }
+
+ /*
+ * At this point we have an authenticated ssh session.
+ */
+ infof(data, "Authentication complete");
+
+ Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */
+
+ conn->sockfd = sock;
+ conn->writesockfd = CURL_SOCKET_BAD;
+
+ if(conn->handler->protocol == CURLPROTO_SFTP) {
+ state(data, SSH_SFTP_INIT);
+ break;
+ }
+ infof(data, "SSH CONNECT phase done");
+ state(data, SSH_STOP);
+ break;
+
+ case SSH_SFTP_INIT:
+ ssh_set_blocking(sshc->ssh_session, 1);
+
+ sshc->sftp_session = sftp_new(sshc->ssh_session);
+ if(!sshc->sftp_session) {
+ failf(data, "Failure initializing sftp session: %s",
+ ssh_get_error(sshc->ssh_session));
+ MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT);
+ break;
+ }
+
+ rc = sftp_init(sshc->sftp_session);
+ if(rc != SSH_OK) {
+ failf(data, "Failure initializing sftp session: %s",
+ ssh_get_error(sshc->ssh_session));
+ MOVE_TO_ERROR_STATE(sftp_error_to_CURLE(SSH_FX_FAILURE));
+ break;
+ }
+ state(data, SSH_SFTP_REALPATH);
+ /* FALLTHROUGH */
+ case SSH_SFTP_REALPATH:
+ /*
+ * Get the "home" directory
+ */
+ sshc->homedir = sftp_canonicalize_path(sshc->sftp_session, ".");
+ if(!sshc->homedir) {
+ MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT);
+ break;
+ }
+ data->state.most_recent_ftp_entrypath = sshc->homedir;
+
+ /* This is the last step in the SFTP connect phase. Do note that while
+ we get the homedir here, we get the "workingpath" in the DO action
+ since the homedir will remain the same between request but the
+ working path will not. */
+ DEBUGF(infof(data, "SSH CONNECT phase done"));
+ state(data, SSH_STOP);
+ break;
+
+ case SSH_SFTP_QUOTE_INIT:
+ result = Curl_getworkingpath(data, sshc->homedir, &protop->path);
+ if(result) {
+ sshc->actualcode = result;
+ state(data, SSH_STOP);
+ break;
+ }
+
+ if(data->set.quote) {
+ infof(data, "Sending quote commands");
+ sshc->quote_item = data->set.quote;
+ state(data, SSH_SFTP_QUOTE);
+ }
+ else {
+ state(data, SSH_SFTP_GETINFO);
+ }
+ break;
+
+ case SSH_SFTP_POSTQUOTE_INIT:
+ if(data->set.postquote) {
+ infof(data, "Sending quote commands");
+ sshc->quote_item = data->set.postquote;
+ state(data, SSH_SFTP_QUOTE);
+ }
+ else {
+ state(data, SSH_STOP);
+ }
+ break;
+
+ case SSH_SFTP_QUOTE:
+ /* Send any quote commands */
+ sftp_quote(data);
+ break;
+
+ case SSH_SFTP_NEXT_QUOTE:
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+
+ sshc->quote_item = sshc->quote_item->next;
+
+ if(sshc->quote_item) {
+ state(data, SSH_SFTP_QUOTE);
+ }
+ else {
+ if(sshc->nextstate != SSH_NO_STATE) {
+ state(data, sshc->nextstate);
+ sshc->nextstate = SSH_NO_STATE;
+ }
+ else {
+ state(data, SSH_SFTP_GETINFO);
+ }
+ }
+ break;
+
+ case SSH_SFTP_QUOTE_STAT:
+ sftp_quote_stat(data);
+ break;
+
+ case SSH_SFTP_QUOTE_SETSTAT:
+ rc = sftp_setstat(sshc->sftp_session, sshc->quote_path2,
+ sshc->quote_attrs);
+ if(rc && !sshc->acceptfail) {
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ failf(data, "Attempt to set SFTP stats failed: %s",
+ ssh_get_error(sshc->ssh_session));
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ /* sshc->actualcode = sftp_error_to_CURLE(err);
+ * we do not send the actual error; we return
+ * the error the libssh2 backend is returning */
+ break;
+ }
+ state(data, SSH_SFTP_NEXT_QUOTE);
+ break;
+
+ case SSH_SFTP_QUOTE_SYMLINK:
+ rc = sftp_symlink(sshc->sftp_session, sshc->quote_path2,
+ sshc->quote_path1);
+ if(rc && !sshc->acceptfail) {
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ failf(data, "symlink command failed: %s",
+ ssh_get_error(sshc->ssh_session));
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
+ }
+ state(data, SSH_SFTP_NEXT_QUOTE);
+ break;
+
+ case SSH_SFTP_QUOTE_MKDIR:
+ rc = sftp_mkdir(sshc->sftp_session, sshc->quote_path1,
+ (mode_t)data->set.new_directory_perms);
+ if(rc && !sshc->acceptfail) {
+ Curl_safefree(sshc->quote_path1);
+ failf(data, "mkdir command failed: %s",
+ ssh_get_error(sshc->ssh_session));
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
+ }
+ state(data, SSH_SFTP_NEXT_QUOTE);
+ break;
+
+ case SSH_SFTP_QUOTE_RENAME:
+ rc = sftp_rename(sshc->sftp_session, sshc->quote_path1,
+ sshc->quote_path2);
+ if(rc && !sshc->acceptfail) {
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ failf(data, "rename command failed: %s",
+ ssh_get_error(sshc->ssh_session));
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
+ }
+ state(data, SSH_SFTP_NEXT_QUOTE);
+ break;
+
+ case SSH_SFTP_QUOTE_RMDIR:
+ rc = sftp_rmdir(sshc->sftp_session, sshc->quote_path1);
+ if(rc && !sshc->acceptfail) {
+ Curl_safefree(sshc->quote_path1);
+ failf(data, "rmdir command failed: %s",
+ ssh_get_error(sshc->ssh_session));
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
+ }
+ state(data, SSH_SFTP_NEXT_QUOTE);
+ break;
+
+ case SSH_SFTP_QUOTE_UNLINK:
+ rc = sftp_unlink(sshc->sftp_session, sshc->quote_path1);
+ if(rc && !sshc->acceptfail) {
+ Curl_safefree(sshc->quote_path1);
+ failf(data, "rm command failed: %s",
+ ssh_get_error(sshc->ssh_session));
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
+ }
+ state(data, SSH_SFTP_NEXT_QUOTE);
+ break;
+
+ case SSH_SFTP_QUOTE_STATVFS:
+ {
+ sftp_statvfs_t statvfs;
+
+ statvfs = sftp_statvfs(sshc->sftp_session, sshc->quote_path1);
+ if(!statvfs && !sshc->acceptfail) {
+ Curl_safefree(sshc->quote_path1);
+ failf(data, "statvfs command failed: %s",
+ ssh_get_error(sshc->ssh_session));
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
+ }
+ else if(statvfs) {
+ char *tmp = aprintf("statvfs:\n"
+ "f_bsize: %llu\n" "f_frsize: %llu\n"
+ "f_blocks: %llu\n" "f_bfree: %llu\n"
+ "f_bavail: %llu\n" "f_files: %llu\n"
+ "f_ffree: %llu\n" "f_favail: %llu\n"
+ "f_fsid: %llu\n" "f_flag: %llu\n"
+ "f_namemax: %llu\n",
+ statvfs->f_bsize, statvfs->f_frsize,
+ statvfs->f_blocks, statvfs->f_bfree,
+ statvfs->f_bavail, statvfs->f_files,
+ statvfs->f_ffree, statvfs->f_favail,
+ statvfs->f_fsid, statvfs->f_flag,
+ statvfs->f_namemax);
+ sftp_statvfs_free(statvfs);
+
+ if(!tmp) {
+ result = CURLE_OUT_OF_MEMORY;
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ break;
+ }
+
+ result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
+ free(tmp);
+ if(result) {
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = result;
+ }
+ }
+ state(data, SSH_SFTP_NEXT_QUOTE);
+ break;
+ }
+
+ case SSH_SFTP_GETINFO:
+ if(data->set.get_filetime) {
+ state(data, SSH_SFTP_FILETIME);
+ }
+ else {
+ state(data, SSH_SFTP_TRANS_INIT);
+ }
+ break;
+
+ case SSH_SFTP_FILETIME:
+ {
+ sftp_attributes attrs;
+
+ attrs = sftp_stat(sshc->sftp_session, protop->path);
+ if(attrs) {
+ data->info.filetime = attrs->mtime;
+ sftp_attributes_free(attrs);
+ }
+
+ state(data, SSH_SFTP_TRANS_INIT);
+ break;
+ }
+
+ case SSH_SFTP_TRANS_INIT:
+ if(data->set.upload)
+ state(data, SSH_SFTP_UPLOAD_INIT);
+ else {
+ if(protop->path[strlen(protop->path)-1] == '/')
+ state(data, SSH_SFTP_READDIR_INIT);
+ else
+ state(data, SSH_SFTP_DOWNLOAD_INIT);
+ }
+ break;
+
+ case SSH_SFTP_UPLOAD_INIT:
+ {
+ int flags;
+
+ if(data->state.resume_from) {
+ sftp_attributes attrs;
+
+ if(data->state.resume_from < 0) {
+ attrs = sftp_stat(sshc->sftp_session, protop->path);
+ if(attrs) {
+ curl_off_t size = attrs->size;
+ if(size < 0) {
+ failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
+ MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
+ break;
+ }
+ data->state.resume_from = attrs->size;
+
+ sftp_attributes_free(attrs);
+ }
+ else {
+ data->state.resume_from = 0;
+ }
+ }
+ }
+
+ if(data->set.remote_append)
+ /* Try to open for append, but create if nonexisting */
+ flags = O_WRONLY|O_CREAT|O_APPEND;
+ else if(data->state.resume_from > 0)
+ /* If we have restart position then open for append */
+ flags = O_WRONLY|O_APPEND;
+ else
+ /* Clear file before writing (normal behavior) */
+ flags = O_WRONLY|O_CREAT|O_TRUNC;
+
+ if(sshc->sftp_file)
+ sftp_close(sshc->sftp_file);
+ sshc->sftp_file =
+ sftp_open(sshc->sftp_session, protop->path,
+ flags, (mode_t)data->set.new_file_perms);
+ if(!sshc->sftp_file) {
+ err = sftp_get_error(sshc->sftp_session);
+
+ if(((err == SSH_FX_NO_SUCH_FILE || err == SSH_FX_FAILURE ||
+ err == SSH_FX_NO_SUCH_PATH)) &&
+ (data->set.ftp_create_missing_dirs &&
+ (strlen(protop->path) > 1))) {
+ /* try to create the path remotely */
+ rc = 0;
+ sshc->secondCreateDirs = 1;
+ state(data, SSH_SFTP_CREATE_DIRS_INIT);
+ break;
+ }
+ else {
+ MOVE_TO_SFTP_CLOSE_STATE();
+ break;
+ }
+ }
+
+ /* If we have a restart point then we need to seek to the correct
+ position. */
+ if(data->state.resume_from > 0) {
+ /* Let's read off the proper amount of bytes from the input. */
+ if(conn->seek_func) {
+ Curl_set_in_callback(data, true);
+ seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
+ SEEK_SET);
+ Curl_set_in_callback(data, false);
+ }
+
+ if(seekerr != CURL_SEEKFUNC_OK) {
+ curl_off_t passed = 0;
+
+ if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
+ failf(data, "Could not seek stream");
+ return CURLE_FTP_COULDNT_USE_REST;
+ }
+ /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
+ do {
+ size_t readthisamountnow =
+ (data->state.resume_from - passed > data->set.buffer_size) ?
+ (size_t)data->set.buffer_size :
+ curlx_sotouz(data->state.resume_from - passed);
+
+ size_t actuallyread =
+ data->state.fread_func(data->state.buffer, 1,
+ readthisamountnow, data->state.in);
+
+ passed += actuallyread;
+ if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
+ /* this checks for greater-than only to make sure that the
+ CURL_READFUNC_ABORT return code still aborts */
+ failf(data, "Failed to read data");
+ MOVE_TO_ERROR_STATE(CURLE_FTP_COULDNT_USE_REST);
+ break;
+ }
+ } while(passed < data->state.resume_from);
+ if(rc)
+ break;
+ }
+
+ /* now, decrease the size of the read */
+ if(data->state.infilesize > 0) {
+ data->state.infilesize -= data->state.resume_from;
+ data->req.size = data->state.infilesize;
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+ }
+
+ rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
+ if(rc) {
+ MOVE_TO_SFTP_CLOSE_STATE();
+ break;
+ }
+ }
+ if(data->state.infilesize > 0) {
+ data->req.size = data->state.infilesize;
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+ }
+ /* upload data */
+ Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
+
+ /* not set by Curl_setup_transfer to preserve keepon bits */
+ conn->sockfd = conn->writesockfd;
+
+ /* store this original bitmask setup to use later on if we can't
+ figure out a "real" bitmask */
+ sshc->orig_waitfor = data->req.keepon;
+
+ /* we want to use the _sending_ function even when the socket turns
+ out readable as the underlying libssh sftp send function will deal
+ with both accordingly */
+ conn->cselect_bits = CURL_CSELECT_OUT;
+
+ /* since we don't really wait for anything at this point, we want the
+ state machine to move on as soon as possible so we set a very short
+ timeout here */
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+
+ state(data, SSH_STOP);
+ break;
+ }
+
+ case SSH_SFTP_CREATE_DIRS_INIT:
+ if(strlen(protop->path) > 1) {
+ sshc->slash_pos = protop->path + 1; /* ignore the leading '/' */
+ state(data, SSH_SFTP_CREATE_DIRS);
+ }
+ else {
+ state(data, SSH_SFTP_UPLOAD_INIT);
+ }
+ break;
+
+ case SSH_SFTP_CREATE_DIRS:
+ sshc->slash_pos = strchr(sshc->slash_pos, '/');
+ if(sshc->slash_pos) {
+ *sshc->slash_pos = 0;
+
+ infof(data, "Creating directory '%s'", protop->path);
+ state(data, SSH_SFTP_CREATE_DIRS_MKDIR);
+ break;
+ }
+ state(data, SSH_SFTP_UPLOAD_INIT);
+ break;
+
+ case SSH_SFTP_CREATE_DIRS_MKDIR:
+ /* 'mode' - parameter is preliminary - default to 0644 */
+ rc = sftp_mkdir(sshc->sftp_session, protop->path,
+ (mode_t)data->set.new_directory_perms);
+ *sshc->slash_pos = '/';
+ ++sshc->slash_pos;
+ if(rc < 0) {
+ /*
+ * Abort if failure wasn't that the dir already exists or the
+ * permission was denied (creation might succeed further down the
+ * path) - retry on unspecific FAILURE also
+ */
+ err = sftp_get_error(sshc->sftp_session);
+ if((err != SSH_FX_FILE_ALREADY_EXISTS) &&
+ (err != SSH_FX_FAILURE) &&
+ (err != SSH_FX_PERMISSION_DENIED)) {
+ MOVE_TO_SFTP_CLOSE_STATE();
+ break;
+ }
+ rc = 0; /* clear rc and continue */
+ }
+ state(data, SSH_SFTP_CREATE_DIRS);
+ break;
+
+ case SSH_SFTP_READDIR_INIT:
+ Curl_pgrsSetDownloadSize(data, -1);
+ if(data->req.no_body) {
+ state(data, SSH_STOP);
+ break;
+ }
+
+ /*
+ * This is a directory that we are trying to get, so produce a directory
+ * listing
+ */
+ sshc->sftp_dir = sftp_opendir(sshc->sftp_session,
+ protop->path);
+ if(!sshc->sftp_dir) {
+ failf(data, "Could not open directory for reading: %s",
+ ssh_get_error(sshc->ssh_session));
+ MOVE_TO_SFTP_CLOSE_STATE();
+ break;
+ }
+ state(data, SSH_SFTP_READDIR);
+ break;
+
+ case SSH_SFTP_READDIR:
+
+ if(sshc->readdir_attrs)
+ sftp_attributes_free(sshc->readdir_attrs);
+
+ sshc->readdir_attrs = sftp_readdir(sshc->sftp_session, sshc->sftp_dir);
+ if(sshc->readdir_attrs) {
+ sshc->readdir_filename = sshc->readdir_attrs->name;
+ sshc->readdir_longentry = sshc->readdir_attrs->longname;
+ sshc->readdir_len = strlen(sshc->readdir_filename);
+
+ if(data->set.list_only) {
+ char *tmpLine;
+
+ tmpLine = aprintf("%s\n", sshc->readdir_filename);
+ if(!tmpLine) {
+ state(data, SSH_SFTP_CLOSE);
+ sshc->actualcode = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+ result = Curl_client_write(data, CLIENTWRITE_BODY,
+ tmpLine, sshc->readdir_len + 1);
+ free(tmpLine);
+
+ if(result) {
+ state(data, SSH_STOP);
+ break;
+ }
+ /* since this counts what we send to the client, we include the
+ newline in this counter */
+ data->req.bytecount += sshc->readdir_len + 1;
+
+ /* output debug output if that is requested */
+ Curl_debug(data, CURLINFO_DATA_OUT, (char *)sshc->readdir_filename,
+ sshc->readdir_len);
+ }
+ else {
+ sshc->readdir_currLen = strlen(sshc->readdir_longentry);
+ sshc->readdir_totalLen = 80 + sshc->readdir_currLen;
+ sshc->readdir_line = calloc(sshc->readdir_totalLen, 1);
+ if(!sshc->readdir_line) {
+ state(data, SSH_SFTP_CLOSE);
+ sshc->actualcode = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+
+ memcpy(sshc->readdir_line, sshc->readdir_longentry,
+ sshc->readdir_currLen);
+ if((sshc->readdir_attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
+ ((sshc->readdir_attrs->permissions & SSH_S_IFMT) ==
+ SSH_S_IFLNK)) {
+ sshc->readdir_linkPath = aprintf("%s%s", protop->path,
+ sshc->readdir_filename);
+
+ if(!sshc->readdir_linkPath) {
+ state(data, SSH_SFTP_CLOSE);
+ sshc->actualcode = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+
+ state(data, SSH_SFTP_READDIR_LINK);
+ break;
+ }
+ state(data, SSH_SFTP_READDIR_BOTTOM);
+ break;
+ }
+ }
+ else if(sftp_dir_eof(sshc->sftp_dir)) {
+ state(data, SSH_SFTP_READDIR_DONE);
+ break;
+ }
+ else {
+ failf(data, "Could not open remote file for reading: %s",
+ ssh_get_error(sshc->ssh_session));
+ MOVE_TO_SFTP_CLOSE_STATE();
+ break;
+ }
+ break;
+
+ case SSH_SFTP_READDIR_LINK:
+ if(sshc->readdir_link_attrs)
+ sftp_attributes_free(sshc->readdir_link_attrs);
+
+ sshc->readdir_link_attrs = sftp_lstat(sshc->sftp_session,
+ sshc->readdir_linkPath);
+ if(sshc->readdir_link_attrs == 0) {
+ failf(data, "Could not read symlink for reading: %s",
+ ssh_get_error(sshc->ssh_session));
+ MOVE_TO_SFTP_CLOSE_STATE();
+ break;
+ }
+
+ if(!sshc->readdir_link_attrs->name) {
+ sshc->readdir_tmp = sftp_readlink(sshc->sftp_session,
+ sshc->readdir_linkPath);
+ if(!sshc->readdir_filename)
+ sshc->readdir_len = 0;
+ else
+ sshc->readdir_len = strlen(sshc->readdir_tmp);
+ sshc->readdir_longentry = NULL;
+ sshc->readdir_filename = sshc->readdir_tmp;
+ }
+ else {
+ sshc->readdir_len = strlen(sshc->readdir_link_attrs->name);
+ sshc->readdir_filename = sshc->readdir_link_attrs->name;
+ sshc->readdir_longentry = sshc->readdir_link_attrs->longname;
+ }
+
+ Curl_safefree(sshc->readdir_linkPath);
+
+ /* get room for the filename and extra output */
+ sshc->readdir_totalLen += 4 + sshc->readdir_len;
+ new_readdir_line = Curl_saferealloc(sshc->readdir_line,
+ sshc->readdir_totalLen);
+ if(!new_readdir_line) {
+ sshc->readdir_line = NULL;
+ state(data, SSH_SFTP_CLOSE);
+ sshc->actualcode = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+ sshc->readdir_line = new_readdir_line;
+
+ sshc->readdir_currLen += msnprintf(sshc->readdir_line +
+ sshc->readdir_currLen,
+ sshc->readdir_totalLen -
+ sshc->readdir_currLen,
+ " -> %s",
+ sshc->readdir_filename);
+
+ sftp_attributes_free(sshc->readdir_link_attrs);
+ sshc->readdir_link_attrs = NULL;
+ sshc->readdir_filename = NULL;
+ sshc->readdir_longentry = NULL;
+
+ state(data, SSH_SFTP_READDIR_BOTTOM);
+ /* FALLTHROUGH */
+ case SSH_SFTP_READDIR_BOTTOM:
+ sshc->readdir_currLen += msnprintf(sshc->readdir_line +
+ sshc->readdir_currLen,
+ sshc->readdir_totalLen -
+ sshc->readdir_currLen, "\n");
+ result = Curl_client_write(data, CLIENTWRITE_BODY,
+ sshc->readdir_line,
+ sshc->readdir_currLen);
+
+ if(!result) {
+ /* output debug output if that is requested */
+ Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line,
+ sshc->readdir_currLen);
+ data->req.bytecount += sshc->readdir_currLen;
+ }
+ Curl_safefree(sshc->readdir_line);
+ ssh_string_free_char(sshc->readdir_tmp);
+ sshc->readdir_tmp = NULL;
+
+ if(result) {
+ state(data, SSH_STOP);
+ }
+ else
+ state(data, SSH_SFTP_READDIR);
+ break;
+
+ case SSH_SFTP_READDIR_DONE:
+ sftp_closedir(sshc->sftp_dir);
+ sshc->sftp_dir = NULL;
+
+ /* no data to transfer */
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
+ state(data, SSH_STOP);
+ break;
+
+ case SSH_SFTP_DOWNLOAD_INIT:
+ /*
+ * Work on getting the specified file
+ */
+ if(sshc->sftp_file)
+ sftp_close(sshc->sftp_file);
+
+ sshc->sftp_file = sftp_open(sshc->sftp_session, protop->path,
+ O_RDONLY, (mode_t)data->set.new_file_perms);
+ if(!sshc->sftp_file) {
+ failf(data, "Could not open remote file for reading: %s",
+ ssh_get_error(sshc->ssh_session));
+
+ MOVE_TO_SFTP_CLOSE_STATE();
+ break;
+ }
+
+ state(data, SSH_SFTP_DOWNLOAD_STAT);
+ break;
+
+ case SSH_SFTP_DOWNLOAD_STAT:
+ {
+ sftp_attributes attrs;
+ curl_off_t size;
+
+ attrs = sftp_fstat(sshc->sftp_file);
+ if(!attrs ||
+ !(attrs->flags & SSH_FILEXFER_ATTR_SIZE) ||
+ (attrs->size == 0)) {
+ /*
+ * sftp_fstat didn't return an error, so maybe the server
+ * just doesn't support stat()
+ * OR the server doesn't return a file size with a stat()
+ * OR file size is 0
+ */
+ data->req.size = -1;
+ data->req.maxdownload = -1;
+ Curl_pgrsSetDownloadSize(data, -1);
+ size = 0;
+ }
+ else {
+ size = attrs->size;
+
+ sftp_attributes_free(attrs);
+
+ if(size < 0) {
+ failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
+ return CURLE_BAD_DOWNLOAD_RESUME;
+ }
+ if(data->state.use_range) {
+ curl_off_t from, to;
+ char *ptr;
+ char *ptr2;
+ CURLofft to_t;
+ CURLofft from_t;
+
+ from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from);
+ if(from_t == CURL_OFFT_FLOW) {
+ return CURLE_RANGE_ERROR;
+ }
+ while(*ptr && (ISBLANK(*ptr) || (*ptr == '-')))
+ ptr++;
+ to_t = curlx_strtoofft(ptr, &ptr2, 10, &to);
+ if(to_t == CURL_OFFT_FLOW) {
+ return CURLE_RANGE_ERROR;
+ }
+ if((to_t == CURL_OFFT_INVAL) /* no "to" value given */
+ || (to >= size)) {
+ to = size - 1;
+ }
+ if(from_t) {
+ /* from is relative to end of file */
+ from = size - to;
+ to = size - 1;
+ }
+ if(from > size) {
+ failf(data, "Offset (%"
+ CURL_FORMAT_CURL_OFF_T ") was beyond file size (%"
+ CURL_FORMAT_CURL_OFF_T ")", from, size);
+ return CURLE_BAD_DOWNLOAD_RESUME;
+ }
+ if(from > to) {
+ from = to;
+ size = 0;
+ }
+ else {
+ size = to - from + 1;
+ }
+
+ rc = sftp_seek64(sshc->sftp_file, from);
+ if(rc) {
+ MOVE_TO_SFTP_CLOSE_STATE();
+ break;
+ }
+ }
+ data->req.size = size;
+ data->req.maxdownload = size;
+ Curl_pgrsSetDownloadSize(data, size);
+ }
+
+ /* We can resume if we can seek to the resume position */
+ if(data->state.resume_from) {
+ if(data->state.resume_from < 0) {
+ /* We're supposed to download the last abs(from) bytes */
+ if((curl_off_t)size < -data->state.resume_from) {
+ failf(data, "Offset (%"
+ CURL_FORMAT_CURL_OFF_T ") was beyond file size (%"
+ CURL_FORMAT_CURL_OFF_T ")",
+ data->state.resume_from, size);
+ return CURLE_BAD_DOWNLOAD_RESUME;
+ }
+ /* download from where? */
+ data->state.resume_from += size;
+ }
+ else {
+ if((curl_off_t)size < data->state.resume_from) {
+ failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
+ ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
+ data->state.resume_from, size);
+ return CURLE_BAD_DOWNLOAD_RESUME;
+ }
+ }
+ /* Now store the number of bytes we are expected to download */
+ data->req.size = size - data->state.resume_from;
+ data->req.maxdownload = size - data->state.resume_from;
+ Curl_pgrsSetDownloadSize(data,
+ size - data->state.resume_from);
+
+ rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
+ if(rc) {
+ MOVE_TO_SFTP_CLOSE_STATE();
+ break;
+ }
+ }
+ }
+
+ /* Setup the actual download */
+ if(data->req.size == 0) {
+ /* no data to transfer */
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
+ infof(data, "File already completely downloaded");
+ state(data, SSH_STOP);
+ break;
+ }
+ Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1);
+
+ /* not set by Curl_setup_transfer to preserve keepon bits */
+ conn->writesockfd = conn->sockfd;
+
+ /* we want to use the _receiving_ function even when the socket turns
+ out writableable as the underlying libssh recv function will deal
+ with both accordingly */
+ conn->cselect_bits = CURL_CSELECT_IN;
+
+ if(result) {
+ /* this should never occur; the close state should be entered
+ at the time the error occurs */
+ state(data, SSH_SFTP_CLOSE);
+ sshc->actualcode = result;
+ }
+ else {
+ sshc->sftp_recv_state = 0;
+ state(data, SSH_STOP);
+ }
+ break;
+
+ case SSH_SFTP_CLOSE:
+ if(sshc->sftp_file) {
+ sftp_close(sshc->sftp_file);
+ sshc->sftp_file = NULL;
+ }
+ Curl_safefree(protop->path);
+
+ DEBUGF(infof(data, "SFTP DONE done"));
+
+ /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT
+ After nextstate is executed, the control should come back to
+ SSH_SFTP_CLOSE to pass the correct result back */
+ if(sshc->nextstate != SSH_NO_STATE &&
+ sshc->nextstate != SSH_SFTP_CLOSE) {
+ state(data, sshc->nextstate);
+ sshc->nextstate = SSH_SFTP_CLOSE;
+ }
+ else {
+ state(data, SSH_STOP);
+ result = sshc->actualcode;
+ }
+ break;
+
+ case SSH_SFTP_SHUTDOWN:
+ /* during times we get here due to a broken transfer and then the
+ sftp_handle might not have been taken down so make sure that is done
+ before we proceed */
+
+ if(sshc->sftp_file) {
+ sftp_close(sshc->sftp_file);
+ sshc->sftp_file = NULL;
+ }
+
+ if(sshc->sftp_session) {
+ sftp_free(sshc->sftp_session);
+ sshc->sftp_session = NULL;
+ }
+
+ SSH_STRING_FREE_CHAR(sshc->homedir);
+ data->state.most_recent_ftp_entrypath = NULL;
+
+ state(data, SSH_SESSION_DISCONNECT);
+ break;
+
+ case SSH_SCP_TRANS_INIT:
+ result = Curl_getworkingpath(data, sshc->homedir, &protop->path);
+ if(result) {
+ sshc->actualcode = result;
+ state(data, SSH_STOP);
+ break;
+ }
+
+ /* Functions from the SCP subsystem cannot handle/return SSH_AGAIN */
+ ssh_set_blocking(sshc->ssh_session, 1);
+
+ if(data->set.upload) {
+ if(data->state.infilesize < 0) {
+ failf(data, "SCP requires a known file size for upload");
+ sshc->actualcode = CURLE_UPLOAD_FAILED;
+ MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED);
+ break;
+ }
+
+ sshc->scp_session =
+ ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, protop->path);
+ state(data, SSH_SCP_UPLOAD_INIT);
+ }
+ else {
+ sshc->scp_session =
+ ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, protop->path);
+ state(data, SSH_SCP_DOWNLOAD_INIT);
+ }
+
+ if(!sshc->scp_session) {
+ err_msg = ssh_get_error(sshc->ssh_session);
+ failf(data, "%s", err_msg);
+ MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED);
+ }
+
+ break;
+
+ case SSH_SCP_UPLOAD_INIT:
+
+ rc = ssh_scp_init(sshc->scp_session);
+ if(rc != SSH_OK) {
+ err_msg = ssh_get_error(sshc->ssh_session);
+ failf(data, "%s", err_msg);
+ MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED);
+ break;
+ }
+
+ rc = ssh_scp_push_file(sshc->scp_session, protop->path,
+ data->state.infilesize,
+ (int)data->set.new_file_perms);
+ if(rc != SSH_OK) {
+ err_msg = ssh_get_error(sshc->ssh_session);
+ failf(data, "%s", err_msg);
+ MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED);
+ break;
+ }
+
+ /* upload data */
+ Curl_setup_transfer(data, -1, data->req.size, FALSE, FIRSTSOCKET);
+
+ /* not set by Curl_setup_transfer to preserve keepon bits */
+ conn->sockfd = conn->writesockfd;
+
+ /* store this original bitmask setup to use later on if we can't
+ figure out a "real" bitmask */
+ sshc->orig_waitfor = data->req.keepon;
+
+ /* we want to use the _sending_ function even when the socket turns
+ out readable as the underlying libssh scp send function will deal
+ with both accordingly */
+ conn->cselect_bits = CURL_CSELECT_OUT;
+
+ state(data, SSH_STOP);
+
+ break;
+
+ case SSH_SCP_DOWNLOAD_INIT:
+
+ rc = ssh_scp_init(sshc->scp_session);
+ if(rc != SSH_OK) {
+ err_msg = ssh_get_error(sshc->ssh_session);
+ failf(data, "%s", err_msg);
+ MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT);
+ break;
+ }
+ state(data, SSH_SCP_DOWNLOAD);
+ /* FALLTHROUGH */
+
+ case SSH_SCP_DOWNLOAD:{
+ curl_off_t bytecount;
+
+ rc = ssh_scp_pull_request(sshc->scp_session);
+ if(rc != SSH_SCP_REQUEST_NEWFILE) {
+ err_msg = ssh_get_error(sshc->ssh_session);
+ failf(data, "%s", err_msg);
+ MOVE_TO_ERROR_STATE(CURLE_REMOTE_FILE_NOT_FOUND);
+ break;
+ }
+
+ /* download data */
+ bytecount = ssh_scp_request_get_size(sshc->scp_session);
+ data->req.maxdownload = (curl_off_t) bytecount;
+ Curl_setup_transfer(data, FIRSTSOCKET, bytecount, FALSE, -1);
+
+ /* not set by Curl_setup_transfer to preserve keepon bits */
+ conn->writesockfd = conn->sockfd;
+
+ /* we want to use the _receiving_ function even when the socket turns
+ out writableable as the underlying libssh recv function will deal
+ with both accordingly */
+ conn->cselect_bits = CURL_CSELECT_IN;
+
+ state(data, SSH_STOP);
+ break;
+ }
+ case SSH_SCP_DONE:
+ if(data->set.upload)
+ state(data, SSH_SCP_SEND_EOF);
+ else
+ state(data, SSH_SCP_CHANNEL_FREE);
+ break;
+
+ case SSH_SCP_SEND_EOF:
+ if(sshc->scp_session) {
+ rc = ssh_scp_close(sshc->scp_session);
+ if(rc == SSH_AGAIN) {
+ /* Currently the ssh_scp_close handles waiting for EOF in
+ * blocking way.
+ */
+ break;
+ }
+ if(rc != SSH_OK) {
+ infof(data, "Failed to close libssh scp channel: %s",
+ ssh_get_error(sshc->ssh_session));
+ }
+ }
+
+ state(data, SSH_SCP_CHANNEL_FREE);
+ break;
+
+ case SSH_SCP_CHANNEL_FREE:
+ if(sshc->scp_session) {
+ ssh_scp_free(sshc->scp_session);
+ sshc->scp_session = NULL;
+ }
+ DEBUGF(infof(data, "SCP DONE phase complete"));
+
+ ssh_set_blocking(sshc->ssh_session, 0);
+
+ state(data, SSH_SESSION_DISCONNECT);
+ /* FALLTHROUGH */
+
+ case SSH_SESSION_DISCONNECT:
+ /* during weird times when we've been prematurely aborted, the channel
+ is still alive when we reach this state and we MUST kill the channel
+ properly first */
+ if(sshc->scp_session) {
+ ssh_scp_free(sshc->scp_session);
+ sshc->scp_session = NULL;
+ }
+
+ ssh_disconnect(sshc->ssh_session);
+ if(!ssh_version(SSH_VERSION_INT(0, 10, 0))) {
+ /* conn->sock[FIRSTSOCKET] is closed by ssh_disconnect behind our back,
+ explicitly mark it as closed with the memdebug macro. This libssh
+ bug is fixed in 0.10.0. */
+ fake_sclose(conn->sock[FIRSTSOCKET]);
+ conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD;
+ }
+
+ SSH_STRING_FREE_CHAR(sshc->homedir);
+ data->state.most_recent_ftp_entrypath = NULL;
+
+ state(data, SSH_SESSION_FREE);
+ /* FALLTHROUGH */
+ case SSH_SESSION_FREE:
+ if(sshc->ssh_session) {
+ ssh_free(sshc->ssh_session);
+ sshc->ssh_session = NULL;
+ }
+
+ /* worst-case scenario cleanup */
+
+ DEBUGASSERT(sshc->ssh_session == NULL);
+ DEBUGASSERT(sshc->scp_session == NULL);
+
+ if(sshc->readdir_tmp) {
+ ssh_string_free_char(sshc->readdir_tmp);
+ sshc->readdir_tmp = NULL;
+ }
+
+ if(sshc->quote_attrs)
+ sftp_attributes_free(sshc->quote_attrs);
+
+ if(sshc->readdir_attrs)
+ sftp_attributes_free(sshc->readdir_attrs);
+
+ if(sshc->readdir_link_attrs)
+ sftp_attributes_free(sshc->readdir_link_attrs);
+
+ if(sshc->privkey)
+ ssh_key_free(sshc->privkey);
+ if(sshc->pubkey)
+ ssh_key_free(sshc->pubkey);
+
+ Curl_safefree(sshc->rsa_pub);
+ Curl_safefree(sshc->rsa);
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ Curl_safefree(sshc->readdir_line);
+ Curl_safefree(sshc->readdir_linkPath);
+ SSH_STRING_FREE_CHAR(sshc->homedir);
+
+ /* the code we are about to return */
+ result = sshc->actualcode;
+
+ memset(sshc, 0, sizeof(struct ssh_conn));
+
+ connclose(conn, "SSH session free");
+ sshc->state = SSH_SESSION_FREE; /* current */
+ sshc->nextstate = SSH_NO_STATE;
+ state(data, SSH_STOP);
+ break;
+
+ case SSH_QUIT:
+ /* fallthrough, just stop! */
+ default:
+ /* internal error */
+ sshc->nextstate = SSH_NO_STATE;
+ state(data, SSH_STOP);
+ break;
+
+ }
+ } while(!rc && (sshc->state != SSH_STOP));
+
+
+ if(rc == SSH_AGAIN) {
+ /* we would block, we need to wait for the socket to be ready (in the
+ right direction too)! */
+ *block = TRUE;
+ }
+
+ return result;
+}
+
+
+/* called by the multi interface to figure out what socket(s) to wait for and
+ for what actions in the DO_DONE, PERFORM and WAITPERFORM states */
+static int myssh_getsock(struct Curl_easy *data,
+ struct connectdata *conn,
+ curl_socket_t *sock)
+{
+ int bitmap = GETSOCK_BLANK;
+ (void)data;
+ sock[0] = conn->sock[FIRSTSOCKET];
+
+ if(conn->waitfor & KEEP_RECV)
+ bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
+
+ if(conn->waitfor & KEEP_SEND)
+ bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
+
+ if(!conn->waitfor)
+ bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
+
+ return bitmap;
+}
+
+static void myssh_block2waitfor(struct connectdata *conn, bool block)
+{
+ struct ssh_conn *sshc = &conn->proto.sshc;
+
+ /* If it didn't block, or nothing was returned by ssh_get_poll_flags
+ * have the original set */
+ conn->waitfor = sshc->orig_waitfor;
+
+ if(block) {
+ int dir = ssh_get_poll_flags(sshc->ssh_session);
+ if(dir & SSH_READ_PENDING) {
+ /* translate the libssh define bits into our own bit defines */
+ conn->waitfor = KEEP_RECV;
+ }
+ else if(dir & SSH_WRITE_PENDING) {
+ conn->waitfor = KEEP_SEND;
+ }
+ }
+}
+
+/* called repeatedly until done from multi.c */
+static CURLcode myssh_multi_statemach(struct Curl_easy *data,
+ bool *done)
+{
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ bool block; /* we store the status and use that to provide a ssh_getsock()
+ implementation */
+ CURLcode result = myssh_statemach_act(data, &block);
+
+ *done = (sshc->state == SSH_STOP) ? TRUE : FALSE;
+ myssh_block2waitfor(conn, block);
+
+ return result;
+}
+
+static CURLcode myssh_block_statemach(struct Curl_easy *data,
+ bool disconnect)
+{
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ CURLcode result = CURLE_OK;
+
+ while((sshc->state != SSH_STOP) && !result) {
+ bool block;
+ timediff_t left = 1000;
+ struct curltime now = Curl_now();
+
+ result = myssh_statemach_act(data, &block);
+ if(result)
+ break;
+
+ if(!disconnect) {
+ if(Curl_pgrsUpdate(data))
+ return CURLE_ABORTED_BY_CALLBACK;
+
+ result = Curl_speedcheck(data, now);
+ if(result)
+ break;
+
+ left = Curl_timeleft(data, NULL, FALSE);
+ if(left < 0) {
+ failf(data, "Operation timed out");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+ }
+
+ if(block) {
+ curl_socket_t fd_read = conn->sock[FIRSTSOCKET];
+ /* wait for the socket to become ready */
+ (void) Curl_socket_check(fd_read, CURL_SOCKET_BAD,
+ CURL_SOCKET_BAD, left > 1000 ? 1000 : left);
+ }
+
+ }
+
+ return result;
+}
+
+/*
+ * SSH setup connection
+ */
+static CURLcode myssh_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ struct SSHPROTO *ssh;
+ (void)conn;
+
+ data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
+ if(!ssh)
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_OK;
+}
+
+static Curl_recv scp_recv, sftp_recv;
+static Curl_send scp_send, sftp_send;
+
+/*
+ * Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to
+ * do protocol-specific actions at connect-time.
+ */
+static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
+{
+ struct ssh_conn *ssh;
+ CURLcode result;
+ struct connectdata *conn = data->conn;
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ int rc;
+
+ /* initialize per-handle data if not already */
+ if(!data->req.p.ssh)
+ myssh_setup_connection(data, conn);
+
+ /* We default to persistent connections. We set this already in this connect
+ function to make the re-use checks properly be able to check this bit. */
+ connkeep(conn, "SSH default");
+
+ if(conn->handler->protocol & CURLPROTO_SCP) {
+ conn->recv[FIRSTSOCKET] = scp_recv;
+ conn->send[FIRSTSOCKET] = scp_send;
+ }
+ else {
+ conn->recv[FIRSTSOCKET] = sftp_recv;
+ conn->send[FIRSTSOCKET] = sftp_send;
+ }
+
+ ssh = &conn->proto.sshc;
+
+ ssh->ssh_session = ssh_new();
+ if(!ssh->ssh_session) {
+ failf(data, "Failure initialising ssh session");
+ return CURLE_FAILED_INIT;
+ }
+
+ rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_HOST, conn->host.name);
+ if(rc != SSH_OK) {
+ failf(data, "Could not set remote host");
+ return CURLE_FAILED_INIT;
+ }
+
+ rc = ssh_options_parse_config(ssh->ssh_session, NULL);
+ if(rc != SSH_OK) {
+ infof(data, "Could not parse SSH configuration files");
+ /* ignore */
+ }
+
+ rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_FD, &sock);
+ if(rc != SSH_OK) {
+ failf(data, "Could not set socket");
+ return CURLE_FAILED_INIT;
+ }
+
+ if(conn->user && conn->user[0] != '\0') {
+ infof(data, "User: %s", conn->user);
+ rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_USER, conn->user);
+ if(rc != SSH_OK) {
+ failf(data, "Could not set user");
+ return CURLE_FAILED_INIT;
+ }
+ }
+
+ if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
+ infof(data, "Known hosts: %s", data->set.str[STRING_SSH_KNOWNHOSTS]);
+ rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_KNOWNHOSTS,
+ data->set.str[STRING_SSH_KNOWNHOSTS]);
+ if(rc != SSH_OK) {
+ failf(data, "Could not set known hosts file path");
+ return CURLE_FAILED_INIT;
+ }
+ }
+
+ if(conn->remote_port) {
+ rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_PORT,
+ &conn->remote_port);
+ if(rc != SSH_OK) {
+ failf(data, "Could not set remote port");
+ return CURLE_FAILED_INIT;
+ }
+ }
+
+ if(data->set.ssh_compression) {
+ rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_COMPRESSION,
+ "zlib,zlib@openssh.com,none");
+ if(rc != SSH_OK) {
+ failf(data, "Could not set compression");
+ return CURLE_FAILED_INIT;
+ }
+ }
+
+ ssh->privkey = NULL;
+ ssh->pubkey = NULL;
+
+ if(data->set.str[STRING_SSH_PUBLIC_KEY]) {
+ rc = ssh_pki_import_pubkey_file(data->set.str[STRING_SSH_PUBLIC_KEY],
+ &ssh->pubkey);
+ if(rc != SSH_OK) {
+ failf(data, "Could not load public key file");
+ return CURLE_FAILED_INIT;
+ }
+ }
+
+ /* we do not verify here, we do it at the state machine,
+ * after connection */
+
+ state(data, SSH_INIT);
+
+ result = myssh_multi_statemach(data, done);
+
+ return result;
+}
+
+/* called from multi.c while DOing */
+static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done)
+{
+ CURLcode result;
+
+ result = myssh_multi_statemach(data, dophase_done);
+
+ if(*dophase_done) {
+ DEBUGF(infof(data, "DO phase is complete"));
+ }
+ return result;
+}
+
+/*
+ ***********************************************************************
+ *
+ * scp_perform()
+ *
+ * This is the actual DO function for SCP. Get a file according to
+ * the options previously setup.
+ */
+
+static
+CURLcode scp_perform(struct Curl_easy *data,
+ bool *connected, bool *dophase_done)
+{
+ CURLcode result = CURLE_OK;
+
+ DEBUGF(infof(data, "DO phase starts"));
+
+ *dophase_done = FALSE; /* not done yet */
+
+ /* start the first command in the DO phase */
+ state(data, SSH_SCP_TRANS_INIT);
+
+ result = myssh_multi_statemach(data, dophase_done);
+
+ *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
+
+ if(*dophase_done) {
+ DEBUGF(infof(data, "DO phase is complete"));
+ }
+
+ return result;
+}
+
+static CURLcode myssh_do_it(struct Curl_easy *data, bool *done)
+{
+ CURLcode result;
+ bool connected = 0;
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+
+ *done = FALSE; /* default to false */
+
+ data->req.size = -1; /* make sure this is unknown at this point */
+
+ sshc->actualcode = CURLE_OK; /* reset error code */
+ sshc->secondCreateDirs = 0; /* reset the create dir attempt state
+ variable */
+
+ Curl_pgrsSetUploadCounter(data, 0);
+ Curl_pgrsSetDownloadCounter(data, 0);
+ Curl_pgrsSetUploadSize(data, -1);
+ Curl_pgrsSetDownloadSize(data, -1);
+
+ if(conn->handler->protocol & CURLPROTO_SCP)
+ result = scp_perform(data, &connected, done);
+ else
+ result = sftp_perform(data, &connected, done);
+
+ return result;
+}
+
+/* BLOCKING, but the function is using the state machine so the only reason
+ this is still blocking is that the multi interface code has no support for
+ disconnecting operations that takes a while */
+static CURLcode scp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection)
+{
+ CURLcode result = CURLE_OK;
+ struct ssh_conn *ssh = &conn->proto.sshc;
+ (void) dead_connection;
+
+ if(ssh->ssh_session) {
+ /* only if there's a session still around to use! */
+
+ state(data, SSH_SESSION_DISCONNECT);
+
+ result = myssh_block_statemach(data, TRUE);
+ }
+
+ return result;
+}
+
+/* generic done function for both SCP and SFTP called from their specific
+ done functions */
+static CURLcode myssh_done(struct Curl_easy *data, CURLcode status)
+{
+ CURLcode result = CURLE_OK;
+ struct SSHPROTO *protop = data->req.p.ssh;
+
+ if(!status) {
+ /* run the state-machine */
+ result = myssh_block_statemach(data, FALSE);
+ }
+ else
+ result = status;
+
+ if(protop)
+ Curl_safefree(protop->path);
+ if(Curl_pgrsDone(data))
+ return CURLE_ABORTED_BY_CALLBACK;
+
+ data->req.keepon = 0; /* clear all bits */
+ return result;
+}
+
+
+static CURLcode scp_done(struct Curl_easy *data, CURLcode status,
+ bool premature)
+{
+ (void) premature; /* not used */
+
+ if(!status)
+ state(data, SSH_SCP_DONE);
+
+ return myssh_done(data, status);
+
+}
+
+static ssize_t scp_send(struct Curl_easy *data, int sockindex,
+ const void *mem, size_t len, CURLcode *err)
+{
+ int rc;
+ struct connectdata *conn = data->conn;
+ (void) sockindex; /* we only support SCP on the fixed known primary socket */
+ (void) err;
+
+ rc = ssh_scp_write(conn->proto.sshc.scp_session, mem, len);
+
+#if 0
+ /* The following code is misleading, mostly added as wishful thinking
+ * that libssh at some point will implement non-blocking ssh_scp_write/read.
+ * Currently rc can only be number of bytes read or SSH_ERROR. */
+ myssh_block2waitfor(conn, (rc == SSH_AGAIN) ? TRUE : FALSE);
+
+ if(rc == SSH_AGAIN) {
+ *err = CURLE_AGAIN;
+ return 0;
+ }
+ else
+#endif
+ if(rc != SSH_OK) {
+ *err = CURLE_SSH;
+ return -1;
+ }
+
+ return len;
+}
+
+static ssize_t scp_recv(struct Curl_easy *data, int sockindex,
+ char *mem, size_t len, CURLcode *err)
+{
+ ssize_t nread;
+ struct connectdata *conn = data->conn;
+ (void) err;
+ (void) sockindex; /* we only support SCP on the fixed known primary socket */
+
+ /* libssh returns int */
+ nread = ssh_scp_read(conn->proto.sshc.scp_session, mem, len);
+
+#if 0
+ /* The following code is misleading, mostly added as wishful thinking
+ * that libssh at some point will implement non-blocking ssh_scp_write/read.
+ * Currently rc can only be SSH_OK or SSH_ERROR. */
+
+ myssh_block2waitfor(conn, (nread == SSH_AGAIN) ? TRUE : FALSE);
+ if(nread == SSH_AGAIN) {
+ *err = CURLE_AGAIN;
+ nread = -1;
+ }
+#endif
+
+ return nread;
+}
+
+/*
+ * =============== SFTP ===============
+ */
+
+/*
+ ***********************************************************************
+ *
+ * sftp_perform()
+ *
+ * This is the actual DO function for SFTP. Get a file/directory according to
+ * the options previously setup.
+ */
+
+static
+CURLcode sftp_perform(struct Curl_easy *data,
+ bool *connected,
+ bool *dophase_done)
+{
+ CURLcode result = CURLE_OK;
+
+ DEBUGF(infof(data, "DO phase starts"));
+
+ *dophase_done = FALSE; /* not done yet */
+
+ /* start the first command in the DO phase */
+ state(data, SSH_SFTP_QUOTE_INIT);
+
+ /* run the state-machine */
+ result = myssh_multi_statemach(data, dophase_done);
+
+ *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
+
+ if(*dophase_done) {
+ DEBUGF(infof(data, "DO phase is complete"));
+ }
+
+ return result;
+}
+
+/* called from multi.c while DOing */
+static CURLcode sftp_doing(struct Curl_easy *data,
+ bool *dophase_done)
+{
+ CURLcode result = myssh_multi_statemach(data, dophase_done);
+ if(*dophase_done) {
+ DEBUGF(infof(data, "DO phase is complete"));
+ }
+ return result;
+}
+
+/* BLOCKING, but the function is using the state machine so the only reason
+ this is still blocking is that the multi interface code has no support for
+ disconnecting operations that takes a while */
+static CURLcode sftp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection)
+{
+ CURLcode result = CURLE_OK;
+ (void) dead_connection;
+
+ DEBUGF(infof(data, "SSH DISCONNECT starts now"));
+
+ if(conn->proto.sshc.ssh_session) {
+ /* only if there's a session still around to use! */
+ state(data, SSH_SFTP_SHUTDOWN);
+ result = myssh_block_statemach(data, TRUE);
+ }
+
+ DEBUGF(infof(data, "SSH DISCONNECT is done"));
+
+ return result;
+
+}
+
+static CURLcode sftp_done(struct Curl_easy *data, CURLcode status,
+ bool premature)
+{
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+
+ if(!status) {
+ /* Post quote commands are executed after the SFTP_CLOSE state to avoid
+ errors that could happen due to open file handles during POSTQUOTE
+ operation */
+ if(!premature && data->set.postquote && !conn->bits.retry)
+ sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT;
+ state(data, SSH_SFTP_CLOSE);
+ }
+ return myssh_done(data, status);
+}
+
+/* return number of sent bytes */
+static ssize_t sftp_send(struct Curl_easy *data, int sockindex,
+ const void *mem, size_t len, CURLcode *err)
+{
+ ssize_t nwrite;
+ struct connectdata *conn = data->conn;
+ (void)sockindex;
+
+ nwrite = sftp_write(conn->proto.sshc.sftp_file, mem, len);
+
+ myssh_block2waitfor(conn, FALSE);
+
+#if 0 /* not returned by libssh on write */
+ if(nwrite == SSH_AGAIN) {
+ *err = CURLE_AGAIN;
+ nwrite = 0;
+ }
+ else
+#endif
+ if(nwrite < 0) {
+ *err = CURLE_SSH;
+ nwrite = -1;
+ }
+
+ return nwrite;
+}
+
+/*
+ * Return number of received (decrypted) bytes
+ * or <0 on error
+ */
+static ssize_t sftp_recv(struct Curl_easy *data, int sockindex,
+ char *mem, size_t len, CURLcode *err)
+{
+ ssize_t nread;
+ struct connectdata *conn = data->conn;
+ (void)sockindex;
+
+ DEBUGASSERT(len < CURL_MAX_READ_SIZE);
+
+ switch(conn->proto.sshc.sftp_recv_state) {
+ case 0:
+ conn->proto.sshc.sftp_file_index =
+ sftp_async_read_begin(conn->proto.sshc.sftp_file,
+ (uint32_t)len);
+ if(conn->proto.sshc.sftp_file_index < 0) {
+ *err = CURLE_RECV_ERROR;
+ return -1;
+ }
+
+ /* FALLTHROUGH */
+ case 1:
+ conn->proto.sshc.sftp_recv_state = 1;
+
+ nread = sftp_async_read(conn->proto.sshc.sftp_file,
+ mem, (uint32_t)len,
+ conn->proto.sshc.sftp_file_index);
+
+ myssh_block2waitfor(conn, (nread == SSH_AGAIN)?TRUE:FALSE);
+
+ if(nread == SSH_AGAIN) {
+ *err = CURLE_AGAIN;
+ return -1;
+ }
+ else if(nread < 0) {
+ *err = CURLE_RECV_ERROR;
+ return -1;
+ }
+
+ conn->proto.sshc.sftp_recv_state = 0;
+ return nread;
+
+ default:
+ /* we never reach here */
+ return -1;
+ }
+}
+
+static void sftp_quote(struct Curl_easy *data)
+{
+ const char *cp;
+ struct connectdata *conn = data->conn;
+ struct SSHPROTO *protop = data->req.p.ssh;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ CURLcode result;
+
+ /*
+ * Support some of the "FTP" commands
+ */
+ char *cmd = sshc->quote_item->data;
+ sshc->acceptfail = FALSE;
+
+ /* if a command starts with an asterisk, which a legal SFTP command never
+ can, the command will be allowed to fail without it causing any
+ aborts or cancels etc. It will cause libcurl to act as if the command
+ is successful, whatever the server reponds. */
+
+ if(cmd[0] == '*') {
+ cmd++;
+ sshc->acceptfail = TRUE;
+ }
+
+ if(strcasecompare("pwd", cmd)) {
+ /* output debug output if that is requested */
+ char *tmp = aprintf("257 \"%s\" is current directory.\n",
+ protop->path);
+ if(!tmp) {
+ sshc->actualcode = CURLE_OUT_OF_MEMORY;
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ return;
+ }
+ Curl_debug(data, CURLINFO_HEADER_OUT, (char *) "PWD\n", 4);
+ Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp));
+
+ /* this sends an FTP-like "header" to the header callback so that the
+ current directory can be read very similar to how it is read when
+ using ordinary FTP. */
+ result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
+ free(tmp);
+ if(result) {
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = result;
+ }
+ else
+ state(data, SSH_SFTP_NEXT_QUOTE);
+ return;
+ }
+
+ /*
+ * the arguments following the command must be separated from the
+ * command with a space so we can check for it unconditionally
+ */
+ cp = strchr(cmd, ' ');
+ if(!cp) {
+ failf(data, "Syntax error in SFTP command. Supply parameter(s)");
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ return;
+ }
+
+ /*
+ * also, every command takes at least one argument so we get that
+ * first argument right now
+ */
+ result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir);
+ if(result) {
+ if(result == CURLE_OUT_OF_MEMORY)
+ failf(data, "Out of memory");
+ else
+ failf(data, "Syntax error: Bad first parameter");
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = result;
+ return;
+ }
+
+ /*
+ * SFTP is a binary protocol, so we don't send text commands
+ * to the server. Instead, we scan for commands used by
+ * OpenSSH's sftp program and call the appropriate libssh
+ * functions.
+ */
+ if(strncasecompare(cmd, "chgrp ", 6) ||
+ strncasecompare(cmd, "chmod ", 6) ||
+ strncasecompare(cmd, "chown ", 6) ||
+ strncasecompare(cmd, "atime ", 6) ||
+ strncasecompare(cmd, "mtime ", 6)) {
+ /* attribute change */
+
+ /* sshc->quote_path1 contains the mode to set */
+ /* get the destination */
+ result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
+ if(result) {
+ if(result == CURLE_OUT_OF_MEMORY)
+ failf(data, "Out of memory");
+ else
+ failf(data, "Syntax error in chgrp/chmod/chown/atime/mtime: "
+ "Bad second parameter");
+ Curl_safefree(sshc->quote_path1);
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = result;
+ return;
+ }
+ sshc->quote_attrs = NULL;
+ state(data, SSH_SFTP_QUOTE_STAT);
+ return;
+ }
+ if(strncasecompare(cmd, "ln ", 3) ||
+ strncasecompare(cmd, "symlink ", 8)) {
+ /* symbolic linking */
+ /* sshc->quote_path1 is the source */
+ /* get the destination */
+ result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
+ if(result) {
+ if(result == CURLE_OUT_OF_MEMORY)
+ failf(data, "Out of memory");
+ else
+ failf(data, "Syntax error in ln/symlink: Bad second parameter");
+ Curl_safefree(sshc->quote_path1);
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = result;
+ return;
+ }
+ state(data, SSH_SFTP_QUOTE_SYMLINK);
+ return;
+ }
+ else if(strncasecompare(cmd, "mkdir ", 6)) {
+ /* create dir */
+ state(data, SSH_SFTP_QUOTE_MKDIR);
+ return;
+ }
+ else if(strncasecompare(cmd, "rename ", 7)) {
+ /* rename file */
+ /* first param is the source path */
+ /* second param is the dest. path */
+ result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
+ if(result) {
+ if(result == CURLE_OUT_OF_MEMORY)
+ failf(data, "Out of memory");
+ else
+ failf(data, "Syntax error in rename: Bad second parameter");
+ Curl_safefree(sshc->quote_path1);
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = result;
+ return;
+ }
+ state(data, SSH_SFTP_QUOTE_RENAME);
+ return;
+ }
+ else if(strncasecompare(cmd, "rmdir ", 6)) {
+ /* delete dir */
+ state(data, SSH_SFTP_QUOTE_RMDIR);
+ return;
+ }
+ else if(strncasecompare(cmd, "rm ", 3)) {
+ state(data, SSH_SFTP_QUOTE_UNLINK);
+ return;
+ }
+#ifdef HAS_STATVFS_SUPPORT
+ else if(strncasecompare(cmd, "statvfs ", 8)) {
+ state(data, SSH_SFTP_QUOTE_STATVFS);
+ return;
+ }
+#endif
+
+ failf(data, "Unknown SFTP command");
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+}
+
+static void sftp_quote_stat(struct Curl_easy *data)
+{
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ char *cmd = sshc->quote_item->data;
+ sshc->acceptfail = FALSE;
+
+ /* if a command starts with an asterisk, which a legal SFTP command never
+ can, the command will be allowed to fail without it causing any
+ aborts or cancels etc. It will cause libcurl to act as if the command
+ is successful, whatever the server reponds. */
+
+ if(cmd[0] == '*') {
+ cmd++;
+ sshc->acceptfail = TRUE;
+ }
+
+ /* We read the file attributes, store them in sshc->quote_attrs
+ * and modify them accordingly to command. Then we switch to
+ * QUOTE_SETSTAT state to write new ones.
+ */
+
+ if(sshc->quote_attrs)
+ sftp_attributes_free(sshc->quote_attrs);
+ sshc->quote_attrs = sftp_stat(sshc->sftp_session, sshc->quote_path2);
+ if(!sshc->quote_attrs) {
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ failf(data, "Attempt to get SFTP stats failed: %d",
+ sftp_get_error(sshc->sftp_session));
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ return;
+ }
+
+ /* Now set the new attributes... */
+ if(strncasecompare(cmd, "chgrp", 5)) {
+ sshc->quote_attrs->gid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10);
+ if(sshc->quote_attrs->gid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
+ !sshc->acceptfail) {
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ failf(data, "Syntax error: chgrp gid not a number");
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ return;
+ }
+ sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID;
+ }
+ else if(strncasecompare(cmd, "chmod", 5)) {
+ mode_t perms;
+ perms = (mode_t)strtoul(sshc->quote_path1, NULL, 8);
+ /* permissions are octal */
+ if(perms == 0 && !ISDIGIT(sshc->quote_path1[0])) {
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ failf(data, "Syntax error: chmod permissions not a number");
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ return;
+ }
+ sshc->quote_attrs->permissions = perms;
+ sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
+ }
+ else if(strncasecompare(cmd, "chown", 5)) {
+ sshc->quote_attrs->uid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10);
+ if(sshc->quote_attrs->uid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
+ !sshc->acceptfail) {
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ failf(data, "Syntax error: chown uid not a number");
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ return;
+ }
+ sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID;
+ }
+ else if(strncasecompare(cmd, "atime", 5) ||
+ strncasecompare(cmd, "mtime", 5)) {
+ time_t date = Curl_getdate_capped(sshc->quote_path1);
+ bool fail = FALSE;
+ if(date == -1) {
+ failf(data, "incorrect date format for %.*s", 5, cmd);
+ fail = TRUE;
+ }
+#if SIZEOF_TIME_T > 4
+ else if(date > 0xffffffff) {
+ failf(data, "date overflow");
+ fail = TRUE; /* avoid setting a capped time */
+ }
+#endif
+ if(fail) {
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ return;
+ }
+ if(strncasecompare(cmd, "atime", 5))
+ sshc->quote_attrs->atime = (uint32_t)date;
+ else /* mtime */
+ sshc->quote_attrs->mtime = (uint32_t)date;
+
+ sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_ACMODTIME;
+ }
+
+ /* Now send the completed structure... */
+ state(data, SSH_SFTP_QUOTE_SETSTAT);
+ return;
+}
+
+CURLcode Curl_ssh_init(void)
+{
+ if(ssh_init()) {
+ DEBUGF(fprintf(stderr, "Error: libssh_init failed\n"));
+ return CURLE_FAILED_INIT;
+ }
+ return CURLE_OK;
+}
+
+void Curl_ssh_cleanup(void)
+{
+ (void)ssh_finalize();
+}
+
+void Curl_ssh_version(char *buffer, size_t buflen)
+{
+ (void)msnprintf(buffer, buflen, "libssh/%s", ssh_version(0));
+}
+
+#endif /* USE_LIBSSH */
diff --git a/libs/libcurl/src/vssh/libssh2.c b/libs/libcurl/src/vssh/libssh2.c index 5a2c0f8bbf..5fa9bdb19f 100644 --- a/libs/libcurl/src/vssh/libssh2.c +++ b/libs/libcurl/src/vssh/libssh2.c @@ -1,3799 +1,3797 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_LIBSSH2_DEBUG */ - -#include "curl_setup.h" - -#ifdef USE_LIBSSH2 - -#include <limits.h> - -#include <libssh2.h> -#include <libssh2_sftp.h> - -#ifdef HAVE_FCNTL_H -#include <fcntl.h> -#endif - -#ifdef HAVE_NETINET_IN_H -#include <netinet/in.h> -#endif -#ifdef HAVE_ARPA_INET_H -#include <arpa/inet.h> -#endif -#ifdef HAVE_UTSNAME_H -#include <sys/utsname.h> -#endif -#ifdef HAVE_NETDB_H -#include <netdb.h> -#endif -#ifdef __VMS -#include <in.h> -#include <inet.h> -#endif - -#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) -#undef in_addr_t -#define in_addr_t unsigned long -#endif - -#include <curl/curl.h> -#include "urldata.h" -#include "sendf.h" -#include "hostip.h" -#include "progress.h" -#include "transfer.h" -#include "escape.h" -#include "http.h" /* for HTTP proxy tunnel stuff */ -#include "ssh.h" -#include "url.h" -#include "speedcheck.h" -#include "getinfo.h" -#include "strdup.h" -#include "strcase.h" -#include "vtls/vtls.h" -#include "connect.h" -#include "inet_ntop.h" -#include "parsedate.h" /* for the week day and month names */ -#include "sockaddr.h" /* required for Curl_sockaddr_storage */ -#include "strtoofft.h" -#include "multiif.h" -#include "select.h" -#include "warnless.h" -#include "curl_path.h" -#include "strcase.h" - -#include <curl_base64.h> /* for base64 encoding/decoding */ -#include <curl_sha256.h> - - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#if LIBSSH2_VERSION_NUM >= 0x010206 -/* libssh2_sftp_statvfs and friends were added in 1.2.6 */ -#define HAS_STATVFS_SUPPORT 1 -#endif - -#define sftp_libssh2_realpath(s,p,t,m) \ - libssh2_sftp_symlink_ex((s), (p), curlx_uztoui(strlen(p)), \ - (t), (m), LIBSSH2_SFTP_REALPATH) - -/* Local functions: */ -static const char *sftp_libssh2_strerror(unsigned long err); -static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc); -static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc); -static LIBSSH2_FREE_FUNC(my_libssh2_free); - -static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data); -static CURLcode ssh_connect(struct Curl_easy *data, bool *done); -static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done); -static CURLcode ssh_do(struct Curl_easy *data, bool *done); -static CURLcode scp_done(struct Curl_easy *data, CURLcode c, bool premature); -static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done); -static CURLcode scp_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead_connection); -static CURLcode sftp_done(struct Curl_easy *data, CURLcode, bool premature); -static CURLcode sftp_doing(struct Curl_easy *data, bool *dophase_done); -static CURLcode sftp_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead); -static CURLcode sftp_perform(struct Curl_easy *data, bool *connected, - bool *dophase_done); -static int ssh_getsock(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t *sock); -static CURLcode ssh_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static void ssh_attach(struct Curl_easy *data, struct connectdata *conn); - -/* - * SCP protocol handler. - */ - -const struct Curl_handler Curl_handler_scp = { - "SCP", /* scheme */ - ssh_setup_connection, /* setup_connection */ - ssh_do, /* do_it */ - scp_done, /* done */ - ZERO_NULL, /* do_more */ - ssh_connect, /* connect_it */ - ssh_multi_statemach, /* connecting */ - scp_doing, /* doing */ - ssh_getsock, /* proto_getsock */ - ssh_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ssh_getsock, /* perform_getsock */ - scp_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ssh_attach, - PORT_SSH, /* defport */ - CURLPROTO_SCP, /* protocol */ - CURLPROTO_SCP, /* family */ - PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION - | PROTOPT_NOURLQUERY /* flags */ -}; - - -/* - * SFTP protocol handler. - */ - -const struct Curl_handler Curl_handler_sftp = { - "SFTP", /* scheme */ - ssh_setup_connection, /* setup_connection */ - ssh_do, /* do_it */ - sftp_done, /* done */ - ZERO_NULL, /* do_more */ - ssh_connect, /* connect_it */ - ssh_multi_statemach, /* connecting */ - sftp_doing, /* doing */ - ssh_getsock, /* proto_getsock */ - ssh_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - ssh_getsock, /* perform_getsock */ - sftp_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ssh_attach, - PORT_SSH, /* defport */ - CURLPROTO_SFTP, /* protocol */ - CURLPROTO_SFTP, /* family */ - PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION - | PROTOPT_NOURLQUERY /* flags */ -}; - -static void -kbd_callback(const char *name, int name_len, const char *instruction, - int instruction_len, int num_prompts, - const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts, - LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses, - void **abstract) -{ - struct Curl_easy *data = (struct Curl_easy *)*abstract; - -#ifdef CURL_LIBSSH2_DEBUG - fprintf(stderr, "name=%s\n", name); - fprintf(stderr, "name_len=%d\n", name_len); - fprintf(stderr, "instruction=%s\n", instruction); - fprintf(stderr, "instruction_len=%d\n", instruction_len); - fprintf(stderr, "num_prompts=%d\n", num_prompts); -#else - (void)name; - (void)name_len; - (void)instruction; - (void)instruction_len; -#endif /* CURL_LIBSSH2_DEBUG */ - if(num_prompts == 1) { - struct connectdata *conn = data->conn; - responses[0].text = strdup(conn->passwd); - responses[0].length = curlx_uztoui(strlen(conn->passwd)); - } - (void)prompts; -} /* kbd_callback */ - -static CURLcode sftp_libssh2_error_to_CURLE(unsigned long err) -{ - switch(err) { - case LIBSSH2_FX_OK: - return CURLE_OK; - - case LIBSSH2_FX_NO_SUCH_FILE: - case LIBSSH2_FX_NO_SUCH_PATH: - return CURLE_REMOTE_FILE_NOT_FOUND; - - case LIBSSH2_FX_PERMISSION_DENIED: - case LIBSSH2_FX_WRITE_PROTECT: - case LIBSSH2_FX_LOCK_CONFlICT: - return CURLE_REMOTE_ACCESS_DENIED; - - case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM: - case LIBSSH2_FX_QUOTA_EXCEEDED: - return CURLE_REMOTE_DISK_FULL; - - case LIBSSH2_FX_FILE_ALREADY_EXISTS: - return CURLE_REMOTE_FILE_EXISTS; - - case LIBSSH2_FX_DIR_NOT_EMPTY: - return CURLE_QUOTE_ERROR; - - default: - break; - } - - return CURLE_SSH; -} - -static CURLcode libssh2_session_error_to_CURLE(int err) -{ - switch(err) { - /* Ordered by order of appearance in libssh2.h */ - case LIBSSH2_ERROR_NONE: - return CURLE_OK; - - /* This is the error returned by libssh2_scp_recv2 - * on unknown file */ - case LIBSSH2_ERROR_SCP_PROTOCOL: - return CURLE_REMOTE_FILE_NOT_FOUND; - - case LIBSSH2_ERROR_SOCKET_NONE: - return CURLE_COULDNT_CONNECT; - - case LIBSSH2_ERROR_ALLOC: - return CURLE_OUT_OF_MEMORY; - - case LIBSSH2_ERROR_SOCKET_SEND: - return CURLE_SEND_ERROR; - - case LIBSSH2_ERROR_HOSTKEY_INIT: - case LIBSSH2_ERROR_HOSTKEY_SIGN: - case LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED: - case LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED: - return CURLE_PEER_FAILED_VERIFICATION; - - case LIBSSH2_ERROR_PASSWORD_EXPIRED: - return CURLE_LOGIN_DENIED; - - case LIBSSH2_ERROR_SOCKET_TIMEOUT: - case LIBSSH2_ERROR_TIMEOUT: - return CURLE_OPERATION_TIMEDOUT; - - case LIBSSH2_ERROR_EAGAIN: - return CURLE_AGAIN; - } - - return CURLE_SSH; -} - -static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc) -{ - (void)abstract; /* arg not used */ - return malloc(count); -} - -static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc) -{ - (void)abstract; /* arg not used */ - return realloc(ptr, count); -} - -static LIBSSH2_FREE_FUNC(my_libssh2_free) -{ - (void)abstract; /* arg not used */ - if(ptr) /* ssh2 agent sometimes call free with null ptr */ - free(ptr); -} - -/* - * SSH State machine related code - */ -/* This is the ONLY way to change SSH state! */ -static void state(struct Curl_easy *data, sshstate nowstate) -{ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - /* for debug purposes */ - static const char * const names[] = { - "SSH_STOP", - "SSH_INIT", - "SSH_S_STARTUP", - "SSH_HOSTKEY", - "SSH_AUTHLIST", - "SSH_AUTH_PKEY_INIT", - "SSH_AUTH_PKEY", - "SSH_AUTH_PASS_INIT", - "SSH_AUTH_PASS", - "SSH_AUTH_AGENT_INIT", - "SSH_AUTH_AGENT_LIST", - "SSH_AUTH_AGENT", - "SSH_AUTH_HOST_INIT", - "SSH_AUTH_HOST", - "SSH_AUTH_KEY_INIT", - "SSH_AUTH_KEY", - "SSH_AUTH_GSSAPI", - "SSH_AUTH_DONE", - "SSH_SFTP_INIT", - "SSH_SFTP_REALPATH", - "SSH_SFTP_QUOTE_INIT", - "SSH_SFTP_POSTQUOTE_INIT", - "SSH_SFTP_QUOTE", - "SSH_SFTP_NEXT_QUOTE", - "SSH_SFTP_QUOTE_STAT", - "SSH_SFTP_QUOTE_SETSTAT", - "SSH_SFTP_QUOTE_SYMLINK", - "SSH_SFTP_QUOTE_MKDIR", - "SSH_SFTP_QUOTE_RENAME", - "SSH_SFTP_QUOTE_RMDIR", - "SSH_SFTP_QUOTE_UNLINK", - "SSH_SFTP_QUOTE_STATVFS", - "SSH_SFTP_GETINFO", - "SSH_SFTP_FILETIME", - "SSH_SFTP_TRANS_INIT", - "SSH_SFTP_UPLOAD_INIT", - "SSH_SFTP_CREATE_DIRS_INIT", - "SSH_SFTP_CREATE_DIRS", - "SSH_SFTP_CREATE_DIRS_MKDIR", - "SSH_SFTP_READDIR_INIT", - "SSH_SFTP_READDIR", - "SSH_SFTP_READDIR_LINK", - "SSH_SFTP_READDIR_BOTTOM", - "SSH_SFTP_READDIR_DONE", - "SSH_SFTP_DOWNLOAD_INIT", - "SSH_SFTP_DOWNLOAD_STAT", - "SSH_SFTP_CLOSE", - "SSH_SFTP_SHUTDOWN", - "SSH_SCP_TRANS_INIT", - "SSH_SCP_UPLOAD_INIT", - "SSH_SCP_DOWNLOAD_INIT", - "SSH_SCP_DOWNLOAD", - "SSH_SCP_DONE", - "SSH_SCP_SEND_EOF", - "SSH_SCP_WAIT_EOF", - "SSH_SCP_WAIT_CLOSE", - "SSH_SCP_CHANNEL_FREE", - "SSH_SESSION_DISCONNECT", - "SSH_SESSION_FREE", - "QUIT" - }; - - /* a precaution to make sure the lists are in sync */ - DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST); - - if(sshc->state != nowstate) { - infof(data, "SFTP %p state change from %s to %s", - (void *)sshc, names[sshc->state], names[nowstate]); - } -#endif - - sshc->state = nowstate; -} - - -#ifdef HAVE_LIBSSH2_KNOWNHOST_API -static int sshkeycallback(struct Curl_easy *easy, - const struct curl_khkey *knownkey, /* known */ - const struct curl_khkey *foundkey, /* found */ - enum curl_khmatch match, - void *clientp) -{ - (void)easy; - (void)knownkey; - (void)foundkey; - (void)clientp; - - /* we only allow perfect matches, and we reject everything else */ - return (match != CURLKHMATCH_OK)?CURLKHSTAT_REJECT:CURLKHSTAT_FINE; -} -#endif - -/* - * Earlier libssh2 versions didn't have the ability to seek to 64bit positions - * with 32bit size_t. - */ -#ifdef HAVE_LIBSSH2_SFTP_SEEK64 -#define SFTP_SEEK(x,y) libssh2_sftp_seek64(x, (libssh2_uint64_t)y) -#else -#define SFTP_SEEK(x,y) libssh2_sftp_seek(x, (size_t)y) -#endif - -/* - * Earlier libssh2 versions didn't do SCP properly beyond 32bit sizes on 32bit - * architectures so we check of the necessary function is present. - */ -#ifndef HAVE_LIBSSH2_SCP_SEND64 -#define SCP_SEND(a,b,c,d) libssh2_scp_send_ex(a, b, (int)(c), (size_t)d, 0, 0) -#else -#define SCP_SEND(a,b,c,d) libssh2_scp_send64(a, b, (int)(c), \ - (libssh2_uint64_t)d, 0, 0) -#endif - -/* - * libssh2 1.2.8 fixed the problem with 32bit ints used for sockets on win64. - */ -#ifdef HAVE_LIBSSH2_SESSION_HANDSHAKE -#define session_startup(x,y) libssh2_session_handshake(x, y) -#else -#define session_startup(x,y) libssh2_session_startup(x, (int)y) -#endif -static int convert_ssh2_keytype(int sshkeytype) -{ - int keytype = CURLKHTYPE_UNKNOWN; - switch(sshkeytype) { - case LIBSSH2_HOSTKEY_TYPE_RSA: - keytype = CURLKHTYPE_RSA; - break; - case LIBSSH2_HOSTKEY_TYPE_DSS: - keytype = CURLKHTYPE_DSS; - break; -#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256 - case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: - keytype = CURLKHTYPE_ECDSA; - break; -#endif -#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_384 - case LIBSSH2_HOSTKEY_TYPE_ECDSA_384: - keytype = CURLKHTYPE_ECDSA; - break; -#endif -#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_521 - case LIBSSH2_HOSTKEY_TYPE_ECDSA_521: - keytype = CURLKHTYPE_ECDSA; - break; -#endif -#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519 - case LIBSSH2_HOSTKEY_TYPE_ED25519: - keytype = CURLKHTYPE_ED25519; - break; -#endif - } - return keytype; -} - -static CURLcode ssh_knownhost(struct Curl_easy *data) -{ - int sshkeytype = 0; - size_t keylen = 0; - int rc = 0; - CURLcode result = CURLE_OK; - -#ifdef HAVE_LIBSSH2_KNOWNHOST_API - if(data->set.str[STRING_SSH_KNOWNHOSTS]) { - /* we're asked to verify the host against a file */ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - struct libssh2_knownhost *host = NULL; - const char *remotekey = libssh2_session_hostkey(sshc->ssh_session, - &keylen, &sshkeytype); - int keycheck = LIBSSH2_KNOWNHOST_CHECK_FAILURE; - int keybit = 0; - - if(remotekey) { - /* - * A subject to figure out is what host name we need to pass in here. - * What host name does OpenSSH store in its file if an IDN name is - * used? - */ - enum curl_khmatch keymatch; - curl_sshkeycallback func = - data->set.ssh_keyfunc ? data->set.ssh_keyfunc : sshkeycallback; - struct curl_khkey knownkey; - struct curl_khkey *knownkeyp = NULL; - struct curl_khkey foundkey; - - switch(sshkeytype) { - case LIBSSH2_HOSTKEY_TYPE_RSA: - keybit = LIBSSH2_KNOWNHOST_KEY_SSHRSA; - break; - case LIBSSH2_HOSTKEY_TYPE_DSS: - keybit = LIBSSH2_KNOWNHOST_KEY_SSHDSS; - break; -#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256 - case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: - keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_256; - break; -#endif -#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_384 - case LIBSSH2_HOSTKEY_TYPE_ECDSA_384: - keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_384; - break; -#endif -#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_521 - case LIBSSH2_HOSTKEY_TYPE_ECDSA_521: - keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_521; - break; -#endif -#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519 - case LIBSSH2_HOSTKEY_TYPE_ED25519: - keybit = LIBSSH2_KNOWNHOST_KEY_ED25519; - break; -#endif - default: - infof(data, "unsupported key type, can't check knownhosts"); - keybit = 0; - break; - } - if(!keybit) - /* no check means failure! */ - rc = CURLKHSTAT_REJECT; - else { -#ifdef HAVE_LIBSSH2_KNOWNHOST_CHECKP - keycheck = libssh2_knownhost_checkp(sshc->kh, - conn->host.name, - (conn->remote_port != PORT_SSH)? - conn->remote_port:-1, - remotekey, keylen, - LIBSSH2_KNOWNHOST_TYPE_PLAIN| - LIBSSH2_KNOWNHOST_KEYENC_RAW| - keybit, - &host); -#else - keycheck = libssh2_knownhost_check(sshc->kh, - conn->host.name, - remotekey, keylen, - LIBSSH2_KNOWNHOST_TYPE_PLAIN| - LIBSSH2_KNOWNHOST_KEYENC_RAW| - keybit, - &host); -#endif - - infof(data, "SSH host check: %d, key: %s", keycheck, - (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH)? - host->key:"<none>"); - - /* setup 'knownkey' */ - if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) { - knownkey.key = host->key; - knownkey.len = 0; - knownkey.keytype = convert_ssh2_keytype(sshkeytype); - knownkeyp = &knownkey; - } - - /* setup 'foundkey' */ - foundkey.key = remotekey; - foundkey.len = keylen; - foundkey.keytype = convert_ssh2_keytype(sshkeytype); - - /* - * if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the - * curl_khmatch enum are ever modified, we need to introduce a - * translation table here! - */ - keymatch = (enum curl_khmatch)keycheck; - - /* Ask the callback how to behave */ - Curl_set_in_callback(data, true); - rc = func(data, knownkeyp, /* from the knownhosts file */ - &foundkey, /* from the remote host */ - keymatch, data->set.ssh_keyfunc_userp); - Curl_set_in_callback(data, false); - } - } - else - /* no remotekey means failure! */ - rc = CURLKHSTAT_REJECT; - - switch(rc) { - default: /* unknown return codes will equal reject */ - /* FALLTHROUGH */ - case CURLKHSTAT_REJECT: - state(data, SSH_SESSION_FREE); - /* FALLTHROUGH */ - case CURLKHSTAT_DEFER: - /* DEFER means bail out but keep the SSH_HOSTKEY state */ - result = sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; - break; - case CURLKHSTAT_FINE_REPLACE: - /* remove old host+key that doesn't match */ - if(host) - libssh2_knownhost_del(sshc->kh, host); - /*FALLTHROUGH*/ - case CURLKHSTAT_FINE: - /*FALLTHROUGH*/ - case CURLKHSTAT_FINE_ADD_TO_FILE: - /* proceed */ - if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) { - /* the found host+key didn't match but has been told to be fine - anyway so we add it in memory */ - int addrc = libssh2_knownhost_add(sshc->kh, - conn->host.name, NULL, - remotekey, keylen, - LIBSSH2_KNOWNHOST_TYPE_PLAIN| - LIBSSH2_KNOWNHOST_KEYENC_RAW| - keybit, NULL); - if(addrc) - infof(data, "WARNING: adding the known host %s failed", - conn->host.name); - else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE || - rc == CURLKHSTAT_FINE_REPLACE) { - /* now we write the entire in-memory list of known hosts to the - known_hosts file */ - int wrc = - libssh2_knownhost_writefile(sshc->kh, - data->set.str[STRING_SSH_KNOWNHOSTS], - LIBSSH2_KNOWNHOST_FILE_OPENSSH); - if(wrc) { - infof(data, "WARNING: writing %s failed", - data->set.str[STRING_SSH_KNOWNHOSTS]); - } - } - } - break; - } - } -#else /* HAVE_LIBSSH2_KNOWNHOST_API */ - (void)data; -#endif - return result; -} - -static CURLcode ssh_check_fingerprint(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]; - const char *pubkey_sha256 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]; - - infof(data, "SSH MD5 public key: %s", - pubkey_md5 != NULL ? pubkey_md5 : "NULL"); - infof(data, "SSH SHA256 public key: %s", - pubkey_sha256 != NULL ? pubkey_sha256 : "NULL"); - - if(pubkey_sha256) { - const char *fingerprint = NULL; - char *fingerprint_b64 = NULL; - size_t fingerprint_b64_len; - size_t pub_pos = 0; - size_t b64_pos = 0; - -#ifdef LIBSSH2_HOSTKEY_HASH_SHA256 - /* The fingerprint points to static storage (!), don't free() it. */ - fingerprint = libssh2_hostkey_hash(sshc->ssh_session, - LIBSSH2_HOSTKEY_HASH_SHA256); -#else - const char *hostkey; - size_t len = 0; - unsigned char hash[32]; - - hostkey = libssh2_session_hostkey(sshc->ssh_session, &len, NULL); - if(hostkey) { - if(!Curl_sha256it(hash, (const unsigned char *) hostkey, len)) - fingerprint = (char *) hash; - } -#endif - - if(!fingerprint) { - failf(data, - "Denied establishing ssh session: sha256 fingerprint " - "not available"); - state(data, SSH_SESSION_FREE); - sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; - return sshc->actualcode; - } - - /* The length of fingerprint is 32 bytes for SHA256. - * See libssh2_hostkey_hash documentation. */ - if(Curl_base64_encode(fingerprint, 32, &fingerprint_b64, - &fingerprint_b64_len) != CURLE_OK) { - state(data, SSH_SESSION_FREE); - sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; - return sshc->actualcode; - } - - if(!fingerprint_b64) { - failf(data, "sha256 fingerprint could not be encoded"); - state(data, SSH_SESSION_FREE); - sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; - return sshc->actualcode; - } - - infof(data, "SSH SHA256 fingerprint: %s", fingerprint_b64); - - /* Find the position of any = padding characters in the public key */ - while((pubkey_sha256[pub_pos] != '=') && pubkey_sha256[pub_pos]) { - pub_pos++; - } - - /* Find the position of any = padding characters in the base64 coded - * hostkey fingerprint */ - while((fingerprint_b64[b64_pos] != '=') && fingerprint_b64[b64_pos]) { - b64_pos++; - } - - /* Before we authenticate we check the hostkey's sha256 fingerprint - * against a known fingerprint, if available. - */ - if((pub_pos != b64_pos) || - strncmp(fingerprint_b64, pubkey_sha256, pub_pos)) { - free(fingerprint_b64); - - failf(data, - "Denied establishing ssh session: mismatch sha256 fingerprint. " - "Remote %s is not equal to %s", fingerprint_b64, pubkey_sha256); - state(data, SSH_SESSION_FREE); - sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; - return sshc->actualcode; - } - - free(fingerprint_b64); - - infof(data, "SHA256 checksum match"); - } - - if(pubkey_md5) { - char md5buffer[33]; - const char *fingerprint = NULL; - - fingerprint = libssh2_hostkey_hash(sshc->ssh_session, - LIBSSH2_HOSTKEY_HASH_MD5); - - if(fingerprint) { - /* The fingerprint points to static storage (!), don't free() it. */ - int i; - for(i = 0; i < 16; i++) { - msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char) fingerprint[i]); - } - - infof(data, "SSH MD5 fingerprint: %s", md5buffer); - } - - /* This does NOT verify the length of 'pubkey_md5' separately, which will - make the comparison below fail unless it is exactly 32 characters */ - if(!fingerprint || !strcasecompare(md5buffer, pubkey_md5)) { - if(fingerprint) { - failf(data, - "Denied establishing ssh session: mismatch md5 fingerprint. " - "Remote %s is not equal to %s", md5buffer, pubkey_md5); - } - else { - failf(data, - "Denied establishing ssh session: md5 fingerprint " - "not available"); - } - state(data, SSH_SESSION_FREE); - sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; - return sshc->actualcode; - } - infof(data, "MD5 checksum match"); - } - - if(!pubkey_md5 && !pubkey_sha256) { - if(data->set.ssh_hostkeyfunc) { - size_t keylen = 0; - int sshkeytype = 0; - int rc = 0; - /* we handle the process to the callback*/ - const char *remotekey = libssh2_session_hostkey(sshc->ssh_session, - &keylen, &sshkeytype); - if(remotekey) { - int keytype = convert_ssh2_keytype(sshkeytype); - Curl_set_in_callback(data, true); - rc = data->set.ssh_hostkeyfunc(data->set.ssh_hostkeyfunc_userp, - keytype, remotekey, keylen); - Curl_set_in_callback(data, false); - if(rc!= CURLKHMATCH_OK) { - state(data, SSH_SESSION_FREE); - } - } - else { - state(data, SSH_SESSION_FREE); - } - return CURLE_OK; - } - else { - return ssh_knownhost(data); - } - } - else { - /* as we already matched, we skip the check for known hosts */ - return CURLE_OK; - } -} - -/* - * ssh_force_knownhost_key_type() will check the known hosts file and try to - * force a specific public key type from the server if an entry is found. - */ -static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - -#ifdef HAVE_LIBSSH2_KNOWNHOST_API - -#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519 - static const char * const hostkey_method_ssh_ed25519 - = "ssh-ed25519"; -#endif -#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521 - static const char * const hostkey_method_ssh_ecdsa_521 - = "ecdsa-sha2-nistp521"; -#endif -#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384 - static const char * const hostkey_method_ssh_ecdsa_384 - = "ecdsa-sha2-nistp384"; -#endif -#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256 - static const char * const hostkey_method_ssh_ecdsa_256 - = "ecdsa-sha2-nistp256"; -#endif - static const char * const hostkey_method_ssh_rsa - = "ssh-rsa"; - static const char * const hostkey_method_ssh_dss - = "ssh-dss"; - - const char *hostkey_method = NULL; - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - struct libssh2_knownhost* store = NULL; - const char *kh_name_end = NULL; - size_t kh_name_size = 0; - int port = 0; - bool found = false; - - if(sshc->kh && !data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) { - /* lets try to find our host in the known hosts file */ - while(!libssh2_knownhost_get(sshc->kh, &store, store)) { - /* For non-standard ports, the name will be enclosed in */ - /* square brackets, followed by a colon and the port */ - if(store) { - if(store->name) { - if(store->name[0] == '[') { - kh_name_end = strstr(store->name, "]:"); - if(!kh_name_end) { - infof(data, "Invalid host pattern %s in %s", - store->name, data->set.str[STRING_SSH_KNOWNHOSTS]); - continue; - } - port = atoi(kh_name_end + 2); - if(kh_name_end && (port == conn->remote_port)) { - kh_name_size = strlen(store->name) - 1 - strlen(kh_name_end); - if(strncmp(store->name + 1, - conn->host.name, kh_name_size) == 0) { - found = true; - break; - } - } - } - else if(strcmp(store->name, conn->host.name) == 0) { - found = true; - break; - } - } - else { - found = true; - break; - } - } - } - - if(found) { - infof(data, "Found host %s in %s", - conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]); - - switch(store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK) { -#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519 - case LIBSSH2_KNOWNHOST_KEY_ED25519: - hostkey_method = hostkey_method_ssh_ed25519; - break; -#endif -#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521 - case LIBSSH2_KNOWNHOST_KEY_ECDSA_521: - hostkey_method = hostkey_method_ssh_ecdsa_521; - break; -#endif -#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384 - case LIBSSH2_KNOWNHOST_KEY_ECDSA_384: - hostkey_method = hostkey_method_ssh_ecdsa_384; - break; -#endif -#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256 - case LIBSSH2_KNOWNHOST_KEY_ECDSA_256: - hostkey_method = hostkey_method_ssh_ecdsa_256; - break; -#endif - case LIBSSH2_KNOWNHOST_KEY_SSHRSA: - hostkey_method = hostkey_method_ssh_rsa; - break; - case LIBSSH2_KNOWNHOST_KEY_SSHDSS: - hostkey_method = hostkey_method_ssh_dss; - break; - case LIBSSH2_KNOWNHOST_KEY_RSA1: - failf(data, "Found host key type RSA1 which is not supported"); - return CURLE_SSH; - default: - failf(data, "Unknown host key type: %i", - (store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK)); - return CURLE_SSH; - } - - infof(data, "Set \"%s\" as SSH hostkey type", hostkey_method); - result = libssh2_session_error_to_CURLE( - libssh2_session_method_pref( - sshc->ssh_session, LIBSSH2_METHOD_HOSTKEY, hostkey_method)); - } - else { - infof(data, "Did not find host %s in %s", - conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]); - } - } - -#endif /* HAVE_LIBSSH2_KNOWNHOST_API */ - - return result; -} - -/* - * ssh_statemach_act() runs the SSH state machine as far as it can without - * blocking and without reaching the end. The data the pointer 'block' points - * to will be set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN - * meaning it wants to be called again when the socket is ready - */ - -static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct SSHPROTO *sshp = data->req.p.ssh; - struct ssh_conn *sshc = &conn->proto.sshc; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; - int rc = LIBSSH2_ERROR_NONE; - int ssherr; - unsigned long sftperr; - int seekerr = CURL_SEEKFUNC_OK; - size_t readdir_len; - *block = 0; /* we're not blocking by default */ - - do { - switch(sshc->state) { - case SSH_INIT: - sshc->secondCreateDirs = 0; - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_OK; - - /* Set libssh2 to non-blocking, since everything internally is - non-blocking */ - libssh2_session_set_blocking(sshc->ssh_session, 0); - - result = ssh_force_knownhost_key_type(data); - if(result) { - state(data, SSH_SESSION_FREE); - sshc->actualcode = result; - break; - } - - state(data, SSH_S_STARTUP); - /* FALLTHROUGH */ - - case SSH_S_STARTUP: - rc = session_startup(sshc->ssh_session, sock); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); - failf(data, "Failure establishing ssh session: %d, %s", rc, err_msg); - - state(data, SSH_SESSION_FREE); - sshc->actualcode = CURLE_FAILED_INIT; - break; - } - - state(data, SSH_HOSTKEY); - - /* FALLTHROUGH */ - case SSH_HOSTKEY: - /* - * Before we authenticate we should check the hostkey's fingerprint - * against our known hosts. How that is handled (reading from file, - * whatever) is up to us. - */ - result = ssh_check_fingerprint(data); - if(!result) - state(data, SSH_AUTHLIST); - /* ssh_check_fingerprint sets state appropriately on error */ - break; - - case SSH_AUTHLIST: - /* - * Figure out authentication methods - * NB: As soon as we have provided a username to an openssh server we - * must never change it later. Thus, always specify the correct username - * here, even though the libssh2 docs kind of indicate that it should be - * possible to get a 'generic' list (not user-specific) of authentication - * methods, presumably with a blank username. That won't work in my - * experience. - * So always specify it here. - */ - sshc->authlist = libssh2_userauth_list(sshc->ssh_session, - conn->user, - curlx_uztoui(strlen(conn->user))); - - if(!sshc->authlist) { - if(libssh2_userauth_authenticated(sshc->ssh_session)) { - sshc->authed = TRUE; - infof(data, "SSH user accepted with no authentication"); - state(data, SSH_AUTH_DONE); - break; - } - ssherr = libssh2_session_last_errno(sshc->ssh_session); - if(ssherr == LIBSSH2_ERROR_EAGAIN) - rc = LIBSSH2_ERROR_EAGAIN; - else { - state(data, SSH_SESSION_FREE); - sshc->actualcode = libssh2_session_error_to_CURLE(ssherr); - } - break; - } - infof(data, "SSH authentication methods available: %s", - sshc->authlist); - - state(data, SSH_AUTH_PKEY_INIT); - break; - - case SSH_AUTH_PKEY_INIT: - /* - * Check the supported auth types in the order I feel is most secure - * with the requested type of authentication - */ - sshc->authed = FALSE; - - if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) && - (strstr(sshc->authlist, "publickey") != NULL)) { - bool out_of_memory = FALSE; - - sshc->rsa_pub = sshc->rsa = NULL; - - if(data->set.str[STRING_SSH_PRIVATE_KEY]) - sshc->rsa = strdup(data->set.str[STRING_SSH_PRIVATE_KEY]); - else { - /* To ponder about: should really the lib be messing about with the - HOME environment variable etc? */ - char *home = curl_getenv("HOME"); - - /* If no private key file is specified, try some common paths. */ - if(home) { - /* Try ~/.ssh first. */ - sshc->rsa = aprintf("%s/.ssh/id_rsa", home); - if(!sshc->rsa) - out_of_memory = TRUE; - else if(access(sshc->rsa, R_OK) != 0) { - Curl_safefree(sshc->rsa); - sshc->rsa = aprintf("%s/.ssh/id_dsa", home); - if(!sshc->rsa) - out_of_memory = TRUE; - else if(access(sshc->rsa, R_OK) != 0) { - Curl_safefree(sshc->rsa); - } - } - free(home); - } - if(!out_of_memory && !sshc->rsa) { - /* Nothing found; try the current dir. */ - sshc->rsa = strdup("id_rsa"); - if(sshc->rsa && access(sshc->rsa, R_OK) != 0) { - Curl_safefree(sshc->rsa); - sshc->rsa = strdup("id_dsa"); - if(sshc->rsa && access(sshc->rsa, R_OK) != 0) { - Curl_safefree(sshc->rsa); - /* Out of guesses. Set to the empty string to avoid - * surprising info messages. */ - sshc->rsa = strdup(""); - } - } - } - } - - /* - * Unless the user explicitly specifies a public key file, let - * libssh2 extract the public key from the private key file. - * This is done by simply passing sshc->rsa_pub = NULL. - */ - if(data->set.str[STRING_SSH_PUBLIC_KEY] - /* treat empty string the same way as NULL */ - && data->set.str[STRING_SSH_PUBLIC_KEY][0]) { - sshc->rsa_pub = strdup(data->set.str[STRING_SSH_PUBLIC_KEY]); - if(!sshc->rsa_pub) - out_of_memory = TRUE; - } - - if(out_of_memory || !sshc->rsa) { - Curl_safefree(sshc->rsa); - Curl_safefree(sshc->rsa_pub); - state(data, SSH_SESSION_FREE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; - break; - } - - sshc->passphrase = data->set.ssl.key_passwd; - if(!sshc->passphrase) - sshc->passphrase = ""; - - if(sshc->rsa_pub) - infof(data, "Using SSH public key file '%s'", sshc->rsa_pub); - infof(data, "Using SSH private key file '%s'", sshc->rsa); - - state(data, SSH_AUTH_PKEY); - } - else { - state(data, SSH_AUTH_PASS_INIT); - } - break; - - case SSH_AUTH_PKEY: - /* The function below checks if the files exists, no need to stat() here. - */ - rc = libssh2_userauth_publickey_fromfile_ex(sshc->ssh_session, - conn->user, - curlx_uztoui( - strlen(conn->user)), - sshc->rsa_pub, - sshc->rsa, sshc->passphrase); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - - Curl_safefree(sshc->rsa_pub); - Curl_safefree(sshc->rsa); - - if(rc == 0) { - sshc->authed = TRUE; - infof(data, "Initialized SSH public key authentication"); - state(data, SSH_AUTH_DONE); - } - else { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "SSH public key authentication failed: %s", err_msg); - state(data, SSH_AUTH_PASS_INIT); - rc = 0; /* clear rc and continue */ - } - break; - - case SSH_AUTH_PASS_INIT: - if((data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) && - (strstr(sshc->authlist, "password") != NULL)) { - state(data, SSH_AUTH_PASS); - } - else { - state(data, SSH_AUTH_HOST_INIT); - rc = 0; /* clear rc and continue */ - } - break; - - case SSH_AUTH_PASS: - rc = libssh2_userauth_password_ex(sshc->ssh_session, conn->user, - curlx_uztoui(strlen(conn->user)), - conn->passwd, - curlx_uztoui(strlen(conn->passwd)), - NULL); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc == 0) { - sshc->authed = TRUE; - infof(data, "Initialized password authentication"); - state(data, SSH_AUTH_DONE); - } - else { - state(data, SSH_AUTH_HOST_INIT); - rc = 0; /* clear rc and continue */ - } - break; - - case SSH_AUTH_HOST_INIT: - if((data->set.ssh_auth_types & CURLSSH_AUTH_HOST) && - (strstr(sshc->authlist, "hostbased") != NULL)) { - state(data, SSH_AUTH_HOST); - } - else { - state(data, SSH_AUTH_AGENT_INIT); - } - break; - - case SSH_AUTH_HOST: - state(data, SSH_AUTH_AGENT_INIT); - break; - - case SSH_AUTH_AGENT_INIT: -#ifdef HAVE_LIBSSH2_AGENT_API - if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT) - && (strstr(sshc->authlist, "publickey") != NULL)) { - - /* Connect to the ssh-agent */ - /* The agent could be shared by a curl thread i believe - but nothing obvious as keys can be added/removed at any time */ - if(!sshc->ssh_agent) { - sshc->ssh_agent = libssh2_agent_init(sshc->ssh_session); - if(!sshc->ssh_agent) { - infof(data, "Could not create agent object"); - - state(data, SSH_AUTH_KEY_INIT); - break; - } - } - - rc = libssh2_agent_connect(sshc->ssh_agent); - if(rc == LIBSSH2_ERROR_EAGAIN) - break; - if(rc < 0) { - infof(data, "Failure connecting to agent"); - state(data, SSH_AUTH_KEY_INIT); - rc = 0; /* clear rc and continue */ - } - else { - state(data, SSH_AUTH_AGENT_LIST); - } - } - else -#endif /* HAVE_LIBSSH2_AGENT_API */ - state(data, SSH_AUTH_KEY_INIT); - break; - - case SSH_AUTH_AGENT_LIST: -#ifdef HAVE_LIBSSH2_AGENT_API - rc = libssh2_agent_list_identities(sshc->ssh_agent); - - if(rc == LIBSSH2_ERROR_EAGAIN) - break; - if(rc < 0) { - infof(data, "Failure requesting identities to agent"); - state(data, SSH_AUTH_KEY_INIT); - rc = 0; /* clear rc and continue */ - } - else { - state(data, SSH_AUTH_AGENT); - sshc->sshagent_prev_identity = NULL; - } -#endif - break; - - case SSH_AUTH_AGENT: -#ifdef HAVE_LIBSSH2_AGENT_API - /* as prev_identity evolves only after an identity user auth finished we - can safely request it again as long as EAGAIN is returned here or by - libssh2_agent_userauth */ - rc = libssh2_agent_get_identity(sshc->ssh_agent, - &sshc->sshagent_identity, - sshc->sshagent_prev_identity); - if(rc == LIBSSH2_ERROR_EAGAIN) - break; - - if(rc == 0) { - rc = libssh2_agent_userauth(sshc->ssh_agent, conn->user, - sshc->sshagent_identity); - - if(rc < 0) { - if(rc != LIBSSH2_ERROR_EAGAIN) { - /* tried and failed? go to next identity */ - sshc->sshagent_prev_identity = sshc->sshagent_identity; - } - break; - } - } - - if(rc < 0) - infof(data, "Failure requesting identities to agent"); - else if(rc == 1) - infof(data, "No identity would match"); - - if(rc == LIBSSH2_ERROR_NONE) { - sshc->authed = TRUE; - infof(data, "Agent based authentication successful"); - state(data, SSH_AUTH_DONE); - } - else { - state(data, SSH_AUTH_KEY_INIT); - rc = 0; /* clear rc and continue */ - } -#endif - break; - - case SSH_AUTH_KEY_INIT: - if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) - && (strstr(sshc->authlist, "keyboard-interactive") != NULL)) { - state(data, SSH_AUTH_KEY); - } - else { - state(data, SSH_AUTH_DONE); - } - break; - - case SSH_AUTH_KEY: - /* Authentication failed. Continue with keyboard-interactive now. */ - rc = libssh2_userauth_keyboard_interactive_ex(sshc->ssh_session, - conn->user, - curlx_uztoui( - strlen(conn->user)), - &kbd_callback); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc == 0) { - sshc->authed = TRUE; - infof(data, "Initialized keyboard interactive authentication"); - } - state(data, SSH_AUTH_DONE); - break; - - case SSH_AUTH_DONE: - if(!sshc->authed) { - failf(data, "Authentication failure"); - state(data, SSH_SESSION_FREE); - sshc->actualcode = CURLE_LOGIN_DENIED; - break; - } - - /* - * At this point we have an authenticated ssh session. - */ - infof(data, "Authentication complete"); - - Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */ - - conn->sockfd = sock; - conn->writesockfd = CURL_SOCKET_BAD; - - if(conn->handler->protocol == CURLPROTO_SFTP) { - state(data, SSH_SFTP_INIT); - break; - } - infof(data, "SSH CONNECT phase done"); - state(data, SSH_STOP); - break; - - case SSH_SFTP_INIT: - /* - * Start the libssh2 sftp session - */ - sshc->sftp_session = libssh2_sftp_init(sshc->ssh_session); - if(!sshc->sftp_session) { - char *err_msg = NULL; - if(libssh2_session_last_errno(sshc->ssh_session) == - LIBSSH2_ERROR_EAGAIN) { - rc = LIBSSH2_ERROR_EAGAIN; - break; - } - - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - failf(data, "Failure initializing sftp session: %s", err_msg); - state(data, SSH_SESSION_FREE); - sshc->actualcode = CURLE_FAILED_INIT; - break; - } - state(data, SSH_SFTP_REALPATH); - break; - - case SSH_SFTP_REALPATH: - { - char tempHome[PATH_MAX]; - - /* - * Get the "home" directory - */ - rc = sftp_libssh2_realpath(sshc->sftp_session, ".", - tempHome, PATH_MAX-1); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc > 0) { - /* It seems that this string is not always NULL terminated */ - tempHome[rc] = '\0'; - sshc->homedir = strdup(tempHome); - if(!sshc->homedir) { - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; - break; - } - data->state.most_recent_ftp_entrypath = sshc->homedir; - } - else { - /* Return the error type */ - sftperr = libssh2_sftp_last_error(sshc->sftp_session); - if(sftperr) - result = sftp_libssh2_error_to_CURLE(sftperr); - else - /* in this case, the error wasn't in the SFTP level but for example - a time-out or similar */ - result = CURLE_SSH; - sshc->actualcode = result; - DEBUGF(infof(data, "error = %lu makes libcurl = %d", - sftperr, (int)result)); - state(data, SSH_STOP); - break; - } - } - /* This is the last step in the SFTP connect phase. Do note that while - we get the homedir here, we get the "workingpath" in the DO action - since the homedir will remain the same between request but the - working path will not. */ - DEBUGF(infof(data, "SSH CONNECT phase done")); - state(data, SSH_STOP); - break; - - case SSH_SFTP_QUOTE_INIT: - - result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); - if(result) { - sshc->actualcode = result; - state(data, SSH_STOP); - break; - } - - if(data->set.quote) { - infof(data, "Sending quote commands"); - sshc->quote_item = data->set.quote; - state(data, SSH_SFTP_QUOTE); - } - else { - state(data, SSH_SFTP_GETINFO); - } - break; - - case SSH_SFTP_POSTQUOTE_INIT: - if(data->set.postquote) { - infof(data, "Sending quote commands"); - sshc->quote_item = data->set.postquote; - state(data, SSH_SFTP_QUOTE); - } - else { - state(data, SSH_STOP); - } - break; - - case SSH_SFTP_QUOTE: - /* Send any quote commands */ - { - const char *cp; - - /* - * Support some of the "FTP" commands - * - * 'sshc->quote_item' is already verified to be non-NULL before it - * switched to this state. - */ - char *cmd = sshc->quote_item->data; - sshc->acceptfail = FALSE; - - /* if a command starts with an asterisk, which a legal SFTP command never - can, the command will be allowed to fail without it causing any - aborts or cancels etc. It will cause libcurl to act as if the command - is successful, whatever the server reponds. */ - - if(cmd[0] == '*') { - cmd++; - sshc->acceptfail = TRUE; - } - - if(strcasecompare("pwd", cmd)) { - /* output debug output if that is requested */ - char *tmp = aprintf("257 \"%s\" is current directory.\n", - sshp->path); - if(!tmp) { - result = CURLE_OUT_OF_MEMORY; - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - break; - } - Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"PWD\n", 4); - Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp)); - - /* this sends an FTP-like "header" to the header callback so that the - current directory can be read very similar to how it is read when - using ordinary FTP. */ - result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); - free(tmp); - if(result) { - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - } - else - state(data, SSH_SFTP_NEXT_QUOTE); - break; - } - { - /* - * the arguments following the command must be separated from the - * command with a space so we can check for it unconditionally - */ - cp = strchr(cmd, ' '); - if(!cp) { - failf(data, "Syntax error command '%s', missing parameter", - cmd); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - - /* - * also, every command takes at least one argument so we get that - * first argument right now - */ - result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, "Syntax error: Bad first parameter to '%s'", cmd); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - break; - } - - /* - * SFTP is a binary protocol, so we don't send text commands - * to the server. Instead, we scan for commands used by - * OpenSSH's sftp program and call the appropriate libssh2 - * functions. - */ - if(strncasecompare(cmd, "chgrp ", 6) || - strncasecompare(cmd, "chmod ", 6) || - strncasecompare(cmd, "chown ", 6) || - strncasecompare(cmd, "atime ", 6) || - strncasecompare(cmd, "mtime ", 6)) { - /* attribute change */ - - /* sshc->quote_path1 contains the mode to set */ - /* get the destination */ - result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, "Syntax error in %s: Bad second parameter", cmd); - Curl_safefree(sshc->quote_path1); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - break; - } - memset(&sshp->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); - state(data, SSH_SFTP_QUOTE_STAT); - break; - } - if(strncasecompare(cmd, "ln ", 3) || - strncasecompare(cmd, "symlink ", 8)) { - /* symbolic linking */ - /* sshc->quote_path1 is the source */ - /* get the destination */ - result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, - "Syntax error in ln/symlink: Bad second parameter"); - Curl_safefree(sshc->quote_path1); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - break; - } - state(data, SSH_SFTP_QUOTE_SYMLINK); - break; - } - else if(strncasecompare(cmd, "mkdir ", 6)) { - /* create dir */ - state(data, SSH_SFTP_QUOTE_MKDIR); - break; - } - else if(strncasecompare(cmd, "rename ", 7)) { - /* rename file */ - /* first param is the source path */ - /* second param is the dest. path */ - result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, "Syntax error in rename: Bad second parameter"); - Curl_safefree(sshc->quote_path1); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - break; - } - state(data, SSH_SFTP_QUOTE_RENAME); - break; - } - else if(strncasecompare(cmd, "rmdir ", 6)) { - /* delete dir */ - state(data, SSH_SFTP_QUOTE_RMDIR); - break; - } - else if(strncasecompare(cmd, "rm ", 3)) { - state(data, SSH_SFTP_QUOTE_UNLINK); - break; - } -#ifdef HAS_STATVFS_SUPPORT - else if(strncasecompare(cmd, "statvfs ", 8)) { - state(data, SSH_SFTP_QUOTE_STATVFS); - break; - } -#endif - - failf(data, "Unknown SFTP command"); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - } - break; - - case SSH_SFTP_NEXT_QUOTE: - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - - sshc->quote_item = sshc->quote_item->next; - - if(sshc->quote_item) { - state(data, SSH_SFTP_QUOTE); - } - else { - if(sshc->nextstate != SSH_NO_STATE) { - state(data, sshc->nextstate); - sshc->nextstate = SSH_NO_STATE; - } - else { - state(data, SSH_SFTP_GETINFO); - } - } - break; - - case SSH_SFTP_QUOTE_STAT: - { - char *cmd = sshc->quote_item->data; - sshc->acceptfail = FALSE; - - /* if a command starts with an asterisk, which a legal SFTP command never - can, the command will be allowed to fail without it causing any - aborts or cancels etc. It will cause libcurl to act as if the command - is successful, whatever the server reponds. */ - - if(cmd[0] == '*') { - cmd++; - sshc->acceptfail = TRUE; - } - - if(!strncasecompare(cmd, "chmod", 5)) { - /* Since chown and chgrp only set owner OR group but libssh2 wants to - * set them both at once, we need to obtain the current ownership - * first. This takes an extra protocol round trip. - */ - rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2, - curlx_uztoui(strlen(sshc->quote_path2)), - LIBSSH2_SFTP_STAT, - &sshp->quote_attrs); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc && !sshc->acceptfail) { /* get those attributes */ - sftperr = libssh2_sftp_last_error(sshc->sftp_session); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Attempt to get SFTP stats failed: %s", - sftp_libssh2_strerror(sftperr)); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - } - - /* Now set the new attributes... */ - if(strncasecompare(cmd, "chgrp", 5)) { - sshp->quote_attrs.gid = strtoul(sshc->quote_path1, NULL, 10); - sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID; - if(sshp->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0]) && - !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Syntax error: chgrp gid not a number"); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - } - else if(strncasecompare(cmd, "chmod", 5)) { - sshp->quote_attrs.permissions = strtoul(sshc->quote_path1, NULL, 8); - sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS; - /* permissions are octal */ - if(sshp->quote_attrs.permissions == 0 && - !ISDIGIT(sshc->quote_path1[0])) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Syntax error: chmod permissions not a number"); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - } - else if(strncasecompare(cmd, "chown", 5)) { - sshp->quote_attrs.uid = strtoul(sshc->quote_path1, NULL, 10); - sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID; - if(sshp->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0]) && - !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Syntax error: chown uid not a number"); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - } - else if(strncasecompare(cmd, "atime", 5) || - strncasecompare(cmd, "mtime", 5)) { - time_t date = Curl_getdate_capped(sshc->quote_path1); - bool fail = FALSE; - - if(date == -1) { - failf(data, "incorrect date format for %.*s", 5, cmd); - fail = TRUE; - } -#if SIZEOF_TIME_T > SIZEOF_LONG - if(date > 0xffffffff) { - /* if 'long' can't old >32bit, this date cannot be sent */ - failf(data, "date overflow"); - fail = TRUE; - } -#endif - if(fail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - if(strncasecompare(cmd, "atime", 5)) - sshp->quote_attrs.atime = (unsigned long)date; - else /* mtime */ - sshp->quote_attrs.mtime = (unsigned long)date; - - sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_ACMODTIME; - } - - /* Now send the completed structure... */ - state(data, SSH_SFTP_QUOTE_SETSTAT); - break; - } - - case SSH_SFTP_QUOTE_SETSTAT: - rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2, - curlx_uztoui(strlen(sshc->quote_path2)), - LIBSSH2_SFTP_SETSTAT, - &sshp->quote_attrs); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc && !sshc->acceptfail) { - sftperr = libssh2_sftp_last_error(sshc->sftp_session); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Attempt to set SFTP stats failed: %s", - sftp_libssh2_strerror(sftperr)); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - state(data, SSH_SFTP_NEXT_QUOTE); - break; - - case SSH_SFTP_QUOTE_SYMLINK: - rc = libssh2_sftp_symlink_ex(sshc->sftp_session, sshc->quote_path1, - curlx_uztoui(strlen(sshc->quote_path1)), - sshc->quote_path2, - curlx_uztoui(strlen(sshc->quote_path2)), - LIBSSH2_SFTP_SYMLINK); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc && !sshc->acceptfail) { - sftperr = libssh2_sftp_last_error(sshc->sftp_session); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "symlink command failed: %s", - sftp_libssh2_strerror(sftperr)); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - state(data, SSH_SFTP_NEXT_QUOTE); - break; - - case SSH_SFTP_QUOTE_MKDIR: - rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshc->quote_path1, - curlx_uztoui(strlen(sshc->quote_path1)), - data->set.new_directory_perms); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc && !sshc->acceptfail) { - sftperr = libssh2_sftp_last_error(sshc->sftp_session); - Curl_safefree(sshc->quote_path1); - failf(data, "mkdir command failed: %s", - sftp_libssh2_strerror(sftperr)); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - state(data, SSH_SFTP_NEXT_QUOTE); - break; - - case SSH_SFTP_QUOTE_RENAME: - rc = libssh2_sftp_rename_ex(sshc->sftp_session, sshc->quote_path1, - curlx_uztoui(strlen(sshc->quote_path1)), - sshc->quote_path2, - curlx_uztoui(strlen(sshc->quote_path2)), - LIBSSH2_SFTP_RENAME_OVERWRITE | - LIBSSH2_SFTP_RENAME_ATOMIC | - LIBSSH2_SFTP_RENAME_NATIVE); - - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc && !sshc->acceptfail) { - sftperr = libssh2_sftp_last_error(sshc->sftp_session); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "rename command failed: %s", - sftp_libssh2_strerror(sftperr)); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - state(data, SSH_SFTP_NEXT_QUOTE); - break; - - case SSH_SFTP_QUOTE_RMDIR: - rc = libssh2_sftp_rmdir_ex(sshc->sftp_session, sshc->quote_path1, - curlx_uztoui(strlen(sshc->quote_path1))); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc && !sshc->acceptfail) { - sftperr = libssh2_sftp_last_error(sshc->sftp_session); - Curl_safefree(sshc->quote_path1); - failf(data, "rmdir command failed: %s", - sftp_libssh2_strerror(sftperr)); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - state(data, SSH_SFTP_NEXT_QUOTE); - break; - - case SSH_SFTP_QUOTE_UNLINK: - rc = libssh2_sftp_unlink_ex(sshc->sftp_session, sshc->quote_path1, - curlx_uztoui(strlen(sshc->quote_path1))); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc && !sshc->acceptfail) { - sftperr = libssh2_sftp_last_error(sshc->sftp_session); - Curl_safefree(sshc->quote_path1); - failf(data, "rm command failed: %s", sftp_libssh2_strerror(sftperr)); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - state(data, SSH_SFTP_NEXT_QUOTE); - break; - -#ifdef HAS_STATVFS_SUPPORT - case SSH_SFTP_QUOTE_STATVFS: - { - LIBSSH2_SFTP_STATVFS statvfs; - rc = libssh2_sftp_statvfs(sshc->sftp_session, sshc->quote_path1, - curlx_uztoui(strlen(sshc->quote_path1)), - &statvfs); - - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc && !sshc->acceptfail) { - sftperr = libssh2_sftp_last_error(sshc->sftp_session); - Curl_safefree(sshc->quote_path1); - failf(data, "statvfs command failed: %s", - sftp_libssh2_strerror(sftperr)); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - else if(rc == 0) { - char *tmp = aprintf("statvfs:\n" - "f_bsize: %llu\n" "f_frsize: %llu\n" - "f_blocks: %llu\n" "f_bfree: %llu\n" - "f_bavail: %llu\n" "f_files: %llu\n" - "f_ffree: %llu\n" "f_favail: %llu\n" - "f_fsid: %llu\n" "f_flag: %llu\n" - "f_namemax: %llu\n", - statvfs.f_bsize, statvfs.f_frsize, - statvfs.f_blocks, statvfs.f_bfree, - statvfs.f_bavail, statvfs.f_files, - statvfs.f_ffree, statvfs.f_favail, - statvfs.f_fsid, statvfs.f_flag, - statvfs.f_namemax); - if(!tmp) { - result = CURLE_OUT_OF_MEMORY; - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - break; - } - - result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); - free(tmp); - if(result) { - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - } - } - state(data, SSH_SFTP_NEXT_QUOTE); - break; - } -#endif - case SSH_SFTP_GETINFO: - { - if(data->set.get_filetime) { - state(data, SSH_SFTP_FILETIME); - } - else { - state(data, SSH_SFTP_TRANS_INIT); - } - break; - } - - case SSH_SFTP_FILETIME: - { - LIBSSH2_SFTP_ATTRIBUTES attrs; - - rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path, - curlx_uztoui(strlen(sshp->path)), - LIBSSH2_SFTP_STAT, &attrs); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc == 0) { - data->info.filetime = attrs.mtime; - } - - state(data, SSH_SFTP_TRANS_INIT); - break; - } - - case SSH_SFTP_TRANS_INIT: - if(data->set.upload) - state(data, SSH_SFTP_UPLOAD_INIT); - else { - if(sshp->path[strlen(sshp->path)-1] == '/') - state(data, SSH_SFTP_READDIR_INIT); - else - state(data, SSH_SFTP_DOWNLOAD_INIT); - } - break; - - case SSH_SFTP_UPLOAD_INIT: - { - unsigned long flags; - /* - * NOTE!!! libssh2 requires that the destination path is a full path - * that includes the destination file and name OR ends in a "/" - * If this is not done the destination file will be named the - * same name as the last directory in the path. - */ - - if(data->state.resume_from) { - LIBSSH2_SFTP_ATTRIBUTES attrs; - if(data->state.resume_from < 0) { - rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path, - curlx_uztoui(strlen(sshp->path)), - LIBSSH2_SFTP_STAT, &attrs); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc) { - data->state.resume_from = 0; - } - else { - curl_off_t size = attrs.filesize; - if(size < 0) { - failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); - return CURLE_BAD_DOWNLOAD_RESUME; - } - data->state.resume_from = attrs.filesize; - } - } - } - - if(data->set.remote_append) - /* Try to open for append, but create if nonexisting */ - flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_APPEND; - else if(data->state.resume_from > 0) - /* If we have restart position then open for append */ - flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_APPEND; - else - /* Clear file before writing (normal behavior) */ - flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC; - - sshc->sftp_handle = - libssh2_sftp_open_ex(sshc->sftp_session, sshp->path, - curlx_uztoui(strlen(sshp->path)), - flags, data->set.new_file_perms, - LIBSSH2_SFTP_OPENFILE); - - if(!sshc->sftp_handle) { - rc = libssh2_session_last_errno(sshc->ssh_session); - - if(LIBSSH2_ERROR_EAGAIN == rc) - break; - - if(LIBSSH2_ERROR_SFTP_PROTOCOL == rc) - /* only when there was an SFTP protocol error can we extract - the sftp error! */ - sftperr = libssh2_sftp_last_error(sshc->sftp_session); - else - sftperr = LIBSSH2_FX_OK; /* not an sftp error at all */ - - if(sshc->secondCreateDirs) { - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = sftperr != LIBSSH2_FX_OK ? - sftp_libssh2_error_to_CURLE(sftperr):CURLE_SSH; - failf(data, "Creating the dir/file failed: %s", - sftp_libssh2_strerror(sftperr)); - break; - } - if(((sftperr == LIBSSH2_FX_NO_SUCH_FILE) || - (sftperr == LIBSSH2_FX_FAILURE) || - (sftperr == LIBSSH2_FX_NO_SUCH_PATH)) && - (data->set.ftp_create_missing_dirs && - (strlen(sshp->path) > 1))) { - /* try to create the path remotely */ - rc = 0; /* clear rc and continue */ - sshc->secondCreateDirs = 1; - state(data, SSH_SFTP_CREATE_DIRS_INIT); - break; - } - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = sftperr != LIBSSH2_FX_OK ? - sftp_libssh2_error_to_CURLE(sftperr):CURLE_SSH; - if(!sshc->actualcode) { - /* Sometimes, for some reason libssh2_sftp_last_error() returns zero - even though libssh2_sftp_open() failed previously! We need to - work around that! */ - sshc->actualcode = CURLE_SSH; - sftperr = LIBSSH2_FX_OK; - } - failf(data, "Upload failed: %s (%lu/%d)", - sftperr != LIBSSH2_FX_OK ? - sftp_libssh2_strerror(sftperr):"ssh error", - sftperr, rc); - break; - } - - /* If we have a restart point then we need to seek to the correct - position. */ - if(data->state.resume_from > 0) { - /* Let's read off the proper amount of bytes from the input. */ - if(conn->seek_func) { - Curl_set_in_callback(data, true); - seekerr = conn->seek_func(conn->seek_client, data->state.resume_from, - SEEK_SET); - Curl_set_in_callback(data, false); - } - - if(seekerr != CURL_SEEKFUNC_OK) { - curl_off_t passed = 0; - - if(seekerr != CURL_SEEKFUNC_CANTSEEK) { - failf(data, "Could not seek stream"); - return CURLE_FTP_COULDNT_USE_REST; - } - /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */ - do { - size_t readthisamountnow = - (data->state.resume_from - passed > data->set.buffer_size) ? - (size_t)data->set.buffer_size : - curlx_sotouz(data->state.resume_from - passed); - - size_t actuallyread; - Curl_set_in_callback(data, true); - actuallyread = data->state.fread_func(data->state.buffer, 1, - readthisamountnow, - data->state.in); - Curl_set_in_callback(data, false); - - passed += actuallyread; - if((actuallyread == 0) || (actuallyread > readthisamountnow)) { - /* this checks for greater-than only to make sure that the - CURL_READFUNC_ABORT return code still aborts */ - failf(data, "Failed to read data"); - return CURLE_FTP_COULDNT_USE_REST; - } - } while(passed < data->state.resume_from); - } - - /* now, decrease the size of the read */ - if(data->state.infilesize > 0) { - data->state.infilesize -= data->state.resume_from; - data->req.size = data->state.infilesize; - Curl_pgrsSetUploadSize(data, data->state.infilesize); - } - - SFTP_SEEK(sshc->sftp_handle, data->state.resume_from); - } - if(data->state.infilesize > 0) { - data->req.size = data->state.infilesize; - Curl_pgrsSetUploadSize(data, data->state.infilesize); - } - /* upload data */ - Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); - - /* not set by Curl_setup_transfer to preserve keepon bits */ - conn->sockfd = conn->writesockfd; - - if(result) { - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = result; - } - else { - /* store this original bitmask setup to use later on if we can't - figure out a "real" bitmask */ - sshc->orig_waitfor = data->req.keepon; - - /* we want to use the _sending_ function even when the socket turns - out readable as the underlying libssh2 sftp send function will deal - with both accordingly */ - conn->cselect_bits = CURL_CSELECT_OUT; - - /* since we don't really wait for anything at this point, we want the - state machine to move on as soon as possible so we set a very short - timeout here */ - Curl_expire(data, 0, EXPIRE_RUN_NOW); - - state(data, SSH_STOP); - } - break; - } - - case SSH_SFTP_CREATE_DIRS_INIT: - if(strlen(sshp->path) > 1) { - sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */ - state(data, SSH_SFTP_CREATE_DIRS); - } - else { - state(data, SSH_SFTP_UPLOAD_INIT); - } - break; - - case SSH_SFTP_CREATE_DIRS: - sshc->slash_pos = strchr(sshc->slash_pos, '/'); - if(sshc->slash_pos) { - *sshc->slash_pos = 0; - - infof(data, "Creating directory '%s'", sshp->path); - state(data, SSH_SFTP_CREATE_DIRS_MKDIR); - break; - } - state(data, SSH_SFTP_UPLOAD_INIT); - break; - - case SSH_SFTP_CREATE_DIRS_MKDIR: - /* 'mode' - parameter is preliminary - default to 0644 */ - rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshp->path, - curlx_uztoui(strlen(sshp->path)), - data->set.new_directory_perms); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - *sshc->slash_pos = '/'; - ++sshc->slash_pos; - if(rc < 0) { - /* - * Abort if failure wasn't that the dir already exists or the - * permission was denied (creation might succeed further down the - * path) - retry on unspecific FAILURE also - */ - sftperr = libssh2_sftp_last_error(sshc->sftp_session); - if((sftperr != LIBSSH2_FX_FILE_ALREADY_EXISTS) && - (sftperr != LIBSSH2_FX_FAILURE) && - (sftperr != LIBSSH2_FX_PERMISSION_DENIED)) { - result = sftp_libssh2_error_to_CURLE(sftperr); - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = result?result:CURLE_SSH; - break; - } - rc = 0; /* clear rc and continue */ - } - state(data, SSH_SFTP_CREATE_DIRS); - break; - - case SSH_SFTP_READDIR_INIT: - Curl_pgrsSetDownloadSize(data, -1); - if(data->set.opt_no_body) { - state(data, SSH_STOP); - break; - } - - /* - * This is a directory that we are trying to get, so produce a directory - * listing - */ - sshc->sftp_handle = libssh2_sftp_open_ex(sshc->sftp_session, - sshp->path, - curlx_uztoui( - strlen(sshp->path)), - 0, 0, LIBSSH2_SFTP_OPENDIR); - if(!sshc->sftp_handle) { - if(libssh2_session_last_errno(sshc->ssh_session) == - LIBSSH2_ERROR_EAGAIN) { - rc = LIBSSH2_ERROR_EAGAIN; - break; - } - sftperr = libssh2_sftp_last_error(sshc->sftp_session); - failf(data, "Could not open directory for reading: %s", - sftp_libssh2_strerror(sftperr)); - state(data, SSH_SFTP_CLOSE); - result = sftp_libssh2_error_to_CURLE(sftperr); - sshc->actualcode = result?result:CURLE_SSH; - break; - } - sshp->readdir_filename = malloc(PATH_MAX + 1); - if(!sshp->readdir_filename) { - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; - break; - } - sshp->readdir_longentry = malloc(PATH_MAX + 1); - if(!sshp->readdir_longentry) { - Curl_safefree(sshp->readdir_filename); - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; - break; - } - Curl_dyn_init(&sshp->readdir, PATH_MAX * 2); - state(data, SSH_SFTP_READDIR); - break; - - case SSH_SFTP_READDIR: - rc = libssh2_sftp_readdir_ex(sshc->sftp_handle, - sshp->readdir_filename, - PATH_MAX, - sshp->readdir_longentry, - PATH_MAX, - &sshp->readdir_attrs); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc > 0) { - readdir_len = (size_t) rc; - sshp->readdir_filename[readdir_len] = '\0'; - - if(data->set.list_only) { - result = Curl_client_write(data, CLIENTWRITE_BODY, - sshp->readdir_filename, - readdir_len); - if(!result) - result = Curl_client_write(data, CLIENTWRITE_BODY, - (char *)"\n", 1); - if(result) { - state(data, SSH_STOP); - break; - } - /* since this counts what we send to the client, we include the - newline in this counter */ - data->req.bytecount += readdir_len + 1; - - /* output debug output if that is requested */ - Curl_debug(data, CURLINFO_DATA_IN, sshp->readdir_filename, - readdir_len); - Curl_debug(data, CURLINFO_DATA_IN, (char *)"\n", 1); - } - else { - result = Curl_dyn_add(&sshp->readdir, sshp->readdir_longentry); - - if(!result) { - if((sshp->readdir_attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) && - ((sshp->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) == - LIBSSH2_SFTP_S_IFLNK)) { - Curl_dyn_init(&sshp->readdir_link, PATH_MAX); - result = Curl_dyn_addf(&sshp->readdir_link, "%s%s", sshp->path, - sshp->readdir_filename); - state(data, SSH_SFTP_READDIR_LINK); - if(!result) - break; - } - else { - state(data, SSH_SFTP_READDIR_BOTTOM); - break; - } - } - sshc->actualcode = result; - state(data, SSH_SFTP_CLOSE); - break; - } - } - else if(rc == 0) { - Curl_safefree(sshp->readdir_filename); - Curl_safefree(sshp->readdir_longentry); - state(data, SSH_SFTP_READDIR_DONE); - break; - } - else if(rc < 0) { - sftperr = libssh2_sftp_last_error(sshc->sftp_session); - result = sftp_libssh2_error_to_CURLE(sftperr); - sshc->actualcode = result?result:CURLE_SSH; - failf(data, "Could not open remote file for reading: %s :: %d", - sftp_libssh2_strerror(sftperr), - libssh2_session_last_errno(sshc->ssh_session)); - Curl_safefree(sshp->readdir_filename); - Curl_safefree(sshp->readdir_longentry); - state(data, SSH_SFTP_CLOSE); - break; - } - break; - - case SSH_SFTP_READDIR_LINK: - rc = - libssh2_sftp_symlink_ex(sshc->sftp_session, - Curl_dyn_ptr(&sshp->readdir_link), - (int)Curl_dyn_len(&sshp->readdir_link), - sshp->readdir_filename, - PATH_MAX, LIBSSH2_SFTP_READLINK); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - Curl_dyn_free(&sshp->readdir_link); - - /* append filename and extra output */ - result = Curl_dyn_addf(&sshp->readdir, " -> %s", sshp->readdir_filename); - - if(result) { - sshc->readdir_line = NULL; - Curl_safefree(sshp->readdir_filename); - Curl_safefree(sshp->readdir_longentry); - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = result; - break; - } - - state(data, SSH_SFTP_READDIR_BOTTOM); - break; - - case SSH_SFTP_READDIR_BOTTOM: - result = Curl_dyn_addn(&sshp->readdir, "\n", 1); - if(!result) - result = Curl_client_write(data, CLIENTWRITE_BODY, - Curl_dyn_ptr(&sshp->readdir), - Curl_dyn_len(&sshp->readdir)); - - if(!result) { - /* output debug output if that is requested */ - Curl_debug(data, CURLINFO_DATA_IN, - Curl_dyn_ptr(&sshp->readdir), - Curl_dyn_len(&sshp->readdir)); - data->req.bytecount += Curl_dyn_len(&sshp->readdir); - } - if(result) { - Curl_dyn_free(&sshp->readdir); - state(data, SSH_STOP); - } - else { - Curl_dyn_reset(&sshp->readdir); - state(data, SSH_SFTP_READDIR); - } - break; - - case SSH_SFTP_READDIR_DONE: - if(libssh2_sftp_closedir(sshc->sftp_handle) == - LIBSSH2_ERROR_EAGAIN) { - rc = LIBSSH2_ERROR_EAGAIN; - break; - } - sshc->sftp_handle = NULL; - Curl_safefree(sshp->readdir_filename); - Curl_safefree(sshp->readdir_longentry); - - /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); - state(data, SSH_STOP); - break; - - case SSH_SFTP_DOWNLOAD_INIT: - /* - * Work on getting the specified file - */ - sshc->sftp_handle = - libssh2_sftp_open_ex(sshc->sftp_session, sshp->path, - curlx_uztoui(strlen(sshp->path)), - LIBSSH2_FXF_READ, data->set.new_file_perms, - LIBSSH2_SFTP_OPENFILE); - if(!sshc->sftp_handle) { - if(libssh2_session_last_errno(sshc->ssh_session) == - LIBSSH2_ERROR_EAGAIN) { - rc = LIBSSH2_ERROR_EAGAIN; - break; - } - sftperr = libssh2_sftp_last_error(sshc->sftp_session); - failf(data, "Could not open remote file for reading: %s", - sftp_libssh2_strerror(sftperr)); - state(data, SSH_SFTP_CLOSE); - result = sftp_libssh2_error_to_CURLE(sftperr); - sshc->actualcode = result?result:CURLE_SSH; - break; - } - state(data, SSH_SFTP_DOWNLOAD_STAT); - break; - - case SSH_SFTP_DOWNLOAD_STAT: - { - LIBSSH2_SFTP_ATTRIBUTES attrs; - - rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path, - curlx_uztoui(strlen(sshp->path)), - LIBSSH2_SFTP_STAT, &attrs); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc || - !(attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) || - (attrs.filesize == 0)) { - /* - * libssh2_sftp_open() didn't return an error, so maybe the server - * just doesn't support stat() - * OR the server doesn't return a file size with a stat() - * OR file size is 0 - */ - data->req.size = -1; - data->req.maxdownload = -1; - Curl_pgrsSetDownloadSize(data, -1); - } - else { - curl_off_t size = attrs.filesize; - - if(size < 0) { - failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); - return CURLE_BAD_DOWNLOAD_RESUME; - } - if(data->state.use_range) { - curl_off_t from, to; - char *ptr; - char *ptr2; - CURLofft to_t; - CURLofft from_t; - - from_t = curlx_strtoofft(data->state.range, &ptr, 0, &from); - if(from_t == CURL_OFFT_FLOW) - return CURLE_RANGE_ERROR; - while(*ptr && (ISBLANK(*ptr) || (*ptr == '-'))) - ptr++; - to_t = curlx_strtoofft(ptr, &ptr2, 0, &to); - if(to_t == CURL_OFFT_FLOW) - return CURLE_RANGE_ERROR; - if((to_t == CURL_OFFT_INVAL) /* no "to" value given */ - || (to >= size)) { - to = size - 1; - } - if(from_t) { - /* from is relative to end of file */ - from = size - to; - to = size - 1; - } - if(from > size) { - failf(data, "Offset (%" - CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" - CURL_FORMAT_CURL_OFF_T ")", from, attrs.filesize); - return CURLE_BAD_DOWNLOAD_RESUME; - } - if(from > to) { - from = to; - size = 0; - } - else { - size = to - from + 1; - } - - SFTP_SEEK(sshc->sftp_handle, from); - } - data->req.size = size; - data->req.maxdownload = size; - Curl_pgrsSetDownloadSize(data, size); - } - - /* We can resume if we can seek to the resume position */ - if(data->state.resume_from) { - if(data->state.resume_from < 0) { - /* We're supposed to download the last abs(from) bytes */ - if((curl_off_t)attrs.filesize < -data->state.resume_from) { - failf(data, "Offset (%" - CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" - CURL_FORMAT_CURL_OFF_T ")", - data->state.resume_from, attrs.filesize); - return CURLE_BAD_DOWNLOAD_RESUME; - } - /* download from where? */ - data->state.resume_from += attrs.filesize; - } - else { - if((curl_off_t)attrs.filesize < data->state.resume_from) { - failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T - ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")", - data->state.resume_from, attrs.filesize); - return CURLE_BAD_DOWNLOAD_RESUME; - } - } - /* Now store the number of bytes we are expected to download */ - data->req.size = attrs.filesize - data->state.resume_from; - data->req.maxdownload = attrs.filesize - data->state.resume_from; - Curl_pgrsSetDownloadSize(data, - attrs.filesize - data->state.resume_from); - SFTP_SEEK(sshc->sftp_handle, data->state.resume_from); - } - } - - /* Setup the actual download */ - if(data->req.size == 0) { - /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); - infof(data, "File already completely downloaded"); - state(data, SSH_STOP); - break; - } - Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1); - - /* not set by Curl_setup_transfer to preserve keepon bits */ - conn->writesockfd = conn->sockfd; - - /* we want to use the _receiving_ function even when the socket turns - out writableable as the underlying libssh2 recv function will deal - with both accordingly */ - conn->cselect_bits = CURL_CSELECT_IN; - - if(result) { - /* this should never occur; the close state should be entered - at the time the error occurs */ - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = result; - } - else { - state(data, SSH_STOP); - } - break; - - case SSH_SFTP_CLOSE: - if(sshc->sftp_handle) { - rc = libssh2_sftp_close(sshc->sftp_handle); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc < 0) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg); - } - sshc->sftp_handle = NULL; - } - - Curl_safefree(sshp->path); - - DEBUGF(infof(data, "SFTP DONE done")); - - /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT - After nextstate is executed, the control should come back to - SSH_SFTP_CLOSE to pass the correct result back */ - if(sshc->nextstate != SSH_NO_STATE && - sshc->nextstate != SSH_SFTP_CLOSE) { - state(data, sshc->nextstate); - sshc->nextstate = SSH_SFTP_CLOSE; - } - else { - state(data, SSH_STOP); - result = sshc->actualcode; - } - break; - - case SSH_SFTP_SHUTDOWN: - /* during times we get here due to a broken transfer and then the - sftp_handle might not have been taken down so make sure that is done - before we proceed */ - - if(sshc->sftp_handle) { - rc = libssh2_sftp_close(sshc->sftp_handle); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc < 0) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, - NULL, 0); - infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg); - } - sshc->sftp_handle = NULL; - } - if(sshc->sftp_session) { - rc = libssh2_sftp_shutdown(sshc->sftp_session); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc < 0) { - infof(data, "Failed to stop libssh2 sftp subsystem"); - } - sshc->sftp_session = NULL; - } - - Curl_safefree(sshc->homedir); - data->state.most_recent_ftp_entrypath = NULL; - - state(data, SSH_SESSION_DISCONNECT); - break; - - case SSH_SCP_TRANS_INIT: - result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); - if(result) { - sshc->actualcode = result; - state(data, SSH_STOP); - break; - } - - if(data->set.upload) { - if(data->state.infilesize < 0) { - failf(data, "SCP requires a known file size for upload"); - sshc->actualcode = CURLE_UPLOAD_FAILED; - state(data, SSH_SCP_CHANNEL_FREE); - break; - } - state(data, SSH_SCP_UPLOAD_INIT); - } - else { - state(data, SSH_SCP_DOWNLOAD_INIT); - } - break; - - case SSH_SCP_UPLOAD_INIT: - /* - * libssh2 requires that the destination path is a full path that - * includes the destination file and name OR ends in a "/" . If this is - * not done the destination file will be named the same name as the last - * directory in the path. - */ - sshc->ssh_channel = - SCP_SEND(sshc->ssh_session, sshp->path, data->set.new_file_perms, - data->state.infilesize); - if(!sshc->ssh_channel) { - int ssh_err; - char *err_msg = NULL; - - if(libssh2_session_last_errno(sshc->ssh_session) == - LIBSSH2_ERROR_EAGAIN) { - rc = LIBSSH2_ERROR_EAGAIN; - break; - } - - ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0)); - failf(data, "%s", err_msg); - state(data, SSH_SCP_CHANNEL_FREE); - sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err); - /* Map generic errors to upload failed */ - if(sshc->actualcode == CURLE_SSH || - sshc->actualcode == CURLE_REMOTE_FILE_NOT_FOUND) - sshc->actualcode = CURLE_UPLOAD_FAILED; - break; - } - - /* upload data */ - data->req.size = data->state.infilesize; - Curl_pgrsSetUploadSize(data, data->state.infilesize); - Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); - - /* not set by Curl_setup_transfer to preserve keepon bits */ - conn->sockfd = conn->writesockfd; - - if(result) { - state(data, SSH_SCP_CHANNEL_FREE); - sshc->actualcode = result; - } - else { - /* store this original bitmask setup to use later on if we can't - figure out a "real" bitmask */ - sshc->orig_waitfor = data->req.keepon; - - /* we want to use the _sending_ function even when the socket turns - out readable as the underlying libssh2 scp send function will deal - with both accordingly */ - conn->cselect_bits = CURL_CSELECT_OUT; - - state(data, SSH_STOP); - } - break; - - case SSH_SCP_DOWNLOAD_INIT: - { - curl_off_t bytecount; - - /* - * We must check the remote file; if it is a directory no values will - * be set in sb - */ - - /* - * If support for >2GB files exists, use it. - */ - - /* get a fresh new channel from the ssh layer */ -#if LIBSSH2_VERSION_NUM < 0x010700 - struct stat sb; - memset(&sb, 0, sizeof(struct stat)); - sshc->ssh_channel = libssh2_scp_recv(sshc->ssh_session, - sshp->path, &sb); -#else - libssh2_struct_stat sb; - memset(&sb, 0, sizeof(libssh2_struct_stat)); - sshc->ssh_channel = libssh2_scp_recv2(sshc->ssh_session, - sshp->path, &sb); -#endif - - if(!sshc->ssh_channel) { - int ssh_err; - char *err_msg = NULL; - - if(libssh2_session_last_errno(sshc->ssh_session) == - LIBSSH2_ERROR_EAGAIN) { - rc = LIBSSH2_ERROR_EAGAIN; - break; - } - - - ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0)); - failf(data, "%s", err_msg); - state(data, SSH_SCP_CHANNEL_FREE); - sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err); - break; - } - - /* download data */ - bytecount = (curl_off_t)sb.st_size; - data->req.maxdownload = (curl_off_t)sb.st_size; - Curl_setup_transfer(data, FIRSTSOCKET, bytecount, FALSE, -1); - - /* not set by Curl_setup_transfer to preserve keepon bits */ - conn->writesockfd = conn->sockfd; - - /* we want to use the _receiving_ function even when the socket turns - out writableable as the underlying libssh2 recv function will deal - with both accordingly */ - conn->cselect_bits = CURL_CSELECT_IN; - - if(result) { - state(data, SSH_SCP_CHANNEL_FREE); - sshc->actualcode = result; - } - else - state(data, SSH_STOP); - } - break; - - case SSH_SCP_DONE: - if(data->set.upload) - state(data, SSH_SCP_SEND_EOF); - else - state(data, SSH_SCP_CHANNEL_FREE); - break; - - case SSH_SCP_SEND_EOF: - if(sshc->ssh_channel) { - rc = libssh2_channel_send_eof(sshc->ssh_channel); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Failed to send libssh2 channel EOF: %d %s", - rc, err_msg); - } - } - state(data, SSH_SCP_WAIT_EOF); - break; - - case SSH_SCP_WAIT_EOF: - if(sshc->ssh_channel) { - rc = libssh2_channel_wait_eof(sshc->ssh_channel); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Failed to get channel EOF: %d %s", rc, err_msg); - } - } - state(data, SSH_SCP_WAIT_CLOSE); - break; - - case SSH_SCP_WAIT_CLOSE: - if(sshc->ssh_channel) { - rc = libssh2_channel_wait_closed(sshc->ssh_channel); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Channel failed to close: %d %s", rc, err_msg); - } - } - state(data, SSH_SCP_CHANNEL_FREE); - break; - - case SSH_SCP_CHANNEL_FREE: - if(sshc->ssh_channel) { - rc = libssh2_channel_free(sshc->ssh_channel); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc < 0) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Failed to free libssh2 scp subsystem: %d %s", - rc, err_msg); - } - sshc->ssh_channel = NULL; - } - DEBUGF(infof(data, "SCP DONE phase complete")); -#if 0 /* PREV */ - state(data, SSH_SESSION_DISCONNECT); -#endif - state(data, SSH_STOP); - result = sshc->actualcode; - break; - - case SSH_SESSION_DISCONNECT: - /* during weird times when we've been prematurely aborted, the channel - is still alive when we reach this state and we MUST kill the channel - properly first */ - if(sshc->ssh_channel) { - rc = libssh2_channel_free(sshc->ssh_channel); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc < 0) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Failed to free libssh2 scp subsystem: %d %s", - rc, err_msg); - } - sshc->ssh_channel = NULL; - } - - if(sshc->ssh_session) { - rc = libssh2_session_disconnect(sshc->ssh_session, "Shutdown"); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc < 0) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Failed to disconnect libssh2 session: %d %s", - rc, err_msg); - } - } - - Curl_safefree(sshc->homedir); - data->state.most_recent_ftp_entrypath = NULL; - - state(data, SSH_SESSION_FREE); - break; - - case SSH_SESSION_FREE: -#ifdef HAVE_LIBSSH2_KNOWNHOST_API - if(sshc->kh) { - libssh2_knownhost_free(sshc->kh); - sshc->kh = NULL; - } -#endif - -#ifdef HAVE_LIBSSH2_AGENT_API - if(sshc->ssh_agent) { - rc = libssh2_agent_disconnect(sshc->ssh_agent); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc < 0) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Failed to disconnect from libssh2 agent: %d %s", - rc, err_msg); - } - libssh2_agent_free(sshc->ssh_agent); - sshc->ssh_agent = NULL; - - /* NB: there is no need to free identities, they are part of internal - agent stuff */ - sshc->sshagent_identity = NULL; - sshc->sshagent_prev_identity = NULL; - } -#endif - - if(sshc->ssh_session) { - rc = libssh2_session_free(sshc->ssh_session); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc < 0) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Failed to free libssh2 session: %d %s", rc, err_msg); - } - sshc->ssh_session = NULL; - } - - /* worst-case scenario cleanup */ - - DEBUGASSERT(sshc->ssh_session == NULL); - DEBUGASSERT(sshc->ssh_channel == NULL); - DEBUGASSERT(sshc->sftp_session == NULL); - DEBUGASSERT(sshc->sftp_handle == NULL); -#ifdef HAVE_LIBSSH2_KNOWNHOST_API - DEBUGASSERT(sshc->kh == NULL); -#endif -#ifdef HAVE_LIBSSH2_AGENT_API - DEBUGASSERT(sshc->ssh_agent == NULL); -#endif - - Curl_safefree(sshc->rsa_pub); - Curl_safefree(sshc->rsa); - - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - - Curl_safefree(sshc->homedir); - Curl_safefree(sshc->readdir_line); - - /* the code we are about to return */ - result = sshc->actualcode; - - memset(sshc, 0, sizeof(struct ssh_conn)); - - connclose(conn, "SSH session free"); - sshc->state = SSH_SESSION_FREE; /* current */ - sshc->nextstate = SSH_NO_STATE; - state(data, SSH_STOP); - break; - - case SSH_QUIT: - /* fallthrough, just stop! */ - default: - /* internal error */ - sshc->nextstate = SSH_NO_STATE; - state(data, SSH_STOP); - break; - } - - } while(!rc && (sshc->state != SSH_STOP)); - - if(rc == LIBSSH2_ERROR_EAGAIN) { - /* we would block, we need to wait for the socket to be ready (in the - right direction too)! */ - *block = TRUE; - } - - return result; -} - -/* called by the multi interface to figure out what socket(s) to wait for and - for what actions in the DO_DONE, PERFORM and WAITPERFORM states */ -static int ssh_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *sock) -{ - int bitmap = GETSOCK_BLANK; - (void)data; - - sock[0] = conn->sock[FIRSTSOCKET]; - - if(conn->waitfor & KEEP_RECV) - bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); - - if(conn->waitfor & KEEP_SEND) - bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); - - return bitmap; -} - -/* - * When one of the libssh2 functions has returned LIBSSH2_ERROR_EAGAIN this - * function is used to figure out in what direction and stores this info so - * that the multi interface can take advantage of it. Make sure to call this - * function in all cases so that when it _doesn't_ return EAGAIN we can - * restore the default wait bits. - */ -static void ssh_block2waitfor(struct Curl_easy *data, bool block) -{ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - int dir = 0; - if(block) { - dir = libssh2_session_block_directions(sshc->ssh_session); - if(dir) { - /* translate the libssh2 define bits into our own bit defines */ - conn->waitfor = ((dir&LIBSSH2_SESSION_BLOCK_INBOUND)?KEEP_RECV:0) | - ((dir&LIBSSH2_SESSION_BLOCK_OUTBOUND)?KEEP_SEND:0); - } - } - if(!dir) - /* It didn't block or libssh2 didn't reveal in which direction, put back - the original set */ - conn->waitfor = sshc->orig_waitfor; -} - -/* called repeatedly until done from multi.c */ -static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - CURLcode result = CURLE_OK; - bool block; /* we store the status and use that to provide a ssh_getsock() - implementation */ - do { - result = ssh_statemach_act(data, &block); - *done = (sshc->state == SSH_STOP) ? TRUE : FALSE; - /* if there's no error, it isn't done and it didn't EWOULDBLOCK, then - try again */ - } while(!result && !*done && !block); - ssh_block2waitfor(data, block); - - return result; -} - -static CURLcode ssh_block_statemach(struct Curl_easy *data, - struct connectdata *conn, - bool disconnect) -{ - struct ssh_conn *sshc = &conn->proto.sshc; - CURLcode result = CURLE_OK; - struct curltime dis = Curl_now(); - - while((sshc->state != SSH_STOP) && !result) { - bool block; - timediff_t left = 1000; - struct curltime now = Curl_now(); - - result = ssh_statemach_act(data, &block); - if(result) - break; - - if(!disconnect) { - if(Curl_pgrsUpdate(data)) - return CURLE_ABORTED_BY_CALLBACK; - - result = Curl_speedcheck(data, now); - if(result) - break; - - left = Curl_timeleft(data, NULL, FALSE); - if(left < 0) { - failf(data, "Operation timed out"); - return CURLE_OPERATION_TIMEDOUT; - } - } - else if(Curl_timediff(now, dis) > 1000) { - /* disconnect timeout */ - failf(data, "Disconnect timed out"); - result = CURLE_OK; - break; - } - - if(block) { - int dir = libssh2_session_block_directions(sshc->ssh_session); - curl_socket_t sock = conn->sock[FIRSTSOCKET]; - curl_socket_t fd_read = CURL_SOCKET_BAD; - curl_socket_t fd_write = CURL_SOCKET_BAD; - if(LIBSSH2_SESSION_BLOCK_INBOUND & dir) - fd_read = sock; - if(LIBSSH2_SESSION_BLOCK_OUTBOUND & dir) - fd_write = sock; - /* wait for the socket to become ready */ - (void)Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write, - left>1000?1000:left); - } - } - - return result; -} - -/* - * SSH setup and connection - */ -static CURLcode ssh_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - struct SSHPROTO *ssh; - (void)conn; - - data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO)); - if(!ssh) - return CURLE_OUT_OF_MEMORY; - - return CURLE_OK; -} - -static Curl_recv scp_recv, sftp_recv; -static Curl_send scp_send, sftp_send; - -#ifndef CURL_DISABLE_PROXY -static ssize_t ssh_tls_recv(libssh2_socket_t sock, void *buffer, - size_t length, int flags, void **abstract) -{ - struct Curl_easy *data = (struct Curl_easy *)*abstract; - ssize_t nread; - CURLcode result; - struct connectdata *conn = data->conn; - Curl_recv *backup = conn->recv[0]; - struct ssh_conn *ssh = &conn->proto.sshc; - (void)flags; - - /* swap in the TLS reader function for this call only, and then swap back - the SSH one again */ - conn->recv[0] = ssh->tls_recv; - result = Curl_read(data, sock, buffer, length, &nread); - conn->recv[0] = backup; - if(result == CURLE_AGAIN) - return -EAGAIN; /* magic return code for libssh2 */ - else if(result) - return -1; /* generic error */ - Curl_debug(data, CURLINFO_DATA_IN, (char *)buffer, (size_t)nread); - return nread; -} - -static ssize_t ssh_tls_send(libssh2_socket_t sock, const void *buffer, - size_t length, int flags, void **abstract) -{ - struct Curl_easy *data = (struct Curl_easy *)*abstract; - ssize_t nwrite; - CURLcode result; - struct connectdata *conn = data->conn; - Curl_send *backup = conn->send[0]; - struct ssh_conn *ssh = &conn->proto.sshc; - (void)flags; - - /* swap in the TLS writer function for this call only, and then swap back - the SSH one again */ - conn->send[0] = ssh->tls_send; - result = Curl_write(data, sock, buffer, length, &nwrite); - conn->send[0] = backup; - if(result == CURLE_AGAIN) - return -EAGAIN; /* magic return code for libssh2 */ - else if(result) - return -1; /* error */ - Curl_debug(data, CURLINFO_DATA_OUT, (char *)buffer, (size_t)nwrite); - return nwrite; -} -#endif - -/* - * Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to - * do protocol-specific actions at connect-time. - */ -static CURLcode ssh_connect(struct Curl_easy *data, bool *done) -{ -#ifdef CURL_LIBSSH2_DEBUG - curl_socket_t sock; -#endif - struct ssh_conn *sshc; - CURLcode result; - struct connectdata *conn = data->conn; - - /* initialize per-handle data if not already */ - if(!data->req.p.ssh) { - result = ssh_setup_connection(data, conn); - if(result) - return result; - } - - /* We default to persistent connections. We set this already in this connect - function to make the re-use checks properly be able to check this bit. */ - connkeep(conn, "SSH default"); - - sshc = &conn->proto.sshc; - -#ifdef CURL_LIBSSH2_DEBUG - if(conn->user) { - infof(data, "User: %s", conn->user); - } - if(conn->passwd) { - infof(data, "Password: %s", conn->passwd); - } - sock = conn->sock[FIRSTSOCKET]; -#endif /* CURL_LIBSSH2_DEBUG */ - - sshc->ssh_session = libssh2_session_init_ex(my_libssh2_malloc, - my_libssh2_free, - my_libssh2_realloc, data); - if(!sshc->ssh_session) { - failf(data, "Failure initialising ssh session"); - return CURLE_FAILED_INIT; - } - -#ifndef CURL_DISABLE_PROXY - if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) { - /* - * This crazy union dance is here to avoid assigning a void pointer a - * function pointer as it is invalid C. The problem is of course that - * libssh2 has such an API... - */ - union receive { - void *recvp; - ssize_t (*recvptr)(libssh2_socket_t, void *, size_t, int, void **); - }; - union transfer { - void *sendp; - ssize_t (*sendptr)(libssh2_socket_t, const void *, size_t, int, void **); - }; - union receive sshrecv; - union transfer sshsend; - - sshrecv.recvptr = ssh_tls_recv; - sshsend.sendptr = ssh_tls_send; - - infof(data, "Uses HTTPS proxy"); - /* - Setup libssh2 callbacks to make it read/write TLS from the socket. - - ssize_t - recvcb(libssh2_socket_t sock, void *buffer, size_t length, - int flags, void **abstract); - - ssize_t - sendcb(libssh2_socket_t sock, const void *buffer, size_t length, - int flags, void **abstract); - - */ - libssh2_session_callback_set(sshc->ssh_session, - LIBSSH2_CALLBACK_RECV, sshrecv.recvp); - libssh2_session_callback_set(sshc->ssh_session, - LIBSSH2_CALLBACK_SEND, sshsend.sendp); - - /* Store the underlying TLS recv/send function pointers to be used when - reading from the proxy */ - sshc->tls_recv = conn->recv[FIRSTSOCKET]; - sshc->tls_send = conn->send[FIRSTSOCKET]; - } - -#endif /* CURL_DISABLE_PROXY */ - if(conn->handler->protocol & CURLPROTO_SCP) { - conn->recv[FIRSTSOCKET] = scp_recv; - conn->send[FIRSTSOCKET] = scp_send; - } - else { - conn->recv[FIRSTSOCKET] = sftp_recv; - conn->send[FIRSTSOCKET] = sftp_send; - } - - if(data->set.ssh_compression) { -#if LIBSSH2_VERSION_NUM >= 0x010208 - if(libssh2_session_flag(sshc->ssh_session, LIBSSH2_FLAG_COMPRESS, 1) < 0) -#endif - infof(data, "Failed to enable compression for ssh session"); - } - -#ifdef HAVE_LIBSSH2_KNOWNHOST_API - if(data->set.str[STRING_SSH_KNOWNHOSTS]) { - int rc; - sshc->kh = libssh2_knownhost_init(sshc->ssh_session); - if(!sshc->kh) { - libssh2_session_free(sshc->ssh_session); - sshc->ssh_session = NULL; - return CURLE_FAILED_INIT; - } - - /* read all known hosts from there */ - rc = libssh2_knownhost_readfile(sshc->kh, - data->set.str[STRING_SSH_KNOWNHOSTS], - LIBSSH2_KNOWNHOST_FILE_OPENSSH); - if(rc < 0) - infof(data, "Failed to read known hosts from %s", - data->set.str[STRING_SSH_KNOWNHOSTS]); - } -#endif /* HAVE_LIBSSH2_KNOWNHOST_API */ - -#ifdef CURL_LIBSSH2_DEBUG - libssh2_trace(sshc->ssh_session, ~0); - infof(data, "SSH socket: %d", (int)sock); -#endif /* CURL_LIBSSH2_DEBUG */ - - state(data, SSH_INIT); - - result = ssh_multi_statemach(data, done); - - return result; -} - -/* - *********************************************************************** - * - * scp_perform() - * - * This is the actual DO function for SCP. Get a file according to - * the options previously setup. - */ - -static -CURLcode scp_perform(struct Curl_easy *data, - bool *connected, - bool *dophase_done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - - DEBUGF(infof(data, "DO phase starts")); - - *dophase_done = FALSE; /* not done yet */ - - /* start the first command in the DO phase */ - state(data, SSH_SCP_TRANS_INIT); - - /* run the state-machine */ - result = ssh_multi_statemach(data, dophase_done); - - *connected = conn->bits.tcpconnect[FIRSTSOCKET]; - - if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); - } - - return result; -} - -/* called from multi.c while DOing */ -static CURLcode scp_doing(struct Curl_easy *data, - bool *dophase_done) -{ - CURLcode result; - result = ssh_multi_statemach(data, dophase_done); - - if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); - } - return result; -} - -/* - * The DO function is generic for both protocols. There was previously two - * separate ones but this way means less duplicated code. - */ - -static CURLcode ssh_do(struct Curl_easy *data, bool *done) -{ - CURLcode result; - bool connected = 0; - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - - *done = FALSE; /* default to false */ - - data->req.size = -1; /* make sure this is unknown at this point */ - - sshc->actualcode = CURLE_OK; /* reset error code */ - sshc->secondCreateDirs = 0; /* reset the create dir attempt state - variable */ - - Curl_pgrsSetUploadCounter(data, 0); - Curl_pgrsSetDownloadCounter(data, 0); - Curl_pgrsSetUploadSize(data, -1); - Curl_pgrsSetDownloadSize(data, -1); - - if(conn->handler->protocol & CURLPROTO_SCP) - result = scp_perform(data, &connected, done); - else - result = sftp_perform(data, &connected, done); - - return result; -} - -/* BLOCKING, but the function is using the state machine so the only reason - this is still blocking is that the multi interface code has no support for - disconnecting operations that takes a while */ -static CURLcode scp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection) -{ - CURLcode result = CURLE_OK; - struct ssh_conn *sshc = &conn->proto.sshc; - (void) dead_connection; - - if(sshc->ssh_session) { - /* only if there's a session still around to use! */ - state(data, SSH_SESSION_DISCONNECT); - result = ssh_block_statemach(data, conn, TRUE); - } - - return result; -} - -/* generic done function for both SCP and SFTP called from their specific - done functions */ -static CURLcode ssh_done(struct Curl_easy *data, CURLcode status) -{ - CURLcode result = CURLE_OK; - struct SSHPROTO *sshp = data->req.p.ssh; - struct connectdata *conn = data->conn; - - if(!status) - /* run the state-machine */ - result = ssh_block_statemach(data, conn, FALSE); - else - result = status; - - Curl_safefree(sshp->path); - Curl_safefree(sshp->readdir_filename); - Curl_safefree(sshp->readdir_longentry); - Curl_dyn_free(&sshp->readdir); - - if(Curl_pgrsDone(data)) - return CURLE_ABORTED_BY_CALLBACK; - - data->req.keepon = 0; /* clear all bits */ - return result; -} - - -static CURLcode scp_done(struct Curl_easy *data, CURLcode status, - bool premature) -{ - (void)premature; /* not used */ - - if(!status) - state(data, SSH_SCP_DONE); - - return ssh_done(data, status); - -} - -static ssize_t scp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, CURLcode *err) -{ - ssize_t nwrite; - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - (void)sockindex; /* we only support SCP on the fixed known primary socket */ - - /* libssh2_channel_write() returns int! */ - nwrite = (ssize_t) libssh2_channel_write(sshc->ssh_channel, mem, len); - - ssh_block2waitfor(data, (nwrite == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE); - - if(nwrite == LIBSSH2_ERROR_EAGAIN) { - *err = CURLE_AGAIN; - nwrite = 0; - } - else if(nwrite < LIBSSH2_ERROR_NONE) { - *err = libssh2_session_error_to_CURLE((int)nwrite); - nwrite = -1; - } - - return nwrite; -} - -static ssize_t scp_recv(struct Curl_easy *data, int sockindex, - char *mem, size_t len, CURLcode *err) -{ - ssize_t nread; - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - (void)sockindex; /* we only support SCP on the fixed known primary socket */ - - /* libssh2_channel_read() returns int */ - nread = (ssize_t) libssh2_channel_read(sshc->ssh_channel, mem, len); - - ssh_block2waitfor(data, (nread == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE); - if(nread == LIBSSH2_ERROR_EAGAIN) { - *err = CURLE_AGAIN; - nread = -1; - } - - return nread; -} - -/* - * =============== SFTP =============== - */ - -/* - *********************************************************************** - * - * sftp_perform() - * - * This is the actual DO function for SFTP. Get a file/directory according to - * the options previously setup. - */ - -static -CURLcode sftp_perform(struct Curl_easy *data, - bool *connected, - bool *dophase_done) -{ - CURLcode result = CURLE_OK; - - DEBUGF(infof(data, "DO phase starts")); - - *dophase_done = FALSE; /* not done yet */ - - /* start the first command in the DO phase */ - state(data, SSH_SFTP_QUOTE_INIT); - - /* run the state-machine */ - result = ssh_multi_statemach(data, dophase_done); - - *connected = data->conn->bits.tcpconnect[FIRSTSOCKET]; - - if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); - } - - return result; -} - -/* called from multi.c while DOing */ -static CURLcode sftp_doing(struct Curl_easy *data, - bool *dophase_done) -{ - CURLcode result = ssh_multi_statemach(data, dophase_done); - - if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); - } - return result; -} - -/* BLOCKING, but the function is using the state machine so the only reason - this is still blocking is that the multi interface code has no support for - disconnecting operations that takes a while */ -static CURLcode sftp_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead_connection) -{ - CURLcode result = CURLE_OK; - struct ssh_conn *sshc = &conn->proto.sshc; - (void) dead_connection; - - DEBUGF(infof(data, "SSH DISCONNECT starts now")); - - if(sshc->ssh_session) { - /* only if there's a session still around to use! */ - state(data, SSH_SFTP_SHUTDOWN); - result = ssh_block_statemach(data, conn, TRUE); - } - - DEBUGF(infof(data, "SSH DISCONNECT is done")); - - return result; - -} - -static CURLcode sftp_done(struct Curl_easy *data, CURLcode status, - bool premature) -{ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - - if(!status) { - /* Post quote commands are executed after the SFTP_CLOSE state to avoid - errors that could happen due to open file handles during POSTQUOTE - operation */ - if(!premature && data->set.postquote && !conn->bits.retry) - sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT; - state(data, SSH_SFTP_CLOSE); - } - return ssh_done(data, status); -} - -/* return number of sent bytes */ -static ssize_t sftp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, CURLcode *err) -{ - ssize_t nwrite; - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - (void)sockindex; - - nwrite = libssh2_sftp_write(sshc->sftp_handle, mem, len); - - ssh_block2waitfor(data, (nwrite == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE); - - if(nwrite == LIBSSH2_ERROR_EAGAIN) { - *err = CURLE_AGAIN; - nwrite = 0; - } - else if(nwrite < LIBSSH2_ERROR_NONE) { - *err = libssh2_session_error_to_CURLE((int)nwrite); - nwrite = -1; - } - - return nwrite; -} - -/* - * Return number of received (decrypted) bytes - * or <0 on error - */ -static ssize_t sftp_recv(struct Curl_easy *data, int sockindex, - char *mem, size_t len, CURLcode *err) -{ - ssize_t nread; - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - (void)sockindex; - - nread = libssh2_sftp_read(sshc->sftp_handle, mem, len); - - ssh_block2waitfor(data, (nread == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE); - - if(nread == LIBSSH2_ERROR_EAGAIN) { - *err = CURLE_AGAIN; - nread = -1; - - } - else if(nread < 0) { - *err = libssh2_session_error_to_CURLE((int)nread); - } - return nread; -} - -static const char *sftp_libssh2_strerror(unsigned long err) -{ - switch(err) { - case LIBSSH2_FX_NO_SUCH_FILE: - return "No such file or directory"; - - case LIBSSH2_FX_PERMISSION_DENIED: - return "Permission denied"; - - case LIBSSH2_FX_FAILURE: - return "Operation failed"; - - case LIBSSH2_FX_BAD_MESSAGE: - return "Bad message from SFTP server"; - - case LIBSSH2_FX_NO_CONNECTION: - return "Not connected to SFTP server"; - - case LIBSSH2_FX_CONNECTION_LOST: - return "Connection to SFTP server lost"; - - case LIBSSH2_FX_OP_UNSUPPORTED: - return "Operation not supported by SFTP server"; - - case LIBSSH2_FX_INVALID_HANDLE: - return "Invalid handle"; - - case LIBSSH2_FX_NO_SUCH_PATH: - return "No such file or directory"; - - case LIBSSH2_FX_FILE_ALREADY_EXISTS: - return "File already exists"; - - case LIBSSH2_FX_WRITE_PROTECT: - return "File is write protected"; - - case LIBSSH2_FX_NO_MEDIA: - return "No media"; - - case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM: - return "Disk full"; - - case LIBSSH2_FX_QUOTA_EXCEEDED: - return "User quota exceeded"; - - case LIBSSH2_FX_UNKNOWN_PRINCIPLE: - return "Unknown principle"; - - case LIBSSH2_FX_LOCK_CONFlICT: - return "File lock conflict"; - - case LIBSSH2_FX_DIR_NOT_EMPTY: - return "Directory not empty"; - - case LIBSSH2_FX_NOT_A_DIRECTORY: - return "Not a directory"; - - case LIBSSH2_FX_INVALID_FILENAME: - return "Invalid filename"; - - case LIBSSH2_FX_LINK_LOOP: - return "Link points to itself"; - } - return "Unknown error in libssh2"; -} - -CURLcode Curl_ssh_init(void) -{ -#ifdef HAVE_LIBSSH2_INIT - if(libssh2_init(0)) { - DEBUGF(fprintf(stderr, "Error: libssh2_init failed\n")); - return CURLE_FAILED_INIT; - } -#endif - return CURLE_OK; -} - -void Curl_ssh_cleanup(void) -{ -#ifdef HAVE_LIBSSH2_EXIT - (void)libssh2_exit(); -#endif -} - -void Curl_ssh_version(char *buffer, size_t buflen) -{ - (void)msnprintf(buffer, buflen, "libssh2/%s", CURL_LIBSSH2_VERSION); -} - -/* The SSH session is associated with the *CONNECTION* but the callback user - * pointer is an easy handle pointer. This function allows us to reassign the - * user pointer to the *CURRENT* (new) easy handle. - */ -static void ssh_attach(struct Curl_easy *data, struct connectdata *conn) -{ - DEBUGASSERT(data); - DEBUGASSERT(conn); - if(conn->handler->protocol & PROTO_FAMILY_SSH) { - struct ssh_conn *sshc = &conn->proto.sshc; - if(sshc->ssh_session) { - /* only re-attach if the session already exists */ - void **abstract = libssh2_session_abstract(sshc->ssh_session); - *abstract = data; - } - } -} -#endif /* USE_LIBSSH2 */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_LIBSSH2_DEBUG */
+
+#include "curl_setup.h"
+
+#ifdef USE_LIBSSH2
+
+#include <limits.h>
+
+#include <libssh2.h>
+#include <libssh2_sftp.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#include <curl/curl.h>
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "progress.h"
+#include "transfer.h"
+#include "escape.h"
+#include "http.h" /* for HTTP proxy tunnel stuff */
+#include "ssh.h"
+#include "url.h"
+#include "speedcheck.h"
+#include "getinfo.h"
+#include "strdup.h"
+#include "strcase.h"
+#include "vtls/vtls.h"
+#include "cfilters.h"
+#include "connect.h"
+#include "inet_ntop.h"
+#include "parsedate.h" /* for the week day and month names */
+#include "sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "strtoofft.h"
+#include "multiif.h"
+#include "select.h"
+#include "warnless.h"
+#include "curl_path.h"
+
+#include <curl_base64.h> /* for base64 encoding/decoding */
+#include <curl_sha256.h>
+
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#if LIBSSH2_VERSION_NUM >= 0x010206
+/* libssh2_sftp_statvfs and friends were added in 1.2.6 */
+#define HAS_STATVFS_SUPPORT 1
+#endif
+
+#define sftp_libssh2_realpath(s,p,t,m) \
+ libssh2_sftp_symlink_ex((s), (p), curlx_uztoui(strlen(p)), \
+ (t), (m), LIBSSH2_SFTP_REALPATH)
+
+/* Local functions: */
+static const char *sftp_libssh2_strerror(unsigned long err);
+static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc);
+static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc);
+static LIBSSH2_FREE_FUNC(my_libssh2_free);
+
+static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data);
+static CURLcode ssh_connect(struct Curl_easy *data, bool *done);
+static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done);
+static CURLcode ssh_do(struct Curl_easy *data, bool *done);
+static CURLcode scp_done(struct Curl_easy *data, CURLcode c, bool premature);
+static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done);
+static CURLcode scp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn, bool dead_connection);
+static CURLcode sftp_done(struct Curl_easy *data, CURLcode, bool premature);
+static CURLcode sftp_doing(struct Curl_easy *data, bool *dophase_done);
+static CURLcode sftp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn, bool dead);
+static CURLcode sftp_perform(struct Curl_easy *data, bool *connected,
+ bool *dophase_done);
+static int ssh_getsock(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t *sock);
+static CURLcode ssh_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn);
+static void ssh_attach(struct Curl_easy *data, struct connectdata *conn);
+
+/*
+ * SCP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_scp = {
+ "SCP", /* scheme */
+ ssh_setup_connection, /* setup_connection */
+ ssh_do, /* do_it */
+ scp_done, /* done */
+ ZERO_NULL, /* do_more */
+ ssh_connect, /* connect_it */
+ ssh_multi_statemach, /* connecting */
+ scp_doing, /* doing */
+ ssh_getsock, /* proto_getsock */
+ ssh_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ssh_getsock, /* perform_getsock */
+ scp_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ssh_attach,
+ PORT_SSH, /* defport */
+ CURLPROTO_SCP, /* protocol */
+ CURLPROTO_SCP, /* family */
+ PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
+ | PROTOPT_NOURLQUERY /* flags */
+};
+
+
+/*
+ * SFTP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_sftp = {
+ "SFTP", /* scheme */
+ ssh_setup_connection, /* setup_connection */
+ ssh_do, /* do_it */
+ sftp_done, /* done */
+ ZERO_NULL, /* do_more */
+ ssh_connect, /* connect_it */
+ ssh_multi_statemach, /* connecting */
+ sftp_doing, /* doing */
+ ssh_getsock, /* proto_getsock */
+ ssh_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ssh_getsock, /* perform_getsock */
+ sftp_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ssh_attach,
+ PORT_SSH, /* defport */
+ CURLPROTO_SFTP, /* protocol */
+ CURLPROTO_SFTP, /* family */
+ PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
+ | PROTOPT_NOURLQUERY /* flags */
+};
+
+static void
+kbd_callback(const char *name, int name_len, const char *instruction,
+ int instruction_len, int num_prompts,
+ const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts,
+ LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses,
+ void **abstract)
+{
+ struct Curl_easy *data = (struct Curl_easy *)*abstract;
+
+#ifdef CURL_LIBSSH2_DEBUG
+ fprintf(stderr, "name=%s\n", name);
+ fprintf(stderr, "name_len=%d\n", name_len);
+ fprintf(stderr, "instruction=%s\n", instruction);
+ fprintf(stderr, "instruction_len=%d\n", instruction_len);
+ fprintf(stderr, "num_prompts=%d\n", num_prompts);
+#else
+ (void)name;
+ (void)name_len;
+ (void)instruction;
+ (void)instruction_len;
+#endif /* CURL_LIBSSH2_DEBUG */
+ if(num_prompts == 1) {
+ struct connectdata *conn = data->conn;
+ responses[0].text = strdup(conn->passwd);
+ responses[0].length = curlx_uztoui(strlen(conn->passwd));
+ }
+ (void)prompts;
+} /* kbd_callback */
+
+static CURLcode sftp_libssh2_error_to_CURLE(unsigned long err)
+{
+ switch(err) {
+ case LIBSSH2_FX_OK:
+ return CURLE_OK;
+
+ case LIBSSH2_FX_NO_SUCH_FILE:
+ case LIBSSH2_FX_NO_SUCH_PATH:
+ return CURLE_REMOTE_FILE_NOT_FOUND;
+
+ case LIBSSH2_FX_PERMISSION_DENIED:
+ case LIBSSH2_FX_WRITE_PROTECT:
+ case LIBSSH2_FX_LOCK_CONFlICT:
+ return CURLE_REMOTE_ACCESS_DENIED;
+
+ case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM:
+ case LIBSSH2_FX_QUOTA_EXCEEDED:
+ return CURLE_REMOTE_DISK_FULL;
+
+ case LIBSSH2_FX_FILE_ALREADY_EXISTS:
+ return CURLE_REMOTE_FILE_EXISTS;
+
+ case LIBSSH2_FX_DIR_NOT_EMPTY:
+ return CURLE_QUOTE_ERROR;
+
+ default:
+ break;
+ }
+
+ return CURLE_SSH;
+}
+
+static CURLcode libssh2_session_error_to_CURLE(int err)
+{
+ switch(err) {
+ /* Ordered by order of appearance in libssh2.h */
+ case LIBSSH2_ERROR_NONE:
+ return CURLE_OK;
+
+ /* This is the error returned by libssh2_scp_recv2
+ * on unknown file */
+ case LIBSSH2_ERROR_SCP_PROTOCOL:
+ return CURLE_REMOTE_FILE_NOT_FOUND;
+
+ case LIBSSH2_ERROR_SOCKET_NONE:
+ return CURLE_COULDNT_CONNECT;
+
+ case LIBSSH2_ERROR_ALLOC:
+ return CURLE_OUT_OF_MEMORY;
+
+ case LIBSSH2_ERROR_SOCKET_SEND:
+ return CURLE_SEND_ERROR;
+
+ case LIBSSH2_ERROR_HOSTKEY_INIT:
+ case LIBSSH2_ERROR_HOSTKEY_SIGN:
+ case LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED:
+ case LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED:
+ return CURLE_PEER_FAILED_VERIFICATION;
+
+ case LIBSSH2_ERROR_PASSWORD_EXPIRED:
+ return CURLE_LOGIN_DENIED;
+
+ case LIBSSH2_ERROR_SOCKET_TIMEOUT:
+ case LIBSSH2_ERROR_TIMEOUT:
+ return CURLE_OPERATION_TIMEDOUT;
+
+ case LIBSSH2_ERROR_EAGAIN:
+ return CURLE_AGAIN;
+ }
+
+ return CURLE_SSH;
+}
+
+static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc)
+{
+ (void)abstract; /* arg not used */
+ return malloc(count);
+}
+
+static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc)
+{
+ (void)abstract; /* arg not used */
+ return realloc(ptr, count);
+}
+
+static LIBSSH2_FREE_FUNC(my_libssh2_free)
+{
+ (void)abstract; /* arg not used */
+ if(ptr) /* ssh2 agent sometimes call free with null ptr */
+ free(ptr);
+}
+
+/*
+ * SSH State machine related code
+ */
+/* This is the ONLY way to change SSH state! */
+static void state(struct Curl_easy *data, sshstate nowstate)
+{
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ /* for debug purposes */
+ static const char * const names[] = {
+ "SSH_STOP",
+ "SSH_INIT",
+ "SSH_S_STARTUP",
+ "SSH_HOSTKEY",
+ "SSH_AUTHLIST",
+ "SSH_AUTH_PKEY_INIT",
+ "SSH_AUTH_PKEY",
+ "SSH_AUTH_PASS_INIT",
+ "SSH_AUTH_PASS",
+ "SSH_AUTH_AGENT_INIT",
+ "SSH_AUTH_AGENT_LIST",
+ "SSH_AUTH_AGENT",
+ "SSH_AUTH_HOST_INIT",
+ "SSH_AUTH_HOST",
+ "SSH_AUTH_KEY_INIT",
+ "SSH_AUTH_KEY",
+ "SSH_AUTH_GSSAPI",
+ "SSH_AUTH_DONE",
+ "SSH_SFTP_INIT",
+ "SSH_SFTP_REALPATH",
+ "SSH_SFTP_QUOTE_INIT",
+ "SSH_SFTP_POSTQUOTE_INIT",
+ "SSH_SFTP_QUOTE",
+ "SSH_SFTP_NEXT_QUOTE",
+ "SSH_SFTP_QUOTE_STAT",
+ "SSH_SFTP_QUOTE_SETSTAT",
+ "SSH_SFTP_QUOTE_SYMLINK",
+ "SSH_SFTP_QUOTE_MKDIR",
+ "SSH_SFTP_QUOTE_RENAME",
+ "SSH_SFTP_QUOTE_RMDIR",
+ "SSH_SFTP_QUOTE_UNLINK",
+ "SSH_SFTP_QUOTE_STATVFS",
+ "SSH_SFTP_GETINFO",
+ "SSH_SFTP_FILETIME",
+ "SSH_SFTP_TRANS_INIT",
+ "SSH_SFTP_UPLOAD_INIT",
+ "SSH_SFTP_CREATE_DIRS_INIT",
+ "SSH_SFTP_CREATE_DIRS",
+ "SSH_SFTP_CREATE_DIRS_MKDIR",
+ "SSH_SFTP_READDIR_INIT",
+ "SSH_SFTP_READDIR",
+ "SSH_SFTP_READDIR_LINK",
+ "SSH_SFTP_READDIR_BOTTOM",
+ "SSH_SFTP_READDIR_DONE",
+ "SSH_SFTP_DOWNLOAD_INIT",
+ "SSH_SFTP_DOWNLOAD_STAT",
+ "SSH_SFTP_CLOSE",
+ "SSH_SFTP_SHUTDOWN",
+ "SSH_SCP_TRANS_INIT",
+ "SSH_SCP_UPLOAD_INIT",
+ "SSH_SCP_DOWNLOAD_INIT",
+ "SSH_SCP_DOWNLOAD",
+ "SSH_SCP_DONE",
+ "SSH_SCP_SEND_EOF",
+ "SSH_SCP_WAIT_EOF",
+ "SSH_SCP_WAIT_CLOSE",
+ "SSH_SCP_CHANNEL_FREE",
+ "SSH_SESSION_DISCONNECT",
+ "SSH_SESSION_FREE",
+ "QUIT"
+ };
+
+ /* a precaution to make sure the lists are in sync */
+ DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST);
+
+ if(sshc->state != nowstate) {
+ infof(data, "SFTP %p state change from %s to %s",
+ (void *)sshc, names[sshc->state], names[nowstate]);
+ }
+#endif
+
+ sshc->state = nowstate;
+}
+
+
+#ifdef HAVE_LIBSSH2_KNOWNHOST_API
+static int sshkeycallback(struct Curl_easy *easy,
+ const struct curl_khkey *knownkey, /* known */
+ const struct curl_khkey *foundkey, /* found */
+ enum curl_khmatch match,
+ void *clientp)
+{
+ (void)easy;
+ (void)knownkey;
+ (void)foundkey;
+ (void)clientp;
+
+ /* we only allow perfect matches, and we reject everything else */
+ return (match != CURLKHMATCH_OK)?CURLKHSTAT_REJECT:CURLKHSTAT_FINE;
+}
+#endif
+
+/*
+ * Earlier libssh2 versions didn't have the ability to seek to 64bit positions
+ * with 32bit size_t.
+ */
+#ifdef HAVE_LIBSSH2_SFTP_SEEK64
+#define SFTP_SEEK(x,y) libssh2_sftp_seek64(x, (libssh2_uint64_t)y)
+#else
+#define SFTP_SEEK(x,y) libssh2_sftp_seek(x, (size_t)y)
+#endif
+
+/*
+ * Earlier libssh2 versions didn't do SCP properly beyond 32bit sizes on 32bit
+ * architectures so we check of the necessary function is present.
+ */
+#ifndef HAVE_LIBSSH2_SCP_SEND64
+#define SCP_SEND(a,b,c,d) libssh2_scp_send_ex(a, b, (int)(c), (size_t)d, 0, 0)
+#else
+#define SCP_SEND(a,b,c,d) libssh2_scp_send64(a, b, (int)(c), \
+ (libssh2_uint64_t)d, 0, 0)
+#endif
+
+/*
+ * libssh2 1.2.8 fixed the problem with 32bit ints used for sockets on win64.
+ */
+#ifdef HAVE_LIBSSH2_SESSION_HANDSHAKE
+#define session_startup(x,y) libssh2_session_handshake(x, y)
+#else
+#define session_startup(x,y) libssh2_session_startup(x, (int)y)
+#endif
+static int convert_ssh2_keytype(int sshkeytype)
+{
+ int keytype = CURLKHTYPE_UNKNOWN;
+ switch(sshkeytype) {
+ case LIBSSH2_HOSTKEY_TYPE_RSA:
+ keytype = CURLKHTYPE_RSA;
+ break;
+ case LIBSSH2_HOSTKEY_TYPE_DSS:
+ keytype = CURLKHTYPE_DSS;
+ break;
+#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
+ keytype = CURLKHTYPE_ECDSA;
+ break;
+#endif
+#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_384
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
+ keytype = CURLKHTYPE_ECDSA;
+ break;
+#endif
+#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_521
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_521:
+ keytype = CURLKHTYPE_ECDSA;
+ break;
+#endif
+#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
+ case LIBSSH2_HOSTKEY_TYPE_ED25519:
+ keytype = CURLKHTYPE_ED25519;
+ break;
+#endif
+ }
+ return keytype;
+}
+
+static CURLcode ssh_knownhost(struct Curl_easy *data)
+{
+ int sshkeytype = 0;
+ size_t keylen = 0;
+ int rc = 0;
+ CURLcode result = CURLE_OK;
+
+#ifdef HAVE_LIBSSH2_KNOWNHOST_API
+ if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
+ /* we're asked to verify the host against a file */
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ struct libssh2_knownhost *host = NULL;
+ const char *remotekey = libssh2_session_hostkey(sshc->ssh_session,
+ &keylen, &sshkeytype);
+ int keycheck = LIBSSH2_KNOWNHOST_CHECK_FAILURE;
+ int keybit = 0;
+
+ if(remotekey) {
+ /*
+ * A subject to figure out is what host name we need to pass in here.
+ * What host name does OpenSSH store in its file if an IDN name is
+ * used?
+ */
+ enum curl_khmatch keymatch;
+ curl_sshkeycallback func =
+ data->set.ssh_keyfunc ? data->set.ssh_keyfunc : sshkeycallback;
+ struct curl_khkey knownkey;
+ struct curl_khkey *knownkeyp = NULL;
+ struct curl_khkey foundkey;
+
+ switch(sshkeytype) {
+ case LIBSSH2_HOSTKEY_TYPE_RSA:
+ keybit = LIBSSH2_KNOWNHOST_KEY_SSHRSA;
+ break;
+ case LIBSSH2_HOSTKEY_TYPE_DSS:
+ keybit = LIBSSH2_KNOWNHOST_KEY_SSHDSS;
+ break;
+#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
+ keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_256;
+ break;
+#endif
+#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_384
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
+ keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_384;
+ break;
+#endif
+#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_521
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_521:
+ keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_521;
+ break;
+#endif
+#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
+ case LIBSSH2_HOSTKEY_TYPE_ED25519:
+ keybit = LIBSSH2_KNOWNHOST_KEY_ED25519;
+ break;
+#endif
+ default:
+ infof(data, "unsupported key type, can't check knownhosts");
+ keybit = 0;
+ break;
+ }
+ if(!keybit)
+ /* no check means failure! */
+ rc = CURLKHSTAT_REJECT;
+ else {
+#ifdef HAVE_LIBSSH2_KNOWNHOST_CHECKP
+ keycheck = libssh2_knownhost_checkp(sshc->kh,
+ conn->host.name,
+ (conn->remote_port != PORT_SSH)?
+ conn->remote_port:-1,
+ remotekey, keylen,
+ LIBSSH2_KNOWNHOST_TYPE_PLAIN|
+ LIBSSH2_KNOWNHOST_KEYENC_RAW|
+ keybit,
+ &host);
+#else
+ keycheck = libssh2_knownhost_check(sshc->kh,
+ conn->host.name,
+ remotekey, keylen,
+ LIBSSH2_KNOWNHOST_TYPE_PLAIN|
+ LIBSSH2_KNOWNHOST_KEYENC_RAW|
+ keybit,
+ &host);
+#endif
+
+ infof(data, "SSH host check: %d, key: %s", keycheck,
+ (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH)?
+ host->key:"<none>");
+
+ /* setup 'knownkey' */
+ if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) {
+ knownkey.key = host->key;
+ knownkey.len = 0;
+ knownkey.keytype = convert_ssh2_keytype(sshkeytype);
+ knownkeyp = &knownkey;
+ }
+
+ /* setup 'foundkey' */
+ foundkey.key = remotekey;
+ foundkey.len = keylen;
+ foundkey.keytype = convert_ssh2_keytype(sshkeytype);
+
+ /*
+ * if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the
+ * curl_khmatch enum are ever modified, we need to introduce a
+ * translation table here!
+ */
+ keymatch = (enum curl_khmatch)keycheck;
+
+ /* Ask the callback how to behave */
+ Curl_set_in_callback(data, true);
+ rc = func(data, knownkeyp, /* from the knownhosts file */
+ &foundkey, /* from the remote host */
+ keymatch, data->set.ssh_keyfunc_userp);
+ Curl_set_in_callback(data, false);
+ }
+ }
+ else
+ /* no remotekey means failure! */
+ rc = CURLKHSTAT_REJECT;
+
+ switch(rc) {
+ default: /* unknown return codes will equal reject */
+ /* FALLTHROUGH */
+ case CURLKHSTAT_REJECT:
+ state(data, SSH_SESSION_FREE);
+ /* FALLTHROUGH */
+ case CURLKHSTAT_DEFER:
+ /* DEFER means bail out but keep the SSH_HOSTKEY state */
+ result = sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
+ break;
+ case CURLKHSTAT_FINE_REPLACE:
+ /* remove old host+key that doesn't match */
+ if(host)
+ libssh2_knownhost_del(sshc->kh, host);
+ /* FALLTHROUGH */
+ case CURLKHSTAT_FINE:
+ /* FALLTHROUGH */
+ case CURLKHSTAT_FINE_ADD_TO_FILE:
+ /* proceed */
+ if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) {
+ /* the found host+key didn't match but has been told to be fine
+ anyway so we add it in memory */
+ int addrc = libssh2_knownhost_add(sshc->kh,
+ conn->host.name, NULL,
+ remotekey, keylen,
+ LIBSSH2_KNOWNHOST_TYPE_PLAIN|
+ LIBSSH2_KNOWNHOST_KEYENC_RAW|
+ keybit, NULL);
+ if(addrc)
+ infof(data, "WARNING: adding the known host %s failed",
+ conn->host.name);
+ else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE ||
+ rc == CURLKHSTAT_FINE_REPLACE) {
+ /* now we write the entire in-memory list of known hosts to the
+ known_hosts file */
+ int wrc =
+ libssh2_knownhost_writefile(sshc->kh,
+ data->set.str[STRING_SSH_KNOWNHOSTS],
+ LIBSSH2_KNOWNHOST_FILE_OPENSSH);
+ if(wrc) {
+ infof(data, "WARNING: writing %s failed",
+ data->set.str[STRING_SSH_KNOWNHOSTS]);
+ }
+ }
+ }
+ break;
+ }
+ }
+#else /* HAVE_LIBSSH2_KNOWNHOST_API */
+ (void)data;
+#endif
+ return result;
+}
+
+static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
+{
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5];
+ const char *pubkey_sha256 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256];
+
+ infof(data, "SSH MD5 public key: %s",
+ pubkey_md5 != NULL ? pubkey_md5 : "NULL");
+ infof(data, "SSH SHA256 public key: %s",
+ pubkey_sha256 != NULL ? pubkey_sha256 : "NULL");
+
+ if(pubkey_sha256) {
+ const char *fingerprint = NULL;
+ char *fingerprint_b64 = NULL;
+ size_t fingerprint_b64_len;
+ size_t pub_pos = 0;
+ size_t b64_pos = 0;
+
+#ifdef LIBSSH2_HOSTKEY_HASH_SHA256
+ /* The fingerprint points to static storage (!), don't free() it. */
+ fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
+ LIBSSH2_HOSTKEY_HASH_SHA256);
+#else
+ const char *hostkey;
+ size_t len = 0;
+ unsigned char hash[32];
+
+ hostkey = libssh2_session_hostkey(sshc->ssh_session, &len, NULL);
+ if(hostkey) {
+ if(!Curl_sha256it(hash, (const unsigned char *) hostkey, len))
+ fingerprint = (char *) hash;
+ }
+#endif
+
+ if(!fingerprint) {
+ failf(data,
+ "Denied establishing ssh session: sha256 fingerprint "
+ "not available");
+ state(data, SSH_SESSION_FREE);
+ sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
+ return sshc->actualcode;
+ }
+
+ /* The length of fingerprint is 32 bytes for SHA256.
+ * See libssh2_hostkey_hash documentation. */
+ if(Curl_base64_encode(fingerprint, 32, &fingerprint_b64,
+ &fingerprint_b64_len) != CURLE_OK) {
+ state(data, SSH_SESSION_FREE);
+ sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
+ return sshc->actualcode;
+ }
+
+ if(!fingerprint_b64) {
+ failf(data, "sha256 fingerprint could not be encoded");
+ state(data, SSH_SESSION_FREE);
+ sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
+ return sshc->actualcode;
+ }
+
+ infof(data, "SSH SHA256 fingerprint: %s", fingerprint_b64);
+
+ /* Find the position of any = padding characters in the public key */
+ while((pubkey_sha256[pub_pos] != '=') && pubkey_sha256[pub_pos]) {
+ pub_pos++;
+ }
+
+ /* Find the position of any = padding characters in the base64 coded
+ * hostkey fingerprint */
+ while((fingerprint_b64[b64_pos] != '=') && fingerprint_b64[b64_pos]) {
+ b64_pos++;
+ }
+
+ /* Before we authenticate we check the hostkey's sha256 fingerprint
+ * against a known fingerprint, if available.
+ */
+ if((pub_pos != b64_pos) ||
+ strncmp(fingerprint_b64, pubkey_sha256, pub_pos)) {
+ free(fingerprint_b64);
+
+ failf(data,
+ "Denied establishing ssh session: mismatch sha256 fingerprint. "
+ "Remote %s is not equal to %s", fingerprint_b64, pubkey_sha256);
+ state(data, SSH_SESSION_FREE);
+ sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
+ return sshc->actualcode;
+ }
+
+ free(fingerprint_b64);
+
+ infof(data, "SHA256 checksum match");
+ }
+
+ if(pubkey_md5) {
+ char md5buffer[33];
+ const char *fingerprint = NULL;
+
+ fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
+ LIBSSH2_HOSTKEY_HASH_MD5);
+
+ if(fingerprint) {
+ /* The fingerprint points to static storage (!), don't free() it. */
+ int i;
+ for(i = 0; i < 16; i++) {
+ msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char) fingerprint[i]);
+ }
+
+ infof(data, "SSH MD5 fingerprint: %s", md5buffer);
+ }
+
+ /* This does NOT verify the length of 'pubkey_md5' separately, which will
+ make the comparison below fail unless it is exactly 32 characters */
+ if(!fingerprint || !strcasecompare(md5buffer, pubkey_md5)) {
+ if(fingerprint) {
+ failf(data,
+ "Denied establishing ssh session: mismatch md5 fingerprint. "
+ "Remote %s is not equal to %s", md5buffer, pubkey_md5);
+ }
+ else {
+ failf(data,
+ "Denied establishing ssh session: md5 fingerprint "
+ "not available");
+ }
+ state(data, SSH_SESSION_FREE);
+ sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
+ return sshc->actualcode;
+ }
+ infof(data, "MD5 checksum match");
+ }
+
+ if(!pubkey_md5 && !pubkey_sha256) {
+ if(data->set.ssh_hostkeyfunc) {
+ size_t keylen = 0;
+ int sshkeytype = 0;
+ int rc = 0;
+ /* we handle the process to the callback */
+ const char *remotekey = libssh2_session_hostkey(sshc->ssh_session,
+ &keylen, &sshkeytype);
+ if(remotekey) {
+ int keytype = convert_ssh2_keytype(sshkeytype);
+ Curl_set_in_callback(data, true);
+ rc = data->set.ssh_hostkeyfunc(data->set.ssh_hostkeyfunc_userp,
+ keytype, remotekey, keylen);
+ Curl_set_in_callback(data, false);
+ if(rc!= CURLKHMATCH_OK) {
+ state(data, SSH_SESSION_FREE);
+ sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
+ return sshc->actualcode;
+ }
+ }
+ else {
+ state(data, SSH_SESSION_FREE);
+ sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
+ return sshc->actualcode;
+ }
+ return CURLE_OK;
+ }
+ else {
+ return ssh_knownhost(data);
+ }
+ }
+ else {
+ /* as we already matched, we skip the check for known hosts */
+ return CURLE_OK;
+ }
+}
+
+/*
+ * ssh_force_knownhost_key_type() will check the known hosts file and try to
+ * force a specific public key type from the server if an entry is found.
+ */
+static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+
+#ifdef HAVE_LIBSSH2_KNOWNHOST_API
+
+#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519
+ static const char * const hostkey_method_ssh_ed25519
+ = "ssh-ed25519";
+#endif
+#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521
+ static const char * const hostkey_method_ssh_ecdsa_521
+ = "ecdsa-sha2-nistp521";
+#endif
+#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384
+ static const char * const hostkey_method_ssh_ecdsa_384
+ = "ecdsa-sha2-nistp384";
+#endif
+#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256
+ static const char * const hostkey_method_ssh_ecdsa_256
+ = "ecdsa-sha2-nistp256";
+#endif
+ static const char * const hostkey_method_ssh_rsa
+ = "ssh-rsa";
+ static const char * const hostkey_method_ssh_dss
+ = "ssh-dss";
+
+ const char *hostkey_method = NULL;
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ struct libssh2_knownhost* store = NULL;
+ const char *kh_name_end = NULL;
+ size_t kh_name_size = 0;
+ int port = 0;
+ bool found = false;
+
+ if(sshc->kh && !data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) {
+ /* lets try to find our host in the known hosts file */
+ while(!libssh2_knownhost_get(sshc->kh, &store, store)) {
+ /* For non-standard ports, the name will be enclosed in */
+ /* square brackets, followed by a colon and the port */
+ if(store) {
+ if(store->name) {
+ if(store->name[0] == '[') {
+ kh_name_end = strstr(store->name, "]:");
+ if(!kh_name_end) {
+ infof(data, "Invalid host pattern %s in %s",
+ store->name, data->set.str[STRING_SSH_KNOWNHOSTS]);
+ continue;
+ }
+ port = atoi(kh_name_end + 2);
+ if(kh_name_end && (port == conn->remote_port)) {
+ kh_name_size = strlen(store->name) - 1 - strlen(kh_name_end);
+ if(strncmp(store->name + 1,
+ conn->host.name, kh_name_size) == 0) {
+ found = true;
+ break;
+ }
+ }
+ }
+ else if(strcmp(store->name, conn->host.name) == 0) {
+ found = true;
+ break;
+ }
+ }
+ else {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if(found) {
+ infof(data, "Found host %s in %s",
+ conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]);
+
+ switch(store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK) {
+#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519
+ case LIBSSH2_KNOWNHOST_KEY_ED25519:
+ hostkey_method = hostkey_method_ssh_ed25519;
+ break;
+#endif
+#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521
+ case LIBSSH2_KNOWNHOST_KEY_ECDSA_521:
+ hostkey_method = hostkey_method_ssh_ecdsa_521;
+ break;
+#endif
+#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384
+ case LIBSSH2_KNOWNHOST_KEY_ECDSA_384:
+ hostkey_method = hostkey_method_ssh_ecdsa_384;
+ break;
+#endif
+#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256
+ case LIBSSH2_KNOWNHOST_KEY_ECDSA_256:
+ hostkey_method = hostkey_method_ssh_ecdsa_256;
+ break;
+#endif
+ case LIBSSH2_KNOWNHOST_KEY_SSHRSA:
+ hostkey_method = hostkey_method_ssh_rsa;
+ break;
+ case LIBSSH2_KNOWNHOST_KEY_SSHDSS:
+ hostkey_method = hostkey_method_ssh_dss;
+ break;
+ case LIBSSH2_KNOWNHOST_KEY_RSA1:
+ failf(data, "Found host key type RSA1 which is not supported");
+ return CURLE_SSH;
+ default:
+ failf(data, "Unknown host key type: %i",
+ (store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK));
+ return CURLE_SSH;
+ }
+
+ infof(data, "Set \"%s\" as SSH hostkey type", hostkey_method);
+ result = libssh2_session_error_to_CURLE(
+ libssh2_session_method_pref(
+ sshc->ssh_session, LIBSSH2_METHOD_HOSTKEY, hostkey_method));
+ }
+ else {
+ infof(data, "Did not find host %s in %s",
+ conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]);
+ }
+ }
+
+#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
+
+ return result;
+}
+
+/*
+ * ssh_statemach_act() runs the SSH state machine as far as it can without
+ * blocking and without reaching the end. The data the pointer 'block' points
+ * to will be set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN
+ * meaning it wants to be called again when the socket is ready
+ */
+
+static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct SSHPROTO *sshp = data->req.p.ssh;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ int rc = LIBSSH2_ERROR_NONE;
+ int ssherr;
+ unsigned long sftperr;
+ int seekerr = CURL_SEEKFUNC_OK;
+ size_t readdir_len;
+ *block = 0; /* we're not blocking by default */
+
+ do {
+ switch(sshc->state) {
+ case SSH_INIT:
+ sshc->secondCreateDirs = 0;
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_OK;
+
+ /* Set libssh2 to non-blocking, since everything internally is
+ non-blocking */
+ libssh2_session_set_blocking(sshc->ssh_session, 0);
+
+ result = ssh_force_knownhost_key_type(data);
+ if(result) {
+ state(data, SSH_SESSION_FREE);
+ sshc->actualcode = result;
+ break;
+ }
+
+ state(data, SSH_S_STARTUP);
+ /* FALLTHROUGH */
+
+ case SSH_S_STARTUP:
+ rc = session_startup(sshc->ssh_session, sock);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0);
+ failf(data, "Failure establishing ssh session: %d, %s", rc, err_msg);
+
+ state(data, SSH_SESSION_FREE);
+ sshc->actualcode = CURLE_FAILED_INIT;
+ break;
+ }
+
+ state(data, SSH_HOSTKEY);
+
+ /* FALLTHROUGH */
+ case SSH_HOSTKEY:
+ /*
+ * Before we authenticate we should check the hostkey's fingerprint
+ * against our known hosts. How that is handled (reading from file,
+ * whatever) is up to us.
+ */
+ result = ssh_check_fingerprint(data);
+ if(!result)
+ state(data, SSH_AUTHLIST);
+ /* ssh_check_fingerprint sets state appropriately on error */
+ break;
+
+ case SSH_AUTHLIST:
+ /*
+ * Figure out authentication methods
+ * NB: As soon as we have provided a username to an openssh server we
+ * must never change it later. Thus, always specify the correct username
+ * here, even though the libssh2 docs kind of indicate that it should be
+ * possible to get a 'generic' list (not user-specific) of authentication
+ * methods, presumably with a blank username. That won't work in my
+ * experience.
+ * So always specify it here.
+ */
+ sshc->authlist = libssh2_userauth_list(sshc->ssh_session,
+ conn->user,
+ curlx_uztoui(strlen(conn->user)));
+
+ if(!sshc->authlist) {
+ if(libssh2_userauth_authenticated(sshc->ssh_session)) {
+ sshc->authed = TRUE;
+ infof(data, "SSH user accepted with no authentication");
+ state(data, SSH_AUTH_DONE);
+ break;
+ }
+ ssherr = libssh2_session_last_errno(sshc->ssh_session);
+ if(ssherr == LIBSSH2_ERROR_EAGAIN)
+ rc = LIBSSH2_ERROR_EAGAIN;
+ else {
+ state(data, SSH_SESSION_FREE);
+ sshc->actualcode = libssh2_session_error_to_CURLE(ssherr);
+ }
+ break;
+ }
+ infof(data, "SSH authentication methods available: %s",
+ sshc->authlist);
+
+ state(data, SSH_AUTH_PKEY_INIT);
+ break;
+
+ case SSH_AUTH_PKEY_INIT:
+ /*
+ * Check the supported auth types in the order I feel is most secure
+ * with the requested type of authentication
+ */
+ sshc->authed = FALSE;
+
+ if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) &&
+ (strstr(sshc->authlist, "publickey") != NULL)) {
+ bool out_of_memory = FALSE;
+
+ sshc->rsa_pub = sshc->rsa = NULL;
+
+ if(data->set.str[STRING_SSH_PRIVATE_KEY])
+ sshc->rsa = strdup(data->set.str[STRING_SSH_PRIVATE_KEY]);
+ else {
+ /* To ponder about: should really the lib be messing about with the
+ HOME environment variable etc? */
+ char *home = curl_getenv("HOME");
+
+ /* If no private key file is specified, try some common paths. */
+ if(home) {
+ /* Try ~/.ssh first. */
+ sshc->rsa = aprintf("%s/.ssh/id_rsa", home);
+ if(!sshc->rsa)
+ out_of_memory = TRUE;
+ else if(access(sshc->rsa, R_OK) != 0) {
+ Curl_safefree(sshc->rsa);
+ sshc->rsa = aprintf("%s/.ssh/id_dsa", home);
+ if(!sshc->rsa)
+ out_of_memory = TRUE;
+ else if(access(sshc->rsa, R_OK) != 0) {
+ Curl_safefree(sshc->rsa);
+ }
+ }
+ free(home);
+ }
+ if(!out_of_memory && !sshc->rsa) {
+ /* Nothing found; try the current dir. */
+ sshc->rsa = strdup("id_rsa");
+ if(sshc->rsa && access(sshc->rsa, R_OK) != 0) {
+ Curl_safefree(sshc->rsa);
+ sshc->rsa = strdup("id_dsa");
+ if(sshc->rsa && access(sshc->rsa, R_OK) != 0) {
+ Curl_safefree(sshc->rsa);
+ /* Out of guesses. Set to the empty string to avoid
+ * surprising info messages. */
+ sshc->rsa = strdup("");
+ }
+ }
+ }
+ }
+
+ /*
+ * Unless the user explicitly specifies a public key file, let
+ * libssh2 extract the public key from the private key file.
+ * This is done by simply passing sshc->rsa_pub = NULL.
+ */
+ if(data->set.str[STRING_SSH_PUBLIC_KEY]
+ /* treat empty string the same way as NULL */
+ && data->set.str[STRING_SSH_PUBLIC_KEY][0]) {
+ sshc->rsa_pub = strdup(data->set.str[STRING_SSH_PUBLIC_KEY]);
+ if(!sshc->rsa_pub)
+ out_of_memory = TRUE;
+ }
+
+ if(out_of_memory || !sshc->rsa) {
+ Curl_safefree(sshc->rsa);
+ Curl_safefree(sshc->rsa_pub);
+ state(data, SSH_SESSION_FREE);
+ sshc->actualcode = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+
+ sshc->passphrase = data->set.ssl.key_passwd;
+ if(!sshc->passphrase)
+ sshc->passphrase = "";
+
+ if(sshc->rsa_pub)
+ infof(data, "Using SSH public key file '%s'", sshc->rsa_pub);
+ infof(data, "Using SSH private key file '%s'", sshc->rsa);
+
+ state(data, SSH_AUTH_PKEY);
+ }
+ else {
+ state(data, SSH_AUTH_PASS_INIT);
+ }
+ break;
+
+ case SSH_AUTH_PKEY:
+ /* The function below checks if the files exists, no need to stat() here.
+ */
+ rc = libssh2_userauth_publickey_fromfile_ex(sshc->ssh_session,
+ conn->user,
+ curlx_uztoui(
+ strlen(conn->user)),
+ sshc->rsa_pub,
+ sshc->rsa, sshc->passphrase);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+
+ Curl_safefree(sshc->rsa_pub);
+ Curl_safefree(sshc->rsa);
+
+ if(rc == 0) {
+ sshc->authed = TRUE;
+ infof(data, "Initialized SSH public key authentication");
+ state(data, SSH_AUTH_DONE);
+ }
+ else {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "SSH public key authentication failed: %s", err_msg);
+ state(data, SSH_AUTH_PASS_INIT);
+ rc = 0; /* clear rc and continue */
+ }
+ break;
+
+ case SSH_AUTH_PASS_INIT:
+ if((data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) &&
+ (strstr(sshc->authlist, "password") != NULL)) {
+ state(data, SSH_AUTH_PASS);
+ }
+ else {
+ state(data, SSH_AUTH_HOST_INIT);
+ rc = 0; /* clear rc and continue */
+ }
+ break;
+
+ case SSH_AUTH_PASS:
+ rc = libssh2_userauth_password_ex(sshc->ssh_session, conn->user,
+ curlx_uztoui(strlen(conn->user)),
+ conn->passwd,
+ curlx_uztoui(strlen(conn->passwd)),
+ NULL);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc == 0) {
+ sshc->authed = TRUE;
+ infof(data, "Initialized password authentication");
+ state(data, SSH_AUTH_DONE);
+ }
+ else {
+ state(data, SSH_AUTH_HOST_INIT);
+ rc = 0; /* clear rc and continue */
+ }
+ break;
+
+ case SSH_AUTH_HOST_INIT:
+ if((data->set.ssh_auth_types & CURLSSH_AUTH_HOST) &&
+ (strstr(sshc->authlist, "hostbased") != NULL)) {
+ state(data, SSH_AUTH_HOST);
+ }
+ else {
+ state(data, SSH_AUTH_AGENT_INIT);
+ }
+ break;
+
+ case SSH_AUTH_HOST:
+ state(data, SSH_AUTH_AGENT_INIT);
+ break;
+
+ case SSH_AUTH_AGENT_INIT:
+#ifdef HAVE_LIBSSH2_AGENT_API
+ if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT)
+ && (strstr(sshc->authlist, "publickey") != NULL)) {
+
+ /* Connect to the ssh-agent */
+ /* The agent could be shared by a curl thread i believe
+ but nothing obvious as keys can be added/removed at any time */
+ if(!sshc->ssh_agent) {
+ sshc->ssh_agent = libssh2_agent_init(sshc->ssh_session);
+ if(!sshc->ssh_agent) {
+ infof(data, "Could not create agent object");
+
+ state(data, SSH_AUTH_KEY_INIT);
+ break;
+ }
+ }
+
+ rc = libssh2_agent_connect(sshc->ssh_agent);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ break;
+ if(rc < 0) {
+ infof(data, "Failure connecting to agent");
+ state(data, SSH_AUTH_KEY_INIT);
+ rc = 0; /* clear rc and continue */
+ }
+ else {
+ state(data, SSH_AUTH_AGENT_LIST);
+ }
+ }
+ else
+#endif /* HAVE_LIBSSH2_AGENT_API */
+ state(data, SSH_AUTH_KEY_INIT);
+ break;
+
+ case SSH_AUTH_AGENT_LIST:
+#ifdef HAVE_LIBSSH2_AGENT_API
+ rc = libssh2_agent_list_identities(sshc->ssh_agent);
+
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ break;
+ if(rc < 0) {
+ infof(data, "Failure requesting identities to agent");
+ state(data, SSH_AUTH_KEY_INIT);
+ rc = 0; /* clear rc and continue */
+ }
+ else {
+ state(data, SSH_AUTH_AGENT);
+ sshc->sshagent_prev_identity = NULL;
+ }
+#endif
+ break;
+
+ case SSH_AUTH_AGENT:
+#ifdef HAVE_LIBSSH2_AGENT_API
+ /* as prev_identity evolves only after an identity user auth finished we
+ can safely request it again as long as EAGAIN is returned here or by
+ libssh2_agent_userauth */
+ rc = libssh2_agent_get_identity(sshc->ssh_agent,
+ &sshc->sshagent_identity,
+ sshc->sshagent_prev_identity);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ break;
+
+ if(rc == 0) {
+ rc = libssh2_agent_userauth(sshc->ssh_agent, conn->user,
+ sshc->sshagent_identity);
+
+ if(rc < 0) {
+ if(rc != LIBSSH2_ERROR_EAGAIN) {
+ /* tried and failed? go to next identity */
+ sshc->sshagent_prev_identity = sshc->sshagent_identity;
+ }
+ break;
+ }
+ }
+
+ if(rc < 0)
+ infof(data, "Failure requesting identities to agent");
+ else if(rc == 1)
+ infof(data, "No identity would match");
+
+ if(rc == LIBSSH2_ERROR_NONE) {
+ sshc->authed = TRUE;
+ infof(data, "Agent based authentication successful");
+ state(data, SSH_AUTH_DONE);
+ }
+ else {
+ state(data, SSH_AUTH_KEY_INIT);
+ rc = 0; /* clear rc and continue */
+ }
+#endif
+ break;
+
+ case SSH_AUTH_KEY_INIT:
+ if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD)
+ && (strstr(sshc->authlist, "keyboard-interactive") != NULL)) {
+ state(data, SSH_AUTH_KEY);
+ }
+ else {
+ state(data, SSH_AUTH_DONE);
+ }
+ break;
+
+ case SSH_AUTH_KEY:
+ /* Authentication failed. Continue with keyboard-interactive now. */
+ rc = libssh2_userauth_keyboard_interactive_ex(sshc->ssh_session,
+ conn->user,
+ curlx_uztoui(
+ strlen(conn->user)),
+ &kbd_callback);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc == 0) {
+ sshc->authed = TRUE;
+ infof(data, "Initialized keyboard interactive authentication");
+ }
+ state(data, SSH_AUTH_DONE);
+ break;
+
+ case SSH_AUTH_DONE:
+ if(!sshc->authed) {
+ failf(data, "Authentication failure");
+ state(data, SSH_SESSION_FREE);
+ sshc->actualcode = CURLE_LOGIN_DENIED;
+ break;
+ }
+
+ /*
+ * At this point we have an authenticated ssh session.
+ */
+ infof(data, "Authentication complete");
+
+ Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */
+
+ conn->sockfd = sock;
+ conn->writesockfd = CURL_SOCKET_BAD;
+
+ if(conn->handler->protocol == CURLPROTO_SFTP) {
+ state(data, SSH_SFTP_INIT);
+ break;
+ }
+ infof(data, "SSH CONNECT phase done");
+ state(data, SSH_STOP);
+ break;
+
+ case SSH_SFTP_INIT:
+ /*
+ * Start the libssh2 sftp session
+ */
+ sshc->sftp_session = libssh2_sftp_init(sshc->ssh_session);
+ if(!sshc->sftp_session) {
+ char *err_msg = NULL;
+ if(libssh2_session_last_errno(sshc->ssh_session) ==
+ LIBSSH2_ERROR_EAGAIN) {
+ rc = LIBSSH2_ERROR_EAGAIN;
+ break;
+ }
+
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ failf(data, "Failure initializing sftp session: %s", err_msg);
+ state(data, SSH_SESSION_FREE);
+ sshc->actualcode = CURLE_FAILED_INIT;
+ break;
+ }
+ state(data, SSH_SFTP_REALPATH);
+ break;
+
+ case SSH_SFTP_REALPATH:
+ {
+ char tempHome[PATH_MAX];
+
+ /*
+ * Get the "home" directory
+ */
+ rc = sftp_libssh2_realpath(sshc->sftp_session, ".",
+ tempHome, PATH_MAX-1);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc > 0) {
+ /* It seems that this string is not always NULL terminated */
+ tempHome[rc] = '\0';
+ sshc->homedir = strdup(tempHome);
+ if(!sshc->homedir) {
+ state(data, SSH_SFTP_CLOSE);
+ sshc->actualcode = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+ data->state.most_recent_ftp_entrypath = sshc->homedir;
+ }
+ else {
+ /* Return the error type */
+ sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ if(sftperr)
+ result = sftp_libssh2_error_to_CURLE(sftperr);
+ else
+ /* in this case, the error wasn't in the SFTP level but for example
+ a time-out or similar */
+ result = CURLE_SSH;
+ sshc->actualcode = result;
+ DEBUGF(infof(data, "error = %lu makes libcurl = %d",
+ sftperr, (int)result));
+ state(data, SSH_STOP);
+ break;
+ }
+ }
+ /* This is the last step in the SFTP connect phase. Do note that while
+ we get the homedir here, we get the "workingpath" in the DO action
+ since the homedir will remain the same between request but the
+ working path will not. */
+ DEBUGF(infof(data, "SSH CONNECT phase done"));
+ state(data, SSH_STOP);
+ break;
+
+ case SSH_SFTP_QUOTE_INIT:
+
+ result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
+ if(result) {
+ sshc->actualcode = result;
+ state(data, SSH_STOP);
+ break;
+ }
+
+ if(data->set.quote) {
+ infof(data, "Sending quote commands");
+ sshc->quote_item = data->set.quote;
+ state(data, SSH_SFTP_QUOTE);
+ }
+ else {
+ state(data, SSH_SFTP_GETINFO);
+ }
+ break;
+
+ case SSH_SFTP_POSTQUOTE_INIT:
+ if(data->set.postquote) {
+ infof(data, "Sending quote commands");
+ sshc->quote_item = data->set.postquote;
+ state(data, SSH_SFTP_QUOTE);
+ }
+ else {
+ state(data, SSH_STOP);
+ }
+ break;
+
+ case SSH_SFTP_QUOTE:
+ /* Send any quote commands */
+ {
+ const char *cp;
+
+ /*
+ * Support some of the "FTP" commands
+ *
+ * 'sshc->quote_item' is already verified to be non-NULL before it
+ * switched to this state.
+ */
+ char *cmd = sshc->quote_item->data;
+ sshc->acceptfail = FALSE;
+
+ /* if a command starts with an asterisk, which a legal SFTP command never
+ can, the command will be allowed to fail without it causing any
+ aborts or cancels etc. It will cause libcurl to act as if the command
+ is successful, whatever the server reponds. */
+
+ if(cmd[0] == '*') {
+ cmd++;
+ sshc->acceptfail = TRUE;
+ }
+
+ if(strcasecompare("pwd", cmd)) {
+ /* output debug output if that is requested */
+ char *tmp = aprintf("257 \"%s\" is current directory.\n",
+ sshp->path);
+ if(!tmp) {
+ result = CURLE_OUT_OF_MEMORY;
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ break;
+ }
+ Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"PWD\n", 4);
+ Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp));
+
+ /* this sends an FTP-like "header" to the header callback so that the
+ current directory can be read very similar to how it is read when
+ using ordinary FTP. */
+ result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
+ free(tmp);
+ if(result) {
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = result;
+ }
+ else
+ state(data, SSH_SFTP_NEXT_QUOTE);
+ break;
+ }
+ {
+ /*
+ * the arguments following the command must be separated from the
+ * command with a space so we can check for it unconditionally
+ */
+ cp = strchr(cmd, ' ');
+ if(!cp) {
+ failf(data, "Syntax error command '%s', missing parameter",
+ cmd);
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
+ }
+
+ /*
+ * also, every command takes at least one argument so we get that
+ * first argument right now
+ */
+ result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir);
+ if(result) {
+ if(result == CURLE_OUT_OF_MEMORY)
+ failf(data, "Out of memory");
+ else
+ failf(data, "Syntax error: Bad first parameter to '%s'", cmd);
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = result;
+ break;
+ }
+
+ /*
+ * SFTP is a binary protocol, so we don't send text commands
+ * to the server. Instead, we scan for commands used by
+ * OpenSSH's sftp program and call the appropriate libssh2
+ * functions.
+ */
+ if(strncasecompare(cmd, "chgrp ", 6) ||
+ strncasecompare(cmd, "chmod ", 6) ||
+ strncasecompare(cmd, "chown ", 6) ||
+ strncasecompare(cmd, "atime ", 6) ||
+ strncasecompare(cmd, "mtime ", 6)) {
+ /* attribute change */
+
+ /* sshc->quote_path1 contains the mode to set */
+ /* get the destination */
+ result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
+ if(result) {
+ if(result == CURLE_OUT_OF_MEMORY)
+ failf(data, "Out of memory");
+ else
+ failf(data, "Syntax error in %s: Bad second parameter", cmd);
+ Curl_safefree(sshc->quote_path1);
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = result;
+ break;
+ }
+ memset(&sshp->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
+ state(data, SSH_SFTP_QUOTE_STAT);
+ break;
+ }
+ if(strncasecompare(cmd, "ln ", 3) ||
+ strncasecompare(cmd, "symlink ", 8)) {
+ /* symbolic linking */
+ /* sshc->quote_path1 is the source */
+ /* get the destination */
+ result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
+ if(result) {
+ if(result == CURLE_OUT_OF_MEMORY)
+ failf(data, "Out of memory");
+ else
+ failf(data,
+ "Syntax error in ln/symlink: Bad second parameter");
+ Curl_safefree(sshc->quote_path1);
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = result;
+ break;
+ }
+ state(data, SSH_SFTP_QUOTE_SYMLINK);
+ break;
+ }
+ else if(strncasecompare(cmd, "mkdir ", 6)) {
+ /* create dir */
+ state(data, SSH_SFTP_QUOTE_MKDIR);
+ break;
+ }
+ else if(strncasecompare(cmd, "rename ", 7)) {
+ /* rename file */
+ /* first param is the source path */
+ /* second param is the dest. path */
+ result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
+ if(result) {
+ if(result == CURLE_OUT_OF_MEMORY)
+ failf(data, "Out of memory");
+ else
+ failf(data, "Syntax error in rename: Bad second parameter");
+ Curl_safefree(sshc->quote_path1);
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = result;
+ break;
+ }
+ state(data, SSH_SFTP_QUOTE_RENAME);
+ break;
+ }
+ else if(strncasecompare(cmd, "rmdir ", 6)) {
+ /* delete dir */
+ state(data, SSH_SFTP_QUOTE_RMDIR);
+ break;
+ }
+ else if(strncasecompare(cmd, "rm ", 3)) {
+ state(data, SSH_SFTP_QUOTE_UNLINK);
+ break;
+ }
+#ifdef HAS_STATVFS_SUPPORT
+ else if(strncasecompare(cmd, "statvfs ", 8)) {
+ state(data, SSH_SFTP_QUOTE_STATVFS);
+ break;
+ }
+#endif
+
+ failf(data, "Unknown SFTP command");
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
+ }
+ }
+ break;
+
+ case SSH_SFTP_NEXT_QUOTE:
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+
+ sshc->quote_item = sshc->quote_item->next;
+
+ if(sshc->quote_item) {
+ state(data, SSH_SFTP_QUOTE);
+ }
+ else {
+ if(sshc->nextstate != SSH_NO_STATE) {
+ state(data, sshc->nextstate);
+ sshc->nextstate = SSH_NO_STATE;
+ }
+ else {
+ state(data, SSH_SFTP_GETINFO);
+ }
+ }
+ break;
+
+ case SSH_SFTP_QUOTE_STAT:
+ {
+ char *cmd = sshc->quote_item->data;
+ sshc->acceptfail = FALSE;
+
+ /* if a command starts with an asterisk, which a legal SFTP command never
+ can, the command will be allowed to fail without it causing any
+ aborts or cancels etc. It will cause libcurl to act as if the command
+ is successful, whatever the server reponds. */
+
+ if(cmd[0] == '*') {
+ cmd++;
+ sshc->acceptfail = TRUE;
+ }
+
+ if(!strncasecompare(cmd, "chmod", 5)) {
+ /* Since chown and chgrp only set owner OR group but libssh2 wants to
+ * set them both at once, we need to obtain the current ownership
+ * first. This takes an extra protocol round trip.
+ */
+ rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2,
+ curlx_uztoui(strlen(sshc->quote_path2)),
+ LIBSSH2_SFTP_STAT,
+ &sshp->quote_attrs);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc && !sshc->acceptfail) { /* get those attributes */
+ sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ failf(data, "Attempt to get SFTP stats failed: %s",
+ sftp_libssh2_strerror(sftperr));
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
+ }
+ }
+
+ /* Now set the new attributes... */
+ if(strncasecompare(cmd, "chgrp", 5)) {
+ sshp->quote_attrs.gid = strtoul(sshc->quote_path1, NULL, 10);
+ sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
+ if(sshp->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
+ !sshc->acceptfail) {
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ failf(data, "Syntax error: chgrp gid not a number");
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
+ }
+ }
+ else if(strncasecompare(cmd, "chmod", 5)) {
+ sshp->quote_attrs.permissions = strtoul(sshc->quote_path1, NULL, 8);
+ sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
+ /* permissions are octal */
+ if(sshp->quote_attrs.permissions == 0 &&
+ !ISDIGIT(sshc->quote_path1[0])) {
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ failf(data, "Syntax error: chmod permissions not a number");
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
+ }
+ }
+ else if(strncasecompare(cmd, "chown", 5)) {
+ sshp->quote_attrs.uid = strtoul(sshc->quote_path1, NULL, 10);
+ sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
+ if(sshp->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
+ !sshc->acceptfail) {
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ failf(data, "Syntax error: chown uid not a number");
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
+ }
+ }
+ else if(strncasecompare(cmd, "atime", 5) ||
+ strncasecompare(cmd, "mtime", 5)) {
+ time_t date = Curl_getdate_capped(sshc->quote_path1);
+ bool fail = FALSE;
+
+ if(date == -1) {
+ failf(data, "incorrect date format for %.*s", 5, cmd);
+ fail = TRUE;
+ }
+#if SIZEOF_TIME_T > SIZEOF_LONG
+ if(date > 0xffffffff) {
+ /* if 'long' can't old >32bit, this date cannot be sent */
+ failf(data, "date overflow");
+ fail = TRUE;
+ }
+#endif
+ if(fail) {
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
+ }
+ if(strncasecompare(cmd, "atime", 5))
+ sshp->quote_attrs.atime = (unsigned long)date;
+ else /* mtime */
+ sshp->quote_attrs.mtime = (unsigned long)date;
+
+ sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_ACMODTIME;
+ }
+
+ /* Now send the completed structure... */
+ state(data, SSH_SFTP_QUOTE_SETSTAT);
+ break;
+ }
+
+ case SSH_SFTP_QUOTE_SETSTAT:
+ rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2,
+ curlx_uztoui(strlen(sshc->quote_path2)),
+ LIBSSH2_SFTP_SETSTAT,
+ &sshp->quote_attrs);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc && !sshc->acceptfail) {
+ sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ failf(data, "Attempt to set SFTP stats failed: %s",
+ sftp_libssh2_strerror(sftperr));
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
+ }
+ state(data, SSH_SFTP_NEXT_QUOTE);
+ break;
+
+ case SSH_SFTP_QUOTE_SYMLINK:
+ rc = libssh2_sftp_symlink_ex(sshc->sftp_session, sshc->quote_path1,
+ curlx_uztoui(strlen(sshc->quote_path1)),
+ sshc->quote_path2,
+ curlx_uztoui(strlen(sshc->quote_path2)),
+ LIBSSH2_SFTP_SYMLINK);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc && !sshc->acceptfail) {
+ sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ failf(data, "symlink command failed: %s",
+ sftp_libssh2_strerror(sftperr));
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
+ }
+ state(data, SSH_SFTP_NEXT_QUOTE);
+ break;
+
+ case SSH_SFTP_QUOTE_MKDIR:
+ rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshc->quote_path1,
+ curlx_uztoui(strlen(sshc->quote_path1)),
+ data->set.new_directory_perms);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc && !sshc->acceptfail) {
+ sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ failf(data, "mkdir command failed: %s",
+ sftp_libssh2_strerror(sftperr));
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
+ }
+ state(data, SSH_SFTP_NEXT_QUOTE);
+ break;
+
+ case SSH_SFTP_QUOTE_RENAME:
+ rc = libssh2_sftp_rename_ex(sshc->sftp_session, sshc->quote_path1,
+ curlx_uztoui(strlen(sshc->quote_path1)),
+ sshc->quote_path2,
+ curlx_uztoui(strlen(sshc->quote_path2)),
+ LIBSSH2_SFTP_RENAME_OVERWRITE |
+ LIBSSH2_SFTP_RENAME_ATOMIC |
+ LIBSSH2_SFTP_RENAME_NATIVE);
+
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc && !sshc->acceptfail) {
+ sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ failf(data, "rename command failed: %s",
+ sftp_libssh2_strerror(sftperr));
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
+ }
+ state(data, SSH_SFTP_NEXT_QUOTE);
+ break;
+
+ case SSH_SFTP_QUOTE_RMDIR:
+ rc = libssh2_sftp_rmdir_ex(sshc->sftp_session, sshc->quote_path1,
+ curlx_uztoui(strlen(sshc->quote_path1)));
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc && !sshc->acceptfail) {
+ sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ failf(data, "rmdir command failed: %s",
+ sftp_libssh2_strerror(sftperr));
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
+ }
+ state(data, SSH_SFTP_NEXT_QUOTE);
+ break;
+
+ case SSH_SFTP_QUOTE_UNLINK:
+ rc = libssh2_sftp_unlink_ex(sshc->sftp_session, sshc->quote_path1,
+ curlx_uztoui(strlen(sshc->quote_path1)));
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc && !sshc->acceptfail) {
+ sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ failf(data, "rm command failed: %s", sftp_libssh2_strerror(sftperr));
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
+ }
+ state(data, SSH_SFTP_NEXT_QUOTE);
+ break;
+
+#ifdef HAS_STATVFS_SUPPORT
+ case SSH_SFTP_QUOTE_STATVFS:
+ {
+ LIBSSH2_SFTP_STATVFS statvfs;
+ rc = libssh2_sftp_statvfs(sshc->sftp_session, sshc->quote_path1,
+ curlx_uztoui(strlen(sshc->quote_path1)),
+ &statvfs);
+
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc && !sshc->acceptfail) {
+ sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ failf(data, "statvfs command failed: %s",
+ sftp_libssh2_strerror(sftperr));
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
+ }
+ else if(rc == 0) {
+ char *tmp = aprintf("statvfs:\n"
+ "f_bsize: %llu\n" "f_frsize: %llu\n"
+ "f_blocks: %llu\n" "f_bfree: %llu\n"
+ "f_bavail: %llu\n" "f_files: %llu\n"
+ "f_ffree: %llu\n" "f_favail: %llu\n"
+ "f_fsid: %llu\n" "f_flag: %llu\n"
+ "f_namemax: %llu\n",
+ statvfs.f_bsize, statvfs.f_frsize,
+ statvfs.f_blocks, statvfs.f_bfree,
+ statvfs.f_bavail, statvfs.f_files,
+ statvfs.f_ffree, statvfs.f_favail,
+ statvfs.f_fsid, statvfs.f_flag,
+ statvfs.f_namemax);
+ if(!tmp) {
+ result = CURLE_OUT_OF_MEMORY;
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ break;
+ }
+
+ result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
+ free(tmp);
+ if(result) {
+ state(data, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = result;
+ }
+ }
+ state(data, SSH_SFTP_NEXT_QUOTE);
+ break;
+ }
+#endif
+ case SSH_SFTP_GETINFO:
+ {
+ if(data->set.get_filetime) {
+ state(data, SSH_SFTP_FILETIME);
+ }
+ else {
+ state(data, SSH_SFTP_TRANS_INIT);
+ }
+ break;
+ }
+
+ case SSH_SFTP_FILETIME:
+ {
+ LIBSSH2_SFTP_ATTRIBUTES attrs;
+
+ rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path,
+ curlx_uztoui(strlen(sshp->path)),
+ LIBSSH2_SFTP_STAT, &attrs);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc == 0) {
+ data->info.filetime = attrs.mtime;
+ }
+
+ state(data, SSH_SFTP_TRANS_INIT);
+ break;
+ }
+
+ case SSH_SFTP_TRANS_INIT:
+ if(data->set.upload)
+ state(data, SSH_SFTP_UPLOAD_INIT);
+ else {
+ if(sshp->path[strlen(sshp->path)-1] == '/')
+ state(data, SSH_SFTP_READDIR_INIT);
+ else
+ state(data, SSH_SFTP_DOWNLOAD_INIT);
+ }
+ break;
+
+ case SSH_SFTP_UPLOAD_INIT:
+ {
+ unsigned long flags;
+ /*
+ * NOTE!!! libssh2 requires that the destination path is a full path
+ * that includes the destination file and name OR ends in a "/"
+ * If this is not done the destination file will be named the
+ * same name as the last directory in the path.
+ */
+
+ if(data->state.resume_from) {
+ LIBSSH2_SFTP_ATTRIBUTES attrs;
+ if(data->state.resume_from < 0) {
+ rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path,
+ curlx_uztoui(strlen(sshp->path)),
+ LIBSSH2_SFTP_STAT, &attrs);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc) {
+ data->state.resume_from = 0;
+ }
+ else {
+ curl_off_t size = attrs.filesize;
+ if(size < 0) {
+ failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
+ return CURLE_BAD_DOWNLOAD_RESUME;
+ }
+ data->state.resume_from = attrs.filesize;
+ }
+ }
+ }
+
+ if(data->set.remote_append)
+ /* Try to open for append, but create if nonexisting */
+ flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_APPEND;
+ else if(data->state.resume_from > 0)
+ /* If we have restart position then open for append */
+ flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_APPEND;
+ else
+ /* Clear file before writing (normal behavior) */
+ flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC;
+
+ sshc->sftp_handle =
+ libssh2_sftp_open_ex(sshc->sftp_session, sshp->path,
+ curlx_uztoui(strlen(sshp->path)),
+ flags, data->set.new_file_perms,
+ LIBSSH2_SFTP_OPENFILE);
+
+ if(!sshc->sftp_handle) {
+ rc = libssh2_session_last_errno(sshc->ssh_session);
+
+ if(LIBSSH2_ERROR_EAGAIN == rc)
+ break;
+
+ if(LIBSSH2_ERROR_SFTP_PROTOCOL == rc)
+ /* only when there was an SFTP protocol error can we extract
+ the sftp error! */
+ sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ else
+ sftperr = LIBSSH2_FX_OK; /* not an sftp error at all */
+
+ if(sshc->secondCreateDirs) {
+ state(data, SSH_SFTP_CLOSE);
+ sshc->actualcode = sftperr != LIBSSH2_FX_OK ?
+ sftp_libssh2_error_to_CURLE(sftperr):CURLE_SSH;
+ failf(data, "Creating the dir/file failed: %s",
+ sftp_libssh2_strerror(sftperr));
+ break;
+ }
+ if(((sftperr == LIBSSH2_FX_NO_SUCH_FILE) ||
+ (sftperr == LIBSSH2_FX_FAILURE) ||
+ (sftperr == LIBSSH2_FX_NO_SUCH_PATH)) &&
+ (data->set.ftp_create_missing_dirs &&
+ (strlen(sshp->path) > 1))) {
+ /* try to create the path remotely */
+ rc = 0; /* clear rc and continue */
+ sshc->secondCreateDirs = 1;
+ state(data, SSH_SFTP_CREATE_DIRS_INIT);
+ break;
+ }
+ state(data, SSH_SFTP_CLOSE);
+ sshc->actualcode = sftperr != LIBSSH2_FX_OK ?
+ sftp_libssh2_error_to_CURLE(sftperr):CURLE_SSH;
+ if(!sshc->actualcode) {
+ /* Sometimes, for some reason libssh2_sftp_last_error() returns zero
+ even though libssh2_sftp_open() failed previously! We need to
+ work around that! */
+ sshc->actualcode = CURLE_SSH;
+ sftperr = LIBSSH2_FX_OK;
+ }
+ failf(data, "Upload failed: %s (%lu/%d)",
+ sftperr != LIBSSH2_FX_OK ?
+ sftp_libssh2_strerror(sftperr):"ssh error",
+ sftperr, rc);
+ break;
+ }
+
+ /* If we have a restart point then we need to seek to the correct
+ position. */
+ if(data->state.resume_from > 0) {
+ /* Let's read off the proper amount of bytes from the input. */
+ if(conn->seek_func) {
+ Curl_set_in_callback(data, true);
+ seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
+ SEEK_SET);
+ Curl_set_in_callback(data, false);
+ }
+
+ if(seekerr != CURL_SEEKFUNC_OK) {
+ curl_off_t passed = 0;
+
+ if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
+ failf(data, "Could not seek stream");
+ return CURLE_FTP_COULDNT_USE_REST;
+ }
+ /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
+ do {
+ size_t readthisamountnow =
+ (data->state.resume_from - passed > data->set.buffer_size) ?
+ (size_t)data->set.buffer_size :
+ curlx_sotouz(data->state.resume_from - passed);
+
+ size_t actuallyread;
+ Curl_set_in_callback(data, true);
+ actuallyread = data->state.fread_func(data->state.buffer, 1,
+ readthisamountnow,
+ data->state.in);
+ Curl_set_in_callback(data, false);
+
+ passed += actuallyread;
+ if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
+ /* this checks for greater-than only to make sure that the
+ CURL_READFUNC_ABORT return code still aborts */
+ failf(data, "Failed to read data");
+ return CURLE_FTP_COULDNT_USE_REST;
+ }
+ } while(passed < data->state.resume_from);
+ }
+
+ /* now, decrease the size of the read */
+ if(data->state.infilesize > 0) {
+ data->state.infilesize -= data->state.resume_from;
+ data->req.size = data->state.infilesize;
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+ }
+
+ SFTP_SEEK(sshc->sftp_handle, data->state.resume_from);
+ }
+ if(data->state.infilesize > 0) {
+ data->req.size = data->state.infilesize;
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+ }
+ /* upload data */
+ Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
+
+ /* not set by Curl_setup_transfer to preserve keepon bits */
+ conn->sockfd = conn->writesockfd;
+
+ if(result) {
+ state(data, SSH_SFTP_CLOSE);
+ sshc->actualcode = result;
+ }
+ else {
+ /* store this original bitmask setup to use later on if we can't
+ figure out a "real" bitmask */
+ sshc->orig_waitfor = data->req.keepon;
+
+ /* we want to use the _sending_ function even when the socket turns
+ out readable as the underlying libssh2 sftp send function will deal
+ with both accordingly */
+ conn->cselect_bits = CURL_CSELECT_OUT;
+
+ /* since we don't really wait for anything at this point, we want the
+ state machine to move on as soon as possible so we set a very short
+ timeout here */
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+
+ state(data, SSH_STOP);
+ }
+ break;
+ }
+
+ case SSH_SFTP_CREATE_DIRS_INIT:
+ if(strlen(sshp->path) > 1) {
+ sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */
+ state(data, SSH_SFTP_CREATE_DIRS);
+ }
+ else {
+ state(data, SSH_SFTP_UPLOAD_INIT);
+ }
+ break;
+
+ case SSH_SFTP_CREATE_DIRS:
+ sshc->slash_pos = strchr(sshc->slash_pos, '/');
+ if(sshc->slash_pos) {
+ *sshc->slash_pos = 0;
+
+ infof(data, "Creating directory '%s'", sshp->path);
+ state(data, SSH_SFTP_CREATE_DIRS_MKDIR);
+ break;
+ }
+ state(data, SSH_SFTP_UPLOAD_INIT);
+ break;
+
+ case SSH_SFTP_CREATE_DIRS_MKDIR:
+ /* 'mode' - parameter is preliminary - default to 0644 */
+ rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshp->path,
+ curlx_uztoui(strlen(sshp->path)),
+ data->set.new_directory_perms);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ *sshc->slash_pos = '/';
+ ++sshc->slash_pos;
+ if(rc < 0) {
+ /*
+ * Abort if failure wasn't that the dir already exists or the
+ * permission was denied (creation might succeed further down the
+ * path) - retry on unspecific FAILURE also
+ */
+ sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ if((sftperr != LIBSSH2_FX_FILE_ALREADY_EXISTS) &&
+ (sftperr != LIBSSH2_FX_FAILURE) &&
+ (sftperr != LIBSSH2_FX_PERMISSION_DENIED)) {
+ result = sftp_libssh2_error_to_CURLE(sftperr);
+ state(data, SSH_SFTP_CLOSE);
+ sshc->actualcode = result?result:CURLE_SSH;
+ break;
+ }
+ rc = 0; /* clear rc and continue */
+ }
+ state(data, SSH_SFTP_CREATE_DIRS);
+ break;
+
+ case SSH_SFTP_READDIR_INIT:
+ Curl_pgrsSetDownloadSize(data, -1);
+ if(data->req.no_body) {
+ state(data, SSH_STOP);
+ break;
+ }
+
+ /*
+ * This is a directory that we are trying to get, so produce a directory
+ * listing
+ */
+ sshc->sftp_handle = libssh2_sftp_open_ex(sshc->sftp_session,
+ sshp->path,
+ curlx_uztoui(
+ strlen(sshp->path)),
+ 0, 0, LIBSSH2_SFTP_OPENDIR);
+ if(!sshc->sftp_handle) {
+ if(libssh2_session_last_errno(sshc->ssh_session) ==
+ LIBSSH2_ERROR_EAGAIN) {
+ rc = LIBSSH2_ERROR_EAGAIN;
+ break;
+ }
+ sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ failf(data, "Could not open directory for reading: %s",
+ sftp_libssh2_strerror(sftperr));
+ state(data, SSH_SFTP_CLOSE);
+ result = sftp_libssh2_error_to_CURLE(sftperr);
+ sshc->actualcode = result?result:CURLE_SSH;
+ break;
+ }
+ sshp->readdir_filename = malloc(PATH_MAX + 1);
+ if(!sshp->readdir_filename) {
+ state(data, SSH_SFTP_CLOSE);
+ sshc->actualcode = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+ sshp->readdir_longentry = malloc(PATH_MAX + 1);
+ if(!sshp->readdir_longentry) {
+ Curl_safefree(sshp->readdir_filename);
+ state(data, SSH_SFTP_CLOSE);
+ sshc->actualcode = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+ Curl_dyn_init(&sshp->readdir, PATH_MAX * 2);
+ state(data, SSH_SFTP_READDIR);
+ break;
+
+ case SSH_SFTP_READDIR:
+ rc = libssh2_sftp_readdir_ex(sshc->sftp_handle,
+ sshp->readdir_filename,
+ PATH_MAX,
+ sshp->readdir_longentry,
+ PATH_MAX,
+ &sshp->readdir_attrs);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc > 0) {
+ readdir_len = (size_t) rc;
+ sshp->readdir_filename[readdir_len] = '\0';
+
+ if(data->set.list_only) {
+ result = Curl_client_write(data, CLIENTWRITE_BODY,
+ sshp->readdir_filename,
+ readdir_len);
+ if(!result)
+ result = Curl_client_write(data, CLIENTWRITE_BODY,
+ (char *)"\n", 1);
+ if(result) {
+ state(data, SSH_STOP);
+ break;
+ }
+ /* since this counts what we send to the client, we include the
+ newline in this counter */
+ data->req.bytecount += readdir_len + 1;
+
+ /* output debug output if that is requested */
+ Curl_debug(data, CURLINFO_DATA_IN, sshp->readdir_filename,
+ readdir_len);
+ Curl_debug(data, CURLINFO_DATA_IN, (char *)"\n", 1);
+ }
+ else {
+ result = Curl_dyn_add(&sshp->readdir, sshp->readdir_longentry);
+
+ if(!result) {
+ if((sshp->readdir_attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) &&
+ ((sshp->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
+ LIBSSH2_SFTP_S_IFLNK)) {
+ Curl_dyn_init(&sshp->readdir_link, PATH_MAX);
+ result = Curl_dyn_addf(&sshp->readdir_link, "%s%s", sshp->path,
+ sshp->readdir_filename);
+ state(data, SSH_SFTP_READDIR_LINK);
+ if(!result)
+ break;
+ }
+ else {
+ state(data, SSH_SFTP_READDIR_BOTTOM);
+ break;
+ }
+ }
+ sshc->actualcode = result;
+ state(data, SSH_SFTP_CLOSE);
+ break;
+ }
+ }
+ else if(rc == 0) {
+ Curl_safefree(sshp->readdir_filename);
+ Curl_safefree(sshp->readdir_longentry);
+ state(data, SSH_SFTP_READDIR_DONE);
+ break;
+ }
+ else if(rc < 0) {
+ sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ result = sftp_libssh2_error_to_CURLE(sftperr);
+ sshc->actualcode = result?result:CURLE_SSH;
+ failf(data, "Could not open remote file for reading: %s :: %d",
+ sftp_libssh2_strerror(sftperr),
+ libssh2_session_last_errno(sshc->ssh_session));
+ Curl_safefree(sshp->readdir_filename);
+ Curl_safefree(sshp->readdir_longentry);
+ state(data, SSH_SFTP_CLOSE);
+ break;
+ }
+ break;
+
+ case SSH_SFTP_READDIR_LINK:
+ rc =
+ libssh2_sftp_symlink_ex(sshc->sftp_session,
+ Curl_dyn_ptr(&sshp->readdir_link),
+ (int)Curl_dyn_len(&sshp->readdir_link),
+ sshp->readdir_filename,
+ PATH_MAX, LIBSSH2_SFTP_READLINK);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ Curl_dyn_free(&sshp->readdir_link);
+
+ /* append filename and extra output */
+ result = Curl_dyn_addf(&sshp->readdir, " -> %s", sshp->readdir_filename);
+
+ if(result) {
+ sshc->readdir_line = NULL;
+ Curl_safefree(sshp->readdir_filename);
+ Curl_safefree(sshp->readdir_longentry);
+ state(data, SSH_SFTP_CLOSE);
+ sshc->actualcode = result;
+ break;
+ }
+
+ state(data, SSH_SFTP_READDIR_BOTTOM);
+ break;
+
+ case SSH_SFTP_READDIR_BOTTOM:
+ result = Curl_dyn_addn(&sshp->readdir, "\n", 1);
+ if(!result)
+ result = Curl_client_write(data, CLIENTWRITE_BODY,
+ Curl_dyn_ptr(&sshp->readdir),
+ Curl_dyn_len(&sshp->readdir));
+
+ if(!result) {
+ /* output debug output if that is requested */
+ Curl_debug(data, CURLINFO_DATA_IN,
+ Curl_dyn_ptr(&sshp->readdir),
+ Curl_dyn_len(&sshp->readdir));
+ data->req.bytecount += Curl_dyn_len(&sshp->readdir);
+ }
+ if(result) {
+ Curl_dyn_free(&sshp->readdir);
+ state(data, SSH_STOP);
+ }
+ else {
+ Curl_dyn_reset(&sshp->readdir);
+ state(data, SSH_SFTP_READDIR);
+ }
+ break;
+
+ case SSH_SFTP_READDIR_DONE:
+ if(libssh2_sftp_closedir(sshc->sftp_handle) ==
+ LIBSSH2_ERROR_EAGAIN) {
+ rc = LIBSSH2_ERROR_EAGAIN;
+ break;
+ }
+ sshc->sftp_handle = NULL;
+ Curl_safefree(sshp->readdir_filename);
+ Curl_safefree(sshp->readdir_longentry);
+
+ /* no data to transfer */
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
+ state(data, SSH_STOP);
+ break;
+
+ case SSH_SFTP_DOWNLOAD_INIT:
+ /*
+ * Work on getting the specified file
+ */
+ sshc->sftp_handle =
+ libssh2_sftp_open_ex(sshc->sftp_session, sshp->path,
+ curlx_uztoui(strlen(sshp->path)),
+ LIBSSH2_FXF_READ, data->set.new_file_perms,
+ LIBSSH2_SFTP_OPENFILE);
+ if(!sshc->sftp_handle) {
+ if(libssh2_session_last_errno(sshc->ssh_session) ==
+ LIBSSH2_ERROR_EAGAIN) {
+ rc = LIBSSH2_ERROR_EAGAIN;
+ break;
+ }
+ sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ failf(data, "Could not open remote file for reading: %s",
+ sftp_libssh2_strerror(sftperr));
+ state(data, SSH_SFTP_CLOSE);
+ result = sftp_libssh2_error_to_CURLE(sftperr);
+ sshc->actualcode = result?result:CURLE_SSH;
+ break;
+ }
+ state(data, SSH_SFTP_DOWNLOAD_STAT);
+ break;
+
+ case SSH_SFTP_DOWNLOAD_STAT:
+ {
+ LIBSSH2_SFTP_ATTRIBUTES attrs;
+
+ rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path,
+ curlx_uztoui(strlen(sshp->path)),
+ LIBSSH2_SFTP_STAT, &attrs);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc ||
+ !(attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) ||
+ (attrs.filesize == 0)) {
+ /*
+ * libssh2_sftp_open() didn't return an error, so maybe the server
+ * just doesn't support stat()
+ * OR the server doesn't return a file size with a stat()
+ * OR file size is 0
+ */
+ data->req.size = -1;
+ data->req.maxdownload = -1;
+ Curl_pgrsSetDownloadSize(data, -1);
+ }
+ else {
+ curl_off_t size = attrs.filesize;
+
+ if(size < 0) {
+ failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
+ return CURLE_BAD_DOWNLOAD_RESUME;
+ }
+ if(data->state.use_range) {
+ curl_off_t from, to;
+ char *ptr;
+ char *ptr2;
+ CURLofft to_t;
+ CURLofft from_t;
+
+ from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from);
+ if(from_t == CURL_OFFT_FLOW)
+ return CURLE_RANGE_ERROR;
+ while(*ptr && (ISBLANK(*ptr) || (*ptr == '-')))
+ ptr++;
+ to_t = curlx_strtoofft(ptr, &ptr2, 10, &to);
+ if(to_t == CURL_OFFT_FLOW)
+ return CURLE_RANGE_ERROR;
+ if((to_t == CURL_OFFT_INVAL) /* no "to" value given */
+ || (to >= size)) {
+ to = size - 1;
+ }
+ if(from_t) {
+ /* from is relative to end of file */
+ from = size - to;
+ to = size - 1;
+ }
+ if(from > size) {
+ failf(data, "Offset (%"
+ CURL_FORMAT_CURL_OFF_T ") was beyond file size (%"
+ CURL_FORMAT_CURL_OFF_T ")", from, attrs.filesize);
+ return CURLE_BAD_DOWNLOAD_RESUME;
+ }
+ if(from > to) {
+ from = to;
+ size = 0;
+ }
+ else {
+ size = to - from + 1;
+ }
+
+ SFTP_SEEK(sshc->sftp_handle, from);
+ }
+ data->req.size = size;
+ data->req.maxdownload = size;
+ Curl_pgrsSetDownloadSize(data, size);
+ }
+
+ /* We can resume if we can seek to the resume position */
+ if(data->state.resume_from) {
+ if(data->state.resume_from < 0) {
+ /* We're supposed to download the last abs(from) bytes */
+ if((curl_off_t)attrs.filesize < -data->state.resume_from) {
+ failf(data, "Offset (%"
+ CURL_FORMAT_CURL_OFF_T ") was beyond file size (%"
+ CURL_FORMAT_CURL_OFF_T ")",
+ data->state.resume_from, attrs.filesize);
+ return CURLE_BAD_DOWNLOAD_RESUME;
+ }
+ /* download from where? */
+ data->state.resume_from += attrs.filesize;
+ }
+ else {
+ if((curl_off_t)attrs.filesize < data->state.resume_from) {
+ failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
+ ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
+ data->state.resume_from, attrs.filesize);
+ return CURLE_BAD_DOWNLOAD_RESUME;
+ }
+ }
+ /* Now store the number of bytes we are expected to download */
+ data->req.size = attrs.filesize - data->state.resume_from;
+ data->req.maxdownload = attrs.filesize - data->state.resume_from;
+ Curl_pgrsSetDownloadSize(data,
+ attrs.filesize - data->state.resume_from);
+ SFTP_SEEK(sshc->sftp_handle, data->state.resume_from);
+ }
+ }
+
+ /* Setup the actual download */
+ if(data->req.size == 0) {
+ /* no data to transfer */
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
+ infof(data, "File already completely downloaded");
+ state(data, SSH_STOP);
+ break;
+ }
+ Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1);
+
+ /* not set by Curl_setup_transfer to preserve keepon bits */
+ conn->writesockfd = conn->sockfd;
+
+ /* we want to use the _receiving_ function even when the socket turns
+ out writableable as the underlying libssh2 recv function will deal
+ with both accordingly */
+ conn->cselect_bits = CURL_CSELECT_IN;
+
+ if(result) {
+ /* this should never occur; the close state should be entered
+ at the time the error occurs */
+ state(data, SSH_SFTP_CLOSE);
+ sshc->actualcode = result;
+ }
+ else {
+ state(data, SSH_STOP);
+ }
+ break;
+
+ case SSH_SFTP_CLOSE:
+ if(sshc->sftp_handle) {
+ rc = libssh2_sftp_close(sshc->sftp_handle);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc < 0) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg);
+ }
+ sshc->sftp_handle = NULL;
+ }
+
+ Curl_safefree(sshp->path);
+
+ DEBUGF(infof(data, "SFTP DONE done"));
+
+ /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT
+ After nextstate is executed, the control should come back to
+ SSH_SFTP_CLOSE to pass the correct result back */
+ if(sshc->nextstate != SSH_NO_STATE &&
+ sshc->nextstate != SSH_SFTP_CLOSE) {
+ state(data, sshc->nextstate);
+ sshc->nextstate = SSH_SFTP_CLOSE;
+ }
+ else {
+ state(data, SSH_STOP);
+ result = sshc->actualcode;
+ }
+ break;
+
+ case SSH_SFTP_SHUTDOWN:
+ /* during times we get here due to a broken transfer and then the
+ sftp_handle might not have been taken down so make sure that is done
+ before we proceed */
+
+ if(sshc->sftp_handle) {
+ rc = libssh2_sftp_close(sshc->sftp_handle);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc < 0) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session, &err_msg,
+ NULL, 0);
+ infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg);
+ }
+ sshc->sftp_handle = NULL;
+ }
+ if(sshc->sftp_session) {
+ rc = libssh2_sftp_shutdown(sshc->sftp_session);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc < 0) {
+ infof(data, "Failed to stop libssh2 sftp subsystem");
+ }
+ sshc->sftp_session = NULL;
+ }
+
+ Curl_safefree(sshc->homedir);
+ data->state.most_recent_ftp_entrypath = NULL;
+
+ state(data, SSH_SESSION_DISCONNECT);
+ break;
+
+ case SSH_SCP_TRANS_INIT:
+ result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
+ if(result) {
+ sshc->actualcode = result;
+ state(data, SSH_STOP);
+ break;
+ }
+
+ if(data->set.upload) {
+ if(data->state.infilesize < 0) {
+ failf(data, "SCP requires a known file size for upload");
+ sshc->actualcode = CURLE_UPLOAD_FAILED;
+ state(data, SSH_SCP_CHANNEL_FREE);
+ break;
+ }
+ state(data, SSH_SCP_UPLOAD_INIT);
+ }
+ else {
+ state(data, SSH_SCP_DOWNLOAD_INIT);
+ }
+ break;
+
+ case SSH_SCP_UPLOAD_INIT:
+ /*
+ * libssh2 requires that the destination path is a full path that
+ * includes the destination file and name OR ends in a "/" . If this is
+ * not done the destination file will be named the same name as the last
+ * directory in the path.
+ */
+ sshc->ssh_channel =
+ SCP_SEND(sshc->ssh_session, sshp->path, data->set.new_file_perms,
+ data->state.infilesize);
+ if(!sshc->ssh_channel) {
+ int ssh_err;
+ char *err_msg = NULL;
+
+ if(libssh2_session_last_errno(sshc->ssh_session) ==
+ LIBSSH2_ERROR_EAGAIN) {
+ rc = LIBSSH2_ERROR_EAGAIN;
+ break;
+ }
+
+ ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0));
+ failf(data, "%s", err_msg);
+ state(data, SSH_SCP_CHANNEL_FREE);
+ sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err);
+ /* Map generic errors to upload failed */
+ if(sshc->actualcode == CURLE_SSH ||
+ sshc->actualcode == CURLE_REMOTE_FILE_NOT_FOUND)
+ sshc->actualcode = CURLE_UPLOAD_FAILED;
+ break;
+ }
+
+ /* upload data */
+ data->req.size = data->state.infilesize;
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+ Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
+
+ /* not set by Curl_setup_transfer to preserve keepon bits */
+ conn->sockfd = conn->writesockfd;
+
+ if(result) {
+ state(data, SSH_SCP_CHANNEL_FREE);
+ sshc->actualcode = result;
+ }
+ else {
+ /* store this original bitmask setup to use later on if we can't
+ figure out a "real" bitmask */
+ sshc->orig_waitfor = data->req.keepon;
+
+ /* we want to use the _sending_ function even when the socket turns
+ out readable as the underlying libssh2 scp send function will deal
+ with both accordingly */
+ conn->cselect_bits = CURL_CSELECT_OUT;
+
+ state(data, SSH_STOP);
+ }
+ break;
+
+ case SSH_SCP_DOWNLOAD_INIT:
+ {
+ curl_off_t bytecount;
+
+ /*
+ * We must check the remote file; if it is a directory no values will
+ * be set in sb
+ */
+
+ /*
+ * If support for >2GB files exists, use it.
+ */
+
+ /* get a fresh new channel from the ssh layer */
+#if LIBSSH2_VERSION_NUM < 0x010700
+ struct stat sb;
+ memset(&sb, 0, sizeof(struct stat));
+ sshc->ssh_channel = libssh2_scp_recv(sshc->ssh_session,
+ sshp->path, &sb);
+#else
+ libssh2_struct_stat sb;
+ memset(&sb, 0, sizeof(libssh2_struct_stat));
+ sshc->ssh_channel = libssh2_scp_recv2(sshc->ssh_session,
+ sshp->path, &sb);
+#endif
+
+ if(!sshc->ssh_channel) {
+ int ssh_err;
+ char *err_msg = NULL;
+
+ if(libssh2_session_last_errno(sshc->ssh_session) ==
+ LIBSSH2_ERROR_EAGAIN) {
+ rc = LIBSSH2_ERROR_EAGAIN;
+ break;
+ }
+
+
+ ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0));
+ failf(data, "%s", err_msg);
+ state(data, SSH_SCP_CHANNEL_FREE);
+ sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err);
+ break;
+ }
+
+ /* download data */
+ bytecount = (curl_off_t)sb.st_size;
+ data->req.maxdownload = (curl_off_t)sb.st_size;
+ Curl_setup_transfer(data, FIRSTSOCKET, bytecount, FALSE, -1);
+
+ /* not set by Curl_setup_transfer to preserve keepon bits */
+ conn->writesockfd = conn->sockfd;
+
+ /* we want to use the _receiving_ function even when the socket turns
+ out writableable as the underlying libssh2 recv function will deal
+ with both accordingly */
+ conn->cselect_bits = CURL_CSELECT_IN;
+
+ if(result) {
+ state(data, SSH_SCP_CHANNEL_FREE);
+ sshc->actualcode = result;
+ }
+ else
+ state(data, SSH_STOP);
+ }
+ break;
+
+ case SSH_SCP_DONE:
+ if(data->set.upload)
+ state(data, SSH_SCP_SEND_EOF);
+ else
+ state(data, SSH_SCP_CHANNEL_FREE);
+ break;
+
+ case SSH_SCP_SEND_EOF:
+ if(sshc->ssh_channel) {
+ rc = libssh2_channel_send_eof(sshc->ssh_channel);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Failed to send libssh2 channel EOF: %d %s",
+ rc, err_msg);
+ }
+ }
+ state(data, SSH_SCP_WAIT_EOF);
+ break;
+
+ case SSH_SCP_WAIT_EOF:
+ if(sshc->ssh_channel) {
+ rc = libssh2_channel_wait_eof(sshc->ssh_channel);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Failed to get channel EOF: %d %s", rc, err_msg);
+ }
+ }
+ state(data, SSH_SCP_WAIT_CLOSE);
+ break;
+
+ case SSH_SCP_WAIT_CLOSE:
+ if(sshc->ssh_channel) {
+ rc = libssh2_channel_wait_closed(sshc->ssh_channel);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Channel failed to close: %d %s", rc, err_msg);
+ }
+ }
+ state(data, SSH_SCP_CHANNEL_FREE);
+ break;
+
+ case SSH_SCP_CHANNEL_FREE:
+ if(sshc->ssh_channel) {
+ rc = libssh2_channel_free(sshc->ssh_channel);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc < 0) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Failed to free libssh2 scp subsystem: %d %s",
+ rc, err_msg);
+ }
+ sshc->ssh_channel = NULL;
+ }
+ DEBUGF(infof(data, "SCP DONE phase complete"));
+#if 0 /* PREV */
+ state(data, SSH_SESSION_DISCONNECT);
+#endif
+ state(data, SSH_STOP);
+ result = sshc->actualcode;
+ break;
+
+ case SSH_SESSION_DISCONNECT:
+ /* during weird times when we've been prematurely aborted, the channel
+ is still alive when we reach this state and we MUST kill the channel
+ properly first */
+ if(sshc->ssh_channel) {
+ rc = libssh2_channel_free(sshc->ssh_channel);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc < 0) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Failed to free libssh2 scp subsystem: %d %s",
+ rc, err_msg);
+ }
+ sshc->ssh_channel = NULL;
+ }
+
+ if(sshc->ssh_session) {
+ rc = libssh2_session_disconnect(sshc->ssh_session, "Shutdown");
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc < 0) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Failed to disconnect libssh2 session: %d %s",
+ rc, err_msg);
+ }
+ }
+
+ Curl_safefree(sshc->homedir);
+ data->state.most_recent_ftp_entrypath = NULL;
+
+ state(data, SSH_SESSION_FREE);
+ break;
+
+ case SSH_SESSION_FREE:
+#ifdef HAVE_LIBSSH2_KNOWNHOST_API
+ if(sshc->kh) {
+ libssh2_knownhost_free(sshc->kh);
+ sshc->kh = NULL;
+ }
+#endif
+
+#ifdef HAVE_LIBSSH2_AGENT_API
+ if(sshc->ssh_agent) {
+ rc = libssh2_agent_disconnect(sshc->ssh_agent);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc < 0) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Failed to disconnect from libssh2 agent: %d %s",
+ rc, err_msg);
+ }
+ libssh2_agent_free(sshc->ssh_agent);
+ sshc->ssh_agent = NULL;
+
+ /* NB: there is no need to free identities, they are part of internal
+ agent stuff */
+ sshc->sshagent_identity = NULL;
+ sshc->sshagent_prev_identity = NULL;
+ }
+#endif
+
+ if(sshc->ssh_session) {
+ rc = libssh2_session_free(sshc->ssh_session);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ break;
+ }
+ if(rc < 0) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Failed to free libssh2 session: %d %s", rc, err_msg);
+ }
+ sshc->ssh_session = NULL;
+ }
+
+ /* worst-case scenario cleanup */
+
+ DEBUGASSERT(sshc->ssh_session == NULL);
+ DEBUGASSERT(sshc->ssh_channel == NULL);
+ DEBUGASSERT(sshc->sftp_session == NULL);
+ DEBUGASSERT(sshc->sftp_handle == NULL);
+#ifdef HAVE_LIBSSH2_KNOWNHOST_API
+ DEBUGASSERT(sshc->kh == NULL);
+#endif
+#ifdef HAVE_LIBSSH2_AGENT_API
+ DEBUGASSERT(sshc->ssh_agent == NULL);
+#endif
+
+ Curl_safefree(sshc->rsa_pub);
+ Curl_safefree(sshc->rsa);
+
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+
+ Curl_safefree(sshc->homedir);
+ Curl_safefree(sshc->readdir_line);
+
+ /* the code we are about to return */
+ result = sshc->actualcode;
+
+ memset(sshc, 0, sizeof(struct ssh_conn));
+
+ connclose(conn, "SSH session free");
+ sshc->state = SSH_SESSION_FREE; /* current */
+ sshc->nextstate = SSH_NO_STATE;
+ state(data, SSH_STOP);
+ break;
+
+ case SSH_QUIT:
+ /* fallthrough, just stop! */
+ default:
+ /* internal error */
+ sshc->nextstate = SSH_NO_STATE;
+ state(data, SSH_STOP);
+ break;
+ }
+
+ } while(!rc && (sshc->state != SSH_STOP));
+
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ /* we would block, we need to wait for the socket to be ready (in the
+ right direction too)! */
+ *block = TRUE;
+ }
+
+ return result;
+}
+
+/* called by the multi interface to figure out what socket(s) to wait for and
+ for what actions in the DO_DONE, PERFORM and WAITPERFORM states */
+static int ssh_getsock(struct Curl_easy *data,
+ struct connectdata *conn,
+ curl_socket_t *sock)
+{
+ int bitmap = GETSOCK_BLANK;
+ (void)data;
+
+ sock[0] = conn->sock[FIRSTSOCKET];
+
+ if(conn->waitfor & KEEP_RECV)
+ bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
+
+ if(conn->waitfor & KEEP_SEND)
+ bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
+
+ return bitmap;
+}
+
+/*
+ * When one of the libssh2 functions has returned LIBSSH2_ERROR_EAGAIN this
+ * function is used to figure out in what direction and stores this info so
+ * that the multi interface can take advantage of it. Make sure to call this
+ * function in all cases so that when it _doesn't_ return EAGAIN we can
+ * restore the default wait bits.
+ */
+static void ssh_block2waitfor(struct Curl_easy *data, bool block)
+{
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ int dir = 0;
+ if(block) {
+ dir = libssh2_session_block_directions(sshc->ssh_session);
+ if(dir) {
+ /* translate the libssh2 define bits into our own bit defines */
+ conn->waitfor = ((dir&LIBSSH2_SESSION_BLOCK_INBOUND)?KEEP_RECV:0) |
+ ((dir&LIBSSH2_SESSION_BLOCK_OUTBOUND)?KEEP_SEND:0);
+ }
+ }
+ if(!dir)
+ /* It didn't block or libssh2 didn't reveal in which direction, put back
+ the original set */
+ conn->waitfor = sshc->orig_waitfor;
+}
+
+/* called repeatedly until done from multi.c */
+static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done)
+{
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ CURLcode result = CURLE_OK;
+ bool block; /* we store the status and use that to provide a ssh_getsock()
+ implementation */
+ do {
+ result = ssh_statemach_act(data, &block);
+ *done = (sshc->state == SSH_STOP) ? TRUE : FALSE;
+ /* if there's no error, it isn't done and it didn't EWOULDBLOCK, then
+ try again */
+ } while(!result && !*done && !block);
+ ssh_block2waitfor(data, block);
+
+ return result;
+}
+
+static CURLcode ssh_block_statemach(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool disconnect)
+{
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ CURLcode result = CURLE_OK;
+ struct curltime dis = Curl_now();
+
+ while((sshc->state != SSH_STOP) && !result) {
+ bool block;
+ timediff_t left = 1000;
+ struct curltime now = Curl_now();
+
+ result = ssh_statemach_act(data, &block);
+ if(result)
+ break;
+
+ if(!disconnect) {
+ if(Curl_pgrsUpdate(data))
+ return CURLE_ABORTED_BY_CALLBACK;
+
+ result = Curl_speedcheck(data, now);
+ if(result)
+ break;
+
+ left = Curl_timeleft(data, NULL, FALSE);
+ if(left < 0) {
+ failf(data, "Operation timed out");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+ }
+ else if(Curl_timediff(now, dis) > 1000) {
+ /* disconnect timeout */
+ failf(data, "Disconnect timed out");
+ result = CURLE_OK;
+ break;
+ }
+
+ if(block) {
+ int dir = libssh2_session_block_directions(sshc->ssh_session);
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ curl_socket_t fd_read = CURL_SOCKET_BAD;
+ curl_socket_t fd_write = CURL_SOCKET_BAD;
+ if(LIBSSH2_SESSION_BLOCK_INBOUND & dir)
+ fd_read = sock;
+ if(LIBSSH2_SESSION_BLOCK_OUTBOUND & dir)
+ fd_write = sock;
+ /* wait for the socket to become ready */
+ (void)Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write,
+ left>1000?1000:left);
+ }
+ }
+
+ return result;
+}
+
+/*
+ * SSH setup and connection
+ */
+static CURLcode ssh_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ struct SSHPROTO *ssh;
+ (void)conn;
+
+ data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
+ if(!ssh)
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_OK;
+}
+
+static Curl_recv scp_recv, sftp_recv;
+static Curl_send scp_send, sftp_send;
+
+#ifndef CURL_DISABLE_PROXY
+static ssize_t ssh_tls_recv(libssh2_socket_t sock, void *buffer,
+ size_t length, int flags, void **abstract)
+{
+ struct Curl_easy *data = (struct Curl_easy *)*abstract;
+ ssize_t nread;
+ CURLcode result;
+ struct connectdata *conn = data->conn;
+ Curl_recv *backup = conn->recv[0];
+ struct ssh_conn *ssh = &conn->proto.sshc;
+ (void)flags;
+
+ /* swap in the TLS reader function for this call only, and then swap back
+ the SSH one again */
+ conn->recv[0] = ssh->tls_recv;
+ result = Curl_read(data, sock, buffer, length, &nread);
+ conn->recv[0] = backup;
+ if(result == CURLE_AGAIN)
+ return -EAGAIN; /* magic return code for libssh2 */
+ else if(result)
+ return -1; /* generic error */
+ Curl_debug(data, CURLINFO_DATA_IN, (char *)buffer, (size_t)nread);
+ return nread;
+}
+
+static ssize_t ssh_tls_send(libssh2_socket_t sock, const void *buffer,
+ size_t length, int flags, void **abstract)
+{
+ struct Curl_easy *data = (struct Curl_easy *)*abstract;
+ ssize_t nwrite;
+ CURLcode result;
+ struct connectdata *conn = data->conn;
+ Curl_send *backup = conn->send[0];
+ struct ssh_conn *ssh = &conn->proto.sshc;
+ (void)flags;
+
+ /* swap in the TLS writer function for this call only, and then swap back
+ the SSH one again */
+ conn->send[0] = ssh->tls_send;
+ result = Curl_write(data, sock, buffer, length, &nwrite);
+ conn->send[0] = backup;
+ if(result == CURLE_AGAIN)
+ return -EAGAIN; /* magic return code for libssh2 */
+ else if(result)
+ return -1; /* error */
+ Curl_debug(data, CURLINFO_DATA_OUT, (char *)buffer, (size_t)nwrite);
+ return nwrite;
+}
+#endif
+
+/*
+ * Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to
+ * do protocol-specific actions at connect-time.
+ */
+static CURLcode ssh_connect(struct Curl_easy *data, bool *done)
+{
+#ifdef CURL_LIBSSH2_DEBUG
+ curl_socket_t sock;
+#endif
+ struct ssh_conn *sshc;
+ CURLcode result;
+ struct connectdata *conn = data->conn;
+
+ /* initialize per-handle data if not already */
+ if(!data->req.p.ssh) {
+ result = ssh_setup_connection(data, conn);
+ if(result)
+ return result;
+ }
+
+ /* We default to persistent connections. We set this already in this connect
+ function to make the re-use checks properly be able to check this bit. */
+ connkeep(conn, "SSH default");
+
+ sshc = &conn->proto.sshc;
+
+#ifdef CURL_LIBSSH2_DEBUG
+ if(conn->user) {
+ infof(data, "User: %s", conn->user);
+ }
+ if(conn->passwd) {
+ infof(data, "Password: %s", conn->passwd);
+ }
+ sock = conn->sock[FIRSTSOCKET];
+#endif /* CURL_LIBSSH2_DEBUG */
+
+ sshc->ssh_session = libssh2_session_init_ex(my_libssh2_malloc,
+ my_libssh2_free,
+ my_libssh2_realloc, data);
+ if(!sshc->ssh_session) {
+ failf(data, "Failure initialising ssh session");
+ return CURLE_FAILED_INIT;
+ }
+
+#ifndef CURL_DISABLE_PROXY
+ if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) {
+ /*
+ * This crazy union dance is here to avoid assigning a void pointer a
+ * function pointer as it is invalid C. The problem is of course that
+ * libssh2 has such an API...
+ */
+ union receive {
+ void *recvp;
+ ssize_t (*recvptr)(libssh2_socket_t, void *, size_t, int, void **);
+ };
+ union transfer {
+ void *sendp;
+ ssize_t (*sendptr)(libssh2_socket_t, const void *, size_t, int, void **);
+ };
+ union receive sshrecv;
+ union transfer sshsend;
+
+ sshrecv.recvptr = ssh_tls_recv;
+ sshsend.sendptr = ssh_tls_send;
+
+ infof(data, "Uses HTTPS proxy");
+ /*
+ Setup libssh2 callbacks to make it read/write TLS from the socket.
+
+ ssize_t
+ recvcb(libssh2_socket_t sock, void *buffer, size_t length,
+ int flags, void **abstract);
+
+ ssize_t
+ sendcb(libssh2_socket_t sock, const void *buffer, size_t length,
+ int flags, void **abstract);
+
+ */
+ libssh2_session_callback_set(sshc->ssh_session,
+ LIBSSH2_CALLBACK_RECV, sshrecv.recvp);
+ libssh2_session_callback_set(sshc->ssh_session,
+ LIBSSH2_CALLBACK_SEND, sshsend.sendp);
+
+ /* Store the underlying TLS recv/send function pointers to be used when
+ reading from the proxy */
+ sshc->tls_recv = conn->recv[FIRSTSOCKET];
+ sshc->tls_send = conn->send[FIRSTSOCKET];
+ }
+
+#endif /* CURL_DISABLE_PROXY */
+ if(conn->handler->protocol & CURLPROTO_SCP) {
+ conn->recv[FIRSTSOCKET] = scp_recv;
+ conn->send[FIRSTSOCKET] = scp_send;
+ }
+ else {
+ conn->recv[FIRSTSOCKET] = sftp_recv;
+ conn->send[FIRSTSOCKET] = sftp_send;
+ }
+
+ if(data->set.ssh_compression) {
+#if LIBSSH2_VERSION_NUM >= 0x010208
+ if(libssh2_session_flag(sshc->ssh_session, LIBSSH2_FLAG_COMPRESS, 1) < 0)
+#endif
+ infof(data, "Failed to enable compression for ssh session");
+ }
+
+#ifdef HAVE_LIBSSH2_KNOWNHOST_API
+ if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
+ int rc;
+ sshc->kh = libssh2_knownhost_init(sshc->ssh_session);
+ if(!sshc->kh) {
+ libssh2_session_free(sshc->ssh_session);
+ sshc->ssh_session = NULL;
+ return CURLE_FAILED_INIT;
+ }
+
+ /* read all known hosts from there */
+ rc = libssh2_knownhost_readfile(sshc->kh,
+ data->set.str[STRING_SSH_KNOWNHOSTS],
+ LIBSSH2_KNOWNHOST_FILE_OPENSSH);
+ if(rc < 0)
+ infof(data, "Failed to read known hosts from %s",
+ data->set.str[STRING_SSH_KNOWNHOSTS]);
+ }
+#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
+
+#ifdef CURL_LIBSSH2_DEBUG
+ libssh2_trace(sshc->ssh_session, ~0);
+ infof(data, "SSH socket: %d", (int)sock);
+#endif /* CURL_LIBSSH2_DEBUG */
+
+ state(data, SSH_INIT);
+
+ result = ssh_multi_statemach(data, done);
+
+ return result;
+}
+
+/*
+ ***********************************************************************
+ *
+ * scp_perform()
+ *
+ * This is the actual DO function for SCP. Get a file according to
+ * the options previously setup.
+ */
+
+static
+CURLcode scp_perform(struct Curl_easy *data,
+ bool *connected,
+ bool *dophase_done)
+{
+ CURLcode result = CURLE_OK;
+
+ DEBUGF(infof(data, "DO phase starts"));
+
+ *dophase_done = FALSE; /* not done yet */
+
+ /* start the first command in the DO phase */
+ state(data, SSH_SCP_TRANS_INIT);
+
+ /* run the state-machine */
+ result = ssh_multi_statemach(data, dophase_done);
+
+ *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
+
+ if(*dophase_done) {
+ DEBUGF(infof(data, "DO phase is complete"));
+ }
+
+ return result;
+}
+
+/* called from multi.c while DOing */
+static CURLcode scp_doing(struct Curl_easy *data,
+ bool *dophase_done)
+{
+ CURLcode result;
+ result = ssh_multi_statemach(data, dophase_done);
+
+ if(*dophase_done) {
+ DEBUGF(infof(data, "DO phase is complete"));
+ }
+ return result;
+}
+
+/*
+ * The DO function is generic for both protocols. There was previously two
+ * separate ones but this way means less duplicated code.
+ */
+
+static CURLcode ssh_do(struct Curl_easy *data, bool *done)
+{
+ CURLcode result;
+ bool connected = 0;
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+
+ *done = FALSE; /* default to false */
+
+ data->req.size = -1; /* make sure this is unknown at this point */
+
+ sshc->actualcode = CURLE_OK; /* reset error code */
+ sshc->secondCreateDirs = 0; /* reset the create dir attempt state
+ variable */
+
+ Curl_pgrsSetUploadCounter(data, 0);
+ Curl_pgrsSetDownloadCounter(data, 0);
+ Curl_pgrsSetUploadSize(data, -1);
+ Curl_pgrsSetDownloadSize(data, -1);
+
+ if(conn->handler->protocol & CURLPROTO_SCP)
+ result = scp_perform(data, &connected, done);
+ else
+ result = sftp_perform(data, &connected, done);
+
+ return result;
+}
+
+/* BLOCKING, but the function is using the state machine so the only reason
+ this is still blocking is that the multi interface code has no support for
+ disconnecting operations that takes a while */
+static CURLcode scp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection)
+{
+ CURLcode result = CURLE_OK;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ (void) dead_connection;
+
+ if(sshc->ssh_session) {
+ /* only if there's a session still around to use! */
+ state(data, SSH_SESSION_DISCONNECT);
+ result = ssh_block_statemach(data, conn, TRUE);
+ }
+
+ return result;
+}
+
+/* generic done function for both SCP and SFTP called from their specific
+ done functions */
+static CURLcode ssh_done(struct Curl_easy *data, CURLcode status)
+{
+ CURLcode result = CURLE_OK;
+ struct SSHPROTO *sshp = data->req.p.ssh;
+ struct connectdata *conn = data->conn;
+
+ if(!status)
+ /* run the state-machine */
+ result = ssh_block_statemach(data, conn, FALSE);
+ else
+ result = status;
+
+ Curl_safefree(sshp->path);
+ Curl_safefree(sshp->readdir_filename);
+ Curl_safefree(sshp->readdir_longentry);
+ Curl_dyn_free(&sshp->readdir);
+
+ if(Curl_pgrsDone(data))
+ return CURLE_ABORTED_BY_CALLBACK;
+
+ data->req.keepon = 0; /* clear all bits */
+ return result;
+}
+
+
+static CURLcode scp_done(struct Curl_easy *data, CURLcode status,
+ bool premature)
+{
+ (void)premature; /* not used */
+
+ if(!status)
+ state(data, SSH_SCP_DONE);
+
+ return ssh_done(data, status);
+
+}
+
+static ssize_t scp_send(struct Curl_easy *data, int sockindex,
+ const void *mem, size_t len, CURLcode *err)
+{
+ ssize_t nwrite;
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ (void)sockindex; /* we only support SCP on the fixed known primary socket */
+
+ /* libssh2_channel_write() returns int! */
+ nwrite = (ssize_t) libssh2_channel_write(sshc->ssh_channel, mem, len);
+
+ ssh_block2waitfor(data, (nwrite == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
+
+ if(nwrite == LIBSSH2_ERROR_EAGAIN) {
+ *err = CURLE_AGAIN;
+ nwrite = 0;
+ }
+ else if(nwrite < LIBSSH2_ERROR_NONE) {
+ *err = libssh2_session_error_to_CURLE((int)nwrite);
+ nwrite = -1;
+ }
+
+ return nwrite;
+}
+
+static ssize_t scp_recv(struct Curl_easy *data, int sockindex,
+ char *mem, size_t len, CURLcode *err)
+{
+ ssize_t nread;
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ (void)sockindex; /* we only support SCP on the fixed known primary socket */
+
+ /* libssh2_channel_read() returns int */
+ nread = (ssize_t) libssh2_channel_read(sshc->ssh_channel, mem, len);
+
+ ssh_block2waitfor(data, (nread == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
+ if(nread == LIBSSH2_ERROR_EAGAIN) {
+ *err = CURLE_AGAIN;
+ nread = -1;
+ }
+
+ return nread;
+}
+
+/*
+ * =============== SFTP ===============
+ */
+
+/*
+ ***********************************************************************
+ *
+ * sftp_perform()
+ *
+ * This is the actual DO function for SFTP. Get a file/directory according to
+ * the options previously setup.
+ */
+
+static
+CURLcode sftp_perform(struct Curl_easy *data,
+ bool *connected,
+ bool *dophase_done)
+{
+ CURLcode result = CURLE_OK;
+
+ DEBUGF(infof(data, "DO phase starts"));
+
+ *dophase_done = FALSE; /* not done yet */
+
+ /* start the first command in the DO phase */
+ state(data, SSH_SFTP_QUOTE_INIT);
+
+ /* run the state-machine */
+ result = ssh_multi_statemach(data, dophase_done);
+
+ *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
+
+ if(*dophase_done) {
+ DEBUGF(infof(data, "DO phase is complete"));
+ }
+
+ return result;
+}
+
+/* called from multi.c while DOing */
+static CURLcode sftp_doing(struct Curl_easy *data,
+ bool *dophase_done)
+{
+ CURLcode result = ssh_multi_statemach(data, dophase_done);
+
+ if(*dophase_done) {
+ DEBUGF(infof(data, "DO phase is complete"));
+ }
+ return result;
+}
+
+/* BLOCKING, but the function is using the state machine so the only reason
+ this is still blocking is that the multi interface code has no support for
+ disconnecting operations that takes a while */
+static CURLcode sftp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn, bool dead_connection)
+{
+ CURLcode result = CURLE_OK;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ (void) dead_connection;
+
+ DEBUGF(infof(data, "SSH DISCONNECT starts now"));
+
+ if(sshc->ssh_session) {
+ /* only if there's a session still around to use! */
+ state(data, SSH_SFTP_SHUTDOWN);
+ result = ssh_block_statemach(data, conn, TRUE);
+ }
+
+ DEBUGF(infof(data, "SSH DISCONNECT is done"));
+
+ return result;
+
+}
+
+static CURLcode sftp_done(struct Curl_easy *data, CURLcode status,
+ bool premature)
+{
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+
+ if(!status) {
+ /* Post quote commands are executed after the SFTP_CLOSE state to avoid
+ errors that could happen due to open file handles during POSTQUOTE
+ operation */
+ if(!premature && data->set.postquote && !conn->bits.retry)
+ sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT;
+ state(data, SSH_SFTP_CLOSE);
+ }
+ return ssh_done(data, status);
+}
+
+/* return number of sent bytes */
+static ssize_t sftp_send(struct Curl_easy *data, int sockindex,
+ const void *mem, size_t len, CURLcode *err)
+{
+ ssize_t nwrite;
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ (void)sockindex;
+
+ nwrite = libssh2_sftp_write(sshc->sftp_handle, mem, len);
+
+ ssh_block2waitfor(data, (nwrite == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
+
+ if(nwrite == LIBSSH2_ERROR_EAGAIN) {
+ *err = CURLE_AGAIN;
+ nwrite = 0;
+ }
+ else if(nwrite < LIBSSH2_ERROR_NONE) {
+ *err = libssh2_session_error_to_CURLE((int)nwrite);
+ nwrite = -1;
+ }
+
+ return nwrite;
+}
+
+/*
+ * Return number of received (decrypted) bytes
+ * or <0 on error
+ */
+static ssize_t sftp_recv(struct Curl_easy *data, int sockindex,
+ char *mem, size_t len, CURLcode *err)
+{
+ ssize_t nread;
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ (void)sockindex;
+
+ nread = libssh2_sftp_read(sshc->sftp_handle, mem, len);
+
+ ssh_block2waitfor(data, (nread == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
+
+ if(nread == LIBSSH2_ERROR_EAGAIN) {
+ *err = CURLE_AGAIN;
+ nread = -1;
+
+ }
+ else if(nread < 0) {
+ *err = libssh2_session_error_to_CURLE((int)nread);
+ }
+ return nread;
+}
+
+static const char *sftp_libssh2_strerror(unsigned long err)
+{
+ switch(err) {
+ case LIBSSH2_FX_NO_SUCH_FILE:
+ return "No such file or directory";
+
+ case LIBSSH2_FX_PERMISSION_DENIED:
+ return "Permission denied";
+
+ case LIBSSH2_FX_FAILURE:
+ return "Operation failed";
+
+ case LIBSSH2_FX_BAD_MESSAGE:
+ return "Bad message from SFTP server";
+
+ case LIBSSH2_FX_NO_CONNECTION:
+ return "Not connected to SFTP server";
+
+ case LIBSSH2_FX_CONNECTION_LOST:
+ return "Connection to SFTP server lost";
+
+ case LIBSSH2_FX_OP_UNSUPPORTED:
+ return "Operation not supported by SFTP server";
+
+ case LIBSSH2_FX_INVALID_HANDLE:
+ return "Invalid handle";
+
+ case LIBSSH2_FX_NO_SUCH_PATH:
+ return "No such file or directory";
+
+ case LIBSSH2_FX_FILE_ALREADY_EXISTS:
+ return "File already exists";
+
+ case LIBSSH2_FX_WRITE_PROTECT:
+ return "File is write protected";
+
+ case LIBSSH2_FX_NO_MEDIA:
+ return "No media";
+
+ case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM:
+ return "Disk full";
+
+ case LIBSSH2_FX_QUOTA_EXCEEDED:
+ return "User quota exceeded";
+
+ case LIBSSH2_FX_UNKNOWN_PRINCIPLE:
+ return "Unknown principle";
+
+ case LIBSSH2_FX_LOCK_CONFlICT:
+ return "File lock conflict";
+
+ case LIBSSH2_FX_DIR_NOT_EMPTY:
+ return "Directory not empty";
+
+ case LIBSSH2_FX_NOT_A_DIRECTORY:
+ return "Not a directory";
+
+ case LIBSSH2_FX_INVALID_FILENAME:
+ return "Invalid filename";
+
+ case LIBSSH2_FX_LINK_LOOP:
+ return "Link points to itself";
+ }
+ return "Unknown error in libssh2";
+}
+
+CURLcode Curl_ssh_init(void)
+{
+#ifdef HAVE_LIBSSH2_INIT
+ if(libssh2_init(0)) {
+ DEBUGF(fprintf(stderr, "Error: libssh2_init failed\n"));
+ return CURLE_FAILED_INIT;
+ }
+#endif
+ return CURLE_OK;
+}
+
+void Curl_ssh_cleanup(void)
+{
+#ifdef HAVE_LIBSSH2_EXIT
+ (void)libssh2_exit();
+#endif
+}
+
+void Curl_ssh_version(char *buffer, size_t buflen)
+{
+ (void)msnprintf(buffer, buflen, "libssh2/%s", CURL_LIBSSH2_VERSION);
+}
+
+/* The SSH session is associated with the *CONNECTION* but the callback user
+ * pointer is an easy handle pointer. This function allows us to reassign the
+ * user pointer to the *CURRENT* (new) easy handle.
+ */
+static void ssh_attach(struct Curl_easy *data, struct connectdata *conn)
+{
+ DEBUGASSERT(data);
+ DEBUGASSERT(conn);
+ if(conn->handler->protocol & PROTO_FAMILY_SSH) {
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ if(sshc->ssh_session) {
+ /* only re-attach if the session already exists */
+ void **abstract = libssh2_session_abstract(sshc->ssh_session);
+ *abstract = data;
+ }
+ }
+}
+#endif /* USE_LIBSSH2 */
diff --git a/libs/libcurl/src/vssh/ssh.h b/libs/libcurl/src/vssh/ssh.h index 13bb8aa2d6..9e1f805f2e 100644 --- a/libs/libcurl/src/vssh/ssh.h +++ b/libs/libcurl/src/vssh/ssh.h @@ -1,272 +1,272 @@ -#ifndef HEADER_CURL_SSH_H -#define HEADER_CURL_SSH_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_LIBSSH2) -#include <libssh2.h> -#include <libssh2_sftp.h> -#elif defined(USE_LIBSSH) -#include <libssh/libssh.h> -#include <libssh/sftp.h> -#elif defined(USE_WOLFSSH) -#include <wolfssh/ssh.h> -#include <wolfssh/wolfsftp.h> -#endif - -/**************************************************************************** - * SSH unique setup - ***************************************************************************/ -typedef enum { - SSH_NO_STATE = -1, /* Used for "nextState" so say there is none */ - SSH_STOP = 0, /* do nothing state, stops the state machine */ - - SSH_INIT, /* First state in SSH-CONNECT */ - SSH_S_STARTUP, /* Session startup */ - SSH_HOSTKEY, /* verify hostkey */ - SSH_AUTHLIST, - SSH_AUTH_PKEY_INIT, - SSH_AUTH_PKEY, - SSH_AUTH_PASS_INIT, - SSH_AUTH_PASS, - SSH_AUTH_AGENT_INIT, /* initialize then wait for connection to agent */ - SSH_AUTH_AGENT_LIST, /* ask for list then wait for entire list to come */ - SSH_AUTH_AGENT, /* attempt one key at a time */ - SSH_AUTH_HOST_INIT, - SSH_AUTH_HOST, - SSH_AUTH_KEY_INIT, - SSH_AUTH_KEY, - SSH_AUTH_GSSAPI, - SSH_AUTH_DONE, - SSH_SFTP_INIT, - SSH_SFTP_REALPATH, /* Last state in SSH-CONNECT */ - - SSH_SFTP_QUOTE_INIT, /* First state in SFTP-DO */ - SSH_SFTP_POSTQUOTE_INIT, /* (Possibly) First state in SFTP-DONE */ - SSH_SFTP_QUOTE, - SSH_SFTP_NEXT_QUOTE, - SSH_SFTP_QUOTE_STAT, - SSH_SFTP_QUOTE_SETSTAT, - SSH_SFTP_QUOTE_SYMLINK, - SSH_SFTP_QUOTE_MKDIR, - SSH_SFTP_QUOTE_RENAME, - SSH_SFTP_QUOTE_RMDIR, - SSH_SFTP_QUOTE_UNLINK, - SSH_SFTP_QUOTE_STATVFS, - SSH_SFTP_GETINFO, - SSH_SFTP_FILETIME, - SSH_SFTP_TRANS_INIT, - SSH_SFTP_UPLOAD_INIT, - SSH_SFTP_CREATE_DIRS_INIT, - SSH_SFTP_CREATE_DIRS, - SSH_SFTP_CREATE_DIRS_MKDIR, - SSH_SFTP_READDIR_INIT, - SSH_SFTP_READDIR, - SSH_SFTP_READDIR_LINK, - SSH_SFTP_READDIR_BOTTOM, - SSH_SFTP_READDIR_DONE, - SSH_SFTP_DOWNLOAD_INIT, - SSH_SFTP_DOWNLOAD_STAT, /* Last state in SFTP-DO */ - SSH_SFTP_CLOSE, /* Last state in SFTP-DONE */ - SSH_SFTP_SHUTDOWN, /* First state in SFTP-DISCONNECT */ - SSH_SCP_TRANS_INIT, /* First state in SCP-DO */ - SSH_SCP_UPLOAD_INIT, - SSH_SCP_DOWNLOAD_INIT, - SSH_SCP_DOWNLOAD, - SSH_SCP_DONE, - SSH_SCP_SEND_EOF, - SSH_SCP_WAIT_EOF, - SSH_SCP_WAIT_CLOSE, - SSH_SCP_CHANNEL_FREE, /* Last state in SCP-DONE */ - SSH_SESSION_DISCONNECT, /* First state in SCP-DISCONNECT */ - SSH_SESSION_FREE, /* Last state in SCP/SFTP-DISCONNECT */ - SSH_QUIT, - SSH_LAST /* never used */ -} sshstate; - -/* this struct is used in the HandleData struct which is part of the - Curl_easy, which means this is used on a per-easy handle basis. - Everything that is strictly related to a connection is banned from this - struct. */ -struct SSHPROTO { - char *path; /* the path we operate on */ -#ifdef USE_LIBSSH2 - struct dynbuf readdir_link; - struct dynbuf readdir; - char *readdir_filename; - char *readdir_longentry; - - LIBSSH2_SFTP_ATTRIBUTES quote_attrs; /* used by the SFTP_QUOTE state */ - - /* Here's a set of struct members used by the SFTP_READDIR state */ - LIBSSH2_SFTP_ATTRIBUTES readdir_attrs; -#endif -}; - -/* ssh_conn is used for struct connection-oriented data in the connectdata - struct */ -struct ssh_conn { - const char *authlist; /* List of auth. methods, managed by libssh2 */ - - /* common */ - const char *passphrase; /* pass-phrase to use */ - char *rsa_pub; /* strdup'ed public key file */ - char *rsa; /* strdup'ed private key file */ - bool authed; /* the connection has been authenticated fine */ - bool acceptfail; /* used by the SFTP_QUOTE (continue if - quote command fails) */ - sshstate state; /* always use ssh.c:state() to change state! */ - sshstate nextstate; /* the state to goto after stopping */ - CURLcode actualcode; /* the actual error code */ - struct curl_slist *quote_item; /* for the quote option */ - char *quote_path1; /* two generic pointers for the QUOTE stuff */ - char *quote_path2; - - char *homedir; /* when doing SFTP we figure out home dir in the - connect phase */ - char *readdir_line; - /* end of READDIR stuff */ - - int secondCreateDirs; /* counter use by the code to see if the - second attempt has been made to change - to/create a directory */ - int orig_waitfor; /* default READ/WRITE bits wait for */ - char *slash_pos; /* used by the SFTP_CREATE_DIRS state */ - -#if defined(USE_LIBSSH) - char *readdir_linkPath; - size_t readdir_len, readdir_totalLen, readdir_currLen; -/* our variables */ - unsigned kbd_state; /* 0 or 1 */ - ssh_key privkey; - ssh_key pubkey; - int auth_methods; - ssh_session ssh_session; - ssh_scp scp_session; - sftp_session sftp_session; - sftp_file sftp_file; - sftp_dir sftp_dir; - - unsigned sftp_recv_state; /* 0 or 1 */ - int sftp_file_index; /* for async read */ - sftp_attributes readdir_attrs; /* used by the SFTP readdir actions */ - sftp_attributes readdir_link_attrs; /* used by the SFTP readdir actions */ - sftp_attributes quote_attrs; /* used by the SFTP_QUOTE state */ - - const char *readdir_filename; /* points within readdir_attrs */ - const char *readdir_longentry; - char *readdir_tmp; -#elif defined(USE_LIBSSH2) - LIBSSH2_SESSION *ssh_session; /* Secure Shell session */ - LIBSSH2_CHANNEL *ssh_channel; /* Secure Shell channel handle */ - LIBSSH2_SFTP *sftp_session; /* SFTP handle */ - LIBSSH2_SFTP_HANDLE *sftp_handle; - -#ifndef CURL_DISABLE_PROXY - /* for HTTPS proxy storage */ - Curl_recv *tls_recv; - Curl_send *tls_send; -#endif - -#ifdef HAVE_LIBSSH2_AGENT_API - LIBSSH2_AGENT *ssh_agent; /* proxy to ssh-agent/pageant */ - struct libssh2_agent_publickey *sshagent_identity, - *sshagent_prev_identity; -#endif - - /* note that HAVE_LIBSSH2_KNOWNHOST_API is a define set in the libssh2.h - header */ -#ifdef HAVE_LIBSSH2_KNOWNHOST_API - LIBSSH2_KNOWNHOSTS *kh; -#endif -#elif defined(USE_WOLFSSH) - WOLFSSH *ssh_session; - WOLFSSH_CTX *ctx; - word32 handleSz; - byte handle[WOLFSSH_MAX_HANDLE]; - curl_off_t offset; -#endif /* USE_LIBSSH */ -}; - -#if defined(USE_LIBSSH2) - -/* Feature detection based on version numbers to better work with - non-configure platforms */ - -#if !defined(LIBSSH2_VERSION_NUM) || (LIBSSH2_VERSION_NUM < 0x001000) -# error "SCP/SFTP protocols require libssh2 0.16 or later" -#endif - -#if LIBSSH2_VERSION_NUM >= 0x010000 -#define HAVE_LIBSSH2_SFTP_SEEK64 1 -#endif - -#if LIBSSH2_VERSION_NUM >= 0x010100 -#define HAVE_LIBSSH2_VERSION 1 -#endif - -#if LIBSSH2_VERSION_NUM >= 0x010205 -#define HAVE_LIBSSH2_INIT 1 -#define HAVE_LIBSSH2_EXIT 1 -#endif - -#if LIBSSH2_VERSION_NUM >= 0x010206 -#define HAVE_LIBSSH2_KNOWNHOST_CHECKP 1 -#define HAVE_LIBSSH2_SCP_SEND64 1 -#endif - -#if LIBSSH2_VERSION_NUM >= 0x010208 -#define HAVE_LIBSSH2_SESSION_HANDSHAKE 1 -#endif - -#ifdef HAVE_LIBSSH2_VERSION -/* get it run-time if possible */ -#define CURL_LIBSSH2_VERSION libssh2_version(0) -#else -/* use build-time if run-time not possible */ -#define CURL_LIBSSH2_VERSION LIBSSH2_VERSION -#endif - -#endif /* USE_LIBSSH2 */ - -#ifdef USE_SSH - -extern const struct Curl_handler Curl_handler_scp; -extern const struct Curl_handler Curl_handler_sftp; - -/* generic SSH backend functions */ -CURLcode Curl_ssh_init(void); -void Curl_ssh_cleanup(void); -void Curl_ssh_version(char *buffer, size_t buflen); -void Curl_ssh_attach(struct Curl_easy *data, - struct connectdata *conn); -#else -/* for non-SSH builds */ -#define Curl_ssh_cleanup() -#define Curl_ssh_attach(x,y) -#endif - -#endif /* HEADER_CURL_SSH_H */ +#ifndef HEADER_CURL_SSH_H
+#define HEADER_CURL_SSH_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_LIBSSH2)
+#include <libssh2.h>
+#include <libssh2_sftp.h>
+#elif defined(USE_LIBSSH)
+#include <libssh/libssh.h>
+#include <libssh/sftp.h>
+#elif defined(USE_WOLFSSH)
+#include <wolfssh/ssh.h>
+#include <wolfssh/wolfsftp.h>
+#endif
+
+/****************************************************************************
+ * SSH unique setup
+ ***************************************************************************/
+typedef enum {
+ SSH_NO_STATE = -1, /* Used for "nextState" so say there is none */
+ SSH_STOP = 0, /* do nothing state, stops the state machine */
+
+ SSH_INIT, /* First state in SSH-CONNECT */
+ SSH_S_STARTUP, /* Session startup */
+ SSH_HOSTKEY, /* verify hostkey */
+ SSH_AUTHLIST,
+ SSH_AUTH_PKEY_INIT,
+ SSH_AUTH_PKEY,
+ SSH_AUTH_PASS_INIT,
+ SSH_AUTH_PASS,
+ SSH_AUTH_AGENT_INIT, /* initialize then wait for connection to agent */
+ SSH_AUTH_AGENT_LIST, /* ask for list then wait for entire list to come */
+ SSH_AUTH_AGENT, /* attempt one key at a time */
+ SSH_AUTH_HOST_INIT,
+ SSH_AUTH_HOST,
+ SSH_AUTH_KEY_INIT,
+ SSH_AUTH_KEY,
+ SSH_AUTH_GSSAPI,
+ SSH_AUTH_DONE,
+ SSH_SFTP_INIT,
+ SSH_SFTP_REALPATH, /* Last state in SSH-CONNECT */
+
+ SSH_SFTP_QUOTE_INIT, /* First state in SFTP-DO */
+ SSH_SFTP_POSTQUOTE_INIT, /* (Possibly) First state in SFTP-DONE */
+ SSH_SFTP_QUOTE,
+ SSH_SFTP_NEXT_QUOTE,
+ SSH_SFTP_QUOTE_STAT,
+ SSH_SFTP_QUOTE_SETSTAT,
+ SSH_SFTP_QUOTE_SYMLINK,
+ SSH_SFTP_QUOTE_MKDIR,
+ SSH_SFTP_QUOTE_RENAME,
+ SSH_SFTP_QUOTE_RMDIR,
+ SSH_SFTP_QUOTE_UNLINK,
+ SSH_SFTP_QUOTE_STATVFS,
+ SSH_SFTP_GETINFO,
+ SSH_SFTP_FILETIME,
+ SSH_SFTP_TRANS_INIT,
+ SSH_SFTP_UPLOAD_INIT,
+ SSH_SFTP_CREATE_DIRS_INIT,
+ SSH_SFTP_CREATE_DIRS,
+ SSH_SFTP_CREATE_DIRS_MKDIR,
+ SSH_SFTP_READDIR_INIT,
+ SSH_SFTP_READDIR,
+ SSH_SFTP_READDIR_LINK,
+ SSH_SFTP_READDIR_BOTTOM,
+ SSH_SFTP_READDIR_DONE,
+ SSH_SFTP_DOWNLOAD_INIT,
+ SSH_SFTP_DOWNLOAD_STAT, /* Last state in SFTP-DO */
+ SSH_SFTP_CLOSE, /* Last state in SFTP-DONE */
+ SSH_SFTP_SHUTDOWN, /* First state in SFTP-DISCONNECT */
+ SSH_SCP_TRANS_INIT, /* First state in SCP-DO */
+ SSH_SCP_UPLOAD_INIT,
+ SSH_SCP_DOWNLOAD_INIT,
+ SSH_SCP_DOWNLOAD,
+ SSH_SCP_DONE,
+ SSH_SCP_SEND_EOF,
+ SSH_SCP_WAIT_EOF,
+ SSH_SCP_WAIT_CLOSE,
+ SSH_SCP_CHANNEL_FREE, /* Last state in SCP-DONE */
+ SSH_SESSION_DISCONNECT, /* First state in SCP-DISCONNECT */
+ SSH_SESSION_FREE, /* Last state in SCP/SFTP-DISCONNECT */
+ SSH_QUIT,
+ SSH_LAST /* never used */
+} sshstate;
+
+/* this struct is used in the HandleData struct which is part of the
+ Curl_easy, which means this is used on a per-easy handle basis.
+ Everything that is strictly related to a connection is banned from this
+ struct. */
+struct SSHPROTO {
+ char *path; /* the path we operate on */
+#ifdef USE_LIBSSH2
+ struct dynbuf readdir_link;
+ struct dynbuf readdir;
+ char *readdir_filename;
+ char *readdir_longentry;
+
+ LIBSSH2_SFTP_ATTRIBUTES quote_attrs; /* used by the SFTP_QUOTE state */
+
+ /* Here's a set of struct members used by the SFTP_READDIR state */
+ LIBSSH2_SFTP_ATTRIBUTES readdir_attrs;
+#endif
+};
+
+/* ssh_conn is used for struct connection-oriented data in the connectdata
+ struct */
+struct ssh_conn {
+ const char *authlist; /* List of auth. methods, managed by libssh2 */
+
+ /* common */
+ const char *passphrase; /* pass-phrase to use */
+ char *rsa_pub; /* strdup'ed public key file */
+ char *rsa; /* strdup'ed private key file */
+ bool authed; /* the connection has been authenticated fine */
+ bool acceptfail; /* used by the SFTP_QUOTE (continue if
+ quote command fails) */
+ sshstate state; /* always use ssh.c:state() to change state! */
+ sshstate nextstate; /* the state to goto after stopping */
+ CURLcode actualcode; /* the actual error code */
+ struct curl_slist *quote_item; /* for the quote option */
+ char *quote_path1; /* two generic pointers for the QUOTE stuff */
+ char *quote_path2;
+
+ char *homedir; /* when doing SFTP we figure out home dir in the
+ connect phase */
+ char *readdir_line;
+ /* end of READDIR stuff */
+
+ int secondCreateDirs; /* counter use by the code to see if the
+ second attempt has been made to change
+ to/create a directory */
+ int orig_waitfor; /* default READ/WRITE bits wait for */
+ char *slash_pos; /* used by the SFTP_CREATE_DIRS state */
+
+#if defined(USE_LIBSSH)
+ char *readdir_linkPath;
+ size_t readdir_len, readdir_totalLen, readdir_currLen;
+/* our variables */
+ unsigned kbd_state; /* 0 or 1 */
+ ssh_key privkey;
+ ssh_key pubkey;
+ int auth_methods;
+ ssh_session ssh_session;
+ ssh_scp scp_session;
+ sftp_session sftp_session;
+ sftp_file sftp_file;
+ sftp_dir sftp_dir;
+
+ unsigned sftp_recv_state; /* 0 or 1 */
+ int sftp_file_index; /* for async read */
+ sftp_attributes readdir_attrs; /* used by the SFTP readdir actions */
+ sftp_attributes readdir_link_attrs; /* used by the SFTP readdir actions */
+ sftp_attributes quote_attrs; /* used by the SFTP_QUOTE state */
+
+ const char *readdir_filename; /* points within readdir_attrs */
+ const char *readdir_longentry;
+ char *readdir_tmp;
+#elif defined(USE_LIBSSH2)
+ LIBSSH2_SESSION *ssh_session; /* Secure Shell session */
+ LIBSSH2_CHANNEL *ssh_channel; /* Secure Shell channel handle */
+ LIBSSH2_SFTP *sftp_session; /* SFTP handle */
+ LIBSSH2_SFTP_HANDLE *sftp_handle;
+
+#ifndef CURL_DISABLE_PROXY
+ /* for HTTPS proxy storage */
+ Curl_recv *tls_recv;
+ Curl_send *tls_send;
+#endif
+
+#ifdef HAVE_LIBSSH2_AGENT_API
+ LIBSSH2_AGENT *ssh_agent; /* proxy to ssh-agent/pageant */
+ struct libssh2_agent_publickey *sshagent_identity,
+ *sshagent_prev_identity;
+#endif
+
+ /* note that HAVE_LIBSSH2_KNOWNHOST_API is a define set in the libssh2.h
+ header */
+#ifdef HAVE_LIBSSH2_KNOWNHOST_API
+ LIBSSH2_KNOWNHOSTS *kh;
+#endif
+#elif defined(USE_WOLFSSH)
+ WOLFSSH *ssh_session;
+ WOLFSSH_CTX *ctx;
+ word32 handleSz;
+ byte handle[WOLFSSH_MAX_HANDLE];
+ curl_off_t offset;
+#endif /* USE_LIBSSH */
+};
+
+#if defined(USE_LIBSSH2)
+
+/* Feature detection based on version numbers to better work with
+ non-configure platforms */
+
+#if !defined(LIBSSH2_VERSION_NUM) || (LIBSSH2_VERSION_NUM < 0x001000)
+# error "SCP/SFTP protocols require libssh2 0.16 or later"
+#endif
+
+#if LIBSSH2_VERSION_NUM >= 0x010000
+#define HAVE_LIBSSH2_SFTP_SEEK64 1
+#endif
+
+#if LIBSSH2_VERSION_NUM >= 0x010100
+#define HAVE_LIBSSH2_VERSION 1
+#endif
+
+#if LIBSSH2_VERSION_NUM >= 0x010205
+#define HAVE_LIBSSH2_INIT 1
+#define HAVE_LIBSSH2_EXIT 1
+#endif
+
+#if LIBSSH2_VERSION_NUM >= 0x010206
+#define HAVE_LIBSSH2_KNOWNHOST_CHECKP 1
+#define HAVE_LIBSSH2_SCP_SEND64 1
+#endif
+
+#if LIBSSH2_VERSION_NUM >= 0x010208
+#define HAVE_LIBSSH2_SESSION_HANDSHAKE 1
+#endif
+
+#ifdef HAVE_LIBSSH2_VERSION
+/* get it run-time if possible */
+#define CURL_LIBSSH2_VERSION libssh2_version(0)
+#else
+/* use build-time if run-time not possible */
+#define CURL_LIBSSH2_VERSION LIBSSH2_VERSION
+#endif
+
+#endif /* USE_LIBSSH2 */
+
+#ifdef USE_SSH
+
+extern const struct Curl_handler Curl_handler_scp;
+extern const struct Curl_handler Curl_handler_sftp;
+
+/* generic SSH backend functions */
+CURLcode Curl_ssh_init(void);
+void Curl_ssh_cleanup(void);
+void Curl_ssh_version(char *buffer, size_t buflen);
+void Curl_ssh_attach(struct Curl_easy *data,
+ struct connectdata *conn);
+#else
+/* for non-SSH builds */
+#define Curl_ssh_cleanup()
+#define Curl_ssh_attach(x,y)
+#endif
+
+#endif /* HEADER_CURL_SSH_H */
diff --git a/libs/libcurl/src/vssh/wolfssh.c b/libs/libcurl/src/vssh/wolfssh.c index c2f85f3fe5..a84436f93b 100644 --- a/libs/libcurl/src/vssh/wolfssh.c +++ b/libs/libcurl/src/vssh/wolfssh.c @@ -1,1173 +1,1173 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2019 - 2022, 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_WOLFSSH - -#include <limits.h> - -#include <wolfssh/ssh.h> -#include <wolfssh/wolfsftp.h> -#include "urldata.h" -#include "connect.h" -#include "sendf.h" -#include "progress.h" -#include "curl_path.h" -#include "strtoofft.h" -#include "transfer.h" -#include "speedcheck.h" -#include "select.h" -#include "multiif.h" -#include "warnless.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -static CURLcode wssh_connect(struct Curl_easy *data, bool *done); -static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done); -static CURLcode wssh_do(struct Curl_easy *data, bool *done); -#if 0 -static CURLcode wscp_done(struct Curl_easy *data, - CURLcode, bool premature); -static CURLcode wscp_doing(struct Curl_easy *data, - bool *dophase_done); -static CURLcode wscp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection); -#endif -static CURLcode wsftp_done(struct Curl_easy *data, - CURLcode, bool premature); -static CURLcode wsftp_doing(struct Curl_easy *data, - bool *dophase_done); -static CURLcode wsftp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead); -static int wssh_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *sock); -static CURLcode wssh_setup_connection(struct Curl_easy *data, - struct connectdata *conn); - -#if 0 -/* - * SCP protocol handler. - */ - -const struct Curl_handler Curl_handler_scp = { - "SCP", /* scheme */ - wssh_setup_connection, /* setup_connection */ - wssh_do, /* do_it */ - wscp_done, /* done */ - ZERO_NULL, /* do_more */ - wssh_connect, /* connect_it */ - wssh_multi_statemach, /* connecting */ - wscp_doing, /* doing */ - wssh_getsock, /* proto_getsock */ - wssh_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - wssh_getsock, /* perform_getsock */ - wscp_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_SSH, /* defport */ - CURLPROTO_SCP, /* protocol */ - PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION - | PROTOPT_NOURLQUERY /* flags */ -}; - -#endif - -/* - * SFTP protocol handler. - */ - -const struct Curl_handler Curl_handler_sftp = { - "SFTP", /* scheme */ - wssh_setup_connection, /* setup_connection */ - wssh_do, /* do_it */ - wsftp_done, /* done */ - ZERO_NULL, /* do_more */ - wssh_connect, /* connect_it */ - wssh_multi_statemach, /* connecting */ - wsftp_doing, /* doing */ - wssh_getsock, /* proto_getsock */ - wssh_getsock, /* doing_getsock */ - ZERO_NULL, /* domore_getsock */ - wssh_getsock, /* perform_getsock */ - wsftp_disconnect, /* disconnect */ - ZERO_NULL, /* readwrite */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - PORT_SSH, /* defport */ - CURLPROTO_SFTP, /* protocol */ - CURLPROTO_SFTP, /* family */ - PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION - | PROTOPT_NOURLQUERY /* flags */ -}; - -/* - * SSH State machine related code - */ -/* This is the ONLY way to change SSH state! */ -static void state(struct Curl_easy *data, sshstate nowstate) -{ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - /* for debug purposes */ - static const char * const names[] = { - "SSH_STOP", - "SSH_INIT", - "SSH_S_STARTUP", - "SSH_HOSTKEY", - "SSH_AUTHLIST", - "SSH_AUTH_PKEY_INIT", - "SSH_AUTH_PKEY", - "SSH_AUTH_PASS_INIT", - "SSH_AUTH_PASS", - "SSH_AUTH_AGENT_INIT", - "SSH_AUTH_AGENT_LIST", - "SSH_AUTH_AGENT", - "SSH_AUTH_HOST_INIT", - "SSH_AUTH_HOST", - "SSH_AUTH_KEY_INIT", - "SSH_AUTH_KEY", - "SSH_AUTH_GSSAPI", - "SSH_AUTH_DONE", - "SSH_SFTP_INIT", - "SSH_SFTP_REALPATH", - "SSH_SFTP_QUOTE_INIT", - "SSH_SFTP_POSTQUOTE_INIT", - "SSH_SFTP_QUOTE", - "SSH_SFTP_NEXT_QUOTE", - "SSH_SFTP_QUOTE_STAT", - "SSH_SFTP_QUOTE_SETSTAT", - "SSH_SFTP_QUOTE_SYMLINK", - "SSH_SFTP_QUOTE_MKDIR", - "SSH_SFTP_QUOTE_RENAME", - "SSH_SFTP_QUOTE_RMDIR", - "SSH_SFTP_QUOTE_UNLINK", - "SSH_SFTP_QUOTE_STATVFS", - "SSH_SFTP_GETINFO", - "SSH_SFTP_FILETIME", - "SSH_SFTP_TRANS_INIT", - "SSH_SFTP_UPLOAD_INIT", - "SSH_SFTP_CREATE_DIRS_INIT", - "SSH_SFTP_CREATE_DIRS", - "SSH_SFTP_CREATE_DIRS_MKDIR", - "SSH_SFTP_READDIR_INIT", - "SSH_SFTP_READDIR", - "SSH_SFTP_READDIR_LINK", - "SSH_SFTP_READDIR_BOTTOM", - "SSH_SFTP_READDIR_DONE", - "SSH_SFTP_DOWNLOAD_INIT", - "SSH_SFTP_DOWNLOAD_STAT", - "SSH_SFTP_CLOSE", - "SSH_SFTP_SHUTDOWN", - "SSH_SCP_TRANS_INIT", - "SSH_SCP_UPLOAD_INIT", - "SSH_SCP_DOWNLOAD_INIT", - "SSH_SCP_DOWNLOAD", - "SSH_SCP_DONE", - "SSH_SCP_SEND_EOF", - "SSH_SCP_WAIT_EOF", - "SSH_SCP_WAIT_CLOSE", - "SSH_SCP_CHANNEL_FREE", - "SSH_SESSION_DISCONNECT", - "SSH_SESSION_FREE", - "QUIT" - }; - - /* a precaution to make sure the lists are in sync */ - DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST); - - if(sshc->state != nowstate) { - infof(data, "wolfssh %p state change from %s to %s", - (void *)sshc, names[sshc->state], names[nowstate]); - } -#endif - - sshc->state = nowstate; -} - -static ssize_t wscp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, CURLcode *err) -{ - ssize_t nwrite = 0; - (void)data; - (void)sockindex; /* we only support SCP on the fixed known primary socket */ - (void)mem; - (void)len; - (void)err; - - return nwrite; -} - -static ssize_t wscp_recv(struct Curl_easy *data, int sockindex, - char *mem, size_t len, CURLcode *err) -{ - ssize_t nread = 0; - (void)data; - (void)sockindex; /* we only support SCP on the fixed known primary socket */ - (void)mem; - (void)len; - (void)err; - - return nread; -} - -/* return number of sent bytes */ -static ssize_t wsftp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, CURLcode *err) -{ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - word32 offset[2]; - int rc; - (void)sockindex; - - offset[0] = (word32)sshc->offset&0xFFFFFFFF; - offset[1] = (word32)(sshc->offset>>32)&0xFFFFFFFF; - - rc = wolfSSH_SFTP_SendWritePacket(sshc->ssh_session, sshc->handle, - sshc->handleSz, - &offset[0], - (byte *)mem, (word32)len); - - if(rc == WS_FATAL_ERROR) - rc = wolfSSH_get_error(sshc->ssh_session); - if(rc == WS_WANT_READ) { - conn->waitfor = KEEP_RECV; - *err = CURLE_AGAIN; - return -1; - } - else if(rc == WS_WANT_WRITE) { - conn->waitfor = KEEP_SEND; - *err = CURLE_AGAIN; - return -1; - } - if(rc < 0) { - failf(data, "wolfSSH_SFTP_SendWritePacket returned %d", rc); - return -1; - } - DEBUGASSERT(rc == (int)len); - infof(data, "sent %zd bytes SFTP from offset %zd", - len, sshc->offset); - sshc->offset += len; - return (ssize_t)rc; -} - -/* - * Return number of received (decrypted) bytes - * or <0 on error - */ -static ssize_t wsftp_recv(struct Curl_easy *data, int sockindex, - char *mem, size_t len, CURLcode *err) -{ - int rc; - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - word32 offset[2]; - (void)sockindex; - - offset[0] = (word32)sshc->offset&0xFFFFFFFF; - offset[1] = (word32)(sshc->offset>>32)&0xFFFFFFFF; - - rc = wolfSSH_SFTP_SendReadPacket(sshc->ssh_session, sshc->handle, - sshc->handleSz, - &offset[0], - (byte *)mem, (word32)len); - if(rc == WS_FATAL_ERROR) - rc = wolfSSH_get_error(sshc->ssh_session); - if(rc == WS_WANT_READ) { - conn->waitfor = KEEP_RECV; - *err = CURLE_AGAIN; - return -1; - } - else if(rc == WS_WANT_WRITE) { - conn->waitfor = KEEP_SEND; - *err = CURLE_AGAIN; - return -1; - } - - DEBUGASSERT(rc <= (int)len); - - if(rc < 0) { - failf(data, "wolfSSH_SFTP_SendReadPacket returned %d", rc); - return -1; - } - sshc->offset += len; - - return (ssize_t)rc; -} - -/* - * SSH setup and connection - */ -static CURLcode wssh_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - struct SSHPROTO *ssh; - (void)conn; - - data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO)); - if(!ssh) - return CURLE_OUT_OF_MEMORY; - - return CURLE_OK; -} - -static Curl_recv wscp_recv, wsftp_recv; -static Curl_send wscp_send, wsftp_send; - -static int userauth(byte authtype, - WS_UserAuthData* authdata, - void *ctx) -{ - struct Curl_easy *data = ctx; - DEBUGF(infof(data, "wolfssh callback: type %s", - authtype == WOLFSSH_USERAUTH_PASSWORD ? "PASSWORD" : - "PUBLICCKEY")); - if(authtype == WOLFSSH_USERAUTH_PASSWORD) { - authdata->sf.password.password = (byte *)data->conn->passwd; - authdata->sf.password.passwordSz = (word32) strlen(data->conn->passwd); - } - - return 0; -} - -static CURLcode wssh_connect(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; - int rc; - - /* initialize per-handle data if not already */ - if(!data->req.p.ssh) - wssh_setup_connection(data, conn); - - /* We default to persistent connections. We set this already in this connect - function to make the re-use checks properly be able to check this bit. */ - connkeep(conn, "SSH default"); - - if(conn->handler->protocol & CURLPROTO_SCP) { - conn->recv[FIRSTSOCKET] = wscp_recv; - conn->send[FIRSTSOCKET] = wscp_send; - } - else { - conn->recv[FIRSTSOCKET] = wsftp_recv; - conn->send[FIRSTSOCKET] = wsftp_send; - } - sshc = &conn->proto.sshc; - sshc->ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL); - if(!sshc->ctx) { - failf(data, "No wolfSSH context"); - goto error; - } - - sshc->ssh_session = wolfSSH_new(sshc->ctx); - if(!sshc->ssh_session) { - failf(data, "No wolfSSH session"); - goto error; - } - - rc = wolfSSH_SetUsername(sshc->ssh_session, conn->user); - if(rc != WS_SUCCESS) { - failf(data, "wolfSSH failed to set user name"); - goto error; - } - - /* set callback for authentication */ - wolfSSH_SetUserAuth(sshc->ctx, userauth); - wolfSSH_SetUserAuthCtx(sshc->ssh_session, data); - - rc = wolfSSH_set_fd(sshc->ssh_session, (int)sock); - if(rc) { - failf(data, "wolfSSH failed to set socket"); - goto error; - } - -#if 0 - wolfSSH_Debugging_ON(); -#endif - - *done = TRUE; - if(conn->handler->protocol & CURLPROTO_SCP) - state(data, SSH_INIT); - else - state(data, SSH_SFTP_INIT); - - return wssh_multi_statemach(data, done); - error: - wolfSSH_free(sshc->ssh_session); - wolfSSH_CTX_free(sshc->ctx); - return CURLE_FAILED_INIT; -} - -/* - * wssh_statemach_act() runs the SSH state machine as far as it can without - * blocking and without reaching the end. The data the pointer 'block' points - * to will be set to TRUE if the wolfssh function returns EAGAIN meaning it - * wants to be called again when the socket is ready - */ - -static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - struct SSHPROTO *sftp_scp = data->req.p.ssh; - WS_SFTPNAME *name; - int rc = 0; - *block = FALSE; /* we're not blocking by default */ - - do { - switch(sshc->state) { - case SSH_INIT: - state(data, SSH_S_STARTUP); - break; - - case SSH_S_STARTUP: - rc = wolfSSH_connect(sshc->ssh_session); - if(rc != WS_SUCCESS) - rc = wolfSSH_get_error(sshc->ssh_session); - if(rc == WS_WANT_READ) { - *block = TRUE; - conn->waitfor = KEEP_RECV; - return CURLE_OK; - } - else if(rc == WS_WANT_WRITE) { - *block = TRUE; - conn->waitfor = KEEP_SEND; - return CURLE_OK; - } - else if(rc != WS_SUCCESS) { - state(data, SSH_STOP); - return CURLE_SSH; - } - infof(data, "wolfssh connected"); - state(data, SSH_STOP); - break; - case SSH_STOP: - break; - - case SSH_SFTP_INIT: - rc = wolfSSH_SFTP_connect(sshc->ssh_session); - if(rc != WS_SUCCESS) - rc = wolfSSH_get_error(sshc->ssh_session); - if(rc == WS_WANT_READ) { - *block = TRUE; - conn->waitfor = KEEP_RECV; - return CURLE_OK; - } - else if(rc == WS_WANT_WRITE) { - *block = TRUE; - conn->waitfor = KEEP_SEND; - return CURLE_OK; - } - else if(rc == WS_SUCCESS) { - infof(data, "wolfssh SFTP connected"); - state(data, SSH_SFTP_REALPATH); - } - else { - failf(data, "wolfssh SFTP connect error %d", rc); - return CURLE_SSH; - } - break; - case SSH_SFTP_REALPATH: - name = wolfSSH_SFTP_RealPath(sshc->ssh_session, (char *)"."); - rc = wolfSSH_get_error(sshc->ssh_session); - if(rc == WS_WANT_READ) { - *block = TRUE; - conn->waitfor = KEEP_RECV; - return CURLE_OK; - } - else if(rc == WS_WANT_WRITE) { - *block = TRUE; - conn->waitfor = KEEP_SEND; - return CURLE_OK; - } - else if(name && (rc == WS_SUCCESS)) { - sshc->homedir = malloc(name->fSz + 1); - if(!sshc->homedir) { - sshc->actualcode = CURLE_OUT_OF_MEMORY; - } - else { - memcpy(sshc->homedir, name->fName, name->fSz); - sshc->homedir[name->fSz] = 0; - infof(data, "wolfssh SFTP realpath succeeded"); - } - wolfSSH_SFTPNAME_list_free(name); - state(data, SSH_STOP); - return CURLE_OK; - } - failf(data, "wolfssh SFTP realpath %d", rc); - return CURLE_SSH; - - case SSH_SFTP_QUOTE_INIT: - result = Curl_getworkingpath(data, sshc->homedir, &sftp_scp->path); - if(result) { - sshc->actualcode = result; - state(data, SSH_STOP); - break; - } - - if(data->set.quote) { - infof(data, "Sending quote commands"); - sshc->quote_item = data->set.quote; - state(data, SSH_SFTP_QUOTE); - } - else { - state(data, SSH_SFTP_GETINFO); - } - break; - case SSH_SFTP_GETINFO: - if(data->set.get_filetime) { - state(data, SSH_SFTP_FILETIME); - } - else { - state(data, SSH_SFTP_TRANS_INIT); - } - break; - case SSH_SFTP_TRANS_INIT: - if(data->set.upload) - state(data, SSH_SFTP_UPLOAD_INIT); - else { - if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/') - state(data, SSH_SFTP_READDIR_INIT); - else - state(data, SSH_SFTP_DOWNLOAD_INIT); - } - break; - case SSH_SFTP_UPLOAD_INIT: { - word32 flags; - WS_SFTP_FILEATRB createattrs; - if(data->state.resume_from) { - WS_SFTP_FILEATRB attrs; - if(data->state.resume_from < 0) { - rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path, - &attrs); - if(rc != WS_SUCCESS) - break; - - if(rc) { - data->state.resume_from = 0; - } - else { - curl_off_t size = ((curl_off_t)attrs.sz[1] << 32) | attrs.sz[0]; - if(size < 0) { - failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); - return CURLE_BAD_DOWNLOAD_RESUME; - } - data->state.resume_from = size; - } - } - } - - if(data->set.remote_append) - /* Try to open for append, but create if nonexisting */ - flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_APPEND; - else if(data->state.resume_from > 0) - /* If we have restart position then open for append */ - flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_APPEND; - else - /* Clear file before writing (normal behavior) */ - flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_TRUNC; - - memset(&createattrs, 0, sizeof(createattrs)); - createattrs.per = (word32)data->set.new_file_perms; - sshc->handleSz = sizeof(sshc->handle); - rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path, - flags, &createattrs, - sshc->handle, &sshc->handleSz); - if(rc == WS_FATAL_ERROR) - rc = wolfSSH_get_error(sshc->ssh_session); - if(rc == WS_WANT_READ) { - *block = TRUE; - conn->waitfor = KEEP_RECV; - return CURLE_OK; - } - else if(rc == WS_WANT_WRITE) { - *block = TRUE; - conn->waitfor = KEEP_SEND; - return CURLE_OK; - } - else if(rc == WS_SUCCESS) { - infof(data, "wolfssh SFTP open succeeded"); - } - else { - failf(data, "wolfssh SFTP upload open failed: %d", rc); - return CURLE_SSH; - } - state(data, SSH_SFTP_DOWNLOAD_STAT); - - /* If we have a restart point then we need to seek to the correct - position. */ - if(data->state.resume_from > 0) { - /* Let's read off the proper amount of bytes from the input. */ - int seekerr = CURL_SEEKFUNC_OK; - if(conn->seek_func) { - Curl_set_in_callback(data, true); - seekerr = conn->seek_func(conn->seek_client, data->state.resume_from, - SEEK_SET); - Curl_set_in_callback(data, false); - } - - if(seekerr != CURL_SEEKFUNC_OK) { - curl_off_t passed = 0; - - if(seekerr != CURL_SEEKFUNC_CANTSEEK) { - failf(data, "Could not seek stream"); - return CURLE_FTP_COULDNT_USE_REST; - } - /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */ - do { - size_t readthisamountnow = - (data->state.resume_from - passed > data->set.buffer_size) ? - (size_t)data->set.buffer_size : - curlx_sotouz(data->state.resume_from - passed); - - size_t actuallyread; - Curl_set_in_callback(data, true); - actuallyread = data->state.fread_func(data->state.buffer, 1, - readthisamountnow, - data->state.in); - Curl_set_in_callback(data, false); - - passed += actuallyread; - if((actuallyread == 0) || (actuallyread > readthisamountnow)) { - /* this checks for greater-than only to make sure that the - CURL_READFUNC_ABORT return code still aborts */ - failf(data, "Failed to read data"); - return CURLE_FTP_COULDNT_USE_REST; - } - } while(passed < data->state.resume_from); - } - - /* now, decrease the size of the read */ - if(data->state.infilesize > 0) { - data->state.infilesize -= data->state.resume_from; - data->req.size = data->state.infilesize; - Curl_pgrsSetUploadSize(data, data->state.infilesize); - } - - sshc->offset += data->state.resume_from; - } - if(data->state.infilesize > 0) { - data->req.size = data->state.infilesize; - Curl_pgrsSetUploadSize(data, data->state.infilesize); - } - /* upload data */ - Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); - - /* not set by Curl_setup_transfer to preserve keepon bits */ - conn->sockfd = conn->writesockfd; - - if(result) { - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = result; - } - else { - /* store this original bitmask setup to use later on if we can't - figure out a "real" bitmask */ - sshc->orig_waitfor = data->req.keepon; - - /* we want to use the _sending_ function even when the socket turns - out readable as the underlying libssh2 sftp send function will deal - with both accordingly */ - conn->cselect_bits = CURL_CSELECT_OUT; - - /* since we don't really wait for anything at this point, we want the - state machine to move on as soon as possible so we set a very short - timeout here */ - Curl_expire(data, 0, EXPIRE_RUN_NOW); - - state(data, SSH_STOP); - } - break; - } - case SSH_SFTP_DOWNLOAD_INIT: - sshc->handleSz = sizeof(sshc->handle); - rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path, - WOLFSSH_FXF_READ, NULL, - sshc->handle, &sshc->handleSz); - if(rc == WS_FATAL_ERROR) - rc = wolfSSH_get_error(sshc->ssh_session); - if(rc == WS_WANT_READ) { - *block = TRUE; - conn->waitfor = KEEP_RECV; - return CURLE_OK; - } - else if(rc == WS_WANT_WRITE) { - *block = TRUE; - conn->waitfor = KEEP_SEND; - return CURLE_OK; - } - else if(rc == WS_SUCCESS) { - infof(data, "wolfssh SFTP open succeeded"); - state(data, SSH_SFTP_DOWNLOAD_STAT); - return CURLE_OK; - } - - failf(data, "wolfssh SFTP open failed: %d", rc); - return CURLE_SSH; - - case SSH_SFTP_DOWNLOAD_STAT: { - WS_SFTP_FILEATRB attrs; - curl_off_t size; - - rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path, &attrs); - if(rc == WS_FATAL_ERROR) - rc = wolfSSH_get_error(sshc->ssh_session); - if(rc == WS_WANT_READ) { - *block = TRUE; - conn->waitfor = KEEP_RECV; - return CURLE_OK; - } - else if(rc == WS_WANT_WRITE) { - *block = TRUE; - conn->waitfor = KEEP_SEND; - return CURLE_OK; - } - else if(rc == WS_SUCCESS) { - infof(data, "wolfssh STAT succeeded"); - } - else { - failf(data, "wolfssh SFTP open failed: %d", rc); - data->req.size = -1; - data->req.maxdownload = -1; - Curl_pgrsSetDownloadSize(data, -1); - return CURLE_SSH; - } - - size = ((curl_off_t)attrs.sz[1] <<32) | attrs.sz[0]; - - data->req.size = size; - data->req.maxdownload = size; - Curl_pgrsSetDownloadSize(data, size); - - infof(data, "SFTP download %" CURL_FORMAT_CURL_OFF_T " bytes", size); - - /* We cannot seek with wolfSSH so resuming and range requests are not - possible */ - if(data->state.use_range || data->state.resume_from) { - infof(data, "wolfSSH cannot do range/seek on SFTP"); - return CURLE_BAD_DOWNLOAD_RESUME; - } - - /* Setup the actual download */ - if(data->req.size == 0) { - /* no data to transfer */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); - infof(data, "File already completely downloaded"); - state(data, SSH_STOP); - break; - } - Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1); - - /* not set by Curl_setup_transfer to preserve keepon bits */ - conn->writesockfd = conn->sockfd; - - /* we want to use the _receiving_ function even when the socket turns - out writableable as the underlying libssh2 recv function will deal - with both accordingly */ - conn->cselect_bits = CURL_CSELECT_IN; - - if(result) { - /* this should never occur; the close state should be entered - at the time the error occurs */ - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = result; - } - else { - state(data, SSH_STOP); - } - break; - } - case SSH_SFTP_CLOSE: - if(sshc->handleSz) - rc = wolfSSH_SFTP_Close(sshc->ssh_session, sshc->handle, - sshc->handleSz); - else - rc = WS_SUCCESS; /* directory listing */ - if(rc == WS_WANT_READ) { - *block = TRUE; - conn->waitfor = KEEP_RECV; - return CURLE_OK; - } - else if(rc == WS_WANT_WRITE) { - *block = TRUE; - conn->waitfor = KEEP_SEND; - return CURLE_OK; - } - else if(rc == WS_SUCCESS) { - state(data, SSH_STOP); - return CURLE_OK; - } - - failf(data, "wolfssh SFTP CLOSE failed: %d", rc); - return CURLE_SSH; - - case SSH_SFTP_READDIR_INIT: - Curl_pgrsSetDownloadSize(data, -1); - if(data->set.opt_no_body) { - state(data, SSH_STOP); - break; - } - state(data, SSH_SFTP_READDIR); - break; - - case SSH_SFTP_READDIR: - name = wolfSSH_SFTP_LS(sshc->ssh_session, sftp_scp->path); - if(!name) - rc = wolfSSH_get_error(sshc->ssh_session); - else - rc = WS_SUCCESS; - - if(rc == WS_WANT_READ) { - *block = TRUE; - conn->waitfor = KEEP_RECV; - return CURLE_OK; - } - else if(rc == WS_WANT_WRITE) { - *block = TRUE; - conn->waitfor = KEEP_SEND; - return CURLE_OK; - } - else if(name && (rc == WS_SUCCESS)) { - WS_SFTPNAME *origname = name; - result = CURLE_OK; - while(name) { - char *line = aprintf("%s\n", - data->set.list_only ? - name->fName : name->lName); - if(!line) { - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; - break; - } - result = Curl_client_write(data, CLIENTWRITE_BODY, - line, strlen(line)); - free(line); - if(result) { - sshc->actualcode = result; - break; - } - name = name->next; - } - wolfSSH_SFTPNAME_list_free(origname); - state(data, SSH_STOP); - return result; - } - failf(data, "wolfssh SFTP ls failed: %d", rc); - return CURLE_SSH; - - case SSH_SFTP_SHUTDOWN: - Curl_safefree(sshc->homedir); - wolfSSH_free(sshc->ssh_session); - wolfSSH_CTX_free(sshc->ctx); - state(data, SSH_STOP); - return CURLE_OK; - default: - break; - } - } while(!rc && (sshc->state != SSH_STOP)); - return result; -} - -/* called repeatedly until done from multi.c */ -static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - CURLcode result = CURLE_OK; - bool block; /* we store the status and use that to provide a ssh_getsock() - implementation */ - do { - result = wssh_statemach_act(data, &block); - *done = (sshc->state == SSH_STOP) ? TRUE : FALSE; - /* if there's no error, it isn't done and it didn't EWOULDBLOCK, then - try again */ - if(*done) { - DEBUGF(infof(data, "wssh_statemach_act says DONE")); - } - } while(!result && !*done && !block); - - return result; -} - -static -CURLcode wscp_perform(struct Curl_easy *data, - bool *connected, - bool *dophase_done) -{ - (void)data; - (void)connected; - (void)dophase_done; - return CURLE_OK; -} - -static -CURLcode wsftp_perform(struct Curl_easy *data, - bool *connected, - bool *dophase_done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - - DEBUGF(infof(data, "DO phase starts")); - - *dophase_done = FALSE; /* not done yet */ - - /* start the first command in the DO phase */ - state(data, SSH_SFTP_QUOTE_INIT); - - /* run the state-machine */ - result = wssh_multi_statemach(data, dophase_done); - - *connected = conn->bits.tcpconnect[FIRSTSOCKET]; - - if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); - } - - return result; -} - -/* - * The DO function is generic for both protocols. - */ -static CURLcode wssh_do(struct Curl_easy *data, bool *done) -{ - CURLcode result; - bool connected = 0; - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - - *done = FALSE; /* default to false */ - data->req.size = -1; /* make sure this is unknown at this point */ - sshc->actualcode = CURLE_OK; /* reset error code */ - sshc->secondCreateDirs = 0; /* reset the create dir attempt state - variable */ - - Curl_pgrsSetUploadCounter(data, 0); - Curl_pgrsSetDownloadCounter(data, 0); - Curl_pgrsSetUploadSize(data, -1); - Curl_pgrsSetDownloadSize(data, -1); - - if(conn->handler->protocol & CURLPROTO_SCP) - result = wscp_perform(data, &connected, done); - else - result = wsftp_perform(data, &connected, done); - - return result; -} - -static CURLcode wssh_block_statemach(struct Curl_easy *data, - bool disconnect) -{ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = &conn->proto.sshc; - CURLcode result = CURLE_OK; - - while((sshc->state != SSH_STOP) && !result) { - bool block; - timediff_t left = 1000; - struct curltime now = Curl_now(); - - result = wssh_statemach_act(data, &block); - if(result) - break; - - if(!disconnect) { - if(Curl_pgrsUpdate(data)) - return CURLE_ABORTED_BY_CALLBACK; - - result = Curl_speedcheck(data, now); - if(result) - break; - - left = Curl_timeleft(data, NULL, FALSE); - if(left < 0) { - failf(data, "Operation timed out"); - return CURLE_OPERATION_TIMEDOUT; - } - } - - if(!result) { - int dir = conn->waitfor; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; - curl_socket_t fd_read = CURL_SOCKET_BAD; - curl_socket_t fd_write = CURL_SOCKET_BAD; - if(dir == KEEP_RECV) - fd_read = sock; - else if(dir == KEEP_SEND) - fd_write = sock; - - /* wait for the socket to become ready */ - (void)Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write, - left>1000?1000:left); /* ignore result */ - } - } - - return result; -} - -/* generic done function for both SCP and SFTP called from their specific - done functions */ -static CURLcode wssh_done(struct Curl_easy *data, CURLcode status) -{ - CURLcode result = CURLE_OK; - struct SSHPROTO *sftp_scp = data->req.p.ssh; - - if(!status) { - /* run the state-machine */ - result = wssh_block_statemach(data, FALSE); - } - else - result = status; - - if(sftp_scp) - Curl_safefree(sftp_scp->path); - if(Curl_pgrsDone(data)) - return CURLE_ABORTED_BY_CALLBACK; - - data->req.keepon = 0; /* clear all bits */ - return result; -} - -#if 0 -static CURLcode wscp_done(struct Curl_easy *data, - CURLcode code, bool premature) -{ - CURLcode result = CURLE_OK; - (void)conn; - (void)code; - (void)premature; - - return result; -} - -static CURLcode wscp_doing(struct Curl_easy *data, - bool *dophase_done) -{ - CURLcode result = CURLE_OK; - (void)conn; - (void)dophase_done; - - return result; -} - -static CURLcode wscp_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead_connection) -{ - CURLcode result = CURLE_OK; - (void)data; - (void)conn; - (void)dead_connection; - - return result; -} -#endif - -static CURLcode wsftp_done(struct Curl_easy *data, - CURLcode code, bool premature) -{ - (void)premature; - state(data, SSH_SFTP_CLOSE); - - return wssh_done(data, code); -} - -static CURLcode wsftp_doing(struct Curl_easy *data, - bool *dophase_done) -{ - CURLcode result = wssh_multi_statemach(data, dophase_done); - - if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); - } - return result; -} - -static CURLcode wsftp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead) -{ - CURLcode result = CURLE_OK; - (void)dead; - - DEBUGF(infof(data, "SSH DISCONNECT starts now")); - - if(conn->proto.sshc.ssh_session) { - /* only if there's a session still around to use! */ - state(data, SSH_SFTP_SHUTDOWN); - result = wssh_block_statemach(data, TRUE); - } - - DEBUGF(infof(data, "SSH DISCONNECT is done")); - return result; -} - -static int wssh_getsock(struct Curl_easy *data, - struct connectdata *conn, - curl_socket_t *sock) -{ - int bitmap = GETSOCK_BLANK; - int dir = conn->waitfor; - (void)data; - sock[0] = conn->sock[FIRSTSOCKET]; - - if(dir == KEEP_RECV) - bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); - else if(dir == KEEP_SEND) - bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); - - return bitmap; -} - -void Curl_ssh_version(char *buffer, size_t buflen) -{ - (void)msnprintf(buffer, buflen, "wolfssh/%s", LIBWOLFSSH_VERSION_STRING); -} - -CURLcode Curl_ssh_init(void) -{ - if(WS_SUCCESS != wolfSSH_Init()) { - DEBUGF(fprintf(stderr, "Error: wolfSSH_Init failed\n")); - return CURLE_FAILED_INIT; - } - - return CURLE_OK; -} -void Curl_ssh_cleanup(void) -{ -} - -#endif /* USE_WOLFSSH */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2019 - 2022, 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_WOLFSSH
+
+#include <limits.h>
+
+#include <wolfssh/ssh.h>
+#include <wolfssh/wolfsftp.h>
+#include "urldata.h"
+#include "cfilters.h"
+#include "connect.h"
+#include "sendf.h"
+#include "progress.h"
+#include "curl_path.h"
+#include "strtoofft.h"
+#include "transfer.h"
+#include "speedcheck.h"
+#include "select.h"
+#include "multiif.h"
+#include "warnless.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+static CURLcode wssh_connect(struct Curl_easy *data, bool *done);
+static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done);
+static CURLcode wssh_do(struct Curl_easy *data, bool *done);
+#if 0
+static CURLcode wscp_done(struct Curl_easy *data,
+ CURLcode, bool premature);
+static CURLcode wscp_doing(struct Curl_easy *data,
+ bool *dophase_done);
+static CURLcode wscp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection);
+#endif
+static CURLcode wsftp_done(struct Curl_easy *data,
+ CURLcode, bool premature);
+static CURLcode wsftp_doing(struct Curl_easy *data,
+ bool *dophase_done);
+static CURLcode wsftp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead);
+static int wssh_getsock(struct Curl_easy *data,
+ struct connectdata *conn,
+ curl_socket_t *sock);
+static CURLcode wssh_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn);
+
+#if 0
+/*
+ * SCP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_scp = {
+ "SCP", /* scheme */
+ wssh_setup_connection, /* setup_connection */
+ wssh_do, /* do_it */
+ wscp_done, /* done */
+ ZERO_NULL, /* do_more */
+ wssh_connect, /* connect_it */
+ wssh_multi_statemach, /* connecting */
+ wscp_doing, /* doing */
+ wssh_getsock, /* proto_getsock */
+ wssh_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ wssh_getsock, /* perform_getsock */
+ wscp_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_SSH, /* defport */
+ CURLPROTO_SCP, /* protocol */
+ PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
+ | PROTOPT_NOURLQUERY /* flags */
+};
+
+#endif
+
+/*
+ * SFTP protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_sftp = {
+ "SFTP", /* scheme */
+ wssh_setup_connection, /* setup_connection */
+ wssh_do, /* do_it */
+ wsftp_done, /* done */
+ ZERO_NULL, /* do_more */
+ wssh_connect, /* connect_it */
+ wssh_multi_statemach, /* connecting */
+ wsftp_doing, /* doing */
+ wssh_getsock, /* proto_getsock */
+ wssh_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ wssh_getsock, /* perform_getsock */
+ wsftp_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ ZERO_NULL, /* attach connection */
+ PORT_SSH, /* defport */
+ CURLPROTO_SFTP, /* protocol */
+ CURLPROTO_SFTP, /* family */
+ PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
+ | PROTOPT_NOURLQUERY /* flags */
+};
+
+/*
+ * SSH State machine related code
+ */
+/* This is the ONLY way to change SSH state! */
+static void state(struct Curl_easy *data, sshstate nowstate)
+{
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ /* for debug purposes */
+ static const char * const names[] = {
+ "SSH_STOP",
+ "SSH_INIT",
+ "SSH_S_STARTUP",
+ "SSH_HOSTKEY",
+ "SSH_AUTHLIST",
+ "SSH_AUTH_PKEY_INIT",
+ "SSH_AUTH_PKEY",
+ "SSH_AUTH_PASS_INIT",
+ "SSH_AUTH_PASS",
+ "SSH_AUTH_AGENT_INIT",
+ "SSH_AUTH_AGENT_LIST",
+ "SSH_AUTH_AGENT",
+ "SSH_AUTH_HOST_INIT",
+ "SSH_AUTH_HOST",
+ "SSH_AUTH_KEY_INIT",
+ "SSH_AUTH_KEY",
+ "SSH_AUTH_GSSAPI",
+ "SSH_AUTH_DONE",
+ "SSH_SFTP_INIT",
+ "SSH_SFTP_REALPATH",
+ "SSH_SFTP_QUOTE_INIT",
+ "SSH_SFTP_POSTQUOTE_INIT",
+ "SSH_SFTP_QUOTE",
+ "SSH_SFTP_NEXT_QUOTE",
+ "SSH_SFTP_QUOTE_STAT",
+ "SSH_SFTP_QUOTE_SETSTAT",
+ "SSH_SFTP_QUOTE_SYMLINK",
+ "SSH_SFTP_QUOTE_MKDIR",
+ "SSH_SFTP_QUOTE_RENAME",
+ "SSH_SFTP_QUOTE_RMDIR",
+ "SSH_SFTP_QUOTE_UNLINK",
+ "SSH_SFTP_QUOTE_STATVFS",
+ "SSH_SFTP_GETINFO",
+ "SSH_SFTP_FILETIME",
+ "SSH_SFTP_TRANS_INIT",
+ "SSH_SFTP_UPLOAD_INIT",
+ "SSH_SFTP_CREATE_DIRS_INIT",
+ "SSH_SFTP_CREATE_DIRS",
+ "SSH_SFTP_CREATE_DIRS_MKDIR",
+ "SSH_SFTP_READDIR_INIT",
+ "SSH_SFTP_READDIR",
+ "SSH_SFTP_READDIR_LINK",
+ "SSH_SFTP_READDIR_BOTTOM",
+ "SSH_SFTP_READDIR_DONE",
+ "SSH_SFTP_DOWNLOAD_INIT",
+ "SSH_SFTP_DOWNLOAD_STAT",
+ "SSH_SFTP_CLOSE",
+ "SSH_SFTP_SHUTDOWN",
+ "SSH_SCP_TRANS_INIT",
+ "SSH_SCP_UPLOAD_INIT",
+ "SSH_SCP_DOWNLOAD_INIT",
+ "SSH_SCP_DOWNLOAD",
+ "SSH_SCP_DONE",
+ "SSH_SCP_SEND_EOF",
+ "SSH_SCP_WAIT_EOF",
+ "SSH_SCP_WAIT_CLOSE",
+ "SSH_SCP_CHANNEL_FREE",
+ "SSH_SESSION_DISCONNECT",
+ "SSH_SESSION_FREE",
+ "QUIT"
+ };
+
+ /* a precaution to make sure the lists are in sync */
+ DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST);
+
+ if(sshc->state != nowstate) {
+ infof(data, "wolfssh %p state change from %s to %s",
+ (void *)sshc, names[sshc->state], names[nowstate]);
+ }
+#endif
+
+ sshc->state = nowstate;
+}
+
+static ssize_t wscp_send(struct Curl_easy *data, int sockindex,
+ const void *mem, size_t len, CURLcode *err)
+{
+ ssize_t nwrite = 0;
+ (void)data;
+ (void)sockindex; /* we only support SCP on the fixed known primary socket */
+ (void)mem;
+ (void)len;
+ (void)err;
+
+ return nwrite;
+}
+
+static ssize_t wscp_recv(struct Curl_easy *data, int sockindex,
+ char *mem, size_t len, CURLcode *err)
+{
+ ssize_t nread = 0;
+ (void)data;
+ (void)sockindex; /* we only support SCP on the fixed known primary socket */
+ (void)mem;
+ (void)len;
+ (void)err;
+
+ return nread;
+}
+
+/* return number of sent bytes */
+static ssize_t wsftp_send(struct Curl_easy *data, int sockindex,
+ const void *mem, size_t len, CURLcode *err)
+{
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ word32 offset[2];
+ int rc;
+ (void)sockindex;
+
+ offset[0] = (word32)sshc->offset&0xFFFFFFFF;
+ offset[1] = (word32)(sshc->offset>>32)&0xFFFFFFFF;
+
+ rc = wolfSSH_SFTP_SendWritePacket(sshc->ssh_session, sshc->handle,
+ sshc->handleSz,
+ &offset[0],
+ (byte *)mem, (word32)len);
+
+ if(rc == WS_FATAL_ERROR)
+ rc = wolfSSH_get_error(sshc->ssh_session);
+ if(rc == WS_WANT_READ) {
+ conn->waitfor = KEEP_RECV;
+ *err = CURLE_AGAIN;
+ return -1;
+ }
+ else if(rc == WS_WANT_WRITE) {
+ conn->waitfor = KEEP_SEND;
+ *err = CURLE_AGAIN;
+ return -1;
+ }
+ if(rc < 0) {
+ failf(data, "wolfSSH_SFTP_SendWritePacket returned %d", rc);
+ return -1;
+ }
+ DEBUGASSERT(rc == (int)len);
+ infof(data, "sent %zd bytes SFTP from offset %zd",
+ len, sshc->offset);
+ sshc->offset += len;
+ return (ssize_t)rc;
+}
+
+/*
+ * Return number of received (decrypted) bytes
+ * or <0 on error
+ */
+static ssize_t wsftp_recv(struct Curl_easy *data, int sockindex,
+ char *mem, size_t len, CURLcode *err)
+{
+ int rc;
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ word32 offset[2];
+ (void)sockindex;
+
+ offset[0] = (word32)sshc->offset&0xFFFFFFFF;
+ offset[1] = (word32)(sshc->offset>>32)&0xFFFFFFFF;
+
+ rc = wolfSSH_SFTP_SendReadPacket(sshc->ssh_session, sshc->handle,
+ sshc->handleSz,
+ &offset[0],
+ (byte *)mem, (word32)len);
+ if(rc == WS_FATAL_ERROR)
+ rc = wolfSSH_get_error(sshc->ssh_session);
+ if(rc == WS_WANT_READ) {
+ conn->waitfor = KEEP_RECV;
+ *err = CURLE_AGAIN;
+ return -1;
+ }
+ else if(rc == WS_WANT_WRITE) {
+ conn->waitfor = KEEP_SEND;
+ *err = CURLE_AGAIN;
+ return -1;
+ }
+
+ DEBUGASSERT(rc <= (int)len);
+
+ if(rc < 0) {
+ failf(data, "wolfSSH_SFTP_SendReadPacket returned %d", rc);
+ return -1;
+ }
+ sshc->offset += len;
+
+ return (ssize_t)rc;
+}
+
+/*
+ * SSH setup and connection
+ */
+static CURLcode wssh_setup_connection(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ struct SSHPROTO *ssh;
+ (void)conn;
+
+ data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
+ if(!ssh)
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_OK;
+}
+
+static Curl_recv wscp_recv, wsftp_recv;
+static Curl_send wscp_send, wsftp_send;
+
+static int userauth(byte authtype,
+ WS_UserAuthData* authdata,
+ void *ctx)
+{
+ struct Curl_easy *data = ctx;
+ DEBUGF(infof(data, "wolfssh callback: type %s",
+ authtype == WOLFSSH_USERAUTH_PASSWORD ? "PASSWORD" :
+ "PUBLICCKEY"));
+ if(authtype == WOLFSSH_USERAUTH_PASSWORD) {
+ authdata->sf.password.password = (byte *)data->conn->passwd;
+ authdata->sf.password.passwordSz = (word32) strlen(data->conn->passwd);
+ }
+
+ return 0;
+}
+
+static CURLcode wssh_connect(struct Curl_easy *data, bool *done)
+{
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc;
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ int rc;
+
+ /* initialize per-handle data if not already */
+ if(!data->req.p.ssh)
+ wssh_setup_connection(data, conn);
+
+ /* We default to persistent connections. We set this already in this connect
+ function to make the re-use checks properly be able to check this bit. */
+ connkeep(conn, "SSH default");
+
+ if(conn->handler->protocol & CURLPROTO_SCP) {
+ conn->recv[FIRSTSOCKET] = wscp_recv;
+ conn->send[FIRSTSOCKET] = wscp_send;
+ }
+ else {
+ conn->recv[FIRSTSOCKET] = wsftp_recv;
+ conn->send[FIRSTSOCKET] = wsftp_send;
+ }
+ sshc = &conn->proto.sshc;
+ sshc->ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
+ if(!sshc->ctx) {
+ failf(data, "No wolfSSH context");
+ goto error;
+ }
+
+ sshc->ssh_session = wolfSSH_new(sshc->ctx);
+ if(!sshc->ssh_session) {
+ failf(data, "No wolfSSH session");
+ goto error;
+ }
+
+ rc = wolfSSH_SetUsername(sshc->ssh_session, conn->user);
+ if(rc != WS_SUCCESS) {
+ failf(data, "wolfSSH failed to set user name");
+ goto error;
+ }
+
+ /* set callback for authentication */
+ wolfSSH_SetUserAuth(sshc->ctx, userauth);
+ wolfSSH_SetUserAuthCtx(sshc->ssh_session, data);
+
+ rc = wolfSSH_set_fd(sshc->ssh_session, (int)sock);
+ if(rc) {
+ failf(data, "wolfSSH failed to set socket");
+ goto error;
+ }
+
+#if 0
+ wolfSSH_Debugging_ON();
+#endif
+
+ *done = TRUE;
+ if(conn->handler->protocol & CURLPROTO_SCP)
+ state(data, SSH_INIT);
+ else
+ state(data, SSH_SFTP_INIT);
+
+ return wssh_multi_statemach(data, done);
+ error:
+ wolfSSH_free(sshc->ssh_session);
+ wolfSSH_CTX_free(sshc->ctx);
+ return CURLE_FAILED_INIT;
+}
+
+/*
+ * wssh_statemach_act() runs the SSH state machine as far as it can without
+ * blocking and without reaching the end. The data the pointer 'block' points
+ * to will be set to TRUE if the wolfssh function returns EAGAIN meaning it
+ * wants to be called again when the socket is ready
+ */
+
+static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ struct SSHPROTO *sftp_scp = data->req.p.ssh;
+ WS_SFTPNAME *name;
+ int rc = 0;
+ *block = FALSE; /* we're not blocking by default */
+
+ do {
+ switch(sshc->state) {
+ case SSH_INIT:
+ state(data, SSH_S_STARTUP);
+ break;
+
+ case SSH_S_STARTUP:
+ rc = wolfSSH_connect(sshc->ssh_session);
+ if(rc != WS_SUCCESS)
+ rc = wolfSSH_get_error(sshc->ssh_session);
+ if(rc == WS_WANT_READ) {
+ *block = TRUE;
+ conn->waitfor = KEEP_RECV;
+ return CURLE_OK;
+ }
+ else if(rc == WS_WANT_WRITE) {
+ *block = TRUE;
+ conn->waitfor = KEEP_SEND;
+ return CURLE_OK;
+ }
+ else if(rc != WS_SUCCESS) {
+ state(data, SSH_STOP);
+ return CURLE_SSH;
+ }
+ infof(data, "wolfssh connected");
+ state(data, SSH_STOP);
+ break;
+ case SSH_STOP:
+ break;
+
+ case SSH_SFTP_INIT:
+ rc = wolfSSH_SFTP_connect(sshc->ssh_session);
+ if(rc != WS_SUCCESS)
+ rc = wolfSSH_get_error(sshc->ssh_session);
+ if(rc == WS_WANT_READ) {
+ *block = TRUE;
+ conn->waitfor = KEEP_RECV;
+ return CURLE_OK;
+ }
+ else if(rc == WS_WANT_WRITE) {
+ *block = TRUE;
+ conn->waitfor = KEEP_SEND;
+ return CURLE_OK;
+ }
+ else if(rc == WS_SUCCESS) {
+ infof(data, "wolfssh SFTP connected");
+ state(data, SSH_SFTP_REALPATH);
+ }
+ else {
+ failf(data, "wolfssh SFTP connect error %d", rc);
+ return CURLE_SSH;
+ }
+ break;
+ case SSH_SFTP_REALPATH:
+ name = wolfSSH_SFTP_RealPath(sshc->ssh_session, (char *)".");
+ rc = wolfSSH_get_error(sshc->ssh_session);
+ if(rc == WS_WANT_READ) {
+ *block = TRUE;
+ conn->waitfor = KEEP_RECV;
+ return CURLE_OK;
+ }
+ else if(rc == WS_WANT_WRITE) {
+ *block = TRUE;
+ conn->waitfor = KEEP_SEND;
+ return CURLE_OK;
+ }
+ else if(name && (rc == WS_SUCCESS)) {
+ sshc->homedir = malloc(name->fSz + 1);
+ if(!sshc->homedir) {
+ sshc->actualcode = CURLE_OUT_OF_MEMORY;
+ }
+ else {
+ memcpy(sshc->homedir, name->fName, name->fSz);
+ sshc->homedir[name->fSz] = 0;
+ infof(data, "wolfssh SFTP realpath succeeded");
+ }
+ wolfSSH_SFTPNAME_list_free(name);
+ state(data, SSH_STOP);
+ return CURLE_OK;
+ }
+ failf(data, "wolfssh SFTP realpath %d", rc);
+ return CURLE_SSH;
+
+ case SSH_SFTP_QUOTE_INIT:
+ result = Curl_getworkingpath(data, sshc->homedir, &sftp_scp->path);
+ if(result) {
+ sshc->actualcode = result;
+ state(data, SSH_STOP);
+ break;
+ }
+
+ if(data->set.quote) {
+ infof(data, "Sending quote commands");
+ sshc->quote_item = data->set.quote;
+ state(data, SSH_SFTP_QUOTE);
+ }
+ else {
+ state(data, SSH_SFTP_GETINFO);
+ }
+ break;
+ case SSH_SFTP_GETINFO:
+ if(data->set.get_filetime) {
+ state(data, SSH_SFTP_FILETIME);
+ }
+ else {
+ state(data, SSH_SFTP_TRANS_INIT);
+ }
+ break;
+ case SSH_SFTP_TRANS_INIT:
+ if(data->set.upload)
+ state(data, SSH_SFTP_UPLOAD_INIT);
+ else {
+ if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
+ state(data, SSH_SFTP_READDIR_INIT);
+ else
+ state(data, SSH_SFTP_DOWNLOAD_INIT);
+ }
+ break;
+ case SSH_SFTP_UPLOAD_INIT: {
+ word32 flags;
+ WS_SFTP_FILEATRB createattrs;
+ if(data->state.resume_from) {
+ WS_SFTP_FILEATRB attrs;
+ if(data->state.resume_from < 0) {
+ rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path,
+ &attrs);
+ if(rc != WS_SUCCESS)
+ break;
+
+ if(rc) {
+ data->state.resume_from = 0;
+ }
+ else {
+ curl_off_t size = ((curl_off_t)attrs.sz[1] << 32) | attrs.sz[0];
+ if(size < 0) {
+ failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
+ return CURLE_BAD_DOWNLOAD_RESUME;
+ }
+ data->state.resume_from = size;
+ }
+ }
+ }
+
+ if(data->set.remote_append)
+ /* Try to open for append, but create if nonexisting */
+ flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_APPEND;
+ else if(data->state.resume_from > 0)
+ /* If we have restart position then open for append */
+ flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_APPEND;
+ else
+ /* Clear file before writing (normal behavior) */
+ flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_TRUNC;
+
+ memset(&createattrs, 0, sizeof(createattrs));
+ createattrs.per = (word32)data->set.new_file_perms;
+ sshc->handleSz = sizeof(sshc->handle);
+ rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path,
+ flags, &createattrs,
+ sshc->handle, &sshc->handleSz);
+ if(rc == WS_FATAL_ERROR)
+ rc = wolfSSH_get_error(sshc->ssh_session);
+ if(rc == WS_WANT_READ) {
+ *block = TRUE;
+ conn->waitfor = KEEP_RECV;
+ return CURLE_OK;
+ }
+ else if(rc == WS_WANT_WRITE) {
+ *block = TRUE;
+ conn->waitfor = KEEP_SEND;
+ return CURLE_OK;
+ }
+ else if(rc == WS_SUCCESS) {
+ infof(data, "wolfssh SFTP open succeeded");
+ }
+ else {
+ failf(data, "wolfssh SFTP upload open failed: %d", rc);
+ return CURLE_SSH;
+ }
+ state(data, SSH_SFTP_DOWNLOAD_STAT);
+
+ /* If we have a restart point then we need to seek to the correct
+ position. */
+ if(data->state.resume_from > 0) {
+ /* Let's read off the proper amount of bytes from the input. */
+ int seekerr = CURL_SEEKFUNC_OK;
+ if(conn->seek_func) {
+ Curl_set_in_callback(data, true);
+ seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
+ SEEK_SET);
+ Curl_set_in_callback(data, false);
+ }
+
+ if(seekerr != CURL_SEEKFUNC_OK) {
+ curl_off_t passed = 0;
+
+ if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
+ failf(data, "Could not seek stream");
+ return CURLE_FTP_COULDNT_USE_REST;
+ }
+ /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
+ do {
+ size_t readthisamountnow =
+ (data->state.resume_from - passed > data->set.buffer_size) ?
+ (size_t)data->set.buffer_size :
+ curlx_sotouz(data->state.resume_from - passed);
+
+ size_t actuallyread;
+ Curl_set_in_callback(data, true);
+ actuallyread = data->state.fread_func(data->state.buffer, 1,
+ readthisamountnow,
+ data->state.in);
+ Curl_set_in_callback(data, false);
+
+ passed += actuallyread;
+ if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
+ /* this checks for greater-than only to make sure that the
+ CURL_READFUNC_ABORT return code still aborts */
+ failf(data, "Failed to read data");
+ return CURLE_FTP_COULDNT_USE_REST;
+ }
+ } while(passed < data->state.resume_from);
+ }
+
+ /* now, decrease the size of the read */
+ if(data->state.infilesize > 0) {
+ data->state.infilesize -= data->state.resume_from;
+ data->req.size = data->state.infilesize;
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+ }
+
+ sshc->offset += data->state.resume_from;
+ }
+ if(data->state.infilesize > 0) {
+ data->req.size = data->state.infilesize;
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+ }
+ /* upload data */
+ Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
+
+ /* not set by Curl_setup_transfer to preserve keepon bits */
+ conn->sockfd = conn->writesockfd;
+
+ if(result) {
+ state(data, SSH_SFTP_CLOSE);
+ sshc->actualcode = result;
+ }
+ else {
+ /* store this original bitmask setup to use later on if we can't
+ figure out a "real" bitmask */
+ sshc->orig_waitfor = data->req.keepon;
+
+ /* we want to use the _sending_ function even when the socket turns
+ out readable as the underlying libssh2 sftp send function will deal
+ with both accordingly */
+ conn->cselect_bits = CURL_CSELECT_OUT;
+
+ /* since we don't really wait for anything at this point, we want the
+ state machine to move on as soon as possible so we set a very short
+ timeout here */
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+
+ state(data, SSH_STOP);
+ }
+ break;
+ }
+ case SSH_SFTP_DOWNLOAD_INIT:
+ sshc->handleSz = sizeof(sshc->handle);
+ rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path,
+ WOLFSSH_FXF_READ, NULL,
+ sshc->handle, &sshc->handleSz);
+ if(rc == WS_FATAL_ERROR)
+ rc = wolfSSH_get_error(sshc->ssh_session);
+ if(rc == WS_WANT_READ) {
+ *block = TRUE;
+ conn->waitfor = KEEP_RECV;
+ return CURLE_OK;
+ }
+ else if(rc == WS_WANT_WRITE) {
+ *block = TRUE;
+ conn->waitfor = KEEP_SEND;
+ return CURLE_OK;
+ }
+ else if(rc == WS_SUCCESS) {
+ infof(data, "wolfssh SFTP open succeeded");
+ state(data, SSH_SFTP_DOWNLOAD_STAT);
+ return CURLE_OK;
+ }
+
+ failf(data, "wolfssh SFTP open failed: %d", rc);
+ return CURLE_SSH;
+
+ case SSH_SFTP_DOWNLOAD_STAT: {
+ WS_SFTP_FILEATRB attrs;
+ curl_off_t size;
+
+ rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path, &attrs);
+ if(rc == WS_FATAL_ERROR)
+ rc = wolfSSH_get_error(sshc->ssh_session);
+ if(rc == WS_WANT_READ) {
+ *block = TRUE;
+ conn->waitfor = KEEP_RECV;
+ return CURLE_OK;
+ }
+ else if(rc == WS_WANT_WRITE) {
+ *block = TRUE;
+ conn->waitfor = KEEP_SEND;
+ return CURLE_OK;
+ }
+ else if(rc == WS_SUCCESS) {
+ infof(data, "wolfssh STAT succeeded");
+ }
+ else {
+ failf(data, "wolfssh SFTP open failed: %d", rc);
+ data->req.size = -1;
+ data->req.maxdownload = -1;
+ Curl_pgrsSetDownloadSize(data, -1);
+ return CURLE_SSH;
+ }
+
+ size = ((curl_off_t)attrs.sz[1] <<32) | attrs.sz[0];
+
+ data->req.size = size;
+ data->req.maxdownload = size;
+ Curl_pgrsSetDownloadSize(data, size);
+
+ infof(data, "SFTP download %" CURL_FORMAT_CURL_OFF_T " bytes", size);
+
+ /* We cannot seek with wolfSSH so resuming and range requests are not
+ possible */
+ if(data->state.use_range || data->state.resume_from) {
+ infof(data, "wolfSSH cannot do range/seek on SFTP");
+ return CURLE_BAD_DOWNLOAD_RESUME;
+ }
+
+ /* Setup the actual download */
+ if(data->req.size == 0) {
+ /* no data to transfer */
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
+ infof(data, "File already completely downloaded");
+ state(data, SSH_STOP);
+ break;
+ }
+ Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1);
+
+ /* not set by Curl_setup_transfer to preserve keepon bits */
+ conn->writesockfd = conn->sockfd;
+
+ /* we want to use the _receiving_ function even when the socket turns
+ out writableable as the underlying libssh2 recv function will deal
+ with both accordingly */
+ conn->cselect_bits = CURL_CSELECT_IN;
+
+ if(result) {
+ /* this should never occur; the close state should be entered
+ at the time the error occurs */
+ state(data, SSH_SFTP_CLOSE);
+ sshc->actualcode = result;
+ }
+ else {
+ state(data, SSH_STOP);
+ }
+ break;
+ }
+ case SSH_SFTP_CLOSE:
+ if(sshc->handleSz)
+ rc = wolfSSH_SFTP_Close(sshc->ssh_session, sshc->handle,
+ sshc->handleSz);
+ else
+ rc = WS_SUCCESS; /* directory listing */
+ if(rc == WS_WANT_READ) {
+ *block = TRUE;
+ conn->waitfor = KEEP_RECV;
+ return CURLE_OK;
+ }
+ else if(rc == WS_WANT_WRITE) {
+ *block = TRUE;
+ conn->waitfor = KEEP_SEND;
+ return CURLE_OK;
+ }
+ else if(rc == WS_SUCCESS) {
+ state(data, SSH_STOP);
+ return CURLE_OK;
+ }
+
+ failf(data, "wolfssh SFTP CLOSE failed: %d", rc);
+ return CURLE_SSH;
+
+ case SSH_SFTP_READDIR_INIT:
+ Curl_pgrsSetDownloadSize(data, -1);
+ if(data->req.no_body) {
+ state(data, SSH_STOP);
+ break;
+ }
+ state(data, SSH_SFTP_READDIR);
+ break;
+
+ case SSH_SFTP_READDIR:
+ name = wolfSSH_SFTP_LS(sshc->ssh_session, sftp_scp->path);
+ if(!name)
+ rc = wolfSSH_get_error(sshc->ssh_session);
+ else
+ rc = WS_SUCCESS;
+
+ if(rc == WS_WANT_READ) {
+ *block = TRUE;
+ conn->waitfor = KEEP_RECV;
+ return CURLE_OK;
+ }
+ else if(rc == WS_WANT_WRITE) {
+ *block = TRUE;
+ conn->waitfor = KEEP_SEND;
+ return CURLE_OK;
+ }
+ else if(name && (rc == WS_SUCCESS)) {
+ WS_SFTPNAME *origname = name;
+ result = CURLE_OK;
+ while(name) {
+ char *line = aprintf("%s\n",
+ data->set.list_only ?
+ name->fName : name->lName);
+ if(!line) {
+ state(data, SSH_SFTP_CLOSE);
+ sshc->actualcode = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+ result = Curl_client_write(data, CLIENTWRITE_BODY,
+ line, strlen(line));
+ free(line);
+ if(result) {
+ sshc->actualcode = result;
+ break;
+ }
+ name = name->next;
+ }
+ wolfSSH_SFTPNAME_list_free(origname);
+ state(data, SSH_STOP);
+ return result;
+ }
+ failf(data, "wolfssh SFTP ls failed: %d", rc);
+ return CURLE_SSH;
+
+ case SSH_SFTP_SHUTDOWN:
+ Curl_safefree(sshc->homedir);
+ wolfSSH_free(sshc->ssh_session);
+ wolfSSH_CTX_free(sshc->ctx);
+ state(data, SSH_STOP);
+ return CURLE_OK;
+ default:
+ break;
+ }
+ } while(!rc && (sshc->state != SSH_STOP));
+ return result;
+}
+
+/* called repeatedly until done from multi.c */
+static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done)
+{
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ CURLcode result = CURLE_OK;
+ bool block; /* we store the status and use that to provide a ssh_getsock()
+ implementation */
+ do {
+ result = wssh_statemach_act(data, &block);
+ *done = (sshc->state == SSH_STOP) ? TRUE : FALSE;
+ /* if there's no error, it isn't done and it didn't EWOULDBLOCK, then
+ try again */
+ if(*done) {
+ DEBUGF(infof(data, "wssh_statemach_act says DONE"));
+ }
+ } while(!result && !*done && !block);
+
+ return result;
+}
+
+static
+CURLcode wscp_perform(struct Curl_easy *data,
+ bool *connected,
+ bool *dophase_done)
+{
+ (void)data;
+ (void)connected;
+ (void)dophase_done;
+ return CURLE_OK;
+}
+
+static
+CURLcode wsftp_perform(struct Curl_easy *data,
+ bool *connected,
+ bool *dophase_done)
+{
+ CURLcode result = CURLE_OK;
+
+ DEBUGF(infof(data, "DO phase starts"));
+
+ *dophase_done = FALSE; /* not done yet */
+
+ /* start the first command in the DO phase */
+ state(data, SSH_SFTP_QUOTE_INIT);
+
+ /* run the state-machine */
+ result = wssh_multi_statemach(data, dophase_done);
+
+ *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
+
+ if(*dophase_done) {
+ DEBUGF(infof(data, "DO phase is complete"));
+ }
+
+ return result;
+}
+
+/*
+ * The DO function is generic for both protocols.
+ */
+static CURLcode wssh_do(struct Curl_easy *data, bool *done)
+{
+ CURLcode result;
+ bool connected = 0;
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+
+ *done = FALSE; /* default to false */
+ data->req.size = -1; /* make sure this is unknown at this point */
+ sshc->actualcode = CURLE_OK; /* reset error code */
+ sshc->secondCreateDirs = 0; /* reset the create dir attempt state
+ variable */
+
+ Curl_pgrsSetUploadCounter(data, 0);
+ Curl_pgrsSetDownloadCounter(data, 0);
+ Curl_pgrsSetUploadSize(data, -1);
+ Curl_pgrsSetDownloadSize(data, -1);
+
+ if(conn->handler->protocol & CURLPROTO_SCP)
+ result = wscp_perform(data, &connected, done);
+ else
+ result = wsftp_perform(data, &connected, done);
+
+ return result;
+}
+
+static CURLcode wssh_block_statemach(struct Curl_easy *data,
+ bool disconnect)
+{
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
+ CURLcode result = CURLE_OK;
+
+ while((sshc->state != SSH_STOP) && !result) {
+ bool block;
+ timediff_t left = 1000;
+ struct curltime now = Curl_now();
+
+ result = wssh_statemach_act(data, &block);
+ if(result)
+ break;
+
+ if(!disconnect) {
+ if(Curl_pgrsUpdate(data))
+ return CURLE_ABORTED_BY_CALLBACK;
+
+ result = Curl_speedcheck(data, now);
+ if(result)
+ break;
+
+ left = Curl_timeleft(data, NULL, FALSE);
+ if(left < 0) {
+ failf(data, "Operation timed out");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+ }
+
+ if(!result) {
+ int dir = conn->waitfor;
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ curl_socket_t fd_read = CURL_SOCKET_BAD;
+ curl_socket_t fd_write = CURL_SOCKET_BAD;
+ if(dir == KEEP_RECV)
+ fd_read = sock;
+ else if(dir == KEEP_SEND)
+ fd_write = sock;
+
+ /* wait for the socket to become ready */
+ (void)Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write,
+ left>1000?1000:left); /* ignore result */
+ }
+ }
+
+ return result;
+}
+
+/* generic done function for both SCP and SFTP called from their specific
+ done functions */
+static CURLcode wssh_done(struct Curl_easy *data, CURLcode status)
+{
+ CURLcode result = CURLE_OK;
+ struct SSHPROTO *sftp_scp = data->req.p.ssh;
+
+ if(!status) {
+ /* run the state-machine */
+ result = wssh_block_statemach(data, FALSE);
+ }
+ else
+ result = status;
+
+ if(sftp_scp)
+ Curl_safefree(sftp_scp->path);
+ if(Curl_pgrsDone(data))
+ return CURLE_ABORTED_BY_CALLBACK;
+
+ data->req.keepon = 0; /* clear all bits */
+ return result;
+}
+
+#if 0
+static CURLcode wscp_done(struct Curl_easy *data,
+ CURLcode code, bool premature)
+{
+ CURLcode result = CURLE_OK;
+ (void)conn;
+ (void)code;
+ (void)premature;
+
+ return result;
+}
+
+static CURLcode wscp_doing(struct Curl_easy *data,
+ bool *dophase_done)
+{
+ CURLcode result = CURLE_OK;
+ (void)conn;
+ (void)dophase_done;
+
+ return result;
+}
+
+static CURLcode wscp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn, bool dead_connection)
+{
+ CURLcode result = CURLE_OK;
+ (void)data;
+ (void)conn;
+ (void)dead_connection;
+
+ return result;
+}
+#endif
+
+static CURLcode wsftp_done(struct Curl_easy *data,
+ CURLcode code, bool premature)
+{
+ (void)premature;
+ state(data, SSH_SFTP_CLOSE);
+
+ return wssh_done(data, code);
+}
+
+static CURLcode wsftp_doing(struct Curl_easy *data,
+ bool *dophase_done)
+{
+ CURLcode result = wssh_multi_statemach(data, dophase_done);
+
+ if(*dophase_done) {
+ DEBUGF(infof(data, "DO phase is complete"));
+ }
+ return result;
+}
+
+static CURLcode wsftp_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead)
+{
+ CURLcode result = CURLE_OK;
+ (void)dead;
+
+ DEBUGF(infof(data, "SSH DISCONNECT starts now"));
+
+ if(conn->proto.sshc.ssh_session) {
+ /* only if there's a session still around to use! */
+ state(data, SSH_SFTP_SHUTDOWN);
+ result = wssh_block_statemach(data, TRUE);
+ }
+
+ DEBUGF(infof(data, "SSH DISCONNECT is done"));
+ return result;
+}
+
+static int wssh_getsock(struct Curl_easy *data,
+ struct connectdata *conn,
+ curl_socket_t *sock)
+{
+ int bitmap = GETSOCK_BLANK;
+ int dir = conn->waitfor;
+ (void)data;
+ sock[0] = conn->sock[FIRSTSOCKET];
+
+ if(dir == KEEP_RECV)
+ bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
+ else if(dir == KEEP_SEND)
+ bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
+
+ return bitmap;
+}
+
+void Curl_ssh_version(char *buffer, size_t buflen)
+{
+ (void)msnprintf(buffer, buflen, "wolfssh/%s", LIBWOLFSSH_VERSION_STRING);
+}
+
+CURLcode Curl_ssh_init(void)
+{
+ if(WS_SUCCESS != wolfSSH_Init()) {
+ DEBUGF(fprintf(stderr, "Error: wolfSSH_Init failed\n"));
+ return CURLE_FAILED_INIT;
+ }
+
+ return CURLE_OK;
+}
+void Curl_ssh_cleanup(void)
+{
+}
+
+#endif /* USE_WOLFSSH */
diff --git a/libs/libcurl/src/vtls/bearssl.c b/libs/libcurl/src/vtls/bearssl.c index 1221ce8c84..6d53fab53c 100644 --- a/libs/libcurl/src/vtls/bearssl.c +++ b/libs/libcurl/src/vtls/bearssl.c @@ -1,1214 +1,1206 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2019 - 2022, Michael Forney, <mforney@mforney.org> - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#include "curl_setup.h" - -#ifdef USE_BEARSSL - -#include <bearssl.h> - -#include "bearssl.h" -#include "urldata.h" -#include "sendf.h" -#include "inet_pton.h" -#include "vtls.h" -#include "connect.h" -#include "select.h" -#include "multiif.h" -#include "curl_printf.h" -#include "curl_memory.h" -#include "strcase.h" - -struct x509_context { - const br_x509_class *vtable; - br_x509_minimal_context minimal; - br_x509_decoder_context decoder; - bool verifyhost; - bool verifypeer; - int cert_num; -}; - -struct ssl_backend_data { - br_ssl_client_context ctx; - struct x509_context x509; - unsigned char buf[BR_SSL_BUFSIZE_BIDI]; - br_x509_trust_anchor *anchors; - size_t anchors_len; - const char *protocols[2]; - /* SSL client context is active */ - bool active; - /* size of pending write, yet to be flushed */ - size_t pending_write; -}; - -struct cafile_parser { - CURLcode err; - bool in_cert; - br_x509_decoder_context xc; - /* array of trust anchors loaded from CAfile */ - br_x509_trust_anchor *anchors; - size_t anchors_len; - /* buffer for DN data */ - unsigned char dn[1024]; - size_t dn_len; -}; - -#define CAFILE_SOURCE_PATH 1 -#define CAFILE_SOURCE_BLOB 2 -struct cafile_source { - int type; - const char *data; - size_t len; -}; - -static void append_dn(void *ctx, const void *buf, size_t len) -{ - struct cafile_parser *ca = ctx; - - if(ca->err != CURLE_OK || !ca->in_cert) - return; - if(sizeof(ca->dn) - ca->dn_len < len) { - ca->err = CURLE_FAILED_INIT; - return; - } - memcpy(ca->dn + ca->dn_len, buf, len); - ca->dn_len += len; -} - -static void x509_push(void *ctx, const void *buf, size_t len) -{ - struct cafile_parser *ca = ctx; - - if(ca->in_cert) - br_x509_decoder_push(&ca->xc, buf, len); -} - -static CURLcode load_cafile(struct cafile_source *source, - br_x509_trust_anchor **anchors, - size_t *anchors_len) -{ - struct cafile_parser ca; - br_pem_decoder_context pc; - br_x509_trust_anchor *ta; - size_t ta_size; - br_x509_trust_anchor *new_anchors; - size_t new_anchors_len; - br_x509_pkey *pkey; - FILE *fp = 0; - unsigned char buf[BUFSIZ]; - const unsigned char *p; - const char *name; - size_t n, i, pushed; - - DEBUGASSERT(source->type == CAFILE_SOURCE_PATH - || source->type == CAFILE_SOURCE_BLOB); - - if(source->type == CAFILE_SOURCE_PATH) { - fp = fopen(source->data, "rb"); - if(!fp) - return CURLE_SSL_CACERT_BADFILE; - } - - if(source->type == CAFILE_SOURCE_BLOB && source->len > (size_t)INT_MAX) - return CURLE_SSL_CACERT_BADFILE; - - ca.err = CURLE_OK; - ca.in_cert = FALSE; - ca.anchors = NULL; - ca.anchors_len = 0; - br_pem_decoder_init(&pc); - br_pem_decoder_setdest(&pc, x509_push, &ca); - do { - if(source->type == CAFILE_SOURCE_PATH) { - n = fread(buf, 1, sizeof(buf), fp); - if(n == 0) - break; - p = buf; - } - else if(source->type == CAFILE_SOURCE_BLOB) { - n = source->len; - p = (unsigned char *) source->data; - } - while(n) { - pushed = br_pem_decoder_push(&pc, p, n); - if(ca.err) - goto fail; - p += pushed; - n -= pushed; - - switch(br_pem_decoder_event(&pc)) { - case 0: - break; - case BR_PEM_BEGIN_OBJ: - name = br_pem_decoder_name(&pc); - if(strcmp(name, "CERTIFICATE") && strcmp(name, "X509 CERTIFICATE")) - break; - br_x509_decoder_init(&ca.xc, append_dn, &ca); - ca.in_cert = TRUE; - ca.dn_len = 0; - break; - case BR_PEM_END_OBJ: - if(!ca.in_cert) - break; - ca.in_cert = FALSE; - if(br_x509_decoder_last_error(&ca.xc)) { - ca.err = CURLE_SSL_CACERT_BADFILE; - goto fail; - } - /* add trust anchor */ - if(ca.anchors_len == SIZE_MAX / sizeof(ca.anchors[0])) { - ca.err = CURLE_OUT_OF_MEMORY; - goto fail; - } - new_anchors_len = ca.anchors_len + 1; - new_anchors = realloc(ca.anchors, - new_anchors_len * sizeof(ca.anchors[0])); - if(!new_anchors) { - ca.err = CURLE_OUT_OF_MEMORY; - goto fail; - } - ca.anchors = new_anchors; - ca.anchors_len = new_anchors_len; - ta = &ca.anchors[ca.anchors_len - 1]; - ta->dn.data = NULL; - ta->flags = 0; - if(br_x509_decoder_isCA(&ca.xc)) - ta->flags |= BR_X509_TA_CA; - pkey = br_x509_decoder_get_pkey(&ca.xc); - if(!pkey) { - ca.err = CURLE_SSL_CACERT_BADFILE; - goto fail; - } - ta->pkey = *pkey; - - /* calculate space needed for trust anchor data */ - ta_size = ca.dn_len; - switch(pkey->key_type) { - case BR_KEYTYPE_RSA: - ta_size += pkey->key.rsa.nlen + pkey->key.rsa.elen; - break; - case BR_KEYTYPE_EC: - ta_size += pkey->key.ec.qlen; - break; - default: - ca.err = CURLE_FAILED_INIT; - goto fail; - } - - /* fill in trust anchor DN and public key data */ - ta->dn.data = malloc(ta_size); - if(!ta->dn.data) { - ca.err = CURLE_OUT_OF_MEMORY; - goto fail; - } - memcpy(ta->dn.data, ca.dn, ca.dn_len); - ta->dn.len = ca.dn_len; - switch(pkey->key_type) { - case BR_KEYTYPE_RSA: - ta->pkey.key.rsa.n = ta->dn.data + ta->dn.len; - memcpy(ta->pkey.key.rsa.n, pkey->key.rsa.n, pkey->key.rsa.nlen); - ta->pkey.key.rsa.e = ta->pkey.key.rsa.n + ta->pkey.key.rsa.nlen; - memcpy(ta->pkey.key.rsa.e, pkey->key.rsa.e, pkey->key.rsa.elen); - break; - case BR_KEYTYPE_EC: - ta->pkey.key.ec.q = ta->dn.data + ta->dn.len; - memcpy(ta->pkey.key.ec.q, pkey->key.ec.q, pkey->key.ec.qlen); - break; - } - break; - default: - ca.err = CURLE_SSL_CACERT_BADFILE; - goto fail; - } - } - } while(source->type != CAFILE_SOURCE_BLOB); - if(fp && ferror(fp)) - ca.err = CURLE_READ_ERROR; - else if(ca.in_cert) - ca.err = CURLE_SSL_CACERT_BADFILE; - -fail: - if(fp) - fclose(fp); - if(ca.err == CURLE_OK) { - *anchors = ca.anchors; - *anchors_len = ca.anchors_len; - } - else { - for(i = 0; i < ca.anchors_len; ++i) - free(ca.anchors[i].dn.data); - free(ca.anchors); - } - - return ca.err; -} - -static void x509_start_chain(const br_x509_class **ctx, - const char *server_name) -{ - struct x509_context *x509 = (struct x509_context *)ctx; - - if(!x509->verifypeer) { - x509->cert_num = 0; - return; - } - - if(!x509->verifyhost) - server_name = NULL; - x509->minimal.vtable->start_chain(&x509->minimal.vtable, server_name); -} - -static void x509_start_cert(const br_x509_class **ctx, uint32_t length) -{ - struct x509_context *x509 = (struct x509_context *)ctx; - - if(!x509->verifypeer) { - /* Only decode the first cert in the chain to obtain the public key */ - if(x509->cert_num == 0) - br_x509_decoder_init(&x509->decoder, NULL, NULL); - return; - } - - x509->minimal.vtable->start_cert(&x509->minimal.vtable, length); -} - -static void x509_append(const br_x509_class **ctx, const unsigned char *buf, - size_t len) -{ - struct x509_context *x509 = (struct x509_context *)ctx; - - if(!x509->verifypeer) { - if(x509->cert_num == 0) - br_x509_decoder_push(&x509->decoder, buf, len); - return; - } - - x509->minimal.vtable->append(&x509->minimal.vtable, buf, len); -} - -static void x509_end_cert(const br_x509_class **ctx) -{ - struct x509_context *x509 = (struct x509_context *)ctx; - - if(!x509->verifypeer) { - x509->cert_num++; - return; - } - - x509->minimal.vtable->end_cert(&x509->minimal.vtable); -} - -static unsigned x509_end_chain(const br_x509_class **ctx) -{ - struct x509_context *x509 = (struct x509_context *)ctx; - - if(!x509->verifypeer) { - return br_x509_decoder_last_error(&x509->decoder); - } - - return x509->minimal.vtable->end_chain(&x509->minimal.vtable); -} - -static const br_x509_pkey *x509_get_pkey(const br_x509_class *const *ctx, - unsigned *usages) -{ - struct x509_context *x509 = (struct x509_context *)ctx; - - if(!x509->verifypeer) { - /* Nothing in the chain is verified, just return the public key of the - first certificate and allow its usage for both TLS_RSA_* and - TLS_ECDHE_* */ - if(usages) - *usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN; - return br_x509_decoder_get_pkey(&x509->decoder); - } - - return x509->minimal.vtable->get_pkey(&x509->minimal.vtable, usages); -} - -static const br_x509_class x509_vtable = { - sizeof(struct x509_context), - x509_start_chain, - x509_start_cert, - x509_append, - x509_end_cert, - x509_end_chain, - x509_get_pkey -}; - -struct st_cipher { - const char *name; /* Cipher suite IANA name. It starts with "TLS_" prefix */ - const char *alias_name; /* Alias name is the same as OpenSSL cipher name */ - uint16_t num; /* BearSSL cipher suite */ -}; - -/* Macro to initialize st_cipher data structure */ -#define CIPHER_DEF(num, alias) { #num, alias, BR_##num } - -static const struct st_cipher ciphertable[] = { - /* RFC 2246 TLS 1.0 */ - CIPHER_DEF(TLS_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x000A */ - "DES-CBC3-SHA"), - - /* RFC 3268 TLS 1.0 AES */ - CIPHER_DEF(TLS_RSA_WITH_AES_128_CBC_SHA, /* 0x002F */ - "AES128-SHA"), - CIPHER_DEF(TLS_RSA_WITH_AES_256_CBC_SHA, /* 0x0035 */ - "AES256-SHA"), - - /* RFC 5246 TLS 1.2 */ - CIPHER_DEF(TLS_RSA_WITH_AES_128_CBC_SHA256, /* 0x003C */ - "AES128-SHA256"), - CIPHER_DEF(TLS_RSA_WITH_AES_256_CBC_SHA256, /* 0x003D */ - "AES256-SHA256"), - - /* RFC 5288 TLS 1.2 AES GCM */ - CIPHER_DEF(TLS_RSA_WITH_AES_128_GCM_SHA256, /* 0x009C */ - "AES128-GCM-SHA256"), - CIPHER_DEF(TLS_RSA_WITH_AES_256_GCM_SHA384, /* 0x009D */ - "AES256-GCM-SHA384"), - - /* RFC 4492 TLS 1.0 ECC */ - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC003 */ - "ECDH-ECDSA-DES-CBC3-SHA"), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC004 */ - "ECDH-ECDSA-AES128-SHA"), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC005 */ - "ECDH-ECDSA-AES256-SHA"), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC008 */ - "ECDHE-ECDSA-DES-CBC3-SHA"), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC009 */ - "ECDHE-ECDSA-AES128-SHA"), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC00A */ - "ECDHE-ECDSA-AES256-SHA"), - CIPHER_DEF(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC00D */ - "ECDH-RSA-DES-CBC3-SHA"), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, /* 0xC00E */ - "ECDH-RSA-AES128-SHA"), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, /* 0xC00F */ - "ECDH-RSA-AES256-SHA"), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC012 */ - "ECDHE-RSA-DES-CBC3-SHA"), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, /* 0xC013 */ - "ECDHE-RSA-AES128-SHA"), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, /* 0xC014 */ - "ECDHE-RSA-AES256-SHA"), - - /* RFC 5289 TLS 1.2 ECC HMAC SHA256/384 */ - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC023 */ - "ECDHE-ECDSA-AES128-SHA256"), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC024 */ - "ECDHE-ECDSA-AES256-SHA384"), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC025 */ - "ECDH-ECDSA-AES128-SHA256"), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC026 */ - "ECDH-ECDSA-AES256-SHA384"), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, /* 0xC027 */ - "ECDHE-RSA-AES128-SHA256"), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, /* 0xC028 */ - "ECDHE-RSA-AES256-SHA384"), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, /* 0xC029 */ - "ECDH-RSA-AES128-SHA256"), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, /* 0xC02A */ - "ECDH-RSA-AES256-SHA384"), - - /* RFC 5289 TLS 1.2 GCM */ - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02B */ - "ECDHE-ECDSA-AES128-GCM-SHA256"), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02C */ - "ECDHE-ECDSA-AES256-GCM-SHA384"), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02D */ - "ECDH-ECDSA-AES128-GCM-SHA256"), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02E */ - "ECDH-ECDSA-AES256-GCM-SHA384"), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* 0xC02F */ - "ECDHE-RSA-AES128-GCM-SHA256"), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, /* 0xC030 */ - "ECDHE-RSA-AES256-GCM-SHA384"), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, /* 0xC031 */ - "ECDH-RSA-AES128-GCM-SHA256"), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, /* 0xC032 */ - "ECDH-RSA-AES256-GCM-SHA384"), -#ifdef BR_TLS_RSA_WITH_AES_128_CCM - - /* RFC 6655 TLS 1.2 CCM - Supported since BearSSL 0.6 */ - CIPHER_DEF(TLS_RSA_WITH_AES_128_CCM, /* 0xC09C */ - "AES128-CCM"), - CIPHER_DEF(TLS_RSA_WITH_AES_256_CCM, /* 0xC09D */ - "AES256-CCM"), - CIPHER_DEF(TLS_RSA_WITH_AES_128_CCM_8, /* 0xC0A0 */ - "AES128-CCM8"), - CIPHER_DEF(TLS_RSA_WITH_AES_256_CCM_8, /* 0xC0A1 */ - "AES256-CCM8"), - - /* RFC 7251 TLS 1.2 ECC CCM - Supported since BearSSL 0.6 */ - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CCM, /* 0xC0AC */ - "ECDHE-ECDSA-AES128-CCM"), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CCM, /* 0xC0AD */ - "ECDHE-ECDSA-AES256-CCM"), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, /* 0xC0AE */ - "ECDHE-ECDSA-AES128-CCM8"), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, /* 0xC0AF */ - "ECDHE-ECDSA-AES256-CCM8"), -#endif - - /* RFC 7905 TLS 1.2 ChaCha20-Poly1305 - Supported since BearSSL 0.2 */ - CIPHER_DEF(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA8 */ - "ECDHE-RSA-CHACHA20-POLY1305"), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA9 */ - "ECDHE-ECDSA-CHACHA20-POLY1305"), -}; - -#define NUM_OF_CIPHERS (sizeof(ciphertable) / sizeof(ciphertable[0])) -#define CIPHER_NAME_BUF_LEN 64 - -static bool is_separator(char c) -{ - /* Return whether character is a cipher list separator. */ - switch(c) { - case ' ': - case '\t': - case ':': - case ',': - case ';': - return true; - } - return false; -} - -static CURLcode bearssl_set_selected_ciphers(struct Curl_easy *data, - br_ssl_engine_context *ssl_eng, - const char *ciphers) -{ - uint16_t selected_ciphers[NUM_OF_CIPHERS]; - size_t selected_count = 0; - char cipher_name[CIPHER_NAME_BUF_LEN]; - const char *cipher_start = ciphers; - const char *cipher_end; - size_t i, j; - - if(!cipher_start) - return CURLE_SSL_CIPHER; - - while(true) { - /* Extract the next cipher name from the ciphers string */ - while(is_separator(*cipher_start)) - ++cipher_start; - if(*cipher_start == '\0') - break; - cipher_end = cipher_start; - while(*cipher_end != '\0' && !is_separator(*cipher_end)) - ++cipher_end; - j = cipher_end - cipher_start < CIPHER_NAME_BUF_LEN - 1 ? - cipher_end - cipher_start : CIPHER_NAME_BUF_LEN - 1; - strncpy(cipher_name, cipher_start, j); - cipher_name[j] = '\0'; - cipher_start = cipher_end; - - /* Lookup the cipher name in the table of available ciphers. If the cipher - name starts with "TLS_" we do the lookup by IANA name. Otherwise, we try - to match cipher name by an (OpenSSL) alias. */ - if(strncasecompare(cipher_name, "TLS_", 4)) { - for(i = 0; i < NUM_OF_CIPHERS && - !strcasecompare(cipher_name, ciphertable[i].name); ++i); - } - else { - for(i = 0; i < NUM_OF_CIPHERS && - !strcasecompare(cipher_name, ciphertable[i].alias_name); ++i); - } - if(i == NUM_OF_CIPHERS) { - infof(data, "BearSSL: unknown cipher in list: %s", cipher_name); - continue; - } - - /* No duplicates allowed */ - for(j = 0; j < selected_count && - selected_ciphers[j] != ciphertable[i].num; j++); - if(j < selected_count) { - infof(data, "BearSSL: duplicate cipher in list: %s", cipher_name); - continue; - } - - DEBUGASSERT(selected_count < NUM_OF_CIPHERS); - selected_ciphers[selected_count] = ciphertable[i].num; - ++selected_count; - } - - if(selected_count == 0) { - failf(data, "BearSSL: no supported cipher in list"); - return CURLE_SSL_CIPHER; - } - - br_ssl_engine_set_suites(ssl_eng, selected_ciphers, selected_count); - return CURLE_OK; -} - -static CURLcode bearssl_connect_step1(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob); - const char * const ssl_cafile = - /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ - (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile)); - const char *hostname = SSL_HOST_NAME(); - const bool verifypeer = SSL_CONN_CONFIG(verifypeer); - const bool verifyhost = SSL_CONN_CONFIG(verifyhost); - CURLcode ret; - unsigned version_min, version_max; -#ifdef ENABLE_IPV6 - struct in6_addr addr; -#else - struct in_addr addr; -#endif - - DEBUGASSERT(backend); - - switch(SSL_CONN_CONFIG(version)) { - case CURL_SSLVERSION_SSLv2: - failf(data, "BearSSL does not support SSLv2"); - return CURLE_SSL_CONNECT_ERROR; - case CURL_SSLVERSION_SSLv3: - failf(data, "BearSSL does not support SSLv3"); - return CURLE_SSL_CONNECT_ERROR; - case CURL_SSLVERSION_TLSv1_0: - version_min = BR_TLS10; - version_max = BR_TLS10; - break; - case CURL_SSLVERSION_TLSv1_1: - version_min = BR_TLS11; - version_max = BR_TLS11; - break; - case CURL_SSLVERSION_TLSv1_2: - version_min = BR_TLS12; - version_max = BR_TLS12; - break; - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - version_min = BR_TLS10; - version_max = BR_TLS12; - break; - default: - failf(data, "BearSSL: unknown CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } - - if(ca_info_blob) { - struct cafile_source source; - source.type = CAFILE_SOURCE_BLOB; - source.data = ca_info_blob->data; - source.len = ca_info_blob->len; - - ret = load_cafile(&source, &backend->anchors, &backend->anchors_len); - if(ret != CURLE_OK) { - if(verifypeer) { - failf(data, "error importing CA certificate blob"); - return ret; - } - /* Only warn if no certificate verification is required. */ - infof(data, "error importing CA certificate blob, continuing anyway"); - } - } - - if(ssl_cafile) { - struct cafile_source source; - source.type = CAFILE_SOURCE_PATH; - source.data = ssl_cafile; - source.len = 0; - - ret = load_cafile(&source, &backend->anchors, &backend->anchors_len); - if(ret != CURLE_OK) { - if(verifypeer) { - failf(data, "error setting certificate verify locations." - " CAfile: %s", ssl_cafile); - return ret; - } - infof(data, "error setting certificate verify locations," - " continuing anyway:"); - } - } - - /* initialize SSL context */ - br_ssl_client_init_full(&backend->ctx, &backend->x509.minimal, - backend->anchors, backend->anchors_len); - br_ssl_engine_set_versions(&backend->ctx.eng, version_min, version_max); - br_ssl_engine_set_buffer(&backend->ctx.eng, backend->buf, - sizeof(backend->buf), 1); - - if(SSL_CONN_CONFIG(cipher_list)) { - /* Override the ciphers as specified. For the default cipher list see the - BearSSL source code of br_ssl_client_init_full() */ - ret = bearssl_set_selected_ciphers(data, &backend->ctx.eng, - SSL_CONN_CONFIG(cipher_list)); - if(ret) - return ret; - } - - /* initialize X.509 context */ - backend->x509.vtable = &x509_vtable; - backend->x509.verifypeer = verifypeer; - backend->x509.verifyhost = verifyhost; - br_ssl_engine_set_x509(&backend->ctx.eng, &backend->x509.vtable); - - if(SSL_SET_OPTION(primary.sessionid)) { - void *session; - - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE, - &session, NULL, sockindex)) { - br_ssl_engine_set_session_parameters(&backend->ctx.eng, session); - infof(data, "BearSSL: re-using session ID"); - } - Curl_ssl_sessionid_unlock(data); - } - - if(conn->bits.tls_enable_alpn) { - int cur = 0; - - /* NOTE: when adding more protocols here, increase the size of the - * protocols array in `struct ssl_backend_data`. - */ - -#ifdef USE_HTTP2 - if(data->state.httpwant >= CURL_HTTP_VERSION_2 -#ifndef CURL_DISABLE_PROXY - && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy) -#endif - ) { - backend->protocols[cur++] = ALPN_H2; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); - } -#endif - - backend->protocols[cur++] = ALPN_HTTP_1_1; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); - - br_ssl_engine_set_protocol_names(&backend->ctx.eng, - backend->protocols, cur); - } - - if((1 == Curl_inet_pton(AF_INET, hostname, &addr)) -#ifdef ENABLE_IPV6 - || (1 == Curl_inet_pton(AF_INET6, hostname, &addr)) -#endif - ) { - if(verifyhost) { - failf(data, "BearSSL: " - "host verification of IP address is not supported"); - return CURLE_PEER_FAILED_VERIFICATION; - } - hostname = NULL; - } - else { - char *snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - hostname = snihost; - } - - /* give application a chance to interfere with SSL set up. */ - if(data->set.ssl.fsslctx) { - Curl_set_in_callback(data, true); - ret = (*data->set.ssl.fsslctx)(data, &backend->ctx, - data->set.ssl.fsslctxp); - Curl_set_in_callback(data, false); - if(ret) { - failf(data, "BearSSL: error signaled by ssl ctx callback"); - return ret; - } - } - - if(!br_ssl_client_reset(&backend->ctx, hostname, 1)) - return CURLE_FAILED_INIT; - backend->active = TRUE; - - connssl->connecting_state = ssl_connect_2; - - return CURLE_OK; -} - -static CURLcode bearssl_run_until(struct Curl_easy *data, - struct connectdata *conn, int sockindex, - unsigned target) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - curl_socket_t sockfd = conn->sock[sockindex]; - unsigned state; - unsigned char *buf; - size_t len; - ssize_t ret; - int err; - - DEBUGASSERT(backend); - - for(;;) { - state = br_ssl_engine_current_state(&backend->ctx.eng); - if(state & BR_SSL_CLOSED) { - err = br_ssl_engine_last_error(&backend->ctx.eng); - switch(err) { - case BR_ERR_OK: - /* TLS close notify */ - if(connssl->state != ssl_connection_complete) { - failf(data, "SSL: connection closed during handshake"); - return CURLE_SSL_CONNECT_ERROR; - } - return CURLE_OK; - case BR_ERR_X509_EXPIRED: - failf(data, "SSL: X.509 verification: " - "certificate is expired or not yet valid"); - return CURLE_PEER_FAILED_VERIFICATION; - case BR_ERR_X509_BAD_SERVER_NAME: - failf(data, "SSL: X.509 verification: " - "expected server name was not found in the chain"); - return CURLE_PEER_FAILED_VERIFICATION; - case BR_ERR_X509_NOT_TRUSTED: - failf(data, "SSL: X.509 verification: " - "chain could not be linked to a trust anchor"); - return CURLE_PEER_FAILED_VERIFICATION; - } - /* X.509 errors are documented to have the range 32..63 */ - if(err >= 32 && err < 64) - return CURLE_PEER_FAILED_VERIFICATION; - return CURLE_SSL_CONNECT_ERROR; - } - if(state & target) - return CURLE_OK; - if(state & BR_SSL_SENDREC) { - buf = br_ssl_engine_sendrec_buf(&backend->ctx.eng, &len); - ret = swrite(sockfd, buf, len); - if(ret == -1) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { - if(connssl->state != ssl_connection_complete) - connssl->connecting_state = ssl_connect_2_writing; - return CURLE_AGAIN; - } - return CURLE_WRITE_ERROR; - } - br_ssl_engine_sendrec_ack(&backend->ctx.eng, ret); - } - else if(state & BR_SSL_RECVREC) { - buf = br_ssl_engine_recvrec_buf(&backend->ctx.eng, &len); - ret = sread(sockfd, buf, len); - if(ret == 0) { - failf(data, "SSL: EOF without close notify"); - return CURLE_READ_ERROR; - } - if(ret == -1) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { - if(connssl->state != ssl_connection_complete) - connssl->connecting_state = ssl_connect_2_reading; - return CURLE_AGAIN; - } - return CURLE_READ_ERROR; - } - br_ssl_engine_recvrec_ack(&backend->ctx.eng, ret); - } - } -} - -static CURLcode bearssl_connect_step2(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - CURLcode ret; - - DEBUGASSERT(backend); - - ret = bearssl_run_until(data, conn, sockindex, - BR_SSL_SENDAPP | BR_SSL_RECVAPP); - if(ret == CURLE_AGAIN) - return CURLE_OK; - if(ret == CURLE_OK) { - if(br_ssl_engine_current_state(&backend->ctx.eng) == BR_SSL_CLOSED) { - failf(data, "SSL: connection closed during handshake"); - return CURLE_SSL_CONNECT_ERROR; - } - connssl->connecting_state = ssl_connect_3; - } - return ret; -} - -static CURLcode bearssl_connect_step3(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - CURLcode ret; - - DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); - DEBUGASSERT(backend); - - if(conn->bits.tls_enable_alpn) { - const char *protocol; - - protocol = br_ssl_engine_get_selected_protocol(&backend->ctx.eng); - if(protocol) { - infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, protocol); - -#ifdef USE_HTTP2 - if(!strcmp(protocol, ALPN_H2)) - conn->alpn = CURL_HTTP_VERSION_2; - else -#endif - if(!strcmp(protocol, ALPN_HTTP_1_1)) - conn->alpn = CURL_HTTP_VERSION_1_1; - else - infof(data, "ALPN, unrecognized protocol %s", protocol); - Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); - } - else - infof(data, VTLS_INFOF_NO_ALPN); - } - - if(SSL_SET_OPTION(primary.sessionid)) { - bool incache; - bool added = FALSE; - void *oldsession; - br_ssl_session_parameters *session; - - session = malloc(sizeof(*session)); - if(!session) - return CURLE_OUT_OF_MEMORY; - br_ssl_engine_get_session_parameters(&backend->ctx.eng, session); - Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(data, conn, - SSL_IS_PROXY() ? TRUE : FALSE, - &oldsession, NULL, sockindex)); - if(incache) - Curl_ssl_delsessionid(data, oldsession); - ret = Curl_ssl_addsessionid(data, conn, - SSL_IS_PROXY() ? TRUE : FALSE, - session, 0, sockindex, &added); - Curl_ssl_sessionid_unlock(data); - if(!added) - free(session); - if(ret) { - return CURLE_OUT_OF_MEMORY; - } - } - - connssl->connecting_state = ssl_connect_done; - - return CURLE_OK; -} - -static ssize_t bearssl_send(struct Curl_easy *data, int sockindex, - const void *buf, size_t len, CURLcode *err) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - unsigned char *app; - size_t applen; - - DEBUGASSERT(backend); - - for(;;) { - *err = bearssl_run_until(data, conn, sockindex, BR_SSL_SENDAPP); - if (*err != CURLE_OK) - return -1; - app = br_ssl_engine_sendapp_buf(&backend->ctx.eng, &applen); - if(!app) { - failf(data, "SSL: connection closed during write"); - *err = CURLE_SEND_ERROR; - return -1; - } - if(backend->pending_write) { - applen = backend->pending_write; - backend->pending_write = 0; - return applen; - } - if(applen > len) - applen = len; - memcpy(app, buf, applen); - br_ssl_engine_sendapp_ack(&backend->ctx.eng, applen); - br_ssl_engine_flush(&backend->ctx.eng, 0); - backend->pending_write = applen; - } -} - -static ssize_t bearssl_recv(struct Curl_easy *data, int sockindex, - char *buf, size_t len, CURLcode *err) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - unsigned char *app; - size_t applen; - - DEBUGASSERT(backend); - - *err = bearssl_run_until(data, conn, sockindex, BR_SSL_RECVAPP); - if(*err != CURLE_OK) - return -1; - app = br_ssl_engine_recvapp_buf(&backend->ctx.eng, &applen); - if(!app) - return 0; - if(applen > len) - applen = len; - memcpy(buf, app, applen); - br_ssl_engine_recvapp_ack(&backend->ctx.eng, applen); - - return applen; -} - -static CURLcode bearssl_connect_common(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool nonblocking, - bool *done) -{ - CURLcode ret; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - curl_socket_t sockfd = conn->sock[sockindex]; - timediff_t timeout_ms; - int what; - - /* check if the connection has already been established */ - if(ssl_connection_complete == connssl->state) { - *done = TRUE; - return CURLE_OK; - } - - if(ssl_connect_1 == connssl->connecting_state) { - ret = bearssl_connect_step1(data, conn, sockindex); - if(ret) - return ret; - } - - while(ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state) { - /* check allowed time left */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - /* if ssl is expecting something, check if it's available. */ - if(ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state) { - - curl_socket_t writefd = ssl_connect_2_writing == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - curl_socket_t readfd = ssl_connect_2_reading == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - - what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, - nonblocking?0:timeout_ms); - if(what < 0) { - /* fatal error */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - return CURLE_SSL_CONNECT_ERROR; - } - else if(0 == what) { - if(nonblocking) { - *done = FALSE; - return CURLE_OK; - } - else { - /* timeout */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - } - /* socket is readable or writable */ - } - - /* Run transaction, and return to the caller if it failed or if this - * connection is done nonblocking and this loop would execute again. This - * permits the owner of a multi handle to abort a connection attempt - * before step2 has completed while ensuring that a client using select() - * or epoll() will always have a valid fdset to wait on. - */ - ret = bearssl_connect_step2(data, conn, sockindex); - if(ret || (nonblocking && - (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) - return ret; - } - - if(ssl_connect_3 == connssl->connecting_state) { - ret = bearssl_connect_step3(data, conn, sockindex); - if(ret) - return ret; - } - - if(ssl_connect_done == connssl->connecting_state) { - connssl->state = ssl_connection_complete; - conn->recv[sockindex] = bearssl_recv; - conn->send[sockindex] = bearssl_send; - *done = TRUE; - } - else - *done = FALSE; - - /* Reset our connect state machine */ - connssl->connecting_state = ssl_connect_1; - - return CURLE_OK; -} - -static size_t bearssl_version(char *buffer, size_t size) -{ - return msnprintf(buffer, size, "BearSSL"); -} - -static bool bearssl_data_pending(const struct connectdata *conn, - int connindex) -{ - const struct ssl_connect_data *connssl = &conn->ssl[connindex]; - struct ssl_backend_data *backend = connssl->backend; - DEBUGASSERT(backend); - return br_ssl_engine_current_state(&backend->ctx.eng) & BR_SSL_RECVAPP; -} - -static CURLcode bearssl_random(struct Curl_easy *data UNUSED_PARAM, - unsigned char *entropy, size_t length) -{ - static br_hmac_drbg_context ctx; - static bool seeded = FALSE; - - if(!seeded) { - br_prng_seeder seeder; - - br_hmac_drbg_init(&ctx, &br_sha256_vtable, NULL, 0); - seeder = br_prng_seeder_system(NULL); - if(!seeder || !seeder(&ctx.vtable)) - return CURLE_FAILED_INIT; - seeded = TRUE; - } - br_hmac_drbg_generate(&ctx, entropy, length); - - return CURLE_OK; -} - -static CURLcode bearssl_connect(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - CURLcode ret; - bool done = FALSE; - - ret = bearssl_connect_common(data, conn, sockindex, FALSE, &done); - if(ret) - return ret; - - DEBUGASSERT(done); - - return CURLE_OK; -} - -static CURLcode bearssl_connect_nonblocking(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, bool *done) -{ - return bearssl_connect_common(data, conn, sockindex, TRUE, done); -} - -static void *bearssl_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - struct ssl_backend_data *backend = connssl->backend; - DEBUGASSERT(backend); - return &backend->ctx; -} - -static void bearssl_close(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - size_t i; - - DEBUGASSERT(backend); - - if(backend->active) { - br_ssl_engine_close(&backend->ctx.eng); - (void)bearssl_run_until(data, conn, sockindex, BR_SSL_CLOSED); - } - for(i = 0; i < backend->anchors_len; ++i) - free(backend->anchors[i].dn.data); - free(backend->anchors); -} - -static void bearssl_session_free(void *ptr) -{ - free(ptr); -} - -static CURLcode bearssl_sha256sum(const unsigned char *input, - size_t inputlen, - unsigned char *sha256sum, - size_t sha256len UNUSED_PARAM) -{ - br_sha256_context ctx; - - br_sha256_init(&ctx); - br_sha256_update(&ctx, input, inputlen); - br_sha256_out(&ctx, sha256sum); - return CURLE_OK; -} - -const struct Curl_ssl Curl_ssl_bearssl = { - { CURLSSLBACKEND_BEARSSL, "bearssl" }, /* info */ - SSLSUPP_CAINFO_BLOB | SSLSUPP_SSL_CTX, - sizeof(struct ssl_backend_data), - - Curl_none_init, /* init */ - Curl_none_cleanup, /* cleanup */ - bearssl_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ - Curl_none_shutdown, /* shutdown */ - bearssl_data_pending, /* data_pending */ - bearssl_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ - bearssl_connect, /* connect */ - bearssl_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_getsock, /* getsock */ - bearssl_get_internals, /* get_internals */ - bearssl_close, /* close_one */ - Curl_none_close_all, /* close_all */ - bearssl_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ - bearssl_sha256sum, /* sha256sum */ - NULL, /* associate_connection */ - NULL /* disassociate_connection */ -}; - -#endif /* USE_BEARSSL */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2019 - 2022, Michael Forney, <mforney@mforney.org>
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#ifdef USE_BEARSSL
+
+#include <bearssl.h>
+
+#include "bearssl.h"
+#include "urldata.h"
+#include "sendf.h"
+#include "inet_pton.h"
+#include "vtls.h"
+#include "vtls_int.h"
+#include "connect.h"
+#include "select.h"
+#include "multiif.h"
+#include "curl_printf.h"
+#include "strcase.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+struct x509_context {
+ const br_x509_class *vtable;
+ br_x509_minimal_context minimal;
+ br_x509_decoder_context decoder;
+ bool verifyhost;
+ bool verifypeer;
+ int cert_num;
+};
+
+struct ssl_backend_data {
+ br_ssl_client_context ctx;
+ struct x509_context x509;
+ unsigned char buf[BR_SSL_BUFSIZE_BIDI];
+ br_x509_trust_anchor *anchors;
+ size_t anchors_len;
+ const char *protocols[2];
+ /* SSL client context is active */
+ bool active;
+ /* size of pending write, yet to be flushed */
+ size_t pending_write;
+};
+
+struct cafile_parser {
+ CURLcode err;
+ bool in_cert;
+ br_x509_decoder_context xc;
+ /* array of trust anchors loaded from CAfile */
+ br_x509_trust_anchor *anchors;
+ size_t anchors_len;
+ /* buffer for DN data */
+ unsigned char dn[1024];
+ size_t dn_len;
+};
+
+#define CAFILE_SOURCE_PATH 1
+#define CAFILE_SOURCE_BLOB 2
+struct cafile_source {
+ int type;
+ const char *data;
+ size_t len;
+};
+
+static void append_dn(void *ctx, const void *buf, size_t len)
+{
+ struct cafile_parser *ca = ctx;
+
+ if(ca->err != CURLE_OK || !ca->in_cert)
+ return;
+ if(sizeof(ca->dn) - ca->dn_len < len) {
+ ca->err = CURLE_FAILED_INIT;
+ return;
+ }
+ memcpy(ca->dn + ca->dn_len, buf, len);
+ ca->dn_len += len;
+}
+
+static void x509_push(void *ctx, const void *buf, size_t len)
+{
+ struct cafile_parser *ca = ctx;
+
+ if(ca->in_cert)
+ br_x509_decoder_push(&ca->xc, buf, len);
+}
+
+static CURLcode load_cafile(struct cafile_source *source,
+ br_x509_trust_anchor **anchors,
+ size_t *anchors_len)
+{
+ struct cafile_parser ca;
+ br_pem_decoder_context pc;
+ br_x509_trust_anchor *ta;
+ size_t ta_size;
+ br_x509_trust_anchor *new_anchors;
+ size_t new_anchors_len;
+ br_x509_pkey *pkey;
+ FILE *fp = 0;
+ unsigned char buf[BUFSIZ];
+ const unsigned char *p;
+ const char *name;
+ size_t n, i, pushed;
+
+ DEBUGASSERT(source->type == CAFILE_SOURCE_PATH
+ || source->type == CAFILE_SOURCE_BLOB);
+
+ if(source->type == CAFILE_SOURCE_PATH) {
+ fp = fopen(source->data, "rb");
+ if(!fp)
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+
+ if(source->type == CAFILE_SOURCE_BLOB && source->len > (size_t)INT_MAX)
+ return CURLE_SSL_CACERT_BADFILE;
+
+ ca.err = CURLE_OK;
+ ca.in_cert = FALSE;
+ ca.anchors = NULL;
+ ca.anchors_len = 0;
+ br_pem_decoder_init(&pc);
+ br_pem_decoder_setdest(&pc, x509_push, &ca);
+ do {
+ if(source->type == CAFILE_SOURCE_PATH) {
+ n = fread(buf, 1, sizeof(buf), fp);
+ if(n == 0)
+ break;
+ p = buf;
+ }
+ else if(source->type == CAFILE_SOURCE_BLOB) {
+ n = source->len;
+ p = (unsigned char *) source->data;
+ }
+ while(n) {
+ pushed = br_pem_decoder_push(&pc, p, n);
+ if(ca.err)
+ goto fail;
+ p += pushed;
+ n -= pushed;
+
+ switch(br_pem_decoder_event(&pc)) {
+ case 0:
+ break;
+ case BR_PEM_BEGIN_OBJ:
+ name = br_pem_decoder_name(&pc);
+ if(strcmp(name, "CERTIFICATE") && strcmp(name, "X509 CERTIFICATE"))
+ break;
+ br_x509_decoder_init(&ca.xc, append_dn, &ca);
+ ca.in_cert = TRUE;
+ ca.dn_len = 0;
+ break;
+ case BR_PEM_END_OBJ:
+ if(!ca.in_cert)
+ break;
+ ca.in_cert = FALSE;
+ if(br_x509_decoder_last_error(&ca.xc)) {
+ ca.err = CURLE_SSL_CACERT_BADFILE;
+ goto fail;
+ }
+ /* add trust anchor */
+ if(ca.anchors_len == SIZE_MAX / sizeof(ca.anchors[0])) {
+ ca.err = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ new_anchors_len = ca.anchors_len + 1;
+ new_anchors = realloc(ca.anchors,
+ new_anchors_len * sizeof(ca.anchors[0]));
+ if(!new_anchors) {
+ ca.err = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ ca.anchors = new_anchors;
+ ca.anchors_len = new_anchors_len;
+ ta = &ca.anchors[ca.anchors_len - 1];
+ ta->dn.data = NULL;
+ ta->flags = 0;
+ if(br_x509_decoder_isCA(&ca.xc))
+ ta->flags |= BR_X509_TA_CA;
+ pkey = br_x509_decoder_get_pkey(&ca.xc);
+ if(!pkey) {
+ ca.err = CURLE_SSL_CACERT_BADFILE;
+ goto fail;
+ }
+ ta->pkey = *pkey;
+
+ /* calculate space needed for trust anchor data */
+ ta_size = ca.dn_len;
+ switch(pkey->key_type) {
+ case BR_KEYTYPE_RSA:
+ ta_size += pkey->key.rsa.nlen + pkey->key.rsa.elen;
+ break;
+ case BR_KEYTYPE_EC:
+ ta_size += pkey->key.ec.qlen;
+ break;
+ default:
+ ca.err = CURLE_FAILED_INIT;
+ goto fail;
+ }
+
+ /* fill in trust anchor DN and public key data */
+ ta->dn.data = malloc(ta_size);
+ if(!ta->dn.data) {
+ ca.err = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ memcpy(ta->dn.data, ca.dn, ca.dn_len);
+ ta->dn.len = ca.dn_len;
+ switch(pkey->key_type) {
+ case BR_KEYTYPE_RSA:
+ ta->pkey.key.rsa.n = ta->dn.data + ta->dn.len;
+ memcpy(ta->pkey.key.rsa.n, pkey->key.rsa.n, pkey->key.rsa.nlen);
+ ta->pkey.key.rsa.e = ta->pkey.key.rsa.n + ta->pkey.key.rsa.nlen;
+ memcpy(ta->pkey.key.rsa.e, pkey->key.rsa.e, pkey->key.rsa.elen);
+ break;
+ case BR_KEYTYPE_EC:
+ ta->pkey.key.ec.q = ta->dn.data + ta->dn.len;
+ memcpy(ta->pkey.key.ec.q, pkey->key.ec.q, pkey->key.ec.qlen);
+ break;
+ }
+ break;
+ default:
+ ca.err = CURLE_SSL_CACERT_BADFILE;
+ goto fail;
+ }
+ }
+ } while(source->type != CAFILE_SOURCE_BLOB);
+ if(fp && ferror(fp))
+ ca.err = CURLE_READ_ERROR;
+ else if(ca.in_cert)
+ ca.err = CURLE_SSL_CACERT_BADFILE;
+
+fail:
+ if(fp)
+ fclose(fp);
+ if(ca.err == CURLE_OK) {
+ *anchors = ca.anchors;
+ *anchors_len = ca.anchors_len;
+ }
+ else {
+ for(i = 0; i < ca.anchors_len; ++i)
+ free(ca.anchors[i].dn.data);
+ free(ca.anchors);
+ }
+
+ return ca.err;
+}
+
+static void x509_start_chain(const br_x509_class **ctx,
+ const char *server_name)
+{
+ struct x509_context *x509 = (struct x509_context *)ctx;
+
+ if(!x509->verifypeer) {
+ x509->cert_num = 0;
+ return;
+ }
+
+ if(!x509->verifyhost)
+ server_name = NULL;
+ x509->minimal.vtable->start_chain(&x509->minimal.vtable, server_name);
+}
+
+static void x509_start_cert(const br_x509_class **ctx, uint32_t length)
+{
+ struct x509_context *x509 = (struct x509_context *)ctx;
+
+ if(!x509->verifypeer) {
+ /* Only decode the first cert in the chain to obtain the public key */
+ if(x509->cert_num == 0)
+ br_x509_decoder_init(&x509->decoder, NULL, NULL);
+ return;
+ }
+
+ x509->minimal.vtable->start_cert(&x509->minimal.vtable, length);
+}
+
+static void x509_append(const br_x509_class **ctx, const unsigned char *buf,
+ size_t len)
+{
+ struct x509_context *x509 = (struct x509_context *)ctx;
+
+ if(!x509->verifypeer) {
+ if(x509->cert_num == 0)
+ br_x509_decoder_push(&x509->decoder, buf, len);
+ return;
+ }
+
+ x509->minimal.vtable->append(&x509->minimal.vtable, buf, len);
+}
+
+static void x509_end_cert(const br_x509_class **ctx)
+{
+ struct x509_context *x509 = (struct x509_context *)ctx;
+
+ if(!x509->verifypeer) {
+ x509->cert_num++;
+ return;
+ }
+
+ x509->minimal.vtable->end_cert(&x509->minimal.vtable);
+}
+
+static unsigned x509_end_chain(const br_x509_class **ctx)
+{
+ struct x509_context *x509 = (struct x509_context *)ctx;
+
+ if(!x509->verifypeer) {
+ return br_x509_decoder_last_error(&x509->decoder);
+ }
+
+ return x509->minimal.vtable->end_chain(&x509->minimal.vtable);
+}
+
+static const br_x509_pkey *x509_get_pkey(const br_x509_class *const *ctx,
+ unsigned *usages)
+{
+ struct x509_context *x509 = (struct x509_context *)ctx;
+
+ if(!x509->verifypeer) {
+ /* Nothing in the chain is verified, just return the public key of the
+ first certificate and allow its usage for both TLS_RSA_* and
+ TLS_ECDHE_* */
+ if(usages)
+ *usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN;
+ return br_x509_decoder_get_pkey(&x509->decoder);
+ }
+
+ return x509->minimal.vtable->get_pkey(&x509->minimal.vtable, usages);
+}
+
+static const br_x509_class x509_vtable = {
+ sizeof(struct x509_context),
+ x509_start_chain,
+ x509_start_cert,
+ x509_append,
+ x509_end_cert,
+ x509_end_chain,
+ x509_get_pkey
+};
+
+struct st_cipher {
+ const char *name; /* Cipher suite IANA name. It starts with "TLS_" prefix */
+ const char *alias_name; /* Alias name is the same as OpenSSL cipher name */
+ uint16_t num; /* BearSSL cipher suite */
+};
+
+/* Macro to initialize st_cipher data structure */
+#define CIPHER_DEF(num, alias) { #num, alias, BR_##num }
+
+static const struct st_cipher ciphertable[] = {
+ /* RFC 2246 TLS 1.0 */
+ CIPHER_DEF(TLS_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x000A */
+ "DES-CBC3-SHA"),
+
+ /* RFC 3268 TLS 1.0 AES */
+ CIPHER_DEF(TLS_RSA_WITH_AES_128_CBC_SHA, /* 0x002F */
+ "AES128-SHA"),
+ CIPHER_DEF(TLS_RSA_WITH_AES_256_CBC_SHA, /* 0x0035 */
+ "AES256-SHA"),
+
+ /* RFC 5246 TLS 1.2 */
+ CIPHER_DEF(TLS_RSA_WITH_AES_128_CBC_SHA256, /* 0x003C */
+ "AES128-SHA256"),
+ CIPHER_DEF(TLS_RSA_WITH_AES_256_CBC_SHA256, /* 0x003D */
+ "AES256-SHA256"),
+
+ /* RFC 5288 TLS 1.2 AES GCM */
+ CIPHER_DEF(TLS_RSA_WITH_AES_128_GCM_SHA256, /* 0x009C */
+ "AES128-GCM-SHA256"),
+ CIPHER_DEF(TLS_RSA_WITH_AES_256_GCM_SHA384, /* 0x009D */
+ "AES256-GCM-SHA384"),
+
+ /* RFC 4492 TLS 1.0 ECC */
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC003 */
+ "ECDH-ECDSA-DES-CBC3-SHA"),
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC004 */
+ "ECDH-ECDSA-AES128-SHA"),
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC005 */
+ "ECDH-ECDSA-AES256-SHA"),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC008 */
+ "ECDHE-ECDSA-DES-CBC3-SHA"),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC009 */
+ "ECDHE-ECDSA-AES128-SHA"),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC00A */
+ "ECDHE-ECDSA-AES256-SHA"),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC00D */
+ "ECDH-RSA-DES-CBC3-SHA"),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, /* 0xC00E */
+ "ECDH-RSA-AES128-SHA"),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, /* 0xC00F */
+ "ECDH-RSA-AES256-SHA"),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC012 */
+ "ECDHE-RSA-DES-CBC3-SHA"),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, /* 0xC013 */
+ "ECDHE-RSA-AES128-SHA"),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, /* 0xC014 */
+ "ECDHE-RSA-AES256-SHA"),
+
+ /* RFC 5289 TLS 1.2 ECC HMAC SHA256/384 */
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC023 */
+ "ECDHE-ECDSA-AES128-SHA256"),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC024 */
+ "ECDHE-ECDSA-AES256-SHA384"),
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC025 */
+ "ECDH-ECDSA-AES128-SHA256"),
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC026 */
+ "ECDH-ECDSA-AES256-SHA384"),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, /* 0xC027 */
+ "ECDHE-RSA-AES128-SHA256"),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, /* 0xC028 */
+ "ECDHE-RSA-AES256-SHA384"),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, /* 0xC029 */
+ "ECDH-RSA-AES128-SHA256"),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, /* 0xC02A */
+ "ECDH-RSA-AES256-SHA384"),
+
+ /* RFC 5289 TLS 1.2 GCM */
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02B */
+ "ECDHE-ECDSA-AES128-GCM-SHA256"),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02C */
+ "ECDHE-ECDSA-AES256-GCM-SHA384"),
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02D */
+ "ECDH-ECDSA-AES128-GCM-SHA256"),
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02E */
+ "ECDH-ECDSA-AES256-GCM-SHA384"),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* 0xC02F */
+ "ECDHE-RSA-AES128-GCM-SHA256"),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, /* 0xC030 */
+ "ECDHE-RSA-AES256-GCM-SHA384"),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, /* 0xC031 */
+ "ECDH-RSA-AES128-GCM-SHA256"),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, /* 0xC032 */
+ "ECDH-RSA-AES256-GCM-SHA384"),
+#ifdef BR_TLS_RSA_WITH_AES_128_CCM
+
+ /* RFC 6655 TLS 1.2 CCM
+ Supported since BearSSL 0.6 */
+ CIPHER_DEF(TLS_RSA_WITH_AES_128_CCM, /* 0xC09C */
+ "AES128-CCM"),
+ CIPHER_DEF(TLS_RSA_WITH_AES_256_CCM, /* 0xC09D */
+ "AES256-CCM"),
+ CIPHER_DEF(TLS_RSA_WITH_AES_128_CCM_8, /* 0xC0A0 */
+ "AES128-CCM8"),
+ CIPHER_DEF(TLS_RSA_WITH_AES_256_CCM_8, /* 0xC0A1 */
+ "AES256-CCM8"),
+
+ /* RFC 7251 TLS 1.2 ECC CCM
+ Supported since BearSSL 0.6 */
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CCM, /* 0xC0AC */
+ "ECDHE-ECDSA-AES128-CCM"),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CCM, /* 0xC0AD */
+ "ECDHE-ECDSA-AES256-CCM"),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, /* 0xC0AE */
+ "ECDHE-ECDSA-AES128-CCM8"),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, /* 0xC0AF */
+ "ECDHE-ECDSA-AES256-CCM8"),
+#endif
+
+ /* RFC 7905 TLS 1.2 ChaCha20-Poly1305
+ Supported since BearSSL 0.2 */
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA8 */
+ "ECDHE-RSA-CHACHA20-POLY1305"),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA9 */
+ "ECDHE-ECDSA-CHACHA20-POLY1305"),
+};
+
+#define NUM_OF_CIPHERS (sizeof(ciphertable) / sizeof(ciphertable[0]))
+#define CIPHER_NAME_BUF_LEN 64
+
+static bool is_separator(char c)
+{
+ /* Return whether character is a cipher list separator. */
+ switch(c) {
+ case ' ':
+ case '\t':
+ case ':':
+ case ',':
+ case ';':
+ return true;
+ }
+ return false;
+}
+
+static CURLcode bearssl_set_selected_ciphers(struct Curl_easy *data,
+ br_ssl_engine_context *ssl_eng,
+ const char *ciphers)
+{
+ uint16_t selected_ciphers[NUM_OF_CIPHERS];
+ size_t selected_count = 0;
+ char cipher_name[CIPHER_NAME_BUF_LEN];
+ const char *cipher_start = ciphers;
+ const char *cipher_end;
+ size_t i, j;
+
+ if(!cipher_start)
+ return CURLE_SSL_CIPHER;
+
+ while(true) {
+ /* Extract the next cipher name from the ciphers string */
+ while(is_separator(*cipher_start))
+ ++cipher_start;
+ if(*cipher_start == '\0')
+ break;
+ cipher_end = cipher_start;
+ while(*cipher_end != '\0' && !is_separator(*cipher_end))
+ ++cipher_end;
+ j = cipher_end - cipher_start < CIPHER_NAME_BUF_LEN - 1 ?
+ cipher_end - cipher_start : CIPHER_NAME_BUF_LEN - 1;
+ strncpy(cipher_name, cipher_start, j);
+ cipher_name[j] = '\0';
+ cipher_start = cipher_end;
+
+ /* Lookup the cipher name in the table of available ciphers. If the cipher
+ name starts with "TLS_" we do the lookup by IANA name. Otherwise, we try
+ to match cipher name by an (OpenSSL) alias. */
+ if(strncasecompare(cipher_name, "TLS_", 4)) {
+ for(i = 0; i < NUM_OF_CIPHERS &&
+ !strcasecompare(cipher_name, ciphertable[i].name); ++i);
+ }
+ else {
+ for(i = 0; i < NUM_OF_CIPHERS &&
+ !strcasecompare(cipher_name, ciphertable[i].alias_name); ++i);
+ }
+ if(i == NUM_OF_CIPHERS) {
+ infof(data, "BearSSL: unknown cipher in list: %s", cipher_name);
+ continue;
+ }
+
+ /* No duplicates allowed */
+ for(j = 0; j < selected_count &&
+ selected_ciphers[j] != ciphertable[i].num; j++);
+ if(j < selected_count) {
+ infof(data, "BearSSL: duplicate cipher in list: %s", cipher_name);
+ continue;
+ }
+
+ DEBUGASSERT(selected_count < NUM_OF_CIPHERS);
+ selected_ciphers[selected_count] = ciphertable[i].num;
+ ++selected_count;
+ }
+
+ if(selected_count == 0) {
+ failf(data, "BearSSL: no supported cipher in list");
+ return CURLE_SSL_CIPHER;
+ }
+
+ br_ssl_engine_set_suites(ssl_eng, selected_ciphers, selected_count);
+ return CURLE_OK;
+}
+
+static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
+ const char * const ssl_cafile =
+ /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
+ (ca_info_blob ? NULL : conn_config->CAfile);
+ const char *hostname = connssl->hostname;
+ const bool verifypeer = conn_config->verifypeer;
+ const bool verifyhost = conn_config->verifyhost;
+ CURLcode ret;
+ unsigned version_min, version_max;
+#ifdef ENABLE_IPV6
+ struct in6_addr addr;
+#else
+ struct in_addr addr;
+#endif
+
+ DEBUGASSERT(backend);
+
+ switch(conn_config->version) {
+ case CURL_SSLVERSION_SSLv2:
+ failf(data, "BearSSL does not support SSLv2");
+ return CURLE_SSL_CONNECT_ERROR;
+ case CURL_SSLVERSION_SSLv3:
+ failf(data, "BearSSL does not support SSLv3");
+ return CURLE_SSL_CONNECT_ERROR;
+ case CURL_SSLVERSION_TLSv1_0:
+ version_min = BR_TLS10;
+ version_max = BR_TLS10;
+ break;
+ case CURL_SSLVERSION_TLSv1_1:
+ version_min = BR_TLS11;
+ version_max = BR_TLS11;
+ break;
+ case CURL_SSLVERSION_TLSv1_2:
+ version_min = BR_TLS12;
+ version_max = BR_TLS12;
+ break;
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+ version_min = BR_TLS10;
+ version_max = BR_TLS12;
+ break;
+ default:
+ failf(data, "BearSSL: unknown CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ if(ca_info_blob) {
+ struct cafile_source source;
+ source.type = CAFILE_SOURCE_BLOB;
+ source.data = ca_info_blob->data;
+ source.len = ca_info_blob->len;
+
+ ret = load_cafile(&source, &backend->anchors, &backend->anchors_len);
+ if(ret != CURLE_OK) {
+ if(verifypeer) {
+ failf(data, "error importing CA certificate blob");
+ return ret;
+ }
+ /* Only warn if no certificate verification is required. */
+ infof(data, "error importing CA certificate blob, continuing anyway");
+ }
+ }
+
+ if(ssl_cafile) {
+ struct cafile_source source;
+ source.type = CAFILE_SOURCE_PATH;
+ source.data = ssl_cafile;
+ source.len = 0;
+
+ ret = load_cafile(&source, &backend->anchors, &backend->anchors_len);
+ if(ret != CURLE_OK) {
+ if(verifypeer) {
+ failf(data, "error setting certificate verify locations."
+ " CAfile: %s", ssl_cafile);
+ return ret;
+ }
+ infof(data, "error setting certificate verify locations,"
+ " continuing anyway:");
+ }
+ }
+
+ /* initialize SSL context */
+ br_ssl_client_init_full(&backend->ctx, &backend->x509.minimal,
+ backend->anchors, backend->anchors_len);
+ br_ssl_engine_set_versions(&backend->ctx.eng, version_min, version_max);
+ br_ssl_engine_set_buffer(&backend->ctx.eng, backend->buf,
+ sizeof(backend->buf), 1);
+
+ if(conn_config->cipher_list) {
+ /* Override the ciphers as specified. For the default cipher list see the
+ BearSSL source code of br_ssl_client_init_full() */
+ ret = bearssl_set_selected_ciphers(data, &backend->ctx.eng,
+ conn_config->cipher_list);
+ if(ret)
+ return ret;
+ }
+
+ /* initialize X.509 context */
+ backend->x509.vtable = &x509_vtable;
+ backend->x509.verifypeer = verifypeer;
+ backend->x509.verifyhost = verifyhost;
+ br_ssl_engine_set_x509(&backend->ctx.eng, &backend->x509.vtable);
+
+ if(ssl_config->primary.sessionid) {
+ void *session;
+
+ Curl_ssl_sessionid_lock(data);
+ if(!Curl_ssl_getsessionid(cf, data, &session, NULL)) {
+ br_ssl_engine_set_session_parameters(&backend->ctx.eng, session);
+ infof(data, "BearSSL: re-using session ID");
+ }
+ Curl_ssl_sessionid_unlock(data);
+ }
+
+ if(cf->conn->bits.tls_enable_alpn) {
+ int cur = 0;
+
+ /* NOTE: when adding more protocols here, increase the size of the
+ * protocols array in `struct ssl_backend_data`.
+ */
+
+#ifdef USE_HTTP2
+ if(data->state.httpwant >= CURL_HTTP_VERSION_2
+#ifndef CURL_DISABLE_PROXY
+ && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy)
+#endif
+ ) {
+ backend->protocols[cur++] = ALPN_H2;
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
+ }
+#endif
+
+ backend->protocols[cur++] = ALPN_HTTP_1_1;
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
+
+ br_ssl_engine_set_protocol_names(&backend->ctx.eng,
+ backend->protocols, cur);
+ }
+
+ if((1 == Curl_inet_pton(AF_INET, hostname, &addr))
+#ifdef ENABLE_IPV6
+ || (1 == Curl_inet_pton(AF_INET6, hostname, &addr))
+#endif
+ ) {
+ if(verifyhost) {
+ failf(data, "BearSSL: "
+ "host verification of IP address is not supported");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ hostname = NULL;
+ }
+ else {
+ char *snihost = Curl_ssl_snihost(data, hostname, NULL);
+ if(!snihost) {
+ failf(data, "Failed to set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ hostname = snihost;
+ }
+
+ /* give application a chance to interfere with SSL set up. */
+ if(data->set.ssl.fsslctx) {
+ Curl_set_in_callback(data, true);
+ ret = (*data->set.ssl.fsslctx)(data, &backend->ctx,
+ data->set.ssl.fsslctxp);
+ Curl_set_in_callback(data, false);
+ if(ret) {
+ failf(data, "BearSSL: error signaled by ssl ctx callback");
+ return ret;
+ }
+ }
+
+ if(!br_ssl_client_reset(&backend->ctx, hostname, 1))
+ return CURLE_FAILED_INIT;
+ backend->active = TRUE;
+
+ connssl->connecting_state = ssl_connect_2;
+
+ return CURLE_OK;
+}
+
+static CURLcode bearssl_run_until(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ unsigned target)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ unsigned state;
+ unsigned char *buf;
+ size_t len;
+ ssize_t ret;
+ CURLcode result;
+ int err;
+
+ DEBUGASSERT(backend);
+
+ for(;;) {
+ state = br_ssl_engine_current_state(&backend->ctx.eng);
+ if(state & BR_SSL_CLOSED) {
+ err = br_ssl_engine_last_error(&backend->ctx.eng);
+ switch(err) {
+ case BR_ERR_OK:
+ /* TLS close notify */
+ if(connssl->state != ssl_connection_complete) {
+ failf(data, "SSL: connection closed during handshake");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ return CURLE_OK;
+ case BR_ERR_X509_EXPIRED:
+ failf(data, "SSL: X.509 verification: "
+ "certificate is expired or not yet valid");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case BR_ERR_X509_BAD_SERVER_NAME:
+ failf(data, "SSL: X.509 verification: "
+ "expected server name was not found in the chain");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case BR_ERR_X509_NOT_TRUSTED:
+ failf(data, "SSL: X.509 verification: "
+ "chain could not be linked to a trust anchor");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ /* X.509 errors are documented to have the range 32..63 */
+ if(err >= 32 && err < 64)
+ return CURLE_PEER_FAILED_VERIFICATION;
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ if(state & target)
+ return CURLE_OK;
+ if(state & BR_SSL_SENDREC) {
+ buf = br_ssl_engine_sendrec_buf(&backend->ctx.eng, &len);
+ ret = Curl_conn_cf_send(cf->next, data, (char *)buf, len, &result);
+ if(ret <= 0) {
+ return result;
+ }
+ br_ssl_engine_sendrec_ack(&backend->ctx.eng, ret);
+ }
+ else if(state & BR_SSL_RECVREC) {
+ buf = br_ssl_engine_recvrec_buf(&backend->ctx.eng, &len);
+ ret = Curl_conn_cf_recv(cf->next, data, (char *)buf, len, &result);
+ if(ret == 0) {
+ failf(data, "SSL: EOF without close notify");
+ return CURLE_READ_ERROR;
+ }
+ if(ret <= 0) {
+ return result;
+ }
+ br_ssl_engine_recvrec_ack(&backend->ctx.eng, ret);
+ }
+ }
+}
+
+static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ CURLcode ret;
+
+ DEBUGASSERT(backend);
+
+ ret = bearssl_run_until(cf, data, BR_SSL_SENDAPP | BR_SSL_RECVAPP);
+ if(ret == CURLE_AGAIN)
+ return CURLE_OK;
+ if(ret == CURLE_OK) {
+ if(br_ssl_engine_current_state(&backend->ctx.eng) == BR_SSL_CLOSED) {
+ failf(data, "SSL: connection closed during handshake");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ connssl->connecting_state = ssl_connect_3;
+ }
+ return ret;
+}
+
+static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ CURLcode ret;
+
+ DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+ DEBUGASSERT(backend);
+
+ if(cf->conn->bits.tls_enable_alpn) {
+ const char *protocol;
+
+ protocol = br_ssl_engine_get_selected_protocol(&backend->ctx.eng);
+ if(protocol) {
+ infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, protocol);
+
+#ifdef USE_HTTP2
+ if(!strcmp(protocol, ALPN_H2))
+ cf->conn->alpn = CURL_HTTP_VERSION_2;
+ else
+#endif
+ if(!strcmp(protocol, ALPN_HTTP_1_1))
+ cf->conn->alpn = CURL_HTTP_VERSION_1_1;
+ else
+ infof(data, "ALPN, unrecognized protocol %s", protocol);
+ Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ }
+ else
+ infof(data, VTLS_INFOF_NO_ALPN);
+ }
+
+ if(ssl_config->primary.sessionid) {
+ bool incache;
+ bool added = FALSE;
+ void *oldsession;
+ br_ssl_session_parameters *session;
+
+ session = malloc(sizeof(*session));
+ if(!session)
+ return CURLE_OUT_OF_MEMORY;
+ br_ssl_engine_get_session_parameters(&backend->ctx.eng, session);
+ Curl_ssl_sessionid_lock(data);
+ incache = !(Curl_ssl_getsessionid(cf, data, &oldsession, NULL));
+ if(incache)
+ Curl_ssl_delsessionid(data, oldsession);
+ ret = Curl_ssl_addsessionid(cf, data, session, 0, &added);
+ Curl_ssl_sessionid_unlock(data);
+ if(!added)
+ free(session);
+ if(ret) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+
+ connssl->connecting_state = ssl_connect_done;
+
+ return CURLE_OK;
+}
+
+static ssize_t bearssl_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ unsigned char *app;
+ size_t applen;
+
+ DEBUGASSERT(backend);
+
+ for(;;) {
+ *err = bearssl_run_until(cf, data, BR_SSL_SENDAPP);
+ if (*err != CURLE_OK)
+ return -1;
+ app = br_ssl_engine_sendapp_buf(&backend->ctx.eng, &applen);
+ if(!app) {
+ failf(data, "SSL: connection closed during write");
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+ if(backend->pending_write) {
+ applen = backend->pending_write;
+ backend->pending_write = 0;
+ return applen;
+ }
+ if(applen > len)
+ applen = len;
+ memcpy(app, buf, applen);
+ br_ssl_engine_sendapp_ack(&backend->ctx.eng, applen);
+ br_ssl_engine_flush(&backend->ctx.eng, 0);
+ backend->pending_write = applen;
+ }
+}
+
+static ssize_t bearssl_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ unsigned char *app;
+ size_t applen;
+
+ DEBUGASSERT(backend);
+
+ *err = bearssl_run_until(cf, data, BR_SSL_RECVAPP);
+ if(*err != CURLE_OK)
+ return -1;
+ app = br_ssl_engine_recvapp_buf(&backend->ctx.eng, &applen);
+ if(!app)
+ return 0;
+ if(applen > len)
+ applen = len;
+ memcpy(buf, app, applen);
+ br_ssl_engine_recvapp_ack(&backend->ctx.eng, applen);
+
+ return applen;
+}
+
+static CURLcode bearssl_connect_common(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool nonblocking,
+ bool *done)
+{
+ CURLcode ret;
+ struct ssl_connect_data *connssl = cf->ctx;
+ curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ timediff_t timeout_ms;
+ int what;
+
+ /* check if the connection has already been established */
+ if(ssl_connection_complete == connssl->state) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ if(ssl_connect_1 == connssl->connecting_state) {
+ ret = bearssl_connect_step1(cf, data);
+ if(ret)
+ return ret;
+ }
+
+ while(ssl_connect_2 == connssl->connecting_state ||
+ ssl_connect_2_reading == connssl->connecting_state ||
+ ssl_connect_2_writing == connssl->connecting_state) {
+ /* check allowed time left */
+ timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+ if(timeout_ms < 0) {
+ /* no need to continue if time already is up */
+ failf(data, "SSL connection timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
+ /* if ssl is expecting something, check if it's available. */
+ if(ssl_connect_2_reading == connssl->connecting_state ||
+ ssl_connect_2_writing == connssl->connecting_state) {
+
+ curl_socket_t writefd = ssl_connect_2_writing ==
+ connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+ curl_socket_t readfd = ssl_connect_2_reading ==
+ connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+
+ what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
+ nonblocking?0:timeout_ms);
+ if(what < 0) {
+ /* fatal error */
+ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else if(0 == what) {
+ if(nonblocking) {
+ *done = FALSE;
+ return CURLE_OK;
+ }
+ else {
+ /* timeout */
+ failf(data, "SSL connection timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+ }
+ /* socket is readable or writable */
+ }
+
+ /* Run transaction, and return to the caller if it failed or if this
+ * connection is done nonblocking and this loop would execute again. This
+ * permits the owner of a multi handle to abort a connection attempt
+ * before step2 has completed while ensuring that a client using select()
+ * or epoll() will always have a valid fdset to wait on.
+ */
+ ret = bearssl_connect_step2(cf, data);
+ if(ret || (nonblocking &&
+ (ssl_connect_2 == connssl->connecting_state ||
+ ssl_connect_2_reading == connssl->connecting_state ||
+ ssl_connect_2_writing == connssl->connecting_state)))
+ return ret;
+ }
+
+ if(ssl_connect_3 == connssl->connecting_state) {
+ ret = bearssl_connect_step3(cf, data);
+ if(ret)
+ return ret;
+ }
+
+ if(ssl_connect_done == connssl->connecting_state) {
+ connssl->state = ssl_connection_complete;
+ *done = TRUE;
+ }
+ else
+ *done = FALSE;
+
+ /* Reset our connect state machine */
+ connssl->connecting_state = ssl_connect_1;
+
+ return CURLE_OK;
+}
+
+static size_t bearssl_version(char *buffer, size_t size)
+{
+ return msnprintf(buffer, size, "BearSSL");
+}
+
+static bool bearssl_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ struct ssl_connect_data *ctx = cf->ctx;
+
+ (void)data;
+ DEBUGASSERT(ctx && ctx->backend);
+ return br_ssl_engine_current_state(&ctx->backend->ctx.eng) & BR_SSL_RECVAPP;
+}
+
+static CURLcode bearssl_random(struct Curl_easy *data UNUSED_PARAM,
+ unsigned char *entropy, size_t length)
+{
+ static br_hmac_drbg_context ctx;
+ static bool seeded = FALSE;
+
+ if(!seeded) {
+ br_prng_seeder seeder;
+
+ br_hmac_drbg_init(&ctx, &br_sha256_vtable, NULL, 0);
+ seeder = br_prng_seeder_system(NULL);
+ if(!seeder || !seeder(&ctx.vtable))
+ return CURLE_FAILED_INIT;
+ seeded = TRUE;
+ }
+ br_hmac_drbg_generate(&ctx, entropy, length);
+
+ return CURLE_OK;
+}
+
+static CURLcode bearssl_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ CURLcode ret;
+ bool done = FALSE;
+
+ ret = bearssl_connect_common(cf, data, FALSE, &done);
+ if(ret)
+ return ret;
+
+ DEBUGASSERT(done);
+
+ return CURLE_OK;
+}
+
+static CURLcode bearssl_connect_nonblocking(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
+{
+ return bearssl_connect_common(cf, data, TRUE, done);
+}
+
+static void *bearssl_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
+{
+ struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
+ return &backend->ctx;
+}
+
+static void bearssl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ size_t i;
+
+ DEBUGASSERT(backend);
+
+ if(backend->active) {
+ backend->active = FALSE;
+ br_ssl_engine_close(&backend->ctx.eng);
+ (void)bearssl_run_until(cf, data, BR_SSL_CLOSED);
+ }
+ if(backend->anchors) {
+ for(i = 0; i < backend->anchors_len; ++i)
+ free(backend->anchors[i].dn.data);
+ Curl_safefree(backend->anchors);
+ }
+}
+
+static void bearssl_session_free(void *ptr)
+{
+ free(ptr);
+}
+
+static CURLcode bearssl_sha256sum(const unsigned char *input,
+ size_t inputlen,
+ unsigned char *sha256sum,
+ size_t sha256len UNUSED_PARAM)
+{
+ br_sha256_context ctx;
+
+ br_sha256_init(&ctx);
+ br_sha256_update(&ctx, input, inputlen);
+ br_sha256_out(&ctx, sha256sum);
+ return CURLE_OK;
+}
+
+const struct Curl_ssl Curl_ssl_bearssl = {
+ { CURLSSLBACKEND_BEARSSL, "bearssl" }, /* info */
+ SSLSUPP_CAINFO_BLOB | SSLSUPP_SSL_CTX | SSLSUPP_HTTPS_PROXY,
+ sizeof(struct ssl_backend_data),
+
+ Curl_none_init, /* init */
+ Curl_none_cleanup, /* cleanup */
+ bearssl_version, /* version */
+ Curl_none_check_cxn, /* check_cxn */
+ Curl_none_shutdown, /* shutdown */
+ bearssl_data_pending, /* data_pending */
+ bearssl_random, /* random */
+ Curl_none_cert_status_request, /* cert_status_request */
+ bearssl_connect, /* connect */
+ bearssl_connect_nonblocking, /* connect_nonblocking */
+ Curl_ssl_get_select_socks, /* getsock */
+ bearssl_get_internals, /* get_internals */
+ bearssl_close, /* close_one */
+ Curl_none_close_all, /* close_all */
+ bearssl_session_free, /* session_free */
+ Curl_none_set_engine, /* set_engine */
+ Curl_none_set_engine_default, /* set_engine_default */
+ Curl_none_engines_list, /* engines_list */
+ Curl_none_false_start, /* false_start */
+ bearssl_sha256sum, /* sha256sum */
+ NULL, /* associate_connection */
+ NULL, /* disassociate_connection */
+ NULL, /* free_multi_ssl_backend_data */
+ bearssl_recv, /* recv decrypted data */
+ bearssl_send, /* send data to encrypt */
+};
+
+#endif /* USE_BEARSSL */
diff --git a/libs/libcurl/src/vtls/bearssl.h b/libs/libcurl/src/vtls/bearssl.h index 5125359961..771c1a4b62 100644 --- a/libs/libcurl/src/vtls/bearssl.h +++ b/libs/libcurl/src/vtls/bearssl.h @@ -1,34 +1,34 @@ -#ifndef HEADER_CURL_BEARSSL_H -#define HEADER_CURL_BEARSSL_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2019 - 2022, Michael Forney, <mforney@mforney.org> - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifdef USE_BEARSSL - -extern const struct Curl_ssl Curl_ssl_bearssl; - -#endif /* USE_BEARSSL */ -#endif /* HEADER_CURL_BEARSSL_H */ +#ifndef HEADER_CURL_BEARSSL_H
+#define HEADER_CURL_BEARSSL_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2019 - 2022, Michael Forney, <mforney@mforney.org>
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_BEARSSL
+
+extern const struct Curl_ssl Curl_ssl_bearssl;
+
+#endif /* USE_BEARSSL */
+#endif /* HEADER_CURL_BEARSSL_H */
diff --git a/libs/libcurl/src/vtls/gskit.c b/libs/libcurl/src/vtls/gskit.c index 4ee4edea6c..ba452f56e8 100644 --- a/libs/libcurl/src/vtls/gskit.c +++ b/libs/libcurl/src/vtls/gskit.c @@ -1,1329 +1,1326 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_GSKIT - -#include <gskssl.h> -#include <qsoasync.h> -#undef HAVE_SOCKETPAIR /* because the native one isn't good enough */ -#include "socketpair.h" -#include "strerror.h" - -/* Some symbols are undefined/unsupported on OS400 versions < V7R1. */ -#ifndef GSK_SSL_EXTN_SERVERNAME_REQUEST -#define GSK_SSL_EXTN_SERVERNAME_REQUEST 230 -#endif - -#ifndef GSK_TLSV10_CIPHER_SPECS -#define GSK_TLSV10_CIPHER_SPECS 236 -#endif - -#ifndef GSK_TLSV11_CIPHER_SPECS -#define GSK_TLSV11_CIPHER_SPECS 237 -#endif - -#ifndef GSK_TLSV12_CIPHER_SPECS -#define GSK_TLSV12_CIPHER_SPECS 238 -#endif - -#ifndef GSK_PROTOCOL_TLSV11 -#define GSK_PROTOCOL_TLSV11 437 -#endif - -#ifndef GSK_PROTOCOL_TLSV12 -#define GSK_PROTOCOL_TLSV12 438 -#endif - -#ifndef GSK_FALSE -#define GSK_FALSE 0 -#endif - -#ifndef GSK_TRUE -#define GSK_TRUE 1 -#endif - - -#include <limits.h> - -#include <curl/curl.h> -#include "urldata.h" -#include "sendf.h" -#include "gskit.h" -#include "vtls.h" -#include "connect.h" /* for the connect timeout */ -#include "select.h" -#include "strcase.h" -#include "timediff.h" -#include "x509asn1.h" -#include "curl_printf.h" - -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - - -/* Directions. */ -#define SOS_READ 0x01 -#define SOS_WRITE 0x02 - -/* SSL version flags. */ -#define CURL_GSKPROTO_SSLV2 0 -#define CURL_GSKPROTO_SSLV2_MASK (1 << CURL_GSKPROTO_SSLV2) -#define CURL_GSKPROTO_SSLV3 1 -#define CURL_GSKPROTO_SSLV3_MASK (1 << CURL_GSKPROTO_SSLV3) -#define CURL_GSKPROTO_TLSV10 2 -#define CURL_GSKPROTO_TLSV10_MASK (1 << CURL_GSKPROTO_TLSV10) -#define CURL_GSKPROTO_TLSV11 3 -#define CURL_GSKPROTO_TLSV11_MASK (1 << CURL_GSKPROTO_TLSV11) -#define CURL_GSKPROTO_TLSV12 4 -#define CURL_GSKPROTO_TLSV12_MASK (1 << CURL_GSKPROTO_TLSV12) -#define CURL_GSKPROTO_LAST 5 - -struct ssl_backend_data { - gsk_handle handle; - int iocport; -#ifndef CURL_DISABLE_PROXY - int localfd; - int remotefd; -#endif -}; - -#define BACKEND connssl->backend - -/* Supported ciphers. */ -struct gskit_cipher { - const char *name; /* Cipher name. */ - const char *gsktoken; /* Corresponding token for GSKit String. */ - unsigned int versions; /* SSL version flags. */ -}; - -static const struct gskit_cipher ciphertable[] = { - { "null-md5", "01", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, - { "null-sha", "02", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, - { "exp-rc4-md5", "03", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK }, - { "rc4-md5", "04", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, - { "rc4-sha", "05", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, - { "exp-rc2-cbc-md5", "06", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK }, - { "exp-des-cbc-sha", "09", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK }, - { "des-cbc3-sha", "0A", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, - { "aes128-sha", "2F", - CURL_GSKPROTO_TLSV10_MASK | CURL_GSKPROTO_TLSV11_MASK | - CURL_GSKPROTO_TLSV12_MASK }, - { "aes256-sha", "35", - CURL_GSKPROTO_TLSV10_MASK | CURL_GSKPROTO_TLSV11_MASK | - CURL_GSKPROTO_TLSV12_MASK }, - { "null-sha256", "3B", CURL_GSKPROTO_TLSV12_MASK }, - { "aes128-sha256", "3C", CURL_GSKPROTO_TLSV12_MASK }, - { "aes256-sha256", "3D", CURL_GSKPROTO_TLSV12_MASK }, - { "aes128-gcm-sha256", - "9C", CURL_GSKPROTO_TLSV12_MASK }, - { "aes256-gcm-sha384", - "9D", CURL_GSKPROTO_TLSV12_MASK }, - { "rc4-md5", "1", CURL_GSKPROTO_SSLV2_MASK }, - { "exp-rc4-md5", "2", CURL_GSKPROTO_SSLV2_MASK }, - { "rc2-md5", "3", CURL_GSKPROTO_SSLV2_MASK }, - { "exp-rc2-md5", "4", CURL_GSKPROTO_SSLV2_MASK }, - { "des-cbc-md5", "6", CURL_GSKPROTO_SSLV2_MASK }, - { "des-cbc3-md5", "7", CURL_GSKPROTO_SSLV2_MASK }, - { (const char *) NULL, (const char *) NULL, 0 } -}; - - -static bool is_separator(char c) -{ - /* Return whether character is a cipher list separator. */ - switch(c) { - case ' ': - case '\t': - case ':': - case ',': - case ';': - return true; - } - return false; -} - - -static CURLcode gskit_status(struct Curl_easy *data, int rc, - const char *procname, CURLcode defcode) -{ - char buffer[STRERROR_LEN]; - /* Process GSKit status and map it to a CURLcode. */ - switch(rc) { - case GSK_OK: - case GSK_OS400_ASYNCHRONOUS_SOC_INIT: - return CURLE_OK; - case GSK_KEYRING_OPEN_ERROR: - case GSK_OS400_ERROR_NO_ACCESS: - return CURLE_SSL_CACERT_BADFILE; - case GSK_INSUFFICIENT_STORAGE: - return CURLE_OUT_OF_MEMORY; - case GSK_ERROR_BAD_V2_CIPHER: - case GSK_ERROR_BAD_V3_CIPHER: - case GSK_ERROR_NO_CIPHERS: - return CURLE_SSL_CIPHER; - case GSK_OS400_ERROR_NOT_TRUSTED_ROOT: - case GSK_ERROR_CERT_VALIDATION: - return CURLE_PEER_FAILED_VERIFICATION; - case GSK_OS400_ERROR_TIMED_OUT: - return CURLE_OPERATION_TIMEDOUT; - case GSK_WOULD_BLOCK: - return CURLE_AGAIN; - case GSK_OS400_ERROR_NOT_REGISTERED: - break; - case GSK_ERROR_IO: - switch(errno) { - case ENOMEM: - return CURLE_OUT_OF_MEMORY; - default: - failf(data, "%s I/O error: %s", procname, - Curl_strerror(errno, buffer, sizeof(buffer))); - break; - } - break; - default: - failf(data, "%s: %s", procname, gsk_strerror(rc)); - break; - } - return defcode; -} - - -static CURLcode set_enum(struct Curl_easy *data, gsk_handle h, - GSK_ENUM_ID id, GSK_ENUM_VALUE value, bool unsupported_ok) -{ - char buffer[STRERROR_LEN]; - int rc = gsk_attribute_set_enum(h, id, value); - - switch(rc) { - case GSK_OK: - return CURLE_OK; - case GSK_ERROR_IO: - failf(data, "gsk_attribute_set_enum() I/O error: %s", - Curl_strerror(errno, buffer, sizeof(buffer))); - break; - case GSK_ATTRIBUTE_INVALID_ID: - if(unsupported_ok) - return CURLE_UNSUPPORTED_PROTOCOL; - default: - failf(data, "gsk_attribute_set_enum(): %s", gsk_strerror(rc)); - break; - } - return CURLE_SSL_CONNECT_ERROR; -} - - -static CURLcode set_buffer(struct Curl_easy *data, gsk_handle h, - GSK_BUF_ID id, const char *buf, bool unsupported_ok) -{ - char buffer[STRERROR_LEN]; - int rc = gsk_attribute_set_buffer(h, id, buf, 0); - - switch(rc) { - case GSK_OK: - return CURLE_OK; - case GSK_ERROR_IO: - failf(data, "gsk_attribute_set_buffer() I/O error: %s", - Curl_strerror(errno, buffer, sizeof(buffer))); - break; - case GSK_ATTRIBUTE_INVALID_ID: - if(unsupported_ok) - return CURLE_UNSUPPORTED_PROTOCOL; - default: - failf(data, "gsk_attribute_set_buffer(): %s", gsk_strerror(rc)); - break; - } - return CURLE_SSL_CONNECT_ERROR; -} - - -static CURLcode set_numeric(struct Curl_easy *data, - gsk_handle h, GSK_NUM_ID id, int value) -{ - char buffer[STRERROR_LEN]; - int rc = gsk_attribute_set_numeric_value(h, id, value); - - switch(rc) { - case GSK_OK: - return CURLE_OK; - case GSK_ERROR_IO: - failf(data, "gsk_attribute_set_numeric_value() I/O error: %s", - Curl_strerror(errno, buffer, sizeof(buffer))); - break; - default: - failf(data, "gsk_attribute_set_numeric_value(): %s", gsk_strerror(rc)); - break; - } - return CURLE_SSL_CONNECT_ERROR; -} - - -static CURLcode set_ciphers(struct Curl_easy *data, - gsk_handle h, unsigned int *protoflags) -{ - struct connectdata *conn = data->conn; - const char *cipherlist = SSL_CONN_CONFIG(cipher_list); - const char *clp; - const struct gskit_cipher *ctp; - int i; - int l; - bool unsupported; - CURLcode result; - struct { - char *buf; - char *ptr; - } ciphers[CURL_GSKPROTO_LAST]; - - /* Compile cipher list into GSKit-compatible cipher lists. */ - - if(!cipherlist) - return CURLE_OK; - while(is_separator(*cipherlist)) /* Skip initial separators. */ - cipherlist++; - if(!*cipherlist) - return CURLE_OK; - - /* We allocate GSKit buffers of the same size as the input string: since - GSKit tokens are always shorter than their cipher names, allocated buffers - will always be large enough to accommodate the result. */ - l = strlen(cipherlist) + 1; - memset((char *) ciphers, 0, sizeof(ciphers)); - for(i = 0; i < CURL_GSKPROTO_LAST; i++) { - ciphers[i].buf = malloc(l); - if(!ciphers[i].buf) { - while(i--) - free(ciphers[i].buf); - return CURLE_OUT_OF_MEMORY; - } - ciphers[i].ptr = ciphers[i].buf; - *ciphers[i].ptr = '\0'; - } - - /* Process each cipher in input string. */ - unsupported = FALSE; - result = CURLE_OK; - for(;;) { - for(clp = cipherlist; *cipherlist && !is_separator(*cipherlist);) - cipherlist++; - l = cipherlist - clp; - if(!l) - break; - /* Search the cipher in our table. */ - for(ctp = ciphertable; ctp->name; ctp++) - if(strncasecompare(ctp->name, clp, l) && !ctp->name[l]) - break; - if(!ctp->name) { - failf(data, "Unknown cipher %.*s", l, clp); - result = CURLE_SSL_CIPHER; - } - else { - unsupported |= !(ctp->versions & (CURL_GSKPROTO_SSLV2_MASK | - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK)); - for(i = 0; i < CURL_GSKPROTO_LAST; i++) { - if(ctp->versions & (1 << i)) { - strcpy(ciphers[i].ptr, ctp->gsktoken); - ciphers[i].ptr += strlen(ctp->gsktoken); - } - } - } - - /* Advance to next cipher name or end of string. */ - while(is_separator(*cipherlist)) - cipherlist++; - } - - /* Disable protocols with empty cipher lists. */ - for(i = 0; i < CURL_GSKPROTO_LAST; i++) { - if(!(*protoflags & (1 << i)) || !ciphers[i].buf[0]) { - *protoflags &= ~(1 << i); - ciphers[i].buf[0] = '\0'; - } - } - - /* Try to set-up TLSv1.1 and TLSv2.1 ciphers. */ - if(*protoflags & CURL_GSKPROTO_TLSV11_MASK) { - result = set_buffer(data, h, GSK_TLSV11_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_TLSV11].buf, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - result = CURLE_OK; - if(unsupported) { - failf(data, "TLSv1.1-only ciphers are not yet supported"); - result = CURLE_SSL_CIPHER; - } - } - } - if(!result && (*protoflags & CURL_GSKPROTO_TLSV12_MASK)) { - result = set_buffer(data, h, GSK_TLSV12_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_TLSV12].buf, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - result = CURLE_OK; - if(unsupported) { - failf(data, "TLSv1.2-only ciphers are not yet supported"); - result = CURLE_SSL_CIPHER; - } - } - } - - /* Try to set-up TLSv1.0 ciphers. If not successful, concatenate them to - the SSLv3 ciphers. OS/400 prior to version 7.1 will understand it. */ - if(!result && (*protoflags & CURL_GSKPROTO_TLSV10_MASK)) { - result = set_buffer(data, h, GSK_TLSV10_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_TLSV10].buf, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - result = CURLE_OK; - strcpy(ciphers[CURL_GSKPROTO_SSLV3].ptr, - ciphers[CURL_GSKPROTO_TLSV10].ptr); - } - } - - /* Set-up other ciphers. */ - if(!result && (*protoflags & CURL_GSKPROTO_SSLV3_MASK)) - result = set_buffer(data, h, GSK_V3_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_SSLV3].buf, FALSE); - if(!result && (*protoflags & CURL_GSKPROTO_SSLV2_MASK)) - result = set_buffer(data, h, GSK_V2_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_SSLV2].buf, FALSE); - - /* Clean-up. */ - for(i = 0; i < CURL_GSKPROTO_LAST; i++) - free(ciphers[i].buf); - - return result; -} - - -static int gskit_init(void) -{ - /* No initialization needed. */ - return 1; -} - - -static void gskit_cleanup(void) -{ - /* Nothing to do. */ -} - - -static CURLcode init_environment(struct Curl_easy *data, - gsk_handle *envir, const char *appid, - const char *file, const char *label, - const char *password) -{ - int rc; - CURLcode result; - gsk_handle h; - - /* Creates the GSKit environment. */ - - rc = gsk_environment_open(&h); - switch(rc) { - case GSK_OK: - break; - case GSK_INSUFFICIENT_STORAGE: - return CURLE_OUT_OF_MEMORY; - default: - failf(data, "gsk_environment_open(): %s", gsk_strerror(rc)); - return CURLE_SSL_CONNECT_ERROR; - } - - result = set_enum(data, h, GSK_SESSION_TYPE, GSK_CLIENT_SESSION, FALSE); - if(!result && appid) - result = set_buffer(data, h, GSK_OS400_APPLICATION_ID, appid, FALSE); - if(!result && file) - result = set_buffer(data, h, GSK_KEYRING_FILE, file, FALSE); - if(!result && label) - result = set_buffer(data, h, GSK_KEYRING_LABEL, label, FALSE); - if(!result && password) - result = set_buffer(data, h, GSK_KEYRING_PW, password, FALSE); - - if(!result) { - /* Locate CAs, Client certificate and key according to our settings. - Note: this call may be blocking for some tenths of seconds. */ - result = gskit_status(data, gsk_environment_init(h), - "gsk_environment_init()", CURLE_SSL_CERTPROBLEM); - if(!result) { - *envir = h; - return result; - } - } - /* Error: rollback. */ - gsk_environment_close(&h); - return result; -} - - -static void cancel_async_handshake(struct connectdata *conn, int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - Qso_OverlappedIO_t cstat; - - DEBUGASSERT(BACKEND); - - if(QsoCancelOperation(conn->sock[sockindex], 0) > 0) - QsoWaitForIOCompletion(BACKEND->iocport, &cstat, (struct timeval *) NULL); -} - - -static void close_async_handshake(struct ssl_connect_data *connssl) -{ - DEBUGASSERT(BACKEND); - QsoDestroyIOCompletionPort(BACKEND->iocport); - BACKEND->iocport = -1; -} - -static int pipe_ssloverssl(struct connectdata *conn, int sockindex, - int directions) -{ -#ifndef CURL_DISABLE_PROXY - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_connect_data *connproxyssl = &conn->proxy_ssl[sockindex]; - struct pollfd fds[2]; - int n; - int m; - int i; - int ret = 0; - char buf[CURL_MAX_WRITE_SIZE]; - - DEBUGASSERT(BACKEND); - DEBUGASSERT(connproxyssl->backend); - - if(!connssl->use || !connproxyssl->use) - return 0; /* No SSL over SSL: OK. */ - - n = 1; - fds[0].fd = BACKEND->remotefd; - fds[1].fd = conn->sock[sockindex]; - - if(directions & SOS_READ) { - fds[0].events |= POLLOUT; - } - if(directions & SOS_WRITE) { - n = 2; - fds[0].events |= POLLIN; - fds[1].events |= POLLOUT; - } - i = Curl_poll(fds, n, 0); - if(i < 0) - return -1; /* Select error. */ - - if(fds[0].revents & POLLOUT) { - /* Try getting data from HTTPS proxy and pipe it upstream. */ - n = 0; - i = gsk_secure_soc_read(connproxyssl->backend->handle, - buf, sizeof(buf), &n); - switch(i) { - case GSK_OK: - if(n) { - i = write(BACKEND->remotefd, buf, n); - if(i < 0) - return -1; - ret = 1; - } - break; - case GSK_OS400_ERROR_TIMED_OUT: - case GSK_WOULD_BLOCK: - break; - default: - return -1; - } - } - - if((fds[0].revents & POLLIN) && (fds[1].revents & POLLOUT)) { - /* Pipe data to HTTPS proxy. */ - n = read(BACKEND->remotefd, buf, sizeof(buf)); - if(n < 0) - return -1; - if(n) { - i = gsk_secure_soc_write(connproxyssl->backend->handle, buf, n, &m); - if(i != GSK_OK || n != m) - return -1; - ret = 1; - } - } - - return ret; /* OK */ -#else - return 0; -#endif -} - - -static void close_one(struct ssl_connect_data *connssl, struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - DEBUGASSERT(BACKEND); - if(BACKEND->handle) { - gskit_status(data, gsk_secure_soc_close(&BACKEND->handle), - "gsk_secure_soc_close()", 0); - /* Last chance to drain output. */ - while(pipe_ssloverssl(conn, sockindex, SOS_WRITE) > 0) - ; - BACKEND->handle = (gsk_handle) NULL; -#ifndef CURL_DISABLE_PROXY - if(BACKEND->localfd >= 0) { - close(BACKEND->localfd); - BACKEND->localfd = -1; - } - if(BACKEND->remotefd >= 0) { - close(BACKEND->remotefd); - BACKEND->remotefd = -1; - } -#endif - } - if(BACKEND->iocport >= 0) - close_async_handshake(connssl); -} - - -static ssize_t gskit_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - CURLcode cc = CURLE_SEND_ERROR; - int written; - - DEBUGASSERT(BACKEND); - - if(pipe_ssloverssl(conn, sockindex, SOS_WRITE) >= 0) { - cc = gskit_status(data, - gsk_secure_soc_write(BACKEND->handle, - (char *) mem, (int) len, &written), - "gsk_secure_soc_write()", CURLE_SEND_ERROR); - if(cc == CURLE_OK) - if(pipe_ssloverssl(conn, sockindex, SOS_WRITE) < 0) - cc = CURLE_SEND_ERROR; - } - if(cc != CURLE_OK) { - *curlcode = cc; - written = -1; - } - return (ssize_t) written; /* number of bytes */ -} - - -static ssize_t gskit_recv(struct Curl_easy *data, int num, char *buf, - size_t buffersize, CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[num]; - int nread; - CURLcode cc = CURLE_RECV_ERROR; - - DEBUGASSERT(BACKEND); - - if(pipe_ssloverssl(conn, num, SOS_READ) >= 0) { - int buffsize = buffersize > (size_t) INT_MAX? INT_MAX: (int) buffersize; - cc = gskit_status(data, gsk_secure_soc_read(BACKEND->handle, - buf, buffsize, &nread), - "gsk_secure_soc_read()", CURLE_RECV_ERROR); - } - switch(cc) { - case CURLE_OK: - break; - case CURLE_OPERATION_TIMEDOUT: - cc = CURLE_AGAIN; - default: - *curlcode = cc; - nread = -1; - break; - } - return (ssize_t) nread; -} - -static CURLcode -set_ssl_version_min_max(unsigned int *protoflags, struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - long ssl_version = SSL_CONN_CONFIG(version); - long ssl_version_max = SSL_CONN_CONFIG(version_max); - long i = ssl_version; - switch(ssl_version_max) { - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_DEFAULT: - ssl_version_max = CURL_SSLVERSION_TLSv1_2; - break; - } - for(; i <= (ssl_version_max >> 16); ++i) { - switch(i) { - case CURL_SSLVERSION_TLSv1_0: - *protoflags |= CURL_GSKPROTO_TLSV10_MASK; - break; - case CURL_SSLVERSION_TLSv1_1: - *protoflags |= CURL_GSKPROTO_TLSV11_MASK; - break; - case CURL_SSLVERSION_TLSv1_2: - *protoflags |= CURL_GSKPROTO_TLSV11_MASK; - break; - case CURL_SSLVERSION_TLSv1_3: - failf(data, "GSKit: TLS 1.3 is not yet supported"); - return CURLE_SSL_CONNECT_ERROR; - } - } - - return CURLE_OK; -} - -static CURLcode gskit_connect_step1(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - gsk_handle envir; - CURLcode result; - const char * const keyringfile = SSL_CONN_CONFIG(CAfile); - const char * const keyringpwd = SSL_SET_OPTION(key_passwd); - const char * const keyringlabel = SSL_SET_OPTION(primary.clientcert); - const long int ssl_version = SSL_CONN_CONFIG(version); - const bool verifypeer = SSL_CONN_CONFIG(verifypeer); - const char * const hostname = SSL_HOST_NAME(); - const char *sni; - unsigned int protoflags = 0; - Qso_OverlappedIO_t commarea; -#ifndef CURL_DISABLE_PROXY - int sockpair[2]; - static const int sobufsize = CURL_MAX_WRITE_SIZE; -#endif - - /* Create SSL environment, start (preferably asynchronous) handshake. */ - DEBUGASSERT(BACKEND); - - BACKEND->handle = (gsk_handle) NULL; - BACKEND->iocport = -1; -#ifndef CURL_DISABLE_PROXY - BACKEND->localfd = -1; - BACKEND->remotefd = -1; -#endif - - /* GSKit supports two ways of specifying an SSL context: either by - * application identifier (that should have been defined at the system - * level) or by keyring file, password and certificate label. - * Local certificate name (CURLOPT_SSLCERT) is used to hold either the - * application identifier of the certificate label. - * Key password (CURLOPT_KEYPASSWD) holds the keyring password. - * It is not possible to have different keyrings for the CAs and the - * local certificate. We thus use the CA file (CURLOPT_CAINFO) to identify - * the keyring file. - * If no key password is given and the keyring is the system keyring, - * application identifier mode is tried first, as recommended in IBM doc. - */ - - envir = (gsk_handle) NULL; - - if(keyringlabel && *keyringlabel && !keyringpwd && - !strcmp(keyringfile, CURL_CA_BUNDLE)) { - /* Try application identifier mode. */ - init_environment(data, &envir, keyringlabel, (const char *) NULL, - (const char *) NULL, (const char *) NULL); - } - - if(!envir) { - /* Use keyring mode. */ - result = init_environment(data, &envir, (const char *) NULL, - keyringfile, keyringlabel, keyringpwd); - if(result) - return result; - } - - /* Create secure session. */ - result = gskit_status(data, gsk_secure_soc_open(envir, &BACKEND->handle), - "gsk_secure_soc_open()", CURLE_SSL_CONNECT_ERROR); - gsk_environment_close(&envir); - if(result) - return result; - -#ifndef CURL_DISABLE_PROXY - /* Establish a pipelining socket pair for SSL over SSL. */ - if(conn->proxy_ssl[sockindex].use) { - if(Curl_socketpair(0, 0, 0, sockpair)) - return CURLE_SSL_CONNECT_ERROR; - BACKEND->localfd = sockpair[0]; - BACKEND->remotefd = sockpair[1]; - setsockopt(BACKEND->localfd, SOL_SOCKET, SO_RCVBUF, - (void *) &sobufsize, sizeof(sobufsize)); - setsockopt(BACKEND->remotefd, SOL_SOCKET, SO_RCVBUF, - (void *) &sobufsize, sizeof(sobufsize)); - setsockopt(BACKEND->localfd, SOL_SOCKET, SO_SNDBUF, - (void *) &sobufsize, sizeof(sobufsize)); - setsockopt(BACKEND->remotefd, SOL_SOCKET, SO_SNDBUF, - (void *) &sobufsize, sizeof(sobufsize)); - curlx_nonblock(BACKEND->localfd, TRUE); - curlx_nonblock(BACKEND->remotefd, TRUE); - } -#endif - - /* Determine which SSL/TLS version should be enabled. */ - sni = hostname; - switch(ssl_version) { - case CURL_SSLVERSION_SSLv2: - protoflags = CURL_GSKPROTO_SSLV2_MASK; - sni = NULL; - break; - case CURL_SSLVERSION_SSLv3: - protoflags = CURL_GSKPROTO_SSLV3_MASK; - sni = NULL; - break; - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - protoflags = CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK; - break; - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_3: - result = set_ssl_version_min_max(&protoflags, data); - if(result != CURLE_OK) - return result; - break; - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } - - /* Process SNI. Ignore if not supported (on OS400 < V7R1). */ - if(sni) { - char *snihost = Curl_ssl_snihost(data, sni, NULL); - if(!snihost) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - result = set_buffer(data, BACKEND->handle, - GSK_SSL_EXTN_SERVERNAME_REQUEST, snihost, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) - result = CURLE_OK; - } - - /* Set session parameters. */ - if(!result) { - /* Compute the handshake timeout. Since GSKit granularity is 1 second, - we round up the required value. */ - timediff_t timeout = Curl_timeleft(data, NULL, TRUE); - if(timeout < 0) - result = CURLE_OPERATION_TIMEDOUT; - else - result = set_numeric(data, BACKEND->handle, GSK_HANDSHAKE_TIMEOUT, - (timeout + 999) / 1000); - } - if(!result) - result = set_numeric(data, BACKEND->handle, GSK_OS400_READ_TIMEOUT, 1); - if(!result) -#ifndef CURL_DISABLE_PROXY - result = set_numeric(data, BACKEND->handle, GSK_FD, BACKEND->localfd >= 0? - BACKEND->localfd: conn->sock[sockindex]); -#else - result = set_numeric(data, BACKEND->handle, GSK_FD, - conn->sock[sockindex]); -#endif - if(!result) - result = set_ciphers(data, BACKEND->handle, &protoflags); - if(!protoflags) { - failf(data, "No SSL protocol/cipher combination enabled"); - result = CURLE_SSL_CIPHER; - } - if(!result) - result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_SSLV2, - (protoflags & CURL_GSKPROTO_SSLV2_MASK)? - GSK_PROTOCOL_SSLV2_ON: GSK_PROTOCOL_SSLV2_OFF, FALSE); - if(!result) - result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_SSLV3, - (protoflags & CURL_GSKPROTO_SSLV3_MASK)? - GSK_PROTOCOL_SSLV3_ON: GSK_PROTOCOL_SSLV3_OFF, FALSE); - if(!result) - result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_TLSV1, - (protoflags & CURL_GSKPROTO_TLSV10_MASK)? - GSK_PROTOCOL_TLSV1_ON: GSK_PROTOCOL_TLSV1_OFF, FALSE); - if(!result) { - result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_TLSV11, - (protoflags & CURL_GSKPROTO_TLSV11_MASK)? - GSK_TRUE: GSK_FALSE, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - result = CURLE_OK; - if(protoflags == CURL_GSKPROTO_TLSV11_MASK) { - failf(data, "TLS 1.1 not yet supported"); - result = CURLE_SSL_CIPHER; - } - } - } - if(!result) { - result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_TLSV12, - (protoflags & CURL_GSKPROTO_TLSV12_MASK)? - GSK_TRUE: GSK_FALSE, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - result = CURLE_OK; - if(protoflags == CURL_GSKPROTO_TLSV12_MASK) { - failf(data, "TLS 1.2 not yet supported"); - result = CURLE_SSL_CIPHER; - } - } - } - if(!result) - result = set_enum(data, BACKEND->handle, GSK_SERVER_AUTH_TYPE, - verifypeer? GSK_SERVER_AUTH_FULL: - GSK_SERVER_AUTH_PASSTHRU, FALSE); - - if(!result) { - /* Start handshake. Try asynchronous first. */ - memset(&commarea, 0, sizeof(commarea)); - BACKEND->iocport = QsoCreateIOCompletionPort(); - if(BACKEND->iocport != -1) { - result = gskit_status(data, - gsk_secure_soc_startInit(BACKEND->handle, - BACKEND->iocport, - &commarea), - "gsk_secure_soc_startInit()", - CURLE_SSL_CONNECT_ERROR); - if(!result) { - connssl->connecting_state = ssl_connect_2; - return CURLE_OK; - } - else - close_async_handshake(connssl); - } - else if(errno != ENOBUFS) - result = gskit_status(data, GSK_ERROR_IO, - "QsoCreateIOCompletionPort()", 0); -#ifndef CURL_DISABLE_PROXY - else if(conn->proxy_ssl[sockindex].use) { - /* Cannot pipeline while handshaking synchronously. */ - result = CURLE_SSL_CONNECT_ERROR; - } -#endif - else { - /* No more completion port available. Use synchronous IO. */ - result = gskit_status(data, gsk_secure_soc_init(BACKEND->handle), - "gsk_secure_soc_init()", CURLE_SSL_CONNECT_ERROR); - if(!result) { - connssl->connecting_state = ssl_connect_3; - return CURLE_OK; - } - } - } - - /* Error: rollback. */ - close_one(connssl, data, conn, sockindex); - return result; -} - - -static CURLcode gskit_connect_step2(struct Curl_easy *data, - struct connectdata *conn, int sockindex, - bool nonblocking) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - Qso_OverlappedIO_t cstat; - struct timeval stmv; - CURLcode result; - - /* Poll or wait for end of SSL asynchronous handshake. */ - DEBUGASSERT(BACKEND); - - for(;;) { - timediff_t timeout_ms = nonblocking? 0: Curl_timeleft(data, NULL, TRUE); - stmv.tv_sec = 0; - stmv.tv_usec = 0; - if(timeout_ms < 0) - timeout_ms = 0; - switch(QsoWaitForIOCompletion(BACKEND->iocport, &cstat, - curlx_mstotv(&stmv, timeout_ms))) { - case 1: /* Operation complete. */ - break; - case -1: /* An error occurred: handshake still in progress. */ - if(errno == EINTR) { - if(nonblocking) - return CURLE_OK; - continue; /* Retry. */ - } - if(errno != ETIME) { - char buffer[STRERROR_LEN]; - failf(data, "QsoWaitForIOCompletion() I/O error: %s", - Curl_strerror(errno, buffer, sizeof(buffer))); - cancel_async_handshake(conn, sockindex); - close_async_handshake(connssl); - return CURLE_SSL_CONNECT_ERROR; - } - /* FALL INTO... */ - case 0: /* Handshake in progress, timeout occurred. */ - if(nonblocking) - return CURLE_OK; - cancel_async_handshake(conn, sockindex); - close_async_handshake(connssl); - return CURLE_OPERATION_TIMEDOUT; - } - break; - } - result = gskit_status(data, cstat.returnValue, "SSL handshake", - CURLE_SSL_CONNECT_ERROR); - if(!result) - connssl->connecting_state = ssl_connect_3; - close_async_handshake(connssl); - return result; -} - - -static CURLcode gskit_connect_step3(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - const gsk_cert_data_elem *cdev; - int cdec; - const gsk_cert_data_elem *p; - const char *cert = (const char *) NULL; - const char *certend; - const char *ptr; - CURLcode result; - - /* SSL handshake done: gather certificate info and verify host. */ - DEBUGASSERT(BACKEND); - - if(gskit_status(data, gsk_attribute_get_cert_info(BACKEND->handle, - GSK_PARTNER_CERT_INFO, - &cdev, &cdec), - "gsk_attribute_get_cert_info()", CURLE_SSL_CONNECT_ERROR) == - CURLE_OK) { - int i; - - infof(data, "Server certificate:"); - p = cdev; - for(i = 0; i++ < cdec; p++) - switch(p->cert_data_id) { - case CERT_BODY_DER: - cert = p->cert_data_p; - certend = cert + cdev->cert_data_l; - break; - case CERT_DN_PRINTABLE: - infof(data, "\t subject: %.*s", p->cert_data_l, p->cert_data_p); - break; - case CERT_ISSUER_DN_PRINTABLE: - infof(data, "\t issuer: %.*s", p->cert_data_l, p->cert_data_p); - break; - case CERT_VALID_FROM: - infof(data, "\t start date: %.*s", p->cert_data_l, p->cert_data_p); - break; - case CERT_VALID_TO: - infof(data, "\t expire date: %.*s", p->cert_data_l, p->cert_data_p); - break; - } - } - - /* Verify host. */ - result = Curl_verifyhost(data, conn, cert, certend); - if(result) - return result; - - /* The only place GSKit can get the whole CA chain is a validation - callback where no user data pointer is available. Therefore it's not - possible to copy this chain into our structures for CAINFO. - However the server certificate may be available, thus we can return - info about it. */ - if(data->set.ssl.certinfo) { - result = Curl_ssl_init_certinfo(data, 1); - if(result) - return result; - - if(cert) { - result = Curl_extract_certinfo(data, 0, cert, certend); - if(result) - return result; - } - } - - /* Check pinned public key. */ - ptr = SSL_PINNED_PUB_KEY(); - if(!result && ptr) { - struct Curl_X509certificate x509; - struct Curl_asn1Element *p; - - memset(&x509, 0, sizeof(x509)); - if(Curl_parseX509(&x509, cert, certend)) - return CURLE_SSL_PINNEDPUBKEYNOTMATCH; - p = &x509.subjectPublicKeyInfo; - result = Curl_pin_peer_pubkey(data, ptr, p->header, p->end - p->header); - if(result) { - failf(data, "SSL: public key does not match pinned public key"); - return result; - } - } - - connssl->connecting_state = ssl_connect_done; - return CURLE_OK; -} - - -static CURLcode gskit_connect_common(struct Curl_easy *data, - struct connectdata *conn, int sockindex, - bool nonblocking, bool *done) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - timediff_t timeout_ms; - CURLcode result = CURLE_OK; - - *done = connssl->state == ssl_connection_complete; - if(*done) - return CURLE_OK; - - /* Step 1: create session, start handshake. */ - if(connssl->connecting_state == ssl_connect_1) { - /* check allowed time left */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - result = CURLE_OPERATION_TIMEDOUT; - } - else - result = gskit_connect_step1(data, conn, sockindex); - } - - /* Handle handshake pipelining. */ - if(!result) - if(pipe_ssloverssl(conn, sockindex, SOS_READ | SOS_WRITE) < 0) - result = CURLE_SSL_CONNECT_ERROR; - - /* Step 2: check if handshake is over. */ - if(!result && connssl->connecting_state == ssl_connect_2) { - /* check allowed time left */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - result = CURLE_OPERATION_TIMEDOUT; - } - else - result = gskit_connect_step2(data, conn, sockindex, nonblocking); - } - - /* Handle handshake pipelining. */ - if(!result) - if(pipe_ssloverssl(conn, sockindex, SOS_READ | SOS_WRITE) < 0) - result = CURLE_SSL_CONNECT_ERROR; - - /* Step 3: gather certificate info, verify host. */ - if(!result && connssl->connecting_state == ssl_connect_3) - result = gskit_connect_step3(data, conn, sockindex); - - if(result) - close_one(connssl, data, conn, sockindex); - else if(connssl->connecting_state == ssl_connect_done) { - connssl->state = ssl_connection_complete; - connssl->connecting_state = ssl_connect_1; - conn->recv[sockindex] = gskit_recv; - conn->send[sockindex] = gskit_send; - *done = TRUE; - } - - return result; -} - - -static CURLcode gskit_connect_nonblocking(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, bool *done) -{ - CURLcode result; - - result = gskit_connect_common(data, conn, sockindex, TRUE, done); - if(*done || result) - conn->ssl[sockindex].connecting_state = ssl_connect_1; - return result; -} - - -static CURLcode gskit_connect(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - CURLcode result; - bool done; - - conn->ssl[sockindex].connecting_state = ssl_connect_1; - result = gskit_connect_common(data, conn, sockindex, FALSE, &done); - if(result) - return result; - - DEBUGASSERT(done); - - return CURLE_OK; -} - - -static void gskit_close(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - close_one(&conn->ssl[sockindex], data, conn, sockindex); -#ifndef CURL_DISABLE_PROXY - close_one(&conn->proxy_ssl[sockindex], data, conn, sockindex); -#endif -} - - -static int gskit_shutdown(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - int what; - int rc; - char buf[120]; - int loop = 10; /* don't get stuck */ - - DEBUGASSERT(BACKEND); - - if(!BACKEND->handle) - return 0; - -#ifndef CURL_DISABLE_FTP - if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE) - return 0; -#endif - - close_one(connssl, data, conn, sockindex); - rc = 0; - what = SOCKET_READABLE(conn->sock[sockindex], - SSL_SHUTDOWN_TIMEOUT); - - while(loop--) { - ssize_t nread; - - if(what < 0) { - /* anything that gets here is fatally bad */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - rc = -1; - break; - } - - if(!what) { /* timeout */ - failf(data, "SSL shutdown timeout"); - break; - } - - /* Something to read, let's do it and hope that it is the close - notify alert from the server. No way to gsk_secure_soc_read() now, so - use read(). */ - - nread = read(conn->sock[sockindex], buf, sizeof(buf)); - - if(nread < 0) { - char buffer[STRERROR_LEN]; - failf(data, "read: %s", Curl_strerror(errno, buffer, sizeof(buffer))); - rc = -1; - } - - if(nread <= 0) - break; - - what = SOCKET_READABLE(conn->sock[sockindex], 0); - } - - return rc; -} - - -static size_t gskit_version(char *buffer, size_t size) -{ - return msnprintf(buffer, size, "GSKit"); -} - - -static int gskit_check_cxn(struct connectdata *cxn) -{ - struct ssl_connect_data *connssl = &cxn->ssl[FIRSTSOCKET]; - int err; - int errlen; - - /* The only thing that can be tested here is at the socket level. */ - DEBUGASSERT(BACKEND); - - if(!BACKEND->handle) - return 0; /* connection has been closed */ - - err = 0; - errlen = sizeof(err); - - if(getsockopt(cxn->sock[FIRSTSOCKET], SOL_SOCKET, SO_ERROR, - (unsigned char *) &err, &errlen) || - errlen != sizeof(err) || err) - return 0; /* connection has been closed */ - - return -1; /* connection status unknown */ -} - -static void *gskit_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - (void)info; - DEBUGASSERT(BACKEND); - return BACKEND->handle; -} - -const struct Curl_ssl Curl_ssl_gskit = { - { CURLSSLBACKEND_GSKIT, "gskit" }, /* info */ - - SSLSUPP_CERTINFO | - SSLSUPP_PINNEDPUBKEY, - - sizeof(struct ssl_backend_data), - - gskit_init, /* init */ - gskit_cleanup, /* cleanup */ - gskit_version, /* version */ - gskit_check_cxn, /* check_cxn */ - gskit_shutdown, /* shutdown */ - Curl_none_data_pending, /* data_pending */ - Curl_none_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ - gskit_connect, /* connect */ - gskit_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_getsock, /* getsock */ - gskit_get_internals, /* get_internals */ - gskit_close, /* close_one */ - Curl_none_close_all, /* close_all */ - /* No session handling for GSKit */ - Curl_none_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ - NULL, /* sha256sum */ - NULL, /* associate_connection */ - NULL /* disassociate_connection */ -}; - -#endif /* USE_GSKIT */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_GSKIT
+
+#include <gskssl.h>
+#include <qsoasync.h>
+#undef HAVE_SOCKETPAIR /* because the native one isn't good enough */
+#include "socketpair.h"
+#include "strerror.h"
+
+/* Some symbols are undefined/unsupported on OS400 versions < V7R1. */
+#ifndef GSK_SSL_EXTN_SERVERNAME_REQUEST
+#define GSK_SSL_EXTN_SERVERNAME_REQUEST 230
+#endif
+
+#ifndef GSK_TLSV10_CIPHER_SPECS
+#define GSK_TLSV10_CIPHER_SPECS 236
+#endif
+
+#ifndef GSK_TLSV11_CIPHER_SPECS
+#define GSK_TLSV11_CIPHER_SPECS 237
+#endif
+
+#ifndef GSK_TLSV12_CIPHER_SPECS
+#define GSK_TLSV12_CIPHER_SPECS 238
+#endif
+
+#ifndef GSK_PROTOCOL_TLSV11
+#define GSK_PROTOCOL_TLSV11 437
+#endif
+
+#ifndef GSK_PROTOCOL_TLSV12
+#define GSK_PROTOCOL_TLSV12 438
+#endif
+
+#ifndef GSK_FALSE
+#define GSK_FALSE 0
+#endif
+
+#ifndef GSK_TRUE
+#define GSK_TRUE 1
+#endif
+
+
+#include <limits.h>
+
+#include <curl/curl.h>
+#include "urldata.h"
+#include "sendf.h"
+#include "gskit.h"
+#include "vtls.h"
+#include "vtls_int.h"
+#include "connect.h" /* for the connect timeout */
+#include "select.h"
+#include "strcase.h"
+#include "timediff.h"
+#include "x509asn1.h"
+#include "curl_printf.h"
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+
+/* Directions. */
+#define SOS_READ 0x01
+#define SOS_WRITE 0x02
+
+/* SSL version flags. */
+#define CURL_GSKPROTO_SSLV2 0
+#define CURL_GSKPROTO_SSLV2_MASK (1 << CURL_GSKPROTO_SSLV2)
+#define CURL_GSKPROTO_SSLV3 1
+#define CURL_GSKPROTO_SSLV3_MASK (1 << CURL_GSKPROTO_SSLV3)
+#define CURL_GSKPROTO_TLSV10 2
+#define CURL_GSKPROTO_TLSV10_MASK (1 << CURL_GSKPROTO_TLSV10)
+#define CURL_GSKPROTO_TLSV11 3
+#define CURL_GSKPROTO_TLSV11_MASK (1 << CURL_GSKPROTO_TLSV11)
+#define CURL_GSKPROTO_TLSV12 4
+#define CURL_GSKPROTO_TLSV12_MASK (1 << CURL_GSKPROTO_TLSV12)
+#define CURL_GSKPROTO_LAST 5
+
+struct ssl_backend_data {
+ gsk_handle handle;
+ int iocport;
+ int localfd;
+ int remotefd;
+};
+
+#define BACKEND connssl->backend
+
+/* Supported ciphers. */
+struct gskit_cipher {
+ const char *name; /* Cipher name. */
+ const char *gsktoken; /* Corresponding token for GSKit String. */
+ unsigned int versions; /* SSL version flags. */
+};
+
+static const struct gskit_cipher ciphertable[] = {
+ { "null-md5", "01",
+ CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK |
+ CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK },
+ { "null-sha", "02",
+ CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK |
+ CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK },
+ { "exp-rc4-md5", "03",
+ CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK },
+ { "rc4-md5", "04",
+ CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK |
+ CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK },
+ { "rc4-sha", "05",
+ CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK |
+ CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK },
+ { "exp-rc2-cbc-md5", "06",
+ CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK },
+ { "exp-des-cbc-sha", "09",
+ CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK |
+ CURL_GSKPROTO_TLSV11_MASK },
+ { "des-cbc3-sha", "0A",
+ CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK |
+ CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK },
+ { "aes128-sha", "2F",
+ CURL_GSKPROTO_TLSV10_MASK | CURL_GSKPROTO_TLSV11_MASK |
+ CURL_GSKPROTO_TLSV12_MASK },
+ { "aes256-sha", "35",
+ CURL_GSKPROTO_TLSV10_MASK | CURL_GSKPROTO_TLSV11_MASK |
+ CURL_GSKPROTO_TLSV12_MASK },
+ { "null-sha256", "3B", CURL_GSKPROTO_TLSV12_MASK },
+ { "aes128-sha256", "3C", CURL_GSKPROTO_TLSV12_MASK },
+ { "aes256-sha256", "3D", CURL_GSKPROTO_TLSV12_MASK },
+ { "aes128-gcm-sha256",
+ "9C", CURL_GSKPROTO_TLSV12_MASK },
+ { "aes256-gcm-sha384",
+ "9D", CURL_GSKPROTO_TLSV12_MASK },
+ { "rc4-md5", "1", CURL_GSKPROTO_SSLV2_MASK },
+ { "exp-rc4-md5", "2", CURL_GSKPROTO_SSLV2_MASK },
+ { "rc2-md5", "3", CURL_GSKPROTO_SSLV2_MASK },
+ { "exp-rc2-md5", "4", CURL_GSKPROTO_SSLV2_MASK },
+ { "des-cbc-md5", "6", CURL_GSKPROTO_SSLV2_MASK },
+ { "des-cbc3-md5", "7", CURL_GSKPROTO_SSLV2_MASK },
+ { (const char *) NULL, (const char *) NULL, 0 }
+};
+
+
+static bool is_separator(char c)
+{
+ /* Return whether character is a cipher list separator. */
+ switch(c) {
+ case ' ':
+ case '\t':
+ case ':':
+ case ',':
+ case ';':
+ return true;
+ }
+ return false;
+}
+
+
+static CURLcode gskit_status(struct Curl_easy *data, int rc,
+ const char *procname, CURLcode defcode)
+{
+ char buffer[STRERROR_LEN];
+ /* Process GSKit status and map it to a CURLcode. */
+ switch(rc) {
+ case GSK_OK:
+ case GSK_OS400_ASYNCHRONOUS_SOC_INIT:
+ return CURLE_OK;
+ case GSK_KEYRING_OPEN_ERROR:
+ case GSK_OS400_ERROR_NO_ACCESS:
+ return CURLE_SSL_CACERT_BADFILE;
+ case GSK_INSUFFICIENT_STORAGE:
+ return CURLE_OUT_OF_MEMORY;
+ case GSK_ERROR_BAD_V2_CIPHER:
+ case GSK_ERROR_BAD_V3_CIPHER:
+ case GSK_ERROR_NO_CIPHERS:
+ return CURLE_SSL_CIPHER;
+ case GSK_OS400_ERROR_NOT_TRUSTED_ROOT:
+ case GSK_ERROR_CERT_VALIDATION:
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case GSK_OS400_ERROR_TIMED_OUT:
+ return CURLE_OPERATION_TIMEDOUT;
+ case GSK_WOULD_BLOCK:
+ return CURLE_AGAIN;
+ case GSK_OS400_ERROR_NOT_REGISTERED:
+ break;
+ case GSK_ERROR_IO:
+ switch(errno) {
+ case ENOMEM:
+ return CURLE_OUT_OF_MEMORY;
+ default:
+ failf(data, "%s I/O error: %s", procname,
+ Curl_strerror(errno, buffer, sizeof(buffer)));
+ break;
+ }
+ break;
+ default:
+ failf(data, "%s: %s", procname, gsk_strerror(rc));
+ break;
+ }
+ return defcode;
+}
+
+
+static CURLcode set_enum(struct Curl_easy *data, gsk_handle h,
+ GSK_ENUM_ID id, GSK_ENUM_VALUE value, bool unsupported_ok)
+{
+ char buffer[STRERROR_LEN];
+ int rc = gsk_attribute_set_enum(h, id, value);
+
+ switch(rc) {
+ case GSK_OK:
+ return CURLE_OK;
+ case GSK_ERROR_IO:
+ failf(data, "gsk_attribute_set_enum() I/O error: %s",
+ Curl_strerror(errno, buffer, sizeof(buffer)));
+ break;
+ case GSK_ATTRIBUTE_INVALID_ID:
+ if(unsupported_ok)
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ default:
+ failf(data, "gsk_attribute_set_enum(): %s", gsk_strerror(rc));
+ break;
+ }
+ return CURLE_SSL_CONNECT_ERROR;
+}
+
+
+static CURLcode set_buffer(struct Curl_easy *data, gsk_handle h,
+ GSK_BUF_ID id, const char *buf, bool unsupported_ok)
+{
+ char buffer[STRERROR_LEN];
+ int rc = gsk_attribute_set_buffer(h, id, buf, 0);
+
+ switch(rc) {
+ case GSK_OK:
+ return CURLE_OK;
+ case GSK_ERROR_IO:
+ failf(data, "gsk_attribute_set_buffer() I/O error: %s",
+ Curl_strerror(errno, buffer, sizeof(buffer)));
+ break;
+ case GSK_ATTRIBUTE_INVALID_ID:
+ if(unsupported_ok)
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ default:
+ failf(data, "gsk_attribute_set_buffer(): %s", gsk_strerror(rc));
+ break;
+ }
+ return CURLE_SSL_CONNECT_ERROR;
+}
+
+
+static CURLcode set_numeric(struct Curl_easy *data,
+ gsk_handle h, GSK_NUM_ID id, int value)
+{
+ char buffer[STRERROR_LEN];
+ int rc = gsk_attribute_set_numeric_value(h, id, value);
+
+ switch(rc) {
+ case GSK_OK:
+ return CURLE_OK;
+ case GSK_ERROR_IO:
+ failf(data, "gsk_attribute_set_numeric_value() I/O error: %s",
+ Curl_strerror(errno, buffer, sizeof(buffer)));
+ break;
+ default:
+ failf(data, "gsk_attribute_set_numeric_value(): %s", gsk_strerror(rc));
+ break;
+ }
+ return CURLE_SSL_CONNECT_ERROR;
+}
+
+
+static CURLcode set_ciphers(struct Curl_cfilter *cf, struct Curl_easy *data,
+ gsk_handle h, unsigned int *protoflags)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct connectdata *conn = data->conn;
+ const char *cipherlist = conn_config->cipher_list;
+ const char *clp;
+ const struct gskit_cipher *ctp;
+ int i;
+ int l;
+ bool unsupported;
+ CURLcode result;
+ struct {
+ char *buf;
+ char *ptr;
+ } ciphers[CURL_GSKPROTO_LAST];
+
+ /* Compile cipher list into GSKit-compatible cipher lists. */
+
+ if(!cipherlist)
+ return CURLE_OK;
+ while(is_separator(*cipherlist)) /* Skip initial separators. */
+ cipherlist++;
+ if(!*cipherlist)
+ return CURLE_OK;
+
+ /* We allocate GSKit buffers of the same size as the input string: since
+ GSKit tokens are always shorter than their cipher names, allocated buffers
+ will always be large enough to accommodate the result. */
+ l = strlen(cipherlist) + 1;
+ memset(ciphers, 0, sizeof(ciphers));
+ for(i = 0; i < CURL_GSKPROTO_LAST; i++) {
+ ciphers[i].buf = malloc(l);
+ if(!ciphers[i].buf) {
+ while(i--)
+ free(ciphers[i].buf);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ ciphers[i].ptr = ciphers[i].buf;
+ *ciphers[i].ptr = '\0';
+ }
+
+ /* Process each cipher in input string. */
+ unsupported = FALSE;
+ result = CURLE_OK;
+ for(;;) {
+ for(clp = cipherlist; *cipherlist && !is_separator(*cipherlist);)
+ cipherlist++;
+ l = cipherlist - clp;
+ if(!l)
+ break;
+ /* Search the cipher in our table. */
+ for(ctp = ciphertable; ctp->name; ctp++)
+ if(strncasecompare(ctp->name, clp, l) && !ctp->name[l])
+ break;
+ if(!ctp->name) {
+ failf(data, "Unknown cipher %.*s", l, clp);
+ result = CURLE_SSL_CIPHER;
+ }
+ else {
+ unsupported |= !(ctp->versions & (CURL_GSKPROTO_SSLV2_MASK |
+ CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK));
+ for(i = 0; i < CURL_GSKPROTO_LAST; i++) {
+ if(ctp->versions & (1 << i)) {
+ strcpy(ciphers[i].ptr, ctp->gsktoken);
+ ciphers[i].ptr += strlen(ctp->gsktoken);
+ }
+ }
+ }
+
+ /* Advance to next cipher name or end of string. */
+ while(is_separator(*cipherlist))
+ cipherlist++;
+ }
+
+ /* Disable protocols with empty cipher lists. */
+ for(i = 0; i < CURL_GSKPROTO_LAST; i++) {
+ if(!(*protoflags & (1 << i)) || !ciphers[i].buf[0]) {
+ *protoflags &= ~(1 << i);
+ ciphers[i].buf[0] = '\0';
+ }
+ }
+
+ /* Try to set-up TLSv1.1 and TLSv2.1 ciphers. */
+ if(*protoflags & CURL_GSKPROTO_TLSV11_MASK) {
+ result = set_buffer(data, h, GSK_TLSV11_CIPHER_SPECS,
+ ciphers[CURL_GSKPROTO_TLSV11].buf, TRUE);
+ if(result == CURLE_UNSUPPORTED_PROTOCOL) {
+ result = CURLE_OK;
+ if(unsupported) {
+ failf(data, "TLSv1.1-only ciphers are not yet supported");
+ result = CURLE_SSL_CIPHER;
+ }
+ }
+ }
+ if(!result && (*protoflags & CURL_GSKPROTO_TLSV12_MASK)) {
+ result = set_buffer(data, h, GSK_TLSV12_CIPHER_SPECS,
+ ciphers[CURL_GSKPROTO_TLSV12].buf, TRUE);
+ if(result == CURLE_UNSUPPORTED_PROTOCOL) {
+ result = CURLE_OK;
+ if(unsupported) {
+ failf(data, "TLSv1.2-only ciphers are not yet supported");
+ result = CURLE_SSL_CIPHER;
+ }
+ }
+ }
+
+ /* Try to set-up TLSv1.0 ciphers. If not successful, concatenate them to
+ the SSLv3 ciphers. OS/400 prior to version 7.1 will understand it. */
+ if(!result && (*protoflags & CURL_GSKPROTO_TLSV10_MASK)) {
+ result = set_buffer(data, h, GSK_TLSV10_CIPHER_SPECS,
+ ciphers[CURL_GSKPROTO_TLSV10].buf, TRUE);
+ if(result == CURLE_UNSUPPORTED_PROTOCOL) {
+ result = CURLE_OK;
+ strcpy(ciphers[CURL_GSKPROTO_SSLV3].ptr,
+ ciphers[CURL_GSKPROTO_TLSV10].ptr);
+ }
+ }
+
+ /* Set-up other ciphers. */
+ if(!result && (*protoflags & CURL_GSKPROTO_SSLV3_MASK))
+ result = set_buffer(data, h, GSK_V3_CIPHER_SPECS,
+ ciphers[CURL_GSKPROTO_SSLV3].buf, FALSE);
+ if(!result && (*protoflags & CURL_GSKPROTO_SSLV2_MASK))
+ result = set_buffer(data, h, GSK_V2_CIPHER_SPECS,
+ ciphers[CURL_GSKPROTO_SSLV2].buf, FALSE);
+
+ /* Clean-up. */
+ for(i = 0; i < CURL_GSKPROTO_LAST; i++)
+ free(ciphers[i].buf);
+
+ return result;
+}
+
+
+static int gskit_init(void)
+{
+ /* No initialization needed. */
+ return 1;
+}
+
+
+static void gskit_cleanup(void)
+{
+ /* Nothing to do. */
+}
+
+
+static CURLcode init_environment(struct Curl_easy *data,
+ gsk_handle *envir, const char *appid,
+ const char *file, const char *label,
+ const char *password)
+{
+ int rc;
+ CURLcode result;
+ gsk_handle h;
+
+ /* Creates the GSKit environment. */
+
+ rc = gsk_environment_open(&h);
+ switch(rc) {
+ case GSK_OK:
+ break;
+ case GSK_INSUFFICIENT_STORAGE:
+ return CURLE_OUT_OF_MEMORY;
+ default:
+ failf(data, "gsk_environment_open(): %s", gsk_strerror(rc));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ result = set_enum(data, h, GSK_SESSION_TYPE, GSK_CLIENT_SESSION, FALSE);
+ if(!result && appid)
+ result = set_buffer(data, h, GSK_OS400_APPLICATION_ID, appid, FALSE);
+ if(!result && file)
+ result = set_buffer(data, h, GSK_KEYRING_FILE, file, FALSE);
+ if(!result && label)
+ result = set_buffer(data, h, GSK_KEYRING_LABEL, label, FALSE);
+ if(!result && password)
+ result = set_buffer(data, h, GSK_KEYRING_PW, password, FALSE);
+
+ if(!result) {
+ /* Locate CAs, Client certificate and key according to our settings.
+ Note: this call may be blocking for some tenths of seconds. */
+ result = gskit_status(data, gsk_environment_init(h),
+ "gsk_environment_init()", CURLE_SSL_CERTPROBLEM);
+ if(!result) {
+ *envir = h;
+ return result;
+ }
+ }
+ /* Error: rollback. */
+ gsk_environment_close(&h);
+ return result;
+}
+
+
+static void cancel_async_handshake(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ Qso_OverlappedIO_t cstat;
+
+ (void)data;
+ DEBUGASSERT(BACKEND);
+
+ if(QsoCancelOperation(cf->conn->sock[cf->sockindex], 0) > 0)
+ QsoWaitForIOCompletion(BACKEND->iocport, &cstat, (struct timeval *) NULL);
+}
+
+
+static void close_async_handshake(struct ssl_connect_data *connssl)
+{
+ DEBUGASSERT(BACKEND);
+ QsoDestroyIOCompletionPort(BACKEND->iocport);
+ BACKEND->iocport = -1;
+}
+
+static int pipe_ssloverssl(struct Curl_cfilter *cf, int directions)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next);
+ struct ssl_connect_data *connssl_next = cf_ssl_next?
+ cf_ssl_next->ctx : NULL;
+ struct pollfd fds[2];
+ int n;
+ int m;
+ int i;
+ int ret = 0;
+ char buf[CURL_MAX_WRITE_SIZE];
+
+ DEBUGASSERT(BACKEND);
+
+ if(!connssl_next)
+ return 0; /* No SSL over SSL: OK. */
+
+ DEBUGASSERT(connssl_next->backend);
+ n = 1;
+ fds[0].fd = BACKEND->remotefd;
+ fds[1].fd = cf->conn->sock[cf->sockindex];
+
+ if(directions & SOS_READ) {
+ fds[0].events |= POLLOUT;
+ }
+ if(directions & SOS_WRITE) {
+ n = 2;
+ fds[0].events |= POLLIN;
+ fds[1].events |= POLLOUT;
+ }
+ i = Curl_poll(fds, n, 0);
+ if(i < 0)
+ return -1; /* Select error. */
+
+ if(fds[0].revents & POLLOUT) {
+ /* Try getting data from HTTPS proxy and pipe it upstream. */
+ n = 0;
+ i = gsk_secure_soc_read(connssl_next->backend->handle,
+ buf, sizeof(buf), &n);
+ switch(i) {
+ case GSK_OK:
+ if(n) {
+ i = write(BACKEND->remotefd, buf, n);
+ if(i < 0)
+ return -1;
+ ret = 1;
+ }
+ break;
+ case GSK_OS400_ERROR_TIMED_OUT:
+ case GSK_WOULD_BLOCK:
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ if((fds[0].revents & POLLIN) && (fds[1].revents & POLLOUT)) {
+ /* Pipe data to HTTPS proxy. */
+ n = read(BACKEND->remotefd, buf, sizeof(buf));
+ if(n < 0)
+ return -1;
+ if(n) {
+ i = gsk_secure_soc_write(connssl_next->backend->handle, buf, n, &m);
+ if(i != GSK_OK || n != m)
+ return -1;
+ ret = 1;
+ }
+ }
+
+ return ret; /* OK */
+}
+
+
+static void close_one(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+
+ DEBUGASSERT(BACKEND);
+ if(BACKEND->handle) {
+ gskit_status(data, gsk_secure_soc_close(&BACKEND->handle),
+ "gsk_secure_soc_close()", 0);
+ /* Last chance to drain output. */
+ while(pipe_ssloverssl(cf, SOS_WRITE) > 0)
+ ;
+ BACKEND->handle = (gsk_handle) NULL;
+ if(BACKEND->localfd >= 0) {
+ close(BACKEND->localfd);
+ BACKEND->localfd = -1;
+ }
+ if(BACKEND->remotefd >= 0) {
+ close(BACKEND->remotefd);
+ BACKEND->remotefd = -1;
+ }
+ }
+ if(BACKEND->iocport >= 0)
+ close_async_handshake(connssl);
+}
+
+
+static ssize_t gskit_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *mem, size_t len, CURLcode *curlcode)
+{
+ struct connectdata *conn = cf->conn;
+ struct ssl_connect_data *connssl = cf->ctx;
+ CURLcode cc = CURLE_SEND_ERROR;
+ int written;
+
+ DEBUGASSERT(BACKEND);
+
+ if(pipe_ssloverssl(cf, SOS_WRITE) >= 0) {
+ cc = gskit_status(data,
+ gsk_secure_soc_write(BACKEND->handle,
+ (char *) mem, (int) len, &written),
+ "gsk_secure_soc_write()", CURLE_SEND_ERROR);
+ if(cc == CURLE_OK)
+ if(pipe_ssloverssl(cf, SOS_WRITE) < 0)
+ cc = CURLE_SEND_ERROR;
+ }
+ if(cc != CURLE_OK) {
+ *curlcode = cc;
+ written = -1;
+ }
+ return (ssize_t) written; /* number of bytes */
+}
+
+
+static ssize_t gskit_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t buffersize, CURLcode *curlcode)
+{
+ struct connectdata *conn = cf->conn;
+ struct ssl_connect_data *connssl = cf->ctx;
+ int nread;
+ CURLcode cc = CURLE_RECV_ERROR;
+
+ (void)data;
+ DEBUGASSERT(BACKEND);
+
+ if(pipe_ssloverssl(cf, SOS_READ) >= 0) {
+ int buffsize = buffersize > (size_t) INT_MAX? INT_MAX: (int) buffersize;
+ cc = gskit_status(data, gsk_secure_soc_read(BACKEND->handle,
+ buf, buffsize, &nread),
+ "gsk_secure_soc_read()", CURLE_RECV_ERROR);
+ }
+ switch(cc) {
+ case CURLE_OK:
+ break;
+ case CURLE_OPERATION_TIMEDOUT:
+ cc = CURLE_AGAIN;
+ default:
+ *curlcode = cc;
+ nread = -1;
+ break;
+ }
+ return (ssize_t) nread;
+}
+
+static CURLcode
+set_ssl_version_min_max(unsigned int *protoflags,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct connectdata *conn = data->conn;
+ long ssl_version = conn_config->version;
+ long ssl_version_max = conn_config->version_max;
+ long i = ssl_version;
+ switch(ssl_version_max) {
+ case CURL_SSLVERSION_MAX_NONE:
+ case CURL_SSLVERSION_MAX_DEFAULT:
+ ssl_version_max = CURL_SSLVERSION_TLSv1_2;
+ break;
+ }
+ for(; i <= (ssl_version_max >> 16); ++i) {
+ switch(i) {
+ case CURL_SSLVERSION_TLSv1_0:
+ *protoflags |= CURL_GSKPROTO_TLSV10_MASK;
+ break;
+ case CURL_SSLVERSION_TLSv1_1:
+ *protoflags |= CURL_GSKPROTO_TLSV11_MASK;
+ break;
+ case CURL_SSLVERSION_TLSv1_2:
+ *protoflags |= CURL_GSKPROTO_TLSV11_MASK;
+ break;
+ case CURL_SSLVERSION_TLSv1_3:
+ failf(data, "GSKit: TLS 1.3 is not yet supported");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode gskit_connect_step1(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next);
+ struct ssl_connect_data *connssl_next = cf_ssl_next?
+ cf_ssl_next->ctx : NULL;
+ gsk_handle envir;
+ CURLcode result;
+ const char * const keyringfile = conn_config->CAfile;
+ const char * const keyringpwd = conn_config->key_passwd;
+ const char * const keyringlabel = ssl_config->primary.clientcert;
+ const long int ssl_version = conn_config->version;
+ const bool verifypeer = conn_config->verifypeer;
+ const char *hostname = connssl->hostname;
+ const char *sni;
+ unsigned int protoflags = 0;
+ Qso_OverlappedIO_t commarea;
+ int sockpair[2];
+ static const int sobufsize = CURL_MAX_WRITE_SIZE;
+
+ /* Create SSL environment, start (preferably asynchronous) handshake. */
+ DEBUGASSERT(BACKEND);
+
+ BACKEND->handle = (gsk_handle) NULL;
+ BACKEND->iocport = -1;
+ BACKEND->localfd = -1;
+ BACKEND->remotefd = -1;
+
+ /* GSKit supports two ways of specifying an SSL context: either by
+ * application identifier (that should have been defined at the system
+ * level) or by keyring file, password and certificate label.
+ * Local certificate name (CURLOPT_SSLCERT) is used to hold either the
+ * application identifier of the certificate label.
+ * Key password (CURLOPT_KEYPASSWD) holds the keyring password.
+ * It is not possible to have different keyrings for the CAs and the
+ * local certificate. We thus use the CA file (CURLOPT_CAINFO) to identify
+ * the keyring file.
+ * If no key password is given and the keyring is the system keyring,
+ * application identifier mode is tried first, as recommended in IBM doc.
+ */
+
+ envir = (gsk_handle) NULL;
+
+ if(keyringlabel && *keyringlabel && !keyringpwd &&
+ !strcmp(keyringfile, CURL_CA_BUNDLE)) {
+ /* Try application identifier mode. */
+ init_environment(data, &envir, keyringlabel, (const char *) NULL,
+ (const char *) NULL, (const char *) NULL);
+ }
+
+ if(!envir) {
+ /* Use keyring mode. */
+ result = init_environment(data, &envir, (const char *) NULL,
+ keyringfile, keyringlabel, keyringpwd);
+ if(result)
+ return result;
+ }
+
+ /* Create secure session. */
+ result = gskit_status(data, gsk_secure_soc_open(envir, &BACKEND->handle),
+ "gsk_secure_soc_open()", CURLE_SSL_CONNECT_ERROR);
+ gsk_environment_close(&envir);
+ if(result)
+ return result;
+
+ /* Establish a pipelining socket pair for SSL over SSL. */
+ if(connssl_next) {
+ if(Curl_socketpair(0, 0, 0, sockpair))
+ return CURLE_SSL_CONNECT_ERROR;
+ BACKEND->localfd = sockpair[0];
+ BACKEND->remotefd = sockpair[1];
+ setsockopt(BACKEND->localfd, SOL_SOCKET, SO_RCVBUF,
+ (void *) &sobufsize, sizeof(sobufsize));
+ setsockopt(BACKEND->remotefd, SOL_SOCKET, SO_RCVBUF,
+ (void *) &sobufsize, sizeof(sobufsize));
+ setsockopt(BACKEND->localfd, SOL_SOCKET, SO_SNDBUF,
+ (void *) &sobufsize, sizeof(sobufsize));
+ setsockopt(BACKEND->remotefd, SOL_SOCKET, SO_SNDBUF,
+ (void *) &sobufsize, sizeof(sobufsize));
+ curlx_nonblock(BACKEND->localfd, TRUE);
+ curlx_nonblock(BACKEND->remotefd, TRUE);
+ }
+
+ /* Determine which SSL/TLS version should be enabled. */
+ sni = hostname;
+ switch(ssl_version) {
+ case CURL_SSLVERSION_SSLv2:
+ protoflags = CURL_GSKPROTO_SSLV2_MASK;
+ sni = NULL;
+ break;
+ case CURL_SSLVERSION_SSLv3:
+ protoflags = CURL_GSKPROTO_SSLV3_MASK;
+ sni = NULL;
+ break;
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+ protoflags = CURL_GSKPROTO_TLSV10_MASK |
+ CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK;
+ break;
+ case CURL_SSLVERSION_TLSv1_0:
+ case CURL_SSLVERSION_TLSv1_1:
+ case CURL_SSLVERSION_TLSv1_2:
+ case CURL_SSLVERSION_TLSv1_3:
+ result = set_ssl_version_min_max(&protoflags, cf, data);
+ if(result != CURLE_OK)
+ return result;
+ break;
+ default:
+ failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ /* Process SNI. Ignore if not supported (on OS400 < V7R1). */
+ if(sni) {
+ char *snihost = Curl_ssl_snihost(data, sni, NULL);
+ if(!snihost) {
+ failf(data, "Failed to set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ result = set_buffer(data, BACKEND->handle,
+ GSK_SSL_EXTN_SERVERNAME_REQUEST, snihost, TRUE);
+ if(result == CURLE_UNSUPPORTED_PROTOCOL)
+ result = CURLE_OK;
+ }
+
+ /* Set session parameters. */
+ if(!result) {
+ /* Compute the handshake timeout. Since GSKit granularity is 1 second,
+ we round up the required value. */
+ timediff_t timeout = Curl_timeleft(data, NULL, TRUE);
+ if(timeout < 0)
+ result = CURLE_OPERATION_TIMEDOUT;
+ else
+ result = set_numeric(data, BACKEND->handle, GSK_HANDSHAKE_TIMEOUT,
+ (timeout + 999) / 1000);
+ }
+ if(!result)
+ result = set_numeric(data, BACKEND->handle, GSK_OS400_READ_TIMEOUT, 1);
+ if(!result)
+ result = set_numeric(data, BACKEND->handle, GSK_FD, BACKEND->localfd >= 0?
+ BACKEND->localfd: cf->conn->sock[cf->sockindex]);
+ if(!result)
+ result = set_ciphers(cf, data, BACKEND->handle, &protoflags);
+ if(!protoflags) {
+ failf(data, "No SSL protocol/cipher combination enabled");
+ result = CURLE_SSL_CIPHER;
+ }
+ if(!result)
+ result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_SSLV2,
+ (protoflags & CURL_GSKPROTO_SSLV2_MASK)?
+ GSK_PROTOCOL_SSLV2_ON: GSK_PROTOCOL_SSLV2_OFF, FALSE);
+ if(!result)
+ result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_SSLV3,
+ (protoflags & CURL_GSKPROTO_SSLV3_MASK)?
+ GSK_PROTOCOL_SSLV3_ON: GSK_PROTOCOL_SSLV3_OFF, FALSE);
+ if(!result)
+ result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_TLSV1,
+ (protoflags & CURL_GSKPROTO_TLSV10_MASK)?
+ GSK_PROTOCOL_TLSV1_ON: GSK_PROTOCOL_TLSV1_OFF, FALSE);
+ if(!result) {
+ result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_TLSV11,
+ (protoflags & CURL_GSKPROTO_TLSV11_MASK)?
+ GSK_TRUE: GSK_FALSE, TRUE);
+ if(result == CURLE_UNSUPPORTED_PROTOCOL) {
+ result = CURLE_OK;
+ if(protoflags == CURL_GSKPROTO_TLSV11_MASK) {
+ failf(data, "TLS 1.1 not yet supported");
+ result = CURLE_SSL_CIPHER;
+ }
+ }
+ }
+ if(!result) {
+ result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_TLSV12,
+ (protoflags & CURL_GSKPROTO_TLSV12_MASK)?
+ GSK_TRUE: GSK_FALSE, TRUE);
+ if(result == CURLE_UNSUPPORTED_PROTOCOL) {
+ result = CURLE_OK;
+ if(protoflags == CURL_GSKPROTO_TLSV12_MASK) {
+ failf(data, "TLS 1.2 not yet supported");
+ result = CURLE_SSL_CIPHER;
+ }
+ }
+ }
+ if(!result)
+ result = set_enum(data, BACKEND->handle, GSK_SERVER_AUTH_TYPE,
+ verifypeer? GSK_SERVER_AUTH_FULL:
+ GSK_SERVER_AUTH_PASSTHRU, FALSE);
+
+ if(!result) {
+ /* Start handshake. Try asynchronous first. */
+ memset(&commarea, 0, sizeof(commarea));
+ BACKEND->iocport = QsoCreateIOCompletionPort();
+ if(BACKEND->iocport != -1) {
+ result = gskit_status(data,
+ gsk_secure_soc_startInit(BACKEND->handle,
+ BACKEND->iocport,
+ &commarea),
+ "gsk_secure_soc_startInit()",
+ CURLE_SSL_CONNECT_ERROR);
+ if(!result) {
+ connssl->connecting_state = ssl_connect_2;
+ return CURLE_OK;
+ }
+ else
+ close_async_handshake(connssl);
+ }
+ else if(errno != ENOBUFS)
+ result = gskit_status(data, GSK_ERROR_IO,
+ "QsoCreateIOCompletionPort()", 0);
+ else if(connssl_next) {
+ /* Cannot pipeline while handshaking synchronously. */
+ result = CURLE_SSL_CONNECT_ERROR;
+ }
+ else {
+ /* No more completion port available. Use synchronous IO. */
+ result = gskit_status(data, gsk_secure_soc_init(BACKEND->handle),
+ "gsk_secure_soc_init()", CURLE_SSL_CONNECT_ERROR);
+ if(!result) {
+ connssl->connecting_state = ssl_connect_3;
+ return CURLE_OK;
+ }
+ }
+ }
+
+ /* Error: rollback. */
+ close_one(connssl, data, conn, sockindex);
+ return result;
+}
+
+
+static CURLcode gskit_connect_step2(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool nonblocking)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ Qso_OverlappedIO_t cstat;
+ struct timeval stmv;
+ CURLcode result;
+
+ /* Poll or wait for end of SSL asynchronous handshake. */
+ DEBUGASSERT(BACKEND);
+
+ for(;;) {
+ timediff_t timeout_ms = nonblocking? 0: Curl_timeleft(data, NULL, TRUE);
+ stmv.tv_sec = 0;
+ stmv.tv_usec = 0;
+ if(timeout_ms < 0)
+ timeout_ms = 0;
+ switch(QsoWaitForIOCompletion(BACKEND->iocport, &cstat,
+ curlx_mstotv(&stmv, timeout_ms))) {
+ case 1: /* Operation complete. */
+ break;
+ case -1: /* An error occurred: handshake still in progress. */
+ if(errno == EINTR) {
+ if(nonblocking)
+ return CURLE_OK;
+ continue; /* Retry. */
+ }
+ if(errno != ETIME) {
+ char buffer[STRERROR_LEN];
+ failf(data, "QsoWaitForIOCompletion() I/O error: %s",
+ Curl_strerror(errno, buffer, sizeof(buffer)));
+ cancel_async_handshake(cf, data);
+ close_async_handshake(connssl);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ /* FALL INTO... */
+ case 0: /* Handshake in progress, timeout occurred. */
+ if(nonblocking)
+ return CURLE_OK;
+ cancel_async_handshake(cf, data);
+ close_async_handshake(connssl);
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+ break;
+ }
+ result = gskit_status(data, cstat.returnValue, "SSL handshake",
+ CURLE_SSL_CONNECT_ERROR);
+ if(!result)
+ connssl->connecting_state = ssl_connect_3;
+ close_async_handshake(connssl);
+ return result;
+}
+
+
+static CURLcode gskit_connect_step3(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ const gsk_cert_data_elem *cdev;
+ int cdec;
+ const gsk_cert_data_elem *p;
+ const char *cert = (const char *) NULL;
+ const char *certend = (const char *) NULL;
+ const char *ptr;
+ CURLcode result;
+
+ /* SSL handshake done: gather certificate info and verify host. */
+ DEBUGASSERT(BACKEND);
+
+ if(gskit_status(data, gsk_attribute_get_cert_info(BACKEND->handle,
+ GSK_PARTNER_CERT_INFO,
+ &cdev, &cdec),
+ "gsk_attribute_get_cert_info()", CURLE_SSL_CONNECT_ERROR) ==
+ CURLE_OK) {
+ int i;
+
+ infof(data, "Server certificate:");
+ p = cdev;
+ for(i = 0; i++ < cdec; p++)
+ switch(p->cert_data_id) {
+ case CERT_BODY_DER:
+ cert = p->cert_data_p;
+ certend = cert + cdev->cert_data_l;
+ break;
+ case CERT_DN_PRINTABLE:
+ infof(data, "\t subject: %.*s", p->cert_data_l, p->cert_data_p);
+ break;
+ case CERT_ISSUER_DN_PRINTABLE:
+ infof(data, "\t issuer: %.*s", p->cert_data_l, p->cert_data_p);
+ break;
+ case CERT_VALID_FROM:
+ infof(data, "\t start date: %.*s", p->cert_data_l, p->cert_data_p);
+ break;
+ case CERT_VALID_TO:
+ infof(data, "\t expire date: %.*s", p->cert_data_l, p->cert_data_p);
+ break;
+ }
+ }
+
+ /* Verify host. */
+ result = Curl_verifyhost(cf, data, cert, certend);
+ if(result)
+ return result;
+
+ /* The only place GSKit can get the whole CA chain is a validation
+ callback where no user data pointer is available. Therefore it's not
+ possible to copy this chain into our structures for CAINFO.
+ However the server certificate may be available, thus we can return
+ info about it. */
+ if(data->set.ssl.certinfo) {
+ result = Curl_ssl_init_certinfo(data, 1);
+ if(result)
+ return result;
+
+ if(cert) {
+ result = Curl_extract_certinfo(data, 0, cert, certend);
+ if(result)
+ return result;
+ }
+ }
+
+ /* Check pinned public key. */
+ ptr = Curl_ssl_cf_is_proxy(cf)?
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY];
+ if(!result && ptr) {
+ struct Curl_X509certificate x509;
+ struct Curl_asn1Element *p;
+
+ memset(&x509, 0, sizeof(x509));
+ if(Curl_parseX509(&x509, cert, certend))
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ p = &x509.subjectPublicKeyInfo;
+ result = Curl_pin_peer_pubkey(data, ptr, p->header, p->end - p->header);
+ if(result) {
+ failf(data, "SSL: public key does not match pinned public key");
+ return result;
+ }
+ }
+
+ connssl->connecting_state = ssl_connect_done;
+ return CURLE_OK;
+}
+
+
+static CURLcode gskit_connect_common(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool nonblocking, bool *done)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ timediff_t timeout_ms;
+ CURLcode result = CURLE_OK;
+
+ *done = connssl->state == ssl_connection_complete;
+ if(*done)
+ return CURLE_OK;
+
+ /* Step 1: create session, start handshake. */
+ if(connssl->connecting_state == ssl_connect_1) {
+ /* check allowed time left */
+ timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+ if(timeout_ms < 0) {
+ /* no need to continue if time already is up */
+ failf(data, "SSL connection timeout");
+ result = CURLE_OPERATION_TIMEDOUT;
+ }
+ else
+ result = gskit_connect_step1(cf, data);
+ }
+
+ /* Handle handshake pipelining. */
+ if(!result)
+ if(pipe_ssloverssl(cf, SOS_READ | SOS_WRITE) < 0)
+ result = CURLE_SSL_CONNECT_ERROR;
+
+ /* Step 2: check if handshake is over. */
+ if(!result && connssl->connecting_state == ssl_connect_2) {
+ /* check allowed time left */
+ timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+ if(timeout_ms < 0) {
+ /* no need to continue if time already is up */
+ failf(data, "SSL connection timeout");
+ result = CURLE_OPERATION_TIMEDOUT;
+ }
+ else
+ result = gskit_connect_step2(cf, data, nonblocking);
+ }
+
+ /* Handle handshake pipelining. */
+ if(!result)
+ if(pipe_ssloverssl(cf, SOS_READ | SOS_WRITE) < 0)
+ result = CURLE_SSL_CONNECT_ERROR;
+
+ /* Step 3: gather certificate info, verify host. */
+ if(!result && connssl->connecting_state == ssl_connect_3)
+ result = gskit_connect_step3(cf, data);
+
+ if(result)
+ close_one(connssl, data, conn, sockindex);
+ else if(connssl->connecting_state == ssl_connect_done) {
+ connssl->state = ssl_connection_complete;
+ connssl->connecting_state = ssl_connect_1;
+ *done = TRUE;
+ }
+
+ return result;
+}
+
+
+static CURLcode gskit_connect_nonblocking(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ CURLcode result;
+
+ result = gskit_connect_common(cf, data, TRUE, done);
+ if(*done || result)
+ connssl->connecting_state = ssl_connect_1;
+ return result;
+}
+
+
+static CURLcode gskit_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ CURLcode result;
+ bool done;
+
+ connssl->connecting_state = ssl_connect_1;
+ result = gskit_connect_common(cf, data, FALSE, &done);
+ if(result)
+ return result;
+
+ DEBUGASSERT(done);
+
+ return CURLE_OK;
+}
+
+
+static void gskit_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ close_one(cf, data);
+}
+
+
+static int gskit_shutdown(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ int what;
+ int rc;
+ char buf[120];
+ int loop = 10; /* don't get stuck */
+
+ DEBUGASSERT(BACKEND);
+
+ if(!BACKEND->handle)
+ return 0;
+
+#ifndef CURL_DISABLE_FTP
+ if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE)
+ return 0;
+#endif
+
+ close_one(cf, data);
+ rc = 0;
+ what = SOCKET_READABLE(cf->conn->sock[cf->sockindex],
+ SSL_SHUTDOWN_TIMEOUT);
+
+ while(loop--) {
+ ssize_t nread;
+
+ if(what < 0) {
+ /* anything that gets here is fatally bad */
+ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+ rc = -1;
+ break;
+ }
+
+ if(!what) { /* timeout */
+ failf(data, "SSL shutdown timeout");
+ break;
+ }
+
+ /* Something to read, let's do it and hope that it is the close
+ notify alert from the server. No way to gsk_secure_soc_read() now, so
+ use read(). */
+
+ nread = read(cf->conn->sock[cf->sockindex], buf, sizeof(buf));
+
+ if(nread < 0) {
+ char buffer[STRERROR_LEN];
+ failf(data, "read: %s", Curl_strerror(errno, buffer, sizeof(buffer)));
+ rc = -1;
+ }
+
+ if(nread <= 0)
+ break;
+
+ what = SOCKET_READABLE(cf->conn->sock[cf->sockindex], 0);
+ }
+
+ return rc;
+}
+
+
+static size_t gskit_version(char *buffer, size_t size)
+{
+ return msnprintf(buffer, size, "GSKit");
+}
+
+
+static int gskit_check_cxn(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ int err;
+ int errlen;
+
+ (void)data;
+ /* The only thing that can be tested here is at the socket level. */
+ DEBUGASSERT(BACKEND);
+
+ if(!BACKEND->handle)
+ return 0; /* connection has been closed */
+
+ err = 0;
+ errlen = sizeof(err);
+
+ if(getsockopt(cxn->sock[FIRSTSOCKET], SOL_SOCKET, SO_ERROR,
+ (unsigned char *) &err, &errlen) ||
+ errlen != sizeof(err) || err)
+ return 0; /* connection has been closed */
+
+ return -1; /* connection status unknown */
+}
+
+static void *gskit_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
+{
+ (void)info;
+ DEBUGASSERT(BACKEND);
+ return BACKEND->handle;
+}
+
+const struct Curl_ssl Curl_ssl_gskit = {
+ { CURLSSLBACKEND_GSKIT, "gskit" }, /* info */
+
+ SSLSUPP_CERTINFO |
+ SSLSUPP_PINNEDPUBKEY,
+
+ sizeof(struct ssl_backend_data),
+
+ gskit_init, /* init */
+ gskit_cleanup, /* cleanup */
+ gskit_version, /* version */
+ gskit_check_cxn, /* check_cxn */
+ gskit_shutdown, /* shutdown */
+ Curl_none_data_pending, /* data_pending */
+ Curl_none_random, /* random */
+ Curl_none_cert_status_request, /* cert_status_request */
+ gskit_connect, /* connect */
+ gskit_connect_nonblocking, /* connect_nonblocking */
+ Curl_ssl_get_select_socks, /* getsock */
+ gskit_get_internals, /* get_internals */
+ gskit_close, /* close_one */
+ Curl_none_close_all, /* close_all */
+ /* No session handling for GSKit */
+ Curl_none_session_free, /* session_free */
+ Curl_none_set_engine, /* set_engine */
+ Curl_none_set_engine_default, /* set_engine_default */
+ Curl_none_engines_list, /* engines_list */
+ Curl_none_false_start, /* false_start */
+ NULL, /* sha256sum */
+ NULL, /* associate_connection */
+ NULL, /* disassociate_connection */
+ NULL, /* free_multi_ssl_backend_data */
+ gskit_recv, /* recv decrypted data */
+ gskit_send, /* send data to encrypt */
+};
+
+#endif /* USE_GSKIT */
diff --git a/libs/libcurl/src/vtls/gskit.h b/libs/libcurl/src/vtls/gskit.h index cf923f6b85..909bbb0c9c 100644 --- a/libs/libcurl/src/vtls/gskit.h +++ b/libs/libcurl/src/vtls/gskit.h @@ -1,40 +1,40 @@ -#ifndef HEADER_CURL_GSKIT_H -#define HEADER_CURL_GSKIT_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -/* - * This header should only be needed to get included by vtls.c and gskit.c - */ - -#include "urldata.h" - -#ifdef USE_GSKIT - -extern const struct Curl_ssl Curl_ssl_gskit; - -#endif /* USE_GSKIT */ - -#endif /* HEADER_CURL_GSKIT_H */ +#ifndef HEADER_CURL_GSKIT_H
+#define HEADER_CURL_GSKIT_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+/*
+ * This header should only be needed to get included by vtls.c and gskit.c
+ */
+
+#include "urldata.h"
+
+#ifdef USE_GSKIT
+
+extern const struct Curl_ssl Curl_ssl_gskit;
+
+#endif /* USE_GSKIT */
+
+#endif /* HEADER_CURL_GSKIT_H */
diff --git a/libs/libcurl/src/vtls/gtls.c b/libs/libcurl/src/vtls/gtls.c index cf3dbc5238..7b6feaa538 100644 --- a/libs/libcurl/src/vtls/gtls.c +++ b/libs/libcurl/src/vtls/gtls.c @@ -1,1705 +1,1699 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - -/* - * Source file for all GnuTLS-specific code for the TLS/SSL layer. No code - * but vtls.c should ever call or use these functions. - * - * Note: don't use the GnuTLS' *_t variable type names in this source code, - * since they were not present in 1.0.X. - */ - -#include "curl_setup.h" - -#ifdef USE_GNUTLS - -#include <gnutls/abstract.h> -#include <gnutls/gnutls.h> -#include <gnutls/x509.h> -#include <gnutls/crypto.h> -#include <nettle/sha2.h> - -#include "urldata.h" -#include "sendf.h" -#include "inet_pton.h" -#include "gtls.h" -#include "vtls.h" -#include "vauth/vauth.h" -#include "parsedate.h" -#include "connect.h" /* for the connect timeout */ -#include "select.h" -#include "strcase.h" -#include "warnless.h" -#include "x509asn1.h" -#include "multiif.h" -#include "curl_printf.h" -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -#ifdef HAVE_GNUTLS_SRP -/* the function exists */ -#ifdef USE_TLS_SRP -/* the functionality is not disabled */ -#define USE_GNUTLS_SRP -#endif -#endif - -/* Enable GnuTLS debugging by defining GTLSDEBUG */ -/*#define GTLSDEBUG */ - -#ifdef GTLSDEBUG -static void tls_log_func(int level, const char *str) -{ - fprintf(stderr, "|<%d>| %s", level, str); -} -#endif -static bool gtls_inited = FALSE; - -#if !defined(GNUTLS_VERSION_NUMBER) || (GNUTLS_VERSION_NUMBER < 0x03010a) -#error "too old GnuTLS version" -#endif - -# include <gnutls/ocsp.h> - -struct ssl_backend_data { - gnutls_session_t session; - gnutls_certificate_credentials_t cred; -#ifdef USE_GNUTLS_SRP - gnutls_srp_client_credentials_t srp_client_cred; -#endif -}; - -static ssize_t gtls_push(void *s, const void *buf, size_t len) -{ - curl_socket_t sock = *(curl_socket_t *)s; - ssize_t ret = swrite(sock, buf, len); - return ret; -} - -static ssize_t gtls_pull(void *s, void *buf, size_t len) -{ - curl_socket_t sock = *(curl_socket_t *)s; - ssize_t ret = sread(sock, buf, len); - return ret; -} - -static ssize_t gtls_push_ssl(void *s, const void *buf, size_t len) -{ - return gnutls_record_send((gnutls_session_t) s, buf, len); -} - -static ssize_t gtls_pull_ssl(void *s, void *buf, size_t len) -{ - return gnutls_record_recv((gnutls_session_t) s, buf, len); -} - -/* gtls_init() - * - * Global GnuTLS init, called from Curl_ssl_init(). This calls functions that - * are not thread-safe and thus this function itself is not thread-safe and - * must only be called from within curl_global_init() to keep the thread - * situation under control! - */ -static int gtls_init(void) -{ - int ret = 1; - if(!gtls_inited) { - ret = gnutls_global_init()?0:1; -#ifdef GTLSDEBUG - gnutls_global_set_log_function(tls_log_func); - gnutls_global_set_log_level(2); -#endif - gtls_inited = TRUE; - } - return ret; -} - -static void gtls_cleanup(void) -{ - if(gtls_inited) { - gnutls_global_deinit(); - gtls_inited = FALSE; - } -} - -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static void showtime(struct Curl_easy *data, - const char *text, - time_t stamp) -{ - struct tm buffer; - const struct tm *tm = &buffer; - char str[96]; - CURLcode result = Curl_gmtime(stamp, &buffer); - if(result) - return; - - msnprintf(str, - sizeof(str), - " %s: %s, %02d %s %4d %02d:%02d:%02d GMT", - text, - Curl_wkday[tm->tm_wday?tm->tm_wday-1:6], - tm->tm_mday, - Curl_month[tm->tm_mon], - tm->tm_year + 1900, - tm->tm_hour, - tm->tm_min, - tm->tm_sec); - infof(data, "%s", str); -} -#endif - -static gnutls_datum_t load_file(const char *file) -{ - FILE *f; - gnutls_datum_t loaded_file = { NULL, 0 }; - long filelen; - void *ptr; - - f = fopen(file, "rb"); - if(!f) - return loaded_file; - if(fseek(f, 0, SEEK_END) != 0 - || (filelen = ftell(f)) < 0 - || fseek(f, 0, SEEK_SET) != 0 - || !(ptr = malloc((size_t)filelen))) - goto out; - if(fread(ptr, 1, (size_t)filelen, f) < (size_t)filelen) { - free(ptr); - goto out; - } - - loaded_file.data = ptr; - loaded_file.size = (unsigned int)filelen; -out: - fclose(f); - return loaded_file; -} - -static void unload_file(gnutls_datum_t data) -{ - free(data.data); -} - - -/* this function does a SSL/TLS (re-)handshake */ -static CURLcode handshake(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool duringconnect, - bool nonblocking) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - gnutls_session_t session; - curl_socket_t sockfd = conn->sock[sockindex]; - - DEBUGASSERT(backend); - session = backend->session; - - for(;;) { - timediff_t timeout_ms; - int rc; - - /* check allowed time left */ - timeout_ms = Curl_timeleft(data, NULL, duringconnect); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - /* if ssl is expecting something, check if it's available. */ - if(connssl->connecting_state == ssl_connect_2_reading - || connssl->connecting_state == ssl_connect_2_writing) { - int what; - curl_socket_t writefd = ssl_connect_2_writing == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - curl_socket_t readfd = ssl_connect_2_reading == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - - what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, - nonblocking?0: - timeout_ms?timeout_ms:1000); - if(what < 0) { - /* fatal error */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - return CURLE_SSL_CONNECT_ERROR; - } - else if(0 == what) { - if(nonblocking) - return CURLE_OK; - else if(timeout_ms) { - /* timeout */ - failf(data, "SSL connection timeout at %ld", (long)timeout_ms); - return CURLE_OPERATION_TIMEDOUT; - } - } - /* socket is readable or writable */ - } - - rc = gnutls_handshake(session); - - if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) { - connssl->connecting_state = - gnutls_record_get_direction(session)? - ssl_connect_2_writing:ssl_connect_2_reading; - continue; - } - else if((rc < 0) && !gnutls_error_is_fatal(rc)) { - const char *strerr = NULL; - - if(rc == GNUTLS_E_WARNING_ALERT_RECEIVED) { - int alert = gnutls_alert_get(session); - strerr = gnutls_alert_get_name(alert); - } - - if(!strerr) - strerr = gnutls_strerror(rc); - - infof(data, "gnutls_handshake() warning: %s", strerr); - continue; - } - else if(rc < 0) { - const char *strerr = NULL; - - if(rc == GNUTLS_E_FATAL_ALERT_RECEIVED) { - int alert = gnutls_alert_get(session); - strerr = gnutls_alert_get_name(alert); - } - - if(!strerr) - strerr = gnutls_strerror(rc); - - failf(data, "gnutls_handshake() failed: %s", strerr); - return CURLE_SSL_CONNECT_ERROR; - } - - /* Reset our connect state machine */ - connssl->connecting_state = ssl_connect_1; - return CURLE_OK; - } -} - -static gnutls_x509_crt_fmt_t do_file_type(const char *type) -{ - if(!type || !type[0]) - return GNUTLS_X509_FMT_PEM; - if(strcasecompare(type, "PEM")) - return GNUTLS_X509_FMT_PEM; - if(strcasecompare(type, "DER")) - return GNUTLS_X509_FMT_DER; - return GNUTLS_X509_FMT_PEM; /* default to PEM */ -} - -#define GNUTLS_CIPHERS "NORMAL:-ARCFOUR-128:-CTYPE-ALL:+CTYPE-X509" -/* If GnuTLS was compiled without support for SRP it will error out if SRP is - requested in the priority string, so treat it specially - */ -#define GNUTLS_SRP "+SRP" - -static CURLcode -set_ssl_version_min_max(struct Curl_easy *data, - const char **prioritylist, - const char *tls13support) -{ - struct connectdata *conn = data->conn; - long ssl_version = SSL_CONN_CONFIG(version); - long ssl_version_max = SSL_CONN_CONFIG(version_max); - - if((ssl_version == CURL_SSLVERSION_DEFAULT) || - (ssl_version == CURL_SSLVERSION_TLSv1)) - ssl_version = CURL_SSLVERSION_TLSv1_0; - if(ssl_version_max == CURL_SSLVERSION_MAX_NONE) - ssl_version_max = CURL_SSLVERSION_MAX_DEFAULT; - if(!tls13support) { - /* If the running GnuTLS doesn't support TLS 1.3, we must not specify a - prioritylist involving that since it will make GnuTLS return an en - error back at us */ - if((ssl_version_max == CURL_SSLVERSION_MAX_TLSv1_3) || - (ssl_version_max == CURL_SSLVERSION_MAX_DEFAULT)) { - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; - } - } - else if(ssl_version_max == CURL_SSLVERSION_MAX_DEFAULT) { - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_3; - } - - switch(ssl_version | ssl_version_max) { - case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_0: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.0"; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_1: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.1:+VERS-TLS1.0"; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_2: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.2:+VERS-TLS1.1:+VERS-TLS1.0"; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_1: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.1"; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_2: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.2:+VERS-TLS1.1"; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_TLSv1_2: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.2"; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_3 | CURL_SSLVERSION_MAX_TLSv1_3: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.3"; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_3: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0"; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_3: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.1"; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_TLSv1_3: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.3:+VERS-TLS1.2"; - return CURLE_OK; - } - - failf(data, "GnuTLS: cannot set ssl protocol"); - return CURLE_SSL_CONNECT_ERROR; -} - -static CURLcode -gtls_connect_step1(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - unsigned int init_flags; - gnutls_session_t session; - int rc; - bool sni = TRUE; /* default is SNI enabled */ - void *transport_ptr = NULL; - gnutls_push_func gnutls_transport_push = NULL; - gnutls_pull_func gnutls_transport_pull = NULL; -#ifdef ENABLE_IPV6 - struct in6_addr addr; -#else - struct in_addr addr; -#endif - const char *prioritylist; - const char *err = NULL; - const char * const hostname = SSL_HOST_NAME(); - long * const certverifyresult = &SSL_SET_OPTION_LVALUE(certverifyresult); - const char *tls13support; - CURLcode result; - - DEBUGASSERT(backend); - - if(connssl->state == ssl_connection_complete) - /* to make us tolerant against being called more than once for the - same connection */ - return CURLE_OK; - - if(!gtls_inited) - gtls_init(); - - /* Initialize certverifyresult to OK */ - *certverifyresult = 0; - - if(SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv2) { - failf(data, "GnuTLS does not support SSLv2"); - return CURLE_SSL_CONNECT_ERROR; - } - else if(SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv3) - sni = FALSE; /* SSLv3 has no SNI */ - - /* allocate a cred struct */ - rc = gnutls_certificate_allocate_credentials(&backend->cred); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc)); - return CURLE_SSL_CONNECT_ERROR; - } - -#ifdef USE_GNUTLS_SRP - if((SSL_SET_OPTION(primary.authtype) == CURL_TLSAUTH_SRP) && - Curl_auth_allowed_to_host(data)) { - infof(data, "Using TLS-SRP username: %s", - SSL_SET_OPTION(primary.username)); - - rc = gnutls_srp_allocate_client_credentials(&backend->srp_client_cred); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_srp_allocate_client_cred() failed: %s", - gnutls_strerror(rc)); - return CURLE_OUT_OF_MEMORY; - } - - rc = gnutls_srp_set_client_credentials(backend->srp_client_cred, - SSL_SET_OPTION(primary.username), - SSL_SET_OPTION(primary.password)); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_srp_set_client_cred() failed: %s", - gnutls_strerror(rc)); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - } -#endif - - if(SSL_CONN_CONFIG(CAfile)) { - /* set the trusted CA cert bundle file */ - gnutls_certificate_set_verify_flags(backend->cred, - GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); - - rc = gnutls_certificate_set_x509_trust_file(backend->cred, - SSL_CONN_CONFIG(CAfile), - GNUTLS_X509_FMT_PEM); - if(rc < 0) { - infof(data, "error reading ca cert file %s (%s)", - SSL_CONN_CONFIG(CAfile), gnutls_strerror(rc)); - if(SSL_CONN_CONFIG(verifypeer)) { - *certverifyresult = rc; - return CURLE_SSL_CACERT_BADFILE; - } - } - else - infof(data, "found %d certificates in %s", rc, - SSL_CONN_CONFIG(CAfile)); - } - - if(SSL_CONN_CONFIG(CApath)) { - /* set the trusted CA cert directory */ - rc = gnutls_certificate_set_x509_trust_dir(backend->cred, - SSL_CONN_CONFIG(CApath), - GNUTLS_X509_FMT_PEM); - if(rc < 0) { - infof(data, "error reading ca cert file %s (%s)", - SSL_CONN_CONFIG(CApath), gnutls_strerror(rc)); - if(SSL_CONN_CONFIG(verifypeer)) { - *certverifyresult = rc; - return CURLE_SSL_CACERT_BADFILE; - } - } - else - infof(data, "found %d certificates in %s", - rc, SSL_CONN_CONFIG(CApath)); - } - -#ifdef CURL_CA_FALLBACK - /* use system ca certificate store as fallback */ - if(SSL_CONN_CONFIG(verifypeer) && - !(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(CApath))) { - /* this ignores errors on purpose */ - gnutls_certificate_set_x509_system_trust(backend->cred); - } -#endif - - if(SSL_SET_OPTION(primary.CRLfile)) { - /* set the CRL list file */ - rc = gnutls_certificate_set_x509_crl_file(backend->cred, - SSL_SET_OPTION(primary.CRLfile), - GNUTLS_X509_FMT_PEM); - if(rc < 0) { - failf(data, "error reading crl file %s (%s)", - SSL_SET_OPTION(primary.CRLfile), gnutls_strerror(rc)); - return CURLE_SSL_CRL_BADFILE; - } - else - infof(data, "found %d CRL in %s", - rc, SSL_SET_OPTION(primary.CRLfile)); - } - - /* Initialize TLS session as a client */ - init_flags = GNUTLS_CLIENT; - -#if defined(GNUTLS_FORCE_CLIENT_CERT) - init_flags |= GNUTLS_FORCE_CLIENT_CERT; -#endif - -#if defined(GNUTLS_NO_TICKETS) - /* Disable TLS session tickets */ - init_flags |= GNUTLS_NO_TICKETS; -#endif - - rc = gnutls_init(&backend->session, init_flags); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_init() failed: %d", rc); - return CURLE_SSL_CONNECT_ERROR; - } - - /* convenient assign */ - session = backend->session; - - if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) && -#ifdef ENABLE_IPV6 - (0 == Curl_inet_pton(AF_INET6, hostname, &addr)) && -#endif - sni) { - size_t snilen; - char *snihost = Curl_ssl_snihost(data, hostname, &snilen); - if(!snihost || gnutls_server_name_set(session, GNUTLS_NAME_DNS, snihost, - snilen) < 0) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - } - - /* Use default priorities */ - rc = gnutls_set_default_priority(session); - if(rc != GNUTLS_E_SUCCESS) - return CURLE_SSL_CONNECT_ERROR; - - /* "In GnuTLS 3.6.5, TLS 1.3 is enabled by default" */ - tls13support = gnutls_check_version("3.6.5"); - - /* Ensure +SRP comes at the *end* of all relevant strings so that it can be - * removed if a run-time error indicates that SRP is not supported by this - * GnuTLS version */ - - if(SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv2 || - SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv3) { - failf(data, "GnuTLS does not support SSLv2 or SSLv3"); - return CURLE_SSL_CONNECT_ERROR; - } - - if(SSL_CONN_CONFIG(version) == CURL_SSLVERSION_TLSv1_3) { - if(!tls13support) { - failf(data, "This GnuTLS installation does not support TLS 1.3"); - return CURLE_SSL_CONNECT_ERROR; - } - } - - /* At this point we know we have a supported TLS version, so set it */ - result = set_ssl_version_min_max(data, &prioritylist, tls13support); - if(result) - return result; - -#ifdef USE_GNUTLS_SRP - /* Only add SRP to the cipher list if SRP is requested. Otherwise - * GnuTLS will disable TLS 1.3 support. */ - if(SSL_SET_OPTION(primary.authtype) == CURL_TLSAUTH_SRP) { - size_t len = strlen(prioritylist); - - char *prioritysrp = malloc(len + sizeof(GNUTLS_SRP) + 1); - if(!prioritysrp) - return CURLE_OUT_OF_MEMORY; - strcpy(prioritysrp, prioritylist); - strcpy(prioritysrp + len, ":" GNUTLS_SRP); - rc = gnutls_priority_set_direct(session, prioritysrp, &err); - free(prioritysrp); - - if((rc == GNUTLS_E_INVALID_REQUEST) && err) { - infof(data, "This GnuTLS does not support SRP"); - } - } - else { -#endif - infof(data, "GnuTLS ciphers: %s", prioritylist); - rc = gnutls_priority_set_direct(session, prioritylist, &err); -#ifdef USE_GNUTLS_SRP - } -#endif - - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "Error %d setting GnuTLS cipher list starting with %s", - rc, err); - return CURLE_SSL_CONNECT_ERROR; - } - - if(conn->bits.tls_enable_alpn) { - int cur = 0; - gnutls_datum_t protocols[2]; - -#ifdef USE_HTTP2 - if(data->state.httpwant >= CURL_HTTP_VERSION_2 -#ifndef CURL_DISABLE_PROXY - && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy) -#endif - ) { - protocols[cur].data = (unsigned char *)ALPN_H2; - protocols[cur].size = ALPN_H2_LENGTH; - cur++; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); - } -#endif - - protocols[cur].data = (unsigned char *)ALPN_HTTP_1_1; - protocols[cur].size = ALPN_HTTP_1_1_LENGTH; - cur++; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); - - if(gnutls_alpn_set_protocols(session, protocols, cur, 0)) { - failf(data, "failed setting ALPN"); - return CURLE_SSL_CONNECT_ERROR; - } - } - - if(SSL_SET_OPTION(primary.clientcert)) { - if(SSL_SET_OPTION(key_passwd)) { - const unsigned int supported_key_encryption_algorithms = - GNUTLS_PKCS_USE_PKCS12_3DES | GNUTLS_PKCS_USE_PKCS12_ARCFOUR | - GNUTLS_PKCS_USE_PKCS12_RC2_40 | GNUTLS_PKCS_USE_PBES2_3DES | - GNUTLS_PKCS_USE_PBES2_AES_128 | GNUTLS_PKCS_USE_PBES2_AES_192 | - GNUTLS_PKCS_USE_PBES2_AES_256; - rc = gnutls_certificate_set_x509_key_file2( - backend->cred, - SSL_SET_OPTION(primary.clientcert), - SSL_SET_OPTION(key) ? - SSL_SET_OPTION(key) : SSL_SET_OPTION(primary.clientcert), - do_file_type(SSL_SET_OPTION(cert_type)), - SSL_SET_OPTION(key_passwd), - supported_key_encryption_algorithms); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, - "error reading X.509 potentially-encrypted key file: %s", - gnutls_strerror(rc)); - return CURLE_SSL_CONNECT_ERROR; - } - } - else { - if(gnutls_certificate_set_x509_key_file( - backend->cred, - SSL_SET_OPTION(primary.clientcert), - SSL_SET_OPTION(key) ? - SSL_SET_OPTION(key) : SSL_SET_OPTION(primary.clientcert), - do_file_type(SSL_SET_OPTION(cert_type)) ) != - GNUTLS_E_SUCCESS) { - failf(data, "error reading X.509 key or certificate file"); - return CURLE_SSL_CONNECT_ERROR; - } - } - } - -#ifdef USE_GNUTLS_SRP - /* put the credentials to the current session */ - if(SSL_SET_OPTION(primary.authtype) == CURL_TLSAUTH_SRP) { - rc = gnutls_credentials_set(session, GNUTLS_CRD_SRP, - backend->srp_client_cred); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc)); - return CURLE_SSL_CONNECT_ERROR; - } - } - else -#endif - { - rc = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, - backend->cred); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc)); - return CURLE_SSL_CONNECT_ERROR; - } - } - -#ifndef CURL_DISABLE_PROXY - if(conn->proxy_ssl[sockindex].use) { - struct ssl_backend_data *proxy_backend; - proxy_backend = conn->proxy_ssl[sockindex].backend; - DEBUGASSERT(proxy_backend); - transport_ptr = proxy_backend->session; - gnutls_transport_push = gtls_push_ssl; - gnutls_transport_pull = gtls_pull_ssl; - } - else -#endif - { - /* file descriptor for the socket */ - transport_ptr = &conn->sock[sockindex]; - gnutls_transport_push = gtls_push; - gnutls_transport_pull = gtls_pull; - } - - /* set the connection handle */ - gnutls_transport_set_ptr(session, transport_ptr); - - /* register callback functions to send and receive data. */ - gnutls_transport_set_push_function(session, gnutls_transport_push); - gnutls_transport_set_pull_function(session, gnutls_transport_pull); - - if(SSL_CONN_CONFIG(verifystatus)) { - rc = gnutls_ocsp_status_request_enable_client(session, NULL, 0, NULL); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_ocsp_status_request_enable_client() failed: %d", rc); - return CURLE_SSL_CONNECT_ERROR; - } - } - - /* This might be a reconnect, so we check for a session ID in the cache - to speed up things */ - if(SSL_SET_OPTION(primary.sessionid)) { - void *ssl_sessionid; - size_t ssl_idsize; - - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(data, conn, - SSL_IS_PROXY() ? TRUE : FALSE, - &ssl_sessionid, &ssl_idsize, sockindex)) { - /* we got a session id, use it! */ - gnutls_session_set_data(session, ssl_sessionid, ssl_idsize); - - /* Informational message */ - infof(data, "SSL re-using session ID"); - } - Curl_ssl_sessionid_unlock(data); - } - - return CURLE_OK; -} - -static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, - gnutls_x509_crt_t cert, - const char *pinnedpubkey) -{ - /* Scratch */ - size_t len1 = 0, len2 = 0; - unsigned char *buff1 = NULL; - - gnutls_pubkey_t key = NULL; - - /* Result is returned to caller */ - CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - - /* if a path wasn't specified, don't pin */ - if(!pinnedpubkey) - return CURLE_OK; - - if(!cert) - return result; - - do { - int ret; - - /* Begin Gyrations to get the public key */ - gnutls_pubkey_init(&key); - - ret = gnutls_pubkey_import_x509(key, cert, 0); - if(ret < 0) - break; /* failed */ - - ret = gnutls_pubkey_export(key, GNUTLS_X509_FMT_DER, NULL, &len1); - if(ret != GNUTLS_E_SHORT_MEMORY_BUFFER || len1 == 0) - break; /* failed */ - - buff1 = malloc(len1); - if(!buff1) - break; /* failed */ - - len2 = len1; - - ret = gnutls_pubkey_export(key, GNUTLS_X509_FMT_DER, buff1, &len2); - if(ret < 0 || len1 != len2) - break; /* failed */ - - /* End Gyrations */ - - /* The one good exit point */ - result = Curl_pin_peer_pubkey(data, pinnedpubkey, buff1, len1); - } while(0); - - if(key) - gnutls_pubkey_deinit(key); - - Curl_safefree(buff1); - - return result; -} - -static Curl_recv gtls_recv; -static Curl_send gtls_send; - -CURLcode -Curl_gtls_verifyserver(struct Curl_easy *data, - struct connectdata *conn, - gnutls_session_t session, - int sockindex) -{ - unsigned int cert_list_size; - const gnutls_datum_t *chainp; - unsigned int verify_status = 0; - gnutls_x509_crt_t x509_cert, x509_issuer; - gnutls_datum_t issuerp; - gnutls_datum_t certfields; - char certname[65] = ""; /* limited to 64 chars by ASN.1 */ - size_t size; - time_t certclock; - const char *ptr; - int rc; - gnutls_datum_t proto; - CURLcode result = CURLE_OK; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - unsigned int algo; - unsigned int bits; - gnutls_protocol_t version = gnutls_protocol_get_version(session); -#endif - const char * const hostname = SSL_HOST_NAME(); - long * const certverifyresult = &SSL_SET_OPTION_LVALUE(certverifyresult); - - /* the name of the cipher suite used, e.g. ECDHE_RSA_AES_256_GCM_SHA384. */ - ptr = gnutls_cipher_suite_get_name(gnutls_kx_get(session), - gnutls_cipher_get(session), - gnutls_mac_get(session)); - - infof(data, "SSL connection using %s / %s", - gnutls_protocol_get_name(version), ptr); - - /* This function will return the peer's raw certificate (chain) as sent by - the peer. These certificates are in raw format (DER encoded for - X.509). In case of a X.509 then a certificate list may be present. The - first certificate in the list is the peer's certificate, following the - issuer's certificate, then the issuer's issuer etc. */ - - chainp = gnutls_certificate_get_peers(session, &cert_list_size); - if(!chainp) { - if(SSL_CONN_CONFIG(verifypeer) || - SSL_CONN_CONFIG(verifyhost) || - SSL_CONN_CONFIG(issuercert)) { -#ifdef USE_GNUTLS_SRP - if(SSL_SET_OPTION(primary.authtype) == CURL_TLSAUTH_SRP - && SSL_SET_OPTION(primary.username) - && !SSL_CONN_CONFIG(verifypeer) - && gnutls_cipher_get(session)) { - /* no peer cert, but auth is ok if we have SRP user and cipher and no - peer verify */ - } - else { -#endif - failf(data, "failed to get server cert"); - *certverifyresult = GNUTLS_E_NO_CERTIFICATE_FOUND; - return CURLE_PEER_FAILED_VERIFICATION; -#ifdef USE_GNUTLS_SRP - } -#endif - } - infof(data, " common name: WARNING couldn't obtain"); - } - - if(data->set.ssl.certinfo && chainp) { - unsigned int i; - - result = Curl_ssl_init_certinfo(data, cert_list_size); - if(result) - return result; - - for(i = 0; i < cert_list_size; i++) { - const char *beg = (const char *) chainp[i].data; - const char *end = beg + chainp[i].size; - - result = Curl_extract_certinfo(data, i, beg, end); - if(result) - return result; - } - } - - if(SSL_CONN_CONFIG(verifypeer)) { - /* This function will try to verify the peer's certificate and return its - status (trusted, invalid etc.). The value of status should be one or - more of the gnutls_certificate_status_t enumerated elements bitwise - or'd. To avoid denial of service attacks some default upper limits - regarding the certificate key size and chain size are set. To override - them use gnutls_certificate_set_verify_limits(). */ - - rc = gnutls_certificate_verify_peers2(session, &verify_status); - if(rc < 0) { - failf(data, "server cert verify failed: %d", rc); - *certverifyresult = rc; - return CURLE_SSL_CONNECT_ERROR; - } - - *certverifyresult = verify_status; - - /* verify_status is a bitmask of gnutls_certificate_status bits */ - if(verify_status & GNUTLS_CERT_INVALID) { - if(SSL_CONN_CONFIG(verifypeer)) { - failf(data, "server certificate verification failed. CAfile: %s " - "CRLfile: %s", SSL_CONN_CONFIG(CAfile) ? SSL_CONN_CONFIG(CAfile): - "none", - SSL_SET_OPTION(primary.CRLfile) ? - SSL_SET_OPTION(primary.CRLfile) : "none"); - return CURLE_PEER_FAILED_VERIFICATION; - } - else - infof(data, " server certificate verification FAILED"); - } - else - infof(data, " server certificate verification OK"); - } - else - infof(data, " server certificate verification SKIPPED"); - - if(SSL_CONN_CONFIG(verifystatus)) { - if(gnutls_ocsp_status_request_is_checked(session, 0) == 0) { - gnutls_datum_t status_request; - gnutls_ocsp_resp_t ocsp_resp; - - gnutls_ocsp_cert_status_t status; - gnutls_x509_crl_reason_t reason; - - rc = gnutls_ocsp_status_request_get(session, &status_request); - - infof(data, " server certificate status verification FAILED"); - - if(rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { - failf(data, "No OCSP response received"); - return CURLE_SSL_INVALIDCERTSTATUS; - } - - if(rc < 0) { - failf(data, "Invalid OCSP response received"); - return CURLE_SSL_INVALIDCERTSTATUS; - } - - gnutls_ocsp_resp_init(&ocsp_resp); - - rc = gnutls_ocsp_resp_import(ocsp_resp, &status_request); - if(rc < 0) { - failf(data, "Invalid OCSP response received"); - return CURLE_SSL_INVALIDCERTSTATUS; - } - - (void)gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL, - &status, NULL, NULL, NULL, &reason); - - switch(status) { - case GNUTLS_OCSP_CERT_GOOD: - break; - - case GNUTLS_OCSP_CERT_REVOKED: { - const char *crl_reason; - - switch(reason) { - default: - case GNUTLS_X509_CRLREASON_UNSPECIFIED: - crl_reason = "unspecified reason"; - break; - - case GNUTLS_X509_CRLREASON_KEYCOMPROMISE: - crl_reason = "private key compromised"; - break; - - case GNUTLS_X509_CRLREASON_CACOMPROMISE: - crl_reason = "CA compromised"; - break; - - case GNUTLS_X509_CRLREASON_AFFILIATIONCHANGED: - crl_reason = "affiliation has changed"; - break; - - case GNUTLS_X509_CRLREASON_SUPERSEDED: - crl_reason = "certificate superseded"; - break; - - case GNUTLS_X509_CRLREASON_CESSATIONOFOPERATION: - crl_reason = "operation has ceased"; - break; - - case GNUTLS_X509_CRLREASON_CERTIFICATEHOLD: - crl_reason = "certificate is on hold"; - break; - - case GNUTLS_X509_CRLREASON_REMOVEFROMCRL: - crl_reason = "will be removed from delta CRL"; - break; - - case GNUTLS_X509_CRLREASON_PRIVILEGEWITHDRAWN: - crl_reason = "privilege withdrawn"; - break; - - case GNUTLS_X509_CRLREASON_AACOMPROMISE: - crl_reason = "AA compromised"; - break; - } - - failf(data, "Server certificate was revoked: %s", crl_reason); - break; - } - - default: - case GNUTLS_OCSP_CERT_UNKNOWN: - failf(data, "Server certificate status is unknown"); - break; - } - - gnutls_ocsp_resp_deinit(ocsp_resp); - - return CURLE_SSL_INVALIDCERTSTATUS; - } - else - infof(data, " server certificate status verification OK"); - } - else - infof(data, " server certificate status verification SKIPPED"); - - /* initialize an X.509 certificate structure. */ - gnutls_x509_crt_init(&x509_cert); - - if(chainp) - /* convert the given DER or PEM encoded Certificate to the native - gnutls_x509_crt_t format */ - gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER); - - if(SSL_CONN_CONFIG(issuercert)) { - gnutls_x509_crt_init(&x509_issuer); - issuerp = load_file(SSL_CONN_CONFIG(issuercert)); - gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM); - rc = gnutls_x509_crt_check_issuer(x509_cert, x509_issuer); - gnutls_x509_crt_deinit(x509_issuer); - unload_file(issuerp); - if(rc <= 0) { - failf(data, "server certificate issuer check failed (IssuerCert: %s)", - SSL_CONN_CONFIG(issuercert)?SSL_CONN_CONFIG(issuercert):"none"); - gnutls_x509_crt_deinit(x509_cert); - return CURLE_SSL_ISSUER_ERROR; - } - infof(data, " server certificate issuer check OK (Issuer Cert: %s)", - SSL_CONN_CONFIG(issuercert)?SSL_CONN_CONFIG(issuercert):"none"); - } - - size = sizeof(certname); - rc = gnutls_x509_crt_get_dn_by_oid(x509_cert, GNUTLS_OID_X520_COMMON_NAME, - 0, /* the first and only one */ - FALSE, - certname, - &size); - if(rc) { - infof(data, "error fetching CN from cert:%s", - gnutls_strerror(rc)); - } - - /* This function will check if the given certificate's subject matches the - given hostname. This is a basic implementation of the matching described - in RFC2818 (HTTPS), which takes into account wildcards, and the subject - alternative name PKIX extension. Returns non zero on success, and zero on - failure. */ - rc = gnutls_x509_crt_check_hostname(x509_cert, hostname); -#if GNUTLS_VERSION_NUMBER < 0x030306 - /* Before 3.3.6, gnutls_x509_crt_check_hostname() didn't check IP - addresses. */ - if(!rc) { -#ifdef ENABLE_IPV6 - #define use_addr in6_addr -#else - #define use_addr in_addr -#endif - unsigned char addrbuf[sizeof(struct use_addr)]; - size_t addrlen = 0; - - if(Curl_inet_pton(AF_INET, hostname, addrbuf) > 0) - addrlen = 4; -#ifdef ENABLE_IPV6 - else if(Curl_inet_pton(AF_INET6, hostname, addrbuf) > 0) - addrlen = 16; -#endif - - if(addrlen) { - unsigned char certaddr[sizeof(struct use_addr)]; - int i; - - for(i = 0; ; i++) { - size_t certaddrlen = sizeof(certaddr); - int ret = gnutls_x509_crt_get_subject_alt_name(x509_cert, i, certaddr, - &certaddrlen, NULL); - /* If this happens, it wasn't an IP address. */ - if(ret == GNUTLS_E_SHORT_MEMORY_BUFFER) - continue; - if(ret < 0) - break; - if(ret != GNUTLS_SAN_IPADDRESS) - continue; - if(certaddrlen == addrlen && !memcmp(addrbuf, certaddr, addrlen)) { - rc = 1; - break; - } - } - } - } -#endif - if(!rc) { - if(SSL_CONN_CONFIG(verifyhost)) { - failf(data, "SSL: certificate subject name (%s) does not match " - "target host name '%s'", certname, SSL_HOST_DISPNAME()); - gnutls_x509_crt_deinit(x509_cert); - return CURLE_PEER_FAILED_VERIFICATION; - } - else - infof(data, " common name: %s (does not match '%s')", - certname, SSL_HOST_DISPNAME()); - } - else - infof(data, " common name: %s (matched)", certname); - - /* Check for time-based validity */ - certclock = gnutls_x509_crt_get_expiration_time(x509_cert); - - if(certclock == (time_t)-1) { - if(SSL_CONN_CONFIG(verifypeer)) { - failf(data, "server cert expiration date verify failed"); - *certverifyresult = GNUTLS_CERT_EXPIRED; - gnutls_x509_crt_deinit(x509_cert); - return CURLE_SSL_CONNECT_ERROR; - } - else - infof(data, " server certificate expiration date verify FAILED"); - } - else { - if(certclock < time(NULL)) { - if(SSL_CONN_CONFIG(verifypeer)) { - failf(data, "server certificate expiration date has passed."); - *certverifyresult = GNUTLS_CERT_EXPIRED; - gnutls_x509_crt_deinit(x509_cert); - return CURLE_PEER_FAILED_VERIFICATION; - } - else - infof(data, " server certificate expiration date FAILED"); - } - else - infof(data, " server certificate expiration date OK"); - } - - certclock = gnutls_x509_crt_get_activation_time(x509_cert); - - if(certclock == (time_t)-1) { - if(SSL_CONN_CONFIG(verifypeer)) { - failf(data, "server cert activation date verify failed"); - *certverifyresult = GNUTLS_CERT_NOT_ACTIVATED; - gnutls_x509_crt_deinit(x509_cert); - return CURLE_SSL_CONNECT_ERROR; - } - else - infof(data, " server certificate activation date verify FAILED"); - } - else { - if(certclock > time(NULL)) { - if(SSL_CONN_CONFIG(verifypeer)) { - failf(data, "server certificate not activated yet."); - *certverifyresult = GNUTLS_CERT_NOT_ACTIVATED; - gnutls_x509_crt_deinit(x509_cert); - return CURLE_PEER_FAILED_VERIFICATION; - } - else - infof(data, " server certificate activation date FAILED"); - } - else - infof(data, " server certificate activation date OK"); - } - - ptr = SSL_PINNED_PUB_KEY(); - if(ptr) { - result = pkp_pin_peer_pubkey(data, x509_cert, ptr); - if(result != CURLE_OK) { - failf(data, "SSL: public key does not match pinned public key"); - gnutls_x509_crt_deinit(x509_cert); - return result; - } - } - - /* Show: - - - subject - - start date - - expire date - - common name - - issuer - - */ - -#ifndef CURL_DISABLE_VERBOSE_STRINGS - /* public key algorithm's parameters */ - algo = gnutls_x509_crt_get_pk_algorithm(x509_cert, &bits); - infof(data, " certificate public key: %s", - gnutls_pk_algorithm_get_name(algo)); - - /* version of the X.509 certificate. */ - infof(data, " certificate version: #%d", - gnutls_x509_crt_get_version(x509_cert)); - - - rc = gnutls_x509_crt_get_dn2(x509_cert, &certfields); - if(rc) - infof(data, "Failed to get certificate name"); - else { - infof(data, " subject: %s", certfields.data); - - certclock = gnutls_x509_crt_get_activation_time(x509_cert); - showtime(data, "start date", certclock); - - certclock = gnutls_x509_crt_get_expiration_time(x509_cert); - showtime(data, "expire date", certclock); - - gnutls_free(certfields.data); - } - - rc = gnutls_x509_crt_get_issuer_dn2(x509_cert, &certfields); - if(rc) - infof(data, "Failed to get certificate issuer"); - else { - infof(data, " issuer: %s", certfields.data); - - gnutls_free(certfields.data); - } -#endif - - gnutls_x509_crt_deinit(x509_cert); - - if(conn->bits.tls_enable_alpn) { - rc = gnutls_alpn_get_selected_protocol(session, &proto); - if(rc == 0) { - infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, proto.size, - proto.data); - -#ifdef USE_HTTP2 - if(proto.size == ALPN_H2_LENGTH && - !memcmp(ALPN_H2, proto.data, - ALPN_H2_LENGTH)) { - conn->alpn = CURL_HTTP_VERSION_2; - } - else -#endif - if(proto.size == ALPN_HTTP_1_1_LENGTH && - !memcmp(ALPN_HTTP_1_1, proto.data, ALPN_HTTP_1_1_LENGTH)) { - conn->alpn = CURL_HTTP_VERSION_1_1; - } - } - else - infof(data, VTLS_INFOF_NO_ALPN); - - Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); - } - - conn->ssl[sockindex].state = ssl_connection_complete; - - if(SSL_SET_OPTION(primary.sessionid)) { - /* we always unconditionally get the session id here, as even if we - already got it from the cache and asked to use it in the connection, it - might've been rejected and then a new one is in use now and we need to - detect that. */ - void *connect_sessionid; - size_t connect_idsize = 0; - - /* get the session ID data size */ - gnutls_session_get_data(session, NULL, &connect_idsize); - connect_sessionid = malloc(connect_idsize); /* get a buffer for it */ - - if(connect_sessionid) { - bool incache; - bool added = FALSE; - void *ssl_sessionid; - - /* extract session ID to the allocated buffer */ - gnutls_session_get_data(session, connect_sessionid, &connect_idsize); - - Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(data, conn, - SSL_IS_PROXY() ? TRUE : FALSE, - &ssl_sessionid, NULL, sockindex)); - if(incache) { - /* there was one before in the cache, so instead of risking that the - previous one was rejected, we just kill that and store the new */ - Curl_ssl_delsessionid(data, ssl_sessionid); - } - - /* store this session id */ - result = Curl_ssl_addsessionid(data, conn, - SSL_IS_PROXY() ? TRUE : FALSE, - connect_sessionid, connect_idsize, - sockindex, &added); - Curl_ssl_sessionid_unlock(data); - if(!added) - free(connect_sessionid); - if(result) { - result = CURLE_OUT_OF_MEMORY; - } - } - else - result = CURLE_OUT_OF_MEMORY; - } - - return result; -} - - -/* - * This function is called after the TCP connect has completed. Setup the TLS - * layer and do all necessary magic. - */ -/* We use connssl->connecting_state to keep track of the connection status; - there are three states: 'ssl_connect_1' (not started yet or complete), - 'ssl_connect_2_reading' (waiting for data from server), and - 'ssl_connect_2_writing' (waiting to be able to write). - */ -static CURLcode -gtls_connect_common(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool nonblocking, - bool *done) -{ - int rc; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - - /* Initiate the connection, if not already done */ - if(ssl_connect_1 == connssl->connecting_state) { - rc = gtls_connect_step1(data, conn, sockindex); - if(rc) - return rc; - } - - rc = handshake(data, conn, sockindex, TRUE, nonblocking); - if(rc) - /* handshake() sets its own error message with failf() */ - return rc; - - /* Finish connecting once the handshake is done */ - if(ssl_connect_1 == connssl->connecting_state) { - struct ssl_backend_data *backend = connssl->backend; - gnutls_session_t session; - DEBUGASSERT(backend); - session = backend->session; - rc = Curl_gtls_verifyserver(data, conn, session, sockindex); - if(rc) - return rc; - conn->recv[sockindex] = gtls_recv; - conn->send[sockindex] = gtls_send; - } - - *done = ssl_connect_1 == connssl->connecting_state; - - return CURLE_OK; -} - -static CURLcode gtls_connect_nonblocking(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, bool *done) -{ - return gtls_connect_common(data, conn, sockindex, TRUE, done); -} - -static CURLcode gtls_connect(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - CURLcode result; - bool done = FALSE; - - result = gtls_connect_common(data, conn, sockindex, FALSE, &done); - if(result) - return result; - - DEBUGASSERT(done); - - return CURLE_OK; -} - -static bool gtls_data_pending(const struct connectdata *conn, - int connindex) -{ - const struct ssl_connect_data *connssl = &conn->ssl[connindex]; - bool res = FALSE; - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - if(backend->session && - 0 != gnutls_record_check_pending(backend->session)) - res = TRUE; - -#ifndef CURL_DISABLE_PROXY - connssl = &conn->proxy_ssl[connindex]; - backend = connssl->backend; - DEBUGASSERT(backend); - if(backend->session && - 0 != gnutls_record_check_pending(backend->session)) - res = TRUE; -#endif - - return res; -} - -static ssize_t gtls_send(struct Curl_easy *data, - int sockindex, - const void *mem, - size_t len, - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - ssize_t rc; - - DEBUGASSERT(backend); - rc = gnutls_record_send(backend->session, mem, len); - - if(rc < 0) { - *curlcode = (rc == GNUTLS_E_AGAIN) - ? CURLE_AGAIN - : CURLE_SEND_ERROR; - - rc = -1; - } - - return rc; -} - -static void close_one(struct ssl_connect_data *connssl) -{ - struct ssl_backend_data *backend = connssl->backend; - DEBUGASSERT(backend); - - if(backend->session) { - char buf[32]; - /* Maybe the server has already sent a close notify alert. - Read it to avoid an RST on the TCP connection. */ - (void)gnutls_record_recv(backend->session, buf, sizeof(buf)); - gnutls_bye(backend->session, GNUTLS_SHUT_WR); - gnutls_deinit(backend->session); - backend->session = NULL; - } - if(backend->cred) { - gnutls_certificate_free_credentials(backend->cred); - backend->cred = NULL; - } -#ifdef USE_GNUTLS_SRP - if(backend->srp_client_cred) { - gnutls_srp_free_client_credentials(backend->srp_client_cred); - backend->srp_client_cred = NULL; - } -#endif -} - -static void gtls_close(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - (void) data; - close_one(&conn->ssl[sockindex]); -#ifndef CURL_DISABLE_PROXY - close_one(&conn->proxy_ssl[sockindex]); -#endif -} - -/* - * This function is called to shut down the SSL layer but keep the - * socket open (CCC - Clear Command Channel) - */ -static int gtls_shutdown(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - int retval = 0; - - DEBUGASSERT(backend); - -#ifndef CURL_DISABLE_FTP - /* This has only been tested on the proftpd server, and the mod_tls code - sends a close notify alert without waiting for a close notify alert in - response. Thus we wait for a close notify alert from the server, but - we do not send one. Let's hope other servers do the same... */ - - if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE) - gnutls_bye(backend->session, GNUTLS_SHUT_WR); -#endif - - if(backend->session) { - ssize_t result; - bool done = FALSE; - char buf[120]; - - while(!done) { - int what = SOCKET_READABLE(conn->sock[sockindex], - SSL_SHUTDOWN_TIMEOUT); - if(what > 0) { - /* Something to read, let's do it and hope that it is the close - notify alert from the server */ - result = gnutls_record_recv(backend->session, - buf, sizeof(buf)); - switch(result) { - case 0: - /* This is the expected response. There was no data but only - the close notify alert */ - done = TRUE; - break; - case GNUTLS_E_AGAIN: - case GNUTLS_E_INTERRUPTED: - infof(data, "GNUTLS_E_AGAIN || GNUTLS_E_INTERRUPTED"); - break; - default: - retval = -1; - done = TRUE; - break; - } - } - else if(0 == what) { - /* timeout */ - failf(data, "SSL shutdown timeout"); - done = TRUE; - } - else { - /* anything that gets here is fatally bad */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - retval = -1; - done = TRUE; - } - } - gnutls_deinit(backend->session); - } - gnutls_certificate_free_credentials(backend->cred); - -#ifdef USE_GNUTLS_SRP - if(SSL_SET_OPTION(primary.authtype) == CURL_TLSAUTH_SRP - && SSL_SET_OPTION(primary.username) != NULL) - gnutls_srp_free_client_credentials(backend->srp_client_cred); -#endif - - backend->cred = NULL; - backend->session = NULL; - - return retval; -} - -static ssize_t gtls_recv(struct Curl_easy *data, /* connection data */ - int num, /* socketindex */ - char *buf, /* store read data here */ - size_t buffersize, /* max amount to read */ - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[num]; - struct ssl_backend_data *backend = connssl->backend; - ssize_t ret; - - DEBUGASSERT(backend); - - ret = gnutls_record_recv(backend->session, buf, buffersize); - if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) { - *curlcode = CURLE_AGAIN; - return -1; - } - - if(ret == GNUTLS_E_REHANDSHAKE) { - /* BLOCKING call, this is bad but a work-around for now. Fixing this "the - proper way" takes a whole lot of work. */ - CURLcode result = handshake(data, conn, num, FALSE, FALSE); - if(result) - /* handshake() writes error message on its own */ - *curlcode = result; - else - *curlcode = CURLE_AGAIN; /* then return as if this was a wouldblock */ - return -1; - } - - if(ret < 0) { - failf(data, "GnuTLS recv error (%d): %s", - - (int)ret, gnutls_strerror((int)ret)); - *curlcode = CURLE_RECV_ERROR; - return -1; - } - - return ret; -} - -static void gtls_session_free(void *ptr) -{ - free(ptr); -} - -static size_t gtls_version(char *buffer, size_t size) -{ - return msnprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL)); -} - -/* data might be NULL! */ -static CURLcode gtls_random(struct Curl_easy *data, - unsigned char *entropy, size_t length) -{ - int rc; - (void)data; - rc = gnutls_rnd(GNUTLS_RND_RANDOM, entropy, length); - return rc?CURLE_FAILED_INIT:CURLE_OK; -} - -static CURLcode gtls_sha256sum(const unsigned char *tmp, /* input */ - size_t tmplen, - unsigned char *sha256sum, /* output */ - size_t sha256len) -{ - struct sha256_ctx SHA256pw; - sha256_init(&SHA256pw); - sha256_update(&SHA256pw, (unsigned int)tmplen, tmp); - sha256_digest(&SHA256pw, (unsigned int)sha256len, sha256sum); - return CURLE_OK; -} - -static bool gtls_cert_status_request(void) -{ - return TRUE; -} - -static void *gtls_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - struct ssl_backend_data *backend = connssl->backend; - (void)info; - DEBUGASSERT(backend); - return backend->session; -} - -const struct Curl_ssl Curl_ssl_gnutls = { - { CURLSSLBACKEND_GNUTLS, "gnutls" }, /* info */ - - SSLSUPP_CA_PATH | - SSLSUPP_CERTINFO | - SSLSUPP_PINNEDPUBKEY | - SSLSUPP_HTTPS_PROXY, - - sizeof(struct ssl_backend_data), - - gtls_init, /* init */ - gtls_cleanup, /* cleanup */ - gtls_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ - gtls_shutdown, /* shutdown */ - gtls_data_pending, /* data_pending */ - gtls_random, /* random */ - gtls_cert_status_request, /* cert_status_request */ - gtls_connect, /* connect */ - gtls_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_getsock, /* getsock */ - gtls_get_internals, /* get_internals */ - gtls_close, /* close_one */ - Curl_none_close_all, /* close_all */ - gtls_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ - gtls_sha256sum, /* sha256sum */ - NULL, /* associate_connection */ - NULL /* disassociate_connection */ -}; - -#endif /* USE_GNUTLS */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+/*
+ * Source file for all GnuTLS-specific code for the TLS/SSL layer. No code
+ * but vtls.c should ever call or use these functions.
+ *
+ * Note: don't use the GnuTLS' *_t variable type names in this source code,
+ * since they were not present in 1.0.X.
+ */
+
+#include "curl_setup.h"
+
+#ifdef USE_GNUTLS
+
+#include <gnutls/abstract.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <gnutls/crypto.h>
+#include <nettle/sha2.h>
+
+#include "urldata.h"
+#include "sendf.h"
+#include "inet_pton.h"
+#include "gtls.h"
+#include "vtls.h"
+#include "vtls_int.h"
+#include "vauth/vauth.h"
+#include "parsedate.h"
+#include "connect.h" /* for the connect timeout */
+#include "select.h"
+#include "strcase.h"
+#include "warnless.h"
+#include "x509asn1.h"
+#include "multiif.h"
+#include "curl_printf.h"
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/* Enable GnuTLS debugging by defining GTLSDEBUG */
+/*#define GTLSDEBUG */
+
+#ifdef GTLSDEBUG
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "|<%d>| %s", level, str);
+}
+#endif
+static bool gtls_inited = FALSE;
+
+#if !defined(GNUTLS_VERSION_NUMBER) || (GNUTLS_VERSION_NUMBER < 0x03010a)
+#error "too old GnuTLS version"
+#endif
+
+# include <gnutls/ocsp.h>
+
+struct ssl_backend_data {
+ struct gtls_instance gtls;
+};
+
+static ssize_t gtls_push(void *s, const void *buf, size_t blen)
+{
+ struct Curl_cfilter *cf = s;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct Curl_easy *data = connssl->call_data;
+ ssize_t nwritten;
+ CURLcode result;
+
+ DEBUGASSERT(data);
+ nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
+ if(nwritten < 0) {
+ gnutls_transport_set_errno(connssl->backend->gtls.session,
+ (CURLE_AGAIN == result)? EAGAIN : EINVAL);
+ nwritten = -1;
+ }
+ return nwritten;
+}
+
+static ssize_t gtls_pull(void *s, void *buf, size_t blen)
+{
+ struct Curl_cfilter *cf = s;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct Curl_easy *data = connssl->call_data;
+ ssize_t nread;
+ CURLcode result;
+
+ DEBUGASSERT(data);
+ nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
+ if(nread < 0) {
+ gnutls_transport_set_errno(connssl->backend->gtls.session,
+ (CURLE_AGAIN == result)? EAGAIN : EINVAL);
+ nread = -1;
+ }
+ return nread;
+}
+
+/* gtls_init()
+ *
+ * Global GnuTLS init, called from Curl_ssl_init(). This calls functions that
+ * are not thread-safe and thus this function itself is not thread-safe and
+ * must only be called from within curl_global_init() to keep the thread
+ * situation under control!
+ */
+static int gtls_init(void)
+{
+ int ret = 1;
+ if(!gtls_inited) {
+ ret = gnutls_global_init()?0:1;
+#ifdef GTLSDEBUG
+ gnutls_global_set_log_function(tls_log_func);
+ gnutls_global_set_log_level(2);
+#endif
+ gtls_inited = TRUE;
+ }
+ return ret;
+}
+
+static void gtls_cleanup(void)
+{
+ if(gtls_inited) {
+ gnutls_global_deinit();
+ gtls_inited = FALSE;
+ }
+}
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static void showtime(struct Curl_easy *data,
+ const char *text,
+ time_t stamp)
+{
+ struct tm buffer;
+ const struct tm *tm = &buffer;
+ char str[96];
+ CURLcode result = Curl_gmtime(stamp, &buffer);
+ if(result)
+ return;
+
+ msnprintf(str,
+ sizeof(str),
+ " %s: %s, %02d %s %4d %02d:%02d:%02d GMT",
+ text,
+ Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
+ tm->tm_mday,
+ Curl_month[tm->tm_mon],
+ tm->tm_year + 1900,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec);
+ infof(data, "%s", str);
+}
+#endif
+
+static gnutls_datum_t load_file(const char *file)
+{
+ FILE *f;
+ gnutls_datum_t loaded_file = { NULL, 0 };
+ long filelen;
+ void *ptr;
+
+ f = fopen(file, "rb");
+ if(!f)
+ return loaded_file;
+ if(fseek(f, 0, SEEK_END) != 0
+ || (filelen = ftell(f)) < 0
+ || fseek(f, 0, SEEK_SET) != 0
+ || !(ptr = malloc((size_t)filelen)))
+ goto out;
+ if(fread(ptr, 1, (size_t)filelen, f) < (size_t)filelen) {
+ free(ptr);
+ goto out;
+ }
+
+ loaded_file.data = ptr;
+ loaded_file.size = (unsigned int)filelen;
+out:
+ fclose(f);
+ return loaded_file;
+}
+
+static void unload_file(gnutls_datum_t data)
+{
+ free(data.data);
+}
+
+
+/* this function does a SSL/TLS (re-)handshake */
+static CURLcode handshake(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool duringconnect,
+ bool nonblocking)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ gnutls_session_t session;
+ curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+
+ DEBUGASSERT(backend);
+ session = backend->gtls.session;
+
+ for(;;) {
+ timediff_t timeout_ms;
+ int rc;
+
+ /* check allowed time left */
+ timeout_ms = Curl_timeleft(data, NULL, duringconnect);
+
+ if(timeout_ms < 0) {
+ /* no need to continue if time already is up */
+ failf(data, "SSL connection timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
+ /* if ssl is expecting something, check if it's available. */
+ if(connssl->connecting_state == ssl_connect_2_reading
+ || connssl->connecting_state == ssl_connect_2_writing) {
+ int what;
+ curl_socket_t writefd = ssl_connect_2_writing ==
+ connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+ curl_socket_t readfd = ssl_connect_2_reading ==
+ connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+
+ what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
+ nonblocking?0:
+ timeout_ms?timeout_ms:1000);
+ if(what < 0) {
+ /* fatal error */
+ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else if(0 == what) {
+ if(nonblocking)
+ return CURLE_OK;
+ else if(timeout_ms) {
+ /* timeout */
+ failf(data, "SSL connection timeout at %ld", (long)timeout_ms);
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+ }
+ /* socket is readable or writable */
+ }
+
+ rc = gnutls_handshake(session);
+
+ if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
+ connssl->connecting_state =
+ gnutls_record_get_direction(session)?
+ ssl_connect_2_writing:ssl_connect_2_reading;
+ continue;
+ }
+ else if((rc < 0) && !gnutls_error_is_fatal(rc)) {
+ const char *strerr = NULL;
+
+ if(rc == GNUTLS_E_WARNING_ALERT_RECEIVED) {
+ int alert = gnutls_alert_get(session);
+ strerr = gnutls_alert_get_name(alert);
+ }
+
+ if(!strerr)
+ strerr = gnutls_strerror(rc);
+
+ infof(data, "gnutls_handshake() warning: %s", strerr);
+ continue;
+ }
+ else if(rc < 0) {
+ const char *strerr = NULL;
+
+ if(rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
+ int alert = gnutls_alert_get(session);
+ strerr = gnutls_alert_get_name(alert);
+ }
+
+ if(!strerr)
+ strerr = gnutls_strerror(rc);
+
+ failf(data, "gnutls_handshake() failed: %s", strerr);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ /* Reset our connect state machine */
+ connssl->connecting_state = ssl_connect_1;
+ return CURLE_OK;
+ }
+}
+
+static gnutls_x509_crt_fmt_t do_file_type(const char *type)
+{
+ if(!type || !type[0])
+ return GNUTLS_X509_FMT_PEM;
+ if(strcasecompare(type, "PEM"))
+ return GNUTLS_X509_FMT_PEM;
+ if(strcasecompare(type, "DER"))
+ return GNUTLS_X509_FMT_DER;
+ return GNUTLS_X509_FMT_PEM; /* default to PEM */
+}
+
+#define GNUTLS_CIPHERS "NORMAL:-ARCFOUR-128:-CTYPE-ALL:+CTYPE-X509"
+/* If GnuTLS was compiled without support for SRP it will error out if SRP is
+ requested in the priority string, so treat it specially
+ */
+#define GNUTLS_SRP "+SRP"
+
+static CURLcode
+set_ssl_version_min_max(struct Curl_easy *data,
+ struct ssl_primary_config *conn_config,
+ const char **prioritylist,
+ const char *tls13support)
+{
+ long ssl_version = conn_config->version;
+ long ssl_version_max = conn_config->version_max;
+
+ if((ssl_version == CURL_SSLVERSION_DEFAULT) ||
+ (ssl_version == CURL_SSLVERSION_TLSv1))
+ ssl_version = CURL_SSLVERSION_TLSv1_0;
+ if(ssl_version_max == CURL_SSLVERSION_MAX_NONE)
+ ssl_version_max = CURL_SSLVERSION_MAX_DEFAULT;
+ if(!tls13support) {
+ /* If the running GnuTLS doesn't support TLS 1.3, we must not specify a
+ prioritylist involving that since it will make GnuTLS return an en
+ error back at us */
+ if((ssl_version_max == CURL_SSLVERSION_MAX_TLSv1_3) ||
+ (ssl_version_max == CURL_SSLVERSION_MAX_DEFAULT)) {
+ ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2;
+ }
+ }
+ else if(ssl_version_max == CURL_SSLVERSION_MAX_DEFAULT) {
+ ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_3;
+ }
+
+ switch(ssl_version | ssl_version_max) {
+ case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_0:
+ *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
+ "+VERS-TLS1.0";
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_1:
+ *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
+ "+VERS-TLS1.1:+VERS-TLS1.0";
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_2:
+ *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
+ "+VERS-TLS1.2:+VERS-TLS1.1:+VERS-TLS1.0";
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_1:
+ *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
+ "+VERS-TLS1.1";
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_2:
+ *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
+ "+VERS-TLS1.2:+VERS-TLS1.1";
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_TLSv1_2:
+ *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
+ "+VERS-TLS1.2";
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_3 | CURL_SSLVERSION_MAX_TLSv1_3:
+ *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
+ "+VERS-TLS1.3";
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_3:
+ *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0";
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_3:
+ *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
+ "+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.1";
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_TLSv1_3:
+ *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
+ "+VERS-TLS1.3:+VERS-TLS1.2";
+ return CURLE_OK;
+ }
+
+ failf(data, "GnuTLS: cannot set ssl protocol");
+ return CURLE_SSL_CONNECT_ERROR;
+}
+
+CURLcode gtls_client_init(struct Curl_easy *data,
+ struct ssl_primary_config *config,
+ struct ssl_config_data *ssl_config,
+ const char *hostname,
+ struct gtls_instance *gtls,
+ long *pverifyresult)
+{
+ unsigned int init_flags;
+ int rc;
+ bool sni = TRUE; /* default is SNI enabled */
+#ifdef ENABLE_IPV6
+ struct in6_addr addr;
+#else
+ struct in_addr addr;
+#endif
+ const char *prioritylist;
+ const char *err = NULL;
+ const char *tls13support;
+ CURLcode result;
+
+ if(!gtls_inited)
+ gtls_init();
+
+ *pverifyresult = 0;
+
+ if(config->version == CURL_SSLVERSION_SSLv2) {
+ failf(data, "GnuTLS does not support SSLv2");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else if(config->version == CURL_SSLVERSION_SSLv3)
+ sni = FALSE; /* SSLv3 has no SNI */
+
+ /* allocate a cred struct */
+ rc = gnutls_certificate_allocate_credentials(>ls->cred);
+ if(rc != GNUTLS_E_SUCCESS) {
+ failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+#ifdef USE_GNUTLS_SRP
+ if((config->authtype == CURL_TLSAUTH_SRP) &&
+ Curl_auth_allowed_to_host(data)) {
+ infof(data, "Using TLS-SRP username: %s", config->username);
+
+ rc = gnutls_srp_allocate_client_credentials(
+ >ls->srp_client_cred);
+ if(rc != GNUTLS_E_SUCCESS) {
+ failf(data, "gnutls_srp_allocate_client_cred() failed: %s",
+ gnutls_strerror(rc));
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ rc = gnutls_srp_set_client_credentials(gtls->srp_client_cred,
+ config->username,
+ config->password);
+ if(rc != GNUTLS_E_SUCCESS) {
+ failf(data, "gnutls_srp_set_client_cred() failed: %s",
+ gnutls_strerror(rc));
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+ }
+#endif
+
+ if(config->CAfile) {
+ /* set the trusted CA cert bundle file */
+ gnutls_certificate_set_verify_flags(gtls->cred,
+ GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
+
+ rc = gnutls_certificate_set_x509_trust_file(gtls->cred,
+ config->CAfile,
+ GNUTLS_X509_FMT_PEM);
+ if(rc < 0) {
+ infof(data, "error reading ca cert file %s (%s)",
+ config->CAfile, gnutls_strerror(rc));
+ if(config->verifypeer) {
+ *pverifyresult = rc;
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ }
+ else
+ infof(data, "found %d certificates in %s", rc, config->CAfile);
+ }
+
+ if(config->CApath) {
+ /* set the trusted CA cert directory */
+ rc = gnutls_certificate_set_x509_trust_dir(gtls->cred,
+ config->CApath,
+ GNUTLS_X509_FMT_PEM);
+ if(rc < 0) {
+ infof(data, "error reading ca cert file %s (%s)",
+ config->CApath, gnutls_strerror(rc));
+ if(config->verifypeer) {
+ *pverifyresult = rc;
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ }
+ else
+ infof(data, "found %d certificates in %s", rc, config->CApath);
+ }
+
+#ifdef CURL_CA_FALLBACK
+ /* use system ca certificate store as fallback */
+ if(config->verifypeer && !(config->CAfile || config->CApath)) {
+ /* this ignores errors on purpose */
+ gnutls_certificate_set_x509_system_trust(gtls->cred);
+ }
+#endif
+
+ if(config->CRLfile) {
+ /* set the CRL list file */
+ rc = gnutls_certificate_set_x509_crl_file(gtls->cred,
+ config->CRLfile,
+ GNUTLS_X509_FMT_PEM);
+ if(rc < 0) {
+ failf(data, "error reading crl file %s (%s)",
+ config->CRLfile, gnutls_strerror(rc));
+ return CURLE_SSL_CRL_BADFILE;
+ }
+ else
+ infof(data, "found %d CRL in %s", rc, config->CRLfile);
+ }
+
+ /* Initialize TLS session as a client */
+ init_flags = GNUTLS_CLIENT;
+
+#if defined(GNUTLS_FORCE_CLIENT_CERT)
+ init_flags |= GNUTLS_FORCE_CLIENT_CERT;
+#endif
+
+#if defined(GNUTLS_NO_TICKETS)
+ /* Disable TLS session tickets */
+ init_flags |= GNUTLS_NO_TICKETS;
+#endif
+
+ rc = gnutls_init(>ls->session, init_flags);
+ if(rc != GNUTLS_E_SUCCESS) {
+ failf(data, "gnutls_init() failed: %d", rc);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) &&
+#ifdef ENABLE_IPV6
+ (0 == Curl_inet_pton(AF_INET6, hostname, &addr)) &&
+#endif
+ sni) {
+ size_t snilen;
+ char *snihost = Curl_ssl_snihost(data, hostname, &snilen);
+ if(!snihost || gnutls_server_name_set(gtls->session, GNUTLS_NAME_DNS,
+ snihost, snilen) < 0) {
+ failf(data, "Failed to set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+ /* Use default priorities */
+ rc = gnutls_set_default_priority(gtls->session);
+ if(rc != GNUTLS_E_SUCCESS)
+ return CURLE_SSL_CONNECT_ERROR;
+
+ /* "In GnuTLS 3.6.5, TLS 1.3 is enabled by default" */
+ tls13support = gnutls_check_version("3.6.5");
+
+ /* Ensure +SRP comes at the *end* of all relevant strings so that it can be
+ * removed if a run-time error indicates that SRP is not supported by this
+ * GnuTLS version */
+
+ if(config->version == CURL_SSLVERSION_SSLv2 ||
+ config->version == CURL_SSLVERSION_SSLv3) {
+ failf(data, "GnuTLS does not support SSLv2 or SSLv3");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ if(config->version == CURL_SSLVERSION_TLSv1_3) {
+ if(!tls13support) {
+ failf(data, "This GnuTLS installation does not support TLS 1.3");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+ /* At this point we know we have a supported TLS version, so set it */
+ result = set_ssl_version_min_max(data, config, &prioritylist, tls13support);
+ if(result)
+ return result;
+
+#ifdef USE_GNUTLS_SRP
+ /* Only add SRP to the cipher list if SRP is requested. Otherwise
+ * GnuTLS will disable TLS 1.3 support. */
+ if(config->authtype == CURL_TLSAUTH_SRP) {
+ size_t len = strlen(prioritylist);
+
+ char *prioritysrp = malloc(len + sizeof(GNUTLS_SRP) + 1);
+ if(!prioritysrp)
+ return CURLE_OUT_OF_MEMORY;
+ strcpy(prioritysrp, prioritylist);
+ strcpy(prioritysrp + len, ":" GNUTLS_SRP);
+ rc = gnutls_priority_set_direct(gtls->session, prioritysrp, &err);
+ free(prioritysrp);
+
+ if((rc == GNUTLS_E_INVALID_REQUEST) && err) {
+ infof(data, "This GnuTLS does not support SRP");
+ }
+ }
+ else {
+#endif
+ infof(data, "GnuTLS ciphers: %s", prioritylist);
+ rc = gnutls_priority_set_direct(gtls->session, prioritylist, &err);
+#ifdef USE_GNUTLS_SRP
+ }
+#endif
+
+ if(rc != GNUTLS_E_SUCCESS) {
+ failf(data, "Error %d setting GnuTLS cipher list starting with %s",
+ rc, err);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ if(config->clientcert) {
+ if(ssl_config->key_passwd) {
+ const unsigned int supported_key_encryption_algorithms =
+ GNUTLS_PKCS_USE_PKCS12_3DES | GNUTLS_PKCS_USE_PKCS12_ARCFOUR |
+ GNUTLS_PKCS_USE_PKCS12_RC2_40 | GNUTLS_PKCS_USE_PBES2_3DES |
+ GNUTLS_PKCS_USE_PBES2_AES_128 | GNUTLS_PKCS_USE_PBES2_AES_192 |
+ GNUTLS_PKCS_USE_PBES2_AES_256;
+ rc = gnutls_certificate_set_x509_key_file2(
+ gtls->cred,
+ config->clientcert,
+ ssl_config->key ? ssl_config->key : config->clientcert,
+ do_file_type(ssl_config->cert_type),
+ ssl_config->key_passwd,
+ supported_key_encryption_algorithms);
+ if(rc != GNUTLS_E_SUCCESS) {
+ failf(data,
+ "error reading X.509 potentially-encrypted key file: %s",
+ gnutls_strerror(rc));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ else {
+ if(gnutls_certificate_set_x509_key_file(
+ gtls->cred,
+ config->clientcert,
+ ssl_config->key ? ssl_config->key : config->clientcert,
+ do_file_type(ssl_config->cert_type) ) !=
+ GNUTLS_E_SUCCESS) {
+ failf(data, "error reading X.509 key or certificate file");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ }
+
+#ifdef USE_GNUTLS_SRP
+ /* put the credentials to the current session */
+ if(config->authtype == CURL_TLSAUTH_SRP) {
+ rc = gnutls_credentials_set(gtls->session, GNUTLS_CRD_SRP,
+ gtls->srp_client_cred);
+ if(rc != GNUTLS_E_SUCCESS) {
+ failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ else
+#endif
+ {
+ rc = gnutls_credentials_set(gtls->session, GNUTLS_CRD_CERTIFICATE,
+ gtls->cred);
+ if(rc != GNUTLS_E_SUCCESS) {
+ failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+ if(config->verifystatus) {
+ rc = gnutls_ocsp_status_request_enable_client(gtls->session,
+ NULL, 0, NULL);
+ if(rc != GNUTLS_E_SUCCESS) {
+ failf(data, "gnutls_ocsp_status_request_enable_client() failed: %d", rc);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode
+gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ long * const pverifyresult = &ssl_config->certverifyresult;
+ CURLcode result;
+
+ DEBUGASSERT(backend);
+
+ if(connssl->state == ssl_connection_complete)
+ /* to make us tolerant against being called more than once for the
+ same connection */
+ return CURLE_OK;
+
+ result = gtls_client_init(data, conn_config, ssl_config,
+ connssl->hostname,
+ &backend->gtls, pverifyresult);
+ if(result)
+ return result;
+
+ if(cf->conn->bits.tls_enable_alpn) {
+ int cur = 0;
+ gnutls_datum_t protocols[2];
+
+#ifdef USE_HTTP2
+ if(data->state.httpwant >= CURL_HTTP_VERSION_2
+#ifndef CURL_DISABLE_PROXY
+ && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy)
+#endif
+ ) {
+ protocols[cur].data = (unsigned char *)ALPN_H2;
+ protocols[cur].size = ALPN_H2_LENGTH;
+ cur++;
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
+ }
+#endif
+
+ protocols[cur].data = (unsigned char *)ALPN_HTTP_1_1;
+ protocols[cur].size = ALPN_HTTP_1_1_LENGTH;
+ cur++;
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
+
+ if(gnutls_alpn_set_protocols(backend->gtls.session, protocols, cur, 0)) {
+ failf(data, "failed setting ALPN");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+ /* This might be a reconnect, so we check for a session ID in the cache
+ to speed up things */
+ if(conn_config->sessionid) {
+ void *ssl_sessionid;
+ size_t ssl_idsize;
+
+ Curl_ssl_sessionid_lock(data);
+ if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, &ssl_idsize)) {
+ /* we got a session id, use it! */
+ gnutls_session_set_data(backend->gtls.session,
+ ssl_sessionid, ssl_idsize);
+
+ /* Informational message */
+ infof(data, "SSL re-using session ID");
+ }
+ Curl_ssl_sessionid_unlock(data);
+ }
+
+ /* register callback functions and handle to send and receive data. */
+ gnutls_transport_set_ptr(backend->gtls.session, cf);
+ gnutls_transport_set_push_function(backend->gtls.session, gtls_push);
+ gnutls_transport_set_pull_function(backend->gtls.session, gtls_pull);
+
+ return CURLE_OK;
+}
+
+static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
+ gnutls_x509_crt_t cert,
+ const char *pinnedpubkey)
+{
+ /* Scratch */
+ size_t len1 = 0, len2 = 0;
+ unsigned char *buff1 = NULL;
+
+ gnutls_pubkey_t key = NULL;
+
+ /* Result is returned to caller */
+ CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+
+ /* if a path wasn't specified, don't pin */
+ if(!pinnedpubkey)
+ return CURLE_OK;
+
+ if(!cert)
+ return result;
+
+ do {
+ int ret;
+
+ /* Begin Gyrations to get the public key */
+ gnutls_pubkey_init(&key);
+
+ ret = gnutls_pubkey_import_x509(key, cert, 0);
+ if(ret < 0)
+ break; /* failed */
+
+ ret = gnutls_pubkey_export(key, GNUTLS_X509_FMT_DER, NULL, &len1);
+ if(ret != GNUTLS_E_SHORT_MEMORY_BUFFER || len1 == 0)
+ break; /* failed */
+
+ buff1 = malloc(len1);
+ if(!buff1)
+ break; /* failed */
+
+ len2 = len1;
+
+ ret = gnutls_pubkey_export(key, GNUTLS_X509_FMT_DER, buff1, &len2);
+ if(ret < 0 || len1 != len2)
+ break; /* failed */
+
+ /* End Gyrations */
+
+ /* The one good exit point */
+ result = Curl_pin_peer_pubkey(data, pinnedpubkey, buff1, len1);
+ } while(0);
+
+ if(key)
+ gnutls_pubkey_deinit(key);
+
+ Curl_safefree(buff1);
+
+ return result;
+}
+
+CURLcode
+Curl_gtls_verifyserver(struct Curl_easy *data,
+ gnutls_session_t session,
+ struct ssl_primary_config *config,
+ struct ssl_config_data *ssl_config,
+ const char *hostname,
+ const char *dispname,
+ const char *pinned_key)
+{
+ unsigned int cert_list_size;
+ const gnutls_datum_t *chainp;
+ unsigned int verify_status = 0;
+ gnutls_x509_crt_t x509_cert, x509_issuer;
+ gnutls_datum_t issuerp;
+ gnutls_datum_t certfields;
+ char certname[65] = ""; /* limited to 64 chars by ASN.1 */
+ size_t size;
+ time_t certclock;
+ const char *ptr;
+ int rc;
+ CURLcode result = CURLE_OK;
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ unsigned int algo;
+ unsigned int bits;
+ gnutls_protocol_t version = gnutls_protocol_get_version(session);
+#endif
+ long * const certverifyresult = &ssl_config->certverifyresult;
+
+ /* the name of the cipher suite used, e.g. ECDHE_RSA_AES_256_GCM_SHA384. */
+ ptr = gnutls_cipher_suite_get_name(gnutls_kx_get(session),
+ gnutls_cipher_get(session),
+ gnutls_mac_get(session));
+
+ infof(data, "SSL connection using %s / %s",
+ gnutls_protocol_get_name(version), ptr);
+
+ /* This function will return the peer's raw certificate (chain) as sent by
+ the peer. These certificates are in raw format (DER encoded for
+ X.509). In case of a X.509 then a certificate list may be present. The
+ first certificate in the list is the peer's certificate, following the
+ issuer's certificate, then the issuer's issuer etc. */
+
+ chainp = gnutls_certificate_get_peers(session, &cert_list_size);
+ if(!chainp) {
+ if(config->verifypeer ||
+ config->verifyhost ||
+ config->issuercert) {
+#ifdef USE_GNUTLS_SRP
+ if(ssl_config->primary.authtype == CURL_TLSAUTH_SRP
+ && ssl_config->primary.username
+ && !config->verifypeer
+ && gnutls_cipher_get(session)) {
+ /* no peer cert, but auth is ok if we have SRP user and cipher and no
+ peer verify */
+ }
+ else {
+#endif
+ failf(data, "failed to get server cert");
+ *certverifyresult = GNUTLS_E_NO_CERTIFICATE_FOUND;
+ return CURLE_PEER_FAILED_VERIFICATION;
+#ifdef USE_GNUTLS_SRP
+ }
+#endif
+ }
+ infof(data, " common name: WARNING couldn't obtain");
+ }
+
+ if(data->set.ssl.certinfo && chainp) {
+ unsigned int i;
+
+ result = Curl_ssl_init_certinfo(data, cert_list_size);
+ if(result)
+ return result;
+
+ for(i = 0; i < cert_list_size; i++) {
+ const char *beg = (const char *) chainp[i].data;
+ const char *end = beg + chainp[i].size;
+
+ result = Curl_extract_certinfo(data, i, beg, end);
+ if(result)
+ return result;
+ }
+ }
+
+ if(config->verifypeer) {
+ /* This function will try to verify the peer's certificate and return its
+ status (trusted, invalid etc.). The value of status should be one or
+ more of the gnutls_certificate_status_t enumerated elements bitwise
+ or'd. To avoid denial of service attacks some default upper limits
+ regarding the certificate key size and chain size are set. To override
+ them use gnutls_certificate_set_verify_limits(). */
+
+ rc = gnutls_certificate_verify_peers2(session, &verify_status);
+ if(rc < 0) {
+ failf(data, "server cert verify failed: %d", rc);
+ *certverifyresult = rc;
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ *certverifyresult = verify_status;
+
+ /* verify_status is a bitmask of gnutls_certificate_status bits */
+ if(verify_status & GNUTLS_CERT_INVALID) {
+ if(config->verifypeer) {
+ failf(data, "server certificate verification failed. CAfile: %s "
+ "CRLfile: %s", config->CAfile ? config->CAfile:
+ "none",
+ ssl_config->primary.CRLfile ?
+ ssl_config->primary.CRLfile : "none");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else
+ infof(data, " server certificate verification FAILED");
+ }
+ else
+ infof(data, " server certificate verification OK");
+ }
+ else
+ infof(data, " server certificate verification SKIPPED");
+
+ if(config->verifystatus) {
+ if(gnutls_ocsp_status_request_is_checked(session, 0) == 0) {
+ gnutls_datum_t status_request;
+ gnutls_ocsp_resp_t ocsp_resp;
+
+ gnutls_ocsp_cert_status_t status;
+ gnutls_x509_crl_reason_t reason;
+
+ rc = gnutls_ocsp_status_request_get(session, &status_request);
+
+ infof(data, " server certificate status verification FAILED");
+
+ if(rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ failf(data, "No OCSP response received");
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
+
+ if(rc < 0) {
+ failf(data, "Invalid OCSP response received");
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
+
+ gnutls_ocsp_resp_init(&ocsp_resp);
+
+ rc = gnutls_ocsp_resp_import(ocsp_resp, &status_request);
+ if(rc < 0) {
+ failf(data, "Invalid OCSP response received");
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
+
+ (void)gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL,
+ &status, NULL, NULL, NULL, &reason);
+
+ switch(status) {
+ case GNUTLS_OCSP_CERT_GOOD:
+ break;
+
+ case GNUTLS_OCSP_CERT_REVOKED: {
+ const char *crl_reason;
+
+ switch(reason) {
+ default:
+ case GNUTLS_X509_CRLREASON_UNSPECIFIED:
+ crl_reason = "unspecified reason";
+ break;
+
+ case GNUTLS_X509_CRLREASON_KEYCOMPROMISE:
+ crl_reason = "private key compromised";
+ break;
+
+ case GNUTLS_X509_CRLREASON_CACOMPROMISE:
+ crl_reason = "CA compromised";
+ break;
+
+ case GNUTLS_X509_CRLREASON_AFFILIATIONCHANGED:
+ crl_reason = "affiliation has changed";
+ break;
+
+ case GNUTLS_X509_CRLREASON_SUPERSEDED:
+ crl_reason = "certificate superseded";
+ break;
+
+ case GNUTLS_X509_CRLREASON_CESSATIONOFOPERATION:
+ crl_reason = "operation has ceased";
+ break;
+
+ case GNUTLS_X509_CRLREASON_CERTIFICATEHOLD:
+ crl_reason = "certificate is on hold";
+ break;
+
+ case GNUTLS_X509_CRLREASON_REMOVEFROMCRL:
+ crl_reason = "will be removed from delta CRL";
+ break;
+
+ case GNUTLS_X509_CRLREASON_PRIVILEGEWITHDRAWN:
+ crl_reason = "privilege withdrawn";
+ break;
+
+ case GNUTLS_X509_CRLREASON_AACOMPROMISE:
+ crl_reason = "AA compromised";
+ break;
+ }
+
+ failf(data, "Server certificate was revoked: %s", crl_reason);
+ break;
+ }
+
+ default:
+ case GNUTLS_OCSP_CERT_UNKNOWN:
+ failf(data, "Server certificate status is unknown");
+ break;
+ }
+
+ gnutls_ocsp_resp_deinit(ocsp_resp);
+
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
+ else
+ infof(data, " server certificate status verification OK");
+ }
+ else
+ infof(data, " server certificate status verification SKIPPED");
+
+ /* initialize an X.509 certificate structure. */
+ gnutls_x509_crt_init(&x509_cert);
+
+ if(chainp)
+ /* convert the given DER or PEM encoded Certificate to the native
+ gnutls_x509_crt_t format */
+ gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER);
+
+ if(config->issuercert) {
+ gnutls_x509_crt_init(&x509_issuer);
+ issuerp = load_file(config->issuercert);
+ gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM);
+ rc = gnutls_x509_crt_check_issuer(x509_cert, x509_issuer);
+ gnutls_x509_crt_deinit(x509_issuer);
+ unload_file(issuerp);
+ if(rc <= 0) {
+ failf(data, "server certificate issuer check failed (IssuerCert: %s)",
+ config->issuercert?config->issuercert:"none");
+ gnutls_x509_crt_deinit(x509_cert);
+ return CURLE_SSL_ISSUER_ERROR;
+ }
+ infof(data, " server certificate issuer check OK (Issuer Cert: %s)",
+ config->issuercert?config->issuercert:"none");
+ }
+
+ size = sizeof(certname);
+ rc = gnutls_x509_crt_get_dn_by_oid(x509_cert, GNUTLS_OID_X520_COMMON_NAME,
+ 0, /* the first and only one */
+ FALSE,
+ certname,
+ &size);
+ if(rc) {
+ infof(data, "error fetching CN from cert:%s",
+ gnutls_strerror(rc));
+ }
+
+ /* This function will check if the given certificate's subject matches the
+ given hostname. This is a basic implementation of the matching described
+ in RFC2818 (HTTPS), which takes into account wildcards, and the subject
+ alternative name PKIX extension. Returns non zero on success, and zero on
+ failure. */
+ rc = gnutls_x509_crt_check_hostname(x509_cert, hostname);
+#if GNUTLS_VERSION_NUMBER < 0x030306
+ /* Before 3.3.6, gnutls_x509_crt_check_hostname() didn't check IP
+ addresses. */
+ if(!rc) {
+#ifdef ENABLE_IPV6
+ #define use_addr in6_addr
+#else
+ #define use_addr in_addr
+#endif
+ unsigned char addrbuf[sizeof(struct use_addr)];
+ size_t addrlen = 0;
+
+ if(Curl_inet_pton(AF_INET, hostname, addrbuf) > 0)
+ addrlen = 4;
+#ifdef ENABLE_IPV6
+ else if(Curl_inet_pton(AF_INET6, hostname, addrbuf) > 0)
+ addrlen = 16;
+#endif
+
+ if(addrlen) {
+ unsigned char certaddr[sizeof(struct use_addr)];
+ int i;
+
+ for(i = 0; ; i++) {
+ size_t certaddrlen = sizeof(certaddr);
+ int ret = gnutls_x509_crt_get_subject_alt_name(x509_cert, i, certaddr,
+ &certaddrlen, NULL);
+ /* If this happens, it wasn't an IP address. */
+ if(ret == GNUTLS_E_SHORT_MEMORY_BUFFER)
+ continue;
+ if(ret < 0)
+ break;
+ if(ret != GNUTLS_SAN_IPADDRESS)
+ continue;
+ if(certaddrlen == addrlen && !memcmp(addrbuf, certaddr, addrlen)) {
+ rc = 1;
+ break;
+ }
+ }
+ }
+ }
+#endif
+ if(!rc) {
+ if(config->verifyhost) {
+ failf(data, "SSL: certificate subject name (%s) does not match "
+ "target host name '%s'", certname, dispname);
+ gnutls_x509_crt_deinit(x509_cert);
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else
+ infof(data, " common name: %s (does not match '%s')",
+ certname, dispname);
+ }
+ else
+ infof(data, " common name: %s (matched)", certname);
+
+ /* Check for time-based validity */
+ certclock = gnutls_x509_crt_get_expiration_time(x509_cert);
+
+ if(certclock == (time_t)-1) {
+ if(config->verifypeer) {
+ failf(data, "server cert expiration date verify failed");
+ *certverifyresult = GNUTLS_CERT_EXPIRED;
+ gnutls_x509_crt_deinit(x509_cert);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else
+ infof(data, " server certificate expiration date verify FAILED");
+ }
+ else {
+ if(certclock < time(NULL)) {
+ if(config->verifypeer) {
+ failf(data, "server certificate expiration date has passed.");
+ *certverifyresult = GNUTLS_CERT_EXPIRED;
+ gnutls_x509_crt_deinit(x509_cert);
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else
+ infof(data, " server certificate expiration date FAILED");
+ }
+ else
+ infof(data, " server certificate expiration date OK");
+ }
+
+ certclock = gnutls_x509_crt_get_activation_time(x509_cert);
+
+ if(certclock == (time_t)-1) {
+ if(config->verifypeer) {
+ failf(data, "server cert activation date verify failed");
+ *certverifyresult = GNUTLS_CERT_NOT_ACTIVATED;
+ gnutls_x509_crt_deinit(x509_cert);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else
+ infof(data, " server certificate activation date verify FAILED");
+ }
+ else {
+ if(certclock > time(NULL)) {
+ if(config->verifypeer) {
+ failf(data, "server certificate not activated yet.");
+ *certverifyresult = GNUTLS_CERT_NOT_ACTIVATED;
+ gnutls_x509_crt_deinit(x509_cert);
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else
+ infof(data, " server certificate activation date FAILED");
+ }
+ else
+ infof(data, " server certificate activation date OK");
+ }
+
+ if(pinned_key) {
+ result = pkp_pin_peer_pubkey(data, x509_cert, pinned_key);
+ if(result != CURLE_OK) {
+ failf(data, "SSL: public key does not match pinned public key");
+ gnutls_x509_crt_deinit(x509_cert);
+ return result;
+ }
+ }
+
+ /* Show:
+
+ - subject
+ - start date
+ - expire date
+ - common name
+ - issuer
+
+ */
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ /* public key algorithm's parameters */
+ algo = gnutls_x509_crt_get_pk_algorithm(x509_cert, &bits);
+ infof(data, " certificate public key: %s",
+ gnutls_pk_algorithm_get_name(algo));
+
+ /* version of the X.509 certificate. */
+ infof(data, " certificate version: #%d",
+ gnutls_x509_crt_get_version(x509_cert));
+
+
+ rc = gnutls_x509_crt_get_dn2(x509_cert, &certfields);
+ if(rc)
+ infof(data, "Failed to get certificate name");
+ else {
+ infof(data, " subject: %s", certfields.data);
+
+ certclock = gnutls_x509_crt_get_activation_time(x509_cert);
+ showtime(data, "start date", certclock);
+
+ certclock = gnutls_x509_crt_get_expiration_time(x509_cert);
+ showtime(data, "expire date", certclock);
+
+ gnutls_free(certfields.data);
+ }
+
+ rc = gnutls_x509_crt_get_issuer_dn2(x509_cert, &certfields);
+ if(rc)
+ infof(data, "Failed to get certificate issuer");
+ else {
+ infof(data, " issuer: %s", certfields.data);
+
+ gnutls_free(certfields.data);
+ }
+#endif
+
+ gnutls_x509_crt_deinit(x509_cert);
+
+ return result;
+}
+
+static CURLcode gtls_verifyserver(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ gnutls_session_t session)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ const char *pinned_key = Curl_ssl_cf_is_proxy(cf)?
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY];
+ CURLcode result;
+
+ result = Curl_gtls_verifyserver(data, session, conn_config, ssl_config,
+ connssl->hostname, connssl->dispname,
+ pinned_key);
+ if(result)
+ goto out;
+
+ if(cf->conn->bits.tls_enable_alpn) {
+ gnutls_datum_t proto;
+ int rc;
+
+ rc = gnutls_alpn_get_selected_protocol(session, &proto);
+ if(rc == 0) {
+ infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, proto.size,
+ proto.data);
+
+#ifdef USE_HTTP2
+ if(proto.size == ALPN_H2_LENGTH &&
+ !memcmp(ALPN_H2, proto.data,
+ ALPN_H2_LENGTH)) {
+ cf->conn->alpn = CURL_HTTP_VERSION_2;
+ }
+ else
+#endif
+ if(proto.size == ALPN_HTTP_1_1_LENGTH &&
+ !memcmp(ALPN_HTTP_1_1, proto.data, ALPN_HTTP_1_1_LENGTH)) {
+ cf->conn->alpn = CURL_HTTP_VERSION_1_1;
+ }
+ }
+ else
+ infof(data, VTLS_INFOF_NO_ALPN);
+
+ Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ }
+
+ if(ssl_config->primary.sessionid) {
+ /* we always unconditionally get the session id here, as even if we
+ already got it from the cache and asked to use it in the connection, it
+ might've been rejected and then a new one is in use now and we need to
+ detect that. */
+ void *connect_sessionid;
+ size_t connect_idsize = 0;
+
+ /* get the session ID data size */
+ gnutls_session_get_data(session, NULL, &connect_idsize);
+ connect_sessionid = malloc(connect_idsize); /* get a buffer for it */
+
+ if(connect_sessionid) {
+ bool incache;
+ bool added = FALSE;
+ void *ssl_sessionid;
+
+ /* extract session ID to the allocated buffer */
+ gnutls_session_get_data(session, connect_sessionid, &connect_idsize);
+
+ Curl_ssl_sessionid_lock(data);
+ incache = !(Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL));
+ if(incache) {
+ /* there was one before in the cache, so instead of risking that the
+ previous one was rejected, we just kill that and store the new */
+ Curl_ssl_delsessionid(data, ssl_sessionid);
+ }
+
+ /* store this session id */
+ result = Curl_ssl_addsessionid(cf, data, connect_sessionid,
+ connect_idsize, &added);
+ Curl_ssl_sessionid_unlock(data);
+ if(!added)
+ free(connect_sessionid);
+ if(result) {
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ }
+ else
+ result = CURLE_OUT_OF_MEMORY;
+ }
+
+out:
+ return result;
+}
+
+/*
+ * This function is called after the TCP connect has completed. Setup the TLS
+ * layer and do all necessary magic.
+ */
+/* We use connssl->connecting_state to keep track of the connection status;
+ there are three states: 'ssl_connect_1' (not started yet or complete),
+ 'ssl_connect_2_reading' (waiting for data from server), and
+ 'ssl_connect_2_writing' (waiting to be able to write).
+ */
+static CURLcode
+gtls_connect_common(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool nonblocking,
+ bool *done)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ int rc;
+ CURLcode result = CURLE_OK;
+
+ /* Initiate the connection, if not already done */
+ if(ssl_connect_1 == connssl->connecting_state) {
+ rc = gtls_connect_step1(cf, data);
+ if(rc) {
+ result = rc;
+ goto out;
+ }
+ }
+
+ rc = handshake(cf, data, TRUE, nonblocking);
+ if(rc) {
+ /* handshake() sets its own error message with failf() */
+ result = rc;
+ goto out;
+ }
+
+ /* Finish connecting once the handshake is done */
+ if(ssl_connect_1 == connssl->connecting_state) {
+ struct ssl_backend_data *backend = connssl->backend;
+ gnutls_session_t session;
+ DEBUGASSERT(backend);
+ session = backend->gtls.session;
+ rc = gtls_verifyserver(cf, data, session);
+ if(rc) {
+ result = rc;
+ goto out;
+ }
+ connssl->state = ssl_connection_complete;
+ }
+
+out:
+ *done = ssl_connect_1 == connssl->connecting_state;
+
+ return result;
+}
+
+static CURLcode gtls_connect_nonblocking(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
+{
+ return gtls_connect_common(cf, data, TRUE, done);
+}
+
+static CURLcode gtls_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ CURLcode result;
+ bool done = FALSE;
+
+ result = gtls_connect_common(cf, data, FALSE, &done);
+ if(result)
+ return result;
+
+ DEBUGASSERT(done);
+
+ return CURLE_OK;
+}
+
+static bool gtls_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ struct ssl_connect_data *ctx = cf->ctx;
+
+ (void)data;
+ DEBUGASSERT(ctx && ctx->backend);
+ if(ctx->backend->gtls.session &&
+ 0 != gnutls_record_check_pending(ctx->backend->gtls.session))
+ return TRUE;
+ return FALSE;
+}
+
+static ssize_t gtls_send(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *mem,
+ size_t len,
+ CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ ssize_t rc;
+
+ (void)data;
+ DEBUGASSERT(backend);
+ rc = gnutls_record_send(backend->gtls.session, mem, len);
+
+ if(rc < 0) {
+ *curlcode = (rc == GNUTLS_E_AGAIN)
+ ? CURLE_AGAIN
+ : CURLE_SEND_ERROR;
+
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static void gtls_close(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+
+ (void) data;
+ DEBUGASSERT(backend);
+
+ if(backend->gtls.session) {
+ char buf[32];
+ /* Maybe the server has already sent a close notify alert.
+ Read it to avoid an RST on the TCP connection. */
+ (void)gnutls_record_recv(backend->gtls.session, buf, sizeof(buf));
+ gnutls_bye(backend->gtls.session, GNUTLS_SHUT_WR);
+ gnutls_deinit(backend->gtls.session);
+ backend->gtls.session = NULL;
+ }
+ if(backend->gtls.cred) {
+ gnutls_certificate_free_credentials(backend->gtls.cred);
+ backend->gtls.cred = NULL;
+ }
+#ifdef USE_GNUTLS_SRP
+ if(backend->gtls.srp_client_cred) {
+ gnutls_srp_free_client_credentials(backend->gtls.srp_client_cred);
+ backend->gtls.srp_client_cred = NULL;
+ }
+#endif
+}
+
+/*
+ * This function is called to shut down the SSL layer but keep the
+ * socket open (CCC - Clear Command Channel)
+ */
+static int gtls_shutdown(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ struct ssl_backend_data *backend = connssl->backend;
+ int retval = 0;
+
+ DEBUGASSERT(backend);
+
+#ifndef CURL_DISABLE_FTP
+ /* This has only been tested on the proftpd server, and the mod_tls code
+ sends a close notify alert without waiting for a close notify alert in
+ response. Thus we wait for a close notify alert from the server, but
+ we do not send one. Let's hope other servers do the same... */
+
+ if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)
+ gnutls_bye(backend->gtls.session, GNUTLS_SHUT_WR);
+#endif
+
+ if(backend->gtls.session) {
+ ssize_t result;
+ bool done = FALSE;
+ char buf[120];
+
+ while(!done) {
+ int what = SOCKET_READABLE(cf->conn->sock[cf->sockindex],
+ SSL_SHUTDOWN_TIMEOUT);
+ if(what > 0) {
+ /* Something to read, let's do it and hope that it is the close
+ notify alert from the server */
+ result = gnutls_record_recv(backend->gtls.session,
+ buf, sizeof(buf));
+ switch(result) {
+ case 0:
+ /* This is the expected response. There was no data but only
+ the close notify alert */
+ done = TRUE;
+ break;
+ case GNUTLS_E_AGAIN:
+ case GNUTLS_E_INTERRUPTED:
+ infof(data, "GNUTLS_E_AGAIN || GNUTLS_E_INTERRUPTED");
+ break;
+ default:
+ retval = -1;
+ done = TRUE;
+ break;
+ }
+ }
+ else if(0 == what) {
+ /* timeout */
+ failf(data, "SSL shutdown timeout");
+ done = TRUE;
+ }
+ else {
+ /* anything that gets here is fatally bad */
+ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+ retval = -1;
+ done = TRUE;
+ }
+ }
+ gnutls_deinit(backend->gtls.session);
+ }
+ gnutls_certificate_free_credentials(backend->gtls.cred);
+
+#ifdef USE_GNUTLS_SRP
+ if(ssl_config->primary.authtype == CURL_TLSAUTH_SRP
+ && ssl_config->primary.username != NULL)
+ gnutls_srp_free_client_credentials(backend->gtls.srp_client_cred);
+#endif
+
+ backend->gtls.cred = NULL;
+ backend->gtls.session = NULL;
+
+ return retval;
+}
+
+static ssize_t gtls_recv(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ char *buf,
+ size_t buffersize,
+ CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ ssize_t ret;
+
+ (void)data;
+ DEBUGASSERT(backend);
+
+ ret = gnutls_record_recv(backend->gtls.session, buf, buffersize);
+ if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) {
+ *curlcode = CURLE_AGAIN;
+ ret = -1;
+ goto out;
+ }
+
+ if(ret == GNUTLS_E_REHANDSHAKE) {
+ /* BLOCKING call, this is bad but a work-around for now. Fixing this "the
+ proper way" takes a whole lot of work. */
+ CURLcode result = handshake(cf, data, FALSE, FALSE);
+ if(result)
+ /* handshake() writes error message on its own */
+ *curlcode = result;
+ else
+ *curlcode = CURLE_AGAIN; /* then return as if this was a wouldblock */
+ ret = -1;
+ goto out;
+ }
+
+ if(ret < 0) {
+ failf(data, "GnuTLS recv error (%d): %s",
+
+ (int)ret, gnutls_strerror((int)ret));
+ *curlcode = CURLE_RECV_ERROR;
+ ret = -1;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+static void gtls_session_free(void *ptr)
+{
+ free(ptr);
+}
+
+static size_t gtls_version(char *buffer, size_t size)
+{
+ return msnprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL));
+}
+
+/* data might be NULL! */
+static CURLcode gtls_random(struct Curl_easy *data,
+ unsigned char *entropy, size_t length)
+{
+ int rc;
+ (void)data;
+ rc = gnutls_rnd(GNUTLS_RND_RANDOM, entropy, length);
+ return rc?CURLE_FAILED_INIT:CURLE_OK;
+}
+
+static CURLcode gtls_sha256sum(const unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *sha256sum, /* output */
+ size_t sha256len)
+{
+ struct sha256_ctx SHA256pw;
+ sha256_init(&SHA256pw);
+ sha256_update(&SHA256pw, (unsigned int)tmplen, tmp);
+ sha256_digest(&SHA256pw, (unsigned int)sha256len, sha256sum);
+ return CURLE_OK;
+}
+
+static bool gtls_cert_status_request(void)
+{
+ return TRUE;
+}
+
+static void *gtls_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
+{
+ struct ssl_backend_data *backend = connssl->backend;
+ (void)info;
+ DEBUGASSERT(backend);
+ return backend->gtls.session;
+}
+
+const struct Curl_ssl Curl_ssl_gnutls = {
+ { CURLSSLBACKEND_GNUTLS, "gnutls" }, /* info */
+
+ SSLSUPP_CA_PATH |
+ SSLSUPP_CERTINFO |
+ SSLSUPP_PINNEDPUBKEY |
+ SSLSUPP_HTTPS_PROXY,
+
+ sizeof(struct ssl_backend_data),
+
+ gtls_init, /* init */
+ gtls_cleanup, /* cleanup */
+ gtls_version, /* version */
+ Curl_none_check_cxn, /* check_cxn */
+ gtls_shutdown, /* shutdown */
+ gtls_data_pending, /* data_pending */
+ gtls_random, /* random */
+ gtls_cert_status_request, /* cert_status_request */
+ gtls_connect, /* connect */
+ gtls_connect_nonblocking, /* connect_nonblocking */
+ Curl_ssl_get_select_socks, /* getsock */
+ gtls_get_internals, /* get_internals */
+ gtls_close, /* close_one */
+ Curl_none_close_all, /* close_all */
+ gtls_session_free, /* session_free */
+ Curl_none_set_engine, /* set_engine */
+ Curl_none_set_engine_default, /* set_engine_default */
+ Curl_none_engines_list, /* engines_list */
+ Curl_none_false_start, /* false_start */
+ gtls_sha256sum, /* sha256sum */
+ NULL, /* associate_connection */
+ NULL, /* disassociate_connection */
+ NULL, /* free_multi_ssl_backend_data */
+ gtls_recv, /* recv decrypted data */
+ gtls_send, /* send data to encrypt */
+};
+
+#endif /* USE_GNUTLS */
diff --git a/libs/libcurl/src/vtls/gtls.h b/libs/libcurl/src/vtls/gtls.h index abade73f80..06e8414fdc 100644 --- a/libs/libcurl/src/vtls/gtls.h +++ b/libs/libcurl/src/vtls/gtls.h @@ -1,40 +1,75 @@ -#ifndef HEADER_CURL_GTLS_H -#define HEADER_CURL_GTLS_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_GNUTLS - -#include "urldata.h" -#include <gnutls/gnutls.h> -CURLcode -Curl_gtls_verifyserver(struct Curl_easy *data, struct connectdata *conn, - gnutls_session_t session, - int sockindex); -extern const struct Curl_ssl Curl_ssl_gnutls; - -#endif /* USE_GNUTLS */ -#endif /* HEADER_CURL_GTLS_H */ +#ifndef HEADER_CURL_GTLS_H
+#define HEADER_CURL_GTLS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_GNUTLS
+
+#include <gnutls/gnutls.h>
+
+#ifdef HAVE_GNUTLS_SRP
+/* the function exists */
+#ifdef USE_TLS_SRP
+/* the functionality is not disabled */
+#define USE_GNUTLS_SRP
+#endif
+#endif
+
+struct Curl_easy;
+struct Curl_cfilter;
+struct ssl_primary_config;
+struct ssl_config_data;
+
+struct gtls_instance {
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t cred;
+#ifdef USE_GNUTLS_SRP
+ gnutls_srp_client_credentials_t srp_client_cred;
+#endif
+};
+
+CURLcode
+gtls_client_init(struct Curl_easy *data,
+ struct ssl_primary_config *config,
+ struct ssl_config_data *ssl_config,
+ const char *hostname,
+ struct gtls_instance *gtls,
+ long *pverifyresult);
+
+CURLcode
+Curl_gtls_verifyserver(struct Curl_easy *data,
+ gnutls_session_t session,
+ struct ssl_primary_config *config,
+ struct ssl_config_data *ssl_config,
+ const char *hostname,
+ const char *dispname,
+ const char *pinned_key);
+
+extern const struct Curl_ssl Curl_ssl_gnutls;
+
+#endif /* USE_GNUTLS */
+#endif /* HEADER_CURL_GTLS_H */
diff --git a/libs/libcurl/src/vtls/hostcheck.c b/libs/libcurl/src/vtls/hostcheck.c index 2a648f20a9..6173ea2b5b 100644 --- a/libs/libcurl/src/vtls/hostcheck.c +++ b/libs/libcurl/src/vtls/hostcheck.c @@ -1,142 +1,142 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_OPENSSL) \ - || defined(USE_GSKIT) \ - || defined(USE_SCHANNEL) -/* these backends use functions from this file */ - -#ifdef HAVE_NETINET_IN_H -#include <netinet/in.h> -#endif -#ifdef HAVE_NETINET_IN6_H -#include <netinet/in6.h> -#endif -#include "curl_memrchr.h" - -#include "hostcheck.h" -#include "strcase.h" -#include "hostip.h" - -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -/* check the two input strings with given length, but do not - assume they end in nul-bytes */ -static bool pmatch(const char *hostname, size_t hostlen, - const char *pattern, size_t patternlen) -{ - if(hostlen != patternlen) - return FALSE; - return strncasecompare(hostname, pattern, hostlen); -} - -/* - * Match a hostname against a wildcard pattern. - * E.g. - * "foo.host.com" matches "*.host.com". - * - * We use the matching rule described in RFC6125, section 6.4.3. - * https://datatracker.ietf.org/doc/html/rfc6125#section-6.4.3 - * - * In addition: ignore trailing dots in the host names and wildcards, so that - * the names are used normalized. This is what the browsers do. - * - * Do not allow wildcard matching on IP numbers. There are apparently - * certificates being used with an IP address in the CN field, thus making no - * apparent distinction between a name and an IP. We need to detect the use of - * an IP address and not wildcard match on such names. - * - * Return TRUE on a match. FALSE if not. - */ - -static bool hostmatch(const char *hostname, - size_t hostlen, - const char *pattern, - size_t patternlen) -{ - const char *pattern_label_end, *wildcard, *hostname_label_end; - size_t prefixlen, suffixlen; - - /* normalize pattern and hostname by stripping off trailing dots */ - DEBUGASSERT(patternlen); - if(hostname[hostlen-1]=='.') - hostlen--; - if(pattern[patternlen-1]=='.') - patternlen--; - - wildcard = memchr(pattern, '*', patternlen); - if(!wildcard) - return pmatch(hostname, hostlen, pattern, patternlen); - - /* detect IP address as hostname and fail the match if so */ - if(Curl_host_is_ipnum(hostname)) - return FALSE; - - /* We require at least 2 dots in the pattern to avoid too wide wildcard - match. */ - pattern_label_end = memchr(pattern, '.', patternlen); - if(!pattern_label_end || - (memrchr(pattern, '.', patternlen) == pattern_label_end) || - strncasecompare(pattern, "xn--", 4)) - return pmatch(hostname, hostlen, pattern, patternlen); - - hostname_label_end = memchr(hostname, '.', hostlen); - if(!hostname_label_end) - return FALSE; - else { - size_t skiphost = hostname_label_end - hostname; - size_t skiplen = pattern_label_end - pattern; - if(!pmatch(hostname_label_end, hostlen - skiphost, - pattern_label_end, patternlen - skiplen)) - return FALSE; - } - /* The wildcard must match at least one character, so the left-most - label of the hostname is at least as large as the left-most label - of the pattern. */ - if(hostname_label_end - hostname < pattern_label_end - pattern) - return FALSE; - - prefixlen = wildcard - pattern; - suffixlen = pattern_label_end - (wildcard + 1); - return strncasecompare(pattern, hostname, prefixlen) && - strncasecompare(wildcard + 1, hostname_label_end - suffixlen, - suffixlen) ? TRUE : FALSE; -} - -/* - * Curl_cert_hostcheck() returns TRUE if a match and FALSE if not. - */ -bool Curl_cert_hostcheck(const char *match, size_t matchlen, - const char *hostname, size_t hostlen) -{ - if(match && *match && hostname && *hostname) - return hostmatch(hostname, hostlen, match, matchlen); - return FALSE; -} - -#endif /* OPENSSL, GSKIT or schannel+wince */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_OPENSSL) \
+ || defined(USE_GSKIT) \
+ || defined(USE_SCHANNEL)
+/* these backends use functions from this file */
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif
+#include "curl_memrchr.h"
+
+#include "hostcheck.h"
+#include "strcase.h"
+#include "hostip.h"
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/* check the two input strings with given length, but do not
+ assume they end in nul-bytes */
+static bool pmatch(const char *hostname, size_t hostlen,
+ const char *pattern, size_t patternlen)
+{
+ if(hostlen != patternlen)
+ return FALSE;
+ return strncasecompare(hostname, pattern, hostlen);
+}
+
+/*
+ * Match a hostname against a wildcard pattern.
+ * E.g.
+ * "foo.host.com" matches "*.host.com".
+ *
+ * We use the matching rule described in RFC6125, section 6.4.3.
+ * https://datatracker.ietf.org/doc/html/rfc6125#section-6.4.3
+ *
+ * In addition: ignore trailing dots in the host names and wildcards, so that
+ * the names are used normalized. This is what the browsers do.
+ *
+ * Do not allow wildcard matching on IP numbers. There are apparently
+ * certificates being used with an IP address in the CN field, thus making no
+ * apparent distinction between a name and an IP. We need to detect the use of
+ * an IP address and not wildcard match on such names.
+ *
+ * Return TRUE on a match. FALSE if not.
+ */
+
+static bool hostmatch(const char *hostname,
+ size_t hostlen,
+ const char *pattern,
+ size_t patternlen)
+{
+ const char *pattern_label_end, *wildcard, *hostname_label_end;
+ size_t prefixlen, suffixlen;
+
+ /* normalize pattern and hostname by stripping off trailing dots */
+ DEBUGASSERT(patternlen);
+ if(hostname[hostlen-1]=='.')
+ hostlen--;
+ if(pattern[patternlen-1]=='.')
+ patternlen--;
+
+ wildcard = memchr(pattern, '*', patternlen);
+ if(!wildcard)
+ return pmatch(hostname, hostlen, pattern, patternlen);
+
+ /* detect IP address as hostname and fail the match if so */
+ if(Curl_host_is_ipnum(hostname))
+ return FALSE;
+
+ /* We require at least 2 dots in the pattern to avoid too wide wildcard
+ match. */
+ pattern_label_end = memchr(pattern, '.', patternlen);
+ if(!pattern_label_end ||
+ (memrchr(pattern, '.', patternlen) == pattern_label_end) ||
+ strncasecompare(pattern, "xn--", 4))
+ return pmatch(hostname, hostlen, pattern, patternlen);
+
+ hostname_label_end = memchr(hostname, '.', hostlen);
+ if(!hostname_label_end)
+ return FALSE;
+ else {
+ size_t skiphost = hostname_label_end - hostname;
+ size_t skiplen = pattern_label_end - pattern;
+ if(!pmatch(hostname_label_end, hostlen - skiphost,
+ pattern_label_end, patternlen - skiplen))
+ return FALSE;
+ }
+ /* The wildcard must match at least one character, so the left-most
+ label of the hostname is at least as large as the left-most label
+ of the pattern. */
+ if(hostname_label_end - hostname < pattern_label_end - pattern)
+ return FALSE;
+
+ prefixlen = wildcard - pattern;
+ suffixlen = pattern_label_end - (wildcard + 1);
+ return strncasecompare(pattern, hostname, prefixlen) &&
+ strncasecompare(wildcard + 1, hostname_label_end - suffixlen,
+ suffixlen) ? TRUE : FALSE;
+}
+
+/*
+ * Curl_cert_hostcheck() returns TRUE if a match and FALSE if not.
+ */
+bool Curl_cert_hostcheck(const char *match, size_t matchlen,
+ const char *hostname, size_t hostlen)
+{
+ if(match && *match && hostname && *hostname)
+ return hostmatch(hostname, hostlen, match, matchlen);
+ return FALSE;
+}
+
+#endif /* OPENSSL, GSKIT or schannel+wince */
diff --git a/libs/libcurl/src/vtls/hostcheck.h b/libs/libcurl/src/vtls/hostcheck.h index d3c4eab56d..27149100fd 100644 --- a/libs/libcurl/src/vtls/hostcheck.h +++ b/libs/libcurl/src/vtls/hostcheck.h @@ -1,33 +1,33 @@ -#ifndef HEADER_CURL_HOSTCHECK_H -#define HEADER_CURL_HOSTCHECK_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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> - -/* returns TRUE if there's a match */ -bool Curl_cert_hostcheck(const char *match_pattern, size_t matchlen, - const char *hostname, size_t hostlen); - -#endif /* HEADER_CURL_HOSTCHECK_H */ +#ifndef HEADER_CURL_HOSTCHECK_H
+#define HEADER_CURL_HOSTCHECK_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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>
+
+/* returns TRUE if there's a match */
+bool Curl_cert_hostcheck(const char *match_pattern, size_t matchlen,
+ const char *hostname, size_t hostlen);
+
+#endif /* HEADER_CURL_HOSTCHECK_H */
diff --git a/libs/libcurl/src/vtls/keylog.c b/libs/libcurl/src/vtls/keylog.c index 1952a690ca..d42aaa8156 100644 --- a/libs/libcurl/src/vtls/keylog.c +++ b/libs/libcurl/src/vtls/keylog.c @@ -1,159 +1,159 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "keylog.h" -#include <curl/curl.h> - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -#define KEYLOG_LABEL_MAXLEN (sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1) - -#define CLIENT_RANDOM_SIZE 32 - -/* - * The master secret in TLS 1.2 and before is always 48 bytes. In TLS 1.3, the - * secret size depends on the cipher suite's hash function which is 32 bytes - * for SHA-256 and 48 bytes for SHA-384. - */ -#define SECRET_MAXLEN 48 - - -/* The fp for the open SSLKEYLOGFILE, or NULL if not open */ -static FILE *keylog_file_fp; - -void -Curl_tls_keylog_open(void) -{ - char *keylog_file_name; - - if(!keylog_file_fp) { - keylog_file_name = curl_getenv("SSLKEYLOGFILE"); - if(keylog_file_name) { - keylog_file_fp = fopen(keylog_file_name, FOPEN_APPENDTEXT); - if(keylog_file_fp) { -#ifdef WIN32 - if(setvbuf(keylog_file_fp, NULL, _IONBF, 0)) -#else - if(setvbuf(keylog_file_fp, NULL, _IOLBF, 4096)) -#endif - { - fclose(keylog_file_fp); - keylog_file_fp = NULL; - } - } - Curl_safefree(keylog_file_name); - } - } -} - -void -Curl_tls_keylog_close(void) -{ - if(keylog_file_fp) { - fclose(keylog_file_fp); - keylog_file_fp = NULL; - } -} - -bool -Curl_tls_keylog_enabled(void) -{ - return keylog_file_fp != NULL; -} - -bool -Curl_tls_keylog_write_line(const char *line) -{ - /* The current maximum valid keylog line length LF and NUL is 195. */ - size_t linelen; - char buf[256]; - - if(!keylog_file_fp || !line) { - return false; - } - - linelen = strlen(line); - if(linelen == 0 || linelen > sizeof(buf) - 2) { - /* Empty line or too big to fit in a LF and NUL. */ - return false; - } - - memcpy(buf, line, linelen); - if(line[linelen - 1] != '\n') { - buf[linelen++] = '\n'; - } - buf[linelen] = '\0'; - - /* Using fputs here instead of fprintf since libcurl's fprintf replacement - may not be thread-safe. */ - fputs(buf, keylog_file_fp); - return true; -} - -bool -Curl_tls_keylog_write(const char *label, - const unsigned char client_random[CLIENT_RANDOM_SIZE], - const unsigned char *secret, size_t secretlen) -{ - const char *hex = "0123456789ABCDEF"; - size_t pos, i; - char line[KEYLOG_LABEL_MAXLEN + 1 + 2 * CLIENT_RANDOM_SIZE + 1 + - 2 * SECRET_MAXLEN + 1 + 1]; - - if(!keylog_file_fp) { - return false; - } - - pos = strlen(label); - if(pos > KEYLOG_LABEL_MAXLEN || !secretlen || secretlen > SECRET_MAXLEN) { - /* Should never happen - sanity check anyway. */ - return false; - } - - memcpy(line, label, pos); - line[pos++] = ' '; - - /* Client Random */ - for(i = 0; i < CLIENT_RANDOM_SIZE; i++) { - line[pos++] = hex[client_random[i] >> 4]; - line[pos++] = hex[client_random[i] & 0xF]; - } - line[pos++] = ' '; - - /* Secret */ - for(i = 0; i < secretlen; i++) { - line[pos++] = hex[secret[i] >> 4]; - line[pos++] = hex[secret[i] & 0xF]; - } - line[pos++] = '\n'; - line[pos] = '\0'; - - /* Using fputs here instead of fprintf since libcurl's fprintf replacement - may not be thread-safe. */ - fputs(line, keylog_file_fp); - return true; -} +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "keylog.h"
+#include <curl/curl.h>
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define KEYLOG_LABEL_MAXLEN (sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1)
+
+#define CLIENT_RANDOM_SIZE 32
+
+/*
+ * The master secret in TLS 1.2 and before is always 48 bytes. In TLS 1.3, the
+ * secret size depends on the cipher suite's hash function which is 32 bytes
+ * for SHA-256 and 48 bytes for SHA-384.
+ */
+#define SECRET_MAXLEN 48
+
+
+/* The fp for the open SSLKEYLOGFILE, or NULL if not open */
+static FILE *keylog_file_fp;
+
+void
+Curl_tls_keylog_open(void)
+{
+ char *keylog_file_name;
+
+ if(!keylog_file_fp) {
+ keylog_file_name = curl_getenv("SSLKEYLOGFILE");
+ if(keylog_file_name) {
+ keylog_file_fp = fopen(keylog_file_name, FOPEN_APPENDTEXT);
+ if(keylog_file_fp) {
+#ifdef WIN32
+ if(setvbuf(keylog_file_fp, NULL, _IONBF, 0))
+#else
+ if(setvbuf(keylog_file_fp, NULL, _IOLBF, 4096))
+#endif
+ {
+ fclose(keylog_file_fp);
+ keylog_file_fp = NULL;
+ }
+ }
+ Curl_safefree(keylog_file_name);
+ }
+ }
+}
+
+void
+Curl_tls_keylog_close(void)
+{
+ if(keylog_file_fp) {
+ fclose(keylog_file_fp);
+ keylog_file_fp = NULL;
+ }
+}
+
+bool
+Curl_tls_keylog_enabled(void)
+{
+ return keylog_file_fp != NULL;
+}
+
+bool
+Curl_tls_keylog_write_line(const char *line)
+{
+ /* The current maximum valid keylog line length LF and NUL is 195. */
+ size_t linelen;
+ char buf[256];
+
+ if(!keylog_file_fp || !line) {
+ return false;
+ }
+
+ linelen = strlen(line);
+ if(linelen == 0 || linelen > sizeof(buf) - 2) {
+ /* Empty line or too big to fit in a LF and NUL. */
+ return false;
+ }
+
+ memcpy(buf, line, linelen);
+ if(line[linelen - 1] != '\n') {
+ buf[linelen++] = '\n';
+ }
+ buf[linelen] = '\0';
+
+ /* Using fputs here instead of fprintf since libcurl's fprintf replacement
+ may not be thread-safe. */
+ fputs(buf, keylog_file_fp);
+ return true;
+}
+
+bool
+Curl_tls_keylog_write(const char *label,
+ const unsigned char client_random[CLIENT_RANDOM_SIZE],
+ const unsigned char *secret, size_t secretlen)
+{
+ const char *hex = "0123456789ABCDEF";
+ size_t pos, i;
+ char line[KEYLOG_LABEL_MAXLEN + 1 + 2 * CLIENT_RANDOM_SIZE + 1 +
+ 2 * SECRET_MAXLEN + 1 + 1];
+
+ if(!keylog_file_fp) {
+ return false;
+ }
+
+ pos = strlen(label);
+ if(pos > KEYLOG_LABEL_MAXLEN || !secretlen || secretlen > SECRET_MAXLEN) {
+ /* Should never happen - sanity check anyway. */
+ return false;
+ }
+
+ memcpy(line, label, pos);
+ line[pos++] = ' ';
+
+ /* Client Random */
+ for(i = 0; i < CLIENT_RANDOM_SIZE; i++) {
+ line[pos++] = hex[client_random[i] >> 4];
+ line[pos++] = hex[client_random[i] & 0xF];
+ }
+ line[pos++] = ' ';
+
+ /* Secret */
+ for(i = 0; i < secretlen; i++) {
+ line[pos++] = hex[secret[i] >> 4];
+ line[pos++] = hex[secret[i] & 0xF];
+ }
+ line[pos++] = '\n';
+ line[pos] = '\0';
+
+ /* Using fputs here instead of fprintf since libcurl's fprintf replacement
+ may not be thread-safe. */
+ fputs(line, keylog_file_fp);
+ return true;
+}
diff --git a/libs/libcurl/src/vtls/keylog.h b/libs/libcurl/src/vtls/keylog.h index 5d3c675b3e..92530f5eb6 100644 --- a/libs/libcurl/src/vtls/keylog.h +++ b/libs/libcurl/src/vtls/keylog.h @@ -1,58 +1,58 @@ -#ifndef HEADER_CURL_KEYLOG_H -#define HEADER_CURL_KEYLOG_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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" - -/* - * Opens the TLS key log file if requested by the user. The SSLKEYLOGFILE - * environment variable specifies the output file. - */ -void Curl_tls_keylog_open(void); - -/* - * Closes the TLS key log file if not already. - */ -void Curl_tls_keylog_close(void); - -/* - * Returns true if the user successfully enabled the TLS key log file. - */ -bool Curl_tls_keylog_enabled(void); - -/* - * Appends a key log file entry. - * Returns true iff the key log file is open and a valid entry was provided. - */ -bool Curl_tls_keylog_write(const char *label, - const unsigned char client_random[32], - const unsigned char *secret, size_t secretlen); - -/* - * Appends a line to the key log file, ensure it is terminated by a LF. - * Returns true iff the key log file is open and a valid line was provided. - */ -bool Curl_tls_keylog_write_line(const char *line); - -#endif /* HEADER_CURL_KEYLOG_H */ +#ifndef HEADER_CURL_KEYLOG_H
+#define HEADER_CURL_KEYLOG_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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"
+
+/*
+ * Opens the TLS key log file if requested by the user. The SSLKEYLOGFILE
+ * environment variable specifies the output file.
+ */
+void Curl_tls_keylog_open(void);
+
+/*
+ * Closes the TLS key log file if not already.
+ */
+void Curl_tls_keylog_close(void);
+
+/*
+ * Returns true if the user successfully enabled the TLS key log file.
+ */
+bool Curl_tls_keylog_enabled(void);
+
+/*
+ * Appends a key log file entry.
+ * Returns true iff the key log file is open and a valid entry was provided.
+ */
+bool Curl_tls_keylog_write(const char *label,
+ const unsigned char client_random[32],
+ const unsigned char *secret, size_t secretlen);
+
+/*
+ * Appends a line to the key log file, ensure it is terminated by a LF.
+ * Returns true iff the key log file is open and a valid line was provided.
+ */
+bool Curl_tls_keylog_write_line(const char *line);
+
+#endif /* HEADER_CURL_KEYLOG_H */
diff --git a/libs/libcurl/src/vtls/mbedtls.c b/libs/libcurl/src/vtls/mbedtls.c index fbde8976eb..cc88f6adbd 100644 --- a/libs/libcurl/src/vtls/mbedtls.c +++ b/libs/libcurl/src/vtls/mbedtls.c @@ -1,1273 +1,1301 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2010 - 2011, Hoi-Ho Chan, <hoiho.chan@gmail.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 - * - ***************************************************************************/ - -/* - * Source file for all mbedTLS-specific code for the TLS/SSL layer. No code - * but vtls.c should ever call or use these functions. - * - */ - -#include "curl_setup.h" - -#ifdef USE_MBEDTLS - -/* Define this to enable lots of debugging for mbedTLS */ -/* #define MBEDTLS_DEBUG */ - -#include <mbedtls/version.h> -#if MBEDTLS_VERSION_NUMBER >= 0x02040000 -#include <mbedtls/net_sockets.h> -#else -#include <mbedtls/net.h> -#endif -#include <mbedtls/ssl.h> -#include <mbedtls/x509.h> - -#include <mbedtls/error.h> -#include <mbedtls/entropy.h> -#include <mbedtls/ctr_drbg.h> -#include <mbedtls/sha256.h> - -#if MBEDTLS_VERSION_MAJOR >= 2 -# ifdef MBEDTLS_DEBUG -# include <mbedtls/debug.h> -# endif -#endif - -#include "urldata.h" -#include "sendf.h" -#include "inet_pton.h" -#include "mbedtls.h" -#include "vtls.h" -#include "parsedate.h" -#include "connect.h" /* for the connect timeout */ -#include "select.h" -#include "multiif.h" -#include "mbedtls_threadlock.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* ALPN for http2 */ -#ifdef USE_HTTP2 -# undef HAS_ALPN -# ifdef MBEDTLS_SSL_ALPN -# define HAS_ALPN -# endif -#endif - -struct ssl_backend_data { - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_entropy_context entropy; - mbedtls_ssl_context ssl; - mbedtls_x509_crt cacert; - mbedtls_x509_crt clicert; -#ifdef MBEDTLS_X509_CRL_PARSE_C - mbedtls_x509_crl crl; -#endif - mbedtls_pk_context pk; - mbedtls_ssl_config config; -#ifdef HAS_ALPN - const char *protocols[3]; -#endif -}; - -/* apply threading? */ -#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) -#define THREADING_SUPPORT -#endif - -#ifndef MBEDTLS_ERROR_C -#define mbedtls_strerror(a,b,c) b[0] = 0 -#endif - -#if defined(THREADING_SUPPORT) -static mbedtls_entropy_context ts_entropy; - -static int entropy_init_initialized = 0; - -/* start of entropy_init_mutex() */ -static void entropy_init_mutex(mbedtls_entropy_context *ctx) -{ - /* lock 0 = entropy_init_mutex() */ - Curl_mbedtlsthreadlock_lock_function(0); - if(entropy_init_initialized == 0) { - mbedtls_entropy_init(ctx); - entropy_init_initialized = 1; - } - Curl_mbedtlsthreadlock_unlock_function(0); -} -/* end of entropy_init_mutex() */ - -/* start of entropy_func_mutex() */ -static int entropy_func_mutex(void *data, unsigned char *output, size_t len) -{ - int ret; - /* lock 1 = entropy_func_mutex() */ - Curl_mbedtlsthreadlock_lock_function(1); - ret = mbedtls_entropy_func(data, output, len); - Curl_mbedtlsthreadlock_unlock_function(1); - - return ret; -} -/* end of entropy_func_mutex() */ - -#endif /* THREADING_SUPPORT */ - -#ifdef MBEDTLS_DEBUG -static void mbed_debug(void *context, int level, const char *f_name, - int line_nb, const char *line) -{ - struct Curl_easy *data = NULL; - - if(!context) - return; - - data = (struct Curl_easy *)context; - - infof(data, "%s", line); - (void) level; -} -#else -#endif - -/* - * profile - */ -static const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_fr = -{ - /* Hashes from SHA-1 and above */ - MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA1) | - MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_RIPEMD160) | - MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA224) | - MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA256) | - MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA384) | - MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA512), - 0xFFFFFFF, /* Any PK alg */ - 0xFFFFFFF, /* Any curve */ - 1024, /* RSA min key len */ -}; - -/* See https://tls.mbed.org/discussions/generic/ - howto-determine-exact-buffer-len-for-mbedtls_pk_write_pubkey_der -*/ -#define RSA_PUB_DER_MAX_BYTES (38 + 2 * MBEDTLS_MPI_MAX_SIZE) -#define ECP_PUB_DER_MAX_BYTES (30 + 2 * MBEDTLS_ECP_MAX_BYTES) - -#define PUB_DER_MAX_BYTES (RSA_PUB_DER_MAX_BYTES > ECP_PUB_DER_MAX_BYTES ? \ - RSA_PUB_DER_MAX_BYTES : ECP_PUB_DER_MAX_BYTES) - -static Curl_recv mbed_recv; -static Curl_send mbed_send; - -static CURLcode mbedtls_version_from_curl(int *mbedver, long version) -{ -#if MBEDTLS_VERSION_NUMBER >= 0x03000000 - switch(version) { - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - *mbedver = MBEDTLS_SSL_MINOR_VERSION_3; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_3: - break; - } -#else - switch(version) { - case CURL_SSLVERSION_TLSv1_0: - *mbedver = MBEDTLS_SSL_MINOR_VERSION_1; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_1: - *mbedver = MBEDTLS_SSL_MINOR_VERSION_2; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_2: - *mbedver = MBEDTLS_SSL_MINOR_VERSION_3; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_3: - break; - } -#endif - - return CURLE_SSL_CONNECT_ERROR; -} - -static CURLcode -set_ssl_version_min_max(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; -#if MBEDTLS_VERSION_NUMBER >= 0x03000000 - int mbedtls_ver_min = MBEDTLS_SSL_MINOR_VERSION_3; - int mbedtls_ver_max = MBEDTLS_SSL_MINOR_VERSION_3; -#else - int mbedtls_ver_min = MBEDTLS_SSL_MINOR_VERSION_1; - int mbedtls_ver_max = MBEDTLS_SSL_MINOR_VERSION_1; -#endif - long ssl_version = SSL_CONN_CONFIG(version); - long ssl_version_max = SSL_CONN_CONFIG(version_max); - CURLcode result = CURLE_OK; - - DEBUGASSERT(backend); - - switch(ssl_version) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - ssl_version = CURL_SSLVERSION_TLSv1_0; - break; - } - - switch(ssl_version_max) { - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_DEFAULT: - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; - break; - } - - result = mbedtls_version_from_curl(&mbedtls_ver_min, ssl_version); - if(result) { - failf(data, "unsupported min version passed via CURLOPT_SSLVERSION"); - return result; - } - result = mbedtls_version_from_curl(&mbedtls_ver_max, ssl_version_max >> 16); - if(result) { - failf(data, "unsupported max version passed via CURLOPT_SSLVERSION"); - return result; - } - - mbedtls_ssl_conf_min_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3, - mbedtls_ver_min); - mbedtls_ssl_conf_max_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3, - mbedtls_ver_max); - - return result; -} - -static CURLcode -mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob); - const char * const ssl_cafile = - /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ - (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile)); - const bool verifypeer = SSL_CONN_CONFIG(verifypeer); - const char * const ssl_capath = SSL_CONN_CONFIG(CApath); - char * const ssl_cert = SSL_SET_OPTION(primary.clientcert); - const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob); - const char * const ssl_crlfile = SSL_SET_OPTION(primary.CRLfile); - const char * const hostname = SSL_HOST_NAME(); -#ifndef CURL_DISABLE_VERBOSE_STRINGS - const long int port = SSL_HOST_PORT(); -#endif - int ret = -1; - char errorbuf[128]; - - DEBUGASSERT(backend); - - if((SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv2) || - (SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv3)) { - failf(data, "Not supported SSL version"); - return CURLE_NOT_BUILT_IN; - } - -#ifdef THREADING_SUPPORT - entropy_init_mutex(&ts_entropy); - mbedtls_ctr_drbg_init(&backend->ctr_drbg); - - ret = mbedtls_ctr_drbg_seed(&backend->ctr_drbg, entropy_func_mutex, - &ts_entropy, NULL, 0); - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "mbedtls_ctr_drbg_seed returned (-0x%04X) %s", - -ret, errorbuf); - return CURLE_FAILED_INIT; - } -#else - mbedtls_entropy_init(&backend->entropy); - mbedtls_ctr_drbg_init(&backend->ctr_drbg); - - ret = mbedtls_ctr_drbg_seed(&backend->ctr_drbg, mbedtls_entropy_func, - &backend->entropy, NULL, 0); - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "mbedtls_ctr_drbg_seed returned (-0x%04X) %s", - -ret, errorbuf); - return CURLE_FAILED_INIT; - } -#endif /* THREADING_SUPPORT */ - - /* Load the trusted CA */ - mbedtls_x509_crt_init(&backend->cacert); - - if(ca_info_blob && verifypeer) { - /* Unfortunately, mbedtls_x509_crt_parse() requires the data to be null - terminated even when provided the exact length, forcing us to waste - extra memory here. */ - unsigned char *newblob = malloc(ca_info_blob->len + 1); - if(!newblob) - return CURLE_OUT_OF_MEMORY; - memcpy(newblob, ca_info_blob->data, ca_info_blob->len); - newblob[ca_info_blob->len] = 0; /* null terminate */ - ret = mbedtls_x509_crt_parse(&backend->cacert, newblob, - ca_info_blob->len + 1); - free(newblob); - if(ret<0) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error importing ca cert blob - mbedTLS: (-0x%04X) %s", - -ret, errorbuf); - return CURLE_SSL_CERTPROBLEM; - } - } - - if(ssl_cafile && verifypeer) { -#ifdef MBEDTLS_FS_IO - ret = mbedtls_x509_crt_parse_file(&backend->cacert, ssl_cafile); - - if(ret<0) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading ca cert file %s - mbedTLS: (-0x%04X) %s", - ssl_cafile, -ret, errorbuf); - return CURLE_SSL_CACERT_BADFILE; - } -#else - failf(data, "mbedtls: functions that use the filesystem not built in"); - return CURLE_NOT_BUILT_IN; -#endif - } - - if(ssl_capath) { -#ifdef MBEDTLS_FS_IO - ret = mbedtls_x509_crt_parse_path(&backend->cacert, ssl_capath); - - if(ret<0) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading ca cert path %s - mbedTLS: (-0x%04X) %s", - ssl_capath, -ret, errorbuf); - - if(verifypeer) - return CURLE_SSL_CACERT_BADFILE; - } -#else - failf(data, "mbedtls: functions that use the filesystem not built in"); - return CURLE_NOT_BUILT_IN; -#endif - } - - /* Load the client certificate */ - mbedtls_x509_crt_init(&backend->clicert); - - if(ssl_cert) { -#ifdef MBEDTLS_FS_IO - ret = mbedtls_x509_crt_parse_file(&backend->clicert, ssl_cert); - - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading client cert file %s - mbedTLS: (-0x%04X) %s", - ssl_cert, -ret, errorbuf); - - return CURLE_SSL_CERTPROBLEM; - } -#else - failf(data, "mbedtls: functions that use the filesystem not built in"); - return CURLE_NOT_BUILT_IN; -#endif - } - - if(ssl_cert_blob) { - /* Unfortunately, mbedtls_x509_crt_parse() requires the data to be null - terminated even when provided the exact length, forcing us to waste - extra memory here. */ - unsigned char *newblob = malloc(ssl_cert_blob->len + 1); - if(!newblob) - return CURLE_OUT_OF_MEMORY; - memcpy(newblob, ssl_cert_blob->data, ssl_cert_blob->len); - newblob[ssl_cert_blob->len] = 0; /* null terminate */ - ret = mbedtls_x509_crt_parse(&backend->clicert, newblob, - ssl_cert_blob->len + 1); - free(newblob); - - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading private key %s - mbedTLS: (-0x%04X) %s", - SSL_SET_OPTION(key), -ret, errorbuf); - return CURLE_SSL_CERTPROBLEM; - } - } - - /* Load the client private key */ - mbedtls_pk_init(&backend->pk); - - if(SSL_SET_OPTION(key) || SSL_SET_OPTION(key_blob)) { - if(SSL_SET_OPTION(key)) { -#ifdef MBEDTLS_FS_IO -#if MBEDTLS_VERSION_NUMBER >= 0x03000000 - ret = mbedtls_pk_parse_keyfile(&backend->pk, SSL_SET_OPTION(key), - SSL_SET_OPTION(key_passwd), - mbedtls_ctr_drbg_random, - &backend->ctr_drbg); -#else - ret = mbedtls_pk_parse_keyfile(&backend->pk, SSL_SET_OPTION(key), - SSL_SET_OPTION(key_passwd)); -#endif - - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading private key %s - mbedTLS: (-0x%04X) %s", - SSL_SET_OPTION(key), -ret, errorbuf); - return CURLE_SSL_CERTPROBLEM; - } -#else - failf(data, "mbedtls: functions that use the filesystem not built in"); - return CURLE_NOT_BUILT_IN; -#endif - } - else { - const struct curl_blob *ssl_key_blob = SSL_SET_OPTION(key_blob); - const unsigned char *key_data = - (const unsigned char *)ssl_key_blob->data; - const char *passwd = SSL_SET_OPTION(key_passwd); -#if MBEDTLS_VERSION_NUMBER >= 0x03000000 - ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len, - (const unsigned char *)passwd, - passwd ? strlen(passwd) : 0, - mbedtls_ctr_drbg_random, - &backend->ctr_drbg); -#else - ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len, - (const unsigned char *)passwd, - passwd ? strlen(passwd) : 0); -#endif - - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error parsing private key - mbedTLS: (-0x%04X) %s", - -ret, errorbuf); - return CURLE_SSL_CERTPROBLEM; - } - } - - if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) || - mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY))) - ret = MBEDTLS_ERR_PK_TYPE_MISMATCH; - } - - /* Load the CRL */ -#ifdef MBEDTLS_X509_CRL_PARSE_C - mbedtls_x509_crl_init(&backend->crl); - - if(ssl_crlfile) { -#ifdef MBEDTLS_FS_IO - ret = mbedtls_x509_crl_parse_file(&backend->crl, ssl_crlfile); - - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading CRL file %s - mbedTLS: (-0x%04X) %s", - ssl_crlfile, -ret, errorbuf); - - return CURLE_SSL_CRL_BADFILE; - } -#else - failf(data, "mbedtls: functions that use the filesystem not built in"); - return CURLE_NOT_BUILT_IN; -#endif - } -#else - if(ssl_crlfile) { - failf(data, "mbedtls: crl support not built in"); - return CURLE_NOT_BUILT_IN; - } -#endif - - infof(data, "mbedTLS: Connecting to %s:%ld", hostname, port); - - mbedtls_ssl_config_init(&backend->config); - ret = mbedtls_ssl_config_defaults(&backend->config, - MBEDTLS_SSL_IS_CLIENT, - MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT); - if(ret) { - failf(data, "mbedTLS: ssl_config failed"); - return CURLE_SSL_CONNECT_ERROR; - } - - mbedtls_ssl_init(&backend->ssl); - if(mbedtls_ssl_setup(&backend->ssl, &backend->config)) { - failf(data, "mbedTLS: ssl_init failed"); - return CURLE_SSL_CONNECT_ERROR; - } - - /* new profile with RSA min key len = 1024 ... */ - mbedtls_ssl_conf_cert_profile(&backend->config, - &mbedtls_x509_crt_profile_fr); - - switch(SSL_CONN_CONFIG(version)) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: -#if MBEDTLS_VERSION_NUMBER < 0x03000000 - mbedtls_ssl_conf_min_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3, - MBEDTLS_SSL_MINOR_VERSION_1); - infof(data, "mbedTLS: Set min SSL version to TLS 1.0"); - break; -#endif - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_3: - { - CURLcode result = set_ssl_version_min_max(data, conn, sockindex); - if(result != CURLE_OK) - return result; - break; - } - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } - - mbedtls_ssl_conf_authmode(&backend->config, MBEDTLS_SSL_VERIFY_OPTIONAL); - - mbedtls_ssl_conf_rng(&backend->config, mbedtls_ctr_drbg_random, - &backend->ctr_drbg); - mbedtls_ssl_set_bio(&backend->ssl, &conn->sock[sockindex], - mbedtls_net_send, - mbedtls_net_recv, - NULL /* rev_timeout() */); - - mbedtls_ssl_conf_ciphersuites(&backend->config, - mbedtls_ssl_list_ciphersuites()); - -#if defined(MBEDTLS_SSL_RENEGOTIATION) - mbedtls_ssl_conf_renegotiation(&backend->config, - MBEDTLS_SSL_RENEGOTIATION_ENABLED); -#endif - -#if defined(MBEDTLS_SSL_SESSION_TICKETS) - mbedtls_ssl_conf_session_tickets(&backend->config, - MBEDTLS_SSL_SESSION_TICKETS_DISABLED); -#endif - - /* Check if there's a cached ID we can/should use here! */ - if(SSL_SET_OPTION(primary.sessionid)) { - void *old_session = NULL; - - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(data, conn, - SSL_IS_PROXY() ? TRUE : FALSE, - &old_session, NULL, sockindex)) { - ret = mbedtls_ssl_set_session(&backend->ssl, old_session); - if(ret) { - Curl_ssl_sessionid_unlock(data); - failf(data, "mbedtls_ssl_set_session returned -0x%x", -ret); - return CURLE_SSL_CONNECT_ERROR; - } - infof(data, "mbedTLS re-using session"); - } - Curl_ssl_sessionid_unlock(data); - } - - mbedtls_ssl_conf_ca_chain(&backend->config, - &backend->cacert, -#ifdef MBEDTLS_X509_CRL_PARSE_C - &backend->crl); -#else - NULL); -#endif - - if(SSL_SET_OPTION(key) || SSL_SET_OPTION(key_blob)) { - mbedtls_ssl_conf_own_cert(&backend->config, - &backend->clicert, &backend->pk); - } - { - char *snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost || mbedtls_ssl_set_hostname(&backend->ssl, snihost)) { - /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks and - the name to set in the SNI extension. So even if curl connects to a - host specified as an IP address, this function must be used. */ - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - } - -#ifdef HAS_ALPN - if(conn->bits.tls_enable_alpn) { - const char **p = &backend->protocols[0]; -#ifdef USE_HTTP2 - if(data->state.httpwant >= CURL_HTTP_VERSION_2) - *p++ = ALPN_H2; -#endif - *p++ = ALPN_HTTP_1_1; - *p = NULL; - /* this function doesn't clone the protocols array, which is why we need - to keep it around */ - if(mbedtls_ssl_conf_alpn_protocols(&backend->config, - &backend->protocols[0])) { - failf(data, "Failed setting ALPN protocols"); - return CURLE_SSL_CONNECT_ERROR; - } - for(p = &backend->protocols[0]; *p; ++p) - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, *p); - } -#endif - -#ifdef MBEDTLS_DEBUG - /* In order to make that work in mbedtls MBEDTLS_DEBUG_C must be defined. */ - mbedtls_ssl_conf_dbg(&backend->config, mbed_debug, data); - /* - 0 No debug - * - 1 Error - * - 2 State change - * - 3 Informational - * - 4 Verbose - */ - mbedtls_debug_set_threshold(4); -#endif - - /* give application a chance to interfere with mbedTLS set up. */ - if(data->set.ssl.fsslctx) { - ret = (*data->set.ssl.fsslctx)(data, &backend->config, - data->set.ssl.fsslctxp); - if(ret) { - failf(data, "error signaled by ssl ctx callback"); - return ret; - } - } - - connssl->connecting_state = ssl_connect_2; - - return CURLE_OK; -} - -static CURLcode -mbed_connect_step2(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - int ret; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - const mbedtls_x509_crt *peercert; - const char * const pinnedpubkey = SSL_PINNED_PUB_KEY(); - - DEBUGASSERT(backend); - - conn->recv[sockindex] = mbed_recv; - conn->send[sockindex] = mbed_send; - - ret = mbedtls_ssl_handshake(&backend->ssl); - - if(ret == MBEDTLS_ERR_SSL_WANT_READ) { - connssl->connecting_state = ssl_connect_2_reading; - return CURLE_OK; - } - else if(ret == MBEDTLS_ERR_SSL_WANT_WRITE) { - connssl->connecting_state = ssl_connect_2_writing; - return CURLE_OK; - } - else if(ret) { - char errorbuf[128]; - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "ssl_handshake returned - mbedTLS: (-0x%04X) %s", - -ret, errorbuf); - return CURLE_SSL_CONNECT_ERROR; - } - - infof(data, "mbedTLS: Handshake complete, cipher is %s", - mbedtls_ssl_get_ciphersuite(&backend->ssl)); - - ret = mbedtls_ssl_get_verify_result(&backend->ssl); - - if(!SSL_CONN_CONFIG(verifyhost)) - /* Ignore hostname errors if verifyhost is disabled */ - ret &= ~MBEDTLS_X509_BADCERT_CN_MISMATCH; - - if(ret && SSL_CONN_CONFIG(verifypeer)) { - if(ret & MBEDTLS_X509_BADCERT_EXPIRED) - failf(data, "Cert verify failed: BADCERT_EXPIRED"); - - else if(ret & MBEDTLS_X509_BADCERT_REVOKED) - failf(data, "Cert verify failed: BADCERT_REVOKED"); - - else if(ret & MBEDTLS_X509_BADCERT_CN_MISMATCH) - failf(data, "Cert verify failed: BADCERT_CN_MISMATCH"); - - else if(ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED) - failf(data, "Cert verify failed: BADCERT_NOT_TRUSTED"); - - else if(ret & MBEDTLS_X509_BADCERT_FUTURE) - failf(data, "Cert verify failed: BADCERT_FUTURE"); - - return CURLE_PEER_FAILED_VERIFICATION; - } - - peercert = mbedtls_ssl_get_peer_cert(&backend->ssl); - - if(peercert && data->set.verbose) { - const size_t bufsize = 16384; - char *buffer = malloc(bufsize); - - if(!buffer) - return CURLE_OUT_OF_MEMORY; - - if(mbedtls_x509_crt_info(buffer, bufsize, "* ", peercert) > 0) - infof(data, "Dumping cert info: %s", buffer); - else - infof(data, "Unable to dump certificate information"); - - free(buffer); - } - - if(pinnedpubkey) { - int size; - CURLcode result; - mbedtls_x509_crt *p = NULL; - unsigned char *pubkey = NULL; - -#if MBEDTLS_VERSION_NUMBER == 0x03000000 - if(!peercert || !peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p) || - !peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len)) { -#else - if(!peercert || !peercert->raw.p || !peercert->raw.len) { -#endif - failf(data, "Failed due to missing peer certificate"); - return CURLE_SSL_PINNEDPUBKEYNOTMATCH; - } - - p = calloc(1, sizeof(*p)); - - if(!p) - return CURLE_OUT_OF_MEMORY; - - pubkey = malloc(PUB_DER_MAX_BYTES); - - if(!pubkey) { - result = CURLE_OUT_OF_MEMORY; - goto pinnedpubkey_error; - } - - mbedtls_x509_crt_init(p); - - /* Make a copy of our const peercert because mbedtls_pk_write_pubkey_der - needs a non-const key, for now. - https://github.com/ARMmbed/mbedtls/issues/396 */ -#if MBEDTLS_VERSION_NUMBER == 0x03000000 - if(mbedtls_x509_crt_parse_der(p, - peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p), - peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len))) { -#else - if(mbedtls_x509_crt_parse_der(p, peercert->raw.p, peercert->raw.len)) { -#endif - failf(data, "Failed copying peer certificate"); - result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - goto pinnedpubkey_error; - } - -#if MBEDTLS_VERSION_NUMBER == 0x03000000 - size = mbedtls_pk_write_pubkey_der(&p->MBEDTLS_PRIVATE(pk), pubkey, - PUB_DER_MAX_BYTES); -#else - size = mbedtls_pk_write_pubkey_der(&p->pk, pubkey, PUB_DER_MAX_BYTES); -#endif - - if(size <= 0) { - failf(data, "Failed copying public key from peer certificate"); - result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - goto pinnedpubkey_error; - } - - /* mbedtls_pk_write_pubkey_der writes data at the end of the buffer. */ - result = Curl_pin_peer_pubkey(data, - pinnedpubkey, - &pubkey[PUB_DER_MAX_BYTES - size], size); - pinnedpubkey_error: - mbedtls_x509_crt_free(p); - free(p); - free(pubkey); - if(result) { - return result; - } - } - -#ifdef HAS_ALPN - if(conn->bits.tls_enable_alpn) { - const char *next_protocol = mbedtls_ssl_get_alpn_protocol(&backend->ssl); - - if(next_protocol) { - infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, next_protocol); -#ifdef USE_HTTP2 - if(!strncmp(next_protocol, ALPN_H2, ALPN_H2_LENGTH) && - !next_protocol[ALPN_H2_LENGTH]) { - conn->alpn = CURL_HTTP_VERSION_2; - } - else -#endif - if(!strncmp(next_protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH) && - !next_protocol[ALPN_HTTP_1_1_LENGTH]) { - conn->alpn = CURL_HTTP_VERSION_1_1; - } - } - else { - infof(data, VTLS_INFOF_NO_ALPN); - } - Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); - } -#endif - - connssl->connecting_state = ssl_connect_3; - infof(data, "SSL connected"); - - return CURLE_OK; -} - -static CURLcode -mbed_connect_step3(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - CURLcode retcode = CURLE_OK; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); - DEBUGASSERT(backend); - - if(SSL_SET_OPTION(primary.sessionid)) { - int ret; - mbedtls_ssl_session *our_ssl_sessionid; - void *old_ssl_sessionid = NULL; - bool isproxy = SSL_IS_PROXY() ? TRUE : FALSE; - bool added = FALSE; - - our_ssl_sessionid = malloc(sizeof(mbedtls_ssl_session)); - if(!our_ssl_sessionid) - return CURLE_OUT_OF_MEMORY; - - mbedtls_ssl_session_init(our_ssl_sessionid); - - ret = mbedtls_ssl_get_session(&backend->ssl, our_ssl_sessionid); - if(ret) { - if(ret != MBEDTLS_ERR_SSL_ALLOC_FAILED) - mbedtls_ssl_session_free(our_ssl_sessionid); - free(our_ssl_sessionid); - failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret); - return CURLE_SSL_CONNECT_ERROR; - } - - /* If there's already a matching session in the cache, delete it */ - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(data, conn, isproxy, &old_ssl_sessionid, NULL, - sockindex)) - Curl_ssl_delsessionid(data, old_ssl_sessionid); - - retcode = Curl_ssl_addsessionid(data, conn, isproxy, our_ssl_sessionid, - 0, sockindex, &added); - Curl_ssl_sessionid_unlock(data); - if(!added) { - mbedtls_ssl_session_free(our_ssl_sessionid); - free(our_ssl_sessionid); - } - if(retcode) { - failf(data, "failed to store ssl session"); - return retcode; - } - } - - connssl->connecting_state = ssl_connect_done; - - return CURLE_OK; -} - -static ssize_t mbed_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - int ret = -1; - - DEBUGASSERT(backend); - - ret = mbedtls_ssl_write(&backend->ssl, (unsigned char *)mem, len); - - if(ret < 0) { - *curlcode = (ret == MBEDTLS_ERR_SSL_WANT_WRITE) ? - CURLE_AGAIN : CURLE_SEND_ERROR; - ret = -1; - } - - return ret; -} - -static void mbedtls_close_all(struct Curl_easy *data) -{ - (void)data; -} - -static void mbedtls_close(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - char buf[32]; - (void) data; - - DEBUGASSERT(backend); - - /* Maybe the server has already sent a close notify alert. - Read it to avoid an RST on the TCP connection. */ - (void)mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf, sizeof(buf)); - - mbedtls_pk_free(&backend->pk); - mbedtls_x509_crt_free(&backend->clicert); - mbedtls_x509_crt_free(&backend->cacert); -#ifdef MBEDTLS_X509_CRL_PARSE_C - mbedtls_x509_crl_free(&backend->crl); -#endif - mbedtls_ssl_config_free(&backend->config); - mbedtls_ssl_free(&backend->ssl); - mbedtls_ctr_drbg_free(&backend->ctr_drbg); -#ifndef THREADING_SUPPORT - mbedtls_entropy_free(&backend->entropy); -#endif /* THREADING_SUPPORT */ -} - -static ssize_t mbed_recv(struct Curl_easy *data, int num, - char *buf, size_t buffersize, - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[num]; - struct ssl_backend_data *backend = connssl->backend; - int ret = -1; - ssize_t len = -1; - - DEBUGASSERT(backend); - - ret = mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf, - buffersize); - - if(ret <= 0) { - if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) - return 0; - - *curlcode = (ret == MBEDTLS_ERR_SSL_WANT_READ) ? - CURLE_AGAIN : CURLE_RECV_ERROR; - return -1; - } - - len = ret; - - return len; -} - -static void mbedtls_session_free(void *ptr) -{ - mbedtls_ssl_session_free(ptr); - free(ptr); -} - -static size_t mbedtls_version(char *buffer, size_t size) -{ -#ifdef MBEDTLS_VERSION_C - /* if mbedtls_version_get_number() is available it is better */ - unsigned int version = mbedtls_version_get_number(); - return msnprintf(buffer, size, "mbedTLS/%u.%u.%u", version>>24, - (version>>16)&0xff, (version>>8)&0xff); -#else - return msnprintf(buffer, size, "mbedTLS/%s", MBEDTLS_VERSION_STRING); -#endif -} - -static CURLcode mbedtls_random(struct Curl_easy *data, - unsigned char *entropy, size_t length) -{ -#if defined(MBEDTLS_CTR_DRBG_C) - int ret = -1; - char errorbuf[128]; - mbedtls_entropy_context ctr_entropy; - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_entropy_init(&ctr_entropy); - mbedtls_ctr_drbg_init(&ctr_drbg); - - ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, - &ctr_entropy, NULL, 0); - - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "mbedtls_ctr_drbg_seed returned (-0x%04X) %s", - -ret, errorbuf); - } - else { - ret = mbedtls_ctr_drbg_random(&ctr_drbg, entropy, length); - - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "mbedtls_ctr_drbg_random returned (-0x%04X) %s", - -ret, errorbuf); - } - } - - mbedtls_ctr_drbg_free(&ctr_drbg); - mbedtls_entropy_free(&ctr_entropy); - - return ret == 0 ? CURLE_OK : CURLE_FAILED_INIT; -#elif defined(MBEDTLS_HAVEGE_C) - mbedtls_havege_state hs; - mbedtls_havege_init(&hs); - mbedtls_havege_random(&hs, entropy, length); - mbedtls_havege_free(&hs); - return CURLE_OK; -#else - return CURLE_NOT_BUILT_IN; -#endif -} - -static CURLcode -mbed_connect_common(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool nonblocking, - bool *done) -{ - CURLcode retcode; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - curl_socket_t sockfd = conn->sock[sockindex]; - timediff_t timeout_ms; - int what; - - /* check if the connection has already been established */ - if(ssl_connection_complete == connssl->state) { - *done = TRUE; - return CURLE_OK; - } - - if(ssl_connect_1 == connssl->connecting_state) { - /* Find out how much more time we're allowed */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - retcode = mbed_connect_step1(data, conn, sockindex); - if(retcode) - return retcode; - } - - while(ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state) { - - /* check allowed time left */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - /* if ssl is expecting something, check if it's available. */ - if(connssl->connecting_state == ssl_connect_2_reading - || connssl->connecting_state == ssl_connect_2_writing) { - - curl_socket_t writefd = ssl_connect_2_writing == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - curl_socket_t readfd = ssl_connect_2_reading == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - - what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, - nonblocking ? 0 : timeout_ms); - if(what < 0) { - /* fatal error */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - return CURLE_SSL_CONNECT_ERROR; - } - else if(0 == what) { - if(nonblocking) { - *done = FALSE; - return CURLE_OK; - } - else { - /* timeout */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - } - /* socket is readable or writable */ - } - - /* Run transaction, and return to the caller if it failed or if - * this connection is part of a multi handle and this loop would - * execute again. This permits the owner of a multi handle to - * abort a connection attempt before step2 has completed while - * ensuring that a client using select() or epoll() will always - * have a valid fdset to wait on. - */ - retcode = mbed_connect_step2(data, conn, sockindex); - if(retcode || (nonblocking && - (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) - return retcode; - - } /* repeat step2 until all transactions are done. */ - - if(ssl_connect_3 == connssl->connecting_state) { - retcode = mbed_connect_step3(data, conn, sockindex); - if(retcode) - return retcode; - } - - if(ssl_connect_done == connssl->connecting_state) { - connssl->state = ssl_connection_complete; - conn->recv[sockindex] = mbed_recv; - conn->send[sockindex] = mbed_send; - *done = TRUE; - } - else - *done = FALSE; - - /* Reset our connect state machine */ - connssl->connecting_state = ssl_connect_1; - - return CURLE_OK; -} - -static CURLcode mbedtls_connect_nonblocking(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, bool *done) -{ - return mbed_connect_common(data, conn, sockindex, TRUE, done); -} - - -static CURLcode mbedtls_connect(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - CURLcode retcode; - bool done = FALSE; - - retcode = mbed_connect_common(data, conn, sockindex, FALSE, &done); - if(retcode) - return retcode; - - DEBUGASSERT(done); - - return CURLE_OK; -} - -/* - * return 0 error initializing SSL - * return 1 SSL initialized successfully - */ -static int mbedtls_init(void) -{ - return Curl_mbedtlsthreadlock_thread_setup(); -} - -static void mbedtls_cleanup(void) -{ - (void)Curl_mbedtlsthreadlock_thread_cleanup(); -} - -static bool mbedtls_data_pending(const struct connectdata *conn, - int sockindex) -{ - const struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - DEBUGASSERT(backend); - return mbedtls_ssl_get_bytes_avail(&backend->ssl) != 0; -} - -static CURLcode mbedtls_sha256sum(const unsigned char *input, - size_t inputlen, - unsigned char *sha256sum, - size_t sha256len UNUSED_PARAM) -{ - /* TODO: explain this for different mbedtls 2.x vs 3 version */ - (void)sha256len; -#if MBEDTLS_VERSION_NUMBER < 0x02070000 - mbedtls_sha256(input, inputlen, sha256sum, 0); -#else - /* returns 0 on success, otherwise failure */ -#if MBEDTLS_VERSION_NUMBER >= 0x03000000 - if(mbedtls_sha256(input, inputlen, sha256sum, 0) != 0) -#else - if(mbedtls_sha256_ret(input, inputlen, sha256sum, 0) != 0) -#endif - return CURLE_BAD_FUNCTION_ARGUMENT; -#endif - return CURLE_OK; -} - -static void *mbedtls_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - struct ssl_backend_data *backend = connssl->backend; - (void)info; - DEBUGASSERT(backend); - return &backend->ssl; -} - -const struct Curl_ssl Curl_ssl_mbedtls = { - { CURLSSLBACKEND_MBEDTLS, "mbedtls" }, /* info */ - - SSLSUPP_CA_PATH | - SSLSUPP_CAINFO_BLOB | - SSLSUPP_PINNEDPUBKEY | - SSLSUPP_SSL_CTX, - - sizeof(struct ssl_backend_data), - - mbedtls_init, /* init */ - mbedtls_cleanup, /* cleanup */ - mbedtls_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ - Curl_none_shutdown, /* shutdown */ - mbedtls_data_pending, /* data_pending */ - mbedtls_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ - mbedtls_connect, /* connect */ - mbedtls_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_getsock, /* getsock */ - mbedtls_get_internals, /* get_internals */ - mbedtls_close, /* close_one */ - mbedtls_close_all, /* close_all */ - mbedtls_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ - mbedtls_sha256sum, /* sha256sum */ - NULL, /* associate_connection */ - NULL /* disassociate_connection */ -}; - -#endif /* USE_MBEDTLS */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2010 - 2011, Hoi-Ho Chan, <hoiho.chan@gmail.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
+ *
+ ***************************************************************************/
+
+/*
+ * Source file for all mbedTLS-specific code for the TLS/SSL layer. No code
+ * but vtls.c should ever call or use these functions.
+ *
+ */
+
+#include "curl_setup.h"
+
+#ifdef USE_MBEDTLS
+
+/* Define this to enable lots of debugging for mbedTLS */
+/* #define MBEDTLS_DEBUG */
+
+#include <mbedtls/version.h>
+#if MBEDTLS_VERSION_NUMBER >= 0x02040000
+#include <mbedtls/net_sockets.h>
+#else
+#include <mbedtls/net.h>
+#endif
+#include <mbedtls/ssl.h>
+#include <mbedtls/x509.h>
+
+#include <mbedtls/error.h>
+#include <mbedtls/entropy.h>
+#include <mbedtls/ctr_drbg.h>
+#include <mbedtls/sha256.h>
+
+#if MBEDTLS_VERSION_MAJOR >= 2
+# ifdef MBEDTLS_DEBUG
+# include <mbedtls/debug.h>
+# endif
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "inet_pton.h"
+#include "mbedtls.h"
+#include "vtls.h"
+#include "vtls_int.h"
+#include "parsedate.h"
+#include "connect.h" /* for the connect timeout */
+#include "select.h"
+#include "multiif.h"
+#include "mbedtls_threadlock.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* ALPN for http2 */
+#ifdef USE_HTTP2
+# undef HAS_ALPN
+# ifdef MBEDTLS_SSL_ALPN
+# define HAS_ALPN
+# endif
+#endif
+
+struct ssl_backend_data {
+ mbedtls_ctr_drbg_context ctr_drbg;
+ mbedtls_entropy_context entropy;
+ mbedtls_ssl_context ssl;
+ mbedtls_x509_crt cacert;
+ mbedtls_x509_crt clicert;
+#ifdef MBEDTLS_X509_CRL_PARSE_C
+ mbedtls_x509_crl crl;
+#endif
+ mbedtls_pk_context pk;
+ mbedtls_ssl_config config;
+#ifdef HAS_ALPN
+ const char *protocols[3];
+#endif
+};
+
+/* apply threading? */
+#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
+#define THREADING_SUPPORT
+#endif
+
+#ifndef MBEDTLS_ERROR_C
+#define mbedtls_strerror(a,b,c) b[0] = 0
+#endif
+
+#if defined(THREADING_SUPPORT)
+static mbedtls_entropy_context ts_entropy;
+
+static int entropy_init_initialized = 0;
+
+/* start of entropy_init_mutex() */
+static void entropy_init_mutex(mbedtls_entropy_context *ctx)
+{
+ /* lock 0 = entropy_init_mutex() */
+ Curl_mbedtlsthreadlock_lock_function(0);
+ if(entropy_init_initialized == 0) {
+ mbedtls_entropy_init(ctx);
+ entropy_init_initialized = 1;
+ }
+ Curl_mbedtlsthreadlock_unlock_function(0);
+}
+/* end of entropy_init_mutex() */
+
+/* start of entropy_func_mutex() */
+static int entropy_func_mutex(void *data, unsigned char *output, size_t len)
+{
+ int ret;
+ /* lock 1 = entropy_func_mutex() */
+ Curl_mbedtlsthreadlock_lock_function(1);
+ ret = mbedtls_entropy_func(data, output, len);
+ Curl_mbedtlsthreadlock_unlock_function(1);
+
+ return ret;
+}
+/* end of entropy_func_mutex() */
+
+#endif /* THREADING_SUPPORT */
+
+#ifdef MBEDTLS_DEBUG
+static void mbed_debug(void *context, int level, const char *f_name,
+ int line_nb, const char *line)
+{
+ struct Curl_easy *data = NULL;
+
+ if(!context)
+ return;
+
+ data = (struct Curl_easy *)context;
+
+ infof(data, "%s", line);
+ (void) level;
+}
+#else
+#endif
+
+static int bio_cf_write(void *bio, const unsigned char *buf, size_t blen)
+{
+ struct Curl_cfilter *cf = bio;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct Curl_easy *data = connssl->call_data;
+ ssize_t nwritten;
+ CURLcode result;
+
+ DEBUGASSERT(data);
+ nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, &result);
+ /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_out_write(len=%d) -> %d, err=%d"),
+ blen, (int)nwritten, result)); */
+ if(nwritten < 0 && CURLE_AGAIN == result) {
+ nwritten = MBEDTLS_ERR_SSL_WANT_WRITE;
+ }
+ return (int)nwritten;
+}
+
+static int bio_cf_read(void *bio, unsigned char *buf, size_t blen)
+{
+ struct Curl_cfilter *cf = bio;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct Curl_easy *data = connssl->call_data;
+ ssize_t nread;
+ CURLcode result;
+
+ DEBUGASSERT(data);
+ /* OpenSSL catches this case, so should we. */
+ if(!buf)
+ return 0;
+
+ nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, blen, &result);
+ /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_in_read(len=%d) -> %d, err=%d"),
+ blen, (int)nread, result)); */
+ if(nread < 0 && CURLE_AGAIN == result) {
+ nread = MBEDTLS_ERR_SSL_WANT_READ;
+ }
+ return (int)nread;
+}
+
+/*
+ * profile
+ */
+static const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_fr =
+{
+ /* Hashes from SHA-1 and above */
+ MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA1) |
+ MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_RIPEMD160) |
+ MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA224) |
+ MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA256) |
+ MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA384) |
+ MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA512),
+ 0xFFFFFFF, /* Any PK alg */
+ 0xFFFFFFF, /* Any curve */
+ 1024, /* RSA min key len */
+};
+
+/* See https://tls.mbed.org/discussions/generic/
+ howto-determine-exact-buffer-len-for-mbedtls_pk_write_pubkey_der
+*/
+#define RSA_PUB_DER_MAX_BYTES (38 + 2 * MBEDTLS_MPI_MAX_SIZE)
+#define ECP_PUB_DER_MAX_BYTES (30 + 2 * MBEDTLS_ECP_MAX_BYTES)
+
+#define PUB_DER_MAX_BYTES (RSA_PUB_DER_MAX_BYTES > ECP_PUB_DER_MAX_BYTES ? \
+ RSA_PUB_DER_MAX_BYTES : ECP_PUB_DER_MAX_BYTES)
+
+static CURLcode mbedtls_version_from_curl(int *mbedver, long version)
+{
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+ switch(version) {
+ case CURL_SSLVERSION_TLSv1_0:
+ case CURL_SSLVERSION_TLSv1_1:
+ case CURL_SSLVERSION_TLSv1_2:
+ *mbedver = MBEDTLS_SSL_MINOR_VERSION_3;
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_3:
+ break;
+ }
+#else
+ switch(version) {
+ case CURL_SSLVERSION_TLSv1_0:
+ *mbedver = MBEDTLS_SSL_MINOR_VERSION_1;
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_1:
+ *mbedver = MBEDTLS_SSL_MINOR_VERSION_2;
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_2:
+ *mbedver = MBEDTLS_SSL_MINOR_VERSION_3;
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_3:
+ break;
+ }
+#endif
+
+ return CURLE_SSL_CONNECT_ERROR;
+}
+
+static CURLcode
+set_ssl_version_min_max(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+ int mbedtls_ver_min = MBEDTLS_SSL_MINOR_VERSION_3;
+ int mbedtls_ver_max = MBEDTLS_SSL_MINOR_VERSION_3;
+#else
+ int mbedtls_ver_min = MBEDTLS_SSL_MINOR_VERSION_1;
+ int mbedtls_ver_max = MBEDTLS_SSL_MINOR_VERSION_1;
+#endif
+ long ssl_version = conn_config->version;
+ long ssl_version_max = conn_config->version_max;
+ CURLcode result = CURLE_OK;
+
+ DEBUGASSERT(backend);
+
+ switch(ssl_version) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+ ssl_version = CURL_SSLVERSION_TLSv1_0;
+ break;
+ }
+
+ switch(ssl_version_max) {
+ case CURL_SSLVERSION_MAX_NONE:
+ case CURL_SSLVERSION_MAX_DEFAULT:
+ ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2;
+ break;
+ }
+
+ result = mbedtls_version_from_curl(&mbedtls_ver_min, ssl_version);
+ if(result) {
+ failf(data, "unsupported min version passed via CURLOPT_SSLVERSION");
+ return result;
+ }
+ result = mbedtls_version_from_curl(&mbedtls_ver_max, ssl_version_max >> 16);
+ if(result) {
+ failf(data, "unsupported max version passed via CURLOPT_SSLVERSION");
+ return result;
+ }
+
+ mbedtls_ssl_conf_min_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3,
+ mbedtls_ver_min);
+ mbedtls_ssl_conf_max_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3,
+ mbedtls_ver_max);
+
+ return result;
+}
+
+static CURLcode
+mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ const char * const ssl_cafile =
+ /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
+ (ca_info_blob ? NULL : conn_config->CAfile);
+ const bool verifypeer = conn_config->verifypeer;
+ const char * const ssl_capath = conn_config->CApath;
+ char * const ssl_cert = ssl_config->primary.clientcert;
+ const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
+ const char * const ssl_crlfile = ssl_config->primary.CRLfile;
+ const char *hostname = connssl->hostname;
+ int ret = -1;
+ char errorbuf[128];
+
+ DEBUGASSERT(backend);
+
+ if((conn_config->version == CURL_SSLVERSION_SSLv2) ||
+ (conn_config->version == CURL_SSLVERSION_SSLv3)) {
+ failf(data, "Not supported SSL version");
+ return CURLE_NOT_BUILT_IN;
+ }
+
+#ifdef THREADING_SUPPORT
+ entropy_init_mutex(&ts_entropy);
+ mbedtls_ctr_drbg_init(&backend->ctr_drbg);
+
+ ret = mbedtls_ctr_drbg_seed(&backend->ctr_drbg, entropy_func_mutex,
+ &ts_entropy, NULL, 0);
+ if(ret) {
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+ failf(data, "mbedtls_ctr_drbg_seed returned (-0x%04X) %s",
+ -ret, errorbuf);
+ return CURLE_FAILED_INIT;
+ }
+#else
+ mbedtls_entropy_init(&backend->entropy);
+ mbedtls_ctr_drbg_init(&backend->ctr_drbg);
+
+ ret = mbedtls_ctr_drbg_seed(&backend->ctr_drbg, mbedtls_entropy_func,
+ &backend->entropy, NULL, 0);
+ if(ret) {
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+ failf(data, "mbedtls_ctr_drbg_seed returned (-0x%04X) %s",
+ -ret, errorbuf);
+ return CURLE_FAILED_INIT;
+ }
+#endif /* THREADING_SUPPORT */
+
+ /* Load the trusted CA */
+ mbedtls_x509_crt_init(&backend->cacert);
+
+ if(ca_info_blob && verifypeer) {
+ /* Unfortunately, mbedtls_x509_crt_parse() requires the data to be null
+ terminated even when provided the exact length, forcing us to waste
+ extra memory here. */
+ unsigned char *newblob = malloc(ca_info_blob->len + 1);
+ if(!newblob)
+ return CURLE_OUT_OF_MEMORY;
+ memcpy(newblob, ca_info_blob->data, ca_info_blob->len);
+ newblob[ca_info_blob->len] = 0; /* null terminate */
+ ret = mbedtls_x509_crt_parse(&backend->cacert, newblob,
+ ca_info_blob->len + 1);
+ free(newblob);
+ if(ret<0) {
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+ failf(data, "Error importing ca cert blob - mbedTLS: (-0x%04X) %s",
+ -ret, errorbuf);
+ return CURLE_SSL_CERTPROBLEM;
+ }
+ }
+
+ if(ssl_cafile && verifypeer) {
+#ifdef MBEDTLS_FS_IO
+ ret = mbedtls_x509_crt_parse_file(&backend->cacert, ssl_cafile);
+
+ if(ret<0) {
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+ failf(data, "Error reading ca cert file %s - mbedTLS: (-0x%04X) %s",
+ ssl_cafile, -ret, errorbuf);
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+#else
+ failf(data, "mbedtls: functions that use the filesystem not built in");
+ return CURLE_NOT_BUILT_IN;
+#endif
+ }
+
+ if(ssl_capath) {
+#ifdef MBEDTLS_FS_IO
+ ret = mbedtls_x509_crt_parse_path(&backend->cacert, ssl_capath);
+
+ if(ret<0) {
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+ failf(data, "Error reading ca cert path %s - mbedTLS: (-0x%04X) %s",
+ ssl_capath, -ret, errorbuf);
+
+ if(verifypeer)
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+#else
+ failf(data, "mbedtls: functions that use the filesystem not built in");
+ return CURLE_NOT_BUILT_IN;
+#endif
+ }
+
+ /* Load the client certificate */
+ mbedtls_x509_crt_init(&backend->clicert);
+
+ if(ssl_cert) {
+#ifdef MBEDTLS_FS_IO
+ ret = mbedtls_x509_crt_parse_file(&backend->clicert, ssl_cert);
+
+ if(ret) {
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+ failf(data, "Error reading client cert file %s - mbedTLS: (-0x%04X) %s",
+ ssl_cert, -ret, errorbuf);
+
+ return CURLE_SSL_CERTPROBLEM;
+ }
+#else
+ failf(data, "mbedtls: functions that use the filesystem not built in");
+ return CURLE_NOT_BUILT_IN;
+#endif
+ }
+
+ if(ssl_cert_blob) {
+ /* Unfortunately, mbedtls_x509_crt_parse() requires the data to be null
+ terminated even when provided the exact length, forcing us to waste
+ extra memory here. */
+ unsigned char *newblob = malloc(ssl_cert_blob->len + 1);
+ if(!newblob)
+ return CURLE_OUT_OF_MEMORY;
+ memcpy(newblob, ssl_cert_blob->data, ssl_cert_blob->len);
+ newblob[ssl_cert_blob->len] = 0; /* null terminate */
+ ret = mbedtls_x509_crt_parse(&backend->clicert, newblob,
+ ssl_cert_blob->len + 1);
+ free(newblob);
+
+ if(ret) {
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+ failf(data, "Error reading private key %s - mbedTLS: (-0x%04X) %s",
+ ssl_config->key, -ret, errorbuf);
+ return CURLE_SSL_CERTPROBLEM;
+ }
+ }
+
+ /* Load the client private key */
+ mbedtls_pk_init(&backend->pk);
+
+ if(ssl_config->key || ssl_config->key_blob) {
+ if(ssl_config->key) {
+#ifdef MBEDTLS_FS_IO
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+ ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key,
+ ssl_config->key_passwd,
+ mbedtls_ctr_drbg_random,
+ &backend->ctr_drbg);
+#else
+ ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key,
+ ssl_config->key_passwd);
+#endif
+
+ if(ret) {
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+ failf(data, "Error reading private key %s - mbedTLS: (-0x%04X) %s",
+ ssl_config->key, -ret, errorbuf);
+ return CURLE_SSL_CERTPROBLEM;
+ }
+#else
+ failf(data, "mbedtls: functions that use the filesystem not built in");
+ return CURLE_NOT_BUILT_IN;
+#endif
+ }
+ else {
+ const struct curl_blob *ssl_key_blob = ssl_config->key_blob;
+ const unsigned char *key_data =
+ (const unsigned char *)ssl_key_blob->data;
+ const char *passwd = ssl_config->key_passwd;
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+ ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len,
+ (const unsigned char *)passwd,
+ passwd ? strlen(passwd) : 0,
+ mbedtls_ctr_drbg_random,
+ &backend->ctr_drbg);
+#else
+ ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len,
+ (const unsigned char *)passwd,
+ passwd ? strlen(passwd) : 0);
+#endif
+
+ if(ret) {
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+ failf(data, "Error parsing private key - mbedTLS: (-0x%04X) %s",
+ -ret, errorbuf);
+ return CURLE_SSL_CERTPROBLEM;
+ }
+ }
+
+ if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) ||
+ mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY)))
+ ret = MBEDTLS_ERR_PK_TYPE_MISMATCH;
+ }
+
+ /* Load the CRL */
+#ifdef MBEDTLS_X509_CRL_PARSE_C
+ mbedtls_x509_crl_init(&backend->crl);
+
+ if(ssl_crlfile) {
+#ifdef MBEDTLS_FS_IO
+ ret = mbedtls_x509_crl_parse_file(&backend->crl, ssl_crlfile);
+
+ if(ret) {
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+ failf(data, "Error reading CRL file %s - mbedTLS: (-0x%04X) %s",
+ ssl_crlfile, -ret, errorbuf);
+
+ return CURLE_SSL_CRL_BADFILE;
+ }
+#else
+ failf(data, "mbedtls: functions that use the filesystem not built in");
+ return CURLE_NOT_BUILT_IN;
+#endif
+ }
+#else
+ if(ssl_crlfile) {
+ failf(data, "mbedtls: crl support not built in");
+ return CURLE_NOT_BUILT_IN;
+ }
+#endif
+
+ infof(data, "mbedTLS: Connecting to %s:%d", hostname, connssl->port);
+
+ mbedtls_ssl_config_init(&backend->config);
+ ret = mbedtls_ssl_config_defaults(&backend->config,
+ MBEDTLS_SSL_IS_CLIENT,
+ MBEDTLS_SSL_TRANSPORT_STREAM,
+ MBEDTLS_SSL_PRESET_DEFAULT);
+ if(ret) {
+ failf(data, "mbedTLS: ssl_config failed");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ mbedtls_ssl_init(&backend->ssl);
+ if(mbedtls_ssl_setup(&backend->ssl, &backend->config)) {
+ failf(data, "mbedTLS: ssl_init failed");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ /* new profile with RSA min key len = 1024 ... */
+ mbedtls_ssl_conf_cert_profile(&backend->config,
+ &mbedtls_x509_crt_profile_fr);
+
+ switch(conn_config->version) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+#if MBEDTLS_VERSION_NUMBER < 0x03000000
+ mbedtls_ssl_conf_min_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3,
+ MBEDTLS_SSL_MINOR_VERSION_1);
+ infof(data, "mbedTLS: Set min SSL version to TLS 1.0");
+ break;
+#endif
+ case CURL_SSLVERSION_TLSv1_0:
+ case CURL_SSLVERSION_TLSv1_1:
+ case CURL_SSLVERSION_TLSv1_2:
+ case CURL_SSLVERSION_TLSv1_3:
+ {
+ CURLcode result = set_ssl_version_min_max(cf, data);
+ if(result != CURLE_OK)
+ return result;
+ break;
+ }
+ default:
+ failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ mbedtls_ssl_conf_authmode(&backend->config, MBEDTLS_SSL_VERIFY_OPTIONAL);
+
+ mbedtls_ssl_conf_rng(&backend->config, mbedtls_ctr_drbg_random,
+ &backend->ctr_drbg);
+ mbedtls_ssl_set_bio(&backend->ssl, cf, bio_cf_write, bio_cf_read,
+ NULL /* rev_timeout() */);
+
+ mbedtls_ssl_conf_ciphersuites(&backend->config,
+ mbedtls_ssl_list_ciphersuites());
+
+#if defined(MBEDTLS_SSL_RENEGOTIATION)
+ mbedtls_ssl_conf_renegotiation(&backend->config,
+ MBEDTLS_SSL_RENEGOTIATION_ENABLED);
+#endif
+
+#if defined(MBEDTLS_SSL_SESSION_TICKETS)
+ mbedtls_ssl_conf_session_tickets(&backend->config,
+ MBEDTLS_SSL_SESSION_TICKETS_DISABLED);
+#endif
+
+ /* Check if there's a cached ID we can/should use here! */
+ if(ssl_config->primary.sessionid) {
+ void *old_session = NULL;
+
+ Curl_ssl_sessionid_lock(data);
+ if(!Curl_ssl_getsessionid(cf, data, &old_session, NULL)) {
+ ret = mbedtls_ssl_set_session(&backend->ssl, old_session);
+ if(ret) {
+ Curl_ssl_sessionid_unlock(data);
+ failf(data, "mbedtls_ssl_set_session returned -0x%x", -ret);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ infof(data, "mbedTLS re-using session");
+ }
+ Curl_ssl_sessionid_unlock(data);
+ }
+
+ mbedtls_ssl_conf_ca_chain(&backend->config,
+ &backend->cacert,
+#ifdef MBEDTLS_X509_CRL_PARSE_C
+ &backend->crl);
+#else
+ NULL);
+#endif
+
+ if(ssl_config->key || ssl_config->key_blob) {
+ mbedtls_ssl_conf_own_cert(&backend->config,
+ &backend->clicert, &backend->pk);
+ }
+ {
+ char *snihost = Curl_ssl_snihost(data, hostname, NULL);
+ if(!snihost || mbedtls_ssl_set_hostname(&backend->ssl, snihost)) {
+ /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks and
+ the name to set in the SNI extension. So even if curl connects to a
+ host specified as an IP address, this function must be used. */
+ failf(data, "Failed to set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+#ifdef HAS_ALPN
+ if(cf->conn->bits.tls_enable_alpn) {
+ const char **p = &backend->protocols[0];
+#ifdef USE_HTTP2
+ if(data->state.httpwant >= CURL_HTTP_VERSION_2)
+ *p++ = ALPN_H2;
+#endif
+ *p++ = ALPN_HTTP_1_1;
+ *p = NULL;
+ /* this function doesn't clone the protocols array, which is why we need
+ to keep it around */
+ if(mbedtls_ssl_conf_alpn_protocols(&backend->config,
+ &backend->protocols[0])) {
+ failf(data, "Failed setting ALPN protocols");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ for(p = &backend->protocols[0]; *p; ++p)
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, *p);
+ }
+#endif
+
+#ifdef MBEDTLS_DEBUG
+ /* In order to make that work in mbedtls MBEDTLS_DEBUG_C must be defined. */
+ mbedtls_ssl_conf_dbg(&backend->config, mbed_debug, data);
+ /* - 0 No debug
+ * - 1 Error
+ * - 2 State change
+ * - 3 Informational
+ * - 4 Verbose
+ */
+ mbedtls_debug_set_threshold(4);
+#endif
+
+ /* give application a chance to interfere with mbedTLS set up. */
+ if(data->set.ssl.fsslctx) {
+ ret = (*data->set.ssl.fsslctx)(data, &backend->config,
+ data->set.ssl.fsslctxp);
+ if(ret) {
+ failf(data, "error signaled by ssl ctx callback");
+ return ret;
+ }
+ }
+
+ connssl->connecting_state = ssl_connect_2;
+
+ return CURLE_OK;
+}
+
+static CURLcode
+mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ int ret;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ const mbedtls_x509_crt *peercert;
+ const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf)?
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY];
+
+ DEBUGASSERT(backend);
+
+ ret = mbedtls_ssl_handshake(&backend->ssl);
+
+ if(ret == MBEDTLS_ERR_SSL_WANT_READ) {
+ connssl->connecting_state = ssl_connect_2_reading;
+ return CURLE_OK;
+ }
+ else if(ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
+ connssl->connecting_state = ssl_connect_2_writing;
+ return CURLE_OK;
+ }
+ else if(ret) {
+ char errorbuf[128];
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+ failf(data, "ssl_handshake returned - mbedTLS: (-0x%04X) %s",
+ -ret, errorbuf);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ infof(data, "mbedTLS: Handshake complete, cipher is %s",
+ mbedtls_ssl_get_ciphersuite(&backend->ssl));
+
+ ret = mbedtls_ssl_get_verify_result(&backend->ssl);
+
+ if(!conn_config->verifyhost)
+ /* Ignore hostname errors if verifyhost is disabled */
+ ret &= ~MBEDTLS_X509_BADCERT_CN_MISMATCH;
+
+ if(ret && conn_config->verifypeer) {
+ if(ret & MBEDTLS_X509_BADCERT_EXPIRED)
+ failf(data, "Cert verify failed: BADCERT_EXPIRED");
+
+ else if(ret & MBEDTLS_X509_BADCERT_REVOKED)
+ failf(data, "Cert verify failed: BADCERT_REVOKED");
+
+ else if(ret & MBEDTLS_X509_BADCERT_CN_MISMATCH)
+ failf(data, "Cert verify failed: BADCERT_CN_MISMATCH");
+
+ else if(ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED)
+ failf(data, "Cert verify failed: BADCERT_NOT_TRUSTED");
+
+ else if(ret & MBEDTLS_X509_BADCERT_FUTURE)
+ failf(data, "Cert verify failed: BADCERT_FUTURE");
+
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+
+ peercert = mbedtls_ssl_get_peer_cert(&backend->ssl);
+
+ if(peercert && data->set.verbose) {
+ const size_t bufsize = 16384;
+ char *buffer = malloc(bufsize);
+
+ if(!buffer)
+ return CURLE_OUT_OF_MEMORY;
+
+ if(mbedtls_x509_crt_info(buffer, bufsize, "* ", peercert) > 0)
+ infof(data, "Dumping cert info: %s", buffer);
+ else
+ infof(data, "Unable to dump certificate information");
+
+ free(buffer);
+ }
+
+ if(pinnedpubkey) {
+ int size;
+ CURLcode result;
+ mbedtls_x509_crt *p = NULL;
+ unsigned char *pubkey = NULL;
+
+#if MBEDTLS_VERSION_NUMBER == 0x03000000
+ if(!peercert || !peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p) ||
+ !peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len)) {
+#else
+ if(!peercert || !peercert->raw.p || !peercert->raw.len) {
+#endif
+ failf(data, "Failed due to missing peer certificate");
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ }
+
+ p = calloc(1, sizeof(*p));
+
+ if(!p)
+ return CURLE_OUT_OF_MEMORY;
+
+ pubkey = malloc(PUB_DER_MAX_BYTES);
+
+ if(!pubkey) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto pinnedpubkey_error;
+ }
+
+ mbedtls_x509_crt_init(p);
+
+ /* Make a copy of our const peercert because mbedtls_pk_write_pubkey_der
+ needs a non-const key, for now.
+ https://github.com/ARMmbed/mbedtls/issues/396 */
+#if MBEDTLS_VERSION_NUMBER == 0x03000000
+ if(mbedtls_x509_crt_parse_der(p,
+ peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p),
+ peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len))) {
+#else
+ if(mbedtls_x509_crt_parse_der(p, peercert->raw.p, peercert->raw.len)) {
+#endif
+ failf(data, "Failed copying peer certificate");
+ result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ goto pinnedpubkey_error;
+ }
+
+#if MBEDTLS_VERSION_NUMBER == 0x03000000
+ size = mbedtls_pk_write_pubkey_der(&p->MBEDTLS_PRIVATE(pk), pubkey,
+ PUB_DER_MAX_BYTES);
+#else
+ size = mbedtls_pk_write_pubkey_der(&p->pk, pubkey, PUB_DER_MAX_BYTES);
+#endif
+
+ if(size <= 0) {
+ failf(data, "Failed copying public key from peer certificate");
+ result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ goto pinnedpubkey_error;
+ }
+
+ /* mbedtls_pk_write_pubkey_der writes data at the end of the buffer. */
+ result = Curl_pin_peer_pubkey(data,
+ pinnedpubkey,
+ &pubkey[PUB_DER_MAX_BYTES - size], size);
+ pinnedpubkey_error:
+ mbedtls_x509_crt_free(p);
+ free(p);
+ free(pubkey);
+ if(result) {
+ return result;
+ }
+ }
+
+#ifdef HAS_ALPN
+ if(cf->conn->bits.tls_enable_alpn) {
+ const char *next_protocol = mbedtls_ssl_get_alpn_protocol(&backend->ssl);
+
+ if(next_protocol) {
+ infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, next_protocol);
+#ifdef USE_HTTP2
+ if(!strncmp(next_protocol, ALPN_H2, ALPN_H2_LENGTH) &&
+ !next_protocol[ALPN_H2_LENGTH]) {
+ cf->conn->alpn = CURL_HTTP_VERSION_2;
+ }
+ else
+#endif
+ if(!strncmp(next_protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH) &&
+ !next_protocol[ALPN_HTTP_1_1_LENGTH]) {
+ cf->conn->alpn = CURL_HTTP_VERSION_1_1;
+ }
+ }
+ else {
+ infof(data, VTLS_INFOF_NO_ALPN);
+ }
+ Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ }
+#endif
+
+ connssl->connecting_state = ssl_connect_3;
+ infof(data, "SSL connected");
+
+ return CURLE_OK;
+}
+
+static CURLcode
+mbed_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ CURLcode retcode = CURLE_OK;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+
+ DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+ DEBUGASSERT(backend);
+
+ if(ssl_config->primary.sessionid) {
+ int ret;
+ mbedtls_ssl_session *our_ssl_sessionid;
+ void *old_ssl_sessionid = NULL;
+ bool added = FALSE;
+
+ our_ssl_sessionid = malloc(sizeof(mbedtls_ssl_session));
+ if(!our_ssl_sessionid)
+ return CURLE_OUT_OF_MEMORY;
+
+ mbedtls_ssl_session_init(our_ssl_sessionid);
+
+ ret = mbedtls_ssl_get_session(&backend->ssl, our_ssl_sessionid);
+ if(ret) {
+ if(ret != MBEDTLS_ERR_SSL_ALLOC_FAILED)
+ mbedtls_ssl_session_free(our_ssl_sessionid);
+ free(our_ssl_sessionid);
+ failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ /* If there's already a matching session in the cache, delete it */
+ Curl_ssl_sessionid_lock(data);
+ if(!Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL))
+ Curl_ssl_delsessionid(data, old_ssl_sessionid);
+
+ retcode = Curl_ssl_addsessionid(cf, data, our_ssl_sessionid,
+ 0, &added);
+ Curl_ssl_sessionid_unlock(data);
+ if(!added) {
+ mbedtls_ssl_session_free(our_ssl_sessionid);
+ free(our_ssl_sessionid);
+ }
+ if(retcode) {
+ failf(data, "failed to store ssl session");
+ return retcode;
+ }
+ }
+
+ connssl->connecting_state = ssl_connect_done;
+
+ return CURLE_OK;
+}
+
+static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *mem, size_t len,
+ CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ int ret = -1;
+
+ (void)data;
+ DEBUGASSERT(backend);
+ ret = mbedtls_ssl_write(&backend->ssl, (unsigned char *)mem, len);
+
+ if(ret < 0) {
+ *curlcode = (ret == MBEDTLS_ERR_SSL_WANT_WRITE) ?
+ CURLE_AGAIN : CURLE_SEND_ERROR;
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static void mbedtls_close_all(struct Curl_easy *data)
+{
+ (void)data;
+}
+
+static void mbedtls_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ char buf[32];
+
+ (void)data;
+ DEBUGASSERT(backend);
+
+ /* Maybe the server has already sent a close notify alert.
+ Read it to avoid an RST on the TCP connection. */
+ (void)mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf, sizeof(buf));
+
+ mbedtls_pk_free(&backend->pk);
+ mbedtls_x509_crt_free(&backend->clicert);
+ mbedtls_x509_crt_free(&backend->cacert);
+#ifdef MBEDTLS_X509_CRL_PARSE_C
+ mbedtls_x509_crl_free(&backend->crl);
+#endif
+ mbedtls_ssl_config_free(&backend->config);
+ mbedtls_ssl_free(&backend->ssl);
+ mbedtls_ctr_drbg_free(&backend->ctr_drbg);
+#ifndef THREADING_SUPPORT
+ mbedtls_entropy_free(&backend->entropy);
+#endif /* THREADING_SUPPORT */
+}
+
+static ssize_t mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t buffersize,
+ CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ int ret = -1;
+ ssize_t len = -1;
+
+ (void)data;
+ DEBUGASSERT(backend);
+
+ ret = mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf,
+ buffersize);
+
+ if(ret <= 0) {
+ if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY)
+ return 0;
+
+ *curlcode = (ret == MBEDTLS_ERR_SSL_WANT_READ) ?
+ CURLE_AGAIN : CURLE_RECV_ERROR;
+ return -1;
+ }
+
+ len = ret;
+
+ return len;
+}
+
+static void mbedtls_session_free(void *ptr)
+{
+ mbedtls_ssl_session_free(ptr);
+ free(ptr);
+}
+
+static size_t mbedtls_version(char *buffer, size_t size)
+{
+#ifdef MBEDTLS_VERSION_C
+ /* if mbedtls_version_get_number() is available it is better */
+ unsigned int version = mbedtls_version_get_number();
+ return msnprintf(buffer, size, "mbedTLS/%u.%u.%u", version>>24,
+ (version>>16)&0xff, (version>>8)&0xff);
+#else
+ return msnprintf(buffer, size, "mbedTLS/%s", MBEDTLS_VERSION_STRING);
+#endif
+}
+
+static CURLcode mbedtls_random(struct Curl_easy *data,
+ unsigned char *entropy, size_t length)
+{
+#if defined(MBEDTLS_CTR_DRBG_C)
+ int ret = -1;
+ char errorbuf[128];
+ mbedtls_entropy_context ctr_entropy;
+ mbedtls_ctr_drbg_context ctr_drbg;
+ mbedtls_entropy_init(&ctr_entropy);
+ mbedtls_ctr_drbg_init(&ctr_drbg);
+
+ ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func,
+ &ctr_entropy, NULL, 0);
+
+ if(ret) {
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+ failf(data, "mbedtls_ctr_drbg_seed returned (-0x%04X) %s",
+ -ret, errorbuf);
+ }
+ else {
+ ret = mbedtls_ctr_drbg_random(&ctr_drbg, entropy, length);
+
+ if(ret) {
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+ failf(data, "mbedtls_ctr_drbg_random returned (-0x%04X) %s",
+ -ret, errorbuf);
+ }
+ }
+
+ mbedtls_ctr_drbg_free(&ctr_drbg);
+ mbedtls_entropy_free(&ctr_entropy);
+
+ return ret == 0 ? CURLE_OK : CURLE_FAILED_INIT;
+#elif defined(MBEDTLS_HAVEGE_C)
+ mbedtls_havege_state hs;
+ mbedtls_havege_init(&hs);
+ mbedtls_havege_random(&hs, entropy, length);
+ mbedtls_havege_free(&hs);
+ return CURLE_OK;
+#else
+ return CURLE_NOT_BUILT_IN;
+#endif
+}
+
+static CURLcode
+mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
+ bool nonblocking,
+ bool *done)
+{
+ CURLcode retcode;
+ struct ssl_connect_data *connssl = cf->ctx;
+ curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ timediff_t timeout_ms;
+ int what;
+
+ /* check if the connection has already been established */
+ if(ssl_connection_complete == connssl->state) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ if(ssl_connect_1 == connssl->connecting_state) {
+ /* Find out how much more time we're allowed */
+ timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+ if(timeout_ms < 0) {
+ /* no need to continue if time already is up */
+ failf(data, "SSL connection timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+ retcode = mbed_connect_step1(cf, data);
+ if(retcode)
+ return retcode;
+ }
+
+ while(ssl_connect_2 == connssl->connecting_state ||
+ ssl_connect_2_reading == connssl->connecting_state ||
+ ssl_connect_2_writing == connssl->connecting_state) {
+
+ /* check allowed time left */
+ timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+ if(timeout_ms < 0) {
+ /* no need to continue if time already is up */
+ failf(data, "SSL connection timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
+ /* if ssl is expecting something, check if it's available. */
+ if(connssl->connecting_state == ssl_connect_2_reading
+ || connssl->connecting_state == ssl_connect_2_writing) {
+
+ curl_socket_t writefd = ssl_connect_2_writing ==
+ connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+ curl_socket_t readfd = ssl_connect_2_reading ==
+ connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+
+ what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
+ nonblocking ? 0 : timeout_ms);
+ if(what < 0) {
+ /* fatal error */
+ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else if(0 == what) {
+ if(nonblocking) {
+ *done = FALSE;
+ return CURLE_OK;
+ }
+ else {
+ /* timeout */
+ failf(data, "SSL connection timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+ }
+ /* socket is readable or writable */
+ }
+
+ /* Run transaction, and return to the caller if it failed or if
+ * this connection is part of a multi handle and this loop would
+ * execute again. This permits the owner of a multi handle to
+ * abort a connection attempt before step2 has completed while
+ * ensuring that a client using select() or epoll() will always
+ * have a valid fdset to wait on.
+ */
+ retcode = mbed_connect_step2(cf, data);
+ if(retcode || (nonblocking &&
+ (ssl_connect_2 == connssl->connecting_state ||
+ ssl_connect_2_reading == connssl->connecting_state ||
+ ssl_connect_2_writing == connssl->connecting_state)))
+ return retcode;
+
+ } /* repeat step2 until all transactions are done. */
+
+ if(ssl_connect_3 == connssl->connecting_state) {
+ retcode = mbed_connect_step3(cf, data);
+ if(retcode)
+ return retcode;
+ }
+
+ if(ssl_connect_done == connssl->connecting_state) {
+ connssl->state = ssl_connection_complete;
+ *done = TRUE;
+ }
+ else
+ *done = FALSE;
+
+ /* Reset our connect state machine */
+ connssl->connecting_state = ssl_connect_1;
+
+ return CURLE_OK;
+}
+
+static CURLcode mbedtls_connect_nonblocking(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
+{
+ return mbed_connect_common(cf, data, TRUE, done);
+}
+
+
+static CURLcode mbedtls_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ CURLcode retcode;
+ bool done = FALSE;
+
+ retcode = mbed_connect_common(cf, data, FALSE, &done);
+ if(retcode)
+ return retcode;
+
+ DEBUGASSERT(done);
+
+ return CURLE_OK;
+}
+
+/*
+ * return 0 error initializing SSL
+ * return 1 SSL initialized successfully
+ */
+static int mbedtls_init(void)
+{
+ return Curl_mbedtlsthreadlock_thread_setup();
+}
+
+static void mbedtls_cleanup(void)
+{
+ (void)Curl_mbedtlsthreadlock_thread_cleanup();
+}
+
+static bool mbedtls_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ struct ssl_connect_data *ctx = cf->ctx;
+
+ (void)data;
+ DEBUGASSERT(ctx && ctx->backend);
+ return mbedtls_ssl_get_bytes_avail(&ctx->backend->ssl) != 0;
+}
+
+static CURLcode mbedtls_sha256sum(const unsigned char *input,
+ size_t inputlen,
+ unsigned char *sha256sum,
+ size_t sha256len UNUSED_PARAM)
+{
+ /* TODO: explain this for different mbedtls 2.x vs 3 version */
+ (void)sha256len;
+#if MBEDTLS_VERSION_NUMBER < 0x02070000
+ mbedtls_sha256(input, inputlen, sha256sum, 0);
+#else
+ /* returns 0 on success, otherwise failure */
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+ if(mbedtls_sha256(input, inputlen, sha256sum, 0) != 0)
+#else
+ if(mbedtls_sha256_ret(input, inputlen, sha256sum, 0) != 0)
+#endif
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+#endif
+ return CURLE_OK;
+}
+
+static void *mbedtls_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
+{
+ struct ssl_backend_data *backend = connssl->backend;
+ (void)info;
+ DEBUGASSERT(backend);
+ return &backend->ssl;
+}
+
+const struct Curl_ssl Curl_ssl_mbedtls = {
+ { CURLSSLBACKEND_MBEDTLS, "mbedtls" }, /* info */
+
+ SSLSUPP_CA_PATH |
+ SSLSUPP_CAINFO_BLOB |
+ SSLSUPP_PINNEDPUBKEY |
+ SSLSUPP_SSL_CTX |
+ SSLSUPP_HTTPS_PROXY,
+
+ sizeof(struct ssl_backend_data),
+
+ mbedtls_init, /* init */
+ mbedtls_cleanup, /* cleanup */
+ mbedtls_version, /* version */
+ Curl_none_check_cxn, /* check_cxn */
+ Curl_none_shutdown, /* shutdown */
+ mbedtls_data_pending, /* data_pending */
+ mbedtls_random, /* random */
+ Curl_none_cert_status_request, /* cert_status_request */
+ mbedtls_connect, /* connect */
+ mbedtls_connect_nonblocking, /* connect_nonblocking */
+ Curl_ssl_get_select_socks, /* getsock */
+ mbedtls_get_internals, /* get_internals */
+ mbedtls_close, /* close_one */
+ mbedtls_close_all, /* close_all */
+ mbedtls_session_free, /* session_free */
+ Curl_none_set_engine, /* set_engine */
+ Curl_none_set_engine_default, /* set_engine_default */
+ Curl_none_engines_list, /* engines_list */
+ Curl_none_false_start, /* false_start */
+ mbedtls_sha256sum, /* sha256sum */
+ NULL, /* associate_connection */
+ NULL, /* disassociate_connection */
+ NULL, /* free_multi_ssl_backend_data */
+ mbed_recv, /* recv decrypted data */
+ mbed_send, /* send data to encrypt */
+};
+
+#endif /* USE_MBEDTLS */
diff --git a/libs/libcurl/src/vtls/mbedtls.h b/libs/libcurl/src/vtls/mbedtls.h index ec3b43bf9c..89131273a6 100644 --- a/libs/libcurl/src/vtls/mbedtls.h +++ b/libs/libcurl/src/vtls/mbedtls.h @@ -1,34 +1,34 @@ -#ifndef HEADER_CURL_MBEDTLS_H -#define HEADER_CURL_MBEDTLS_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2010, Hoi-Ho Chan, <hoiho.chan@gmail.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_MBEDTLS - -extern const struct Curl_ssl Curl_ssl_mbedtls; - -#endif /* USE_MBEDTLS */ -#endif /* HEADER_CURL_MBEDTLS_H */ +#ifndef HEADER_CURL_MBEDTLS_H
+#define HEADER_CURL_MBEDTLS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2010, Hoi-Ho Chan, <hoiho.chan@gmail.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_MBEDTLS
+
+extern const struct Curl_ssl Curl_ssl_mbedtls;
+
+#endif /* USE_MBEDTLS */
+#endif /* HEADER_CURL_MBEDTLS_H */
diff --git a/libs/libcurl/src/vtls/mbedtls_threadlock.c b/libs/libcurl/src/vtls/mbedtls_threadlock.c index 3971e69b25..c4a3ae2e84 100644 --- a/libs/libcurl/src/vtls/mbedtls_threadlock.c +++ b/libs/libcurl/src/vtls/mbedtls_threadlock.c @@ -1,135 +1,134 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2013 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.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" - -#if defined(USE_MBEDTLS) && \ - ((defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \ - (defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H))) - -#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) -# include <pthread.h> -# define MBEDTLS_MUTEX_T pthread_mutex_t -#elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H) -# include <process.h> -# define MBEDTLS_MUTEX_T HANDLE -#endif - -#include "mbedtls_threadlock.h" -#include "curl_printf.h" -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -/* number of thread locks */ -#define NUMT 2 - -/* This array will store all of the mutexes available to Mbedtls. */ -static MBEDTLS_MUTEX_T *mutex_buf = NULL; - -int Curl_mbedtlsthreadlock_thread_setup(void) -{ - int i; - - mutex_buf = calloc(NUMT * sizeof(MBEDTLS_MUTEX_T), 1); - if(!mutex_buf) - return 0; /* error, no number of threads defined */ - - for(i = 0; i < NUMT; i++) { -#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) - if(pthread_mutex_init(&mutex_buf[i], NULL)) - return 0; /* pthread_mutex_init failed */ -#elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H) - mutex_buf[i] = CreateMutex(0, FALSE, 0); - if(mutex_buf[i] == 0) - return 0; /* CreateMutex failed */ -#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */ - } - - return 1; /* OK */ -} - -int Curl_mbedtlsthreadlock_thread_cleanup(void) -{ - int i; - - if(!mutex_buf) - return 0; /* error, no threads locks defined */ - - for(i = 0; i < NUMT; i++) { -#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) - if(pthread_mutex_destroy(&mutex_buf[i])) - return 0; /* pthread_mutex_destroy failed */ -#elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H) - if(!CloseHandle(mutex_buf[i])) - return 0; /* CloseHandle failed */ -#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */ - } - free(mutex_buf); - mutex_buf = NULL; - - return 1; /* OK */ -} - -int Curl_mbedtlsthreadlock_lock_function(int n) -{ - if(n < NUMT) { -#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) - if(pthread_mutex_lock(&mutex_buf[n])) { - DEBUGF(fprintf(stderr, - "Error: mbedtlsthreadlock_lock_function failed\n")); - return 0; /* pthread_mutex_lock failed */ - } -#elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H) - if(WaitForSingleObject(mutex_buf[n], INFINITE) == WAIT_FAILED) { - DEBUGF(fprintf(stderr, - "Error: mbedtlsthreadlock_lock_function failed\n")); - return 0; /* pthread_mutex_lock failed */ - } -#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */ - } - return 1; /* OK */ -} - -int Curl_mbedtlsthreadlock_unlock_function(int n) -{ - if(n < NUMT) { -#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) - if(pthread_mutex_unlock(&mutex_buf[n])) { - DEBUGF(fprintf(stderr, - "Error: mbedtlsthreadlock_unlock_function failed\n")); - return 0; /* pthread_mutex_unlock failed */ - } -#elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H) - if(!ReleaseMutex(mutex_buf[n])) { - DEBUGF(fprintf(stderr, - "Error: mbedtlsthreadlock_unlock_function failed\n")); - return 0; /* pthread_mutex_lock failed */ - } -#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */ - } - return 1; /* OK */ -} - -#endif /* USE_MBEDTLS */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2013 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.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"
+
+#if defined(USE_MBEDTLS) && \
+ ((defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \
+ defined(USE_THREADS_WIN32))
+
+#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
+# include <pthread.h>
+# define MBEDTLS_MUTEX_T pthread_mutex_t
+#elif defined(USE_THREADS_WIN32)
+# define MBEDTLS_MUTEX_T HANDLE
+#endif
+
+#include "mbedtls_threadlock.h"
+#include "curl_printf.h"
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/* number of thread locks */
+#define NUMT 2
+
+/* This array will store all of the mutexes available to Mbedtls. */
+static MBEDTLS_MUTEX_T *mutex_buf = NULL;
+
+int Curl_mbedtlsthreadlock_thread_setup(void)
+{
+ int i;
+
+ mutex_buf = calloc(NUMT * sizeof(MBEDTLS_MUTEX_T), 1);
+ if(!mutex_buf)
+ return 0; /* error, no number of threads defined */
+
+ for(i = 0; i < NUMT; i++) {
+#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
+ if(pthread_mutex_init(&mutex_buf[i], NULL))
+ return 0; /* pthread_mutex_init failed */
+#elif defined(USE_THREADS_WIN32)
+ mutex_buf[i] = CreateMutex(0, FALSE, 0);
+ if(mutex_buf[i] == 0)
+ return 0; /* CreateMutex failed */
+#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */
+ }
+
+ return 1; /* OK */
+}
+
+int Curl_mbedtlsthreadlock_thread_cleanup(void)
+{
+ int i;
+
+ if(!mutex_buf)
+ return 0; /* error, no threads locks defined */
+
+ for(i = 0; i < NUMT; i++) {
+#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
+ if(pthread_mutex_destroy(&mutex_buf[i]))
+ return 0; /* pthread_mutex_destroy failed */
+#elif defined(USE_THREADS_WIN32)
+ if(!CloseHandle(mutex_buf[i]))
+ return 0; /* CloseHandle failed */
+#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */
+ }
+ free(mutex_buf);
+ mutex_buf = NULL;
+
+ return 1; /* OK */
+}
+
+int Curl_mbedtlsthreadlock_lock_function(int n)
+{
+ if(n < NUMT) {
+#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
+ if(pthread_mutex_lock(&mutex_buf[n])) {
+ DEBUGF(fprintf(stderr,
+ "Error: mbedtlsthreadlock_lock_function failed\n"));
+ return 0; /* pthread_mutex_lock failed */
+ }
+#elif defined(USE_THREADS_WIN32)
+ if(WaitForSingleObject(mutex_buf[n], INFINITE) == WAIT_FAILED) {
+ DEBUGF(fprintf(stderr,
+ "Error: mbedtlsthreadlock_lock_function failed\n"));
+ return 0; /* pthread_mutex_lock failed */
+ }
+#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */
+ }
+ return 1; /* OK */
+}
+
+int Curl_mbedtlsthreadlock_unlock_function(int n)
+{
+ if(n < NUMT) {
+#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
+ if(pthread_mutex_unlock(&mutex_buf[n])) {
+ DEBUGF(fprintf(stderr,
+ "Error: mbedtlsthreadlock_unlock_function failed\n"));
+ return 0; /* pthread_mutex_unlock failed */
+ }
+#elif defined(USE_THREADS_WIN32)
+ if(!ReleaseMutex(mutex_buf[n])) {
+ DEBUGF(fprintf(stderr,
+ "Error: mbedtlsthreadlock_unlock_function failed\n"));
+ return 0; /* pthread_mutex_lock failed */
+ }
+#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */
+ }
+ return 1; /* OK */
+}
+
+#endif /* USE_MBEDTLS */
diff --git a/libs/libcurl/src/vtls/mbedtls_threadlock.h b/libs/libcurl/src/vtls/mbedtls_threadlock.h index 3a50d03817..bc268f93eb 100644 --- a/libs/libcurl/src/vtls/mbedtls_threadlock.h +++ b/libs/libcurl/src/vtls/mbedtls_threadlock.h @@ -1,50 +1,50 @@ -#ifndef HEADER_CURL_MBEDTLS_THREADLOCK_H -#define HEADER_CURL_MBEDTLS_THREADLOCK_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2013 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2010, Hoi-Ho Chan, <hoiho.chan@gmail.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_MBEDTLS - -#if (defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \ - (defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H)) - -int Curl_mbedtlsthreadlock_thread_setup(void); -int Curl_mbedtlsthreadlock_thread_cleanup(void); -int Curl_mbedtlsthreadlock_lock_function(int n); -int Curl_mbedtlsthreadlock_unlock_function(int n); - -#else - -#define Curl_mbedtlsthreadlock_thread_setup() 1 -#define Curl_mbedtlsthreadlock_thread_cleanup() 1 -#define Curl_mbedtlsthreadlock_lock_function(x) 1 -#define Curl_mbedtlsthreadlock_unlock_function(x) 1 - -#endif /* USE_THREADS_POSIX || USE_THREADS_WIN32 */ - -#endif /* USE_MBEDTLS */ - -#endif /* HEADER_CURL_MBEDTLS_THREADLOCK_H */ +#ifndef HEADER_CURL_MBEDTLS_THREADLOCK_H
+#define HEADER_CURL_MBEDTLS_THREADLOCK_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2013 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2010, Hoi-Ho Chan, <hoiho.chan@gmail.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_MBEDTLS
+
+#if (defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \
+ defined(USE_THREADS_WIN32)
+
+int Curl_mbedtlsthreadlock_thread_setup(void);
+int Curl_mbedtlsthreadlock_thread_cleanup(void);
+int Curl_mbedtlsthreadlock_lock_function(int n);
+int Curl_mbedtlsthreadlock_unlock_function(int n);
+
+#else
+
+#define Curl_mbedtlsthreadlock_thread_setup() 1
+#define Curl_mbedtlsthreadlock_thread_cleanup() 1
+#define Curl_mbedtlsthreadlock_lock_function(x) 1
+#define Curl_mbedtlsthreadlock_unlock_function(x) 1
+
+#endif /* USE_THREADS_POSIX || USE_THREADS_WIN32 */
+
+#endif /* USE_MBEDTLS */
+
+#endif /* HEADER_CURL_MBEDTLS_THREADLOCK_H */
diff --git a/libs/libcurl/src/vtls/nss.c b/libs/libcurl/src/vtls/nss.c index 12cf618f56..350c369d83 100644 --- a/libs/libcurl/src/vtls/nss.c +++ b/libs/libcurl/src/vtls/nss.c @@ -1,2537 +1,2569 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - -/* - * Source file for all NSS-specific code for the TLS/SSL layer. No code - * but vtls.c should ever call or use these functions. - */ - -#include "curl_setup.h" - -#ifdef USE_NSS - -#include "urldata.h" -#include "sendf.h" -#include "formdata.h" /* for the boundary function */ -#include "url.h" /* for the ssl config check function */ -#include "connect.h" -#include "strcase.h" -#include "select.h" -#include "vtls.h" -#include "llist.h" -#include "multiif.h" -#include "curl_printf.h" -#include "nssg.h" -#include <nspr.h> -#include <nss.h> -#include <ssl.h> -#include <sslerr.h> -#include <secerr.h> -#include <secmod.h> -#include <sslproto.h> -#include <prtypes.h> -#include <pk11pub.h> -#include <prio.h> -#include <secitem.h> -#include <secport.h> -#include <certdb.h> -#include <base64.h> -#include <cert.h> -#include <prerror.h> -#include <keyhi.h> /* for SECKEY_DestroyPublicKey() */ -#include <private/pprio.h> /* for PR_ImportTCPSocket */ - -#define NSSVERNUM ((NSS_VMAJOR<<16)|(NSS_VMINOR<<8)|NSS_VPATCH) - -#if NSSVERNUM >= 0x030f00 /* 3.15.0 */ -#include <ocsp.h> -#endif - -#include "strcase.h" -#include "warnless.h" -#include "x509asn1.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -#define SSL_DIR "/etc/pki/nssdb" - -/* enough to fit the string "PEM Token #[0|1]" */ -#define SLOTSIZE 13 - -struct ssl_backend_data { - PRFileDesc *handle; - char *client_nickname; - struct Curl_easy *data; - struct Curl_llist obj_list; - PK11GenericObject *obj_clicert; -}; - -static PRLock *nss_initlock = NULL; -static PRLock *nss_crllock = NULL; -static PRLock *nss_findslot_lock = NULL; -static PRLock *nss_trustload_lock = NULL; -static struct Curl_llist nss_crl_list; -static NSSInitContext *nss_context = NULL; -static volatile int initialized = 0; - -/* type used to wrap pointers as list nodes */ -struct ptr_list_wrap { - void *ptr; - struct Curl_llist_element node; -}; - -struct cipher_s { - const char *name; - int num; -}; - -#define PK11_SETATTRS(_attr, _idx, _type, _val, _len) do { \ - CK_ATTRIBUTE *ptr = (_attr) + ((_idx)++); \ - ptr->type = (_type); \ - ptr->pValue = (_val); \ - ptr->ulValueLen = (_len); \ -} while(0) - -#define CERT_NewTempCertificate __CERT_NewTempCertificate - -#define NUM_OF_CIPHERS sizeof(cipherlist)/sizeof(cipherlist[0]) -static const struct cipher_s cipherlist[] = { - /* SSL2 cipher suites */ - {"rc4", SSL_EN_RC4_128_WITH_MD5}, - {"rc4-md5", SSL_EN_RC4_128_WITH_MD5}, - {"rc4export", SSL_EN_RC4_128_EXPORT40_WITH_MD5}, - {"rc2", SSL_EN_RC2_128_CBC_WITH_MD5}, - {"rc2export", SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5}, - {"des", SSL_EN_DES_64_CBC_WITH_MD5}, - {"desede3", SSL_EN_DES_192_EDE3_CBC_WITH_MD5}, - /* SSL3/TLS cipher suites */ - {"rsa_rc4_128_md5", SSL_RSA_WITH_RC4_128_MD5}, - {"rsa_rc4_128_sha", SSL_RSA_WITH_RC4_128_SHA}, - {"rsa_3des_sha", SSL_RSA_WITH_3DES_EDE_CBC_SHA}, - {"rsa_des_sha", SSL_RSA_WITH_DES_CBC_SHA}, - {"rsa_rc4_40_md5", SSL_RSA_EXPORT_WITH_RC4_40_MD5}, - {"rsa_rc2_40_md5", SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5}, - {"rsa_null_md5", SSL_RSA_WITH_NULL_MD5}, - {"rsa_null_sha", SSL_RSA_WITH_NULL_SHA}, - {"fips_3des_sha", SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA}, - {"fips_des_sha", SSL_RSA_FIPS_WITH_DES_CBC_SHA}, - {"fortezza", SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA}, - {"fortezza_rc4_128_sha", SSL_FORTEZZA_DMS_WITH_RC4_128_SHA}, - {"fortezza_null", SSL_FORTEZZA_DMS_WITH_NULL_SHA}, - {"dhe_rsa_3des_sha", SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA}, - {"dhe_dss_3des_sha", SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA}, - {"dhe_rsa_des_sha", SSL_DHE_RSA_WITH_DES_CBC_SHA}, - {"dhe_dss_des_sha", SSL_DHE_DSS_WITH_DES_CBC_SHA}, - /* TLS 1.0: Exportable 56-bit Cipher Suites. */ - {"rsa_des_56_sha", TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA}, - {"rsa_rc4_56_sha", TLS_RSA_EXPORT1024_WITH_RC4_56_SHA}, - /* Ephemeral DH with RC4 bulk encryption */ - {"dhe_dss_rc4_128_sha", TLS_DHE_DSS_WITH_RC4_128_SHA}, - /* AES ciphers. */ - {"dhe_dss_aes_128_cbc_sha", TLS_DHE_DSS_WITH_AES_128_CBC_SHA}, - {"dhe_dss_aes_256_cbc_sha", TLS_DHE_DSS_WITH_AES_256_CBC_SHA}, - {"dhe_rsa_aes_128_cbc_sha", TLS_DHE_RSA_WITH_AES_128_CBC_SHA}, - {"dhe_rsa_aes_256_cbc_sha", TLS_DHE_RSA_WITH_AES_256_CBC_SHA}, - {"rsa_aes_128_sha", TLS_RSA_WITH_AES_128_CBC_SHA}, - {"rsa_aes_256_sha", TLS_RSA_WITH_AES_256_CBC_SHA}, - /* ECC ciphers. */ - {"ecdh_ecdsa_null_sha", TLS_ECDH_ECDSA_WITH_NULL_SHA}, - {"ecdh_ecdsa_rc4_128_sha", TLS_ECDH_ECDSA_WITH_RC4_128_SHA}, - {"ecdh_ecdsa_3des_sha", TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA}, - {"ecdh_ecdsa_aes_128_sha", TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA}, - {"ecdh_ecdsa_aes_256_sha", TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA}, - {"ecdhe_ecdsa_null_sha", TLS_ECDHE_ECDSA_WITH_NULL_SHA}, - {"ecdhe_ecdsa_rc4_128_sha", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA}, - {"ecdhe_ecdsa_3des_sha", TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA}, - {"ecdhe_ecdsa_aes_128_sha", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA}, - {"ecdhe_ecdsa_aes_256_sha", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}, - {"ecdh_rsa_null_sha", TLS_ECDH_RSA_WITH_NULL_SHA}, - {"ecdh_rsa_128_sha", TLS_ECDH_RSA_WITH_RC4_128_SHA}, - {"ecdh_rsa_3des_sha", TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA}, - {"ecdh_rsa_aes_128_sha", TLS_ECDH_RSA_WITH_AES_128_CBC_SHA}, - {"ecdh_rsa_aes_256_sha", TLS_ECDH_RSA_WITH_AES_256_CBC_SHA}, - {"ecdhe_rsa_null", TLS_ECDHE_RSA_WITH_NULL_SHA}, - {"ecdhe_rsa_rc4_128_sha", TLS_ECDHE_RSA_WITH_RC4_128_SHA}, - {"ecdhe_rsa_3des_sha", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA}, - {"ecdhe_rsa_aes_128_sha", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, - {"ecdhe_rsa_aes_256_sha", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, - {"ecdh_anon_null_sha", TLS_ECDH_anon_WITH_NULL_SHA}, - {"ecdh_anon_rc4_128sha", TLS_ECDH_anon_WITH_RC4_128_SHA}, - {"ecdh_anon_3des_sha", TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA}, - {"ecdh_anon_aes_128_sha", TLS_ECDH_anon_WITH_AES_128_CBC_SHA}, - {"ecdh_anon_aes_256_sha", TLS_ECDH_anon_WITH_AES_256_CBC_SHA}, -#ifdef TLS_RSA_WITH_NULL_SHA256 - /* new HMAC-SHA256 cipher suites specified in RFC */ - {"rsa_null_sha_256", TLS_RSA_WITH_NULL_SHA256}, - {"rsa_aes_128_cbc_sha_256", TLS_RSA_WITH_AES_128_CBC_SHA256}, - {"rsa_aes_256_cbc_sha_256", TLS_RSA_WITH_AES_256_CBC_SHA256}, - {"dhe_rsa_aes_128_cbc_sha_256", TLS_DHE_RSA_WITH_AES_128_CBC_SHA256}, - {"dhe_rsa_aes_256_cbc_sha_256", TLS_DHE_RSA_WITH_AES_256_CBC_SHA256}, - {"ecdhe_ecdsa_aes_128_cbc_sha_256", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256}, - {"ecdhe_rsa_aes_128_cbc_sha_256", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256}, -#endif -#ifdef TLS_RSA_WITH_AES_128_GCM_SHA256 - /* AES GCM cipher suites in RFC 5288 and RFC 5289 */ - {"rsa_aes_128_gcm_sha_256", TLS_RSA_WITH_AES_128_GCM_SHA256}, - {"dhe_rsa_aes_128_gcm_sha_256", TLS_DHE_RSA_WITH_AES_128_GCM_SHA256}, - {"dhe_dss_aes_128_gcm_sha_256", TLS_DHE_DSS_WITH_AES_128_GCM_SHA256}, - {"ecdhe_ecdsa_aes_128_gcm_sha_256", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, - {"ecdh_ecdsa_aes_128_gcm_sha_256", TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256}, - {"ecdhe_rsa_aes_128_gcm_sha_256", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - {"ecdh_rsa_aes_128_gcm_sha_256", TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256}, -#endif -#ifdef TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - /* cipher suites using SHA384 */ - {"rsa_aes_256_gcm_sha_384", TLS_RSA_WITH_AES_256_GCM_SHA384}, - {"dhe_rsa_aes_256_gcm_sha_384", TLS_DHE_RSA_WITH_AES_256_GCM_SHA384}, - {"dhe_dss_aes_256_gcm_sha_384", TLS_DHE_DSS_WITH_AES_256_GCM_SHA384}, - {"ecdhe_ecdsa_aes_256_sha_384", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384}, - {"ecdhe_rsa_aes_256_sha_384", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384}, - {"ecdhe_ecdsa_aes_256_gcm_sha_384", TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384}, - {"ecdhe_rsa_aes_256_gcm_sha_384", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384}, -#endif -#ifdef TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 - /* chacha20-poly1305 cipher suites */ - {"ecdhe_rsa_chacha20_poly1305_sha_256", - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, - {"ecdhe_ecdsa_chacha20_poly1305_sha_256", - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256}, - {"dhe_rsa_chacha20_poly1305_sha_256", - TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, -#endif -#ifdef TLS_AES_256_GCM_SHA384 - {"aes_128_gcm_sha_256", TLS_AES_128_GCM_SHA256}, - {"aes_256_gcm_sha_384", TLS_AES_256_GCM_SHA384}, - {"chacha20_poly1305_sha_256", TLS_CHACHA20_POLY1305_SHA256}, -#endif -#ifdef TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 - /* AES CBC cipher suites in RFC 5246. Introduced in NSS release 3.20 */ - {"dhe_dss_aes_128_sha_256", TLS_DHE_DSS_WITH_AES_128_CBC_SHA256}, - {"dhe_dss_aes_256_sha_256", TLS_DHE_DSS_WITH_AES_256_CBC_SHA256}, -#endif -#ifdef TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA - /* Camellia cipher suites in RFC 4132/5932. - Introduced in NSS release 3.12 */ - {"dhe_rsa_camellia_128_sha", TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA}, - {"dhe_dss_camellia_128_sha", TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA}, - {"dhe_rsa_camellia_256_sha", TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA}, - {"dhe_dss_camellia_256_sha", TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA}, - {"rsa_camellia_128_sha", TLS_RSA_WITH_CAMELLIA_128_CBC_SHA}, - {"rsa_camellia_256_sha", TLS_RSA_WITH_CAMELLIA_256_CBC_SHA}, -#endif -#ifdef TLS_RSA_WITH_SEED_CBC_SHA - /* SEED cipher suite in RFC 4162. Introduced in NSS release 3.12.3 */ - {"rsa_seed_sha", TLS_RSA_WITH_SEED_CBC_SHA}, -#endif -}; - -#if defined(WIN32) -static const char *pem_library = "nsspem.dll"; -static const char *trust_library = "nssckbi.dll"; -#elif defined(__APPLE__) -static const char *pem_library = "libnsspem.dylib"; -static const char *trust_library = "libnssckbi.dylib"; -#else -static const char *pem_library = "libnsspem.so"; -static const char *trust_library = "libnssckbi.so"; -#endif - -static SECMODModule *pem_module = NULL; -static SECMODModule *trust_module = NULL; - -/* NSPR I/O layer we use to detect blocking direction during SSL handshake */ -static PRDescIdentity nspr_io_identity = PR_INVALID_IO_LAYER; -static PRIOMethods nspr_io_methods; - -static const char *nss_error_to_name(PRErrorCode code) -{ - const char *name = PR_ErrorToName(code); - if(name) - return name; - - return "unknown error"; -} - -static void nss_print_error_message(struct Curl_easy *data, PRUint32 err) -{ - failf(data, "%s", PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT)); -} - -static char *nss_sslver_to_name(PRUint16 nssver) -{ - switch(nssver) { - case SSL_LIBRARY_VERSION_2: - return strdup("SSLv2"); - case SSL_LIBRARY_VERSION_3_0: - return strdup("SSLv3"); - case SSL_LIBRARY_VERSION_TLS_1_0: - return strdup("TLSv1.0"); -#ifdef SSL_LIBRARY_VERSION_TLS_1_1 - case SSL_LIBRARY_VERSION_TLS_1_1: - return strdup("TLSv1.1"); -#endif -#ifdef SSL_LIBRARY_VERSION_TLS_1_2 - case SSL_LIBRARY_VERSION_TLS_1_2: - return strdup("TLSv1.2"); -#endif -#ifdef SSL_LIBRARY_VERSION_TLS_1_3 - case SSL_LIBRARY_VERSION_TLS_1_3: - return strdup("TLSv1.3"); -#endif - default: - return curl_maprintf("0x%04x", nssver); - } -} - -/* the longest cipher name this supports */ -#define MAX_CIPHER_LENGTH 128 - -static SECStatus set_ciphers(struct Curl_easy *data, PRFileDesc *model, - const char *cipher_list) -{ - unsigned int i; - const char *cipher; - - /* use accessors to avoid dynamic linking issues after an update of NSS */ - const PRUint16 num_implemented_ciphers = SSL_GetNumImplementedCiphers(); - const PRUint16 *implemented_ciphers = SSL_GetImplementedCiphers(); - if(!implemented_ciphers) - return SECFailure; - - /* First disable all ciphers. This uses a different max value in case - * NSS adds more ciphers later we don't want them available by - * accident - */ - for(i = 0; i < num_implemented_ciphers; i++) { - SSL_CipherPrefSet(model, implemented_ciphers[i], PR_FALSE); - } - - cipher = cipher_list; - - while(cipher && cipher[0]) { - const char *end; - char name[MAX_CIPHER_LENGTH + 1]; - size_t len; - bool found = FALSE; - while((*cipher) && (ISBLANK(*cipher))) - ++cipher; - - end = strpbrk(cipher, ":, "); - if(end) - len = end - cipher; - else - len = strlen(cipher); - - if(len > MAX_CIPHER_LENGTH) { - failf(data, "Bad cipher list"); - return SECFailure; - } - else if(len) { - memcpy(name, cipher, len); - name[len] = 0; - - for(i = 0; i<NUM_OF_CIPHERS; i++) { - if(strcasecompare(name, cipherlist[i].name)) { - /* Enable the selected cipher */ - if(SSL_CipherPrefSet(model, cipherlist[i].num, PR_TRUE) != - SECSuccess) { - failf(data, "cipher-suite not supported by NSS: %s", name); - return SECFailure; - } - found = TRUE; - break; - } - } - } - - if(!found && len) { - failf(data, "Unknown cipher: %s", name); - return SECFailure; - } - if(end) - cipher = ++end; - else - break; - } - - return SECSuccess; -} - -/* - * Return true if at least one cipher-suite is enabled. Used to determine - * if we need to call NSS_SetDomesticPolicy() to enable the default ciphers. - */ -static bool any_cipher_enabled(void) -{ - unsigned int i; - - for(i = 0; i<NUM_OF_CIPHERS; i++) { - PRInt32 policy = 0; - SSL_CipherPolicyGet(cipherlist[i].num, &policy); - if(policy) - return TRUE; - } - - return FALSE; -} - -/* - * Determine whether the nickname passed in is a filename that needs to - * be loaded as a PEM or a regular NSS nickname. - * - * returns 1 for a file - * returns 0 for not a file (NSS nickname) - */ -static int is_file(const char *filename) -{ - struct_stat st; - - if(!filename) - return 0; - - if(stat(filename, &st) == 0) - if(S_ISREG(st.st_mode) || S_ISFIFO(st.st_mode) || S_ISCHR(st.st_mode)) - return 1; - - return 0; -} - -/* Check if the given string is filename or nickname of a certificate. If the - * given string is recognized as filename, return NULL. If the given string is - * recognized as nickname, return a duplicated string. The returned string - * should be later deallocated using free(). If the OOM failure occurs, we - * return NULL, too. - */ -static char *dup_nickname(struct Curl_easy *data, const char *str) -{ - const char *n; - - if(!is_file(str)) - /* no such file exists, use the string as nickname */ - return strdup(str); - - /* search the first slash; we require at least one slash in a file name */ - n = strchr(str, '/'); - if(!n) { - infof(data, "WARNING: certificate file name \"%s\" handled as nickname; " - "please use \"./%s\" to force file name", str, str); - return strdup(str); - } - - /* we'll use the PEM reader to read the certificate from file */ - return NULL; -} - -/* Lock/unlock wrapper for PK11_FindSlotByName() to work around race condition - * in nssSlot_IsTokenPresent() causing spurious SEC_ERROR_NO_TOKEN. For more - * details, go to <https://bugzilla.mozilla.org/1297397>. - */ -static PK11SlotInfo* nss_find_slot_by_name(const char *slot_name) -{ - PK11SlotInfo *slot; - PR_Lock(nss_findslot_lock); - slot = PK11_FindSlotByName(slot_name); - PR_Unlock(nss_findslot_lock); - return slot; -} - -/* wrap 'ptr' as list node and tail-insert into 'list' */ -static CURLcode insert_wrapped_ptr(struct Curl_llist *list, void *ptr) -{ - struct ptr_list_wrap *wrap = malloc(sizeof(*wrap)); - if(!wrap) - return CURLE_OUT_OF_MEMORY; - - wrap->ptr = ptr; - Curl_llist_insert_next(list, list->tail, wrap, &wrap->node); - return CURLE_OK; -} - -/* Call PK11_CreateGenericObject() with the given obj_class and filename. If - * the call succeeds, append the object handle to the list of objects so that - * the object can be destroyed in nss_close(). */ -static CURLcode nss_create_object(struct ssl_connect_data *connssl, - CK_OBJECT_CLASS obj_class, - const char *filename, bool cacert) -{ - PK11SlotInfo *slot; - PK11GenericObject *obj; - CK_BBOOL cktrue = CK_TRUE; - CK_BBOOL ckfalse = CK_FALSE; - CK_ATTRIBUTE attrs[/* max count of attributes */ 4]; - int attr_cnt = 0; - CURLcode result = (cacert) - ? CURLE_SSL_CACERT_BADFILE - : CURLE_SSL_CERTPROBLEM; - - const int slot_id = (cacert) ? 0 : 1; - char *slot_name = aprintf("PEM Token #%d", slot_id); - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - if(!slot_name) - return CURLE_OUT_OF_MEMORY; - - slot = nss_find_slot_by_name(slot_name); - free(slot_name); - if(!slot) - return result; - - PK11_SETATTRS(attrs, attr_cnt, CKA_CLASS, &obj_class, sizeof(obj_class)); - PK11_SETATTRS(attrs, attr_cnt, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL)); - PK11_SETATTRS(attrs, attr_cnt, CKA_LABEL, (unsigned char *)filename, - (CK_ULONG)strlen(filename) + 1); - - if(CKO_CERTIFICATE == obj_class) { - CK_BBOOL *pval = (cacert) ? (&cktrue) : (&ckfalse); - PK11_SETATTRS(attrs, attr_cnt, CKA_TRUST, pval, sizeof(*pval)); - } - - /* PK11_CreateManagedGenericObject() was introduced in NSS 3.34 because - * PK11_DestroyGenericObject() does not release resources allocated by - * PK11_CreateGenericObject() early enough. */ - obj = -#ifdef HAVE_PK11_CREATEMANAGEDGENERICOBJECT - PK11_CreateManagedGenericObject -#else - PK11_CreateGenericObject -#endif - (slot, attrs, attr_cnt, PR_FALSE); - - PK11_FreeSlot(slot); - if(!obj) - return result; - - if(insert_wrapped_ptr(&backend->obj_list, obj) != CURLE_OK) { - PK11_DestroyGenericObject(obj); - return CURLE_OUT_OF_MEMORY; - } - - if(!cacert && CKO_CERTIFICATE == obj_class) - /* store reference to a client certificate */ - backend->obj_clicert = obj; - - return CURLE_OK; -} - -/* Destroy the NSS object whose handle is given by ptr. This function is - * a callback of Curl_llist_alloc() used by Curl_llist_destroy() to destroy - * NSS objects in nss_close() */ -static void nss_destroy_object(void *user, void *ptr) -{ - struct ptr_list_wrap *wrap = (struct ptr_list_wrap *) ptr; - PK11GenericObject *obj = (PK11GenericObject *) wrap->ptr; - (void) user; - PK11_DestroyGenericObject(obj); - free(wrap); -} - -/* same as nss_destroy_object() but for CRL items */ -static void nss_destroy_crl_item(void *user, void *ptr) -{ - struct ptr_list_wrap *wrap = (struct ptr_list_wrap *) ptr; - SECItem *crl_der = (SECItem *) wrap->ptr; - (void) user; - SECITEM_FreeItem(crl_der, PR_TRUE); - free(wrap); -} - -static CURLcode nss_load_cert(struct ssl_connect_data *ssl, - const char *filename, PRBool cacert) -{ - CURLcode result = (cacert) - ? CURLE_SSL_CACERT_BADFILE - : CURLE_SSL_CERTPROBLEM; - - /* libnsspem.so leaks memory if the requested file does not exist. For more - * details, go to <https://bugzilla.redhat.com/734760>. */ - if(is_file(filename)) - result = nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert); - - if(!result && !cacert) { - /* we have successfully loaded a client certificate */ - char *nickname = NULL; - char *n = strrchr(filename, '/'); - if(n) - n++; - - /* The following undocumented magic helps to avoid a SIGSEGV on call - * of PK11_ReadRawAttribute() from SelectClientCert() when using an - * immature version of libnsspem.so. For more details, go to - * <https://bugzilla.redhat.com/733685>. */ - nickname = aprintf("PEM Token #1:%s", n); - if(nickname) { - CERTCertificate *cert = PK11_FindCertFromNickname(nickname, NULL); - if(cert) - CERT_DestroyCertificate(cert); - - free(nickname); - } - } - - return result; -} - -/* add given CRL to cache if it is not already there */ -static CURLcode nss_cache_crl(SECItem *crl_der) -{ - CERTCertDBHandle *db = CERT_GetDefaultCertDB(); - CERTSignedCrl *crl = SEC_FindCrlByDERCert(db, crl_der, 0); - if(crl) { - /* CRL already cached */ - SEC_DestroyCrl(crl); - SECITEM_FreeItem(crl_der, PR_TRUE); - return CURLE_OK; - } - - /* acquire lock before call of CERT_CacheCRL() and accessing nss_crl_list */ - PR_Lock(nss_crllock); - - if(SECSuccess != CERT_CacheCRL(db, crl_der)) { - /* unable to cache CRL */ - SECITEM_FreeItem(crl_der, PR_TRUE); - PR_Unlock(nss_crllock); - return CURLE_SSL_CRL_BADFILE; - } - - /* store the CRL item so that we can free it in nss_cleanup() */ - if(insert_wrapped_ptr(&nss_crl_list, crl_der) != CURLE_OK) { - if(SECSuccess == CERT_UncacheCRL(db, crl_der)) - SECITEM_FreeItem(crl_der, PR_TRUE); - PR_Unlock(nss_crllock); - return CURLE_OUT_OF_MEMORY; - } - - /* we need to clear session cache, so that the CRL could take effect */ - SSL_ClearSessionCache(); - PR_Unlock(nss_crllock); - return CURLE_OK; -} - -static CURLcode nss_load_crl(const char *crlfilename) -{ - PRFileDesc *infile; - PRFileInfo info; - SECItem filedata = { 0, NULL, 0 }; - SECItem *crl_der = NULL; - char *body; - - infile = PR_Open(crlfilename, PR_RDONLY, 0); - if(!infile) - return CURLE_SSL_CRL_BADFILE; - - if(PR_SUCCESS != PR_GetOpenFileInfo(infile, &info)) - goto fail; - - if(!SECITEM_AllocItem(NULL, &filedata, info.size + /* zero ended */ 1)) - goto fail; - - if(info.size != PR_Read(infile, filedata.data, info.size)) - goto fail; - - crl_der = SECITEM_AllocItem(NULL, NULL, 0U); - if(!crl_der) - goto fail; - - /* place a trailing zero right after the visible data */ - body = (char *)filedata.data; - body[--filedata.len] = '\0'; - - body = strstr(body, "-----BEGIN"); - if(body) { - /* assume ASCII */ - char *trailer; - char *begin = PORT_Strchr(body, '\n'); - if(!begin) - begin = PORT_Strchr(body, '\r'); - if(!begin) - goto fail; - - trailer = strstr(++begin, "-----END"); - if(!trailer) - goto fail; - - /* retrieve DER from ASCII */ - *trailer = '\0'; - if(ATOB_ConvertAsciiToItem(crl_der, begin)) - goto fail; - - SECITEM_FreeItem(&filedata, PR_FALSE); - } - else - /* assume DER */ - *crl_der = filedata; - - PR_Close(infile); - return nss_cache_crl(crl_der); - -fail: - PR_Close(infile); - SECITEM_FreeItem(crl_der, PR_TRUE); - SECITEM_FreeItem(&filedata, PR_FALSE); - return CURLE_SSL_CRL_BADFILE; -} - -static CURLcode nss_load_key(struct Curl_easy *data, struct connectdata *conn, - int sockindex, char *key_file) -{ - PK11SlotInfo *slot, *tmp; - SECStatus status; - CURLcode result; - struct ssl_connect_data *ssl = conn->ssl; - - (void)sockindex; /* unused */ - - result = nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE); - if(result) { - PR_SetError(SEC_ERROR_BAD_KEY, 0); - return result; - } - - slot = nss_find_slot_by_name("PEM Token #1"); - if(!slot) - return CURLE_SSL_CERTPROBLEM; - - /* This will force the token to be seen as re-inserted */ - tmp = SECMOD_WaitForAnyTokenEvent(pem_module, 0, 0); - if(tmp) - PK11_FreeSlot(tmp); - if(!PK11_IsPresent(slot)) { - PK11_FreeSlot(slot); - return CURLE_SSL_CERTPROBLEM; - } - - status = PK11_Authenticate(slot, PR_TRUE, SSL_SET_OPTION(key_passwd)); - PK11_FreeSlot(slot); - - return (SECSuccess == status) ? CURLE_OK : CURLE_SSL_CERTPROBLEM; -} - -static int display_error(struct Curl_easy *data, PRInt32 err, - const char *filename) -{ - switch(err) { - case SEC_ERROR_BAD_PASSWORD: - failf(data, "Unable to load client key: Incorrect password"); - return 1; - case SEC_ERROR_UNKNOWN_CERT: - failf(data, "Unable to load certificate %s", filename); - return 1; - default: - break; - } - return 0; /* The caller will print a generic error */ -} - -static CURLcode cert_stuff(struct Curl_easy *data, struct connectdata *conn, - int sockindex, char *cert_file, char *key_file) -{ - CURLcode result; - - if(cert_file) { - result = nss_load_cert(&conn->ssl[sockindex], cert_file, PR_FALSE); - if(result) { - const PRErrorCode err = PR_GetError(); - if(!display_error(data, err, cert_file)) { - const char *err_name = nss_error_to_name(err); - failf(data, "unable to load client cert: %d (%s)", err, err_name); - } - - return result; - } - } - - if(key_file || (is_file(cert_file))) { - if(key_file) - result = nss_load_key(data, conn, sockindex, key_file); - else - /* In case the cert file also has the key */ - result = nss_load_key(data, conn, sockindex, cert_file); - if(result) { - const PRErrorCode err = PR_GetError(); - if(!display_error(data, err, key_file)) { - const char *err_name = nss_error_to_name(err); - failf(data, "unable to load client key: %d (%s)", err, err_name); - } - - return result; - } - } - - return CURLE_OK; -} - -static char *nss_get_password(PK11SlotInfo *slot, PRBool retry, void *arg) -{ - (void)slot; /* unused */ - - if(retry || !arg) - return NULL; - else - return (char *)PORT_Strdup((char *)arg); -} - -/* bypass the default SSL_AuthCertificate() hook in case we do not want to - * verify peer */ -static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig, - PRBool isServer) -{ - struct Curl_easy *data = (struct Curl_easy *)arg; - struct connectdata *conn = data->conn; - -#ifdef SSL_ENABLE_OCSP_STAPLING - if(SSL_CONN_CONFIG(verifystatus)) { - SECStatus cacheResult; - - const SECItemArray *csa = SSL_PeerStapledOCSPResponses(fd); - if(!csa) { - failf(data, "Invalid OCSP response"); - return SECFailure; - } - - if(csa->len == 0) { - failf(data, "No OCSP response received"); - return SECFailure; - } - - cacheResult = CERT_CacheOCSPResponseFromSideChannel( - CERT_GetDefaultCertDB(), SSL_PeerCertificate(fd), - PR_Now(), &csa->items[0], arg - ); - - if(cacheResult != SECSuccess) { - failf(data, "Invalid OCSP response"); - return cacheResult; - } - } -#endif - - if(!SSL_CONN_CONFIG(verifypeer)) { - infof(data, "skipping SSL peer certificate verification"); - return SECSuccess; - } - - return SSL_AuthCertificate(CERT_GetDefaultCertDB(), fd, checksig, isServer); -} - -/** - * Inform the application that the handshake is complete. - */ -static void HandshakeCallback(PRFileDesc *sock, void *arg) -{ - struct Curl_easy *data = (struct Curl_easy *)arg; - struct connectdata *conn = data->conn; - unsigned int buflenmax = 50; - unsigned char buf[50]; - unsigned int buflen; - SSLNextProtoState state; - - if(!conn->bits.tls_enable_alpn) { - return; - } - - if(SSL_GetNextProto(sock, &state, buf, &buflen, buflenmax) == SECSuccess) { - - switch(state) { -#if NSSVERNUM >= 0x031a00 /* 3.26.0 */ - /* used by NSS internally to implement 0-RTT */ - case SSL_NEXT_PROTO_EARLY_VALUE: - /* fall through! */ -#endif - case SSL_NEXT_PROTO_NO_SUPPORT: - case SSL_NEXT_PROTO_NO_OVERLAP: - infof(data, VTLS_INFOF_NO_ALPN); - return; -#ifdef SSL_ENABLE_ALPN - case SSL_NEXT_PROTO_SELECTED: - infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, buflen, buf); - break; -#endif - default: - /* ignore SSL_NEXT_PROTO_NEGOTIATED */ - break; - } - -#ifdef USE_HTTP2 - if(buflen == ALPN_H2_LENGTH && - !memcmp(ALPN_H2, buf, ALPN_H2_LENGTH)) { - conn->alpn = CURL_HTTP_VERSION_2; - } - else -#endif - if(buflen == ALPN_HTTP_1_1_LENGTH && - !memcmp(ALPN_HTTP_1_1, buf, ALPN_HTTP_1_1_LENGTH)) { - conn->alpn = CURL_HTTP_VERSION_1_1; - } - - /* This callback might get called when PR_Recv() is used within - * close_one() during a connection shutdown. At that point there might not - * be any "bundle" associated with the connection anymore. - */ - if(conn->bundle) - Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); - } -} - -#if NSSVERNUM >= 0x030f04 /* 3.15.4 */ -static SECStatus CanFalseStartCallback(PRFileDesc *sock, void *client_data, - PRBool *canFalseStart) -{ - struct Curl_easy *data = (struct Curl_easy *)client_data; - - SSLChannelInfo channelInfo; - SSLCipherSuiteInfo cipherInfo; - - SECStatus rv; - PRBool negotiatedExtension; - - *canFalseStart = PR_FALSE; - - if(SSL_GetChannelInfo(sock, &channelInfo, sizeof(channelInfo)) != SECSuccess) - return SECFailure; - - if(SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo, - sizeof(cipherInfo)) != SECSuccess) - return SECFailure; - - /* Prevent version downgrade attacks from TLS 1.2, and avoid False Start for - * TLS 1.3 and later. See https://bugzilla.mozilla.org/show_bug.cgi?id=861310 - */ - if(channelInfo.protocolVersion != SSL_LIBRARY_VERSION_TLS_1_2) - goto end; - - /* Only allow ECDHE key exchange algorithm. - * See https://bugzilla.mozilla.org/show_bug.cgi?id=952863 */ - if(cipherInfo.keaType != ssl_kea_ecdh) - goto end; - - /* Prevent downgrade attacks on the symmetric cipher. We do not allow CBC - * mode due to BEAST, POODLE, and other attacks on the MAC-then-Encrypt - * design. See https://bugzilla.mozilla.org/show_bug.cgi?id=1109766 */ - if(cipherInfo.symCipher != ssl_calg_aes_gcm) - goto end; - - /* Enforce ALPN to do False Start, as an indicator of server - compatibility. */ - rv = SSL_HandshakeNegotiatedExtension(sock, ssl_app_layer_protocol_xtn, - &negotiatedExtension); - if(rv != SECSuccess || !negotiatedExtension) { - rv = SSL_HandshakeNegotiatedExtension(sock, ssl_next_proto_nego_xtn, - &negotiatedExtension); - } - - if(rv != SECSuccess || !negotiatedExtension) - goto end; - - *canFalseStart = PR_TRUE; - - infof(data, "Trying TLS False Start"); - -end: - return SECSuccess; -} -#endif - -static void display_cert_info(struct Curl_easy *data, - CERTCertificate *cert) -{ - char *subject, *issuer, *common_name; - PRExplodedTime printableTime; - char timeString[256]; - PRTime notBefore, notAfter; - - subject = CERT_NameToAscii(&cert->subject); - issuer = CERT_NameToAscii(&cert->issuer); - common_name = CERT_GetCommonName(&cert->subject); - infof(data, "subject: %s", subject); - - CERT_GetCertTimes(cert, ¬Before, ¬After); - PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime); - PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); - infof(data, " start date: %s", timeString); - PR_ExplodeTime(notAfter, PR_GMTParameters, &printableTime); - PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); - infof(data, " expire date: %s", timeString); - infof(data, " common name: %s", common_name); - infof(data, " issuer: %s", issuer); - - PR_Free(subject); - PR_Free(issuer); - PR_Free(common_name); -} - -/* A number of certs that will never occur in a real server handshake */ -#define TOO_MANY_CERTS 300 - -static CURLcode display_conn_info(struct Curl_easy *data, PRFileDesc *sock) -{ - CURLcode result = CURLE_OK; - SSLChannelInfo channel; - SSLCipherSuiteInfo suite; - CERTCertificate *cert; - CERTCertificate *cert2; - CERTCertificate *cert3; - PRTime now; - - if(SSL_GetChannelInfo(sock, &channel, sizeof(channel)) == - SECSuccess && channel.length == sizeof(channel) && - channel.cipherSuite) { - if(SSL_GetCipherSuiteInfo(channel.cipherSuite, - &suite, sizeof(suite)) == SECSuccess) { - infof(data, "SSL connection using %s", suite.cipherSuiteName); - } - } - - cert = SSL_PeerCertificate(sock); - if(cert) { - infof(data, "Server certificate:"); - - if(!data->set.ssl.certinfo) { - display_cert_info(data, cert); - CERT_DestroyCertificate(cert); - } - else { - /* Count certificates in chain. */ - int i = 1; - now = PR_Now(); - if(!cert->isRoot) { - cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA); - while(cert2) { - i++; - if(i >= TOO_MANY_CERTS) { - CERT_DestroyCertificate(cert2); - failf(data, "certificate loop"); - return CURLE_SSL_CERTPROBLEM; - } - if(cert2->isRoot) { - CERT_DestroyCertificate(cert2); - break; - } - cert3 = CERT_FindCertIssuer(cert2, now, certUsageSSLCA); - CERT_DestroyCertificate(cert2); - cert2 = cert3; - } - } - - result = Curl_ssl_init_certinfo(data, i); - if(!result) { - for(i = 0; cert; cert = cert2) { - result = Curl_extract_certinfo(data, i++, (char *)cert->derCert.data, - (char *)cert->derCert.data + - cert->derCert.len); - if(result) - break; - - if(cert->isRoot) { - CERT_DestroyCertificate(cert); - break; - } - - cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA); - CERT_DestroyCertificate(cert); - } - } - } - } - - return result; -} - -static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) -{ - struct Curl_easy *data = (struct Curl_easy *)arg; - struct connectdata *conn = data->conn; - PRErrorCode err = PR_GetError(); - CERTCertificate *cert; - - /* remember the cert verification result */ - SSL_SET_OPTION_LVALUE(certverifyresult) = err; - - if(err == SSL_ERROR_BAD_CERT_DOMAIN && !SSL_CONN_CONFIG(verifyhost)) - /* we are asked not to verify the host name */ - return SECSuccess; - - /* print only info about the cert, the error is printed off the callback */ - cert = SSL_PeerCertificate(sock); - if(cert) { - infof(data, "Server certificate:"); - display_cert_info(data, cert); - CERT_DestroyCertificate(cert); - } - - return SECFailure; -} - -/** - * - * Check that the Peer certificate's issuer certificate matches the one found - * by issuer_nickname. This is not exactly the way OpenSSL and GNU TLS do the - * issuer check, so we provide comments that mimic the OpenSSL - * X509_check_issued function (in x509v3/v3_purp.c) - */ -static SECStatus check_issuer_cert(PRFileDesc *sock, - char *issuer_nickname) -{ - CERTCertificate *cert, *cert_issuer, *issuer; - SECStatus res = SECSuccess; - void *proto_win = NULL; - - cert = SSL_PeerCertificate(sock); - cert_issuer = CERT_FindCertIssuer(cert, PR_Now(), certUsageObjectSigner); - - proto_win = SSL_RevealPinArg(sock); - issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win); - - if((!cert_issuer) || (!issuer)) - res = SECFailure; - else if(SECITEM_CompareItem(&cert_issuer->derCert, - &issuer->derCert) != SECEqual) - res = SECFailure; - - CERT_DestroyCertificate(cert); - CERT_DestroyCertificate(issuer); - CERT_DestroyCertificate(cert_issuer); - return res; -} - -static CURLcode cmp_peer_pubkey(struct ssl_connect_data *connssl, - const char *pinnedpubkey) -{ - CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - struct ssl_backend_data *backend = connssl->backend; - struct Curl_easy *data = NULL; - CERTCertificate *cert; - - DEBUGASSERT(backend); - data = backend->data; - - if(!pinnedpubkey) - /* no pinned public key specified */ - return CURLE_OK; - - /* get peer certificate */ - cert = SSL_PeerCertificate(backend->handle); - if(cert) { - /* extract public key from peer certificate */ - SECKEYPublicKey *pubkey = CERT_ExtractPublicKey(cert); - if(pubkey) { - /* encode the public key as DER */ - SECItem *cert_der = PK11_DEREncodePublicKey(pubkey); - if(cert_der) { - /* compare the public key with the pinned public key */ - result = Curl_pin_peer_pubkey(data, pinnedpubkey, cert_der->data, - cert_der->len); - SECITEM_FreeItem(cert_der, PR_TRUE); - } - SECKEY_DestroyPublicKey(pubkey); - } - CERT_DestroyCertificate(cert); - } - - /* report the resulting status */ - switch(result) { - case CURLE_OK: - infof(data, "pinned public key verified successfully"); - break; - case CURLE_SSL_PINNEDPUBKEYNOTMATCH: - failf(data, "failed to verify pinned public key"); - break; - default: - /* OOM, etc. */ - break; - } - - return result; -} - -/** - * - * Callback to pick the SSL client certificate. - */ -static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, - struct CERTDistNamesStr *caNames, - struct CERTCertificateStr **pRetCert, - struct SECKEYPrivateKeyStr **pRetKey) -{ - struct ssl_connect_data *connssl = (struct ssl_connect_data *)arg; - struct ssl_backend_data *backend = connssl->backend; - struct Curl_easy *data = NULL; - const char *nickname = NULL; - static const char pem_slotname[] = "PEM Token #1"; - - DEBUGASSERT(backend); - - data = backend->data; - nickname = backend->client_nickname; - - if(backend->obj_clicert) { - /* use the cert/key provided by PEM reader */ - SECItem cert_der = { 0, NULL, 0 }; - void *proto_win = SSL_RevealPinArg(sock); - struct CERTCertificateStr *cert; - struct SECKEYPrivateKeyStr *key; - - PK11SlotInfo *slot = nss_find_slot_by_name(pem_slotname); - if(!slot) { - failf(data, "NSS: PK11 slot not found: %s", pem_slotname); - return SECFailure; - } - - if(PK11_ReadRawAttribute(PK11_TypeGeneric, backend->obj_clicert, CKA_VALUE, - &cert_der) != SECSuccess) { - failf(data, "NSS: CKA_VALUE not found in PK11 generic object"); - PK11_FreeSlot(slot); - return SECFailure; - } - - cert = PK11_FindCertFromDERCertItem(slot, &cert_der, proto_win); - SECITEM_FreeItem(&cert_der, PR_FALSE); - if(!cert) { - failf(data, "NSS: client certificate from file not found"); - PK11_FreeSlot(slot); - return SECFailure; - } - - key = PK11_FindPrivateKeyFromCert(slot, cert, NULL); - PK11_FreeSlot(slot); - if(!key) { - failf(data, "NSS: private key from file not found"); - CERT_DestroyCertificate(cert); - return SECFailure; - } - - infof(data, "NSS: client certificate from file"); - display_cert_info(data, cert); - - *pRetCert = cert; - *pRetKey = key; - return SECSuccess; - } - - /* use the default NSS hook */ - if(SECSuccess != NSS_GetClientAuthData((void *)nickname, sock, caNames, - pRetCert, pRetKey) - || !*pRetCert) { - - if(!nickname) - failf(data, "NSS: client certificate not found (nickname not " - "specified)"); - else - failf(data, "NSS: client certificate not found: %s", nickname); - - return SECFailure; - } - - /* get certificate nickname if any */ - nickname = (*pRetCert)->nickname; - if(!nickname) - nickname = "[unknown]"; - - if(!strncmp(nickname, pem_slotname, sizeof(pem_slotname) - 1U)) { - failf(data, "NSS: refusing previously loaded certificate from file: %s", - nickname); - return SECFailure; - } - - if(!*pRetKey) { - failf(data, "NSS: private key not found for certificate: %s", nickname); - return SECFailure; - } - - infof(data, "NSS: using client certificate: %s", nickname); - display_cert_info(data, *pRetCert); - return SECSuccess; -} - -/* update blocking direction in case of PR_WOULD_BLOCK_ERROR */ -static void nss_update_connecting_state(ssl_connect_state state, void *secret) -{ - struct ssl_connect_data *connssl = (struct ssl_connect_data *)secret; - if(PR_GetError() != PR_WOULD_BLOCK_ERROR) - /* an unrelated error is passing by */ - return; - - switch(connssl->connecting_state) { - case ssl_connect_2: - case ssl_connect_2_reading: - case ssl_connect_2_writing: - break; - default: - /* we are not called from an SSL handshake */ - return; - } - - /* update the state accordingly */ - connssl->connecting_state = state; -} - -/* recv() wrapper we use to detect blocking direction during SSL handshake */ -static PRInt32 nspr_io_recv(PRFileDesc *fd, void *buf, PRInt32 amount, - PRIntn flags, PRIntervalTime timeout) -{ - const PRRecvFN recv_fn = fd->lower->methods->recv; - const PRInt32 rv = recv_fn(fd->lower, buf, amount, flags, timeout); - if(rv < 0) - /* check for PR_WOULD_BLOCK_ERROR and update blocking direction */ - nss_update_connecting_state(ssl_connect_2_reading, fd->secret); - return rv; -} - -/* send() wrapper we use to detect blocking direction during SSL handshake */ -static PRInt32 nspr_io_send(PRFileDesc *fd, const void *buf, PRInt32 amount, - PRIntn flags, PRIntervalTime timeout) -{ - const PRSendFN send_fn = fd->lower->methods->send; - const PRInt32 rv = send_fn(fd->lower, buf, amount, flags, timeout); - if(rv < 0) - /* check for PR_WOULD_BLOCK_ERROR and update blocking direction */ - nss_update_connecting_state(ssl_connect_2_writing, fd->secret); - return rv; -} - -/* close() wrapper to avoid assertion failure due to fd->secret != NULL */ -static PRStatus nspr_io_close(PRFileDesc *fd) -{ - const PRCloseFN close_fn = PR_GetDefaultIOMethods()->close; - fd->secret = NULL; - return close_fn(fd); -} - -/* load a PKCS #11 module */ -static CURLcode nss_load_module(SECMODModule **pmod, const char *library, - const char *name) -{ - char *config_string; - SECMODModule *module = *pmod; - if(module) - /* already loaded */ - return CURLE_OK; - - config_string = aprintf("library=%s name=%s", library, name); - if(!config_string) - return CURLE_OUT_OF_MEMORY; - - module = SECMOD_LoadUserModule(config_string, NULL, PR_FALSE); - free(config_string); - - if(module && module->loaded) { - /* loaded successfully */ - *pmod = module; - return CURLE_OK; - } - - if(module) - SECMOD_DestroyModule(module); - return CURLE_FAILED_INIT; -} - -/* unload a PKCS #11 module */ -static void nss_unload_module(SECMODModule **pmod) -{ - SECMODModule *module = *pmod; - if(!module) - /* not loaded */ - return; - - if(SECMOD_UnloadUserModule(module) != SECSuccess) - /* unload failed */ - return; - - SECMOD_DestroyModule(module); - *pmod = NULL; -} - -/* data might be NULL */ -static CURLcode nss_init_core(struct Curl_easy *data, const char *cert_dir) -{ - NSSInitParameters initparams; - PRErrorCode err; - const char *err_name; - - if(nss_context) - return CURLE_OK; - - memset((void *) &initparams, '\0', sizeof(initparams)); - initparams.length = sizeof(initparams); - - if(cert_dir) { - char *certpath = aprintf("sql:%s", cert_dir); - if(!certpath) - return CURLE_OUT_OF_MEMORY; - - infof(data, "Initializing NSS with certpath: %s", certpath); - nss_context = NSS_InitContext(certpath, "", "", "", &initparams, - NSS_INIT_READONLY | NSS_INIT_PK11RELOAD); - free(certpath); - - if(nss_context) - return CURLE_OK; - - err = PR_GetError(); - err_name = nss_error_to_name(err); - infof(data, "Unable to initialize NSS database: %d (%s)", err, err_name); - } - - infof(data, "Initializing NSS with certpath: none"); - nss_context = NSS_InitContext("", "", "", "", &initparams, NSS_INIT_READONLY - | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN - | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD); - if(nss_context) - return CURLE_OK; - - err = PR_GetError(); - err_name = nss_error_to_name(err); - failf(data, "Unable to initialize NSS: %d (%s)", err, err_name); - return CURLE_SSL_CACERT_BADFILE; -} - -/* data might be NULL */ -static CURLcode nss_setup(struct Curl_easy *data) -{ - char *cert_dir; - struct_stat st; - CURLcode result; - - if(initialized) - return CURLE_OK; - - /* list of all CRL items we need to destroy in nss_cleanup() */ - Curl_llist_init(&nss_crl_list, nss_destroy_crl_item); - - /* First we check if $SSL_DIR points to a valid dir */ - cert_dir = getenv("SSL_DIR"); - if(cert_dir) { - if((stat(cert_dir, &st) != 0) || - (!S_ISDIR(st.st_mode))) { - cert_dir = NULL; - } - } - - /* Now we check if the default location is a valid dir */ - if(!cert_dir) { - if((stat(SSL_DIR, &st) == 0) && - (S_ISDIR(st.st_mode))) { - cert_dir = (char *)SSL_DIR; - } - } - - if(nspr_io_identity == PR_INVALID_IO_LAYER) { - /* allocate an identity for our own NSPR I/O layer */ - nspr_io_identity = PR_GetUniqueIdentity("libcurl"); - if(nspr_io_identity == PR_INVALID_IO_LAYER) - return CURLE_OUT_OF_MEMORY; - - /* the default methods just call down to the lower I/O layer */ - memcpy(&nspr_io_methods, PR_GetDefaultIOMethods(), - sizeof(nspr_io_methods)); - - /* override certain methods in the table by our wrappers */ - nspr_io_methods.recv = nspr_io_recv; - nspr_io_methods.send = nspr_io_send; - nspr_io_methods.close = nspr_io_close; - } - - result = nss_init_core(data, cert_dir); - if(result) - return result; - - if(!any_cipher_enabled()) - NSS_SetDomesticPolicy(); - - initialized = 1; - - return CURLE_OK; -} - -/** - * Global SSL init - * - * @retval 0 error initializing SSL - * @retval 1 SSL initialized successfully - */ -static int nss_init(void) -{ - /* curl_global_init() is not thread-safe so this test is ok */ - if(!nss_initlock) { - PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); - nss_initlock = PR_NewLock(); - nss_crllock = PR_NewLock(); - nss_findslot_lock = PR_NewLock(); - nss_trustload_lock = PR_NewLock(); - } - - /* We will actually initialize NSS later */ - - return 1; -} - -/* data might be NULL */ -CURLcode Curl_nss_force_init(struct Curl_easy *data) -{ - CURLcode result; - if(!nss_initlock) { - if(data) - failf(data, "unable to initialize NSS, curl_global_init() should have " - "been called with CURL_GLOBAL_SSL or CURL_GLOBAL_ALL"); - return CURLE_FAILED_INIT; - } - - PR_Lock(nss_initlock); - result = nss_setup(data); - PR_Unlock(nss_initlock); - - return result; -} - -/* Global cleanup */ -static void nss_cleanup(void) -{ - /* This function isn't required to be threadsafe and this is only done - * as a safety feature. - */ - PR_Lock(nss_initlock); - if(initialized) { - /* Free references to client certificates held in the SSL session cache. - * Omitting this hampers destruction of the security module owning - * the certificates. */ - SSL_ClearSessionCache(); - - nss_unload_module(&pem_module); - nss_unload_module(&trust_module); - NSS_ShutdownContext(nss_context); - nss_context = NULL; - } - - /* destroy all CRL items */ - Curl_llist_destroy(&nss_crl_list, NULL); - - PR_Unlock(nss_initlock); - - PR_DestroyLock(nss_initlock); - PR_DestroyLock(nss_crllock); - PR_DestroyLock(nss_findslot_lock); - PR_DestroyLock(nss_trustload_lock); - nss_initlock = NULL; - - initialized = 0; -} - -/* - * This function uses SSL_peek to determine connection status. - * - * Return codes: - * 1 means the connection is still in place - * 0 means the connection has been closed - * -1 means the connection status is unknown - */ -static int nss_check_cxn(struct connectdata *conn) -{ - struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET]; - struct ssl_backend_data *backend = connssl->backend; - int rc; - char buf; - - DEBUGASSERT(backend); - - rc = - PR_Recv(backend->handle, (void *)&buf, 1, PR_MSG_PEEK, - PR_SecondsToInterval(1)); - if(rc > 0) - return 1; /* connection still in place */ - - if(rc == 0) - return 0; /* connection has been closed */ - - return -1; /* connection status unknown */ -} - -static void close_one(struct ssl_connect_data *connssl) -{ - /* before the cleanup, check whether we are using a client certificate */ - struct ssl_backend_data *backend = connssl->backend; - bool client_cert = true; - - DEBUGASSERT(backend); - - client_cert = (backend->client_nickname != NULL) - || (backend->obj_clicert != NULL); - - if(backend->handle) { - char buf[32]; - /* Maybe the server has already sent a close notify alert. - Read it to avoid an RST on the TCP connection. */ - (void)PR_Recv(backend->handle, buf, (int)sizeof(buf), 0, - PR_INTERVAL_NO_WAIT); - } - - free(backend->client_nickname); - backend->client_nickname = NULL; - - /* destroy all NSS objects in order to avoid failure of NSS shutdown */ - Curl_llist_destroy(&backend->obj_list, NULL); - backend->obj_clicert = NULL; - - if(backend->handle) { - if(client_cert) - /* A server might require different authentication based on the - * particular path being requested by the client. To support this - * scenario, we must ensure that a connection will never reuse the - * authentication data from a previous connection. */ - SSL_InvalidateSession(backend->handle); - - PR_Close(backend->handle); - backend->handle = NULL; - } -} - -/* - * This function is called when an SSL connection is closed. - */ -static void nss_close(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; -#ifndef CURL_DISABLE_PROXY - struct ssl_connect_data *connssl_proxy = &conn->proxy_ssl[sockindex]; -#endif - struct ssl_backend_data *backend = connssl->backend; - (void)data; - - DEBUGASSERT(backend); -#ifndef CURL_DISABLE_PROXY - DEBUGASSERT(connssl_proxy->backend != NULL); -#endif - - if(backend->handle -#ifndef CURL_DISABLE_PROXY - || connssl_proxy->backend->handle -#endif - ) { - /* NSS closes the socket we previously handed to it, so we must mark it - as closed to avoid double close */ - fake_sclose(conn->sock[sockindex]); - conn->sock[sockindex] = CURL_SOCKET_BAD; - } - -#ifndef CURL_DISABLE_PROXY - if(backend->handle) - /* nss_close(connssl) will transitively close also - connssl_proxy->backend->handle if both are used. Clear it to avoid - a double close leading to crash. */ - connssl_proxy->backend->handle = NULL; - - close_one(connssl_proxy); -#endif - close_one(connssl); -} - -/* return true if NSS can provide error code (and possibly msg) for the - error */ -static bool is_nss_error(CURLcode err) -{ - switch(err) { - case CURLE_PEER_FAILED_VERIFICATION: - case CURLE_SSL_CERTPROBLEM: - case CURLE_SSL_CONNECT_ERROR: - case CURLE_SSL_ISSUER_ERROR: - return true; - - default: - return false; - } -} - -/* return true if the given error code is related to a client certificate */ -static bool is_cc_error(PRInt32 err) -{ - switch(err) { - case SSL_ERROR_BAD_CERT_ALERT: - case SSL_ERROR_EXPIRED_CERT_ALERT: - case SSL_ERROR_REVOKED_CERT_ALERT: - return true; - - default: - return false; - } -} - -static Curl_recv nss_recv; -static Curl_send nss_send; - -static CURLcode nss_load_ca_certificates(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) -{ - const char *cafile = SSL_CONN_CONFIG(CAfile); - const char *capath = SSL_CONN_CONFIG(CApath); - bool use_trust_module; - CURLcode result = CURLE_OK; - - /* treat empty string as unset */ - if(cafile && !cafile[0]) - cafile = NULL; - if(capath && !capath[0]) - capath = NULL; - - infof(data, " CAfile: %s", cafile ? cafile : "none"); - infof(data, " CApath: %s", capath ? capath : "none"); - - /* load libnssckbi.so if no other trust roots were specified */ - use_trust_module = !cafile && !capath; - - PR_Lock(nss_trustload_lock); - if(use_trust_module && !trust_module) { - /* libnssckbi.so needed but not yet loaded --> load it! */ - result = nss_load_module(&trust_module, trust_library, "trust"); - infof(data, "%s %s", (result) ? "failed to load" : "loaded", - trust_library); - if(result == CURLE_FAILED_INIT) - /* If libnssckbi.so is not available (or fails to load), one can still - use CA certificates stored in NSS database. Ignore the failure. */ - result = CURLE_OK; - } - else if(!use_trust_module && trust_module) { - /* libnssckbi.so not needed but already loaded --> unload it! */ - infof(data, "unloading %s", trust_library); - nss_unload_module(&trust_module); - } - PR_Unlock(nss_trustload_lock); - - if(cafile) - result = nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE); - - if(result) - return result; - - if(capath) { - struct_stat st; - if(stat(capath, &st) == -1) - return CURLE_SSL_CACERT_BADFILE; - - if(S_ISDIR(st.st_mode)) { - PRDirEntry *entry; - PRDir *dir = PR_OpenDir(capath); - if(!dir) - return CURLE_SSL_CACERT_BADFILE; - - while((entry = - PR_ReadDir(dir, (PRDirFlags)(PR_SKIP_BOTH | PR_SKIP_HIDDEN)))) { - char *fullpath = aprintf("%s/%s", capath, entry->name); - if(!fullpath) { - PR_CloseDir(dir); - return CURLE_OUT_OF_MEMORY; - } - - if(CURLE_OK != nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE)) - /* This is purposefully tolerant of errors so non-PEM files can - * be in the same directory */ - infof(data, "failed to load '%s' from CURLOPT_CAPATH", fullpath); - - free(fullpath); - } - - PR_CloseDir(dir); - } - else - infof(data, "WARNING: CURLOPT_CAPATH not a directory (%s)", capath); - } - - return CURLE_OK; -} - -static CURLcode nss_sslver_from_curl(PRUint16 *nssver, long version) -{ - switch(version) { - case CURL_SSLVERSION_SSLv2: - *nssver = SSL_LIBRARY_VERSION_2; - return CURLE_OK; - - case CURL_SSLVERSION_SSLv3: - return CURLE_NOT_BUILT_IN; - - case CURL_SSLVERSION_TLSv1_0: - *nssver = SSL_LIBRARY_VERSION_TLS_1_0; - return CURLE_OK; - - case CURL_SSLVERSION_TLSv1_1: -#ifdef SSL_LIBRARY_VERSION_TLS_1_1 - *nssver = SSL_LIBRARY_VERSION_TLS_1_1; - return CURLE_OK; -#else - return CURLE_SSL_CONNECT_ERROR; -#endif - - case CURL_SSLVERSION_TLSv1_2: -#ifdef SSL_LIBRARY_VERSION_TLS_1_2 - *nssver = SSL_LIBRARY_VERSION_TLS_1_2; - return CURLE_OK; -#else - return CURLE_SSL_CONNECT_ERROR; -#endif - - case CURL_SSLVERSION_TLSv1_3: -#ifdef SSL_LIBRARY_VERSION_TLS_1_3 - *nssver = SSL_LIBRARY_VERSION_TLS_1_3; - return CURLE_OK; -#else - return CURLE_SSL_CONNECT_ERROR; -#endif - - default: - return CURLE_SSL_CONNECT_ERROR; - } -} - -static CURLcode nss_init_sslver(SSLVersionRange *sslver, - struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result; - const long min = SSL_CONN_CONFIG(version); - const long max = SSL_CONN_CONFIG(version_max); - SSLVersionRange vrange; - - switch(min) { - case CURL_SSLVERSION_TLSv1: - case CURL_SSLVERSION_DEFAULT: - /* Bump our minimum TLS version if NSS has stricter requirements. */ - if(SSL_VersionRangeGetDefault(ssl_variant_stream, &vrange) != SECSuccess) - return CURLE_SSL_CONNECT_ERROR; - if(sslver->min < vrange.min) - sslver->min = vrange.min; - break; - default: - result = nss_sslver_from_curl(&sslver->min, min); - if(result) { - failf(data, "unsupported min version passed via CURLOPT_SSLVERSION"); - return result; - } - } - - switch(max) { - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_DEFAULT: - break; - default: - result = nss_sslver_from_curl(&sslver->max, max >> 16); - if(result) { - failf(data, "unsupported max version passed via CURLOPT_SSLVERSION"); - return result; - } - } - - return CURLE_OK; -} - -static CURLcode nss_fail_connect(struct ssl_connect_data *connssl, - struct Curl_easy *data, - CURLcode curlerr) -{ - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - if(is_nss_error(curlerr)) { - /* read NSPR error code */ - PRErrorCode err = PR_GetError(); - if(is_cc_error(err)) - curlerr = CURLE_SSL_CERTPROBLEM; - - /* print the error number and error string */ - infof(data, "NSS error %d (%s)", err, nss_error_to_name(err)); - - /* print a human-readable message describing the error if available */ - nss_print_error_message(data, err); - } - - /* cleanup on connection failure */ - Curl_llist_destroy(&backend->obj_list, NULL); - - return curlerr; -} - -/* Switch the SSL socket into blocking or non-blocking mode. */ -static CURLcode nss_set_blocking(struct ssl_connect_data *connssl, - struct Curl_easy *data, - bool blocking) -{ - PRSocketOptionData sock_opt; - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - sock_opt.option = PR_SockOpt_Nonblocking; - sock_opt.value.non_blocking = !blocking; - - if(PR_SetSocketOption(backend->handle, &sock_opt) != PR_SUCCESS) - return nss_fail_connect(connssl, data, CURLE_SSL_CONNECT_ERROR); - - return CURLE_OK; -} - -static CURLcode nss_setup_connect(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - PRFileDesc *model = NULL; - PRFileDesc *nspr_io = NULL; - PRFileDesc *nspr_io_stub = NULL; - PRBool ssl_no_cache; - PRBool ssl_cbc_random_iv; - curl_socket_t sockfd = conn->sock[sockindex]; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - CURLcode result; - bool second_layer = FALSE; - SSLVersionRange sslver_supported; - SSLVersionRange sslver = { - SSL_LIBRARY_VERSION_TLS_1_0, /* min */ -#ifdef SSL_LIBRARY_VERSION_TLS_1_3 - SSL_LIBRARY_VERSION_TLS_1_3 /* max */ -#elif defined SSL_LIBRARY_VERSION_TLS_1_2 - SSL_LIBRARY_VERSION_TLS_1_2 -#elif defined SSL_LIBRARY_VERSION_TLS_1_1 - SSL_LIBRARY_VERSION_TLS_1_1 -#else - SSL_LIBRARY_VERSION_TLS_1_0 -#endif - }; - char *snihost = Curl_ssl_snihost(data, SSL_HOST_NAME(), NULL); - if(!snihost) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - - DEBUGASSERT(backend); - - backend->data = data; - - /* list of all NSS objects we need to destroy in nss_do_close() */ - Curl_llist_init(&backend->obj_list, nss_destroy_object); - - PR_Lock(nss_initlock); - result = nss_setup(data); - if(result) { - PR_Unlock(nss_initlock); - goto error; - } - - PK11_SetPasswordFunc(nss_get_password); - - result = nss_load_module(&pem_module, pem_library, "PEM"); - PR_Unlock(nss_initlock); - if(result == CURLE_FAILED_INIT) - infof(data, "WARNING: failed to load NSS PEM library %s. Using " - "OpenSSL PEM certificates will not work.", pem_library); - else if(result) - goto error; - - result = CURLE_SSL_CONNECT_ERROR; - - model = PR_NewTCPSocket(); - if(!model) - goto error; - model = SSL_ImportFD(NULL, model); - - if(SSL_OptionSet(model, SSL_SECURITY, PR_TRUE) != SECSuccess) - goto error; - if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess) - goto error; - if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess) - goto error; - - /* do not use SSL cache if disabled or we are not going to verify peer */ - ssl_no_cache = (SSL_SET_OPTION(primary.sessionid) - && SSL_CONN_CONFIG(verifypeer)) ? PR_FALSE : PR_TRUE; - if(SSL_OptionSet(model, SSL_NO_CACHE, ssl_no_cache) != SECSuccess) - goto error; - - /* enable/disable the requested SSL version(s) */ - if(nss_init_sslver(&sslver, data, conn) != CURLE_OK) - goto error; - if(SSL_VersionRangeGetSupported(ssl_variant_stream, - &sslver_supported) != SECSuccess) - goto error; - if(sslver_supported.max < sslver.max && sslver_supported.max >= sslver.min) { - char *sslver_req_str, *sslver_supp_str; - sslver_req_str = nss_sslver_to_name(sslver.max); - sslver_supp_str = nss_sslver_to_name(sslver_supported.max); - if(sslver_req_str && sslver_supp_str) - infof(data, "Falling back from %s to max supported SSL version (%s)", - sslver_req_str, sslver_supp_str); - free(sslver_req_str); - free(sslver_supp_str); - sslver.max = sslver_supported.max; - } - if(SSL_VersionRangeSet(model, &sslver) != SECSuccess) - goto error; - - ssl_cbc_random_iv = !SSL_SET_OPTION(enable_beast); -#ifdef SSL_CBC_RANDOM_IV - /* unless the user explicitly asks to allow the protocol vulnerability, we - use the work-around */ - if(SSL_OptionSet(model, SSL_CBC_RANDOM_IV, ssl_cbc_random_iv) != SECSuccess) - infof(data, "WARNING: failed to set SSL_CBC_RANDOM_IV = %d", - ssl_cbc_random_iv); -#else - if(ssl_cbc_random_iv) - infof(data, "WARNING: support for SSL_CBC_RANDOM_IV not compiled in"); -#endif - - if(SSL_CONN_CONFIG(cipher_list)) { - if(set_ciphers(data, model, SSL_CONN_CONFIG(cipher_list)) != SECSuccess) { - result = CURLE_SSL_CIPHER; - goto error; - } - } - - if(!SSL_CONN_CONFIG(verifypeer) && SSL_CONN_CONFIG(verifyhost)) - infof(data, "WARNING: ignoring value of ssl.verifyhost"); - - /* bypass the default SSL_AuthCertificate() hook in case we do not want to - * verify peer */ - if(SSL_AuthCertificateHook(model, nss_auth_cert_hook, data) != SECSuccess) - goto error; - - /* not checked yet */ - SSL_SET_OPTION_LVALUE(certverifyresult) = 0; - - if(SSL_BadCertHook(model, BadCertHandler, data) != SECSuccess) - goto error; - - if(SSL_HandshakeCallback(model, HandshakeCallback, data) != SECSuccess) - goto error; - - { - const CURLcode rv = nss_load_ca_certificates(data, conn, sockindex); - if((rv == CURLE_SSL_CACERT_BADFILE) && !SSL_CONN_CONFIG(verifypeer)) - /* not a fatal error because we are not going to verify the peer */ - infof(data, "WARNING: CA certificates failed to load"); - else if(rv) { - result = rv; - goto error; - } - } - - if(SSL_SET_OPTION(primary.CRLfile)) { - const CURLcode rv = nss_load_crl(SSL_SET_OPTION(primary.CRLfile)); - if(rv) { - result = rv; - goto error; - } - infof(data, " CRLfile: %s", SSL_SET_OPTION(primary.CRLfile)); - } - - if(SSL_SET_OPTION(primary.clientcert)) { - char *nickname = dup_nickname(data, SSL_SET_OPTION(primary.clientcert)); - if(nickname) { - /* we are not going to use libnsspem.so to read the client cert */ - backend->obj_clicert = NULL; - } - else { - CURLcode rv = cert_stuff(data, conn, sockindex, - SSL_SET_OPTION(primary.clientcert), - SSL_SET_OPTION(key)); - if(rv) { - /* failf() is already done in cert_stuff() */ - result = rv; - goto error; - } - } - - /* store the nickname for SelectClientCert() called during handshake */ - backend->client_nickname = nickname; - } - else - backend->client_nickname = NULL; - - if(SSL_GetClientAuthDataHook(model, SelectClientCert, - (void *)connssl) != SECSuccess) { - result = CURLE_SSL_CERTPROBLEM; - goto error; - } - -#ifndef CURL_DISABLE_PROXY - if(conn->proxy_ssl[sockindex].use) { - struct ssl_backend_data *proxy_backend; - proxy_backend = conn->proxy_ssl[sockindex].backend; - DEBUGASSERT(ssl_connection_complete == conn->proxy_ssl[sockindex].state); - DEBUGASSERT(proxy_backend); - DEBUGASSERT(proxy_backend->handle); - nspr_io = proxy_backend->handle; - second_layer = TRUE; - } -#endif - else { - /* wrap OS file descriptor by NSPR's file descriptor abstraction */ - nspr_io = PR_ImportTCPSocket(sockfd); - if(!nspr_io) - goto error; - } - - /* create our own NSPR I/O layer */ - nspr_io_stub = PR_CreateIOLayerStub(nspr_io_identity, &nspr_io_methods); - if(!nspr_io_stub) { - if(!second_layer) - PR_Close(nspr_io); - goto error; - } - - /* make the per-connection data accessible from NSPR I/O callbacks */ - nspr_io_stub->secret = (void *)connssl; - - /* push our new layer to the NSPR I/O stack */ - if(PR_PushIOLayer(nspr_io, PR_TOP_IO_LAYER, nspr_io_stub) != PR_SUCCESS) { - if(!second_layer) - PR_Close(nspr_io); - PR_Close(nspr_io_stub); - goto error; - } - - /* import our model socket onto the current I/O stack */ - backend->handle = SSL_ImportFD(model, nspr_io); - if(!backend->handle) { - if(!second_layer) - PR_Close(nspr_io); - goto error; - } - - PR_Close(model); /* We don't need this any more */ - model = NULL; - - /* This is the password associated with the cert that we're using */ - if(SSL_SET_OPTION(key_passwd)) { - SSL_SetPKCS11PinArg(backend->handle, SSL_SET_OPTION(key_passwd)); - } - -#ifdef SSL_ENABLE_OCSP_STAPLING - if(SSL_CONN_CONFIG(verifystatus)) { - if(SSL_OptionSet(backend->handle, SSL_ENABLE_OCSP_STAPLING, PR_TRUE) - != SECSuccess) - goto error; - } -#endif - -#ifdef SSL_ENABLE_ALPN - if(SSL_OptionSet(backend->handle, SSL_ENABLE_ALPN, conn->bits.tls_enable_alpn - ? PR_TRUE : PR_FALSE) != SECSuccess) - goto error; -#endif - -#if NSSVERNUM >= 0x030f04 /* 3.15.4 */ - if(data->set.ssl.falsestart) { - if(SSL_OptionSet(backend->handle, SSL_ENABLE_FALSE_START, PR_TRUE) - != SECSuccess) - goto error; - - if(SSL_SetCanFalseStartCallback(backend->handle, CanFalseStartCallback, - data) != SECSuccess) - goto error; - } -#endif - -#if defined(SSL_ENABLE_ALPN) - if(conn->bits.tls_enable_alpn) { - int cur = 0; - unsigned char protocols[128]; - -#ifdef USE_HTTP2 - if(data->state.httpwant >= CURL_HTTP_VERSION_2 -#ifndef CURL_DISABLE_PROXY - && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy) -#endif - ) { - protocols[cur++] = ALPN_H2_LENGTH; - memcpy(&protocols[cur], ALPN_H2, ALPN_H2_LENGTH); - cur += ALPN_H2_LENGTH; - } -#endif - protocols[cur++] = ALPN_HTTP_1_1_LENGTH; - memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH); - cur += ALPN_HTTP_1_1_LENGTH; - - if(SSL_SetNextProtoNego(backend->handle, protocols, cur) != SECSuccess) - goto error; - } -#endif - - - /* Force handshake on next I/O */ - if(SSL_ResetHandshake(backend->handle, /* asServer */ PR_FALSE) - != SECSuccess) - goto error; - - /* propagate hostname to the TLS layer */ - if(SSL_SetURL(backend->handle, snihost) != SECSuccess) - goto error; - - /* prevent NSS from re-using the session for a different hostname */ - if(SSL_SetSockPeerID(backend->handle, snihost) != SECSuccess) - goto error; - - return CURLE_OK; - -error: - if(model) - PR_Close(model); - - return nss_fail_connect(connssl, data, result); -} - -static CURLcode nss_do_connect(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - CURLcode result = CURLE_SSL_CONNECT_ERROR; - PRUint32 timeout; - - /* check timeout situation */ - const timediff_t time_left = Curl_timeleft(data, NULL, TRUE); - if(time_left < 0) { - failf(data, "timed out before SSL handshake"); - result = CURLE_OPERATION_TIMEDOUT; - goto error; - } - - DEBUGASSERT(backend); - - /* Force the handshake now */ - timeout = PR_MillisecondsToInterval((PRUint32) time_left); - if(SSL_ForceHandshakeWithTimeout(backend->handle, timeout) != SECSuccess) { - if(PR_GetError() == PR_WOULD_BLOCK_ERROR) - /* blocking direction is updated by nss_update_connecting_state() */ - return CURLE_AGAIN; - else if(SSL_SET_OPTION(certverifyresult) == SSL_ERROR_BAD_CERT_DOMAIN) - result = CURLE_PEER_FAILED_VERIFICATION; - else if(SSL_SET_OPTION(certverifyresult) != 0) - result = CURLE_PEER_FAILED_VERIFICATION; - goto error; - } - - result = display_conn_info(data, backend->handle); - if(result) - goto error; - - if(SSL_CONN_CONFIG(issuercert)) { - SECStatus ret = SECFailure; - char *nickname = dup_nickname(data, SSL_CONN_CONFIG(issuercert)); - if(nickname) { - /* we support only nicknames in case of issuercert for now */ - ret = check_issuer_cert(backend->handle, nickname); - free(nickname); - } - - if(SECFailure == ret) { - infof(data, "SSL certificate issuer check failed"); - result = CURLE_SSL_ISSUER_ERROR; - goto error; - } - else { - infof(data, "SSL certificate issuer check ok"); - } - } - - result = cmp_peer_pubkey(connssl, SSL_PINNED_PUB_KEY()); - if(result) - /* status already printed */ - goto error; - - return CURLE_OK; - -error: - return nss_fail_connect(connssl, data, result); -} - -static CURLcode nss_connect_common(struct Curl_easy *data, - struct connectdata *conn, int sockindex, - bool *done) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - const bool blocking = (done == NULL); - CURLcode result; - - if(connssl->state == ssl_connection_complete) { - if(!blocking) - *done = TRUE; - return CURLE_OK; - } - - if(connssl->connecting_state == ssl_connect_1) { - result = nss_setup_connect(data, conn, sockindex); - if(result) - /* we do not expect CURLE_AGAIN from nss_setup_connect() */ - return result; - - connssl->connecting_state = ssl_connect_2; - } - - /* enable/disable blocking mode before handshake */ - result = nss_set_blocking(connssl, data, blocking); - if(result) - return result; - - result = nss_do_connect(data, conn, sockindex); - switch(result) { - case CURLE_OK: - break; - case CURLE_AGAIN: - /* CURLE_AGAIN in non-blocking mode is not an error */ - if(!blocking) - return CURLE_OK; - else - return result; - default: - return result; - } - - if(blocking) { - /* in blocking mode, set NSS non-blocking mode _after_ SSL handshake */ - result = nss_set_blocking(connssl, data, /* blocking */ FALSE); - if(result) - return result; - } - else - /* signal completed SSL handshake */ - *done = TRUE; - - connssl->state = ssl_connection_complete; - conn->recv[sockindex] = nss_recv; - conn->send[sockindex] = nss_send; - - /* ssl_connect_done is never used outside, go back to the initial state */ - connssl->connecting_state = ssl_connect_1; - - return CURLE_OK; -} - -static CURLcode nss_connect(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - return nss_connect_common(data, conn, sockindex, /* blocking */ NULL); -} - -static CURLcode nss_connect_nonblocking(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, bool *done) -{ - return nss_connect_common(data, conn, sockindex, done); -} - -static ssize_t nss_send(struct Curl_easy *data, /* transfer */ - int sockindex, /* socketindex */ - const void *mem, /* send this data */ - size_t len, /* amount to write */ - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - ssize_t rc; - - DEBUGASSERT(backend); - - /* The SelectClientCert() hook uses this for infof() and failf() but the - handle stored in nss_setup_connect() could have already been freed. */ - backend->data = data; - - rc = PR_Send(backend->handle, mem, (int)len, 0, PR_INTERVAL_NO_WAIT); - if(rc < 0) { - PRInt32 err = PR_GetError(); - if(err == PR_WOULD_BLOCK_ERROR) - *curlcode = CURLE_AGAIN; - else { - /* print the error number and error string */ - const char *err_name = nss_error_to_name(err); - infof(data, "SSL write: error %d (%s)", err, err_name); - - /* print a human-readable message describing the error if available */ - nss_print_error_message(data, err); - - *curlcode = (is_cc_error(err)) - ? CURLE_SSL_CERTPROBLEM - : CURLE_SEND_ERROR; - } - - return -1; - } - - return rc; /* number of bytes */ -} - -static ssize_t nss_recv(struct Curl_easy *data, /* transfer */ - int sockindex, /* socketindex */ - char *buf, /* store read data here */ - size_t buffersize, /* max amount to read */ - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - ssize_t nread; - - DEBUGASSERT(backend); - - /* The SelectClientCert() hook uses this for infof() and failf() but the - handle stored in nss_setup_connect() could have already been freed. */ - backend->data = data; - - nread = PR_Recv(backend->handle, buf, (int)buffersize, 0, - PR_INTERVAL_NO_WAIT); - if(nread < 0) { - /* failed SSL read */ - PRInt32 err = PR_GetError(); - - if(err == PR_WOULD_BLOCK_ERROR) - *curlcode = CURLE_AGAIN; - else { - /* print the error number and error string */ - const char *err_name = nss_error_to_name(err); - infof(data, "SSL read: errno %d (%s)", err, err_name); - - /* print a human-readable message describing the error if available */ - nss_print_error_message(data, err); - - *curlcode = (is_cc_error(err)) - ? CURLE_SSL_CERTPROBLEM - : CURLE_RECV_ERROR; - } - - return -1; - } - - return nread; -} - -static size_t nss_version(char *buffer, size_t size) -{ - return msnprintf(buffer, size, "NSS/%s", NSS_GetVersion()); -} - -/* data might be NULL */ -static int Curl_nss_seed(struct Curl_easy *data) -{ - /* make sure that NSS is initialized */ - return !!Curl_nss_force_init(data); -} - -/* data might be NULL */ -static CURLcode nss_random(struct Curl_easy *data, - unsigned char *entropy, - size_t length) -{ - Curl_nss_seed(data); /* Initiate the seed if not already done */ - - if(SECSuccess != PK11_GenerateRandom(entropy, curlx_uztosi(length))) - /* signal a failure */ - return CURLE_FAILED_INIT; - - return CURLE_OK; -} - -static CURLcode nss_sha256sum(const unsigned char *tmp, /* input */ - size_t tmplen, - unsigned char *sha256sum, /* output */ - size_t sha256len) -{ - PK11Context *SHA256pw = PK11_CreateDigestContext(SEC_OID_SHA256); - unsigned int SHA256out; - - if(!SHA256pw) - return CURLE_NOT_BUILT_IN; - - PK11_DigestOp(SHA256pw, tmp, curlx_uztoui(tmplen)); - PK11_DigestFinal(SHA256pw, sha256sum, &SHA256out, curlx_uztoui(sha256len)); - PK11_DestroyContext(SHA256pw, PR_TRUE); - - return CURLE_OK; -} - -static bool nss_cert_status_request(void) -{ -#ifdef SSL_ENABLE_OCSP_STAPLING - return TRUE; -#else - return FALSE; -#endif -} - -static bool nss_false_start(void) -{ -#if NSSVERNUM >= 0x030f04 /* 3.15.4 */ - return TRUE; -#else - return FALSE; -#endif -} - -static void *nss_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - struct ssl_backend_data *backend = connssl->backend; - (void)info; - DEBUGASSERT(backend); - return backend->handle; -} - -const struct Curl_ssl Curl_ssl_nss = { - { CURLSSLBACKEND_NSS, "nss" }, /* info */ - - SSLSUPP_CA_PATH | - SSLSUPP_CERTINFO | - SSLSUPP_PINNEDPUBKEY | - SSLSUPP_HTTPS_PROXY, - - sizeof(struct ssl_backend_data), - - nss_init, /* init */ - nss_cleanup, /* cleanup */ - nss_version, /* version */ - nss_check_cxn, /* check_cxn */ - /* NSS has no shutdown function provided and thus always fail */ - Curl_none_shutdown, /* shutdown */ - Curl_none_data_pending, /* data_pending */ - nss_random, /* random */ - nss_cert_status_request, /* cert_status_request */ - nss_connect, /* connect */ - nss_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_getsock, /* getsock */ - nss_get_internals, /* get_internals */ - nss_close, /* close_one */ - Curl_none_close_all, /* close_all */ - /* NSS has its own session ID cache */ - Curl_none_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - nss_false_start, /* false_start */ - nss_sha256sum, /* sha256sum */ - NULL, /* associate_connection */ - NULL /* disassociate_connection */ -}; - -#endif /* USE_NSS */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+/*
+ * Source file for all NSS-specific code for the TLS/SSL layer. No code
+ * but vtls.c should ever call or use these functions.
+ */
+
+#include "curl_setup.h"
+
+#ifdef USE_NSS
+
+#include "urldata.h"
+#include "sendf.h"
+#include "formdata.h" /* for the boundary function */
+#include "url.h" /* for the ssl config check function */
+#include "connect.h"
+#include "strcase.h"
+#include "select.h"
+#include "vtls.h"
+#include "vtls_int.h"
+#include "llist.h"
+#include "multiif.h"
+#include "curl_printf.h"
+#include "nssg.h"
+#include <nspr.h>
+#include <nss.h>
+#include <ssl.h>
+#include <sslerr.h>
+#include <secerr.h>
+#include <secmod.h>
+#include <sslproto.h>
+#include <prtypes.h>
+#include <pk11pub.h>
+#include <prio.h>
+#include <secitem.h>
+#include <secport.h>
+#include <certdb.h>
+#include <base64.h>
+#include <cert.h>
+#include <prerror.h>
+#include <keyhi.h> /* for SECKEY_DestroyPublicKey() */
+#include <private/pprio.h> /* for PR_ImportTCPSocket */
+
+#define NSSVERNUM ((NSS_VMAJOR<<16)|(NSS_VMINOR<<8)|NSS_VPATCH)
+
+#if NSSVERNUM >= 0x030f00 /* 3.15.0 */
+#include <ocsp.h>
+#endif
+
+#include "warnless.h"
+#include "x509asn1.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define SSL_DIR "/etc/pki/nssdb"
+
+/* enough to fit the string "PEM Token #[0|1]" */
+#define SLOTSIZE 13
+
+struct ssl_backend_data {
+ PRFileDesc *handle;
+ char *client_nickname;
+ struct Curl_easy *data;
+ struct Curl_llist obj_list;
+ PK11GenericObject *obj_clicert;
+};
+
+static PRLock *nss_initlock = NULL;
+static PRLock *nss_crllock = NULL;
+static PRLock *nss_findslot_lock = NULL;
+static PRLock *nss_trustload_lock = NULL;
+static struct Curl_llist nss_crl_list;
+static NSSInitContext *nss_context = NULL;
+static volatile int initialized = 0;
+
+/* type used to wrap pointers as list nodes */
+struct ptr_list_wrap {
+ void *ptr;
+ struct Curl_llist_element node;
+};
+
+struct cipher_s {
+ const char *name;
+ int num;
+};
+
+#define PK11_SETATTRS(_attr, _idx, _type, _val, _len) do { \
+ CK_ATTRIBUTE *ptr = (_attr) + ((_idx)++); \
+ ptr->type = (_type); \
+ ptr->pValue = (_val); \
+ ptr->ulValueLen = (_len); \
+} while(0)
+
+#define CERT_NewTempCertificate __CERT_NewTempCertificate
+
+#define NUM_OF_CIPHERS sizeof(cipherlist)/sizeof(cipherlist[0])
+static const struct cipher_s cipherlist[] = {
+ /* SSL2 cipher suites */
+ {"rc4", SSL_EN_RC4_128_WITH_MD5},
+ {"rc4-md5", SSL_EN_RC4_128_WITH_MD5},
+ {"rc4export", SSL_EN_RC4_128_EXPORT40_WITH_MD5},
+ {"rc2", SSL_EN_RC2_128_CBC_WITH_MD5},
+ {"rc2export", SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5},
+ {"des", SSL_EN_DES_64_CBC_WITH_MD5},
+ {"desede3", SSL_EN_DES_192_EDE3_CBC_WITH_MD5},
+ /* SSL3/TLS cipher suites */
+ {"rsa_rc4_128_md5", SSL_RSA_WITH_RC4_128_MD5},
+ {"rsa_rc4_128_sha", SSL_RSA_WITH_RC4_128_SHA},
+ {"rsa_3des_sha", SSL_RSA_WITH_3DES_EDE_CBC_SHA},
+ {"rsa_des_sha", SSL_RSA_WITH_DES_CBC_SHA},
+ {"rsa_rc4_40_md5", SSL_RSA_EXPORT_WITH_RC4_40_MD5},
+ {"rsa_rc2_40_md5", SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5},
+ {"rsa_null_md5", SSL_RSA_WITH_NULL_MD5},
+ {"rsa_null_sha", SSL_RSA_WITH_NULL_SHA},
+ {"fips_3des_sha", SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA},
+ {"fips_des_sha", SSL_RSA_FIPS_WITH_DES_CBC_SHA},
+ {"fortezza", SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA},
+ {"fortezza_rc4_128_sha", SSL_FORTEZZA_DMS_WITH_RC4_128_SHA},
+ {"fortezza_null", SSL_FORTEZZA_DMS_WITH_NULL_SHA},
+ {"dhe_rsa_3des_sha", SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA},
+ {"dhe_dss_3des_sha", SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA},
+ {"dhe_rsa_des_sha", SSL_DHE_RSA_WITH_DES_CBC_SHA},
+ {"dhe_dss_des_sha", SSL_DHE_DSS_WITH_DES_CBC_SHA},
+ /* TLS 1.0: Exportable 56-bit Cipher Suites. */
+ {"rsa_des_56_sha", TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA},
+ {"rsa_rc4_56_sha", TLS_RSA_EXPORT1024_WITH_RC4_56_SHA},
+ /* Ephemeral DH with RC4 bulk encryption */
+ {"dhe_dss_rc4_128_sha", TLS_DHE_DSS_WITH_RC4_128_SHA},
+ /* AES ciphers. */
+ {"dhe_dss_aes_128_cbc_sha", TLS_DHE_DSS_WITH_AES_128_CBC_SHA},
+ {"dhe_dss_aes_256_cbc_sha", TLS_DHE_DSS_WITH_AES_256_CBC_SHA},
+ {"dhe_rsa_aes_128_cbc_sha", TLS_DHE_RSA_WITH_AES_128_CBC_SHA},
+ {"dhe_rsa_aes_256_cbc_sha", TLS_DHE_RSA_WITH_AES_256_CBC_SHA},
+ {"rsa_aes_128_sha", TLS_RSA_WITH_AES_128_CBC_SHA},
+ {"rsa_aes_256_sha", TLS_RSA_WITH_AES_256_CBC_SHA},
+ /* ECC ciphers. */
+ {"ecdh_ecdsa_null_sha", TLS_ECDH_ECDSA_WITH_NULL_SHA},
+ {"ecdh_ecdsa_rc4_128_sha", TLS_ECDH_ECDSA_WITH_RC4_128_SHA},
+ {"ecdh_ecdsa_3des_sha", TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA},
+ {"ecdh_ecdsa_aes_128_sha", TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA},
+ {"ecdh_ecdsa_aes_256_sha", TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA},
+ {"ecdhe_ecdsa_null_sha", TLS_ECDHE_ECDSA_WITH_NULL_SHA},
+ {"ecdhe_ecdsa_rc4_128_sha", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
+ {"ecdhe_ecdsa_3des_sha", TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA},
+ {"ecdhe_ecdsa_aes_128_sha", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
+ {"ecdhe_ecdsa_aes_256_sha", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
+ {"ecdh_rsa_null_sha", TLS_ECDH_RSA_WITH_NULL_SHA},
+ {"ecdh_rsa_128_sha", TLS_ECDH_RSA_WITH_RC4_128_SHA},
+ {"ecdh_rsa_3des_sha", TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA},
+ {"ecdh_rsa_aes_128_sha", TLS_ECDH_RSA_WITH_AES_128_CBC_SHA},
+ {"ecdh_rsa_aes_256_sha", TLS_ECDH_RSA_WITH_AES_256_CBC_SHA},
+ {"ecdhe_rsa_null", TLS_ECDHE_RSA_WITH_NULL_SHA},
+ {"ecdhe_rsa_rc4_128_sha", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
+ {"ecdhe_rsa_3des_sha", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA},
+ {"ecdhe_rsa_aes_128_sha", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
+ {"ecdhe_rsa_aes_256_sha", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
+ {"ecdh_anon_null_sha", TLS_ECDH_anon_WITH_NULL_SHA},
+ {"ecdh_anon_rc4_128sha", TLS_ECDH_anon_WITH_RC4_128_SHA},
+ {"ecdh_anon_3des_sha", TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA},
+ {"ecdh_anon_aes_128_sha", TLS_ECDH_anon_WITH_AES_128_CBC_SHA},
+ {"ecdh_anon_aes_256_sha", TLS_ECDH_anon_WITH_AES_256_CBC_SHA},
+#ifdef TLS_RSA_WITH_NULL_SHA256
+ /* new HMAC-SHA256 cipher suites specified in RFC */
+ {"rsa_null_sha_256", TLS_RSA_WITH_NULL_SHA256},
+ {"rsa_aes_128_cbc_sha_256", TLS_RSA_WITH_AES_128_CBC_SHA256},
+ {"rsa_aes_256_cbc_sha_256", TLS_RSA_WITH_AES_256_CBC_SHA256},
+ {"dhe_rsa_aes_128_cbc_sha_256", TLS_DHE_RSA_WITH_AES_128_CBC_SHA256},
+ {"dhe_rsa_aes_256_cbc_sha_256", TLS_DHE_RSA_WITH_AES_256_CBC_SHA256},
+ {"ecdhe_ecdsa_aes_128_cbc_sha_256", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256},
+ {"ecdhe_rsa_aes_128_cbc_sha_256", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256},
+#endif
+#ifdef TLS_RSA_WITH_AES_128_GCM_SHA256
+ /* AES GCM cipher suites in RFC 5288 and RFC 5289 */
+ {"rsa_aes_128_gcm_sha_256", TLS_RSA_WITH_AES_128_GCM_SHA256},
+ {"dhe_rsa_aes_128_gcm_sha_256", TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},
+ {"dhe_dss_aes_128_gcm_sha_256", TLS_DHE_DSS_WITH_AES_128_GCM_SHA256},
+ {"ecdhe_ecdsa_aes_128_gcm_sha_256", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
+ {"ecdh_ecdsa_aes_128_gcm_sha_256", TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256},
+ {"ecdhe_rsa_aes_128_gcm_sha_256", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ {"ecdh_rsa_aes_128_gcm_sha_256", TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256},
+#endif
+#ifdef TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
+ /* cipher suites using SHA384 */
+ {"rsa_aes_256_gcm_sha_384", TLS_RSA_WITH_AES_256_GCM_SHA384},
+ {"dhe_rsa_aes_256_gcm_sha_384", TLS_DHE_RSA_WITH_AES_256_GCM_SHA384},
+ {"dhe_dss_aes_256_gcm_sha_384", TLS_DHE_DSS_WITH_AES_256_GCM_SHA384},
+ {"ecdhe_ecdsa_aes_256_sha_384", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384},
+ {"ecdhe_rsa_aes_256_sha_384", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384},
+ {"ecdhe_ecdsa_aes_256_gcm_sha_384", TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384},
+ {"ecdhe_rsa_aes_256_gcm_sha_384", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
+#endif
+#ifdef TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
+ /* chacha20-poly1305 cipher suites */
+ {"ecdhe_rsa_chacha20_poly1305_sha_256",
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
+ {"ecdhe_ecdsa_chacha20_poly1305_sha_256",
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256},
+ {"dhe_rsa_chacha20_poly1305_sha_256",
+ TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
+#endif
+#ifdef TLS_AES_256_GCM_SHA384
+ {"aes_128_gcm_sha_256", TLS_AES_128_GCM_SHA256},
+ {"aes_256_gcm_sha_384", TLS_AES_256_GCM_SHA384},
+ {"chacha20_poly1305_sha_256", TLS_CHACHA20_POLY1305_SHA256},
+#endif
+#ifdef TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
+ /* AES CBC cipher suites in RFC 5246. Introduced in NSS release 3.20 */
+ {"dhe_dss_aes_128_sha_256", TLS_DHE_DSS_WITH_AES_128_CBC_SHA256},
+ {"dhe_dss_aes_256_sha_256", TLS_DHE_DSS_WITH_AES_256_CBC_SHA256},
+#endif
+#ifdef TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
+ /* Camellia cipher suites in RFC 4132/5932.
+ Introduced in NSS release 3.12 */
+ {"dhe_rsa_camellia_128_sha", TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA},
+ {"dhe_dss_camellia_128_sha", TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA},
+ {"dhe_rsa_camellia_256_sha", TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA},
+ {"dhe_dss_camellia_256_sha", TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA},
+ {"rsa_camellia_128_sha", TLS_RSA_WITH_CAMELLIA_128_CBC_SHA},
+ {"rsa_camellia_256_sha", TLS_RSA_WITH_CAMELLIA_256_CBC_SHA},
+#endif
+#ifdef TLS_RSA_WITH_SEED_CBC_SHA
+ /* SEED cipher suite in RFC 4162. Introduced in NSS release 3.12.3 */
+ {"rsa_seed_sha", TLS_RSA_WITH_SEED_CBC_SHA},
+#endif
+};
+
+#if defined(WIN32)
+static const char *pem_library = "nsspem.dll";
+static const char *trust_library = "nssckbi.dll";
+#elif defined(__APPLE__)
+static const char *pem_library = "libnsspem.dylib";
+static const char *trust_library = "libnssckbi.dylib";
+#else
+static const char *pem_library = "libnsspem.so";
+static const char *trust_library = "libnssckbi.so";
+#endif
+
+static SECMODModule *pem_module = NULL;
+static SECMODModule *trust_module = NULL;
+
+/* NSPR I/O layer we use to detect blocking direction during SSL handshake */
+static PRDescIdentity nspr_io_identity = PR_INVALID_IO_LAYER;
+static PRIOMethods nspr_io_methods;
+
+static const char *nss_error_to_name(PRErrorCode code)
+{
+ const char *name = PR_ErrorToName(code);
+ if(name)
+ return name;
+
+ return "unknown error";
+}
+
+static void nss_print_error_message(struct Curl_easy *data, PRUint32 err)
+{
+ failf(data, "%s", PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT));
+}
+
+static char *nss_sslver_to_name(PRUint16 nssver)
+{
+ switch(nssver) {
+ case SSL_LIBRARY_VERSION_2:
+ return strdup("SSLv2");
+ case SSL_LIBRARY_VERSION_3_0:
+ return strdup("SSLv3");
+ case SSL_LIBRARY_VERSION_TLS_1_0:
+ return strdup("TLSv1.0");
+#ifdef SSL_LIBRARY_VERSION_TLS_1_1
+ case SSL_LIBRARY_VERSION_TLS_1_1:
+ return strdup("TLSv1.1");
+#endif
+#ifdef SSL_LIBRARY_VERSION_TLS_1_2
+ case SSL_LIBRARY_VERSION_TLS_1_2:
+ return strdup("TLSv1.2");
+#endif
+#ifdef SSL_LIBRARY_VERSION_TLS_1_3
+ case SSL_LIBRARY_VERSION_TLS_1_3:
+ return strdup("TLSv1.3");
+#endif
+ default:
+ return curl_maprintf("0x%04x", nssver);
+ }
+}
+
+/* the longest cipher name this supports */
+#define MAX_CIPHER_LENGTH 128
+
+static SECStatus set_ciphers(struct Curl_easy *data, PRFileDesc *model,
+ const char *cipher_list)
+{
+ unsigned int i;
+ const char *cipher;
+
+ /* use accessors to avoid dynamic linking issues after an update of NSS */
+ const PRUint16 num_implemented_ciphers = SSL_GetNumImplementedCiphers();
+ const PRUint16 *implemented_ciphers = SSL_GetImplementedCiphers();
+ if(!implemented_ciphers)
+ return SECFailure;
+
+ /* First disable all ciphers. This uses a different max value in case
+ * NSS adds more ciphers later we don't want them available by
+ * accident
+ */
+ for(i = 0; i < num_implemented_ciphers; i++) {
+ SSL_CipherPrefSet(model, implemented_ciphers[i], PR_FALSE);
+ }
+
+ cipher = cipher_list;
+
+ while(cipher && cipher[0]) {
+ const char *end;
+ char name[MAX_CIPHER_LENGTH + 1];
+ size_t len;
+ bool found = FALSE;
+ while((*cipher) && (ISBLANK(*cipher)))
+ ++cipher;
+
+ end = strpbrk(cipher, ":, ");
+ if(end)
+ len = end - cipher;
+ else
+ len = strlen(cipher);
+
+ if(len > MAX_CIPHER_LENGTH) {
+ failf(data, "Bad cipher list");
+ return SECFailure;
+ }
+ else if(len) {
+ memcpy(name, cipher, len);
+ name[len] = 0;
+
+ for(i = 0; i<NUM_OF_CIPHERS; i++) {
+ if(strcasecompare(name, cipherlist[i].name)) {
+ /* Enable the selected cipher */
+ if(SSL_CipherPrefSet(model, cipherlist[i].num, PR_TRUE) !=
+ SECSuccess) {
+ failf(data, "cipher-suite not supported by NSS: %s", name);
+ return SECFailure;
+ }
+ found = TRUE;
+ break;
+ }
+ }
+ }
+
+ if(!found && len) {
+ failf(data, "Unknown cipher: %s", name);
+ return SECFailure;
+ }
+ if(end)
+ cipher = ++end;
+ else
+ break;
+ }
+
+ return SECSuccess;
+}
+
+/*
+ * Return true if at least one cipher-suite is enabled. Used to determine
+ * if we need to call NSS_SetDomesticPolicy() to enable the default ciphers.
+ */
+static bool any_cipher_enabled(void)
+{
+ unsigned int i;
+
+ for(i = 0; i<NUM_OF_CIPHERS; i++) {
+ PRInt32 policy = 0;
+ SSL_CipherPolicyGet(cipherlist[i].num, &policy);
+ if(policy)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * Determine whether the nickname passed in is a filename that needs to
+ * be loaded as a PEM or a regular NSS nickname.
+ *
+ * returns 1 for a file
+ * returns 0 for not a file (NSS nickname)
+ */
+static int is_file(const char *filename)
+{
+ struct_stat st;
+
+ if(!filename)
+ return 0;
+
+ if(stat(filename, &st) == 0)
+ if(S_ISREG(st.st_mode) || S_ISFIFO(st.st_mode) || S_ISCHR(st.st_mode))
+ return 1;
+
+ return 0;
+}
+
+/* Check if the given string is filename or nickname of a certificate. If the
+ * given string is recognized as filename, return NULL. If the given string is
+ * recognized as nickname, return a duplicated string. The returned string
+ * should be later deallocated using free(). If the OOM failure occurs, we
+ * return NULL, too.
+ */
+static char *dup_nickname(struct Curl_easy *data, const char *str)
+{
+ const char *n;
+
+ if(!is_file(str))
+ /* no such file exists, use the string as nickname */
+ return strdup(str);
+
+ /* search the first slash; we require at least one slash in a file name */
+ n = strchr(str, '/');
+ if(!n) {
+ infof(data, "WARNING: certificate file name \"%s\" handled as nickname; "
+ "please use \"./%s\" to force file name", str, str);
+ return strdup(str);
+ }
+
+ /* we'll use the PEM reader to read the certificate from file */
+ return NULL;
+}
+
+/* Lock/unlock wrapper for PK11_FindSlotByName() to work around race condition
+ * in nssSlot_IsTokenPresent() causing spurious SEC_ERROR_NO_TOKEN. For more
+ * details, go to <https://bugzilla.mozilla.org/1297397>.
+ */
+static PK11SlotInfo* nss_find_slot_by_name(const char *slot_name)
+{
+ PK11SlotInfo *slot;
+ PR_Lock(nss_findslot_lock);
+ slot = PK11_FindSlotByName(slot_name);
+ PR_Unlock(nss_findslot_lock);
+ return slot;
+}
+
+/* wrap 'ptr' as list node and tail-insert into 'list' */
+static CURLcode insert_wrapped_ptr(struct Curl_llist *list, void *ptr)
+{
+ struct ptr_list_wrap *wrap = malloc(sizeof(*wrap));
+ if(!wrap)
+ return CURLE_OUT_OF_MEMORY;
+
+ wrap->ptr = ptr;
+ Curl_llist_insert_next(list, list->tail, wrap, &wrap->node);
+ return CURLE_OK;
+}
+
+/* Call PK11_CreateGenericObject() with the given obj_class and filename. If
+ * the call succeeds, append the object handle to the list of objects so that
+ * the object can be destroyed in nss_close(). */
+static CURLcode nss_create_object(struct ssl_connect_data *connssl,
+ CK_OBJECT_CLASS obj_class,
+ const char *filename, bool cacert)
+{
+ PK11SlotInfo *slot;
+ PK11GenericObject *obj;
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_BBOOL ckfalse = CK_FALSE;
+ CK_ATTRIBUTE attrs[/* max count of attributes */ 4];
+ int attr_cnt = 0;
+ CURLcode result = (cacert)
+ ? CURLE_SSL_CACERT_BADFILE
+ : CURLE_SSL_CERTPROBLEM;
+
+ const int slot_id = (cacert) ? 0 : 1;
+ char *slot_name = aprintf("PEM Token #%d", slot_id);
+ struct ssl_backend_data *backend = connssl->backend;
+
+ DEBUGASSERT(backend);
+
+ if(!slot_name)
+ return CURLE_OUT_OF_MEMORY;
+
+ slot = nss_find_slot_by_name(slot_name);
+ free(slot_name);
+ if(!slot)
+ return result;
+
+ PK11_SETATTRS(attrs, attr_cnt, CKA_CLASS, &obj_class, sizeof(obj_class));
+ PK11_SETATTRS(attrs, attr_cnt, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL));
+ PK11_SETATTRS(attrs, attr_cnt, CKA_LABEL, (unsigned char *)filename,
+ (CK_ULONG)strlen(filename) + 1);
+
+ if(CKO_CERTIFICATE == obj_class) {
+ CK_BBOOL *pval = (cacert) ? (&cktrue) : (&ckfalse);
+ PK11_SETATTRS(attrs, attr_cnt, CKA_TRUST, pval, sizeof(*pval));
+ }
+
+ /* PK11_CreateManagedGenericObject() was introduced in NSS 3.34 because
+ * PK11_DestroyGenericObject() does not release resources allocated by
+ * PK11_CreateGenericObject() early enough. */
+ obj =
+#ifdef HAVE_PK11_CREATEMANAGEDGENERICOBJECT
+ PK11_CreateManagedGenericObject
+#else
+ PK11_CreateGenericObject
+#endif
+ (slot, attrs, attr_cnt, PR_FALSE);
+
+ PK11_FreeSlot(slot);
+ if(!obj)
+ return result;
+
+ if(insert_wrapped_ptr(&backend->obj_list, obj) != CURLE_OK) {
+ PK11_DestroyGenericObject(obj);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(!cacert && CKO_CERTIFICATE == obj_class)
+ /* store reference to a client certificate */
+ backend->obj_clicert = obj;
+
+ return CURLE_OK;
+}
+
+/* Destroy the NSS object whose handle is given by ptr. This function is
+ * a callback of Curl_llist_alloc() used by Curl_llist_destroy() to destroy
+ * NSS objects in nss_close() */
+static void nss_destroy_object(void *user, void *ptr)
+{
+ struct ptr_list_wrap *wrap = (struct ptr_list_wrap *) ptr;
+ PK11GenericObject *obj = (PK11GenericObject *) wrap->ptr;
+ (void) user;
+ PK11_DestroyGenericObject(obj);
+ free(wrap);
+}
+
+/* same as nss_destroy_object() but for CRL items */
+static void nss_destroy_crl_item(void *user, void *ptr)
+{
+ struct ptr_list_wrap *wrap = (struct ptr_list_wrap *) ptr;
+ SECItem *crl_der = (SECItem *) wrap->ptr;
+ (void) user;
+ SECITEM_FreeItem(crl_der, PR_TRUE);
+ free(wrap);
+}
+
+static CURLcode nss_load_cert(struct ssl_connect_data *ssl,
+ const char *filename, PRBool cacert)
+{
+ CURLcode result = (cacert)
+ ? CURLE_SSL_CACERT_BADFILE
+ : CURLE_SSL_CERTPROBLEM;
+
+ /* libnsspem.so leaks memory if the requested file does not exist. For more
+ * details, go to <https://bugzilla.redhat.com/734760>. */
+ if(is_file(filename))
+ result = nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert);
+
+ if(!result && !cacert) {
+ /* we have successfully loaded a client certificate */
+ char *nickname = NULL;
+ char *n = strrchr(filename, '/');
+ if(n)
+ n++;
+
+ /* The following undocumented magic helps to avoid a SIGSEGV on call
+ * of PK11_ReadRawAttribute() from SelectClientCert() when using an
+ * immature version of libnsspem.so. For more details, go to
+ * <https://bugzilla.redhat.com/733685>. */
+ nickname = aprintf("PEM Token #1:%s", n);
+ if(nickname) {
+ CERTCertificate *cert = PK11_FindCertFromNickname(nickname, NULL);
+ if(cert)
+ CERT_DestroyCertificate(cert);
+
+ free(nickname);
+ }
+ }
+
+ return result;
+}
+
+/* add given CRL to cache if it is not already there */
+static CURLcode nss_cache_crl(SECItem *crl_der)
+{
+ CERTCertDBHandle *db = CERT_GetDefaultCertDB();
+ CERTSignedCrl *crl = SEC_FindCrlByDERCert(db, crl_der, 0);
+ if(crl) {
+ /* CRL already cached */
+ SEC_DestroyCrl(crl);
+ SECITEM_FreeItem(crl_der, PR_TRUE);
+ return CURLE_OK;
+ }
+
+ /* acquire lock before call of CERT_CacheCRL() and accessing nss_crl_list */
+ PR_Lock(nss_crllock);
+
+ if(SECSuccess != CERT_CacheCRL(db, crl_der)) {
+ /* unable to cache CRL */
+ SECITEM_FreeItem(crl_der, PR_TRUE);
+ PR_Unlock(nss_crllock);
+ return CURLE_SSL_CRL_BADFILE;
+ }
+
+ /* store the CRL item so that we can free it in nss_cleanup() */
+ if(insert_wrapped_ptr(&nss_crl_list, crl_der) != CURLE_OK) {
+ if(SECSuccess == CERT_UncacheCRL(db, crl_der))
+ SECITEM_FreeItem(crl_der, PR_TRUE);
+ PR_Unlock(nss_crllock);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* we need to clear session cache, so that the CRL could take effect */
+ SSL_ClearSessionCache();
+ PR_Unlock(nss_crllock);
+ return CURLE_OK;
+}
+
+static CURLcode nss_load_crl(const char *crlfilename)
+{
+ PRFileDesc *infile;
+ PRFileInfo info;
+ SECItem filedata = { 0, NULL, 0 };
+ SECItem *crl_der = NULL;
+ char *body;
+
+ infile = PR_Open(crlfilename, PR_RDONLY, 0);
+ if(!infile)
+ return CURLE_SSL_CRL_BADFILE;
+
+ if(PR_SUCCESS != PR_GetOpenFileInfo(infile, &info))
+ goto fail;
+
+ if(!SECITEM_AllocItem(NULL, &filedata, info.size + /* zero ended */ 1))
+ goto fail;
+
+ if(info.size != PR_Read(infile, filedata.data, info.size))
+ goto fail;
+
+ crl_der = SECITEM_AllocItem(NULL, NULL, 0U);
+ if(!crl_der)
+ goto fail;
+
+ /* place a trailing zero right after the visible data */
+ body = (char *)filedata.data;
+ body[--filedata.len] = '\0';
+
+ body = strstr(body, "-----BEGIN");
+ if(body) {
+ /* assume ASCII */
+ char *trailer;
+ char *begin = PORT_Strchr(body, '\n');
+ if(!begin)
+ begin = PORT_Strchr(body, '\r');
+ if(!begin)
+ goto fail;
+
+ trailer = strstr(++begin, "-----END");
+ if(!trailer)
+ goto fail;
+
+ /* retrieve DER from ASCII */
+ *trailer = '\0';
+ if(ATOB_ConvertAsciiToItem(crl_der, begin))
+ goto fail;
+
+ SECITEM_FreeItem(&filedata, PR_FALSE);
+ }
+ else
+ /* assume DER */
+ *crl_der = filedata;
+
+ PR_Close(infile);
+ return nss_cache_crl(crl_der);
+
+fail:
+ PR_Close(infile);
+ SECITEM_FreeItem(crl_der, PR_TRUE);
+ SECITEM_FreeItem(&filedata, PR_FALSE);
+ return CURLE_SSL_CRL_BADFILE;
+}
+
+static CURLcode nss_load_key(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ char *key_file)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ PK11SlotInfo *slot, *tmp;
+ SECStatus status;
+ CURLcode result;
+
+ (void)data;
+ result = nss_create_object(connssl, CKO_PRIVATE_KEY, key_file, FALSE);
+ if(result) {
+ PR_SetError(SEC_ERROR_BAD_KEY, 0);
+ return result;
+ }
+
+ slot = nss_find_slot_by_name("PEM Token #1");
+ if(!slot)
+ return CURLE_SSL_CERTPROBLEM;
+
+ /* This will force the token to be seen as re-inserted */
+ tmp = SECMOD_WaitForAnyTokenEvent(pem_module, 0, 0);
+ if(tmp)
+ PK11_FreeSlot(tmp);
+ if(!PK11_IsPresent(slot)) {
+ PK11_FreeSlot(slot);
+ return CURLE_SSL_CERTPROBLEM;
+ }
+
+ status = PK11_Authenticate(slot, PR_TRUE, ssl_config->key_passwd);
+ PK11_FreeSlot(slot);
+
+ return (SECSuccess == status) ? CURLE_OK : CURLE_SSL_CERTPROBLEM;
+}
+
+static int display_error(struct Curl_easy *data, PRInt32 err,
+ const char *filename)
+{
+ switch(err) {
+ case SEC_ERROR_BAD_PASSWORD:
+ failf(data, "Unable to load client key: Incorrect password");
+ return 1;
+ case SEC_ERROR_UNKNOWN_CERT:
+ failf(data, "Unable to load certificate %s", filename);
+ return 1;
+ default:
+ break;
+ }
+ return 0; /* The caller will print a generic error */
+}
+
+static CURLcode cert_stuff(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ char *cert_file, char *key_file)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ CURLcode result;
+
+ if(cert_file) {
+ result = nss_load_cert(connssl, cert_file, PR_FALSE);
+ if(result) {
+ const PRErrorCode err = PR_GetError();
+ if(!display_error(data, err, cert_file)) {
+ const char *err_name = nss_error_to_name(err);
+ failf(data, "unable to load client cert: %d (%s)", err, err_name);
+ }
+
+ return result;
+ }
+ }
+
+ if(key_file || (is_file(cert_file))) {
+ if(key_file)
+ result = nss_load_key(cf, data, key_file);
+ else
+ /* In case the cert file also has the key */
+ result = nss_load_key(cf, data, cert_file);
+ if(result) {
+ const PRErrorCode err = PR_GetError();
+ if(!display_error(data, err, key_file)) {
+ const char *err_name = nss_error_to_name(err);
+ failf(data, "unable to load client key: %d (%s)", err, err_name);
+ }
+
+ return result;
+ }
+ }
+
+ return CURLE_OK;
+}
+
+static char *nss_get_password(PK11SlotInfo *slot, PRBool retry, void *arg)
+{
+ (void)slot; /* unused */
+
+ if(retry || !arg)
+ return NULL;
+ else
+ return (char *)PORT_Strdup((char *)arg);
+}
+
+/* bypass the default SSL_AuthCertificate() hook in case we do not want to
+ * verify peer */
+static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig,
+ PRBool isServer)
+{
+ struct Curl_cfilter *cf = (struct Curl_cfilter *)arg;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct Curl_easy *data = connssl->backend->data;
+
+ DEBUGASSERT(data);
+#ifdef SSL_ENABLE_OCSP_STAPLING
+ if(conn_config->verifystatus) {
+ SECStatus cacheResult;
+
+ const SECItemArray *csa = SSL_PeerStapledOCSPResponses(fd);
+ if(!csa) {
+ failf(data, "Invalid OCSP response");
+ return SECFailure;
+ }
+
+ if(csa->len == 0) {
+ failf(data, "No OCSP response received");
+ return SECFailure;
+ }
+
+ cacheResult = CERT_CacheOCSPResponseFromSideChannel(
+ CERT_GetDefaultCertDB(), SSL_PeerCertificate(fd),
+ PR_Now(), &csa->items[0], arg
+ );
+
+ if(cacheResult != SECSuccess) {
+ failf(data, "Invalid OCSP response");
+ return cacheResult;
+ }
+ }
+#endif
+
+ if(!conn_config->verifypeer) {
+ infof(data, "skipping SSL peer certificate verification");
+ return SECSuccess;
+ }
+
+ return SSL_AuthCertificate(CERT_GetDefaultCertDB(), fd, checksig, isServer);
+}
+
+/**
+ * Inform the application that the handshake is complete.
+ */
+static void HandshakeCallback(PRFileDesc *sock, void *arg)
+{
+ struct Curl_cfilter *cf = (struct Curl_cfilter *)arg;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct Curl_easy *data = connssl->backend->data;
+ struct connectdata *conn = cf->conn;
+ unsigned int buflenmax = 50;
+ unsigned char buf[50];
+ unsigned int buflen;
+ SSLNextProtoState state;
+
+ DEBUGASSERT(data);
+ if(!conn->bits.tls_enable_alpn) {
+ return;
+ }
+
+ if(SSL_GetNextProto(sock, &state, buf, &buflen, buflenmax) == SECSuccess) {
+
+ switch(state) {
+#if NSSVERNUM >= 0x031a00 /* 3.26.0 */
+ /* used by NSS internally to implement 0-RTT */
+ case SSL_NEXT_PROTO_EARLY_VALUE:
+ /* fall through! */
+#endif
+ case SSL_NEXT_PROTO_NO_SUPPORT:
+ case SSL_NEXT_PROTO_NO_OVERLAP:
+ infof(data, VTLS_INFOF_NO_ALPN);
+ return;
+#ifdef SSL_ENABLE_ALPN
+ case SSL_NEXT_PROTO_SELECTED:
+ infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, buflen, buf);
+ break;
+#endif
+ default:
+ /* ignore SSL_NEXT_PROTO_NEGOTIATED */
+ break;
+ }
+
+#ifdef USE_HTTP2
+ if(buflen == ALPN_H2_LENGTH &&
+ !memcmp(ALPN_H2, buf, ALPN_H2_LENGTH)) {
+ cf->conn->alpn = CURL_HTTP_VERSION_2;
+ }
+ else
+#endif
+ if(buflen == ALPN_HTTP_1_1_LENGTH &&
+ !memcmp(ALPN_HTTP_1_1, buf, ALPN_HTTP_1_1_LENGTH)) {
+ cf->conn->alpn = CURL_HTTP_VERSION_1_1;
+ }
+
+ /* This callback might get called when PR_Recv() is used within
+ * close_one() during a connection shutdown. At that point there might not
+ * be any "bundle" associated with the connection anymore.
+ */
+ if(conn->bundle)
+ Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ }
+}
+
+#if NSSVERNUM >= 0x030f04 /* 3.15.4 */
+static SECStatus CanFalseStartCallback(PRFileDesc *sock, void *client_data,
+ PRBool *canFalseStart)
+{
+ struct Curl_easy *data = (struct Curl_easy *)client_data;
+
+ SSLChannelInfo channelInfo;
+ SSLCipherSuiteInfo cipherInfo;
+
+ SECStatus rv;
+ PRBool negotiatedExtension;
+
+ *canFalseStart = PR_FALSE;
+
+ if(SSL_GetChannelInfo(sock, &channelInfo, sizeof(channelInfo)) != SECSuccess)
+ return SECFailure;
+
+ if(SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo,
+ sizeof(cipherInfo)) != SECSuccess)
+ return SECFailure;
+
+ /* Prevent version downgrade attacks from TLS 1.2, and avoid False Start for
+ * TLS 1.3 and later. See https://bugzilla.mozilla.org/show_bug.cgi?id=861310
+ */
+ if(channelInfo.protocolVersion != SSL_LIBRARY_VERSION_TLS_1_2)
+ goto end;
+
+ /* Only allow ECDHE key exchange algorithm.
+ * See https://bugzilla.mozilla.org/show_bug.cgi?id=952863 */
+ if(cipherInfo.keaType != ssl_kea_ecdh)
+ goto end;
+
+ /* Prevent downgrade attacks on the symmetric cipher. We do not allow CBC
+ * mode due to BEAST, POODLE, and other attacks on the MAC-then-Encrypt
+ * design. See https://bugzilla.mozilla.org/show_bug.cgi?id=1109766 */
+ if(cipherInfo.symCipher != ssl_calg_aes_gcm)
+ goto end;
+
+ /* Enforce ALPN to do False Start, as an indicator of server
+ compatibility. */
+ rv = SSL_HandshakeNegotiatedExtension(sock, ssl_app_layer_protocol_xtn,
+ &negotiatedExtension);
+ if(rv != SECSuccess || !negotiatedExtension) {
+ rv = SSL_HandshakeNegotiatedExtension(sock, ssl_next_proto_nego_xtn,
+ &negotiatedExtension);
+ }
+
+ if(rv != SECSuccess || !negotiatedExtension)
+ goto end;
+
+ *canFalseStart = PR_TRUE;
+
+ infof(data, "Trying TLS False Start");
+
+end:
+ return SECSuccess;
+}
+#endif
+
+static void display_cert_info(struct Curl_easy *data,
+ CERTCertificate *cert)
+{
+ char *subject, *issuer, *common_name;
+ PRExplodedTime printableTime;
+ char timeString[256];
+ PRTime notBefore, notAfter;
+
+ subject = CERT_NameToAscii(&cert->subject);
+ issuer = CERT_NameToAscii(&cert->issuer);
+ common_name = CERT_GetCommonName(&cert->subject);
+ infof(data, "subject: %s", subject);
+
+ CERT_GetCertTimes(cert, ¬Before, ¬After);
+ PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime);
+ PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime);
+ infof(data, " start date: %s", timeString);
+ PR_ExplodeTime(notAfter, PR_GMTParameters, &printableTime);
+ PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime);
+ infof(data, " expire date: %s", timeString);
+ infof(data, " common name: %s", common_name);
+ infof(data, " issuer: %s", issuer);
+
+ PR_Free(subject);
+ PR_Free(issuer);
+ PR_Free(common_name);
+}
+
+/* A number of certs that will never occur in a real server handshake */
+#define TOO_MANY_CERTS 300
+
+static CURLcode display_conn_info(struct Curl_easy *data, PRFileDesc *sock)
+{
+ CURLcode result = CURLE_OK;
+ SSLChannelInfo channel;
+ SSLCipherSuiteInfo suite;
+ CERTCertificate *cert;
+ CERTCertificate *cert2;
+ CERTCertificate *cert3;
+ PRTime now;
+
+ if(SSL_GetChannelInfo(sock, &channel, sizeof(channel)) ==
+ SECSuccess && channel.length == sizeof(channel) &&
+ channel.cipherSuite) {
+ if(SSL_GetCipherSuiteInfo(channel.cipherSuite,
+ &suite, sizeof(suite)) == SECSuccess) {
+ infof(data, "SSL connection using %s", suite.cipherSuiteName);
+ }
+ }
+
+ cert = SSL_PeerCertificate(sock);
+ if(cert) {
+ infof(data, "Server certificate:");
+
+ if(!data->set.ssl.certinfo) {
+ display_cert_info(data, cert);
+ CERT_DestroyCertificate(cert);
+ }
+ else {
+ /* Count certificates in chain. */
+ int i = 1;
+ now = PR_Now();
+ if(!cert->isRoot) {
+ cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA);
+ while(cert2) {
+ i++;
+ if(i >= TOO_MANY_CERTS) {
+ CERT_DestroyCertificate(cert2);
+ failf(data, "certificate loop");
+ return CURLE_SSL_CERTPROBLEM;
+ }
+ if(cert2->isRoot) {
+ CERT_DestroyCertificate(cert2);
+ break;
+ }
+ cert3 = CERT_FindCertIssuer(cert2, now, certUsageSSLCA);
+ CERT_DestroyCertificate(cert2);
+ cert2 = cert3;
+ }
+ }
+
+ result = Curl_ssl_init_certinfo(data, i);
+ if(!result) {
+ for(i = 0; cert; cert = cert2) {
+ result = Curl_extract_certinfo(data, i++, (char *)cert->derCert.data,
+ (char *)cert->derCert.data +
+ cert->derCert.len);
+ if(result)
+ break;
+
+ if(cert->isRoot) {
+ CERT_DestroyCertificate(cert);
+ break;
+ }
+
+ cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA);
+ CERT_DestroyCertificate(cert);
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+static SECStatus BadCertHandler(void *arg, PRFileDesc *sock)
+{
+ struct Curl_cfilter *cf = (struct Curl_cfilter *)arg;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct Curl_easy *data = connssl->backend->data;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config;
+ PRErrorCode err = PR_GetError();
+ CERTCertificate *cert;
+
+ DEBUGASSERT(data);
+ ssl_config = Curl_ssl_cf_get_config(cf, data);
+ /* remember the cert verification result */
+ ssl_config->certverifyresult = err;
+
+ if(err == SSL_ERROR_BAD_CERT_DOMAIN && !conn_config->verifyhost)
+ /* we are asked not to verify the host name */
+ return SECSuccess;
+
+ /* print only info about the cert, the error is printed off the callback */
+ cert = SSL_PeerCertificate(sock);
+ if(cert) {
+ infof(data, "Server certificate:");
+ display_cert_info(data, cert);
+ CERT_DestroyCertificate(cert);
+ }
+
+ return SECFailure;
+}
+
+/**
+ *
+ * Check that the Peer certificate's issuer certificate matches the one found
+ * by issuer_nickname. This is not exactly the way OpenSSL and GNU TLS do the
+ * issuer check, so we provide comments that mimic the OpenSSL
+ * X509_check_issued function (in x509v3/v3_purp.c)
+ */
+static SECStatus check_issuer_cert(PRFileDesc *sock,
+ char *issuer_nickname)
+{
+ CERTCertificate *cert, *cert_issuer, *issuer;
+ SECStatus res = SECSuccess;
+ void *proto_win = NULL;
+
+ cert = SSL_PeerCertificate(sock);
+ cert_issuer = CERT_FindCertIssuer(cert, PR_Now(), certUsageObjectSigner);
+
+ proto_win = SSL_RevealPinArg(sock);
+ issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win);
+
+ if((!cert_issuer) || (!issuer))
+ res = SECFailure;
+ else if(SECITEM_CompareItem(&cert_issuer->derCert,
+ &issuer->derCert) != SECEqual)
+ res = SECFailure;
+
+ CERT_DestroyCertificate(cert);
+ CERT_DestroyCertificate(issuer);
+ CERT_DestroyCertificate(cert_issuer);
+ return res;
+}
+
+static CURLcode cmp_peer_pubkey(struct ssl_connect_data *connssl,
+ const char *pinnedpubkey)
+{
+ CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct Curl_easy *data = NULL;
+ CERTCertificate *cert;
+
+ DEBUGASSERT(backend);
+ data = backend->data;
+
+ if(!pinnedpubkey)
+ /* no pinned public key specified */
+ return CURLE_OK;
+
+ /* get peer certificate */
+ cert = SSL_PeerCertificate(backend->handle);
+ if(cert) {
+ /* extract public key from peer certificate */
+ SECKEYPublicKey *pubkey = CERT_ExtractPublicKey(cert);
+ if(pubkey) {
+ /* encode the public key as DER */
+ SECItem *cert_der = PK11_DEREncodePublicKey(pubkey);
+ if(cert_der) {
+ /* compare the public key with the pinned public key */
+ result = Curl_pin_peer_pubkey(data, pinnedpubkey, cert_der->data,
+ cert_der->len);
+ SECITEM_FreeItem(cert_der, PR_TRUE);
+ }
+ SECKEY_DestroyPublicKey(pubkey);
+ }
+ CERT_DestroyCertificate(cert);
+ }
+
+ /* report the resulting status */
+ switch(result) {
+ case CURLE_OK:
+ infof(data, "pinned public key verified successfully");
+ break;
+ case CURLE_SSL_PINNEDPUBKEYNOTMATCH:
+ failf(data, "failed to verify pinned public key");
+ break;
+ default:
+ /* OOM, etc. */
+ break;
+ }
+
+ return result;
+}
+
+/**
+ *
+ * Callback to pick the SSL client certificate.
+ */
+static SECStatus SelectClientCert(void *arg, PRFileDesc *sock,
+ struct CERTDistNamesStr *caNames,
+ struct CERTCertificateStr **pRetCert,
+ struct SECKEYPrivateKeyStr **pRetKey)
+{
+ struct ssl_connect_data *connssl = (struct ssl_connect_data *)arg;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct Curl_easy *data = NULL;
+ const char *nickname = NULL;
+ static const char pem_slotname[] = "PEM Token #1";
+
+ DEBUGASSERT(backend);
+
+ data = backend->data;
+ nickname = backend->client_nickname;
+
+ if(backend->obj_clicert) {
+ /* use the cert/key provided by PEM reader */
+ SECItem cert_der = { 0, NULL, 0 };
+ void *proto_win = SSL_RevealPinArg(sock);
+ struct CERTCertificateStr *cert;
+ struct SECKEYPrivateKeyStr *key;
+
+ PK11SlotInfo *slot = nss_find_slot_by_name(pem_slotname);
+ if(!slot) {
+ failf(data, "NSS: PK11 slot not found: %s", pem_slotname);
+ return SECFailure;
+ }
+
+ if(PK11_ReadRawAttribute(PK11_TypeGeneric, backend->obj_clicert, CKA_VALUE,
+ &cert_der) != SECSuccess) {
+ failf(data, "NSS: CKA_VALUE not found in PK11 generic object");
+ PK11_FreeSlot(slot);
+ return SECFailure;
+ }
+
+ cert = PK11_FindCertFromDERCertItem(slot, &cert_der, proto_win);
+ SECITEM_FreeItem(&cert_der, PR_FALSE);
+ if(!cert) {
+ failf(data, "NSS: client certificate from file not found");
+ PK11_FreeSlot(slot);
+ return SECFailure;
+ }
+
+ key = PK11_FindPrivateKeyFromCert(slot, cert, NULL);
+ PK11_FreeSlot(slot);
+ if(!key) {
+ failf(data, "NSS: private key from file not found");
+ CERT_DestroyCertificate(cert);
+ return SECFailure;
+ }
+
+ infof(data, "NSS: client certificate from file");
+ display_cert_info(data, cert);
+
+ *pRetCert = cert;
+ *pRetKey = key;
+ return SECSuccess;
+ }
+
+ /* use the default NSS hook */
+ if(SECSuccess != NSS_GetClientAuthData((void *)nickname, sock, caNames,
+ pRetCert, pRetKey)
+ || !*pRetCert) {
+
+ if(!nickname)
+ failf(data, "NSS: client certificate not found (nickname not "
+ "specified)");
+ else
+ failf(data, "NSS: client certificate not found: %s", nickname);
+
+ return SECFailure;
+ }
+
+ /* get certificate nickname if any */
+ nickname = (*pRetCert)->nickname;
+ if(!nickname)
+ nickname = "[unknown]";
+
+ if(!strncmp(nickname, pem_slotname, sizeof(pem_slotname) - 1U)) {
+ failf(data, "NSS: refusing previously loaded certificate from file: %s",
+ nickname);
+ return SECFailure;
+ }
+
+ if(!*pRetKey) {
+ failf(data, "NSS: private key not found for certificate: %s", nickname);
+ return SECFailure;
+ }
+
+ infof(data, "NSS: using client certificate: %s", nickname);
+ display_cert_info(data, *pRetCert);
+ return SECSuccess;
+}
+
+/* update blocking direction in case of PR_WOULD_BLOCK_ERROR */
+static void nss_update_connecting_state(ssl_connect_state state, void *secret)
+{
+ struct ssl_connect_data *connssl = (struct ssl_connect_data *)secret;
+ if(PR_GetError() != PR_WOULD_BLOCK_ERROR)
+ /* an unrelated error is passing by */
+ return;
+
+ switch(connssl->connecting_state) {
+ case ssl_connect_2:
+ case ssl_connect_2_reading:
+ case ssl_connect_2_writing:
+ break;
+ default:
+ /* we are not called from an SSL handshake */
+ return;
+ }
+
+ /* update the state accordingly */
+ connssl->connecting_state = state;
+}
+
+/* recv() wrapper we use to detect blocking direction during SSL handshake */
+static PRInt32 nspr_io_recv(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ const PRRecvFN recv_fn = fd->lower->methods->recv;
+ const PRInt32 rv = recv_fn(fd->lower, buf, amount, flags, timeout);
+ if(rv < 0)
+ /* check for PR_WOULD_BLOCK_ERROR and update blocking direction */
+ nss_update_connecting_state(ssl_connect_2_reading, fd->secret);
+ return rv;
+}
+
+/* send() wrapper we use to detect blocking direction during SSL handshake */
+static PRInt32 nspr_io_send(PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ const PRSendFN send_fn = fd->lower->methods->send;
+ const PRInt32 rv = send_fn(fd->lower, buf, amount, flags, timeout);
+ if(rv < 0)
+ /* check for PR_WOULD_BLOCK_ERROR and update blocking direction */
+ nss_update_connecting_state(ssl_connect_2_writing, fd->secret);
+ return rv;
+}
+
+/* close() wrapper to avoid assertion failure due to fd->secret != NULL */
+static PRStatus nspr_io_close(PRFileDesc *fd)
+{
+ const PRCloseFN close_fn = PR_GetDefaultIOMethods()->close;
+ fd->secret = NULL;
+ return close_fn(fd);
+}
+
+/* load a PKCS #11 module */
+static CURLcode nss_load_module(SECMODModule **pmod, const char *library,
+ const char *name)
+{
+ char *config_string;
+ SECMODModule *module = *pmod;
+ if(module)
+ /* already loaded */
+ return CURLE_OK;
+
+ config_string = aprintf("library=%s name=%s", library, name);
+ if(!config_string)
+ return CURLE_OUT_OF_MEMORY;
+
+ module = SECMOD_LoadUserModule(config_string, NULL, PR_FALSE);
+ free(config_string);
+
+ if(module && module->loaded) {
+ /* loaded successfully */
+ *pmod = module;
+ return CURLE_OK;
+ }
+
+ if(module)
+ SECMOD_DestroyModule(module);
+ return CURLE_FAILED_INIT;
+}
+
+/* unload a PKCS #11 module */
+static void nss_unload_module(SECMODModule **pmod)
+{
+ SECMODModule *module = *pmod;
+ if(!module)
+ /* not loaded */
+ return;
+
+ if(SECMOD_UnloadUserModule(module) != SECSuccess)
+ /* unload failed */
+ return;
+
+ SECMOD_DestroyModule(module);
+ *pmod = NULL;
+}
+
+/* data might be NULL */
+static CURLcode nss_init_core(struct Curl_easy *data, const char *cert_dir)
+{
+ NSSInitParameters initparams;
+ PRErrorCode err;
+ const char *err_name;
+
+ if(nss_context)
+ return CURLE_OK;
+
+ memset((void *) &initparams, '\0', sizeof(initparams));
+ initparams.length = sizeof(initparams);
+
+ if(cert_dir) {
+ char *certpath = aprintf("sql:%s", cert_dir);
+ if(!certpath)
+ return CURLE_OUT_OF_MEMORY;
+
+ infof(data, "Initializing NSS with certpath: %s", certpath);
+ nss_context = NSS_InitContext(certpath, "", "", "", &initparams,
+ NSS_INIT_READONLY | NSS_INIT_PK11RELOAD);
+ free(certpath);
+
+ if(nss_context)
+ return CURLE_OK;
+
+ err = PR_GetError();
+ err_name = nss_error_to_name(err);
+ infof(data, "Unable to initialize NSS database: %d (%s)", err, err_name);
+ }
+
+ infof(data, "Initializing NSS with certpath: none");
+ nss_context = NSS_InitContext("", "", "", "", &initparams, NSS_INIT_READONLY
+ | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN
+ | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD);
+ if(nss_context)
+ return CURLE_OK;
+
+ err = PR_GetError();
+ err_name = nss_error_to_name(err);
+ failf(data, "Unable to initialize NSS: %d (%s)", err, err_name);
+ return CURLE_SSL_CACERT_BADFILE;
+}
+
+/* data might be NULL */
+static CURLcode nss_setup(struct Curl_easy *data)
+{
+ char *cert_dir;
+ struct_stat st;
+ CURLcode result;
+
+ if(initialized)
+ return CURLE_OK;
+
+ /* list of all CRL items we need to destroy in nss_cleanup() */
+ Curl_llist_init(&nss_crl_list, nss_destroy_crl_item);
+
+ /* First we check if $SSL_DIR points to a valid dir */
+ cert_dir = getenv("SSL_DIR");
+ if(cert_dir) {
+ if((stat(cert_dir, &st) != 0) ||
+ (!S_ISDIR(st.st_mode))) {
+ cert_dir = NULL;
+ }
+ }
+
+ /* Now we check if the default location is a valid dir */
+ if(!cert_dir) {
+ if((stat(SSL_DIR, &st) == 0) &&
+ (S_ISDIR(st.st_mode))) {
+ cert_dir = (char *)SSL_DIR;
+ }
+ }
+
+ if(nspr_io_identity == PR_INVALID_IO_LAYER) {
+ /* allocate an identity for our own NSPR I/O layer */
+ nspr_io_identity = PR_GetUniqueIdentity("libcurl");
+ if(nspr_io_identity == PR_INVALID_IO_LAYER)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* the default methods just call down to the lower I/O layer */
+ memcpy(&nspr_io_methods, PR_GetDefaultIOMethods(),
+ sizeof(nspr_io_methods));
+
+ /* override certain methods in the table by our wrappers */
+ nspr_io_methods.recv = nspr_io_recv;
+ nspr_io_methods.send = nspr_io_send;
+ nspr_io_methods.close = nspr_io_close;
+ }
+
+ result = nss_init_core(data, cert_dir);
+ if(result)
+ return result;
+
+ if(!any_cipher_enabled())
+ NSS_SetDomesticPolicy();
+
+ initialized = 1;
+
+ return CURLE_OK;
+}
+
+/**
+ * Global SSL init
+ *
+ * @retval 0 error initializing SSL
+ * @retval 1 SSL initialized successfully
+ */
+static int nss_init(void)
+{
+ /* curl_global_init() is not thread-safe so this test is ok */
+ if(!nss_initlock) {
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ nss_initlock = PR_NewLock();
+ nss_crllock = PR_NewLock();
+ nss_findslot_lock = PR_NewLock();
+ nss_trustload_lock = PR_NewLock();
+ }
+
+ /* We will actually initialize NSS later */
+
+ return 1;
+}
+
+/* data might be NULL */
+CURLcode Curl_nss_force_init(struct Curl_easy *data)
+{
+ CURLcode result;
+ if(!nss_initlock) {
+ if(data)
+ failf(data, "unable to initialize NSS, curl_global_init() should have "
+ "been called with CURL_GLOBAL_SSL or CURL_GLOBAL_ALL");
+ return CURLE_FAILED_INIT;
+ }
+
+ PR_Lock(nss_initlock);
+ result = nss_setup(data);
+ PR_Unlock(nss_initlock);
+
+ return result;
+}
+
+/* Global cleanup */
+static void nss_cleanup(void)
+{
+ /* This function isn't required to be threadsafe and this is only done
+ * as a safety feature.
+ */
+ PR_Lock(nss_initlock);
+ if(initialized) {
+ /* Free references to client certificates held in the SSL session cache.
+ * Omitting this hampers destruction of the security module owning
+ * the certificates. */
+ SSL_ClearSessionCache();
+
+ nss_unload_module(&pem_module);
+ nss_unload_module(&trust_module);
+ NSS_ShutdownContext(nss_context);
+ nss_context = NULL;
+ }
+
+ /* destroy all CRL items */
+ Curl_llist_destroy(&nss_crl_list, NULL);
+
+ PR_Unlock(nss_initlock);
+
+ PR_DestroyLock(nss_initlock);
+ PR_DestroyLock(nss_crllock);
+ PR_DestroyLock(nss_findslot_lock);
+ PR_DestroyLock(nss_trustload_lock);
+ nss_initlock = NULL;
+
+ initialized = 0;
+}
+
+/*
+ * This function uses SSL_peek to determine connection status.
+ *
+ * Return codes:
+ * 1 means the connection is still in place
+ * 0 means the connection has been closed
+ * -1 means the connection status is unknown
+ */
+static int nss_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ int rc;
+ char buf;
+
+ (void)data;
+ DEBUGASSERT(backend);
+
+ rc =
+ PR_Recv(backend->handle, (void *)&buf, 1, PR_MSG_PEEK,
+ PR_SecondsToInterval(1));
+ if(rc > 0)
+ return 1; /* connection still in place */
+
+ if(rc == 0)
+ return 0; /* connection has been closed */
+
+ return -1; /* connection status unknown */
+}
+
+static void close_one(struct ssl_connect_data *connssl)
+{
+ /* before the cleanup, check whether we are using a client certificate */
+ struct ssl_backend_data *backend = connssl->backend;
+ bool client_cert = true;
+
+ DEBUGASSERT(backend);
+
+ client_cert = (backend->client_nickname != NULL)
+ || (backend->obj_clicert != NULL);
+
+ if(backend->handle) {
+ char buf[32];
+ /* Maybe the server has already sent a close notify alert.
+ Read it to avoid an RST on the TCP connection. */
+ (void)PR_Recv(backend->handle, buf, (int)sizeof(buf), 0,
+ PR_INTERVAL_NO_WAIT);
+ }
+
+ free(backend->client_nickname);
+ backend->client_nickname = NULL;
+
+ /* destroy all NSS objects in order to avoid failure of NSS shutdown */
+ Curl_llist_destroy(&backend->obj_list, NULL);
+ backend->obj_clicert = NULL;
+
+ if(backend->handle) {
+ if(client_cert)
+ /* A server might require different authentication based on the
+ * particular path being requested by the client. To support this
+ * scenario, we must ensure that a connection will never reuse the
+ * authentication data from a previous connection. */
+ SSL_InvalidateSession(backend->handle);
+
+ PR_Close(backend->handle);
+ backend->handle = NULL;
+ }
+}
+
+/*
+ * This function is called when an SSL connection is closed.
+ */
+static void nss_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ (void)data;
+ DEBUGASSERT(backend);
+
+ if(backend->handle) {
+ /* NSS closes the socket we previously handed to it, so we must mark it
+ as closed to avoid double close */
+ fake_sclose(cf->conn->sock[cf->sockindex]);
+ cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
+ }
+
+ close_one(connssl);
+}
+
+/* return true if NSS can provide error code (and possibly msg) for the
+ error */
+static bool is_nss_error(CURLcode err)
+{
+ switch(err) {
+ case CURLE_PEER_FAILED_VERIFICATION:
+ case CURLE_SSL_CERTPROBLEM:
+ case CURLE_SSL_CONNECT_ERROR:
+ case CURLE_SSL_ISSUER_ERROR:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/* return true if the given error code is related to a client certificate */
+static bool is_cc_error(PRInt32 err)
+{
+ switch(err) {
+ case SSL_ERROR_BAD_CERT_ALERT:
+ case SSL_ERROR_EXPIRED_CERT_ALERT:
+ case SSL_ERROR_REVOKED_CERT_ALERT:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static CURLcode nss_load_ca_certificates(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ const char *cafile = conn_config->CAfile;
+ const char *capath = conn_config->CApath;
+ bool use_trust_module;
+ CURLcode result = CURLE_OK;
+
+ /* treat empty string as unset */
+ if(cafile && !cafile[0])
+ cafile = NULL;
+ if(capath && !capath[0])
+ capath = NULL;
+
+ infof(data, " CAfile: %s", cafile ? cafile : "none");
+ infof(data, " CApath: %s", capath ? capath : "none");
+
+ /* load libnssckbi.so if no other trust roots were specified */
+ use_trust_module = !cafile && !capath;
+
+ PR_Lock(nss_trustload_lock);
+ if(use_trust_module && !trust_module) {
+ /* libnssckbi.so needed but not yet loaded --> load it! */
+ result = nss_load_module(&trust_module, trust_library, "trust");
+ infof(data, "%s %s", (result) ? "failed to load" : "loaded",
+ trust_library);
+ if(result == CURLE_FAILED_INIT)
+ /* If libnssckbi.so is not available (or fails to load), one can still
+ use CA certificates stored in NSS database. Ignore the failure. */
+ result = CURLE_OK;
+ }
+ else if(!use_trust_module && trust_module) {
+ /* libnssckbi.so not needed but already loaded --> unload it! */
+ infof(data, "unloading %s", trust_library);
+ nss_unload_module(&trust_module);
+ }
+ PR_Unlock(nss_trustload_lock);
+
+ if(cafile)
+ result = nss_load_cert(connssl, cafile, PR_TRUE);
+
+ if(result)
+ return result;
+
+ if(capath) {
+ struct_stat st;
+ if(stat(capath, &st) == -1)
+ return CURLE_SSL_CACERT_BADFILE;
+
+ if(S_ISDIR(st.st_mode)) {
+ PRDirEntry *entry;
+ PRDir *dir = PR_OpenDir(capath);
+ if(!dir)
+ return CURLE_SSL_CACERT_BADFILE;
+
+ while((entry =
+ PR_ReadDir(dir, (PRDirFlags)(PR_SKIP_BOTH | PR_SKIP_HIDDEN)))) {
+ char *fullpath = aprintf("%s/%s", capath, entry->name);
+ if(!fullpath) {
+ PR_CloseDir(dir);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(CURLE_OK != nss_load_cert(connssl, fullpath, PR_TRUE))
+ /* This is purposefully tolerant of errors so non-PEM files can
+ * be in the same directory */
+ infof(data, "failed to load '%s' from CURLOPT_CAPATH", fullpath);
+
+ free(fullpath);
+ }
+
+ PR_CloseDir(dir);
+ }
+ else
+ infof(data, "WARNING: CURLOPT_CAPATH not a directory (%s)", capath);
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode nss_sslver_from_curl(PRUint16 *nssver, long version)
+{
+ switch(version) {
+ case CURL_SSLVERSION_SSLv2:
+ *nssver = SSL_LIBRARY_VERSION_2;
+ return CURLE_OK;
+
+ case CURL_SSLVERSION_SSLv3:
+ return CURLE_NOT_BUILT_IN;
+
+ case CURL_SSLVERSION_TLSv1_0:
+ *nssver = SSL_LIBRARY_VERSION_TLS_1_0;
+ return CURLE_OK;
+
+ case CURL_SSLVERSION_TLSv1_1:
+#ifdef SSL_LIBRARY_VERSION_TLS_1_1
+ *nssver = SSL_LIBRARY_VERSION_TLS_1_1;
+ return CURLE_OK;
+#else
+ return CURLE_SSL_CONNECT_ERROR;
+#endif
+
+ case CURL_SSLVERSION_TLSv1_2:
+#ifdef SSL_LIBRARY_VERSION_TLS_1_2
+ *nssver = SSL_LIBRARY_VERSION_TLS_1_2;
+ return CURLE_OK;
+#else
+ return CURLE_SSL_CONNECT_ERROR;
+#endif
+
+ case CURL_SSLVERSION_TLSv1_3:
+#ifdef SSL_LIBRARY_VERSION_TLS_1_3
+ *nssver = SSL_LIBRARY_VERSION_TLS_1_3;
+ return CURLE_OK;
+#else
+ return CURLE_SSL_CONNECT_ERROR;
+#endif
+
+ default:
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+}
+
+static CURLcode nss_init_sslver(SSLVersionRange *sslver,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ CURLcode result;
+ const long min = conn_config->version;
+ const long max = conn_config->version_max;
+ SSLVersionRange vrange;
+
+ switch(min) {
+ case CURL_SSLVERSION_TLSv1:
+ case CURL_SSLVERSION_DEFAULT:
+ /* Bump our minimum TLS version if NSS has stricter requirements. */
+ if(SSL_VersionRangeGetDefault(ssl_variant_stream, &vrange) != SECSuccess)
+ return CURLE_SSL_CONNECT_ERROR;
+ if(sslver->min < vrange.min)
+ sslver->min = vrange.min;
+ break;
+ default:
+ result = nss_sslver_from_curl(&sslver->min, min);
+ if(result) {
+ failf(data, "unsupported min version passed via CURLOPT_SSLVERSION");
+ return result;
+ }
+ }
+
+ switch(max) {
+ case CURL_SSLVERSION_MAX_NONE:
+ case CURL_SSLVERSION_MAX_DEFAULT:
+ break;
+ default:
+ result = nss_sslver_from_curl(&sslver->max, max >> 16);
+ if(result) {
+ failf(data, "unsupported max version passed via CURLOPT_SSLVERSION");
+ return result;
+ }
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode nss_fail_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ CURLcode curlerr)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+
+ DEBUGASSERT(backend);
+
+ if(is_nss_error(curlerr)) {
+ /* read NSPR error code */
+ PRErrorCode err = PR_GetError();
+ if(is_cc_error(err))
+ curlerr = CURLE_SSL_CERTPROBLEM;
+
+ /* print the error number and error string */
+ infof(data, "NSS error %d (%s)", err, nss_error_to_name(err));
+
+ /* print a human-readable message describing the error if available */
+ nss_print_error_message(data, err);
+ }
+
+ /* cleanup on connection failure */
+ Curl_llist_destroy(&backend->obj_list, NULL);
+
+ return curlerr;
+}
+
+/* Switch the SSL socket into blocking or non-blocking mode. */
+static CURLcode nss_set_blocking(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ PRSocketOptionData sock_opt;
+ struct ssl_backend_data *backend = connssl->backend;
+
+ DEBUGASSERT(backend);
+
+ sock_opt.option = PR_SockOpt_Nonblocking;
+ sock_opt.value.non_blocking = !blocking;
+
+ if(PR_SetSocketOption(backend->handle, &sock_opt) != PR_SUCCESS)
+ return nss_fail_connect(cf, data, CURLE_SSL_CONNECT_ERROR);
+
+ return CURLE_OK;
+}
+
+static CURLcode nss_setup_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ PRFileDesc *model = NULL;
+ PRFileDesc *nspr_io = NULL;
+ PRFileDesc *nspr_io_stub = NULL;
+ PRBool ssl_no_cache;
+ PRBool ssl_cbc_random_iv;
+ curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next);
+ struct ssl_connect_data *connssl_next = cf_ssl_next?
+ cf_ssl_next->ctx : NULL;
+ CURLcode result;
+ bool second_layer = FALSE;
+ SSLVersionRange sslver_supported;
+ SSLVersionRange sslver = {
+ SSL_LIBRARY_VERSION_TLS_1_0, /* min */
+#ifdef SSL_LIBRARY_VERSION_TLS_1_3
+ SSL_LIBRARY_VERSION_TLS_1_3 /* max */
+#elif defined SSL_LIBRARY_VERSION_TLS_1_2
+ SSL_LIBRARY_VERSION_TLS_1_2
+#elif defined SSL_LIBRARY_VERSION_TLS_1_1
+ SSL_LIBRARY_VERSION_TLS_1_1
+#else
+ SSL_LIBRARY_VERSION_TLS_1_0
+#endif
+ };
+ const char *hostname = connssl->hostname;
+ char *snihost;
+
+ snihost = Curl_ssl_snihost(data, hostname, NULL);
+ if(!snihost) {
+ failf(data, "Failed to set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ DEBUGASSERT(backend);
+
+ backend->data = data;
+
+ /* list of all NSS objects we need to destroy in nss_do_close() */
+ Curl_llist_init(&backend->obj_list, nss_destroy_object);
+
+ PR_Lock(nss_initlock);
+ result = nss_setup(data);
+ if(result) {
+ PR_Unlock(nss_initlock);
+ goto error;
+ }
+
+ PK11_SetPasswordFunc(nss_get_password);
+
+ result = nss_load_module(&pem_module, pem_library, "PEM");
+ PR_Unlock(nss_initlock);
+ if(result == CURLE_FAILED_INIT)
+ infof(data, "WARNING: failed to load NSS PEM library %s. Using "
+ "OpenSSL PEM certificates will not work.", pem_library);
+ else if(result)
+ goto error;
+
+ result = CURLE_SSL_CONNECT_ERROR;
+
+ model = PR_NewTCPSocket();
+ if(!model)
+ goto error;
+ model = SSL_ImportFD(NULL, model);
+
+ if(SSL_OptionSet(model, SSL_SECURITY, PR_TRUE) != SECSuccess)
+ goto error;
+ if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess)
+ goto error;
+ if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess)
+ goto error;
+
+ /* do not use SSL cache if disabled or we are not going to verify peer */
+ ssl_no_cache = (ssl_config->primary.sessionid
+ && conn_config->verifypeer) ? PR_FALSE : PR_TRUE;
+ if(SSL_OptionSet(model, SSL_NO_CACHE, ssl_no_cache) != SECSuccess)
+ goto error;
+
+ /* enable/disable the requested SSL version(s) */
+ if(nss_init_sslver(&sslver, cf, data) != CURLE_OK)
+ goto error;
+ if(SSL_VersionRangeGetSupported(ssl_variant_stream,
+ &sslver_supported) != SECSuccess)
+ goto error;
+ if(sslver_supported.max < sslver.max && sslver_supported.max >= sslver.min) {
+ char *sslver_req_str, *sslver_supp_str;
+ sslver_req_str = nss_sslver_to_name(sslver.max);
+ sslver_supp_str = nss_sslver_to_name(sslver_supported.max);
+ if(sslver_req_str && sslver_supp_str)
+ infof(data, "Falling back from %s to max supported SSL version (%s)",
+ sslver_req_str, sslver_supp_str);
+ free(sslver_req_str);
+ free(sslver_supp_str);
+ sslver.max = sslver_supported.max;
+ }
+ if(SSL_VersionRangeSet(model, &sslver) != SECSuccess)
+ goto error;
+
+ ssl_cbc_random_iv = !ssl_config->enable_beast;
+#ifdef SSL_CBC_RANDOM_IV
+ /* unless the user explicitly asks to allow the protocol vulnerability, we
+ use the work-around */
+ if(SSL_OptionSet(model, SSL_CBC_RANDOM_IV, ssl_cbc_random_iv) != SECSuccess)
+ infof(data, "WARNING: failed to set SSL_CBC_RANDOM_IV = %d",
+ ssl_cbc_random_iv);
+#else
+ if(ssl_cbc_random_iv)
+ infof(data, "WARNING: support for SSL_CBC_RANDOM_IV not compiled in");
+#endif
+
+ if(conn_config->cipher_list) {
+ if(set_ciphers(data, model, conn_config->cipher_list) != SECSuccess) {
+ result = CURLE_SSL_CIPHER;
+ goto error;
+ }
+ }
+
+ if(!conn_config->verifypeer && conn_config->verifyhost)
+ infof(data, "WARNING: ignoring value of ssl.verifyhost");
+
+ /* bypass the default SSL_AuthCertificate() hook in case we do not want to
+ * verify peer */
+ if(SSL_AuthCertificateHook(model, nss_auth_cert_hook, cf) != SECSuccess)
+ goto error;
+
+ /* not checked yet */
+ ssl_config->certverifyresult = 0;
+
+ if(SSL_BadCertHook(model, BadCertHandler, cf) != SECSuccess)
+ goto error;
+
+ if(SSL_HandshakeCallback(model, HandshakeCallback, cf) != SECSuccess)
+ goto error;
+
+ {
+ const CURLcode rv = nss_load_ca_certificates(cf, data);
+ if((rv == CURLE_SSL_CACERT_BADFILE) && !conn_config->verifypeer)
+ /* not a fatal error because we are not going to verify the peer */
+ infof(data, "WARNING: CA certificates failed to load");
+ else if(rv) {
+ result = rv;
+ goto error;
+ }
+ }
+
+ if(ssl_config->primary.CRLfile) {
+ const CURLcode rv = nss_load_crl(ssl_config->primary.CRLfile);
+ if(rv) {
+ result = rv;
+ goto error;
+ }
+ infof(data, " CRLfile: %s", ssl_config->primary.CRLfile);
+ }
+
+ if(ssl_config->primary.clientcert) {
+ char *nickname = dup_nickname(data, ssl_config->primary.clientcert);
+ if(nickname) {
+ /* we are not going to use libnsspem.so to read the client cert */
+ backend->obj_clicert = NULL;
+ }
+ else {
+ CURLcode rv = cert_stuff(cf, data,
+ ssl_config->primary.clientcert,
+ ssl_config->key);
+ if(rv) {
+ /* failf() is already done in cert_stuff() */
+ result = rv;
+ goto error;
+ }
+ }
+
+ /* store the nickname for SelectClientCert() called during handshake */
+ backend->client_nickname = nickname;
+ }
+ else
+ backend->client_nickname = NULL;
+
+ if(SSL_GetClientAuthDataHook(model, SelectClientCert,
+ (void *)connssl) != SECSuccess) {
+ result = CURLE_SSL_CERTPROBLEM;
+ goto error;
+ }
+
+ /* Is there an SSL filter "in front" of us or are we writing directly
+ * to the socket? */
+ if(connssl_next) {
+ /* The filter should be connected by now, with full handshake */
+ DEBUGASSERT(connssl_next->backend->handle);
+ DEBUGASSERT(ssl_connection_complete == connssl_next->state);
+ /* We tell our NSS instance to use do IO with the 'next' NSS
+ * instance. This NSS instance will take ownership of the next
+ * one, including its destruction. We therefore need to `disown`
+ * the next filter's handle, once import succeeds. */
+ nspr_io = connssl_next->backend->handle;
+ second_layer = TRUE;
+ }
+ else {
+ /* wrap OS file descriptor by NSPR's file descriptor abstraction */
+ nspr_io = PR_ImportTCPSocket(sockfd);
+ if(!nspr_io)
+ goto error;
+ }
+
+ /* create our own NSPR I/O layer */
+ nspr_io_stub = PR_CreateIOLayerStub(nspr_io_identity, &nspr_io_methods);
+ if(!nspr_io_stub) {
+ if(!second_layer)
+ PR_Close(nspr_io);
+ goto error;
+ }
+
+ /* make the per-connection data accessible from NSPR I/O callbacks */
+ nspr_io_stub->secret = (void *)connssl;
+
+ /* push our new layer to the NSPR I/O stack */
+ if(PR_PushIOLayer(nspr_io, PR_TOP_IO_LAYER, nspr_io_stub) != PR_SUCCESS) {
+ if(!second_layer)
+ PR_Close(nspr_io);
+ PR_Close(nspr_io_stub);
+ goto error;
+ }
+
+ /* import our model socket onto the current I/O stack */
+ backend->handle = SSL_ImportFD(model, nspr_io);
+ if(!backend->handle) {
+ if(!second_layer)
+ PR_Close(nspr_io);
+ goto error;
+ }
+
+ PR_Close(model); /* We don't need this any more */
+ model = NULL;
+ if(connssl_next) /* steal the NSS handle we just imported successfully */
+ connssl_next->backend->handle = NULL;
+
+ /* This is the password associated with the cert that we're using */
+ if(ssl_config->key_passwd) {
+ SSL_SetPKCS11PinArg(backend->handle, ssl_config->key_passwd);
+ }
+
+#ifdef SSL_ENABLE_OCSP_STAPLING
+ if(conn_config->verifystatus) {
+ if(SSL_OptionSet(backend->handle, SSL_ENABLE_OCSP_STAPLING, PR_TRUE)
+ != SECSuccess)
+ goto error;
+ }
+#endif
+
+#ifdef SSL_ENABLE_ALPN
+ if(SSL_OptionSet(backend->handle, SSL_ENABLE_ALPN,
+ cf->conn->bits.tls_enable_alpn ? PR_TRUE : PR_FALSE)
+ != SECSuccess)
+ goto error;
+#endif
+
+#if NSSVERNUM >= 0x030f04 /* 3.15.4 */
+ if(data->set.ssl.falsestart) {
+ if(SSL_OptionSet(backend->handle, SSL_ENABLE_FALSE_START, PR_TRUE)
+ != SECSuccess)
+ goto error;
+
+ if(SSL_SetCanFalseStartCallback(backend->handle, CanFalseStartCallback,
+ data) != SECSuccess)
+ goto error;
+ }
+#endif
+
+#if defined(SSL_ENABLE_ALPN)
+ if(cf->conn->bits.tls_enable_alpn) {
+ int cur = 0;
+ unsigned char protocols[128];
+
+#ifdef USE_HTTP2
+ if(data->state.httpwant >= CURL_HTTP_VERSION_2
+#ifndef CURL_DISABLE_PROXY
+ && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy)
+#endif
+ ) {
+ protocols[cur++] = ALPN_H2_LENGTH;
+ memcpy(&protocols[cur], ALPN_H2, ALPN_H2_LENGTH);
+ cur += ALPN_H2_LENGTH;
+ }
+#endif
+ protocols[cur++] = ALPN_HTTP_1_1_LENGTH;
+ memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
+ cur += ALPN_HTTP_1_1_LENGTH;
+
+ if(SSL_SetNextProtoNego(backend->handle, protocols, cur) != SECSuccess)
+ goto error;
+ }
+#endif
+
+
+ /* Force handshake on next I/O */
+ if(SSL_ResetHandshake(backend->handle, /* asServer */ PR_FALSE)
+ != SECSuccess)
+ goto error;
+
+ /* propagate hostname to the TLS layer */
+ if(SSL_SetURL(backend->handle, snihost) != SECSuccess)
+ goto error;
+
+ /* prevent NSS from re-using the session for a different hostname */
+ if(SSL_SetSockPeerID(backend->handle, snihost) != SECSuccess)
+ goto error;
+
+ return CURLE_OK;
+
+error:
+ if(model)
+ PR_Close(model);
+
+ return nss_fail_connect(cf, data, result);
+}
+
+static CURLcode nss_do_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ CURLcode result = CURLE_SSL_CONNECT_ERROR;
+ PRUint32 timeout;
+
+ /* check timeout situation */
+ const timediff_t time_left = Curl_timeleft(data, NULL, TRUE);
+ if(time_left < 0) {
+ failf(data, "timed out before SSL handshake");
+ result = CURLE_OPERATION_TIMEDOUT;
+ goto error;
+ }
+
+ DEBUGASSERT(backend);
+
+ /* Force the handshake now */
+ timeout = PR_MillisecondsToInterval((PRUint32) time_left);
+ if(SSL_ForceHandshakeWithTimeout(backend->handle, timeout) != SECSuccess) {
+ if(PR_GetError() == PR_WOULD_BLOCK_ERROR)
+ /* blocking direction is updated by nss_update_connecting_state() */
+ return CURLE_AGAIN;
+ else if(ssl_config->certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN)
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ else if(ssl_config->certverifyresult)
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ goto error;
+ }
+
+ result = display_conn_info(data, backend->handle);
+ if(result)
+ goto error;
+
+ if(conn_config->issuercert) {
+ SECStatus ret = SECFailure;
+ char *nickname = dup_nickname(data, conn_config->issuercert);
+ if(nickname) {
+ /* we support only nicknames in case of issuercert for now */
+ ret = check_issuer_cert(backend->handle, nickname);
+ free(nickname);
+ }
+
+ if(SECFailure == ret) {
+ infof(data, "SSL certificate issuer check failed");
+ result = CURLE_SSL_ISSUER_ERROR;
+ goto error;
+ }
+ else {
+ infof(data, "SSL certificate issuer check ok");
+ }
+ }
+
+ result = cmp_peer_pubkey(connssl, Curl_ssl_cf_is_proxy(cf)?
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
+ if(result)
+ /* status already printed */
+ goto error;
+
+ return CURLE_OK;
+
+error:
+ return nss_fail_connect(cf, data, result);
+}
+
+static CURLcode nss_connect_common(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ const bool blocking = (done == NULL);
+ CURLcode result;
+
+ if(connssl->state == ssl_connection_complete) {
+ if(!blocking)
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ if(connssl->connecting_state == ssl_connect_1) {
+ result = nss_setup_connect(cf, data);
+ if(result)
+ /* we do not expect CURLE_AGAIN from nss_setup_connect() */
+ return result;
+
+ connssl->connecting_state = ssl_connect_2;
+ }
+
+ /* enable/disable blocking mode before handshake */
+ result = nss_set_blocking(cf, data, blocking);
+ if(result)
+ return result;
+
+ result = nss_do_connect(cf, data);
+ switch(result) {
+ case CURLE_OK:
+ break;
+ case CURLE_AGAIN:
+ /* CURLE_AGAIN in non-blocking mode is not an error */
+ if(!blocking)
+ return CURLE_OK;
+ else
+ return result;
+ default:
+ return result;
+ }
+
+ if(blocking) {
+ /* in blocking mode, set NSS non-blocking mode _after_ SSL handshake */
+ result = nss_set_blocking(cf, data, /* blocking */ FALSE);
+ if(result)
+ return result;
+ }
+ else
+ /* signal completed SSL handshake */
+ *done = TRUE;
+
+ connssl->state = ssl_connection_complete;
+
+ /* ssl_connect_done is never used outside, go back to the initial state */
+ connssl->connecting_state = ssl_connect_1;
+
+ return CURLE_OK;
+}
+
+static CURLcode nss_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ return nss_connect_common(cf, data, /* blocking */ NULL);
+}
+
+static CURLcode nss_connect_nonblocking(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
+{
+ return nss_connect_common(cf, data, done);
+}
+
+static ssize_t nss_send(struct Curl_cfilter *cf,
+ struct Curl_easy *data, /* transfer */
+ const void *mem, /* send this data */
+ size_t len, /* amount to write */
+ CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ ssize_t rc;
+
+ (void)data;
+ DEBUGASSERT(backend);
+
+ /* The SelectClientCert() hook uses this for infof() and failf() but the
+ handle stored in nss_setup_connect() could have already been freed. */
+ backend->data = data;
+
+ rc = PR_Send(backend->handle, mem, (int)len, 0, PR_INTERVAL_NO_WAIT);
+ if(rc < 0) {
+ PRInt32 err = PR_GetError();
+ if(err == PR_WOULD_BLOCK_ERROR)
+ *curlcode = CURLE_AGAIN;
+ else {
+ /* print the error number and error string */
+ const char *err_name = nss_error_to_name(err);
+ infof(data, "SSL write: error %d (%s)", err, err_name);
+
+ /* print a human-readable message describing the error if available */
+ nss_print_error_message(data, err);
+
+ *curlcode = (is_cc_error(err))
+ ? CURLE_SSL_CERTPROBLEM
+ : CURLE_SEND_ERROR;
+ }
+
+ return -1;
+ }
+
+ return rc; /* number of bytes */
+}
+
+static ssize_t nss_recv(struct Curl_cfilter *cf,
+ struct Curl_easy *data, /* transfer */
+ char *buf, /* store read data here */
+ size_t buffersize, /* max amount to read */
+ CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ ssize_t nread;
+
+ (void)data;
+ DEBUGASSERT(backend);
+
+ /* The SelectClientCert() hook uses this for infof() and failf() but the
+ handle stored in nss_setup_connect() could have already been freed. */
+ backend->data = data;
+
+ nread = PR_Recv(backend->handle, buf, (int)buffersize, 0,
+ PR_INTERVAL_NO_WAIT);
+ if(nread < 0) {
+ /* failed SSL read */
+ PRInt32 err = PR_GetError();
+
+ if(err == PR_WOULD_BLOCK_ERROR)
+ *curlcode = CURLE_AGAIN;
+ else {
+ /* print the error number and error string */
+ const char *err_name = nss_error_to_name(err);
+ infof(data, "SSL read: errno %d (%s)", err, err_name);
+
+ /* print a human-readable message describing the error if available */
+ nss_print_error_message(data, err);
+
+ *curlcode = (is_cc_error(err))
+ ? CURLE_SSL_CERTPROBLEM
+ : CURLE_RECV_ERROR;
+ }
+
+ return -1;
+ }
+
+ return nread;
+}
+
+static size_t nss_version(char *buffer, size_t size)
+{
+ return msnprintf(buffer, size, "NSS/%s", NSS_GetVersion());
+}
+
+/* data might be NULL */
+static int Curl_nss_seed(struct Curl_easy *data)
+{
+ /* make sure that NSS is initialized */
+ return !!Curl_nss_force_init(data);
+}
+
+/* data might be NULL */
+static CURLcode nss_random(struct Curl_easy *data,
+ unsigned char *entropy,
+ size_t length)
+{
+ Curl_nss_seed(data); /* Initiate the seed if not already done */
+
+ if(SECSuccess != PK11_GenerateRandom(entropy, curlx_uztosi(length)))
+ /* signal a failure */
+ return CURLE_FAILED_INIT;
+
+ return CURLE_OK;
+}
+
+static CURLcode nss_sha256sum(const unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *sha256sum, /* output */
+ size_t sha256len)
+{
+ PK11Context *SHA256pw = PK11_CreateDigestContext(SEC_OID_SHA256);
+ unsigned int SHA256out;
+
+ if(!SHA256pw)
+ return CURLE_NOT_BUILT_IN;
+
+ PK11_DigestOp(SHA256pw, tmp, curlx_uztoui(tmplen));
+ PK11_DigestFinal(SHA256pw, sha256sum, &SHA256out, curlx_uztoui(sha256len));
+ PK11_DestroyContext(SHA256pw, PR_TRUE);
+
+ return CURLE_OK;
+}
+
+static bool nss_cert_status_request(void)
+{
+#ifdef SSL_ENABLE_OCSP_STAPLING
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+static bool nss_false_start(void)
+{
+#if NSSVERNUM >= 0x030f04 /* 3.15.4 */
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+static void *nss_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
+{
+ struct ssl_backend_data *backend = connssl->backend;
+ (void)info;
+ DEBUGASSERT(backend);
+ return backend->handle;
+}
+
+static bool nss_attach_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+
+ if(!connssl->backend->data)
+ connssl->backend->data = data;
+ return TRUE;
+}
+
+static void nss_detach_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+
+ if(connssl->backend->data == data)
+ connssl->backend->data = NULL;
+}
+
+const struct Curl_ssl Curl_ssl_nss = {
+ { CURLSSLBACKEND_NSS, "nss" }, /* info */
+
+ SSLSUPP_CA_PATH |
+ SSLSUPP_CERTINFO |
+ SSLSUPP_PINNEDPUBKEY |
+ SSLSUPP_HTTPS_PROXY,
+
+ sizeof(struct ssl_backend_data),
+
+ nss_init, /* init */
+ nss_cleanup, /* cleanup */
+ nss_version, /* version */
+ nss_check_cxn, /* check_cxn */
+ /* NSS has no shutdown function provided and thus always fail */
+ Curl_none_shutdown, /* shutdown */
+ Curl_none_data_pending, /* data_pending */
+ nss_random, /* random */
+ nss_cert_status_request, /* cert_status_request */
+ nss_connect, /* connect */
+ nss_connect_nonblocking, /* connect_nonblocking */
+ Curl_ssl_get_select_socks, /* getsock */
+ nss_get_internals, /* get_internals */
+ nss_close, /* close_one */
+ Curl_none_close_all, /* close_all */
+ /* NSS has its own session ID cache */
+ Curl_none_session_free, /* session_free */
+ Curl_none_set_engine, /* set_engine */
+ Curl_none_set_engine_default, /* set_engine_default */
+ Curl_none_engines_list, /* engines_list */
+ nss_false_start, /* false_start */
+ nss_sha256sum, /* sha256sum */
+ nss_attach_data, /* associate_connection */
+ nss_detach_data, /* disassociate_connection */
+ NULL, /* free_multi_ssl_backend_data */
+ nss_recv, /* recv decrypted data */
+ nss_send, /* send data to encrypt */
+};
+
+#endif /* USE_NSS */
diff --git a/libs/libcurl/src/vtls/nssg.h b/libs/libcurl/src/vtls/nssg.h index 454a38f1fb..71d3bc3c2c 100644 --- a/libs/libcurl/src/vtls/nssg.h +++ b/libs/libcurl/src/vtls/nssg.h @@ -1,41 +1,41 @@ -#ifndef HEADER_CURL_NSSG_H -#define HEADER_CURL_NSSG_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_NSS -/* - * This header should only be needed to get included by vtls.c and nss.c - */ - -#include "urldata.h" - -/* initialize NSS library if not already */ -CURLcode Curl_nss_force_init(struct Curl_easy *data); - -extern const struct Curl_ssl Curl_ssl_nss; - -#endif /* USE_NSS */ -#endif /* HEADER_CURL_NSSG_H */ +#ifndef HEADER_CURL_NSSG_H
+#define HEADER_CURL_NSSG_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_NSS
+/*
+ * This header should only be needed to get included by vtls.c and nss.c
+ */
+
+#include "urldata.h"
+
+/* initialize NSS library if not already */
+CURLcode Curl_nss_force_init(struct Curl_easy *data);
+
+extern const struct Curl_ssl Curl_ssl_nss;
+
+#endif /* USE_NSS */
+#endif /* HEADER_CURL_NSSG_H */
diff --git a/libs/libcurl/src/vtls/openssl.c b/libs/libcurl/src/vtls/openssl.c index ad2efa5586..2142ff5ccd 100644 --- a/libs/libcurl/src/vtls/openssl.c +++ b/libs/libcurl/src/vtls/openssl.c @@ -1,4617 +1,4984 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - -/* - * Source file for all OpenSSL-specific code for the TLS/SSL layer. No code - * but vtls.c should ever call or use these functions. - */ - -#include "curl_setup.h" - -#if defined(USE_QUICHE) || defined(USE_OPENSSL) - -#include <limits.h> - -/* Wincrypt must be included before anything that could include OpenSSL. */ -#if defined(USE_WIN32_CRYPTO) -#include <wincrypt.h> -/* Undefine wincrypt conflicting symbols for BoringSSL. */ -#undef X509_NAME -#undef X509_EXTENSIONS -#undef PKCS7_ISSUER_AND_SERIAL -#undef PKCS7_SIGNER_INFO -#undef OCSP_REQUEST -#undef OCSP_RESPONSE -#endif - -#include "urldata.h" -#include "sendf.h" -#include "formdata.h" /* for the boundary function */ -#include "url.h" /* for the ssl config check function */ -#include "inet_pton.h" -#include "openssl.h" -#include "connect.h" -#include "slist.h" -#include "select.h" -#include "vtls.h" -#include "vauth/vauth.h" -#include "keylog.h" -#include "strcase.h" -#include "hostcheck.h" -#include "multiif.h" -#include "strerror.h" -#include "curl_printf.h" - -#include <openssl/ssl.h> -#include <openssl/rand.h> -#include <openssl/x509v3.h> -#ifndef OPENSSL_NO_DSA -#include <openssl/dsa.h> -#endif -#include <openssl/dh.h> -#include <openssl/err.h> -#include <openssl/md5.h> -#include <openssl/conf.h> -#include <openssl/bn.h> -#include <openssl/rsa.h> -#include <openssl/bio.h> -#include <openssl/buffer.h> -#include <openssl/pkcs12.h> - -#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP) -#include <openssl/ocsp.h> -#endif - -#if (OPENSSL_VERSION_NUMBER >= 0x0090700fL) && /* 0.9.7 or later */ \ - !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_UI_CONSOLE) -#define USE_OPENSSL_ENGINE -#include <openssl/engine.h> -#endif - -#include "warnless.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* Uncomment the ALLOW_RENEG line to a real #define if you want to allow TLS - renegotiations when built with BoringSSL. Renegotiating is non-compliant - with HTTP/2 and "an extremely dangerous protocol feature". Beware. - -#define ALLOW_RENEG 1 - */ - -#ifndef OPENSSL_VERSION_NUMBER -#error "OPENSSL_VERSION_NUMBER not defined" -#endif - -#ifdef USE_OPENSSL_ENGINE -#include <openssl/ui.h> -#endif - -#if OPENSSL_VERSION_NUMBER >= 0x00909000L -#define SSL_METHOD_QUAL const -#else -#define SSL_METHOD_QUAL -#endif - -#if (OPENSSL_VERSION_NUMBER >= 0x10000000L) -#define HAVE_ERR_REMOVE_THREAD_STATE 1 -#endif - -#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && /* OpenSSL 1.1.0+ */ \ - !(defined(LIBRESSL_VERSION_NUMBER) && \ - LIBRESSL_VERSION_NUMBER < 0x20700000L) -#define SSLEAY_VERSION_NUMBER OPENSSL_VERSION_NUMBER -#define HAVE_X509_GET0_EXTENSIONS 1 /* added in 1.1.0 -pre1 */ -#define HAVE_OPAQUE_EVP_PKEY 1 /* since 1.1.0 -pre3 */ -#define HAVE_OPAQUE_RSA_DSA_DH 1 /* since 1.1.0 -pre5 */ -#define CONST_EXTS const -#define HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED 1 - -/* funny typecast define due to difference in API */ -#ifdef LIBRESSL_VERSION_NUMBER -#define ARG2_X509_signature_print (X509_ALGOR *) -#else -#define ARG2_X509_signature_print -#endif - -#else -/* For OpenSSL before 1.1.0 */ -#define ASN1_STRING_get0_data(x) ASN1_STRING_data(x) -#define X509_get0_notBefore(x) X509_get_notBefore(x) -#define X509_get0_notAfter(x) X509_get_notAfter(x) -#define CONST_EXTS /* nope */ -#ifndef LIBRESSL_VERSION_NUMBER -#define OpenSSL_version_num() SSLeay() -#endif -#endif - -#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) && /* 1.0.2 or later */ \ - !(defined(LIBRESSL_VERSION_NUMBER) && \ - LIBRESSL_VERSION_NUMBER < 0x20700000L) -#define HAVE_X509_GET0_SIGNATURE 1 -#endif - -#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) /* 1.0.2 or later */ -#define HAVE_SSL_GET_SHUTDOWN 1 -#endif - -#if OPENSSL_VERSION_NUMBER >= 0x10002003L && \ - OPENSSL_VERSION_NUMBER <= 0x10002FFFL && \ - !defined(OPENSSL_NO_COMP) -#define HAVE_SSL_COMP_FREE_COMPRESSION_METHODS 1 -#endif - -#if (OPENSSL_VERSION_NUMBER < 0x0090808fL) -/* not present in older OpenSSL */ -#define OPENSSL_load_builtin_modules(x) -#endif - -#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) -#define HAVE_EVP_PKEY_GET_PARAMS 1 -#else -#define SSL_get1_peer_certificate SSL_get_peer_certificate -#endif - -#ifdef HAVE_EVP_PKEY_GET_PARAMS -#include <openssl/core_names.h> -#define DECLARE_PKEY_PARAM_BIGNUM(name) BIGNUM *name = NULL -#define FREE_PKEY_PARAM_BIGNUM(name) BN_clear_free(name) -#else -#define DECLARE_PKEY_PARAM_BIGNUM(name) const BIGNUM *name -#define FREE_PKEY_PARAM_BIGNUM(name) -#endif - -/* - * Whether SSL_CTX_set_keylog_callback is available. - * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287 - * BoringSSL: supported since d28f59c27bac (committed 2015-11-19) - * LibreSSL: unsupported in at least 2.7.2 (explicitly check for it since it - * lies and pretends to be OpenSSL 2.0.0). - */ -#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \ - !defined(LIBRESSL_VERSION_NUMBER)) || \ - defined(OPENSSL_IS_BORINGSSL) -#define HAVE_KEYLOG_CALLBACK -#endif - -/* Whether SSL_CTX_set_ciphersuites is available. - * OpenSSL: supported since 1.1.1 (commit a53b5be6a05) - * BoringSSL: no - * LibreSSL: no - */ -#if ((OPENSSL_VERSION_NUMBER >= 0x10101000L) && \ - !defined(LIBRESSL_VERSION_NUMBER) && \ - !defined(OPENSSL_IS_BORINGSSL)) -#define HAVE_SSL_CTX_SET_CIPHERSUITES -#define HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH -#endif - -/* - * Whether SSL_CTX_set1_curves_list is available. - * OpenSSL: supported since 1.0.2, see - * https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set1_groups.html - * BoringSSL: supported since 5fd1807d95f7 (committed 2016-09-30) - * LibreSSL: since 2.5.3 (April 12, 2017) - */ -#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) || \ - defined(OPENSSL_IS_BORINGSSL) -#define HAVE_SSL_CTX_SET_EC_CURVES -#endif - -#if defined(LIBRESSL_VERSION_NUMBER) -#define OSSL_PACKAGE "LibreSSL" -#elif defined(OPENSSL_IS_BORINGSSL) -#define OSSL_PACKAGE "BoringSSL" -#else -#define OSSL_PACKAGE "OpenSSL" -#endif - -#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) -/* up2date versions of OpenSSL maintain reasonably secure defaults without - * breaking compatibility, so it is better not to override the defaults in curl - */ -#define DEFAULT_CIPHER_SELECTION NULL -#else -/* ... but it is not the case with old versions of OpenSSL */ -#define DEFAULT_CIPHER_SELECTION \ - "ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH" -#endif - -#ifdef HAVE_OPENSSL_SRP -/* the function exists */ -#ifdef USE_TLS_SRP -/* the functionality is not disabled */ -#define USE_OPENSSL_SRP -#endif -#endif - -#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) -#define HAVE_RANDOM_INIT_BY_DEFAULT 1 -#endif - -#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \ - !(defined(LIBRESSL_VERSION_NUMBER) && \ - LIBRESSL_VERSION_NUMBER < 0x2070100fL) && \ - !defined(OPENSSL_IS_BORINGSSL) -#define HAVE_OPENSSL_VERSION -#endif - -struct ssl_backend_data { - struct Curl_easy *logger; /* transfer handle to pass trace logs to, only - using sockindex 0 */ - /* these ones requires specific SSL-types */ - SSL_CTX* ctx; - SSL* handle; - X509* server_cert; -#ifndef HAVE_KEYLOG_CALLBACK - /* Set to true once a valid keylog entry has been created to avoid dupes. */ - bool keylog_done; -#endif -}; - -#define push_certinfo(_label, _num) \ -do { \ - long info_len = BIO_get_mem_data(mem, &ptr); \ - Curl_ssl_push_certinfo_len(data, _num, _label, ptr, info_len); \ - if(1 != BIO_reset(mem)) \ - break; \ -} while(0) - -static void pubkey_show(struct Curl_easy *data, - BIO *mem, - int num, - const char *type, - const char *name, - const BIGNUM *bn) -{ - char *ptr; - char namebuf[32]; - - msnprintf(namebuf, sizeof(namebuf), "%s(%s)", type, name); - - if(bn) - BN_print(mem, bn); - push_certinfo(namebuf, num); -} - -#ifdef HAVE_OPAQUE_RSA_DSA_DH -#define print_pubkey_BN(_type, _name, _num) \ - pubkey_show(data, mem, _num, #_type, #_name, _name) - -#else -#define print_pubkey_BN(_type, _name, _num) \ -do { \ - if(_type->_name) { \ - pubkey_show(data, mem, _num, #_type, #_name, _type->_name); \ - } \ -} while(0) -#endif - -static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len) -{ - int i, ilen; - - ilen = (int)len; - if(ilen < 0) - return 1; /* buffer too big */ - - i = i2t_ASN1_OBJECT(buf, ilen, a); - - if(i >= ilen) - return 1; /* buffer too small */ - - return 0; -} - -static void X509V3_ext(struct Curl_easy *data, - int certnum, - CONST_EXTS STACK_OF(X509_EXTENSION) *exts) -{ - int i; - - if((int)sk_X509_EXTENSION_num(exts) <= 0) - /* no extensions, bail out */ - return; - - for(i = 0; i < (int)sk_X509_EXTENSION_num(exts); i++) { - ASN1_OBJECT *obj; - X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i); - BUF_MEM *biomem; - char namebuf[128]; - BIO *bio_out = BIO_new(BIO_s_mem()); - - if(!bio_out) - return; - - obj = X509_EXTENSION_get_object(ext); - - asn1_object_dump(obj, namebuf, sizeof(namebuf)); - - if(!X509V3_EXT_print(bio_out, ext, 0, 0)) - ASN1_STRING_print(bio_out, (ASN1_STRING *)X509_EXTENSION_get_data(ext)); - - BIO_get_mem_ptr(bio_out, &biomem); - Curl_ssl_push_certinfo_len(data, certnum, namebuf, biomem->data, - biomem->length); - BIO_free(bio_out); - } -} - -#ifdef OPENSSL_IS_BORINGSSL -typedef size_t numcert_t; -#else -typedef int numcert_t; -#endif - -CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl) -{ - CURLcode result; - STACK_OF(X509) *sk; - int i; - numcert_t numcerts; - BIO *mem; - - DEBUGASSERT(ssl); - - sk = SSL_get_peer_cert_chain(ssl); - if(!sk) { - return CURLE_OUT_OF_MEMORY; - } - - numcerts = sk_X509_num(sk); - - result = Curl_ssl_init_certinfo(data, (int)numcerts); - if(result) { - return result; - } - - mem = BIO_new(BIO_s_mem()); - if(!mem) { - return CURLE_OUT_OF_MEMORY; - } - - for(i = 0; i < (int)numcerts; i++) { - ASN1_INTEGER *num; - X509 *x = sk_X509_value(sk, i); - EVP_PKEY *pubkey = NULL; - int j; - char *ptr; - const ASN1_BIT_STRING *psig = NULL; - - X509_NAME_print_ex(mem, X509_get_subject_name(x), 0, XN_FLAG_ONELINE); - push_certinfo("Subject", i); - - X509_NAME_print_ex(mem, X509_get_issuer_name(x), 0, XN_FLAG_ONELINE); - push_certinfo("Issuer", i); - - BIO_printf(mem, "%lx", X509_get_version(x)); - push_certinfo("Version", i); - - num = X509_get_serialNumber(x); - if(num->type == V_ASN1_NEG_INTEGER) - BIO_puts(mem, "-"); - for(j = 0; j < num->length; j++) - BIO_printf(mem, "%02x", num->data[j]); - push_certinfo("Serial Number", i); - -#if defined(HAVE_X509_GET0_SIGNATURE) && defined(HAVE_X509_GET0_EXTENSIONS) - { - const X509_ALGOR *sigalg = NULL; - X509_PUBKEY *xpubkey = NULL; - ASN1_OBJECT *pubkeyoid = NULL; - - X509_get0_signature(&psig, &sigalg, x); - if(sigalg) { - i2a_ASN1_OBJECT(mem, sigalg->algorithm); - push_certinfo("Signature Algorithm", i); - } - - xpubkey = X509_get_X509_PUBKEY(x); - if(xpubkey) { - X509_PUBKEY_get0_param(&pubkeyoid, NULL, NULL, NULL, xpubkey); - if(pubkeyoid) { - i2a_ASN1_OBJECT(mem, pubkeyoid); - push_certinfo("Public Key Algorithm", i); - } - } - - X509V3_ext(data, i, X509_get0_extensions(x)); - } -#else - { - /* before OpenSSL 1.0.2 */ - X509_CINF *cinf = x->cert_info; - - i2a_ASN1_OBJECT(mem, cinf->signature->algorithm); - push_certinfo("Signature Algorithm", i); - - i2a_ASN1_OBJECT(mem, cinf->key->algor->algorithm); - push_certinfo("Public Key Algorithm", i); - - X509V3_ext(data, i, cinf->extensions); - - psig = x->signature; - } -#endif - - ASN1_TIME_print(mem, X509_get0_notBefore(x)); - push_certinfo("Start date", i); - - ASN1_TIME_print(mem, X509_get0_notAfter(x)); - push_certinfo("Expire date", i); - - pubkey = X509_get_pubkey(x); - if(!pubkey) - infof(data, " Unable to load public key"); - else { - int pktype; -#ifdef HAVE_OPAQUE_EVP_PKEY - pktype = EVP_PKEY_id(pubkey); -#else - pktype = pubkey->type; -#endif - switch(pktype) { - case EVP_PKEY_RSA: - { -#ifndef HAVE_EVP_PKEY_GET_PARAMS - RSA *rsa; -#ifdef HAVE_OPAQUE_EVP_PKEY - rsa = EVP_PKEY_get0_RSA(pubkey); -#else - rsa = pubkey->pkey.rsa; -#endif /* HAVE_OPAQUE_EVP_PKEY */ -#endif /* !HAVE_EVP_PKEY_GET_PARAMS */ - - { -#ifdef HAVE_OPAQUE_RSA_DSA_DH - DECLARE_PKEY_PARAM_BIGNUM(n); - DECLARE_PKEY_PARAM_BIGNUM(e); -#ifdef HAVE_EVP_PKEY_GET_PARAMS - EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_N, &n); - EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_E, &e); -#else - RSA_get0_key(rsa, &n, &e, NULL); -#endif /* HAVE_EVP_PKEY_GET_PARAMS */ - BIO_printf(mem, "%d", BN_num_bits(n)); -#else - BIO_printf(mem, "%d", BN_num_bits(rsa->n)); -#endif /* HAVE_OPAQUE_RSA_DSA_DH */ - push_certinfo("RSA Public Key", i); - print_pubkey_BN(rsa, n, i); - print_pubkey_BN(rsa, e, i); - FREE_PKEY_PARAM_BIGNUM(n); - FREE_PKEY_PARAM_BIGNUM(e); - } - - break; - } - case EVP_PKEY_DSA: - { -#ifndef OPENSSL_NO_DSA -#ifndef HAVE_EVP_PKEY_GET_PARAMS - DSA *dsa; -#ifdef HAVE_OPAQUE_EVP_PKEY - dsa = EVP_PKEY_get0_DSA(pubkey); -#else - dsa = pubkey->pkey.dsa; -#endif /* HAVE_OPAQUE_EVP_PKEY */ -#endif /* !HAVE_EVP_PKEY_GET_PARAMS */ - { -#ifdef HAVE_OPAQUE_RSA_DSA_DH - DECLARE_PKEY_PARAM_BIGNUM(p); - DECLARE_PKEY_PARAM_BIGNUM(q); - DECLARE_PKEY_PARAM_BIGNUM(g); - DECLARE_PKEY_PARAM_BIGNUM(pub_key); -#ifdef HAVE_EVP_PKEY_GET_PARAMS - EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p); - EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q); - EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g); - EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key); -#else - DSA_get0_pqg(dsa, &p, &q, &g); - DSA_get0_key(dsa, &pub_key, NULL); -#endif /* HAVE_EVP_PKEY_GET_PARAMS */ -#endif /* HAVE_OPAQUE_RSA_DSA_DH */ - print_pubkey_BN(dsa, p, i); - print_pubkey_BN(dsa, q, i); - print_pubkey_BN(dsa, g, i); - print_pubkey_BN(dsa, pub_key, i); - FREE_PKEY_PARAM_BIGNUM(p); - FREE_PKEY_PARAM_BIGNUM(q); - FREE_PKEY_PARAM_BIGNUM(g); - FREE_PKEY_PARAM_BIGNUM(pub_key); - } -#endif /* !OPENSSL_NO_DSA */ - break; - } - case EVP_PKEY_DH: - { -#ifndef HAVE_EVP_PKEY_GET_PARAMS - DH *dh; -#ifdef HAVE_OPAQUE_EVP_PKEY - dh = EVP_PKEY_get0_DH(pubkey); -#else - dh = pubkey->pkey.dh; -#endif /* HAVE_OPAQUE_EVP_PKEY */ -#endif /* !HAVE_EVP_PKEY_GET_PARAMS */ - { -#ifdef HAVE_OPAQUE_RSA_DSA_DH - DECLARE_PKEY_PARAM_BIGNUM(p); - DECLARE_PKEY_PARAM_BIGNUM(q); - DECLARE_PKEY_PARAM_BIGNUM(g); - DECLARE_PKEY_PARAM_BIGNUM(pub_key); -#ifdef HAVE_EVP_PKEY_GET_PARAMS - EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p); - EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q); - EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g); - EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key); -#else - DH_get0_pqg(dh, &p, &q, &g); - DH_get0_key(dh, &pub_key, NULL); -#endif /* HAVE_EVP_PKEY_GET_PARAMS */ - print_pubkey_BN(dh, p, i); - print_pubkey_BN(dh, q, i); - print_pubkey_BN(dh, g, i); -#else - print_pubkey_BN(dh, p, i); - print_pubkey_BN(dh, g, i); -#endif /* HAVE_OPAQUE_RSA_DSA_DH */ - print_pubkey_BN(dh, pub_key, i); - FREE_PKEY_PARAM_BIGNUM(p); - FREE_PKEY_PARAM_BIGNUM(q); - FREE_PKEY_PARAM_BIGNUM(g); - FREE_PKEY_PARAM_BIGNUM(pub_key); - } - break; - } - } - EVP_PKEY_free(pubkey); - } - - if(psig) { - for(j = 0; j < psig->length; j++) - BIO_printf(mem, "%02x:", psig->data[j]); - push_certinfo("Signature", i); - } - - PEM_write_bio_X509(mem, x); - push_certinfo("Cert", i); - } - - BIO_free(mem); - - return CURLE_OK; -} - -#endif /* quiche or OpenSSL */ - -#ifdef USE_OPENSSL - -static bool ossl_associate_connection(struct Curl_easy *data, - struct connectdata *conn, - int sockindex); - -/* - * Number of bytes to read from the random number seed file. This must be - * a finite value (because some entropy "files" like /dev/urandom have - * an infinite length), but must be large enough to provide enough - * entropy to properly seed OpenSSL's PRNG. - */ -#define RAND_LOAD_LENGTH 1024 - -#ifdef HAVE_KEYLOG_CALLBACK -static void ossl_keylog_callback(const SSL *ssl, const char *line) -{ - (void)ssl; - - Curl_tls_keylog_write_line(line); -} -#else -/* - * ossl_log_tls12_secret is called by libcurl to make the CLIENT_RANDOMs if the - * OpenSSL being used doesn't have native support for doing that. - */ -static void -ossl_log_tls12_secret(const SSL *ssl, bool *keylog_done) -{ - const SSL_SESSION *session = SSL_get_session(ssl); - unsigned char client_random[SSL3_RANDOM_SIZE]; - unsigned char master_key[SSL_MAX_MASTER_KEY_LENGTH]; - int master_key_length = 0; - - if(!session || *keylog_done) - return; - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ - !(defined(LIBRESSL_VERSION_NUMBER) && \ - LIBRESSL_VERSION_NUMBER < 0x20700000L) - /* ssl->s3 is not checked in openssl 1.1.0-pre6, but let's assume that - * we have a valid SSL context if we have a non-NULL session. */ - SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE); - master_key_length = (int) - SSL_SESSION_get_master_key(session, master_key, SSL_MAX_MASTER_KEY_LENGTH); -#else - if(ssl->s3 && session->master_key_length > 0) { - master_key_length = session->master_key_length; - memcpy(master_key, session->master_key, session->master_key_length); - memcpy(client_random, ssl->s3->client_random, SSL3_RANDOM_SIZE); - } -#endif - - /* The handshake has not progressed sufficiently yet, or this is a TLS 1.3 - * session (when curl was built with older OpenSSL headers and running with - * newer OpenSSL runtime libraries). */ - if(master_key_length <= 0) - return; - - *keylog_done = true; - Curl_tls_keylog_write("CLIENT_RANDOM", client_random, - master_key, master_key_length); -} -#endif /* !HAVE_KEYLOG_CALLBACK */ - -static const char *SSL_ERROR_to_str(int err) -{ - switch(err) { - case SSL_ERROR_NONE: - return "SSL_ERROR_NONE"; - case SSL_ERROR_SSL: - return "SSL_ERROR_SSL"; - case SSL_ERROR_WANT_READ: - return "SSL_ERROR_WANT_READ"; - case SSL_ERROR_WANT_WRITE: - return "SSL_ERROR_WANT_WRITE"; - case SSL_ERROR_WANT_X509_LOOKUP: - return "SSL_ERROR_WANT_X509_LOOKUP"; - case SSL_ERROR_SYSCALL: - return "SSL_ERROR_SYSCALL"; - case SSL_ERROR_ZERO_RETURN: - return "SSL_ERROR_ZERO_RETURN"; - case SSL_ERROR_WANT_CONNECT: - return "SSL_ERROR_WANT_CONNECT"; - case SSL_ERROR_WANT_ACCEPT: - return "SSL_ERROR_WANT_ACCEPT"; -#if defined(SSL_ERROR_WANT_ASYNC) - case SSL_ERROR_WANT_ASYNC: - return "SSL_ERROR_WANT_ASYNC"; -#endif -#if defined(SSL_ERROR_WANT_ASYNC_JOB) - case SSL_ERROR_WANT_ASYNC_JOB: - return "SSL_ERROR_WANT_ASYNC_JOB"; -#endif -#if defined(SSL_ERROR_WANT_EARLY) - case SSL_ERROR_WANT_EARLY: - return "SSL_ERROR_WANT_EARLY"; -#endif - default: - return "SSL_ERROR unknown"; - } -} - -/* Return error string for last OpenSSL error - */ -static char *ossl_strerror(unsigned long error, char *buf, size_t size) -{ - if(size) - *buf = '\0'; - -#ifdef OPENSSL_IS_BORINGSSL - ERR_error_string_n((uint32_t)error, buf, size); -#else - ERR_error_string_n(error, buf, size); -#endif - - if(size > 1 && !*buf) { - strncpy(buf, (error ? "Unknown error" : "No error"), size); - buf[size - 1] = '\0'; - } - - return buf; -} - -/* Return an extra data index for the transfer data. - * This index can be used with SSL_get_ex_data() and SSL_set_ex_data(). - */ -static int ossl_get_ssl_data_index(void) -{ - static int ssl_ex_data_data_index = -1; - if(ssl_ex_data_data_index < 0) { - ssl_ex_data_data_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); - } - return ssl_ex_data_data_index; -} - -/* Return an extra data index for the connection data. - * This index can be used with SSL_get_ex_data() and SSL_set_ex_data(). - */ -static int ossl_get_ssl_conn_index(void) -{ - static int ssl_ex_data_conn_index = -1; - if(ssl_ex_data_conn_index < 0) { - ssl_ex_data_conn_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); - } - return ssl_ex_data_conn_index; -} - -/* Return an extra data index for the sockindex. - * This index can be used with SSL_get_ex_data() and SSL_set_ex_data(). - */ -static int ossl_get_ssl_sockindex_index(void) -{ - static int sockindex_index = -1; - if(sockindex_index < 0) { - sockindex_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); - } - return sockindex_index; -} - -/* Return an extra data index for proxy boolean. - * This index can be used with SSL_get_ex_data() and SSL_set_ex_data(). - */ -static int ossl_get_proxy_index(void) -{ - static int proxy_index = -1; - if(proxy_index < 0) { - proxy_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); - } - return proxy_index; -} - -static int passwd_callback(char *buf, int num, int encrypting, - void *global_passwd) -{ - DEBUGASSERT(0 == encrypting); - - if(!encrypting) { - int klen = curlx_uztosi(strlen((char *)global_passwd)); - if(num > klen) { - memcpy(buf, global_passwd, klen + 1); - return klen; - } - } - return 0; -} - -/* - * rand_enough() returns TRUE if we have seeded the random engine properly. - */ -static bool rand_enough(void) -{ - return (0 != RAND_status()) ? TRUE : FALSE; -} - -static CURLcode ossl_seed(struct Curl_easy *data) -{ - /* This might get called before it has been added to a multi handle */ - if(data->multi && data->multi->ssl_seeded) - return CURLE_OK; - - if(rand_enough()) { - /* OpenSSL 1.1.0+ should return here */ - if(data->multi) - data->multi->ssl_seeded = TRUE; - return CURLE_OK; - } -#ifdef HAVE_RANDOM_INIT_BY_DEFAULT - /* with OpenSSL 1.1.0+, a failed RAND_status is a showstopper */ - failf(data, "Insufficient randomness"); - return CURLE_SSL_CONNECT_ERROR; -#else - -#ifdef RANDOM_FILE - RAND_load_file(RANDOM_FILE, RAND_LOAD_LENGTH); - if(rand_enough()) - return CURLE_OK; -#endif - -#if defined(HAVE_RAND_EGD) && defined(EGD_SOCKET) - /* available in OpenSSL 0.9.5 and later */ - /* EGD_SOCKET is set at configure time or not at all */ - { - /* If there's an option and a define, the option overrides the - define */ - int ret = RAND_egd(EGD_SOCKET); - if(-1 != ret) { - if(rand_enough()) - return CURLE_OK; - } - } -#endif - - /* fallback to a custom seeding of the PRNG using a hash based on a current - time */ - do { - unsigned char randb[64]; - size_t len = sizeof(randb); - size_t i, i_max; - for(i = 0, i_max = len / sizeof(struct curltime); i < i_max; ++i) { - struct curltime tv = Curl_now(); - Curl_wait_ms(1); - tv.tv_sec *= i + 1; - tv.tv_usec *= (unsigned int)i + 2; - tv.tv_sec ^= ((Curl_now().tv_sec + Curl_now().tv_usec) * - (i + 3)) << 8; - tv.tv_usec ^= (unsigned int) ((Curl_now().tv_sec + - Curl_now().tv_usec) * - (i + 4)) << 16; - memcpy(&randb[i * sizeof(struct curltime)], &tv, - sizeof(struct curltime)); - } - RAND_add(randb, (int)len, (double)len/2); - } while(!rand_enough()); - - { - /* generates a default path for the random seed file */ - char fname[256]; - fname[0] = 0; /* blank it first */ - RAND_file_name(fname, sizeof(fname)); - if(fname[0]) { - /* we got a file name to try */ - RAND_load_file(fname, RAND_LOAD_LENGTH); - if(rand_enough()) - return CURLE_OK; - } - } - - infof(data, "libcurl is now using a weak random seed"); - return (rand_enough() ? CURLE_OK : - CURLE_SSL_CONNECT_ERROR /* confusing error code */); -#endif -} - -#ifndef SSL_FILETYPE_ENGINE -#define SSL_FILETYPE_ENGINE 42 -#endif -#ifndef SSL_FILETYPE_PKCS12 -#define SSL_FILETYPE_PKCS12 43 -#endif -static int do_file_type(const char *type) -{ - if(!type || !type[0]) - return SSL_FILETYPE_PEM; - if(strcasecompare(type, "PEM")) - return SSL_FILETYPE_PEM; - if(strcasecompare(type, "DER")) - return SSL_FILETYPE_ASN1; - if(strcasecompare(type, "ENG")) - return SSL_FILETYPE_ENGINE; - if(strcasecompare(type, "P12")) - return SSL_FILETYPE_PKCS12; - return -1; -} - -#ifdef USE_OPENSSL_ENGINE -/* - * Supply default password to the engine user interface conversation. - * The password is passed by OpenSSL engine from ENGINE_load_private_key() - * last argument to the ui and can be obtained by UI_get0_user_data(ui) here. - */ -static int ssl_ui_reader(UI *ui, UI_STRING *uis) -{ - const char *password; - switch(UI_get_string_type(uis)) { - case UIT_PROMPT: - case UIT_VERIFY: - password = (const char *)UI_get0_user_data(ui); - if(password && (UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD)) { - UI_set_result(ui, uis, password); - return 1; - } - default: - break; - } - return (UI_method_get_reader(UI_OpenSSL()))(ui, uis); -} - -/* - * Suppress interactive request for a default password if available. - */ -static int ssl_ui_writer(UI *ui, UI_STRING *uis) -{ - switch(UI_get_string_type(uis)) { - case UIT_PROMPT: - case UIT_VERIFY: - if(UI_get0_user_data(ui) && - (UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD)) { - return 1; - } - default: - break; - } - return (UI_method_get_writer(UI_OpenSSL()))(ui, uis); -} - -/* - * Check if a given string is a PKCS#11 URI - */ -static bool is_pkcs11_uri(const char *string) -{ - return (string && strncasecompare(string, "pkcs11:", 7)); -} - -#endif - -static CURLcode ossl_set_engine(struct Curl_easy *data, const char *engine); - -static int -SSL_CTX_use_certificate_blob(SSL_CTX *ctx, const struct curl_blob *blob, - int type, const char *key_passwd) -{ - int ret = 0; - X509 *x = NULL; - /* the typecast of blob->len is fine since it is guaranteed to never be - larger than CURL_MAX_INPUT_LENGTH */ - BIO *in = BIO_new_mem_buf(blob->data, (int)(blob->len)); - if(!in) - return CURLE_OUT_OF_MEMORY; - - if(type == SSL_FILETYPE_ASN1) { - /* j = ERR_R_ASN1_LIB; */ - x = d2i_X509_bio(in, NULL); - } - else if(type == SSL_FILETYPE_PEM) { - /* ERR_R_PEM_LIB; */ - x = PEM_read_bio_X509(in, NULL, - passwd_callback, (void *)key_passwd); - } - else { - ret = 0; - goto end; - } - - if(!x) { - ret = 0; - goto end; - } - - ret = SSL_CTX_use_certificate(ctx, x); - end: - X509_free(x); - BIO_free(in); - return ret; -} - -static int -SSL_CTX_use_PrivateKey_blob(SSL_CTX *ctx, const struct curl_blob *blob, - int type, const char *key_passwd) -{ - int ret = 0; - EVP_PKEY *pkey = NULL; - BIO *in = BIO_new_mem_buf(blob->data, (int)(blob->len)); - if(!in) - return CURLE_OUT_OF_MEMORY; - - if(type == SSL_FILETYPE_PEM) - pkey = PEM_read_bio_PrivateKey(in, NULL, passwd_callback, - (void *)key_passwd); - else if(type == SSL_FILETYPE_ASN1) - pkey = d2i_PrivateKey_bio(in, NULL); - else { - ret = 0; - goto end; - } - if(!pkey) { - ret = 0; - goto end; - } - ret = SSL_CTX_use_PrivateKey(ctx, pkey); - EVP_PKEY_free(pkey); - end: - BIO_free(in); - return ret; -} - -static int -SSL_CTX_use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob, - const char *key_passwd) -{ -/* SSL_CTX_add1_chain_cert introduced in OpenSSL 1.0.2 */ -#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) && /* OpenSSL 1.0.2 or later */ \ - !(defined(LIBRESSL_VERSION_NUMBER) && \ - (LIBRESSL_VERSION_NUMBER < 0x2090100fL)) /* LibreSSL 2.9.1 or later */ - int ret = 0; - X509 *x = NULL; - void *passwd_callback_userdata = (void *)key_passwd; - BIO *in = BIO_new_mem_buf(blob->data, (int)(blob->len)); - if(!in) - return CURLE_OUT_OF_MEMORY; - - ERR_clear_error(); - - x = PEM_read_bio_X509_AUX(in, NULL, - passwd_callback, (void *)key_passwd); - - if(!x) { - ret = 0; - goto end; - } - - ret = SSL_CTX_use_certificate(ctx, x); - - if(ERR_peek_error() != 0) - ret = 0; - - if(ret) { - X509 *ca; - unsigned long err; - - if(!SSL_CTX_clear_chain_certs(ctx)) { - ret = 0; - goto end; - } - - while((ca = PEM_read_bio_X509(in, NULL, passwd_callback, - passwd_callback_userdata)) - != NULL) { - - if(!SSL_CTX_add0_chain_cert(ctx, ca)) { - X509_free(ca); - ret = 0; - goto end; - } - } - - err = ERR_peek_last_error(); - if((ERR_GET_LIB(err) == ERR_LIB_PEM) && - (ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) - ERR_clear_error(); - else - ret = 0; - } - - end: - X509_free(x); - BIO_free(in); - return ret; -#else - (void)ctx; /* unused */ - (void)blob; /* unused */ - (void)key_passwd; /* unused */ - return 0; -#endif -} - -static -int cert_stuff(struct Curl_easy *data, - SSL_CTX* ctx, - char *cert_file, - const struct curl_blob *cert_blob, - const char *cert_type, - char *key_file, - const struct curl_blob *key_blob, - const char *key_type, - char *key_passwd) -{ - char error_buffer[256]; - bool check_privkey = TRUE; - - int file_type = do_file_type(cert_type); - - if(cert_file || cert_blob || (file_type == SSL_FILETYPE_ENGINE)) { - SSL *ssl; - X509 *x509; - int cert_done = 0; - int cert_use_result; - - if(key_passwd) { - /* set the password in the callback userdata */ - SSL_CTX_set_default_passwd_cb_userdata(ctx, key_passwd); - /* Set passwd callback: */ - SSL_CTX_set_default_passwd_cb(ctx, passwd_callback); - } - - - switch(file_type) { - case SSL_FILETYPE_PEM: - /* SSL_CTX_use_certificate_chain_file() only works on PEM files */ - cert_use_result = cert_blob ? - SSL_CTX_use_certificate_chain_blob(ctx, cert_blob, key_passwd) : - SSL_CTX_use_certificate_chain_file(ctx, cert_file); - if(cert_use_result != 1) { - failf(data, - "could not load PEM client certificate from %s, " OSSL_PACKAGE - " error %s, " - "(no key found, wrong pass phrase, or wrong file format?)", - (cert_blob ? "CURLOPT_SSLCERT_BLOB" : cert_file), - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); - return 0; - } - break; - - case SSL_FILETYPE_ASN1: - /* SSL_CTX_use_certificate_file() works with either PEM or ASN1, but - we use the case above for PEM so this can only be performed with - ASN1 files. */ - - cert_use_result = cert_blob ? - SSL_CTX_use_certificate_blob(ctx, cert_blob, - file_type, key_passwd) : - SSL_CTX_use_certificate_file(ctx, cert_file, file_type); - if(cert_use_result != 1) { - failf(data, - "could not load ASN1 client certificate from %s, " OSSL_PACKAGE - " error %s, " - "(no key found, wrong pass phrase, or wrong file format?)", - (cert_blob ? "CURLOPT_SSLCERT_BLOB" : cert_file), - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); - return 0; - } - break; - case SSL_FILETYPE_ENGINE: -#if defined(USE_OPENSSL_ENGINE) && defined(ENGINE_CTRL_GET_CMD_FROM_NAME) - { - /* Implicitly use pkcs11 engine if none was provided and the - * cert_file is a PKCS#11 URI */ - if(!data->state.engine) { - if(is_pkcs11_uri(cert_file)) { - if(ossl_set_engine(data, "pkcs11") != CURLE_OK) { - return 0; - } - } - } - - if(data->state.engine) { - const char *cmd_name = "LOAD_CERT_CTRL"; - struct { - const char *cert_id; - X509 *cert; - } params; - - params.cert_id = cert_file; - params.cert = NULL; - - /* Does the engine supports LOAD_CERT_CTRL ? */ - if(!ENGINE_ctrl(data->state.engine, ENGINE_CTRL_GET_CMD_FROM_NAME, - 0, (void *)cmd_name, NULL)) { - failf(data, "ssl engine does not support loading certificates"); - return 0; - } - - /* Load the certificate from the engine */ - if(!ENGINE_ctrl_cmd(data->state.engine, cmd_name, - 0, ¶ms, NULL, 1)) { - failf(data, "ssl engine cannot load client cert with id" - " '%s' [%s]", cert_file, - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); - return 0; - } - - if(!params.cert) { - failf(data, "ssl engine didn't initialized the certificate " - "properly."); - return 0; - } - - if(SSL_CTX_use_certificate(ctx, params.cert) != 1) { - failf(data, "unable to set client certificate [%s]", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); - return 0; - } - X509_free(params.cert); /* we don't need the handle any more... */ - } - else { - failf(data, "crypto engine not set, can't load certificate"); - return 0; - } - } - break; -#else - failf(data, "file type ENG for certificate not implemented"); - return 0; -#endif - - case SSL_FILETYPE_PKCS12: - { - BIO *cert_bio = NULL; - PKCS12 *p12 = NULL; - EVP_PKEY *pri; - STACK_OF(X509) *ca = NULL; - if(cert_blob) { - cert_bio = BIO_new_mem_buf(cert_blob->data, (int)(cert_blob->len)); - if(!cert_bio) { - failf(data, - "BIO_new_mem_buf NULL, " OSSL_PACKAGE - " error %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); - return 0; - } - } - else { - cert_bio = BIO_new(BIO_s_file()); - if(!cert_bio) { - failf(data, - "BIO_new return NULL, " OSSL_PACKAGE - " error %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); - return 0; - } - - if(BIO_read_filename(cert_bio, cert_file) <= 0) { - failf(data, "could not open PKCS12 file '%s'", cert_file); - BIO_free(cert_bio); - return 0; - } - } - - p12 = d2i_PKCS12_bio(cert_bio, NULL); - BIO_free(cert_bio); - - if(!p12) { - failf(data, "error reading PKCS12 file '%s'", - cert_blob ? "(memory blob)" : cert_file); - return 0; - } - - PKCS12_PBE_add(); - - if(!PKCS12_parse(p12, key_passwd, &pri, &x509, - &ca)) { - failf(data, - "could not parse PKCS12 file, check password, " OSSL_PACKAGE - " error %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); - PKCS12_free(p12); - return 0; - } - - PKCS12_free(p12); - - if(SSL_CTX_use_certificate(ctx, x509) != 1) { - failf(data, - "could not load PKCS12 client certificate, " OSSL_PACKAGE - " error %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); - goto fail; - } - - if(SSL_CTX_use_PrivateKey(ctx, pri) != 1) { - failf(data, "unable to use private key from PKCS12 file '%s'", - cert_file); - goto fail; - } - - if(!SSL_CTX_check_private_key (ctx)) { - failf(data, "private key from PKCS12 file '%s' " - "does not match certificate in same file", cert_file); - goto fail; - } - /* Set Certificate Verification chain */ - if(ca) { - while(sk_X509_num(ca)) { - /* - * Note that sk_X509_pop() is used below to make sure the cert is - * removed from the stack properly before getting passed to - * SSL_CTX_add_extra_chain_cert(), which takes ownership. Previously - * we used sk_X509_value() instead, but then we'd clean it in the - * subsequent sk_X509_pop_free() call. - */ - X509 *x = sk_X509_pop(ca); - if(!SSL_CTX_add_client_CA(ctx, x)) { - X509_free(x); - failf(data, "cannot add certificate to client CA list"); - goto fail; - } - if(!SSL_CTX_add_extra_chain_cert(ctx, x)) { - X509_free(x); - failf(data, "cannot add certificate to certificate chain"); - goto fail; - } - } - } - - cert_done = 1; - fail: - EVP_PKEY_free(pri); - X509_free(x509); - sk_X509_pop_free(ca, X509_free); - if(!cert_done) - return 0; /* failure! */ - break; - } - default: - failf(data, "not supported file type '%s' for certificate", cert_type); - return 0; - } - - if((!key_file) && (!key_blob)) { - key_file = cert_file; - key_blob = cert_blob; - } - else - file_type = do_file_type(key_type); - - switch(file_type) { - case SSL_FILETYPE_PEM: - if(cert_done) - break; - /* FALLTHROUGH */ - case SSL_FILETYPE_ASN1: - cert_use_result = key_blob ? - SSL_CTX_use_PrivateKey_blob(ctx, key_blob, file_type, key_passwd) : - SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type); - if(cert_use_result != 1) { - failf(data, "unable to set private key file: '%s' type %s", - key_file?key_file:"(memory blob)", key_type?key_type:"PEM"); - return 0; - } - break; - case SSL_FILETYPE_ENGINE: -#ifdef USE_OPENSSL_ENGINE - { /* XXXX still needs some work */ - EVP_PKEY *priv_key = NULL; - - /* Implicitly use pkcs11 engine if none was provided and the - * key_file is a PKCS#11 URI */ - if(!data->state.engine) { - if(is_pkcs11_uri(key_file)) { - if(ossl_set_engine(data, "pkcs11") != CURLE_OK) { - return 0; - } - } - } - - if(data->state.engine) { - UI_METHOD *ui_method = - UI_create_method((char *)"curl user interface"); - if(!ui_method) { - failf(data, "unable do create " OSSL_PACKAGE - " user-interface method"); - return 0; - } - UI_method_set_opener(ui_method, UI_method_get_opener(UI_OpenSSL())); - UI_method_set_closer(ui_method, UI_method_get_closer(UI_OpenSSL())); - UI_method_set_reader(ui_method, ssl_ui_reader); - UI_method_set_writer(ui_method, ssl_ui_writer); - /* the typecast below was added to please mingw32 */ - priv_key = (EVP_PKEY *) - ENGINE_load_private_key(data->state.engine, key_file, - ui_method, - key_passwd); - UI_destroy_method(ui_method); - if(!priv_key) { - failf(data, "failed to load private key from crypto engine"); - return 0; - } - if(SSL_CTX_use_PrivateKey(ctx, priv_key) != 1) { - failf(data, "unable to set private key"); - EVP_PKEY_free(priv_key); - return 0; - } - EVP_PKEY_free(priv_key); /* we don't need the handle any more... */ - } - else { - failf(data, "crypto engine not set, can't load private key"); - return 0; - } - } - break; -#else - failf(data, "file type ENG for private key not supported"); - return 0; -#endif - case SSL_FILETYPE_PKCS12: - if(!cert_done) { - failf(data, "file type P12 for private key not supported"); - return 0; - } - break; - default: - failf(data, "not supported file type for private key"); - return 0; - } - - ssl = SSL_new(ctx); - if(!ssl) { - failf(data, "unable to create an SSL structure"); - return 0; - } - - x509 = SSL_get_certificate(ssl); - - /* This version was provided by Evan Jordan and is supposed to not - leak memory as the previous version: */ - if(x509) { - EVP_PKEY *pktmp = X509_get_pubkey(x509); - EVP_PKEY_copy_parameters(pktmp, SSL_get_privatekey(ssl)); - EVP_PKEY_free(pktmp); - } - -#if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_IS_BORINGSSL) && \ - !defined(OPENSSL_NO_DEPRECATED_3_0) - { - /* If RSA is used, don't check the private key if its flags indicate - * it doesn't support it. */ - EVP_PKEY *priv_key = SSL_get_privatekey(ssl); - int pktype; -#ifdef HAVE_OPAQUE_EVP_PKEY - pktype = EVP_PKEY_id(priv_key); -#else - pktype = priv_key->type; -#endif - if(pktype == EVP_PKEY_RSA) { - RSA *rsa = EVP_PKEY_get1_RSA(priv_key); - if(RSA_flags(rsa) & RSA_METHOD_FLAG_NO_CHECK) - check_privkey = FALSE; - RSA_free(rsa); /* Decrement reference count */ - } - } -#endif - - SSL_free(ssl); - - /* If we are using DSA, we can copy the parameters from - * the private key */ - - if(check_privkey == TRUE) { - /* Now we know that a key and cert have been set against - * the SSL context */ - if(!SSL_CTX_check_private_key(ctx)) { - failf(data, "Private key does not match the certificate public key"); - return 0; - } - } - } - return 1; -} - -CURLcode Curl_ossl_set_client_cert(struct Curl_easy *data, SSL_CTX *ctx, - char *cert_file, - const struct curl_blob *cert_blob, - const char *cert_type, char *key_file, - const struct curl_blob *key_blob, - const char *key_type, char *key_passwd) -{ - int rv = cert_stuff(data, ctx, cert_file, cert_blob, cert_type, key_file, - key_blob, key_type, key_passwd); - if(rv != 1) { - return CURLE_SSL_CERTPROBLEM; - } - - return CURLE_OK; -} - -/* returns non-zero on failure */ -static int x509_name_oneline(X509_NAME *a, char *buf, size_t size) -{ - BIO *bio_out = BIO_new(BIO_s_mem()); - BUF_MEM *biomem; - int rc; - - if(!bio_out) - return 1; /* alloc failed! */ - - rc = X509_NAME_print_ex(bio_out, a, 0, XN_FLAG_SEP_SPLUS_SPC); - BIO_get_mem_ptr(bio_out, &biomem); - - if((size_t)biomem->length < size) - size = biomem->length; - else - size--; /* don't overwrite the buffer end */ - - memcpy(buf, biomem->data, size); - buf[size] = 0; - - BIO_free(bio_out); - - return !rc; -} - -/** - * Global SSL init - * - * @retval 0 error initializing SSL - * @retval 1 SSL initialized successfully - */ -static int ossl_init(void) -{ -#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \ - !defined(LIBRESSL_VERSION_NUMBER) - const uint64_t flags = -#ifdef OPENSSL_INIT_ENGINE_ALL_BUILTIN - /* not present in BoringSSL */ - OPENSSL_INIT_ENGINE_ALL_BUILTIN | -#endif -#ifdef CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG - OPENSSL_INIT_NO_LOAD_CONFIG | -#else - OPENSSL_INIT_LOAD_CONFIG | -#endif - 0; - OPENSSL_init_ssl(flags, NULL); -#else - OPENSSL_load_builtin_modules(); - -#ifdef USE_OPENSSL_ENGINE - ENGINE_load_builtin_engines(); -#endif - -/* CONF_MFLAGS_DEFAULT_SECTION was introduced some time between 0.9.8b and - 0.9.8e */ -#ifndef CONF_MFLAGS_DEFAULT_SECTION -#define CONF_MFLAGS_DEFAULT_SECTION 0x0 -#endif - -#ifndef CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG - CONF_modules_load_file(NULL, NULL, - CONF_MFLAGS_DEFAULT_SECTION| - CONF_MFLAGS_IGNORE_MISSING_FILE); -#endif - - /* Let's get nice error messages */ - SSL_load_error_strings(); - - /* Init the global ciphers and digests */ - if(!SSLeay_add_ssl_algorithms()) - return 0; - - OpenSSL_add_all_algorithms(); -#endif - - Curl_tls_keylog_open(); - - /* Initialize the extra data indexes */ - if(ossl_get_ssl_data_index() < 0 || ossl_get_ssl_conn_index() < 0 || - ossl_get_ssl_sockindex_index() < 0 || ossl_get_proxy_index() < 0) - return 0; - - return 1; -} - -/* Global cleanup */ -static void ossl_cleanup(void) -{ -#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \ - !defined(LIBRESSL_VERSION_NUMBER) - /* OpenSSL 1.1 deprecates all these cleanup functions and - turns them into no-ops in OpenSSL 1.0 compatibility mode */ -#else - /* Free ciphers and digests lists */ - EVP_cleanup(); - -#ifdef USE_OPENSSL_ENGINE - /* Free engine list */ - ENGINE_cleanup(); -#endif - - /* Free OpenSSL error strings */ - ERR_free_strings(); - - /* Free thread local error state, destroying hash upon zero refcount */ -#ifdef HAVE_ERR_REMOVE_THREAD_STATE - ERR_remove_thread_state(NULL); -#else - ERR_remove_state(0); -#endif - - /* Free all memory allocated by all configuration modules */ - CONF_modules_free(); - -#ifdef HAVE_SSL_COMP_FREE_COMPRESSION_METHODS - SSL_COMP_free_compression_methods(); -#endif -#endif - - Curl_tls_keylog_close(); -} - -/* - * This function is used to determine connection status. - * - * Return codes: - * 1 means the connection is still in place - * 0 means the connection has been closed - * -1 means the connection status is unknown - */ -static int ossl_check_cxn(struct connectdata *conn) -{ - /* SSL_peek takes data out of the raw recv buffer without peeking so we use - recv MSG_PEEK instead. Bug #795 */ -#ifdef MSG_PEEK - char buf; - ssize_t nread; - nread = recv((RECV_TYPE_ARG1)conn->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf, - (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK); - if(nread == 0) - return 0; /* connection has been closed */ - if(nread == 1) - return 1; /* connection still in place */ - else if(nread == -1) { - int err = SOCKERRNO; - if(err == EINPROGRESS || -#if defined(EAGAIN) && (EAGAIN != EWOULDBLOCK) - err == EAGAIN || -#endif - err == EWOULDBLOCK) - return 1; /* connection still in place */ - if(err == ECONNRESET || -#ifdef ECONNABORTED - err == ECONNABORTED || -#endif -#ifdef ENETDOWN - err == ENETDOWN || -#endif -#ifdef ENETRESET - err == ENETRESET || -#endif -#ifdef ESHUTDOWN - err == ESHUTDOWN || -#endif -#ifdef ETIMEDOUT - err == ETIMEDOUT || -#endif - err == ENOTCONN) - return 0; /* connection has been closed */ - } -#endif - return -1; /* connection status unknown */ -} - -/* Selects an OpenSSL crypto engine - */ -static CURLcode ossl_set_engine(struct Curl_easy *data, const char *engine) -{ -#ifdef USE_OPENSSL_ENGINE - ENGINE *e; - -#if OPENSSL_VERSION_NUMBER >= 0x00909000L - e = ENGINE_by_id(engine); -#else - /* avoid memory leak */ - for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)) { - const char *e_id = ENGINE_get_id(e); - if(!strcmp(engine, e_id)) - break; - } -#endif - - if(!e) { - failf(data, "SSL Engine '%s' not found", engine); - return CURLE_SSL_ENGINE_NOTFOUND; - } - - if(data->state.engine) { - ENGINE_finish(data->state.engine); - ENGINE_free(data->state.engine); - data->state.engine = NULL; - } - if(!ENGINE_init(e)) { - char buf[256]; - - ENGINE_free(e); - failf(data, "Failed to initialise SSL Engine '%s': %s", - engine, ossl_strerror(ERR_get_error(), buf, sizeof(buf))); - return CURLE_SSL_ENGINE_INITFAILED; - } - data->state.engine = e; - return CURLE_OK; -#else - (void)engine; - failf(data, "SSL Engine not supported"); - return CURLE_SSL_ENGINE_NOTFOUND; -#endif -} - -/* Sets engine as default for all SSL operations - */ -static CURLcode ossl_set_engine_default(struct Curl_easy *data) -{ -#ifdef USE_OPENSSL_ENGINE - if(data->state.engine) { - if(ENGINE_set_default(data->state.engine, ENGINE_METHOD_ALL) > 0) { - infof(data, "set default crypto engine '%s'", - ENGINE_get_id(data->state.engine)); - } - else { - failf(data, "set default crypto engine '%s' failed", - ENGINE_get_id(data->state.engine)); - return CURLE_SSL_ENGINE_SETFAILED; - } - } -#else - (void) data; -#endif - return CURLE_OK; -} - -/* Return list of OpenSSL crypto engine names. - */ -static struct curl_slist *ossl_engines_list(struct Curl_easy *data) -{ - struct curl_slist *list = NULL; -#ifdef USE_OPENSSL_ENGINE - struct curl_slist *beg; - ENGINE *e; - - for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)) { - beg = curl_slist_append(list, ENGINE_get_id(e)); - if(!beg) { - curl_slist_free_all(list); - return NULL; - } - list = beg; - } -#endif - (void) data; - return list; -} - -#define set_logger(conn, data) \ - conn->ssl[0].backend->logger = data - -static void ossl_closeone(struct Curl_easy *data, - struct connectdata *conn, - struct ssl_connect_data *connssl) -{ - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - if(backend->handle) { - char buf[32]; - set_logger(conn, data); - /* - * The conn->sock[0] socket is passed to openssl with SSL_set_fd(). Make - * sure the socket is not closed before calling OpenSSL functions that - * will use it. - */ - DEBUGASSERT(conn->sock[FIRSTSOCKET] != CURL_SOCKET_BAD); - - /* Maybe the server has already sent a close notify alert. - Read it to avoid an RST on the TCP connection. */ - (void)SSL_read(backend->handle, buf, (int)sizeof(buf)); - - (void)SSL_shutdown(backend->handle); - SSL_set_connect_state(backend->handle); - - SSL_free(backend->handle); - backend->handle = NULL; - } - if(backend->ctx) { - SSL_CTX_free(backend->ctx); - backend->ctx = NULL; - } -} - -/* - * This function is called when an SSL connection is closed. - */ -static void ossl_close(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - ossl_closeone(data, conn, &conn->ssl[sockindex]); -#ifndef CURL_DISABLE_PROXY - ossl_closeone(data, conn, &conn->proxy_ssl[sockindex]); -#endif -} - -/* - * This function is called to shut down the SSL layer but keep the - * socket open (CCC - Clear Command Channel) - */ -static int ossl_shutdown(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - int retval = 0; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - char buf[256]; /* We will use this for the OpenSSL error buffer, so it has - to be at least 256 bytes long. */ - unsigned long sslerror; - ssize_t nread; - int buffsize; - int err; - bool done = FALSE; - struct ssl_backend_data *backend = connssl->backend; - int loop = 10; - - DEBUGASSERT(backend); - -#ifndef CURL_DISABLE_FTP - /* This has only been tested on the proftpd server, and the mod_tls code - sends a close notify alert without waiting for a close notify alert in - response. Thus we wait for a close notify alert from the server, but - we do not send one. Let's hope other servers do the same... */ - - if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE) - (void)SSL_shutdown(backend->handle); -#endif - - if(backend->handle) { - buffsize = (int)sizeof(buf); - while(!done && loop--) { - int what = SOCKET_READABLE(conn->sock[sockindex], - SSL_SHUTDOWN_TIMEOUT); - if(what > 0) { - ERR_clear_error(); - - /* Something to read, let's do it and hope that it is the close - notify alert from the server */ - nread = (ssize_t)SSL_read(backend->handle, buf, buffsize); - err = SSL_get_error(backend->handle, (int)nread); - - switch(err) { - case SSL_ERROR_NONE: /* this is not an error */ - case SSL_ERROR_ZERO_RETURN: /* no more data */ - /* This is the expected response. There was no data but only - the close notify alert */ - done = TRUE; - break; - case SSL_ERROR_WANT_READ: - /* there's data pending, re-invoke SSL_read() */ - infof(data, "SSL_ERROR_WANT_READ"); - break; - case SSL_ERROR_WANT_WRITE: - /* SSL wants a write. Really odd. Let's bail out. */ - infof(data, "SSL_ERROR_WANT_WRITE"); - done = TRUE; - break; - default: - /* openssl/ssl.h says "look at error stack/return value/errno" */ - sslerror = ERR_get_error(); - failf(data, OSSL_PACKAGE " SSL_read on shutdown: %s, errno %d", - (sslerror ? - ossl_strerror(sslerror, buf, sizeof(buf)) : - SSL_ERROR_to_str(err)), - SOCKERRNO); - done = TRUE; - break; - } - } - else if(0 == what) { - /* timeout */ - failf(data, "SSL shutdown timeout"); - done = TRUE; - } - else { - /* anything that gets here is fatally bad */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - retval = -1; - done = TRUE; - } - } /* while()-loop for the select() */ - - if(data->set.verbose) { -#ifdef HAVE_SSL_GET_SHUTDOWN - switch(SSL_get_shutdown(backend->handle)) { - case SSL_SENT_SHUTDOWN: - infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN"); - break; - case SSL_RECEIVED_SHUTDOWN: - infof(data, "SSL_get_shutdown() returned SSL_RECEIVED_SHUTDOWN"); - break; - case SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN: - infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN|" - "SSL_RECEIVED__SHUTDOWN"); - break; - } -#endif - } - - SSL_free(backend->handle); - backend->handle = NULL; - } - return retval; -} - -static void ossl_session_free(void *ptr) -{ - /* free the ID */ - SSL_SESSION_free(ptr); -} - -/* - * This function is called when the 'data' struct is going away. Close - * down everything and free all resources! - */ -static void ossl_close_all(struct Curl_easy *data) -{ -#ifdef USE_OPENSSL_ENGINE - if(data->state.engine) { - ENGINE_finish(data->state.engine); - ENGINE_free(data->state.engine); - data->state.engine = NULL; - } -#else - (void)data; -#endif -#if !defined(HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED) && \ - defined(HAVE_ERR_REMOVE_THREAD_STATE) - /* OpenSSL 1.0.1 and 1.0.2 build an error queue that is stored per-thread - so we need to clean it here in case the thread will be killed. All OpenSSL - code should extract the error in association with the error so clearing - this queue here should be harmless at worst. */ - ERR_remove_thread_state(NULL); -#endif -} - -/* ====================================================== */ - -/* - * Match subjectAltName against the host name. - */ -static bool subj_alt_hostcheck(struct Curl_easy *data, - const char *match_pattern, - size_t matchlen, - const char *hostname, - size_t hostlen, - const char *dispname) -{ -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)dispname; - (void)data; -#endif - if(Curl_cert_hostcheck(match_pattern, matchlen, hostname, hostlen)) { - infof(data, " subjectAltName: host \"%s\" matched cert's \"%s\"", - dispname, match_pattern); - return TRUE; - } - return FALSE; -} - -/* Quote from RFC2818 section 3.1 "Server Identity" - - If a subjectAltName extension of type dNSName is present, that MUST - be used as the identity. Otherwise, the (most specific) Common Name - field in the Subject field of the certificate MUST be used. Although - the use of the Common Name is existing practice, it is deprecated and - Certification Authorities are encouraged to use the dNSName instead. - - Matching is performed using the matching rules specified by - [RFC2459]. If more than one identity of a given type is present in - the certificate (e.g., more than one dNSName name, a match in any one - of the set is considered acceptable.) Names may contain the wildcard - character * which is considered to match any single domain name - component or component fragment. E.g., *.a.com matches foo.a.com but - not bar.foo.a.com. f*.com matches foo.com but not bar.com. - - In some cases, the URI is specified as an IP address rather than a - hostname. In this case, the iPAddress subjectAltName must be present - in the certificate and must exactly match the IP in the URI. - - This function is now used from ngtcp2 (QUIC) as well. -*/ -CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - X509 *server_cert) -{ - bool matched = FALSE; - int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */ - size_t addrlen = 0; - STACK_OF(GENERAL_NAME) *altnames; -#ifdef ENABLE_IPV6 - struct in6_addr addr; -#else - struct in_addr addr; -#endif - CURLcode result = CURLE_OK; - bool dNSName = FALSE; /* if a dNSName field exists in the cert */ - bool iPAddress = FALSE; /* if a iPAddress field exists in the cert */ - const char * const hostname = SSL_HOST_NAME(); - const char * const dispname = SSL_HOST_DISPNAME(); - size_t hostlen = strlen(hostname); - -#ifdef ENABLE_IPV6 - if(conn->bits.ipv6_ip && - Curl_inet_pton(AF_INET6, hostname, &addr)) { - target = GEN_IPADD; - addrlen = sizeof(struct in6_addr); - } - else -#endif - if(Curl_inet_pton(AF_INET, hostname, &addr)) { - target = GEN_IPADD; - addrlen = sizeof(struct in_addr); - } - - /* get a "list" of alternative names */ - altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL); - - if(altnames) { -#ifdef OPENSSL_IS_BORINGSSL - size_t numalts; - size_t i; -#else - int numalts; - int i; -#endif - bool dnsmatched = FALSE; - bool ipmatched = FALSE; - - /* get amount of alternatives, RFC2459 claims there MUST be at least - one, but we don't depend on it... */ - numalts = sk_GENERAL_NAME_num(altnames); - - /* loop through all alternatives - until a dnsmatch */ - for(i = 0; (i < numalts) && !dnsmatched; i++) { - /* get a handle to alternative name number i */ - const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i); - - if(check->type == GEN_DNS) - dNSName = TRUE; - else if(check->type == GEN_IPADD) - iPAddress = TRUE; - - /* only check alternatives of the same type the target is */ - if(check->type == target) { - /* get data and length */ - const char *altptr = (char *)ASN1_STRING_get0_data(check->d.ia5); - size_t altlen = (size_t) ASN1_STRING_length(check->d.ia5); - - switch(target) { - case GEN_DNS: /* name/pattern comparison */ - /* The OpenSSL man page explicitly says: "In general it cannot be - assumed that the data returned by ASN1_STRING_data() is null - terminated or does not contain embedded nulls." But also that - "The actual format of the data will depend on the actual string - type itself: for example for an IA5String the data will be ASCII" - - It has been however verified that in 0.9.6 and 0.9.7, IA5String - is always null-terminated. - */ - if((altlen == strlen(altptr)) && - /* if this isn't true, there was an embedded zero in the name - string and we cannot match it. */ - subj_alt_hostcheck(data, - altptr, - altlen, hostname, hostlen, dispname)) { - dnsmatched = TRUE; - } - break; - - case GEN_IPADD: /* IP address comparison */ - /* compare alternative IP address if the data chunk is the same size - our server IP address is */ - if((altlen == addrlen) && !memcmp(altptr, &addr, altlen)) { - ipmatched = TRUE; - infof(data, - " subjectAltName: host \"%s\" matched cert's IP address!", - dispname); - } - break; - } - } - } - GENERAL_NAMES_free(altnames); - - if(dnsmatched || ipmatched) - matched = TRUE; - } - - if(matched) - /* an alternative name matched */ - ; - else if(dNSName || iPAddress) { - infof(data, " subjectAltName does not match %s", dispname); - failf(data, "SSL: no alternative certificate subject name matches " - "target host name '%s'", dispname); - result = CURLE_PEER_FAILED_VERIFICATION; - } - else { - /* we have to look to the last occurrence of a commonName in the - distinguished one to get the most significant one. */ - int i = -1; - unsigned char *peer_CN = NULL; - int peerlen = 0; - - /* The following is done because of a bug in 0.9.6b */ - X509_NAME *name = X509_get_subject_name(server_cert); - if(name) { - int j; - while((j = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) - i = j; - } - - /* we have the name entry and we will now convert this to a string - that we can use for comparison. Doing this we support BMPstring, - UTF8, etc. */ - - if(i >= 0) { - ASN1_STRING *tmp = - X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i)); - - /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input - is already UTF-8 encoded. We check for this case and copy the raw - string manually to avoid the problem. This code can be made - conditional in the future when OpenSSL has been fixed. */ - if(tmp) { - if(ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) { - peerlen = ASN1_STRING_length(tmp); - if(peerlen >= 0) { - peer_CN = OPENSSL_malloc(peerlen + 1); - if(peer_CN) { - memcpy(peer_CN, ASN1_STRING_get0_data(tmp), peerlen); - peer_CN[peerlen] = '\0'; - } - else - result = CURLE_OUT_OF_MEMORY; - } - } - else /* not a UTF8 name */ - peerlen = ASN1_STRING_to_UTF8(&peer_CN, tmp); - - if(peer_CN && (curlx_uztosi(strlen((char *)peer_CN)) != peerlen)) { - /* there was a terminating zero before the end of string, this - cannot match and we return failure! */ - failf(data, "SSL: illegal cert name field"); - result = CURLE_PEER_FAILED_VERIFICATION; - } - } - } - - if(result) - /* error already detected, pass through */ - ; - else if(!peer_CN) { - failf(data, - "SSL: unable to obtain common name from peer certificate"); - result = CURLE_PEER_FAILED_VERIFICATION; - } - else if(!Curl_cert_hostcheck((const char *)peer_CN, - peerlen, hostname, hostlen)) { - failf(data, "SSL: certificate subject name '%s' does not match " - "target host name '%s'", peer_CN, dispname); - result = CURLE_PEER_FAILED_VERIFICATION; - } - else { - infof(data, " common name: %s (matched)", peer_CN); - } - if(peer_CN) - OPENSSL_free(peer_CN); - } - - return result; -} - -#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ - !defined(OPENSSL_NO_OCSP) -static CURLcode verifystatus(struct Curl_easy *data, - struct ssl_connect_data *connssl) -{ - int i, ocsp_status; - unsigned char *status; - const unsigned char *p; - CURLcode result = CURLE_OK; - OCSP_RESPONSE *rsp = NULL; - OCSP_BASICRESP *br = NULL; - X509_STORE *st = NULL; - STACK_OF(X509) *ch = NULL; - struct ssl_backend_data *backend = connssl->backend; - X509 *cert; - OCSP_CERTID *id = NULL; - int cert_status, crl_reason; - ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; - int ret; - long len; - - DEBUGASSERT(backend); - - len = SSL_get_tlsext_status_ocsp_resp(backend->handle, &status); - - if(!status) { - failf(data, "No OCSP response received"); - result = CURLE_SSL_INVALIDCERTSTATUS; - goto end; - } - p = status; - rsp = d2i_OCSP_RESPONSE(NULL, &p, len); - if(!rsp) { - failf(data, "Invalid OCSP response"); - result = CURLE_SSL_INVALIDCERTSTATUS; - goto end; - } - - ocsp_status = OCSP_response_status(rsp); - if(ocsp_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { - failf(data, "Invalid OCSP response status: %s (%d)", - OCSP_response_status_str(ocsp_status), ocsp_status); - result = CURLE_SSL_INVALIDCERTSTATUS; - goto end; - } - - br = OCSP_response_get1_basic(rsp); - if(!br) { - failf(data, "Invalid OCSP response"); - result = CURLE_SSL_INVALIDCERTSTATUS; - goto end; - } - - ch = SSL_get_peer_cert_chain(backend->handle); - if(!ch) { - failf(data, "Could not get peer certificate chain"); - result = CURLE_SSL_INVALIDCERTSTATUS; - goto end; - } - st = SSL_CTX_get_cert_store(backend->ctx); - -#if ((OPENSSL_VERSION_NUMBER <= 0x1000201fL) /* Fixed after 1.0.2a */ || \ - (defined(LIBRESSL_VERSION_NUMBER) && \ - LIBRESSL_VERSION_NUMBER <= 0x2040200fL)) - /* The authorized responder cert in the OCSP response MUST be signed by the - peer cert's issuer (see RFC6960 section 4.2.2.2). If that's a root cert, - no problem, but if it's an intermediate cert OpenSSL has a bug where it - expects this issuer to be present in the chain embedded in the OCSP - response. So we add it if necessary. */ - - /* First make sure the peer cert chain includes both a peer and an issuer, - and the OCSP response contains a responder cert. */ - if(sk_X509_num(ch) >= 2 && sk_X509_num(br->certs) >= 1) { - X509 *responder = sk_X509_value(br->certs, sk_X509_num(br->certs) - 1); - - /* Find issuer of responder cert and add it to the OCSP response chain */ - for(i = 0; i < sk_X509_num(ch); i++) { - X509 *issuer = sk_X509_value(ch, i); - if(X509_check_issued(issuer, responder) == X509_V_OK) { - if(!OCSP_basic_add1_cert(br, issuer)) { - failf(data, "Could not add issuer cert to OCSP response"); - result = CURLE_SSL_INVALIDCERTSTATUS; - goto end; - } - } - } - } -#endif - - if(OCSP_basic_verify(br, ch, st, 0) <= 0) { - failf(data, "OCSP response verification failed"); - result = CURLE_SSL_INVALIDCERTSTATUS; - goto end; - } - - /* Compute the certificate's ID */ - cert = SSL_get1_peer_certificate(backend->handle); - if(!cert) { - failf(data, "Error getting peer certificate"); - result = CURLE_SSL_INVALIDCERTSTATUS; - goto end; - } - - for(i = 0; i < sk_X509_num(ch); i++) { - X509 *issuer = sk_X509_value(ch, i); - if(X509_check_issued(issuer, cert) == X509_V_OK) { - id = OCSP_cert_to_id(EVP_sha1(), cert, issuer); - break; - } - } - X509_free(cert); - - if(!id) { - failf(data, "Error computing OCSP ID"); - result = CURLE_SSL_INVALIDCERTSTATUS; - goto end; - } - - /* Find the single OCSP response corresponding to the certificate ID */ - ret = OCSP_resp_find_status(br, id, &cert_status, &crl_reason, &rev, - &thisupd, &nextupd); - OCSP_CERTID_free(id); - if(ret != 1) { - failf(data, "Could not find certificate ID in OCSP response"); - result = CURLE_SSL_INVALIDCERTSTATUS; - goto end; - } - - /* Validate the corresponding single OCSP response */ - if(!OCSP_check_validity(thisupd, nextupd, 300L, -1L)) { - failf(data, "OCSP response has expired"); - result = CURLE_SSL_INVALIDCERTSTATUS; - goto end; - } - - infof(data, "SSL certificate status: %s (%d)", - OCSP_cert_status_str(cert_status), cert_status); - - switch(cert_status) { - case V_OCSP_CERTSTATUS_GOOD: - break; - - case V_OCSP_CERTSTATUS_REVOKED: - result = CURLE_SSL_INVALIDCERTSTATUS; - failf(data, "SSL certificate revocation reason: %s (%d)", - OCSP_crl_reason_str(crl_reason), crl_reason); - goto end; - - case V_OCSP_CERTSTATUS_UNKNOWN: - default: - result = CURLE_SSL_INVALIDCERTSTATUS; - goto end; - } - -end: - if(br) - OCSP_BASICRESP_free(br); - OCSP_RESPONSE_free(rsp); - - return result; -} -#endif - -#endif /* USE_OPENSSL */ - -/* The SSL_CTRL_SET_MSG_CALLBACK doesn't exist in ancient OpenSSL versions - and thus this cannot be done there. */ -#ifdef SSL_CTRL_SET_MSG_CALLBACK - -static const char *ssl_msg_type(int ssl_ver, int msg) -{ -#ifdef SSL2_VERSION_MAJOR - if(ssl_ver == SSL2_VERSION_MAJOR) { - switch(msg) { - case SSL2_MT_ERROR: - return "Error"; - case SSL2_MT_CLIENT_HELLO: - return "Client hello"; - case SSL2_MT_CLIENT_MASTER_KEY: - return "Client key"; - case SSL2_MT_CLIENT_FINISHED: - return "Client finished"; - case SSL2_MT_SERVER_HELLO: - return "Server hello"; - case SSL2_MT_SERVER_VERIFY: - return "Server verify"; - case SSL2_MT_SERVER_FINISHED: - return "Server finished"; - case SSL2_MT_REQUEST_CERTIFICATE: - return "Request CERT"; - case SSL2_MT_CLIENT_CERTIFICATE: - return "Client CERT"; - } - } - else -#endif - if(ssl_ver == SSL3_VERSION_MAJOR) { - switch(msg) { - case SSL3_MT_HELLO_REQUEST: - return "Hello request"; - case SSL3_MT_CLIENT_HELLO: - return "Client hello"; - case SSL3_MT_SERVER_HELLO: - return "Server hello"; -#ifdef SSL3_MT_NEWSESSION_TICKET - case SSL3_MT_NEWSESSION_TICKET: - return "Newsession Ticket"; -#endif - case SSL3_MT_CERTIFICATE: - return "Certificate"; - case SSL3_MT_SERVER_KEY_EXCHANGE: - return "Server key exchange"; - case SSL3_MT_CLIENT_KEY_EXCHANGE: - return "Client key exchange"; - case SSL3_MT_CERTIFICATE_REQUEST: - return "Request CERT"; - case SSL3_MT_SERVER_DONE: - return "Server finished"; - case SSL3_MT_CERTIFICATE_VERIFY: - return "CERT verify"; - case SSL3_MT_FINISHED: - return "Finished"; -#ifdef SSL3_MT_CERTIFICATE_STATUS - case SSL3_MT_CERTIFICATE_STATUS: - return "Certificate Status"; -#endif -#ifdef SSL3_MT_ENCRYPTED_EXTENSIONS - case SSL3_MT_ENCRYPTED_EXTENSIONS: - return "Encrypted Extensions"; -#endif -#ifdef SSL3_MT_SUPPLEMENTAL_DATA - case SSL3_MT_SUPPLEMENTAL_DATA: - return "Supplemental data"; -#endif -#ifdef SSL3_MT_END_OF_EARLY_DATA - case SSL3_MT_END_OF_EARLY_DATA: - return "End of early data"; -#endif -#ifdef SSL3_MT_KEY_UPDATE - case SSL3_MT_KEY_UPDATE: - return "Key update"; -#endif -#ifdef SSL3_MT_NEXT_PROTO - case SSL3_MT_NEXT_PROTO: - return "Next protocol"; -#endif -#ifdef SSL3_MT_MESSAGE_HASH - case SSL3_MT_MESSAGE_HASH: - return "Message hash"; -#endif - } - } - return "Unknown"; -} - -static const char *tls_rt_type(int type) -{ - switch(type) { -#ifdef SSL3_RT_HEADER - case SSL3_RT_HEADER: - return "TLS header"; -#endif - case SSL3_RT_CHANGE_CIPHER_SPEC: - return "TLS change cipher"; - case SSL3_RT_ALERT: - return "TLS alert"; - case SSL3_RT_HANDSHAKE: - return "TLS handshake"; - case SSL3_RT_APPLICATION_DATA: - return "TLS app data"; - default: - return "TLS Unknown"; - } -} - -/* - * Our callback from the SSL/TLS layers. - */ -static void ossl_trace(int direction, int ssl_ver, int content_type, - const void *buf, size_t len, SSL *ssl, - void *userp) -{ - char unknown[32]; - const char *verstr = NULL; - struct connectdata *conn = userp; - struct ssl_connect_data *connssl = &conn->ssl[0]; - struct ssl_backend_data *backend = connssl->backend; - struct Curl_easy *data = NULL; - - DEBUGASSERT(backend); - data = backend->logger; - - if(!conn || !data || !data->set.fdebug || - (direction != 0 && direction != 1)) - return; - - switch(ssl_ver) { -#ifdef SSL2_VERSION /* removed in recent versions */ - case SSL2_VERSION: - verstr = "SSLv2"; - break; -#endif -#ifdef SSL3_VERSION - case SSL3_VERSION: - verstr = "SSLv3"; - break; -#endif - case TLS1_VERSION: - verstr = "TLSv1.0"; - break; -#ifdef TLS1_1_VERSION - case TLS1_1_VERSION: - verstr = "TLSv1.1"; - break; -#endif -#ifdef TLS1_2_VERSION - case TLS1_2_VERSION: - verstr = "TLSv1.2"; - break; -#endif -#ifdef TLS1_3_VERSION - case TLS1_3_VERSION: - verstr = "TLSv1.3"; - break; -#endif - case 0: - break; - default: - msnprintf(unknown, sizeof(unknown), "(%x)", ssl_ver); - verstr = unknown; - break; - } - - /* Log progress for interesting records only (like Handshake or Alert), skip - * all raw record headers (content_type == SSL3_RT_HEADER or ssl_ver == 0). - * For TLS 1.3, skip notification of the decrypted inner Content-Type. - */ - if(ssl_ver -#ifdef SSL3_RT_INNER_CONTENT_TYPE - && content_type != SSL3_RT_INNER_CONTENT_TYPE -#endif - ) { - const char *msg_name, *tls_rt_name; - char ssl_buf[1024]; - int msg_type, txt_len; - - /* the info given when the version is zero is not that useful for us */ - - ssl_ver >>= 8; /* check the upper 8 bits only below */ - - /* SSLv2 doesn't seem to have TLS record-type headers, so OpenSSL - * always pass-up content-type as 0. But the interesting message-type - * is at 'buf[0]'. - */ - if(ssl_ver == SSL3_VERSION_MAJOR && content_type) - tls_rt_name = tls_rt_type(content_type); - else - tls_rt_name = ""; - - if(content_type == SSL3_RT_CHANGE_CIPHER_SPEC) { - msg_type = *(char *)buf; - msg_name = "Change cipher spec"; - } - else if(content_type == SSL3_RT_ALERT) { - msg_type = (((char *)buf)[0] << 8) + ((char *)buf)[1]; - msg_name = SSL_alert_desc_string_long(msg_type); - } - else { - msg_type = *(char *)buf; - msg_name = ssl_msg_type(ssl_ver, msg_type); - } - - txt_len = msnprintf(ssl_buf, sizeof(ssl_buf), "%s (%s), %s, %s (%d):\n", - verstr, direction?"OUT":"IN", - tls_rt_name, msg_name, msg_type); - if(0 <= txt_len && (unsigned)txt_len < sizeof(ssl_buf)) { - Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len); - } - } - - Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT : - CURLINFO_SSL_DATA_IN, (char *)buf, len); - (void) ssl; -} -#endif - -#ifdef USE_OPENSSL -/* ====================================================== */ - -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME -# define use_sni(x) sni = (x) -#else -# define use_sni(x) Curl_nop_stmt -#endif - -/* Check for OpenSSL 1.0.2 which has ALPN support. */ -#undef HAS_ALPN -#if OPENSSL_VERSION_NUMBER >= 0x10002000L \ - && !defined(OPENSSL_NO_TLSEXT) -# define HAS_ALPN 1 -#endif - -#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */ -static CURLcode -set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn) -{ - /* first, TLS min version... */ - long curl_ssl_version_min = SSL_CONN_CONFIG(version); - long curl_ssl_version_max; - - /* convert curl min SSL version option to OpenSSL constant */ -#if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER) - uint16_t ossl_ssl_version_min = 0; - uint16_t ossl_ssl_version_max = 0; -#else - long ossl_ssl_version_min = 0; - long ossl_ssl_version_max = 0; -#endif - switch(curl_ssl_version_min) { - case CURL_SSLVERSION_TLSv1: /* TLS 1.x */ - case CURL_SSLVERSION_TLSv1_0: - ossl_ssl_version_min = TLS1_VERSION; - break; - case CURL_SSLVERSION_TLSv1_1: - ossl_ssl_version_min = TLS1_1_VERSION; - break; - case CURL_SSLVERSION_TLSv1_2: - ossl_ssl_version_min = TLS1_2_VERSION; - break; - case CURL_SSLVERSION_TLSv1_3: -#ifdef TLS1_3_VERSION - ossl_ssl_version_min = TLS1_3_VERSION; - break; -#else - return CURLE_NOT_BUILT_IN; -#endif - } - - /* CURL_SSLVERSION_DEFAULT means that no option was selected. - We don't want to pass 0 to SSL_CTX_set_min_proto_version as - it would enable all versions down to the lowest supported by - the library. - So we skip this, and stay with the library default - */ - if(curl_ssl_version_min != CURL_SSLVERSION_DEFAULT) { - if(!SSL_CTX_set_min_proto_version(ctx, ossl_ssl_version_min)) { - return CURLE_SSL_CONNECT_ERROR; - } - } - - /* ... then, TLS max version */ - curl_ssl_version_max = SSL_CONN_CONFIG(version_max); - - /* convert curl max SSL version option to OpenSSL constant */ - switch(curl_ssl_version_max) { - case CURL_SSLVERSION_MAX_TLSv1_0: - ossl_ssl_version_max = TLS1_VERSION; - break; - case CURL_SSLVERSION_MAX_TLSv1_1: - ossl_ssl_version_max = TLS1_1_VERSION; - break; - case CURL_SSLVERSION_MAX_TLSv1_2: - ossl_ssl_version_max = TLS1_2_VERSION; - break; -#ifdef TLS1_3_VERSION - case CURL_SSLVERSION_MAX_TLSv1_3: - ossl_ssl_version_max = TLS1_3_VERSION; - break; -#endif - case CURL_SSLVERSION_MAX_NONE: /* none selected */ - case CURL_SSLVERSION_MAX_DEFAULT: /* max selected */ - default: - /* SSL_CTX_set_max_proto_version states that: - setting the maximum to 0 will enable - protocol versions up to the highest version - supported by the library */ - ossl_ssl_version_max = 0; - break; - } - - if(!SSL_CTX_set_max_proto_version(ctx, ossl_ssl_version_max)) { - return CURLE_SSL_CONNECT_ERROR; - } - - return CURLE_OK; -} -#endif - -#ifdef OPENSSL_IS_BORINGSSL -typedef uint32_t ctx_option_t; -#elif OPENSSL_VERSION_NUMBER >= 0x30000000L -typedef uint64_t ctx_option_t; -#else -typedef long ctx_option_t; -#endif - -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) /* 1.1.0 */ -static CURLcode -set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, - struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - long ssl_version = SSL_CONN_CONFIG(version); - long ssl_version_max = SSL_CONN_CONFIG(version_max); - - (void) data; /* In case it's unused. */ - - switch(ssl_version) { - case CURL_SSLVERSION_TLSv1_3: -#ifdef TLS1_3_VERSION - { - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - DEBUGASSERT(backend); - SSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION); - *ctx_options |= SSL_OP_NO_TLSv1_2; - } -#else - (void)sockindex; - (void)ctx_options; - failf(data, OSSL_PACKAGE " was built without TLS 1.3 support"); - return CURLE_NOT_BUILT_IN; -#endif - /* FALLTHROUGH */ - case CURL_SSLVERSION_TLSv1_2: -#if OPENSSL_VERSION_NUMBER >= 0x1000100FL - *ctx_options |= SSL_OP_NO_TLSv1_1; -#else - failf(data, OSSL_PACKAGE " was built without TLS 1.2 support"); - return CURLE_NOT_BUILT_IN; -#endif - /* FALLTHROUGH */ - case CURL_SSLVERSION_TLSv1_1: -#if OPENSSL_VERSION_NUMBER >= 0x1000100FL - *ctx_options |= SSL_OP_NO_TLSv1; -#else - failf(data, OSSL_PACKAGE " was built without TLS 1.1 support"); - return CURLE_NOT_BUILT_IN; -#endif - /* FALLTHROUGH */ - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1: - break; - } - - switch(ssl_version_max) { - case CURL_SSLVERSION_MAX_TLSv1_0: -#if OPENSSL_VERSION_NUMBER >= 0x1000100FL - *ctx_options |= SSL_OP_NO_TLSv1_1; -#endif - /* FALLTHROUGH */ - case CURL_SSLVERSION_MAX_TLSv1_1: -#if OPENSSL_VERSION_NUMBER >= 0x1000100FL - *ctx_options |= SSL_OP_NO_TLSv1_2; -#endif - /* FALLTHROUGH */ - case CURL_SSLVERSION_MAX_TLSv1_2: -#ifdef TLS1_3_VERSION - *ctx_options |= SSL_OP_NO_TLSv1_3; -#endif - break; - case CURL_SSLVERSION_MAX_TLSv1_3: -#ifdef TLS1_3_VERSION - break; -#else - failf(data, OSSL_PACKAGE " was built without TLS 1.3 support"); - return CURLE_NOT_BUILT_IN; -#endif - } - return CURLE_OK; -} -#endif - -/* The "new session" callback must return zero if the session can be removed - * or non-zero if the session has been put into the session cache. - */ -static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) -{ - int res = 0; - struct connectdata *conn; - struct Curl_easy *data; - int sockindex; - curl_socket_t *sockindex_ptr; - int data_idx = ossl_get_ssl_data_index(); - int connectdata_idx = ossl_get_ssl_conn_index(); - int sockindex_idx = ossl_get_ssl_sockindex_index(); - int proxy_idx = ossl_get_proxy_index(); - bool isproxy; - - if(data_idx < 0 || connectdata_idx < 0 || sockindex_idx < 0 || proxy_idx < 0) - return 0; - - conn = (struct connectdata*) SSL_get_ex_data(ssl, connectdata_idx); - data = (struct Curl_easy *) SSL_get_ex_data(ssl, data_idx); - /* The sockindex has been stored as a pointer to an array element */ - sockindex_ptr = (curl_socket_t*) SSL_get_ex_data(ssl, sockindex_idx); - if(!conn || !data || !sockindex_ptr) - return 0; - - sockindex = (int)(sockindex_ptr - conn->sock); - - isproxy = SSL_get_ex_data(ssl, proxy_idx) ? TRUE : FALSE; - - if(SSL_SET_OPTION(primary.sessionid)) { - bool incache; - bool added = FALSE; - void *old_ssl_sessionid = NULL; - - Curl_ssl_sessionid_lock(data); - if(isproxy) - incache = FALSE; - else - incache = !(Curl_ssl_getsessionid(data, conn, isproxy, - &old_ssl_sessionid, NULL, sockindex)); - if(incache) { - if(old_ssl_sessionid != ssl_sessionid) { - infof(data, "old SSL session ID is stale, removing"); - Curl_ssl_delsessionid(data, old_ssl_sessionid); - incache = FALSE; - } - } - - if(!incache) { - if(!Curl_ssl_addsessionid(data, conn, isproxy, ssl_sessionid, - 0 /* unknown size */, sockindex, &added)) { - if(added) { - /* the session has been put into the session cache */ - res = 1; - } - } - else - failf(data, "failed to store ssl session"); - } - Curl_ssl_sessionid_unlock(data); - } - - return res; -} - -static CURLcode load_cacert_from_memory(SSL_CTX *ctx, - const struct curl_blob *ca_info_blob) -{ - /* these need to be freed at the end */ - BIO *cbio = NULL; - STACK_OF(X509_INFO) *inf = NULL; - - /* everything else is just a reference */ - int i, count = 0; - X509_STORE *cts = NULL; - X509_INFO *itmp = NULL; - - if(ca_info_blob->len > (size_t)INT_MAX) - return CURLE_SSL_CACERT_BADFILE; - - cts = SSL_CTX_get_cert_store(ctx); - if(!cts) - return CURLE_OUT_OF_MEMORY; - - cbio = BIO_new_mem_buf(ca_info_blob->data, (int)ca_info_blob->len); - if(!cbio) - return CURLE_OUT_OF_MEMORY; - - inf = PEM_X509_INFO_read_bio(cbio, NULL, NULL, NULL); - if(!inf) { - BIO_free(cbio); - return CURLE_SSL_CACERT_BADFILE; - } - - /* add each entry from PEM file to x509_store */ - for(i = 0; i < (int)sk_X509_INFO_num(inf); ++i) { - itmp = sk_X509_INFO_value(inf, i); - if(itmp->x509) { - if(X509_STORE_add_cert(cts, itmp->x509)) { - ++count; - } - else { - /* set count to 0 to return an error */ - count = 0; - break; - } - } - if(itmp->crl) { - if(X509_STORE_add_crl(cts, itmp->crl)) { - ++count; - } - else { - /* set count to 0 to return an error */ - count = 0; - break; - } - } - } - - sk_X509_INFO_pop_free(inf, X509_INFO_free); - BIO_free(cbio); - - /* if we didn't end up importing anything, treat that as an error */ - return (count > 0 ? CURLE_OK : CURLE_SSL_CACERT_BADFILE); -} - -static CURLcode ossl_connect_step1(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - CURLcode result = CURLE_OK; - char *ciphers; - SSL_METHOD_QUAL SSL_METHOD *req_method = NULL; - X509_LOOKUP *lookup = NULL; - curl_socket_t sockfd = conn->sock[sockindex]; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - ctx_option_t ctx_options = 0; - void *ssl_sessionid = NULL; - -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - bool sni; - const char * const hostname = SSL_HOST_NAME(); - -#ifdef ENABLE_IPV6 - struct in6_addr addr; -#else - struct in_addr addr; -#endif -#endif - const long int ssl_version = SSL_CONN_CONFIG(version); -#ifdef USE_OPENSSL_SRP - const enum CURL_TLSAUTH ssl_authtype = SSL_SET_OPTION(primary.authtype); -#endif - char * const ssl_cert = SSL_SET_OPTION(primary.clientcert); - const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob); - const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob); - const char * const ssl_cert_type = SSL_SET_OPTION(cert_type); - const char * const ssl_cafile = - /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ - (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile)); - const char * const ssl_capath = SSL_CONN_CONFIG(CApath); - const bool verifypeer = SSL_CONN_CONFIG(verifypeer); - const char * const ssl_crlfile = SSL_SET_OPTION(primary.CRLfile); - char error_buffer[256]; - struct ssl_backend_data *backend = connssl->backend; - bool imported_native_ca = false; - - DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); - DEBUGASSERT(backend); - - /* Make funny stuff to get random input */ - result = ossl_seed(data); - if(result) - return result; - - SSL_SET_OPTION_LVALUE(certverifyresult) = !X509_V_OK; - - /* check to see if we've been told to use an explicit SSL/TLS version */ - - switch(ssl_version) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_3: - /* it will be handled later with the context options */ -#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) - req_method = TLS_client_method(); -#else - req_method = SSLv23_client_method(); -#endif - use_sni(TRUE); - break; - case CURL_SSLVERSION_SSLv2: - failf(data, "No SSLv2 support"); - return CURLE_NOT_BUILT_IN; - case CURL_SSLVERSION_SSLv3: - failf(data, "No SSLv3 support"); - return CURLE_NOT_BUILT_IN; - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } - - DEBUGASSERT(!backend->ctx); - backend->ctx = SSL_CTX_new(req_method); - - if(!backend->ctx) { - failf(data, "SSL: couldn't create a context: %s", - ossl_strerror(ERR_peek_error(), error_buffer, sizeof(error_buffer))); - return CURLE_OUT_OF_MEMORY; - } - -#ifdef SSL_MODE_RELEASE_BUFFERS - SSL_CTX_set_mode(backend->ctx, SSL_MODE_RELEASE_BUFFERS); -#endif - -#ifdef SSL_CTRL_SET_MSG_CALLBACK - if(data->set.fdebug && data->set.verbose) { - /* the SSL trace callback is only used for verbose logging */ - SSL_CTX_set_msg_callback(backend->ctx, ossl_trace); - SSL_CTX_set_msg_callback_arg(backend->ctx, conn); - set_logger(conn, data); - } -#endif - - /* OpenSSL contains code to work around lots of bugs and flaws in various - SSL-implementations. SSL_CTX_set_options() is used to enabled those - work-arounds. The man page for this option states that SSL_OP_ALL enables - all the work-arounds and that "It is usually safe to use SSL_OP_ALL to - enable the bug workaround options if compatibility with somewhat broken - implementations is desired." - - The "-no_ticket" option was introduced in OpenSSL 0.9.8j. It's a flag to - disable "rfc4507bis session ticket support". rfc4507bis was later turned - into the proper RFC5077: https://datatracker.ietf.org/doc/html/rfc5077 - - The enabled extension concerns the session management. I wonder how often - libcurl stops a connection and then resumes a TLS session. Also, sending - the session data is some overhead. I suggest that you just use your - proposed patch (which explicitly disables TICKET). - - If someone writes an application with libcurl and OpenSSL who wants to - enable the feature, one can do this in the SSL callback. - - SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG option enabling allowed proper - interoperability with web server Netscape Enterprise Server 2.0.1 which - was released back in 1996. - - Due to CVE-2010-4180, option SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG has - become ineffective as of OpenSSL 0.9.8q and 1.0.0c. In order to mitigate - CVE-2010-4180 when using previous OpenSSL versions we no longer enable - this option regardless of OpenSSL version and SSL_OP_ALL definition. - - OpenSSL added a work-around for a SSL 3.0/TLS 1.0 CBC vulnerability - (https://www.openssl.org/~bodo/tls-cbc.txt). In 0.9.6e they added a bit to - SSL_OP_ALL that _disables_ that work-around despite the fact that - SSL_OP_ALL is documented to do "rather harmless" workarounds. In order to - keep the secure work-around, the SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS bit - must not be set. - */ - - ctx_options = SSL_OP_ALL; - -#ifdef SSL_OP_NO_TICKET - ctx_options |= SSL_OP_NO_TICKET; -#endif - -#ifdef SSL_OP_NO_COMPRESSION - ctx_options |= SSL_OP_NO_COMPRESSION; -#endif - -#ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG - /* mitigate CVE-2010-4180 */ - ctx_options &= ~SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG; -#endif - -#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS - /* unless the user explicitly asks to allow the protocol vulnerability we - use the work-around */ - if(!SSL_SET_OPTION(enable_beast)) - ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; -#endif - - switch(ssl_version) { - case CURL_SSLVERSION_SSLv2: - case CURL_SSLVERSION_SSLv3: - return CURLE_NOT_BUILT_IN; - - /* "--tlsv<x.y>" options mean TLS >= version <x.y> */ - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: /* TLS >= version 1.0 */ - case CURL_SSLVERSION_TLSv1_0: /* TLS >= version 1.0 */ - case CURL_SSLVERSION_TLSv1_1: /* TLS >= version 1.1 */ - case CURL_SSLVERSION_TLSv1_2: /* TLS >= version 1.2 */ - case CURL_SSLVERSION_TLSv1_3: /* TLS >= version 1.3 */ - /* asking for any TLS version as the minimum, means no SSL versions - allowed */ - ctx_options |= SSL_OP_NO_SSLv2; - ctx_options |= SSL_OP_NO_SSLv3; - -#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */ - result = set_ssl_version_min_max(backend->ctx, conn); -#else - result = set_ssl_version_min_max_legacy(&ctx_options, data, conn, - sockindex); -#endif - if(result != CURLE_OK) - return result; - break; - - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } - - SSL_CTX_set_options(backend->ctx, ctx_options); - -#ifdef HAS_ALPN - if(conn->bits.tls_enable_alpn) { - int cur = 0; - unsigned char protocols[128]; - -#ifdef USE_HTTP2 - if(data->state.httpwant >= CURL_HTTP_VERSION_2 -#ifndef CURL_DISABLE_PROXY - && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy) -#endif - ) { - protocols[cur++] = ALPN_H2_LENGTH; - - memcpy(&protocols[cur], ALPN_H2, ALPN_H2_LENGTH); - cur += ALPN_H2_LENGTH; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); - } -#endif - - protocols[cur++] = ALPN_HTTP_1_1_LENGTH; - memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH); - cur += ALPN_HTTP_1_1_LENGTH; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); - - /* expects length prefixed preference ordered list of protocols in wire - * format - */ - if(SSL_CTX_set_alpn_protos(backend->ctx, protocols, cur)) { - failf(data, "Error setting ALPN"); - return CURLE_SSL_CONNECT_ERROR; - } - } -#endif - - if(ssl_cert || ssl_cert_blob || ssl_cert_type) { - if(!result && - !cert_stuff(data, backend->ctx, - ssl_cert, ssl_cert_blob, ssl_cert_type, - SSL_SET_OPTION(key), SSL_SET_OPTION(key_blob), - SSL_SET_OPTION(key_type), SSL_SET_OPTION(key_passwd))) - result = CURLE_SSL_CERTPROBLEM; - if(result) - /* failf() is already done in cert_stuff() */ - return result; - } - - ciphers = SSL_CONN_CONFIG(cipher_list); - if(!ciphers) - ciphers = (char *)DEFAULT_CIPHER_SELECTION; - if(ciphers) { - if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) { - failf(data, "failed setting cipher list: %s", ciphers); - return CURLE_SSL_CIPHER; - } - infof(data, "Cipher selection: %s", ciphers); - } - -#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES - { - char *ciphers13 = SSL_CONN_CONFIG(cipher_list13); - if(ciphers13) { - if(!SSL_CTX_set_ciphersuites(backend->ctx, ciphers13)) { - failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers13); - return CURLE_SSL_CIPHER; - } - infof(data, "TLS 1.3 cipher selection: %s", ciphers13); - } - } -#endif - -#ifdef HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH - /* OpenSSL 1.1.1 requires clients to opt-in for PHA */ - SSL_CTX_set_post_handshake_auth(backend->ctx, 1); -#endif - -#ifdef HAVE_SSL_CTX_SET_EC_CURVES - { - char *curves = SSL_CONN_CONFIG(curves); - if(curves) { - if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) { - failf(data, "failed setting curves list: '%s'", curves); - return CURLE_SSL_CIPHER; - } - } - } -#endif - -#ifdef USE_OPENSSL_SRP - if((ssl_authtype == CURL_TLSAUTH_SRP) && - Curl_auth_allowed_to_host(data)) { - char * const ssl_username = SSL_SET_OPTION(primary.username); - char * const ssl_password = SSL_SET_OPTION(primary.password); - infof(data, "Using TLS-SRP username: %s", ssl_username); - - if(!SSL_CTX_set_srp_username(backend->ctx, ssl_username)) { - failf(data, "Unable to set SRP user name"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - if(!SSL_CTX_set_srp_password(backend->ctx, ssl_password)) { - failf(data, "failed setting SRP password"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - if(!SSL_CONN_CONFIG(cipher_list)) { - infof(data, "Setting cipher list SRP"); - - if(!SSL_CTX_set_cipher_list(backend->ctx, "SRP")) { - failf(data, "failed setting SRP cipher list"); - return CURLE_SSL_CIPHER; - } - } - } -#endif - - -#if defined(USE_WIN32_CRYPTO) - /* Import certificates from the Windows root certificate store if requested. - https://stackoverflow.com/questions/9507184/ - https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037 - https://datatracker.ietf.org/doc/html/rfc5280 */ - if((SSL_CONN_CONFIG(verifypeer) || SSL_CONN_CONFIG(verifyhost)) && - (SSL_SET_OPTION(native_ca_store))) { - X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx); - HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT")); - - if(hStore) { - PCCERT_CONTEXT pContext = NULL; - /* The array of enhanced key usage OIDs will vary per certificate and is - declared outside of the loop so that rather than malloc/free each - iteration we can grow it with realloc, when necessary. */ - CERT_ENHKEY_USAGE *enhkey_usage = NULL; - DWORD enhkey_usage_size = 0; - - /* This loop makes a best effort to import all valid certificates from - the MS root store. If a certificate cannot be imported it is skipped. - 'result' is used to store only hard-fail conditions (such as out of - memory) that cause an early break. */ - result = CURLE_OK; - for(;;) { - X509 *x509; - FILETIME now; - BYTE key_usage[2]; - DWORD req_size; - const unsigned char *encoded_cert; -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - char cert_name[256]; -#endif - - pContext = CertEnumCertificatesInStore(hStore, pContext); - if(!pContext) - break; - -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, - NULL, cert_name, sizeof(cert_name))) { - strcpy(cert_name, "Unknown"); - } - infof(data, "SSL: Checking cert \"%s\"", cert_name); -#endif - - encoded_cert = (const unsigned char *)pContext->pbCertEncoded; - if(!encoded_cert) - continue; - - GetSystemTimeAsFileTime(&now); - if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 || - CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0) - continue; - - /* If key usage exists check for signing attribute */ - if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType, - pContext->pCertInfo, - key_usage, sizeof(key_usage))) { - if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE)) - continue; - } - else if(GetLastError()) - continue; - - /* If enhanced key usage exists check for server auth attribute. - * - * Note "In a Microsoft environment, a certificate might also have EKU - * extended properties that specify valid uses for the certificate." - * The call below checks both, and behavior varies depending on what is - * found. For more details see CertGetEnhancedKeyUsage doc. - */ - if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) { - if(req_size && req_size > enhkey_usage_size) { - void *tmp = realloc(enhkey_usage, req_size); - - if(!tmp) { - failf(data, "SSL: Out of memory allocating for OID list"); - result = CURLE_OUT_OF_MEMORY; - break; - } - - enhkey_usage = (CERT_ENHKEY_USAGE *)tmp; - enhkey_usage_size = req_size; - } - - if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) { - if(!enhkey_usage->cUsageIdentifier) { - /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate is - good for all uses. If it returns zero, the certificate has no - valid uses." */ - if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND) - continue; - } - else { - DWORD i; - bool found = false; - - for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) { - if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */, - enhkey_usage->rgpszUsageIdentifier[i])) { - found = true; - break; - } - } - - if(!found) - continue; - } - } - else - continue; - } - else - continue; - - x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); - if(!x509) - continue; - - /* Try to import the certificate. This may fail for legitimate reasons - such as duplicate certificate, which is allowed by MS but not - OpenSSL. */ - if(X509_STORE_add_cert(store, x509) == 1) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - infof(data, "SSL: Imported cert \"%s\"", cert_name); -#endif - imported_native_ca = true; - } - X509_free(x509); - } - - free(enhkey_usage); - CertFreeCertificateContext(pContext); - CertCloseStore(hStore, 0); - - if(result) - return result; - } - if(imported_native_ca) - infof(data, "successfully imported Windows CA store"); - else - infof(data, "error importing Windows CA store, continuing anyway"); - } -#endif - - if(ca_info_blob) { - result = load_cacert_from_memory(backend->ctx, ca_info_blob); - if(result) { - if(result == CURLE_OUT_OF_MEMORY || - (verifypeer && !imported_native_ca)) { - failf(data, "error importing CA certificate blob"); - return result; - } - /* Only warn if no certificate verification is required. */ - infof(data, "error importing CA certificate blob, continuing anyway"); - } - } - - if(verifypeer && !imported_native_ca && (ssl_cafile || ssl_capath)) { -#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) - /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ - if(ssl_cafile && - !SSL_CTX_load_verify_file(backend->ctx, ssl_cafile)) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate file: %s", ssl_cafile); - return CURLE_SSL_CACERT_BADFILE; - } - if(ssl_capath && - !SSL_CTX_load_verify_dir(backend->ctx, ssl_capath)) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate path: %s", ssl_capath); - return CURLE_SSL_CACERT_BADFILE; - } -#else - /* tell OpenSSL where to find CA certificates that are used to verify the - server's certificate. */ - if(!SSL_CTX_load_verify_locations(backend->ctx, ssl_cafile, ssl_capath)) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate verify locations:" - " CAfile: %s CApath: %s", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); - return CURLE_SSL_CACERT_BADFILE; - } -#endif - infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); - infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); - } - -#ifdef CURL_CA_FALLBACK - if(verifypeer && - !ca_info_blob && !ssl_cafile && !ssl_capath && !imported_native_ca) { - /* verifying the peer without any CA certificates won't - work so use openssl's built-in default as fallback */ - SSL_CTX_set_default_verify_paths(backend->ctx); - } -#endif - - if(ssl_crlfile) { - /* tell OpenSSL where to find CRL file that is used to check certificate - * revocation */ - lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(backend->ctx), - X509_LOOKUP_file()); - if(!lookup || - (!X509_load_crl_file(lookup, ssl_crlfile, X509_FILETYPE_PEM)) ) { - failf(data, "error loading CRL file: %s", ssl_crlfile); - return CURLE_SSL_CRL_BADFILE; - } - /* Everything is fine. */ - infof(data, "successfully loaded CRL file:"); - X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), - X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); - - infof(data, " CRLfile: %s", ssl_crlfile); - } - - if(verifypeer) { - /* Try building a chain using issuers in the trusted store first to avoid - problems with server-sent legacy intermediates. Newer versions of - OpenSSL do alternate chain checking by default but we do not know how to - determine that in a reliable manner. - https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest - */ -#if defined(X509_V_FLAG_TRUSTED_FIRST) - X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), - X509_V_FLAG_TRUSTED_FIRST); -#endif -#ifdef X509_V_FLAG_PARTIAL_CHAIN - if(!SSL_SET_OPTION(no_partialchain) && !ssl_crlfile) { - /* Have intermediate certificates in the trust store be treated as - trust-anchors, in the same way as self-signed root CA certificates - are. This allows users to verify servers using the intermediate cert - only, instead of needing the whole chain. - - Due to OpenSSL bug https://github.com/openssl/openssl/issues/5081 we - cannot do partial chains with a CRL check. - */ - X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), - X509_V_FLAG_PARTIAL_CHAIN); - } -#endif - } - - /* OpenSSL always tries to verify the peer, this only says whether it should - * fail to connect if the verification fails, or if it should continue - * anyway. In the latter case the result of the verification is checked with - * SSL_get_verify_result() below. */ - SSL_CTX_set_verify(backend->ctx, - verifypeer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); - - /* Enable logging of secrets to the file specified in env SSLKEYLOGFILE. */ -#ifdef HAVE_KEYLOG_CALLBACK - if(Curl_tls_keylog_enabled()) { - SSL_CTX_set_keylog_callback(backend->ctx, ossl_keylog_callback); - } -#endif - - /* Enable the session cache because it's a prerequisite for the "new session" - * callback. Use the "external storage" mode to prevent OpenSSL from creating - * an internal session cache. - */ - SSL_CTX_set_session_cache_mode(backend->ctx, - SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL); - SSL_CTX_sess_set_new_cb(backend->ctx, ossl_new_session_cb); - - /* give application a chance to interfere with SSL set up. */ - if(data->set.ssl.fsslctx) { - Curl_set_in_callback(data, true); - result = (*data->set.ssl.fsslctx)(data, backend->ctx, - data->set.ssl.fsslctxp); - Curl_set_in_callback(data, false); - if(result) { - failf(data, "error signaled by ssl ctx callback"); - return result; - } - } - - /* Let's make an SSL structure */ - if(backend->handle) - SSL_free(backend->handle); - backend->handle = SSL_new(backend->ctx); - if(!backend->handle) { - failf(data, "SSL: couldn't create a context (handle)"); - return CURLE_OUT_OF_MEMORY; - } - -#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ - !defined(OPENSSL_NO_OCSP) - if(SSL_CONN_CONFIG(verifystatus)) - SSL_set_tlsext_status_type(backend->handle, TLSEXT_STATUSTYPE_ocsp); -#endif - -#if defined(OPENSSL_IS_BORINGSSL) && defined(ALLOW_RENEG) - SSL_set_renegotiate_mode(backend->handle, ssl_renegotiate_freely); -#endif - - SSL_set_connect_state(backend->handle); - - backend->server_cert = 0x0; -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) && -#ifdef ENABLE_IPV6 - (0 == Curl_inet_pton(AF_INET6, hostname, &addr)) && -#endif - sni) { - char *snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost || !SSL_set_tlsext_host_name(backend->handle, snihost)) { - failf(data, "Failed set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - } -#endif - - if(!ossl_associate_connection(data, conn, sockindex)) { - /* Maybe the internal errors of SSL_get_ex_new_index or SSL_set_ex_data */ - failf(data, "SSL: ossl_associate_connection failed: %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); - return CURLE_SSL_CONNECT_ERROR; - } - - if(SSL_SET_OPTION(primary.sessionid)) { - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE, - &ssl_sessionid, NULL, sockindex)) { - /* we got a session id, use it! */ - if(!SSL_set_session(backend->handle, ssl_sessionid)) { - Curl_ssl_sessionid_unlock(data); - failf(data, "SSL: SSL_set_session failed: %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); - return CURLE_SSL_CONNECT_ERROR; - } - /* Informational message */ - infof(data, "SSL re-using session ID"); - } - Curl_ssl_sessionid_unlock(data); - } - -#ifndef CURL_DISABLE_PROXY - if(conn->proxy_ssl[sockindex].use) { - BIO *const bio = BIO_new(BIO_f_ssl()); - struct ssl_backend_data *proxy_backend; - SSL* handle = NULL; - proxy_backend = conn->proxy_ssl[sockindex].backend; - DEBUGASSERT(proxy_backend); - handle = proxy_backend->handle; - DEBUGASSERT(ssl_connection_complete == conn->proxy_ssl[sockindex].state); - DEBUGASSERT(handle != NULL); - DEBUGASSERT(bio != NULL); - BIO_set_ssl(bio, handle, FALSE); - SSL_set_bio(backend->handle, bio, bio); - } - else -#endif - if(!SSL_set_fd(backend->handle, (int)sockfd)) { - /* pass the raw socket into the SSL layers */ - failf(data, "SSL: SSL_set_fd failed: %s", - ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer))); - return CURLE_SSL_CONNECT_ERROR; - } - - connssl->connecting_state = ssl_connect_2; - - return CURLE_OK; -} - -static CURLcode ossl_connect_step2(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - int err; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - DEBUGASSERT(ssl_connect_2 == connssl->connecting_state - || ssl_connect_2_reading == connssl->connecting_state - || ssl_connect_2_writing == connssl->connecting_state); - DEBUGASSERT(backend); - - ERR_clear_error(); - - err = SSL_connect(backend->handle); -#ifndef HAVE_KEYLOG_CALLBACK - if(Curl_tls_keylog_enabled()) { - /* If key logging is enabled, wait for the handshake to complete and then - * proceed with logging secrets (for TLS 1.2 or older). - */ - ossl_log_tls12_secret(backend->handle, &backend->keylog_done); - } -#endif - - /* 1 is fine - 0 is "not successful but was shut down controlled" - <0 is "handshake was not successful, because a fatal error occurred" */ - if(1 != err) { - int detail = SSL_get_error(backend->handle, err); - - if(SSL_ERROR_WANT_READ == detail) { - connssl->connecting_state = ssl_connect_2_reading; - return CURLE_OK; - } - if(SSL_ERROR_WANT_WRITE == detail) { - connssl->connecting_state = ssl_connect_2_writing; - return CURLE_OK; - } -#ifdef SSL_ERROR_WANT_ASYNC - if(SSL_ERROR_WANT_ASYNC == detail) { - connssl->connecting_state = ssl_connect_2; - return CURLE_OK; - } -#endif - else { - /* untreated error */ - unsigned long errdetail; - char error_buffer[256]=""; - CURLcode result; - long lerr; - int lib; - int reason; - - /* the connection failed, we're not waiting for anything else. */ - connssl->connecting_state = ssl_connect_2; - - /* Get the earliest error code from the thread's error queue and remove - the entry. */ - errdetail = ERR_get_error(); - - /* Extract which lib and reason */ - lib = ERR_GET_LIB(errdetail); - reason = ERR_GET_REASON(errdetail); - - if((lib == ERR_LIB_SSL) && - ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) || - (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) { - result = CURLE_PEER_FAILED_VERIFICATION; - - lerr = SSL_get_verify_result(backend->handle); - if(lerr != X509_V_OK) { - SSL_SET_OPTION_LVALUE(certverifyresult) = lerr; - msnprintf(error_buffer, sizeof(error_buffer), - "SSL certificate problem: %s", - X509_verify_cert_error_string(lerr)); - } - else - /* strcpy() is fine here as long as the string fits within - error_buffer */ - strcpy(error_buffer, "SSL certificate verification failed"); - } -#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \ - !defined(LIBRESSL_VERSION_NUMBER) && \ - !defined(OPENSSL_IS_BORINGSSL)) - /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on - OpenSSL version above v1.1.1, not LibreSSL nor BoringSSL */ - else if((lib == ERR_LIB_SSL) && - (reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)) { - /* If client certificate is required, communicate the - error to client */ - result = CURLE_SSL_CLIENTCERT; - ossl_strerror(errdetail, error_buffer, sizeof(error_buffer)); - } -#endif - else { - result = CURLE_SSL_CONNECT_ERROR; - ossl_strerror(errdetail, error_buffer, sizeof(error_buffer)); - } - - /* detail is already set to the SSL error above */ - - /* If we e.g. use SSLv2 request-method and the server doesn't like us - * (RST connection, etc.), OpenSSL gives no explanation whatsoever and - * the SO_ERROR is also lost. - */ - if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) { - const char * const hostname = SSL_HOST_NAME(); - const long int port = SSL_HOST_PORT(); - char extramsg[80]=""; - int sockerr = SOCKERRNO; - if(sockerr && detail == SSL_ERROR_SYSCALL) - Curl_strerror(sockerr, extramsg, sizeof(extramsg)); - failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%ld ", - extramsg[0] ? extramsg : SSL_ERROR_to_str(detail), - hostname, port); - return result; - } - - /* Could be a CERT problem */ - failf(data, "%s", error_buffer); - - return result; - } - } - else { - /* we connected fine, we're not waiting for anything else. */ - connssl->connecting_state = ssl_connect_3; - - /* Informational message */ - infof(data, "SSL connection using %s / %s", - SSL_get_version(backend->handle), - SSL_get_cipher(backend->handle)); - -#ifdef HAS_ALPN - /* Sets data and len to negotiated protocol, len is 0 if no protocol was - * negotiated - */ - if(conn->bits.tls_enable_alpn) { - const unsigned char *neg_protocol; - unsigned int len; - SSL_get0_alpn_selected(backend->handle, &neg_protocol, &len); - if(len) { - infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, len, neg_protocol); - -#ifdef USE_HTTP2 - if(len == ALPN_H2_LENGTH && - !memcmp(ALPN_H2, neg_protocol, len)) { - conn->alpn = CURL_HTTP_VERSION_2; - } - else -#endif - if(len == ALPN_HTTP_1_1_LENGTH && - !memcmp(ALPN_HTTP_1_1, neg_protocol, ALPN_HTTP_1_1_LENGTH)) { - conn->alpn = CURL_HTTP_VERSION_1_1; - } - } - else - infof(data, VTLS_INFOF_NO_ALPN); - - Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); - } -#endif - - return CURLE_OK; - } -} - -/* - * Heavily modified from: - * https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#OpenSSL - */ -static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert, - const char *pinnedpubkey) -{ - /* Scratch */ - int len1 = 0, len2 = 0; - unsigned char *buff1 = NULL, *temp = NULL; - - /* Result is returned to caller */ - CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - - /* if a path wasn't specified, don't pin */ - if(!pinnedpubkey) - return CURLE_OK; - - if(!cert) - return result; - - do { - /* Begin Gyrations to get the subjectPublicKeyInfo */ - /* Thanks to Viktor Dukhovni on the OpenSSL mailing list */ - - /* https://groups.google.com/group/mailing.openssl.users/browse_thread - /thread/d61858dae102c6c7 */ - len1 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), NULL); - if(len1 < 1) - break; /* failed */ - - buff1 = temp = malloc(len1); - if(!buff1) - break; /* failed */ - - /* https://www.openssl.org/docs/crypto/d2i_X509.html */ - len2 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &temp); - - /* - * These checks are verifying we got back the same values as when we - * sized the buffer. It's pretty weak since they should always be the - * same. But it gives us something to test. - */ - if((len1 != len2) || !temp || ((temp - buff1) != len1)) - break; /* failed */ - - /* End Gyrations */ - - /* The one good exit point */ - result = Curl_pin_peer_pubkey(data, pinnedpubkey, buff1, len1); - } while(0); - - if(buff1) - free(buff1); - - return result; -} - -/* - * Get the server cert, verify it and show it, etc., only call failf() if the - * 'strict' argument is TRUE as otherwise all this is for informational - * purposes only! - * - * We check certificates to authenticate the server; otherwise we risk - * man-in-the-middle attack. - */ -static CURLcode servercert(struct Curl_easy *data, - struct connectdata *conn, - struct ssl_connect_data *connssl, - bool strict) -{ - CURLcode result = CURLE_OK; - int rc; - long lerr; - X509 *issuer; - BIO *fp = NULL; - char error_buffer[256]=""; - char buffer[2048]; - const char *ptr; - BIO *mem = BIO_new(BIO_s_mem()); - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - if(!mem) { - failf(data, - "BIO_new return NULL, " OSSL_PACKAGE - " error %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); - return CURLE_OUT_OF_MEMORY; - } - - if(data->set.ssl.certinfo) - /* asked to gather certificate info */ - (void)Curl_ossl_certchain(data, connssl->backend->handle); - - backend->server_cert = SSL_get1_peer_certificate(backend->handle); - if(!backend->server_cert) { - BIO_free(mem); - if(!strict) - return CURLE_OK; - - failf(data, "SSL: couldn't get peer certificate"); - return CURLE_PEER_FAILED_VERIFICATION; - } - - infof(data, "%s certificate:", SSL_IS_PROXY() ? "Proxy" : "Server"); - - rc = x509_name_oneline(X509_get_subject_name(backend->server_cert), - buffer, sizeof(buffer)); - infof(data, " subject: %s", rc?"[NONE]":buffer); - -#ifndef CURL_DISABLE_VERBOSE_STRINGS - { - long len; - ASN1_TIME_print(mem, X509_get0_notBefore(backend->server_cert)); - len = BIO_get_mem_data(mem, (char **) &ptr); - infof(data, " start date: %.*s", (int)len, ptr); - (void)BIO_reset(mem); - - ASN1_TIME_print(mem, X509_get0_notAfter(backend->server_cert)); - len = BIO_get_mem_data(mem, (char **) &ptr); - infof(data, " expire date: %.*s", (int)len, ptr); - (void)BIO_reset(mem); - } -#endif - - BIO_free(mem); - - if(SSL_CONN_CONFIG(verifyhost)) { - result = Curl_ossl_verifyhost(data, conn, backend->server_cert); - if(result) { - X509_free(backend->server_cert); - backend->server_cert = NULL; - return result; - } - } - - rc = x509_name_oneline(X509_get_issuer_name(backend->server_cert), - buffer, sizeof(buffer)); - if(rc) { - if(strict) - failf(data, "SSL: couldn't get X509-issuer name"); - result = CURLE_PEER_FAILED_VERIFICATION; - } - else { - infof(data, " issuer: %s", buffer); - - /* We could do all sorts of certificate verification stuff here before - deallocating the certificate. */ - - /* e.g. match issuer name with provided issuer certificate */ - if(SSL_CONN_CONFIG(issuercert) || SSL_CONN_CONFIG(issuercert_blob)) { - if(SSL_CONN_CONFIG(issuercert_blob)) { - fp = BIO_new_mem_buf(SSL_CONN_CONFIG(issuercert_blob)->data, - (int)SSL_CONN_CONFIG(issuercert_blob)->len); - if(!fp) { - failf(data, - "BIO_new_mem_buf NULL, " OSSL_PACKAGE - " error %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); - X509_free(backend->server_cert); - backend->server_cert = NULL; - return CURLE_OUT_OF_MEMORY; - } - } - else { - fp = BIO_new(BIO_s_file()); - if(!fp) { - failf(data, - "BIO_new return NULL, " OSSL_PACKAGE - " error %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); - X509_free(backend->server_cert); - backend->server_cert = NULL; - return CURLE_OUT_OF_MEMORY; - } - - if(BIO_read_filename(fp, SSL_CONN_CONFIG(issuercert)) <= 0) { - if(strict) - failf(data, "SSL: Unable to open issuer cert (%s)", - SSL_CONN_CONFIG(issuercert)); - BIO_free(fp); - X509_free(backend->server_cert); - backend->server_cert = NULL; - return CURLE_SSL_ISSUER_ERROR; - } - } - - issuer = PEM_read_bio_X509(fp, NULL, ZERO_NULL, NULL); - if(!issuer) { - if(strict) - failf(data, "SSL: Unable to read issuer cert (%s)", - SSL_CONN_CONFIG(issuercert)); - BIO_free(fp); - X509_free(issuer); - X509_free(backend->server_cert); - backend->server_cert = NULL; - return CURLE_SSL_ISSUER_ERROR; - } - - if(X509_check_issued(issuer, backend->server_cert) != X509_V_OK) { - if(strict) - failf(data, "SSL: Certificate issuer check failed (%s)", - SSL_CONN_CONFIG(issuercert)); - BIO_free(fp); - X509_free(issuer); - X509_free(backend->server_cert); - backend->server_cert = NULL; - return CURLE_SSL_ISSUER_ERROR; - } - - infof(data, " SSL certificate issuer check ok (%s)", - SSL_CONN_CONFIG(issuercert)); - BIO_free(fp); - X509_free(issuer); - } - - lerr = SSL_get_verify_result(backend->handle); - SSL_SET_OPTION_LVALUE(certverifyresult) = lerr; - if(lerr != X509_V_OK) { - if(SSL_CONN_CONFIG(verifypeer)) { - /* We probably never reach this, because SSL_connect() will fail - and we return earlier if verifypeer is set? */ - if(strict) - failf(data, "SSL certificate verify result: %s (%ld)", - X509_verify_cert_error_string(lerr), lerr); - result = CURLE_PEER_FAILED_VERIFICATION; - } - else - infof(data, " SSL certificate verify result: %s (%ld)," - " continuing anyway.", - X509_verify_cert_error_string(lerr), lerr); - } - else - infof(data, " SSL certificate verify ok."); - } - -#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ - !defined(OPENSSL_NO_OCSP) - if(SSL_CONN_CONFIG(verifystatus)) { - result = verifystatus(data, connssl); - if(result) { - X509_free(backend->server_cert); - backend->server_cert = NULL; - return result; - } - } -#endif - - if(!strict) - /* when not strict, we don't bother about the verify cert problems */ - result = CURLE_OK; - - ptr = SSL_PINNED_PUB_KEY(); - if(!result && ptr) { - result = pkp_pin_peer_pubkey(data, backend->server_cert, ptr); - if(result) - failf(data, "SSL: public key does not match pinned public key"); - } - - X509_free(backend->server_cert); - backend->server_cert = NULL; - connssl->connecting_state = ssl_connect_done; - - return result; -} - -static CURLcode ossl_connect_step3(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - CURLcode result = CURLE_OK; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - - DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); - - /* - * We check certificates to authenticate the server; otherwise we risk - * man-in-the-middle attack; NEVERTHELESS, if we're told explicitly not to - * verify the peer, ignore faults and failures from the server cert - * operations. - */ - - result = servercert(data, conn, connssl, (SSL_CONN_CONFIG(verifypeer) || - SSL_CONN_CONFIG(verifyhost))); - - if(!result) - connssl->connecting_state = ssl_connect_done; - - return result; -} - -static Curl_recv ossl_recv; -static Curl_send ossl_send; - -static CURLcode ossl_connect_common(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool nonblocking, - bool *done) -{ - CURLcode result; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - curl_socket_t sockfd = conn->sock[sockindex]; - int what; - - /* check if the connection has already been established */ - if(ssl_connection_complete == connssl->state) { - *done = TRUE; - return CURLE_OK; - } - - if(ssl_connect_1 == connssl->connecting_state) { - /* Find out how much more time we're allowed */ - const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time is already up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - result = ossl_connect_step1(data, conn, sockindex); - if(result) - return result; - } - - while(ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state) { - - /* check allowed time left */ - const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - /* if ssl is expecting something, check if it's available. */ - if(connssl->connecting_state == ssl_connect_2_reading || - connssl->connecting_state == ssl_connect_2_writing) { - - curl_socket_t writefd = ssl_connect_2_writing == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - curl_socket_t readfd = ssl_connect_2_reading == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - - what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, - nonblocking?0:timeout_ms); - if(what < 0) { - /* fatal error */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - return CURLE_SSL_CONNECT_ERROR; - } - if(0 == what) { - if(nonblocking) { - *done = FALSE; - return CURLE_OK; - } - /* timeout */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - /* socket is readable or writable */ - } - - /* Run transaction, and return to the caller if it failed or if this - * connection is done nonblocking and this loop would execute again. This - * permits the owner of a multi handle to abort a connection attempt - * before step2 has completed while ensuring that a client using select() - * or epoll() will always have a valid fdset to wait on. - */ - result = ossl_connect_step2(data, conn, sockindex); - if(result || (nonblocking && - (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) - return result; - - } /* repeat step2 until all transactions are done. */ - - if(ssl_connect_3 == connssl->connecting_state) { - result = ossl_connect_step3(data, conn, sockindex); - if(result) - return result; - } - - if(ssl_connect_done == connssl->connecting_state) { - connssl->state = ssl_connection_complete; - conn->recv[sockindex] = ossl_recv; - conn->send[sockindex] = ossl_send; - *done = TRUE; - } - else - *done = FALSE; - - /* Reset our connect state machine */ - connssl->connecting_state = ssl_connect_1; - - return CURLE_OK; -} - -static CURLcode ossl_connect_nonblocking(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool *done) -{ - return ossl_connect_common(data, conn, sockindex, TRUE, done); -} - -static CURLcode ossl_connect(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - CURLcode result; - bool done = FALSE; - - result = ossl_connect_common(data, conn, sockindex, FALSE, &done); - if(result) - return result; - - DEBUGASSERT(done); - - return CURLE_OK; -} - -static bool ossl_data_pending(const struct connectdata *conn, - int connindex) -{ - const struct ssl_connect_data *connssl = &conn->ssl[connindex]; - DEBUGASSERT(connssl->backend); - if(connssl->backend->handle && SSL_pending(connssl->backend->handle)) - return TRUE; -#ifndef CURL_DISABLE_PROXY - { - const struct ssl_connect_data *proxyssl = &conn->proxy_ssl[connindex]; - DEBUGASSERT(proxyssl->backend); - if(proxyssl->backend->handle && SSL_pending(proxyssl->backend->handle)) - return TRUE; - } -#endif - return FALSE; -} - -static size_t ossl_version(char *buffer, size_t size); - -static ssize_t ossl_send(struct Curl_easy *data, - int sockindex, - const void *mem, - size_t len, - CURLcode *curlcode) -{ - /* SSL_write() is said to return 'int' while write() and send() returns - 'size_t' */ - int err; - char error_buffer[256]; - unsigned long sslerror; - int memlen; - int rc; - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - ERR_clear_error(); - - memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; - set_logger(conn, data); - rc = SSL_write(backend->handle, mem, memlen); - - if(rc <= 0) { - err = SSL_get_error(backend->handle, rc); - - switch(err) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - /* The operation did not complete; the same TLS/SSL I/O function - should be called again later. This is basically an EWOULDBLOCK - equivalent. */ - *curlcode = CURLE_AGAIN; - return -1; - case SSL_ERROR_SYSCALL: - { - int sockerr = SOCKERRNO; - sslerror = ERR_get_error(); - if(sslerror) - ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)); - else if(sockerr) - Curl_strerror(sockerr, error_buffer, sizeof(error_buffer)); - else { - strncpy(error_buffer, SSL_ERROR_to_str(err), sizeof(error_buffer)); - error_buffer[sizeof(error_buffer) - 1] = '\0'; - } - failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d", - error_buffer, sockerr); - *curlcode = CURLE_SEND_ERROR; - return -1; - } - case SSL_ERROR_SSL: - /* A failure in the SSL library occurred, usually a protocol error. - The OpenSSL error queue contains more information on the error. */ - sslerror = ERR_get_error(); - if(ERR_GET_LIB(sslerror) == ERR_LIB_SSL && - ERR_GET_REASON(sslerror) == SSL_R_BIO_NOT_SET && - conn->ssl[sockindex].state == ssl_connection_complete -#ifndef CURL_DISABLE_PROXY - && conn->proxy_ssl[sockindex].state == ssl_connection_complete -#endif - ) { - char ver[120]; - (void)ossl_version(ver, sizeof(ver)); - failf(data, "Error: %s does not support double SSL tunneling.", ver); - } - else - failf(data, "SSL_write() error: %s", - ossl_strerror(sslerror, error_buffer, sizeof(error_buffer))); - *curlcode = CURLE_SEND_ERROR; - return -1; - } - /* a true error */ - failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d", - SSL_ERROR_to_str(err), SOCKERRNO); - *curlcode = CURLE_SEND_ERROR; - return -1; - } - *curlcode = CURLE_OK; - return (ssize_t)rc; /* number of bytes */ -} - -static ssize_t ossl_recv(struct Curl_easy *data, /* transfer */ - int num, /* socketindex */ - char *buf, /* store read data here */ - size_t buffersize, /* max amount to read */ - CURLcode *curlcode) -{ - char error_buffer[256]; - unsigned long sslerror; - ssize_t nread; - int buffsize; - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[num]; - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - ERR_clear_error(); - - buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; - set_logger(conn, data); - nread = (ssize_t)SSL_read(backend->handle, buf, buffsize); - if(nread <= 0) { - /* failed SSL_read */ - int err = SSL_get_error(backend->handle, (int)nread); - - switch(err) { - case SSL_ERROR_NONE: /* this is not an error */ - break; - case SSL_ERROR_ZERO_RETURN: /* no more data */ - /* close_notify alert */ - if(num == FIRSTSOCKET) - /* mark the connection for close if it is indeed the control - connection */ - connclose(conn, "TLS close_notify"); - break; - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - /* there's data pending, re-invoke SSL_read() */ - *curlcode = CURLE_AGAIN; - return -1; - default: - /* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return - value/errno" */ - /* https://www.openssl.org/docs/crypto/ERR_get_error.html */ - sslerror = ERR_get_error(); - if((nread < 0) || sslerror) { - /* If the return code was negative or there actually is an error in the - queue */ - int sockerr = SOCKERRNO; - if(sslerror) - ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)); - else if(sockerr && err == SSL_ERROR_SYSCALL) - Curl_strerror(sockerr, error_buffer, sizeof(error_buffer)); - else { - strncpy(error_buffer, SSL_ERROR_to_str(err), sizeof(error_buffer)); - error_buffer[sizeof(error_buffer) - 1] = '\0'; - } - failf(data, OSSL_PACKAGE " SSL_read: %s, errno %d", - error_buffer, sockerr); - *curlcode = CURLE_RECV_ERROR; - return -1; - } - /* For debug builds be a little stricter and error on any - SSL_ERROR_SYSCALL. For example a server may have closed the connection - abruptly without a close_notify alert. For compatibility with older - peers we don't do this by default. #4624 - - We can use this to gauge how many users may be affected, and - if it goes ok eventually transition to allow in dev and release with - the newest OpenSSL: #if (OPENSSL_VERSION_NUMBER >= 0x10101000L) */ -#ifdef DEBUGBUILD - if(err == SSL_ERROR_SYSCALL) { - int sockerr = SOCKERRNO; - if(sockerr) - Curl_strerror(sockerr, error_buffer, sizeof(error_buffer)); - else { - msnprintf(error_buffer, sizeof(error_buffer), - "Connection closed abruptly"); - } - failf(data, OSSL_PACKAGE " SSL_read: %s, errno %d" - " (Fatal because this is a curl debug build)", - error_buffer, sockerr); - *curlcode = CURLE_RECV_ERROR; - return -1; - } -#endif - } - } - return nread; -} - -static size_t ossl_version(char *buffer, size_t size) -{ -#ifdef LIBRESSL_VERSION_NUMBER -#ifdef HAVE_OPENSSL_VERSION - char *p; - int count; - const char *ver = OpenSSL_version(OPENSSL_VERSION); - const char expected[] = OSSL_PACKAGE " "; /* ie "LibreSSL " */ - if(Curl_strncasecompare(ver, expected, sizeof(expected) - 1)) { - ver += sizeof(expected) - 1; - } - count = msnprintf(buffer, size, "%s/%s", OSSL_PACKAGE, ver); - for(p = buffer; *p; ++p) { - if(ISBLANK(*p)) - *p = '_'; - } - return count; -#else - return msnprintf(buffer, size, "%s/%lx.%lx.%lx", - OSSL_PACKAGE, - (LIBRESSL_VERSION_NUMBER>>28)&0xf, - (LIBRESSL_VERSION_NUMBER>>20)&0xff, - (LIBRESSL_VERSION_NUMBER>>12)&0xff); -#endif -#elif defined(OPENSSL_IS_BORINGSSL) -#ifdef CURL_BORINGSSL_VERSION - return msnprintf(buffer, size, "%s/%s", - OSSL_PACKAGE, - CURL_BORINGSSL_VERSION); -#else - return msnprintf(buffer, size, OSSL_PACKAGE); -#endif -#elif defined(HAVE_OPENSSL_VERSION) && defined(OPENSSL_VERSION_STRING) - return msnprintf(buffer, size, "%s/%s", - OSSL_PACKAGE, OpenSSL_version(OPENSSL_VERSION_STRING)); -#else - /* not LibreSSL, BoringSSL and not using OpenSSL_version */ - - char sub[3]; - unsigned long ssleay_value; - sub[2]='\0'; - sub[1]='\0'; - ssleay_value = OpenSSL_version_num(); - if(ssleay_value < 0x906000) { - ssleay_value = SSLEAY_VERSION_NUMBER; - sub[0]='\0'; - } - else { - if(ssleay_value&0xff0) { - int minor_ver = (ssleay_value >> 4) & 0xff; - if(minor_ver > 26) { - /* handle extended version introduced for 0.9.8za */ - sub[1] = (char) ((minor_ver - 1) % 26 + 'a' + 1); - sub[0] = 'z'; - } - else { - sub[0] = (char) (minor_ver + 'a' - 1); - } - } - else - sub[0]='\0'; - } - - return msnprintf(buffer, size, "%s/%lx.%lx.%lx%s" -#ifdef OPENSSL_FIPS - "-fips" -#endif - , - OSSL_PACKAGE, - (ssleay_value>>28)&0xf, - (ssleay_value>>20)&0xff, - (ssleay_value>>12)&0xff, - sub); -#endif /* OPENSSL_IS_BORINGSSL */ -} - -/* can be called with data == NULL */ -static CURLcode ossl_random(struct Curl_easy *data, - unsigned char *entropy, size_t length) -{ - int rc; - if(data) { - if(ossl_seed(data)) /* Initiate the seed if not already done */ - return CURLE_FAILED_INIT; /* couldn't seed for some reason */ - } - else { - if(!rand_enough()) - return CURLE_FAILED_INIT; - } - /* RAND_bytes() returns 1 on success, 0 otherwise. */ - rc = RAND_bytes(entropy, curlx_uztosi(length)); - return (rc == 1 ? CURLE_OK : CURLE_FAILED_INIT); -} - -#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) -static CURLcode ossl_sha256sum(const unsigned char *tmp, /* input */ - size_t tmplen, - unsigned char *sha256sum /* output */, - size_t unused) -{ - EVP_MD_CTX *mdctx; - unsigned int len = 0; - (void) unused; - - mdctx = EVP_MD_CTX_create(); - if(!mdctx) - return CURLE_OUT_OF_MEMORY; - EVP_DigestInit(mdctx, EVP_sha256()); - EVP_DigestUpdate(mdctx, tmp, tmplen); - EVP_DigestFinal_ex(mdctx, sha256sum, &len); - EVP_MD_CTX_destroy(mdctx); - return CURLE_OK; -} -#endif - -static bool ossl_cert_status_request(void) -{ -#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ - !defined(OPENSSL_NO_OCSP) - return TRUE; -#else - return FALSE; -#endif -} - -static void *ossl_get_internals(struct ssl_connect_data *connssl, - CURLINFO info) -{ - /* Legacy: CURLINFO_TLS_SESSION must return an SSL_CTX pointer. */ - struct ssl_backend_data *backend = connssl->backend; - DEBUGASSERT(backend); - return info == CURLINFO_TLS_SESSION ? - (void *)backend->ctx : (void *)backend->handle; -} - -static bool ossl_associate_connection(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - DEBUGASSERT(backend); - - /* If we don't have SSL context, do nothing. */ - if(!backend->handle) - return FALSE; - - if(SSL_SET_OPTION(primary.sessionid)) { - int data_idx = ossl_get_ssl_data_index(); - int connectdata_idx = ossl_get_ssl_conn_index(); - int sockindex_idx = ossl_get_ssl_sockindex_index(); - int proxy_idx = ossl_get_proxy_index(); - - if(data_idx >= 0 && connectdata_idx >= 0 && sockindex_idx >= 0 && - proxy_idx >= 0) { - int data_status, conn_status, sockindex_status, proxy_status; - - /* Store the data needed for the "new session" callback. - * The sockindex is stored as a pointer to an array element. */ - data_status = SSL_set_ex_data(backend->handle, data_idx, data); - conn_status = SSL_set_ex_data(backend->handle, connectdata_idx, conn); - sockindex_status = SSL_set_ex_data(backend->handle, sockindex_idx, - conn->sock + sockindex); -#ifndef CURL_DISABLE_PROXY - proxy_status = SSL_set_ex_data(backend->handle, proxy_idx, - SSL_IS_PROXY() ? (void *) 1 : NULL); -#else - proxy_status = SSL_set_ex_data(backend->handle, proxy_idx, NULL); -#endif - if(data_status && conn_status && sockindex_status && proxy_status) - return TRUE; - } - return FALSE; - } - return TRUE; -} - -/* - * Starting with TLS 1.3, the ossl_new_session_cb callback gets called after - * the handshake. If the transfer that sets up the callback gets killed before - * this callback arrives, we must make sure to properly clear the data to - * avoid UAF problems. A future optimization could be to instead store another - * transfer that might still be using the same connection. - */ - -static void ossl_disassociate_connection(struct Curl_easy *data, - int sockindex) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - DEBUGASSERT(backend); - - /* If we don't have SSL context, do nothing. */ - if(!backend->handle) - return; - - if(SSL_SET_OPTION(primary.sessionid)) { - int data_idx = ossl_get_ssl_data_index(); - int connectdata_idx = ossl_get_ssl_conn_index(); - int sockindex_idx = ossl_get_ssl_sockindex_index(); - int proxy_idx = ossl_get_proxy_index(); - - if(data_idx >= 0 && connectdata_idx >= 0 && sockindex_idx >= 0 && - proxy_idx >= 0) { - /* Disable references to data in "new session" callback to avoid - * accessing a stale pointer. */ - SSL_set_ex_data(backend->handle, data_idx, NULL); - SSL_set_ex_data(backend->handle, connectdata_idx, NULL); - SSL_set_ex_data(backend->handle, sockindex_idx, NULL); - SSL_set_ex_data(backend->handle, proxy_idx, NULL); - } - } -} - -const struct Curl_ssl Curl_ssl_openssl = { - { CURLSSLBACKEND_OPENSSL, "openssl" }, /* info */ - - SSLSUPP_CA_PATH | - SSLSUPP_CAINFO_BLOB | - SSLSUPP_CERTINFO | - SSLSUPP_PINNEDPUBKEY | - SSLSUPP_SSL_CTX | -#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES - SSLSUPP_TLS13_CIPHERSUITES | -#endif - SSLSUPP_HTTPS_PROXY, - - sizeof(struct ssl_backend_data), - - ossl_init, /* init */ - ossl_cleanup, /* cleanup */ - ossl_version, /* version */ - ossl_check_cxn, /* check_cxn */ - ossl_shutdown, /* shutdown */ - ossl_data_pending, /* data_pending */ - ossl_random, /* random */ - ossl_cert_status_request, /* cert_status_request */ - ossl_connect, /* connect */ - ossl_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_getsock, /* getsock */ - ossl_get_internals, /* get_internals */ - ossl_close, /* close_one */ - ossl_close_all, /* close_all */ - ossl_session_free, /* session_free */ - ossl_set_engine, /* set_engine */ - ossl_set_engine_default, /* set_engine_default */ - ossl_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ -#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) - ossl_sha256sum, /* sha256sum */ -#else - NULL, /* sha256sum */ -#endif - ossl_associate_connection, /* associate_connection */ - ossl_disassociate_connection /* disassociate_connection */ -}; - -#endif /* USE_OPENSSL */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+/*
+ * Source file for all OpenSSL-specific code for the TLS/SSL layer. No code
+ * but vtls.c should ever call or use these functions.
+ */
+
+#include "curl_setup.h"
+
+#if defined(USE_QUICHE) || defined(USE_OPENSSL)
+
+#include <limits.h>
+
+/* Wincrypt must be included before anything that could include OpenSSL. */
+#if defined(USE_WIN32_CRYPTO)
+#include <wincrypt.h>
+/* Undefine wincrypt conflicting symbols for BoringSSL. */
+#undef X509_NAME
+#undef X509_EXTENSIONS
+#undef PKCS7_ISSUER_AND_SERIAL
+#undef PKCS7_SIGNER_INFO
+#undef OCSP_REQUEST
+#undef OCSP_RESPONSE
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "formdata.h" /* for the boundary function */
+#include "url.h" /* for the ssl config check function */
+#include "inet_pton.h"
+#include "openssl.h"
+#include "connect.h"
+#include "slist.h"
+#include "select.h"
+#include "vtls.h"
+#include "vtls_int.h"
+#include "vauth/vauth.h"
+#include "keylog.h"
+#include "strcase.h"
+#include "hostcheck.h"
+#include "multiif.h"
+#include "strerror.h"
+#include "curl_printf.h"
+
+#include <openssl/ssl.h>
+#include <openssl/rand.h>
+#include <openssl/x509v3.h>
+#ifndef OPENSSL_NO_DSA
+#include <openssl/dsa.h>
+#endif
+#include <openssl/dh.h>
+#include <openssl/err.h>
+#include <openssl/md5.h>
+#include <openssl/conf.h>
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+#include <openssl/bio.h>
+#include <openssl/buffer.h>
+#include <openssl/pkcs12.h>
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP)
+#include <openssl/ocsp.h>
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090700fL) && /* 0.9.7 or later */ \
+ !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_UI_CONSOLE)
+#define USE_OPENSSL_ENGINE
+#include <openssl/engine.h>
+#endif
+
+#include "warnless.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* Uncomment the ALLOW_RENEG line to a real #define if you want to allow TLS
+ renegotiations when built with BoringSSL. Renegotiating is non-compliant
+ with HTTP/2 and "an extremely dangerous protocol feature". Beware.
+
+#define ALLOW_RENEG 1
+ */
+
+#ifndef OPENSSL_VERSION_NUMBER
+#error "OPENSSL_VERSION_NUMBER not defined"
+#endif
+
+#ifdef USE_OPENSSL_ENGINE
+#include <openssl/ui.h>
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x00909000L
+#define SSL_METHOD_QUAL const
+#else
+#define SSL_METHOD_QUAL
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
+#define HAVE_ERR_REMOVE_THREAD_STATE 1
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && /* OpenSSL 1.1.0+ */ \
+ !(defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+#define SSLEAY_VERSION_NUMBER OPENSSL_VERSION_NUMBER
+#define HAVE_X509_GET0_EXTENSIONS 1 /* added in 1.1.0 -pre1 */
+#define HAVE_OPAQUE_EVP_PKEY 1 /* since 1.1.0 -pre3 */
+#define HAVE_OPAQUE_RSA_DSA_DH 1 /* since 1.1.0 -pre5 */
+#define CONST_EXTS const
+#define HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED 1
+
+/* funny typecast define due to difference in API */
+#ifdef LIBRESSL_VERSION_NUMBER
+#define ARG2_X509_signature_print (X509_ALGOR *)
+#else
+#define ARG2_X509_signature_print
+#endif
+
+#else
+/* For OpenSSL before 1.1.0 */
+#define ASN1_STRING_get0_data(x) ASN1_STRING_data(x)
+#define X509_get0_notBefore(x) X509_get_notBefore(x)
+#define X509_get0_notAfter(x) X509_get_notAfter(x)
+#define CONST_EXTS /* nope */
+#ifndef LIBRESSL_VERSION_NUMBER
+#define OpenSSL_version_num() SSLeay()
+#endif
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) && /* 1.0.2 or later */ \
+ !(defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+#define HAVE_X509_GET0_SIGNATURE 1
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) /* 1.0.2 or later */
+#define HAVE_SSL_GET_SHUTDOWN 1
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x10002003L && \
+ OPENSSL_VERSION_NUMBER <= 0x10002FFFL && \
+ !defined(OPENSSL_NO_COMP)
+#define HAVE_SSL_COMP_FREE_COMPRESSION_METHODS 1
+#endif
+
+#if (OPENSSL_VERSION_NUMBER < 0x0090808fL)
+/* not present in older OpenSSL */
+#define OPENSSL_load_builtin_modules(x)
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
+#define HAVE_EVP_PKEY_GET_PARAMS 1
+#else
+#define SSL_get1_peer_certificate SSL_get_peer_certificate
+#endif
+
+#ifdef HAVE_EVP_PKEY_GET_PARAMS
+#include <openssl/core_names.h>
+#define DECLARE_PKEY_PARAM_BIGNUM(name) BIGNUM *name = NULL
+#define FREE_PKEY_PARAM_BIGNUM(name) BN_clear_free(name)
+#else
+#define DECLARE_PKEY_PARAM_BIGNUM(name) const BIGNUM *name
+#define FREE_PKEY_PARAM_BIGNUM(name)
+#endif
+
+/*
+ * Whether SSL_CTX_set_keylog_callback is available.
+ * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287
+ * BoringSSL: supported since d28f59c27bac (committed 2015-11-19)
+ * LibreSSL: unsupported in at least 2.7.2 (explicitly check for it since it
+ * lies and pretends to be OpenSSL 2.0.0).
+ */
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \
+ !defined(LIBRESSL_VERSION_NUMBER)) || \
+ defined(OPENSSL_IS_BORINGSSL)
+#define HAVE_KEYLOG_CALLBACK
+#endif
+
+/* Whether SSL_CTX_set_ciphersuites is available.
+ * OpenSSL: supported since 1.1.1 (commit a53b5be6a05)
+ * BoringSSL: no
+ * LibreSSL: no
+ */
+#if ((OPENSSL_VERSION_NUMBER >= 0x10101000L) && \
+ !defined(LIBRESSL_VERSION_NUMBER) && \
+ !defined(OPENSSL_IS_BORINGSSL))
+#define HAVE_SSL_CTX_SET_CIPHERSUITES
+#define HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH
+#endif
+
+/*
+ * Whether SSL_CTX_set1_curves_list is available.
+ * OpenSSL: supported since 1.0.2, see
+ * https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set1_groups.html
+ * BoringSSL: supported since 5fd1807d95f7 (committed 2016-09-30)
+ * LibreSSL: since 2.5.3 (April 12, 2017)
+ */
+#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) || \
+ defined(OPENSSL_IS_BORINGSSL)
+#define HAVE_SSL_CTX_SET_EC_CURVES
+#endif
+
+#if defined(LIBRESSL_VERSION_NUMBER)
+#define OSSL_PACKAGE "LibreSSL"
+#elif defined(OPENSSL_IS_BORINGSSL)
+#define OSSL_PACKAGE "BoringSSL"
+#else
+#define OSSL_PACKAGE "OpenSSL"
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+/* up2date versions of OpenSSL maintain reasonably secure defaults without
+ * breaking compatibility, so it is better not to override the defaults in curl
+ */
+#define DEFAULT_CIPHER_SELECTION NULL
+#else
+/* ... but it is not the case with old versions of OpenSSL */
+#define DEFAULT_CIPHER_SELECTION \
+ "ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH"
+#endif
+
+#ifdef HAVE_OPENSSL_SRP
+/* the function exists */
+#ifdef USE_TLS_SRP
+/* the functionality is not disabled */
+#define USE_OPENSSL_SRP
+#endif
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+#define HAVE_RANDOM_INIT_BY_DEFAULT 1
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \
+ !(defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x2070100fL) && \
+ !defined(OPENSSL_IS_BORINGSSL)
+#define HAVE_OPENSSL_VERSION
+#endif
+
+/*
+ * Whether the OpenSSL version has the API needed to support sharing an
+ * X509_STORE between connections. The API is:
+ * * `X509_STORE_up_ref` -- Introduced: OpenSSL 1.1.0.
+ */
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* OpenSSL >= 1.1.0 */
+#define HAVE_SSL_X509_STORE_SHARE
+#endif
+
+/* What API version do we use? */
+#if defined(LIBRESSL_VERSION_NUMBER)
+#define USE_PRE_1_1_API (LIBRESSL_VERSION_NUMBER < 0x2070000f)
+#else /* !LIBRESSL_VERSION_NUMBER */
+#define USE_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L)
+#endif /* !LIBRESSL_VERSION_NUMBER */
+
+struct ssl_backend_data {
+ struct Curl_easy *logger; /* transfer handle to pass trace logs to, only
+ using sockindex 0 */
+ /* these ones requires specific SSL-types */
+ SSL_CTX* ctx;
+ SSL* handle;
+ X509* server_cert;
+ CURLcode io_result; /* result of last BIO cfilter operation */
+#ifndef HAVE_KEYLOG_CALLBACK
+ /* Set to true once a valid keylog entry has been created to avoid dupes. */
+ bool keylog_done;
+#endif
+};
+
+#if defined(HAVE_SSL_X509_STORE_SHARE)
+struct multi_ssl_backend_data {
+ char *CAfile; /* CAfile path used to generate X509 store */
+ X509_STORE *store; /* cached X509 store or NULL if none */
+ struct curltime time; /* when the cached store was created */
+};
+#endif /* HAVE_SSL_X509_STORE_SHARE */
+
+#define push_certinfo(_label, _num) \
+do { \
+ long info_len = BIO_get_mem_data(mem, &ptr); \
+ Curl_ssl_push_certinfo_len(data, _num, _label, ptr, info_len); \
+ if(1 != BIO_reset(mem)) \
+ break; \
+} while(0)
+
+static void pubkey_show(struct Curl_easy *data,
+ BIO *mem,
+ int num,
+ const char *type,
+ const char *name,
+ const BIGNUM *bn)
+{
+ char *ptr;
+ char namebuf[32];
+
+ msnprintf(namebuf, sizeof(namebuf), "%s(%s)", type, name);
+
+ if(bn)
+ BN_print(mem, bn);
+ push_certinfo(namebuf, num);
+}
+
+#ifdef HAVE_OPAQUE_RSA_DSA_DH
+#define print_pubkey_BN(_type, _name, _num) \
+ pubkey_show(data, mem, _num, #_type, #_name, _name)
+
+#else
+#define print_pubkey_BN(_type, _name, _num) \
+do { \
+ if(_type->_name) { \
+ pubkey_show(data, mem, _num, #_type, #_name, _type->_name); \
+ } \
+} while(0)
+#endif
+
+static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len)
+{
+ int i, ilen;
+
+ ilen = (int)len;
+ if(ilen < 0)
+ return 1; /* buffer too big */
+
+ i = i2t_ASN1_OBJECT(buf, ilen, a);
+
+ if(i >= ilen)
+ return 1; /* buffer too small */
+
+ return 0;
+}
+
+static void X509V3_ext(struct Curl_easy *data,
+ int certnum,
+ CONST_EXTS STACK_OF(X509_EXTENSION) *exts)
+{
+ int i;
+
+ if((int)sk_X509_EXTENSION_num(exts) <= 0)
+ /* no extensions, bail out */
+ return;
+
+ for(i = 0; i < (int)sk_X509_EXTENSION_num(exts); i++) {
+ ASN1_OBJECT *obj;
+ X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i);
+ BUF_MEM *biomem;
+ char namebuf[128];
+ BIO *bio_out = BIO_new(BIO_s_mem());
+
+ if(!bio_out)
+ return;
+
+ obj = X509_EXTENSION_get_object(ext);
+
+ asn1_object_dump(obj, namebuf, sizeof(namebuf));
+
+ if(!X509V3_EXT_print(bio_out, ext, 0, 0))
+ ASN1_STRING_print(bio_out, (ASN1_STRING *)X509_EXTENSION_get_data(ext));
+
+ BIO_get_mem_ptr(bio_out, &biomem);
+ Curl_ssl_push_certinfo_len(data, certnum, namebuf, biomem->data,
+ biomem->length);
+ BIO_free(bio_out);
+ }
+}
+
+#ifdef OPENSSL_IS_BORINGSSL
+typedef size_t numcert_t;
+#else
+typedef int numcert_t;
+#endif
+
+CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
+{
+ CURLcode result;
+ STACK_OF(X509) *sk;
+ int i;
+ numcert_t numcerts;
+ BIO *mem;
+
+ DEBUGASSERT(ssl);
+
+ sk = SSL_get_peer_cert_chain(ssl);
+ if(!sk) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ numcerts = sk_X509_num(sk);
+
+ result = Curl_ssl_init_certinfo(data, (int)numcerts);
+ if(result) {
+ return result;
+ }
+
+ mem = BIO_new(BIO_s_mem());
+ if(!mem) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ for(i = 0; i < (int)numcerts; i++) {
+ ASN1_INTEGER *num;
+ X509 *x = sk_X509_value(sk, i);
+ EVP_PKEY *pubkey = NULL;
+ int j;
+ char *ptr;
+ const ASN1_BIT_STRING *psig = NULL;
+
+ X509_NAME_print_ex(mem, X509_get_subject_name(x), 0, XN_FLAG_ONELINE);
+ push_certinfo("Subject", i);
+
+ X509_NAME_print_ex(mem, X509_get_issuer_name(x), 0, XN_FLAG_ONELINE);
+ push_certinfo("Issuer", i);
+
+ BIO_printf(mem, "%lx", X509_get_version(x));
+ push_certinfo("Version", i);
+
+ num = X509_get_serialNumber(x);
+ if(num->type == V_ASN1_NEG_INTEGER)
+ BIO_puts(mem, "-");
+ for(j = 0; j < num->length; j++)
+ BIO_printf(mem, "%02x", num->data[j]);
+ push_certinfo("Serial Number", i);
+
+#if defined(HAVE_X509_GET0_SIGNATURE) && defined(HAVE_X509_GET0_EXTENSIONS)
+ {
+ const X509_ALGOR *sigalg = NULL;
+ X509_PUBKEY *xpubkey = NULL;
+ ASN1_OBJECT *pubkeyoid = NULL;
+
+ X509_get0_signature(&psig, &sigalg, x);
+ if(sigalg) {
+ i2a_ASN1_OBJECT(mem, sigalg->algorithm);
+ push_certinfo("Signature Algorithm", i);
+ }
+
+ xpubkey = X509_get_X509_PUBKEY(x);
+ if(xpubkey) {
+ X509_PUBKEY_get0_param(&pubkeyoid, NULL, NULL, NULL, xpubkey);
+ if(pubkeyoid) {
+ i2a_ASN1_OBJECT(mem, pubkeyoid);
+ push_certinfo("Public Key Algorithm", i);
+ }
+ }
+
+ X509V3_ext(data, i, X509_get0_extensions(x));
+ }
+#else
+ {
+ /* before OpenSSL 1.0.2 */
+ X509_CINF *cinf = x->cert_info;
+
+ i2a_ASN1_OBJECT(mem, cinf->signature->algorithm);
+ push_certinfo("Signature Algorithm", i);
+
+ i2a_ASN1_OBJECT(mem, cinf->key->algor->algorithm);
+ push_certinfo("Public Key Algorithm", i);
+
+ X509V3_ext(data, i, cinf->extensions);
+
+ psig = x->signature;
+ }
+#endif
+
+ ASN1_TIME_print(mem, X509_get0_notBefore(x));
+ push_certinfo("Start date", i);
+
+ ASN1_TIME_print(mem, X509_get0_notAfter(x));
+ push_certinfo("Expire date", i);
+
+ pubkey = X509_get_pubkey(x);
+ if(!pubkey)
+ infof(data, " Unable to load public key");
+ else {
+ int pktype;
+#ifdef HAVE_OPAQUE_EVP_PKEY
+ pktype = EVP_PKEY_id(pubkey);
+#else
+ pktype = pubkey->type;
+#endif
+ switch(pktype) {
+ case EVP_PKEY_RSA:
+ {
+#ifndef HAVE_EVP_PKEY_GET_PARAMS
+ RSA *rsa;
+#ifdef HAVE_OPAQUE_EVP_PKEY
+ rsa = EVP_PKEY_get0_RSA(pubkey);
+#else
+ rsa = pubkey->pkey.rsa;
+#endif /* HAVE_OPAQUE_EVP_PKEY */
+#endif /* !HAVE_EVP_PKEY_GET_PARAMS */
+
+ {
+#ifdef HAVE_OPAQUE_RSA_DSA_DH
+ DECLARE_PKEY_PARAM_BIGNUM(n);
+ DECLARE_PKEY_PARAM_BIGNUM(e);
+#ifdef HAVE_EVP_PKEY_GET_PARAMS
+ EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_N, &n);
+ EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_E, &e);
+#else
+ RSA_get0_key(rsa, &n, &e, NULL);
+#endif /* HAVE_EVP_PKEY_GET_PARAMS */
+ BIO_printf(mem, "%d", BN_num_bits(n));
+#else
+ BIO_printf(mem, "%d", BN_num_bits(rsa->n));
+#endif /* HAVE_OPAQUE_RSA_DSA_DH */
+ push_certinfo("RSA Public Key", i);
+ print_pubkey_BN(rsa, n, i);
+ print_pubkey_BN(rsa, e, i);
+ FREE_PKEY_PARAM_BIGNUM(n);
+ FREE_PKEY_PARAM_BIGNUM(e);
+ }
+
+ break;
+ }
+ case EVP_PKEY_DSA:
+ {
+#ifndef OPENSSL_NO_DSA
+#ifndef HAVE_EVP_PKEY_GET_PARAMS
+ DSA *dsa;
+#ifdef HAVE_OPAQUE_EVP_PKEY
+ dsa = EVP_PKEY_get0_DSA(pubkey);
+#else
+ dsa = pubkey->pkey.dsa;
+#endif /* HAVE_OPAQUE_EVP_PKEY */
+#endif /* !HAVE_EVP_PKEY_GET_PARAMS */
+ {
+#ifdef HAVE_OPAQUE_RSA_DSA_DH
+ DECLARE_PKEY_PARAM_BIGNUM(p);
+ DECLARE_PKEY_PARAM_BIGNUM(q);
+ DECLARE_PKEY_PARAM_BIGNUM(g);
+ DECLARE_PKEY_PARAM_BIGNUM(pub_key);
+#ifdef HAVE_EVP_PKEY_GET_PARAMS
+ EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p);
+ EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q);
+ EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g);
+ EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key);
+#else
+ DSA_get0_pqg(dsa, &p, &q, &g);
+ DSA_get0_key(dsa, &pub_key, NULL);
+#endif /* HAVE_EVP_PKEY_GET_PARAMS */
+#endif /* HAVE_OPAQUE_RSA_DSA_DH */
+ print_pubkey_BN(dsa, p, i);
+ print_pubkey_BN(dsa, q, i);
+ print_pubkey_BN(dsa, g, i);
+ print_pubkey_BN(dsa, pub_key, i);
+ FREE_PKEY_PARAM_BIGNUM(p);
+ FREE_PKEY_PARAM_BIGNUM(q);
+ FREE_PKEY_PARAM_BIGNUM(g);
+ FREE_PKEY_PARAM_BIGNUM(pub_key);
+ }
+#endif /* !OPENSSL_NO_DSA */
+ break;
+ }
+ case EVP_PKEY_DH:
+ {
+#ifndef HAVE_EVP_PKEY_GET_PARAMS
+ DH *dh;
+#ifdef HAVE_OPAQUE_EVP_PKEY
+ dh = EVP_PKEY_get0_DH(pubkey);
+#else
+ dh = pubkey->pkey.dh;
+#endif /* HAVE_OPAQUE_EVP_PKEY */
+#endif /* !HAVE_EVP_PKEY_GET_PARAMS */
+ {
+#ifdef HAVE_OPAQUE_RSA_DSA_DH
+ DECLARE_PKEY_PARAM_BIGNUM(p);
+ DECLARE_PKEY_PARAM_BIGNUM(q);
+ DECLARE_PKEY_PARAM_BIGNUM(g);
+ DECLARE_PKEY_PARAM_BIGNUM(pub_key);
+#ifdef HAVE_EVP_PKEY_GET_PARAMS
+ EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p);
+ EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q);
+ EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g);
+ EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key);
+#else
+ DH_get0_pqg(dh, &p, &q, &g);
+ DH_get0_key(dh, &pub_key, NULL);
+#endif /* HAVE_EVP_PKEY_GET_PARAMS */
+ print_pubkey_BN(dh, p, i);
+ print_pubkey_BN(dh, q, i);
+ print_pubkey_BN(dh, g, i);
+#else
+ print_pubkey_BN(dh, p, i);
+ print_pubkey_BN(dh, g, i);
+#endif /* HAVE_OPAQUE_RSA_DSA_DH */
+ print_pubkey_BN(dh, pub_key, i);
+ FREE_PKEY_PARAM_BIGNUM(p);
+ FREE_PKEY_PARAM_BIGNUM(q);
+ FREE_PKEY_PARAM_BIGNUM(g);
+ FREE_PKEY_PARAM_BIGNUM(pub_key);
+ }
+ break;
+ }
+ }
+ EVP_PKEY_free(pubkey);
+ }
+
+ if(psig) {
+ for(j = 0; j < psig->length; j++)
+ BIO_printf(mem, "%02x:", psig->data[j]);
+ push_certinfo("Signature", i);
+ }
+
+ PEM_write_bio_X509(mem, x);
+ push_certinfo("Cert", i);
+ }
+
+ BIO_free(mem);
+
+ return CURLE_OK;
+}
+
+#endif /* quiche or OpenSSL */
+
+#ifdef USE_OPENSSL
+
+#if USE_PRE_1_1_API
+#if !defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER < 0x2070000fL
+#define BIO_set_init(x,v) ((x)->init=(v))
+#define BIO_get_data(x) ((x)->ptr)
+#define BIO_set_data(x,v) ((x)->ptr=(v))
+#endif
+#define BIO_get_shutdown(x) ((x)->shutdown)
+#define BIO_set_shutdown(x,v) ((x)->shutdown=(v))
+#endif /* USE_PRE_1_1_API */
+
+static int bio_cf_create(BIO *bio)
+{
+ BIO_set_shutdown(bio, 1);
+ BIO_set_init(bio, 1);
+#if USE_PRE_1_1_API
+ bio->num = -1;
+#endif
+ BIO_set_data(bio, NULL);
+ return 1;
+}
+
+static int bio_cf_destroy(BIO *bio)
+{
+ if(!bio)
+ return 0;
+ return 1;
+}
+
+static long bio_cf_ctrl(BIO *bio, int cmd, long num, void *ptr)
+{
+ struct Curl_cfilter *cf = BIO_get_data(bio);
+ long ret = 1;
+
+ (void)cf;
+ (void)ptr;
+ switch(cmd) {
+ case BIO_CTRL_GET_CLOSE:
+ ret = (long)BIO_get_shutdown(bio);
+ break;
+ case BIO_CTRL_SET_CLOSE:
+ BIO_set_shutdown(bio, (int)num);
+ break;
+ case BIO_CTRL_FLUSH:
+ /* we do no delayed writes, but if we ever would, this
+ * needs to trigger it. */
+ ret = 1;
+ break;
+ case BIO_CTRL_DUP:
+ ret = 1;
+ break;
+#ifdef BIO_CTRL_EOF
+ case BIO_CTRL_EOF:
+ /* EOF has been reached on input? */
+ return (!cf->next || !cf->next->connected);
+#endif
+ default:
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+static int bio_cf_out_write(BIO *bio, const char *buf, int blen)
+{
+ struct Curl_cfilter *cf = BIO_get_data(bio);
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct Curl_easy *data = connssl->call_data;
+ ssize_t nwritten;
+ CURLcode result = CURLE_SEND_ERROR;
+
+ DEBUGASSERT(data);
+ nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
+ /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_out_write(len=%d) -> %d, err=%d"),
+ blen, (int)nwritten, result)); */
+ BIO_clear_retry_flags(bio);
+ connssl->backend->io_result = result;
+ if(nwritten < 0) {
+ if(CURLE_AGAIN == result)
+ BIO_set_retry_write(bio);
+ }
+ return (int)nwritten;
+}
+
+static int bio_cf_in_read(BIO *bio, char *buf, int blen)
+{
+ struct Curl_cfilter *cf = BIO_get_data(bio);
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct Curl_easy *data = connssl->call_data;
+ ssize_t nread;
+ CURLcode result = CURLE_RECV_ERROR;
+
+ DEBUGASSERT(data);
+ /* OpenSSL catches this case, so should we. */
+ if(!buf)
+ return 0;
+
+ nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
+ /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_in_read(len=%d) -> %d, err=%d"),
+ blen, (int)nread, result)); */
+ BIO_clear_retry_flags(bio);
+ connssl->backend->io_result = result;
+ if(nread < 0) {
+ if(CURLE_AGAIN == result)
+ BIO_set_retry_read(bio);
+ }
+ return (int)nread;
+}
+
+static BIO_METHOD *bio_cf_method = NULL;
+
+#if USE_PRE_1_1_API
+
+static BIO_METHOD bio_cf_meth_1_0 = {
+ BIO_TYPE_MEM,
+ "OpenSSL CF BIO",
+ bio_cf_out_write,
+ bio_cf_in_read,
+ NULL, /* puts is never called */
+ NULL, /* gets is never called */
+ bio_cf_ctrl,
+ bio_cf_create,
+ bio_cf_destroy,
+ NULL
+};
+
+static void bio_cf_init_methods(void)
+{
+ bio_cf_method = &bio_cf_meth_1_0;
+}
+
+#define bio_cf_free_methods() Curl_nop_stmt
+
+#else
+
+static void bio_cf_init_methods(void)
+{
+ bio_cf_method = BIO_meth_new(BIO_TYPE_MEM, "OpenSSL CF BIO");
+ BIO_meth_set_write(bio_cf_method, &bio_cf_out_write);
+ BIO_meth_set_read(bio_cf_method, &bio_cf_in_read);
+ BIO_meth_set_ctrl(bio_cf_method, &bio_cf_ctrl);
+ BIO_meth_set_create(bio_cf_method, &bio_cf_create);
+ BIO_meth_set_destroy(bio_cf_method, &bio_cf_destroy);
+}
+
+static void bio_cf_free_methods(void)
+{
+ BIO_meth_free(bio_cf_method);
+}
+
+#endif
+
+
+static bool ossl_attach_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+
+/*
+ * Number of bytes to read from the random number seed file. This must be
+ * a finite value (because some entropy "files" like /dev/urandom have
+ * an infinite length), but must be large enough to provide enough
+ * entropy to properly seed OpenSSL's PRNG.
+ */
+#define RAND_LOAD_LENGTH 1024
+
+#ifdef HAVE_KEYLOG_CALLBACK
+static void ossl_keylog_callback(const SSL *ssl, const char *line)
+{
+ (void)ssl;
+
+ Curl_tls_keylog_write_line(line);
+}
+#else
+/*
+ * ossl_log_tls12_secret is called by libcurl to make the CLIENT_RANDOMs if the
+ * OpenSSL being used doesn't have native support for doing that.
+ */
+static void
+ossl_log_tls12_secret(const SSL *ssl, bool *keylog_done)
+{
+ const SSL_SESSION *session = SSL_get_session(ssl);
+ unsigned char client_random[SSL3_RANDOM_SIZE];
+ unsigned char master_key[SSL_MAX_MASTER_KEY_LENGTH];
+ int master_key_length = 0;
+
+ if(!session || *keylog_done)
+ return;
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+ !(defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+ /* ssl->s3 is not checked in openssl 1.1.0-pre6, but let's assume that
+ * we have a valid SSL context if we have a non-NULL session. */
+ SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE);
+ master_key_length = (int)
+ SSL_SESSION_get_master_key(session, master_key, SSL_MAX_MASTER_KEY_LENGTH);
+#else
+ if(ssl->s3 && session->master_key_length > 0) {
+ master_key_length = session->master_key_length;
+ memcpy(master_key, session->master_key, session->master_key_length);
+ memcpy(client_random, ssl->s3->client_random, SSL3_RANDOM_SIZE);
+ }
+#endif
+
+ /* The handshake has not progressed sufficiently yet, or this is a TLS 1.3
+ * session (when curl was built with older OpenSSL headers and running with
+ * newer OpenSSL runtime libraries). */
+ if(master_key_length <= 0)
+ return;
+
+ *keylog_done = true;
+ Curl_tls_keylog_write("CLIENT_RANDOM", client_random,
+ master_key, master_key_length);
+}
+#endif /* !HAVE_KEYLOG_CALLBACK */
+
+static const char *SSL_ERROR_to_str(int err)
+{
+ switch(err) {
+ case SSL_ERROR_NONE:
+ return "SSL_ERROR_NONE";
+ case SSL_ERROR_SSL:
+ return "SSL_ERROR_SSL";
+ case SSL_ERROR_WANT_READ:
+ return "SSL_ERROR_WANT_READ";
+ case SSL_ERROR_WANT_WRITE:
+ return "SSL_ERROR_WANT_WRITE";
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ return "SSL_ERROR_WANT_X509_LOOKUP";
+ case SSL_ERROR_SYSCALL:
+ return "SSL_ERROR_SYSCALL";
+ case SSL_ERROR_ZERO_RETURN:
+ return "SSL_ERROR_ZERO_RETURN";
+ case SSL_ERROR_WANT_CONNECT:
+ return "SSL_ERROR_WANT_CONNECT";
+ case SSL_ERROR_WANT_ACCEPT:
+ return "SSL_ERROR_WANT_ACCEPT";
+#if defined(SSL_ERROR_WANT_ASYNC)
+ case SSL_ERROR_WANT_ASYNC:
+ return "SSL_ERROR_WANT_ASYNC";
+#endif
+#if defined(SSL_ERROR_WANT_ASYNC_JOB)
+ case SSL_ERROR_WANT_ASYNC_JOB:
+ return "SSL_ERROR_WANT_ASYNC_JOB";
+#endif
+#if defined(SSL_ERROR_WANT_EARLY)
+ case SSL_ERROR_WANT_EARLY:
+ return "SSL_ERROR_WANT_EARLY";
+#endif
+ default:
+ return "SSL_ERROR unknown";
+ }
+}
+
+static size_t ossl_version(char *buffer, size_t size);
+
+/* Return error string for last OpenSSL error
+ */
+static char *ossl_strerror(unsigned long error, char *buf, size_t size)
+{
+ size_t len;
+ DEBUGASSERT(size);
+ *buf = '\0';
+
+ len = ossl_version(buf, size);
+ DEBUGASSERT(len < (size - 2));
+ if(len < (size - 2)) {
+ buf += len;
+ size -= (len + 2);
+ *buf++ = ':';
+ *buf++ = ' ';
+ *buf = '\0';
+ }
+
+#ifdef OPENSSL_IS_BORINGSSL
+ ERR_error_string_n((uint32_t)error, buf, size);
+#else
+ ERR_error_string_n(error, buf, size);
+#endif
+
+ if(!*buf) {
+ strncpy(buf, (error ? "Unknown error" : "No error"), size);
+ buf[size - 1] = '\0';
+ }
+
+ return buf;
+}
+
+/* Return an extra data index for the transfer data.
+ * This index can be used with SSL_get_ex_data() and SSL_set_ex_data().
+ */
+static int ossl_get_ssl_data_index(void)
+{
+ static int ssl_ex_data_data_index = -1;
+ if(ssl_ex_data_data_index < 0) {
+ ssl_ex_data_data_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+ }
+ return ssl_ex_data_data_index;
+}
+
+/* Return an extra data index for the associated Curl_cfilter instance.
+ * This index can be used with SSL_get_ex_data() and SSL_set_ex_data().
+ */
+static int ossl_get_ssl_cf_index(void)
+{
+ static int ssl_ex_data_cf_index = -1;
+ if(ssl_ex_data_cf_index < 0) {
+ ssl_ex_data_cf_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+ }
+ return ssl_ex_data_cf_index;
+}
+
+/* Return an extra data index for the sockindex.
+ * This index can be used with SSL_get_ex_data() and SSL_set_ex_data().
+ */
+static int ossl_get_ssl_sockindex_index(void)
+{
+ static int sockindex_index = -1;
+ if(sockindex_index < 0) {
+ sockindex_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+ }
+ return sockindex_index;
+}
+
+/* Return an extra data index for proxy boolean.
+ * This index can be used with SSL_get_ex_data() and SSL_set_ex_data().
+ */
+static int ossl_get_proxy_index(void)
+{
+ static int proxy_index = -1;
+ if(proxy_index < 0) {
+ proxy_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+ }
+ return proxy_index;
+}
+
+static int passwd_callback(char *buf, int num, int encrypting,
+ void *global_passwd)
+{
+ DEBUGASSERT(0 == encrypting);
+
+ if(!encrypting) {
+ int klen = curlx_uztosi(strlen((char *)global_passwd));
+ if(num > klen) {
+ memcpy(buf, global_passwd, klen + 1);
+ return klen;
+ }
+ }
+ return 0;
+}
+
+/*
+ * rand_enough() returns TRUE if we have seeded the random engine properly.
+ */
+static bool rand_enough(void)
+{
+ return (0 != RAND_status()) ? TRUE : FALSE;
+}
+
+static CURLcode ossl_seed(struct Curl_easy *data)
+{
+ /* This might get called before it has been added to a multi handle */
+ if(data->multi && data->multi->ssl_seeded)
+ return CURLE_OK;
+
+ if(rand_enough()) {
+ /* OpenSSL 1.1.0+ should return here */
+ if(data->multi)
+ data->multi->ssl_seeded = TRUE;
+ return CURLE_OK;
+ }
+#ifdef HAVE_RANDOM_INIT_BY_DEFAULT
+ /* with OpenSSL 1.1.0+, a failed RAND_status is a showstopper */
+ failf(data, "Insufficient randomness");
+ return CURLE_SSL_CONNECT_ERROR;
+#else
+
+#ifdef RANDOM_FILE
+ RAND_load_file(RANDOM_FILE, RAND_LOAD_LENGTH);
+ if(rand_enough())
+ return CURLE_OK;
+#endif
+
+#if defined(HAVE_RAND_EGD) && defined(EGD_SOCKET)
+ /* available in OpenSSL 0.9.5 and later */
+ /* EGD_SOCKET is set at configure time or not at all */
+ {
+ /* If there's an option and a define, the option overrides the
+ define */
+ int ret = RAND_egd(EGD_SOCKET);
+ if(-1 != ret) {
+ if(rand_enough())
+ return CURLE_OK;
+ }
+ }
+#endif
+
+ /* fallback to a custom seeding of the PRNG using a hash based on a current
+ time */
+ do {
+ unsigned char randb[64];
+ size_t len = sizeof(randb);
+ size_t i, i_max;
+ for(i = 0, i_max = len / sizeof(struct curltime); i < i_max; ++i) {
+ struct curltime tv = Curl_now();
+ Curl_wait_ms(1);
+ tv.tv_sec *= i + 1;
+ tv.tv_usec *= (unsigned int)i + 2;
+ tv.tv_sec ^= ((Curl_now().tv_sec + Curl_now().tv_usec) *
+ (i + 3)) << 8;
+ tv.tv_usec ^= (unsigned int) ((Curl_now().tv_sec +
+ Curl_now().tv_usec) *
+ (i + 4)) << 16;
+ memcpy(&randb[i * sizeof(struct curltime)], &tv,
+ sizeof(struct curltime));
+ }
+ RAND_add(randb, (int)len, (double)len/2);
+ } while(!rand_enough());
+
+ {
+ /* generates a default path for the random seed file */
+ char fname[256];
+ fname[0] = 0; /* blank it first */
+ RAND_file_name(fname, sizeof(fname));
+ if(fname[0]) {
+ /* we got a file name to try */
+ RAND_load_file(fname, RAND_LOAD_LENGTH);
+ if(rand_enough())
+ return CURLE_OK;
+ }
+ }
+
+ infof(data, "libcurl is now using a weak random seed");
+ return (rand_enough() ? CURLE_OK :
+ CURLE_SSL_CONNECT_ERROR /* confusing error code */);
+#endif
+}
+
+#ifndef SSL_FILETYPE_ENGINE
+#define SSL_FILETYPE_ENGINE 42
+#endif
+#ifndef SSL_FILETYPE_PKCS12
+#define SSL_FILETYPE_PKCS12 43
+#endif
+static int do_file_type(const char *type)
+{
+ if(!type || !type[0])
+ return SSL_FILETYPE_PEM;
+ if(strcasecompare(type, "PEM"))
+ return SSL_FILETYPE_PEM;
+ if(strcasecompare(type, "DER"))
+ return SSL_FILETYPE_ASN1;
+ if(strcasecompare(type, "ENG"))
+ return SSL_FILETYPE_ENGINE;
+ if(strcasecompare(type, "P12"))
+ return SSL_FILETYPE_PKCS12;
+ return -1;
+}
+
+#ifdef USE_OPENSSL_ENGINE
+/*
+ * Supply default password to the engine user interface conversation.
+ * The password is passed by OpenSSL engine from ENGINE_load_private_key()
+ * last argument to the ui and can be obtained by UI_get0_user_data(ui) here.
+ */
+static int ssl_ui_reader(UI *ui, UI_STRING *uis)
+{
+ const char *password;
+ switch(UI_get_string_type(uis)) {
+ case UIT_PROMPT:
+ case UIT_VERIFY:
+ password = (const char *)UI_get0_user_data(ui);
+ if(password && (UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD)) {
+ UI_set_result(ui, uis, password);
+ return 1;
+ }
+ default:
+ break;
+ }
+ return (UI_method_get_reader(UI_OpenSSL()))(ui, uis);
+}
+
+/*
+ * Suppress interactive request for a default password if available.
+ */
+static int ssl_ui_writer(UI *ui, UI_STRING *uis)
+{
+ switch(UI_get_string_type(uis)) {
+ case UIT_PROMPT:
+ case UIT_VERIFY:
+ if(UI_get0_user_data(ui) &&
+ (UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD)) {
+ return 1;
+ }
+ default:
+ break;
+ }
+ return (UI_method_get_writer(UI_OpenSSL()))(ui, uis);
+}
+
+/*
+ * Check if a given string is a PKCS#11 URI
+ */
+static bool is_pkcs11_uri(const char *string)
+{
+ return (string && strncasecompare(string, "pkcs11:", 7));
+}
+
+#endif
+
+static CURLcode ossl_set_engine(struct Curl_easy *data, const char *engine);
+
+static int
+SSL_CTX_use_certificate_blob(SSL_CTX *ctx, const struct curl_blob *blob,
+ int type, const char *key_passwd)
+{
+ int ret = 0;
+ X509 *x = NULL;
+ /* the typecast of blob->len is fine since it is guaranteed to never be
+ larger than CURL_MAX_INPUT_LENGTH */
+ BIO *in = BIO_new_mem_buf(blob->data, (int)(blob->len));
+ if(!in)
+ return CURLE_OUT_OF_MEMORY;
+
+ if(type == SSL_FILETYPE_ASN1) {
+ /* j = ERR_R_ASN1_LIB; */
+ x = d2i_X509_bio(in, NULL);
+ }
+ else if(type == SSL_FILETYPE_PEM) {
+ /* ERR_R_PEM_LIB; */
+ x = PEM_read_bio_X509(in, NULL,
+ passwd_callback, (void *)key_passwd);
+ }
+ else {
+ ret = 0;
+ goto end;
+ }
+
+ if(!x) {
+ ret = 0;
+ goto end;
+ }
+
+ ret = SSL_CTX_use_certificate(ctx, x);
+ end:
+ X509_free(x);
+ BIO_free(in);
+ return ret;
+}
+
+static int
+SSL_CTX_use_PrivateKey_blob(SSL_CTX *ctx, const struct curl_blob *blob,
+ int type, const char *key_passwd)
+{
+ int ret = 0;
+ EVP_PKEY *pkey = NULL;
+ BIO *in = BIO_new_mem_buf(blob->data, (int)(blob->len));
+ if(!in)
+ return CURLE_OUT_OF_MEMORY;
+
+ if(type == SSL_FILETYPE_PEM)
+ pkey = PEM_read_bio_PrivateKey(in, NULL, passwd_callback,
+ (void *)key_passwd);
+ else if(type == SSL_FILETYPE_ASN1)
+ pkey = d2i_PrivateKey_bio(in, NULL);
+ else {
+ ret = 0;
+ goto end;
+ }
+ if(!pkey) {
+ ret = 0;
+ goto end;
+ }
+ ret = SSL_CTX_use_PrivateKey(ctx, pkey);
+ EVP_PKEY_free(pkey);
+ end:
+ BIO_free(in);
+ return ret;
+}
+
+static int
+SSL_CTX_use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob,
+ const char *key_passwd)
+{
+/* SSL_CTX_add1_chain_cert introduced in OpenSSL 1.0.2 */
+#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) && /* OpenSSL 1.0.2 or later */ \
+ !(defined(LIBRESSL_VERSION_NUMBER) && \
+ (LIBRESSL_VERSION_NUMBER < 0x2090100fL)) /* LibreSSL 2.9.1 or later */
+ int ret = 0;
+ X509 *x = NULL;
+ void *passwd_callback_userdata = (void *)key_passwd;
+ BIO *in = BIO_new_mem_buf(blob->data, (int)(blob->len));
+ if(!in)
+ return CURLE_OUT_OF_MEMORY;
+
+ ERR_clear_error();
+
+ x = PEM_read_bio_X509_AUX(in, NULL,
+ passwd_callback, (void *)key_passwd);
+
+ if(!x) {
+ ret = 0;
+ goto end;
+ }
+
+ ret = SSL_CTX_use_certificate(ctx, x);
+
+ if(ERR_peek_error() != 0)
+ ret = 0;
+
+ if(ret) {
+ X509 *ca;
+ unsigned long err;
+
+ if(!SSL_CTX_clear_chain_certs(ctx)) {
+ ret = 0;
+ goto end;
+ }
+
+ while((ca = PEM_read_bio_X509(in, NULL, passwd_callback,
+ passwd_callback_userdata))
+ != NULL) {
+
+ if(!SSL_CTX_add0_chain_cert(ctx, ca)) {
+ X509_free(ca);
+ ret = 0;
+ goto end;
+ }
+ }
+
+ err = ERR_peek_last_error();
+ if((ERR_GET_LIB(err) == ERR_LIB_PEM) &&
+ (ERR_GET_REASON(err) == PEM_R_NO_START_LINE))
+ ERR_clear_error();
+ else
+ ret = 0;
+ }
+
+ end:
+ X509_free(x);
+ BIO_free(in);
+ return ret;
+#else
+ (void)ctx; /* unused */
+ (void)blob; /* unused */
+ (void)key_passwd; /* unused */
+ return 0;
+#endif
+}
+
+static
+int cert_stuff(struct Curl_easy *data,
+ SSL_CTX* ctx,
+ char *cert_file,
+ const struct curl_blob *cert_blob,
+ const char *cert_type,
+ char *key_file,
+ const struct curl_blob *key_blob,
+ const char *key_type,
+ char *key_passwd)
+{
+ char error_buffer[256];
+ bool check_privkey = TRUE;
+
+ int file_type = do_file_type(cert_type);
+
+ if(cert_file || cert_blob || (file_type == SSL_FILETYPE_ENGINE)) {
+ SSL *ssl;
+ X509 *x509;
+ int cert_done = 0;
+ int cert_use_result;
+
+ if(key_passwd) {
+ /* set the password in the callback userdata */
+ SSL_CTX_set_default_passwd_cb_userdata(ctx, key_passwd);
+ /* Set passwd callback: */
+ SSL_CTX_set_default_passwd_cb(ctx, passwd_callback);
+ }
+
+
+ switch(file_type) {
+ case SSL_FILETYPE_PEM:
+ /* SSL_CTX_use_certificate_chain_file() only works on PEM files */
+ cert_use_result = cert_blob ?
+ SSL_CTX_use_certificate_chain_blob(ctx, cert_blob, key_passwd) :
+ SSL_CTX_use_certificate_chain_file(ctx, cert_file);
+ if(cert_use_result != 1) {
+ failf(data,
+ "could not load PEM client certificate from %s, " OSSL_PACKAGE
+ " error %s, "
+ "(no key found, wrong pass phrase, or wrong file format?)",
+ (cert_blob ? "CURLOPT_SSLCERT_BLOB" : cert_file),
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)) );
+ return 0;
+ }
+ break;
+
+ case SSL_FILETYPE_ASN1:
+ /* SSL_CTX_use_certificate_file() works with either PEM or ASN1, but
+ we use the case above for PEM so this can only be performed with
+ ASN1 files. */
+
+ cert_use_result = cert_blob ?
+ SSL_CTX_use_certificate_blob(ctx, cert_blob,
+ file_type, key_passwd) :
+ SSL_CTX_use_certificate_file(ctx, cert_file, file_type);
+ if(cert_use_result != 1) {
+ failf(data,
+ "could not load ASN1 client certificate from %s, " OSSL_PACKAGE
+ " error %s, "
+ "(no key found, wrong pass phrase, or wrong file format?)",
+ (cert_blob ? "CURLOPT_SSLCERT_BLOB" : cert_file),
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)) );
+ return 0;
+ }
+ break;
+ case SSL_FILETYPE_ENGINE:
+#if defined(USE_OPENSSL_ENGINE) && defined(ENGINE_CTRL_GET_CMD_FROM_NAME)
+ {
+ /* Implicitly use pkcs11 engine if none was provided and the
+ * cert_file is a PKCS#11 URI */
+ if(!data->state.engine) {
+ if(is_pkcs11_uri(cert_file)) {
+ if(ossl_set_engine(data, "pkcs11") != CURLE_OK) {
+ return 0;
+ }
+ }
+ }
+
+ if(data->state.engine) {
+ const char *cmd_name = "LOAD_CERT_CTRL";
+ struct {
+ const char *cert_id;
+ X509 *cert;
+ } params;
+
+ params.cert_id = cert_file;
+ params.cert = NULL;
+
+ /* Does the engine supports LOAD_CERT_CTRL ? */
+ if(!ENGINE_ctrl(data->state.engine, ENGINE_CTRL_GET_CMD_FROM_NAME,
+ 0, (void *)cmd_name, NULL)) {
+ failf(data, "ssl engine does not support loading certificates");
+ return 0;
+ }
+
+ /* Load the certificate from the engine */
+ if(!ENGINE_ctrl_cmd(data->state.engine, cmd_name,
+ 0, ¶ms, NULL, 1)) {
+ failf(data, "ssl engine cannot load client cert with id"
+ " '%s' [%s]", cert_file,
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return 0;
+ }
+
+ if(!params.cert) {
+ failf(data, "ssl engine didn't initialized the certificate "
+ "properly.");
+ return 0;
+ }
+
+ if(SSL_CTX_use_certificate(ctx, params.cert) != 1) {
+ failf(data, "unable to set client certificate [%s]",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return 0;
+ }
+ X509_free(params.cert); /* we don't need the handle any more... */
+ }
+ else {
+ failf(data, "crypto engine not set, can't load certificate");
+ return 0;
+ }
+ }
+ break;
+#else
+ failf(data, "file type ENG for certificate not implemented");
+ return 0;
+#endif
+
+ case SSL_FILETYPE_PKCS12:
+ {
+ BIO *cert_bio = NULL;
+ PKCS12 *p12 = NULL;
+ EVP_PKEY *pri;
+ STACK_OF(X509) *ca = NULL;
+ if(cert_blob) {
+ cert_bio = BIO_new_mem_buf(cert_blob->data, (int)(cert_blob->len));
+ if(!cert_bio) {
+ failf(data,
+ "BIO_new_mem_buf NULL, " OSSL_PACKAGE
+ " error %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)) );
+ return 0;
+ }
+ }
+ else {
+ cert_bio = BIO_new(BIO_s_file());
+ if(!cert_bio) {
+ failf(data,
+ "BIO_new return NULL, " OSSL_PACKAGE
+ " error %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)) );
+ return 0;
+ }
+
+ if(BIO_read_filename(cert_bio, cert_file) <= 0) {
+ failf(data, "could not open PKCS12 file '%s'", cert_file);
+ BIO_free(cert_bio);
+ return 0;
+ }
+ }
+
+ p12 = d2i_PKCS12_bio(cert_bio, NULL);
+ BIO_free(cert_bio);
+
+ if(!p12) {
+ failf(data, "error reading PKCS12 file '%s'",
+ cert_blob ? "(memory blob)" : cert_file);
+ return 0;
+ }
+
+ PKCS12_PBE_add();
+
+ if(!PKCS12_parse(p12, key_passwd, &pri, &x509,
+ &ca)) {
+ failf(data,
+ "could not parse PKCS12 file, check password, " OSSL_PACKAGE
+ " error %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)) );
+ PKCS12_free(p12);
+ return 0;
+ }
+
+ PKCS12_free(p12);
+
+ if(SSL_CTX_use_certificate(ctx, x509) != 1) {
+ failf(data,
+ "could not load PKCS12 client certificate, " OSSL_PACKAGE
+ " error %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)) );
+ goto fail;
+ }
+
+ if(SSL_CTX_use_PrivateKey(ctx, pri) != 1) {
+ failf(data, "unable to use private key from PKCS12 file '%s'",
+ cert_file);
+ goto fail;
+ }
+
+ if(!SSL_CTX_check_private_key (ctx)) {
+ failf(data, "private key from PKCS12 file '%s' "
+ "does not match certificate in same file", cert_file);
+ goto fail;
+ }
+ /* Set Certificate Verification chain */
+ if(ca) {
+ while(sk_X509_num(ca)) {
+ /*
+ * Note that sk_X509_pop() is used below to make sure the cert is
+ * removed from the stack properly before getting passed to
+ * SSL_CTX_add_extra_chain_cert(), which takes ownership. Previously
+ * we used sk_X509_value() instead, but then we'd clean it in the
+ * subsequent sk_X509_pop_free() call.
+ */
+ X509 *x = sk_X509_pop(ca);
+ if(!SSL_CTX_add_client_CA(ctx, x)) {
+ X509_free(x);
+ failf(data, "cannot add certificate to client CA list");
+ goto fail;
+ }
+ if(!SSL_CTX_add_extra_chain_cert(ctx, x)) {
+ X509_free(x);
+ failf(data, "cannot add certificate to certificate chain");
+ goto fail;
+ }
+ }
+ }
+
+ cert_done = 1;
+ fail:
+ EVP_PKEY_free(pri);
+ X509_free(x509);
+ sk_X509_pop_free(ca, X509_free);
+ if(!cert_done)
+ return 0; /* failure! */
+ break;
+ }
+ default:
+ failf(data, "not supported file type '%s' for certificate", cert_type);
+ return 0;
+ }
+
+ if((!key_file) && (!key_blob)) {
+ key_file = cert_file;
+ key_blob = cert_blob;
+ }
+ else
+ file_type = do_file_type(key_type);
+
+ switch(file_type) {
+ case SSL_FILETYPE_PEM:
+ if(cert_done)
+ break;
+ /* FALLTHROUGH */
+ case SSL_FILETYPE_ASN1:
+ cert_use_result = key_blob ?
+ SSL_CTX_use_PrivateKey_blob(ctx, key_blob, file_type, key_passwd) :
+ SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type);
+ if(cert_use_result != 1) {
+ failf(data, "unable to set private key file: '%s' type %s",
+ key_file?key_file:"(memory blob)", key_type?key_type:"PEM");
+ return 0;
+ }
+ break;
+ case SSL_FILETYPE_ENGINE:
+#ifdef USE_OPENSSL_ENGINE
+ { /* XXXX still needs some work */
+ EVP_PKEY *priv_key = NULL;
+
+ /* Implicitly use pkcs11 engine if none was provided and the
+ * key_file is a PKCS#11 URI */
+ if(!data->state.engine) {
+ if(is_pkcs11_uri(key_file)) {
+ if(ossl_set_engine(data, "pkcs11") != CURLE_OK) {
+ return 0;
+ }
+ }
+ }
+
+ if(data->state.engine) {
+ UI_METHOD *ui_method =
+ UI_create_method((char *)"curl user interface");
+ if(!ui_method) {
+ failf(data, "unable do create " OSSL_PACKAGE
+ " user-interface method");
+ return 0;
+ }
+ UI_method_set_opener(ui_method, UI_method_get_opener(UI_OpenSSL()));
+ UI_method_set_closer(ui_method, UI_method_get_closer(UI_OpenSSL()));
+ UI_method_set_reader(ui_method, ssl_ui_reader);
+ UI_method_set_writer(ui_method, ssl_ui_writer);
+ /* the typecast below was added to please mingw32 */
+ priv_key = (EVP_PKEY *)
+ ENGINE_load_private_key(data->state.engine, key_file,
+ ui_method,
+ key_passwd);
+ UI_destroy_method(ui_method);
+ if(!priv_key) {
+ failf(data, "failed to load private key from crypto engine");
+ return 0;
+ }
+ if(SSL_CTX_use_PrivateKey(ctx, priv_key) != 1) {
+ failf(data, "unable to set private key");
+ EVP_PKEY_free(priv_key);
+ return 0;
+ }
+ EVP_PKEY_free(priv_key); /* we don't need the handle any more... */
+ }
+ else {
+ failf(data, "crypto engine not set, can't load private key");
+ return 0;
+ }
+ }
+ break;
+#else
+ failf(data, "file type ENG for private key not supported");
+ return 0;
+#endif
+ case SSL_FILETYPE_PKCS12:
+ if(!cert_done) {
+ failf(data, "file type P12 for private key not supported");
+ return 0;
+ }
+ break;
+ default:
+ failf(data, "not supported file type for private key");
+ return 0;
+ }
+
+ ssl = SSL_new(ctx);
+ if(!ssl) {
+ failf(data, "unable to create an SSL structure");
+ return 0;
+ }
+
+ x509 = SSL_get_certificate(ssl);
+
+ /* This version was provided by Evan Jordan and is supposed to not
+ leak memory as the previous version: */
+ if(x509) {
+ EVP_PKEY *pktmp = X509_get_pubkey(x509);
+ EVP_PKEY_copy_parameters(pktmp, SSL_get_privatekey(ssl));
+ EVP_PKEY_free(pktmp);
+ }
+
+#if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_IS_BORINGSSL) && \
+ !defined(OPENSSL_NO_DEPRECATED_3_0)
+ {
+ /* If RSA is used, don't check the private key if its flags indicate
+ * it doesn't support it. */
+ EVP_PKEY *priv_key = SSL_get_privatekey(ssl);
+ int pktype;
+#ifdef HAVE_OPAQUE_EVP_PKEY
+ pktype = EVP_PKEY_id(priv_key);
+#else
+ pktype = priv_key->type;
+#endif
+ if(pktype == EVP_PKEY_RSA) {
+ RSA *rsa = EVP_PKEY_get1_RSA(priv_key);
+ if(RSA_flags(rsa) & RSA_METHOD_FLAG_NO_CHECK)
+ check_privkey = FALSE;
+ RSA_free(rsa); /* Decrement reference count */
+ }
+ }
+#endif
+
+ SSL_free(ssl);
+
+ /* If we are using DSA, we can copy the parameters from
+ * the private key */
+
+ if(check_privkey == TRUE) {
+ /* Now we know that a key and cert have been set against
+ * the SSL context */
+ if(!SSL_CTX_check_private_key(ctx)) {
+ failf(data, "Private key does not match the certificate public key");
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+CURLcode Curl_ossl_set_client_cert(struct Curl_easy *data, SSL_CTX *ctx,
+ char *cert_file,
+ const struct curl_blob *cert_blob,
+ const char *cert_type, char *key_file,
+ const struct curl_blob *key_blob,
+ const char *key_type, char *key_passwd)
+{
+ int rv = cert_stuff(data, ctx, cert_file, cert_blob, cert_type, key_file,
+ key_blob, key_type, key_passwd);
+ if(rv != 1) {
+ return CURLE_SSL_CERTPROBLEM;
+ }
+
+ return CURLE_OK;
+}
+
+/* returns non-zero on failure */
+static int x509_name_oneline(X509_NAME *a, char *buf, size_t size)
+{
+ BIO *bio_out = BIO_new(BIO_s_mem());
+ BUF_MEM *biomem;
+ int rc;
+
+ if(!bio_out)
+ return 1; /* alloc failed! */
+
+ rc = X509_NAME_print_ex(bio_out, a, 0, XN_FLAG_SEP_SPLUS_SPC);
+ BIO_get_mem_ptr(bio_out, &biomem);
+
+ if((size_t)biomem->length < size)
+ size = biomem->length;
+ else
+ size--; /* don't overwrite the buffer end */
+
+ memcpy(buf, biomem->data, size);
+ buf[size] = 0;
+
+ BIO_free(bio_out);
+
+ return !rc;
+}
+
+/**
+ * Global SSL init
+ *
+ * @retval 0 error initializing SSL
+ * @retval 1 SSL initialized successfully
+ */
+static int ossl_init(void)
+{
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \
+ !defined(LIBRESSL_VERSION_NUMBER)
+ const uint64_t flags =
+#ifdef OPENSSL_INIT_ENGINE_ALL_BUILTIN
+ /* not present in BoringSSL */
+ OPENSSL_INIT_ENGINE_ALL_BUILTIN |
+#endif
+#ifdef CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG
+ OPENSSL_INIT_NO_LOAD_CONFIG |
+#else
+ OPENSSL_INIT_LOAD_CONFIG |
+#endif
+ 0;
+ OPENSSL_init_ssl(flags, NULL);
+#else
+ OPENSSL_load_builtin_modules();
+
+#ifdef USE_OPENSSL_ENGINE
+ ENGINE_load_builtin_engines();
+#endif
+
+/* CONF_MFLAGS_DEFAULT_SECTION was introduced some time between 0.9.8b and
+ 0.9.8e */
+#ifndef CONF_MFLAGS_DEFAULT_SECTION
+#define CONF_MFLAGS_DEFAULT_SECTION 0x0
+#endif
+
+#ifndef CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG
+ CONF_modules_load_file(NULL, NULL,
+ CONF_MFLAGS_DEFAULT_SECTION|
+ CONF_MFLAGS_IGNORE_MISSING_FILE);
+#endif
+
+ /* Let's get nice error messages */
+ SSL_load_error_strings();
+
+ /* Init the global ciphers and digests */
+ if(!SSLeay_add_ssl_algorithms())
+ return 0;
+
+ OpenSSL_add_all_algorithms();
+#endif
+
+ bio_cf_init_methods();
+ Curl_tls_keylog_open();
+
+ /* Initialize the extra data indexes */
+ if(ossl_get_ssl_data_index() < 0 || ossl_get_ssl_cf_index() < 0 ||
+ ossl_get_ssl_sockindex_index() < 0 || ossl_get_proxy_index() < 0)
+ return 0;
+
+ return 1;
+}
+
+/* Global cleanup */
+static void ossl_cleanup(void)
+{
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \
+ !defined(LIBRESSL_VERSION_NUMBER)
+ /* OpenSSL 1.1 deprecates all these cleanup functions and
+ turns them into no-ops in OpenSSL 1.0 compatibility mode */
+#else
+ /* Free ciphers and digests lists */
+ EVP_cleanup();
+
+#ifdef USE_OPENSSL_ENGINE
+ /* Free engine list */
+ ENGINE_cleanup();
+#endif
+
+ /* Free OpenSSL error strings */
+ ERR_free_strings();
+
+ /* Free thread local error state, destroying hash upon zero refcount */
+#ifdef HAVE_ERR_REMOVE_THREAD_STATE
+ ERR_remove_thread_state(NULL);
+#else
+ ERR_remove_state(0);
+#endif
+
+ /* Free all memory allocated by all configuration modules */
+ CONF_modules_free();
+
+#ifdef HAVE_SSL_COMP_FREE_COMPRESSION_METHODS
+ SSL_COMP_free_compression_methods();
+#endif
+#endif
+
+ Curl_tls_keylog_close();
+ bio_cf_free_methods();
+}
+
+/*
+ * This function is used to determine connection status.
+ *
+ * Return codes:
+ * 1 means the connection is still in place
+ * 0 means the connection has been closed
+ * -1 means the connection status is unknown
+ */
+static int ossl_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ /* SSL_peek takes data out of the raw recv buffer without peeking so we use
+ recv MSG_PEEK instead. Bug #795 */
+#ifdef MSG_PEEK
+ char buf;
+ ssize_t nread;
+ nread = recv((RECV_TYPE_ARG1)cf->conn->sock[cf->sockindex],
+ (RECV_TYPE_ARG2)&buf, (RECV_TYPE_ARG3)1,
+ (RECV_TYPE_ARG4)MSG_PEEK);
+ if(nread == 0)
+ return 0; /* connection has been closed */
+ if(nread == 1)
+ return 1; /* connection still in place */
+ else if(nread == -1) {
+ int err = SOCKERRNO;
+ if(err == EINPROGRESS ||
+#if defined(EAGAIN) && (EAGAIN != EWOULDBLOCK)
+ err == EAGAIN ||
+#endif
+ err == EWOULDBLOCK)
+ return 1; /* connection still in place */
+ if(err == ECONNRESET ||
+#ifdef ECONNABORTED
+ err == ECONNABORTED ||
+#endif
+#ifdef ENETDOWN
+ err == ENETDOWN ||
+#endif
+#ifdef ENETRESET
+ err == ENETRESET ||
+#endif
+#ifdef ESHUTDOWN
+ err == ESHUTDOWN ||
+#endif
+#ifdef ETIMEDOUT
+ err == ETIMEDOUT ||
+#endif
+ err == ENOTCONN)
+ return 0; /* connection has been closed */
+ }
+#endif
+ (void)data;
+ return -1; /* connection status unknown */
+}
+
+/* Selects an OpenSSL crypto engine
+ */
+static CURLcode ossl_set_engine(struct Curl_easy *data, const char *engine)
+{
+#ifdef USE_OPENSSL_ENGINE
+ ENGINE *e;
+
+#if OPENSSL_VERSION_NUMBER >= 0x00909000L
+ e = ENGINE_by_id(engine);
+#else
+ /* avoid memory leak */
+ for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)) {
+ const char *e_id = ENGINE_get_id(e);
+ if(!strcmp(engine, e_id))
+ break;
+ }
+#endif
+
+ if(!e) {
+ failf(data, "SSL Engine '%s' not found", engine);
+ return CURLE_SSL_ENGINE_NOTFOUND;
+ }
+
+ if(data->state.engine) {
+ ENGINE_finish(data->state.engine);
+ ENGINE_free(data->state.engine);
+ data->state.engine = NULL;
+ }
+ if(!ENGINE_init(e)) {
+ char buf[256];
+
+ ENGINE_free(e);
+ failf(data, "Failed to initialise SSL Engine '%s': %s",
+ engine, ossl_strerror(ERR_get_error(), buf, sizeof(buf)));
+ return CURLE_SSL_ENGINE_INITFAILED;
+ }
+ data->state.engine = e;
+ return CURLE_OK;
+#else
+ (void)engine;
+ failf(data, "SSL Engine not supported");
+ return CURLE_SSL_ENGINE_NOTFOUND;
+#endif
+}
+
+/* Sets engine as default for all SSL operations
+ */
+static CURLcode ossl_set_engine_default(struct Curl_easy *data)
+{
+#ifdef USE_OPENSSL_ENGINE
+ if(data->state.engine) {
+ if(ENGINE_set_default(data->state.engine, ENGINE_METHOD_ALL) > 0) {
+ infof(data, "set default crypto engine '%s'",
+ ENGINE_get_id(data->state.engine));
+ }
+ else {
+ failf(data, "set default crypto engine '%s' failed",
+ ENGINE_get_id(data->state.engine));
+ return CURLE_SSL_ENGINE_SETFAILED;
+ }
+ }
+#else
+ (void) data;
+#endif
+ return CURLE_OK;
+}
+
+/* Return list of OpenSSL crypto engine names.
+ */
+static struct curl_slist *ossl_engines_list(struct Curl_easy *data)
+{
+ struct curl_slist *list = NULL;
+#ifdef USE_OPENSSL_ENGINE
+ struct curl_slist *beg;
+ ENGINE *e;
+
+ for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)) {
+ beg = curl_slist_append(list, ENGINE_get_id(e));
+ if(!beg) {
+ curl_slist_free_all(list);
+ return NULL;
+ }
+ list = beg;
+ }
+#endif
+ (void) data;
+ return list;
+}
+
+#define set_logger(connssl, data) \
+ connssl->backend->logger = data
+
+static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+
+ DEBUGASSERT(backend);
+
+ if(backend->handle) {
+ set_logger(connssl, data);
+
+ if(cf->next && cf->next->connected) {
+ char buf[32];
+ /* Maybe the server has already sent a close notify alert.
+ Read it to avoid an RST on the TCP connection. */
+ (void)SSL_read(backend->handle, buf, (int)sizeof(buf));
+
+ (void)SSL_shutdown(backend->handle);
+ SSL_set_connect_state(backend->handle);
+ }
+
+ SSL_free(backend->handle);
+ backend->handle = NULL;
+ }
+ if(backend->ctx) {
+ SSL_CTX_free(backend->ctx);
+ backend->ctx = NULL;
+ }
+}
+
+/*
+ * This function is called to shut down the SSL layer but keep the
+ * socket open (CCC - Clear Command Channel)
+ */
+static int ossl_shutdown(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ int retval = 0;
+ struct ssl_connect_data *connssl = cf->ctx;
+ char buf[256]; /* We will use this for the OpenSSL error buffer, so it has
+ to be at least 256 bytes long. */
+ unsigned long sslerror;
+ int nread;
+ int buffsize;
+ int err;
+ bool done = FALSE;
+ struct ssl_backend_data *backend = connssl->backend;
+ int loop = 10;
+
+ DEBUGASSERT(backend);
+
+#ifndef CURL_DISABLE_FTP
+ /* This has only been tested on the proftpd server, and the mod_tls code
+ sends a close notify alert without waiting for a close notify alert in
+ response. Thus we wait for a close notify alert from the server, but
+ we do not send one. Let's hope other servers do the same... */
+
+ if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)
+ (void)SSL_shutdown(backend->handle);
+#endif
+
+ if(backend->handle) {
+ buffsize = (int)sizeof(buf);
+ while(!done && loop--) {
+ int what = SOCKET_READABLE(cf->conn->sock[cf->sockindex],
+ SSL_SHUTDOWN_TIMEOUT);
+ if(what > 0) {
+ ERR_clear_error();
+
+ /* Something to read, let's do it and hope that it is the close
+ notify alert from the server */
+ nread = SSL_read(backend->handle, buf, buffsize);
+ err = SSL_get_error(backend->handle, nread);
+
+ switch(err) {
+ case SSL_ERROR_NONE: /* this is not an error */
+ case SSL_ERROR_ZERO_RETURN: /* no more data */
+ /* This is the expected response. There was no data but only
+ the close notify alert */
+ done = TRUE;
+ break;
+ case SSL_ERROR_WANT_READ:
+ /* there's data pending, re-invoke SSL_read() */
+ infof(data, "SSL_ERROR_WANT_READ");
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ /* SSL wants a write. Really odd. Let's bail out. */
+ infof(data, "SSL_ERROR_WANT_WRITE");
+ done = TRUE;
+ break;
+ default:
+ /* openssl/ssl.h says "look at error stack/return value/errno" */
+ sslerror = ERR_get_error();
+ failf(data, OSSL_PACKAGE " SSL_read on shutdown: %s, errno %d",
+ (sslerror ?
+ ossl_strerror(sslerror, buf, sizeof(buf)) :
+ SSL_ERROR_to_str(err)),
+ SOCKERRNO);
+ done = TRUE;
+ break;
+ }
+ }
+ else if(0 == what) {
+ /* timeout */
+ failf(data, "SSL shutdown timeout");
+ done = TRUE;
+ }
+ else {
+ /* anything that gets here is fatally bad */
+ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+ retval = -1;
+ done = TRUE;
+ }
+ } /* while()-loop for the select() */
+
+ if(data->set.verbose) {
+#ifdef HAVE_SSL_GET_SHUTDOWN
+ switch(SSL_get_shutdown(backend->handle)) {
+ case SSL_SENT_SHUTDOWN:
+ infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN");
+ break;
+ case SSL_RECEIVED_SHUTDOWN:
+ infof(data, "SSL_get_shutdown() returned SSL_RECEIVED_SHUTDOWN");
+ break;
+ case SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN:
+ infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN|"
+ "SSL_RECEIVED__SHUTDOWN");
+ break;
+ }
+#endif
+ }
+
+ SSL_free(backend->handle);
+ backend->handle = NULL;
+ }
+ return retval;
+}
+
+static void ossl_session_free(void *ptr)
+{
+ /* free the ID */
+ SSL_SESSION_free(ptr);
+}
+
+/*
+ * This function is called when the 'data' struct is going away. Close
+ * down everything and free all resources!
+ */
+static void ossl_close_all(struct Curl_easy *data)
+{
+#ifdef USE_OPENSSL_ENGINE
+ if(data->state.engine) {
+ ENGINE_finish(data->state.engine);
+ ENGINE_free(data->state.engine);
+ data->state.engine = NULL;
+ }
+#else
+ (void)data;
+#endif
+#if !defined(HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED) && \
+ defined(HAVE_ERR_REMOVE_THREAD_STATE)
+ /* OpenSSL 1.0.1 and 1.0.2 build an error queue that is stored per-thread
+ so we need to clean it here in case the thread will be killed. All OpenSSL
+ code should extract the error in association with the error so clearing
+ this queue here should be harmless at worst. */
+ ERR_remove_thread_state(NULL);
+#endif
+}
+
+/* ====================================================== */
+
+/*
+ * Match subjectAltName against the host name.
+ */
+static bool subj_alt_hostcheck(struct Curl_easy *data,
+ const char *match_pattern,
+ size_t matchlen,
+ const char *hostname,
+ size_t hostlen,
+ const char *dispname)
+{
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+ (void)dispname;
+ (void)data;
+#endif
+ if(Curl_cert_hostcheck(match_pattern, matchlen, hostname, hostlen)) {
+ infof(data, " subjectAltName: host \"%s\" matched cert's \"%s\"",
+ dispname, match_pattern);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Quote from RFC2818 section 3.1 "Server Identity"
+
+ If a subjectAltName extension of type dNSName is present, that MUST
+ be used as the identity. Otherwise, the (most specific) Common Name
+ field in the Subject field of the certificate MUST be used. Although
+ the use of the Common Name is existing practice, it is deprecated and
+ Certification Authorities are encouraged to use the dNSName instead.
+
+ Matching is performed using the matching rules specified by
+ [RFC2459]. If more than one identity of a given type is present in
+ the certificate (e.g., more than one dNSName name, a match in any one
+ of the set is considered acceptable.) Names may contain the wildcard
+ character * which is considered to match any single domain name
+ component or component fragment. E.g., *.a.com matches foo.a.com but
+ not bar.foo.a.com. f*.com matches foo.com but not bar.com.
+
+ In some cases, the URI is specified as an IP address rather than a
+ hostname. In this case, the iPAddress subjectAltName must be present
+ in the certificate and must exactly match the IP in the URI.
+
+ This function is now used from ngtcp2 (QUIC) as well.
+*/
+CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
+ X509 *server_cert)
+{
+ bool matched = FALSE;
+ int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */
+ size_t addrlen = 0;
+ STACK_OF(GENERAL_NAME) *altnames;
+#ifdef ENABLE_IPV6
+ struct in6_addr addr;
+#else
+ struct in_addr addr;
+#endif
+ CURLcode result = CURLE_OK;
+ bool dNSName = FALSE; /* if a dNSName field exists in the cert */
+ bool iPAddress = FALSE; /* if a iPAddress field exists in the cert */
+ const char *hostname, *dispname;
+ int port;
+ size_t hostlen;
+
+ (void)conn;
+ Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &dispname, &port);
+ hostlen = strlen(hostname);
+
+#ifndef ENABLE_IPV6
+ /* Silence compiler warnings for unused params */
+ (void) conn;
+#endif
+
+#ifdef ENABLE_IPV6
+ if(conn->bits.ipv6_ip &&
+ Curl_inet_pton(AF_INET6, hostname, &addr)) {
+ target = GEN_IPADD;
+ addrlen = sizeof(struct in6_addr);
+ }
+ else
+#endif
+ if(Curl_inet_pton(AF_INET, hostname, &addr)) {
+ target = GEN_IPADD;
+ addrlen = sizeof(struct in_addr);
+ }
+
+ /* get a "list" of alternative names */
+ altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL);
+
+ if(altnames) {
+#ifdef OPENSSL_IS_BORINGSSL
+ size_t numalts;
+ size_t i;
+#else
+ int numalts;
+ int i;
+#endif
+ bool dnsmatched = FALSE;
+ bool ipmatched = FALSE;
+
+ /* get amount of alternatives, RFC2459 claims there MUST be at least
+ one, but we don't depend on it... */
+ numalts = sk_GENERAL_NAME_num(altnames);
+
+ /* loop through all alternatives - until a dnsmatch */
+ for(i = 0; (i < numalts) && !dnsmatched; i++) {
+ /* get a handle to alternative name number i */
+ const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i);
+
+ if(check->type == GEN_DNS)
+ dNSName = TRUE;
+ else if(check->type == GEN_IPADD)
+ iPAddress = TRUE;
+
+ /* only check alternatives of the same type the target is */
+ if(check->type == target) {
+ /* get data and length */
+ const char *altptr = (char *)ASN1_STRING_get0_data(check->d.ia5);
+ size_t altlen = (size_t) ASN1_STRING_length(check->d.ia5);
+
+ switch(target) {
+ case GEN_DNS: /* name/pattern comparison */
+ /* The OpenSSL man page explicitly says: "In general it cannot be
+ assumed that the data returned by ASN1_STRING_data() is null
+ terminated or does not contain embedded nulls." But also that
+ "The actual format of the data will depend on the actual string
+ type itself: for example for an IA5String the data will be ASCII"
+
+ It has been however verified that in 0.9.6 and 0.9.7, IA5String
+ is always null-terminated.
+ */
+ if((altlen == strlen(altptr)) &&
+ /* if this isn't true, there was an embedded zero in the name
+ string and we cannot match it. */
+ subj_alt_hostcheck(data,
+ altptr,
+ altlen, hostname, hostlen, dispname)) {
+ dnsmatched = TRUE;
+ }
+ break;
+
+ case GEN_IPADD: /* IP address comparison */
+ /* compare alternative IP address if the data chunk is the same size
+ our server IP address is */
+ if((altlen == addrlen) && !memcmp(altptr, &addr, altlen)) {
+ ipmatched = TRUE;
+ infof(data,
+ " subjectAltName: host \"%s\" matched cert's IP address!",
+ dispname);
+ }
+ break;
+ }
+ }
+ }
+ GENERAL_NAMES_free(altnames);
+
+ if(dnsmatched || ipmatched)
+ matched = TRUE;
+ }
+
+ if(matched)
+ /* an alternative name matched */
+ ;
+ else if(dNSName || iPAddress) {
+ infof(data, " subjectAltName does not match %s", dispname);
+ failf(data, "SSL: no alternative certificate subject name matches "
+ "target host name '%s'", dispname);
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else {
+ /* we have to look to the last occurrence of a commonName in the
+ distinguished one to get the most significant one. */
+ int i = -1;
+ unsigned char *peer_CN = NULL;
+ int peerlen = 0;
+
+ /* The following is done because of a bug in 0.9.6b */
+ X509_NAME *name = X509_get_subject_name(server_cert);
+ if(name) {
+ int j;
+ while((j = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0)
+ i = j;
+ }
+
+ /* we have the name entry and we will now convert this to a string
+ that we can use for comparison. Doing this we support BMPstring,
+ UTF8, etc. */
+
+ if(i >= 0) {
+ ASN1_STRING *tmp =
+ X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i));
+
+ /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input
+ is already UTF-8 encoded. We check for this case and copy the raw
+ string manually to avoid the problem. This code can be made
+ conditional in the future when OpenSSL has been fixed. */
+ if(tmp) {
+ if(ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) {
+ peerlen = ASN1_STRING_length(tmp);
+ if(peerlen >= 0) {
+ peer_CN = OPENSSL_malloc(peerlen + 1);
+ if(peer_CN) {
+ memcpy(peer_CN, ASN1_STRING_get0_data(tmp), peerlen);
+ peer_CN[peerlen] = '\0';
+ }
+ else
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ }
+ else /* not a UTF8 name */
+ peerlen = ASN1_STRING_to_UTF8(&peer_CN, tmp);
+
+ if(peer_CN && (curlx_uztosi(strlen((char *)peer_CN)) != peerlen)) {
+ /* there was a terminating zero before the end of string, this
+ cannot match and we return failure! */
+ failf(data, "SSL: illegal cert name field");
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+ }
+ }
+
+ if(result)
+ /* error already detected, pass through */
+ ;
+ else if(!peer_CN) {
+ failf(data,
+ "SSL: unable to obtain common name from peer certificate");
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else if(!Curl_cert_hostcheck((const char *)peer_CN,
+ peerlen, hostname, hostlen)) {
+ failf(data, "SSL: certificate subject name '%s' does not match "
+ "target host name '%s'", peer_CN, dispname);
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else {
+ infof(data, " common name: %s (matched)", peer_CN);
+ }
+ if(peer_CN)
+ OPENSSL_free(peer_CN);
+ }
+
+ return result;
+}
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
+ !defined(OPENSSL_NO_OCSP)
+static CURLcode verifystatus(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ int i, ocsp_status;
+ unsigned char *status;
+ const unsigned char *p;
+ CURLcode result = CURLE_OK;
+ OCSP_RESPONSE *rsp = NULL;
+ OCSP_BASICRESP *br = NULL;
+ X509_STORE *st = NULL;
+ STACK_OF(X509) *ch = NULL;
+ struct ssl_backend_data *backend = connssl->backend;
+ X509 *cert;
+ OCSP_CERTID *id = NULL;
+ int cert_status, crl_reason;
+ ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
+ int ret;
+ long len;
+
+ DEBUGASSERT(backend);
+
+ len = SSL_get_tlsext_status_ocsp_resp(backend->handle, &status);
+
+ if(!status) {
+ failf(data, "No OCSP response received");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+ p = status;
+ rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
+ if(!rsp) {
+ failf(data, "Invalid OCSP response");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ ocsp_status = OCSP_response_status(rsp);
+ if(ocsp_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+ failf(data, "Invalid OCSP response status: %s (%d)",
+ OCSP_response_status_str(ocsp_status), ocsp_status);
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ br = OCSP_response_get1_basic(rsp);
+ if(!br) {
+ failf(data, "Invalid OCSP response");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ ch = SSL_get_peer_cert_chain(backend->handle);
+ if(!ch) {
+ failf(data, "Could not get peer certificate chain");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+ st = SSL_CTX_get_cert_store(backend->ctx);
+
+#if ((OPENSSL_VERSION_NUMBER <= 0x1000201fL) /* Fixed after 1.0.2a */ || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER <= 0x2040200fL))
+ /* The authorized responder cert in the OCSP response MUST be signed by the
+ peer cert's issuer (see RFC6960 section 4.2.2.2). If that's a root cert,
+ no problem, but if it's an intermediate cert OpenSSL has a bug where it
+ expects this issuer to be present in the chain embedded in the OCSP
+ response. So we add it if necessary. */
+
+ /* First make sure the peer cert chain includes both a peer and an issuer,
+ and the OCSP response contains a responder cert. */
+ if(sk_X509_num(ch) >= 2 && sk_X509_num(br->certs) >= 1) {
+ X509 *responder = sk_X509_value(br->certs, sk_X509_num(br->certs) - 1);
+
+ /* Find issuer of responder cert and add it to the OCSP response chain */
+ for(i = 0; i < sk_X509_num(ch); i++) {
+ X509 *issuer = sk_X509_value(ch, i);
+ if(X509_check_issued(issuer, responder) == X509_V_OK) {
+ if(!OCSP_basic_add1_cert(br, issuer)) {
+ failf(data, "Could not add issuer cert to OCSP response");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+ }
+ }
+ }
+#endif
+
+ if(OCSP_basic_verify(br, ch, st, 0) <= 0) {
+ failf(data, "OCSP response verification failed");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ /* Compute the certificate's ID */
+ cert = SSL_get1_peer_certificate(backend->handle);
+ if(!cert) {
+ failf(data, "Error getting peer certificate");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ for(i = 0; i < sk_X509_num(ch); i++) {
+ X509 *issuer = sk_X509_value(ch, i);
+ if(X509_check_issued(issuer, cert) == X509_V_OK) {
+ id = OCSP_cert_to_id(EVP_sha1(), cert, issuer);
+ break;
+ }
+ }
+ X509_free(cert);
+
+ if(!id) {
+ failf(data, "Error computing OCSP ID");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ /* Find the single OCSP response corresponding to the certificate ID */
+ ret = OCSP_resp_find_status(br, id, &cert_status, &crl_reason, &rev,
+ &thisupd, &nextupd);
+ OCSP_CERTID_free(id);
+ if(ret != 1) {
+ failf(data, "Could not find certificate ID in OCSP response");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ /* Validate the corresponding single OCSP response */
+ if(!OCSP_check_validity(thisupd, nextupd, 300L, -1L)) {
+ failf(data, "OCSP response has expired");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ infof(data, "SSL certificate status: %s (%d)",
+ OCSP_cert_status_str(cert_status), cert_status);
+
+ switch(cert_status) {
+ case V_OCSP_CERTSTATUS_GOOD:
+ break;
+
+ case V_OCSP_CERTSTATUS_REVOKED:
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ failf(data, "SSL certificate revocation reason: %s (%d)",
+ OCSP_crl_reason_str(crl_reason), crl_reason);
+ goto end;
+
+ case V_OCSP_CERTSTATUS_UNKNOWN:
+ default:
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+end:
+ if(br)
+ OCSP_BASICRESP_free(br);
+ OCSP_RESPONSE_free(rsp);
+
+ return result;
+}
+#endif
+
+#endif /* USE_OPENSSL */
+
+/* The SSL_CTRL_SET_MSG_CALLBACK doesn't exist in ancient OpenSSL versions
+ and thus this cannot be done there. */
+#ifdef SSL_CTRL_SET_MSG_CALLBACK
+
+static const char *ssl_msg_type(int ssl_ver, int msg)
+{
+#ifdef SSL2_VERSION_MAJOR
+ if(ssl_ver == SSL2_VERSION_MAJOR) {
+ switch(msg) {
+ case SSL2_MT_ERROR:
+ return "Error";
+ case SSL2_MT_CLIENT_HELLO:
+ return "Client hello";
+ case SSL2_MT_CLIENT_MASTER_KEY:
+ return "Client key";
+ case SSL2_MT_CLIENT_FINISHED:
+ return "Client finished";
+ case SSL2_MT_SERVER_HELLO:
+ return "Server hello";
+ case SSL2_MT_SERVER_VERIFY:
+ return "Server verify";
+ case SSL2_MT_SERVER_FINISHED:
+ return "Server finished";
+ case SSL2_MT_REQUEST_CERTIFICATE:
+ return "Request CERT";
+ case SSL2_MT_CLIENT_CERTIFICATE:
+ return "Client CERT";
+ }
+ }
+ else
+#endif
+ if(ssl_ver == SSL3_VERSION_MAJOR) {
+ switch(msg) {
+ case SSL3_MT_HELLO_REQUEST:
+ return "Hello request";
+ case SSL3_MT_CLIENT_HELLO:
+ return "Client hello";
+ case SSL3_MT_SERVER_HELLO:
+ return "Server hello";
+#ifdef SSL3_MT_NEWSESSION_TICKET
+ case SSL3_MT_NEWSESSION_TICKET:
+ return "Newsession Ticket";
+#endif
+ case SSL3_MT_CERTIFICATE:
+ return "Certificate";
+ case SSL3_MT_SERVER_KEY_EXCHANGE:
+ return "Server key exchange";
+ case SSL3_MT_CLIENT_KEY_EXCHANGE:
+ return "Client key exchange";
+ case SSL3_MT_CERTIFICATE_REQUEST:
+ return "Request CERT";
+ case SSL3_MT_SERVER_DONE:
+ return "Server finished";
+ case SSL3_MT_CERTIFICATE_VERIFY:
+ return "CERT verify";
+ case SSL3_MT_FINISHED:
+ return "Finished";
+#ifdef SSL3_MT_CERTIFICATE_STATUS
+ case SSL3_MT_CERTIFICATE_STATUS:
+ return "Certificate Status";
+#endif
+#ifdef SSL3_MT_ENCRYPTED_EXTENSIONS
+ case SSL3_MT_ENCRYPTED_EXTENSIONS:
+ return "Encrypted Extensions";
+#endif
+#ifdef SSL3_MT_SUPPLEMENTAL_DATA
+ case SSL3_MT_SUPPLEMENTAL_DATA:
+ return "Supplemental data";
+#endif
+#ifdef SSL3_MT_END_OF_EARLY_DATA
+ case SSL3_MT_END_OF_EARLY_DATA:
+ return "End of early data";
+#endif
+#ifdef SSL3_MT_KEY_UPDATE
+ case SSL3_MT_KEY_UPDATE:
+ return "Key update";
+#endif
+#ifdef SSL3_MT_NEXT_PROTO
+ case SSL3_MT_NEXT_PROTO:
+ return "Next protocol";
+#endif
+#ifdef SSL3_MT_MESSAGE_HASH
+ case SSL3_MT_MESSAGE_HASH:
+ return "Message hash";
+#endif
+ }
+ }
+ return "Unknown";
+}
+
+static const char *tls_rt_type(int type)
+{
+ switch(type) {
+#ifdef SSL3_RT_HEADER
+ case SSL3_RT_HEADER:
+ return "TLS header";
+#endif
+ case SSL3_RT_CHANGE_CIPHER_SPEC:
+ return "TLS change cipher";
+ case SSL3_RT_ALERT:
+ return "TLS alert";
+ case SSL3_RT_HANDSHAKE:
+ return "TLS handshake";
+ case SSL3_RT_APPLICATION_DATA:
+ return "TLS app data";
+ default:
+ return "TLS Unknown";
+ }
+}
+
+/*
+ * Our callback from the SSL/TLS layers.
+ */
+static void ossl_trace(int direction, int ssl_ver, int content_type,
+ const void *buf, size_t len, SSL *ssl,
+ void *userp)
+{
+ char unknown[32];
+ const char *verstr = NULL;
+ struct connectdata *conn = userp;
+ int cf_idx = ossl_get_ssl_cf_index();
+ struct ssl_connect_data *connssl;
+ struct Curl_easy *data = NULL;
+ struct Curl_cfilter *cf;
+
+ DEBUGASSERT(cf_idx >= 0);
+ cf = (struct Curl_cfilter*) SSL_get_ex_data(ssl, cf_idx);
+ DEBUGASSERT(cf);
+ connssl = cf->ctx;
+ DEBUGASSERT(connssl);
+ DEBUGASSERT(connssl->backend);
+ data = connssl->backend->logger;
+
+ if(!conn || !data || !data->set.fdebug ||
+ (direction != 0 && direction != 1))
+ return;
+
+ switch(ssl_ver) {
+#ifdef SSL2_VERSION /* removed in recent versions */
+ case SSL2_VERSION:
+ verstr = "SSLv2";
+ break;
+#endif
+#ifdef SSL3_VERSION
+ case SSL3_VERSION:
+ verstr = "SSLv3";
+ break;
+#endif
+ case TLS1_VERSION:
+ verstr = "TLSv1.0";
+ break;
+#ifdef TLS1_1_VERSION
+ case TLS1_1_VERSION:
+ verstr = "TLSv1.1";
+ break;
+#endif
+#ifdef TLS1_2_VERSION
+ case TLS1_2_VERSION:
+ verstr = "TLSv1.2";
+ break;
+#endif
+#ifdef TLS1_3_VERSION
+ case TLS1_3_VERSION:
+ verstr = "TLSv1.3";
+ break;
+#endif
+ case 0:
+ break;
+ default:
+ msnprintf(unknown, sizeof(unknown), "(%x)", ssl_ver);
+ verstr = unknown;
+ break;
+ }
+
+ /* Log progress for interesting records only (like Handshake or Alert), skip
+ * all raw record headers (content_type == SSL3_RT_HEADER or ssl_ver == 0).
+ * For TLS 1.3, skip notification of the decrypted inner Content-Type.
+ */
+ if(ssl_ver
+#ifdef SSL3_RT_INNER_CONTENT_TYPE
+ && content_type != SSL3_RT_INNER_CONTENT_TYPE
+#endif
+ ) {
+ const char *msg_name, *tls_rt_name;
+ char ssl_buf[1024];
+ int msg_type, txt_len;
+
+ /* the info given when the version is zero is not that useful for us */
+
+ ssl_ver >>= 8; /* check the upper 8 bits only below */
+
+ /* SSLv2 doesn't seem to have TLS record-type headers, so OpenSSL
+ * always pass-up content-type as 0. But the interesting message-type
+ * is at 'buf[0]'.
+ */
+ if(ssl_ver == SSL3_VERSION_MAJOR && content_type)
+ tls_rt_name = tls_rt_type(content_type);
+ else
+ tls_rt_name = "";
+
+ if(content_type == SSL3_RT_CHANGE_CIPHER_SPEC) {
+ msg_type = *(char *)buf;
+ msg_name = "Change cipher spec";
+ }
+ else if(content_type == SSL3_RT_ALERT) {
+ msg_type = (((char *)buf)[0] << 8) + ((char *)buf)[1];
+ msg_name = SSL_alert_desc_string_long(msg_type);
+ }
+ else {
+ msg_type = *(char *)buf;
+ msg_name = ssl_msg_type(ssl_ver, msg_type);
+ }
+
+ txt_len = msnprintf(ssl_buf, sizeof(ssl_buf),
+ CFMSG(cf, "%s (%s), %s, %s (%d):\n"),
+ verstr, direction?"OUT":"IN",
+ tls_rt_name, msg_name, msg_type);
+ if(0 <= txt_len && (unsigned)txt_len < sizeof(ssl_buf)) {
+ Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len);
+ }
+ }
+
+ Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT :
+ CURLINFO_SSL_DATA_IN, (char *)buf, len);
+ (void) ssl;
+}
+#endif
+
+#ifdef USE_OPENSSL
+/* ====================================================== */
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+# define use_sni(x) sni = (x)
+#else
+# define use_sni(x) Curl_nop_stmt
+#endif
+
+/* Check for OpenSSL 1.0.2 which has ALPN support. */
+#undef HAS_ALPN
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L \
+ && !defined(OPENSSL_NO_TLSEXT)
+# define HAS_ALPN 1
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */
+static CURLcode
+set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ /* first, TLS min version... */
+ long curl_ssl_version_min = conn_config->version;
+ long curl_ssl_version_max;
+
+ /* convert curl min SSL version option to OpenSSL constant */
+#if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)
+ uint16_t ossl_ssl_version_min = 0;
+ uint16_t ossl_ssl_version_max = 0;
+#else
+ long ossl_ssl_version_min = 0;
+ long ossl_ssl_version_max = 0;
+#endif
+ switch(curl_ssl_version_min) {
+ case CURL_SSLVERSION_TLSv1: /* TLS 1.x */
+ case CURL_SSLVERSION_TLSv1_0:
+ ossl_ssl_version_min = TLS1_VERSION;
+ break;
+ case CURL_SSLVERSION_TLSv1_1:
+ ossl_ssl_version_min = TLS1_1_VERSION;
+ break;
+ case CURL_SSLVERSION_TLSv1_2:
+ ossl_ssl_version_min = TLS1_2_VERSION;
+ break;
+ case CURL_SSLVERSION_TLSv1_3:
+#ifdef TLS1_3_VERSION
+ ossl_ssl_version_min = TLS1_3_VERSION;
+ break;
+#else
+ return CURLE_NOT_BUILT_IN;
+#endif
+ }
+
+ /* CURL_SSLVERSION_DEFAULT means that no option was selected.
+ We don't want to pass 0 to SSL_CTX_set_min_proto_version as
+ it would enable all versions down to the lowest supported by
+ the library.
+ So we skip this, and stay with the library default
+ */
+ if(curl_ssl_version_min != CURL_SSLVERSION_DEFAULT) {
+ if(!SSL_CTX_set_min_proto_version(ctx, ossl_ssl_version_min)) {
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+ /* ... then, TLS max version */
+ curl_ssl_version_max = conn_config->version_max;
+
+ /* convert curl max SSL version option to OpenSSL constant */
+ switch(curl_ssl_version_max) {
+ case CURL_SSLVERSION_MAX_TLSv1_0:
+ ossl_ssl_version_max = TLS1_VERSION;
+ break;
+ case CURL_SSLVERSION_MAX_TLSv1_1:
+ ossl_ssl_version_max = TLS1_1_VERSION;
+ break;
+ case CURL_SSLVERSION_MAX_TLSv1_2:
+ ossl_ssl_version_max = TLS1_2_VERSION;
+ break;
+#ifdef TLS1_3_VERSION
+ case CURL_SSLVERSION_MAX_TLSv1_3:
+ ossl_ssl_version_max = TLS1_3_VERSION;
+ break;
+#endif
+ case CURL_SSLVERSION_MAX_NONE: /* none selected */
+ case CURL_SSLVERSION_MAX_DEFAULT: /* max selected */
+ default:
+ /* SSL_CTX_set_max_proto_version states that:
+ setting the maximum to 0 will enable
+ protocol versions up to the highest version
+ supported by the library */
+ ossl_ssl_version_max = 0;
+ break;
+ }
+
+ if(!SSL_CTX_set_max_proto_version(ctx, ossl_ssl_version_max)) {
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ return CURLE_OK;
+}
+#endif
+
+#ifdef OPENSSL_IS_BORINGSSL
+typedef uint32_t ctx_option_t;
+#elif OPENSSL_VERSION_NUMBER >= 0x30000000L
+typedef uint64_t ctx_option_t;
+#else
+typedef long ctx_option_t;
+#endif
+
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L) /* 1.1.0 */
+static CURLcode
+set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ long ssl_version = conn_config->version;
+ long ssl_version_max = conn_config->version_max;
+
+ (void) data; /* In case it's unused. */
+
+ switch(ssl_version) {
+ case CURL_SSLVERSION_TLSv1_3:
+#ifdef TLS1_3_VERSION
+ {
+ struct ssl_connect_data *connssl = cf->ctx;
+ DEBUGASSERT(connssl->backend);
+ SSL_CTX_set_max_proto_version(connssl->backend->ctx, TLS1_3_VERSION);
+ *ctx_options |= SSL_OP_NO_TLSv1_2;
+ }
+#else
+ (void)ctx_options;
+ failf(data, OSSL_PACKAGE " was built without TLS 1.3 support");
+ return CURLE_NOT_BUILT_IN;
+#endif
+ /* FALLTHROUGH */
+ case CURL_SSLVERSION_TLSv1_2:
+#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+ *ctx_options |= SSL_OP_NO_TLSv1_1;
+#else
+ failf(data, OSSL_PACKAGE " was built without TLS 1.2 support");
+ return CURLE_NOT_BUILT_IN;
+#endif
+ /* FALLTHROUGH */
+ case CURL_SSLVERSION_TLSv1_1:
+#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+ *ctx_options |= SSL_OP_NO_TLSv1;
+#else
+ failf(data, OSSL_PACKAGE " was built without TLS 1.1 support");
+ return CURLE_NOT_BUILT_IN;
+#endif
+ /* FALLTHROUGH */
+ case CURL_SSLVERSION_TLSv1_0:
+ case CURL_SSLVERSION_TLSv1:
+ break;
+ }
+
+ switch(ssl_version_max) {
+ case CURL_SSLVERSION_MAX_TLSv1_0:
+#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+ *ctx_options |= SSL_OP_NO_TLSv1_1;
+#endif
+ /* FALLTHROUGH */
+ case CURL_SSLVERSION_MAX_TLSv1_1:
+#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+ *ctx_options |= SSL_OP_NO_TLSv1_2;
+#endif
+ /* FALLTHROUGH */
+ case CURL_SSLVERSION_MAX_TLSv1_2:
+#ifdef TLS1_3_VERSION
+ *ctx_options |= SSL_OP_NO_TLSv1_3;
+#endif
+ break;
+ case CURL_SSLVERSION_MAX_TLSv1_3:
+#ifdef TLS1_3_VERSION
+ break;
+#else
+ failf(data, OSSL_PACKAGE " was built without TLS 1.3 support");
+ return CURLE_NOT_BUILT_IN;
+#endif
+ }
+ return CURLE_OK;
+}
+#endif
+
+/* The "new session" callback must return zero if the session can be removed
+ * or non-zero if the session has been put into the session cache.
+ */
+static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
+{
+ int res = 0;
+ struct Curl_easy *data;
+ struct Curl_cfilter *cf;
+ const struct ssl_config_data *config;
+ curl_socket_t *sockindex_ptr;
+ int data_idx = ossl_get_ssl_data_index();
+ int cf_idx = ossl_get_ssl_cf_index();
+ int sockindex_idx = ossl_get_ssl_sockindex_index();
+ int proxy_idx = ossl_get_proxy_index();
+ bool isproxy;
+
+ if(data_idx < 0 || cf_idx < 0 || sockindex_idx < 0 || proxy_idx < 0)
+ return 0;
+
+ cf = (struct Curl_cfilter*) SSL_get_ex_data(ssl, cf_idx);
+ data = (struct Curl_easy *) SSL_get_ex_data(ssl, data_idx);
+ /* The sockindex has been stored as a pointer to an array element */
+ sockindex_ptr = (curl_socket_t*) SSL_get_ex_data(ssl, sockindex_idx);
+ if(!cf || !data || !sockindex_ptr)
+ return 0;
+
+ isproxy = Curl_ssl_cf_is_proxy(cf);
+
+ config = Curl_ssl_cf_get_config(cf, data);
+ if(config->primary.sessionid) {
+ bool incache;
+ bool added = FALSE;
+ void *old_ssl_sessionid = NULL;
+
+ Curl_ssl_sessionid_lock(data);
+ if(isproxy)
+ incache = FALSE;
+ else
+ incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL));
+ if(incache) {
+ if(old_ssl_sessionid != ssl_sessionid) {
+ infof(data, "old SSL session ID is stale, removing");
+ Curl_ssl_delsessionid(data, old_ssl_sessionid);
+ incache = FALSE;
+ }
+ }
+
+ if(!incache) {
+ if(!Curl_ssl_addsessionid(cf, data, ssl_sessionid,
+ 0 /* unknown size */, &added)) {
+ if(added) {
+ /* the session has been put into the session cache */
+ res = 1;
+ }
+ }
+ else
+ failf(data, "failed to store ssl session");
+ }
+ Curl_ssl_sessionid_unlock(data);
+ }
+
+ return res;
+}
+
+static CURLcode load_cacert_from_memory(X509_STORE *store,
+ const struct curl_blob *ca_info_blob)
+{
+ /* these need to be freed at the end */
+ BIO *cbio = NULL;
+ STACK_OF(X509_INFO) *inf = NULL;
+
+ /* everything else is just a reference */
+ int i, count = 0;
+ X509_INFO *itmp = NULL;
+
+ if(ca_info_blob->len > (size_t)INT_MAX)
+ return CURLE_SSL_CACERT_BADFILE;
+
+ cbio = BIO_new_mem_buf(ca_info_blob->data, (int)ca_info_blob->len);
+ if(!cbio)
+ return CURLE_OUT_OF_MEMORY;
+
+ inf = PEM_X509_INFO_read_bio(cbio, NULL, NULL, NULL);
+ if(!inf) {
+ BIO_free(cbio);
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+
+ /* add each entry from PEM file to x509_store */
+ for(i = 0; i < (int)sk_X509_INFO_num(inf); ++i) {
+ itmp = sk_X509_INFO_value(inf, i);
+ if(itmp->x509) {
+ if(X509_STORE_add_cert(store, itmp->x509)) {
+ ++count;
+ }
+ else {
+ /* set count to 0 to return an error */
+ count = 0;
+ break;
+ }
+ }
+ if(itmp->crl) {
+ if(X509_STORE_add_crl(store, itmp->crl)) {
+ ++count;
+ }
+ else {
+ /* set count to 0 to return an error */
+ count = 0;
+ break;
+ }
+ }
+ }
+
+ sk_X509_INFO_pop_free(inf, X509_INFO_free);
+ BIO_free(cbio);
+
+ /* if we didn't end up importing anything, treat that as an error */
+ return (count > 0 ? CURLE_OK : CURLE_SSL_CACERT_BADFILE);
+}
+
+static CURLcode populate_x509_store(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ X509_STORE *store)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ CURLcode result = CURLE_OK;
+ X509_LOOKUP *lookup = NULL;
+ const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
+ const char * const ssl_cafile =
+ /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
+ (ca_info_blob ? NULL : conn_config->CAfile);
+ const char * const ssl_capath = conn_config->CApath;
+ const char * const ssl_crlfile = ssl_config->primary.CRLfile;
+ const bool verifypeer = conn_config->verifypeer;
+ bool imported_native_ca = false;
+
+ if(!store)
+ return CURLE_OUT_OF_MEMORY;
+
+#if defined(USE_WIN32_CRYPTO)
+ /* Import certificates from the Windows root certificate store if requested.
+ https://stackoverflow.com/questions/9507184/
+ https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037
+ https://datatracker.ietf.org/doc/html/rfc5280 */
+ if((conn_config->verifypeer || conn_config->verifyhost) &&
+ (ssl_config->native_ca_store)) {
+ HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT"));
+
+ if(hStore) {
+ PCCERT_CONTEXT pContext = NULL;
+ /* The array of enhanced key usage OIDs will vary per certificate and is
+ declared outside of the loop so that rather than malloc/free each
+ iteration we can grow it with realloc, when necessary. */
+ CERT_ENHKEY_USAGE *enhkey_usage = NULL;
+ DWORD enhkey_usage_size = 0;
+
+ /* This loop makes a best effort to import all valid certificates from
+ the MS root store. If a certificate cannot be imported it is skipped.
+ 'result' is used to store only hard-fail conditions (such as out of
+ memory) that cause an early break. */
+ result = CURLE_OK;
+ for(;;) {
+ X509 *x509;
+ FILETIME now;
+ BYTE key_usage[2];
+ DWORD req_size;
+ const unsigned char *encoded_cert;
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ char cert_name[256];
+#endif
+
+ pContext = CertEnumCertificatesInStore(hStore, pContext);
+ if(!pContext)
+ break;
+
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
+ NULL, cert_name, sizeof(cert_name))) {
+ strcpy(cert_name, "Unknown");
+ }
+ infof(data, "SSL: Checking cert \"%s\"", cert_name);
+#endif
+
+ encoded_cert = (const unsigned char *)pContext->pbCertEncoded;
+ if(!encoded_cert)
+ continue;
+
+ GetSystemTimeAsFileTime(&now);
+ if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 ||
+ CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0)
+ continue;
+
+ /* If key usage exists check for signing attribute */
+ if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType,
+ pContext->pCertInfo,
+ key_usage, sizeof(key_usage))) {
+ if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE))
+ continue;
+ }
+ else if(GetLastError())
+ continue;
+
+ /* If enhanced key usage exists check for server auth attribute.
+ *
+ * Note "In a Microsoft environment, a certificate might also have EKU
+ * extended properties that specify valid uses for the certificate."
+ * The call below checks both, and behavior varies depending on what is
+ * found. For more details see CertGetEnhancedKeyUsage doc.
+ */
+ if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) {
+ if(req_size && req_size > enhkey_usage_size) {
+ void *tmp = realloc(enhkey_usage, req_size);
+
+ if(!tmp) {
+ failf(data, "SSL: Out of memory allocating for OID list");
+ result = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+
+ enhkey_usage = (CERT_ENHKEY_USAGE *)tmp;
+ enhkey_usage_size = req_size;
+ }
+
+ if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) {
+ if(!enhkey_usage->cUsageIdentifier) {
+ /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate is
+ good for all uses. If it returns zero, the certificate has no
+ valid uses." */
+ if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND)
+ continue;
+ }
+ else {
+ DWORD i;
+ bool found = false;
+
+ for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) {
+ if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */,
+ enhkey_usage->rgpszUsageIdentifier[i])) {
+ found = true;
+ break;
+ }
+ }
+
+ if(!found)
+ continue;
+ }
+ }
+ else
+ continue;
+ }
+ else
+ continue;
+
+ x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
+ if(!x509)
+ continue;
+
+ /* Try to import the certificate. This may fail for legitimate reasons
+ such as duplicate certificate, which is allowed by MS but not
+ OpenSSL. */
+ if(X509_STORE_add_cert(store, x509) == 1) {
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ infof(data, "SSL: Imported cert \"%s\"", cert_name);
+#endif
+ imported_native_ca = true;
+ }
+ X509_free(x509);
+ }
+
+ free(enhkey_usage);
+ CertFreeCertificateContext(pContext);
+ CertCloseStore(hStore, 0);
+
+ if(result)
+ return result;
+ }
+ if(imported_native_ca)
+ infof(data, "successfully imported Windows CA store");
+ else
+ infof(data, "error importing Windows CA store, continuing anyway");
+ }
+#endif
+
+ if(ca_info_blob) {
+ result = load_cacert_from_memory(store, ca_info_blob);
+ if(result) {
+ if(result == CURLE_OUT_OF_MEMORY ||
+ (verifypeer && !imported_native_ca)) {
+ failf(data, "error importing CA certificate blob");
+ return result;
+ }
+ /* Only warn if no certificate verification is required. */
+ infof(data, "error importing CA certificate blob, continuing anyway");
+ }
+ }
+
+ if(verifypeer && !imported_native_ca && (ssl_cafile || ssl_capath)) {
+#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
+ /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */
+ if(ssl_cafile &&
+ !X509_STORE_load_file(store, ssl_cafile)) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate file: %s", ssl_cafile);
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ if(ssl_capath &&
+ !X509_STORE_load_path(store, ssl_capath)) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate path: %s", ssl_capath);
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+#else
+ /* tell OpenSSL where to find CA certificates that are used to verify the
+ server's certificate. */
+ if(!X509_STORE_load_locations(store, ssl_cafile, ssl_capath)) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate verify locations:"
+ " CAfile: %s CApath: %s",
+ ssl_cafile ? ssl_cafile : "none",
+ ssl_capath ? ssl_capath : "none");
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+#endif
+ infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
+ infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
+ }
+
+#ifdef CURL_CA_FALLBACK
+ if(verifypeer &&
+ !ca_info_blob && !ssl_cafile && !ssl_capath && !imported_native_ca) {
+ /* verifying the peer without any CA certificates won't
+ work so use openssl's built-in default as fallback */
+ X509_STORE_set_default_paths(store);
+ }
+#endif
+
+ if(ssl_crlfile) {
+ /* tell OpenSSL where to find CRL file that is used to check certificate
+ * revocation */
+ lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
+ if(!lookup ||
+ (!X509_load_crl_file(lookup, ssl_crlfile, X509_FILETYPE_PEM)) ) {
+ failf(data, "error loading CRL file: %s", ssl_crlfile);
+ return CURLE_SSL_CRL_BADFILE;
+ }
+ /* Everything is fine. */
+ infof(data, "successfully loaded CRL file:");
+ X509_STORE_set_flags(store,
+ X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
+
+ infof(data, " CRLfile: %s", ssl_crlfile);
+ }
+
+ if(verifypeer) {
+ /* Try building a chain using issuers in the trusted store first to avoid
+ problems with server-sent legacy intermediates. Newer versions of
+ OpenSSL do alternate chain checking by default but we do not know how to
+ determine that in a reliable manner.
+ https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest
+ */
+#if defined(X509_V_FLAG_TRUSTED_FIRST)
+ X509_STORE_set_flags(store, X509_V_FLAG_TRUSTED_FIRST);
+#endif
+#ifdef X509_V_FLAG_PARTIAL_CHAIN
+ if(!ssl_config->no_partialchain && !ssl_crlfile) {
+ /* Have intermediate certificates in the trust store be treated as
+ trust-anchors, in the same way as self-signed root CA certificates
+ are. This allows users to verify servers using the intermediate cert
+ only, instead of needing the whole chain.
+
+ Due to OpenSSL bug https://github.com/openssl/openssl/issues/5081 we
+ cannot do partial chains with a CRL check.
+ */
+ X509_STORE_set_flags(store, X509_V_FLAG_PARTIAL_CHAIN);
+ }
+#endif
+ }
+
+ return result;
+}
+
+#if defined(HAVE_SSL_X509_STORE_SHARE)
+static bool cached_x509_store_expired(const struct Curl_easy *data,
+ const struct multi_ssl_backend_data *mb)
+{
+ const struct ssl_general_config *cfg = &data->set.general_ssl;
+ struct curltime now = Curl_now();
+ timediff_t elapsed_ms = Curl_timediff(now, mb->time);
+ timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
+
+ if(timeout_ms < 0)
+ return false;
+
+ return elapsed_ms >= timeout_ms;
+}
+
+static bool cached_x509_store_different(
+ struct Curl_cfilter *cf,
+ const struct multi_ssl_backend_data *mb)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ if(!mb->CAfile || !conn_config->CAfile)
+ return mb->CAfile != conn_config->CAfile;
+
+ return strcmp(mb->CAfile, conn_config->CAfile);
+}
+
+static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi;
+ X509_STORE *store = NULL;
+
+ if(multi &&
+ multi->ssl_backend_data &&
+ multi->ssl_backend_data->store &&
+ !cached_x509_store_expired(data, multi->ssl_backend_data) &&
+ !cached_x509_store_different(cf, multi->ssl_backend_data)) {
+ store = multi->ssl_backend_data->store;
+ }
+
+ return store;
+}
+
+static void set_cached_x509_store(struct Curl_cfilter *cf,
+ const struct Curl_easy *data,
+ X509_STORE *store)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi;
+ struct multi_ssl_backend_data *mbackend;
+
+ if(!multi)
+ return;
+
+ if(!multi->ssl_backend_data) {
+ multi->ssl_backend_data = calloc(1, sizeof(struct multi_ssl_backend_data));
+ if(!multi->ssl_backend_data)
+ return;
+ }
+
+ mbackend = multi->ssl_backend_data;
+
+ if(X509_STORE_up_ref(store)) {
+ char *CAfile = NULL;
+
+ if(conn_config->CAfile) {
+ CAfile = strdup(conn_config->CAfile);
+ if(!CAfile) {
+ X509_STORE_free(store);
+ return;
+ }
+ }
+
+ if(mbackend->store) {
+ X509_STORE_free(mbackend->store);
+ free(mbackend->CAfile);
+ }
+
+ mbackend->time = Curl_now();
+ mbackend->store = store;
+ mbackend->CAfile = CAfile;
+ }
+}
+
+static CURLcode set_up_x509_store(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ssl_backend_data *backend)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ CURLcode result = CURLE_OK;
+ X509_STORE *cached_store;
+ bool cache_criteria_met;
+
+ /* Consider the X509 store cacheable if it comes exclusively from a CAfile,
+ or no source is provided and we are falling back to openssl's built-in
+ default. */
+ cache_criteria_met = (data->set.general_ssl.ca_cache_timeout != 0) &&
+ conn_config->verifypeer &&
+ !conn_config->CApath &&
+ !conn_config->ca_info_blob &&
+ !ssl_config->primary.CRLfile &&
+ !ssl_config->native_ca_store;
+
+ cached_store = get_cached_x509_store(cf, data);
+ if(cached_store && cache_criteria_met && X509_STORE_up_ref(cached_store)) {
+ SSL_CTX_set_cert_store(backend->ctx, cached_store);
+ }
+ else {
+ X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx);
+
+ result = populate_x509_store(cf, data, store);
+ if(result == CURLE_OK && cache_criteria_met) {
+ set_cached_x509_store(cf, data, store);
+ }
+ }
+
+ return result;
+}
+#else /* HAVE_SSL_X509_STORE_SHARE */
+static CURLcode set_up_x509_store(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ssl_backend_data *backend)
+{
+ X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx);
+
+ return populate_x509_store(cf, data, store);
+}
+#endif /* HAVE_SSL_X509_STORE_SHARE */
+
+static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ char *ciphers;
+ SSL_METHOD_QUAL SSL_METHOD *req_method = NULL;
+ struct ssl_connect_data *connssl = cf->ctx;
+ ctx_option_t ctx_options = 0;
+ void *ssl_sessionid = NULL;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ BIO *bio;
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+ bool sni;
+ const char *hostname = connssl->hostname;
+
+#ifdef ENABLE_IPV6
+ struct in6_addr addr;
+#else
+ struct in_addr addr;
+#endif
+#endif
+ const long int ssl_version = conn_config->version;
+#ifdef USE_OPENSSL_SRP
+ const enum CURL_TLSAUTH ssl_authtype = ssl_config->primary.authtype;
+#endif
+ char * const ssl_cert = ssl_config->primary.clientcert;
+ const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
+ const char * const ssl_cert_type = ssl_config->cert_type;
+ const bool verifypeer = conn_config->verifypeer;
+ char error_buffer[256];
+ struct ssl_backend_data *backend = connssl->backend;
+
+ DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
+ DEBUGASSERT(backend);
+
+ /* Make funny stuff to get random input */
+ result = ossl_seed(data);
+ if(result)
+ return result;
+
+ ssl_config->certverifyresult = !X509_V_OK;
+
+ /* check to see if we've been told to use an explicit SSL/TLS version */
+
+ switch(ssl_version) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+ case CURL_SSLVERSION_TLSv1_0:
+ case CURL_SSLVERSION_TLSv1_1:
+ case CURL_SSLVERSION_TLSv1_2:
+ case CURL_SSLVERSION_TLSv1_3:
+ /* it will be handled later with the context options */
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+ req_method = TLS_client_method();
+#else
+ req_method = SSLv23_client_method();
+#endif
+ use_sni(TRUE);
+ break;
+ case CURL_SSLVERSION_SSLv2:
+ failf(data, "No SSLv2 support");
+ return CURLE_NOT_BUILT_IN;
+ case CURL_SSLVERSION_SSLv3:
+ failf(data, "No SSLv3 support");
+ return CURLE_NOT_BUILT_IN;
+ default:
+ failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ if(backend->ctx) {
+ /* This happens when an error was encountered before in this
+ * step and we are called to do it again. Get rid of any leftover
+ * from the previous call. */
+ ossl_close(cf, data);
+ }
+ backend->ctx = SSL_CTX_new(req_method);
+
+ if(!backend->ctx) {
+ failf(data, "SSL: couldn't create a context: %s",
+ ossl_strerror(ERR_peek_error(), error_buffer, sizeof(error_buffer)));
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+#ifdef SSL_MODE_RELEASE_BUFFERS
+ SSL_CTX_set_mode(backend->ctx, SSL_MODE_RELEASE_BUFFERS);
+#endif
+
+#ifdef SSL_CTRL_SET_MSG_CALLBACK
+ if(data->set.fdebug && data->set.verbose) {
+ /* the SSL trace callback is only used for verbose logging */
+ SSL_CTX_set_msg_callback(backend->ctx, ossl_trace);
+ SSL_CTX_set_msg_callback_arg(backend->ctx, cf->conn);
+ set_logger(connssl, data);
+ }
+#endif
+
+ /* OpenSSL contains code to work around lots of bugs and flaws in various
+ SSL-implementations. SSL_CTX_set_options() is used to enabled those
+ work-arounds. The man page for this option states that SSL_OP_ALL enables
+ all the work-arounds and that "It is usually safe to use SSL_OP_ALL to
+ enable the bug workaround options if compatibility with somewhat broken
+ implementations is desired."
+
+ The "-no_ticket" option was introduced in OpenSSL 0.9.8j. It's a flag to
+ disable "rfc4507bis session ticket support". rfc4507bis was later turned
+ into the proper RFC5077: https://datatracker.ietf.org/doc/html/rfc5077
+
+ The enabled extension concerns the session management. I wonder how often
+ libcurl stops a connection and then resumes a TLS session. Also, sending
+ the session data is some overhead. I suggest that you just use your
+ proposed patch (which explicitly disables TICKET).
+
+ If someone writes an application with libcurl and OpenSSL who wants to
+ enable the feature, one can do this in the SSL callback.
+
+ SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG option enabling allowed proper
+ interoperability with web server Netscape Enterprise Server 2.0.1 which
+ was released back in 1996.
+
+ Due to CVE-2010-4180, option SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG has
+ become ineffective as of OpenSSL 0.9.8q and 1.0.0c. In order to mitigate
+ CVE-2010-4180 when using previous OpenSSL versions we no longer enable
+ this option regardless of OpenSSL version and SSL_OP_ALL definition.
+
+ OpenSSL added a work-around for a SSL 3.0/TLS 1.0 CBC vulnerability
+ (https://www.openssl.org/~bodo/tls-cbc.txt). In 0.9.6e they added a bit to
+ SSL_OP_ALL that _disables_ that work-around despite the fact that
+ SSL_OP_ALL is documented to do "rather harmless" workarounds. In order to
+ keep the secure work-around, the SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS bit
+ must not be set.
+ */
+
+ ctx_options = SSL_OP_ALL;
+
+#ifdef SSL_OP_NO_TICKET
+ ctx_options |= SSL_OP_NO_TICKET;
+#endif
+
+#ifdef SSL_OP_NO_COMPRESSION
+ ctx_options |= SSL_OP_NO_COMPRESSION;
+#endif
+
+#ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
+ /* mitigate CVE-2010-4180 */
+ ctx_options &= ~SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG;
+#endif
+
+#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
+ /* unless the user explicitly asks to allow the protocol vulnerability we
+ use the work-around */
+ if(!ssl_config->enable_beast)
+ ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
+#endif
+
+ switch(ssl_version) {
+ case CURL_SSLVERSION_SSLv2:
+ case CURL_SSLVERSION_SSLv3:
+ return CURLE_NOT_BUILT_IN;
+
+ /* "--tlsv<x.y>" options mean TLS >= version <x.y> */
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1: /* TLS >= version 1.0 */
+ case CURL_SSLVERSION_TLSv1_0: /* TLS >= version 1.0 */
+ case CURL_SSLVERSION_TLSv1_1: /* TLS >= version 1.1 */
+ case CURL_SSLVERSION_TLSv1_2: /* TLS >= version 1.2 */
+ case CURL_SSLVERSION_TLSv1_3: /* TLS >= version 1.3 */
+ /* asking for any TLS version as the minimum, means no SSL versions
+ allowed */
+ ctx_options |= SSL_OP_NO_SSLv2;
+ ctx_options |= SSL_OP_NO_SSLv3;
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */
+ result = set_ssl_version_min_max(cf, backend->ctx);
+#else
+ result = set_ssl_version_min_max_legacy(&ctx_options, cf, data);
+#endif
+ if(result != CURLE_OK)
+ return result;
+ break;
+
+ default:
+ failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ SSL_CTX_set_options(backend->ctx, ctx_options);
+
+#ifdef HAS_ALPN
+ if(cf->conn->bits.tls_enable_alpn) {
+ int cur = 0;
+ unsigned char protocols[128];
+
+#ifdef USE_HTTP2
+ if(data->state.httpwant >= CURL_HTTP_VERSION_2
+#ifndef CURL_DISABLE_PROXY
+ && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy)
+#endif
+ ) {
+ protocols[cur++] = ALPN_H2_LENGTH;
+
+ memcpy(&protocols[cur], ALPN_H2, ALPN_H2_LENGTH);
+ cur += ALPN_H2_LENGTH;
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
+ }
+#endif
+
+ protocols[cur++] = ALPN_HTTP_1_1_LENGTH;
+ memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
+ cur += ALPN_HTTP_1_1_LENGTH;
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
+
+ /* expects length prefixed preference ordered list of protocols in wire
+ * format
+ */
+ if(SSL_CTX_set_alpn_protos(backend->ctx, protocols, cur)) {
+ failf(data, "Error setting ALPN");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+#endif
+
+ if(ssl_cert || ssl_cert_blob || ssl_cert_type) {
+ if(!result &&
+ !cert_stuff(data, backend->ctx,
+ ssl_cert, ssl_cert_blob, ssl_cert_type,
+ ssl_config->key, ssl_config->key_blob,
+ ssl_config->key_type, ssl_config->key_passwd))
+ result = CURLE_SSL_CERTPROBLEM;
+ if(result)
+ /* failf() is already done in cert_stuff() */
+ return result;
+ }
+
+ ciphers = conn_config->cipher_list;
+ if(!ciphers)
+ ciphers = (char *)DEFAULT_CIPHER_SELECTION;
+ if(ciphers) {
+ if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) {
+ failf(data, "failed setting cipher list: %s", ciphers);
+ return CURLE_SSL_CIPHER;
+ }
+ infof(data, "Cipher selection: %s", ciphers);
+ }
+
+#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
+ {
+ char *ciphers13 = conn_config->cipher_list13;
+ if(ciphers13) {
+ if(!SSL_CTX_set_ciphersuites(backend->ctx, ciphers13)) {
+ failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers13);
+ return CURLE_SSL_CIPHER;
+ }
+ infof(data, "TLS 1.3 cipher selection: %s", ciphers13);
+ }
+ }
+#endif
+
+#ifdef HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH
+ /* OpenSSL 1.1.1 requires clients to opt-in for PHA */
+ SSL_CTX_set_post_handshake_auth(backend->ctx, 1);
+#endif
+
+#ifdef HAVE_SSL_CTX_SET_EC_CURVES
+ {
+ char *curves = conn_config->curves;
+ if(curves) {
+ if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) {
+ failf(data, "failed setting curves list: '%s'", curves);
+ return CURLE_SSL_CIPHER;
+ }
+ }
+ }
+#endif
+
+#ifdef USE_OPENSSL_SRP
+ if((ssl_authtype == CURL_TLSAUTH_SRP) &&
+ Curl_auth_allowed_to_host(data)) {
+ char * const ssl_username = ssl_config->primary.username;
+ char * const ssl_password = ssl_config->primary.password;
+ infof(data, "Using TLS-SRP username: %s", ssl_username);
+
+ if(!SSL_CTX_set_srp_username(backend->ctx, ssl_username)) {
+ failf(data, "Unable to set SRP user name");
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+ if(!SSL_CTX_set_srp_password(backend->ctx, ssl_password)) {
+ failf(data, "failed setting SRP password");
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+ if(!conn_config->cipher_list) {
+ infof(data, "Setting cipher list SRP");
+
+ if(!SSL_CTX_set_cipher_list(backend->ctx, "SRP")) {
+ failf(data, "failed setting SRP cipher list");
+ return CURLE_SSL_CIPHER;
+ }
+ }
+ }
+#endif
+
+ result = set_up_x509_store(cf, data, backend);
+ if(result)
+ return result;
+
+ /* OpenSSL always tries to verify the peer, this only says whether it should
+ * fail to connect if the verification fails, or if it should continue
+ * anyway. In the latter case the result of the verification is checked with
+ * SSL_get_verify_result() below. */
+ SSL_CTX_set_verify(backend->ctx,
+ verifypeer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
+
+ /* Enable logging of secrets to the file specified in env SSLKEYLOGFILE. */
+#ifdef HAVE_KEYLOG_CALLBACK
+ if(Curl_tls_keylog_enabled()) {
+ SSL_CTX_set_keylog_callback(backend->ctx, ossl_keylog_callback);
+ }
+#endif
+
+ /* Enable the session cache because it's a prerequisite for the "new session"
+ * callback. Use the "external storage" mode to prevent OpenSSL from creating
+ * an internal session cache.
+ */
+ SSL_CTX_set_session_cache_mode(backend->ctx,
+ SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL);
+ SSL_CTX_sess_set_new_cb(backend->ctx, ossl_new_session_cb);
+
+ /* give application a chance to interfere with SSL set up. */
+ if(data->set.ssl.fsslctx) {
+ Curl_set_in_callback(data, true);
+ result = (*data->set.ssl.fsslctx)(data, backend->ctx,
+ data->set.ssl.fsslctxp);
+ Curl_set_in_callback(data, false);
+ if(result) {
+ failf(data, "error signaled by ssl ctx callback");
+ return result;
+ }
+ }
+
+ /* Let's make an SSL structure */
+ if(backend->handle)
+ SSL_free(backend->handle);
+ backend->handle = SSL_new(backend->ctx);
+ if(!backend->handle) {
+ failf(data, "SSL: couldn't create a context (handle)");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
+ !defined(OPENSSL_NO_OCSP)
+ if(conn_config->verifystatus)
+ SSL_set_tlsext_status_type(backend->handle, TLSEXT_STATUSTYPE_ocsp);
+#endif
+
+#if defined(OPENSSL_IS_BORINGSSL) && defined(ALLOW_RENEG)
+ SSL_set_renegotiate_mode(backend->handle, ssl_renegotiate_freely);
+#endif
+
+ SSL_set_connect_state(backend->handle);
+
+ backend->server_cert = 0x0;
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+ if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) &&
+#ifdef ENABLE_IPV6
+ (0 == Curl_inet_pton(AF_INET6, hostname, &addr)) &&
+#endif
+ sni) {
+ char *snihost = Curl_ssl_snihost(data, hostname, NULL);
+ if(!snihost || !SSL_set_tlsext_host_name(backend->handle, snihost)) {
+ failf(data, "Failed set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+#endif
+
+ if(!ossl_attach_data(cf, data)) {
+ /* Maybe the internal errors of SSL_get_ex_new_index or SSL_set_ex_data */
+ failf(data, "SSL: ossl_attach_data failed: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ if(ssl_config->primary.sessionid) {
+ Curl_ssl_sessionid_lock(data);
+ if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)) {
+ /* we got a session id, use it! */
+ if(!SSL_set_session(backend->handle, ssl_sessionid)) {
+ Curl_ssl_sessionid_unlock(data);
+ failf(data, "SSL: SSL_set_session failed: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ /* Informational message */
+ infof(data, "SSL re-using session ID");
+ }
+ Curl_ssl_sessionid_unlock(data);
+ }
+
+ bio = BIO_new(bio_cf_method);
+ if(!bio)
+ return CURLE_OUT_OF_MEMORY;
+
+ BIO_set_data(bio, cf);
+ SSL_set_bio(backend->handle, bio, bio);
+
+ connssl->connecting_state = ssl_connect_2;
+
+ return CURLE_OK;
+}
+
+static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ int err;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ DEBUGASSERT(ssl_connect_2 == connssl->connecting_state
+ || ssl_connect_2_reading == connssl->connecting_state
+ || ssl_connect_2_writing == connssl->connecting_state);
+ DEBUGASSERT(backend);
+
+ ERR_clear_error();
+
+ err = SSL_connect(backend->handle);
+#ifndef HAVE_KEYLOG_CALLBACK
+ if(Curl_tls_keylog_enabled()) {
+ /* If key logging is enabled, wait for the handshake to complete and then
+ * proceed with logging secrets (for TLS 1.2 or older).
+ */
+ ossl_log_tls12_secret(backend->handle, &backend->keylog_done);
+ }
+#endif
+
+ /* 1 is fine
+ 0 is "not successful but was shut down controlled"
+ <0 is "handshake was not successful, because a fatal error occurred" */
+ if(1 != err) {
+ int detail = SSL_get_error(backend->handle, err);
+
+ if(SSL_ERROR_WANT_READ == detail) {
+ connssl->connecting_state = ssl_connect_2_reading;
+ return CURLE_OK;
+ }
+ if(SSL_ERROR_WANT_WRITE == detail) {
+ connssl->connecting_state = ssl_connect_2_writing;
+ return CURLE_OK;
+ }
+#ifdef SSL_ERROR_WANT_ASYNC
+ if(SSL_ERROR_WANT_ASYNC == detail) {
+ connssl->connecting_state = ssl_connect_2;
+ return CURLE_OK;
+ }
+#endif
+ else if(backend->io_result == CURLE_AGAIN) {
+ return CURLE_OK;
+ }
+ else {
+ /* untreated error */
+ unsigned long errdetail;
+ char error_buffer[256]="";
+ CURLcode result;
+ long lerr;
+ int lib;
+ int reason;
+
+ /* the connection failed, we're not waiting for anything else. */
+ connssl->connecting_state = ssl_connect_2;
+
+ /* Get the earliest error code from the thread's error queue and remove
+ the entry. */
+ errdetail = ERR_get_error();
+
+ /* Extract which lib and reason */
+ lib = ERR_GET_LIB(errdetail);
+ reason = ERR_GET_REASON(errdetail);
+
+ if((lib == ERR_LIB_SSL) &&
+ ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) ||
+ (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) {
+ result = CURLE_PEER_FAILED_VERIFICATION;
+
+ lerr = SSL_get_verify_result(backend->handle);
+ if(lerr != X509_V_OK) {
+ ssl_config->certverifyresult = lerr;
+ msnprintf(error_buffer, sizeof(error_buffer),
+ "SSL certificate problem: %s",
+ X509_verify_cert_error_string(lerr));
+ }
+ else
+ /* strcpy() is fine here as long as the string fits within
+ error_buffer */
+ strcpy(error_buffer, "SSL certificate verification failed");
+ }
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \
+ !defined(LIBRESSL_VERSION_NUMBER) && \
+ !defined(OPENSSL_IS_BORINGSSL))
+ /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on
+ OpenSSL version above v1.1.1, not LibreSSL nor BoringSSL */
+ else if((lib == ERR_LIB_SSL) &&
+ (reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)) {
+ /* If client certificate is required, communicate the
+ error to client */
+ result = CURLE_SSL_CLIENTCERT;
+ ossl_strerror(errdetail, error_buffer, sizeof(error_buffer));
+ }
+#endif
+ else {
+ result = CURLE_SSL_CONNECT_ERROR;
+ ossl_strerror(errdetail, error_buffer, sizeof(error_buffer));
+ }
+
+ /* detail is already set to the SSL error above */
+
+ /* If we e.g. use SSLv2 request-method and the server doesn't like us
+ * (RST connection, etc.), OpenSSL gives no explanation whatsoever and
+ * the SO_ERROR is also lost.
+ */
+ if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) {
+ char extramsg[80]="";
+ int sockerr = SOCKERRNO;
+
+ if(sockerr && detail == SSL_ERROR_SYSCALL)
+ Curl_strerror(sockerr, extramsg, sizeof(extramsg));
+ failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ",
+ extramsg[0] ? extramsg : SSL_ERROR_to_str(detail),
+ connssl->hostname, connssl->port);
+ return result;
+ }
+
+ /* Could be a CERT problem */
+ failf(data, "%s", error_buffer);
+
+ return result;
+ }
+ }
+ else {
+ /* we connected fine, we're not waiting for anything else. */
+ connssl->connecting_state = ssl_connect_3;
+
+ /* Informational message */
+ infof(data, "SSL connection using %s / %s",
+ SSL_get_version(backend->handle),
+ SSL_get_cipher(backend->handle));
+
+#ifdef HAS_ALPN
+ /* Sets data and len to negotiated protocol, len is 0 if no protocol was
+ * negotiated
+ */
+ if(cf->conn->bits.tls_enable_alpn) {
+ const unsigned char *neg_protocol;
+ unsigned int len;
+ SSL_get0_alpn_selected(backend->handle, &neg_protocol, &len);
+ if(len) {
+ infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, len, neg_protocol);
+
+#ifdef USE_HTTP2
+ if(len == ALPN_H2_LENGTH &&
+ !memcmp(ALPN_H2, neg_protocol, len)) {
+ cf->conn->alpn = CURL_HTTP_VERSION_2;
+ }
+ else
+#endif
+ if(len == ALPN_HTTP_1_1_LENGTH &&
+ !memcmp(ALPN_HTTP_1_1, neg_protocol, ALPN_HTTP_1_1_LENGTH)) {
+ cf->conn->alpn = CURL_HTTP_VERSION_1_1;
+ }
+ }
+ else
+ infof(data, VTLS_INFOF_NO_ALPN);
+
+ Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ }
+#endif
+
+ return CURLE_OK;
+ }
+}
+
+/*
+ * Heavily modified from:
+ * https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#OpenSSL
+ */
+static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert,
+ const char *pinnedpubkey)
+{
+ /* Scratch */
+ int len1 = 0, len2 = 0;
+ unsigned char *buff1 = NULL, *temp = NULL;
+
+ /* Result is returned to caller */
+ CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+
+ /* if a path wasn't specified, don't pin */
+ if(!pinnedpubkey)
+ return CURLE_OK;
+
+ if(!cert)
+ return result;
+
+ do {
+ /* Begin Gyrations to get the subjectPublicKeyInfo */
+ /* Thanks to Viktor Dukhovni on the OpenSSL mailing list */
+
+ /* https://groups.google.com/group/mailing.openssl.users/browse_thread
+ /thread/d61858dae102c6c7 */
+ len1 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), NULL);
+ if(len1 < 1)
+ break; /* failed */
+
+ buff1 = temp = malloc(len1);
+ if(!buff1)
+ break; /* failed */
+
+ /* https://www.openssl.org/docs/crypto/d2i_X509.html */
+ len2 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &temp);
+
+ /*
+ * These checks are verifying we got back the same values as when we
+ * sized the buffer. It's pretty weak since they should always be the
+ * same. But it gives us something to test.
+ */
+ if((len1 != len2) || !temp || ((temp - buff1) != len1))
+ break; /* failed */
+
+ /* End Gyrations */
+
+ /* The one good exit point */
+ result = Curl_pin_peer_pubkey(data, pinnedpubkey, buff1, len1);
+ } while(0);
+
+ if(buff1)
+ free(buff1);
+
+ return result;
+}
+
+/*
+ * Get the server cert, verify it and show it, etc., only call failf() if the
+ * 'strict' argument is TRUE as otherwise all this is for informational
+ * purposes only!
+ *
+ * We check certificates to authenticate the server; otherwise we risk
+ * man-in-the-middle attack.
+ */
+static CURLcode servercert(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool strict)
+{
+ struct connectdata *conn = cf->conn;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ CURLcode result = CURLE_OK;
+ int rc;
+ long lerr;
+ X509 *issuer;
+ BIO *fp = NULL;
+ char error_buffer[256]="";
+ char buffer[2048];
+ const char *ptr;
+ BIO *mem = BIO_new(BIO_s_mem());
+ struct ssl_backend_data *backend = connssl->backend;
+
+ DEBUGASSERT(backend);
+
+ if(!mem) {
+ failf(data,
+ "BIO_new return NULL, " OSSL_PACKAGE
+ " error %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)) );
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(data->set.ssl.certinfo)
+ /* asked to gather certificate info */
+ (void)Curl_ossl_certchain(data, connssl->backend->handle);
+
+ backend->server_cert = SSL_get1_peer_certificate(backend->handle);
+ if(!backend->server_cert) {
+ BIO_free(mem);
+ if(!strict)
+ return CURLE_OK;
+
+ failf(data, "SSL: couldn't get peer certificate");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+
+ infof(data, "%s certificate:",
+ Curl_ssl_cf_is_proxy(cf)? "Proxy" : "Server");
+
+ rc = x509_name_oneline(X509_get_subject_name(backend->server_cert),
+ buffer, sizeof(buffer));
+ infof(data, " subject: %s", rc?"[NONE]":buffer);
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ {
+ long len;
+ ASN1_TIME_print(mem, X509_get0_notBefore(backend->server_cert));
+ len = BIO_get_mem_data(mem, (char **) &ptr);
+ infof(data, " start date: %.*s", (int)len, ptr);
+ (void)BIO_reset(mem);
+
+ ASN1_TIME_print(mem, X509_get0_notAfter(backend->server_cert));
+ len = BIO_get_mem_data(mem, (char **) &ptr);
+ infof(data, " expire date: %.*s", (int)len, ptr);
+ (void)BIO_reset(mem);
+ }
+#endif
+
+ BIO_free(mem);
+
+ if(conn_config->verifyhost) {
+ result = Curl_ossl_verifyhost(data, conn, backend->server_cert);
+ if(result) {
+ X509_free(backend->server_cert);
+ backend->server_cert = NULL;
+ return result;
+ }
+ }
+
+ rc = x509_name_oneline(X509_get_issuer_name(backend->server_cert),
+ buffer, sizeof(buffer));
+ if(rc) {
+ if(strict)
+ failf(data, "SSL: couldn't get X509-issuer name");
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else {
+ infof(data, " issuer: %s", buffer);
+
+ /* We could do all sorts of certificate verification stuff here before
+ deallocating the certificate. */
+
+ /* e.g. match issuer name with provided issuer certificate */
+ if(conn_config->issuercert || conn_config->issuercert_blob) {
+ if(conn_config->issuercert_blob) {
+ fp = BIO_new_mem_buf(conn_config->issuercert_blob->data,
+ (int)conn_config->issuercert_blob->len);
+ if(!fp) {
+ failf(data,
+ "BIO_new_mem_buf NULL, " OSSL_PACKAGE
+ " error %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)) );
+ X509_free(backend->server_cert);
+ backend->server_cert = NULL;
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ else {
+ fp = BIO_new(BIO_s_file());
+ if(!fp) {
+ failf(data,
+ "BIO_new return NULL, " OSSL_PACKAGE
+ " error %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)) );
+ X509_free(backend->server_cert);
+ backend->server_cert = NULL;
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(BIO_read_filename(fp, conn_config->issuercert) <= 0) {
+ if(strict)
+ failf(data, "SSL: Unable to open issuer cert (%s)",
+ conn_config->issuercert);
+ BIO_free(fp);
+ X509_free(backend->server_cert);
+ backend->server_cert = NULL;
+ return CURLE_SSL_ISSUER_ERROR;
+ }
+ }
+
+ issuer = PEM_read_bio_X509(fp, NULL, ZERO_NULL, NULL);
+ if(!issuer) {
+ if(strict)
+ failf(data, "SSL: Unable to read issuer cert (%s)",
+ conn_config->issuercert);
+ BIO_free(fp);
+ X509_free(issuer);
+ X509_free(backend->server_cert);
+ backend->server_cert = NULL;
+ return CURLE_SSL_ISSUER_ERROR;
+ }
+
+ if(X509_check_issued(issuer, backend->server_cert) != X509_V_OK) {
+ if(strict)
+ failf(data, "SSL: Certificate issuer check failed (%s)",
+ conn_config->issuercert);
+ BIO_free(fp);
+ X509_free(issuer);
+ X509_free(backend->server_cert);
+ backend->server_cert = NULL;
+ return CURLE_SSL_ISSUER_ERROR;
+ }
+
+ infof(data, " SSL certificate issuer check ok (%s)",
+ conn_config->issuercert);
+ BIO_free(fp);
+ X509_free(issuer);
+ }
+
+ lerr = SSL_get_verify_result(backend->handle);
+ ssl_config->certverifyresult = lerr;
+ if(lerr != X509_V_OK) {
+ if(conn_config->verifypeer) {
+ /* We probably never reach this, because SSL_connect() will fail
+ and we return earlier if verifypeer is set? */
+ if(strict)
+ failf(data, "SSL certificate verify result: %s (%ld)",
+ X509_verify_cert_error_string(lerr), lerr);
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else
+ infof(data, " SSL certificate verify result: %s (%ld),"
+ " continuing anyway.",
+ X509_verify_cert_error_string(lerr), lerr);
+ }
+ else
+ infof(data, " SSL certificate verify ok.");
+ }
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
+ !defined(OPENSSL_NO_OCSP)
+ if(conn_config->verifystatus) {
+ result = verifystatus(cf, data);
+ if(result) {
+ X509_free(backend->server_cert);
+ backend->server_cert = NULL;
+ return result;
+ }
+ }
+#endif
+
+ if(!strict)
+ /* when not strict, we don't bother about the verify cert problems */
+ result = CURLE_OK;
+
+ ptr = Curl_ssl_cf_is_proxy(cf)?
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY];
+ if(!result && ptr) {
+ result = pkp_pin_peer_pubkey(data, backend->server_cert, ptr);
+ if(result)
+ failf(data, "SSL: public key does not match pinned public key");
+ }
+
+ X509_free(backend->server_cert);
+ backend->server_cert = NULL;
+ connssl->connecting_state = ssl_connect_done;
+
+ return result;
+}
+
+static CURLcode ossl_connect_step3(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+
+ DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+
+ /*
+ * We check certificates to authenticate the server; otherwise we risk
+ * man-in-the-middle attack; NEVERTHELESS, if we're told explicitly not to
+ * verify the peer, ignore faults and failures from the server cert
+ * operations.
+ */
+
+ result = servercert(cf, data, conn_config->verifypeer ||
+ conn_config->verifyhost);
+
+ if(!result)
+ connssl->connecting_state = ssl_connect_done;
+
+ return result;
+}
+
+static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool nonblocking,
+ bool *done)
+{
+ CURLcode result = CURLE_OK;
+ struct ssl_connect_data *connssl = cf->ctx;
+ curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ int what;
+
+ /* check if the connection has already been established */
+ if(ssl_connection_complete == connssl->state) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ if(ssl_connect_1 == connssl->connecting_state) {
+ /* Find out how much more time we're allowed */
+ const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+ if(timeout_ms < 0) {
+ /* no need to continue if time is already up */
+ failf(data, "SSL connection timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
+ result = ossl_connect_step1(cf, data);
+ if(result)
+ goto out;
+ }
+
+ while(ssl_connect_2 == connssl->connecting_state ||
+ ssl_connect_2_reading == connssl->connecting_state ||
+ ssl_connect_2_writing == connssl->connecting_state) {
+
+ /* check allowed time left */
+ const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+ if(timeout_ms < 0) {
+ /* no need to continue if time already is up */
+ failf(data, "SSL connection timeout");
+ result = CURLE_OPERATION_TIMEDOUT;
+ goto out;
+ }
+
+ /* if ssl is expecting something, check if it's available. */
+ if(connssl->connecting_state == ssl_connect_2_reading ||
+ connssl->connecting_state == ssl_connect_2_writing) {
+
+ curl_socket_t writefd = ssl_connect_2_writing ==
+ connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+ curl_socket_t readfd = ssl_connect_2_reading ==
+ connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+
+ what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
+ nonblocking?0:timeout_ms);
+ if(what < 0) {
+ /* fatal error */
+ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
+ }
+ if(0 == what) {
+ if(nonblocking) {
+ *done = FALSE;
+ result = CURLE_OK;
+ goto out;
+ }
+ /* timeout */
+ failf(data, "SSL connection timeout");
+ result = CURLE_OPERATION_TIMEDOUT;
+ goto out;
+ }
+ /* socket is readable or writable */
+ }
+
+ /* Run transaction, and return to the caller if it failed or if this
+ * connection is done nonblocking and this loop would execute again. This
+ * permits the owner of a multi handle to abort a connection attempt
+ * before step2 has completed while ensuring that a client using select()
+ * or epoll() will always have a valid fdset to wait on.
+ */
+ result = ossl_connect_step2(cf, data);
+ if(result || (nonblocking &&
+ (ssl_connect_2 == connssl->connecting_state ||
+ ssl_connect_2_reading == connssl->connecting_state ||
+ ssl_connect_2_writing == connssl->connecting_state)))
+ goto out;
+
+ } /* repeat step2 until all transactions are done. */
+
+ if(ssl_connect_3 == connssl->connecting_state) {
+ result = ossl_connect_step3(cf, data);
+ if(result)
+ goto out;
+ }
+
+ if(ssl_connect_done == connssl->connecting_state) {
+ connssl->state = ssl_connection_complete;
+ *done = TRUE;
+ }
+ else
+ *done = FALSE;
+
+ /* Reset our connect state machine */
+ connssl->connecting_state = ssl_connect_1;
+
+out:
+ return result;
+}
+
+static CURLcode ossl_connect_nonblocking(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
+{
+ return ossl_connect_common(cf, data, TRUE, done);
+}
+
+static CURLcode ossl_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ CURLcode result;
+ bool done = FALSE;
+
+ result = ossl_connect_common(cf, data, FALSE, &done);
+ if(result)
+ return result;
+
+ DEBUGASSERT(done);
+
+ return CURLE_OK;
+}
+
+static bool ossl_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ struct ssl_connect_data *ctx = cf->ctx;
+
+ (void)data;
+ DEBUGASSERT(ctx && ctx->backend);
+ if(ctx->backend->handle && SSL_pending(ctx->backend->handle))
+ return TRUE;
+ return FALSE;
+}
+
+static ssize_t ossl_send(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *mem,
+ size_t len,
+ CURLcode *curlcode)
+{
+ /* SSL_write() is said to return 'int' while write() and send() returns
+ 'size_t' */
+ int err;
+ char error_buffer[256];
+ unsigned long sslerror;
+ int memlen;
+ int rc;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+
+ (void)data;
+ DEBUGASSERT(backend);
+
+ ERR_clear_error();
+
+ memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
+ set_logger(connssl, data);
+ rc = SSL_write(backend->handle, mem, memlen);
+
+ if(rc <= 0) {
+ err = SSL_get_error(backend->handle, rc);
+
+ switch(err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ /* The operation did not complete; the same TLS/SSL I/O function
+ should be called again later. This is basically an EWOULDBLOCK
+ equivalent. */
+ *curlcode = CURLE_AGAIN;
+ rc = -1;
+ goto out;
+ case SSL_ERROR_SYSCALL:
+ {
+ int sockerr = SOCKERRNO;
+
+ if(backend->io_result == CURLE_AGAIN) {
+ *curlcode = CURLE_AGAIN;
+ rc = -1;
+ goto out;
+ }
+ sslerror = ERR_get_error();
+ if(sslerror)
+ ossl_strerror(sslerror, error_buffer, sizeof(error_buffer));
+ else if(sockerr)
+ Curl_strerror(sockerr, error_buffer, sizeof(error_buffer));
+ else {
+ strncpy(error_buffer, SSL_ERROR_to_str(err), sizeof(error_buffer));
+ error_buffer[sizeof(error_buffer) - 1] = '\0';
+ }
+ failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d",
+ error_buffer, sockerr);
+ *curlcode = CURLE_SEND_ERROR;
+ rc = -1;
+ goto out;
+ }
+ case SSL_ERROR_SSL: {
+ /* A failure in the SSL library occurred, usually a protocol error.
+ The OpenSSL error queue contains more information on the error. */
+ struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next);
+ struct ssl_connect_data *connssl_next = cf_ssl_next?
+ cf_ssl_next->ctx : NULL;
+ sslerror = ERR_get_error();
+ if(ERR_GET_LIB(sslerror) == ERR_LIB_SSL &&
+ ERR_GET_REASON(sslerror) == SSL_R_BIO_NOT_SET &&
+ connssl->state == ssl_connection_complete &&
+ (connssl_next && connssl_next->state == ssl_connection_complete)
+ ) {
+ char ver[120];
+ (void)ossl_version(ver, sizeof(ver));
+ failf(data, "Error: %s does not support double SSL tunneling.", ver);
+ }
+ else
+ failf(data, "SSL_write() error: %s",
+ ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)));
+ *curlcode = CURLE_SEND_ERROR;
+ rc = -1;
+ goto out;
+ }
+ default:
+ /* a true error */
+ failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d",
+ SSL_ERROR_to_str(err), SOCKERRNO);
+ *curlcode = CURLE_SEND_ERROR;
+ rc = -1;
+ goto out;
+ }
+ }
+ *curlcode = CURLE_OK;
+
+out:
+ return (ssize_t)rc; /* number of bytes */
+}
+
+static ssize_t ossl_recv(struct Curl_cfilter *cf,
+ struct Curl_easy *data, /* transfer */
+ char *buf, /* store read data here */
+ size_t buffersize, /* max amount to read */
+ CURLcode *curlcode)
+{
+ char error_buffer[256];
+ unsigned long sslerror;
+ ssize_t nread;
+ int buffsize;
+ struct connectdata *conn = cf->conn;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+
+ (void)data;
+ DEBUGASSERT(backend);
+
+ ERR_clear_error();
+
+ buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
+ set_logger(connssl, data);
+ nread = (ssize_t)SSL_read(backend->handle, buf, buffsize);
+
+ if(nread <= 0) {
+ /* failed SSL_read */
+ int err = SSL_get_error(backend->handle, (int)nread);
+
+ switch(err) {
+ case SSL_ERROR_NONE: /* this is not an error */
+ break;
+ case SSL_ERROR_ZERO_RETURN: /* no more data */
+ /* close_notify alert */
+ if(cf->sockindex == FIRSTSOCKET)
+ /* mark the connection for close if it is indeed the control
+ connection */
+ connclose(conn, "TLS close_notify");
+ break;
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ /* there's data pending, re-invoke SSL_read() */
+ *curlcode = CURLE_AGAIN;
+ nread = -1;
+ goto out;
+ default:
+ /* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return
+ value/errno" */
+ /* https://www.openssl.org/docs/crypto/ERR_get_error.html */
+ if(backend->io_result == CURLE_AGAIN) {
+ *curlcode = CURLE_AGAIN;
+ nread = -1;
+ goto out;
+ }
+ sslerror = ERR_get_error();
+ if((nread < 0) || sslerror) {
+ /* If the return code was negative or there actually is an error in the
+ queue */
+ int sockerr = SOCKERRNO;
+ if(sslerror)
+ ossl_strerror(sslerror, error_buffer, sizeof(error_buffer));
+ else if(sockerr && err == SSL_ERROR_SYSCALL)
+ Curl_strerror(sockerr, error_buffer, sizeof(error_buffer));
+ else {
+ strncpy(error_buffer, SSL_ERROR_to_str(err), sizeof(error_buffer));
+ error_buffer[sizeof(error_buffer) - 1] = '\0';
+ }
+ failf(data, OSSL_PACKAGE " SSL_read: %s, errno %d",
+ error_buffer, sockerr);
+ *curlcode = CURLE_RECV_ERROR;
+ nread = -1;
+ goto out;
+ }
+ /* For debug builds be a little stricter and error on any
+ SSL_ERROR_SYSCALL. For example a server may have closed the connection
+ abruptly without a close_notify alert. For compatibility with older
+ peers we don't do this by default. #4624
+
+ We can use this to gauge how many users may be affected, and
+ if it goes ok eventually transition to allow in dev and release with
+ the newest OpenSSL: #if (OPENSSL_VERSION_NUMBER >= 0x10101000L) */
+#ifdef DEBUGBUILD
+ if(err == SSL_ERROR_SYSCALL) {
+ int sockerr = SOCKERRNO;
+ if(sockerr)
+ Curl_strerror(sockerr, error_buffer, sizeof(error_buffer));
+ else {
+ msnprintf(error_buffer, sizeof(error_buffer),
+ "Connection closed abruptly");
+ }
+ failf(data, OSSL_PACKAGE " SSL_read: %s, errno %d"
+ " (Fatal because this is a curl debug build)",
+ error_buffer, sockerr);
+ *curlcode = CURLE_RECV_ERROR;
+ nread = -1;
+ goto out;
+ }
+#endif
+ }
+ }
+
+out:
+ return nread;
+}
+
+static size_t ossl_version(char *buffer, size_t size)
+{
+#ifdef LIBRESSL_VERSION_NUMBER
+#ifdef HAVE_OPENSSL_VERSION
+ char *p;
+ int count;
+ const char *ver = OpenSSL_version(OPENSSL_VERSION);
+ const char expected[] = OSSL_PACKAGE " "; /* ie "LibreSSL " */
+ if(strncasecompare(ver, expected, sizeof(expected) - 1)) {
+ ver += sizeof(expected) - 1;
+ }
+ count = msnprintf(buffer, size, "%s/%s", OSSL_PACKAGE, ver);
+ for(p = buffer; *p; ++p) {
+ if(ISBLANK(*p))
+ *p = '_';
+ }
+ return count;
+#else
+ return msnprintf(buffer, size, "%s/%lx.%lx.%lx",
+ OSSL_PACKAGE,
+ (LIBRESSL_VERSION_NUMBER>>28)&0xf,
+ (LIBRESSL_VERSION_NUMBER>>20)&0xff,
+ (LIBRESSL_VERSION_NUMBER>>12)&0xff);
+#endif
+#elif defined(OPENSSL_IS_BORINGSSL)
+#ifdef CURL_BORINGSSL_VERSION
+ return msnprintf(buffer, size, "%s/%s",
+ OSSL_PACKAGE,
+ CURL_BORINGSSL_VERSION);
+#else
+ return msnprintf(buffer, size, OSSL_PACKAGE);
+#endif
+#elif defined(HAVE_OPENSSL_VERSION) && defined(OPENSSL_VERSION_STRING)
+ return msnprintf(buffer, size, "%s/%s",
+ OSSL_PACKAGE, OpenSSL_version(OPENSSL_VERSION_STRING));
+#else
+ /* not LibreSSL, BoringSSL and not using OpenSSL_version */
+
+ char sub[3];
+ unsigned long ssleay_value;
+ sub[2]='\0';
+ sub[1]='\0';
+ ssleay_value = OpenSSL_version_num();
+ if(ssleay_value < 0x906000) {
+ ssleay_value = SSLEAY_VERSION_NUMBER;
+ sub[0]='\0';
+ }
+ else {
+ if(ssleay_value&0xff0) {
+ int minor_ver = (ssleay_value >> 4) & 0xff;
+ if(minor_ver > 26) {
+ /* handle extended version introduced for 0.9.8za */
+ sub[1] = (char) ((minor_ver - 1) % 26 + 'a' + 1);
+ sub[0] = 'z';
+ }
+ else {
+ sub[0] = (char) (minor_ver + 'a' - 1);
+ }
+ }
+ else
+ sub[0]='\0';
+ }
+
+ return msnprintf(buffer, size, "%s/%lx.%lx.%lx%s"
+#ifdef OPENSSL_FIPS
+ "-fips"
+#endif
+ ,
+ OSSL_PACKAGE,
+ (ssleay_value>>28)&0xf,
+ (ssleay_value>>20)&0xff,
+ (ssleay_value>>12)&0xff,
+ sub);
+#endif /* OPENSSL_IS_BORINGSSL */
+}
+
+/* can be called with data == NULL */
+static CURLcode ossl_random(struct Curl_easy *data,
+ unsigned char *entropy, size_t length)
+{
+ int rc;
+ if(data) {
+ if(ossl_seed(data)) /* Initiate the seed if not already done */
+ return CURLE_FAILED_INIT; /* couldn't seed for some reason */
+ }
+ else {
+ if(!rand_enough())
+ return CURLE_FAILED_INIT;
+ }
+ /* RAND_bytes() returns 1 on success, 0 otherwise. */
+ rc = RAND_bytes(entropy, curlx_uztosi(length));
+ return (rc == 1 ? CURLE_OK : CURLE_FAILED_INIT);
+}
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
+static CURLcode ossl_sha256sum(const unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *sha256sum /* output */,
+ size_t unused)
+{
+ EVP_MD_CTX *mdctx;
+ unsigned int len = 0;
+ (void) unused;
+
+ mdctx = EVP_MD_CTX_create();
+ if(!mdctx)
+ return CURLE_OUT_OF_MEMORY;
+ EVP_DigestInit(mdctx, EVP_sha256());
+ EVP_DigestUpdate(mdctx, tmp, tmplen);
+ EVP_DigestFinal_ex(mdctx, sha256sum, &len);
+ EVP_MD_CTX_destroy(mdctx);
+ return CURLE_OK;
+}
+#endif
+
+static bool ossl_cert_status_request(void)
+{
+#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
+ !defined(OPENSSL_NO_OCSP)
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+static void *ossl_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info)
+{
+ /* Legacy: CURLINFO_TLS_SESSION must return an SSL_CTX pointer. */
+ struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
+ return info == CURLINFO_TLS_SESSION ?
+ (void *)backend->ctx : (void *)backend->handle;
+}
+
+static bool ossl_attach_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ const struct ssl_config_data *config;
+
+ DEBUGASSERT(backend);
+
+ /* If we don't have SSL context, do nothing. */
+ if(!backend->handle)
+ return FALSE;
+
+ config = Curl_ssl_cf_get_config(cf, data);
+ if(config->primary.sessionid) {
+ int data_idx = ossl_get_ssl_data_index();
+ int cf_idx = ossl_get_ssl_cf_index();
+ int sockindex_idx = ossl_get_ssl_sockindex_index();
+ int proxy_idx = ossl_get_proxy_index();
+
+ if(data_idx >= 0 && cf_idx >= 0 && sockindex_idx >= 0 &&
+ proxy_idx >= 0) {
+ int data_status, cf_status, sockindex_status, proxy_status;
+
+ /* Store the data needed for the "new session" callback.
+ * The sockindex is stored as a pointer to an array element. */
+ data_status = SSL_set_ex_data(backend->handle, data_idx, data);
+ cf_status = SSL_set_ex_data(backend->handle, cf_idx, cf);
+ sockindex_status = SSL_set_ex_data(backend->handle, sockindex_idx,
+ cf->conn->sock + cf->sockindex);
+#ifndef CURL_DISABLE_PROXY
+ proxy_status = SSL_set_ex_data(backend->handle, proxy_idx,
+ Curl_ssl_cf_is_proxy(cf)?
+ (void *) 1 : NULL);
+#else
+ proxy_status = SSL_set_ex_data(backend->handle, proxy_idx, NULL);
+#endif
+ if(data_status && cf_status && sockindex_status && proxy_status)
+ return TRUE;
+ }
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * Starting with TLS 1.3, the ossl_new_session_cb callback gets called after
+ * the handshake. If the transfer that sets up the callback gets killed before
+ * this callback arrives, we must make sure to properly clear the data to
+ * avoid UAF problems. A future optimization could be to instead store another
+ * transfer that might still be using the same connection.
+ */
+
+static void ossl_detach_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
+
+ /* If we don't have SSL context, do nothing. */
+ if(!backend->handle)
+ return;
+
+ if(ssl_config->primary.sessionid) {
+ int data_idx = ossl_get_ssl_data_index();
+ int cf_idx = ossl_get_ssl_cf_index();
+ int sockindex_idx = ossl_get_ssl_sockindex_index();
+ int proxy_idx = ossl_get_proxy_index();
+
+ if(data_idx >= 0 && cf_idx >= 0 && sockindex_idx >= 0 &&
+ proxy_idx >= 0) {
+ /* Disable references to data in "new session" callback to avoid
+ * accessing a stale pointer. */
+ SSL_set_ex_data(backend->handle, data_idx, NULL);
+ SSL_set_ex_data(backend->handle, cf_idx, NULL);
+ SSL_set_ex_data(backend->handle, sockindex_idx, NULL);
+ SSL_set_ex_data(backend->handle, proxy_idx, NULL);
+ }
+ }
+}
+
+static void ossl_free_multi_ssl_backend_data(
+ struct multi_ssl_backend_data *mbackend)
+{
+#if defined(HAVE_SSL_X509_STORE_SHARE)
+ if(mbackend->store) {
+ X509_STORE_free(mbackend->store);
+ }
+ free(mbackend->CAfile);
+ free(mbackend);
+#else /* HAVE_SSL_X509_STORE_SHARE */
+ (void)mbackend;
+#endif /* HAVE_SSL_X509_STORE_SHARE */
+}
+
+const struct Curl_ssl Curl_ssl_openssl = {
+ { CURLSSLBACKEND_OPENSSL, "openssl" }, /* info */
+
+ SSLSUPP_CA_PATH |
+ SSLSUPP_CAINFO_BLOB |
+ SSLSUPP_CERTINFO |
+ SSLSUPP_PINNEDPUBKEY |
+ SSLSUPP_SSL_CTX |
+#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
+ SSLSUPP_TLS13_CIPHERSUITES |
+#endif
+ SSLSUPP_HTTPS_PROXY,
+
+ sizeof(struct ssl_backend_data),
+
+ ossl_init, /* init */
+ ossl_cleanup, /* cleanup */
+ ossl_version, /* version */
+ ossl_check_cxn, /* check_cxn */
+ ossl_shutdown, /* shutdown */
+ ossl_data_pending, /* data_pending */
+ ossl_random, /* random */
+ ossl_cert_status_request, /* cert_status_request */
+ ossl_connect, /* connect */
+ ossl_connect_nonblocking, /* connect_nonblocking */
+ Curl_ssl_get_select_socks,/* getsock */
+ ossl_get_internals, /* get_internals */
+ ossl_close, /* close_one */
+ ossl_close_all, /* close_all */
+ ossl_session_free, /* session_free */
+ ossl_set_engine, /* set_engine */
+ ossl_set_engine_default, /* set_engine_default */
+ ossl_engines_list, /* engines_list */
+ Curl_none_false_start, /* false_start */
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
+ ossl_sha256sum, /* sha256sum */
+#else
+ NULL, /* sha256sum */
+#endif
+ ossl_attach_data, /* use of data in this connection */
+ ossl_detach_data, /* remote of data from this connection */
+ ossl_free_multi_ssl_backend_data, /* free_multi_ssl_backend_data */
+ ossl_recv, /* recv decrypted data */
+ ossl_send, /* send data to encrypt */
+};
+
+#endif /* USE_OPENSSL */
diff --git a/libs/libcurl/src/vtls/openssl.h b/libs/libcurl/src/vtls/openssl.h index 9df4ecddba..d8cd5bee4a 100644 --- a/libs/libcurl/src/vtls/openssl.h +++ b/libs/libcurl/src/vtls/openssl.h @@ -1,60 +1,60 @@ -#ifndef HEADER_CURL_SSLUSE_H -#define HEADER_CURL_SSLUSE_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_OPENSSL -/* - * This header should only be needed to get included by vtls.c, openssl.c - * and ngtcp2.c - */ -#include <openssl/ssl.h> - -#include "urldata.h" - -/* - * In an effort to avoid using 'X509 *' here, we instead use the struct - * x509_st version of the type so that we can forward-declare it here without - * having to include <openssl/x509v3.h>. Including that header causes name - * conflicts when libcurl is built with both Schannel and OpenSSL support. - */ -struct x509_st; -CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - struct x509_st *server_cert); -extern const struct Curl_ssl Curl_ssl_openssl; - -struct ssl_ctx_st; -CURLcode Curl_ossl_set_client_cert(struct Curl_easy *data, - struct ssl_ctx_st *ctx, char *cert_file, - const struct curl_blob *cert_blob, - const char *cert_type, char *key_file, - const struct curl_blob *key_blob, - const char *key_type, char *key_passwd); - -CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl); - -#endif /* USE_OPENSSL */ -#endif /* HEADER_CURL_SSLUSE_H */ +#ifndef HEADER_CURL_SSLUSE_H
+#define HEADER_CURL_SSLUSE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_OPENSSL
+/*
+ * This header should only be needed to get included by vtls.c, openssl.c
+ * and ngtcp2.c
+ */
+#include <openssl/ssl.h>
+
+#include "urldata.h"
+
+/*
+ * In an effort to avoid using 'X509 *' here, we instead use the struct
+ * x509_st version of the type so that we can forward-declare it here without
+ * having to include <openssl/x509v3.h>. Including that header causes name
+ * conflicts when libcurl is built with both Schannel and OpenSSL support.
+ */
+struct x509_st;
+CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
+ struct x509_st *server_cert);
+extern const struct Curl_ssl Curl_ssl_openssl;
+
+struct ssl_ctx_st;
+CURLcode Curl_ossl_set_client_cert(struct Curl_easy *data,
+ struct ssl_ctx_st *ctx, char *cert_file,
+ const struct curl_blob *cert_blob,
+ const char *cert_type, char *key_file,
+ const struct curl_blob *key_blob,
+ const char *key_type, char *key_passwd);
+
+CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl);
+
+#endif /* USE_OPENSSL */
+#endif /* HEADER_CURL_SSLUSE_H */
diff --git a/libs/libcurl/src/vtls/rustls.c b/libs/libcurl/src/vtls/rustls.c index 77a49f1ab4..49b5869ac2 100644 --- a/libs/libcurl/src/vtls/rustls.c +++ b/libs/libcurl/src/vtls/rustls.c @@ -1,636 +1,671 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2020 - 2022, Jacob Hoffman-Andrews, - * <github@hoffman-andrews.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_RUSTLS - -#include "curl_printf.h" - -#include <errno.h> -#include <rustls.h> - -#include "inet_pton.h" -#include "urldata.h" -#include "sendf.h" -#include "vtls.h" -#include "select.h" -#include "strerror.h" -#include "multiif.h" - -struct ssl_backend_data -{ - const struct rustls_client_config *config; - struct rustls_connection *conn; - bool data_pending; -}; - -/* For a given rustls_result error code, return the best-matching CURLcode. */ -static CURLcode map_error(rustls_result r) -{ - if(rustls_result_is_cert_error(r)) { - return CURLE_PEER_FAILED_VERIFICATION; - } - switch(r) { - case RUSTLS_RESULT_OK: - return CURLE_OK; - case RUSTLS_RESULT_NULL_PARAMETER: - return CURLE_BAD_FUNCTION_ARGUMENT; - default: - return CURLE_READ_ERROR; - } -} - -static bool -cr_data_pending(const struct connectdata *conn, int sockindex) -{ - const struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - DEBUGASSERT(backend); - return backend->data_pending; -} - -static CURLcode -cr_connect(struct Curl_easy *data UNUSED_PARAM, - struct connectdata *conn UNUSED_PARAM, - int sockindex UNUSED_PARAM) -{ - infof(data, "rustls_connect: unimplemented"); - return CURLE_SSL_CONNECT_ERROR; -} - -static int -read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n) -{ - ssize_t n = sread(*(int *)userdata, buf, len); - if(n < 0) { - return SOCKERRNO; - } - *out_n = n; - return 0; -} - -static int -write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n) -{ - ssize_t n = swrite(*(int *)userdata, buf, len); - if(n < 0) { - return SOCKERRNO; - } - *out_n = n; - return 0; -} - -/* - * On each run: - * - Read a chunk of bytes from the socket into rustls' TLS input buffer. - * - Tell rustls to process any new packets. - * - Read out as many plaintext bytes from rustls as possible, until hitting - * error, EOF, or EAGAIN/EWOULDBLOCK, or plainbuf/plainlen is filled up. - * - * It's okay to call this function with plainbuf == NULL and plainlen == 0. - * In that case, it will copy bytes from the socket into rustls' TLS input - * buffer, and process packets, but won't consume bytes from rustls' plaintext - * output buffer. - */ -static ssize_t -cr_recv(struct Curl_easy *data, int sockindex, - char *plainbuf, size_t plainlen, CURLcode *err) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *const connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *const backend = connssl->backend; - struct rustls_connection *rconn = NULL; - - size_t n = 0; - size_t tls_bytes_read = 0; - size_t plain_bytes_copied = 0; - rustls_result rresult = 0; - char errorbuf[255]; - rustls_io_result io_error; - - DEBUGASSERT(backend); - rconn = backend->conn; - - io_error = rustls_connection_read_tls(rconn, read_cb, - &conn->sock[sockindex], &tls_bytes_read); - if(io_error == EAGAIN || io_error == EWOULDBLOCK) { - infof(data, "sread: EAGAIN or EWOULDBLOCK"); - } - else if(io_error) { - char buffer[STRERROR_LEN]; - failf(data, "reading from socket: %s", - Curl_strerror(io_error, buffer, sizeof(buffer))); - *err = CURLE_READ_ERROR; - return -1; - } - - infof(data, "cr_recv read %ld bytes from the network", tls_bytes_read); - - rresult = rustls_connection_process_new_packets(rconn); - if(rresult != RUSTLS_RESULT_OK) { - rustls_error(rresult, errorbuf, sizeof(errorbuf), &n); - failf(data, "%.*s", n, errorbuf); - *err = map_error(rresult); - return -1; - } - - backend->data_pending = TRUE; - - while(plain_bytes_copied < plainlen) { - rresult = rustls_connection_read(rconn, - (uint8_t *)plainbuf + plain_bytes_copied, - plainlen - plain_bytes_copied, - &n); - if(rresult == RUSTLS_RESULT_PLAINTEXT_EMPTY) { - infof(data, "cr_recv got PLAINTEXT_EMPTY. will try again later."); - backend->data_pending = FALSE; - break; - } - else if(rresult != RUSTLS_RESULT_OK) { - /* n always equals 0 in this case, don't need to check it */ - failf(data, "error in rustls_connection_read: %d", rresult); - *err = CURLE_READ_ERROR; - return -1; - } - else if(n == 0) { - /* n == 0 indicates clean EOF, but we may have read some other - plaintext bytes before we reached this. Break out of the loop - so we can figure out whether to return success or EOF. */ - break; - } - else { - infof(data, "cr_recv copied out %ld bytes of plaintext", n); - plain_bytes_copied += n; - } - } - - if(plain_bytes_copied) { - *err = CURLE_OK; - return plain_bytes_copied; - } - - /* If we wrote out 0 plaintext bytes, that means either we hit a clean EOF, - OR we got a RUSTLS_RESULT_PLAINTEXT_EMPTY. - If the latter, return CURLE_AGAIN so curl doesn't treat this as EOF. */ - if(!backend->data_pending) { - *err = CURLE_AGAIN; - return -1; - } - - /* Zero bytes read, and no RUSTLS_RESULT_PLAINTEXT_EMPTY, means the TCP - connection was cleanly closed (with a close_notify alert). */ - *err = CURLE_OK; - return 0; -} - -/* - * On each call: - * - Copy `plainlen` bytes into rustls' plaintext input buffer (if > 0). - * - Fully drain rustls' plaintext output buffer into the socket until - * we get either an error or EAGAIN/EWOULDBLOCK. - * - * It's okay to call this function with plainbuf == NULL and plainlen == 0. - * In that case, it won't read anything into rustls' plaintext input buffer. - * It will only drain rustls' plaintext output buffer into the socket. - */ -static ssize_t -cr_send(struct Curl_easy *data, int sockindex, - const void *plainbuf, size_t plainlen, CURLcode *err) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *const connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *const backend = connssl->backend; - struct rustls_connection *rconn = NULL; - size_t plainwritten = 0; - size_t tlswritten = 0; - size_t tlswritten_total = 0; - rustls_result rresult; - rustls_io_result io_error; - - DEBUGASSERT(backend); - rconn = backend->conn; - - infof(data, "cr_send %ld bytes of plaintext", plainlen); - - if(plainlen > 0) { - rresult = rustls_connection_write(rconn, plainbuf, plainlen, - &plainwritten); - if(rresult != RUSTLS_RESULT_OK) { - failf(data, "error in rustls_connection_write"); - *err = CURLE_WRITE_ERROR; - return -1; - } - else if(plainwritten == 0) { - failf(data, "EOF in rustls_connection_write"); - *err = CURLE_WRITE_ERROR; - return -1; - } - } - - while(rustls_connection_wants_write(rconn)) { - io_error = rustls_connection_write_tls(rconn, write_cb, - &conn->sock[sockindex], &tlswritten); - if(io_error == EAGAIN || io_error == EWOULDBLOCK) { - infof(data, "swrite: EAGAIN after %ld bytes", tlswritten_total); - *err = CURLE_AGAIN; - return -1; - } - else if(io_error) { - char buffer[STRERROR_LEN]; - failf(data, "writing to socket: %s", - Curl_strerror(io_error, buffer, sizeof(buffer))); - *err = CURLE_WRITE_ERROR; - return -1; - } - if(tlswritten == 0) { - failf(data, "EOF in swrite"); - *err = CURLE_WRITE_ERROR; - return -1; - } - infof(data, "cr_send wrote %ld bytes to network", tlswritten); - tlswritten_total += tlswritten; - } - - return plainwritten; -} - -/* A server certificate verify callback for rustls that always returns - RUSTLS_RESULT_OK, or in other words disable certificate verification. */ -static enum rustls_result -cr_verify_none(void *userdata UNUSED_PARAM, - const rustls_verify_server_cert_params *params UNUSED_PARAM) -{ - return RUSTLS_RESULT_OK; -} - -static bool -cr_hostname_is_ip(const char *hostname) -{ - struct in_addr in; -#ifdef ENABLE_IPV6 - struct in6_addr in6; - if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) { - return true; - } -#endif /* ENABLE_IPV6 */ - if(Curl_inet_pton(AF_INET, hostname, &in) > 0) { - return true; - } - return false; -} - -static CURLcode -cr_init_backend(struct Curl_easy *data, struct connectdata *conn, - struct ssl_backend_data *const backend) -{ - struct rustls_connection *rconn = NULL; - struct rustls_client_config_builder *config_builder = NULL; - struct rustls_root_cert_store *roots = NULL; - const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob); - const char * const ssl_cafile = - /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ - (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile)); - const bool verifypeer = SSL_CONN_CONFIG(verifypeer); - const char *hostname = conn->host.name; - char errorbuf[256]; - size_t errorlen; - int result; - rustls_slice_bytes alpn[2] = { - { (const uint8_t *)ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH }, - { (const uint8_t *)ALPN_H2, ALPN_H2_LENGTH }, - }; - - DEBUGASSERT(backend); - rconn = backend->conn; - - config_builder = rustls_client_config_builder_new(); -#ifdef USE_HTTP2 - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); - rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, 2); -#else - rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, 1); -#endif - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); - if(!verifypeer) { - rustls_client_config_builder_dangerous_set_certificate_verifier( - config_builder, cr_verify_none); - /* rustls doesn't support IP addresses (as of 0.19.0), and will reject - * connections created with an IP address, even when certificate - * verification is turned off. Set a placeholder hostname and disable - * SNI. */ - if(cr_hostname_is_ip(hostname)) { - rustls_client_config_builder_set_enable_sni(config_builder, false); - hostname = "example.invalid"; - } - } - else if(ca_info_blob) { - roots = rustls_root_cert_store_new(); - - /* Enable strict parsing only if verification isn't disabled. */ - result = rustls_root_cert_store_add_pem(roots, ca_info_blob->data, - ca_info_blob->len, verifypeer); - if(result != RUSTLS_RESULT_OK) { - failf(data, "failed to parse trusted certificates from blob"); - rustls_root_cert_store_free(roots); - rustls_client_config_free( - rustls_client_config_builder_build(config_builder)); - return CURLE_SSL_CACERT_BADFILE; - } - - result = rustls_client_config_builder_use_roots(config_builder, roots); - rustls_root_cert_store_free(roots); - if(result != RUSTLS_RESULT_OK) { - failf(data, "failed to load trusted certificates"); - rustls_client_config_free( - rustls_client_config_builder_build(config_builder)); - return CURLE_SSL_CACERT_BADFILE; - } - } - else if(ssl_cafile) { - result = rustls_client_config_builder_load_roots_from_file( - config_builder, ssl_cafile); - if(result != RUSTLS_RESULT_OK) { - failf(data, "failed to load trusted certificates"); - rustls_client_config_free( - rustls_client_config_builder_build(config_builder)); - return CURLE_SSL_CACERT_BADFILE; - } - } - - backend->config = rustls_client_config_builder_build(config_builder); - DEBUGASSERT(rconn == NULL); - { - char *snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - result = rustls_client_connection_new(backend->config, snihost, &rconn); - } - if(result != RUSTLS_RESULT_OK) { - rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen); - failf(data, "rustls_client_connection_new: %.*s", errorlen, errorbuf); - return CURLE_COULDNT_CONNECT; - } - rustls_connection_set_userdata(rconn, backend); - backend->conn = rconn; - return CURLE_OK; -} - -static void -cr_set_negotiated_alpn(struct Curl_easy *data, struct connectdata *conn, - const struct rustls_connection *rconn) -{ - const uint8_t *protocol = NULL; - size_t len = 0; - - rustls_connection_get_alpn_protocol(rconn, &protocol, &len); - if(!protocol) { - infof(data, VTLS_INFOF_NO_ALPN); - return; - } - -#ifdef USE_HTTP2 - if(len == ALPN_H2_LENGTH && 0 == memcmp(ALPN_H2, protocol, len)) { - infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, ALPN_H2); - conn->alpn = CURL_HTTP_VERSION_2; - } - else -#endif - if(len == ALPN_HTTP_1_1_LENGTH && - 0 == memcmp(ALPN_HTTP_1_1, protocol, len)) { - infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, ALPN_HTTP_1_1); - conn->alpn = CURL_HTTP_VERSION_1_1; - } - else { - infof(data, "ALPN, negotiated an unrecognized protocol"); - } - - Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); -} - -static CURLcode -cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn, - int sockindex, bool *done) -{ - struct ssl_connect_data *const connssl = &conn->ssl[sockindex]; - curl_socket_t sockfd = conn->sock[sockindex]; - struct ssl_backend_data *const backend = connssl->backend; - struct rustls_connection *rconn = NULL; - CURLcode tmperr = CURLE_OK; - int result; - int what; - bool wants_read; - bool wants_write; - curl_socket_t writefd; - curl_socket_t readfd; - - DEBUGASSERT(backend); - - if(ssl_connection_none == connssl->state) { - result = cr_init_backend(data, conn, connssl->backend); - if(result != CURLE_OK) { - return result; - } - connssl->state = ssl_connection_negotiating; - } - - rconn = backend->conn; - - /* Read/write data until the handshake is done or the socket would block. */ - for(;;) { - /* - * Connection has been established according to rustls. Set send/recv - * handlers, and update the state machine. - */ - if(!rustls_connection_is_handshaking(rconn)) { - infof(data, "Done handshaking"); - /* Done with the handshake. Set up callbacks to send/receive data. */ - connssl->state = ssl_connection_complete; - - cr_set_negotiated_alpn(data, conn, rconn); - - conn->recv[sockindex] = cr_recv; - conn->send[sockindex] = cr_send; - *done = TRUE; - return CURLE_OK; - } - - wants_read = rustls_connection_wants_read(rconn); - wants_write = rustls_connection_wants_write(rconn); - DEBUGASSERT(wants_read || wants_write); - writefd = wants_write?sockfd:CURL_SOCKET_BAD; - readfd = wants_read?sockfd:CURL_SOCKET_BAD; - - what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, 0); - if(what < 0) { - /* fatal error */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - return CURLE_SSL_CONNECT_ERROR; - } - if(0 == what) { - infof(data, "Curl_socket_check: %s would block", - wants_read&&wants_write ? "writing and reading" : - wants_write ? "writing" : "reading"); - *done = FALSE; - return CURLE_OK; - } - /* socket is readable or writable */ - - if(wants_write) { - infof(data, "rustls_connection wants us to write_tls."); - cr_send(data, sockindex, NULL, 0, &tmperr); - if(tmperr == CURLE_AGAIN) { - infof(data, "writing would block"); - /* fall through */ - } - else if(tmperr != CURLE_OK) { - return tmperr; - } - } - - if(wants_read) { - infof(data, "rustls_connection wants us to read_tls."); - - cr_recv(data, sockindex, NULL, 0, &tmperr); - if(tmperr == CURLE_AGAIN) { - infof(data, "reading would block"); - /* fall through */ - } - else if(tmperr != CURLE_OK) { - if(tmperr == CURLE_READ_ERROR) { - return CURLE_SSL_CONNECT_ERROR; - } - else { - return tmperr; - } - } - } - } - - /* We should never fall through the loop. We should return either because - the handshake is done or because we can't read/write without blocking. */ - DEBUGASSERT(false); -} - -/* returns a bitmap of flags for this connection's first socket indicating - whether we want to read or write */ -static int -cr_getsock(struct connectdata *conn, curl_socket_t *socks) -{ - struct ssl_connect_data *const connssl = &conn->ssl[FIRSTSOCKET]; - curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; - struct ssl_backend_data *const backend = connssl->backend; - struct rustls_connection *rconn = NULL; - - DEBUGASSERT(backend); - rconn = backend->conn; - - if(rustls_connection_wants_write(rconn)) { - socks[0] = sockfd; - return GETSOCK_WRITESOCK(0); - } - if(rustls_connection_wants_read(rconn)) { - socks[0] = sockfd; - return GETSOCK_READSOCK(0); - } - - return GETSOCK_BLANK; -} - -static void * -cr_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - struct ssl_backend_data *backend = connssl->backend; - DEBUGASSERT(backend); - return &backend->conn; -} - -static void -cr_close(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - CURLcode tmperr = CURLE_OK; - ssize_t n = 0; - - DEBUGASSERT(backend); - - if(backend->conn) { - rustls_connection_send_close_notify(backend->conn); - n = cr_send(data, sockindex, NULL, 0, &tmperr); - if(n < 0) { - failf(data, "error sending close notify: %d", tmperr); - } - - rustls_connection_free(backend->conn); - backend->conn = NULL; - } - if(backend->config) { - rustls_client_config_free(backend->config); - backend->config = NULL; - } -} - -static size_t cr_version(char *buffer, size_t size) -{ - struct rustls_str ver = rustls_version(); - return msnprintf(buffer, size, "%.*s", (int)ver.len, ver.data); -} - -const struct Curl_ssl Curl_ssl_rustls = { - { CURLSSLBACKEND_RUSTLS, "rustls" }, - SSLSUPP_CAINFO_BLOB | /* supports */ - SSLSUPP_TLS13_CIPHERSUITES, - sizeof(struct ssl_backend_data), - - Curl_none_init, /* init */ - Curl_none_cleanup, /* cleanup */ - cr_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ - Curl_none_shutdown, /* shutdown */ - cr_data_pending, /* data_pending */ - Curl_none_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ - cr_connect, /* connect */ - cr_connect_nonblocking, /* connect_nonblocking */ - cr_getsock, /* cr_getsock */ - cr_get_internals, /* get_internals */ - cr_close, /* close_one */ - Curl_none_close_all, /* close_all */ - Curl_none_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ - NULL, /* sha256sum */ - NULL, /* associate_connection */ - NULL /* disassociate_connection */ -}; - -#endif /* USE_RUSTLS */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2020 - 2022, Jacob Hoffman-Andrews,
+ * <github@hoffman-andrews.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_RUSTLS
+
+#include "curl_printf.h"
+
+#include <errno.h>
+#include <rustls.h>
+
+#include "inet_pton.h"
+#include "urldata.h"
+#include "sendf.h"
+#include "vtls.h"
+#include "vtls_int.h"
+#include "select.h"
+#include "strerror.h"
+#include "multiif.h"
+
+struct ssl_backend_data
+{
+ const struct rustls_client_config *config;
+ struct rustls_connection *conn;
+ bool data_pending;
+};
+
+/* For a given rustls_result error code, return the best-matching CURLcode. */
+static CURLcode map_error(rustls_result r)
+{
+ if(rustls_result_is_cert_error(r)) {
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ switch(r) {
+ case RUSTLS_RESULT_OK:
+ return CURLE_OK;
+ case RUSTLS_RESULT_NULL_PARAMETER:
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ default:
+ return CURLE_READ_ERROR;
+ }
+}
+
+static bool
+cr_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data)
+{
+ struct ssl_connect_data *ctx = cf->ctx;
+
+ (void)data;
+ DEBUGASSERT(ctx && ctx->backend);
+ return ctx->backend->data_pending;
+}
+
+static CURLcode
+cr_connect(struct Curl_cfilter *cf UNUSED_PARAM,
+ struct Curl_easy *data UNUSED_PARAM)
+{
+ infof(data, "rustls_connect: unimplemented");
+ return CURLE_SSL_CONNECT_ERROR;
+}
+
+struct io_ctx {
+ struct Curl_cfilter *cf;
+ struct Curl_easy *data;
+};
+
+static int
+read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n)
+{
+ struct io_ctx *io_ctx = userdata;
+ CURLcode result;
+ int ret = 0;
+ ssize_t nread = Curl_conn_cf_recv(io_ctx->cf->next, io_ctx->data,
+ (char *)buf, len, &result);
+ if(nread < 0) {
+ nread = 0;
+ if(CURLE_AGAIN == result)
+ ret = EAGAIN;
+ else
+ ret = EINVAL;
+ }
+ *out_n = (int)nread;
+ return ret;
+}
+
+static int
+write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n)
+{
+ struct io_ctx *io_ctx = userdata;
+ CURLcode result;
+ int ret = 0;
+ ssize_t nwritten = Curl_conn_cf_send(io_ctx->cf->next, io_ctx->data,
+ (const char *)buf, len, &result);
+ if(nwritten < 0) {
+ nwritten = 0;
+ if(CURLE_AGAIN == result)
+ ret = EAGAIN;
+ else
+ ret = EINVAL;
+ }
+ *out_n = (int)nwritten;
+ return ret;
+}
+
+/*
+ * On each run:
+ * - Read a chunk of bytes from the socket into rustls' TLS input buffer.
+ * - Tell rustls to process any new packets.
+ * - Read out as many plaintext bytes from rustls as possible, until hitting
+ * error, EOF, or EAGAIN/EWOULDBLOCK, or plainbuf/plainlen is filled up.
+ *
+ * It's okay to call this function with plainbuf == NULL and plainlen == 0.
+ * In that case, it will copy bytes from the socket into rustls' TLS input
+ * buffer, and process packets, but won't consume bytes from rustls' plaintext
+ * output buffer.
+ */
+static ssize_t
+cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *plainbuf, size_t plainlen, CURLcode *err)
+{
+ struct ssl_connect_data *const connssl = cf->ctx;
+ struct ssl_backend_data *const backend = connssl->backend;
+ struct rustls_connection *rconn = NULL;
+ struct io_ctx io_ctx;
+
+ size_t n = 0;
+ size_t tls_bytes_read = 0;
+ size_t plain_bytes_copied = 0;
+ rustls_result rresult = 0;
+ char errorbuf[255];
+ rustls_io_result io_error;
+
+ DEBUGASSERT(backend);
+ rconn = backend->conn;
+
+ io_ctx.cf = cf;
+ io_ctx.data = data;
+
+ io_error = rustls_connection_read_tls(rconn, read_cb, &io_ctx,
+ &tls_bytes_read);
+ if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
+ infof(data, CFMSG(cf, "cr_recv: EAGAIN or EWOULDBLOCK"));
+ }
+ else if(io_error) {
+ char buffer[STRERROR_LEN];
+ failf(data, "reading from socket: %s",
+ Curl_strerror(io_error, buffer, sizeof(buffer)));
+ *err = CURLE_READ_ERROR;
+ return -1;
+ }
+
+ infof(data, CFMSG(cf, "cr_recv: read %ld TLS bytes"), tls_bytes_read);
+
+ rresult = rustls_connection_process_new_packets(rconn);
+ if(rresult != RUSTLS_RESULT_OK) {
+ rustls_error(rresult, errorbuf, sizeof(errorbuf), &n);
+ failf(data, "%.*s", n, errorbuf);
+ *err = map_error(rresult);
+ return -1;
+ }
+
+ backend->data_pending = TRUE;
+
+ while(plain_bytes_copied < plainlen) {
+ rresult = rustls_connection_read(rconn,
+ (uint8_t *)plainbuf + plain_bytes_copied,
+ plainlen - plain_bytes_copied,
+ &n);
+ if(rresult == RUSTLS_RESULT_PLAINTEXT_EMPTY) {
+ infof(data, CFMSG(cf, "cr_recv: got PLAINTEXT_EMPTY. "
+ "will try again later."));
+ backend->data_pending = FALSE;
+ break;
+ }
+ else if(rresult != RUSTLS_RESULT_OK) {
+ /* n always equals 0 in this case, don't need to check it */
+ failf(data, "error in rustls_connection_read: %d", rresult);
+ *err = CURLE_READ_ERROR;
+ return -1;
+ }
+ else if(n == 0) {
+ /* n == 0 indicates clean EOF, but we may have read some other
+ plaintext bytes before we reached this. Break out of the loop
+ so we can figure out whether to return success or EOF. */
+ break;
+ }
+ else {
+ infof(data, CFMSG(cf, "cr_recv: got %ld plain bytes"), n);
+ plain_bytes_copied += n;
+ }
+ }
+
+ if(plain_bytes_copied) {
+ *err = CURLE_OK;
+ return plain_bytes_copied;
+ }
+
+ /* If we wrote out 0 plaintext bytes, that means either we hit a clean EOF,
+ OR we got a RUSTLS_RESULT_PLAINTEXT_EMPTY.
+ If the latter, return CURLE_AGAIN so curl doesn't treat this as EOF. */
+ if(!backend->data_pending) {
+ *err = CURLE_AGAIN;
+ return -1;
+ }
+
+ /* Zero bytes read, and no RUSTLS_RESULT_PLAINTEXT_EMPTY, means the TCP
+ connection was cleanly closed (with a close_notify alert). */
+ *err = CURLE_OK;
+ return 0;
+}
+
+/*
+ * On each call:
+ * - Copy `plainlen` bytes into rustls' plaintext input buffer (if > 0).
+ * - Fully drain rustls' plaintext output buffer into the socket until
+ * we get either an error or EAGAIN/EWOULDBLOCK.
+ *
+ * It's okay to call this function with plainbuf == NULL and plainlen == 0.
+ * In that case, it won't read anything into rustls' plaintext input buffer.
+ * It will only drain rustls' plaintext output buffer into the socket.
+ */
+static ssize_t
+cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *plainbuf, size_t plainlen, CURLcode *err)
+{
+ struct ssl_connect_data *const connssl = cf->ctx;
+ struct ssl_backend_data *const backend = connssl->backend;
+ struct rustls_connection *rconn = NULL;
+ struct io_ctx io_ctx;
+ size_t plainwritten = 0;
+ size_t tlswritten = 0;
+ size_t tlswritten_total = 0;
+ rustls_result rresult;
+ rustls_io_result io_error;
+
+ DEBUGASSERT(backend);
+ rconn = backend->conn;
+
+ infof(data, CFMSG(cf, "cr_send: %ld plain bytes"), plainlen);
+
+ if(plainlen > 0) {
+ rresult = rustls_connection_write(rconn, plainbuf, plainlen,
+ &plainwritten);
+ if(rresult != RUSTLS_RESULT_OK) {
+ failf(data, "error in rustls_connection_write");
+ *err = CURLE_WRITE_ERROR;
+ return -1;
+ }
+ else if(plainwritten == 0) {
+ failf(data, "EOF in rustls_connection_write");
+ *err = CURLE_WRITE_ERROR;
+ return -1;
+ }
+ }
+
+ io_ctx.cf = cf;
+ io_ctx.data = data;
+
+ while(rustls_connection_wants_write(rconn)) {
+ io_error = rustls_connection_write_tls(rconn, write_cb, &io_ctx,
+ &tlswritten);
+ if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
+ infof(data, CFMSG(cf, "cr_send: EAGAIN after %ld bytes"),
+ tlswritten_total);
+ *err = CURLE_AGAIN;
+ return -1;
+ }
+ else if(io_error) {
+ char buffer[STRERROR_LEN];
+ failf(data, "writing to socket: %s",
+ Curl_strerror(io_error, buffer, sizeof(buffer)));
+ *err = CURLE_WRITE_ERROR;
+ return -1;
+ }
+ if(tlswritten == 0) {
+ failf(data, "EOF in swrite");
+ *err = CURLE_WRITE_ERROR;
+ return -1;
+ }
+ infof(data, CFMSG(cf, "cr_send: wrote %ld TLS bytes"), tlswritten);
+ tlswritten_total += tlswritten;
+ }
+
+ return plainwritten;
+}
+
+/* A server certificate verify callback for rustls that always returns
+ RUSTLS_RESULT_OK, or in other words disable certificate verification. */
+static enum rustls_result
+cr_verify_none(void *userdata UNUSED_PARAM,
+ const rustls_verify_server_cert_params *params UNUSED_PARAM)
+{
+ return RUSTLS_RESULT_OK;
+}
+
+static bool
+cr_hostname_is_ip(const char *hostname)
+{
+ struct in_addr in;
+#ifdef ENABLE_IPV6
+ struct in6_addr in6;
+ if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) {
+ return true;
+ }
+#endif /* ENABLE_IPV6 */
+ if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
+ return true;
+ }
+ return false;
+}
+
+static CURLcode
+cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
+ struct ssl_backend_data *const backend)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct rustls_connection *rconn = NULL;
+ struct rustls_client_config_builder *config_builder = NULL;
+ struct rustls_root_cert_store *roots = NULL;
+ const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
+ const char * const ssl_cafile =
+ /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
+ (ca_info_blob ? NULL : conn_config->CAfile);
+ const bool verifypeer = conn_config->verifypeer;
+ const char *hostname = connssl->hostname;
+ char errorbuf[256];
+ size_t errorlen;
+ int result;
+ rustls_slice_bytes alpn[2] = {
+ { (const uint8_t *)ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH },
+ { (const uint8_t *)ALPN_H2, ALPN_H2_LENGTH },
+ };
+
+ DEBUGASSERT(backend);
+ rconn = backend->conn;
+
+ config_builder = rustls_client_config_builder_new();
+#ifdef USE_HTTP2
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
+ rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, 2);
+#else
+ rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, 1);
+#endif
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
+ if(!verifypeer) {
+ rustls_client_config_builder_dangerous_set_certificate_verifier(
+ config_builder, cr_verify_none);
+ /* rustls doesn't support IP addresses (as of 0.19.0), and will reject
+ * connections created with an IP address, even when certificate
+ * verification is turned off. Set a placeholder hostname and disable
+ * SNI. */
+ if(cr_hostname_is_ip(hostname)) {
+ rustls_client_config_builder_set_enable_sni(config_builder, false);
+ hostname = "example.invalid";
+ }
+ }
+ else if(ca_info_blob) {
+ roots = rustls_root_cert_store_new();
+
+ /* Enable strict parsing only if verification isn't disabled. */
+ result = rustls_root_cert_store_add_pem(roots, ca_info_blob->data,
+ ca_info_blob->len, verifypeer);
+ if(result != RUSTLS_RESULT_OK) {
+ failf(data, "failed to parse trusted certificates from blob");
+ rustls_root_cert_store_free(roots);
+ rustls_client_config_free(
+ rustls_client_config_builder_build(config_builder));
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+
+ result = rustls_client_config_builder_use_roots(config_builder, roots);
+ rustls_root_cert_store_free(roots);
+ if(result != RUSTLS_RESULT_OK) {
+ failf(data, "failed to load trusted certificates");
+ rustls_client_config_free(
+ rustls_client_config_builder_build(config_builder));
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ }
+ else if(ssl_cafile) {
+ result = rustls_client_config_builder_load_roots_from_file(
+ config_builder, ssl_cafile);
+ if(result != RUSTLS_RESULT_OK) {
+ failf(data, "failed to load trusted certificates");
+ rustls_client_config_free(
+ rustls_client_config_builder_build(config_builder));
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ }
+
+ backend->config = rustls_client_config_builder_build(config_builder);
+ DEBUGASSERT(rconn == NULL);
+ {
+ char *snihost = Curl_ssl_snihost(data, hostname, NULL);
+ if(!snihost) {
+ failf(data, "Failed to set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ result = rustls_client_connection_new(backend->config, snihost, &rconn);
+ }
+ if(result != RUSTLS_RESULT_OK) {
+ rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen);
+ failf(data, "rustls_client_connection_new: %.*s", errorlen, errorbuf);
+ return CURLE_COULDNT_CONNECT;
+ }
+ rustls_connection_set_userdata(rconn, backend);
+ backend->conn = rconn;
+ return CURLE_OK;
+}
+
+static void
+cr_set_negotiated_alpn(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const struct rustls_connection *rconn)
+{
+ const uint8_t *protocol = NULL;
+ size_t len = 0;
+
+ rustls_connection_get_alpn_protocol(rconn, &protocol, &len);
+ if(!protocol) {
+ infof(data, VTLS_INFOF_NO_ALPN);
+ return;
+ }
+
+#ifdef USE_HTTP2
+ if(len == ALPN_H2_LENGTH && 0 == memcmp(ALPN_H2, protocol, len)) {
+ infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, ALPN_H2);
+ cf->conn->alpn = CURL_HTTP_VERSION_2;
+ }
+ else
+#endif
+ if(len == ALPN_HTTP_1_1_LENGTH &&
+ 0 == memcmp(ALPN_HTTP_1_1, protocol, len)) {
+ infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, ALPN_HTTP_1_1);
+ cf->conn->alpn = CURL_HTTP_VERSION_1_1;
+ }
+ else {
+ infof(data, "ALPN, negotiated an unrecognized protocol");
+ }
+
+ Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+}
+
+static CURLcode
+cr_connect_nonblocking(struct Curl_cfilter *cf,
+ struct Curl_easy *data, bool *done)
+{
+ struct ssl_connect_data *const connssl = cf->ctx;
+ curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ struct ssl_backend_data *const backend = connssl->backend;
+ struct rustls_connection *rconn = NULL;
+ CURLcode tmperr = CURLE_OK;
+ int result;
+ int what;
+ bool wants_read;
+ bool wants_write;
+ curl_socket_t writefd;
+ curl_socket_t readfd;
+
+ DEBUGASSERT(backend);
+
+ if(ssl_connection_none == connssl->state) {
+ result = cr_init_backend(cf, data, connssl->backend);
+ if(result != CURLE_OK) {
+ return result;
+ }
+ connssl->state = ssl_connection_negotiating;
+ }
+
+ rconn = backend->conn;
+
+ /* Read/write data until the handshake is done or the socket would block. */
+ for(;;) {
+ /*
+ * Connection has been established according to rustls. Set send/recv
+ * handlers, and update the state machine.
+ */
+ if(!rustls_connection_is_handshaking(rconn)) {
+ infof(data, "Done handshaking");
+ /* Done with the handshake. Set up callbacks to send/receive data. */
+ connssl->state = ssl_connection_complete;
+
+ cr_set_negotiated_alpn(cf, data, rconn);
+
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ wants_read = rustls_connection_wants_read(rconn);
+ wants_write = rustls_connection_wants_write(rconn);
+ DEBUGASSERT(wants_read || wants_write);
+ writefd = wants_write?sockfd:CURL_SOCKET_BAD;
+ readfd = wants_read?sockfd:CURL_SOCKET_BAD;
+
+ what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, 0);
+ if(what < 0) {
+ /* fatal error */
+ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ if(0 == what) {
+ infof(data, "Curl_socket_check: %s would block",
+ wants_read&&wants_write ? "writing and reading" :
+ wants_write ? "writing" : "reading");
+ *done = FALSE;
+ return CURLE_OK;
+ }
+ /* socket is readable or writable */
+
+ if(wants_write) {
+ infof(data, "rustls_connection wants us to write_tls.");
+ cr_send(cf, data, NULL, 0, &tmperr);
+ if(tmperr == CURLE_AGAIN) {
+ infof(data, "writing would block");
+ /* fall through */
+ }
+ else if(tmperr != CURLE_OK) {
+ return tmperr;
+ }
+ }
+
+ if(wants_read) {
+ infof(data, "rustls_connection wants us to read_tls.");
+
+ cr_recv(cf, data, NULL, 0, &tmperr);
+ if(tmperr == CURLE_AGAIN) {
+ infof(data, "reading would block");
+ /* fall through */
+ }
+ else if(tmperr != CURLE_OK) {
+ if(tmperr == CURLE_READ_ERROR) {
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else {
+ return tmperr;
+ }
+ }
+ }
+ }
+
+ /* We should never fall through the loop. We should return either because
+ the handshake is done or because we can't read/write without blocking. */
+ DEBUGASSERT(false);
+}
+
+/* returns a bitmap of flags for this connection's first socket indicating
+ whether we want to read or write */
+static int
+cr_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ struct ssl_connect_data *const connssl = cf->ctx;
+ curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ struct ssl_backend_data *const backend = connssl->backend;
+ struct rustls_connection *rconn = NULL;
+
+ (void)data;
+ DEBUGASSERT(backend);
+ rconn = backend->conn;
+
+ if(rustls_connection_wants_write(rconn)) {
+ socks[0] = sockfd;
+ return GETSOCK_WRITESOCK(0);
+ }
+ if(rustls_connection_wants_read(rconn)) {
+ socks[0] = sockfd;
+ return GETSOCK_READSOCK(0);
+ }
+
+ return GETSOCK_BLANK;
+}
+
+static void *
+cr_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
+{
+ struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
+ return &backend->conn;
+}
+
+static void
+cr_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ CURLcode tmperr = CURLE_OK;
+ ssize_t n = 0;
+
+ DEBUGASSERT(backend);
+
+ if(backend->conn) {
+ rustls_connection_send_close_notify(backend->conn);
+ n = cr_send(cf, data, NULL, 0, &tmperr);
+ if(n < 0) {
+ failf(data, "error sending close notify: %d", tmperr);
+ }
+
+ rustls_connection_free(backend->conn);
+ backend->conn = NULL;
+ }
+ if(backend->config) {
+ rustls_client_config_free(backend->config);
+ backend->config = NULL;
+ }
+}
+
+static size_t cr_version(char *buffer, size_t size)
+{
+ struct rustls_str ver = rustls_version();
+ return msnprintf(buffer, size, "%.*s", (int)ver.len, ver.data);
+}
+
+const struct Curl_ssl Curl_ssl_rustls = {
+ { CURLSSLBACKEND_RUSTLS, "rustls" },
+ SSLSUPP_CAINFO_BLOB | /* supports */
+ SSLSUPP_TLS13_CIPHERSUITES |
+ SSLSUPP_HTTPS_PROXY,
+ sizeof(struct ssl_backend_data),
+
+ Curl_none_init, /* init */
+ Curl_none_cleanup, /* cleanup */
+ cr_version, /* version */
+ Curl_none_check_cxn, /* check_cxn */
+ Curl_none_shutdown, /* shutdown */
+ cr_data_pending, /* data_pending */
+ Curl_none_random, /* random */
+ Curl_none_cert_status_request, /* cert_status_request */
+ cr_connect, /* connect */
+ cr_connect_nonblocking, /* connect_nonblocking */
+ cr_get_select_socks, /* get_select_socks */
+ cr_get_internals, /* get_internals */
+ cr_close, /* close_one */
+ Curl_none_close_all, /* close_all */
+ Curl_none_session_free, /* session_free */
+ Curl_none_set_engine, /* set_engine */
+ Curl_none_set_engine_default, /* set_engine_default */
+ Curl_none_engines_list, /* engines_list */
+ Curl_none_false_start, /* false_start */
+ NULL, /* sha256sum */
+ NULL, /* associate_connection */
+ NULL, /* disassociate_connection */
+ NULL, /* free_multi_ssl_backend_data */
+ cr_recv, /* recv decrypted data */
+ cr_send, /* send data to encrypt */
+};
+
+#endif /* USE_RUSTLS */
diff --git a/libs/libcurl/src/vtls/rustls.h b/libs/libcurl/src/vtls/rustls.h index 6b393dd639..2739b458bd 100644 --- a/libs/libcurl/src/vtls/rustls.h +++ b/libs/libcurl/src/vtls/rustls.h @@ -1,35 +1,35 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2020 - 2022, Jacob Hoffman-Andrews, - * <github@hoffman-andrews.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 - * - ***************************************************************************/ -#ifndef HEADER_CURL_RUSTLS_H -#define HEADER_CURL_RUSTLS_H - -#include "curl_setup.h" - -#ifdef USE_RUSTLS - -extern const struct Curl_ssl Curl_ssl_rustls; - -#endif /* USE_RUSTLS */ -#endif /* HEADER_CURL_RUSTLS_H */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2020 - 2022, Jacob Hoffman-Andrews,
+ * <github@hoffman-andrews.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
+ *
+ ***************************************************************************/
+#ifndef HEADER_CURL_RUSTLS_H
+#define HEADER_CURL_RUSTLS_H
+
+#include "curl_setup.h"
+
+#ifdef USE_RUSTLS
+
+extern const struct Curl_ssl Curl_ssl_rustls;
+
+#endif /* USE_RUSTLS */
+#endif /* HEADER_CURL_RUSTLS_H */
diff --git a/libs/libcurl/src/vtls/schannel.c b/libs/libcurl/src/vtls/schannel.c index 454eb79674..48dc6651ab 100644 --- a/libs/libcurl/src/vtls/schannel.c +++ b/libs/libcurl/src/vtls/schannel.c @@ -1,2815 +1,2801 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de> - * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.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 - * - ***************************************************************************/ - -/* - * Source file for all Schannel-specific code for the TLS/SSL layer. No code - * but vtls.c should ever call or use these functions. - */ - -#include "curl_setup.h" - -#ifdef USE_SCHANNEL - -#define EXPOSE_SCHANNEL_INTERNAL_STRUCTS - -#ifndef USE_WINDOWS_SSPI -# error "Can't compile SCHANNEL support without SSPI." -#endif - -#include "schannel.h" -#include "vtls.h" -#include "strcase.h" -#include "sendf.h" -#include "connect.h" /* for the connect timeout */ -#include "strerror.h" -#include "select.h" /* for the socket readiness */ -#include "inet_pton.h" /* for IP addr SNI check */ -#include "curl_multibyte.h" -#include "warnless.h" -#include "x509asn1.h" -#include "curl_printf.h" -#include "multiif.h" -#include "version_win32.h" -#include "rand.h" - -/* The last #include file should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* ALPN requires version 8.1 of the Windows SDK, which was - shipped with Visual Studio 2013, aka _MSC_VER 1800: - - https://technet.microsoft.com/en-us/library/hh831771%28v=ws.11%29.aspx -*/ -#if defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(_USING_V110_SDK71_) -# define HAS_ALPN 1 -#endif - -#ifndef UNISP_NAME_A -#define UNISP_NAME_A "Microsoft Unified Security Protocol Provider" -#endif - -#ifndef UNISP_NAME_W -#define UNISP_NAME_W L"Microsoft Unified Security Protocol Provider" -#endif - -#ifndef UNISP_NAME -#ifdef UNICODE -#define UNISP_NAME UNISP_NAME_W -#else -#define UNISP_NAME UNISP_NAME_A -#endif -#endif - -#ifndef BCRYPT_CHACHA20_POLY1305_ALGORITHM -#define BCRYPT_CHACHA20_POLY1305_ALGORITHM L"CHACHA20_POLY1305" -#endif - -#ifndef BCRYPT_CHAIN_MODE_CCM -#define BCRYPT_CHAIN_MODE_CCM L"ChainingModeCCM" -#endif - -#ifndef BCRYPT_CHAIN_MODE_GCM -#define BCRYPT_CHAIN_MODE_GCM L"ChainingModeGCM" -#endif - -#ifndef BCRYPT_AES_ALGORITHM -#define BCRYPT_AES_ALGORITHM L"AES" -#endif - -#ifndef BCRYPT_SHA256_ALGORITHM -#define BCRYPT_SHA256_ALGORITHM L"SHA256" -#endif - -#ifndef BCRYPT_SHA384_ALGORITHM -#define BCRYPT_SHA384_ALGORITHM L"SHA384" -#endif - -/* Workaround broken compilers like MinGW. - Return the number of elements in a statically sized array. -*/ -#ifndef ARRAYSIZE -#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) -#endif - -#ifdef HAS_CLIENT_CERT_PATH -#ifdef UNICODE -#define CURL_CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_W -#else -#define CURL_CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_A -#endif -#endif - -#ifndef SP_PROT_SSL2_CLIENT -#define SP_PROT_SSL2_CLIENT 0x00000008 -#endif - -#ifndef SP_PROT_SSL3_CLIENT -#define SP_PROT_SSL3_CLIENT 0x00000008 -#endif - -#ifndef SP_PROT_TLS1_CLIENT -#define SP_PROT_TLS1_CLIENT 0x00000080 -#endif - -#ifndef SP_PROT_TLS1_0_CLIENT -#define SP_PROT_TLS1_0_CLIENT SP_PROT_TLS1_CLIENT -#endif - -#ifndef SP_PROT_TLS1_1_CLIENT -#define SP_PROT_TLS1_1_CLIENT 0x00000200 -#endif - -#ifndef SP_PROT_TLS1_2_CLIENT -#define SP_PROT_TLS1_2_CLIENT 0x00000800 -#endif - -#ifndef SP_PROT_TLS1_3_CLIENT -#define SP_PROT_TLS1_3_CLIENT 0x00002000 -#endif - -#ifndef SCH_USE_STRONG_CRYPTO -#define SCH_USE_STRONG_CRYPTO 0x00400000 -#endif - -#ifndef SECBUFFER_ALERT -#define SECBUFFER_ALERT 17 -#endif - -/* Both schannel buffer sizes must be > 0 */ -#define CURL_SCHANNEL_BUFFER_INIT_SIZE 4096 -#define CURL_SCHANNEL_BUFFER_FREE_SIZE 1024 - -#define CERT_THUMBPRINT_STR_LEN 40 -#define CERT_THUMBPRINT_DATA_LEN 20 - -/* Uncomment to force verbose output - * #define infof(x, y, ...) printf(y, __VA_ARGS__) - * #define failf(x, y, ...) printf(y, __VA_ARGS__) - */ - -#ifndef CALG_SHA_256 -# define CALG_SHA_256 0x0000800c -#endif - -/* Work around typo in classic MinGW's w32api up to version 5.0, - see https://osdn.net/projects/mingw/ticket/38391 */ -#if !defined(ALG_CLASS_DHASH) && defined(ALG_CLASS_HASH) -#define ALG_CLASS_DHASH ALG_CLASS_HASH -#endif - -#ifndef PKCS12_NO_PERSIST_KEY -#define PKCS12_NO_PERSIST_KEY 0x00008000 -#endif - -static Curl_recv schannel_recv; -static Curl_send schannel_send; - -static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, - struct connectdata *conn, int sockindex, - const char *pinnedpubkey); - -static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType, - void *BufDataPtr, unsigned long BufByteSize) -{ - buffer->cbBuffer = BufByteSize; - buffer->BufferType = BufType; - buffer->pvBuffer = BufDataPtr; -} - -static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr, - unsigned long NumArrElem) -{ - desc->ulVersion = SECBUFFER_VERSION; - desc->pBuffers = BufArr; - desc->cBuffers = NumArrElem; -} - -static CURLcode -set_ssl_version_min_max(DWORD *enabled_protocols, struct Curl_easy *data, - struct connectdata *conn) -{ - long ssl_version = SSL_CONN_CONFIG(version); - long ssl_version_max = SSL_CONN_CONFIG(version_max); - long i = ssl_version; - - switch(ssl_version_max) { - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_DEFAULT: - - /* Windows Server 2022 and newer (including Windows 11) support TLS 1.3 - built-in. Previous builds of Windows 10 had broken TLS 1.3 - implementations that could be enabled via registry. - */ - if(curlx_verify_windows_version(10, 0, 20348, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL)) { - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_3; - } - else /* Windows 10 and older */ - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; - - break; - } - - for(; i <= (ssl_version_max >> 16); ++i) { - switch(i) { - case CURL_SSLVERSION_TLSv1_0: - (*enabled_protocols) |= SP_PROT_TLS1_0_CLIENT; - break; - case CURL_SSLVERSION_TLSv1_1: - (*enabled_protocols) |= SP_PROT_TLS1_1_CLIENT; - break; - case CURL_SSLVERSION_TLSv1_2: - (*enabled_protocols) |= SP_PROT_TLS1_2_CLIENT; - break; - case CURL_SSLVERSION_TLSv1_3: - - /* Windows Server 2022 and newer */ - if(curlx_verify_windows_version(10, 0, 20348, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL)) { - (*enabled_protocols) |= SP_PROT_TLS1_3_CLIENT; - break; - } - else { /* Windows 10 and older */ - failf(data, "schannel: TLS 1.3 not supported on Windows prior to 11"); - return CURLE_SSL_CONNECT_ERROR; - } - } - } - return CURLE_OK; -} - -/*longest is 26, buffer is slightly bigger*/ -#define LONGEST_ALG_ID 32 -#define CIPHEROPTION(X) \ - if(strcmp(#X, tmp) == 0) \ - return X - -static int -get_alg_id_by_name(char *name) -{ - char tmp[LONGEST_ALG_ID] = { 0 }; - char *nameEnd = strchr(name, ':'); - size_t n = nameEnd ? (size_t)(nameEnd - name) : strlen(name); - - /* reject too-long alg names */ - if(n > (LONGEST_ALG_ID - 1)) - return 0; - - strncpy(tmp, name, n); - tmp[n] = 0; - CIPHEROPTION(CALG_MD2); - CIPHEROPTION(CALG_MD4); - CIPHEROPTION(CALG_MD5); - CIPHEROPTION(CALG_SHA); - CIPHEROPTION(CALG_SHA1); - CIPHEROPTION(CALG_MAC); - CIPHEROPTION(CALG_RSA_SIGN); - CIPHEROPTION(CALG_DSS_SIGN); -/*ifdefs for the options that are defined conditionally in wincrypt.h*/ -#ifdef CALG_NO_SIGN - CIPHEROPTION(CALG_NO_SIGN); -#endif - CIPHEROPTION(CALG_RSA_KEYX); - CIPHEROPTION(CALG_DES); -#ifdef CALG_3DES_112 - CIPHEROPTION(CALG_3DES_112); -#endif - CIPHEROPTION(CALG_3DES); - CIPHEROPTION(CALG_DESX); - CIPHEROPTION(CALG_RC2); - CIPHEROPTION(CALG_RC4); - CIPHEROPTION(CALG_SEAL); -#ifdef CALG_DH_SF - CIPHEROPTION(CALG_DH_SF); -#endif - CIPHEROPTION(CALG_DH_EPHEM); -#ifdef CALG_AGREEDKEY_ANY - CIPHEROPTION(CALG_AGREEDKEY_ANY); -#endif -#ifdef CALG_HUGHES_MD5 - CIPHEROPTION(CALG_HUGHES_MD5); -#endif - CIPHEROPTION(CALG_SKIPJACK); -#ifdef CALG_TEK - CIPHEROPTION(CALG_TEK); -#endif - CIPHEROPTION(CALG_CYLINK_MEK); - CIPHEROPTION(CALG_SSL3_SHAMD5); -#ifdef CALG_SSL3_MASTER - CIPHEROPTION(CALG_SSL3_MASTER); -#endif -#ifdef CALG_SCHANNEL_MASTER_HASH - CIPHEROPTION(CALG_SCHANNEL_MASTER_HASH); -#endif -#ifdef CALG_SCHANNEL_MAC_KEY - CIPHEROPTION(CALG_SCHANNEL_MAC_KEY); -#endif -#ifdef CALG_SCHANNEL_ENC_KEY - CIPHEROPTION(CALG_SCHANNEL_ENC_KEY); -#endif -#ifdef CALG_PCT1_MASTER - CIPHEROPTION(CALG_PCT1_MASTER); -#endif -#ifdef CALG_SSL2_MASTER - CIPHEROPTION(CALG_SSL2_MASTER); -#endif -#ifdef CALG_TLS1_MASTER - CIPHEROPTION(CALG_TLS1_MASTER); -#endif -#ifdef CALG_RC5 - CIPHEROPTION(CALG_RC5); -#endif -#ifdef CALG_HMAC - CIPHEROPTION(CALG_HMAC); -#endif -#ifdef CALG_TLS1PRF - CIPHEROPTION(CALG_TLS1PRF); -#endif -#ifdef CALG_HASH_REPLACE_OWF - CIPHEROPTION(CALG_HASH_REPLACE_OWF); -#endif -#ifdef CALG_AES_128 - CIPHEROPTION(CALG_AES_128); -#endif -#ifdef CALG_AES_192 - CIPHEROPTION(CALG_AES_192); -#endif -#ifdef CALG_AES_256 - CIPHEROPTION(CALG_AES_256); -#endif -#ifdef CALG_AES - CIPHEROPTION(CALG_AES); -#endif -#ifdef CALG_SHA_256 - CIPHEROPTION(CALG_SHA_256); -#endif -#ifdef CALG_SHA_384 - CIPHEROPTION(CALG_SHA_384); -#endif -#ifdef CALG_SHA_512 - CIPHEROPTION(CALG_SHA_512); -#endif -#ifdef CALG_ECDH - CIPHEROPTION(CALG_ECDH); -#endif -#ifdef CALG_ECMQV - CIPHEROPTION(CALG_ECMQV); -#endif -#ifdef CALG_ECDSA - CIPHEROPTION(CALG_ECDSA); -#endif -#ifdef CALG_ECDH_EPHEM - CIPHEROPTION(CALG_ECDH_EPHEM); -#endif - return 0; -} - -#define NUM_CIPHERS 47 /* There are 47 options listed above */ - -static CURLcode -set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers, - ALG_ID *algIds) -{ - char *startCur = ciphers; - int algCount = 0; - while(startCur && (0 != *startCur) && (algCount < NUM_CIPHERS)) { - long alg = strtol(startCur, 0, 0); - if(!alg) - alg = get_alg_id_by_name(startCur); - if(alg) - algIds[algCount++] = alg; - else if(!strncmp(startCur, "USE_STRONG_CRYPTO", - sizeof("USE_STRONG_CRYPTO") - 1) || - !strncmp(startCur, "SCH_USE_STRONG_CRYPTO", - sizeof("SCH_USE_STRONG_CRYPTO") - 1)) - schannel_cred->dwFlags |= SCH_USE_STRONG_CRYPTO; - else - return CURLE_SSL_CIPHER; - startCur = strchr(startCur, ':'); - if(startCur) - startCur++; - } - schannel_cred->palgSupportedAlgs = algIds; - schannel_cred->cSupportedAlgs = algCount; - return CURLE_OK; -} - -#ifdef HAS_CLIENT_CERT_PATH - -/* Function allocates memory for store_path only if CURLE_OK is returned */ -static CURLcode -get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path, - TCHAR **thumbprint) -{ - TCHAR *sep; - TCHAR *store_path_start; - size_t store_name_len; - - sep = _tcschr(path, TEXT('\\')); - if(!sep) - return CURLE_SSL_CERTPROBLEM; - - store_name_len = sep - path; - - if(_tcsncmp(path, TEXT("CurrentUser"), store_name_len) == 0) - *store_name = CERT_SYSTEM_STORE_CURRENT_USER; - else if(_tcsncmp(path, TEXT("LocalMachine"), store_name_len) == 0) - *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE; - else if(_tcsncmp(path, TEXT("CurrentService"), store_name_len) == 0) - *store_name = CERT_SYSTEM_STORE_CURRENT_SERVICE; - else if(_tcsncmp(path, TEXT("Services"), store_name_len) == 0) - *store_name = CERT_SYSTEM_STORE_SERVICES; - else if(_tcsncmp(path, TEXT("Users"), store_name_len) == 0) - *store_name = CERT_SYSTEM_STORE_USERS; - else if(_tcsncmp(path, TEXT("CurrentUserGroupPolicy"), - store_name_len) == 0) - *store_name = CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY; - else if(_tcsncmp(path, TEXT("LocalMachineGroupPolicy"), - store_name_len) == 0) - *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY; - else if(_tcsncmp(path, TEXT("LocalMachineEnterprise"), - store_name_len) == 0) - *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE; - else - return CURLE_SSL_CERTPROBLEM; - - store_path_start = sep + 1; - - sep = _tcschr(store_path_start, TEXT('\\')); - if(!sep) - return CURLE_SSL_CERTPROBLEM; - - *thumbprint = sep + 1; - if(_tcslen(*thumbprint) != CERT_THUMBPRINT_STR_LEN) - return CURLE_SSL_CERTPROBLEM; - - *sep = TEXT('\0'); - *store_path = _tcsdup(store_path_start); - *sep = TEXT('\\'); - if(!*store_path) - return CURLE_OUT_OF_MEMORY; - - return CURLE_OK; -} -#endif -static CURLcode -schannel_acquire_credential_handle(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - -#ifdef HAS_CLIENT_CERT_PATH - PCCERT_CONTEXT client_certs[1] = { NULL }; - HCERTSTORE client_cert_store = NULL; -#endif - SECURITY_STATUS sspi_status = SEC_E_OK; - CURLcode result; - - /* setup Schannel API options */ - DWORD flags = 0; - DWORD enabled_protocols = 0; - - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - if(conn->ssl_config.verifypeer) { -#ifdef HAS_MANUAL_VERIFY_API - if(backend->use_manual_cred_validation) - flags = SCH_CRED_MANUAL_CRED_VALIDATION; - else -#endif - flags = SCH_CRED_AUTO_CRED_VALIDATION; - - if(SSL_SET_OPTION(no_revoke)) { - flags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | - SCH_CRED_IGNORE_REVOCATION_OFFLINE; - - DEBUGF(infof(data, "schannel: disabled server certificate revocation " - "checks")); - } - else if(SSL_SET_OPTION(revoke_best_effort)) { - flags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | - SCH_CRED_IGNORE_REVOCATION_OFFLINE | SCH_CRED_REVOCATION_CHECK_CHAIN; - - DEBUGF(infof(data, "schannel: ignore revocation offline errors")); - } - else { - flags |= SCH_CRED_REVOCATION_CHECK_CHAIN; - - DEBUGF(infof(data, - "schannel: checking server certificate revocation")); - } - } - else { - flags = SCH_CRED_MANUAL_CRED_VALIDATION | - SCH_CRED_IGNORE_NO_REVOCATION_CHECK | - SCH_CRED_IGNORE_REVOCATION_OFFLINE; - DEBUGF(infof(data, - "schannel: disabled server cert revocation checks")); - } - - if(!conn->ssl_config.verifyhost) { - flags |= SCH_CRED_NO_SERVERNAME_CHECK; - DEBUGF(infof(data, "schannel: verifyhost setting prevents Schannel from " - "comparing the supplied target name with the subject " - "names in server certificates.")); - } - - if(!SSL_SET_OPTION(auto_client_cert)) { - flags &= ~SCH_CRED_USE_DEFAULT_CREDS; - flags |= SCH_CRED_NO_DEFAULT_CREDS; - infof(data, "schannel: disabled automatic use of client certificate"); - } - else - infof(data, "schannel: enabled automatic use of client certificate"); - - switch(conn->ssl_config.version) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_3: - { - result = set_ssl_version_min_max(&enabled_protocols, data, conn); - if(result != CURLE_OK) - return result; - break; - } - case CURL_SSLVERSION_SSLv3: - case CURL_SSLVERSION_SSLv2: - failf(data, "SSL versions not supported"); - return CURLE_NOT_BUILT_IN; - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } - -#ifdef HAS_CLIENT_CERT_PATH - /* client certificate */ - if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) { - DWORD cert_store_name = 0; - TCHAR *cert_store_path = NULL; - TCHAR *cert_thumbprint_str = NULL; - CRYPT_HASH_BLOB cert_thumbprint; - BYTE cert_thumbprint_data[CERT_THUMBPRINT_DATA_LEN]; - HCERTSTORE cert_store = NULL; - FILE *fInCert = NULL; - void *certdata = NULL; - size_t certsize = 0; - bool blob = data->set.ssl.primary.cert_blob != NULL; - TCHAR *cert_path = NULL; - if(blob) { - certdata = data->set.ssl.primary.cert_blob->data; - certsize = data->set.ssl.primary.cert_blob->len; - } - else { - cert_path = curlx_convert_UTF8_to_tchar( - data->set.ssl.primary.clientcert); - if(!cert_path) - return CURLE_OUT_OF_MEMORY; - - result = get_cert_location(cert_path, &cert_store_name, - &cert_store_path, &cert_thumbprint_str); - - if(result && (data->set.ssl.primary.clientcert[0]!='\0')) - fInCert = fopen(data->set.ssl.primary.clientcert, "rb"); - - if(result && !fInCert) { - failf(data, "schannel: Failed to get certificate location" - " or file for %s", - data->set.ssl.primary.clientcert); - curlx_unicodefree(cert_path); - return result; - } - } - - if((fInCert || blob) && (data->set.ssl.cert_type) && - (!strcasecompare(data->set.ssl.cert_type, "P12"))) { - failf(data, "schannel: certificate format compatibility error " - " for %s", - blob ? "(memory blob)" : data->set.ssl.primary.clientcert); - curlx_unicodefree(cert_path); - return CURLE_SSL_CERTPROBLEM; - } - - if(fInCert || blob) { - /* Reading a .P12 or .pfx file, like the example at bottom of - https://social.msdn.microsoft.com/Forums/windowsdesktop/ - en-US/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5 - */ - CRYPT_DATA_BLOB datablob; - WCHAR* pszPassword; - size_t pwd_len = 0; - int str_w_len = 0; - const char *cert_showfilename_error = blob ? - "(memory blob)" : data->set.ssl.primary.clientcert; - curlx_unicodefree(cert_path); - if(fInCert) { - long cert_tell = 0; - bool continue_reading = fseek(fInCert, 0, SEEK_END) == 0; - if(continue_reading) - cert_tell = ftell(fInCert); - if(cert_tell < 0) - continue_reading = FALSE; - else - certsize = (size_t)cert_tell; - if(continue_reading) - continue_reading = fseek(fInCert, 0, SEEK_SET) == 0; - if(continue_reading) - certdata = malloc(certsize + 1); - if((!certdata) || - ((int) fread(certdata, certsize, 1, fInCert) != 1)) - continue_reading = FALSE; - fclose(fInCert); - if(!continue_reading) { - failf(data, "schannel: Failed to read cert file %s", - data->set.ssl.primary.clientcert); - free(certdata); - return CURLE_SSL_CERTPROBLEM; - } - } - - /* Convert key-pair data to the in-memory certificate store */ - datablob.pbData = (BYTE*)certdata; - datablob.cbData = (DWORD)certsize; - - if(data->set.ssl.key_passwd) - pwd_len = strlen(data->set.ssl.key_passwd); - pszPassword = (WCHAR*)malloc(sizeof(WCHAR)*(pwd_len + 1)); - if(pszPassword) { - if(pwd_len > 0) - str_w_len = MultiByteToWideChar(CP_UTF8, - MB_ERR_INVALID_CHARS, - data->set.ssl.key_passwd, - (int)pwd_len, - pszPassword, (int)(pwd_len + 1)); - - if((str_w_len >= 0) && (str_w_len <= (int)pwd_len)) - pszPassword[str_w_len] = 0; - else - pszPassword[0] = 0; - - if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL)) - cert_store = PFXImportCertStore(&datablob, pszPassword, - PKCS12_NO_PERSIST_KEY); - else - cert_store = PFXImportCertStore(&datablob, pszPassword, 0); - - free(pszPassword); - } - if(!blob) - free(certdata); - if(!cert_store) { - DWORD errorcode = GetLastError(); - if(errorcode == ERROR_INVALID_PASSWORD) - failf(data, "schannel: Failed to import cert file %s, " - "password is bad", - cert_showfilename_error); - else - failf(data, "schannel: Failed to import cert file %s, " - "last error is 0x%x", - cert_showfilename_error, errorcode); - return CURLE_SSL_CERTPROBLEM; - } - - client_certs[0] = CertFindCertificateInStore( - cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, - CERT_FIND_ANY, NULL, NULL); - - if(!client_certs[0]) { - failf(data, "schannel: Failed to get certificate from file %s" - ", last error is 0x%x", - cert_showfilename_error, GetLastError()); - CertCloseStore(cert_store, 0); - return CURLE_SSL_CERTPROBLEM; - } - } - else { - cert_store = - CertOpenStore(CURL_CERT_STORE_PROV_SYSTEM, 0, - (HCRYPTPROV)NULL, - CERT_STORE_OPEN_EXISTING_FLAG | cert_store_name, - cert_store_path); - if(!cert_store) { - failf(data, "schannel: Failed to open cert store %x %s, " - "last error is 0x%x", - cert_store_name, cert_store_path, GetLastError()); - free(cert_store_path); - curlx_unicodefree(cert_path); - return CURLE_SSL_CERTPROBLEM; - } - free(cert_store_path); - - cert_thumbprint.pbData = cert_thumbprint_data; - cert_thumbprint.cbData = CERT_THUMBPRINT_DATA_LEN; - - if(!CryptStringToBinary(cert_thumbprint_str, - CERT_THUMBPRINT_STR_LEN, - CRYPT_STRING_HEX, - cert_thumbprint_data, - &cert_thumbprint.cbData, - NULL, NULL)) { - curlx_unicodefree(cert_path); - CertCloseStore(cert_store, 0); - return CURLE_SSL_CERTPROBLEM; - } - - client_certs[0] = CertFindCertificateInStore( - cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, - CERT_FIND_HASH, &cert_thumbprint, NULL); - - curlx_unicodefree(cert_path); - - if(!client_certs[0]) { - /* CRYPT_E_NOT_FOUND / E_INVALIDARG */ - CertCloseStore(cert_store, 0); - return CURLE_SSL_CERTPROBLEM; - } - } - client_cert_store = cert_store; - } -#else - if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) { - failf(data, "schannel: client cert support not built in"); - return CURLE_NOT_BUILT_IN; - } -#endif - - /* allocate memory for the re-usable credential handle */ - backend->cred = (struct Curl_schannel_cred *) - calloc(1, sizeof(struct Curl_schannel_cred)); - if(!backend->cred) { - failf(data, "schannel: unable to allocate memory"); - -#ifdef HAS_CLIENT_CERT_PATH - if(client_certs[0]) - CertFreeCertificateContext(client_certs[0]); - if(client_cert_store) - CertCloseStore(client_cert_store, 0); -#endif - - return CURLE_OUT_OF_MEMORY; - } - backend->cred->refcount = 1; - -#ifdef HAS_CLIENT_CERT_PATH - /* Since we did not persist the key, we need to extend the store's - * lifetime until the end of the connection - */ - backend->cred->client_cert_store = client_cert_store; -#endif - - /* Windows 10, 1809 (a.k.a. Windows 10 build 17763) */ - if(curlx_verify_windows_version(10, 0, 17763, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL)) { - - char *ciphers13 = 0; - - bool disable_aes_gcm_sha384 = FALSE; - bool disable_aes_gcm_sha256 = FALSE; - bool disable_chacha_poly = FALSE; - bool disable_aes_ccm_8_sha256 = FALSE; - bool disable_aes_ccm_sha256 = FALSE; - - SCH_CREDENTIALS credentials = { 0 }; - TLS_PARAMETERS tls_parameters = { 0 }; - CRYPTO_SETTINGS crypto_settings[4] = { 0 }; - UNICODE_STRING blocked_ccm_modes[1] = { 0 }; - UNICODE_STRING blocked_gcm_modes[1] = { 0 }; - - int crypto_settings_idx = 0; - - - /* If TLS 1.3 ciphers are explicitly listed, then - * disable all the ciphers and re-enable which - * ciphers the user has provided. - */ - ciphers13 = SSL_CONN_CONFIG(cipher_list13); - if(ciphers13) { - const int remaining_ciphers = 5; - - /* detect which remaining ciphers to enable - and then disable everything else. - */ - - char *startCur = ciphers13; - int algCount = 0; - char tmp[LONGEST_ALG_ID] = { 0 }; - char *nameEnd; - size_t n; - - disable_aes_gcm_sha384 = TRUE; - disable_aes_gcm_sha256 = TRUE; - disable_chacha_poly = TRUE; - disable_aes_ccm_8_sha256 = TRUE; - disable_aes_ccm_sha256 = TRUE; - - while(startCur && (0 != *startCur) && (algCount < remaining_ciphers)) { - nameEnd = strchr(startCur, ':'); - n = nameEnd ? (size_t)(nameEnd - startCur) : strlen(startCur); - - /* reject too-long cipher names */ - if(n > (LONGEST_ALG_ID - 1)) { - failf(data, "Cipher name too long, not checked."); - return CURLE_SSL_CIPHER; - } - - strncpy(tmp, startCur, n); - tmp[n] = 0; - - if(disable_aes_gcm_sha384 - && !strcmp("TLS_AES_256_GCM_SHA384", tmp)) { - disable_aes_gcm_sha384 = FALSE; - } - else if(disable_aes_gcm_sha256 - && !strcmp("TLS_AES_128_GCM_SHA256", tmp)) { - disable_aes_gcm_sha256 = FALSE; - } - else if(disable_chacha_poly - && !strcmp("TLS_CHACHA20_POLY1305_SHA256", tmp)) { - disable_chacha_poly = FALSE; - } - else if(disable_aes_ccm_8_sha256 - && !strcmp("TLS_AES_128_CCM_8_SHA256", tmp)) { - disable_aes_ccm_8_sha256 = FALSE; - } - else if(disable_aes_ccm_sha256 - && !strcmp("TLS_AES_128_CCM_SHA256", tmp)) { - disable_aes_ccm_sha256 = FALSE; - } - else { - failf(data, "Passed in an unknown TLS 1.3 cipher."); - return CURLE_SSL_CIPHER; - } - - startCur = nameEnd; - if(startCur) - startCur++; - - algCount++; - } - } - - if(disable_aes_gcm_sha384 && disable_aes_gcm_sha256 - && disable_chacha_poly && disable_aes_ccm_8_sha256 - && disable_aes_ccm_sha256) { - failf(data, "All available TLS 1.3 ciphers were disabled."); - return CURLE_SSL_CIPHER; - } - - /* Disable TLS_AES_128_CCM_8_SHA256 and/or TLS_AES_128_CCM_SHA256 */ - if(disable_aes_ccm_8_sha256 || disable_aes_ccm_sha256) { - /* - Disallow AES_CCM algorithm. - */ - blocked_ccm_modes[0].Length = sizeof(BCRYPT_CHAIN_MODE_CCM); - blocked_ccm_modes[0].MaximumLength = sizeof(BCRYPT_CHAIN_MODE_CCM); - blocked_ccm_modes[0].Buffer = (PWSTR)BCRYPT_CHAIN_MODE_CCM; - - crypto_settings[crypto_settings_idx].eAlgorithmUsage = - TlsParametersCngAlgUsageCipher; - crypto_settings[crypto_settings_idx].rgstrChainingModes = - blocked_ccm_modes; - crypto_settings[crypto_settings_idx].cChainingModes = - ARRAYSIZE(blocked_ccm_modes); - crypto_settings[crypto_settings_idx].strCngAlgId.Length = - sizeof(BCRYPT_AES_ALGORITHM); - crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength = - sizeof(BCRYPT_AES_ALGORITHM); - crypto_settings[crypto_settings_idx].strCngAlgId.Buffer = - (PWSTR)BCRYPT_AES_ALGORITHM; - - /* only disabling one of the CCM modes */ - if(disable_aes_ccm_8_sha256 != disable_aes_ccm_sha256) { - if(disable_aes_ccm_8_sha256) - crypto_settings[crypto_settings_idx].dwMinBitLength = 128; - else /* disable_aes_ccm_sha256 */ - crypto_settings[crypto_settings_idx].dwMaxBitLength = 64; - } - - crypto_settings_idx++; - } - - /* Disable TLS_AES_256_GCM_SHA384 and/or TLS_AES_128_GCM_SHA256 */ - if(disable_aes_gcm_sha384 || disable_aes_gcm_sha256) { - - /* - Disallow AES_GCM algorithm - */ - blocked_gcm_modes[0].Length = sizeof(BCRYPT_CHAIN_MODE_GCM); - blocked_gcm_modes[0].MaximumLength = sizeof(BCRYPT_CHAIN_MODE_GCM); - blocked_gcm_modes[0].Buffer = (PWSTR)BCRYPT_CHAIN_MODE_GCM; - - /* if only one is disabled, then explicitly disable the - digest cipher suite (sha384 or sha256) */ - if(disable_aes_gcm_sha384 != disable_aes_gcm_sha256) { - crypto_settings[crypto_settings_idx].eAlgorithmUsage = - TlsParametersCngAlgUsageDigest; - crypto_settings[crypto_settings_idx].strCngAlgId.Length = - sizeof(disable_aes_gcm_sha384 ? - BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM); - crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength = - sizeof(disable_aes_gcm_sha384 ? - BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM); - crypto_settings[crypto_settings_idx].strCngAlgId.Buffer = - (PWSTR)(disable_aes_gcm_sha384 ? - BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM); - } - else { /* Disable both AES_GCM ciphers */ - crypto_settings[crypto_settings_idx].eAlgorithmUsage = - TlsParametersCngAlgUsageCipher; - crypto_settings[crypto_settings_idx].strCngAlgId.Length = - sizeof(BCRYPT_AES_ALGORITHM); - crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength = - sizeof(BCRYPT_AES_ALGORITHM); - crypto_settings[crypto_settings_idx].strCngAlgId.Buffer = - (PWSTR)BCRYPT_AES_ALGORITHM; - } - - crypto_settings[crypto_settings_idx].rgstrChainingModes = - blocked_gcm_modes; - crypto_settings[crypto_settings_idx].cChainingModes = 1; - - crypto_settings_idx++; - } - - /* - Disable ChaCha20-Poly1305. - */ - if(disable_chacha_poly) { - crypto_settings[crypto_settings_idx].eAlgorithmUsage = - TlsParametersCngAlgUsageCipher; - crypto_settings[crypto_settings_idx].strCngAlgId.Length = - sizeof(BCRYPT_CHACHA20_POLY1305_ALGORITHM); - crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength = - sizeof(BCRYPT_CHACHA20_POLY1305_ALGORITHM); - crypto_settings[crypto_settings_idx].strCngAlgId.Buffer = - (PWSTR)BCRYPT_CHACHA20_POLY1305_ALGORITHM; - crypto_settings_idx++; - } - - tls_parameters.pDisabledCrypto = crypto_settings; - - /* The number of blocked suites */ - tls_parameters.cDisabledCrypto = crypto_settings_idx; - credentials.pTlsParameters = &tls_parameters; - credentials.cTlsParameters = 1; - - credentials.dwVersion = SCH_CREDENTIALS_VERSION; - credentials.dwFlags = flags | SCH_USE_STRONG_CRYPTO; - - credentials.pTlsParameters->grbitDisabledProtocols = - (DWORD)~enabled_protocols; - -#ifdef HAS_CLIENT_CERT_PATH - if(client_certs[0]) { - credentials.cCreds = 1; - credentials.paCred = client_certs; - } -#endif - - sspi_status = - s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME, - SECPKG_CRED_OUTBOUND, NULL, - &credentials, NULL, NULL, - &backend->cred->cred_handle, - &backend->cred->time_stamp); - } - else { - /* Pre-Windows 10 1809 */ - ALG_ID algIds[NUM_CIPHERS]; - char *ciphers = SSL_CONN_CONFIG(cipher_list); - SCHANNEL_CRED schannel_cred = { 0 }; - schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; - schannel_cred.dwFlags = flags; - schannel_cred.grbitEnabledProtocols = enabled_protocols; - - if(ciphers) { - result = set_ssl_ciphers(&schannel_cred, ciphers, algIds); - if(CURLE_OK != result) { - failf(data, "Unable to set ciphers to passed via SSL_CONN_CONFIG"); - return result; - } - } - else { - schannel_cred.dwFlags = flags | SCH_USE_STRONG_CRYPTO; - } - -#ifdef HAS_CLIENT_CERT_PATH - if(client_certs[0]) { - schannel_cred.cCreds = 1; - schannel_cred.paCred = client_certs; - } -#endif - - sspi_status = - s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME, - SECPKG_CRED_OUTBOUND, NULL, - &schannel_cred, NULL, NULL, - &backend->cred->cred_handle, - &backend->cred->time_stamp); - } - -#ifdef HAS_CLIENT_CERT_PATH - if(client_certs[0]) - CertFreeCertificateContext(client_certs[0]); -#endif - - if(sspi_status != SEC_E_OK) { - char buffer[STRERROR_LEN]; - failf(data, "schannel: AcquireCredentialsHandle failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - Curl_safefree(backend->cred); - switch(sspi_status) { - case SEC_E_INSUFFICIENT_MEMORY: - return CURLE_OUT_OF_MEMORY; - case SEC_E_NO_CREDENTIALS: - case SEC_E_SECPKG_NOT_FOUND: - case SEC_E_NOT_OWNER: - case SEC_E_UNKNOWN_CREDENTIALS: - case SEC_E_INTERNAL_ERROR: - default: - return CURLE_SSL_CONNECT_ERROR; - } - } - - return CURLE_OK; -} - -static CURLcode -schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - ssize_t written = -1; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - SecBuffer outbuf; - SecBufferDesc outbuf_desc; - SecBuffer inbuf; - SecBufferDesc inbuf_desc; -#ifdef HAS_ALPN - unsigned char alpn_buffer[128]; -#endif - SECURITY_STATUS sspi_status = SEC_E_OK; - struct Curl_schannel_cred *old_cred = NULL; - struct in_addr addr; -#ifdef ENABLE_IPV6 - struct in6_addr addr6; -#endif - CURLcode result; - char * const hostname = SSL_HOST_NAME(); - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - DEBUGF(infof(data, - "schannel: SSL/TLS connection with %s port %hu (step 1/3)", - hostname, conn->remote_port)); - - if(curlx_verify_windows_version(5, 1, 0, PLATFORM_WINNT, - VERSION_LESS_THAN_EQUAL)) { - /* Schannel in Windows XP (OS version 5.1) uses legacy handshakes and - algorithms that may not be supported by all servers. */ - infof(data, "schannel: Windows version is old and may not be able to " - "connect to some servers due to lack of SNI, algorithms, etc."); - } - -#ifdef HAS_ALPN - /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above. - Also it doesn't seem to be supported for Wine, see curl bug #983. */ - backend->use_alpn = conn->bits.tls_enable_alpn && - !GetProcAddress(GetModuleHandle(TEXT("ntdll")), - "wine_get_version") && - curlx_verify_windows_version(6, 3, 0, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL); -#else - backend->use_alpn = false; -#endif - -#ifdef _WIN32_WCE -#ifdef HAS_MANUAL_VERIFY_API - /* certificate validation on CE doesn't seem to work right; we'll - * do it following a more manual process. */ - backend->use_manual_cred_validation = true; -#else -#error "compiler too old to support requisite manual cert verify for Win CE" -#endif -#else -#ifdef HAS_MANUAL_VERIFY_API - if(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) { - if(curlx_verify_windows_version(6, 1, 0, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL)) { - backend->use_manual_cred_validation = true; - } - else { - failf(data, "schannel: this version of Windows is too old to support " - "certificate verification via CA bundle file."); - return CURLE_SSL_CACERT_BADFILE; - } - } - else - backend->use_manual_cred_validation = false; -#else - if(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) { - failf(data, "schannel: CA cert support not built in"); - return CURLE_NOT_BUILT_IN; - } -#endif -#endif - - backend->cred = NULL; - - /* check for an existing re-usable credential handle */ - if(SSL_SET_OPTION(primary.sessionid)) { - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(data, conn, - SSL_IS_PROXY() ? TRUE : FALSE, - (void **)&old_cred, NULL, sockindex)) { - backend->cred = old_cred; - DEBUGF(infof(data, "schannel: re-using existing credential handle")); - - /* increment the reference counter of the credential/session handle */ - backend->cred->refcount++; - DEBUGF(infof(data, - "schannel: incremented credential handle refcount = %d", - backend->cred->refcount)); - } - Curl_ssl_sessionid_unlock(data); - } - - if(!backend->cred) { - char *snihost; - result = schannel_acquire_credential_handle(data, conn, sockindex); - if(result != CURLE_OK) { - return result; - } - /* A hostname associated with the credential is needed by - InitializeSecurityContext for SNI and other reasons. */ - snihost = Curl_ssl_snihost(data, SSL_HOST_NAME(), NULL); - if(!snihost) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - backend->cred->sni_hostname = curlx_convert_UTF8_to_tchar(snihost); - if(!backend->cred->sni_hostname) - return CURLE_OUT_OF_MEMORY; - } - - /* Warn if SNI is disabled due to use of an IP address */ - if(Curl_inet_pton(AF_INET, hostname, &addr) -#ifdef ENABLE_IPV6 - || Curl_inet_pton(AF_INET6, hostname, &addr6) -#endif - ) { - infof(data, "schannel: using IP address, SNI is not supported by OS."); - } - -#ifdef HAS_ALPN - if(backend->use_alpn) { - int cur = 0; - int list_start_index = 0; - unsigned int *extension_len = NULL; - unsigned short* list_len = NULL; - - /* The first four bytes will be an unsigned int indicating number - of bytes of data in the rest of the buffer. */ - extension_len = (unsigned int *)(&alpn_buffer[cur]); - cur += sizeof(unsigned int); - - /* The next four bytes are an indicator that this buffer will contain - ALPN data, as opposed to NPN, for example. */ - *(unsigned int *)&alpn_buffer[cur] = - SecApplicationProtocolNegotiationExt_ALPN; - cur += sizeof(unsigned int); - - /* The next two bytes will be an unsigned short indicating the number - of bytes used to list the preferred protocols. */ - list_len = (unsigned short*)(&alpn_buffer[cur]); - cur += sizeof(unsigned short); - - list_start_index = cur; - -#ifdef USE_HTTP2 - if(data->state.httpwant >= CURL_HTTP_VERSION_2) { - alpn_buffer[cur++] = ALPN_H2_LENGTH; - memcpy(&alpn_buffer[cur], ALPN_H2, ALPN_H2_LENGTH); - cur += ALPN_H2_LENGTH; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); - } -#endif - - alpn_buffer[cur++] = ALPN_HTTP_1_1_LENGTH; - memcpy(&alpn_buffer[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH); - cur += ALPN_HTTP_1_1_LENGTH; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); - - *list_len = curlx_uitous(cur - list_start_index); - *extension_len = *list_len + sizeof(unsigned int) + sizeof(unsigned short); - - InitSecBuffer(&inbuf, SECBUFFER_APPLICATION_PROTOCOLS, alpn_buffer, cur); - InitSecBufferDesc(&inbuf_desc, &inbuf, 1); - } - else { - InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0); - InitSecBufferDesc(&inbuf_desc, &inbuf, 1); - } -#else /* HAS_ALPN */ - InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0); - InitSecBufferDesc(&inbuf_desc, &inbuf, 1); -#endif - - /* setup output buffer */ - InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0); - InitSecBufferDesc(&outbuf_desc, &outbuf, 1); - - /* security request flags */ - backend->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | - ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY | - ISC_REQ_STREAM; - - if(!SSL_SET_OPTION(auto_client_cert)) { - backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS; - } - - /* allocate memory for the security context handle */ - backend->ctxt = (struct Curl_schannel_ctxt *) - calloc(1, sizeof(struct Curl_schannel_ctxt)); - if(!backend->ctxt) { - failf(data, "schannel: unable to allocate memory"); - return CURLE_OUT_OF_MEMORY; - } - - /* Schannel InitializeSecurityContext: - https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx - - At the moment we don't pass inbuf unless we're using ALPN since we only - use it for that, and Wine (for which we currently disable ALPN) is giving - us problems with inbuf regardless. https://github.com/curl/curl/issues/983 - */ - sspi_status = s_pSecFn->InitializeSecurityContext( - &backend->cred->cred_handle, NULL, backend->cred->sni_hostname, - backend->req_flags, 0, 0, - (backend->use_alpn ? &inbuf_desc : NULL), - 0, &backend->ctxt->ctxt_handle, - &outbuf_desc, &backend->ret_flags, &backend->ctxt->time_stamp); - - if(sspi_status != SEC_I_CONTINUE_NEEDED) { - char buffer[STRERROR_LEN]; - Curl_safefree(backend->ctxt); - switch(sspi_status) { - case SEC_E_INSUFFICIENT_MEMORY: - failf(data, "schannel: initial InitializeSecurityContext failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_OUT_OF_MEMORY; - case SEC_E_WRONG_PRINCIPAL: - failf(data, "schannel: SNI or certificate check failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_PEER_FAILED_VERIFICATION; - /* - case SEC_E_INVALID_HANDLE: - case SEC_E_INVALID_TOKEN: - case SEC_E_LOGON_DENIED: - case SEC_E_TARGET_UNKNOWN: - case SEC_E_NO_AUTHENTICATING_AUTHORITY: - case SEC_E_INTERNAL_ERROR: - case SEC_E_NO_CREDENTIALS: - case SEC_E_UNSUPPORTED_FUNCTION: - case SEC_E_APPLICATION_PROTOCOL_MISMATCH: - */ - default: - failf(data, "schannel: initial InitializeSecurityContext failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_SSL_CONNECT_ERROR; - } - } - - DEBUGF(infof(data, "schannel: sending initial handshake data: " - "sending %lu bytes.", outbuf.cbBuffer)); - - /* send initial handshake data which is now stored in output buffer */ - result = Curl_write_plain(data, conn->sock[sockindex], outbuf.pvBuffer, - outbuf.cbBuffer, &written); - s_pSecFn->FreeContextBuffer(outbuf.pvBuffer); - if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) { - failf(data, "schannel: failed to send initial handshake data: " - "sent %zd of %lu bytes", written, outbuf.cbBuffer); - return CURLE_SSL_CONNECT_ERROR; - } - - DEBUGF(infof(data, "schannel: sent initial handshake data: " - "sent %zd bytes", written)); - - backend->recv_unrecoverable_err = CURLE_OK; - backend->recv_sspi_close_notify = false; - backend->recv_connection_closed = false; - backend->recv_renegotiating = false; - backend->encdata_is_incomplete = false; - - /* continue to second handshake step */ - connssl->connecting_state = ssl_connect_2; - - return CURLE_OK; -} - -static CURLcode -schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - int i; - ssize_t nread = -1, written = -1; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - unsigned char *reallocated_buffer; - SecBuffer outbuf[3]; - SecBufferDesc outbuf_desc; - SecBuffer inbuf[2]; - SecBufferDesc inbuf_desc; - SECURITY_STATUS sspi_status = SEC_E_OK; - CURLcode result; - bool doread; - const char *pubkey_ptr; - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE; - - DEBUGF(infof(data, - "schannel: SSL/TLS connection with %s port %hu (step 2/3)", - SSL_HOST_NAME(), conn->remote_port)); - - if(!backend->cred || !backend->ctxt) - return CURLE_SSL_CONNECT_ERROR; - - /* buffer to store previously received and decrypted data */ - if(!backend->decdata_buffer) { - backend->decdata_offset = 0; - backend->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; - backend->decdata_buffer = malloc(backend->decdata_length); - if(!backend->decdata_buffer) { - failf(data, "schannel: unable to allocate memory"); - return CURLE_OUT_OF_MEMORY; - } - } - - /* buffer to store previously received and encrypted data */ - if(!backend->encdata_buffer) { - backend->encdata_is_incomplete = false; - backend->encdata_offset = 0; - backend->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; - backend->encdata_buffer = malloc(backend->encdata_length); - if(!backend->encdata_buffer) { - failf(data, "schannel: unable to allocate memory"); - return CURLE_OUT_OF_MEMORY; - } - } - - /* if we need a bigger buffer to read a full message, increase buffer now */ - if(backend->encdata_length - backend->encdata_offset < - CURL_SCHANNEL_BUFFER_FREE_SIZE) { - /* increase internal encrypted data buffer */ - size_t reallocated_length = backend->encdata_offset + - CURL_SCHANNEL_BUFFER_FREE_SIZE; - reallocated_buffer = realloc(backend->encdata_buffer, - reallocated_length); - - if(!reallocated_buffer) { - failf(data, "schannel: unable to re-allocate memory"); - return CURLE_OUT_OF_MEMORY; - } - else { - backend->encdata_buffer = reallocated_buffer; - backend->encdata_length = reallocated_length; - } - } - - for(;;) { - if(doread) { - /* read encrypted handshake data from socket */ - result = Curl_read_plain(conn->sock[sockindex], - (char *) (backend->encdata_buffer + - backend->encdata_offset), - backend->encdata_length - - backend->encdata_offset, - &nread); - if(result == CURLE_AGAIN) { - if(connssl->connecting_state != ssl_connect_2_writing) - connssl->connecting_state = ssl_connect_2_reading; - DEBUGF(infof(data, "schannel: failed to receive handshake, " - "need more data")); - return CURLE_OK; - } - else if((result != CURLE_OK) || (nread == 0)) { - failf(data, "schannel: failed to receive handshake, " - "SSL/TLS connection failed"); - return CURLE_SSL_CONNECT_ERROR; - } - - /* increase encrypted data buffer offset */ - backend->encdata_offset += nread; - backend->encdata_is_incomplete = false; - DEBUGF(infof(data, "schannel: encrypted data got %zd", nread)); - } - - DEBUGF(infof(data, - "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); - - /* setup input buffers */ - InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(backend->encdata_offset), - curlx_uztoul(backend->encdata_offset)); - InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0); - InitSecBufferDesc(&inbuf_desc, inbuf, 2); - - /* setup output buffers */ - InitSecBuffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0); - InitSecBuffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0); - InitSecBuffer(&outbuf[2], SECBUFFER_EMPTY, NULL, 0); - InitSecBufferDesc(&outbuf_desc, outbuf, 3); - - if(!inbuf[0].pvBuffer) { - failf(data, "schannel: unable to allocate memory"); - return CURLE_OUT_OF_MEMORY; - } - - /* copy received handshake data into input buffer */ - memcpy(inbuf[0].pvBuffer, backend->encdata_buffer, - backend->encdata_offset); - - sspi_status = s_pSecFn->InitializeSecurityContext( - &backend->cred->cred_handle, &backend->ctxt->ctxt_handle, - backend->cred->sni_hostname, backend->req_flags, - 0, 0, &inbuf_desc, 0, NULL, - &outbuf_desc, &backend->ret_flags, &backend->ctxt->time_stamp); - - /* free buffer for received handshake data */ - Curl_safefree(inbuf[0].pvBuffer); - - /* check if the handshake was incomplete */ - if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { - backend->encdata_is_incomplete = true; - connssl->connecting_state = ssl_connect_2_reading; - DEBUGF(infof(data, - "schannel: received incomplete message, need more data")); - return CURLE_OK; - } - - /* If the server has requested a client certificate, attempt to continue - the handshake without one. This will allow connections to servers which - request a client certificate but do not require it. */ - if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS && - !(backend->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) { - backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS; - connssl->connecting_state = ssl_connect_2_writing; - DEBUGF(infof(data, - "schannel: a client certificate has been requested")); - return CURLE_OK; - } - - /* check if the handshake needs to be continued */ - if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) { - for(i = 0; i < 3; i++) { - /* search for handshake tokens that need to be send */ - if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) { - DEBUGF(infof(data, "schannel: sending next handshake data: " - "sending %lu bytes.", outbuf[i].cbBuffer)); - - /* send handshake token to server */ - result = Curl_write_plain(data, conn->sock[sockindex], - outbuf[i].pvBuffer, outbuf[i].cbBuffer, - &written); - if((result != CURLE_OK) || - (outbuf[i].cbBuffer != (size_t) written)) { - failf(data, "schannel: failed to send next handshake data: " - "sent %zd of %lu bytes", written, outbuf[i].cbBuffer); - return CURLE_SSL_CONNECT_ERROR; - } - } - - /* free obsolete buffer */ - if(outbuf[i].pvBuffer) { - s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer); - } - } - } - else { - char buffer[STRERROR_LEN]; - switch(sspi_status) { - case SEC_E_INSUFFICIENT_MEMORY: - failf(data, "schannel: next InitializeSecurityContext failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_OUT_OF_MEMORY; - case SEC_E_WRONG_PRINCIPAL: - failf(data, "schannel: SNI or certificate check failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_PEER_FAILED_VERIFICATION; - case SEC_E_UNTRUSTED_ROOT: - failf(data, "schannel: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_PEER_FAILED_VERIFICATION; - /* - case SEC_E_INVALID_HANDLE: - case SEC_E_INVALID_TOKEN: - case SEC_E_LOGON_DENIED: - case SEC_E_TARGET_UNKNOWN: - case SEC_E_NO_AUTHENTICATING_AUTHORITY: - case SEC_E_INTERNAL_ERROR: - case SEC_E_NO_CREDENTIALS: - case SEC_E_UNSUPPORTED_FUNCTION: - case SEC_E_APPLICATION_PROTOCOL_MISMATCH: - */ - default: - failf(data, "schannel: next InitializeSecurityContext failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_SSL_CONNECT_ERROR; - } - } - - /* check if there was additional remaining encrypted data */ - if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) { - DEBUGF(infof(data, "schannel: encrypted data length: %lu", - inbuf[1].cbBuffer)); - /* - There are two cases where we could be getting extra data here: - 1) If we're renegotiating a connection and the handshake is already - complete (from the server perspective), it can encrypted app data - (not handshake data) in an extra buffer at this point. - 2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a - connection and this extra data is part of the handshake. - We should process the data immediately; waiting for the socket to - be ready may fail since the server is done sending handshake data. - */ - /* check if the remaining data is less than the total amount - and therefore begins after the already processed data */ - if(backend->encdata_offset > inbuf[1].cbBuffer) { - memmove(backend->encdata_buffer, - (backend->encdata_buffer + backend->encdata_offset) - - inbuf[1].cbBuffer, inbuf[1].cbBuffer); - backend->encdata_offset = inbuf[1].cbBuffer; - if(sspi_status == SEC_I_CONTINUE_NEEDED) { - doread = FALSE; - continue; - } - } - } - else { - backend->encdata_offset = 0; - } - break; - } - - /* check if the handshake needs to be continued */ - if(sspi_status == SEC_I_CONTINUE_NEEDED) { - connssl->connecting_state = ssl_connect_2_reading; - return CURLE_OK; - } - - /* check if the handshake is complete */ - if(sspi_status == SEC_E_OK) { - connssl->connecting_state = ssl_connect_3; - DEBUGF(infof(data, "schannel: SSL/TLS handshake complete")); - } - - pubkey_ptr = SSL_PINNED_PUB_KEY(); - if(pubkey_ptr) { - result = pkp_pin_peer_pubkey(data, conn, sockindex, pubkey_ptr); - if(result) { - failf(data, "SSL: public key does not match pinned public key"); - return result; - } - } - -#ifdef HAS_MANUAL_VERIFY_API - if(conn->ssl_config.verifypeer && backend->use_manual_cred_validation) { - return Curl_verify_certificate(data, conn, sockindex); - } -#endif - - return CURLE_OK; -} - -static bool -valid_cert_encoding(const CERT_CONTEXT *cert_context) -{ - return (cert_context != NULL) && - ((cert_context->dwCertEncodingType & X509_ASN_ENCODING) != 0) && - (cert_context->pbCertEncoded != NULL) && - (cert_context->cbCertEncoded > 0); -} - -typedef bool(*Read_crt_func)(const CERT_CONTEXT *ccert_context, void *arg); - -static void -traverse_cert_store(const CERT_CONTEXT *context, Read_crt_func func, - void *arg) -{ - const CERT_CONTEXT *current_context = NULL; - bool should_continue = true; - while(should_continue && - (current_context = CertEnumCertificatesInStore( - context->hCertStore, - current_context)) != NULL) - should_continue = func(current_context, arg); - - if(current_context) - CertFreeCertificateContext(current_context); -} - -static bool -cert_counter_callback(const CERT_CONTEXT *ccert_context, void *certs_count) -{ - if(valid_cert_encoding(ccert_context)) - (*(int *)certs_count)++; - return true; -} - -struct Adder_args -{ - struct Curl_easy *data; - CURLcode result; - int idx; - int certs_count; -}; - -static bool -add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, void *raw_arg) -{ - struct Adder_args *args = (struct Adder_args*)raw_arg; - args->result = CURLE_OK; - if(valid_cert_encoding(ccert_context)) { - const char *beg = (const char *) ccert_context->pbCertEncoded; - const char *end = beg + ccert_context->cbCertEncoded; - int insert_index = (args->certs_count - 1) - args->idx; - args->result = Curl_extract_certinfo(args->data, insert_index, - beg, end); - args->idx++; - } - return args->result == CURLE_OK; -} - -static CURLcode -schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - CURLcode result = CURLE_OK; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - SECURITY_STATUS sspi_status = SEC_E_OK; - CERT_CONTEXT *ccert_context = NULL; - bool isproxy = SSL_IS_PROXY(); -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - const char * const hostname = SSL_HOST_NAME(); -#endif -#ifdef HAS_ALPN - SecPkgContext_ApplicationProtocol alpn_result; -#endif - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); - DEBUGASSERT(backend); - - DEBUGF(infof(data, - "schannel: SSL/TLS connection with %s port %hu (step 3/3)", - hostname, conn->remote_port)); - - if(!backend->cred) - return CURLE_SSL_CONNECT_ERROR; - - /* check if the required context attributes are met */ - if(backend->ret_flags != backend->req_flags) { - if(!(backend->ret_flags & ISC_RET_SEQUENCE_DETECT)) - failf(data, "schannel: failed to setup sequence detection"); - if(!(backend->ret_flags & ISC_RET_REPLAY_DETECT)) - failf(data, "schannel: failed to setup replay detection"); - if(!(backend->ret_flags & ISC_RET_CONFIDENTIALITY)) - failf(data, "schannel: failed to setup confidentiality"); - if(!(backend->ret_flags & ISC_RET_ALLOCATED_MEMORY)) - failf(data, "schannel: failed to setup memory allocation"); - if(!(backend->ret_flags & ISC_RET_STREAM)) - failf(data, "schannel: failed to setup stream orientation"); - return CURLE_SSL_CONNECT_ERROR; - } - -#ifdef HAS_ALPN - if(backend->use_alpn) { - sspi_status = - s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, - SECPKG_ATTR_APPLICATION_PROTOCOL, - &alpn_result); - - if(sspi_status != SEC_E_OK) { - failf(data, "schannel: failed to retrieve ALPN result"); - return CURLE_SSL_CONNECT_ERROR; - } - - if(alpn_result.ProtoNegoStatus == - SecApplicationProtocolNegotiationStatus_Success) { - unsigned char alpn = 0; - - infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, - alpn_result.ProtocolIdSize, alpn_result.ProtocolId); - -#ifdef USE_HTTP2 - if(alpn_result.ProtocolIdSize == ALPN_H2_LENGTH && - !memcmp(ALPN_H2, alpn_result.ProtocolId, ALPN_H2_LENGTH)) { - alpn = CURL_HTTP_VERSION_2; - } - else -#endif - if(alpn_result.ProtocolIdSize == ALPN_HTTP_1_1_LENGTH && - !memcmp(ALPN_HTTP_1_1, alpn_result.ProtocolId, - ALPN_HTTP_1_1_LENGTH)) { - alpn = CURL_HTTP_VERSION_1_1; - } - if(backend->recv_renegotiating) { - if(alpn != conn->alpn) { - failf(data, "schannel: server selected an ALPN protocol too late"); - return CURLE_SSL_CONNECT_ERROR; - } - } - else - conn->alpn = alpn; - } - else { - if(!backend->recv_renegotiating) - infof(data, VTLS_INFOF_NO_ALPN); - } - - if(!backend->recv_renegotiating) { - Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); - } - } -#endif - - /* save the current session data for possible re-use */ - if(SSL_SET_OPTION(primary.sessionid)) { - bool incache; - bool added = FALSE; - struct Curl_schannel_cred *old_cred = NULL; - - Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(data, conn, isproxy, (void **)&old_cred, - NULL, sockindex)); - if(incache) { - if(old_cred != backend->cred) { - DEBUGF(infof(data, - "schannel: old credential handle is stale, removing")); - /* we're not taking old_cred ownership here, no refcount++ is needed */ - Curl_ssl_delsessionid(data, (void *)old_cred); - incache = FALSE; - } - } - if(!incache) { - result = Curl_ssl_addsessionid(data, conn, isproxy, backend->cred, - sizeof(struct Curl_schannel_cred), - sockindex, &added); - if(result) { - Curl_ssl_sessionid_unlock(data); - failf(data, "schannel: failed to store credential handle"); - return result; - } - else if(added) { - /* this cred session is now also referenced by sessionid cache */ - backend->cred->refcount++; - DEBUGF(infof(data, - "schannel: stored credential handle in session cache")); - } - } - Curl_ssl_sessionid_unlock(data); - } - - if(data->set.ssl.certinfo) { - int certs_count = 0; - sspi_status = - s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, - SECPKG_ATTR_REMOTE_CERT_CONTEXT, - &ccert_context); - - if((sspi_status != SEC_E_OK) || !ccert_context) { - failf(data, "schannel: failed to retrieve remote cert context"); - return CURLE_PEER_FAILED_VERIFICATION; - } - - traverse_cert_store(ccert_context, cert_counter_callback, &certs_count); - - result = Curl_ssl_init_certinfo(data, certs_count); - if(!result) { - struct Adder_args args; - args.data = data; - args.idx = 0; - args.certs_count = certs_count; - traverse_cert_store(ccert_context, add_cert_to_certinfo, &args); - result = args.result; - } - CertFreeCertificateContext(ccert_context); - if(result) - return result; - } - - connssl->connecting_state = ssl_connect_done; - - return CURLE_OK; -} - -static CURLcode -schannel_connect_common(struct Curl_easy *data, struct connectdata *conn, - int sockindex, bool nonblocking, bool *done) -{ - CURLcode result; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - curl_socket_t sockfd = conn->sock[sockindex]; - timediff_t timeout_ms; - int what; - - /* check if the connection has already been established */ - if(ssl_connection_complete == connssl->state) { - *done = TRUE; - return CURLE_OK; - } - - if(ssl_connect_1 == connssl->connecting_state) { - /* check out how much more time we're allowed */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL/TLS connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - result = schannel_connect_step1(data, conn, sockindex); - if(result) - return result; - } - - while(ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state) { - - /* check out how much more time we're allowed */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL/TLS connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - /* if ssl is expecting something, check if it's available. */ - if(connssl->connecting_state == ssl_connect_2_reading - || connssl->connecting_state == ssl_connect_2_writing) { - - curl_socket_t writefd = ssl_connect_2_writing == - connssl->connecting_state ? sockfd : CURL_SOCKET_BAD; - curl_socket_t readfd = ssl_connect_2_reading == - connssl->connecting_state ? sockfd : CURL_SOCKET_BAD; - - what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, - nonblocking ? 0 : timeout_ms); - if(what < 0) { - /* fatal error */ - failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO); - return CURLE_SSL_CONNECT_ERROR; - } - else if(0 == what) { - if(nonblocking) { - *done = FALSE; - return CURLE_OK; - } - else { - /* timeout */ - failf(data, "SSL/TLS connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - } - /* socket is readable or writable */ - } - - /* Run transaction, and return to the caller if it failed or if - * this connection is part of a multi handle and this loop would - * execute again. This permits the owner of a multi handle to - * abort a connection attempt before step2 has completed while - * ensuring that a client using select() or epoll() will always - * have a valid fdset to wait on. - */ - result = schannel_connect_step2(data, conn, sockindex); - if(result || (nonblocking && - (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) - return result; - - } /* repeat step2 until all transactions are done. */ - - if(ssl_connect_3 == connssl->connecting_state) { - result = schannel_connect_step3(data, conn, sockindex); - if(result) - return result; - } - - if(ssl_connect_done == connssl->connecting_state) { - connssl->state = ssl_connection_complete; - if(!connssl->backend->recv_renegotiating) { - /* On renegotiation, we don't want to reset the existing recv/send - * function pointers. They will have been set after the initial TLS - * handshake was completed. If they were subsequently modified, as - * is the case with HTTP/2, we don't want to override that change. - */ - conn->recv[sockindex] = schannel_recv; - conn->send[sockindex] = schannel_send; - } - -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS - /* When SSPI is used in combination with Schannel - * we need the Schannel context to create the Schannel - * binding to pass the IIS extended protection checks. - * Available on Windows 7 or later. - */ - { - struct ssl_backend_data *backend = connssl->backend; - DEBUGASSERT(backend); - conn->sslContext = &backend->ctxt->ctxt_handle; - } -#endif - - *done = TRUE; - } - else - *done = FALSE; - - /* reset our connection state machine */ - connssl->connecting_state = ssl_connect_1; - - return CURLE_OK; -} - -static ssize_t -schannel_send(struct Curl_easy *data, int sockindex, - const void *buf, size_t len, CURLcode *err) -{ - ssize_t written = -1; - size_t data_len = 0; - unsigned char *ptr = NULL; - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - SecBuffer outbuf[4]; - SecBufferDesc outbuf_desc; - SECURITY_STATUS sspi_status = SEC_E_OK; - CURLcode result; - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - /* check if the maximum stream sizes were queried */ - if(backend->stream_sizes.cbMaximumMessage == 0) { - sspi_status = s_pSecFn->QueryContextAttributes( - &backend->ctxt->ctxt_handle, - SECPKG_ATTR_STREAM_SIZES, - &backend->stream_sizes); - if(sspi_status != SEC_E_OK) { - *err = CURLE_SEND_ERROR; - return -1; - } - } - - /* check if the buffer is longer than the maximum message length */ - if(len > backend->stream_sizes.cbMaximumMessage) { - len = backend->stream_sizes.cbMaximumMessage; - } - - /* calculate the complete message length and allocate a buffer for it */ - data_len = backend->stream_sizes.cbHeader + len + - backend->stream_sizes.cbTrailer; - ptr = (unsigned char *) malloc(data_len); - if(!ptr) { - *err = CURLE_OUT_OF_MEMORY; - return -1; - } - - /* setup output buffers (header, data, trailer, empty) */ - InitSecBuffer(&outbuf[0], SECBUFFER_STREAM_HEADER, - ptr, backend->stream_sizes.cbHeader); - InitSecBuffer(&outbuf[1], SECBUFFER_DATA, - ptr + backend->stream_sizes.cbHeader, curlx_uztoul(len)); - InitSecBuffer(&outbuf[2], SECBUFFER_STREAM_TRAILER, - ptr + backend->stream_sizes.cbHeader + len, - backend->stream_sizes.cbTrailer); - InitSecBuffer(&outbuf[3], SECBUFFER_EMPTY, NULL, 0); - InitSecBufferDesc(&outbuf_desc, outbuf, 4); - - /* copy data into output buffer */ - memcpy(outbuf[1].pvBuffer, buf, len); - - /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */ - sspi_status = s_pSecFn->EncryptMessage(&backend->ctxt->ctxt_handle, 0, - &outbuf_desc, 0); - - /* check if the message was encrypted */ - if(sspi_status == SEC_E_OK) { - written = 0; - - /* send the encrypted message including header, data and trailer */ - len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer; - - /* - It's important to send the full message which includes the header, - encrypted payload, and trailer. Until the client receives all the - data a coherent message has not been delivered and the client - can't read any of it. - - If we wanted to buffer the unwritten encrypted bytes, we would - tell the client that all data it has requested to be sent has been - sent. The unwritten encrypted bytes would be the first bytes to - send on the next invocation. - Here's the catch with this - if we tell the client that all the - bytes have been sent, will the client call this method again to - send the buffered data? Looking at who calls this function, it - seems the answer is NO. - */ - - /* send entire message or fail */ - while(len > (size_t)written) { - ssize_t this_write = 0; - int what; - timediff_t timeout_ms = Curl_timeleft(data, NULL, FALSE); - if(timeout_ms < 0) { - /* we already got the timeout */ - failf(data, "schannel: timed out sending data " - "(bytes sent: %zd)", written); - *err = CURLE_OPERATION_TIMEDOUT; - written = -1; - break; - } - else if(!timeout_ms) - timeout_ms = TIMEDIFF_T_MAX; - what = SOCKET_WRITABLE(conn->sock[sockindex], timeout_ms); - if(what < 0) { - /* fatal error */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - *err = CURLE_SEND_ERROR; - written = -1; - break; - } - else if(0 == what) { - failf(data, "schannel: timed out sending data " - "(bytes sent: %zd)", written); - *err = CURLE_OPERATION_TIMEDOUT; - written = -1; - break; - } - /* socket is writable */ - - result = Curl_write_plain(data, conn->sock[sockindex], ptr + written, - len - written, &this_write); - if(result == CURLE_AGAIN) - continue; - else if(result != CURLE_OK) { - *err = result; - written = -1; - break; - } - - written += this_write; - } - } - else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) { - *err = CURLE_OUT_OF_MEMORY; - } - else{ - *err = CURLE_SEND_ERROR; - } - - Curl_safefree(ptr); - - if(len == (size_t)written) - /* Encrypted message including header, data and trailer entirely sent. - The return value is the number of unencrypted bytes that were sent. */ - written = outbuf[1].cbBuffer; - - return written; -} - -static ssize_t -schannel_recv(struct Curl_easy *data, int sockindex, - char *buf, size_t len, CURLcode *err) -{ - size_t size = 0; - ssize_t nread = -1; - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - unsigned char *reallocated_buffer; - size_t reallocated_length; - bool done = FALSE; - SecBuffer inbuf[4]; - SecBufferDesc inbuf_desc; - SECURITY_STATUS sspi_status = SEC_E_OK; - /* we want the length of the encrypted buffer to be at least large enough - that it can hold all the bytes requested and some TLS record overhead. */ - size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE; - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - /**************************************************************************** - * Don't return or set backend->recv_unrecoverable_err unless in the cleanup. - * The pattern for return error is set *err, optional infof, goto cleanup. - * - * Our priority is to always return as much decrypted data to the caller as - * possible, even if an error occurs. The state of the decrypted buffer must - * always be valid. Transfer of decrypted data to the caller's buffer is - * handled in the cleanup. - */ - - DEBUGF(infof(data, "schannel: client wants to read %zu bytes", len)); - *err = CURLE_OK; - - if(len && len <= backend->decdata_offset) { - infof(data, "schannel: enough decrypted data is already available"); - goto cleanup; - } - else if(backend->recv_unrecoverable_err) { - *err = backend->recv_unrecoverable_err; - infof(data, "schannel: an unrecoverable error occurred in a prior call"); - goto cleanup; - } - else if(backend->recv_sspi_close_notify) { - /* once a server has indicated shutdown there is no more encrypted data */ - infof(data, "schannel: server indicated shutdown in a prior call"); - goto cleanup; - } - - /* It's debatable what to return when !len. Regardless we can't return - immediately because there may be data to decrypt (in the case we want to - decrypt all encrypted cached data) so handle !len later in cleanup. - */ - else if(len && !backend->recv_connection_closed) { - /* increase enc buffer in order to fit the requested amount of data */ - size = backend->encdata_length - backend->encdata_offset; - if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE || - backend->encdata_length < min_encdata_length) { - reallocated_length = backend->encdata_offset + - CURL_SCHANNEL_BUFFER_FREE_SIZE; - if(reallocated_length < min_encdata_length) { - reallocated_length = min_encdata_length; - } - reallocated_buffer = realloc(backend->encdata_buffer, - reallocated_length); - if(!reallocated_buffer) { - *err = CURLE_OUT_OF_MEMORY; - failf(data, "schannel: unable to re-allocate memory"); - goto cleanup; - } - - backend->encdata_buffer = reallocated_buffer; - backend->encdata_length = reallocated_length; - size = backend->encdata_length - backend->encdata_offset; - DEBUGF(infof(data, "schannel: encdata_buffer resized %zu", - backend->encdata_length)); - } - - DEBUGF(infof(data, - "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); - - /* read encrypted data from socket */ - *err = Curl_read_plain(conn->sock[sockindex], - (char *)(backend->encdata_buffer + - backend->encdata_offset), - size, &nread); - if(*err) { - nread = -1; - if(*err == CURLE_AGAIN) - DEBUGF(infof(data, - "schannel: Curl_read_plain returned CURLE_AGAIN")); - else if(*err == CURLE_RECV_ERROR) - infof(data, "schannel: Curl_read_plain returned CURLE_RECV_ERROR"); - else - infof(data, "schannel: Curl_read_plain returned error %d", *err); - } - else if(nread == 0) { - backend->recv_connection_closed = true; - DEBUGF(infof(data, "schannel: server closed the connection")); - } - else if(nread > 0) { - backend->encdata_offset += (size_t)nread; - backend->encdata_is_incomplete = false; - DEBUGF(infof(data, "schannel: encrypted data got %zd", nread)); - } - } - - DEBUGF(infof(data, - "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); - - /* decrypt loop */ - while(backend->encdata_offset > 0 && sspi_status == SEC_E_OK && - (!len || backend->decdata_offset < len || - backend->recv_connection_closed)) { - /* prepare data buffer for DecryptMessage call */ - InitSecBuffer(&inbuf[0], SECBUFFER_DATA, backend->encdata_buffer, - curlx_uztoul(backend->encdata_offset)); - - /* we need 3 more empty input buffers for possible output */ - InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0); - InitSecBuffer(&inbuf[2], SECBUFFER_EMPTY, NULL, 0); - InitSecBuffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0); - InitSecBufferDesc(&inbuf_desc, inbuf, 4); - - /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx - */ - sspi_status = s_pSecFn->DecryptMessage(&backend->ctxt->ctxt_handle, - &inbuf_desc, 0, NULL); - - /* check if everything went fine (server may want to renegotiate - or shutdown the connection context) */ - if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE || - sspi_status == SEC_I_CONTEXT_EXPIRED) { - /* check for successfully decrypted data, even before actual - renegotiation or shutdown of the connection context */ - if(inbuf[1].BufferType == SECBUFFER_DATA) { - DEBUGF(infof(data, "schannel: decrypted data length: %lu", - inbuf[1].cbBuffer)); - - /* increase buffer in order to fit the received amount of data */ - size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ? - inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE; - if(backend->decdata_length - backend->decdata_offset < size || - backend->decdata_length < len) { - /* increase internal decrypted data buffer */ - reallocated_length = backend->decdata_offset + size; - /* make sure that the requested amount of data fits */ - if(reallocated_length < len) { - reallocated_length = len; - } - reallocated_buffer = realloc(backend->decdata_buffer, - reallocated_length); - if(!reallocated_buffer) { - *err = CURLE_OUT_OF_MEMORY; - failf(data, "schannel: unable to re-allocate memory"); - goto cleanup; - } - backend->decdata_buffer = reallocated_buffer; - backend->decdata_length = reallocated_length; - } - - /* copy decrypted data to internal buffer */ - size = inbuf[1].cbBuffer; - if(size) { - memcpy(backend->decdata_buffer + backend->decdata_offset, - inbuf[1].pvBuffer, size); - backend->decdata_offset += size; - } - - DEBUGF(infof(data, "schannel: decrypted data added: %zu", size)); - DEBUGF(infof(data, - "schannel: decrypted cached: offset %zu length %zu", - backend->decdata_offset, backend->decdata_length)); - } - - /* check for remaining encrypted data */ - if(inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) { - DEBUGF(infof(data, "schannel: encrypted data length: %lu", - inbuf[3].cbBuffer)); - - /* check if the remaining data is less than the total amount - * and therefore begins after the already processed data - */ - if(backend->encdata_offset > inbuf[3].cbBuffer) { - /* move remaining encrypted data forward to the beginning of - buffer */ - memmove(backend->encdata_buffer, - (backend->encdata_buffer + backend->encdata_offset) - - inbuf[3].cbBuffer, inbuf[3].cbBuffer); - backend->encdata_offset = inbuf[3].cbBuffer; - } - - DEBUGF(infof(data, - "schannel: encrypted cached: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); - } - else { - /* reset encrypted buffer offset, because there is no data remaining */ - backend->encdata_offset = 0; - } - - /* check if server wants to renegotiate the connection context */ - if(sspi_status == SEC_I_RENEGOTIATE) { - infof(data, "schannel: remote party requests renegotiation"); - if(*err && *err != CURLE_AGAIN) { - infof(data, "schannel: can't renegotiate, an error is pending"); - goto cleanup; - } - - /* begin renegotiation */ - infof(data, "schannel: renegotiating SSL/TLS connection"); - connssl->state = ssl_connection_negotiating; - connssl->connecting_state = ssl_connect_2_writing; - backend->recv_renegotiating = true; - *err = schannel_connect_common(data, conn, sockindex, FALSE, &done); - backend->recv_renegotiating = false; - if(*err) { - infof(data, "schannel: renegotiation failed"); - goto cleanup; - } - /* now retry receiving data */ - sspi_status = SEC_E_OK; - infof(data, "schannel: SSL/TLS connection renegotiated"); - continue; - } - /* check if the server closed the connection */ - else if(sspi_status == SEC_I_CONTEXT_EXPIRED) { - /* In Windows 2000 SEC_I_CONTEXT_EXPIRED (close_notify) is not - returned so we have to work around that in cleanup. */ - backend->recv_sspi_close_notify = true; - if(!backend->recv_connection_closed) { - backend->recv_connection_closed = true; - infof(data, "schannel: server closed the connection"); - } - goto cleanup; - } - } - else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { - backend->encdata_is_incomplete = true; - if(!*err) - *err = CURLE_AGAIN; - infof(data, "schannel: failed to decrypt data, need more data"); - goto cleanup; - } - else { -#ifndef CURL_DISABLE_VERBOSE_STRINGS - char buffer[STRERROR_LEN]; -#endif - *err = CURLE_RECV_ERROR; - infof(data, "schannel: failed to read data from server: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - goto cleanup; - } - } - - DEBUGF(infof(data, - "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); - - DEBUGF(infof(data, - "schannel: decrypted data buffer: offset %zu length %zu", - backend->decdata_offset, backend->decdata_length)); - - cleanup: - /* Warning- there is no guarantee the encdata state is valid at this point */ - DEBUGF(infof(data, "schannel: schannel_recv cleanup")); - - /* Error if the connection has closed without a close_notify. - - The behavior here is a matter of debate. We don't want to be vulnerable - to a truncation attack however there's some browser precedent for - ignoring the close_notify for compatibility reasons. - - Additionally, Windows 2000 (v5.0) is a special case since it seems it - doesn't return close_notify. In that case if the connection was closed we - assume it was graceful (close_notify) since there doesn't seem to be a - way to tell. - */ - if(len && !backend->decdata_offset && backend->recv_connection_closed && - !backend->recv_sspi_close_notify) { - bool isWin2k = curlx_verify_windows_version(5, 0, 0, PLATFORM_WINNT, - VERSION_EQUAL); - - if(isWin2k && sspi_status == SEC_E_OK) - backend->recv_sspi_close_notify = true; - else { - *err = CURLE_RECV_ERROR; - infof(data, "schannel: server closed abruptly (missing close_notify)"); - } - } - - /* Any error other than CURLE_AGAIN is an unrecoverable error. */ - if(*err && *err != CURLE_AGAIN) - backend->recv_unrecoverable_err = *err; - - size = len < backend->decdata_offset ? len : backend->decdata_offset; - if(size) { - memcpy(buf, backend->decdata_buffer, size); - memmove(backend->decdata_buffer, backend->decdata_buffer + size, - backend->decdata_offset - size); - backend->decdata_offset -= size; - DEBUGF(infof(data, "schannel: decrypted data returned %zu", size)); - DEBUGF(infof(data, - "schannel: decrypted data buffer: offset %zu length %zu", - backend->decdata_offset, backend->decdata_length)); - *err = CURLE_OK; - return (ssize_t)size; - } - - if(!*err && !backend->recv_connection_closed) - *err = CURLE_AGAIN; - - /* It's debatable what to return when !len. We could return whatever error - we got from decryption but instead we override here so the return is - consistent. - */ - if(!len) - *err = CURLE_OK; - - return *err ? -1 : 0; -} - -static CURLcode schannel_connect_nonblocking(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, bool *done) -{ - return schannel_connect_common(data, conn, sockindex, TRUE, done); -} - -static CURLcode schannel_connect(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - CURLcode result; - bool done = FALSE; - - result = schannel_connect_common(data, conn, sockindex, FALSE, &done); - if(result) - return result; - - DEBUGASSERT(done); - - return CURLE_OK; -} - -static bool schannel_data_pending(const struct connectdata *conn, - int sockindex) -{ - const struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - if(connssl->use) /* SSL/TLS is in use */ - return (backend->decdata_offset > 0 || - (backend->encdata_offset > 0 && !backend->encdata_is_incomplete)); - else - return FALSE; -} - -static void schannel_session_free(void *ptr) -{ - /* this is expected to be called under sessionid lock */ - struct Curl_schannel_cred *cred = ptr; - - if(cred) { - cred->refcount--; - if(cred->refcount == 0) { - s_pSecFn->FreeCredentialsHandle(&cred->cred_handle); - curlx_unicodefree(cred->sni_hostname); -#ifdef HAS_CLIENT_CERT_PATH - if(cred->client_cert_store) { - CertCloseStore(cred->client_cert_store, 0); - cred->client_cert_store = NULL; - } -#endif - Curl_safefree(cred); - } - } -} - -/* shut down the SSL connection and clean up related memory. - this function can be called multiple times on the same connection including - if the SSL connection failed (eg connection made but failed handshake). */ -static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - /* See https://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx - * Shutting Down an Schannel Connection - */ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - char * const hostname = SSL_HOST_NAME(); - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(data); - DEBUGASSERT(backend); - - if(connssl->use) { - infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu", - hostname, conn->remote_port); - } - - if(connssl->use && backend->cred && backend->ctxt) { - SecBufferDesc BuffDesc; - SecBuffer Buffer; - SECURITY_STATUS sspi_status; - SecBuffer outbuf; - SecBufferDesc outbuf_desc; - CURLcode result; - DWORD dwshut = SCHANNEL_SHUTDOWN; - - InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut)); - InitSecBufferDesc(&BuffDesc, &Buffer, 1); - - sspi_status = s_pSecFn->ApplyControlToken(&backend->ctxt->ctxt_handle, - &BuffDesc); - - if(sspi_status != SEC_E_OK) { - char buffer[STRERROR_LEN]; - failf(data, "schannel: ApplyControlToken failure: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - } - - /* setup output buffer */ - InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0); - InitSecBufferDesc(&outbuf_desc, &outbuf, 1); - - sspi_status = s_pSecFn->InitializeSecurityContext( - &backend->cred->cred_handle, - &backend->ctxt->ctxt_handle, - backend->cred->sni_hostname, - backend->req_flags, - 0, - 0, - NULL, - 0, - &backend->ctxt->ctxt_handle, - &outbuf_desc, - &backend->ret_flags, - &backend->ctxt->time_stamp); - - if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) { - /* send close message which is in output buffer */ - ssize_t written; - result = Curl_write_plain(data, conn->sock[sockindex], outbuf.pvBuffer, - outbuf.cbBuffer, &written); - - s_pSecFn->FreeContextBuffer(outbuf.pvBuffer); - if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) { - infof(data, "schannel: failed to send close msg: %s" - " (bytes written: %zd)", curl_easy_strerror(result), written); - } - } - } - - /* free SSPI Schannel API security context handle */ - if(backend->ctxt) { - DEBUGF(infof(data, "schannel: clear security context handle")); - s_pSecFn->DeleteSecurityContext(&backend->ctxt->ctxt_handle); - Curl_safefree(backend->ctxt); - } - - /* free SSPI Schannel API credential handle */ - if(backend->cred) { - Curl_ssl_sessionid_lock(data); - schannel_session_free(backend->cred); - Curl_ssl_sessionid_unlock(data); - backend->cred = NULL; - } - - /* free internal buffer for received encrypted data */ - if(backend->encdata_buffer) { - Curl_safefree(backend->encdata_buffer); - backend->encdata_length = 0; - backend->encdata_offset = 0; - backend->encdata_is_incomplete = false; - } - - /* free internal buffer for received decrypted data */ - if(backend->decdata_buffer) { - Curl_safefree(backend->decdata_buffer); - backend->decdata_length = 0; - backend->decdata_offset = 0; - } - - return CURLE_OK; -} - -static void schannel_close(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - if(conn->ssl[sockindex].use) - /* Curl_ssl_shutdown resets the socket state and calls schannel_shutdown */ - Curl_ssl_shutdown(data, conn, sockindex); - else - schannel_shutdown(data, conn, sockindex); -} - -static int schannel_init(void) -{ - return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0); -} - -static void schannel_cleanup(void) -{ - Curl_sspi_global_cleanup(); -} - -static size_t schannel_version(char *buffer, size_t size) -{ - size = msnprintf(buffer, size, "Schannel"); - - return size; -} - -static CURLcode schannel_random(struct Curl_easy *data UNUSED_PARAM, - unsigned char *entropy, size_t length) -{ - (void)data; - - return Curl_win32_random(entropy, length); -} - -static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, - struct connectdata *conn, int sockindex, - const char *pinnedpubkey) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - CERT_CONTEXT *pCertContextServer = NULL; - - /* Result is returned to caller */ - CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - - DEBUGASSERT(backend); - - /* if a path wasn't specified, don't pin */ - if(!pinnedpubkey) - return CURLE_OK; - - do { - SECURITY_STATUS sspi_status; - const char *x509_der; - DWORD x509_der_len; - struct Curl_X509certificate x509_parsed; - struct Curl_asn1Element *pubkey; - - sspi_status = - s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, - SECPKG_ATTR_REMOTE_CERT_CONTEXT, - &pCertContextServer); - - if((sspi_status != SEC_E_OK) || !pCertContextServer) { - char buffer[STRERROR_LEN]; - failf(data, "schannel: Failed to read remote certificate context: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - break; /* failed */ - } - - - if(!(((pCertContextServer->dwCertEncodingType & X509_ASN_ENCODING) != 0) && - (pCertContextServer->cbCertEncoded > 0))) - break; - - x509_der = (const char *)pCertContextServer->pbCertEncoded; - x509_der_len = pCertContextServer->cbCertEncoded; - memset(&x509_parsed, 0, sizeof(x509_parsed)); - if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len)) - break; - - pubkey = &x509_parsed.subjectPublicKeyInfo; - if(!pubkey->header || pubkey->end <= pubkey->header) { - failf(data, "SSL: failed retrieving public key from server certificate"); - break; - } - - result = Curl_pin_peer_pubkey(data, - pinnedpubkey, - (const unsigned char *)pubkey->header, - (size_t)(pubkey->end - pubkey->header)); - if(result) { - failf(data, "SSL: public key does not match pinned public key"); - } - } while(0); - - if(pCertContextServer) - CertFreeCertificateContext(pCertContextServer); - - return result; -} - -static void schannel_checksum(const unsigned char *input, - size_t inputlen, - unsigned char *checksum, - size_t checksumlen, - DWORD provType, - const unsigned int algId) -{ - HCRYPTPROV hProv = 0; - HCRYPTHASH hHash = 0; - DWORD cbHashSize = 0; - DWORD dwHashSizeLen = (DWORD)sizeof(cbHashSize); - DWORD dwChecksumLen = (DWORD)checksumlen; - - /* since this can fail in multiple ways, zero memory first so we never - * return old data - */ - memset(checksum, 0, checksumlen); - - if(!CryptAcquireContext(&hProv, NULL, NULL, provType, - CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) - return; /* failed */ - - do { - if(!CryptCreateHash(hProv, algId, 0, 0, &hHash)) - break; /* failed */ - - /* workaround for original MinGW, should be (const BYTE*) */ - if(!CryptHashData(hHash, (BYTE*)input, (DWORD)inputlen, 0)) - break; /* failed */ - - /* get hash size */ - if(!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&cbHashSize, - &dwHashSizeLen, 0)) - break; /* failed */ - - /* check hash size */ - if(checksumlen < cbHashSize) - break; /* failed */ - - if(CryptGetHashParam(hHash, HP_HASHVAL, checksum, &dwChecksumLen, 0)) - break; /* failed */ - } while(0); - - if(hHash) - CryptDestroyHash(hHash); - - if(hProv) - CryptReleaseContext(hProv, 0); -} - -static CURLcode schannel_sha256sum(const unsigned char *input, - size_t inputlen, - unsigned char *sha256sum, - size_t sha256len) -{ - schannel_checksum(input, inputlen, sha256sum, sha256len, - PROV_RSA_AES, CALG_SHA_256); - return CURLE_OK; -} - -static void *schannel_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - struct ssl_backend_data *backend = connssl->backend; - (void)info; - DEBUGASSERT(backend); - return &backend->ctxt->ctxt_handle; -} - -const struct Curl_ssl Curl_ssl_schannel = { - { CURLSSLBACKEND_SCHANNEL, "schannel" }, /* info */ - - SSLSUPP_CERTINFO | -#ifdef HAS_MANUAL_VERIFY_API - SSLSUPP_CAINFO_BLOB | -#endif - SSLSUPP_PINNEDPUBKEY | - SSLSUPP_TLS13_CIPHERSUITES, - - sizeof(struct ssl_backend_data), - - schannel_init, /* init */ - schannel_cleanup, /* cleanup */ - schannel_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ - schannel_shutdown, /* shutdown */ - schannel_data_pending, /* data_pending */ - schannel_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ - schannel_connect, /* connect */ - schannel_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_getsock, /* getsock */ - schannel_get_internals, /* get_internals */ - schannel_close, /* close_one */ - Curl_none_close_all, /* close_all */ - schannel_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ - schannel_sha256sum, /* sha256sum */ - NULL, /* associate_connection */ - NULL /* disassociate_connection */ -}; - -#endif /* USE_SCHANNEL */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de>
+ * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.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
+ *
+ ***************************************************************************/
+
+/*
+ * Source file for all Schannel-specific code for the TLS/SSL layer. No code
+ * but vtls.c should ever call or use these functions.
+ */
+
+#include "curl_setup.h"
+
+#ifdef USE_SCHANNEL
+
+#define EXPOSE_SCHANNEL_INTERNAL_STRUCTS
+
+#ifndef USE_WINDOWS_SSPI
+# error "Can't compile SCHANNEL support without SSPI."
+#endif
+
+#include "schannel.h"
+#include "vtls.h"
+#include "vtls_int.h"
+#include "strcase.h"
+#include "sendf.h"
+#include "connect.h" /* for the connect timeout */
+#include "strerror.h"
+#include "select.h" /* for the socket readiness */
+#include "inet_pton.h" /* for IP addr SNI check */
+#include "curl_multibyte.h"
+#include "warnless.h"
+#include "x509asn1.h"
+#include "curl_printf.h"
+#include "multiif.h"
+#include "version_win32.h"
+#include "rand.h"
+
+/* The last #include file should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* ALPN requires version 8.1 of the Windows SDK, which was
+ shipped with Visual Studio 2013, aka _MSC_VER 1800:
+
+ https://technet.microsoft.com/en-us/library/hh831771%28v=ws.11%29.aspx
+*/
+#if defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(_USING_V110_SDK71_)
+# define HAS_ALPN 1
+#endif
+
+#ifndef UNISP_NAME_A
+#define UNISP_NAME_A "Microsoft Unified Security Protocol Provider"
+#endif
+
+#ifndef UNISP_NAME_W
+#define UNISP_NAME_W L"Microsoft Unified Security Protocol Provider"
+#endif
+
+#ifndef UNISP_NAME
+#ifdef UNICODE
+#define UNISP_NAME UNISP_NAME_W
+#else
+#define UNISP_NAME UNISP_NAME_A
+#endif
+#endif
+
+#ifndef BCRYPT_CHACHA20_POLY1305_ALGORITHM
+#define BCRYPT_CHACHA20_POLY1305_ALGORITHM L"CHACHA20_POLY1305"
+#endif
+
+#ifndef BCRYPT_CHAIN_MODE_CCM
+#define BCRYPT_CHAIN_MODE_CCM L"ChainingModeCCM"
+#endif
+
+#ifndef BCRYPT_CHAIN_MODE_GCM
+#define BCRYPT_CHAIN_MODE_GCM L"ChainingModeGCM"
+#endif
+
+#ifndef BCRYPT_AES_ALGORITHM
+#define BCRYPT_AES_ALGORITHM L"AES"
+#endif
+
+#ifndef BCRYPT_SHA256_ALGORITHM
+#define BCRYPT_SHA256_ALGORITHM L"SHA256"
+#endif
+
+#ifndef BCRYPT_SHA384_ALGORITHM
+#define BCRYPT_SHA384_ALGORITHM L"SHA384"
+#endif
+
+/* Workaround broken compilers like MinGW.
+ Return the number of elements in a statically sized array.
+*/
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
+
+#ifdef HAS_CLIENT_CERT_PATH
+#ifdef UNICODE
+#define CURL_CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_W
+#else
+#define CURL_CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_A
+#endif
+#endif
+
+#ifndef SP_PROT_SSL2_CLIENT
+#define SP_PROT_SSL2_CLIENT 0x00000008
+#endif
+
+#ifndef SP_PROT_SSL3_CLIENT
+#define SP_PROT_SSL3_CLIENT 0x00000008
+#endif
+
+#ifndef SP_PROT_TLS1_CLIENT
+#define SP_PROT_TLS1_CLIENT 0x00000080
+#endif
+
+#ifndef SP_PROT_TLS1_0_CLIENT
+#define SP_PROT_TLS1_0_CLIENT SP_PROT_TLS1_CLIENT
+#endif
+
+#ifndef SP_PROT_TLS1_1_CLIENT
+#define SP_PROT_TLS1_1_CLIENT 0x00000200
+#endif
+
+#ifndef SP_PROT_TLS1_2_CLIENT
+#define SP_PROT_TLS1_2_CLIENT 0x00000800
+#endif
+
+#ifndef SP_PROT_TLS1_3_CLIENT
+#define SP_PROT_TLS1_3_CLIENT 0x00002000
+#endif
+
+#ifndef SCH_USE_STRONG_CRYPTO
+#define SCH_USE_STRONG_CRYPTO 0x00400000
+#endif
+
+#ifndef SECBUFFER_ALERT
+#define SECBUFFER_ALERT 17
+#endif
+
+/* Both schannel buffer sizes must be > 0 */
+#define CURL_SCHANNEL_BUFFER_INIT_SIZE 4096
+#define CURL_SCHANNEL_BUFFER_FREE_SIZE 1024
+
+#define CERT_THUMBPRINT_STR_LEN 40
+#define CERT_THUMBPRINT_DATA_LEN 20
+
+/* Uncomment to force verbose output
+ * #define infof(x, y, ...) printf(y, __VA_ARGS__)
+ * #define failf(x, y, ...) printf(y, __VA_ARGS__)
+ */
+
+#ifndef CALG_SHA_256
+# define CALG_SHA_256 0x0000800c
+#endif
+
+/* Work around typo in classic MinGW's w32api up to version 5.0,
+ see https://osdn.net/projects/mingw/ticket/38391 */
+#if !defined(ALG_CLASS_DHASH) && defined(ALG_CLASS_HASH)
+#define ALG_CLASS_DHASH ALG_CLASS_HASH
+#endif
+
+#ifndef PKCS12_NO_PERSIST_KEY
+#define PKCS12_NO_PERSIST_KEY 0x00008000
+#endif
+
+static CURLcode pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *pinnedpubkey);
+
+static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType,
+ void *BufDataPtr, unsigned long BufByteSize)
+{
+ buffer->cbBuffer = BufByteSize;
+ buffer->BufferType = BufType;
+ buffer->pvBuffer = BufDataPtr;
+}
+
+static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr,
+ unsigned long NumArrElem)
+{
+ desc->ulVersion = SECBUFFER_VERSION;
+ desc->pBuffers = BufArr;
+ desc->cBuffers = NumArrElem;
+}
+
+static CURLcode
+set_ssl_version_min_max(DWORD *enabled_protocols,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ long ssl_version = conn_config->version;
+ long ssl_version_max = conn_config->version_max;
+ long i = ssl_version;
+
+ switch(ssl_version_max) {
+ case CURL_SSLVERSION_MAX_NONE:
+ case CURL_SSLVERSION_MAX_DEFAULT:
+
+ /* Windows Server 2022 and newer (including Windows 11) support TLS 1.3
+ built-in. Previous builds of Windows 10 had broken TLS 1.3
+ implementations that could be enabled via registry.
+ */
+ if(curlx_verify_windows_version(10, 0, 20348, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL)) {
+ ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_3;
+ }
+ else /* Windows 10 and older */
+ ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2;
+
+ break;
+ }
+
+ for(; i <= (ssl_version_max >> 16); ++i) {
+ switch(i) {
+ case CURL_SSLVERSION_TLSv1_0:
+ (*enabled_protocols) |= SP_PROT_TLS1_0_CLIENT;
+ break;
+ case CURL_SSLVERSION_TLSv1_1:
+ (*enabled_protocols) |= SP_PROT_TLS1_1_CLIENT;
+ break;
+ case CURL_SSLVERSION_TLSv1_2:
+ (*enabled_protocols) |= SP_PROT_TLS1_2_CLIENT;
+ break;
+ case CURL_SSLVERSION_TLSv1_3:
+
+ /* Windows Server 2022 and newer */
+ if(curlx_verify_windows_version(10, 0, 20348, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL)) {
+ (*enabled_protocols) |= SP_PROT_TLS1_3_CLIENT;
+ break;
+ }
+ else { /* Windows 10 and older */
+ failf(data, "schannel: TLS 1.3 not supported on Windows prior to 11");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ }
+ return CURLE_OK;
+}
+
+/* longest is 26, buffer is slightly bigger */
+#define LONGEST_ALG_ID 32
+#define CIPHEROPTION(X) \
+ if(strcmp(#X, tmp) == 0) \
+ return X
+
+static int
+get_alg_id_by_name(char *name)
+{
+ char tmp[LONGEST_ALG_ID] = { 0 };
+ char *nameEnd = strchr(name, ':');
+ size_t n = nameEnd ? (size_t)(nameEnd - name) : strlen(name);
+
+ /* reject too-long alg names */
+ if(n > (LONGEST_ALG_ID - 1))
+ return 0;
+
+ strncpy(tmp, name, n);
+ tmp[n] = 0;
+ CIPHEROPTION(CALG_MD2);
+ CIPHEROPTION(CALG_MD4);
+ CIPHEROPTION(CALG_MD5);
+ CIPHEROPTION(CALG_SHA);
+ CIPHEROPTION(CALG_SHA1);
+ CIPHEROPTION(CALG_MAC);
+ CIPHEROPTION(CALG_RSA_SIGN);
+ CIPHEROPTION(CALG_DSS_SIGN);
+/* ifdefs for the options that are defined conditionally in wincrypt.h */
+#ifdef CALG_NO_SIGN
+ CIPHEROPTION(CALG_NO_SIGN);
+#endif
+ CIPHEROPTION(CALG_RSA_KEYX);
+ CIPHEROPTION(CALG_DES);
+#ifdef CALG_3DES_112
+ CIPHEROPTION(CALG_3DES_112);
+#endif
+ CIPHEROPTION(CALG_3DES);
+ CIPHEROPTION(CALG_DESX);
+ CIPHEROPTION(CALG_RC2);
+ CIPHEROPTION(CALG_RC4);
+ CIPHEROPTION(CALG_SEAL);
+#ifdef CALG_DH_SF
+ CIPHEROPTION(CALG_DH_SF);
+#endif
+ CIPHEROPTION(CALG_DH_EPHEM);
+#ifdef CALG_AGREEDKEY_ANY
+ CIPHEROPTION(CALG_AGREEDKEY_ANY);
+#endif
+#ifdef CALG_HUGHES_MD5
+ CIPHEROPTION(CALG_HUGHES_MD5);
+#endif
+ CIPHEROPTION(CALG_SKIPJACK);
+#ifdef CALG_TEK
+ CIPHEROPTION(CALG_TEK);
+#endif
+ CIPHEROPTION(CALG_CYLINK_MEK);
+ CIPHEROPTION(CALG_SSL3_SHAMD5);
+#ifdef CALG_SSL3_MASTER
+ CIPHEROPTION(CALG_SSL3_MASTER);
+#endif
+#ifdef CALG_SCHANNEL_MASTER_HASH
+ CIPHEROPTION(CALG_SCHANNEL_MASTER_HASH);
+#endif
+#ifdef CALG_SCHANNEL_MAC_KEY
+ CIPHEROPTION(CALG_SCHANNEL_MAC_KEY);
+#endif
+#ifdef CALG_SCHANNEL_ENC_KEY
+ CIPHEROPTION(CALG_SCHANNEL_ENC_KEY);
+#endif
+#ifdef CALG_PCT1_MASTER
+ CIPHEROPTION(CALG_PCT1_MASTER);
+#endif
+#ifdef CALG_SSL2_MASTER
+ CIPHEROPTION(CALG_SSL2_MASTER);
+#endif
+#ifdef CALG_TLS1_MASTER
+ CIPHEROPTION(CALG_TLS1_MASTER);
+#endif
+#ifdef CALG_RC5
+ CIPHEROPTION(CALG_RC5);
+#endif
+#ifdef CALG_HMAC
+ CIPHEROPTION(CALG_HMAC);
+#endif
+#ifdef CALG_TLS1PRF
+ CIPHEROPTION(CALG_TLS1PRF);
+#endif
+#ifdef CALG_HASH_REPLACE_OWF
+ CIPHEROPTION(CALG_HASH_REPLACE_OWF);
+#endif
+#ifdef CALG_AES_128
+ CIPHEROPTION(CALG_AES_128);
+#endif
+#ifdef CALG_AES_192
+ CIPHEROPTION(CALG_AES_192);
+#endif
+#ifdef CALG_AES_256
+ CIPHEROPTION(CALG_AES_256);
+#endif
+#ifdef CALG_AES
+ CIPHEROPTION(CALG_AES);
+#endif
+#ifdef CALG_SHA_256
+ CIPHEROPTION(CALG_SHA_256);
+#endif
+#ifdef CALG_SHA_384
+ CIPHEROPTION(CALG_SHA_384);
+#endif
+#ifdef CALG_SHA_512
+ CIPHEROPTION(CALG_SHA_512);
+#endif
+#ifdef CALG_ECDH
+ CIPHEROPTION(CALG_ECDH);
+#endif
+#ifdef CALG_ECMQV
+ CIPHEROPTION(CALG_ECMQV);
+#endif
+#ifdef CALG_ECDSA
+ CIPHEROPTION(CALG_ECDSA);
+#endif
+#ifdef CALG_ECDH_EPHEM
+ CIPHEROPTION(CALG_ECDH_EPHEM);
+#endif
+ return 0;
+}
+
+#define NUM_CIPHERS 47 /* There are 47 options listed above */
+
+static CURLcode
+set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers,
+ ALG_ID *algIds)
+{
+ char *startCur = ciphers;
+ int algCount = 0;
+ while(startCur && (0 != *startCur) && (algCount < NUM_CIPHERS)) {
+ long alg = strtol(startCur, 0, 0);
+ if(!alg)
+ alg = get_alg_id_by_name(startCur);
+ if(alg)
+ algIds[algCount++] = alg;
+ else if(!strncmp(startCur, "USE_STRONG_CRYPTO",
+ sizeof("USE_STRONG_CRYPTO") - 1) ||
+ !strncmp(startCur, "SCH_USE_STRONG_CRYPTO",
+ sizeof("SCH_USE_STRONG_CRYPTO") - 1))
+ schannel_cred->dwFlags |= SCH_USE_STRONG_CRYPTO;
+ else
+ return CURLE_SSL_CIPHER;
+ startCur = strchr(startCur, ':');
+ if(startCur)
+ startCur++;
+ }
+ schannel_cred->palgSupportedAlgs = algIds;
+ schannel_cred->cSupportedAlgs = algCount;
+ return CURLE_OK;
+}
+
+#ifdef HAS_CLIENT_CERT_PATH
+
+/* Function allocates memory for store_path only if CURLE_OK is returned */
+static CURLcode
+get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path,
+ TCHAR **thumbprint)
+{
+ TCHAR *sep;
+ TCHAR *store_path_start;
+ size_t store_name_len;
+
+ sep = _tcschr(path, TEXT('\\'));
+ if(!sep)
+ return CURLE_SSL_CERTPROBLEM;
+
+ store_name_len = sep - path;
+
+ if(_tcsncmp(path, TEXT("CurrentUser"), store_name_len) == 0)
+ *store_name = CERT_SYSTEM_STORE_CURRENT_USER;
+ else if(_tcsncmp(path, TEXT("LocalMachine"), store_name_len) == 0)
+ *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE;
+ else if(_tcsncmp(path, TEXT("CurrentService"), store_name_len) == 0)
+ *store_name = CERT_SYSTEM_STORE_CURRENT_SERVICE;
+ else if(_tcsncmp(path, TEXT("Services"), store_name_len) == 0)
+ *store_name = CERT_SYSTEM_STORE_SERVICES;
+ else if(_tcsncmp(path, TEXT("Users"), store_name_len) == 0)
+ *store_name = CERT_SYSTEM_STORE_USERS;
+ else if(_tcsncmp(path, TEXT("CurrentUserGroupPolicy"),
+ store_name_len) == 0)
+ *store_name = CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY;
+ else if(_tcsncmp(path, TEXT("LocalMachineGroupPolicy"),
+ store_name_len) == 0)
+ *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY;
+ else if(_tcsncmp(path, TEXT("LocalMachineEnterprise"),
+ store_name_len) == 0)
+ *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE;
+ else
+ return CURLE_SSL_CERTPROBLEM;
+
+ store_path_start = sep + 1;
+
+ sep = _tcschr(store_path_start, TEXT('\\'));
+ if(!sep)
+ return CURLE_SSL_CERTPROBLEM;
+
+ *thumbprint = sep + 1;
+ if(_tcslen(*thumbprint) != CERT_THUMBPRINT_STR_LEN)
+ return CURLE_SSL_CERTPROBLEM;
+
+ *sep = TEXT('\0');
+ *store_path = _tcsdup(store_path_start);
+ *sep = TEXT('\\');
+ if(!*store_path)
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_OK;
+}
+#endif
+static CURLcode
+schannel_acquire_credential_handle(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+
+#ifdef HAS_CLIENT_CERT_PATH
+ PCCERT_CONTEXT client_certs[1] = { NULL };
+ HCERTSTORE client_cert_store = NULL;
+#endif
+ SECURITY_STATUS sspi_status = SEC_E_OK;
+ CURLcode result;
+
+ /* setup Schannel API options */
+ DWORD flags = 0;
+ DWORD enabled_protocols = 0;
+
+ struct ssl_backend_data *backend = connssl->backend;
+
+ DEBUGASSERT(backend);
+
+ if(conn_config->verifypeer) {
+#ifdef HAS_MANUAL_VERIFY_API
+ if(backend->use_manual_cred_validation)
+ flags = SCH_CRED_MANUAL_CRED_VALIDATION;
+ else
+#endif
+ flags = SCH_CRED_AUTO_CRED_VALIDATION;
+
+ if(ssl_config->no_revoke) {
+ flags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+ SCH_CRED_IGNORE_REVOCATION_OFFLINE;
+
+ DEBUGF(infof(data, "schannel: disabled server certificate revocation "
+ "checks"));
+ }
+ else if(ssl_config->revoke_best_effort) {
+ flags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+ SCH_CRED_IGNORE_REVOCATION_OFFLINE | SCH_CRED_REVOCATION_CHECK_CHAIN;
+
+ DEBUGF(infof(data, "schannel: ignore revocation offline errors"));
+ }
+ else {
+ flags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
+
+ DEBUGF(infof(data,
+ "schannel: checking server certificate revocation"));
+ }
+ }
+ else {
+ flags = SCH_CRED_MANUAL_CRED_VALIDATION |
+ SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+ SCH_CRED_IGNORE_REVOCATION_OFFLINE;
+ DEBUGF(infof(data,
+ "schannel: disabled server cert revocation checks"));
+ }
+
+ if(!conn_config->verifyhost) {
+ flags |= SCH_CRED_NO_SERVERNAME_CHECK;
+ DEBUGF(infof(data, "schannel: verifyhost setting prevents Schannel from "
+ "comparing the supplied target name with the subject "
+ "names in server certificates."));
+ }
+
+ if(!ssl_config->auto_client_cert) {
+ flags &= ~SCH_CRED_USE_DEFAULT_CREDS;
+ flags |= SCH_CRED_NO_DEFAULT_CREDS;
+ infof(data, "schannel: disabled automatic use of client certificate");
+ }
+ else
+ infof(data, "schannel: enabled automatic use of client certificate");
+
+ switch(conn_config->version) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+ case CURL_SSLVERSION_TLSv1_0:
+ case CURL_SSLVERSION_TLSv1_1:
+ case CURL_SSLVERSION_TLSv1_2:
+ case CURL_SSLVERSION_TLSv1_3:
+ {
+ result = set_ssl_version_min_max(&enabled_protocols, cf, data);
+ if(result != CURLE_OK)
+ return result;
+ break;
+ }
+ case CURL_SSLVERSION_SSLv3:
+ case CURL_SSLVERSION_SSLv2:
+ failf(data, "SSL versions not supported");
+ return CURLE_NOT_BUILT_IN;
+ default:
+ failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+#ifdef HAS_CLIENT_CERT_PATH
+ /* client certificate */
+ if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) {
+ DWORD cert_store_name = 0;
+ TCHAR *cert_store_path = NULL;
+ TCHAR *cert_thumbprint_str = NULL;
+ CRYPT_HASH_BLOB cert_thumbprint;
+ BYTE cert_thumbprint_data[CERT_THUMBPRINT_DATA_LEN];
+ HCERTSTORE cert_store = NULL;
+ FILE *fInCert = NULL;
+ void *certdata = NULL;
+ size_t certsize = 0;
+ bool blob = data->set.ssl.primary.cert_blob != NULL;
+ TCHAR *cert_path = NULL;
+ if(blob) {
+ certdata = data->set.ssl.primary.cert_blob->data;
+ certsize = data->set.ssl.primary.cert_blob->len;
+ }
+ else {
+ cert_path = curlx_convert_UTF8_to_tchar(
+ data->set.ssl.primary.clientcert);
+ if(!cert_path)
+ return CURLE_OUT_OF_MEMORY;
+
+ result = get_cert_location(cert_path, &cert_store_name,
+ &cert_store_path, &cert_thumbprint_str);
+
+ if(result && (data->set.ssl.primary.clientcert[0]!='\0'))
+ fInCert = fopen(data->set.ssl.primary.clientcert, "rb");
+
+ if(result && !fInCert) {
+ failf(data, "schannel: Failed to get certificate location"
+ " or file for %s",
+ data->set.ssl.primary.clientcert);
+ curlx_unicodefree(cert_path);
+ return result;
+ }
+ }
+
+ if((fInCert || blob) && (data->set.ssl.cert_type) &&
+ (!strcasecompare(data->set.ssl.cert_type, "P12"))) {
+ failf(data, "schannel: certificate format compatibility error "
+ " for %s",
+ blob ? "(memory blob)" : data->set.ssl.primary.clientcert);
+ curlx_unicodefree(cert_path);
+ return CURLE_SSL_CERTPROBLEM;
+ }
+
+ if(fInCert || blob) {
+ /* Reading a .P12 or .pfx file, like the example at bottom of
+ https://social.msdn.microsoft.com/Forums/windowsdesktop/
+ en-US/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5
+ */
+ CRYPT_DATA_BLOB datablob;
+ WCHAR* pszPassword;
+ size_t pwd_len = 0;
+ int str_w_len = 0;
+ const char *cert_showfilename_error = blob ?
+ "(memory blob)" : data->set.ssl.primary.clientcert;
+ curlx_unicodefree(cert_path);
+ if(fInCert) {
+ long cert_tell = 0;
+ bool continue_reading = fseek(fInCert, 0, SEEK_END) == 0;
+ if(continue_reading)
+ cert_tell = ftell(fInCert);
+ if(cert_tell < 0)
+ continue_reading = FALSE;
+ else
+ certsize = (size_t)cert_tell;
+ if(continue_reading)
+ continue_reading = fseek(fInCert, 0, SEEK_SET) == 0;
+ if(continue_reading)
+ certdata = malloc(certsize + 1);
+ if((!certdata) ||
+ ((int) fread(certdata, certsize, 1, fInCert) != 1))
+ continue_reading = FALSE;
+ fclose(fInCert);
+ if(!continue_reading) {
+ failf(data, "schannel: Failed to read cert file %s",
+ data->set.ssl.primary.clientcert);
+ free(certdata);
+ return CURLE_SSL_CERTPROBLEM;
+ }
+ }
+
+ /* Convert key-pair data to the in-memory certificate store */
+ datablob.pbData = (BYTE*)certdata;
+ datablob.cbData = (DWORD)certsize;
+
+ if(data->set.ssl.key_passwd)
+ pwd_len = strlen(data->set.ssl.key_passwd);
+ pszPassword = (WCHAR*)malloc(sizeof(WCHAR)*(pwd_len + 1));
+ if(pszPassword) {
+ if(pwd_len > 0)
+ str_w_len = MultiByteToWideChar(CP_UTF8,
+ MB_ERR_INVALID_CHARS,
+ data->set.ssl.key_passwd,
+ (int)pwd_len,
+ pszPassword, (int)(pwd_len + 1));
+
+ if((str_w_len >= 0) && (str_w_len <= (int)pwd_len))
+ pszPassword[str_w_len] = 0;
+ else
+ pszPassword[0] = 0;
+
+ if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL))
+ cert_store = PFXImportCertStore(&datablob, pszPassword,
+ PKCS12_NO_PERSIST_KEY);
+ else
+ cert_store = PFXImportCertStore(&datablob, pszPassword, 0);
+
+ free(pszPassword);
+ }
+ if(!blob)
+ free(certdata);
+ if(!cert_store) {
+ DWORD errorcode = GetLastError();
+ if(errorcode == ERROR_INVALID_PASSWORD)
+ failf(data, "schannel: Failed to import cert file %s, "
+ "password is bad",
+ cert_showfilename_error);
+ else
+ failf(data, "schannel: Failed to import cert file %s, "
+ "last error is 0x%x",
+ cert_showfilename_error, errorcode);
+ return CURLE_SSL_CERTPROBLEM;
+ }
+
+ client_certs[0] = CertFindCertificateInStore(
+ cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0,
+ CERT_FIND_ANY, NULL, NULL);
+
+ if(!client_certs[0]) {
+ failf(data, "schannel: Failed to get certificate from file %s"
+ ", last error is 0x%x",
+ cert_showfilename_error, GetLastError());
+ CertCloseStore(cert_store, 0);
+ return CURLE_SSL_CERTPROBLEM;
+ }
+ }
+ else {
+ cert_store =
+ CertOpenStore(CURL_CERT_STORE_PROV_SYSTEM, 0,
+ (HCRYPTPROV)NULL,
+ CERT_STORE_OPEN_EXISTING_FLAG | cert_store_name,
+ cert_store_path);
+ if(!cert_store) {
+ failf(data, "schannel: Failed to open cert store %x %s, "
+ "last error is 0x%x",
+ cert_store_name, cert_store_path, GetLastError());
+ free(cert_store_path);
+ curlx_unicodefree(cert_path);
+ return CURLE_SSL_CERTPROBLEM;
+ }
+ free(cert_store_path);
+
+ cert_thumbprint.pbData = cert_thumbprint_data;
+ cert_thumbprint.cbData = CERT_THUMBPRINT_DATA_LEN;
+
+ if(!CryptStringToBinary(cert_thumbprint_str,
+ CERT_THUMBPRINT_STR_LEN,
+ CRYPT_STRING_HEX,
+ cert_thumbprint_data,
+ &cert_thumbprint.cbData,
+ NULL, NULL)) {
+ curlx_unicodefree(cert_path);
+ CertCloseStore(cert_store, 0);
+ return CURLE_SSL_CERTPROBLEM;
+ }
+
+ client_certs[0] = CertFindCertificateInStore(
+ cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0,
+ CERT_FIND_HASH, &cert_thumbprint, NULL);
+
+ curlx_unicodefree(cert_path);
+
+ if(!client_certs[0]) {
+ /* CRYPT_E_NOT_FOUND / E_INVALIDARG */
+ CertCloseStore(cert_store, 0);
+ return CURLE_SSL_CERTPROBLEM;
+ }
+ }
+ client_cert_store = cert_store;
+ }
+#else
+ if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) {
+ failf(data, "schannel: client cert support not built in");
+ return CURLE_NOT_BUILT_IN;
+ }
+#endif
+
+ /* allocate memory for the re-usable credential handle */
+ backend->cred = (struct Curl_schannel_cred *)
+ calloc(1, sizeof(struct Curl_schannel_cred));
+ if(!backend->cred) {
+ failf(data, "schannel: unable to allocate memory");
+
+#ifdef HAS_CLIENT_CERT_PATH
+ if(client_certs[0])
+ CertFreeCertificateContext(client_certs[0]);
+ if(client_cert_store)
+ CertCloseStore(client_cert_store, 0);
+#endif
+
+ return CURLE_OUT_OF_MEMORY;
+ }
+ backend->cred->refcount = 1;
+
+#ifdef HAS_CLIENT_CERT_PATH
+ /* Since we did not persist the key, we need to extend the store's
+ * lifetime until the end of the connection
+ */
+ backend->cred->client_cert_store = client_cert_store;
+#endif
+
+ /* Windows 10, 1809 (a.k.a. Windows 10 build 17763) */
+ if(curlx_verify_windows_version(10, 0, 17763, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL)) {
+
+ char *ciphers13 = 0;
+
+ bool disable_aes_gcm_sha384 = FALSE;
+ bool disable_aes_gcm_sha256 = FALSE;
+ bool disable_chacha_poly = FALSE;
+ bool disable_aes_ccm_8_sha256 = FALSE;
+ bool disable_aes_ccm_sha256 = FALSE;
+
+ SCH_CREDENTIALS credentials = { 0 };
+ TLS_PARAMETERS tls_parameters = { 0 };
+ CRYPTO_SETTINGS crypto_settings[4] = { 0 };
+ UNICODE_STRING blocked_ccm_modes[1] = { 0 };
+ UNICODE_STRING blocked_gcm_modes[1] = { 0 };
+
+ int crypto_settings_idx = 0;
+
+
+ /* If TLS 1.3 ciphers are explicitly listed, then
+ * disable all the ciphers and re-enable which
+ * ciphers the user has provided.
+ */
+ ciphers13 = conn_config->cipher_list13;
+ if(ciphers13) {
+ const int remaining_ciphers = 5;
+
+ /* detect which remaining ciphers to enable
+ and then disable everything else.
+ */
+
+ char *startCur = ciphers13;
+ int algCount = 0;
+ char tmp[LONGEST_ALG_ID] = { 0 };
+ char *nameEnd;
+ size_t n;
+
+ disable_aes_gcm_sha384 = TRUE;
+ disable_aes_gcm_sha256 = TRUE;
+ disable_chacha_poly = TRUE;
+ disable_aes_ccm_8_sha256 = TRUE;
+ disable_aes_ccm_sha256 = TRUE;
+
+ while(startCur && (0 != *startCur) && (algCount < remaining_ciphers)) {
+ nameEnd = strchr(startCur, ':');
+ n = nameEnd ? (size_t)(nameEnd - startCur) : strlen(startCur);
+
+ /* reject too-long cipher names */
+ if(n > (LONGEST_ALG_ID - 1)) {
+ failf(data, "Cipher name too long, not checked.");
+ return CURLE_SSL_CIPHER;
+ }
+
+ strncpy(tmp, startCur, n);
+ tmp[n] = 0;
+
+ if(disable_aes_gcm_sha384
+ && !strcmp("TLS_AES_256_GCM_SHA384", tmp)) {
+ disable_aes_gcm_sha384 = FALSE;
+ }
+ else if(disable_aes_gcm_sha256
+ && !strcmp("TLS_AES_128_GCM_SHA256", tmp)) {
+ disable_aes_gcm_sha256 = FALSE;
+ }
+ else if(disable_chacha_poly
+ && !strcmp("TLS_CHACHA20_POLY1305_SHA256", tmp)) {
+ disable_chacha_poly = FALSE;
+ }
+ else if(disable_aes_ccm_8_sha256
+ && !strcmp("TLS_AES_128_CCM_8_SHA256", tmp)) {
+ disable_aes_ccm_8_sha256 = FALSE;
+ }
+ else if(disable_aes_ccm_sha256
+ && !strcmp("TLS_AES_128_CCM_SHA256", tmp)) {
+ disable_aes_ccm_sha256 = FALSE;
+ }
+ else {
+ failf(data, "Passed in an unknown TLS 1.3 cipher.");
+ return CURLE_SSL_CIPHER;
+ }
+
+ startCur = nameEnd;
+ if(startCur)
+ startCur++;
+
+ algCount++;
+ }
+ }
+
+ if(disable_aes_gcm_sha384 && disable_aes_gcm_sha256
+ && disable_chacha_poly && disable_aes_ccm_8_sha256
+ && disable_aes_ccm_sha256) {
+ failf(data, "All available TLS 1.3 ciphers were disabled.");
+ return CURLE_SSL_CIPHER;
+ }
+
+ /* Disable TLS_AES_128_CCM_8_SHA256 and/or TLS_AES_128_CCM_SHA256 */
+ if(disable_aes_ccm_8_sha256 || disable_aes_ccm_sha256) {
+ /*
+ Disallow AES_CCM algorithm.
+ */
+ blocked_ccm_modes[0].Length = sizeof(BCRYPT_CHAIN_MODE_CCM);
+ blocked_ccm_modes[0].MaximumLength = sizeof(BCRYPT_CHAIN_MODE_CCM);
+ blocked_ccm_modes[0].Buffer = (PWSTR)BCRYPT_CHAIN_MODE_CCM;
+
+ crypto_settings[crypto_settings_idx].eAlgorithmUsage =
+ TlsParametersCngAlgUsageCipher;
+ crypto_settings[crypto_settings_idx].rgstrChainingModes =
+ blocked_ccm_modes;
+ crypto_settings[crypto_settings_idx].cChainingModes =
+ ARRAYSIZE(blocked_ccm_modes);
+ crypto_settings[crypto_settings_idx].strCngAlgId.Length =
+ sizeof(BCRYPT_AES_ALGORITHM);
+ crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength =
+ sizeof(BCRYPT_AES_ALGORITHM);
+ crypto_settings[crypto_settings_idx].strCngAlgId.Buffer =
+ (PWSTR)BCRYPT_AES_ALGORITHM;
+
+ /* only disabling one of the CCM modes */
+ if(disable_aes_ccm_8_sha256 != disable_aes_ccm_sha256) {
+ if(disable_aes_ccm_8_sha256)
+ crypto_settings[crypto_settings_idx].dwMinBitLength = 128;
+ else /* disable_aes_ccm_sha256 */
+ crypto_settings[crypto_settings_idx].dwMaxBitLength = 64;
+ }
+
+ crypto_settings_idx++;
+ }
+
+ /* Disable TLS_AES_256_GCM_SHA384 and/or TLS_AES_128_GCM_SHA256 */
+ if(disable_aes_gcm_sha384 || disable_aes_gcm_sha256) {
+
+ /*
+ Disallow AES_GCM algorithm
+ */
+ blocked_gcm_modes[0].Length = sizeof(BCRYPT_CHAIN_MODE_GCM);
+ blocked_gcm_modes[0].MaximumLength = sizeof(BCRYPT_CHAIN_MODE_GCM);
+ blocked_gcm_modes[0].Buffer = (PWSTR)BCRYPT_CHAIN_MODE_GCM;
+
+ /* if only one is disabled, then explicitly disable the
+ digest cipher suite (sha384 or sha256) */
+ if(disable_aes_gcm_sha384 != disable_aes_gcm_sha256) {
+ crypto_settings[crypto_settings_idx].eAlgorithmUsage =
+ TlsParametersCngAlgUsageDigest;
+ crypto_settings[crypto_settings_idx].strCngAlgId.Length =
+ sizeof(disable_aes_gcm_sha384 ?
+ BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM);
+ crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength =
+ sizeof(disable_aes_gcm_sha384 ?
+ BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM);
+ crypto_settings[crypto_settings_idx].strCngAlgId.Buffer =
+ (PWSTR)(disable_aes_gcm_sha384 ?
+ BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM);
+ }
+ else { /* Disable both AES_GCM ciphers */
+ crypto_settings[crypto_settings_idx].eAlgorithmUsage =
+ TlsParametersCngAlgUsageCipher;
+ crypto_settings[crypto_settings_idx].strCngAlgId.Length =
+ sizeof(BCRYPT_AES_ALGORITHM);
+ crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength =
+ sizeof(BCRYPT_AES_ALGORITHM);
+ crypto_settings[crypto_settings_idx].strCngAlgId.Buffer =
+ (PWSTR)BCRYPT_AES_ALGORITHM;
+ }
+
+ crypto_settings[crypto_settings_idx].rgstrChainingModes =
+ blocked_gcm_modes;
+ crypto_settings[crypto_settings_idx].cChainingModes = 1;
+
+ crypto_settings_idx++;
+ }
+
+ /*
+ Disable ChaCha20-Poly1305.
+ */
+ if(disable_chacha_poly) {
+ crypto_settings[crypto_settings_idx].eAlgorithmUsage =
+ TlsParametersCngAlgUsageCipher;
+ crypto_settings[crypto_settings_idx].strCngAlgId.Length =
+ sizeof(BCRYPT_CHACHA20_POLY1305_ALGORITHM);
+ crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength =
+ sizeof(BCRYPT_CHACHA20_POLY1305_ALGORITHM);
+ crypto_settings[crypto_settings_idx].strCngAlgId.Buffer =
+ (PWSTR)BCRYPT_CHACHA20_POLY1305_ALGORITHM;
+ crypto_settings_idx++;
+ }
+
+ tls_parameters.pDisabledCrypto = crypto_settings;
+
+ /* The number of blocked suites */
+ tls_parameters.cDisabledCrypto = crypto_settings_idx;
+ credentials.pTlsParameters = &tls_parameters;
+ credentials.cTlsParameters = 1;
+
+ credentials.dwVersion = SCH_CREDENTIALS_VERSION;
+ credentials.dwFlags = flags | SCH_USE_STRONG_CRYPTO;
+
+ credentials.pTlsParameters->grbitDisabledProtocols =
+ (DWORD)~enabled_protocols;
+
+#ifdef HAS_CLIENT_CERT_PATH
+ if(client_certs[0]) {
+ credentials.cCreds = 1;
+ credentials.paCred = client_certs;
+ }
+#endif
+
+ sspi_status =
+ s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME,
+ SECPKG_CRED_OUTBOUND, NULL,
+ &credentials, NULL, NULL,
+ &backend->cred->cred_handle,
+ &backend->cred->time_stamp);
+ }
+ else {
+ /* Pre-Windows 10 1809 */
+ ALG_ID algIds[NUM_CIPHERS];
+ char *ciphers = conn_config->cipher_list;
+ SCHANNEL_CRED schannel_cred = { 0 };
+ schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
+ schannel_cred.dwFlags = flags;
+ schannel_cred.grbitEnabledProtocols = enabled_protocols;
+
+ if(ciphers) {
+ result = set_ssl_ciphers(&schannel_cred, ciphers, algIds);
+ if(CURLE_OK != result) {
+ failf(data, "Unable to set ciphers to from connection ssl config");
+ return result;
+ }
+ }
+ else {
+ schannel_cred.dwFlags = flags | SCH_USE_STRONG_CRYPTO;
+ }
+
+#ifdef HAS_CLIENT_CERT_PATH
+ if(client_certs[0]) {
+ schannel_cred.cCreds = 1;
+ schannel_cred.paCred = client_certs;
+ }
+#endif
+
+ sspi_status =
+ s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME,
+ SECPKG_CRED_OUTBOUND, NULL,
+ &schannel_cred, NULL, NULL,
+ &backend->cred->cred_handle,
+ &backend->cred->time_stamp);
+ }
+
+#ifdef HAS_CLIENT_CERT_PATH
+ if(client_certs[0])
+ CertFreeCertificateContext(client_certs[0]);
+#endif
+
+ if(sspi_status != SEC_E_OK) {
+ char buffer[STRERROR_LEN];
+ failf(data, "schannel: AcquireCredentialsHandle failed: %s",
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+ Curl_safefree(backend->cred);
+ switch(sspi_status) {
+ case SEC_E_INSUFFICIENT_MEMORY:
+ return CURLE_OUT_OF_MEMORY;
+ case SEC_E_NO_CREDENTIALS:
+ case SEC_E_SECPKG_NOT_FOUND:
+ case SEC_E_NOT_OWNER:
+ case SEC_E_UNKNOWN_CREDENTIALS:
+ case SEC_E_INTERNAL_ERROR:
+ default:
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode
+schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ ssize_t written = -1;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ SecBuffer outbuf;
+ SecBufferDesc outbuf_desc;
+ SecBuffer inbuf;
+ SecBufferDesc inbuf_desc;
+#ifdef HAS_ALPN
+ unsigned char alpn_buffer[128];
+#endif
+ SECURITY_STATUS sspi_status = SEC_E_OK;
+ struct Curl_schannel_cred *old_cred = NULL;
+ struct in_addr addr;
+#ifdef ENABLE_IPV6
+ struct in6_addr addr6;
+#endif
+ CURLcode result;
+ const char *hostname = connssl->hostname;
+
+ DEBUGASSERT(backend);
+ DEBUGF(infof(data,
+ "schannel: SSL/TLS connection with %s port %d (step 1/3)",
+ hostname, connssl->port));
+
+ if(curlx_verify_windows_version(5, 1, 0, PLATFORM_WINNT,
+ VERSION_LESS_THAN_EQUAL)) {
+ /* Schannel in Windows XP (OS version 5.1) uses legacy handshakes and
+ algorithms that may not be supported by all servers. */
+ infof(data, "schannel: Windows version is old and may not be able to "
+ "connect to some servers due to lack of SNI, algorithms, etc.");
+ }
+
+#ifdef HAS_ALPN
+ /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above.
+ Also it doesn't seem to be supported for Wine, see curl bug #983. */
+ backend->use_alpn = cf->conn->bits.tls_enable_alpn &&
+ !GetProcAddress(GetModuleHandle(TEXT("ntdll")),
+ "wine_get_version") &&
+ curlx_verify_windows_version(6, 3, 0, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL);
+#else
+ backend->use_alpn = false;
+#endif
+
+#ifdef _WIN32_WCE
+#ifdef HAS_MANUAL_VERIFY_API
+ /* certificate validation on CE doesn't seem to work right; we'll
+ * do it following a more manual process. */
+ backend->use_manual_cred_validation = true;
+#else
+#error "compiler too old to support requisite manual cert verify for Win CE"
+#endif
+#else
+#ifdef HAS_MANUAL_VERIFY_API
+ if(conn_config->CAfile || conn_config->ca_info_blob) {
+ if(curlx_verify_windows_version(6, 1, 0, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL)) {
+ backend->use_manual_cred_validation = true;
+ }
+ else {
+ failf(data, "schannel: this version of Windows is too old to support "
+ "certificate verification via CA bundle file.");
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ }
+ else
+ backend->use_manual_cred_validation = false;
+#else
+ if(conn_config->CAfile || conn_config->ca_info_blob) {
+ failf(data, "schannel: CA cert support not built in");
+ return CURLE_NOT_BUILT_IN;
+ }
+#endif
+#endif
+
+ backend->cred = NULL;
+
+ /* check for an existing re-usable credential handle */
+ if(ssl_config->primary.sessionid) {
+ Curl_ssl_sessionid_lock(data);
+ if(!Curl_ssl_getsessionid(cf, data, (void **)&old_cred, NULL)) {
+ backend->cred = old_cred;
+ DEBUGF(infof(data, "schannel: re-using existing credential handle"));
+
+ /* increment the reference counter of the credential/session handle */
+ backend->cred->refcount++;
+ DEBUGF(infof(data,
+ "schannel: incremented credential handle refcount = %d",
+ backend->cred->refcount));
+ }
+ Curl_ssl_sessionid_unlock(data);
+ }
+
+ if(!backend->cred) {
+ char *snihost;
+ result = schannel_acquire_credential_handle(cf, data);
+ if(result != CURLE_OK) {
+ return result;
+ }
+ /* A hostname associated with the credential is needed by
+ InitializeSecurityContext for SNI and other reasons. */
+ snihost = Curl_ssl_snihost(data, hostname, NULL);
+ if(!snihost) {
+ failf(data, "Failed to set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ backend->cred->sni_hostname = curlx_convert_UTF8_to_tchar(snihost);
+ if(!backend->cred->sni_hostname)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* Warn if SNI is disabled due to use of an IP address */
+ if(Curl_inet_pton(AF_INET, hostname, &addr)
+#ifdef ENABLE_IPV6
+ || Curl_inet_pton(AF_INET6, hostname, &addr6)
+#endif
+ ) {
+ infof(data, "schannel: using IP address, SNI is not supported by OS.");
+ }
+
+#ifdef HAS_ALPN
+ if(backend->use_alpn) {
+ int cur = 0;
+ int list_start_index = 0;
+ unsigned int *extension_len = NULL;
+ unsigned short* list_len = NULL;
+
+ /* The first four bytes will be an unsigned int indicating number
+ of bytes of data in the rest of the buffer. */
+ extension_len = (unsigned int *)(void *)(&alpn_buffer[cur]);
+ cur += sizeof(unsigned int);
+
+ /* The next four bytes are an indicator that this buffer will contain
+ ALPN data, as opposed to NPN, for example. */
+ *(unsigned int *)(void *)&alpn_buffer[cur] =
+ SecApplicationProtocolNegotiationExt_ALPN;
+ cur += sizeof(unsigned int);
+
+ /* The next two bytes will be an unsigned short indicating the number
+ of bytes used to list the preferred protocols. */
+ list_len = (unsigned short*)(void *)(&alpn_buffer[cur]);
+ cur += sizeof(unsigned short);
+
+ list_start_index = cur;
+
+#ifdef USE_HTTP2
+ if(data->state.httpwant >= CURL_HTTP_VERSION_2) {
+ alpn_buffer[cur++] = ALPN_H2_LENGTH;
+ memcpy(&alpn_buffer[cur], ALPN_H2, ALPN_H2_LENGTH);
+ cur += ALPN_H2_LENGTH;
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
+ }
+#endif
+
+ alpn_buffer[cur++] = ALPN_HTTP_1_1_LENGTH;
+ memcpy(&alpn_buffer[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
+ cur += ALPN_HTTP_1_1_LENGTH;
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
+
+ *list_len = curlx_uitous(cur - list_start_index);
+ *extension_len = *list_len + sizeof(unsigned int) + sizeof(unsigned short);
+
+ InitSecBuffer(&inbuf, SECBUFFER_APPLICATION_PROTOCOLS, alpn_buffer, cur);
+ InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
+ }
+ else {
+ InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0);
+ InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
+ }
+#else /* HAS_ALPN */
+ InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0);
+ InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
+#endif
+
+ /* setup output buffer */
+ InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
+ InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
+
+ /* security request flags */
+ backend->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_STREAM;
+
+ if(!ssl_config->auto_client_cert) {
+ backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
+ }
+
+ /* allocate memory for the security context handle */
+ backend->ctxt = (struct Curl_schannel_ctxt *)
+ calloc(1, sizeof(struct Curl_schannel_ctxt));
+ if(!backend->ctxt) {
+ failf(data, "schannel: unable to allocate memory");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* Schannel InitializeSecurityContext:
+ https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx
+
+ At the moment we don't pass inbuf unless we're using ALPN since we only
+ use it for that, and Wine (for which we currently disable ALPN) is giving
+ us problems with inbuf regardless. https://github.com/curl/curl/issues/983
+ */
+ sspi_status = s_pSecFn->InitializeSecurityContext(
+ &backend->cred->cred_handle, NULL, backend->cred->sni_hostname,
+ backend->req_flags, 0, 0,
+ (backend->use_alpn ? &inbuf_desc : NULL),
+ 0, &backend->ctxt->ctxt_handle,
+ &outbuf_desc, &backend->ret_flags, &backend->ctxt->time_stamp);
+
+ if(sspi_status != SEC_I_CONTINUE_NEEDED) {
+ char buffer[STRERROR_LEN];
+ Curl_safefree(backend->ctxt);
+ switch(sspi_status) {
+ case SEC_E_INSUFFICIENT_MEMORY:
+ failf(data, "schannel: initial InitializeSecurityContext failed: %s",
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+ return CURLE_OUT_OF_MEMORY;
+ case SEC_E_WRONG_PRINCIPAL:
+ failf(data, "schannel: SNI or certificate check failed: %s",
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+ return CURLE_PEER_FAILED_VERIFICATION;
+ /*
+ case SEC_E_INVALID_HANDLE:
+ case SEC_E_INVALID_TOKEN:
+ case SEC_E_LOGON_DENIED:
+ case SEC_E_TARGET_UNKNOWN:
+ case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+ case SEC_E_INTERNAL_ERROR:
+ case SEC_E_NO_CREDENTIALS:
+ case SEC_E_UNSUPPORTED_FUNCTION:
+ case SEC_E_APPLICATION_PROTOCOL_MISMATCH:
+ */
+ default:
+ failf(data, "schannel: initial InitializeSecurityContext failed: %s",
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+ DEBUGF(infof(data, "schannel: sending initial handshake data: "
+ "sending %lu bytes.", outbuf.cbBuffer));
+
+ /* send initial handshake data which is now stored in output buffer */
+ written = Curl_conn_cf_send(cf->next, data,
+ outbuf.pvBuffer, outbuf.cbBuffer,
+ &result);
+ s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
+ if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
+ failf(data, "schannel: failed to send initial handshake data: "
+ "sent %zd of %lu bytes", written, outbuf.cbBuffer);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ DEBUGF(infof(data, "schannel: sent initial handshake data: "
+ "sent %zd bytes", written));
+
+ backend->recv_unrecoverable_err = CURLE_OK;
+ backend->recv_sspi_close_notify = false;
+ backend->recv_connection_closed = false;
+ backend->recv_renegotiating = false;
+ backend->encdata_is_incomplete = false;
+
+ /* continue to second handshake step */
+ connssl->connecting_state = ssl_connect_2;
+
+ return CURLE_OK;
+}
+
+static CURLcode
+schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ int i;
+ ssize_t nread = -1, written = -1;
+ unsigned char *reallocated_buffer;
+ SecBuffer outbuf[3];
+ SecBufferDesc outbuf_desc;
+ SecBuffer inbuf[2];
+ SecBufferDesc inbuf_desc;
+ SECURITY_STATUS sspi_status = SEC_E_OK;
+ CURLcode result;
+ bool doread;
+ const char *pubkey_ptr;
+
+ DEBUGASSERT(backend);
+
+ doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE;
+
+ DEBUGF(infof(data,
+ "schannel: SSL/TLS connection with %s port %d (step 2/3)",
+ connssl->hostname, connssl->port));
+
+ if(!backend->cred || !backend->ctxt)
+ return CURLE_SSL_CONNECT_ERROR;
+
+ /* buffer to store previously received and decrypted data */
+ if(!backend->decdata_buffer) {
+ backend->decdata_offset = 0;
+ backend->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
+ backend->decdata_buffer = malloc(backend->decdata_length);
+ if(!backend->decdata_buffer) {
+ failf(data, "schannel: unable to allocate memory");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+
+ /* buffer to store previously received and encrypted data */
+ if(!backend->encdata_buffer) {
+ backend->encdata_is_incomplete = false;
+ backend->encdata_offset = 0;
+ backend->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
+ backend->encdata_buffer = malloc(backend->encdata_length);
+ if(!backend->encdata_buffer) {
+ failf(data, "schannel: unable to allocate memory");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+
+ /* if we need a bigger buffer to read a full message, increase buffer now */
+ if(backend->encdata_length - backend->encdata_offset <
+ CURL_SCHANNEL_BUFFER_FREE_SIZE) {
+ /* increase internal encrypted data buffer */
+ size_t reallocated_length = backend->encdata_offset +
+ CURL_SCHANNEL_BUFFER_FREE_SIZE;
+ reallocated_buffer = realloc(backend->encdata_buffer,
+ reallocated_length);
+
+ if(!reallocated_buffer) {
+ failf(data, "schannel: unable to re-allocate memory");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else {
+ backend->encdata_buffer = reallocated_buffer;
+ backend->encdata_length = reallocated_length;
+ }
+ }
+
+ for(;;) {
+ if(doread) {
+ /* read encrypted handshake data from socket */
+ nread = Curl_conn_cf_recv(cf->next, data,
+ (char *) (backend->encdata_buffer +
+ backend->encdata_offset),
+ backend->encdata_length -
+ backend->encdata_offset,
+ &result);
+ if(result == CURLE_AGAIN) {
+ if(connssl->connecting_state != ssl_connect_2_writing)
+ connssl->connecting_state = ssl_connect_2_reading;
+ DEBUGF(infof(data, "schannel: failed to receive handshake, "
+ "need more data"));
+ return CURLE_OK;
+ }
+ else if((result != CURLE_OK) || (nread == 0)) {
+ failf(data, "schannel: failed to receive handshake, "
+ "SSL/TLS connection failed");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ /* increase encrypted data buffer offset */
+ backend->encdata_offset += nread;
+ backend->encdata_is_incomplete = false;
+ DEBUGF(infof(data, "schannel: encrypted data got %zd", nread));
+ }
+
+ DEBUGF(infof(data,
+ "schannel: encrypted data buffer: offset %zu length %zu",
+ backend->encdata_offset, backend->encdata_length));
+
+ /* setup input buffers */
+ InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(backend->encdata_offset),
+ curlx_uztoul(backend->encdata_offset));
+ InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
+ InitSecBufferDesc(&inbuf_desc, inbuf, 2);
+
+ /* setup output buffers */
+ InitSecBuffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0);
+ InitSecBuffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0);
+ InitSecBuffer(&outbuf[2], SECBUFFER_EMPTY, NULL, 0);
+ InitSecBufferDesc(&outbuf_desc, outbuf, 3);
+
+ if(!inbuf[0].pvBuffer) {
+ failf(data, "schannel: unable to allocate memory");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* copy received handshake data into input buffer */
+ memcpy(inbuf[0].pvBuffer, backend->encdata_buffer,
+ backend->encdata_offset);
+
+ sspi_status = s_pSecFn->InitializeSecurityContext(
+ &backend->cred->cred_handle, &backend->ctxt->ctxt_handle,
+ backend->cred->sni_hostname, backend->req_flags,
+ 0, 0, &inbuf_desc, 0, NULL,
+ &outbuf_desc, &backend->ret_flags, &backend->ctxt->time_stamp);
+
+ /* free buffer for received handshake data */
+ Curl_safefree(inbuf[0].pvBuffer);
+
+ /* check if the handshake was incomplete */
+ if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
+ backend->encdata_is_incomplete = true;
+ connssl->connecting_state = ssl_connect_2_reading;
+ DEBUGF(infof(data,
+ "schannel: received incomplete message, need more data"));
+ return CURLE_OK;
+ }
+
+ /* If the server has requested a client certificate, attempt to continue
+ the handshake without one. This will allow connections to servers which
+ request a client certificate but do not require it. */
+ if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS &&
+ !(backend->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
+ backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
+ connssl->connecting_state = ssl_connect_2_writing;
+ DEBUGF(infof(data,
+ "schannel: a client certificate has been requested"));
+ return CURLE_OK;
+ }
+
+ /* check if the handshake needs to be continued */
+ if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) {
+ for(i = 0; i < 3; i++) {
+ /* search for handshake tokens that need to be send */
+ if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) {
+ DEBUGF(infof(data, "schannel: sending next handshake data: "
+ "sending %lu bytes.", outbuf[i].cbBuffer));
+
+ /* send handshake token to server */
+ written = Curl_conn_cf_send(cf->next, data,
+ outbuf[i].pvBuffer, outbuf[i].cbBuffer,
+ &result);
+ if((result != CURLE_OK) ||
+ (outbuf[i].cbBuffer != (size_t) written)) {
+ failf(data, "schannel: failed to send next handshake data: "
+ "sent %zd of %lu bytes", written, outbuf[i].cbBuffer);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+ /* free obsolete buffer */
+ if(outbuf[i].pvBuffer) {
+ s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer);
+ }
+ }
+ }
+ else {
+ char buffer[STRERROR_LEN];
+ switch(sspi_status) {
+ case SEC_E_INSUFFICIENT_MEMORY:
+ failf(data, "schannel: next InitializeSecurityContext failed: %s",
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+ return CURLE_OUT_OF_MEMORY;
+ case SEC_E_WRONG_PRINCIPAL:
+ failf(data, "schannel: SNI or certificate check failed: %s",
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case SEC_E_UNTRUSTED_ROOT:
+ failf(data, "schannel: %s",
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+ return CURLE_PEER_FAILED_VERIFICATION;
+ /*
+ case SEC_E_INVALID_HANDLE:
+ case SEC_E_INVALID_TOKEN:
+ case SEC_E_LOGON_DENIED:
+ case SEC_E_TARGET_UNKNOWN:
+ case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+ case SEC_E_INTERNAL_ERROR:
+ case SEC_E_NO_CREDENTIALS:
+ case SEC_E_UNSUPPORTED_FUNCTION:
+ case SEC_E_APPLICATION_PROTOCOL_MISMATCH:
+ */
+ default:
+ failf(data, "schannel: next InitializeSecurityContext failed: %s",
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+ /* check if there was additional remaining encrypted data */
+ if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) {
+ DEBUGF(infof(data, "schannel: encrypted data length: %lu",
+ inbuf[1].cbBuffer));
+ /*
+ There are two cases where we could be getting extra data here:
+ 1) If we're renegotiating a connection and the handshake is already
+ complete (from the server perspective), it can encrypted app data
+ (not handshake data) in an extra buffer at this point.
+ 2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a
+ connection and this extra data is part of the handshake.
+ We should process the data immediately; waiting for the socket to
+ be ready may fail since the server is done sending handshake data.
+ */
+ /* check if the remaining data is less than the total amount
+ and therefore begins after the already processed data */
+ if(backend->encdata_offset > inbuf[1].cbBuffer) {
+ memmove(backend->encdata_buffer,
+ (backend->encdata_buffer + backend->encdata_offset) -
+ inbuf[1].cbBuffer, inbuf[1].cbBuffer);
+ backend->encdata_offset = inbuf[1].cbBuffer;
+ if(sspi_status == SEC_I_CONTINUE_NEEDED) {
+ doread = FALSE;
+ continue;
+ }
+ }
+ }
+ else {
+ backend->encdata_offset = 0;
+ }
+ break;
+ }
+
+ /* check if the handshake needs to be continued */
+ if(sspi_status == SEC_I_CONTINUE_NEEDED) {
+ connssl->connecting_state = ssl_connect_2_reading;
+ return CURLE_OK;
+ }
+
+ /* check if the handshake is complete */
+ if(sspi_status == SEC_E_OK) {
+ connssl->connecting_state = ssl_connect_3;
+ DEBUGF(infof(data, "schannel: SSL/TLS handshake complete"));
+ }
+
+ pubkey_ptr = Curl_ssl_cf_is_proxy(cf)?
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY];
+ if(pubkey_ptr) {
+ result = pkp_pin_peer_pubkey(cf, data, pubkey_ptr);
+ if(result) {
+ failf(data, "SSL: public key does not match pinned public key");
+ return result;
+ }
+ }
+
+#ifdef HAS_MANUAL_VERIFY_API
+ if(conn_config->verifypeer && backend->use_manual_cred_validation) {
+ return Curl_verify_certificate(cf, data);
+ }
+#endif
+
+ return CURLE_OK;
+}
+
+static bool
+valid_cert_encoding(const CERT_CONTEXT *cert_context)
+{
+ return (cert_context != NULL) &&
+ ((cert_context->dwCertEncodingType & X509_ASN_ENCODING) != 0) &&
+ (cert_context->pbCertEncoded != NULL) &&
+ (cert_context->cbCertEncoded > 0);
+}
+
+typedef bool(*Read_crt_func)(const CERT_CONTEXT *ccert_context, void *arg);
+
+static void
+traverse_cert_store(const CERT_CONTEXT *context, Read_crt_func func,
+ void *arg)
+{
+ const CERT_CONTEXT *current_context = NULL;
+ bool should_continue = true;
+ while(should_continue &&
+ (current_context = CertEnumCertificatesInStore(
+ context->hCertStore,
+ current_context)) != NULL)
+ should_continue = func(current_context, arg);
+
+ if(current_context)
+ CertFreeCertificateContext(current_context);
+}
+
+static bool
+cert_counter_callback(const CERT_CONTEXT *ccert_context, void *certs_count)
+{
+ if(valid_cert_encoding(ccert_context))
+ (*(int *)certs_count)++;
+ return true;
+}
+
+struct Adder_args
+{
+ struct Curl_easy *data;
+ CURLcode result;
+ int idx;
+ int certs_count;
+};
+
+static bool
+add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, void *raw_arg)
+{
+ struct Adder_args *args = (struct Adder_args*)raw_arg;
+ args->result = CURLE_OK;
+ if(valid_cert_encoding(ccert_context)) {
+ const char *beg = (const char *) ccert_context->pbCertEncoded;
+ const char *end = beg + ccert_context->cbCertEncoded;
+ int insert_index = (args->certs_count - 1) - args->idx;
+ args->result = Curl_extract_certinfo(args->data, insert_index,
+ beg, end);
+ args->idx++;
+ }
+ return args->result == CURLE_OK;
+}
+
+static CURLcode
+schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ CURLcode result = CURLE_OK;
+ SECURITY_STATUS sspi_status = SEC_E_OK;
+ CERT_CONTEXT *ccert_context = NULL;
+#ifdef HAS_ALPN
+ SecPkgContext_ApplicationProtocol alpn_result;
+#endif
+
+ DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+ DEBUGASSERT(backend);
+
+ DEBUGF(infof(data,
+ "schannel: SSL/TLS connection with %s port %d (step 3/3)",
+ connssl->hostname, connssl->port));
+
+ if(!backend->cred)
+ return CURLE_SSL_CONNECT_ERROR;
+
+ /* check if the required context attributes are met */
+ if(backend->ret_flags != backend->req_flags) {
+ if(!(backend->ret_flags & ISC_RET_SEQUENCE_DETECT))
+ failf(data, "schannel: failed to setup sequence detection");
+ if(!(backend->ret_flags & ISC_RET_REPLAY_DETECT))
+ failf(data, "schannel: failed to setup replay detection");
+ if(!(backend->ret_flags & ISC_RET_CONFIDENTIALITY))
+ failf(data, "schannel: failed to setup confidentiality");
+ if(!(backend->ret_flags & ISC_RET_ALLOCATED_MEMORY))
+ failf(data, "schannel: failed to setup memory allocation");
+ if(!(backend->ret_flags & ISC_RET_STREAM))
+ failf(data, "schannel: failed to setup stream orientation");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+#ifdef HAS_ALPN
+ if(backend->use_alpn) {
+ sspi_status =
+ s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle,
+ SECPKG_ATTR_APPLICATION_PROTOCOL,
+ &alpn_result);
+
+ if(sspi_status != SEC_E_OK) {
+ failf(data, "schannel: failed to retrieve ALPN result");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ if(alpn_result.ProtoNegoStatus ==
+ SecApplicationProtocolNegotiationStatus_Success) {
+ unsigned char alpn = 0;
+
+ infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR,
+ alpn_result.ProtocolIdSize, alpn_result.ProtocolId);
+
+#ifdef USE_HTTP2
+ if(alpn_result.ProtocolIdSize == ALPN_H2_LENGTH &&
+ !memcmp(ALPN_H2, alpn_result.ProtocolId, ALPN_H2_LENGTH)) {
+ alpn = CURL_HTTP_VERSION_2;
+ }
+ else
+#endif
+ if(alpn_result.ProtocolIdSize == ALPN_HTTP_1_1_LENGTH &&
+ !memcmp(ALPN_HTTP_1_1, alpn_result.ProtocolId,
+ ALPN_HTTP_1_1_LENGTH)) {
+ alpn = CURL_HTTP_VERSION_1_1;
+ }
+ if(backend->recv_renegotiating) {
+ if(alpn != cf->conn->alpn) {
+ failf(data, "schannel: server selected an ALPN protocol too late");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ else
+ cf->conn->alpn = alpn;
+ }
+ else {
+ if(!backend->recv_renegotiating)
+ infof(data, VTLS_INFOF_NO_ALPN);
+ }
+
+ if(!backend->recv_renegotiating) {
+ Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ }
+ }
+#endif
+
+ /* save the current session data for possible re-use */
+ if(ssl_config->primary.sessionid) {
+ bool incache;
+ bool added = FALSE;
+ struct Curl_schannel_cred *old_cred = NULL;
+
+ Curl_ssl_sessionid_lock(data);
+ incache = !(Curl_ssl_getsessionid(cf, data, (void **)&old_cred, NULL));
+ if(incache) {
+ if(old_cred != backend->cred) {
+ DEBUGF(infof(data,
+ "schannel: old credential handle is stale, removing"));
+ /* we're not taking old_cred ownership here, no refcount++ is needed */
+ Curl_ssl_delsessionid(data, (void *)old_cred);
+ incache = FALSE;
+ }
+ }
+ if(!incache) {
+ result = Curl_ssl_addsessionid(cf, data, backend->cred,
+ sizeof(struct Curl_schannel_cred),
+ &added);
+ if(result) {
+ Curl_ssl_sessionid_unlock(data);
+ failf(data, "schannel: failed to store credential handle");
+ return result;
+ }
+ else if(added) {
+ /* this cred session is now also referenced by sessionid cache */
+ backend->cred->refcount++;
+ DEBUGF(infof(data,
+ "schannel: stored credential handle in session cache"));
+ }
+ }
+ Curl_ssl_sessionid_unlock(data);
+ }
+
+ if(data->set.ssl.certinfo) {
+ int certs_count = 0;
+ sspi_status =
+ s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle,
+ SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+ &ccert_context);
+
+ if((sspi_status != SEC_E_OK) || !ccert_context) {
+ failf(data, "schannel: failed to retrieve remote cert context");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+
+ traverse_cert_store(ccert_context, cert_counter_callback, &certs_count);
+
+ result = Curl_ssl_init_certinfo(data, certs_count);
+ if(!result) {
+ struct Adder_args args;
+ args.data = data;
+ args.idx = 0;
+ args.certs_count = certs_count;
+ traverse_cert_store(ccert_context, add_cert_to_certinfo, &args);
+ result = args.result;
+ }
+ CertFreeCertificateContext(ccert_context);
+ if(result)
+ return result;
+ }
+
+ connssl->connecting_state = ssl_connect_done;
+
+ return CURLE_OK;
+}
+
+static CURLcode
+schannel_connect_common(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool nonblocking, bool *done)
+{
+ CURLcode result;
+ struct ssl_connect_data *connssl = cf->ctx;
+ curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ timediff_t timeout_ms;
+ int what;
+
+ /* check if the connection has already been established */
+ if(ssl_connection_complete == connssl->state) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ if(ssl_connect_1 == connssl->connecting_state) {
+ /* check out how much more time we're allowed */
+ timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+ if(timeout_ms < 0) {
+ /* no need to continue if time already is up */
+ failf(data, "SSL/TLS connection timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
+ result = schannel_connect_step1(cf, data);
+ if(result)
+ return result;
+ }
+
+ while(ssl_connect_2 == connssl->connecting_state ||
+ ssl_connect_2_reading == connssl->connecting_state ||
+ ssl_connect_2_writing == connssl->connecting_state) {
+
+ /* check out how much more time we're allowed */
+ timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+ if(timeout_ms < 0) {
+ /* no need to continue if time already is up */
+ failf(data, "SSL/TLS connection timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
+ /* if ssl is expecting something, check if it's available. */
+ if(connssl->connecting_state == ssl_connect_2_reading
+ || connssl->connecting_state == ssl_connect_2_writing) {
+
+ curl_socket_t writefd = ssl_connect_2_writing ==
+ connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
+ curl_socket_t readfd = ssl_connect_2_reading ==
+ connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
+
+ what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
+ nonblocking ? 0 : timeout_ms);
+ if(what < 0) {
+ /* fatal error */
+ failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else if(0 == what) {
+ if(nonblocking) {
+ *done = FALSE;
+ return CURLE_OK;
+ }
+ else {
+ /* timeout */
+ failf(data, "SSL/TLS connection timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+ }
+ /* socket is readable or writable */
+ }
+
+ /* Run transaction, and return to the caller if it failed or if
+ * this connection is part of a multi handle and this loop would
+ * execute again. This permits the owner of a multi handle to
+ * abort a connection attempt before step2 has completed while
+ * ensuring that a client using select() or epoll() will always
+ * have a valid fdset to wait on.
+ */
+ result = schannel_connect_step2(cf, data);
+ if(result || (nonblocking &&
+ (ssl_connect_2 == connssl->connecting_state ||
+ ssl_connect_2_reading == connssl->connecting_state ||
+ ssl_connect_2_writing == connssl->connecting_state)))
+ return result;
+
+ } /* repeat step2 until all transactions are done. */
+
+ if(ssl_connect_3 == connssl->connecting_state) {
+ result = schannel_connect_step3(cf, data);
+ if(result)
+ return result;
+ }
+
+ if(ssl_connect_done == connssl->connecting_state) {
+ connssl->state = ssl_connection_complete;
+
+#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
+ /* When SSPI is used in combination with Schannel
+ * we need the Schannel context to create the Schannel
+ * binding to pass the IIS extended protection checks.
+ * Available on Windows 7 or later.
+ */
+ {
+ struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
+ cf->conn->sslContext = &backend->ctxt->ctxt_handle;
+ }
+#endif
+
+ *done = TRUE;
+ }
+ else
+ *done = FALSE;
+
+ /* reset our connection state machine */
+ connssl->connecting_state = ssl_connect_1;
+
+ return CURLE_OK;
+}
+
+static ssize_t
+schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err)
+{
+ ssize_t written = -1;
+ size_t data_len = 0;
+ unsigned char *ptr = NULL;
+ struct ssl_connect_data *connssl = cf->ctx;
+ SecBuffer outbuf[4];
+ SecBufferDesc outbuf_desc;
+ SECURITY_STATUS sspi_status = SEC_E_OK;
+ CURLcode result;
+ struct ssl_backend_data *backend = connssl->backend;
+
+ DEBUGASSERT(backend);
+
+ /* check if the maximum stream sizes were queried */
+ if(backend->stream_sizes.cbMaximumMessage == 0) {
+ sspi_status = s_pSecFn->QueryContextAttributes(
+ &backend->ctxt->ctxt_handle,
+ SECPKG_ATTR_STREAM_SIZES,
+ &backend->stream_sizes);
+ if(sspi_status != SEC_E_OK) {
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+ }
+
+ /* check if the buffer is longer than the maximum message length */
+ if(len > backend->stream_sizes.cbMaximumMessage) {
+ len = backend->stream_sizes.cbMaximumMessage;
+ }
+
+ /* calculate the complete message length and allocate a buffer for it */
+ data_len = backend->stream_sizes.cbHeader + len +
+ backend->stream_sizes.cbTrailer;
+ ptr = (unsigned char *) malloc(data_len);
+ if(!ptr) {
+ *err = CURLE_OUT_OF_MEMORY;
+ return -1;
+ }
+
+ /* setup output buffers (header, data, trailer, empty) */
+ InitSecBuffer(&outbuf[0], SECBUFFER_STREAM_HEADER,
+ ptr, backend->stream_sizes.cbHeader);
+ InitSecBuffer(&outbuf[1], SECBUFFER_DATA,
+ ptr + backend->stream_sizes.cbHeader, curlx_uztoul(len));
+ InitSecBuffer(&outbuf[2], SECBUFFER_STREAM_TRAILER,
+ ptr + backend->stream_sizes.cbHeader + len,
+ backend->stream_sizes.cbTrailer);
+ InitSecBuffer(&outbuf[3], SECBUFFER_EMPTY, NULL, 0);
+ InitSecBufferDesc(&outbuf_desc, outbuf, 4);
+
+ /* copy data into output buffer */
+ memcpy(outbuf[1].pvBuffer, buf, len);
+
+ /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */
+ sspi_status = s_pSecFn->EncryptMessage(&backend->ctxt->ctxt_handle, 0,
+ &outbuf_desc, 0);
+
+ /* check if the message was encrypted */
+ if(sspi_status == SEC_E_OK) {
+ written = 0;
+
+ /* send the encrypted message including header, data and trailer */
+ len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
+
+ /*
+ It's important to send the full message which includes the header,
+ encrypted payload, and trailer. Until the client receives all the
+ data a coherent message has not been delivered and the client
+ can't read any of it.
+
+ If we wanted to buffer the unwritten encrypted bytes, we would
+ tell the client that all data it has requested to be sent has been
+ sent. The unwritten encrypted bytes would be the first bytes to
+ send on the next invocation.
+ Here's the catch with this - if we tell the client that all the
+ bytes have been sent, will the client call this method again to
+ send the buffered data? Looking at who calls this function, it
+ seems the answer is NO.
+ */
+
+ /* send entire message or fail */
+ while(len > (size_t)written) {
+ ssize_t this_write = 0;
+ int what;
+ timediff_t timeout_ms = Curl_timeleft(data, NULL, FALSE);
+ if(timeout_ms < 0) {
+ /* we already got the timeout */
+ failf(data, "schannel: timed out sending data "
+ "(bytes sent: %zd)", written);
+ *err = CURLE_OPERATION_TIMEDOUT;
+ written = -1;
+ break;
+ }
+ else if(!timeout_ms)
+ timeout_ms = TIMEDIFF_T_MAX;
+ what = SOCKET_WRITABLE(cf->conn->sock[cf->sockindex], timeout_ms);
+ if(what < 0) {
+ /* fatal error */
+ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+ *err = CURLE_SEND_ERROR;
+ written = -1;
+ break;
+ }
+ else if(0 == what) {
+ failf(data, "schannel: timed out sending data "
+ "(bytes sent: %zd)", written);
+ *err = CURLE_OPERATION_TIMEDOUT;
+ written = -1;
+ break;
+ }
+ /* socket is writable */
+
+ this_write = Curl_conn_cf_send(cf->next, data,
+ ptr + written, len - written,
+ &result);
+ if(result == CURLE_AGAIN)
+ continue;
+ else if(result != CURLE_OK) {
+ *err = result;
+ written = -1;
+ break;
+ }
+
+ written += this_write;
+ }
+ }
+ else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) {
+ *err = CURLE_OUT_OF_MEMORY;
+ }
+ else{
+ *err = CURLE_SEND_ERROR;
+ }
+
+ Curl_safefree(ptr);
+
+ if(len == (size_t)written)
+ /* Encrypted message including header, data and trailer entirely sent.
+ The return value is the number of unencrypted bytes that were sent. */
+ written = outbuf[1].cbBuffer;
+
+ return written;
+}
+
+static ssize_t
+schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err)
+{
+ size_t size = 0;
+ ssize_t nread = -1;
+ struct ssl_connect_data *connssl = cf->ctx;
+ unsigned char *reallocated_buffer;
+ size_t reallocated_length;
+ bool done = FALSE;
+ SecBuffer inbuf[4];
+ SecBufferDesc inbuf_desc;
+ SECURITY_STATUS sspi_status = SEC_E_OK;
+ /* we want the length of the encrypted buffer to be at least large enough
+ that it can hold all the bytes requested and some TLS record overhead. */
+ size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE;
+ struct ssl_backend_data *backend = connssl->backend;
+
+ DEBUGASSERT(backend);
+
+ /****************************************************************************
+ * Don't return or set backend->recv_unrecoverable_err unless in the cleanup.
+ * The pattern for return error is set *err, optional infof, goto cleanup.
+ *
+ * Our priority is to always return as much decrypted data to the caller as
+ * possible, even if an error occurs. The state of the decrypted buffer must
+ * always be valid. Transfer of decrypted data to the caller's buffer is
+ * handled in the cleanup.
+ */
+
+ DEBUGF(infof(data, "schannel: client wants to read %zu bytes", len));
+ *err = CURLE_OK;
+
+ if(len && len <= backend->decdata_offset) {
+ infof(data, "schannel: enough decrypted data is already available");
+ goto cleanup;
+ }
+ else if(backend->recv_unrecoverable_err) {
+ *err = backend->recv_unrecoverable_err;
+ infof(data, "schannel: an unrecoverable error occurred in a prior call");
+ goto cleanup;
+ }
+ else if(backend->recv_sspi_close_notify) {
+ /* once a server has indicated shutdown there is no more encrypted data */
+ infof(data, "schannel: server indicated shutdown in a prior call");
+ goto cleanup;
+ }
+
+ /* It's debatable what to return when !len. Regardless we can't return
+ immediately because there may be data to decrypt (in the case we want to
+ decrypt all encrypted cached data) so handle !len later in cleanup.
+ */
+ else if(len && !backend->recv_connection_closed) {
+ /* increase enc buffer in order to fit the requested amount of data */
+ size = backend->encdata_length - backend->encdata_offset;
+ if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE ||
+ backend->encdata_length < min_encdata_length) {
+ reallocated_length = backend->encdata_offset +
+ CURL_SCHANNEL_BUFFER_FREE_SIZE;
+ if(reallocated_length < min_encdata_length) {
+ reallocated_length = min_encdata_length;
+ }
+ reallocated_buffer = realloc(backend->encdata_buffer,
+ reallocated_length);
+ if(!reallocated_buffer) {
+ *err = CURLE_OUT_OF_MEMORY;
+ failf(data, "schannel: unable to re-allocate memory");
+ goto cleanup;
+ }
+
+ backend->encdata_buffer = reallocated_buffer;
+ backend->encdata_length = reallocated_length;
+ size = backend->encdata_length - backend->encdata_offset;
+ DEBUGF(infof(data, "schannel: encdata_buffer resized %zu",
+ backend->encdata_length));
+ }
+
+ DEBUGF(infof(data,
+ "schannel: encrypted data buffer: offset %zu length %zu",
+ backend->encdata_offset, backend->encdata_length));
+
+ /* read encrypted data from socket */
+ nread = Curl_conn_cf_recv(cf->next, data,
+ (char *)(backend->encdata_buffer +
+ backend->encdata_offset),
+ size, err);
+ if(*err) {
+ nread = -1;
+ if(*err == CURLE_AGAIN)
+ DEBUGF(infof(data,
+ "schannel: recv returned CURLE_AGAIN"));
+ else if(*err == CURLE_RECV_ERROR)
+ infof(data, "schannel: recv returned CURLE_RECV_ERROR");
+ else
+ infof(data, "schannel: recv returned error %d", *err);
+ }
+ else if(nread == 0) {
+ backend->recv_connection_closed = true;
+ DEBUGF(infof(data, "schannel: server closed the connection"));
+ }
+ else if(nread > 0) {
+ backend->encdata_offset += (size_t)nread;
+ backend->encdata_is_incomplete = false;
+ DEBUGF(infof(data, "schannel: encrypted data got %zd", nread));
+ }
+ }
+
+ DEBUGF(infof(data,
+ "schannel: encrypted data buffer: offset %zu length %zu",
+ backend->encdata_offset, backend->encdata_length));
+
+ /* decrypt loop */
+ while(backend->encdata_offset > 0 && sspi_status == SEC_E_OK &&
+ (!len || backend->decdata_offset < len ||
+ backend->recv_connection_closed)) {
+ /* prepare data buffer for DecryptMessage call */
+ InitSecBuffer(&inbuf[0], SECBUFFER_DATA, backend->encdata_buffer,
+ curlx_uztoul(backend->encdata_offset));
+
+ /* we need 3 more empty input buffers for possible output */
+ InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
+ InitSecBuffer(&inbuf[2], SECBUFFER_EMPTY, NULL, 0);
+ InitSecBuffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0);
+ InitSecBufferDesc(&inbuf_desc, inbuf, 4);
+
+ /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx
+ */
+ sspi_status = s_pSecFn->DecryptMessage(&backend->ctxt->ctxt_handle,
+ &inbuf_desc, 0, NULL);
+
+ /* check if everything went fine (server may want to renegotiate
+ or shutdown the connection context) */
+ if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE ||
+ sspi_status == SEC_I_CONTEXT_EXPIRED) {
+ /* check for successfully decrypted data, even before actual
+ renegotiation or shutdown of the connection context */
+ if(inbuf[1].BufferType == SECBUFFER_DATA) {
+ DEBUGF(infof(data, "schannel: decrypted data length: %lu",
+ inbuf[1].cbBuffer));
+
+ /* increase buffer in order to fit the received amount of data */
+ size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ?
+ inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE;
+ if(backend->decdata_length - backend->decdata_offset < size ||
+ backend->decdata_length < len) {
+ /* increase internal decrypted data buffer */
+ reallocated_length = backend->decdata_offset + size;
+ /* make sure that the requested amount of data fits */
+ if(reallocated_length < len) {
+ reallocated_length = len;
+ }
+ reallocated_buffer = realloc(backend->decdata_buffer,
+ reallocated_length);
+ if(!reallocated_buffer) {
+ *err = CURLE_OUT_OF_MEMORY;
+ failf(data, "schannel: unable to re-allocate memory");
+ goto cleanup;
+ }
+ backend->decdata_buffer = reallocated_buffer;
+ backend->decdata_length = reallocated_length;
+ }
+
+ /* copy decrypted data to internal buffer */
+ size = inbuf[1].cbBuffer;
+ if(size) {
+ memcpy(backend->decdata_buffer + backend->decdata_offset,
+ inbuf[1].pvBuffer, size);
+ backend->decdata_offset += size;
+ }
+
+ DEBUGF(infof(data, "schannel: decrypted data added: %zu", size));
+ DEBUGF(infof(data,
+ "schannel: decrypted cached: offset %zu length %zu",
+ backend->decdata_offset, backend->decdata_length));
+ }
+
+ /* check for remaining encrypted data */
+ if(inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) {
+ DEBUGF(infof(data, "schannel: encrypted data length: %lu",
+ inbuf[3].cbBuffer));
+
+ /* check if the remaining data is less than the total amount
+ * and therefore begins after the already processed data
+ */
+ if(backend->encdata_offset > inbuf[3].cbBuffer) {
+ /* move remaining encrypted data forward to the beginning of
+ buffer */
+ memmove(backend->encdata_buffer,
+ (backend->encdata_buffer + backend->encdata_offset) -
+ inbuf[3].cbBuffer, inbuf[3].cbBuffer);
+ backend->encdata_offset = inbuf[3].cbBuffer;
+ }
+
+ DEBUGF(infof(data,
+ "schannel: encrypted cached: offset %zu length %zu",
+ backend->encdata_offset, backend->encdata_length));
+ }
+ else {
+ /* reset encrypted buffer offset, because there is no data remaining */
+ backend->encdata_offset = 0;
+ }
+
+ /* check if server wants to renegotiate the connection context */
+ if(sspi_status == SEC_I_RENEGOTIATE) {
+ infof(data, "schannel: remote party requests renegotiation");
+ if(*err && *err != CURLE_AGAIN) {
+ infof(data, "schannel: can't renegotiate, an error is pending");
+ goto cleanup;
+ }
+
+ /* begin renegotiation */
+ infof(data, "schannel: renegotiating SSL/TLS connection");
+ connssl->state = ssl_connection_negotiating;
+ connssl->connecting_state = ssl_connect_2_writing;
+ backend->recv_renegotiating = true;
+ *err = schannel_connect_common(cf, data, FALSE, &done);
+ backend->recv_renegotiating = false;
+ if(*err) {
+ infof(data, "schannel: renegotiation failed");
+ goto cleanup;
+ }
+ /* now retry receiving data */
+ sspi_status = SEC_E_OK;
+ infof(data, "schannel: SSL/TLS connection renegotiated");
+ continue;
+ }
+ /* check if the server closed the connection */
+ else if(sspi_status == SEC_I_CONTEXT_EXPIRED) {
+ /* In Windows 2000 SEC_I_CONTEXT_EXPIRED (close_notify) is not
+ returned so we have to work around that in cleanup. */
+ backend->recv_sspi_close_notify = true;
+ if(!backend->recv_connection_closed) {
+ backend->recv_connection_closed = true;
+ infof(data, "schannel: server closed the connection");
+ }
+ goto cleanup;
+ }
+ }
+ else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
+ backend->encdata_is_incomplete = true;
+ if(!*err)
+ *err = CURLE_AGAIN;
+ infof(data, "schannel: failed to decrypt data, need more data");
+ goto cleanup;
+ }
+ else {
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ char buffer[STRERROR_LEN];
+#endif
+ *err = CURLE_RECV_ERROR;
+ infof(data, "schannel: failed to read data from server: %s",
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+ goto cleanup;
+ }
+ }
+
+ DEBUGF(infof(data,
+ "schannel: encrypted data buffer: offset %zu length %zu",
+ backend->encdata_offset, backend->encdata_length));
+
+ DEBUGF(infof(data,
+ "schannel: decrypted data buffer: offset %zu length %zu",
+ backend->decdata_offset, backend->decdata_length));
+
+ cleanup:
+ /* Warning- there is no guarantee the encdata state is valid at this point */
+ DEBUGF(infof(data, "schannel: schannel_recv cleanup"));
+
+ /* Error if the connection has closed without a close_notify.
+
+ The behavior here is a matter of debate. We don't want to be vulnerable
+ to a truncation attack however there's some browser precedent for
+ ignoring the close_notify for compatibility reasons.
+
+ Additionally, Windows 2000 (v5.0) is a special case since it seems it
+ doesn't return close_notify. In that case if the connection was closed we
+ assume it was graceful (close_notify) since there doesn't seem to be a
+ way to tell.
+ */
+ if(len && !backend->decdata_offset && backend->recv_connection_closed &&
+ !backend->recv_sspi_close_notify) {
+ bool isWin2k = curlx_verify_windows_version(5, 0, 0, PLATFORM_WINNT,
+ VERSION_EQUAL);
+
+ if(isWin2k && sspi_status == SEC_E_OK)
+ backend->recv_sspi_close_notify = true;
+ else {
+ *err = CURLE_RECV_ERROR;
+ infof(data, "schannel: server closed abruptly (missing close_notify)");
+ }
+ }
+
+ /* Any error other than CURLE_AGAIN is an unrecoverable error. */
+ if(*err && *err != CURLE_AGAIN)
+ backend->recv_unrecoverable_err = *err;
+
+ size = len < backend->decdata_offset ? len : backend->decdata_offset;
+ if(size) {
+ memcpy(buf, backend->decdata_buffer, size);
+ memmove(backend->decdata_buffer, backend->decdata_buffer + size,
+ backend->decdata_offset - size);
+ backend->decdata_offset -= size;
+ DEBUGF(infof(data, "schannel: decrypted data returned %zu", size));
+ DEBUGF(infof(data,
+ "schannel: decrypted data buffer: offset %zu length %zu",
+ backend->decdata_offset, backend->decdata_length));
+ *err = CURLE_OK;
+ return (ssize_t)size;
+ }
+
+ if(!*err && !backend->recv_connection_closed)
+ *err = CURLE_AGAIN;
+
+ /* It's debatable what to return when !len. We could return whatever error
+ we got from decryption but instead we override here so the return is
+ consistent.
+ */
+ if(!len)
+ *err = CURLE_OK;
+
+ return *err ? -1 : 0;
+}
+
+static CURLcode schannel_connect_nonblocking(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
+{
+ return schannel_connect_common(cf, data, TRUE, done);
+}
+
+static CURLcode schannel_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ CURLcode result;
+ bool done = FALSE;
+
+ result = schannel_connect_common(cf, data, FALSE, &done);
+ if(result)
+ return result;
+
+ DEBUGASSERT(done);
+
+ return CURLE_OK;
+}
+
+static bool schannel_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ const struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+
+ (void)data;
+ DEBUGASSERT(backend);
+
+ if(connssl->backend->ctxt) /* SSL/TLS is in use */
+ return (backend->decdata_offset > 0 ||
+ (backend->encdata_offset > 0 && !backend->encdata_is_incomplete));
+ else
+ return FALSE;
+}
+
+static void schannel_session_free(void *ptr)
+{
+ /* this is expected to be called under sessionid lock */
+ struct Curl_schannel_cred *cred = ptr;
+
+ if(cred) {
+ cred->refcount--;
+ if(cred->refcount == 0) {
+ s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
+ curlx_unicodefree(cred->sni_hostname);
+#ifdef HAS_CLIENT_CERT_PATH
+ if(cred->client_cert_store) {
+ CertCloseStore(cred->client_cert_store, 0);
+ cred->client_cert_store = NULL;
+ }
+#endif
+ Curl_safefree(cred);
+ }
+ }
+}
+
+/* shut down the SSL connection and clean up related memory.
+ this function can be called multiple times on the same connection including
+ if the SSL connection failed (eg connection made but failed handshake). */
+static int schannel_shutdown(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ /* See https://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx
+ * Shutting Down an Schannel Connection
+ */
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+
+ DEBUGASSERT(data);
+ DEBUGASSERT(backend);
+
+ if(connssl->backend->ctxt) {
+ infof(data, "schannel: shutting down SSL/TLS connection with %s port %d",
+ connssl->hostname, connssl->port);
+ }
+
+ if(backend->cred && backend->ctxt) {
+ SecBufferDesc BuffDesc;
+ SecBuffer Buffer;
+ SECURITY_STATUS sspi_status;
+ SecBuffer outbuf;
+ SecBufferDesc outbuf_desc;
+ CURLcode result;
+ DWORD dwshut = SCHANNEL_SHUTDOWN;
+
+ InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut));
+ InitSecBufferDesc(&BuffDesc, &Buffer, 1);
+
+ sspi_status = s_pSecFn->ApplyControlToken(&backend->ctxt->ctxt_handle,
+ &BuffDesc);
+
+ if(sspi_status != SEC_E_OK) {
+ char buffer[STRERROR_LEN];
+ failf(data, "schannel: ApplyControlToken failure: %s",
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+ }
+
+ /* setup output buffer */
+ InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
+ InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
+
+ sspi_status = s_pSecFn->InitializeSecurityContext(
+ &backend->cred->cred_handle,
+ &backend->ctxt->ctxt_handle,
+ backend->cred->sni_hostname,
+ backend->req_flags,
+ 0,
+ 0,
+ NULL,
+ 0,
+ &backend->ctxt->ctxt_handle,
+ &outbuf_desc,
+ &backend->ret_flags,
+ &backend->ctxt->time_stamp);
+
+ if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) {
+ /* send close message which is in output buffer */
+ ssize_t written = Curl_conn_cf_send(cf->next, data,
+ outbuf.pvBuffer, outbuf.cbBuffer,
+ &result);
+ s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
+ if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
+ infof(data, "schannel: failed to send close msg: %s"
+ " (bytes written: %zd)", curl_easy_strerror(result), written);
+ }
+ }
+ }
+
+ /* free SSPI Schannel API security context handle */
+ if(backend->ctxt) {
+ DEBUGF(infof(data, "schannel: clear security context handle"));
+ s_pSecFn->DeleteSecurityContext(&backend->ctxt->ctxt_handle);
+ Curl_safefree(backend->ctxt);
+ }
+
+ /* free SSPI Schannel API credential handle */
+ if(backend->cred) {
+ Curl_ssl_sessionid_lock(data);
+ schannel_session_free(backend->cred);
+ Curl_ssl_sessionid_unlock(data);
+ backend->cred = NULL;
+ }
+
+ /* free internal buffer for received encrypted data */
+ if(backend->encdata_buffer) {
+ Curl_safefree(backend->encdata_buffer);
+ backend->encdata_length = 0;
+ backend->encdata_offset = 0;
+ backend->encdata_is_incomplete = false;
+ }
+
+ /* free internal buffer for received decrypted data */
+ if(backend->decdata_buffer) {
+ Curl_safefree(backend->decdata_buffer);
+ backend->decdata_length = 0;
+ backend->decdata_offset = 0;
+ }
+
+ return CURLE_OK;
+}
+
+static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ schannel_shutdown(cf, data);
+}
+
+static int schannel_init(void)
+{
+ return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
+}
+
+static void schannel_cleanup(void)
+{
+ Curl_sspi_global_cleanup();
+}
+
+static size_t schannel_version(char *buffer, size_t size)
+{
+ size = msnprintf(buffer, size, "Schannel");
+
+ return size;
+}
+
+static CURLcode schannel_random(struct Curl_easy *data UNUSED_PARAM,
+ unsigned char *entropy, size_t length)
+{
+ (void)data;
+
+ return Curl_win32_random(entropy, length);
+}
+
+static CURLcode pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *pinnedpubkey)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ CERT_CONTEXT *pCertContextServer = NULL;
+
+ /* Result is returned to caller */
+ CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+
+ DEBUGASSERT(backend);
+
+ /* if a path wasn't specified, don't pin */
+ if(!pinnedpubkey)
+ return CURLE_OK;
+
+ do {
+ SECURITY_STATUS sspi_status;
+ const char *x509_der;
+ DWORD x509_der_len;
+ struct Curl_X509certificate x509_parsed;
+ struct Curl_asn1Element *pubkey;
+
+ sspi_status =
+ s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle,
+ SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+ &pCertContextServer);
+
+ if((sspi_status != SEC_E_OK) || !pCertContextServer) {
+ char buffer[STRERROR_LEN];
+ failf(data, "schannel: Failed to read remote certificate context: %s",
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+ break; /* failed */
+ }
+
+
+ if(!(((pCertContextServer->dwCertEncodingType & X509_ASN_ENCODING) != 0) &&
+ (pCertContextServer->cbCertEncoded > 0)))
+ break;
+
+ x509_der = (const char *)pCertContextServer->pbCertEncoded;
+ x509_der_len = pCertContextServer->cbCertEncoded;
+ memset(&x509_parsed, 0, sizeof(x509_parsed));
+ if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len))
+ break;
+
+ pubkey = &x509_parsed.subjectPublicKeyInfo;
+ if(!pubkey->header || pubkey->end <= pubkey->header) {
+ failf(data, "SSL: failed retrieving public key from server certificate");
+ break;
+ }
+
+ result = Curl_pin_peer_pubkey(data,
+ pinnedpubkey,
+ (const unsigned char *)pubkey->header,
+ (size_t)(pubkey->end - pubkey->header));
+ if(result) {
+ failf(data, "SSL: public key does not match pinned public key");
+ }
+ } while(0);
+
+ if(pCertContextServer)
+ CertFreeCertificateContext(pCertContextServer);
+
+ return result;
+}
+
+static void schannel_checksum(const unsigned char *input,
+ size_t inputlen,
+ unsigned char *checksum,
+ size_t checksumlen,
+ DWORD provType,
+ const unsigned int algId)
+{
+ HCRYPTPROV hProv = 0;
+ HCRYPTHASH hHash = 0;
+ DWORD cbHashSize = 0;
+ DWORD dwHashSizeLen = (DWORD)sizeof(cbHashSize);
+ DWORD dwChecksumLen = (DWORD)checksumlen;
+
+ /* since this can fail in multiple ways, zero memory first so we never
+ * return old data
+ */
+ memset(checksum, 0, checksumlen);
+
+ if(!CryptAcquireContext(&hProv, NULL, NULL, provType,
+ CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
+ return; /* failed */
+
+ do {
+ if(!CryptCreateHash(hProv, algId, 0, 0, &hHash))
+ break; /* failed */
+
+ /* workaround for original MinGW, should be (const BYTE*) */
+ if(!CryptHashData(hHash, (BYTE*)input, (DWORD)inputlen, 0))
+ break; /* failed */
+
+ /* get hash size */
+ if(!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&cbHashSize,
+ &dwHashSizeLen, 0))
+ break; /* failed */
+
+ /* check hash size */
+ if(checksumlen < cbHashSize)
+ break; /* failed */
+
+ if(CryptGetHashParam(hHash, HP_HASHVAL, checksum, &dwChecksumLen, 0))
+ break; /* failed */
+ } while(0);
+
+ if(hHash)
+ CryptDestroyHash(hHash);
+
+ if(hProv)
+ CryptReleaseContext(hProv, 0);
+}
+
+static CURLcode schannel_sha256sum(const unsigned char *input,
+ size_t inputlen,
+ unsigned char *sha256sum,
+ size_t sha256len)
+{
+ schannel_checksum(input, inputlen, sha256sum, sha256len,
+ PROV_RSA_AES, CALG_SHA_256);
+ return CURLE_OK;
+}
+
+static void *schannel_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
+{
+ struct ssl_backend_data *backend = connssl->backend;
+ (void)info;
+ DEBUGASSERT(backend);
+ return &backend->ctxt->ctxt_handle;
+}
+
+const struct Curl_ssl Curl_ssl_schannel = {
+ { CURLSSLBACKEND_SCHANNEL, "schannel" }, /* info */
+
+ SSLSUPP_CERTINFO |
+#ifdef HAS_MANUAL_VERIFY_API
+ SSLSUPP_CAINFO_BLOB |
+#endif
+ SSLSUPP_PINNEDPUBKEY |
+ SSLSUPP_TLS13_CIPHERSUITES |
+ SSLSUPP_HTTPS_PROXY,
+
+ sizeof(struct ssl_backend_data),
+
+ schannel_init, /* init */
+ schannel_cleanup, /* cleanup */
+ schannel_version, /* version */
+ Curl_none_check_cxn, /* check_cxn */
+ schannel_shutdown, /* shutdown */
+ schannel_data_pending, /* data_pending */
+ schannel_random, /* random */
+ Curl_none_cert_status_request, /* cert_status_request */
+ schannel_connect, /* connect */
+ schannel_connect_nonblocking, /* connect_nonblocking */
+ Curl_ssl_get_select_socks, /* getsock */
+ schannel_get_internals, /* get_internals */
+ schannel_close, /* close_one */
+ Curl_none_close_all, /* close_all */
+ schannel_session_free, /* session_free */
+ Curl_none_set_engine, /* set_engine */
+ Curl_none_set_engine_default, /* set_engine_default */
+ Curl_none_engines_list, /* engines_list */
+ Curl_none_false_start, /* false_start */
+ schannel_sha256sum, /* sha256sum */
+ NULL, /* associate_connection */
+ NULL, /* disassociate_connection */
+ NULL, /* free_multi_ssl_backend_data */
+ schannel_recv, /* recv decrypted data */
+ schannel_send, /* send data to encrypt */
+};
+
+#endif /* USE_SCHANNEL */
diff --git a/libs/libcurl/src/vtls/schannel.h b/libs/libcurl/src/vtls/schannel.h index 24d7eff25b..234315272b 100644 --- a/libs/libcurl/src/vtls/schannel.h +++ b/libs/libcurl/src/vtls/schannel.h @@ -1,200 +1,199 @@ -#ifndef HEADER_CURL_SCHANNEL_H -#define HEADER_CURL_SCHANNEL_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2012, Marc Hoersken, <info@marc-hoersken.de>, et al. - * Copyright (C) 2012 - 2022, 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_SCHANNEL - -#define SCHANNEL_USE_BLACKLISTS 1 - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4201) -#endif -#include <subauth.h> -#ifdef _MSC_VER -#pragma warning(pop) -#endif -/* Wincrypt must be included before anything that could include OpenSSL. */ -#if defined(USE_WIN32_CRYPTO) -#include <wincrypt.h> -/* Undefine wincrypt conflicting symbols for BoringSSL. */ -#undef X509_NAME -#undef X509_EXTENSIONS -#undef PKCS7_ISSUER_AND_SERIAL -#undef PKCS7_SIGNER_INFO -#undef OCSP_REQUEST -#undef OCSP_RESPONSE -#endif - -#include <schnlsp.h> -#include <schannel.h> -#include "curl_sspi.h" - -#include "urldata.h" - -/* <wincrypt.h> has been included via the above <schnlsp.h>. - * Or in case of ldap.c, it was included via <winldap.h>. - * And since <wincrypt.h> has this: - * #define X509_NAME ((LPCSTR) 7) - * - * And in BoringSSL's <openssl/base.h> there is: - * typedef struct X509_name_st X509_NAME; - * etc. - * - * this will cause all kinds of C-preprocessing paste errors in - * BoringSSL's <openssl/x509.h>: So just undefine those defines here - * (and only here). - */ -#if defined(HAVE_BORINGSSL) || defined(OPENSSL_IS_BORINGSSL) -# undef X509_NAME -# undef X509_CERT_PAIR -# undef X509_EXTENSIONS -#endif - -extern const struct Curl_ssl Curl_ssl_schannel; - -CURLcode Curl_verify_certificate(struct Curl_easy *data, - struct connectdata *conn, int sockindex); - -/* structs to expose only in schannel.c and schannel_verify.c */ -#ifdef EXPOSE_SCHANNEL_INTERNAL_STRUCTS - -#include <wincrypt.h> - -#ifdef __MINGW32__ -#ifdef __MINGW64_VERSION_MAJOR -#define HAS_MANUAL_VERIFY_API -#endif -#else -#ifdef CERT_CHAIN_REVOCATION_CHECK_CHAIN -#define HAS_MANUAL_VERIFY_API -#endif -#endif - -#if defined(CryptStringToBinary) && defined(CRYPT_STRING_HEX) \ - && !defined(DISABLE_SCHANNEL_CLIENT_CERT) -#define HAS_CLIENT_CERT_PATH -#endif - -#ifndef SCH_CREDENTIALS_VERSION - -#define SCH_CREDENTIALS_VERSION 0x00000005 - -typedef enum _eTlsAlgorithmUsage -{ - TlsParametersCngAlgUsageKeyExchange, - TlsParametersCngAlgUsageSignature, - TlsParametersCngAlgUsageCipher, - TlsParametersCngAlgUsageDigest, - TlsParametersCngAlgUsageCertSig -} eTlsAlgorithmUsage; - -typedef struct _CRYPTO_SETTINGS -{ - eTlsAlgorithmUsage eAlgorithmUsage; - UNICODE_STRING strCngAlgId; - DWORD cChainingModes; - PUNICODE_STRING rgstrChainingModes; - DWORD dwMinBitLength; - DWORD dwMaxBitLength; -} CRYPTO_SETTINGS, * PCRYPTO_SETTINGS; - -typedef struct _TLS_PARAMETERS -{ - DWORD cAlpnIds; - PUNICODE_STRING rgstrAlpnIds; - DWORD grbitDisabledProtocols; - DWORD cDisabledCrypto; - PCRYPTO_SETTINGS pDisabledCrypto; - DWORD dwFlags; -} TLS_PARAMETERS, * PTLS_PARAMETERS; - -typedef struct _SCH_CREDENTIALS -{ - DWORD dwVersion; - DWORD dwCredFormat; - DWORD cCreds; - PCCERT_CONTEXT* paCred; - HCERTSTORE hRootStore; - - DWORD cMappers; - struct _HMAPPER **aphMappers; - - DWORD dwSessionLifespan; - DWORD dwFlags; - DWORD cTlsParameters; - PTLS_PARAMETERS pTlsParameters; -} SCH_CREDENTIALS, * PSCH_CREDENTIALS; - -#define SCH_CRED_MAX_SUPPORTED_PARAMETERS 16 -#define SCH_CRED_MAX_SUPPORTED_ALPN_IDS 16 -#define SCH_CRED_MAX_SUPPORTED_CRYPTO_SETTINGS 16 -#define SCH_CRED_MAX_SUPPORTED_CHAINING_MODES 16 - -#endif - -struct Curl_schannel_cred { - CredHandle cred_handle; - TimeStamp time_stamp; - TCHAR *sni_hostname; -#ifdef HAS_CLIENT_CERT_PATH - HCERTSTORE client_cert_store; -#endif - int refcount; -}; - -struct Curl_schannel_ctxt { - CtxtHandle ctxt_handle; - TimeStamp time_stamp; -}; - -struct ssl_backend_data { - struct Curl_schannel_cred *cred; - struct Curl_schannel_ctxt *ctxt; - SecPkgContext_StreamSizes stream_sizes; - size_t encdata_length, decdata_length; - size_t encdata_offset, decdata_offset; - unsigned char *encdata_buffer, *decdata_buffer; - /* encdata_is_incomplete: if encdata contains only a partial record that - can't be decrypted without another Curl_read_plain (that is, status is - SEC_E_INCOMPLETE_MESSAGE) then set this true. after Curl_read_plain writes - more bytes into encdata then set this back to false. */ - bool encdata_is_incomplete; - unsigned long req_flags, ret_flags; - CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */ - bool recv_sspi_close_notify; /* true if connection closed by close_notify */ - bool recv_connection_closed; /* true if connection closed, regardless how */ - bool recv_renegotiating; /* true if recv is doing renegotiation */ - bool use_alpn; /* true if ALPN is used for this connection */ -#ifdef HAS_MANUAL_VERIFY_API - bool use_manual_cred_validation; /* true if manual cred validation is used */ -#endif -}; -#endif /* EXPOSE_SCHANNEL_INTERNAL_STRUCTS */ - -#endif /* USE_SCHANNEL */ -#endif /* HEADER_CURL_SCHANNEL_H */ +#ifndef HEADER_CURL_SCHANNEL_H
+#define HEADER_CURL_SCHANNEL_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012, Marc Hoersken, <info@marc-hoersken.de>, et al.
+ * Copyright (C) 2012 - 2022, 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_SCHANNEL
+
+#define SCHANNEL_USE_BLACKLISTS 1
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4201)
+#endif
+#include <subauth.h>
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+/* Wincrypt must be included before anything that could include OpenSSL. */
+#if defined(USE_WIN32_CRYPTO)
+#include <wincrypt.h>
+/* Undefine wincrypt conflicting symbols for BoringSSL. */
+#undef X509_NAME
+#undef X509_EXTENSIONS
+#undef PKCS7_ISSUER_AND_SERIAL
+#undef PKCS7_SIGNER_INFO
+#undef OCSP_REQUEST
+#undef OCSP_RESPONSE
+#endif
+
+#include <schnlsp.h>
+#include <schannel.h>
+#include "curl_sspi.h"
+
+#include "cfilters.h"
+#include "urldata.h"
+
+/* <wincrypt.h> has been included via the above <schnlsp.h>.
+ * Or in case of ldap.c, it was included via <winldap.h>.
+ * And since <wincrypt.h> has this:
+ * #define X509_NAME ((LPCSTR) 7)
+ *
+ * And in BoringSSL's <openssl/base.h> there is:
+ * typedef struct X509_name_st X509_NAME;
+ * etc.
+ *
+ * this will cause all kinds of C-preprocessing paste errors in
+ * BoringSSL's <openssl/x509.h>: So just undefine those defines here
+ * (and only here).
+ */
+#if defined(HAVE_BORINGSSL) || defined(OPENSSL_IS_BORINGSSL)
+# undef X509_NAME
+# undef X509_CERT_PAIR
+# undef X509_EXTENSIONS
+#endif
+
+extern const struct Curl_ssl Curl_ssl_schannel;
+
+CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+
+/* structs to expose only in schannel.c and schannel_verify.c */
+#ifdef EXPOSE_SCHANNEL_INTERNAL_STRUCTS
+
+#ifdef __MINGW32__
+#ifdef __MINGW64_VERSION_MAJOR
+#define HAS_MANUAL_VERIFY_API
+#endif
+#else
+#ifdef CERT_CHAIN_REVOCATION_CHECK_CHAIN
+#define HAS_MANUAL_VERIFY_API
+#endif
+#endif
+
+#if defined(CryptStringToBinary) && defined(CRYPT_STRING_HEX) \
+ && !defined(DISABLE_SCHANNEL_CLIENT_CERT)
+#define HAS_CLIENT_CERT_PATH
+#endif
+
+#ifndef SCH_CREDENTIALS_VERSION
+
+#define SCH_CREDENTIALS_VERSION 0x00000005
+
+typedef enum _eTlsAlgorithmUsage
+{
+ TlsParametersCngAlgUsageKeyExchange,
+ TlsParametersCngAlgUsageSignature,
+ TlsParametersCngAlgUsageCipher,
+ TlsParametersCngAlgUsageDigest,
+ TlsParametersCngAlgUsageCertSig
+} eTlsAlgorithmUsage;
+
+typedef struct _CRYPTO_SETTINGS
+{
+ eTlsAlgorithmUsage eAlgorithmUsage;
+ UNICODE_STRING strCngAlgId;
+ DWORD cChainingModes;
+ PUNICODE_STRING rgstrChainingModes;
+ DWORD dwMinBitLength;
+ DWORD dwMaxBitLength;
+} CRYPTO_SETTINGS, * PCRYPTO_SETTINGS;
+
+typedef struct _TLS_PARAMETERS
+{
+ DWORD cAlpnIds;
+ PUNICODE_STRING rgstrAlpnIds;
+ DWORD grbitDisabledProtocols;
+ DWORD cDisabledCrypto;
+ PCRYPTO_SETTINGS pDisabledCrypto;
+ DWORD dwFlags;
+} TLS_PARAMETERS, * PTLS_PARAMETERS;
+
+typedef struct _SCH_CREDENTIALS
+{
+ DWORD dwVersion;
+ DWORD dwCredFormat;
+ DWORD cCreds;
+ PCCERT_CONTEXT* paCred;
+ HCERTSTORE hRootStore;
+
+ DWORD cMappers;
+ struct _HMAPPER **aphMappers;
+
+ DWORD dwSessionLifespan;
+ DWORD dwFlags;
+ DWORD cTlsParameters;
+ PTLS_PARAMETERS pTlsParameters;
+} SCH_CREDENTIALS, * PSCH_CREDENTIALS;
+
+#define SCH_CRED_MAX_SUPPORTED_PARAMETERS 16
+#define SCH_CRED_MAX_SUPPORTED_ALPN_IDS 16
+#define SCH_CRED_MAX_SUPPORTED_CRYPTO_SETTINGS 16
+#define SCH_CRED_MAX_SUPPORTED_CHAINING_MODES 16
+
+#endif
+
+struct Curl_schannel_cred {
+ CredHandle cred_handle;
+ TimeStamp time_stamp;
+ TCHAR *sni_hostname;
+#ifdef HAS_CLIENT_CERT_PATH
+ HCERTSTORE client_cert_store;
+#endif
+ int refcount;
+};
+
+struct Curl_schannel_ctxt {
+ CtxtHandle ctxt_handle;
+ TimeStamp time_stamp;
+};
+
+struct ssl_backend_data {
+ struct Curl_schannel_cred *cred;
+ struct Curl_schannel_ctxt *ctxt;
+ SecPkgContext_StreamSizes stream_sizes;
+ size_t encdata_length, decdata_length;
+ size_t encdata_offset, decdata_offset;
+ unsigned char *encdata_buffer, *decdata_buffer;
+ /* encdata_is_incomplete: if encdata contains only a partial record that
+ can't be decrypted without another Curl_read_plain (that is, status is
+ SEC_E_INCOMPLETE_MESSAGE) then set this true. after Curl_read_plain writes
+ more bytes into encdata then set this back to false. */
+ bool encdata_is_incomplete;
+ unsigned long req_flags, ret_flags;
+ CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */
+ bool recv_sspi_close_notify; /* true if connection closed by close_notify */
+ bool recv_connection_closed; /* true if connection closed, regardless how */
+ bool recv_renegotiating; /* true if recv is doing renegotiation */
+ bool use_alpn; /* true if ALPN is used for this connection */
+#ifdef HAS_MANUAL_VERIFY_API
+ bool use_manual_cred_validation; /* true if manual cred validation is used */
+#endif
+};
+#endif /* EXPOSE_SCHANNEL_INTERNAL_STRUCTS */
+
+#endif /* USE_SCHANNEL */
+#endif /* HEADER_CURL_SCHANNEL_H */
diff --git a/libs/libcurl/src/vtls/schannel_verify.c b/libs/libcurl/src/vtls/schannel_verify.c index 1ac1d3eaf5..3ec3503871 100644 --- a/libs/libcurl/src/vtls/schannel_verify.c +++ b/libs/libcurl/src/vtls/schannel_verify.c @@ -1,744 +1,746 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de> - * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com> - * Copyright (C) 2012 - 2022, 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 - * - ***************************************************************************/ - -/* - * Source file for Schannel-specific certificate verification. This code should - * only be invoked by code in schannel.c. - */ - -#include "curl_setup.h" - -#ifdef USE_SCHANNEL -#ifndef USE_WINDOWS_SSPI -# error "Can't compile SCHANNEL support without SSPI." -#endif - -#define EXPOSE_SCHANNEL_INTERNAL_STRUCTS -#include "schannel.h" - -#ifdef HAS_MANUAL_VERIFY_API - -#include "vtls.h" -#include "sendf.h" -#include "strerror.h" -#include "curl_multibyte.h" -#include "curl_printf.h" -#include "hostcheck.h" -#include "version_win32.h" - -/* The last #include file should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -#define BACKEND connssl->backend - -#define MAX_CAFILE_SIZE 1048576 /* 1 MiB */ -#define BEGIN_CERT "-----BEGIN CERTIFICATE-----" -#define END_CERT "\n-----END CERTIFICATE-----" - -struct cert_chain_engine_config_win7 { - DWORD cbSize; - HCERTSTORE hRestrictedRoot; - HCERTSTORE hRestrictedTrust; - HCERTSTORE hRestrictedOther; - DWORD cAdditionalStore; - HCERTSTORE *rghAdditionalStore; - DWORD dwFlags; - DWORD dwUrlRetrievalTimeout; - DWORD MaximumCachedCertificates; - DWORD CycleDetectionModulus; - HCERTSTORE hExclusiveRoot; - HCERTSTORE hExclusiveTrustedPeople; -}; - -static int is_cr_or_lf(char c) -{ - return c == '\r' || c == '\n'; -} - -/* Search the substring needle,needlelen into string haystack,haystacklen - * Strings don't need to be terminated by a '\0'. - * Similar of OSX/Linux memmem (not available on Visual Studio). - * Return position of beginning of first occurrence or NULL if not found - */ -static const char *c_memmem(const void *haystack, size_t haystacklen, - const void *needle, size_t needlelen) -{ - const char *p; - char first; - const char *str_limit = (const char *)haystack + haystacklen; - if(!needlelen || needlelen > haystacklen) - return NULL; - first = *(const char *)needle; - for(p = (const char *)haystack; p <= (str_limit - needlelen); p++) - if(((*p) == first) && (memcmp(p, needle, needlelen) == 0)) - return p; - - return NULL; -} - -static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, - const char *ca_buffer, - size_t ca_buffer_size, - const char *ca_file_text, - struct Curl_easy *data) -{ - const size_t begin_cert_len = strlen(BEGIN_CERT); - const size_t end_cert_len = strlen(END_CERT); - CURLcode result = CURLE_OK; - int num_certs = 0; - bool more_certs = 1; - const char *current_ca_file_ptr = ca_buffer; - const char *ca_buffer_limit = ca_buffer + ca_buffer_size; - - while(more_certs && (current_ca_file_ptr<ca_buffer_limit)) { - const char *begin_cert_ptr = c_memmem(current_ca_file_ptr, - ca_buffer_limit-current_ca_file_ptr, - BEGIN_CERT, - begin_cert_len); - if(!begin_cert_ptr || !is_cr_or_lf(begin_cert_ptr[begin_cert_len])) { - more_certs = 0; - } - else { - const char *end_cert_ptr = c_memmem(begin_cert_ptr, - ca_buffer_limit-begin_cert_ptr, - END_CERT, - end_cert_len); - if(!end_cert_ptr) { - failf(data, - "schannel: CA file '%s' is not correctly formatted", - ca_file_text); - result = CURLE_SSL_CACERT_BADFILE; - more_certs = 0; - } - else { - CERT_BLOB cert_blob; - CERT_CONTEXT *cert_context = NULL; - BOOL add_cert_result = FALSE; - DWORD actual_content_type = 0; - DWORD cert_size = (DWORD) - ((end_cert_ptr + end_cert_len) - begin_cert_ptr); - - cert_blob.pbData = (BYTE *)begin_cert_ptr; - cert_blob.cbData = cert_size; - if(!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, - &cert_blob, - CERT_QUERY_CONTENT_FLAG_CERT, - CERT_QUERY_FORMAT_FLAG_ALL, - 0, - NULL, - &actual_content_type, - NULL, - NULL, - NULL, - (const void **)&cert_context)) { - char buffer[STRERROR_LEN]; - failf(data, - "schannel: failed to extract certificate from CA file " - "'%s': %s", - ca_file_text, - Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - more_certs = 0; - } - else { - current_ca_file_ptr = begin_cert_ptr + cert_size; - - /* Sanity check that the cert_context object is the right type */ - if(CERT_QUERY_CONTENT_CERT != actual_content_type) { - failf(data, - "schannel: unexpected content type '%d' when extracting " - "certificate from CA file '%s'", - actual_content_type, ca_file_text); - result = CURLE_SSL_CACERT_BADFILE; - more_certs = 0; - } - else { - add_cert_result = - CertAddCertificateContextToStore(trust_store, - cert_context, - CERT_STORE_ADD_ALWAYS, - NULL); - CertFreeCertificateContext(cert_context); - if(!add_cert_result) { - char buffer[STRERROR_LEN]; - failf(data, - "schannel: failed to add certificate from CA file '%s' " - "to certificate store: %s", - ca_file_text, - Curl_winapi_strerror(GetLastError(), buffer, - sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - more_certs = 0; - } - else { - num_certs++; - } - } - } - } - } - } - - if(result == CURLE_OK) { - if(!num_certs) { - infof(data, - "schannel: did not add any certificates from CA file '%s'", - ca_file_text); - } - else { - infof(data, - "schannel: added %d certificate(s) from CA file '%s'", - num_certs, ca_file_text); - } - } - return result; -} - -static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, - const char *ca_file, - struct Curl_easy *data) -{ - CURLcode result; - HANDLE ca_file_handle = INVALID_HANDLE_VALUE; - LARGE_INTEGER file_size; - char *ca_file_buffer = NULL; - TCHAR *ca_file_tstr = NULL; - size_t ca_file_bufsize = 0; - DWORD total_bytes_read = 0; - - ca_file_tstr = curlx_convert_UTF8_to_tchar((char *)ca_file); - if(!ca_file_tstr) { - char buffer[STRERROR_LEN]; - failf(data, - "schannel: invalid path name for CA file '%s': %s", - ca_file, - Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - goto cleanup; - } - - /* - * Read the CA file completely into memory before parsing it. This - * optimizes for the common case where the CA file will be relatively - * small ( < 1 MiB ). - */ - ca_file_handle = CreateFile(ca_file_tstr, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - if(ca_file_handle == INVALID_HANDLE_VALUE) { - char buffer[STRERROR_LEN]; - failf(data, - "schannel: failed to open CA file '%s': %s", - ca_file, - Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - goto cleanup; - } - - if(!GetFileSizeEx(ca_file_handle, &file_size)) { - char buffer[STRERROR_LEN]; - failf(data, - "schannel: failed to determine size of CA file '%s': %s", - ca_file, - Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - goto cleanup; - } - - if(file_size.QuadPart > MAX_CAFILE_SIZE) { - failf(data, - "schannel: CA file exceeds max size of %u bytes", - MAX_CAFILE_SIZE); - result = CURLE_SSL_CACERT_BADFILE; - goto cleanup; - } - - ca_file_bufsize = (size_t)file_size.QuadPart; - ca_file_buffer = (char *)malloc(ca_file_bufsize + 1); - if(!ca_file_buffer) { - result = CURLE_OUT_OF_MEMORY; - goto cleanup; - } - - while(total_bytes_read < ca_file_bufsize) { - DWORD bytes_to_read = (DWORD)(ca_file_bufsize - total_bytes_read); - DWORD bytes_read = 0; - - if(!ReadFile(ca_file_handle, ca_file_buffer + total_bytes_read, - bytes_to_read, &bytes_read, NULL)) { - char buffer[STRERROR_LEN]; - failf(data, - "schannel: failed to read from CA file '%s': %s", - ca_file, - Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - goto cleanup; - } - if(bytes_read == 0) { - /* Premature EOF -- adjust the bufsize to the new value */ - ca_file_bufsize = total_bytes_read; - } - else { - total_bytes_read += bytes_read; - } - } - - /* Null terminate the buffer */ - ca_file_buffer[ca_file_bufsize] = '\0'; - - result = add_certs_data_to_store(trust_store, - ca_file_buffer, ca_file_bufsize, - ca_file, - data); - -cleanup: - if(ca_file_handle != INVALID_HANDLE_VALUE) { - CloseHandle(ca_file_handle); - } - Curl_safefree(ca_file_buffer); - curlx_unicodefree(ca_file_tstr); - - return result; -} - -/* - * Returns the number of characters necessary to populate all the host_names. - * If host_names is not NULL, populate it with all the host names. Each string - * in the host_names is null-terminated and the last string is double - * null-terminated. If no DNS names are found, a single null-terminated empty - * string is returned. - */ -static DWORD cert_get_name_string(struct Curl_easy *data, - CERT_CONTEXT *cert_context, - LPTSTR host_names, - DWORD length) -{ - DWORD actual_length = 0; - BOOL compute_content = FALSE; - CERT_INFO *cert_info = NULL; - CERT_EXTENSION *extension = NULL; - CRYPT_DECODE_PARA decode_para = {0, 0, 0}; - CERT_ALT_NAME_INFO *alt_name_info = NULL; - DWORD alt_name_info_size = 0; - BOOL ret_val = FALSE; - LPTSTR current_pos = NULL; - DWORD i; - - /* CERT_NAME_SEARCH_ALL_NAMES_FLAG is available from Windows 8 onwards. */ - if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL)) { -#ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG - /* CertGetNameString will provide the 8-bit character string without - * any decoding */ - DWORD name_flags = - CERT_NAME_DISABLE_IE4_UTF8_FLAG | CERT_NAME_SEARCH_ALL_NAMES_FLAG; - actual_length = CertGetNameString(cert_context, - CERT_NAME_DNS_TYPE, - name_flags, - NULL, - host_names, - length); - return actual_length; -#endif - } - - compute_content = host_names != NULL && length != 0; - - /* Initialize default return values. */ - actual_length = 1; - if(compute_content) { - *host_names = '\0'; - } - - if(!cert_context) { - failf(data, "schannel: Null certificate context."); - return actual_length; - } - - cert_info = cert_context->pCertInfo; - if(!cert_info) { - failf(data, "schannel: Null certificate info."); - return actual_length; - } - - extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2, - cert_info->cExtension, - cert_info->rgExtension); - if(!extension) { - failf(data, "schannel: CertFindExtension() returned no extension."); - return actual_length; - } - - decode_para.cbSize = sizeof(CRYPT_DECODE_PARA); - - ret_val = - CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, - szOID_SUBJECT_ALT_NAME2, - extension->Value.pbData, - extension->Value.cbData, - CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, - &decode_para, - &alt_name_info, - &alt_name_info_size); - if(!ret_val) { - failf(data, - "schannel: CryptDecodeObjectEx() returned no alternate name " - "information."); - return actual_length; - } - - current_pos = host_names; - - /* Iterate over the alternate names and populate host_names. */ - for(i = 0; i < alt_name_info->cAltEntry; i++) { - const CERT_ALT_NAME_ENTRY *entry = &alt_name_info->rgAltEntry[i]; - wchar_t *dns_w = NULL; - size_t current_length = 0; - - if(entry->dwAltNameChoice != CERT_ALT_NAME_DNS_NAME) { - continue; - } - if(!entry->pwszDNSName) { - infof(data, "schannel: Empty DNS name."); - continue; - } - current_length = wcslen(entry->pwszDNSName) + 1; - if(!compute_content) { - actual_length += (DWORD)current_length; - continue; - } - /* Sanity check to prevent buffer overrun. */ - if((actual_length + current_length) > length) { - failf(data, "schannel: Not enough memory to list all host names."); - break; - } - dns_w = entry->pwszDNSName; - /* pwszDNSName is in ia5 string format and hence doesn't contain any - * non-ascii characters. */ - while(*dns_w != '\0') { - *current_pos++ = (char)(*dns_w++); - } - *current_pos++ = '\0'; - actual_length += (DWORD)current_length; - } - if(compute_content) { - /* Last string has double null-terminator. */ - *current_pos = '\0'; - } - return actual_length; -} - -static CURLcode verify_host(struct Curl_easy *data, - CERT_CONTEXT *pCertContextServer, - const char * const conn_hostname) -{ - CURLcode result = CURLE_PEER_FAILED_VERIFICATION; - TCHAR *cert_hostname_buff = NULL; - size_t cert_hostname_buff_index = 0; - size_t hostlen = strlen(conn_hostname); - DWORD len = 0; - DWORD actual_len = 0; - - /* Determine the size of the string needed for the cert hostname */ - len = cert_get_name_string(data, pCertContextServer, NULL, 0); - if(len == 0) { - failf(data, - "schannel: CertGetNameString() returned no " - "certificate name information"); - result = CURLE_PEER_FAILED_VERIFICATION; - goto cleanup; - } - - /* CertGetNameString guarantees that the returned name will not contain - * embedded null bytes. This appears to be undocumented behavior. - */ - cert_hostname_buff = (LPTSTR)malloc(len * sizeof(TCHAR)); - if(!cert_hostname_buff) { - result = CURLE_OUT_OF_MEMORY; - goto cleanup; - } - actual_len = cert_get_name_string( - data, pCertContextServer, (LPTSTR)cert_hostname_buff, len); - - /* Sanity check */ - if(actual_len != len) { - failf(data, - "schannel: CertGetNameString() returned certificate " - "name information of unexpected size"); - result = CURLE_PEER_FAILED_VERIFICATION; - goto cleanup; - } - - /* If HAVE_CERT_NAME_SEARCH_ALL_NAMES is available, the output - * will contain all DNS names, where each name is null-terminated - * and the last DNS name is double null-terminated. Due to this - * encoding, use the length of the buffer to iterate over all names. - */ - result = CURLE_PEER_FAILED_VERIFICATION; - while(cert_hostname_buff_index < len && - cert_hostname_buff[cert_hostname_buff_index] != TEXT('\0') && - result == CURLE_PEER_FAILED_VERIFICATION) { - - char *cert_hostname; - - /* Comparing the cert name and the connection hostname encoded as UTF-8 - * is acceptable since both values are assumed to use ASCII - * (or some equivalent) encoding - */ - cert_hostname = curlx_convert_tchar_to_UTF8( - &cert_hostname_buff[cert_hostname_buff_index]); - if(!cert_hostname) { - result = CURLE_OUT_OF_MEMORY; - } - else { - if(Curl_cert_hostcheck(cert_hostname, strlen(cert_hostname), - conn_hostname, hostlen)) { - infof(data, - "schannel: connection hostname (%s) validated " - "against certificate name (%s)", - conn_hostname, cert_hostname); - result = CURLE_OK; - } - else { - size_t cert_hostname_len; - - infof(data, - "schannel: connection hostname (%s) did not match " - "against certificate name (%s)", - conn_hostname, cert_hostname); - - cert_hostname_len = - _tcslen(&cert_hostname_buff[cert_hostname_buff_index]); - - /* Move on to next cert name */ - cert_hostname_buff_index += cert_hostname_len + 1; - - result = CURLE_PEER_FAILED_VERIFICATION; - } - curlx_unicodefree(cert_hostname); - } - } - - if(result == CURLE_PEER_FAILED_VERIFICATION) { - failf(data, - "schannel: CertGetNameString() failed to match " - "connection hostname (%s) against server certificate names", - conn_hostname); - } - else if(result != CURLE_OK) - failf(data, "schannel: server certificate name verification failed"); - -cleanup: - Curl_safefree(cert_hostname_buff); - - return result; -} - -CURLcode Curl_verify_certificate(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - SECURITY_STATUS sspi_status; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - CURLcode result = CURLE_OK; - CERT_CONTEXT *pCertContextServer = NULL; - const CERT_CHAIN_CONTEXT *pChainContext = NULL; - HCERTCHAINENGINE cert_chain_engine = NULL; - HCERTSTORE trust_store = NULL; - const char * const conn_hostname = SSL_HOST_NAME(); - - DEBUGASSERT(BACKEND); - - sspi_status = - s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, - SECPKG_ATTR_REMOTE_CERT_CONTEXT, - &pCertContextServer); - - if((sspi_status != SEC_E_OK) || !pCertContextServer) { - char buffer[STRERROR_LEN]; - failf(data, "schannel: Failed to read remote certificate context: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - result = CURLE_PEER_FAILED_VERIFICATION; - } - - if(result == CURLE_OK && - (SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) && - BACKEND->use_manual_cred_validation) { - /* - * Create a chain engine that uses the certificates in the CA file as - * trusted certificates. This is only supported on Windows 7+. - */ - - if(curlx_verify_windows_version(6, 1, 0, PLATFORM_WINNT, - VERSION_LESS_THAN)) { - failf(data, "schannel: this version of Windows is too old to support " - "certificate verification via CA bundle file."); - result = CURLE_SSL_CACERT_BADFILE; - } - else { - /* Open the certificate store */ - trust_store = CertOpenStore(CERT_STORE_PROV_MEMORY, - 0, - (HCRYPTPROV)NULL, - CERT_STORE_CREATE_NEW_FLAG, - NULL); - if(!trust_store) { - char buffer[STRERROR_LEN]; - failf(data, "schannel: failed to create certificate store: %s", - Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - } - else { - const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob); - if(ca_info_blob) { - result = add_certs_data_to_store(trust_store, - (const char *)ca_info_blob->data, - ca_info_blob->len, - "(memory blob)", - data); - } - else { - result = add_certs_file_to_store(trust_store, - SSL_CONN_CONFIG(CAfile), - data); - } - } - } - - if(result == CURLE_OK) { - struct cert_chain_engine_config_win7 engine_config; - BOOL create_engine_result; - - memset(&engine_config, 0, sizeof(engine_config)); - engine_config.cbSize = sizeof(engine_config); - engine_config.hExclusiveRoot = trust_store; - - /* CertCreateCertificateChainEngine will check the expected size of the - * CERT_CHAIN_ENGINE_CONFIG structure and fail if the specified size - * does not match the expected size. When this occurs, it indicates that - * CAINFO is not supported on the version of Windows in use. - */ - create_engine_result = - CertCreateCertificateChainEngine( - (CERT_CHAIN_ENGINE_CONFIG *)&engine_config, &cert_chain_engine); - if(!create_engine_result) { - char buffer[STRERROR_LEN]; - failf(data, - "schannel: failed to create certificate chain engine: %s", - Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - } - } - } - - if(result == CURLE_OK) { - CERT_CHAIN_PARA ChainPara; - - memset(&ChainPara, 0, sizeof(ChainPara)); - ChainPara.cbSize = sizeof(ChainPara); - - if(!CertGetCertificateChain(cert_chain_engine, - pCertContextServer, - NULL, - pCertContextServer->hCertStore, - &ChainPara, - (SSL_SET_OPTION(no_revoke) ? 0 : - CERT_CHAIN_REVOCATION_CHECK_CHAIN), - NULL, - &pChainContext)) { - char buffer[STRERROR_LEN]; - failf(data, "schannel: CertGetCertificateChain failed: %s", - Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - pChainContext = NULL; - result = CURLE_PEER_FAILED_VERIFICATION; - } - - if(result == CURLE_OK) { - CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0]; - DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED); - dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus; - - if(data->set.ssl.revoke_best_effort) { - /* Ignore errors when root certificates are missing the revocation - * list URL, or when the list could not be downloaded because the - * server is currently unreachable. */ - dwTrustErrorMask &= ~(DWORD)(CERT_TRUST_REVOCATION_STATUS_UNKNOWN | - CERT_TRUST_IS_OFFLINE_REVOCATION); - } - - if(dwTrustErrorMask) { - if(dwTrustErrorMask & CERT_TRUST_IS_REVOKED) - failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_IS_REVOKED"); - else if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN) - failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_IS_PARTIAL_CHAIN"); - else if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT) - failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_IS_UNTRUSTED_ROOT"); - else if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID) - failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_IS_NOT_TIME_VALID"); - else if(dwTrustErrorMask & CERT_TRUST_REVOCATION_STATUS_UNKNOWN) - failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_REVOCATION_STATUS_UNKNOWN"); - else - failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x", - dwTrustErrorMask); - result = CURLE_PEER_FAILED_VERIFICATION; - } - } - } - - if(result == CURLE_OK) { - if(SSL_CONN_CONFIG(verifyhost)) { - result = verify_host(data, pCertContextServer, conn_hostname); - } - } - - if(cert_chain_engine) { - CertFreeCertificateChainEngine(cert_chain_engine); - } - - if(trust_store) { - CertCloseStore(trust_store, 0); - } - - if(pChainContext) - CertFreeCertificateChain(pChainContext); - - if(pCertContextServer) - CertFreeCertificateContext(pCertContextServer); - - return result; -} - -#endif /* HAS_MANUAL_VERIFY_API */ -#endif /* USE_SCHANNEL */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de>
+ * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
+ * Copyright (C) 2012 - 2022, 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
+ *
+ ***************************************************************************/
+
+/*
+ * Source file for Schannel-specific certificate verification. This code should
+ * only be invoked by code in schannel.c.
+ */
+
+#include "curl_setup.h"
+
+#ifdef USE_SCHANNEL
+#ifndef USE_WINDOWS_SSPI
+# error "Can't compile SCHANNEL support without SSPI."
+#endif
+
+#define EXPOSE_SCHANNEL_INTERNAL_STRUCTS
+#include "schannel.h"
+
+#ifdef HAS_MANUAL_VERIFY_API
+
+#include "vtls.h"
+#include "vtls_int.h"
+#include "sendf.h"
+#include "strerror.h"
+#include "curl_multibyte.h"
+#include "curl_printf.h"
+#include "hostcheck.h"
+#include "version_win32.h"
+
+/* The last #include file should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define BACKEND connssl->backend
+
+#define MAX_CAFILE_SIZE 1048576 /* 1 MiB */
+#define BEGIN_CERT "-----BEGIN CERTIFICATE-----"
+#define END_CERT "\n-----END CERTIFICATE-----"
+
+struct cert_chain_engine_config_win7 {
+ DWORD cbSize;
+ HCERTSTORE hRestrictedRoot;
+ HCERTSTORE hRestrictedTrust;
+ HCERTSTORE hRestrictedOther;
+ DWORD cAdditionalStore;
+ HCERTSTORE *rghAdditionalStore;
+ DWORD dwFlags;
+ DWORD dwUrlRetrievalTimeout;
+ DWORD MaximumCachedCertificates;
+ DWORD CycleDetectionModulus;
+ HCERTSTORE hExclusiveRoot;
+ HCERTSTORE hExclusiveTrustedPeople;
+};
+
+static int is_cr_or_lf(char c)
+{
+ return c == '\r' || c == '\n';
+}
+
+/* Search the substring needle,needlelen into string haystack,haystacklen
+ * Strings don't need to be terminated by a '\0'.
+ * Similar of OSX/Linux memmem (not available on Visual Studio).
+ * Return position of beginning of first occurrence or NULL if not found
+ */
+static const char *c_memmem(const void *haystack, size_t haystacklen,
+ const void *needle, size_t needlelen)
+{
+ const char *p;
+ char first;
+ const char *str_limit = (const char *)haystack + haystacklen;
+ if(!needlelen || needlelen > haystacklen)
+ return NULL;
+ first = *(const char *)needle;
+ for(p = (const char *)haystack; p <= (str_limit - needlelen); p++)
+ if(((*p) == first) && (memcmp(p, needle, needlelen) == 0))
+ return p;
+
+ return NULL;
+}
+
+static CURLcode add_certs_data_to_store(HCERTSTORE trust_store,
+ const char *ca_buffer,
+ size_t ca_buffer_size,
+ const char *ca_file_text,
+ struct Curl_easy *data)
+{
+ const size_t begin_cert_len = strlen(BEGIN_CERT);
+ const size_t end_cert_len = strlen(END_CERT);
+ CURLcode result = CURLE_OK;
+ int num_certs = 0;
+ bool more_certs = 1;
+ const char *current_ca_file_ptr = ca_buffer;
+ const char *ca_buffer_limit = ca_buffer + ca_buffer_size;
+
+ while(more_certs && (current_ca_file_ptr<ca_buffer_limit)) {
+ const char *begin_cert_ptr = c_memmem(current_ca_file_ptr,
+ ca_buffer_limit-current_ca_file_ptr,
+ BEGIN_CERT,
+ begin_cert_len);
+ if(!begin_cert_ptr || !is_cr_or_lf(begin_cert_ptr[begin_cert_len])) {
+ more_certs = 0;
+ }
+ else {
+ const char *end_cert_ptr = c_memmem(begin_cert_ptr,
+ ca_buffer_limit-begin_cert_ptr,
+ END_CERT,
+ end_cert_len);
+ if(!end_cert_ptr) {
+ failf(data,
+ "schannel: CA file '%s' is not correctly formatted",
+ ca_file_text);
+ result = CURLE_SSL_CACERT_BADFILE;
+ more_certs = 0;
+ }
+ else {
+ CERT_BLOB cert_blob;
+ CERT_CONTEXT *cert_context = NULL;
+ BOOL add_cert_result = FALSE;
+ DWORD actual_content_type = 0;
+ DWORD cert_size = (DWORD)
+ ((end_cert_ptr + end_cert_len) - begin_cert_ptr);
+
+ cert_blob.pbData = (BYTE *)begin_cert_ptr;
+ cert_blob.cbData = cert_size;
+ if(!CryptQueryObject(CERT_QUERY_OBJECT_BLOB,
+ &cert_blob,
+ CERT_QUERY_CONTENT_FLAG_CERT,
+ CERT_QUERY_FORMAT_FLAG_ALL,
+ 0,
+ NULL,
+ &actual_content_type,
+ NULL,
+ NULL,
+ NULL,
+ (const void **)&cert_context)) {
+ char buffer[STRERROR_LEN];
+ failf(data,
+ "schannel: failed to extract certificate from CA file "
+ "'%s': %s",
+ ca_file_text,
+ Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ result = CURLE_SSL_CACERT_BADFILE;
+ more_certs = 0;
+ }
+ else {
+ current_ca_file_ptr = begin_cert_ptr + cert_size;
+
+ /* Sanity check that the cert_context object is the right type */
+ if(CERT_QUERY_CONTENT_CERT != actual_content_type) {
+ failf(data,
+ "schannel: unexpected content type '%d' when extracting "
+ "certificate from CA file '%s'",
+ actual_content_type, ca_file_text);
+ result = CURLE_SSL_CACERT_BADFILE;
+ more_certs = 0;
+ }
+ else {
+ add_cert_result =
+ CertAddCertificateContextToStore(trust_store,
+ cert_context,
+ CERT_STORE_ADD_ALWAYS,
+ NULL);
+ CertFreeCertificateContext(cert_context);
+ if(!add_cert_result) {
+ char buffer[STRERROR_LEN];
+ failf(data,
+ "schannel: failed to add certificate from CA file '%s' "
+ "to certificate store: %s",
+ ca_file_text,
+ Curl_winapi_strerror(GetLastError(), buffer,
+ sizeof(buffer)));
+ result = CURLE_SSL_CACERT_BADFILE;
+ more_certs = 0;
+ }
+ else {
+ num_certs++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(result == CURLE_OK) {
+ if(!num_certs) {
+ infof(data,
+ "schannel: did not add any certificates from CA file '%s'",
+ ca_file_text);
+ }
+ else {
+ infof(data,
+ "schannel: added %d certificate(s) from CA file '%s'",
+ num_certs, ca_file_text);
+ }
+ }
+ return result;
+}
+
+static CURLcode add_certs_file_to_store(HCERTSTORE trust_store,
+ const char *ca_file,
+ struct Curl_easy *data)
+{
+ CURLcode result;
+ HANDLE ca_file_handle = INVALID_HANDLE_VALUE;
+ LARGE_INTEGER file_size;
+ char *ca_file_buffer = NULL;
+ TCHAR *ca_file_tstr = NULL;
+ size_t ca_file_bufsize = 0;
+ DWORD total_bytes_read = 0;
+
+ ca_file_tstr = curlx_convert_UTF8_to_tchar((char *)ca_file);
+ if(!ca_file_tstr) {
+ char buffer[STRERROR_LEN];
+ failf(data,
+ "schannel: invalid path name for CA file '%s': %s",
+ ca_file,
+ Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ result = CURLE_SSL_CACERT_BADFILE;
+ goto cleanup;
+ }
+
+ /*
+ * Read the CA file completely into memory before parsing it. This
+ * optimizes for the common case where the CA file will be relatively
+ * small ( < 1 MiB ).
+ */
+ ca_file_handle = CreateFile(ca_file_tstr,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ if(ca_file_handle == INVALID_HANDLE_VALUE) {
+ char buffer[STRERROR_LEN];
+ failf(data,
+ "schannel: failed to open CA file '%s': %s",
+ ca_file,
+ Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ result = CURLE_SSL_CACERT_BADFILE;
+ goto cleanup;
+ }
+
+ if(!GetFileSizeEx(ca_file_handle, &file_size)) {
+ char buffer[STRERROR_LEN];
+ failf(data,
+ "schannel: failed to determine size of CA file '%s': %s",
+ ca_file,
+ Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ result = CURLE_SSL_CACERT_BADFILE;
+ goto cleanup;
+ }
+
+ if(file_size.QuadPart > MAX_CAFILE_SIZE) {
+ failf(data,
+ "schannel: CA file exceeds max size of %u bytes",
+ MAX_CAFILE_SIZE);
+ result = CURLE_SSL_CACERT_BADFILE;
+ goto cleanup;
+ }
+
+ ca_file_bufsize = (size_t)file_size.QuadPart;
+ ca_file_buffer = (char *)malloc(ca_file_bufsize + 1);
+ if(!ca_file_buffer) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto cleanup;
+ }
+
+ while(total_bytes_read < ca_file_bufsize) {
+ DWORD bytes_to_read = (DWORD)(ca_file_bufsize - total_bytes_read);
+ DWORD bytes_read = 0;
+
+ if(!ReadFile(ca_file_handle, ca_file_buffer + total_bytes_read,
+ bytes_to_read, &bytes_read, NULL)) {
+ char buffer[STRERROR_LEN];
+ failf(data,
+ "schannel: failed to read from CA file '%s': %s",
+ ca_file,
+ Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ result = CURLE_SSL_CACERT_BADFILE;
+ goto cleanup;
+ }
+ if(bytes_read == 0) {
+ /* Premature EOF -- adjust the bufsize to the new value */
+ ca_file_bufsize = total_bytes_read;
+ }
+ else {
+ total_bytes_read += bytes_read;
+ }
+ }
+
+ /* Null terminate the buffer */
+ ca_file_buffer[ca_file_bufsize] = '\0';
+
+ result = add_certs_data_to_store(trust_store,
+ ca_file_buffer, ca_file_bufsize,
+ ca_file,
+ data);
+
+cleanup:
+ if(ca_file_handle != INVALID_HANDLE_VALUE) {
+ CloseHandle(ca_file_handle);
+ }
+ Curl_safefree(ca_file_buffer);
+ curlx_unicodefree(ca_file_tstr);
+
+ return result;
+}
+
+/*
+ * Returns the number of characters necessary to populate all the host_names.
+ * If host_names is not NULL, populate it with all the host names. Each string
+ * in the host_names is null-terminated and the last string is double
+ * null-terminated. If no DNS names are found, a single null-terminated empty
+ * string is returned.
+ */
+static DWORD cert_get_name_string(struct Curl_easy *data,
+ CERT_CONTEXT *cert_context,
+ LPTSTR host_names,
+ DWORD length)
+{
+ DWORD actual_length = 0;
+ BOOL compute_content = FALSE;
+ CERT_INFO *cert_info = NULL;
+ CERT_EXTENSION *extension = NULL;
+ CRYPT_DECODE_PARA decode_para = {0, 0, 0};
+ CERT_ALT_NAME_INFO *alt_name_info = NULL;
+ DWORD alt_name_info_size = 0;
+ BOOL ret_val = FALSE;
+ LPTSTR current_pos = NULL;
+ DWORD i;
+
+ /* CERT_NAME_SEARCH_ALL_NAMES_FLAG is available from Windows 8 onwards. */
+ if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL)) {
+#ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG
+ /* CertGetNameString will provide the 8-bit character string without
+ * any decoding */
+ DWORD name_flags =
+ CERT_NAME_DISABLE_IE4_UTF8_FLAG | CERT_NAME_SEARCH_ALL_NAMES_FLAG;
+ actual_length = CertGetNameString(cert_context,
+ CERT_NAME_DNS_TYPE,
+ name_flags,
+ NULL,
+ host_names,
+ length);
+ return actual_length;
+#endif
+ }
+
+ compute_content = host_names != NULL && length != 0;
+
+ /* Initialize default return values. */
+ actual_length = 1;
+ if(compute_content) {
+ *host_names = '\0';
+ }
+
+ if(!cert_context) {
+ failf(data, "schannel: Null certificate context.");
+ return actual_length;
+ }
+
+ cert_info = cert_context->pCertInfo;
+ if(!cert_info) {
+ failf(data, "schannel: Null certificate info.");
+ return actual_length;
+ }
+
+ extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2,
+ cert_info->cExtension,
+ cert_info->rgExtension);
+ if(!extension) {
+ failf(data, "schannel: CertFindExtension() returned no extension.");
+ return actual_length;
+ }
+
+ decode_para.cbSize = sizeof(CRYPT_DECODE_PARA);
+
+ ret_val =
+ CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ szOID_SUBJECT_ALT_NAME2,
+ extension->Value.pbData,
+ extension->Value.cbData,
+ CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
+ &decode_para,
+ &alt_name_info,
+ &alt_name_info_size);
+ if(!ret_val) {
+ failf(data,
+ "schannel: CryptDecodeObjectEx() returned no alternate name "
+ "information.");
+ return actual_length;
+ }
+
+ current_pos = host_names;
+
+ /* Iterate over the alternate names and populate host_names. */
+ for(i = 0; i < alt_name_info->cAltEntry; i++) {
+ const CERT_ALT_NAME_ENTRY *entry = &alt_name_info->rgAltEntry[i];
+ wchar_t *dns_w = NULL;
+ size_t current_length = 0;
+
+ if(entry->dwAltNameChoice != CERT_ALT_NAME_DNS_NAME) {
+ continue;
+ }
+ if(!entry->pwszDNSName) {
+ infof(data, "schannel: Empty DNS name.");
+ continue;
+ }
+ current_length = wcslen(entry->pwszDNSName) + 1;
+ if(!compute_content) {
+ actual_length += (DWORD)current_length;
+ continue;
+ }
+ /* Sanity check to prevent buffer overrun. */
+ if((actual_length + current_length) > length) {
+ failf(data, "schannel: Not enough memory to list all host names.");
+ break;
+ }
+ dns_w = entry->pwszDNSName;
+ /* pwszDNSName is in ia5 string format and hence doesn't contain any
+ * non-ascii characters. */
+ while(*dns_w != '\0') {
+ *current_pos++ = (char)(*dns_w++);
+ }
+ *current_pos++ = '\0';
+ actual_length += (DWORD)current_length;
+ }
+ if(compute_content) {
+ /* Last string has double null-terminator. */
+ *current_pos = '\0';
+ }
+ return actual_length;
+}
+
+static CURLcode verify_host(struct Curl_easy *data,
+ CERT_CONTEXT *pCertContextServer,
+ const char *conn_hostname)
+{
+ CURLcode result = CURLE_PEER_FAILED_VERIFICATION;
+ TCHAR *cert_hostname_buff = NULL;
+ size_t cert_hostname_buff_index = 0;
+ size_t hostlen = strlen(conn_hostname);
+ DWORD len = 0;
+ DWORD actual_len = 0;
+
+ /* Determine the size of the string needed for the cert hostname */
+ len = cert_get_name_string(data, pCertContextServer, NULL, 0);
+ if(len == 0) {
+ failf(data,
+ "schannel: CertGetNameString() returned no "
+ "certificate name information");
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ goto cleanup;
+ }
+
+ /* CertGetNameString guarantees that the returned name will not contain
+ * embedded null bytes. This appears to be undocumented behavior.
+ */
+ cert_hostname_buff = (LPTSTR)malloc(len * sizeof(TCHAR));
+ if(!cert_hostname_buff) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto cleanup;
+ }
+ actual_len = cert_get_name_string(
+ data, pCertContextServer, (LPTSTR)cert_hostname_buff, len);
+
+ /* Sanity check */
+ if(actual_len != len) {
+ failf(data,
+ "schannel: CertGetNameString() returned certificate "
+ "name information of unexpected size");
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ goto cleanup;
+ }
+
+ /* If HAVE_CERT_NAME_SEARCH_ALL_NAMES is available, the output
+ * will contain all DNS names, where each name is null-terminated
+ * and the last DNS name is double null-terminated. Due to this
+ * encoding, use the length of the buffer to iterate over all names.
+ */
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ while(cert_hostname_buff_index < len &&
+ cert_hostname_buff[cert_hostname_buff_index] != TEXT('\0') &&
+ result == CURLE_PEER_FAILED_VERIFICATION) {
+
+ char *cert_hostname;
+
+ /* Comparing the cert name and the connection hostname encoded as UTF-8
+ * is acceptable since both values are assumed to use ASCII
+ * (or some equivalent) encoding
+ */
+ cert_hostname = curlx_convert_tchar_to_UTF8(
+ &cert_hostname_buff[cert_hostname_buff_index]);
+ if(!cert_hostname) {
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ else {
+ if(Curl_cert_hostcheck(cert_hostname, strlen(cert_hostname),
+ conn_hostname, hostlen)) {
+ infof(data,
+ "schannel: connection hostname (%s) validated "
+ "against certificate name (%s)",
+ conn_hostname, cert_hostname);
+ result = CURLE_OK;
+ }
+ else {
+ size_t cert_hostname_len;
+
+ infof(data,
+ "schannel: connection hostname (%s) did not match "
+ "against certificate name (%s)",
+ conn_hostname, cert_hostname);
+
+ cert_hostname_len =
+ _tcslen(&cert_hostname_buff[cert_hostname_buff_index]);
+
+ /* Move on to next cert name */
+ cert_hostname_buff_index += cert_hostname_len + 1;
+
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+ curlx_unicodefree(cert_hostname);
+ }
+ }
+
+ if(result == CURLE_PEER_FAILED_VERIFICATION) {
+ failf(data,
+ "schannel: CertGetNameString() failed to match "
+ "connection hostname (%s) against server certificate names",
+ conn_hostname);
+ }
+ else if(result != CURLE_OK)
+ failf(data, "schannel: server certificate name verification failed");
+
+cleanup:
+ Curl_safefree(cert_hostname_buff);
+
+ return result;
+}
+
+CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ SECURITY_STATUS sspi_status;
+ CURLcode result = CURLE_OK;
+ CERT_CONTEXT *pCertContextServer = NULL;
+ const CERT_CHAIN_CONTEXT *pChainContext = NULL;
+ HCERTCHAINENGINE cert_chain_engine = NULL;
+ HCERTSTORE trust_store = NULL;
+
+ DEBUGASSERT(BACKEND);
+
+ sspi_status =
+ s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
+ SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+ &pCertContextServer);
+
+ if((sspi_status != SEC_E_OK) || !pCertContextServer) {
+ char buffer[STRERROR_LEN];
+ failf(data, "schannel: Failed to read remote certificate context: %s",
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+
+ if(result == CURLE_OK &&
+ (conn_config->CAfile || conn_config->ca_info_blob) &&
+ BACKEND->use_manual_cred_validation) {
+ /*
+ * Create a chain engine that uses the certificates in the CA file as
+ * trusted certificates. This is only supported on Windows 7+.
+ */
+
+ if(curlx_verify_windows_version(6, 1, 0, PLATFORM_WINNT,
+ VERSION_LESS_THAN)) {
+ failf(data, "schannel: this version of Windows is too old to support "
+ "certificate verification via CA bundle file.");
+ result = CURLE_SSL_CACERT_BADFILE;
+ }
+ else {
+ /* Open the certificate store */
+ trust_store = CertOpenStore(CERT_STORE_PROV_MEMORY,
+ 0,
+ (HCRYPTPROV)NULL,
+ CERT_STORE_CREATE_NEW_FLAG,
+ NULL);
+ if(!trust_store) {
+ char buffer[STRERROR_LEN];
+ failf(data, "schannel: failed to create certificate store: %s",
+ Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ result = CURLE_SSL_CACERT_BADFILE;
+ }
+ else {
+ const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
+ if(ca_info_blob) {
+ result = add_certs_data_to_store(trust_store,
+ (const char *)ca_info_blob->data,
+ ca_info_blob->len,
+ "(memory blob)",
+ data);
+ }
+ else {
+ result = add_certs_file_to_store(trust_store,
+ conn_config->CAfile,
+ data);
+ }
+ }
+ }
+
+ if(result == CURLE_OK) {
+ struct cert_chain_engine_config_win7 engine_config;
+ BOOL create_engine_result;
+
+ memset(&engine_config, 0, sizeof(engine_config));
+ engine_config.cbSize = sizeof(engine_config);
+ engine_config.hExclusiveRoot = trust_store;
+
+ /* CertCreateCertificateChainEngine will check the expected size of the
+ * CERT_CHAIN_ENGINE_CONFIG structure and fail if the specified size
+ * does not match the expected size. When this occurs, it indicates that
+ * CAINFO is not supported on the version of Windows in use.
+ */
+ create_engine_result =
+ CertCreateCertificateChainEngine(
+ (CERT_CHAIN_ENGINE_CONFIG *)&engine_config, &cert_chain_engine);
+ if(!create_engine_result) {
+ char buffer[STRERROR_LEN];
+ failf(data,
+ "schannel: failed to create certificate chain engine: %s",
+ Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ result = CURLE_SSL_CACERT_BADFILE;
+ }
+ }
+ }
+
+ if(result == CURLE_OK) {
+ CERT_CHAIN_PARA ChainPara;
+
+ memset(&ChainPara, 0, sizeof(ChainPara));
+ ChainPara.cbSize = sizeof(ChainPara);
+
+ if(!CertGetCertificateChain(cert_chain_engine,
+ pCertContextServer,
+ NULL,
+ pCertContextServer->hCertStore,
+ &ChainPara,
+ (ssl_config->no_revoke ? 0 :
+ CERT_CHAIN_REVOCATION_CHECK_CHAIN),
+ NULL,
+ &pChainContext)) {
+ char buffer[STRERROR_LEN];
+ failf(data, "schannel: CertGetCertificateChain failed: %s",
+ Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ pChainContext = NULL;
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+
+ if(result == CURLE_OK) {
+ CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0];
+ DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED);
+ dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus;
+
+ if(data->set.ssl.revoke_best_effort) {
+ /* Ignore errors when root certificates are missing the revocation
+ * list URL, or when the list could not be downloaded because the
+ * server is currently unreachable. */
+ dwTrustErrorMask &= ~(DWORD)(CERT_TRUST_REVOCATION_STATUS_UNKNOWN |
+ CERT_TRUST_IS_OFFLINE_REVOCATION);
+ }
+
+ if(dwTrustErrorMask) {
+ if(dwTrustErrorMask & CERT_TRUST_IS_REVOKED)
+ failf(data, "schannel: CertGetCertificateChain trust error"
+ " CERT_TRUST_IS_REVOKED");
+ else if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN)
+ failf(data, "schannel: CertGetCertificateChain trust error"
+ " CERT_TRUST_IS_PARTIAL_CHAIN");
+ else if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT)
+ failf(data, "schannel: CertGetCertificateChain trust error"
+ " CERT_TRUST_IS_UNTRUSTED_ROOT");
+ else if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID)
+ failf(data, "schannel: CertGetCertificateChain trust error"
+ " CERT_TRUST_IS_NOT_TIME_VALID");
+ else if(dwTrustErrorMask & CERT_TRUST_REVOCATION_STATUS_UNKNOWN)
+ failf(data, "schannel: CertGetCertificateChain trust error"
+ " CERT_TRUST_REVOCATION_STATUS_UNKNOWN");
+ else
+ failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
+ dwTrustErrorMask);
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+ }
+ }
+
+ if(result == CURLE_OK) {
+ if(conn_config->verifyhost) {
+ result = verify_host(data, pCertContextServer, connssl->hostname);
+ }
+ }
+
+ if(cert_chain_engine) {
+ CertFreeCertificateChainEngine(cert_chain_engine);
+ }
+
+ if(trust_store) {
+ CertCloseStore(trust_store, 0);
+ }
+
+ if(pChainContext)
+ CertFreeCertificateChain(pChainContext);
+
+ if(pCertContextServer)
+ CertFreeCertificateContext(pCertContextServer);
+
+ return result;
+}
+
+#endif /* HAS_MANUAL_VERIFY_API */
+#endif /* USE_SCHANNEL */
diff --git a/libs/libcurl/src/vtls/sectransp.c b/libs/libcurl/src/vtls/sectransp.c index c764e3631b..c5707a4df1 100644 --- a/libs/libcurl/src/vtls/sectransp.c +++ b/libs/libcurl/src/vtls/sectransp.c @@ -1,3542 +1,3474 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2012 - 2017, Nick Zitzmann, <nickzman@gmail.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 - * - ***************************************************************************/ - -/* - * Source file for all iOS and macOS SecureTransport-specific code for the - * TLS/SSL layer. No code but vtls.c should ever call or use these functions. - */ - -#include "curl_setup.h" - -#include "urldata.h" /* for the Curl_easy definition */ -#include "curl_base64.h" -#include "strtok.h" -#include "multiif.h" -#include "strcase.h" -#include "x509asn1.h" -#include "strerror.h" - -#ifdef USE_SECTRANSP - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wtautological-pointer-compare" -#endif /* __clang__ */ - -#include <limits.h> - -#include <Security/Security.h> -/* For some reason, when building for iOS, the omnibus header above does - * not include SecureTransport.h as of iOS SDK 5.1. */ -#include <Security/SecureTransport.h> -#include <CoreFoundation/CoreFoundation.h> -#include <CommonCrypto/CommonDigest.h> - -/* The Security framework has changed greatly between iOS and different macOS - versions, and we will try to support as many of them as we can (back to - Leopard and iOS 5) by using macros and weak-linking. - - In general, you want to build this using the most recent OS SDK, since some - features require curl to be built against the latest SDK. TLS 1.1 and 1.2 - support, for instance, require the macOS 10.8 SDK or later. TLS 1.3 - requires the macOS 10.13 or iOS 11 SDK or later. */ -#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) - -#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 -#error "The Secure Transport back-end requires Leopard or later." -#endif /* MAC_OS_X_VERSION_MAX_ALLOWED < 1050 */ - -#define CURL_BUILD_IOS 0 -#define CURL_BUILD_IOS_7 0 -#define CURL_BUILD_IOS_9 0 -#define CURL_BUILD_IOS_11 0 -#define CURL_BUILD_IOS_13 0 -#define CURL_BUILD_MAC 1 -/* This is the maximum API level we are allowed to use when building: */ -#define CURL_BUILD_MAC_10_5 MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 -#define CURL_BUILD_MAC_10_6 MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 -#define CURL_BUILD_MAC_10_7 MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 -#define CURL_BUILD_MAC_10_8 MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 -#define CURL_BUILD_MAC_10_9 MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 -#define CURL_BUILD_MAC_10_11 MAC_OS_X_VERSION_MAX_ALLOWED >= 101100 -#define CURL_BUILD_MAC_10_13 MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 -#define CURL_BUILD_MAC_10_15 MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 -/* These macros mean "the following code is present to allow runtime backward - compatibility with at least this cat or earlier": - (You set this at build-time using the compiler command line option - "-mmacosx-version-min.") */ -#define CURL_SUPPORT_MAC_10_5 MAC_OS_X_VERSION_MIN_REQUIRED <= 1050 -#define CURL_SUPPORT_MAC_10_6 MAC_OS_X_VERSION_MIN_REQUIRED <= 1060 -#define CURL_SUPPORT_MAC_10_7 MAC_OS_X_VERSION_MIN_REQUIRED <= 1070 -#define CURL_SUPPORT_MAC_10_8 MAC_OS_X_VERSION_MIN_REQUIRED <= 1080 -#define CURL_SUPPORT_MAC_10_9 MAC_OS_X_VERSION_MIN_REQUIRED <= 1090 - -#elif TARGET_OS_EMBEDDED || TARGET_OS_IPHONE -#define CURL_BUILD_IOS 1 -#define CURL_BUILD_IOS_7 __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 -#define CURL_BUILD_IOS_9 __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000 -#define CURL_BUILD_IOS_11 __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 -#define CURL_BUILD_IOS_13 __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 -#define CURL_BUILD_MAC 0 -#define CURL_BUILD_MAC_10_5 0 -#define CURL_BUILD_MAC_10_6 0 -#define CURL_BUILD_MAC_10_7 0 -#define CURL_BUILD_MAC_10_8 0 -#define CURL_BUILD_MAC_10_9 0 -#define CURL_BUILD_MAC_10_11 0 -#define CURL_BUILD_MAC_10_13 0 -#define CURL_BUILD_MAC_10_15 0 -#define CURL_SUPPORT_MAC_10_5 0 -#define CURL_SUPPORT_MAC_10_6 0 -#define CURL_SUPPORT_MAC_10_7 0 -#define CURL_SUPPORT_MAC_10_8 0 -#define CURL_SUPPORT_MAC_10_9 0 - -#else -#error "The Secure Transport back-end requires iOS or macOS." -#endif /* (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */ - -#if CURL_BUILD_MAC -#include <sys/sysctl.h> -#endif /* CURL_BUILD_MAC */ - -#include "urldata.h" -#include "sendf.h" -#include "inet_pton.h" -#include "connect.h" -#include "select.h" -#include "vtls.h" -#include "sectransp.h" -#include "curl_printf.h" -#include "strdup.h" - -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -/* From MacTypes.h (which we can't include because it isn't present in iOS: */ -#define ioErr -36 -#define paramErr -50 - -struct ssl_backend_data { - SSLContextRef ssl_ctx; - curl_socket_t ssl_sockfd; - bool ssl_direction; /* true if writing, false if reading */ - size_t ssl_write_buffered_length; -}; - -struct st_cipher { - const char *name; /* Cipher suite IANA name. It starts with "TLS_" prefix */ - const char *alias_name; /* Alias name is the same as OpenSSL cipher name */ - SSLCipherSuite num; /* Cipher suite code/number defined in IANA registry */ - bool weak; /* Flag to mark cipher as weak based on previous implementation - of Secure Transport back-end by CURL */ -}; - -/* Macro to initialize st_cipher data structure: stringify id to name, cipher - number/id, 'weak' suite flag - */ -#define CIPHER_DEF(num, alias, weak) \ - { #num, alias, num, weak } - -/* - Macro to initialize st_cipher data structure with name, code (IANA cipher - number/id value), and 'weak' suite flag. The first 28 cipher suite numbers - have the same IANA code for both SSL and TLS standards: numbers 0x0000 to - 0x001B. They have different names though. The first 4 letters of the cipher - suite name are the protocol name: "SSL_" or "TLS_", rest of the IANA name is - the same for both SSL and TLS cipher suite name. - The second part of the problem is that macOS/iOS SDKs don't define all TLS - codes but only 12 of them. The SDK defines all SSL codes though, i.e. SSL_NUM - constant is always defined for those 28 ciphers while TLS_NUM is defined only - for 12 of the first 28 ciphers. Those 12 TLS cipher codes match to - corresponding SSL enum value and represent the same cipher suite. Therefore - we'll use the SSL enum value for those cipher suites because it is defined - for all 28 of them. - We make internal data consistent and based on TLS names, i.e. all st_cipher - item names start with the "TLS_" prefix. - Summarizing all the above, those 28 first ciphers are presented in our table - with both TLS and SSL names. Their cipher numbers are assigned based on the - SDK enum value for the SSL cipher, which matches to IANA TLS number. - */ -#define CIPHER_DEF_SSLTLS(num_wo_prefix, alias, weak) \ - { "TLS_" #num_wo_prefix, alias, SSL_##num_wo_prefix, weak } - -/* - Cipher suites were marked as weak based on the following: - RC4 encryption - rfc7465, the document contains a list of deprecated ciphers. - Marked in the code below as weak. - RC2 encryption - many mentions, was found vulnerable to a relatively easy - attack https://link.springer.com/chapter/10.1007%2F3-540-69710-1_14 - Marked in the code below as weak. - DES and IDEA encryption - rfc5469, has a list of deprecated ciphers. - Marked in the code below as weak. - Anonymous Diffie-Hellman authentication and anonymous elliptic curve - Diffie-Hellman - vulnerable to a man-in-the-middle attack. Deprecated by - RFC 4346 aka TLS 1.1 (section A.5, page 60) - Null bulk encryption suites - not encrypted communication - Export ciphers, i.e. ciphers with restrictions to be used outside the US for - software exported to some countries, they were excluded from TLS 1.1 - version. More precisely, they were noted as ciphers which MUST NOT be - negotiated in RFC 4346 aka TLS 1.1 (section A.5, pages 60 and 61). - All of those filters were considered weak because they contain a weak - algorithm like DES, RC2 or RC4, and already considered weak by other - criteria. - 3DES - NIST deprecated it and is going to retire it by 2023 - https://csrc.nist.gov/News/2017/Update-to-Current-Use-and-Deprecation-of-TDEA - OpenSSL https://www.openssl.org/blog/blog/2016/08/24/sweet32/ also - deprecated those ciphers. Some other libraries also consider it - vulnerable or at least not strong enough. - - CBC ciphers are vulnerable with SSL3.0 and TLS1.0: - https://www.cisco.com/c/en/us/support/docs/security/email-security-appliance - /118518-technote-esa-00.html - We don't take care of this issue because it is resolved by later TLS - versions and for us, it requires more complicated checks, we need to - check a protocol version also. Vulnerability doesn't look very critical - and we do not filter out those cipher suites. - */ - -#define CIPHER_WEAK_NOT_ENCRYPTED TRUE -#define CIPHER_WEAK_RC_ENCRYPTION TRUE -#define CIPHER_WEAK_DES_ENCRYPTION TRUE -#define CIPHER_WEAK_IDEA_ENCRYPTION TRUE -#define CIPHER_WEAK_ANON_AUTH TRUE -#define CIPHER_WEAK_3DES_ENCRYPTION TRUE -#define CIPHER_STRONG_ENOUGH FALSE - -/* Please do not change the order of the first ciphers available for SSL. - Do not insert and do not delete any of them. Code below - depends on their order and continuity. - If you add a new cipher, please maintain order by number, i.e. - insert in between existing items to appropriate place based on - cipher suite IANA number -*/ -const static struct st_cipher ciphertable[] = { - /* SSL version 3.0 and initial TLS 1.0 cipher suites. - Defined since SDK 10.2.8 */ - CIPHER_DEF_SSLTLS(NULL_WITH_NULL_NULL, /* 0x0000 */ - NULL, - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF_SSLTLS(RSA_WITH_NULL_MD5, /* 0x0001 */ - "NULL-MD5", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF_SSLTLS(RSA_WITH_NULL_SHA, /* 0x0002 */ - "NULL-SHA", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF_SSLTLS(RSA_EXPORT_WITH_RC4_40_MD5, /* 0x0003 */ - "EXP-RC4-MD5", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF_SSLTLS(RSA_WITH_RC4_128_MD5, /* 0x0004 */ - "RC4-MD5", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF_SSLTLS(RSA_WITH_RC4_128_SHA, /* 0x0005 */ - "RC4-SHA", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF_SSLTLS(RSA_EXPORT_WITH_RC2_CBC_40_MD5, /* 0x0006 */ - "EXP-RC2-CBC-MD5", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF_SSLTLS(RSA_WITH_IDEA_CBC_SHA, /* 0x0007 */ - "IDEA-CBC-SHA", - CIPHER_WEAK_IDEA_ENCRYPTION), - CIPHER_DEF_SSLTLS(RSA_EXPORT_WITH_DES40_CBC_SHA, /* 0x0008 */ - "EXP-DES-CBC-SHA", - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(RSA_WITH_DES_CBC_SHA, /* 0x0009 */ - "DES-CBC-SHA", - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(RSA_WITH_3DES_EDE_CBC_SHA, /* 0x000A */ - "DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DH_DSS_EXPORT_WITH_DES40_CBC_SHA, /* 0x000B */ - "EXP-DH-DSS-DES-CBC-SHA", - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DH_DSS_WITH_DES_CBC_SHA, /* 0x000C */ - "DH-DSS-DES-CBC-SHA", - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DH_DSS_WITH_3DES_EDE_CBC_SHA, /* 0x000D */ - "DH-DSS-DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DH_RSA_EXPORT_WITH_DES40_CBC_SHA, /* 0x000E */ - "EXP-DH-RSA-DES-CBC-SHA", - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DH_RSA_WITH_DES_CBC_SHA, /* 0x000F */ - "DH-RSA-DES-CBC-SHA", - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DH_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x0010 */ - "DH-RSA-DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, /* 0x0011 */ - "EXP-EDH-DSS-DES-CBC-SHA", - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DHE_DSS_WITH_DES_CBC_SHA, /* 0x0012 */ - "EDH-DSS-CBC-SHA", - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DHE_DSS_WITH_3DES_EDE_CBC_SHA, /* 0x0013 */ - "DHE-DSS-DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, /* 0x0014 */ - "EXP-EDH-RSA-DES-CBC-SHA", - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DHE_RSA_WITH_DES_CBC_SHA, /* 0x0015 */ - "EDH-RSA-DES-CBC-SHA", - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DHE_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x0016 */ - "DHE-RSA-DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF_SSLTLS(DH_anon_EXPORT_WITH_RC4_40_MD5, /* 0x0017 */ - "EXP-ADH-RC4-MD5", - CIPHER_WEAK_ANON_AUTH), - CIPHER_DEF_SSLTLS(DH_anon_WITH_RC4_128_MD5, /* 0x0018 */ - "ADH-RC4-MD5", - CIPHER_WEAK_ANON_AUTH), - CIPHER_DEF_SSLTLS(DH_anon_EXPORT_WITH_DES40_CBC_SHA, /* 0x0019 */ - "EXP-ADH-DES-CBC-SHA", - CIPHER_WEAK_ANON_AUTH), - CIPHER_DEF_SSLTLS(DH_anon_WITH_DES_CBC_SHA, /* 0x001A */ - "ADH-DES-CBC-SHA", - CIPHER_WEAK_ANON_AUTH), - CIPHER_DEF_SSLTLS(DH_anon_WITH_3DES_EDE_CBC_SHA, /* 0x001B */ - "ADH-DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF(SSL_FORTEZZA_DMS_WITH_NULL_SHA, /* 0x001C */ - NULL, - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA, /* 0x001D */ - NULL, - CIPHER_STRONG_ENOUGH), - -#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 - /* RFC 4785 - Pre-Shared Key (PSK) Ciphersuites with NULL Encryption */ - CIPHER_DEF(TLS_PSK_WITH_NULL_SHA, /* 0x002C */ - "PSK-NULL-SHA", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_DHE_PSK_WITH_NULL_SHA, /* 0x002D */ - "DHE-PSK-NULL-SHA", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_RSA_PSK_WITH_NULL_SHA, /* 0x002E */ - "RSA-PSK-NULL-SHA", - CIPHER_WEAK_NOT_ENCRYPTED), -#endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */ - - /* TLS addenda using AES, per RFC 3268. Defined since SDK 10.4u */ - CIPHER_DEF(TLS_RSA_WITH_AES_128_CBC_SHA, /* 0x002F */ - "AES128-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_DSS_WITH_AES_128_CBC_SHA, /* 0x0030 */ - "DH-DSS-AES128-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_RSA_WITH_AES_128_CBC_SHA, /* 0x0031 */ - "DH-RSA-AES128-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_DSS_WITH_AES_128_CBC_SHA, /* 0x0032 */ - "DHE-DSS-AES128-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_RSA_WITH_AES_128_CBC_SHA, /* 0x0033 */ - "DHE-RSA-AES128-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_anon_WITH_AES_128_CBC_SHA, /* 0x0034 */ - "ADH-AES128-SHA", - CIPHER_WEAK_ANON_AUTH), - CIPHER_DEF(TLS_RSA_WITH_AES_256_CBC_SHA, /* 0x0035 */ - "AES256-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_DSS_WITH_AES_256_CBC_SHA, /* 0x0036 */ - "DH-DSS-AES256-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_RSA_WITH_AES_256_CBC_SHA, /* 0x0037 */ - "DH-RSA-AES256-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_DSS_WITH_AES_256_CBC_SHA, /* 0x0038 */ - "DHE-DSS-AES256-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_RSA_WITH_AES_256_CBC_SHA, /* 0x0039 */ - "DHE-RSA-AES256-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_anon_WITH_AES_256_CBC_SHA, /* 0x003A */ - "ADH-AES256-SHA", - CIPHER_WEAK_ANON_AUTH), - -#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - /* TLS 1.2 addenda, RFC 5246 */ - /* Server provided RSA certificate for key exchange. */ - CIPHER_DEF(TLS_RSA_WITH_NULL_SHA256, /* 0x003B */ - "NULL-SHA256", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_RSA_WITH_AES_128_CBC_SHA256, /* 0x003C */ - "AES128-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_RSA_WITH_AES_256_CBC_SHA256, /* 0x003D */ - "AES256-SHA256", - CIPHER_STRONG_ENOUGH), - /* Server-authenticated (and optionally client-authenticated) - Diffie-Hellman. */ - CIPHER_DEF(TLS_DH_DSS_WITH_AES_128_CBC_SHA256, /* 0x003E */ - "DH-DSS-AES128-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_RSA_WITH_AES_128_CBC_SHA256, /* 0x003F */ - "DH-RSA-AES128-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, /* 0x0040 */ - "DHE-DSS-AES128-SHA256", - CIPHER_STRONG_ENOUGH), - - /* TLS 1.2 addenda, RFC 5246 */ - CIPHER_DEF(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, /* 0x0067 */ - "DHE-RSA-AES128-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_DSS_WITH_AES_256_CBC_SHA256, /* 0x0068 */ - "DH-DSS-AES256-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_RSA_WITH_AES_256_CBC_SHA256, /* 0x0069 */ - "DH-RSA-AES256-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, /* 0x006A */ - "DHE-DSS-AES256-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, /* 0x006B */ - "DHE-RSA-AES256-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_anon_WITH_AES_128_CBC_SHA256, /* 0x006C */ - "ADH-AES128-SHA256", - CIPHER_WEAK_ANON_AUTH), - CIPHER_DEF(TLS_DH_anon_WITH_AES_256_CBC_SHA256, /* 0x006D */ - "ADH-AES256-SHA256", - CIPHER_WEAK_ANON_AUTH), -#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ - -#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 - /* Addendum from RFC 4279, TLS PSK */ - CIPHER_DEF(TLS_PSK_WITH_RC4_128_SHA, /* 0x008A */ - "PSK-RC4-SHA", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF(TLS_PSK_WITH_3DES_EDE_CBC_SHA, /* 0x008B */ - "PSK-3DES-EDE-CBC-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF(TLS_PSK_WITH_AES_128_CBC_SHA, /* 0x008C */ - "PSK-AES128-CBC-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_PSK_WITH_AES_256_CBC_SHA, /* 0x008D */ - "PSK-AES256-CBC-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_PSK_WITH_RC4_128_SHA, /* 0x008E */ - "DHE-PSK-RC4-SHA", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF(TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, /* 0x008F */ - "DHE-PSK-3DES-EDE-CBC-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF(TLS_DHE_PSK_WITH_AES_128_CBC_SHA, /* 0x0090 */ - "DHE-PSK-AES128-CBC-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_PSK_WITH_AES_256_CBC_SHA, /* 0x0091 */ - "DHE-PSK-AES256-CBC-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_RSA_PSK_WITH_RC4_128_SHA, /* 0x0092 */ - "RSA-PSK-RC4-SHA", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF(TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, /* 0x0093 */ - "RSA-PSK-3DES-EDE-CBC-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF(TLS_RSA_PSK_WITH_AES_128_CBC_SHA, /* 0x0094 */ - "RSA-PSK-AES128-CBC-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_RSA_PSK_WITH_AES_256_CBC_SHA, /* 0x0095 */ - "RSA-PSK-AES256-CBC-SHA", - CIPHER_STRONG_ENOUGH), -#endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */ - -#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - /* Addenda from rfc 5288 AES Galois Counter Mode (GCM) Cipher Suites - for TLS. */ - CIPHER_DEF(TLS_RSA_WITH_AES_128_GCM_SHA256, /* 0x009C */ - "AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_RSA_WITH_AES_256_GCM_SHA384, /* 0x009D */ - "AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, /* 0x009E */ - "DHE-RSA-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, /* 0x009F */ - "DHE-RSA-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_RSA_WITH_AES_128_GCM_SHA256, /* 0x00A0 */ - "DH-RSA-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_RSA_WITH_AES_256_GCM_SHA384, /* 0x00A1 */ - "DH-RSA-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, /* 0x00A2 */ - "DHE-DSS-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, /* 0x00A3 */ - "DHE-DSS-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_DSS_WITH_AES_128_GCM_SHA256, /* 0x00A4 */ - "DH-DSS-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_DSS_WITH_AES_256_GCM_SHA384, /* 0x00A5 */ - "DH-DSS-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DH_anon_WITH_AES_128_GCM_SHA256, /* 0x00A6 */ - "ADH-AES128-GCM-SHA256", - CIPHER_WEAK_ANON_AUTH), - CIPHER_DEF(TLS_DH_anon_WITH_AES_256_GCM_SHA384, /* 0x00A7 */ - "ADH-AES256-GCM-SHA384", - CIPHER_WEAK_ANON_AUTH), -#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ - -#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 - /* RFC 5487 - PSK with SHA-256/384 and AES GCM */ - CIPHER_DEF(TLS_PSK_WITH_AES_128_GCM_SHA256, /* 0x00A8 */ - "PSK-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_PSK_WITH_AES_256_GCM_SHA384, /* 0x00A9 */ - "PSK-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, /* 0x00AA */ - "DHE-PSK-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, /* 0x00AB */ - "DHE-PSK-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, /* 0x00AC */ - "RSA-PSK-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, /* 0x00AD */ - "RSA-PSK-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_PSK_WITH_AES_128_CBC_SHA256, /* 0x00AE */ - "PSK-AES128-CBC-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_PSK_WITH_AES_256_CBC_SHA384, /* 0x00AF */ - "PSK-AES256-CBC-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_PSK_WITH_NULL_SHA256, /* 0x00B0 */ - "PSK-NULL-SHA256", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_PSK_WITH_NULL_SHA384, /* 0x00B1 */ - "PSK-NULL-SHA384", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, /* 0x00B2 */ - "DHE-PSK-AES128-CBC-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, /* 0x00B3 */ - "DHE-PSK-AES256-CBC-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_DHE_PSK_WITH_NULL_SHA256, /* 0x00B4 */ - "DHE-PSK-NULL-SHA256", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_DHE_PSK_WITH_NULL_SHA384, /* 0x00B5 */ - "DHE-PSK-NULL-SHA384", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, /* 0x00B6 */ - "RSA-PSK-AES128-CBC-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, /* 0x00B7 */ - "RSA-PSK-AES256-CBC-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_RSA_PSK_WITH_NULL_SHA256, /* 0x00B8 */ - "RSA-PSK-NULL-SHA256", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_RSA_PSK_WITH_NULL_SHA384, /* 0x00B9 */ - "RSA-PSK-NULL-SHA384", - CIPHER_WEAK_NOT_ENCRYPTED), -#endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */ - - /* RFC 5746 - Secure Renegotiation. This is not a real suite, - it is a response to initiate negotiation again */ - CIPHER_DEF(TLS_EMPTY_RENEGOTIATION_INFO_SCSV, /* 0x00FF */ - NULL, - CIPHER_STRONG_ENOUGH), - -#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 - /* TLS 1.3 standard cipher suites for ChaCha20+Poly1305. - Note: TLS 1.3 ciphersuites do not specify the key exchange - algorithm -- they only specify the symmetric ciphers. - Cipher alias name matches to OpenSSL cipher name, and for - TLS 1.3 ciphers */ - CIPHER_DEF(TLS_AES_128_GCM_SHA256, /* 0x1301 */ - NULL, /* The OpenSSL cipher name matches to the IANA name */ - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_AES_256_GCM_SHA384, /* 0x1302 */ - NULL, /* The OpenSSL cipher name matches to the IANA name */ - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_CHACHA20_POLY1305_SHA256, /* 0x1303 */ - NULL, /* The OpenSSL cipher name matches to the IANA name */ - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_AES_128_CCM_SHA256, /* 0x1304 */ - NULL, /* The OpenSSL cipher name matches to the IANA name */ - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_AES_128_CCM_8_SHA256, /* 0x1305 */ - NULL, /* The OpenSSL cipher name matches to the IANA name */ - CIPHER_STRONG_ENOUGH), -#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */ - -#if CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS - /* ECDSA addenda, RFC 4492 */ - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_NULL_SHA, /* 0xC001 */ - "ECDH-ECDSA-NULL-SHA", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_RC4_128_SHA, /* 0xC002 */ - "ECDH-ECDSA-RC4-SHA", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC003 */ - "ECDH-ECDSA-DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC004 */ - "ECDH-ECDSA-AES128-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC005 */ - "ECDH-ECDSA-AES256-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_NULL_SHA, /* 0xC006 */ - "ECDHE-ECDSA-NULL-SHA", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, /* 0xC007 */ - "ECDHE-ECDSA-RC4-SHA", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC008 */ - "ECDHE-ECDSA-DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC009 */ - "ECDHE-ECDSA-AES128-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC00A */ - "ECDHE-ECDSA-AES256-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_RSA_WITH_NULL_SHA, /* 0xC00B */ - "ECDH-RSA-NULL-SHA", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_ECDH_RSA_WITH_RC4_128_SHA, /* 0xC00C */ - "ECDH-RSA-RC4-SHA", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC00D */ - "ECDH-RSA-DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, /* 0xC00E */ - "ECDH-RSA-AES128-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, /* 0xC00F */ - "ECDH-RSA-AES256-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_NULL_SHA, /* 0xC010 */ - "ECDHE-RSA-NULL-SHA", - CIPHER_WEAK_NOT_ENCRYPTED), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_RC4_128_SHA, /* 0xC011 */ - "ECDHE-RSA-RC4-SHA", - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC012 */ - "ECDHE-RSA-DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, /* 0xC013 */ - "ECDHE-RSA-AES128-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, /* 0xC014 */ - "ECDHE-RSA-AES256-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_anon_WITH_NULL_SHA, /* 0xC015 */ - "AECDH-NULL-SHA", - CIPHER_WEAK_ANON_AUTH), - CIPHER_DEF(TLS_ECDH_anon_WITH_RC4_128_SHA, /* 0xC016 */ - "AECDH-RC4-SHA", - CIPHER_WEAK_ANON_AUTH), - CIPHER_DEF(TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, /* 0xC017 */ - "AECDH-DES-CBC3-SHA", - CIPHER_WEAK_3DES_ENCRYPTION), - CIPHER_DEF(TLS_ECDH_anon_WITH_AES_128_CBC_SHA, /* 0xC018 */ - "AECDH-AES128-SHA", - CIPHER_WEAK_ANON_AUTH), - CIPHER_DEF(TLS_ECDH_anon_WITH_AES_256_CBC_SHA, /* 0xC019 */ - "AECDH-AES256-SHA", - CIPHER_WEAK_ANON_AUTH), -#endif /* CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS */ - -#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - /* Addenda from rfc 5289 Elliptic Curve Cipher Suites with - HMAC SHA-256/384. */ - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC023 */ - "ECDHE-ECDSA-AES128-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC024 */ - "ECDHE-ECDSA-AES256-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC025 */ - "ECDH-ECDSA-AES128-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC026 */ - "ECDH-ECDSA-AES256-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, /* 0xC027 */ - "ECDHE-RSA-AES128-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, /* 0xC028 */ - "ECDHE-RSA-AES256-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, /* 0xC029 */ - "ECDH-RSA-AES128-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, /* 0xC02A */ - "ECDH-RSA-AES256-SHA384", - CIPHER_STRONG_ENOUGH), - /* Addenda from rfc 5289 Elliptic Curve Cipher Suites with - SHA-256/384 and AES Galois Counter Mode (GCM) */ - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02B */ - "ECDHE-ECDSA-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02C */ - "ECDHE-ECDSA-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02D */ - "ECDH-ECDSA-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02E */ - "ECDH-ECDSA-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* 0xC02F */ - "ECDHE-RSA-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, /* 0xC030 */ - "ECDHE-RSA-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, /* 0xC031 */ - "ECDH-RSA-AES128-GCM-SHA256", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, /* 0xC032 */ - "ECDH-RSA-AES256-GCM-SHA384", - CIPHER_STRONG_ENOUGH), -#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ - -#if CURL_BUILD_MAC_10_15 || CURL_BUILD_IOS_13 - /* ECDHE_PSK Cipher Suites for Transport Layer Security (TLS), RFC 5489 */ - CIPHER_DEF(TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, /* 0xC035 */ - "ECDHE-PSK-AES128-CBC-SHA", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, /* 0xC036 */ - "ECDHE-PSK-AES256-CBC-SHA", - CIPHER_STRONG_ENOUGH), -#endif /* CURL_BUILD_MAC_10_15 || CURL_BUILD_IOS_13 */ - -#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 - /* Addenda from rfc 7905 ChaCha20-Poly1305 Cipher Suites for - Transport Layer Security (TLS). */ - CIPHER_DEF(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA8 */ - "ECDHE-RSA-CHACHA20-POLY1305", - CIPHER_STRONG_ENOUGH), - CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA9 */ - "ECDHE-ECDSA-CHACHA20-POLY1305", - CIPHER_STRONG_ENOUGH), -#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */ - -#if CURL_BUILD_MAC_10_15 || CURL_BUILD_IOS_13 - /* ChaCha20-Poly1305 Cipher Suites for Transport Layer Security (TLS), - RFC 7905 */ - CIPHER_DEF(TLS_PSK_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCAB */ - "PSK-CHACHA20-POLY1305", - CIPHER_STRONG_ENOUGH), -#endif /* CURL_BUILD_MAC_10_15 || CURL_BUILD_IOS_13 */ - - /* Tags for SSL 2 cipher kinds which are not specified for SSL 3. - Defined since SDK 10.2.8 */ - CIPHER_DEF(SSL_RSA_WITH_RC2_CBC_MD5, /* 0xFF80 */ - NULL, - CIPHER_WEAK_RC_ENCRYPTION), - CIPHER_DEF(SSL_RSA_WITH_IDEA_CBC_MD5, /* 0xFF81 */ - NULL, - CIPHER_WEAK_IDEA_ENCRYPTION), - CIPHER_DEF(SSL_RSA_WITH_DES_CBC_MD5, /* 0xFF82 */ - NULL, - CIPHER_WEAK_DES_ENCRYPTION), - CIPHER_DEF(SSL_RSA_WITH_3DES_EDE_CBC_MD5, /* 0xFF83 */ - NULL, - CIPHER_WEAK_3DES_ENCRYPTION), -}; - -#define NUM_OF_CIPHERS sizeof(ciphertable)/sizeof(ciphertable[0]) - - -/* pinned public key support tests */ - -/* version 1 supports macOS 10.12+ and iOS 10+ */ -#if ((TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000) || \ - (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)) -#define SECTRANSP_PINNEDPUBKEY_V1 1 -#endif - -/* version 2 supports MacOSX 10.7+ */ -#if (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) -#define SECTRANSP_PINNEDPUBKEY_V2 1 -#endif - -#if defined(SECTRANSP_PINNEDPUBKEY_V1) || defined(SECTRANSP_PINNEDPUBKEY_V2) -/* this backend supports CURLOPT_PINNEDPUBLICKEY */ -#define SECTRANSP_PINNEDPUBKEY 1 -#endif /* SECTRANSP_PINNEDPUBKEY */ - -#ifdef SECTRANSP_PINNEDPUBKEY -/* both new and old APIs return rsa keys missing the spki header (not DER) */ -static const unsigned char rsa4096SpkiHeader[] = { - 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, - 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00}; - -static const unsigned char rsa2048SpkiHeader[] = { - 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, - 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00}; -#ifdef SECTRANSP_PINNEDPUBKEY_V1 -/* the *new* version doesn't return DER encoded ecdsa certs like the old... */ -static const unsigned char ecDsaSecp256r1SpkiHeader[] = { - 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, - 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, - 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, - 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, - 0x42, 0x00}; - -static const unsigned char ecDsaSecp384r1SpkiHeader[] = { - 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, - 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, - 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, - 0x00, 0x22, 0x03, 0x62, 0x00}; -#endif /* SECTRANSP_PINNEDPUBKEY_V1 */ -#endif /* SECTRANSP_PINNEDPUBKEY */ - -/* The following two functions were ripped from Apple sample code, - * with some modifications: */ -static OSStatus SocketRead(SSLConnectionRef connection, - void *data, /* owned by - * caller, data - * RETURNED */ - size_t *dataLength) /* IN/OUT */ -{ - size_t bytesToGo = *dataLength; - size_t initLen = bytesToGo; - UInt8 *currData = (UInt8 *)data; - /*int sock = *(int *)connection;*/ - struct ssl_connect_data *connssl = (struct ssl_connect_data *)connection; - struct ssl_backend_data *backend = connssl->backend; - int sock; - OSStatus rtn = noErr; - size_t bytesRead; - ssize_t rrtn; - int theErr; - - DEBUGASSERT(backend); - sock = backend->ssl_sockfd; - *dataLength = 0; - - for(;;) { - bytesRead = 0; - rrtn = read(sock, currData, bytesToGo); - if(rrtn <= 0) { - /* this is guesswork... */ - theErr = errno; - if(rrtn == 0) { /* EOF = server hung up */ - /* the framework will turn this into errSSLClosedNoNotify */ - rtn = errSSLClosedGraceful; - } - else /* do the switch */ - switch(theErr) { - case ENOENT: - /* connection closed */ - rtn = errSSLClosedGraceful; - break; - case ECONNRESET: - rtn = errSSLClosedAbort; - break; - case EAGAIN: - rtn = errSSLWouldBlock; - backend->ssl_direction = false; - break; - default: - rtn = ioErr; - break; - } - break; - } - else { - bytesRead = rrtn; - } - bytesToGo -= bytesRead; - currData += bytesRead; - - if(bytesToGo == 0) { - /* filled buffer with incoming data, done */ - break; - } - } - *dataLength = initLen - bytesToGo; - - return rtn; -} - -static OSStatus SocketWrite(SSLConnectionRef connection, - const void *data, - size_t *dataLength) /* IN/OUT */ -{ - size_t bytesSent = 0; - /*int sock = *(int *)connection;*/ - struct ssl_connect_data *connssl = (struct ssl_connect_data *)connection; - struct ssl_backend_data *backend = connssl->backend; - int sock; - ssize_t length; - size_t dataLen = *dataLength; - const UInt8 *dataPtr = (UInt8 *)data; - OSStatus ortn; - int theErr; - - DEBUGASSERT(backend); - sock = backend->ssl_sockfd; - *dataLength = 0; - - do { - length = write(sock, - (char *)dataPtr + bytesSent, - dataLen - bytesSent); - } while((length > 0) && - ( (bytesSent += length) < dataLen) ); - - if(length <= 0) { - theErr = errno; - if(theErr == EAGAIN) { - ortn = errSSLWouldBlock; - backend->ssl_direction = true; - } - else { - ortn = ioErr; - } - } - else { - ortn = noErr; - } - *dataLength = bytesSent; - return ortn; -} - -#ifndef CURL_DISABLE_VERBOSE_STRINGS -CF_INLINE const char *TLSCipherNameForNumber(SSLCipherSuite cipher) -{ - /* The first ciphers in the ciphertable are continuous. Here we do small - optimization and instead of loop directly get SSL name by cipher number. - */ - if(cipher <= SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA) { - return ciphertable[cipher].name; - } - /* Iterate through the rest of the ciphers */ - for(size_t i = SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA + 1; - i < NUM_OF_CIPHERS; - ++i) { - if(ciphertable[i].num == cipher) { - return ciphertable[i].name; - } - } - return ciphertable[SSL_NULL_WITH_NULL_NULL].name; -} -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ - -#if CURL_BUILD_MAC -CF_INLINE void GetDarwinVersionNumber(int *major, int *minor) -{ - int mib[2]; - char *os_version; - size_t os_version_len; - char *os_version_major, *os_version_minor; - char *tok_buf; - - /* Get the Darwin kernel version from the kernel using sysctl(): */ - mib[0] = CTL_KERN; - mib[1] = KERN_OSRELEASE; - if(sysctl(mib, 2, NULL, &os_version_len, NULL, 0) == -1) - return; - os_version = malloc(os_version_len*sizeof(char)); - if(!os_version) - return; - if(sysctl(mib, 2, os_version, &os_version_len, NULL, 0) == -1) { - free(os_version); - return; - } - - /* Parse the version: */ - os_version_major = strtok_r(os_version, ".", &tok_buf); - os_version_minor = strtok_r(NULL, ".", &tok_buf); - *major = atoi(os_version_major); - *minor = atoi(os_version_minor); - free(os_version); -} -#endif /* CURL_BUILD_MAC */ - -/* Apple provides a myriad of ways of getting information about a certificate - into a string. Some aren't available under iOS or newer cats. So here's - a unified function for getting a string describing the certificate that - ought to work in all cats starting with Leopard. */ -CF_INLINE CFStringRef getsubject(SecCertificateRef cert) -{ - CFStringRef server_cert_summary = CFSTR("(null)"); - -#if CURL_BUILD_IOS - /* iOS: There's only one way to do this. */ - server_cert_summary = SecCertificateCopySubjectSummary(cert); -#else -#if CURL_BUILD_MAC_10_7 - /* Lion & later: Get the long description if we can. */ - if(SecCertificateCopyLongDescription) - server_cert_summary = - SecCertificateCopyLongDescription(NULL, cert, NULL); - else -#endif /* CURL_BUILD_MAC_10_7 */ -#if CURL_BUILD_MAC_10_6 - /* Snow Leopard: Get the certificate summary. */ - if(SecCertificateCopySubjectSummary) - server_cert_summary = SecCertificateCopySubjectSummary(cert); - else -#endif /* CURL_BUILD_MAC_10_6 */ - /* Leopard is as far back as we go... */ - (void)SecCertificateCopyCommonName(cert, &server_cert_summary); -#endif /* CURL_BUILD_IOS */ - return server_cert_summary; -} - -static CURLcode CopyCertSubject(struct Curl_easy *data, - SecCertificateRef cert, char **certp) -{ - CFStringRef c = getsubject(cert); - CURLcode result = CURLE_OK; - const char *direct; - char *cbuf = NULL; - *certp = NULL; - - if(!c) { - failf(data, "SSL: invalid CA certificate subject"); - return CURLE_PEER_FAILED_VERIFICATION; - } - - /* If the subject is already available as UTF-8 encoded (ie 'direct') then - use that, else convert it. */ - direct = CFStringGetCStringPtr(c, kCFStringEncodingUTF8); - if(direct) { - *certp = strdup(direct); - if(!*certp) { - failf(data, "SSL: out of memory"); - result = CURLE_OUT_OF_MEMORY; - } - } - else { - size_t cbuf_size = ((size_t)CFStringGetLength(c) * 4) + 1; - cbuf = calloc(cbuf_size, 1); - if(cbuf) { - if(!CFStringGetCString(c, cbuf, cbuf_size, - kCFStringEncodingUTF8)) { - failf(data, "SSL: invalid CA certificate subject"); - result = CURLE_PEER_FAILED_VERIFICATION; - } - else - /* pass back the buffer */ - *certp = cbuf; - } - else { - failf(data, "SSL: couldn't allocate %zu bytes of memory", cbuf_size); - result = CURLE_OUT_OF_MEMORY; - } - } - if(result) - free(cbuf); - CFRelease(c); - return result; -} - -#if CURL_SUPPORT_MAC_10_6 -/* The SecKeychainSearch API was deprecated in Lion, and using it will raise - deprecation warnings, so let's not compile this unless it's necessary: */ -static OSStatus CopyIdentityWithLabelOldSchool(char *label, - SecIdentityRef *out_c_a_k) -{ - OSStatus status = errSecItemNotFound; - SecKeychainAttributeList attr_list; - SecKeychainAttribute attr; - SecKeychainSearchRef search = NULL; - SecCertificateRef cert = NULL; - - /* Set up the attribute list: */ - attr_list.count = 1L; - attr_list.attr = &attr; - - /* Set up our lone search criterion: */ - attr.tag = kSecLabelItemAttr; - attr.data = label; - attr.length = (UInt32)strlen(label); - - /* Start searching: */ - status = SecKeychainSearchCreateFromAttributes(NULL, - kSecCertificateItemClass, - &attr_list, - &search); - if(status == noErr) { - status = SecKeychainSearchCopyNext(search, - (SecKeychainItemRef *)&cert); - if(status == noErr && cert) { - /* If we found a certificate, does it have a private key? */ - status = SecIdentityCreateWithCertificate(NULL, cert, out_c_a_k); - CFRelease(cert); - } - } - - if(search) - CFRelease(search); - return status; -} -#endif /* CURL_SUPPORT_MAC_10_6 */ - -static OSStatus CopyIdentityWithLabel(char *label, - SecIdentityRef *out_cert_and_key) -{ - OSStatus status = errSecItemNotFound; - -#if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS - CFArrayRef keys_list; - CFIndex keys_list_count; - CFIndex i; - CFStringRef common_name; - - /* SecItemCopyMatching() was introduced in iOS and Snow Leopard. - kSecClassIdentity was introduced in Lion. If both exist, let's use them - to find the certificate. */ - if(SecItemCopyMatching && kSecClassIdentity) { - CFTypeRef keys[5]; - CFTypeRef values[5]; - CFDictionaryRef query_dict; - CFStringRef label_cf = CFStringCreateWithCString(NULL, label, - kCFStringEncodingUTF8); - - /* Set up our search criteria and expected results: */ - values[0] = kSecClassIdentity; /* we want a certificate and a key */ - keys[0] = kSecClass; - values[1] = kCFBooleanTrue; /* we want a reference */ - keys[1] = kSecReturnRef; - values[2] = kSecMatchLimitAll; /* kSecMatchLimitOne would be better if the - * label matching below worked correctly */ - keys[2] = kSecMatchLimit; - /* identity searches need a SecPolicyRef in order to work */ - values[3] = SecPolicyCreateSSL(false, NULL); - keys[3] = kSecMatchPolicy; - /* match the name of the certificate (doesn't work in macOS 10.12.1) */ - values[4] = label_cf; - keys[4] = kSecAttrLabel; - query_dict = CFDictionaryCreate(NULL, (const void **)keys, - (const void **)values, 5L, - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - CFRelease(values[3]); - - /* Do we have a match? */ - status = SecItemCopyMatching(query_dict, (CFTypeRef *) &keys_list); - - /* Because kSecAttrLabel matching doesn't work with kSecClassIdentity, - * we need to find the correct identity ourselves */ - if(status == noErr) { - keys_list_count = CFArrayGetCount(keys_list); - *out_cert_and_key = NULL; - status = 1; - for(i = 0; i<keys_list_count; i++) { - OSStatus err = noErr; - SecCertificateRef cert = NULL; - SecIdentityRef identity = - (SecIdentityRef) CFArrayGetValueAtIndex(keys_list, i); - err = SecIdentityCopyCertificate(identity, &cert); - if(err == noErr) { - OSStatus copy_status = noErr; -#if CURL_BUILD_IOS - common_name = SecCertificateCopySubjectSummary(cert); -#elif CURL_BUILD_MAC_10_7 - copy_status = SecCertificateCopyCommonName(cert, &common_name); -#endif - if(copy_status == noErr && - CFStringCompare(common_name, label_cf, 0) == kCFCompareEqualTo) { - CFRelease(cert); - CFRelease(common_name); - CFRetain(identity); - *out_cert_and_key = identity; - status = noErr; - break; - } - CFRelease(common_name); - } - CFRelease(cert); - } - } - - if(keys_list) - CFRelease(keys_list); - CFRelease(query_dict); - CFRelease(label_cf); - } - else { -#if CURL_SUPPORT_MAC_10_6 - /* On Leopard and Snow Leopard, fall back to SecKeychainSearch. */ - status = CopyIdentityWithLabelOldSchool(label, out_cert_and_key); -#endif /* CURL_SUPPORT_MAC_10_6 */ - } -#elif CURL_SUPPORT_MAC_10_6 - /* For developers building on older cats, we have no choice but to fall back - to SecKeychainSearch. */ - status = CopyIdentityWithLabelOldSchool(label, out_cert_and_key); -#endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */ - return status; -} - -static OSStatus CopyIdentityFromPKCS12File(const char *cPath, - const struct curl_blob *blob, - const char *cPassword, - SecIdentityRef *out_cert_and_key) -{ - OSStatus status = errSecItemNotFound; - CFURLRef pkcs_url = NULL; - CFStringRef password = cPassword ? CFStringCreateWithCString(NULL, - cPassword, kCFStringEncodingUTF8) : NULL; - CFDataRef pkcs_data = NULL; - - /* We can import P12 files on iOS or OS X 10.7 or later: */ - /* These constants are documented as having first appeared in 10.6 but they - raise linker errors when used on that cat for some reason. */ -#if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS - bool resource_imported; - - if(blob) { - pkcs_data = CFDataCreate(kCFAllocatorDefault, - (const unsigned char *)blob->data, blob->len); - status = (pkcs_data != NULL) ? errSecSuccess : errSecAllocate; - resource_imported = (pkcs_data != NULL); - } - else { - pkcs_url = - CFURLCreateFromFileSystemRepresentation(NULL, - (const UInt8 *)cPath, - strlen(cPath), false); - resource_imported = - CFURLCreateDataAndPropertiesFromResource(NULL, - pkcs_url, &pkcs_data, - NULL, NULL, &status); - } - - if(resource_imported) { - CFArrayRef items = NULL; - - /* On iOS SecPKCS12Import will never add the client certificate to the - * Keychain. - * - * It gives us back a SecIdentityRef that we can use directly. */ -#if CURL_BUILD_IOS - const void *cKeys[] = {kSecImportExportPassphrase}; - const void *cValues[] = {password}; - CFDictionaryRef options = CFDictionaryCreate(NULL, cKeys, cValues, - password ? 1L : 0L, NULL, NULL); - - if(options) { - status = SecPKCS12Import(pkcs_data, options, &items); - CFRelease(options); - } - - - /* On macOS SecPKCS12Import will always add the client certificate to - * the Keychain. - * - * As this doesn't match iOS, and apps may not want to see their client - * certificate saved in the user's keychain, we use SecItemImport - * with a NULL keychain to avoid importing it. - * - * This returns a SecCertificateRef from which we can construct a - * SecIdentityRef. - */ -#elif CURL_BUILD_MAC_10_7 - SecItemImportExportKeyParameters keyParams; - SecExternalFormat inputFormat = kSecFormatPKCS12; - SecExternalItemType inputType = kSecItemTypeCertificate; - - memset(&keyParams, 0x00, sizeof(keyParams)); - keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; - keyParams.passphrase = password; - - status = SecItemImport(pkcs_data, NULL, &inputFormat, &inputType, - 0, &keyParams, NULL, &items); -#endif - - - /* Extract the SecIdentityRef */ - if(status == errSecSuccess && items && CFArrayGetCount(items)) { - CFIndex i, count; - count = CFArrayGetCount(items); - - for(i = 0; i < count; i++) { - CFTypeRef item = (CFTypeRef) CFArrayGetValueAtIndex(items, i); - CFTypeID itemID = CFGetTypeID(item); - - if(itemID == CFDictionaryGetTypeID()) { - CFTypeRef identity = (CFTypeRef) CFDictionaryGetValue( - (CFDictionaryRef) item, - kSecImportItemIdentity); - CFRetain(identity); - *out_cert_and_key = (SecIdentityRef) identity; - break; - } -#if CURL_BUILD_MAC_10_7 - else if(itemID == SecCertificateGetTypeID()) { - status = SecIdentityCreateWithCertificate(NULL, - (SecCertificateRef) item, - out_cert_and_key); - break; - } -#endif - } - } - - if(items) - CFRelease(items); - CFRelease(pkcs_data); - } -#endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */ - if(password) - CFRelease(password); - if(pkcs_url) - CFRelease(pkcs_url); - return status; -} - -/* This code was borrowed from nss.c, with some modifications: - * Determine whether the nickname passed in is a filename that needs to - * be loaded as a PEM or a regular NSS nickname. - * - * returns 1 for a file - * returns 0 for not a file - */ -CF_INLINE bool is_file(const char *filename) -{ - struct_stat st; - - if(!filename) - return false; - - if(stat(filename, &st) == 0) - return S_ISREG(st.st_mode); - return false; -} - -#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS -static CURLcode sectransp_version_from_curl(SSLProtocol *darwinver, - long ssl_version) -{ - switch(ssl_version) { - case CURL_SSLVERSION_TLSv1_0: - *darwinver = kTLSProtocol1; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_1: - *darwinver = kTLSProtocol11; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_2: - *darwinver = kTLSProtocol12; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_3: - /* TLS 1.3 support first appeared in iOS 11 and macOS 10.13 */ -#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 - if(__builtin_available(macOS 10.13, iOS 11.0, *)) { - *darwinver = kTLSProtocol13; - return CURLE_OK; - } -#endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && - HAVE_BUILTIN_AVAILABLE == 1 */ - break; - } - return CURLE_SSL_CONNECT_ERROR; -} -#endif - -static CURLcode -set_ssl_version_min_max(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - long ssl_version = SSL_CONN_CONFIG(version); - long ssl_version_max = SSL_CONN_CONFIG(version_max); - long max_supported_version_by_os; - - DEBUGASSERT(backend); - - /* macOS 10.5-10.7 supported TLS 1.0 only. - macOS 10.8 and later, and iOS 5 and later, added TLS 1.1 and 1.2. - macOS 10.13 and later, and iOS 11 and later, added TLS 1.3. */ -#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 - if(__builtin_available(macOS 10.13, iOS 11.0, *)) { - max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_3; - } - else { - max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_2; - } -#else - max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_2; -#endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && - HAVE_BUILTIN_AVAILABLE == 1 */ - - switch(ssl_version) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - ssl_version = CURL_SSLVERSION_TLSv1_0; - break; - } - - switch(ssl_version_max) { - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_DEFAULT: - ssl_version_max = max_supported_version_by_os; - break; - } - -#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - if(SSLSetProtocolVersionMax) { - SSLProtocol darwin_ver_min = kTLSProtocol1; - SSLProtocol darwin_ver_max = kTLSProtocol1; - CURLcode result = sectransp_version_from_curl(&darwin_ver_min, - ssl_version); - if(result) { - failf(data, "unsupported min version passed via CURLOPT_SSLVERSION"); - return result; - } - result = sectransp_version_from_curl(&darwin_ver_max, - ssl_version_max >> 16); - if(result) { - failf(data, "unsupported max version passed via CURLOPT_SSLVERSION"); - return result; - } - - (void)SSLSetProtocolVersionMin(backend->ssl_ctx, darwin_ver_min); - (void)SSLSetProtocolVersionMax(backend->ssl_ctx, darwin_ver_max); - return result; - } - else { -#if CURL_SUPPORT_MAC_10_8 - long i = ssl_version; - (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, - kSSLProtocolAll, - false); - for(; i <= (ssl_version_max >> 16); i++) { - switch(i) { - case CURL_SSLVERSION_TLSv1_0: - (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, - kTLSProtocol1, - true); - break; - case CURL_SSLVERSION_TLSv1_1: - (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, - kTLSProtocol11, - true); - break; - case CURL_SSLVERSION_TLSv1_2: - (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, - kTLSProtocol12, - true); - break; - case CURL_SSLVERSION_TLSv1_3: - failf(data, "Your version of the OS does not support TLSv1.3"); - return CURLE_SSL_CONNECT_ERROR; - } - } - return CURLE_OK; -#endif /* CURL_SUPPORT_MAC_10_8 */ - } -#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ - failf(data, "Secure Transport: cannot set SSL protocol"); - return CURLE_SSL_CONNECT_ERROR; -} - -static bool is_cipher_suite_strong(SSLCipherSuite suite_num) -{ - for(size_t i = 0; i < NUM_OF_CIPHERS; ++i) { - if(ciphertable[i].num == suite_num) { - return !ciphertable[i].weak; - } - } - /* If the cipher is not in our list, assume it is a new one - and therefore strong. Previous implementation was the same, - if cipher suite is not in the list, it was considered strong enough */ - return true; -} - -static bool is_separator(char c) -{ - /* Return whether character is a cipher list separator. */ - switch(c) { - case ' ': - case '\t': - case ':': - case ',': - case ';': - return true; - } - return false; -} - -static CURLcode sectransp_set_default_ciphers(struct Curl_easy *data, - SSLContextRef ssl_ctx) -{ - size_t all_ciphers_count = 0UL, allowed_ciphers_count = 0UL, i; - SSLCipherSuite *all_ciphers = NULL, *allowed_ciphers = NULL; - OSStatus err = noErr; - -#if CURL_BUILD_MAC - int darwinver_maj = 0, darwinver_min = 0; - - GetDarwinVersionNumber(&darwinver_maj, &darwinver_min); -#endif /* CURL_BUILD_MAC */ - - /* Disable cipher suites that ST supports but are not safe. These ciphers - are unlikely to be used in any case since ST gives other ciphers a much - higher priority, but it's probably better that we not connect at all than - to give the user a false sense of security if the server only supports - insecure ciphers. (Note: We don't care about SSLv2-only ciphers.) */ - err = SSLGetNumberSupportedCiphers(ssl_ctx, &all_ciphers_count); - if(err != noErr) { - failf(data, "SSL: SSLGetNumberSupportedCiphers() failed: OSStatus %d", - err); - return CURLE_SSL_CIPHER; - } - all_ciphers = malloc(all_ciphers_count*sizeof(SSLCipherSuite)); - if(!all_ciphers) { - failf(data, "SSL: Failed to allocate memory for all ciphers"); - return CURLE_OUT_OF_MEMORY; - } - allowed_ciphers = malloc(all_ciphers_count*sizeof(SSLCipherSuite)); - if(!allowed_ciphers) { - Curl_safefree(all_ciphers); - failf(data, "SSL: Failed to allocate memory for allowed ciphers"); - return CURLE_OUT_OF_MEMORY; - } - err = SSLGetSupportedCiphers(ssl_ctx, all_ciphers, - &all_ciphers_count); - if(err != noErr) { - Curl_safefree(all_ciphers); - Curl_safefree(allowed_ciphers); - return CURLE_SSL_CIPHER; - } - for(i = 0UL ; i < all_ciphers_count ; i++) { -#if CURL_BUILD_MAC - /* There's a known bug in early versions of Mountain Lion where ST's ECC - ciphers (cipher suite 0xC001 through 0xC032) simply do not work. - Work around the problem here by disabling those ciphers if we are - running in an affected version of OS X. */ - if(darwinver_maj == 12 && darwinver_min <= 3 && - all_ciphers[i] >= 0xC001 && all_ciphers[i] <= 0xC032) { - continue; - } -#endif /* CURL_BUILD_MAC */ - if(is_cipher_suite_strong(all_ciphers[i])) { - allowed_ciphers[allowed_ciphers_count++] = all_ciphers[i]; - } - } - err = SSLSetEnabledCiphers(ssl_ctx, allowed_ciphers, - allowed_ciphers_count); - Curl_safefree(all_ciphers); - Curl_safefree(allowed_ciphers); - if(err != noErr) { - failf(data, "SSL: SSLSetEnabledCiphers() failed: OSStatus %d", err); - return CURLE_SSL_CIPHER; - } - return CURLE_OK; -} - -static CURLcode sectransp_set_selected_ciphers(struct Curl_easy *data, - SSLContextRef ssl_ctx, - const char *ciphers) -{ - size_t ciphers_count = 0; - const char *cipher_start = ciphers; - OSStatus err = noErr; - SSLCipherSuite selected_ciphers[NUM_OF_CIPHERS]; - - if(!ciphers) - return CURLE_OK; - - while(is_separator(*ciphers)) /* Skip initial separators. */ - ciphers++; - if(!*ciphers) - return CURLE_OK; - - cipher_start = ciphers; - while(*cipher_start && ciphers_count < NUM_OF_CIPHERS) { - bool cipher_found = FALSE; - size_t cipher_len = 0; - const char *cipher_end = NULL; - bool tls_name = FALSE; - - /* Skip separators */ - while(is_separator(*cipher_start)) - cipher_start++; - if(*cipher_start == '\0') { - break; - } - /* Find last position of a cipher in the ciphers string */ - cipher_end = cipher_start; - while (*cipher_end != '\0' && !is_separator(*cipher_end)) { - ++cipher_end; - } - - /* IANA cipher names start with the TLS_ or SSL_ prefix. - If the 4th symbol of the cipher is '_' we look for a cipher in the - table by its (TLS) name. - Otherwise, we try to match cipher by an alias. */ - if(cipher_start[3] == '_') { - tls_name = TRUE; - } - /* Iterate through the cipher table and look for the cipher, starting - the cipher number 0x01 because the 0x00 is not the real cipher */ - cipher_len = cipher_end - cipher_start; - for(size_t i = 1; i < NUM_OF_CIPHERS; ++i) { - const char *table_cipher_name = NULL; - if(tls_name) { - table_cipher_name = ciphertable[i].name; - } - else if(ciphertable[i].alias_name) { - table_cipher_name = ciphertable[i].alias_name; - } - else { - continue; - } - /* Compare a part of the string between separators with a cipher name - in the table and make sure we matched the whole cipher name */ - if(strncmp(cipher_start, table_cipher_name, cipher_len) == 0 - && table_cipher_name[cipher_len] == '\0') { - selected_ciphers[ciphers_count] = ciphertable[i].num; - ++ciphers_count; - cipher_found = TRUE; - break; - } - } - if(!cipher_found) { - /* It would be more human-readable if we print the wrong cipher name - but we don't want to allocate any additional memory and copy the name - into it, then add it into logs. - Also, we do not modify an original cipher list string. We just point - to positions where cipher starts and ends in the cipher list string. - The message is a bit cryptic and longer than necessary but can be - understood by humans. */ - failf(data, "SSL: cipher string \"%s\" contains unsupported cipher name" - " starting position %d and ending position %d", - ciphers, - cipher_start - ciphers, - cipher_end - ciphers); - return CURLE_SSL_CIPHER; - } - if(*cipher_end) { - cipher_start = cipher_end + 1; - } - else { - break; - } - } - /* All cipher suites in the list are found. Report to logs as-is */ - infof(data, "SSL: Setting cipher suites list \"%s\"", ciphers); - - err = SSLSetEnabledCiphers(ssl_ctx, selected_ciphers, ciphers_count); - if(err != noErr) { - failf(data, "SSL: SSLSetEnabledCiphers() failed: OSStatus %d", err); - return CURLE_SSL_CIPHER; - } - return CURLE_OK; -} - -static CURLcode sectransp_connect_step1(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) -{ - curl_socket_t sockfd = conn->sock[sockindex]; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - const struct curl_blob *ssl_cablob = SSL_CONN_CONFIG(ca_info_blob); - const char * const ssl_cafile = - /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ - (ssl_cablob ? NULL : SSL_CONN_CONFIG(CAfile)); - const bool verifypeer = SSL_CONN_CONFIG(verifypeer); - char * const ssl_cert = SSL_SET_OPTION(primary.clientcert); - const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob); - bool isproxy = SSL_IS_PROXY(); - const char * const hostname = SSL_HOST_NAME(); - const long int port = SSL_HOST_PORT(); -#ifdef ENABLE_IPV6 - struct in6_addr addr; -#else - struct in_addr addr; -#endif /* ENABLE_IPV6 */ - char *ciphers; - OSStatus err = noErr; -#if CURL_BUILD_MAC - int darwinver_maj = 0, darwinver_min = 0; - - DEBUGASSERT(backend); - - GetDarwinVersionNumber(&darwinver_maj, &darwinver_min); -#endif /* CURL_BUILD_MAC */ - -#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - if(SSLCreateContext) { /* use the newer API if available */ - if(backend->ssl_ctx) - CFRelease(backend->ssl_ctx); - backend->ssl_ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType); - if(!backend->ssl_ctx) { - failf(data, "SSL: couldn't create a context"); - return CURLE_OUT_OF_MEMORY; - } - } - else { - /* The old ST API does not exist under iOS, so don't compile it: */ -#if CURL_SUPPORT_MAC_10_8 - if(backend->ssl_ctx) - (void)SSLDisposeContext(backend->ssl_ctx); - err = SSLNewContext(false, &(backend->ssl_ctx)); - if(err != noErr) { - failf(data, "SSL: couldn't create a context: OSStatus %d", err); - return CURLE_OUT_OF_MEMORY; - } -#endif /* CURL_SUPPORT_MAC_10_8 */ - } -#else - if(backend->ssl_ctx) - (void)SSLDisposeContext(backend->ssl_ctx); - err = SSLNewContext(false, &(backend->ssl_ctx)); - if(err != noErr) { - failf(data, "SSL: couldn't create a context: OSStatus %d", err); - return CURLE_OUT_OF_MEMORY; - } -#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ - backend->ssl_write_buffered_length = 0UL; /* reset buffered write length */ - - /* check to see if we've been told to use an explicit SSL/TLS version */ -#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - if(SSLSetProtocolVersionMax) { - switch(conn->ssl_config.version) { - case CURL_SSLVERSION_TLSv1: - (void)SSLSetProtocolVersionMin(backend->ssl_ctx, kTLSProtocol1); -#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 - if(__builtin_available(macOS 10.13, iOS 11.0, *)) { - (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol13); - } - else { - (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol12); - } -#else - (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol12); -#endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && - HAVE_BUILTIN_AVAILABLE == 1 */ - break; - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_3: - { - CURLcode result = set_ssl_version_min_max(data, conn, sockindex); - if(result != CURLE_OK) - return result; - break; - } - case CURL_SSLVERSION_SSLv3: - case CURL_SSLVERSION_SSLv2: - failf(data, "SSL versions not supported"); - return CURLE_NOT_BUILT_IN; - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } - } - else { -#if CURL_SUPPORT_MAC_10_8 - (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, - kSSLProtocolAll, - false); - switch(conn->ssl_config.version) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, - kTLSProtocol1, - true); - (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, - kTLSProtocol11, - true); - (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, - kTLSProtocol12, - true); - break; - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_3: - { - CURLcode result = set_ssl_version_min_max(data, conn, sockindex); - if(result != CURLE_OK) - return result; - break; - } - case CURL_SSLVERSION_SSLv3: - case CURL_SSLVERSION_SSLv2: - failf(data, "SSL versions not supported"); - return CURLE_NOT_BUILT_IN; - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } -#endif /* CURL_SUPPORT_MAC_10_8 */ - } -#else - if(conn->ssl_config.version_max != CURL_SSLVERSION_MAX_NONE) { - failf(data, "Your version of the OS does not support to set maximum" - " SSL/TLS version"); - return CURLE_SSL_CONNECT_ERROR; - } - (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, kSSLProtocolAll, false); - switch(conn->ssl_config.version) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - case CURL_SSLVERSION_TLSv1_0: - (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, - kTLSProtocol1, - true); - break; - case CURL_SSLVERSION_TLSv1_1: - failf(data, "Your version of the OS does not support TLSv1.1"); - return CURLE_SSL_CONNECT_ERROR; - case CURL_SSLVERSION_TLSv1_2: - failf(data, "Your version of the OS does not support TLSv1.2"); - return CURLE_SSL_CONNECT_ERROR; - case CURL_SSLVERSION_TLSv1_3: - failf(data, "Your version of the OS does not support TLSv1.3"); - return CURLE_SSL_CONNECT_ERROR; - case CURL_SSLVERSION_SSLv2: - case CURL_SSLVERSION_SSLv3: - failf(data, "SSL versions not supported"); - return CURLE_NOT_BUILT_IN; - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } -#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ - -#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 - if(conn->bits.tls_enable_alpn) { - if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) { - CFMutableArrayRef alpnArr = CFArrayCreateMutable(NULL, 0, - &kCFTypeArrayCallBacks); - -#ifdef USE_HTTP2 - if(data->state.httpwant >= CURL_HTTP_VERSION_2 -#ifndef CURL_DISABLE_PROXY - && (!isproxy || !conn->bits.tunnel_proxy) -#endif - ) { - CFArrayAppendValue(alpnArr, CFSTR(ALPN_H2)); - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); - } -#endif - - CFArrayAppendValue(alpnArr, CFSTR(ALPN_HTTP_1_1)); - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); - - /* expects length prefixed preference ordered list of protocols in wire - * format - */ - err = SSLSetALPNProtocols(backend->ssl_ctx, alpnArr); - if(err != noErr) - infof(data, "WARNING: failed to set ALPN protocols; OSStatus %d", - err); - CFRelease(alpnArr); - } - } -#endif - - if(SSL_SET_OPTION(key)) { - infof(data, "WARNING: SSL: CURLOPT_SSLKEY is ignored by Secure " - "Transport. The private key must be in the Keychain."); - } - - if(ssl_cert || ssl_cert_blob) { - bool is_cert_data = ssl_cert_blob != NULL; - bool is_cert_file = (!is_cert_data) && is_file(ssl_cert); - SecIdentityRef cert_and_key = NULL; - - /* User wants to authenticate with a client cert. Look for it. Assume that - the user wants to use an identity loaded from the Keychain. If not, try - it as a file on disk */ - - if(!is_cert_data) - err = CopyIdentityWithLabel(ssl_cert, &cert_and_key); - else - err = !noErr; - if((err != noErr) && (is_cert_file || is_cert_data)) { - if(!SSL_SET_OPTION(cert_type)) - infof(data, "SSL: Certificate type not set, assuming " - "PKCS#12 format."); - else if(!strcasecompare(SSL_SET_OPTION(cert_type), "P12")) { - failf(data, "SSL: The Security framework only supports " - "loading identities that are in PKCS#12 format."); - return CURLE_SSL_CERTPROBLEM; - } - - err = CopyIdentityFromPKCS12File(ssl_cert, ssl_cert_blob, - SSL_SET_OPTION(key_passwd), - &cert_and_key); - } - - if(err == noErr && cert_and_key) { - SecCertificateRef cert = NULL; - CFTypeRef certs_c[1]; - CFArrayRef certs; - - /* If we found one, print it out: */ - err = SecIdentityCopyCertificate(cert_and_key, &cert); - if(err == noErr) { - char *certp; - CURLcode result = CopyCertSubject(data, cert, &certp); - if(!result) { - infof(data, "Client certificate: %s", certp); - free(certp); - } - - CFRelease(cert); - if(result == CURLE_PEER_FAILED_VERIFICATION) - return CURLE_SSL_CERTPROBLEM; - if(result) - return result; - } - certs_c[0] = cert_and_key; - certs = CFArrayCreate(NULL, (const void **)certs_c, 1L, - &kCFTypeArrayCallBacks); - err = SSLSetCertificate(backend->ssl_ctx, certs); - if(certs) - CFRelease(certs); - if(err != noErr) { - failf(data, "SSL: SSLSetCertificate() failed: OSStatus %d", err); - return CURLE_SSL_CERTPROBLEM; - } - CFRelease(cert_and_key); - } - else { - const char *cert_showfilename_error = - is_cert_data ? "(memory blob)" : ssl_cert; - - switch(err) { - case errSecAuthFailed: case -25264: /* errSecPkcs12VerifyFailure */ - failf(data, "SSL: Incorrect password for the certificate \"%s\" " - "and its private key.", cert_showfilename_error); - break; - case -26275: /* errSecDecode */ case -25257: /* errSecUnknownFormat */ - failf(data, "SSL: Couldn't make sense of the data in the " - "certificate \"%s\" and its private key.", - cert_showfilename_error); - break; - case -25260: /* errSecPassphraseRequired */ - failf(data, "SSL The certificate \"%s\" requires a password.", - cert_showfilename_error); - break; - case errSecItemNotFound: - failf(data, "SSL: Can't find the certificate \"%s\" and its private " - "key in the Keychain.", cert_showfilename_error); - break; - default: - failf(data, "SSL: Can't load the certificate \"%s\" and its private " - "key: OSStatus %d", cert_showfilename_error, err); - break; - } - return CURLE_SSL_CERTPROBLEM; - } - } - - /* SSL always tries to verify the peer, this only says whether it should - * fail to connect if the verification fails, or if it should continue - * anyway. In the latter case the result of the verification is checked with - * SSL_get_verify_result() below. */ -#if CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS - /* Snow Leopard introduced the SSLSetSessionOption() function, but due to - a library bug with the way the kSSLSessionOptionBreakOnServerAuth flag - works, it doesn't work as expected under Snow Leopard, Lion or - Mountain Lion. - So we need to call SSLSetEnableCertVerify() on those older cats in order - to disable certificate validation if the user turned that off. - (SecureTransport will always validate the certificate chain by - default.) - Note: - Darwin 11.x.x is Lion (10.7) - Darwin 12.x.x is Mountain Lion (10.8) - Darwin 13.x.x is Mavericks (10.9) - Darwin 14.x.x is Yosemite (10.10) - Darwin 15.x.x is El Capitan (10.11) - */ -#if CURL_BUILD_MAC - if(SSLSetSessionOption && darwinver_maj >= 13) { -#else - if(SSLSetSessionOption) { -#endif /* CURL_BUILD_MAC */ - bool break_on_auth = !conn->ssl_config.verifypeer || - ssl_cafile || ssl_cablob; - err = SSLSetSessionOption(backend->ssl_ctx, - kSSLSessionOptionBreakOnServerAuth, - break_on_auth); - if(err != noErr) { - failf(data, "SSL: SSLSetSessionOption() failed: OSStatus %d", err); - return CURLE_SSL_CONNECT_ERROR; - } - } - else { -#if CURL_SUPPORT_MAC_10_8 - err = SSLSetEnableCertVerify(backend->ssl_ctx, - conn->ssl_config.verifypeer?true:false); - if(err != noErr) { - failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err); - return CURLE_SSL_CONNECT_ERROR; - } -#endif /* CURL_SUPPORT_MAC_10_8 */ - } -#else - err = SSLSetEnableCertVerify(backend->ssl_ctx, - conn->ssl_config.verifypeer?true:false); - if(err != noErr) { - failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err); - return CURLE_SSL_CONNECT_ERROR; - } -#endif /* CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS */ - - if((ssl_cafile || ssl_cablob) && verifypeer) { - bool is_cert_data = ssl_cablob != NULL; - bool is_cert_file = (!is_cert_data) && is_file(ssl_cafile); - - if(!(is_cert_file || is_cert_data)) { - failf(data, "SSL: can't load CA certificate file %s", - ssl_cafile ? ssl_cafile : "(blob memory)"); - return CURLE_SSL_CACERT_BADFILE; - } - } - - /* Configure hostname check. SNI is used if available. - * Both hostname check and SNI require SSLSetPeerDomainName(). - * Also: the verifyhost setting influences SNI usage */ - if(conn->ssl_config.verifyhost) { - size_t snilen; - char *snihost = Curl_ssl_snihost(data, hostname, &snilen); - if(!snihost) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - err = SSLSetPeerDomainName(backend->ssl_ctx, snihost, snilen); - - if(err != noErr) { - failf(data, "SSL: SSLSetPeerDomainName() failed: OSStatus %d", - err); - return CURLE_SSL_CONNECT_ERROR; - } - - if((Curl_inet_pton(AF_INET, hostname, &addr)) - #ifdef ENABLE_IPV6 - || (Curl_inet_pton(AF_INET6, hostname, &addr)) - #endif - ) { - infof(data, "WARNING: using IP address, SNI is being disabled by " - "the OS."); - } - } - else { - infof(data, "WARNING: disabling hostname validation also disables SNI."); - } - - ciphers = SSL_CONN_CONFIG(cipher_list); - if(ciphers) { - err = sectransp_set_selected_ciphers(data, backend->ssl_ctx, ciphers); - } - else { - err = sectransp_set_default_ciphers(data, backend->ssl_ctx); - } - if(err != noErr) { - failf(data, "SSL: Unable to set ciphers for SSL/TLS handshake. " - "Error code: %d", err); - return CURLE_SSL_CIPHER; - } - -#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 - /* We want to enable 1/n-1 when using a CBC cipher unless the user - specifically doesn't want us doing that: */ - if(SSLSetSessionOption) { - SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionSendOneByteRecord, - !SSL_SET_OPTION(enable_beast)); - SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionFalseStart, - data->set.ssl.falsestart); /* false start support */ - } -#endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */ - - /* Check if there's a cached ID we can/should use here! */ - if(SSL_SET_OPTION(primary.sessionid)) { - char *ssl_sessionid; - size_t ssl_sessionid_len; - - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(data, conn, isproxy, (void **)&ssl_sessionid, - &ssl_sessionid_len, sockindex)) { - /* we got a session id, use it! */ - err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len); - Curl_ssl_sessionid_unlock(data); - if(err != noErr) { - failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err); - return CURLE_SSL_CONNECT_ERROR; - } - /* Informational message */ - infof(data, "SSL re-using session ID"); - } - /* If there isn't one, then let's make one up! This has to be done prior - to starting the handshake. */ - else { - CURLcode result; - ssl_sessionid = - aprintf("%s:%d:%d:%s:%ld", - ssl_cafile ? ssl_cafile : "(blob memory)", - verifypeer, SSL_CONN_CONFIG(verifyhost), hostname, port); - ssl_sessionid_len = strlen(ssl_sessionid); - - err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len); - if(err != noErr) { - Curl_ssl_sessionid_unlock(data); - failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err); - return CURLE_SSL_CONNECT_ERROR; - } - - result = Curl_ssl_addsessionid(data, conn, isproxy, ssl_sessionid, - ssl_sessionid_len, sockindex, NULL); - Curl_ssl_sessionid_unlock(data); - if(result) { - failf(data, "failed to store ssl session"); - return result; - } - } - } - - err = SSLSetIOFuncs(backend->ssl_ctx, SocketRead, SocketWrite); - if(err != noErr) { - failf(data, "SSL: SSLSetIOFuncs() failed: OSStatus %d", err); - return CURLE_SSL_CONNECT_ERROR; - } - - /* pass the raw socket into the SSL layers */ - /* We need to store the FD in a constant memory address, because - * SSLSetConnection() will not copy that address. I've found that - * conn->sock[sockindex] may change on its own. */ - backend->ssl_sockfd = sockfd; - err = SSLSetConnection(backend->ssl_ctx, connssl); - if(err != noErr) { - failf(data, "SSL: SSLSetConnection() failed: %d", err); - return CURLE_SSL_CONNECT_ERROR; - } - - connssl->connecting_state = ssl_connect_2; - return CURLE_OK; -} - -static long pem_to_der(const char *in, unsigned char **out, size_t *outlen) -{ - char *sep_start, *sep_end, *cert_start, *cert_end; - size_t i, j, err; - size_t len; - unsigned char *b64; - - /* Jump through the separators at the beginning of the certificate. */ - sep_start = strstr(in, "-----"); - if(!sep_start) - return 0; - cert_start = strstr(sep_start + 1, "-----"); - if(!cert_start) - return -1; - - cert_start += 5; - - /* Find separator after the end of the certificate. */ - cert_end = strstr(cert_start, "-----"); - if(!cert_end) - return -1; - - sep_end = strstr(cert_end + 1, "-----"); - if(!sep_end) - return -1; - sep_end += 5; - - len = cert_end - cert_start; - b64 = malloc(len + 1); - if(!b64) - return -1; - - /* Create base64 string without linefeeds. */ - for(i = 0, j = 0; i < len; i++) { - if(cert_start[i] != '\r' && cert_start[i] != '\n') - b64[j++] = cert_start[i]; - } - b64[j] = '\0'; - - err = Curl_base64_decode((const char *)b64, out, outlen); - free(b64); - if(err) { - free(*out); - return -1; - } - - return sep_end - in; -} - -static int read_cert(const char *file, unsigned char **out, size_t *outlen) -{ - int fd; - ssize_t n, len = 0, cap = 512; - unsigned char buf[512], *data; - - fd = open(file, 0); - if(fd < 0) - return -1; - - data = malloc(cap); - if(!data) { - close(fd); - return -1; - } - - for(;;) { - n = read(fd, buf, sizeof(buf)); - if(n < 0) { - close(fd); - free(data); - return -1; - } - else if(n == 0) { - close(fd); - break; - } - - if(len + n >= cap) { - cap *= 2; - data = Curl_saferealloc(data, cap); - if(!data) { - close(fd); - return -1; - } - } - - memcpy(data + len, buf, n); - len += n; - } - data[len] = '\0'; - - *out = data; - *outlen = len; - - return 0; -} - -static int append_cert_to_array(struct Curl_easy *data, - const unsigned char *buf, size_t buflen, - CFMutableArrayRef array) -{ - CFDataRef certdata = CFDataCreate(kCFAllocatorDefault, buf, buflen); - char *certp; - CURLcode result; - if(!certdata) { - failf(data, "SSL: failed to allocate array for CA certificate"); - return CURLE_OUT_OF_MEMORY; - } - - SecCertificateRef cacert = - SecCertificateCreateWithData(kCFAllocatorDefault, certdata); - CFRelease(certdata); - if(!cacert) { - failf(data, "SSL: failed to create SecCertificate from CA certificate"); - return CURLE_SSL_CACERT_BADFILE; - } - - /* Check if cacert is valid. */ - result = CopyCertSubject(data, cacert, &certp); - switch(result) { - case CURLE_OK: - break; - case CURLE_PEER_FAILED_VERIFICATION: - return CURLE_SSL_CACERT_BADFILE; - case CURLE_OUT_OF_MEMORY: - default: - return result; - } - free(certp); - - CFArrayAppendValue(array, cacert); - CFRelease(cacert); - - return CURLE_OK; -} - -static CURLcode verify_cert_buf(struct Curl_easy *data, - const unsigned char *certbuf, size_t buflen, - SSLContextRef ctx) -{ - int n = 0, rc; - long res; - unsigned char *der; - size_t derlen, offset = 0; - - /* - * Certbuf now contains the contents of the certificate file, which can be - * - a single DER certificate, - * - a single PEM certificate or - * - a bunch of PEM certificates (certificate bundle). - * - * Go through certbuf, and convert any PEM certificate in it into DER - * format. - */ - CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeArrayCallBacks); - if(!array) { - failf(data, "SSL: out of memory creating CA certificate array"); - return CURLE_OUT_OF_MEMORY; - } - - while(offset < buflen) { - n++; - - /* - * Check if the certificate is in PEM format, and convert it to DER. If - * this fails, we assume the certificate is in DER format. - */ - res = pem_to_der((const char *)certbuf + offset, &der, &derlen); - if(res < 0) { - CFRelease(array); - failf(data, "SSL: invalid CA certificate #%d (offset %zu) in bundle", - n, offset); - return CURLE_SSL_CACERT_BADFILE; - } - offset += res; - - if(res == 0 && offset == 0) { - /* This is not a PEM file, probably a certificate in DER format. */ - rc = append_cert_to_array(data, certbuf, buflen, array); - if(rc != CURLE_OK) { - CFRelease(array); - return rc; - } - break; - } - else if(res == 0) { - /* No more certificates in the bundle. */ - break; - } - - rc = append_cert_to_array(data, der, derlen, array); - free(der); - if(rc != CURLE_OK) { - CFRelease(array); - return rc; - } - } - - SecTrustRef trust; - OSStatus ret = SSLCopyPeerTrust(ctx, &trust); - if(!trust) { - failf(data, "SSL: error getting certificate chain"); - CFRelease(array); - return CURLE_PEER_FAILED_VERIFICATION; - } - else if(ret != noErr) { - CFRelease(array); - failf(data, "SSLCopyPeerTrust() returned error %d", ret); - return CURLE_PEER_FAILED_VERIFICATION; - } - - ret = SecTrustSetAnchorCertificates(trust, array); - if(ret != noErr) { - CFRelease(array); - CFRelease(trust); - failf(data, "SecTrustSetAnchorCertificates() returned error %d", ret); - return CURLE_PEER_FAILED_VERIFICATION; - } - ret = SecTrustSetAnchorCertificatesOnly(trust, true); - if(ret != noErr) { - CFRelease(array); - CFRelease(trust); - failf(data, "SecTrustSetAnchorCertificatesOnly() returned error %d", ret); - return CURLE_PEER_FAILED_VERIFICATION; - } - - SecTrustResultType trust_eval = 0; - ret = SecTrustEvaluate(trust, &trust_eval); - CFRelease(array); - CFRelease(trust); - if(ret != noErr) { - failf(data, "SecTrustEvaluate() returned error %d", ret); - return CURLE_PEER_FAILED_VERIFICATION; - } - - switch(trust_eval) { - case kSecTrustResultUnspecified: - case kSecTrustResultProceed: - return CURLE_OK; - - case kSecTrustResultRecoverableTrustFailure: - case kSecTrustResultDeny: - default: - failf(data, "SSL: certificate verification failed (result: %d)", - trust_eval); - return CURLE_PEER_FAILED_VERIFICATION; - } -} - -static CURLcode verify_cert(struct Curl_easy *data, const char *cafile, - const struct curl_blob *ca_info_blob, - SSLContextRef ctx) -{ - int result; - unsigned char *certbuf; - size_t buflen; - - if(ca_info_blob) { - certbuf = (unsigned char *)malloc(ca_info_blob->len + 1); - if(!certbuf) { - return CURLE_OUT_OF_MEMORY; - } - buflen = ca_info_blob->len; - memcpy(certbuf, ca_info_blob->data, ca_info_blob->len); - certbuf[ca_info_blob->len]='\0'; - } - else if(cafile) { - if(read_cert(cafile, &certbuf, &buflen) < 0) { - failf(data, "SSL: failed to read or invalid CA certificate"); - return CURLE_SSL_CACERT_BADFILE; - } - } - else - return CURLE_SSL_CACERT_BADFILE; - - result = verify_cert_buf(data, certbuf, buflen, ctx); - free(certbuf); - return result; -} - - -#ifdef SECTRANSP_PINNEDPUBKEY -static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, - SSLContextRef ctx, - const char *pinnedpubkey) -{ /* Scratch */ - size_t pubkeylen, realpubkeylen, spkiHeaderLength = 24; - unsigned char *pubkey = NULL, *realpubkey = NULL; - const unsigned char *spkiHeader = NULL; - CFDataRef publicKeyBits = NULL; - - /* Result is returned to caller */ - CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - - /* if a path wasn't specified, don't pin */ - if(!pinnedpubkey) - return CURLE_OK; - - - if(!ctx) - return result; - - do { - SecTrustRef trust; - OSStatus ret = SSLCopyPeerTrust(ctx, &trust); - if(ret != noErr || !trust) - break; - - SecKeyRef keyRef = SecTrustCopyPublicKey(trust); - CFRelease(trust); - if(!keyRef) - break; - -#ifdef SECTRANSP_PINNEDPUBKEY_V1 - - publicKeyBits = SecKeyCopyExternalRepresentation(keyRef, NULL); - CFRelease(keyRef); - if(!publicKeyBits) - break; - -#elif SECTRANSP_PINNEDPUBKEY_V2 - - OSStatus success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL, - &publicKeyBits); - CFRelease(keyRef); - if(success != errSecSuccess || !publicKeyBits) - break; - -#endif /* SECTRANSP_PINNEDPUBKEY_V2 */ - - pubkeylen = CFDataGetLength(publicKeyBits); - pubkey = (unsigned char *)CFDataGetBytePtr(publicKeyBits); - - switch(pubkeylen) { - case 526: - /* 4096 bit RSA pubkeylen == 526 */ - spkiHeader = rsa4096SpkiHeader; - break; - case 270: - /* 2048 bit RSA pubkeylen == 270 */ - spkiHeader = rsa2048SpkiHeader; - break; -#ifdef SECTRANSP_PINNEDPUBKEY_V1 - case 65: - /* ecDSA secp256r1 pubkeylen == 65 */ - spkiHeader = ecDsaSecp256r1SpkiHeader; - spkiHeaderLength = 26; - break; - case 97: - /* ecDSA secp384r1 pubkeylen == 97 */ - spkiHeader = ecDsaSecp384r1SpkiHeader; - spkiHeaderLength = 23; - break; - default: - infof(data, "SSL: unhandled public key length: %d", pubkeylen); -#elif SECTRANSP_PINNEDPUBKEY_V2 - default: - /* ecDSA secp256r1 pubkeylen == 91 header already included? - * ecDSA secp384r1 header already included too - * we assume rest of algorithms do same, so do nothing - */ - result = Curl_pin_peer_pubkey(data, pinnedpubkey, pubkey, - pubkeylen); -#endif /* SECTRANSP_PINNEDPUBKEY_V2 */ - continue; /* break from loop */ - } - - realpubkeylen = pubkeylen + spkiHeaderLength; - realpubkey = malloc(realpubkeylen); - if(!realpubkey) - break; - - memcpy(realpubkey, spkiHeader, spkiHeaderLength); - memcpy(realpubkey + spkiHeaderLength, pubkey, pubkeylen); - - result = Curl_pin_peer_pubkey(data, pinnedpubkey, realpubkey, - realpubkeylen); - - } while(0); - - Curl_safefree(realpubkey); - if(publicKeyBits) - CFRelease(publicKeyBits); - - return result; -} -#endif /* SECTRANSP_PINNEDPUBKEY */ - -static CURLcode -sectransp_connect_step2(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - OSStatus err; - SSLCipherSuite cipher; - SSLProtocol protocol = 0; - const char * const hostname = SSL_HOST_NAME(); - - DEBUGASSERT(ssl_connect_2 == connssl->connecting_state - || ssl_connect_2_reading == connssl->connecting_state - || ssl_connect_2_writing == connssl->connecting_state); - DEBUGASSERT(backend); - - /* Here goes nothing: */ - err = SSLHandshake(backend->ssl_ctx); - - if(err != noErr) { - switch(err) { - case errSSLWouldBlock: /* they're not done with us yet */ - connssl->connecting_state = backend->ssl_direction ? - ssl_connect_2_writing : ssl_connect_2_reading; - return CURLE_OK; - - /* The below is errSSLServerAuthCompleted; it's not defined in - Leopard's headers */ - case -9841: - if((SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) && - SSL_CONN_CONFIG(verifypeer)) { - CURLcode result = verify_cert(data, SSL_CONN_CONFIG(CAfile), - SSL_CONN_CONFIG(ca_info_blob), - backend->ssl_ctx); - if(result) - return result; - } - /* the documentation says we need to call SSLHandshake() again */ - return sectransp_connect_step2(data, conn, sockindex); - - /* Problem with encrypt / decrypt */ - case errSSLPeerDecodeError: - failf(data, "Decode failed"); - break; - case errSSLDecryptionFail: - case errSSLPeerDecryptionFail: - failf(data, "Decryption failed"); - break; - case errSSLPeerDecryptError: - failf(data, "A decryption error occurred"); - break; - case errSSLBadCipherSuite: - failf(data, "A bad SSL cipher suite was encountered"); - break; - case errSSLCrypto: - failf(data, "An underlying cryptographic error was encountered"); - break; -#if CURL_BUILD_MAC_10_11 || CURL_BUILD_IOS_9 - case errSSLWeakPeerEphemeralDHKey: - failf(data, "Indicates a weak ephemeral Diffie-Hellman key"); - break; -#endif - - /* Problem with the message record validation */ - case errSSLBadRecordMac: - case errSSLPeerBadRecordMac: - failf(data, "A record with a bad message authentication code (MAC) " - "was encountered"); - break; - case errSSLRecordOverflow: - case errSSLPeerRecordOverflow: - failf(data, "A record overflow occurred"); - break; - - /* Problem with zlib decompression */ - case errSSLPeerDecompressFail: - failf(data, "Decompression failed"); - break; - - /* Problem with access */ - case errSSLPeerAccessDenied: - failf(data, "Access was denied"); - break; - case errSSLPeerInsufficientSecurity: - failf(data, "There is insufficient security for this operation"); - break; - - /* These are all certificate problems with the server: */ - case errSSLXCertChainInvalid: - failf(data, "SSL certificate problem: Invalid certificate chain"); - return CURLE_PEER_FAILED_VERIFICATION; - case errSSLUnknownRootCert: - failf(data, "SSL certificate problem: Untrusted root certificate"); - return CURLE_PEER_FAILED_VERIFICATION; - case errSSLNoRootCert: - failf(data, "SSL certificate problem: No root certificate"); - return CURLE_PEER_FAILED_VERIFICATION; - case errSSLCertNotYetValid: - failf(data, "SSL certificate problem: The certificate chain had a " - "certificate that is not yet valid"); - return CURLE_PEER_FAILED_VERIFICATION; - case errSSLCertExpired: - case errSSLPeerCertExpired: - failf(data, "SSL certificate problem: Certificate chain had an " - "expired certificate"); - return CURLE_PEER_FAILED_VERIFICATION; - case errSSLBadCert: - case errSSLPeerBadCert: - failf(data, "SSL certificate problem: Couldn't understand the server " - "certificate format"); - return CURLE_PEER_FAILED_VERIFICATION; - case errSSLPeerUnsupportedCert: - failf(data, "SSL certificate problem: An unsupported certificate " - "format was encountered"); - return CURLE_PEER_FAILED_VERIFICATION; - case errSSLPeerCertRevoked: - failf(data, "SSL certificate problem: The certificate was revoked"); - return CURLE_PEER_FAILED_VERIFICATION; - case errSSLPeerCertUnknown: - failf(data, "SSL certificate problem: The certificate is unknown"); - return CURLE_PEER_FAILED_VERIFICATION; - - /* These are all certificate problems with the client: */ - case errSecAuthFailed: - failf(data, "SSL authentication failed"); - break; - case errSSLPeerHandshakeFail: - failf(data, "SSL peer handshake failed, the server most likely " - "requires a client certificate to connect"); - break; - case errSSLPeerUnknownCA: - failf(data, "SSL server rejected the client certificate due to " - "the certificate being signed by an unknown certificate " - "authority"); - break; - - /* This error is raised if the server's cert didn't match the server's - host name: */ - case errSSLHostNameMismatch: - failf(data, "SSL certificate peer verification failed, the " - "certificate did not match \"%s\"\n", conn->host.dispname); - return CURLE_PEER_FAILED_VERIFICATION; - - /* Problem with SSL / TLS negotiation */ - case errSSLNegotiation: - failf(data, "Could not negotiate an SSL cipher suite with the server"); - break; - case errSSLBadConfiguration: - failf(data, "A configuration error occurred"); - break; - case errSSLProtocol: - failf(data, "SSL protocol error"); - break; - case errSSLPeerProtocolVersion: - failf(data, "A bad protocol version was encountered"); - break; - case errSSLPeerNoRenegotiation: - failf(data, "No renegotiation is allowed"); - break; - - /* Generic handshake errors: */ - case errSSLConnectionRefused: - failf(data, "Server dropped the connection during the SSL handshake"); - break; - case errSSLClosedAbort: - failf(data, "Server aborted the SSL handshake"); - break; - case errSSLClosedGraceful: - failf(data, "The connection closed gracefully"); - break; - case errSSLClosedNoNotify: - failf(data, "The server closed the session with no notification"); - break; - /* Sometimes paramErr happens with buggy ciphers: */ - case paramErr: - case errSSLInternal: - case errSSLPeerInternalError: - failf(data, "Internal SSL engine error encountered during the " - "SSL handshake"); - break; - case errSSLFatalAlert: - failf(data, "Fatal SSL engine error encountered during the SSL " - "handshake"); - break; - /* Unclassified error */ - case errSSLBufferOverflow: - failf(data, "An insufficient buffer was provided"); - break; - case errSSLIllegalParam: - failf(data, "An illegal parameter was encountered"); - break; - case errSSLModuleAttach: - failf(data, "Module attach failure"); - break; - case errSSLSessionNotFound: - failf(data, "An attempt to restore an unknown session failed"); - break; - case errSSLPeerExportRestriction: - failf(data, "An export restriction occurred"); - break; - case errSSLPeerUserCancelled: - failf(data, "The user canceled the operation"); - break; - case errSSLPeerUnexpectedMsg: - failf(data, "Peer rejected unexpected message"); - break; -#if CURL_BUILD_MAC_10_11 || CURL_BUILD_IOS_9 - /* Treaing non-fatal error as fatal like before */ - case errSSLClientHelloReceived: - failf(data, "A non-fatal result for providing a server name " - "indication"); - break; -#endif - - /* Error codes defined in the enum but should never be returned. - We list them here just in case. */ -#if CURL_BUILD_MAC_10_6 - /* Only returned when kSSLSessionOptionBreakOnCertRequested is set */ - case errSSLClientCertRequested: - failf(data, "Server requested a client certificate during the " - "handshake"); - return CURLE_SSL_CLIENTCERT; -#endif -#if CURL_BUILD_MAC_10_9 - /* Alias for errSSLLast, end of error range */ - case errSSLUnexpectedRecord: - failf(data, "Unexpected (skipped) record in DTLS"); - break; -#endif - default: - /* May also return codes listed in Security Framework Result Codes */ - failf(data, "Unknown SSL protocol error in connection to %s:%d", - hostname, err); - break; - } - return CURLE_SSL_CONNECT_ERROR; - } - else { - /* we have been connected fine, we're not waiting for anything else. */ - connssl->connecting_state = ssl_connect_3; - -#ifdef SECTRANSP_PINNEDPUBKEY - if(data->set.str[STRING_SSL_PINNEDPUBLICKEY]) { - CURLcode result = - pkp_pin_peer_pubkey(data, backend->ssl_ctx, - data->set.str[STRING_SSL_PINNEDPUBLICKEY]); - if(result) { - failf(data, "SSL: public key does not match pinned public key"); - return result; - } - } -#endif /* SECTRANSP_PINNEDPUBKEY */ - - /* Informational message */ - (void)SSLGetNegotiatedCipher(backend->ssl_ctx, &cipher); - (void)SSLGetNegotiatedProtocolVersion(backend->ssl_ctx, &protocol); - switch(protocol) { - case kSSLProtocol2: - infof(data, "SSL 2.0 connection using %s", - TLSCipherNameForNumber(cipher)); - break; - case kSSLProtocol3: - infof(data, "SSL 3.0 connection using %s", - TLSCipherNameForNumber(cipher)); - break; - case kTLSProtocol1: - infof(data, "TLS 1.0 connection using %s", - TLSCipherNameForNumber(cipher)); - break; -#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - case kTLSProtocol11: - infof(data, "TLS 1.1 connection using %s", - TLSCipherNameForNumber(cipher)); - break; - case kTLSProtocol12: - infof(data, "TLS 1.2 connection using %s", - TLSCipherNameForNumber(cipher)); - break; -#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ -#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 - case kTLSProtocol13: - infof(data, "TLS 1.3 connection using %s", - TLSCipherNameForNumber(cipher)); - break; -#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */ - default: - infof(data, "Unknown protocol connection"); - break; - } - -#if(CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 - if(conn->bits.tls_enable_alpn) { - if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) { - CFArrayRef alpnArr = NULL; - CFStringRef chosenProtocol = NULL; - err = SSLCopyALPNProtocols(backend->ssl_ctx, &alpnArr); - - if(err == noErr && alpnArr && CFArrayGetCount(alpnArr) >= 1) - chosenProtocol = CFArrayGetValueAtIndex(alpnArr, 0); - -#ifdef USE_HTTP2 - if(chosenProtocol && - !CFStringCompare(chosenProtocol, CFSTR(ALPN_H2), 0)) { - conn->alpn = CURL_HTTP_VERSION_2; - } - else -#endif - if(chosenProtocol && - !CFStringCompare(chosenProtocol, CFSTR(ALPN_HTTP_1_1), 0)) { - conn->alpn = CURL_HTTP_VERSION_1_1; - } - else - infof(data, VTLS_INFOF_NO_ALPN); - - Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); - - /* chosenProtocol is a reference to the string within alpnArr - and doesn't need to be freed separately */ - if(alpnArr) - CFRelease(alpnArr); - } - } -#endif - - return CURLE_OK; - } -} - -static CURLcode -add_cert_to_certinfo(struct Curl_easy *data, - SecCertificateRef server_cert, - int idx) -{ - CURLcode result = CURLE_OK; - const char *beg; - const char *end; - CFDataRef cert_data = SecCertificateCopyData(server_cert); - - if(!cert_data) - return CURLE_PEER_FAILED_VERIFICATION; - - beg = (const char *)CFDataGetBytePtr(cert_data); - end = beg + CFDataGetLength(cert_data); - result = Curl_extract_certinfo(data, idx, beg, end); - CFRelease(cert_data); - return result; -} - -static CURLcode -collect_server_cert_single(struct Curl_easy *data, - SecCertificateRef server_cert, - CFIndex idx) -{ - CURLcode result = CURLE_OK; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - if(data->set.verbose) { - char *certp; - result = CopyCertSubject(data, server_cert, &certp); - if(!result) { - infof(data, "Server certificate: %s", certp); - free(certp); - } - } -#endif - if(data->set.ssl.certinfo) - result = add_cert_to_certinfo(data, server_cert, (int)idx); - return result; -} - -/* This should be called during step3 of the connection at the earliest */ -static CURLcode -collect_server_cert(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) -{ -#ifndef CURL_DISABLE_VERBOSE_STRINGS - const bool show_verbose_server_cert = data->set.verbose; -#else - const bool show_verbose_server_cert = false; -#endif - CURLcode result = data->set.ssl.certinfo ? - CURLE_PEER_FAILED_VERIFICATION : CURLE_OK; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - CFArrayRef server_certs = NULL; - SecCertificateRef server_cert; - OSStatus err; - CFIndex i, count; - SecTrustRef trust = NULL; - - DEBUGASSERT(backend); - - if(!show_verbose_server_cert && !data->set.ssl.certinfo) - return CURLE_OK; - - if(!backend->ssl_ctx) - return result; - -#if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS -#if CURL_BUILD_IOS -#pragma unused(server_certs) - err = SSLCopyPeerTrust(backend->ssl_ctx, &trust); - /* For some reason, SSLCopyPeerTrust() can return noErr and yet return - a null trust, so be on guard for that: */ - if(err == noErr && trust) { - count = SecTrustGetCertificateCount(trust); - if(data->set.ssl.certinfo) - result = Curl_ssl_init_certinfo(data, (int)count); - for(i = 0L ; !result && (i < count) ; i++) { - server_cert = SecTrustGetCertificateAtIndex(trust, i); - result = collect_server_cert_single(data, server_cert, i); - } - CFRelease(trust); - } -#else - /* SSLCopyPeerCertificates() is deprecated as of Mountain Lion. - The function SecTrustGetCertificateAtIndex() is officially present - in Lion, but it is unfortunately also present in Snow Leopard as - private API and doesn't work as expected. So we have to look for - a different symbol to make sure this code is only executed under - Lion or later. */ - if(SecTrustCopyPublicKey) { -#pragma unused(server_certs) - err = SSLCopyPeerTrust(backend->ssl_ctx, &trust); - /* For some reason, SSLCopyPeerTrust() can return noErr and yet return - a null trust, so be on guard for that: */ - if(err == noErr && trust) { - count = SecTrustGetCertificateCount(trust); - if(data->set.ssl.certinfo) - result = Curl_ssl_init_certinfo(data, (int)count); - for(i = 0L ; !result && (i < count) ; i++) { - server_cert = SecTrustGetCertificateAtIndex(trust, i); - result = collect_server_cert_single(data, server_cert, i); - } - CFRelease(trust); - } - } - else { -#if CURL_SUPPORT_MAC_10_8 - err = SSLCopyPeerCertificates(backend->ssl_ctx, &server_certs); - /* Just in case SSLCopyPeerCertificates() returns null too... */ - if(err == noErr && server_certs) { - count = CFArrayGetCount(server_certs); - if(data->set.ssl.certinfo) - result = Curl_ssl_init_certinfo(data, (int)count); - for(i = 0L ; !result && (i < count) ; i++) { - server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, - i); - result = collect_server_cert_single(data, server_cert, i); - } - CFRelease(server_certs); - } -#endif /* CURL_SUPPORT_MAC_10_8 */ - } -#endif /* CURL_BUILD_IOS */ -#else -#pragma unused(trust) - err = SSLCopyPeerCertificates(backend->ssl_ctx, &server_certs); - if(err == noErr) { - count = CFArrayGetCount(server_certs); - if(data->set.ssl.certinfo) - result = Curl_ssl_init_certinfo(data, (int)count); - for(i = 0L ; !result && (i < count) ; i++) { - server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, i); - result = collect_server_cert_single(data, server_cert, i); - } - CFRelease(server_certs); - } -#endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */ - return result; -} - -static CURLcode -sectransp_connect_step3(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - - /* There is no step 3! - * Well, okay, let's collect server certificates, and if verbose mode is on, - * let's print the details of the server certificates. */ - const CURLcode result = collect_server_cert(data, conn, sockindex); - if(result) - return result; - - connssl->connecting_state = ssl_connect_done; - return CURLE_OK; -} - -static Curl_recv sectransp_recv; -static Curl_send sectransp_send; - -static CURLcode -sectransp_connect_common(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool nonblocking, - bool *done) -{ - CURLcode result; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - curl_socket_t sockfd = conn->sock[sockindex]; - int what; - - /* check if the connection has already been established */ - if(ssl_connection_complete == connssl->state) { - *done = TRUE; - return CURLE_OK; - } - - if(ssl_connect_1 == connssl->connecting_state) { - /* Find out how much more time we're allowed */ - const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - result = sectransp_connect_step1(data, conn, sockindex); - if(result) - return result; - } - - while(ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state) { - - /* check allowed time left */ - const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - /* if ssl is expecting something, check if it's available. */ - if(connssl->connecting_state == ssl_connect_2_reading || - connssl->connecting_state == ssl_connect_2_writing) { - - curl_socket_t writefd = ssl_connect_2_writing == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - curl_socket_t readfd = ssl_connect_2_reading == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - - what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, - nonblocking ? 0 : timeout_ms); - if(what < 0) { - /* fatal error */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - return CURLE_SSL_CONNECT_ERROR; - } - else if(0 == what) { - if(nonblocking) { - *done = FALSE; - return CURLE_OK; - } - else { - /* timeout */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - } - /* socket is readable or writable */ - } - - /* Run transaction, and return to the caller if it failed or if this - * connection is done nonblocking and this loop would execute again. This - * permits the owner of a multi handle to abort a connection attempt - * before step2 has completed while ensuring that a client using select() - * or epoll() will always have a valid fdset to wait on. - */ - result = sectransp_connect_step2(data, conn, sockindex); - if(result || (nonblocking && - (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) - return result; - - } /* repeat step2 until all transactions are done. */ - - - if(ssl_connect_3 == connssl->connecting_state) { - result = sectransp_connect_step3(data, conn, sockindex); - if(result) - return result; - } - - if(ssl_connect_done == connssl->connecting_state) { - connssl->state = ssl_connection_complete; - conn->recv[sockindex] = sectransp_recv; - conn->send[sockindex] = sectransp_send; - *done = TRUE; - } - else - *done = FALSE; - - /* Reset our connect state machine */ - connssl->connecting_state = ssl_connect_1; - - return CURLE_OK; -} - -static CURLcode sectransp_connect_nonblocking(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, bool *done) -{ - return sectransp_connect_common(data, conn, sockindex, TRUE, done); -} - -static CURLcode sectransp_connect(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - CURLcode result; - bool done = FALSE; - - result = sectransp_connect_common(data, conn, sockindex, FALSE, &done); - - if(result) - return result; - - DEBUGASSERT(done); - - return CURLE_OK; -} - -static void sectransp_close(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - - (void) data; - - DEBUGASSERT(backend); - - if(backend->ssl_ctx) { - (void)SSLClose(backend->ssl_ctx); -#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - if(SSLCreateContext) - CFRelease(backend->ssl_ctx); -#if CURL_SUPPORT_MAC_10_8 - else - (void)SSLDisposeContext(backend->ssl_ctx); -#endif /* CURL_SUPPORT_MAC_10_8 */ -#else - (void)SSLDisposeContext(backend->ssl_ctx); -#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ - backend->ssl_ctx = NULL; - } - backend->ssl_sockfd = 0; -} - -static int sectransp_shutdown(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - ssize_t nread; - int what; - int rc; - char buf[120]; - int loop = 10; /* avoid getting stuck */ - - DEBUGASSERT(backend); - - if(!backend->ssl_ctx) - return 0; - -#ifndef CURL_DISABLE_FTP - if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE) - return 0; -#endif - - sectransp_close(data, conn, sockindex); - - rc = 0; - - what = SOCKET_READABLE(conn->sock[sockindex], SSL_SHUTDOWN_TIMEOUT); - - while(loop--) { - if(what < 0) { - /* anything that gets here is fatally bad */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - rc = -1; - break; - } - - if(!what) { /* timeout */ - failf(data, "SSL shutdown timeout"); - break; - } - - /* Something to read, let's do it and hope that it is the close - notify alert from the server. No way to SSL_Read now, so use read(). */ - - nread = read(conn->sock[sockindex], buf, sizeof(buf)); - - if(nread < 0) { - char buffer[STRERROR_LEN]; - failf(data, "read: %s", - Curl_strerror(errno, buffer, sizeof(buffer))); - rc = -1; - } - - if(nread <= 0) - break; - - what = SOCKET_READABLE(conn->sock[sockindex], 0); - } - - return rc; -} - -static void sectransp_session_free(void *ptr) -{ - /* ST, as of iOS 5 and Mountain Lion, has no public method of deleting a - cached session ID inside the Security framework. There is a private - function that does this, but I don't want to have to explain to you why I - got your application rejected from the App Store due to the use of a - private API, so the best we can do is free up our own char array that we - created way back in sectransp_connect_step1... */ - Curl_safefree(ptr); -} - -static size_t sectransp_version(char *buffer, size_t size) -{ - return msnprintf(buffer, size, "SecureTransport"); -} - -/* - * This function uses SSLGetSessionState to determine connection status. - * - * Return codes: - * 1 means the connection is still in place - * 0 means the connection has been closed - * -1 means the connection status is unknown - */ -static int sectransp_check_cxn(struct connectdata *conn) -{ - struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET]; - struct ssl_backend_data *backend = connssl->backend; - OSStatus err; - SSLSessionState state; - - DEBUGASSERT(backend); - - if(backend->ssl_ctx) { - err = SSLGetSessionState(backend->ssl_ctx, &state); - if(err == noErr) - return state == kSSLConnected || state == kSSLHandshake; - return -1; - } - return 0; -} - -static bool sectransp_data_pending(const struct connectdata *conn, - int connindex) -{ - const struct ssl_connect_data *connssl = &conn->ssl[connindex]; - struct ssl_backend_data *backend = connssl->backend; - OSStatus err; - size_t buffer; - - DEBUGASSERT(backend); - - if(backend->ssl_ctx) { /* SSL is in use */ - err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer); - if(err == noErr) - return buffer > 0UL; - return false; - } - else - return false; -} - -static CURLcode sectransp_random(struct Curl_easy *data UNUSED_PARAM, - unsigned char *entropy, size_t length) -{ - /* arc4random_buf() isn't available on cats older than Lion, so let's - do this manually for the benefit of the older cats. */ - size_t i; - u_int32_t random_number = 0; - - (void)data; - - for(i = 0 ; i < length ; i++) { - if(i % sizeof(u_int32_t) == 0) - random_number = arc4random(); - entropy[i] = random_number & 0xFF; - random_number >>= 8; - } - i = random_number = 0; - return CURLE_OK; -} - -static CURLcode sectransp_sha256sum(const unsigned char *tmp, /* input */ - size_t tmplen, - unsigned char *sha256sum, /* output */ - size_t sha256len) -{ - assert(sha256len >= CURL_SHA256_DIGEST_LENGTH); - (void)CC_SHA256(tmp, (CC_LONG)tmplen, sha256sum); - return CURLE_OK; -} - -static bool sectransp_false_start(void) -{ -#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 - if(SSLSetSessionOption) - return TRUE; -#endif - return FALSE; -} - -static ssize_t sectransp_send(struct Curl_easy *data, - int sockindex, - const void *mem, - size_t len, - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - size_t processed = 0UL; - OSStatus err; - - DEBUGASSERT(backend); - - /* The SSLWrite() function works a little differently than expected. The - fourth argument (processed) is currently documented in Apple's - documentation as: "On return, the length, in bytes, of the data actually - written." - - Now, one could interpret that as "written to the socket," but actually, - it returns the amount of data that was written to a buffer internal to - the SSLContextRef instead. So it's possible for SSLWrite() to return - errSSLWouldBlock and a number of bytes "written" because those bytes were - encrypted and written to a buffer, not to the socket. - - So if this happens, then we need to keep calling SSLWrite() over and - over again with no new data until it quits returning errSSLWouldBlock. */ - - /* Do we have buffered data to write from the last time we were called? */ - if(backend->ssl_write_buffered_length) { - /* Write the buffered data: */ - err = SSLWrite(backend->ssl_ctx, NULL, 0UL, &processed); - switch(err) { - case noErr: - /* processed is always going to be 0 because we didn't write to - the buffer, so return how much was written to the socket */ - processed = backend->ssl_write_buffered_length; - backend->ssl_write_buffered_length = 0UL; - break; - case errSSLWouldBlock: /* argh, try again */ - *curlcode = CURLE_AGAIN; - return -1L; - default: - failf(data, "SSLWrite() returned error %d", err); - *curlcode = CURLE_SEND_ERROR; - return -1L; - } - } - else { - /* We've got new data to write: */ - err = SSLWrite(backend->ssl_ctx, mem, len, &processed); - if(err != noErr) { - switch(err) { - case errSSLWouldBlock: - /* Data was buffered but not sent, we have to tell the caller - to try sending again, and remember how much was buffered */ - backend->ssl_write_buffered_length = len; - *curlcode = CURLE_AGAIN; - return -1L; - default: - failf(data, "SSLWrite() returned error %d", err); - *curlcode = CURLE_SEND_ERROR; - return -1L; - } - } - } - return (ssize_t)processed; -} - -static ssize_t sectransp_recv(struct Curl_easy *data, - int num, - char *buf, - size_t buffersize, - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[num]; - struct ssl_backend_data *backend = connssl->backend; - size_t processed = 0UL; - OSStatus err; - - DEBUGASSERT(backend); - - again: - err = SSLRead(backend->ssl_ctx, buf, buffersize, &processed); - - if(err != noErr) { - switch(err) { - case errSSLWouldBlock: /* return how much we read (if anything) */ - if(processed) - return (ssize_t)processed; - *curlcode = CURLE_AGAIN; - return -1L; - break; - - /* errSSLClosedGraceful - server gracefully shut down the SSL session - errSSLClosedNoNotify - server hung up on us instead of sending a - closure alert notice, read() is returning 0 - Either way, inform the caller that the server disconnected. */ - case errSSLClosedGraceful: - case errSSLClosedNoNotify: - *curlcode = CURLE_OK; - return -1L; - break; - - /* The below is errSSLPeerAuthCompleted; it's not defined in - Leopard's headers */ - case -9841: - if((SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) && - SSL_CONN_CONFIG(verifypeer)) { - CURLcode result = verify_cert(data, SSL_CONN_CONFIG(CAfile), - SSL_CONN_CONFIG(ca_info_blob), - backend->ssl_ctx); - if(result) - return result; - } - goto again; - default: - failf(data, "SSLRead() return error %d", err); - *curlcode = CURLE_RECV_ERROR; - return -1L; - break; - } - } - return (ssize_t)processed; -} - -static void *sectransp_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - struct ssl_backend_data *backend = connssl->backend; - (void)info; - DEBUGASSERT(backend); - return backend->ssl_ctx; -} - -const struct Curl_ssl Curl_ssl_sectransp = { - { CURLSSLBACKEND_SECURETRANSPORT, "secure-transport" }, /* info */ - - SSLSUPP_CAINFO_BLOB | - SSLSUPP_CERTINFO | -#ifdef SECTRANSP_PINNEDPUBKEY - SSLSUPP_PINNEDPUBKEY, -#else - 0, -#endif /* SECTRANSP_PINNEDPUBKEY */ - - sizeof(struct ssl_backend_data), - - Curl_none_init, /* init */ - Curl_none_cleanup, /* cleanup */ - sectransp_version, /* version */ - sectransp_check_cxn, /* check_cxn */ - sectransp_shutdown, /* shutdown */ - sectransp_data_pending, /* data_pending */ - sectransp_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ - sectransp_connect, /* connect */ - sectransp_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_getsock, /* getsock */ - sectransp_get_internals, /* get_internals */ - sectransp_close, /* close_one */ - Curl_none_close_all, /* close_all */ - sectransp_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - sectransp_false_start, /* false_start */ - sectransp_sha256sum, /* sha256sum */ - NULL, /* associate_connection */ - NULL /* disassociate_connection */ -}; - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#endif /* USE_SECTRANSP */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2017, Nick Zitzmann, <nickzman@gmail.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
+ *
+ ***************************************************************************/
+
+/*
+ * Source file for all iOS and macOS SecureTransport-specific code for the
+ * TLS/SSL layer. No code but vtls.c should ever call or use these functions.
+ */
+
+#include "curl_setup.h"
+
+#include "urldata.h" /* for the Curl_easy definition */
+#include "curl_base64.h"
+#include "strtok.h"
+#include "multiif.h"
+#include "strcase.h"
+#include "x509asn1.h"
+#include "strerror.h"
+
+#ifdef USE_SECTRANSP
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wtautological-pointer-compare"
+#endif /* __clang__ */
+
+#include <limits.h>
+
+#include <Security/Security.h>
+/* For some reason, when building for iOS, the omnibus header above does
+ * not include SecureTransport.h as of iOS SDK 5.1. */
+#include <Security/SecureTransport.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <CommonCrypto/CommonDigest.h>
+
+/* The Security framework has changed greatly between iOS and different macOS
+ versions, and we will try to support as many of them as we can (back to
+ Leopard and iOS 5) by using macros and weak-linking.
+
+ In general, you want to build this using the most recent OS SDK, since some
+ features require curl to be built against the latest SDK. TLS 1.1 and 1.2
+ support, for instance, require the macOS 10.8 SDK or later. TLS 1.3
+ requires the macOS 10.13 or iOS 11 SDK or later. */
+#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050
+#error "The Secure Transport back-end requires Leopard or later."
+#endif /* MAC_OS_X_VERSION_MAX_ALLOWED < 1050 */
+
+#define CURL_BUILD_IOS 0
+#define CURL_BUILD_IOS_7 0
+#define CURL_BUILD_IOS_9 0
+#define CURL_BUILD_IOS_11 0
+#define CURL_BUILD_IOS_13 0
+#define CURL_BUILD_MAC 1
+/* This is the maximum API level we are allowed to use when building: */
+#define CURL_BUILD_MAC_10_5 MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
+#define CURL_BUILD_MAC_10_6 MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
+#define CURL_BUILD_MAC_10_7 MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
+#define CURL_BUILD_MAC_10_8 MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
+#define CURL_BUILD_MAC_10_9 MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
+#define CURL_BUILD_MAC_10_11 MAC_OS_X_VERSION_MAX_ALLOWED >= 101100
+#define CURL_BUILD_MAC_10_13 MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
+#define CURL_BUILD_MAC_10_15 MAC_OS_X_VERSION_MAX_ALLOWED >= 101500
+/* These macros mean "the following code is present to allow runtime backward
+ compatibility with at least this cat or earlier":
+ (You set this at build-time using the compiler command line option
+ "-mmacosx-version-min.") */
+#define CURL_SUPPORT_MAC_10_5 MAC_OS_X_VERSION_MIN_REQUIRED <= 1050
+#define CURL_SUPPORT_MAC_10_6 MAC_OS_X_VERSION_MIN_REQUIRED <= 1060
+#define CURL_SUPPORT_MAC_10_7 MAC_OS_X_VERSION_MIN_REQUIRED <= 1070
+#define CURL_SUPPORT_MAC_10_8 MAC_OS_X_VERSION_MIN_REQUIRED <= 1080
+#define CURL_SUPPORT_MAC_10_9 MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
+
+#elif TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
+#define CURL_BUILD_IOS 1
+#define CURL_BUILD_IOS_7 __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
+#define CURL_BUILD_IOS_9 __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000
+#define CURL_BUILD_IOS_11 __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
+#define CURL_BUILD_IOS_13 __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
+#define CURL_BUILD_MAC 0
+#define CURL_BUILD_MAC_10_5 0
+#define CURL_BUILD_MAC_10_6 0
+#define CURL_BUILD_MAC_10_7 0
+#define CURL_BUILD_MAC_10_8 0
+#define CURL_BUILD_MAC_10_9 0
+#define CURL_BUILD_MAC_10_11 0
+#define CURL_BUILD_MAC_10_13 0
+#define CURL_BUILD_MAC_10_15 0
+#define CURL_SUPPORT_MAC_10_5 0
+#define CURL_SUPPORT_MAC_10_6 0
+#define CURL_SUPPORT_MAC_10_7 0
+#define CURL_SUPPORT_MAC_10_8 0
+#define CURL_SUPPORT_MAC_10_9 0
+
+#else
+#error "The Secure Transport back-end requires iOS or macOS."
+#endif /* (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */
+
+#if CURL_BUILD_MAC
+#include <sys/sysctl.h>
+#endif /* CURL_BUILD_MAC */
+
+#include "sendf.h"
+#include "inet_pton.h"
+#include "connect.h"
+#include "select.h"
+#include "vtls.h"
+#include "vtls_int.h"
+#include "sectransp.h"
+#include "curl_printf.h"
+#include "strdup.h"
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/* From MacTypes.h (which we can't include because it isn't present in iOS: */
+#define ioErr -36
+#define paramErr -50
+
+struct ssl_backend_data {
+ SSLContextRef ssl_ctx;
+ bool ssl_direction; /* true if writing, false if reading */
+ size_t ssl_write_buffered_length;
+};
+
+struct st_cipher {
+ const char *name; /* Cipher suite IANA name. It starts with "TLS_" prefix */
+ const char *alias_name; /* Alias name is the same as OpenSSL cipher name */
+ SSLCipherSuite num; /* Cipher suite code/number defined in IANA registry */
+ bool weak; /* Flag to mark cipher as weak based on previous implementation
+ of Secure Transport back-end by CURL */
+};
+
+/* Macro to initialize st_cipher data structure: stringify id to name, cipher
+ number/id, 'weak' suite flag
+ */
+#define CIPHER_DEF(num, alias, weak) \
+ { #num, alias, num, weak }
+
+/*
+ Macro to initialize st_cipher data structure with name, code (IANA cipher
+ number/id value), and 'weak' suite flag. The first 28 cipher suite numbers
+ have the same IANA code for both SSL and TLS standards: numbers 0x0000 to
+ 0x001B. They have different names though. The first 4 letters of the cipher
+ suite name are the protocol name: "SSL_" or "TLS_", rest of the IANA name is
+ the same for both SSL and TLS cipher suite name.
+ The second part of the problem is that macOS/iOS SDKs don't define all TLS
+ codes but only 12 of them. The SDK defines all SSL codes though, i.e. SSL_NUM
+ constant is always defined for those 28 ciphers while TLS_NUM is defined only
+ for 12 of the first 28 ciphers. Those 12 TLS cipher codes match to
+ corresponding SSL enum value and represent the same cipher suite. Therefore
+ we'll use the SSL enum value for those cipher suites because it is defined
+ for all 28 of them.
+ We make internal data consistent and based on TLS names, i.e. all st_cipher
+ item names start with the "TLS_" prefix.
+ Summarizing all the above, those 28 first ciphers are presented in our table
+ with both TLS and SSL names. Their cipher numbers are assigned based on the
+ SDK enum value for the SSL cipher, which matches to IANA TLS number.
+ */
+#define CIPHER_DEF_SSLTLS(num_wo_prefix, alias, weak) \
+ { "TLS_" #num_wo_prefix, alias, SSL_##num_wo_prefix, weak }
+
+/*
+ Cipher suites were marked as weak based on the following:
+ RC4 encryption - rfc7465, the document contains a list of deprecated ciphers.
+ Marked in the code below as weak.
+ RC2 encryption - many mentions, was found vulnerable to a relatively easy
+ attack https://link.springer.com/chapter/10.1007%2F3-540-69710-1_14
+ Marked in the code below as weak.
+ DES and IDEA encryption - rfc5469, has a list of deprecated ciphers.
+ Marked in the code below as weak.
+ Anonymous Diffie-Hellman authentication and anonymous elliptic curve
+ Diffie-Hellman - vulnerable to a man-in-the-middle attack. Deprecated by
+ RFC 4346 aka TLS 1.1 (section A.5, page 60)
+ Null bulk encryption suites - not encrypted communication
+ Export ciphers, i.e. ciphers with restrictions to be used outside the US for
+ software exported to some countries, they were excluded from TLS 1.1
+ version. More precisely, they were noted as ciphers which MUST NOT be
+ negotiated in RFC 4346 aka TLS 1.1 (section A.5, pages 60 and 61).
+ All of those filters were considered weak because they contain a weak
+ algorithm like DES, RC2 or RC4, and already considered weak by other
+ criteria.
+ 3DES - NIST deprecated it and is going to retire it by 2023
+ https://csrc.nist.gov/News/2017/Update-to-Current-Use-and-Deprecation-of-TDEA
+ OpenSSL https://www.openssl.org/blog/blog/2016/08/24/sweet32/ also
+ deprecated those ciphers. Some other libraries also consider it
+ vulnerable or at least not strong enough.
+
+ CBC ciphers are vulnerable with SSL3.0 and TLS1.0:
+ https://www.cisco.com/c/en/us/support/docs/security/email-security-appliance
+ /118518-technote-esa-00.html
+ We don't take care of this issue because it is resolved by later TLS
+ versions and for us, it requires more complicated checks, we need to
+ check a protocol version also. Vulnerability doesn't look very critical
+ and we do not filter out those cipher suites.
+ */
+
+#define CIPHER_WEAK_NOT_ENCRYPTED TRUE
+#define CIPHER_WEAK_RC_ENCRYPTION TRUE
+#define CIPHER_WEAK_DES_ENCRYPTION TRUE
+#define CIPHER_WEAK_IDEA_ENCRYPTION TRUE
+#define CIPHER_WEAK_ANON_AUTH TRUE
+#define CIPHER_WEAK_3DES_ENCRYPTION TRUE
+#define CIPHER_STRONG_ENOUGH FALSE
+
+/* Please do not change the order of the first ciphers available for SSL.
+ Do not insert and do not delete any of them. Code below
+ depends on their order and continuity.
+ If you add a new cipher, please maintain order by number, i.e.
+ insert in between existing items to appropriate place based on
+ cipher suite IANA number
+*/
+const static struct st_cipher ciphertable[] = {
+ /* SSL version 3.0 and initial TLS 1.0 cipher suites.
+ Defined since SDK 10.2.8 */
+ CIPHER_DEF_SSLTLS(NULL_WITH_NULL_NULL, /* 0x0000 */
+ NULL,
+ CIPHER_WEAK_NOT_ENCRYPTED),
+ CIPHER_DEF_SSLTLS(RSA_WITH_NULL_MD5, /* 0x0001 */
+ "NULL-MD5",
+ CIPHER_WEAK_NOT_ENCRYPTED),
+ CIPHER_DEF_SSLTLS(RSA_WITH_NULL_SHA, /* 0x0002 */
+ "NULL-SHA",
+ CIPHER_WEAK_NOT_ENCRYPTED),
+ CIPHER_DEF_SSLTLS(RSA_EXPORT_WITH_RC4_40_MD5, /* 0x0003 */
+ "EXP-RC4-MD5",
+ CIPHER_WEAK_RC_ENCRYPTION),
+ CIPHER_DEF_SSLTLS(RSA_WITH_RC4_128_MD5, /* 0x0004 */
+ "RC4-MD5",
+ CIPHER_WEAK_RC_ENCRYPTION),
+ CIPHER_DEF_SSLTLS(RSA_WITH_RC4_128_SHA, /* 0x0005 */
+ "RC4-SHA",
+ CIPHER_WEAK_RC_ENCRYPTION),
+ CIPHER_DEF_SSLTLS(RSA_EXPORT_WITH_RC2_CBC_40_MD5, /* 0x0006 */
+ "EXP-RC2-CBC-MD5",
+ CIPHER_WEAK_RC_ENCRYPTION),
+ CIPHER_DEF_SSLTLS(RSA_WITH_IDEA_CBC_SHA, /* 0x0007 */
+ "IDEA-CBC-SHA",
+ CIPHER_WEAK_IDEA_ENCRYPTION),
+ CIPHER_DEF_SSLTLS(RSA_EXPORT_WITH_DES40_CBC_SHA, /* 0x0008 */
+ "EXP-DES-CBC-SHA",
+ CIPHER_WEAK_DES_ENCRYPTION),
+ CIPHER_DEF_SSLTLS(RSA_WITH_DES_CBC_SHA, /* 0x0009 */
+ "DES-CBC-SHA",
+ CIPHER_WEAK_DES_ENCRYPTION),
+ CIPHER_DEF_SSLTLS(RSA_WITH_3DES_EDE_CBC_SHA, /* 0x000A */
+ "DES-CBC3-SHA",
+ CIPHER_WEAK_3DES_ENCRYPTION),
+ CIPHER_DEF_SSLTLS(DH_DSS_EXPORT_WITH_DES40_CBC_SHA, /* 0x000B */
+ "EXP-DH-DSS-DES-CBC-SHA",
+ CIPHER_WEAK_DES_ENCRYPTION),
+ CIPHER_DEF_SSLTLS(DH_DSS_WITH_DES_CBC_SHA, /* 0x000C */
+ "DH-DSS-DES-CBC-SHA",
+ CIPHER_WEAK_DES_ENCRYPTION),
+ CIPHER_DEF_SSLTLS(DH_DSS_WITH_3DES_EDE_CBC_SHA, /* 0x000D */
+ "DH-DSS-DES-CBC3-SHA",
+ CIPHER_WEAK_3DES_ENCRYPTION),
+ CIPHER_DEF_SSLTLS(DH_RSA_EXPORT_WITH_DES40_CBC_SHA, /* 0x000E */
+ "EXP-DH-RSA-DES-CBC-SHA",
+ CIPHER_WEAK_DES_ENCRYPTION),
+ CIPHER_DEF_SSLTLS(DH_RSA_WITH_DES_CBC_SHA, /* 0x000F */
+ "DH-RSA-DES-CBC-SHA",
+ CIPHER_WEAK_DES_ENCRYPTION),
+ CIPHER_DEF_SSLTLS(DH_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x0010 */
+ "DH-RSA-DES-CBC3-SHA",
+ CIPHER_WEAK_3DES_ENCRYPTION),
+ CIPHER_DEF_SSLTLS(DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, /* 0x0011 */
+ "EXP-EDH-DSS-DES-CBC-SHA",
+ CIPHER_WEAK_DES_ENCRYPTION),
+ CIPHER_DEF_SSLTLS(DHE_DSS_WITH_DES_CBC_SHA, /* 0x0012 */
+ "EDH-DSS-CBC-SHA",
+ CIPHER_WEAK_DES_ENCRYPTION),
+ CIPHER_DEF_SSLTLS(DHE_DSS_WITH_3DES_EDE_CBC_SHA, /* 0x0013 */
+ "DHE-DSS-DES-CBC3-SHA",
+ CIPHER_WEAK_3DES_ENCRYPTION),
+ CIPHER_DEF_SSLTLS(DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, /* 0x0014 */
+ "EXP-EDH-RSA-DES-CBC-SHA",
+ CIPHER_WEAK_DES_ENCRYPTION),
+ CIPHER_DEF_SSLTLS(DHE_RSA_WITH_DES_CBC_SHA, /* 0x0015 */
+ "EDH-RSA-DES-CBC-SHA",
+ CIPHER_WEAK_DES_ENCRYPTION),
+ CIPHER_DEF_SSLTLS(DHE_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x0016 */
+ "DHE-RSA-DES-CBC3-SHA",
+ CIPHER_WEAK_3DES_ENCRYPTION),
+ CIPHER_DEF_SSLTLS(DH_anon_EXPORT_WITH_RC4_40_MD5, /* 0x0017 */
+ "EXP-ADH-RC4-MD5",
+ CIPHER_WEAK_ANON_AUTH),
+ CIPHER_DEF_SSLTLS(DH_anon_WITH_RC4_128_MD5, /* 0x0018 */
+ "ADH-RC4-MD5",
+ CIPHER_WEAK_ANON_AUTH),
+ CIPHER_DEF_SSLTLS(DH_anon_EXPORT_WITH_DES40_CBC_SHA, /* 0x0019 */
+ "EXP-ADH-DES-CBC-SHA",
+ CIPHER_WEAK_ANON_AUTH),
+ CIPHER_DEF_SSLTLS(DH_anon_WITH_DES_CBC_SHA, /* 0x001A */
+ "ADH-DES-CBC-SHA",
+ CIPHER_WEAK_ANON_AUTH),
+ CIPHER_DEF_SSLTLS(DH_anon_WITH_3DES_EDE_CBC_SHA, /* 0x001B */
+ "ADH-DES-CBC3-SHA",
+ CIPHER_WEAK_3DES_ENCRYPTION),
+ CIPHER_DEF(SSL_FORTEZZA_DMS_WITH_NULL_SHA, /* 0x001C */
+ NULL,
+ CIPHER_WEAK_NOT_ENCRYPTED),
+ CIPHER_DEF(SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA, /* 0x001D */
+ NULL,
+ CIPHER_STRONG_ENOUGH),
+
+#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7
+ /* RFC 4785 - Pre-Shared Key (PSK) Ciphersuites with NULL Encryption */
+ CIPHER_DEF(TLS_PSK_WITH_NULL_SHA, /* 0x002C */
+ "PSK-NULL-SHA",
+ CIPHER_WEAK_NOT_ENCRYPTED),
+ CIPHER_DEF(TLS_DHE_PSK_WITH_NULL_SHA, /* 0x002D */
+ "DHE-PSK-NULL-SHA",
+ CIPHER_WEAK_NOT_ENCRYPTED),
+ CIPHER_DEF(TLS_RSA_PSK_WITH_NULL_SHA, /* 0x002E */
+ "RSA-PSK-NULL-SHA",
+ CIPHER_WEAK_NOT_ENCRYPTED),
+#endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */
+
+ /* TLS addenda using AES, per RFC 3268. Defined since SDK 10.4u */
+ CIPHER_DEF(TLS_RSA_WITH_AES_128_CBC_SHA, /* 0x002F */
+ "AES128-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DH_DSS_WITH_AES_128_CBC_SHA, /* 0x0030 */
+ "DH-DSS-AES128-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DH_RSA_WITH_AES_128_CBC_SHA, /* 0x0031 */
+ "DH-RSA-AES128-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DHE_DSS_WITH_AES_128_CBC_SHA, /* 0x0032 */
+ "DHE-DSS-AES128-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DHE_RSA_WITH_AES_128_CBC_SHA, /* 0x0033 */
+ "DHE-RSA-AES128-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DH_anon_WITH_AES_128_CBC_SHA, /* 0x0034 */
+ "ADH-AES128-SHA",
+ CIPHER_WEAK_ANON_AUTH),
+ CIPHER_DEF(TLS_RSA_WITH_AES_256_CBC_SHA, /* 0x0035 */
+ "AES256-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DH_DSS_WITH_AES_256_CBC_SHA, /* 0x0036 */
+ "DH-DSS-AES256-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DH_RSA_WITH_AES_256_CBC_SHA, /* 0x0037 */
+ "DH-RSA-AES256-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DHE_DSS_WITH_AES_256_CBC_SHA, /* 0x0038 */
+ "DHE-DSS-AES256-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DHE_RSA_WITH_AES_256_CBC_SHA, /* 0x0039 */
+ "DHE-RSA-AES256-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DH_anon_WITH_AES_256_CBC_SHA, /* 0x003A */
+ "ADH-AES256-SHA",
+ CIPHER_WEAK_ANON_AUTH),
+
+#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
+ /* TLS 1.2 addenda, RFC 5246 */
+ /* Server provided RSA certificate for key exchange. */
+ CIPHER_DEF(TLS_RSA_WITH_NULL_SHA256, /* 0x003B */
+ "NULL-SHA256",
+ CIPHER_WEAK_NOT_ENCRYPTED),
+ CIPHER_DEF(TLS_RSA_WITH_AES_128_CBC_SHA256, /* 0x003C */
+ "AES128-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_RSA_WITH_AES_256_CBC_SHA256, /* 0x003D */
+ "AES256-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ /* Server-authenticated (and optionally client-authenticated)
+ Diffie-Hellman. */
+ CIPHER_DEF(TLS_DH_DSS_WITH_AES_128_CBC_SHA256, /* 0x003E */
+ "DH-DSS-AES128-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DH_RSA_WITH_AES_128_CBC_SHA256, /* 0x003F */
+ "DH-RSA-AES128-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, /* 0x0040 */
+ "DHE-DSS-AES128-SHA256",
+ CIPHER_STRONG_ENOUGH),
+
+ /* TLS 1.2 addenda, RFC 5246 */
+ CIPHER_DEF(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, /* 0x0067 */
+ "DHE-RSA-AES128-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DH_DSS_WITH_AES_256_CBC_SHA256, /* 0x0068 */
+ "DH-DSS-AES256-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DH_RSA_WITH_AES_256_CBC_SHA256, /* 0x0069 */
+ "DH-RSA-AES256-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, /* 0x006A */
+ "DHE-DSS-AES256-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, /* 0x006B */
+ "DHE-RSA-AES256-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DH_anon_WITH_AES_128_CBC_SHA256, /* 0x006C */
+ "ADH-AES128-SHA256",
+ CIPHER_WEAK_ANON_AUTH),
+ CIPHER_DEF(TLS_DH_anon_WITH_AES_256_CBC_SHA256, /* 0x006D */
+ "ADH-AES256-SHA256",
+ CIPHER_WEAK_ANON_AUTH),
+#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
+
+#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7
+ /* Addendum from RFC 4279, TLS PSK */
+ CIPHER_DEF(TLS_PSK_WITH_RC4_128_SHA, /* 0x008A */
+ "PSK-RC4-SHA",
+ CIPHER_WEAK_RC_ENCRYPTION),
+ CIPHER_DEF(TLS_PSK_WITH_3DES_EDE_CBC_SHA, /* 0x008B */
+ "PSK-3DES-EDE-CBC-SHA",
+ CIPHER_WEAK_3DES_ENCRYPTION),
+ CIPHER_DEF(TLS_PSK_WITH_AES_128_CBC_SHA, /* 0x008C */
+ "PSK-AES128-CBC-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_PSK_WITH_AES_256_CBC_SHA, /* 0x008D */
+ "PSK-AES256-CBC-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DHE_PSK_WITH_RC4_128_SHA, /* 0x008E */
+ "DHE-PSK-RC4-SHA",
+ CIPHER_WEAK_RC_ENCRYPTION),
+ CIPHER_DEF(TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, /* 0x008F */
+ "DHE-PSK-3DES-EDE-CBC-SHA",
+ CIPHER_WEAK_3DES_ENCRYPTION),
+ CIPHER_DEF(TLS_DHE_PSK_WITH_AES_128_CBC_SHA, /* 0x0090 */
+ "DHE-PSK-AES128-CBC-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DHE_PSK_WITH_AES_256_CBC_SHA, /* 0x0091 */
+ "DHE-PSK-AES256-CBC-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_RSA_PSK_WITH_RC4_128_SHA, /* 0x0092 */
+ "RSA-PSK-RC4-SHA",
+ CIPHER_WEAK_RC_ENCRYPTION),
+ CIPHER_DEF(TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, /* 0x0093 */
+ "RSA-PSK-3DES-EDE-CBC-SHA",
+ CIPHER_WEAK_3DES_ENCRYPTION),
+ CIPHER_DEF(TLS_RSA_PSK_WITH_AES_128_CBC_SHA, /* 0x0094 */
+ "RSA-PSK-AES128-CBC-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_RSA_PSK_WITH_AES_256_CBC_SHA, /* 0x0095 */
+ "RSA-PSK-AES256-CBC-SHA",
+ CIPHER_STRONG_ENOUGH),
+#endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */
+
+#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
+ /* Addenda from rfc 5288 AES Galois Counter Mode (GCM) Cipher Suites
+ for TLS. */
+ CIPHER_DEF(TLS_RSA_WITH_AES_128_GCM_SHA256, /* 0x009C */
+ "AES128-GCM-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_RSA_WITH_AES_256_GCM_SHA384, /* 0x009D */
+ "AES256-GCM-SHA384",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, /* 0x009E */
+ "DHE-RSA-AES128-GCM-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, /* 0x009F */
+ "DHE-RSA-AES256-GCM-SHA384",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DH_RSA_WITH_AES_128_GCM_SHA256, /* 0x00A0 */
+ "DH-RSA-AES128-GCM-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DH_RSA_WITH_AES_256_GCM_SHA384, /* 0x00A1 */
+ "DH-RSA-AES256-GCM-SHA384",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, /* 0x00A2 */
+ "DHE-DSS-AES128-GCM-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, /* 0x00A3 */
+ "DHE-DSS-AES256-GCM-SHA384",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DH_DSS_WITH_AES_128_GCM_SHA256, /* 0x00A4 */
+ "DH-DSS-AES128-GCM-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DH_DSS_WITH_AES_256_GCM_SHA384, /* 0x00A5 */
+ "DH-DSS-AES256-GCM-SHA384",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DH_anon_WITH_AES_128_GCM_SHA256, /* 0x00A6 */
+ "ADH-AES128-GCM-SHA256",
+ CIPHER_WEAK_ANON_AUTH),
+ CIPHER_DEF(TLS_DH_anon_WITH_AES_256_GCM_SHA384, /* 0x00A7 */
+ "ADH-AES256-GCM-SHA384",
+ CIPHER_WEAK_ANON_AUTH),
+#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
+
+#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7
+ /* RFC 5487 - PSK with SHA-256/384 and AES GCM */
+ CIPHER_DEF(TLS_PSK_WITH_AES_128_GCM_SHA256, /* 0x00A8 */
+ "PSK-AES128-GCM-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_PSK_WITH_AES_256_GCM_SHA384, /* 0x00A9 */
+ "PSK-AES256-GCM-SHA384",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, /* 0x00AA */
+ "DHE-PSK-AES128-GCM-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, /* 0x00AB */
+ "DHE-PSK-AES256-GCM-SHA384",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, /* 0x00AC */
+ "RSA-PSK-AES128-GCM-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, /* 0x00AD */
+ "RSA-PSK-AES256-GCM-SHA384",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_PSK_WITH_AES_128_CBC_SHA256, /* 0x00AE */
+ "PSK-AES128-CBC-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_PSK_WITH_AES_256_CBC_SHA384, /* 0x00AF */
+ "PSK-AES256-CBC-SHA384",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_PSK_WITH_NULL_SHA256, /* 0x00B0 */
+ "PSK-NULL-SHA256",
+ CIPHER_WEAK_NOT_ENCRYPTED),
+ CIPHER_DEF(TLS_PSK_WITH_NULL_SHA384, /* 0x00B1 */
+ "PSK-NULL-SHA384",
+ CIPHER_WEAK_NOT_ENCRYPTED),
+ CIPHER_DEF(TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, /* 0x00B2 */
+ "DHE-PSK-AES128-CBC-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, /* 0x00B3 */
+ "DHE-PSK-AES256-CBC-SHA384",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_DHE_PSK_WITH_NULL_SHA256, /* 0x00B4 */
+ "DHE-PSK-NULL-SHA256",
+ CIPHER_WEAK_NOT_ENCRYPTED),
+ CIPHER_DEF(TLS_DHE_PSK_WITH_NULL_SHA384, /* 0x00B5 */
+ "DHE-PSK-NULL-SHA384",
+ CIPHER_WEAK_NOT_ENCRYPTED),
+ CIPHER_DEF(TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, /* 0x00B6 */
+ "RSA-PSK-AES128-CBC-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, /* 0x00B7 */
+ "RSA-PSK-AES256-CBC-SHA384",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_RSA_PSK_WITH_NULL_SHA256, /* 0x00B8 */
+ "RSA-PSK-NULL-SHA256",
+ CIPHER_WEAK_NOT_ENCRYPTED),
+ CIPHER_DEF(TLS_RSA_PSK_WITH_NULL_SHA384, /* 0x00B9 */
+ "RSA-PSK-NULL-SHA384",
+ CIPHER_WEAK_NOT_ENCRYPTED),
+#endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */
+
+ /* RFC 5746 - Secure Renegotiation. This is not a real suite,
+ it is a response to initiate negotiation again */
+ CIPHER_DEF(TLS_EMPTY_RENEGOTIATION_INFO_SCSV, /* 0x00FF */
+ NULL,
+ CIPHER_STRONG_ENOUGH),
+
+#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11
+ /* TLS 1.3 standard cipher suites for ChaCha20+Poly1305.
+ Note: TLS 1.3 ciphersuites do not specify the key exchange
+ algorithm -- they only specify the symmetric ciphers.
+ Cipher alias name matches to OpenSSL cipher name, and for
+ TLS 1.3 ciphers */
+ CIPHER_DEF(TLS_AES_128_GCM_SHA256, /* 0x1301 */
+ NULL, /* The OpenSSL cipher name matches to the IANA name */
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_AES_256_GCM_SHA384, /* 0x1302 */
+ NULL, /* The OpenSSL cipher name matches to the IANA name */
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_CHACHA20_POLY1305_SHA256, /* 0x1303 */
+ NULL, /* The OpenSSL cipher name matches to the IANA name */
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_AES_128_CCM_SHA256, /* 0x1304 */
+ NULL, /* The OpenSSL cipher name matches to the IANA name */
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_AES_128_CCM_8_SHA256, /* 0x1305 */
+ NULL, /* The OpenSSL cipher name matches to the IANA name */
+ CIPHER_STRONG_ENOUGH),
+#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */
+
+#if CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS
+ /* ECDSA addenda, RFC 4492 */
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_NULL_SHA, /* 0xC001 */
+ "ECDH-ECDSA-NULL-SHA",
+ CIPHER_WEAK_NOT_ENCRYPTED),
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_RC4_128_SHA, /* 0xC002 */
+ "ECDH-ECDSA-RC4-SHA",
+ CIPHER_WEAK_RC_ENCRYPTION),
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC003 */
+ "ECDH-ECDSA-DES-CBC3-SHA",
+ CIPHER_WEAK_3DES_ENCRYPTION),
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC004 */
+ "ECDH-ECDSA-AES128-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC005 */
+ "ECDH-ECDSA-AES256-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_NULL_SHA, /* 0xC006 */
+ "ECDHE-ECDSA-NULL-SHA",
+ CIPHER_WEAK_NOT_ENCRYPTED),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, /* 0xC007 */
+ "ECDHE-ECDSA-RC4-SHA",
+ CIPHER_WEAK_RC_ENCRYPTION),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC008 */
+ "ECDHE-ECDSA-DES-CBC3-SHA",
+ CIPHER_WEAK_3DES_ENCRYPTION),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC009 */
+ "ECDHE-ECDSA-AES128-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC00A */
+ "ECDHE-ECDSA-AES256-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_NULL_SHA, /* 0xC00B */
+ "ECDH-RSA-NULL-SHA",
+ CIPHER_WEAK_NOT_ENCRYPTED),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_RC4_128_SHA, /* 0xC00C */
+ "ECDH-RSA-RC4-SHA",
+ CIPHER_WEAK_RC_ENCRYPTION),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC00D */
+ "ECDH-RSA-DES-CBC3-SHA",
+ CIPHER_WEAK_3DES_ENCRYPTION),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, /* 0xC00E */
+ "ECDH-RSA-AES128-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, /* 0xC00F */
+ "ECDH-RSA-AES256-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_NULL_SHA, /* 0xC010 */
+ "ECDHE-RSA-NULL-SHA",
+ CIPHER_WEAK_NOT_ENCRYPTED),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_RC4_128_SHA, /* 0xC011 */
+ "ECDHE-RSA-RC4-SHA",
+ CIPHER_WEAK_RC_ENCRYPTION),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC012 */
+ "ECDHE-RSA-DES-CBC3-SHA",
+ CIPHER_WEAK_3DES_ENCRYPTION),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, /* 0xC013 */
+ "ECDHE-RSA-AES128-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, /* 0xC014 */
+ "ECDHE-RSA-AES256-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDH_anon_WITH_NULL_SHA, /* 0xC015 */
+ "AECDH-NULL-SHA",
+ CIPHER_WEAK_ANON_AUTH),
+ CIPHER_DEF(TLS_ECDH_anon_WITH_RC4_128_SHA, /* 0xC016 */
+ "AECDH-RC4-SHA",
+ CIPHER_WEAK_ANON_AUTH),
+ CIPHER_DEF(TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, /* 0xC017 */
+ "AECDH-DES-CBC3-SHA",
+ CIPHER_WEAK_3DES_ENCRYPTION),
+ CIPHER_DEF(TLS_ECDH_anon_WITH_AES_128_CBC_SHA, /* 0xC018 */
+ "AECDH-AES128-SHA",
+ CIPHER_WEAK_ANON_AUTH),
+ CIPHER_DEF(TLS_ECDH_anon_WITH_AES_256_CBC_SHA, /* 0xC019 */
+ "AECDH-AES256-SHA",
+ CIPHER_WEAK_ANON_AUTH),
+#endif /* CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS */
+
+#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
+ /* Addenda from rfc 5289 Elliptic Curve Cipher Suites with
+ HMAC SHA-256/384. */
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC023 */
+ "ECDHE-ECDSA-AES128-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC024 */
+ "ECDHE-ECDSA-AES256-SHA384",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC025 */
+ "ECDH-ECDSA-AES128-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC026 */
+ "ECDH-ECDSA-AES256-SHA384",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, /* 0xC027 */
+ "ECDHE-RSA-AES128-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, /* 0xC028 */
+ "ECDHE-RSA-AES256-SHA384",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, /* 0xC029 */
+ "ECDH-RSA-AES128-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, /* 0xC02A */
+ "ECDH-RSA-AES256-SHA384",
+ CIPHER_STRONG_ENOUGH),
+ /* Addenda from rfc 5289 Elliptic Curve Cipher Suites with
+ SHA-256/384 and AES Galois Counter Mode (GCM) */
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02B */
+ "ECDHE-ECDSA-AES128-GCM-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02C */
+ "ECDHE-ECDSA-AES256-GCM-SHA384",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02D */
+ "ECDH-ECDSA-AES128-GCM-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02E */
+ "ECDH-ECDSA-AES256-GCM-SHA384",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* 0xC02F */
+ "ECDHE-RSA-AES128-GCM-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, /* 0xC030 */
+ "ECDHE-RSA-AES256-GCM-SHA384",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, /* 0xC031 */
+ "ECDH-RSA-AES128-GCM-SHA256",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, /* 0xC032 */
+ "ECDH-RSA-AES256-GCM-SHA384",
+ CIPHER_STRONG_ENOUGH),
+#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
+
+#if CURL_BUILD_MAC_10_15 || CURL_BUILD_IOS_13
+ /* ECDHE_PSK Cipher Suites for Transport Layer Security (TLS), RFC 5489 */
+ CIPHER_DEF(TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, /* 0xC035 */
+ "ECDHE-PSK-AES128-CBC-SHA",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, /* 0xC036 */
+ "ECDHE-PSK-AES256-CBC-SHA",
+ CIPHER_STRONG_ENOUGH),
+#endif /* CURL_BUILD_MAC_10_15 || CURL_BUILD_IOS_13 */
+
+#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11
+ /* Addenda from rfc 7905 ChaCha20-Poly1305 Cipher Suites for
+ Transport Layer Security (TLS). */
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA8 */
+ "ECDHE-RSA-CHACHA20-POLY1305",
+ CIPHER_STRONG_ENOUGH),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA9 */
+ "ECDHE-ECDSA-CHACHA20-POLY1305",
+ CIPHER_STRONG_ENOUGH),
+#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */
+
+#if CURL_BUILD_MAC_10_15 || CURL_BUILD_IOS_13
+ /* ChaCha20-Poly1305 Cipher Suites for Transport Layer Security (TLS),
+ RFC 7905 */
+ CIPHER_DEF(TLS_PSK_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCAB */
+ "PSK-CHACHA20-POLY1305",
+ CIPHER_STRONG_ENOUGH),
+#endif /* CURL_BUILD_MAC_10_15 || CURL_BUILD_IOS_13 */
+
+ /* Tags for SSL 2 cipher kinds which are not specified for SSL 3.
+ Defined since SDK 10.2.8 */
+ CIPHER_DEF(SSL_RSA_WITH_RC2_CBC_MD5, /* 0xFF80 */
+ NULL,
+ CIPHER_WEAK_RC_ENCRYPTION),
+ CIPHER_DEF(SSL_RSA_WITH_IDEA_CBC_MD5, /* 0xFF81 */
+ NULL,
+ CIPHER_WEAK_IDEA_ENCRYPTION),
+ CIPHER_DEF(SSL_RSA_WITH_DES_CBC_MD5, /* 0xFF82 */
+ NULL,
+ CIPHER_WEAK_DES_ENCRYPTION),
+ CIPHER_DEF(SSL_RSA_WITH_3DES_EDE_CBC_MD5, /* 0xFF83 */
+ NULL,
+ CIPHER_WEAK_3DES_ENCRYPTION),
+};
+
+#define NUM_OF_CIPHERS sizeof(ciphertable)/sizeof(ciphertable[0])
+
+
+/* pinned public key support tests */
+
+/* version 1 supports macOS 10.12+ and iOS 10+ */
+#if ((TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000) || \
+ (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200))
+#define SECTRANSP_PINNEDPUBKEY_V1 1
+#endif
+
+/* version 2 supports MacOSX 10.7+ */
+#if (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
+#define SECTRANSP_PINNEDPUBKEY_V2 1
+#endif
+
+#if defined(SECTRANSP_PINNEDPUBKEY_V1) || defined(SECTRANSP_PINNEDPUBKEY_V2)
+/* this backend supports CURLOPT_PINNEDPUBLICKEY */
+#define SECTRANSP_PINNEDPUBKEY 1
+#endif /* SECTRANSP_PINNEDPUBKEY */
+
+#ifdef SECTRANSP_PINNEDPUBKEY
+/* both new and old APIs return rsa keys missing the spki header (not DER) */
+static const unsigned char rsa4096SpkiHeader[] = {
+ 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00};
+
+static const unsigned char rsa2048SpkiHeader[] = {
+ 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00};
+#ifdef SECTRANSP_PINNEDPUBKEY_V1
+/* the *new* version doesn't return DER encoded ecdsa certs like the old... */
+static const unsigned char ecDsaSecp256r1SpkiHeader[] = {
+ 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
+ 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48,
+ 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
+ 0x42, 0x00};
+
+static const unsigned char ecDsaSecp384r1SpkiHeader[] = {
+ 0x30, 0x76, 0x30, 0x10, 0x06, 0x07,
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
+ 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04,
+ 0x00, 0x22, 0x03, 0x62, 0x00};
+#endif /* SECTRANSP_PINNEDPUBKEY_V1 */
+#endif /* SECTRANSP_PINNEDPUBKEY */
+
+static OSStatus bio_cf_in_read(SSLConnectionRef connection,
+ void *buf,
+ size_t *dataLength) /* IN/OUT */
+{
+ struct Curl_cfilter *cf = (struct Curl_cfilter *)connection;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct Curl_easy *data = connssl->call_data;
+ ssize_t nread;
+ CURLcode result;
+ OSStatus rtn = noErr;
+
+ DEBUGASSERT(data);
+ nread = Curl_conn_cf_recv(cf->next, data, buf, *dataLength, &result);
+ if(nread < 0) {
+ switch(result) {
+ case CURLE_OK:
+ case CURLE_AGAIN:
+ rtn = errSSLWouldBlock;
+ backend->ssl_direction = false;
+ break;
+ default:
+ rtn = ioErr;
+ break;
+ }
+ nread = 0;
+ }
+ *dataLength = nread;
+ return rtn;
+}
+
+static OSStatus bio_cf_out_write(SSLConnectionRef connection,
+ const void *buf,
+ size_t *dataLength) /* IN/OUT */
+{
+ struct Curl_cfilter *cf = (struct Curl_cfilter *)connection;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct Curl_easy *data = connssl->call_data;
+ ssize_t nwritten;
+ CURLcode result;
+ OSStatus ortn = noErr;
+
+ DEBUGASSERT(data);
+ nwritten = Curl_conn_cf_send(cf->next, data, buf, *dataLength, &result);
+ if(nwritten <= 0) {
+ if(result == CURLE_AGAIN) {
+ ortn = errSSLWouldBlock;
+ backend->ssl_direction = true;
+ }
+ else {
+ ortn = ioErr;
+ }
+ nwritten = 0;
+ }
+ *dataLength = nwritten;
+ return ortn;
+}
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+CF_INLINE const char *TLSCipherNameForNumber(SSLCipherSuite cipher)
+{
+ /* The first ciphers in the ciphertable are continuous. Here we do small
+ optimization and instead of loop directly get SSL name by cipher number.
+ */
+ if(cipher <= SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA) {
+ return ciphertable[cipher].name;
+ }
+ /* Iterate through the rest of the ciphers */
+ for(size_t i = SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA + 1;
+ i < NUM_OF_CIPHERS;
+ ++i) {
+ if(ciphertable[i].num == cipher) {
+ return ciphertable[i].name;
+ }
+ }
+ return ciphertable[SSL_NULL_WITH_NULL_NULL].name;
+}
+#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
+
+#if CURL_BUILD_MAC
+CF_INLINE void GetDarwinVersionNumber(int *major, int *minor)
+{
+ int mib[2];
+ char *os_version;
+ size_t os_version_len;
+ char *os_version_major, *os_version_minor;
+ char *tok_buf;
+
+ /* Get the Darwin kernel version from the kernel using sysctl(): */
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_OSRELEASE;
+ if(sysctl(mib, 2, NULL, &os_version_len, NULL, 0) == -1)
+ return;
+ os_version = malloc(os_version_len*sizeof(char));
+ if(!os_version)
+ return;
+ if(sysctl(mib, 2, os_version, &os_version_len, NULL, 0) == -1) {
+ free(os_version);
+ return;
+ }
+
+ /* Parse the version: */
+ os_version_major = strtok_r(os_version, ".", &tok_buf);
+ os_version_minor = strtok_r(NULL, ".", &tok_buf);
+ *major = atoi(os_version_major);
+ *minor = atoi(os_version_minor);
+ free(os_version);
+}
+#endif /* CURL_BUILD_MAC */
+
+/* Apple provides a myriad of ways of getting information about a certificate
+ into a string. Some aren't available under iOS or newer cats. So here's
+ a unified function for getting a string describing the certificate that
+ ought to work in all cats starting with Leopard. */
+CF_INLINE CFStringRef getsubject(SecCertificateRef cert)
+{
+ CFStringRef server_cert_summary = CFSTR("(null)");
+
+#if CURL_BUILD_IOS
+ /* iOS: There's only one way to do this. */
+ server_cert_summary = SecCertificateCopySubjectSummary(cert);
+#else
+#if CURL_BUILD_MAC_10_7
+ /* Lion & later: Get the long description if we can. */
+ if(SecCertificateCopyLongDescription)
+ server_cert_summary =
+ SecCertificateCopyLongDescription(NULL, cert, NULL);
+ else
+#endif /* CURL_BUILD_MAC_10_7 */
+#if CURL_BUILD_MAC_10_6
+ /* Snow Leopard: Get the certificate summary. */
+ if(SecCertificateCopySubjectSummary)
+ server_cert_summary = SecCertificateCopySubjectSummary(cert);
+ else
+#endif /* CURL_BUILD_MAC_10_6 */
+ /* Leopard is as far back as we go... */
+ (void)SecCertificateCopyCommonName(cert, &server_cert_summary);
+#endif /* CURL_BUILD_IOS */
+ return server_cert_summary;
+}
+
+static CURLcode CopyCertSubject(struct Curl_easy *data,
+ SecCertificateRef cert, char **certp)
+{
+ CFStringRef c = getsubject(cert);
+ CURLcode result = CURLE_OK;
+ const char *direct;
+ char *cbuf = NULL;
+ *certp = NULL;
+
+ if(!c) {
+ failf(data, "SSL: invalid CA certificate subject");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+
+ /* If the subject is already available as UTF-8 encoded (ie 'direct') then
+ use that, else convert it. */
+ direct = CFStringGetCStringPtr(c, kCFStringEncodingUTF8);
+ if(direct) {
+ *certp = strdup(direct);
+ if(!*certp) {
+ failf(data, "SSL: out of memory");
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ }
+ else {
+ size_t cbuf_size = ((size_t)CFStringGetLength(c) * 4) + 1;
+ cbuf = calloc(cbuf_size, 1);
+ if(cbuf) {
+ if(!CFStringGetCString(c, cbuf, cbuf_size,
+ kCFStringEncodingUTF8)) {
+ failf(data, "SSL: invalid CA certificate subject");
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else
+ /* pass back the buffer */
+ *certp = cbuf;
+ }
+ else {
+ failf(data, "SSL: couldn't allocate %zu bytes of memory", cbuf_size);
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ }
+ if(result)
+ free(cbuf);
+ CFRelease(c);
+ return result;
+}
+
+#if CURL_SUPPORT_MAC_10_6
+/* The SecKeychainSearch API was deprecated in Lion, and using it will raise
+ deprecation warnings, so let's not compile this unless it's necessary: */
+static OSStatus CopyIdentityWithLabelOldSchool(char *label,
+ SecIdentityRef *out_c_a_k)
+{
+ OSStatus status = errSecItemNotFound;
+ SecKeychainAttributeList attr_list;
+ SecKeychainAttribute attr;
+ SecKeychainSearchRef search = NULL;
+ SecCertificateRef cert = NULL;
+
+ /* Set up the attribute list: */
+ attr_list.count = 1L;
+ attr_list.attr = &attr;
+
+ /* Set up our lone search criterion: */
+ attr.tag = kSecLabelItemAttr;
+ attr.data = label;
+ attr.length = (UInt32)strlen(label);
+
+ /* Start searching: */
+ status = SecKeychainSearchCreateFromAttributes(NULL,
+ kSecCertificateItemClass,
+ &attr_list,
+ &search);
+ if(status == noErr) {
+ status = SecKeychainSearchCopyNext(search,
+ (SecKeychainItemRef *)&cert);
+ if(status == noErr && cert) {
+ /* If we found a certificate, does it have a private key? */
+ status = SecIdentityCreateWithCertificate(NULL, cert, out_c_a_k);
+ CFRelease(cert);
+ }
+ }
+
+ if(search)
+ CFRelease(search);
+ return status;
+}
+#endif /* CURL_SUPPORT_MAC_10_6 */
+
+static OSStatus CopyIdentityWithLabel(char *label,
+ SecIdentityRef *out_cert_and_key)
+{
+ OSStatus status = errSecItemNotFound;
+
+#if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS
+ CFArrayRef keys_list;
+ CFIndex keys_list_count;
+ CFIndex i;
+ CFStringRef common_name;
+
+ /* SecItemCopyMatching() was introduced in iOS and Snow Leopard.
+ kSecClassIdentity was introduced in Lion. If both exist, let's use them
+ to find the certificate. */
+ if(SecItemCopyMatching && kSecClassIdentity) {
+ CFTypeRef keys[5];
+ CFTypeRef values[5];
+ CFDictionaryRef query_dict;
+ CFStringRef label_cf = CFStringCreateWithCString(NULL, label,
+ kCFStringEncodingUTF8);
+
+ /* Set up our search criteria and expected results: */
+ values[0] = kSecClassIdentity; /* we want a certificate and a key */
+ keys[0] = kSecClass;
+ values[1] = kCFBooleanTrue; /* we want a reference */
+ keys[1] = kSecReturnRef;
+ values[2] = kSecMatchLimitAll; /* kSecMatchLimitOne would be better if the
+ * label matching below worked correctly */
+ keys[2] = kSecMatchLimit;
+ /* identity searches need a SecPolicyRef in order to work */
+ values[3] = SecPolicyCreateSSL(false, NULL);
+ keys[3] = kSecMatchPolicy;
+ /* match the name of the certificate (doesn't work in macOS 10.12.1) */
+ values[4] = label_cf;
+ keys[4] = kSecAttrLabel;
+ query_dict = CFDictionaryCreate(NULL, (const void **)keys,
+ (const void **)values, 5L,
+ &kCFCopyStringDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ CFRelease(values[3]);
+
+ /* Do we have a match? */
+ status = SecItemCopyMatching(query_dict, (CFTypeRef *) &keys_list);
+
+ /* Because kSecAttrLabel matching doesn't work with kSecClassIdentity,
+ * we need to find the correct identity ourselves */
+ if(status == noErr) {
+ keys_list_count = CFArrayGetCount(keys_list);
+ *out_cert_and_key = NULL;
+ status = 1;
+ for(i = 0; i<keys_list_count; i++) {
+ OSStatus err = noErr;
+ SecCertificateRef cert = NULL;
+ SecIdentityRef identity =
+ (SecIdentityRef) CFArrayGetValueAtIndex(keys_list, i);
+ err = SecIdentityCopyCertificate(identity, &cert);
+ if(err == noErr) {
+ OSStatus copy_status = noErr;
+#if CURL_BUILD_IOS
+ common_name = SecCertificateCopySubjectSummary(cert);
+#elif CURL_BUILD_MAC_10_7
+ copy_status = SecCertificateCopyCommonName(cert, &common_name);
+#endif
+ if(copy_status == noErr &&
+ CFStringCompare(common_name, label_cf, 0) == kCFCompareEqualTo) {
+ CFRelease(cert);
+ CFRelease(common_name);
+ CFRetain(identity);
+ *out_cert_and_key = identity;
+ status = noErr;
+ break;
+ }
+ CFRelease(common_name);
+ }
+ CFRelease(cert);
+ }
+ }
+
+ if(keys_list)
+ CFRelease(keys_list);
+ CFRelease(query_dict);
+ CFRelease(label_cf);
+ }
+ else {
+#if CURL_SUPPORT_MAC_10_6
+ /* On Leopard and Snow Leopard, fall back to SecKeychainSearch. */
+ status = CopyIdentityWithLabelOldSchool(label, out_cert_and_key);
+#endif /* CURL_SUPPORT_MAC_10_6 */
+ }
+#elif CURL_SUPPORT_MAC_10_6
+ /* For developers building on older cats, we have no choice but to fall back
+ to SecKeychainSearch. */
+ status = CopyIdentityWithLabelOldSchool(label, out_cert_and_key);
+#endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */
+ return status;
+}
+
+static OSStatus CopyIdentityFromPKCS12File(const char *cPath,
+ const struct curl_blob *blob,
+ const char *cPassword,
+ SecIdentityRef *out_cert_and_key)
+{
+ OSStatus status = errSecItemNotFound;
+ CFURLRef pkcs_url = NULL;
+ CFStringRef password = cPassword ? CFStringCreateWithCString(NULL,
+ cPassword, kCFStringEncodingUTF8) : NULL;
+ CFDataRef pkcs_data = NULL;
+
+ /* We can import P12 files on iOS or OS X 10.7 or later: */
+ /* These constants are documented as having first appeared in 10.6 but they
+ raise linker errors when used on that cat for some reason. */
+#if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS
+ bool resource_imported;
+
+ if(blob) {
+ pkcs_data = CFDataCreate(kCFAllocatorDefault,
+ (const unsigned char *)blob->data, blob->len);
+ status = (pkcs_data != NULL) ? errSecSuccess : errSecAllocate;
+ resource_imported = (pkcs_data != NULL);
+ }
+ else {
+ pkcs_url =
+ CFURLCreateFromFileSystemRepresentation(NULL,
+ (const UInt8 *)cPath,
+ strlen(cPath), false);
+ resource_imported =
+ CFURLCreateDataAndPropertiesFromResource(NULL,
+ pkcs_url, &pkcs_data,
+ NULL, NULL, &status);
+ }
+
+ if(resource_imported) {
+ CFArrayRef items = NULL;
+
+ /* On iOS SecPKCS12Import will never add the client certificate to the
+ * Keychain.
+ *
+ * It gives us back a SecIdentityRef that we can use directly. */
+#if CURL_BUILD_IOS
+ const void *cKeys[] = {kSecImportExportPassphrase};
+ const void *cValues[] = {password};
+ CFDictionaryRef options = CFDictionaryCreate(NULL, cKeys, cValues,
+ password ? 1L : 0L, NULL, NULL);
+
+ if(options) {
+ status = SecPKCS12Import(pkcs_data, options, &items);
+ CFRelease(options);
+ }
+
+
+ /* On macOS SecPKCS12Import will always add the client certificate to
+ * the Keychain.
+ *
+ * As this doesn't match iOS, and apps may not want to see their client
+ * certificate saved in the user's keychain, we use SecItemImport
+ * with a NULL keychain to avoid importing it.
+ *
+ * This returns a SecCertificateRef from which we can construct a
+ * SecIdentityRef.
+ */
+#elif CURL_BUILD_MAC_10_7
+ SecItemImportExportKeyParameters keyParams;
+ SecExternalFormat inputFormat = kSecFormatPKCS12;
+ SecExternalItemType inputType = kSecItemTypeCertificate;
+
+ memset(&keyParams, 0x00, sizeof(keyParams));
+ keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+ keyParams.passphrase = password;
+
+ status = SecItemImport(pkcs_data, NULL, &inputFormat, &inputType,
+ 0, &keyParams, NULL, &items);
+#endif
+
+
+ /* Extract the SecIdentityRef */
+ if(status == errSecSuccess && items && CFArrayGetCount(items)) {
+ CFIndex i, count;
+ count = CFArrayGetCount(items);
+
+ for(i = 0; i < count; i++) {
+ CFTypeRef item = (CFTypeRef) CFArrayGetValueAtIndex(items, i);
+ CFTypeID itemID = CFGetTypeID(item);
+
+ if(itemID == CFDictionaryGetTypeID()) {
+ CFTypeRef identity = (CFTypeRef) CFDictionaryGetValue(
+ (CFDictionaryRef) item,
+ kSecImportItemIdentity);
+ CFRetain(identity);
+ *out_cert_and_key = (SecIdentityRef) identity;
+ break;
+ }
+#if CURL_BUILD_MAC_10_7
+ else if(itemID == SecCertificateGetTypeID()) {
+ status = SecIdentityCreateWithCertificate(NULL,
+ (SecCertificateRef) item,
+ out_cert_and_key);
+ break;
+ }
+#endif
+ }
+ }
+
+ if(items)
+ CFRelease(items);
+ CFRelease(pkcs_data);
+ }
+#endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */
+ if(password)
+ CFRelease(password);
+ if(pkcs_url)
+ CFRelease(pkcs_url);
+ return status;
+}
+
+/* This code was borrowed from nss.c, with some modifications:
+ * Determine whether the nickname passed in is a filename that needs to
+ * be loaded as a PEM or a regular NSS nickname.
+ *
+ * returns 1 for a file
+ * returns 0 for not a file
+ */
+CF_INLINE bool is_file(const char *filename)
+{
+ struct_stat st;
+
+ if(!filename)
+ return false;
+
+ if(stat(filename, &st) == 0)
+ return S_ISREG(st.st_mode);
+ return false;
+}
+
+#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
+static CURLcode sectransp_version_from_curl(SSLProtocol *darwinver,
+ long ssl_version)
+{
+ switch(ssl_version) {
+ case CURL_SSLVERSION_TLSv1_0:
+ *darwinver = kTLSProtocol1;
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_1:
+ *darwinver = kTLSProtocol11;
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_2:
+ *darwinver = kTLSProtocol12;
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_3:
+ /* TLS 1.3 support first appeared in iOS 11 and macOS 10.13 */
+#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1
+ if(__builtin_available(macOS 10.13, iOS 11.0, *)) {
+ *darwinver = kTLSProtocol13;
+ return CURLE_OK;
+ }
+#endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) &&
+ HAVE_BUILTIN_AVAILABLE == 1 */
+ break;
+ }
+ return CURLE_SSL_CONNECT_ERROR;
+}
+#endif
+
+static CURLcode set_ssl_version_min_max(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ long ssl_version = conn_config->version;
+ long ssl_version_max = conn_config->version_max;
+ long max_supported_version_by_os;
+
+ DEBUGASSERT(backend);
+
+ /* macOS 10.5-10.7 supported TLS 1.0 only.
+ macOS 10.8 and later, and iOS 5 and later, added TLS 1.1 and 1.2.
+ macOS 10.13 and later, and iOS 11 and later, added TLS 1.3. */
+#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1
+ if(__builtin_available(macOS 10.13, iOS 11.0, *)) {
+ max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_3;
+ }
+ else {
+ max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_2;
+ }
+#else
+ max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_2;
+#endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) &&
+ HAVE_BUILTIN_AVAILABLE == 1 */
+
+ switch(ssl_version) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+ ssl_version = CURL_SSLVERSION_TLSv1_0;
+ break;
+ }
+
+ switch(ssl_version_max) {
+ case CURL_SSLVERSION_MAX_NONE:
+ case CURL_SSLVERSION_MAX_DEFAULT:
+ ssl_version_max = max_supported_version_by_os;
+ break;
+ }
+
+#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
+ if(SSLSetProtocolVersionMax) {
+ SSLProtocol darwin_ver_min = kTLSProtocol1;
+ SSLProtocol darwin_ver_max = kTLSProtocol1;
+ CURLcode result = sectransp_version_from_curl(&darwin_ver_min,
+ ssl_version);
+ if(result) {
+ failf(data, "unsupported min version passed via CURLOPT_SSLVERSION");
+ return result;
+ }
+ result = sectransp_version_from_curl(&darwin_ver_max,
+ ssl_version_max >> 16);
+ if(result) {
+ failf(data, "unsupported max version passed via CURLOPT_SSLVERSION");
+ return result;
+ }
+
+ (void)SSLSetProtocolVersionMin(backend->ssl_ctx, darwin_ver_min);
+ (void)SSLSetProtocolVersionMax(backend->ssl_ctx, darwin_ver_max);
+ return result;
+ }
+ else {
+#if CURL_SUPPORT_MAC_10_8
+ long i = ssl_version;
+ (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kSSLProtocolAll,
+ false);
+ for(; i <= (ssl_version_max >> 16); i++) {
+ switch(i) {
+ case CURL_SSLVERSION_TLSv1_0:
+ (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kTLSProtocol1,
+ true);
+ break;
+ case CURL_SSLVERSION_TLSv1_1:
+ (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kTLSProtocol11,
+ true);
+ break;
+ case CURL_SSLVERSION_TLSv1_2:
+ (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kTLSProtocol12,
+ true);
+ break;
+ case CURL_SSLVERSION_TLSv1_3:
+ failf(data, "Your version of the OS does not support TLSv1.3");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ return CURLE_OK;
+#endif /* CURL_SUPPORT_MAC_10_8 */
+ }
+#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
+ failf(data, "Secure Transport: cannot set SSL protocol");
+ return CURLE_SSL_CONNECT_ERROR;
+}
+
+static bool is_cipher_suite_strong(SSLCipherSuite suite_num)
+{
+ for(size_t i = 0; i < NUM_OF_CIPHERS; ++i) {
+ if(ciphertable[i].num == suite_num) {
+ return !ciphertable[i].weak;
+ }
+ }
+ /* If the cipher is not in our list, assume it is a new one
+ and therefore strong. Previous implementation was the same,
+ if cipher suite is not in the list, it was considered strong enough */
+ return true;
+}
+
+static bool is_separator(char c)
+{
+ /* Return whether character is a cipher list separator. */
+ switch(c) {
+ case ' ':
+ case '\t':
+ case ':':
+ case ',':
+ case ';':
+ return true;
+ }
+ return false;
+}
+
+static CURLcode sectransp_set_default_ciphers(struct Curl_easy *data,
+ SSLContextRef ssl_ctx)
+{
+ size_t all_ciphers_count = 0UL, allowed_ciphers_count = 0UL, i;
+ SSLCipherSuite *all_ciphers = NULL, *allowed_ciphers = NULL;
+ OSStatus err = noErr;
+
+#if CURL_BUILD_MAC
+ int darwinver_maj = 0, darwinver_min = 0;
+
+ GetDarwinVersionNumber(&darwinver_maj, &darwinver_min);
+#endif /* CURL_BUILD_MAC */
+
+ /* Disable cipher suites that ST supports but are not safe. These ciphers
+ are unlikely to be used in any case since ST gives other ciphers a much
+ higher priority, but it's probably better that we not connect at all than
+ to give the user a false sense of security if the server only supports
+ insecure ciphers. (Note: We don't care about SSLv2-only ciphers.) */
+ err = SSLGetNumberSupportedCiphers(ssl_ctx, &all_ciphers_count);
+ if(err != noErr) {
+ failf(data, "SSL: SSLGetNumberSupportedCiphers() failed: OSStatus %d",
+ err);
+ return CURLE_SSL_CIPHER;
+ }
+ all_ciphers = malloc(all_ciphers_count*sizeof(SSLCipherSuite));
+ if(!all_ciphers) {
+ failf(data, "SSL: Failed to allocate memory for all ciphers");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ allowed_ciphers = malloc(all_ciphers_count*sizeof(SSLCipherSuite));
+ if(!allowed_ciphers) {
+ Curl_safefree(all_ciphers);
+ failf(data, "SSL: Failed to allocate memory for allowed ciphers");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ err = SSLGetSupportedCiphers(ssl_ctx, all_ciphers,
+ &all_ciphers_count);
+ if(err != noErr) {
+ Curl_safefree(all_ciphers);
+ Curl_safefree(allowed_ciphers);
+ return CURLE_SSL_CIPHER;
+ }
+ for(i = 0UL ; i < all_ciphers_count ; i++) {
+#if CURL_BUILD_MAC
+ /* There's a known bug in early versions of Mountain Lion where ST's ECC
+ ciphers (cipher suite 0xC001 through 0xC032) simply do not work.
+ Work around the problem here by disabling those ciphers if we are
+ running in an affected version of OS X. */
+ if(darwinver_maj == 12 && darwinver_min <= 3 &&
+ all_ciphers[i] >= 0xC001 && all_ciphers[i] <= 0xC032) {
+ continue;
+ }
+#endif /* CURL_BUILD_MAC */
+ if(is_cipher_suite_strong(all_ciphers[i])) {
+ allowed_ciphers[allowed_ciphers_count++] = all_ciphers[i];
+ }
+ }
+ err = SSLSetEnabledCiphers(ssl_ctx, allowed_ciphers,
+ allowed_ciphers_count);
+ Curl_safefree(all_ciphers);
+ Curl_safefree(allowed_ciphers);
+ if(err != noErr) {
+ failf(data, "SSL: SSLSetEnabledCiphers() failed: OSStatus %d", err);
+ return CURLE_SSL_CIPHER;
+ }
+ return CURLE_OK;
+}
+
+static CURLcode sectransp_set_selected_ciphers(struct Curl_easy *data,
+ SSLContextRef ssl_ctx,
+ const char *ciphers)
+{
+ size_t ciphers_count = 0;
+ const char *cipher_start = ciphers;
+ OSStatus err = noErr;
+ SSLCipherSuite selected_ciphers[NUM_OF_CIPHERS];
+
+ if(!ciphers)
+ return CURLE_OK;
+
+ while(is_separator(*ciphers)) /* Skip initial separators. */
+ ciphers++;
+ if(!*ciphers)
+ return CURLE_OK;
+
+ cipher_start = ciphers;
+ while(*cipher_start && ciphers_count < NUM_OF_CIPHERS) {
+ bool cipher_found = FALSE;
+ size_t cipher_len = 0;
+ const char *cipher_end = NULL;
+ bool tls_name = FALSE;
+
+ /* Skip separators */
+ while(is_separator(*cipher_start))
+ cipher_start++;
+ if(*cipher_start == '\0') {
+ break;
+ }
+ /* Find last position of a cipher in the ciphers string */
+ cipher_end = cipher_start;
+ while (*cipher_end != '\0' && !is_separator(*cipher_end)) {
+ ++cipher_end;
+ }
+
+ /* IANA cipher names start with the TLS_ or SSL_ prefix.
+ If the 4th symbol of the cipher is '_' we look for a cipher in the
+ table by its (TLS) name.
+ Otherwise, we try to match cipher by an alias. */
+ if(cipher_start[3] == '_') {
+ tls_name = TRUE;
+ }
+ /* Iterate through the cipher table and look for the cipher, starting
+ the cipher number 0x01 because the 0x00 is not the real cipher */
+ cipher_len = cipher_end - cipher_start;
+ for(size_t i = 1; i < NUM_OF_CIPHERS; ++i) {
+ const char *table_cipher_name = NULL;
+ if(tls_name) {
+ table_cipher_name = ciphertable[i].name;
+ }
+ else if(ciphertable[i].alias_name) {
+ table_cipher_name = ciphertable[i].alias_name;
+ }
+ else {
+ continue;
+ }
+ /* Compare a part of the string between separators with a cipher name
+ in the table and make sure we matched the whole cipher name */
+ if(strncmp(cipher_start, table_cipher_name, cipher_len) == 0
+ && table_cipher_name[cipher_len] == '\0') {
+ selected_ciphers[ciphers_count] = ciphertable[i].num;
+ ++ciphers_count;
+ cipher_found = TRUE;
+ break;
+ }
+ }
+ if(!cipher_found) {
+ /* It would be more human-readable if we print the wrong cipher name
+ but we don't want to allocate any additional memory and copy the name
+ into it, then add it into logs.
+ Also, we do not modify an original cipher list string. We just point
+ to positions where cipher starts and ends in the cipher list string.
+ The message is a bit cryptic and longer than necessary but can be
+ understood by humans. */
+ failf(data, "SSL: cipher string \"%s\" contains unsupported cipher name"
+ " starting position %d and ending position %d",
+ ciphers,
+ cipher_start - ciphers,
+ cipher_end - ciphers);
+ return CURLE_SSL_CIPHER;
+ }
+ if(*cipher_end) {
+ cipher_start = cipher_end + 1;
+ }
+ else {
+ break;
+ }
+ }
+ /* All cipher suites in the list are found. Report to logs as-is */
+ infof(data, "SSL: Setting cipher suites list \"%s\"", ciphers);
+
+ err = SSLSetEnabledCiphers(ssl_ctx, selected_ciphers, ciphers_count);
+ if(err != noErr) {
+ failf(data, "SSL: SSLSetEnabledCiphers() failed: OSStatus %d", err);
+ return CURLE_SSL_CIPHER;
+ }
+ return CURLE_OK;
+}
+
+static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ const struct curl_blob *ssl_cablob = conn_config->ca_info_blob;
+ const char * const ssl_cafile =
+ /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
+ (ssl_cablob ? NULL : conn_config->CAfile);
+ const bool verifypeer = conn_config->verifypeer;
+ char * const ssl_cert = ssl_config->primary.clientcert;
+ const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
+ bool isproxy = Curl_ssl_cf_is_proxy(cf);
+#ifdef ENABLE_IPV6
+ struct in6_addr addr;
+#else
+ struct in_addr addr;
+#endif /* ENABLE_IPV6 */
+ char *ciphers;
+ OSStatus err = noErr;
+#if CURL_BUILD_MAC
+ int darwinver_maj = 0, darwinver_min = 0;
+
+ DEBUGASSERT(backend);
+
+ GetDarwinVersionNumber(&darwinver_maj, &darwinver_min);
+#endif /* CURL_BUILD_MAC */
+
+#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
+ if(SSLCreateContext) { /* use the newer API if available */
+ if(backend->ssl_ctx)
+ CFRelease(backend->ssl_ctx);
+ backend->ssl_ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType);
+ if(!backend->ssl_ctx) {
+ failf(data, "SSL: couldn't create a context");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ else {
+ /* The old ST API does not exist under iOS, so don't compile it: */
+#if CURL_SUPPORT_MAC_10_8
+ if(backend->ssl_ctx)
+ (void)SSLDisposeContext(backend->ssl_ctx);
+ err = SSLNewContext(false, &(backend->ssl_ctx));
+ if(err != noErr) {
+ failf(data, "SSL: couldn't create a context: OSStatus %d", err);
+ return CURLE_OUT_OF_MEMORY;
+ }
+#endif /* CURL_SUPPORT_MAC_10_8 */
+ }
+#else
+ if(backend->ssl_ctx)
+ (void)SSLDisposeContext(backend->ssl_ctx);
+ err = SSLNewContext(false, &(backend->ssl_ctx));
+ if(err != noErr) {
+ failf(data, "SSL: couldn't create a context: OSStatus %d", err);
+ return CURLE_OUT_OF_MEMORY;
+ }
+#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
+ backend->ssl_write_buffered_length = 0UL; /* reset buffered write length */
+
+ /* check to see if we've been told to use an explicit SSL/TLS version */
+#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
+ if(SSLSetProtocolVersionMax) {
+ switch(conn_config->version) {
+ case CURL_SSLVERSION_TLSv1:
+ (void)SSLSetProtocolVersionMin(backend->ssl_ctx, kTLSProtocol1);
+#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1
+ if(__builtin_available(macOS 10.13, iOS 11.0, *)) {
+ (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol13);
+ }
+ else {
+ (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol12);
+ }
+#else
+ (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol12);
+#endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) &&
+ HAVE_BUILTIN_AVAILABLE == 1 */
+ break;
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1_0:
+ case CURL_SSLVERSION_TLSv1_1:
+ case CURL_SSLVERSION_TLSv1_2:
+ case CURL_SSLVERSION_TLSv1_3:
+ {
+ CURLcode result = set_ssl_version_min_max(cf, data);
+ if(result != CURLE_OK)
+ return result;
+ break;
+ }
+ case CURL_SSLVERSION_SSLv3:
+ case CURL_SSLVERSION_SSLv2:
+ failf(data, "SSL versions not supported");
+ return CURLE_NOT_BUILT_IN;
+ default:
+ failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ else {
+#if CURL_SUPPORT_MAC_10_8
+ (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kSSLProtocolAll,
+ false);
+ switch(conn_config->version) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+ (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kTLSProtocol1,
+ true);
+ (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kTLSProtocol11,
+ true);
+ (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kTLSProtocol12,
+ true);
+ break;
+ case CURL_SSLVERSION_TLSv1_0:
+ case CURL_SSLVERSION_TLSv1_1:
+ case CURL_SSLVERSION_TLSv1_2:
+ case CURL_SSLVERSION_TLSv1_3:
+ {
+ CURLcode result = set_ssl_version_min_max(cf, data);
+ if(result != CURLE_OK)
+ return result;
+ break;
+ }
+ case CURL_SSLVERSION_SSLv3:
+ case CURL_SSLVERSION_SSLv2:
+ failf(data, "SSL versions not supported");
+ return CURLE_NOT_BUILT_IN;
+ default:
+ failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+#endif /* CURL_SUPPORT_MAC_10_8 */
+ }
+#else
+ if(conn_config->version_max != CURL_SSLVERSION_MAX_NONE) {
+ failf(data, "Your version of the OS does not support to set maximum"
+ " SSL/TLS version");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, kSSLProtocolAll, false);
+ switch(conn_config->version) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+ case CURL_SSLVERSION_TLSv1_0:
+ (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kTLSProtocol1,
+ true);
+ break;
+ case CURL_SSLVERSION_TLSv1_1:
+ failf(data, "Your version of the OS does not support TLSv1.1");
+ return CURLE_SSL_CONNECT_ERROR;
+ case CURL_SSLVERSION_TLSv1_2:
+ failf(data, "Your version of the OS does not support TLSv1.2");
+ return CURLE_SSL_CONNECT_ERROR;
+ case CURL_SSLVERSION_TLSv1_3:
+ failf(data, "Your version of the OS does not support TLSv1.3");
+ return CURLE_SSL_CONNECT_ERROR;
+ case CURL_SSLVERSION_SSLv2:
+ case CURL_SSLVERSION_SSLv3:
+ failf(data, "SSL versions not supported");
+ return CURLE_NOT_BUILT_IN;
+ default:
+ failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
+
+#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1
+ if(cf->conn->bits.tls_enable_alpn) {
+ if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) {
+ CFMutableArrayRef alpnArr = CFArrayCreateMutable(NULL, 0,
+ &kCFTypeArrayCallBacks);
+
+#ifdef USE_HTTP2
+ if(data->state.httpwant >= CURL_HTTP_VERSION_2
+#ifndef CURL_DISABLE_PROXY
+ && (!isproxy || !cf->conn->bits.tunnel_proxy)
+#endif
+ ) {
+ CFArrayAppendValue(alpnArr, CFSTR(ALPN_H2));
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
+ }
+#endif
+
+ CFArrayAppendValue(alpnArr, CFSTR(ALPN_HTTP_1_1));
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
+
+ /* expects length prefixed preference ordered list of protocols in wire
+ * format
+ */
+ err = SSLSetALPNProtocols(backend->ssl_ctx, alpnArr);
+ if(err != noErr)
+ infof(data, "WARNING: failed to set ALPN protocols; OSStatus %d",
+ err);
+ CFRelease(alpnArr);
+ }
+ }
+#endif
+
+ if(ssl_config->key) {
+ infof(data, "WARNING: SSL: CURLOPT_SSLKEY is ignored by Secure "
+ "Transport. The private key must be in the Keychain.");
+ }
+
+ if(ssl_cert || ssl_cert_blob) {
+ bool is_cert_data = ssl_cert_blob != NULL;
+ bool is_cert_file = (!is_cert_data) && is_file(ssl_cert);
+ SecIdentityRef cert_and_key = NULL;
+
+ /* User wants to authenticate with a client cert. Look for it. Assume that
+ the user wants to use an identity loaded from the Keychain. If not, try
+ it as a file on disk */
+
+ if(!is_cert_data)
+ err = CopyIdentityWithLabel(ssl_cert, &cert_and_key);
+ else
+ err = !noErr;
+ if((err != noErr) && (is_cert_file || is_cert_data)) {
+ if(!ssl_config->cert_type)
+ infof(data, "SSL: Certificate type not set, assuming "
+ "PKCS#12 format.");
+ else if(!strcasecompare(ssl_config->cert_type, "P12")) {
+ failf(data, "SSL: The Security framework only supports "
+ "loading identities that are in PKCS#12 format.");
+ return CURLE_SSL_CERTPROBLEM;
+ }
+
+ err = CopyIdentityFromPKCS12File(ssl_cert, ssl_cert_blob,
+ ssl_config->key_passwd,
+ &cert_and_key);
+ }
+
+ if(err == noErr && cert_and_key) {
+ SecCertificateRef cert = NULL;
+ CFTypeRef certs_c[1];
+ CFArrayRef certs;
+
+ /* If we found one, print it out: */
+ err = SecIdentityCopyCertificate(cert_and_key, &cert);
+ if(err == noErr) {
+ char *certp;
+ CURLcode result = CopyCertSubject(data, cert, &certp);
+ if(!result) {
+ infof(data, "Client certificate: %s", certp);
+ free(certp);
+ }
+
+ CFRelease(cert);
+ if(result == CURLE_PEER_FAILED_VERIFICATION)
+ return CURLE_SSL_CERTPROBLEM;
+ if(result)
+ return result;
+ }
+ certs_c[0] = cert_and_key;
+ certs = CFArrayCreate(NULL, (const void **)certs_c, 1L,
+ &kCFTypeArrayCallBacks);
+ err = SSLSetCertificate(backend->ssl_ctx, certs);
+ if(certs)
+ CFRelease(certs);
+ if(err != noErr) {
+ failf(data, "SSL: SSLSetCertificate() failed: OSStatus %d", err);
+ return CURLE_SSL_CERTPROBLEM;
+ }
+ CFRelease(cert_and_key);
+ }
+ else {
+ const char *cert_showfilename_error =
+ is_cert_data ? "(memory blob)" : ssl_cert;
+
+ switch(err) {
+ case errSecAuthFailed: case -25264: /* errSecPkcs12VerifyFailure */
+ failf(data, "SSL: Incorrect password for the certificate \"%s\" "
+ "and its private key.", cert_showfilename_error);
+ break;
+ case -26275: /* errSecDecode */ case -25257: /* errSecUnknownFormat */
+ failf(data, "SSL: Couldn't make sense of the data in the "
+ "certificate \"%s\" and its private key.",
+ cert_showfilename_error);
+ break;
+ case -25260: /* errSecPassphraseRequired */
+ failf(data, "SSL The certificate \"%s\" requires a password.",
+ cert_showfilename_error);
+ break;
+ case errSecItemNotFound:
+ failf(data, "SSL: Can't find the certificate \"%s\" and its private "
+ "key in the Keychain.", cert_showfilename_error);
+ break;
+ default:
+ failf(data, "SSL: Can't load the certificate \"%s\" and its private "
+ "key: OSStatus %d", cert_showfilename_error, err);
+ break;
+ }
+ return CURLE_SSL_CERTPROBLEM;
+ }
+ }
+
+ /* SSL always tries to verify the peer, this only says whether it should
+ * fail to connect if the verification fails, or if it should continue
+ * anyway. In the latter case the result of the verification is checked with
+ * SSL_get_verify_result() below. */
+#if CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS
+ /* Snow Leopard introduced the SSLSetSessionOption() function, but due to
+ a library bug with the way the kSSLSessionOptionBreakOnServerAuth flag
+ works, it doesn't work as expected under Snow Leopard, Lion or
+ Mountain Lion.
+ So we need to call SSLSetEnableCertVerify() on those older cats in order
+ to disable certificate validation if the user turned that off.
+ (SecureTransport will always validate the certificate chain by
+ default.)
+ Note:
+ Darwin 11.x.x is Lion (10.7)
+ Darwin 12.x.x is Mountain Lion (10.8)
+ Darwin 13.x.x is Mavericks (10.9)
+ Darwin 14.x.x is Yosemite (10.10)
+ Darwin 15.x.x is El Capitan (10.11)
+ */
+#if CURL_BUILD_MAC
+ if(SSLSetSessionOption && darwinver_maj >= 13) {
+#else
+ if(SSLSetSessionOption) {
+#endif /* CURL_BUILD_MAC */
+ bool break_on_auth = !conn_config->verifypeer ||
+ ssl_cafile || ssl_cablob;
+ err = SSLSetSessionOption(backend->ssl_ctx,
+ kSSLSessionOptionBreakOnServerAuth,
+ break_on_auth);
+ if(err != noErr) {
+ failf(data, "SSL: SSLSetSessionOption() failed: OSStatus %d", err);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ else {
+#if CURL_SUPPORT_MAC_10_8
+ err = SSLSetEnableCertVerify(backend->ssl_ctx,
+ conn_config->verifypeer?true:false);
+ if(err != noErr) {
+ failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+#endif /* CURL_SUPPORT_MAC_10_8 */
+ }
+#else
+ err = SSLSetEnableCertVerify(backend->ssl_ctx,
+ conn_config->verifypeer?true:false);
+ if(err != noErr) {
+ failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+#endif /* CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS */
+
+ if((ssl_cafile || ssl_cablob) && verifypeer) {
+ bool is_cert_data = ssl_cablob != NULL;
+ bool is_cert_file = (!is_cert_data) && is_file(ssl_cafile);
+
+ if(!(is_cert_file || is_cert_data)) {
+ failf(data, "SSL: can't load CA certificate file %s",
+ ssl_cafile ? ssl_cafile : "(blob memory)");
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ }
+
+ /* Configure hostname check. SNI is used if available.
+ * Both hostname check and SNI require SSLSetPeerDomainName().
+ * Also: the verifyhost setting influences SNI usage */
+ if(conn_config->verifyhost) {
+ size_t snilen;
+ char *snihost = Curl_ssl_snihost(data, connssl->hostname, &snilen);
+ if(!snihost) {
+ failf(data, "Failed to set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ err = SSLSetPeerDomainName(backend->ssl_ctx, snihost, snilen);
+
+ if(err != noErr) {
+ failf(data, "SSL: SSLSetPeerDomainName() failed: OSStatus %d",
+ err);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ if((Curl_inet_pton(AF_INET, connssl->hostname, &addr))
+ #ifdef ENABLE_IPV6
+ || (Curl_inet_pton(AF_INET6, connssl->hostname, &addr))
+ #endif
+ ) {
+ infof(data, "WARNING: using IP address, SNI is being disabled by "
+ "the OS.");
+ }
+ }
+ else {
+ infof(data, "WARNING: disabling hostname validation also disables SNI.");
+ }
+
+ ciphers = conn_config->cipher_list;
+ if(ciphers) {
+ err = sectransp_set_selected_ciphers(data, backend->ssl_ctx, ciphers);
+ }
+ else {
+ err = sectransp_set_default_ciphers(data, backend->ssl_ctx);
+ }
+ if(err != noErr) {
+ failf(data, "SSL: Unable to set ciphers for SSL/TLS handshake. "
+ "Error code: %d", err);
+ return CURLE_SSL_CIPHER;
+ }
+
+#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7
+ /* We want to enable 1/n-1 when using a CBC cipher unless the user
+ specifically doesn't want us doing that: */
+ if(SSLSetSessionOption) {
+ SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionSendOneByteRecord,
+ !ssl_config->enable_beast);
+ SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionFalseStart,
+ ssl_config->falsestart); /* false start support */
+ }
+#endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */
+
+ /* Check if there's a cached ID we can/should use here! */
+ if(ssl_config->primary.sessionid) {
+ char *ssl_sessionid;
+ size_t ssl_sessionid_len;
+
+ Curl_ssl_sessionid_lock(data);
+ if(!Curl_ssl_getsessionid(cf, data, (void **)&ssl_sessionid,
+ &ssl_sessionid_len)) {
+ /* we got a session id, use it! */
+ err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len);
+ Curl_ssl_sessionid_unlock(data);
+ if(err != noErr) {
+ failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ /* Informational message */
+ infof(data, "SSL re-using session ID");
+ }
+ /* If there isn't one, then let's make one up! This has to be done prior
+ to starting the handshake. */
+ else {
+ CURLcode result;
+ ssl_sessionid =
+ aprintf("%s:%d:%d:%s:%d",
+ ssl_cafile ? ssl_cafile : "(blob memory)",
+ verifypeer, conn_config->verifyhost, connssl->hostname,
+ connssl->port);
+ ssl_sessionid_len = strlen(ssl_sessionid);
+
+ err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len);
+ if(err != noErr) {
+ Curl_ssl_sessionid_unlock(data);
+ failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ result = Curl_ssl_addsessionid(cf, data, ssl_sessionid,
+ ssl_sessionid_len, NULL);
+ Curl_ssl_sessionid_unlock(data);
+ if(result) {
+ failf(data, "failed to store ssl session");
+ return result;
+ }
+ }
+ }
+
+ err = SSLSetIOFuncs(backend->ssl_ctx, bio_cf_in_read, bio_cf_out_write);
+ if(err != noErr) {
+ failf(data, "SSL: SSLSetIOFuncs() failed: OSStatus %d", err);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ err = SSLSetConnection(backend->ssl_ctx, cf);
+ if(err != noErr) {
+ failf(data, "SSL: SSLSetConnection() failed: %d", err);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ connssl->connecting_state = ssl_connect_2;
+ return CURLE_OK;
+}
+
+static long pem_to_der(const char *in, unsigned char **out, size_t *outlen)
+{
+ char *sep_start, *sep_end, *cert_start, *cert_end;
+ size_t i, j, err;
+ size_t len;
+ unsigned char *b64;
+
+ /* Jump through the separators at the beginning of the certificate. */
+ sep_start = strstr(in, "-----");
+ if(!sep_start)
+ return 0;
+ cert_start = strstr(sep_start + 1, "-----");
+ if(!cert_start)
+ return -1;
+
+ cert_start += 5;
+
+ /* Find separator after the end of the certificate. */
+ cert_end = strstr(cert_start, "-----");
+ if(!cert_end)
+ return -1;
+
+ sep_end = strstr(cert_end + 1, "-----");
+ if(!sep_end)
+ return -1;
+ sep_end += 5;
+
+ len = cert_end - cert_start;
+ b64 = malloc(len + 1);
+ if(!b64)
+ return -1;
+
+ /* Create base64 string without linefeeds. */
+ for(i = 0, j = 0; i < len; i++) {
+ if(cert_start[i] != '\r' && cert_start[i] != '\n')
+ b64[j++] = cert_start[i];
+ }
+ b64[j] = '\0';
+
+ err = Curl_base64_decode((const char *)b64, out, outlen);
+ free(b64);
+ if(err) {
+ free(*out);
+ return -1;
+ }
+
+ return sep_end - in;
+}
+
+static int read_cert(const char *file, unsigned char **out, size_t *outlen)
+{
+ int fd;
+ ssize_t n, len = 0, cap = 512;
+ unsigned char buf[512], *data;
+
+ fd = open(file, 0);
+ if(fd < 0)
+ return -1;
+
+ data = malloc(cap);
+ if(!data) {
+ close(fd);
+ return -1;
+ }
+
+ for(;;) {
+ n = read(fd, buf, sizeof(buf));
+ if(n < 0) {
+ close(fd);
+ free(data);
+ return -1;
+ }
+ else if(n == 0) {
+ close(fd);
+ break;
+ }
+
+ if(len + n >= cap) {
+ cap *= 2;
+ data = Curl_saferealloc(data, cap);
+ if(!data) {
+ close(fd);
+ return -1;
+ }
+ }
+
+ memcpy(data + len, buf, n);
+ len += n;
+ }
+ data[len] = '\0';
+
+ *out = data;
+ *outlen = len;
+
+ return 0;
+}
+
+static int append_cert_to_array(struct Curl_easy *data,
+ const unsigned char *buf, size_t buflen,
+ CFMutableArrayRef array)
+{
+ CFDataRef certdata = CFDataCreate(kCFAllocatorDefault, buf, buflen);
+ char *certp;
+ CURLcode result;
+ if(!certdata) {
+ failf(data, "SSL: failed to allocate array for CA certificate");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ SecCertificateRef cacert =
+ SecCertificateCreateWithData(kCFAllocatorDefault, certdata);
+ CFRelease(certdata);
+ if(!cacert) {
+ failf(data, "SSL: failed to create SecCertificate from CA certificate");
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+
+ /* Check if cacert is valid. */
+ result = CopyCertSubject(data, cacert, &certp);
+ switch(result) {
+ case CURLE_OK:
+ break;
+ case CURLE_PEER_FAILED_VERIFICATION:
+ return CURLE_SSL_CACERT_BADFILE;
+ case CURLE_OUT_OF_MEMORY:
+ default:
+ return result;
+ }
+ free(certp);
+
+ CFArrayAppendValue(array, cacert);
+ CFRelease(cacert);
+
+ return CURLE_OK;
+}
+
+static CURLcode verify_cert_buf(struct Curl_easy *data,
+ const unsigned char *certbuf, size_t buflen,
+ SSLContextRef ctx)
+{
+ int n = 0, rc;
+ long res;
+ unsigned char *der;
+ size_t derlen, offset = 0;
+
+ /*
+ * Certbuf now contains the contents of the certificate file, which can be
+ * - a single DER certificate,
+ * - a single PEM certificate or
+ * - a bunch of PEM certificates (certificate bundle).
+ *
+ * Go through certbuf, and convert any PEM certificate in it into DER
+ * format.
+ */
+ CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeArrayCallBacks);
+ if(!array) {
+ failf(data, "SSL: out of memory creating CA certificate array");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ while(offset < buflen) {
+ n++;
+
+ /*
+ * Check if the certificate is in PEM format, and convert it to DER. If
+ * this fails, we assume the certificate is in DER format.
+ */
+ res = pem_to_der((const char *)certbuf + offset, &der, &derlen);
+ if(res < 0) {
+ CFRelease(array);
+ failf(data, "SSL: invalid CA certificate #%d (offset %zu) in bundle",
+ n, offset);
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ offset += res;
+
+ if(res == 0 && offset == 0) {
+ /* This is not a PEM file, probably a certificate in DER format. */
+ rc = append_cert_to_array(data, certbuf, buflen, array);
+ if(rc != CURLE_OK) {
+ CFRelease(array);
+ return rc;
+ }
+ break;
+ }
+ else if(res == 0) {
+ /* No more certificates in the bundle. */
+ break;
+ }
+
+ rc = append_cert_to_array(data, der, derlen, array);
+ free(der);
+ if(rc != CURLE_OK) {
+ CFRelease(array);
+ return rc;
+ }
+ }
+
+ SecTrustRef trust;
+ OSStatus ret = SSLCopyPeerTrust(ctx, &trust);
+ if(!trust) {
+ failf(data, "SSL: error getting certificate chain");
+ CFRelease(array);
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else if(ret != noErr) {
+ CFRelease(array);
+ failf(data, "SSLCopyPeerTrust() returned error %d", ret);
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+
+ ret = SecTrustSetAnchorCertificates(trust, array);
+ if(ret != noErr) {
+ CFRelease(array);
+ CFRelease(trust);
+ failf(data, "SecTrustSetAnchorCertificates() returned error %d", ret);
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ ret = SecTrustSetAnchorCertificatesOnly(trust, true);
+ if(ret != noErr) {
+ CFRelease(array);
+ CFRelease(trust);
+ failf(data, "SecTrustSetAnchorCertificatesOnly() returned error %d", ret);
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+
+ SecTrustResultType trust_eval = 0;
+ ret = SecTrustEvaluate(trust, &trust_eval);
+ CFRelease(array);
+ CFRelease(trust);
+ if(ret != noErr) {
+ failf(data, "SecTrustEvaluate() returned error %d", ret);
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+
+ switch(trust_eval) {
+ case kSecTrustResultUnspecified:
+ case kSecTrustResultProceed:
+ return CURLE_OK;
+
+ case kSecTrustResultRecoverableTrustFailure:
+ case kSecTrustResultDeny:
+ default:
+ failf(data, "SSL: certificate verification failed (result: %d)",
+ trust_eval);
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+}
+
+static CURLcode verify_cert(struct Curl_easy *data, const char *cafile,
+ const struct curl_blob *ca_info_blob,
+ SSLContextRef ctx)
+{
+ int result;
+ unsigned char *certbuf;
+ size_t buflen;
+
+ if(ca_info_blob) {
+ certbuf = (unsigned char *)malloc(ca_info_blob->len + 1);
+ if(!certbuf) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+ buflen = ca_info_blob->len;
+ memcpy(certbuf, ca_info_blob->data, ca_info_blob->len);
+ certbuf[ca_info_blob->len]='\0';
+ }
+ else if(cafile) {
+ if(read_cert(cafile, &certbuf, &buflen) < 0) {
+ failf(data, "SSL: failed to read or invalid CA certificate");
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ }
+ else
+ return CURLE_SSL_CACERT_BADFILE;
+
+ result = verify_cert_buf(data, certbuf, buflen, ctx);
+ free(certbuf);
+ return result;
+}
+
+
+#ifdef SECTRANSP_PINNEDPUBKEY
+static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
+ SSLContextRef ctx,
+ const char *pinnedpubkey)
+{ /* Scratch */
+ size_t pubkeylen, realpubkeylen, spkiHeaderLength = 24;
+ unsigned char *pubkey = NULL, *realpubkey = NULL;
+ const unsigned char *spkiHeader = NULL;
+ CFDataRef publicKeyBits = NULL;
+
+ /* Result is returned to caller */
+ CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+
+ /* if a path wasn't specified, don't pin */
+ if(!pinnedpubkey)
+ return CURLE_OK;
+
+
+ if(!ctx)
+ return result;
+
+ do {
+ SecTrustRef trust;
+ OSStatus ret = SSLCopyPeerTrust(ctx, &trust);
+ if(ret != noErr || !trust)
+ break;
+
+ SecKeyRef keyRef = SecTrustCopyPublicKey(trust);
+ CFRelease(trust);
+ if(!keyRef)
+ break;
+
+#ifdef SECTRANSP_PINNEDPUBKEY_V1
+
+ publicKeyBits = SecKeyCopyExternalRepresentation(keyRef, NULL);
+ CFRelease(keyRef);
+ if(!publicKeyBits)
+ break;
+
+#elif SECTRANSP_PINNEDPUBKEY_V2
+
+ OSStatus success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL,
+ &publicKeyBits);
+ CFRelease(keyRef);
+ if(success != errSecSuccess || !publicKeyBits)
+ break;
+
+#endif /* SECTRANSP_PINNEDPUBKEY_V2 */
+
+ pubkeylen = CFDataGetLength(publicKeyBits);
+ pubkey = (unsigned char *)CFDataGetBytePtr(publicKeyBits);
+
+ switch(pubkeylen) {
+ case 526:
+ /* 4096 bit RSA pubkeylen == 526 */
+ spkiHeader = rsa4096SpkiHeader;
+ break;
+ case 270:
+ /* 2048 bit RSA pubkeylen == 270 */
+ spkiHeader = rsa2048SpkiHeader;
+ break;
+#ifdef SECTRANSP_PINNEDPUBKEY_V1
+ case 65:
+ /* ecDSA secp256r1 pubkeylen == 65 */
+ spkiHeader = ecDsaSecp256r1SpkiHeader;
+ spkiHeaderLength = 26;
+ break;
+ case 97:
+ /* ecDSA secp384r1 pubkeylen == 97 */
+ spkiHeader = ecDsaSecp384r1SpkiHeader;
+ spkiHeaderLength = 23;
+ break;
+ default:
+ infof(data, "SSL: unhandled public key length: %d", pubkeylen);
+#elif SECTRANSP_PINNEDPUBKEY_V2
+ default:
+ /* ecDSA secp256r1 pubkeylen == 91 header already included?
+ * ecDSA secp384r1 header already included too
+ * we assume rest of algorithms do same, so do nothing
+ */
+ result = Curl_pin_peer_pubkey(data, pinnedpubkey, pubkey,
+ pubkeylen);
+#endif /* SECTRANSP_PINNEDPUBKEY_V2 */
+ continue; /* break from loop */
+ }
+
+ realpubkeylen = pubkeylen + spkiHeaderLength;
+ realpubkey = malloc(realpubkeylen);
+ if(!realpubkey)
+ break;
+
+ memcpy(realpubkey, spkiHeader, spkiHeaderLength);
+ memcpy(realpubkey + spkiHeaderLength, pubkey, pubkeylen);
+
+ result = Curl_pin_peer_pubkey(data, pinnedpubkey, realpubkey,
+ realpubkeylen);
+
+ } while(0);
+
+ Curl_safefree(realpubkey);
+ if(publicKeyBits)
+ CFRelease(publicKeyBits);
+
+ return result;
+}
+#endif /* SECTRANSP_PINNEDPUBKEY */
+
+static CURLcode sectransp_connect_step2(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ OSStatus err;
+ SSLCipherSuite cipher;
+ SSLProtocol protocol = 0;
+
+ DEBUGASSERT(ssl_connect_2 == connssl->connecting_state
+ || ssl_connect_2_reading == connssl->connecting_state
+ || ssl_connect_2_writing == connssl->connecting_state);
+ DEBUGASSERT(backend);
+
+ /* Here goes nothing: */
+ err = SSLHandshake(backend->ssl_ctx);
+
+ if(err != noErr) {
+ switch(err) {
+ case errSSLWouldBlock: /* they're not done with us yet */
+ connssl->connecting_state = backend->ssl_direction ?
+ ssl_connect_2_writing : ssl_connect_2_reading;
+ return CURLE_OK;
+
+ /* The below is errSSLServerAuthCompleted; it's not defined in
+ Leopard's headers */
+ case -9841:
+ if((conn_config->CAfile || conn_config->ca_info_blob) &&
+ conn_config->verifypeer) {
+ CURLcode result = verify_cert(data, conn_config->CAfile,
+ conn_config->ca_info_blob,
+ backend->ssl_ctx);
+ if(result)
+ return result;
+ }
+ /* the documentation says we need to call SSLHandshake() again */
+ return sectransp_connect_step2(cf, data);
+
+ /* Problem with encrypt / decrypt */
+ case errSSLPeerDecodeError:
+ failf(data, "Decode failed");
+ break;
+ case errSSLDecryptionFail:
+ case errSSLPeerDecryptionFail:
+ failf(data, "Decryption failed");
+ break;
+ case errSSLPeerDecryptError:
+ failf(data, "A decryption error occurred");
+ break;
+ case errSSLBadCipherSuite:
+ failf(data, "A bad SSL cipher suite was encountered");
+ break;
+ case errSSLCrypto:
+ failf(data, "An underlying cryptographic error was encountered");
+ break;
+#if CURL_BUILD_MAC_10_11 || CURL_BUILD_IOS_9
+ case errSSLWeakPeerEphemeralDHKey:
+ failf(data, "Indicates a weak ephemeral Diffie-Hellman key");
+ break;
+#endif
+
+ /* Problem with the message record validation */
+ case errSSLBadRecordMac:
+ case errSSLPeerBadRecordMac:
+ failf(data, "A record with a bad message authentication code (MAC) "
+ "was encountered");
+ break;
+ case errSSLRecordOverflow:
+ case errSSLPeerRecordOverflow:
+ failf(data, "A record overflow occurred");
+ break;
+
+ /* Problem with zlib decompression */
+ case errSSLPeerDecompressFail:
+ failf(data, "Decompression failed");
+ break;
+
+ /* Problem with access */
+ case errSSLPeerAccessDenied:
+ failf(data, "Access was denied");
+ break;
+ case errSSLPeerInsufficientSecurity:
+ failf(data, "There is insufficient security for this operation");
+ break;
+
+ /* These are all certificate problems with the server: */
+ case errSSLXCertChainInvalid:
+ failf(data, "SSL certificate problem: Invalid certificate chain");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case errSSLUnknownRootCert:
+ failf(data, "SSL certificate problem: Untrusted root certificate");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case errSSLNoRootCert:
+ failf(data, "SSL certificate problem: No root certificate");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case errSSLCertNotYetValid:
+ failf(data, "SSL certificate problem: The certificate chain had a "
+ "certificate that is not yet valid");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case errSSLCertExpired:
+ case errSSLPeerCertExpired:
+ failf(data, "SSL certificate problem: Certificate chain had an "
+ "expired certificate");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case errSSLBadCert:
+ case errSSLPeerBadCert:
+ failf(data, "SSL certificate problem: Couldn't understand the server "
+ "certificate format");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case errSSLPeerUnsupportedCert:
+ failf(data, "SSL certificate problem: An unsupported certificate "
+ "format was encountered");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case errSSLPeerCertRevoked:
+ failf(data, "SSL certificate problem: The certificate was revoked");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case errSSLPeerCertUnknown:
+ failf(data, "SSL certificate problem: The certificate is unknown");
+ return CURLE_PEER_FAILED_VERIFICATION;
+
+ /* These are all certificate problems with the client: */
+ case errSecAuthFailed:
+ failf(data, "SSL authentication failed");
+ break;
+ case errSSLPeerHandshakeFail:
+ failf(data, "SSL peer handshake failed, the server most likely "
+ "requires a client certificate to connect");
+ break;
+ case errSSLPeerUnknownCA:
+ failf(data, "SSL server rejected the client certificate due to "
+ "the certificate being signed by an unknown certificate "
+ "authority");
+ break;
+
+ /* This error is raised if the server's cert didn't match the server's
+ host name: */
+ case errSSLHostNameMismatch:
+ failf(data, "SSL certificate peer verification failed, the "
+ "certificate did not match \"%s\"\n", connssl->dispname);
+ return CURLE_PEER_FAILED_VERIFICATION;
+
+ /* Problem with SSL / TLS negotiation */
+ case errSSLNegotiation:
+ failf(data, "Could not negotiate an SSL cipher suite with the server");
+ break;
+ case errSSLBadConfiguration:
+ failf(data, "A configuration error occurred");
+ break;
+ case errSSLProtocol:
+ failf(data, "SSL protocol error");
+ break;
+ case errSSLPeerProtocolVersion:
+ failf(data, "A bad protocol version was encountered");
+ break;
+ case errSSLPeerNoRenegotiation:
+ failf(data, "No renegotiation is allowed");
+ break;
+
+ /* Generic handshake errors: */
+ case errSSLConnectionRefused:
+ failf(data, "Server dropped the connection during the SSL handshake");
+ break;
+ case errSSLClosedAbort:
+ failf(data, "Server aborted the SSL handshake");
+ break;
+ case errSSLClosedGraceful:
+ failf(data, "The connection closed gracefully");
+ break;
+ case errSSLClosedNoNotify:
+ failf(data, "The server closed the session with no notification");
+ break;
+ /* Sometimes paramErr happens with buggy ciphers: */
+ case paramErr:
+ case errSSLInternal:
+ case errSSLPeerInternalError:
+ failf(data, "Internal SSL engine error encountered during the "
+ "SSL handshake");
+ break;
+ case errSSLFatalAlert:
+ failf(data, "Fatal SSL engine error encountered during the SSL "
+ "handshake");
+ break;
+ /* Unclassified error */
+ case errSSLBufferOverflow:
+ failf(data, "An insufficient buffer was provided");
+ break;
+ case errSSLIllegalParam:
+ failf(data, "An illegal parameter was encountered");
+ break;
+ case errSSLModuleAttach:
+ failf(data, "Module attach failure");
+ break;
+ case errSSLSessionNotFound:
+ failf(data, "An attempt to restore an unknown session failed");
+ break;
+ case errSSLPeerExportRestriction:
+ failf(data, "An export restriction occurred");
+ break;
+ case errSSLPeerUserCancelled:
+ failf(data, "The user canceled the operation");
+ break;
+ case errSSLPeerUnexpectedMsg:
+ failf(data, "Peer rejected unexpected message");
+ break;
+#if CURL_BUILD_MAC_10_11 || CURL_BUILD_IOS_9
+ /* Treaing non-fatal error as fatal like before */
+ case errSSLClientHelloReceived:
+ failf(data, "A non-fatal result for providing a server name "
+ "indication");
+ break;
+#endif
+
+ /* Error codes defined in the enum but should never be returned.
+ We list them here just in case. */
+#if CURL_BUILD_MAC_10_6
+ /* Only returned when kSSLSessionOptionBreakOnCertRequested is set */
+ case errSSLClientCertRequested:
+ failf(data, "Server requested a client certificate during the "
+ "handshake");
+ return CURLE_SSL_CLIENTCERT;
+#endif
+#if CURL_BUILD_MAC_10_9
+ /* Alias for errSSLLast, end of error range */
+ case errSSLUnexpectedRecord:
+ failf(data, "Unexpected (skipped) record in DTLS");
+ break;
+#endif
+ default:
+ /* May also return codes listed in Security Framework Result Codes */
+ failf(data, "Unknown SSL protocol error in connection to %s:%d",
+ connssl->hostname, err);
+ break;
+ }
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else {
+ /* we have been connected fine, we're not waiting for anything else. */
+ connssl->connecting_state = ssl_connect_3;
+
+#ifdef SECTRANSP_PINNEDPUBKEY
+ if(data->set.str[STRING_SSL_PINNEDPUBLICKEY]) {
+ CURLcode result =
+ pkp_pin_peer_pubkey(data, backend->ssl_ctx,
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
+ if(result) {
+ failf(data, "SSL: public key does not match pinned public key");
+ return result;
+ }
+ }
+#endif /* SECTRANSP_PINNEDPUBKEY */
+
+ /* Informational message */
+ (void)SSLGetNegotiatedCipher(backend->ssl_ctx, &cipher);
+ (void)SSLGetNegotiatedProtocolVersion(backend->ssl_ctx, &protocol);
+ switch(protocol) {
+ case kSSLProtocol2:
+ infof(data, "SSL 2.0 connection using %s",
+ TLSCipherNameForNumber(cipher));
+ break;
+ case kSSLProtocol3:
+ infof(data, "SSL 3.0 connection using %s",
+ TLSCipherNameForNumber(cipher));
+ break;
+ case kTLSProtocol1:
+ infof(data, "TLS 1.0 connection using %s",
+ TLSCipherNameForNumber(cipher));
+ break;
+#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
+ case kTLSProtocol11:
+ infof(data, "TLS 1.1 connection using %s",
+ TLSCipherNameForNumber(cipher));
+ break;
+ case kTLSProtocol12:
+ infof(data, "TLS 1.2 connection using %s",
+ TLSCipherNameForNumber(cipher));
+ break;
+#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
+#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11
+ case kTLSProtocol13:
+ infof(data, "TLS 1.3 connection using %s",
+ TLSCipherNameForNumber(cipher));
+ break;
+#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */
+ default:
+ infof(data, "Unknown protocol connection");
+ break;
+ }
+
+#if(CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1
+ if(cf->conn->bits.tls_enable_alpn) {
+ if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) {
+ CFArrayRef alpnArr = NULL;
+ CFStringRef chosenProtocol = NULL;
+ err = SSLCopyALPNProtocols(backend->ssl_ctx, &alpnArr);
+
+ if(err == noErr && alpnArr && CFArrayGetCount(alpnArr) >= 1)
+ chosenProtocol = CFArrayGetValueAtIndex(alpnArr, 0);
+
+#ifdef USE_HTTP2
+ if(chosenProtocol &&
+ !CFStringCompare(chosenProtocol, CFSTR(ALPN_H2), 0)) {
+ cf->conn->alpn = CURL_HTTP_VERSION_2;
+ }
+ else
+#endif
+ if(chosenProtocol &&
+ !CFStringCompare(chosenProtocol, CFSTR(ALPN_HTTP_1_1), 0)) {
+ cf->conn->alpn = CURL_HTTP_VERSION_1_1;
+ }
+ else
+ infof(data, VTLS_INFOF_NO_ALPN);
+
+ Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+
+ /* chosenProtocol is a reference to the string within alpnArr
+ and doesn't need to be freed separately */
+ if(alpnArr)
+ CFRelease(alpnArr);
+ }
+ }
+#endif
+
+ return CURLE_OK;
+ }
+}
+
+static CURLcode
+add_cert_to_certinfo(struct Curl_easy *data,
+ SecCertificateRef server_cert,
+ int idx)
+{
+ CURLcode result = CURLE_OK;
+ const char *beg;
+ const char *end;
+ CFDataRef cert_data = SecCertificateCopyData(server_cert);
+
+ if(!cert_data)
+ return CURLE_PEER_FAILED_VERIFICATION;
+
+ beg = (const char *)CFDataGetBytePtr(cert_data);
+ end = beg + CFDataGetLength(cert_data);
+ result = Curl_extract_certinfo(data, idx, beg, end);
+ CFRelease(cert_data);
+ return result;
+}
+
+static CURLcode
+collect_server_cert_single(struct Curl_cfilter *cf, struct Curl_easy *data,
+ SecCertificateRef server_cert,
+ CFIndex idx)
+{
+ CURLcode result = CURLE_OK;
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ if(data->set.verbose) {
+ char *certp;
+ result = CopyCertSubject(data, server_cert, &certp);
+ if(!result) {
+ infof(data, "Server certificate: %s", certp);
+ free(certp);
+ }
+ }
+#endif
+ if(ssl_config->certinfo)
+ result = add_cert_to_certinfo(data, server_cert, (int)idx);
+ return result;
+}
+
+/* This should be called during step3 of the connection at the earliest */
+static CURLcode collect_server_cert(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ const bool show_verbose_server_cert = data->set.verbose;
+#else
+ const bool show_verbose_server_cert = false;
+#endif
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ CURLcode result = ssl_config->certinfo ?
+ CURLE_PEER_FAILED_VERIFICATION : CURLE_OK;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ CFArrayRef server_certs = NULL;
+ SecCertificateRef server_cert;
+ OSStatus err;
+ CFIndex i, count;
+ SecTrustRef trust = NULL;
+
+ DEBUGASSERT(backend);
+
+ if(!show_verbose_server_cert && !ssl_config->certinfo)
+ return CURLE_OK;
+
+ if(!backend->ssl_ctx)
+ return result;
+
+#if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS
+#if CURL_BUILD_IOS
+#pragma unused(server_certs)
+ err = SSLCopyPeerTrust(backend->ssl_ctx, &trust);
+ /* For some reason, SSLCopyPeerTrust() can return noErr and yet return
+ a null trust, so be on guard for that: */
+ if(err == noErr && trust) {
+ count = SecTrustGetCertificateCount(trust);
+ if(ssl_config->certinfo)
+ result = Curl_ssl_init_certinfo(data, (int)count);
+ for(i = 0L ; !result && (i < count) ; i++) {
+ server_cert = SecTrustGetCertificateAtIndex(trust, i);
+ result = collect_server_cert_single(cf, data, server_cert, i);
+ }
+ CFRelease(trust);
+ }
+#else
+ /* SSLCopyPeerCertificates() is deprecated as of Mountain Lion.
+ The function SecTrustGetCertificateAtIndex() is officially present
+ in Lion, but it is unfortunately also present in Snow Leopard as
+ private API and doesn't work as expected. So we have to look for
+ a different symbol to make sure this code is only executed under
+ Lion or later. */
+ if(SecTrustCopyPublicKey) {
+#pragma unused(server_certs)
+ err = SSLCopyPeerTrust(backend->ssl_ctx, &trust);
+ /* For some reason, SSLCopyPeerTrust() can return noErr and yet return
+ a null trust, so be on guard for that: */
+ if(err == noErr && trust) {
+ count = SecTrustGetCertificateCount(trust);
+ if(ssl_config->certinfo)
+ result = Curl_ssl_init_certinfo(data, (int)count);
+ for(i = 0L ; !result && (i < count) ; i++) {
+ server_cert = SecTrustGetCertificateAtIndex(trust, i);
+ result = collect_server_cert_single(cf, data, server_cert, i);
+ }
+ CFRelease(trust);
+ }
+ }
+ else {
+#if CURL_SUPPORT_MAC_10_8
+ err = SSLCopyPeerCertificates(backend->ssl_ctx, &server_certs);
+ /* Just in case SSLCopyPeerCertificates() returns null too... */
+ if(err == noErr && server_certs) {
+ count = CFArrayGetCount(server_certs);
+ if(ssl_config->certinfo)
+ result = Curl_ssl_init_certinfo(data, (int)count);
+ for(i = 0L ; !result && (i < count) ; i++) {
+ server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs,
+ i);
+ result = collect_server_cert_single(cf, data, server_cert, i);
+ }
+ CFRelease(server_certs);
+ }
+#endif /* CURL_SUPPORT_MAC_10_8 */
+ }
+#endif /* CURL_BUILD_IOS */
+#else
+#pragma unused(trust)
+ err = SSLCopyPeerCertificates(backend->ssl_ctx, &server_certs);
+ if(err == noErr) {
+ count = CFArrayGetCount(server_certs);
+ if(ssl_config->certinfo)
+ result = Curl_ssl_init_certinfo(data, (int)count);
+ for(i = 0L ; !result && (i < count) ; i++) {
+ server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, i);
+ result = collect_server_cert_single(cf, data, server_cert, i);
+ }
+ CFRelease(server_certs);
+ }
+#endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */
+ return result;
+}
+
+static CURLcode sectransp_connect_step3(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+
+ /* There is no step 3!
+ * Well, okay, let's collect server certificates, and if verbose mode is on,
+ * let's print the details of the server certificates. */
+ const CURLcode result = collect_server_cert(cf, data);
+ if(result)
+ return result;
+
+ connssl->connecting_state = ssl_connect_done;
+ return CURLE_OK;
+}
+
+static CURLcode
+sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
+ bool nonblocking,
+ bool *done)
+{
+ CURLcode result;
+ struct ssl_connect_data *connssl = cf->ctx;
+ curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ int what;
+
+ /* check if the connection has already been established */
+ if(ssl_connection_complete == connssl->state) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ if(ssl_connect_1 == connssl->connecting_state) {
+ /* Find out how much more time we're allowed */
+ const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+ if(timeout_ms < 0) {
+ /* no need to continue if time already is up */
+ failf(data, "SSL connection timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
+ result = sectransp_connect_step1(cf, data);
+ if(result)
+ return result;
+ }
+
+ while(ssl_connect_2 == connssl->connecting_state ||
+ ssl_connect_2_reading == connssl->connecting_state ||
+ ssl_connect_2_writing == connssl->connecting_state) {
+
+ /* check allowed time left */
+ const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+ if(timeout_ms < 0) {
+ /* no need to continue if time already is up */
+ failf(data, "SSL connection timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
+ /* if ssl is expecting something, check if it's available. */
+ if(connssl->connecting_state == ssl_connect_2_reading ||
+ connssl->connecting_state == ssl_connect_2_writing) {
+
+ curl_socket_t writefd = ssl_connect_2_writing ==
+ connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+ curl_socket_t readfd = ssl_connect_2_reading ==
+ connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+
+ what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
+ nonblocking ? 0 : timeout_ms);
+ if(what < 0) {
+ /* fatal error */
+ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else if(0 == what) {
+ if(nonblocking) {
+ *done = FALSE;
+ return CURLE_OK;
+ }
+ else {
+ /* timeout */
+ failf(data, "SSL connection timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+ }
+ /* socket is readable or writable */
+ }
+
+ /* Run transaction, and return to the caller if it failed or if this
+ * connection is done nonblocking and this loop would execute again. This
+ * permits the owner of a multi handle to abort a connection attempt
+ * before step2 has completed while ensuring that a client using select()
+ * or epoll() will always have a valid fdset to wait on.
+ */
+ result = sectransp_connect_step2(cf, data);
+ if(result || (nonblocking &&
+ (ssl_connect_2 == connssl->connecting_state ||
+ ssl_connect_2_reading == connssl->connecting_state ||
+ ssl_connect_2_writing == connssl->connecting_state)))
+ return result;
+
+ } /* repeat step2 until all transactions are done. */
+
+
+ if(ssl_connect_3 == connssl->connecting_state) {
+ result = sectransp_connect_step3(cf, data);
+ if(result)
+ return result;
+ }
+
+ if(ssl_connect_done == connssl->connecting_state) {
+ connssl->state = ssl_connection_complete;
+ *done = TRUE;
+ }
+ else
+ *done = FALSE;
+
+ /* Reset our connect state machine */
+ connssl->connecting_state = ssl_connect_1;
+
+ return CURLE_OK;
+}
+
+static CURLcode sectransp_connect_nonblocking(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
+{
+ return sectransp_connect_common(cf, data, TRUE, done);
+}
+
+static CURLcode sectransp_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ CURLcode result;
+ bool done = FALSE;
+
+ result = sectransp_connect_common(cf, data, FALSE, &done);
+
+ if(result)
+ return result;
+
+ DEBUGASSERT(done);
+
+ return CURLE_OK;
+}
+
+static void sectransp_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+
+ (void) data;
+
+ DEBUGASSERT(backend);
+
+ if(backend->ssl_ctx) {
+ (void)SSLClose(backend->ssl_ctx);
+#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
+ if(SSLCreateContext)
+ CFRelease(backend->ssl_ctx);
+#if CURL_SUPPORT_MAC_10_8
+ else
+ (void)SSLDisposeContext(backend->ssl_ctx);
+#endif /* CURL_SUPPORT_MAC_10_8 */
+#else
+ (void)SSLDisposeContext(backend->ssl_ctx);
+#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
+ backend->ssl_ctx = NULL;
+ }
+}
+
+static int sectransp_shutdown(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ ssize_t nread;
+ int what;
+ int rc;
+ char buf[120];
+ int loop = 10; /* avoid getting stuck */
+ CURLcode result;
+
+ DEBUGASSERT(backend);
+
+ if(!backend->ssl_ctx)
+ return 0;
+
+#ifndef CURL_DISABLE_FTP
+ if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE)
+ return 0;
+#endif
+
+ sectransp_close(cf, data);
+
+ rc = 0;
+
+ what = SOCKET_READABLE(cf->conn->sock[cf->sockindex], SSL_SHUTDOWN_TIMEOUT);
+
+ while(loop--) {
+ if(what < 0) {
+ /* anything that gets here is fatally bad */
+ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+ rc = -1;
+ break;
+ }
+
+ if(!what) { /* timeout */
+ failf(data, "SSL shutdown timeout");
+ break;
+ }
+
+ /* Something to read, let's do it and hope that it is the close
+ notify alert from the server. No way to SSL_Read now, so use read(). */
+
+ nread = Curl_conn_cf_recv(cf->next, data, buf, sizeof(buf), &result);
+
+ if(nread < 0) {
+ failf(data, "read: %s", curl_easy_strerror(result));
+ rc = -1;
+ }
+
+ if(nread <= 0)
+ break;
+
+ what = SOCKET_READABLE(cf->conn->sock[cf->sockindex], 0);
+ }
+
+ return rc;
+}
+
+static void sectransp_session_free(void *ptr)
+{
+ /* ST, as of iOS 5 and Mountain Lion, has no public method of deleting a
+ cached session ID inside the Security framework. There is a private
+ function that does this, but I don't want to have to explain to you why I
+ got your application rejected from the App Store due to the use of a
+ private API, so the best we can do is free up our own char array that we
+ created way back in sectransp_connect_step1... */
+ Curl_safefree(ptr);
+}
+
+static size_t sectransp_version(char *buffer, size_t size)
+{
+ return msnprintf(buffer, size, "SecureTransport");
+}
+
+/*
+ * This function uses SSLGetSessionState to determine connection status.
+ *
+ * Return codes:
+ * 1 means the connection is still in place
+ * 0 means the connection has been closed
+ * -1 means the connection status is unknown
+ */
+static int sectransp_check_cxn(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ OSStatus err;
+ SSLSessionState state;
+
+ (void)data;
+ DEBUGASSERT(backend);
+
+ if(backend->ssl_ctx) {
+ err = SSLGetSessionState(backend->ssl_ctx, &state);
+ if(err == noErr)
+ return state == kSSLConnected || state == kSSLHandshake;
+ return -1;
+ }
+ return 0;
+}
+
+static bool sectransp_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ const struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ OSStatus err;
+ size_t buffer;
+
+ (void)data;
+ DEBUGASSERT(backend);
+
+ if(backend->ssl_ctx) { /* SSL is in use */
+ err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer);
+ if(err == noErr)
+ return buffer > 0UL;
+ return false;
+ }
+ else
+ return false;
+}
+
+static CURLcode sectransp_random(struct Curl_easy *data UNUSED_PARAM,
+ unsigned char *entropy, size_t length)
+{
+ /* arc4random_buf() isn't available on cats older than Lion, so let's
+ do this manually for the benefit of the older cats. */
+ size_t i;
+ u_int32_t random_number = 0;
+
+ (void)data;
+
+ for(i = 0 ; i < length ; i++) {
+ if(i % sizeof(u_int32_t) == 0)
+ random_number = arc4random();
+ entropy[i] = random_number & 0xFF;
+ random_number >>= 8;
+ }
+ i = random_number = 0;
+ return CURLE_OK;
+}
+
+static CURLcode sectransp_sha256sum(const unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *sha256sum, /* output */
+ size_t sha256len)
+{
+ assert(sha256len >= CURL_SHA256_DIGEST_LENGTH);
+ (void)CC_SHA256(tmp, (CC_LONG)tmplen, sha256sum);
+ return CURLE_OK;
+}
+
+static bool sectransp_false_start(void)
+{
+#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7
+ if(SSLSetSessionOption)
+ return TRUE;
+#endif
+ return FALSE;
+}
+
+static ssize_t sectransp_send(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *mem,
+ size_t len,
+ CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ size_t processed = 0UL;
+ OSStatus err;
+
+ DEBUGASSERT(backend);
+
+ /* The SSLWrite() function works a little differently than expected. The
+ fourth argument (processed) is currently documented in Apple's
+ documentation as: "On return, the length, in bytes, of the data actually
+ written."
+
+ Now, one could interpret that as "written to the socket," but actually,
+ it returns the amount of data that was written to a buffer internal to
+ the SSLContextRef instead. So it's possible for SSLWrite() to return
+ errSSLWouldBlock and a number of bytes "written" because those bytes were
+ encrypted and written to a buffer, not to the socket.
+
+ So if this happens, then we need to keep calling SSLWrite() over and
+ over again with no new data until it quits returning errSSLWouldBlock. */
+
+ /* Do we have buffered data to write from the last time we were called? */
+ if(backend->ssl_write_buffered_length) {
+ /* Write the buffered data: */
+ err = SSLWrite(backend->ssl_ctx, NULL, 0UL, &processed);
+ switch(err) {
+ case noErr:
+ /* processed is always going to be 0 because we didn't write to
+ the buffer, so return how much was written to the socket */
+ processed = backend->ssl_write_buffered_length;
+ backend->ssl_write_buffered_length = 0UL;
+ break;
+ case errSSLWouldBlock: /* argh, try again */
+ *curlcode = CURLE_AGAIN;
+ return -1L;
+ default:
+ failf(data, "SSLWrite() returned error %d", err);
+ *curlcode = CURLE_SEND_ERROR;
+ return -1L;
+ }
+ }
+ else {
+ /* We've got new data to write: */
+ err = SSLWrite(backend->ssl_ctx, mem, len, &processed);
+ if(err != noErr) {
+ switch(err) {
+ case errSSLWouldBlock:
+ /* Data was buffered but not sent, we have to tell the caller
+ to try sending again, and remember how much was buffered */
+ backend->ssl_write_buffered_length = len;
+ *curlcode = CURLE_AGAIN;
+ return -1L;
+ default:
+ failf(data, "SSLWrite() returned error %d", err);
+ *curlcode = CURLE_SEND_ERROR;
+ return -1L;
+ }
+ }
+ }
+ return (ssize_t)processed;
+}
+
+static ssize_t sectransp_recv(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ char *buf,
+ size_t buffersize,
+ CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ size_t processed = 0UL;
+ OSStatus err;
+
+ DEBUGASSERT(backend);
+
+ again:
+ err = SSLRead(backend->ssl_ctx, buf, buffersize, &processed);
+
+ if(err != noErr) {
+ switch(err) {
+ case errSSLWouldBlock: /* return how much we read (if anything) */
+ if(processed)
+ return (ssize_t)processed;
+ *curlcode = CURLE_AGAIN;
+ return -1L;
+ break;
+
+ /* errSSLClosedGraceful - server gracefully shut down the SSL session
+ errSSLClosedNoNotify - server hung up on us instead of sending a
+ closure alert notice, read() is returning 0
+ Either way, inform the caller that the server disconnected. */
+ case errSSLClosedGraceful:
+ case errSSLClosedNoNotify:
+ *curlcode = CURLE_OK;
+ return -1L;
+ break;
+
+ /* The below is errSSLPeerAuthCompleted; it's not defined in
+ Leopard's headers */
+ case -9841:
+ if((conn_config->CAfile || conn_config->ca_info_blob) &&
+ conn_config->verifypeer) {
+ CURLcode result = verify_cert(data, conn_config->CAfile,
+ conn_config->ca_info_blob,
+ backend->ssl_ctx);
+ if(result)
+ return result;
+ }
+ goto again;
+ default:
+ failf(data, "SSLRead() return error %d", err);
+ *curlcode = CURLE_RECV_ERROR;
+ return -1L;
+ break;
+ }
+ }
+ return (ssize_t)processed;
+}
+
+static void *sectransp_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
+{
+ struct ssl_backend_data *backend = connssl->backend;
+ (void)info;
+ DEBUGASSERT(backend);
+ return backend->ssl_ctx;
+}
+
+const struct Curl_ssl Curl_ssl_sectransp = {
+ { CURLSSLBACKEND_SECURETRANSPORT, "secure-transport" }, /* info */
+
+ SSLSUPP_CAINFO_BLOB |
+ SSLSUPP_CERTINFO |
+#ifdef SECTRANSP_PINNEDPUBKEY
+ SSLSUPP_PINNEDPUBKEY |
+#endif /* SECTRANSP_PINNEDPUBKEY */
+ SSLSUPP_HTTPS_PROXY,
+
+ sizeof(struct ssl_backend_data),
+
+ Curl_none_init, /* init */
+ Curl_none_cleanup, /* cleanup */
+ sectransp_version, /* version */
+ sectransp_check_cxn, /* check_cxn */
+ sectransp_shutdown, /* shutdown */
+ sectransp_data_pending, /* data_pending */
+ sectransp_random, /* random */
+ Curl_none_cert_status_request, /* cert_status_request */
+ sectransp_connect, /* connect */
+ sectransp_connect_nonblocking, /* connect_nonblocking */
+ Curl_ssl_get_select_socks, /* getsock */
+ sectransp_get_internals, /* get_internals */
+ sectransp_close, /* close_one */
+ Curl_none_close_all, /* close_all */
+ sectransp_session_free, /* session_free */
+ Curl_none_set_engine, /* set_engine */
+ Curl_none_set_engine_default, /* set_engine_default */
+ Curl_none_engines_list, /* engines_list */
+ sectransp_false_start, /* false_start */
+ sectransp_sha256sum, /* sha256sum */
+ NULL, /* associate_connection */
+ NULL, /* disassociate_connection */
+ NULL, /* free_multi_ssl_backend_data */
+ sectransp_recv, /* recv decrypted data */
+ sectransp_send, /* send data to encrypt */
+};
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#endif /* USE_SECTRANSP */
diff --git a/libs/libcurl/src/vtls/sectransp.h b/libs/libcurl/src/vtls/sectransp.h index 2d53b7c480..caa6374065 100644 --- a/libs/libcurl/src/vtls/sectransp.h +++ b/libs/libcurl/src/vtls/sectransp.h @@ -1,34 +1,34 @@ -#ifndef HEADER_CURL_SECTRANSP_H -#define HEADER_CURL_SECTRANSP_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2012 - 2014, Nick Zitzmann, <nickzman@gmail.com>. - * Copyright (C) 2012 - 2022, 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_SECTRANSP - -extern const struct Curl_ssl Curl_ssl_sectransp; - -#endif /* USE_SECTRANSP */ -#endif /* HEADER_CURL_SECTRANSP_H */ +#ifndef HEADER_CURL_SECTRANSP_H
+#define HEADER_CURL_SECTRANSP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012 - 2014, Nick Zitzmann, <nickzman@gmail.com>.
+ * Copyright (C) 2012 - 2022, 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_SECTRANSP
+
+extern const struct Curl_ssl Curl_ssl_sectransp;
+
+#endif /* USE_SECTRANSP */
+#endif /* HEADER_CURL_SECTRANSP_H */
diff --git a/libs/libcurl/src/vtls/vtls.c b/libs/libcurl/src/vtls/vtls.c index 9dee5aa3b3..6cd7b12983 100644 --- a/libs/libcurl/src/vtls/vtls.c +++ b/libs/libcurl/src/vtls/vtls.c @@ -1,1500 +1,1823 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 for implementing all "generic" SSL functions that all libcurl - internals should use. It is then responsible for calling the proper - "backend" function. - - SSL-functions in libcurl should call functions in this source file, and not - to any specific SSL-layer. - - Curl_ssl_ - prefix for generic ones - - Note that this source code uses the functions of the configured SSL - backend via the global Curl_ssl instance. - - "SSL/TLS Strong Encryption: An Introduction" - https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html -*/ - -#include "curl_setup.h" - -#ifdef HAVE_SYS_TYPES_H -#include <sys/types.h> -#endif -#ifdef HAVE_SYS_STAT_H -#include <sys/stat.h> -#endif -#ifdef HAVE_FCNTL_H -#include <fcntl.h> -#endif - -#include "urldata.h" - -#include "vtls.h" /* generic SSL protos etc */ -#include "slist.h" -#include "sendf.h" -#include "strcase.h" -#include "url.h" -#include "progress.h" -#include "share.h" -#include "multiif.h" -#include "timeval.h" -#include "curl_md5.h" -#include "warnless.h" -#include "curl_base64.h" -#include "curl_printf.h" -#include "strdup.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* convenience macro to check if this handle is using a shared SSL session */ -#define SSLSESSION_SHARED(data) (data->share && \ - (data->share->specifier & \ - (1<<CURL_LOCK_DATA_SSL_SESSION))) - -#define CLONE_STRING(var) \ - do { \ - if(source->var) { \ - dest->var = strdup(source->var); \ - if(!dest->var) \ - return FALSE; \ - } \ - else \ - dest->var = NULL; \ - } while(0) - -#define CLONE_BLOB(var) \ - do { \ - if(blobdup(&dest->var, source->var)) \ - return FALSE; \ - } while(0) - -static CURLcode blobdup(struct curl_blob **dest, - struct curl_blob *src) -{ - DEBUGASSERT(dest); - DEBUGASSERT(!*dest); - if(src) { - /* only if there's data to dupe! */ - struct curl_blob *d; - d = malloc(sizeof(struct curl_blob) + src->len); - if(!d) - return CURLE_OUT_OF_MEMORY; - d->len = src->len; - /* Always duplicate because the connection may survive longer than the - handle that passed in the blob. */ - d->flags = CURL_BLOB_COPY; - d->data = (void *)((char *)d + sizeof(struct curl_blob)); - memcpy(d->data, src->data, src->len); - *dest = d; - } - return CURLE_OK; -} - -/* returns TRUE if the blobs are identical */ -static bool blobcmp(struct curl_blob *first, struct curl_blob *second) -{ - if(!first && !second) /* both are NULL */ - return TRUE; - if(!first || !second) /* one is NULL */ - return FALSE; - if(first->len != second->len) /* different sizes */ - return FALSE; - return !memcmp(first->data, second->data, first->len); /* same data */ -} - - -bool -Curl_ssl_config_matches(struct ssl_primary_config *data, - struct ssl_primary_config *needle) -{ - if((data->version == needle->version) && - (data->version_max == needle->version_max) && - (data->ssl_options == needle->ssl_options) && - (data->verifypeer == needle->verifypeer) && - (data->verifyhost == needle->verifyhost) && - (data->verifystatus == needle->verifystatus) && - blobcmp(data->cert_blob, needle->cert_blob) && - blobcmp(data->ca_info_blob, needle->ca_info_blob) && - blobcmp(data->issuercert_blob, needle->issuercert_blob) && - Curl_safecmp(data->CApath, needle->CApath) && - Curl_safecmp(data->CAfile, needle->CAfile) && - Curl_safecmp(data->issuercert, needle->issuercert) && - Curl_safecmp(data->clientcert, needle->clientcert) && -#ifdef USE_TLS_SRP - !Curl_timestrcmp(data->username, needle->username) && - !Curl_timestrcmp(data->password, needle->password) && - (data->authtype == needle->authtype) && -#endif - Curl_safe_strcasecompare(data->cipher_list, needle->cipher_list) && - Curl_safe_strcasecompare(data->cipher_list13, needle->cipher_list13) && - Curl_safe_strcasecompare(data->curves, needle->curves) && - Curl_safe_strcasecompare(data->CRLfile, needle->CRLfile) && - Curl_safe_strcasecompare(data->pinned_key, needle->pinned_key)) - return TRUE; - - return FALSE; -} - -bool -Curl_clone_primary_ssl_config(struct ssl_primary_config *source, - struct ssl_primary_config *dest) -{ - dest->version = source->version; - dest->version_max = source->version_max; - dest->verifypeer = source->verifypeer; - dest->verifyhost = source->verifyhost; - dest->verifystatus = source->verifystatus; - dest->sessionid = source->sessionid; - dest->ssl_options = source->ssl_options; -#ifdef USE_TLS_SRP - dest->authtype = source->authtype; -#endif - - CLONE_BLOB(cert_blob); - CLONE_BLOB(ca_info_blob); - CLONE_BLOB(issuercert_blob); - CLONE_STRING(CApath); - CLONE_STRING(CAfile); - CLONE_STRING(issuercert); - CLONE_STRING(clientcert); - CLONE_STRING(cipher_list); - CLONE_STRING(cipher_list13); - CLONE_STRING(pinned_key); - CLONE_STRING(curves); - CLONE_STRING(CRLfile); -#ifdef USE_TLS_SRP - CLONE_STRING(username); - CLONE_STRING(password); -#endif - - return TRUE; -} - -void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc) -{ - Curl_safefree(sslc->CApath); - Curl_safefree(sslc->CAfile); - Curl_safefree(sslc->issuercert); - Curl_safefree(sslc->clientcert); - Curl_safefree(sslc->cipher_list); - Curl_safefree(sslc->cipher_list13); - Curl_safefree(sslc->pinned_key); - Curl_safefree(sslc->cert_blob); - Curl_safefree(sslc->ca_info_blob); - Curl_safefree(sslc->issuercert_blob); - Curl_safefree(sslc->curves); - Curl_safefree(sslc->CRLfile); -#ifdef USE_TLS_SRP - Curl_safefree(sslc->username); - Curl_safefree(sslc->password); -#endif -} - -#ifdef USE_SSL -static int multissl_setup(const struct Curl_ssl *backend); -#endif - -curl_sslbackend Curl_ssl_backend(void) -{ -#ifdef USE_SSL - multissl_setup(NULL); - return Curl_ssl->info.id; -#else - return CURLSSLBACKEND_NONE; -#endif -} - -#ifdef USE_SSL - -/* "global" init done? */ -static bool init_ssl = FALSE; - -/** - * Global SSL init - * - * @retval 0 error initializing SSL - * @retval 1 SSL initialized successfully - */ -int Curl_ssl_init(void) -{ - /* make sure this is only done once */ - if(init_ssl) - return 1; - init_ssl = TRUE; /* never again */ - - return Curl_ssl->init(); -} - -#if defined(CURL_WITH_MULTI_SSL) -static const struct Curl_ssl Curl_ssl_multi; -#endif - -/* Global cleanup */ -void Curl_ssl_cleanup(void) -{ - if(init_ssl) { - /* only cleanup if we did a previous init */ - Curl_ssl->cleanup(); -#if defined(CURL_WITH_MULTI_SSL) - Curl_ssl = &Curl_ssl_multi; -#endif - init_ssl = FALSE; - } -} - -static bool ssl_prefs_check(struct Curl_easy *data) -{ - /* check for CURLOPT_SSLVERSION invalid parameter value */ - const long sslver = data->set.ssl.primary.version; - if((sslver < 0) || (sslver >= CURL_SSLVERSION_LAST)) { - failf(data, "Unrecognized parameter value passed via CURLOPT_SSLVERSION"); - return FALSE; - } - - switch(data->set.ssl.primary.version_max) { - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_DEFAULT: - break; - - default: - if((data->set.ssl.primary.version_max >> 16) < sslver) { - failf(data, "CURL_SSLVERSION_MAX incompatible with CURL_SSLVERSION"); - return FALSE; - } - } - - return TRUE; -} - -#ifndef CURL_DISABLE_PROXY -static CURLcode -ssl_connect_init_proxy(struct connectdata *conn, int sockindex) -{ - DEBUGASSERT(conn->bits.proxy_ssl_connected[sockindex]); - if(ssl_connection_complete == conn->ssl[sockindex].state && - !conn->proxy_ssl[sockindex].use) { - struct ssl_backend_data *pbdata; - - if(!(Curl_ssl->supports & SSLSUPP_HTTPS_PROXY)) - return CURLE_NOT_BUILT_IN; - - /* The pointers to the ssl backend data, which is opaque here, are swapped - rather than move the contents. */ - pbdata = conn->proxy_ssl[sockindex].backend; - conn->proxy_ssl[sockindex] = conn->ssl[sockindex]; - - DEBUGASSERT(pbdata != NULL); - - memset(&conn->ssl[sockindex], 0, sizeof(conn->ssl[sockindex])); - memset(pbdata, 0, Curl_ssl->sizeof_ssl_backend_data); - - conn->ssl[sockindex].backend = pbdata; - } - return CURLE_OK; -} -#endif - -CURLcode -Curl_ssl_connect(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - CURLcode result; - -#ifndef CURL_DISABLE_PROXY - if(conn->bits.proxy_ssl_connected[sockindex]) { - result = ssl_connect_init_proxy(conn, sockindex); - if(result) - return result; - } -#endif - - if(!ssl_prefs_check(data)) - return CURLE_SSL_CONNECT_ERROR; - - /* mark this is being ssl-enabled from here on. */ - conn->ssl[sockindex].use = TRUE; - conn->ssl[sockindex].state = ssl_connection_negotiating; - - result = Curl_ssl->connect_blocking(data, conn, sockindex); - - if(!result) - Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSL is connected */ - else - conn->ssl[sockindex].use = FALSE; - - return result; -} - -CURLcode -Curl_ssl_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn, - bool isproxy, int sockindex, bool *done) -{ - CURLcode result; - -#ifndef CURL_DISABLE_PROXY - if(conn->bits.proxy_ssl_connected[sockindex]) { - result = ssl_connect_init_proxy(conn, sockindex); - if(result) - return result; - } -#endif - if(!ssl_prefs_check(data)) - return CURLE_SSL_CONNECT_ERROR; - - /* mark this is being ssl requested from here on. */ - conn->ssl[sockindex].use = TRUE; - result = Curl_ssl->connect_nonblocking(data, conn, sockindex, done); - if(result) - conn->ssl[sockindex].use = FALSE; - else if(*done && !isproxy) - Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSL is connected */ - return result; -} - -/* - * Lock shared SSL session data - */ -void Curl_ssl_sessionid_lock(struct Curl_easy *data) -{ - if(SSLSESSION_SHARED(data)) - Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE); -} - -/* - * Unlock shared SSL session data - */ -void Curl_ssl_sessionid_unlock(struct Curl_easy *data) -{ - if(SSLSESSION_SHARED(data)) - Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION); -} - -/* - * Check if there's a session ID for the given connection in the cache, and if - * there's one suitable, it is provided. Returns TRUE when no entry matched. - */ -bool Curl_ssl_getsessionid(struct Curl_easy *data, - struct connectdata *conn, - const bool isProxy, - void **ssl_sessionid, - size_t *idsize, /* set 0 if unknown */ - int sockindex) -{ - struct Curl_ssl_session *check; - size_t i; - long *general_age; - bool no_match = TRUE; - -#ifndef CURL_DISABLE_PROXY - struct ssl_primary_config * const ssl_config = isProxy ? - &conn->proxy_ssl_config : - &conn->ssl_config; - const char * const name = isProxy ? - conn->http_proxy.host.name : conn->host.name; - int port = isProxy ? (int)conn->port : conn->remote_port; -#else - /* no proxy support */ - struct ssl_primary_config * const ssl_config = &conn->ssl_config; - const char * const name = conn->host.name; - int port = conn->remote_port; -#endif - (void)sockindex; - *ssl_sessionid = NULL; - -#ifdef CURL_DISABLE_PROXY - if(isProxy) - return TRUE; -#endif - - DEBUGASSERT(SSL_SET_OPTION(primary.sessionid)); - - if(!SSL_SET_OPTION(primary.sessionid) || !data->state.session) - /* session ID re-use is disabled or the session cache has not been - setup */ - return TRUE; - - /* Lock if shared */ - if(SSLSESSION_SHARED(data)) - general_age = &data->share->sessionage; - else - general_age = &data->state.sessionage; - - for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) { - check = &data->state.session[i]; - if(!check->sessionid) - /* not session ID means blank entry */ - continue; - if(strcasecompare(name, check->name) && - ((!conn->bits.conn_to_host && !check->conn_to_host) || - (conn->bits.conn_to_host && check->conn_to_host && - strcasecompare(conn->conn_to_host.name, check->conn_to_host))) && - ((!conn->bits.conn_to_port && check->conn_to_port == -1) || - (conn->bits.conn_to_port && check->conn_to_port != -1 && - conn->conn_to_port == check->conn_to_port)) && - (port == check->remote_port) && - strcasecompare(conn->handler->scheme, check->scheme) && - Curl_ssl_config_matches(ssl_config, &check->ssl_config)) { - /* yes, we have a session ID! */ - (*general_age)++; /* increase general age */ - check->age = *general_age; /* set this as used in this age */ - *ssl_sessionid = check->sessionid; - if(idsize) - *idsize = check->idsize; - no_match = FALSE; - break; - } - } - - DEBUGF(infof(data, "%s Session ID in cache for %s %s://%s:%d", - no_match? "Didn't find": "Found", - isProxy ? "proxy" : "host", - conn->handler->scheme, name, port)); - return no_match; -} - -/* - * Kill a single session ID entry in the cache. - */ -void Curl_ssl_kill_session(struct Curl_ssl_session *session) -{ - if(session->sessionid) { - /* defensive check */ - - /* free the ID the SSL-layer specific way */ - Curl_ssl->session_free(session->sessionid); - - session->sessionid = NULL; - session->age = 0; /* fresh */ - - Curl_free_primary_ssl_config(&session->ssl_config); - - Curl_safefree(session->name); - Curl_safefree(session->conn_to_host); - } -} - -/* - * Delete the given session ID from the cache. - */ -void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid) -{ - size_t i; - - for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) { - struct Curl_ssl_session *check = &data->state.session[i]; - - if(check->sessionid == ssl_sessionid) { - Curl_ssl_kill_session(check); - break; - } - } -} - -/* - * Store session id in the session cache. The ID passed on to this function - * must already have been extracted and allocated the proper way for the SSL - * layer. Curl_XXXX_session_free() will be called to free/kill the session ID - * later on. - */ -CURLcode Curl_ssl_addsessionid(struct Curl_easy *data, - struct connectdata *conn, - const bool isProxy, - void *ssl_sessionid, - size_t idsize, - int sockindex, - bool *added) -{ - size_t i; - struct Curl_ssl_session *store; - long oldest_age; - char *clone_host; - char *clone_conn_to_host; - int conn_to_port; - long *general_age; -#ifndef CURL_DISABLE_PROXY - struct ssl_primary_config * const ssl_config = isProxy ? - &conn->proxy_ssl_config : - &conn->ssl_config; - const char *hostname = isProxy ? conn->http_proxy.host.name : - conn->host.name; -#else - struct ssl_primary_config * const ssl_config = &conn->ssl_config; - const char *hostname = conn->host.name; -#endif - (void)sockindex; - - if(added) - *added = FALSE; - - if(!data->state.session) - return CURLE_OK; - - store = &data->state.session[0]; - oldest_age = data->state.session[0].age; /* zero if unused */ - DEBUGASSERT(SSL_SET_OPTION(primary.sessionid)); - - clone_host = strdup(hostname); - if(!clone_host) - return CURLE_OUT_OF_MEMORY; /* bail out */ - - if(conn->bits.conn_to_host) { - clone_conn_to_host = strdup(conn->conn_to_host.name); - if(!clone_conn_to_host) { - free(clone_host); - return CURLE_OUT_OF_MEMORY; /* bail out */ - } - } - else - clone_conn_to_host = NULL; - - if(conn->bits.conn_to_port) - conn_to_port = conn->conn_to_port; - else - conn_to_port = -1; - - /* Now we should add the session ID and the host name to the cache, (remove - the oldest if necessary) */ - - /* If using shared SSL session, lock! */ - if(SSLSESSION_SHARED(data)) { - general_age = &data->share->sessionage; - } - else { - general_age = &data->state.sessionage; - } - - /* find an empty slot for us, or find the oldest */ - for(i = 1; (i < data->set.general_ssl.max_ssl_sessions) && - data->state.session[i].sessionid; i++) { - if(data->state.session[i].age < oldest_age) { - oldest_age = data->state.session[i].age; - store = &data->state.session[i]; - } - } - if(i == data->set.general_ssl.max_ssl_sessions) - /* cache is full, we must "kill" the oldest entry! */ - Curl_ssl_kill_session(store); - else - store = &data->state.session[i]; /* use this slot */ - - /* now init the session struct wisely */ - store->sessionid = ssl_sessionid; - store->idsize = idsize; - store->age = *general_age; /* set current age */ - /* free it if there's one already present */ - free(store->name); - free(store->conn_to_host); - store->name = clone_host; /* clone host name */ - store->conn_to_host = clone_conn_to_host; /* clone connect to host name */ - store->conn_to_port = conn_to_port; /* connect to port number */ - /* port number */ - store->remote_port = isProxy ? (int)conn->port : conn->remote_port; - store->scheme = conn->handler->scheme; - - if(!Curl_clone_primary_ssl_config(ssl_config, &store->ssl_config)) { - Curl_free_primary_ssl_config(&store->ssl_config); - store->sessionid = NULL; /* let caller free sessionid */ - free(clone_host); - free(clone_conn_to_host); - return CURLE_OUT_OF_MEMORY; - } - - if(added) - *added = TRUE; - - DEBUGF(infof(data, "Added Session ID to cache for %s://%s:%d [%s]", - store->scheme, store->name, store->remote_port, - isProxy ? "PROXY" : "server")); - return CURLE_OK; -} - -void Curl_ssl_associate_conn(struct Curl_easy *data, - struct connectdata *conn) -{ - if(Curl_ssl->associate_connection) { - Curl_ssl->associate_connection(data, conn, FIRSTSOCKET); - if((conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) && - conn->bits.sock_accepted) - Curl_ssl->associate_connection(data, conn, SECONDARYSOCKET); - } -} - -void Curl_ssl_detach_conn(struct Curl_easy *data, - struct connectdata *conn) -{ - if(Curl_ssl->disassociate_connection) { - Curl_ssl->disassociate_connection(data, FIRSTSOCKET); - if((conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) && - conn->bits.sock_accepted) - Curl_ssl->disassociate_connection(data, SECONDARYSOCKET); - } -} - -void Curl_ssl_close_all(struct Curl_easy *data) -{ - /* kill the session ID cache if not shared */ - if(data->state.session && !SSLSESSION_SHARED(data)) { - size_t i; - for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) - /* the single-killer function handles empty table slots */ - Curl_ssl_kill_session(&data->state.session[i]); - - /* free the cache data */ - Curl_safefree(data->state.session); - } - - Curl_ssl->close_all(data); -} - -int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks) -{ - struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET]; - - if(connssl->connecting_state == ssl_connect_2_writing) { - /* write mode */ - socks[0] = conn->sock[FIRSTSOCKET]; - return GETSOCK_WRITESOCK(0); - } - if(connssl->connecting_state == ssl_connect_2_reading) { - /* read mode */ - socks[0] = conn->sock[FIRSTSOCKET]; - return GETSOCK_READSOCK(0); - } - - return GETSOCK_BLANK; -} - -void Curl_ssl_close(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - DEBUGASSERT((sockindex <= 1) && (sockindex >= -1)); - Curl_ssl->close_one(data, conn, sockindex); - conn->ssl[sockindex].state = ssl_connection_none; -} - -CURLcode Curl_ssl_shutdown(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - if(Curl_ssl->shut_down(data, conn, sockindex)) - return CURLE_SSL_SHUTDOWN_FAILED; - - conn->ssl[sockindex].use = FALSE; /* get back to ordinary socket usage */ - conn->ssl[sockindex].state = ssl_connection_none; - - conn->recv[sockindex] = Curl_recv_plain; - conn->send[sockindex] = Curl_send_plain; - - return CURLE_OK; -} - -/* Selects an SSL crypto engine - */ -CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine) -{ - return Curl_ssl->set_engine(data, engine); -} - -/* Selects the default SSL crypto engine - */ -CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data) -{ - return Curl_ssl->set_engine_default(data); -} - -/* Return list of OpenSSL crypto engine names. */ -struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data) -{ - return Curl_ssl->engines_list(data); -} - -/* - * This sets up a session ID cache to the specified size. Make sure this code - * is agnostic to what underlying SSL technology we use. - */ -CURLcode Curl_ssl_initsessions(struct Curl_easy *data, size_t amount) -{ - struct Curl_ssl_session *session; - - if(data->state.session) - /* this is just a precaution to prevent multiple inits */ - return CURLE_OK; - - session = calloc(amount, sizeof(struct Curl_ssl_session)); - if(!session) - return CURLE_OUT_OF_MEMORY; - - /* store the info in the SSL section */ - data->set.general_ssl.max_ssl_sessions = amount; - data->state.session = session; - data->state.sessionage = 1; /* this is brand new */ - return CURLE_OK; -} - -static size_t multissl_version(char *buffer, size_t size); - -void Curl_ssl_version(char *buffer, size_t size) -{ -#ifdef CURL_WITH_MULTI_SSL - (void)multissl_version(buffer, size); -#else - (void)Curl_ssl->version(buffer, size); -#endif -} - -/* - * This function tries to determine connection status. - * - * Return codes: - * 1 means the connection is still in place - * 0 means the connection has been closed - * -1 means the connection status is unknown - */ -int Curl_ssl_check_cxn(struct connectdata *conn) -{ - return Curl_ssl->check_cxn(conn); -} - -bool Curl_ssl_data_pending(const struct connectdata *conn, - int connindex) -{ - return Curl_ssl->data_pending(conn, connindex); -} - -void Curl_ssl_free_certinfo(struct Curl_easy *data) -{ - struct curl_certinfo *ci = &data->info.certs; - - if(ci->num_of_certs) { - /* free all individual lists used */ - int i; - for(i = 0; i<ci->num_of_certs; i++) { - curl_slist_free_all(ci->certinfo[i]); - ci->certinfo[i] = NULL; - } - - free(ci->certinfo); /* free the actual array too */ - ci->certinfo = NULL; - ci->num_of_certs = 0; - } -} - -CURLcode Curl_ssl_init_certinfo(struct Curl_easy *data, int num) -{ - struct curl_certinfo *ci = &data->info.certs; - struct curl_slist **table; - - /* Free any previous certificate information structures */ - Curl_ssl_free_certinfo(data); - - /* Allocate the required certificate information structures */ - table = calloc((size_t) num, sizeof(struct curl_slist *)); - if(!table) - return CURLE_OUT_OF_MEMORY; - - ci->num_of_certs = num; - ci->certinfo = table; - - return CURLE_OK; -} - -/* - * 'value' is NOT a null-terminated string - */ -CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data, - int certnum, - const char *label, - const char *value, - size_t valuelen) -{ - struct curl_certinfo *ci = &data->info.certs; - char *output; - struct curl_slist *nl; - CURLcode result = CURLE_OK; - size_t labellen = strlen(label); - size_t outlen = labellen + 1 + valuelen + 1; /* label:value\0 */ - - output = malloc(outlen); - if(!output) - return CURLE_OUT_OF_MEMORY; - - /* sprintf the label and colon */ - msnprintf(output, outlen, "%s:", label); - - /* memcpy the value (it might not be null-terminated) */ - memcpy(&output[labellen + 1], value, valuelen); - - /* null-terminate the output */ - output[labellen + 1 + valuelen] = 0; - - nl = Curl_slist_append_nodup(ci->certinfo[certnum], output); - if(!nl) { - free(output); - curl_slist_free_all(ci->certinfo[certnum]); - result = CURLE_OUT_OF_MEMORY; - } - - ci->certinfo[certnum] = nl; - return result; -} - -/* - * This is a convenience function for push_certinfo_len that takes a zero - * terminated value. - */ -CURLcode Curl_ssl_push_certinfo(struct Curl_easy *data, - int certnum, - const char *label, - const char *value) -{ - size_t valuelen = strlen(value); - - return Curl_ssl_push_certinfo_len(data, certnum, label, value, valuelen); -} - -CURLcode Curl_ssl_random(struct Curl_easy *data, - unsigned char *entropy, - size_t length) -{ - return Curl_ssl->random(data, entropy, length); -} - -/* - * Curl_ssl_snihost() converts the input host name to a suitable SNI name put - * in data->state.buffer. Returns a pointer to the name (or NULL if a problem) - * and stores the new length in 'olen'. - * - * SNI fields must not have any trailing dot and while RFC 6066 section 3 says - * the SNI field is case insensitive, browsers always send the data lowercase - * and subsequently there are numerous servers out there that don't work - * unless the name is lowercased. - */ - -char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen) -{ - size_t len = strlen(host); - if(len && (host[len-1] == '.')) - len--; - if(len >= data->set.buffer_size) - return NULL; - - Curl_strntolower(data->state.buffer, host, len); - data->state.buffer[len] = 0; - if(olen) - *olen = len; - return data->state.buffer; -} - -/* - * Public key pem to der conversion - */ - -static CURLcode pubkey_pem_to_der(const char *pem, - unsigned char **der, size_t *der_len) -{ - char *stripped_pem, *begin_pos, *end_pos; - size_t pem_count, stripped_pem_count = 0, pem_len; - CURLcode result; - - /* if no pem, exit. */ - if(!pem) - return CURLE_BAD_CONTENT_ENCODING; - - begin_pos = strstr(pem, "-----BEGIN PUBLIC KEY-----"); - if(!begin_pos) - return CURLE_BAD_CONTENT_ENCODING; - - pem_count = begin_pos - pem; - /* Invalid if not at beginning AND not directly following \n */ - if(0 != pem_count && '\n' != pem[pem_count - 1]) - return CURLE_BAD_CONTENT_ENCODING; - - /* 26 is length of "-----BEGIN PUBLIC KEY-----" */ - pem_count += 26; - - /* Invalid if not directly following \n */ - end_pos = strstr(pem + pem_count, "\n-----END PUBLIC KEY-----"); - if(!end_pos) - return CURLE_BAD_CONTENT_ENCODING; - - pem_len = end_pos - pem; - - stripped_pem = malloc(pem_len - pem_count + 1); - if(!stripped_pem) - return CURLE_OUT_OF_MEMORY; - - /* - * Here we loop through the pem array one character at a time between the - * correct indices, and place each character that is not '\n' or '\r' - * into the stripped_pem array, which should represent the raw base64 string - */ - while(pem_count < pem_len) { - if('\n' != pem[pem_count] && '\r' != pem[pem_count]) - stripped_pem[stripped_pem_count++] = pem[pem_count]; - ++pem_count; - } - /* Place the null terminator in the correct place */ - stripped_pem[stripped_pem_count] = '\0'; - - result = Curl_base64_decode(stripped_pem, der, der_len); - - Curl_safefree(stripped_pem); - - return result; -} - -/* - * Generic pinned public key check. - */ - -CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, - const char *pinnedpubkey, - const unsigned char *pubkey, size_t pubkeylen) -{ - FILE *fp; - unsigned char *buf = NULL, *pem_ptr = NULL; - CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - - /* if a path wasn't specified, don't pin */ - if(!pinnedpubkey) - return CURLE_OK; - if(!pubkey || !pubkeylen) - return result; - - /* only do this if pinnedpubkey starts with "sha256//", length 8 */ - if(strncmp(pinnedpubkey, "sha256//", 8) == 0) { - CURLcode encode; - size_t encodedlen, pinkeylen; - char *encoded, *pinkeycopy, *begin_pos, *end_pos; - unsigned char *sha256sumdigest; - - if(!Curl_ssl->sha256sum) { - /* without sha256 support, this cannot match */ - return result; - } - - /* compute sha256sum of public key */ - sha256sumdigest = malloc(CURL_SHA256_DIGEST_LENGTH); - if(!sha256sumdigest) - return CURLE_OUT_OF_MEMORY; - encode = Curl_ssl->sha256sum(pubkey, pubkeylen, - sha256sumdigest, CURL_SHA256_DIGEST_LENGTH); - - if(encode != CURLE_OK) - return encode; - - encode = Curl_base64_encode((char *)sha256sumdigest, - CURL_SHA256_DIGEST_LENGTH, &encoded, - &encodedlen); - Curl_safefree(sha256sumdigest); - - if(encode) - return encode; - - infof(data, " public key hash: sha256//%s", encoded); - - /* it starts with sha256//, copy so we can modify it */ - pinkeylen = strlen(pinnedpubkey) + 1; - pinkeycopy = malloc(pinkeylen); - if(!pinkeycopy) { - Curl_safefree(encoded); - return CURLE_OUT_OF_MEMORY; - } - memcpy(pinkeycopy, pinnedpubkey, pinkeylen); - /* point begin_pos to the copy, and start extracting keys */ - begin_pos = pinkeycopy; - do { - end_pos = strstr(begin_pos, ";sha256//"); - /* - * if there is an end_pos, null terminate, - * otherwise it'll go to the end of the original string - */ - if(end_pos) - end_pos[0] = '\0'; - - /* compare base64 sha256 digests, 8 is the length of "sha256//" */ - if(encodedlen == strlen(begin_pos + 8) && - !memcmp(encoded, begin_pos + 8, encodedlen)) { - result = CURLE_OK; - break; - } - - /* - * change back the null-terminator we changed earlier, - * and look for next begin - */ - if(end_pos) { - end_pos[0] = ';'; - begin_pos = strstr(end_pos, "sha256//"); - } - } while(end_pos && begin_pos); - Curl_safefree(encoded); - Curl_safefree(pinkeycopy); - return result; - } - - fp = fopen(pinnedpubkey, "rb"); - if(!fp) - return result; - - do { - long filesize; - size_t size, pem_len; - CURLcode pem_read; - - /* Determine the file's size */ - if(fseek(fp, 0, SEEK_END)) - break; - filesize = ftell(fp); - if(fseek(fp, 0, SEEK_SET)) - break; - if(filesize < 0 || filesize > MAX_PINNED_PUBKEY_SIZE) - break; - - /* - * if the size of our certificate is bigger than the file - * size then it can't match - */ - size = curlx_sotouz((curl_off_t) filesize); - if(pubkeylen > size) - break; - - /* - * Allocate buffer for the pinned key - * With 1 additional byte for null terminator in case of PEM key - */ - buf = malloc(size + 1); - if(!buf) - break; - - /* Returns number of elements read, which should be 1 */ - if((int) fread(buf, size, 1, fp) != 1) - break; - - /* If the sizes are the same, it can't be base64 encoded, must be der */ - if(pubkeylen == size) { - if(!memcmp(pubkey, buf, pubkeylen)) - result = CURLE_OK; - break; - } - - /* - * Otherwise we will assume it's PEM and try to decode it - * after placing null terminator - */ - buf[size] = '\0'; - pem_read = pubkey_pem_to_der((const char *)buf, &pem_ptr, &pem_len); - /* if it wasn't read successfully, exit */ - if(pem_read) - break; - - /* - * if the size of our certificate doesn't match the size of - * the decoded file, they can't be the same, otherwise compare - */ - if(pubkeylen == pem_len && !memcmp(pubkey, pem_ptr, pubkeylen)) - result = CURLE_OK; - } while(0); - - Curl_safefree(buf); - Curl_safefree(pem_ptr); - fclose(fp); - - return result; -} - -/* - * Check whether the SSL backend supports the status_request extension. - */ -bool Curl_ssl_cert_status_request(void) -{ - return Curl_ssl->cert_status_request(); -} - -/* - * Check whether the SSL backend supports false start. - */ -bool Curl_ssl_false_start(void) -{ - return Curl_ssl->false_start(); -} - -/* - * Check whether the SSL backend supports setting TLS 1.3 cipher suites - */ -bool Curl_ssl_tls13_ciphersuites(void) -{ - return Curl_ssl->supports & SSLSUPP_TLS13_CIPHERSUITES; -} - -/* - * Default implementations for unsupported functions. - */ - -int Curl_none_init(void) -{ - return 1; -} - -void Curl_none_cleanup(void) -{ } - -int Curl_none_shutdown(struct Curl_easy *data UNUSED_PARAM, - struct connectdata *conn UNUSED_PARAM, - int sockindex UNUSED_PARAM) -{ - (void)data; - (void)conn; - (void)sockindex; - return 0; -} - -int Curl_none_check_cxn(struct connectdata *conn UNUSED_PARAM) -{ - (void)conn; - return -1; -} - -CURLcode Curl_none_random(struct Curl_easy *data UNUSED_PARAM, - unsigned char *entropy UNUSED_PARAM, - size_t length UNUSED_PARAM) -{ - (void)data; - (void)entropy; - (void)length; - return CURLE_NOT_BUILT_IN; -} - -void Curl_none_close_all(struct Curl_easy *data UNUSED_PARAM) -{ - (void)data; -} - -void Curl_none_session_free(void *ptr UNUSED_PARAM) -{ - (void)ptr; -} - -bool Curl_none_data_pending(const struct connectdata *conn UNUSED_PARAM, - int connindex UNUSED_PARAM) -{ - (void)conn; - (void)connindex; - return 0; -} - -bool Curl_none_cert_status_request(void) -{ - return FALSE; -} - -CURLcode Curl_none_set_engine(struct Curl_easy *data UNUSED_PARAM, - const char *engine UNUSED_PARAM) -{ - (void)data; - (void)engine; - return CURLE_NOT_BUILT_IN; -} - -CURLcode Curl_none_set_engine_default(struct Curl_easy *data UNUSED_PARAM) -{ - (void)data; - return CURLE_NOT_BUILT_IN; -} - -struct curl_slist *Curl_none_engines_list(struct Curl_easy *data UNUSED_PARAM) -{ - (void)data; - return (struct curl_slist *)NULL; -} - -bool Curl_none_false_start(void) -{ - return FALSE; -} - -static int multissl_init(void) -{ - if(multissl_setup(NULL)) - return 1; - return Curl_ssl->init(); -} - -static CURLcode multissl_connect(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - if(multissl_setup(NULL)) - return CURLE_FAILED_INIT; - return Curl_ssl->connect_blocking(data, conn, sockindex); -} - -static CURLcode multissl_connect_nonblocking(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, bool *done) -{ - if(multissl_setup(NULL)) - return CURLE_FAILED_INIT; - return Curl_ssl->connect_nonblocking(data, conn, sockindex, done); -} - -static int multissl_getsock(struct connectdata *conn, curl_socket_t *socks) -{ - if(multissl_setup(NULL)) - return 0; - return Curl_ssl->getsock(conn, socks); -} - -static void *multissl_get_internals(struct ssl_connect_data *connssl, - CURLINFO info) -{ - if(multissl_setup(NULL)) - return NULL; - return Curl_ssl->get_internals(connssl, info); -} - -static void multissl_close(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - if(multissl_setup(NULL)) - return; - Curl_ssl->close_one(data, conn, sockindex); -} - -static const struct Curl_ssl Curl_ssl_multi = { - { CURLSSLBACKEND_NONE, "multi" }, /* info */ - 0, /* supports nothing */ - (size_t)-1, /* something insanely large to be on the safe side */ - - multissl_init, /* init */ - Curl_none_cleanup, /* cleanup */ - multissl_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ - Curl_none_shutdown, /* shutdown */ - Curl_none_data_pending, /* data_pending */ - Curl_none_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ - multissl_connect, /* connect */ - multissl_connect_nonblocking, /* connect_nonblocking */ - multissl_getsock, /* getsock */ - multissl_get_internals, /* get_internals */ - multissl_close, /* close_one */ - Curl_none_close_all, /* close_all */ - Curl_none_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ - NULL, /* sha256sum */ - NULL, /* associate_connection */ - NULL /* disassociate_connection */ -}; - -const struct Curl_ssl *Curl_ssl = -#if defined(CURL_WITH_MULTI_SSL) - &Curl_ssl_multi; -#elif defined(USE_WOLFSSL) - &Curl_ssl_wolfssl; -#elif defined(USE_SECTRANSP) - &Curl_ssl_sectransp; -#elif defined(USE_GNUTLS) - &Curl_ssl_gnutls; -#elif defined(USE_GSKIT) - &Curl_ssl_gskit; -#elif defined(USE_MBEDTLS) - &Curl_ssl_mbedtls; -#elif defined(USE_NSS) - &Curl_ssl_nss; -#elif defined(USE_RUSTLS) - &Curl_ssl_rustls; -#elif defined(USE_OPENSSL) - &Curl_ssl_openssl; -#elif defined(USE_SCHANNEL) - &Curl_ssl_schannel; -#elif defined(USE_BEARSSL) - &Curl_ssl_bearssl; -#else -#error "Missing struct Curl_ssl for selected SSL backend" -#endif - -static const struct Curl_ssl *available_backends[] = { -#if defined(USE_WOLFSSL) - &Curl_ssl_wolfssl, -#endif -#if defined(USE_SECTRANSP) - &Curl_ssl_sectransp, -#endif -#if defined(USE_GNUTLS) - &Curl_ssl_gnutls, -#endif -#if defined(USE_GSKIT) - &Curl_ssl_gskit, -#endif -#if defined(USE_MBEDTLS) - &Curl_ssl_mbedtls, -#endif -#if defined(USE_NSS) - &Curl_ssl_nss, -#endif -#if defined(USE_OPENSSL) - &Curl_ssl_openssl, -#endif -#if defined(USE_SCHANNEL) - &Curl_ssl_schannel, -#endif -#if defined(USE_BEARSSL) - &Curl_ssl_bearssl, -#endif -#if defined(USE_RUSTLS) - &Curl_ssl_rustls, -#endif - NULL -}; - -static size_t multissl_version(char *buffer, size_t size) -{ - static const struct Curl_ssl *selected; - static char backends[200]; - static size_t backends_len; - const struct Curl_ssl *current; - - current = Curl_ssl == &Curl_ssl_multi ? available_backends[0] : Curl_ssl; - - if(current != selected) { - char *p = backends; - char *end = backends + sizeof(backends); - int i; - - selected = current; - - backends[0] = '\0'; - - for(i = 0; available_backends[i]; ++i) { - char vb[200]; - bool paren = (selected != available_backends[i]); - - if(available_backends[i]->version(vb, sizeof(vb))) { - p += msnprintf(p, end - p, "%s%s%s%s", (p != backends ? " " : ""), - (paren ? "(" : ""), vb, (paren ? ")" : "")); - } - } - - backends_len = p - backends; - } - - if(!size) - return 0; - - if(size <= backends_len) { - strncpy(buffer, backends, size - 1); - buffer[size - 1] = '\0'; - return size - 1; - } - - strcpy(buffer, backends); - return backends_len; -} - -static int multissl_setup(const struct Curl_ssl *backend) -{ - const char *env; - char *env_tmp; - - if(Curl_ssl != &Curl_ssl_multi) - return 1; - - if(backend) { - Curl_ssl = backend; - return 0; - } - - if(!available_backends[0]) - return 1; - - env = env_tmp = curl_getenv("CURL_SSL_BACKEND"); -#ifdef CURL_DEFAULT_SSL_BACKEND - if(!env) - env = CURL_DEFAULT_SSL_BACKEND; -#endif - if(env) { - int i; - for(i = 0; available_backends[i]; i++) { - if(strcasecompare(env, available_backends[i]->info.name)) { - Curl_ssl = available_backends[i]; - free(env_tmp); - return 0; - } - } - } - - /* Fall back to first available backend */ - Curl_ssl = available_backends[0]; - free(env_tmp); - return 0; -} - -/* This function is used to select the SSL backend to use. It is called by - curl_global_sslset (easy.c) which uses the global init lock. */ -CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, - const curl_ssl_backend ***avail) -{ - int i; - - if(avail) - *avail = (const curl_ssl_backend **)&available_backends; - - if(Curl_ssl != &Curl_ssl_multi) - return id == Curl_ssl->info.id || - (name && strcasecompare(name, Curl_ssl->info.name)) ? - CURLSSLSET_OK : -#if defined(CURL_WITH_MULTI_SSL) - CURLSSLSET_TOO_LATE; -#else - CURLSSLSET_UNKNOWN_BACKEND; -#endif - - for(i = 0; available_backends[i]; i++) { - if(available_backends[i]->info.id == id || - (name && strcasecompare(available_backends[i]->info.name, name))) { - multissl_setup(available_backends[i]); - return CURLSSLSET_OK; - } - } - - return CURLSSLSET_UNKNOWN_BACKEND; -} - -#else /* USE_SSL */ -CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, - const curl_ssl_backend ***avail) -{ - (void)id; - (void)name; - (void)avail; - return CURLSSLSET_NO_BACKENDS; -} - -#endif /* !USE_SSL */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 for implementing all "generic" SSL functions that all libcurl
+ internals should use. It is then responsible for calling the proper
+ "backend" function.
+
+ SSL-functions in libcurl should call functions in this source file, and not
+ to any specific SSL-layer.
+
+ Curl_ssl_ - prefix for generic ones
+
+ Note that this source code uses the functions of the configured SSL
+ backend via the global Curl_ssl instance.
+
+ "SSL/TLS Strong Encryption: An Introduction"
+ https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html
+*/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "urldata.h"
+#include "cfilters.h"
+
+#include "vtls.h" /* generic SSL protos etc */
+#include "vtls_int.h"
+#include "slist.h"
+#include "sendf.h"
+#include "strcase.h"
+#include "url.h"
+#include "progress.h"
+#include "share.h"
+#include "multiif.h"
+#include "timeval.h"
+#include "curl_md5.h"
+#include "warnless.h"
+#include "curl_base64.h"
+#include "curl_printf.h"
+#include "strdup.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* convenience macro to check if this handle is using a shared SSL session */
+#define SSLSESSION_SHARED(data) (data->share && \
+ (data->share->specifier & \
+ (1<<CURL_LOCK_DATA_SSL_SESSION)))
+
+#define CLONE_STRING(var) \
+ do { \
+ if(source->var) { \
+ dest->var = strdup(source->var); \
+ if(!dest->var) \
+ return FALSE; \
+ } \
+ else \
+ dest->var = NULL; \
+ } while(0)
+
+#define CLONE_BLOB(var) \
+ do { \
+ if(blobdup(&dest->var, source->var)) \
+ return FALSE; \
+ } while(0)
+
+static CURLcode blobdup(struct curl_blob **dest,
+ struct curl_blob *src)
+{
+ DEBUGASSERT(dest);
+ DEBUGASSERT(!*dest);
+ if(src) {
+ /* only if there's data to dupe! */
+ struct curl_blob *d;
+ d = malloc(sizeof(struct curl_blob) + src->len);
+ if(!d)
+ return CURLE_OUT_OF_MEMORY;
+ d->len = src->len;
+ /* Always duplicate because the connection may survive longer than the
+ handle that passed in the blob. */
+ d->flags = CURL_BLOB_COPY;
+ d->data = (void *)((char *)d + sizeof(struct curl_blob));
+ memcpy(d->data, src->data, src->len);
+ *dest = d;
+ }
+ return CURLE_OK;
+}
+
+/* returns TRUE if the blobs are identical */
+static bool blobcmp(struct curl_blob *first, struct curl_blob *second)
+{
+ if(!first && !second) /* both are NULL */
+ return TRUE;
+ if(!first || !second) /* one is NULL */
+ return FALSE;
+ if(first->len != second->len) /* different sizes */
+ return FALSE;
+ return !memcmp(first->data, second->data, first->len); /* same data */
+}
+
+
+bool
+Curl_ssl_config_matches(struct ssl_primary_config *data,
+ struct ssl_primary_config *needle)
+{
+ if((data->version == needle->version) &&
+ (data->version_max == needle->version_max) &&
+ (data->ssl_options == needle->ssl_options) &&
+ (data->verifypeer == needle->verifypeer) &&
+ (data->verifyhost == needle->verifyhost) &&
+ (data->verifystatus == needle->verifystatus) &&
+ blobcmp(data->cert_blob, needle->cert_blob) &&
+ blobcmp(data->ca_info_blob, needle->ca_info_blob) &&
+ blobcmp(data->issuercert_blob, needle->issuercert_blob) &&
+ Curl_safecmp(data->CApath, needle->CApath) &&
+ Curl_safecmp(data->CAfile, needle->CAfile) &&
+ Curl_safecmp(data->issuercert, needle->issuercert) &&
+ Curl_safecmp(data->clientcert, needle->clientcert) &&
+#ifdef USE_TLS_SRP
+ !Curl_timestrcmp(data->username, needle->username) &&
+ !Curl_timestrcmp(data->password, needle->password) &&
+ (data->authtype == needle->authtype) &&
+#endif
+ strcasecompare(data->cipher_list, needle->cipher_list) &&
+ strcasecompare(data->cipher_list13, needle->cipher_list13) &&
+ strcasecompare(data->curves, needle->curves) &&
+ strcasecompare(data->CRLfile, needle->CRLfile) &&
+ strcasecompare(data->pinned_key, needle->pinned_key))
+ return TRUE;
+
+ return FALSE;
+}
+
+bool
+Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
+ struct ssl_primary_config *dest)
+{
+ dest->version = source->version;
+ dest->version_max = source->version_max;
+ dest->verifypeer = source->verifypeer;
+ dest->verifyhost = source->verifyhost;
+ dest->verifystatus = source->verifystatus;
+ dest->sessionid = source->sessionid;
+ dest->ssl_options = source->ssl_options;
+#ifdef USE_TLS_SRP
+ dest->authtype = source->authtype;
+#endif
+
+ CLONE_BLOB(cert_blob);
+ CLONE_BLOB(ca_info_blob);
+ CLONE_BLOB(issuercert_blob);
+ CLONE_STRING(CApath);
+ CLONE_STRING(CAfile);
+ CLONE_STRING(issuercert);
+ CLONE_STRING(clientcert);
+ CLONE_STRING(cipher_list);
+ CLONE_STRING(cipher_list13);
+ CLONE_STRING(pinned_key);
+ CLONE_STRING(curves);
+ CLONE_STRING(CRLfile);
+#ifdef USE_TLS_SRP
+ CLONE_STRING(username);
+ CLONE_STRING(password);
+#endif
+
+ return TRUE;
+}
+
+void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc)
+{
+ Curl_safefree(sslc->CApath);
+ Curl_safefree(sslc->CAfile);
+ Curl_safefree(sslc->issuercert);
+ Curl_safefree(sslc->clientcert);
+ Curl_safefree(sslc->cipher_list);
+ Curl_safefree(sslc->cipher_list13);
+ Curl_safefree(sslc->pinned_key);
+ Curl_safefree(sslc->cert_blob);
+ Curl_safefree(sslc->ca_info_blob);
+ Curl_safefree(sslc->issuercert_blob);
+ Curl_safefree(sslc->curves);
+ Curl_safefree(sslc->CRLfile);
+#ifdef USE_TLS_SRP
+ Curl_safefree(sslc->username);
+ Curl_safefree(sslc->password);
+#endif
+}
+
+#ifdef USE_SSL
+static int multissl_setup(const struct Curl_ssl *backend);
+#endif
+
+curl_sslbackend Curl_ssl_backend(void)
+{
+#ifdef USE_SSL
+ multissl_setup(NULL);
+ return Curl_ssl->info.id;
+#else
+ return CURLSSLBACKEND_NONE;
+#endif
+}
+
+#ifdef USE_SSL
+
+/* "global" init done? */
+static bool init_ssl = FALSE;
+
+/**
+ * Global SSL init
+ *
+ * @retval 0 error initializing SSL
+ * @retval 1 SSL initialized successfully
+ */
+int Curl_ssl_init(void)
+{
+ /* make sure this is only done once */
+ if(init_ssl)
+ return 1;
+ init_ssl = TRUE; /* never again */
+
+ return Curl_ssl->init();
+}
+
+#if defined(CURL_WITH_MULTI_SSL)
+static const struct Curl_ssl Curl_ssl_multi;
+#endif
+
+/* Global cleanup */
+void Curl_ssl_cleanup(void)
+{
+ if(init_ssl) {
+ /* only cleanup if we did a previous init */
+ Curl_ssl->cleanup();
+#if defined(CURL_WITH_MULTI_SSL)
+ Curl_ssl = &Curl_ssl_multi;
+#endif
+ init_ssl = FALSE;
+ }
+}
+
+static bool ssl_prefs_check(struct Curl_easy *data)
+{
+ /* check for CURLOPT_SSLVERSION invalid parameter value */
+ const long sslver = data->set.ssl.primary.version;
+ if((sslver < 0) || (sslver >= CURL_SSLVERSION_LAST)) {
+ failf(data, "Unrecognized parameter value passed via CURLOPT_SSLVERSION");
+ return FALSE;
+ }
+
+ switch(data->set.ssl.primary.version_max) {
+ case CURL_SSLVERSION_MAX_NONE:
+ case CURL_SSLVERSION_MAX_DEFAULT:
+ break;
+
+ default:
+ if((data->set.ssl.primary.version_max >> 16) < sslver) {
+ failf(data, "CURL_SSLVERSION_MAX incompatible with CURL_SSLVERSION");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data)
+{
+ struct ssl_connect_data *ctx;
+
+ (void)data;
+ ctx = calloc(1, sizeof(*ctx));
+ if(!ctx)
+ return NULL;
+
+ ctx->backend = calloc(1, Curl_ssl->sizeof_ssl_backend_data);
+ if(!ctx->backend) {
+ free(ctx);
+ return NULL;
+ }
+ return ctx;
+}
+
+static void cf_ctx_free(struct ssl_connect_data *ctx)
+{
+ if(ctx) {
+ free(ctx->backend);
+ free(ctx);
+ }
+}
+
+static void cf_ctx_set_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ if(cf->ctx)
+ ((struct ssl_connect_data *)cf->ctx)->call_data = data;
+}
+
+static CURLcode ssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ CURLcode result;
+
+ if(!ssl_prefs_check(data))
+ return CURLE_SSL_CONNECT_ERROR;
+
+ /* mark this is being ssl-enabled from here on. */
+ connssl->state = ssl_connection_negotiating;
+
+ result = Curl_ssl->connect_blocking(cf, data);
+
+ if(!result) {
+ Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSL is connected */
+ DEBUGASSERT(connssl->state == ssl_connection_complete);
+ }
+
+ return result;
+}
+
+static CURLcode
+ssl_connect_nonblocking(struct Curl_cfilter *cf, struct Curl_easy *data,
+ bool *done)
+{
+ if(!ssl_prefs_check(data))
+ return CURLE_SSL_CONNECT_ERROR;
+
+ /* mark this is being ssl requested from here on. */
+ return Curl_ssl->connect_nonblocking(cf, data, done);
+}
+
+/*
+ * Lock shared SSL session data
+ */
+void Curl_ssl_sessionid_lock(struct Curl_easy *data)
+{
+ if(SSLSESSION_SHARED(data))
+ Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
+}
+
+/*
+ * Unlock shared SSL session data
+ */
+void Curl_ssl_sessionid_unlock(struct Curl_easy *data)
+{
+ if(SSLSESSION_SHARED(data))
+ Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
+}
+
+/*
+ * Check if there's a session ID for the given connection in the cache, and if
+ * there's one suitable, it is provided. Returns TRUE when no entry matched.
+ */
+bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ void **ssl_sessionid,
+ size_t *idsize) /* set 0 if unknown */
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ struct Curl_ssl_session *check;
+ size_t i;
+ long *general_age;
+ bool no_match = TRUE;
+
+ *ssl_sessionid = NULL;
+ if(!ssl_config)
+ return TRUE;
+
+ DEBUGASSERT(ssl_config->primary.sessionid);
+
+ if(!ssl_config->primary.sessionid || !data->state.session)
+ /* session ID re-use is disabled or the session cache has not been
+ setup */
+ return TRUE;
+
+ /* Lock if shared */
+ if(SSLSESSION_SHARED(data))
+ general_age = &data->share->sessionage;
+ else
+ general_age = &data->state.sessionage;
+
+ for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) {
+ check = &data->state.session[i];
+ if(!check->sessionid)
+ /* not session ID means blank entry */
+ continue;
+ if(strcasecompare(connssl->hostname, check->name) &&
+ ((!cf->conn->bits.conn_to_host && !check->conn_to_host) ||
+ (cf->conn->bits.conn_to_host && check->conn_to_host &&
+ strcasecompare(cf->conn->conn_to_host.name, check->conn_to_host))) &&
+ ((!cf->conn->bits.conn_to_port && check->conn_to_port == -1) ||
+ (cf->conn->bits.conn_to_port && check->conn_to_port != -1 &&
+ cf->conn->conn_to_port == check->conn_to_port)) &&
+ (connssl->port == check->remote_port) &&
+ strcasecompare(cf->conn->handler->scheme, check->scheme) &&
+ Curl_ssl_config_matches(conn_config, &check->ssl_config)) {
+ /* yes, we have a session ID! */
+ (*general_age)++; /* increase general age */
+ check->age = *general_age; /* set this as used in this age */
+ *ssl_sessionid = check->sessionid;
+ if(idsize)
+ *idsize = check->idsize;
+ no_match = FALSE;
+ break;
+ }
+ }
+
+ DEBUGF(infof(data, DMSG(data, "%s Session ID in cache for %s %s://%s:%d"),
+ no_match? "Didn't find": "Found",
+ Curl_ssl_cf_is_proxy(cf) ? "proxy" : "host",
+ cf->conn->handler->scheme, connssl->hostname, connssl->port));
+ return no_match;
+}
+
+/*
+ * Kill a single session ID entry in the cache.
+ */
+void Curl_ssl_kill_session(struct Curl_ssl_session *session)
+{
+ if(session->sessionid) {
+ /* defensive check */
+
+ /* free the ID the SSL-layer specific way */
+ Curl_ssl->session_free(session->sessionid);
+
+ session->sessionid = NULL;
+ session->age = 0; /* fresh */
+
+ Curl_free_primary_ssl_config(&session->ssl_config);
+
+ Curl_safefree(session->name);
+ Curl_safefree(session->conn_to_host);
+ }
+}
+
+/*
+ * Delete the given session ID from the cache.
+ */
+void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid)
+{
+ size_t i;
+
+ for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) {
+ struct Curl_ssl_session *check = &data->state.session[i];
+
+ if(check->sessionid == ssl_sessionid) {
+ Curl_ssl_kill_session(check);
+ break;
+ }
+ }
+}
+
+/*
+ * Store session id in the session cache. The ID passed on to this function
+ * must already have been extracted and allocated the proper way for the SSL
+ * layer. Curl_XXXX_session_free() will be called to free/kill the session ID
+ * later on.
+ */
+CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ void *ssl_sessionid,
+ size_t idsize,
+ bool *added)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ size_t i;
+ struct Curl_ssl_session *store;
+ long oldest_age;
+ char *clone_host;
+ char *clone_conn_to_host;
+ int conn_to_port;
+ long *general_age;
+
+ if(added)
+ *added = FALSE;
+
+ if(!data->state.session)
+ return CURLE_OK;
+
+ store = &data->state.session[0];
+ oldest_age = data->state.session[0].age; /* zero if unused */
+ (void)ssl_config;
+ DEBUGASSERT(ssl_config->primary.sessionid);
+
+ clone_host = strdup(connssl->hostname);
+ if(!clone_host)
+ return CURLE_OUT_OF_MEMORY; /* bail out */
+
+ if(cf->conn->bits.conn_to_host) {
+ clone_conn_to_host = strdup(cf->conn->conn_to_host.name);
+ if(!clone_conn_to_host) {
+ free(clone_host);
+ return CURLE_OUT_OF_MEMORY; /* bail out */
+ }
+ }
+ else
+ clone_conn_to_host = NULL;
+
+ if(cf->conn->bits.conn_to_port)
+ conn_to_port = cf->conn->conn_to_port;
+ else
+ conn_to_port = -1;
+
+ /* Now we should add the session ID and the host name to the cache, (remove
+ the oldest if necessary) */
+
+ /* If using shared SSL session, lock! */
+ if(SSLSESSION_SHARED(data)) {
+ general_age = &data->share->sessionage;
+ }
+ else {
+ general_age = &data->state.sessionage;
+ }
+
+ /* find an empty slot for us, or find the oldest */
+ for(i = 1; (i < data->set.general_ssl.max_ssl_sessions) &&
+ data->state.session[i].sessionid; i++) {
+ if(data->state.session[i].age < oldest_age) {
+ oldest_age = data->state.session[i].age;
+ store = &data->state.session[i];
+ }
+ }
+ if(i == data->set.general_ssl.max_ssl_sessions)
+ /* cache is full, we must "kill" the oldest entry! */
+ Curl_ssl_kill_session(store);
+ else
+ store = &data->state.session[i]; /* use this slot */
+
+ /* now init the session struct wisely */
+ store->sessionid = ssl_sessionid;
+ store->idsize = idsize;
+ store->age = *general_age; /* set current age */
+ /* free it if there's one already present */
+ free(store->name);
+ free(store->conn_to_host);
+ store->name = clone_host; /* clone host name */
+ store->conn_to_host = clone_conn_to_host; /* clone connect to host name */
+ store->conn_to_port = conn_to_port; /* connect to port number */
+ /* port number */
+ store->remote_port = connssl->port;
+ store->scheme = cf->conn->handler->scheme;
+
+ if(!Curl_clone_primary_ssl_config(conn_config, &store->ssl_config)) {
+ Curl_free_primary_ssl_config(&store->ssl_config);
+ store->sessionid = NULL; /* let caller free sessionid */
+ free(clone_host);
+ free(clone_conn_to_host);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(added)
+ *added = TRUE;
+
+ DEBUGF(infof(data, DMSG(data, "Added Session ID to cache for %s://%s:%d"
+ " [%s]"), store->scheme, store->name, store->remote_port,
+ Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server"));
+ return CURLE_OK;
+}
+
+void Curl_free_multi_ssl_backend_data(struct multi_ssl_backend_data *mbackend)
+{
+ if(Curl_ssl->free_multi_ssl_backend_data && mbackend)
+ Curl_ssl->free_multi_ssl_backend_data(mbackend);
+}
+
+void Curl_ssl_close_all(struct Curl_easy *data)
+{
+ /* kill the session ID cache if not shared */
+ if(data->state.session && !SSLSESSION_SHARED(data)) {
+ size_t i;
+ for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++)
+ /* the single-killer function handles empty table slots */
+ Curl_ssl_kill_session(&data->state.session[i]);
+
+ /* free the cache data */
+ Curl_safefree(data->state.session);
+ }
+
+ Curl_ssl->close_all(data);
+}
+
+int Curl_ssl_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+
+ (void)data;
+ if(connssl->connecting_state == ssl_connect_2_writing) {
+ /* write mode */
+ socks[0] = cf->conn->sock[FIRSTSOCKET];
+ return GETSOCK_WRITESOCK(0);
+ }
+ if(connssl->connecting_state == ssl_connect_2_reading) {
+ /* read mode */
+ socks[0] = cf->conn->sock[FIRSTSOCKET];
+ return GETSOCK_READSOCK(0);
+ }
+
+ return GETSOCK_BLANK;
+}
+
+/* Selects an SSL crypto engine
+ */
+CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine)
+{
+ return Curl_ssl->set_engine(data, engine);
+}
+
+/* Selects the default SSL crypto engine
+ */
+CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data)
+{
+ return Curl_ssl->set_engine_default(data);
+}
+
+/* Return list of OpenSSL crypto engine names. */
+struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data)
+{
+ return Curl_ssl->engines_list(data);
+}
+
+/*
+ * This sets up a session ID cache to the specified size. Make sure this code
+ * is agnostic to what underlying SSL technology we use.
+ */
+CURLcode Curl_ssl_initsessions(struct Curl_easy *data, size_t amount)
+{
+ struct Curl_ssl_session *session;
+
+ if(data->state.session)
+ /* this is just a precaution to prevent multiple inits */
+ return CURLE_OK;
+
+ session = calloc(amount, sizeof(struct Curl_ssl_session));
+ if(!session)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* store the info in the SSL section */
+ data->set.general_ssl.max_ssl_sessions = amount;
+ data->state.session = session;
+ data->state.sessionage = 1; /* this is brand new */
+ return CURLE_OK;
+}
+
+static size_t multissl_version(char *buffer, size_t size);
+
+void Curl_ssl_version(char *buffer, size_t size)
+{
+#ifdef CURL_WITH_MULTI_SSL
+ (void)multissl_version(buffer, size);
+#else
+ (void)Curl_ssl->version(buffer, size);
+#endif
+}
+
+/*
+ * This function tries to determine connection status.
+ *
+ * Return codes:
+ * 1 means the connection is still in place
+ * 0 means the connection has been closed
+ * -1 means the connection status is unknown
+ */
+int Curl_ssl_check_cxn(struct Curl_easy *data, struct connectdata *conn)
+{
+ struct Curl_cfilter *cf = Curl_ssl_cf_get_ssl(conn->cfilter[FIRSTSOCKET]);
+ return cf? Curl_ssl->check_cxn(cf, data) : -1;
+}
+
+void Curl_ssl_free_certinfo(struct Curl_easy *data)
+{
+ struct curl_certinfo *ci = &data->info.certs;
+
+ if(ci->num_of_certs) {
+ /* free all individual lists used */
+ int i;
+ for(i = 0; i<ci->num_of_certs; i++) {
+ curl_slist_free_all(ci->certinfo[i]);
+ ci->certinfo[i] = NULL;
+ }
+
+ free(ci->certinfo); /* free the actual array too */
+ ci->certinfo = NULL;
+ ci->num_of_certs = 0;
+ }
+}
+
+CURLcode Curl_ssl_init_certinfo(struct Curl_easy *data, int num)
+{
+ struct curl_certinfo *ci = &data->info.certs;
+ struct curl_slist **table;
+
+ /* Free any previous certificate information structures */
+ Curl_ssl_free_certinfo(data);
+
+ /* Allocate the required certificate information structures */
+ table = calloc((size_t) num, sizeof(struct curl_slist *));
+ if(!table)
+ return CURLE_OUT_OF_MEMORY;
+
+ ci->num_of_certs = num;
+ ci->certinfo = table;
+
+ return CURLE_OK;
+}
+
+/*
+ * 'value' is NOT a null-terminated string
+ */
+CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data,
+ int certnum,
+ const char *label,
+ const char *value,
+ size_t valuelen)
+{
+ struct curl_certinfo *ci = &data->info.certs;
+ char *output;
+ struct curl_slist *nl;
+ CURLcode result = CURLE_OK;
+ size_t labellen = strlen(label);
+ size_t outlen = labellen + 1 + valuelen + 1; /* label:value\0 */
+
+ output = malloc(outlen);
+ if(!output)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* sprintf the label and colon */
+ msnprintf(output, outlen, "%s:", label);
+
+ /* memcpy the value (it might not be null-terminated) */
+ memcpy(&output[labellen + 1], value, valuelen);
+
+ /* null-terminate the output */
+ output[labellen + 1 + valuelen] = 0;
+
+ nl = Curl_slist_append_nodup(ci->certinfo[certnum], output);
+ if(!nl) {
+ free(output);
+ curl_slist_free_all(ci->certinfo[certnum]);
+ result = CURLE_OUT_OF_MEMORY;
+ }
+
+ ci->certinfo[certnum] = nl;
+ return result;
+}
+
+/*
+ * This is a convenience function for push_certinfo_len that takes a zero
+ * terminated value.
+ */
+CURLcode Curl_ssl_push_certinfo(struct Curl_easy *data,
+ int certnum,
+ const char *label,
+ const char *value)
+{
+ size_t valuelen = strlen(value);
+
+ return Curl_ssl_push_certinfo_len(data, certnum, label, value, valuelen);
+}
+
+CURLcode Curl_ssl_random(struct Curl_easy *data,
+ unsigned char *entropy,
+ size_t length)
+{
+ return Curl_ssl->random(data, entropy, length);
+}
+
+/*
+ * Curl_ssl_snihost() converts the input host name to a suitable SNI name put
+ * in data->state.buffer. Returns a pointer to the name (or NULL if a problem)
+ * and stores the new length in 'olen'.
+ *
+ * SNI fields must not have any trailing dot and while RFC 6066 section 3 says
+ * the SNI field is case insensitive, browsers always send the data lowercase
+ * and subsequently there are numerous servers out there that don't work
+ * unless the name is lowercased.
+ */
+
+char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen)
+{
+ size_t len = strlen(host);
+ if(len && (host[len-1] == '.'))
+ len--;
+ if(len >= data->set.buffer_size)
+ return NULL;
+
+ Curl_strntolower(data->state.buffer, host, len);
+ data->state.buffer[len] = 0;
+ if(olen)
+ *olen = len;
+ return data->state.buffer;
+}
+
+/*
+ * Public key pem to der conversion
+ */
+
+static CURLcode pubkey_pem_to_der(const char *pem,
+ unsigned char **der, size_t *der_len)
+{
+ char *stripped_pem, *begin_pos, *end_pos;
+ size_t pem_count, stripped_pem_count = 0, pem_len;
+ CURLcode result;
+
+ /* if no pem, exit. */
+ if(!pem)
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ begin_pos = strstr(pem, "-----BEGIN PUBLIC KEY-----");
+ if(!begin_pos)
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ pem_count = begin_pos - pem;
+ /* Invalid if not at beginning AND not directly following \n */
+ if(0 != pem_count && '\n' != pem[pem_count - 1])
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ /* 26 is length of "-----BEGIN PUBLIC KEY-----" */
+ pem_count += 26;
+
+ /* Invalid if not directly following \n */
+ end_pos = strstr(pem + pem_count, "\n-----END PUBLIC KEY-----");
+ if(!end_pos)
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ pem_len = end_pos - pem;
+
+ stripped_pem = malloc(pem_len - pem_count + 1);
+ if(!stripped_pem)
+ return CURLE_OUT_OF_MEMORY;
+
+ /*
+ * Here we loop through the pem array one character at a time between the
+ * correct indices, and place each character that is not '\n' or '\r'
+ * into the stripped_pem array, which should represent the raw base64 string
+ */
+ while(pem_count < pem_len) {
+ if('\n' != pem[pem_count] && '\r' != pem[pem_count])
+ stripped_pem[stripped_pem_count++] = pem[pem_count];
+ ++pem_count;
+ }
+ /* Place the null terminator in the correct place */
+ stripped_pem[stripped_pem_count] = '\0';
+
+ result = Curl_base64_decode(stripped_pem, der, der_len);
+
+ Curl_safefree(stripped_pem);
+
+ return result;
+}
+
+/*
+ * Generic pinned public key check.
+ */
+
+CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
+ const char *pinnedpubkey,
+ const unsigned char *pubkey, size_t pubkeylen)
+{
+ FILE *fp;
+ unsigned char *buf = NULL, *pem_ptr = NULL;
+ CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+
+ /* if a path wasn't specified, don't pin */
+ if(!pinnedpubkey)
+ return CURLE_OK;
+ if(!pubkey || !pubkeylen)
+ return result;
+
+ /* only do this if pinnedpubkey starts with "sha256//", length 8 */
+ if(strncmp(pinnedpubkey, "sha256//", 8) == 0) {
+ CURLcode encode;
+ size_t encodedlen, pinkeylen;
+ char *encoded, *pinkeycopy, *begin_pos, *end_pos;
+ unsigned char *sha256sumdigest;
+
+ if(!Curl_ssl->sha256sum) {
+ /* without sha256 support, this cannot match */
+ return result;
+ }
+
+ /* compute sha256sum of public key */
+ sha256sumdigest = malloc(CURL_SHA256_DIGEST_LENGTH);
+ if(!sha256sumdigest)
+ return CURLE_OUT_OF_MEMORY;
+ encode = Curl_ssl->sha256sum(pubkey, pubkeylen,
+ sha256sumdigest, CURL_SHA256_DIGEST_LENGTH);
+
+ if(encode != CURLE_OK)
+ return encode;
+
+ encode = Curl_base64_encode((char *)sha256sumdigest,
+ CURL_SHA256_DIGEST_LENGTH, &encoded,
+ &encodedlen);
+ Curl_safefree(sha256sumdigest);
+
+ if(encode)
+ return encode;
+
+ infof(data, " public key hash: sha256//%s", encoded);
+
+ /* it starts with sha256//, copy so we can modify it */
+ pinkeylen = strlen(pinnedpubkey) + 1;
+ pinkeycopy = malloc(pinkeylen);
+ if(!pinkeycopy) {
+ Curl_safefree(encoded);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ memcpy(pinkeycopy, pinnedpubkey, pinkeylen);
+ /* point begin_pos to the copy, and start extracting keys */
+ begin_pos = pinkeycopy;
+ do {
+ end_pos = strstr(begin_pos, ";sha256//");
+ /*
+ * if there is an end_pos, null terminate,
+ * otherwise it'll go to the end of the original string
+ */
+ if(end_pos)
+ end_pos[0] = '\0';
+
+ /* compare base64 sha256 digests, 8 is the length of "sha256//" */
+ if(encodedlen == strlen(begin_pos + 8) &&
+ !memcmp(encoded, begin_pos + 8, encodedlen)) {
+ result = CURLE_OK;
+ break;
+ }
+
+ /*
+ * change back the null-terminator we changed earlier,
+ * and look for next begin
+ */
+ if(end_pos) {
+ end_pos[0] = ';';
+ begin_pos = strstr(end_pos, "sha256//");
+ }
+ } while(end_pos && begin_pos);
+ Curl_safefree(encoded);
+ Curl_safefree(pinkeycopy);
+ return result;
+ }
+
+ fp = fopen(pinnedpubkey, "rb");
+ if(!fp)
+ return result;
+
+ do {
+ long filesize;
+ size_t size, pem_len;
+ CURLcode pem_read;
+
+ /* Determine the file's size */
+ if(fseek(fp, 0, SEEK_END))
+ break;
+ filesize = ftell(fp);
+ if(fseek(fp, 0, SEEK_SET))
+ break;
+ if(filesize < 0 || filesize > MAX_PINNED_PUBKEY_SIZE)
+ break;
+
+ /*
+ * if the size of our certificate is bigger than the file
+ * size then it can't match
+ */
+ size = curlx_sotouz((curl_off_t) filesize);
+ if(pubkeylen > size)
+ break;
+
+ /*
+ * Allocate buffer for the pinned key
+ * With 1 additional byte for null terminator in case of PEM key
+ */
+ buf = malloc(size + 1);
+ if(!buf)
+ break;
+
+ /* Returns number of elements read, which should be 1 */
+ if((int) fread(buf, size, 1, fp) != 1)
+ break;
+
+ /* If the sizes are the same, it can't be base64 encoded, must be der */
+ if(pubkeylen == size) {
+ if(!memcmp(pubkey, buf, pubkeylen))
+ result = CURLE_OK;
+ break;
+ }
+
+ /*
+ * Otherwise we will assume it's PEM and try to decode it
+ * after placing null terminator
+ */
+ buf[size] = '\0';
+ pem_read = pubkey_pem_to_der((const char *)buf, &pem_ptr, &pem_len);
+ /* if it wasn't read successfully, exit */
+ if(pem_read)
+ break;
+
+ /*
+ * if the size of our certificate doesn't match the size of
+ * the decoded file, they can't be the same, otherwise compare
+ */
+ if(pubkeylen == pem_len && !memcmp(pubkey, pem_ptr, pubkeylen))
+ result = CURLE_OK;
+ } while(0);
+
+ Curl_safefree(buf);
+ Curl_safefree(pem_ptr);
+ fclose(fp);
+
+ return result;
+}
+
+/*
+ * Check whether the SSL backend supports the status_request extension.
+ */
+bool Curl_ssl_cert_status_request(void)
+{
+ return Curl_ssl->cert_status_request();
+}
+
+/*
+ * Check whether the SSL backend supports false start.
+ */
+bool Curl_ssl_false_start(struct Curl_easy *data)
+{
+ (void)data;
+ return Curl_ssl->false_start();
+}
+
+/*
+ * Default implementations for unsupported functions.
+ */
+
+int Curl_none_init(void)
+{
+ return 1;
+}
+
+void Curl_none_cleanup(void)
+{ }
+
+int Curl_none_shutdown(struct Curl_cfilter *cf UNUSED_PARAM,
+ struct Curl_easy *data UNUSED_PARAM)
+{
+ (void)data;
+ (void)cf;
+ return 0;
+}
+
+int Curl_none_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ (void)cf;
+ (void)data;
+ return -1;
+}
+
+CURLcode Curl_none_random(struct Curl_easy *data UNUSED_PARAM,
+ unsigned char *entropy UNUSED_PARAM,
+ size_t length UNUSED_PARAM)
+{
+ (void)data;
+ (void)entropy;
+ (void)length;
+ return CURLE_NOT_BUILT_IN;
+}
+
+void Curl_none_close_all(struct Curl_easy *data UNUSED_PARAM)
+{
+ (void)data;
+}
+
+void Curl_none_session_free(void *ptr UNUSED_PARAM)
+{
+ (void)ptr;
+}
+
+bool Curl_none_data_pending(struct Curl_cfilter *cf UNUSED_PARAM,
+ const struct Curl_easy *data UNUSED_PARAM)
+{
+ (void)cf;
+ (void)data;
+ return 0;
+}
+
+bool Curl_none_cert_status_request(void)
+{
+ return FALSE;
+}
+
+CURLcode Curl_none_set_engine(struct Curl_easy *data UNUSED_PARAM,
+ const char *engine UNUSED_PARAM)
+{
+ (void)data;
+ (void)engine;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode Curl_none_set_engine_default(struct Curl_easy *data UNUSED_PARAM)
+{
+ (void)data;
+ return CURLE_NOT_BUILT_IN;
+}
+
+struct curl_slist *Curl_none_engines_list(struct Curl_easy *data UNUSED_PARAM)
+{
+ (void)data;
+ return (struct curl_slist *)NULL;
+}
+
+bool Curl_none_false_start(void)
+{
+ return FALSE;
+}
+
+static int multissl_init(void)
+{
+ if(multissl_setup(NULL))
+ return 1;
+ return Curl_ssl->init();
+}
+
+static CURLcode multissl_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ if(multissl_setup(NULL))
+ return CURLE_FAILED_INIT;
+ return Curl_ssl->connect_blocking(cf, data);
+}
+
+static CURLcode multissl_connect_nonblocking(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
+{
+ if(multissl_setup(NULL))
+ return CURLE_FAILED_INIT;
+ return Curl_ssl->connect_nonblocking(cf, data, done);
+}
+
+static int multissl_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ if(multissl_setup(NULL))
+ return 0;
+ return Curl_ssl->get_select_socks(cf, data, socks);
+}
+
+static void *multissl_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info)
+{
+ if(multissl_setup(NULL))
+ return NULL;
+ return Curl_ssl->get_internals(connssl, info);
+}
+
+static void multissl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ if(multissl_setup(NULL))
+ return;
+ Curl_ssl->close(cf, data);
+}
+
+static ssize_t multissl_recv_plain(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *code)
+{
+ if(multissl_setup(NULL))
+ return CURLE_FAILED_INIT;
+ return Curl_ssl->recv_plain(cf, data, buf, len, code);
+}
+
+static ssize_t multissl_send_plain(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *mem, size_t len,
+ CURLcode *code)
+{
+ if(multissl_setup(NULL))
+ return CURLE_FAILED_INIT;
+ return Curl_ssl->send_plain(cf, data, mem, len, code);
+}
+
+static const struct Curl_ssl Curl_ssl_multi = {
+ { CURLSSLBACKEND_NONE, "multi" }, /* info */
+ 0, /* supports nothing */
+ (size_t)-1, /* something insanely large to be on the safe side */
+
+ multissl_init, /* init */
+ Curl_none_cleanup, /* cleanup */
+ multissl_version, /* version */
+ Curl_none_check_cxn, /* check_cxn */
+ Curl_none_shutdown, /* shutdown */
+ Curl_none_data_pending, /* data_pending */
+ Curl_none_random, /* random */
+ Curl_none_cert_status_request, /* cert_status_request */
+ multissl_connect, /* connect */
+ multissl_connect_nonblocking, /* connect_nonblocking */
+ multissl_get_select_socks, /* getsock */
+ multissl_get_internals, /* get_internals */
+ multissl_close, /* close_one */
+ Curl_none_close_all, /* close_all */
+ Curl_none_session_free, /* session_free */
+ Curl_none_set_engine, /* set_engine */
+ Curl_none_set_engine_default, /* set_engine_default */
+ Curl_none_engines_list, /* engines_list */
+ Curl_none_false_start, /* false_start */
+ NULL, /* sha256sum */
+ NULL, /* associate_connection */
+ NULL, /* disassociate_connection */
+ NULL, /* free_multi_ssl_backend_data */
+ multissl_recv_plain, /* recv decrypted data */
+ multissl_send_plain, /* send data to encrypt */
+};
+
+const struct Curl_ssl *Curl_ssl =
+#if defined(CURL_WITH_MULTI_SSL)
+ &Curl_ssl_multi;
+#elif defined(USE_WOLFSSL)
+ &Curl_ssl_wolfssl;
+#elif defined(USE_SECTRANSP)
+ &Curl_ssl_sectransp;
+#elif defined(USE_GNUTLS)
+ &Curl_ssl_gnutls;
+#elif defined(USE_GSKIT)
+ &Curl_ssl_gskit;
+#elif defined(USE_MBEDTLS)
+ &Curl_ssl_mbedtls;
+#elif defined(USE_NSS)
+ &Curl_ssl_nss;
+#elif defined(USE_RUSTLS)
+ &Curl_ssl_rustls;
+#elif defined(USE_OPENSSL)
+ &Curl_ssl_openssl;
+#elif defined(USE_SCHANNEL)
+ &Curl_ssl_schannel;
+#elif defined(USE_BEARSSL)
+ &Curl_ssl_bearssl;
+#else
+#error "Missing struct Curl_ssl for selected SSL backend"
+#endif
+
+static const struct Curl_ssl *available_backends[] = {
+#if defined(USE_WOLFSSL)
+ &Curl_ssl_wolfssl,
+#endif
+#if defined(USE_SECTRANSP)
+ &Curl_ssl_sectransp,
+#endif
+#if defined(USE_GNUTLS)
+ &Curl_ssl_gnutls,
+#endif
+#if defined(USE_GSKIT)
+ &Curl_ssl_gskit,
+#endif
+#if defined(USE_MBEDTLS)
+ &Curl_ssl_mbedtls,
+#endif
+#if defined(USE_NSS)
+ &Curl_ssl_nss,
+#endif
+#if defined(USE_OPENSSL)
+ &Curl_ssl_openssl,
+#endif
+#if defined(USE_SCHANNEL)
+ &Curl_ssl_schannel,
+#endif
+#if defined(USE_BEARSSL)
+ &Curl_ssl_bearssl,
+#endif
+#if defined(USE_RUSTLS)
+ &Curl_ssl_rustls,
+#endif
+ NULL
+};
+
+static size_t multissl_version(char *buffer, size_t size)
+{
+ static const struct Curl_ssl *selected;
+ static char backends[200];
+ static size_t backends_len;
+ const struct Curl_ssl *current;
+
+ current = Curl_ssl == &Curl_ssl_multi ? available_backends[0] : Curl_ssl;
+
+ if(current != selected) {
+ char *p = backends;
+ char *end = backends + sizeof(backends);
+ int i;
+
+ selected = current;
+
+ backends[0] = '\0';
+
+ for(i = 0; available_backends[i]; ++i) {
+ char vb[200];
+ bool paren = (selected != available_backends[i]);
+
+ if(available_backends[i]->version(vb, sizeof(vb))) {
+ p += msnprintf(p, end - p, "%s%s%s%s", (p != backends ? " " : ""),
+ (paren ? "(" : ""), vb, (paren ? ")" : ""));
+ }
+ }
+
+ backends_len = p - backends;
+ }
+
+ if(!size)
+ return 0;
+
+ if(size <= backends_len) {
+ strncpy(buffer, backends, size - 1);
+ buffer[size - 1] = '\0';
+ return size - 1;
+ }
+
+ strcpy(buffer, backends);
+ return backends_len;
+}
+
+static int multissl_setup(const struct Curl_ssl *backend)
+{
+ const char *env;
+ char *env_tmp;
+
+ if(Curl_ssl != &Curl_ssl_multi)
+ return 1;
+
+ if(backend) {
+ Curl_ssl = backend;
+ return 0;
+ }
+
+ if(!available_backends[0])
+ return 1;
+
+ env = env_tmp = curl_getenv("CURL_SSL_BACKEND");
+#ifdef CURL_DEFAULT_SSL_BACKEND
+ if(!env)
+ env = CURL_DEFAULT_SSL_BACKEND;
+#endif
+ if(env) {
+ int i;
+ for(i = 0; available_backends[i]; i++) {
+ if(strcasecompare(env, available_backends[i]->info.name)) {
+ Curl_ssl = available_backends[i];
+ free(env_tmp);
+ return 0;
+ }
+ }
+ }
+
+ /* Fall back to first available backend */
+ Curl_ssl = available_backends[0];
+ free(env_tmp);
+ return 0;
+}
+
+/* This function is used to select the SSL backend to use. It is called by
+ curl_global_sslset (easy.c) which uses the global init lock. */
+CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
+ const curl_ssl_backend ***avail)
+{
+ int i;
+
+ if(avail)
+ *avail = (const curl_ssl_backend **)&available_backends;
+
+ if(Curl_ssl != &Curl_ssl_multi)
+ return id == Curl_ssl->info.id ||
+ (name && strcasecompare(name, Curl_ssl->info.name)) ?
+ CURLSSLSET_OK :
+#if defined(CURL_WITH_MULTI_SSL)
+ CURLSSLSET_TOO_LATE;
+#else
+ CURLSSLSET_UNKNOWN_BACKEND;
+#endif
+
+ for(i = 0; available_backends[i]; i++) {
+ if(available_backends[i]->info.id == id ||
+ (name && strcasecompare(available_backends[i]->info.name, name))) {
+ multissl_setup(available_backends[i]);
+ return CURLSSLSET_OK;
+ }
+ }
+
+ return CURLSSLSET_UNKNOWN_BACKEND;
+}
+
+#else /* USE_SSL */
+CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
+ const curl_ssl_backend ***avail)
+{
+ (void)id;
+ (void)name;
+ (void)avail;
+ return CURLSSLSET_NO_BACKENDS;
+}
+
+#endif /* !USE_SSL */
+
+#ifdef USE_SSL
+
+static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ /* TODO: close_one closes BOTH conn->ssl AND conn->proxy_ssl for this
+ * sockindex (if in use). Gladly, it is safe to call more than once. */
+ if(connssl) {
+ Curl_ssl->close(cf, data);
+ connssl->state = ssl_connection_none;
+ }
+ cf->connected = FALSE;
+}
+
+static void reinit_hostname(struct Curl_cfilter *cf)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+
+#ifndef CURL_DISABLE_PROXY
+ if(Curl_ssl_cf_is_proxy(cf)) {
+ /* TODO: there is not definition for a proxy setup on a secondary conn */
+ connssl->hostname = cf->conn->http_proxy.host.name;
+ connssl->dispname = cf->conn->http_proxy.host.dispname;
+ connssl->port = cf->conn->http_proxy.port;
+ }
+ else
+#endif
+ {
+ /* TODO: secondaryhostname is set to the IP address we connect to
+ * in the FTP handler, it is assumed that host verification uses the
+ * hostname from FIRSTSOCKET */
+ if(cf->sockindex == SECONDARYSOCKET && 0) {
+ connssl->hostname = cf->conn->secondaryhostname;
+ connssl->dispname = connssl->hostname;
+ connssl->port = cf->conn->secondary_port;
+ }
+ else {
+ connssl->hostname = cf->conn->host.name;
+ connssl->dispname = cf->conn->host.dispname;
+ connssl->port = cf->conn->remote_port;
+ }
+ }
+ DEBUGASSERT(connssl->hostname);
+}
+
+static void ssl_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ cf_ctx_set_data(cf, data);
+ cf_close(cf, data);
+ cf_ctx_free(cf->ctx);
+ cf->ctx = NULL;
+}
+
+static void ssl_cf_close(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ cf_ctx_set_data(cf, data);
+ cf_close(cf, data);
+ cf->next->cft->close(cf->next, data);
+ cf_ctx_set_data(cf, NULL);
+}
+
+static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ CURLcode result;
+
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ cf_ctx_set_data(cf, data);
+ (void)connssl;
+ DEBUGASSERT(data->conn);
+ DEBUGASSERT(data->conn == cf->conn);
+ DEBUGASSERT(connssl);
+ DEBUGASSERT(cf->conn->host.name);
+
+ result = cf->next->cft->connect(cf->next, data, blocking, done);
+ if(result || !*done)
+ goto out;
+
+ /* TODO: right now we do not fully control when hostname is set,
+ * assign it on each connect call. */
+ reinit_hostname(cf);
+ *done = FALSE;
+
+ if(blocking) {
+ result = ssl_connect(cf, data);
+ *done = (result == CURLE_OK);
+ }
+ else {
+ result = ssl_connect_nonblocking(cf, data, done);
+ }
+
+ if(!result && *done) {
+ cf->connected = TRUE;
+ if(cf->sockindex == FIRSTSOCKET && !Curl_ssl_cf_is_proxy(cf))
+ Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSL is connected */
+ DEBUGASSERT(connssl->state == ssl_connection_complete);
+ }
+out:
+ cf_ctx_set_data(cf, NULL);
+ return result;
+}
+
+static bool ssl_cf_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ bool result;
+
+ cf_ctx_set_data(cf, (struct Curl_easy *)data);
+ if(cf->ctx && Curl_ssl->data_pending(cf, data))
+ result = TRUE;
+ else
+ result = cf->next->cft->has_data_pending(cf->next, data);
+ cf_ctx_set_data(cf, NULL);
+ return result;
+}
+
+static ssize_t ssl_cf_send(struct Curl_cfilter *cf,
+ struct Curl_easy *data, const void *buf, size_t len,
+ CURLcode *err)
+{
+ ssize_t nwritten;
+
+ *err = CURLE_OK;
+ cf_ctx_set_data(cf, data);
+ nwritten = Curl_ssl->send_plain(cf, data, buf, len, err);
+ cf_ctx_set_data(cf, NULL);
+ return nwritten;
+}
+
+static ssize_t ssl_cf_recv(struct Curl_cfilter *cf,
+ struct Curl_easy *data, char *buf, size_t len,
+ CURLcode *err)
+{
+ ssize_t nread;
+
+ *err = CURLE_OK;
+ cf_ctx_set_data(cf, data);
+ nread = Curl_ssl->recv_plain(cf, data, buf, len, err);
+ cf_ctx_set_data(cf, NULL);
+ return nread;
+}
+
+static int ssl_cf_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ int result;
+
+ cf_ctx_set_data(cf, data);
+ result = Curl_ssl->get_select_socks(cf, data, socks);
+ cf_ctx_set_data(cf, NULL);
+ return result;
+}
+
+static void ssl_cf_attach_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ if(Curl_ssl->attach_data) {
+ cf_ctx_set_data(cf, data);
+ Curl_ssl->attach_data(cf, data);
+ cf_ctx_set_data(cf, NULL);
+ }
+}
+
+static void ssl_cf_detach_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ if(Curl_ssl->detach_data) {
+ cf_ctx_set_data(cf, data);
+ Curl_ssl->detach_data(cf, data);
+ cf_ctx_set_data(cf, NULL);
+ }
+}
+
+static const struct Curl_cftype cft_ssl = {
+ "SSL",
+ CF_TYPE_SSL,
+ ssl_cf_destroy,
+ Curl_cf_def_setup,
+ ssl_cf_connect,
+ ssl_cf_close,
+ Curl_cf_def_get_host,
+ ssl_cf_get_select_socks,
+ ssl_cf_data_pending,
+ ssl_cf_send,
+ ssl_cf_recv,
+ ssl_cf_attach_data,
+ ssl_cf_detach_data,
+};
+
+static const struct Curl_cftype cft_ssl_proxy = {
+ "SSL-PROXY",
+ CF_TYPE_SSL,
+ ssl_cf_destroy,
+ Curl_cf_def_setup,
+ ssl_cf_connect,
+ ssl_cf_close,
+ Curl_cf_def_get_host,
+ ssl_cf_get_select_socks,
+ ssl_cf_data_pending,
+ ssl_cf_send,
+ ssl_cf_recv,
+ ssl_cf_attach_data,
+ ssl_cf_detach_data,
+};
+
+CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf;
+ struct ssl_connect_data *ctx;
+ CURLcode result;
+
+ DEBUGASSERT(data->conn);
+ ctx = cf_ctx_new(data);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ result = Curl_cf_create(&cf, &cft_ssl, ctx);
+ if(result)
+ goto out;
+
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+
+ result = CURLE_OK;
+
+out:
+ if(result)
+ cf_ctx_free(ctx);
+ return result;
+}
+
+#ifndef CURL_DISABLE_PROXY
+CURLcode Curl_ssl_cfilter_proxy_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf;
+ struct ssl_connect_data *ctx;
+ CURLcode result;
+
+ ctx = cf_ctx_new(data);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ result = Curl_cf_create(&cf, &cft_ssl_proxy, ctx);
+ if(result)
+ goto out;
+
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+
+ result = CURLE_OK;
+
+out:
+ if(result)
+ cf_ctx_free(ctx);
+ return result;
+}
+
+#endif /* !CURL_DISABLE_PROXY */
+
+bool Curl_ssl_supports(struct Curl_easy *data, int option)
+{
+ (void)data;
+ return (Curl_ssl->supports & option)? TRUE : FALSE;
+}
+
+void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
+ CURLINFO info, int n)
+{
+ void *result = NULL;
+ (void)n;
+ if(data->conn) {
+ struct Curl_cfilter *cf;
+ /* get first filter in chain, if any is present */
+ cf = Curl_ssl_cf_get_ssl(data->conn->cfilter[sockindex]);
+ if(cf) {
+ cf_ctx_set_data(cf, data);
+ result = Curl_ssl->get_internals(cf->ctx, info);
+ cf_ctx_set_data(cf, NULL);
+ }
+ }
+ return result;
+}
+
+CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
+ int sockindex)
+{
+ struct Curl_cfilter *cf = data->conn? data->conn->cfilter[sockindex] : NULL;
+ CURLcode result = CURLE_OK;
+
+ (void)data;
+ for(; cf; cf = cf->next) {
+ if(cf->cft == &cft_ssl) {
+ if(Curl_ssl->shut_down(cf, data))
+ result = CURLE_SSL_SHUTDOWN_FAILED;
+ Curl_conn_cf_discard(cf, data);
+ break;
+ }
+ }
+ return result;
+}
+
+static struct Curl_cfilter *get_ssl_cf_engaged(struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf, *lowest_ssl_cf = NULL;
+
+ for(cf = conn->cfilter[sockindex]; cf; cf = cf->next) {
+ if(cf->cft == &cft_ssl || cf->cft == &cft_ssl_proxy) {
+ lowest_ssl_cf = cf;
+ if(cf->connected || (cf->next && cf->next->connected)) {
+ /* connected or about to start */
+ return cf;
+ }
+ }
+ }
+ return lowest_ssl_cf;
+}
+
+bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf)
+{
+ return (cf->cft == &cft_ssl_proxy);
+}
+
+struct ssl_config_data *
+Curl_ssl_cf_get_config(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+#ifdef CURL_DISABLE_PROXY
+ (void)cf;
+ return &data->set.ssl;
+#else
+ return Curl_ssl_cf_is_proxy(cf)? &data->set.proxy_ssl : &data->set.ssl;
+#endif
+}
+
+struct ssl_config_data *
+Curl_ssl_get_config(struct Curl_easy *data, int sockindex)
+{
+ struct Curl_cfilter *cf;
+
+ (void)data;
+ DEBUGASSERT(data->conn);
+ cf = get_ssl_cf_engaged(data->conn, sockindex);
+ return cf? Curl_ssl_cf_get_config(cf, data) : &data->set.ssl;
+}
+
+struct ssl_primary_config *
+Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf)
+{
+#ifdef CURL_DISABLE_PROXY
+ return &cf->conn->ssl_config;
+#else
+ return Curl_ssl_cf_is_proxy(cf)?
+ &cf->conn->proxy_ssl_config : &cf->conn->ssl_config;
+#endif
+}
+
+struct ssl_primary_config *
+Curl_ssl_get_primary_config(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf;
+
+ (void)data;
+ DEBUGASSERT(conn);
+ cf = get_ssl_cf_engaged(conn, sockindex);
+ return cf? Curl_ssl_cf_get_primary_config(cf) : NULL;
+}
+
+struct Curl_cfilter *Curl_ssl_cf_get_ssl(struct Curl_cfilter *cf)
+{
+ for(; cf; cf = cf->next) {
+ if(cf->cft == &cft_ssl || cf->cft == &cft_ssl_proxy)
+ return cf;
+ }
+ return NULL;
+}
+
+#endif /* USE_SSL */
diff --git a/libs/libcurl/src/vtls/vtls.h b/libs/libcurl/src/vtls/vtls.h index 50c53b3fbd..0bd5a27d1e 100644 --- a/libs/libcurl/src/vtls/vtls.h +++ b/libs/libcurl/src/vtls/vtls.h @@ -1,344 +1,235 @@ -#ifndef HEADER_CURL_VTLS_H -#define HEADER_CURL_VTLS_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 connectdata; -struct ssl_connect_data; - -#define SSLSUPP_CA_PATH (1<<0) /* supports CAPATH */ -#define SSLSUPP_CERTINFO (1<<1) /* supports CURLOPT_CERTINFO */ -#define SSLSUPP_PINNEDPUBKEY (1<<2) /* supports CURLOPT_PINNEDPUBLICKEY */ -#define SSLSUPP_SSL_CTX (1<<3) /* supports CURLOPT_SSL_CTX */ -#define SSLSUPP_HTTPS_PROXY (1<<4) /* supports access via HTTPS proxies */ -#define SSLSUPP_TLS13_CIPHERSUITES (1<<5) /* supports TLS 1.3 ciphersuites */ -#define SSLSUPP_CAINFO_BLOB (1<<6) - -#define ALPN_ACCEPTED "ALPN: server accepted " - -#define VTLS_INFOF_NO_ALPN \ - "ALPN: server did not agree on a protocol. Uses default." -#define VTLS_INFOF_ALPN_OFFER_1STR \ - "ALPN: offers %s" -#define VTLS_INFOF_ALPN_ACCEPTED_1STR \ - ALPN_ACCEPTED "%s" -#define VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR \ - ALPN_ACCEPTED "%.*s" - -struct Curl_ssl { - /* - * This *must* be the first entry to allow returning the list of available - * backends in curl_global_sslset(). - */ - curl_ssl_backend info; - unsigned int supports; /* bitfield, see above */ - size_t sizeof_ssl_backend_data; - - int (*init)(void); - void (*cleanup)(void); - - size_t (*version)(char *buffer, size_t size); - int (*check_cxn)(struct connectdata *cxn); - int (*shut_down)(struct Curl_easy *data, struct connectdata *conn, - int sockindex); - bool (*data_pending)(const struct connectdata *conn, - int connindex); - - /* return 0 if a find random is filled in */ - CURLcode (*random)(struct Curl_easy *data, unsigned char *entropy, - size_t length); - bool (*cert_status_request)(void); - - CURLcode (*connect_blocking)(struct Curl_easy *data, - struct connectdata *conn, int sockindex); - CURLcode (*connect_nonblocking)(struct Curl_easy *data, - struct connectdata *conn, int sockindex, - bool *done); - - /* If the SSL backend wants to read or write on this connection during a - handshake, set socks[0] to the connection's FIRSTSOCKET, and return - a bitmap indicating read or write with GETSOCK_WRITESOCK(0) or - GETSOCK_READSOCK(0). Otherwise return GETSOCK_BLANK. - Mandatory. */ - int (*getsock)(struct connectdata *conn, curl_socket_t *socks); - - void *(*get_internals)(struct ssl_connect_data *connssl, CURLINFO info); - void (*close_one)(struct Curl_easy *data, struct connectdata *conn, - int sockindex); - void (*close_all)(struct Curl_easy *data); - void (*session_free)(void *ptr); - - CURLcode (*set_engine)(struct Curl_easy *data, const char *engine); - CURLcode (*set_engine_default)(struct Curl_easy *data); - struct curl_slist *(*engines_list)(struct Curl_easy *data); - - bool (*false_start)(void); - CURLcode (*sha256sum)(const unsigned char *input, size_t inputlen, - unsigned char *sha256sum, size_t sha256sumlen); - - bool (*associate_connection)(struct Curl_easy *data, - struct connectdata *conn, - int sockindex); - void (*disassociate_connection)(struct Curl_easy *data, int sockindex); -}; - -#ifdef USE_SSL -extern const struct Curl_ssl *Curl_ssl; -#endif - -int Curl_none_init(void); -void Curl_none_cleanup(void); -int Curl_none_shutdown(struct Curl_easy *data, struct connectdata *conn, - int sockindex); -int Curl_none_check_cxn(struct connectdata *conn); -CURLcode Curl_none_random(struct Curl_easy *data, unsigned char *entropy, - size_t length); -void Curl_none_close_all(struct Curl_easy *data); -void Curl_none_session_free(void *ptr); -bool Curl_none_data_pending(const struct connectdata *conn, int connindex); -bool Curl_none_cert_status_request(void); -CURLcode Curl_none_set_engine(struct Curl_easy *data, const char *engine); -CURLcode Curl_none_set_engine_default(struct Curl_easy *data); -struct curl_slist *Curl_none_engines_list(struct Curl_easy *data); -bool Curl_none_false_start(void); -bool Curl_ssl_tls13_ciphersuites(void); - -CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, - const curl_ssl_backend ***avail); - -#include "openssl.h" /* OpenSSL versions */ -#include "gtls.h" /* GnuTLS versions */ -#include "nssg.h" /* NSS versions */ -#include "gskit.h" /* Global Secure ToolKit versions */ -#include "wolfssl.h" /* wolfSSL versions */ -#include "schannel.h" /* Schannel SSPI version */ -#include "sectransp.h" /* SecureTransport (Darwin) version */ -#include "mbedtls.h" /* mbedTLS versions */ -#include "bearssl.h" /* BearSSL versions */ -#include "rustls.h" /* rustls versions */ - -#ifndef MAX_PINNED_PUBKEY_SIZE -#define MAX_PINNED_PUBKEY_SIZE 1048576 /* 1MB */ -#endif - -#ifndef CURL_SHA256_DIGEST_LENGTH -#define CURL_SHA256_DIGEST_LENGTH 32 /* fixed size */ -#endif - -/* see https://www.iana.org/assignments/tls-extensiontype-values/ */ -#define ALPN_HTTP_1_1_LENGTH 8 -#define ALPN_HTTP_1_1 "http/1.1" -#define ALPN_H2_LENGTH 2 -#define ALPN_H2 "h2" - -/* set of helper macros for the backends to access the correct fields. For the - proxy or for the remote host - to properly support HTTPS proxy */ -#ifndef CURL_DISABLE_PROXY -#define SSL_IS_PROXY() \ - (CURLPROXY_HTTPS == conn->http_proxy.proxytype && \ - ssl_connection_complete != \ - conn->proxy_ssl[conn->sock[SECONDARYSOCKET] == \ - CURL_SOCKET_BAD ? FIRSTSOCKET : SECONDARYSOCKET].state) -#define SSL_SET_OPTION(var) \ - (SSL_IS_PROXY() ? data->set.proxy_ssl.var : data->set.ssl.var) -#define SSL_SET_OPTION_LVALUE(var) \ - (*(SSL_IS_PROXY() ? &data->set.proxy_ssl.var : &data->set.ssl.var)) -#define SSL_CONN_CONFIG(var) \ - (SSL_IS_PROXY() ? conn->proxy_ssl_config.var : conn->ssl_config.var) -#define SSL_HOST_NAME() \ - (SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name) -#define SSL_HOST_DISPNAME() \ - (SSL_IS_PROXY() ? conn->http_proxy.host.dispname : conn->host.dispname) -#define SSL_HOST_PORT() \ - (SSL_IS_PROXY() ? conn->port : conn->remote_port) -#define SSL_PINNED_PUB_KEY() (SSL_IS_PROXY() \ - ? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] \ - : data->set.str[STRING_SSL_PINNEDPUBLICKEY]) -#else -#define SSL_IS_PROXY() FALSE -#define SSL_SET_OPTION(var) data->set.ssl.var -#define SSL_SET_OPTION_LVALUE(var) data->set.ssl.var -#define SSL_CONN_CONFIG(var) conn->ssl_config.var -#define SSL_HOST_NAME() conn->host.name -#define SSL_HOST_DISPNAME() conn->host.dispname -#define SSL_HOST_PORT() conn->remote_port -#define SSL_PINNED_PUB_KEY() \ - data->set.str[STRING_SSL_PINNEDPUBLICKEY] -#endif - -char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen); -bool Curl_ssl_config_matches(struct ssl_primary_config *data, - struct ssl_primary_config *needle); -bool Curl_clone_primary_ssl_config(struct ssl_primary_config *source, - struct ssl_primary_config *dest); -void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc); -/* An implementation of the getsock field of Curl_ssl that relies - on the ssl_connect_state enum. Asks for read or write depending - on whether conn->state is ssl_connect_2_reading or - ssl_connect_2_writing. */ -int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks); - -curl_sslbackend Curl_ssl_backend(void); - -#ifdef USE_SSL -int Curl_ssl_init(void); -void Curl_ssl_cleanup(void); -CURLcode Curl_ssl_connect(struct Curl_easy *data, struct connectdata *conn, - int sockindex); -CURLcode Curl_ssl_connect_nonblocking(struct Curl_easy *data, - struct connectdata *conn, - bool isproxy, - int sockindex, - bool *done); -/* tell the SSL stuff to close down all open information regarding - connections (and thus session ID caching etc) */ -void Curl_ssl_close_all(struct Curl_easy *data); -void Curl_ssl_close(struct Curl_easy *data, struct connectdata *conn, - int sockindex); -CURLcode Curl_ssl_shutdown(struct Curl_easy *data, struct connectdata *conn, - int sockindex); -CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine); -/* Sets engine as default for all SSL operations */ -CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data); -struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data); - -/* init the SSL session ID cache */ -CURLcode Curl_ssl_initsessions(struct Curl_easy *, size_t); -void Curl_ssl_version(char *buffer, size_t size); -bool Curl_ssl_data_pending(const struct connectdata *conn, - int connindex); -int Curl_ssl_check_cxn(struct connectdata *conn); - -/* Certificate information list handling. */ - -void Curl_ssl_free_certinfo(struct Curl_easy *data); -CURLcode Curl_ssl_init_certinfo(struct Curl_easy *data, int num); -CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data, int certnum, - const char *label, const char *value, - size_t valuelen); -CURLcode Curl_ssl_push_certinfo(struct Curl_easy *data, int certnum, - const char *label, const char *value); - -/* Functions to be used by SSL library adaptation functions */ - -/* Lock session cache mutex. - * Call this before calling other Curl_ssl_*session* functions - * Caller should unlock this mutex as soon as possible, as it may block - * other SSL connection from making progress. - * The purpose of explicitly locking SSL session cache data is to allow - * individual SSL engines to manage session lifetime in their specific way. - */ -void Curl_ssl_sessionid_lock(struct Curl_easy *data); - -/* Unlock session cache mutex */ -void Curl_ssl_sessionid_unlock(struct Curl_easy *data); - -/* extract a session ID - * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). - * Caller must make sure that the ownership of returned sessionid object - * is properly taken (e.g. its refcount is incremented - * under sessionid mutex). - */ -bool Curl_ssl_getsessionid(struct Curl_easy *data, - struct connectdata *conn, - const bool isProxy, - void **ssl_sessionid, - size_t *idsize, /* set 0 if unknown */ - int sockindex); -/* add a new session ID - * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). - * Caller must ensure that it has properly shared ownership of this sessionid - * object with cache (e.g. incrementing refcount on success) - */ -CURLcode Curl_ssl_addsessionid(struct Curl_easy *data, - struct connectdata *conn, - const bool isProxy, - void *ssl_sessionid, - size_t idsize, - int sockindex, - bool *added); -/* Kill a single session ID entry in the cache - * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). - * This will call engine-specific curlssl_session_free function, which must - * take sessionid object ownership from sessionid cache - * (e.g. decrement refcount). - */ -void Curl_ssl_kill_session(struct Curl_ssl_session *session); -/* delete a session from the cache - * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). - * This will call engine-specific curlssl_session_free function, which must - * take sessionid object ownership from sessionid cache - * (e.g. decrement refcount). - */ -void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid); - -/* get N random bytes into the buffer */ -CURLcode Curl_ssl_random(struct Curl_easy *data, unsigned char *buffer, - size_t length); -/* Check pinned public key. */ -CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, - const char *pinnedpubkey, - const unsigned char *pubkey, size_t pubkeylen); - -bool Curl_ssl_cert_status_request(void); - -bool Curl_ssl_false_start(void); - -void Curl_ssl_associate_conn(struct Curl_easy *data, - struct connectdata *conn); -void Curl_ssl_detach_conn(struct Curl_easy *data, - struct connectdata *conn); - -#define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */ - -#else /* if not USE_SSL */ - -/* When SSL support is not present, just define away these function calls */ -#define Curl_ssl_init() 1 -#define Curl_ssl_cleanup() Curl_nop_stmt -#define Curl_ssl_connect(x,y,z) CURLE_NOT_BUILT_IN -#define Curl_ssl_close_all(x) Curl_nop_stmt -#define Curl_ssl_close(x,y,z) Curl_nop_stmt -#define Curl_ssl_shutdown(x,y,z) CURLE_NOT_BUILT_IN -#define Curl_ssl_set_engine(x,y) CURLE_NOT_BUILT_IN -#define Curl_ssl_set_engine_default(x) CURLE_NOT_BUILT_IN -#define Curl_ssl_engines_list(x) NULL -#define Curl_ssl_send(a,b,c,d,e) -1 -#define Curl_ssl_recv(a,b,c,d,e) -1 -#define Curl_ssl_initsessions(x,y) CURLE_OK -#define Curl_ssl_data_pending(x,y) 0 -#define Curl_ssl_check_cxn(x) 0 -#define Curl_ssl_free_certinfo(x) Curl_nop_stmt -#define Curl_ssl_connect_nonblocking(x,y,z,w,a) CURLE_NOT_BUILT_IN -#define Curl_ssl_kill_session(x) Curl_nop_stmt -#define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN) -#define Curl_ssl_cert_status_request() FALSE -#define Curl_ssl_false_start() FALSE -#define Curl_ssl_tls13_ciphersuites() FALSE -#define Curl_ssl_associate_conn(a,b) Curl_nop_stmt -#define Curl_ssl_detach_conn(a,b) Curl_nop_stmt -#endif - -#endif /* HEADER_CURL_VTLS_H */ +#ifndef HEADER_CURL_VTLS_H
+#define HEADER_CURL_VTLS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 connectdata;
+struct ssl_config_data;
+struct ssl_connect_data;
+struct ssl_primary_config;
+struct Curl_ssl_session;
+
+#define SSLSUPP_CA_PATH (1<<0) /* supports CAPATH */
+#define SSLSUPP_CERTINFO (1<<1) /* supports CURLOPT_CERTINFO */
+#define SSLSUPP_PINNEDPUBKEY (1<<2) /* supports CURLOPT_PINNEDPUBLICKEY */
+#define SSLSUPP_SSL_CTX (1<<3) /* supports CURLOPT_SSL_CTX */
+#define SSLSUPP_HTTPS_PROXY (1<<4) /* supports access via HTTPS proxies */
+#define SSLSUPP_TLS13_CIPHERSUITES (1<<5) /* supports TLS 1.3 ciphersuites */
+#define SSLSUPP_CAINFO_BLOB (1<<6)
+
+#define ALPN_ACCEPTED "ALPN: server accepted "
+
+#define VTLS_INFOF_NO_ALPN \
+ "ALPN: server did not agree on a protocol. Uses default."
+#define VTLS_INFOF_ALPN_OFFER_1STR \
+ "ALPN: offers %s"
+#define VTLS_INFOF_ALPN_ACCEPTED_1STR \
+ ALPN_ACCEPTED "%s"
+#define VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR \
+ ALPN_ACCEPTED "%.*s"
+
+/* Curl_multi SSL backend-specific data; declared differently by each SSL
+ backend */
+struct multi_ssl_backend_data;
+
+CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
+ const curl_ssl_backend ***avail);
+
+#ifndef MAX_PINNED_PUBKEY_SIZE
+#define MAX_PINNED_PUBKEY_SIZE 1048576 /* 1MB */
+#endif
+
+#ifndef CURL_SHA256_DIGEST_LENGTH
+#define CURL_SHA256_DIGEST_LENGTH 32 /* fixed size */
+#endif
+
+/* see https://www.iana.org/assignments/tls-extensiontype-values/ */
+#define ALPN_HTTP_1_1_LENGTH 8
+#define ALPN_HTTP_1_1 "http/1.1"
+#define ALPN_H2_LENGTH 2
+#define ALPN_H2 "h2"
+
+
+char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen);
+bool Curl_ssl_config_matches(struct ssl_primary_config *data,
+ struct ssl_primary_config *needle);
+bool Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
+ struct ssl_primary_config *dest);
+void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc);
+
+curl_sslbackend Curl_ssl_backend(void);
+
+#ifdef USE_SSL
+int Curl_ssl_init(void);
+void Curl_ssl_cleanup(void);
+/* tell the SSL stuff to close down all open information regarding
+ connections (and thus session ID caching etc) */
+void Curl_ssl_close_all(struct Curl_easy *data);
+CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine);
+/* Sets engine as default for all SSL operations */
+CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data);
+struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data);
+
+/* init the SSL session ID cache */
+CURLcode Curl_ssl_initsessions(struct Curl_easy *, size_t);
+void Curl_ssl_version(char *buffer, size_t size);
+int Curl_ssl_check_cxn(struct Curl_easy *data, struct connectdata *conn);
+
+/* Certificate information list handling. */
+
+void Curl_ssl_free_certinfo(struct Curl_easy *data);
+CURLcode Curl_ssl_init_certinfo(struct Curl_easy *data, int num);
+CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data, int certnum,
+ const char *label, const char *value,
+ size_t valuelen);
+CURLcode Curl_ssl_push_certinfo(struct Curl_easy *data, int certnum,
+ const char *label, const char *value);
+
+/* Functions to be used by SSL library adaptation functions */
+
+/* Lock session cache mutex.
+ * Call this before calling other Curl_ssl_*session* functions
+ * Caller should unlock this mutex as soon as possible, as it may block
+ * other SSL connection from making progress.
+ * The purpose of explicitly locking SSL session cache data is to allow
+ * individual SSL engines to manage session lifetime in their specific way.
+ */
+void Curl_ssl_sessionid_lock(struct Curl_easy *data);
+
+/* Unlock session cache mutex */
+void Curl_ssl_sessionid_unlock(struct Curl_easy *data);
+
+/* Kill a single session ID entry in the cache
+ * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
+ * This will call engine-specific curlssl_session_free function, which must
+ * take sessionid object ownership from sessionid cache
+ * (e.g. decrement refcount).
+ */
+void Curl_ssl_kill_session(struct Curl_ssl_session *session);
+/* delete a session from the cache
+ * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
+ * This will call engine-specific curlssl_session_free function, which must
+ * take sessionid object ownership from sessionid cache
+ * (e.g. decrement refcount).
+ */
+void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid);
+
+/* get N random bytes into the buffer */
+CURLcode Curl_ssl_random(struct Curl_easy *data, unsigned char *buffer,
+ size_t length);
+/* Check pinned public key. */
+CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
+ const char *pinnedpubkey,
+ const unsigned char *pubkey, size_t pubkeylen);
+
+bool Curl_ssl_cert_status_request(void);
+
+bool Curl_ssl_false_start(struct Curl_easy *data);
+
+void Curl_free_multi_ssl_backend_data(struct multi_ssl_backend_data *mbackend);
+
+#define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */
+
+CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex);
+
+CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
+ int sockindex);
+
+#ifndef CURL_DISABLE_PROXY
+CURLcode Curl_ssl_cfilter_proxy_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex);
+#endif /* !CURL_DISABLE_PROXY */
+
+/**
+ * Get the SSL configuration that is used on the connection.
+ * This returns NULL if no SSL is configured.
+ * Otherwise it returns the config of the first (highest) one that is
+ * either connected, in handshake or about to start
+ * (e.g. all filters below it are connected). If SSL filters are present,
+ * but neither can start operating, return the config of the lowest one
+ * that will first come into effect when connecting.
+ */
+struct ssl_config_data *Curl_ssl_get_config(struct Curl_easy *data,
+ int sockindex);
+
+/**
+ * Get the primary SSL configuration from the connection.
+ * This returns NULL if no SSL is configured.
+ * Otherwise it returns the config of the first (highest) one that is
+ * either connected, in handshake or about to start
+ * (e.g. all filters below it are connected). If SSL filters are present,
+ * but neither can start operating, return the config of the lowest one
+ * that will first come into effect when connecting.
+ */
+struct ssl_primary_config *
+Curl_ssl_get_primary_config(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex);
+
+/**
+ * True iff the underlying SSL implementation supports the option.
+ * Option is one of the defined SSLSUPP_* values.
+ * `data` maybe NULL for the features of the default implementation.
+ */
+bool Curl_ssl_supports(struct Curl_easy *data, int ssl_option);
+
+/**
+ * Get the internal ssl instance (like OpenSSL's SSL*) from the filter
+ * chain at `sockindex` of type specified by `info`.
+ * For `n` == 0, the first active (top down) instance is returned.
+ * 1 gives the second active, etc.
+ * NULL is returned when no active SSL filter is present.
+ */
+void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
+ CURLINFO info, int n);
+
+#else /* if not USE_SSL */
+
+/* When SSL support is not present, just define away these function calls */
+#define Curl_ssl_init() 1
+#define Curl_ssl_cleanup() Curl_nop_stmt
+#define Curl_ssl_close_all(x) Curl_nop_stmt
+#define Curl_ssl_set_engine(x,y) CURLE_NOT_BUILT_IN
+#define Curl_ssl_set_engine_default(x) CURLE_NOT_BUILT_IN
+#define Curl_ssl_engines_list(x) NULL
+#define Curl_ssl_initsessions(x,y) CURLE_OK
+#define Curl_ssl_check_cxn(d,x) 0
+#define Curl_ssl_free_certinfo(x) Curl_nop_stmt
+#define Curl_ssl_kill_session(x) Curl_nop_stmt
+#define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN)
+#define Curl_ssl_cert_status_request() FALSE
+#define Curl_ssl_false_start(a) FALSE
+#define Curl_ssl_get_internals(a,b,c,d) NULL
+#define Curl_ssl_supports(a,b) FALSE
+#define Curl_ssl_cfilter_add(a,b,c) CURLE_NOT_BUILT_IN
+#define Curl_ssl_cfilter_proxy_add(a,b,c) CURLE_NOT_BUILT_IN
+#define Curl_ssl_get_config(a,b) NULL
+#define Curl_ssl_cfilter_remove(a,b) CURLE_OK
+#endif
+
+#endif /* HEADER_CURL_VTLS_H */
diff --git a/libs/libcurl/src/vtls/vtls_int.h b/libs/libcurl/src/vtls/vtls_int.h new file mode 100644 index 0000000000..0f04390216 --- /dev/null +++ b/libs/libcurl/src/vtls/vtls_int.h @@ -0,0 +1,190 @@ +#ifndef HEADER_CURL_VTLS_INT_H
+#define HEADER_CURL_VTLS_INT_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "cfilters.h"
+#include "urldata.h"
+
+#ifdef USE_SSL
+
+/* Information in each SSL cfilter context: cf->ctx */
+struct ssl_connect_data {
+ ssl_connection_state state;
+ ssl_connect_state connecting_state;
+ const char *hostname; /* hostnaem for verification */
+ const char *dispname; /* display version of hostname */
+ int port; /* remote port at origin */
+ struct ssl_backend_data *backend; /* vtls backend specific props */
+ struct Curl_easy *call_data; /* data handle used in current call,
+ * same as parameter passed, but available
+ * here for backend internal callbacks
+ * that need it. NULLed after at the
+ * end of each vtls filter invcocation. */
+};
+
+
+/* Definitions for SSL Implementations */
+
+struct Curl_ssl {
+ /*
+ * This *must* be the first entry to allow returning the list of available
+ * backends in curl_global_sslset().
+ */
+ curl_ssl_backend info;
+ unsigned int supports; /* bitfield, see above */
+ size_t sizeof_ssl_backend_data;
+
+ int (*init)(void);
+ void (*cleanup)(void);
+
+ size_t (*version)(char *buffer, size_t size);
+ int (*check_cxn)(struct Curl_cfilter *cf, struct Curl_easy *data);
+ int (*shut_down)(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+ bool (*data_pending)(struct Curl_cfilter *cf,
+ const struct Curl_easy *data);
+
+ /* return 0 if a find random is filled in */
+ CURLcode (*random)(struct Curl_easy *data, unsigned char *entropy,
+ size_t length);
+ bool (*cert_status_request)(void);
+
+ CURLcode (*connect_blocking)(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+ CURLcode (*connect_nonblocking)(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done);
+
+ /* If the SSL backend wants to read or write on this connection during a
+ handshake, set socks[0] to the connection's FIRSTSOCKET, and return
+ a bitmap indicating read or write with GETSOCK_WRITESOCK(0) or
+ GETSOCK_READSOCK(0). Otherwise return GETSOCK_BLANK.
+ Mandatory. */
+ int (*get_select_socks)(struct Curl_cfilter *cf, struct Curl_easy *data,
+ curl_socket_t *socks);
+
+ void *(*get_internals)(struct ssl_connect_data *connssl, CURLINFO info);
+ void (*close)(struct Curl_cfilter *cf, struct Curl_easy *data);
+ void (*close_all)(struct Curl_easy *data);
+ void (*session_free)(void *ptr);
+
+ CURLcode (*set_engine)(struct Curl_easy *data, const char *engine);
+ CURLcode (*set_engine_default)(struct Curl_easy *data);
+ struct curl_slist *(*engines_list)(struct Curl_easy *data);
+
+ bool (*false_start)(void);
+ CURLcode (*sha256sum)(const unsigned char *input, size_t inputlen,
+ unsigned char *sha256sum, size_t sha256sumlen);
+
+ bool (*attach_data)(struct Curl_cfilter *cf, struct Curl_easy *data);
+ void (*detach_data)(struct Curl_cfilter *cf, struct Curl_easy *data);
+
+ void (*free_multi_ssl_backend_data)(struct multi_ssl_backend_data *mbackend);
+
+ ssize_t (*recv_plain)(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *code);
+ ssize_t (*send_plain)(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *mem, size_t len, CURLcode *code);
+
+};
+
+extern const struct Curl_ssl *Curl_ssl;
+
+
+int Curl_none_init(void);
+void Curl_none_cleanup(void);
+int Curl_none_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data);
+int Curl_none_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data);
+CURLcode Curl_none_random(struct Curl_easy *data, unsigned char *entropy,
+ size_t length);
+void Curl_none_close_all(struct Curl_easy *data);
+void Curl_none_session_free(void *ptr);
+bool Curl_none_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data);
+bool Curl_none_cert_status_request(void);
+CURLcode Curl_none_set_engine(struct Curl_easy *data, const char *engine);
+CURLcode Curl_none_set_engine_default(struct Curl_easy *data);
+struct curl_slist *Curl_none_engines_list(struct Curl_easy *data);
+bool Curl_none_false_start(void);
+int Curl_ssl_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data,
+ curl_socket_t *socks);
+
+/**
+ * Get the ssl_config_data in `data` that is relevant for cfilter `cf`.
+ */
+struct ssl_config_data *Curl_ssl_cf_get_config(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+
+/**
+ * Get the primary config relevant for the filter from its connection.
+ */
+struct ssl_primary_config *
+ Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf);
+
+/**
+ * Get the first SSL filter in the chain starting with `cf`, or NULL.
+ */
+struct Curl_cfilter *Curl_ssl_cf_get_ssl(struct Curl_cfilter *cf);
+
+/**
+ * Get the SSL filter below the given one or NULL if there is none.
+ */
+bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf);
+
+/* extract a session ID
+ * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
+ * Caller must make sure that the ownership of returned sessionid object
+ * is properly taken (e.g. its refcount is incremented
+ * under sessionid mutex).
+ */
+bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ void **ssl_sessionid,
+ size_t *idsize); /* set 0 if unknown */
+/* add a new session ID
+ * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
+ * Caller must ensure that it has properly shared ownership of this sessionid
+ * object with cache (e.g. incrementing refcount on success)
+ */
+CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ void *ssl_sessionid,
+ size_t idsize,
+ bool *added);
+
+#include "openssl.h" /* OpenSSL versions */
+#include "gtls.h" /* GnuTLS versions */
+#include "nssg.h" /* NSS versions */
+#include "gskit.h" /* Global Secure ToolKit versions */
+#include "wolfssl.h" /* wolfSSL versions */
+#include "schannel.h" /* Schannel SSPI version */
+#include "sectransp.h" /* SecureTransport (Darwin) version */
+#include "mbedtls.h" /* mbedTLS versions */
+#include "bearssl.h" /* BearSSL versions */
+#include "rustls.h" /* rustls versions */
+
+#endif /* USE_SSL */
+
+#endif /* HEADER_CURL_VTLS_INT_H */
diff --git a/libs/libcurl/src/vtls/wolfssl.c b/libs/libcurl/src/vtls/wolfssl.c index 594c39a324..5ae0bc9b11 100644 --- a/libs/libcurl/src/vtls/wolfssl.c +++ b/libs/libcurl/src/vtls/wolfssl.c @@ -1,1247 +1,1372 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 - * - ***************************************************************************/ - -/* - * Source file for all wolfSSL specific code for the TLS/SSL layer. No code - * but vtls.c should ever call or use these functions. - * - */ - -#include "curl_setup.h" - -#ifdef USE_WOLFSSL - -#define WOLFSSL_OPTIONS_IGNORE_SYS -#include <wolfssl/version.h> -#include <wolfssl/options.h> - -/* To determine what functions are available we rely on one or both of: - - the user's options.h generated by wolfSSL - - the symbols detected by curl's configure - Since they are markedly different from one another, and one or the other may - not be available, we do some checking below to bring things in sync. */ - -/* HAVE_ALPN is wolfSSL's build time symbol for enabling ALPN in options.h. */ -#ifndef HAVE_ALPN -#ifdef HAVE_WOLFSSL_USEALPN -#define HAVE_ALPN -#endif -#endif - -#include <limits.h> - -#include "urldata.h" -#include "sendf.h" -#include "inet_pton.h" -#include "vtls.h" -#include "keylog.h" -#include "parsedate.h" -#include "connect.h" /* for the connect timeout */ -#include "select.h" -#include "strcase.h" -#include "x509asn1.h" -#include "curl_printf.h" -#include "multiif.h" - -#include <wolfssl/openssl/ssl.h> -#include <wolfssl/ssl.h> -#include <wolfssl/error-ssl.h> -#include "wolfssl.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* KEEP_PEER_CERT is a product of the presence of build time symbol - OPENSSL_EXTRA without NO_CERTS, depending on the version. KEEP_PEER_CERT is - in wolfSSL's settings.h, and the latter two are build time symbols in - options.h. */ -#ifndef KEEP_PEER_CERT -#if defined(HAVE_WOLFSSL_GET_PEER_CERTIFICATE) || \ - (defined(OPENSSL_EXTRA) && !defined(NO_CERTS)) -#define KEEP_PEER_CERT -#endif -#endif - -struct ssl_backend_data { - SSL_CTX* ctx; - SSL* handle; -}; - -static Curl_recv wolfssl_recv; -static Curl_send wolfssl_send; - -#ifdef OPENSSL_EXTRA -/* - * Availability note: - * The TLS 1.3 secret callback (wolfSSL_set_tls13_secret_cb) was added in - * WolfSSL 4.4.0, but requires the -DHAVE_SECRET_CALLBACK build option. If that - * option is not set, then TLS 1.3 will not be logged. - * For TLS 1.2 and before, we use wolfSSL_get_keys(). - * SSL_get_client_random and wolfSSL_get_keys require OPENSSL_EXTRA - * (--enable-opensslextra or --enable-all). - */ -#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) -static int -wolfssl_tls13_secret_callback(SSL *ssl, int id, const unsigned char *secret, - int secretSz, void *ctx) -{ - const char *label; - unsigned char client_random[SSL3_RANDOM_SIZE]; - (void)ctx; - - if(!ssl || !Curl_tls_keylog_enabled()) { - return 0; - } - - switch(id) { - case CLIENT_EARLY_TRAFFIC_SECRET: - label = "CLIENT_EARLY_TRAFFIC_SECRET"; - break; - case CLIENT_HANDSHAKE_TRAFFIC_SECRET: - label = "CLIENT_HANDSHAKE_TRAFFIC_SECRET"; - break; - case SERVER_HANDSHAKE_TRAFFIC_SECRET: - label = "SERVER_HANDSHAKE_TRAFFIC_SECRET"; - break; - case CLIENT_TRAFFIC_SECRET: - label = "CLIENT_TRAFFIC_SECRET_0"; - break; - case SERVER_TRAFFIC_SECRET: - label = "SERVER_TRAFFIC_SECRET_0"; - break; - case EARLY_EXPORTER_SECRET: - label = "EARLY_EXPORTER_SECRET"; - break; - case EXPORTER_SECRET: - label = "EXPORTER_SECRET"; - break; - default: - return 0; - } - - if(SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE) == 0) { - /* Should never happen as wolfSSL_KeepArrays() was called before. */ - return 0; - } - - Curl_tls_keylog_write(label, client_random, secret, secretSz); - return 0; -} -#endif /* defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) */ - -static void -wolfssl_log_tls12_secret(SSL *ssl) -{ - unsigned char *ms, *sr, *cr; - unsigned int msLen, srLen, crLen, i, x = 0; - -#if LIBWOLFSSL_VERSION_HEX >= 0x0300d000 /* >= 3.13.0 */ - /* wolfSSL_GetVersion is available since 3.13, we use it instead of - * SSL_version since the latter relies on OPENSSL_ALL (--enable-opensslall or - * --enable-all). Failing to perform this check could result in an unusable - * key log line when TLS 1.3 is actually negotiated. */ - switch(wolfSSL_GetVersion(ssl)) { - case WOLFSSL_SSLV3: - case WOLFSSL_TLSV1: - case WOLFSSL_TLSV1_1: - case WOLFSSL_TLSV1_2: - break; - default: - /* TLS 1.3 does not use this mechanism, the "master secret" returned below - * is not directly usable. */ - return; - } -#endif - - if(SSL_get_keys(ssl, &ms, &msLen, &sr, &srLen, &cr, &crLen) != SSL_SUCCESS) { - return; - } - - /* Check for a missing master secret and skip logging. That can happen if - * curl rejects the server certificate and aborts the handshake. - */ - for(i = 0; i < msLen; i++) { - x |= ms[i]; - } - if(x == 0) { - return; - } - - Curl_tls_keylog_write("CLIENT_RANDOM", cr, ms, msLen); -} -#endif /* OPENSSL_EXTRA */ - -static int do_file_type(const char *type) -{ - if(!type || !type[0]) - return SSL_FILETYPE_PEM; - if(strcasecompare(type, "PEM")) - return SSL_FILETYPE_PEM; - if(strcasecompare(type, "DER")) - return SSL_FILETYPE_ASN1; - return -1; -} - -#ifdef HAVE_LIBOQS -struct group_name_map { - const word16 group; - const char *name; -}; - -static const struct group_name_map gnm[] = { - { WOLFSSL_KYBER_LEVEL1, "KYBER_LEVEL1" }, - { WOLFSSL_KYBER_LEVEL3, "KYBER_LEVEL3" }, - { WOLFSSL_KYBER_LEVEL5, "KYBER_LEVEL5" }, - { WOLFSSL_NTRU_HPS_LEVEL1, "NTRU_HPS_LEVEL1" }, - { WOLFSSL_NTRU_HPS_LEVEL3, "NTRU_HPS_LEVEL3" }, - { WOLFSSL_NTRU_HPS_LEVEL5, "NTRU_HPS_LEVEL5" }, - { WOLFSSL_NTRU_HRSS_LEVEL3, "NTRU_HRSS_LEVEL3" }, - { WOLFSSL_SABER_LEVEL1, "SABER_LEVEL1" }, - { WOLFSSL_SABER_LEVEL3, "SABER_LEVEL3" }, - { WOLFSSL_SABER_LEVEL5, "SABER_LEVEL5" }, - { WOLFSSL_KYBER_90S_LEVEL1, "KYBER_90S_LEVEL1" }, - { WOLFSSL_KYBER_90S_LEVEL3, "KYBER_90S_LEVEL3" }, - { WOLFSSL_KYBER_90S_LEVEL5, "KYBER_90S_LEVEL5" }, - { WOLFSSL_P256_NTRU_HPS_LEVEL1, "P256_NTRU_HPS_LEVEL1" }, - { WOLFSSL_P384_NTRU_HPS_LEVEL3, "P384_NTRU_HPS_LEVEL3" }, - { WOLFSSL_P521_NTRU_HPS_LEVEL5, "P521_NTRU_HPS_LEVEL5" }, - { WOLFSSL_P384_NTRU_HRSS_LEVEL3, "P384_NTRU_HRSS_LEVEL3" }, - { WOLFSSL_P256_SABER_LEVEL1, "P256_SABER_LEVEL1" }, - { WOLFSSL_P384_SABER_LEVEL3, "P384_SABER_LEVEL3" }, - { WOLFSSL_P521_SABER_LEVEL5, "P521_SABER_LEVEL5" }, - { WOLFSSL_P256_KYBER_LEVEL1, "P256_KYBER_LEVEL1" }, - { WOLFSSL_P384_KYBER_LEVEL3, "P384_KYBER_LEVEL3" }, - { WOLFSSL_P521_KYBER_LEVEL5, "P521_KYBER_LEVEL5" }, - { WOLFSSL_P256_KYBER_90S_LEVEL1, "P256_KYBER_90S_LEVEL1" }, - { WOLFSSL_P384_KYBER_90S_LEVEL3, "P384_KYBER_90S_LEVEL3" }, - { WOLFSSL_P521_KYBER_90S_LEVEL5, "P521_KYBER_90S_LEVEL5" }, - { 0, NULL } -}; -#endif - -/* - * This function loads all the client/CA certificates and CRLs. Setup the TLS - * layer and do all necessary magic. - */ -static CURLcode -wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - char *ciphers, *curves; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - SSL_METHOD* req_method = NULL; - curl_socket_t sockfd = conn->sock[sockindex]; -#ifdef HAVE_LIBOQS - word16 oqsAlg = 0; - size_t idx = 0; -#endif -#ifdef HAVE_SNI - bool sni = FALSE; -#define use_sni(x) sni = (x) -#else -#define use_sni(x) Curl_nop_stmt -#endif - - DEBUGASSERT(backend); - - if(connssl->state == ssl_connection_complete) - return CURLE_OK; - - if(SSL_CONN_CONFIG(version_max) != CURL_SSLVERSION_MAX_NONE) { - failf(data, "wolfSSL does not support to set maximum SSL/TLS version"); - return CURLE_SSL_CONNECT_ERROR; - } - - /* check to see if we've been told to use an explicit SSL/TLS version */ - switch(SSL_CONN_CONFIG(version)) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: -#if LIBWOLFSSL_VERSION_HEX >= 0x03003000 /* >= 3.3.0 */ - /* minimum protocol version is set later after the CTX object is created */ - req_method = SSLv23_client_method(); -#else - infof(data, "wolfSSL <3.3.0 cannot be configured to use TLS 1.0-1.2, " - "TLS 1.0 is used exclusively"); - req_method = TLSv1_client_method(); -#endif - use_sni(TRUE); - break; - case CURL_SSLVERSION_TLSv1_0: -#if defined(WOLFSSL_ALLOW_TLSV10) && !defined(NO_OLD_TLS) - req_method = TLSv1_client_method(); - use_sni(TRUE); -#else - failf(data, "wolfSSL does not support TLS 1.0"); - return CURLE_NOT_BUILT_IN; -#endif - break; - case CURL_SSLVERSION_TLSv1_1: -#ifndef NO_OLD_TLS - req_method = TLSv1_1_client_method(); - use_sni(TRUE); -#else - failf(data, "wolfSSL does not support TLS 1.1"); - return CURLE_NOT_BUILT_IN; -#endif - break; - case CURL_SSLVERSION_TLSv1_2: - req_method = TLSv1_2_client_method(); - use_sni(TRUE); - break; - case CURL_SSLVERSION_TLSv1_3: -#ifdef WOLFSSL_TLS13 - req_method = wolfTLSv1_3_client_method(); - use_sni(TRUE); - break; -#else - failf(data, "wolfSSL: TLS 1.3 is not yet supported"); - return CURLE_SSL_CONNECT_ERROR; -#endif - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } - - if(!req_method) { - failf(data, "SSL: couldn't create a method"); - return CURLE_OUT_OF_MEMORY; - } - - if(backend->ctx) - SSL_CTX_free(backend->ctx); - backend->ctx = SSL_CTX_new(req_method); - - if(!backend->ctx) { - failf(data, "SSL: couldn't create a context"); - return CURLE_OUT_OF_MEMORY; - } - - switch(SSL_CONN_CONFIG(version)) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: -#if LIBWOLFSSL_VERSION_HEX > 0x03004006 /* > 3.4.6 */ - /* Versions 3.3.0 to 3.4.6 we know the minimum protocol version is - * whatever minimum version of TLS was built in and at least TLS 1.0. For - * later library versions that could change (eg TLS 1.0 built in but - * defaults to TLS 1.1) so we have this short circuit evaluation to find - * the minimum supported TLS version. - */ - if((wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1) != 1) && - (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_1) != 1) && - (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_2) != 1) -#ifdef WOLFSSL_TLS13 - && (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_3) != 1) -#endif - ) { - failf(data, "SSL: couldn't set the minimum protocol version"); - return CURLE_SSL_CONNECT_ERROR; - } -#endif - break; - } - - ciphers = SSL_CONN_CONFIG(cipher_list); - if(ciphers) { - if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) { - failf(data, "failed setting cipher list: %s", ciphers); - return CURLE_SSL_CIPHER; - } - infof(data, "Cipher selection: %s", ciphers); - } - - curves = SSL_CONN_CONFIG(curves); - if(curves) { - -#ifdef HAVE_LIBOQS - for(idx = 0; gnm[idx].name != NULL; idx++) { - if(strncmp(curves, gnm[idx].name, strlen(gnm[idx].name)) == 0) { - oqsAlg = gnm[idx].group; - break; - } - } - - if(oqsAlg == 0) -#endif - { - if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) { - failf(data, "failed setting curves list: '%s'", curves); - return CURLE_SSL_CIPHER; - } - } - } -#ifndef NO_FILESYSTEM - /* load trusted cacert */ - if(SSL_CONN_CONFIG(CAfile)) { - if(1 != SSL_CTX_load_verify_locations(backend->ctx, - SSL_CONN_CONFIG(CAfile), - SSL_CONN_CONFIG(CApath))) { - if(SSL_CONN_CONFIG(verifypeer)) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate verify locations:" - " CAfile: %s CApath: %s", - SSL_CONN_CONFIG(CAfile)? - SSL_CONN_CONFIG(CAfile): "none", - SSL_CONN_CONFIG(CApath)? - SSL_CONN_CONFIG(CApath) : "none"); - return CURLE_SSL_CACERT_BADFILE; - } - else { - /* Just continue with a warning if no strict certificate - verification is required. */ - infof(data, "error setting certificate verify locations," - " continuing anyway:"); - } - } - else { - /* Everything is fine. */ - infof(data, "successfully set certificate verify locations:"); - } - infof(data, " CAfile: %s", - SSL_CONN_CONFIG(CAfile) ? SSL_CONN_CONFIG(CAfile) : "none"); - infof(data, " CApath: %s", - SSL_CONN_CONFIG(CApath) ? SSL_CONN_CONFIG(CApath) : "none"); - } - - /* Load the client certificate, and private key */ - if(SSL_SET_OPTION(primary.clientcert) && SSL_SET_OPTION(key)) { - int file_type = do_file_type(SSL_SET_OPTION(cert_type)); - - if(SSL_CTX_use_certificate_file(backend->ctx, - SSL_SET_OPTION(primary.clientcert), - file_type) != 1) { - failf(data, "unable to use client certificate (no key or wrong pass" - " phrase?)"); - return CURLE_SSL_CONNECT_ERROR; - } - - file_type = do_file_type(SSL_SET_OPTION(key_type)); - if(SSL_CTX_use_PrivateKey_file(backend->ctx, SSL_SET_OPTION(key), - file_type) != 1) { - failf(data, "unable to set private key"); - return CURLE_SSL_CONNECT_ERROR; - } - } -#endif /* !NO_FILESYSTEM */ - - /* SSL always tries to verify the peer, this only says whether it should - * fail to connect if the verification fails, or if it should continue - * anyway. In the latter case the result of the verification is checked with - * SSL_get_verify_result() below. */ - SSL_CTX_set_verify(backend->ctx, - SSL_CONN_CONFIG(verifypeer)?SSL_VERIFY_PEER: - SSL_VERIFY_NONE, - NULL); - -#ifdef HAVE_SNI - if(sni) { - struct in_addr addr4; -#ifdef ENABLE_IPV6 - struct in6_addr addr6; -#endif - const char * const hostname = SSL_HOST_NAME(); - size_t hostname_len = strlen(hostname); - if((hostname_len < USHRT_MAX) && - !Curl_inet_pton(AF_INET, hostname, &addr4) -#ifdef ENABLE_IPV6 - && !Curl_inet_pton(AF_INET6, hostname, &addr6) -#endif - ) { - size_t snilen; - char *snihost = Curl_ssl_snihost(data, hostname, &snilen); - if(!snihost || - wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME, snihost, - (unsigned short)snilen) != 1) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - } - } -#endif - - /* give application a chance to interfere with SSL set up. */ - if(data->set.ssl.fsslctx) { - CURLcode result = (*data->set.ssl.fsslctx)(data, backend->ctx, - data->set.ssl.fsslctxp); - if(result) { - failf(data, "error signaled by ssl ctx callback"); - return result; - } - } -#ifdef NO_FILESYSTEM - else if(SSL_CONN_CONFIG(verifypeer)) { - failf(data, "SSL: Certificates can't be loaded because wolfSSL was built" - " with \"no filesystem\". Either disable peer verification" - " (insecure) or if you are building an application with libcurl you" - " can load certificates via CURLOPT_SSL_CTX_FUNCTION."); - return CURLE_SSL_CONNECT_ERROR; - } -#endif - - /* Let's make an SSL structure */ - if(backend->handle) - SSL_free(backend->handle); - backend->handle = SSL_new(backend->ctx); - if(!backend->handle) { - failf(data, "SSL: couldn't create a handle"); - return CURLE_OUT_OF_MEMORY; - } - -#ifdef HAVE_LIBOQS - if(oqsAlg) { - if(wolfSSL_UseKeyShare(backend->handle, oqsAlg) != WOLFSSL_SUCCESS) { - failf(data, "unable to use oqs KEM"); - } - } -#endif - -#ifdef HAVE_ALPN - if(conn->bits.tls_enable_alpn) { - char protocols[128]; - *protocols = '\0'; - - /* wolfSSL's ALPN protocol name list format is a comma separated string of - protocols in descending order of preference, eg: "h2,http/1.1" */ - -#ifdef USE_HTTP2 - if(data->state.httpwant >= CURL_HTTP_VERSION_2) { - strcpy(protocols + strlen(protocols), ALPN_H2 ","); - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); - } -#endif - - strcpy(protocols + strlen(protocols), ALPN_HTTP_1_1); - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); - - if(wolfSSL_UseALPN(backend->handle, protocols, - (unsigned)strlen(protocols), - WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != SSL_SUCCESS) { - failf(data, "SSL: failed setting ALPN protocols"); - return CURLE_SSL_CONNECT_ERROR; - } - } -#endif /* HAVE_ALPN */ - -#ifdef OPENSSL_EXTRA - if(Curl_tls_keylog_enabled()) { - /* Ensure the Client Random is preserved. */ - wolfSSL_KeepArrays(backend->handle); -#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) - wolfSSL_set_tls13_secret_cb(backend->handle, - wolfssl_tls13_secret_callback, NULL); -#endif - } -#endif /* OPENSSL_EXTRA */ - -#ifdef HAVE_SECURE_RENEGOTIATION - if(wolfSSL_UseSecureRenegotiation(backend->handle) != SSL_SUCCESS) { - failf(data, "SSL: failed setting secure renegotiation"); - return CURLE_SSL_CONNECT_ERROR; - } -#endif /* HAVE_SECURE_RENEGOTIATION */ - - /* Check if there's a cached ID we can/should use here! */ - if(SSL_SET_OPTION(primary.sessionid)) { - void *ssl_sessionid = NULL; - - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(data, conn, - SSL_IS_PROXY() ? TRUE : FALSE, - &ssl_sessionid, NULL, sockindex)) { - /* we got a session id, use it! */ - if(!SSL_set_session(backend->handle, ssl_sessionid)) { - Curl_ssl_delsessionid(data, ssl_sessionid); - infof(data, "Can't use session ID, going on without"); - } - else - infof(data, "SSL re-using session ID"); - } - Curl_ssl_sessionid_unlock(data); - } - - /* pass the raw socket into the SSL layer */ - if(!SSL_set_fd(backend->handle, (int)sockfd)) { - failf(data, "SSL: SSL_set_fd failed"); - return CURLE_SSL_CONNECT_ERROR; - } - - connssl->connecting_state = ssl_connect_2; - return CURLE_OK; -} - - -static CURLcode -wolfssl_connect_step2(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - int ret = -1; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - const char * const dispname = SSL_HOST_DISPNAME(); - const char * const pinnedpubkey = SSL_PINNED_PUB_KEY(); - - DEBUGASSERT(backend); - - ERR_clear_error(); - - conn->recv[sockindex] = wolfssl_recv; - conn->send[sockindex] = wolfssl_send; - - /* Enable RFC2818 checks */ - if(SSL_CONN_CONFIG(verifyhost)) { - char *snihost = Curl_ssl_snihost(data, SSL_HOST_NAME(), NULL); - if(!snihost || - (wolfSSL_check_domain_name(backend->handle, snihost) == SSL_FAILURE)) - return CURLE_SSL_CONNECT_ERROR; - } - - ret = SSL_connect(backend->handle); - -#ifdef OPENSSL_EXTRA - if(Curl_tls_keylog_enabled()) { - /* If key logging is enabled, wait for the handshake to complete and then - * proceed with logging secrets (for TLS 1.2 or older). - * - * During the handshake (ret==-1), wolfSSL_want_read() is true as it waits - * for the server response. At that point the master secret is not yet - * available, so we must not try to read it. - * To log the secret on completion with a handshake failure, detect - * completion via the observation that there is nothing to read or write. - * Note that OpenSSL SSL_want_read() is always true here. If wolfSSL ever - * changes, the worst case is that no key is logged on error. - */ - if(ret == SSL_SUCCESS || - (!wolfSSL_want_read(backend->handle) && - !wolfSSL_want_write(backend->handle))) { - wolfssl_log_tls12_secret(backend->handle); - /* Client Random and master secrets are no longer needed, erase these. - * Ignored while the handshake is still in progress. */ - wolfSSL_FreeArrays(backend->handle); - } - } -#endif /* OPENSSL_EXTRA */ - - if(ret != 1) { - char error_buffer[WOLFSSL_MAX_ERROR_SZ]; - int detail = SSL_get_error(backend->handle, ret); - - if(SSL_ERROR_WANT_READ == detail) { - connssl->connecting_state = ssl_connect_2_reading; - return CURLE_OK; - } - else if(SSL_ERROR_WANT_WRITE == detail) { - connssl->connecting_state = ssl_connect_2_writing; - return CURLE_OK; - } - /* There is no easy way to override only the CN matching. - * This will enable the override of both mismatching SubjectAltNames - * as also mismatching CN fields */ - else if(DOMAIN_NAME_MISMATCH == detail) { -#if 1 - failf(data, " subject alt name(s) or common name do not match \"%s\"", - dispname); - return CURLE_PEER_FAILED_VERIFICATION; -#else - /* When the wolfssl_check_domain_name() is used and you desire to - * continue on a DOMAIN_NAME_MISMATCH, i.e. 'conn->ssl_config.verifyhost - * == 0', CyaSSL version 2.4.0 will fail with an INCOMPLETE_DATA - * error. The only way to do this is currently to switch the - * Wolfssl_check_domain_name() in and out based on the - * 'conn->ssl_config.verifyhost' value. */ - if(SSL_CONN_CONFIG(verifyhost)) { - failf(data, - " subject alt name(s) or common name do not match \"%s\"\n", - dispname); - return CURLE_PEER_FAILED_VERIFICATION; - } - else { - infof(data, - " subject alt name(s) and/or common name do not match \"%s\"", - dispname); - return CURLE_OK; - } -#endif - } -#if LIBWOLFSSL_VERSION_HEX >= 0x02007000 /* 2.7.0 */ - else if(ASN_NO_SIGNER_E == detail) { - if(SSL_CONN_CONFIG(verifypeer)) { - failf(data, " CA signer not available for verification"); - return CURLE_SSL_CACERT_BADFILE; - } - else { - /* Just continue with a warning if no strict certificate - verification is required. */ - infof(data, "CA signer not available for verification, " - "continuing anyway"); - } - } -#endif - else { - failf(data, "SSL_connect failed with error %d: %s", detail, - ERR_error_string(detail, error_buffer)); - return CURLE_SSL_CONNECT_ERROR; - } - } - - if(pinnedpubkey) { -#ifdef KEEP_PEER_CERT - X509 *x509; - const char *x509_der; - int x509_der_len; - struct Curl_X509certificate x509_parsed; - struct Curl_asn1Element *pubkey; - CURLcode result; - - x509 = SSL_get_peer_certificate(backend->handle); - if(!x509) { - failf(data, "SSL: failed retrieving server certificate"); - return CURLE_SSL_PINNEDPUBKEYNOTMATCH; - } - - x509_der = (const char *)wolfSSL_X509_get_der(x509, &x509_der_len); - if(!x509_der) { - failf(data, "SSL: failed retrieving ASN.1 server certificate"); - return CURLE_SSL_PINNEDPUBKEYNOTMATCH; - } - - memset(&x509_parsed, 0, sizeof(x509_parsed)); - if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len)) - return CURLE_SSL_PINNEDPUBKEYNOTMATCH; - - pubkey = &x509_parsed.subjectPublicKeyInfo; - if(!pubkey->header || pubkey->end <= pubkey->header) { - failf(data, "SSL: failed retrieving public key from server certificate"); - return CURLE_SSL_PINNEDPUBKEYNOTMATCH; - } - - result = Curl_pin_peer_pubkey(data, - pinnedpubkey, - (const unsigned char *)pubkey->header, - (size_t)(pubkey->end - pubkey->header)); - if(result) { - failf(data, "SSL: public key does not match pinned public key"); - return result; - } -#else - failf(data, "Library lacks pinning support built-in"); - return CURLE_NOT_BUILT_IN; -#endif - } - -#ifdef HAVE_ALPN - if(conn->bits.tls_enable_alpn) { - int rc; - char *protocol = NULL; - unsigned short protocol_len = 0; - - rc = wolfSSL_ALPN_GetProtocol(backend->handle, &protocol, &protocol_len); - - if(rc == SSL_SUCCESS) { - infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, protocol_len, protocol); - - if(protocol_len == ALPN_HTTP_1_1_LENGTH && - !memcmp(protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH)) - conn->alpn = CURL_HTTP_VERSION_1_1; -#ifdef USE_HTTP2 - else if(data->state.httpwant >= CURL_HTTP_VERSION_2 && - protocol_len == ALPN_H2_LENGTH && - !memcmp(protocol, ALPN_H2, ALPN_H2_LENGTH)) - conn->alpn = CURL_HTTP_VERSION_2; -#endif - else - infof(data, "ALPN, unrecognized protocol %.*s", protocol_len, - protocol); - Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); - } - else if(rc == SSL_ALPN_NOT_FOUND) - infof(data, VTLS_INFOF_NO_ALPN); - else { - failf(data, "ALPN, failure getting protocol, error %d", rc); - return CURLE_SSL_CONNECT_ERROR; - } - } -#endif /* HAVE_ALPN */ - - connssl->connecting_state = ssl_connect_3; -#if (LIBWOLFSSL_VERSION_HEX >= 0x03009010) - infof(data, "SSL connection using %s / %s", - wolfSSL_get_version(backend->handle), - wolfSSL_get_cipher_name(backend->handle)); -#else - infof(data, "SSL connected"); -#endif - - return CURLE_OK; -} - - -static CURLcode -wolfssl_connect_step3(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - CURLcode result = CURLE_OK; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); - DEBUGASSERT(backend); - - if(SSL_SET_OPTION(primary.sessionid)) { - bool incache; - bool added = FALSE; - void *old_ssl_sessionid = NULL; - /* SSL_get1_session allocates memory that has to be freed. */ - SSL_SESSION *our_ssl_sessionid = SSL_get1_session(backend->handle); - bool isproxy = SSL_IS_PROXY() ? TRUE : FALSE; - - if(our_ssl_sessionid) { - Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(data, conn, isproxy, - &old_ssl_sessionid, NULL, sockindex)); - if(incache) { - if(old_ssl_sessionid != our_ssl_sessionid) { - infof(data, "old SSL session ID is stale, removing"); - Curl_ssl_delsessionid(data, old_ssl_sessionid); - incache = FALSE; - } - } - - if(!incache) { - result = Curl_ssl_addsessionid(data, conn, isproxy, our_ssl_sessionid, - 0, sockindex, NULL); - if(result) { - Curl_ssl_sessionid_unlock(data); - SSL_SESSION_free(our_ssl_sessionid); - failf(data, "failed to store ssl session"); - return result; - } - else { - added = TRUE; - } - } - Curl_ssl_sessionid_unlock(data); - - if(!added) { - /* If the session info wasn't added to the cache, free our copy. */ - SSL_SESSION_free(our_ssl_sessionid); - } - } - } - - connssl->connecting_state = ssl_connect_done; - - return result; -} - - -static ssize_t wolfssl_send(struct Curl_easy *data, - int sockindex, - const void *mem, - size_t len, - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - char error_buffer[WOLFSSL_MAX_ERROR_SZ]; - int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; - int rc; - - DEBUGASSERT(backend); - - ERR_clear_error(); - - rc = SSL_write(backend->handle, mem, memlen); - - if(rc <= 0) { - int err = SSL_get_error(backend->handle, rc); - - switch(err) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - /* there's data pending, re-invoke SSL_write() */ - *curlcode = CURLE_AGAIN; - return -1; - default: - failf(data, "SSL write: %s, errno %d", - ERR_error_string(err, error_buffer), - SOCKERRNO); - *curlcode = CURLE_SEND_ERROR; - return -1; - } - } - return rc; -} - -static void wolfssl_close(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - - (void) data; - - DEBUGASSERT(backend); - - if(backend->handle) { - char buf[32]; - /* Maybe the server has already sent a close notify alert. - Read it to avoid an RST on the TCP connection. */ - (void)SSL_read(backend->handle, buf, (int)sizeof(buf)); - (void)SSL_shutdown(backend->handle); - SSL_free(backend->handle); - backend->handle = NULL; - } - if(backend->ctx) { - SSL_CTX_free(backend->ctx); - backend->ctx = NULL; - } -} - -static ssize_t wolfssl_recv(struct Curl_easy *data, - int num, - char *buf, - size_t buffersize, - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[num]; - struct ssl_backend_data *backend = connssl->backend; - char error_buffer[WOLFSSL_MAX_ERROR_SZ]; - int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; - int nread; - - DEBUGASSERT(backend); - - ERR_clear_error(); - - nread = SSL_read(backend->handle, buf, buffsize); - - if(nread <= 0) { - int err = SSL_get_error(backend->handle, nread); - - switch(err) { - case SSL_ERROR_ZERO_RETURN: /* no more data */ - break; - case SSL_ERROR_NONE: - /* FALLTHROUGH */ - case SSL_ERROR_WANT_READ: - /* FALLTHROUGH */ - case SSL_ERROR_WANT_WRITE: - /* there's data pending, re-invoke SSL_read() */ - *curlcode = CURLE_AGAIN; - return -1; - default: - failf(data, "SSL read: %s, errno %d", - ERR_error_string(err, error_buffer), SOCKERRNO); - *curlcode = CURLE_RECV_ERROR; - return -1; - } - } - return nread; -} - - -static void wolfssl_session_free(void *ptr) -{ - SSL_SESSION_free(ptr); -} - - -static size_t wolfssl_version(char *buffer, size_t size) -{ -#if LIBWOLFSSL_VERSION_HEX >= 0x03006000 - return msnprintf(buffer, size, "wolfSSL/%s", wolfSSL_lib_version()); -#elif defined(WOLFSSL_VERSION) - return msnprintf(buffer, size, "wolfSSL/%s", WOLFSSL_VERSION); -#endif -} - - -static int wolfssl_init(void) -{ -#ifdef OPENSSL_EXTRA - Curl_tls_keylog_open(); -#endif - return (wolfSSL_Init() == SSL_SUCCESS); -} - - -static void wolfssl_cleanup(void) -{ - wolfSSL_Cleanup(); -#ifdef OPENSSL_EXTRA - Curl_tls_keylog_close(); -#endif -} - - -static bool wolfssl_data_pending(const struct connectdata *conn, - int connindex) -{ - const struct ssl_connect_data *connssl = &conn->ssl[connindex]; - struct ssl_backend_data *backend = connssl->backend; - DEBUGASSERT(backend); - if(backend->handle) /* SSL is in use */ - return (0 != SSL_pending(backend->handle)) ? TRUE : FALSE; - else - return FALSE; -} - - -/* - * This function is called to shut down the SSL layer but keep the - * socket open (CCC - Clear Command Channel) - */ -static int wolfssl_shutdown(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - int retval = 0; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - - (void) data; - - DEBUGASSERT(backend); - - if(backend->handle) { - ERR_clear_error(); - SSL_free(backend->handle); - backend->handle = NULL; - } - return retval; -} - - -static CURLcode -wolfssl_connect_common(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool nonblocking, - bool *done) -{ - CURLcode result; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - curl_socket_t sockfd = conn->sock[sockindex]; - int what; - - /* check if the connection has already been established */ - if(ssl_connection_complete == connssl->state) { - *done = TRUE; - return CURLE_OK; - } - - if(ssl_connect_1 == connssl->connecting_state) { - /* Find out how much more time we're allowed */ - const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - result = wolfssl_connect_step1(data, conn, sockindex); - if(result) - return result; - } - - while(ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state) { - - /* check allowed time left */ - const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - /* if ssl is expecting something, check if it's available. */ - if(connssl->connecting_state == ssl_connect_2_reading - || connssl->connecting_state == ssl_connect_2_writing) { - - curl_socket_t writefd = ssl_connect_2_writing == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - curl_socket_t readfd = ssl_connect_2_reading == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - - what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, - nonblocking?0:timeout_ms); - if(what < 0) { - /* fatal error */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - return CURLE_SSL_CONNECT_ERROR; - } - else if(0 == what) { - if(nonblocking) { - *done = FALSE; - return CURLE_OK; - } - else { - /* timeout */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - } - /* socket is readable or writable */ - } - - /* Run transaction, and return to the caller if it failed or if - * this connection is part of a multi handle and this loop would - * execute again. This permits the owner of a multi handle to - * abort a connection attempt before step2 has completed while - * ensuring that a client using select() or epoll() will always - * have a valid fdset to wait on. - */ - result = wolfssl_connect_step2(data, conn, sockindex); - if(result || (nonblocking && - (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) - return result; - } /* repeat step2 until all transactions are done. */ - - if(ssl_connect_3 == connssl->connecting_state) { - result = wolfssl_connect_step3(data, conn, sockindex); - if(result) - return result; - } - - if(ssl_connect_done == connssl->connecting_state) { - connssl->state = ssl_connection_complete; - conn->recv[sockindex] = wolfssl_recv; - conn->send[sockindex] = wolfssl_send; - *done = TRUE; - } - else - *done = FALSE; - - /* Reset our connect state machine */ - connssl->connecting_state = ssl_connect_1; - - return CURLE_OK; -} - - -static CURLcode wolfssl_connect_nonblocking(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, bool *done) -{ - return wolfssl_connect_common(data, conn, sockindex, TRUE, done); -} - - -static CURLcode wolfssl_connect(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - CURLcode result; - bool done = FALSE; - - result = wolfssl_connect_common(data, conn, sockindex, FALSE, &done); - if(result) - return result; - - DEBUGASSERT(done); - - return CURLE_OK; -} - -static CURLcode wolfssl_random(struct Curl_easy *data, - unsigned char *entropy, size_t length) -{ - WC_RNG rng; - (void)data; - if(wc_InitRng(&rng)) - return CURLE_FAILED_INIT; - if(length > UINT_MAX) - return CURLE_FAILED_INIT; - if(wc_RNG_GenerateBlock(&rng, entropy, (unsigned)length)) - return CURLE_FAILED_INIT; - if(wc_FreeRng(&rng)) - return CURLE_FAILED_INIT; - return CURLE_OK; -} - -static CURLcode wolfssl_sha256sum(const unsigned char *tmp, /* input */ - size_t tmplen, - unsigned char *sha256sum /* output */, - size_t unused) -{ - wc_Sha256 SHA256pw; - (void)unused; - wc_InitSha256(&SHA256pw); - wc_Sha256Update(&SHA256pw, tmp, (word32)tmplen); - wc_Sha256Final(&SHA256pw, sha256sum); - return CURLE_OK; -} - -static void *wolfssl_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - struct ssl_backend_data *backend = connssl->backend; - (void)info; - DEBUGASSERT(backend); - return backend->handle; -} - -const struct Curl_ssl Curl_ssl_wolfssl = { - { CURLSSLBACKEND_WOLFSSL, "WolfSSL" }, /* info */ - -#ifdef KEEP_PEER_CERT - SSLSUPP_PINNEDPUBKEY | -#endif - SSLSUPP_SSL_CTX, - - sizeof(struct ssl_backend_data), - - wolfssl_init, /* init */ - wolfssl_cleanup, /* cleanup */ - wolfssl_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ - wolfssl_shutdown, /* shutdown */ - wolfssl_data_pending, /* data_pending */ - wolfssl_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ - wolfssl_connect, /* connect */ - wolfssl_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_getsock, /* getsock */ - wolfssl_get_internals, /* get_internals */ - wolfssl_close, /* close_one */ - Curl_none_close_all, /* close_all */ - wolfssl_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ - wolfssl_sha256sum, /* sha256sum */ - NULL, /* associate_connection */ - NULL /* disassociate_connection */ -}; - -#endif +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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
+ *
+ ***************************************************************************/
+
+/*
+ * Source file for all wolfSSL specific code for the TLS/SSL layer. No code
+ * but vtls.c should ever call or use these functions.
+ *
+ */
+
+#include "curl_setup.h"
+
+#ifdef USE_WOLFSSL
+
+#define WOLFSSL_OPTIONS_IGNORE_SYS
+#include <wolfssl/version.h>
+#include <wolfssl/options.h>
+
+/* To determine what functions are available we rely on one or both of:
+ - the user's options.h generated by wolfSSL
+ - the symbols detected by curl's configure
+ Since they are markedly different from one another, and one or the other may
+ not be available, we do some checking below to bring things in sync. */
+
+/* HAVE_ALPN is wolfSSL's build time symbol for enabling ALPN in options.h. */
+#ifndef HAVE_ALPN
+#ifdef HAVE_WOLFSSL_USEALPN
+#define HAVE_ALPN
+#endif
+#endif
+
+#include <limits.h>
+
+#include "urldata.h"
+#include "sendf.h"
+#include "inet_pton.h"
+#include "vtls.h"
+#include "vtls_int.h"
+#include "keylog.h"
+#include "parsedate.h"
+#include "connect.h" /* for the connect timeout */
+#include "select.h"
+#include "strcase.h"
+#include "x509asn1.h"
+#include "curl_printf.h"
+#include "multiif.h"
+
+#include <wolfssl/openssl/ssl.h>
+#include <wolfssl/ssl.h>
+#include <wolfssl/error-ssl.h>
+#include "wolfssl.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* KEEP_PEER_CERT is a product of the presence of build time symbol
+ OPENSSL_EXTRA without NO_CERTS, depending on the version. KEEP_PEER_CERT is
+ in wolfSSL's settings.h, and the latter two are build time symbols in
+ options.h. */
+#ifndef KEEP_PEER_CERT
+#if defined(HAVE_WOLFSSL_GET_PEER_CERTIFICATE) || \
+ (defined(OPENSSL_EXTRA) && !defined(NO_CERTS))
+#define KEEP_PEER_CERT
+#endif
+#endif
+
+#if defined(HAVE_WOLFSSL_FULL_BIO) && HAVE_WOLFSSL_FULL_BIO
+#define USE_BIO_CHAIN
+#else
+#undef USE_BIO_CHAIN
+#endif
+
+struct ssl_backend_data {
+ SSL_CTX* ctx;
+ SSL* handle;
+};
+
+#ifdef OPENSSL_EXTRA
+/*
+ * Availability note:
+ * The TLS 1.3 secret callback (wolfSSL_set_tls13_secret_cb) was added in
+ * WolfSSL 4.4.0, but requires the -DHAVE_SECRET_CALLBACK build option. If that
+ * option is not set, then TLS 1.3 will not be logged.
+ * For TLS 1.2 and before, we use wolfSSL_get_keys().
+ * SSL_get_client_random and wolfSSL_get_keys require OPENSSL_EXTRA
+ * (--enable-opensslextra or --enable-all).
+ */
+#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13)
+static int
+wolfssl_tls13_secret_callback(SSL *ssl, int id, const unsigned char *secret,
+ int secretSz, void *ctx)
+{
+ const char *label;
+ unsigned char client_random[SSL3_RANDOM_SIZE];
+ (void)ctx;
+
+ if(!ssl || !Curl_tls_keylog_enabled()) {
+ return 0;
+ }
+
+ switch(id) {
+ case CLIENT_EARLY_TRAFFIC_SECRET:
+ label = "CLIENT_EARLY_TRAFFIC_SECRET";
+ break;
+ case CLIENT_HANDSHAKE_TRAFFIC_SECRET:
+ label = "CLIENT_HANDSHAKE_TRAFFIC_SECRET";
+ break;
+ case SERVER_HANDSHAKE_TRAFFIC_SECRET:
+ label = "SERVER_HANDSHAKE_TRAFFIC_SECRET";
+ break;
+ case CLIENT_TRAFFIC_SECRET:
+ label = "CLIENT_TRAFFIC_SECRET_0";
+ break;
+ case SERVER_TRAFFIC_SECRET:
+ label = "SERVER_TRAFFIC_SECRET_0";
+ break;
+ case EARLY_EXPORTER_SECRET:
+ label = "EARLY_EXPORTER_SECRET";
+ break;
+ case EXPORTER_SECRET:
+ label = "EXPORTER_SECRET";
+ break;
+ default:
+ return 0;
+ }
+
+ if(SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE) == 0) {
+ /* Should never happen as wolfSSL_KeepArrays() was called before. */
+ return 0;
+ }
+
+ Curl_tls_keylog_write(label, client_random, secret, secretSz);
+ return 0;
+}
+#endif /* defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) */
+
+static void
+wolfssl_log_tls12_secret(SSL *ssl)
+{
+ unsigned char *ms, *sr, *cr;
+ unsigned int msLen, srLen, crLen, i, x = 0;
+
+#if LIBWOLFSSL_VERSION_HEX >= 0x0300d000 /* >= 3.13.0 */
+ /* wolfSSL_GetVersion is available since 3.13, we use it instead of
+ * SSL_version since the latter relies on OPENSSL_ALL (--enable-opensslall or
+ * --enable-all). Failing to perform this check could result in an unusable
+ * key log line when TLS 1.3 is actually negotiated. */
+ switch(wolfSSL_GetVersion(ssl)) {
+ case WOLFSSL_SSLV3:
+ case WOLFSSL_TLSV1:
+ case WOLFSSL_TLSV1_1:
+ case WOLFSSL_TLSV1_2:
+ break;
+ default:
+ /* TLS 1.3 does not use this mechanism, the "master secret" returned below
+ * is not directly usable. */
+ return;
+ }
+#endif
+
+ if(SSL_get_keys(ssl, &ms, &msLen, &sr, &srLen, &cr, &crLen) != SSL_SUCCESS) {
+ return;
+ }
+
+ /* Check for a missing master secret and skip logging. That can happen if
+ * curl rejects the server certificate and aborts the handshake.
+ */
+ for(i = 0; i < msLen; i++) {
+ x |= ms[i];
+ }
+ if(x == 0) {
+ return;
+ }
+
+ Curl_tls_keylog_write("CLIENT_RANDOM", cr, ms, msLen);
+}
+#endif /* OPENSSL_EXTRA */
+
+static int do_file_type(const char *type)
+{
+ if(!type || !type[0])
+ return SSL_FILETYPE_PEM;
+ if(strcasecompare(type, "PEM"))
+ return SSL_FILETYPE_PEM;
+ if(strcasecompare(type, "DER"))
+ return SSL_FILETYPE_ASN1;
+ return -1;
+}
+
+#ifdef HAVE_LIBOQS
+struct group_name_map {
+ const word16 group;
+ const char *name;
+};
+
+static const struct group_name_map gnm[] = {
+ { WOLFSSL_KYBER_LEVEL1, "KYBER_LEVEL1" },
+ { WOLFSSL_KYBER_LEVEL3, "KYBER_LEVEL3" },
+ { WOLFSSL_KYBER_LEVEL5, "KYBER_LEVEL5" },
+ { WOLFSSL_NTRU_HPS_LEVEL1, "NTRU_HPS_LEVEL1" },
+ { WOLFSSL_NTRU_HPS_LEVEL3, "NTRU_HPS_LEVEL3" },
+ { WOLFSSL_NTRU_HPS_LEVEL5, "NTRU_HPS_LEVEL5" },
+ { WOLFSSL_NTRU_HRSS_LEVEL3, "NTRU_HRSS_LEVEL3" },
+ { WOLFSSL_SABER_LEVEL1, "SABER_LEVEL1" },
+ { WOLFSSL_SABER_LEVEL3, "SABER_LEVEL3" },
+ { WOLFSSL_SABER_LEVEL5, "SABER_LEVEL5" },
+ { WOLFSSL_KYBER_90S_LEVEL1, "KYBER_90S_LEVEL1" },
+ { WOLFSSL_KYBER_90S_LEVEL3, "KYBER_90S_LEVEL3" },
+ { WOLFSSL_KYBER_90S_LEVEL5, "KYBER_90S_LEVEL5" },
+ { WOLFSSL_P256_NTRU_HPS_LEVEL1, "P256_NTRU_HPS_LEVEL1" },
+ { WOLFSSL_P384_NTRU_HPS_LEVEL3, "P384_NTRU_HPS_LEVEL3" },
+ { WOLFSSL_P521_NTRU_HPS_LEVEL5, "P521_NTRU_HPS_LEVEL5" },
+ { WOLFSSL_P384_NTRU_HRSS_LEVEL3, "P384_NTRU_HRSS_LEVEL3" },
+ { WOLFSSL_P256_SABER_LEVEL1, "P256_SABER_LEVEL1" },
+ { WOLFSSL_P384_SABER_LEVEL3, "P384_SABER_LEVEL3" },
+ { WOLFSSL_P521_SABER_LEVEL5, "P521_SABER_LEVEL5" },
+ { WOLFSSL_P256_KYBER_LEVEL1, "P256_KYBER_LEVEL1" },
+ { WOLFSSL_P384_KYBER_LEVEL3, "P384_KYBER_LEVEL3" },
+ { WOLFSSL_P521_KYBER_LEVEL5, "P521_KYBER_LEVEL5" },
+ { WOLFSSL_P256_KYBER_90S_LEVEL1, "P256_KYBER_90S_LEVEL1" },
+ { WOLFSSL_P384_KYBER_90S_LEVEL3, "P384_KYBER_90S_LEVEL3" },
+ { WOLFSSL_P521_KYBER_90S_LEVEL5, "P521_KYBER_90S_LEVEL5" },
+ { 0, NULL }
+};
+#endif
+
+#ifdef USE_BIO_CHAIN
+
+static int bio_cf_create(WOLFSSL_BIO *bio)
+{
+ wolfSSL_BIO_set_shutdown(bio, 1);
+ wolfSSL_BIO_set_init(bio, 1);
+ wolfSSL_BIO_set_data(bio, NULL);
+ return 1;
+}
+
+static int bio_cf_destroy(WOLFSSL_BIO *bio)
+{
+ if(!bio)
+ return 0;
+ return 1;
+}
+
+static long bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr)
+{
+ struct Curl_cfilter *cf = BIO_get_data(bio);
+ long ret = 1;
+
+ (void)cf;
+ (void)ptr;
+ switch(cmd) {
+ case BIO_CTRL_GET_CLOSE:
+ ret = (long)wolfSSL_BIO_get_shutdown(bio);
+ break;
+ case BIO_CTRL_SET_CLOSE:
+ wolfSSL_BIO_set_shutdown(bio, (int)num);
+ break;
+ case BIO_CTRL_FLUSH:
+ /* we do no delayed writes, but if we ever would, this
+ * needs to trigger it. */
+ ret = 1;
+ break;
+ case BIO_CTRL_DUP:
+ ret = 1;
+ break;
+#ifdef BIO_CTRL_EOF
+ case BIO_CTRL_EOF:
+ /* EOF has been reached on input? */
+ return (!cf->next || !cf->next->connected);
+#endif
+ default:
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+static int bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen)
+{
+ struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct Curl_easy *data = connssl->call_data;
+ ssize_t nwritten;
+ CURLcode result = CURLE_OK;
+
+ DEBUGASSERT(data);
+ nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
+ wolfSSL_BIO_clear_retry_flags(bio);
+ if(nwritten < 0 && CURLE_AGAIN == result)
+ BIO_set_retry_read(bio);
+ return (int)nwritten;
+}
+
+static int bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
+{
+ struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct Curl_easy *data = connssl->call_data;
+ ssize_t nread;
+ CURLcode result = CURLE_OK;
+
+ DEBUGASSERT(data);
+ /* OpenSSL catches this case, so should we. */
+ if(!buf)
+ return 0;
+
+ nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
+ wolfSSL_BIO_clear_retry_flags(bio);
+ if(nread < 0 && CURLE_AGAIN == result)
+ BIO_set_retry_read(bio);
+ return (int)nread;
+}
+
+static WOLFSSL_BIO_METHOD *bio_cf_method = NULL;
+
+static void bio_cf_init_methods(void)
+{
+ bio_cf_method = wolfSSL_BIO_meth_new(BIO_TYPE_MEM, "wolfSSL CF BIO");
+ wolfSSL_BIO_meth_set_write(bio_cf_method, &bio_cf_out_write);
+ wolfSSL_BIO_meth_set_read(bio_cf_method, &bio_cf_in_read);
+ wolfSSL_BIO_meth_set_ctrl(bio_cf_method, &bio_cf_ctrl);
+ wolfSSL_BIO_meth_set_create(bio_cf_method, &bio_cf_create);
+ wolfSSL_BIO_meth_set_destroy(bio_cf_method, &bio_cf_destroy);
+}
+
+static void bio_cf_free_methods(void)
+{
+ wolfSSL_BIO_meth_free(bio_cf_method);
+}
+
+#else /* USE_BIO_CHAIN */
+
+#define bio_cf_init_methods() Curl_nop_stmt
+#define bio_cf_free_methods() Curl_nop_stmt
+
+#endif /* !USE_BIO_CHAIN */
+
+/*
+ * This function loads all the client/CA certificates and CRLs. Setup the TLS
+ * layer and do all necessary magic.
+ */
+static CURLcode
+wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ char *ciphers, *curves;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ SSL_METHOD* req_method = NULL;
+#ifdef HAVE_LIBOQS
+ word16 oqsAlg = 0;
+ size_t idx = 0;
+#endif
+#ifdef HAVE_SNI
+ bool sni = FALSE;
+#define use_sni(x) sni = (x)
+#else
+#define use_sni(x) Curl_nop_stmt
+#endif
+
+ DEBUGASSERT(backend);
+
+ if(connssl->state == ssl_connection_complete)
+ return CURLE_OK;
+
+ if(conn_config->version_max != CURL_SSLVERSION_MAX_NONE) {
+ failf(data, "wolfSSL does not support to set maximum SSL/TLS version");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ /* check to see if we've been told to use an explicit SSL/TLS version */
+ switch(conn_config->version) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+#if LIBWOLFSSL_VERSION_HEX >= 0x03003000 /* >= 3.3.0 */
+ /* minimum protocol version is set later after the CTX object is created */
+ req_method = SSLv23_client_method();
+#else
+ infof(data, "wolfSSL <3.3.0 cannot be configured to use TLS 1.0-1.2, "
+ "TLS 1.0 is used exclusively");
+ req_method = TLSv1_client_method();
+#endif
+ use_sni(TRUE);
+ break;
+ case CURL_SSLVERSION_TLSv1_0:
+#if defined(WOLFSSL_ALLOW_TLSV10) && !defined(NO_OLD_TLS)
+ req_method = TLSv1_client_method();
+ use_sni(TRUE);
+#else
+ failf(data, "wolfSSL does not support TLS 1.0");
+ return CURLE_NOT_BUILT_IN;
+#endif
+ break;
+ case CURL_SSLVERSION_TLSv1_1:
+#ifndef NO_OLD_TLS
+ req_method = TLSv1_1_client_method();
+ use_sni(TRUE);
+#else
+ failf(data, "wolfSSL does not support TLS 1.1");
+ return CURLE_NOT_BUILT_IN;
+#endif
+ break;
+ case CURL_SSLVERSION_TLSv1_2:
+ req_method = TLSv1_2_client_method();
+ use_sni(TRUE);
+ break;
+ case CURL_SSLVERSION_TLSv1_3:
+#ifdef WOLFSSL_TLS13
+ req_method = wolfTLSv1_3_client_method();
+ use_sni(TRUE);
+ break;
+#else
+ failf(data, "wolfSSL: TLS 1.3 is not yet supported");
+ return CURLE_SSL_CONNECT_ERROR;
+#endif
+ default:
+ failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ if(!req_method) {
+ failf(data, "SSL: couldn't create a method");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(backend->ctx)
+ SSL_CTX_free(backend->ctx);
+ backend->ctx = SSL_CTX_new(req_method);
+
+ if(!backend->ctx) {
+ failf(data, "SSL: couldn't create a context");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ switch(conn_config->version) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+#if LIBWOLFSSL_VERSION_HEX > 0x03004006 /* > 3.4.6 */
+ /* Versions 3.3.0 to 3.4.6 we know the minimum protocol version is
+ * whatever minimum version of TLS was built in and at least TLS 1.0. For
+ * later library versions that could change (eg TLS 1.0 built in but
+ * defaults to TLS 1.1) so we have this short circuit evaluation to find
+ * the minimum supported TLS version.
+ */
+ if((wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1) != 1) &&
+ (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_1) != 1) &&
+ (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_2) != 1)
+#ifdef WOLFSSL_TLS13
+ && (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_3) != 1)
+#endif
+ ) {
+ failf(data, "SSL: couldn't set the minimum protocol version");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+#endif
+ break;
+ }
+
+ ciphers = conn_config->cipher_list;
+ if(ciphers) {
+ if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) {
+ failf(data, "failed setting cipher list: %s", ciphers);
+ return CURLE_SSL_CIPHER;
+ }
+ infof(data, "Cipher selection: %s", ciphers);
+ }
+
+ curves = conn_config->curves;
+ if(curves) {
+
+#ifdef HAVE_LIBOQS
+ for(idx = 0; gnm[idx].name != NULL; idx++) {
+ if(strncmp(curves, gnm[idx].name, strlen(gnm[idx].name)) == 0) {
+ oqsAlg = gnm[idx].group;
+ break;
+ }
+ }
+
+ if(oqsAlg == 0)
+#endif
+ {
+ if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) {
+ failf(data, "failed setting curves list: '%s'", curves);
+ return CURLE_SSL_CIPHER;
+ }
+ }
+ }
+#ifndef NO_FILESYSTEM
+ /* load trusted cacert */
+ if(conn_config->CAfile) {
+ if(1 != SSL_CTX_load_verify_locations(backend->ctx,
+ conn_config->CAfile,
+ conn_config->CApath)) {
+ if(conn_config->verifypeer) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate verify locations:"
+ " CAfile: %s CApath: %s",
+ conn_config->CAfile?
+ conn_config->CAfile: "none",
+ conn_config->CApath?
+ conn_config->CApath : "none");
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ else {
+ /* Just continue with a warning if no strict certificate
+ verification is required. */
+ infof(data, "error setting certificate verify locations,"
+ " continuing anyway:");
+ }
+ }
+ else {
+ /* Everything is fine. */
+ infof(data, "successfully set certificate verify locations:");
+ }
+ infof(data, " CAfile: %s",
+ conn_config->CAfile ? conn_config->CAfile : "none");
+ infof(data, " CApath: %s",
+ conn_config->CApath ? conn_config->CApath : "none");
+ }
+
+ /* Load the client certificate, and private key */
+ if(ssl_config->primary.clientcert && ssl_config->key) {
+ int file_type = do_file_type(ssl_config->cert_type);
+
+ if(SSL_CTX_use_certificate_file(backend->ctx,
+ ssl_config->primary.clientcert,
+ file_type) != 1) {
+ failf(data, "unable to use client certificate (no key or wrong pass"
+ " phrase?)");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ file_type = do_file_type(ssl_config->key_type);
+ if(SSL_CTX_use_PrivateKey_file(backend->ctx, ssl_config->key,
+ file_type) != 1) {
+ failf(data, "unable to set private key");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+#endif /* !NO_FILESYSTEM */
+
+ /* SSL always tries to verify the peer, this only says whether it should
+ * fail to connect if the verification fails, or if it should continue
+ * anyway. In the latter case the result of the verification is checked with
+ * SSL_get_verify_result() below. */
+ SSL_CTX_set_verify(backend->ctx,
+ conn_config->verifypeer?SSL_VERIFY_PEER:
+ SSL_VERIFY_NONE,
+ NULL);
+
+#ifdef HAVE_SNI
+ if(sni) {
+ struct in_addr addr4;
+#ifdef ENABLE_IPV6
+ struct in6_addr addr6;
+#endif
+ size_t hostname_len = strlen(connssl->hostname);
+
+ if((hostname_len < USHRT_MAX) &&
+ !Curl_inet_pton(AF_INET, connssl->hostname, &addr4)
+#ifdef ENABLE_IPV6
+ && !Curl_inet_pton(AF_INET6, connssl->hostname, &addr6)
+#endif
+ ) {
+ size_t snilen;
+ char *snihost = Curl_ssl_snihost(data, connssl->hostname, &snilen);
+ if(!snihost ||
+ wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME, snihost,
+ (unsigned short)snilen) != 1) {
+ failf(data, "Failed to set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ }
+#endif
+
+ /* give application a chance to interfere with SSL set up. */
+ if(data->set.ssl.fsslctx) {
+ CURLcode result = (*data->set.ssl.fsslctx)(data, backend->ctx,
+ data->set.ssl.fsslctxp);
+ if(result) {
+ failf(data, "error signaled by ssl ctx callback");
+ return result;
+ }
+ }
+#ifdef NO_FILESYSTEM
+ else if(conn_config->verifypeer) {
+ failf(data, "SSL: Certificates can't be loaded because wolfSSL was built"
+ " with \"no filesystem\". Either disable peer verification"
+ " (insecure) or if you are building an application with libcurl you"
+ " can load certificates via CURLOPT_SSL_CTX_FUNCTION.");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+#endif
+
+ /* Let's make an SSL structure */
+ if(backend->handle)
+ SSL_free(backend->handle);
+ backend->handle = SSL_new(backend->ctx);
+ if(!backend->handle) {
+ failf(data, "SSL: couldn't create a handle");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+#ifdef HAVE_LIBOQS
+ if(oqsAlg) {
+ if(wolfSSL_UseKeyShare(backend->handle, oqsAlg) != WOLFSSL_SUCCESS) {
+ failf(data, "unable to use oqs KEM");
+ }
+ }
+#endif
+
+#ifdef HAVE_ALPN
+ if(cf->conn->bits.tls_enable_alpn) {
+ char protocols[128];
+ *protocols = '\0';
+
+ /* wolfSSL's ALPN protocol name list format is a comma separated string of
+ protocols in descending order of preference, eg: "h2,http/1.1" */
+
+#ifdef USE_HTTP2
+ if(data->state.httpwant >= CURL_HTTP_VERSION_2) {
+ strcpy(protocols + strlen(protocols), ALPN_H2 ",");
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
+ }
+#endif
+
+ strcpy(protocols + strlen(protocols), ALPN_HTTP_1_1);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
+
+ if(wolfSSL_UseALPN(backend->handle, protocols,
+ (unsigned)strlen(protocols),
+ WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != SSL_SUCCESS) {
+ failf(data, "SSL: failed setting ALPN protocols");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+#endif /* HAVE_ALPN */
+
+#ifdef OPENSSL_EXTRA
+ if(Curl_tls_keylog_enabled()) {
+ /* Ensure the Client Random is preserved. */
+ wolfSSL_KeepArrays(backend->handle);
+#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13)
+ wolfSSL_set_tls13_secret_cb(backend->handle,
+ wolfssl_tls13_secret_callback, NULL);
+#endif
+ }
+#endif /* OPENSSL_EXTRA */
+
+#ifdef HAVE_SECURE_RENEGOTIATION
+ if(wolfSSL_UseSecureRenegotiation(backend->handle) != SSL_SUCCESS) {
+ failf(data, "SSL: failed setting secure renegotiation");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+#endif /* HAVE_SECURE_RENEGOTIATION */
+
+ /* Check if there's a cached ID we can/should use here! */
+ if(ssl_config->primary.sessionid) {
+ void *ssl_sessionid = NULL;
+
+ Curl_ssl_sessionid_lock(data);
+ if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)) {
+ /* we got a session id, use it! */
+ if(!SSL_set_session(backend->handle, ssl_sessionid)) {
+ Curl_ssl_delsessionid(data, ssl_sessionid);
+ infof(data, "Can't use session ID, going on without");
+ }
+ else
+ infof(data, "SSL re-using session ID");
+ }
+ Curl_ssl_sessionid_unlock(data);
+ }
+
+#ifdef USE_BIO_CHAIN
+ {
+ WOLFSSL_BIO *bio;
+
+ bio = BIO_new(bio_cf_method);
+ if(!bio)
+ return CURLE_OUT_OF_MEMORY;
+
+ wolfSSL_BIO_set_data(bio, cf);
+ wolfSSL_set_bio(backend->handle, bio, bio);
+ }
+#else /* USE_BIO_CHAIN */
+ /* pass the raw socket into the SSL layer */
+ if(!SSL_set_fd(backend->handle, (int)cf->conn->sock[cf->sockindex])) {
+ failf(data, "SSL: SSL_set_fd failed");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+#endif /* !USE_BIO_CHAIN */
+
+ connssl->connecting_state = ssl_connect_2;
+ return CURLE_OK;
+}
+
+
+static CURLcode
+wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ int ret = -1;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf)?
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY];
+
+ DEBUGASSERT(backend);
+
+ ERR_clear_error();
+
+ /* Enable RFC2818 checks */
+ if(conn_config->verifyhost) {
+ char *snihost = Curl_ssl_snihost(data, connssl->hostname, NULL);
+ if(!snihost ||
+ (wolfSSL_check_domain_name(backend->handle, snihost) == SSL_FAILURE))
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ ret = SSL_connect(backend->handle);
+
+#ifdef OPENSSL_EXTRA
+ if(Curl_tls_keylog_enabled()) {
+ /* If key logging is enabled, wait for the handshake to complete and then
+ * proceed with logging secrets (for TLS 1.2 or older).
+ *
+ * During the handshake (ret==-1), wolfSSL_want_read() is true as it waits
+ * for the server response. At that point the master secret is not yet
+ * available, so we must not try to read it.
+ * To log the secret on completion with a handshake failure, detect
+ * completion via the observation that there is nothing to read or write.
+ * Note that OpenSSL SSL_want_read() is always true here. If wolfSSL ever
+ * changes, the worst case is that no key is logged on error.
+ */
+ if(ret == SSL_SUCCESS ||
+ (!wolfSSL_want_read(backend->handle) &&
+ !wolfSSL_want_write(backend->handle))) {
+ wolfssl_log_tls12_secret(backend->handle);
+ /* Client Random and master secrets are no longer needed, erase these.
+ * Ignored while the handshake is still in progress. */
+ wolfSSL_FreeArrays(backend->handle);
+ }
+ }
+#endif /* OPENSSL_EXTRA */
+
+ if(ret != 1) {
+ char error_buffer[WOLFSSL_MAX_ERROR_SZ];
+ int detail = SSL_get_error(backend->handle, ret);
+
+ if(SSL_ERROR_WANT_READ == detail) {
+ connssl->connecting_state = ssl_connect_2_reading;
+ return CURLE_OK;
+ }
+ else if(SSL_ERROR_WANT_WRITE == detail) {
+ connssl->connecting_state = ssl_connect_2_writing;
+ return CURLE_OK;
+ }
+ /* There is no easy way to override only the CN matching.
+ * This will enable the override of both mismatching SubjectAltNames
+ * as also mismatching CN fields */
+ else if(DOMAIN_NAME_MISMATCH == detail) {
+#if 1
+ failf(data, " subject alt name(s) or common name do not match \"%s\"",
+ connssl->dispname);
+ return CURLE_PEER_FAILED_VERIFICATION;
+#else
+ /* When the wolfssl_check_domain_name() is used and you desire to
+ * continue on a DOMAIN_NAME_MISMATCH, i.e. 'ssl_config.verifyhost
+ * == 0', CyaSSL version 2.4.0 will fail with an INCOMPLETE_DATA
+ * error. The only way to do this is currently to switch the
+ * Wolfssl_check_domain_name() in and out based on the
+ * 'ssl_config.verifyhost' value. */
+ if(conn_config->verifyhost) {
+ failf(data,
+ " subject alt name(s) or common name do not match \"%s\"\n",
+ connssl->dispname);
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else {
+ infof(data,
+ " subject alt name(s) and/or common name do not match \"%s\"",
+ connssl->dispname);
+ return CURLE_OK;
+ }
+#endif
+ }
+#if LIBWOLFSSL_VERSION_HEX >= 0x02007000 /* 2.7.0 */
+ else if(ASN_NO_SIGNER_E == detail) {
+ if(conn_config->verifypeer) {
+ failf(data, " CA signer not available for verification");
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ else {
+ /* Just continue with a warning if no strict certificate
+ verification is required. */
+ infof(data, "CA signer not available for verification, "
+ "continuing anyway");
+ }
+ }
+#endif
+ else {
+ failf(data, "SSL_connect failed with error %d: %s", detail,
+ ERR_error_string(detail, error_buffer));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+ if(pinnedpubkey) {
+#ifdef KEEP_PEER_CERT
+ X509 *x509;
+ const char *x509_der;
+ int x509_der_len;
+ struct Curl_X509certificate x509_parsed;
+ struct Curl_asn1Element *pubkey;
+ CURLcode result;
+
+ x509 = SSL_get_peer_certificate(backend->handle);
+ if(!x509) {
+ failf(data, "SSL: failed retrieving server certificate");
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ }
+
+ x509_der = (const char *)wolfSSL_X509_get_der(x509, &x509_der_len);
+ if(!x509_der) {
+ failf(data, "SSL: failed retrieving ASN.1 server certificate");
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ }
+
+ memset(&x509_parsed, 0, sizeof(x509_parsed));
+ if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len))
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+
+ pubkey = &x509_parsed.subjectPublicKeyInfo;
+ if(!pubkey->header || pubkey->end <= pubkey->header) {
+ failf(data, "SSL: failed retrieving public key from server certificate");
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ }
+
+ result = Curl_pin_peer_pubkey(data,
+ pinnedpubkey,
+ (const unsigned char *)pubkey->header,
+ (size_t)(pubkey->end - pubkey->header));
+ if(result) {
+ failf(data, "SSL: public key does not match pinned public key");
+ return result;
+ }
+#else
+ failf(data, "Library lacks pinning support built-in");
+ return CURLE_NOT_BUILT_IN;
+#endif
+ }
+
+#ifdef HAVE_ALPN
+ if(cf->conn->bits.tls_enable_alpn) {
+ int rc;
+ char *protocol = NULL;
+ unsigned short protocol_len = 0;
+
+ rc = wolfSSL_ALPN_GetProtocol(backend->handle, &protocol, &protocol_len);
+
+ if(rc == SSL_SUCCESS) {
+ infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, protocol_len, protocol);
+
+ if(protocol_len == ALPN_HTTP_1_1_LENGTH &&
+ !memcmp(protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH))
+ cf->conn->alpn = CURL_HTTP_VERSION_1_1;
+#ifdef USE_HTTP2
+ else if(data->state.httpwant >= CURL_HTTP_VERSION_2 &&
+ protocol_len == ALPN_H2_LENGTH &&
+ !memcmp(protocol, ALPN_H2, ALPN_H2_LENGTH))
+ cf->conn->alpn = CURL_HTTP_VERSION_2;
+#endif
+ else
+ infof(data, "ALPN, unrecognized protocol %.*s", protocol_len,
+ protocol);
+ Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ }
+ else if(rc == SSL_ALPN_NOT_FOUND)
+ infof(data, VTLS_INFOF_NO_ALPN);
+ else {
+ failf(data, "ALPN, failure getting protocol, error %d", rc);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+#endif /* HAVE_ALPN */
+
+ connssl->connecting_state = ssl_connect_3;
+#if (LIBWOLFSSL_VERSION_HEX >= 0x03009010)
+ infof(data, "SSL connection using %s / %s",
+ wolfSSL_get_version(backend->handle),
+ wolfSSL_get_cipher_name(backend->handle));
+#else
+ infof(data, "SSL connected");
+#endif
+
+ return CURLE_OK;
+}
+
+
+static CURLcode
+wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+
+ DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+ DEBUGASSERT(backend);
+
+ if(ssl_config->primary.sessionid) {
+ bool incache;
+ bool added = FALSE;
+ void *old_ssl_sessionid = NULL;
+ /* SSL_get1_session allocates memory that has to be freed. */
+ SSL_SESSION *our_ssl_sessionid = SSL_get1_session(backend->handle);
+
+ if(our_ssl_sessionid) {
+ Curl_ssl_sessionid_lock(data);
+ incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL));
+ if(incache) {
+ if(old_ssl_sessionid != our_ssl_sessionid) {
+ infof(data, "old SSL session ID is stale, removing");
+ Curl_ssl_delsessionid(data, old_ssl_sessionid);
+ incache = FALSE;
+ }
+ }
+
+ if(!incache) {
+ result = Curl_ssl_addsessionid(cf, data, our_ssl_sessionid, 0, NULL);
+ if(result) {
+ Curl_ssl_sessionid_unlock(data);
+ SSL_SESSION_free(our_ssl_sessionid);
+ failf(data, "failed to store ssl session");
+ return result;
+ }
+ else {
+ added = TRUE;
+ }
+ }
+ Curl_ssl_sessionid_unlock(data);
+
+ if(!added) {
+ /* If the session info wasn't added to the cache, free our copy. */
+ SSL_SESSION_free(our_ssl_sessionid);
+ }
+ }
+ }
+
+ connssl->connecting_state = ssl_connect_done;
+
+ return result;
+}
+
+
+static ssize_t wolfssl_send(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *mem,
+ size_t len,
+ CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ char error_buffer[WOLFSSL_MAX_ERROR_SZ];
+ int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
+ int rc;
+
+ DEBUGASSERT(backend);
+
+ ERR_clear_error();
+
+ rc = SSL_write(backend->handle, mem, memlen);
+
+ if(rc <= 0) {
+ int err = SSL_get_error(backend->handle, rc);
+
+ switch(err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ /* there's data pending, re-invoke SSL_write() */
+ *curlcode = CURLE_AGAIN;
+ return -1;
+ default:
+ failf(data, "SSL write: %s, errno %d",
+ ERR_error_string(err, error_buffer),
+ SOCKERRNO);
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+ }
+ return rc;
+}
+
+static void wolfssl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+
+ (void) data;
+
+ DEBUGASSERT(backend);
+
+ if(backend->handle) {
+ char buf[32];
+ /* Maybe the server has already sent a close notify alert.
+ Read it to avoid an RST on the TCP connection. */
+ (void)SSL_read(backend->handle, buf, (int)sizeof(buf));
+ (void)SSL_shutdown(backend->handle);
+ SSL_free(backend->handle);
+ backend->handle = NULL;
+ }
+ if(backend->ctx) {
+ SSL_CTX_free(backend->ctx);
+ backend->ctx = NULL;
+ }
+}
+
+static ssize_t wolfssl_recv(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ char *buf,
+ size_t buffersize,
+ CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ char error_buffer[WOLFSSL_MAX_ERROR_SZ];
+ int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
+ int nread;
+
+ DEBUGASSERT(backend);
+
+ ERR_clear_error();
+
+ nread = SSL_read(backend->handle, buf, buffsize);
+
+ if(nread <= 0) {
+ int err = SSL_get_error(backend->handle, nread);
+
+ switch(err) {
+ case SSL_ERROR_ZERO_RETURN: /* no more data */
+ break;
+ case SSL_ERROR_NONE:
+ /* FALLTHROUGH */
+ case SSL_ERROR_WANT_READ:
+ /* FALLTHROUGH */
+ case SSL_ERROR_WANT_WRITE:
+ /* there's data pending, re-invoke SSL_read() */
+ *curlcode = CURLE_AGAIN;
+ return -1;
+ default:
+ failf(data, "SSL read: %s, errno %d",
+ ERR_error_string(err, error_buffer), SOCKERRNO);
+ *curlcode = CURLE_RECV_ERROR;
+ return -1;
+ }
+ }
+ return nread;
+}
+
+
+static void wolfssl_session_free(void *ptr)
+{
+ SSL_SESSION_free(ptr);
+}
+
+
+static size_t wolfssl_version(char *buffer, size_t size)
+{
+#if LIBWOLFSSL_VERSION_HEX >= 0x03006000
+ return msnprintf(buffer, size, "wolfSSL/%s", wolfSSL_lib_version());
+#elif defined(WOLFSSL_VERSION)
+ return msnprintf(buffer, size, "wolfSSL/%s", WOLFSSL_VERSION);
+#endif
+}
+
+
+static int wolfssl_init(void)
+{
+ int ret;
+
+#ifdef OPENSSL_EXTRA
+ Curl_tls_keylog_open();
+#endif
+ ret = (wolfSSL_Init() == SSL_SUCCESS);
+ bio_cf_init_methods();
+ return ret;
+}
+
+
+static void wolfssl_cleanup(void)
+{
+ bio_cf_free_methods();
+ wolfSSL_Cleanup();
+#ifdef OPENSSL_EXTRA
+ Curl_tls_keylog_close();
+#endif
+}
+
+
+static bool wolfssl_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ struct ssl_connect_data *ctx = cf->ctx;
+
+ (void)data;
+ DEBUGASSERT(ctx && ctx->backend);
+ if(ctx->backend->handle) /* SSL is in use */
+ return (0 != SSL_pending(ctx->backend->handle)) ? TRUE : FALSE;
+ else
+ return FALSE;
+}
+
+
+/*
+ * This function is called to shut down the SSL layer but keep the
+ * socket open (CCC - Clear Command Channel)
+ */
+static int wolfssl_shutdown(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *ctx = cf->ctx;
+ int retval = 0;
+
+ (void)data;
+ DEBUGASSERT(ctx && ctx->backend);
+
+ if(ctx->backend->handle) {
+ ERR_clear_error();
+ SSL_free(ctx->backend->handle);
+ ctx->backend->handle = NULL;
+ }
+ return retval;
+}
+
+
+static CURLcode
+wolfssl_connect_common(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool nonblocking,
+ bool *done)
+{
+ CURLcode result;
+ struct ssl_connect_data *connssl = cf->ctx;
+ curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ int what;
+
+ /* check if the connection has already been established */
+ if(ssl_connection_complete == connssl->state) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ if(ssl_connect_1 == connssl->connecting_state) {
+ /* Find out how much more time we're allowed */
+ const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+ if(timeout_ms < 0) {
+ /* no need to continue if time already is up */
+ failf(data, "SSL connection timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
+ result = wolfssl_connect_step1(cf, data);
+ if(result)
+ return result;
+ }
+
+ while(ssl_connect_2 == connssl->connecting_state ||
+ ssl_connect_2_reading == connssl->connecting_state ||
+ ssl_connect_2_writing == connssl->connecting_state) {
+
+ /* check allowed time left */
+ const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+ if(timeout_ms < 0) {
+ /* no need to continue if time already is up */
+ failf(data, "SSL connection timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
+ /* if ssl is expecting something, check if it's available. */
+ if(connssl->connecting_state == ssl_connect_2_reading
+ || connssl->connecting_state == ssl_connect_2_writing) {
+
+ curl_socket_t writefd = ssl_connect_2_writing ==
+ connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+ curl_socket_t readfd = ssl_connect_2_reading ==
+ connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+
+ what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
+ nonblocking?0:timeout_ms);
+ if(what < 0) {
+ /* fatal error */
+ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else if(0 == what) {
+ if(nonblocking) {
+ *done = FALSE;
+ return CURLE_OK;
+ }
+ else {
+ /* timeout */
+ failf(data, "SSL connection timeout");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+ }
+ /* socket is readable or writable */
+ }
+
+ /* Run transaction, and return to the caller if it failed or if
+ * this connection is part of a multi handle and this loop would
+ * execute again. This permits the owner of a multi handle to
+ * abort a connection attempt before step2 has completed while
+ * ensuring that a client using select() or epoll() will always
+ * have a valid fdset to wait on.
+ */
+ result = wolfssl_connect_step2(cf, data);
+ if(result || (nonblocking &&
+ (ssl_connect_2 == connssl->connecting_state ||
+ ssl_connect_2_reading == connssl->connecting_state ||
+ ssl_connect_2_writing == connssl->connecting_state)))
+ return result;
+ } /* repeat step2 until all transactions are done. */
+
+ if(ssl_connect_3 == connssl->connecting_state) {
+ result = wolfssl_connect_step3(cf, data);
+ if(result)
+ return result;
+ }
+
+ if(ssl_connect_done == connssl->connecting_state) {
+ connssl->state = ssl_connection_complete;
+ *done = TRUE;
+ }
+ else
+ *done = FALSE;
+
+ /* Reset our connect state machine */
+ connssl->connecting_state = ssl_connect_1;
+
+ return CURLE_OK;
+}
+
+
+static CURLcode wolfssl_connect_nonblocking(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
+{
+ return wolfssl_connect_common(cf, data, TRUE, done);
+}
+
+
+static CURLcode wolfssl_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ CURLcode result;
+ bool done = FALSE;
+
+ result = wolfssl_connect_common(cf, data, FALSE, &done);
+ if(result)
+ return result;
+
+ DEBUGASSERT(done);
+
+ return CURLE_OK;
+}
+
+static CURLcode wolfssl_random(struct Curl_easy *data,
+ unsigned char *entropy, size_t length)
+{
+ WC_RNG rng;
+ (void)data;
+ if(wc_InitRng(&rng))
+ return CURLE_FAILED_INIT;
+ if(length > UINT_MAX)
+ return CURLE_FAILED_INIT;
+ if(wc_RNG_GenerateBlock(&rng, entropy, (unsigned)length))
+ return CURLE_FAILED_INIT;
+ if(wc_FreeRng(&rng))
+ return CURLE_FAILED_INIT;
+ return CURLE_OK;
+}
+
+static CURLcode wolfssl_sha256sum(const unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *sha256sum /* output */,
+ size_t unused)
+{
+ wc_Sha256 SHA256pw;
+ (void)unused;
+ wc_InitSha256(&SHA256pw);
+ wc_Sha256Update(&SHA256pw, tmp, (word32)tmplen);
+ wc_Sha256Final(&SHA256pw, sha256sum);
+ return CURLE_OK;
+}
+
+static void *wolfssl_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
+{
+ struct ssl_backend_data *backend = connssl->backend;
+ (void)info;
+ DEBUGASSERT(backend);
+ return backend->handle;
+}
+
+const struct Curl_ssl Curl_ssl_wolfssl = {
+ { CURLSSLBACKEND_WOLFSSL, "WolfSSL" }, /* info */
+
+#ifdef KEEP_PEER_CERT
+ SSLSUPP_PINNEDPUBKEY |
+#endif
+#ifdef USE_BIO_CHAIN
+ SSLSUPP_HTTPS_PROXY |
+#endif
+ SSLSUPP_SSL_CTX,
+
+ sizeof(struct ssl_backend_data),
+
+ wolfssl_init, /* init */
+ wolfssl_cleanup, /* cleanup */
+ wolfssl_version, /* version */
+ Curl_none_check_cxn, /* check_cxn */
+ wolfssl_shutdown, /* shutdown */
+ wolfssl_data_pending, /* data_pending */
+ wolfssl_random, /* random */
+ Curl_none_cert_status_request, /* cert_status_request */
+ wolfssl_connect, /* connect */
+ wolfssl_connect_nonblocking, /* connect_nonblocking */
+ Curl_ssl_get_select_socks, /* getsock */
+ wolfssl_get_internals, /* get_internals */
+ wolfssl_close, /* close_one */
+ Curl_none_close_all, /* close_all */
+ wolfssl_session_free, /* session_free */
+ Curl_none_set_engine, /* set_engine */
+ Curl_none_set_engine_default, /* set_engine_default */
+ Curl_none_engines_list, /* engines_list */
+ Curl_none_false_start, /* false_start */
+ wolfssl_sha256sum, /* sha256sum */
+ NULL, /* associate_connection */
+ NULL, /* disassociate_connection */
+ NULL, /* free_multi_ssl_backend_data */
+ wolfssl_recv, /* recv decrypted data */
+ wolfssl_send, /* send data to encrypt */
+};
+
+#endif
diff --git a/libs/libcurl/src/vtls/wolfssl.h b/libs/libcurl/src/vtls/wolfssl.h index b2e7c3fde2..78bf07aa92 100644 --- a/libs/libcurl/src/vtls/wolfssl.h +++ b/libs/libcurl/src/vtls/wolfssl.h @@ -1,33 +1,33 @@ -#ifndef HEADER_CURL_WOLFSSL_H -#define HEADER_CURL_WOLFSSL_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_WOLFSSL - -extern const struct Curl_ssl Curl_ssl_wolfssl; - -#endif /* USE_WOLFSSL */ -#endif /* HEADER_CURL_WOLFSSL_H */ +#ifndef HEADER_CURL_WOLFSSL_H
+#define HEADER_CURL_WOLFSSL_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_WOLFSSL
+
+extern const struct Curl_ssl Curl_ssl_wolfssl;
+
+#endif /* USE_WOLFSSL */
+#endif /* HEADER_CURL_WOLFSSL_H */
diff --git a/libs/libcurl/src/vtls/x509asn1.c b/libs/libcurl/src/vtls/x509asn1.c index 0cfcbe87da..3ebcf16ce9 100644 --- a/libs/libcurl/src/vtls/x509asn1.c +++ b/libs/libcurl/src/vtls/x509asn1.c @@ -1,1426 +1,1428 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ - defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) - -#if defined(USE_GSKIT) || defined(USE_WOLFSSL) || defined(USE_SCHANNEL) -#define WANT_PARSEX509 /* uses Curl_parseX509() */ -#endif - -#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ - defined(USE_SCHANNEL) || defined(USE_SECTRANSP) -#define WANT_EXTRACT_CERTINFO /* uses Curl_extract_certinfo() */ -#define WANT_PARSEX509 /* ... uses Curl_parseX509() */ -#endif - -#if defined(USE_GSKIT) -#define WANT_VERIFYHOST /* uses Curl_verifyhost () */ -#define WANT_PARSEX509 /* ... uses Curl_parseX509() */ -#endif - -#include <curl/curl.h> -#include "urldata.h" -#include "strcase.h" -#include "curl_ctype.h" -#include "hostcheck.h" -#include "vtls/vtls.h" -#include "sendf.h" -#include "inet_pton.h" -#include "curl_base64.h" -#include "x509asn1.h" -#include "dynbuf.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* - * Constants. - */ - -/* Largest supported ASN.1 structure. */ -#define CURL_ASN1_MAX ((size_t) 0x40000) /* 256K */ - -/* ASN.1 classes. */ -#define CURL_ASN1_UNIVERSAL 0 -#define CURL_ASN1_APPLICATION 1 -#define CURL_ASN1_CONTEXT_SPECIFIC 2 -#define CURL_ASN1_PRIVATE 3 - -/* ASN.1 types. */ -#define CURL_ASN1_BOOLEAN 1 -#define CURL_ASN1_INTEGER 2 -#define CURL_ASN1_BIT_STRING 3 -#define CURL_ASN1_OCTET_STRING 4 -#define CURL_ASN1_NULL 5 -#define CURL_ASN1_OBJECT_IDENTIFIER 6 -#define CURL_ASN1_OBJECT_DESCRIPTOR 7 -#define CURL_ASN1_INSTANCE_OF 8 -#define CURL_ASN1_REAL 9 -#define CURL_ASN1_ENUMERATED 10 -#define CURL_ASN1_EMBEDDED 11 -#define CURL_ASN1_UTF8_STRING 12 -#define CURL_ASN1_RELATIVE_OID 13 -#define CURL_ASN1_SEQUENCE 16 -#define CURL_ASN1_SET 17 -#define CURL_ASN1_NUMERIC_STRING 18 -#define CURL_ASN1_PRINTABLE_STRING 19 -#define CURL_ASN1_TELETEX_STRING 20 -#define CURL_ASN1_VIDEOTEX_STRING 21 -#define CURL_ASN1_IA5_STRING 22 -#define CURL_ASN1_UTC_TIME 23 -#define CURL_ASN1_GENERALIZED_TIME 24 -#define CURL_ASN1_GRAPHIC_STRING 25 -#define CURL_ASN1_VISIBLE_STRING 26 -#define CURL_ASN1_GENERAL_STRING 27 -#define CURL_ASN1_UNIVERSAL_STRING 28 -#define CURL_ASN1_CHARACTER_STRING 29 -#define CURL_ASN1_BMP_STRING 30 - -#ifdef WANT_EXTRACT_CERTINFO -/* ASN.1 OID table entry. */ -struct Curl_OID { - const char *numoid; /* Dotted-numeric OID. */ - const char *textoid; /* OID name. */ -}; - -/* ASN.1 OIDs. */ -static const char cnOID[] = "2.5.4.3"; /* Common name. */ -static const char sanOID[] = "2.5.29.17"; /* Subject alternative name. */ - -static const struct Curl_OID OIDtable[] = { - { "1.2.840.10040.4.1", "dsa" }, - { "1.2.840.10040.4.3", "dsa-with-sha1" }, - { "1.2.840.10045.2.1", "ecPublicKey" }, - { "1.2.840.10045.3.0.1", "c2pnb163v1" }, - { "1.2.840.10045.4.1", "ecdsa-with-SHA1" }, - { "1.2.840.10046.2.1", "dhpublicnumber" }, - { "1.2.840.113549.1.1.1", "rsaEncryption" }, - { "1.2.840.113549.1.1.2", "md2WithRSAEncryption" }, - { "1.2.840.113549.1.1.4", "md5WithRSAEncryption" }, - { "1.2.840.113549.1.1.5", "sha1WithRSAEncryption" }, - { "1.2.840.113549.1.1.10", "RSASSA-PSS" }, - { "1.2.840.113549.1.1.14", "sha224WithRSAEncryption" }, - { "1.2.840.113549.1.1.11", "sha256WithRSAEncryption" }, - { "1.2.840.113549.1.1.12", "sha384WithRSAEncryption" }, - { "1.2.840.113549.1.1.13", "sha512WithRSAEncryption" }, - { "1.2.840.113549.2.2", "md2" }, - { "1.2.840.113549.2.5", "md5" }, - { "1.3.14.3.2.26", "sha1" }, - { cnOID, "CN" }, - { "2.5.4.4", "SN" }, - { "2.5.4.5", "serialNumber" }, - { "2.5.4.6", "C" }, - { "2.5.4.7", "L" }, - { "2.5.4.8", "ST" }, - { "2.5.4.9", "streetAddress" }, - { "2.5.4.10", "O" }, - { "2.5.4.11", "OU" }, - { "2.5.4.12", "title" }, - { "2.5.4.13", "description" }, - { "2.5.4.17", "postalCode" }, - { "2.5.4.41", "name" }, - { "2.5.4.42", "givenName" }, - { "2.5.4.43", "initials" }, - { "2.5.4.44", "generationQualifier" }, - { "2.5.4.45", "X500UniqueIdentifier" }, - { "2.5.4.46", "dnQualifier" }, - { "2.5.4.65", "pseudonym" }, - { "1.2.840.113549.1.9.1", "emailAddress" }, - { "2.5.4.72", "role" }, - { sanOID, "subjectAltName" }, - { "2.5.29.18", "issuerAltName" }, - { "2.5.29.19", "basicConstraints" }, - { "2.16.840.1.101.3.4.2.4", "sha224" }, - { "2.16.840.1.101.3.4.2.1", "sha256" }, - { "2.16.840.1.101.3.4.2.2", "sha384" }, - { "2.16.840.1.101.3.4.2.3", "sha512" }, - { (const char *) NULL, (const char *) NULL } -}; - -#endif /* WANT_EXTRACT_CERTINFO */ - -/* - * Lightweight ASN.1 parser. - * In particular, it does not check for syntactic/lexical errors. - * It is intended to support certificate information gathering for SSL backends - * that offer a mean to get certificates as a whole, but do not supply - * entry points to get particular certificate sub-fields. - * Please note there is no pretention here to rewrite a full SSL library. - */ - -static const char *getASN1Element(struct Curl_asn1Element *elem, - const char *beg, const char *end) - WARN_UNUSED_RESULT; - -static const char *getASN1Element(struct Curl_asn1Element *elem, - const char *beg, const char *end) -{ - unsigned char b; - unsigned long len; - struct Curl_asn1Element lelem; - - /* Get a single ASN.1 element into `elem', parse ASN.1 string at `beg' - ending at `end'. - Returns a pointer in source string after the parsed element, or NULL - if an error occurs. */ - if(!beg || !end || beg >= end || !*beg || - (size_t)(end - beg) > CURL_ASN1_MAX) - return NULL; - - /* Process header byte. */ - elem->header = beg; - b = (unsigned char) *beg++; - elem->constructed = (b & 0x20) != 0; - elem->class = (b >> 6) & 3; - b &= 0x1F; - if(b == 0x1F) - return NULL; /* Long tag values not supported here. */ - elem->tag = b; - - /* Process length. */ - if(beg >= end) - return NULL; - b = (unsigned char) *beg++; - if(!(b & 0x80)) - len = b; - else if(!(b &= 0x7F)) { - /* Unspecified length. Since we have all the data, we can determine the - effective length by skipping element until an end element is found. */ - if(!elem->constructed) - return NULL; - elem->beg = beg; - while(beg < end && *beg) { - beg = getASN1Element(&lelem, beg, end); - if(!beg) - return NULL; - } - if(beg >= end) - return NULL; - elem->end = beg; - return beg + 1; - } - else if((unsigned)b > (size_t)(end - beg)) - return NULL; /* Does not fit in source. */ - else { - /* Get long length. */ - len = 0; - do { - if(len & 0xFF000000L) - return NULL; /* Lengths > 32 bits are not supported. */ - len = (len << 8) | (unsigned char) *beg++; - } while(--b); - } - if(len > (size_t)(end - beg)) - return NULL; /* Element data does not fit in source. */ - elem->beg = beg; - elem->end = beg + len; - return elem->end; -} - -#ifdef WANT_EXTRACT_CERTINFO - -/* - * Search the null terminated OID or OID identifier in local table. - * Return the table entry pointer or NULL if not found. - */ -static const struct Curl_OID *searchOID(const char *oid) -{ - const struct Curl_OID *op; - for(op = OIDtable; op->numoid; op++) - if(!strcmp(op->numoid, oid) || strcasecompare(op->textoid, oid)) - return op; - - return NULL; -} - -/* - * Convert an ASN.1 Boolean value into its string representation. Return the - * dynamically allocated string, or NULL if source is not an ASN.1 Boolean - * value. - */ - -static const char *bool2str(const char *beg, const char *end) -{ - if(end - beg != 1) - return NULL; - return strdup(*beg? "TRUE": "FALSE"); -} - -/* - * Convert an ASN.1 octet string to a printable string. - * Return the dynamically allocated string, or NULL if an error occurs. - */ -static const char *octet2str(const char *beg, const char *end) -{ - struct dynbuf buf; - CURLcode result; - - Curl_dyn_init(&buf, 3 * CURL_ASN1_MAX + 1); - result = Curl_dyn_addn(&buf, "", 0); - - while(!result && beg < end) - result = Curl_dyn_addf(&buf, "%02x:", (unsigned char) *beg++); - - return Curl_dyn_ptr(&buf); -} - -static const char *bit2str(const char *beg, const char *end) -{ - /* Convert an ASN.1 bit string to a printable string. - Return the dynamically allocated string, or NULL if an error occurs. */ - - if(++beg > end) - return NULL; - return octet2str(beg, end); -} - -/* - * Convert an ASN.1 integer value into its string representation. - * Return the dynamically allocated string, or NULL if source is not an - * ASN.1 integer value. - */ -static const char *int2str(const char *beg, const char *end) -{ - unsigned long val = 0; - size_t n = end - beg; - - if(!n) - return NULL; - - if(n > 4) - return octet2str(beg, end); - - /* Represent integers <= 32-bit as a single value. */ - if(*beg & 0x80) - val = ~val; - - do - val = (val << 8) | *(const unsigned char *) beg++; - while(beg < end); - return curl_maprintf("%s%lx", val >= 10? "0x": "", val); -} - -/* - * Perform a lazy conversion from an ASN.1 typed string to UTF8. Allocate the - * destination buffer dynamically. The allocation size will normally be too - * large: this is to avoid buffer overflows. - * Terminate the string with a nul byte and return the converted - * string length. - */ -static ssize_t -utf8asn1str(char **to, int type, const char *from, const char *end) -{ - size_t inlength = end - from; - int size = 1; - size_t outlength; - char *buf; - - *to = NULL; - switch(type) { - case CURL_ASN1_BMP_STRING: - size = 2; - break; - case CURL_ASN1_UNIVERSAL_STRING: - size = 4; - break; - case CURL_ASN1_NUMERIC_STRING: - case CURL_ASN1_PRINTABLE_STRING: - case CURL_ASN1_TELETEX_STRING: - case CURL_ASN1_IA5_STRING: - case CURL_ASN1_VISIBLE_STRING: - case CURL_ASN1_UTF8_STRING: - break; - default: - return -1; /* Conversion not supported. */ - } - - if(inlength % size) - return -1; /* Length inconsistent with character size. */ - if(inlength / size > (SIZE_T_MAX - 1) / 4) - return -1; /* Too big. */ - buf = malloc(4 * (inlength / size) + 1); - if(!buf) - return -1; /* Not enough memory. */ - - if(type == CURL_ASN1_UTF8_STRING) { - /* Just copy. */ - outlength = inlength; - if(outlength) - memcpy(buf, from, outlength); - } - else { - for(outlength = 0; from < end;) { - int charsize; - unsigned int wc; - - wc = 0; - switch(size) { - case 4: - wc = (wc << 8) | *(const unsigned char *) from++; - wc = (wc << 8) | *(const unsigned char *) from++; - /* FALLTHROUGH */ - case 2: - wc = (wc << 8) | *(const unsigned char *) from++; - /* FALLTHROUGH */ - default: /* case 1: */ - wc = (wc << 8) | *(const unsigned char *) from++; - } - charsize = 1; - if(wc >= 0x00000080) { - if(wc >= 0x00000800) { - if(wc >= 0x00010000) { - if(wc >= 0x00200000) { - free(buf); - return -1; /* Invalid char. size for target encoding. */ - } - buf[outlength + 3] = (char) (0x80 | (wc & 0x3F)); - wc = (wc >> 6) | 0x00010000; - charsize++; - } - buf[outlength + 2] = (char) (0x80 | (wc & 0x3F)); - wc = (wc >> 6) | 0x00000800; - charsize++; - } - buf[outlength + 1] = (char) (0x80 | (wc & 0x3F)); - wc = (wc >> 6) | 0x000000C0; - charsize++; - } - buf[outlength] = (char) wc; - outlength += charsize; - } - } - buf[outlength] = '\0'; - *to = buf; - return outlength; -} - -/* - * Convert an ASN.1 String into its UTF-8 string representation. - * Return the dynamically allocated string, or NULL if an error occurs. - */ -static const char *string2str(int type, const char *beg, const char *end) -{ - char *buf; - if(utf8asn1str(&buf, type, beg, end) < 0) - return NULL; - return buf; -} - -/* - * Decimal ASCII encode unsigned integer `x' into the buflen sized buffer at - * buf. Return the total number of encoded digits, even if larger than - * `buflen'. - */ -static size_t encodeUint(char *buf, size_t buflen, unsigned int x) -{ - size_t i = 0; - unsigned int y = x / 10; - - if(y) { - i = encodeUint(buf, buflen, y); - x -= y * 10; - } - if(i < buflen) - buf[i] = (char) ('0' + x); - i++; - if(i < buflen) - buf[i] = '\0'; /* Store a terminator if possible. */ - return i; -} - -/* - * Convert an ASN.1 OID into its dotted string representation. - * Store the result in th `n'-byte buffer at `buf'. - * Return the converted string length, or 0 on errors. - */ -static size_t encodeOID(char *buf, size_t buflen, - const char *beg, const char *end) -{ - size_t i; - unsigned int x; - unsigned int y; - - /* Process the first two numbers. */ - y = *(const unsigned char *) beg++; - x = y / 40; - y -= x * 40; - i = encodeUint(buf, buflen, x); - if(i < buflen) - buf[i] = '.'; - i++; - if(i >= buflen) - i += encodeUint(NULL, 0, y); - else - i += encodeUint(buf + i, buflen - i, y); - - /* Process the trailing numbers. */ - while(beg < end) { - if(i < buflen) - buf[i] = '.'; - i++; - x = 0; - do { - if(x & 0xFF000000) - return 0; - y = *(const unsigned char *) beg++; - x = (x << 7) | (y & 0x7F); - } while(y & 0x80); - if(i >= buflen) - i += encodeUint(NULL, 0, x); - else - i += encodeUint(buf + i, buflen - i, x); - } - if(i < buflen) - buf[i] = '\0'; - return i; -} - -/* - * Convert an ASN.1 OID into its dotted or symbolic string representation. - * Return the dynamically allocated string, or NULL if an error occurs. - */ - -static const char *OID2str(const char *beg, const char *end, bool symbolic) -{ - char *buf = NULL; - if(beg < end) { - size_t buflen = encodeOID(NULL, 0, beg, end); - if(buflen) { - buf = malloc(buflen + 1); /* one extra for the zero byte */ - if(buf) { - encodeOID(buf, buflen, beg, end); - buf[buflen] = '\0'; - - if(symbolic) { - const struct Curl_OID *op = searchOID(buf); - if(op) { - free(buf); - buf = strdup(op->textoid); - } - } - } - } - } - return buf; -} - -static const char *GTime2str(const char *beg, const char *end) -{ - const char *tzp; - const char *fracp; - char sec1, sec2; - size_t fracl; - size_t tzl; - const char *sep = ""; - - /* Convert an ASN.1 Generalized time to a printable string. - Return the dynamically allocated string, or NULL if an error occurs. */ - - for(fracp = beg; fracp < end && *fracp >= '0' && *fracp <= '9'; fracp++) - ; - - /* Get seconds digits. */ - sec1 = '0'; - switch(fracp - beg - 12) { - case 0: - sec2 = '0'; - break; - case 2: - sec1 = fracp[-2]; - /* FALLTHROUGH */ - case 1: - sec2 = fracp[-1]; - break; - default: - return NULL; - } - - /* Scan for timezone, measure fractional seconds. */ - tzp = fracp; - fracl = 0; - if(fracp < end && (*fracp == '.' || *fracp == ',')) { - fracp++; - do - tzp++; - while(tzp < end && *tzp >= '0' && *tzp <= '9'); - /* Strip leading zeroes in fractional seconds. */ - for(fracl = tzp - fracp - 1; fracl && fracp[fracl - 1] == '0'; fracl--) - ; - } - - /* Process timezone. */ - if(tzp >= end) - ; /* Nothing to do. */ - else if(*tzp == 'Z') { - tzp = " GMT"; - end = tzp + 4; - } - else { - sep = " "; - tzp++; - } - - tzl = end - tzp; - return curl_maprintf("%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s", - beg, beg + 4, beg + 6, - beg + 8, beg + 10, sec1, sec2, - fracl? ".": "", (int)fracl, fracp, - sep, (int)tzl, tzp); -} - -/* - * Convert an ASN.1 UTC time to a printable string. - * Return the dynamically allocated string, or NULL if an error occurs. - */ -static const char *UTime2str(const char *beg, const char *end) -{ - const char *tzp; - size_t tzl; - const char *sec; - - for(tzp = beg; tzp < end && *tzp >= '0' && *tzp <= '9'; tzp++) - ; - /* Get the seconds. */ - sec = beg + 10; - switch(tzp - sec) { - case 0: - sec = "00"; - case 2: - break; - default: - return NULL; - } - - /* Process timezone. */ - if(tzp >= end) - return NULL; - if(*tzp == 'Z') { - tzp = "GMT"; - end = tzp + 3; - } - else - tzp++; - - tzl = end - tzp; - return curl_maprintf("%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s", - 20 - (*beg >= '5'), beg, beg + 2, beg + 4, - beg + 6, beg + 8, sec, - (int)tzl, tzp); -} - -/* - * Convert an ASN.1 element to a printable string. - * Return the dynamically allocated string, or NULL if an error occurs. - */ -static const char *ASN1tostr(struct Curl_asn1Element *elem, int type) -{ - if(elem->constructed) - return NULL; /* No conversion of structured elements. */ - - if(!type) - type = elem->tag; /* Type not forced: use element tag as type. */ - - switch(type) { - case CURL_ASN1_BOOLEAN: - return bool2str(elem->beg, elem->end); - case CURL_ASN1_INTEGER: - case CURL_ASN1_ENUMERATED: - return int2str(elem->beg, elem->end); - case CURL_ASN1_BIT_STRING: - return bit2str(elem->beg, elem->end); - case CURL_ASN1_OCTET_STRING: - return octet2str(elem->beg, elem->end); - case CURL_ASN1_NULL: - return strdup(""); - case CURL_ASN1_OBJECT_IDENTIFIER: - return OID2str(elem->beg, elem->end, TRUE); - case CURL_ASN1_UTC_TIME: - return UTime2str(elem->beg, elem->end); - case CURL_ASN1_GENERALIZED_TIME: - return GTime2str(elem->beg, elem->end); - case CURL_ASN1_UTF8_STRING: - case CURL_ASN1_NUMERIC_STRING: - case CURL_ASN1_PRINTABLE_STRING: - case CURL_ASN1_TELETEX_STRING: - case CURL_ASN1_IA5_STRING: - case CURL_ASN1_VISIBLE_STRING: - case CURL_ASN1_UNIVERSAL_STRING: - case CURL_ASN1_BMP_STRING: - return string2str(type, elem->beg, elem->end); - } - - return NULL; /* Unsupported. */ -} - -/* - * ASCII encode distinguished name at `dn' into the `buflen'-sized buffer at - * `buf'. - * - * Returns the total string length, even if larger than `buflen' or -1 on - * error. - */ -static ssize_t encodeDN(char *buf, size_t buflen, struct Curl_asn1Element *dn) -{ - struct Curl_asn1Element rdn; - struct Curl_asn1Element atv; - struct Curl_asn1Element oid; - struct Curl_asn1Element value; - size_t l = 0; - const char *p1; - const char *p2; - const char *p3; - const char *str; - - for(p1 = dn->beg; p1 < dn->end;) { - p1 = getASN1Element(&rdn, p1, dn->end); - if(!p1) - return -1; - for(p2 = rdn.beg; p2 < rdn.end;) { - p2 = getASN1Element(&atv, p2, rdn.end); - if(!p2) - return -1; - p3 = getASN1Element(&oid, atv.beg, atv.end); - if(!p3) - return -1; - if(!getASN1Element(&value, p3, atv.end)) - return -1; - str = ASN1tostr(&oid, 0); - if(!str) - return -1; - - /* Encode delimiter. - If attribute has a short uppercase name, delimiter is ", ". */ - if(l) { - for(p3 = str; ISUPPER(*p3); p3++) - ; - for(p3 = (*p3 || p3 - str > 2)? "/": ", "; *p3; p3++) { - if(l < buflen) - buf[l] = *p3; - l++; - } - } - - /* Encode attribute name. */ - for(p3 = str; *p3; p3++) { - if(l < buflen) - buf[l] = *p3; - l++; - } - free((char *) str); - - /* Generate equal sign. */ - if(l < buflen) - buf[l] = '='; - l++; - - /* Generate value. */ - str = ASN1tostr(&value, 0); - if(!str) - return -1; - for(p3 = str; *p3; p3++) { - if(l < buflen) - buf[l] = *p3; - l++; - } - free((char *) str); - } - } - - return l; -} - -#endif /* WANT_EXTRACT_CERTINFO */ - -#ifdef WANT_PARSEX509 -/* - * ASN.1 parse an X509 certificate into structure subfields. - * Syntax is assumed to have already been checked by the SSL backend. - * See RFC 5280. - */ -int Curl_parseX509(struct Curl_X509certificate *cert, - const char *beg, const char *end) -{ - struct Curl_asn1Element elem; - struct Curl_asn1Element tbsCertificate; - const char *ccp; - static const char defaultVersion = 0; /* v1. */ - - cert->certificate.header = NULL; - cert->certificate.beg = beg; - cert->certificate.end = end; - - /* Get the sequence content. */ - if(!getASN1Element(&elem, beg, end)) - return -1; /* Invalid bounds/size. */ - beg = elem.beg; - end = elem.end; - - /* Get tbsCertificate. */ - beg = getASN1Element(&tbsCertificate, beg, end); - if(!beg) - return -1; - /* Skip the signatureAlgorithm. */ - beg = getASN1Element(&cert->signatureAlgorithm, beg, end); - if(!beg) - return -1; - /* Get the signatureValue. */ - if(!getASN1Element(&cert->signature, beg, end)) - return -1; - - /* Parse TBSCertificate. */ - beg = tbsCertificate.beg; - end = tbsCertificate.end; - /* Get optional version, get serialNumber. */ - cert->version.header = NULL; - cert->version.beg = &defaultVersion; - cert->version.end = &defaultVersion + sizeof(defaultVersion); - beg = getASN1Element(&elem, beg, end); - if(!beg) - return -1; - if(elem.tag == 0) { - if(!getASN1Element(&cert->version, elem.beg, elem.end)) - return -1; - beg = getASN1Element(&elem, beg, end); - if(!beg) - return -1; - } - cert->serialNumber = elem; - /* Get signature algorithm. */ - beg = getASN1Element(&cert->signatureAlgorithm, beg, end); - /* Get issuer. */ - beg = getASN1Element(&cert->issuer, beg, end); - if(!beg) - return -1; - /* Get notBefore and notAfter. */ - beg = getASN1Element(&elem, beg, end); - if(!beg) - return -1; - ccp = getASN1Element(&cert->notBefore, elem.beg, elem.end); - if(!ccp) - return -1; - if(!getASN1Element(&cert->notAfter, ccp, elem.end)) - return -1; - /* Get subject. */ - beg = getASN1Element(&cert->subject, beg, end); - if(!beg) - return -1; - /* Get subjectPublicKeyAlgorithm and subjectPublicKey. */ - beg = getASN1Element(&cert->subjectPublicKeyInfo, beg, end); - if(!beg) - return -1; - ccp = getASN1Element(&cert->subjectPublicKeyAlgorithm, - cert->subjectPublicKeyInfo.beg, - cert->subjectPublicKeyInfo.end); - if(!ccp) - return -1; - if(!getASN1Element(&cert->subjectPublicKey, ccp, - cert->subjectPublicKeyInfo.end)) - return -1; - /* Get optional issuerUiqueID, subjectUniqueID and extensions. */ - cert->issuerUniqueID.tag = cert->subjectUniqueID.tag = 0; - cert->extensions.tag = elem.tag = 0; - cert->issuerUniqueID.header = cert->subjectUniqueID.header = NULL; - cert->issuerUniqueID.beg = cert->issuerUniqueID.end = ""; - cert->subjectUniqueID.beg = cert->subjectUniqueID.end = ""; - cert->extensions.header = NULL; - cert->extensions.beg = cert->extensions.end = ""; - if(beg < end) { - beg = getASN1Element(&elem, beg, end); - if(!beg) - return -1; - } - if(elem.tag == 1) { - cert->issuerUniqueID = elem; - if(beg < end) { - beg = getASN1Element(&elem, beg, end); - if(!beg) - return -1; - } - } - if(elem.tag == 2) { - cert->subjectUniqueID = elem; - if(beg < end) { - beg = getASN1Element(&elem, beg, end); - if(!beg) - return -1; - } - } - if(elem.tag == 3) - if(!getASN1Element(&cert->extensions, elem.beg, elem.end)) - return -1; - return 0; -} - -#endif /* WANT_PARSEX509 */ - -#ifdef WANT_EXTRACT_CERTINFO - -/* - * Copy at most 64-characters, terminate with a newline and returns the - * effective number of stored characters. - */ -static size_t copySubstring(char *to, const char *from) -{ - size_t i; - for(i = 0; i < 64; i++) { - to[i] = *from; - if(!*from++) - break; - } - - to[i++] = '\n'; - return i; -} - -static const char *dumpAlgo(struct Curl_asn1Element *param, - const char *beg, const char *end) -{ - struct Curl_asn1Element oid; - - /* Get algorithm parameters and return algorithm name. */ - - beg = getASN1Element(&oid, beg, end); - if(!beg) - return NULL; - param->header = NULL; - param->tag = 0; - param->beg = param->end = end; - if(beg < end) - if(!getASN1Element(param, beg, end)) - return NULL; - return OID2str(oid.beg, oid.end, TRUE); -} - -/* return 0 on success, 1 on error */ -static int do_pubkey_field(struct Curl_easy *data, int certnum, - const char *label, struct Curl_asn1Element *elem) -{ - const char *output; - CURLcode result = CURLE_OK; - - /* Generate a certificate information record for the public key. */ - - output = ASN1tostr(elem, 0); - if(output) { - if(data->set.ssl.certinfo) - result = Curl_ssl_push_certinfo(data, certnum, label, output); - if(!certnum && !result) - infof(data, " %s: %s", label, output); - free((char *) output); - } - return result ? 1 : 0; -} - -/* return 0 on success, 1 on error */ -static int do_pubkey(struct Curl_easy *data, int certnum, - const char *algo, struct Curl_asn1Element *param, - struct Curl_asn1Element *pubkey) -{ - struct Curl_asn1Element elem; - struct Curl_asn1Element pk; - const char *p; - - /* Generate all information records for the public key. */ - - if(strcasecompare(algo, "ecPublicKey")) { - /* - * ECC public key is all the data, a value of type BIT STRING mapped to - * OCTET STRING and should not be parsed as an ASN.1 value. - */ - const unsigned long len = - (unsigned long)((pubkey->end - pubkey->beg - 2) * 4); - if(!certnum) - infof(data, " ECC Public Key (%lu bits)", len); - if(data->set.ssl.certinfo) { - char q[sizeof(len) * 8 / 3 + 1]; - (void)msnprintf(q, sizeof(q), "%lu", len); - if(Curl_ssl_push_certinfo(data, certnum, "ECC Public Key", q)) - return 1; - } - return do_pubkey_field(data, certnum, "ecPublicKey", pubkey); - } - - /* Get the public key (single element). */ - if(!getASN1Element(&pk, pubkey->beg + 1, pubkey->end)) - return 1; - - if(strcasecompare(algo, "rsaEncryption")) { - const char *q; - unsigned long len; - - p = getASN1Element(&elem, pk.beg, pk.end); - if(!p) - return 1; - - /* Compute key length. */ - for(q = elem.beg; !*q && q < elem.end; q++) - ; - len = (unsigned long)((elem.end - q) * 8); - if(len) { - unsigned int i; - for(i = *(unsigned char *) q; !(i & 0x80); i <<= 1) - len--; - } - if(len > 32) - elem.beg = q; /* Strip leading zero bytes. */ - if(!certnum) - infof(data, " RSA Public Key (%lu bits)", len); - if(data->set.ssl.certinfo) { - char r[sizeof(len) * 8 / 3 + 1]; - msnprintf(r, sizeof(r), "%lu", len); - if(Curl_ssl_push_certinfo(data, certnum, "RSA Public Key", r)) - return 1; - } - /* Generate coefficients. */ - if(do_pubkey_field(data, certnum, "rsa(n)", &elem)) - return 1; - if(!getASN1Element(&elem, p, pk.end)) - return 1; - if(do_pubkey_field(data, certnum, "rsa(e)", &elem)) - return 1; - } - else if(strcasecompare(algo, "dsa")) { - p = getASN1Element(&elem, param->beg, param->end); - if(p) { - if(do_pubkey_field(data, certnum, "dsa(p)", &elem)) - return 1; - p = getASN1Element(&elem, p, param->end); - if(p) { - if(do_pubkey_field(data, certnum, "dsa(q)", &elem)) - return 1; - if(getASN1Element(&elem, p, param->end)) { - if(do_pubkey_field(data, certnum, "dsa(g)", &elem)) - return 1; - if(do_pubkey_field(data, certnum, "dsa(pub_key)", &pk)) - return 1; - } - } - } - } - else if(strcasecompare(algo, "dhpublicnumber")) { - p = getASN1Element(&elem, param->beg, param->end); - if(p) { - if(do_pubkey_field(data, certnum, "dh(p)", &elem)) - return 1; - if(getASN1Element(&elem, param->beg, param->end)) { - if(do_pubkey_field(data, certnum, "dh(g)", &elem)) - return 1; - if(do_pubkey_field(data, certnum, "dh(pub_key)", &pk)) - return 1; - } - } - } - return 0; -} - -/* - * Convert an ASN.1 distinguished name into a printable string. - * Return the dynamically allocated string, or NULL if an error occurs. - */ -static const char *DNtostr(struct Curl_asn1Element *dn) -{ - char *buf = NULL; - ssize_t buflen = encodeDN(NULL, 0, dn); - - if(buflen >= 0) { - buf = malloc(buflen + 1); - if(buf) { - if(encodeDN(buf, buflen + 1, dn) == -1) { - free(buf); - return NULL; - } - buf[buflen] = '\0'; - } - } - return buf; -} - -CURLcode Curl_extract_certinfo(struct Curl_easy *data, - int certnum, - const char *beg, - const char *end) -{ - struct Curl_X509certificate cert; - struct Curl_asn1Element param; - const char *ccp; - char *cp1; - size_t cl1; - char *cp2; - CURLcode result = CURLE_OK; - unsigned long version; - size_t i; - size_t j; - - if(!data->set.ssl.certinfo) - if(certnum) - return CURLE_OK; - - /* Prepare the certificate information for curl_easy_getinfo(). */ - - /* Extract the certificate ASN.1 elements. */ - if(Curl_parseX509(&cert, beg, end)) - return CURLE_PEER_FAILED_VERIFICATION; - - /* Subject. */ - ccp = DNtostr(&cert.subject); - if(!ccp) - return CURLE_OUT_OF_MEMORY; - if(data->set.ssl.certinfo) { - result = Curl_ssl_push_certinfo(data, certnum, "Subject", ccp); - if(result) - return result; - } - if(!certnum) - infof(data, "%2d Subject: %s", certnum, ccp); - free((char *) ccp); - - /* Issuer. */ - ccp = DNtostr(&cert.issuer); - if(!ccp) - return CURLE_OUT_OF_MEMORY; - if(data->set.ssl.certinfo) { - result = Curl_ssl_push_certinfo(data, certnum, "Issuer", ccp); - } - if(!certnum) - infof(data, " Issuer: %s", ccp); - free((char *) ccp); - if(result) - return result; - - /* Version (always fits in less than 32 bits). */ - version = 0; - for(ccp = cert.version.beg; ccp < cert.version.end; ccp++) - version = (version << 8) | *(const unsigned char *) ccp; - if(data->set.ssl.certinfo) { - ccp = curl_maprintf("%lx", version); - if(!ccp) - return CURLE_OUT_OF_MEMORY; - result = Curl_ssl_push_certinfo(data, certnum, "Version", ccp); - free((char *) ccp); - if(result) - return result; - } - if(!certnum) - infof(data, " Version: %lu (0x%lx)", version + 1, version); - - /* Serial number. */ - ccp = ASN1tostr(&cert.serialNumber, 0); - if(!ccp) - return CURLE_OUT_OF_MEMORY; - if(data->set.ssl.certinfo) - result = Curl_ssl_push_certinfo(data, certnum, "Serial Number", ccp); - if(!certnum) - infof(data, " Serial Number: %s", ccp); - free((char *) ccp); - if(result) - return result; - - /* Signature algorithm .*/ - ccp = dumpAlgo(¶m, cert.signatureAlgorithm.beg, - cert.signatureAlgorithm.end); - if(!ccp) - return CURLE_OUT_OF_MEMORY; - if(data->set.ssl.certinfo) - result = Curl_ssl_push_certinfo(data, certnum, "Signature Algorithm", ccp); - if(!certnum) - infof(data, " Signature Algorithm: %s", ccp); - free((char *) ccp); - if(result) - return result; - - /* Start Date. */ - ccp = ASN1tostr(&cert.notBefore, 0); - if(!ccp) - return CURLE_OUT_OF_MEMORY; - if(data->set.ssl.certinfo) - result = Curl_ssl_push_certinfo(data, certnum, "Start Date", ccp); - if(!certnum) - infof(data, " Start Date: %s", ccp); - free((char *) ccp); - if(result) - return result; - - /* Expire Date. */ - ccp = ASN1tostr(&cert.notAfter, 0); - if(!ccp) - return CURLE_OUT_OF_MEMORY; - if(data->set.ssl.certinfo) - result = Curl_ssl_push_certinfo(data, certnum, "Expire Date", ccp); - if(!certnum) - infof(data, " Expire Date: %s", ccp); - free((char *) ccp); - if(result) - return result; - - /* Public Key Algorithm. */ - ccp = dumpAlgo(¶m, cert.subjectPublicKeyAlgorithm.beg, - cert.subjectPublicKeyAlgorithm.end); - if(!ccp) - return CURLE_OUT_OF_MEMORY; - if(data->set.ssl.certinfo) - result = Curl_ssl_push_certinfo(data, certnum, "Public Key Algorithm", - ccp); - if(!result) { - int ret; - if(!certnum) - infof(data, " Public Key Algorithm: %s", ccp); - ret = do_pubkey(data, certnum, ccp, ¶m, &cert.subjectPublicKey); - if(ret) - result = CURLE_OUT_OF_MEMORY; /* the most likely error */ - } - free((char *) ccp); - if(result) - return result; - - /* Signature. */ - ccp = ASN1tostr(&cert.signature, 0); - if(!ccp) - return CURLE_OUT_OF_MEMORY; - if(data->set.ssl.certinfo) - result = Curl_ssl_push_certinfo(data, certnum, "Signature", ccp); - if(!certnum) - infof(data, " Signature: %s", ccp); - free((char *) ccp); - if(result) - return result; - - /* Generate PEM certificate. */ - result = Curl_base64_encode(cert.certificate.beg, - cert.certificate.end - cert.certificate.beg, - &cp1, &cl1); - if(result) - return result; - /* Compute the number of characters in final certificate string. Format is: - -----BEGIN CERTIFICATE-----\n - <max 64 base64 characters>\n - . - . - . - -----END CERTIFICATE-----\n - */ - i = 28 + cl1 + (cl1 + 64 - 1) / 64 + 26; - cp2 = malloc(i + 1); - if(!cp2) { - free(cp1); - return CURLE_OUT_OF_MEMORY; - } - /* Build the certificate string. */ - i = copySubstring(cp2, "-----BEGIN CERTIFICATE-----"); - for(j = 0; j < cl1; j += 64) - i += copySubstring(cp2 + i, cp1 + j); - i += copySubstring(cp2 + i, "-----END CERTIFICATE-----"); - cp2[i] = '\0'; - free(cp1); - if(data->set.ssl.certinfo) - result = Curl_ssl_push_certinfo(data, certnum, "Cert", cp2); - if(!certnum) - infof(data, "%s", cp2); - free(cp2); - return result; -} - -#endif /* WANT_EXTRACT_CERTINFO */ - -#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL - * or USE_SECTRANSP */ - -#ifdef WANT_VERIFYHOST - -static const char *checkOID(const char *beg, const char *end, - const char *oid) -{ - struct Curl_asn1Element e; - const char *ccp; - const char *p; - bool matched; - - /* Check if first ASN.1 element at `beg' is the given OID. - Return a pointer in the source after the OID if found, else NULL. */ - - ccp = getASN1Element(&e, beg, end); - if(!ccp || e.tag != CURL_ASN1_OBJECT_IDENTIFIER) - return NULL; - - p = OID2str(e.beg, e.end, FALSE); - if(!p) - return NULL; - - matched = !strcmp(p, oid); - free((char *) p); - return matched? ccp: NULL; -} - -CURLcode Curl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - const char *beg, const char *end) -{ - struct Curl_X509certificate cert; - struct Curl_asn1Element dn; - struct Curl_asn1Element elem; - struct Curl_asn1Element ext; - struct Curl_asn1Element name; - const char *p; - const char *q; - char *dnsname; - int matched = -1; - size_t addrlen = (size_t) -1; - ssize_t len; - const char * const hostname = SSL_HOST_NAME(); - const char * const dispname = SSL_HOST_DISPNAME(); - size_t hostlen = strlen(hostname); -#ifdef ENABLE_IPV6 - struct in6_addr addr; -#else - struct in_addr addr; -#endif - - /* Verify that connection server matches info in X509 certificate at - `beg'..`end'. */ - - if(!SSL_CONN_CONFIG(verifyhost)) - return CURLE_OK; - - if(Curl_parseX509(&cert, beg, end)) - return CURLE_PEER_FAILED_VERIFICATION; - - /* Get the server IP address. */ -#ifdef ENABLE_IPV6 - if(conn->bits.ipv6_ip && Curl_inet_pton(AF_INET6, hostname, &addr)) - addrlen = sizeof(struct in6_addr); - else -#endif - if(Curl_inet_pton(AF_INET, hostname, &addr)) - addrlen = sizeof(struct in_addr); - - /* Process extensions. */ - for(p = cert.extensions.beg; p < cert.extensions.end && matched != 1;) { - p = getASN1Element(&ext, p, cert.extensions.end); - if(!p) - return CURLE_PEER_FAILED_VERIFICATION; - - /* Check if extension is a subjectAlternativeName. */ - ext.beg = checkOID(ext.beg, ext.end, sanOID); - if(ext.beg) { - ext.beg = getASN1Element(&elem, ext.beg, ext.end); - if(!ext.beg) - return CURLE_PEER_FAILED_VERIFICATION; - /* Skip critical if present. */ - if(elem.tag == CURL_ASN1_BOOLEAN) { - ext.beg = getASN1Element(&elem, ext.beg, ext.end); - if(!ext.beg) - return CURLE_PEER_FAILED_VERIFICATION; - } - /* Parse the octet string contents: is a single sequence. */ - if(!getASN1Element(&elem, elem.beg, elem.end)) - return CURLE_PEER_FAILED_VERIFICATION; - /* Check all GeneralNames. */ - for(q = elem.beg; matched != 1 && q < elem.end;) { - q = getASN1Element(&name, q, elem.end); - if(!q) - break; - switch(name.tag) { - case 2: /* DNS name. */ - len = utf8asn1str(&dnsname, CURL_ASN1_IA5_STRING, - name.beg, name.end); - if(len > 0 && (size_t)len == strlen(dnsname)) - matched = Curl_cert_hostcheck(dnsname, - (size_t)len, hostname, hostlen); - else - matched = 0; - free(dnsname); - break; - - case 7: /* IP address. */ - matched = (size_t) (name.end - name.beg) == addrlen && - !memcmp(&addr, name.beg, addrlen); - break; - } - } - } - } - - switch(matched) { - case 1: - /* an alternative name matched the server hostname */ - infof(data, " subjectAltName: %s matched", dispname); - return CURLE_OK; - case 0: - /* an alternative name field existed, but didn't match and then - we MUST fail */ - infof(data, " subjectAltName does not match %s", dispname); - return CURLE_PEER_FAILED_VERIFICATION; - } - - /* Process subject. */ - name.header = NULL; - name.beg = name.end = ""; - q = cert.subject.beg; - /* we have to look to the last occurrence of a commonName in the - distinguished one to get the most significant one. */ - while(q < cert.subject.end) { - q = getASN1Element(&dn, q, cert.subject.end); - if(!q) - break; - for(p = dn.beg; p < dn.end;) { - p = getASN1Element(&elem, p, dn.end); - if(!p) - return CURLE_PEER_FAILED_VERIFICATION; - /* We have a DN's AttributeTypeAndValue: check it in case it's a CN. */ - elem.beg = checkOID(elem.beg, elem.end, cnOID); - if(elem.beg) - name = elem; /* Latch CN. */ - } - } - - /* Check the CN if found. */ - if(!getASN1Element(&elem, name.beg, name.end)) - failf(data, "SSL: unable to obtain common name from peer certificate"); - else { - len = utf8asn1str(&dnsname, elem.tag, elem.beg, elem.end); - if(len < 0) { - free(dnsname); - return CURLE_OUT_OF_MEMORY; - } - if(strlen(dnsname) != (size_t) len) /* Nul byte in string ? */ - failf(data, "SSL: illegal cert name field"); - else if(Curl_cert_hostcheck((const char *) dnsname, - len, hostname, hostlen)) { - infof(data, " common name: %s (matched)", dnsname); - free(dnsname); - return CURLE_OK; - } - else - failf(data, "SSL: certificate subject name '%s' does not match " - "target host name '%s'", dnsname, dispname); - free(dnsname); - } - - return CURLE_PEER_FAILED_VERIFICATION; -} - -#endif /* WANT_VERIFYHOST */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \
+ defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP)
+
+#if defined(USE_GSKIT) || defined(USE_WOLFSSL) || defined(USE_SCHANNEL)
+#define WANT_PARSEX509 /* uses Curl_parseX509() */
+#endif
+
+#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \
+ defined(USE_SCHANNEL) || defined(USE_SECTRANSP)
+#define WANT_EXTRACT_CERTINFO /* uses Curl_extract_certinfo() */
+#define WANT_PARSEX509 /* ... uses Curl_parseX509() */
+#endif
+
+#if defined(USE_GSKIT)
+#define WANT_VERIFYHOST /* uses Curl_verifyhost () */
+#define WANT_PARSEX509 /* ... uses Curl_parseX509() */
+#endif
+
+#include <curl/curl.h>
+#include "urldata.h"
+#include "strcase.h"
+#include "curl_ctype.h"
+#include "hostcheck.h"
+#include "vtls/vtls.h"
+#include "sendf.h"
+#include "inet_pton.h"
+#include "curl_base64.h"
+#include "x509asn1.h"
+#include "dynbuf.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * Constants.
+ */
+
+/* Largest supported ASN.1 structure. */
+#define CURL_ASN1_MAX ((size_t) 0x40000) /* 256K */
+
+/* ASN.1 classes. */
+#define CURL_ASN1_UNIVERSAL 0
+#define CURL_ASN1_APPLICATION 1
+#define CURL_ASN1_CONTEXT_SPECIFIC 2
+#define CURL_ASN1_PRIVATE 3
+
+/* ASN.1 types. */
+#define CURL_ASN1_BOOLEAN 1
+#define CURL_ASN1_INTEGER 2
+#define CURL_ASN1_BIT_STRING 3
+#define CURL_ASN1_OCTET_STRING 4
+#define CURL_ASN1_NULL 5
+#define CURL_ASN1_OBJECT_IDENTIFIER 6
+#define CURL_ASN1_OBJECT_DESCRIPTOR 7
+#define CURL_ASN1_INSTANCE_OF 8
+#define CURL_ASN1_REAL 9
+#define CURL_ASN1_ENUMERATED 10
+#define CURL_ASN1_EMBEDDED 11
+#define CURL_ASN1_UTF8_STRING 12
+#define CURL_ASN1_RELATIVE_OID 13
+#define CURL_ASN1_SEQUENCE 16
+#define CURL_ASN1_SET 17
+#define CURL_ASN1_NUMERIC_STRING 18
+#define CURL_ASN1_PRINTABLE_STRING 19
+#define CURL_ASN1_TELETEX_STRING 20
+#define CURL_ASN1_VIDEOTEX_STRING 21
+#define CURL_ASN1_IA5_STRING 22
+#define CURL_ASN1_UTC_TIME 23
+#define CURL_ASN1_GENERALIZED_TIME 24
+#define CURL_ASN1_GRAPHIC_STRING 25
+#define CURL_ASN1_VISIBLE_STRING 26
+#define CURL_ASN1_GENERAL_STRING 27
+#define CURL_ASN1_UNIVERSAL_STRING 28
+#define CURL_ASN1_CHARACTER_STRING 29
+#define CURL_ASN1_BMP_STRING 30
+
+#ifdef WANT_EXTRACT_CERTINFO
+/* ASN.1 OID table entry. */
+struct Curl_OID {
+ const char *numoid; /* Dotted-numeric OID. */
+ const char *textoid; /* OID name. */
+};
+
+/* ASN.1 OIDs. */
+static const char cnOID[] = "2.5.4.3"; /* Common name. */
+static const char sanOID[] = "2.5.29.17"; /* Subject alternative name. */
+
+static const struct Curl_OID OIDtable[] = {
+ { "1.2.840.10040.4.1", "dsa" },
+ { "1.2.840.10040.4.3", "dsa-with-sha1" },
+ { "1.2.840.10045.2.1", "ecPublicKey" },
+ { "1.2.840.10045.3.0.1", "c2pnb163v1" },
+ { "1.2.840.10045.4.1", "ecdsa-with-SHA1" },
+ { "1.2.840.10046.2.1", "dhpublicnumber" },
+ { "1.2.840.113549.1.1.1", "rsaEncryption" },
+ { "1.2.840.113549.1.1.2", "md2WithRSAEncryption" },
+ { "1.2.840.113549.1.1.4", "md5WithRSAEncryption" },
+ { "1.2.840.113549.1.1.5", "sha1WithRSAEncryption" },
+ { "1.2.840.113549.1.1.10", "RSASSA-PSS" },
+ { "1.2.840.113549.1.1.14", "sha224WithRSAEncryption" },
+ { "1.2.840.113549.1.1.11", "sha256WithRSAEncryption" },
+ { "1.2.840.113549.1.1.12", "sha384WithRSAEncryption" },
+ { "1.2.840.113549.1.1.13", "sha512WithRSAEncryption" },
+ { "1.2.840.113549.2.2", "md2" },
+ { "1.2.840.113549.2.5", "md5" },
+ { "1.3.14.3.2.26", "sha1" },
+ { cnOID, "CN" },
+ { "2.5.4.4", "SN" },
+ { "2.5.4.5", "serialNumber" },
+ { "2.5.4.6", "C" },
+ { "2.5.4.7", "L" },
+ { "2.5.4.8", "ST" },
+ { "2.5.4.9", "streetAddress" },
+ { "2.5.4.10", "O" },
+ { "2.5.4.11", "OU" },
+ { "2.5.4.12", "title" },
+ { "2.5.4.13", "description" },
+ { "2.5.4.17", "postalCode" },
+ { "2.5.4.41", "name" },
+ { "2.5.4.42", "givenName" },
+ { "2.5.4.43", "initials" },
+ { "2.5.4.44", "generationQualifier" },
+ { "2.5.4.45", "X500UniqueIdentifier" },
+ { "2.5.4.46", "dnQualifier" },
+ { "2.5.4.65", "pseudonym" },
+ { "1.2.840.113549.1.9.1", "emailAddress" },
+ { "2.5.4.72", "role" },
+ { sanOID, "subjectAltName" },
+ { "2.5.29.18", "issuerAltName" },
+ { "2.5.29.19", "basicConstraints" },
+ { "2.16.840.1.101.3.4.2.4", "sha224" },
+ { "2.16.840.1.101.3.4.2.1", "sha256" },
+ { "2.16.840.1.101.3.4.2.2", "sha384" },
+ { "2.16.840.1.101.3.4.2.3", "sha512" },
+ { (const char *) NULL, (const char *) NULL }
+};
+
+#endif /* WANT_EXTRACT_CERTINFO */
+
+/*
+ * Lightweight ASN.1 parser.
+ * In particular, it does not check for syntactic/lexical errors.
+ * It is intended to support certificate information gathering for SSL backends
+ * that offer a mean to get certificates as a whole, but do not supply
+ * entry points to get particular certificate sub-fields.
+ * Please note there is no pretention here to rewrite a full SSL library.
+ */
+
+static const char *getASN1Element(struct Curl_asn1Element *elem,
+ const char *beg, const char *end)
+ WARN_UNUSED_RESULT;
+
+static const char *getASN1Element(struct Curl_asn1Element *elem,
+ const char *beg, const char *end)
+{
+ unsigned char b;
+ size_t len;
+ struct Curl_asn1Element lelem;
+
+ /* Get a single ASN.1 element into `elem', parse ASN.1 string at `beg'
+ ending at `end'.
+ Returns a pointer in source string after the parsed element, or NULL
+ if an error occurs. */
+ if(!beg || !end || beg >= end || !*beg ||
+ (size_t)(end - beg) > CURL_ASN1_MAX)
+ return NULL;
+
+ /* Process header byte. */
+ elem->header = beg;
+ b = (unsigned char) *beg++;
+ elem->constructed = (b & 0x20) != 0;
+ elem->class = (b >> 6) & 3;
+ b &= 0x1F;
+ if(b == 0x1F)
+ return NULL; /* Long tag values not supported here. */
+ elem->tag = b;
+
+ /* Process length. */
+ if(beg >= end)
+ return NULL;
+ b = (unsigned char) *beg++;
+ if(!(b & 0x80))
+ len = b;
+ else if(!(b &= 0x7F)) {
+ /* Unspecified length. Since we have all the data, we can determine the
+ effective length by skipping element until an end element is found. */
+ if(!elem->constructed)
+ return NULL;
+ elem->beg = beg;
+ while(beg < end && *beg) {
+ beg = getASN1Element(&lelem, beg, end);
+ if(!beg)
+ return NULL;
+ }
+ if(beg >= end)
+ return NULL;
+ elem->end = beg;
+ return beg + 1;
+ }
+ else if((unsigned)b > (size_t)(end - beg))
+ return NULL; /* Does not fit in source. */
+ else {
+ /* Get long length. */
+ len = 0;
+ do {
+ if(len & 0xFF000000L)
+ return NULL; /* Lengths > 32 bits are not supported. */
+ len = (len << 8) | (unsigned char) *beg++;
+ } while(--b);
+ }
+ if(len > (size_t)(end - beg))
+ return NULL; /* Element data does not fit in source. */
+ elem->beg = beg;
+ elem->end = beg + len;
+ return elem->end;
+}
+
+#ifdef WANT_EXTRACT_CERTINFO
+
+/*
+ * Search the null terminated OID or OID identifier in local table.
+ * Return the table entry pointer or NULL if not found.
+ */
+static const struct Curl_OID *searchOID(const char *oid)
+{
+ const struct Curl_OID *op;
+ for(op = OIDtable; op->numoid; op++)
+ if(!strcmp(op->numoid, oid) || strcasecompare(op->textoid, oid))
+ return op;
+
+ return NULL;
+}
+
+/*
+ * Convert an ASN.1 Boolean value into its string representation. Return the
+ * dynamically allocated string, or NULL if source is not an ASN.1 Boolean
+ * value.
+ */
+
+static const char *bool2str(const char *beg, const char *end)
+{
+ if(end - beg != 1)
+ return NULL;
+ return strdup(*beg? "TRUE": "FALSE");
+}
+
+/*
+ * Convert an ASN.1 octet string to a printable string.
+ * Return the dynamically allocated string, or NULL if an error occurs.
+ */
+static const char *octet2str(const char *beg, const char *end)
+{
+ struct dynbuf buf;
+ CURLcode result;
+
+ Curl_dyn_init(&buf, 3 * CURL_ASN1_MAX + 1);
+ result = Curl_dyn_addn(&buf, "", 0);
+
+ while(!result && beg < end)
+ result = Curl_dyn_addf(&buf, "%02x:", (unsigned char) *beg++);
+
+ return Curl_dyn_ptr(&buf);
+}
+
+static const char *bit2str(const char *beg, const char *end)
+{
+ /* Convert an ASN.1 bit string to a printable string.
+ Return the dynamically allocated string, or NULL if an error occurs. */
+
+ if(++beg > end)
+ return NULL;
+ return octet2str(beg, end);
+}
+
+/*
+ * Convert an ASN.1 integer value into its string representation.
+ * Return the dynamically allocated string, or NULL if source is not an
+ * ASN.1 integer value.
+ */
+static const char *int2str(const char *beg, const char *end)
+{
+ unsigned int val = 0;
+ size_t n = end - beg;
+
+ if(!n)
+ return NULL;
+
+ if(n > 4)
+ return octet2str(beg, end);
+
+ /* Represent integers <= 32-bit as a single value. */
+ if(*beg & 0x80)
+ val = ~val;
+
+ do
+ val = (val << 8) | *(const unsigned char *) beg++;
+ while(beg < end);
+ return curl_maprintf("%s%x", val >= 10? "0x": "", val);
+}
+
+/*
+ * Perform a lazy conversion from an ASN.1 typed string to UTF8. Allocate the
+ * destination buffer dynamically. The allocation size will normally be too
+ * large: this is to avoid buffer overflows.
+ * Terminate the string with a nul byte and return the converted
+ * string length.
+ */
+static ssize_t
+utf8asn1str(char **to, int type, const char *from, const char *end)
+{
+ size_t inlength = end - from;
+ int size = 1;
+ size_t outlength;
+ char *buf;
+
+ *to = NULL;
+ switch(type) {
+ case CURL_ASN1_BMP_STRING:
+ size = 2;
+ break;
+ case CURL_ASN1_UNIVERSAL_STRING:
+ size = 4;
+ break;
+ case CURL_ASN1_NUMERIC_STRING:
+ case CURL_ASN1_PRINTABLE_STRING:
+ case CURL_ASN1_TELETEX_STRING:
+ case CURL_ASN1_IA5_STRING:
+ case CURL_ASN1_VISIBLE_STRING:
+ case CURL_ASN1_UTF8_STRING:
+ break;
+ default:
+ return -1; /* Conversion not supported. */
+ }
+
+ if(inlength % size)
+ return -1; /* Length inconsistent with character size. */
+ if(inlength / size > (SIZE_T_MAX - 1) / 4)
+ return -1; /* Too big. */
+ buf = malloc(4 * (inlength / size) + 1);
+ if(!buf)
+ return -1; /* Not enough memory. */
+
+ if(type == CURL_ASN1_UTF8_STRING) {
+ /* Just copy. */
+ outlength = inlength;
+ if(outlength)
+ memcpy(buf, from, outlength);
+ }
+ else {
+ for(outlength = 0; from < end;) {
+ int charsize;
+ unsigned int wc;
+
+ wc = 0;
+ switch(size) {
+ case 4:
+ wc = (wc << 8) | *(const unsigned char *) from++;
+ wc = (wc << 8) | *(const unsigned char *) from++;
+ /* FALLTHROUGH */
+ case 2:
+ wc = (wc << 8) | *(const unsigned char *) from++;
+ /* FALLTHROUGH */
+ default: /* case 1: */
+ wc = (wc << 8) | *(const unsigned char *) from++;
+ }
+ charsize = 1;
+ if(wc >= 0x00000080) {
+ if(wc >= 0x00000800) {
+ if(wc >= 0x00010000) {
+ if(wc >= 0x00200000) {
+ free(buf);
+ return -1; /* Invalid char. size for target encoding. */
+ }
+ buf[outlength + 3] = (char) (0x80 | (wc & 0x3F));
+ wc = (wc >> 6) | 0x00010000;
+ charsize++;
+ }
+ buf[outlength + 2] = (char) (0x80 | (wc & 0x3F));
+ wc = (wc >> 6) | 0x00000800;
+ charsize++;
+ }
+ buf[outlength + 1] = (char) (0x80 | (wc & 0x3F));
+ wc = (wc >> 6) | 0x000000C0;
+ charsize++;
+ }
+ buf[outlength] = (char) wc;
+ outlength += charsize;
+ }
+ }
+ buf[outlength] = '\0';
+ *to = buf;
+ return outlength;
+}
+
+/*
+ * Convert an ASN.1 String into its UTF-8 string representation.
+ * Return the dynamically allocated string, or NULL if an error occurs.
+ */
+static const char *string2str(int type, const char *beg, const char *end)
+{
+ char *buf;
+ if(utf8asn1str(&buf, type, beg, end) < 0)
+ return NULL;
+ return buf;
+}
+
+/*
+ * Decimal ASCII encode unsigned integer `x' into the buflen sized buffer at
+ * buf. Return the total number of encoded digits, even if larger than
+ * `buflen'.
+ */
+static size_t encodeUint(char *buf, size_t buflen, unsigned int x)
+{
+ size_t i = 0;
+ unsigned int y = x / 10;
+
+ if(y) {
+ i = encodeUint(buf, buflen, y);
+ x -= y * 10;
+ }
+ if(i < buflen)
+ buf[i] = (char) ('0' + x);
+ i++;
+ if(i < buflen)
+ buf[i] = '\0'; /* Store a terminator if possible. */
+ return i;
+}
+
+/*
+ * Convert an ASN.1 OID into its dotted string representation.
+ * Store the result in th `n'-byte buffer at `buf'.
+ * Return the converted string length, or 0 on errors.
+ */
+static size_t encodeOID(char *buf, size_t buflen,
+ const char *beg, const char *end)
+{
+ size_t i;
+ unsigned int x;
+ unsigned int y;
+
+ /* Process the first two numbers. */
+ y = *(const unsigned char *) beg++;
+ x = y / 40;
+ y -= x * 40;
+ i = encodeUint(buf, buflen, x);
+ if(i < buflen)
+ buf[i] = '.';
+ i++;
+ if(i >= buflen)
+ i += encodeUint(NULL, 0, y);
+ else
+ i += encodeUint(buf + i, buflen - i, y);
+
+ /* Process the trailing numbers. */
+ while(beg < end) {
+ if(i < buflen)
+ buf[i] = '.';
+ i++;
+ x = 0;
+ do {
+ if(x & 0xFF000000)
+ return 0;
+ y = *(const unsigned char *) beg++;
+ x = (x << 7) | (y & 0x7F);
+ } while(y & 0x80);
+ if(i >= buflen)
+ i += encodeUint(NULL, 0, x);
+ else
+ i += encodeUint(buf + i, buflen - i, x);
+ }
+ if(i < buflen)
+ buf[i] = '\0';
+ return i;
+}
+
+/*
+ * Convert an ASN.1 OID into its dotted or symbolic string representation.
+ * Return the dynamically allocated string, or NULL if an error occurs.
+ */
+
+static const char *OID2str(const char *beg, const char *end, bool symbolic)
+{
+ char *buf = NULL;
+ if(beg < end) {
+ size_t buflen = encodeOID(NULL, 0, beg, end);
+ if(buflen) {
+ buf = malloc(buflen + 1); /* one extra for the zero byte */
+ if(buf) {
+ encodeOID(buf, buflen, beg, end);
+ buf[buflen] = '\0';
+
+ if(symbolic) {
+ const struct Curl_OID *op = searchOID(buf);
+ if(op) {
+ free(buf);
+ buf = strdup(op->textoid);
+ }
+ }
+ }
+ }
+ }
+ return buf;
+}
+
+static const char *GTime2str(const char *beg, const char *end)
+{
+ const char *tzp;
+ const char *fracp;
+ char sec1, sec2;
+ size_t fracl;
+ size_t tzl;
+ const char *sep = "";
+
+ /* Convert an ASN.1 Generalized time to a printable string.
+ Return the dynamically allocated string, or NULL if an error occurs. */
+
+ for(fracp = beg; fracp < end && *fracp >= '0' && *fracp <= '9'; fracp++)
+ ;
+
+ /* Get seconds digits. */
+ sec1 = '0';
+ switch(fracp - beg - 12) {
+ case 0:
+ sec2 = '0';
+ break;
+ case 2:
+ sec1 = fracp[-2];
+ /* FALLTHROUGH */
+ case 1:
+ sec2 = fracp[-1];
+ break;
+ default:
+ return NULL;
+ }
+
+ /* Scan for timezone, measure fractional seconds. */
+ tzp = fracp;
+ fracl = 0;
+ if(fracp < end && (*fracp == '.' || *fracp == ',')) {
+ fracp++;
+ do
+ tzp++;
+ while(tzp < end && *tzp >= '0' && *tzp <= '9');
+ /* Strip leading zeroes in fractional seconds. */
+ for(fracl = tzp - fracp - 1; fracl && fracp[fracl - 1] == '0'; fracl--)
+ ;
+ }
+
+ /* Process timezone. */
+ if(tzp >= end)
+ ; /* Nothing to do. */
+ else if(*tzp == 'Z') {
+ tzp = " GMT";
+ end = tzp + 4;
+ }
+ else {
+ sep = " ";
+ tzp++;
+ }
+
+ tzl = end - tzp;
+ return curl_maprintf("%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s",
+ beg, beg + 4, beg + 6,
+ beg + 8, beg + 10, sec1, sec2,
+ fracl? ".": "", (int)fracl, fracp,
+ sep, (int)tzl, tzp);
+}
+
+/*
+ * Convert an ASN.1 UTC time to a printable string.
+ * Return the dynamically allocated string, or NULL if an error occurs.
+ */
+static const char *UTime2str(const char *beg, const char *end)
+{
+ const char *tzp;
+ size_t tzl;
+ const char *sec;
+
+ for(tzp = beg; tzp < end && *tzp >= '0' && *tzp <= '9'; tzp++)
+ ;
+ /* Get the seconds. */
+ sec = beg + 10;
+ switch(tzp - sec) {
+ case 0:
+ sec = "00";
+ case 2:
+ break;
+ default:
+ return NULL;
+ }
+
+ /* Process timezone. */
+ if(tzp >= end)
+ return NULL;
+ if(*tzp == 'Z') {
+ tzp = "GMT";
+ end = tzp + 3;
+ }
+ else
+ tzp++;
+
+ tzl = end - tzp;
+ return curl_maprintf("%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s",
+ 20 - (*beg >= '5'), beg, beg + 2, beg + 4,
+ beg + 6, beg + 8, sec,
+ (int)tzl, tzp);
+}
+
+/*
+ * Convert an ASN.1 element to a printable string.
+ * Return the dynamically allocated string, or NULL if an error occurs.
+ */
+static const char *ASN1tostr(struct Curl_asn1Element *elem, int type)
+{
+ if(elem->constructed)
+ return NULL; /* No conversion of structured elements. */
+
+ if(!type)
+ type = elem->tag; /* Type not forced: use element tag as type. */
+
+ switch(type) {
+ case CURL_ASN1_BOOLEAN:
+ return bool2str(elem->beg, elem->end);
+ case CURL_ASN1_INTEGER:
+ case CURL_ASN1_ENUMERATED:
+ return int2str(elem->beg, elem->end);
+ case CURL_ASN1_BIT_STRING:
+ return bit2str(elem->beg, elem->end);
+ case CURL_ASN1_OCTET_STRING:
+ return octet2str(elem->beg, elem->end);
+ case CURL_ASN1_NULL:
+ return strdup("");
+ case CURL_ASN1_OBJECT_IDENTIFIER:
+ return OID2str(elem->beg, elem->end, TRUE);
+ case CURL_ASN1_UTC_TIME:
+ return UTime2str(elem->beg, elem->end);
+ case CURL_ASN1_GENERALIZED_TIME:
+ return GTime2str(elem->beg, elem->end);
+ case CURL_ASN1_UTF8_STRING:
+ case CURL_ASN1_NUMERIC_STRING:
+ case CURL_ASN1_PRINTABLE_STRING:
+ case CURL_ASN1_TELETEX_STRING:
+ case CURL_ASN1_IA5_STRING:
+ case CURL_ASN1_VISIBLE_STRING:
+ case CURL_ASN1_UNIVERSAL_STRING:
+ case CURL_ASN1_BMP_STRING:
+ return string2str(type, elem->beg, elem->end);
+ }
+
+ return NULL; /* Unsupported. */
+}
+
+/*
+ * ASCII encode distinguished name at `dn' into the `buflen'-sized buffer at
+ * `buf'.
+ *
+ * Returns the total string length, even if larger than `buflen' or -1 on
+ * error.
+ */
+static ssize_t encodeDN(char *buf, size_t buflen, struct Curl_asn1Element *dn)
+{
+ struct Curl_asn1Element rdn;
+ struct Curl_asn1Element atv;
+ struct Curl_asn1Element oid;
+ struct Curl_asn1Element value;
+ size_t l = 0;
+ const char *p1;
+ const char *p2;
+ const char *p3;
+ const char *str;
+
+ for(p1 = dn->beg; p1 < dn->end;) {
+ p1 = getASN1Element(&rdn, p1, dn->end);
+ if(!p1)
+ return -1;
+ for(p2 = rdn.beg; p2 < rdn.end;) {
+ p2 = getASN1Element(&atv, p2, rdn.end);
+ if(!p2)
+ return -1;
+ p3 = getASN1Element(&oid, atv.beg, atv.end);
+ if(!p3)
+ return -1;
+ if(!getASN1Element(&value, p3, atv.end))
+ return -1;
+ str = ASN1tostr(&oid, 0);
+ if(!str)
+ return -1;
+
+ /* Encode delimiter.
+ If attribute has a short uppercase name, delimiter is ", ". */
+ if(l) {
+ for(p3 = str; ISUPPER(*p3); p3++)
+ ;
+ for(p3 = (*p3 || p3 - str > 2)? "/": ", "; *p3; p3++) {
+ if(l < buflen)
+ buf[l] = *p3;
+ l++;
+ }
+ }
+
+ /* Encode attribute name. */
+ for(p3 = str; *p3; p3++) {
+ if(l < buflen)
+ buf[l] = *p3;
+ l++;
+ }
+ free((char *) str);
+
+ /* Generate equal sign. */
+ if(l < buflen)
+ buf[l] = '=';
+ l++;
+
+ /* Generate value. */
+ str = ASN1tostr(&value, 0);
+ if(!str)
+ return -1;
+ for(p3 = str; *p3; p3++) {
+ if(l < buflen)
+ buf[l] = *p3;
+ l++;
+ }
+ free((char *) str);
+ }
+ }
+
+ return l;
+}
+
+#endif /* WANT_EXTRACT_CERTINFO */
+
+#ifdef WANT_PARSEX509
+/*
+ * ASN.1 parse an X509 certificate into structure subfields.
+ * Syntax is assumed to have already been checked by the SSL backend.
+ * See RFC 5280.
+ */
+int Curl_parseX509(struct Curl_X509certificate *cert,
+ const char *beg, const char *end)
+{
+ struct Curl_asn1Element elem;
+ struct Curl_asn1Element tbsCertificate;
+ const char *ccp;
+ static const char defaultVersion = 0; /* v1. */
+
+ cert->certificate.header = NULL;
+ cert->certificate.beg = beg;
+ cert->certificate.end = end;
+
+ /* Get the sequence content. */
+ if(!getASN1Element(&elem, beg, end))
+ return -1; /* Invalid bounds/size. */
+ beg = elem.beg;
+ end = elem.end;
+
+ /* Get tbsCertificate. */
+ beg = getASN1Element(&tbsCertificate, beg, end);
+ if(!beg)
+ return -1;
+ /* Skip the signatureAlgorithm. */
+ beg = getASN1Element(&cert->signatureAlgorithm, beg, end);
+ if(!beg)
+ return -1;
+ /* Get the signatureValue. */
+ if(!getASN1Element(&cert->signature, beg, end))
+ return -1;
+
+ /* Parse TBSCertificate. */
+ beg = tbsCertificate.beg;
+ end = tbsCertificate.end;
+ /* Get optional version, get serialNumber. */
+ cert->version.header = NULL;
+ cert->version.beg = &defaultVersion;
+ cert->version.end = &defaultVersion + sizeof(defaultVersion);
+ beg = getASN1Element(&elem, beg, end);
+ if(!beg)
+ return -1;
+ if(elem.tag == 0) {
+ if(!getASN1Element(&cert->version, elem.beg, elem.end))
+ return -1;
+ beg = getASN1Element(&elem, beg, end);
+ if(!beg)
+ return -1;
+ }
+ cert->serialNumber = elem;
+ /* Get signature algorithm. */
+ beg = getASN1Element(&cert->signatureAlgorithm, beg, end);
+ /* Get issuer. */
+ beg = getASN1Element(&cert->issuer, beg, end);
+ if(!beg)
+ return -1;
+ /* Get notBefore and notAfter. */
+ beg = getASN1Element(&elem, beg, end);
+ if(!beg)
+ return -1;
+ ccp = getASN1Element(&cert->notBefore, elem.beg, elem.end);
+ if(!ccp)
+ return -1;
+ if(!getASN1Element(&cert->notAfter, ccp, elem.end))
+ return -1;
+ /* Get subject. */
+ beg = getASN1Element(&cert->subject, beg, end);
+ if(!beg)
+ return -1;
+ /* Get subjectPublicKeyAlgorithm and subjectPublicKey. */
+ beg = getASN1Element(&cert->subjectPublicKeyInfo, beg, end);
+ if(!beg)
+ return -1;
+ ccp = getASN1Element(&cert->subjectPublicKeyAlgorithm,
+ cert->subjectPublicKeyInfo.beg,
+ cert->subjectPublicKeyInfo.end);
+ if(!ccp)
+ return -1;
+ if(!getASN1Element(&cert->subjectPublicKey, ccp,
+ cert->subjectPublicKeyInfo.end))
+ return -1;
+ /* Get optional issuerUiqueID, subjectUniqueID and extensions. */
+ cert->issuerUniqueID.tag = cert->subjectUniqueID.tag = 0;
+ cert->extensions.tag = elem.tag = 0;
+ cert->issuerUniqueID.header = cert->subjectUniqueID.header = NULL;
+ cert->issuerUniqueID.beg = cert->issuerUniqueID.end = "";
+ cert->subjectUniqueID.beg = cert->subjectUniqueID.end = "";
+ cert->extensions.header = NULL;
+ cert->extensions.beg = cert->extensions.end = "";
+ if(beg < end) {
+ beg = getASN1Element(&elem, beg, end);
+ if(!beg)
+ return -1;
+ }
+ if(elem.tag == 1) {
+ cert->issuerUniqueID = elem;
+ if(beg < end) {
+ beg = getASN1Element(&elem, beg, end);
+ if(!beg)
+ return -1;
+ }
+ }
+ if(elem.tag == 2) {
+ cert->subjectUniqueID = elem;
+ if(beg < end) {
+ beg = getASN1Element(&elem, beg, end);
+ if(!beg)
+ return -1;
+ }
+ }
+ if(elem.tag == 3)
+ if(!getASN1Element(&cert->extensions, elem.beg, elem.end))
+ return -1;
+ return 0;
+}
+
+#endif /* WANT_PARSEX509 */
+
+#ifdef WANT_EXTRACT_CERTINFO
+
+/*
+ * Copy at most 64-characters, terminate with a newline and returns the
+ * effective number of stored characters.
+ */
+static size_t copySubstring(char *to, const char *from)
+{
+ size_t i;
+ for(i = 0; i < 64; i++) {
+ to[i] = *from;
+ if(!*from++)
+ break;
+ }
+
+ to[i++] = '\n';
+ return i;
+}
+
+static const char *dumpAlgo(struct Curl_asn1Element *param,
+ const char *beg, const char *end)
+{
+ struct Curl_asn1Element oid;
+
+ /* Get algorithm parameters and return algorithm name. */
+
+ beg = getASN1Element(&oid, beg, end);
+ if(!beg)
+ return NULL;
+ param->header = NULL;
+ param->tag = 0;
+ param->beg = param->end = end;
+ if(beg < end)
+ if(!getASN1Element(param, beg, end))
+ return NULL;
+ return OID2str(oid.beg, oid.end, TRUE);
+}
+
+/* return 0 on success, 1 on error */
+static int do_pubkey_field(struct Curl_easy *data, int certnum,
+ const char *label, struct Curl_asn1Element *elem)
+{
+ const char *output;
+ CURLcode result = CURLE_OK;
+
+ /* Generate a certificate information record for the public key. */
+
+ output = ASN1tostr(elem, 0);
+ if(output) {
+ if(data->set.ssl.certinfo)
+ result = Curl_ssl_push_certinfo(data, certnum, label, output);
+ if(!certnum && !result)
+ infof(data, " %s: %s", label, output);
+ free((char *) output);
+ }
+ return result ? 1 : 0;
+}
+
+/* return 0 on success, 1 on error */
+static int do_pubkey(struct Curl_easy *data, int certnum,
+ const char *algo, struct Curl_asn1Element *param,
+ struct Curl_asn1Element *pubkey)
+{
+ struct Curl_asn1Element elem;
+ struct Curl_asn1Element pk;
+ const char *p;
+
+ /* Generate all information records for the public key. */
+
+ if(strcasecompare(algo, "ecPublicKey")) {
+ /*
+ * ECC public key is all the data, a value of type BIT STRING mapped to
+ * OCTET STRING and should not be parsed as an ASN.1 value.
+ */
+ const size_t len = ((pubkey->end - pubkey->beg - 2) * 4);
+ if(!certnum)
+ infof(data, " ECC Public Key (%lu bits)", len);
+ if(data->set.ssl.certinfo) {
+ char q[sizeof(len) * 8 / 3 + 1];
+ (void)msnprintf(q, sizeof(q), "%lu", len);
+ if(Curl_ssl_push_certinfo(data, certnum, "ECC Public Key", q))
+ return 1;
+ }
+ return do_pubkey_field(data, certnum, "ecPublicKey", pubkey);
+ }
+
+ /* Get the public key (single element). */
+ if(!getASN1Element(&pk, pubkey->beg + 1, pubkey->end))
+ return 1;
+
+ if(strcasecompare(algo, "rsaEncryption")) {
+ const char *q;
+ size_t len;
+
+ p = getASN1Element(&elem, pk.beg, pk.end);
+ if(!p)
+ return 1;
+
+ /* Compute key length. */
+ for(q = elem.beg; !*q && q < elem.end; q++)
+ ;
+ len = ((elem.end - q) * 8);
+ if(len) {
+ unsigned int i;
+ for(i = *(unsigned char *) q; !(i & 0x80); i <<= 1)
+ len--;
+ }
+ if(len > 32)
+ elem.beg = q; /* Strip leading zero bytes. */
+ if(!certnum)
+ infof(data, " RSA Public Key (%lu bits)", len);
+ if(data->set.ssl.certinfo) {
+ char r[sizeof(len) * 8 / 3 + 1];
+ msnprintf(r, sizeof(r), "%lu", len);
+ if(Curl_ssl_push_certinfo(data, certnum, "RSA Public Key", r))
+ return 1;
+ }
+ /* Generate coefficients. */
+ if(do_pubkey_field(data, certnum, "rsa(n)", &elem))
+ return 1;
+ if(!getASN1Element(&elem, p, pk.end))
+ return 1;
+ if(do_pubkey_field(data, certnum, "rsa(e)", &elem))
+ return 1;
+ }
+ else if(strcasecompare(algo, "dsa")) {
+ p = getASN1Element(&elem, param->beg, param->end);
+ if(p) {
+ if(do_pubkey_field(data, certnum, "dsa(p)", &elem))
+ return 1;
+ p = getASN1Element(&elem, p, param->end);
+ if(p) {
+ if(do_pubkey_field(data, certnum, "dsa(q)", &elem))
+ return 1;
+ if(getASN1Element(&elem, p, param->end)) {
+ if(do_pubkey_field(data, certnum, "dsa(g)", &elem))
+ return 1;
+ if(do_pubkey_field(data, certnum, "dsa(pub_key)", &pk))
+ return 1;
+ }
+ }
+ }
+ }
+ else if(strcasecompare(algo, "dhpublicnumber")) {
+ p = getASN1Element(&elem, param->beg, param->end);
+ if(p) {
+ if(do_pubkey_field(data, certnum, "dh(p)", &elem))
+ return 1;
+ if(getASN1Element(&elem, param->beg, param->end)) {
+ if(do_pubkey_field(data, certnum, "dh(g)", &elem))
+ return 1;
+ if(do_pubkey_field(data, certnum, "dh(pub_key)", &pk))
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * Convert an ASN.1 distinguished name into a printable string.
+ * Return the dynamically allocated string, or NULL if an error occurs.
+ */
+static const char *DNtostr(struct Curl_asn1Element *dn)
+{
+ char *buf = NULL;
+ ssize_t buflen = encodeDN(NULL, 0, dn);
+
+ if(buflen >= 0) {
+ buf = malloc(buflen + 1);
+ if(buf) {
+ if(encodeDN(buf, buflen + 1, dn) == -1) {
+ free(buf);
+ return NULL;
+ }
+ buf[buflen] = '\0';
+ }
+ }
+ return buf;
+}
+
+CURLcode Curl_extract_certinfo(struct Curl_easy *data,
+ int certnum,
+ const char *beg,
+ const char *end)
+{
+ struct Curl_X509certificate cert;
+ struct Curl_asn1Element param;
+ const char *ccp;
+ char *cp1;
+ size_t cl1;
+ char *cp2;
+ CURLcode result = CURLE_OK;
+ unsigned int version;
+ size_t i;
+ size_t j;
+
+ if(!data->set.ssl.certinfo)
+ if(certnum)
+ return CURLE_OK;
+
+ /* Prepare the certificate information for curl_easy_getinfo(). */
+
+ /* Extract the certificate ASN.1 elements. */
+ if(Curl_parseX509(&cert, beg, end))
+ return CURLE_PEER_FAILED_VERIFICATION;
+
+ /* Subject. */
+ ccp = DNtostr(&cert.subject);
+ if(!ccp)
+ return CURLE_OUT_OF_MEMORY;
+ if(data->set.ssl.certinfo) {
+ result = Curl_ssl_push_certinfo(data, certnum, "Subject", ccp);
+ if(result)
+ return result;
+ }
+ if(!certnum)
+ infof(data, "%2d Subject: %s", certnum, ccp);
+ free((char *) ccp);
+
+ /* Issuer. */
+ ccp = DNtostr(&cert.issuer);
+ if(!ccp)
+ return CURLE_OUT_OF_MEMORY;
+ if(data->set.ssl.certinfo) {
+ result = Curl_ssl_push_certinfo(data, certnum, "Issuer", ccp);
+ }
+ if(!certnum)
+ infof(data, " Issuer: %s", ccp);
+ free((char *) ccp);
+ if(result)
+ return result;
+
+ /* Version (always fits in less than 32 bits). */
+ version = 0;
+ for(ccp = cert.version.beg; ccp < cert.version.end; ccp++)
+ version = (version << 8) | *(const unsigned char *) ccp;
+ if(data->set.ssl.certinfo) {
+ ccp = curl_maprintf("%lx", version);
+ if(!ccp)
+ return CURLE_OUT_OF_MEMORY;
+ result = Curl_ssl_push_certinfo(data, certnum, "Version", ccp);
+ free((char *) ccp);
+ if(result)
+ return result;
+ }
+ if(!certnum)
+ infof(data, " Version: %lu (0x%lx)", version + 1, version);
+
+ /* Serial number. */
+ ccp = ASN1tostr(&cert.serialNumber, 0);
+ if(!ccp)
+ return CURLE_OUT_OF_MEMORY;
+ if(data->set.ssl.certinfo)
+ result = Curl_ssl_push_certinfo(data, certnum, "Serial Number", ccp);
+ if(!certnum)
+ infof(data, " Serial Number: %s", ccp);
+ free((char *) ccp);
+ if(result)
+ return result;
+
+ /* Signature algorithm .*/
+ ccp = dumpAlgo(¶m, cert.signatureAlgorithm.beg,
+ cert.signatureAlgorithm.end);
+ if(!ccp)
+ return CURLE_OUT_OF_MEMORY;
+ if(data->set.ssl.certinfo)
+ result = Curl_ssl_push_certinfo(data, certnum, "Signature Algorithm", ccp);
+ if(!certnum)
+ infof(data, " Signature Algorithm: %s", ccp);
+ free((char *) ccp);
+ if(result)
+ return result;
+
+ /* Start Date. */
+ ccp = ASN1tostr(&cert.notBefore, 0);
+ if(!ccp)
+ return CURLE_OUT_OF_MEMORY;
+ if(data->set.ssl.certinfo)
+ result = Curl_ssl_push_certinfo(data, certnum, "Start Date", ccp);
+ if(!certnum)
+ infof(data, " Start Date: %s", ccp);
+ free((char *) ccp);
+ if(result)
+ return result;
+
+ /* Expire Date. */
+ ccp = ASN1tostr(&cert.notAfter, 0);
+ if(!ccp)
+ return CURLE_OUT_OF_MEMORY;
+ if(data->set.ssl.certinfo)
+ result = Curl_ssl_push_certinfo(data, certnum, "Expire Date", ccp);
+ if(!certnum)
+ infof(data, " Expire Date: %s", ccp);
+ free((char *) ccp);
+ if(result)
+ return result;
+
+ /* Public Key Algorithm. */
+ ccp = dumpAlgo(¶m, cert.subjectPublicKeyAlgorithm.beg,
+ cert.subjectPublicKeyAlgorithm.end);
+ if(!ccp)
+ return CURLE_OUT_OF_MEMORY;
+ if(data->set.ssl.certinfo)
+ result = Curl_ssl_push_certinfo(data, certnum, "Public Key Algorithm",
+ ccp);
+ if(!result) {
+ int ret;
+ if(!certnum)
+ infof(data, " Public Key Algorithm: %s", ccp);
+ ret = do_pubkey(data, certnum, ccp, ¶m, &cert.subjectPublicKey);
+ if(ret)
+ result = CURLE_OUT_OF_MEMORY; /* the most likely error */
+ }
+ free((char *) ccp);
+ if(result)
+ return result;
+
+ /* Signature. */
+ ccp = ASN1tostr(&cert.signature, 0);
+ if(!ccp)
+ return CURLE_OUT_OF_MEMORY;
+ if(data->set.ssl.certinfo)
+ result = Curl_ssl_push_certinfo(data, certnum, "Signature", ccp);
+ if(!certnum)
+ infof(data, " Signature: %s", ccp);
+ free((char *) ccp);
+ if(result)
+ return result;
+
+ /* Generate PEM certificate. */
+ result = Curl_base64_encode(cert.certificate.beg,
+ cert.certificate.end - cert.certificate.beg,
+ &cp1, &cl1);
+ if(result)
+ return result;
+ /* Compute the number of characters in final certificate string. Format is:
+ -----BEGIN CERTIFICATE-----\n
+ <max 64 base64 characters>\n
+ .
+ .
+ .
+ -----END CERTIFICATE-----\n
+ */
+ i = 28 + cl1 + (cl1 + 64 - 1) / 64 + 26;
+ cp2 = malloc(i + 1);
+ if(!cp2) {
+ free(cp1);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ /* Build the certificate string. */
+ i = copySubstring(cp2, "-----BEGIN CERTIFICATE-----");
+ for(j = 0; j < cl1; j += 64)
+ i += copySubstring(cp2 + i, cp1 + j);
+ i += copySubstring(cp2 + i, "-----END CERTIFICATE-----");
+ cp2[i] = '\0';
+ free(cp1);
+ if(data->set.ssl.certinfo)
+ result = Curl_ssl_push_certinfo(data, certnum, "Cert", cp2);
+ if(!certnum)
+ infof(data, "%s", cp2);
+ free(cp2);
+ return result;
+}
+
+#endif /* WANT_EXTRACT_CERTINFO */
+
+#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL
+ * or USE_SECTRANSP */
+
+#ifdef WANT_VERIFYHOST
+
+static const char *checkOID(const char *beg, const char *end,
+ const char *oid)
+{
+ struct Curl_asn1Element e;
+ const char *ccp;
+ const char *p;
+ bool matched;
+
+ /* Check if first ASN.1 element at `beg' is the given OID.
+ Return a pointer in the source after the OID if found, else NULL. */
+
+ ccp = getASN1Element(&e, beg, end);
+ if(!ccp || e.tag != CURL_ASN1_OBJECT_IDENTIFIER)
+ return NULL;
+
+ p = OID2str(e.beg, e.end, FALSE);
+ if(!p)
+ return NULL;
+
+ matched = !strcmp(p, oid);
+ free((char *) p);
+ return matched? ccp: NULL;
+}
+
+CURLcode Curl_verifyhost(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *beg, const char *end)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct Curl_X509certificate cert;
+ struct Curl_asn1Element dn;
+ struct Curl_asn1Element elem;
+ struct Curl_asn1Element ext;
+ struct Curl_asn1Element name;
+ const char *p;
+ const char *q;
+ char *dnsname;
+ int matched = -1;
+ size_t addrlen = (size_t) -1;
+ ssize_t len;
+ size_t hostlen;
+
+#ifdef ENABLE_IPV6
+ struct in6_addr addr;
+#else
+ struct in_addr addr;
+#endif
+
+ /* Verify that connection server matches info in X509 certificate at
+ `beg'..`end'. */
+
+ if(!conn_config->verifyhost)
+ return CURLE_OK;
+
+ if(Curl_parseX509(&cert, beg, end))
+ return CURLE_PEER_FAILED_VERIFICATION;
+
+ hostlen = strlen(connssl->hostname);
+
+ /* Get the server IP address. */
+#ifdef ENABLE_IPV6
+ if(conn->bits.ipv6_ip && Curl_inet_pton(AF_INET6, connssl->hostname, &addr))
+ addrlen = sizeof(struct in6_addr);
+ else
+#endif
+ if(Curl_inet_pton(AF_INET, connssl->hostname, &addr))
+ addrlen = sizeof(struct in_addr);
+
+ /* Process extensions. */
+ for(p = cert.extensions.beg; p < cert.extensions.end && matched != 1;) {
+ p = getASN1Element(&ext, p, cert.extensions.end);
+ if(!p)
+ return CURLE_PEER_FAILED_VERIFICATION;
+
+ /* Check if extension is a subjectAlternativeName. */
+ ext.beg = checkOID(ext.beg, ext.end, sanOID);
+ if(ext.beg) {
+ ext.beg = getASN1Element(&elem, ext.beg, ext.end);
+ if(!ext.beg)
+ return CURLE_PEER_FAILED_VERIFICATION;
+ /* Skip critical if present. */
+ if(elem.tag == CURL_ASN1_BOOLEAN) {
+ ext.beg = getASN1Element(&elem, ext.beg, ext.end);
+ if(!ext.beg)
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ /* Parse the octet string contents: is a single sequence. */
+ if(!getASN1Element(&elem, elem.beg, elem.end))
+ return CURLE_PEER_FAILED_VERIFICATION;
+ /* Check all GeneralNames. */
+ for(q = elem.beg; matched != 1 && q < elem.end;) {
+ q = getASN1Element(&name, q, elem.end);
+ if(!q)
+ break;
+ switch(name.tag) {
+ case 2: /* DNS name. */
+ matched = 0;
+ len = utf8asn1str(&dnsname, CURL_ASN1_IA5_STRING,
+ name.beg, name.end);
+ if(len > 0) {
+ if(size_t)len == strlen(dnsname)
+ matched = Curl_cert_hostcheck(dnsname, (size_t)len,
+ connssl->hostname, hostlen);
+ free(dnsname);
+ }
+ break;
+
+ case 7: /* IP address. */
+ matched = (name.end - name.beg) == addrlen &&
+ !memcmp(&addr, name.beg, addrlen);
+ break;
+ }
+ }
+ }
+ }
+
+ switch(matched) {
+ case 1:
+ /* an alternative name matched the server hostname */
+ infof(data, " subjectAltName: %s matched", connssl->dispname);
+ return CURLE_OK;
+ case 0:
+ /* an alternative name field existed, but didn't match and then
+ we MUST fail */
+ infof(data, " subjectAltName does not match %s", connssl->dispname);
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+
+ /* Process subject. */
+ name.header = NULL;
+ name.beg = name.end = "";
+ q = cert.subject.beg;
+ /* we have to look to the last occurrence of a commonName in the
+ distinguished one to get the most significant one. */
+ while(q < cert.subject.end) {
+ q = getASN1Element(&dn, q, cert.subject.end);
+ if(!q)
+ break;
+ for(p = dn.beg; p < dn.end;) {
+ p = getASN1Element(&elem, p, dn.end);
+ if(!p)
+ return CURLE_PEER_FAILED_VERIFICATION;
+ /* We have a DN's AttributeTypeAndValue: check it in case it's a CN. */
+ elem.beg = checkOID(elem.beg, elem.end, cnOID);
+ if(elem.beg)
+ name = elem; /* Latch CN. */
+ }
+ }
+
+ /* Check the CN if found. */
+ if(!getASN1Element(&elem, name.beg, name.end))
+ failf(data, "SSL: unable to obtain common name from peer certificate");
+ else {
+ len = utf8asn1str(&dnsname, elem.tag, elem.beg, elem.end);
+ if(len < 0)
+ return CURLE_OUT_OF_MEMORY;
+ if(strlen(dnsname) != (size_t) len) /* Nul byte in string ? */
+ failf(data, "SSL: illegal cert name field");
+ else if(Curl_cert_hostcheck((const char *) dnsname,
+ len, connssl->hostname, hostlen)) {
+ infof(data, " common name: %s (matched)", dnsname);
+ free(dnsname);
+ return CURLE_OK;
+ }
+ else
+ failf(data, "SSL: certificate subject name '%s' does not match "
+ "target host name '%s'", dnsname, connssl->dispname);
+ free(dnsname);
+ }
+
+ return CURLE_PEER_FAILED_VERIFICATION;
+}
+
+#endif /* WANT_VERIFYHOST */
diff --git a/libs/libcurl/src/vtls/x509asn1.h b/libs/libcurl/src/vtls/x509asn1.h index a18fa11530..e582f72464 100644 --- a/libs/libcurl/src/vtls/x509asn1.h +++ b/libs/libcurl/src/vtls/x509asn1.h @@ -1,80 +1,81 @@ -#ifndef HEADER_CURL_X509ASN1_H -#define HEADER_CURL_X509ASN1_H - -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ - defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) - -#include "urldata.h" - -/* - * Types. - */ - -/* ASN.1 parsed element. */ -struct Curl_asn1Element { - const char *header; /* Pointer to header byte. */ - const char *beg; /* Pointer to element data. */ - const char *end; /* Pointer to 1st byte after element. */ - unsigned char class; /* ASN.1 element class. */ - unsigned char tag; /* ASN.1 element tag. */ - bool constructed; /* Element is constructed. */ -}; - -/* X509 certificate: RFC 5280. */ -struct Curl_X509certificate { - struct Curl_asn1Element certificate; - struct Curl_asn1Element version; - struct Curl_asn1Element serialNumber; - struct Curl_asn1Element signatureAlgorithm; - struct Curl_asn1Element signature; - struct Curl_asn1Element issuer; - struct Curl_asn1Element notBefore; - struct Curl_asn1Element notAfter; - struct Curl_asn1Element subject; - struct Curl_asn1Element subjectPublicKeyInfo; - struct Curl_asn1Element subjectPublicKeyAlgorithm; - struct Curl_asn1Element subjectPublicKey; - struct Curl_asn1Element issuerUniqueID; - struct Curl_asn1Element subjectUniqueID; - struct Curl_asn1Element extensions; -}; - -/* - * Prototypes. - */ - -int Curl_parseX509(struct Curl_X509certificate *cert, - const char *beg, const char *end); -CURLcode Curl_extract_certinfo(struct Curl_easy *data, int certnum, - const char *beg, const char *end); -CURLcode Curl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - const char *beg, const char *end); -#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL - * or USE_SECTRANSP */ -#endif /* HEADER_CURL_X509ASN1_H */ +#ifndef HEADER_CURL_X509ASN1_H
+#define HEADER_CURL_X509ASN1_H
+
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \
+ defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP)
+
+#include "cfilters.h"
+#include "urldata.h"
+
+/*
+ * Types.
+ */
+
+/* ASN.1 parsed element. */
+struct Curl_asn1Element {
+ const char *header; /* Pointer to header byte. */
+ const char *beg; /* Pointer to element data. */
+ const char *end; /* Pointer to 1st byte after element. */
+ unsigned char class; /* ASN.1 element class. */
+ unsigned char tag; /* ASN.1 element tag. */
+ bool constructed; /* Element is constructed. */
+};
+
+/* X509 certificate: RFC 5280. */
+struct Curl_X509certificate {
+ struct Curl_asn1Element certificate;
+ struct Curl_asn1Element version;
+ struct Curl_asn1Element serialNumber;
+ struct Curl_asn1Element signatureAlgorithm;
+ struct Curl_asn1Element signature;
+ struct Curl_asn1Element issuer;
+ struct Curl_asn1Element notBefore;
+ struct Curl_asn1Element notAfter;
+ struct Curl_asn1Element subject;
+ struct Curl_asn1Element subjectPublicKeyInfo;
+ struct Curl_asn1Element subjectPublicKeyAlgorithm;
+ struct Curl_asn1Element subjectPublicKey;
+ struct Curl_asn1Element issuerUniqueID;
+ struct Curl_asn1Element subjectUniqueID;
+ struct Curl_asn1Element extensions;
+};
+
+/*
+ * Prototypes.
+ */
+
+int Curl_parseX509(struct Curl_X509certificate *cert,
+ const char *beg, const char *end);
+CURLcode Curl_extract_certinfo(struct Curl_easy *data, int certnum,
+ const char *beg, const char *end);
+CURLcode Curl_verifyhost(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const char *beg, const char *end);
+#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL
+ * or USE_SECTRANSP */
+#endif /* HEADER_CURL_X509ASN1_H */
diff --git a/libs/libcurl/src/warnless.c b/libs/libcurl/src/warnless.c index b00d7a5a26..a38b996207 100644 --- a/libs/libcurl/src/warnless.c +++ b/libs/libcurl/src/warnless.c @@ -1,431 +1,431 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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(__INTEL_COMPILER) && defined(__unix__) - -#ifdef HAVE_NETINET_IN_H -# include <netinet/in.h> -#endif -#ifdef HAVE_ARPA_INET_H -# include <arpa/inet.h> -#endif - -#endif /* __INTEL_COMPILER && __unix__ */ - -#define BUILDING_WARNLESS_C 1 - -#include "warnless.h" - -#include <limits.h> - -#define CURL_MASK_UCHAR ((unsigned char)~0) -#define CURL_MASK_SCHAR (CURL_MASK_UCHAR >> 1) - -#define CURL_MASK_USHORT ((unsigned short)~0) -#define CURL_MASK_SSHORT (CURL_MASK_USHORT >> 1) - -#define CURL_MASK_UINT ((unsigned int)~0) -#define CURL_MASK_SINT (CURL_MASK_UINT >> 1) - -#define CURL_MASK_ULONG ((unsigned long)~0) -#define CURL_MASK_SLONG (CURL_MASK_ULONG >> 1) - -#define CURL_MASK_UCOFFT ((unsigned CURL_TYPEOF_CURL_OFF_T)~0) -#define CURL_MASK_SCOFFT (CURL_MASK_UCOFFT >> 1) - -#define CURL_MASK_USIZE_T ((size_t)~0) -#define CURL_MASK_SSIZE_T (CURL_MASK_USIZE_T >> 1) - -/* -** unsigned long to unsigned short -*/ - -unsigned short curlx_ultous(unsigned long ulnum) -{ -#ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ -#endif - - DEBUGASSERT(ulnum <= (unsigned long) CURL_MASK_USHORT); - return (unsigned short)(ulnum & (unsigned long) CURL_MASK_USHORT); - -#ifdef __INTEL_COMPILER -# pragma warning(pop) -#endif -} - -/* -** unsigned long to unsigned char -*/ - -unsigned char curlx_ultouc(unsigned long ulnum) -{ -#ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ -#endif - - DEBUGASSERT(ulnum <= (unsigned long) CURL_MASK_UCHAR); - return (unsigned char)(ulnum & (unsigned long) CURL_MASK_UCHAR); - -#ifdef __INTEL_COMPILER -# pragma warning(pop) -#endif -} - -/* -** unsigned size_t to signed curl_off_t -*/ - -curl_off_t curlx_uztoso(size_t uznum) -{ -#ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ -#elif defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable:4310) /* cast truncates constant value */ -#endif - - DEBUGASSERT(uznum <= (size_t) CURL_MASK_SCOFFT); - return (curl_off_t)(uznum & (size_t) CURL_MASK_SCOFFT); - -#if defined(__INTEL_COMPILER) || defined(_MSC_VER) -# pragma warning(pop) -#endif -} - -/* -** unsigned size_t to signed int -*/ - -int curlx_uztosi(size_t uznum) -{ -#ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ -#endif - - DEBUGASSERT(uznum <= (size_t) CURL_MASK_SINT); - return (int)(uznum & (size_t) CURL_MASK_SINT); - -#ifdef __INTEL_COMPILER -# pragma warning(pop) -#endif -} - -/* -** unsigned size_t to unsigned long -*/ - -unsigned long curlx_uztoul(size_t uznum) -{ -#ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ -#endif - -#if ULONG_MAX < SIZE_T_MAX - DEBUGASSERT(uznum <= (size_t) CURL_MASK_ULONG); -#endif - return (unsigned long)(uznum & (size_t) CURL_MASK_ULONG); - -#ifdef __INTEL_COMPILER -# pragma warning(pop) -#endif -} - -/* -** unsigned size_t to unsigned int -*/ - -unsigned int curlx_uztoui(size_t uznum) -{ -#ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ -#endif - -#if UINT_MAX < SIZE_T_MAX - DEBUGASSERT(uznum <= (size_t) CURL_MASK_UINT); -#endif - return (unsigned int)(uznum & (size_t) CURL_MASK_UINT); - -#ifdef __INTEL_COMPILER -# pragma warning(pop) -#endif -} - -/* -** signed long to signed int -*/ - -int curlx_sltosi(long slnum) -{ -#ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ -#endif - - DEBUGASSERT(slnum >= 0); -#if INT_MAX < LONG_MAX - DEBUGASSERT((unsigned long) slnum <= (unsigned long) CURL_MASK_SINT); -#endif - return (int)(slnum & (long) CURL_MASK_SINT); - -#ifdef __INTEL_COMPILER -# pragma warning(pop) -#endif -} - -/* -** signed long to unsigned int -*/ - -unsigned int curlx_sltoui(long slnum) -{ -#ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ -#endif - - DEBUGASSERT(slnum >= 0); -#if UINT_MAX < LONG_MAX - DEBUGASSERT((unsigned long) slnum <= (unsigned long) CURL_MASK_UINT); -#endif - return (unsigned int)(slnum & (long) CURL_MASK_UINT); - -#ifdef __INTEL_COMPILER -# pragma warning(pop) -#endif -} - -/* -** signed long to unsigned short -*/ - -unsigned short curlx_sltous(long slnum) -{ -#ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ -#endif - - DEBUGASSERT(slnum >= 0); - DEBUGASSERT((unsigned long) slnum <= (unsigned long) CURL_MASK_USHORT); - return (unsigned short)(slnum & (long) CURL_MASK_USHORT); - -#ifdef __INTEL_COMPILER -# pragma warning(pop) -#endif -} - -/* -** unsigned size_t to signed ssize_t -*/ - -ssize_t curlx_uztosz(size_t uznum) -{ -#ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ -#endif - - DEBUGASSERT(uznum <= (size_t) CURL_MASK_SSIZE_T); - return (ssize_t)(uznum & (size_t) CURL_MASK_SSIZE_T); - -#ifdef __INTEL_COMPILER -# pragma warning(pop) -#endif -} - -/* -** signed curl_off_t to unsigned size_t -*/ - -size_t curlx_sotouz(curl_off_t sonum) -{ -#ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ -#endif - - DEBUGASSERT(sonum >= 0); - return (size_t)(sonum & (curl_off_t) CURL_MASK_USIZE_T); - -#ifdef __INTEL_COMPILER -# pragma warning(pop) -#endif -} - -/* -** signed ssize_t to signed int -*/ - -int curlx_sztosi(ssize_t sznum) -{ -#ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ -#endif - - DEBUGASSERT(sznum >= 0); -#if INT_MAX < SSIZE_T_MAX - DEBUGASSERT((size_t) sznum <= (size_t) CURL_MASK_SINT); -#endif - return (int)(sznum & (ssize_t) CURL_MASK_SINT); - -#ifdef __INTEL_COMPILER -# pragma warning(pop) -#endif -} - -/* -** unsigned int to unsigned short -*/ - -unsigned short curlx_uitous(unsigned int uinum) -{ -#ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ -#endif - - DEBUGASSERT(uinum <= (unsigned int) CURL_MASK_USHORT); - return (unsigned short) (uinum & (unsigned int) CURL_MASK_USHORT); - -#ifdef __INTEL_COMPILER -# pragma warning(pop) -#endif -} - -/* -** signed int to unsigned size_t -*/ - -size_t curlx_sitouz(int sinum) -{ -#ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ -#endif - - DEBUGASSERT(sinum >= 0); - return (size_t) sinum; - -#ifdef __INTEL_COMPILER -# pragma warning(pop) -#endif -} - -#ifdef USE_WINSOCK - -/* -** curl_socket_t to signed int -*/ - -int curlx_sktosi(curl_socket_t s) -{ - return (int)((ssize_t) s); -} - -/* -** signed int to curl_socket_t -*/ - -curl_socket_t curlx_sitosk(int i) -{ - return (curl_socket_t)((ssize_t) i); -} - -#endif /* USE_WINSOCK */ - -#if defined(WIN32) - -ssize_t curlx_read(int fd, void *buf, size_t count) -{ - return (ssize_t)read(fd, buf, curlx_uztoui(count)); -} - -ssize_t curlx_write(int fd, const void *buf, size_t count) -{ - return (ssize_t)write(fd, buf, curlx_uztoui(count)); -} - -#endif /* WIN32 */ - -#if defined(__INTEL_COMPILER) && defined(__unix__) - -int curlx_FD_ISSET(int fd, fd_set *fdset) -{ - #pragma warning(push) - #pragma warning(disable:1469) /* clobber ignored */ - return FD_ISSET(fd, fdset); - #pragma warning(pop) -} - -void curlx_FD_SET(int fd, fd_set *fdset) -{ - #pragma warning(push) - #pragma warning(disable:1469) /* clobber ignored */ - FD_SET(fd, fdset); - #pragma warning(pop) -} - -void curlx_FD_ZERO(fd_set *fdset) -{ - #pragma warning(push) - #pragma warning(disable:593) /* variable was set but never used */ - FD_ZERO(fdset); - #pragma warning(pop) -} - -unsigned short curlx_htons(unsigned short usnum) -{ -#if (__INTEL_COMPILER == 910) && defined(__i386__) - return (unsigned short)(((usnum << 8) & 0xFF00) | ((usnum >> 8) & 0x00FF)); -#else - #pragma warning(push) - #pragma warning(disable:810) /* conversion may lose significant bits */ - return htons(usnum); - #pragma warning(pop) -#endif -} - -unsigned short curlx_ntohs(unsigned short usnum) -{ -#if (__INTEL_COMPILER == 910) && defined(__i386__) - return (unsigned short)(((usnum << 8) & 0xFF00) | ((usnum >> 8) & 0x00FF)); -#else - #pragma warning(push) - #pragma warning(disable:810) /* conversion may lose significant bits */ - return ntohs(usnum); - #pragma warning(pop) -#endif -} - -#endif /* __INTEL_COMPILER && __unix__ */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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(__INTEL_COMPILER) && defined(__unix__)
+
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+
+#endif /* __INTEL_COMPILER && __unix__ */
+
+#define BUILDING_WARNLESS_C 1
+
+#include "warnless.h"
+
+#include <limits.h>
+
+#define CURL_MASK_UCHAR ((unsigned char)~0)
+#define CURL_MASK_SCHAR (CURL_MASK_UCHAR >> 1)
+
+#define CURL_MASK_USHORT ((unsigned short)~0)
+#define CURL_MASK_SSHORT (CURL_MASK_USHORT >> 1)
+
+#define CURL_MASK_UINT ((unsigned int)~0)
+#define CURL_MASK_SINT (CURL_MASK_UINT >> 1)
+
+#define CURL_MASK_ULONG ((unsigned long)~0)
+#define CURL_MASK_SLONG (CURL_MASK_ULONG >> 1)
+
+#define CURL_MASK_UCOFFT ((unsigned CURL_TYPEOF_CURL_OFF_T)~0)
+#define CURL_MASK_SCOFFT (CURL_MASK_UCOFFT >> 1)
+
+#define CURL_MASK_USIZE_T ((size_t)~0)
+#define CURL_MASK_SSIZE_T (CURL_MASK_USIZE_T >> 1)
+
+/*
+** unsigned long to unsigned short
+*/
+
+unsigned short curlx_ultous(unsigned long ulnum)
+{
+#ifdef __INTEL_COMPILER
+# pragma warning(push)
+# pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+ DEBUGASSERT(ulnum <= (unsigned long) CURL_MASK_USHORT);
+ return (unsigned short)(ulnum & (unsigned long) CURL_MASK_USHORT);
+
+#ifdef __INTEL_COMPILER
+# pragma warning(pop)
+#endif
+}
+
+/*
+** unsigned long to unsigned char
+*/
+
+unsigned char curlx_ultouc(unsigned long ulnum)
+{
+#ifdef __INTEL_COMPILER
+# pragma warning(push)
+# pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+ DEBUGASSERT(ulnum <= (unsigned long) CURL_MASK_UCHAR);
+ return (unsigned char)(ulnum & (unsigned long) CURL_MASK_UCHAR);
+
+#ifdef __INTEL_COMPILER
+# pragma warning(pop)
+#endif
+}
+
+/*
+** unsigned size_t to signed curl_off_t
+*/
+
+curl_off_t curlx_uztoso(size_t uznum)
+{
+#ifdef __INTEL_COMPILER
+# pragma warning(push)
+# pragma warning(disable:810) /* conversion may lose significant bits */
+#elif defined(_MSC_VER)
+# pragma warning(push)
+# pragma warning(disable:4310) /* cast truncates constant value */
+#endif
+
+ DEBUGASSERT(uznum <= (size_t) CURL_MASK_SCOFFT);
+ return (curl_off_t)(uznum & (size_t) CURL_MASK_SCOFFT);
+
+#if defined(__INTEL_COMPILER) || defined(_MSC_VER)
+# pragma warning(pop)
+#endif
+}
+
+/*
+** unsigned size_t to signed int
+*/
+
+int curlx_uztosi(size_t uznum)
+{
+#ifdef __INTEL_COMPILER
+# pragma warning(push)
+# pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+ DEBUGASSERT(uznum <= (size_t) CURL_MASK_SINT);
+ return (int)(uznum & (size_t) CURL_MASK_SINT);
+
+#ifdef __INTEL_COMPILER
+# pragma warning(pop)
+#endif
+}
+
+/*
+** unsigned size_t to unsigned long
+*/
+
+unsigned long curlx_uztoul(size_t uznum)
+{
+#ifdef __INTEL_COMPILER
+# pragma warning(push)
+# pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+#if ULONG_MAX < SIZE_T_MAX
+ DEBUGASSERT(uznum <= (size_t) CURL_MASK_ULONG);
+#endif
+ return (unsigned long)(uznum & (size_t) CURL_MASK_ULONG);
+
+#ifdef __INTEL_COMPILER
+# pragma warning(pop)
+#endif
+}
+
+/*
+** unsigned size_t to unsigned int
+*/
+
+unsigned int curlx_uztoui(size_t uznum)
+{
+#ifdef __INTEL_COMPILER
+# pragma warning(push)
+# pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+#if UINT_MAX < SIZE_T_MAX
+ DEBUGASSERT(uznum <= (size_t) CURL_MASK_UINT);
+#endif
+ return (unsigned int)(uznum & (size_t) CURL_MASK_UINT);
+
+#ifdef __INTEL_COMPILER
+# pragma warning(pop)
+#endif
+}
+
+/*
+** signed long to signed int
+*/
+
+int curlx_sltosi(long slnum)
+{
+#ifdef __INTEL_COMPILER
+# pragma warning(push)
+# pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+ DEBUGASSERT(slnum >= 0);
+#if INT_MAX < LONG_MAX
+ DEBUGASSERT((unsigned long) slnum <= (unsigned long) CURL_MASK_SINT);
+#endif
+ return (int)(slnum & (long) CURL_MASK_SINT);
+
+#ifdef __INTEL_COMPILER
+# pragma warning(pop)
+#endif
+}
+
+/*
+** signed long to unsigned int
+*/
+
+unsigned int curlx_sltoui(long slnum)
+{
+#ifdef __INTEL_COMPILER
+# pragma warning(push)
+# pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+ DEBUGASSERT(slnum >= 0);
+#if UINT_MAX < LONG_MAX
+ DEBUGASSERT((unsigned long) slnum <= (unsigned long) CURL_MASK_UINT);
+#endif
+ return (unsigned int)(slnum & (long) CURL_MASK_UINT);
+
+#ifdef __INTEL_COMPILER
+# pragma warning(pop)
+#endif
+}
+
+/*
+** signed long to unsigned short
+*/
+
+unsigned short curlx_sltous(long slnum)
+{
+#ifdef __INTEL_COMPILER
+# pragma warning(push)
+# pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+ DEBUGASSERT(slnum >= 0);
+ DEBUGASSERT((unsigned long) slnum <= (unsigned long) CURL_MASK_USHORT);
+ return (unsigned short)(slnum & (long) CURL_MASK_USHORT);
+
+#ifdef __INTEL_COMPILER
+# pragma warning(pop)
+#endif
+}
+
+/*
+** unsigned size_t to signed ssize_t
+*/
+
+ssize_t curlx_uztosz(size_t uznum)
+{
+#ifdef __INTEL_COMPILER
+# pragma warning(push)
+# pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+ DEBUGASSERT(uznum <= (size_t) CURL_MASK_SSIZE_T);
+ return (ssize_t)(uznum & (size_t) CURL_MASK_SSIZE_T);
+
+#ifdef __INTEL_COMPILER
+# pragma warning(pop)
+#endif
+}
+
+/*
+** signed curl_off_t to unsigned size_t
+*/
+
+size_t curlx_sotouz(curl_off_t sonum)
+{
+#ifdef __INTEL_COMPILER
+# pragma warning(push)
+# pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+ DEBUGASSERT(sonum >= 0);
+ return (size_t)(sonum & (curl_off_t) CURL_MASK_USIZE_T);
+
+#ifdef __INTEL_COMPILER
+# pragma warning(pop)
+#endif
+}
+
+/*
+** signed ssize_t to signed int
+*/
+
+int curlx_sztosi(ssize_t sznum)
+{
+#ifdef __INTEL_COMPILER
+# pragma warning(push)
+# pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+ DEBUGASSERT(sznum >= 0);
+#if INT_MAX < SSIZE_T_MAX
+ DEBUGASSERT((size_t) sznum <= (size_t) CURL_MASK_SINT);
+#endif
+ return (int)(sznum & (ssize_t) CURL_MASK_SINT);
+
+#ifdef __INTEL_COMPILER
+# pragma warning(pop)
+#endif
+}
+
+/*
+** unsigned int to unsigned short
+*/
+
+unsigned short curlx_uitous(unsigned int uinum)
+{
+#ifdef __INTEL_COMPILER
+# pragma warning(push)
+# pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+ DEBUGASSERT(uinum <= (unsigned int) CURL_MASK_USHORT);
+ return (unsigned short) (uinum & (unsigned int) CURL_MASK_USHORT);
+
+#ifdef __INTEL_COMPILER
+# pragma warning(pop)
+#endif
+}
+
+/*
+** signed int to unsigned size_t
+*/
+
+size_t curlx_sitouz(int sinum)
+{
+#ifdef __INTEL_COMPILER
+# pragma warning(push)
+# pragma warning(disable:810) /* conversion may lose significant bits */
+#endif
+
+ DEBUGASSERT(sinum >= 0);
+ return (size_t) sinum;
+
+#ifdef __INTEL_COMPILER
+# pragma warning(pop)
+#endif
+}
+
+#ifdef USE_WINSOCK
+
+/*
+** curl_socket_t to signed int
+*/
+
+int curlx_sktosi(curl_socket_t s)
+{
+ return (int)((ssize_t) s);
+}
+
+/*
+** signed int to curl_socket_t
+*/
+
+curl_socket_t curlx_sitosk(int i)
+{
+ return (curl_socket_t)((ssize_t) i);
+}
+
+#endif /* USE_WINSOCK */
+
+#if defined(WIN32)
+
+ssize_t curlx_read(int fd, void *buf, size_t count)
+{
+ return (ssize_t)read(fd, buf, curlx_uztoui(count));
+}
+
+ssize_t curlx_write(int fd, const void *buf, size_t count)
+{
+ return (ssize_t)write(fd, buf, curlx_uztoui(count));
+}
+
+#endif /* WIN32 */
+
+#if defined(__INTEL_COMPILER) && defined(__unix__)
+
+int curlx_FD_ISSET(int fd, fd_set *fdset)
+{
+ #pragma warning(push)
+ #pragma warning(disable:1469) /* clobber ignored */
+ return FD_ISSET(fd, fdset);
+ #pragma warning(pop)
+}
+
+void curlx_FD_SET(int fd, fd_set *fdset)
+{
+ #pragma warning(push)
+ #pragma warning(disable:1469) /* clobber ignored */
+ FD_SET(fd, fdset);
+ #pragma warning(pop)
+}
+
+void curlx_FD_ZERO(fd_set *fdset)
+{
+ #pragma warning(push)
+ #pragma warning(disable:593) /* variable was set but never used */
+ FD_ZERO(fdset);
+ #pragma warning(pop)
+}
+
+unsigned short curlx_htons(unsigned short usnum)
+{
+#if (__INTEL_COMPILER == 910) && defined(__i386__)
+ return (unsigned short)(((usnum << 8) & 0xFF00) | ((usnum >> 8) & 0x00FF));
+#else
+ #pragma warning(push)
+ #pragma warning(disable:810) /* conversion may lose significant bits */
+ return htons(usnum);
+ #pragma warning(pop)
+#endif
+}
+
+unsigned short curlx_ntohs(unsigned short usnum)
+{
+#if (__INTEL_COMPILER == 910) && defined(__i386__)
+ return (unsigned short)(((usnum << 8) & 0xFF00) | ((usnum >> 8) & 0x00FF));
+#else
+ #pragma warning(push)
+ #pragma warning(disable:810) /* conversion may lose significant bits */
+ return ntohs(usnum);
+ #pragma warning(pop)
+#endif
+}
+
+#endif /* __INTEL_COMPILER && __unix__ */
diff --git a/libs/libcurl/src/warnless.h b/libs/libcurl/src/warnless.h index 4367099d92..492763a6b6 100644 --- a/libs/libcurl/src/warnless.h +++ b/libs/libcurl/src/warnless.h @@ -1,101 +1,101 @@ -#ifndef HEADER_CURL_WARNLESS_H -#define HEADER_CURL_WARNLESS_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_WINSOCK -#include <curl/curl.h> /* for curl_socket_t */ -#endif - -#define CURLX_FUNCTION_CAST(target_type, func) \ - (target_type)(void (*) (void))(func) - -unsigned short curlx_ultous(unsigned long ulnum); - -unsigned char curlx_ultouc(unsigned long ulnum); - -int curlx_uztosi(size_t uznum); - -curl_off_t curlx_uztoso(size_t uznum); - -unsigned long curlx_uztoul(size_t uznum); - -unsigned int curlx_uztoui(size_t uznum); - -int curlx_sltosi(long slnum); - -unsigned int curlx_sltoui(long slnum); - -unsigned short curlx_sltous(long slnum); - -ssize_t curlx_uztosz(size_t uznum); - -size_t curlx_sotouz(curl_off_t sonum); - -int curlx_sztosi(ssize_t sznum); - -unsigned short curlx_uitous(unsigned int uinum); - -size_t curlx_sitouz(int sinum); - -#ifdef USE_WINSOCK - -int curlx_sktosi(curl_socket_t s); - -curl_socket_t curlx_sitosk(int i); - -#endif /* USE_WINSOCK */ - -#if defined(WIN32) - -ssize_t curlx_read(int fd, void *buf, size_t count); - -ssize_t curlx_write(int fd, const void *buf, size_t count); - -#ifndef BUILDING_WARNLESS_C -# undef read -# define read(fd, buf, count) curlx_read(fd, buf, count) -# undef write -# define write(fd, buf, count) curlx_write(fd, buf, count) -#endif - -#endif /* WIN32 */ - -#if defined(__INTEL_COMPILER) && defined(__unix__) - -int curlx_FD_ISSET(int fd, fd_set *fdset); - -void curlx_FD_SET(int fd, fd_set *fdset); - -void curlx_FD_ZERO(fd_set *fdset); - -unsigned short curlx_htons(unsigned short usnum); - -unsigned short curlx_ntohs(unsigned short usnum); - -#endif /* __INTEL_COMPILER && __unix__ */ - -#endif /* HEADER_CURL_WARNLESS_H */ +#ifndef HEADER_CURL_WARNLESS_H
+#define HEADER_CURL_WARNLESS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_WINSOCK
+#include <curl/curl.h> /* for curl_socket_t */
+#endif
+
+#define CURLX_FUNCTION_CAST(target_type, func) \
+ (target_type)(void (*) (void))(func)
+
+unsigned short curlx_ultous(unsigned long ulnum);
+
+unsigned char curlx_ultouc(unsigned long ulnum);
+
+int curlx_uztosi(size_t uznum);
+
+curl_off_t curlx_uztoso(size_t uznum);
+
+unsigned long curlx_uztoul(size_t uznum);
+
+unsigned int curlx_uztoui(size_t uznum);
+
+int curlx_sltosi(long slnum);
+
+unsigned int curlx_sltoui(long slnum);
+
+unsigned short curlx_sltous(long slnum);
+
+ssize_t curlx_uztosz(size_t uznum);
+
+size_t curlx_sotouz(curl_off_t sonum);
+
+int curlx_sztosi(ssize_t sznum);
+
+unsigned short curlx_uitous(unsigned int uinum);
+
+size_t curlx_sitouz(int sinum);
+
+#ifdef USE_WINSOCK
+
+int curlx_sktosi(curl_socket_t s);
+
+curl_socket_t curlx_sitosk(int i);
+
+#endif /* USE_WINSOCK */
+
+#if defined(WIN32)
+
+ssize_t curlx_read(int fd, void *buf, size_t count);
+
+ssize_t curlx_write(int fd, const void *buf, size_t count);
+
+#ifndef BUILDING_WARNLESS_C
+# undef read
+# define read(fd, buf, count) curlx_read(fd, buf, count)
+# undef write
+# define write(fd, buf, count) curlx_write(fd, buf, count)
+#endif
+
+#endif /* WIN32 */
+
+#if defined(__INTEL_COMPILER) && defined(__unix__)
+
+int curlx_FD_ISSET(int fd, fd_set *fdset);
+
+void curlx_FD_SET(int fd, fd_set *fdset);
+
+void curlx_FD_ZERO(fd_set *fdset);
+
+unsigned short curlx_htons(unsigned short usnum);
+
+unsigned short curlx_ntohs(unsigned short usnum);
+
+#endif /* __INTEL_COMPILER && __unix__ */
+
+#endif /* HEADER_CURL_WARNLESS_H */
diff --git a/libs/libcurl/src/wildcard.c b/libs/libcurl/src/wildcard.c index a3e24b6784..4818777e39 100644 --- a/libs/libcurl/src/wildcard.c +++ b/libs/libcurl/src/wildcard.c @@ -1,75 +1,75 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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 "wildcard.h" -#include "llist.h" -#include "fileinfo.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -static void fileinfo_dtor(void *user, void *element) -{ - (void)user; - Curl_fileinfo_cleanup(element); -} - -CURLcode Curl_wildcard_init(struct WildcardData *wc) -{ - Curl_llist_init(&wc->filelist, fileinfo_dtor); - wc->state = CURLWC_INIT; - - return CURLE_OK; -} - -void Curl_wildcard_dtor(struct WildcardData *wc) -{ - if(!wc) - return; - - if(wc->dtor) { - wc->dtor(wc->protdata); - wc->dtor = ZERO_NULL; - wc->protdata = NULL; - } - DEBUGASSERT(wc->protdata == NULL); - - Curl_llist_destroy(&wc->filelist, NULL); - - - free(wc->path); - wc->path = NULL; - free(wc->pattern); - wc->pattern = NULL; - - wc->customptr = NULL; - wc->state = CURLWC_INIT; -} - -#endif /* if disabled */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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 "wildcard.h"
+#include "llist.h"
+#include "fileinfo.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+static void fileinfo_dtor(void *user, void *element)
+{
+ (void)user;
+ Curl_fileinfo_cleanup(element);
+}
+
+CURLcode Curl_wildcard_init(struct WildcardData *wc)
+{
+ Curl_llist_init(&wc->filelist, fileinfo_dtor);
+ wc->state = CURLWC_INIT;
+
+ return CURLE_OK;
+}
+
+void Curl_wildcard_dtor(struct WildcardData *wc)
+{
+ if(!wc)
+ return;
+
+ if(wc->dtor) {
+ wc->dtor(wc->protdata);
+ wc->dtor = ZERO_NULL;
+ wc->protdata = NULL;
+ }
+ DEBUGASSERT(wc->protdata == NULL);
+
+ Curl_llist_destroy(&wc->filelist, NULL);
+
+
+ free(wc->path);
+ wc->path = NULL;
+ free(wc->pattern);
+ wc->pattern = NULL;
+
+ wc->customptr = NULL;
+ wc->state = CURLWC_INIT;
+}
+
+#endif /* if disabled */
diff --git a/libs/libcurl/src/wildcard.h b/libs/libcurl/src/wildcard.h index 21e933b9a4..d97f1b5743 100644 --- a/libs/libcurl/src/wildcard.h +++ b/libs/libcurl/src/wildcard.h @@ -1,70 +1,70 @@ -#ifndef HEADER_CURL_WILDCARD_H -#define HEADER_CURL_WILDCARD_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2010 - 2022, 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 "llist.h" - -/* list of wildcard process states */ -typedef enum { - CURLWC_CLEAR = 0, - CURLWC_INIT = 1, - CURLWC_MATCHING, /* library is trying to get list of addresses for - downloading */ - CURLWC_DOWNLOADING, - CURLWC_CLEAN, /* deallocate resources and reset settings */ - CURLWC_SKIP, /* skip over concrete file */ - CURLWC_ERROR, /* error cases */ - CURLWC_DONE /* if is wildcard->state == CURLWC_DONE wildcard loop - will end */ -} wildcard_states; - -typedef void (*wildcard_dtor)(void *ptr); - -/* struct keeping information about wildcard download process */ -struct WildcardData { - wildcard_states state; - char *path; /* path to the directory, where we trying wildcard-match */ - char *pattern; /* wildcard pattern */ - struct Curl_llist filelist; /* llist with struct Curl_fileinfo */ - void *protdata; /* pointer to protocol specific temporary data */ - wildcard_dtor dtor; - void *customptr; /* for CURLOPT_CHUNK_DATA pointer */ -}; - -CURLcode Curl_wildcard_init(struct WildcardData *wc); -void Curl_wildcard_dtor(struct WildcardData *wc); - -struct Curl_easy; - -#else -/* FTP is disabled */ -#define Curl_wildcard_dtor(x) -#endif - -#endif /* HEADER_CURL_WILDCARD_H */ +#ifndef HEADER_CURL_WILDCARD_H
+#define HEADER_CURL_WILDCARD_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2010 - 2022, 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 "llist.h"
+
+/* list of wildcard process states */
+typedef enum {
+ CURLWC_CLEAR = 0,
+ CURLWC_INIT = 1,
+ CURLWC_MATCHING, /* library is trying to get list of addresses for
+ downloading */
+ CURLWC_DOWNLOADING,
+ CURLWC_CLEAN, /* deallocate resources and reset settings */
+ CURLWC_SKIP, /* skip over concrete file */
+ CURLWC_ERROR, /* error cases */
+ CURLWC_DONE /* if is wildcard->state == CURLWC_DONE wildcard loop
+ will end */
+} wildcard_states;
+
+typedef void (*wildcard_dtor)(void *ptr);
+
+/* struct keeping information about wildcard download process */
+struct WildcardData {
+ wildcard_states state;
+ char *path; /* path to the directory, where we trying wildcard-match */
+ char *pattern; /* wildcard pattern */
+ struct Curl_llist filelist; /* llist with struct Curl_fileinfo */
+ void *protdata; /* pointer to protocol specific temporary data */
+ wildcard_dtor dtor;
+ void *customptr; /* for CURLOPT_CHUNK_DATA pointer */
+};
+
+CURLcode Curl_wildcard_init(struct WildcardData *wc);
+void Curl_wildcard_dtor(struct WildcardData *wc);
+
+struct Curl_easy;
+
+#else
+/* FTP is disabled */
+#define Curl_wildcard_dtor(x)
+#endif
+
+#endif /* HEADER_CURL_WILDCARD_H */
diff --git a/libs/libcurl/src/ws.c b/libs/libcurl/src/ws.c index a673446625..bc18e61767 100644 --- a/libs/libcurl/src/ws.c +++ b/libs/libcurl/src/ws.c @@ -1,757 +1,744 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_WEBSOCKETS - -#include "urldata.h" -#include "dynbuf.h" -#include "rand.h" -#include "curl_base64.h" -#include "sendf.h" -#include "multiif.h" -#include "ws.h" -#include "easyif.h" -#include "transfer.h" -#include "nonblock.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -struct wsfield { - const char *name; - const char *val; -}; - -CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req) -{ - unsigned int i; - CURLcode result = CURLE_OK; - unsigned char rand[16]; - char *randstr; - size_t randlen; - char keyval[40]; - struct SingleRequest *k = &data->req; - struct wsfield heads[]= { - { - /* The request MUST contain an |Upgrade| header field whose value - MUST include the "websocket" keyword. */ - "Upgrade:", "websocket" - }, - { - /* The request MUST contain a |Connection| header field whose value - MUST include the "Upgrade" token. */ - "Connection:", "Upgrade", - }, - { - /* The request MUST include a header field with the name - |Sec-WebSocket-Version|. The value of this header field MUST be - 13. */ - "Sec-WebSocket-Version:", "13", - }, - { - /* The request MUST include a header field with the name - |Sec-WebSocket-Key|. The value of this header field MUST be a nonce - consisting of a randomly selected 16-byte value that has been - base64-encoded (see Section 4 of [RFC4648]). The nonce MUST be - selected randomly for each connection. */ - "Sec-WebSocket-Key:", NULL, - } - }; - heads[3].val = &keyval[0]; - - /* 16 bytes random */ - result = Curl_rand(data, (unsigned char *)rand, sizeof(rand)); - if(result) - return result; - result = Curl_base64_encode((char *)rand, sizeof(rand), &randstr, &randlen); - if(result) - return result; - DEBUGASSERT(randlen < sizeof(keyval)); - if(randlen >= sizeof(keyval)) - return CURLE_FAILED_INIT; - strcpy(keyval, randstr); - free(randstr); - for(i = 0; !result && (i < sizeof(heads)/sizeof(heads[0])); i++) { - if(!Curl_checkheaders(data, STRCONST(heads[i].name))) { -#ifdef USE_HYPER - char field[128]; - msnprintf(field, sizeof(field), "%s %s", heads[i].name, - heads[i].val); - result = Curl_hyper_header(data, req, field); -#else - (void)data; - result = Curl_dyn_addf(req, "%s %s\r\n", heads[i].name, - heads[i].val); -#endif - } - } - k->upgr101 = UPGR101_WS; - Curl_dyn_init(&data->req.p.http->ws.buf, MAX_WS_SIZE * 2); - return result; -} - -CURLcode Curl_ws_accept(struct Curl_easy *data) -{ - struct SingleRequest *k = &data->req; - struct HTTP *ws = data->req.p.http; - struct connectdata *conn = data->conn; - struct websocket *wsp = &data->req.p.http->ws; - CURLcode result; - - /* Verify the Sec-WebSocket-Accept response. - - The sent value is the base64 encoded version of a SHA-1 hash done on the - |Sec-WebSocket-Key| header field concatenated with - the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11". - */ - - /* If the response includes a |Sec-WebSocket-Extensions| header field and - this header field indicates the use of an extension that was not present - in the client's handshake (the server has indicated an extension not - requested by the client), the client MUST Fail the WebSocket Connection. - */ - - /* If the response includes a |Sec-WebSocket-Protocol| header field - and this header field indicates the use of a subprotocol that was - not present in the client's handshake (the server has indicated a - subprotocol not requested by the client), the client MUST Fail - the WebSocket Connection. */ - - /* 4 bytes random */ - result = Curl_rand(data, (unsigned char *)&ws->ws.mask, sizeof(ws->ws.mask)); - if(result) - return result; - - infof(data, "Received 101, switch to WebSocket; mask %02x%02x%02x%02x", - ws->ws.mask[0], ws->ws.mask[1], ws->ws.mask[2], ws->ws.mask[3]); - k->upgr101 = UPGR101_RECEIVED; - - if(data->set.connect_only) - /* switch off non-blocking sockets */ - (void)curlx_nonblock(conn->sock[FIRSTSOCKET], FALSE); - - wsp->oleft = 0; - return result; -} - -#define WSBIT_FIN 0x80 -#define WSBIT_OPCODE_CONT 0 -#define WSBIT_OPCODE_TEXT (1) -#define WSBIT_OPCODE_BIN (2) -#define WSBIT_OPCODE_CLOSE (8) -#define WSBIT_OPCODE_PING (9) -#define WSBIT_OPCODE_PONG (0xa) -#define WSBIT_OPCODE_MASK (0xf) - -#define WSBIT_MASK 0x80 - -/* remove the spent bytes from the beginning of the buffer as that part has - now been delivered to the application */ -static void ws_decode_clear(struct Curl_easy *data) -{ - struct websocket *wsp = &data->req.p.http->ws; - size_t spent = wsp->usedbuf; - size_t len = Curl_dyn_len(&wsp->buf); - size_t keep = len - spent; - DEBUGASSERT(len >= spent); - Curl_dyn_tail(&wsp->buf, keep); -} - -/* ws_decode() decodes a binary frame into structured WebSocket data, - - wpkt - the incoming raw data. If NULL, work on the already buffered data. - ilen - the size of the provided data, perhaps too little, perhaps too much - out - stored pointed to extracted data - olen - stored length of the extracted data - oleft - number of unread bytes pending to that belongs to this frame - more - if there is more data in there - flags - stored bitmask about the frame - - Returns CURLE_AGAIN if there is only a partial frame in the buffer. Then it - stores the first part in the ->extra buffer to be used in the next call - when more data is provided. -*/ - -static CURLcode ws_decode(struct Curl_easy *data, - unsigned char *wpkt, size_t ilen, - unsigned char **out, size_t *olen, - curl_off_t *oleft, - bool *more, - unsigned int *flags) -{ - bool fin; - unsigned char opcode; - curl_off_t total; - size_t dataindex = 2; - curl_off_t plen; /* size of data in the buffer */ - curl_off_t payloadsize; - struct websocket *wsp = &data->req.p.http->ws; - unsigned char *p; - CURLcode result; - - *olen = 0; - - /* add the incoming bytes, if any */ - if(wpkt) { - result = Curl_dyn_addn(&wsp->buf, wpkt, ilen); - if(result) - return result; - } - - plen = Curl_dyn_len(&wsp->buf); - if(plen < 2) { - /* the smallest possible frame is two bytes */ - infof(data, "WS: plen == %u, EAGAIN", (int)plen); - return CURLE_AGAIN; - } - - p = Curl_dyn_uptr(&wsp->buf); - - fin = p[0] & WSBIT_FIN; - opcode = p[0] & WSBIT_OPCODE_MASK; - infof(data, "WS:%d received FIN bit %u", __LINE__, (int)fin); - *flags = 0; - switch(opcode) { - case WSBIT_OPCODE_CONT: - if(!fin) - *flags |= CURLWS_CONT; - infof(data, "WS: received OPCODE CONT"); - break; - case WSBIT_OPCODE_TEXT: - infof(data, "WS: received OPCODE TEXT"); - *flags |= CURLWS_TEXT; - break; - case WSBIT_OPCODE_BIN: - infof(data, "WS: received OPCODE BINARY"); - *flags |= CURLWS_BINARY; - break; - case WSBIT_OPCODE_CLOSE: - infof(data, "WS: received OPCODE CLOSE"); - *flags |= CURLWS_CLOSE; - break; - case WSBIT_OPCODE_PING: - infof(data, "WS: received OPCODE PING"); - *flags |= CURLWS_PING; - break; - case WSBIT_OPCODE_PONG: - infof(data, "WS: received OPCODE PONG"); - *flags |= CURLWS_PONG; - break; - } - - if(p[1] & WSBIT_MASK) { - /* A client MUST close a connection if it detects a masked frame. */ - failf(data, "WS: masked input frame"); - return CURLE_RECV_ERROR; - } - payloadsize = p[1]; - if(payloadsize == 126) { - if(plen < 4) { - infof(data, "WS:%d plen == %u, EAGAIN", __LINE__, (int)plen); - return CURLE_AGAIN; /* not enough data available */ - } - payloadsize = (p[2] << 8) | p[3]; - dataindex += 2; - } - else if(payloadsize == 127) { - /* 64 bit payload size */ - if(plen < 10) - return CURLE_AGAIN; - if(p[2] & 80) { - failf(data, "WS: too large frame"); - return CURLE_RECV_ERROR; - } - dataindex += 8; - payloadsize = ((curl_off_t)p[2] << 56) | - (curl_off_t)p[3] << 48 | - (curl_off_t)p[4] << 40 | - (curl_off_t)p[5] << 32 | - (curl_off_t)p[6] << 24 | - (curl_off_t)p[7] << 16 | - (curl_off_t)p[8] << 8 | - p[9]; - } - - total = dataindex + payloadsize; - if(total > plen) { - /* deliver a partial frame */ - *oleft = total - dataindex; - payloadsize = total - dataindex; - } - else { - *oleft = 0; - if(plen > total) - /* there is another fragment after */ - *more = TRUE; - } - - /* point to the payload */ - *out = &p[dataindex]; - - /* return the payload length */ - *olen = payloadsize; - - /* number of bytes "used" from the buffer */ - wsp->usedbuf = dataindex + payloadsize; - infof(data, "WS: received %zu bytes payload (%zu left)", - payloadsize, *oleft); - return CURLE_OK; -} - -/* Curl_ws_writecb() is the write callback for websocket traffic. The - websocket data is provided to this raw, in chunks. This function should - handle/decode the data and call the "real" underlying callback accordingly. -*/ -size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */, - size_t nitems, void *userp) -{ - struct HTTP *ws = (struct HTTP *)userp; - struct Curl_easy *data = ws->ws.data; - void *writebody_ptr = data->set.out; - if(data->set.ws_raw_mode) - return data->set.fwrite_func(buffer, size, nitems, writebody_ptr); - else if(nitems) { - unsigned char *frame = NULL; - size_t flen = 0; - size_t wrote = 0; - CURLcode result; - bool more; /* there's is more to parse in the buffer */ - curl_off_t oleft; - - decode: - more = FALSE; - oleft = ws->ws.frame.bytesleft; - if(!oleft) { - unsigned int recvflags; - result = ws_decode(data, (unsigned char *)buffer, nitems, - &frame, &flen, &oleft, &more, &recvflags); - if(result == CURLE_AGAIN) - /* insufficient amount of data, keep it for later */ - return nitems; - else if(result) { - infof(data, "WS: decode error %d", (int)result); - return nitems - 1; - } - /* Store details about the frame to be reachable with curl_ws_meta() - from within the write callback */ - ws->ws.frame.age = 0; - ws->ws.frame.offset = 0; - ws->ws.frame.flags = recvflags; - ws->ws.frame.bytesleft = oleft; - } - else { - if(nitems > (size_t)ws->ws.frame.bytesleft) { - nitems = ws->ws.frame.bytesleft; - more = TRUE; - } - else - more = FALSE; - ws->ws.frame.offset += nitems; - ws->ws.frame.bytesleft -= nitems; - frame = (unsigned char *)buffer; - flen = nitems; - } - if((ws->ws.frame.flags & CURLWS_PING) && !oleft) { - /* auto-respond to PINGs, only works for single-frame payloads atm */ - size_t bytes; - infof(data, "WS: auto-respond to PING with a PONG"); - DEBUGASSERT(frame); - /* send back the exact same content as a PONG */ - result = curl_ws_send(data, frame, flen, &bytes, 0, CURLWS_PONG); - if(result) - return result; - } - else { - /* deliver the decoded frame to the user callback */ - Curl_set_in_callback(data, true); - wrote = data->set.fwrite_func((char *)frame, 1, flen, writebody_ptr); - Curl_set_in_callback(data, false); - if(wrote != flen) - return 0; - } - if(oleft) - ws->ws.frame.offset += flen; - /* the websocket frame has been delivered */ - ws_decode_clear(data); - if(more) { - /* there's more websocket data to deal with in the buffer */ - buffer = NULL; /* the buffer as been drained already */ - goto decode; - } - } - return nitems; -} - - -CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, - size_t buflen, size_t *nread, - struct curl_ws_frame **metap) -{ - size_t bytes; - CURLcode result; - struct websocket *wsp = &data->req.p.http->ws; - - *nread = 0; - *metap = NULL; - /* get a download buffer */ - result = Curl_preconnect(data); - if(result) - return result; - - do { - bool drain = FALSE; /* if there is pending buffered data to drain */ - char *inbuf = data->state.buffer; - bytes = wsp->stillbuffer; - if(!bytes) { - result = curl_easy_recv(data, data->state.buffer, - data->set.buffer_size, &bytes); - if(result) - return result; - } - else { - /* the pending bytes can be found here */ - inbuf = wsp->stillb; - drain = TRUE; - } - if(bytes) { - unsigned char *out; - size_t olen; - bool more; - unsigned int recvflags; - curl_off_t oleft = wsp->frame.bytesleft; - - infof(data, "WS: got %u websocket bytes to decode", (int)bytes); - if(!oleft && !drain) { - result = ws_decode(data, (unsigned char *)inbuf, bytes, - &out, &olen, &oleft, &more, &recvflags); - if(result == CURLE_AGAIN) - /* a packet fragment only */ - break; - else if(result) - return result; - wsp->frame.offset = 0; - wsp->frame.bytesleft = oleft; - wsp->frame.flags = recvflags; - } - else { - olen = oleft; - out = (unsigned char *)wsp->stillb; - recvflags = wsp->frame.flags; - if((curl_off_t)buflen < oleft) - /* there is still data left after this */ - wsp->frame.bytesleft -= buflen; - else - wsp->frame.bytesleft = 0; - } - - /* auto-respond to PINGs */ - if((recvflags & CURLWS_PING) && !oleft) { - infof(data, "WS: auto-respond to PING with a PONG"); - /* send back the exact same content as a PONG */ - result = curl_ws_send(data, out, olen, &bytes, 0, CURLWS_PONG); - if(result) - return result; - } - else { - if(olen < buflen) { - /* copy the payload to the user buffer */ - memcpy(buffer, out, olen); - *nread = olen; - if(!oleft) - /* websocket frame has been delivered */ - ws_decode_clear(data); - } - else { - /* copy a partial payload */ - memcpy(buffer, out, buflen); - *nread = buflen; - /* remember what is left and where */ - wsp->stillbuffer = olen - buflen; - wsp->stillb = (char *)buffer + buflen; - } - wsp->frame.offset += *nread; - } - } - else - *nread = bytes; - break; - } while(1); - *metap = &wsp->frame; - return CURLE_OK; -} - -static void ws_xor(struct Curl_easy *data, - const unsigned char *source, - unsigned char *dest, - size_t len) -{ - struct websocket *wsp = &data->req.p.http->ws; - size_t i; - /* append payload after the mask, XOR appropriately */ - for(i = 0; i < len; i++) { - dest[i] = source[i] ^ wsp->mask[wsp->xori]; - wsp->xori++; - wsp->xori &= 3; - } -} - -/*** - RFC 6455 Section 5.2 - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-------+-+-------------+-------------------------------+ - |F|R|R|R| opcode|M| Payload len | Extended payload length | - |I|S|S|S| (4) |A| (7) | (16/64) | - |N|V|V|V| |S| | (if payload len==126/127) | - | |1|2|3| |K| | | - +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + - | Extended payload length continued, if payload len == 127 | - + - - - - - - - - - - - - - - - +-------------------------------+ - | |Masking-key, if MASK set to 1 | - +-------------------------------+-------------------------------+ - | Masking-key (continued) | Payload Data | - +-------------------------------- - - - - - - - - - - - - - - - + - : Payload Data continued ... : - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - | Payload Data continued ... | - +---------------------------------------------------------------+ -*/ - -static size_t ws_packethead(struct Curl_easy *data, - size_t len, unsigned int flags) -{ - struct HTTP *ws = data->req.p.http; - unsigned char *out = (unsigned char *)data->state.ulbuf; - unsigned char firstbyte = 0; - int outi; - unsigned char opcode; - if(flags & CURLWS_TEXT) { - opcode = WSBIT_OPCODE_TEXT; - infof(data, "WS: send OPCODE TEXT"); - } - else if(flags & CURLWS_CLOSE) { - opcode = WSBIT_OPCODE_CLOSE; - infof(data, "WS: send OPCODE CLOSE"); - } - else if(flags & CURLWS_PING) { - opcode = WSBIT_OPCODE_PING; - infof(data, "WS: send OPCODE PING"); - } - else if(flags & CURLWS_PONG) { - opcode = WSBIT_OPCODE_PONG; - infof(data, "WS: send OPCODE PONG"); - } - else { - opcode = WSBIT_OPCODE_BIN; - infof(data, "WS: send OPCODE BINARY"); - } - - if(!(flags & CURLWS_CONT)) { - /* if not marked as continuing, assume this is the final fragment */ - firstbyte |= WSBIT_FIN | opcode; - ws->ws.contfragment = FALSE; - } - else if(ws->ws.contfragment) { - /* the previous fragment was not a final one and this isn't either, keep a - CONT opcode and no FIN bit */ - firstbyte |= WSBIT_OPCODE_CONT; - } - else { - ws->ws.contfragment = TRUE; - } - out[0] = firstbyte; - if(len > 65535) { - out[1] = 127 | WSBIT_MASK; - out[2] = (len >> 8) & 0xff; - out[3] = len & 0xff; - outi = 10; - } - else if(len > 126) { - out[1] = 126 | WSBIT_MASK; - out[2] = (len >> 8) & 0xff; - out[3] = len & 0xff; - outi = 4; - } - else { - out[1] = (unsigned char)len | WSBIT_MASK; - outi = 2; - } - - infof(data, "WS: send FIN bit %u (byte %02x)", - firstbyte & WSBIT_FIN ? 1 : 0, - firstbyte); - infof(data, "WS: send payload len %u", (int)len); - - /* 4 bytes mask */ - memcpy(&out[outi], &ws->ws.mask, 4); - - if(data->set.upload_buffer_size < (len + 10)) - return 0; - - /* pass over the mask */ - outi += 4; - - ws->ws.xori = 0; - /* return packet size */ - return outi; -} - -CURL_EXTERN CURLcode curl_ws_send(struct Curl_easy *data, const void *buffer, - size_t buflen, size_t *sent, - curl_off_t totalsize, - unsigned int sendflags) -{ - CURLcode result; - size_t headlen; - char *out; - ssize_t written; - struct websocket *wsp = &data->req.p.http->ws; - - if(!data->set.ws_raw_mode) { - result = Curl_get_upload_buffer(data); - if(result) - return result; - } - else { - if(totalsize || sendflags) - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - if(data->set.ws_raw_mode) { - if(!buflen) - /* nothing to do */ - return CURLE_OK; - /* raw mode sends exactly what was requested, and this is from within - the write callback */ - if(Curl_is_in_callback(data)) - result = Curl_write(data, data->conn->writesockfd, buffer, buflen, - &written); - else - result = Curl_senddata(data, buffer, buflen, &written); - - infof(data, "WS: wanted to send %zu bytes, sent %zu bytes", - buflen, written); - *sent = written; - return result; - } - - if(buflen > (data->set.upload_buffer_size - 10)) - /* don't do more than this in one go */ - buflen = data->set.upload_buffer_size - 10; - - if(sendflags & CURLWS_OFFSET) { - if(totalsize) { - /* a frame series 'totalsize' bytes big, this is the first */ - headlen = ws_packethead(data, totalsize, sendflags); - wsp->sleft = totalsize - buflen; - } - else { - headlen = 0; - if((curl_off_t)buflen > wsp->sleft) { - infof(data, "WS: unaligned frame size (sending %zu instead of %zu)", - buflen, wsp->sleft); - wsp->sleft = 0; - } - else - wsp->sleft -= buflen; - } - } - else - headlen = ws_packethead(data, buflen, sendflags); - - /* headlen is the size of the frame header */ - out = data->state.ulbuf; - if(buflen) - /* for PING and PONG etc there might not be a payload */ - ws_xor(data, buffer, (unsigned char *)out + headlen, buflen); - - if(data->set.connect_only) - result = Curl_senddata(data, out, buflen + headlen, &written); - else - result = Curl_write(data, data->conn->writesockfd, out, - buflen + headlen, &written); - - infof(data, "WS: wanted to send %zu bytes, sent %zu bytes", - headlen + buflen, written); - *sent = written; - - return result; -} - -void Curl_ws_done(struct Curl_easy *data) -{ - struct websocket *wsp = &data->req.p.http->ws; - DEBUGASSERT(wsp); - Curl_dyn_free(&wsp->buf); -} - -CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data) -{ - /* we only return something for websocket, called from within the callback - when not using raw mode */ - if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) && data->req.p.http && - !data->set.ws_raw_mode) - return &data->req.p.http->ws.frame; - return NULL; -} - -#else - -CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, - size_t *nread, - struct curl_ws_frame **metap) -{ - (void)curl; - (void)buffer; - (void)buflen; - (void)nread; - (void)metap; - return CURLE_OK; -} - -CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer, - size_t buflen, size_t *sent, - curl_off_t framesize, - unsigned int sendflags) -{ - (void)curl; - (void)buffer; - (void)buflen; - (void)sent; - (void)framesize; - (void)sendflags; - return CURLE_OK; -} - -CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data) -{ - (void)data; - return NULL; -} -#endif /* USE_WEBSOCKETS */ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_WEBSOCKETS
+
+#include "urldata.h"
+#include "dynbuf.h"
+#include "rand.h"
+#include "curl_base64.h"
+#include "sendf.h"
+#include "multiif.h"
+#include "ws.h"
+#include "easyif.h"
+#include "transfer.h"
+#include "nonblock.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+struct wsfield {
+ const char *name;
+ const char *val;
+};
+
+CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req)
+{
+ unsigned int i;
+ CURLcode result = CURLE_OK;
+ unsigned char rand[16];
+ char *randstr;
+ size_t randlen;
+ char keyval[40];
+ struct SingleRequest *k = &data->req;
+ struct wsfield heads[]= {
+ {
+ /* The request MUST contain an |Upgrade| header field whose value
+ MUST include the "websocket" keyword. */
+ "Upgrade:", "websocket"
+ },
+ {
+ /* The request MUST contain a |Connection| header field whose value
+ MUST include the "Upgrade" token. */
+ "Connection:", "Upgrade",
+ },
+ {
+ /* The request MUST include a header field with the name
+ |Sec-WebSocket-Version|. The value of this header field MUST be
+ 13. */
+ "Sec-WebSocket-Version:", "13",
+ },
+ {
+ /* The request MUST include a header field with the name
+ |Sec-WebSocket-Key|. The value of this header field MUST be a nonce
+ consisting of a randomly selected 16-byte value that has been
+ base64-encoded (see Section 4 of [RFC4648]). The nonce MUST be
+ selected randomly for each connection. */
+ "Sec-WebSocket-Key:", NULL,
+ }
+ };
+ heads[3].val = &keyval[0];
+
+ /* 16 bytes random */
+ result = Curl_rand(data, (unsigned char *)rand, sizeof(rand));
+ if(result)
+ return result;
+ result = Curl_base64_encode((char *)rand, sizeof(rand), &randstr, &randlen);
+ if(result)
+ return result;
+ DEBUGASSERT(randlen < sizeof(keyval));
+ if(randlen >= sizeof(keyval))
+ return CURLE_FAILED_INIT;
+ strcpy(keyval, randstr);
+ free(randstr);
+ for(i = 0; !result && (i < sizeof(heads)/sizeof(heads[0])); i++) {
+ if(!Curl_checkheaders(data, STRCONST(heads[i].name))) {
+#ifdef USE_HYPER
+ char field[128];
+ msnprintf(field, sizeof(field), "%s %s", heads[i].name,
+ heads[i].val);
+ result = Curl_hyper_header(data, req, field);
+#else
+ (void)data;
+ result = Curl_dyn_addf(req, "%s %s\r\n", heads[i].name,
+ heads[i].val);
+#endif
+ }
+ }
+ k->upgr101 = UPGR101_WS;
+ Curl_dyn_init(&data->req.p.http->ws.buf, MAX_WS_SIZE * 2);
+ return result;
+}
+
+CURLcode Curl_ws_accept(struct Curl_easy *data)
+{
+ struct SingleRequest *k = &data->req;
+ struct HTTP *ws = data->req.p.http;
+ struct connectdata *conn = data->conn;
+ struct websocket *wsp = &data->req.p.http->ws;
+ CURLcode result;
+
+ /* Verify the Sec-WebSocket-Accept response.
+
+ The sent value is the base64 encoded version of a SHA-1 hash done on the
+ |Sec-WebSocket-Key| header field concatenated with
+ the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".
+ */
+
+ /* If the response includes a |Sec-WebSocket-Extensions| header field and
+ this header field indicates the use of an extension that was not present
+ in the client's handshake (the server has indicated an extension not
+ requested by the client), the client MUST Fail the WebSocket Connection.
+ */
+
+ /* If the response includes a |Sec-WebSocket-Protocol| header field
+ and this header field indicates the use of a subprotocol that was
+ not present in the client's handshake (the server has indicated a
+ subprotocol not requested by the client), the client MUST Fail
+ the WebSocket Connection. */
+
+ /* 4 bytes random */
+ result = Curl_rand(data, (unsigned char *)&ws->ws.mask, sizeof(ws->ws.mask));
+ if(result)
+ return result;
+
+ infof(data, "Received 101, switch to WebSocket; mask %02x%02x%02x%02x",
+ ws->ws.mask[0], ws->ws.mask[1], ws->ws.mask[2], ws->ws.mask[3]);
+ k->upgr101 = UPGR101_RECEIVED;
+
+ if(data->set.connect_only)
+ /* switch off non-blocking sockets */
+ (void)curlx_nonblock(conn->sock[FIRSTSOCKET], FALSE);
+
+ wsp->oleft = 0;
+ return result;
+}
+
+#define WSBIT_FIN 0x80
+#define WSBIT_OPCODE_CONT 0
+#define WSBIT_OPCODE_TEXT (1)
+#define WSBIT_OPCODE_BIN (2)
+#define WSBIT_OPCODE_CLOSE (8)
+#define WSBIT_OPCODE_PING (9)
+#define WSBIT_OPCODE_PONG (0xa)
+#define WSBIT_OPCODE_MASK (0xf)
+
+#define WSBIT_MASK 0x80
+
+/* remove the spent bytes from the beginning of the buffer as that part has
+ now been delivered to the application */
+static void ws_decode_shift(struct Curl_easy *data, size_t spent)
+{
+ struct websocket *wsp = &data->req.p.http->ws;
+ size_t len = Curl_dyn_len(&wsp->buf);
+ size_t keep = len - spent;
+ DEBUGASSERT(len >= spent);
+ Curl_dyn_tail(&wsp->buf, keep);
+}
+
+/* ws_decode() decodes a binary frame into structured WebSocket data,
+
+ wpkt - the incoming raw data. If NULL, work on the already buffered data.
+ ilen - the size of the provided data, perhaps too little, perhaps too much
+ out - stored pointed to extracted data
+ olen - stored length of the extracted data
+ oleft - number of unread bytes pending to that belongs to this frame
+ more - if there is more data in there
+ flags - stored bitmask about the frame
+
+ Returns CURLE_AGAIN if there is only a partial frame in the buffer. Then it
+ stores the first part in the ->extra buffer to be used in the next call
+ when more data is provided.
+*/
+
+static CURLcode ws_decode(struct Curl_easy *data,
+ unsigned char *inbuf, size_t inlen,
+ size_t *headlen, size_t *olen,
+ curl_off_t *oleft,
+ unsigned int *flags)
+{
+ bool fin;
+ unsigned char opcode;
+ curl_off_t total;
+ size_t dataindex = 2;
+ curl_off_t payloadsize;
+
+ *olen = *headlen = 0;
+
+ if(inlen < 2) {
+ /* the smallest possible frame is two bytes */
+ infof(data, "WS: plen == %u, EAGAIN", (int)inlen);
+ return CURLE_AGAIN;
+ }
+
+ fin = inbuf[0] & WSBIT_FIN;
+ opcode = inbuf[0] & WSBIT_OPCODE_MASK;
+ infof(data, "WS:%d received FIN bit %u", __LINE__, (int)fin);
+ *flags = 0;
+ switch(opcode) {
+ case WSBIT_OPCODE_CONT:
+ if(!fin)
+ *flags |= CURLWS_CONT;
+ infof(data, "WS: received OPCODE CONT");
+ break;
+ case WSBIT_OPCODE_TEXT:
+ infof(data, "WS: received OPCODE TEXT");
+ *flags |= CURLWS_TEXT;
+ break;
+ case WSBIT_OPCODE_BIN:
+ infof(data, "WS: received OPCODE BINARY");
+ *flags |= CURLWS_BINARY;
+ break;
+ case WSBIT_OPCODE_CLOSE:
+ infof(data, "WS: received OPCODE CLOSE");
+ *flags |= CURLWS_CLOSE;
+ break;
+ case WSBIT_OPCODE_PING:
+ infof(data, "WS: received OPCODE PING");
+ *flags |= CURLWS_PING;
+ break;
+ case WSBIT_OPCODE_PONG:
+ infof(data, "WS: received OPCODE PONG");
+ *flags |= CURLWS_PONG;
+ break;
+ }
+
+ if(inbuf[1] & WSBIT_MASK) {
+ /* A client MUST close a connection if it detects a masked frame. */
+ failf(data, "WS: masked input frame");
+ return CURLE_RECV_ERROR;
+ }
+ payloadsize = inbuf[1];
+ if(payloadsize == 126) {
+ if(inlen < 4) {
+ infof(data, "WS:%d plen == %u, EAGAIN", __LINE__, (int)inlen);
+ return CURLE_AGAIN; /* not enough data available */
+ }
+ payloadsize = (inbuf[2] << 8) | inbuf[3];
+ dataindex += 2;
+ }
+ else if(payloadsize == 127) {
+ /* 64 bit payload size */
+ if(inlen < 10)
+ return CURLE_AGAIN;
+ if(inbuf[2] & 80) {
+ failf(data, "WS: too large frame");
+ return CURLE_RECV_ERROR;
+ }
+ dataindex += 8;
+ payloadsize = ((curl_off_t)inbuf[2] << 56) |
+ (curl_off_t)inbuf[3] << 48 |
+ (curl_off_t)inbuf[4] << 40 |
+ (curl_off_t)inbuf[5] << 32 |
+ (curl_off_t)inbuf[6] << 24 |
+ (curl_off_t)inbuf[7] << 16 |
+ (curl_off_t)inbuf[8] << 8 |
+ inbuf[9];
+ }
+
+ /* point to the payload */
+ *headlen = dataindex;
+ total = dataindex + payloadsize;
+ if(total > (curl_off_t)inlen) {
+ /* buffer contains partial frame */
+ *olen = inlen - dataindex; /* bytes to write out */
+ *oleft = total - inlen; /* bytes yet to come (for this frame) */
+ payloadsize = total - dataindex;
+ }
+ else {
+ /* we have the complete frame (`total` bytes) in buffer */
+ *olen = payloadsize; /* bytes to write out */
+ *oleft = 0; /* bytes yet to come (for this frame) */
+ }
+
+ infof(data, "WS: received %zu bytes payload (%zu left, buflen was %zu)",
+ payloadsize, *oleft, inlen);
+ return CURLE_OK;
+}
+
+/* Curl_ws_writecb() is the write callback for websocket traffic. The
+ websocket data is provided to this raw, in chunks. This function should
+ handle/decode the data and call the "real" underlying callback accordingly.
+*/
+size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */,
+ size_t nitems, void *userp)
+{
+ struct HTTP *ws = (struct HTTP *)userp;
+ struct Curl_easy *data = ws->ws.data;
+ struct websocket *wsp = &data->req.p.http->ws;
+ void *writebody_ptr = data->set.out;
+ if(data->set.ws_raw_mode)
+ return data->set.fwrite_func(buffer, size, nitems, writebody_ptr);
+ else if(nitems) {
+ size_t wrote = 0, headlen;
+ CURLcode result;
+
+ if(buffer) {
+ result = Curl_dyn_addn(&wsp->buf, buffer, nitems);
+ if(result) {
+ infof(data, "WS: error adding data to buffer %d", (int)result);
+ return nitems - 1;
+ }
+ buffer = NULL;
+ }
+
+ while(Curl_dyn_len(&wsp->buf)) {
+ unsigned char *wsbuf = Curl_dyn_uptr(&wsp->buf);
+ size_t buflen = Curl_dyn_len(&wsp->buf);
+ size_t write_len = 0;
+ size_t consumed = 0;
+
+ if(!ws->ws.frame.bytesleft) {
+ unsigned int recvflags;
+ curl_off_t fb_left;
+
+ result = ws_decode(data, wsbuf, buflen,
+ &headlen, &write_len, &fb_left, &recvflags);
+ consumed += headlen;
+ wsbuf += headlen;
+ buflen -= headlen;
+ if(result == CURLE_AGAIN)
+ /* insufficient amount of data, keep it for later.
+ * we pretend to have written all since we have a copy */
+ return nitems;
+ else if(result) {
+ infof(data, "WS: decode error %d", (int)result);
+ return nitems - 1;
+ }
+ /* New frame. store details about the frame to be reachable with
+ curl_ws_meta() from within the write callback */
+ ws->ws.frame.age = 0;
+ ws->ws.frame.offset = 0;
+ ws->ws.frame.flags = recvflags;
+ ws->ws.frame.bytesleft = fb_left;
+ }
+ else {
+ /* continuing frame */
+ write_len = (size_t)ws->ws.frame.bytesleft;
+ if(write_len > buflen)
+ write_len = buflen;
+ ws->ws.frame.offset += write_len;
+ ws->ws.frame.bytesleft -= write_len;
+ }
+ if((ws->ws.frame.flags & CURLWS_PING) && !ws->ws.frame.bytesleft) {
+ /* auto-respond to PINGs, only works for single-frame payloads atm */
+ size_t bytes;
+ infof(data, "WS: auto-respond to PING with a PONG");
+ /* send back the exact same content as a PONG */
+ result = curl_ws_send(data, wsbuf, write_len,
+ &bytes, 0, CURLWS_PONG);
+ if(result)
+ return result;
+ }
+ else if(write_len || !wsp->frame.bytesleft) {
+ /* deliver the decoded frame to the user callback */
+ Curl_set_in_callback(data, true);
+ wrote = data->set.fwrite_func((char *)wsbuf, 1,
+ write_len, writebody_ptr);
+ Curl_set_in_callback(data, false);
+ if(wrote != write_len)
+ return 0;
+ }
+ /* get rid of the buffered data consumed */
+ consumed += write_len;
+ ws_decode_shift(data, consumed);
+ }
+ }
+ return nitems;
+}
+
+
+CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
+ size_t buflen, size_t *nread,
+ struct curl_ws_frame **metap)
+{
+ CURLcode result;
+ struct websocket *wsp = &data->req.p.http->ws;
+ bool done = FALSE; /* not filled passed buffer yet */
+
+ *nread = 0;
+ *metap = NULL;
+ /* get a download buffer */
+ result = Curl_preconnect(data);
+ if(result)
+ return result;
+
+ while(!done) {
+ size_t write_len;
+ unsigned int recvflags;
+
+ if(!wsp->stillblen) {
+ /* try to get more data */
+ size_t n;
+ result = curl_easy_recv(data, data->state.buffer,
+ data->set.buffer_size, &n);
+ if(result)
+ return result;
+ if(!n)
+ /* connection closed */
+ return CURLE_GOT_NOTHING;
+ wsp->stillb = data->state.buffer;
+ wsp->stillblen = n;
+ }
+
+ infof(data, "WS: got %u websocket bytes to decode",
+ (int)wsp->stillblen);
+ if(!wsp->frame.bytesleft) {
+ size_t headlen;
+ curl_off_t oleft;
+ /* detect new frame */
+ result = ws_decode(data, (unsigned char *)wsp->stillb, wsp->stillblen,
+ &headlen, &write_len, &oleft, &recvflags);
+ if(result == CURLE_AGAIN)
+ /* a packet fragment only */
+ break;
+ else if(result)
+ return result;
+ wsp->stillb += headlen;
+ wsp->stillblen -= headlen;
+ wsp->frame.offset = 0;
+ wsp->frame.bytesleft = oleft;
+ wsp->frame.flags = recvflags;
+ }
+ else {
+ /* existing frame, remaining payload handling */
+ write_len = wsp->frame.bytesleft;
+ if(write_len > wsp->stillblen)
+ write_len = wsp->stillblen;
+ }
+
+ /* auto-respond to PINGs */
+ if((wsp->frame.flags & CURLWS_PING) && !wsp->frame.bytesleft) {
+ infof(data, "WS: auto-respond to PING with a PONG");
+ /* send back the exact same content as a PONG */
+ result = curl_ws_send(data, wsp->stillb, write_len,
+ &write_len, 0, CURLWS_PONG);
+ if(result)
+ return result;
+ }
+ else if(write_len || !wsp->frame.bytesleft) {
+ if(write_len > buflen)
+ write_len = buflen;
+ /* copy the payload to the user buffer */
+ memcpy(buffer, wsp->stillb, write_len);
+ *nread = write_len;
+ done = TRUE;
+ }
+ if(write_len) {
+ /* update buffer and frame info */
+ wsp->frame.offset += write_len;
+ DEBUGASSERT(wsp->frame.bytesleft >= (curl_off_t)write_len);
+ if(wsp->frame.bytesleft)
+ wsp->frame.bytesleft -= write_len;
+ DEBUGASSERT(write_len <= wsp->stillblen);
+ wsp->stillblen -= write_len;
+ if(wsp->stillblen)
+ wsp->stillb += write_len;
+ else
+ wsp->stillb = NULL;
+ }
+ }
+ *metap = &wsp->frame;
+ return CURLE_OK;
+}
+
+static void ws_xor(struct Curl_easy *data,
+ const unsigned char *source,
+ unsigned char *dest,
+ size_t len)
+{
+ struct websocket *wsp = &data->req.p.http->ws;
+ size_t i;
+ /* append payload after the mask, XOR appropriately */
+ for(i = 0; i < len; i++) {
+ dest[i] = source[i] ^ wsp->mask[wsp->xori];
+ wsp->xori++;
+ wsp->xori &= 3;
+ }
+}
+
+/***
+ RFC 6455 Section 5.2
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-------+-+-------------+-------------------------------+
+ |F|R|R|R| opcode|M| Payload len | Extended payload length |
+ |I|S|S|S| (4) |A| (7) | (16/64) |
+ |N|V|V|V| |S| | (if payload len==126/127) |
+ | |1|2|3| |K| | |
+ +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
+ | Extended payload length continued, if payload len == 127 |
+ + - - - - - - - - - - - - - - - +-------------------------------+
+ | |Masking-key, if MASK set to 1 |
+ +-------------------------------+-------------------------------+
+ | Masking-key (continued) | Payload Data |
+ +-------------------------------- - - - - - - - - - - - - - - - +
+ : Payload Data continued ... :
+ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+ | Payload Data continued ... |
+ +---------------------------------------------------------------+
+*/
+
+static size_t ws_packethead(struct Curl_easy *data,
+ size_t len, unsigned int flags)
+{
+ struct HTTP *ws = data->req.p.http;
+ unsigned char *out = (unsigned char *)data->state.ulbuf;
+ unsigned char firstbyte = 0;
+ int outi;
+ unsigned char opcode;
+ if(flags & CURLWS_TEXT) {
+ opcode = WSBIT_OPCODE_TEXT;
+ infof(data, "WS: send OPCODE TEXT");
+ }
+ else if(flags & CURLWS_CLOSE) {
+ opcode = WSBIT_OPCODE_CLOSE;
+ infof(data, "WS: send OPCODE CLOSE");
+ }
+ else if(flags & CURLWS_PING) {
+ opcode = WSBIT_OPCODE_PING;
+ infof(data, "WS: send OPCODE PING");
+ }
+ else if(flags & CURLWS_PONG) {
+ opcode = WSBIT_OPCODE_PONG;
+ infof(data, "WS: send OPCODE PONG");
+ }
+ else {
+ opcode = WSBIT_OPCODE_BIN;
+ infof(data, "WS: send OPCODE BINARY");
+ }
+
+ if(!(flags & CURLWS_CONT)) {
+ /* if not marked as continuing, assume this is the final fragment */
+ firstbyte |= WSBIT_FIN | opcode;
+ ws->ws.contfragment = FALSE;
+ }
+ else if(ws->ws.contfragment) {
+ /* the previous fragment was not a final one and this isn't either, keep a
+ CONT opcode and no FIN bit */
+ firstbyte |= WSBIT_OPCODE_CONT;
+ }
+ else {
+ ws->ws.contfragment = TRUE;
+ }
+ out[0] = firstbyte;
+ if(len > 65535) {
+ out[1] = 127 | WSBIT_MASK;
+ out[2] = (len >> 8) & 0xff;
+ out[3] = len & 0xff;
+ outi = 10;
+ }
+ else if(len > 126) {
+ out[1] = 126 | WSBIT_MASK;
+ out[2] = (len >> 8) & 0xff;
+ out[3] = len & 0xff;
+ outi = 4;
+ }
+ else {
+ out[1] = (unsigned char)len | WSBIT_MASK;
+ outi = 2;
+ }
+
+ infof(data, "WS: send FIN bit %u (byte %02x)",
+ firstbyte & WSBIT_FIN ? 1 : 0,
+ firstbyte);
+ infof(data, "WS: send payload len %u", (int)len);
+
+ /* 4 bytes mask */
+ memcpy(&out[outi], &ws->ws.mask, 4);
+
+ if(data->set.upload_buffer_size < (len + 10))
+ return 0;
+
+ /* pass over the mask */
+ outi += 4;
+
+ ws->ws.xori = 0;
+ /* return packet size */
+ return outi;
+}
+
+CURL_EXTERN CURLcode curl_ws_send(struct Curl_easy *data, const void *buffer,
+ size_t buflen, size_t *sent,
+ curl_off_t totalsize,
+ unsigned int sendflags)
+{
+ CURLcode result;
+ size_t headlen;
+ char *out;
+ ssize_t written;
+ struct websocket *wsp = &data->req.p.http->ws;
+
+ if(!data->set.ws_raw_mode) {
+ result = Curl_get_upload_buffer(data);
+ if(result)
+ return result;
+ }
+ else {
+ if(totalsize || sendflags)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+
+ if(data->set.ws_raw_mode) {
+ if(!buflen)
+ /* nothing to do */
+ return CURLE_OK;
+ /* raw mode sends exactly what was requested, and this is from within
+ the write callback */
+ if(Curl_is_in_callback(data)) {
+ if(!data->conn) {
+ failf(data, "No associated connection");
+ return CURLE_SEND_ERROR;
+ }
+ result = Curl_write(data, data->conn->writesockfd, buffer, buflen,
+ &written);
+ }
+ else
+ result = Curl_senddata(data, buffer, buflen, &written);
+
+ infof(data, "WS: wanted to send %zu bytes, sent %zu bytes",
+ buflen, written);
+ *sent = written;
+ return result;
+ }
+
+ if(buflen > (data->set.upload_buffer_size - 10))
+ /* don't do more than this in one go */
+ buflen = data->set.upload_buffer_size - 10;
+
+ if(sendflags & CURLWS_OFFSET) {
+ if(totalsize) {
+ /* a frame series 'totalsize' bytes big, this is the first */
+ headlen = ws_packethead(data, totalsize, sendflags);
+ wsp->sleft = totalsize - buflen;
+ }
+ else {
+ headlen = 0;
+ if((curl_off_t)buflen > wsp->sleft) {
+ infof(data, "WS: unaligned frame size (sending %zu instead of %zu)",
+ buflen, wsp->sleft);
+ wsp->sleft = 0;
+ }
+ else
+ wsp->sleft -= buflen;
+ }
+ }
+ else
+ headlen = ws_packethead(data, buflen, sendflags);
+
+ /* headlen is the size of the frame header */
+ out = data->state.ulbuf;
+ if(buflen)
+ /* for PING and PONG etc there might not be a payload */
+ ws_xor(data, buffer, (unsigned char *)out + headlen, buflen);
+
+ if(data->set.connect_only)
+ result = Curl_senddata(data, out, buflen + headlen, &written);
+ else
+ result = Curl_write(data, data->conn->writesockfd, out,
+ buflen + headlen, &written);
+
+ infof(data, "WS: wanted to send %zu bytes, sent %zu bytes",
+ headlen + buflen, written);
+ *sent = written;
+
+ return result;
+}
+
+void Curl_ws_done(struct Curl_easy *data)
+{
+ struct websocket *wsp = &data->req.p.http->ws;
+ DEBUGASSERT(wsp);
+ Curl_dyn_free(&wsp->buf);
+}
+
+CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
+{
+ /* we only return something for websocket, called from within the callback
+ when not using raw mode */
+ if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) && data->req.p.http &&
+ !data->set.ws_raw_mode)
+ return &data->req.p.http->ws.frame;
+ return NULL;
+}
+
+#else
+
+CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen,
+ size_t *nread,
+ struct curl_ws_frame **metap)
+{
+ (void)curl;
+ (void)buffer;
+ (void)buflen;
+ (void)nread;
+ (void)metap;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer,
+ size_t buflen, size_t *sent,
+ curl_off_t framesize,
+ unsigned int sendflags)
+{
+ (void)curl;
+ (void)buffer;
+ (void)buflen;
+ (void)sent;
+ (void)framesize;
+ (void)sendflags;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
+{
+ (void)data;
+ return NULL;
+}
+#endif /* USE_WEBSOCKETS */
diff --git a/libs/libcurl/src/ws.h b/libs/libcurl/src/ws.h index 341242e50e..6f0aa48983 100644 --- a/libs/libcurl/src/ws.h +++ b/libs/libcurl/src/ws.h @@ -1,69 +1,69 @@ -#ifndef HEADER_CURL_WS_H -#define HEADER_CURL_WS_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, 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_WEBSOCKETS - -#ifdef USE_HYPER -#define REQTYPE void -#else -#define REQTYPE struct dynbuf -#endif - -/* this is the largest single fragment size we support */ -#define MAX_WS_SIZE 65535 - -/* part of 'struct HTTP', when used in the 'struct SingleRequest' in the - Curl_easy struct */ -struct websocket { - bool contfragment; /* set TRUE if the previous fragment sent was not final */ - unsigned char mask[4]; /* 32 bit mask for this connection */ - struct Curl_easy *data; /* used for write callback handling */ - struct dynbuf buf; - size_t usedbuf; /* number of leading bytes in 'buf' the most recent complete - websocket frame uses */ - struct curl_ws_frame frame; /* the struct used for frame state */ - curl_off_t oleft; /* outstanding number of payload bytes left from the - server */ - curl_off_t stillbuffer; /* number of bytes left in the buffer to deliver in - the next curl_ws_recv() call */ - char *stillb; /* the stillbuffer pending bytes are here */ - curl_off_t sleft; /* outstanding number of payload bytes left to send */ - unsigned int xori; /* xor index */ -}; - -CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req); -CURLcode Curl_ws_accept(struct Curl_easy *data); - -size_t Curl_ws_writecb(char *buffer, size_t size, size_t nitems, void *userp); -void Curl_ws_done(struct Curl_easy *data); - -#else -#define Curl_ws_request(x,y) CURLE_OK -#define Curl_ws_done(x) Curl_nop_stmt -#endif - -#endif /* HEADER_CURL_WS_H */ +#ifndef HEADER_CURL_WS_H
+#define HEADER_CURL_WS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, 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_WEBSOCKETS
+
+#ifdef USE_HYPER
+#define REQTYPE void
+#else
+#define REQTYPE struct dynbuf
+#endif
+
+/* this is the largest single fragment size we support */
+#define MAX_WS_SIZE 65535
+
+/* part of 'struct HTTP', when used in the 'struct SingleRequest' in the
+ Curl_easy struct */
+struct websocket {
+ bool contfragment; /* set TRUE if the previous fragment sent was not final */
+ unsigned char mask[4]; /* 32 bit mask for this connection */
+ struct Curl_easy *data; /* used for write callback handling */
+ struct dynbuf buf;
+ size_t usedbuf; /* number of leading bytes in 'buf' the most recent complete
+ websocket frame uses */
+ struct curl_ws_frame frame; /* the struct used for frame state */
+ curl_off_t oleft; /* outstanding number of payload bytes left from the
+ server */
+ size_t stillblen; /* number of bytes left in the buffer to deliver in
+ the next curl_ws_recv() call */
+ char *stillb; /* the stillblen pending bytes are here */
+ curl_off_t sleft; /* outstanding number of payload bytes left to send */
+ unsigned int xori; /* xor index */
+};
+
+CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req);
+CURLcode Curl_ws_accept(struct Curl_easy *data);
+
+size_t Curl_ws_writecb(char *buffer, size_t size, size_t nitems, void *userp);
+void Curl_ws_done(struct Curl_easy *data);
+
+#else
+#define Curl_ws_request(x,y) CURLE_OK
+#define Curl_ws_done(x) Curl_nop_stmt
+#endif
+
+#endif /* HEADER_CURL_WS_H */
|