summaryrefslogtreecommitdiff
path: root/libs/libcurl/src
diff options
context:
space:
mode:
Diffstat (limited to 'libs/libcurl/src')
-rw-r--r--libs/libcurl/src/.checksrc1
-rw-r--r--libs/libcurl/src/CMakeLists.txt128
-rw-r--r--libs/libcurl/src/Makefile.am10
-rw-r--r--libs/libcurl/src/Makefile.in23
-rw-r--r--libs/libcurl/src/altsvc.c44
-rw-r--r--libs/libcurl/src/altsvc.h2
-rw-r--r--libs/libcurl/src/asyn-thread.c252
-rw-r--r--libs/libcurl/src/bufq.c69
-rw-r--r--libs/libcurl/src/bufq.h6
-rw-r--r--libs/libcurl/src/c-hyper.c315
-rw-r--r--libs/libcurl/src/cf-h1-proxy.c5
-rw-r--r--libs/libcurl/src/cf-h2-proxy.c192
-rw-r--r--libs/libcurl/src/cf-haproxy.c36
-rw-r--r--libs/libcurl/src/cf-https-connect.c46
-rw-r--r--libs/libcurl/src/cf-socket.c144
-rw-r--r--libs/libcurl/src/cf-socket.h2
-rw-r--r--libs/libcurl/src/cfilters.c75
-rw-r--r--libs/libcurl/src/cfilters.h41
-rw-r--r--libs/libcurl/src/config-mac.h2
-rw-r--r--libs/libcurl/src/config-os400.h5
-rw-r--r--libs/libcurl/src/config-plan9.h1
-rw-r--r--libs/libcurl/src/config-riscos.h10
-rw-r--r--libs/libcurl/src/config-win32.h7
-rw-r--r--libs/libcurl/src/config-win32ce.h4
-rw-r--r--libs/libcurl/src/conncache.c1295
-rw-r--r--libs/libcurl/src/conncache.h253
-rw-r--r--libs/libcurl/src/connect.c128
-rw-r--r--libs/libcurl/src/connect.h3
-rw-r--r--libs/libcurl/src/content_encoding.c14
-rw-r--r--libs/libcurl/src/cookie.c9
-rw-r--r--libs/libcurl/src/curl_addrinfo.c2
-rw-r--r--libs/libcurl/src/curl_config.h.cmake34
-rw-r--r--libs/libcurl/src/curl_config.h.in31
-rw-r--r--libs/libcurl/src/curl_des.c8
-rw-r--r--libs/libcurl/src/curl_des.h8
-rw-r--r--libs/libcurl/src/curl_fnmatch.h2
-rw-r--r--libs/libcurl/src/curl_gethostname.c17
-rw-r--r--libs/libcurl/src/curl_multibyte.h6
-rw-r--r--libs/libcurl/src/curl_ntlm_core.c26
-rw-r--r--libs/libcurl/src/curl_printf.h5
-rw-r--r--libs/libcurl/src/curl_range.c15
-rw-r--r--libs/libcurl/src/curl_rtmp.c3
-rw-r--r--libs/libcurl/src/curl_setup.h44
-rw-r--r--libs/libcurl/src/curl_sha256.h9
-rw-r--r--libs/libcurl/src/curl_sha512_256.c75
-rw-r--r--libs/libcurl/src/curl_sha512_256.h2
-rw-r--r--libs/libcurl/src/curl_sspi.c26
-rw-r--r--libs/libcurl/src/curl_sspi.h4
-rw-r--r--libs/libcurl/src/curl_threads.c2
-rw-r--r--libs/libcurl/src/curl_trc.c205
-rw-r--r--libs/libcurl/src/curl_trc.h32
-rw-r--r--libs/libcurl/src/curlx.h43
-rw-r--r--libs/libcurl/src/dict.c2
-rw-r--r--libs/libcurl/src/doh.c525
-rw-r--r--libs/libcurl/src/doh.h59
-rw-r--r--libs/libcurl/src/easy.c159
-rw-r--r--libs/libcurl/src/escape.c4
-rw-r--r--libs/libcurl/src/file.c5
-rw-r--r--libs/libcurl/src/fileinfo.h2
-rw-r--r--libs/libcurl/src/formdata.c6
-rw-r--r--libs/libcurl/src/ftp.c133
-rw-r--r--libs/libcurl/src/getinfo.c48
-rw-r--r--libs/libcurl/src/gopher.c4
-rw-r--r--libs/libcurl/src/hash.c78
-rw-r--r--libs/libcurl/src/hash.h16
-rw-r--r--libs/libcurl/src/headers.c44
-rw-r--r--libs/libcurl/src/headers.h2
-rw-r--r--libs/libcurl/src/hostasyn.c2
-rw-r--r--libs/libcurl/src/hostip.c93
-rw-r--r--libs/libcurl/src/hostip.h21
-rw-r--r--libs/libcurl/src/hostip4.c4
-rw-r--r--libs/libcurl/src/hostip6.c2
-rw-r--r--libs/libcurl/src/hsts.c42
-rw-r--r--libs/libcurl/src/hsts.h2
-rw-r--r--libs/libcurl/src/http.c60
-rw-r--r--libs/libcurl/src/http2.c636
-rw-r--r--libs/libcurl/src/http_aws_sigv4.c137
-rw-r--r--libs/libcurl/src/http_chunks.c4
-rw-r--r--libs/libcurl/src/http_negotiate.c17
-rw-r--r--libs/libcurl/src/http_ntlm.c6
-rw-r--r--libs/libcurl/src/http_ntlm.h4
-rw-r--r--libs/libcurl/src/idn.c71
-rw-r--r--libs/libcurl/src/if2ip.c8
-rw-r--r--libs/libcurl/src/imap.c8
-rw-r--r--libs/libcurl/src/inet_ntop.c4
-rw-r--r--libs/libcurl/src/inet_pton.c4
-rw-r--r--libs/libcurl/src/krb5.c17
-rw-r--r--libs/libcurl/src/ldap.c2
-rw-r--r--libs/libcurl/src/libcurl.def95
-rw-r--r--libs/libcurl/src/llist.c200
-rw-r--r--libs/libcurl/src/llist.h67
-rw-r--r--libs/libcurl/src/macos.c28
-rw-r--r--libs/libcurl/src/memdebug.c10
-rw-r--r--libs/libcurl/src/memdebug.h12
-rw-r--r--libs/libcurl/src/mime.c51
-rw-r--r--libs/libcurl/src/mprintf.c1
-rw-r--r--libs/libcurl/src/mqtt.c2
-rw-r--r--libs/libcurl/src/multi.c1092
-rw-r--r--libs/libcurl/src/multihandle.h24
-rw-r--r--libs/libcurl/src/multiif.h17
-rw-r--r--libs/libcurl/src/netrc.c4
-rw-r--r--libs/libcurl/src/nonblock.c4
-rw-r--r--libs/libcurl/src/openldap.c2
-rw-r--r--libs/libcurl/src/optiontable.pl152
-rw-r--r--libs/libcurl/src/pingpong.c156
-rw-r--r--libs/libcurl/src/pingpong.h2
-rw-r--r--libs/libcurl/src/pop3.c84
-rw-r--r--libs/libcurl/src/pop3.h5
-rw-r--r--libs/libcurl/src/progress.c238
-rw-r--r--libs/libcurl/src/progress.h6
-rw-r--r--libs/libcurl/src/rand.c148
-rw-r--r--libs/libcurl/src/rand.h17
-rw-r--r--libs/libcurl/src/request.c85
-rw-r--r--libs/libcurl/src/request.h22
-rw-r--r--libs/libcurl/src/rtsp.c5
-rw-r--r--libs/libcurl/src/select.c14
-rw-r--r--libs/libcurl/src/sendf.c35
-rw-r--r--libs/libcurl/src/setopt.c77
-rw-r--r--libs/libcurl/src/setup-vms.h2
-rw-r--r--libs/libcurl/src/setup-win32.h6
-rw-r--r--libs/libcurl/src/sha256.c6
-rw-r--r--libs/libcurl/src/share.c14
-rw-r--r--libs/libcurl/src/share.h5
-rw-r--r--libs/libcurl/src/sigpipe.h3
-rw-r--r--libs/libcurl/src/smb.c35
-rw-r--r--libs/libcurl/src/smtp.c33
-rw-r--r--libs/libcurl/src/socketpair.h2
-rw-r--r--libs/libcurl/src/socks.c6
-rw-r--r--libs/libcurl/src/socks_gssapi.c13
-rw-r--r--libs/libcurl/src/socks_sspi.c169
-rw-r--r--libs/libcurl/src/splay.c31
-rw-r--r--libs/libcurl/src/splay.h12
-rw-r--r--libs/libcurl/src/strtoofft.c2
-rw-r--r--libs/libcurl/src/system_win32.c53
-rw-r--r--libs/libcurl/src/system_win32.h32
-rw-r--r--libs/libcurl/src/telnet.c6
-rw-r--r--libs/libcurl/src/tftp.c7
-rw-r--r--libs/libcurl/src/timediff.h2
-rw-r--r--libs/libcurl/src/timeval.c2
-rw-r--r--libs/libcurl/src/transfer.c219
-rw-r--r--libs/libcurl/src/transfer.h15
-rw-r--r--libs/libcurl/src/url.c1203
-rw-r--r--libs/libcurl/src/url.h19
-rw-r--r--libs/libcurl/src/urlapi.c24
-rw-r--r--libs/libcurl/src/urldata.h128
-rw-r--r--libs/libcurl/src/vauth/digest.c4
-rw-r--r--libs/libcurl/src/vauth/digest_sspi.c54
-rw-r--r--libs/libcurl/src/vauth/krb5_sspi.c26
-rw-r--r--libs/libcurl/src/vauth/ntlm.c24
-rw-r--r--libs/libcurl/src/vauth/ntlm_sspi.c22
-rw-r--r--libs/libcurl/src/vauth/spnego_gssapi.c12
-rw-r--r--libs/libcurl/src/vauth/spnego_sspi.c38
-rw-r--r--libs/libcurl/src/version.c6
-rw-r--r--libs/libcurl/src/version_win32.c2
-rw-r--r--libs/libcurl/src/version_win32.h2
-rw-r--r--libs/libcurl/src/vquic/curl_msh3.c66
-rw-r--r--libs/libcurl/src/vquic/curl_ngtcp2.c191
-rw-r--r--libs/libcurl/src/vquic/curl_osslq.c163
-rw-r--r--libs/libcurl/src/vquic/curl_quiche.c331
-rw-r--r--libs/libcurl/src/vquic/vquic-tls.c2
-rw-r--r--libs/libcurl/src/vquic/vquic.c4
-rw-r--r--libs/libcurl/src/vssh/libssh.c31
-rw-r--r--libs/libcurl/src/vssh/libssh2.c32
-rw-r--r--libs/libcurl/src/vssh/ssh.h2
-rw-r--r--libs/libcurl/src/vssh/wolfssh.c15
-rw-r--r--libs/libcurl/src/vtls/bearssl.c128
-rw-r--r--libs/libcurl/src/vtls/cipher_suite.c79
-rw-r--r--libs/libcurl/src/vtls/cipher_suite.h5
-rw-r--r--libs/libcurl/src/vtls/gtls.c221
-rw-r--r--libs/libcurl/src/vtls/mbedtls.c482
-rw-r--r--libs/libcurl/src/vtls/openssl.c395
-rw-r--r--libs/libcurl/src/vtls/openssl.h11
-rw-r--r--libs/libcurl/src/vtls/rustls.c289
-rw-r--r--libs/libcurl/src/vtls/schannel.c70
-rw-r--r--libs/libcurl/src/vtls/schannel_int.h4
-rw-r--r--libs/libcurl/src/vtls/schannel_verify.c8
-rw-r--r--libs/libcurl/src/vtls/sectransp.c302
-rw-r--r--libs/libcurl/src/vtls/vtls.c172
-rw-r--r--libs/libcurl/src/vtls/vtls.h21
-rw-r--r--libs/libcurl/src/vtls/vtls_int.h7
-rw-r--r--libs/libcurl/src/vtls/wolfssl.c491
-rw-r--r--libs/libcurl/src/vtls/wolfssl.h2
-rw-r--r--libs/libcurl/src/vtls/x509asn1.c12
-rw-r--r--libs/libcurl/src/ws.c259
184 files changed, 8079 insertions, 6562 deletions
diff --git a/libs/libcurl/src/.checksrc b/libs/libcurl/src/.checksrc
index 8ba24278c4..aa8bcffd6f 100644
--- a/libs/libcurl/src/.checksrc
+++ b/libs/libcurl/src/.checksrc
@@ -1 +1,2 @@
enable STRERROR
+enable STRNCPY
diff --git a/libs/libcurl/src/CMakeLists.txt b/libs/libcurl/src/CMakeLists.txt
index e0d8bcfd7c..479391fa19 100644
--- a/libs/libcurl/src/CMakeLists.txt
+++ b/libs/libcurl/src/CMakeLists.txt
@@ -23,46 +23,44 @@
###########################################################################
set(LIB_NAME libcurl)
set(LIBCURL_OUTPUT_NAME libcurl CACHE STRING "Basename of the curl library")
-add_definitions(-DBUILDING_LIBCURL)
+add_definitions("-DBUILDING_LIBCURL")
-configure_file(curl_config.h.cmake
- ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h)
+configure_file("curl_config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/curl_config.h")
+# Get 'CSOURCES', 'HHEADERS' variables
transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
-include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake)
+include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
# DllMain is added later for DLL builds only.
-list(REMOVE_ITEM CSOURCES dllmain.c)
+list(REMOVE_ITEM CSOURCES "dllmain.c")
-list(APPEND HHEADERS ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h)
+list(APPEND HHEADERS "${CMAKE_CURRENT_BINARY_DIR}/curl_config.h")
# 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})
+include_directories(
+ "${CMAKE_CURRENT_BINARY_DIR}"
+ "${CMAKE_CURRENT_SOURCE_DIR}"
+)
if(USE_ARES)
- include_directories(${CARES_INCLUDE_DIR})
+ include_directories(${CARES_INCLUDE_DIRS})
endif()
if(BUILD_TESTING)
add_library(
- curlu # special libcurlu library just for unittests
+ curlu # special libcurlu library just for unittests
STATIC
EXCLUDE_FROM_ALL
${HHEADERS} ${CSOURCES}
)
- target_compile_definitions(curlu PUBLIC UNITTESTS CURL_STATICLIB)
+ target_compile_definitions(curlu PUBLIC "UNITTESTS" "CURL_STATICLIB")
target_link_libraries(curlu PRIVATE ${CURL_LIBS})
endif()
if(ENABLE_CURLDEBUG)
# We must compile these sources separately to avoid memdebug.h redefinitions
# applying to them.
- set_source_files_properties(memdebug.c curl_multibyte.c PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
+ set_source_files_properties("memdebug.c" "curl_multibyte.c" PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
endif()
## Library definition
@@ -97,13 +95,12 @@ if(SHARE_LIB_OBJECT)
# exported libcurl symbols. We handle exports via libcurl.def instead.
# Except with symbol hiding disabled or debug mode enabled, when we export
# _all_ symbols from libcurl DLL, without using libcurl.def.
- set_property(TARGET ${LIB_OBJECT} APPEND
- PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB")
+ set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB")
endif()
target_link_libraries(${LIB_OBJECT} PRIVATE ${CURL_LIBS})
set_target_properties(${LIB_OBJECT} PROPERTIES
POSITION_INDEPENDENT_CODE ON)
- if(HIDES_CURL_PRIVATE_SYMBOLS)
+ if(CURL_HIDES_PRIVATE_SYMBOLS)
set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}")
set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
endif()
@@ -114,22 +111,21 @@ if(SHARE_LIB_OBJECT)
endif()
target_include_directories(${LIB_OBJECT} INTERFACE
- $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
- $<BUILD_INTERFACE:${CURL_SOURCE_DIR}/include>)
+ "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
+ "$<BUILD_INTERFACE:${CURL_SOURCE_DIR}/include>")
set(LIB_SOURCE $<TARGET_OBJECTS:${LIB_OBJECT}>)
else()
set(LIB_SOURCE ${HHEADERS} ${CSOURCES})
endif()
-# we want it to be called libcurl on all platforms
+# We want it to be called libcurl on all platforms
if(BUILD_STATIC_LIBS)
list(APPEND libcurl_export ${LIB_STATIC})
add_library(${LIB_STATIC} STATIC ${LIB_SOURCE})
add_library(${PROJECT_NAME}::${LIB_STATIC} ALIAS ${LIB_STATIC})
if(WIN32)
- set_property(TARGET ${LIB_OBJECT} APPEND
- PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB")
+ set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB")
endif()
target_link_libraries(${LIB_STATIC} PRIVATE ${CURL_LIBS})
# Remove the "lib" prefix since the library is already named "libcurl".
@@ -137,7 +133,7 @@ if(BUILD_STATIC_LIBS)
PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}"
SUFFIX "${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}"
INTERFACE_COMPILE_DEFINITIONS "CURL_STATICLIB")
- if(HIDES_CURL_PRIVATE_SYMBOLS)
+ if(CURL_HIDES_PRIVATE_SYMBOLS)
set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}")
set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
endif()
@@ -148,8 +144,8 @@ if(BUILD_STATIC_LIBS)
endif()
target_include_directories(${LIB_STATIC} INTERFACE
- $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
- $<BUILD_INTERFACE:${CURL_SOURCE_DIR}/include>)
+ "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
+ "$<BUILD_INTERFACE:${CURL_SOURCE_DIR}/include>")
endif()
if(BUILD_SHARED_LIBS)
@@ -160,15 +156,15 @@ if(BUILD_SHARED_LIBS)
if(CYGWIN)
# For Cygwin always compile dllmain.c as a separate unit since it
# includes windows.h, which should not be included in other units.
- set_source_files_properties(dllmain.c PROPERTIES
+ set_source_files_properties("dllmain.c" PROPERTIES
SKIP_UNITY_BUILD_INCLUSION ON)
endif()
- set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES dllmain.c)
+ set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "dllmain.c")
endif()
if(WIN32)
- set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES libcurl.rc)
- if(HIDES_CURL_PRIVATE_SYMBOLS)
- set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "${CURL_SOURCE_DIR}/libcurl.def")
+ set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "libcurl.rc")
+ if(CURL_HIDES_PRIVATE_SYMBOLS)
+ set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "${CURL_SOURCE_DIR}/lib/libcurl.def")
endif()
endif()
target_link_libraries(${LIB_SHARED} PRIVATE ${CURL_LIBS})
@@ -177,7 +173,7 @@ if(BUILD_SHARED_LIBS)
PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}"
IMPORT_PREFIX "" IMPORT_SUFFIX "${IMPORT_LIB_SUFFIX}${CMAKE_IMPORT_LIBRARY_SUFFIX}"
POSITION_INDEPENDENT_CODE ON)
- if(HIDES_CURL_PRIVATE_SYMBOLS)
+ if(CURL_HIDES_PRIVATE_SYMBOLS)
set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}")
set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
endif()
@@ -188,8 +184,8 @@ if(BUILD_SHARED_LIBS)
endif()
target_include_directories(${LIB_SHARED} INTERFACE
- $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
- $<BUILD_INTERFACE:${CURL_SOURCE_DIR}/include>)
+ "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
+ "$<BUILD_INTERFACE:${CURL_SOURCE_DIR}/include>")
if(CMAKE_DLL_NAME_WITH_SOVERSION OR
CYGWIN OR
@@ -203,22 +199,64 @@ if(BUILD_SHARED_LIBS)
# up to v3.x and ELF from v3.x. I cannot imagine someone running CMake
# on those ancient systems.
CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
- set(soversion_default TRUE)
+ set(_soversion_default TRUE)
else()
- set(soversion_default FALSE)
+ set(_soversion_default FALSE)
endif()
- option(CURL_LIBCURL_SOVERSION "Enable libcurl SOVERSION" ${soversion_default})
+ option(CURL_LIBCURL_SOVERSION "Enable libcurl SOVERSION" ${_soversion_default})
+ option(CURL_LIBCURL_VERSIONED_SYMBOLS "Enable libcurl versioned symbols" OFF)
- if(CURL_LIBCURL_SOVERSION)
+ if(CURL_LIBCURL_SOVERSION OR CURL_LIBCURL_VERSIONED_SYMBOLS)
+ # Get 'VERSIONCHANGE', 'VERSIONADD', 'VERSIONDEL', 'VERSIONINFO' variables
transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake")
- include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake)
+ include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake")
- math(EXPR CMAKESONAME "${VERSIONCHANGE} - ${VERSIONDEL}")
- set(CMAKEVERSION "${CMAKESONAME}.${VERSIONDEL}.${VERSIONADD}")
+ math(EXPR _cmakesoname "${VERSIONCHANGE} - ${VERSIONDEL}")
+ set(_cmakeversion "${_cmakesoname}.${VERSIONDEL}.${VERSIONADD}")
+ endif()
+ if(CURL_LIBCURL_SOVERSION)
set_target_properties(${LIB_SHARED} PROPERTIES
- VERSION "${CMAKEVERSION}" SOVERSION "${CMAKESONAME}")
+ VERSION "${_cmakeversion}" SOVERSION "${_cmakesoname}")
+ endif()
+
+ ## Versioned symbols
+
+ if(CURL_LIBCURL_VERSIONED_SYMBOLS)
+ if(NOT DEFINED CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX)
+ # Default to prefixes used by autotools
+ if(CURL_WITH_MULTI_SSL)
+ set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "MULTISSL_")
+ elseif(CURL_USE_OPENSSL)
+ set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "OPENSSL_")
+ elseif(CURL_USE_MBEDTLS)
+ set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "MBEDTLS_")
+ elseif(CURL_USE_BEARSSL)
+ set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "BEARSSL_")
+ elseif(CURL_USE_WOLFSSL)
+ set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "WOLFSSL_")
+ elseif(CURL_USE_GNUTLS)
+ set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "GNUTLS_")
+ endif()
+ endif()
+ file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libcurl.vers" "
+ HIDDEN {};
+ CURL_${CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX}${_cmakesoname}
+ {
+ global: curl_*;
+ local: *;
+ };")
+ include(CheckCSourceCompiles)
+ set(CMAKE_REQUIRED_LINK_OPTIONS "-Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/libcurl.vers")
+ check_c_source_compiles("int main(void) { return 0; }" HAVE_VERSIONED_SYMBOLS)
+ if(HAVE_VERSIONED_SYMBOLS)
+ # Superseded by LINK_OPTIONS in CMake 3.13 and later.
+ set_target_properties(${LIB_SHARED} PROPERTIES LINK_FLAGS "${CMAKE_REQUIRED_LINK_OPTIONS}")
+ else()
+ message(WARNING "Versioned symbols requested, but not supported by the toolchain.")
+ endif()
+ unset(CMAKE_REQUIRED_LINK_OPTIONS)
endif()
endif()
@@ -244,7 +282,7 @@ if(CURL_ENABLE_EXPORT_TARGET)
endif()
export(TARGETS ${libcurl_export}
- FILE ${PROJECT_BINARY_DIR}/libcurl-target.cmake
- NAMESPACE ${PROJECT_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 7f615a0711..f96af50d9b 100644
--- a/libs/libcurl/src/Makefile.am
+++ b/libs/libcurl/src/Makefile.am
@@ -25,10 +25,14 @@ AUTOMAKE_OPTIONS = foreign nostdinc
CMAKE_DIST = CMakeLists.txt curl_config.h.cmake
+CHECKSRC_DIST = .checksrc vauth/.checksrc vquic/.checksrc vssh/.checksrc \
+ vtls/.checksrc
+
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.rc \
config-amigaos.h config-win32ce.h config-os400.h setup-os400.h \
- $(CMAKE_DIST) setup-win32.h .checksrc Makefile.soname
+ $(CMAKE_DIST) setup-win32.h Makefile.soname optiontable.pl libcurl.def \
+ $(CHECKSRC_DIST)
lib_LTLIBRARIES = libcurl.la
@@ -109,11 +113,11 @@ libcurl_la_CFLAGS_EXTRA += $(CFLAG_CURL_SYMBOL_HIDING)
endif
libcurl_la_CPPFLAGS = $(AM_CPPFLAGS) $(libcurl_la_CPPFLAGS_EXTRA)
-libcurl_la_LDFLAGS = $(AM_LDFLAGS) $(libcurl_la_LDFLAGS_EXTRA) $(CURL_LDFLAGS_LIB) $(LIBCURL_LIBS)
+libcurl_la_LDFLAGS = $(AM_LDFLAGS) $(libcurl_la_LDFLAGS_EXTRA) $(CURL_LDFLAGS_LIB) $(LIBCURL_PC_LIBS_PRIVATE)
libcurl_la_CFLAGS = $(AM_CFLAGS) $(libcurl_la_CFLAGS_EXTRA)
libcurlu_la_CPPFLAGS = $(AM_CPPFLAGS) -DCURL_STATICLIB -DUNITTESTS
-libcurlu_la_LDFLAGS = $(AM_LDFLAGS) -static $(LIBCURL_LIBS)
+libcurlu_la_LDFLAGS = $(AM_LDFLAGS) -static $(LIBCURL_PC_LIBS_PRIVATE)
libcurlu_la_CFLAGS = $(AM_CFLAGS)
CHECKSRC = $(CS_$(V))
diff --git a/libs/libcurl/src/Makefile.in b/libs/libcurl/src/Makefile.in
index 3bc0887503..2bc6bc026b 100644
--- a/libs/libcurl/src/Makefile.in
+++ b/libs/libcurl/src/Makefile.in
@@ -897,11 +897,11 @@ 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_CA_EMBED = @CURL_CA_EMBED@
CURL_CFLAG_EXTRAS = @CURL_CFLAG_EXTRAS@
CURL_DISABLE_DICT = @CURL_DISABLE_DICT@
CURL_DISABLE_FILE = @CURL_DISABLE_FILE@
@@ -947,7 +947,6 @@ HAVE_BROTLI = @HAVE_BROTLI@
HAVE_GNUTLS_SRP = @HAVE_GNUTLS_SRP@
HAVE_LDAP_SSL = @HAVE_LDAP_SSL@
HAVE_LIBZ = @HAVE_LIBZ@
-HAVE_OPENSSL_QUIC = @HAVE_OPENSSL_QUIC@
HAVE_OPENSSL_SRP = @HAVE_OPENSSL_SRP@
HAVE_PROTO_BSDSOCKET_H = @HAVE_PROTO_BSDSOCKET_H@
HAVE_ZSTD = @HAVE_ZSTD@
@@ -963,8 +962,10 @@ IPV6_ENABLED = @IPV6_ENABLED@
LCOV = @LCOV@
LD = @LD@
LDFLAGS = @LDFLAGS@
-LIBCURL_LIBS = @LIBCURL_LIBS@
-LIBCURL_NO_SHARED = @LIBCURL_NO_SHARED@
+LIBCURL_PC_CFLAGS = @LIBCURL_PC_CFLAGS@
+LIBCURL_PC_CFLAGS_PRIVATE = @LIBCURL_PC_CFLAGS_PRIVATE@
+LIBCURL_PC_LIBS = @LIBCURL_PC_LIBS@
+LIBCURL_PC_LIBS_PRIVATE = @LIBCURL_PC_LIBS_PRIVATE@
LIBCURL_PC_REQUIRES = @LIBCURL_PC_REQUIRES@
LIBCURL_PC_REQUIRES_PRIVATE = @LIBCURL_PC_REQUIRES_PRIVATE@
LIBOBJS = @LIBOBJS@
@@ -999,10 +1000,8 @@ 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@
@@ -1013,6 +1012,7 @@ STRIP = @STRIP@
SUPPORT_FEATURES = @SUPPORT_FEATURES@
SUPPORT_PROTOCOLS = @SUPPORT_PROTOCOLS@
TEST_NGHTTPX = @TEST_NGHTTPX@
+USE_APPLE_IDN = @USE_APPLE_IDN@
USE_ARES = @USE_ARES@
USE_BEARSSL = @USE_BEARSSL@
USE_GNUTLS = @USE_GNUTLS@
@@ -1021,6 +1021,7 @@ USE_LIBPSL = @USE_LIBPSL@
USE_LIBRTMP = @USE_LIBRTMP@
USE_LIBSSH = @USE_LIBSSH@
USE_LIBSSH2 = @USE_LIBSSH2@
+USE_LIBUV = @USE_LIBUV@
USE_MBEDTLS = @USE_MBEDTLS@
USE_MSH3 = @USE_MSH3@
USE_NGHTTP2 = @USE_NGHTTP2@
@@ -1130,10 +1131,14 @@ top_srcdir = @top_srcdir@
###########################################################################
AUTOMAKE_OPTIONS = foreign nostdinc
CMAKE_DIST = CMakeLists.txt curl_config.h.cmake
+CHECKSRC_DIST = .checksrc vauth/.checksrc vquic/.checksrc vssh/.checksrc \
+ vtls/.checksrc
+
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.rc \
config-amigaos.h config-win32ce.h config-os400.h setup-os400.h \
- $(CMAKE_DIST) setup-win32.h .checksrc Makefile.soname
+ $(CMAKE_DIST) setup-win32.h Makefile.soname optiontable.pl libcurl.def \
+ $(CHECKSRC_DIST)
lib_LTLIBRARIES = libcurl.la
@BUILD_UNITTESTS_FALSE@noinst_LTLIBRARIES =
@@ -1524,10 +1529,10 @@ 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) $(CURL_LDFLAGS_LIB) $(LIBCURL_LIBS)
+libcurl_la_LDFLAGS = $(AM_LDFLAGS) $(libcurl_la_LDFLAGS_EXTRA) $(CURL_LDFLAGS_LIB) $(LIBCURL_PC_LIBS_PRIVATE)
libcurl_la_CFLAGS = $(AM_CFLAGS) $(libcurl_la_CFLAGS_EXTRA)
libcurlu_la_CPPFLAGS = $(AM_CPPFLAGS) -DCURL_STATICLIB -DUNITTESTS
-libcurlu_la_LDFLAGS = $(AM_LDFLAGS) -static $(LIBCURL_LIBS)
+libcurlu_la_LDFLAGS = $(AM_LDFLAGS) -static $(LIBCURL_PC_LIBS_PRIVATE)
libcurlu_la_CFLAGS = $(AM_CFLAGS)
CHECKSRC = $(CS_$(V))
CS_0 = @echo " RUN " $@;
diff --git a/libs/libcurl/src/altsvc.c b/libs/libcurl/src/altsvc.c
index f8d3516952..4dca09292f 100644
--- a/libs/libcurl/src/altsvc.c
+++ b/libs/libcurl/src/altsvc.c
@@ -337,13 +337,13 @@ CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl)
*/
void Curl_altsvc_cleanup(struct altsvcinfo **altsvcp)
{
- struct Curl_llist_element *e;
- struct Curl_llist_element *n;
if(*altsvcp) {
+ struct Curl_llist_node *e;
+ struct Curl_llist_node *n;
struct altsvcinfo *altsvc = *altsvcp;
- for(e = altsvc->list.head; e; e = n) {
- struct altsvc *as = e->ptr;
- n = e->next;
+ for(e = Curl_llist_head(&altsvc->list); e; e = n) {
+ struct altsvc *as = Curl_node_elem(e);
+ n = Curl_node_next(e);
altsvc_free(as);
}
free(altsvc->filename);
@@ -358,8 +358,6 @@ void Curl_altsvc_cleanup(struct altsvcinfo **altsvcp)
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;
@@ -378,12 +376,14 @@ CURLcode Curl_altsvc_save(struct Curl_easy *data,
result = Curl_fopen(data, file, &out, &tempstore);
if(!result) {
+ struct Curl_llist_node *e;
+ struct Curl_llist_node *n;
fputs("# Your alt-svc cache. https://curl.se/docs/alt-svc.html\n"
"# This file was generated by libcurl! Edit at your own risk.\n",
out);
- for(e = altsvc->list.head; e; e = n) {
- struct altsvc *as = e->ptr;
- n = e->next;
+ for(e = Curl_llist_head(&altsvc->list); e; e = n) {
+ struct altsvc *as = Curl_node_elem(e);
+ n = Curl_node_next(e);
result = altsvc_out(as, out);
if(result)
break;
@@ -440,15 +440,15 @@ static bool hostcompare(const char *host, const char *check)
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;
+ struct Curl_llist_node *e;
+ struct Curl_llist_node *n;
+ for(e = Curl_llist_head(&asi->list); e; e = n) {
+ struct altsvc *as = Curl_node_elem(e);
+ n = Curl_node_next(e);
if((srcalpnid == as->src.alpnid) &&
(srcport == as->src.port) &&
hostcompare(srchost, as->src.host)) {
- Curl_llist_remove(&asi->list, e, NULL);
+ Curl_node_remove(e);
altsvc_free(as);
}
}
@@ -677,19 +677,19 @@ bool Curl_altsvc_lookup(struct altsvcinfo *asi,
struct altsvc **dstentry,
const int versions) /* one or more bits */
{
- struct Curl_llist_element *e;
- struct Curl_llist_element *n;
+ struct Curl_llist_node *e;
+ struct Curl_llist_node *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;
+ for(e = Curl_llist_head(&asi->list); e; e = n) {
+ struct altsvc *as = Curl_node_elem(e);
+ n = Curl_node_next(e);
if(as->expires < now) {
/* an expired entry, remove */
- Curl_llist_remove(&asi->list, e, NULL);
+ Curl_node_remove(e);
altsvc_free(as);
continue;
}
diff --git a/libs/libcurl/src/altsvc.h b/libs/libcurl/src/altsvc.h
index c3d2e8da21..bf427bb84f 100644
--- a/libs/libcurl/src/altsvc.h
+++ b/libs/libcurl/src/altsvc.h
@@ -48,7 +48,7 @@ struct altsvc {
time_t expires;
bool persist;
unsigned int prio;
- struct Curl_llist_element node;
+ struct Curl_llist_node node;
};
struct altsvcinfo {
diff --git a/libs/libcurl/src/asyn-thread.c b/libs/libcurl/src/asyn-thread.c
index d6687fe8f8..40dbef3491 100644
--- a/libs/libcurl/src/asyn-thread.c
+++ b/libs/libcurl/src/asyn-thread.c
@@ -54,7 +54,6 @@
# define RESOLVER_ENOMEM ENOMEM
#endif
-#include "system_win32.h"
#include "urldata.h"
#include "sendf.h"
#include "hostip.h"
@@ -145,22 +144,9 @@ static bool init_resolve_thread(struct Curl_easy *data,
const char *hostname, int port,
const struct addrinfo *hints);
-#ifdef _WIN32
-/* Thread sync data used by GetAddrInfoExW for win8+ */
-struct thread_sync_data_w8
-{
- OVERLAPPED overlapped;
- ADDRINFOEXW_ *res;
- HANDLE cancel_ev;
- ADDRINFOEXW_ hints;
-};
-#endif
/* Data for synchronization between resolver thread and its parent */
struct thread_sync_data {
-#ifdef _WIN32
- struct thread_sync_data_w8 w8;
-#endif
curl_mutex_t *mtx;
int done;
int port;
@@ -179,9 +165,6 @@ struct thread_sync_data {
};
struct thread_data {
-#ifdef _WIN32
- HANDLE complete_ev;
-#endif
curl_thread_t thread_hnd;
unsigned int poll_interval;
timediff_t interval_end;
@@ -293,162 +276,6 @@ static CURLcode getaddrinfo_complete(struct Curl_easy *data)
return result;
}
-#ifdef _WIN32
-static VOID WINAPI
-query_complete(DWORD err, DWORD bytes, LPWSAOVERLAPPED overlapped)
-{
- size_t ss_size;
- const ADDRINFOEXW_ *ai;
- struct Curl_addrinfo *ca;
- struct Curl_addrinfo *cafirst = NULL;
- struct Curl_addrinfo *calast = NULL;
-#ifndef CURL_DISABLE_SOCKETPAIR
-#ifdef USE_EVENTFD
- const void *buf;
- const uint64_t val = 1;
-#else
- char buf[1];
-#endif
-#endif
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wcast-align"
-#endif
- struct thread_sync_data *tsd =
- CONTAINING_RECORD(overlapped, struct thread_sync_data, w8.overlapped);
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
- struct thread_data *td = tsd->td;
- const ADDRINFOEXW_ *res = tsd->w8.res;
- int error = (int)err;
- (void)bytes;
-
- if(error == ERROR_SUCCESS) {
- /* traverse the addrinfo list */
-
- for(ai = res; ai != NULL; ai = ai->ai_next) {
- size_t namelen = ai->ai_canonname ? wcslen(ai->ai_canonname) + 1 : 0;
- /* ignore elements with unsupported address family, */
- /* settle family-specific sockaddr structure size. */
- if(ai->ai_family == AF_INET)
- ss_size = sizeof(struct sockaddr_in);
-#ifdef USE_IPV6
- else if(ai->ai_family == AF_INET6)
- ss_size = sizeof(struct sockaddr_in6);
-#endif
- else
- continue;
-
- /* ignore elements without required address info */
- if(!ai->ai_addr || !(ai->ai_addrlen > 0))
- continue;
-
- /* ignore elements with bogus address size */
- if((size_t)ai->ai_addrlen < ss_size)
- continue;
-
- ca = malloc(sizeof(struct Curl_addrinfo) + ss_size + namelen);
- if(!ca) {
- error = EAI_MEMORY;
- break;
- }
-
- /* copy each structure member individually, member ordering, */
- /* size, or padding might be different for each platform. */
- ca->ai_flags = ai->ai_flags;
- ca->ai_family = ai->ai_family;
- ca->ai_socktype = ai->ai_socktype;
- ca->ai_protocol = ai->ai_protocol;
- ca->ai_addrlen = (curl_socklen_t)ss_size;
- ca->ai_addr = NULL;
- ca->ai_canonname = NULL;
- ca->ai_next = NULL;
-
- ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
- memcpy(ca->ai_addr, ai->ai_addr, ss_size);
-
- if(namelen) {
- size_t i;
- ca->ai_canonname = (void *)((char *)ca->ai_addr + ss_size);
- for(i = 0; i < namelen; ++i) /* convert wide string to ascii */
- ca->ai_canonname[i] = (char)ai->ai_canonname[i];
- ca->ai_canonname[namelen] = '\0';
- }
-
- /* 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, 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
- }
- tsd->res = cafirst;
- }
-
- if(tsd->w8.res) {
- Curl_FreeAddrInfoExW(tsd->w8.res);
- tsd->w8.res = NULL;
- }
-
- if(error) {
- tsd->sock_error = SOCKERRNO?SOCKERRNO:error;
- 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) {
-#ifdef USE_EVENTFD
- buf = &val;
-#else
- buf[0] = 1;
-#endif
- /* DNS has been resolved, signal client task */
- if(wakeup_write(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);
- if(td->complete_ev)
- SetEvent(td->complete_ev); /* Notify caller that the query completed */
- }
-}
-#endif
#ifdef HAVE_GETADDRINFO
@@ -585,26 +412,9 @@ static void destroy_async_data(struct Curl_async *async)
Curl_mutex_release(td->tsd.mtx);
if(!done) {
-#ifdef _WIN32
- if(td->complete_ev) {
- CloseHandle(td->complete_ev);
- td->complete_ev = NULL;
- }
-#endif
- if(td->thread_hnd != curl_thread_t_null) {
- Curl_thread_destroy(td->thread_hnd);
- td->thread_hnd = curl_thread_t_null;
- }
+ Curl_thread_destroy(td->thread_hnd);
}
else {
-#ifdef _WIN32
- if(td->complete_ev) {
- Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev);
- WaitForSingleObject(td->complete_ev, INFINITE);
- CloseHandle(td->complete_ev);
- td->complete_ev = NULL;
- }
-#endif
if(td->thread_hnd != curl_thread_t_null)
Curl_thread_join(&td->thread_hnd);
@@ -650,9 +460,6 @@ static bool init_resolve_thread(struct Curl_easy *data,
asp->status = 0;
asp->dns = NULL;
td->thread_hnd = curl_thread_t_null;
-#ifdef _WIN32
- td->complete_ev = NULL;
-#endif
if(!init_thread_sync_data(td, hostname, port, hints)) {
asp->tdata = NULL;
@@ -668,42 +475,6 @@ static bool init_resolve_thread(struct Curl_easy *data,
/* The thread will set this to 1 when complete. */
td->tsd.done = 0;
-#ifdef _WIN32
- if(Curl_isWindows8OrGreater && Curl_FreeAddrInfoExW &&
- Curl_GetAddrInfoExCancel && Curl_GetAddrInfoExW &&
- !Curl_win32_impersonating()) {
-#define MAX_NAME_LEN 256 /* max domain name is 253 chars */
-#define MAX_PORT_LEN 8
- WCHAR namebuf[MAX_NAME_LEN];
- WCHAR portbuf[MAX_PORT_LEN];
- /* calculate required length */
- int w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, hostname,
- -1, NULL, 0);
- if((w_len > 0) && (w_len < MAX_NAME_LEN)) {
- /* do utf8 conversion */
- w_len = MultiByteToWideChar(CP_UTF8, 0, hostname, -1, namebuf, w_len);
- if((w_len > 0) && (w_len < MAX_NAME_LEN)) {
- swprintf(portbuf, MAX_PORT_LEN, L"%d", port);
- td->tsd.w8.hints.ai_family = hints->ai_family;
- td->tsd.w8.hints.ai_socktype = hints->ai_socktype;
- td->complete_ev = CreateEvent(NULL, TRUE, FALSE, NULL);
- if(!td->complete_ev) {
- /* failed to start, mark it as done here for proper cleanup. */
- td->tsd.done = 1;
- goto err_exit;
- }
- err = Curl_GetAddrInfoExW(namebuf, portbuf, NS_DNS,
- NULL, &td->tsd.w8.hints, &td->tsd.w8.res,
- NULL, &td->tsd.w8.overlapped,
- &query_complete, &td->tsd.w8.cancel_ev);
- if(err != WSA_IO_PENDING)
- query_complete((DWORD)err, 0, &td->tsd.w8.overlapped);
- return TRUE;
- }
- }
- }
-#endif
-
#ifdef HAVE_GETADDRINFO
td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
#else
@@ -740,23 +511,9 @@ static CURLcode thread_wait_resolv(struct Curl_easy *data,
DEBUGASSERT(data);
td = data->state.async.tdata;
DEBUGASSERT(td);
-#ifdef _WIN32
- DEBUGASSERT(td->complete_ev || td->thread_hnd != curl_thread_t_null);
-#else
DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
-#endif
/* wait for the thread to resolve the name */
-#ifdef _WIN32
- if(td->complete_ev) {
- WaitForSingleObject(td->complete_ev, INFINITE);
- CloseHandle(td->complete_ev);
- td->complete_ev = NULL;
- if(entry)
- result = getaddrinfo_complete(data);
- }
- else
-#endif
if(Curl_thread_join(&td->thread_hnd)) {
if(entry)
result = getaddrinfo_complete(data);
@@ -793,13 +550,6 @@ void Curl_resolver_kill(struct Curl_easy *data)
/* If we are still resolving, we must wait for the threads to fully clean up,
unfortunately. Otherwise, we can simply cancel to clean up any resolver
data. */
-#ifdef _WIN32
- if(td && td->complete_ev) {
- Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev);
- (void)thread_wait_resolv(data, NULL, FALSE);
- }
- else
-#endif
if(td && td->thread_hnd != curl_thread_t_null
&& (data->set.quick_exit != 1L))
(void)thread_wait_resolv(data, NULL, FALSE);
diff --git a/libs/libcurl/src/bufq.c b/libs/libcurl/src/bufq.c
index 2bcad8a57f..5db25a3666 100644
--- a/libs/libcurl/src/bufq.c
+++ b/libs/libcurl/src/bufq.c
@@ -91,6 +91,23 @@ static size_t chunk_read(struct buf_chunk *chunk,
}
}
+static size_t chunk_unwrite(struct buf_chunk *chunk, size_t len)
+{
+ size_t n = chunk->w_offset - chunk->r_offset;
+ DEBUGASSERT(chunk->w_offset >= chunk->r_offset);
+ if(!n) {
+ return 0;
+ }
+ else if(n <= len) {
+ chunk->r_offset = chunk->w_offset = 0;
+ return n;
+ }
+ else {
+ chunk->w_offset -= len;
+ return len;
+ }
+}
+
static ssize_t chunk_slurpn(struct buf_chunk *chunk, size_t max_len,
Curl_bufq_reader *reader,
void *reader_ctx, CURLcode *err)
@@ -363,6 +380,49 @@ static void prune_head(struct bufq *q)
}
}
+static struct buf_chunk *chunk_prev(struct buf_chunk *head,
+ struct buf_chunk *chunk)
+{
+ while(head) {
+ if(head == chunk)
+ return NULL;
+ if(head->next == chunk)
+ return head;
+ head = head->next;
+ }
+ return NULL;
+}
+
+static void prune_tail(struct bufq *q)
+{
+ struct buf_chunk *chunk;
+
+ while(q->tail && chunk_is_empty(q->tail)) {
+ chunk = q->tail;
+ q->tail = chunk_prev(q->head, chunk);
+ if(q->tail)
+ q->tail->next = NULL;
+ if(q->head == chunk)
+ q->head = q->tail;
+ if(q->pool) {
+ bufcp_put(q->pool, chunk);
+ --q->chunk_count;
+ }
+ else if((q->chunk_count > q->max_chunks) ||
+ (q->opts & BUFQ_OPT_NO_SPARES)) {
+ /* SOFT_LIMIT allowed us more than max. free spares until
+ * we are at max again. Or free them if we are configured
+ * to not use spares. */
+ free(chunk);
+ --q->chunk_count;
+ }
+ else {
+ chunk->next = q->spare;
+ q->spare = chunk;
+ }
+ }
+}
+
static struct buf_chunk *get_non_full_tail(struct bufq *q)
{
struct buf_chunk *chunk;
@@ -428,6 +488,15 @@ CURLcode Curl_bufq_cwrite(struct bufq *q,
return result;
}
+CURLcode Curl_bufq_unwrite(struct bufq *q, size_t len)
+{
+ while(len && q->tail) {
+ len -= chunk_unwrite(q->head, len);
+ prune_tail(q);
+ }
+ return len? CURLE_AGAIN : CURLE_OK;
+}
+
ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len,
CURLcode *err)
{
diff --git a/libs/libcurl/src/bufq.h b/libs/libcurl/src/bufq.h
index dc803bd9dc..71e2de8d82 100644
--- a/libs/libcurl/src/bufq.h
+++ b/libs/libcurl/src/bufq.h
@@ -183,6 +183,12 @@ CURLcode Curl_bufq_cwrite(struct bufq *q,
size_t *pnwritten);
/**
+ * Remove `len` bytes from the end of the buffer queue again.
+ * Returns CURLE_AGAIN if less than `len` bytes were in the queue.
+ */
+CURLcode Curl_bufq_unwrite(struct bufq *q, size_t len);
+
+/**
* Read buf from the start of the buffer queue. The buf is copied
* and the amount of copied bytes is returned.
* A return code of -1 indicates an error, setting `err` to the
diff --git a/libs/libcurl/src/c-hyper.c b/libs/libcurl/src/c-hyper.c
index 9780019f66..3be9f2514c 100644
--- a/libs/libcurl/src/c-hyper.c
+++ b/libs/libcurl/src/c-hyper.c
@@ -119,7 +119,7 @@ size_t Curl_hyper_send(void *userp, hyper_context *ctx,
DEBUGF(infof(data, "Curl_hyper_send(%zu)", buflen));
result = Curl_conn_send(data, io_ctx->sockindex,
- (void *)buf, buflen, &nwrote);
+ (void *)buf, buflen, FALSE, &nwrote);
if(result == CURLE_AGAIN) {
DEBUGF(infof(data, "Curl_hyper_send(%zu) -> EAGAIN", buflen));
/* would block, register interest */
@@ -352,6 +352,8 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
(void)conn;
if(data->hyp.send_body_waker) {
+ /* If there is still something to upload, wake it to give it
+ * another try. */
hyper_waker_wake(data->hyp.send_body_waker);
data->hyp.send_body_waker = NULL;
}
@@ -367,7 +369,7 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
h->write_waker = NULL;
}
- do {
+ while(1) {
hyper_task_return_type t;
task = hyper_executor_poll(h->exec);
if(!task) {
@@ -391,22 +393,22 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
switch(code) {
case HYPERE_ABORTED_BY_CALLBACK:
result = CURLE_OK;
- break;
+ goto out;
case HYPERE_UNEXPECTED_EOF:
if(!data->req.bytecount)
result = CURLE_GOT_NOTHING;
else
result = CURLE_RECV_ERROR;
- break;
+ goto out;
case HYPERE_INVALID_PEER_MESSAGE:
/* bump headerbytecount to avoid the count remaining at zero and
appearing to not having read anything from the peer at all */
data->req.headerbytecount++;
result = CURLE_UNSUPPORTED_PROTOCOL; /* maybe */
- break;
+ goto out;
default:
result = CURLE_RECV_ERROR;
- break;
+ goto out;
}
}
data->req.done = TRUE;
@@ -416,7 +418,7 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
else if(t == HYPER_TASK_EMPTY) {
void *userdata = hyper_task_userdata(task);
hyper_task_free(task);
- if((userdata_t)userdata == USERDATA_RESP_BODY) {
+ if(userdata == (void *)USERDATA_RESP_BODY) {
/* end of transfer */
data->req.done = TRUE;
infof(data, "hyperstream is done");
@@ -428,103 +430,115 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
}
else {
/* A background task for hyper; ignore */
+ DEBUGF(infof(data, "hyper: some background task done"));
continue;
}
}
+ else if(t == HYPER_TASK_RESPONSE) {
+ resp = hyper_task_value(task);
+ hyper_task_free(task);
- DEBUGASSERT(HYPER_TASK_RESPONSE);
-
- resp = hyper_task_value(task);
- hyper_task_free(task);
-
- *didwhat = KEEP_RECV;
- if(!resp) {
- failf(data, "hyperstream: could not get response");
- return CURLE_RECV_ERROR;
- }
+ *didwhat = KEEP_RECV;
+ if(!resp) {
+ failf(data, "hyperstream: could not get response");
+ result = CURLE_RECV_ERROR;
+ goto out;
+ }
- 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);
+ 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 && Curl_http_exp100_is_selected(data)) {
- infof(data, "Got 417 while waiting for a 100");
- data->state.disableexpect = TRUE;
- data->req.newurl = strdup(data->state.url);
- Curl_req_abort_sending(data);
- }
+ if(http_status == 417 && Curl_http_exp100_is_selected(data)) {
+ infof(data, "Got 417 while waiting for a 100");
+ data->state.disableexpect = TRUE;
+ data->req.newurl = strdup(data->state.url);
+ Curl_req_abort_sending(data);
+ }
- result = status_line(data, conn,
- http_status, http_version, reasonp, reason_len);
- if(result)
- break;
+ result = status_line(data, conn,
+ http_status, http_version, reasonp, reason_len);
+ if(result)
+ goto out;
- headers = hyper_response_headers(resp);
- if(!headers) {
- failf(data, "hyperstream: could not get response headers");
- result = CURLE_RECV_ERROR;
- break;
- }
+ headers = hyper_response_headers(resp);
+ if(!headers) {
+ failf(data, "hyperstream: could not get response headers");
+ result = CURLE_RECV_ERROR;
+ goto out;
+ }
- /* the headers are already received */
- hyper_headers_foreach(headers, hyper_each_header, data);
- if(data->state.hresult) {
- result = data->state.hresult;
- break;
- }
+ /* the headers are already received */
+ hyper_headers_foreach(headers, hyper_each_header, data);
+ if(data->state.hresult) {
+ result = data->state.hresult;
+ goto out;
+ }
- result = empty_header(data);
- if(result)
- break;
+ result = empty_header(data);
+ if(result)
+ goto out;
- k->deductheadercount =
- (100 <= http_status && 199 >= http_status)?k->headerbytecount:0;
+ 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, NULL, 0);
- if(result)
- return result;
- }
- else {
- failf(data, "Expected 101, got %u", k->httpcode);
- result = CURLE_HTTP_RETURNED_ERROR;
- break;
+ if(k->upgr101 == UPGR101_WS) {
+ if(http_status == 101) {
+ /* verify the response */
+ result = Curl_ws_accept(data, NULL, 0);
+ if(result)
+ goto out;
+ }
+ else {
+ failf(data, "Expected 101, got %u", k->httpcode);
+ result = CURLE_HTTP_RETURNED_ERROR;
+ goto out;
+ }
}
- }
#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;
+ /* 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)
+ goto out;
- resp_body = hyper_response_body(resp);
- if(!resp_body) {
- failf(data, "hyperstream: could not 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;
+ resp_body = hyper_response_body(resp);
+ if(!resp_body) {
+ failf(data, "hyperstream: could not get response body");
+ result = CURLE_RECV_ERROR;
+ goto out;
+ }
+ foreach = hyper_body_foreach(resp_body, hyper_body_chunk, data);
+ if(!foreach) {
+ failf(data, "hyperstream: body foreach failed");
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ hyper_task_set_userdata(foreach, (void *)USERDATA_RESP_BODY);
+ if(HYPERE_OK != hyper_executor_push(h->exec, foreach)) {
+ failf(data, "Couldn't hyper_executor_push the body-foreach");
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ hyper_response_free(resp);
+ resp = NULL;
}
- hyper_task_set_userdata(foreach, (void *)USERDATA_RESP_BODY);
- 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;
+ else {
+ DEBUGF(infof(data, "hyper: unhandled tasktype %x", t));
}
+ } /* while(1) */
- hyper_response_free(resp);
- resp = NULL;
- } while(1);
+ if(!result && Curl_xfer_needs_flush(data)) {
+ DEBUGF(infof(data, "Curl_hyper_stream(), connection needs flush"));
+ result = Curl_xfer_flush(data);
+ }
+
+out:
+ DEBUGF(infof(data, "Curl_hyper_stream() -> %d", result));
if(resp)
hyper_response_free(resp);
return result;
@@ -671,10 +685,13 @@ static int uploadstreamed(void *userdata, hyper_context *ctx,
/* increasing the writebytecount here is a little premature but we
do not know exactly when the body is sent */
data->req.writebytecount += fillcount;
+ if(eos)
+ data->req.eos_read = TRUE;
Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
rc = HYPER_POLL_READY;
}
else if(eos) {
+ data->req.eos_read = TRUE;
*chunk = NULL;
rc = HYPER_POLL_READY;
}
@@ -686,9 +703,15 @@ static int uploadstreamed(void *userdata, hyper_context *ctx,
rc = HYPER_POLL_PENDING;
}
+ if(!data->req.upload_done && data->req.eos_read) {
+ DEBUGF(infof(data, "hyper: uploadstreamed(), upload is done"));
+ result = Curl_req_set_upload_done(data);
+ }
+
out:
Curl_multi_xfer_ulbuf_release(data, xfer_ulbuf);
data->state.hresult = result;
+ DEBUGF(infof(data, "hyper: uploadstreamed() -> %d", result));
return rc;
}
@@ -702,8 +725,9 @@ static CURLcode finalize_request(struct Curl_easy *data,
{
CURLcode result = CURLE_OK;
struct dynbuf req;
- if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD))
+ 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);
@@ -821,21 +845,21 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
*done = TRUE;
result = Curl_client_start(data);
if(result)
- return result;
+ goto out;
/* Add collecting of headers written to client. For a new connection,
* we might have done that already, but reuse
* or multiplex needs it here as well. */
result = Curl_headers_init(data);
if(result)
- return result;
+ goto out;
infof(data, "Time for the Hyper dance");
memset(h, 0, sizeof(struct hyptransfer));
result = Curl_http_host(data, conn);
if(result)
- return result;
+ goto out;
Curl_http_method(data, conn, &method, &httpreq);
@@ -846,33 +870,35 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
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;
+ if(!pq) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
}
result = Curl_http_output_auth(data, conn, method, httpreq,
(pq ? pq : data->state.up.path), FALSE);
free(pq);
if(result)
- return result;
+ goto out;
}
result = Curl_http_req_set_reader(data, httpreq, &te);
if(result)
- goto error;
+ goto out;
result = Curl_http_range(data, httpreq);
if(result)
- return result;
+ goto out;
result = Curl_http_useragent(data);
if(result)
- return result;
+ goto out;
io = hyper_io_new();
if(!io) {
failf(data, "Couldn't create hyper IO");
result = CURLE_OUT_OF_MEMORY;
- goto error;
+ goto out;
}
/* tell Hyper how to read/write network data */
h->io_ctx.data = data;
@@ -887,7 +913,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
if(!h->exec) {
failf(data, "Couldn't create hyper executor");
result = CURLE_OUT_OF_MEMORY;
- goto error;
+ goto out;
}
}
@@ -895,12 +921,12 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
if(!options) {
failf(data, "Couldn't create hyper client options");
result = CURLE_OUT_OF_MEMORY;
- goto error;
+ goto out;
}
if(conn->alpn == CURL_HTTP_VERSION_2) {
failf(data, "ALPN protocol h2 not supported with Hyper");
result = CURLE_UNSUPPORTED_PROTOCOL;
- goto error;
+ goto out;
}
hyper_clientconn_options_set_preserve_header_case(options, 1);
hyper_clientconn_options_set_preserve_header_order(options, 1);
@@ -913,7 +939,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
if(!handshake) {
failf(data, "Couldn't create hyper client handshake");
result = CURLE_OUT_OF_MEMORY;
- goto error;
+ goto out;
}
io = NULL;
options = NULL;
@@ -921,7 +947,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
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;
+ goto out;
}
handshake = NULL; /* ownership passed on */
@@ -929,7 +955,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
if(!task) {
failf(data, "Couldn't hyper_executor_poll the handshake");
result = CURLE_OUT_OF_MEMORY;
- goto error;
+ goto out;
}
client = hyper_task_value(task);
@@ -939,7 +965,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
if(!req) {
failf(data, "Couldn't hyper_request_new");
result = CURLE_OUT_OF_MEMORY;
- goto error;
+ goto out;
}
if(!Curl_use_http_1_1plus(data, conn)) {
@@ -947,57 +973,57 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
HYPER_HTTP_VERSION_1_0)) {
failf(data, "error setting HTTP version");
result = CURLE_OUT_OF_MEMORY;
- goto error;
+ goto out;
}
}
if(hyper_request_set_method(req, (uint8_t *)method, strlen(method))) {
failf(data, "error setting method");
result = CURLE_OUT_OF_MEMORY;
- goto error;
+ goto out;
}
result = request_target(data, conn, method, req);
if(result)
- goto error;
+ goto out;
headers = hyper_request_headers(req);
if(!headers) {
failf(data, "hyper_request_headers");
result = CURLE_OUT_OF_MEMORY;
- goto error;
+ goto out;
}
rc = hyper_request_on_informational(req, http1xx_cb, data);
if(rc) {
result = CURLE_OUT_OF_MEMORY;
- goto error;
+ goto out;
}
if(data->state.aptr.host) {
result = Curl_hyper_header(data, headers, data->state.aptr.host);
if(result)
- goto error;
+ goto out;
}
#ifndef CURL_DISABLE_PROXY
if(data->state.aptr.proxyuserpwd) {
result = Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd);
if(result)
- goto error;
+ goto out;
}
#endif
if(data->state.aptr.userpwd) {
result = Curl_hyper_header(data, headers, data->state.aptr.userpwd);
if(result)
- goto error;
+ goto out;
}
if((data->state.use_range && data->state.aptr.rangeline)) {
result = Curl_hyper_header(data, headers, data->state.aptr.rangeline);
if(result)
- goto error;
+ goto out;
}
if(data->set.str[STRING_USERAGENT] &&
@@ -1005,7 +1031,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
data->state.aptr.uagent) {
result = Curl_hyper_header(data, headers, data->state.aptr.uagent);
if(result)
- goto error;
+ goto out;
}
p_accept = Curl_checkheaders(data,
@@ -1013,12 +1039,12 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
if(p_accept) {
result = Curl_hyper_header(data, headers, p_accept);
if(result)
- goto error;
+ goto out;
}
if(te) {
result = Curl_hyper_header(data, headers, te);
if(result)
- goto error;
+ goto out;
}
#ifndef CURL_DISABLE_ALTSVC
@@ -1027,11 +1053,11 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
conn->conn_to_host.name, conn->conn_to_port);
if(!altused) {
result = CURLE_OUT_OF_MEMORY;
- goto error;
+ goto out;
}
result = Curl_hyper_header(data, headers, altused);
if(result)
- goto error;
+ goto out;
free(altused);
}
#endif
@@ -1042,7 +1068,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) {
result = Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive");
if(result)
- goto error;
+ goto out;
}
#endif
@@ -1054,17 +1080,17 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
else
result = Curl_hyper_header(data, headers, data->state.aptr.ref);
if(result)
- goto error;
+ goto out;
}
#ifdef HAVE_LIBZ
/* we only consider transfer-encoding magic if libz support is built-in */
result = Curl_transferencode(data);
if(result)
- goto error;
+ goto out;
result = Curl_hyper_header(data, headers, data->state.aptr.te);
if(result)
- goto error;
+ goto out;
#endif
if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
@@ -1078,29 +1104,29 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
result = Curl_hyper_header(data, headers,
data->state.aptr.accept_encoding);
if(result)
- goto error;
+ goto out;
}
else
Curl_safefree(data->state.aptr.accept_encoding);
result = cookies(data, conn, headers);
if(result)
- goto error;
+ goto out;
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;
+ goto out;
result = Curl_add_custom_headers(data, FALSE, headers);
if(result)
- goto error;
+ goto out;
result = finalize_request(data, headers, req, httpreq);
if(result)
- goto error;
+ goto out;
Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2);
@@ -1114,14 +1140,14 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
if(!sendtask) {
failf(data, "hyper_clientconn_send");
result = CURLE_OUT_OF_MEMORY;
- goto error;
+ goto out;
}
req = NULL;
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;
+ goto out;
}
sendtask = NULL; /* ownership passed on */
@@ -1131,6 +1157,9 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) {
/* HTTP GET/HEAD download */
Curl_pgrsSetUploadSize(data, 0); /* nothing */
+ result = Curl_req_set_upload_done(data);
+ if(result)
+ goto out;
}
Curl_xfer_setup1(data, CURL_XFER_SENDRECV, -1, TRUE);
@@ -1142,24 +1171,20 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
#ifndef CURL_DISABLE_PROXY
Curl_safefree(data->state.aptr.proxyuserpwd);
#endif
- 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);
-
- if(client)
- hyper_clientconn_free(client);
-
- if(req)
- hyper_request_free(req);
+out:
+ if(result) {
+ 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);
+ if(req)
+ hyper_request_free(req);
+ }
return result;
}
diff --git a/libs/libcurl/src/cf-h1-proxy.c b/libs/libcurl/src/cf-h1-proxy.c
index 5340495f5e..31cb59775c 100644
--- a/libs/libcurl/src/cf-h1-proxy.c
+++ b/libs/libcurl/src/cf-h1-proxy.c
@@ -266,7 +266,7 @@ static CURLcode send_CONNECT(struct Curl_cfilter *cf,
blen -= ts->nsent;
buf += ts->nsent;
- nwritten = cf->next->cft->do_send(cf->next, data, buf, blen, &result);
+ nwritten = cf->next->cft->do_send(cf->next, data, buf, blen, FALSE, &result);
if(nwritten < 0) {
if(result == CURLE_AGAIN) {
result = CURLE_OK;
@@ -489,8 +489,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
ts->keepon = KEEPON_IGNORE;
if(ts->cl) {
- infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T
- " bytes of response-body", ts->cl);
+ infof(data, "Ignore %" FMT_OFF_T " bytes of response-body", ts->cl);
}
else if(ts->chunked_encoding) {
infof(data, "Ignore chunked response-body");
diff --git a/libs/libcurl/src/cf-h2-proxy.c b/libs/libcurl/src/cf-h2-proxy.c
index 9d62352a8b..5ea1435533 100644
--- a/libs/libcurl/src/cf-h2-proxy.c
+++ b/libs/libcurl/src/cf-h2-proxy.c
@@ -73,7 +73,6 @@ struct tunnel_stream {
char *authority;
int32_t stream_id;
uint32_t error;
- size_t upload_blocked_len;
h2_tunnel_state state;
BIT(has_final_response);
BIT(closed);
@@ -217,11 +216,13 @@ static void drain_tunnel(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct tunnel_stream *tunnel)
{
+ struct cf_h2_proxy_ctx *ctx = cf->ctx;
unsigned char bits;
(void)cf;
bits = CURL_CSELECT_IN;
- if(!tunnel->closed && !tunnel->reset && tunnel->upload_blocked_len)
+ if(!tunnel->closed && !tunnel->reset &&
+ !Curl_bufq_is_empty(&ctx->tunnel.sendbuf))
bits |= CURL_CSELECT_OUT;
if(data->state.select_bits != bits) {
CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x",
@@ -260,7 +261,7 @@ static ssize_t proxy_h2_nw_out_writer(void *writer_ctx,
if(cf) {
struct Curl_easy *data = CF_DATA_CURRENT(cf);
nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen,
- err);
+ FALSE, err);
CURL_TRC_CF(data, cf, "[0] nw_out_writer(len=%zu) -> %zd, %d",
buflen, nwritten, *err);
}
@@ -1079,7 +1080,7 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf,
} while(ts->state == H2_TUNNEL_INIT);
out:
- if(result || ctx->tunnel.closed)
+ if((result && (result != CURLE_AGAIN)) || ctx->tunnel.closed)
h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data);
return result;
}
@@ -1185,7 +1186,8 @@ static CURLcode cf_h2_proxy_shutdown(struct Curl_cfilter *cf,
if(!ctx->sent_goaway) {
rv = nghttp2_submit_goaway(ctx->h2, NGHTTP2_FLAG_NONE,
0, 0,
- (const uint8_t *)"shutown", sizeof("shutown"));
+ (const uint8_t *)"shutdown",
+ sizeof("shutdown"));
if(rv) {
failf(data, "nghttp2_submit_goaway() failed: %s(%d)",
nghttp2_strerror(rv), rv);
@@ -1231,7 +1233,9 @@ static void cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf,
bool want_recv, want_send;
if(!cf->connected && ctx->h2) {
- want_send = nghttp2_session_want_write(ctx->h2);
+ want_send = nghttp2_session_want_write(ctx->h2) ||
+ !Curl_bufq_is_empty(&ctx->outbufq) ||
+ !Curl_bufq_is_empty(&ctx->tunnel.sendbuf);
want_recv = nghttp2_session_want_read(ctx->h2);
}
else
@@ -1247,17 +1251,25 @@ static void cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf,
ctx->h2, ctx->tunnel.stream_id);
want_recv = (want_recv || c_exhaust || s_exhaust);
want_send = (!s_exhaust && want_send) ||
- (!c_exhaust && nghttp2_session_want_write(ctx->h2));
+ (!c_exhaust && nghttp2_session_want_write(ctx->h2)) ||
+ !Curl_bufq_is_empty(&ctx->outbufq) ||
+ !Curl_bufq_is_empty(&ctx->tunnel.sendbuf);
Curl_pollset_set(data, ps, sock, want_recv, want_send);
+ CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d",
+ want_recv, want_send);
CF_DATA_RESTORE(cf, save);
}
else if(ctx->sent_goaway && !cf->shutdown) {
/* shutdown in progress */
CF_DATA_SAVE(save, cf, data);
- want_send = nghttp2_session_want_write(ctx->h2);
+ want_send = nghttp2_session_want_write(ctx->h2) ||
+ !Curl_bufq_is_empty(&ctx->outbufq) ||
+ !Curl_bufq_is_empty(&ctx->tunnel.sendbuf);
want_recv = nghttp2_session_want_read(ctx->h2);
Curl_pollset_set(data, ps, sock, want_recv, want_send);
+ CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d",
+ want_recv, want_send);
CF_DATA_RESTORE(cf, save);
}
}
@@ -1364,16 +1376,7 @@ static ssize_t cf_h2_proxy_recv(struct Curl_cfilter *cf,
}
result = proxy_h2_progress_egress(cf, data);
- if(result == CURLE_AGAIN) {
- /* pending data to send, need to be called again. Ideally, we would
- * monitor the socket for POLLOUT, but we might not be in SENDING
- * transfer state any longer and are unable to make this happen.
- */
- CURL_TRC_CF(data, cf, "[%d] egress blocked, DRAIN",
- ctx->tunnel.stream_id);
- drain_tunnel(cf, data, &ctx->tunnel);
- }
- else if(result) {
+ if(result && (result != CURLE_AGAIN)) {
*err = result;
nread = -1;
}
@@ -1393,15 +1396,16 @@ out:
static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf,
struct Curl_easy *data,
- const void *buf, size_t len, CURLcode *err)
+ const void *buf, size_t len, bool eos,
+ CURLcode *err)
{
struct cf_h2_proxy_ctx *ctx = cf->ctx;
struct cf_call_data save;
int rv;
ssize_t nwritten;
CURLcode result;
- int blocked = 0;
+ (void)eos; /* TODO, maybe useful for blocks? */
if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) {
*err = CURLE_SEND_ERROR;
return -1;
@@ -1413,29 +1417,10 @@ static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf,
*err = CURLE_SEND_ERROR;
goto out;
}
- else if(ctx->tunnel.upload_blocked_len) {
- /* the data in `buf` has already been submitted or added to the
- * buffers, but have been EAGAINed on the last invocation. */
- DEBUGASSERT(len >= ctx->tunnel.upload_blocked_len);
- if(len < ctx->tunnel.upload_blocked_len) {
- /* Did we get called again with a smaller `len`? This should not
- * happen. We are not prepared to handle that. */
- failf(data, "HTTP/2 proxy, send again with decreased length");
- *err = CURLE_HTTP2;
- nwritten = -1;
- goto out;
- }
- nwritten = (ssize_t)ctx->tunnel.upload_blocked_len;
- ctx->tunnel.upload_blocked_len = 0;
- *err = CURLE_OK;
- }
else {
nwritten = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, err);
- if(nwritten < 0) {
- if(*err != CURLE_AGAIN)
- goto out;
- nwritten = 0;
- }
+ if(nwritten < 0 && (*err != CURLE_AGAIN))
+ goto out;
}
if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) {
@@ -1458,52 +1443,13 @@ static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf,
/* Call the nghttp2 send loop and flush to write ALL buffered data,
* headers and/or request body completely out to the network */
result = proxy_h2_progress_egress(cf, data);
- if(result == CURLE_AGAIN) {
- blocked = 1;
- }
- else if(result) {
+ if(result && (result != CURLE_AGAIN)) {
*err = result;
nwritten = -1;
goto out;
}
- else if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) {
- /* although we wrote everything that nghttp2 wants to send now,
- * there is data left in our stream send buffer unwritten. This may
- * be due to the stream's HTTP/2 flow window being exhausted. */
- blocked = 1;
- }
-
- if(blocked) {
- /* Unable to send all data, due to connection blocked or H2 window
- * exhaustion. Data is left in our stream buffer, or nghttp2's internal
- * frame buffer or our network out buffer. */
- size_t rwin = (size_t)nghttp2_session_get_stream_remote_window_size(
- ctx->h2, ctx->tunnel.stream_id);
- if(rwin == 0) {
- /* H2 flow window exhaustion.
- * FIXME: there is no way to HOLD all transfers that use this
- * proxy connection AND to UNHOLD all of them again when the
- * window increases.
- * We *could* iterate over all data on this conn maybe? */
- CURL_TRC_CF(data, cf, "[%d] remote flow "
- "window is exhausted", ctx->tunnel.stream_id);
- }
- /* Whatever the cause, we need to return CURL_EAGAIN for this call.
- * We have unwritten state that needs us being invoked again and EAGAIN
- * is the only way to ensure that. */
- ctx->tunnel.upload_blocked_len = nwritten;
- CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) BLOCK: win %u/%zu "
- "blocked_len=%zu",
- ctx->tunnel.stream_id, len,
- nghttp2_session_get_remote_window_size(ctx->h2), rwin,
- nwritten);
- drain_tunnel(cf, data, &ctx->tunnel);
- *err = CURLE_AGAIN;
- nwritten = -1;
- goto out;
- }
- else if(proxy_h2_should_close_session(ctx)) {
+ if(proxy_h2_should_close_session(ctx)) {
/* nghttp2 thinks this session is done. If the stream has not been
* closed, this is an error state for out transfer */
if(ctx->tunnel.closed) {
@@ -1536,6 +1482,38 @@ out:
return nwritten;
}
+static CURLcode cf_h2_proxy_flush(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_h2_proxy_ctx *ctx = cf->ctx;
+ struct cf_call_data save;
+ CURLcode result = CURLE_OK;
+
+ CF_DATA_SAVE(save, cf, data);
+ if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) {
+ /* resume the potentially suspended tunnel */
+ int rv = nghttp2_session_resume_data(ctx->h2, ctx->tunnel.stream_id);
+ if(nghttp2_is_fatal(rv)) {
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
+ }
+
+ result = proxy_h2_progress_egress(cf, data);
+
+out:
+ CURL_TRC_CF(data, cf, "[%d] flush -> %d, "
+ "h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)",
+ ctx->tunnel.stream_id, result,
+ nghttp2_session_get_stream_remote_window_size(
+ ctx->h2, ctx->tunnel.stream_id),
+ nghttp2_session_get_remote_window_size(ctx->h2),
+ Curl_bufq_len(&ctx->tunnel.sendbuf),
+ Curl_bufq_len(&ctx->outbufq));
+ CF_DATA_RESTORE(cf, save);
+ return result;
+}
+
static bool proxy_h2_connisalive(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *input_pending)
@@ -1589,6 +1567,52 @@ static bool cf_h2_proxy_is_alive(struct Curl_cfilter *cf,
return result;
}
+static CURLcode cf_h2_proxy_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2)
+{
+ struct cf_h2_proxy_ctx *ctx = cf->ctx;
+
+ switch(query) {
+ case CF_QUERY_NEED_FLUSH: {
+ if(!Curl_bufq_is_empty(&ctx->outbufq) ||
+ !Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) {
+ CURL_TRC_CF(data, cf, "needs flush");
+ *pres1 = TRUE;
+ return CURLE_OK;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return cf->next?
+ cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+ CURLE_UNKNOWN_OPTION;
+}
+
+static CURLcode cf_h2_proxy_cntrl(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2)
+{
+ CURLcode result = CURLE_OK;
+ struct cf_call_data save;
+
+ (void)arg1;
+ (void)arg2;
+
+ switch(event) {
+ case CF_CTRL_FLUSH:
+ CF_DATA_SAVE(save, cf, data);
+ result = cf_h2_proxy_flush(cf, data);
+ CF_DATA_RESTORE(cf, save);
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
struct Curl_cftype Curl_cft_h2_proxy = {
"H2-PROXY",
CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
@@ -1602,10 +1626,10 @@ struct Curl_cftype Curl_cft_h2_proxy = {
cf_h2_proxy_data_pending,
cf_h2_proxy_send,
cf_h2_proxy_recv,
- Curl_cf_def_cntrl,
+ cf_h2_proxy_cntrl,
cf_h2_proxy_is_alive,
Curl_cf_def_conn_keep_alive,
- Curl_cf_def_query,
+ cf_h2_proxy_query,
};
CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf,
diff --git a/libs/libcurl/src/cf-haproxy.c b/libs/libcurl/src/cf-haproxy.c
index d7c684b0fe..0089cc1600 100644
--- a/libs/libcurl/src/cf-haproxy.c
+++ b/libs/libcurl/src/cf-haproxy.c
@@ -70,8 +70,9 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
{
struct cf_haproxy_ctx *ctx = cf->ctx;
CURLcode result;
- const char *tcp_version;
const char *client_ip;
+ struct ip_quadruple ipquad;
+ int is_ipv6;
DEBUGASSERT(ctx);
DEBUGASSERT(ctx->state == HAPROXY_INIT);
@@ -81,19 +82,20 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
result = Curl_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n"));
else {
#endif /* USE_UNIX_SOCKETS */
+ result = Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad);
+ if(result)
+ return result;
+
/* Emit the correct prefix for IPv6 */
- tcp_version = cf->conn->bits.ipv6 ? "TCP6" : "TCP4";
if(data->set.str[STRING_HAPROXY_CLIENT_IP])
client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP];
else
- client_ip = data->info.primary.local_ip;
+ client_ip = ipquad.local_ip;
result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
- tcp_version,
- client_ip,
- data->info.primary.remote_ip,
- data->info.primary.local_port,
- data->info.primary.remote_port);
+ is_ipv6? "TCP6" : "TCP4",
+ client_ip, ipquad.remote_ip,
+ ipquad.local_port, ipquad.remote_port);
#ifdef USE_UNIX_SOCKETS
}
@@ -129,17 +131,17 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
case HAPROXY_SEND:
len = Curl_dyn_len(&ctx->data_out);
if(len > 0) {
- size_t written;
- result = Curl_conn_send(data, cf->sockindex,
- Curl_dyn_ptr(&ctx->data_out),
- len, &written);
- if(result == CURLE_AGAIN) {
+ ssize_t nwritten;
+ nwritten = Curl_conn_cf_send(cf->next, data,
+ Curl_dyn_ptr(&ctx->data_out), len, FALSE,
+ &result);
+ if(nwritten < 0) {
+ if(result != CURLE_AGAIN)
+ goto out;
result = CURLE_OK;
- written = 0;
+ nwritten = 0;
}
- else if(result)
- goto out;
- Curl_dyn_tail(&ctx->data_out, len - written);
+ Curl_dyn_tail(&ctx->data_out, len - (size_t)nwritten);
if(Curl_dyn_len(&ctx->data_out) > 0) {
result = CURLE_OK;
goto out;
diff --git a/libs/libcurl/src/cf-https-connect.c b/libs/libcurl/src/cf-https-connect.c
index ae796d3a72..2597c0eee5 100644
--- a/libs/libcurl/src/cf-https-connect.c
+++ b/libs/libcurl/src/cf-https-connect.c
@@ -96,6 +96,21 @@ static bool cf_hc_baller_data_pending(struct cf_hc_baller *b,
return b->cf && !b->result && b->cf->cft->has_data_pending(b->cf, data);
}
+static bool cf_hc_baller_needs_flush(struct cf_hc_baller *b,
+ struct Curl_easy *data)
+{
+ return b->cf && !b->result && Curl_conn_cf_needs_flush(b->cf, data);
+}
+
+static CURLcode cf_hc_baller_cntrl(struct cf_hc_baller *b,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2)
+{
+ if(b->cf && !b->result)
+ return Curl_conn_cf_cntrl(b->cf, data, FALSE, event, arg1, arg2);
+ return CURLE_OK;
+}
+
struct cf_hc_ctx {
cf_hc_state state;
const struct Curl_dns_entry *remotehost;
@@ -195,8 +210,6 @@ static CURLcode baller_connected(struct Curl_cfilter *cf,
}
ctx->state = CF_HC_SUCCESS;
cf->connected = TRUE;
- Curl_conn_cf_cntrl(cf->next, data, TRUE,
- CF_CTRL_CONN_INFO_UPDATE, 0, NULL);
return result;
}
@@ -428,6 +441,8 @@ static CURLcode cf_hc_query(struct Curl_cfilter *cf,
struct Curl_easy *data,
int query, int *pres1, void *pres2)
{
+ struct cf_hc_ctx *ctx = cf->ctx;
+
if(!cf->connected) {
switch(query) {
case CF_QUERY_TIMER_CONNECT: {
@@ -440,6 +455,14 @@ static CURLcode cf_hc_query(struct Curl_cfilter *cf,
*when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT);
return CURLE_OK;
}
+ case CF_QUERY_NEED_FLUSH: {
+ if(cf_hc_baller_needs_flush(&ctx->h3_baller, data)
+ || cf_hc_baller_needs_flush(&ctx->h21_baller, data)) {
+ *pres1 = TRUE;
+ return CURLE_OK;
+ }
+ break;
+ }
default:
break;
}
@@ -449,6 +472,23 @@ static CURLcode cf_hc_query(struct Curl_cfilter *cf,
CURLE_UNKNOWN_OPTION;
}
+static CURLcode cf_hc_cntrl(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2)
+{
+ struct cf_hc_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ if(!cf->connected) {
+ result = cf_hc_baller_cntrl(&ctx->h3_baller, data, event, arg1, arg2);
+ if(!result || (result == CURLE_AGAIN))
+ result = cf_hc_baller_cntrl(&ctx->h21_baller, data, event, arg1, arg2);
+ if(result == CURLE_AGAIN)
+ result = CURLE_OK;
+ }
+ return result;
+}
+
static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
CURL_TRC_CF(data, cf, "close");
@@ -484,7 +524,7 @@ struct Curl_cftype Curl_cft_http_connect = {
cf_hc_data_pending,
Curl_cf_def_send,
Curl_cf_def_recv,
- Curl_cf_def_cntrl,
+ cf_hc_cntrl,
Curl_cf_def_conn_is_alive,
Curl_cf_def_conn_keep_alive,
cf_hc_query,
diff --git a/libs/libcurl/src/cf-socket.c b/libs/libcurl/src/cf-socket.c
index bd3acb7e8b..50b5b51865 100644
--- a/libs/libcurl/src/cf-socket.c
+++ b/libs/libcurl/src/cf-socket.c
@@ -124,7 +124,7 @@ static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd)
}
#ifdef SO_NOSIGPIPE
-/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
+/* The preferred method on macOS (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? */
@@ -146,7 +146,13 @@ static void nosigpipe(struct Curl_easy *data,
#define nosigpipe(x,y) Curl_nop_stmt
#endif
-#if defined(USE_WINSOCK) || \
+#if defined(USE_WINSOCK) && \
+ defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) && defined(TCP_KEEPCNT)
+/* Win 10, v 1709 (10.0.16299) and later can use SetSockOpt TCP_KEEP____
+ * so should use seconds */
+#define CURL_WINSOCK_KEEP_SSO
+#define KEEPALIVE_FACTOR(x)
+#elif defined(USE_WINSOCK) || \
(defined(__sun) && !defined(TCP_KEEPIDLE)) || \
(defined(__DragonFly__) && __DragonFly_version < 500702) || \
(defined(_WIN32) && !defined(TCP_KEEPIDLE))
@@ -177,19 +183,19 @@ tcpkeepalive(struct Curl_easy *data,
if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
(void *)&optval, sizeof(optval)) < 0) {
infof(data, "Failed to set SO_KEEPALIVE on fd "
- "%" CURL_FORMAT_SOCKET_T ": errno %d",
+ "%" FMT_SOCKET_T ": errno %d",
sockfd, SOCKERRNO);
}
else {
#if defined(SIO_KEEPALIVE_VALS) /* Windows */
/* Windows 10, version 1709 (10.0.16299) and later versions */
-#if defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) && defined(TCP_KEEPCNT)
+#if defined(CURL_WINSOCK_KEEP_SSO)
optval = curlx_sltosi(data->set.tcp_keepidle);
KEEPALIVE_FACTOR(optval);
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
(const char *)&optval, sizeof(optval)) < 0) {
infof(data, "Failed to set TCP_KEEPIDLE on fd "
- "%" CURL_FORMAT_SOCKET_T ": errno %d",
+ "%" FMT_SOCKET_T ": errno %d",
sockfd, SOCKERRNO);
}
optval = curlx_sltosi(data->set.tcp_keepintvl);
@@ -197,14 +203,14 @@ tcpkeepalive(struct Curl_easy *data,
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
(const char *)&optval, sizeof(optval)) < 0) {
infof(data, "Failed to set TCP_KEEPINTVL on fd "
- "%" CURL_FORMAT_SOCKET_T ": errno %d",
+ "%" FMT_SOCKET_T ": errno %d",
sockfd, SOCKERRNO);
}
optval = curlx_sltosi(data->set.tcp_keepcnt);
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT,
(const char *)&optval, sizeof(optval)) < 0) {
infof(data, "Failed to set TCP_KEEPCNT on fd "
- "%" CURL_FORMAT_SOCKET_T ": errno %d",
+ "%" FMT_SOCKET_T ": errno %d",
sockfd, SOCKERRNO);
}
#else /* Windows < 10.0.16299 */
@@ -220,8 +226,7 @@ tcpkeepalive(struct Curl_easy *data,
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 "
- "%" CURL_FORMAT_SOCKET_T ": errno %d",
- sockfd, SOCKERRNO);
+ "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
}
#endif
#else /* !Windows */
@@ -231,17 +236,17 @@ tcpkeepalive(struct Curl_easy *data,
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
(void *)&optval, sizeof(optval)) < 0) {
infof(data, "Failed to set TCP_KEEPIDLE on fd "
- "%" CURL_FORMAT_SOCKET_T ": errno %d",
+ "%" FMT_SOCKET_T ": errno %d",
sockfd, SOCKERRNO);
}
#elif defined(TCP_KEEPALIVE)
- /* Mac OS X style */
+ /* macOS 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 "
- "%" CURL_FORMAT_SOCKET_T ": errno %d",
+ "%" FMT_SOCKET_T ": errno %d",
sockfd, SOCKERRNO);
}
#elif defined(TCP_KEEPALIVE_THRESHOLD)
@@ -251,7 +256,7 @@ tcpkeepalive(struct Curl_easy *data,
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD,
(void *)&optval, sizeof(optval)) < 0) {
infof(data, "Failed to set TCP_KEEPALIVE_THRESHOLD on fd "
- "%" CURL_FORMAT_SOCKET_T ": errno %d",
+ "%" FMT_SOCKET_T ": errno %d",
sockfd, SOCKERRNO);
}
#endif
@@ -261,7 +266,7 @@ tcpkeepalive(struct Curl_easy *data,
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
(void *)&optval, sizeof(optval)) < 0) {
infof(data, "Failed to set TCP_KEEPINTVL on fd "
- "%" CURL_FORMAT_SOCKET_T ": errno %d",
+ "%" FMT_SOCKET_T ": errno %d",
sockfd, SOCKERRNO);
}
#elif defined(TCP_KEEPALIVE_ABORT_THRESHOLD)
@@ -282,8 +287,7 @@ tcpkeepalive(struct Curl_easy *data,
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD,
(void *)&optval, sizeof(optval)) < 0) {
infof(data, "Failed to set TCP_KEEPALIVE_ABORT_THRESHOLD on fd "
- "%" CURL_FORMAT_SOCKET_T ": errno %d",
- sockfd, SOCKERRNO);
+ "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
}
#endif
#ifdef TCP_KEEPCNT
@@ -291,8 +295,7 @@ tcpkeepalive(struct Curl_easy *data,
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT,
(void *)&optval, sizeof(optval)) < 0) {
infof(data, "Failed to set TCP_KEEPCNT on fd "
- "%" CURL_FORMAT_SOCKET_T ": errno %d",
- sockfd, SOCKERRNO);
+ "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
}
#endif
#endif
@@ -404,6 +407,9 @@ CURLcode Curl_socket_open(struct Curl_easy *data,
static int socket_close(struct Curl_easy *data, struct connectdata *conn,
int use_callback, curl_socket_t sock)
{
+ if(CURL_SOCKET_BAD == sock)
+ return 0;
+
if(use_callback && conn && conn->fclosesocket) {
int rc;
Curl_multi_closed(data, sock);
@@ -502,32 +508,37 @@ void Curl_sndbuf_init(curl_socket_t sockfd)
*
* Returns CURLE_OK on success.
*/
-CURLcode Curl_parse_interface(const char *input, size_t len,
+CURLcode Curl_parse_interface(const char *input,
char **dev, char **iface, char **host)
{
static const char if_prefix[] = "if!";
static const char host_prefix[] = "host!";
static const char if_host_prefix[] = "ifhost!";
+ size_t len;
DEBUGASSERT(dev);
DEBUGASSERT(iface);
DEBUGASSERT(host);
- if(strncmp(if_prefix, input, strlen(if_prefix)) == 0) {
+ len = strlen(input);
+ if(len > 512)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ if(!strncmp(if_prefix, input, strlen(if_prefix))) {
input += strlen(if_prefix);
if(!*input)
return CURLE_BAD_FUNCTION_ARGUMENT;
*iface = Curl_memdup0(input, len - strlen(if_prefix));
return *iface ? CURLE_OK : CURLE_OUT_OF_MEMORY;
}
- if(strncmp(host_prefix, input, strlen(host_prefix)) == 0) {
+ else if(!strncmp(host_prefix, input, strlen(host_prefix))) {
input += strlen(host_prefix);
if(!*input)
return CURLE_BAD_FUNCTION_ARGUMENT;
*host = Curl_memdup0(input, len - strlen(host_prefix));
return *host ? CURLE_OK : CURLE_OUT_OF_MEMORY;
}
- if(strncmp(if_host_prefix, input, strlen(if_host_prefix)) == 0) {
+ else if(!strncmp(if_host_prefix, input, strlen(if_host_prefix))) {
const char *host_part;
input += strlen(if_host_prefix);
len -= strlen(if_host_prefix);
@@ -679,12 +690,13 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
conn->ip_version = ipver;
if(h) {
+ int h_af = h->addr->ai_family;
/* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
Curl_printable_address(h->addr, myhost, sizeof(myhost));
infof(data, "Name '%s' family %i resolved to '%s' family %i",
- host, af, myhost, h->addr->ai_family);
- Curl_resolv_unlock(data, h);
- if(af != h->addr->ai_family) {
+ host, af, myhost, h_af);
+ Curl_resolv_unlink(data, &h); /* this will NULL, potential free h */
+ if(af != h_af) {
/* bad IP version combo, signal the caller to try another address
family if available */
return CURLE_UNSUPPORTED_PROTOCOL;
@@ -694,7 +706,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
else {
/*
* provided dev was no interface (or interfaces are not supported
- * e.g. solaris) no ip address and no domain we fail here
+ * e.g. Solaris) no ip address and no domain we fail here
*/
done = -1;
}
@@ -843,7 +855,7 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
err = SOCKERRNO;
#ifdef _WIN32_WCE
- /* Old WinCE versions do not support SO_ERROR */
+ /* Old Windows CE versions do not support SO_ERROR */
if(WSAENOPROTOOPT == err) {
SET_SOCKERRNO(0);
err = 0;
@@ -938,6 +950,7 @@ struct cf_socket_ctx {
size_t recv_max; /* max enforced read size */
#endif
BIT(got_first_byte); /* if first byte was received */
+ BIT(listening); /* socket is listening */
BIT(accepted); /* socket was accepted, not connected */
BIT(sock_connected); /* socket is "connected", e.g. in UDP */
BIT(active);
@@ -986,8 +999,7 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
struct cf_socket_ctx *ctx = cf->ctx;
if(ctx && CURL_SOCKET_BAD != ctx->sock) {
- CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
- ")", ctx->sock);
+ CURL_TRC_CF(data, cf, "cf_socket_close(%" FMT_SOCKET_T ")", ctx->sock);
if(ctx->sock == cf->conn->sock[cf->sockindex])
cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
@@ -1009,8 +1021,7 @@ static CURLcode cf_socket_shutdown(struct Curl_cfilter *cf,
if(cf->connected) {
struct cf_socket_ctx *ctx = cf->ctx;
- CURL_TRC_CF(data, cf, "cf_socket_shutdown(%" CURL_FORMAT_SOCKET_T
- ")", ctx->sock);
+ CURL_TRC_CF(data, cf, "cf_socket_shutdown(%" FMT_SOCKET_T ")", ctx->sock);
/* On TCP, and when the socket looks well and non-blocking mode
* can be enabled, receive dangling bytes before close to avoid
* entering RST states unnecessarily. */
@@ -1220,7 +1231,7 @@ out:
ctx->connected_at = Curl_now();
cf->connected = TRUE;
}
- CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" CURL_FORMAT_SOCKET_T,
+ CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" FMT_SOCKET_T,
result, ctx->sock);
return result;
}
@@ -1262,8 +1273,8 @@ static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data,
#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
if(setsockopt(ctx->sock, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
(void *)&optval, sizeof(optval)) < 0)
- infof(data, "Failed to enable TCP Fast Open on fd %"
- CURL_FORMAT_SOCKET_T, ctx->sock);
+ infof(data, "Failed to enable TCP Fast Open on fd %" FMT_SOCKET_T,
+ ctx->sock);
rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
#elif defined(MSG_FASTOPEN) /* old Linux */
@@ -1398,15 +1409,24 @@ static void cf_socket_adjust_pollset(struct Curl_cfilter *cf,
struct cf_socket_ctx *ctx = cf->ctx;
if(ctx->sock != CURL_SOCKET_BAD) {
- if(!cf->connected) {
+ /* A listening socket filter needs to be connected before the accept
+ * for some weird FTP interaction. This should be rewritten, so that
+ * FTP no longer does the socket checks and accept calls and delegates
+ * all that to the filter. TODO. */
+ if(ctx->listening) {
+ Curl_pollset_set_in_only(data, ps, ctx->sock);
+ CURL_TRC_CF(data, cf, "adjust_pollset, listening, POLLIN fd=%"
+ FMT_SOCKET_T, ctx->sock);
+ }
+ else if(!cf->connected) {
Curl_pollset_set_out_only(data, ps, ctx->sock);
CURL_TRC_CF(data, cf, "adjust_pollset, !connected, POLLOUT fd=%"
- CURL_FORMAT_SOCKET_T, ctx->sock);
+ FMT_SOCKET_T, ctx->sock);
}
else if(!ctx->active) {
Curl_pollset_add_in(data, ps, ctx->sock);
CURL_TRC_CF(data, cf, "adjust_pollset, !active, POLLIN fd=%"
- CURL_FORMAT_SOCKET_T, ctx->sock);
+ FMT_SOCKET_T, ctx->sock);
}
}
}
@@ -1449,13 +1469,15 @@ static void win_update_sndbuf_size(struct cf_socket_ctx *ctx)
#endif /* USE_WINSOCK */
static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
- const void *buf, size_t len, CURLcode *err)
+ const void *buf, size_t len, bool eos,
+ CURLcode *err)
{
struct cf_socket_ctx *ctx = cf->ctx;
curl_socket_t fdsave;
ssize_t nwritten;
size_t orig_len = len;
+ (void)eos; /* unused */
*err = CURLE_OK;
fdsave = cf->conn->sock[cf->sockindex];
cf->conn->sock[cf->sockindex] = ctx->sock;
@@ -1464,7 +1486,7 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
/* simulate network blocking/partial writes */
if(ctx->wblock_percent > 0) {
unsigned char c = 0;
- Curl_rand(data, &c, 1);
+ Curl_rand_bytes(data, FALSE, &c, 1);
if(c >= ((100-ctx->wblock_percent)*256/100)) {
CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE EWOULDBLOCK", orig_len);
*err = CURLE_AGAIN;
@@ -1597,6 +1619,18 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
return nread;
}
+static void cf_socket_update_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ /* Update the IP info held in the transfer, if we have that. */
+ if(cf->connected && (cf->sockindex == FIRSTSOCKET)) {
+ struct cf_socket_ctx *ctx = cf->ctx;
+ data->info.primary = ctx->ip;
+ /* not sure if this is redundant... */
+ data->info.conn_remote_port = cf->conn->remote_port;
+ }
+}
+
static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_socket_ctx *ctx = cf->ctx;
@@ -1604,17 +1638,15 @@ static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
/* use this socket from now on */
cf->conn->sock[cf->sockindex] = ctx->sock;
set_local_ip(cf, data);
- if(cf->sockindex == SECONDARYSOCKET)
- cf->conn->secondary = ctx->ip;
- else
- cf->conn->primary = ctx->ip;
- /* the first socket info gets some specials */
if(cf->sockindex == FIRSTSOCKET) {
+ cf->conn->primary = ctx->ip;
cf->conn->remote_addr = &ctx->addr;
#ifdef USE_IPV6
cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
#endif
- Curl_persistconninfo(data, cf->conn, &ctx->ip);
+ }
+ else {
+ cf->conn->secondary = ctx->ip;
}
ctx->active = TRUE;
}
@@ -1630,9 +1662,10 @@ static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf,
switch(event) {
case CF_CTRL_CONN_INFO_UPDATE:
cf_socket_active(cf, data);
+ cf_socket_update_data(cf, data);
break;
case CF_CTRL_DATA_SETUP:
- Curl_persistconninfo(data, cf->conn, &ctx->ip);
+ cf_socket_update_data(cf, data);
break;
case CF_CTRL_FORGET_SOCKET:
ctx->sock = CURL_SOCKET_BAD;
@@ -1715,6 +1748,10 @@ static CURLcode cf_socket_query(struct Curl_cfilter *cf,
}
return CURLE_OK;
}
+ case CF_QUERY_IP_INFO:
+ *pres1 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
+ *(struct ip_quadruple *)pres2 = ctx->ip;
+ return CURLE_OK;
default:
break;
}
@@ -1793,7 +1830,7 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
}
ctx->sock_connected = TRUE;
set_local_ip(cf, data);
- CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T
+ CURL_TRC_CF(data, cf, "%s socket %" FMT_SOCKET_T
" connected: [%s:%d] -> [%s:%d]",
(ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP",
ctx->sock, ctx->ip.local_ip, ctx->ip.local_port,
@@ -1858,12 +1895,12 @@ static CURLcode cf_udp_connect(struct Curl_cfilter *cf,
if(result)
goto out;
CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
- CURL_FORMAT_SOCKET_T " (%s:%d)",
+ FMT_SOCKET_T " (%s:%d)",
ctx->sock, ctx->ip.local_ip, ctx->ip.local_port);
}
else {
CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
- CURL_FORMAT_SOCKET_T " (unconnected)", ctx->sock);
+ FMT_SOCKET_T " (unconnected)", ctx->sock);
}
*done = TRUE;
cf->connected = TRUE;
@@ -2027,6 +2064,7 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
}
ctx->transport = conn->transport;
ctx->sock = *s;
+ ctx->listening = TRUE;
ctx->accepted = FALSE;
result = Curl_cf_create(&cf, &Curl_cft_tcp_accept, ctx);
if(result)
@@ -2038,8 +2076,8 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
ctx->active = TRUE;
ctx->connected_at = Curl_now();
cf->connected = TRUE;
- CURL_TRC_CF(data, cf, "Curl_conn_tcp_listen_set(%"
- CURL_FORMAT_SOCKET_T ")", ctx->sock);
+ CURL_TRC_CF(data, cf, "Curl_conn_tcp_listen_set(%" FMT_SOCKET_T ")",
+ ctx->sock);
out:
if(result) {
@@ -2093,8 +2131,10 @@ CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data,
return CURLE_FAILED_INIT;
ctx = cf->ctx;
+ DEBUGASSERT(ctx->listening);
/* discard the listen socket */
socket_close(data, conn, TRUE, ctx->sock);
+ ctx->listening = FALSE;
ctx->sock = *s;
conn->sock[sockindex] = ctx->sock;
set_accepted_remote_ip(cf, data);
@@ -2103,7 +2143,7 @@ CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data,
ctx->accepted = TRUE;
ctx->connected_at = Curl_now();
cf->connected = TRUE;
- CURL_TRC_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T
+ CURL_TRC_CF(data, cf, "accepted_set(sock=%" FMT_SOCKET_T
", remote=%s port=%d)",
ctx->sock, ctx->ip.remote_ip, ctx->ip.remote_port);
diff --git a/libs/libcurl/src/cf-socket.h b/libs/libcurl/src/cf-socket.h
index 65a5435040..29e6129dee 100644
--- a/libs/libcurl/src/cf-socket.h
+++ b/libs/libcurl/src/cf-socket.h
@@ -57,7 +57,7 @@ struct Curl_sockaddr_ex {
/*
* Parse interface option, and return the interface name and the host part.
*/
-CURLcode Curl_parse_interface(const char *input, size_t len,
+CURLcode Curl_parse_interface(const char *input,
char **dev, char **iface, char **host);
/*
diff --git a/libs/libcurl/src/cfilters.c b/libs/libcurl/src/cfilters.c
index d10c6c1ed0..7ec8f3a79f 100644
--- a/libs/libcurl/src/cfilters.c
+++ b/libs/libcurl/src/cfilters.c
@@ -45,6 +45,9 @@
#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
#endif
+static void cf_cntrl_update_info(struct Curl_easy *data,
+ struct connectdata *conn);
+
#ifdef UNITTESTS
/* used by unit2600.c */
void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data)
@@ -98,10 +101,11 @@ bool Curl_cf_def_data_pending(struct Curl_cfilter *cf,
}
ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
- const void *buf, size_t len, CURLcode *err)
+ const void *buf, size_t len, bool eos,
+ CURLcode *err)
{
return cf->next?
- cf->next->cft->do_send(cf->next, data, buf, len, err) :
+ cf->next->cft->do_send(cf->next, data, buf, len, eos, err) :
CURLE_RECV_ERROR;
}
@@ -256,7 +260,8 @@ ssize_t Curl_cf_recv(struct Curl_easy *data, int num, char *buf,
}
ssize_t Curl_cf_send(struct Curl_easy *data, int num,
- const void *mem, size_t len, CURLcode *code)
+ const void *mem, size_t len, bool eos,
+ CURLcode *code)
{
struct Curl_cfilter *cf;
@@ -268,7 +273,7 @@ ssize_t Curl_cf_send(struct Curl_easy *data, int num,
cf = cf->next;
}
if(cf) {
- ssize_t nwritten = cf->cft->do_send(cf, data, mem, len, code);
+ ssize_t nwritten = cf->cft->do_send(cf, data, mem, len, eos, code);
DEBUGASSERT(nwritten >= 0 || *code);
DEBUGASSERT(nwritten < 0 || !*code || !len);
return nwritten;
@@ -379,10 +384,11 @@ void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
}
ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
- const void *buf, size_t len, CURLcode *err)
+ const void *buf, size_t len, bool eos,
+ CURLcode *err)
{
if(cf)
- return cf->cft->do_send(cf, data, buf, len, err);
+ return cf->cft->do_send(cf, data, buf, len, eos, err);
*err = CURLE_SEND_ERROR;
return -1;
}
@@ -416,9 +422,19 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
*done = cf->connected;
if(!*done) {
+ if(Curl_conn_needs_flush(data, sockindex)) {
+ DEBUGF(infof(data, "Curl_conn_connect(index=%d), flush", sockindex));
+ result = Curl_conn_flush(data, sockindex);
+ if(result && (result != CURLE_AGAIN))
+ return result;
+ }
+
result = cf->cft->do_connect(cf, data, blocking, done);
if(!result && *done) {
- Curl_conn_ev_update_info(data, data->conn);
+ /* Now that the complete filter chain is connected, let all filters
+ * persist information at the connection. E.g. cf-socket sets the
+ * socket and ip related information. */
+ cf_cntrl_update_info(data, data->conn);
conn_report_connect_stats(data, data->conn);
data->conn->keepalive = Curl_now();
}
@@ -501,6 +517,21 @@ bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex)
return FALSE;
}
+bool Curl_conn_cf_needs_flush(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ CURLcode result;
+ int pending = FALSE;
+ result = cf? cf->cft->query(cf, data, CF_QUERY_NEED_FLUSH,
+ &pending, NULL) : CURLE_UNKNOWN_OPTION;
+ return (result || pending == FALSE)? FALSE : TRUE;
+}
+
+bool Curl_conn_needs_flush(struct Curl_easy *data, int sockindex)
+{
+ return Curl_conn_cf_needs_flush(data->conn->cfilter[sockindex], data);
+}
+
void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct easy_pollset *ps)
@@ -627,6 +658,15 @@ curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf,
return CURL_SOCKET_BAD;
}
+CURLcode Curl_conn_cf_get_ip_info(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int *is_ipv6, struct ip_quadruple *ipquad)
+{
+ if(cf)
+ return cf->cft->query(cf, data, CF_QUERY_IP_INFO, is_ipv6, ipquad);
+ return CURLE_UNKNOWN_OPTION;
+}
+
curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex)
{
struct Curl_cfilter *cf;
@@ -693,6 +733,13 @@ CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data)
CF_CTRL_DATA_IDLE, 0, NULL);
}
+
+CURLcode Curl_conn_flush(struct Curl_easy *data, int sockindex)
+{
+ return Curl_conn_cf_cntrl(data->conn->cfilter[sockindex], data, FALSE,
+ CF_CTRL_FLUSH, 0, NULL);
+}
+
/**
* Notify connection filters that the transfer represented by `data`
* is done with sending data (e.g. has uploaded everything).
@@ -717,8 +764,8 @@ CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause)
CF_CTRL_DATA_PAUSE, do_pause, NULL);
}
-void Curl_conn_ev_update_info(struct Curl_easy *data,
- struct connectdata *conn)
+static void cf_cntrl_update_info(struct Curl_easy *data,
+ struct connectdata *conn)
{
cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL);
}
@@ -811,9 +858,10 @@ CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex,
}
CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex,
- const void *buf, size_t blen,
+ const void *buf, size_t blen, bool eos,
size_t *pnwritten)
{
+ size_t write_len = blen;
ssize_t nwritten;
CURLcode result = CURLE_OK;
struct connectdata *conn;
@@ -831,11 +879,14 @@ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex,
if(p) {
size_t altsize = (size_t)strtoul(p, NULL, 10);
if(altsize)
- blen = CURLMIN(blen, altsize);
+ write_len = CURLMIN(write_len, altsize);
}
}
#endif
- nwritten = conn->send[sockindex](data, sockindex, buf, blen, &result);
+ if(write_len != blen)
+ eos = FALSE;
+ nwritten = conn->send[sockindex](data, sockindex, buf, write_len, eos,
+ &result);
DEBUGASSERT((nwritten >= 0) || result);
*pnwritten = (nwritten < 0)? 0 : (size_t)nwritten;
return result;
diff --git a/libs/libcurl/src/cfilters.h b/libs/libcurl/src/cfilters.h
index 87b2f746a9..9d835fdbf3 100644
--- a/libs/libcurl/src/cfilters.h
+++ b/libs/libcurl/src/cfilters.h
@@ -30,6 +30,7 @@ struct Curl_cfilter;
struct Curl_easy;
struct Curl_dns_entry;
struct connectdata;
+struct ip_quadruple;
/* Callback to destroy resources held by this filter instance.
* Implementations MUST NOT chain calls to cf->next.
@@ -105,6 +106,7 @@ 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 */
+ bool eos, /* last chunk */
CURLcode *err); /* error to return */
typedef ssize_t Curl_cft_recv(struct Curl_cfilter *cf,
@@ -140,6 +142,7 @@ typedef CURLcode Curl_cft_conn_keep_alive(struct Curl_cfilter *cf,
/* update conn info at connection and data */
#define CF_CTRL_CONN_INFO_UPDATE (256+0) /* 0 NULL ignored */
#define CF_CTRL_FORGET_SOCKET (256+1) /* 0 NULL ignored */
+#define CF_CTRL_FLUSH (256+2) /* 0 NULL first fail */
/**
* Handle event/control for the filter.
@@ -162,6 +165,9 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf,
* were received.
* -1 if not determined yet.
* - CF_QUERY_SOCKET: the socket used by the filter chain
+ * - CF_QUERY_NEED_FLUSH: TRUE iff any of the filters have unsent data
+ * - CF_QUERY_IP_INFO: res1 says if connection used IPv6, res2 is the
+ * ip quadruple
*/
/* query res1 res2 */
#define CF_QUERY_MAX_CONCURRENT 1 /* number - */
@@ -170,6 +176,8 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf,
#define CF_QUERY_TIMER_CONNECT 4 /* - struct curltime */
#define CF_QUERY_TIMER_APPCONNECT 5 /* - struct curltime */
#define CF_QUERY_STREAM_ERROR 6 /* error code - */
+#define CF_QUERY_NEED_FLUSH 7 /* TRUE/FALSE - */
+#define CF_QUERY_IP_INFO 8 /* TRUE/FALSE struct ip_quadruple */
/**
* Query the cfilter for properties. Filters ignorant of a query will
@@ -241,7 +249,8 @@ void Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf,
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);
+ const void *buf, size_t len, bool eos,
+ CURLcode *err);
ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *err);
CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf,
@@ -317,7 +326,8 @@ CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf,
bool blocking, bool *done);
void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data);
ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
- const void *buf, size_t len, CURLcode *err);
+ const void *buf, size_t len, bool eos,
+ CURLcode *err);
ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *err);
CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf,
@@ -338,6 +348,12 @@ bool Curl_conn_cf_is_ssl(struct Curl_cfilter *cf);
curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf,
struct Curl_easy *data);
+CURLcode Curl_conn_cf_get_ip_info(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int *is_ipv6, struct ip_quadruple *ipquad);
+
+bool Curl_conn_cf_needs_flush(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
#define CURL_CF_SSL_DEFAULT -1
#define CURL_CF_SSL_DISABLE 0
@@ -399,6 +415,17 @@ bool Curl_conn_data_pending(struct Curl_easy *data,
int sockindex);
/**
+ * Return TRUE if any of the connection filters at chain `sockindex`
+ * have data still to send.
+ */
+bool Curl_conn_needs_flush(struct Curl_easy *data, int sockindex);
+
+/**
+ * Flush any pending data on the connection filters at chain `sockindex`.
+ */
+CURLcode Curl_conn_flush(struct Curl_easy *data, int sockindex);
+
+/**
* Return the socket used on data's connection for the index.
* Returns CURL_SOCKET_BAD if not available.
*/
@@ -447,7 +474,7 @@ ssize_t Curl_cf_recv(struct Curl_easy *data, int sockindex, char *buf,
* The error code is placed into `*code`.
*/
ssize_t Curl_cf_send(struct Curl_easy *data, int sockindex,
- const void *buf, size_t len, CURLcode *code);
+ const void *buf, size_t len, bool eos, CURLcode *code);
/**
* The easy handle `data` is being attached to `conn`. This does
@@ -497,12 +524,6 @@ void Curl_conn_ev_data_done(struct Curl_easy *data, bool premature);
CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause);
/**
- * Inform connection filters to update their info in `conn`.
- */
-void Curl_conn_ev_update_info(struct Curl_easy *data,
- struct connectdata *conn);
-
-/**
* Check if FIRSTSOCKET's cfilter chain deems connection alive.
*/
bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn,
@@ -557,7 +578,7 @@ CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex,
* Will return CURLE_AGAIN iff blocked on sending.
*/
CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex,
- const void *buf, size_t blen,
+ const void *buf, size_t blen, bool eos,
size_t *pnwritten);
diff --git a/libs/libcurl/src/config-mac.h b/libs/libcurl/src/config-mac.h
index ab07f74183..76d009bb67 100644
--- a/libs/libcurl/src/config-mac.h
+++ b/libs/libcurl/src/config-mac.h
@@ -27,7 +27,7 @@
/* =================================================================== */
/* Hand crafted config file for Mac OS 9 */
/* =================================================================== */
-/* On Mac OS X you must run configure to generate curl_config.h file */
+/* On macOS you must run configure to generate curl_config.h file */
/* =================================================================== */
#ifndef OS
diff --git a/libs/libcurl/src/config-os400.h b/libs/libcurl/src/config-os400.h
index 6e410ac7ce..5c7305159a 100644
--- a/libs/libcurl/src/config-os400.h
+++ b/libs/libcurl/src/config-os400.h
@@ -65,9 +65,6 @@
/* 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 to 1 if you have the alarm function. */
#define HAVE_ALARM 1
@@ -232,7 +229,7 @@
/* Define if you have the ANSI C header files. */
#define STDC_HEADERS
-/* Define to enable HTTP3 support (experimental, requires NGTCP2, QUICHE or
+/* Define to enable HTTP3 support (experimental, requires NGTCP2, quiche or
MSH3) */
#undef USE_HTTP3
diff --git a/libs/libcurl/src/config-plan9.h b/libs/libcurl/src/config-plan9.h
index ca095df3a8..1c91c2ae15 100644
--- a/libs/libcurl/src/config-plan9.h
+++ b/libs/libcurl/src/config-plan9.h
@@ -41,7 +41,6 @@
#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
diff --git a/libs/libcurl/src/config-riscos.h b/libs/libcurl/src/config-riscos.h
index f537b58c2a..6192f7040f 100644
--- a/libs/libcurl/src/config-riscos.h
+++ b/libs/libcurl/src/config-riscos.h
@@ -63,9 +63,6 @@
/* 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 if you have the alarm function. */
#define HAVE_ALARM
@@ -204,13 +201,6 @@
/* 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
diff --git a/libs/libcurl/src/config-win32.h b/libs/libcurl/src/config-win32.h
index 2e6261c745..17924e3dec 100644
--- a/libs/libcurl/src/config-win32.h
+++ b/libs/libcurl/src/config-win32.h
@@ -149,10 +149,6 @@
/* Define if you have the select function. */
#define HAVE_SELECT 1
-/* Define if libSSH2 is in use */
-#define USE_LIBSSH2 1
-#define HAVE_LIBSSH2_H 1
-
/* Define if you have the setlocale function. */
#define HAVE_SETLOCALE 1
@@ -478,9 +474,6 @@ Vista
#define USE_WIN32_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
diff --git a/libs/libcurl/src/config-win32ce.h b/libs/libcurl/src/config-win32ce.h
index 7e913dffbe..62c64cf948 100644
--- a/libs/libcurl/src/config-win32ce.h
+++ b/libs/libcurl/src/config-win32ce.h
@@ -25,7 +25,7 @@
***************************************************************************/
/* ================================================================ */
-/* lib/config-win32ce.h - Hand crafted config file for windows ce */
+/* lib/config-win32ce.h - Hand crafted config file for Windows CE */
/* ================================================================ */
/* ---------------------------------------------------------------- */
@@ -279,7 +279,7 @@
#define PACKAGE "curl"
/* ---------------------------------------------------------------- */
-/* WinCE */
+/* Windows CE */
/* ---------------------------------------------------------------- */
#ifndef UNICODE
diff --git a/libs/libcurl/src/conncache.c b/libs/libcurl/src/conncache.c
index af470d81cd..d0625abf1a 100644
--- a/libs/libcurl/src/conncache.c
+++ b/libs/libcurl/src/conncache.c
@@ -47,194 +47,242 @@
#include "curl_memory.h"
#include "memdebug.h"
-#define HASHKEY_SIZE 128
-static void connc_discard_conn(struct conncache *connc,
- struct Curl_easy *last_data,
+#define CPOOL_IS_LOCKED(c) ((c) && (c)->locked)
+
+#define CPOOL_LOCK(c) \
+ do { \
+ if((c)) { \
+ if(CURL_SHARE_KEEP_CONNECT((c)->share)) \
+ Curl_share_lock(((c)->idata), CURL_LOCK_DATA_CONNECT, \
+ CURL_LOCK_ACCESS_SINGLE); \
+ DEBUGASSERT(!(c)->locked); \
+ (c)->locked = TRUE; \
+ } \
+ } while(0)
+
+#define CPOOL_UNLOCK(c) \
+ do { \
+ if((c)) { \
+ DEBUGASSERT((c)->locked); \
+ (c)->locked = FALSE; \
+ if(CURL_SHARE_KEEP_CONNECT((c)->share)) \
+ Curl_share_unlock((c)->idata, CURL_LOCK_DATA_CONNECT); \
+ } \
+ } while(0)
+
+
+/* A list of connections to the same destinationn. */
+struct cpool_bundle {
+ struct Curl_llist conns; /* connections in the bundle */
+ size_t dest_len; /* total length of destination, including NUL */
+ char *dest[1]; /* destination of bundle, allocated to keep dest_len bytes */
+};
+
+
+static void cpool_discard_conn(struct cpool *cpool,
+ struct Curl_easy *data,
struct connectdata *conn,
bool aborted);
-static void connc_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- struct conncache *connc,
- bool do_shutdown);
-static void connc_run_conn_shutdown(struct Curl_easy *data,
+static void cpool_close_and_destroy(struct cpool *cpool,
+ struct connectdata *conn,
+ struct Curl_easy *data,
+ bool do_shutdown);
+static void cpool_run_conn_shutdown(struct Curl_easy *data,
struct connectdata *conn,
bool *done);
-static void connc_run_conn_shutdown_handler(struct Curl_easy *data,
+static void cpool_run_conn_shutdown_handler(struct Curl_easy *data,
struct connectdata *conn);
-static CURLMcode connc_update_shutdown_ev(struct Curl_multi *multi,
+static CURLMcode cpool_update_shutdown_ev(struct Curl_multi *multi,
struct Curl_easy *data,
struct connectdata *conn);
-static void connc_shutdown_all(struct conncache *connc, int timeout_ms);
+static void cpool_shutdown_all(struct cpool *cpool,
+ struct Curl_easy *data, int timeout_ms);
+static void cpool_close_and_destroy_all(struct cpool *cpool);
+static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool);
-static CURLcode bundle_create(struct connectbundle **bundlep)
+static struct cpool_bundle *cpool_bundle_create(const char *dest,
+ size_t dest_len)
{
- 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, NULL);
- return CURLE_OK;
+ struct cpool_bundle *bundle;
+ bundle = calloc(1, sizeof(*bundle) + dest_len);
+ if(!bundle)
+ return NULL;
+ Curl_llist_init(&bundle->conns, NULL);
+ bundle->dest_len = dest_len;
+ memcpy(bundle->dest, dest, dest_len);
+ return bundle;
}
-static void bundle_destroy(struct connectbundle *bundle)
+static void cpool_bundle_destroy(struct cpool_bundle *bundle)
{
+ DEBUGASSERT(!Curl_llist_count(&bundle->conns));
free(bundle);
}
/* Add a connection to a bundle */
-static void bundle_add_conn(struct connectbundle *bundle,
- struct connectdata *conn)
+static void cpool_bundle_add(struct cpool_bundle *bundle,
+ struct connectdata *conn)
{
- Curl_llist_append(&bundle->conn_list, conn, &conn->bundle_node);
- conn->bundle = bundle;
- bundle->num_connections++;
+ DEBUGASSERT(!Curl_node_llist(&conn->cpool_node));
+ Curl_llist_append(&bundle->conns, conn, &conn->cpool_node);
+ conn->bits.in_cpool = TRUE;
}
/* Remove a connection from a bundle */
-static int bundle_remove_conn(struct connectbundle *bundle,
- struct connectdata *conn)
+static void cpool_bundle_remove(struct cpool_bundle *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;
+ (void)bundle;
+ DEBUGASSERT(Curl_node_llist(&conn->cpool_node) == &bundle->conns);
+ Curl_node_remove(&conn->cpool_node);
+ conn->bits.in_cpool = FALSE;
}
-static void free_bundle_hash_entry(void *freethis)
+static void cpool_bundle_free_entry(void *freethis)
{
- struct connectbundle *b = (struct connectbundle *) freethis;
-
- bundle_destroy(b);
+ cpool_bundle_destroy((struct cpool_bundle *)freethis);
}
-int Curl_conncache_init(struct conncache *connc,
- struct Curl_multi *multi, size_t size)
+int Curl_cpool_init(struct cpool *cpool,
+ Curl_cpool_disconnect_cb *disconnect_cb,
+ struct Curl_multi *multi,
+ struct Curl_share *share,
+ size_t size)
{
+ DEBUGASSERT(!!multi != !!share); /* either one */
+ Curl_hash_init(&cpool->dest2bundle, size, Curl_hash_str,
+ Curl_str_key_compare, cpool_bundle_free_entry);
+ Curl_llist_init(&cpool->shutdowns, NULL);
+
+ DEBUGASSERT(disconnect_cb);
+ if(!disconnect_cb)
+ return 1;
+
/* allocate a new easy handle to use when closing cached connections */
- connc->closure_handle = curl_easy_init();
- if(!connc->closure_handle)
+ cpool->idata = curl_easy_init();
+ if(!cpool->idata)
return 1; /* bad */
- connc->closure_handle->state.internal = true;
+ cpool->idata->state.internal = true;
+ /* TODO: this is quirky. We need an internal handle for certain
+ * operations, but we do not add it to the multi (if there is one).
+ * But we give it the multi so that socket event operations can work.
+ * Probably better to have an internal handle owned by the multi that
+ * can be used for cpool operations. */
+ cpool->idata->multi = multi;
#ifdef DEBUGBUILD
if(getenv("CURL_DEBUG"))
- connc->closure_handle->set.verbose = true;
+ cpool->idata->set.verbose = true;
#endif
- Curl_hash_init(&connc->hash, size, Curl_hash_str,
- Curl_str_key_compare, free_bundle_hash_entry);
- connc->closure_handle->state.conn_cache = connc;
- connc->multi = multi;
- Curl_llist_init(&connc->shutdowns.conn_list, NULL);
+ cpool->disconnect_cb = disconnect_cb;
+ cpool->idata->multi = cpool->multi = multi;
+ cpool->idata->share = cpool->share = share;
return 0; /* good */
}
-void Curl_conncache_destroy(struct conncache *connc)
+void Curl_cpool_destroy(struct cpool *cpool)
{
- if(connc) {
- Curl_hash_destroy(&connc->hash);
- connc->multi = NULL;
- DEBUGASSERT(!Curl_llist_count(&connc->shutdowns.conn_list));
+ if(cpool) {
+ if(cpool->idata) {
+ cpool_close_and_destroy_all(cpool);
+ /* The internal closure handle is special and we need to
+ * disconnect it from multi/share before closing it down. */
+ cpool->idata->multi = NULL;
+ cpool->idata->share = NULL;
+ Curl_close(&cpool->idata);
+ }
+ Curl_hash_destroy(&cpool->dest2bundle);
+ cpool->multi = NULL;
}
}
-/* creates a key to find a bundle for this connection */
-static void hashkey(struct connectdata *conn, char *buf, size_t len)
+static struct cpool *cpool_get_instance(struct Curl_easy *data)
{
- 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->primary.remote_port;
+ if(data) {
+ if(CURL_SHARE_KEEP_CONNECT(data->share))
+ return &data->share->cpool;
+ else if(data->multi_easy)
+ return &data->multi_easy->cpool;
+ else if(data->multi)
+ return &data->multi->cpool;
}
- 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 USE_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);
+ return NULL;
}
-/* Returns number of connections currently held in the connection cache.
- Locks/unlocks the cache itself!
-*/
-size_t Curl_conncache_size(struct Curl_easy *data)
+void Curl_cpool_xfer_init(struct Curl_easy *data)
{
- size_t num;
- CONNCACHE_LOCK(data);
- num = data->state.conn_cache->num_conn;
- CONNCACHE_UNLOCK(data);
- return num;
+ struct cpool *cpool = cpool_get_instance(data);
+
+ DEBUGASSERT(cpool);
+ if(cpool) {
+ CPOOL_LOCK(cpool);
+ /* the identifier inside the connection cache */
+ data->id = cpool->next_easy_id++;
+ if(cpool->next_easy_id <= 0)
+ cpool->next_easy_id = 0;
+ data->state.lastconnect_id = -1;
+
+ /* 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. */
+ cpool->idata->set.timeout = data->set.timeout;
+ cpool->idata->set.server_response_timeout =
+ data->set.server_response_timeout;
+ cpool->idata->set.no_signal = data->set.no_signal;
+
+ CPOOL_UNLOCK(cpool);
+ }
+ else {
+ /* We should not get here, but in a non-debug build, do something */
+ data->id = 0;
+ data->state.lastconnect_id = -1;
+ }
}
-/* 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)
+static struct cpool_bundle *cpool_find_bundle(struct cpool *cpool,
+ struct connectdata *conn)
{
- 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;
+ return Curl_hash_pick(&cpool->dest2bundle,
+ conn->destination, conn->destination_len);
}
-static void *connc_add_bundle(struct conncache *connc,
- char *key, struct connectbundle *bundle)
+static struct cpool_bundle *
+cpool_add_bundle(struct cpool *cpool, struct connectdata *conn)
{
- return Curl_hash_add(&connc->hash, key, strlen(key), bundle);
+ struct cpool_bundle *bundle;
+
+ bundle = cpool_bundle_create(conn->destination, conn->destination_len);
+ if(!bundle)
+ return NULL;
+
+ if(!Curl_hash_add(&cpool->dest2bundle,
+ bundle->dest, bundle->dest_len, bundle)) {
+ cpool_bundle_destroy(bundle);
+ return NULL;
+ }
+ return bundle;
}
-static void connc_remove_bundle(struct conncache *connc,
- struct connectbundle *bundle)
+static void cpool_remove_bundle(struct cpool *cpool,
+ struct cpool_bundle *bundle)
{
struct Curl_hash_iterator iter;
struct Curl_hash_element *he;
- if(!connc)
+ if(!cpool)
return;
- Curl_hash_start_iterate(&connc->hash, &iter);
+ Curl_hash_start_iterate(&cpool->dest2bundle, &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);
+ Curl_hash_delete(&cpool->dest2bundle, he->key, he->key_len);
return;
}
@@ -242,229 +290,252 @@ static void connc_remove_bundle(struct conncache *connc,
}
}
-CURLcode Curl_conncache_add_conn(struct Curl_easy *data)
+static struct connectdata *
+cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle);
+
+int Curl_cpool_check_limits(struct Curl_easy *data,
+ struct connectdata *conn)
{
- CURLcode result = CURLE_OK;
- struct connectbundle *bundle = NULL;
- struct connectdata *conn = data->conn;
- struct conncache *connc = data->state.conn_cache;
- DEBUGASSERT(conn);
+ struct cpool *cpool = cpool_get_instance(data);
+ struct cpool_bundle *bundle;
+ size_t dest_limit = 0;
+ size_t total_limit = 0;
+ int result = CPOOL_LIMIT_OK;
+
+ if(!cpool)
+ return CPOOL_LIMIT_OK;
+
+ if(data && data->multi) {
+ dest_limit = data->multi->max_host_connections;
+ total_limit = data->multi->max_total_connections;
+ }
- /* *find_bundle() locks the connection cache */
- bundle = Curl_conncache_find_bundle(data, conn, data->state.conn_cache);
- if(!bundle) {
- char key[HASHKEY_SIZE];
+ if(!dest_limit && !total_limit)
+ return CPOOL_LIMIT_OK;
+
+ CPOOL_LOCK(cpool);
+ if(dest_limit) {
+ bundle = cpool_find_bundle(cpool, conn);
+ while(bundle && (Curl_llist_count(&bundle->conns) >= dest_limit)) {
+ struct connectdata *oldest_idle = NULL;
+ /* The bundle is full. Extract the oldest connection that may
+ * be removed now, if there is one. */
+ oldest_idle = cpool_bundle_get_oldest_idle(bundle);
+ if(!oldest_idle)
+ break;
+ /* disconnect the old conn and continue */
+ DEBUGF(infof(data, "Discarding connection #%"
+ FMT_OFF_T " from %zu to reach destination "
+ "limit of %zu", oldest_idle->connection_id,
+ Curl_llist_count(&bundle->conns), dest_limit));
+ Curl_cpool_disconnect(data, oldest_idle, FALSE);
+ }
+ if(bundle && (Curl_llist_count(&bundle->conns) >= dest_limit)) {
+ result = CPOOL_LIMIT_DEST;
+ goto out;
+ }
+ }
- result = bundle_create(&bundle);
- if(result) {
- goto unlock;
+ if(total_limit) {
+ while(cpool->num_conn >= total_limit) {
+ struct connectdata *oldest_idle = cpool_get_oldest_idle(cpool);
+ if(!oldest_idle)
+ break;
+ /* disconnect the old conn and continue */
+ DEBUGF(infof(data, "Discarding connection #%"
+ FMT_OFF_T " from %zu to reach total "
+ "limit of %zu",
+ oldest_idle->connection_id, cpool->num_conn, total_limit));
+ Curl_cpool_disconnect(data, oldest_idle, FALSE);
}
+ if(cpool->num_conn >= total_limit) {
+ result = CPOOL_LIMIT_TOTAL;
+ goto out;
+ }
+ }
+
+out:
+ CPOOL_UNLOCK(cpool);
+ return result;
+}
- hashkey(conn, key, sizeof(key));
+CURLcode Curl_cpool_add_conn(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+ struct cpool_bundle *bundle = NULL;
+ struct cpool *cpool = cpool_get_instance(data);
+ DEBUGASSERT(conn);
- if(!connc_add_bundle(data->state.conn_cache, key, bundle)) {
- bundle_destroy(bundle);
+ DEBUGASSERT(cpool);
+ if(!cpool)
+ return CURLE_FAILED_INIT;
+
+ CPOOL_LOCK(cpool);
+ bundle = cpool_find_bundle(cpool, conn);
+ if(!bundle) {
+ bundle = cpool_add_bundle(cpool, conn);
+ if(!bundle) {
result = CURLE_OUT_OF_MEMORY;
- goto unlock;
+ goto out;
}
}
- bundle_add_conn(bundle, conn);
- conn->connection_id = connc->next_connection_id++;
- connc->num_conn++;
-
- DEBUGF(infof(data, "Added connection %" CURL_FORMAT_CURL_OFF_T ". "
+ cpool_bundle_add(bundle, conn);
+ conn->connection_id = cpool->next_connection_id++;
+ cpool->num_conn++;
+ DEBUGF(infof(data, "Added connection %" FMT_OFF_T ". "
"The cache now contains %zu members",
- conn->connection_id, connc->num_conn));
-
-unlock:
- CONNCACHE_UNLOCK(data);
+ conn->connection_id, cpool->num_conn));
+out:
+ CPOOL_UNLOCK(cpool);
return result;
}
-static void connc_remove_conn(struct conncache *connc,
+static void cpool_remove_conn(struct cpool *cpool,
struct connectdata *conn)
{
- struct connectbundle *bundle = conn->bundle;
-
- /* 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) {
- bundle_remove_conn(bundle, conn);
- if(connc && bundle->num_connections == 0)
- connc_remove_bundle(connc, bundle);
- conn->bundle = NULL; /* removed from it */
- if(connc)
- connc->num_conn--;
+ struct Curl_llist *list = Curl_node_llist(&conn->cpool_node);
+ DEBUGASSERT(cpool);
+ if(list) {
+ /* The connection is certainly in the pool, but where? */
+ struct cpool_bundle *bundle = cpool_find_bundle(cpool, conn);
+ if(bundle && (list == &bundle->conns)) {
+ cpool_bundle_remove(bundle, conn);
+ if(!Curl_llist_count(&bundle->conns))
+ cpool_remove_bundle(cpool, bundle);
+ conn->bits.in_cpool = FALSE;
+ cpool->num_conn--;
+ }
+ else {
+ /* Not in a bundle, already in the shutdown list? */
+ DEBUGASSERT(list == &cpool->shutdowns);
+ }
}
}
-/*
- * 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 conncache *connc = data->state.conn_cache;
-
- if(lock)
- CONNCACHE_LOCK(data);
- connc_remove_conn(connc, conn);
- if(lock)
- CONNCACHE_UNLOCK(data);
- if(connc)
- DEBUGF(infof(data, "The cache now contains %zu members",
- connc->num_conn));
-}
-
-/* This function iterates the entire connection cache and calls the function
+/* This function iterates the entire connection pool and calls the function
func() with the connection pointer as the first argument and the supplied
'param' argument as the other.
- The conncache lock is still held when the callback is called. It needs it,
+ The cpool lock is still held when the callback is called. It needs it,
so that it can safely continue traversing the lists once the callback
returns.
- Returns 1 if the loop was aborted due to the callback's return code.
+ Returns TRUE if the loop was aborted due to the callback's return code.
Return 0 from func() to continue the loop, return 1 to abort it.
*/
-bool Curl_conncache_foreach(struct Curl_easy *data,
- struct conncache *connc,
- void *param,
- int (*func)(struct Curl_easy *data,
- struct connectdata *conn, void *param))
+static bool cpool_foreach(struct Curl_easy *data,
+ struct cpool *cpool,
+ void *param,
+ int (*func)(struct Curl_easy *data,
+ struct connectdata *conn, void *param))
{
struct Curl_hash_iterator iter;
- struct Curl_llist_element *curr;
struct Curl_hash_element *he;
- if(!connc)
+ if(!cpool)
return FALSE;
- CONNCACHE_LOCK(data);
- Curl_hash_start_iterate(&connc->hash, &iter);
+ Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
he = Curl_hash_next_element(&iter);
while(he) {
- struct connectbundle *bundle;
-
- bundle = he->ptr;
+ struct Curl_llist_node *curr;
+ struct cpool_bundle *bundle = he->ptr;
he = Curl_hash_next_element(&iter);
- curr = bundle->conn_list.head;
+ curr = Curl_llist_head(&bundle->conns);
while(curr) {
/* Yes, we need to update curr before calling func(), because func()
might decide to remove the connection */
- struct connectdata *conn = curr->ptr;
- curr = curr->next;
+ struct connectdata *conn = Curl_node_elem(curr);
+ curr = Curl_node_next(curr);
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 *
-connc_find_first_connection(struct conncache *connc)
+/* Return a live connection in the pool or NULL. */
+static struct connectdata *cpool_get_live_conn(struct cpool *cpool)
{
struct Curl_hash_iterator iter;
struct Curl_hash_element *he;
- struct connectbundle *bundle;
+ struct cpool_bundle *bundle;
+ struct Curl_llist_node *conn_node;
- Curl_hash_start_iterate(&connc->hash, &iter);
-
- he = Curl_hash_next_element(&iter);
- while(he) {
- struct Curl_llist_element *curr;
+ Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
+ for(he = Curl_hash_next_element(&iter); he;
+ he = Curl_hash_next_element(&iter)) {
bundle = he->ptr;
-
- curr = bundle->conn_list.head;
- if(curr) {
- return curr->ptr;
- }
-
- he = Curl_hash_next_element(&iter);
+ conn_node = Curl_llist_head(&bundle->conns);
+ if(conn_node)
+ return Curl_node_elem(conn_node);
}
-
return NULL;
}
/*
- * Give ownership of a connection back to the connection cache. Might
- * disconnect the oldest existing in there to make space.
+ * A connection (already in the pool) has become idle. Do any
+ * cleanups in regard to the pool's limits.
*
- * Return TRUE if stored, FALSE if closed.
+ * Return TRUE if idle connection kept in pool, FALSE if closed.
*/
-bool Curl_conncache_return_conn(struct Curl_easy *data,
- struct connectdata *conn)
+bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
+ struct connectdata *conn)
{
unsigned int maxconnects = !data->multi->maxconnects ?
data->multi->num_easy * 4: data->multi->maxconnects;
- struct connectdata *conn_candidate = NULL;
+ struct connectdata *oldest_idle = NULL;
+ struct cpool *cpool = cpool_get_instance(data);
+ bool kept = TRUE;
conn->lastused = Curl_now(); /* it was used up until now */
- if(maxconnects && 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) {
- /* Use the closure handle for this disconnect so that anything that
- happens during the disconnect is not stored and associated with the
- 'data' handle which already just finished a transfer and it is
- important that details from this (unrelated) disconnect does not
- taint meta-data in the data handle. */
- struct conncache *connc = data->state.conn_cache;
- connc_disconnect(NULL, conn_candidate, connc, TRUE);
+ if(cpool && maxconnects) {
+ /* may be called form a callback already under lock */
+ bool do_lock = !CPOOL_IS_LOCKED(cpool);
+ if(do_lock)
+ CPOOL_LOCK(cpool);
+ if(cpool->num_conn > maxconnects) {
+ infof(data, "Connection pool is full, closing the oldest one");
+
+ oldest_idle = cpool_get_oldest_idle(cpool);
+ kept = (oldest_idle != conn);
+ if(oldest_idle) {
+ Curl_cpool_disconnect(cpool->idata, oldest_idle, FALSE);
+ }
}
+ if(do_lock)
+ CPOOL_UNLOCK(cpool);
}
- return (conn_candidate == conn) ? FALSE : TRUE;
-
+ return kept;
}
/*
* 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)
+static struct connectdata *
+cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle)
{
- struct Curl_llist_element *curr;
+ struct Curl_llist_node *curr;
timediff_t highscore = -1;
timediff_t score;
struct curltime now;
- struct connectdata *conn_candidate = NULL;
+ struct connectdata *oldest_idle = NULL;
struct connectdata *conn;
- (void)data;
-
now = Curl_now();
-
- curr = bundle->conn_list.head;
+ curr = Curl_llist_head(&bundle->conns);
while(curr) {
- conn = curr->ptr;
+ conn = Curl_node_elem(curr);
if(!CONN_INUSE(conn)) {
/* Set higher score for the age passed since the connection was used */
@@ -472,130 +543,126 @@ Curl_conncache_extract_bundle(struct Curl_easy *data,
if(score > highscore) {
highscore = score;
- conn_candidate = conn;
+ oldest_idle = 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));
+ curr = Curl_node_next(curr);
}
-
- return conn_candidate;
+ return oldest_idle;
}
-/*
- * 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)
+static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool)
{
- struct conncache *connc = data->state.conn_cache;
struct Curl_hash_iterator iter;
- struct Curl_llist_element *curr;
+ struct Curl_llist_node *curr;
struct Curl_hash_element *he;
+ struct connectdata *oldest_idle = NULL;
+ struct cpool_bundle *bundle;
+ struct curltime now;
timediff_t highscore =- 1;
timediff_t score;
- struct curltime now;
- struct connectdata *conn_candidate = NULL;
- struct connectbundle *bundle;
- struct connectbundle *bundle_candidate = NULL;
now = Curl_now();
+ Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
- CONNCACHE_LOCK(data);
- Curl_hash_start_iterate(&connc->hash, &iter);
-
- he = Curl_hash_next_element(&iter);
- while(he) {
+ for(he = Curl_hash_next_element(&iter); he;
+ he = Curl_hash_next_element(&iter)) {
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;
- }
+ for(curr = Curl_llist_head(&bundle->conns); curr;
+ curr = Curl_node_next(curr)) {
+ conn = Curl_node_elem(curr);
+ if(CONN_INUSE(conn) || conn->bits.close || conn->connect_only)
+ continue;
+ /* Set higher score for the age passed since the connection was used */
+ score = Curl_timediff(now, conn->lastused);
+ if(score > highscore) {
+ highscore = score;
+ oldest_idle = conn;
}
- 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));
+ return oldest_idle;
+}
+
+bool Curl_cpool_find(struct Curl_easy *data,
+ const char *destination, size_t dest_len,
+ Curl_cpool_conn_match_cb *conn_cb,
+ Curl_cpool_done_match_cb *done_cb,
+ void *userdata)
+{
+ struct cpool *cpool = cpool_get_instance(data);
+ struct cpool_bundle *bundle;
+ bool result = FALSE;
+
+ DEBUGASSERT(cpool);
+ DEBUGASSERT(conn_cb);
+ if(!cpool)
+ return FALSE;
+
+ CPOOL_LOCK(cpool);
+ bundle = Curl_hash_pick(&cpool->dest2bundle, (void *)destination, dest_len);
+ if(bundle) {
+ struct Curl_llist_node *curr = Curl_llist_head(&bundle->conns);
+ while(curr) {
+ struct connectdata *conn = Curl_node_elem(curr);
+ /* Get next node now. callback might discard current */
+ curr = Curl_node_next(curr);
+
+ if(conn_cb(conn, userdata)) {
+ result = TRUE;
+ break;
+ }
+ }
}
- CONNCACHE_UNLOCK(data);
- return conn_candidate;
+ if(done_cb) {
+ result = done_cb(result, userdata);
+ }
+ CPOOL_UNLOCK(cpool);
+ return result;
}
-static void connc_shutdown_discard_all(struct conncache *connc)
+static void cpool_shutdown_discard_all(struct cpool *cpool)
{
- struct Curl_llist_element *e = connc->shutdowns.conn_list.head;
+ struct Curl_llist_node *e = Curl_llist_head(&cpool->shutdowns);
struct connectdata *conn;
if(!e)
return;
- DEBUGF(infof(connc->closure_handle, "conncache_shutdown_discard_all"));
- DEBUGASSERT(!connc->shutdowns.iter_locked);
- connc->shutdowns.iter_locked = TRUE;
+ DEBUGF(infof(cpool->idata, "cpool_shutdown_discard_all"));
while(e) {
- conn = e->ptr;
- Curl_llist_remove(&connc->shutdowns.conn_list, e, NULL);
- DEBUGF(infof(connc->closure_handle, "discard connection #%"
- CURL_FORMAT_CURL_OFF_T, conn->connection_id));
- connc_disconnect(NULL, conn, connc, FALSE);
- e = connc->shutdowns.conn_list.head;
+ conn = Curl_node_elem(e);
+ Curl_node_remove(e);
+ DEBUGF(infof(cpool->idata, "discard connection #%" FMT_OFF_T,
+ conn->connection_id));
+ cpool_close_and_destroy(cpool, conn, NULL, FALSE);
+ e = Curl_llist_head(&cpool->shutdowns);
}
- connc->shutdowns.iter_locked = FALSE;
}
-static void connc_close_all(struct conncache *connc)
+static void cpool_close_and_destroy_all(struct cpool *cpool)
{
- struct Curl_easy *data = connc->closure_handle;
struct connectdata *conn;
int timeout_ms = 0;
SIGPIPE_VARIABLE(pipe_st);
- if(!data)
- return;
-
+ DEBUGASSERT(cpool);
/* Move all connections to the shutdown list */
sigpipe_init(&pipe_st);
- conn = connc_find_first_connection(connc);
+ CPOOL_LOCK(cpool);
+ conn = cpool_get_live_conn(cpool);
while(conn) {
- connc_remove_conn(connc, conn);
- sigpipe_apply(data, &pipe_st);
- /* This will remove the connection from the cache */
+ cpool_remove_conn(cpool, conn);
+ sigpipe_apply(cpool->idata, &pipe_st);
connclose(conn, "kill all");
- Curl_conncache_remove_conn(connc->closure_handle, conn, TRUE);
- connc_discard_conn(connc, connc->closure_handle, conn, FALSE);
+ cpool_discard_conn(cpool, cpool->idata, conn, FALSE);
- conn = connc_find_first_connection(connc);
+ conn = cpool_get_live_conn(cpool);
}
+ CPOOL_UNLOCK(cpool);
/* Just for testing, run graceful shutdown */
#ifdef DEBUGBUILD
@@ -608,73 +675,53 @@ static void connc_close_all(struct conncache *connc)
}
}
#endif
- connc_shutdown_all(connc, timeout_ms);
+ sigpipe_apply(cpool->idata, &pipe_st);
+ cpool_shutdown_all(cpool, cpool->idata, timeout_ms);
/* discard all connections in the shutdown list */
- connc_shutdown_discard_all(connc);
+ cpool_shutdown_discard_all(cpool);
- sigpipe_apply(data, &pipe_st);
- Curl_hostcache_clean(data, data->dns.hostcache);
- Curl_close(&data);
+ Curl_hostcache_clean(cpool->idata, cpool->idata->dns.hostcache);
sigpipe_restore(&pipe_st);
}
-void Curl_conncache_close_all_connections(struct conncache *connc)
-{
- connc_close_all(connc);
-}
-static void connc_shutdown_discard_oldest(struct conncache *connc)
+static void cpool_shutdown_destroy_oldest(struct cpool *cpool)
{
- struct Curl_llist_element *e;
+ struct Curl_llist_node *e;
struct connectdata *conn;
- DEBUGASSERT(!connc->shutdowns.iter_locked);
- if(connc->shutdowns.iter_locked)
- return;
-
- e = connc->shutdowns.conn_list.head;
+ e = Curl_llist_head(&cpool->shutdowns);
if(e) {
SIGPIPE_VARIABLE(pipe_st);
- conn = e->ptr;
- Curl_llist_remove(&connc->shutdowns.conn_list, e, NULL);
+ conn = Curl_node_elem(e);
+ Curl_node_remove(e);
sigpipe_init(&pipe_st);
- sigpipe_apply(connc->closure_handle, &pipe_st);
- connc_disconnect(NULL, conn, connc, FALSE);
+ sigpipe_apply(cpool->idata, &pipe_st);
+ cpool_close_and_destroy(cpool, conn, NULL, FALSE);
sigpipe_restore(&pipe_st);
}
}
-static void connc_discard_conn(struct conncache *connc,
- struct Curl_easy *last_data,
+static void cpool_discard_conn(struct cpool *cpool,
+ struct Curl_easy *data,
struct connectdata *conn,
bool aborted)
{
- /* `last_data`, if present, is the transfer that last worked with
- * the connection. It is present when the connection is being shut down
- * via `Curl_conncache_discard_conn()`, e.g. when the transfer failed
- * or does not allow connection reuse.
- * Using the original handle is necessary for shutting down the protocol
- * handler belonging to the connection. Protocols like 'file:' rely on
- * being invoked to clean up their allocations in the easy handle.
- * When a connection comes from the cache, the transfer is no longer
- * there and we use the cache is own closure handle.
- */
- struct Curl_easy *data = last_data? last_data : connc->closure_handle;
bool done = FALSE;
DEBUGASSERT(data);
- DEBUGASSERT(connc);
- DEBUGASSERT(!conn->bundle);
+ DEBUGASSERT(cpool);
+ DEBUGASSERT(!conn->bits.in_cpool);
/*
* If this connection is not marked to force-close, leave it open if there
* are other users of it
*/
if(CONN_INUSE(conn) && !aborted) {
- DEBUGF(infof(data, "[CCACHE] not discarding #%" CURL_FORMAT_CURL_OFF_T
- " still in use by %zu transfers", conn->connection_id,
- CONN_INUSE(conn)));
+ DEBUGF(infof(data, "[CCACHE] not discarding #%" FMT_OFF_T
+ " still in use by %zu transfers", conn->connection_id,
+ CONN_INUSE(conn)));
return;
}
@@ -694,22 +741,14 @@ static void connc_discard_conn(struct conncache *connc,
if(!done) {
/* Attempt to shutdown the connection right away. */
Curl_attach_connection(data, conn);
- connc_run_conn_shutdown(data, conn, &done);
- DEBUGF(infof(data, "[CCACHE] shutdown #%" CURL_FORMAT_CURL_OFF_T
- ", done=%d",conn->connection_id, done));
+ cpool_run_conn_shutdown(data, conn, &done);
+ DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d",
+ conn->connection_id, done));
Curl_detach_connection(data);
}
if(done) {
- connc_disconnect(data, conn, connc, FALSE);
- return;
- }
-
- DEBUGASSERT(!connc->shutdowns.iter_locked);
- if(connc->shutdowns.iter_locked) {
- DEBUGF(infof(data, "[CCACHE] discarding #%" CURL_FORMAT_CURL_OFF_T
- ", list locked", conn->connection_id));
- connc_disconnect(data, conn, connc, FALSE);
+ cpool_close_and_destroy(cpool, conn, data, FALSE);
return;
}
@@ -717,65 +756,89 @@ static void connc_discard_conn(struct conncache *connc,
* during multi processing. */
if(data->multi && data->multi->max_shutdown_connections > 0 &&
(data->multi->max_shutdown_connections >=
- (long)Curl_llist_count(&connc->shutdowns.conn_list))) {
+ (long)Curl_llist_count(&cpool->shutdowns))) {
DEBUGF(infof(data, "[CCACHE] discarding oldest shutdown connection "
"due to limit of %ld",
data->multi->max_shutdown_connections));
- connc_shutdown_discard_oldest(connc);
+ cpool_shutdown_destroy_oldest(cpool);
}
if(data->multi && data->multi->socket_cb) {
- DEBUGASSERT(connc == &data->multi->conn_cache);
+ DEBUGASSERT(cpool == &data->multi->cpool);
/* Start with an empty shutdown pollset, so out internal closure handle
* is added to the sockets. */
memset(&conn->shutdown_poll, 0, sizeof(conn->shutdown_poll));
- if(connc_update_shutdown_ev(data->multi, connc->closure_handle, conn)) {
+ if(cpool_update_shutdown_ev(data->multi, cpool->idata, conn)) {
DEBUGF(infof(data, "[CCACHE] update events for shutdown failed, "
- "discarding #%" CURL_FORMAT_CURL_OFF_T,
- conn->connection_id));
- connc_disconnect(data, conn, connc, FALSE);
+ "discarding #%" FMT_OFF_T,
+ conn->connection_id));
+ cpool_close_and_destroy(cpool, conn, data, FALSE);
return;
}
}
- Curl_llist_append(&connc->shutdowns.conn_list, conn, &conn->bundle_node);
- DEBUGF(infof(data, "[CCACHE] added #%" CURL_FORMAT_CURL_OFF_T
- " to shutdown list of length %zu", conn->connection_id,
- Curl_llist_count(&connc->shutdowns.conn_list)));
+ Curl_llist_append(&cpool->shutdowns, conn, &conn->cpool_node);
+ DEBUGF(infof(data, "[CCACHE] added #%" FMT_OFF_T
+ " to shutdown list of length %zu", conn->connection_id,
+ Curl_llist_count(&cpool->shutdowns)));
}
-void Curl_conncache_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- bool aborted)
+void Curl_cpool_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool aborted)
{
- DEBUGASSERT(data);
- /* Connection must no longer be in and connection cache */
- DEBUGASSERT(!conn->bundle);
+ struct cpool *cpool = cpool_get_instance(data);
+ bool do_lock;
+
+ DEBUGASSERT(cpool);
+ DEBUGASSERT(data && !data->conn);
+ if(!cpool)
+ return;
+
+ /* If this connection is not marked to force-close, leave it open if there
+ * are other users of it */
+ if(CONN_INUSE(conn) && !aborted) {
+ DEBUGASSERT(0); /* does this ever happen? */
+ DEBUGF(infof(data, "Curl_disconnect when inuse: %zu", CONN_INUSE(conn)));
+ return;
+ }
+
+ /* This method may be called while we are under lock, e.g. from a
+ * user callback in find. */
+ do_lock = !CPOOL_IS_LOCKED(cpool);
+ if(do_lock)
+ CPOOL_LOCK(cpool);
+
+ if(conn->bits.in_cpool) {
+ cpool_remove_conn(cpool, conn);
+ DEBUGASSERT(!conn->bits.in_cpool);
+ }
+
+ /* Run the callback to let it clean up anything it wants to. */
+ aborted = cpool->disconnect_cb(data, conn, aborted);
if(data->multi) {
- /* Add it to the multi's conncache for shutdown handling */
- infof(data, "%s connection #%" CURL_FORMAT_CURL_OFF_T,
+ /* Add it to the multi's cpool for shutdown handling */
+ infof(data, "%s connection #%" FMT_OFF_T,
aborted? "closing" : "shutting down", conn->connection_id);
- connc_discard_conn(&data->multi->conn_cache, data, conn, aborted);
+ cpool_discard_conn(&data->multi->cpool, data, conn, aborted);
}
else {
/* No multi available. Make a best-effort shutdown + close */
- infof(data, "closing connection #%" CURL_FORMAT_CURL_OFF_T,
- conn->connection_id);
- DEBUGASSERT(!conn->bundle);
- connc_run_conn_shutdown_handler(data, conn);
- connc_disconnect(data, conn, NULL, !aborted);
+ infof(data, "closing connection #%" FMT_OFF_T, conn->connection_id);
+ cpool_close_and_destroy(NULL, conn, data, !aborted);
}
+
+ if(do_lock)
+ CPOOL_UNLOCK(cpool);
}
-static void connc_run_conn_shutdown_handler(struct Curl_easy *data,
+static void cpool_run_conn_shutdown_handler(struct Curl_easy *data,
struct connectdata *conn)
{
if(!conn->bits.shutdown_handler) {
- if(conn->dns_entry) {
- Curl_resolv_unlock(data, conn->dns_entry);
- conn->dns_entry = NULL;
- }
+ if(conn->dns_entry)
+ Curl_resolv_unlink(data, &conn->dns_entry);
/* Cleanup NTLM connection-related data */
Curl_http_auth_cleanup_ntlm(conn);
@@ -785,9 +848,10 @@ static void connc_run_conn_shutdown_handler(struct Curl_easy *data,
if(conn->handler && conn->handler->disconnect) {
/* This is set if protocol-specific cleanups should be made */
- DEBUGF(infof(data, "connection #%" CURL_FORMAT_CURL_OFF_T
+ DEBUGF(infof(data, "connection #%" FMT_OFF_T
", shutdown protocol handler (aborted=%d)",
conn->connection_id, conn->bits.aborted));
+
conn->handler->disconnect(data, conn, conn->bits.aborted);
}
@@ -798,7 +862,7 @@ static void connc_run_conn_shutdown_handler(struct Curl_easy *data,
}
}
-static void connc_run_conn_shutdown(struct Curl_easy *data,
+static void cpool_run_conn_shutdown(struct Curl_easy *data,
struct connectdata *conn,
bool *done)
{
@@ -808,7 +872,7 @@ static void connc_run_conn_shutdown(struct Curl_easy *data,
/* We expect to be attached when called */
DEBUGASSERT(data->conn == conn);
- connc_run_conn_shutdown_handler(data, conn);
+ cpool_run_conn_shutdown_handler(data, conn);
if(conn->bits.shutdown_filters) {
*done = TRUE;
@@ -835,24 +899,23 @@ static void connc_run_conn_shutdown(struct Curl_easy *data,
conn->bits.shutdown_filters = TRUE;
}
-CURLcode Curl_conncache_add_pollfds(struct conncache *connc,
- struct curl_pollfds *cpfds)
+static CURLcode cpool_add_pollfds(struct cpool *cpool,
+ struct curl_pollfds *cpfds)
{
CURLcode result = CURLE_OK;
- DEBUGASSERT(!connc->shutdowns.iter_locked);
- connc->shutdowns.iter_locked = TRUE;
- if(connc->shutdowns.conn_list.head) {
- struct Curl_llist_element *e;
+ if(Curl_llist_head(&cpool->shutdowns)) {
+ struct Curl_llist_node *e;
struct easy_pollset ps;
struct connectdata *conn;
- for(e = connc->shutdowns.conn_list.head; e; e = e->next) {
- conn = e->ptr;
+ for(e = Curl_llist_head(&cpool->shutdowns); e;
+ e = Curl_node_next(e)) {
+ conn = Curl_node_elem(e);
memset(&ps, 0, sizeof(ps));
- Curl_attach_connection(connc->closure_handle, conn);
- Curl_conn_adjust_pollset(connc->closure_handle, &ps);
- Curl_detach_connection(connc->closure_handle);
+ Curl_attach_connection(cpool->idata, conn);
+ Curl_conn_adjust_pollset(cpool->idata, &ps);
+ Curl_detach_connection(cpool->idata);
result = Curl_pollfds_add_ps(cpfds, &ps);
if(result) {
@@ -862,28 +925,37 @@ CURLcode Curl_conncache_add_pollfds(struct conncache *connc,
}
}
out:
- connc->shutdowns.iter_locked = FALSE;
return result;
}
-CURLcode Curl_conncache_add_waitfds(struct conncache *connc,
- struct curl_waitfds *cwfds)
+CURLcode Curl_cpool_add_pollfds(struct cpool *cpool,
+ struct curl_pollfds *cpfds)
+{
+ CURLcode result;
+ CPOOL_LOCK(cpool);
+ result = cpool_add_pollfds(cpool, cpfds);
+ CPOOL_UNLOCK(cpool);
+ return result;
+}
+
+CURLcode Curl_cpool_add_waitfds(struct cpool *cpool,
+ struct curl_waitfds *cwfds)
{
CURLcode result = CURLE_OK;
- DEBUGASSERT(!connc->shutdowns.iter_locked);
- connc->shutdowns.iter_locked = TRUE;
- if(connc->shutdowns.conn_list.head) {
- struct Curl_llist_element *e;
+ CPOOL_LOCK(cpool);
+ if(Curl_llist_head(&cpool->shutdowns)) {
+ struct Curl_llist_node *e;
struct easy_pollset ps;
struct connectdata *conn;
- for(e = connc->shutdowns.conn_list.head; e; e = e->next) {
- conn = e->ptr;
+ for(e = Curl_llist_head(&cpool->shutdowns); e;
+ e = Curl_node_next(e)) {
+ conn = Curl_node_elem(e);
memset(&ps, 0, sizeof(ps));
- Curl_attach_connection(connc->closure_handle, conn);
- Curl_conn_adjust_pollset(connc->closure_handle, &ps);
- Curl_detach_connection(connc->closure_handle);
+ Curl_attach_connection(cpool->idata, conn);
+ Curl_conn_adjust_pollset(cpool->idata, &ps);
+ Curl_detach_connection(cpool->idata);
result = Curl_waitfds_add_ps(cwfds, &ps);
if(result)
@@ -891,15 +963,15 @@ CURLcode Curl_conncache_add_waitfds(struct conncache *connc,
}
}
out:
- connc->shutdowns.iter_locked = FALSE;
+ CPOOL_UNLOCK(cpool);
return result;
}
-static void connc_perform(struct conncache *connc)
+static void cpool_perform(struct cpool *cpool)
{
- struct Curl_easy *data = connc->closure_handle;
- struct Curl_llist_element *e = connc->shutdowns.conn_list.head;
- struct Curl_llist_element *enext;
+ struct Curl_easy *data = cpool->idata;
+ struct Curl_llist_node *e = Curl_llist_head(&cpool->shutdowns);
+ struct Curl_llist_node *enext;
struct connectdata *conn;
struct curltime *nowp = NULL;
struct curltime now;
@@ -910,21 +982,19 @@ static void connc_perform(struct conncache *connc)
return;
DEBUGASSERT(data);
- DEBUGASSERT(!connc->shutdowns.iter_locked);
DEBUGF(infof(data, "[CCACHE] perform, %zu connections being shutdown",
- Curl_llist_count(&connc->shutdowns.conn_list)));
- connc->shutdowns.iter_locked = TRUE;
+ Curl_llist_count(&cpool->shutdowns)));
while(e) {
- enext = e->next;
- conn = e->ptr;
+ enext = Curl_node_next(e);
+ conn = Curl_node_elem(e);
Curl_attach_connection(data, conn);
- connc_run_conn_shutdown(data, conn, &done);
- DEBUGF(infof(data, "[CCACHE] shutdown #%" CURL_FORMAT_CURL_OFF_T
- ", done=%d", conn->connection_id, done));
+ cpool_run_conn_shutdown(data, conn, &done);
+ DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d",
+ conn->connection_id, done));
Curl_detach_connection(data);
if(done) {
- Curl_llist_remove(&connc->shutdowns.conn_list, e, NULL);
- connc_disconnect(NULL, conn, connc, FALSE);
+ Curl_node_remove(e);
+ cpool_close_and_destroy(cpool, conn, NULL, FALSE);
}
else {
/* Not done, when does this connection time out? */
@@ -938,72 +1008,56 @@ static void connc_perform(struct conncache *connc)
}
e = enext;
}
- connc->shutdowns.iter_locked = FALSE;
if(next_from_now_ms)
Curl_expire(data, next_from_now_ms, EXPIRE_RUN_NOW);
}
-void Curl_conncache_multi_perform(struct Curl_multi *multi)
+void Curl_cpool_multi_perform(struct Curl_multi *multi)
{
- connc_perform(&multi->conn_cache);
+ CPOOL_LOCK(&multi->cpool);
+ cpool_perform(&multi->cpool);
+ CPOOL_UNLOCK(&multi->cpool);
}
/*
- * 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
- * is not strictly bound to the life-time of *this* particular connection.
- *
+ * Close and destroy the connection. Run the shutdown sequence once,
+ * of so requested.
*/
-static void connc_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- struct conncache *connc,
- bool do_shutdown)
+static void cpool_close_and_destroy(struct cpool *cpool,
+ struct connectdata *conn,
+ struct Curl_easy *data,
+ bool do_shutdown)
{
bool done;
/* there must be a connection to close */
DEBUGASSERT(conn);
- /* it must be removed from the connection cache */
- DEBUGASSERT(!conn->bundle);
+ /* it must be removed from the connection pool */
+ DEBUGASSERT(!conn->bits.in_cpool);
/* there must be an associated transfer */
- DEBUGASSERT(data || connc);
+ DEBUGASSERT(data || cpool);
if(!data)
- data = connc->closure_handle;
+ data = cpool->idata;
/* the transfer must be detached from the connection */
DEBUGASSERT(data && !data->conn);
Curl_attach_connection(data, conn);
- if(connc && connc->multi && connc->multi->socket_cb) {
- struct easy_pollset ps;
- /* With an empty pollset, all previously polled sockets will be removed
- * via the multi_socket API callback. */
- memset(&ps, 0, sizeof(ps));
- (void)Curl_multi_pollset_ev(connc->multi, data, &ps, &conn->shutdown_poll);
- }
-
- connc_run_conn_shutdown_handler(data, conn);
+ cpool_run_conn_shutdown_handler(data, conn);
if(do_shutdown) {
/* Make a last attempt to shutdown handlers and filters, if
* not done so already. */
- connc_run_conn_shutdown(data, conn, &done);
+ cpool_run_conn_shutdown(data, conn, &done);
}
- if(connc)
- DEBUGF(infof(data, "[CCACHE] closing #%" CURL_FORMAT_CURL_OFF_T,
+ if(cpool)
+ DEBUGF(infof(data, "[CCACHE] closing #%" FMT_OFF_T,
conn->connection_id));
else
- DEBUGF(infof(data, "closing connection #%" CURL_FORMAT_CURL_OFF_T,
+ DEBUGF(infof(data, "closing connection #%" FMT_OFF_T,
conn->connection_id));
Curl_conn_close(data, SECONDARYSOCKET);
Curl_conn_close(data, FIRSTSOCKET);
@@ -1013,7 +1067,7 @@ static void connc_disconnect(struct Curl_easy *data,
}
-static CURLMcode connc_update_shutdown_ev(struct Curl_multi *multi,
+static CURLMcode cpool_update_shutdown_ev(struct Curl_multi *multi,
struct Curl_easy *data,
struct connectdata *conn)
{
@@ -1036,49 +1090,41 @@ static CURLMcode connc_update_shutdown_ev(struct Curl_multi *multi,
return mresult;
}
-void Curl_conncache_multi_socket(struct Curl_multi *multi,
- curl_socket_t s, int ev_bitmask)
+void Curl_cpool_multi_socket(struct Curl_multi *multi,
+ curl_socket_t s, int ev_bitmask)
{
- struct conncache *connc = &multi->conn_cache;
- struct Curl_easy *data = connc->closure_handle;
- struct Curl_llist_element *e = connc->shutdowns.conn_list.head;
+ struct cpool *cpool = &multi->cpool;
+ struct Curl_easy *data = cpool->idata;
+ struct Curl_llist_node *e;
struct connectdata *conn;
bool done;
(void)ev_bitmask;
DEBUGASSERT(multi->socket_cb);
- if(!e)
- return;
-
- connc->shutdowns.iter_locked = TRUE;
+ CPOOL_LOCK(cpool);
+ e = Curl_llist_head(&cpool->shutdowns);
while(e) {
- conn = e->ptr;
+ conn = Curl_node_elem(e);
if(s == conn->sock[FIRSTSOCKET] || s == conn->sock[SECONDARYSOCKET]) {
Curl_attach_connection(data, conn);
- connc_run_conn_shutdown(data, conn, &done);
- DEBUGF(infof(data, "[CCACHE] shutdown #%" CURL_FORMAT_CURL_OFF_T
- ", done=%d", conn->connection_id, done));
+ cpool_run_conn_shutdown(data, conn, &done);
+ DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d",
+ conn->connection_id, done));
Curl_detach_connection(data);
- if(done || connc_update_shutdown_ev(multi, data, conn)) {
- Curl_llist_remove(&connc->shutdowns.conn_list, e, NULL);
- connc_disconnect(NULL, conn, connc, FALSE);
+ if(done || cpool_update_shutdown_ev(multi, data, conn)) {
+ Curl_node_remove(e);
+ cpool_close_and_destroy(cpool, conn, NULL, FALSE);
}
break;
}
- e = e->next;
+ e = Curl_node_next(e);
}
- connc->shutdowns.iter_locked = FALSE;
-}
-
-void Curl_conncache_multi_close_all(struct Curl_multi *multi)
-{
- connc_close_all(&multi->conn_cache);
+ CPOOL_UNLOCK(cpool);
}
-
#define NUM_POLLS_ON_STACK 10
-static CURLcode connc_shutdown_wait(struct conncache *connc, int timeout_ms)
+static CURLcode cpool_shutdown_wait(struct cpool *cpool, int timeout_ms)
{
struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
struct curl_pollfds cpfds;
@@ -1086,7 +1132,7 @@ static CURLcode connc_shutdown_wait(struct conncache *connc, int timeout_ms)
Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
- result = Curl_conncache_add_pollfds(connc, &cpfds);
+ result = cpool_add_pollfds(cpool, &cpfds);
if(result)
goto out;
@@ -1097,9 +1143,9 @@ out:
return result;
}
-static void connc_shutdown_all(struct conncache *connc, int timeout_ms)
+static void cpool_shutdown_all(struct cpool *cpool,
+ struct Curl_easy *data, int timeout_ms)
{
- struct Curl_easy *data = connc->closure_handle;
struct connectdata *conn;
struct curltime started = Curl_now();
@@ -1107,79 +1153,226 @@ static void connc_shutdown_all(struct conncache *connc, int timeout_ms)
return;
(void)data;
- DEBUGF(infof(data, "conncache shutdown all"));
+ DEBUGF(infof(data, "cpool shutdown all"));
/* Move all connections into the shutdown queue */
- conn = connc_find_first_connection(connc);
- while(conn) {
- /* This will remove the connection from the cache */
- DEBUGF(infof(data, "moving connection %" CURL_FORMAT_CURL_OFF_T
+ for(conn = cpool_get_live_conn(cpool); conn;
+ conn = cpool_get_live_conn(cpool)) {
+ /* Move conn from live set to shutdown or destroy right away */
+ DEBUGF(infof(data, "moving connection #%" FMT_OFF_T
" to shutdown queue", conn->connection_id));
- connc_remove_conn(connc, conn);
- connc_discard_conn(connc, NULL, conn, FALSE);
- conn = connc_find_first_connection(connc);
+ cpool_remove_conn(cpool, conn);
+ cpool_discard_conn(cpool, data, conn, FALSE);
}
- DEBUGASSERT(!connc->shutdowns.iter_locked);
- while(connc->shutdowns.conn_list.head) {
+ while(Curl_llist_head(&cpool->shutdowns)) {
timediff_t timespent;
int remain_ms;
- connc_perform(connc);
+ cpool_perform(cpool);
- if(!connc->shutdowns.conn_list.head) {
- DEBUGF(infof(data, "conncache shutdown ok"));
+ if(!Curl_llist_head(&cpool->shutdowns)) {
+ DEBUGF(infof(data, "cpool shutdown ok"));
break;
}
/* wait for activity, timeout or "nothing" */
timespent = Curl_timediff(Curl_now(), started);
if(timespent >= (timediff_t)timeout_ms) {
- DEBUGF(infof(data, "conncache shutdown %s",
+ DEBUGF(infof(data, "cpool shutdown %s",
(timeout_ms > 0)? "timeout" : "best effort done"));
break;
}
remain_ms = timeout_ms - (int)timespent;
- if(connc_shutdown_wait(connc, remain_ms)) {
- DEBUGF(infof(data, "conncache shutdown all, abort"));
+ if(cpool_shutdown_wait(cpool, remain_ms)) {
+ DEBUGF(infof(data, "cpool shutdown all, abort"));
break;
}
}
- /* Due to errors/timeout, we might come here without being full ydone. */
- connc_shutdown_discard_all(connc);
+ /* Due to errors/timeout, we might come here without being done. */
+ cpool_shutdown_discard_all(cpool);
+}
+
+struct cpool_reaper_ctx {
+ struct curltime now;
+};
+
+static int cpool_reap_dead_cb(struct Curl_easy *data,
+ struct connectdata *conn, void *param)
+{
+ struct cpool_reaper_ctx *rctx = param;
+ if(Curl_conn_seems_dead(conn, data, &rctx->now)) {
+ /* stop the iteration here, pass back the connection that was pruned */
+ Curl_cpool_disconnect(data, conn, FALSE);
+ return 1;
+ }
+ return 0; /* continue iteration */
+}
+
+/*
+ * This function scans the data's connection pool for half-open/dead
+ * connections, closes and removes them.
+ * The cleanup is done at most once per second.
+ *
+ * When called, this transfer has no connection attached.
+ */
+void Curl_cpool_prune_dead(struct Curl_easy *data)
+{
+ struct cpool *cpool = cpool_get_instance(data);
+ struct cpool_reaper_ctx rctx;
+ timediff_t elapsed;
+
+ if(!cpool)
+ return;
+
+ rctx.now = Curl_now();
+ CPOOL_LOCK(cpool);
+ elapsed = Curl_timediff(rctx.now, cpool->last_cleanup);
+
+ if(elapsed >= 1000L) {
+ while(cpool_foreach(data, cpool, &rctx, cpool_reap_dead_cb))
+ ;
+ cpool->last_cleanup = rctx.now;
+ }
+ CPOOL_UNLOCK(cpool);
+}
+
+static int conn_upkeep(struct Curl_easy *data,
+ struct connectdata *conn,
+ void *param)
+{
+ struct curltime *now = param;
+ /* TODO, shall we reap connections that return an error here? */
+ Curl_conn_upkeep(data, conn, now);
+ return 0; /* continue iteration */
+}
+
+CURLcode Curl_cpool_upkeep(void *data)
+{
+ struct cpool *cpool = cpool_get_instance(data);
+ struct curltime now = Curl_now();
+
+ if(!cpool)
+ return CURLE_OK;
+
+ CPOOL_LOCK(cpool);
+ cpool_foreach(data, cpool, &now, conn_upkeep);
+ CPOOL_UNLOCK(cpool);
+ return CURLE_OK;
+}
+
+struct cpool_find_ctx {
+ curl_off_t id;
+ struct connectdata *conn;
+};
+
+static int cpool_find_conn(struct Curl_easy *data,
+ struct connectdata *conn, void *param)
+{
+ struct cpool_find_ctx *fctx = param;
+ (void)data;
+ if(conn->connection_id == fctx->id) {
+ fctx->conn = conn;
+ return 1;
+ }
+ return 0;
+}
+
+struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data,
+ curl_off_t conn_id)
+{
+ struct cpool *cpool = cpool_get_instance(data);
+ struct cpool_find_ctx fctx;
+
+ if(!cpool)
+ return NULL;
+ fctx.id = conn_id;
+ fctx.conn = NULL;
+ CPOOL_LOCK(cpool);
+ cpool_foreach(cpool->idata, cpool, &fctx, cpool_find_conn);
+ CPOOL_UNLOCK(cpool);
+ return fctx.conn;
+}
+
+struct cpool_do_conn_ctx {
+ curl_off_t id;
+ Curl_cpool_conn_do_cb *cb;
+ void *cbdata;
+};
+
+static int cpool_do_conn(struct Curl_easy *data,
+ struct connectdata *conn, void *param)
+{
+ struct cpool_do_conn_ctx *dctx = param;
+ (void)data;
+ if(conn->connection_id == dctx->id) {
+ dctx->cb(conn, data, dctx->cbdata);
+ return 1;
+ }
+ return 0;
+}
+
+void Curl_cpool_do_by_id(struct Curl_easy *data, curl_off_t conn_id,
+ Curl_cpool_conn_do_cb *cb, void *cbdata)
+{
+ struct cpool *cpool = cpool_get_instance(data);
+ struct cpool_do_conn_ctx dctx;
+
+ if(!cpool)
+ return;
+ dctx.id = conn_id;
+ dctx.cb = cb;
+ dctx.cbdata = cbdata;
+ CPOOL_LOCK(cpool);
+ cpool_foreach(data, cpool, &dctx, cpool_do_conn);
+ CPOOL_UNLOCK(cpool);
+}
+
+void Curl_cpool_do_locked(struct Curl_easy *data,
+ struct connectdata *conn,
+ Curl_cpool_conn_do_cb *cb, void *cbdata)
+{
+ struct cpool *cpool = cpool_get_instance(data);
+ if(cpool) {
+ CPOOL_LOCK(cpool);
+ cb(conn, data, cbdata);
+ CPOOL_UNLOCK(cpool);
+ }
+ else
+ cb(conn, data, cbdata);
}
#if 0
-/* Useful for debugging the connection cache */
-void Curl_conncache_print(struct conncache *connc)
+/* Useful for debugging the connection pool */
+void Curl_cpool_print(struct cpool *cpool)
{
struct Curl_hash_iterator iter;
- struct Curl_llist_element *curr;
+ struct Curl_llist_node *curr;
struct Curl_hash_element *he;
- if(!connc)
+ if(!cpool)
return;
fprintf(stderr, "=Bundle cache=\n");
- Curl_hash_start_iterate(connc->hash, &iter);
+ Curl_hash_start_iterate(cpool->dest2bundle, &iter);
he = Curl_hash_next_element(&iter);
while(he) {
- struct connectbundle *bundle;
+ struct cpool_bundle *bundle;
struct connectdata *conn;
bundle = he->ptr;
fprintf(stderr, "%s -", he->key);
- curr = bundle->conn_list->head;
+ curr = Curl_llist_head(bundle->conns);
while(curr) {
- conn = curr->ptr;
+ conn = Curl_node_elem(curr);
- fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
- curr = curr->next;
+ fprintf(stderr, " [%p %d]", (void *)conn, conn->refcount);
+ curr = Curl_node_next(curr);
}
fprintf(stderr, "\n");
diff --git a/libs/libcurl/src/conncache.h b/libs/libcurl/src/conncache.h
index 7baae615a0..df1f1353aa 100644
--- a/libs/libcurl/src/conncache.h
+++ b/libs/libcurl/src/conncache.h
@@ -25,140 +25,177 @@
*
***************************************************************************/
-/*
- * 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 Curl_easy;
struct curl_pollfds;
struct curl_waitfds;
struct Curl_multi;
+struct Curl_share;
-struct connshutdowns {
- struct Curl_llist conn_list; /* The connectdata to shut down */
- BIT(iter_locked); /* TRUE while iterating the list */
-};
-
-struct conncache {
- struct Curl_hash hash;
+/**
+ * Callback invoked when disconnecting connections.
+ * @param data transfer last handling the connection, not attached
+ * @param conn the connection to discard
+ * @param aborted if the connection is being aborted
+ * @return if the connection is being aborted, e.g. should NOT perform
+ * a shutdown and just close.
+ **/
+typedef bool Curl_cpool_disconnect_cb(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool aborted);
+
+struct cpool {
+ /* the pooled connections, bundled per destination */
+ struct Curl_hash dest2bundle;
size_t num_conn;
curl_off_t next_connection_id;
curl_off_t next_easy_id;
struct curltime last_cleanup;
- struct connshutdowns shutdowns;
- /* handle used for closing cached connections */
- struct Curl_easy *closure_handle;
- struct Curl_multi *multi; /* Optional, set if cache belongs to multi */
+ struct Curl_llist shutdowns; /* The connections being shut down */
+ struct Curl_easy *idata; /* internal handle used for discard */
+ struct Curl_multi *multi; /* != NULL iff pool belongs to multi */
+ struct Curl_share *share; /* != NULL iff pool belongs to share */
+ Curl_cpool_disconnect_cb *disconnect_cb;
+ BIT(locked);
};
-#define BUNDLE_NO_MULTIUSE -1
-#define BUNDLE_UNKNOWN 0 /* initial value */
-#define BUNDLE_MULTIPLEX 2
-
-#ifdef DEBUGBUILD
-/* 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 */
-};
-
-/* Init the cache, pass multi only if cache is owned by it.
+/* Init the pool, pass multi only if pool is owned by it.
* returns 1 on error, 0 is fine.
*/
-int Curl_conncache_init(struct conncache *,
- struct Curl_multi *multi,
- size_t 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);
+int Curl_cpool_init(struct cpool *cpool,
+ Curl_cpool_disconnect_cb *disconnect_cb,
+ struct Curl_multi *multi,
+ struct Curl_share *share,
+ size_t size);
+
+/* Destroy all connections and free all members */
+void Curl_cpool_destroy(struct cpool *connc);
+
+/* Init the transfer to be used within its connection pool.
+ * Assigns `data->id`. */
+void Curl_cpool_xfer_init(struct Curl_easy *data);
/**
- * Tear down the connection. If `aborted` is FALSE, the connection
- * will be shut down first before discarding. If the shutdown
- * is not immediately complete, the connection
- * will be placed into the cache is shutdown queue.
+ * Get the connection with the given id from the transfer's pool.
*/
-void Curl_conncache_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- bool aborted);
+struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data,
+ curl_off_t conn_id);
+
+CURLcode Curl_cpool_add_conn(struct Curl_easy *data,
+ struct connectdata *conn) WARN_UNUSED_RESULT;
/**
- * Add sockets and POLLIN/OUT flags for connections handled by the cache.
+ * Return if the pool has reached its configured limits for adding
+ * the given connection. Will try to discard the oldest, idle
+ * connections to make space.
*/
-CURLcode Curl_conncache_add_pollfds(struct conncache *connc,
- struct curl_pollfds *cpfds);
-CURLcode Curl_conncache_add_waitfds(struct conncache *connc,
- struct curl_waitfds *cwfds);
+#define CPOOL_LIMIT_OK 0
+#define CPOOL_LIMIT_DEST 1
+#define CPOOL_LIMIT_TOTAL 2
+int Curl_cpool_check_limits(struct Curl_easy *data,
+ struct connectdata *conn);
+
+/* Return of conn is suitable. If so, stops iteration. */
+typedef bool Curl_cpool_conn_match_cb(struct connectdata *conn,
+ void *userdata);
+
+/* Act on the result of the find, may override it. */
+typedef bool Curl_cpool_done_match_cb(bool result, void *userdata);
+
+/**
+ * Find a connection in the pool matching `destination`.
+ * All callbacks are invoked while the pool's lock is held.
+ * @param data current transfer
+ * @param destination match agaonst `conn->destination` in pool
+ * @param dest_len destination length, including terminating NUL
+ * @param conn_cb must be present, called for each connection in the
+ * bundle until it returns TRUE
+ * @param result_cb if not NULL, is called at the end with the result
+ * of the `conn_cb` or FALSE if never called.
+ * @return combined result of last conn_db and result_cb or FALSE if no
+ connections were present.
+ */
+bool Curl_cpool_find(struct Curl_easy *data,
+ const char *destination, size_t dest_len,
+ Curl_cpool_conn_match_cb *conn_cb,
+ Curl_cpool_done_match_cb *done_cb,
+ void *userdata);
+
+/*
+ * A connection (already in the pool) is now idle. Do any
+ * cleanups in regard to the pool's limits.
+ *
+ * Return TRUE if idle connection kept in pool, FALSE if closed.
+ */
+bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
+ struct connectdata *conn);
/**
- * Perform maintenance on connections in the cache. Specifically,
+ * Remove the connection from the pool and tear it down.
+ * If `aborted` is FALSE, the connection will be shut down first
+ * before closing and destroying it.
+ * If the shutdown is not immediately complete, the connection
+ * will be placed into the pool's shutdown queue.
+ */
+void Curl_cpool_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool aborted);
+
+/**
+ * This function scans the data's connection pool for half-open/dead
+ * connections, closes and removes them.
+ * The cleanup is done at most once per second.
+ *
+ * When called, this transfer has no connection attached.
+ */
+void Curl_cpool_prune_dead(struct Curl_easy *data);
+
+/**
+ * Perform upkeep actions on connections in the transfer's pool.
+ */
+CURLcode Curl_cpool_upkeep(void *data);
+
+typedef void Curl_cpool_conn_do_cb(struct connectdata *conn,
+ struct Curl_easy *data,
+ void *cbdata);
+
+/**
+ * Invoke the callback on the pool's connection with the
+ * given connection id (if it exists).
+ */
+void Curl_cpool_do_by_id(struct Curl_easy *data,
+ curl_off_t conn_id,
+ Curl_cpool_conn_do_cb *cb, void *cbdata);
+
+/**
+ * Invoked the callback for the given data + connection under the
+ * connection pool's lock.
+ * The callback is always invoked, even if the transfer has no connection
+ * pool associated.
+ */
+void Curl_cpool_do_locked(struct Curl_easy *data,
+ struct connectdata *conn,
+ Curl_cpool_conn_do_cb *cb, void *cbdata);
+
+/**
+ * Add sockets and POLLIN/OUT flags for connections handled by the pool.
+ */
+CURLcode Curl_cpool_add_pollfds(struct cpool *connc,
+ struct curl_pollfds *cpfds);
+CURLcode Curl_cpool_add_waitfds(struct cpool *connc,
+ struct curl_waitfds *cwfds);
+
+/**
+ * Perform maintenance on connections in the pool. Specifically,
* progress the shutdown of connections in the queue.
*/
-void Curl_conncache_multi_perform(struct Curl_multi *multi);
+void Curl_cpool_multi_perform(struct Curl_multi *multi);
+
+void Curl_cpool_multi_socket(struct Curl_multi *multi,
+ curl_socket_t s, int ev_bitmask);
-void Curl_conncache_multi_socket(struct Curl_multi *multi,
- curl_socket_t s, int ev_bitmask);
-void Curl_conncache_multi_close_all(struct Curl_multi *multi);
#endif /* HEADER_CURL_CONNCACHE_H */
diff --git a/libs/libcurl/src/connect.c b/libs/libcurl/src/connect.c
index 943551d321..651b7ff467 100644
--- a/libs/libcurl/src/connect.c
+++ b/libs/libcurl/src/connect.c
@@ -208,31 +208,6 @@ bool Curl_shutdown_started(struct Curl_easy *data, int sockindex)
return (pt->tv_sec > 0) || (pt->tv_usec > 0);
}
-/* 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,
- struct ip_quadruple *ip)
-{
- if(ip)
- data->info.primary = *ip;
- else {
- memset(&data->info.primary, 0, sizeof(data->info.primary));
- data->info.primary.remote_port = -1;
- data->info.primary.local_port = -1;
- }
- 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_remote_port = conn->remote_port;
- data->info.used_proxy =
-#ifdef CURL_DISABLE_PROXY
- 0
-#else
- conn->bits.proxy
-#endif
- ;
-}
-
static const struct Curl_addrinfo *
addr_first_match(const struct Curl_addrinfo *addr, int family)
{
@@ -312,23 +287,6 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
return FALSE;
}
-struct connfind {
- curl_off_t 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.
@@ -345,30 +303,19 @@ curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
* - 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) {
+ if(data->state.lastconnect_id != -1) {
+ struct connectdata *conn;
+
+ conn = Curl_cpool_get_conn(data, data->state.lastconnect_id);
+ if(!conn) {
data->state.lastconnect_id = -1;
return CURL_SOCKET_BAD;
}
- c = find.found;
if(connp)
/* only store this if the caller cares for it */
- *connp = c;
- return c->sock[FIRSTSOCKET];
+ *connp = conn;
+ return conn->sock[FIRSTSOCKET];
}
return CURL_SOCKET_BAD;
}
@@ -634,7 +581,7 @@ static CURLcode baller_connect(struct Curl_cfilter *cf,
baller->is_done = TRUE;
}
else if(Curl_timediff(*now, baller->started) >= baller->timeoutms) {
- infof(data, "%s connect timeout after %" CURL_FORMAT_TIMEDIFF_T
+ infof(data, "%s connect timeout after %" FMT_TIMEDIFF_T
"ms, move on!", baller->name, baller->timeoutms);
#if defined(ETIMEDOUT)
baller->error = ETIMEDOUT;
@@ -725,7 +672,7 @@ evaluate:
/* Nothing connected, check the time before we might
* start new ballers or return ok. */
if((ongoing || not_started) && Curl_timeleft(data, &now, TRUE) < 0) {
- failf(data, "Connection timeout after %" CURL_FORMAT_CURL_OFF_T " ms",
+ failf(data, "Connection timeout after %" FMT_OFF_T " ms",
Curl_timediff(now, data->progress.t_startsingle));
return CURLE_OPERATION_TIMEDOUT;
}
@@ -748,8 +695,7 @@ evaluate:
CURL_TRC_CF(data, cf, "%s done", baller->name);
}
else {
- CURL_TRC_CF(data, cf, "%s starting (timeout=%"
- CURL_FORMAT_TIMEDIFF_T "ms)",
+ CURL_TRC_CF(data, cf, "%s starting (timeout=%" FMT_TIMEDIFF_T "ms)",
baller->name, baller->timeoutms);
++ongoing;
++added;
@@ -794,7 +740,7 @@ evaluate:
hostname = conn->host.name;
failf(data, "Failed to connect to %s port %u after "
- "%" CURL_FORMAT_TIMEDIFF_T " ms: %s",
+ "%" FMT_TIMEDIFF_T " ms: %s",
hostname, conn->primary.remote_port,
Curl_timediff(now, data->progress.t_startsingle),
curl_easy_strerror(result));
@@ -821,9 +767,9 @@ static CURLcode start_connect(struct Curl_cfilter *cf,
struct cf_he_ctx *ctx = cf->ctx;
struct connectdata *conn = cf->conn;
CURLcode result = CURLE_COULDNT_CONNECT;
- int ai_family0, ai_family1;
+ int ai_family0 = 0, ai_family1 = 0;
timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
- const struct Curl_addrinfo *addr0, *addr1;
+ const struct Curl_addrinfo *addr0 = NULL, *addr1 = NULL;
if(timeout_ms < 0) {
/* a precaution, no need to continue if time already is up */
@@ -842,33 +788,31 @@ static CURLcode start_connect(struct Curl_cfilter *cf,
* the 2 connect attempt ballers to try different families, if possible.
*
*/
- if(conn->ip_version == CURL_IPRESOLVE_WHATEVER) {
- /* any IP version is allowed */
- ai_family0 = remotehost->addr?
- remotehost->addr->ai_family : 0;
+ if(conn->ip_version == CURL_IPRESOLVE_V6) {
#ifdef USE_IPV6
- ai_family1 = ai_family0 == AF_INET6 ?
- AF_INET : AF_INET6;
-#else
- ai_family1 = AF_UNSPEC;
+ ai_family0 = AF_INET6;
+ addr0 = addr_first_match(remotehost->addr, ai_family0);
#endif
}
+ else if(conn->ip_version == CURL_IPRESOLVE_V4) {
+ ai_family0 = AF_INET;
+ addr0 = addr_first_match(remotehost->addr, ai_family0);
+ }
else {
- /* only one IP version is allowed */
- ai_family0 = (conn->ip_version == CURL_IPRESOLVE_V4) ?
- AF_INET :
-#ifdef USE_IPV6
- AF_INET6;
-#else
- AF_UNSPEC;
-#endif
- ai_family1 = AF_UNSPEC;
+ /* no user preference, we try ipv6 always first when available */
+ ai_family0 = AF_INET6;
+ addr0 = addr_first_match(remotehost->addr, ai_family0);
+ /* next candidate is ipv4 */
+ ai_family1 = AF_INET;
+ addr1 = addr_first_match(remotehost->addr, ai_family1);
+ /* no ip address families, probably AF_UNIX or something, use the
+ * address family given to us */
+ if(!addr1 && !addr0 && remotehost->addr) {
+ ai_family0 = remotehost->addr->ai_family;
+ addr0 = addr_first_match(remotehost->addr, ai_family0);
+ }
}
- /* Get the first address in the list that matches the family,
- * this might give NULL, if we do not have any matches. */
- addr0 = addr_first_match(remotehost->addr, ai_family0);
- addr1 = addr_first_match(remotehost->addr, ai_family1);
if(!addr0 && addr1) {
/* switch around, so a single baller always uses addr0 */
addr0 = addr1;
@@ -887,8 +831,7 @@ static CURLcode start_connect(struct Curl_cfilter *cf,
timeout_ms, EXPIRE_DNS_PER_NAME);
if(result)
return result;
- CURL_TRC_CF(data, cf, "created %s (timeout %"
- CURL_FORMAT_TIMEDIFF_T "ms)",
+ CURL_TRC_CF(data, cf, "created %s (timeout %" FMT_TIMEDIFF_T "ms)",
ctx->baller[0]->name, ctx->baller[0]->timeoutms);
if(addr1) {
/* second one gets a delayed start */
@@ -899,8 +842,7 @@ static CURLcode start_connect(struct Curl_cfilter *cf,
timeout_ms, EXPIRE_DNS_PER_NAME2);
if(result)
return result;
- CURL_TRC_CF(data, cf, "created %s (timeout %"
- CURL_FORMAT_TIMEDIFF_T "ms)",
+ CURL_TRC_CF(data, cf, "created %s (timeout %" FMT_TIMEDIFF_T "ms)",
ctx->baller[1]->name, ctx->baller[1]->timeoutms);
Curl_expire(data, data->set.happy_eyeballs_timeout,
EXPIRE_HAPPY_EYEBALLS);
@@ -1020,8 +962,6 @@ static CURLcode cf_he_connect(struct Curl_cfilter *cf,
cf->next = ctx->winner->cf;
ctx->winner->cf = NULL;
cf_he_ctx_clear(cf, data);
- Curl_conn_cf_cntrl(cf->next, data, TRUE,
- CF_CTRL_CONN_INFO_UPDATE, 0, NULL);
if(cf->conn->handler->protocol & PROTO_FAMILY_SSH)
Curl_pgrsTime(data, TIMER_APPCONNECT); /* we are connected already */
diff --git a/libs/libcurl/src/connect.h b/libs/libcurl/src/connect.h
index 33ba201545..c9a12188c4 100644
--- a/libs/libcurl/src/connect.h
+++ b/libs/libcurl/src/connect.h
@@ -72,9 +72,6 @@ curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
char *addr, int *port);
-void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn,
- struct ip_quadruple *ip);
-
/*
* Curl_conncontrol() marks the end of a connection/stream. The 'closeit'
* argument specifies if it is the end of a connection or a stream.
diff --git a/libs/libcurl/src/content_encoding.c b/libs/libcurl/src/content_encoding.c
index 80c71afa72..fb8cbef1db 100644
--- a/libs/libcurl/src/content_encoding.c
+++ b/libs/libcurl/src/content_encoding.c
@@ -79,7 +79,7 @@
#define GZIP_MAGIC_1 0x8b
/* gzip flag byte */
-#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
+#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 filename present */
@@ -909,18 +909,18 @@ static CURLcode error_do_write(struct Curl_easy *data,
struct Curl_cwriter *writer, int type,
const char *buf, size_t nbytes)
{
- char all[256];
- (void)Curl_all_content_encodings(all, sizeof(all));
-
(void) writer;
(void) buf;
(void) nbytes;
if(!(type & CLIENTWRITE_BODY) || !nbytes)
return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
-
- failf(data, "Unrecognized content encoding type. "
- "libcurl understands %s content encodings.", all);
+ else {
+ char all[256];
+ (void)Curl_all_content_encodings(all, sizeof(all));
+ failf(data, "Unrecognized content encoding type. "
+ "libcurl understands %s content encodings.", all);
+ }
return CURLE_BAD_CONTENT_ENCODING;
}
diff --git a/libs/libcurl/src/cookie.c b/libs/libcurl/src/cookie.c
index a098c17e82..07dedad378 100644
--- a/libs/libcurl/src/cookie.c
+++ b/libs/libcurl/src/cookie.c
@@ -1028,6 +1028,8 @@ Curl_cookie_add(struct Curl_easy *data,
* must also check that the data handle is not NULL since the psl code will
* dereference it.
*/
+ DEBUGF(infof(data, "PSL check set-cookie '%s' for domain=%s in %s",
+ co->name, co->domain, domain));
if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) {
bool acceptable = FALSE;
char lcase[256];
@@ -1054,6 +1056,9 @@ Curl_cookie_add(struct Curl_easy *data,
return NULL;
}
}
+#else
+ DEBUGF(infof(data, "NO PSL to check set-cookie '%s' for domain=%s in %s",
+ co->name, co->domain, domain));
#endif
/* A non-secure cookie may not overlay an existing secure cookie. */
@@ -1165,7 +1170,7 @@ Curl_cookie_add(struct Curl_easy *data,
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,
+ "expire %" FMT_OFF_T,
replace_old?"Replaced":"Added", co->name, co->value,
co->domain, co->path, co->expires);
@@ -1584,7 +1589,7 @@ static char *get_netscape_format(const struct Cookie *co)
"%s\t" /* tailmatch */
"%s\t" /* path */
"%s\t" /* secure */
- "%" CURL_FORMAT_CURL_OFF_T "\t" /* expires */
+ "%" FMT_OFF_T "\t" /* expires */
"%s\t" /* name */
"%s", /* value */
co->httponly?"#HttpOnly_":"",
diff --git a/libs/libcurl/src/curl_addrinfo.c b/libs/libcurl/src/curl_addrinfo.c
index 69437b70fd..463fc005c2 100644
--- a/libs/libcurl/src/curl_addrinfo.c
+++ b/libs/libcurl/src/curl_addrinfo.c
@@ -571,7 +571,7 @@ curl_dbg_getaddrinfo(const char *hostname,
#if defined(HAVE_GETADDRINFO) && defined(USE_RESOLVE_ON_IPS)
/*
- * Work-arounds the sin6_port is always zero bug on iOS 9.3.2 and Mac OS X
+ * Work-arounds the sin6_port is always zero bug on iOS 9.3.2 and macOS
* 10.11.5.
*/
void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port)
diff --git a/libs/libcurl/src/curl_config.h.cmake b/libs/libcurl/src/curl_config.h.cmake
index c7ee19b047..8569f463ea 100644
--- a/libs/libcurl/src/curl_config.h.cmake
+++ b/libs/libcurl/src/curl_config.h.cmake
@@ -89,6 +89,9 @@
/* disables HTTP */
#cmakedefine CURL_DISABLE_HTTP 1
+/* disabled all HTTP authentication methods */
+#cmakedefine CURL_DISABLE_HTTP_AUTH 1
+
/* disables IMAP */
#cmakedefine CURL_DISABLE_IMAP 1
@@ -131,6 +134,12 @@
/* disables RTSP */
#cmakedefine CURL_DISABLE_RTSP 1
+/* disables SHA-512/256 hash algorithm */
+#cmakedefine CURL_DISABLE_SHA512_256 1
+
+/* disabled shuffle DNS feature */
+#cmakedefine CURL_DISABLE_SHUFFLE_DNS 1
+
/* disables SMB */
#cmakedefine CURL_DISABLE_SMB 1
@@ -304,9 +313,6 @@
/* if you have the GNU gssapi libraries */
#cmakedefine HAVE_GSSGNU 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
@@ -632,9 +638,6 @@
/* 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
@@ -677,7 +680,7 @@ ${SIZEOF_TIME_T_CODE}
/* 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 */
+/* Define if you want to enable Win32 threaded DNS lookup */
#cmakedefine USE_THREADS_WIN32 1
/* if GnuTLS is enabled */
@@ -692,6 +695,9 @@ ${SIZEOF_TIME_T_CODE}
/* if BearSSL is enabled */
#cmakedefine USE_BEARSSL 1
+/* if Rustls is enabled */
+#cmakedefine USE_RUSTLS 1
+
/* if wolfSSL is enabled */
#cmakedefine USE_WOLFSSL 1
@@ -707,6 +713,9 @@ ${SIZEOF_TIME_T_CODE}
/* if libssh2 is in use */
#cmakedefine USE_LIBSSH2 1
+/* if wolfssh is in use */
+#cmakedefine USE_WOLFSSH 1
+
/* if libpsl is in use */
#cmakedefine USE_LIBPSL 1
@@ -722,6 +731,12 @@ ${SIZEOF_TIME_T_CODE}
/* if GSASL is in use */
#cmakedefine USE_GSASL 1
+/* if libuv is in use */
+#cmakedefine USE_LIBUV 1
+
+/* Define to 1 if you have the <uv.h> header file. */
+#cmakedefine HAVE_UV_H 1
+
/* Define to 1 if you do not want the OpenSSL configuration to be loaded
automatically */
#cmakedefine CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG 1
@@ -765,11 +780,6 @@ ${SIZEOF_TIME_T_CODE}
/* 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}
diff --git a/libs/libcurl/src/curl_config.h.in b/libs/libcurl/src/curl_config.h.in
index af491b43b1..5d8c2978d6 100644
--- a/libs/libcurl/src/curl_config.h.in
+++ b/libs/libcurl/src/curl_config.h.in
@@ -9,7 +9,7 @@
/* Location of default ca bundle */
#undef CURL_CA_BUNDLE
-/* define "1" to use built in CA store of SSL library */
+/* define "1" to use built-in CA store of SSL library */
#undef CURL_CA_FALLBACK
/* Location of default ca path */
@@ -120,6 +120,9 @@
/* to disable RTSP */
#undef CURL_DISABLE_RTSP
+/* disable SHA-512/256 hash algorithm */
+#undef CURL_DISABLE_SHA512_256
+
/* disable DNS shuffling */
#undef CURL_DISABLE_SHUFFLE_DNS
@@ -487,9 +490,6 @@
/* if you have opendir */
#undef HAVE_OPENDIR
-/* 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
@@ -499,9 +499,6 @@
/* Define to 1 if you have the <openssl/pem.h> header file. */
#undef HAVE_OPENSSL_PEM_H
-/* if you have the functions OSSL_QUIC_client_method */
-#undef HAVE_OPENSSL_QUIC
-
/* Define to 1 if you have the <openssl/rsa.h> header file. */
#undef HAVE_OPENSSL_RSA_H
@@ -729,6 +726,9 @@
/* Define this if time_t is unsigned */
#undef HAVE_TIME_T_UNSIGNED
+/* Define to 1 if you have the <unicode/uidna.h> header file. */
+#undef HAVE_UNICODE_UIDNA_H
+
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
@@ -741,6 +741,9 @@
/* Define to 1 if you have the <utime.h> header file. */
#undef HAVE_UTIME_H
+/* Define to 1 if you have the <uv.h> header file. */
+#undef HAVE_UV_H
+
/* Define to 1 if you have the <wolfssh/ssh.h> header file. */
#undef HAVE_WOLFSSH_SSH_H
@@ -810,9 +813,6 @@
/* 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
@@ -845,6 +845,9 @@
/* if AmiSSL is in use */
#undef USE_AMISSL
+/* if AppleIDN */
+#undef USE_APPLE_IDN
+
/* Define to enable c-ares support */
#undef USE_ARES
@@ -881,8 +884,8 @@
/* if libssh2 is in use */
#undef USE_LIBSSH2
-/* If you want to build curl with the built-in manual */
-#undef USE_MANUAL
+/* if libuv is in use */
+#undef USE_LIBUV
/* if mbedTLS is enabled */
#undef USE_MBEDTLS
@@ -929,7 +932,7 @@
/* if quiche is in use */
#undef USE_QUICHE
-/* if rustls is enabled */
+/* if Rustls is enabled */
#undef USE_RUSTLS
/* to enable Windows native SSL/TLS support */
@@ -950,7 +953,7 @@
/* Use Unix domain sockets */
#undef USE_UNIX_SOCKETS
-/* enable websockets support */
+/* enable WebSockets support */
#undef USE_WEBSOCKETS
/* Define to 1 if you are building a Windows target with crypto API support.
diff --git a/libs/libcurl/src/curl_des.c b/libs/libcurl/src/curl_des.c
index ce568e6e13..51fc8e6ccf 100644
--- a/libs/libcurl/src/curl_des.c
+++ b/libs/libcurl/src/curl_des.c
@@ -24,10 +24,10 @@
#include "curl_setup.h"
-#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \
- (defined(USE_GNUTLS) || \
- defined(USE_SECTRANSP) || \
- defined(USE_OS400CRYPTO) || \
+#if defined(USE_CURL_NTLM_CORE) && \
+ (defined(USE_GNUTLS) || \
+ defined(USE_SECTRANSP) || \
+ defined(USE_OS400CRYPTO) || \
defined(USE_WIN32_CRYPTO))
#include "curl_des.h"
diff --git a/libs/libcurl/src/curl_des.h b/libs/libcurl/src/curl_des.h
index 520faf800a..0d29b94f77 100644
--- a/libs/libcurl/src/curl_des.h
+++ b/libs/libcurl/src/curl_des.h
@@ -26,10 +26,10 @@
#include "curl_setup.h"
-#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \
- (defined(USE_GNUTLS) || \
- defined(USE_SECTRANSP) || \
- defined(USE_OS400CRYPTO) || \
+#if defined(USE_CURL_NTLM_CORE) && \
+ (defined(USE_GNUTLS) || \
+ defined(USE_SECTRANSP) || \
+ defined(USE_OS400CRYPTO) || \
defined(USE_WIN32_CRYPTO))
/* Applies odd parity to the given byte array */
diff --git a/libs/libcurl/src/curl_fnmatch.h b/libs/libcurl/src/curl_fnmatch.h
index c0b92f7c4f..593771c123 100644
--- a/libs/libcurl/src/curl_fnmatch.h
+++ b/libs/libcurl/src/curl_fnmatch.h
@@ -31,7 +31,7 @@
/* 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.
+ * please note that there is not implemented UTF/Unicode support.
*
* Implemented features:
* '?' notation, does not match UTF characters
diff --git a/libs/libcurl/src/curl_gethostname.c b/libs/libcurl/src/curl_gethostname.c
index c207e7bd8f..f55514acad 100644
--- a/libs/libcurl/src/curl_gethostname.c
+++ b/libs/libcurl/src/curl_gethostname.c
@@ -39,15 +39,6 @@
*
* 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 hostname.
- *
- * For libcurl static library release builds no overriding takes place.
*/
int Curl_gethostname(char * const name, GETHOSTNAME_TYPE_ARG2 namelen)
@@ -68,7 +59,10 @@ int Curl_gethostname(char * const name, GETHOSTNAME_TYPE_ARG2 namelen)
/* Override hostname when environment variable CURL_GETHOSTNAME is set */
const char *force_hostname = getenv("CURL_GETHOSTNAME");
if(force_hostname) {
- strncpy(name, force_hostname, namelen - 1);
+ if(strlen(force_hostname) < (size_t)namelen)
+ strcpy(name, force_hostname);
+ else
+ return 1; /* can't do it */
err = 0;
}
else {
@@ -78,9 +72,6 @@ int Curl_gethostname(char * const name, GETHOSTNAME_TYPE_ARG2 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);
diff --git a/libs/libcurl/src/curl_multibyte.h b/libs/libcurl/src/curl_multibyte.h
index 814d900c09..449d0ac97f 100644
--- a/libs/libcurl/src/curl_multibyte.h
+++ b/libs/libcurl/src/curl_multibyte.h
@@ -39,12 +39,12 @@ char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w);
* 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.
+ * to differentiate Unicode from non-Unicode builds.
*
- * In the case of a non-UNICODE build the tchar strings are char strings that
+ * 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.
+ * significance of the conversion functions is primarily for Unicode builds.
*
* Allocated memory should be free'd with curlx_unicodefree().
*
diff --git a/libs/libcurl/src/curl_ntlm_core.c b/libs/libcurl/src/curl_ntlm_core.c
index 7d2c47dde4..528370ee84 100644
--- a/libs/libcurl/src/curl_ntlm_core.c
+++ b/libs/libcurl/src/curl_ntlm_core.c
@@ -57,9 +57,14 @@
#if !defined(OPENSSL_NO_DES) && !defined(OPENSSL_NO_DEPRECATED_3_0)
#define USE_OPENSSL_DES
#endif
+#elif defined(USE_WOLFSSL)
+ #include <wolfssl/options.h>
+ #if !defined(NO_DES3)
+ #define USE_OPENSSL_DES
+ #endif
#endif
-#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL)
+#if defined(USE_OPENSSL_DES)
#if defined(USE_OPENSSL)
# include <openssl/des.h>
@@ -67,7 +72,6 @@
# 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>
@@ -148,7 +152,7 @@ static void extend_key_56_to_64(const unsigned char *key_56, char *key)
}
#endif
-#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL)
+#if defined(USE_OPENSSL_DES)
/*
* Turns a 56-bit key into a 64-bit, odd parity key and sets the key. The
* key schedule ks is also set.
@@ -313,7 +317,7 @@ 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)
+#if defined(USE_OPENSSL_DES)
DES_key_schedule ks;
setup_des_key(keys, DESKEY(ks));
@@ -367,7 +371,7 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password,
{
/* Create LanManager hashed password. */
-#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL)
+#if defined(USE_OPENSSL_DES)
DES_key_schedule ks;
setup_des_key(pw, DESKEY(ks));
@@ -534,13 +538,13 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_hash(const char *user, size_t userlen,
/*
* Curl_ntlm_core_mk_ntlmv2_resp()
*
- * This creates the NTLMv2 response as set in the ntlm type-3 message.
+ * This creates the NTLMv2 response as set in the NTLM type-3 message.
*
* Parameters:
*
- * ntlmv2hash [in] - The ntlmv2 hash (16 bytes)
+ * 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
+ * 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.
@@ -629,11 +633,11 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash,
/*
* Curl_ntlm_core_mk_lmv2_resp()
*
- * This creates the LMv2 response as used in the ntlm type-3 message.
+ * This creates the LMv2 response as used in the NTLM type-3 message.
*
* Parameters:
*
- * ntlmv2hash [in] - The ntlmv2 hash (16 bytes)
+ * 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)
@@ -657,7 +661,7 @@ CURLcode Curl_ntlm_core_mk_lmv2_resp(unsigned char *ntlmv2hash,
if(result)
return result;
- /* Concatenate the HMAC MD5 output with the client nonce */
+ /* Concatenate the HMAC MD5 output with the client nonce */
memcpy(lmresp, hmac_output, 16);
memcpy(lmresp + 16, challenge_client, 8);
diff --git a/libs/libcurl/src/curl_printf.h b/libs/libcurl/src/curl_printf.h
index 35a3bf81fc..349735d000 100644
--- a/libs/libcurl/src/curl_printf.h
+++ b/libs/libcurl/src/curl_printf.h
@@ -29,6 +29,10 @@
* *rintf() functions.
*/
+#ifndef CURL_TEMP_PRINTF
+#error "CURL_TEMP_PRINTF must be set before including curl/mprintf.h"
+#endif
+
#include <curl/mprintf.h>
#define MERR_OK 0
@@ -40,7 +44,6 @@
# undef msnprintf
# undef vprintf
# undef vfprintf
-# undef vsnprintf
# undef mvsnprintf
# undef aprintf
# undef vaprintf
diff --git a/libs/libcurl/src/curl_range.c b/libs/libcurl/src/curl_range.c
index 30e37048e6..3ab7f98b3c 100644
--- a/libs/libcurl/src/curl_range.c
+++ b/libs/libcurl/src/curl_range.c
@@ -55,15 +55,13 @@ CURLcode Curl_range(struct Curl_easy *data)
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));
+ DEBUGF(infof(data, "RANGE %" FMT_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));
+ DEBUGF(infof(data, "RANGE the last %" FMT_OFF_T " bytes", to));
}
else {
/* X-Y */
@@ -79,13 +77,12 @@ CURLcode Curl_range(struct Curl_easy *data)
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",
+ DEBUGF(infof(data, "RANGE from %" FMT_OFF_T
+ " getting %" FMT_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",
+ DEBUGF(infof(data, "range-download from %" FMT_OFF_T
+ " to %" FMT_OFF_T ", totally %" FMT_OFF_T " bytes",
from, to, data->req.maxdownload));
}
else
diff --git a/libs/libcurl/src/curl_rtmp.c b/libs/libcurl/src/curl_rtmp.c
index a8e5547d1d..b01e66284c 100644
--- a/libs/libcurl/src/curl_rtmp.c
+++ b/libs/libcurl/src/curl_rtmp.c
@@ -329,13 +329,14 @@ static ssize_t rtmp_recv(struct Curl_easy *data, int sockindex, char *buf,
}
static ssize_t rtmp_send(struct Curl_easy *data, int sockindex,
- const void *buf, size_t len, CURLcode *err)
+ const void *buf, size_t len, bool eos, CURLcode *err)
{
struct connectdata *conn = data->conn;
RTMP *r = conn->proto.rtmp;
ssize_t num;
(void)sockindex; /* unused */
+ (void)eos; /* unused */
num = RTMP_Write(r, (char *)buf, curlx_uztosi(len));
if(num < 0)
diff --git a/libs/libcurl/src/curl_setup.h b/libs/libcurl/src/curl_setup.h
index 68f32c7191..dc56ee9d0b 100644
--- a/libs/libcurl/src/curl_setup.h
+++ b/libs/libcurl/src/curl_setup.h
@@ -28,6 +28,9 @@
#define CURL_NO_OLDIES
#endif
+/* Tell "curl/curl.h" not to include "curl/mprintf.h" */
+#define CURL_SKIP_INCLUDE_MPRINTF
+
/* FIXME: Delete this once the warnings have been fixed. */
#if !defined(CURL_WARN_SIGN_CONVERSION)
#ifdef __GNUC__
@@ -99,6 +102,16 @@
# 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
/* Compatibility */
@@ -309,7 +322,7 @@
/* curl uses its own printf() function internally. It understands the GNU
* format. Use this format, so that is matches the GNU format attribute we
- * use with the mingw compiler, allowing it to verify them at compile-time.
+ * use with the MinGW compiler, allowing it to verify them at compile-time.
*/
#ifdef __MINGW32__
# undef CURL_FORMAT_CURL_OFF_T
@@ -335,6 +348,9 @@
#define CURL_PRINTF(fmt, arg)
#endif
+/* Override default printf mask check rules in "curl/mprintf.h" */
+#define CURL_TEMP_PRINTF CURL_PRINTF
+
/* Workaround for mainline llvm v16 and earlier missing a built-in macro
expected by macOS SDK v14 / Xcode v15 (2023) and newer.
gcc (as of v14) is also missing it. */
@@ -434,7 +450,7 @@
#endif
/*
- * Large file (>2Gb) support using WIN32 functions.
+ * Large file (>2Gb) support using Win32 functions.
*/
#ifdef USE_WIN32_LARGE_FILES
@@ -457,7 +473,7 @@
#endif
/*
- * Small file (<2Gb) support using WIN32 functions.
+ * Small file (<2Gb) support using Win32 functions.
*/
#ifdef USE_WIN32_SMALL_FILES
@@ -503,11 +519,11 @@
#endif
#if SIZEOF_CURL_SOCKET_T < 8
-# define CURL_FORMAT_SOCKET_T "d"
+# define FMT_SOCKET_T "d"
#elif defined(__MINGW32__)
-# define CURL_FORMAT_SOCKET_T "zd"
+# define FMT_SOCKET_T "zd"
#else
-# define CURL_FORMAT_SOCKET_T "qd"
+# define FMT_SOCKET_T "qd"
#endif
/*
@@ -555,10 +571,13 @@
# endif
# define CURL_UINT64_SUFFIX CURL_SUFFIX_CURL_OFF_TU
# define CURL_UINT64_C(val) CURL_CONC_MACROS(val,CURL_UINT64_SUFFIX)
-# define CURL_PRId64 CURL_FORMAT_CURL_OFF_T
-# define CURL_PRIu64 CURL_FORMAT_CURL_OFF_TU
+# define FMT_PRId64 CURL_FORMAT_CURL_OFF_T
+# define FMT_PRIu64 CURL_FORMAT_CURL_OFF_TU
#endif
+#define FMT_OFF_T CURL_FORMAT_CURL_OFF_T
+#define FMT_OFF_TU CURL_FORMAT_CURL_OFF_TU
+
#if (SIZEOF_TIME_T == 4)
# ifdef HAVE_TIME_T_UNSIGNED
# define TIME_T_MAX UINT_MAX
@@ -705,6 +724,11 @@
#define USE_SSL /* SSL support has been enabled */
#endif
+#if defined(USE_WOLFSSL) && defined(USE_GNUTLS)
+/* Avoid defining unprefixed wolfSSL SHA macros colliding with nettle ones */
+#define NO_OLD_WC_NAMES
+#endif
+
/* Single point where USE_SPNEGO definition might be defined */
#if !defined(CURL_DISABLE_NEGOTIATE_AUTH) && \
(defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI))
@@ -806,7 +830,7 @@
#if defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H)
# if defined(SOCKET) || defined(USE_WINSOCK)
-# error "WinSock and lwIP TCP/IP stack definitions shall not coexist!"
+# error "Winsock and lwIP TCP/IP stack definitions shall not coexist!"
# endif
#endif
@@ -844,7 +868,7 @@ Therefore we specify it explicitly. https://github.com/curl/curl/pull/258
#define FOPEN_WRITETEXT "wt"
#define FOPEN_APPENDTEXT "at"
#elif defined(__CYGWIN__)
-/* Cygwin has specific behavior we need to address when WIN32 is not defined.
+/* 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
diff --git a/libs/libcurl/src/curl_sha256.h b/libs/libcurl/src/curl_sha256.h
index 1f9d561386..18f7879f64 100644
--- a/libs/libcurl/src/curl_sha256.h
+++ b/libs/libcurl/src/curl_sha256.h
@@ -33,13 +33,8 @@
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
+#ifndef CURL_SHA256_DIGEST_LENGTH
+#define CURL_SHA256_DIGEST_LENGTH 32 /* fixed size */
#endif
CURLcode Curl_sha256it(unsigned char *outbuffer, const unsigned char *input,
diff --git a/libs/libcurl/src/curl_sha512_256.c b/libs/libcurl/src/curl_sha512_256.c
index 4697ffad6a..fc0d30d9d6 100644
--- a/libs/libcurl/src/curl_sha512_256.c
+++ b/libs/libcurl/src/curl_sha512_256.c
@@ -37,7 +37,7 @@
* * SecureTransport (Darwin)
* * mbedTLS
* * BearSSL
- * * rustls
+ * * Rustls
* Skip the backend if it does not support the required algorithm */
#if defined(USE_OPENSSL)
@@ -93,13 +93,13 @@
/**
* Size of the SHA-512/256 single processing block in bytes.
*/
-#define SHA512_256_BLOCK_SIZE 128
+#define CURL_SHA512_256_BLOCK_SIZE 128
/**
* Size of the SHA-512/256 resulting digest in bytes.
* This is the final digest size, not intermediate hash.
*/
-#define SHA512_256_DIGEST_SIZE SHA512_256_DIGEST_LENGTH
+#define CURL_SHA512_256_DIGEST_SIZE CURL_SHA512_256_DIGEST_LENGTH
/**
* Context type used for SHA-512/256 calculations
@@ -124,9 +124,9 @@ Curl_sha512_256_init(void *context)
if(EVP_DigestInit_ex(*ctx, EVP_sha512_256(), NULL)) {
/* Check whether the header and this file use the same numbers */
- DEBUGASSERT(EVP_MD_CTX_size(*ctx) == SHA512_256_DIGEST_SIZE);
+ DEBUGASSERT(EVP_MD_CTX_size(*ctx) == CURL_SHA512_256_DIGEST_SIZE);
/* Check whether the block size is correct */
- DEBUGASSERT(EVP_MD_CTX_block_size(*ctx) == SHA512_256_BLOCK_SIZE);
+ DEBUGASSERT(EVP_MD_CTX_block_size(*ctx) == CURL_SHA512_256_BLOCK_SIZE);
return CURLE_OK; /* Success */
}
@@ -163,7 +163,8 @@ Curl_sha512_256_update(void *context,
* Finalise SHA-512/256 calculation, return digest.
*
* @param context the calculation context
- * @param[out] digest set to the hash, must be #SHA512_256_DIGEST_SIZE bytes
+ * @param[out] digest set to the hash, must be #CURL_SHA512_256_DIGEST_SIZE
+ # bytes
* @return CURLE_OK if succeed,
* error code otherwise
*/
@@ -177,11 +178,11 @@ Curl_sha512_256_finish(unsigned char *digest,
#ifdef NEED_NETBSD_SHA512_256_WORKAROUND
/* Use a larger buffer to work around a bug in NetBSD:
https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 */
- unsigned char tmp_digest[SHA512_256_DIGEST_SIZE * 2];
+ unsigned char tmp_digest[CURL_SHA512_256_DIGEST_SIZE * 2];
ret = EVP_DigestFinal_ex(*ctx,
tmp_digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER;
if(ret == CURLE_OK)
- memcpy(digest, tmp_digest, SHA512_256_DIGEST_SIZE);
+ memcpy(digest, tmp_digest, CURL_SHA512_256_DIGEST_SIZE);
explicit_memset(tmp_digest, 0, sizeof(tmp_digest));
#else /* ! NEED_NETBSD_SHA512_256_WORKAROUND */
ret = EVP_DigestFinal_ex(*ctx, digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER;
@@ -195,6 +196,9 @@ Curl_sha512_256_finish(unsigned char *digest,
#elif defined(USE_GNUTLS_SHA512_256)
+#define CURL_SHA512_256_BLOCK_SIZE SHA512_256_BLOCK_SIZE
+#define CURL_SHA512_256_DIGEST_SIZE SHA512_256_DIGEST_SIZE
+
/**
* Context type used for SHA-512/256 calculations
*/
@@ -212,7 +216,7 @@ Curl_sha512_256_init(void *context)
Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
/* Check whether the header and this file use the same numbers */
- DEBUGASSERT(SHA512_256_DIGEST_LENGTH == SHA512_256_DIGEST_SIZE);
+ DEBUGASSERT(CURL_SHA512_256_DIGEST_LENGTH == CURL_SHA512_256_DIGEST_SIZE);
sha512_256_init(ctx);
@@ -247,7 +251,8 @@ Curl_sha512_256_update(void *context,
* Finalise SHA-512/256 calculation, return digest.
*
* @param context the calculation context
- * @param[out] digest set to the hash, must be #SHA512_256_DIGEST_SIZE bytes
+ * @param[out] digest set to the hash, must be #CURL_SHA512_256_DIGEST_SIZE
+ # bytes
* @return always CURLE_OK
*/
static CURLcode
@@ -256,7 +261,8 @@ Curl_sha512_256_finish(unsigned char *digest,
{
Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
- sha512_256_digest(ctx, (size_t)SHA512_256_DIGEST_SIZE, (uint8_t *)digest);
+ sha512_256_digest(ctx,
+ (size_t)CURL_SHA512_256_DIGEST_SIZE, (uint8_t *)digest);
return CURLE_OK;
}
@@ -360,7 +366,7 @@ MHDx_rotr64(curl_uint64_t value, unsigned int bits)
* Size of the SHA-512/256 resulting digest in bytes
* This is the final digest size, not intermediate hash.
*/
-#define SHA512_256_DIGEST_SIZE \
+#define CURL_SHA512_256_DIGEST_SIZE \
(SHA512_256_DIGEST_SIZE_WORDS * SHA512_256_BYTES_IN_WORD)
/**
@@ -371,7 +377,7 @@ MHDx_rotr64(curl_uint64_t value, unsigned int bits)
/**
* Size of the SHA-512/256 single processing block in bytes.
*/
-#define SHA512_256_BLOCK_SIZE (SHA512_256_BLOCK_SIZE_BITS / 8)
+#define CURL_SHA512_256_BLOCK_SIZE (SHA512_256_BLOCK_SIZE_BITS / 8)
/**
* Size of the SHA-512/256 single processing block in words.
@@ -425,7 +431,7 @@ MHDx_sha512_256_init(void *context)
struct mhdx_sha512_256ctx *const ctx = (struct mhdx_sha512_256ctx *) context;
/* Check whether the header and this file use the same numbers */
- DEBUGASSERT(SHA512_256_DIGEST_LENGTH == SHA512_256_DIGEST_SIZE);
+ DEBUGASSERT(CURL_SHA512_256_DIGEST_LENGTH == CURL_SHA512_256_DIGEST_SIZE);
DEBUGASSERT(sizeof(curl_uint64_t) == 8);
@@ -453,7 +459,7 @@ MHDx_sha512_256_init(void *context)
* Base of the SHA-512/256 transformation.
* Gets a full 128 bytes block of data and updates hash values;
* @param H hash values
- * @param data the data buffer with #SHA512_256_BLOCK_SIZE bytes block
+ * @param data the data buffer with #CURL_SHA512_256_BLOCK_SIZE bytes block
*/
static void
MHDx_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS],
@@ -636,9 +642,9 @@ MHDx_sha512_256_update(void *context,
if(0 == length)
return CURLE_OK; /* Shortcut, do nothing */
- /* Note: (count & (SHA512_256_BLOCK_SIZE-1))
- equals (count % SHA512_256_BLOCK_SIZE) for this block size. */
- bytes_have = (unsigned int) (ctx->count & (SHA512_256_BLOCK_SIZE - 1));
+ /* Note: (count & (CURL_SHA512_256_BLOCK_SIZE-1))
+ equals (count % CURL_SHA512_256_BLOCK_SIZE) for this block size. */
+ bytes_have = (unsigned int) (ctx->count & (CURL_SHA512_256_BLOCK_SIZE - 1));
ctx->count += length;
if(length > ctx->count)
ctx->count_bits_hi += 1U << 3; /* Value wrap */
@@ -646,7 +652,7 @@ MHDx_sha512_256_update(void *context,
ctx->count &= CURL_UINT64_C(0x1FFFFFFFFFFFFFFF);
if(0 != bytes_have) {
- unsigned int bytes_left = SHA512_256_BLOCK_SIZE - bytes_have;
+ unsigned int bytes_left = CURL_SHA512_256_BLOCK_SIZE - bytes_have;
if(length >= bytes_left) {
/* Combine new data with data in the buffer and process the full
block. */
@@ -660,12 +666,12 @@ MHDx_sha512_256_update(void *context,
}
}
- while(SHA512_256_BLOCK_SIZE <= length) {
+ while(CURL_SHA512_256_BLOCK_SIZE <= length) {
/* Process any full blocks of new data directly,
without copying to the buffer. */
MHDx_sha512_256_transform(ctx->H, data);
- data += SHA512_256_BLOCK_SIZE;
- length -= SHA512_256_BLOCK_SIZE;
+ data += CURL_SHA512_256_BLOCK_SIZE;
+ length -= CURL_SHA512_256_BLOCK_SIZE;
}
if(0 != length) {
@@ -694,7 +700,8 @@ MHDx_sha512_256_update(void *context,
* Finalise SHA-512/256 calculation, return digest.
*
* @param context the calculation context
- * @param[out] digest set to the hash, must be #SHA512_256_DIGEST_SIZE bytes
+ * @param[out] digest set to the hash, must be #CURL_SHA512_256_DIGEST_SIZE
+ # bytes
* @return always CURLE_OK
*/
static CURLcode
@@ -712,9 +719,9 @@ MHDx_sha512_256_finish(unsigned char *digest,
not change the amount of hashed data. */
num_bits = ctx->count << 3;
- /* Note: (count & (SHA512_256_BLOCK_SIZE-1))
- equals (count % SHA512_256_BLOCK_SIZE) for this block size. */
- bytes_have = (unsigned int) (ctx->count & (SHA512_256_BLOCK_SIZE - 1));
+ /* Note: (count & (CURL_SHA512_256_BLOCK_SIZE-1))
+ equals (count % CURL_SHA512_256_BLOCK_SIZE) for this block size. */
+ bytes_have = (unsigned int) (ctx->count & (CURL_SHA512_256_BLOCK_SIZE - 1));
/* Input data must be padded with a single bit "1", then with zeros and
the finally the length of data in bits must be added as the final bytes
@@ -728,12 +735,12 @@ MHDx_sha512_256_finish(unsigned char *digest,
processed when formed). */
((unsigned char *) ctx_buf)[bytes_have++] = 0x80U;
- if(SHA512_256_BLOCK_SIZE - bytes_have < SHA512_256_SIZE_OF_LEN_ADD) {
+ if(CURL_SHA512_256_BLOCK_SIZE - bytes_have < SHA512_256_SIZE_OF_LEN_ADD) {
/* No space in the current block to put the total length of message.
Pad the current block with zeros and process it. */
- if(bytes_have < SHA512_256_BLOCK_SIZE)
+ if(bytes_have < CURL_SHA512_256_BLOCK_SIZE)
memset(((unsigned char *) ctx_buf) + bytes_have, 0,
- SHA512_256_BLOCK_SIZE - bytes_have);
+ CURL_SHA512_256_BLOCK_SIZE - bytes_have);
/* Process the full block. */
MHDx_sha512_256_transform(ctx->H, ctx->buffer);
/* Start the new block. */
@@ -742,17 +749,17 @@ MHDx_sha512_256_finish(unsigned char *digest,
/* Pad the rest of the buffer with zeros. */
memset(((unsigned char *) ctx_buf) + bytes_have, 0,
- SHA512_256_BLOCK_SIZE - SHA512_256_SIZE_OF_LEN_ADD - bytes_have);
+ CURL_SHA512_256_BLOCK_SIZE - SHA512_256_SIZE_OF_LEN_ADD - bytes_have);
/* Put high part of number of bits in processed message and then lower
part of number of bits as big-endian values.
See FIPS PUB 180-4 section 5.1.2. */
/* Note: the target location is predefined and buffer is always aligned */
MHDX_PUT_64BIT_BE(((unsigned char *) ctx_buf) \
- + SHA512_256_BLOCK_SIZE \
+ + CURL_SHA512_256_BLOCK_SIZE \
- SHA512_256_SIZE_OF_LEN_ADD, \
ctx->count_bits_hi);
MHDX_PUT_64BIT_BE(((unsigned char *) ctx_buf) \
- + SHA512_256_BLOCK_SIZE \
+ + CURL_SHA512_256_BLOCK_SIZE \
- SHA512_256_SIZE_OF_LEN_ADD \
+ SHA512_256_BYTES_IN_WORD, \
num_bits);
@@ -841,9 +848,9 @@ const struct HMAC_params Curl_HMAC_SHA512_256[] = {
/* Context structure size. */
sizeof(Curl_sha512_256_ctx),
/* Maximum key length (bytes). */
- SHA512_256_BLOCK_SIZE,
+ CURL_SHA512_256_BLOCK_SIZE,
/* Result length (bytes). */
- SHA512_256_DIGEST_SIZE
+ CURL_SHA512_256_DIGEST_SIZE
}
};
diff --git a/libs/libcurl/src/curl_sha512_256.h b/libs/libcurl/src/curl_sha512_256.h
index 3acdc90a01..8e97fc55f2 100644
--- a/libs/libcurl/src/curl_sha512_256.h
+++ b/libs/libcurl/src/curl_sha512_256.h
@@ -33,7 +33,7 @@
extern const struct HMAC_params Curl_HMAC_SHA512_256[1];
-#define SHA512_256_DIGEST_LENGTH 32
+#define CURL_SHA512_256_DIGEST_LENGTH 32
CURLcode
Curl_sha512_256it(unsigned char *output, const unsigned char *input,
diff --git a/libs/libcurl/src/curl_sspi.c b/libs/libcurl/src/curl_sspi.c
index 3e83a9801e..a08957c7cc 100644
--- a/libs/libcurl/src/curl_sspi.c
+++ b/libs/libcurl/src/curl_sspi.c
@@ -52,10 +52,10 @@ typedef PSecurityFunctionTable (APIENTRY *INITSECURITYINTERFACE_FN)(VOID);
#endif
/* Handle of security.dll or secur32.dll, depending on Windows version */
-HMODULE s_hSecDll = NULL;
+HMODULE Curl_hSecDll = NULL;
/* Pointer to SSPI dispatch table */
-PSecurityFunctionTable s_pSecFn = NULL;
+PSecurityFunctionTable Curl_pSecFn = NULL;
/*
* Curl_sspi_global_init()
@@ -79,29 +79,29 @@ CURLcode Curl_sspi_global_init(void)
INITSECURITYINTERFACE_FN pInitSecurityInterface;
/* If security interface is not yet initialized try to do this */
- if(!s_hSecDll) {
+ if(!Curl_hSecDll) {
/* Security Service Provider Interface (SSPI) functions are located in
* security.dll on WinNT 4.0 and in secur32.dll on Win9x. Win2K and XP
* have both these DLLs (security.dll forwards calls to secur32.dll) */
/* Load SSPI dll into the address space of the calling process */
if(curlx_verify_windows_version(4, 0, 0, PLATFORM_WINNT, VERSION_EQUAL))
- s_hSecDll = Curl_load_library(TEXT("security.dll"));
+ Curl_hSecDll = Curl_load_library(TEXT("security.dll"));
else
- s_hSecDll = Curl_load_library(TEXT("secur32.dll"));
- if(!s_hSecDll)
+ Curl_hSecDll = Curl_load_library(TEXT("secur32.dll"));
+ if(!Curl_hSecDll)
return CURLE_FAILED_INIT;
/* Get address of the InitSecurityInterfaceA function from the SSPI dll */
pInitSecurityInterface =
CURLX_FUNCTION_CAST(INITSECURITYINTERFACE_FN,
- (GetProcAddress(s_hSecDll, SECURITYENTRYPOINT)));
+ (GetProcAddress(Curl_hSecDll, SECURITYENTRYPOINT)));
if(!pInitSecurityInterface)
return CURLE_FAILED_INIT;
/* Get pointer to Security Service Provider Interface dispatch table */
- s_pSecFn = pInitSecurityInterface();
- if(!s_pSecFn)
+ Curl_pSecFn = pInitSecurityInterface();
+ if(!Curl_pSecFn)
return CURLE_FAILED_INIT;
}
@@ -119,10 +119,10 @@ CURLcode Curl_sspi_global_init(void)
*/
void Curl_sspi_global_cleanup(void)
{
- if(s_hSecDll) {
- FreeLibrary(s_hSecDll);
- s_hSecDll = NULL;
- s_pSecFn = NULL;
+ if(Curl_hSecDll) {
+ FreeLibrary(Curl_hSecDll);
+ Curl_hSecDll = NULL;
+ Curl_pSecFn = NULL;
}
}
diff --git a/libs/libcurl/src/curl_sspi.h b/libs/libcurl/src/curl_sspi.h
index d1f89720dd..9c7a2d4c04 100644
--- a/libs/libcurl/src/curl_sspi.h
+++ b/libs/libcurl/src/curl_sspi.h
@@ -57,8 +57,8 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp,
void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity);
/* Forward-declaration of global variables defined in curl_sspi.c */
-extern HMODULE s_hSecDll;
-extern PSecurityFunctionTable s_pSecFn;
+extern HMODULE Curl_hSecDll;
+extern PSecurityFunctionTable Curl_pSecFn;
/* Provide some definitions missing in old headers */
#define SP_NAME_DIGEST "WDigest"
diff --git a/libs/libcurl/src/curl_threads.c b/libs/libcurl/src/curl_threads.c
index 7bb22850df..c74a0deba4 100644
--- a/libs/libcurl/src/curl_threads.c
+++ b/libs/libcurl/src/curl_threads.c
@@ -35,7 +35,9 @@
#endif
#include "curl_threads.h"
+#ifdef BUILDING_LIBCURL
#include "curl_memory.h"
+#endif
/* The last #include file should be: */
#include "memdebug.h"
diff --git a/libs/libcurl/src/curl_trc.c b/libs/libcurl/src/curl_trc.c
index eba9d57d52..dfe4d7d99b 100644
--- a/libs/libcurl/src/curl_trc.c
+++ b/libs/libcurl/src/curl_trc.c
@@ -53,6 +53,9 @@
#include "curl_memory.h"
#include "memdebug.h"
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
void Curl_debug(struct Curl_easy *data, curl_infotype type,
char *ptr, size_t size)
@@ -118,10 +121,16 @@ static void trc_infof(struct Curl_easy *data, struct curl_trc_feat *feat,
const char * const fmt, va_list ap)
{
int len = 0;
- char buffer[MAXINFO + 2];
+ char buffer[MAXINFO + 5];
if(feat)
- len = msnprintf(buffer, MAXINFO, "[%s] ", feat->name);
- len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap);
+ len = msnprintf(buffer, (MAXINFO + 1), "[%s] ", feat->name);
+ len += mvsnprintf(buffer + len, (MAXINFO + 1) - len, fmt, ap);
+ if(len >= MAXINFO) { /* too long, shorten with '...' */
+ --len;
+ buffer[len++] = '.';
+ buffer[len++] = '.';
+ buffer[len++] = '.';
+ }
buffer[len++] = '\n';
buffer[len] = '\0';
Curl_debug(data, CURLINFO_TEXT, buffer, len);
@@ -212,58 +221,144 @@ void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...)
}
#endif /* !CURL_DISABLE_FTP */
-static struct curl_trc_feat *trc_feats[] = {
- &Curl_trc_feat_read,
- &Curl_trc_feat_write,
+#ifndef CURL_DISABLE_SMTP
+struct curl_trc_feat Curl_trc_feat_smtp = {
+ "SMTP",
+ CURL_LOG_LVL_NONE,
+};
+
+void Curl_trc_smtp(struct Curl_easy *data, const char *fmt, ...)
+{
+ DEBUGASSERT(!strchr(fmt, '\n'));
+ if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_smtp)) {
+ va_list ap;
+ va_start(ap, fmt);
+ trc_infof(data, &Curl_trc_feat_smtp, fmt, ap);
+ va_end(ap);
+ }
+}
+#endif /* !CURL_DISABLE_SMTP */
+
+#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
+struct curl_trc_feat Curl_trc_feat_ws = {
+ "WS",
+ CURL_LOG_LVL_NONE,
+};
+
+void Curl_trc_ws(struct Curl_easy *data, const char *fmt, ...)
+{
+ DEBUGASSERT(!strchr(fmt, '\n'));
+ if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ws)) {
+ va_list ap;
+ va_start(ap, fmt);
+ trc_infof(data, &Curl_trc_feat_ws, fmt, ap);
+ va_end(ap);
+ }
+}
+#endif /* USE_WEBSOCKETS && !CURL_DISABLE_HTTP */
+
+#define TRC_CT_NONE (0)
+#define TRC_CT_PROTOCOL (1<<(0))
+#define TRC_CT_NETWORK (1<<(1))
+#define TRC_CT_PROXY (1<<(2))
+
+struct trc_feat_def {
+ struct curl_trc_feat *feat;
+ unsigned int category;
+};
+
+static struct trc_feat_def trc_feats[] = {
+ { &Curl_trc_feat_read, TRC_CT_NONE },
+ { &Curl_trc_feat_write, TRC_CT_NONE },
#ifndef CURL_DISABLE_FTP
- &Curl_trc_feat_ftp,
+ { &Curl_trc_feat_ftp, TRC_CT_PROTOCOL },
#endif
#ifndef CURL_DISABLE_DOH
- &Curl_doh_trc,
+ { &Curl_doh_trc, TRC_CT_NETWORK },
+#endif
+#ifndef CURL_DISABLE_SMTP
+ { &Curl_trc_feat_smtp, TRC_CT_PROTOCOL },
#endif
- NULL,
+#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
+ { &Curl_trc_feat_ws, TRC_CT_PROTOCOL },
+#endif
+};
+
+struct trc_cft_def {
+ struct Curl_cftype *cft;
+ unsigned int category;
};
-static struct Curl_cftype *cf_types[] = {
- &Curl_cft_tcp,
- &Curl_cft_udp,
- &Curl_cft_unix,
- &Curl_cft_tcp_accept,
- &Curl_cft_happy_eyeballs,
- &Curl_cft_setup,
+static struct trc_cft_def trc_cfts[] = {
+ { &Curl_cft_tcp, TRC_CT_NETWORK },
+ { &Curl_cft_udp, TRC_CT_NETWORK },
+ { &Curl_cft_unix, TRC_CT_NETWORK },
+ { &Curl_cft_tcp_accept, TRC_CT_NETWORK },
+ { &Curl_cft_happy_eyeballs, TRC_CT_NETWORK },
+ { &Curl_cft_setup, TRC_CT_PROTOCOL },
#ifdef USE_NGHTTP2
- &Curl_cft_nghttp2,
+ { &Curl_cft_nghttp2, TRC_CT_PROTOCOL },
#endif
#ifdef USE_SSL
- &Curl_cft_ssl,
+ { &Curl_cft_ssl, TRC_CT_NETWORK },
#ifndef CURL_DISABLE_PROXY
- &Curl_cft_ssl_proxy,
+ { &Curl_cft_ssl_proxy, TRC_CT_PROXY },
#endif
#endif
#if !defined(CURL_DISABLE_PROXY)
#if !defined(CURL_DISABLE_HTTP)
- &Curl_cft_h1_proxy,
+ { &Curl_cft_h1_proxy, TRC_CT_PROXY },
#ifdef USE_NGHTTP2
- &Curl_cft_h2_proxy,
+ { &Curl_cft_h2_proxy, TRC_CT_PROXY },
#endif
- &Curl_cft_http_proxy,
+ { &Curl_cft_http_proxy, TRC_CT_PROXY },
#endif /* !CURL_DISABLE_HTTP */
- &Curl_cft_haproxy,
- &Curl_cft_socks_proxy,
+ { &Curl_cft_haproxy, TRC_CT_PROXY },
+ { &Curl_cft_socks_proxy, TRC_CT_PROXY },
#endif /* !CURL_DISABLE_PROXY */
#ifdef USE_HTTP3
- &Curl_cft_http3,
+ { &Curl_cft_http3, TRC_CT_PROTOCOL },
#endif
#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
- &Curl_cft_http_connect,
+ { &Curl_cft_http_connect, TRC_CT_PROTOCOL },
#endif
- NULL,
};
-CURLcode Curl_trc_opt(const char *config)
+static void trc_apply_level_by_name(const char * const token, int lvl)
+{
+ size_t i;
+
+ for(i = 0; i < ARRAYSIZE(trc_cfts); ++i) {
+ if(strcasecompare(token, trc_cfts[i].cft->name)) {
+ trc_cfts[i].cft->log_level = lvl;
+ break;
+ }
+ }
+ for(i = 0; i < ARRAYSIZE(trc_feats); ++i) {
+ if(strcasecompare(token, trc_feats[i].feat->name)) {
+ trc_feats[i].feat->log_level = lvl;
+ break;
+ }
+ }
+}
+
+static void trc_apply_level_by_category(int category, int lvl)
{
- char *token, *tok_buf, *tmp;
size_t i;
+
+ for(i = 0; i < ARRAYSIZE(trc_cfts); ++i) {
+ if(!category || (trc_cfts[i].category & category))
+ trc_cfts[i].cft->log_level = lvl;
+ }
+ for(i = 0; i < ARRAYSIZE(trc_feats); ++i) {
+ if(!category || (trc_feats[i].category & category))
+ trc_feats[i].feat->log_level = lvl;
+ }
+}
+
+static CURLcode trc_opt(const char *config)
+{
+ char *token, *tok_buf, *tmp;
int lvl;
tmp = strdup(config);
@@ -285,42 +380,46 @@ CURLcode Curl_trc_opt(const char *config)
lvl = CURL_LOG_LVL_INFO;
break;
}
- for(i = 0; cf_types[i]; ++i) {
- if(strcasecompare(token, "all")) {
- cf_types[i]->log_level = lvl;
- }
- else if(strcasecompare(token, cf_types[i]->name)) {
- cf_types[i]->log_level = lvl;
- break;
- }
- }
- for(i = 0; trc_feats[i]; ++i) {
- if(strcasecompare(token, "all")) {
- trc_feats[i]->log_level = lvl;
- }
- else if(strcasecompare(token, trc_feats[i]->name)) {
- trc_feats[i]->log_level = lvl;
- break;
- }
- }
+ if(strcasecompare(token, "all"))
+ trc_apply_level_by_category(TRC_CT_NONE, lvl);
+ else if(strcasecompare(token, "protocol"))
+ trc_apply_level_by_category(TRC_CT_PROTOCOL, lvl);
+ else if(strcasecompare(token, "network"))
+ trc_apply_level_by_category(TRC_CT_NETWORK, lvl);
+ else if(strcasecompare(token, "proxy"))
+ trc_apply_level_by_category(TRC_CT_PROXY, lvl);
+ else
+ trc_apply_level_by_name(token, lvl);
+
token = strtok_r(NULL, ", ", &tok_buf);
}
free(tmp);
return CURLE_OK;
}
-CURLcode Curl_trc_init(void)
+CURLcode Curl_trc_opt(const char *config)
{
+ CURLcode result = config? trc_opt(config) : CURLE_OK;
#ifdef DEBUGBUILD
- /* WIP: we use the auto-init from an env var only in DEBUG builds for
- * convenience. */
- const char *config = getenv("CURL_DEBUG");
- if(config) {
- return Curl_trc_opt(config);
+ /* CURL_DEBUG can override anything */
+ if(!result) {
+ const char *dbg_config = getenv("CURL_DEBUG");
+ if(dbg_config)
+ result = trc_opt(dbg_config);
}
#endif /* DEBUGBUILD */
+ return result;
+}
+
+CURLcode Curl_trc_init(void)
+{
+#ifdef DEBUGBUILD
+ return Curl_trc_opt(NULL);
+#else
return CURLE_OK;
+#endif
}
+
#else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */
CURLcode Curl_trc_init(void)
diff --git a/libs/libcurl/src/curl_trc.h b/libs/libcurl/src/curl_trc.h
index 437e54cb30..1732832248 100644
--- a/libs/libcurl/src/curl_trc.h
+++ b/libs/libcurl/src/curl_trc.h
@@ -89,6 +89,16 @@ void Curl_failf(struct Curl_easy *data,
do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ftp)) \
Curl_trc_ftp(data, __VA_ARGS__); } while(0)
#endif /* !CURL_DISABLE_FTP */
+#ifndef CURL_DISABLE_SMTP
+#define CURL_TRC_SMTP(data, ...) \
+ do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_smtp)) \
+ Curl_trc_smtp(data, __VA_ARGS__); } while(0)
+#endif /* !CURL_DISABLE_SMTP */
+#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
+#define CURL_TRC_WS(data, ...) \
+ do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ws)) \
+ Curl_trc_ws(data, __VA_ARGS__); } while(0)
+#endif /* USE_WEBSOCKETS && !CURL_DISABLE_HTTP */
#else /* CURL_HAVE_C99 */
@@ -100,6 +110,12 @@ void Curl_failf(struct Curl_easy *data,
#ifndef CURL_DISABLE_FTP
#define CURL_TRC_FTP Curl_trc_ftp
#endif
+#ifndef CURL_DISABLE_SMTP
+#define CURL_TRC_SMTP Curl_trc_smtp
+#endif
+#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
+#define CURL_TRC_WS Curl_trc_ws
+#endif
#endif /* !CURL_HAVE_C99 */
@@ -148,6 +164,16 @@ extern struct curl_trc_feat Curl_trc_feat_ftp;
void Curl_trc_ftp(struct Curl_easy *data,
const char *fmt, ...) CURL_PRINTF(2, 3);
#endif
+#ifndef CURL_DISABLE_SMTP
+extern struct curl_trc_feat Curl_trc_feat_smtp;
+void Curl_trc_smtp(struct Curl_easy *data,
+ const char *fmt, ...) CURL_PRINTF(2, 3);
+#endif
+#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
+extern struct curl_trc_feat Curl_trc_feat_ws;
+void Curl_trc_ws(struct Curl_easy *data,
+ const char *fmt, ...) CURL_PRINTF(2, 3);
+#endif
#else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */
@@ -194,6 +220,12 @@ static void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...)
(void)data; (void)fmt;
}
#endif
+#ifndef CURL_DISABLE_SMTP
+static void Curl_trc_smtp(struct Curl_easy *data, const char *fmt, ...)
+{
+ (void)data; (void)fmt;
+}
+#endif
#endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */
diff --git a/libs/libcurl/src/curlx.h b/libs/libcurl/src/curlx.h
index daa46f46af..883f591f0c 100644
--- a/libs/libcurl/src/curlx.h
+++ b/libs/libcurl/src/curlx.h
@@ -31,10 +31,8 @@
* 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 */
+/* map standard printf functions to curl implementations */
+#include "curl_printf.h"
#include "strcase.h"
/* "strcase.h" provides the strcasecompare protos */
@@ -77,41 +75,4 @@
*/
-#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 66cc6f300f..5bf76332af 100644
--- a/libs/libcurl/src/dict.c
+++ b/libs/libcurl/src/dict.c
@@ -146,7 +146,7 @@ static CURLcode sendf(struct Curl_easy *data, const char *fmt, ...)
for(;;) {
/* Write the buffer to the socket */
- result = Curl_xfer_send(data, sptr, write_len, &bytes_written);
+ result = Curl_xfer_send(data, sptr, write_len, FALSE, &bytes_written);
if(result)
break;
diff --git a/libs/libcurl/src/doh.c b/libs/libcurl/src/doh.c
index 9653f32639..d9064e1389 100644
--- a/libs/libcurl/src/doh.c
+++ b/libs/libcurl/src/doh.c
@@ -46,7 +46,7 @@
#define DNS_CLASS_IN 0x01
-/* local_print_buf truncates if the hex string will be more than this */
+/* doh_print_buf truncates if the hex string will be more than this */
#define LOCAL_PB_HEXMAX 400
#ifndef CURL_DISABLE_VERBOSE_STRINGS
@@ -82,11 +82,11 @@ struct curl_trc_feat Curl_doh_trc = {
/* @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 */
+UNITTEST DOHcode doh_req_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;
@@ -192,9 +192,9 @@ doh_write_cb(const void *contents, size_t size, size_t nmemb, void *userp)
}
#if defined(USE_HTTPSRR) && defined(DEBUGBUILD)
-static void local_print_buf(struct Curl_easy *data,
- const char *prefix,
- unsigned char *buf, size_t len)
+static void doh_print_buf(struct Curl_easy *data,
+ const char *prefix,
+ unsigned char *buf, size_t len)
{
unsigned char hexstr[LOCAL_PB_HEXMAX];
size_t hlen = LOCAL_PB_HEXMAX;
@@ -214,19 +214,26 @@ static void local_print_buf(struct Curl_easy *data,
/* 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(doh, "a DoH request is completed, %u to go", dohp->pending);
- if(result)
- infof(doh, "DoH request %s", curl_easy_strerror(result));
+ struct Curl_easy *data; /* the transfer that asked for the DoH probe */
- if(!dohp->pending) {
- /* DoH completed */
- curl_slist_free_all(dohp->headers);
- dohp->headers = NULL;
- Curl_expire(data, 0, EXPIRE_RUN_NOW);
+ data = Curl_multi_get_handle(doh->multi, doh->set.dohfor_mid);
+ if(!data) {
+ DEBUGF(infof(doh, "doh_done: xfer for mid=%" FMT_OFF_T
+ " not found", doh->set.dohfor_mid));
+ DEBUGASSERT(0);
+ }
+ else {
+ struct doh_probes *dohp = data->req.doh;
+ /* one of the DoH request done for the 'data' transfer is now complete! */
+ dohp->pending--;
+ infof(doh, "a DoH request is completed, %u to go", dohp->pending);
+ if(result)
+ infof(doh, "DoH request %s", curl_easy_strerror(result));
+
+ if(!dohp->pending) {
+ /* DoH completed, run the transfer picking up the results */
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+ }
}
return 0;
}
@@ -240,24 +247,24 @@ do { \
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)
+static CURLcode doh_run_probe(struct Curl_easy *data,
+ struct doh_probe *p, DNStype dnstype,
+ const char *host,
+ const char *url, CURLM *multi,
+ struct curl_slist *headers)
{
struct Curl_easy *doh = NULL;
CURLcode result = CURLE_OK;
timediff_t timeout_ms;
- DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer),
- &p->dohlen);
+ DOHcode d = doh_req_encode(host, dnstype, p->req_body, sizeof(p->req_body),
+ &p->req_body_len);
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);
+ Curl_dyn_init(&p->resp_body, DYN_DOH_RESPONSE);
timeout_ms = Curl_timeleft(data, NULL, TRUE);
if(timeout_ms <= 0) {
@@ -266,126 +273,126 @@ static CURLcode dohprobe(struct Curl_easy *data,
}
/* 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;
- doh->state.internal = true;
+ if(result)
+ goto error;
+
+ /* pass in the struct pointer via a local variable to please coverity and
+ the gcc typecheck helpers */
+ doh->state.internal = true;
#ifndef CURL_DISABLE_VERBOSE_STRINGS
- doh->state.feat = &Curl_doh_trc;
+ doh->state.feat = &Curl_doh_trc;
#endif
- 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);
+ 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, &p->resp_body);
+ ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->req_body);
+ ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->req_body_len);
+ ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
#ifdef USE_HTTP2
- ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
- ERROR_CHECK_SETOPT(CURLOPT_PIPEWAIT, 1L);
+ ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
+ ERROR_CHECK_SETOPT(CURLOPT_PIPEWAIT, 1L);
#endif
#ifndef DEBUGBUILD
- /* enforce HTTPS if not debug */
- ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
+ /* 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);
+ /* 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(Curl_trc_ft_is_verbose(data, &Curl_doh_trc))
- 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.fdebug)
- ERROR_CHECK_SETOPT(CURLOPT_DEBUGFUNCTION, data->set.fdebug);
- if(data->set.debugdata)
- ERROR_CHECK_SETOPT(CURLOPT_DEBUGDATA, data->set.debugdata);
- if(data->set.str[STRING_SSL_EC_CURVES]) {
- ERROR_CHECK_SETOPT(CURLOPT_SSL_EC_CURVES,
- data->set.str[STRING_SSL_EC_CURVES]);
- }
+ 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(Curl_trc_ft_is_verbose(data, &Curl_doh_trc))
+ 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.fdebug)
+ ERROR_CHECK_SETOPT(CURLOPT_DEBUGFUNCTION, data->set.fdebug);
+ if(data->set.debugdata)
+ ERROR_CHECK_SETOPT(CURLOPT_DEBUGDATA, data->set.debugdata);
+ 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);
- }
+ {
+ 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->set.fmultidone = doh_done;
+ doh->set.dohfor_mid = data->mid; /* for which transfer this is done */
- /* DoH handles must not inherit private_data. The handles may be passed to
- the user via callbacks and the user will be able to identify them as
- internal handles because private data is not set. The user can then set
- private_data via CURLOPT_PRIVATE if they so choose. */
- DEBUGASSERT(!doh->set.private_data);
+ /* DoH handles must not inherit private_data. The handles may be passed to
+ the user via callbacks and the user will be able to identify them as
+ internal handles because private data is not set. The user can then set
+ private_data via CURLOPT_PRIVATE if they so choose. */
+ DEBUGASSERT(!doh->set.private_data);
- if(curl_multi_add_handle(multi, doh))
- goto error;
- }
- else
+ if(curl_multi_add_handle(multi, doh))
goto error;
+
+ p->easy_mid = doh->mid;
return CURLE_OK;
error:
Curl_close(&doh);
+ p->easy_mid = -1;
return result;
}
@@ -400,8 +407,9 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
int *waitp)
{
CURLcode result = CURLE_OK;
- struct dohdata *dohp;
+ struct doh_probes *dohp;
struct connectdata *conn = data->conn;
+ size_t i;
#ifdef USE_HTTPSRR
/* for now, this is only used when ECH is enabled */
# ifdef USE_ECH
@@ -416,23 +424,27 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
DEBUGASSERT(conn);
/* start clean, consider allocating this struct on demand */
- dohp = data->req.doh = calloc(1, sizeof(struct dohdata));
+ dohp = data->req.doh = calloc(1, sizeof(struct doh_probes));
if(!dohp)
return NULL;
+ for(i = 0; i < DOH_SLOT_COUNT; ++i) {
+ dohp->probe[i].easy_mid = -1;
+ }
+
conn->bits.doh = TRUE;
dohp->host = hostname;
dohp->port = port;
- dohp->headers =
+ dohp->req_hds =
curl_slist_append(NULL,
"Content-Type: application/dns-message");
- if(!dohp->headers)
+ if(!dohp->req_hds)
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);
+ result = doh_run_probe(data, &dohp->probe[DOH_SLOT_IPV4],
+ DNS_TYPE_A, hostname, data->set.str[STRING_DOH],
+ data->multi, dohp->req_hds);
if(result)
goto error;
dohp->pending++;
@@ -440,9 +452,9 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
#ifdef USE_IPV6
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);
+ result = doh_run_probe(data, &dohp->probe[DOH_SLOT_IPV6],
+ DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
+ data->multi, dohp->req_hds);
if(result)
goto error;
dohp->pending++;
@@ -469,9 +481,9 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
qname = aprintf("_%d._https.%s", port, hostname);
if(!qname)
goto error;
- result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_HTTPS],
- DNS_TYPE_HTTPS, qname, data->set.str[STRING_DOH],
- data->multi, dohp->headers);
+ result = doh_run_probe(data, &dohp->probe[DOH_SLOT_HTTPS_RR],
+ DNS_TYPE_HTTPS, qname, data->set.str[STRING_DOH],
+ data->multi, dohp->req_hds);
Curl_safefree(qname);
if(result)
goto error;
@@ -487,8 +499,8 @@ error:
return NULL;
}
-static DOHcode skipqname(const unsigned char *doh, size_t dohlen,
- unsigned int *indexp)
+static DOHcode doh_skipqname(const unsigned char *doh, size_t dohlen,
+ unsigned int *indexp)
{
unsigned char length;
do {
@@ -511,12 +523,13 @@ static DOHcode skipqname(const unsigned char *doh, size_t dohlen,
return DOH_OK;
}
-static unsigned short get16bit(const unsigned char *doh, unsigned int index)
+static unsigned short doh_get16bit(const unsigned char *doh,
+ unsigned int index)
{
return (unsigned short)((doh[index] << 8) | doh[index + 1]);
}
-static unsigned int get32bit(const unsigned char *doh, unsigned int index)
+static unsigned int doh_get32bit(const unsigned char *doh, unsigned int index)
{
/* make clang and gcc optimize this to bswap by incrementing
the pointer first. */
@@ -529,7 +542,8 @@ static unsigned int get32bit(const unsigned char *doh, unsigned int index)
((unsigned)doh[2] << 8) | doh[3];
}
-static DOHcode store_a(const unsigned char *doh, int index, struct dohentry *d)
+static void doh_store_a(const unsigned char *doh, int index,
+ struct dohentry *d)
{
/* silently ignore addresses over the limit */
if(d->numaddr < DOH_MAX_ADDR) {
@@ -538,12 +552,10 @@ static DOHcode store_a(const unsigned char *doh, int index, struct dohentry *d)
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)
+static void doh_store_aaaa(const unsigned char *doh, int index,
+ struct dohentry *d)
{
/* silently ignore addresses over the limit */
if(d->numaddr < DOH_MAX_ADDR) {
@@ -552,14 +564,11 @@ static DOHcode store_aaaa(const unsigned char *doh,
memcpy(&a->ip.v6, &doh[index], 16);
d->numaddr++;
}
- return DOH_OK;
}
#ifdef USE_HTTPSRR
-static DOHcode store_https(const unsigned char *doh,
- int index,
- struct dohentry *d,
- uint16_t len)
+static DOHcode doh_store_https(const unsigned char *doh, int index,
+ struct dohentry *d, uint16_t len)
{
/* silently ignore RRs over the limit */
if(d->numhttps_rrs < DOH_MAX_HTTPS) {
@@ -574,10 +583,8 @@ static DOHcode store_https(const unsigned char *doh,
}
#endif
-static DOHcode store_cname(const unsigned char *doh,
- size_t dohlen,
- unsigned int index,
- struct dohentry *d)
+static DOHcode doh_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 */
@@ -626,12 +633,12 @@ static DOHcode store_cname(const unsigned char *doh,
return DOH_OK;
}
-static DOHcode rdata(const unsigned char *doh,
- size_t dohlen,
- unsigned short rdlength,
- unsigned short type,
- int index,
- struct dohentry *d)
+static DOHcode doh_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
@@ -644,26 +651,22 @@ static DOHcode rdata(const unsigned char *doh,
case DNS_TYPE_A:
if(rdlength != 4)
return DOH_DNS_RDATA_LEN;
- rc = store_a(doh, index, d);
- if(rc)
- return rc;
+ doh_store_a(doh, index, d);
break;
case DNS_TYPE_AAAA:
if(rdlength != 16)
return DOH_DNS_RDATA_LEN;
- rc = store_aaaa(doh, index, d);
- if(rc)
- return rc;
+ doh_store_aaaa(doh, index, d);
break;
#ifdef USE_HTTPSRR
case DNS_TYPE_HTTPS:
- rc = store_https(doh, index, d, rdlength);
+ rc = doh_store_https(doh, index, d, rdlength);
if(rc)
return rc;
break;
#endif
case DNS_TYPE_CNAME:
- rc = store_cname(doh, dohlen, (unsigned int)index, d);
+ rc = doh_store_cname(doh, dohlen, (unsigned int)index, d);
if(rc)
return rc;
break;
@@ -687,10 +690,10 @@ UNITTEST void de_init(struct dohentry *de)
}
-UNITTEST DOHcode doh_decode(const unsigned char *doh,
- size_t dohlen,
- DNStype dnstype,
- struct dohentry *d)
+UNITTEST DOHcode doh_resp_decode(const unsigned char *doh,
+ size_t dohlen,
+ DNStype dnstype,
+ struct dohentry *d)
{
unsigned char rcode;
unsigned short qdcount;
@@ -710,9 +713,9 @@ UNITTEST DOHcode doh_decode(const unsigned char *doh,
if(rcode)
return DOH_DNS_BAD_RCODE; /* bad rcode */
- qdcount = get16bit(doh, 4);
+ qdcount = doh_get16bit(doh, 4);
while(qdcount) {
- rc = skipqname(doh, dohlen, &index);
+ rc = doh_skipqname(doh, dohlen, &index);
if(rc)
return rc; /* bad qname */
if(dohlen < (index + 4))
@@ -721,19 +724,19 @@ UNITTEST DOHcode doh_decode(const unsigned char *doh,
qdcount--;
}
- ancount = get16bit(doh, 6);
+ ancount = doh_get16bit(doh, 6);
while(ancount) {
unsigned short class;
unsigned int ttl;
- rc = skipqname(doh, dohlen, &index);
+ rc = doh_skipqname(doh, dohlen, &index);
if(rc)
return rc; /* bad qname */
if(dohlen < (index + 2))
return DOH_DNS_OUT_OF_RANGE;
- type = get16bit(doh, index);
+ type = doh_get16bit(doh, index);
if((type != DNS_TYPE_CNAME) /* may be synthesized from DNAME */
&& (type != DNS_TYPE_DNAME) /* if present, accept and ignore */
&& (type != dnstype))
@@ -743,7 +746,7 @@ UNITTEST DOHcode doh_decode(const unsigned char *doh,
if(dohlen < (index + 2))
return DOH_DNS_OUT_OF_RANGE;
- class = get16bit(doh, index);
+ class = doh_get16bit(doh, index);
if(DNS_CLASS_IN != class)
return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */
index += 2;
@@ -751,7 +754,7 @@ UNITTEST DOHcode doh_decode(const unsigned char *doh,
if(dohlen < (index + 4))
return DOH_DNS_OUT_OF_RANGE;
- ttl = get32bit(doh, index);
+ ttl = doh_get32bit(doh, index);
if(ttl < d->ttl)
d->ttl = ttl;
index += 4;
@@ -759,21 +762,21 @@ UNITTEST DOHcode doh_decode(const unsigned char *doh,
if(dohlen < (index + 2))
return DOH_DNS_OUT_OF_RANGE;
- rdlength = get16bit(doh, index);
+ rdlength = doh_get16bit(doh, index);
index += 2;
if(dohlen < (index + rdlength))
return DOH_DNS_OUT_OF_RANGE;
- rc = rdata(doh, dohlen, rdlength, type, (int)index, d);
+ rc = doh_rdata(doh, dohlen, rdlength, type, (int)index, d);
if(rc)
- return rc; /* bad rdata */
+ return rc; /* bad doh_rdata */
index += rdlength;
ancount--;
}
- nscount = get16bit(doh, 8);
+ nscount = doh_get16bit(doh, 8);
while(nscount) {
- rc = skipqname(doh, dohlen, &index);
+ rc = doh_skipqname(doh, dohlen, &index);
if(rc)
return rc; /* bad qname */
@@ -785,7 +788,7 @@ UNITTEST DOHcode doh_decode(const unsigned char *doh,
if(dohlen < (index + 2))
return DOH_DNS_OUT_OF_RANGE;
- rdlength = get16bit(doh, index);
+ rdlength = doh_get16bit(doh, index);
index += 2;
if(dohlen < (index + rdlength))
return DOH_DNS_OUT_OF_RANGE;
@@ -793,9 +796,9 @@ UNITTEST DOHcode doh_decode(const unsigned char *doh,
nscount--;
}
- arcount = get16bit(doh, 10);
+ arcount = doh_get16bit(doh, 10);
while(arcount) {
- rc = skipqname(doh, dohlen, &index);
+ rc = doh_skipqname(doh, dohlen, &index);
if(rc)
return rc; /* bad qname */
@@ -807,7 +810,7 @@ UNITTEST DOHcode doh_decode(const unsigned char *doh,
if(dohlen < (index + 2))
return DOH_DNS_OUT_OF_RANGE;
- rdlength = get16bit(doh, index);
+ rdlength = doh_get16bit(doh, index);
index += 2;
if(dohlen < (index + rdlength))
return DOH_DNS_OUT_OF_RANGE;
@@ -830,8 +833,8 @@ UNITTEST DOHcode doh_decode(const unsigned char *doh,
}
#ifndef CURL_DISABLE_VERBOSE_STRINGS
-static void showdoh(struct Curl_easy *data,
- const struct dohentry *d)
+static void doh_show(struct Curl_easy *data,
+ const struct dohentry *d)
{
int i;
infof(data, "[DoH] TTL: %u seconds", d->ttl);
@@ -864,8 +867,8 @@ static void showdoh(struct Curl_easy *data,
#ifdef USE_HTTPSRR
for(i = 0; i < d->numhttps_rrs; i++) {
# ifdef DEBUGBUILD
- local_print_buf(data, "DoH HTTPS",
- d->https_rrs[i].val, d->https_rrs[i].len);
+ doh_print_buf(data, "DoH HTTPS",
+ d->https_rrs[i].val, d->https_rrs[i].len);
# else
infof(data, "DoH HTTPS RR: length %d", d->https_rrs[i].len);
# endif
@@ -876,7 +879,7 @@ static void showdoh(struct Curl_easy *data,
}
}
#else
-#define showdoh(x,y)
+#define doh_show(x,y)
#endif
/*
@@ -996,7 +999,7 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname,
}
#ifndef CURL_DISABLE_VERBOSE_STRINGS
-static const char *type2name(DNStype dnstype)
+static const char *doh_type2name(DNStype dnstype)
{
switch(dnstype) {
case DNS_TYPE_A:
@@ -1041,8 +1044,8 @@ UNITTEST void de_cleanup(struct dohentry *d)
* just after the end of the DNS name encoding on output. (And
* that is why it is an "unsigned char **" :-)
*/
-static CURLcode local_decode_rdata_name(unsigned char **buf, size_t *remaining,
- char **dnsname)
+static CURLcode doh_decode_rdata_name(unsigned char **buf, size_t *remaining,
+ char **dnsname)
{
unsigned char *cp = NULL;
int rem = 0;
@@ -1088,8 +1091,8 @@ static CURLcode local_decode_rdata_name(unsigned char **buf, size_t *remaining,
return CURLE_OK;
}
-static CURLcode local_decode_rdata_alpn(unsigned char *rrval, size_t len,
- char **alpns)
+static CURLcode doh_decode_rdata_alpn(unsigned char *rrval, size_t len,
+ char **alpns)
{
/*
* spec here is as per draft-ietf-dnsop-svcb-https, section-7.1.1
@@ -1145,7 +1148,7 @@ err:
}
#ifdef DEBUGBUILD
-static CURLcode test_alpn_escapes(void)
+static CURLcode doh_test_alpn_escapes(void)
{
/* we will use an example from draft-ietf-dnsop-svcb, figure 10 */
static unsigned char example[] = {
@@ -1158,7 +1161,7 @@ static CURLcode test_alpn_escapes(void)
char *aval = NULL;
static const char *expected = "f\\\\oo\\,bar,h2";
- if(local_decode_rdata_alpn(example, example_len, &aval) != CURLE_OK)
+ if(doh_decode_rdata_alpn(example, example_len, &aval) != CURLE_OK)
return CURLE_BAD_CONTENT_ENCODING;
if(strlen(aval) != strlen(expected))
return CURLE_BAD_CONTENT_ENCODING;
@@ -1168,7 +1171,7 @@ static CURLcode test_alpn_escapes(void)
}
#endif
-static CURLcode Curl_doh_decode_httpsrr(unsigned char *rrval, size_t len,
+static CURLcode doh_resp_decode_httpsrr(unsigned char *rrval, size_t len,
struct Curl_https_rrinfo **hrr)
{
size_t remaining = len;
@@ -1179,7 +1182,7 @@ static CURLcode Curl_doh_decode_httpsrr(unsigned char *rrval, size_t len,
#ifdef DEBUGBUILD
/* a few tests of escaping, should not be here but ok for now */
- if(test_alpn_escapes() != CURLE_OK)
+ if(doh_test_alpn_escapes() != CURLE_OK)
return CURLE_OUT_OF_MEMORY;
#endif
lhrr = calloc(1, sizeof(struct Curl_https_rrinfo));
@@ -1194,7 +1197,7 @@ static CURLcode Curl_doh_decode_httpsrr(unsigned char *rrval, size_t len,
lhrr->priority = (uint16_t)((cp[0] << 8) + cp[1]);
cp += 2;
remaining -= (uint16_t)2;
- if(local_decode_rdata_name(&cp, &remaining, &dnsname) != CURLE_OK)
+ if(doh_decode_rdata_name(&cp, &remaining, &dnsname) != CURLE_OK)
goto err;
lhrr->target = dnsname;
while(remaining >= 4) {
@@ -1204,7 +1207,7 @@ static CURLcode Curl_doh_decode_httpsrr(unsigned char *rrval, size_t len,
cp += 2;
remaining -= 4;
if(pcode == HTTPS_RR_CODE_ALPN) {
- if(local_decode_rdata_alpn(cp, plen, &lhrr->alpns) != CURLE_OK)
+ if(doh_decode_rdata_alpn(cp, plen, &lhrr->alpns) != CURLE_OK)
goto err;
}
if(pcode == HTTPS_RR_CODE_NO_DEF_ALPN)
@@ -1253,8 +1256,8 @@ err:
}
# ifdef DEBUGBUILD
-static void local_print_httpsrr(struct Curl_easy *data,
- struct Curl_https_rrinfo *hrr)
+static void doh_print_httpsrr(struct Curl_easy *data,
+ struct Curl_https_rrinfo *hrr)
{
DEBUGASSERT(hrr);
infof(data, "HTTPS RR: priority %d, target: %s",
@@ -1268,20 +1271,20 @@ static void local_print_httpsrr(struct Curl_easy *data,
else
infof(data, "HTTPS RR: no_def_alpn not set");
if(hrr->ipv4hints) {
- local_print_buf(data, "HTTPS RR: ipv4hints",
- hrr->ipv4hints, hrr->ipv4hints_len);
+ doh_print_buf(data, "HTTPS RR: ipv4hints",
+ hrr->ipv4hints, hrr->ipv4hints_len);
}
else
infof(data, "HTTPS RR: no ipv4hints");
if(hrr->echconfiglist) {
- local_print_buf(data, "HTTPS RR: ECHConfigList",
- hrr->echconfiglist, hrr->echconfiglist_len);
+ doh_print_buf(data, "HTTPS RR: ECHConfigList",
+ hrr->echconfiglist, hrr->echconfiglist_len);
}
else
infof(data, "HTTPS RR: no ECHConfigList");
if(hrr->ipv6hints) {
- local_print_buf(data, "HTTPS RR: ipv6hint",
- hrr->ipv6hints, hrr->ipv6hints_len);
+ doh_print_buf(data, "HTTPS RR: ipv6hint",
+ hrr->ipv6hints, hrr->ipv6hints_len);
}
else
infof(data, "HTTPS RR: no ipv6hints");
@@ -1294,52 +1297,45 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
struct Curl_dns_entry **dnsp)
{
CURLcode result;
- struct dohdata *dohp = data->req.doh;
+ struct doh_probes *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) {
+ if(dohp->probe[DOH_SLOT_IPV4].easy_mid < 0 &&
+ dohp->probe[DOH_SLOT_IPV6].easy_mid < 0) {
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) {
-#ifndef USE_HTTPSRR
- DOHcode rc[DOH_PROBE_SLOTS] = {
- DOH_OK, DOH_OK
- };
-#else
- DOHcode rc[DOH_PROBE_SLOTS] = {
- DOH_OK, DOH_OK, DOH_OK
- };
-#endif
+ DOHcode rc[DOH_SLOT_COUNT];
struct dohentry de;
int slot;
+
+ memset(rc, 0, sizeof(rc));
/* remove DoH handles from multi handle and close them */
Curl_doh_close(data);
/* 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];
+ for(slot = 0; slot < DOH_SLOT_COUNT; slot++) {
+ struct doh_probe *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);
+ rc[slot] = doh_resp_decode(Curl_dyn_uptr(&p->resp_body),
+ Curl_dyn_len(&p->resp_body),
+ p->dnstype, &de);
+ Curl_dyn_free(&p->resp_body);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
if(rc[slot]) {
infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
- type2name(p->dnstype), dohp->host);
+ doh_type2name(p->dnstype), dohp->host);
}
#endif
} /* next slot */
result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
- if(!rc[DOH_PROBE_SLOT_IPADDR_V4] || !rc[DOH_PROBE_SLOT_IPADDR_V6]) {
+ if(!rc[DOH_SLOT_IPV4] || !rc[DOH_SLOT_IPV6]) {
/* we have an address, of one kind or other */
struct Curl_dns_entry *dns;
struct Curl_addrinfo *ai;
@@ -1347,7 +1343,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc)) {
infof(data, "[DoH] hostname: %s", dohp->host);
- showdoh(data, &de);
+ doh_show(data, &de);
}
result = doh2ai(&de, dohp->host, dohp->port, &ai);
@@ -1360,7 +1356,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
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, 0, dohp->port);
+ dns = Curl_cache_addr(data, ai, dohp->host, 0, dohp->port, FALSE);
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
@@ -1380,7 +1376,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
#ifdef USE_HTTPSRR
if(de.numhttps_rrs > 0 && result == CURLE_OK && *dnsp) {
struct Curl_https_rrinfo *hrr = NULL;
- result = Curl_doh_decode_httpsrr(de.https_rrs->val, de.https_rrs->len,
+ result = doh_resp_decode_httpsrr(de.https_rrs->val, de.https_rrs->len,
&hrr);
if(result) {
infof(data, "Failed to decode HTTPS RR");
@@ -1388,7 +1384,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
}
infof(data, "Some HTTPS RR to process");
# ifdef DEBUGBUILD
- local_print_httpsrr(data, hrr);
+ doh_print_httpsrr(data, hrr);
# endif
(*dnsp)->hinfo = hrr;
}
@@ -1396,7 +1392,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
/* All done */
de_cleanup(&de);
- Curl_safefree(data->req.doh);
+ Curl_doh_cleanup(data);
return result;
} /* !dohp->pending */
@@ -1407,28 +1403,39 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
void Curl_doh_close(struct Curl_easy *data)
{
- struct dohdata *doh = data->req.doh;
- if(doh) {
+ struct doh_probes *doh = data->req.doh;
+ if(doh && data->multi) {
+ struct Curl_easy *probe_data;
+ curl_off_t mid;
size_t slot;
- for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
- if(!doh->probe[slot].easy)
+ for(slot = 0; slot < DOH_SLOT_COUNT; slot++) {
+ mid = doh->probe[slot].easy_mid;
+ if(mid < 0)
continue;
+ doh->probe[slot].easy_mid = -1;
+ /* should have been called before data is removed from multi handle */
+ DEBUGASSERT(data->multi);
+ probe_data = data->multi? Curl_multi_get_handle(data->multi, mid) : NULL;
+ if(!probe_data) {
+ DEBUGF(infof(data, "Curl_doh_close: xfer for mid=%"
+ FMT_OFF_T " not found!",
+ doh->probe[slot].easy_mid));
+ continue;
+ }
/* data->multi might already be reset at this time */
- if(doh->probe[slot].easy->multi)
- curl_multi_remove_handle(doh->probe[slot].easy->multi,
- doh->probe[slot].easy);
- Curl_close(&doh->probe[slot].easy);
+ curl_multi_remove_handle(data->multi, probe_data);
+ Curl_close(&probe_data);
}
}
}
void Curl_doh_cleanup(struct Curl_easy *data)
{
- struct dohdata *doh = data->req.doh;
+ struct doh_probes *doh = data->req.doh;
if(doh) {
Curl_doh_close(data);
- curl_slist_free_all(doh->headers);
- data->req.doh->headers = NULL;
+ curl_slist_free_all(doh->req_hds);
+ data->req.doh->req_hds = NULL;
Curl_safefree(data->req.doh);
}
}
diff --git a/libs/libcurl/src/doh.h b/libs/libcurl/src/doh.h
index a8cc480bd3..cc0b3dd3ec 100644
--- a/libs/libcurl/src/doh.h
+++ b/libs/libcurl/src/doh.h
@@ -59,18 +59,39 @@ typedef enum {
} DNStype;
/* one of these for each DoH request */
-struct dnsprobe {
- CURL *easy;
+struct doh_probe {
+ curl_off_t easy_mid; /* multi id of easy handle doing the lookup */
DNStype dnstype;
- unsigned char dohbuffer[512];
- size_t dohlen;
- struct dynbuf serverdoh;
+ unsigned char req_body[512];
+ size_t req_body_len;
+ struct dynbuf resp_body;
};
-struct dohdata {
- struct curl_slist *headers;
- struct dnsprobe probe[DOH_PROBE_SLOTS];
- unsigned int pending; /* still outstanding requests */
+enum doh_slot_num {
+ /* Explicit values for first two symbols so as to match hard-coded
+ * constants in existing code
+ */
+ DOH_SLOT_IPV4 = 0, /* make 'V4' stand out for readability */
+ DOH_SLOT_IPV6 = 1, /* 'V6' likewise */
+
+ /* Space here for (possibly build-specific) additional slot definitions */
+#ifdef USE_HTTPSRR
+ DOH_SLOT_HTTPS_RR = 2, /* for HTTPS RR */
+#endif
+
+ /* for example */
+ /* #ifdef WANT_DOH_FOOBAR_TXT */
+ /* DOH_PROBE_SLOT_FOOBAR_TXT, */
+ /* #endif */
+
+ /* AFTER all slot definitions, establish how many we have */
+ DOH_SLOT_COUNT
+};
+
+struct doh_probes {
+ struct curl_slist *req_hds;
+ struct doh_probe probe[DOH_SLOT_COUNT];
+ unsigned int pending; /* still outstanding probes */
int port;
const char *host;
};
@@ -116,7 +137,7 @@ struct dohaddr {
#define HTTPS_RR_CODE_IPV6 0x06
/*
- * These may need escaping when found within an alpn string
+ * These may need escaping when found within an ALPN string
* value.
*/
#define COMMA_CHAR ','
@@ -144,15 +165,15 @@ void Curl_doh_close(struct Curl_easy *data);
void Curl_doh_cleanup(struct Curl_easy *data);
#ifdef UNITTESTS
-UNITTEST DOHcode doh_encode(const char *host,
- DNStype dnstype,
- unsigned char *dnsp, /* buffer */
- size_t len, /* buffer size */
- size_t *olen); /* output length */
-UNITTEST DOHcode doh_decode(const unsigned char *doh,
- size_t dohlen,
- DNStype dnstype,
- struct dohentry *d);
+UNITTEST DOHcode doh_req_encode(const char *host,
+ DNStype dnstype,
+ unsigned char *dnsp, /* buffer */
+ size_t len, /* buffer size */
+ size_t *olen); /* output length */
+UNITTEST DOHcode doh_resp_decode(const unsigned char *doh,
+ size_t dohlen,
+ DNStype dnstype,
+ struct dohentry *d);
UNITTEST void de_init(struct dohentry *d);
UNITTEST void de_cleanup(struct dohentry *d);
diff --git a/libs/libcurl/src/easy.c b/libs/libcurl/src/easy.c
index 034dd93d99..9ce9e3aba7 100644
--- a/libs/libcurl/src/easy.c
+++ b/libs/libcurl/src/easy.c
@@ -113,6 +113,7 @@ static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT;
#endif
#if defined(_MSC_VER) && defined(_DLL)
+# pragma warning(push)
# pragma warning(disable:4232) /* MSVC extension, dllimport identity */
#endif
@@ -130,7 +131,7 @@ curl_wcsdup_callback Curl_cwcsdup = Curl_wcsdup;
#endif
#if defined(_MSC_VER) && defined(_DLL)
-# pragma warning(default:4232) /* MSVC extension, dllimport identity */
+# pragma warning(pop)
#endif
#ifdef DEBUGBUILD
@@ -390,25 +391,22 @@ struct events {
int running_handles; /* store the returned number */
};
+#define DEBUG_EV_POLL 0
+
/* 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 */
-
+#if DEBUG_EV_POLL
+ fprintf(stderr, "events_timer: set timeout %ldms\n", timeout_ms);
+#endif
ev->ms = timeout_ms;
ev->msbump = TRUE;
return 0;
@@ -462,6 +460,7 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */
struct events *ev = userp;
struct socketmonitor *m;
struct socketmonitor *prev = NULL;
+ bool found = FALSE;
#if defined(CURL_DISABLE_VERBOSE_STRINGS)
(void) easy;
@@ -471,7 +470,7 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */
m = ev->list;
while(m) {
if(m->socket.fd == s) {
-
+ found = TRUE;
if(what == CURL_POLL_REMOVE) {
struct socketmonitor *nxt = m->next;
/* remove this node from the list of monitored sockets */
@@ -480,15 +479,13 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */
else
ev->list = nxt;
free(m);
- m = nxt;
- infof(easy, "socket cb: socket %" CURL_FORMAT_SOCKET_T
- " REMOVED", s);
+ infof(easy, "socket cb: socket %" FMT_SOCKET_T " 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 %" CURL_FORMAT_SOCKET_T
+ infof(easy, "socket cb: socket %" FMT_SOCKET_T
" UPDATED as %s%s", s,
(what&CURL_POLL_IN)?"IN":"",
(what&CURL_POLL_OUT)?"OUT":"");
@@ -498,12 +495,13 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */
prev = m;
m = m->next; /* move to next node */
}
- if(!m) {
+
+ if(!found) {
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); */
+ /* should not happen if our logic is correct, but is no drama. */
+ DEBUGF(infof(easy, "socket cb: asked to REMOVE socket %"
+ FMT_SOCKET_T "but not present!", s));
+ DEBUGASSERT(0);
}
else {
m = malloc(sizeof(struct socketmonitor));
@@ -513,8 +511,7 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */
m->socket.events = socketcb2poll(what);
m->socket.revents = 0;
ev->list = m;
- infof(easy, "socket cb: socket %" CURL_FORMAT_SOCKET_T
- " ADDED as %s%s", s,
+ infof(easy, "socket cb: socket %" FMT_SOCKET_T " ADDED as %s%s", s,
(what&CURL_POLL_IN)?"IN":"",
(what&CURL_POLL_OUT)?"OUT":"");
}
@@ -564,14 +561,15 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
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); */
+#if DEBUG_EV_POLL
+ fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd);
+#endif
f++;
numfds++;
}
@@ -579,12 +577,27 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
/* 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, (unsigned int)numfds, ev->ms);
- if(pollrc < 0)
- return CURLE_UNRECOVERABLE_POLL;
-
- after = Curl_now();
+ if(numfds) {
+ /* wait for activity or timeout */
+#if DEBUG_EV_POLL
+ fprintf(stderr, "poll(numfds=%d, timeout=%ldms)\n", numfds, ev->ms);
+#endif
+ pollrc = Curl_poll(fds, (unsigned int)numfds, ev->ms);
+#if DEBUG_EV_POLL
+ fprintf(stderr, "poll(numfds=%d, timeout=%ldms) -> %d\n",
+ numfds, ev->ms, pollrc);
+#endif
+ if(pollrc < 0)
+ return CURLE_UNRECOVERABLE_POLL;
+ }
+ else {
+#if DEBUG_EV_POLL
+ fprintf(stderr, "poll, but no fds, wait timeout=%ldms\n", ev->ms);
+#endif
+ pollrc = 0;
+ if(ev->ms > 0)
+ Curl_wait_ms(ev->ms);
+ }
ev->msbump = FALSE; /* reset here */
@@ -597,26 +610,37 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
}
else {
/* here pollrc is > 0 */
+ struct Curl_llist_node *e = Curl_llist_head(&multi->process);
+ struct Curl_easy *data;
+ DEBUGASSERT(e);
+ data = Curl_node_elem(e);
+ DEBUGASSERT(data);
/* 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 "
- "%" CURL_FORMAT_SOCKET_T ")", fds[i].fd);
+
+ /* sending infof "randomly" to the first easy handle */
+ infof(data, "call curl_multi_socket_action(socket "
+ "%" FMT_SOCKET_T ")", (curl_socket_t)fds[i].fd);
mcode = curl_multi_socket_action(multi, fds[i].fd, act,
&ev->running_handles);
}
}
- if(!ev->msbump) {
+
+ if(!ev->msbump && ev->ms >= 0) {
/* 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);
+ timediff_t timediff = Curl_timediff(Curl_now(), before);
if(timediff > 0) {
+#if DEBUG_EV_POLL
+ fprintf(stderr, "poll timeout %ldms not updated, decrease by "
+ "time spent %ldms\n", ev->ms, (long)timediff);
+#endif
if(timediff > ev->ms)
ev->ms = 0;
else
@@ -649,7 +673,7 @@ 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};
+ static struct events evs = {-1, FALSE, 0, NULL, 0};
/* if running event-based, do some further multi inits */
events_setup(multi, &evs);
@@ -914,8 +938,7 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
Curl_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER);
- /* the connection cache is setup on demand */
- outcurl->state.conn_cache = NULL;
+ /* the connection pool is setup on demand */
outcurl->state.lastconnect_id = -1;
outcurl->state.recent_conn_id = -1;
outcurl->id = -1;
@@ -1012,7 +1035,9 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
goto fail;
}
#endif /* USE_ARES */
-
+#ifndef CURL_DISABLE_HTTP
+ Curl_llist_init(&outcurl->state.httphdrs, NULL);
+#endif
Curl_initinfo(outcurl);
outcurl->magic = CURLEASY_MAGIC_NUMBER;
@@ -1112,7 +1137,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
(data->mstate == MSTATE_PERFORMING ||
data->mstate == MSTATE_RATELIMITING));
/* Unpausing writes is detected on the next run in
- * transfer.c:Curl_readwrite(). This is because this may result
+ * transfer.c:Curl_sendrecv(). This is because this may result
* in a transfer error if the application's callbacks fail */
/* Set the new keepon state, so it takes effect no matter what error
@@ -1264,7 +1289,7 @@ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer,
Curl_attach_connection(data, c);
sigpipe_ignore(data, &pipe_st);
- result = Curl_conn_send(data, FIRSTSOCKET, buffer, buflen, n);
+ result = Curl_conn_send(data, FIRSTSOCKET, buffer, buflen, FALSE, n);
sigpipe_restore(&pipe_st);
if(result && result != CURLE_AGAIN)
@@ -1290,47 +1315,6 @@ CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer,
}
/*
- * Wrapper to call functions in Curl_conncache_foreach()
- *
- * Returns always 0.
- */
-static int conn_upkeep(struct Curl_easy *data,
- struct connectdata *conn,
- void *param)
-{
- struct curltime *now = param;
-
- if(Curl_timediff(*now, conn->keepalive) <= data->set.upkeep_interval_ms)
- return 0;
-
- /* briefly attach for action */
- Curl_attach_connection(data, conn);
- if(conn->handler->connection_check) {
- /* Do a protocol-specific keepalive check on the connection. */
- conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE);
- }
- else {
- /* Do the generic action on the FIRSTSOCKET filter chain */
- Curl_conn_keep_alive(data, conn, FIRSTSOCKET);
- }
- Curl_detach_connection(data);
-
- conn->keepalive = *now;
- return 0; /* continue iteration */
-}
-
-static CURLcode upkeep(struct conncache *conn_cache, void *data)
-{
- struct curltime now = Curl_now();
- /* Loop over every connection and make connection alive. */
- Curl_conncache_foreach(data,
- conn_cache,
- &now,
- conn_upkeep);
- return CURLE_OK;
-}
-
-/*
* Performs connection upkeep for the given session handle.
*/
CURLcode curl_easy_upkeep(struct Curl_easy *data)
@@ -1339,12 +1323,9 @@ CURLcode curl_easy_upkeep(struct Curl_easy *data)
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;
- }
+ if(Curl_is_in_callback(data))
+ return CURLE_RECURSIVE_API_CALL;
+
+ /* Use the common function to keep connections alive. */
+ return Curl_cpool_upkeep(data);
}
diff --git a/libs/libcurl/src/escape.c b/libs/libcurl/src/escape.c
index 96e108cd2c..1b708a64f6 100644
--- a/libs/libcurl/src/escape.c
+++ b/libs/libcurl/src/escape.c
@@ -63,12 +63,12 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string,
if(!string || (inlength < 0))
return NULL;
- Curl_dyn_init(&d, CURL_MAX_INPUT_LENGTH * 3);
-
length = (inlength?(size_t)inlength:strlen(string));
if(!length)
return strdup("");
+ Curl_dyn_init(&d, length * 3 + 1);
+
while(length--) {
/* treat the characters unsigned */
unsigned char in = (unsigned char)*string++;
diff --git a/libs/libcurl/src/file.c b/libs/libcurl/src/file.c
index 0d3040d092..8e9443c9cd 100644
--- a/libs/libcurl/src/file.c
+++ b/libs/libcurl/src/file.c
@@ -223,7 +223,7 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done)
* 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
+ * this first. Failing that, we treat the path as a real Unix
* path, but only if the application was compiled with -lunix.
*/
fd = -1;
@@ -468,8 +468,7 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
static const char accept_ranges[]= { "Accept-ranges: bytes\r\n" };
if(expected_size >= 0) {
headerlen =
- msnprintf(header, sizeof(header),
- "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n",
+ msnprintf(header, sizeof(header), "Content-Length: %" FMT_OFF_T "\r\n",
expected_size);
result = Curl_client_write(data, CLIENTWRITE_HEADER, header, headerlen);
if(result)
diff --git a/libs/libcurl/src/fileinfo.h b/libs/libcurl/src/fileinfo.h
index 4868bacecb..bf32b3a003 100644
--- a/libs/libcurl/src/fileinfo.h
+++ b/libs/libcurl/src/fileinfo.h
@@ -30,7 +30,7 @@
struct fileinfo {
struct curl_fileinfo info;
- struct Curl_llist_element list;
+ struct Curl_llist_node list;
struct dynbuf buf;
};
diff --git a/libs/libcurl/src/formdata.c b/libs/libcurl/src/formdata.c
index 6125d3c8bd..3f5ec456b7 100644
--- a/libs/libcurl/src/formdata.c
+++ b/libs/libcurl/src/formdata.c
@@ -790,10 +790,10 @@ static CURLcode setname(curl_mimepart *part, const char *name, size_t len)
/* wrap call to fseeko so it matches the calling convention of callback */
static int fseeko_wrapper(void *stream, curl_off_t offset, int whence)
{
-#if defined(HAVE_FSEEKO) && defined(HAVE_DECL_FSEEKO)
- return fseeko(stream, (off_t)offset, whence);
-#elif defined(HAVE__FSEEKI64)
+#if defined(HAVE__FSEEKI64)
return _fseeki64(stream, (__int64)offset, whence);
+#elif defined(HAVE_FSEEKO) && defined(HAVE_DECL_FSEEKO)
+ return fseeko(stream, (off_t)offset, whence);
#else
if(offset > LONG_MAX)
return -1;
diff --git a/libs/libcurl/src/ftp.c b/libs/libcurl/src/ftp.c
index 17fa5a6c39..dd3180e592 100644
--- a/libs/libcurl/src/ftp.c
+++ b/libs/libcurl/src/ftp.c
@@ -188,7 +188,7 @@ 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 */
+ char *newhost, /* ASCII version */
int port);
#endif
static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data);
@@ -327,7 +327,6 @@ static void freedirs(struct ftp_conn *ftpc)
Curl_safefree(ftpc->newhost);
}
-#ifdef CURL_DO_LINEEND_CONV
/***********************************************************************
*
* Lineend Conversions
@@ -369,7 +368,6 @@ static CURLcode ftp_cw_lc_write(struct Curl_easy *data,
}
/* either we just wrote the newline or it is part of the next
* chunk of bytes we write. */
- data->state.crlf_conversions++;
ctx->newline_pending = FALSE;
}
@@ -400,7 +398,6 @@ static CURLcode ftp_cw_lc_write(struct Curl_easy *data,
/* EndOfStream, if we have a trailing cr, now is the time to write it */
if(ctx->newline_pending) {
ctx->newline_pending = FALSE;
- data->state.crlf_conversions++;
return Curl_cwriter_write(data, writer->next, type, &nl, 1);
}
/* Always pass on the EOS type indicator */
@@ -418,7 +415,6 @@ static const struct Curl_cwtype ftp_cw_lc = {
sizeof(struct ftp_cw_lc_ctx)
};
-#endif /* CURL_DO_LINEEND_CONV */
/***********************************************************************
*
* AcceptServerConnect()
@@ -819,6 +815,8 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
int cache_skip = 0;
int value_to_be_ignored = 0;
+ CURL_TRC_FTP(data, "getFTPResponse start");
+
if(ftpcode)
*ftpcode = 0; /* 0 for errors */
else
@@ -864,21 +862,27 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
*/
}
else if(!Curl_conn_data_pending(data, FIRSTSOCKET)) {
- switch(SOCKET_READABLE(sockfd, interval_ms)) {
- case -1: /* select() error, stop reading */
+ curl_socket_t wsock = Curl_pp_needs_flush(data, pp)?
+ sockfd : CURL_SOCKET_BAD;
+ int ev = Curl_socket_check(sockfd, CURL_SOCKET_BAD, wsock, interval_ms);
+ if(ev < 0) {
failf(data, "FTP response aborted due to select/poll error: %d",
SOCKERRNO);
return CURLE_RECV_ERROR;
-
- case 0: /* timeout */
+ }
+ else if(ev == 0) {
if(Curl_pgrsUpdate(data))
return CURLE_ABORTED_BY_CALLBACK;
continue; /* just continue in our loop for the timeout duration */
+ }
+ }
- default: /* for clarity */
+ if(Curl_pp_needs_flush(data, pp)) {
+ result = Curl_pp_flushsend(data, pp);
+ if(result)
break;
- }
}
+
result = ftp_readresp(data, FIRSTSOCKET, pp, ftpcode, &nread);
if(result)
break;
@@ -897,6 +901,8 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
} /* while there is buffer left and loop is requested */
pp->pending_resp = FALSE;
+ CURL_TRC_FTP(data, "getFTPResponse -> result=%d, nread=%zd, ftpcode=%d",
+ result, *nreadp, *ftpcode);
return result;
}
@@ -1042,7 +1048,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
int error;
char *host = NULL;
char *string_ftpport = data->set.str[STRING_FTPPORT];
- struct Curl_dns_entry *h = NULL;
+ struct Curl_dns_entry *dns_entry = NULL;
unsigned short port_min = 0;
unsigned short port_max = 0;
unsigned short port;
@@ -1178,15 +1184,12 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
}
/* resolv ip/host to ip */
- rc = Curl_resolv(data, host, 0, FALSE, &h);
+ rc = Curl_resolv(data, host, 0, FALSE, &dns_entry);
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) */
+ (void)Curl_resolver_wait_resolv(data, &dns_entry);
+ if(dns_entry) {
+ res = dns_entry->addr;
+ }
else
res = NULL; /* failure! */
@@ -1381,6 +1384,9 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
ftp_state(data, FTP_PORT);
out:
+ /* If we looked up a dns_entry, now is the time to safely release it */
+ if(dns_entry)
+ Curl_resolv_unlink(data, &dns_entry);
if(result) {
ftp_state(data, FTP_STOP);
}
@@ -2072,7 +2078,6 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
/* postponed address resolution in case of tcp fastopen */
if(conn->bits.tcp_fastopen && !conn->bits.reuse && !ftpc->newhost[0]) {
- Curl_conn_ev_update_info(data, conn);
Curl_safefree(ftpc->newhost);
ftpc->newhost = strdup(control_address(conn));
if(!ftpc->newhost)
@@ -2098,7 +2103,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE);
if(result) {
- Curl_resolv_unlock(data, addr); /* we are done using this address */
+ Curl_resolv_unlink(data, &addr); /* we are done using this address */
if(ftpc->count1 == 0 && ftpcode == 229)
return ftp_epsv_disable(data, conn);
@@ -2116,7 +2121,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
/* this just dumps information about this second connection */
ftp_pasv_verbose(data, addr->addr, ftpc->newhost, connectport);
- Curl_resolv_unlock(data, addr); /* we are done using this address */
+ Curl_resolv_unlink(data, &addr); /* we are done using this address */
Curl_safefree(conn->secondaryhostname);
conn->secondary_port = ftpc->newport;
@@ -2383,8 +2388,8 @@ static CURLcode ftp_state_retr(struct Curl_easy *data,
if(data->state.resume_from< 0) {
/* We are 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 ")",
+ failf(data, "Offset (%" FMT_OFF_T
+ ") was beyond file size (%" FMT_OFF_T ")",
data->state.resume_from, filesize);
return CURLE_BAD_DOWNLOAD_RESUME;
}
@@ -2395,8 +2400,8 @@ static CURLcode ftp_state_retr(struct Curl_easy *data,
}
else {
if(filesize < data->state.resume_from) {
- failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
- ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
+ failf(data, "Offset (%" FMT_OFF_T
+ ") was beyond file size (%" FMT_OFF_T ")",
data->state.resume_from, filesize);
return CURLE_BAD_DOWNLOAD_RESUME;
}
@@ -2418,10 +2423,10 @@ static CURLcode ftp_state_retr(struct Curl_easy *data,
}
/* Set resume file transfer offset */
- infof(data, "Instructs server to resume from offset %"
- CURL_FORMAT_CURL_OFF_T, data->state.resume_from);
+ infof(data, "Instructs server to resume from offset %" FMT_OFF_T,
+ data->state.resume_from);
- result = Curl_pp_sendf(data, &ftpc->pp, "REST %" CURL_FORMAT_CURL_OFF_T,
+ result = Curl_pp_sendf(data, &ftpc->pp, "REST %" FMT_OFF_T,
data->state.resume_from);
if(!result)
ftp_state(data, FTP_RETR_REST);
@@ -2479,7 +2484,7 @@ static CURLcode ftp_state_size_resp(struct Curl_easy *data,
if(-1 != filesize) {
char clbuf[128];
int clbuflen = msnprintf(clbuf, sizeof(clbuf),
- "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", filesize);
+ "Content-Length: %" FMT_OFF_T "\r\n", filesize);
result = client_write_header(data, clbuf, clbuflen);
if(result)
return result;
@@ -2659,12 +2664,10 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data,
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);
+ infof(data, "Maxdownload = %" FMT_OFF_T, data->req.maxdownload);
if(instate != FTP_LIST)
- infof(data, "Getting file with size: %" CURL_FORMAT_CURL_OFF_T,
- size);
+ infof(data, "Getting file with size: %" FMT_OFF_T, size);
/* FTP download: */
conn->proto.ftpc.state_saved = instate;
@@ -3529,8 +3532,8 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
(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)",
+ failf(data, "Uploaded unaligned file size (%" FMT_OFF_T
+ " out of %" FMT_OFF_T " bytes)",
data->req.writebytecount, data->state.infilesize);
result = CURLE_PARTIAL_FILE;
}
@@ -3538,17 +3541,9 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
else {
if((-1 != data->req.size) &&
(data->req.size != data->req.bytecount) &&
-#ifdef CURL_DO_LINEEND_CONV
- /* Most FTP servers do not adjust their file SIZE response for CRLFs,
- * so we will check to see if the discrepancy can be explained by the
- * number of CRLFs we have 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);
+ failf(data, "Received only partial file: %" FMT_OFF_T " bytes",
+ data->req.bytecount);
result = CURLE_PARTIAL_FILE;
}
else if(!ftpc->dont_check &&
@@ -3684,7 +3679,7 @@ static CURLcode ftp_nb_type(struct Curl_easy *data,
static void
ftp_pasv_verbose(struct Curl_easy *data,
struct Curl_addrinfo *ai,
- char *newhost, /* ascii version */
+ char *newhost, /* ASCII version */
int port)
{
char buf[256];
@@ -4027,7 +4022,7 @@ static CURLcode wc_statemach(struct Curl_easy *data)
wildcard->state = CURLWC_CLEAN;
continue;
}
- if(wildcard->filelist.size == 0) {
+ if(Curl_llist_count(&wildcard->filelist) == 0) {
/* no corresponding file */
wildcard->state = CURLWC_CLEAN;
return CURLE_REMOTE_FILE_NOT_FOUND;
@@ -4038,7 +4033,8 @@ static CURLcode wc_statemach(struct Curl_easy *data)
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 Curl_llist_node *head = Curl_llist_head(&wildcard->filelist);
+ struct curl_fileinfo *finfo = Curl_node_elem(head);
struct FTP *ftp = data->req.p.ftp;
char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename);
@@ -4054,7 +4050,8 @@ static CURLcode wc_statemach(struct Curl_easy *data)
long userresponse;
Curl_set_in_callback(data, true);
userresponse = data->set.chunk_bgn(
- finfo, data->set.wildcardptr, (int)wildcard->filelist.size);
+ finfo, data->set.wildcardptr,
+ (int)Curl_llist_count(&wildcard->filelist));
Curl_set_in_callback(data, false);
switch(userresponse) {
case CURL_CHUNK_BGN_FUNC_SKIP:
@@ -4080,9 +4077,10 @@ static CURLcode wc_statemach(struct Curl_easy *data)
return result;
/* we do not need the Curl_fileinfo of first file anymore */
- Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
+ Curl_node_remove(Curl_llist_head(&wildcard->filelist));
- if(wildcard->filelist.size == 0) { /* remains only one file to down. */
+ if(Curl_llist_count(&wildcard->filelist) == 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 */
@@ -4097,8 +4095,8 @@ static CURLcode wc_statemach(struct Curl_easy *data)
data->set.chunk_end(data->set.wildcardptr);
Curl_set_in_callback(data, false);
}
- Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
- wildcard->state = (wildcard->filelist.size == 0) ?
+ Curl_node_remove(Curl_llist_head(&wildcard->filelist));
+ wildcard->state = (Curl_llist_count(&wildcard->filelist) == 0) ?
CURLWC_CLEAN : CURLWC_DOWNLOADING;
continue;
}
@@ -4140,27 +4138,22 @@ 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;
+ /* FTP data may need conversion. */
+ struct Curl_cwriter *ftp_lc_writer;
*done = FALSE; /* default to false */
ftpc->wait_data_conn = FALSE; /* default to no such wait */
-#ifdef CURL_DO_LINEEND_CONV
- {
- /* FTP data may need conversion. */
- struct Curl_cwriter *ftp_lc_writer;
-
- result = Curl_cwriter_create(&ftp_lc_writer, data, &ftp_cw_lc,
- CURL_CW_CONTENT_DECODE);
- if(result)
- return result;
+ result = Curl_cwriter_create(&ftp_lc_writer, data, &ftp_cw_lc,
+ CURL_CW_CONTENT_DECODE);
+ if(result)
+ return result;
- result = Curl_cwriter_add(data, ftp_lc_writer);
- if(result) {
- Curl_cwriter_free(data, ftp_lc_writer);
- return result;
- }
+ result = Curl_cwriter_add(data, ftp_lc_writer);
+ if(result) {
+ Curl_cwriter_free(data, ftp_lc_writer);
+ return result;
}
-#endif /* CURL_DO_LINEEND_CONV */
if(data->state.wildcardmatch) {
result = wc_statemach(data);
diff --git a/libs/libcurl/src/getinfo.c b/libs/libcurl/src/getinfo.c
index 08d9a332f6..001c9fc671 100644
--- a/libs/libcurl/src/getinfo.c
+++ b/libs/libcurl/src/getinfo.c
@@ -53,6 +53,7 @@ CURLcode Curl_initinfo(struct Curl_easy *data)
pro->t_connect = 0;
pro->t_appconnect = 0;
pro->t_pretransfer = 0;
+ pro->t_posttransfer = 0;
pro->t_starttransfer = 0;
pro->timespent = 0;
pro->t_redirect = 0;
@@ -76,10 +77,9 @@ CURLcode Curl_initinfo(struct Curl_easy *data)
free(info->wouldredirect);
info->wouldredirect = NULL;
- info->primary.remote_ip[0] = '\0';
- info->primary.local_ip[0] = '\0';
- info->primary.remote_port = 0;
- info->primary.local_port = 0;
+ memset(&info->primary, 0, sizeof(info->primary));
+ info->primary.remote_port = -1;
+ info->primary.local_port = -1;
info->retry_after = 0;
info->conn_scheme = 0;
@@ -252,11 +252,13 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
case CURLINFO_SSL_VERIFYRESULT:
*param_longp = data->set.ssl.certverifyresult;
break;
-#ifndef CURL_DISABLE_PROXY
case CURLINFO_PROXY_SSL_VERIFYRESULT:
+#ifndef CURL_DISABLE_PROXY
*param_longp = data->set.proxy_ssl.certverifyresult;
- break;
+#else
+ *param_longp = 0;
#endif
+ break;
case CURLINFO_REDIRECT_COUNT:
*param_longp = data->state.followlocation;
break;
@@ -314,6 +316,12 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
case CURLINFO_RTSP_CSEQ_RECV:
*param_longp = data->state.rtsp_CSeq_recv;
break;
+#else
+ case CURLINFO_RTSP_CLIENT_CSEQ:
+ case CURLINFO_RTSP_SERVER_CSEQ:
+ case CURLINFO_RTSP_CSEQ_RECV:
+ *param_longp = 0;
+ break;
#endif
case CURLINFO_HTTP_VERSION:
switch(data->info.httpversion) {
@@ -368,6 +376,7 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info,
case CURLINFO_CONNECT_TIME_T:
case CURLINFO_APPCONNECT_TIME_T:
case CURLINFO_PRETRANSFER_TIME_T:
+ case CURLINFO_POSTTRANSFER_TIME_T:
case CURLINFO_STARTTRANSFER_TIME_T:
case CURLINFO_REDIRECT_TIME_T:
case CURLINFO_SPEED_DOWNLOAD_T:
@@ -384,24 +393,24 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info,
*param_offt = (curl_off_t)data->info.filetime;
break;
case CURLINFO_SIZE_UPLOAD_T:
- *param_offt = data->progress.uploaded;
+ *param_offt = data->progress.ul.cur_size;
break;
case CURLINFO_SIZE_DOWNLOAD_T:
- *param_offt = data->progress.downloaded;
+ *param_offt = data->progress.dl.cur_size;
break;
case CURLINFO_SPEED_DOWNLOAD_T:
- *param_offt = data->progress.dlspeed;
+ *param_offt = data->progress.dl.speed;
break;
case CURLINFO_SPEED_UPLOAD_T:
- *param_offt = data->progress.ulspeed;
+ *param_offt = data->progress.ul.speed;
break;
case CURLINFO_CONTENT_LENGTH_DOWNLOAD_T:
*param_offt = (data->progress.flags & PGRS_DL_SIZE_KNOWN)?
- data->progress.size_dl:-1;
+ data->progress.dl.total_size:-1;
break;
case CURLINFO_CONTENT_LENGTH_UPLOAD_T:
*param_offt = (data->progress.flags & PGRS_UL_SIZE_KNOWN)?
- data->progress.size_ul:-1;
+ data->progress.ul.total_size:-1;
break;
case CURLINFO_TOTAL_TIME_T:
*param_offt = data->progress.timespent;
@@ -418,6 +427,9 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info,
case CURLINFO_PRETRANSFER_TIME_T:
*param_offt = data->progress.t_pretransfer;
break;
+ case CURLINFO_POSTTRANSFER_TIME_T:
+ *param_offt = data->progress.t_posttransfer;
+ break;
case CURLINFO_STARTTRANSFER_TIME_T:
*param_offt = data->progress.t_starttransfer;
break;
@@ -488,24 +500,24 @@ static CURLcode getinfo_double(struct Curl_easy *data, CURLINFO info,
*param_doublep = DOUBLE_SECS(data->progress.t_starttransfer);
break;
case CURLINFO_SIZE_UPLOAD:
- *param_doublep = (double)data->progress.uploaded;
+ *param_doublep = (double)data->progress.ul.cur_size;
break;
case CURLINFO_SIZE_DOWNLOAD:
- *param_doublep = (double)data->progress.downloaded;
+ *param_doublep = (double)data->progress.dl.cur_size;
break;
case CURLINFO_SPEED_DOWNLOAD:
- *param_doublep = (double)data->progress.dlspeed;
+ *param_doublep = (double)data->progress.dl.speed;
break;
case CURLINFO_SPEED_UPLOAD:
- *param_doublep = (double)data->progress.ulspeed;
+ *param_doublep = (double)data->progress.ul.speed;
break;
case CURLINFO_CONTENT_LENGTH_DOWNLOAD:
*param_doublep = (data->progress.flags & PGRS_DL_SIZE_KNOWN)?
- (double)data->progress.size_dl:-1;
+ (double)data->progress.dl.total_size:-1;
break;
case CURLINFO_CONTENT_LENGTH_UPLOAD:
*param_doublep = (data->progress.flags & PGRS_UL_SIZE_KNOWN)?
- (double)data->progress.size_ul:-1;
+ (double)data->progress.ul.total_size:-1;
break;
case CURLINFO_REDIRECT_TIME:
*param_doublep = DOUBLE_SECS(data->progress.t_redirect);
diff --git a/libs/libcurl/src/gopher.c b/libs/libcurl/src/gopher.c
index 70cd99cf3b..220dc21807 100644
--- a/libs/libcurl/src/gopher.c
+++ b/libs/libcurl/src/gopher.c
@@ -187,7 +187,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
if(strlen(sel) < 1)
break;
- result = Curl_xfer_send(data, sel, k, &amount);
+ result = Curl_xfer_send(data, sel, k, FALSE, &amount);
if(!result) { /* Which may not have written it all! */
result = Curl_client_write(data, CLIENTWRITE_HEADER, sel, amount);
if(result)
@@ -229,7 +229,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
free(sel_org);
if(!result)
- result = Curl_xfer_send(data, "\r\n", 2, &amount);
+ result = Curl_xfer_send(data, "\r\n", 2, FALSE, &amount);
if(result) {
failf(data, "Failed sending Gopher request");
return result;
diff --git a/libs/libcurl/src/hash.c b/libs/libcurl/src/hash.c
index ca55a4bb04..64810bd1df 100644
--- a/libs/libcurl/src/hash.c
+++ b/libs/libcurl/src/hash.c
@@ -33,6 +33,10 @@
/* The last #include file should be: */
#include "memdebug.h"
+/* random patterns for API verification */
+#define HASHINIT 0x7017e781
+#define ITERINIT 0x5FEDCBA9
+
static void
hash_element_dtor(void *user, void *element)
{
@@ -77,6 +81,9 @@ Curl_hash_init(struct Curl_hash *h,
h->dtor = dtor;
h->size = 0;
h->slots = slots;
+#ifdef DEBUGBUILD
+ h->init = HASHINIT;
+#endif
}
static struct Curl_hash_element *
@@ -102,11 +109,12 @@ void *Curl_hash_add2(struct Curl_hash *h, void *key, size_t key_len, void *p,
Curl_hash_elem_dtor dtor)
{
struct Curl_hash_element *he;
- struct Curl_llist_element *le;
+ struct Curl_llist_node *le;
struct Curl_llist *l;
DEBUGASSERT(h);
DEBUGASSERT(h->slots);
+ DEBUGASSERT(h->init == HASHINIT);
if(!h->table) {
size_t i;
h->table = malloc(h->slots * sizeof(struct Curl_llist));
@@ -118,10 +126,10 @@ void *Curl_hash_add2(struct Curl_hash *h, void *key, size_t key_len, void *p,
l = FETCH_LIST(h, key, key_len);
- for(le = l->head; le; le = le->next) {
- he = (struct Curl_hash_element *) le->ptr;
+ for(le = Curl_llist_head(l); le; le = Curl_node_next(le)) {
+ he = (struct Curl_hash_element *) Curl_node_elem(le);
if(h->comp_func(he->key, he->key_len, key, key_len)) {
- Curl_llist_remove(l, le, (void *)h);
+ Curl_node_uremove(le, (void *)h);
--h->size;
break;
}
@@ -158,18 +166,17 @@ 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)
{
- struct Curl_llist_element *le;
- struct Curl_llist *l;
-
DEBUGASSERT(h);
DEBUGASSERT(h->slots);
+ DEBUGASSERT(h->init == HASHINIT);
if(h->table) {
- l = FETCH_LIST(h, key, key_len);
+ struct Curl_llist_node *le;
+ struct Curl_llist *l = FETCH_LIST(h, key, key_len);
- for(le = l->head; le; le = le->next) {
- struct Curl_hash_element *he = le->ptr;
+ for(le = Curl_llist_head(l); le; le = Curl_node_next(le)) {
+ struct Curl_hash_element *he = Curl_node_elem(le);
if(h->comp_func(he->key, he->key_len, key, key_len)) {
- Curl_llist_remove(l, le, (void *) h);
+ Curl_node_uremove(le, (void *) h);
--h->size;
return 0;
}
@@ -185,15 +192,15 @@ int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len)
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);
+ DEBUGASSERT(h->init == HASHINIT);
if(h->table) {
+ struct Curl_llist_node *le;
+ struct Curl_llist *l;
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;
+ for(le = Curl_llist_head(l); le; le = Curl_node_next(le)) {
+ struct Curl_hash_element *he = Curl_node_elem(le);
if(h->comp_func(he->key, he->key_len, key, key_len)) {
return he->ptr;
}
@@ -213,6 +220,7 @@ Curl_hash_pick(struct Curl_hash *h, void *key, size_t key_len)
void
Curl_hash_destroy(struct Curl_hash *h)
{
+ DEBUGASSERT(h->init == HASHINIT);
if(h->table) {
size_t i;
for(i = 0; i < h->slots; ++i) {
@@ -234,28 +242,33 @@ Curl_hash_clean(struct Curl_hash *h)
Curl_hash_clean_with_criterium(h, NULL, NULL);
}
+size_t Curl_hash_count(struct Curl_hash *h)
+{
+ DEBUGASSERT(h->init == HASHINIT);
+ return h->size;
+}
+
/* 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;
size_t i;
if(!h || !h->table)
return;
+ DEBUGASSERT(h->init == HASHINIT);
for(i = 0; i < h->slots; ++i) {
- list = &h->table[i];
- le = list->head; /* get first list entry */
+ struct Curl_llist *list = &h->table[i];
+ struct Curl_llist_node *le =
+ Curl_llist_head(list); /* get first list entry */
while(le) {
- struct Curl_hash_element *he = le->ptr;
- lnext = le->next;
+ struct Curl_hash_element *he = Curl_node_elem(le);
+ struct Curl_llist_node *lnext = Curl_node_next(le);
/* 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);
+ Curl_node_uremove(le, (void *) h);
--h->size; /* one less entry in the hash now */
}
le = lnext;
@@ -290,29 +303,34 @@ size_t Curl_str_key_compare(void *k1, size_t key1_len,
void Curl_hash_start_iterate(struct Curl_hash *hash,
struct Curl_hash_iterator *iter)
{
+ DEBUGASSERT(hash->init == HASHINIT);
iter->hash = hash;
iter->slot_index = 0;
iter->current_element = NULL;
+#ifdef DEBUGBUILD
+ iter->init = ITERINIT;
+#endif
}
struct Curl_hash_element *
Curl_hash_next_element(struct Curl_hash_iterator *iter)
{
- struct Curl_hash *h = iter->hash;
-
+ struct Curl_hash *h;
+ DEBUGASSERT(iter->init == ITERINIT);
+ 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;
+ iter->current_element = Curl_node_next(iter->current_element);
/* If we have reached the end of the list, find the next one */
if(!iter->current_element) {
size_t i;
for(i = iter->slot_index; i < h->slots; i++) {
- if(h->table[i].head) {
- iter->current_element = h->table[i].head;
+ if(Curl_llist_head(&h->table[i])) {
+ iter->current_element = Curl_llist_head(&h->table[i]);
iter->slot_index = i + 1;
break;
}
@@ -320,7 +338,7 @@ Curl_hash_next_element(struct Curl_hash_iterator *iter)
}
if(iter->current_element) {
- struct Curl_hash_element *he = iter->current_element->ptr;
+ struct Curl_hash_element *he = Curl_node_elem(iter->current_element);
return he;
}
return NULL;
diff --git a/libs/libcurl/src/hash.h b/libs/libcurl/src/hash.h
index 7e2abb38bf..c3387fbde6 100644
--- a/libs/libcurl/src/hash.h
+++ b/libs/libcurl/src/hash.h
@@ -56,22 +56,31 @@ struct Curl_hash {
Curl_hash_dtor dtor;
size_t slots;
size_t size;
+#ifdef DEBUGBUILD
+ int init;
+#endif
};
typedef void (*Curl_hash_elem_dtor)(void *key, size_t key_len, void *p);
struct Curl_hash_element {
- struct Curl_llist_element list;
+ struct Curl_llist_node list;
void *ptr;
Curl_hash_elem_dtor dtor;
size_t key_len;
+#ifdef DEBUGBUILD
+ int init;
+#endif
char key[1]; /* allocated memory following the struct */
};
struct Curl_hash_iterator {
struct Curl_hash *hash;
size_t slot_index;
- struct Curl_llist_element *current_element;
+ struct Curl_llist_node *current_element;
+#ifdef DEBUGBUILD
+ int init;
+#endif
};
void Curl_hash_init(struct Curl_hash *h,
@@ -85,8 +94,9 @@ void *Curl_hash_add2(struct Curl_hash *h, void *key, size_t key_len, void *p,
Curl_hash_elem_dtor dtor);
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);
-#define Curl_hash_count(h) ((h)->size)
+
void Curl_hash_destroy(struct Curl_hash *h);
+size_t Curl_hash_count(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 *));
diff --git a/libs/libcurl/src/headers.c b/libs/libcurl/src/headers.c
index 92f1f91938..4141d1bc3f 100644
--- a/libs/libcurl/src/headers.c
+++ b/libs/libcurl/src/headers.c
@@ -42,7 +42,7 @@
static void copy_header_external(struct Curl_header_store *hs,
size_t index,
size_t amount,
- struct Curl_llist_element *e,
+ struct Curl_llist_node *e,
struct curl_header *hout)
{
struct curl_header *h = hout;
@@ -66,8 +66,8 @@ CURLHcode curl_easy_header(CURL *easy,
int request,
struct curl_header **hout)
{
- struct Curl_llist_element *e;
- struct Curl_llist_element *e_pick = NULL;
+ struct Curl_llist_node *e;
+ struct Curl_llist_node *e_pick = NULL;
struct Curl_easy *data = easy;
size_t match = 0;
size_t amount = 0;
@@ -85,8 +85,8 @@ CURLHcode curl_easy_header(CURL *easy,
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;
+ for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
+ hs = Curl_node_elem(e);
if(strcasecompare(hs->name, name) &&
(hs->type & type) &&
(hs->request == request)) {
@@ -104,8 +104,8 @@ CURLHcode curl_easy_header(CURL *easy,
/* 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;
+ for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
+ hs = Curl_node_elem(e);
if(strcasecompare(hs->name, name) &&
(hs->type & type) &&
(hs->request == request) &&
@@ -131,8 +131,8 @@ struct curl_header *curl_easy_nextheader(CURL *easy,
struct curl_header *prev)
{
struct Curl_easy *data = easy;
- struct Curl_llist_element *pick;
- struct Curl_llist_element *e;
+ struct Curl_llist_node *pick;
+ struct Curl_llist_node *e;
struct Curl_header_store *hs;
size_t amount = 0;
size_t index = 0;
@@ -147,18 +147,18 @@ struct curl_header *curl_easy_nextheader(CURL *easy,
if(!pick)
/* something is wrong */
return NULL;
- pick = pick->next;
+ pick = Curl_node_next(pick);
}
else
- pick = data->state.httphdrs.head;
+ pick = Curl_llist_head(&data->state.httphdrs);
if(pick) {
/* make sure it is the next header of the desired type */
do {
- hs = pick->ptr;
+ hs = Curl_node_elem(pick);
if((hs->type & type) && (hs->request == request))
break;
- pick = pick->next;
+ pick = Curl_node_next(pick);
} while(pick);
}
@@ -166,12 +166,12 @@ struct curl_header *curl_easy_nextheader(CURL *easy,
/* no more headers available */
return NULL;
- hs = pick->ptr;
+ hs = Curl_node_elem(pick);
/* 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;
+ for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
+ struct Curl_header_store *check = Curl_node_elem(e);
if(strcasecompare(hs->name, check->name) &&
(check->request == request) &&
(check->type & type))
@@ -247,7 +247,7 @@ static CURLcode unfold_value(struct Curl_easy *data, const char *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);
+ Curl_node_remove(&hs->node);
/* new size = struct + new value length + old name+value length */
newhs = Curl_saferealloc(hs, sizeof(*hs) + vlen + oalloc + 1);
@@ -405,12 +405,12 @@ CURLcode Curl_headers_init(struct Curl_easy *data)
*/
CURLcode Curl_headers_cleanup(struct Curl_easy *data)
{
- struct Curl_llist_element *e;
- struct Curl_llist_element *n;
+ struct Curl_llist_node *e;
+ struct Curl_llist_node *n;
- for(e = data->state.httphdrs.head; e; e = n) {
- struct Curl_header_store *hs = e->ptr;
- n = e->next;
+ for(e = Curl_llist_head(&data->state.httphdrs); e; e = n) {
+ struct Curl_header_store *hs = Curl_node_elem(e);
+ n = Curl_node_next(e);
free(hs);
}
headers_reset(data);
diff --git a/libs/libcurl/src/headers.h b/libs/libcurl/src/headers.h
index f33e6c8585..592490d4e4 100644
--- a/libs/libcurl/src/headers.h
+++ b/libs/libcurl/src/headers.h
@@ -28,7 +28,7 @@
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API)
struct Curl_header_store {
- struct Curl_llist_element node;
+ struct Curl_llist_node node;
char *name; /* points into 'buffer' */
char *value; /* points into 'buffer */
int request; /* 0 is the first request, then 1.. 2.. */
diff --git a/libs/libcurl/src/hostasyn.c b/libs/libcurl/src/hostasyn.c
index 59a009ce33..87b38602fc 100644
--- a/libs/libcurl/src/hostasyn.c
+++ b/libs/libcurl/src/hostasyn.c
@@ -79,7 +79,7 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data,
dns = Curl_cache_addr(data, ai,
data->state.async.hostname, 0,
- data->state.async.port);
+ data->state.async.port, FALSE);
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
diff --git a/libs/libcurl/src/hostip.c b/libs/libcurl/src/hostip.c
index fcd21c9a32..ee5c17875d 100644
--- a/libs/libcurl/src/hostip.c
+++ b/libs/libcurl/src/hostip.c
@@ -115,7 +115,7 @@
* CURLRES_* defines based on the config*.h and curl_setup.h defines.
*/
-static void freednsentry(void *freethis);
+static void hostcache_unlink_entry(void *entry);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
static void show_resolve_info(struct Curl_easy *data,
@@ -178,7 +178,7 @@ create_hostcache_id(const char *name,
struct hostcache_prune_data {
time_t now;
time_t oldest; /* oldest time in cache not pruned. */
- int cache_timeout;
+ int max_age_sec;
};
/*
@@ -189,16 +189,16 @@ struct hostcache_prune_data {
* cache.
*/
static int
-hostcache_timestamp_remove(void *datap, void *hc)
+hostcache_entry_is_stale(void *datap, void *hc)
{
struct hostcache_prune_data *prune =
(struct hostcache_prune_data *) datap;
- struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
+ struct Curl_dns_entry *dns = (struct Curl_dns_entry *) hc;
- if(c->timestamp) {
+ if(dns->timestamp) {
/* age in seconds */
- time_t age = prune->now - c->timestamp;
- if(age >= prune->cache_timeout)
+ time_t age = prune->now - dns->timestamp;
+ if(age >= prune->max_age_sec)
return TRUE;
if(age > prune->oldest)
prune->oldest = age;
@@ -216,13 +216,13 @@ hostcache_prune(struct Curl_hash *hostcache, int cache_timeout,
{
struct hostcache_prune_data user;
- user.cache_timeout = cache_timeout;
+ user.max_age_sec = cache_timeout;
user.now = now;
user.oldest = 0;
Curl_hash_clean_with_criterium(hostcache,
(void *) &user,
- hostcache_timestamp_remove);
+ hostcache_entry_is_stale);
return user.oldest;
}
@@ -257,7 +257,8 @@ void Curl_hostcache_prune(struct Curl_easy *data)
/* if the cache size is still too big, use the oldest age as new
prune limit */
- } while(timeout && (data->dns.hostcache->size > MAX_DNS_CACHE_SIZE));
+ } while(timeout &&
+ (Curl_hash_count(data->dns.hostcache) > MAX_DNS_CACHE_SIZE));
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
@@ -299,10 +300,10 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
struct hostcache_prune_data user;
user.now = time(NULL);
- user.cache_timeout = data->set.dns_cache_timeout;
+ user.max_age_sec = data->set.dns_cache_timeout;
user.oldest = 0;
- if(hostcache_timestamp_remove(&user, dns)) {
+ if(hostcache_entry_is_stale(&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);
@@ -348,7 +349,7 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
*
* 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
+ * The returned data *MUST* be "released" with Curl_resolv_unlink() after
* use, or we will leak memory!
*/
struct Curl_dns_entry *
@@ -364,7 +365,7 @@ Curl_fetch_addr(struct Curl_easy *data,
dns = fetch_addr(data, hostname, port);
if(dns)
- dns->inuse++; /* we use it! */
+ dns->refcount++; /* we use it! */
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
@@ -468,7 +469,8 @@ Curl_cache_addr(struct Curl_easy *data,
struct Curl_addrinfo *addr,
const char *hostname,
size_t hostlen, /* length or zero */
- int port)
+ int port,
+ bool permanent)
{
char entry_id[MAX_HOSTCACHE_LEN];
size_t entry_len;
@@ -496,11 +498,15 @@ Curl_cache_addr(struct Curl_easy *data,
entry_len = create_hostcache_id(hostname, hostlen, port,
entry_id, sizeof(entry_id));
- dns->inuse = 1; /* the cache has the first reference */
+ dns->refcount = 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 */
+ if(permanent)
+ dns->timestamp = 0; /* an entry that never goes stale */
+ else {
+ dns->timestamp = time(NULL);
+ if(dns->timestamp == 0)
+ dns->timestamp = 1;
+ }
dns->hostport = port;
if(hostlen)
memcpy(dns->hostname, hostname, hostlen);
@@ -514,7 +520,7 @@ Curl_cache_addr(struct Curl_easy *data,
}
dns = dns2;
- dns->inuse++; /* mark entry as in-use */
+ dns->refcount++; /* mark entry as in-use */
return dns;
}
@@ -666,8 +672,8 @@ static bool tailmatch(const char *full, const char *part)
* 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 are
- * done using this struct) to decrease the counter again.
+ * function is used. You MUST call Curl_resolv_unlink() later (when you are
+ * done using this struct) to decrease the reference counter again.
*
* Return codes:
*
@@ -708,7 +714,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
if(dns) {
infof(data, "Hostname %s was found in DNS cache", hostname);
- dns->inuse++; /* we use it! */
+ dns->refcount++; /* we use it! */
rc = CURLRESOLV_RESOLVED;
}
@@ -828,7 +834,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
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, 0, port);
+ dns = Curl_cache_addr(data, addr, hostname, 0, port, FALSE);
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
@@ -868,8 +874,8 @@ void alarmfunc(int sig)
* 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 are
- * done using this struct) to decrease the counter again.
+ * function is used. You MUST call Curl_resolv_unlink() later (when you are
+ * done using this struct) to decrease the reference 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
@@ -955,7 +961,7 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
keep_copysig = TRUE; /* yes, we have a copy */
sigact.sa_handler = alarmfunc;
#ifdef SA_RESTART
- /* HPUX does not have SA_RESTART but defaults to that behavior! */
+ /* HP-UX does not have SA_RESTART but defaults to that behavior! */
sigact.sa_flags &= ~SA_RESTART;
#endif
/* now set the new struct */
@@ -1037,18 +1043,20 @@ clean_up:
}
/*
- * 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.
+ * Curl_resolv_unlink() releases a reference to the given cached DNS entry.
+ * When the reference count reaches 0, the entry is destroyed. It is important
+ * that only one unlink 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)
+void Curl_resolv_unlink(struct Curl_easy *data, struct Curl_dns_entry **pdns)
{
+ struct Curl_dns_entry *dns = *pdns;
+ *pdns = NULL;
if(data && data->share)
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
- freednsentry(dns);
+ hostcache_unlink_entry(dns);
if(data && data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
@@ -1057,13 +1065,13 @@ void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns)
/*
* File-internal: release cache dns entry reference, free if inuse drops to 0
*/
-static void freednsentry(void *freethis)
+static void hostcache_unlink_entry(void *entry)
{
- struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis;
- DEBUGASSERT(dns && (dns->inuse>0));
+ struct Curl_dns_entry *dns = (struct Curl_dns_entry *) entry;
+ DEBUGASSERT(dns && (dns->refcount>0));
- dns->inuse--;
- if(dns->inuse == 0) {
+ dns->refcount--;
+ if(dns->refcount == 0) {
Curl_freeaddrinfo(dns->addr);
#ifdef USE_HTTPSRR
if(dns->hinfo) {
@@ -1092,7 +1100,7 @@ static void freednsentry(void *freethis)
void Curl_init_dnscache(struct Curl_hash *hash, size_t size)
{
Curl_hash_init(hash, size, Curl_hash_str, Curl_str_key_compare,
- freednsentry);
+ hostcache_unlink_entry);
}
/*
@@ -1285,13 +1293,11 @@ err:
}
/* put this new host in the cache */
- dns = Curl_cache_addr(data, head, host_begin, hlen, port);
+ dns = Curl_cache_addr(data, head, host_begin, hlen, port, permanent);
if(dns) {
- if(permanent)
- dns->timestamp = 0; /* mark as permanent */
/* release the returned reference; the cache itself will keep the
* entry alive: */
- dns->inuse--;
+ dns->refcount--;
}
if(data->share)
@@ -1443,8 +1449,7 @@ CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done)
if(result) {
Curl_detach_connection(data);
- Curl_conncache_remove_conn(data, conn, TRUE);
- Curl_disconnect(data, conn, TRUE);
+ Curl_cpool_disconnect(data, conn, TRUE);
}
return result;
}
diff --git a/libs/libcurl/src/hostip.h b/libs/libcurl/src/hostip.h
index 8b46b9a76c..af0bde47f8 100644
--- a/libs/libcurl/src/hostip.h
+++ b/libs/libcurl/src/hostip.h
@@ -99,11 +99,11 @@ struct Curl_dns_entry {
#endif
/* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (does not time out) */
time_t timestamp;
- /* use-counter, use Curl_resolv_unlock to release reference */
- long inuse;
+ /* reference counter, entry is freed on reaching 0 */
+ size_t refcount;
/* hostname port number that resolved to addr. */
int hostport;
- /* hostname that resolved to addr. may be NULL (unix domain sockets). */
+ /* hostname that resolved to addr. may be NULL (Unix domain sockets). */
char hostname[1];
};
@@ -113,7 +113,7 @@ 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
+ * The returned data *MUST* be "released" with Curl_resolv_unlink() after
* use, or we will leak memory!
*/
/* return codes */
@@ -161,9 +161,9 @@ struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
int *waitp);
-/* unlock a previously resolved dns entry */
-void Curl_resolv_unlock(struct Curl_easy *data,
- struct Curl_dns_entry *dns);
+/* unlink a dns entry, potentially shared with a cache */
+void Curl_resolv_unlink(struct Curl_easy *data,
+ struct Curl_dns_entry **pdns);
/* init a new dns cache */
void Curl_init_dnscache(struct Curl_hash *hash, size_t hashsize);
@@ -199,7 +199,7 @@ void Curl_printable_address(const struct Curl_addrinfo *ip,
*
* 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
+ * The returned data *MUST* be "released" with Curl_resolv_unlink() after
* use, or we will leak memory!
*/
struct Curl_dns_entry *
@@ -209,12 +209,13 @@ Curl_fetch_addr(struct Curl_easy *data,
/*
* Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
- *
+ * @param permanent iff TRUE, entry will never become stale
* 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, size_t hostlen, int port);
+ const char *hostname, size_t hostlen, int port,
+ bool permanent);
#ifndef INADDR_NONE
#define CURL_INADDR_NONE (in_addr_t) ~0
diff --git a/libs/libcurl/src/hostip4.c b/libs/libcurl/src/hostip4.c
index f13c5e7405..b658ff56ab 100644
--- a/libs/libcurl/src/hostip4.c
+++ b/libs/libcurl/src/hostip4.c
@@ -82,7 +82,7 @@ bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn)
* 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
+ * has the corresponding rules. This is primarily on *nix. Note that some Unix
* flavours have thread-safe versions of the plain gethostbyname() etc.
*
*/
@@ -221,7 +221,7 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname,
if(!h) /* failure */
#elif defined(HAVE_GETHOSTBYNAME_R_3)
- /* AIX, Digital Unix/Tru64, HPUX 10, more? */
+ /* AIX, Digital UNIX/Tru64, HP-UX 10, more? */
/* For AIX 4.3 or later, we do not use gethostbyname_r() at all, because of
* the plain fact that it does not return unique full buffers on each
diff --git a/libs/libcurl/src/hostip6.c b/libs/libcurl/src/hostip6.c
index 5e8c3abbda..8de74abf97 100644
--- a/libs/libcurl/src/hostip6.c
+++ b/libs/libcurl/src/hostip6.c
@@ -124,7 +124,7 @@ struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
#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.
+ * an IPv4 address on iOS and macOS.
*/
if((1 == Curl_inet_pton(AF_INET, hostname, addrbuf)) ||
(1 == Curl_inet_pton(AF_INET6, hostname, addrbuf))) {
diff --git a/libs/libcurl/src/hsts.c b/libs/libcurl/src/hsts.c
index 635bd6100d..caec4282dc 100644
--- a/libs/libcurl/src/hsts.c
+++ b/libs/libcurl/src/hsts.c
@@ -94,11 +94,11 @@ 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;
+ struct Curl_llist_node *e;
+ struct Curl_llist_node *n;
+ for(e = Curl_llist_head(&h->list); e; e = n) {
+ struct stsentry *sts = Curl_node_elem(e);
+ n = Curl_node_next(e);
hsts_free(sts);
}
free(h->filename);
@@ -215,7 +215,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
/* 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);
+ Curl_node_remove(&sts->node);
hsts_free(sts);
}
return CURLE_OK;
@@ -253,8 +253,8 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname,
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;
+ struct Curl_llist_node *e;
+ struct Curl_llist_node *n;
if((hlen > MAX_HSTS_HOSTLEN) || !hlen)
return NULL;
@@ -265,12 +265,12 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname,
buffer[hlen] = 0;
hostname = buffer;
- for(e = h->list.head; e; e = n) {
- struct stsentry *sts = e->ptr;
- n = e->next;
+ for(e = Curl_llist_head(&h->list); e; e = n) {
+ struct stsentry *sts = Curl_node_elem(e);
+ n = Curl_node_next(e);
if(sts->expires <= now) {
/* remove expired entries */
- Curl_llist_remove(&h->list, &sts->node, NULL);
+ Curl_node_remove(&sts->node);
hsts_free(sts);
continue;
}
@@ -353,8 +353,8 @@ static CURLcode hsts_out(struct stsentry *sts, FILE *fp)
CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
const char *file)
{
- struct Curl_llist_element *e;
- struct Curl_llist_element *n;
+ struct Curl_llist_node *e;
+ struct Curl_llist_node *n;
CURLcode result = CURLE_OK;
FILE *out;
char *tempstore = NULL;
@@ -376,9 +376,9 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
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;
+ for(e = Curl_llist_head(&h->list); e; e = n) {
+ struct stsentry *sts = Curl_node_elem(e);
+ n = Curl_node_next(e);
result = hsts_out(sts, out);
if(result)
break;
@@ -395,12 +395,12 @@ skipsave:
if(data->set.hsts_write) {
/* if there is a write callback */
struct curl_index i; /* count */
- i.total = h->list.size;
+ i.total = Curl_llist_count(&h->list);
i.index = 0;
- for(e = h->list.head; e; e = n) {
- struct stsentry *sts = e->ptr;
+ for(e = Curl_llist_head(&h->list); e; e = n) {
+ struct stsentry *sts = Curl_node_elem(e);
bool stop;
- n = e->next;
+ n = Curl_node_next(e);
result = hsts_push(data, &i, sts, &stop);
if(result || stop)
break;
diff --git a/libs/libcurl/src/hsts.h b/libs/libcurl/src/hsts.h
index 098b93c3a2..de08a1066a 100644
--- a/libs/libcurl/src/hsts.h
+++ b/libs/libcurl/src/hsts.h
@@ -34,7 +34,7 @@ extern time_t deltatime;
#endif
struct stsentry {
- struct Curl_llist_element node;
+ struct Curl_llist_node node;
const char *host;
bool includeSubDomains;
curl_off_t expires; /* the timestamp of this entry's expiry */
diff --git a/libs/libcurl/src/http.c b/libs/libcurl/src/http.c
index 3163bd7e9f..cb585b4571 100644
--- a/libs/libcurl/src/http.c
+++ b/libs/libcurl/src/http.c
@@ -462,16 +462,15 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data,
if(abort_upload) {
if(upload_remain >= 0)
- infof(data, "%s%sclose instead of sending %"
- CURL_FORMAT_CURL_OFF_T " more bytes",
- ongoing_auth? ongoing_auth : "",
- ongoing_auth? " send, " : "",
- upload_remain);
+ infof(data, "%s%sclose instead of sending %" FMT_OFF_T " more bytes",
+ ongoing_auth? ongoing_auth : "",
+ ongoing_auth? " send, " : "",
+ upload_remain);
else
infof(data, "%s%sclose instead of sending unknown amount "
- "of more bytes",
- ongoing_auth? ongoing_auth : "",
- ongoing_auth? " send, " : "");
+ "of more bytes",
+ ongoing_auth? ongoing_auth : "",
+ ongoing_auth? " send, " : "");
/* We decided to abort the ongoing transfer */
streamclose(conn, "Mid-auth HTTP and much data left to send");
/* FIXME: questionable manipulation here, can we do this differently? */
@@ -2049,7 +2048,7 @@ static CURLcode http_resume(struct Curl_easy *data, Curl_HttpReq httpreq)
CURLcode result;
result = Curl_creader_resume_from(data, data->state.resume_from);
if(result) {
- failf(data, "Unable to resume from offset %" CURL_FORMAT_CURL_OFF_T,
+ failf(data, "Unable to resume from offset %" FMT_OFF_T,
data->state.resume_from);
return result;
}
@@ -2186,9 +2185,8 @@ CURLcode Curl_http_req_complete(struct Curl_easy *data,
!Curl_checkheaders(data, STRCONST("Content-Length")))) {
/* we allow replacing this header if not during auth negotiation,
although it is not very wise to actually set your own */
- result = Curl_dyn_addf(r,
- "Content-Length: %" CURL_FORMAT_CURL_OFF_T
- "\r\n", req_clen);
+ result = Curl_dyn_addf(r, "Content-Length: %" FMT_OFF_T "\r\n",
+ req_clen);
}
if(result)
goto out;
@@ -2343,8 +2341,7 @@ CURLcode Curl_http_range(struct Curl_easy *data,
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",
+ aprintf("Content-Range: bytes 0-%" FMT_OFF_T "/%" FMT_OFF_T "\r\n",
req_clen - 1, req_clen);
}
@@ -2357,15 +2354,14 @@ CURLcode Curl_http_range(struct Curl_easy *data,
data->state.infilesize :
(data->state.resume_from + req_clen);
data->state.aptr.rangeline =
- aprintf("Content-Range: bytes %s%" CURL_FORMAT_CURL_OFF_T
- "/%" CURL_FORMAT_CURL_OFF_T "\r\n",
+ aprintf("Content-Range: bytes %s%" FMT_OFF_T "/%" FMT_OFF_T "\r\n",
data->state.range, total_len-1, total_len);
}
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",
+ aprintf("Content-Range: bytes %s/%" FMT_OFF_T "\r\n",
data->state.range, req_clen);
}
if(!data->state.aptr.rangeline)
@@ -3118,7 +3114,7 @@ CURLcode Curl_http_header(struct Curl_easy *data,
#ifdef DEBUGBUILD
else
infof(data, "Parsed STS header fine (%zu entries)",
- data->hsts->list.size);
+ Curl_llist_count(&data->hsts->list));
#endif
}
#endif
@@ -3154,6 +3150,11 @@ CURLcode Curl_http_header(struct Curl_easy *data,
}
return CURLE_OK;
}
+ v = HD_VAL(hd, hdlen, "Trailer:");
+ if(v) {
+ data->req.resp_trailer = TRUE;
+ return CURLE_OK;
+ }
break;
case 'w':
case 'W':
@@ -3241,9 +3242,6 @@ CURLcode Curl_http_statusline(struct Curl_easy *data,
else if(k->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;
}
k->http_bodyless = k->httpcode >= 100 && k->httpcode < 200;
@@ -3393,9 +3391,6 @@ static CURLcode http_on_response(struct Curl_easy *data,
if(conn->httpversion != 20)
infof(data, "Lying server, not serving HTTP/2");
}
- if(conn->httpversion < 20) {
- conn->bundle->multiuse = BUNDLE_NO_MULTIUSE;
- }
if(k->httpcode < 200 && last_hd) {
/* Intermediate responses might trigger processing of more
@@ -3440,6 +3435,7 @@ static CURLcode http_on_response(struct Curl_easy *data,
/* Switching to HTTP/2, where we will get more responses */
infof(data, "Received 101, Switching to HTTP/2");
k->upgr101 = UPGR101_RECEIVED;
+ data->conn->bits.asks_multiplex = FALSE;
/* We expect more response from HTTP/2 later */
k->header = TRUE;
k->headerline = 0; /* restart the header line counter */
@@ -3486,6 +3482,7 @@ static CURLcode http_on_response(struct Curl_easy *data,
if(k->upgr101 == UPGR101_H2) {
/* A requested upgrade was denied, poke the multi handle to possibly
allow a pending pipewait to continue */
+ data->conn->bits.asks_multiplex = FALSE;
Curl_multi_connchanged(data->multi);
}
@@ -3535,7 +3532,7 @@ static CURLcode http_on_response(struct Curl_easy *data,
#endif
#ifdef USE_WEBSOCKETS
- /* All >=200 HTTP status codes are errors when wanting websockets */
+ /* All >=200 HTTP status codes are errors when wanting WebSockets */
if(data->req.upgr101 == UPGR101_WS) {
failf(data, "Refused WebSockets upgrade: %d", k->httpcode);
result = CURLE_HTTP_RETURNED_ERROR;
@@ -4431,9 +4428,18 @@ static CURLcode cr_exp100_read(struct Curl_easy *data,
switch(ctx->state) {
case EXP100_SENDING_REQUEST:
+ if(!Curl_req_sendbuf_empty(data)) {
+ /* The initial request data has not been fully sent yet. Do
+ * not start the timer yet. */
+ DEBUGF(infof(data, "cr_exp100_read, request not full sent yet"));
+ *nread = 0;
+ *eos = FALSE;
+ return CURLE_OK;
+ }
/* We are now waiting for a reply from the server or
- * a timeout on our side */
- DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE"));
+ * a timeout on our side IFF the request has been fully sent. */
+ DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE, "
+ "timeout %ldms", data->set.expect_100_timeout));
ctx->state = EXP100_AWAITING_CONTINUE;
ctx->start = Curl_now();
Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
diff --git a/libs/libcurl/src/http2.c b/libs/libcurl/src/http2.c
index 108678a2b8..7ec8ad6fcb 100644
--- a/libs/libcurl/src/http2.c
+++ b/libs/libcurl/src/http2.c
@@ -69,25 +69,32 @@
/* buffer dimensioning:
* use 16K as chunk size, as that fits H2 DATA frames well */
#define H2_CHUNK_SIZE (16 * 1024)
-/* this is how much we want "in flight" for a stream */
-#define H2_STREAM_WINDOW_SIZE (10 * 1024 * 1024)
+/* connection window size */
+#define H2_CONN_WINDOW_SIZE (10 * 1024 * 1024)
/* on receiving from TLS, we prep for holding a full stream window */
-#define H2_NW_RECV_CHUNKS (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE)
+#define H2_NW_RECV_CHUNKS (H2_CONN_WINDOW_SIZE / H2_CHUNK_SIZE)
/* on send into TLS, we just want to accumulate small frames */
#define H2_NW_SEND_CHUNKS 1
-/* stream recv/send chunks are a result of window / chunk sizes */
-#define H2_STREAM_RECV_CHUNKS (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE)
+/* this is how much we want "in flight" for a stream, unthrottled */
+#define H2_STREAM_WINDOW_SIZE_MAX (10 * 1024 * 1024)
+/* this is how much we want "in flight" for a stream, initially, IFF
+ * nghttp2 allows us to tweak the local window size. */
+#if NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
+#define H2_STREAM_WINDOW_SIZE_INITIAL (64 * 1024)
+#else
+#define H2_STREAM_WINDOW_SIZE_INITIAL H2_STREAM_WINDOW_SIZE_MAX
+#endif
/* keep smaller stream upload buffer (default h2 window size) to have
* our progress bars and "upload done" reporting closer to reality */
#define H2_STREAM_SEND_CHUNKS ((64 * 1024) / H2_CHUNK_SIZE)
/* spare chunks we keep for a full window */
-#define H2_STREAM_POOL_SPARES (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE)
+#define H2_STREAM_POOL_SPARES (H2_CONN_WINDOW_SIZE / H2_CHUNK_SIZE)
/* We need to accommodate the max number of streams with their window sizes on
* the overall connection. Streams might become PAUSED which will block their
* received QUOTA in the connection window. If we run out of space, the server
* is blocked from sending us any data. See #10988 for an issue with this. */
-#define HTTP2_HUGE_WINDOW_SIZE (100 * H2_STREAM_WINDOW_SIZE)
+#define HTTP2_HUGE_WINDOW_SIZE (100 * H2_STREAM_WINDOW_SIZE_MAX)
#define H2_SETTINGS_IV_LEN 3
#define H2_BINSETTINGS_LEN 80
@@ -99,7 +106,7 @@ static size_t populate_settings(nghttp2_settings_entry *iv,
iv[0].value = Curl_multi_max_concurrent_streams(data->multi);
iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
- iv[1].value = H2_STREAM_WINDOW_SIZE;
+ iv[1].value = H2_STREAM_WINDOW_SIZE_INITIAL;
iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
iv[2].value = data->multi->push_cb != NULL;
@@ -129,12 +136,14 @@ struct cf_h2_ctx {
struct bufc_pool stream_bufcp; /* spares for stream buffers */
struct dynbuf scratch; /* scratch buffer for temp use */
- struct Curl_hash streams; /* hash of `data->id` to `h2_stream_ctx` */
+ struct Curl_hash streams; /* hash of `data->mid` to `h2_stream_ctx` */
size_t drain_total; /* sum of all stream's UrlState drain */
uint32_t max_concurrent_streams;
uint32_t goaway_error; /* goaway error code from server */
int32_t remote_max_sid; /* max id processed by server */
int32_t local_max_sid; /* max id processed by us */
+ BIT(initialized);
+ BIT(via_h1_upgrade);
BIT(conn_closed);
BIT(rcvd_goaway);
BIT(sent_goaway);
@@ -147,28 +156,38 @@ struct cf_h2_ctx {
#define CF_CTX_CALL_DATA(cf) \
((struct cf_h2_ctx *)(cf)->ctx)->call_data
-static void cf_h2_ctx_clear(struct cf_h2_ctx *ctx)
+static void h2_stream_hash_free(void *stream);
+
+static void cf_h2_ctx_init(struct cf_h2_ctx *ctx, bool via_h1_upgrade)
{
- struct cf_call_data save = ctx->call_data;
+ Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES);
+ Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0);
+ Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0);
+ Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
+ Curl_hash_offt_init(&ctx->streams, 63, h2_stream_hash_free);
+ ctx->remote_max_sid = 2147483647;
+ ctx->via_h1_upgrade = via_h1_upgrade;
+ ctx->initialized = TRUE;
+}
- if(ctx->h2) {
- nghttp2_session_del(ctx->h2);
+static void cf_h2_ctx_free(struct cf_h2_ctx *ctx)
+{
+ if(ctx && ctx->initialized) {
+ Curl_bufq_free(&ctx->inbufq);
+ Curl_bufq_free(&ctx->outbufq);
+ Curl_bufcp_free(&ctx->stream_bufcp);
+ Curl_dyn_free(&ctx->scratch);
+ Curl_hash_clean(&ctx->streams);
+ Curl_hash_destroy(&ctx->streams);
+ memset(ctx, 0, sizeof(*ctx));
}
- Curl_bufq_free(&ctx->inbufq);
- Curl_bufq_free(&ctx->outbufq);
- Curl_bufcp_free(&ctx->stream_bufcp);
- Curl_dyn_free(&ctx->scratch);
- Curl_hash_clean(&ctx->streams);
- Curl_hash_destroy(&ctx->streams);
- memset(ctx, 0, sizeof(*ctx));
- ctx->call_data = save;
+ free(ctx);
}
-static void cf_h2_ctx_free(struct cf_h2_ctx *ctx)
+static void cf_h2_ctx_close(struct cf_h2_ctx *ctx)
{
- if(ctx) {
- cf_h2_ctx_clear(ctx);
- free(ctx);
+ if(ctx->h2) {
+ nghttp2_session_del(ctx->h2);
}
}
@@ -184,8 +203,6 @@ struct h2_stream_ctx {
struct h1_req_parser h1; /* parsing the request */
struct dynhds resp_trailers; /* response trailer fields */
size_t resp_hds_len; /* amount of response header bytes in recvbuf */
- size_t upload_blocked_len;
- curl_off_t upload_left; /* number of request bytes left to upload */
curl_off_t nrcvd_data; /* number of DATA bytes received */
char **push_headers; /* allocated array */
@@ -195,19 +212,19 @@ struct h2_stream_ctx {
int status_code; /* HTTP response status code */
uint32_t error; /* stream error code */
CURLcode xfer_result; /* Result of writing out response */
- uint32_t local_window_size; /* the local recv window size */
+ int32_t local_window_size; /* the local recv window size */
int32_t id; /* HTTP/2 protocol identifier for stream */
BIT(resp_hds_complete); /* we have a complete, final response */
BIT(closed); /* TRUE on stream close */
BIT(reset); /* TRUE on stream reset */
BIT(close_handled); /* TRUE if stream closure is handled by libcurl */
BIT(bodystarted);
- BIT(send_closed); /* transfer is done sending, we might have still
- buffered data in stream->sendbuf to upload. */
+ BIT(body_eos); /* the complete body has been added to `sendbuf` and
+ * is being/has been processed from there. */
};
#define H2_STREAM_CTX(ctx,data) ((struct h2_stream_ctx *)(\
- data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL))
+ data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx)
{
@@ -229,8 +246,7 @@ static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx)
stream->closed = FALSE;
stream->close_handled = FALSE;
stream->error = NGHTTP2_NO_ERROR;
- stream->local_window_size = H2_STREAM_WINDOW_SIZE;
- stream->upload_left = 0;
+ stream->local_window_size = H2_STREAM_WINDOW_SIZE_INITIAL;
stream->nrcvd_data = 0;
return stream;
}
@@ -259,6 +275,77 @@ static void h2_stream_hash_free(void *stream)
h2_stream_ctx_free((struct h2_stream_ctx *)stream);
}
+#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
+static int32_t cf_h2_get_desired_local_win(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ (void)cf;
+ if(data->set.max_recv_speed && data->set.max_recv_speed < INT32_MAX) {
+ /* The transfer should only receive `max_recv_speed` bytes per second.
+ * We restrict the stream's local window size, so that the server cannot
+ * send us "too much" at a time.
+ * This gets less precise the higher the latency. */
+ return (int32_t)data->set.max_recv_speed;
+ }
+ return H2_STREAM_WINDOW_SIZE_MAX;
+}
+
+static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct h2_stream_ctx *stream,
+ bool paused)
+{
+ struct cf_h2_ctx *ctx = cf->ctx;
+ int32_t dwsize;
+ int rv;
+
+ dwsize = paused? 0 : cf_h2_get_desired_local_win(cf, data);
+ if(dwsize != stream->local_window_size) {
+ int32_t wsize = nghttp2_session_get_stream_effective_local_window_size(
+ ctx->h2, stream->id);
+ if(dwsize > wsize) {
+ rv = nghttp2_submit_window_update(ctx->h2, NGHTTP2_FLAG_NONE,
+ stream->id, dwsize - wsize);
+ if(rv) {
+ failf(data, "[%d] nghttp2_submit_window_update() failed: "
+ "%s(%d)", stream->id, nghttp2_strerror(rv), rv);
+ return CURLE_HTTP2;
+ }
+ stream->local_window_size = dwsize;
+ CURL_TRC_CF(data, cf, "[%d] local window update by %d",
+ stream->id, dwsize - wsize);
+ }
+ else {
+ rv = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE,
+ stream->id, dwsize);
+ if(rv) {
+ failf(data, "[%d] nghttp2_session_set_local_window_size() failed: "
+ "%s(%d)", stream->id, nghttp2_strerror(rv), rv);
+ return CURLE_HTTP2;
+ }
+ stream->local_window_size = dwsize;
+ CURL_TRC_CF(data, cf, "[%d] local window size now %d",
+ stream->id, dwsize);
+ }
+ }
+ return CURLE_OK;
+}
+
+#else /* NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE */
+
+static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct h2_stream_ctx *stream,
+ bool paused)
+{
+ (void)cf;
+ (void)data;
+ (void)stream;
+ (void)paused;
+ return CURLE_OK;
+}
+#endif /* !NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE */
+
/*
* Mark this transfer to get "drained".
*/
@@ -270,10 +357,10 @@ static void drain_stream(struct Curl_cfilter *cf,
(void)cf;
bits = CURL_CSELECT_IN;
- if(!stream->send_closed &&
- (stream->upload_left || stream->upload_blocked_len))
+ if(!stream->closed &&
+ (!stream->body_eos || !Curl_bufq_is_empty(&stream->sendbuf)))
bits |= CURL_CSELECT_OUT;
- if(data->state.select_bits != bits) {
+ if(stream->closed || (data->state.select_bits != bits)) {
CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x",
stream->id, bits);
data->state.select_bits = bits;
@@ -300,7 +387,7 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf,
if(!stream)
return CURLE_OUT_OF_MEMORY;
- if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) {
+ if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
h2_stream_ctx_free(stream);
return CURLE_OUT_OF_MEMORY;
}
@@ -315,7 +402,7 @@ static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
DEBUGASSERT(ctx);
- if(!stream)
+ if(!stream || !ctx->initialized)
return;
if(ctx->h2) {
@@ -329,7 +416,6 @@ static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
stream->id);
stream->closed = TRUE;
stream->reset = TRUE;
- stream->send_closed = TRUE;
nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
stream->id, NGHTTP2_STREAM_CLOSED);
flush_egress = TRUE;
@@ -339,7 +425,7 @@ static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
nghttp2_session_send(ctx->h2);
}
- Curl_hash_offt_remove(&ctx->streams, data->id);
+ Curl_hash_offt_remove(&ctx->streams, data->mid);
}
static int h2_client_new(struct Curl_cfilter *cf,
@@ -382,8 +468,8 @@ static ssize_t nw_out_writer(void *writer_ctx,
struct Curl_easy *data = CF_DATA_CURRENT(cf);
if(data) {
- ssize_t nwritten = Curl_conn_cf_send(cf->next, data,
- (const char *)buf, buflen, err);
+ ssize_t nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf,
+ buflen, FALSE, err);
if(nwritten > 0)
CURL_TRC_CF(data, cf, "[0] egress: wrote %zd bytes", nwritten);
return nwritten;
@@ -415,12 +501,8 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
static int error_callback(nghttp2_session *session, const char *msg,
size_t len, void *userp);
-/*
- * Initialize the cfilter context
- */
-static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool via_h1_upgrade)
+static CURLcode cf_h2_ctx_open(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
struct cf_h2_ctx *ctx = cf->ctx;
struct h2_stream_ctx *stream;
@@ -429,12 +511,7 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
nghttp2_session_callbacks *cbs = NULL;
DEBUGASSERT(!ctx->h2);
- Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES);
- Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0);
- Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0);
- Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
- Curl_hash_offt_init(&ctx->streams, 63, h2_stream_hash_free);
- ctx->remote_max_sid = 2147483647;
+ DEBUGASSERT(ctx->initialized);
rc = nghttp2_session_callbacks_new(&cbs);
if(rc) {
@@ -463,7 +540,7 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
}
ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS;
- if(via_h1_upgrade) {
+ if(ctx->via_h1_upgrade) {
/* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted
* in the H1 request and we upgrade from there. This stream
* is opened implicitly as #1. */
@@ -529,7 +606,7 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
/* all set, traffic will be send on connect */
result = CURLE_OK;
CURL_TRC_CF(data, cf, "[0] created h2 session%s",
- via_h1_upgrade? " (via h1 upgrade)" : "");
+ ctx->via_h1_upgrade? " (via h1 upgrade)" : "");
out:
if(cbs)
@@ -975,6 +1052,8 @@ static void h2_xfer_write_resp_hd(struct Curl_cfilter *cf,
/* If we already encountered an error, skip further writes */
if(!stream->xfer_result) {
stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos);
+ if(!stream->xfer_result && !eos)
+ stream->xfer_result = cf_h2_update_local_win(cf, data, stream, FALSE);
if(stream->xfer_result)
CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of headers",
stream->id, stream->xfer_result, blen);
@@ -990,6 +1069,8 @@ static void h2_xfer_write_resp(struct Curl_cfilter *cf,
/* If we already encountered an error, skip further writes */
if(!stream->xfer_result)
stream->xfer_result = Curl_xfer_write_resp(data, buf, blen, eos);
+ if(!stream->xfer_result && !eos)
+ stream->xfer_result = cf_h2_update_local_win(cf, data, stream, FALSE);
/* If the transfer write is errored, we do not want any more data */
if(stream->xfer_result) {
struct cf_h2_ctx *ctx = cf->ctx;
@@ -1083,13 +1164,19 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
if(frame->rst_stream.error_code) {
stream->reset = TRUE;
}
- stream->send_closed = TRUE;
drain_stream(cf, data, stream);
break;
case NGHTTP2_WINDOW_UPDATE:
- if(CURL_WANT_SEND(data)) {
+ if(CURL_WANT_SEND(data) && Curl_bufq_is_empty(&stream->sendbuf)) {
+ /* need more data, force processing of transfer */
drain_stream(cf, data, stream);
}
+ else if(!Curl_bufq_is_empty(&stream->sendbuf)) {
+ /* resume the potentially suspended stream */
+ rv = nghttp2_session_resume_data(ctx->h2, stream->id);
+ if(nghttp2_is_fatal(rv))
+ return CURLE_SEND_ERROR;
+ }
break;
default:
break;
@@ -1300,9 +1387,6 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
nghttp2_session_consume(ctx->h2, stream_id, len);
stream->nrcvd_data += (curl_off_t)len;
-
- /* if we receive data for another handle, wake that up */
- drain_stream(cf, data_s, stream);
return 0;
}
@@ -1317,8 +1401,7 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
(void)session;
DEBUGASSERT(call_data);
- /* get the stream from the hash based on Stream ID, stream ID zero is for
- connection-oriented stuff */
+ /* stream id 0 is the connection, do not look there for streams. */
data_s = stream_id?
nghttp2_session_get_stream_user_data(session, stream_id) : NULL;
if(!data_s) {
@@ -1346,7 +1429,6 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
stream->error = error_code;
if(stream->error) {
stream->reset = TRUE;
- stream->send_closed = TRUE;
}
if(stream->error)
@@ -1572,22 +1654,21 @@ static ssize_t req_body_read_callback(nghttp2_session *session,
(void)source;
(void)cf;
- 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 = H2_STREAM_CTX(ctx, data_s);
- if(!stream)
- return NGHTTP2_ERR_CALLBACK_FAILURE;
- }
- else
+ if(!stream_id)
return NGHTTP2_ERR_INVALID_ARGUMENT;
+ /* 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 = H2_STREAM_CTX(ctx, data_s);
+ if(!stream)
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+
nread = Curl_bufq_read(&stream->sendbuf, buf, length, &result);
if(nread < 0) {
if(result != CURLE_AGAIN)
@@ -1595,17 +1676,13 @@ static ssize_t req_body_read_callback(nghttp2_session *session,
nread = 0;
}
- if(nread > 0 && stream->upload_left != -1)
- stream->upload_left -= nread;
-
- CURL_TRC_CF(data_s, cf, "[%d] req_body_read(len=%zu) left=%"
- CURL_FORMAT_CURL_OFF_T " -> %zd, %d",
- stream_id, length, stream->upload_left, nread, result);
+ CURL_TRC_CF(data_s, cf, "[%d] req_body_read(len=%zu) eos=%d -> %zd, %d",
+ stream_id, length, stream->body_eos, nread, result);
- if(stream->upload_left == 0)
- *data_flags = NGHTTP2_DATA_FLAG_EOF;
- else if(nread == 0)
+ if(nread == 0)
return NGHTTP2_ERR_DEFERRED;
+ if(stream->body_eos && Curl_bufq_is_empty(&stream->sendbuf))
+ *data_flags = NGHTTP2_DATA_FLAG_EOF;
return nread;
}
@@ -1659,37 +1736,11 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
free(base64);
k->upgr101 = UPGR101_H2;
+ data->conn->bits.asks_multiplex = TRUE;
return result;
}
-static CURLcode http2_data_done_send(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- struct cf_h2_ctx *ctx = cf->ctx;
- CURLcode result = CURLE_OK;
- struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
-
- if(!ctx || !ctx->h2 || !stream)
- goto out;
-
- CURL_TRC_CF(data, cf, "[%d] data done send", stream->id);
- if(!stream->send_closed) {
- stream->send_closed = TRUE;
- if(stream->upload_left) {
- /* we now know that everything that is buffered is all there is. */
- stream->upload_left = Curl_bufq_len(&stream->sendbuf);
- /* resume sending here to trigger the callback to get called again so
- that it can signal EOF to nghttp2 */
- (void)nghttp2_session_resume_data(ctx->h2, stream->id);
- drain_stream(cf, data, stream);
- }
- }
-
-out:
- return result;
-}
-
static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct h2_stream_ctx *stream,
@@ -1934,12 +1985,15 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
if(h2_process_pending_input(cf, data, &result))
return result;
+ CURL_TRC_CF(data, cf, "[0] progress ingress: inbufg=%zu",
+ Curl_bufq_len(&ctx->inbufq));
}
if(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) {
connclose(cf->conn, "GOAWAY received");
}
+ CURL_TRC_CF(data, cf, "[0] progress ingress: done");
return CURLE_OK;
}
@@ -1957,9 +2011,8 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
* (unlikely) or the transfer has been done, cleaned up its resources, but
* a read() is called anyway. It is not clear what the calling sequence
* is for such a case. */
- failf(data, "[%zd-%zd], http/2 recv on a transfer never opened "
- "or already cleared", (ssize_t)data->id,
- (ssize_t)cf->conn->connection_id);
+ failf(data, "http/2 recv on a transfer never opened "
+ "or already cleared, mid=%" FMT_OFF_T, data->mid);
*err = CURLE_HTTP2;
return -1;
}
@@ -2005,11 +2058,11 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
out:
result = h2_progress_egress(cf, data);
if(result == CURLE_AGAIN) {
- /* pending data to send, need to be called again. Ideally, we would
- * monitor the socket for POLLOUT, but we might not be in SENDING
- * transfer state any longer and are unable to make this happen.
- */
- drain_stream(cf, data, stream);
+ /* pending data to send, need to be called again. Ideally, we
+ * monitor the socket for POLLOUT, but when not SENDING
+ * any more, we force processing of the transfer. */
+ if(!CURL_WANT_SEND(data))
+ drain_stream(cf, data, stream);
}
else if(result) {
*err = result;
@@ -2029,10 +2082,57 @@ out:
return nread;
}
+static ssize_t cf_h2_body_send(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct h2_stream_ctx *stream,
+ const void *buf, size_t blen, bool eos,
+ CURLcode *err)
+{
+ struct cf_h2_ctx *ctx = cf->ctx;
+ ssize_t nwritten;
+
+ if(stream->closed) {
+ if(stream->resp_hds_complete) {
+ /* Server decided to close the stream after having sent us a final
+ * response. This is valid if it is not interested in the request
+ * body. This happens on 30x or 40x responses.
+ * We silently discard the data sent, since this is not a transport
+ * error situation. */
+ CURL_TRC_CF(data, cf, "[%d] discarding data"
+ "on closed stream with response", stream->id);
+ if(eos)
+ stream->body_eos = TRUE;
+ *err = CURLE_OK;
+ return (ssize_t)blen;
+ }
+ /* Server closed before we got a response, this is an error */
+ infof(data, "stream %u closed", stream->id);
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+
+ nwritten = Curl_bufq_write(&stream->sendbuf, buf, blen, err);
+ if(nwritten < 0)
+ return -1;
+
+ if(eos && (blen == (size_t)nwritten))
+ stream->body_eos = TRUE;
+
+ if(eos || !Curl_bufq_is_empty(&stream->sendbuf)) {
+ /* resume the potentially suspended stream */
+ int rv = nghttp2_session_resume_data(ctx->h2, stream->id);
+ if(nghttp2_is_fatal(rv)) {
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+ }
+ return nwritten;
+}
+
static ssize_t h2_submit(struct h2_stream_ctx **pstream,
struct Curl_cfilter *cf, struct Curl_easy *data,
const void *buf, size_t len,
- size_t *phdslen, CURLcode *err)
+ bool eos, CURLcode *err)
{
struct cf_h2_ctx *ctx = cf->ctx;
struct h2_stream_ctx *stream = NULL;
@@ -2045,7 +2145,6 @@ static ssize_t h2_submit(struct h2_stream_ctx **pstream,
nghttp2_priority_spec pri_spec;
ssize_t nwritten;
- *phdslen = 0;
Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
*err = http2_data_setup(cf, data, &stream);
@@ -2057,7 +2156,6 @@ static ssize_t h2_submit(struct h2_stream_ctx **pstream,
nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
if(nwritten < 0)
goto out;
- *phdslen = (size_t)nwritten;
if(!stream->h1.done) {
/* need more data */
goto out;
@@ -2088,19 +2186,12 @@ static ssize_t h2_submit(struct h2_stream_ctx **pstream,
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 */
-
data_prd.read_callback = req_body_read_callback;
data_prd.source.ptr = NULL;
stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader,
&data_prd, data);
break;
default:
- stream->upload_left = 0; /* no request body */
stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader,
NULL, data);
}
@@ -2135,32 +2226,21 @@ static ssize_t h2_submit(struct h2_stream_ctx **pstream,
}
stream->id = stream_id;
- stream->local_window_size = H2_STREAM_WINDOW_SIZE;
- if(data->set.max_recv_speed) {
- /* We are asked to only receive `max_recv_speed` bytes per second.
- * Let's limit our stream window size around that, otherwise the server
- * will send in large bursts only. We make the window 50% larger to
- * allow for data in flight and avoid stalling. */
- curl_off_t n = (((data->set.max_recv_speed - 1) / H2_CHUNK_SIZE) + 1);
- n += CURLMAX((n/2), 1);
- if(n < (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) &&
- n < (UINT_MAX / H2_CHUNK_SIZE)) {
- stream->local_window_size = (uint32_t)n * H2_CHUNK_SIZE;
- }
- }
body = (const char *)buf + nwritten;
bodylen = len - nwritten;
- if(bodylen) {
- /* We have request body to send in DATA frame */
- ssize_t n = Curl_bufq_write(&stream->sendbuf, body, bodylen, err);
- if(n < 0) {
+ if(bodylen || eos) {
+ ssize_t n = cf_h2_body_send(cf, data, stream, body, bodylen, eos, err);
+ if(n >= 0)
+ nwritten += n;
+ else if(*err == CURLE_AGAIN)
+ *err = CURLE_OK;
+ else if(*err != CURLE_AGAIN) {
*err = CURLE_SEND_ERROR;
nwritten = -1;
goto out;
}
- nwritten += n;
}
out:
@@ -2173,139 +2253,63 @@ out:
}
static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
- const void *buf, size_t len, CURLcode *err)
+ const void *buf, size_t len, bool eos,
+ CURLcode *err)
{
struct cf_h2_ctx *ctx = cf->ctx;
struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
struct cf_call_data save;
- int rv;
ssize_t nwritten;
- size_t hdslen = 0;
CURLcode result;
- int blocked = 0, was_blocked = 0;
CF_DATA_SAVE(save, cf, data);
- if(stream && stream->id != -1) {
- if(stream->upload_blocked_len) {
- /* the data in `buf` has already been submitted or added to the
- * buffers, but have been EAGAINed on the last invocation. */
- /* TODO: this assertion triggers in OSSFuzz runs and it is not
- * clear why. Disable for now to let OSSFuzz continue its tests. */
- DEBUGASSERT(len >= stream->upload_blocked_len);
- if(len < stream->upload_blocked_len) {
- /* Did we get called again with a smaller `len`? This should not
- * happen. We are not prepared to handle that. */
- failf(data, "HTTP/2 send again with decreased length (%zd vs %zd)",
- len, stream->upload_blocked_len);
- *err = CURLE_HTTP2;
- nwritten = -1;
- goto out;
- }
- nwritten = (ssize_t)stream->upload_blocked_len;
- stream->upload_blocked_len = 0;
- was_blocked = 1;
- }
- else if(stream->closed) {
- if(stream->resp_hds_complete) {
- /* Server decided to close the stream after having sent us a findl
- * response. This is valid if it is not interested in the request
- * body. This happens on 30x or 40x responses.
- * We silently discard the data sent, since this is not a transport
- * error situation. */
- CURL_TRC_CF(data, cf, "[%d] discarding data"
- "on closed stream with response", stream->id);
- *err = CURLE_OK;
- nwritten = (ssize_t)len;
- goto out;
- }
- infof(data, "stream %u closed", stream->id);
- *err = CURLE_SEND_ERROR;
- nwritten = -1;
+ if(!stream || stream->id == -1) {
+ nwritten = h2_submit(&stream, cf, data, buf, len, eos, err);
+ if(nwritten < 0) {
goto out;
}
- else {
- /* If stream_id != -1, we have dispatched request HEADERS and
- * optionally request body, and now are going to send or sending
- * more request body in DATA frame */
- nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err);
- if(nwritten < 0 && *err != CURLE_AGAIN)
- goto out;
- }
-
- if(!Curl_bufq_is_empty(&stream->sendbuf)) {
- /* req body data is buffered, resume the potentially suspended stream */
- rv = nghttp2_session_resume_data(ctx->h2, stream->id);
- if(nghttp2_is_fatal(rv)) {
- *err = CURLE_SEND_ERROR;
- nwritten = -1;
- goto out;
- }
- }
+ DEBUGASSERT(stream);
}
- else {
- nwritten = h2_submit(&stream, cf, data, buf, len, &hdslen, err);
+ else if(stream->body_eos) {
+ /* We already wrote this, but CURLE_AGAINed the call due to not
+ * being able to flush stream->sendbuf. Make a 0-length write
+ * to trigger flushing again.
+ * If this works, we report to have written `len` bytes. */
+ DEBUGASSERT(eos);
+ nwritten = cf_h2_body_send(cf, data, stream, buf, 0, eos, err);
+ CURL_TRC_CF(data, cf, "[%d] cf_body_send last CHUNK -> %zd, %d, eos=%d",
+ stream->id, nwritten, *err, eos);
if(nwritten < 0) {
goto out;
}
- DEBUGASSERT(stream);
- DEBUGASSERT(hdslen <= (size_t)nwritten);
+ nwritten = len;
+ }
+ else {
+ nwritten = cf_h2_body_send(cf, data, stream, buf, len, eos, err);
+ CURL_TRC_CF(data, cf, "[%d] cf_body_send(len=%zu) -> %zd, %d, eos=%d",
+ stream->id, len, nwritten, *err, eos);
}
/* Call the nghttp2 send loop and flush to write ALL buffered data,
* headers and/or request body completely out to the network */
result = h2_progress_egress(cf, data);
+
/* if the stream has been closed in egress handling (nghttp2 does that
* when it does not like the headers, for example */
- if(stream && stream->closed && !was_blocked) {
+ if(stream && stream->closed) {
infof(data, "stream %u closed", stream->id);
*err = CURLE_SEND_ERROR;
nwritten = -1;
goto out;
}
- else if(result == CURLE_AGAIN) {
- blocked = 1;
- }
- else if(result) {
+ else if(result && (result != CURLE_AGAIN)) {
*err = result;
nwritten = -1;
goto out;
}
- else if(stream && !Curl_bufq_is_empty(&stream->sendbuf)) {
- /* although we wrote everything that nghttp2 wants to send now,
- * there is data left in our stream send buffer unwritten. This may
- * be due to the stream's HTTP/2 flow window being exhausted. */
- blocked = 1;
- }
-
- if(stream && blocked && nwritten > 0) {
- /* Unable to send all data, due to connection blocked or H2 window
- * exhaustion. Data is left in our stream buffer, or nghttp2's internal
- * frame buffer or our network out buffer. */
- size_t rwin = (size_t)nghttp2_session_get_stream_remote_window_size(
- ctx->h2, stream->id);
- /* At the start of a stream, we are called with request headers
- * and, possibly, parts of the body. Later, only body data.
- * If we cannot send pure body data, we EAGAIN. If there had been
- * header, we return that *they* have been written and remember the
- * block on the data length only. */
- stream->upload_blocked_len = ((size_t)nwritten) - hdslen;
- CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) BLOCK: win %u/%zu "
- "hds_len=%zu blocked_len=%zu",
- stream->id, len,
- nghttp2_session_get_remote_window_size(ctx->h2), rwin,
- hdslen, stream->upload_blocked_len);
- if(hdslen) {
- *err = CURLE_OK;
- nwritten = hdslen;
- }
- else {
- *err = CURLE_AGAIN;
- nwritten = -1;
- goto out;
- }
- }
- else if(should_close_session(ctx)) {
+
+ if(should_close_session(ctx)) {
/* nghttp2 thinks this session is done. If the stream has not been
* closed, this is an error state for out transfer */
if(stream->closed) {
@@ -2321,11 +2325,10 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
out:
if(stream) {
CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %zd, %d, "
- "upload_left=%" CURL_FORMAT_CURL_OFF_T ", "
- "h2 windows %d-%d (stream-conn), "
+ "eos=%d, h2 windows %d-%d (stream-conn), "
"buffers %zu-%zu (stream-conn)",
stream->id, len, nwritten, *err,
- stream->upload_left,
+ stream->body_eos,
nghttp2_session_get_stream_remote_window_size(
ctx->h2, stream->id),
nghttp2_session_get_remote_window_size(ctx->h2),
@@ -2343,6 +2346,48 @@ out:
return nwritten;
}
+static CURLcode cf_h2_flush(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_h2_ctx *ctx = cf->ctx;
+ struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
+ struct cf_call_data save;
+ CURLcode result = CURLE_OK;
+
+ CF_DATA_SAVE(save, cf, data);
+ if(stream && !Curl_bufq_is_empty(&stream->sendbuf)) {
+ /* resume the potentially suspended stream */
+ int rv = nghttp2_session_resume_data(ctx->h2, stream->id);
+ if(nghttp2_is_fatal(rv)) {
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
+ }
+
+ result = h2_progress_egress(cf, data);
+
+out:
+ if(stream) {
+ CURL_TRC_CF(data, cf, "[%d] flush -> %d, "
+ "h2 windows %d-%d (stream-conn), "
+ "buffers %zu-%zu (stream-conn)",
+ stream->id, result,
+ nghttp2_session_get_stream_remote_window_size(
+ ctx->h2, stream->id),
+ nghttp2_session_get_remote_window_size(ctx->h2),
+ Curl_bufq_len(&stream->sendbuf),
+ Curl_bufq_len(&ctx->outbufq));
+ }
+ else {
+ CURL_TRC_CF(data, cf, "flush -> %d, "
+ "connection-window=%d, nw_send_buffer(%zu)",
+ result, nghttp2_session_get_remote_window_size(ctx->h2),
+ Curl_bufq_len(&ctx->outbufq));
+ }
+ CF_DATA_RESTORE(cf, save);
+ return result;
+}
+
static void cf_h2_adjust_pollset(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct easy_pollset *ps)
@@ -2368,7 +2413,8 @@ static void cf_h2_adjust_pollset(struct Curl_cfilter *cf,
stream->id);
want_recv = (want_recv || c_exhaust || s_exhaust);
want_send = (!s_exhaust && want_send) ||
- (!c_exhaust && nghttp2_session_want_write(ctx->h2));
+ (!c_exhaust && nghttp2_session_want_write(ctx->h2)) ||
+ !Curl_bufq_is_empty(&ctx->outbufq);
Curl_pollset_set(data, ps, sock, want_recv, want_send);
CF_DATA_RESTORE(cf, save);
@@ -2376,7 +2422,8 @@ static void cf_h2_adjust_pollset(struct Curl_cfilter *cf,
else if(ctx->sent_goaway && !cf->shutdown) {
/* shutdown in progress */
CF_DATA_SAVE(save, cf, data);
- want_send = nghttp2_session_want_write(ctx->h2);
+ want_send = nghttp2_session_want_write(ctx->h2) ||
+ !Curl_bufq_is_empty(&ctx->outbufq);
want_recv = nghttp2_session_want_read(ctx->h2);
Curl_pollset_set(data, ps, sock, want_recv, want_send);
CF_DATA_RESTORE(cf, save);
@@ -2406,8 +2453,9 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
*done = FALSE;
CF_DATA_SAVE(save, cf, data);
+ DEBUGASSERT(ctx->initialized);
if(!ctx->h2) {
- result = cf_h2_ctx_init(cf, data, FALSE);
+ result = cf_h2_ctx_open(cf, data);
if(result)
goto out;
}
@@ -2442,7 +2490,7 @@ static void cf_h2_close(struct Curl_cfilter *cf, struct Curl_easy *data)
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
- cf_h2_ctx_clear(ctx);
+ cf_h2_ctx_close(ctx);
CF_DATA_RESTORE(cf, save);
cf->connected = FALSE;
}
@@ -2479,7 +2527,8 @@ static CURLcode cf_h2_shutdown(struct Curl_cfilter *cf,
if(!ctx->sent_goaway) {
rv = nghttp2_submit_goaway(ctx->h2, NGHTTP2_FLAG_NONE,
ctx->local_max_sid, 0,
- (const uint8_t *)"shutown", sizeof("shutown"));
+ (const uint8_t *)"shutdown",
+ sizeof("shutdown"));
if(rv) {
failf(data, "nghttp2_submit_goaway() failed: %s(%d)",
nghttp2_strerror(rv), rv);
@@ -2490,14 +2539,19 @@ static CURLcode cf_h2_shutdown(struct Curl_cfilter *cf,
}
/* GOAWAY submitted, process egress and ingress until nghttp2 is done. */
result = CURLE_OK;
- if(nghttp2_session_want_write(ctx->h2))
+ if(nghttp2_session_want_write(ctx->h2) ||
+ !Curl_bufq_is_empty(&ctx->outbufq))
result = h2_progress_egress(cf, data);
if(!result && nghttp2_session_want_read(ctx->h2))
result = h2_progress_ingress(cf, data, 0);
+ if(result == CURLE_AGAIN)
+ result = CURLE_OK;
+
*done = (ctx->conn_closed ||
(!result && !nghttp2_session_want_write(ctx->h2) &&
- !nghttp2_session_want_read(ctx->h2)));
+ !nghttp2_session_want_read(ctx->h2) &&
+ Curl_bufq_is_empty(&ctx->outbufq)));
out:
CF_DATA_RESTORE(cf, save);
@@ -2509,26 +2563,14 @@ static CURLcode http2_data_pause(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool pause)
{
-#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
struct cf_h2_ctx *ctx = cf->ctx;
struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
DEBUGASSERT(data);
if(ctx && ctx->h2 && stream) {
- uint32_t window = pause? 0 : stream->local_window_size;
-
- int rv = (int)nghttp2_session_set_local_window_size(ctx->h2,
- NGHTTP2_FLAG_NONE,
- stream->id,
- (int32_t)window);
- if(rv) {
- failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
- nghttp2_strerror(rv), rv);
- return CURLE_HTTP2;
- }
-
- if(!pause)
- drain_stream(cf, data, stream);
+ CURLcode result = cf_h2_update_local_win(cf, data, stream, pause);
+ if(result)
+ return result;
/* attempt to send the window update */
(void)h2_progress_egress(cf, data);
@@ -2542,21 +2584,9 @@ static CURLcode http2_data_pause(struct Curl_cfilter *cf,
drain_stream(cf, data, stream);
Curl_expire(data, 0, EXPIRE_RUN_NOW);
}
- DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u",
- window, stream->id));
-
-#ifdef DEBUGBUILD
- {
- /* read out the stream local window again */
- uint32_t window2 = (uint32_t)
- nghttp2_session_get_stream_local_window_size(ctx->h2,
- stream->id);
- DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u",
- window2, stream->id));
- }
-#endif
+ CURL_TRC_CF(data, cf, "[%d] stream now %spaused", stream->id,
+ pause? "" : "un");
}
-#endif
return CURLE_OK;
}
@@ -2576,8 +2606,8 @@ static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf,
case CF_CTRL_DATA_PAUSE:
result = http2_data_pause(cf, data, (arg1 != 0));
break;
- case CF_CTRL_DATA_DONE_SEND:
- result = http2_data_done_send(cf, data);
+ case CF_CTRL_FLUSH:
+ result = cf_h2_flush(cf, data);
break;
case CF_CTRL_DATA_DETACH:
http2_data_done(cf, data);
@@ -2660,6 +2690,15 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf,
*pres1 = stream? (int)stream->error : 0;
return CURLE_OK;
}
+ case CF_QUERY_NEED_FLUSH: {
+ struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
+ if(!Curl_bufq_is_empty(&ctx->outbufq) ||
+ (stream && !Curl_bufq_is_empty(&stream->sendbuf))) {
+ *pres1 = TRUE;
+ return CURLE_OK;
+ }
+ break;
+ }
default:
break;
}
@@ -2701,6 +2740,7 @@ static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf,
ctx = calloc(1, sizeof(*ctx));
if(!ctx)
goto out;
+ cf_h2_ctx_init(ctx, via_h1_upgrade);
result = Curl_cf_create(&cf, &Curl_cft_nghttp2, ctx);
if(result)
@@ -2708,7 +2748,6 @@ static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf,
ctx = NULL;
Curl_conn_cf_add(data, conn, sockindex, cf);
- result = cf_h2_ctx_init(cf, data, via_h1_upgrade);
out:
if(result)
@@ -2729,6 +2768,7 @@ static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf,
ctx = calloc(1, sizeof(*ctx));
if(!ctx)
goto out;
+ cf_h2_ctx_init(ctx, via_h1_upgrade);
result = Curl_cf_create(&cf_h2, &Curl_cft_nghttp2, ctx);
if(result)
@@ -2736,7 +2776,6 @@ static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf,
ctx = NULL;
Curl_conn_cf_insert_after(cf, cf_h2);
- result = cf_h2_ctx_init(cf_h2, data, via_h1_upgrade);
out:
if(result)
@@ -2791,15 +2830,14 @@ CURLcode Curl_http2_switch(struct Curl_easy *data,
CURLcode result;
DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
- DEBUGF(infof(data, "switching to HTTP/2"));
result = http2_cfilter_add(&cf, data, conn, sockindex, FALSE);
if(result)
return result;
+ CURL_TRC_CF(data, cf, "switching connection to HTTP/2");
conn->httpversion = 20; /* we know we are on HTTP/2 now */
conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
- conn->bundle->multiuse = BUNDLE_MULTIPLEX;
Curl_multi_connchanged(data->multi);
if(cf->next) {
@@ -2823,7 +2861,6 @@ CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data)
cf_h2 = cf->next;
cf->conn->httpversion = 20; /* we know we are on HTTP/2 now */
cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
- cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
Curl_multi_connchanged(data->multi);
if(cf_h2->next) {
@@ -2842,12 +2879,12 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data,
CURLcode result;
DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
- DEBUGF(infof(data, "upgrading to HTTP/2"));
DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED);
result = http2_cfilter_add(&cf, data, conn, sockindex, TRUE);
if(result)
return result;
+ CURL_TRC_CF(data, cf, "upgrading connection to HTTP/2");
DEBUGASSERT(cf->cft == &Curl_cft_nghttp2);
ctx = cf->ctx;
@@ -2876,7 +2913,6 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data,
conn->httpversion = 20; /* we know we are on HTTP/2 now */
conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
- conn->bundle->multiuse = BUNDLE_MULTIPLEX;
Curl_multi_connchanged(data->multi);
if(cf->next) {
diff --git a/libs/libcurl/src/http_aws_sigv4.c b/libs/libcurl/src/http_aws_sigv4.c
index 3381474cb2..bbb21b198b 100644
--- a/libs/libcurl/src/http_aws_sigv4.c
+++ b/libs/libcurl/src/http_aws_sigv4.c
@@ -60,11 +60,11 @@
#define TIMESTAMP_SIZE 17
/* hex-encoded with trailing null */
-#define SHA256_HEX_LENGTH (2 * SHA256_DIGEST_LENGTH + 1)
+#define SHA256_HEX_LENGTH (2 * CURL_SHA256_DIGEST_LENGTH + 1)
static void sha256_to_hex(char *dst, unsigned char *sha)
{
- Curl_hexencode(sha, SHA256_DIGEST_LENGTH,
+ Curl_hexencode(sha, CURL_SHA256_DIGEST_LENGTH,
(unsigned char *)dst, SHA256_HEX_LENGTH);
}
@@ -129,6 +129,37 @@ static void trim_headers(struct curl_slist *head)
/* string been x-PROVIDER-date:TIMESTAMP, I need +1 for ':' */
#define DATE_FULL_HDR_LEN (DATE_HDR_KEY_LEN + TIMESTAMP_SIZE + 1)
+/* alphabetically compare two headers by their name, expecting
+ headers to use ':' at this point */
+static int compare_header_names(const char *a, const char *b)
+{
+ const char *colon_a;
+ const char *colon_b;
+ size_t len_a;
+ size_t len_b;
+ size_t min_len;
+ int cmp;
+
+ colon_a = strchr(a, ':');
+ colon_b = strchr(b, ':');
+
+ DEBUGASSERT(colon_a);
+ DEBUGASSERT(colon_b);
+
+ len_a = colon_a ? (size_t)(colon_a - a) : strlen(a);
+ len_b = colon_b ? (size_t)(colon_b - b) : strlen(b);
+
+ min_len = (len_a < len_b) ? len_a : len_b;
+
+ cmp = strncmp(a, b, min_len);
+
+ /* return the shorter of the two if one is shorter */
+ if(!cmp)
+ return (int)(len_a - len_b);
+
+ return cmp;
+}
+
/* timestamp should point to a buffer of at last TIMESTAMP_SIZE bytes */
static CURLcode make_headers(struct Curl_easy *data,
const char *hostname,
@@ -240,7 +271,7 @@ static CURLcode make_headers(struct Curl_easy *data,
if(!tmp_head)
goto fail;
head = tmp_head;
- *date_header = curl_maprintf("%s: %s\r\n", date_hdr_key, timestamp);
+ *date_header = aprintf("%s: %s\r\n", date_hdr_key, timestamp);
}
else {
char *value;
@@ -267,13 +298,13 @@ static CURLcode make_headers(struct Curl_easy *data,
*date_header = NULL;
}
- /* alpha-sort in a case sensitive manner */
+ /* alpha-sort by header name 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) {
+ if(next && compare_header_names(l->data, next->data) > 0) {
char *tmp = l->data;
l->data = next->data;
@@ -573,7 +604,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
const char *method = NULL;
char *payload_hash = NULL;
size_t payload_hash_len = 0;
- unsigned char sha_hash[SHA256_DIGEST_LENGTH];
+ unsigned char sha_hash[CURL_SHA256_DIGEST_LENGTH];
char sha_hex[SHA256_HEX_LENGTH];
char content_sha256_hdr[CONTENT_SHA256_HDR_LEN + 2] = ""; /* add \r\n */
char *canonical_request = NULL;
@@ -582,8 +613,8 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
char *str_to_sign = NULL;
const char *user = data->state.aptr.user ? data->state.aptr.user : "";
char *secret = NULL;
- unsigned char sign0[SHA256_DIGEST_LENGTH] = {0};
- unsigned char sign1[SHA256_DIGEST_LENGTH] = {0};
+ unsigned char sign0[CURL_SHA256_DIGEST_LENGTH] = {0};
+ unsigned char sign1[CURL_SHA256_DIGEST_LENGTH] = {0};
char *auth_headers = NULL;
DEBUGASSERT(!proxy);
@@ -735,19 +766,19 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
result = CURLE_OUT_OF_MEMORY;
canonical_request =
- curl_maprintf("%s\n" /* HTTPRequestMethod */
- "%s\n" /* CanonicalURI */
- "%s\n" /* CanonicalQueryString */
- "%s\n" /* CanonicalHeaders */
- "%s\n" /* SignedHeaders */
- "%.*s", /* HashedRequestPayload in hex */
- method,
- Curl_dyn_ptr(&canonical_path),
- Curl_dyn_ptr(&canonical_query) ?
- Curl_dyn_ptr(&canonical_query) : "",
- Curl_dyn_ptr(&canonical_headers),
- Curl_dyn_ptr(&signed_headers),
- (int)payload_hash_len, payload_hash);
+ aprintf("%s\n" /* HTTPRequestMethod */
+ "%s\n" /* CanonicalURI */
+ "%s\n" /* CanonicalQueryString */
+ "%s\n" /* CanonicalHeaders */
+ "%s\n" /* SignedHeaders */
+ "%.*s", /* HashedRequestPayload in hex */
+ method,
+ Curl_dyn_ptr(&canonical_path),
+ Curl_dyn_ptr(&canonical_query) ?
+ Curl_dyn_ptr(&canonical_query) : "",
+ Curl_dyn_ptr(&canonical_headers),
+ Curl_dyn_ptr(&signed_headers),
+ (int)payload_hash_len, payload_hash);
if(!canonical_request)
goto fail;
@@ -755,12 +786,12 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
/* provider 0 lowercase */
Curl_strntolower(provider0, provider0, strlen(provider0));
- request_type = curl_maprintf("%s4_request", provider0);
+ request_type = aprintf("%s4_request", provider0);
if(!request_type)
goto fail;
- credential_scope = curl_maprintf("%s/%s/%s/%s",
- date, region, service, request_type);
+ credential_scope = aprintf("%s/%s/%s/%s",
+ date, region, service, request_type);
if(!credential_scope)
goto fail;
@@ -777,22 +808,22 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
* 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);
+ str_to_sign = aprintf("%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 : "");
+ secret = aprintf("%s4%s", provider0,
+ data->state.aptr.passwd ?
+ data->state.aptr.passwd : "");
if(!secret)
goto fail;
@@ -805,24 +836,24 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
sha256_to_hex(sha_hex, sign0);
/* provider 0 uppercase */
- auth_headers = curl_maprintf("Authorization: %s4-HMAC-SHA256 "
- "Credential=%s/%s, "
- "SignedHeaders=%s, "
- "Signature=%s\r\n"
- /*
- * date_header is added here, only if it was not
- * user-specified (using CURLOPT_HTTPHEADER).
- * date_header includes \r\n
- */
- "%s"
- "%s", /* optional sha256 header includes \r\n */
- provider0,
- user,
- credential_scope,
- Curl_dyn_ptr(&signed_headers),
- sha_hex,
- date_header ? date_header : "",
- content_sha256_hdr);
+ auth_headers = aprintf("Authorization: %s4-HMAC-SHA256 "
+ "Credential=%s/%s, "
+ "SignedHeaders=%s, "
+ "Signature=%s\r\n"
+ /*
+ * date_header is added here, only if it was not
+ * user-specified (using CURLOPT_HTTPHEADER).
+ * date_header includes \r\n
+ */
+ "%s"
+ "%s", /* optional sha256 header includes \r\n */
+ provider0,
+ user,
+ credential_scope,
+ Curl_dyn_ptr(&signed_headers),
+ sha_hex,
+ date_header ? date_header : "",
+ content_sha256_hdr);
if(!auth_headers) {
goto fail;
}
diff --git a/libs/libcurl/src/http_chunks.c b/libs/libcurl/src/http_chunks.c
index 77e3ee3104..d703316435 100644
--- a/libs/libcurl/src/http_chunks.c
+++ b/libs/libcurl/src/http_chunks.c
@@ -189,7 +189,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
else {
ch->state = CHUNK_DATA;
CURL_TRC_WRITE(data, "http_chunked, chunk start of %"
- CURL_FORMAT_CURL_OFF_T " bytes", ch->datasize);
+ FMT_OFF_T " bytes", ch->datasize);
}
}
@@ -226,7 +226,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
buf += piece; /* move read pointer forward */
blen -= piece; /* decrease space left in this round */
CURL_TRC_WRITE(data, "http_chunked, write %zu body bytes, %"
- CURL_FORMAT_CURL_OFF_T " bytes in chunk remain",
+ FMT_OFF_T " bytes in chunk remain",
piece, ch->datasize);
if(0 == ch->datasize)
diff --git a/libs/libcurl/src/http_negotiate.c b/libs/libcurl/src/http_negotiate.c
index 4613bf8eda..53668e2501 100644
--- a/libs/libcurl/src/http_negotiate.c
+++ b/libs/libcurl/src/http_negotiate.c
@@ -30,6 +30,7 @@
#include "sendf.h"
#include "http_negotiate.h"
#include "vauth/vauth.h"
+#include "vtls/vtls.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -106,11 +107,27 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
#if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS)
neg_ctx->sslContext = conn->sslContext;
#endif
+ /* Check if the connection is using SSL and get the channel binding data */
+#ifdef HAVE_GSSAPI
+ if(conn->handler->flags & PROTOPT_SSL) {
+ Curl_dyn_init(&neg_ctx->channel_binding_data, SSL_CB_MAX_SIZE);
+ result = Curl_ssl_get_channel_binding(
+ data, FIRSTSOCKET, &neg_ctx->channel_binding_data);
+ if(result) {
+ Curl_http_auth_cleanup_negotiate(conn);
+ return result;
+ }
+ }
+#endif
/* Initialize the security context and decode our challenge */
result = Curl_auth_decode_spnego_message(data, userp, passwdp, service,
host, header, neg_ctx);
+#ifdef HAVE_GSSAPI
+ Curl_dyn_free(&neg_ctx->channel_binding_data);
+#endif
+
if(result)
Curl_http_auth_cleanup_negotiate(conn);
diff --git a/libs/libcurl/src/http_ntlm.c b/libs/libcurl/src/http_ntlm.c
index a93545be32..03788062ff 100644
--- a/libs/libcurl/src/http_ntlm.c
+++ b/libs/libcurl/src/http_ntlm.c
@@ -123,7 +123,7 @@ CURLcode Curl_input_ntlm(struct Curl_easy *data,
}
/*
- * This is for creating ntlm header output
+ * This is for creating NTLM header output
*/
CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
{
@@ -187,10 +187,10 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
passwdp = "";
#ifdef USE_WINDOWS_SSPI
- if(!s_hSecDll) {
+ if(!Curl_hSecDll) {
/* not thread safe and leaks - use curl_global_init() to avoid */
CURLcode err = Curl_sspi_global_init();
- if(!s_hSecDll)
+ if(!Curl_hSecDll)
return err;
}
#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
diff --git a/libs/libcurl/src/http_ntlm.h b/libs/libcurl/src/http_ntlm.h
index 1614317f19..c03ba310e8 100644
--- a/libs/libcurl/src/http_ntlm.h
+++ b/libs/libcurl/src/http_ntlm.h
@@ -28,11 +28,11 @@
#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM)
-/* this is for ntlm header input */
+/* 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 */
+/* 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);
diff --git a/libs/libcurl/src/idn.c b/libs/libcurl/src/idn.c
index 7607e34d82..f767906e1d 100644
--- a/libs/libcurl/src/idn.c
+++ b/libs/libcurl/src/idn.c
@@ -53,30 +53,70 @@
/* for macOS and iOS targets */
#if defined(USE_APPLE_IDN)
#include <unicode/uidna.h>
+#include <iconv.h>
+#include <langinfo.h>
#define MAX_HOST_LENGTH 512
+static CURLcode iconv_to_utf8(const char *in, size_t inlen,
+ char **out, size_t *outlen)
+{
+ iconv_t cd = iconv_open("UTF-8", nl_langinfo(CODESET));
+ if(cd != (iconv_t)-1) {
+ size_t iconv_outlen = *outlen;
+ char *iconv_in = (char *)in;
+ size_t iconv_inlen = inlen;
+ size_t iconv_result = iconv(cd, &iconv_in, &iconv_inlen,
+ out, &iconv_outlen);
+ *outlen -= iconv_outlen;
+ iconv_close(cd);
+ if(iconv_result == (size_t)-1) {
+ if(errno == ENOMEM)
+ return CURLE_OUT_OF_MEMORY;
+ else
+ return CURLE_URL_MALFORMAT;
+ }
+
+ return CURLE_OK;
+ }
+ else {
+ if(errno == ENOMEM)
+ return CURLE_OUT_OF_MEMORY;
+ else
+ return CURLE_FAILED_INIT;
+ }
+}
+
static CURLcode mac_idn_to_ascii(const char *in, char **out)
{
size_t inlen = strlen(in);
if(inlen < MAX_HOST_LENGTH) {
- UErrorCode err = U_ZERO_ERROR;
- UIDNA* idna = uidna_openUTS46(
- UIDNA_CHECK_BIDI|UIDNA_NONTRANSITIONAL_TO_ASCII, &err);
- if(!U_FAILURE(err)) {
- UIDNAInfo info = UIDNA_INFO_INITIALIZER;
- char buffer[MAX_HOST_LENGTH] = {0};
- (void)uidna_nameToASCII_UTF8(idna, in, -1, buffer,
- sizeof(buffer) - 1, &info, &err);
- uidna_close(idna);
+ char iconv_buffer[MAX_HOST_LENGTH] = {0};
+ char *iconv_outptr = iconv_buffer;
+ size_t iconv_outlen = sizeof(iconv_buffer);
+ CURLcode iconv_result = iconv_to_utf8(in, inlen,
+ &iconv_outptr, &iconv_outlen);
+ if(!iconv_result) {
+ UErrorCode err = U_ZERO_ERROR;
+ UIDNA* idna = uidna_openUTS46(
+ UIDNA_CHECK_BIDI|UIDNA_NONTRANSITIONAL_TO_ASCII, &err);
if(!U_FAILURE(err)) {
- *out = strdup(buffer);
- if(*out)
- return CURLE_OK;
- else
- return CURLE_OUT_OF_MEMORY;
+ UIDNAInfo info = UIDNA_INFO_INITIALIZER;
+ char buffer[MAX_HOST_LENGTH] = {0};
+ (void)uidna_nameToASCII_UTF8(idna, iconv_buffer, (int)iconv_outlen,
+ buffer, sizeof(buffer) - 1, &info, &err);
+ uidna_close(idna);
+ if(!U_FAILURE(err) && !info.errors) {
+ *out = strdup(buffer);
+ if(*out)
+ return CURLE_OK;
+ else
+ return CURLE_OUT_OF_MEMORY;
+ }
}
}
+ else
+ return iconv_result;
}
return CURLE_URL_MALFORMAT;
}
@@ -110,7 +150,8 @@ static CURLcode mac_ascii_to_idn(const char *in, char **out)
#ifdef USE_WIN32_IDN
/* using Windows kernel32 and normaliz libraries. */
-#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x600
+#if (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x600) && \
+ (!defined(WINVER) || WINVER < 0x600)
WINBASEAPI int WINAPI IdnToAscii(DWORD dwFlags,
const WCHAR *lpUnicodeCharStr,
int cchUnicodeChar,
diff --git a/libs/libcurl/src/if2ip.c b/libs/libcurl/src/if2ip.c
index 25948b9edf..a3f07befce 100644
--- a/libs/libcurl/src/if2ip.c
+++ b/libs/libcurl/src/if2ip.c
@@ -216,7 +216,15 @@ if2ip_result_t Curl_if2ip(int af,
memcpy(req.ifr_name, interf, len + 1);
req.ifr_addr.sa_family = AF_INET;
+#if defined(__GNUC__) && defined(_AIX)
+/* Suppress warning inside system headers */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshift-sign-overflow"
+#endif
if(ioctl(dummy, SIOCGIFADDR, &req) < 0) {
+#if defined(__GNUC__) && defined(_AIX)
+#pragma GCC diagnostic pop
+#endif
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
diff --git a/libs/libcurl/src/imap.c b/libs/libcurl/src/imap.c
index 95c36b7868..7d097b95d5 100644
--- a/libs/libcurl/src/imap.c
+++ b/libs/libcurl/src/imap.c
@@ -814,8 +814,7 @@ static CURLcode imap_perform_append(struct Curl_easy *data)
return CURLE_OUT_OF_MEMORY;
/* Send the APPEND command */
- result = imap_sendf(data,
- "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}",
+ result = imap_sendf(data, "APPEND %s (\\Seen) {%" FMT_OFF_T "}",
mailbox, data->state.infilesize);
free(mailbox);
@@ -1168,8 +1167,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
}
if(parsed) {
- infof(data, "Found %" CURL_FORMAT_CURL_OFF_T " bytes to download",
- size);
+ infof(data, "Found %" FMT_OFF_T " bytes to download", size);
Curl_pgrsSetDownloadSize(data, size);
if(pp->overflow) {
@@ -1196,7 +1194,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
if(result)
return result;
- infof(data, "Written %zu bytes, %" CURL_FORMAT_CURL_OFF_TU
+ infof(data, "Written %zu bytes, %" FMT_OFF_TU
" bytes are left for transfer", chunk, size - chunk);
/* Have we used the entire overflow or just part of it?*/
diff --git a/libs/libcurl/src/inet_ntop.c b/libs/libcurl/src/inet_ntop.c
index 408bdf1537..ff318d1a25 100644
--- a/libs/libcurl/src/inet_ntop.c
+++ b/libs/libcurl/src/inet_ntop.c
@@ -185,8 +185,8 @@ static char *inet_ntop6(const unsigned char *src, char *dst, size_t size)
* 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. When this
+ * 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. When this
* function returns NULL, check errno not SOCKERRNO.
*/
char *Curl_inet_ntop(int af, const void *src, char *buf, size_t size)
diff --git a/libs/libcurl/src/inet_pton.c b/libs/libcurl/src/inet_pton.c
index 6f39bc170f..cf0027648a 100644
--- a/libs/libcurl/src/inet_pton.c
+++ b/libs/libcurl/src/inet_pton.c
@@ -65,8 +65,8 @@ static int inet_pton6(const char *src, unsigned char *dst);
* -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. When this function returns
+ * in the Winsock error code. This is to avoid losing the
+ * actual last Winsock error. When this function returns
* -1, check errno not SOCKERRNO.
* author:
* Paul Vixie, 1996.
diff --git a/libs/libcurl/src/krb5.c b/libs/libcurl/src/krb5.c
index f7f6d2570c..cf2e3f769f 100644
--- a/libs/libcurl/src/krb5.c
+++ b/libs/libcurl/src/krb5.c
@@ -91,7 +91,7 @@ static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn,
#ifdef HAVE_GSSAPI
conn->data_prot = PROT_CMD;
#endif
- result = Curl_xfer_send(data, sptr, write_len, &bytes_written);
+ result = Curl_xfer_send(data, sptr, write_len, FALSE, &bytes_written);
#ifdef HAVE_GSSAPI
DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
conn->data_prot = data_sec;
@@ -336,17 +336,20 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
}
_gssresp.value = NULL; /* make sure it is initialized */
+ _gssresp.length = 0;
p += 4; /* over '789 ' */
p = strstr(p, "ADAT=");
if(p) {
- result = Curl_base64_decode(p + 5,
- (unsigned char **)&_gssresp.value,
- &_gssresp.length);
+ unsigned char *outptr;
+ size_t outlen;
+ result = Curl_base64_decode(p + 5, &outptr, &outlen);
if(result) {
failf(data, "base64-decoding: %s", curl_easy_strerror(result));
ret = AUTH_CONTINUE;
break;
}
+ _gssresp.value = outptr;
+ _gssresp.length = outlen;
}
gssresp = &_gssresp;
@@ -497,7 +500,7 @@ socket_write(struct Curl_easy *data, int sockindex, const void *to,
size_t written;
while(len > 0) {
- result = Curl_conn_send(data, sockindex, to_p, len, &written);
+ result = Curl_conn_send(data, sockindex, to_p, len, FALSE, &written);
if(!result && written > 0) {
len -= written;
to_p += written;
@@ -686,10 +689,12 @@ static ssize_t sec_write(struct Curl_easy *data, struct connectdata *conn,
/* Matches Curl_send signature */
static ssize_t sec_send(struct Curl_easy *data, int sockindex,
- const void *buffer, size_t len, CURLcode *err)
+ const void *buffer, size_t len, bool eos,
+ CURLcode *err)
{
struct connectdata *conn = data->conn;
curl_socket_t fd = conn->sock[sockindex];
+ (void)eos; /* unused */
*err = CURLE_OK;
return sec_write(data, conn, fd, buffer, len);
}
diff --git a/libs/libcurl/src/ldap.c b/libs/libcurl/src/ldap.c
index 073f003fd4..6bc73526d7 100644
--- a/libs/libcurl/src/ldap.c
+++ b/libs/libcurl/src/ldap.c
@@ -143,7 +143,7 @@ static void _ldap_free_urldesc(LDAPURLDesc *ludp);
#endif
#if defined(USE_WIN32_LDAP) && defined(ldap_err2string)
-/* Use ansi error strings in UNICODE builds */
+/* Use ANSI error strings in Unicode builds */
#undef ldap_err2string
#define ldap_err2string ldap_err2stringA
#endif
diff --git a/libs/libcurl/src/libcurl.def b/libs/libcurl/src/libcurl.def
new file mode 100644
index 0000000000..4f8f9229d2
--- /dev/null
+++ b/libs/libcurl/src/libcurl.def
@@ -0,0 +1,95 @@
+EXPORTS
+curl_easy_cleanup
+curl_easy_duphandle
+curl_easy_escape
+curl_easy_getinfo
+curl_easy_header
+curl_easy_init
+curl_easy_nextheader
+curl_easy_option_by_id
+curl_easy_option_by_name
+curl_easy_option_next
+curl_easy_pause
+curl_easy_perform
+curl_easy_recv
+curl_easy_reset
+curl_easy_send
+curl_easy_setopt
+curl_easy_strerror
+curl_easy_unescape
+curl_easy_upkeep
+curl_escape
+curl_formadd
+curl_formfree
+curl_formget
+curl_free
+curl_getdate
+curl_getenv
+curl_global_cleanup
+curl_global_init
+curl_global_init_mem
+curl_global_sslset
+curl_global_trace
+curl_maprintf
+curl_mfprintf
+curl_mime_addpart
+curl_mime_data
+curl_mime_data_cb
+curl_mime_encoder
+curl_mime_filedata
+curl_mime_filename
+curl_mime_free
+curl_mime_headers
+curl_mime_init
+curl_mime_name
+curl_mime_subparts
+curl_mime_type
+curl_mprintf
+curl_msnprintf
+curl_msprintf
+curl_multi_add_handle
+curl_multi_assign
+curl_multi_cleanup
+curl_multi_fdset
+curl_multi_get_handles
+curl_multi_info_read
+curl_multi_init
+curl_multi_perform
+curl_multi_poll
+curl_multi_remove_handle
+curl_multi_setopt
+curl_multi_socket
+curl_multi_socket_action
+curl_multi_socket_all
+curl_multi_strerror
+curl_multi_timeout
+curl_multi_wait
+curl_multi_waitfds
+curl_multi_wakeup
+curl_mvaprintf
+curl_mvfprintf
+curl_mvprintf
+curl_mvsnprintf
+curl_mvsprintf
+curl_pushheader_byname
+curl_pushheader_bynum
+curl_share_cleanup
+curl_share_init
+curl_share_setopt
+curl_share_strerror
+curl_slist_append
+curl_slist_free_all
+curl_strequal
+curl_strnequal
+curl_unescape
+curl_url
+curl_url_cleanup
+curl_url_dup
+curl_url_get
+curl_url_set
+curl_url_strerror
+curl_version
+curl_version_info
+curl_ws_meta
+curl_ws_recv
+curl_ws_send
diff --git a/libs/libcurl/src/llist.c b/libs/libcurl/src/llist.c
index 39a20f2b8c..4dafa6e0d1 100644
--- a/libs/libcurl/src/llist.c
+++ b/libs/libcurl/src/llist.c
@@ -32,16 +32,34 @@
/* this must be the last include file */
#include "memdebug.h"
+#define LLISTINIT 0x100cc001 /* random pattern */
+#define NODEINIT 0x12344321 /* random pattern */
+#define NODEREM 0x54321012 /* random pattern */
+
+
+#ifdef DEBUGBUILD
+#define VERIFYNODE(x) verifynode(x)
+static struct Curl_llist_node *verifynode(struct Curl_llist_node *n)
+{
+ DEBUGASSERT(!n || (n->_init == NODEINIT));
+ return n;
+}
+#else
+#define VERIFYNODE(x) x
+#endif
/*
* @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;
+ l->_size = 0;
+ l->_dtor = dtor;
+ l->_head = NULL;
+ l->_tail = NULL;
+#ifdef DEBUGBUILD
+ l->_init = LLISTINIT;
+#endif
}
/*
@@ -56,36 +74,45 @@ Curl_llist_init(struct Curl_llist *l, Curl_llist_dtor dtor)
* @unittest: 1300
*/
void
-Curl_llist_insert_next(struct Curl_llist *list, struct Curl_llist_element *e,
+Curl_llist_insert_next(struct Curl_llist *list,
+ struct Curl_llist_node *e, /* may be NULL */
const void *p,
- struct Curl_llist_element *ne)
+ struct Curl_llist_node *ne)
{
- ne->ptr = (void *) p;
- if(list->size == 0) {
- list->head = ne;
- list->head->prev = NULL;
- list->head->next = NULL;
- list->tail = ne;
+ DEBUGASSERT(list);
+ DEBUGASSERT(list->_init == LLISTINIT);
+ DEBUGASSERT(ne);
+
+#ifdef DEBUGBUILD
+ ne->_init = NODEINIT;
+#endif
+ ne->_ptr = (void *) p;
+ ne->_list = list;
+ 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;
+ ne->_next = e?e->_next:list->_head;
+ ne->_prev = e;
if(!e) {
- list->head->prev = ne;
- list->head = ne;
+ list->_head->_prev = ne;
+ list->_head = ne;
}
- else if(e->next) {
- e->next->prev = ne;
+ else if(e->_next) {
+ e->_next->_prev = ne;
}
else {
- list->tail = ne;
+ list->_tail = ne;
}
if(e)
- e->next = ne;
+ e->_next = ne;
}
- ++list->size;
+ ++list->_size;
}
/*
@@ -99,64 +126,141 @@ Curl_llist_insert_next(struct Curl_llist *list, struct Curl_llist_element *e,
*/
void
Curl_llist_append(struct Curl_llist *list, const void *p,
- struct Curl_llist_element *ne)
+ struct Curl_llist_node *ne)
{
- Curl_llist_insert_next(list, list->tail, p, ne);
+ DEBUGASSERT(list);
+ DEBUGASSERT(list->_init == LLISTINIT);
+ DEBUGASSERT(ne);
+ Curl_llist_insert_next(list, list->_tail, p, ne);
}
/*
* @unittest: 1300
*/
void
-Curl_llist_remove(struct Curl_llist *list, struct Curl_llist_element *e,
- void *user)
+Curl_node_uremove(struct Curl_llist_node *e, void *user)
{
void *ptr;
- if(!e || list->size == 0)
+ struct Curl_llist *list;
+ if(!e)
return;
- if(e == list->head) {
- list->head = e->next;
+ list = e->_list;
+ DEBUGASSERT(list);
+ DEBUGASSERT(list->_init == LLISTINIT);
+ DEBUGASSERT(list->_size);
+ DEBUGASSERT(e->_init == NODEINIT);
+ if(e == list->_head) {
+ list->_head = e->_next;
- if(!list->head)
- list->tail = NULL;
+ if(!list->_head)
+ list->_tail = NULL;
else
- e->next->prev = NULL;
+ e->_next->_prev = NULL;
}
else {
- if(e->prev)
- e->prev->next = e->next;
+ if(e->_prev)
+ e->_prev->_next = e->_next;
- if(!e->next)
- list->tail = e->prev;
+ if(!e->_next)
+ list->_tail = e->_prev;
else
- e->next->prev = e->prev;
+ e->_next->_prev = e->_prev;
}
- ptr = e->ptr;
+ ptr = e->_ptr;
- e->ptr = NULL;
- e->prev = NULL;
- e->next = NULL;
+ e->_list = NULL;
+ e->_ptr = NULL;
+ e->_prev = NULL;
+ e->_next = NULL;
+#ifdef DEBUGBUILD
+ e->_init = NODEREM; /* specific pattern on remove - not zero */
+#endif
- --list->size;
+ --list->_size;
/* call the dtor() last for when it actually frees the 'e' memory itself */
- if(list->dtor)
- list->dtor(user, ptr);
+ if(list->_dtor)
+ list->_dtor(user, ptr);
+}
+
+void Curl_node_remove(struct Curl_llist_node *e)
+{
+ Curl_node_uremove(e, NULL);
}
void
Curl_llist_destroy(struct Curl_llist *list, void *user)
{
if(list) {
- while(list->size > 0)
- Curl_llist_remove(list, list->tail, user);
+ DEBUGASSERT(list->_init == LLISTINIT);
+ while(list->_size > 0)
+ Curl_node_uremove(list->_tail, user);
}
}
-size_t
-Curl_llist_count(struct Curl_llist *list)
+/* Curl_llist_head() returns the first 'struct Curl_llist_node *', which
+ might be NULL */
+struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list)
+{
+ DEBUGASSERT(list);
+ DEBUGASSERT(list->_init == LLISTINIT);
+ return VERIFYNODE(list->_head);
+}
+
+#ifdef UNITTESTS
+/* Curl_llist_tail() returns the last 'struct Curl_llist_node *', which
+ might be NULL */
+struct Curl_llist_node *Curl_llist_tail(struct Curl_llist *list)
+{
+ DEBUGASSERT(list);
+ DEBUGASSERT(list->_init == LLISTINIT);
+ return VERIFYNODE(list->_tail);
+}
+#endif
+
+/* Curl_llist_count() returns a size_t the number of nodes in the list */
+size_t Curl_llist_count(struct Curl_llist *list)
+{
+ DEBUGASSERT(list);
+ DEBUGASSERT(list->_init == LLISTINIT);
+ return list->_size;
+}
+
+/* Curl_node_elem() returns the custom data from a Curl_llist_node */
+void *Curl_node_elem(struct Curl_llist_node *n)
+{
+ DEBUGASSERT(n);
+ DEBUGASSERT(n->_init == NODEINIT);
+ return n->_ptr;
+}
+
+/* Curl_node_next() returns the next element in a list from a given
+ Curl_llist_node */
+struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n)
+{
+ DEBUGASSERT(n);
+ DEBUGASSERT(n->_init == NODEINIT);
+ return VERIFYNODE(n->_next);
+}
+
+#ifdef UNITTESTS
+
+/* Curl_node_prev() returns the previous element in a list from a given
+ Curl_llist_node */
+struct Curl_llist_node *Curl_node_prev(struct Curl_llist_node *n)
+{
+ DEBUGASSERT(n);
+ DEBUGASSERT(n->_init == NODEINIT);
+ return VERIFYNODE(n->_prev);
+}
+
+#endif
+
+struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n)
{
- return list->size;
+ DEBUGASSERT(n);
+ DEBUGASSERT(!n->_list || n->_init == NODEINIT);
+ return n->_list;
}
diff --git a/libs/libcurl/src/llist.h b/libs/libcurl/src/llist.h
index bff60fe138..3f44090341 100644
--- a/libs/libcurl/src/llist.h
+++ b/libs/libcurl/src/llist.h
@@ -27,28 +27,63 @@
#include "curl_setup.h"
#include <stddef.h>
-typedef void (*Curl_llist_dtor)(void *, void *);
+typedef void (*Curl_llist_dtor)(void *user, void *elem);
-struct Curl_llist_element {
- void *ptr;
- struct Curl_llist_element *prev;
- struct Curl_llist_element *next;
-};
+/* none of these struct members should be referenced directly, use the
+ dedicated functions */
struct Curl_llist {
- struct Curl_llist_element *head;
- struct Curl_llist_element *tail;
- Curl_llist_dtor dtor;
- size_t size;
+ struct Curl_llist_node *_head;
+ struct Curl_llist_node *_tail;
+ Curl_llist_dtor _dtor;
+ size_t _size;
+#ifdef DEBUGBUILD
+ int _init; /* detect API usage mistakes */
+#endif
+};
+
+struct Curl_llist_node {
+ struct Curl_llist *_list; /* the list where this belongs */
+ void *_ptr;
+ struct Curl_llist_node *_prev;
+ struct Curl_llist_node *_next;
+#ifdef DEBUGBUILD
+ int _init; /* detect API usage mistakes */
+#endif
};
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_insert_next(struct Curl_llist *, struct Curl_llist_node *,
+ const void *, struct Curl_llist_node *node);
void Curl_llist_append(struct Curl_llist *,
- const void *, struct Curl_llist_element *node);
-void Curl_llist_remove(struct Curl_llist *, struct Curl_llist_element *,
- void *);
-size_t Curl_llist_count(struct Curl_llist *);
+ const void *, struct Curl_llist_node *node);
+void Curl_node_uremove(struct Curl_llist_node *, void *);
+void Curl_node_remove(struct Curl_llist_node *);
void Curl_llist_destroy(struct Curl_llist *, void *);
+
+/* Curl_llist_head() returns the first 'struct Curl_llist_node *', which
+ might be NULL */
+struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list);
+
+/* Curl_llist_tail() returns the last 'struct Curl_llist_node *', which
+ might be NULL */
+struct Curl_llist_node *Curl_llist_tail(struct Curl_llist *list);
+
+/* Curl_llist_count() returns a size_t the number of nodes in the list */
+size_t Curl_llist_count(struct Curl_llist *list);
+
+/* Curl_node_elem() returns the custom data from a Curl_llist_node */
+void *Curl_node_elem(struct Curl_llist_node *n);
+
+/* Curl_node_next() returns the next element in a list from a given
+ Curl_llist_node */
+struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n);
+
+/* Curl_node_prev() returns the previous element in a list from a given
+ Curl_llist_node */
+struct Curl_llist_node *Curl_node_prev(struct Curl_llist_node *n);
+
+/* Curl_node_llist() return the list the node is in or NULL. */
+struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n);
+
#endif /* HEADER_CURL_LLIST_H */
diff --git a/libs/libcurl/src/macos.c b/libs/libcurl/src/macos.c
index d8f4fe1d7c..f9396e0360 100644
--- a/libs/libcurl/src/macos.c
+++ b/libs/libcurl/src/macos.c
@@ -34,21 +34,19 @@
CURLcode Curl_macos_init(void)
{
- {
- /*
- * The automagic conversion from IPv4 literals to IPv6 literals only
- * works if the SCDynamicStoreCopyProxies system function gets called
- * first. As Curl currently does not support system-wide HTTP proxies, we
- * therefore do not use any value this function might return.
- *
- * This function is only available on macOS and is not needed for
- * IPv4-only builds, hence the conditions for defining
- * CURL_MACOS_CALL_COPYPROXIES in curl_setup.h.
- */
- CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL);
- if(dict)
- CFRelease(dict);
- }
+ /*
+ * The automagic conversion from IPv4 literals to IPv6 literals only
+ * works if the SCDynamicStoreCopyProxies system function gets called
+ * first. As Curl currently does not support system-wide HTTP proxies, we
+ * therefore do not use any value this function might return.
+ *
+ * This function is only available on macOS and is not needed for
+ * IPv4-only builds, hence the conditions for defining
+ * CURL_MACOS_CALL_COPYPROXIES in curl_setup.h.
+ */
+ CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL);
+ if(dict)
+ CFRelease(dict);
return CURLE_OK;
}
diff --git a/libs/libcurl/src/memdebug.c b/libs/libcurl/src/memdebug.c
index 14a3911e03..3665dfa113 100644
--- a/libs/libcurl/src/memdebug.c
+++ b/libs/libcurl/src/memdebug.c
@@ -312,7 +312,7 @@ curl_socket_t curl_dbg_socket(int domain, int type, int protocol,
sockfd = socket(domain, type, protocol);
if(source && (sockfd != CURL_SOCKET_BAD))
- curl_dbg_log("FD %s:%d socket() = %" CURL_FORMAT_SOCKET_T "\n",
+ curl_dbg_log("FD %s:%d socket() = %" FMT_SOCKET_T "\n",
source, line, sockfd);
return sockfd;
@@ -356,8 +356,8 @@ int curl_dbg_socketpair(int domain, int type, int protocol,
if(source && (0 == res))
curl_dbg_log("FD %s:%d socketpair() = "
- "%" CURL_FORMAT_SOCKET_T " %" CURL_FORMAT_SOCKET_T "\n",
- source, line, socket_vector[0], socket_vector[1]);
+ "%" FMT_SOCKET_T " %" FMT_SOCKET_T "\n",
+ source, line, socket_vector[0], socket_vector[1]);
return res;
}
@@ -372,7 +372,7 @@ curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen,
curl_socket_t sockfd = accept(s, addr, addrlen);
if(source && (sockfd != CURL_SOCKET_BAD))
- curl_dbg_log("FD %s:%d accept() = %" CURL_FORMAT_SOCKET_T "\n",
+ curl_dbg_log("FD %s:%d accept() = %" FMT_SOCKET_T "\n",
source, line, sockfd);
return sockfd;
@@ -382,7 +382,7 @@ curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen,
void curl_dbg_mark_sclose(curl_socket_t sockfd, int line, const char *source)
{
if(source)
- curl_dbg_log("FD %s:%d sclose(%" CURL_FORMAT_SOCKET_T ")\n",
+ curl_dbg_log("FD %s:%d sclose(%" FMT_SOCKET_T ")\n",
source, line, sockfd);
}
diff --git a/libs/libcurl/src/memdebug.h b/libs/libcurl/src/memdebug.h
index 23f378643e..d5ec4e3def 100644
--- a/libs/libcurl/src/memdebug.h
+++ b/libs/libcurl/src/memdebug.h
@@ -34,9 +34,9 @@
#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)))
+# 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)
@@ -114,11 +114,17 @@ CURL_EXTERN int curl_dbg_fclose(FILE *file, int line, const char *source);
/* Set this symbol on the command-line, recompile all lib-sources */
#undef strdup
#define strdup(ptr) curl_dbg_strdup(ptr, __LINE__, __FILE__)
+#undef malloc
#define malloc(size) curl_dbg_malloc(size, __LINE__, __FILE__)
+#undef calloc
#define calloc(nbelem,size) curl_dbg_calloc(nbelem, size, __LINE__, __FILE__)
+#undef realloc
#define realloc(ptr,size) curl_dbg_realloc(ptr, size, __LINE__, __FILE__)
+#undef free
#define free(ptr) curl_dbg_free(ptr, __LINE__, __FILE__)
+#undef send
#define send(a,b,c,d) curl_dbg_send(a,b,c,d, __LINE__, __FILE__)
+#undef recv
#define recv(a,b,c,d) curl_dbg_recv(a,b,c,d, __LINE__, __FILE__)
#ifdef _WIN32
diff --git a/libs/libcurl/src/mime.c b/libs/libcurl/src/mime.c
index f3ba1f929d..9cdff89208 100644
--- a/libs/libcurl/src/mime.c
+++ b/libs/libcurl/src/mime.c
@@ -92,7 +92,7 @@ static const char base64enc[] =
/* 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. */
+ * 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. */
@@ -557,7 +557,7 @@ static size_t encoder_qp_read(char *buffer, size_t size, bool ateof,
/* 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
+ 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;
@@ -1587,6 +1587,8 @@ size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream)
(void) size; /* Always 1. */
+ /* TODO: this loop is broken. If `nitems` is <= 4, some encoders will
+ * return STOP_FILLING without adding any data and this loops infinitely. */
do {
hasread = FALSE;
ret = readback_part(part, buffer, nitems, &hasread);
@@ -1678,7 +1680,7 @@ CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...)
va_list ap;
va_start(ap, fmt);
- s = curl_mvaprintf(fmt, ap);
+ s = vaprintf(fmt, ap);
va_end(ap);
if(s) {
@@ -1944,13 +1946,17 @@ static CURLcode cr_mime_read(struct Curl_easy *data,
struct cr_mime_ctx *ctx = reader->ctx;
size_t nread;
+
/* Once we have errored, we will return the same error forever */
if(ctx->errored) {
+ CURL_TRC_READ(data, "cr_mime_read(len=%zu) is errored -> %d, eos=0",
+ blen, ctx->error_result);
*pnread = 0;
*peos = FALSE;
return ctx->error_result;
}
if(ctx->seen_eos) {
+ CURL_TRC_READ(data, "cr_mime_read(len=%zu) seen eos -> 0, eos=1", blen);
*pnread = 0;
*peos = TRUE;
return CURLE_OK;
@@ -1963,16 +1969,27 @@ static CURLcode cr_mime_read(struct Curl_easy *data,
else if(remain < (curl_off_t)blen)
blen = (size_t)remain;
}
- nread = 0;
- if(blen) {
- nread = Curl_mime_read(buf, 1, blen, ctx->part);
+
+ if(blen <= 4) {
+ /* TODO: Curl_mime_read() may go into an infinite loop when reading
+ * such small lengths. Returning 0 bytes read is a fix that only works
+ * as request upload buffers will get flushed eventually and larger
+ * reads will happen again. */
+ CURL_TRC_READ(data, "cr_mime_read(len=%zu), too small, return", blen);
+ *pnread = 0;
+ *peos = FALSE;
+ goto out;
}
+ nread = Curl_mime_read(buf, 1, blen, ctx->part);
+ CURL_TRC_READ(data, "cr_mime_read(len=%zu), mime_read() -> %zd",
+ blen, nread);
+
switch(nread) {
case 0:
if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) {
failf(data, "client mime read EOF fail, "
- "only %"CURL_FORMAT_CURL_OFF_T"/%"CURL_FORMAT_CURL_OFF_T
+ "only %"FMT_OFF_T"/%"FMT_OFF_T
" of needed bytes read", ctx->read_len, ctx->total_len);
return CURLE_READ_ERROR;
}
@@ -1991,11 +2008,21 @@ static CURLcode cr_mime_read(struct Curl_easy *data,
case CURL_READFUNC_PAUSE:
/* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */
+ CURL_TRC_READ(data, "cr_mime_read(len=%zu), paused by callback", blen);
data->req.keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */
*pnread = 0;
*peos = FALSE;
break; /* nothing was read */
+ case STOP_FILLING:
+ case READ_ERROR:
+ failf(data, "read error getting mime data");
+ *pnread = 0;
+ *peos = FALSE;
+ ctx->errored = TRUE;
+ ctx->error_result = CURLE_READ_ERROR;
+ return CURLE_READ_ERROR;
+
default:
if(nread > blen) {
/* the read function returned a too large value */
@@ -2013,9 +2040,11 @@ static CURLcode cr_mime_read(struct Curl_easy *data,
*peos = ctx->seen_eos;
break;
}
- DEBUGF(infof(data, "cr_mime_read(len=%zu, total=%"CURL_FORMAT_CURL_OFF_T
- ", read=%"CURL_FORMAT_CURL_OFF_T") -> %d, %zu, %d",
- blen, ctx->total_len, ctx->read_len, CURLE_OK, *pnread, *peos));
+
+out:
+ CURL_TRC_READ(data, "cr_mime_read(len=%zu, total=%" FMT_OFF_T
+ ", read=%"FMT_OFF_T") -> %d, %zu, %d",
+ blen, ctx->total_len, ctx->read_len, CURLE_OK, *pnread, *peos);
return CURLE_OK;
}
@@ -2057,7 +2086,7 @@ static CURLcode cr_mime_resume_from(struct Curl_easy *data,
if((nread == 0) || (nread > 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
+ failf(data, "Could only read %" FMT_OFF_T
" bytes from the mime post", passed);
return CURLE_READ_ERROR;
}
diff --git a/libs/libcurl/src/mprintf.c b/libs/libcurl/src/mprintf.c
index 24e7981f1a..3325c7c4a1 100644
--- a/libs/libcurl/src/mprintf.c
+++ b/libs/libcurl/src/mprintf.c
@@ -25,7 +25,6 @@
#include "curl_setup.h"
#include "dynbuf.h"
#include "curl_printf.h"
-#include <curl/mprintf.h>
#include "curl_memory.h"
/* The last #include file should be: */
diff --git a/libs/libcurl/src/mqtt.c b/libs/libcurl/src/mqtt.c
index 83d49ce707..c8cf2a22ac 100644
--- a/libs/libcurl/src/mqtt.c
+++ b/libs/libcurl/src/mqtt.c
@@ -121,7 +121,7 @@ static CURLcode mqtt_send(struct Curl_easy *data,
CURLcode result = CURLE_OK;
struct MQTT *mq = data->req.p.mqtt;
size_t n;
- result = Curl_xfer_send(data, buf, len, &n);
+ result = Curl_xfer_send(data, buf, len, FALSE, &n);
if(result)
return result;
Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n);
diff --git a/libs/libcurl/src/multi.c b/libs/libcurl/src/multi.c
index 39f4c2c4f2..051bbd7efa 100644
--- a/libs/libcurl/src/multi.c
+++ b/libs/libcurl/src/multi.c
@@ -94,9 +94,12 @@ static CURLMcode add_next_timeout(struct curltime now,
struct Curl_multi *multi,
struct Curl_easy *d);
static CURLMcode multi_timeout(struct Curl_multi *multi,
+ struct curltime *expire_time,
long *timeout_ms);
static void process_pending_handles(struct Curl_multi *multi);
static void multi_xfer_bufs_free(struct Curl_multi *multi);
+static void Curl_expire_ex(struct Curl_easy *data, const struct curltime *nowp,
+ timediff_t milli, expire_id id);
#ifdef DEBUGBUILD
static const char * const multi_statename[]={
@@ -247,10 +250,8 @@ static size_t trhash(void *key, size_t key_length, size_t 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;
+ return !memcmp(k1, k2, k1_len);
}
static void trhash_dtor(void *nada)
@@ -410,15 +411,18 @@ struct Curl_multi *Curl_multi_handle(size_t hashsize, /* socket hash */
Curl_hash_init(&multi->proto_hash, 23,
Curl_hash_str, Curl_str_key_compare, ph_freeentry);
- if(Curl_conncache_init(&multi->conn_cache, multi, chashsize))
+ if(Curl_cpool_init(&multi->cpool, Curl_on_disconnect,
+ multi, NULL, chashsize))
goto error;
Curl_llist_init(&multi->msglist, NULL);
+ Curl_llist_init(&multi->process, NULL);
Curl_llist_init(&multi->pending, NULL);
Curl_llist_init(&multi->msgsent, NULL);
multi->multiplexing = TRUE;
multi->max_concurrent_streams = 100;
+ multi->last_timeout_ms = -1;
#ifdef USE_WINSOCK
multi->wsa_event = WSACreateEvent();
@@ -440,7 +444,7 @@ error:
sockhash_destroy(&multi->sockhash);
Curl_hash_destroy(&multi->proto_hash);
Curl_hash_destroy(&multi->hostcache);
- Curl_conncache_destroy(&multi->conn_cache);
+ Curl_cpool_destroy(&multi->cpool);
free(multi);
return NULL;
}
@@ -466,52 +470,6 @@ static void multi_warn_debug(struct Curl_multi *multi, struct Curl_easy *data)
#define multi_warn_debug(x,y) Curl_nop_stmt
#endif
-/* returns TRUE if the easy handle is supposed to be present in the main link
- list */
-static bool in_main_list(struct Curl_easy *data)
-{
- return ((data->mstate != MSTATE_PENDING) &&
- (data->mstate != MSTATE_MSGSENT));
-}
-
-static void link_easy(struct Curl_multi *multi,
- struct Curl_easy *data)
-{
- /* We add the new easy 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 */
- }
-}
-
-/* unlink the given easy handle from the linked list of easy handles */
-static void unlink_easy(struct Curl_multi *multi,
- struct Curl_easy *data)
-{
- /* 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 */
-
- data->prev = data->next = NULL;
-}
-
-
CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
struct Curl_easy *data)
{
@@ -554,7 +512,7 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
/*
* No failure allowed in this function beyond this point. No modification of
* easy nor multi handle allowed before this except for potential multi's
- * connection cache growing which will not be undone in this function no
+ * connection pool growing which will not be undone in this function no
* matter what.
*/
if(data->set.errorbuffer)
@@ -574,21 +532,11 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
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)
+ if(rc) {
+ data->multi = NULL; /* not anymore */
return rc;
+ }
/* set the easy handle */
multistate(data, MSTATE_INIT);
@@ -601,13 +549,6 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
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)))
@@ -616,7 +557,8 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
data->psl = &multi->psl;
#endif
- link_easy(multi, data);
+ /* add the easy handle to the process list */
+ Curl_llist_append(&multi->process, data, &data->multi_queue);
/* increase the node-counter */
multi->num_easy++;
@@ -624,21 +566,12 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
/* 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;
- data->id = data->state.conn_cache->next_easy_id++;
- if(data->state.conn_cache->next_easy_id <= 0)
- data->state.conn_cache->next_easy_id = 0;
- CONNCACHE_UNLOCK(data);
+ /* the identifier inside the multi instance */
+ data->mid = multi->next_easy_mid++;
+ if(multi->next_easy_mid <= 0)
+ multi->next_easy_mid = 0;
+ Curl_cpool_xfer_init(data);
multi_warn_debug(multi, data);
return CURLM_OK;
@@ -660,6 +593,91 @@ static void debug_print_sock_hash(void *p)
}
#endif
+struct multi_done_ctx {
+ BIT(premature);
+};
+
+static void multi_done_locked(struct connectdata *conn,
+ struct Curl_easy *data,
+ void *userdata)
+{
+ struct multi_done_ctx *mdctx = userdata;
+
+ Curl_detach_connection(data);
+
+ if(CONN_INUSE(conn)) {
+ /* Stop if still used. */
+ DEBUGF(infof(data, "Connection still in use %zu, "
+ "no more multi_done now!",
+ Curl_llist_count(&conn->easyq)));
+ return;
+ }
+
+ data->state.done = TRUE; /* called just now! */
+ data->state.recent_conn_id = conn->connection_id;
+
+ if(conn->dns_entry)
+ Curl_resolv_unlink(data, &conn->dns_entry); /* done with this */
+ Curl_hostcache_prune(data);
+
+ /* 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 cannot know in what
+ state it is for reusing, so we are 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
+ || (mdctx->premature && !Curl_conn_is_multiplex(conn, FIRSTSOCKET))) {
+ DEBUGF(infof(data, "multi_done, not reusing connection=%"
+ FMT_OFF_T ", forbid=%d"
+ ", close=%d, premature=%d, conn_multiplex=%d",
+ conn->connection_id, data->set.reuse_forbid,
+ conn->bits.close, mdctx->premature,
+ Curl_conn_is_multiplex(conn, FIRSTSOCKET)));
+ connclose(conn, "disconnecting");
+ Curl_cpool_disconnect(data, conn, mdctx->premature);
+ }
+ else {
+ /* the connection is no longer in use by any transfer */
+ if(Curl_cpool_conn_now_idle(data, conn)) {
+ /* connection kept in the cpool */
+ 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;
+ data->state.lastconnect_id = conn->connection_id;
+ infof(data, "Connection #%" FMT_OFF_T " to host %s left intact",
+ conn->connection_id, host);
+ }
+ else {
+ /* connection was removed from the cpool and destroyed. */
+ data->state.lastconnect_id = -1;
+ }
+ }
+}
+
static CURLcode multi_done(struct Curl_easy *data,
CURLcode status, /* an error if this is called
after an error was detected */
@@ -667,6 +685,9 @@ static CURLcode multi_done(struct Curl_easy *data,
{
CURLcode result, r2;
struct connectdata *conn = data->conn;
+ struct multi_done_ctx mdctx;
+
+ memset(&mdctx, 0, sizeof(mdctx));
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
DEBUGF(infof(data, "multi_done[%s]: status: %d prem: %d done: %d",
@@ -729,106 +750,22 @@ static CURLcode multi_done(struct Curl_easy *data,
if(!result)
result = Curl_req_done(&data->req, data, premature);
- 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);
-
- /* 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 cannot know in what
- state it is for reusing, so we are 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.
- */
-
- data->state.recent_conn_id = conn->connection_id;
- 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 && !Curl_conn_is_multiplex(conn, FIRSTSOCKET))) {
- DEBUGF(infof(data, "multi_done, not reusing connection=%"
- CURL_FORMAT_CURL_OFF_T ", forbid=%d"
- ", close=%d, premature=%d, conn_multiplex=%d",
- conn->connection_id,
- data->set.reuse_forbid, conn->bits.close, premature,
- Curl_conn_is_multiplex(conn, FIRSTSOCKET)));
- 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 */
- curl_off_t connection_id = conn->connection_id;
- msnprintf(buffer, sizeof(buffer),
- "Connection #%" CURL_FORMAT_CURL_OFF_T " 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;
- data->state.recent_conn_id = connection_id;
- infof(data, "%s", buffer);
- }
- else
- data->state.lastconnect_id = -1;
- }
+ /* Under the potential connection pool's share lock, decide what to
+ * do with the transfer's connection. */
+ mdctx.premature = premature;
+ Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx);
return result;
}
-static int close_connect_only(struct Curl_easy *data,
- struct connectdata *conn, void *param)
+static void close_connect_only(struct connectdata *conn,
+ struct Curl_easy *data,
+ void *userdata)
{
- (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;
+ (void)userdata;
+ (void)data;
+ if(conn->connect_only)
+ connclose(conn, "Removing connect-only easy handle");
}
CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
@@ -836,15 +773,16 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
{
struct Curl_easy *easy = data;
bool premature;
- struct Curl_llist_element *e;
+ struct Curl_llist_node *e;
CURLMcode rc;
+ bool removed_timer = FALSE;
/* 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))
+ if(!GOOD_EASY_HANDLE(data) || !multi->num_easy)
return CURLM_BAD_EASY_HANDLE;
/* Prevent users from trying to remove same easy handle more than once */
@@ -888,18 +826,10 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
/* 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);
+ removed_timer = Curl_expire_clear(data);
- if(data->connect_queue.ptr) {
- /* the handle is in the pending or msgsent lists, so go ahead and remove
- it */
- if(data->mstate == MSTATE_PENDING)
- Curl_llist_remove(&multi->pending, &data->connect_queue, NULL);
- else
- Curl_llist_remove(&multi->msgsent, &data->connect_queue, NULL);
- }
- if(in_main_list(data))
- unlink_easy(multi, data);
+ /* the handle is in a list, remove it from whichever it is */
+ Curl_node_remove(&data->multi_queue);
if(data->dns.hostcachetype == HCACHE_MULTI) {
/* stop using the multi handle's DNS cache, *after* the possible
@@ -935,15 +865,14 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
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);
+ Curl_cpool_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);
+ Curl_cpool_do_by_id(data, data->state.lastconnect_id,
+ close_connect_only, NULL);
}
#ifdef USE_LIBPSL
@@ -952,33 +881,31 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
data->psl = NULL;
#endif
- /* as this was using a shared connection cache we clear the pointer to that
- since we are 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 is 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;
+ for(e = Curl_llist_head(&multi->msglist); e; e = Curl_node_next(e)) {
+ struct Curl_message *msg = Curl_node_elem(e);
if(msg->extmsg.easy_handle == easy) {
- Curl_llist_remove(&multi->msglist, e, NULL);
+ Curl_node_remove(e);
/* there can only be one from this specific handle */
break;
}
}
+ data->multi = NULL; /* clear the association to this multi handle */
+ data->mid = -1;
+
/* 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;
+ if(removed_timer) {
+ rc = Curl_update_timer(multi);
+ if(rc)
+ return rc;
+ }
return CURLM_OK;
}
@@ -999,7 +926,7 @@ void Curl_detach_connection(struct Curl_easy *data)
struct connectdata *conn = data->conn;
if(conn) {
Curl_conn_ev_data_detach(conn, data);
- Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL);
+ Curl_node_remove(&data->conn_queue);
}
data->conn = NULL;
}
@@ -1103,7 +1030,7 @@ static int perform_getsock(struct Curl_easy *data, curl_socket_t *sock)
sock[sockindex] = conn->sockfd;
}
- if(CURL_WANT_SEND(data)) {
+ if(Curl_req_want_send(data)) {
if((conn->sockfd != conn->writesockfd) ||
bitmap == GETSOCK_BLANK) {
/* only if they are not the same socket and we have a readable
@@ -1212,10 +1139,8 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi,
/* 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;
- struct easy_pollset ps;
- unsigned int i;
+ struct Curl_llist_node *e;
(void)exc_fd_set; /* not used */
if(!GOOD_MULTI_HANDLE(multi))
@@ -1224,20 +1149,22 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi,
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
- memset(&ps, 0, sizeof(ps));
- for(data = multi->easyp; data; data = data->next) {
- multi_getsock(data, &ps);
+ for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
+ struct Curl_easy *data = Curl_node_elem(e);
+ unsigned int i;
+
+ multi_getsock(data, &data->last_poll);
- for(i = 0; i < ps.num; i++) {
- if(!FDSET_SOCK(ps.sockets[i]))
+ for(i = 0; i < data->last_poll.num; i++) {
+ if(!FDSET_SOCK(data->last_poll.sockets[i]))
/* pretend it does not exist */
continue;
- if(ps.actions[i] & CURL_POLL_IN)
- FD_SET(ps.sockets[i], read_fd_set);
- if(ps.actions[i] & CURL_POLL_OUT)
- FD_SET(ps.sockets[i], write_fd_set);
- if((int)ps.sockets[i] > this_max_fd)
- this_max_fd = (int)ps.sockets[i];
+ if(data->last_poll.actions[i] & CURL_POLL_IN)
+ FD_SET(data->last_poll.sockets[i], read_fd_set);
+ if(data->last_poll.actions[i] & CURL_POLL_OUT)
+ FD_SET(data->last_poll.sockets[i], write_fd_set);
+ if((int)data->last_poll.sockets[i] > this_max_fd)
+ this_max_fd = (int)data->last_poll.sockets[i];
}
}
@@ -1251,10 +1178,9 @@ CURLMcode curl_multi_waitfds(struct Curl_multi *multi,
unsigned int size,
unsigned int *fd_count)
{
- struct Curl_easy *data;
struct curl_waitfds cwfds;
- struct easy_pollset ps;
CURLMcode result = CURLM_OK;
+ struct Curl_llist_node *e;
if(!ufds)
return CURLM_BAD_FUNCTION_ARGUMENT;
@@ -1266,16 +1192,16 @@ CURLMcode curl_multi_waitfds(struct Curl_multi *multi,
return CURLM_RECURSIVE_API_CALL;
Curl_waitfds_init(&cwfds, ufds, size);
- memset(&ps, 0, sizeof(ps));
- for(data = multi->easyp; data; data = data->next) {
- multi_getsock(data, &ps);
- if(Curl_waitfds_add_ps(&cwfds, &ps)) {
+ for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
+ struct Curl_easy *data = Curl_node_elem(e);
+ multi_getsock(data, &data->last_poll);
+ if(Curl_waitfds_add_ps(&cwfds, &data->last_poll)) {
result = CURLM_OUT_OF_MEMORY;
goto out;
}
}
- if(Curl_conncache_add_waitfds(&multi->conn_cache, &cwfds)) {
+ if(Curl_cpool_add_waitfds(&multi->cpool, &cwfds)) {
result = CURLM_OUT_OF_MEMORY;
goto out;
}
@@ -1312,15 +1238,16 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
bool extrawait, /* when no socket, wait */
bool use_wakeup)
{
- struct Curl_easy *data;
- struct easy_pollset ps;
size_t i;
+ struct curltime expire_time;
long timeout_internal;
int retcode = 0;
struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
struct curl_pollfds cpfds;
unsigned int curl_nfds = 0; /* how many pfds are for curl transfers */
CURLMcode result = CURLM_OK;
+ struct Curl_llist_node *e;
+
#ifdef USE_WINSOCK
WSANETWORKEVENTS wsa_events;
DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT);
@@ -1339,18 +1266,19 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
return CURLM_BAD_FUNCTION_ARGUMENT;
Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
- memset(&ps, 0, sizeof(ps));
/* Add the curl handles to our pollfds first */
- for(data = multi->easyp; data; data = data->next) {
- multi_getsock(data, &ps);
- if(Curl_pollfds_add_ps(&cpfds, &ps)) {
+ for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
+ struct Curl_easy *data = Curl_node_elem(e);
+
+ multi_getsock(data, &data->last_poll);
+ if(Curl_pollfds_add_ps(&cpfds, &data->last_poll)) {
result = CURLM_OUT_OF_MEMORY;
goto out;
}
}
- if(Curl_conncache_add_pollfds(&multi->conn_cache, &cpfds)) {
+ if(Curl_cpool_add_pollfds(&multi->cpool, &cpfds)) {
result = CURLM_OUT_OF_MEMORY;
goto out;
}
@@ -1407,7 +1335,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
* poll. Collecting the sockets may install new timers by protocols
* and connection filters.
* Use the shorter one of the internal and the caller requested timeout. */
- (void)multi_timeout(multi, &timeout_internal);
+ (void)multi_timeout(multi, &expire_time, &timeout_internal);
if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms))
timeout_ms = (int)timeout_internal;
@@ -1418,7 +1346,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
#endif
int pollrc;
#ifdef USE_WINSOCK
- if(cpfds.n) /* just pre-check with WinSock */
+ if(cpfds.n) /* just pre-check with Winsock */
pollrc = Curl_poll(cpfds.pfds, cpfds.n, 0);
else
pollrc = 0;
@@ -1438,7 +1366,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, (DWORD)timeout_ms,
FALSE);
}
- /* With WinSock, we have to run the following section unconditionally
+ /* With Winsock, we have to run the following section unconditionally
to call WSAEventSelect(fd, event, 0) on all the sockets */
{
#endif
@@ -1480,18 +1408,18 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
/* Count up all our own sockets that had activity,
and remove them from the event. */
if(curl_nfds) {
+ for(e = Curl_llist_head(&multi->process); e && !result;
+ e = Curl_node_next(e)) {
+ struct Curl_easy *data = Curl_node_elem(e);
- for(data = multi->easyp; data; data = data->next) {
- multi_getsock(data, &ps);
-
- for(i = 0; i < ps.num; i++) {
+ for(i = 0; i < data->last_poll.num; i++) {
wsa_events.lNetworkEvents = 0;
- if(WSAEnumNetworkEvents(ps.sockets[i], NULL,
+ if(WSAEnumNetworkEvents(data->last_poll.sockets[i], NULL,
&wsa_events) == 0) {
if(ret && !pollrc && wsa_events.lNetworkEvents)
retcode++;
}
- WSAEventSelect(ps.sockets[i], multi->wsa_event, 0);
+ WSAEventSelect(data->last_poll.sockets[i], multi->wsa_event, 0);
}
}
}
@@ -1741,23 +1669,23 @@ static bool multi_handle_timeout(struct Curl_easy *data,
else
since = data->progress.t_startop;
if(data->mstate == MSTATE_RESOLVING)
- failf(data, "Resolving timed out after %" CURL_FORMAT_TIMEDIFF_T
+ failf(data, "Resolving timed out after %" FMT_TIMEDIFF_T
" milliseconds", Curl_timediff(*now, since));
else if(data->mstate == MSTATE_CONNECTING)
- failf(data, "Connection timed out after %" CURL_FORMAT_TIMEDIFF_T
+ failf(data, "Connection timed out after %" FMT_TIMEDIFF_T
" milliseconds", Curl_timediff(*now, since));
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",
+ failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
+ " milliseconds with %" FMT_OFF_T " out of %"
+ FMT_OFF_T " bytes received",
Curl_timediff(*now, since), 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, since), k->bytecount);
+ failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
+ " milliseconds with %" FMT_OFF_T " bytes received",
+ Curl_timediff(*now, since), k->bytecount);
}
}
*result = CURLE_OPERATION_TIMEDOUT;
@@ -1974,11 +1902,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* 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_append(&multi->pending, data, &data->connect_queue);
- /* unlink from the main list */
- unlink_easy(multi, data);
+ /* unlink from process list */
+ Curl_node_remove(&data->multi_queue);
+ /* add handle to pending list */
+ Curl_llist_append(&multi->pending, data, &data->multi_queue);
result = CURLE_OK;
break;
}
@@ -1996,8 +1923,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
WAITDO or DO! */
rc = CURLM_CALL_MULTI_PERFORM;
- if(connected)
+ if(connected) {
+ if(!data->conn->bits.reuse &&
+ Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) {
+ /* new connection, can multiplex, wake pending handles */
+ process_pending_handles(data->multi);
+ }
multistate(data, MSTATE_PROTOCONNECT);
+ }
else {
multistate(data, MSTATE_CONNECTING);
}
@@ -2081,22 +2014,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* 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) {
+ if(!result) {
rc = CURLM_CALL_MULTI_PERFORM;
- /* connect back to proxy again */
- result = CURLE_OK;
- multi_done(data, CURLE_OK, FALSE);
- multistate(data, MSTATE_CONNECT);
+ /* initiate protocol connect phase */
+ multistate(data, MSTATE_PROTOCONNECT);
}
else
-#endif
- if(!result) {
- rc = CURLM_CALL_MULTI_PERFORM;
- /* initiate protocol connect phase */
- multistate(data, MSTATE_PROTOCONNECT);
- }
- else
stream_error = TRUE;
break;
#endif
@@ -2106,6 +2029,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
DEBUGASSERT(data->conn);
result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected);
if(connected && !result) {
+ if(!data->conn->bits.reuse &&
+ Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) {
+ /* new connection, can multiplex, wake pending handles */
+ process_pending_handles(data->multi);
+ }
rc = CURLM_CALL_MULTI_PERFORM;
multistate(data, MSTATE_PROTOCONNECT);
}
@@ -2384,24 +2312,22 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
send_timeout_ms = 0;
if(data->set.max_send_speed)
send_timeout_ms =
- Curl_pgrsLimitWaitTime(data->progress.uploaded,
- data->progress.ul_limit_size,
+ Curl_pgrsLimitWaitTime(&data->progress.ul,
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,
+ Curl_pgrsLimitWaitTime(&data->progress.dl,
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);
+ /* start performing again right away */
+ rc = CURLM_CALL_MULTI_PERFORM;
}
else if(send_timeout_ms >= recv_timeout_ms)
Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST);
@@ -2417,19 +2343,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* 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,
+ send_timeout_ms = Curl_pgrsLimitWaitTime(&data->progress.ul,
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,
+ recv_timeout_ms = Curl_pgrsLimitWaitTime(&data->progress.dl,
data->set.max_recv_speed,
- data->progress.dl_limit_start,
*nowp);
if(send_timeout_ms || recv_timeout_ms) {
@@ -2443,7 +2365,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
/* read/write data if it is ready to do so */
- result = Curl_readwrite(data);
+ result = Curl_sendrecv(data, nowp);
if(data->req.done || (result == CURLE_RECV_ERROR)) {
/* If CURLE_RECV_ERROR happens early enough, we assume it was a race
@@ -2568,10 +2490,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
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);
@@ -2646,12 +2564,7 @@ statemachine_end:
We do not 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);
+ Curl_cpool_disconnect(data, conn, dead_connection);
}
}
else if(data->mstate == MSTATE_CONNECT) {
@@ -2695,10 +2608,10 @@ statemachine_end:
}
multistate(data, MSTATE_MSGSENT);
- /* add this handle to the list of msgsent handles */
- Curl_llist_append(&multi->msgsent, data, &data->connect_queue);
- /* unlink from the main list */
- unlink_easy(multi, data);
+ /* unlink from the process list */
+ Curl_node_remove(&data->multi_queue);
+ /* add this handle msgsent list */
+ Curl_llist_append(&multi->msgsent, data, &data->multi_queue);
return CURLM_OK;
}
} while((rc == CURLM_CALL_MULTI_PERFORM) || multi_ischanged(multi, FALSE));
@@ -2710,10 +2623,11 @@ statemachine_end:
CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles)
{
- struct Curl_easy *data;
CURLMcode returncode = CURLM_OK;
- struct Curl_tree *t;
+ struct Curl_tree *t = NULL;
struct curltime now = Curl_now();
+ struct Curl_llist_node *e;
+ struct Curl_llist_node *n = NULL;
SIGPIPE_VARIABLE(pipe_st);
if(!GOOD_MULTI_HANDLE(multi))
@@ -2723,30 +2637,27 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles)
return CURLM_RECURSIVE_API_CALL;
sigpipe_init(&pipe_st);
- data = multi->easyp;
- if(data) {
+ for(e = Curl_llist_head(&multi->process); e; e = n) {
+ struct Curl_easy *data = Curl_node_elem(e);
CURLMcode result;
/* Do the loop and only alter the signal ignore state if the next handle
has a different NO_SIGNAL state than the previous */
- do {
- /* the current node might be unlinked in multi_runsingle(), get the next
- pointer now */
- struct Curl_easy *datanext = data->next;
-
- if(data != multi->conn_cache.closure_handle) {
- /* connection cache handle is processed below */
- sigpipe_apply(data, &pipe_st);
- result = multi_runsingle(multi, &now, data);
- if(result)
- returncode = result;
- }
- data = datanext; /* operate on next handle */
- } while(data);
+ /* the current node might be unlinked in multi_runsingle(), get the next
+ pointer now */
+ n = Curl_node_next(e);
+
+ if(data != multi->cpool.idata) {
+ /* connection pool handle is processed below */
+ sigpipe_apply(data, &pipe_st);
+ result = multi_runsingle(multi, &now, data);
+ if(result)
+ returncode = result;
+ }
}
- sigpipe_apply(multi->conn_cache.closure_handle, &pipe_st);
- Curl_conncache_multi_perform(multi);
+ sigpipe_apply(multi->cpool.idata, &pipe_st);
+ Curl_cpool_multi_perform(multi);
sigpipe_restore(&pipe_st);
@@ -2764,7 +2675,7 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles)
multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
if(t) {
/* the removed may have another timeout in queue */
- data = t->payload;
+ struct Curl_easy *data = Curl_splayget(t);
if(data->mstate == MSTATE_PENDING) {
bool stream_unused;
CURLcode result_unused;
@@ -2774,7 +2685,7 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles)
move_pending_to_connect(multi, data);
}
}
- (void)add_next_timeout(now, multi, t->payload);
+ (void)add_next_timeout(now, multi, Curl_splayget(t));
}
} while(t);
@@ -2787,38 +2698,45 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles)
return returncode;
}
-/* unlink_all_msgsent_handles() detaches all those easy handles from this
- multi handle */
+/* unlink_all_msgsent_handles() moves all nodes back from the msgsent list to
+ the process list */
static void unlink_all_msgsent_handles(struct Curl_multi *multi)
{
- struct Curl_llist_element *e = multi->msgsent.head;
- if(e) {
- struct Curl_easy *data = e->ptr;
- DEBUGASSERT(data->mstate == MSTATE_MSGSENT);
- data->multi = NULL;
+ struct Curl_llist_node *e;
+ for(e = Curl_llist_head(&multi->msgsent); e; e = Curl_node_next(e)) {
+ struct Curl_easy *data = Curl_node_elem(e);
+ if(data) {
+ DEBUGASSERT(data->mstate == MSTATE_MSGSENT);
+ Curl_node_remove(&data->multi_queue);
+ /* put it into the process list */
+ Curl_llist_append(&multi->process, data, &data->multi_queue);
+ }
}
}
CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
{
- struct Curl_easy *data;
- struct Curl_easy *nextdata;
-
if(GOOD_MULTI_HANDLE(multi)) {
+ struct Curl_llist_node *e;
+ struct Curl_llist_node *n;
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
multi->magic = 0; /* not good anymore */
+ /* move the pending and msgsent entries back to process
+ so that there is just one list to iterate over */
unlink_all_msgsent_handles(multi);
process_pending_handles(multi);
+
/* First remove all remaining easy handles */
- data = multi->easyp;
- while(data) {
+ for(e = Curl_llist_head(&multi->process); e; e = n) {
+ struct Curl_easy *data = Curl_node_elem(e);
+
if(!GOOD_EASY_HANDLE(data))
return CURLM_BAD_HANDLE;
- nextdata = data->next;
+ n = Curl_node_next(e);
if(!data->state.done && data->conn)
/* if DONE was never called for this handle */
(void)multi_done(data, CURLE_OK, TRUE);
@@ -2829,24 +2747,18 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
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_multi_close_all(multi);
+ Curl_cpool_destroy(&multi->cpool);
sockhash_destroy(&multi->sockhash);
Curl_hash_destroy(&multi->proto_hash);
- Curl_conncache_destroy(&multi->conn_cache);
Curl_hash_destroy(&multi->hostcache);
Curl_psl_destroy(&multi->psl);
@@ -2889,15 +2801,15 @@ CURLMsg *curl_multi_info_read(struct Curl_multi *multi, int *msgs_in_queue)
!multi->in_callback &&
Curl_llist_count(&multi->msglist)) {
/* there is one or more messages in the list */
- struct Curl_llist_element *e;
+ struct Curl_llist_node *e;
/* extract the head of the list to return */
- e = multi->msglist.head;
+ e = Curl_llist_head(&multi->msglist);
- msg = e->ptr;
+ msg = Curl_node_elem(e);
/* remove the extracted entry */
- Curl_llist_remove(&multi->msglist, e, NULL);
+ Curl_node_remove(e);
*msgs_in_queue = curlx_uztosi(Curl_llist_count(&multi->msglist));
@@ -2970,18 +2882,24 @@ CURLMcode Curl_multi_pollset_ev(struct Curl_multi *multi,
}
if(last_action && (last_action != cur_action)) {
/* Socket was used already, but different action now */
- if(last_action & CURL_POLL_IN)
+ if(last_action & CURL_POLL_IN) {
+ DEBUGASSERT(entry->readers);
entry->readers--;
- if(last_action & CURL_POLL_OUT)
+ }
+ if(last_action & CURL_POLL_OUT) {
+ DEBUGASSERT(entry->writers);
entry->writers--;
- if(cur_action & CURL_POLL_IN)
+ }
+ if(cur_action & CURL_POLL_IN) {
entry->readers++;
+ }
if(cur_action & CURL_POLL_OUT)
entry->writers++;
}
else if(!last_action &&
!Curl_hash_pick(&entry->transfers, (char *)&data, /* hash key */
sizeof(struct Curl_easy *))) {
+ DEBUGASSERT(entry->users < 100000); /* detect weird values */
/* a new transfer using this socket */
entry->users++;
if(cur_action & CURL_POLL_IN)
@@ -3043,23 +2961,27 @@ CURLMcode Curl_multi_pollset_ev(struct Curl_multi *multi,
if(entry) {
unsigned char oldactions = last_ps->actions[i];
/* this socket has been removed. Decrease user count */
+ DEBUGASSERT(entry->users);
entry->users--;
if(oldactions & CURL_POLL_OUT)
entry->writers--;
if(oldactions & CURL_POLL_IN)
entry->readers--;
if(!entry->users) {
+ bool dead = FALSE;
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;
- }
+ if(rc == -1)
+ dead = TRUE;
}
sh_delentry(entry, &multi->sockhash, s);
+ if(dead) {
+ multi->dead = TRUE;
+ return CURLM_ABORTED_BY_CALLBACK;
+ }
}
else {
/* still users, but remove this handle as a user of this socket */
@@ -3097,11 +3019,15 @@ void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s)
if(data) {
/* if there is still an easy handle associated with this connection */
struct Curl_multi *multi = data->multi;
+ DEBUGF(infof(data, "Curl_multi_closed, fd=%" FMT_SOCKET_T
+ " multi is %p", s, (void *)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);
+ DEBUGF(infof(data, "Curl_multi_closed, fd=%" FMT_SOCKET_T
+ " entry is %p", s, (void *)entry));
if(entry) {
int rc = 0;
if(multi->socket_cb) {
@@ -3141,26 +3067,24 @@ static CURLMcode add_next_timeout(struct curltime now,
{
struct curltime *tv = &d->state.expiretime;
struct Curl_llist *list = &d->state.timeoutlist;
- struct Curl_llist_element *e;
- struct time_node *node = NULL;
+ struct Curl_llist_node *e;
/* 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_us(node->time, now);
+ for(e = Curl_llist_head(list); e;) {
+ struct Curl_llist_node *n = Curl_node_next(e);
+ struct time_node *node = Curl_node_elem(e);
+ timediff_t diff = Curl_timediff_us(node->time, now);
if(diff <= 0)
/* remove outdated entry */
- Curl_llist_remove(list, e, NULL);
+ Curl_node_remove(e);
else
/* the list is sorted so get out on the first mismatch */
break;
e = n;
}
- e = list->head;
+ e = Curl_llist_head(list);
if(!e) {
/* clear the expire times within the handles that we remove from the
splay tree */
@@ -3168,6 +3092,7 @@ static CURLMcode add_next_timeout(struct curltime now,
tv->tv_usec = 0;
}
else {
+ struct time_node *node = Curl_node_elem(e);
/* copy the first entry to 'tv' */
memcpy(tv, &node->time, sizeof(*tv));
@@ -3179,6 +3104,59 @@ static CURLMcode add_next_timeout(struct curltime now,
return CURLM_OK;
}
+struct multi_run_ctx {
+ struct Curl_multi *multi;
+ struct curltime now;
+ size_t run_xfers;
+ SIGPIPE_MEMBER(pipe_st);
+ bool run_cpool;
+};
+
+static CURLMcode multi_run_expired(struct multi_run_ctx *mrc)
+{
+ struct Curl_multi *multi = mrc->multi;
+ struct Curl_easy *data = NULL;
+ struct Curl_tree *t = NULL;
+ CURLMcode result = CURLM_OK;
+
+ /*
+ * The loop following here will go on as long as there are expire-times left
+ * to process (compared to mrc->now) in the splay and 'data' will be
+ * re-assigned for every expired handle we deal with.
+ */
+ while(1) {
+ /* Check if there is one (more) expired timer to deal with! This function
+ extracts a matching node if there is one */
+ multi->timetree = Curl_splaygetbest(mrc->now, multi->timetree, &t);
+ if(!t)
+ goto out;
+
+ data = Curl_splayget(t); /* assign this for next loop */
+ if(!data)
+ continue;
+
+ (void)add_next_timeout(mrc->now, multi, data);
+ if(data == multi->cpool.idata) {
+ mrc->run_cpool = TRUE;
+ continue;
+ }
+
+ mrc->run_xfers++;
+ sigpipe_apply(data, &mrc->pipe_st);
+ result = multi_runsingle(multi, &mrc->now, data);
+
+ if(CURLM_OK >= result) {
+ /* get the socket(s) and check if the state has been changed since
+ last */
+ result = singlesocket(multi, data);
+ if(result)
+ goto out;
+ }
+ }
+
+out:
+ return result;
+}
static CURLMcode multi_socket(struct Curl_multi *multi,
bool checkall,
curl_socket_t s,
@@ -3187,28 +3165,31 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
{
CURLMcode result = CURLM_OK;
struct Curl_easy *data = NULL;
- struct Curl_tree *t;
- struct curltime now = Curl_now();
- bool run_conn_cache = FALSE;
- SIGPIPE_VARIABLE(pipe_st);
+ struct multi_run_ctx mrc;
+
+ (void)ev_bitmask;
+ memset(&mrc, 0, sizeof(mrc));
+ mrc.multi = multi;
+ mrc.now = Curl_now();
+ sigpipe_init(&mrc.pipe_st);
if(checkall) {
+ struct Curl_llist_node *e;
/* *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;
+ for(e = Curl_llist_head(&multi->process); e && !result;
+ e = Curl_node_next(e)) {
+ result = singlesocket(multi, Curl_node_elem(e));
}
}
-
- /* or should we fall-through and do the timer-based stuff? */
- return result;
+ mrc.run_cpool = TRUE;
+ goto out;
}
+
if(s != CURL_SOCKET_TIMEOUT) {
struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
@@ -3219,8 +3200,8 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
asked to get removed, so thus we better survive stray socket actions
and just move on. */
/* The socket might come from a connection that is being shut down
- * by the multi's conncache. */
- Curl_conncache_multi_socket(multi, s, ev_bitmask);
+ * by the multi's connection pool. */
+ Curl_cpool_multi_socket(multi, s, ev_bitmask);
}
else {
struct Curl_hash_iterator iter;
@@ -3234,79 +3215,43 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
DEBUGASSERT(data);
DEBUGASSERT(data->magic == CURLEASY_MAGIC_NUMBER);
- if(data == multi->conn_cache.closure_handle)
- run_conn_cache = TRUE;
+ if(data == multi->cpool.idata)
+ mrc.run_cpool = TRUE;
else {
- if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK))
- /* set socket event bitmask if they are not locked */
- data->state.select_bits |= (unsigned char)ev_bitmask;
-
- Curl_expire(data, 0, EXPIRE_RUN_NOW);
+ /* Expire with out current now, so we will get it below when
+ * asking the splaytree for expired transfers. */
+ Curl_expire_ex(data, &mrc.now, 0, EXPIRE_RUN_NOW);
}
}
-
- /* Now we fall-through and do the timer-based stuff, since we do not 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 is 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.
- */
- sigpipe_init(&pipe_st);
- do {
- if(data == multi->conn_cache.closure_handle)
- run_conn_cache = TRUE;
- /* the first loop lap 'data' can be NULL */
- else if(data) {
- sigpipe_apply(data, &pipe_st);
- result = multi_runsingle(multi, &now, data);
-
- if(CURLM_OK >= result) {
- /* get the socket(s) and check if the state has been changed since
- last */
- result = singlesocket(multi, data);
- if(result)
- break;
- }
- }
-
- /* Check if there is 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);
+ result = multi_run_expired(&mrc);
+ if(result)
+ goto out;
- if(run_conn_cache) {
- sigpipe_apply(multi->conn_cache.closure_handle, &pipe_st);
- Curl_conncache_multi_perform(multi);
+ if(mrc.run_xfers) {
+ /* Running transfers takes time. With a new timestamp, we might catch
+ * other expires which are due now. Instead of telling the application
+ * to set a 0 timeout and call us again, we run them here.
+ * Do that only once or it might be unfair to transfers on other
+ * sockets. */
+ mrc.now = Curl_now();
+ result = multi_run_expired(&mrc);
}
- sigpipe_restore(&pipe_st);
+out:
+ if(mrc.run_cpool) {
+ sigpipe_apply(multi->cpool.idata, &mrc.pipe_st);
+ Curl_cpool_multi_perform(multi);
+ }
+ sigpipe_restore(&mrc.pipe_st);
if(running_handles)
*running_handles = (int)multi->num_alive;
+
+ if(CURLM_OK >= result)
+ result = Curl_update_timer(multi);
return result;
}
@@ -3395,39 +3340,28 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi,
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;
+ return multi_socket(multi, FALSE, s, 0, running_handles);
}
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;
+ return multi_socket(multi, FALSE, s, ev_bitmask, running_handles);
}
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;
+ return multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles);
}
static CURLMcode multi_timeout(struct Curl_multi *multi,
+ struct curltime *expire_time,
long *timeout_ms)
{
static const struct curltime tv_zero = {0, 0};
@@ -3443,20 +3377,29 @@ static CURLMcode multi_timeout(struct Curl_multi *multi,
/* splay the lowest to the bottom */
multi->timetree = Curl_splay(tv_zero, multi->timetree);
-
- if(Curl_splaycomparekeys(multi->timetree->key, now) > 0) {
+ /* this will not return NULL from a non-emtpy tree, but some compilers
+ * are not convinced of that. Analyzers are hard. */
+ *expire_time = multi->timetree? multi->timetree->key : tv_zero;
+
+ /* 'multi->timetree' will be non-NULL here but the compilers sometimes
+ yell at us if we assume so */
+ if(multi->timetree &&
+ Curl_timediff_us(multi->timetree->key, now) > 0) {
/* some time left before expiration */
timediff_t diff = Curl_timediff_ceil(multi->timetree->key, now);
/* this should be safe even on 32-bit archs, as we do not use that
overly long timeouts */
*timeout_ms = (long)diff;
}
- else
+ else {
/* 0 means immediately */
*timeout_ms = 0;
+ }
}
- else
+ else {
+ *expire_time = tv_zero;
*timeout_ms = -1;
+ }
return CURLM_OK;
}
@@ -3464,6 +3407,8 @@ static CURLMcode multi_timeout(struct Curl_multi *multi,
CURLMcode curl_multi_timeout(struct Curl_multi *multi,
long *timeout_ms)
{
+ struct curltime expire_time;
+
/* First, make some basic checks that the CURLM handle is a good handle */
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
@@ -3471,56 +3416,79 @@ CURLMcode curl_multi_timeout(struct Curl_multi *multi,
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
- return multi_timeout(multi, timeout_ms);
+ return multi_timeout(multi, &expire_time, timeout_ms);
}
+#define DEBUG_UPDATE_TIMER 0
+
/*
* Tell the application it should update its timers, if it subscribes to the
* update timer callback.
*/
CURLMcode Curl_update_timer(struct Curl_multi *multi)
{
+ struct curltime expire_ts;
long timeout_ms;
int rc;
+ bool set_value = FALSE;
if(!multi->timer_cb || multi->dead)
return CURLM_OK;
- if(multi_timeout(multi, &timeout_ms)) {
+ if(multi_timeout(multi, &expire_ts, &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 is 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;
+ if(timeout_ms < 0 && multi->last_timeout_ms < 0) {
+#if DEBUG_UPDATE_TIMER
+ fprintf(stderr, "Curl_update_timer(), still no timeout, no change\n");
+#endif
+ }
+ else if(timeout_ms < 0) {
+ /* there is no timeout now but there was one previously */
+#if DEBUG_UPDATE_TIMER
+ fprintf(stderr, "Curl_update_timer(), remove timeout, "
+ " last_timeout=%ldms\n", multi->last_timeout_ms);
+#endif
+ timeout_ms = -1; /* normalize */
+ set_value = TRUE;
+ }
+ else if(multi->last_timeout_ms < 0) {
+#if DEBUG_UPDATE_TIMER
+ fprintf(stderr, "Curl_update_timer(), had no timeout, set now\n");
+#endif
+ set_value = TRUE;
+ }
+ else if(Curl_timediff_us(multi->last_expire_ts, expire_ts)) {
+ /* We had a timeout before and have one now, the absolute timestamp
+ * differs. The relative timeout_ms may be the same, but the starting
+ * point differs. Let the application restart its timer. */
+#if DEBUG_UPDATE_TIMER
+ fprintf(stderr, "Curl_update_timer(), expire timestamp changed\n");
+#endif
+ set_value = TRUE;
+ }
+ else {
+ /* We have same expire time as previously. Our relative 'timeout_ms'
+ * may be different now, but the application has the timer running
+ * and we do not to tell it to start this again. */
+#if DEBUG_UPDATE_TIMER
+ fprintf(stderr, "Curl_update_timer(), same expire timestamp, no change\n");
+#endif
+ }
- 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;
+ if(set_value) {
+#if DEBUG_UPDATE_TIMER
+ fprintf(stderr, "Curl_update_timer(), set timeout %ldms\n", timeout_ms);
+#endif
+ multi->last_expire_ts = expire_ts;
+ multi->last_timeout_ms = timeout_ms;
+ 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;
}
@@ -3533,13 +3501,13 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi)
static void
multi_deltimeout(struct Curl_easy *data, expire_id eid)
{
- struct Curl_llist_element *e;
+ struct Curl_llist_node *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;
+ for(e = Curl_llist_head(timeoutlist); e; e = Curl_node_next(e)) {
+ struct time_node *n = Curl_node_elem(e);
if(n->eid == eid) {
- Curl_llist_remove(timeoutlist, e, NULL);
+ Curl_node_remove(e);
return;
}
}
@@ -3557,9 +3525,9 @@ multi_addtimeout(struct Curl_easy *data,
struct curltime *stamp,
expire_id eid)
{
- struct Curl_llist_element *e;
+ struct Curl_llist_node *e;
struct time_node *node;
- struct Curl_llist_element *prev = NULL;
+ struct Curl_llist_node *prev = NULL;
size_t n;
struct Curl_llist *timeoutlist = &data->state.timeoutlist;
@@ -3572,8 +3540,8 @@ multi_addtimeout(struct Curl_easy *data,
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;
+ for(e = Curl_llist_head(timeoutlist); e; e = Curl_node_next(e)) {
+ struct time_node *check = Curl_node_elem(e);
timediff_t diff = Curl_timediff(check->time, node->time);
if(diff > 0)
break;
@@ -3599,10 +3567,12 @@ multi_addtimeout(struct Curl_easy *data,
*
* 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)
+static void Curl_expire_ex(struct Curl_easy *data,
+ const struct curltime *nowp,
+ timediff_t milli, expire_id id)
{
struct Curl_multi *multi = data->multi;
- struct curltime *nowp = &data->state.expiretime;
+ struct curltime *curr_expire = &data->state.expiretime;
struct curltime set;
/* this is only interesting while there is still an associated multi struct
@@ -3612,7 +3582,7 @@ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id)
DEBUGASSERT(id < EXPIRE_LAST);
- set = Curl_now();
+ set = *nowp;
set.tv_sec += (time_t)(milli/1000); /* might be a 64 to 32 bits conversion */
set.tv_usec += (int)(milli%1000)*1000;
@@ -3628,11 +3598,11 @@ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id)
in case we need to recompute the minimum timer later. */
multi_addtimeout(data, &set, id);
- if(nowp->tv_sec || nowp->tv_usec) {
+ if(curr_expire->tv_sec || curr_expire->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);
+ timediff_t diff = Curl_timediff(set, *curr_expire);
int rc;
if(diff > 0) {
@@ -3651,12 +3621,18 @@ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id)
/* 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,
+ *curr_expire = set;
+ Curl_splayset(&data->state.timenode, data);
+ multi->timetree = Curl_splayinsert(*curr_expire, multi->timetree,
&data->state.timenode);
}
+void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id)
+{
+ struct curltime now = Curl_now();
+ Curl_expire_ex(data, &now, milli, id);
+}
+
/*
* Curl_expire_done()
*
@@ -3674,7 +3650,7 @@ void Curl_expire_done(struct Curl_easy *data, expire_id id)
*
* Clear ALL timeout values for this handle.
*/
-void Curl_expire_clear(struct Curl_easy *data)
+bool Curl_expire_clear(struct Curl_easy *data)
{
struct Curl_multi *multi = data->multi;
struct curltime *nowp = &data->state.expiretime;
@@ -3682,7 +3658,7 @@ void Curl_expire_clear(struct Curl_easy *data)
/* this is only interesting while there is still an associated multi struct
remaining! */
if(!multi)
- return;
+ return FALSE;
if(nowp->tv_sec || nowp->tv_usec) {
/* Since this is an cleared time, we must remove the previous entry from
@@ -3695,22 +3671,19 @@ void Curl_expire_clear(struct Curl_easy *data)
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);
- }
+ /* clear the timeout list too */
+ Curl_llist_destroy(list, NULL);
#ifdef DEBUGBUILD
infof(data, "Expire cleared");
#endif
nowp->tv_sec = 0;
nowp->tv_usec = 0;
+ return TRUE;
}
+ return FALSE;
}
-
-
-
CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s,
void *hashp)
{
@@ -3726,52 +3699,24 @@ CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s,
return CURLM_OK;
}
-size_t Curl_multi_max_host_connections(struct Curl_multi *multi)
-{
- return multi ? (size_t)multi->max_host_connections : 0;
-}
-
-size_t Curl_multi_max_total_connections(struct Curl_multi *multi)
-{
- return multi ? (size_t)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 move_pending_to_connect(struct Curl_multi *multi,
struct Curl_easy *data)
{
DEBUGASSERT(data->mstate == MSTATE_PENDING);
- /* put it back into the main list */
- link_easy(multi, data);
+ /* Remove this node from the pending list */
+ Curl_node_remove(&data->multi_queue);
- multistate(data, MSTATE_CONNECT);
+ /* put it into the process list */
+ Curl_llist_append(&multi->process, data, &data->multi_queue);
- /* Remove this node from the pending list */
- Curl_llist_remove(&multi->pending, &data->connect_queue, NULL);
+ multistate(data, MSTATE_CONNECT);
/* Make sure that the handle will be processed soonish. */
Curl_expire(data, 0, EXPIRE_RUN_NOW);
}
-/* process_pending_handles() moves a handle from PENDING back into the main
+/* process_pending_handles() moves a handle from PENDING back into the process
list and change state to CONNECT.
We do not move all transfers because that can be a significant amount.
@@ -3787,9 +3732,9 @@ static void move_pending_to_connect(struct Curl_multi *multi,
*/
static void process_pending_handles(struct Curl_multi *multi)
{
- struct Curl_llist_element *e = multi->pending.head;
+ struct Curl_llist_node *e = Curl_llist_head(&multi->pending);
if(e) {
- struct Curl_easy *data = e->ptr;
+ struct Curl_easy *data = Curl_node_elem(e);
move_pending_to_connect(multi, data);
}
}
@@ -3817,12 +3762,12 @@ struct Curl_easy **curl_multi_get_handles(struct Curl_multi *multi)
(multi->num_easy + 1));
if(a) {
unsigned int i = 0;
- struct Curl_easy *e = multi->easyp;
- while(e) {
+ struct Curl_llist_node *e;
+ for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
+ struct Curl_easy *data = Curl_node_elem(e);
DEBUGASSERT(i < multi->num_easy);
- if(!e->state.internal)
- a[i++] = e;
- e = e->next;
+ if(!data->state.internal)
+ a[i++] = data;
}
a[i] = NULL; /* last entry is a NULL */
}
@@ -3945,3 +3890,32 @@ static void multi_xfer_bufs_free(struct Curl_multi *multi)
multi->xfer_ulbuf_len = 0;
multi->xfer_ulbuf_borrowed = FALSE;
}
+
+struct Curl_easy *Curl_multi_get_handle(struct Curl_multi *multi,
+ curl_off_t mid)
+{
+
+ if(mid >= 0) {
+ struct Curl_easy *data;
+ struct Curl_llist_node *e;
+
+ for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
+ data = Curl_node_elem(e);
+ if(data->mid == mid)
+ return data;
+ }
+ /* may be in msgsent queue */
+ for(e = Curl_llist_head(&multi->msgsent); e; e = Curl_node_next(e)) {
+ data = Curl_node_elem(e);
+ if(data->mid == mid)
+ return data;
+ }
+ /* may be in pending queue */
+ for(e = Curl_llist_head(&multi->pending); e; e = Curl_node_next(e)) {
+ data = Curl_node_elem(e);
+ if(data->mid == mid)
+ return data;
+ }
+ }
+ return NULL;
+}
diff --git a/libs/libcurl/src/multihandle.h b/libs/libcurl/src/multihandle.h
index a19c399af7..37a025df21 100644
--- a/libs/libcurl/src/multihandle.h
+++ b/libs/libcurl/src/multihandle.h
@@ -33,7 +33,7 @@
struct connectdata;
struct Curl_message {
- struct Curl_llist_element list;
+ struct Curl_llist_node list;
/* the 'CURLMsg' is the part that is visible to the external user */
struct CURLMsg extmsg;
};
@@ -86,20 +86,17 @@ struct Curl_multi {
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 */
-
unsigned int num_easy; /* amount of entries in the linked list above. */
unsigned 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 */
- struct Curl_llist msgsent; /* Curl_easys that are in the
- MSTATE_MSGSENT state */
+ /* Each added easy handle is added to ONE of these three lists */
+ struct Curl_llist process; /* not in PENDING or MSGSENT */
+ struct Curl_llist pending; /* in PENDING */
+ struct Curl_llist msgsent; /* in MSGSENT */
+ curl_off_t next_easy_mid; /* next multi-id for easy handle added */
/* callback function and user data pointer for the *socket() API */
curl_socket_callback socket_cb;
@@ -141,7 +138,7 @@ struct Curl_multi {
struct Curl_hash proto_hash;
/* Shared connection cache (bundles)*/
- struct conncache conn_cache;
+ struct cpool cpool;
long max_host_connections; /* if >0, a fixed limit of the maximum number
of connections per host */
@@ -154,10 +151,11 @@ struct Curl_multi {
/* 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 */
+ long last_timeout_ms; /* the last timeout value set via timer_cb */
+ struct curltime last_expire_ts; /* timestamp of last expiry */
+
#ifdef USE_WINSOCK
- WSAEVENT wsa_event; /* winsock event used for waits */
+ WSAEVENT wsa_event; /* Winsock event used for waits */
#else
#ifdef ENABLE_WAKEUP
curl_socket_t wakeup_pair[2]; /* eventfd()/pipe()/socketpair() used for
diff --git a/libs/libcurl/src/multiif.h b/libs/libcurl/src/multiif.h
index b528d8b56f..f9d8683595 100644
--- a/libs/libcurl/src/multiif.h
+++ b/libs/libcurl/src/multiif.h
@@ -30,7 +30,7 @@
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);
+bool Curl_expire_clear(struct Curl_easy *data);
void Curl_expire_done(struct Curl_easy *data, expire_id id);
CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT;
void Curl_attach_connection(struct Curl_easy *data,
@@ -63,15 +63,6 @@ struct Curl_multi *Curl_multi_handle(size_t hashsize,
/* mask for checking if read and/or write is set for index x */
#define GETSOCK_MASK_RW(x) (GETSOCK_READSOCK(x)|GETSOCK_WRITESOCK(x))
-/* 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()
*
@@ -153,4 +144,10 @@ CURLcode Curl_multi_xfer_ulbuf_borrow(struct Curl_easy *data,
*/
void Curl_multi_xfer_ulbuf_release(struct Curl_easy *data, char *buf);
+/**
+ * Get the transfer handle for the given id. Returns NULL if not found.
+ */
+struct Curl_easy *Curl_multi_get_handle(struct Curl_multi *multi,
+ curl_off_t id);
+
#endif /* HEADER_CURL_MULTIIF_H */
diff --git a/libs/libcurl/src/netrc.c b/libs/libcurl/src/netrc.c
index c0c7840620..7183fcb0c0 100644
--- a/libs/libcurl/src/netrc.c
+++ b/libs/libcurl/src/netrc.c
@@ -324,7 +324,7 @@ int Curl_parsenetrc(const char *host, char **loginp, char **passwordp,
return retcode; /* no home directory found (or possibly out of
memory) */
- filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR);
+ filealloc = aprintf("%s%s.netrc", home, DIR_CHAR);
if(!filealloc) {
free(homea);
return -1;
@@ -334,7 +334,7 @@ int Curl_parsenetrc(const char *host, char **loginp, char **passwordp,
#ifdef _WIN32
if(retcode == NETRC_FILE_MISSING) {
/* fallback to the old-style "_netrc" file */
- filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR);
+ filealloc = aprintf("%s%s_netrc", home, DIR_CHAR);
if(!filealloc) {
free(homea);
return -1;
diff --git a/libs/libcurl/src/nonblock.c b/libs/libcurl/src/nonblock.c
index 4b1268aa98..c4d745f543 100644
--- a/libs/libcurl/src/nonblock.c
+++ b/libs/libcurl/src/nonblock.c
@@ -47,7 +47,7 @@ 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 */
+ /* most recent Unix versions */
int flags;
flags = sfcntl(sockfd, F_GETFL, 0);
if(flags < 0)
@@ -65,7 +65,7 @@ int curlx_nonblock(curl_socket_t sockfd, /* operate on this */
#elif defined(HAVE_IOCTL_FIONBIO)
- /* older unix versions */
+ /* older Unix versions */
int flags = nonblock ? 1 : 0;
return ioctl(sockfd, FIONBIO, &flags);
diff --git a/libs/libcurl/src/openldap.c b/libs/libcurl/src/openldap.c
index f992d994fc..5feb3813d4 100644
--- a/libs/libcurl/src/openldap.c
+++ b/libs/libcurl/src/openldap.c
@@ -1201,7 +1201,7 @@ ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
if(conn) {
struct ldapconninfo *li = conn->proto.ldapc;
CURLcode err = CURLE_SEND_ERROR;
- ret = (li->send)(data, FIRSTSOCKET, buf, len, &err);
+ ret = (li->send)(data, FIRSTSOCKET, buf, len, FALSE, &err);
if(ret < 0 && err == CURLE_AGAIN) {
SET_SOCKERRNO(EWOULDBLOCK);
}
diff --git a/libs/libcurl/src/optiontable.pl b/libs/libcurl/src/optiontable.pl
new file mode 100644
index 0000000000..ec229b2392
--- /dev/null
+++ b/libs/libcurl/src/optiontable.pl
@@ -0,0 +1,152 @@
+#!/usr/bin/env perl
+
+print <<HEAD
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \\| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \\___|\\___/|_| \\_\\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel\@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+/* This 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[] = {
+HEAD
+ ;
+
+my $lastnum=0;
+
+sub add {
+ my($opt, $type, $num)=@_;
+ my $name;
+ # remove all spaces from the type
+ $type =~ s/ //g;
+ my $ext = $type;
+
+ if($opt =~ /OBSOLETE/) {
+ # skip obsolete options
+ next;
+ }
+
+ if($opt =~ /^CURLOPT_(.*)/) {
+ $name=$1;
+ }
+ $ext =~ s/CURLOPTTYPE_//;
+ $ext =~ s/CBPOINT/CBPTR/;
+ $ext =~ s/POINT\z//;
+ $type = "CURLOT_$ext";
+
+ $opt{$name} = $opt;
+ $type{$name} = $type;
+ push @names, $name;
+ if($num < $lastnum) {
+ print STDERR "ERROR: $opt has bad number: $num < $lastnum\n";
+ exit 2;
+ }
+ else {
+ $lastnum = $num;
+ }
+}
+
+
+my $fl;
+while(<STDIN>) {
+ my $l = $_;
+ if($fl) {
+ # continued deprecation
+ if($l =~ /(.*)\),/) {
+ $fl .= $1;
+
+ # the end
+ my @p=split(/, */, $fl);
+ add($p[0], $p[1], $p[2]);
+ undef $fl;
+ }
+ else {
+ # another line to append
+ chomp $l;
+ $fl .= $l;
+ }
+ }
+
+ if(/^ *CURLOPTDEPRECATED\((.*)/) {
+ $fl = $1;
+ chomp $fl;
+ }
+
+ if(/^ *CURLOPT\(([^,]*), ([^,]*), (\d+)\)/) {
+ my($opt, $type, $num)=($1,$2,$3);
+ add($opt, $type, $num);
+ }
+
+ # alias for an older option
+ # old = new
+ if(/^#define (CURLOPT_[^ ]*) *(CURLOPT_\S*)/) {
+ my ($o, $n)=($1, $2);
+ # skip obsolete ones
+ if(($n !~ /OBSOLETE/) && ($o !~ /OBSOLETE/)) {
+ $o =~ s/^CURLOPT_//;
+ $n =~ s/^CURLOPT_//;
+ $alias{$o} = $n;
+ push @names, $o,
+ }
+ }
+}
+
+
+for my $name (sort @names) {
+ my $oname = $name;
+ my $a = $alias{$name};
+ my $flag = "0";
+ if($a) {
+ $name = $alias{$name};
+ $flag = "CURLOT_FLAG_ALIAS";
+ }
+ $o = sprintf(" {\"%s\", %s, %s, %s},\n",
+ $oname, $opt{$name}, $type{$name}, $flag);
+ if(length($o) < 80) {
+ print $o;
+ }
+ else {
+ printf(" {\"%s\", %s,\n %s, %s},\n",
+ $oname, $opt{$name}, $type{$name}, $flag);
+ }
+}
+
+print <<FOOT
+ {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) != ($lastnum + 1));
+}
+#endif
+FOOT
+ ;
diff --git a/libs/libcurl/src/pingpong.c b/libs/libcurl/src/pingpong.c
index 809d519992..a484c6d17f 100644
--- a/libs/libcurl/src/pingpong.c
+++ b/libs/libcurl/src/pingpong.c
@@ -199,7 +199,8 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data,
#ifdef HAVE_GSSAPI
conn->data_prot = PROT_CMD;
#endif
- result = Curl_conn_send(data, FIRSTSOCKET, s, write_len, &bytes_written);
+ result = Curl_conn_send(data, FIRSTSOCKET, s, write_len, FALSE,
+ &bytes_written);
if(result == CURLE_AGAIN) {
bytes_written = 0;
}
@@ -285,94 +286,99 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
{
struct connectdata *conn = data->conn;
CURLcode result = CURLE_OK;
+ ssize_t gotbytes;
+ char buffer[900];
*code = 0; /* 0 for errors or not done */
*size = 0;
- if(pp->nfinal) {
- /* a previous call left this many bytes in the beginning of the buffer as
- that was the final line; now ditch that */
- size_t full = Curl_dyn_len(&pp->recvbuf);
+ do {
+ gotbytes = 0;
+ if(pp->nfinal) {
+ /* a previous call left this many bytes in the beginning of the buffer as
+ that was the final line; now ditch that */
+ size_t full = Curl_dyn_len(&pp->recvbuf);
- /* trim off the "final" leading part */
- Curl_dyn_tail(&pp->recvbuf, full - pp->nfinal);
+ /* trim off the "final" leading part */
+ Curl_dyn_tail(&pp->recvbuf, full - pp->nfinal);
- pp->nfinal = 0; /* now gone */
- }
- if(!pp->overflow) {
- ssize_t gotbytes = 0;
- char buffer[900];
+ pp->nfinal = 0; /* now gone */
+ }
+ if(!pp->overflow) {
+ result = pingpong_read(data, sockindex, buffer, sizeof(buffer),
+ &gotbytes);
+ if(result == CURLE_AGAIN)
+ return CURLE_OK;
- result = pingpong_read(data, sockindex, buffer, sizeof(buffer), &gotbytes);
- if(result == CURLE_AGAIN)
- return CURLE_OK;
+ if(result)
+ return result;
- if(result)
- return result;
+ if(gotbytes <= 0) {
+ failf(data, "response reading failed (errno: %d)", SOCKERRNO);
+ return CURLE_RECV_ERROR;
+ }
- if(gotbytes <= 0) {
- failf(data, "response reading failed (errno: %d)", SOCKERRNO);
- return CURLE_RECV_ERROR;
- }
+ result = Curl_dyn_addn(&pp->recvbuf, buffer, gotbytes);
+ if(result)
+ return result;
- result = Curl_dyn_addn(&pp->recvbuf, buffer, gotbytes);
- if(result)
- return result;
+ data->req.headerbytecount += (unsigned int)gotbytes;
- data->req.headerbytecount += (unsigned int)gotbytes;
+ pp->nread_resp += gotbytes;
+ }
- pp->nread_resp += gotbytes;
- }
+ do {
+ char *line = Curl_dyn_ptr(&pp->recvbuf);
+ char *nl = memchr(line, '\n', Curl_dyn_len(&pp->recvbuf));
+ if(nl) {
+ /* a newline is CRLF in pp-talk, so the CR is ignored as
+ the line is not really terminated until the LF comes */
+ size_t length = nl - line + 1;
- do {
- char *line = Curl_dyn_ptr(&pp->recvbuf);
- char *nl = memchr(line, '\n', Curl_dyn_len(&pp->recvbuf));
- if(nl) {
- /* a newline is CRLF in pp-talk, so the CR is ignored as
- the line is not really terminated until the LF comes */
- size_t length = nl - line + 1;
-
- /* output debug output if that is requested */
+ /* output debug output if that is requested */
#ifdef HAVE_GSSAPI
- if(!conn->sec_complete)
+ if(!conn->sec_complete)
#endif
- Curl_debug(data, CURLINFO_HEADER_IN, line, length);
-
- /*
- * 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_INFO, line, length);
- if(result)
- return result;
-
- if(pp->endofresp(data, conn, line, length, code)) {
- /* When at "end of response", keep the endofresp line first in the
- buffer since it will be accessed outside (by pingpong
- parsers). Store the overflow counter to inform about additional
- data in this buffer after the endofresp line. */
- pp->nfinal = length;
+ Curl_debug(data, CURLINFO_HEADER_IN, line, length);
+
+ /*
+ * 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_INFO, line, length);
+ if(result)
+ return result;
+
+ if(pp->endofresp(data, conn, line, length, code)) {
+ /* When at "end of response", keep the endofresp line first in the
+ buffer since it will be accessed outside (by pingpong
+ parsers). Store the overflow counter to inform about additional
+ data in this buffer after the endofresp line. */
+ pp->nfinal = length;
+ if(Curl_dyn_len(&pp->recvbuf) > length)
+ pp->overflow = Curl_dyn_len(&pp->recvbuf) - length;
+ else
+ pp->overflow = 0;
+ *size = pp->nread_resp; /* size of the response */
+ pp->nread_resp = 0; /* restart */
+ gotbytes = 0; /* force break out of outer loop */
+ break;
+ }
if(Curl_dyn_len(&pp->recvbuf) > length)
- pp->overflow = Curl_dyn_len(&pp->recvbuf) - length;
+ /* keep the remaining piece */
+ Curl_dyn_tail((&pp->recvbuf), Curl_dyn_len(&pp->recvbuf) - length);
else
- pp->overflow = 0;
- *size = pp->nread_resp; /* size of the response */
- pp->nread_resp = 0; /* restart */
+ Curl_dyn_reset(&pp->recvbuf);
+ }
+ else {
+ /* without a newline, there is no overflow */
+ pp->overflow = 0;
break;
}
- if(Curl_dyn_len(&pp->recvbuf) > length)
- /* keep the remaining piece */
- Curl_dyn_tail((&pp->recvbuf), Curl_dyn_len(&pp->recvbuf) - length);
- else
- Curl_dyn_reset(&pp->recvbuf);
- }
- else {
- /* without a newline, there is no overflow */
- pp->overflow = 0;
- break;
- }
- } while(1); /* while there is buffer left to scan */
+ } while(1); /* while there is buffer left to scan */
+
+ } while(gotbytes == sizeof(buffer));
pp->pending_resp = FALSE;
@@ -394,6 +400,13 @@ int Curl_pp_getsock(struct Curl_easy *data,
return GETSOCK_READSOCK(0);
}
+bool Curl_pp_needs_flush(struct Curl_easy *data,
+ struct pingpong *pp)
+{
+ (void)data;
+ return pp->sendleft > 0;
+}
+
CURLcode Curl_pp_flushsend(struct Curl_easy *data,
struct pingpong *pp)
{
@@ -401,9 +414,12 @@ CURLcode Curl_pp_flushsend(struct Curl_easy *data,
size_t written;
CURLcode result;
+ if(!Curl_pp_needs_flush(data, pp))
+ return CURLE_OK;
+
result = Curl_conn_send(data, FIRSTSOCKET,
pp->sendthis + pp->sendsize - pp->sendleft,
- pp->sendleft, &written);
+ pp->sendleft, FALSE, &written);
if(result == CURLE_AGAIN) {
result = CURLE_OK;
written = 0;
diff --git a/libs/libcurl/src/pingpong.h b/libs/libcurl/src/pingpong.h
index 4b096c250d..ffd1fd7741 100644
--- a/libs/libcurl/src/pingpong.h
+++ b/libs/libcurl/src/pingpong.h
@@ -137,6 +137,8 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
int *code, /* return the server code if done */
size_t *size); /* size of the response */
+bool Curl_pp_needs_flush(struct Curl_easy *data,
+ struct pingpong *pp);
CURLcode Curl_pp_flushsend(struct Curl_easy *data,
struct pingpong *pp);
diff --git a/libs/libcurl/src/pop3.c b/libs/libcurl/src/pop3.c
index 5a9dfbe0d9..e6408f05e8 100644
--- a/libs/libcurl/src/pop3.c
+++ b/libs/libcurl/src/pop3.c
@@ -83,6 +83,10 @@
#include "curl_memory.h"
#include "memdebug.h"
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
+
/* Local API functions */
static CURLcode pop3_regular_transfer(struct Curl_easy *data, bool *done);
static CURLcode pop3_do(struct Curl_easy *data, bool *done);
@@ -107,6 +111,11 @@ static CURLcode pop3_continue_auth(struct Curl_easy *data, const char *mech,
static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech);
static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out);
+/* This function scans the body after the end-of-body and writes everything
+ * until the end is found */
+static CURLcode pop3_write(struct Curl_easy *data,
+ const char *str, size_t nread, bool is_eos);
+
/*
* POP3 protocol handler.
*/
@@ -125,7 +134,7 @@ const struct Curl_handler Curl_handler_pop3 = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
pop3_disconnect, /* disconnect */
- ZERO_NULL, /* write_resp */
+ pop3_write, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
@@ -155,7 +164,7 @@ const struct Curl_handler Curl_handler_pop3s = {
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
pop3_disconnect, /* disconnect */
- ZERO_NULL, /* write_resp */
+ pop3_write, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
@@ -194,6 +203,53 @@ static void pop3_to_pop3s(struct connectdata *conn)
#define pop3_to_pop3s(x) Curl_nop_stmt
#endif
+struct pop3_cmd {
+ const char *name;
+ unsigned short nlen;
+ BIT(multiline); /* response is multi-line with last '.' line */
+ BIT(multiline_with_args); /* is multi-line when command has args */
+};
+
+static const struct pop3_cmd pop3cmds[] = {
+ { "APOP", 4, FALSE, FALSE },
+ { "AUTH", 4, FALSE, FALSE },
+ { "CAPA", 4, TRUE, TRUE },
+ { "DELE", 4, FALSE, FALSE },
+ { "LIST", 4, TRUE, FALSE },
+ { "MSG", 3, TRUE, TRUE },
+ { "NOOP", 4, FALSE, FALSE },
+ { "PASS", 4, FALSE, FALSE },
+ { "QUIT", 4, FALSE, FALSE },
+ { "RETR", 4, TRUE, TRUE },
+ { "RSET", 4, FALSE, FALSE },
+ { "STAT", 4, FALSE, FALSE },
+ { "STLS", 4, FALSE, FALSE },
+ { "TOP", 3, TRUE, TRUE },
+ { "UIDL", 4, TRUE, FALSE },
+ { "USER", 4, FALSE, FALSE },
+ { "UTF8", 4, FALSE, FALSE },
+ { "XTND", 4, TRUE, TRUE },
+};
+
+/* Return iff a command is defined as "multi-line" (RFC 1939),
+ * has a response terminated by a last line with a '.'.
+ */
+static bool pop3_is_multiline(const char *cmdline)
+{
+ size_t i;
+ for(i = 0; i < ARRAYSIZE(pop3cmds); ++i) {
+ if(strncasecompare(pop3cmds[i].name, cmdline, pop3cmds[i].nlen)) {
+ if(!cmdline[pop3cmds[i].nlen])
+ return pop3cmds[i].multiline;
+ else if(cmdline[pop3cmds[i].nlen] == ' ')
+ return pop3cmds[i].multiline_with_args;
+ }
+ }
+ /* Unknown command, assume multi-line for backward compatibility with
+ * earlier curl versions that only could do multi-line responses. */
+ return TRUE;
+}
+
/***********************************************************************
*
* pop3_endofresp()
@@ -609,18 +665,20 @@ static CURLcode pop3_perform_command(struct Curl_easy *data)
else
command = "RETR";
+ if(pop3->custom && pop3->custom[0] != '\0')
+ command = pop3->custom;
+
/* 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);
+ command, pop3->id);
else
- result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s",
- (pop3->custom && pop3->custom[0] != '\0' ?
- pop3->custom : command));
+ result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", command);
- if(!result)
+ if(!result) {
pop3_state(data, POP3_COMMAND);
+ data->req.no_body = !pop3_is_multiline(command);
+ }
return result;
}
@@ -948,8 +1006,8 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data,
pp->nfinal = 0; /* done */
if(!data->req.no_body) {
- result = Curl_pop3_write(data, Curl_dyn_ptr(&pp->recvbuf),
- Curl_dyn_len(&pp->recvbuf));
+ result = pop3_write(data, Curl_dyn_ptr(&pp->recvbuf),
+ Curl_dyn_len(&pp->recvbuf), FALSE);
if(result)
return result;
}
@@ -1447,12 +1505,13 @@ static CURLcode pop3_parse_custom_request(struct Curl_easy *data)
/***********************************************************************
*
- * Curl_pop3_write()
+ * 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, const char *str, size_t nread)
+static CURLcode pop3_write(struct Curl_easy *data, const char *str,
+ size_t nread, bool is_eos)
{
/* This code could be made into a special function in the handler struct */
CURLcode result = CURLE_OK;
@@ -1462,6 +1521,7 @@ CURLcode Curl_pop3_write(struct Curl_easy *data, const char *str, size_t nread)
bool strip_dot = FALSE;
size_t last = 0;
size_t i;
+ (void)is_eos;
/* 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
diff --git a/libs/libcurl/src/pop3.h b/libs/libcurl/src/pop3.h
index 408bd0233c..ce0d5f6dc2 100644
--- a/libs/libcurl/src/pop3.h
+++ b/libs/libcurl/src/pop3.h
@@ -90,9 +90,4 @@ extern const struct Curl_handler Curl_handler_pop3s;
#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,
- const 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 4e6d71f9b7..22ffea73d4 100644
--- a/libs/libcurl/src/progress.c
+++ b/libs/libcurl/src/progress.c
@@ -48,8 +48,7 @@ static void time2str(char *r, curl_off_t seconds)
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);
+ msnprintf(r, 9, "%2" FMT_OFF_T ":%02" FMT_OFF_T ":%02" FMT_OFF_T, h, m, s);
}
else {
/* this equals to more than 99 hours, switch to a more suitable output
@@ -57,10 +56,9 @@ static void time2str(char *r, curl_off_t seconds)
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);
+ msnprintf(r, 9, "%3" FMT_OFF_T "d %02" FMT_OFF_T "h", d, h);
else
- msnprintf(r, 9, "%7" CURL_FORMAT_CURL_OFF_T "d", d);
+ msnprintf(r, 9, "%7" FMT_OFF_T "d", d);
}
}
@@ -76,38 +74,38 @@ static char *max5data(curl_off_t bytes, char *max5)
#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);
+ msnprintf(max5, 6, "%5" FMT_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);
+ msnprintf(max5, 6, "%4" FMT_OFF_T "k", bytes/ONE_KILOBYTE);
else if(bytes < CURL_OFF_T_C(100) * ONE_MEGABYTE)
/* 'XX.XM' is good as long as we are less than 100 megs */
- msnprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0"
- CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE,
+ msnprintf(max5, 6, "%2" FMT_OFF_T ".%0"
+ FMT_OFF_T "M", bytes/ONE_MEGABYTE,
(bytes%ONE_MEGABYTE) / (ONE_MEGABYTE/CURL_OFF_T_C(10)) );
else if(bytes < CURL_OFF_T_C(10000) * ONE_MEGABYTE)
/* 'XXXXM' is good until we are at 10000MB or above */
- msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE);
+ msnprintf(max5, 6, "%4" FMT_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,
+ msnprintf(max5, 6, "%2" FMT_OFF_T ".%0"
+ FMT_OFF_T "G", bytes/ONE_GIGABYTE,
(bytes%ONE_GIGABYTE) / (ONE_GIGABYTE/CURL_OFF_T_C(10)) );
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);
+ msnprintf(max5, 6, "%4" FMT_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);
+ msnprintf(max5, 6, "%4" FMT_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);
+ msnprintf(max5, 6, "%4" FMT_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. */
@@ -217,7 +215,7 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer,
break;
}
case TIMER_POSTRANSFER:
- /* this is the normal end-of-transfer thing */
+ delta = &data->progress.t_posttransfer;
break;
case TIMER_REDIRECT:
data->progress.t_redirect = Curl_timediff_us(timestamp,
@@ -252,12 +250,12 @@ 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;
+ data->progress.ul.limit.start = data->progress.start;
+ data->progress.dl.limit.start = data->progress.start;
+ data->progress.ul.limit.start_size = 0;
+ data->progress.dl.limit.start_size = 0;
+ data->progress.dl.cur_size = 0;
+ data->progress.ul.cur_size = 0;
/* clear all bits except HIDE and HEADERS_OUT */
data->progress.flags &= PGRS_HIDE|PGRS_HEADERS_OUT;
Curl_ratelimit(data, data->progress.start);
@@ -281,17 +279,15 @@ void Curl_pgrsStartNow(struct Curl_easy *data)
* 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,
+timediff_t Curl_pgrsLimitWaitTime(struct pgrs_dir *d,
+ curl_off_t speed_limit,
struct curltime now)
{
- curl_off_t size = cursize - startsize;
+ curl_off_t size = d->cur_size - d->limit.start_size;
timediff_t minimum;
timediff_t actual;
- if(!limit || !size)
+ if(!speed_limit || !size)
return 0;
/*
@@ -299,9 +295,9 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize,
* stay below 'limit'.
*/
if(size < CURL_OFF_T_MAX/1000)
- minimum = (timediff_t) (CURL_OFF_T_C(1000) * size / limit);
+ minimum = (timediff_t) (CURL_OFF_T_C(1000) * size / speed_limit);
else {
- minimum = (timediff_t) (size / limit);
+ minimum = (timediff_t) (size / speed_limit);
if(minimum < TIMEDIFF_T_MAX/1000)
minimum *= 1000;
else
@@ -312,7 +308,7 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize,
* 'actual' is the time in milliseconds it took to actually download the
* last 'size' bytes.
*/
- actual = Curl_timediff_ceil(now, start);
+ actual = Curl_timediff_ceil(now, d->limit.start);
if(actual < minimum) {
/* if it downloaded the data faster than the limit, make it wait the
difference */
@@ -327,7 +323,7 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize,
*/
CURLcode Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size)
{
- data->progress.downloaded = size;
+ data->progress.dl.cur_size = size;
return CURLE_OK;
}
@@ -338,17 +334,17 @@ void Curl_ratelimit(struct Curl_easy *data, struct curltime now)
{
/* do not 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) >=
+ 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;
+ data->progress.dl.limit.start = now;
+ data->progress.dl.limit.start_size = data->progress.dl.cur_size;
}
}
if(data->set.max_send_speed) {
- if(Curl_timediff(now, data->progress.ul_limit_start) >=
+ 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;
+ data->progress.ul.limit.start = now;
+ data->progress.ul.limit.start_size = data->progress.ul.cur_size;
}
}
}
@@ -358,17 +354,17 @@ void Curl_ratelimit(struct Curl_easy *data, struct curltime now)
*/
void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size)
{
- data->progress.uploaded = size;
+ data->progress.ul.cur_size = size;
}
void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size)
{
if(size >= 0) {
- data->progress.size_dl = size;
+ data->progress.dl.total_size = size;
data->progress.flags |= PGRS_DL_SIZE_KNOWN;
}
else {
- data->progress.size_dl = 0;
+ data->progress.dl.total_size = 0;
data->progress.flags &= ~PGRS_DL_SIZE_KNOWN;
}
}
@@ -376,11 +372,11 @@ void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size)
void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size)
{
if(size >= 0) {
- data->progress.size_ul = size;
+ data->progress.ul.total_size = size;
data->progress.flags |= PGRS_UL_SIZE_KNOWN;
}
else {
- data->progress.size_ul = 0;
+ data->progress.ul.total_size = 0;
data->progress.flags &= ~PGRS_UL_SIZE_KNOWN;
}
}
@@ -407,8 +403,8 @@ static bool progress_calc(struct Curl_easy *data, struct curltime now)
/* 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);
+ p->dl.speed = trspeed(p->dl.cur_size, p->timespent);
+ p->ul.speed = trspeed(p->ul.cur_size, p->timespent);
/* Calculations done at most once a second, unless end is reached */
if(p->lastshow != now.tv_sec) {
@@ -419,7 +415,7 @@ static bool progress_calc(struct Curl_easy *data, struct curltime now)
/* 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;
+ p->speeder[ nowindex ] = p->dl.cur_size + p->ul.cur_size;
/* remember the exact time for this moment */
p->speeder_time [ nowindex ] = now;
@@ -465,113 +461,107 @@ static bool progress_calc(struct Curl_easy *data, struct curltime now)
}
else
/* the first second we use the average */
- p->current_speed = p->ulspeed + p->dlspeed;
+ p->current_speed = p->ul.speed + p->dl.speed;
} /* Calculations end */
return timetoshow;
}
#ifndef CURL_DISABLE_PROGRESS_METER
+
+struct pgrs_estimate {
+ curl_off_t secs;
+ curl_off_t percent;
+};
+
+static curl_off_t pgrs_est_percent(curl_off_t total, curl_off_t cur)
+{
+ if(total > CURL_OFF_T_C(10000))
+ return cur / (total/CURL_OFF_T_C(100));
+ else if(total > CURL_OFF_T_C(0))
+ return (cur*100) / total;
+ return 0;
+}
+
+static void pgrs_estimates(struct pgrs_dir *d,
+ bool total_known,
+ struct pgrs_estimate *est)
+{
+ est->secs = 0;
+ est->percent = 0;
+ if(total_known && (d->speed > CURL_OFF_T_C(0))) {
+ est->secs = d->total_size / d->speed;
+ est->percent = pgrs_est_percent(d->total_size, d->cur_size);
+ }
+}
+
static void progress_meter(struct Curl_easy *data)
{
+ struct Progress *p = &data->progress;
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;
+ struct pgrs_estimate dl_estm;
+ struct pgrs_estimate ul_estm;
+ struct pgrs_estimate total_estm;
+ curl_off_t total_cur_size;
+ curl_off_t total_expected_size;
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 */
+ curl_off_t cur_secs = (curl_off_t)p->timespent/1000000; /* seconds */
- if(!(data->progress.flags & PGRS_HEADERS_OUT)) {
+ if(!(p->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);
+ "** Resuming transfer from byte position %" FMT_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;
+ p->flags |= PGRS_HEADERS_OUT; /* headers are shown */
}
- /* Now figure out which of them is slower and use that one for the
- total estimate! */
- total_estimate = ulestimate>dlestimate?ulestimate:dlestimate;
+ /* Figure out the estimated time of arrival for upload and download */
+ pgrs_estimates(&p->ul, (p->flags & PGRS_UL_SIZE_KNOWN), &ul_estm);
+ pgrs_estimates(&p->dl, (p->flags & PGRS_DL_SIZE_KNOWN), &dl_estm);
+ /* Since both happen at the same time, total expected duration is max. */
+ total_estm.secs = CURLMAX(ul_estm.secs, dl_estm.secs);
/* create the three time strings */
- time2str(time_left, total_estimate > 0?(total_estimate - timespent):0);
- time2str(time_total, total_estimate);
- time2str(time_spent, timespent);
+ time2str(time_left, total_estm.secs > 0?(total_estm.secs - cur_secs):0);
+ time2str(time_total, total_estm.secs);
+ time2str(time_spent, cur_secs);
/* 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);
+ total_expected_size =
+ ((p->flags & PGRS_UL_SIZE_KNOWN)? p->ul.total_size:p->ul.cur_size) +
+ ((p->flags & PGRS_DL_SIZE_KNOWN)? p->dl.total_size:p->dl.cur_size);
/* We have transferred this much so far */
- total_transfer = data->progress.downloaded + data->progress.uploaded;
+ total_cur_size = p->dl.cur_size + p->ul.cur_size;
/* 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;
+ total_estm.percent = pgrs_est_percent(total_expected_size, total_cur_size);
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 */
+ "%3" FMT_OFF_T " %s "
+ "%3" FMT_OFF_T " %s "
+ "%3" FMT_OFF_T " %s %s %s %s %s %s %s",
+ total_estm.percent, /* 3 letters */ /* total % */
+ max5data(total_expected_size, max5[2]), /* total size */
+ dl_estm.percent, /* 3 letters */ /* rcvd % */
+ max5data(p->dl.cur_size, max5[0]), /* rcvd size */
+ ul_estm.percent, /* 3 letters */ /* xfer % */
+ max5data(p->ul.cur_size, max5[1]), /* xfer size */
+ max5data(p->dl.speed, max5[3]), /* avrg dl speed */
+ max5data(p->ul.speed, 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])
+ max5data(p->current_speed, max5[5])
);
/* we flush the output stream to make it appear as soon as possible */
@@ -595,10 +585,10 @@ static int pgrsupdate(struct Curl_easy *data, bool showprogress)
/* There is 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);
+ data->progress.dl.total_size,
+ data->progress.dl.cur_size,
+ data->progress.ul.total_size,
+ data->progress.ul.cur_size);
Curl_set_in_callback(data, false);
if(result != CURL_PROGRESSFUNC_CONTINUE) {
if(result)
@@ -611,10 +601,10 @@ static int pgrsupdate(struct Curl_easy *data, bool showprogress)
/* 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);
+ (double)data->progress.dl.total_size,
+ (double)data->progress.dl.cur_size,
+ (double)data->progress.ul.total_size,
+ (double)data->progress.ul.cur_size);
Curl_set_in_callback(data, false);
if(result != CURL_PROGRESSFUNC_CONTINUE) {
if(result)
diff --git a/libs/libcurl/src/progress.h b/libs/libcurl/src/progress.h
index c2b90146d7..51babd8721 100644
--- a/libs/libcurl/src/progress.h
+++ b/libs/libcurl/src/progress.h
@@ -58,10 +58,8 @@ void Curl_pgrsUpdate_nometer(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,
+timediff_t Curl_pgrsLimitWaitTime(struct pgrs_dir *d,
+ curl_off_t speed_limit,
struct curltime now);
/**
* Update progress timer with the elapsed time from its start to `timestamp`.
diff --git a/libs/libcurl/src/rand.c b/libs/libcurl/src/rand.c
index 772462513f..8cfd7d4a7e 100644
--- a/libs/libcurl/src/rand.c
+++ b/libs/libcurl/src/rand.c
@@ -100,86 +100,91 @@ CURLcode Curl_win32_random(unsigned char *entropy, size_t length)
}
#endif
-static CURLcode randit(struct Curl_easy *data, unsigned int *rnd)
+#if !defined(USE_SSL) || defined(USE_RUSTLS)
+/* ---- possibly non-cryptographic version following ---- */
+CURLcode Curl_weak_random(struct Curl_easy *data,
+ unsigned char *entropy,
+ size_t length) /* always 4, size of int */
{
- CURLcode result = CURLE_OK;
- static unsigned int randseed;
- static bool seeded = FALSE;
-
-#ifdef DEBUGBUILD
- 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 ---- */
+ unsigned int r;
+ DEBUGASSERT(length == sizeof(int));
+ /* Trying cryptographically secure functions first */
#ifdef _WIN32
- if(!seeded) {
- result = Curl_win32_random((unsigned char *)rnd, sizeof(*rnd));
+ (void)data;
+ {
+ CURLcode result = Curl_win32_random(entropy, length);
if(result != CURLE_NOT_BUILT_IN)
return result;
}
#endif
-#if defined(HAVE_ARC4RANDOM) && !defined(USE_OPENSSL)
- if(!seeded) {
- *rnd = (unsigned int)arc4random();
- return CURLE_OK;
+#if defined(HAVE_ARC4RANDOM)
+ (void)data;
+ r = (unsigned int)arc4random();
+ memcpy(entropy, &r, length);
+#else
+ infof(data, "WARNING: using weak random seed");
+ {
+ static unsigned int randseed;
+ static bool seeded = FALSE;
+ unsigned int rnd;
+ if(!seeded) {
+ struct curltime now = Curl_now();
+ 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);
+ memcpy(entropy, &rnd, length);
}
#endif
+ return CURLE_OK;
+}
+#endif
+
+#ifdef USE_SSL
+#define _random(x,y,z) Curl_ssl_random(x,y,z)
+#else
+#define _random(x,y,z) Curl_weak_random(x,y,z)
+#endif
-#if defined(RANDOM_FILE) && !defined(_WIN32)
- if(!seeded) {
- /* if there is 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))
+static CURLcode randit(struct Curl_easy *data, unsigned int *rnd,
+ bool env_override)
+{
+#ifdef DEBUGBUILD
+ if(env_override) {
+ char *force_entropy = getenv("CURL_ENTROPY");
+ if(force_entropy) {
+ static unsigned int randseed;
+ static bool seeded = FALSE;
+
+ 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;
- close(fd);
+ }
+ else
+ randseed++;
+ *rnd = randseed;
+ return CURLE_OK;
}
}
+#else
+ (void)env_override;
#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;
- }
-
- {
- unsigned int r;
- /* Return an unsigned 32-bit pseudo-random number. */
- r = randseed = randseed * 1103515245 + 12345;
- *rnd = (r << 16) | ((r >> 16) & 0xFFFF);
- }
- return CURLE_OK;
+ /* data may be NULL! */
+ return _random(data, (unsigned char *)rnd, sizeof(*rnd));
}
/*
@@ -187,7 +192,7 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd)
* 'rnd' points to.
*
* If libcurl is built without TLS support or with a TLS backend that lacks a
- * proper random API (rustls or mbedTLS), this function will use "weak"
+ * proper random API (Rustls or mbedTLS), this function will use "weak"
* random.
*
* When built *with* TLS support and a backend that offers strong random, it
@@ -198,9 +203,16 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd)
*
*/
-CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num)
+CURLcode Curl_rand_bytes(struct Curl_easy *data,
+#ifdef DEBUGBUILD
+ bool env_override,
+#endif
+ unsigned char *rnd, size_t num)
{
CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT;
+#ifndef DEBUGBUILD
+ const bool env_override = FALSE;
+#endif
DEBUGASSERT(num);
@@ -208,7 +220,7 @@ CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num)
unsigned int r;
size_t left = num < sizeof(unsigned int) ? num : sizeof(unsigned int);
- result = randit(data, &r);
+ result = randit(data, &r, env_override);
if(result)
return result;
@@ -278,7 +290,7 @@ CURLcode Curl_rand_alnum(struct Curl_easy *data, unsigned char *rnd,
while(num) {
do {
- result = randit(data, &r);
+ result = randit(data, &r, TRUE);
if(result)
return result;
} while(r >= (UINT_MAX - UINT_MAX % alnumspace));
diff --git a/libs/libcurl/src/rand.h b/libs/libcurl/src/rand.h
index 5984de9b38..8a0c754d64 100644
--- a/libs/libcurl/src/rand.h
+++ b/libs/libcurl/src/rand.h
@@ -24,7 +24,22 @@
*
***************************************************************************/
-CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num);
+CURLcode Curl_rand_bytes(struct Curl_easy *data,
+#ifdef DEBUGBUILD
+ bool allow_env_override,
+#endif
+ unsigned char *rnd, size_t num);
+
+#ifdef DEBUGBUILD
+#define Curl_rand(a,b,c) Curl_rand_bytes((a), TRUE, (b), (c))
+#else
+#define Curl_rand(a,b,c) Curl_rand_bytes((a), (b), (c))
+#endif
+
+/* ---- non-cryptographic version following ---- */
+CURLcode Curl_weak_random(struct Curl_easy *data,
+ unsigned char *rnd,
+ size_t length);
/*
* Curl_rand_hex() fills the 'rnd' buffer with a given 'num' size with random
diff --git a/libs/libcurl/src/request.c b/libs/libcurl/src/request.c
index 45812c113a..011e8233c4 100644
--- a/libs/libcurl/src/request.c
+++ b/libs/libcurl/src/request.c
@@ -100,6 +100,9 @@ CURLcode Curl_req_done(struct SingleRequest *req,
if(!aborted)
(void)req_flush(data);
Curl_client_reset(data);
+#ifndef CURL_DISABLE_DOH
+ Curl_doh_close(data);
+#endif
return CURLE_OK;
}
@@ -179,18 +182,20 @@ static CURLcode xfer_send(struct Curl_easy *data,
size_t hds_len, size_t *pnwritten)
{
CURLcode result = CURLE_OK;
+ bool eos = FALSE;
*pnwritten = 0;
+ DEBUGASSERT(hds_len <= blen);
#ifdef DEBUGBUILD
{
/* Allow debug builds to override this logic to force short initial
- sends
- */
+ sends */
+ size_t body_len = blen - hds_len;
char *p = getenv("CURL_SMALLREQSEND");
if(p) {
- size_t altsize = (size_t)strtoul(p, NULL, 10);
- if(altsize && altsize < blen)
- blen = altsize;
+ size_t body_small = (size_t)strtoul(p, NULL, 10);
+ if(body_small && body_small < body_len)
+ blen = hds_len + body_small;
}
}
#endif
@@ -202,7 +207,13 @@ static CURLcode xfer_send(struct Curl_easy *data,
blen = hds_len + (size_t)data->set.max_send_speed;
}
- result = Curl_xfer_send(data, buf, blen, pnwritten);
+ if(data->req.eos_read &&
+ (Curl_bufq_is_empty(&data->req.sendbuf) ||
+ Curl_bufq_len(&data->req.sendbuf) == blen)) {
+ DEBUGF(infof(data, "sending last upload chunk of %zu bytes", blen));
+ eos = TRUE;
+ }
+ result = Curl_xfer_send(data, buf, blen, eos, pnwritten);
if(!result && *pnwritten) {
if(hds_len)
Curl_debug(data, CURLINFO_HEADER_OUT, (char *)buf,
@@ -241,28 +252,32 @@ static CURLcode req_send_buffer_flush(struct Curl_easy *data)
return result;
}
-static CURLcode req_set_upload_done(struct Curl_easy *data)
+CURLcode Curl_req_set_upload_done(struct Curl_easy *data)
{
DEBUGASSERT(!data->req.upload_done);
data->req.upload_done = TRUE;
data->req.keepon &= ~(KEEP_SEND|KEEP_SEND_TIMED); /* we are done sending */
+ Curl_pgrsTime(data, TIMER_POSTRANSFER);
Curl_creader_done(data, data->req.upload_aborted);
if(data->req.upload_aborted) {
+ Curl_bufq_reset(&data->req.sendbuf);
if(data->req.writebytecount)
- infof(data, "abort upload after having sent %" CURL_FORMAT_CURL_OFF_T
- " bytes", data->req.writebytecount);
+ infof(data, "abort upload after having sent %" FMT_OFF_T " bytes",
+ data->req.writebytecount);
else
infof(data, "abort upload");
}
else if(data->req.writebytecount)
- infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T
- " bytes", data->req.writebytecount);
- else if(!data->req.download_done)
+ infof(data, "upload completely sent off: %" FMT_OFF_T " bytes",
+ data->req.writebytecount);
+ else if(!data->req.download_done) {
+ DEBUGASSERT(Curl_bufq_is_empty(&data->req.sendbuf));
infof(data, Curl_creader_total_length(data)?
"We are completely uploaded and fine" :
"Request completely sent off");
+ }
return Curl_xfer_send_close(data);
}
@@ -279,9 +294,15 @@ static CURLcode req_flush(struct Curl_easy *data)
if(result)
return result;
if(!Curl_bufq_is_empty(&data->req.sendbuf)) {
+ DEBUGF(infof(data, "Curl_req_flush(len=%zu) -> EAGAIN",
+ Curl_bufq_len(&data->req.sendbuf)));
return CURLE_AGAIN;
}
}
+ else if(Curl_xfer_needs_flush(data)) {
+ DEBUGF(infof(data, "Curl_req_flush(), xfer send_pending"));
+ return Curl_xfer_flush(data);
+ }
if(!data->req.upload_done && data->req.eos_read &&
Curl_bufq_is_empty(&data->req.sendbuf)) {
@@ -293,7 +314,7 @@ static CURLcode req_flush(struct Curl_easy *data)
if(!done)
return CURLE_AGAIN;
}
- return req_set_upload_done(data);
+ return Curl_req_set_upload_done(data);
}
return CURLE_OK;
}
@@ -367,18 +388,26 @@ CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req)
}
#endif /* !USE_HYPER */
+bool Curl_req_sendbuf_empty(struct Curl_easy *data)
+{
+ return !data->req.sendbuf_init || Curl_bufq_is_empty(&data->req.sendbuf);
+}
+
bool Curl_req_want_send(struct Curl_easy *data)
{
- return data->req.sendbuf_init && !Curl_bufq_is_empty(&data->req.sendbuf);
+ /* Not done and
+ * - KEEP_SEND and not PAUSEd.
+ * - or request has buffered data to send
+ * - or transfer connection has pending data to send */
+ return !data->req.done &&
+ (((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) ||
+ !Curl_req_sendbuf_empty(data) ||
+ Curl_xfer_needs_flush(data));
}
bool Curl_req_done_sending(struct Curl_easy *data)
{
- if(data->req.upload_done) {
- DEBUGASSERT(Curl_bufq_is_empty(&data->req.sendbuf));
- return TRUE;
- }
- return FALSE;
+ return data->req.upload_done && !Curl_req_want_send(data);
}
CURLcode Curl_req_send_more(struct Curl_easy *data)
@@ -386,7 +415,10 @@ CURLcode Curl_req_send_more(struct Curl_easy *data)
CURLcode result;
/* Fill our send buffer if more from client can be read. */
- if(!data->req.eos_read && !Curl_bufq_is_full(&data->req.sendbuf)) {
+ if(!data->req.upload_aborted &&
+ !data->req.eos_read &&
+ !(data->req.keepon & KEEP_SEND_PAUSE) &&
+ !Curl_bufq_is_full(&data->req.sendbuf)) {
ssize_t nread = Curl_bufq_sipn(&data->req.sendbuf, 0,
add_from_client, data, &result);
if(nread < 0 && result != CURLE_AGAIN)
@@ -405,7 +437,18 @@ CURLcode Curl_req_abort_sending(struct Curl_easy *data)
if(!data->req.upload_done) {
Curl_bufq_reset(&data->req.sendbuf);
data->req.upload_aborted = TRUE;
- return req_set_upload_done(data);
+ /* no longer KEEP_SEND and KEEP_SEND_PAUSE */
+ data->req.keepon &= ~KEEP_SENDBITS;
+ return Curl_req_set_upload_done(data);
}
return CURLE_OK;
}
+
+CURLcode Curl_req_stop_send_recv(struct Curl_easy *data)
+{
+ /* stop receiving and ALL sending as well, including PAUSE and HOLD.
+ * We might still be paused on receive client writes though, so
+ * keep those bits around. */
+ data->req.keepon &= ~(KEEP_RECV|KEEP_SENDBITS);
+ return Curl_req_abort_sending(data);
+}
diff --git a/libs/libcurl/src/request.h b/libs/libcurl/src/request.h
index ba4964b724..4b40889f3c 100644
--- a/libs/libcurl/src/request.h
+++ b/libs/libcurl/src/request.h
@@ -32,6 +32,9 @@
/* forward declarations */
struct UserDefined;
+#ifndef CURL_DISABLE_DOH
+struct doh_probes;
+#endif
enum expect100 {
EXP100_SEND_DATA, /* enough waiting, just send the body now */
@@ -114,7 +117,7 @@ struct SingleRequest {
struct TELNET *telnet;
} p;
#ifndef CURL_DISABLE_DOH
- struct dohdata *doh; /* DoH specific data for this request */
+ struct doh_probes *doh; /* DoH specific data for this request */
#endif
#ifndef CURL_DISABLE_COOKIES
unsigned char setcookies;
@@ -135,6 +138,7 @@ struct SingleRequest {
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(resp_trailer); /* response carried 'Trailer:' header field */
BIT(ignore_cl); /* ignore content-length */
BIT(upload_chunky); /* set TRUE if we are doing chunked transfer-encoding
on upload */
@@ -221,9 +225,25 @@ CURLcode Curl_req_send_more(struct Curl_easy *data);
bool Curl_req_want_send(struct Curl_easy *data);
/**
+ * TRUE iff the request has no buffered bytes yet to send.
+ */
+bool Curl_req_sendbuf_empty(struct Curl_easy *data);
+
+/**
* Stop sending any more request data to the server.
* Will clear the send buffer and mark request sending as done.
*/
CURLcode Curl_req_abort_sending(struct Curl_easy *data);
+/**
+ * Stop sending and receiving any more request data.
+ * Will abort sending if not done.
+ */
+CURLcode Curl_req_stop_send_recv(struct Curl_easy *data);
+
+/**
+ * Invoked when all request data has been uploaded.
+ */
+CURLcode Curl_req_set_upload_done(struct Curl_easy *data);
+
#endif /* HEADER_CURL_REQUEST_H */
diff --git a/libs/libcurl/src/rtsp.c b/libs/libcurl/src/rtsp.c
index bf29c34cfb..cf25ba75ce 100644
--- a/libs/libcurl/src/rtsp.c
+++ b/libs/libcurl/src/rtsp.c
@@ -533,8 +533,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
* 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",
+ Curl_dyn_addf(&req_buffer, "Content-Length: %" FMT_OFF_T"\r\n",
req_clen);
if(result)
goto out;
@@ -854,7 +853,7 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
* In which case we write out the left over bytes, letting the client
* writer deal with it (it will report EXCESS and fail the transfer). */
DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, done=%d "
- " rtspc->state=%d, req.size=%" CURL_FORMAT_CURL_OFF_T ")",
+ " rtspc->state=%d, req.size=%" FMT_OFF_T ")",
blen, rtspc->in_header, data->req.done, rtspc->state,
data->req.size));
if(!result && (is_eos || blen)) {
diff --git a/libs/libcurl/src/select.c b/libs/libcurl/src/select.c
index a016bcc185..df8294b9b6 100644
--- a/libs/libcurl/src/select.c
+++ b/libs/libcurl/src/select.c
@@ -56,7 +56,7 @@
* 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
+ * 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.
@@ -135,7 +135,7 @@ static int our_select(curl_socket_t maxfd, /* highest socket number */
struct timeval *ptimeout;
#ifdef USE_WINSOCK
- /* WinSock select() cannot handle zero events. See the comment below. */
+ /* Winsock select() cannot 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)) {
@@ -147,7 +147,7 @@ static int our_select(curl_socket_t maxfd, /* highest socket number */
ptimeout = curlx_mstotv(&pending_tv, timeout_ms);
#ifdef USE_WINSOCK
- /* WinSock select() must not be called with an fd_set that contains zero
+ /* Winsock select() must not be called with an fd_set that contains zero
fd flags, or it will return WSAEINVAL. But, it also cannot be called
with no fd_sets at all! From the documentation:
@@ -155,8 +155,8 @@ static int our_select(curl_socket_t maxfd, /* highest socket number */
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 does not just handle this for us instead of
- calling this an error. Luckily, with WinSock, we can _also_ ask how
+ It is unclear why Winsock does not 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,
@@ -361,8 +361,8 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms)
}
/*
- Note also that WinSock ignores the first argument, so we do not worry
- about the fact that maxfd is computed incorrectly with WinSock (since
+ Note also that Winsock ignores the first argument, so we do not 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).
*/
diff --git a/libs/libcurl/src/sendf.c b/libs/libcurl/src/sendf.c
index 390907fae8..92b21dc7ea 100644
--- a/libs/libcurl/src/sendf.c
+++ b/libs/libcurl/src/sendf.c
@@ -292,8 +292,8 @@ static CURLcode cw_download_write(struct Curl_easy *data,
if((type & CLIENTWRITE_EOS) && !data->req.no_body &&
(data->req.maxdownload > data->req.bytecount)) {
- failf(data, "end of response with %" CURL_FORMAT_CURL_OFF_T
- " bytes missing", data->req.maxdownload - data->req.bytecount);
+ failf(data, "end of response with %" FMT_OFF_T " bytes missing",
+ data->req.maxdownload - data->req.bytecount);
return CURLE_PARTIAL_FILE;
}
}
@@ -328,9 +328,9 @@ static CURLcode cw_download_write(struct Curl_easy *data,
infof(data,
"Excess found writing body:"
" excess = %zu"
- ", size = %" CURL_FORMAT_CURL_OFF_T
- ", maxdownload = %" CURL_FORMAT_CURL_OFF_T
- ", bytecount = %" CURL_FORMAT_CURL_OFF_T,
+ ", size = %" FMT_OFF_T
+ ", maxdownload = %" FMT_OFF_T
+ ", bytecount = %" FMT_OFF_T,
excess_len, data->req.size, data->req.maxdownload,
data->req.bytecount);
connclose(data->conn, "excess found in a read");
@@ -338,8 +338,7 @@ static CURLcode cw_download_write(struct Curl_easy *data,
}
else if(nwrite < nbytes) {
failf(data, "Exceeded the maximum allowed file size "
- "(%" CURL_FORMAT_CURL_OFF_T ") with %"
- CURL_FORMAT_CURL_OFF_T " bytes",
+ "(%" FMT_OFF_T ") with %" FMT_OFF_T " bytes",
data->set.max_filesize, data->req.bytecount);
return CURLE_FILESIZE_EXCEEDED;
}
@@ -688,8 +687,8 @@ static CURLcode cr_in_read(struct Curl_easy *data,
case 0:
if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) {
failf(data, "client read function EOF fail, "
- "only %"CURL_FORMAT_CURL_OFF_T"/%"CURL_FORMAT_CURL_OFF_T
- " of needed bytes read", ctx->read_len, ctx->total_len);
+ "only %"FMT_OFF_T"/%"FMT_OFF_T " of needed bytes read",
+ ctx->read_len, ctx->total_len);
return CURLE_READ_ERROR;
}
*pnread = 0;
@@ -738,8 +737,8 @@ static CURLcode cr_in_read(struct Curl_easy *data,
*peos = ctx->seen_eos;
break;
}
- CURL_TRC_READ(data, "cr_in_read(len=%zu, total=%"CURL_FORMAT_CURL_OFF_T
- ", read=%"CURL_FORMAT_CURL_OFF_T") -> %d, nread=%zu, eos=%d",
+ CURL_TRC_READ(data, "cr_in_read(len=%zu, total=%"FMT_OFF_T
+ ", read=%"FMT_OFF_T") -> %d, nread=%zu, eos=%d",
blen, ctx->total_len, ctx->read_len, CURLE_OK,
*pnread, *peos);
return CURLE_OK;
@@ -804,8 +803,8 @@ static CURLcode cr_in_resume_from(struct Curl_easy *data,
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);
+ failf(data, "Could only read %" FMT_OFF_T " bytes from the input",
+ passed);
return CURLE_READ_ERROR;
}
} while(passed < offset);
@@ -1102,11 +1101,7 @@ static CURLcode do_init_reader_stack(struct Curl_easy *data,
clen = r->crt->total_length(data, r);
/* if we do not have 0 length init, and crlf conversion is wanted,
* add the reader for it */
- if(clen && (data->set.crlf
-#ifdef CURL_DO_LINEEND_CONV
- || data->state.prefer_ascii
-#endif
- )) {
+ if(clen && (data->set.crlf || data->state.prefer_ascii)) {
result = cr_lc_add(data);
if(result)
return result;
@@ -1130,8 +1125,8 @@ CURLcode Curl_creader_set_fread(struct Curl_easy *data, curl_off_t len)
cl_reset_reader(data);
result = do_init_reader_stack(data, r);
out:
- CURL_TRC_READ(data, "add fread reader, len=%"CURL_FORMAT_CURL_OFF_T
- " -> %d", len, result);
+ CURL_TRC_READ(data, "add fread reader, len=%"FMT_OFF_T " -> %d",
+ len, result);
return result;
}
diff --git a/libs/libcurl/src/setopt.c b/libs/libcurl/src/setopt.c
index 4956c5674f..488266e9b5 100644
--- a/libs/libcurl/src/setopt.c
+++ b/libs/libcurl/src/setopt.c
@@ -139,30 +139,24 @@ static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp)
return CURLE_OK;
}
-static CURLcode setstropt_interface(
- char *option, char **devp, char **ifacep, char **hostp)
+static CURLcode setstropt_interface(char *option, char **devp,
+ char **ifacep, char **hostp)
{
char *dev = NULL;
char *iface = NULL;
char *host = NULL;
- size_t len;
CURLcode result;
DEBUGASSERT(devp);
DEBUGASSERT(ifacep);
DEBUGASSERT(hostp);
- /* Parse the interface details */
- if(!option || !*option)
- return CURLE_BAD_FUNCTION_ARGUMENT;
- len = strlen(option);
- if(len > 255)
- return CURLE_BAD_FUNCTION_ARGUMENT;
-
- result = Curl_parse_interface(option, len, &dev, &iface, &host);
- if(result)
- return result;
-
+ if(option) {
+ /* Parse the interface details if set, otherwise clear them all */
+ result = Curl_parse_interface(option, &dev, &iface, &host);
+ if(result)
+ return result;
+ }
free(*devp);
*devp = dev;
@@ -255,15 +249,23 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
/* 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 *));
+ if(Curl_ssl_supports(data, SSLSUPP_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 *));
+ }
+ else
+ return CURLE_NOT_BUILT_IN;
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 *));
+ if(Curl_ssl_supports(data, SSLSUPP_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 *));
+ }
+ else
+ return CURLE_NOT_BUILT_IN;
break;
#endif
case CURLOPT_TLS13_CIPHERS:
@@ -423,8 +425,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
* TFTP option that specifies the block size to use for data transmission.
*/
arg = va_arg(param, long);
- if(arg > TFTP_BLKSIZE_MAX || arg < TFTP_BLKSIZE_MIN)
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ if(arg < TFTP_BLKSIZE_MIN)
+ arg = 512;
+ else if(arg > TFTP_BLKSIZE_MAX)
+ arg = TFTP_BLKSIZE_MAX;
data->set.tftp_blksize = arg;
break;
#endif
@@ -2165,7 +2169,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
*/
#ifdef USE_SSL
if(Curl_ssl_supports(data, SSLSUPP_CA_PATH))
- /* This does not work on windows. */
+ /* This does not work on Windows. */
result = Curl_setstropt(&data->set.str[STRING_SSL_CAPATH],
va_arg(param, char *));
else
@@ -2180,7 +2184,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
*/
#ifdef USE_SSL
if(Curl_ssl_supports(data, SSLSUPP_CA_PATH))
- /* This does not work on windows. */
+ /* This does not work on Windows. */
result = Curl_setstropt(&data->set.str[STRING_SSL_CAPATH_PROXY],
va_arg(param, char *));
else
@@ -2696,17 +2700,27 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
case CURLOPT_PROTOCOLS_STR: {
argptr = va_arg(param, char *);
- result = protocol2num(argptr, &data->set.allowed_protocols);
- if(result)
- return result;
+ if(argptr) {
+ result = protocol2num(argptr, &data->set.allowed_protocols);
+ if(result)
+ return result;
+ }
+ else
+ /* make a NULL argument reset to default */
+ data->set.allowed_protocols = (curl_prot_t) CURLPROTO_ALL;
break;
}
case CURLOPT_REDIR_PROTOCOLS_STR: {
argptr = va_arg(param, char *);
- result = protocol2num(argptr, &data->set.redir_protocols);
- if(result)
- return result;
+ if(argptr) {
+ result = protocol2num(argptr, &data->set.redir_protocols);
+ if(result)
+ return result;
+ }
+ else
+ /* make a NULL argument reset to default */
+ data->set.redir_protocols = (curl_prot_t) CURLPROTO_REDIR;
break;
}
@@ -3186,8 +3200,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
argptr = va_arg(param, char *);
if(!argptr) {
data->set.tls_ech = CURLECH_DISABLE;
- result = CURLE_BAD_FUNCTION_ARGUMENT;
- return result;
+ return CURLE_OK;
}
plen = strlen(argptr);
if(plen > CURL_MAX_INPUT_LENGTH) {
diff --git a/libs/libcurl/src/setup-vms.h b/libs/libcurl/src/setup-vms.h
index ec857fec27..98cd6eb979 100644
--- a/libs/libcurl/src/setup-vms.h
+++ b/libs/libcurl/src/setup-vms.h
@@ -101,7 +101,7 @@ static char *vms_translate_path(const char *path)
}
}
# else
- /* VMS translate path is actually not needed on the current 64 bit */
+ /* 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
diff --git a/libs/libcurl/src/setup-win32.h b/libs/libcurl/src/setup-win32.h
index 70789ec840..3d16dce8fc 100644
--- a/libs/libcurl/src/setup-win32.h
+++ b/libs/libcurl/src/setup-win32.h
@@ -62,11 +62,11 @@
#endif
/*
- * Include header files for windows builds before redefining anything.
+ * 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
+ * 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
+ * just as under Linux (e.g. <sys/socket.h>) and the Winsock headers should
* never be included when __CYGWIN__ is defined.
*/
diff --git a/libs/libcurl/src/sha256.c b/libs/libcurl/src/sha256.c
index f7f7649477..0169edc012 100644
--- a/libs/libcurl/src/sha256.c
+++ b/libs/libcurl/src/sha256.c
@@ -100,10 +100,10 @@
#if defined(USE_OPENSSL_SHA256)
-struct sha256_ctx {
+struct ossl_sha256_ctx {
EVP_MD_CTX *openssl_ctx;
};
-typedef struct sha256_ctx my_sha256_ctx;
+typedef struct ossl_sha256_ctx my_sha256_ctx;
static CURLcode my_sha256_init(my_sha256_ctx *ctx)
{
@@ -247,7 +247,7 @@ 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)
+ if(length == CURL_SHA256_DIGEST_LENGTH)
CryptGetHashParam(ctx->hHash, HP_HASHVAL, digest, &length, 0);
if(ctx->hHash)
diff --git a/libs/libcurl/src/share.c b/libs/libcurl/src/share.c
index d233ffa116..5f63a8ee13 100644
--- a/libs/libcurl/src/share.c
+++ b/libs/libcurl/src/share.c
@@ -31,6 +31,7 @@
#include "psl.h"
#include "vtls/vtls.h"
#include "hsts.h"
+#include "url.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -120,8 +121,12 @@ curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...)
break;
case CURL_LOCK_DATA_CONNECT:
- if(Curl_conncache_init(&share->conn_cache, NULL, 103))
- res = CURLSHE_NOMEM;
+ /* It is safe to set this option several times on a share. */
+ if(!share->cpool.idata) {
+ if(Curl_cpool_init(&share->cpool, Curl_on_disconnect,
+ NULL, share, 103))
+ res = CURLSHE_NOMEM;
+ }
break;
case CURL_LOCK_DATA_PSL:
@@ -224,8 +229,9 @@ curl_share_cleanup(struct Curl_share *share)
return CURLSHE_IN_USE;
}
- Curl_conncache_close_all_connections(&share->conn_cache);
- Curl_conncache_destroy(&share->conn_cache);
+ if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) {
+ Curl_cpool_destroy(&share->cpool);
+ }
Curl_hash_destroy(&share->hostcache);
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
diff --git a/libs/libcurl/src/share.h b/libs/libcurl/src/share.h
index 6eaf0a84ae..05cd2b0be2 100644
--- a/libs/libcurl/src/share.h
+++ b/libs/libcurl/src/share.h
@@ -34,6 +34,9 @@
#define CURL_GOOD_SHARE 0x7e117a1e
#define GOOD_SHARE_HANDLE(x) ((x) && (x)->magic == CURL_GOOD_SHARE)
+#define CURL_SHARE_KEEP_CONNECT(s) \
+ ((s) && ((s)->specifier & (1<< CURL_LOCK_DATA_CONNECT)))
+
/* this struct is libcurl-private, do not export details */
struct Curl_share {
unsigned int magic; /* CURL_GOOD_SHARE */
@@ -43,7 +46,7 @@ struct Curl_share {
curl_lock_function lockfunc;
curl_unlock_function unlockfunc;
void *clientdata;
- struct conncache conn_cache;
+ struct cpool cpool;
struct Curl_hash hostcache;
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
struct CookieInfo *cookies;
diff --git a/libs/libcurl/src/sigpipe.h b/libs/libcurl/src/sigpipe.h
index 8126d59325..c68a076b99 100644
--- a/libs/libcurl/src/sigpipe.h
+++ b/libs/libcurl/src/sigpipe.h
@@ -35,10 +35,12 @@ struct sigpipe_ignore {
};
#define SIGPIPE_VARIABLE(x) struct sigpipe_ignore x
+#define SIGPIPE_MEMBER(x) struct sigpipe_ignore x
static void sigpipe_init(struct sigpipe_ignore *ig)
{
memset(ig, 0, sizeof(*ig));
+ ig->no_signal = TRUE;
}
/*
@@ -91,6 +93,7 @@ static void sigpipe_apply(struct Curl_easy *data,
#define sigpipe_init(x) Curl_nop_stmt
#define sigpipe_restore(x) Curl_nop_stmt
#define SIGPIPE_VARIABLE(x)
+#define SIGPIPE_MEMBER(x) bool x
#endif
#endif /* HEADER_CURL_SIGPIPE_H */
diff --git a/libs/libcurl/src/smb.c b/libs/libcurl/src/smb.c
index 27d5a8d325..c07eb5454c 100644
--- a/libs/libcurl/src/smb.c
+++ b/libs/libcurl/src/smb.c
@@ -572,7 +572,7 @@ static CURLcode smb_send(struct Curl_easy *data, size_t len,
size_t bytes_written;
CURLcode result;
- result = Curl_xfer_send(data, smbc->send_buf, len, &bytes_written);
+ result = Curl_xfer_send(data, smbc->send_buf, len, FALSE, &bytes_written);
if(result)
return result;
@@ -597,7 +597,7 @@ static CURLcode smb_flush(struct Curl_easy *data)
if(!smbc->send_size)
return CURLE_OK;
- result = Curl_xfer_send(data, smbc->send_buf + smbc->sent, len,
+ result = Curl_xfer_send(data, smbc->send_buf + smbc->sent, len, FALSE,
&bytes_written);
if(result)
return result;
@@ -642,9 +642,9 @@ static CURLcode smb_send_setup(struct Curl_easy *data)
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 */
+ const size_t byte_count = sizeof(lm) + sizeof(nt) +
+ strlen(smbc->user) + strlen(smbc->domain) +
+ strlen(OS) + strlen(CLIENTNAME) + 4; /* 4 null chars */
if(byte_count > sizeof(msg.bytes))
return CURLE_FILESIZE_EXCEEDED;
@@ -653,7 +653,7 @@ static CURLcode smb_send_setup(struct Curl_easy *data)
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));
+ memset(&msg, 0, sizeof(msg) - sizeof(msg.bytes));
msg.word_count = SMB_WC_SETUP_ANDX;
msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
msg.max_buffer_size = smb_swap16(MAX_MESSAGE_SIZE);
@@ -671,7 +671,7 @@ static CURLcode smb_send_setup(struct Curl_easy *data)
MSGCATNULL(smbc->domain);
MSGCATNULL(OS);
MSGCATNULL(CLIENTNAME);
- byte_count = p - msg.bytes;
+ DEBUGASSERT(byte_count == (size_t)(p - msg.bytes));
msg.byte_count = smb_swap16((unsigned short)byte_count);
return smb_send_message(data, SMB_COM_SETUP_ANDX, &msg,
@@ -685,12 +685,12 @@ static CURLcode smb_send_tree_connect(struct Curl_easy *data)
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 */
+ const size_t byte_count = strlen(conn->host.name) + strlen(smbc->share) +
+ strlen(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */
if(byte_count > sizeof(msg.bytes))
return CURLE_FILESIZE_EXCEEDED;
- memset(&msg, 0, sizeof(msg));
+ memset(&msg, 0, sizeof(msg) - sizeof(msg.bytes));
msg.word_count = SMB_WC_TREE_CONNECT_ANDX;
msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
msg.pw_len = 0;
@@ -699,7 +699,7 @@ static CURLcode smb_send_tree_connect(struct Curl_easy *data)
MSGCAT("\\");
MSGCATNULL(smbc->share);
MSGCATNULL(SERVICENAME); /* Match any type of service */
- byte_count = p - msg.bytes;
+ DEBUGASSERT(byte_count == (size_t)(p - msg.bytes));
msg.byte_count = smb_swap16((unsigned short)byte_count);
return smb_send_message(data, SMB_COM_TREE_CONNECT_ANDX, &msg,
@@ -710,16 +710,15 @@ 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;
+ const size_t byte_count = strlen(req->path) + 1;
- if((strlen(req->path) + 1) > sizeof(msg.bytes))
+ if(byte_count > sizeof(msg.bytes))
return CURLE_FILESIZE_EXCEEDED;
- memset(&msg, 0, sizeof(msg));
+ memset(&msg, 0, sizeof(msg) - sizeof(msg.bytes));
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.name_length = smb_swap16((unsigned short)(byte_count - 1));
msg.share_access = smb_swap32(SMB_FILE_SHARE_ALL);
if(data->state.upload) {
msg.access = smb_swap32(SMB_GENERIC_READ | SMB_GENERIC_WRITE);
@@ -729,7 +728,7 @@ static CURLcode smb_send_open(struct Curl_easy *data)
msg.access = smb_swap32(SMB_GENERIC_READ);
msg.create_disposition = smb_swap32(SMB_FILE_OPEN);
}
- msg.byte_count = smb_swap16((unsigned short) ++byte_count);
+ 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,
@@ -924,7 +923,7 @@ static CURLcode smb_connection_state(struct Curl_easy *data, bool *done)
/*
* 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.
+ * to POSIX time. Cap the output to fit within a time_t.
*/
static void get_posix_time(time_t *out, curl_off_t timestamp)
{
diff --git a/libs/libcurl/src/smtp.c b/libs/libcurl/src/smtp.c
index 02321ebf85..a3191f15e4 100644
--- a/libs/libcurl/src/smtp.c
+++ b/libs/libcurl/src/smtp.c
@@ -288,7 +288,7 @@ static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out)
static void smtp_state(struct Curl_easy *data, smtpstate newstate)
{
struct smtp_conn *smtpc = &data->conn->proto.smtpc;
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
/* for debug purposes */
static const char * const names[] = {
"STOP",
@@ -308,8 +308,8 @@ static void smtp_state(struct Curl_easy *data, smtpstate newstate)
};
if(smtpc->state != newstate)
- infof(data, "SMTP %p state change from %s to %s",
- (void *)smtpc, names[smtpc->state], names[newstate]);
+ CURL_TRC_SMTP(data, "state change from %s to %s",
+ names[smtpc->state], names[newstate]);
#endif
smtpc->state = newstate;
@@ -723,7 +723,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
/* 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);
+ size = aprintf("%" FMT_OFF_T, data->state.infilesize);
if(!size) {
result = CURLE_OUT_OF_MEMORY;
@@ -1422,7 +1422,8 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
/* Clear the transfer mode for the next request */
smtp->transfer = PPTRANSFER_BODY;
-
+ CURL_TRC_SMTP(data, "smtp_done(status=%d, premature=%d) -> %d",
+ status, premature, result);
return result;
}
@@ -1440,7 +1441,7 @@ static CURLcode smtp_perform(struct Curl_easy *data, bool *connected,
CURLcode result = CURLE_OK;
struct SMTP *smtp = data->req.p.smtp;
- DEBUGF(infof(data, "DO phase starts"));
+ CURL_TRC_SMTP(data, "smtp_perform(), start");
if(data->req.no_body) {
/* Requested no body means no transfer */
@@ -1472,16 +1473,16 @@ static CURLcode smtp_perform(struct Curl_easy *data, bool *connected,
result = smtp_perform_command(data);
if(result)
- return result;
+ goto out;
/* 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"));
-
+out:
+ CURL_TRC_SMTP(data, "smtp_perform() -> %d, connected=%d, done=%d",
+ result, *connected, *dophase_done);
return result;
}
@@ -1507,7 +1508,7 @@ static CURLcode smtp_do(struct Curl_easy *data, bool *done)
return result;
result = smtp_regular_transfer(data, done);
-
+ CURL_TRC_SMTP(data, "smtp_do() -> %d, done=%d", result, *done);
return result;
}
@@ -1542,6 +1543,7 @@ static CURLcode smtp_disconnect(struct Curl_easy *data,
/* Cleanup our connection based variables */
Curl_safefree(smtpc->domain);
+ CURL_TRC_SMTP(data, "smtp_disconnect(), finished");
return CURLE_OK;
}
@@ -1573,6 +1575,7 @@ static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done)
DEBUGF(infof(data, "DO phase is complete"));
}
+ CURL_TRC_SMTP(data, "smtp_doing() -> %d, done=%d", result, *dophase_done);
return result;
}
@@ -1607,6 +1610,8 @@ static CURLcode smtp_regular_transfer(struct Curl_easy *data,
if(!result && *dophase_done)
result = smtp_dophase_done(data, connected);
+ CURL_TRC_SMTP(data, "smtp_regular_transfer() -> %d, done=%d",
+ result, *dophase_done);
return result;
}
@@ -1620,10 +1625,8 @@ static CURLcode smtp_setup_connection(struct Curl_easy *data,
/* Initialise the SMTP layer */
result = smtp_init(data);
- if(result)
- return result;
-
- return CURLE_OK;
+ CURL_TRC_SMTP(data, "smtp_setup_connection() -> %d", result);
+ return result;
}
/***********************************************************************
diff --git a/libs/libcurl/src/socketpair.h b/libs/libcurl/src/socketpair.h
index 38d8245bc4..d3fa60c135 100644
--- a/libs/libcurl/src/socketpair.h
+++ b/libs/libcurl/src/socketpair.h
@@ -74,7 +74,7 @@ int Curl_pipe(curl_socket_t socks[2], bool nonblocking);
#elif !defined(HAVE_SOCKETPAIR)
#define SOCKETPAIR_FAMILY 0 /* not used */
#else
-#error "unsupported unix domain and socketpair build combo"
+#error "unsupported Unix domain and socketpair build combo"
#endif
#ifdef SOCK_CLOEXEC
diff --git a/libs/libcurl/src/socks.c b/libs/libcurl/src/socks.c
index a21e6d51fb..659d6efd1c 100644
--- a/libs/libcurl/src/socks.c
+++ b/libs/libcurl/src/socks.c
@@ -217,7 +217,7 @@ static CURLproxycode socks_state_send(struct Curl_cfilter *cf,
CURLcode result;
nwritten = Curl_conn_cf_send(cf->next, data, (char *)sx->outp,
- sx->outstanding, &result);
+ sx->outstanding, FALSE, &result);
if(nwritten <= 0) {
if(CURLE_AGAIN == result) {
return CURLPX_OK;
@@ -388,7 +388,7 @@ CONNECT_RESOLVED:
infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)", buf);
- Curl_resolv_unlock(data, dns); /* not used anymore from now on */
+ Curl_resolv_unlink(data, &dns); /* not used anymore from now on */
}
else
failf(data, "SOCKS4 connection to %s not supported", sx->hostname);
@@ -893,7 +893,7 @@ CONNECT_RESOLVED:
failf(data, "SOCKS5 connection to %s not supported", dest);
}
- Curl_resolv_unlock(data, dns); /* not used anymore from now on */
+ Curl_resolv_unlink(data, &dns); /* not used anymore from now on */
goto CONNECT_REQ_SEND;
}
CONNECT_RESOLVE_REMOTE:
diff --git a/libs/libcurl/src/socks_gssapi.c b/libs/libcurl/src/socks_gssapi.c
index c302ddf5bb..5c3837cb6e 100644
--- a/libs/libcurl/src/socks_gssapi.c
+++ b/libs/libcurl/src/socks_gssapi.c
@@ -204,7 +204,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
us_length = htons((unsigned short)gss_send_token.length);
memcpy(socksreq + 2, &us_length, sizeof(short));
- nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code);
+ nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4,
+ FALSE, &code);
if(code || (4 != nwritten)) {
failf(data, "Failed to send GSS-API authentication request.");
gss_release_name(&gss_status, &server);
@@ -216,7 +217,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
nwritten = Curl_conn_cf_send(cf->next, data,
(char *)gss_send_token.value,
- gss_send_token.length, &code);
+ gss_send_token.length, FALSE, &code);
if(code || ((ssize_t)gss_send_token.length != nwritten)) {
failf(data, "Failed to send GSS-API authentication token.");
gss_release_name(&gss_status, &server);
@@ -410,7 +411,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
memcpy(socksreq + 2, &us_length, sizeof(short));
}
- nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code);
+ nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, FALSE,
+ &code);
if(code || (4 != nwritten)) {
failf(data, "Failed to send GSS-API encryption request.");
gss_release_buffer(&gss_status, &gss_w_token);
@@ -420,7 +422,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
if(data->set.socks5_gssapi_nec) {
memcpy(socksreq, &gss_enc, 1);
- nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, &code);
+ nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, FALSE,
+ &code);
if(code || ( 1 != nwritten)) {
failf(data, "Failed to send GSS-API encryption type.");
gss_delete_sec_context(&gss_status, &gss_context, NULL);
@@ -430,7 +433,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
else {
nwritten = Curl_conn_cf_send(cf->next, data,
(char *)gss_w_token.value,
- gss_w_token.length, &code);
+ gss_w_token.length, FALSE, &code);
if(code || ((ssize_t)gss_w_token.length != nwritten)) {
failf(data, "Failed to send GSS-API encryption type.");
gss_release_buffer(&gss_status, &gss_w_token);
diff --git a/libs/libcurl/src/socks_sspi.c b/libs/libcurl/src/socks_sspi.c
index 882c8a2a45..34b90e0752 100644
--- a/libs/libcurl/src/socks_sspi.c
+++ b/libs/libcurl/src/socks_sspi.c
@@ -139,7 +139,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
cred_handle.dwLower = 0;
cred_handle.dwUpper = 0;
- status = s_pSecFn->AcquireCredentialsHandle(NULL,
+ status = Curl_pSecFn->AcquireCredentialsHandle(NULL,
(TCHAR *) TEXT("Kerberos"),
SECPKG_CRED_OUTBOUND,
NULL,
@@ -152,7 +152,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
if(check_sspi_err(data, status, "AcquireCredentialsHandle")) {
failf(data, "Failed to acquire credentials.");
free(service_name);
- s_pSecFn->FreeCredentialsHandle(&cred_handle);
+ Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
return CURLE_COULDNT_CONNECT;
}
@@ -167,7 +167,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
if(!sname)
return CURLE_OUT_OF_MEMORY;
- status = s_pSecFn->InitializeSecurityContext(&cred_handle,
+ status = Curl_pSecFn->InitializeSecurityContext(&cred_handle,
context_handle,
sname,
ISC_REQ_MUTUAL_AUTH |
@@ -186,17 +186,17 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
curlx_unicodefree(sname);
if(sspi_recv_token.pvBuffer) {
- s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
+ Curl_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);
+ Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
if(sspi_recv_token.pvBuffer)
- s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
+ Curl_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
failf(data, "Failed to initialise security context.");
return CURLE_COULDNT_CONNECT;
}
@@ -207,44 +207,45 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
us_length = htons((unsigned short)sspi_send_token.cbBuffer);
memcpy(socksreq + 2, &us_length, sizeof(short));
- written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code);
+ written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, FALSE,
+ &code);
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);
+ Curl_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);
+ Curl_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
+ Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
return CURLE_COULDNT_CONNECT;
}
written = Curl_conn_cf_send(cf->next, data,
(char *)sspi_send_token.pvBuffer,
- sspi_send_token.cbBuffer, &code);
+ sspi_send_token.cbBuffer, FALSE, &code);
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);
+ Curl_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);
+ Curl_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
+ Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
return CURLE_COULDNT_CONNECT;
}
}
if(sspi_send_token.pvBuffer) {
- s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
+ Curl_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);
+ Curl_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
sspi_recv_token.pvBuffer = NULL;
}
sspi_recv_token.cbBuffer = 0;
@@ -266,8 +267,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
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);
+ Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
return CURLE_COULDNT_CONNECT;
}
@@ -276,8 +277,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
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);
+ Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
return CURLE_COULDNT_CONNECT;
}
@@ -285,8 +286,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
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);
+ Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
return CURLE_COULDNT_CONNECT;
}
@@ -298,8 +299,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
if(!sspi_recv_token.pvBuffer) {
free(service_name);
- s_pSecFn->FreeCredentialsHandle(&cred_handle);
- s_pSecFn->DeleteSecurityContext(&sspi_context);
+ Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
return CURLE_OUT_OF_MEMORY;
}
result = Curl_blockread_all(cf, data, (char *)sspi_recv_token.pvBuffer,
@@ -309,9 +310,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
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);
+ Curl_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
+ Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
return CURLE_COULDNT_CONNECT;
}
@@ -321,13 +322,13 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
free(service_name);
/* Everything is good so far, user was authenticated! */
- status = s_pSecFn->QueryCredentialsAttributes(&cred_handle,
+ status = Curl_pSecFn->QueryCredentialsAttributes(&cred_handle,
SECPKG_CRED_ATTR_NAMES,
&names);
- s_pSecFn->FreeCredentialsHandle(&cred_handle);
+ Curl_pSecFn->FreeCredentialsHandle(&cred_handle);
if(check_sspi_err(data, status, "QueryCredentialAttributes")) {
- s_pSecFn->DeleteSecurityContext(&sspi_context);
- s_pSecFn->FreeContextBuffer(names.sUserName);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
+ Curl_pSecFn->FreeContextBuffer(names.sUserName);
failf(data, "Failed to determine username.");
return CURLE_COULDNT_CONNECT;
}
@@ -338,7 +339,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
(user_utf8 ? user_utf8 : "(unknown)"));
curlx_unicodefree(user_utf8);
#endif
- s_pSecFn->FreeContextBuffer(names.sUserName);
+ Curl_pSecFn->FreeContextBuffer(names.sUserName);
}
/* Do encryption */
@@ -393,11 +394,11 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
memcpy(socksreq + 2, &us_length, sizeof(short));
}
else {
- status = s_pSecFn->QueryContextAttributes(&sspi_context,
+ status = Curl_pSecFn->QueryContextAttributes(&sspi_context,
SECPKG_ATTR_SIZES,
&sspi_sizes);
if(check_sspi_err(data, status, "QueryContextAttributes")) {
- s_pSecFn->DeleteSecurityContext(&sspi_context);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
failf(data, "Failed to query security context attributes.");
return CURLE_COULDNT_CONNECT;
}
@@ -407,15 +408,15 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
sspi_w_token[0].pvBuffer = malloc(sspi_sizes.cbSecurityTrailer);
if(!sspi_w_token[0].pvBuffer) {
- s_pSecFn->DeleteSecurityContext(&sspi_context);
+ Curl_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);
+ Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
return CURLE_OUT_OF_MEMORY;
}
@@ -424,20 +425,20 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
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);
+ Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+ Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
return CURLE_OUT_OF_MEMORY;
}
- status = s_pSecFn->EncryptMessage(&sspi_context,
+ status = Curl_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);
+ Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+ Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
+ Curl_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
failf(data, "Failed to query security context attributes.");
return CURLE_COULDNT_CONNECT;
}
@@ -446,10 +447,10 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
+ 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);
+ Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+ Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
+ Curl_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
return CURLE_OUT_OF_MEMORY;
}
@@ -462,13 +463,13 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
+ sspi_w_token[1].cbBuffer,
sspi_w_token[2].pvBuffer, sspi_w_token[2].cbBuffer);
- s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+ Curl_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);
+ Curl_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);
+ Curl_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
sspi_w_token[2].pvBuffer = NULL;
sspi_w_token[2].cbBuffer = 0;
@@ -476,43 +477,45 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
memcpy(socksreq + 2, &us_length, sizeof(short));
}
- written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code);
+ written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, FALSE,
+ &code);
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);
+ Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
return CURLE_COULDNT_CONNECT;
}
if(data->set.socks5_gssapi_nec) {
memcpy(socksreq, &gss_enc, 1);
- written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, &code);
+ written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, FALSE,
+ &code);
if(code || (1 != written)) {
failf(data, "Failed to send SSPI encryption type.");
- s_pSecFn->DeleteSecurityContext(&sspi_context);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
return CURLE_COULDNT_CONNECT;
}
}
else {
written = Curl_conn_cf_send(cf->next, data,
(char *)sspi_send_token.pvBuffer,
- sspi_send_token.cbBuffer, &code);
+ sspi_send_token.cbBuffer, FALSE, &code);
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);
+ Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
return CURLE_COULDNT_CONNECT;
}
if(sspi_send_token.pvBuffer)
- s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
+ Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
}
result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread);
if(result || (actualread != 4)) {
failf(data, "Failed to receive SSPI encryption response.");
- s_pSecFn->DeleteSecurityContext(&sspi_context);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
return CURLE_COULDNT_CONNECT;
}
@@ -520,14 +523,14 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
if(socksreq[1] == 255) { /* status / message type */
failf(data, "User was rejected by the SOCKS5 server (%u %u).",
(unsigned int)socksreq[0], (unsigned int)socksreq[1]);
- s_pSecFn->DeleteSecurityContext(&sspi_context);
+ Curl_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);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
return CURLE_COULDNT_CONNECT;
}
@@ -537,7 +540,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
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);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
return CURLE_OUT_OF_MEMORY;
}
@@ -546,8 +549,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
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);
+ Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
return CURLE_COULDNT_CONNECT;
}
@@ -559,17 +562,17 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
sspi_w_token[1].cbBuffer = 0;
sspi_w_token[1].pvBuffer = NULL;
- status = s_pSecFn->DecryptMessage(&sspi_context,
+ status = Curl_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);
+ Curl_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);
+ Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
failf(data, "Failed to query security context attributes.");
return CURLE_COULDNT_CONNECT;
}
@@ -578,27 +581,27 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
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);
+ Curl_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);
+ Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
+ Curl_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);
+ Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+ Curl_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);
+ Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
+ Curl_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);
+ Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
}
(void)curlx_nonblock(sock, TRUE);
@@ -611,7 +614,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
if(socksreq[0] != 0)
conn->socks5_sspi_context = sspi_context;
else {
- s_pSecFn->DeleteSecurityContext(&sspi_context);
+ Curl_pSecFn->DeleteSecurityContext(&sspi_context);
conn->socks5_sspi_context = sspi_context;
}
*/
diff --git a/libs/libcurl/src/splay.c b/libs/libcurl/src/splay.c
index 20053c6770..210a427bbc 100644
--- a/libs/libcurl/src/splay.c
+++ b/libs/libcurl/src/splay.c
@@ -24,6 +24,7 @@
#include "curl_setup.h"
+#include "timeval.h"
#include "splay.h"
/*
@@ -33,7 +34,7 @@
* zero : when i is equal to j
* positive when : when i is larger than j
*/
-#define compare(i,j) Curl_splaycomparekeys((i),(j))
+#define compare(i,j) Curl_timediff_us(i,j)
/*
* Splay using the key i (which may or may not be in the tree.) The starting
@@ -45,12 +46,12 @@ struct Curl_tree *Curl_splay(struct curltime i,
struct Curl_tree N, *l, *r, *y;
if(!t)
- return t;
+ return NULL;
N.smaller = N.larger = NULL;
l = r = &N;
for(;;) {
- long comp = compare(i, t->key);
+ timediff_t comp = compare(i, t->key);
if(comp < 0) {
if(!t->smaller)
break;
@@ -106,11 +107,11 @@ struct Curl_tree *Curl_splayinsert(struct curltime i,
~0, -1
}; /* will *NEVER* appear */
- if(!node)
- return t;
+ DEBUGASSERT(node);
if(t) {
t = Curl_splay(i, t);
+ DEBUGASSERT(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
@@ -166,6 +167,7 @@ struct Curl_tree *Curl_splaygetbest(struct curltime i,
/* find smallest */
t = Curl_splay(tv_zero, t);
+ DEBUGASSERT(t);
if(compare(i, t->key) < 0) {
/* even the smallest is too big */
*removed = NULL;
@@ -217,9 +219,11 @@ int Curl_splayremove(struct Curl_tree *t,
}; /* will *NEVER* appear */
struct Curl_tree *x;
- if(!t || !removenode)
+ if(!t)
return 1;
+ DEBUGASSERT(removenode);
+
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. */
@@ -238,6 +242,7 @@ int Curl_splayremove(struct Curl_tree *t,
}
t = Curl_splay(removenode->key, t);
+ DEBUGASSERT(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
@@ -268,6 +273,7 @@ int Curl_splayremove(struct Curl_tree *t,
x = t->larger;
else {
x = Curl_splay(removenode->key, t->smaller);
+ DEBUGASSERT(x);
x->larger = t->larger;
}
}
@@ -276,3 +282,16 @@ int Curl_splayremove(struct Curl_tree *t,
return 0;
}
+
+/* set and get the custom payload for this tree node */
+void Curl_splayset(struct Curl_tree *node, void *payload)
+{
+ DEBUGASSERT(node);
+ node->ptr = payload;
+}
+
+void *Curl_splayget(struct Curl_tree *node)
+{
+ DEBUGASSERT(node);
+ return node->ptr;
+}
diff --git a/libs/libcurl/src/splay.h b/libs/libcurl/src/splay.h
index 5ec7b7993d..4816ac7a66 100644
--- a/libs/libcurl/src/splay.h
+++ b/libs/libcurl/src/splay.h
@@ -26,13 +26,14 @@
#include "curl_setup.h"
#include "timeval.h"
+/* only use function calls to access this struct */
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 does not care about */
+ struct curltime key; /* this node's "sort" key */
+ void *ptr; /* data the splay code does not care about */
};
struct Curl_tree *Curl_splay(struct curltime i,
@@ -50,9 +51,8 @@ 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))))
+/* set and get the custom payload for this tree node */
+void Curl_splayset(struct Curl_tree *node, void *payload);
+void *Curl_splayget(struct Curl_tree *node);
#endif /* HEADER_CURL_SPLAY_H */
diff --git a/libs/libcurl/src/strtoofft.c b/libs/libcurl/src/strtoofft.c
index 48332495e9..141d485331 100644
--- a/libs/libcurl/src/strtoofft.c
+++ b/libs/libcurl/src/strtoofft.c
@@ -207,7 +207,7 @@ static int get_char(char c, int base)
#endif /* Only present if we need strtoll, but do not have it. */
/*
- * Parse a *positive* up to 64-bit number written in ascii.
+ * 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)
diff --git a/libs/libcurl/src/system_win32.c b/libs/libcurl/src/system_win32.c
index 53ddcf345d..a4979f6768 100644
--- a/libs/libcurl/src/system_win32.c
+++ b/libs/libcurl/src/system_win32.c
@@ -38,25 +38,18 @@
LARGE_INTEGER Curl_freq;
bool Curl_isVistaOrGreater;
-bool Curl_isWindows8OrGreater;
/* Handle of iphlpapp.dll */
static HMODULE s_hIpHlpApiDll = NULL;
-/* Function pointers */
+/* Pointer to the if_nametoindex function */
IF_NAMETOINDEX_FN Curl_if_nametoindex = NULL;
-FREEADDRINFOEXW_FN Curl_FreeAddrInfoExW = NULL;
-GETADDRINFOEXCANCEL_FN Curl_GetAddrInfoExCancel = NULL;
-GETADDRINFOEXW_FN Curl_GetAddrInfoExW = NULL;
-/* Curl_win32_init() performs win32 global initialization */
+/* Curl_win32_init() performs Win32 global initialization */
CURLcode Curl_win32_init(long flags)
{
-#ifdef USE_WINSOCK
- HMODULE ws2_32Dll;
-#endif
/* CURL_GLOBAL_WIN32 controls the *optional* part of the initialization which
- is just for Winsock at the moment. Any required win32 initialization
+ 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
@@ -111,22 +104,6 @@ CURLcode Curl_win32_init(long flags)
Curl_if_nametoindex = pIfNameToIndex;
}
-#ifdef USE_WINSOCK
-#ifdef CURL_WINDOWS_APP
- ws2_32Dll = Curl_load_library(TEXT("ws2_32.dll"));
-#else
- ws2_32Dll = GetModuleHandleA("ws2_32");
-#endif
- if(ws2_32Dll) {
- Curl_FreeAddrInfoExW = CURLX_FUNCTION_CAST(FREEADDRINFOEXW_FN,
- GetProcAddress(ws2_32Dll, "FreeAddrInfoExW"));
- Curl_GetAddrInfoExCancel = CURLX_FUNCTION_CAST(GETADDRINFOEXCANCEL_FN,
- GetProcAddress(ws2_32Dll, "GetAddrInfoExCancel"));
- Curl_GetAddrInfoExW = CURLX_FUNCTION_CAST(GETADDRINFOEXW_FN,
- GetProcAddress(ws2_32Dll, "GetAddrInfoExW"));
- }
-#endif
-
/* 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,
@@ -136,13 +113,6 @@ CURLcode Curl_win32_init(long flags)
else
Curl_isVistaOrGreater = FALSE;
- if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT,
- VERSION_GREATER_THAN_EQUAL)) {
- Curl_isWindows8OrGreater = TRUE;
- }
- else
- Curl_isWindows8OrGreater = FALSE;
-
QueryPerformanceFrequency(&Curl_freq);
return CURLE_OK;
}
@@ -150,9 +120,6 @@ CURLcode Curl_win32_init(long flags)
/* Curl_win32_cleanup() is the opposite of Curl_win32_init() */
void Curl_win32_cleanup(long init_flags)
{
- Curl_FreeAddrInfoExW = NULL;
- Curl_GetAddrInfoExCancel = NULL;
- Curl_GetAddrInfoExW = NULL;
if(s_hIpHlpApiDll) {
FreeLibrary(s_hIpHlpApiDll);
s_hIpHlpApiDll = NULL;
@@ -212,7 +179,7 @@ HMODULE Curl_load_library(LPCTSTR filename)
HMODULE hModule = NULL;
LOADLIBRARYEX_FN pLoadLibraryEx = NULL;
- /* Get a handle to kernel32 so we can access it is functions at runtime */
+ /* Get a handle to kernel32 so we can access its functions at runtime */
HMODULE hKernel32 = GetModuleHandle(TEXT("kernel32"));
if(!hKernel32)
return NULL;
@@ -271,16 +238,4 @@ HMODULE Curl_load_library(LPCTSTR filename)
#endif
}
-bool Curl_win32_impersonating(void)
-{
-#ifndef CURL_WINDOWS_APP
- HANDLE token = NULL;
- if(OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token)) {
- CloseHandle(token);
- return TRUE;
- }
-#endif
- return FALSE;
-}
-
#endif /* _WIN32 */
diff --git a/libs/libcurl/src/system_win32.h b/libs/libcurl/src/system_win32.h
index ec66d3b887..4ba748d381 100644
--- a/libs/libcurl/src/system_win32.h
+++ b/libs/libcurl/src/system_win32.h
@@ -26,13 +26,12 @@
#include "curl_setup.h"
-#ifdef _WIN32
+#if defined(_WIN32)
#include <curl/curl.h>
extern LARGE_INTEGER Curl_freq;
extern bool Curl_isVistaOrGreater;
-extern bool Curl_isWindows8OrGreater;
CURLcode Curl_win32_init(long flags);
void Curl_win32_cleanup(long init_flags);
@@ -43,35 +42,6 @@ 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;
-/* Identical copy of addrinfoexW/ADDRINFOEXW */
-typedef struct addrinfoexW_
-{
- int ai_flags;
- int ai_family;
- int ai_socktype;
- int ai_protocol;
- size_t ai_addrlen;
- PWSTR ai_canonname;
- struct sockaddr *ai_addr;
- void *ai_blob;
- size_t ai_bloblen;
- LPGUID ai_provider;
- struct addrinfoexW_ *ai_next;
-} ADDRINFOEXW_;
-
-typedef void (CALLBACK *LOOKUP_COMPLETION_FN)(DWORD, DWORD, LPWSAOVERLAPPED);
-typedef void (WSAAPI *FREEADDRINFOEXW_FN)(ADDRINFOEXW_*);
-typedef int (WSAAPI *GETADDRINFOEXCANCEL_FN)(LPHANDLE);
-typedef int (WSAAPI *GETADDRINFOEXW_FN)(PCWSTR, PCWSTR, DWORD, LPGUID,
- const ADDRINFOEXW_*, ADDRINFOEXW_**, struct timeval*, LPOVERLAPPED,
- LOOKUP_COMPLETION_FN, LPHANDLE);
-
-extern FREEADDRINFOEXW_FN Curl_FreeAddrInfoExW;
-extern GETADDRINFOEXCANCEL_FN Curl_GetAddrInfoExCancel;
-extern GETADDRINFOEXW_FN Curl_GetAddrInfoExW;
-
-bool Curl_win32_impersonating(void);
-
/* This is used to dynamically load DLLs */
HMODULE Curl_load_library(LPCTSTR filename);
#else /* _WIN32 */
diff --git a/libs/libcurl/src/telnet.c b/libs/libcurl/src/telnet.c
index bced497ad2..b8dac143d5 100644
--- a/libs/libcurl/src/telnet.c
+++ b/libs/libcurl/src/telnet.c
@@ -1276,7 +1276,7 @@ static CURLcode send_telnet_data(struct Curl_easy *data,
default: /* write! */
bytes_written = 0;
result = Curl_xfer_send(data, outbuf + total_written,
- outlen - total_written, &bytes_written);
+ outlen - total_written, FALSE, &bytes_written);
total_written += bytes_written;
break;
}
@@ -1342,7 +1342,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
#ifdef USE_WINSOCK
/* We want to wait for both stdin and the socket. Since
- ** the select() function in winsock only works on sockets
+ ** the select() function in Winsock only works on sockets
** we have to use the WaitForMultipleObjects() call.
*/
@@ -1353,7 +1353,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
return CURLE_FAILED_INIT;
}
- /* Tell winsock what events we want to listen to */
+ /* 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;
diff --git a/libs/libcurl/src/tftp.c b/libs/libcurl/src/tftp.c
index bc562c8e49..e56aabcb62 100644
--- a/libs/libcurl/src/tftp.c
+++ b/libs/libcurl/src/tftp.c
@@ -240,8 +240,7 @@ static CURLcode tftp_set_timeouts(struct tftp_state_data *state)
state->retry_time = 1;
infof(state->data,
- "set timeouts for state %d; Total % " CURL_FORMAT_CURL_OFF_T
- ", retry %d maxtry %d",
+ "set timeouts for state %d; Total % " FMT_OFF_T ", retry %d maxtry %d",
(int)state->state, timeout_ms, state->retry_time, state->retry_max);
/* init RX time */
@@ -434,7 +433,7 @@ static CURLcode tftp_send_first(struct tftp_state_data *state,
struct Curl_easy *data = state->data;
CURLcode result = CURLE_OK;
- /* Set ascii mode if -B flag was used */
+ /* Set ASCII mode if -B flag was used */
if(data->state.prefer_ascii)
mode = "netascii";
@@ -484,7 +483,7 @@ static CURLcode tftp_send_first(struct tftp_state_data *state,
char buf[64];
/* add tsize option */
if(data->state.upload && (data->state.infilesize != -1))
- msnprintf(buf, sizeof(buf), "%" CURL_FORMAT_CURL_OFF_T,
+ msnprintf(buf, sizeof(buf), "%" FMT_OFF_T,
data->state.infilesize);
else
strcpy(buf, "0"); /* the destination is large enough */
diff --git a/libs/libcurl/src/timediff.h b/libs/libcurl/src/timediff.h
index f8d17047c3..d24e01803c 100644
--- a/libs/libcurl/src/timediff.h
+++ b/libs/libcurl/src/timediff.h
@@ -29,7 +29,7 @@
/* 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 FMT_TIMEDIFF_T FMT_OFF_T
#define TIMEDIFF_T_MAX CURL_OFF_T_MAX
#define TIMEDIFF_T_MIN CURL_OFF_T_MIN
diff --git a/libs/libcurl/src/timeval.c b/libs/libcurl/src/timeval.c
index 5deed16fba..cf7a0b055c 100644
--- a/libs/libcurl/src/timeval.c
+++ b/libs/libcurl/src/timeval.c
@@ -137,7 +137,7 @@ struct curltime Curl_now(void)
struct curltime Curl_now(void)
{
/*
- ** Monotonic timer on Mac OS is provided by mach_absolute_time(), which
+ ** Monotonic timer on macOS 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().
diff --git a/libs/libcurl/src/transfer.c b/libs/libcurl/src/transfer.c
index 598f59a83f..22e3151245 100644
--- a/libs/libcurl/src/transfer.c
+++ b/libs/libcurl/src/transfer.c
@@ -184,6 +184,18 @@ static bool xfer_recv_shutdown_started(struct Curl_easy *data)
return Curl_shutdown_started(data, sockindex);
}
+CURLcode Curl_xfer_send_shutdown(struct Curl_easy *data, bool *done)
+{
+ int sockindex;
+
+ if(!data || !data->conn)
+ return CURLE_FAILED_INIT;
+ if(data->conn->writesockfd == CURL_SOCKET_BAD)
+ return CURLE_FAILED_INIT;
+ sockindex = (data->conn->writesockfd == data->conn->sock[SECONDARYSOCKET]);
+ return Curl_conn_shutdown(data, sockindex, done);
+}
+
/**
* Receive raw response data for the transfer.
* @param data the transfer
@@ -237,7 +249,7 @@ static ssize_t Curl_xfer_recv_resp(struct Curl_easy *data,
return -1;
}
}
- DEBUGF(infof(data, "readwrite_data: we are done"));
+ DEBUGF(infof(data, "sendrecv_dl: we are done"));
}
DEBUGASSERT(nread >= 0);
return nread;
@@ -248,9 +260,9 @@ static ssize_t Curl_xfer_recv_resp(struct Curl_easy *data,
* the stream was rewound (in which case we have data in a
* buffer)
*/
-static CURLcode readwrite_data(struct Curl_easy *data,
- struct SingleRequest *k,
- int *didwhat)
+static CURLcode sendrecv_dl(struct Curl_easy *data,
+ struct SingleRequest *k,
+ int *didwhat)
{
struct connectdata *conn = data->conn;
CURLcode result = CURLE_OK;
@@ -294,11 +306,18 @@ static CURLcode readwrite_data(struct Curl_easy *data,
nread = Curl_xfer_recv_resp(data, buf, bytestoread,
is_multiplex, &result);
if(nread < 0) {
- if(CURLE_AGAIN == result) {
- result = CURLE_OK;
- break; /* get out of loop */
+ if(CURLE_AGAIN != result)
+ goto out; /* real error */
+ result = CURLE_OK;
+ if(data->req.download_done && data->req.no_body &&
+ !data->req.resp_trailer) {
+ DEBUGF(infof(data, "EAGAIN, download done, no trailer announced, "
+ "not waiting for EOS"));
+ nread = 0;
+ /* continue as if we read the EOS */
}
- goto out; /* real error */
+ else
+ break; /* get out of loop */
}
/* We only get a 0-length read on EndOfStream */
@@ -313,10 +332,9 @@ static CURLcode readwrite_data(struct Curl_easy *data,
DEBUGF(infof(data, "nread == 0, stream closed, bailing"));
else
DEBUGF(infof(data, "nread <= 0, server closed connection, bailing"));
- /* stop receiving and ALL sending as well, including PAUSE and HOLD.
- * We might still be paused on receive client writes though, so
- * keep those bits around. */
- k->keepon &= ~(KEEP_RECV|KEEP_SENDBITS);
+ result = Curl_req_stop_send_recv(data);
+ if(result)
+ goto out;
if(k->eos_written) /* already did write this to client, leave */
break;
}
@@ -352,25 +370,21 @@ static CURLcode readwrite_data(struct Curl_easy *data,
may now close the connection. If there is 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 */
- k->keepon &= ~KEEP_SEND_PAUSE; /* no pausing anymore either */
+ Curl_req_abort_sending(data);
}
out:
Curl_multi_xfer_buf_release(data, xfer_buf);
if(result)
- DEBUGF(infof(data, "readwrite_data() -> %d", result));
+ DEBUGF(infof(data, "sendrecv_dl() -> %d", result));
return result;
}
/*
* Send data to upload to the server, when the socket is writable.
*/
-static CURLcode readwrite_upload(struct Curl_easy *data, int *didwhat)
+static CURLcode sendrecv_ul(struct Curl_easy *data, int *didwhat)
{
- if((data->req.keepon & KEEP_SEND_PAUSE))
- return CURLE_OK;
-
/* We should not get here when the sending is already done. It
* probably means that someone set `data-req.keepon |= KEEP_SEND`
* when it should not. */
@@ -402,56 +416,44 @@ static int select_bits_paused(struct Curl_easy *data, int select_bits)
}
/*
- * Curl_readwrite() is the low-level function to be called when data is to
+ * Curl_sendrecv() is the low-level function to be called when data is to
* be read and written to/from the connection.
*/
-CURLcode Curl_readwrite(struct Curl_easy *data)
+CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp)
{
- struct connectdata *conn = data->conn;
struct SingleRequest *k = &data->req;
- CURLcode result;
- struct curltime now;
+ CURLcode result = CURLE_OK;
int didwhat = 0;
- int select_bits;
+ int select_bits = 0;
+ DEBUGASSERT(nowp);
if(data->state.select_bits) {
if(select_bits_paused(data, data->state.select_bits)) {
/* leave the bits unchanged, so they'll tell us what to do when
* this transfer gets unpaused. */
- DEBUGF(infof(data, "readwrite, select_bits, early return on PAUSED"));
+ /* DEBUGF(infof(data, "sendrecv, select_bits, early return on PAUSED"));
+ */
result = CURLE_OK;
goto out;
}
- select_bits = data->state.select_bits;
data->state.select_bits = 0;
+ /* DEBUGF(infof(data, "sendrecv, select_bits %x, RUN", select_bits)); */
+ select_bits = (CURL_CSELECT_OUT|CURL_CSELECT_IN);
}
- else {
- curl_socket_t fd_read;
- curl_socket_t fd_write;
- /* 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;
-
- select_bits = Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write, 0);
+ else if(data->last_poll.num) {
+ /* The transfer wanted something polled. Let's run all available
+ * send/receives. Worst case we EAGAIN on some. */
+ /* DEBUGF(infof(data, "sendrecv, had poll sockets, RUN")); */
+ select_bits = (CURL_CSELECT_OUT|CURL_CSELECT_IN);
}
-
- if(select_bits == CURL_CSELECT_ERR) {
- failf(data, "select/poll returned error");
- result = CURLE_SEND_ERROR;
- goto out;
+ else if(data->req.keepon & KEEP_SEND_TIMED) {
+ /* DEBUGF(infof(data, "sendrecv, KEEP_SEND_TIMED, RUN ul")); */
+ select_bits = CURL_CSELECT_OUT;
}
#ifdef USE_HYPER
- if(conn->datastream) {
- result = conn->datastream(data, conn, &didwhat, select_bits);
+ if(data->conn->datastream) {
+ result = data->conn->datastream(data, data->conn, &didwhat, select_bits);
if(result || data->req.done)
goto out;
}
@@ -461,17 +463,15 @@ CURLcode Curl_readwrite(struct Curl_easy *data)
the stream was rewound (in which case we have data in a
buffer) */
if((k->keepon & KEEP_RECV) && (select_bits & CURL_CSELECT_IN)) {
- result = readwrite_data(data, k, &didwhat);
+ result = sendrecv_dl(data, k, &didwhat);
if(result || data->req.done)
goto out;
}
/* If we still have writing to do, we check if we have a writable socket. */
- if(((k->keepon & KEEP_SEND) && (select_bits & CURL_CSELECT_OUT)) ||
- (k->keepon & KEEP_SEND_TIMED)) {
- /* write */
-
- result = readwrite_upload(data, &didwhat);
+ if((Curl_req_want_send(data) || (data->req.keepon & KEEP_SEND_TIMED)) &&
+ (select_bits & CURL_CSELECT_OUT)) {
+ result = sendrecv_ul(data, &didwhat);
if(result)
goto out;
}
@@ -479,8 +479,8 @@ CURLcode Curl_readwrite(struct Curl_easy *data)
}
#endif
- now = Curl_now();
- if(!didwhat) {
+ if(select_bits && !didwhat) {
+ /* Transfer wanted to send/recv, but nothing was possible. */
result = Curl_conn_ev_data_idle(data);
if(result)
goto out;
@@ -489,23 +489,23 @@ CURLcode Curl_readwrite(struct Curl_easy *data)
if(Curl_pgrsUpdate(data))
result = CURLE_ABORTED_BY_CALLBACK;
else
- result = Curl_speedcheck(data, now);
+ result = Curl_speedcheck(data, *nowp);
if(result)
goto out;
if(k->keepon) {
- if(0 > Curl_timeleft(data, &now, FALSE)) {
+ if(0 > Curl_timeleft(data, nowp, 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(now, data->progress.t_startsingle),
+ failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
+ " milliseconds with %" FMT_OFF_T " out of %"
+ FMT_OFF_T " bytes received",
+ Curl_timediff(*nowp, 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),
+ failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
+ " milliseconds with %" FMT_OFF_T " bytes received",
+ Curl_timediff(*nowp, data->progress.t_startsingle),
k->bytecount);
}
result = CURLE_OPERATION_TIMEDOUT;
@@ -518,16 +518,8 @@ CURLcode Curl_readwrite(struct Curl_easy *data)
* returning.
*/
if(!(data->req.no_body) && (k->size != -1) &&
- (k->bytecount != k->size) &&
-#ifdef CURL_DO_LINEEND_CONV
- /* Most FTP servers do not adjust their file SIZE response for CRLFs,
- so we will check to see if the discrepancy can be explained
- by the number of CRLFs we have 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
+ (k->bytecount != k->size) && !k->newurl) {
+ failf(data, "transfer closed with %" FMT_OFF_T
" bytes remaining to read", k->size - k->bytecount);
result = CURLE_PARTIAL_FILE;
goto out;
@@ -544,7 +536,7 @@ CURLcode Curl_readwrite(struct Curl_easy *data)
out:
if(result)
- DEBUGF(infof(data, "Curl_readwrite() -> %d", result));
+ DEBUGF(infof(data, "Curl_sendrecv() -> %d", result));
return result;
}
@@ -1193,15 +1185,7 @@ CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
int cwtype = CLIENTWRITE_BODY;
if(is_eos)
cwtype |= CLIENTWRITE_EOS;
-
-#ifndef CURL_DISABLE_POP3
- if(blen && data->conn->handler->protocol & PROTO_FAMILY_POP3) {
- result = data->req.ignorebody? CURLE_OK :
- Curl_pop3_write(data, buf, blen);
- }
- else
-#endif /* CURL_DISABLE_POP3 */
- result = Curl_client_write(data, cwtype, buf, blen);
+ result = Curl_client_write(data, cwtype, buf, blen);
}
}
@@ -1233,25 +1217,35 @@ CURLcode Curl_xfer_write_done(struct Curl_easy *data, bool premature)
return Curl_cw_out_done(data);
}
+bool Curl_xfer_needs_flush(struct Curl_easy *data)
+{
+ int sockindex;
+ sockindex = ((data->conn->writesockfd != CURL_SOCKET_BAD) &&
+ (data->conn->writesockfd == data->conn->sock[SECONDARYSOCKET]));
+ return Curl_conn_needs_flush(data, sockindex);
+}
+
+CURLcode Curl_xfer_flush(struct Curl_easy *data)
+{
+ int sockindex;
+ sockindex = ((data->conn->writesockfd != CURL_SOCKET_BAD) &&
+ (data->conn->writesockfd == data->conn->sock[SECONDARYSOCKET]));
+ return Curl_conn_flush(data, sockindex);
+}
+
CURLcode Curl_xfer_send(struct Curl_easy *data,
- const void *buf, size_t blen,
+ const void *buf, size_t blen, bool eos,
size_t *pnwritten)
{
CURLcode result;
int sockindex;
- if(!data || !data->conn)
- return CURLE_FAILED_INIT;
- /* FIXME: would like to enable this, but some protocols (MQTT) do not
- * setup the transfer correctly, it seems
- if(data->conn->writesockfd == CURL_SOCKET_BAD) {
- failf(data, "transfer not setup for sending");
- DEBUGASSERT(0);
- return CURLE_SEND_ERROR;
- } */
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->conn);
+
sockindex = ((data->conn->writesockfd != CURL_SOCKET_BAD) &&
(data->conn->writesockfd == data->conn->sock[SECONDARYSOCKET]));
- result = Curl_conn_send(data, sockindex, buf, blen, pnwritten);
+ result = Curl_conn_send(data, sockindex, buf, blen, eos, pnwritten);
if(result == CURLE_AGAIN) {
result = CURLE_OK;
*pnwritten = 0;
@@ -1259,6 +1253,8 @@ CURLcode Curl_xfer_send(struct Curl_easy *data,
else if(!result && *pnwritten)
data->info.request_size += *pnwritten;
+ DEBUGF(infof(data, "Curl_xfer_send(len=%zu) -> %d, %zu",
+ blen, result, *pnwritten));
return result;
}
@@ -1268,18 +1264,13 @@ CURLcode Curl_xfer_recv(struct Curl_easy *data,
{
int sockindex;
- if(!data || !data->conn)
- return CURLE_FAILED_INIT;
- /* FIXME: would like to enable this, but some protocols (MQTT) do not
- * setup the transfer correctly, it seems
- if(data->conn->sockfd == CURL_SOCKET_BAD) {
- failf(data, "transfer not setup for receiving");
- DEBUGASSERT(0);
- return CURLE_RECV_ERROR;
- } */
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->conn);
+ DEBUGASSERT(data->set.buffer_size > 0);
+
sockindex = ((data->conn->sockfd != CURL_SOCKET_BAD) &&
(data->conn->sockfd == data->conn->sock[SECONDARYSOCKET]));
- if(data->set.buffer_size > 0 && (size_t)data->set.buffer_size < blen)
+ if((size_t)data->set.buffer_size < blen)
blen = (size_t)data->set.buffer_size;
return Curl_conn_recv(data, sockindex, buf, blen, pnrcvd);
}
@@ -1290,18 +1281,6 @@ CURLcode Curl_xfer_send_close(struct Curl_easy *data)
return CURLE_OK;
}
-CURLcode Curl_xfer_send_shutdown(struct Curl_easy *data, bool *done)
-{
- int sockindex;
-
- if(!data || !data->conn)
- return CURLE_FAILED_INIT;
- if(data->conn->writesockfd == CURL_SOCKET_BAD)
- return CURLE_FAILED_INIT;
- sockindex = (data->conn->writesockfd == data->conn->sock[SECONDARYSOCKET]);
- return Curl_conn_shutdown(data, sockindex, done);
-}
-
bool Curl_xfer_is_blocked(struct Curl_easy *data)
{
bool want_send = ((data)->req.keepon & KEEP_SEND);
diff --git a/libs/libcurl/src/transfer.h b/libs/libcurl/src/transfer.h
index 04adc30fac..75ffc23e31 100644
--- a/libs/libcurl/src/transfer.h
+++ b/libs/libcurl/src/transfer.h
@@ -44,7 +44,7 @@ typedef enum {
CURLcode Curl_follow(struct Curl_easy *data, char *newurl,
followtype type);
-CURLcode Curl_readwrite(struct Curl_easy *data);
+CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp);
int Curl_single_getsock(struct Curl_easy *data,
struct connectdata *conn, curl_socket_t *socks);
CURLcode Curl_retry_request(struct Curl_easy *data, char **url);
@@ -114,12 +114,23 @@ void Curl_xfer_setup2(struct Curl_easy *data,
CURLcode Curl_xfer_write_done(struct Curl_easy *data, bool premature);
/**
+ * Return TRUE iff transfer has pending data to send. Checks involved
+ * connection filters.
+ */
+bool Curl_xfer_needs_flush(struct Curl_easy *data);
+
+/**
+ * Flush any pending send data on the transfer connection.
+ */
+CURLcode Curl_xfer_flush(struct Curl_easy *data);
+
+/**
* Send data on the socket/connection filter designated
* for transfer's outgoing data.
* Will return CURLE_OK on blocking with (*pnwritten == 0).
*/
CURLcode Curl_xfer_send(struct Curl_easy *data,
- const void *buf, size_t blen,
+ const void *buf, size_t blen, bool eos,
size_t *pnwritten);
/**
diff --git a/libs/libcurl/src/url.c b/libs/libcurl/src/url.c
index 0f623b955b..5977a41071 100644
--- a/libs/libcurl/src/url.c
+++ b/libs/libcurl/src/url.c
@@ -234,8 +234,6 @@ CURLcode Curl_close(struct Curl_easy **datap)
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);
@@ -253,6 +251,8 @@ CURLcode Curl_close(struct Curl_easy **datap)
}
}
+ Curl_expire_clear(data); /* shut off any timers left */
+
data->magic = 0; /* force a clear AFTER the possibly enforced removal from
the multi handle, since that function uses the magic
field! */
@@ -266,7 +266,6 @@ CURLcode Curl_close(struct Curl_easy **datap)
/* 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);
if(data->state.referer_alloc) {
@@ -415,8 +414,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
set->new_file_perms = 0644; /* Default permissions */
set->allowed_protocols = (curl_prot_t) CURLPROTO_ALL;
- set->redir_protocols = CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP |
- CURLPROTO_FTPS;
+ set->redir_protocols = CURLPROTO_REDIR;
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
/*
@@ -537,9 +535,16 @@ CURLcode Curl_open(struct Curl_easy **curl)
data->state.recent_conn_id = -1;
/* and not assigned an id yet */
data->id = -1;
+ data->mid = -1;
+#ifndef CURL_DISABLE_DOH
+ data->set.dohfor_mid = -1;
+#endif
data->progress.flags |= PGRS_HIDE;
data->state.current_speed = -1; /* init to negative == impossible */
+#ifndef CURL_DISABLE_HTTP
+ Curl_llist_init(&data->state.httphdrs, NULL);
+#endif
}
if(result) {
@@ -552,7 +557,6 @@ CURLcode Curl_open(struct Curl_easy **curl)
}
else
*curl = data;
-
return result;
}
@@ -593,13 +597,14 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn)
#ifdef USE_UNIX_SOCKETS
Curl_safefree(conn->unix_domain_socket);
#endif
+ Curl_safefree(conn->destination);
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
+ * primary connection, like when freeing room in the connection pool or
* killing of a dead old connection.
*
* A connection needs an easy handle when closing down. We support this passed
@@ -609,14 +614,14 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn)
* This function MUST NOT reset state in the Curl_easy struct if that
* is not strictly bound to the life-time of *this* particular connection.
*/
-void Curl_disconnect(struct Curl_easy *data,
- struct connectdata *conn, bool aborted)
+bool Curl_on_disconnect(struct Curl_easy *data,
+ struct connectdata *conn, bool aborted)
{
/* there must be a connection to close */
DEBUGASSERT(conn);
- /* it must be removed from the connection cache */
- DEBUGASSERT(!conn->bundle);
+ /* it must be removed from the connection pool */
+ DEBUGASSERT(!conn->bits.in_cpool);
/* there must be an associated transfer */
DEBUGASSERT(data);
@@ -624,23 +629,11 @@ void Curl_disconnect(struct Curl_easy *data,
/* the transfer must be detached from the connection */
DEBUGASSERT(!data->conn);
- DEBUGF(infof(data, "Curl_disconnect(conn #%"
- CURL_FORMAT_CURL_OFF_T ", aborted=%d)",
+ DEBUGF(infof(data, "Curl_disconnect(conn #%" FMT_OFF_T ", aborted=%d)",
conn->connection_id, aborted));
- /*
- * If this connection is not marked to force-close, leave it open if there
- * are other users of it
- */
- if(CONN_INUSE(conn) && !aborted) {
- 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;
- }
+ if(conn->dns_entry)
+ Curl_resolv_unlink(data, &conn->dns_entry);
/* Cleanup NTLM connection-related data */
Curl_http_auth_cleanup_ntlm(conn);
@@ -652,30 +645,28 @@ void Curl_disconnect(struct Curl_easy *data,
/* treat the connection as aborted in CONNECT_ONLY situations */
aborted = TRUE;
- Curl_conncache_disconnect(data, conn, aborted);
+ return aborted;
}
/*
- * IsMultiplexingPossible()
+ * Curl_xfer_may_multiplex()
*
- * Return a bitmask with the available multiplexing options for the given
- * requested connection.
+ * Return a TRUE, iff the transfer can be done over an (appropriate)
+ * multiplexed connection.
*/
-static int IsMultiplexingPossible(const struct Curl_easy *handle,
- const struct connectdata *conn)
+static bool Curl_xfer_may_multiplex(const struct Curl_easy *data,
+ 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;
+ if(Curl_multiplex_wanted(data->multi) &&
+ (data->state.httpwant >= CURL_HTTP_VERSION_2))
+ /* allows HTTP/2 or newer */
+ return TRUE;
}
- return avail;
+ return FALSE;
}
#ifndef CURL_DISABLE_PROXY
@@ -729,7 +720,7 @@ static bool conn_maxage(struct Curl_easy *data,
idletime /= 1000; /* integer seconds is fine */
if(idletime > data->set.maxage_conn) {
- infof(data, "Too old connection (%" CURL_FORMAT_TIMEDIFF_T
+ infof(data, "Too old connection (%" FMT_TIMEDIFF_T
" seconds idle), disconnect it", idletime);
return TRUE;
}
@@ -739,7 +730,7 @@ static bool conn_maxage(struct Curl_easy *data,
if(data->set.maxlifetime_conn && lifetime > data->set.maxlifetime_conn) {
infof(data,
- "Too old connection (%" CURL_FORMAT_TIMEDIFF_T
+ "Too old connection (%" FMT_TIMEDIFF_T
" seconds since creation), disconnect it", lifetime);
return TRUE;
}
@@ -749,23 +740,24 @@ static bool conn_maxage(struct Curl_easy *data,
}
/*
- * This function checks if the given connection is dead and prunes 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 pruned.
+ * Return TRUE iff the given connection is considered dead.
*/
-static bool prune_if_dead(struct connectdata *conn,
- struct Curl_easy *data)
+bool Curl_conn_seems_dead(struct connectdata *conn,
+ struct Curl_easy *data,
+ struct curltime *pnow)
{
+ DEBUGASSERT(!data->conn);
if(!CONN_INUSE(conn)) {
/* The check for a dead socket makes sense only if the connection is not in
use */
bool dead;
- struct curltime now = Curl_now();
- if(conn_maxage(data, conn, now)) {
+ struct curltime now;
+ if(!pnow) {
+ now = Curl_now();
+ pnow = &now;
+ }
+
+ if(conn_maxage(data, conn, *pnow)) {
/* avoid check if already too old */
dead = TRUE;
}
@@ -805,64 +797,40 @@ static bool prune_if_dead(struct connectdata *conn,
}
if(dead) {
- /* remove connection from cache */
- infof(data, "Connection %" CURL_FORMAT_CURL_OFF_T " seems to be dead",
+ /* remove connection from cpool */
+ infof(data, "Connection %" FMT_OFF_T " seems to be dead",
conn->connection_id);
- Curl_conncache_remove_conn(data, conn, FALSE);
return TRUE;
}
}
return FALSE;
}
-/*
- * Wrapper to use prune_if_dead() function in Curl_conncache_foreach()
- *
- */
-static int call_prune_if_dead(struct Curl_easy *data,
- struct connectdata *conn, void *param)
+CURLcode Curl_conn_upkeep(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct curltime *now)
{
- struct connectdata **pruned = (struct connectdata **)param;
- if(prune_if_dead(conn, data)) {
- /* stop the iteration here, pass back the connection that was pruned */
- *pruned = 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 connectdata *pruned = NULL;
- while(Curl_conncache_foreach(data, data->state.conn_cache, &pruned,
- call_prune_if_dead)) {
- /* unlocked */
-
- /* connection previously removed from cache in prune_if_dead() */
+ CURLcode result = CURLE_OK;
+ if(Curl_timediff(*now, conn->keepalive) <= data->set.upkeep_interval_ms)
+ return result;
- /* disconnect it, do not treat as aborted */
- Curl_disconnect(data, pruned, FALSE);
- }
- CONNCACHE_LOCK(data);
- data->state.conn_cache->last_cleanup = now;
- CONNCACHE_UNLOCK(data);
+ /* briefly attach for action */
+ Curl_attach_connection(data, conn);
+ if(conn->handler->connection_check) {
+ /* Do a protocol-specific keepalive check on the connection. */
+ unsigned int rc;
+ rc = conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE);
+ if(rc & CONNRESULT_DEAD)
+ result = CURLE_RECV_ERROR;
+ }
+ else {
+ /* Do the generic action on the FIRSTSOCKET filter chain */
+ result = Curl_conn_keep_alive(data, conn, FIRSTSOCKET);
}
+ Curl_detach_connection(data);
+
+ conn->keepalive = *now;
+ return result;
}
#ifdef USE_SSH
@@ -876,425 +844,420 @@ static bool ssh_config_matches(struct connectdata *one,
#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 url_conn_match {
+ struct connectdata *found;
+ struct Curl_easy *data;
+ struct connectdata *needle;
+ BIT(may_multiplex);
+ BIT(want_ntlm_http);
+ BIT(want_proxy_ntlm_http);
+
+ BIT(wait_pipe);
+ BIT(force_reuse);
+ BIT(seen_pending_conn);
+ BIT(seen_single_use_conn);
+ BIT(seen_multiplex_conn);
+};
+
+static bool url_match_conn(struct connectdata *conn, void *userdata)
{
- struct connectdata *chosen = NULL;
- bool foundPendingCandidate = FALSE;
- bool canmultiplex = FALSE;
- struct connectbundle *bundle;
- struct Curl_llist_element *curr;
+ struct url_conn_match *match = userdata;
+ struct Curl_easy *data = match->data;
+ struct connectdata *needle = match->needle;
-#ifdef USE_NTLM
- bool wantNTLMhttp = ((data->state.authhost.want & CURLAUTH_NTLM) &&
- (needle->handler->protocol & PROTO_FAMILY_HTTP));
-#ifndef CURL_DISABLE_PROXY
- bool wantProxyNTLMhttp = (needle->bits.proxy_user_passwd &&
- ((data->state.authproxy.want &
- CURLAUTH_NTLM) &&
- (needle->handler->protocol & PROTO_FAMILY_HTTP)));
-#else
- bool wantProxyNTLMhttp = FALSE;
-#endif
-#endif
- /* plain HTTP with upgrade */
- bool h2upgrade = (data->state.httpwant == CURL_HTTP_VERSION_2_0) &&
- (needle->handler->protocol & CURLPROTO_HTTP);
+ /* Check if `conn` can be used for transfer `data` */
- *usethis = NULL;
- *force_reuse = FALSE;
- *waitpipe = FALSE;
+ if(conn->connect_only || conn->bits.close)
+ /* connect-only or to-be-closed connections will not be reused */
+ return 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) {
- CONNCACHE_UNLOCK(data);
+ if(data->set.ipver != CURL_IPRESOLVE_WHATEVER
+ && data->set.ipver != conn->ip_version) {
+ /* skip because the connection is not via the requested IP version */
return FALSE;
}
- infof(data, "Found bundle for host: %p [%s]",
- (void *)bundle, (bundle->multiuse == BUNDLE_MULTIPLEX ?
- "can multiplex" : "serially"));
-
- /* We can only multiplex iff the transfer allows it AND we know
- * that the server we want to talk to supports it as well. */
- canmultiplex = FALSE;
- if(IsMultiplexingPossible(data, needle)) {
- if(bundle->multiuse == BUNDLE_UNKNOWN) {
- if(data->set.pipewait) {
- infof(data, "Server does not support multiplex yet, wait");
- *waitpipe = TRUE;
- CONNCACHE_UNLOCK(data);
- return FALSE; /* no reuse */
- }
- infof(data, "Server does not support multiplex (yet)");
- }
- else if(bundle->multiuse == BUNDLE_MULTIPLEX) {
- if(Curl_multiplex_wanted(data->multi))
- canmultiplex = TRUE;
- else
- infof(data, "Could multiplex, but not asked to");
- }
- else if(bundle->multiuse == BUNDLE_NO_MULTIUSE) {
- infof(data, "Can not multiplex, even if we wanted to");
- }
- }
-
- curr = bundle->conn_list.head;
- while(curr) {
- struct connectdata *check = curr->ptr;
- /* Get next node now. We might remove a dead `check` connection which
- * would invalidate `curr` as well. */
- curr = curr->next;
- /* 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.
- */
- if(check->connect_only || check->bits.close)
- /* connect-only or to-be-closed connections will not be reused */
- continue;
+ if(needle->localdev || needle->localport) {
+ /* If we are bound to a specific local end (IP+port), we must not
+ reuse a random other one, although if we did not 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 reuses of bound connections
+ will most likely also reuse the exact same binding parameters and
+ missing out a few edge cases should not hurt anyone very much.
+ */
+ if((conn->localport != needle->localport) ||
+ (conn->localportrange != needle->localportrange) ||
+ (needle->localdev &&
+ (!conn->localdev || strcmp(conn->localdev, needle->localdev))))
+ return FALSE;
+ }
+
+ if(needle->bits.conn_to_host != conn->bits.conn_to_host)
+ /* do not mix connections that use the "connect to host" feature and
+ * connections that do not use this feature */
+ return FALSE;
- 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(needle->bits.conn_to_port != conn->bits.conn_to_port)
+ /* do not mix connections that use the "connect to port" feature and
+ * connections that do not use this feature */
+ return FALSE;
- if(!canmultiplex) {
- if(Curl_resolver_asynch() &&
- /* remote_ip[0] is NUL only if the resolving of the name has not
- completed yet and until then we do not reuse this connection */
- !check->primary.remote_ip[0])
- continue;
+ if(!Curl_conn_is_connected(conn, FIRSTSOCKET) ||
+ conn->bits.asks_multiplex) {
+ /* Not yet connected, or not yet decided if it multiplexes. The later
+ * happens for HTTP/2 Upgrade: requests that need a response. */
+ if(match->may_multiplex) {
+ match->seen_pending_conn = TRUE;
+ /* Do not pick a connection that has not connected yet */
+ infof(data, "Connection #%" FMT_OFF_T
+ " is not open enough, cannot reuse", conn->connection_id);
}
+ /* Do not pick a connection that has not connected yet */
+ return FALSE;
+ }
+ /* `conn` is connected. If it has transfers, can we add ours to it? */
- if(CONN_INUSE(check)) {
- if(!canmultiplex) {
- /* transfer cannot be multiplexed and check is in use */
- continue;
- }
- else {
- /* Could multiplex, but not when check belongs to another multi */
- struct Curl_llist_element *e = check->easyq.head;
- struct Curl_easy *entry = e->ptr;
- if(entry->multi != data->multi)
- continue;
- }
+ if(CONN_INUSE(conn)) {
+ if(!conn->bits.multiplex) {
+ /* conn busy and conn cannot take more transfers */
+ match->seen_single_use_conn = TRUE;
+ return FALSE;
}
-
- if(!Curl_conn_is_connected(check, FIRSTSOCKET)) {
- foundPendingCandidate = TRUE;
- /* Do not pick a connection that has not connected yet */
- infof(data, "Connection #%" CURL_FORMAT_CURL_OFF_T
- " is not open enough, cannot reuse", check->connection_id);
- continue;
+ match->seen_multiplex_conn = TRUE;
+ if(!match->may_multiplex)
+ /* conn busy and transfer cannot be multiplexed */
+ return FALSE;
+ else {
+ /* transfer and conn multiplex. Are they on the same multi? */
+ struct Curl_llist_node *e = Curl_llist_head(&conn->easyq);
+ struct Curl_easy *entry = Curl_node_elem(e);
+ if(entry->multi != data->multi)
+ return FALSE;
}
+ }
+ /* `conn` is connected and we could add the transfer to it, if
+ * all the other criteria do match. */
- /* `check` is connected. if it is in use and does not support multiplex,
- * we cannot use it. */
- if(!check->bits.multiplex && CONN_INUSE(check))
- continue;
-
+ /* Does `conn` use the correct protocol? */
#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))
- /* do not 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;
-
- if(needle->bits.conn_to_host != check->bits.conn_to_host)
- /* do not mix connections that use the "connect to host" feature and
- * connections that do not use this feature */
- continue;
-
- if(needle->bits.conn_to_port != check->bits.conn_to_port)
- /* do not mix connections that use the "connect to port" feature and
- * connections that do not use this feature */
- continue;
+ if(needle->unix_domain_socket) {
+ if(!conn->unix_domain_socket)
+ return FALSE;
+ if(strcmp(needle->unix_domain_socket, conn->unix_domain_socket))
+ return FALSE;
+ if(needle->bits.abstract_unix_socket != conn->bits.abstract_unix_socket)
+ return FALSE;
+ }
+ else if(conn->unix_domain_socket)
+ return FALSE;
+#endif
+
+ if((needle->handler->flags&PROTOPT_SSL) !=
+ (conn->handler->flags&PROTOPT_SSL))
+ /* do not do mixed SSL and non-SSL connections */
+ if(get_protocol_family(conn->handler) !=
+ needle->handler->protocol || !conn->bits.tls_upgraded)
+ /* except protocols that have been upgraded via TLS */
+ return FALSE;
#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;
-
- if(needle->bits.httpproxy) {
- if(needle->bits.tunnel_proxy != check->bits.tunnel_proxy)
- continue;
-
- if(!proxy_info_matches(&needle->http_proxy, &check->http_proxy))
- continue;
-
- if(IS_HTTPS_PROXY(needle->http_proxy.proxytype)) {
- /* https proxies come in different types, http/1.1, h2, ... */
- if(needle->http_proxy.proxytype != check->http_proxy.proxytype)
- continue;
- /* match SSL config to proxy */
- if(!Curl_ssl_conn_config_match(data, check, TRUE)) {
- DEBUGF(infof(data,
- "Connection #%" CURL_FORMAT_CURL_OFF_T
- " has different SSL proxy parameters, cannot reuse",
- check->connection_id));
- continue;
- }
- /* the SSL config to the server, which may apply here is checked
- * further below */
+ if(needle->bits.httpproxy != conn->bits.httpproxy ||
+ needle->bits.socksproxy != conn->bits.socksproxy)
+ return FALSE;
+
+ if(needle->bits.socksproxy &&
+ !socks_proxy_info_matches(&needle->socks_proxy,
+ &conn->socks_proxy))
+ return FALSE;
+
+ if(needle->bits.httpproxy) {
+ if(needle->bits.tunnel_proxy != conn->bits.tunnel_proxy)
+ return FALSE;
+
+ if(!proxy_info_matches(&needle->http_proxy, &conn->http_proxy))
+ return FALSE;
+
+ if(IS_HTTPS_PROXY(needle->http_proxy.proxytype)) {
+ /* https proxies come in different types, http/1.1, h2, ... */
+ if(needle->http_proxy.proxytype != conn->http_proxy.proxytype)
+ return FALSE;
+ /* match SSL config to proxy */
+ if(!Curl_ssl_conn_config_match(data, conn, TRUE)) {
+ DEBUGF(infof(data,
+ "Connection #%" FMT_OFF_T
+ " has different SSL proxy parameters, cannot reuse",
+ conn->connection_id));
+ return FALSE;
}
+ /* the SSL config to the server, which may apply here is checked
+ * further below */
}
+ }
#endif
- if(h2upgrade && !check->httpversion && canmultiplex) {
- if(data->set.pipewait) {
- infof(data, "Server upgrade does not support multiplex yet, wait");
- *waitpipe = TRUE;
- CONNCACHE_UNLOCK(data);
- return FALSE; /* no reuse */
- }
- infof(data, "Server upgrade cannot be used");
- continue; /* cannot be used atm */
- }
-
- if(needle->localdev || needle->localport) {
- /* If we are bound to a specific local end (IP+port), we must not
- reuse a random other one, although if we did not 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 reuses of bound connections
- will most likely also reuse the exact same binding parameters and
- missing out a few edge cases should not 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 are 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(match->may_multiplex &&
+ (data->state.httpwant == CURL_HTTP_VERSION_2_0) &&
+ (needle->handler->protocol & CURLPROTO_HTTP) &&
+ !conn->httpversion) {
+ if(data->set.pipewait) {
+ infof(data, "Server upgrade does not support multiplex yet, wait");
+ match->found = NULL;
+ match->wait_pipe = TRUE;
+ return TRUE; /* stop searching, we want to wait */
}
+ infof(data, "Server upgrade cannot be used");
+ return FALSE;
+ }
- /* GSS delegation differences do not actually affect every connection
- and auth method, but this check takes precaution before efficiency */
- if(needle->gssapi_delegation != check->gssapi_delegation)
- continue;
+ if(!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) {
+ /* This protocol requires credentials per connection,
+ so verify that we are using the same name and password as well */
+ if(Curl_timestrcmp(needle->user, conn->user) ||
+ Curl_timestrcmp(needle->passwd, conn->passwd) ||
+ Curl_timestrcmp(needle->sasl_authzid, conn->sasl_authzid) ||
+ Curl_timestrcmp(needle->oauth_bearer, conn->oauth_bearer)) {
+ /* one of them was different */
+ return FALSE;
+ }
+ }
- /* If looking for HTTP and the HTTP version we want is less
- * than the HTTP version of the check connection, continue looking */
- if((needle->handler->protocol & PROTO_FAMILY_HTTP) &&
- (((check->httpversion >= 20) &&
- (data->state.httpwant < CURL_HTTP_VERSION_2_0))
- || ((check->httpversion >= 30) &&
- (data->state.httpwant < CURL_HTTP_VERSION_3))))
- continue;
+ /* GSS delegation differences do not actually affect every connection
+ and auth method, but this check takes precaution before efficiency */
+ if(needle->gssapi_delegation != conn->gssapi_delegation)
+ return FALSE;
+
+ /* If looking for HTTP and the HTTP version we want is less
+ * than the HTTP version of conn, continue looking */
+ if((needle->handler->protocol & PROTO_FAMILY_HTTP) &&
+ (((conn->httpversion >= 20) &&
+ (data->state.httpwant < CURL_HTTP_VERSION_2_0))
+ || ((conn->httpversion >= 30) &&
+ (data->state.httpwant < CURL_HTTP_VERSION_3))))
+ return FALSE;
#ifdef USE_SSH
- else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) {
- if(!ssh_config_matches(needle, check))
- continue;
- }
+ else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) {
+ if(!ssh_config_matches(needle, conn))
+ return FALSE;
+ }
#endif
#ifndef CURL_DISABLE_FTP
- else if(get_protocol_family(needle->handler) & PROTO_FAMILY_FTP) {
- /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */
- if(Curl_timestrcmp(needle->proto.ftpc.account,
- check->proto.ftpc.account) ||
- Curl_timestrcmp(needle->proto.ftpc.alternative_to_user,
- check->proto.ftpc.alternative_to_user) ||
- (needle->proto.ftpc.use_ssl != check->proto.ftpc.use_ssl) ||
- (needle->proto.ftpc.ccc != check->proto.ftpc.ccc))
- continue;
- }
+ else if(get_protocol_family(needle->handler) & PROTO_FAMILY_FTP) {
+ /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */
+ if(Curl_timestrcmp(needle->proto.ftpc.account,
+ conn->proto.ftpc.account) ||
+ Curl_timestrcmp(needle->proto.ftpc.alternative_to_user,
+ conn->proto.ftpc.alternative_to_user) ||
+ (needle->proto.ftpc.use_ssl != conn->proto.ftpc.use_ssl) ||
+ (needle->proto.ftpc.ccc != conn->proto.ftpc.ccc))
+ return FALSE;
+ }
#endif
- /* Additional match requirements if talking TLS OR
- * not talking to an HTTP proxy OR using a tunnel through a proxy */
- if((needle->handler->flags&PROTOPT_SSL)
+ /* Additional match requirements if talking TLS OR
+ * not talking to an HTTP proxy OR using a tunnel through a proxy */
+ if((needle->handler->flags&PROTOPT_SSL)
#ifndef CURL_DISABLE_PROXY
- || !needle->bits.httpproxy || needle->bits.tunnel_proxy
-#endif
- ) {
- /* Talking the same protocol scheme or a TLS upgraded protocol in the
- * same protocol family? */
- if(!strcasecompare(needle->handler->scheme, check->handler->scheme) &&
- (get_protocol_family(check->handler) !=
- needle->handler->protocol || !check->bits.tls_upgraded))
- continue;
-
- /* If needle has "conn_to_*" set, check must match this */
- if((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))
- continue;
-
- /* hostname and port must match */
- if(!strcasecompare(needle->host.name, check->host.name) ||
- needle->remote_port != check->remote_port)
- continue;
-
- /* If talking TLS, check needs to use the same SSL options. */
- if((needle->handler->flags & PROTOPT_SSL) &&
- !Curl_ssl_conn_config_match(data, check, FALSE)) {
- DEBUGF(infof(data,
- "Connection #%" CURL_FORMAT_CURL_OFF_T
- " has different SSL parameters, cannot reuse",
- check->connection_id));
- continue;
- }
+ || !needle->bits.httpproxy || needle->bits.tunnel_proxy
+#endif
+ ) {
+ /* Talking the same protocol scheme or a TLS upgraded protocol in the
+ * same protocol family? */
+ if(!strcasecompare(needle->handler->scheme, conn->handler->scheme) &&
+ (get_protocol_family(conn->handler) !=
+ needle->handler->protocol || !conn->bits.tls_upgraded))
+ return FALSE;
+
+ /* If needle has "conn_to_*" set, conn must match this */
+ if((needle->bits.conn_to_host && !strcasecompare(
+ needle->conn_to_host.name, conn->conn_to_host.name)) ||
+ (needle->bits.conn_to_port &&
+ needle->conn_to_port != conn->conn_to_port))
+ return FALSE;
+
+ /* hostname and port must match */
+ if(!strcasecompare(needle->host.name, conn->host.name) ||
+ needle->remote_port != conn->remote_port)
+ return FALSE;
+
+ /* If talking TLS, conn needs to use the same SSL options. */
+ if((needle->handler->flags & PROTOPT_SSL) &&
+ !Curl_ssl_conn_config_match(data, conn, FALSE)) {
+ DEBUGF(infof(data,
+ "Connection #%" FMT_OFF_T
+ " has different SSL parameters, cannot reuse",
+ conn->connection_id));
+ return FALSE;
}
+ }
#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 do not want NTLM */
- continue;
- }
+ /* 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(match->want_ntlm_http) {
+ if(Curl_timestrcmp(needle->user, conn->user) ||
+ Curl_timestrcmp(needle->passwd, conn->passwd)) {
+
+ /* we prefer a credential match, but this is at least a connection
+ that can be reused and "upgraded" to NTLM */
+ if(conn->http_ntlm_state == NTLMSTATE_NONE)
+ match->found = conn;
+ return FALSE;
+ }
+ }
+ else if(conn->http_ntlm_state != NTLMSTATE_NONE) {
+ /* Connection is using NTLM auth but we do not want NTLM */
+ return FALSE;
+ }
#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 do not want NTLM */
- continue;
- }
-#endif
- if(wantNTLMhttp || wantProxyNTLMhttp) {
- /* Credentials are already checked, we may use this connection.
- * With NTLM being weird as it is, we MUST use a
- * connection where it has already been fully negotiated.
- * If it has not, we keep on looking for a better one. */
- 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;
+ /* Same for Proxy NTLM authentication */
+ if(match->want_proxy_ntlm_http) {
+ /* Both conn->http_proxy.user and conn->http_proxy.passwd can be
+ * NULL */
+ if(!conn->http_proxy.user || !conn->http_proxy.passwd)
+ return FALSE;
+
+ if(Curl_timestrcmp(needle->http_proxy.user,
+ conn->http_proxy.user) ||
+ Curl_timestrcmp(needle->http_proxy.passwd,
+ conn->http_proxy.passwd))
+ return FALSE;
+ }
+ else if(conn->proxy_ntlm_state != NTLMSTATE_NONE) {
+ /* Proxy connection is using NTLM auth but we do not want NTLM */
+ return FALSE;
+ }
+#endif
+ if(match->want_ntlm_http || match->want_proxy_ntlm_http) {
+ /* Credentials are already checked, we may use this connection.
+ * With NTLM being weird as it is, we MUST use a
+ * connection where it has already been fully negotiated.
+ * If it has not, we keep on looking for a better one. */
+ match->found = conn;
+
+ if((match->want_ntlm_http &&
+ (conn->http_ntlm_state != NTLMSTATE_NONE)) ||
+ (match->want_proxy_ntlm_http &&
+ (conn->proxy_ntlm_state != NTLMSTATE_NONE))) {
+ /* We must use this connection, no other */
+ match->force_reuse = TRUE;
+ return TRUE;
}
+ /* Continue look up for a better connection */
+ return FALSE;
+ }
#endif
- if(CONN_INUSE(check)) {
- DEBUGASSERT(canmultiplex);
- DEBUGASSERT(check->bits.multiplex);
- /* If multiplexed, make sure we do not go over concurrency limit */
- if(CONN_INUSE(check) >=
- Curl_multi_max_concurrent_streams(data->multi)) {
- infof(data, "client side MAX_CONCURRENT_STREAMS reached"
- ", skip (%zu)", CONN_INUSE(check));
- continue;
- }
- if(CONN_INUSE(check) >=
- Curl_conn_get_max_concurrent(data, check, FIRSTSOCKET)) {
- infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)",
- CONN_INUSE(check));
- continue;
- }
- /* When not multiplexed, we have a match here! */
- infof(data, "Multiplexed connection found");
+ if(CONN_INUSE(conn)) {
+ DEBUGASSERT(match->may_multiplex);
+ DEBUGASSERT(conn->bits.multiplex);
+ /* If multiplexed, make sure we do not go over concurrency limit */
+ if(CONN_INUSE(conn) >=
+ Curl_multi_max_concurrent_streams(data->multi)) {
+ infof(data, "client side MAX_CONCURRENT_STREAMS reached"
+ ", skip (%zu)", CONN_INUSE(conn));
+ return FALSE;
}
- else if(prune_if_dead(check, data)) {
- /* disconnect it, do not treat as aborted */
- Curl_disconnect(data, check, FALSE);
- continue;
+ if(CONN_INUSE(conn) >=
+ Curl_conn_get_max_concurrent(data, conn, FIRSTSOCKET)) {
+ infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)",
+ CONN_INUSE(conn));
+ return FALSE;
}
+ /* When not multiplexed, we have a match here! */
+ infof(data, "Multiplexed connection found");
+ }
+ else if(Curl_conn_seems_dead(conn, data, NULL)) {
+ /* removed and disconnect. Do not treat as aborted. */
+ Curl_cpool_disconnect(data, conn, FALSE);
+ return FALSE;
+ }
- /* We have found a connection. Let's stop searching. */
- chosen = check;
- break;
- } /* loop over connection bundle */
+ /* We have found a connection. Let's stop searching. */
+ match->found = conn;
+ return TRUE;
+}
- 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! */
+static bool url_match_result(bool result, void *userdata)
+{
+ struct url_conn_match *match = userdata;
+ (void)result;
+ if(match->found) {
+ /* Attach it now while still under lock, so the connection does
+ * no longer appear idle and can be reaped. */
+ Curl_attach_connection(match->data, match->found);
+ return TRUE;
}
- CONNCACHE_UNLOCK(data);
-
- if(foundPendingCandidate && data->set.pipewait) {
- infof(data,
+ else if(match->seen_single_use_conn && !match->seen_multiplex_conn) {
+ /* We've seen a single-use, existing connection to the destination and
+ * no multiplexed one. It seems safe to assume that the server does
+ * not support multiplexing. */
+ match->wait_pipe = FALSE;
+ }
+ else if(match->seen_pending_conn && match->data->set.pipewait) {
+ infof(match->data,
"Found pending candidate for reuse and CURLOPT_PIPEWAIT is set");
- *waitpipe = TRUE;
+ match->wait_pipe = TRUE;
}
+ match->force_reuse = FALSE;
+ return FALSE;
+}
- return FALSE; /* no matching connecting exists */
+/*
+ * 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 url_conn_match match;
+ bool result;
+
+ memset(&match, 0, sizeof(match));
+ match.data = data;
+ match.needle = needle;
+ match.may_multiplex = Curl_xfer_may_multiplex(data, needle);
+
+#ifdef USE_NTLM
+ match.want_ntlm_http = ((data->state.authhost.want & CURLAUTH_NTLM) &&
+ (needle->handler->protocol & PROTO_FAMILY_HTTP));
+#ifndef CURL_DISABLE_PROXY
+ match.want_proxy_ntlm_http =
+ (needle->bits.proxy_user_passwd &&
+ (data->state.authproxy.want & CURLAUTH_NTLM) &&
+ (needle->handler->protocol & PROTO_FAMILY_HTTP));
+#endif
+#endif
+
+ /* Find a connection in the pool that matches what "data + needle"
+ * requires. If a suitable candidate is found, it is attached to "data". */
+ result = Curl_cpool_find(data, needle->destination, needle->destination_len,
+ url_match_conn, url_match_result, &match);
+
+ /* wait_pipe is TRUE if we encounter a bundle that is undecided. There
+ * is no matching connection then, yet. */
+ *usethis = match.found;
+ *force_reuse = match.force_reuse;
+ *waitpipe = match.wait_pipe;
+ return result;
}
/*
@@ -1967,7 +1930,7 @@ static CURLcode setup_range(struct Curl_easy *data)
free(s->range);
if(s->resume_from)
- s->range = aprintf("%" CURL_FORMAT_CURL_OFF_T "-", s->resume_from);
+ s->range = aprintf("%" FMT_OFF_T "-", s->resume_from);
else
s->range = strdup(data->set.str[STRING_SET_RANGE]);
@@ -1999,6 +1962,8 @@ static CURLcode setup_connection_internals(struct Curl_easy *data,
struct connectdata *conn)
{
const struct Curl_handler *p;
+ const char *hostname;
+ int port;
CURLcode result;
/* Perform setup complement if some. */
@@ -2018,6 +1983,34 @@ static CURLcode setup_connection_internals(struct Curl_easy *data,
was very likely already set to the proxy port */
conn->primary.remote_port = p->defport;
+ /* Now create the destination name */
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
+ hostname = conn->http_proxy.host.name;
+ port = conn->primary.remote_port;
+ }
+ else
+#endif
+ {
+ port = conn->remote_port;
+ if(conn->bits.conn_to_host)
+ hostname = conn->conn_to_host.name;
+ else
+ hostname = conn->host.name;
+ }
+
+#ifdef USE_IPV6
+ conn->destination = aprintf("%u/%d/%s", conn->scope_id, port, hostname);
+#else
+ conn->destination = aprintf("%d/%s", port, hostname);
+#endif
+ if(!conn->destination)
+ return CURLE_OUT_OF_MEMORY;
+
+ conn->destination_len = strlen(conn->destination) + 1;
+ Curl_strntolower(conn->destination, conn->destination,
+ conn->destination_len - 1);
+
return CURLE_OK;
}
@@ -2341,7 +2334,7 @@ static CURLcode parse_proxy_auth(struct Curl_easy *data,
return result;
}
-/* create_conn helper to parse and init proxy values. to be called after unix
+/* 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)
@@ -2408,7 +2401,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data,
Curl_safefree(no_proxy);
#ifdef USE_UNIX_SOCKETS
- /* For the time being do not mix proxy and unix domain sockets. See #1274 */
+ /* For the time being do not mix proxy and Unix domain sockets. See #1274 */
if(proxy && conn->unix_domain_socket) {
free(proxy);
proxy = NULL;
@@ -3101,140 +3094,82 @@ static CURLcode resolve_unix(struct Curl_easy *data,
return longpath ? CURLE_COULDNT_RESOLVE_HOST : CURLE_OUT_OF_MEMORY;
}
- hostaddr->inuse++;
+ hostaddr->refcount = 1; /* connection is the only one holding this */
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)
+/*************************************************************
+ * Resolve the address of the server or proxy
+ *************************************************************/
+static CURLcode resolve_server(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool *async)
{
- struct Curl_dns_entry *hostaddr = NULL;
- struct hostname *host;
+ struct hostname *ehost;
timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+ const char *peertype = "host";
int rc;
+#ifdef USE_UNIX_SOCKETS
+ char *unix_path = conn->unix_domain_socket;
- 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;
+#ifndef CURL_DISABLE_PROXY
+ if(!unix_path && CONN_IS_PROXIED(conn) && 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
- rc = Curl_resolv_timeout(data, conn->hostname_resolve,
- conn->primary.remote_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;
+ if(unix_path) {
+ /* TODO, this only works if previous transport is TRNSPRT_TCP. Check it? */
+ conn->transport = TRNSPRT_UNIX;
+ return resolve_unix(data, conn, unix_path);
}
-
- 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->primary.remote_port = conn->bits.conn_to_port ? conn->conn_to_port :
- conn->remote_port;
+#ifndef CURL_DISABLE_PROXY
+ if(CONN_IS_PROXIED(conn)) {
+ ehost = conn->bits.socksproxy ? &conn->socks_proxy.host :
+ &conn->http_proxy.host;
+ peertype = "proxy";
+ }
+ else
+#endif
+ {
+ ehost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host;
+ /* If not connecting via a proxy, extract the port from the URL, if it is
+ * there, thus overriding any defaults that might have been set above. */
+ conn->primary.remote_port = conn->bits.conn_to_port ? conn->conn_to_port :
+ conn->remote_port;
+ }
/* Resolve target host right on */
- conn->hostname_resolve = strdup(connhost->name);
+ conn->hostname_resolve = strdup(ehost->name);
if(!conn->hostname_resolve)
return CURLE_OUT_OF_MEMORY;
rc = Curl_resolv_timeout(data, conn->hostname_resolve,
- conn->primary.remote_port, &hostaddr, timeout_ms);
- conn->dns_entry = hostaddr;
+ conn->primary.remote_port,
+ &conn->dns_entry, timeout_ms);
if(rc == CURLRESOLV_PENDING)
*async = TRUE;
else if(rc == CURLRESOLV_TIMEDOUT) {
- failf(data, "Failed to resolve host '%s' with timeout after %"
- CURL_FORMAT_TIMEDIFF_T " ms", connhost->dispname,
+ failf(data, "Failed to resolve %s '%s' with timeout after %"
+ FMT_TIMEDIFF_T " ms", peertype, ehost->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);
+ else if(!conn->dns_entry) {
+ failf(data, "Could not resolve %s: %s", peertype, ehost->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 are reusing the connection - no need to resolve anything, and
- idnconvert_hostname() was called already in create_conn() for the reuse
- 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
@@ -3275,7 +3210,7 @@ static void reuse_conn(struct Curl_easy *data,
}
#endif
- /* Finding a connection for reuse in the cache matches, among other
+ /* Finding a connection for reuse in the cpool matches, among other
* things on the "remote-relevant" hostname. This is not necessarily
* the authority of the URL, e.g. conn->host. For example:
* - we use a proxy (not tunneling). we want to send all requests
@@ -3337,8 +3272,6 @@ static CURLcode create_conn(struct Curl_easy *data,
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;
@@ -3398,7 +3331,7 @@ static CURLcode create_conn(struct Curl_easy *data,
}
#endif
- /* After the unix socket init but before the proxy vars are used, parse and
+ /* 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);
@@ -3503,13 +3436,15 @@ static CURLcode create_conn(struct Curl_easy *data,
/* 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);
+ data->info.conn_scheme = conn->handler->scheme;
+ /* conn_protocol can only provide "old" protocols */
+ data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK;
result = conn->handler->connect_it(data, &done);
/* Setup a "faked" transfer that will do nothing */
if(!result) {
Curl_attach_connection(data, conn);
- result = Curl_conncache_add_conn(data);
+ result = Curl_cpool_add_conn(data, conn);
if(result)
goto out;
@@ -3545,7 +3480,8 @@ static CURLcode create_conn(struct Curl_easy *data,
if(result)
goto out;
- prune_dead_connections(data);
+ /* FIXME: do we really want to run this every time we add a transfer? */
+ Curl_cpool_prune_dead(data);
/*************************************************************
* Check the current list of connections to see if we can
@@ -3604,43 +3540,14 @@ static CURLcode create_conn(struct Curl_easy *data,
"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
+ switch(Curl_cpool_check_limits(data, conn)) {
+ case CPOOL_LIMIT_DEST:
+ infof(data, "No more connections allowed to host");
+ connections_available = FALSE;
+ break;
+ case CPOOL_LIMIT_TOTAL:
#ifndef CURL_DISABLE_DOH
- if(data->set.dohfor)
+ if(data->set.dohfor_mid >= 0)
infof(data, "Allowing DoH to override max connection limit");
else
#endif
@@ -3648,6 +3555,10 @@ static CURLcode create_conn(struct Curl_easy *data,
infof(data, "No connections available in cache");
connections_available = FALSE;
}
+ break;
+ default:
+ break;
+ }
}
if(!connections_available) {
@@ -3671,7 +3582,7 @@ static CURLcode create_conn(struct Curl_easy *data,
}
Curl_attach_connection(data, conn);
- result = Curl_conncache_add_conn(data);
+ result = Curl_cpool_add_conn(data, conn);
if(result)
goto out;
}
@@ -3708,16 +3619,35 @@ static CURLcode create_conn(struct Curl_easy *data,
/* Continue connectdata initialization here. */
- /*************************************************************
- * Resolve the address of the server or proxy
- *************************************************************/
- result = resolve_server(data, conn, async);
- if(result)
- goto out;
+ if(conn->bits.reuse) {
+ /* We are reusing the connection - no need to resolve anything, and
+ idnconvert_hostname() was called already in create_conn() for the reuse
+ case. */
+ *async = FALSE;
+ }
+ else {
+ /*************************************************************
+ * Resolve the address of the server or proxy
+ *************************************************************/
+ result = resolve_server(data, conn, async);
+ if(result)
+ goto out;
+ }
+
+ /* persist the scheme and handler the transfer is using */
+ 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.used_proxy =
+#ifdef CURL_DISABLE_PROXY
+ 0
+#else
+ conn->bits.proxy
+#endif
+ ;
/* Everything general done, inform filters that they need
- * to prepare for a data transfer.
- */
+ * to prepare for a data transfer. */
result = Curl_conn_ev_data_setup(data);
out:
@@ -3743,18 +3673,6 @@ CURLcode Curl_setup_conn(struct Curl_easy *data,
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 do not 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();
@@ -3803,8 +3721,7 @@ CURLcode Curl_connect(struct Curl_easy *data,
/* We are 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);
+ Curl_cpool_disconnect(data, conn, TRUE);
}
return result;
diff --git a/libs/libcurl/src/url.h b/libs/libcurl/src/url.h
index bd080dd35a..d09bf14137 100644
--- a/libs/libcurl/src/url.h
+++ b/libs/libcurl/src/url.h
@@ -37,8 +37,8 @@ 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 aborted);
+bool Curl_on_disconnect(struct Curl_easy *data,
+ struct connectdata *, bool aborted);
CURLcode Curl_setup_conn(struct Curl_easy *data,
bool *protocol_done);
void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn);
@@ -65,6 +65,21 @@ void Curl_verboseconnect(struct Curl_easy *data, struct connectdata *conn,
int sockindex);
#endif
+/**
+ * Return TRUE iff the given connection is considered dead.
+ * @param nowp NULL or pointer to time being checked against.
+ */
+bool Curl_conn_seems_dead(struct connectdata *conn,
+ struct Curl_easy *data,
+ struct curltime *nowp);
+
+/**
+ * Perform upkeep operations on the connection.
+ */
+CURLcode Curl_conn_upkeep(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct curltime *now);
+
#if defined(USE_HTTP2) || defined(USE_HTTP3)
void Curl_data_priority_clear_state(struct Curl_easy *data);
#else
diff --git a/libs/libcurl/src/urlapi.c b/libs/libcurl/src/urlapi.c
index 2179e106de..9e7565c0f7 100644
--- a/libs/libcurl/src/urlapi.c
+++ b/libs/libcurl/src/urlapi.c
@@ -41,13 +41,13 @@
#include "curl_memory.h"
#include "memdebug.h"
- /* MSDOS/Windows style drive prefix, eg c: in c:foo */
+ /* MS-DOS/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
+ /* MS-DOS/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') || \
@@ -1121,7 +1121,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
* 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 */
+ /* File drive letters are only accepted in MS-DOS/Windows */
result = CURLUE_BAD_FILE_URL;
goto fail;
}
@@ -1991,7 +1991,23 @@ nomem:
/* Skip hostname check, it is allowed to be empty. */
}
else {
- if(!n || hostname_check(u, (char *)newp, n)) {
+ bool bad = FALSE;
+ if(!n)
+ bad = TRUE; /* empty hostname is not okay */
+ else if(!urlencode) {
+ /* if the host name part was not URL encoded here, it was set ready
+ URL encoded so we need to decode it to check */
+ size_t dlen;
+ char *decoded = NULL;
+ CURLcode result =
+ Curl_urldecode(newp, n, &decoded, &dlen, REJECT_CTRL);
+ if(result || hostname_check(u, decoded, dlen))
+ bad = TRUE;
+ free(decoded);
+ }
+ else if(hostname_check(u, (char *)newp, n))
+ bad = TRUE;
+ if(bad) {
Curl_dyn_free(&enc);
return CURLUE_BAD_HOSTNAME;
}
diff --git a/libs/libcurl/src/urldata.h b/libs/libcurl/src/urldata.h
index 5a5ef0663b..009bbb6232 100644
--- a/libs/libcurl/src/urldata.h
+++ b/libs/libcurl/src/urldata.h
@@ -77,6 +77,10 @@ struct curl_trc_featt;
#define CURLPROTO_WSS 0
#endif
+/* the default protocols accepting a redirect to */
+#define CURLPROTO_REDIR (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | \
+ CURLPROTO_FTPS)
+
/* This should be undefined once we need bit 32 or higher */
#define PROTO_TYPE_SMALL
@@ -159,6 +163,7 @@ typedef ssize_t (Curl_send)(struct Curl_easy *data, /* transfer */
int sockindex, /* socketindex */
const void *buf, /* data to write */
size_t len, /* max amount to write */
+ bool eos, /* last chunk */
CURLcode *err); /* error to return */
/* return the count of bytes read, or -1 on error */
@@ -272,11 +277,11 @@ struct ssl_peer {
char *sni; /* SNI version of hostname or NULL if not usable */
ssl_peer_type type; /* type of the peer information */
int port; /* port we are talking to */
- int transport; /* TCP or QUIC */
+ int transport; /* one of TRNSPRT_* defines */
};
struct ssl_primary_config {
- char *CApath; /* certificate dir (does not work on windows) */
+ char *CApath; /* certificate dir (does not work on Windows) */
char *CAfile; /* certificate to verify peer against */
char *issuercert; /* optional issuer certificate filename */
char *clientcert;
@@ -441,7 +446,7 @@ struct ntlmdata {
unsigned int flags;
unsigned char nonce[8];
unsigned int target_info_len;
- void *target_info; /* TargetInfo received in the ntlm type-2 message */
+ void *target_info; /* TargetInfo received in the NTLM type-2 message */
#endif
};
#endif
@@ -454,6 +459,7 @@ struct negotiatedata {
gss_ctx_id_t context;
gss_name_t spn;
gss_buffer_desc output_token;
+ struct dynbuf channel_binding_data;
#else
#ifdef USE_WINDOWS_SSPI
#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
@@ -495,9 +501,6 @@ struct ConnectBits {
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! */
@@ -532,6 +535,7 @@ struct ConnectBits {
#endif
BIT(bound); /* set true if bind() has already been done on this socket/
connection */
+ BIT(asks_multiplex); /* connection asks for multiplexing, but is not yet */
BIT(multiplex); /* connection is multiplexed */
BIT(tcp_fastopen); /* use TCP Fast Open */
BIT(tls_enable_alpn); /* TLS ALPN extension? */
@@ -549,6 +553,7 @@ struct ConnectBits {
BIT(aborted); /* connection was aborted, e.g. in unclean state */
BIT(shutdown_handler); /* connection shutdown: handler shut down */
BIT(shutdown_filters); /* connection shutdown: filters shut down */
+ BIT(in_cpool); /* connection is kept in a connection pool */
};
struct hostname {
@@ -617,27 +622,6 @@ struct easy_pollset {
unsigned char actions[MAX_SOCKSPEREASYHANDLE];
};
-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 */
-#ifdef USE_HTTPSRR
- DOH_PROBE_SLOT_HTTPS = 2, /* for HTTPS RR */
-#endif
-
- /* 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
-};
-
/*
* Specific protocol handler.
*/
@@ -757,7 +741,7 @@ struct Curl_handler {
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
+#define PROTOPT_USERPWDCTRL (1<<13) /* Allow "control bytes" (< 32 ASCII) in
username and password */
#define PROTOPT_NOTCPPROXY (1<<14) /* this protocol cannot proxy over TCP */
@@ -796,20 +780,22 @@ struct ldapconninfo;
* unique for an entire connection.
*/
struct connectdata {
- struct Curl_llist_element bundle_node; /* conncache */
+ struct Curl_llist_node cpool_node; /* conncache lists */
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
+ /* This is used by the connection pool 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)
+#define CONN_INUSE(c) Curl_llist_count(&(c)->easyq)
/**** Fields set when inited and not modified again */
curl_off_t connection_id; /* Contains a unique number to make it easier to
track the connections in the log output */
+ char *destination; /* string carrying normalized hostname+port+scope */
+ size_t destination_len; /* strlen(destination) + 1 */
/* '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
@@ -831,7 +817,8 @@ struct connectdata {
struct proxy_info http_proxy;
#endif
/* 'primary' and 'secondary' get filled with IP quadruple
- (local/remote numerical ip address and port) whenever a is *attempted*.
+ (local/remote numerical ip address and port) whenever a connect is
+ *attempted*.
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. */
@@ -844,7 +831,7 @@ struct connectdata {
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 */
+ struct curltime lastused; /* when returned to the connection poolas idle */
curl_socket_t sock[2]; /* two sockets, the second is used for the data
transfer when doing FTP */
Curl_recv *recv[2];
@@ -964,7 +951,6 @@ struct connectdata {
unsigned int unused:1; /* avoids empty union */
} proto;
- struct connectbundle *bundle; /* The bundle we are member of */
#ifdef USE_UNIX_SOCKETS
char *unix_domain_socket;
#endif
@@ -1043,7 +1029,7 @@ struct PureInfo {
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. */
+ that might be reused, in the connection pool. */
struct ip_quadruple primary;
int conn_remote_port; /* this is the "remote port", which is the port
number of the used URL, independent of proxy or
@@ -1058,14 +1044,23 @@ struct PureInfo {
BIT(used_proxy); /* the transfer used a proxy */
};
+struct pgrs_measure {
+ struct curltime start; /* when measure started */
+ curl_off_t start_size; /* the 'cur_size' the measure started at */
+};
+
+struct pgrs_dir {
+ curl_off_t total_size; /* total expected bytes */
+ curl_off_t cur_size; /* transferred bytes so far */
+ curl_off_t speed; /* bytes per second transferred */
+ struct pgrs_measure limit;
+};
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 */
+ struct pgrs_dir ul;
+ struct pgrs_dir dl;
curl_off_t current_speed; /* uses the currently fastest transfer */
@@ -1074,14 +1069,12 @@ struct Progress {
timediff_t timespent;
- curl_off_t dlspeed;
- curl_off_t ulspeed;
-
timediff_t t_postqueue;
timediff_t t_nslookup;
timediff_t t_connect;
timediff_t t_appconnect;
timediff_t t_pretransfer;
+ timediff_t t_posttransfer;
timediff_t t_starttransfer;
timediff_t t_redirect;
@@ -1090,14 +1083,6 @@ struct Progress {
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 ];
@@ -1194,7 +1179,7 @@ typedef enum {
* One instance for each timeout an easy handle can set.
*/
struct time_node {
- struct Curl_llist_element list;
+ struct Curl_llist_node list;
struct curltime time;
expire_id eid;
};
@@ -1212,8 +1197,6 @@ struct urlpieces {
};
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 */
@@ -1238,7 +1221,6 @@ struct UrlState {
struct Curl_ssl_session *session; /* array of 'max_ssl_sessions' size */
long sessionage; /* number of the most recent session */
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
@@ -1266,12 +1248,6 @@ struct UrlState {
/* a place to store the most recently set (S)FTP entrypath */
char *most_recent_ftp_entrypath;
-#if !defined(_WIN32) && !defined(MSDOS) && !defined(__EMX__)
-/* do FTP line-end conversions on most platforms */
-#define CURL_DO_LINEEND_CONV
- /* 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 */
@@ -1364,9 +1340,6 @@ struct UrlState {
unsigned char select_bits; /* != 0 -> bitmask of socket events for this
transfer overriding anything the socket may
report */
-#ifdef DEBUGBUILD
- 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! */
@@ -1422,7 +1395,7 @@ enum dupstring {
STRING_KEY, /* private key filename */
STRING_KEY_PASSWD, /* plain text private key password */
STRING_KEY_TYPE, /* format for private key (default: PEM) */
- STRING_SSL_CAPATH, /* CA directory name (does not work on windows) */
+ STRING_SSL_CAPATH, /* CA directory name (does not work on Windows) */
STRING_SSL_CAFILE, /* certificate file to verify peer against */
STRING_SSL_PINNEDPUBLICKEY, /* public key file to verify peer against */
STRING_SSL_CIPHER_LIST, /* list of ciphers to use */
@@ -1436,7 +1409,7 @@ enum dupstring {
STRING_KEY_PROXY, /* private key filename */
STRING_KEY_PASSWD_PROXY, /* plain text private key password */
STRING_KEY_TYPE_PROXY, /* format for private key (default: PEM) */
- STRING_SSL_CAPATH_PROXY, /* CA directory name (does not work on windows) */
+ STRING_SSL_CAPATH_PROXY, /* CA directory name (does not work on Windows) */
STRING_SSL_CAFILE_PROXY, /* certificate file to verify peer against */
STRING_SSL_PINNEDPUBLICKEY_PROXY, /* public key file to verify proxy */
STRING_SSL_CIPHER_LIST_PROXY, /* list of ciphers to use */
@@ -1493,7 +1466,7 @@ enum dupstring {
#ifdef USE_SSH
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_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, /* filename of knownhosts file */
#endif
@@ -1751,7 +1724,7 @@ struct UserDefined {
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 */
+ curl_off_t dohfor_mid; /* this is a DoH request for that transfer */
#endif
CURLU *uh; /* URL handle for the current parsed URL */
#ifndef CURL_DISABLE_HTTP
@@ -1902,22 +1875,23 @@ 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;
- /* once an easy handle is tied to a connection cache
+ /* once an easy handle is tied to a connection pool
a non-negative number to distinguish this transfer from
- other using the same cache. For easier tracking
+ other using the same pool. For easier tracking
in log output.
This may wrap around after LONG_MAX to 0 again, so it
- has no uniqueness guarantee for very large processings. */
+ has no uniqueness guarantee for very large processings.
+ Note: it has no uniqueness either IFF more than one connection pool
+ is used by the libcurl application. */
curl_off_t id;
-
- /* first, two fields for the linked list of these */
- struct Curl_easy *next;
- struct Curl_easy *prev;
+ /* once an easy handle is added to a multi, either explicitly by the
+ * libcurl application or implicitly during `curl_easy_perform()`,
+ * a unique identifier inside this one multi instance. */
+ curl_off_t mid;
struct connectdata *conn;
- struct Curl_llist_element connect_queue; /* for the pending and msgsent
- lists */
- struct Curl_llist_element conn_queue; /* list per connectdata */
+ struct Curl_llist_node multi_queue; /* for multihandle list management */
+ struct Curl_llist_node conn_queue; /* list per connectdata */
CURLMstate mstate; /* the handle's state */
CURLcode result; /* previous result */
diff --git a/libs/libcurl/src/vauth/digest.c b/libs/libcurl/src/vauth/digest.c
index 5e1461fff4..ab396d2f6b 100644
--- a/libs/libcurl/src/vauth/digest.c
+++ b/libs/libcurl/src/vauth/digest.c
@@ -142,7 +142,7 @@ bool Curl_auth_digest_get_pair(const char *str, char *value, char *content,
}
#if !defined(USE_WINDOWS_SSPI)
-/* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string */
+/* 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 */
{
@@ -151,7 +151,7 @@ static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */
msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
}
-/* Convert sha256 or SHA-512/256 chunk to RFC7616 -suitable ascii string */
+/* Convert sha256 or SHA-512/256 chunk to RFC7616 -suitable ASCII string */
static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */
unsigned char *dest) /* 65 bytes */
{
diff --git a/libs/libcurl/src/vauth/digest_sspi.c b/libs/libcurl/src/vauth/digest_sspi.c
index 1562e124f4..4a24d5c577 100644
--- a/libs/libcurl/src/vauth/digest_sspi.c
+++ b/libs/libcurl/src/vauth/digest_sspi.c
@@ -60,12 +60,13 @@ bool Curl_auth_is_digest_supported(void)
SECURITY_STATUS status;
/* Query the security package for Digest */
- status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
- &SecurityPackage);
+ status =
+ Curl_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);
+ Curl_pSecFn->FreeContextBuffer(SecurityPackage);
}
return (status == SEC_E_OK ? TRUE : FALSE);
@@ -119,8 +120,9 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
}
/* Query the security package for DigestSSP */
- status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
- &SecurityPackage);
+ status =
+ Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
+ &SecurityPackage);
if(status != SEC_E_OK) {
failf(data, "SSPI: could not get auth info");
return CURLE_AUTH_ERROR;
@@ -129,7 +131,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
token_max = SecurityPackage->cbMaxToken;
/* Release the package buffer as it is not required anymore */
- s_pSecFn->FreeContextBuffer(SecurityPackage);
+ Curl_pSecFn->FreeContextBuffer(SecurityPackage);
/* Allocate our response buffer */
output_token = malloc(token_max);
@@ -160,7 +162,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
p_identity = NULL;
/* Acquire our credentials handle */
- status = s_pSecFn->AcquireCredentialsHandle(NULL,
+ status = Curl_pSecFn->AcquireCredentialsHandle(NULL,
(TCHAR *) TEXT(SP_NAME_DIGEST),
SECPKG_CRED_OUTBOUND, NULL,
p_identity, NULL, NULL,
@@ -190,20 +192,20 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
resp_buf.cbBuffer = curlx_uztoul(token_max);
/* Generate our response message */
- status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, spn,
+ status = Curl_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);
+ Curl_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_pSecFn->FreeCredentialsHandle(&credentials);
Curl_sspi_free_identity(p_identity);
free(spn);
free(output_token);
@@ -223,8 +225,8 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
Curl_bufref_set(out, output_token, resp_buf.cbBuffer, curl_free);
/* Free our handles */
- s_pSecFn->DeleteSecurityContext(&context);
- s_pSecFn->FreeCredentialsHandle(&credentials);
+ Curl_pSecFn->DeleteSecurityContext(&context);
+ Curl_pSecFn->FreeCredentialsHandle(&credentials);
/* Free the identity structure */
Curl_sspi_free_identity(p_identity);
@@ -410,8 +412,9 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
(void) data;
/* Query the security package for DigestSSP */
- status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
- &SecurityPackage);
+ status =
+ Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
+ &SecurityPackage);
if(status != SEC_E_OK) {
failf(data, "SSPI: could not get auth info");
return CURLE_AUTH_ERROR;
@@ -420,7 +423,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
token_max = SecurityPackage->cbMaxToken;
/* Release the package buffer as it is not required anymore */
- s_pSecFn->FreeContextBuffer(SecurityPackage);
+ Curl_pSecFn->FreeContextBuffer(SecurityPackage);
/* Allocate the output buffer according to the max token size as indicated
by the security package */
@@ -436,7 +439,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
(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_pSecFn->DeleteSecurityContext(digest->http_context);
Curl_safefree(digest->http_context);
}
Curl_safefree(digest->user);
@@ -463,13 +466,14 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
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);
+ status = Curl_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_pSecFn->DeleteSecurityContext(digest->http_context);
Curl_safefree(digest->http_context);
}
}
@@ -529,7 +533,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
}
/* Acquire our credentials handle */
- status = s_pSecFn->AcquireCredentialsHandle(NULL,
+ status = Curl_pSecFn->AcquireCredentialsHandle(NULL,
(TCHAR *) TEXT(SP_NAME_DIGEST),
SECPKG_CRED_OUTBOUND, NULL,
p_identity, NULL, NULL,
@@ -565,7 +569,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
spn = curlx_convert_UTF8_to_tchar((char *) uripath);
if(!spn) {
- s_pSecFn->FreeCredentialsHandle(&credentials);
+ Curl_pSecFn->FreeCredentialsHandle(&credentials);
Curl_sspi_free_identity(p_identity);
free(output_token);
@@ -579,7 +583,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
return CURLE_OUT_OF_MEMORY;
/* Generate our response message */
- status = s_pSecFn->InitializeSecurityContext(&credentials, NULL,
+ status = Curl_pSecFn->InitializeSecurityContext(&credentials, NULL,
spn,
ISC_REQ_USE_HTTP_STYLE, 0, 0,
&chlg_desc, 0,
@@ -589,13 +593,13 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
if(status == SEC_I_COMPLETE_NEEDED ||
status == SEC_I_COMPLETE_AND_CONTINUE)
- s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
+ Curl_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_pSecFn->FreeCredentialsHandle(&credentials);
Curl_sspi_free_identity(p_identity);
free(output_token);
@@ -615,7 +619,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
output_token_len = resp_buf.cbBuffer;
- s_pSecFn->FreeCredentialsHandle(&credentials);
+ Curl_pSecFn->FreeCredentialsHandle(&credentials);
Curl_sspi_free_identity(p_identity);
}
@@ -660,7 +664,7 @@ void Curl_auth_digest_cleanup(struct digestdata *digest)
/* Delete security context */
if(digest->http_context) {
- s_pSecFn->DeleteSecurityContext(digest->http_context);
+ Curl_pSecFn->DeleteSecurityContext(digest->http_context);
Curl_safefree(digest->http_context);
}
diff --git a/libs/libcurl/src/vauth/krb5_sspi.c b/libs/libcurl/src/vauth/krb5_sspi.c
index d93cd6ef42..eae79ccaa5 100644
--- a/libs/libcurl/src/vauth/krb5_sspi.c
+++ b/libs/libcurl/src/vauth/krb5_sspi.c
@@ -55,13 +55,13 @@ bool Curl_auth_is_gssapi_supported(void)
SECURITY_STATUS status;
/* Query the security package for Kerberos */
- status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
+ status = Curl_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);
+ Curl_pSecFn->FreeContextBuffer(SecurityPackage);
}
return (status == SEC_E_OK ? TRUE : FALSE);
@@ -118,7 +118,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
if(!krb5->output_token) {
/* Query the security package for Kerberos */
- status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
+ status = Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *)
TEXT(SP_NAME_KERBEROS),
&SecurityPackage);
if(status != SEC_E_OK) {
@@ -129,7 +129,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
krb5->token_max = SecurityPackage->cbMaxToken;
/* Release the package buffer as it is not required anymore */
- s_pSecFn->FreeContextBuffer(SecurityPackage);
+ Curl_pSecFn->FreeContextBuffer(SecurityPackage);
/* Allocate our response buffer */
krb5->output_token = malloc(krb5->token_max);
@@ -158,7 +158,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
return CURLE_OUT_OF_MEMORY;
/* Acquire our credentials handle */
- status = s_pSecFn->AcquireCredentialsHandle(NULL,
+ status = Curl_pSecFn->AcquireCredentialsHandle(NULL,
(TCHAR *)
TEXT(SP_NAME_KERBEROS),
SECPKG_CRED_OUTBOUND, NULL,
@@ -197,7 +197,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
resp_buf.cbBuffer = curlx_uztoul(krb5->token_max);
/* Generate our challenge-response message */
- status = s_pSecFn->InitializeSecurityContext(krb5->credentials,
+ status = Curl_pSecFn->InitializeSecurityContext(krb5->credentials,
chlg ? krb5->context : NULL,
krb5->spn,
(mutual_auth ?
@@ -215,7 +215,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
return CURLE_AUTH_ERROR;
if(memcmp(&context, krb5->context, sizeof(context))) {
- s_pSecFn->DeleteSecurityContext(krb5->context);
+ Curl_pSecFn->DeleteSecurityContext(krb5->context);
memcpy(krb5->context, &context, sizeof(context));
}
@@ -282,7 +282,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
}
/* Get our response size information */
- status = s_pSecFn->QueryContextAttributes(krb5->context,
+ status = Curl_pSecFn->QueryContextAttributes(krb5->context,
SECPKG_ATTR_SIZES,
&sizes);
@@ -304,7 +304,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
input_buf[1].cbBuffer = 0;
/* Decrypt the inbound challenge and obtain the qop */
- status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop);
+ status = Curl_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;
@@ -323,7 +323,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
((unsigned long)indata[2] << 8) | indata[3];
/* Free the challenge as it is not required anymore */
- s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer);
+ Curl_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer);
/* Process the security layer */
if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) {
@@ -392,7 +392,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
wrap_buf[2].cbBuffer = sizes.cbBlockSize;
/* Encrypt the data */
- status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT,
+ status = Curl_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT,
&wrap_desc, 0);
if(status != SEC_E_OK) {
free(padding);
@@ -448,14 +448,14 @@ void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5)
{
/* Free our security context */
if(krb5->context) {
- s_pSecFn->DeleteSecurityContext(krb5->context);
+ Curl_pSecFn->DeleteSecurityContext(krb5->context);
free(krb5->context);
krb5->context = NULL;
}
/* Free our credentials handle */
if(krb5->credentials) {
- s_pSecFn->FreeCredentialsHandle(krb5->credentials);
+ Curl_pSecFn->FreeCredentialsHandle(krb5->credentials);
free(krb5->credentials);
krb5->credentials = NULL;
}
diff --git a/libs/libcurl/src/vauth/ntlm.c b/libs/libcurl/src/vauth/ntlm.c
index 99bd62470e..4a80ba3c8b 100644
--- a/libs/libcurl/src/vauth/ntlm.c
+++ b/libs/libcurl/src/vauth/ntlm.c
@@ -59,10 +59,6 @@
/* "NTLMSSP" signature is always in ASCII regardless of the platform */
#define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50"
-/* The fixed hostname 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)
@@ -490,7 +486,9 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
unsigned char *ptr_ntresp = &ntresp[0];
unsigned char *ntlmv2resp = NULL;
bool unicode = (ntlm->flags & NTLMFLAG_NEGOTIATE_UNICODE) ? TRUE : FALSE;
- char host[HOSTNAME_MAX + 1] = "";
+ /* The fixed hostname we provide, in order to not leak our real local host
+ name. Copy the name used by Firefox. */
+ static const char host[] = "WORKSTATION";
const char *user;
const char *domain = "";
size_t hostoff = 0;
@@ -515,21 +513,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
user = userp;
userlen = strlen(user);
-
-#ifndef NTLM_HOSTNAME
- /* Get the machine's un-qualified hostname as NTLM does not 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
+ hostlen = sizeof(host) - 1;
if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) {
unsigned char ntbuffer[0x18];
diff --git a/libs/libcurl/src/vauth/ntlm_sspi.c b/libs/libcurl/src/vauth/ntlm_sspi.c
index 2da0789d73..e3d56335b4 100644
--- a/libs/libcurl/src/vauth/ntlm_sspi.c
+++ b/libs/libcurl/src/vauth/ntlm_sspi.c
@@ -55,12 +55,12 @@ bool Curl_auth_is_ntlm_supported(void)
SECURITY_STATUS status;
/* Query the security package for NTLM */
- status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
+ status = Curl_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);
+ Curl_pSecFn->FreeContextBuffer(SecurityPackage);
}
return (status == SEC_E_OK ? TRUE : FALSE);
@@ -103,7 +103,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
Curl_auth_cleanup_ntlm(ntlm);
/* Query the security package for NTLM */
- status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
+ status = Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
&SecurityPackage);
if(status != SEC_E_OK) {
failf(data, "SSPI: could not get auth info");
@@ -113,7 +113,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
ntlm->token_max = SecurityPackage->cbMaxToken;
/* Release the package buffer as it is not required anymore */
- s_pSecFn->FreeContextBuffer(SecurityPackage);
+ Curl_pSecFn->FreeContextBuffer(SecurityPackage);
/* Allocate our output buffer */
ntlm->output_token = malloc(ntlm->token_max);
@@ -141,7 +141,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
return CURLE_OUT_OF_MEMORY;
/* Acquire our credentials handle */
- status = s_pSecFn->AcquireCredentialsHandle(NULL,
+ status = Curl_pSecFn->AcquireCredentialsHandle(NULL,
(TCHAR *) TEXT(SP_NAME_NTLM),
SECPKG_CRED_OUTBOUND, NULL,
ntlm->p_identity, NULL, NULL,
@@ -167,7 +167,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
type_1_buf.cbBuffer = curlx_uztoul(ntlm->token_max);
/* Generate our type-1 message */
- status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL,
+ status = Curl_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL,
ntlm->spn,
0, 0, SECURITY_NETWORK_DREP,
NULL, 0,
@@ -175,7 +175,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
&attrs, &expiry);
if(status == SEC_I_COMPLETE_NEEDED ||
status == SEC_I_COMPLETE_AND_CONTINUE)
- s_pSecFn->CompleteAuthToken(ntlm->context, &type_1_desc);
+ Curl_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)
@@ -282,7 +282,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
SEC_CHANNEL_BINDINGS channelBindings;
SecPkgContext_Bindings pkgBindings;
pkgBindings.Bindings = &channelBindings;
- status = s_pSecFn->QueryContextAttributes(
+ status = Curl_pSecFn->QueryContextAttributes(
ntlm->sslContext,
SECPKG_ATTR_ENDPOINT_BINDINGS,
&pkgBindings
@@ -305,7 +305,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
type_3_buf.cbBuffer = curlx_uztoul(ntlm->token_max);
/* Generate our type-3 message */
- status = s_pSecFn->InitializeSecurityContext(ntlm->credentials,
+ status = Curl_pSecFn->InitializeSecurityContext(ntlm->credentials,
ntlm->context,
ntlm->spn,
0, 0, SECURITY_NETWORK_DREP,
@@ -343,14 +343,14 @@ void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm)
{
/* Free our security context */
if(ntlm->context) {
- s_pSecFn->DeleteSecurityContext(ntlm->context);
+ Curl_pSecFn->DeleteSecurityContext(ntlm->context);
free(ntlm->context);
ntlm->context = NULL;
}
/* Free our credentials handle */
if(ntlm->credentials) {
- s_pSecFn->FreeCredentialsHandle(ntlm->credentials);
+ Curl_pSecFn->FreeCredentialsHandle(ntlm->credentials);
free(ntlm->credentials);
ntlm->credentials = NULL;
}
diff --git a/libs/libcurl/src/vauth/spnego_gssapi.c b/libs/libcurl/src/vauth/spnego_gssapi.c
index d7a533eb98..74d4fa3362 100644
--- a/libs/libcurl/src/vauth/spnego_gssapi.c
+++ b/libs/libcurl/src/vauth/spnego_gssapi.c
@@ -91,6 +91,8 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+ gss_channel_bindings_t chan_bindings = GSS_C_NO_CHANNEL_BINDINGS;
+ struct gss_channel_bindings_struct chan;
(void) user;
(void) password;
@@ -148,13 +150,21 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
input_token.length = chlglen;
}
+ /* Set channel binding data if available */
+ if(nego->channel_binding_data.leng > 0) {
+ memset(&chan, 0, sizeof(struct gss_channel_bindings_struct));
+ chan.application_data.length = nego->channel_binding_data.leng;
+ chan.application_data.value = nego->channel_binding_data.bufr;
+ chan_bindings = &chan;
+ }
+
/* 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,
+ chan_bindings,
&input_token,
&output_token,
TRUE,
diff --git a/libs/libcurl/src/vauth/spnego_sspi.c b/libs/libcurl/src/vauth/spnego_sspi.c
index a1883d0cd3..5a0ffe3b57 100644
--- a/libs/libcurl/src/vauth/spnego_sspi.c
+++ b/libs/libcurl/src/vauth/spnego_sspi.c
@@ -57,13 +57,13 @@ bool Curl_auth_is_spnego_supported(void)
SECURITY_STATUS status;
/* Query the security package for Negotiate */
- status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
+ status = Curl_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);
+ Curl_pSecFn->FreeContextBuffer(SecurityPackage);
}
@@ -128,7 +128,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
if(!nego->output_token) {
/* Query the security package for Negotiate */
- nego->status = (DWORD)s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
+ nego->status = (DWORD)Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *)
TEXT(SP_NAME_NEGOTIATE),
&SecurityPackage);
if(nego->status != SEC_E_OK) {
@@ -139,7 +139,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
nego->token_max = SecurityPackage->cbMaxToken;
/* Release the package buffer as it is not required anymore */
- s_pSecFn->FreeContextBuffer(SecurityPackage);
+ Curl_pSecFn->FreeContextBuffer(SecurityPackage);
/* Allocate our output buffer */
nego->output_token = malloc(nego->token_max);
@@ -169,7 +169,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
/* Acquire our credentials handle */
nego->status = (DWORD)
- s_pSecFn->AcquireCredentialsHandle(NULL,
+ Curl_pSecFn->AcquireCredentialsHandle(NULL,
(TCHAR *)TEXT(SP_NAME_NEGOTIATE),
SECPKG_CRED_OUTBOUND, NULL,
nego->p_identity, NULL, NULL,
@@ -218,7 +218,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
SEC_CHANNEL_BINDINGS channelBindings;
SecPkgContext_Bindings pkgBindings;
pkgBindings.Bindings = &channelBindings;
- nego->status = (DWORD)s_pSecFn->QueryContextAttributes(
+ nego->status = (DWORD)Curl_pSecFn->QueryContextAttributes(
nego->sslContext,
SECPKG_ATTR_ENDPOINT_BINDINGS,
&pkgBindings
@@ -242,16 +242,16 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
resp_buf.cbBuffer = curlx_uztoul(nego->token_max);
/* Generate our challenge-response message */
- nego->status = (DWORD)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);
+ nego->status =
+ (DWORD)Curl_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);
@@ -269,7 +269,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
if(nego->status == SEC_I_COMPLETE_NEEDED ||
nego->status == SEC_I_COMPLETE_AND_CONTINUE) {
- nego->status = (DWORD)s_pSecFn->CompleteAuthToken(nego->context,
+ nego->status = (DWORD)Curl_pSecFn->CompleteAuthToken(nego->context,
&resp_desc);
if(GSS_ERROR(nego->status)) {
char buffer[STRERROR_LEN];
@@ -333,14 +333,14 @@ void Curl_auth_cleanup_spnego(struct negotiatedata *nego)
{
/* Free our security context */
if(nego->context) {
- s_pSecFn->DeleteSecurityContext(nego->context);
+ Curl_pSecFn->DeleteSecurityContext(nego->context);
free(nego->context);
nego->context = NULL;
}
/* Free our credentials handle */
if(nego->credentials) {
- s_pSecFn->FreeCredentialsHandle(nego->credentials);
+ Curl_pSecFn->FreeCredentialsHandle(nego->credentials);
free(nego->credentials);
nego->credentials = NULL;
}
diff --git a/libs/libcurl/src/version.c b/libs/libcurl/src/version.c
index 324e55e3b3..fce1d82f0b 100644
--- a/libs/libcurl/src/version.c
+++ b/libs/libcurl/src/version.c
@@ -581,7 +581,7 @@ curl_version_info_data *curl_version_info(CURLversion stamp)
int features = 0;
#if defined(USE_SSH)
- static char ssh_buffer[80];
+ static char ssh_buf[80]; /* 'ssh_buffer' clashes with libssh/libssh.h */
#endif
#ifdef USE_SSL
#ifdef CURL_WITH_MULTI_SSL
@@ -622,8 +622,8 @@ curl_version_info_data *curl_version_info(CURLversion stamp)
#endif
#if defined(USE_SSH)
- Curl_ssh_version(ssh_buffer, sizeof(ssh_buffer));
- version_info.libssh_version = ssh_buffer;
+ Curl_ssh_version(ssh_buf, sizeof(ssh_buf));
+ version_info.libssh_version = ssh_buf;
#endif
#ifdef HAVE_BROTLI
diff --git a/libs/libcurl/src/version_win32.c b/libs/libcurl/src/version_win32.c
index 4898ff1560..b0553c6c07 100644
--- a/libs/libcurl/src/version_win32.c
+++ b/libs/libcurl/src/version_win32.c
@@ -55,7 +55,7 @@ struct OUR_OSVERSIONINFOEXW {
/*
* curlx_verify_windows_version()
*
- * This is used to verify if we are running on a specific windows version.
+ * This is used to verify if we are running on a specific Windows version.
*
* Parameters:
*
diff --git a/libs/libcurl/src/version_win32.h b/libs/libcurl/src/version_win32.h
index f45270c8f4..e07dfba838 100644
--- a/libs/libcurl/src/version_win32.h
+++ b/libs/libcurl/src/version_win32.h
@@ -44,7 +44,7 @@ typedef enum {
PLATFORM_WINNT
} PlatformIdentifier;
-/* This is used to verify if we are running on a specific windows version */
+/* 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,
diff --git a/libs/libcurl/src/vquic/curl_msh3.c b/libs/libcurl/src/vquic/curl_msh3.c
index a780acc31f..464f7c5af6 100644
--- a/libs/libcurl/src/vquic/curl_msh3.c
+++ b/libs/libcurl/src/vquic/curl_msh3.c
@@ -119,16 +119,38 @@ struct cf_msh3_ctx {
struct cf_call_data call_data;
struct curltime connect_started; /* time the current attempt started */
struct curltime handshake_at; /* time connect handshake finished */
- struct Curl_hash streams; /* hash `data->id` to `stream_ctx` */
+ struct Curl_hash streams; /* hash `data->mid` to `stream_ctx` */
/* Flags written by msh3/msquic thread */
bool handshake_complete;
bool handshake_succeeded;
bool connected;
+ BIT(initialized);
/* Flags written by curl thread */
BIT(verbose);
BIT(active);
};
+static void h3_stream_hash_free(void *stream);
+
+static void cf_msh3_ctx_init(struct cf_msh3_ctx *ctx,
+ const struct Curl_addrinfo *ai)
+{
+ DEBUGASSERT(!ctx->initialized);
+ Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
+ Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC);
+ ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
+ ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
+ ctx->initialized = TRUE;
+}
+
+static void cf_msh3_ctx_free(struct cf_msh3_ctx *ctx)
+{
+ if(ctx && ctx->initialized) {
+ Curl_hash_destroy(&ctx->streams);
+ }
+ free(ctx);
+}
+
static struct cf_msh3_ctx *h3_get_msh3_ctx(struct Curl_easy *data);
/* How to access `call_data` from a cf_msh3 filter */
@@ -158,7 +180,7 @@ struct stream_ctx {
};
#define H3_STREAM_CTX(ctx,data) ((struct stream_ctx *)((data && ctx)? \
- Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL))
+ Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
static void h3_stream_ctx_free(struct stream_ctx *stream)
{
@@ -191,7 +213,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
CURL_TRC_CF(data, cf, "data setup");
- if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) {
+ if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
h3_stream_ctx_free(stream);
return CURLE_OUT_OF_MEMORY;
}
@@ -207,7 +229,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
(void)cf;
if(stream) {
CURL_TRC_CF(data, cf, "easy handle is done");
- Curl_hash_offt_remove(&ctx->streams, data->id);
+ Curl_hash_offt_remove(&ctx->streams, data->mid);
}
}
@@ -593,7 +615,8 @@ out:
}
static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
- const void *buf, size_t len, CURLcode *err)
+ const void *buf, size_t len, bool eos,
+ CURLcode *err)
{
struct cf_msh3_ctx *ctx = cf->ctx;
struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
@@ -603,7 +626,6 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
size_t nheader, i;
ssize_t nwritten = -1;
struct cf_call_data save;
- bool eos;
CF_DATA_SAVE(save, cf, data);
@@ -646,21 +668,6 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
nva[i].ValueLength = e->valuelen;
}
- switch(data->state.httpreq) {
- case HTTPREQ_POST:
- case HTTPREQ_POST_FORM:
- case HTTPREQ_POST_MIME:
- case HTTPREQ_PUT:
- /* known request body size or -1 */
- eos = FALSE;
- break;
- default:
- /* there is not request body */
- eos = TRUE;
- stream->upload_done = TRUE;
- break;
- }
-
CURL_TRC_CF(data, cf, "req: send %zu headers", nheader);
stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data,
nva, nheader,
@@ -813,7 +820,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
CURLcode result;
bool verify;
- Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
+ DEBUGASSERT(ctx->initialized);
conn_config = Curl_ssl_cf_get_primary_config(cf);
if(!conn_config)
return CURLE_FAILED_INIT;
@@ -904,7 +911,6 @@ static CURLcode cf_msh3_connect(struct Curl_cfilter *cf,
CURL_TRC_CF(data, cf, "handshake succeeded");
cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
cf->conn->httpversion = 30;
- cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
cf->connected = TRUE;
cf->conn->alpn = CURL_HTTP_VERSION_3;
*done = TRUE;
@@ -940,7 +946,6 @@ static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data)
MsH3ApiClose(ctx->api);
ctx->api = NULL;
}
- Curl_hash_destroy(&ctx->streams);
if(ctx->active) {
/* We share our socket at cf->conn->sock[cf->sockindex] when active.
@@ -979,10 +984,11 @@ static void cf_msh3_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
CF_DATA_SAVE(save, cf, data);
cf_msh3_close(cf, data);
- free(cf->ctx);
- cf->ctx = NULL;
+ if(cf->ctx) {
+ cf_msh3_ctx_free(cf->ctx);
+ cf->ctx = NULL;
+ }
/* no CF_DATA_RESTORE(cf, save); its gone */
-
}
static CURLcode cf_msh3_query(struct Curl_cfilter *cf,
@@ -1081,9 +1087,7 @@ CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf,
result = CURLE_OUT_OF_MEMORY;
goto out;
}
- Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC);
- ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
- ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
+ cf_msh3_ctx_init(ctx, ai);
result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
@@ -1091,7 +1095,7 @@ out:
*pcf = (!result)? cf : NULL;
if(result) {
Curl_safefree(cf);
- Curl_safefree(ctx);
+ cf_msh3_ctx_free(ctx);
}
return result;
diff --git a/libs/libcurl/src/vquic/curl_ngtcp2.c b/libs/libcurl/src/vquic/curl_ngtcp2.c
index 790e3c6ff4..54f3ce6929 100644
--- a/libs/libcurl/src/vquic/curl_ngtcp2.c
+++ b/libs/libcurl/src/vquic/curl_ngtcp2.c
@@ -132,12 +132,13 @@ struct cf_ngtcp2_ctx {
struct curltime reconnect_at; /* time the next attempt should start */
struct bufc_pool stream_bufcp; /* chunk pool for streams */
struct dynbuf scratch; /* temp buffer for header construction */
- struct Curl_hash streams; /* hash `data->id` to `h3_stream_ctx` */
+ struct Curl_hash streams; /* hash `data->mid` to `h3_stream_ctx` */
size_t max_stream_window; /* max flow window for one stream */
uint64_t max_idle_ms; /* max idle time for QUIC connection */
uint64_t used_bidi_streams; /* bidi streams we have opened */
uint64_t max_bidi_streams; /* max bidi streams we can open */
int qlogfd;
+ BIT(initialized);
BIT(shutdown_started); /* queued shutdown packets */
};
@@ -146,6 +147,34 @@ struct cf_ngtcp2_ctx {
#define CF_CTX_CALL_DATA(cf) \
((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data
+static void h3_stream_hash_free(void *stream);
+
+static void cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx)
+{
+ DEBUGASSERT(!ctx->initialized);
+ ctx->qlogfd = -1;
+ ctx->version = NGTCP2_PROTO_VER_MAX;
+ ctx->max_stream_window = H3_STREAM_WINDOW_SIZE;
+ ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS;
+ Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
+ H3_STREAM_POOL_SPARES);
+ Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
+ Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
+ ctx->initialized = TRUE;
+}
+
+static void cf_ngtcp2_ctx_free(struct cf_ngtcp2_ctx *ctx)
+{
+ if(ctx && ctx->initialized) {
+ Curl_bufcp_free(&ctx->stream_bufcp);
+ Curl_dyn_free(&ctx->scratch);
+ Curl_hash_clean(&ctx->streams);
+ Curl_hash_destroy(&ctx->streams);
+ Curl_ssl_peer_cleanup(&ctx->peer);
+ }
+ free(ctx);
+}
+
struct pkt_io_ctx;
static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
struct Curl_easy *data,
@@ -174,7 +203,7 @@ struct h3_stream_ctx {
};
#define H3_STREAM_CTX(ctx,data) ((struct h3_stream_ctx *)(\
- data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL))
+ data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
#define H3_STREAM_CTX_ID(ctx,id) ((struct h3_stream_ctx *)(\
Curl_hash_offt_get(&(ctx)->streams, (id))))
@@ -197,10 +226,8 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
struct cf_ngtcp2_ctx *ctx = cf->ctx;
struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
- if(!data) {
- failf(data, "initialization failure, transfer not http initialized");
+ if(!data)
return CURLE_FAILED_INIT;
- }
if(stream)
return CURLE_OK;
@@ -216,7 +243,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
stream->sendbuf_len_in_flight = 0;
Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
- if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) {
+ if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
h3_stream_ctx_free(stream);
return CURLE_OUT_OF_MEMORY;
}
@@ -241,7 +268,7 @@ static void cf_ngtcp2_stream_close(struct Curl_cfilter *cf,
NGHTTP3_H3_REQUEST_CANCELLED);
result = cf_progress_egress(cf, data, NULL);
if(result)
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cancel stream -> %d",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cancel stream -> %d",
stream->id, result);
}
}
@@ -252,10 +279,10 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
(void)cf;
if(stream) {
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] easy handle is done",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] easy handle is done",
stream->id);
cf_ngtcp2_stream_close(cf, data, stream);
- Curl_hash_offt_remove(&ctx->streams, data->id);
+ Curl_hash_offt_remove(&ctx->streams, data->mid);
}
}
@@ -265,7 +292,6 @@ static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf,
struct h3_stream_ctx **pstream)
{
struct cf_ngtcp2_ctx *ctx = cf->ctx;
- struct Curl_easy *sdata;
struct h3_stream_ctx *stream;
(void)cf;
@@ -275,8 +301,10 @@ static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf,
return data;
}
else {
+ struct Curl_llist_node *e;
DEBUGASSERT(data->multi);
- for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
+ for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
+ struct Curl_easy *sdata = Curl_node_elem(e);
if(sdata->conn != data->conn)
continue;
stream = H3_STREAM_CTX(ctx, sdata);
@@ -490,12 +518,12 @@ static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags,
if(!data)
data = CF_DATA_CURRENT(cf);
if(data)
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read_stream(len=%zu) -> %zd",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read_stream(len=%zu) -> %zd",
stream_id, buflen, nconsumed);
if(nconsumed < 0) {
struct h3_stream_ctx *stream = H3_STREAM_CTX_ID(ctx, stream_id);
if(data && stream) {
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] error on known stream, "
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] error on known stream, "
"reset=%d, closed=%d",
stream_id, stream->reset, stream->closed);
}
@@ -555,8 +583,8 @@ static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags,
}
rv = nghttp3_conn_close_stream(ctx->h3conn, stream_id, app_error_code);
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] quic close(app_error=%"
- CURL_PRIu64 ") -> %d", stream_id, (curl_uint64_t)app_error_code,
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] quic close(app_error=%"
+ FMT_PRIu64 ") -> %d", stream_id, (curl_uint64_t)app_error_code,
rv);
if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
cf_ngtcp2_h3_err_set(cf, data, rv);
@@ -581,7 +609,7 @@ static int cb_stream_reset(ngtcp2_conn *tconn, int64_t sid,
(void)data;
rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id);
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reset -> %d", stream_id, rv);
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reset -> %d", stream_id, rv);
if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
@@ -619,8 +647,8 @@ static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn,
(void)tconn;
ctx->max_bidi_streams = max_streams;
if(data)
- CURL_TRC_CF(data, cf, "max bidi streams now %" CURL_PRIu64
- ", used %" CURL_PRIu64, (curl_uint64_t)ctx->max_bidi_streams,
+ CURL_TRC_CF(data, cf, "max bidi streams now %" FMT_PRIu64
+ ", used %" FMT_PRIu64, (curl_uint64_t)ctx->max_bidi_streams,
(curl_uint64_t)ctx->used_bidi_streams);
return 0;
}
@@ -646,8 +674,7 @@ static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t sid,
}
s_data = get_stream_easy(cf, data, stream_id, &stream);
if(s_data && stream && stream->quic_flow_blocked) {
- CURL_TRC_CF(s_data, cf, "[%" CURL_PRId64 "] unblock quic flow",
- stream_id);
+ CURL_TRC_CF(s_data, cf, "[%" FMT_PRId64 "] unblock quic flow", stream_id);
stream->quic_flow_blocked = FALSE;
h3_drain_stream(cf, s_data);
}
@@ -705,6 +732,11 @@ static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level,
return 0;
}
+#if defined(_MSC_VER) && defined(_DLL)
+# pragma warning(push)
+# pragma warning(disable:4232) /* MSVC extension, dllimport identity */
+#endif
+
static ngtcp2_callbacks ng_callbacks = {
ngtcp2_crypto_client_initial_cb,
NULL, /* recv_client_initial */
@@ -748,6 +780,10 @@ static ngtcp2_callbacks ng_callbacks = {
NULL, /* early_data_rejected */
};
+#if defined(_MSC_VER) && defined(_DLL)
+# pragma warning(pop)
+#endif
+
/**
* Connection maintenance like timeouts on packet ACKs etc. are done by us, not
* the OS like for TCP. POLL events on the socket therefore are not
@@ -858,11 +894,11 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t sid,
if(stream->error3 != NGHTTP3_H3_NO_ERROR) {
stream->reset = TRUE;
stream->send_closed = TRUE;
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] RESET: error %" CURL_PRIu64,
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] RESET: error %" FMT_PRIu64,
stream->id, stream->error3);
}
else {
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] CLOSED", stream->id);
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] CLOSED", stream->id);
}
h3_drain_stream(cf, data);
return 0;
@@ -878,7 +914,7 @@ static void h3_xfer_write_resp_hd(struct Curl_cfilter *cf,
if(!stream->xfer_result) {
stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos);
if(stream->xfer_result)
- CURL_TRC_CF(data, cf, "[%"CURL_PRId64"] error %d writing %zu "
+ CURL_TRC_CF(data, cf, "[%"FMT_PRId64"] error %d writing %zu "
"bytes of headers", stream->id, stream->xfer_result, blen);
}
}
@@ -894,7 +930,7 @@ static void h3_xfer_write_resp(struct Curl_cfilter *cf,
stream->xfer_result = Curl_xfer_write_resp(data, buf, blen, eos);
/* If the transfer write is errored, we do not want any more data */
if(stream->xfer_result) {
- CURL_TRC_CF(data, cf, "[%"CURL_PRId64"] error %d writing %zu bytes "
+ CURL_TRC_CF(data, cf, "[%"FMT_PRId64"] error %d writing %zu bytes "
"of data", stream->id, stream->xfer_result, blen);
}
}
@@ -917,12 +953,12 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
h3_xfer_write_resp(cf, data, stream, (char *)buf, blen, FALSE);
if(blen) {
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] ACK %zu bytes of DATA",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] ACK %zu bytes of DATA",
stream->id, blen);
ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, blen);
ngtcp2_conn_extend_max_offset(ctx->qconn, blen);
}
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] DATA len=%zu", stream->id, blen);
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] DATA len=%zu", stream->id, blen);
return 0;
}
@@ -960,7 +996,7 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid,
/* add a CRLF only if we have received some headers */
h3_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), stream->closed);
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] end_headers, status=%d",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] end_headers, status=%d",
stream_id, stream->status_code);
if(stream->status_code / 100 != 1) {
stream->resp_hds_complete = TRUE;
@@ -1008,7 +1044,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid,
if(!result)
h3_xfer_write_resp_hd(cf, data, stream, Curl_dyn_ptr(&ctx->scratch),
Curl_dyn_len(&ctx->scratch), FALSE);
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] status: %s",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] status: %s",
stream_id, Curl_dyn_ptr(&ctx->scratch));
if(result) {
return -1;
@@ -1016,7 +1052,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid,
}
else {
/* store as an HTTP1-style header */
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] header: %.*s: %.*s",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] header: %.*s: %.*s",
stream_id, (int)h3name.len, h3name.base,
(int)h3val.len, h3val.base);
Curl_dyn_reset(&ctx->scratch);
@@ -1049,7 +1085,7 @@ static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id,
rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, 0, stream_id,
app_error_code);
if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
}
return 0;
@@ -1068,9 +1104,9 @@ static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t sid,
rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id,
app_error_code);
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reset -> %d", stream_id, rv);
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reset -> %d", stream_id, rv);
if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
}
return 0;
@@ -1163,14 +1199,13 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
(void)cf;
if(stream->reset) {
- failf(data,
- "HTTP/3 stream %" CURL_PRId64 " reset by server", stream->id);
+ failf(data, "HTTP/3 stream %" FMT_PRId64 " reset by server", stream->id);
*err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP3;
goto out;
}
else if(!stream->resp_hds_complete) {
failf(data,
- "HTTP/3 stream %" CURL_PRId64 " was closed cleanly, but before "
+ "HTTP/3 stream %" FMT_PRId64 " was closed cleanly, but before "
"getting all response header fields, treated as error",
stream->id);
*err = CURLE_HTTP3;
@@ -1217,7 +1252,7 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
}
if(stream->xfer_result) {
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] xfer write failed", stream->id);
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] xfer write failed", stream->id);
cf_ngtcp2_stream_close(cf, data, stream);
*err = stream->xfer_result;
nread = -1;
@@ -1242,7 +1277,7 @@ out:
nread = -1;
}
}
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_recv(blen=%zu) -> %zd, %d",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_recv(blen=%zu) -> %zd, %d",
stream? stream->id : -1, blen, nread, *err);
CF_DATA_RESTORE(cf, save);
return nread;
@@ -1275,7 +1310,7 @@ static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id,
if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) {
int rv = nghttp3_conn_resume_stream(conn, stream_id);
if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
}
}
return 0;
@@ -1333,14 +1368,13 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id,
}
else if(!nwritten) {
/* Not EOF, and nothing to give, we signal WOULDBLOCK. */
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read req body -> AGAIN",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> AGAIN",
stream->id);
return NGHTTP3_ERR_WOULDBLOCK;
}
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read req body -> "
- "%d vecs%s with %zu (buffered=%zu, left=%"
- CURL_FORMAT_CURL_OFF_T ")",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> "
+ "%d vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")",
stream->id, (int)nvecs,
*pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
nwritten, Curl_bufq_len(&stream->sendbuf),
@@ -1454,12 +1488,12 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf,
if(rc) {
switch(rc) {
case NGHTTP3_ERR_CONN_CLOSING:
- CURL_TRC_CF(data, cf, "h3sid[%" CURL_PRId64 "] failed to send, "
+ CURL_TRC_CF(data, cf, "h3sid[%" FMT_PRId64 "] failed to send, "
"connection is closing", stream->id);
break;
default:
- CURL_TRC_CF(data, cf, "h3sid[%" CURL_PRId64 "] failed to send -> "
- "%d (%s)", stream->id, rc, ngtcp2_strerror(rc));
+ CURL_TRC_CF(data, cf, "h3sid[%" FMT_PRId64 "] failed to send -> "
+ "%d (%s)", stream->id, rc, nghttp3_strerror(rc));
break;
}
*err = CURLE_SEND_ERROR;
@@ -1468,10 +1502,10 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf,
}
if(Curl_trc_is_verbose(data)) {
- infof(data, "[HTTP/3] [%" CURL_PRId64 "] OPENED stream for %s",
+ infof(data, "[HTTP/3] [%" FMT_PRId64 "] OPENED stream for %s",
stream->id, data->state.url);
for(i = 0; i < nheader; ++i) {
- infof(data, "[HTTP/3] [%" CURL_PRId64 "] [%.*s: %.*s]", stream->id,
+ infof(data, "[HTTP/3] [%" FMT_PRId64 "] [%.*s: %.*s]", stream->id,
(int)nva[i].namelen, nva[i].name,
(int)nva[i].valuelen, nva[i].value);
}
@@ -1484,7 +1518,8 @@ out:
}
static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
- const void *buf, size_t len, CURLcode *err)
+ const void *buf, size_t len, bool eos,
+ CURLcode *err)
{
struct cf_ngtcp2_ctx *ctx = cf->ctx;
struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
@@ -1500,6 +1535,7 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
pktx_init(&pktx, cf, data);
*err = CURLE_OK;
+ (void)eos; /* TODO: use for stream EOF and block handling */
result = cf_progress_ingress(cf, data, &pktx);
if(result) {
*err = result;
@@ -1521,7 +1557,7 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
stream = H3_STREAM_CTX(ctx, data);
}
else if(stream->xfer_result) {
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] xfer write failed", stream->id);
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] xfer write failed", stream->id);
cf_ngtcp2_stream_close(cf, data, stream);
*err = stream->xfer_result;
sent = -1;
@@ -1534,13 +1570,13 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
* body. This happens on 30x or 40x responses.
* We silently discard the data sent, since this is not a transport
* error situation. */
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] discarding data"
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] discarding data"
"on closed stream with response", stream->id);
*err = CURLE_OK;
sent = (ssize_t)len;
goto out;
}
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] send_body(len=%zu) "
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] send_body(len=%zu) "
"-> stream closed", stream->id, len);
*err = CURLE_HTTP3;
sent = -1;
@@ -1554,7 +1590,7 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
}
else {
sent = Curl_bufq_write(&stream->sendbuf, buf, len, err);
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send, add to "
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send, add to "
"sendbuf(len=%zu) -> %zd, %d",
stream->id, len, sent, *err);
if(sent < 0) {
@@ -1576,7 +1612,7 @@ out:
*err = result;
sent = -1;
}
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send(len=%zu) -> %zd, %d",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send(len=%zu) -> %zd, %d",
stream? stream->id : -1, len, sent, *err);
CF_DATA_RESTORE(cf, save);
return sent;
@@ -1589,7 +1625,6 @@ static CURLcode qng_verify_peer(struct Curl_cfilter *cf,
cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
cf->conn->httpversion = 30;
- cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
}
@@ -1723,7 +1758,7 @@ static ssize_t read_pkt_to_send(void *userp,
struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, x->data);
DEBUGASSERT(ndatalen == -1);
nghttp3_conn_block_stream(ctx->h3conn, stream_id);
- CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRId64 "] block quic flow",
+ CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRId64 "] block quic flow",
(curl_int64_t)stream_id);
DEBUGASSERT(stream);
if(stream)
@@ -1960,27 +1995,22 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf,
return result;
}
-static void cf_ngtcp2_ctx_clear(struct cf_ngtcp2_ctx *ctx)
+static void cf_ngtcp2_ctx_close(struct cf_ngtcp2_ctx *ctx)
{
struct cf_call_data save = ctx->call_data;
+ if(!ctx->initialized)
+ return;
if(ctx->qlogfd != -1) {
close(ctx->qlogfd);
}
+ ctx->qlogfd = -1;
Curl_vquic_tls_cleanup(&ctx->tls);
vquic_ctx_free(&ctx->q);
if(ctx->h3conn)
nghttp3_conn_del(ctx->h3conn);
if(ctx->qconn)
ngtcp2_conn_del(ctx->qconn);
- Curl_bufcp_free(&ctx->stream_bufcp);
- Curl_dyn_free(&ctx->scratch);
- Curl_hash_clean(&ctx->streams);
- Curl_hash_destroy(&ctx->streams);
- Curl_ssl_peer_cleanup(&ctx->peer);
-
- memset(ctx, 0, sizeof(*ctx));
- ctx->qlogfd = -1;
ctx->call_data = save;
}
@@ -2027,7 +2057,7 @@ static CURLcode cf_ngtcp2_shutdown(struct Curl_cfilter *cf,
(uint8_t *)buffer, sizeof(buffer),
&ctx->last_error, pktx.ts);
CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%"
- CURL_PRIu64 ") -> %d", ctx->last_error.type,
+ FMT_PRIu64 ") -> %d", ctx->last_error.type,
(curl_uint64_t)ctx->last_error.error_code, (int)nwritten);
if(nwritten > 0) {
Curl_bufq_write(&ctx->q.sendbuf, (const unsigned char *)buffer,
@@ -2085,7 +2115,7 @@ static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data)
CF_DATA_SAVE(save, cf, data);
if(ctx && ctx->qconn) {
cf_ngtcp2_conn_close(cf, data);
- cf_ngtcp2_ctx_clear(ctx);
+ cf_ngtcp2_ctx_close(ctx);
CURL_TRC_CF(data, cf, "close");
}
cf->connected = FALSE;
@@ -2094,18 +2124,11 @@ static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data)
static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- struct cf_ngtcp2_ctx *ctx = cf->ctx;
- struct cf_call_data save;
-
- CF_DATA_SAVE(save, cf, data);
CURL_TRC_CF(data, cf, "destroy");
- if(ctx) {
- cf_ngtcp2_ctx_clear(ctx);
- free(ctx);
+ if(cf->ctx) {
+ cf_ngtcp2_ctx_free(cf->ctx);
+ cf->ctx = NULL;
}
- cf->ctx = NULL;
- /* No CF_DATA_RESTORE(cf, save) possible */
- (void)save;
}
#ifdef USE_OPENSSL
@@ -2187,14 +2210,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
const struct Curl_sockaddr_ex *sockaddr = NULL;
int qfd;
- ctx->version = NGTCP2_PROTO_VER_MAX;
- ctx->max_stream_window = H3_STREAM_WINDOW_SIZE;
- ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS;
- Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
- H3_STREAM_POOL_SPARES);
- Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
- Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
-
+ DEBUGASSERT(ctx->initialized);
result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
if(result)
return result;
@@ -2389,7 +2405,7 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf,
}
else /* transport params not arrived yet? take our default. */
*pres1 = (int)Curl_multi_max_concurrent_streams(data->multi);
- CURL_TRC_CF(data, cf, "query conn[%" CURL_FORMAT_CURL_OFF_T "]: "
+ CURL_TRC_CF(data, cf, "query conn[%" FMT_OFF_T "]: "
"MAX_CONCURRENT -> %d (%zu in use)",
cf->conn->connection_id, *pres1, CONN_INUSE(cf->conn));
CF_DATA_RESTORE(cf, save);
@@ -2509,8 +2525,7 @@ CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf,
result = CURLE_OUT_OF_MEMORY;
goto out;
}
- ctx->qlogfd = -1;
- cf_ngtcp2_ctx_clear(ctx);
+ cf_ngtcp2_ctx_init(ctx);
result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
if(result)
@@ -2531,7 +2546,7 @@ out:
if(udp_cf)
Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE);
Curl_safefree(cf);
- Curl_safefree(ctx);
+ cf_ngtcp2_ctx_free(ctx);
}
return result;
}
diff --git a/libs/libcurl/src/vquic/curl_osslq.c b/libs/libcurl/src/vquic/curl_osslq.c
index 59c1f5053e..1f83726e93 100644
--- a/libs/libcurl/src/vquic/curl_osslq.c
+++ b/libs/libcurl/src/vquic/curl_osslq.c
@@ -290,9 +290,10 @@ struct cf_osslq_ctx {
struct curltime first_byte_at; /* when first byte was recvd */
struct curltime reconnect_at; /* time the next attempt should start */
struct bufc_pool stream_bufcp; /* chunk pool for streams */
- struct Curl_hash streams; /* hash `data->id` to `h3_stream_ctx` */
+ struct Curl_hash streams; /* hash `data->mid` to `h3_stream_ctx` */
size_t max_stream_window; /* max flow window for one stream */
uint64_t max_idle_ms; /* max idle time for QUIC connection */
+ BIT(initialized);
BIT(got_first_byte); /* if first byte was received */
BIT(x509_store_setup); /* if x509 store has been set up */
BIT(protocol_shutdown); /* QUIC connection is shut down */
@@ -300,19 +301,35 @@ struct cf_osslq_ctx {
BIT(need_send); /* QUIC connection needs to send */
};
-static void cf_osslq_ctx_clear(struct cf_osslq_ctx *ctx)
+static void h3_stream_hash_free(void *stream);
+
+static void cf_osslq_ctx_init(struct cf_osslq_ctx *ctx)
+{
+ DEBUGASSERT(!ctx->initialized);
+ Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
+ H3_STREAM_POOL_SPARES);
+ Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
+ ctx->initialized = TRUE;
+}
+
+static void cf_osslq_ctx_free(struct cf_osslq_ctx *ctx)
+{
+ if(ctx && ctx->initialized) {
+ Curl_bufcp_free(&ctx->stream_bufcp);
+ Curl_hash_clean(&ctx->streams);
+ Curl_hash_destroy(&ctx->streams);
+ Curl_ssl_peer_cleanup(&ctx->peer);
+ }
+ free(ctx);
+}
+
+static void cf_osslq_ctx_close(struct cf_osslq_ctx *ctx)
{
struct cf_call_data save = ctx->call_data;
cf_osslq_h3conn_cleanup(&ctx->h3);
Curl_vquic_tls_cleanup(&ctx->tls);
vquic_ctx_free(&ctx->q);
- Curl_bufcp_free(&ctx->stream_bufcp);
- Curl_hash_clean(&ctx->streams);
- Curl_hash_destroy(&ctx->streams);
- Curl_ssl_peer_cleanup(&ctx->peer);
-
- memset(ctx, 0, sizeof(*ctx));
ctx->call_data = save;
}
@@ -401,7 +418,7 @@ static void cf_osslq_close(struct Curl_cfilter *cf, struct Curl_easy *data)
(SSL_SHUTDOWN_FLAG_NO_BLOCK | SSL_SHUTDOWN_FLAG_RAPID),
NULL, 0);
}
- cf_osslq_ctx_clear(ctx);
+ cf_osslq_ctx_close(ctx);
}
cf->connected = FALSE;
@@ -417,8 +434,9 @@ static void cf_osslq_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
CURL_TRC_CF(data, cf, "destroy");
if(ctx) {
CURL_TRC_CF(data, cf, "cf_osslq_destroy()");
- cf_osslq_ctx_clear(ctx);
- free(ctx);
+ if(ctx->tls.ossl.ssl)
+ cf_osslq_ctx_close(ctx);
+ cf_osslq_ctx_free(ctx);
}
cf->ctx = NULL;
/* No CF_DATA_RESTORE(cf, save) possible */
@@ -435,7 +453,7 @@ static CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3,
if(h3->remote_ctrl_n >= ARRAYSIZE(h3->remote_ctrl)) {
/* rejected, we are full */
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] rejecting remote stream",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] rejecting remote stream",
stream_id);
SSL_free(stream_ssl);
return CURLE_FAILED_INIT;
@@ -446,12 +464,12 @@ static CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3,
nstream->id = stream_id;
nstream->ssl = stream_ssl;
Curl_bufq_initp(&nstream->recvbuf, &ctx->stream_bufcp, 1, BUFQ_OPT_NONE);
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] accepted remote uni stream",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] accepted remote uni stream",
stream_id);
break;
}
default:
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reject remote non-uni-read"
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reject remote non-uni-read"
" stream", stream_id);
SSL_free(stream_ssl);
return CURLE_FAILED_INIT;
@@ -546,7 +564,6 @@ static CURLcode cf_osslq_verify_peer(struct Curl_cfilter *cf,
cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
cf->conn->httpversion = 30;
- cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
}
@@ -573,7 +590,7 @@ struct h3_stream_ctx {
};
#define H3_STREAM_CTX(ctx,data) ((struct h3_stream_ctx *)(\
- data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL))
+ data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
static void h3_stream_ctx_free(struct h3_stream_ctx *stream)
{
@@ -596,10 +613,8 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
struct cf_osslq_ctx *ctx = cf->ctx;
struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
- if(!data) {
- failf(data, "initialization failure, transfer not http initialized");
+ if(!data)
return CURLE_FAILED_INIT;
- }
if(stream)
return CURLE_OK;
@@ -620,7 +635,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
stream->recv_buf_nonflow = 0;
Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
- if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) {
+ if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
h3_stream_ctx_free(stream);
return CURLE_OUT_OF_MEMORY;
}
@@ -635,7 +650,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
(void)cf;
if(stream) {
- CURL_TRC_CF(data, cf, "[%"CURL_PRId64"] easy handle is done",
+ CURL_TRC_CF(data, cf, "[%"FMT_PRId64"] easy handle is done",
stream->s.id);
if(ctx->h3.conn && !stream->closed) {
nghttp3_conn_shutdown_stream_read(ctx->h3.conn, stream->s.id);
@@ -645,7 +660,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
stream->closed = TRUE;
}
- Curl_hash_offt_remove(&ctx->streams, data->id);
+ Curl_hash_offt_remove(&ctx->streams, data->mid);
}
}
@@ -655,7 +670,6 @@ static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf,
{
struct cf_osslq_ctx *ctx = cf->ctx;
struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
- struct Curl_easy *sdata;
if(stream && stream->s.id == stream_id) {
return &stream->s;
@@ -670,8 +684,10 @@ static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf,
return &ctx->h3.s_qpack_dec;
}
else {
+ struct Curl_llist_node *e;
DEBUGASSERT(data->multi);
- for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
+ for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
+ struct Curl_easy *sdata = Curl_node_elem(e);
if(sdata->conn != data->conn)
continue;
stream = H3_STREAM_CTX(ctx, sdata);
@@ -732,11 +748,11 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
if(stream->error3 != NGHTTP3_H3_NO_ERROR) {
stream->reset = TRUE;
stream->send_closed = TRUE;
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] RESET: error %" CURL_PRIu64,
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] RESET: error %" FMT_PRIu64,
stream->s.id, stream->error3);
}
else {
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] CLOSED", stream->s.id);
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] CLOSED", stream->s.id);
}
h3_drain_stream(cf, data);
return 0;
@@ -796,12 +812,12 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
result = write_resp_raw(cf, data, buf, buflen, TRUE);
if(result) {
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] DATA len=%zu, ERROR %d",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] DATA len=%zu, ERROR %d",
stream->s.id, buflen, result);
return NGHTTP3_ERR_CALLBACK_FAILURE;
}
stream->download_recvd += (curl_off_t)buflen;
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] DATA len=%zu, total=%zd",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] DATA len=%zu, total=%zd",
stream->s.id, buflen, stream->download_recvd);
h3_drain_stream(cf, data);
return 0;
@@ -819,7 +835,7 @@ static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id,
(void)conn;
(void)stream_id;
if(stream)
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] deferred consume %zu bytes",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] deferred consume %zu bytes",
stream->s.id, consumed);
return 0;
}
@@ -857,7 +873,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid,
return -1;
ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
stream->status_code);
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] status: %s", stream_id, line);
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] status: %s", stream_id, line);
result = write_resp_raw(cf, data, line, ncopy, FALSE);
if(result) {
return -1;
@@ -865,7 +881,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid,
}
else {
/* store as an HTTP1-style header */
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] header: %.*s: %.*s",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] header: %.*s: %.*s",
stream_id, (int)h3name.len, h3name.base,
(int)h3val.len, h3val.base);
result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE);
@@ -910,7 +926,7 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid,
return -1;
}
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] end_headers, status=%d",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] end_headers, status=%d",
stream_id, stream->status_code);
if(stream->status_code / 100 != 1) {
stream->resp_hds_complete = TRUE;
@@ -934,7 +950,7 @@ static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t sid,
if(!stream || !stream->s.ssl)
return 0;
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] stop_sending", stream_id);
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] stop_sending", stream_id);
cf_osslq_stream_close(&stream->s);
return 0;
}
@@ -954,7 +970,7 @@ static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t sid,
SSL_STREAM_RESET_ARGS args = {0};
args.quic_error_code = app_error_code;
rv = !SSL_stream_reset(stream->s.ssl, &args, sizeof(args));
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reset -> %d", stream_id, rv);
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reset -> %d", stream_id, rv);
if(!rv) {
return NGHTTP3_ERR_CALLBACK_FAILURE;
}
@@ -1014,14 +1030,13 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id,
}
else if(!nwritten) {
/* Not EOF, and nothing to give, we signal WOULDBLOCK. */
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read req body -> AGAIN",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> AGAIN",
stream->s.id);
return NGHTTP3_ERR_WOULDBLOCK;
}
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read req body -> "
- "%d vecs%s with %zu (buffered=%zu, left=%"
- CURL_FORMAT_CURL_OFF_T ")",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> "
+ "%d vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")",
stream->s.id, (int)nvecs,
*pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
nwritten, Curl_bufq_len(&stream->sendbuf),
@@ -1147,9 +1162,7 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
BIO *bio = NULL;
BIO_ADDR *baddr = NULL;
- Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
- H3_STREAM_POOL_SPARES);
- Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
+ DEBUGASSERT(ctx->initialized);
result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
if(result)
goto out;
@@ -1263,7 +1276,7 @@ static ssize_t h3_quic_recv(void *reader_ctx,
return -1;
}
else if(detail == SSL_ERROR_ZERO_RETURN) {
- CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRId64 "] h3_quic_recv -> EOS",
+ CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRId64 "] h3_quic_recv -> EOS",
x->s->id);
x->s->recvd_eos = TRUE;
return 0;
@@ -1272,8 +1285,8 @@ static ssize_t h3_quic_recv(void *reader_ctx,
SSL_STREAM_STATE_RESET_REMOTE) {
uint64_t app_error_code = NGHTTP3_H3_NO_ERROR;
SSL_get_stream_read_error_code(x->s->ssl, &app_error_code);
- CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRId64 "] h3_quic_recv -> RESET, "
- "rv=%d, app_err=%" CURL_PRIu64,
+ CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRId64 "] h3_quic_recv -> RESET, "
+ "rv=%d, app_err=%" FMT_PRIu64,
x->s->id, rv, (curl_uint64_t)app_error_code);
if(app_error_code != NGHTTP3_H3_NO_ERROR) {
x->s->reset = TRUE;
@@ -1329,7 +1342,7 @@ static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s,
while(Curl_bufq_peek(&s->recvbuf, &buf, &blen)) {
nread = nghttp3_conn_read_stream(ctx->h3.conn, s->id,
buf, blen, 0);
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] forward %zu bytes "
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] forward %zu bytes "
"to nghttp3 -> %zd", s->id, blen, nread);
if(nread < 0) {
failf(data, "nghttp3_conn_read_stream(len=%zu) error: %s",
@@ -1368,7 +1381,7 @@ static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s,
rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id,
NGHTTP3_H3_NO_ERROR);
s->closed = TRUE;
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] close nghttp3 stream -> %d",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] close nghttp3 stream -> %d",
s->id, rv);
if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
failf(data, "nghttp3_conn_close_stream returned error: %s",
@@ -1381,7 +1394,7 @@ static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s,
}
out:
if(result)
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_osslq_stream_recv -> %d",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_osslq_stream_recv -> %d",
s->id, result);
return result;
}
@@ -1422,11 +1435,12 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
}
if(ctx->h3.conn) {
- struct Curl_easy *sdata;
+ struct Curl_llist_node *e;
struct h3_stream_ctx *stream;
/* PULL all open streams */
DEBUGASSERT(data->multi);
- for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
+ for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
+ struct Curl_easy *sdata = Curl_node_elem(e);
if(sdata->conn == data->conn && CURL_WANT_RECV(sdata)) {
stream = H3_STREAM_CTX(ctx, sdata);
if(stream && !stream->closed &&
@@ -1449,11 +1463,12 @@ static CURLcode cf_osslq_check_and_unblock(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_osslq_ctx *ctx = cf->ctx;
- struct Curl_easy *sdata;
struct h3_stream_ctx *stream;
if(ctx->h3.conn) {
- for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
+ struct Curl_llist_node *e;
+ for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
+ struct Curl_easy *sdata = Curl_node_elem(e);
if(sdata->conn == data->conn) {
stream = H3_STREAM_CTX(ctx, sdata);
if(stream && stream->s.ssl && stream->s.send_blocked &&
@@ -1505,7 +1520,7 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf,
s = cf_osslq_get_qstream(cf, data, stream_id);
if(!s) {
failf(data, "nghttp3_conn_writev_stream gave unknown stream %"
- CURL_PRId64, (curl_int64_t)stream_id);
+ FMT_PRId64, (curl_int64_t)stream_id);
result = CURLE_SEND_ERROR;
goto out;
}
@@ -1526,7 +1541,7 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf,
if(ok) {
/* As OpenSSL buffers the data, we count this as acknowledged
* from nghttp3's point of view */
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] send %zu bytes to QUIC ok",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] send %zu bytes to QUIC ok",
s->id, vec[i].len);
acked_len += vec[i].len;
}
@@ -1536,14 +1551,14 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf,
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_READ:
/* QUIC blocked us from writing more */
- CURL_TRC_CF(data, cf, "[%"CURL_PRId64 "] send %zu bytes to "
+ CURL_TRC_CF(data, cf, "[%"FMT_PRId64 "] send %zu bytes to "
"QUIC blocked", s->id, vec[i].len);
written = 0;
nghttp3_conn_block_stream(ctx->h3.conn, s->id);
s->send_blocked = blocked = TRUE;
break;
default:
- failf(data, "[%"CURL_PRId64 "] send %zu bytes to QUIC, SSL error %d",
+ failf(data, "[%"FMT_PRId64 "] send %zu bytes to QUIC, SSL error %d",
s->id, vec[i].len, detail);
result = cf_osslq_ssl_err(cf, data, detail, CURLE_HTTP3);
goto out;
@@ -1569,13 +1584,13 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf,
result = CURLE_SEND_ERROR;
goto out;
}
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] forwarded %zu/%zu h3 bytes "
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] forwarded %zu/%zu h3 bytes "
"to QUIC, eos=%d", s->id, acked_len, total_len, eos);
}
if(eos && !s->send_blocked && !eos_written) {
/* wrote everything and H3 indicates end of stream */
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] closing QUIC stream", s->id);
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] closing QUIC stream", s->id);
SSL_stream_conclude(s->ssl, 0);
}
}
@@ -1868,11 +1883,11 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf,
if(rc) {
switch(rc) {
case NGHTTP3_ERR_CONN_CLOSING:
- CURL_TRC_CF(data, cf, "h3sid[%"CURL_PRId64"] failed to send, "
+ CURL_TRC_CF(data, cf, "h3sid[%"FMT_PRId64"] failed to send, "
"connection is closing", stream->s.id);
break;
default:
- CURL_TRC_CF(data, cf, "h3sid[%"CURL_PRId64 "] failed to send -> %d (%s)",
+ CURL_TRC_CF(data, cf, "h3sid[%"FMT_PRId64 "] failed to send -> %d (%s)",
stream->s.id, rc, nghttp3_strerror(rc));
break;
}
@@ -1882,10 +1897,10 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf,
}
if(Curl_trc_is_verbose(data)) {
- infof(data, "[HTTP/3] [%" CURL_PRId64 "] OPENED stream for %s",
+ infof(data, "[HTTP/3] [%" FMT_PRId64 "] OPENED stream for %s",
stream->s.id, data->state.url);
for(i = 0; i < nheader; ++i) {
- infof(data, "[HTTP/3] [%" CURL_PRId64 "] [%.*s: %.*s]",
+ infof(data, "[HTTP/3] [%" FMT_PRId64 "] [%.*s: %.*s]",
stream->s.id,
(int)nva[i].namelen, nva[i].name,
(int)nva[i].valuelen, nva[i].value);
@@ -1899,7 +1914,8 @@ out:
}
static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data,
- const void *buf, size_t len, CURLcode *err)
+ const void *buf, size_t len, bool eos,
+ CURLcode *err)
{
struct cf_osslq_ctx *ctx = cf->ctx;
struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
@@ -1907,6 +1923,7 @@ static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data,
ssize_t nwritten;
CURLcode result;
+ (void)eos; /* TODO: use to end stream */
CF_DATA_SAVE(save, cf, data);
DEBUGASSERT(cf->connected);
DEBUGASSERT(ctx->tls.ossl.ssl);
@@ -1942,13 +1959,13 @@ static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data,
* body. This happens on 30x or 40x responses.
* We silently discard the data sent, since this is not a transport
* error situation. */
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] discarding data"
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] discarding data"
"on closed stream with response", stream->s.id);
*err = CURLE_OK;
nwritten = (ssize_t)len;
goto out;
}
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] send_body(len=%zu) "
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] send_body(len=%zu) "
"-> stream closed", stream->s.id, len);
*err = CURLE_HTTP3;
nwritten = -1;
@@ -1956,7 +1973,7 @@ static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data,
}
else {
nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err);
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send, add to "
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send, add to "
"sendbuf(len=%zu) -> %zd, %d",
stream->s.id, len, nwritten, *err);
if(nwritten < 0) {
@@ -1974,7 +1991,7 @@ static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data,
out:
result = check_and_set_expiry(cf, data);
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send(len=%zu) -> %zd, %d",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send(len=%zu) -> %zd, %d",
stream? stream->s.id : -1, len, nwritten, *err);
CF_DATA_RESTORE(cf, save);
return nwritten;
@@ -1990,14 +2007,14 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
(void)cf;
if(stream->reset) {
failf(data,
- "HTTP/3 stream %" CURL_PRId64 " reset by server",
+ "HTTP/3 stream %" FMT_PRId64 " reset by server",
stream->s.id);
*err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP3;
goto out;
}
else if(!stream->resp_hds_complete) {
failf(data,
- "HTTP/3 stream %" CURL_PRId64
+ "HTTP/3 stream %" FMT_PRId64
" was closed cleanly, but before getting"
" all response header fields, treated as error",
stream->s.id);
@@ -2037,7 +2054,7 @@ static ssize_t cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
nread = Curl_bufq_read(&stream->recvbuf,
(unsigned char *)buf, len, err);
if(nread < 0) {
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read recvbuf(len=%zu) "
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read recvbuf(len=%zu) "
"-> %zd, %d", stream->s.id, len, nread, *err);
goto out;
}
@@ -2055,7 +2072,7 @@ static ssize_t cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
nread = Curl_bufq_read(&stream->recvbuf,
(unsigned char *)buf, len, err);
if(nread < 0) {
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] read recvbuf(len=%zu) "
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read recvbuf(len=%zu) "
"-> %zd, %d", stream->s.id, len, nread, *err);
goto out;
}
@@ -2085,7 +2102,7 @@ out:
nread = -1;
}
}
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_recv(len=%zu) -> %zd, %d",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_recv(len=%zu) -> %zd, %d",
stream? stream->s.id : -1, len, nread, *err);
CF_DATA_RESTORE(cf, save);
return nread;
@@ -2325,7 +2342,7 @@ CURLcode Curl_cf_osslq_create(struct Curl_cfilter **pcf,
result = CURLE_OUT_OF_MEMORY;
goto out;
}
- cf_osslq_ctx_clear(ctx);
+ cf_osslq_ctx_init(ctx);
result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
if(result)
@@ -2346,7 +2363,7 @@ out:
if(udp_cf)
Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE);
Curl_safefree(cf);
- Curl_safefree(ctx);
+ cf_osslq_ctx_free(ctx);
}
return result;
}
diff --git a/libs/libcurl/src/vquic/curl_quiche.c b/libs/libcurl/src/vquic/curl_quiche.c
index 66e3592bb2..61b97e2119 100644
--- a/libs/libcurl/src/vquic/curl_quiche.c
+++ b/libs/libcurl/src/vquic/curl_quiche.c
@@ -98,14 +98,17 @@ struct cf_quiche_ctx {
struct curltime handshake_at; /* time connect handshake finished */
struct curltime reconnect_at; /* time the next attempt should start */
struct bufc_pool stream_bufcp; /* chunk pool for streams */
- struct Curl_hash streams; /* hash `data->id` to `stream_ctx` */
+ struct Curl_hash streams; /* hash `data->mid` to `stream_ctx` */
curl_off_t data_recvd;
+ BIT(initialized);
BIT(goaway); /* got GOAWAY from server */
BIT(x509_store_setup); /* if x509 store has been set up */
BIT(shutdown_started); /* queued shutdown packets */
};
#ifdef DEBUG_QUICHE
+/* initialize debug log callback only once */
+static int debug_log_init = 0;
static void quiche_debug_log(const char *line, void *argp)
{
(void)argp;
@@ -113,17 +116,27 @@ static void quiche_debug_log(const char *line, void *argp)
}
#endif
-static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx)
+static void h3_stream_hash_free(void *stream);
+
+static void cf_quiche_ctx_init(struct cf_quiche_ctx *ctx)
{
- if(ctx) {
- if(ctx->h3c)
- quiche_h3_conn_free(ctx->h3c);
- if(ctx->h3config)
- quiche_h3_config_free(ctx->h3config);
- if(ctx->qconn)
- quiche_conn_free(ctx->qconn);
- if(ctx->cfg)
- quiche_config_free(ctx->cfg);
+ DEBUGASSERT(!ctx->initialized);
+#ifdef DEBUG_QUICHE
+ if(!debug_log_init) {
+ quiche_enable_debug_logging(quiche_debug_log, NULL);
+ debug_log_init = 1;
+ }
+#endif
+ Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
+ H3_STREAM_POOL_SPARES);
+ Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
+ ctx->data_recvd = 0;
+ ctx->initialized = TRUE;
+}
+
+static void cf_quiche_ctx_free(struct cf_quiche_ctx *ctx)
+{
+ if(ctx && ctx->initialized) {
/* quiche just freed it */
ctx->tls.ossl.ssl = NULL;
Curl_vquic_tls_cleanup(&ctx->tls);
@@ -132,9 +145,20 @@ static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx)
Curl_bufcp_free(&ctx->stream_bufcp);
Curl_hash_clean(&ctx->streams);
Curl_hash_destroy(&ctx->streams);
-
- memset(ctx, 0, sizeof(*ctx));
}
+ free(ctx);
+}
+
+static void cf_quiche_ctx_close(struct cf_quiche_ctx *ctx)
+{
+ if(ctx->h3c)
+ quiche_h3_conn_free(ctx->h3c);
+ if(ctx->h3config)
+ quiche_h3_config_free(ctx->h3config);
+ if(ctx->qconn)
+ quiche_conn_free(ctx->qconn);
+ if(ctx->cfg)
+ quiche_config_free(ctx->cfg);
}
static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
@@ -148,7 +172,6 @@ struct stream_ctx {
struct bufq recvbuf; /* h3 response */
struct h1_req_parser h1; /* h1 request parsing */
curl_uint64_t error3; /* HTTP/3 stream error code */
- curl_off_t upload_left; /* number of request bytes left to upload */
BIT(opened); /* TRUE after stream has been opened */
BIT(closed); /* TRUE on stream close */
BIT(reset); /* TRUE on stream reset */
@@ -159,7 +182,7 @@ struct stream_ctx {
};
#define H3_STREAM_CTX(ctx,data) ((struct stream_ctx *)(\
- data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL))
+ data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
static void h3_stream_ctx_free(struct stream_ctx *stream)
{
@@ -178,17 +201,17 @@ static void check_resumes(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct Curl_easy *sdata;
- struct stream_ctx *stream;
+ struct Curl_llist_node *e;
DEBUGASSERT(data->multi);
- for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
+ for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
+ struct Curl_easy *sdata = Curl_node_elem(e);
if(sdata->conn == data->conn) {
- stream = H3_STREAM_CTX(ctx, sdata);
+ struct stream_ctx *stream = H3_STREAM_CTX(ctx, sdata);
if(stream && stream->quic_flow_blocked) {
stream->quic_flow_blocked = FALSE;
Curl_expire(data, 0, EXPIRE_RUN_NOW);
- CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] unblock", stream->id);
+ CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] unblock", stream->id);
}
}
}
@@ -212,7 +235,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
- if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) {
+ if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
h3_stream_ctx_free(stream);
return CURLE_OUT_OF_MEMORY;
}
@@ -228,7 +251,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
(void)cf;
if(stream) {
- CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] easy handle is done", stream->id);
+ CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] easy handle is done", stream->id);
if(ctx->qconn && !stream->closed) {
quiche_conn_stream_shutdown(ctx->qconn, stream->id,
QUICHE_SHUTDOWN_READ, CURL_H3_NO_ERROR);
@@ -242,7 +265,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
if(result)
CURL_TRC_CF(data, cf, "data_done, flush egress -> %d", result);
}
- Curl_hash_offt_remove(&ctx->streams, data->id);
+ Curl_hash_offt_remove(&ctx->streams, data->mid);
}
}
@@ -255,7 +278,7 @@ static void drain_stream(struct Curl_cfilter *cf,
(void)cf;
bits = CURL_CSELECT_IN;
- if(stream && !stream->send_closed && stream->upload_left)
+ if(stream && !stream->send_closed)
bits |= CURL_CSELECT_OUT;
if(data->state.select_bits != bits) {
data->state.select_bits = bits;
@@ -269,7 +292,6 @@ static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf,
struct stream_ctx **pstream)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct Curl_easy *sdata;
struct stream_ctx *stream;
(void)cf;
@@ -279,8 +301,10 @@ static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf,
return data;
}
else {
+ struct Curl_llist_node *e;
DEBUGASSERT(data->multi);
- for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
+ for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
+ struct Curl_easy *sdata = Curl_node_elem(e);
if(sdata->conn != data->conn)
continue;
stream = H3_STREAM_CTX(ctx, sdata);
@@ -297,11 +321,12 @@ static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf,
static void cf_quiche_expire_conn_closed(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
- struct Curl_easy *sdata;
+ struct Curl_llist_node *e;
DEBUGASSERT(data->multi);
CURL_TRC_CF(data, cf, "conn closed, expire all transfers");
- for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
+ for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
+ struct Curl_easy *sdata = Curl_node_elem(e);
if(sdata == data || sdata->conn != data->conn)
continue;
CURL_TRC_CF(sdata, cf, "conn closed, expire transfer");
@@ -357,7 +382,7 @@ static int cb_each_header(uint8_t *name, size_t name_len,
return CURLE_OK;
if((name_len == 7) && !strncmp(HTTP_PSEUDO_STATUS, (char *)name, 7)) {
- CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRIu64 "] status: %.*s",
+ CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRIu64 "] status: %.*s",
stream->id, (int)value_len, value);
result = write_resp_raw(x->cf, x->data, "HTTP/3 ", sizeof("HTTP/3 ") - 1);
if(!result)
@@ -366,7 +391,7 @@ static int cb_each_header(uint8_t *name, size_t name_len,
result = write_resp_raw(x->cf, x->data, " \r\n", 3);
}
else {
- CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRIu64 "] header: %.*s: %.*s",
+ CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRIu64 "] header: %.*s: %.*s",
stream->id, (int)name_len, name,
(int)value_len, value);
result = write_resp_raw(x->cf, x->data, name, name_len);
@@ -378,7 +403,7 @@ static int cb_each_header(uint8_t *name, size_t name_len,
result = write_resp_raw(x->cf, x->data, "\r\n", 2);
}
if(result) {
- CURL_TRC_CF(x->data, x->cf, "[%"CURL_PRIu64"] on header error %d",
+ CURL_TRC_CF(x->data, x->cf, "[%"FMT_PRIu64"] on header error %d",
stream->id, result);
}
return result;
@@ -435,9 +460,9 @@ static CURLcode cf_recv_body(struct Curl_cfilter *cf,
stream_resp_read, &cb_ctx, &result);
if(nwritten < 0 && result != CURLE_AGAIN) {
- CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] recv_body error %zd",
+ CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] recv_body error %zd",
stream->id, nwritten);
- failf(data, "Error %d in HTTP/3 response body for stream[%"CURL_PRIu64"]",
+ failf(data, "Error %d in HTTP/3 response body for stream[%"FMT_PRIu64"]",
result, stream->id);
stream->closed = TRUE;
stream->reset = TRUE;
@@ -489,10 +514,10 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf,
rc = quiche_h3_event_for_each_header(ev, cb_each_header, &cb_ctx);
if(rc) {
failf(data, "Error %d in HTTP/3 response header for stream[%"
- CURL_PRIu64"]", rc, stream->id);
+ FMT_PRIu64"]", rc, stream->id);
return CURLE_RECV_ERROR;
}
- CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] <- [HEADERS]", stream->id);
+ CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] <- [HEADERS]", stream->id);
break;
case QUICHE_H3_EVENT_DATA:
@@ -502,7 +527,7 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf,
break;
case QUICHE_H3_EVENT_RESET:
- CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] RESET", stream->id);
+ CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] RESET", stream->id);
stream->closed = TRUE;
stream->reset = TRUE;
stream->send_closed = TRUE;
@@ -510,7 +535,7 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf,
break;
case QUICHE_H3_EVENT_FINISHED:
- CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] CLOSED", stream->id);
+ CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] CLOSED", stream->id);
if(!stream->resp_hds_complete) {
result = write_resp_raw(cf, data, "\r\n", 2);
if(result)
@@ -522,11 +547,11 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf,
break;
case QUICHE_H3_EVENT_GOAWAY:
- CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] <- [GOAWAY]", stream->id);
+ CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] <- [GOAWAY]", stream->id);
break;
default:
- CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] recv, unhandled event %d",
+ CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] recv, unhandled event %d",
stream->id, quiche_h3_event_type(ev));
break;
}
@@ -549,13 +574,13 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf,
break;
}
else if(stream3_id < 0) {
- CURL_TRC_CF(data, cf, "error poll: %"CURL_PRId64, stream3_id);
+ CURL_TRC_CF(data, cf, "error poll: %"FMT_PRId64, stream3_id);
return CURLE_HTTP3;
}
sdata = get_stream_easy(cf, data, stream3_id, &stream);
if(!sdata || !stream) {
- CURL_TRC_CF(data, cf, "discard event %s for unknown [%"CURL_PRId64"]",
+ CURL_TRC_CF(data, cf, "discard event %s for unknown [%"FMT_PRId64"]",
cf_ev_name(ev), stream3_id);
}
else {
@@ -563,7 +588,7 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf,
drain_stream(cf, sdata);
if(result) {
CURL_TRC_CF(data, cf, "error processing event %s "
- "for [%"CURL_PRIu64"] -> %d", cf_ev_name(ev),
+ "for [%"FMT_PRIu64"] -> %d", cf_ev_name(ev),
stream3_id, result);
if(data == sdata) {
/* Only report this error to the caller if it is about the
@@ -793,19 +818,19 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
DEBUGASSERT(stream);
if(stream->reset) {
failf(data,
- "HTTP/3 stream %" CURL_PRIu64 " reset by server", stream->id);
+ "HTTP/3 stream %" FMT_PRIu64 " reset by server", stream->id);
*err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP3;
- CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] cf_recv, was reset -> %d",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] cf_recv, was reset -> %d",
stream->id, *err);
}
else if(!stream->resp_got_header) {
failf(data,
- "HTTP/3 stream %" CURL_PRIu64 " was closed cleanly, but before "
+ "HTTP/3 stream %" FMT_PRIu64 " was closed cleanly, but before "
"getting all response header fields, treated as error",
stream->id);
/* *err = CURLE_PARTIAL_FILE; */
*err = CURLE_HTTP3;
- CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] cf_recv, closed incomplete"
+ CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] cf_recv, closed incomplete"
" -> %d", stream->id, *err);
}
else {
@@ -833,7 +858,7 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
if(!Curl_bufq_is_empty(&stream->recvbuf)) {
nread = Curl_bufq_read(&stream->recvbuf,
(unsigned char *)buf, len, err);
- CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] read recvbuf(len=%zu) "
+ CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] read recvbuf(len=%zu) "
"-> %zd, %d", stream->id, len, nread, *err);
if(nread < 0)
goto out;
@@ -850,7 +875,7 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) {
nread = Curl_bufq_read(&stream->recvbuf,
(unsigned char *)buf, len, err);
- CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] read recvbuf(len=%zu) "
+ CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] read recvbuf(len=%zu) "
"-> %zd, %d", stream->id, len, nread, *err);
if(nread < 0)
goto out;
@@ -884,19 +909,70 @@ out:
}
if(nread > 0)
ctx->data_recvd += nread;
- CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] cf_recv(total=%"
- CURL_FORMAT_CURL_OFF_T ") -> %zd, %d",
+ CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] cf_recv(total=%"
+ FMT_OFF_T ") -> %zd, %d",
stream->id, ctx->data_recvd, nread, *err);
return nread;
}
+static ssize_t cf_quiche_send_body(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct stream_ctx *stream,
+ const void *buf, size_t len, bool eos,
+ CURLcode *err)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ ssize_t nwritten;
+
+ nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id,
+ (uint8_t *)buf, len, eos);
+ if(nwritten == QUICHE_H3_ERR_DONE || (nwritten == 0 && len > 0)) {
+ /* TODO: we seem to be blocked on flow control and should HOLD
+ * sending. But when do we open again? */
+ if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) {
+ CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) "
+ "-> window exhausted", stream->id, len);
+ stream->quic_flow_blocked = TRUE;
+ }
+ *err = CURLE_AGAIN;
+ return -1;
+ }
+ else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE) {
+ CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) "
+ "-> invalid stream state", stream->id, len);
+ *err = CURLE_HTTP3;
+ return -1;
+ }
+ else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) {
+ CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) "
+ "-> exceeds size", stream->id, len);
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+ else if(nwritten < 0) {
+ CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) "
+ "-> quiche err %zd", stream->id, len, nwritten);
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+ else {
+ if(eos && (len == (size_t)nwritten))
+ stream->send_closed = TRUE;
+ CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send body(len=%zu, "
+ "eos=%d) -> %zd",
+ stream->id, len, stream->send_closed, nwritten);
+ *err = CURLE_OK;
+ return nwritten;
+ }
+}
+
/* Index where :authority header field will appear in request header
field list. */
#define AUTHORITY_DST_IDX 3
static ssize_t h3_open_stream(struct Curl_cfilter *cf,
struct Curl_easy *data,
- const void *buf, size_t len,
+ const char *buf, size_t len, bool eos,
CURLcode *err)
{
struct cf_quiche_ctx *ctx = cf->ctx;
@@ -952,23 +1028,7 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf,
nva[i].value_len = e->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 */
- break;
- default:
- stream->upload_left = 0; /* no request body */
- break;
- }
-
- if(stream->upload_left == 0)
+ if(eos && ((size_t)nwritten == len))
stream->send_closed = TRUE;
stream3_id = quiche_h3_send_request(ctx->h3c, ctx->qconn, nva, nheader,
@@ -977,14 +1037,14 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf,
if(QUICHE_H3_ERR_STREAM_BLOCKED == stream3_id) {
/* quiche seems to report this error if the connection window is
* exhausted. Which happens frequently and intermittent. */
- CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] blocked", stream->id);
+ CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] blocked", stream->id);
stream->quic_flow_blocked = TRUE;
*err = CURLE_AGAIN;
nwritten = -1;
goto out;
}
else {
- CURL_TRC_CF(data, cf, "send_request(%s) -> %" CURL_PRIu64,
+ CURL_TRC_CF(data, cf, "send_request(%s) -> %" FMT_PRIu64,
data->state.url, stream3_id);
}
*err = CURLE_SEND_ERROR;
@@ -1000,15 +1060,31 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf,
stream->reset = FALSE;
if(Curl_trc_is_verbose(data)) {
- infof(data, "[HTTP/3] [%" CURL_PRIu64 "] OPENED stream for %s",
+ infof(data, "[HTTP/3] [%" FMT_PRIu64 "] OPENED stream for %s",
stream->id, data->state.url);
for(i = 0; i < nheader; ++i) {
- infof(data, "[HTTP/3] [%" CURL_PRIu64 "] [%.*s: %.*s]", stream->id,
+ infof(data, "[HTTP/3] [%" FMT_PRIu64 "] [%.*s: %.*s]", stream->id,
(int)nva[i].name_len, nva[i].name,
(int)nva[i].value_len, nva[i].value);
}
}
+ if(nwritten > 0 && ((size_t)nwritten < len)) {
+ /* after the headers, there was request BODY data */
+ size_t hds_len = (size_t)nwritten;
+ ssize_t bwritten;
+
+ bwritten = cf_quiche_send_body(cf, data, stream,
+ buf + hds_len, len - hds_len, eos, err);
+ if((bwritten < 0) && (CURLE_AGAIN != *err)) {
+ /* real error, fail */
+ nwritten = -1;
+ }
+ else if(bwritten > 0) {
+ nwritten += bwritten;
+ }
+ }
+
out:
free(nva);
Curl_dynhds_free(&h2_headers);
@@ -1016,7 +1092,8 @@ out:
}
static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data,
- const void *buf, size_t len, CURLcode *err)
+ const void *buf, size_t len, bool eos,
+ CURLcode *err)
{
struct cf_quiche_ctx *ctx = cf->ctx;
struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
@@ -1032,7 +1109,7 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data,
}
if(!stream || !stream->opened) {
- nwritten = h3_open_stream(cf, data, buf, len, err);
+ nwritten = h3_open_stream(cf, data, buf, len, eos, err);
if(nwritten < 0)
goto out;
stream = H3_STREAM_CTX(ctx, data);
@@ -1047,70 +1124,20 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data,
* sending the 30x response.
* This is sort of a race: had the transfer loop called recv first,
* it would see the response and stop/discard sending on its own- */
- CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] discarding data"
+ CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] discarding data"
"on closed stream with response", stream->id);
*err = CURLE_OK;
nwritten = (ssize_t)len;
goto out;
}
- CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) "
+ CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) "
"-> stream closed", stream->id, len);
*err = CURLE_HTTP3;
nwritten = -1;
goto out;
}
else {
- bool eof = (stream->upload_left >= 0 &&
- (curl_off_t)len >= stream->upload_left);
- nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id,
- (uint8_t *)buf, len, eof);
- if(nwritten == QUICHE_H3_ERR_DONE || (nwritten == 0 && len > 0)) {
- /* TODO: we seem to be blocked on flow control and should HOLD
- * sending. But when do we open again? */
- if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) {
- CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) "
- "-> window exhausted", stream->id, len);
- stream->quic_flow_blocked = TRUE;
- }
- *err = CURLE_AGAIN;
- nwritten = -1;
- goto out;
- }
- else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE) {
- CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) "
- "-> invalid stream state", stream->id, len);
- *err = CURLE_HTTP3;
- nwritten = -1;
- goto out;
- }
- else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) {
- CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) "
- "-> exceeds size", stream->id, len);
- *err = CURLE_SEND_ERROR;
- nwritten = -1;
- goto out;
- }
- else if(nwritten < 0) {
- CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) "
- "-> quiche err %zd", stream->id, len, nwritten);
- *err = CURLE_SEND_ERROR;
- nwritten = -1;
- goto out;
- }
- else {
- /* quiche accepted all or at least a part of the buf */
- if(stream->upload_left > 0) {
- stream->upload_left = (nwritten < stream->upload_left)?
- (stream->upload_left - nwritten) : 0;
- }
- if(stream->upload_left == 0)
- stream->send_closed = TRUE;
-
- CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send body(len=%zu, "
- "left=%" CURL_FORMAT_CURL_OFF_T ") -> %zd",
- stream->id, len, stream->upload_left, nwritten);
- *err = CURLE_OK;
- }
+ nwritten = cf_quiche_send_body(cf, data, stream, buf, len, eos, err);
}
out:
@@ -1119,8 +1146,8 @@ out:
*err = result;
nwritten = -1;
}
- CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] cf_send(len=%zu) -> %zd, %d",
- stream? stream->id : (uint64_t)~0, len, nwritten, *err);
+ CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] cf_send(len=%zu) -> %zd, %d",
+ stream? stream->id : (curl_uint64_t)~0, len, nwritten, *err);
return nwritten;
}
@@ -1215,10 +1242,9 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf,
ssize_t sent;
stream->send_closed = TRUE;
- stream->upload_left = 0;
body[0] = 'X';
- sent = cf_quiche_send(cf, data, body, 0, &result);
- CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] DONE_SEND -> %zd, %d",
+ sent = cf_quiche_send(cf, data, body, 0, TRUE, &result);
+ CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] DONE_SEND -> %zd, %d",
stream->id, sent, result);
}
break;
@@ -1238,8 +1264,8 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf,
return result;
}
-static CURLcode cf_connect_start(struct Curl_cfilter *cf,
- struct Curl_easy *data)
+static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
int rv;
@@ -1247,19 +1273,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
const struct Curl_sockaddr_ex *sockaddr;
DEBUGASSERT(ctx->q.sockfd != CURL_SOCKET_BAD);
-
-#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
- Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
- H3_STREAM_POOL_SPARES);
- Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
- ctx->data_recvd = 0;
+ DEBUGASSERT(ctx->initialized);
result = vquic_ctx_init(&ctx->q);
if(result)
@@ -1366,7 +1380,6 @@ static CURLcode cf_quiche_verify_peer(struct Curl_cfilter *cf,
cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
cf->conn->httpversion = 30;
- cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
}
@@ -1401,7 +1414,7 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
}
if(!ctx->qconn) {
- result = cf_connect_start(cf, data);
+ result = cf_quiche_ctx_open(cf, data);
if(result)
goto out;
ctx->started_at = ctx->q.last_op;
@@ -1513,23 +1526,20 @@ out:
static void cf_quiche_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- struct cf_quiche_ctx *ctx = cf->ctx;
-
- if(ctx) {
+ if(cf->ctx) {
bool done;
(void)cf_quiche_shutdown(cf, data, &done);
- cf_quiche_ctx_clear(ctx);
+ cf_quiche_ctx_close(cf->ctx);
}
}
static void cf_quiche_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- struct cf_quiche_ctx *ctx = cf->ctx;
-
(void)data;
- cf_quiche_ctx_clear(ctx);
- free(ctx);
- cf->ctx = NULL;
+ if(cf->ctx) {
+ cf_quiche_ctx_free(cf->ctx);
+ cf->ctx = NULL;
+ }
}
static CURLcode cf_quiche_query(struct Curl_cfilter *cf,
@@ -1545,7 +1555,7 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf,
max_streams += quiche_conn_peer_streams_left_bidi(ctx->qconn);
}
*pres1 = (max_streams > INT_MAX)? INT_MAX : (int)max_streams;
- CURL_TRC_CF(data, cf, "query conn[%" CURL_FORMAT_CURL_OFF_T "]: "
+ CURL_TRC_CF(data, cf, "query conn[%" FMT_OFF_T "]: "
"MAX_CONCURRENT -> %d (%zu in use)",
cf->conn->connection_id, *pres1, CONN_INUSE(cf->conn));
return CURLE_OK;
@@ -1650,6 +1660,7 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf,
result = CURLE_OUT_OF_MEMORY;
goto out;
}
+ cf_quiche_ctx_init(ctx);
result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
if(result)
@@ -1669,7 +1680,7 @@ out:
if(udp_cf)
Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE);
Curl_safefree(cf);
- Curl_safefree(ctx);
+ cf_quiche_ctx_free(ctx);
}
return result;
diff --git a/libs/libcurl/src/vquic/vquic-tls.c b/libs/libcurl/src/vquic/vquic-tls.c
index d3ddce2c53..f51ec0cc22 100644
--- a/libs/libcurl/src/vquic/vquic-tls.c
+++ b/libs/libcurl/src/vquic/vquic-tls.c
@@ -162,7 +162,7 @@ static CURLcode Curl_wssl_init_ctx(struct curl_tls_ctx *ctx,
#ifdef CURL_CA_FALLBACK
else {
/* verifying the peer without any CA certificates will not work so
- use wolfssl's built-in default as fallback */
+ use wolfSSL's built-in default as fallback */
wolfSSL_CTX_set_default_verify_paths(ctx->wssl.ctx);
}
#endif
diff --git a/libs/libcurl/src/vquic/vquic.c b/libs/libcurl/src/vquic/vquic.c
index 13d9929ee2..f68a8d2912 100644
--- a/libs/libcurl/src/vquic/vquic.c
+++ b/libs/libcurl/src/vquic/vquic.c
@@ -22,7 +22,7 @@
*
***************************************************************************/
-/* WIP, experimental: use recvmmsg() on linux
+/* WIP, experimental: use recvmmsg() on Linux
* we have no configure check, yet
* and also it is only available for _GNU_SOURCE, which
* we do not use otherwise.
@@ -720,7 +720,7 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data,
const struct connectdata *conn)
{
if(conn->transport == TRNSPRT_UNIX) {
- /* cannot do QUIC over a unix domain socket */
+ /* cannot do QUIC over a Unix domain socket */
return CURLE_QUIC_CONNECT_ERROR;
}
if(!(conn->handler->flags & PROTOPT_SSL)) {
diff --git a/libs/libcurl/src/vssh/libssh.c b/libs/libcurl/src/vssh/libssh.c
index 8cebb44ac1..b41d66ab9e 100644
--- a/libs/libcurl/src/vssh/libssh.c
+++ b/libs/libcurl/src/vssh/libssh.c
@@ -31,11 +31,6 @@
#include <limits.h>
-/* in 0.10.0 or later, ignore deprecated warnings */
-#define SSH_SUPPRESS_DEPRECATED
-#include <libssh/libssh.h>
-#include <libssh/sftp.h>
-
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
@@ -1241,7 +1236,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
if(attrs) {
curl_off_t size = attrs->size;
if(size < 0) {
- failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
+ failf(data, "Bad file size (%" FMT_OFF_T ")", size);
MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
break;
}
@@ -1626,7 +1621,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
sftp_attributes_free(attrs);
if(size < 0) {
- failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
+ failf(data, "Bad file size (%" FMT_OFF_T ")", size);
return CURLE_BAD_DOWNLOAD_RESUME;
}
if(data->state.use_range) {
@@ -1656,9 +1651,8 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
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);
+ failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%"
+ FMT_OFF_T ")", from, size);
return CURLE_BAD_DOWNLOAD_RESUME;
}
if(from > to) {
@@ -1687,10 +1681,8 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
if(data->state.resume_from < 0) {
/* We are 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);
+ failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%"
+ FMT_OFF_T ")", data->state.resume_from, size);
return CURLE_BAD_DOWNLOAD_RESUME;
}
/* download from where? */
@@ -1698,8 +1690,8 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
}
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 ")",
+ failf(data, "Offset (%" FMT_OFF_T
+ ") was beyond file size (%" FMT_OFF_T ")",
data->state.resume_from, size);
return CURLE_BAD_DOWNLOAD_RESUME;
}
@@ -2404,12 +2396,13 @@ static CURLcode scp_done(struct Curl_easy *data, CURLcode status,
}
static ssize_t scp_send(struct Curl_easy *data, int sockindex,
- const void *mem, size_t len, CURLcode *err)
+ const void *mem, size_t len, bool eos, CURLcode *err)
{
int rc;
struct connectdata *conn = data->conn;
(void) sockindex; /* we only support SCP on the fixed known primary socket */
(void) err;
+ (void)eos;
rc = ssh_scp_write(conn->proto.sshc.scp_session, mem, len);
@@ -2552,11 +2545,13 @@ static CURLcode sftp_done(struct Curl_easy *data, CURLcode status,
/* return number of sent bytes */
static ssize_t sftp_send(struct Curl_easy *data, int sockindex,
- const void *mem, size_t len, CURLcode *err)
+ const void *mem, size_t len, bool eos,
+ CURLcode *err)
{
ssize_t nwrite;
struct connectdata *conn = data->conn;
(void)sockindex;
+ (void)eos;
/* limit the writes to the maximum specified in Section 3 of
* https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02
diff --git a/libs/libcurl/src/vssh/libssh2.c b/libs/libcurl/src/vssh/libssh2.c
index 88d5469272..b752eb5aaa 100644
--- a/libs/libcurl/src/vssh/libssh2.c
+++ b/libs/libcurl/src/vssh/libssh2.c
@@ -30,9 +30,6 @@
#include <limits.h>
-#include <libssh2.h>
-#include <libssh2_sftp.h>
-
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
@@ -1786,7 +1783,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
}
#if SIZEOF_TIME_T > SIZEOF_LONG
if(date > 0xffffffff) {
- /* if 'long' cannot old >32bit, this date cannot be sent */
+ /* if 'long' cannot old >32-bit, this date cannot be sent */
failf(data, "date overflow");
fail = TRUE;
}
@@ -2069,7 +2066,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
else {
curl_off_t size = attrs.filesize;
if(size < 0) {
- failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
+ failf(data, "Bad file size (%" FMT_OFF_T ")", size);
return CURLE_BAD_DOWNLOAD_RESUME;
}
data->state.resume_from = attrs.filesize;
@@ -2510,7 +2507,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
curl_off_t size = attrs.filesize;
if(size < 0) {
- failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
+ failf(data, "Bad file size (%" FMT_OFF_T ")", size);
return CURLE_BAD_DOWNLOAD_RESUME;
}
if(data->state.use_range) {
@@ -2538,10 +2535,8 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
to = size - 1;
}
if(from > size) {
- failf(data, "Offset (%"
- CURL_FORMAT_CURL_OFF_T ") was beyond file size (%"
- CURL_FORMAT_CURL_OFF_T ")", from,
- (curl_off_t)attrs.filesize);
+ failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%"
+ FMT_OFF_T ")", from, (curl_off_t)attrs.filesize);
return CURLE_BAD_DOWNLOAD_RESUME;
}
if(from > to) {
@@ -2566,9 +2561,8 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
if(data->state.resume_from < 0) {
/* We are 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 ")",
+ failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%"
+ FMT_OFF_T ")",
data->state.resume_from, (curl_off_t)attrs.filesize);
return CURLE_BAD_DOWNLOAD_RESUME;
}
@@ -2577,8 +2571,8 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
}
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 ")",
+ failf(data, "Offset (%" FMT_OFF_T
+ ") was beyond file size (%" FMT_OFF_T ")",
data->state.resume_from, (curl_off_t)attrs.filesize);
return CURLE_BAD_DOWNLOAD_RESUME;
}
@@ -3229,7 +3223,7 @@ static ssize_t ssh_tls_send(libssh2_socket_t sock, const void *buffer,
/* 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_conn_send(data, socknum, buffer, length, &nwrite);
+ result = Curl_conn_send(data, socknum, buffer, length, FALSE, &nwrite);
conn->send[0] = backup;
if(result == CURLE_AGAIN)
return -EAGAIN; /* magic return code for libssh2 */
@@ -3540,12 +3534,13 @@ static CURLcode scp_done(struct Curl_easy *data, CURLcode status,
}
static ssize_t scp_send(struct Curl_easy *data, int sockindex,
- const void *mem, size_t len, CURLcode *err)
+ const void *mem, size_t len, bool eos, 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 */
+ (void)eos;
/* libssh2_channel_write() returns int! */
nwrite = (ssize_t) libssh2_channel_write(sshc->ssh_channel, mem, len);
@@ -3678,12 +3673,13 @@ static CURLcode sftp_done(struct Curl_easy *data, CURLcode status,
/* return number of sent bytes */
static ssize_t sftp_send(struct Curl_easy *data, int sockindex,
- const void *mem, size_t len, CURLcode *err)
+ const void *mem, size_t len, bool eos, CURLcode *err)
{
ssize_t nwrite;
struct connectdata *conn = data->conn;
struct ssh_conn *sshc = &conn->proto.sshc;
(void)sockindex;
+ (void)eos;
nwrite = libssh2_sftp_write(sshc->sftp_handle, mem, len);
diff --git a/libs/libcurl/src/vssh/ssh.h b/libs/libcurl/src/vssh/ssh.h
index 04ade52823..272e47ce6c 100644
--- a/libs/libcurl/src/vssh/ssh.h
+++ b/libs/libcurl/src/vssh/ssh.h
@@ -30,6 +30,8 @@
#include <libssh2.h>
#include <libssh2_sftp.h>
#elif defined(USE_LIBSSH)
+/* in 0.10.0 or later, ignore deprecated warnings */
+#define SSH_SUPPRESS_DEPRECATED
#include <libssh/libssh.h>
#include <libssh/sftp.h>
#elif defined(USE_WOLFSSH)
diff --git a/libs/libcurl/src/vssh/wolfssh.c b/libs/libcurl/src/vssh/wolfssh.c
index bf1f16463c..d53e2bc130 100644
--- a/libs/libcurl/src/vssh/wolfssh.c
+++ b/libs/libcurl/src/vssh/wolfssh.c
@@ -28,8 +28,6 @@
#include <limits.h>
-#include <wolfssh/ssh.h>
-#include <wolfssh/wolfsftp.h>
#include "urldata.h"
#include "cfilters.h"
#include "connect.h"
@@ -220,13 +218,15 @@ static void state(struct Curl_easy *data, sshstate nowstate)
}
static ssize_t wscp_send(struct Curl_easy *data, int sockindex,
- const void *mem, size_t len, CURLcode *err)
+ const void *mem, size_t len, bool eos,
+ 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)eos;
(void)err;
return nwrite;
@@ -247,13 +247,14 @@ static ssize_t wscp_recv(struct Curl_easy *data, int sockindex,
/* return number of sent bytes */
static ssize_t wsftp_send(struct Curl_easy *data, int sockindex,
- const void *mem, size_t len, CURLcode *err)
+ const void *mem, size_t len, bool eos, CURLcode *err)
{
struct connectdata *conn = data->conn;
struct ssh_conn *sshc = &conn->proto.sshc;
word32 offset[2];
int rc;
(void)sockindex;
+ (void)eos;
offset[0] = (word32)sshc->offset&0xFFFFFFFF;
offset[1] = (word32)(sshc->offset>>32)&0xFFFFFFFF;
@@ -280,7 +281,7 @@ static ssize_t wsftp_send(struct Curl_easy *data, int sockindex,
return -1;
}
DEBUGASSERT(rc == (int)len);
- infof(data, "sent %zu bytes SFTP from offset %" CURL_FORMAT_CURL_OFF_T,
+ infof(data, "sent %zu bytes SFTP from offset %" FMT_OFF_T,
len, sshc->offset);
sshc->offset += len;
return (ssize_t)rc;
@@ -577,7 +578,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
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);
+ failf(data, "Bad file size (%" FMT_OFF_T ")", size);
return CURLE_BAD_DOWNLOAD_RESUME;
}
data->state.resume_from = size;
@@ -768,7 +769,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
data->req.maxdownload = size;
Curl_pgrsSetDownloadSize(data, size);
- infof(data, "SFTP download %" CURL_FORMAT_CURL_OFF_T " bytes", size);
+ infof(data, "SFTP download %" FMT_OFF_T " bytes", size);
/* We cannot seek with wolfSSH so resuming and range requests are not
possible */
diff --git a/libs/libcurl/src/vtls/bearssl.c b/libs/libcurl/src/vtls/bearssl.c
index fdbb4f648a..f262639540 100644
--- a/libs/libcurl/src/vtls/bearssl.c
+++ b/libs/libcurl/src/vtls/bearssl.c
@@ -361,6 +361,56 @@ static const br_x509_class x509_vtable = {
x509_get_pkey
};
+static CURLcode
+bearssl_set_ssl_version_min_max(struct Curl_easy *data,
+ br_ssl_engine_context *ssl_eng,
+ struct ssl_primary_config *conn_config)
+{
+ unsigned version_min, version_max;
+
+ switch(conn_config->version) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+ case CURL_SSLVERSION_TLSv1_0:
+ version_min = BR_TLS10;
+ break;
+ case CURL_SSLVERSION_TLSv1_1:
+ version_min = BR_TLS11;
+ break;
+ case CURL_SSLVERSION_TLSv1_2:
+ version_min = BR_TLS12;
+ break;
+ case CURL_SSLVERSION_TLSv1_3:
+ failf(data, "BearSSL: does not support TLS 1.3");
+ return CURLE_SSL_CONNECT_ERROR;
+ default:
+ failf(data, "BearSSL: unsupported minimum TLS version value");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ switch(conn_config->version_max) {
+ case CURL_SSLVERSION_MAX_DEFAULT:
+ case CURL_SSLVERSION_MAX_NONE:
+ case CURL_SSLVERSION_MAX_TLSv1_3:
+ case CURL_SSLVERSION_MAX_TLSv1_2:
+ version_max = BR_TLS12;
+ break;
+ case CURL_SSLVERSION_MAX_TLSv1_1:
+ version_max = BR_TLS11;
+ break;
+ case CURL_SSLVERSION_MAX_TLSv1_0:
+ version_max = BR_TLS10;
+ break;
+ default:
+ failf(data, "BearSSL: unsupported maximum TLS version value");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ br_ssl_engine_set_versions(ssl_eng, version_min, version_max);
+
+ return CURLE_OK;
+}
+
static const uint16_t ciphertable[] = {
/* RFC 2246 TLS 1.0 */
BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x000A */
@@ -495,41 +545,11 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf,
const bool verifypeer = conn_config->verifypeer;
const bool verifyhost = conn_config->verifyhost;
CURLcode ret;
- unsigned version_min, version_max;
int session_set = 0;
DEBUGASSERT(backend);
CURL_TRC_CF(data, cf, "connect_step1");
- 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(verifypeer) {
if(ca_info_blob) {
struct cafile_source source;
@@ -564,7 +584,11 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf,
/* 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);
+
+ ret = bearssl_set_ssl_version_min_max(data, &backend->ctx.eng, conn_config);
+ if(ret != CURLE_OK)
+ return ret;
+
br_ssl_engine_set_buffer(&backend->ctx.eng, backend->buf,
sizeof(backend->buf), 1);
@@ -648,28 +672,6 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf,
return CURLE_OK;
}
-static void bearssl_adjust_pollset(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct easy_pollset *ps)
-{
- if(!cf->connected) {
- curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data);
- if(sock != CURL_SOCKET_BAD) {
- struct ssl_connect_data *connssl = cf->ctx;
- struct bearssl_ssl_backend_data *backend =
- (struct bearssl_ssl_backend_data *)connssl->backend;
- unsigned state = br_ssl_engine_current_state(&backend->ctx.eng);
-
- if(state & BR_SSL_SENDREC) {
- Curl_pollset_set_out_only(data, ps, sock);
- }
- else {
- Curl_pollset_set_in_only(data, ps, sock);
- }
- }
- }
-}
-
static CURLcode bearssl_run_until(struct Curl_cfilter *cf,
struct Curl_easy *data,
unsigned target)
@@ -686,6 +688,7 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf,
DEBUGASSERT(backend);
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
for(;;) {
state = br_ssl_engine_current_state(&backend->ctx.eng);
if(state & BR_SSL_CLOSED) {
@@ -710,7 +713,9 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf,
failf(data, "SSL: X.509 verification: "
"chain could not be linked to a trust anchor");
return CURLE_PEER_FAILED_VERIFICATION;
+ default:;
}
+ failf(data, "BearSSL: connection error 0x%04x", err);
/* X.509 errors are documented to have the range 32..63 */
if(err >= 32 && err < 64)
return CURLE_PEER_FAILED_VERIFICATION;
@@ -720,7 +725,8 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf,
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);
+ ret = Curl_conn_cf_send(cf->next, data, (char *)buf, len, FALSE,
+ &result);
CURL_TRC_CF(data, cf, "ssl_send(len=%zu) -> %zd, %d", len, ret, result);
if(ret <= 0) {
if(result == CURLE_AGAIN)
@@ -1089,8 +1095,10 @@ static CURLcode bearssl_shutdown(struct Curl_cfilter *cf,
if(result == CURLE_OK) {
*done = TRUE;
}
- else if(result == CURLE_AGAIN)
+ else if(result == CURLE_AGAIN) {
+ CURL_TRC_CF(data, cf, "shutdown EAGAIN, io_need=%x", connssl->io_need);
result = CURLE_OK;
+ }
else
CURL_TRC_CF(data, cf, "shutdown error: %d", result);
@@ -1131,7 +1139,12 @@ static CURLcode bearssl_sha256sum(const unsigned char *input,
const struct Curl_ssl Curl_ssl_bearssl = {
{ CURLSSLBACKEND_BEARSSL, "bearssl" }, /* info */
- SSLSUPP_CAINFO_BLOB | SSLSUPP_SSL_CTX | SSLSUPP_HTTPS_PROXY,
+
+ SSLSUPP_CAINFO_BLOB |
+ SSLSUPP_SSL_CTX |
+ SSLSUPP_HTTPS_PROXY |
+ SSLSUPP_CIPHER_LIST,
+
sizeof(struct bearssl_ssl_backend_data),
Curl_none_init, /* init */
@@ -1144,7 +1157,7 @@ const struct Curl_ssl Curl_ssl_bearssl = {
Curl_none_cert_status_request, /* cert_status_request */
bearssl_connect, /* connect */
bearssl_connect_nonblocking, /* connect_nonblocking */
- bearssl_adjust_pollset, /* adjust_pollset */
+ Curl_ssl_adjust_pollset, /* adjust_pollset */
bearssl_get_internals, /* get_internals */
bearssl_close, /* close_one */
Curl_none_close_all, /* close_all */
@@ -1157,6 +1170,7 @@ const struct Curl_ssl Curl_ssl_bearssl = {
NULL, /* disassociate_connection */
bearssl_recv, /* recv decrypted data */
bearssl_send, /* send data to encrypt */
+ NULL, /* get_channel_binding */
};
#endif /* USE_BEARSSL */
diff --git a/libs/libcurl/src/vtls/cipher_suite.c b/libs/libcurl/src/vtls/cipher_suite.c
index 76a0d424ff..be5dbf5cbe 100644
--- a/libs/libcurl/src/vtls/cipher_suite.c
+++ b/libs/libcurl/src/vtls/cipher_suite.c
@@ -23,7 +23,8 @@
***************************************************************************/
#include "curl_setup.h"
-#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || defined(USE_BEARSSL)
+#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \
+ defined(USE_BEARSSL) || defined(USE_RUSTLS)
#include "cipher_suite.h"
#include "curl_printf.h"
#include "strcase.h"
@@ -163,24 +164,24 @@ enum {
CS_TXT_LEN,
};
-#define CS_ZIP_IDX(a, b, c, d, e, f, g, h) \
-{ \
- (uint8_t) ((a) << 2 | ((b) & 0x3F) >> 4), \
- (uint8_t) ((b) << 4 | ((c) & 0x3F) >> 2), \
- (uint8_t) ((c) << 6 | ((d) & 0x3F)), \
- (uint8_t) ((e) << 2 | ((f) & 0x3F) >> 4), \
- (uint8_t) ((f) << 4 | ((g) & 0x3F) >> 2), \
- (uint8_t) ((g) << 6 | ((h) & 0x3F)) \
+#define CS_ZIP_IDX(a, b, c, d, e, f, g, h) \
+{ \
+ (uint8_t) ((((a) << 2) & 0xFF) | ((b) & 0x3F) >> 4), \
+ (uint8_t) ((((b) << 4) & 0xFF) | ((c) & 0x3F) >> 2), \
+ (uint8_t) ((((c) << 6) & 0xFF) | ((d) & 0x3F)), \
+ (uint8_t) ((((e) << 2) & 0xFF) | ((f) & 0x3F) >> 4), \
+ (uint8_t) ((((f) << 4) & 0xFF) | ((g) & 0x3F) >> 2), \
+ (uint8_t) ((((g) << 6) & 0xFF) | ((h) & 0x3F)) \
}
-#define CS_ENTRY(id, a, b, c, d, e, f, g, h) \
-{ \
- id, \
- CS_ZIP_IDX( \
- CS_TXT_IDX_ ## a, CS_TXT_IDX_ ## b, \
- CS_TXT_IDX_ ## c, CS_TXT_IDX_ ## d, \
- CS_TXT_IDX_ ## e, CS_TXT_IDX_ ## f, \
- CS_TXT_IDX_ ## g, CS_TXT_IDX_ ## h \
- ) \
+#define CS_ENTRY(id, a, b, c, d, e, f, g, h) \
+{ \
+ id, \
+ CS_ZIP_IDX( \
+ CS_TXT_IDX_ ## a, CS_TXT_IDX_ ## b, \
+ CS_TXT_IDX_ ## c, CS_TXT_IDX_ ## d, \
+ CS_TXT_IDX_ ## e, CS_TXT_IDX_ ## f, \
+ CS_TXT_IDX_ ## g, CS_TXT_IDX_ ## h \
+ ) \
}
struct cs_entry {
@@ -190,6 +191,28 @@ struct cs_entry {
/* !checksrc! disable COMMANOSPACE all */
static const struct cs_entry cs_list [] = {
+ /* TLS 1.3 ciphers */
+#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || defined(USE_RUSTLS)
+ CS_ENTRY(0x1301, TLS,AES,128,GCM,SHA256,,,),
+ CS_ENTRY(0x1302, TLS,AES,256,GCM,SHA384,,,),
+ CS_ENTRY(0x1303, TLS,CHACHA20,POLY1305,SHA256,,,,),
+ CS_ENTRY(0x1304, TLS,AES,128,CCM,SHA256,,,),
+ CS_ENTRY(0x1305, TLS,AES,128,CCM,8,SHA256,,),
+#endif
+ /* TLS 1.2 ciphers */
+ CS_ENTRY(0xC02B, TLS,ECDHE,ECDSA,WITH,AES,128,GCM,SHA256),
+ CS_ENTRY(0xC02B, ECDHE,ECDSA,AES128,GCM,SHA256,,,),
+ CS_ENTRY(0xC02C, TLS,ECDHE,ECDSA,WITH,AES,256,GCM,SHA384),
+ CS_ENTRY(0xC02C, ECDHE,ECDSA,AES256,GCM,SHA384,,,),
+ CS_ENTRY(0xC02F, TLS,ECDHE,RSA,WITH,AES,128,GCM,SHA256),
+ CS_ENTRY(0xC02F, ECDHE,RSA,AES128,GCM,SHA256,,,),
+ CS_ENTRY(0xC030, TLS,ECDHE,RSA,WITH,AES,256,GCM,SHA384),
+ CS_ENTRY(0xC030, ECDHE,RSA,AES256,GCM,SHA384,,,),
+ CS_ENTRY(0xCCA8, TLS,ECDHE,RSA,WITH,CHACHA20,POLY1305,SHA256,),
+ CS_ENTRY(0xCCA8, ECDHE,RSA,CHACHA20,POLY1305,,,,),
+ CS_ENTRY(0xCCA9, TLS,ECDHE,ECDSA,WITH,CHACHA20,POLY1305,SHA256,),
+ CS_ENTRY(0xCCA9, ECDHE,ECDSA,CHACHA20,POLY1305,,,,),
+#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || defined(USE_BEARSSL)
CS_ENTRY(0x002F, TLS,RSA,WITH,AES,128,CBC,SHA,),
CS_ENTRY(0x002F, AES128,SHA,,,,,,),
CS_ENTRY(0x0035, TLS,RSA,WITH,AES,256,CBC,SHA,),
@@ -234,26 +257,15 @@ static const struct cs_entry cs_list [] = {
CS_ENTRY(0xC029, ECDH,RSA,AES128,SHA256,,,,),
CS_ENTRY(0xC02A, TLS,ECDH,RSA,WITH,AES,256,CBC,SHA384),
CS_ENTRY(0xC02A, ECDH,RSA,AES256,SHA384,,,,),
- CS_ENTRY(0xC02B, TLS,ECDHE,ECDSA,WITH,AES,128,GCM,SHA256),
- CS_ENTRY(0xC02B, ECDHE,ECDSA,AES128,GCM,SHA256,,,),
- CS_ENTRY(0xC02C, TLS,ECDHE,ECDSA,WITH,AES,256,GCM,SHA384),
- CS_ENTRY(0xC02C, ECDHE,ECDSA,AES256,GCM,SHA384,,,),
CS_ENTRY(0xC02D, TLS,ECDH,ECDSA,WITH,AES,128,GCM,SHA256),
CS_ENTRY(0xC02D, ECDH,ECDSA,AES128,GCM,SHA256,,,),
CS_ENTRY(0xC02E, TLS,ECDH,ECDSA,WITH,AES,256,GCM,SHA384),
CS_ENTRY(0xC02E, ECDH,ECDSA,AES256,GCM,SHA384,,,),
- CS_ENTRY(0xC02F, TLS,ECDHE,RSA,WITH,AES,128,GCM,SHA256),
- CS_ENTRY(0xC02F, ECDHE,RSA,AES128,GCM,SHA256,,,),
- CS_ENTRY(0xC030, TLS,ECDHE,RSA,WITH,AES,256,GCM,SHA384),
- CS_ENTRY(0xC030, ECDHE,RSA,AES256,GCM,SHA384,,,),
CS_ENTRY(0xC031, TLS,ECDH,RSA,WITH,AES,128,GCM,SHA256),
CS_ENTRY(0xC031, ECDH,RSA,AES128,GCM,SHA256,,,),
CS_ENTRY(0xC032, TLS,ECDH,RSA,WITH,AES,256,GCM,SHA384),
CS_ENTRY(0xC032, ECDH,RSA,AES256,GCM,SHA384,,,),
- CS_ENTRY(0xCCA8, TLS,ECDHE,RSA,WITH,CHACHA20,POLY1305,SHA256,),
- CS_ENTRY(0xCCA8, ECDHE,RSA,CHACHA20,POLY1305,,,,),
- CS_ENTRY(0xCCA9, TLS,ECDHE,ECDSA,WITH,CHACHA20,POLY1305,SHA256,),
- CS_ENTRY(0xCCA9, ECDHE,ECDSA,CHACHA20,POLY1305,,,,),
+#endif
#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS)
CS_ENTRY(0x0001, TLS,RSA,WITH,NULL,MD5,,,),
CS_ENTRY(0x0001, NULL,MD5,,,,,,),
@@ -327,11 +339,6 @@ static const struct cs_entry cs_list [] = {
CS_ENTRY(0x00B8, RSA,PSK,NULL,SHA256,,,,),
CS_ENTRY(0x00B9, TLS,RSA,PSK,WITH,NULL,SHA384,,),
CS_ENTRY(0x00B9, RSA,PSK,NULL,SHA384,,,,),
- CS_ENTRY(0x1301, TLS,AES,128,GCM,SHA256,,,),
- CS_ENTRY(0x1302, TLS,AES,256,GCM,SHA384,,,),
- CS_ENTRY(0x1303, TLS,CHACHA20,POLY1305,SHA256,,,,),
- CS_ENTRY(0x1304, TLS,AES,128,CCM,SHA256,,,),
- CS_ENTRY(0x1305, TLS,AES,128,CCM,8,SHA256,,),
CS_ENTRY(0xC001, TLS,ECDH,ECDSA,WITH,NULL,SHA,,),
CS_ENTRY(0xC001, ECDH,ECDSA,NULL,SHA,,,,),
CS_ENTRY(0xC006, TLS,ECDHE,ECDSA,WITH,NULL,SHA,,),
@@ -881,4 +888,4 @@ int Curl_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size,
}
#endif /* defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \
- defined(USE_BEARSSL) */
+ defined(USE_BEARSSL) || defined(USE_RUSTLS) */
diff --git a/libs/libcurl/src/vtls/cipher_suite.h b/libs/libcurl/src/vtls/cipher_suite.h
index 4f66070d43..8f3381d1b9 100644
--- a/libs/libcurl/src/vtls/cipher_suite.h
+++ b/libs/libcurl/src/vtls/cipher_suite.h
@@ -26,7 +26,8 @@
#include "curl_setup.h"
-#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || defined(USE_BEARSSL)
+#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \
+ defined(USE_BEARSSL) || defined(USE_RUSTLS)
#include <stdint.h>
/* Lookup IANA id for cipher suite string, returns 0 if not recognized */
@@ -43,5 +44,5 @@ int Curl_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size,
bool prefer_rfc);
#endif /* defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \
- defined(USE_BEARSSL) */
+ defined(USE_BEARSSL) || defined(USE_RUSTLS) */
#endif /* HEADER_CURL_CIPHER_SUITE_H */
diff --git a/libs/libcurl/src/vtls/gtls.c b/libs/libcurl/src/vtls/gtls.c
index aa838b22a7..2d03712801 100644
--- a/libs/libcurl/src/vtls/gtls.c
+++ b/libs/libcurl/src/vtls/gtls.c
@@ -102,7 +102,7 @@ static ssize_t gtls_push(void *s, const void *buf, size_t blen)
CURLcode result;
DEBUGASSERT(data);
- nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
+ nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &result);
CURL_TRC_CF(data, cf, "gtls_push(len=%zu) -> %zd, err=%d",
blen, nwritten, result);
backend->gtls.io_result = result;
@@ -340,7 +340,7 @@ static CURLcode handshake(struct Curl_cfilter *cf,
if(!strerr)
strerr = gnutls_strerror(rc);
- failf(data, "gnutls_handshake() failed: %s", strerr);
+ failf(data, "GnuTLS, handshake failed: %s", strerr);
return CURLE_SSL_CONNECT_ERROR;
}
@@ -350,7 +350,7 @@ static CURLcode handshake(struct Curl_cfilter *cf,
}
}
-static gnutls_x509_crt_fmt_t do_file_type(const char *type)
+static gnutls_x509_crt_fmt_t gnutls_do_file_type(const char *type)
{
if(!type || !type[0])
return GNUTLS_X509_FMT_PEM;
@@ -368,11 +368,11 @@ static gnutls_x509_crt_fmt_t do_file_type(const char *type)
#define GNUTLS_SRP "+SRP"
static CURLcode
-set_ssl_version_min_max(struct Curl_easy *data,
- struct ssl_peer *peer,
- struct ssl_primary_config *conn_config,
- const char **prioritylist,
- const char *tls13support)
+gnutls_set_ssl_version_min_max(struct Curl_easy *data,
+ struct ssl_peer *peer,
+ 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;
@@ -850,6 +850,13 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
init_flags |= GNUTLS_NO_TICKETS;
#endif
+#if defined(GNUTLS_NO_STATUS_REQUEST)
+ if(!config->verifystatus)
+ /* Disable the "status_request" TLS extension, enabled by default since
+ GnuTLS 3.8.0. */
+ init_flags |= GNUTLS_NO_STATUS_REQUEST;
+#endif
+
rc = gnutls_init(&gtls->session, init_flags);
if(rc != GNUTLS_E_SUCCESS) {
failf(data, "gnutls_init() failed: %d", rc);
@@ -890,8 +897,8 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
}
/* At this point we know we have a supported TLS version, so set it */
- result = set_ssl_version_min_max(data, peer,
- config, &prioritylist, tls13support);
+ result = gnutls_set_ssl_version_min_max(data, peer,
+ config, &prioritylist, tls13support);
if(result)
return result;
@@ -939,7 +946,7 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
gtls->shared_creds->creds,
config->clientcert,
ssl_config->key ? ssl_config->key : config->clientcert,
- do_file_type(ssl_config->cert_type),
+ gnutls_do_file_type(ssl_config->cert_type),
ssl_config->key_passwd,
supported_key_encryption_algorithms);
if(rc != GNUTLS_E_SUCCESS) {
@@ -954,7 +961,7 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
gtls->shared_creds->creds,
config->clientcert,
ssl_config->key ? ssl_config->key : config->clientcert,
- do_file_type(ssl_config->cert_type) ) !=
+ gnutls_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;
@@ -1295,9 +1302,18 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
/* 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",
+ const char *cause = "certificate error, no details available";
+ if(verify_status & GNUTLS_CERT_EXPIRED)
+ cause = "certificate has expired";
+ else if(verify_status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+ cause = "certificate signer not trusted";
+ else if(verify_status & GNUTLS_CERT_INSECURE_ALGORITHM)
+ cause = "certificate uses insecure algorithm";
+ else if(verify_status & GNUTLS_CERT_INVALID_OCSP_STATUS)
+ cause = "attached OCSP status response is invalid";
+ failf(data, "server verification failed: %s. (CAfile: %s "
+ "CRLfile: %s)", cause,
+ config->CAfile ? config->CAfile: "none",
ssl_config->primary.CRLfile ?
ssl_config->primary.CRLfile : "none");
return CURLE_PEER_FAILED_VERIFICATION;
@@ -1312,104 +1328,97 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
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_datum_t status_request;
+ gnutls_ocsp_resp_t ocsp_resp;
+ gnutls_ocsp_cert_status_t status;
+ gnutls_x509_crl_reason_t reason;
- gnutls_ocsp_cert_status_t status;
- gnutls_x509_crl_reason_t reason;
+ rc = gnutls_ocsp_status_request_get(session, &status_request);
- rc = gnutls_ocsp_status_request_get(session, &status_request);
+ if(rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ failf(data, "No OCSP response received");
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
- infof(data, " server certificate status verification FAILED");
+ if(rc < 0) {
+ failf(data, "Invalid OCSP response received");
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
- if(rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
- failf(data, "No OCSP response received");
- return CURLE_SSL_INVALIDCERTSTATUS;
- }
+ gnutls_ocsp_resp_init(&ocsp_resp);
- if(rc < 0) {
- failf(data, "Invalid OCSP response received");
- return CURLE_SSL_INVALIDCERTSTATUS;
- }
+ rc = gnutls_ocsp_resp_import(ocsp_resp, &status_request);
+ if(rc < 0) {
+ failf(data, "Invalid OCSP response received");
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
- gnutls_ocsp_resp_init(&ocsp_resp);
+ (void)gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL,
+ &status, NULL, NULL, NULL, &reason);
- rc = gnutls_ocsp_resp_import(ocsp_resp, &status_request);
- if(rc < 0) {
- failf(data, "Invalid OCSP response received");
- return CURLE_SSL_INVALIDCERTSTATUS;
- }
+ switch(status) {
+ case GNUTLS_OCSP_CERT_GOOD:
+ break;
- (void)gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL,
- &status, NULL, NULL, NULL, &reason);
+ case GNUTLS_OCSP_CERT_REVOKED: {
+ const char *crl_reason;
- switch(status) {
- case GNUTLS_OCSP_CERT_GOOD:
+ switch(reason) {
+ default:
+ case GNUTLS_X509_CRLREASON_UNSPECIFIED:
+ crl_reason = "unspecified reason";
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_KEYCOMPROMISE:
+ crl_reason = "private key compromised";
+ break;
- case GNUTLS_X509_CRLREASON_SUPERSEDED:
- crl_reason = "certificate superseded";
- break;
+ case GNUTLS_X509_CRLREASON_CACOMPROMISE:
+ crl_reason = "CA compromised";
+ break;
- case GNUTLS_X509_CRLREASON_CESSATIONOFOPERATION:
- crl_reason = "operation has ceased";
- break;
+ case GNUTLS_X509_CRLREASON_AFFILIATIONCHANGED:
+ crl_reason = "affiliation has changed";
+ break;
- case GNUTLS_X509_CRLREASON_CERTIFICATEHOLD:
- crl_reason = "certificate is on hold";
- break;
+ case GNUTLS_X509_CRLREASON_SUPERSEDED:
+ crl_reason = "certificate superseded";
+ break;
- case GNUTLS_X509_CRLREASON_REMOVEFROMCRL:
- crl_reason = "will be removed from delta CRL";
- break;
+ case GNUTLS_X509_CRLREASON_CESSATIONOFOPERATION:
+ crl_reason = "operation has ceased";
+ break;
- case GNUTLS_X509_CRLREASON_PRIVILEGEWITHDRAWN:
- crl_reason = "privilege withdrawn";
- break;
+ case GNUTLS_X509_CRLREASON_CERTIFICATEHOLD:
+ crl_reason = "certificate is on hold";
+ break;
- case GNUTLS_X509_CRLREASON_AACOMPROMISE:
- crl_reason = "AA compromised";
- break;
- }
+ case GNUTLS_X509_CRLREASON_REMOVEFROMCRL:
+ crl_reason = "will be removed from delta CRL";
+ break;
- failf(data, "Server certificate was revoked: %s", crl_reason);
+ case GNUTLS_X509_CRLREASON_PRIVILEGEWITHDRAWN:
+ crl_reason = "privilege withdrawn";
break;
- }
- default:
- case GNUTLS_OCSP_CERT_UNKNOWN:
- failf(data, "Server certificate status is unknown");
+ case GNUTLS_X509_CRLREASON_AACOMPROMISE:
+ crl_reason = "AA compromised";
break;
}
- gnutls_ocsp_resp_deinit(ocsp_resp);
+ failf(data, "Server certificate was revoked: %s", crl_reason);
+ break;
+ }
- return CURLE_SSL_INVALIDCERTSTATUS;
+ default:
+ case GNUTLS_OCSP_CERT_UNKNOWN:
+ failf(data, "Server certificate status is unknown");
+ break;
}
- else
- infof(data, " server certificate status verification OK");
+
+ gnutls_ocsp_resp_deinit(ocsp_resp);
+ if(status != GNUTLS_OCSP_CERT_GOOD)
+ return CURLE_SSL_INVALIDCERTSTATUS;
}
else
infof(data, " server certificate status verification SKIPPED");
@@ -1769,28 +1778,44 @@ static bool gtls_data_pending(struct Curl_cfilter *cf,
static ssize_t gtls_send(struct Curl_cfilter *cf,
struct Curl_easy *data,
- const void *mem,
- size_t len,
+ const void *buf,
+ size_t blen,
CURLcode *curlcode)
{
struct ssl_connect_data *connssl = cf->ctx;
struct gtls_ssl_backend_data *backend =
(struct gtls_ssl_backend_data *)connssl->backend;
ssize_t rc;
+ size_t nwritten, total_written = 0;
(void)data;
DEBUGASSERT(backend);
- backend->gtls.io_result = CURLE_OK;
- rc = gnutls_record_send(backend->gtls.session, mem, len);
+ while(blen) {
+ backend->gtls.io_result = CURLE_OK;
+ rc = gnutls_record_send(backend->gtls.session, buf, blen);
- if(rc < 0) {
- *curlcode = (rc == GNUTLS_E_AGAIN)?
- CURLE_AGAIN :
- (backend->gtls.io_result? backend->gtls.io_result : CURLE_SEND_ERROR);
+ if(rc < 0) {
+ if(total_written && (rc == GNUTLS_E_AGAIN)) {
+ *curlcode = CURLE_OK;
+ rc = (ssize_t)total_written;
+ goto out;
+ }
+ *curlcode = (rc == GNUTLS_E_AGAIN)?
+ CURLE_AGAIN :
+ (backend->gtls.io_result? backend->gtls.io_result : CURLE_SEND_ERROR);
- rc = -1;
+ rc = -1;
+ goto out;
+ }
+ nwritten = (size_t)rc;
+ total_written += nwritten;
+ DEBUGASSERT(nwritten <= blen);
+ buf = (char *)buf + nwritten;
+ blen -= nwritten;
}
+ rc = total_written;
+out:
return rc;
}
@@ -1828,6 +1853,7 @@ static CURLcode gtls_shutdown(struct Curl_cfilter *cf,
CURL_TRC_CF(data, cf, "SSL shutdown, gnutls_bye EAGAIN");
connssl->io_need = gnutls_record_get_direction(backend->gtls.session)?
CURL_SSL_IO_NEED_SEND : CURL_SSL_IO_NEED_RECV;
+ backend->gtls.sent_shutdown = FALSE;
result = CURLE_OK;
goto out;
}
@@ -2019,6 +2045,7 @@ const struct Curl_ssl Curl_ssl_gnutls = {
NULL, /* disassociate_connection */
gtls_recv, /* recv decrypted data */
gtls_send, /* send data to encrypt */
+ NULL, /* get_channel_binding */
};
#endif /* USE_GNUTLS */
diff --git a/libs/libcurl/src/vtls/mbedtls.c b/libs/libcurl/src/vtls/mbedtls.c
index 9500baff40..14fedf8b23 100644
--- a/libs/libcurl/src/vtls/mbedtls.c
+++ b/libs/libcurl/src/vtls/mbedtls.c
@@ -200,7 +200,8 @@ static int mbedtls_bio_cf_write(void *bio,
if(!data)
return 0;
- nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, &result);
+ nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, FALSE,
+ &result);
CURL_TRC_CF(data, cf, "mbedtls_bio_cf_out_write(len=%zu) -> %zd, err=%d",
blen, nwritten, result);
if(nwritten < 0 && CURLE_AGAIN == result) {
@@ -258,138 +259,91 @@ static const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_fr =
#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)
-#if MBEDTLS_VERSION_NUMBER >= 0x03020000
-static CURLcode mbedtls_version_from_curl(
- mbedtls_ssl_protocol_version* mbedver, long version)
+static CURLcode
+mbed_set_ssl_version_min_max(struct Curl_easy *data,
+ struct mbed_ssl_backend_data *backend,
+ struct ssl_primary_config *conn_config)
{
- switch(version) {
+ /* TLS 1.0 and TLS 1.1 were dropped with mbedTLS 3.0.0 (2021). So, since
+ * then, and before the introduction of TLS 1.3 in 3.6.0 (2024), this
+ * function basically always sets TLS 1.2 as min/max, unless given
+ * unsupported option values. */
+
+#if MBEDTLS_VERSION_NUMBER < 0x03020000
+ int ver_min = MBEDTLS_SSL_MINOR_VERSION_3; /* TLS 1.2 */
+ int ver_max = MBEDTLS_SSL_MINOR_VERSION_3; /* TLS 1.2 */
+#else
+ /* mbedTLS 3.2.0 (2022) introduced new methods for setting TLS version */
+ mbedtls_ssl_protocol_version ver_min = MBEDTLS_SSL_VERSION_TLS1_2;
+ mbedtls_ssl_protocol_version ver_max = MBEDTLS_SSL_VERSION_TLS1_2;
+#endif
+
+ switch(conn_config->version) {
+ case CURL_SSLVERSION_DEFAULT:
+#if MBEDTLS_VERSION_NUMBER < 0x03000000
+ case CURL_SSLVERSION_TLSv1:
+ case CURL_SSLVERSION_TLSv1_0:
+ ver_min = MBEDTLS_SSL_MINOR_VERSION_1;
+ break;
+ case CURL_SSLVERSION_TLSv1_1:
+ ver_min = MBEDTLS_SSL_MINOR_VERSION_2;
+ break;
+#else
+ case CURL_SSLVERSION_TLSv1:
case CURL_SSLVERSION_TLSv1_0:
case CURL_SSLVERSION_TLSv1_1:
+#endif
case CURL_SSLVERSION_TLSv1_2:
- *mbedver = MBEDTLS_SSL_VERSION_TLS1_2;
- return CURLE_OK;
+ /* ver_min = MBEDTLS_SSL_VERSION_TLS1_2; */
+ break;
case CURL_SSLVERSION_TLSv1_3:
#ifdef TLS13_SUPPORT
- *mbedver = MBEDTLS_SSL_VERSION_TLS1_3;
- return CURLE_OK;
-#else
+ ver_min = MBEDTLS_SSL_VERSION_TLS1_3;
break;
#endif
+ default:
+ failf(data, "mbedTLS: unsupported minimum TLS version value");
+ return CURLE_SSL_CONNECT_ERROR;
}
- return CURLE_SSL_CONNECT_ERROR;
-}
-#else
-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;
-}
-#endif
-
-static CURLcode
-set_ssl_version_min_max(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
- struct ssl_connect_data *connssl = cf->ctx;
- struct mbed_ssl_backend_data *backend =
- (struct mbed_ssl_backend_data *)connssl->backend;
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
-#if MBEDTLS_VERSION_NUMBER >= 0x03020000
- mbedtls_ssl_protocol_version mbedtls_ver_min = MBEDTLS_SSL_VERSION_TLS1_2;
+ switch(conn_config->version_max) {
+ case CURL_SSLVERSION_MAX_DEFAULT:
+ case CURL_SSLVERSION_MAX_NONE:
+ case CURL_SSLVERSION_MAX_TLSv1_3:
#ifdef TLS13_SUPPORT
- mbedtls_ssl_protocol_version mbedtls_ver_max = MBEDTLS_SSL_VERSION_TLS1_3;
-#else
- mbedtls_ssl_protocol_version mbedtls_ver_max = MBEDTLS_SSL_VERSION_TLS1_2;
-#endif
-#elif 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;
+ ver_max = MBEDTLS_SSL_VERSION_TLS1_3;
+ break;
#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:
-#ifdef TLS13_SUPPORT
- ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_3;
+ case CURL_SSLVERSION_MAX_TLSv1_2:
+ /* ver_max = MBEDTLS_SSL_VERSION_TLS1_2; */
+ break;
+#if MBEDTLS_VERSION_NUMBER < 0x03000000
+ case CURL_SSLVERSION_MAX_TLSv1_1:
+ ver_max = MBEDTLS_SSL_MINOR_VERSION_2;
+ break;
+ case CURL_SSLVERSION_MAX_TLSv1_0:
+ ver_max = MBEDTLS_SSL_MINOR_VERSION_1;
+ break;
#else
- ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2;
+ case CURL_SSLVERSION_MAX_TLSv1_1:
+ case CURL_SSLVERSION_MAX_TLSv1_0:
#endif
- 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;
+ default:
+ failf(data, "mbedTLS: unsupported maximum TLS version value");
+ return CURLE_SSL_CONNECT_ERROR;
}
-#if MBEDTLS_VERSION_NUMBER >= 0x03020000
- mbedtls_ssl_conf_min_tls_version(&backend->config, mbedtls_ver_min);
- mbedtls_ssl_conf_max_tls_version(&backend->config, mbedtls_ver_max);
-#else
+#if MBEDTLS_VERSION_NUMBER < 0x03020000
mbedtls_ssl_conf_min_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3,
- mbedtls_ver_min);
+ ver_min);
mbedtls_ssl_conf_max_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3,
- mbedtls_ver_max);
-#endif
-
-#ifdef TLS13_SUPPORT
- if(mbedtls_ver_min == MBEDTLS_SSL_VERSION_TLS1_3) {
- mbedtls_ssl_conf_authmode(&backend->config, MBEDTLS_SSL_VERIFY_REQUIRED);
- }
- else {
- mbedtls_ssl_conf_authmode(&backend->config, MBEDTLS_SSL_VERIFY_OPTIONAL);
- }
+ ver_max);
#else
- mbedtls_ssl_conf_authmode(&backend->config, MBEDTLS_SSL_VERIFY_OPTIONAL);
+ mbedtls_ssl_conf_min_tls_version(&backend->config, ver_min);
+ mbedtls_ssl_conf_max_tls_version(&backend->config, ver_max);
#endif
- return result;
+ return CURLE_OK;
}
/* TLS_ECJPAKE_WITH_AES_128_CCM_8 (0xC0FF) is marked experimental
@@ -428,11 +382,13 @@ mbed_cipher_suite_walk_str(const char **str, const char **end)
static CURLcode
mbed_set_selected_ciphers(struct Curl_easy *data,
struct mbed_ssl_backend_data *backend,
- const char *ciphers)
+ const char *ciphers12,
+ const char *ciphers13)
{
+ const char *ciphers = ciphers12;
const int *supported;
int *selected;
- size_t supported_len, count = 0, i;
+ size_t supported_len, count = 0, default13_count = 0, i, j;
const char *ptr, *end;
supported = mbedtls_ssl_list_ciphersuites();
@@ -443,6 +399,26 @@ mbed_set_selected_ciphers(struct Curl_easy *data,
if(!selected)
return CURLE_OUT_OF_MEMORY;
+#ifndef TLS13_SUPPORT
+ (void) ciphers13, (void) j;
+#else
+ if(!ciphers13) {
+ /* Add default TLSv1.3 ciphers to selection */
+ for(j = 0; j < supported_len; j++) {
+ uint16_t id = (uint16_t) supported[j];
+ if(strncmp(mbedtls_ssl_get_ciphersuite_name(id), "TLS1-3", 6) != 0)
+ continue;
+
+ selected[count++] = id;
+ }
+
+ default13_count = count;
+ }
+ else
+ ciphers = ciphers13;
+
+add_ciphers:
+#endif
for(ptr = ciphers; ptr[0] != '\0' && count < supported_len; ptr = end) {
uint16_t id = mbed_cipher_suite_walk_str(&ptr, &end);
@@ -462,14 +438,38 @@ mbed_set_selected_ciphers(struct Curl_easy *data,
/* No duplicates allowed (so selected cannot overflow) */
for(i = 0; i < count && selected[i] != id; i++);
if(i < count) {
- infof(data, "mbedTLS: duplicate cipher in list: \"%.*s\"",
- (int) (end - ptr), ptr);
+ if(i >= default13_count)
+ infof(data, "mbedTLS: duplicate cipher in list: \"%.*s\"",
+ (int) (end - ptr), ptr);
continue;
}
selected[count++] = id;
}
+#ifdef TLS13_SUPPORT
+ if(ciphers == ciphers13 && ciphers12) {
+ ciphers = ciphers12;
+ goto add_ciphers;
+ }
+
+ if(!ciphers12) {
+ /* Add default TLSv1.2 ciphers to selection */
+ for(j = 0; j < supported_len; j++) {
+ uint16_t id = (uint16_t) supported[j];
+ if(strncmp(mbedtls_ssl_get_ciphersuite_name(id), "TLS1-3", 6) == 0)
+ continue;
+
+ /* No duplicates allowed (so selected cannot overflow) */
+ for(i = 0; i < count && selected[i] != id; i++);
+ if(i < count)
+ continue;
+
+ selected[count++] = id;
+ }
+ }
+#endif
+
selected[count] = 0;
if(count == 0) {
@@ -485,19 +485,79 @@ mbed_set_selected_ciphers(struct Curl_easy *data,
return CURLE_OK;
}
-#ifdef TLS13_SUPPORT
-static int mbed_no_verify(void *udata, mbedtls_x509_crt *crt,
+static void
+mbed_dump_cert_info(struct Curl_easy *data, const mbedtls_x509_crt *crt)
+{
+#if defined(CURL_DISABLE_VERBOSE_STRINGS) || \
+ (MBEDTLS_VERSION_NUMBER >= 0x03000000 && defined(MBEDTLS_X509_REMOVE_INFO))
+ (void) data, (void) crt;
+#else
+ const size_t bufsize = 16384;
+ char *p, *buffer = malloc(bufsize);
+
+ if(buffer && mbedtls_x509_crt_info(buffer, bufsize, " ", crt) > 0) {
+ infof(data, "Server certificate:");
+ for(p = buffer; *p; p += *p != '\0') {
+ size_t s = strcspn(p, "\n");
+ infof(data, "%.*s", (int) s, p);
+ p += s;
+ }
+ }
+ else
+ infof(data, "Unable to dump certificate information");
+
+ free(buffer);
+#endif
+}
+
+static void
+mbed_extract_certinfo(struct Curl_easy *data, const mbedtls_x509_crt *crt)
+{
+ CURLcode result;
+ const mbedtls_x509_crt *cur;
+ int i;
+
+ for(i = 0, cur = crt; cur; ++i, cur = cur->next);
+ result = Curl_ssl_init_certinfo(data, i);
+
+ for(i = 0, cur = crt; result == CURLE_OK && cur; ++i, cur = cur->next) {
+ const char *beg = (const char *) cur->raw.p;
+ const char *end = beg + cur->raw.len;
+ result = Curl_extract_certinfo(data, i, beg, end);
+ }
+}
+
+static int mbed_verify_cb(void *ptr, mbedtls_x509_crt *crt,
int depth, uint32_t *flags)
{
- (void)udata;
- (void)crt;
- (void)depth;
- /* we clear any faults the mbedtls' own verification found.
- * See <https://github.com/Mbed-TLS/mbedtls/issues/9210> */
- *flags = 0;
+ struct Curl_cfilter *cf = (struct Curl_cfilter *) ptr;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
+
+ if(depth == 0) {
+ if(data->set.verbose)
+ mbed_dump_cert_info(data, crt);
+ if(data->set.ssl.certinfo)
+ mbed_extract_certinfo(data, crt);
+ }
+
+ if(!conn_config->verifypeer)
+ *flags = 0;
+ else if(!conn_config->verifyhost)
+ *flags &= ~MBEDTLS_X509_BADCERT_CN_MISMATCH;
+
+ if(*flags) {
+#if MBEDTLS_VERSION_NUMBER < 0x03000000 || !defined(MBEDTLS_X509_REMOVE_INFO)
+ char buf[128];
+ mbedtls_x509_crt_verify_info(buf, sizeof(buf), "", *flags);
+ failf(data, "mbedTLS: %s", buf);
+#else
+ failf(data, "mbedTLS: cerificate verification error 0x%08x", *flags);
+#endif
+ }
+
return 0;
}
-#endif
static CURLcode
mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
@@ -755,16 +815,12 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
failf(data, "mbedTLS: ssl_config failed");
return CURLE_SSL_CONNECT_ERROR;
}
-#ifdef TLS13_SUPPORT
- if(!verifypeer) {
- /* Default verify behaviour changed in mbedtls v3.6.0 with TLS v1.3.
- * On 1.3 connections, the handshake fails by default without trust
- * anchors. We override this questionable change by installing our
- * own verify callback that clears all errors. */
- mbedtls_ssl_conf_verify(&backend->config, mbed_no_verify, cf);
- }
-#endif
+ /* Always let mbedTLS verify certificates, if verifypeer or verifyhost are
+ * disabled we clear the corresponding error flags in the verify callback
+ * function. That is also where we log verification errors. */
+ mbedtls_ssl_conf_verify(&backend->config, mbed_verify_cb, cf);
+ mbedtls_ssl_conf_authmode(&backend->config, MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_init(&backend->ssl);
backend->initialized = TRUE;
@@ -773,29 +829,9 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
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;
- }
+ ret = mbed_set_ssl_version_min_max(data, backend, conn_config);
+ if(ret != CURLE_OK)
+ return ret;
mbedtls_ssl_conf_rng(&backend->config, mbedtls_ctr_drbg_random,
&backend->ctr_drbg);
@@ -813,9 +849,17 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
mbedtls_bio_cf_read,
NULL /* rev_timeout() */);
+#ifndef TLS13_SUPPORT
if(conn_config->cipher_list) {
CURLcode result = mbed_set_selected_ciphers(data, backend,
- conn_config->cipher_list);
+ conn_config->cipher_list,
+ NULL);
+#else
+ if(conn_config->cipher_list || conn_config->cipher_list13) {
+ CURLcode result = mbed_set_selected_ciphers(data, backend,
+ conn_config->cipher_list,
+ conn_config->cipher_list13);
+#endif
if(result != CURLE_OK) {
failf(data, "mbedTLS: failed to set cipher suites");
return result;
@@ -923,60 +967,6 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
return CURLE_OK;
}
-static int count_server_cert(const mbedtls_x509_crt *peercert)
-{
- int count = 1;
-
- DEBUGASSERT(peercert);
-
- while(peercert->next) {
- ++count;
- peercert = peercert->next;
- }
- return count;
-}
-
-static CURLcode collect_server_cert_single(struct Curl_easy *data,
- const mbedtls_x509_crt *server_cert,
- int idx)
-{
- const char *beg, *end;
-
- DEBUGASSERT(server_cert);
-
- beg = (const char *)server_cert->raw.p;
- end = beg + server_cert->raw.len;
- return Curl_extract_certinfo(data, idx, beg, end);
-}
-
-static CURLcode collect_server_cert(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct mbedtls_x509_crt *peercert)
-{
-#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 = CURLE_PEER_FAILED_VERIFICATION;
- int i, count;
-
- if(!show_verbose_server_cert && !ssl_config->certinfo)
- return CURLE_OK;
-
- if(!peercert)
- return result;
-
- count = count_server_cert(peercert);
- result = Curl_ssl_init_certinfo(data, count);
- for(i = 0 ; !result && peercert ; i++) {
- result = collect_server_cert_single(data, peercert, i);
- peercert = peercert->next;
- }
- return result;
-}
-
static CURLcode
mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
{
@@ -984,8 +974,6 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
struct ssl_connect_data *connssl = cf->ctx;
struct mbed_ssl_backend_data *backend =
(struct mbed_ssl_backend_data *)connssl->backend;
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
- const mbedtls_x509_crt *peercert;
#ifndef CURL_DISABLE_PROXY
const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf)?
data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
@@ -1029,69 +1017,22 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
cipher_id = (uint16_t)
mbedtls_ssl_get_ciphersuite_id_from_ssl(&backend->ssl);
mbed_cipher_suite_get_str(cipher_id, cipher_str, sizeof(cipher_str), true);
- infof(data, "mbedTLS: Handshake complete, cipher is %s", cipher_str);
+ infof(data, "mbedTLS: %s Handshake complete, cipher is %s",
+ mbedtls_ssl_get_version(&backend->ssl), cipher_str);
}
#else
- infof(data, "mbedTLS: Handshake complete");
+ infof(data, "mbedTLS: %s Handshake complete",
+ mbedtls_ssl_get_version(&backend->ssl));
#endif
- 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) {
- const CURLcode result = collect_server_cert(cf, data, peercert);
- if(result)
- return result;
- }
-
- if(peercert && data->set.verbose) {
-#ifndef MBEDTLS_X509_REMOVE_INFO
- 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);
-#else
- infof(data, "Unable to dump certificate information");
-#endif
- }
if(pinnedpubkey) {
int size;
CURLcode result;
+ const mbedtls_x509_crt *peercert;
mbedtls_x509_crt *p = NULL;
unsigned char *pubkey = NULL;
+ peercert = mbedtls_ssl_get_peer_cert(&backend->ssl);
#if MBEDTLS_VERSION_NUMBER == 0x03000000
if(!peercert || !peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p) ||
!peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len)) {
@@ -1668,7 +1609,11 @@ const struct Curl_ssl Curl_ssl_mbedtls = {
SSLSUPP_CERTINFO |
SSLSUPP_PINNEDPUBKEY |
SSLSUPP_SSL_CTX |
- SSLSUPP_HTTPS_PROXY,
+#ifdef TLS13_SUPPORT
+ SSLSUPP_TLS13_CIPHERSUITES |
+#endif
+ SSLSUPP_HTTPS_PROXY |
+ SSLSUPP_CIPHER_LIST,
sizeof(struct mbed_ssl_backend_data),
@@ -1695,6 +1640,7 @@ const struct Curl_ssl Curl_ssl_mbedtls = {
NULL, /* disassociate_connection */
mbed_recv, /* recv decrypted data */
mbed_send, /* send data to encrypt */
+ NULL, /* get_channel_binding */
};
#endif /* USE_MBEDTLS */
diff --git a/libs/libcurl/src/vtls/openssl.c b/libs/libcurl/src/vtls/openssl.c
index 69329a7e4b..4aea58d87c 100644
--- a/libs/libcurl/src/vtls/openssl.c
+++ b/libs/libcurl/src/vtls/openssl.c
@@ -314,29 +314,36 @@ typedef unsigned long sslerr_t;
#define USE_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L)
#endif /* !LIBRESSL_VERSION_NUMBER */
-#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 CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl);
+
+static CURLcode push_certinfo(struct Curl_easy *data,
+ BIO *mem, const char *label, int num)
+ WARN_UNUSED_RESULT;
-static void pubkey_show(struct Curl_easy *data,
- BIO *mem,
- int num,
- const char *type,
- const char *name,
- const BIGNUM *bn)
+static CURLcode push_certinfo(struct Curl_easy *data,
+ BIO *mem, const char *label, int num)
{
char *ptr;
+ long len = BIO_get_mem_data(mem, &ptr);
+ CURLcode result = Curl_ssl_push_certinfo_len(data, num, label, ptr, len);
+ (void)BIO_reset(mem);
+ return result;
+}
+
+static CURLcode pubkey_show(struct Curl_easy *data,
+ BIO *mem,
+ int num,
+ const char *type,
+ const char *name,
+ const BIGNUM *bn)
+{
char namebuf[32];
msnprintf(namebuf, sizeof(namebuf), "%s(%s)", type, name);
if(bn)
BN_print(mem, bn);
- push_certinfo(namebuf, num);
+ return push_certinfo(data, mem, namebuf, num);
}
#ifdef HAVE_OPAQUE_RSA_DSA_DH
@@ -368,15 +375,16 @@ static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len)
return 0;
}
-static void X509V3_ext(struct Curl_easy *data,
- int certnum,
- CONST_EXTS STACK_OF(X509_EXTENSION) *exts)
+static CURLcode X509V3_ext(struct Curl_easy *data,
+ int certnum,
+ CONST_EXTS STACK_OF(X509_EXTENSION) *exts)
{
int i;
+ CURLcode result = CURLE_OK;
if((int)sk_X509_EXTENSION_num(exts) <= 0)
/* no extensions, bail out */
- return;
+ return result;
for(i = 0; i < (int)sk_X509_EXTENSION_num(exts); i++) {
ASN1_OBJECT *obj;
@@ -386,7 +394,7 @@ static void X509V3_ext(struct Curl_easy *data,
BIO *bio_out = BIO_new(BIO_s_mem());
if(!bio_out)
- return;
+ return result;
obj = X509_EXTENSION_get_object(ext);
@@ -396,13 +404,16 @@ static void X509V3_ext(struct Curl_easy *data,
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);
+ result = Curl_ssl_push_certinfo_len(data, certnum, namebuf, biomem->data,
+ biomem->length);
BIO_free(bio_out);
+ if(result)
+ break;
}
+ return result;
}
-CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
+static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl)
{
CURLcode result;
STACK_OF(X509) *sk;
@@ -420,38 +431,43 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
numcerts = sk_X509_num(sk);
result = Curl_ssl_init_certinfo(data, (int)numcerts);
- if(result) {
+ if(result)
return result;
- }
mem = BIO_new(BIO_s_mem());
- if(!mem) {
- return CURLE_OUT_OF_MEMORY;
- }
+ if(!mem)
+ result = CURLE_OUT_OF_MEMORY;
- for(i = 0; i < (int)numcerts; i++) {
+ for(i = 0; !result && (i < (int)numcerts); i++) {
ASN1_INTEGER *num;
X509 *x = sk_X509_value(sk, (ossl_valsize_t)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);
+ result = push_certinfo(data, mem, "Subject", i);
+ if(result)
+ break;
X509_NAME_print_ex(mem, X509_get_issuer_name(x), 0, XN_FLAG_ONELINE);
- push_certinfo("Issuer", i);
+ result = push_certinfo(data, mem, "Issuer", i);
+ if(result)
+ break;
BIO_printf(mem, "%lx", X509_get_version(x));
- push_certinfo("Version", i);
+ result = push_certinfo(data, mem, "Version", i);
+ if(result)
+ break;
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);
+ result = push_certinfo(data, mem, "Serial Number", i);
+ if(result)
+ break;
#if defined(HAVE_X509_GET0_SIGNATURE) && defined(HAVE_X509_GET0_EXTENSIONS)
{
@@ -464,7 +480,9 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
const ASN1_OBJECT *sigalgoid = NULL;
X509_ALGOR_get0(&sigalgoid, NULL, NULL, sigalg);
i2a_ASN1_OBJECT(mem, sigalgoid);
- push_certinfo("Signature Algorithm", i);
+ result = push_certinfo(data, mem, "Signature Algorithm", i);
+ if(result)
+ break;
}
xpubkey = X509_get_X509_PUBKEY(x);
@@ -472,11 +490,15 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
X509_PUBKEY_get0_param(&pubkeyoid, NULL, NULL, NULL, xpubkey);
if(pubkeyoid) {
i2a_ASN1_OBJECT(mem, pubkeyoid);
- push_certinfo("Public Key Algorithm", i);
+ result = push_certinfo(data, mem, "Public Key Algorithm", i);
+ if(result)
+ break;
}
}
- X509V3_ext(data, i, X509_get0_extensions(x));
+ result = X509V3_ext(data, i, X509_get0_extensions(x));
+ if(result)
+ break;
}
#else
{
@@ -484,22 +506,32 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
X509_CINF *cinf = x->cert_info;
i2a_ASN1_OBJECT(mem, cinf->signature->algorithm);
- push_certinfo("Signature Algorithm", i);
+ result = push_certinfo(data, mem, "Signature Algorithm", i);
- i2a_ASN1_OBJECT(mem, cinf->key->algor->algorithm);
- push_certinfo("Public Key Algorithm", i);
+ if(!result) {
+ i2a_ASN1_OBJECT(mem, cinf->key->algor->algorithm);
+ result = push_certinfo(data, mem, "Public Key Algorithm", i);
+ }
- X509V3_ext(data, i, cinf->extensions);
+ if(!result)
+ result = X509V3_ext(data, i, cinf->extensions);
+
+ if(result)
+ break;
psig = x->signature;
}
#endif
ASN1_TIME_print(mem, X509_get0_notBefore(x));
- push_certinfo("Start date", i);
+ result = push_certinfo(data, mem, "Start date", i);
+ if(result)
+ break;
ASN1_TIME_print(mem, X509_get0_notAfter(x));
- push_certinfo("Expire date", i);
+ result = push_certinfo(data, mem, "Expire date", i);
+ if(result)
+ break;
pubkey = X509_get_pubkey(x);
if(!pubkey)
@@ -512,8 +544,7 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
pktype = pubkey->type;
#endif
switch(pktype) {
- case EVP_PKEY_RSA:
- {
+ case EVP_PKEY_RSA: {
#ifndef HAVE_EVP_PKEY_GET_PARAMS
RSA *rsa;
#ifdef HAVE_OPAQUE_EVP_PKEY
@@ -537,7 +568,9 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
#else
BIO_printf(mem, "%d", rsa->n ? BN_num_bits(rsa->n) : 0);
#endif /* HAVE_OPAQUE_RSA_DSA_DH */
- push_certinfo("RSA Public Key", i);
+ result = push_certinfo(data, mem, "RSA Public Key", i);
+ if(result)
+ break;
print_pubkey_BN(rsa, n, i);
print_pubkey_BN(rsa, e, i);
FREE_PKEY_PARAM_BIGNUM(n);
@@ -585,8 +618,7 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
#endif /* !OPENSSL_NO_DSA */
break;
}
- case EVP_PKEY_DH:
- {
+ case EVP_PKEY_DH: {
#ifndef HAVE_EVP_PKEY_GET_PARAMS
DH *dh;
#ifdef HAVE_OPAQUE_EVP_PKEY
@@ -629,19 +661,25 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
EVP_PKEY_free(pubkey);
}
- if(psig) {
+ if(!result && psig) {
for(j = 0; j < psig->length; j++)
BIO_printf(mem, "%02x:", psig->data[j]);
- push_certinfo("Signature", i);
+ result = push_certinfo(data, mem, "Signature", i);
}
- PEM_write_bio_X509(mem, x);
- push_certinfo("Cert", i);
+ if(!result) {
+ PEM_write_bio_X509(mem, x);
+ result = push_certinfo(data, mem, "Cert", i);
+ }
}
BIO_free(mem);
- return CURLE_OK;
+ if(result)
+ /* cleanup all leftovers */
+ Curl_ssl_free_certinfo(data);
+
+ return result;
}
#endif /* quiche or OpenSSL */
@@ -723,7 +761,8 @@ static int ossl_bio_cf_out_write(BIO *bio, const char *buf, int blen)
if(blen < 0)
return 0;
- nwritten = Curl_conn_cf_send(cf->next, data, buf, (size_t)blen, &result);
+ nwritten = Curl_conn_cf_send(cf->next, data, buf, (size_t)blen, FALSE,
+ &result);
CURL_TRC_CF(data, cf, "ossl_bio_cf_out_write(len=%d) -> %d, err=%d",
blen, (int)nwritten, result);
BIO_clear_retry_flags(bio);
@@ -997,12 +1036,6 @@ static CURLcode ossl_seed(struct Curl_easy *data)
return CURLE_SSL_CONNECT_ERROR;
#else
-#ifdef RANDOM_FILE
- RAND_load_file(RANDOM_FILE, RAND_LOAD_LENGTH);
- if(rand_enough())
- return CURLE_OK;
-#endif
-
/* fallback to a custom seeding of the PRNG using a hash based on a current
time */
do {
@@ -1049,7 +1082,7 @@ static CURLcode ossl_seed(struct Curl_easy *data)
#ifndef SSL_FILETYPE_PKCS12
#define SSL_FILETYPE_PKCS12 43
#endif
-static int do_file_type(const char *type)
+static int ossl_do_file_type(const char *type)
{
if(!type || !type[0])
return SSL_FILETYPE_PEM;
@@ -1271,7 +1304,7 @@ int cert_stuff(struct Curl_easy *data,
char error_buffer[256];
bool check_privkey = TRUE;
- int file_type = do_file_type(cert_type);
+ int file_type = ossl_do_file_type(cert_type);
if(cert_file || cert_blob || (file_type == SSL_FILETYPE_ENGINE)) {
SSL *ssl;
@@ -1512,7 +1545,7 @@ fail:
key_blob = cert_blob;
}
else
- file_type = do_file_type(key_type);
+ file_type = ossl_do_file_type(key_type);
switch(file_type) {
case SSL_FILETYPE_PEM:
@@ -1646,22 +1679,6 @@ fail:
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)
{
@@ -1878,7 +1895,7 @@ static CURLcode ossl_shutdown(struct Curl_cfilter *cf,
struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
CURLcode result = CURLE_OK;
char buf[1024];
- int nread, err;
+ int nread = -1, err;
unsigned long sslerr;
size_t i;
@@ -1919,15 +1936,26 @@ static CURLcode ossl_shutdown(struct Curl_cfilter *cf,
goto out;
}
}
- if(send_shutdown && SSL_shutdown(octx->ssl) == 1) {
+ }
+
+ /* SSL should now have started the shutdown from our side. Since it
+ * was not complete, we are lacking the close notify from the server. */
+ if(send_shutdown) {
+ ERR_clear_error();
+ if(SSL_shutdown(octx->ssl) == 1) {
CURL_TRC_CF(data, cf, "SSL shutdown finished");
*done = TRUE;
goto out;
}
+ if(SSL_ERROR_WANT_WRITE == SSL_get_error(octx->ssl, nread)) {
+ CURL_TRC_CF(data, cf, "SSL shutdown still wants to send");
+ connssl->io_need = CURL_SSL_IO_NEED_SEND;
+ goto out;
+ }
+ /* Having sent the close notify, we use SSL_read() to get the
+ * missing close notify from the server. */
}
- /* SSL should now have started the shutdown from our side. Since it
- * was not complete, we are lacking the close notify from the server. */
for(i = 0; i < 10; ++i) {
ERR_clear_error();
nread = SSL_read(octx->ssl, buf, (int)sizeof(buf));
@@ -1935,11 +1963,6 @@ static CURLcode ossl_shutdown(struct Curl_cfilter *cf,
if(nread <= 0)
break;
}
- if(SSL_get_shutdown(octx->ssl) & SSL_RECEIVED_SHUTDOWN) {
- CURL_TRC_CF(data, cf, "SSL shutdown received, finished");
- *done = TRUE;
- goto out;
- }
err = SSL_get_error(octx->ssl, nread);
switch(err) {
case SSL_ERROR_ZERO_RETURN: /* no more data */
@@ -2003,7 +2026,7 @@ static void ossl_session_free(void *sessionid, size_t idsize)
{
/* free the ID */
(void)idsize;
- SSL_SESSION_free(sessionid);
+ free(sessionid);
}
/*
@@ -2077,8 +2100,9 @@ static bool subj_alt_hostcheck(struct Curl_easy *data,
This function is now used from ngtcp2 (QUIC) as well.
*/
-CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
- struct ssl_peer *peer, X509 *server_cert)
+static CURLcode ossl_verifyhost(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct ssl_peer *peer, X509 *server_cert)
{
bool matched = FALSE;
int target; /* target type, GEN_DNS or GEN_IPADD */
@@ -2868,6 +2892,9 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
{
const struct ssl_config_data *config;
CURLcode result = CURLE_OK;
+ size_t der_session_size;
+ unsigned char *der_session_buf;
+ unsigned char *der_session_ptr;
if(!cf || !data)
goto out;
@@ -2875,16 +2902,32 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
config = Curl_ssl_cf_get_config(cf, data);
if(config->primary.cache_session) {
+ der_session_size = i2d_SSL_SESSION(session, NULL);
+ if(der_session_size == 0) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ der_session_buf = der_session_ptr = malloc(der_session_size);
+ if(!der_session_buf) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ der_session_size = i2d_SSL_SESSION(session, &der_session_ptr);
+ if(der_session_size == 0) {
+ result = CURLE_OUT_OF_MEMORY;
+ free(der_session_buf);
+ goto out;
+ }
+
Curl_ssl_sessionid_lock(data);
- result = Curl_ssl_set_sessionid(cf, data, peer, session, 0,
- ossl_session_free);
- session = NULL; /* call has taken ownership */
+ result = Curl_ssl_set_sessionid(cf, data, peer, der_session_buf,
+ der_session_size, ossl_session_free);
Curl_ssl_sessionid_unlock(data);
}
out:
- if(session)
- ossl_session_free(session, 0);
return result;
}
@@ -2901,7 +2944,7 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
connssl = cf? cf->ctx : NULL;
data = connssl? CF_DATA_CURRENT(cf) : NULL;
Curl_ossl_add_session(cf, data, &connssl->peer, ssl_sessionid);
- return 1;
+ return 0;
}
static CURLcode load_cacert_from_memory(X509_STORE *store,
@@ -3451,7 +3494,9 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
const char *ciphers;
SSL_METHOD_QUAL SSL_METHOD *req_method = NULL;
ctx_option_t ctx_options = 0;
- void *ssl_sessionid = NULL;
+ SSL_SESSION *ssl_session = NULL;
+ const unsigned char *der_sessionid = NULL;
+ size_t der_sessionid_size = 0;
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 long int ssl_version_min = conn_config->version;
@@ -3631,6 +3676,11 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
SSL_CTX_set_options(octx->ssl_ctx, ctx_options);
+#ifdef SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER
+ /* We do retry writes sometimes from another buffer address */
+ SSL_CTX_set_mode(octx->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+#endif
+
#ifdef HAS_ALPN
if(alpn && alpn_len) {
if(SSL_CTX_set_alpn_protos(octx->ssl_ctx, alpn, (int)alpn_len)) {
@@ -3897,7 +3947,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
if(data->set.tls_ech & CURLECH_HARD)
return CURLE_SSL_CONNECT_ERROR;
}
- Curl_resolv_unlock(data, dns);
+ Curl_resolv_unlink(data, &dns);
}
}
# ifdef OPENSSL_IS_BORINGSSL
@@ -3931,18 +3981,29 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
octx->reused_session = FALSE;
if(ssl_config->primary.cache_session && transport == TRNSPRT_TCP) {
Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, peer, &ssl_sessionid, NULL)) {
+ if(!Curl_ssl_getsessionid(cf, data, peer, (void **)&der_sessionid,
+ &der_sessionid_size)) {
/* we got a session id, use it! */
- if(!SSL_set_session(octx->ssl, 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;
+ ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid,
+ (long)der_sessionid_size);
+ if(ssl_session) {
+ if(!SSL_set_session(octx->ssl, ssl_session)) {
+ Curl_ssl_sessionid_unlock(data);
+ SSL_SESSION_free(ssl_session);
+ failf(data, "SSL: SSL_set_session failed: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ SSL_SESSION_free(ssl_session);
+ /* Informational message */
+ infof(data, "SSL reusing session ID");
+ octx->reused_session = TRUE;
+ }
+ else {
+ Curl_ssl_sessionid_unlock(data);
+ return CURLE_SSL_CONNECT_ERROR;
}
- /* Informational message */
- infof(data, "SSL reusing session ID");
- octx->reused_session = TRUE;
}
Curl_ssl_sessionid_unlock(data);
}
@@ -4110,30 +4171,34 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
<0 is "handshake was not successful, because a fatal error occurred" */
if(1 != err) {
int detail = SSL_get_error(octx->ssl, err);
+ CURL_TRC_CF(data, cf, "SSL_connect() -> err=%d, detail=%d", err, detail);
if(SSL_ERROR_WANT_READ == detail) {
+ CURL_TRC_CF(data, cf, "SSL_connect() -> want recv");
connssl->io_need = CURL_SSL_IO_NEED_RECV;
return CURLE_OK;
}
if(SSL_ERROR_WANT_WRITE == detail) {
+ CURL_TRC_CF(data, cf, "SSL_connect() -> want send");
connssl->io_need = CURL_SSL_IO_NEED_SEND;
return CURLE_OK;
}
#ifdef SSL_ERROR_WANT_ASYNC
if(SSL_ERROR_WANT_ASYNC == detail) {
+ CURL_TRC_CF(data, cf, "SSL_connect() -> want async");
+ connssl->io_need = CURL_SSL_IO_NEED_RECV;
connssl->connecting_state = ssl_connect_2;
return CURLE_OK;
}
#endif
#ifdef SSL_ERROR_WANT_RETRY_VERIFY
if(SSL_ERROR_WANT_RETRY_VERIFY == detail) {
+ CURL_TRC_CF(data, cf, "SSL_connect() -> want retry_verify");
+ connssl->io_need = CURL_SSL_IO_NEED_RECV;
connssl->connecting_state = ssl_connect_2;
return CURLE_OK;
}
#endif
- if(octx->io_result == CURLE_AGAIN) {
- return CURLE_OK;
- }
else {
/* untreated error */
sslerr_t errdetail;
@@ -4488,7 +4553,7 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
if(data->set.ssl.certinfo)
/* asked to gather certificate info */
- (void)Curl_ossl_certchain(data, octx->ssl);
+ (void)ossl_certchain(data, octx->ssl);
octx->server_cert = SSL_get1_peer_certificate(octx->ssl);
if(!octx->server_cert) {
@@ -4525,7 +4590,7 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
BIO_free(mem);
if(conn_config->verifyhost) {
- result = Curl_ossl_verifyhost(data, conn, peer, octx->server_cert);
+ result = ossl_verifyhost(data, conn, peer, octx->server_cert);
if(result) {
X509_free(octx->server_cert);
octx->server_cert = NULL;
@@ -4721,6 +4786,7 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
int what;
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
/* check if the connection has already been established */
if(ssl_connection_complete == connssl->state) {
*done = TRUE;
@@ -4867,6 +4933,7 @@ static ssize_t ossl_send(struct Curl_cfilter *cf,
ERR_clear_error();
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
rc = SSL_write(octx->ssl, mem, memlen);
@@ -4875,10 +4942,11 @@ static ssize_t ossl_send(struct Curl_cfilter *cf,
switch(err) {
case SSL_ERROR_WANT_READ:
+ connssl->io_need = CURL_SSL_IO_NEED_RECV;
+ *curlcode = CURLE_AGAIN;
+ rc = -1;
+ goto out;
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;
@@ -4950,6 +5018,7 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf,
ERR_clear_error();
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
nread = (ssize_t)SSL_read(octx->ssl, buf, buffsize);
@@ -4968,8 +5037,11 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf,
connclose(conn, "TLS close_notify");
break;
case SSL_ERROR_WANT_READ:
+ *curlcode = CURLE_AGAIN;
+ nread = -1;
+ goto out;
case SSL_ERROR_WANT_WRITE:
- /* there is data pending, re-invoke SSL_read() */
+ connssl->io_need = CURL_SSL_IO_NEED_SEND;
*curlcode = CURLE_AGAIN;
nread = -1;
goto out;
@@ -5032,6 +5104,91 @@ out:
return nread;
}
+static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex,
+ struct dynbuf *binding)
+{
+ /* required for X509_get_signature_nid support */
+#if OPENSSL_VERSION_NUMBER > 0x10100000L
+ X509 *cert;
+ int algo_nid;
+ const EVP_MD *algo_type;
+ const char *algo_name;
+ unsigned int length;
+ unsigned char buf[EVP_MAX_MD_SIZE];
+
+ const char prefix[] = "tls-server-end-point:";
+ struct connectdata *conn = data->conn;
+ struct Curl_cfilter *cf = conn->cfilter[sockindex];
+ struct ossl_ctx *octx = NULL;
+
+ do {
+ const struct Curl_cftype *cft = cf->cft;
+ struct ssl_connect_data *connssl = cf->ctx;
+
+ if(cft->name && !strcmp(cft->name, "SSL")) {
+ octx = (struct ossl_ctx *)connssl->backend;
+ break;
+ }
+
+ if(cf->next)
+ cf = cf->next;
+
+ } while(cf->next);
+
+ if(!octx) {
+ failf(data,
+ "Failed to find SSL backend for endpoint");
+ return CURLE_SSL_ENGINE_INITFAILED;
+ }
+
+ cert = SSL_get1_peer_certificate(octx->ssl);
+ if(!cert) {
+ /* No server certificate, don't do channel binding */
+ return CURLE_OK;
+ }
+
+ if(!OBJ_find_sigid_algs(X509_get_signature_nid(cert), &algo_nid, NULL)) {
+ failf(data,
+ "Unable to find digest NID for certificate signature algorithm");
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
+
+ /* https://datatracker.ietf.org/doc/html/rfc5929#section-4.1 */
+ if(algo_nid == NID_md5 || algo_nid == NID_sha1) {
+ algo_type = EVP_sha256();
+ }
+ else {
+ algo_type = EVP_get_digestbynid(algo_nid);
+ if(!algo_type) {
+ algo_name = OBJ_nid2sn(algo_nid);
+ failf(data, "Could not find digest algorithm %s (NID %d)",
+ algo_name ? algo_name : "(null)", algo_nid);
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
+ }
+
+ if(!X509_digest(cert, algo_type, buf, &length)) {
+ failf(data, "X509_digest() failed");
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
+
+ /* Append "tls-server-end-point:" */
+ if(Curl_dyn_addn(binding, prefix, sizeof(prefix) - 1) != CURLE_OK)
+ return CURLE_OUT_OF_MEMORY;
+ /* Append digest */
+ if(Curl_dyn_addn(binding, buf, length))
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_OK;
+#else
+ /* No X509_get_signature_nid support */
+ (void)data; /* unused */
+ (void)sockindex; /* unused */
+ (void)binding; /* unused */
+ return CURLE_OK;
+#endif
+}
+
static size_t ossl_version(char *buffer, size_t size)
{
#ifdef LIBRESSL_VERSION_NUMBER
@@ -5189,7 +5346,8 @@ const struct Curl_ssl Curl_ssl_openssl = {
SSLSUPP_ECH |
#endif
SSLSUPP_CA_CACHE |
- SSLSUPP_HTTPS_PROXY,
+ SSLSUPP_HTTPS_PROXY |
+ SSLSUPP_CIPHER_LIST,
sizeof(struct ossl_ctx),
@@ -5220,6 +5378,7 @@ const struct Curl_ssl Curl_ssl_openssl = {
NULL, /* remote of data from this connection */
ossl_recv, /* recv decrypted data */
ossl_send, /* send data to encrypt */
+ ossl_get_channel_binding /* get_channel_binding */
};
#endif /* USE_OPENSSL */
diff --git a/libs/libcurl/src/vtls/openssl.h b/libs/libcurl/src/vtls/openssl.h
index 46e75efbf1..e662e23a8d 100644
--- a/libs/libcurl/src/vtls/openssl.h
+++ b/libs/libcurl/src/vtls/openssl.h
@@ -74,19 +74,8 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
#define SSL_get1_peer_certificate SSL_get_peer_certificate
#endif
-CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
- struct ssl_peer *peer, X509 *server_cert);
extern const struct Curl_ssl Curl_ssl_openssl;
-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);
-
-CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl);
-
/**
* Setup the OpenSSL X509_STORE in `ssl_ctx` for the cfilter `cf` and
* easy handle `data`. Will allow reuse of a shared cache if suitable
diff --git a/libs/libcurl/src/vtls/rustls.c b/libs/libcurl/src/vtls/rustls.c
index 70592e6f76..668c24dd43 100644
--- a/libs/libcurl/src/vtls/rustls.c
+++ b/libs/libcurl/src/vtls/rustls.c
@@ -41,6 +41,8 @@
#include "strerror.h"
#include "multiif.h"
#include "connect.h" /* for the connect timeout */
+#include "cipher_suite.h"
+#include "rand.h"
struct rustls_ssl_backend_data
{
@@ -115,7 +117,8 @@ write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n)
CURLcode result;
int ret = 0;
ssize_t nwritten = Curl_conn_cf_send(io_ctx->cf->next, io_ctx->data,
- (const char *)buf, len, &result);
+ (const char *)buf, len, FALSE,
+ &result);
if(nwritten < 0) {
nwritten = 0;
if(CURLE_AGAIN == result)
@@ -174,14 +177,14 @@ static ssize_t tls_recv_more(struct Curl_cfilter *cf,
/*
* 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
+ * - 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 is 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 will not consume bytes from rustls'
+ * that case, it will copy bytes from the socket into Rustls' TLS input
+ * buffer, and process packets, but will not consume bytes from Rustls'
* plaintext output buffer.
*/
static ssize_t
@@ -305,13 +308,13 @@ static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data,
/*
* On each call:
- * - Copy `plainlen` bytes into rustls' plaintext input buffer (if > 0).
- * - Fully drain rustls' plaintext output buffer into the socket until
+ * - 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 is okay to call this function with plainbuf == NULL and plainlen == 0.
- * In that case, it will not read anything into rustls' plaintext input buffer.
- * It will only drain rustls' plaintext output buffer into the socket.
+ * In that case, it will not read anything into Rustls' plaintext input buffer.
+ * It will only drain Rustls' plaintext output buffer into the socket.
*/
static ssize_t
cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
@@ -356,7 +359,7 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
}
if(blen > 0) {
- CURL_TRC_CF(data, cf, "cf_send: adding %zu plain bytes to rustls", blen);
+ CURL_TRC_CF(data, cf, "cf_send: adding %zu plain bytes to Rustls", blen);
rresult = rustls_connection_write(rconn, buf, blen, &plainwritten);
if(rresult != RUSTLS_RESULT_OK) {
rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
@@ -375,9 +378,9 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
if(*err) {
if(CURLE_AGAIN == *err) {
/* The TLS bytes may have been partially written, but we fail the
- * complete send() and remember how much we already added to rustls. */
+ * complete send() and remember how much we already added to Rustls. */
CURL_TRC_CF(data, cf, "cf_send: EAGAIN, remember we added %zu plain"
- " bytes already to rustls", blen);
+ " bytes already to Rustls", blen);
backend->plain_out_buffered = plainwritten;
if(nwritten) {
*err = CURLE_OK;
@@ -394,7 +397,7 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
return nwritten;
}
-/* A server certificate verify callback for rustls that always returns
+/* A server certificate verify callback for Rustls that always returns
RUSTLS_RESULT_OK, or in other words disable certificate verification. */
static uint32_t
cr_verify_none(void *userdata UNUSED_PARAM,
@@ -403,20 +406,121 @@ cr_verify_none(void *userdata UNUSED_PARAM,
return RUSTLS_RESULT_OK;
}
-static bool
-cr_hostname_is_ip(const char *hostname)
+static int
+read_file_into(const char *filename,
+ struct dynbuf *out)
{
- struct in_addr in;
-#ifdef USE_IPV6
- struct in6_addr in6;
- if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) {
- return true;
+ FILE *f = fopen(filename, FOPEN_READTEXT);
+ if(!f) {
+ return 0;
}
-#endif /* USE_IPV6 */
- if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
- return true;
+
+ while(!feof(f)) {
+ uint8_t buf[256];
+ size_t rr = fread(buf, 1, sizeof(buf), f);
+ if(rr == 0 ||
+ CURLE_OK != Curl_dyn_addn(out, buf, rr)) {
+ fclose(f);
+ return 0;
+ }
}
- return false;
+
+ return fclose(f) == 0;
+}
+
+static void
+cr_get_selected_ciphers(struct Curl_easy *data,
+ const char *ciphers12,
+ const char *ciphers13,
+ const struct rustls_supported_ciphersuite **selected,
+ size_t *selected_size)
+{
+ size_t supported_len = *selected_size;
+ size_t default_len = rustls_default_ciphersuites_len();
+ const struct rustls_supported_ciphersuite *entry;
+ const char *ciphers = ciphers12;
+ size_t count = 0, default13_count = 0, i, j;
+ const char *ptr, *end;
+
+ DEBUGASSERT(default_len <= supported_len);
+
+ if(!ciphers13) {
+ /* Add default TLSv1.3 ciphers to selection */
+ for(j = 0; j < default_len; j++) {
+ struct rustls_str s;
+ entry = rustls_default_ciphersuites_get_entry(j);
+ s = rustls_supported_ciphersuite_get_name(entry);
+ if(s.len < 5 || strncmp(s.data, "TLS13", 5) != 0)
+ continue;
+
+ selected[count++] = entry;
+ }
+
+ default13_count = count;
+
+ if(!ciphers)
+ ciphers = "";
+ }
+ else
+ ciphers = ciphers13;
+
+add_ciphers:
+ for(ptr = ciphers; ptr[0] != '\0' && count < supported_len; ptr = end) {
+ uint16_t id = Curl_cipher_suite_walk_str(&ptr, &end);
+
+ /* Check if cipher is supported */
+ if(id) {
+ for(i = 0; i < supported_len; i++) {
+ entry = rustls_all_ciphersuites_get_entry(i);
+ if(rustls_supported_ciphersuite_get_suite(entry) == id)
+ break;
+ }
+ if(i == supported_len)
+ id = 0;
+ }
+ if(!id) {
+ if(ptr[0] != '\0')
+ infof(data, "rustls: unknown cipher in list: \"%.*s\"",
+ (int) (end - ptr), ptr);
+ continue;
+ }
+
+ /* No duplicates allowed (so selected cannot overflow) */
+ for(i = 0; i < count && selected[i] != entry; i++);
+ if(i < count) {
+ if(i >= default13_count)
+ infof(data, "rustls: duplicate cipher in list: \"%.*s\"",
+ (int) (end - ptr), ptr);
+ continue;
+ }
+
+ selected[count++] = entry;
+ }
+
+ if(ciphers == ciphers13 && ciphers12) {
+ ciphers = ciphers12;
+ goto add_ciphers;
+ }
+
+ if(!ciphers12) {
+ /* Add default TLSv1.2 ciphers to selection */
+ for(j = 0; j < default_len; j++) {
+ struct rustls_str s;
+ entry = rustls_default_ciphersuites_get_entry(j);
+ s = rustls_supported_ciphersuite_get_name(entry);
+ if(s.len < 5 || strncmp(s.data, "TLS13", 5) == 0)
+ continue;
+
+ /* No duplicates allowed (so selected cannot overflow) */
+ for(i = 0; i < count && selected[i] != entry; i++);
+ if(i < count)
+ continue;
+
+ selected[count++] = entry;
+ }
+ }
+
+ *selected_size = count;
}
static CURLcode
@@ -436,7 +540,6 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
/* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
(ca_info_blob ? NULL : conn_config->CAfile);
const bool verifypeer = conn_config->verifypeer;
- const char *hostname = connssl->peer.hostname;
char errorbuf[256];
size_t errorlen;
rustls_result result;
@@ -444,7 +547,75 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
DEBUGASSERT(backend);
rconn = backend->conn;
- config_builder = rustls_client_config_builder_new();
+ {
+ uint16_t tls_versions[2] = {
+ RUSTLS_TLS_VERSION_TLSV1_2,
+ RUSTLS_TLS_VERSION_TLSV1_3,
+ };
+ size_t tls_versions_len = 2;
+ const struct rustls_supported_ciphersuite **cipher_suites;
+ size_t cipher_suites_len = rustls_default_ciphersuites_len();
+
+ 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:
+ break;
+ case CURL_SSLVERSION_TLSv1_3:
+ tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3;
+ tls_versions_len = 1;
+ break;
+ default:
+ failf(data, "rustls: unsupported minimum TLS version value");
+ return CURLE_SSL_ENGINE_INITFAILED;
+ }
+
+ switch(conn_config->version_max) {
+ case CURL_SSLVERSION_MAX_DEFAULT:
+ case CURL_SSLVERSION_MAX_NONE:
+ case CURL_SSLVERSION_MAX_TLSv1_3:
+ break;
+ case CURL_SSLVERSION_MAX_TLSv1_2:
+ if(tls_versions[0] == RUSTLS_TLS_VERSION_TLSV1_2) {
+ tls_versions_len = 1;
+ break;
+ }
+ FALLTHROUGH();
+ case CURL_SSLVERSION_MAX_TLSv1_1:
+ case CURL_SSLVERSION_MAX_TLSv1_0:
+ default:
+ failf(data, "rustls: unsupported maximum TLS version value");
+ return CURLE_SSL_ENGINE_INITFAILED;
+ }
+
+ cipher_suites = malloc(sizeof(cipher_suites) * (cipher_suites_len));
+ if(!cipher_suites)
+ return CURLE_OUT_OF_MEMORY;
+
+ cr_get_selected_ciphers(data,
+ conn_config->cipher_list,
+ conn_config->cipher_list13,
+ cipher_suites, &cipher_suites_len);
+ if(cipher_suites_len == 0) {
+ failf(data, "rustls: no supported cipher in list");
+ free(cipher_suites);
+ return CURLE_SSL_CIPHER;
+ }
+
+ result = rustls_client_config_builder_new_custom(cipher_suites,
+ cipher_suites_len,
+ tls_versions,
+ tls_versions_len,
+ &config_builder);
+ free(cipher_suites);
+ if(result != RUSTLS_RESULT_OK) {
+ failf(data, "rustls: failed to create client config");
+ return CURLE_SSL_ENGINE_INITFAILED;
+ }
+ }
+
if(connssl->alpn) {
struct alpn_proto_buf proto;
rustls_slice_bytes alpn[ALPN_ENTRIES_MAX];
@@ -462,14 +633,6 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
if(!verifypeer) {
rustls_client_config_builder_dangerous_set_certificate_verifier(
config_builder, cr_verify_none);
- /* rustls does not 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 || ssl_cafile) {
roots_builder = rustls_root_cert_store_builder_new();
@@ -511,6 +674,29 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
}
verifier_builder = rustls_web_pki_server_cert_verifier_builder_new(roots);
+ rustls_root_cert_store_free(roots);
+
+ if(conn_config->CRLfile) {
+ struct dynbuf crl_contents;
+ Curl_dyn_init(&crl_contents, SIZE_MAX);
+ if(!read_file_into(conn_config->CRLfile, &crl_contents)) {
+ failf(data, "rustls: failed to read revocation list file");
+ Curl_dyn_free(&crl_contents);
+ rustls_web_pki_server_cert_verifier_builder_free(verifier_builder);
+ return CURLE_SSL_CRL_BADFILE;
+ }
+
+ result = rustls_web_pki_server_cert_verifier_builder_add_crl(
+ verifier_builder,
+ Curl_dyn_uptr(&crl_contents),
+ Curl_dyn_len(&crl_contents));
+ Curl_dyn_free(&crl_contents);
+ if(result != RUSTLS_RESULT_OK) {
+ failf(data, "rustls: failed to parse revocation list");
+ rustls_web_pki_server_cert_verifier_builder_free(verifier_builder);
+ return CURLE_SSL_CRL_BADFILE;
+ }
+ }
result = rustls_web_pki_server_cert_verifier_builder_build(
verifier_builder, &server_cert_verifier);
@@ -525,6 +711,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
rustls_client_config_builder_set_server_verifier(config_builder,
server_cert_verifier);
+ rustls_server_cert_verifier_free(server_cert_verifier);
}
backend->config = rustls_client_config_builder_build(config_builder);
@@ -602,13 +789,12 @@ cr_connect_common(struct Curl_cfilter *cf,
/* Read/write data until the handshake is done or the socket would block. */
for(;;) {
/*
- * Connection has been established according to rustls. Set send/recv
+ * Connection has been established according to Rustls. Set send/recv
* handlers, and update the state machine.
*/
connssl->io_need = CURL_SSL_IO_NEED_NONE;
if(!rustls_connection_is_handshaking(rconn)) {
- infof(data, "Done handshaking");
- /* rustls claims it is no longer handshaking *before* it has
+ /* Rustls claims it is no longer handshaking *before* it has
* send its FINISHED message off. We attempt to let it write
* one more time. Oh my.
*/
@@ -622,6 +808,22 @@ cr_connect_common(struct Curl_cfilter *cf,
return tmperr;
}
/* REALLY Done with the handshake. */
+ {
+ uint16_t proto = rustls_connection_get_protocol_version(rconn);
+ const rustls_supported_ciphersuite *rcipher =
+ rustls_connection_get_negotiated_ciphersuite(rconn);
+ uint16_t cipher = rcipher ?
+ rustls_supported_ciphersuite_get_suite(rcipher) : 0;
+ char buf[64] = "";
+ const char *ver = "TLS version unknown";
+ if(proto == RUSTLS_TLS_VERSION_TLSV1_3)
+ ver = "TLSv1.3";
+ if(proto == RUSTLS_TLS_VERSION_TLSV1_2)
+ ver = "TLSv1.2";
+ Curl_cipher_suite_get_str(cipher, buf, sizeof(buf), true);
+ infof(data, "rustls: handshake complete, %s, cipher: %s",
+ ver, buf);
+ }
connssl->state = ssl_connection_complete;
*done = TRUE;
return CURLE_OK;
@@ -654,8 +856,8 @@ cr_connect_common(struct Curl_cfilter *cf,
return CURLE_SSL_CONNECT_ERROR;
}
if(blocking && 0 == what) {
- failf(data, "rustls connection timeout after %"
- CURL_FORMAT_TIMEDIFF_T " ms", socket_check_timeout);
+ failf(data, "rustls: connection timeout after %" FMT_TIMEDIFF_T " ms",
+ socket_check_timeout);
return CURLE_OPERATION_TIMEDOUT;
}
if(0 == what) {
@@ -825,7 +1027,9 @@ static size_t cr_version(char *buffer, size_t size)
const struct Curl_ssl Curl_ssl_rustls = {
{ CURLSSLBACKEND_RUSTLS, "rustls" },
SSLSUPP_CAINFO_BLOB | /* supports */
- SSLSUPP_HTTPS_PROXY,
+ SSLSUPP_HTTPS_PROXY |
+ SSLSUPP_CIPHER_LIST |
+ SSLSUPP_TLS13_CIPHERSUITES,
sizeof(struct rustls_ssl_backend_data),
Curl_none_init, /* init */
@@ -834,7 +1038,7 @@ const struct Curl_ssl Curl_ssl_rustls = {
Curl_none_check_cxn, /* check_cxn */
cr_shutdown, /* shutdown */
cr_data_pending, /* data_pending */
- Curl_none_random, /* random */
+ Curl_weak_random, /* random */
Curl_none_cert_status_request, /* cert_status_request */
cr_connect_blocking, /* connect */
cr_connect_nonblocking, /* connect_nonblocking */
@@ -851,6 +1055,7 @@ const struct Curl_ssl Curl_ssl_rustls = {
NULL, /* disassociate_connection */
cr_recv, /* recv decrypted data */
cr_send, /* send data to encrypt */
+ NULL, /* get_channel_binding */
};
#endif /* USE_RUSTLS */
diff --git a/libs/libcurl/src/vtls/schannel.c b/libs/libcurl/src/vtls/schannel.c
index 6fef076099..d785ea2c60 100644
--- a/libs/libcurl/src/vtls/schannel.c
+++ b/libs/libcurl/src/vtls/schannel.c
@@ -968,7 +968,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
#endif
sspi_status =
- s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME,
+ Curl_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME,
SECPKG_CRED_OUTBOUND, NULL,
&credentials, NULL, NULL,
&backend->cred->cred_handle,
@@ -1015,7 +1015,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
#endif
sspi_status =
- s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME,
+ Curl_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME,
SECPKG_CRED_OUTBOUND, NULL,
&schannel_cred, NULL, NULL,
&backend->cred->cred_handle,
@@ -1083,7 +1083,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
#ifdef HAS_ALPN
/* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above.
- Also it does not seem to be supported for Wine, see curl bug #983. */
+ Also it does not seem to be supported for WINE, see curl bug #983. */
backend->use_alpn = connssl->alpn &&
!GetProcAddress(GetModuleHandle(TEXT("ntdll")),
"wine_get_version") &&
@@ -1099,7 +1099,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
* 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"
+#error "compiler too old to support Windows CE requisite manual cert verify"
#endif
#else
#ifdef HAS_MANUAL_VERIFY_API
@@ -1242,10 +1242,10 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx
At the moment we do not pass inbuf unless we are using ALPN since we only
- use it for that, and Wine (for which we currently disable ALPN) is giving
+ 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(
+ sspi_status = Curl_pSecFn->InitializeSecurityContext(
&backend->cred->cred_handle, NULL, backend->cred->sni_hostname,
backend->req_flags, 0, 0,
(backend->use_alpn ? &inbuf_desc : NULL),
@@ -1287,9 +1287,9 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
/* send initial handshake data which is now stored in output buffer */
written = Curl_conn_cf_send(cf->next, data,
- outbuf.pvBuffer, outbuf.cbBuffer,
+ outbuf.pvBuffer, outbuf.cbBuffer, FALSE,
&result);
- s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
+ Curl_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);
@@ -1436,7 +1436,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
memcpy(inbuf[0].pvBuffer, backend->encdata_buffer,
backend->encdata_offset);
- sspi_status = s_pSecFn->InitializeSecurityContext(
+ sspi_status = Curl_pSecFn->InitializeSecurityContext(
&backend->cred->cred_handle, &backend->ctxt->ctxt_handle,
backend->cred->sni_hostname, backend->req_flags,
0, 0, &inbuf_desc, 0, NULL,
@@ -1477,7 +1477,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
/* send handshake token to server */
written = Curl_conn_cf_send(cf->next, data,
outbuf[i].pvBuffer, outbuf[i].cbBuffer,
- &result);
+ FALSE, &result);
if((result != CURLE_OK) ||
(outbuf[i].cbBuffer != (size_t) written)) {
failf(data, "schannel: failed to send next handshake data: "
@@ -1488,7 +1488,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
/* free obsolete buffer */
if(outbuf[i].pvBuffer) {
- s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer);
+ Curl_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer);
}
}
}
@@ -1684,7 +1684,7 @@ static void schannel_session_free(void *sessionid, size_t idsize)
if(cred) {
cred->refcount--;
if(cred->refcount == 0) {
- s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
+ Curl_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
curlx_unicodefree(cred->sni_hostname);
#ifdef HAS_CLIENT_CERT_PATH
if(cred->client_cert_store) {
@@ -1739,7 +1739,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
#ifdef HAS_ALPN
if(backend->use_alpn) {
sspi_status =
- s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle,
+ Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle,
SECPKG_ATTR_APPLICATION_PROTOCOL,
&alpn_result);
@@ -1787,7 +1787,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
if(data->set.ssl.certinfo) {
int certs_count = 0;
sspi_status =
- s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle,
+ Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle,
SECPKG_ATTR_REMOTE_CERT_CONTEXT,
&ccert_context);
@@ -1955,7 +1955,7 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data,
/* check if the maximum stream sizes were queried */
if(backend->stream_sizes.cbMaximumMessage == 0) {
- sspi_status = s_pSecFn->QueryContextAttributes(
+ sspi_status = Curl_pSecFn->QueryContextAttributes(
&backend->ctxt->ctxt_handle,
SECPKG_ATTR_STREAM_SIZES,
&backend->stream_sizes);
@@ -1994,7 +1994,7 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data,
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,
+ sspi_status = Curl_pSecFn->EncryptMessage(&backend->ctxt->ctxt_handle, 0,
&outbuf_desc, 0);
/* check if the message was encrypted */
@@ -2054,7 +2054,7 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data,
this_write = Curl_conn_cf_send(cf->next, data,
ptr + written, len - written,
- &result);
+ FALSE, &result);
if(result == CURLE_AGAIN)
continue;
else if(result != CURLE_OK) {
@@ -2211,7 +2211,7 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
/* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx
*/
- sspi_status = s_pSecFn->DecryptMessage(&backend->ctxt->ctxt_handle,
+ sspi_status = Curl_pSecFn->DecryptMessage(&backend->ctxt->ctxt_handle,
&inbuf_desc, 0, NULL);
/* check if everything went fine (server may want to renegotiate
@@ -2498,7 +2498,7 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf,
InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut));
InitSecBufferDesc(&BuffDesc, &Buffer, 1);
- sspi_status = s_pSecFn->ApplyControlToken(&backend->ctxt->ctxt_handle,
+ sspi_status = Curl_pSecFn->ApplyControlToken(&backend->ctxt->ctxt_handle,
&BuffDesc);
if(sspi_status != SEC_E_OK) {
@@ -2513,7 +2513,7 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf,
InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
- sspi_status = s_pSecFn->InitializeSecurityContext(
+ sspi_status = Curl_pSecFn->InitializeSecurityContext(
&backend->cred->cred_handle,
&backend->ctxt->ctxt_handle,
backend->cred->sni_hostname,
@@ -2531,8 +2531,8 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf,
/* 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);
+ FALSE, &result);
+ Curl_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
if(!result) {
if(written < (ssize_t)outbuf.cbBuffer) {
/* TODO: handle partial sends */
@@ -2605,7 +2605,7 @@ static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data)
/* 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_pSecFn->DeleteSecurityContext(&backend->ctxt->ctxt_handle);
Curl_safefree(backend->ctxt);
}
@@ -2682,7 +2682,7 @@ static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
struct Curl_asn1Element *pubkey;
sspi_status =
- s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle,
+ Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle,
SECPKG_ATTR_REMOTE_CERT_CONTEXT,
&pCertContextServer);
@@ -2846,9 +2846,6 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf,
}
if(ca_info_blob) {
- if(!share->CAinfo_blob_digest) {
- return NULL;
- }
if(share->CAinfo_blob_size != ca_info_blob->len) {
return NULL;
}
@@ -2856,10 +2853,9 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf,
ca_info_blob->len,
info_blob_digest,
CURL_SHA256_DIGEST_LENGTH);
- if(memcmp(share->CAinfo_blob_digest,
- info_blob_digest,
+ if(memcmp(share->CAinfo_blob_digest, info_blob_digest,
CURL_SHA256_DIGEST_LENGTH)) {
- return NULL;
+ return NULL;
}
}
else {
@@ -2882,7 +2878,6 @@ static void schannel_cert_share_free(void *key, size_t key_len, void *p)
if(share->cert_store) {
CertCloseStore(share->cert_store, 0);
}
- free(share->CAinfo_blob_digest);
free(share->CAfile);
free(share);
}
@@ -2895,7 +2890,6 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf,
struct Curl_multi *multi = data->multi;
const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
struct schannel_cert_share *share;
- unsigned char *CAinfo_blob_digest = NULL;
size_t CAinfo_blob_size = 0;
char *CAfile = NULL;
@@ -2923,13 +2917,9 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf,
}
if(ca_info_blob) {
- CAinfo_blob_digest = malloc(CURL_SHA256_DIGEST_LENGTH);
- if(!CAinfo_blob_digest) {
- return false;
- }
schannel_sha256sum((const unsigned char *)ca_info_blob->data,
ca_info_blob->len,
- CAinfo_blob_digest,
+ share->CAinfo_blob_digest,
CURL_SHA256_DIGEST_LENGTH);
CAinfo_blob_size = ca_info_blob->len;
}
@@ -2946,12 +2936,10 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf,
if(share->cert_store) {
CertCloseStore(share->cert_store, 0);
}
- free(share->CAinfo_blob_digest);
free(share->CAfile);
share->time = Curl_now();
share->cert_store = cert_store;
- share->CAinfo_blob_digest = CAinfo_blob_digest;
share->CAinfo_blob_size = CAinfo_blob_size;
share->CAfile = CAfile;
return true;
@@ -2969,7 +2957,8 @@ const struct Curl_ssl Curl_ssl_schannel = {
#endif
SSLSUPP_TLS13_CIPHERSUITES |
SSLSUPP_CA_CACHE |
- SSLSUPP_HTTPS_PROXY,
+ SSLSUPP_HTTPS_PROXY |
+ SSLSUPP_CIPHER_LIST,
sizeof(struct schannel_ssl_backend_data),
@@ -2996,6 +2985,7 @@ const struct Curl_ssl Curl_ssl_schannel = {
NULL, /* disassociate_connection */
schannel_recv, /* recv decrypted data */
schannel_send, /* send data to encrypt */
+ NULL, /* get_channel_binding */
};
#endif /* USE_SCHANNEL */
diff --git a/libs/libcurl/src/vtls/schannel_int.h b/libs/libcurl/src/vtls/schannel_int.h
index 77105f81ab..c914ccfc6e 100644
--- a/libs/libcurl/src/vtls/schannel_int.h
+++ b/libs/libcurl/src/vtls/schannel_int.h
@@ -28,6 +28,8 @@
#ifdef USE_SCHANNEL
+#include "vtls.h"
+
#if (defined(__MINGW32__) || defined(CERT_CHAIN_REVOCATION_CHECK_CHAIN)) \
&& !defined(CURL_WINDOWS_APP)
#define HAS_MANUAL_VERIFY_API
@@ -165,7 +167,7 @@ struct schannel_ssl_backend_data {
#define MPROTO_SCHANNEL_CERT_SHARE_KEY "tls:schannel:cert:share"
struct schannel_cert_share {
- unsigned char *CAinfo_blob_digest; /* CA info blob digest */
+ unsigned char CAinfo_blob_digest[CURL_SHA256_DIGEST_LENGTH];
size_t CAinfo_blob_size; /* CA info blob size */
char *CAfile; /* CAfile path used to generate
certificate store */
diff --git a/libs/libcurl/src/vtls/schannel_verify.c b/libs/libcurl/src/vtls/schannel_verify.c
index 00b3b4d7d3..afc19baba0 100644
--- a/libs/libcurl/src/vtls/schannel_verify.c
+++ b/libs/libcurl/src/vtls/schannel_verify.c
@@ -83,7 +83,7 @@ static int is_cr_or_lf(char c)
/* Search the substring needle,needlelen into string haystack,haystacklen
* Strings do not need to be terminated by a '\0'.
- * Similar of OSX/Linux memmem (not available on Visual Studio).
+ * Similar of macOS/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,
@@ -452,7 +452,7 @@ static DWORD cert_get_name_string(struct Curl_easy *data,
}
dns_w = entry->pwszDNSName;
/* pwszDNSName is in ia5 string format and hence does not contain any
- * non-ascii characters. */
+ * non-ASCII characters. */
while(*dns_w != '\0') {
*current_pos++ = (TCHAR)(*dns_w++);
}
@@ -483,7 +483,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf,
DWORD actual_len = 0;
sspi_status =
- s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
+ Curl_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
SECPKG_ATTR_REMOTE_CERT_CONTEXT,
&pCertContextServer);
@@ -612,7 +612,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
DEBUGASSERT(BACKEND);
sspi_status =
- s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
+ Curl_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
SECPKG_ATTR_REMOTE_CERT_CONTEXT,
&pCertContextServer);
diff --git a/libs/libcurl/src/vtls/sectransp.c b/libs/libcurl/src/vtls/sectransp.c
index c79e18b95e..8ca7d4e507 100644
--- a/libs/libcurl/src/vtls/sectransp.c
+++ b/libs/libcurl/src/vtls/sectransp.c
@@ -216,7 +216,7 @@ static const uint16_t default_ciphers[] = {
#define SECTRANSP_PINNEDPUBKEY_V1 1
#endif
-/* version 2 supports MacOSX 10.7+ */
+/* version 2 supports macOS 10.7+ */
#if (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
#define SECTRANSP_PINNEDPUBKEY_V2 1
#endif
@@ -310,7 +310,8 @@ static OSStatus sectransp_bio_cf_out_write(SSLConnectionRef connection,
OSStatus rtn = noErr;
DEBUGASSERT(data);
- nwritten = Curl_conn_cf_send(cf->next, data, buf, *dataLength, &result);
+ nwritten = Curl_conn_cf_send(cf->next, data, buf, *dataLength, FALSE,
+ &result);
CURL_TRC_CF(data, cf, "bio_send(len=%zu) -> %zd, result=%d",
*dataLength, nwritten, result);
if(nwritten <= 0) {
@@ -591,7 +592,7 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath,
cPassword, kCFStringEncodingUTF8) : NULL;
CFDataRef pkcs_data = NULL;
- /* We can import P12 files on iOS or OS X 10.7 or later: */
+ /* We can import P12 files on iOS or macOS 10.7 or later: */
/* These constants are documented as having first appeared in 10.6 but they
raise linker errors when used on that cat for some reason. */
#if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS
@@ -717,134 +718,89 @@ CF_INLINE bool is_file(const char *filename)
return false;
}
-#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
-static CURLcode sectransp_version_from_curl(SSLProtocol *darwinver,
- long ssl_version)
+static CURLcode
+sectransp_set_ssl_version_min_max(struct Curl_easy *data,
+ struct st_ssl_backend_data *backend,
+ struct ssl_primary_config *conn_config)
{
- switch(ssl_version) {
+#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
+ OSStatus err;
+ SSLProtocol ver_min;
+ SSLProtocol ver_max;
+
+#if CURL_SUPPORT_MAC_10_7
+ if(!&SSLSetProtocolVersionMax)
+ goto legacy;
+#endif
+
+ switch(conn_config->version) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
case CURL_SSLVERSION_TLSv1_0:
- *darwinver = kTLSProtocol1;
- return CURLE_OK;
+ ver_min = kTLSProtocol1;
+ break;
case CURL_SSLVERSION_TLSv1_1:
- *darwinver = kTLSProtocol11;
- return CURLE_OK;
+ ver_min = kTLSProtocol11;
+ break;
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) && \
- defined(HAVE_BUILTIN_AVAILABLE)
- if(__builtin_available(macOS 10.13, iOS 11.0, *)) {
- *darwinver = kTLSProtocol13;
- return CURLE_OK;
- }
-#endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) &&
- defined(HAVE_BUILTIN_AVAILABLE) */
+ ver_min = kTLSProtocol12;
break;
+ case CURL_SSLVERSION_TLSv1_3:
+ default:
+ failf(data, "SSL: unsupported minimum TLS version value");
+ return CURLE_SSL_CONNECT_ERROR;
}
- 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 st_ssl_backend_data *backend =
- (struct st_ssl_backend_data *)connssl->backend;
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
- long ssl_version = conn_config->version;
- long ssl_version_max = conn_config->version_max;
- long max_supported_version_by_os;
-
- DEBUGASSERT(backend);
+ switch(conn_config->version_max) {
+ case CURL_SSLVERSION_MAX_DEFAULT:
+ case CURL_SSLVERSION_MAX_NONE:
+ case CURL_SSLVERSION_MAX_TLSv1_3:
+ case CURL_SSLVERSION_MAX_TLSv1_2:
+ ver_max = kTLSProtocol12;
+ break;
+ case CURL_SSLVERSION_MAX_TLSv1_1:
+ ver_max = kTLSProtocol11;
+ break;
+ case CURL_SSLVERSION_MAX_TLSv1_0:
+ ver_max = kTLSProtocol1;
+ break;
+ default:
+ failf(data, "SSL: unsupported maximum TLS version value");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
- /* 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) && \
- defined(HAVE_BUILTIN_AVAILABLE)
- if(__builtin_available(macOS 10.13, iOS 11.0, *)) {
- max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_3;
+ err = SSLSetProtocolVersionMin(backend->ssl_ctx, ver_min);
+ if(err != noErr) {
+ failf(data, "SSL: failed to set minimum TLS version");
+ return CURLE_SSL_CONNECT_ERROR;
}
- else {
- max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_2;
+ err = SSLSetProtocolVersionMax(backend->ssl_ctx, ver_max);
+ if(err != noErr) {
+ failf(data, "SSL: failed to set maximum TLS version");
+ return CURLE_SSL_CONNECT_ERROR;
}
-#else
- max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_2;
-#endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) &&
- defined(HAVE_BUILTIN_AVAILABLE) */
- switch(ssl_version) {
+ return CURLE_OK;
+#endif
+#if CURL_SUPPORT_MAC_10_7
+ goto legacy;
+legacy:
+ switch(conn_config->version) {
case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
- 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;
+ case CURL_SSLVERSION_TLSv1_0:
break;
+ default:
+ failf(data, "SSL: unsupported minimum TLS version value");
+ return CURLE_SSL_CONNECT_ERROR;
}
-#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;
- }
+ /* only TLS 1.0 is supported, disable SSL 3.0 and SSL 2.0 */
+ SSLSetProtocolVersionEnabled(backend->ssl_ctx, kSSLProtocolAll, false);
+ SSLSetProtocolVersionEnabled(backend->ssl_ctx, kTLSProtocol1, true);
- (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;
+ return CURLE_OK;
+#endif
}
static int sectransp_cipher_suite_get_str(uint16_t id, char *buf,
@@ -927,7 +883,7 @@ static SSLCipherSuite * sectransp_get_supported_ciphers(SSLContextRef ssl_ctx,
/* There is a known bug in early versions of Mountain Lion where ST's ECC
ciphers (cipher suite 0xC001 through 0xC032) simply do not work.
Work around the problem here by disabling those ciphers if we are
- running in an affected version of OS X. */
+ running in an affected version of macOS. */
if(maj == 12 && min <= 3) {
size_t i = 0, j = 0;
for(; i < *len; i++) {
@@ -1131,112 +1087,9 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
backend->ssl_write_buffered_length = 0UL; /* reset buffered write length */
- /* check to see if we have 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) && \
- defined(HAVE_BUILTIN_AVAILABLE)
- 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) &&
- defined(HAVE_BUILTIN_AVAILABLE) */
- 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:
- 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:
- 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 */
+ result = sectransp_set_ssl_version_min_max(data, backend, conn_config);
+ if(result != CURLE_OK)
+ return result;
#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && \
defined(HAVE_BUILTIN_AVAILABLE)
@@ -2255,9 +2108,6 @@ check_handshake:
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 does not need to be freed separately */
if(alpnArr)
@@ -2887,7 +2737,8 @@ const struct Curl_ssl Curl_ssl_sectransp = {
#ifdef SECTRANSP_PINNEDPUBKEY
SSLSUPP_PINNEDPUBKEY |
#endif /* SECTRANSP_PINNEDPUBKEY */
- SSLSUPP_HTTPS_PROXY,
+ SSLSUPP_HTTPS_PROXY |
+ SSLSUPP_CIPHER_LIST,
sizeof(struct st_ssl_backend_data),
@@ -2914,6 +2765,7 @@ const struct Curl_ssl Curl_ssl_sectransp = {
NULL, /* disassociate_connection */
sectransp_recv, /* recv decrypted data */
sectransp_send, /* send data to encrypt */
+ NULL, /* get_channel_binding */
};
#ifdef __GNUC__
diff --git a/libs/libcurl/src/vtls/vtls.c b/libs/libcurl/src/vtls/vtls.c
index 72fe052c56..2a0d232024 100644
--- a/libs/libcurl/src/vtls/vtls.c
+++ b/libs/libcurl/src/vtls/vtls.c
@@ -71,6 +71,7 @@
#include "connect.h"
#include "select.h"
#include "strdup.h"
+#include "rand.h"
/* The last #include files should be: */
#include "curl_memory.h"
@@ -138,6 +139,9 @@ static const struct alpn_spec ALPN_SPEC_H11 = {
{ ALPN_HTTP_1_1 }, 1
};
#ifdef USE_HTTP2
+static const struct alpn_spec ALPN_SPEC_H2 = {
+ { ALPN_H2 }, 1
+};
static const struct alpn_spec ALPN_SPEC_H2_H11 = {
{ ALPN_H2, ALPN_HTTP_1_1 }, 2
};
@@ -148,6 +152,8 @@ static const struct alpn_spec *alpn_get_spec(int httpwant, bool use_alpn)
if(!use_alpn)
return NULL;
#ifdef USE_HTTP2
+ if(httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE)
+ return &ALPN_SPEC_H2;
if(httpwant >= CURL_HTTP_VERSION_2)
return &ALPN_SPEC_H2_H11;
#else
@@ -574,10 +580,9 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
}
}
- DEBUGF(infof(data, "%s Session ID in cache for %s %s://%s:%d",
- no_match? "Did not find": "Found",
- Curl_ssl_cf_is_proxy(cf) ? "proxy" : "host",
- cf->conn->handler->scheme, peer->hostname, peer->port));
+ CURL_TRC_CF(data, cf, "%s cached session ID for %s://%s:%d",
+ no_match? "No": "Found",
+ cf->conn->handler->scheme, peer->hostname, peer->port);
return no_match;
}
@@ -745,6 +750,14 @@ out:
return CURLE_OK;
}
+CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex,
+ struct dynbuf *binding)
+{
+ if(Curl_ssl->get_channel_binding)
+ return Curl_ssl->get_channel_binding(data, sockindex, binding);
+ return CURLE_OK;
+}
+
void Curl_ssl_close_all(struct Curl_easy *data)
{
/* kill the session ID cache if not shared */
@@ -771,13 +784,13 @@ void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data,
if(sock != CURL_SOCKET_BAD) {
if(connssl->io_need & CURL_SSL_IO_NEED_SEND) {
Curl_pollset_set_out_only(data, ps, sock);
- CURL_TRC_CF(data, cf, "adjust_pollset, POLLOUT fd=%"
- CURL_FORMAT_SOCKET_T, sock);
+ CURL_TRC_CF(data, cf, "adjust_pollset, POLLOUT fd=%" FMT_SOCKET_T,
+ sock);
}
else {
Curl_pollset_set_in_only(data, ps, sock);
- CURL_TRC_CF(data, cf, "adjust_pollset, POLLIN fd=%"
- CURL_FORMAT_SOCKET_T, sock);
+ CURL_TRC_CF(data, cf, "adjust_pollset, POLLIN fd=%" FMT_SOCKET_T,
+ sock);
}
}
}
@@ -888,7 +901,9 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data,
CURLcode result = CURLE_OK;
struct dynbuf build;
- Curl_dyn_init(&build, 10000);
+ DEBUGASSERT(certnum < ci->num_of_certs);
+
+ Curl_dyn_init(&build, CURL_X509_STR_MAX);
if(Curl_dyn_add(&build, label) ||
Curl_dyn_addn(&build, ":", 1) ||
@@ -907,11 +922,16 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data,
return result;
}
+/* get 32 bits of random */
CURLcode Curl_ssl_random(struct Curl_easy *data,
unsigned char *entropy,
size_t length)
{
- return Curl_ssl->random(data, entropy, length);
+ DEBUGASSERT(length == sizeof(int));
+ if(Curl_ssl->random)
+ return Curl_ssl->random(data, entropy, length);
+ else
+ return CURLE_NOT_BUILT_IN;
}
/*
@@ -1181,16 +1201,6 @@ int Curl_none_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *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;
@@ -1317,7 +1327,7 @@ static const struct Curl_ssl Curl_ssl_multi = {
Curl_none_check_cxn, /* check_cxn */
Curl_none_shutdown, /* shutdown */
Curl_none_data_pending, /* data_pending */
- Curl_none_random, /* random */
+ NULL, /* random */
Curl_none_cert_status_request, /* cert_status_request */
multissl_connect, /* connect */
multissl_connect_nonblocking, /* connect_nonblocking */
@@ -1334,6 +1344,7 @@ static const struct Curl_ssl Curl_ssl_multi = {
NULL, /* disassociate_connection */
multissl_recv_plain, /* recv decrypted data */
multissl_send_plain, /* send data to encrypt */
+ NULL, /* get_channel_binding */
};
const struct Curl_ssl *Curl_ssl =
@@ -1567,69 +1578,70 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf,
int transport)
{
const char *ehostname, *edispname;
- int eport;
+ CURLcode result = CURLE_OUT_OF_MEMORY;
+ /* We expect a clean struct, e.g. called only ONCE */
+ DEBUGASSERT(peer);
+ DEBUGASSERT(!peer->hostname);
+ DEBUGASSERT(!peer->dispname);
+ DEBUGASSERT(!peer->sni);
/* We need the hostname for SNI negotiation. Once handshaked, this remains
* the SNI hostname for the TLS connection. When the connection is reused,
* the settings in cf->conn might change. We keep a copy of the hostname we
* use for SNI.
*/
+ peer->transport = transport;
#ifndef CURL_DISABLE_PROXY
if(Curl_ssl_cf_is_proxy(cf)) {
ehostname = cf->conn->http_proxy.host.name;
edispname = cf->conn->http_proxy.host.dispname;
- eport = cf->conn->http_proxy.port;
+ peer->port = cf->conn->http_proxy.port;
}
else
#endif
{
ehostname = cf->conn->host.name;
edispname = cf->conn->host.dispname;
- eport = cf->conn->remote_port;
+ peer->port = cf->conn->remote_port;
}
- /* change if ehostname changed */
- DEBUGASSERT(!ehostname || ehostname[0]);
- if(ehostname && (!peer->hostname
- || strcmp(ehostname, peer->hostname))) {
- Curl_ssl_peer_cleanup(peer);
- peer->hostname = strdup(ehostname);
- if(!peer->hostname) {
- Curl_ssl_peer_cleanup(peer);
- return CURLE_OUT_OF_MEMORY;
- }
- if(!edispname || !strcmp(ehostname, edispname))
- peer->dispname = peer->hostname;
- else {
- peer->dispname = strdup(edispname);
- if(!peer->dispname) {
- Curl_ssl_peer_cleanup(peer);
- return CURLE_OUT_OF_MEMORY;
- }
- }
+ /* hostname MUST exist and not be empty */
+ if(!ehostname || !ehostname[0]) {
+ result = CURLE_FAILED_INIT;
+ goto out;
+ }
- peer->type = get_peer_type(peer->hostname);
- if(peer->type == CURL_SSL_PEER_DNS && peer->hostname[0]) {
- /* not an IP address, normalize according to RCC 6066 ch. 3,
- * max len of SNI is 2^16-1, no trailing dot */
- size_t len = strlen(peer->hostname);
- if(len && (peer->hostname[len-1] == '.'))
- len--;
- if(len < USHRT_MAX) {
- peer->sni = calloc(1, len + 1);
- if(!peer->sni) {
- Curl_ssl_peer_cleanup(peer);
- return CURLE_OUT_OF_MEMORY;
- }
- Curl_strntolower(peer->sni, peer->hostname, len);
- peer->sni[len] = 0;
- }
+ peer->hostname = strdup(ehostname);
+ if(!peer->hostname)
+ goto out;
+ if(!edispname || !strcmp(ehostname, edispname))
+ peer->dispname = peer->hostname;
+ else {
+ peer->dispname = strdup(edispname);
+ if(!peer->dispname)
+ goto out;
+ }
+ peer->type = get_peer_type(peer->hostname);
+ if(peer->type == CURL_SSL_PEER_DNS) {
+ /* not an IP address, normalize according to RCC 6066 ch. 3,
+ * max len of SNI is 2^16-1, no trailing dot */
+ size_t len = strlen(peer->hostname);
+ if(len && (peer->hostname[len-1] == '.'))
+ len--;
+ if(len < USHRT_MAX) {
+ peer->sni = calloc(1, len + 1);
+ if(!peer->sni)
+ goto out;
+ Curl_strntolower(peer->sni, peer->hostname, len);
+ peer->sni[len] = 0;
}
-
}
- peer->port = eport;
- peer->transport = transport;
- return CURLE_OK;
+ result = CURLE_OK;
+
+out:
+ if(result)
+ Curl_ssl_peer_cleanup(peer);
+ return result;
}
static void ssl_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
@@ -1668,22 +1680,29 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
return CURLE_OK;
}
+ if(!cf->next) {
+ *done = FALSE;
+ return CURLE_FAILED_INIT;
+ }
+
+ if(!cf->next->connected) {
+ result = cf->next->cft->do_connect(cf->next, data, blocking, done);
+ if(result || !*done)
+ return result;
+ }
+
CF_DATA_SAVE(save, cf, data);
CURL_TRC_CF(data, cf, "cf_connect()");
- (void)connssl;
DEBUGASSERT(data->conn);
DEBUGASSERT(data->conn == cf->conn);
DEBUGASSERT(connssl);
- DEBUGASSERT(cf->conn->host.name);
-
- result = cf->next->cft->do_connect(cf->next, data, blocking, done);
- if(result || !*done)
- goto out;
*done = FALSE;
- result = Curl_ssl_peer_init(&connssl->peer, cf, TRNSPRT_TCP);
- if(result)
- goto out;
+ if(!connssl->peer.hostname) {
+ result = Curl_ssl_peer_init(&connssl->peer, cf, TRNSPRT_TCP);
+ if(result)
+ goto out;
+ }
if(blocking) {
result = ssl_connect(cf, data);
@@ -1721,11 +1740,12 @@ static bool ssl_cf_data_pending(struct Curl_cfilter *cf,
static ssize_t ssl_cf_send(struct Curl_cfilter *cf,
struct Curl_easy *data, const void *buf, size_t len,
- CURLcode *err)
+ bool eos, CURLcode *err)
{
struct cf_call_data save;
ssize_t nwritten;
+ (void)eos; /* unused */
CF_DATA_SAVE(save, cf, data);
*err = CURLE_OK;
nwritten = Curl_ssl->send_plain(cf, data, buf, len, err);
@@ -2200,7 +2220,6 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
const unsigned char *proto,
size_t proto_len)
{
- int can_multi = 0;
unsigned char *palpn =
#ifndef CURL_DISABLE_PROXY
(cf->conn->bits.tunnel_proxy && Curl_ssl_cf_is_proxy(cf))?
@@ -2219,14 +2238,12 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
else if(proto_len == ALPN_H2_LENGTH &&
!memcmp(ALPN_H2, proto, ALPN_H2_LENGTH)) {
*palpn = CURL_HTTP_VERSION_2;
- can_multi = 1;
}
#endif
#ifdef USE_HTTP3
else if(proto_len == ALPN_H3_LENGTH &&
!memcmp(ALPN_H3, proto, ALPN_H3_LENGTH)) {
*palpn = CURL_HTTP_VERSION_3;
- can_multi = 1;
}
#endif
else {
@@ -2245,9 +2262,6 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
}
out:
- if(!Curl_ssl_cf_is_proxy(cf))
- Curl_multiuse_state(data, can_multi?
- BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
return CURLE_OK;
}
diff --git a/libs/libcurl/src/vtls/vtls.h b/libs/libcurl/src/vtls/vtls.h
index c81b048a68..10c78c386e 100644
--- a/libs/libcurl/src/vtls/vtls.h
+++ b/libs/libcurl/src/vtls/vtls.h
@@ -39,6 +39,7 @@ struct Curl_ssl_session;
#define SSLSUPP_CAINFO_BLOB (1<<6)
#define SSLSUPP_ECH (1<<7)
#define SSLSUPP_CA_CACHE (1<<8)
+#define SSLSUPP_CIPHER_LIST (1<<9) /* supports TLS 1.0-1.2 ciphersuites */
#define ALPN_ACCEPTED "ALPN: server accepted "
@@ -131,6 +132,7 @@ CURLcode Curl_ssl_initsessions(struct Curl_easy *, size_t);
void Curl_ssl_version(char *buffer, size_t size);
/* Certificate information list handling. */
+#define CURL_X509_STR_MAX 100000
void Curl_ssl_free_certinfo(struct Curl_easy *data);
CURLcode Curl_ssl_init_certinfo(struct Curl_easy *data, int num);
@@ -181,6 +183,25 @@ bool Curl_ssl_cert_status_request(void);
bool Curl_ssl_false_start(struct Curl_easy *data);
+/* The maximum size of the SSL channel binding is 85 bytes, as defined in
+ * RFC 5929, Section 4.1. The 'tls-server-end-point:' prefix is 21 bytes long,
+ * and SHA-512 is the longest supported hash algorithm, with a digest length of
+ * 64 bytes.
+ * The maximum size of the channel binding is therefore 21 + 64 = 85 bytes.
+ */
+#define SSL_CB_MAX_SIZE 85
+
+/* Return the tls-server-end-point channel binding, including the
+ * 'tls-server-end-point:' prefix.
+ * If successful, the data is written to the dynbuf, and CURLE_OK is returned.
+ * The dynbuf MUST HAVE a minimum toobig size of SSL_CB_MAX_SIZE.
+ * If the dynbuf is too small, CURLE_OUT_OF_MEMORY is returned.
+ * If channel binding is not supported, binding stays empty and CURLE_OK is
+ * returned.
+ */
+CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex,
+ struct dynbuf *binding);
+
#define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */
CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data,
diff --git a/libs/libcurl/src/vtls/vtls_int.h b/libs/libcurl/src/vtls/vtls_int.h
index 7fc93c0ebe..6cd2867dee 100644
--- a/libs/libcurl/src/vtls/vtls_int.h
+++ b/libs/libcurl/src/vtls/vtls_int.h
@@ -158,6 +158,9 @@ struct Curl_ssl {
ssize_t (*send_plain)(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *mem, size_t len, CURLcode *code);
+ CURLcode (*get_channel_binding)(struct Curl_easy *data, int sockindex,
+ struct dynbuf *binding);
+
};
extern const struct Curl_ssl *Curl_ssl;
@@ -168,8 +171,6 @@ void Curl_none_cleanup(void);
CURLcode Curl_none_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data,
bool send_shutdown, bool *done);
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,
@@ -222,7 +223,7 @@ CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf,
#include "sectransp.h" /* SecureTransport (Darwin) version */
#include "mbedtls.h" /* mbedTLS versions */
#include "bearssl.h" /* BearSSL versions */
-#include "rustls.h" /* rustls versions */
+#include "rustls.h" /* Rustls versions */
#endif /* USE_SSL */
diff --git a/libs/libcurl/src/vtls/wolfssl.c b/libs/libcurl/src/vtls/wolfssl.c
index d9a8e84325..458219ef61 100644
--- a/libs/libcurl/src/vtls/wolfssl.c
+++ b/libs/libcurl/src/vtls/wolfssl.c
@@ -36,6 +36,10 @@
#include <wolfssl/version.h>
#include <wolfssl/options.h>
+#if LIBWOLFSSL_VERSION_HEX < 0x03004006 /* wolfSSL 3.4.6 (2015) */
+#error "wolfSSL version should be at least 3.4.6"
+#endif
+
/* 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
@@ -201,7 +205,7 @@ wolfssl_log_tls12_secret(SSL *ssl)
}
#endif /* OPENSSL_EXTRA */
-static int do_file_type(const char *type)
+static int wolfssl_do_file_type(const char *type)
{
if(!type || !type[0])
return SSL_FILETYPE_PEM;
@@ -287,17 +291,32 @@ static int wolfssl_bio_cf_out_write(WOLFSSL_BIO *bio,
struct wolfssl_ctx *backend =
(struct wolfssl_ctx *)connssl->backend;
struct Curl_easy *data = CF_DATA_CURRENT(cf);
- ssize_t nwritten;
+ ssize_t nwritten, skiplen = 0;
CURLcode result = CURLE_OK;
DEBUGASSERT(data);
- nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
+ if(backend->shutting_down && backend->io_send_blocked_len &&
+ (backend->io_send_blocked_len < blen)) {
+ /* bug in wolfSSL: <https://github.com/wolfSSL/wolfssl/issues/7784>
+ * It adds the close notify message again every time we retry
+ * sending during shutdown. */
+ CURL_TRC_CF(data, cf, "bio_write, shutdown restrict send of %d"
+ " to %d bytes", blen, backend->io_send_blocked_len);
+ skiplen = (ssize_t)(blen - backend->io_send_blocked_len);
+ blen = backend->io_send_blocked_len;
+ }
+ nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &result);
backend->io_result = result;
CURL_TRC_CF(data, cf, "bio_write(len=%d) -> %zd, %d",
blen, nwritten, result);
wolfSSL_BIO_clear_retry_flags(bio);
- if(nwritten < 0 && CURLE_AGAIN == result)
+ if(nwritten < 0 && CURLE_AGAIN == result) {
BIO_set_retry_write(bio);
+ if(backend->shutting_down && !backend->io_send_blocked_len)
+ backend->io_send_blocked_len = blen;
+ }
+ else if(!result && skiplen)
+ nwritten += skiplen;
return (int)nwritten;
}
@@ -581,7 +600,10 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
!ssl_config->native_ca_store;
cached_store = cache_criteria_met ? get_cached_x509_store(cf, data) : NULL;
- if(cached_store && wolfSSL_X509_STORE_up_ref(cached_store)) {
+ if(cached_store && wolfSSL_CTX_get_cert_store(wssl->ctx) == cached_store) {
+ /* The cached store is already in use, do nothing. */
+ }
+ else if(cached_store && wolfSSL_X509_STORE_up_ref(cached_store)) {
wolfSSL_CTX_set_cert_store(wssl->ctx, cached_store);
}
else if(cache_criteria_met) {
@@ -608,6 +630,75 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
return result;
}
+#ifdef WOLFSSL_TLS13
+static size_t
+wssl_get_default_ciphers(bool tls13, char *buf, size_t size)
+{
+ size_t len = 0;
+ char *term = buf;
+ int i;
+ char *str;
+ size_t n;
+
+ for(i = 0; (str = wolfSSL_get_cipher_list(i)); i++) {
+ if((strncmp(str, "TLS13", 5) == 0) != tls13)
+ continue;
+
+ n = strlen(str);
+ if(buf && len + n + 1 <= size) {
+ memcpy(buf + len, str, n);
+ term = buf + len + n;
+ *term = ':';
+ }
+ len += n + 1;
+ }
+
+ if(buf)
+ *term = '\0';
+
+ return len > 0 ? len - 1 : 0;
+}
+#endif
+
+#if LIBWOLFSSL_VERSION_HEX < 0x04002000 /* 4.2.0 (2019) */
+static int
+wssl_legacy_CTX_set_min_proto_version(WOLFSSL_CTX* ctx, int version)
+{
+ int res;
+ switch(version) {
+ default:
+ case TLS1_VERSION:
+ res = wolfSSL_CTX_SetMinVersion(ctx, WOLFSSL_TLSV1);
+ if(res == WOLFSSL_SUCCESS)
+ return res;
+ FALLTHROUGH();
+ case TLS1_1_VERSION:
+ res = wolfSSL_CTX_SetMinVersion(ctx, WOLFSSL_TLSV1_1);
+ if(res == WOLFSSL_SUCCESS)
+ return res;
+ FALLTHROUGH();
+ case TLS1_2_VERSION:
+ res = wolfSSL_CTX_SetMinVersion(ctx, WOLFSSL_TLSV1_2);
+#ifdef WOLFSSL_TLS13
+ if(res == WOLFSSL_SUCCESS)
+ return res;
+ FALLTHROUGH();
+ case TLS1_3_VERSION:
+ res = wolfSSL_CTX_SetMinVersion(ctx, WOLFSSL_TLSV1_3);
+#endif
+ }
+ return res;
+}
+static int
+wssl_legacy_CTX_set_max_proto_version(WOLFSSL_CTX* ctx, int version)
+{
+ (void) ctx, (void) version;
+ return WOLFSSL_NOT_IMPLEMENTED;
+}
+#define wolfSSL_CTX_set_min_proto_version wssl_legacy_CTX_set_min_proto_version
+#define wolfSSL_CTX_set_max_proto_version wssl_legacy_CTX_set_max_proto_version
+#endif
+
/*
* This function loads all the client/CA certificates and CRLs. Setup the TLS
* layer and do all necessary magic.
@@ -615,6 +706,7 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
static CURLcode
wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
{
+ int res;
char *ciphers, *curves;
struct ssl_connect_data *connssl = cf->ctx;
struct wolfssl_ctx *backend =
@@ -626,118 +718,86 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
word16 pqkem = 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;
+#if LIBWOLFSSL_VERSION_HEX < 0x04002000 /* 4.2.0 (2019) */
+ req_method = wolfSSLv23_client_method();
+#else
+ req_method = wolfTLS_client_method();
+#endif
+ if(!req_method) {
+ failf(data, "wolfSSL: could not create a client method");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(backend->ctx)
+ wolfSSL_CTX_free(backend->ctx);
+
+ backend->ctx = wolfSSL_CTX_new(req_method);
+ if(!backend->ctx) {
+ failf(data, "wolfSSL: could not create a context");
+ return CURLE_OUT_OF_MEMORY;
}
- /* check to see if we have 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);
+ res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_VERSION);
break;
-#else
- failf(data, "wolfSSL does not support TLS 1.0");
- return CURLE_NOT_BUILT_IN;
-#endif
case CURL_SSLVERSION_TLSv1_1:
-#ifndef NO_OLD_TLS
- req_method = TLSv1_1_client_method();
- use_sni(TRUE);
+ res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_1_VERSION);
break;
-#else
- failf(data, "wolfSSL does not support TLS 1.1");
- return CURLE_NOT_BUILT_IN;
-#endif
case CURL_SSLVERSION_TLSv1_2:
-#ifndef WOLFSSL_NO_TLS12
- req_method = TLSv1_2_client_method();
- use_sni(TRUE);
-#else
- failf(data, "wolfSSL does not support TLS 1.2");
- return CURLE_NOT_BUILT_IN;
-#endif
+ res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_2_VERSION);
break;
- case CURL_SSLVERSION_TLSv1_3:
#ifdef WOLFSSL_TLS13
- req_method = wolfTLSv1_3_client_method();
- use_sni(TRUE);
+ case CURL_SSLVERSION_TLSv1_3:
+ res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_3_VERSION);
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");
+ failf(data, "wolfSSL: unsupported minimum TLS version value");
return CURLE_SSL_CONNECT_ERROR;
}
-
- if(!req_method) {
- failf(data, "SSL: could not create a method");
- return CURLE_OUT_OF_MEMORY;
- }
-
- if(backend->ctx)
- wolfSSL_CTX_free(backend->ctx);
- backend->ctx = wolfSSL_CTX_new(req_method);
-
- if(!backend->ctx) {
- failf(data, "SSL: could not create a context");
- return CURLE_OUT_OF_MEMORY;
+ if(res != WOLFSSL_SUCCESS) {
+ failf(data, "wolfSSL: failed set the minimum TLS version");
+ return CURLE_SSL_CONNECT_ERROR;
}
- 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)
+ switch(conn_config->version_max) {
#ifdef WOLFSSL_TLS13
- && (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_3) != 1)
-#endif
- ) {
- failf(data, "SSL: could not set the minimum protocol version");
- return CURLE_SSL_CONNECT_ERROR;
- }
+ case CURL_SSLVERSION_MAX_TLSv1_3:
+ res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION);
+ break;
#endif
- FALLTHROUGH();
- default:
+ case CURL_SSLVERSION_MAX_TLSv1_2:
+ res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_2_VERSION);
+ break;
+ case CURL_SSLVERSION_MAX_TLSv1_1:
+ res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_1_VERSION);
+ break;
+ case CURL_SSLVERSION_MAX_TLSv1_0:
+ res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_VERSION);
break;
+ case CURL_SSLVERSION_MAX_DEFAULT:
+ case CURL_SSLVERSION_MAX_NONE:
+ res = WOLFSSL_SUCCESS;
+ break;
+ default:
+ failf(data, "wolfSSL: unsupported maximum TLS version value");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ if(res != WOLFSSL_SUCCESS) {
+ failf(data, "wolfSSL: failed set the maximum TLS version");
+ return CURLE_SSL_CONNECT_ERROR;
}
+#ifndef WOLFSSL_TLS13
ciphers = conn_config->cipher_list;
if(ciphers) {
if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) {
@@ -746,6 +806,44 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
}
infof(data, "Cipher selection: %s", ciphers);
}
+#else
+ if(conn_config->cipher_list || conn_config->cipher_list13) {
+ const char *ciphers12 = conn_config->cipher_list;
+ const char *ciphers13 = conn_config->cipher_list13;
+
+ /* Set ciphers to a combination of ciphers_list and ciphers_list13.
+ * If cipher_list is not set use the default TLSv1.2 (1.1, 1.0) ciphers.
+ * If cipher_list13 is not set use the default TLSv1.3 ciphers. */
+ size_t len13 = ciphers13 ? strlen(ciphers13)
+ : wssl_get_default_ciphers(true, NULL, 0);
+ size_t len12 = ciphers12 ? strlen(ciphers12)
+ : wssl_get_default_ciphers(false, NULL, 0);
+
+ ciphers = malloc(len13 + 1 + len12 + 1);
+ if(!ciphers)
+ return CURLE_OUT_OF_MEMORY;
+
+ if(ciphers13)
+ memcpy(ciphers, ciphers13, len13);
+ else
+ wssl_get_default_ciphers(true, ciphers, len13 + 1);
+ ciphers[len13] = ':';
+
+ if(ciphers12)
+ memcpy(ciphers + len13 + 1, ciphers12, len12);
+ else
+ wssl_get_default_ciphers(false, ciphers + len13 + 1, len12 + 1);
+ ciphers[len13 + 1 + len12] = '\0';
+
+ if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) {
+ failf(data, "failed setting cipher list: %s", ciphers);
+ free(ciphers);
+ return CURLE_SSL_CIPHER;
+ }
+ infof(data, "Cipher selection: %s", ciphers);
+ free(ciphers);
+ }
+#endif
curves = conn_config->curves;
if(curves) {
@@ -768,40 +866,89 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
}
}
-#ifndef NO_FILESYSTEM
/* Load the client certificate, and private key */
- if(ssl_config->primary.clientcert) {
- char *key_file = ssl_config->key;
- int file_type = do_file_type(ssl_config->cert_type);
-
- if(file_type == WOLFSSL_FILETYPE_PEM) {
- if(wolfSSL_CTX_use_certificate_chain_file(backend->ctx,
- ssl_config->primary.clientcert)
- != 1) {
- failf(data, "unable to use client certificate");
- return CURLE_SSL_CONNECT_ERROR;
- }
+#ifndef NO_FILESYSTEM
+ if(ssl_config->primary.cert_blob || ssl_config->primary.clientcert) {
+ const char *cert_file = ssl_config->primary.clientcert;
+ const char *key_file = ssl_config->key;
+ const struct curl_blob *cert_blob = ssl_config->primary.cert_blob;
+ const struct curl_blob *key_blob = ssl_config->key_blob;
+ int file_type = wolfssl_do_file_type(ssl_config->cert_type);
+ int rc;
+
+ switch(file_type) {
+ case WOLFSSL_FILETYPE_PEM:
+ rc = cert_blob ?
+ wolfSSL_CTX_use_certificate_chain_buffer(backend->ctx,
+ cert_blob->data,
+ (long)cert_blob->len) :
+ wolfSSL_CTX_use_certificate_chain_file(backend->ctx, cert_file);
+ break;
+ case WOLFSSL_FILETYPE_ASN1:
+ rc = cert_blob ?
+ wolfSSL_CTX_use_certificate_buffer(backend->ctx, cert_blob->data,
+ (long)cert_blob->len, file_type) :
+ wolfSSL_CTX_use_certificate_file(backend->ctx, cert_file, file_type);
+ break;
+ default:
+ failf(data, "unknown cert type");
+ return CURLE_BAD_FUNCTION_ARGUMENT;
}
- else if(file_type == WOLFSSL_FILETYPE_ASN1) {
- if(wolfSSL_CTX_use_certificate_file(backend->ctx,
- ssl_config->primary.clientcert,
- file_type) != 1) {
- failf(data, "unable to use client certificate");
- return CURLE_SSL_CONNECT_ERROR;
- }
+ if(rc != 1) {
+ failf(data, "unable to use client certificate");
+ return CURLE_SSL_CONNECT_ERROR;
}
- else {
+
+ if(!key_blob && !key_file) {
+ key_blob = cert_blob;
+ key_file = cert_file;
+ }
+ else
+ file_type = wolfssl_do_file_type(ssl_config->key_type);
+
+ rc = key_blob ?
+ wolfSSL_CTX_use_PrivateKey_buffer(backend->ctx, key_blob->data,
+ (long)key_blob->len, file_type) :
+ wolfSSL_CTX_use_PrivateKey_file(backend->ctx, key_file, file_type);
+ if(rc != 1) {
+ failf(data, "unable to set private key");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+#else /* NO_FILESYSTEM */
+ if(ssl_config->primary.cert_blob) {
+ const struct curl_blob *cert_blob = ssl_config->primary.cert_blob;
+ const struct curl_blob *key_blob = ssl_config->key_blob;
+ int file_type = wolfssl_do_file_type(ssl_config->cert_type);
+ int rc;
+
+ switch(file_type) {
+ case WOLFSSL_FILETYPE_PEM:
+ rc = wolfSSL_CTX_use_certificate_chain_buffer(backend->ctx,
+ cert_blob->data,
+ (long)cert_blob->len);
+ break;
+ case WOLFSSL_FILETYPE_ASN1:
+ rc = wolfSSL_CTX_use_certificate_buffer(backend->ctx, cert_blob->data,
+ (long)cert_blob->len, file_type);
+ break;
+ default:
failf(data, "unknown cert type");
return CURLE_BAD_FUNCTION_ARGUMENT;
}
+ if(rc != 1) {
+ failf(data, "unable to use client certificate");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
- if(!key_file)
- key_file = ssl_config->primary.clientcert;
+ if(!key_blob)
+ key_blob = cert_blob;
else
- file_type = do_file_type(ssl_config->key_type);
+ file_type = wolfssl_do_file_type(ssl_config->key_type);
- if(wolfSSL_CTX_use_PrivateKey_file(backend->ctx, key_file,
- file_type) != 1) {
+ if(wolfSSL_CTX_use_PrivateKey_buffer(backend->ctx, key_blob->data,
+ (long)key_blob->len,
+ file_type) != 1) {
failf(data, "unable to set private key");
return CURLE_SSL_CONNECT_ERROR;
}
@@ -817,7 +964,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
SSL_VERIFY_NONE, NULL);
#ifdef HAVE_SNI
- if(sni && connssl->peer.sni) {
+ if(connssl->peer.sni) {
size_t sni_len = strlen(connssl->peer.sni);
if((sni_len < USHRT_MAX)) {
if(wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME,
@@ -988,7 +1135,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
if(data->set.tls_ech & CURLECH_HARD)
return CURLE_SSL_CONNECT_ERROR;
}
- Curl_resolv_unlock(data, dns);
+ Curl_resolv_unlink(data, &dns);
}
}
@@ -1029,15 +1176,15 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
static char *wolfssl_strerror(unsigned long error, char *buf,
unsigned long size)
{
- DEBUGASSERT(size);
+ DEBUGASSERT(size > 40);
*buf = '\0';
wolfSSL_ERR_error_string_n(error, buf, size);
if(!*buf) {
const char *msg = error ? "Unknown error" : "No error";
- strncpy(buf, msg, size - 1);
- buf[size - 1] = '\0';
+ /* the string fits because the assert above assures this */
+ strcpy(buf, msg);
}
return buf;
@@ -1148,7 +1295,6 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
}
#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");
@@ -1161,7 +1307,14 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
"continuing anyway");
}
}
-#endif
+ else if(ASN_AFTER_DATE_E == detail) {
+ failf(data, "server verification failed: certificate has expired.");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else if(ASN_BEFORE_DATE_E == detail) {
+ failf(data, "server verification failed: certificate not valid yet.");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
#ifdef USE_ECH
else if(-1 == detail) {
/* try access a retry_config ECHConfigList for tracing */
@@ -1379,7 +1532,10 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
struct wolfssl_ctx *wctx = (struct wolfssl_ctx *)connssl->backend;
CURLcode result = CURLE_OK;
char buf[1024];
- int nread, err;
+ char error_buffer[256];
+ int nread = -1, err;
+ size_t i;
+ int detail;
DEBUGASSERT(wctx);
if(!wctx->handle || cf->shutdown) {
@@ -1387,6 +1543,7 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
goto out;
}
+ wctx->shutting_down = TRUE;
connssl->io_need = CURL_SSL_IO_NEED_NONE;
*done = FALSE;
if(!(wolfSSL_get_shutdown(wctx->handle) & SSL_SENT_SHUTDOWN)) {
@@ -1395,6 +1552,7 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
ERR_clear_error();
nread = wolfSSL_read(wctx->handle, buf, (int)sizeof(buf));
err = wolfSSL_get_error(wctx->handle, nread);
+ CURL_TRC_CF(data, cf, "wolfSSL_read, nread=%d, err=%d", nread, err);
if(!nread && err == SSL_ERROR_ZERO_RETURN) {
bool input_pending;
/* Yes, it did. */
@@ -1415,49 +1573,55 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
}
}
- if(send_shutdown && wolfSSL_shutdown(wctx->handle) == 1) {
- CURL_TRC_CF(data, cf, "SSL shutdown finished");
- *done = TRUE;
- goto out;
- }
- else {
- size_t i;
- /* SSL should now have started the shutdown from our side. Since it
- * was not complete, we are lacking the close notify from the server. */
- for(i = 0; i < 10; ++i) {
- ERR_clear_error();
- nread = wolfSSL_read(wctx->handle, buf, (int)sizeof(buf));
- if(nread <= 0)
- break;
- }
- err = wolfSSL_get_error(wctx->handle, nread);
- switch(err) {
- case SSL_ERROR_ZERO_RETURN: /* no more data */
- CURL_TRC_CF(data, cf, "SSL shutdown received");
+ /* SSL should now have started the shutdown from our side. Since it
+ * was not complete, we are lacking the close notify from the server. */
+ if(send_shutdown) {
+ ERR_clear_error();
+ if(wolfSSL_shutdown(wctx->handle) == 1) {
+ CURL_TRC_CF(data, cf, "SSL shutdown finished");
*done = TRUE;
- break;
- case SSL_ERROR_NONE: /* just did not get anything */
- case SSL_ERROR_WANT_READ:
- /* SSL has send its notify and now wants to read the reply
- * from the server. We are not really interested in that. */
- CURL_TRC_CF(data, cf, "SSL shutdown sent, want receive");
- connssl->io_need = CURL_SSL_IO_NEED_RECV;
- break;
- case SSL_ERROR_WANT_WRITE:
- CURL_TRC_CF(data, cf, "SSL shutdown send blocked");
- connssl->io_need = CURL_SSL_IO_NEED_SEND;
- break;
- default: {
- char error_buffer[256];
- int detail = wolfSSL_get_error(wctx->handle, err);
- CURL_TRC_CF(data, cf, "SSL shutdown, error: '%s'(%d)",
- wolfssl_strerror((unsigned long)err, error_buffer,
- sizeof(error_buffer)),
- detail);
- result = CURLE_RECV_ERROR;
- break;
+ goto out;
}
+ if(SSL_ERROR_WANT_WRITE == wolfSSL_get_error(wctx->handle, nread)) {
+ CURL_TRC_CF(data, cf, "SSL shutdown still wants to send");
+ connssl->io_need = CURL_SSL_IO_NEED_SEND;
+ goto out;
}
+ /* Having sent the close notify, we use wolfSSL_read() to get the
+ * missing close notify from the server. */
+ }
+
+ for(i = 0; i < 10; ++i) {
+ ERR_clear_error();
+ nread = wolfSSL_read(wctx->handle, buf, (int)sizeof(buf));
+ if(nread <= 0)
+ break;
+ }
+ err = wolfSSL_get_error(wctx->handle, nread);
+ switch(err) {
+ case SSL_ERROR_ZERO_RETURN: /* no more data */
+ CURL_TRC_CF(data, cf, "SSL shutdown received");
+ *done = TRUE;
+ break;
+ case SSL_ERROR_NONE: /* just did not get anything */
+ case SSL_ERROR_WANT_READ:
+ /* SSL has send its notify and now wants to read the reply
+ * from the server. We are not really interested in that. */
+ CURL_TRC_CF(data, cf, "SSL shutdown sent, want receive");
+ connssl->io_need = CURL_SSL_IO_NEED_RECV;
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ CURL_TRC_CF(data, cf, "SSL shutdown send blocked");
+ connssl->io_need = CURL_SSL_IO_NEED_SEND;
+ break;
+ default:
+ detail = wolfSSL_get_error(wctx->handle, err);
+ CURL_TRC_CF(data, cf, "SSL shutdown, error: '%s'(%d)",
+ wolfssl_strerror((unsigned long)err, error_buffer,
+ sizeof(error_buffer)),
+ detail);
+ result = CURLE_RECV_ERROR;
+ break;
}
out:
@@ -1771,7 +1935,11 @@ const struct Curl_ssl Curl_ssl_wolfssl = {
SSLSUPP_ECH |
#endif
SSLSUPP_SSL_CTX |
- SSLSUPP_CA_CACHE,
+#ifdef WOLFSSL_TLS13
+ SSLSUPP_TLS13_CIPHERSUITES |
+#endif
+ SSLSUPP_CA_CACHE |
+ SSLSUPP_CIPHER_LIST,
sizeof(struct wolfssl_ctx),
@@ -1798,6 +1966,7 @@ const struct Curl_ssl Curl_ssl_wolfssl = {
NULL, /* disassociate_connection */
wolfssl_recv, /* recv decrypted data */
wolfssl_send, /* send data to encrypt */
+ NULL, /* get_channel_binding */
};
#endif
diff --git a/libs/libcurl/src/vtls/wolfssl.h b/libs/libcurl/src/vtls/wolfssl.h
index fcc1b6a625..0e0ffb6e70 100644
--- a/libs/libcurl/src/vtls/wolfssl.h
+++ b/libs/libcurl/src/vtls/wolfssl.h
@@ -39,7 +39,9 @@ struct wolfssl_ctx {
WOLFSSL_CTX *ctx;
WOLFSSL *handle;
CURLcode io_result; /* result of last BIO cfilter operation */
+ int io_send_blocked_len; /* length of last BIO write that EAGAINed */
BIT(x509_store_setup); /* x509 store has been set up */
+ BIT(shutting_down); /* TLS is being shut down */
};
CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
diff --git a/libs/libcurl/src/vtls/x509asn1.c b/libs/libcurl/src/vtls/x509asn1.c
index 5d90c45067..e92449fb2e 100644
--- a/libs/libcurl/src/vtls/x509asn1.c
+++ b/libs/libcurl/src/vtls/x509asn1.c
@@ -99,10 +99,6 @@
#define CURL_ASN1_CHARACTER_STRING 29
#define CURL_ASN1_BMP_STRING 30
-/* Max sixes */
-
-#define MAX_X509_STR 10000
-#define MAX_X509_CERT 100000
#ifdef WANT_EXTRACT_CERTINFO
/* ASN.1 OID table entry. */
@@ -463,7 +459,7 @@ static CURLcode OID2str(struct dynbuf *store,
if(beg < end) {
if(symbolic) {
struct dynbuf buf;
- Curl_dyn_init(&buf, MAX_X509_STR);
+ Curl_dyn_init(&buf, CURL_X509_STR_MAX);
result = encodeOID(&buf, beg, end);
if(!result) {
@@ -685,7 +681,7 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
CURLcode result = CURLE_OK;
bool added = FALSE;
struct dynbuf temp;
- Curl_dyn_init(&temp, MAX_X509_STR);
+ Curl_dyn_init(&temp, CURL_X509_STR_MAX);
for(p1 = dn->beg; p1 < dn->end;) {
p1 = getASN1Element(&rdn, p1, dn->end);
@@ -949,7 +945,7 @@ static CURLcode do_pubkey_field(struct Curl_easy *data, int certnum,
CURLcode result;
struct dynbuf out;
- Curl_dyn_init(&out, MAX_X509_STR);
+ Curl_dyn_init(&out, CURL_X509_STR_MAX);
/* Generate a certificate information record for the public key. */
@@ -1093,7 +1089,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
if(certnum)
return CURLE_OK;
- Curl_dyn_init(&out, MAX_X509_STR);
+ Curl_dyn_init(&out, CURL_X509_STR_MAX);
/* Prepare the certificate information for curl_easy_getinfo(). */
/* Extract the certificate ASN.1 elements. */
diff --git a/libs/libcurl/src/ws.c b/libs/libcurl/src/ws.c
index 37b1039a51..ebc976b70b 100644
--- a/libs/libcurl/src/ws.c
+++ b/libs/libcurl/src/ws.c
@@ -37,6 +37,7 @@
#include "ws.h"
#include "easyif.h"
#include "transfer.h"
+#include "select.h"
#include "nonblock.h"
/* The last 3 #include files should be in this order */
@@ -127,7 +128,7 @@ static void ws_dec_info(struct ws_decoder *dec, struct Curl_easy *data,
}
else {
CURL_TRC_WRITE(data, "websocket, decoded %s [%s%s payload=%"
- CURL_FORMAT_CURL_OFF_T "/%" CURL_FORMAT_CURL_OFF_T "]",
+ FMT_OFF_T "/%" FMT_OFF_T "]",
msg, ws_frame_name_of_op(dec->head[0]),
(dec->head[0] & WSBIT_FIN)? "" : " NON-FINAL",
dec->payload_offset, dec->payload_len);
@@ -136,6 +137,9 @@ static void ws_dec_info(struct ws_decoder *dec, struct Curl_easy *data,
}
}
+static CURLcode ws_send_raw_blocking(CURL *data, struct websocket *ws,
+ const char *buffer, size_t buflen);
+
typedef ssize_t ws_write_payload(const unsigned char *buf, size_t buflen,
int frame_age, int frame_flags,
curl_off_t payload_offset,
@@ -278,7 +282,7 @@ static CURLcode ws_dec_pass_payload(struct ws_decoder *dec,
dec->payload_offset += (curl_off_t)nwritten;
remain = dec->payload_len - dec->payload_offset;
CURL_TRC_WRITE(data, "websocket, passed %zd bytes payload, %"
- CURL_FORMAT_CURL_OFF_T " remain", nwritten, remain);
+ FMT_OFF_T " remain", nwritten, remain);
}
return remain? CURLE_AGAIN : CURLE_OK;
@@ -488,8 +492,7 @@ static const struct Curl_cwtype ws_cw_decode = {
static void ws_enc_info(struct ws_encoder *enc, struct Curl_easy *data,
const char *msg)
{
- infof(data, "WS-ENC: %s [%s%s%s payload=%" CURL_FORMAT_CURL_OFF_T
- "/%" CURL_FORMAT_CURL_OFF_T "]",
+ infof(data, "WS-ENC: %s [%s%s%s payload=%" FMT_OFF_T "/%" FMT_OFF_T "]",
msg, ws_frame_name_of_op(enc->firstbyte),
(enc->firstbyte & WSBIT_OPCODE_MASK) == WSBIT_OPCODE_CONT ?
" CONT" : "",
@@ -547,20 +550,20 @@ static ssize_t ws_enc_write_head(struct Curl_easy *data,
if(payload_len < 0) {
failf(data, "WS: starting new frame with negative payload length %"
- CURL_FORMAT_CURL_OFF_T, payload_len);
+ FMT_OFF_T, payload_len);
*err = CURLE_SEND_ERROR;
return -1;
}
if(enc->payload_remain > 0) {
/* trying to write a new frame before the previous one is finished */
- failf(data, "WS: starting new frame with %zd bytes from last one"
+ failf(data, "WS: starting new frame with %zd bytes from last one "
"remaining to be sent", (ssize_t)enc->payload_remain);
*err = CURLE_SEND_ERROR;
return -1;
}
- opcode = ws_frame_flags2op((int)flags);
+ opcode = ws_frame_flags2op((int)flags & ~CURLWS_CONT);
if(!opcode) {
failf(data, "WS: provided flags not recognized '%x'", flags);
*err = CURLE_SEND_ERROR;
@@ -773,7 +776,7 @@ CURLcode Curl_ws_accept(struct Curl_easy *data,
}
}
#endif
- DEBUGF(infof(data, "WS, using chunk size %zu", chunk_size));
+ CURL_TRC_WS(data, "WS, using chunk size %zu", chunk_size);
Curl_bufq_init2(&ws->recvbuf, chunk_size, WS_CHUNK_COUNT,
BUFQ_OPT_SOFT_LIMIT);
Curl_bufq_init2(&ws->sendbuf, chunk_size, WS_CHUNK_COUNT,
@@ -970,8 +973,8 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
infof(data, "connection expectedly closed?");
return CURLE_GOT_NOTHING;
}
- DEBUGF(infof(data, "curl_ws_recv, added %zu bytes from network",
- Curl_bufq_len(&ws->recvbuf)));
+ CURL_TRC_WS(data, "curl_ws_recv, added %zu bytes from network",
+ Curl_bufq_len(&ws->recvbuf));
}
result = ws_dec_pass(&ws->dec, data, &ws->recvbuf,
@@ -1001,14 +1004,14 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
ctx.payload_len, ctx.bufidx);
*metap = &ws->frame;
*nread = ws->frame.len;
- /* infof(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %"
- CURL_FORMAT_CURL_OFF_T ", %" CURL_FORMAT_CURL_OFF_T " left)",
- buflen, *nread, ws->frame.offset, ws->frame.bytesleft); */
+ CURL_TRC_WS(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %"
+ FMT_OFF_T ", %" FMT_OFF_T " left)",
+ buflen, *nread, ws->frame.offset, ws->frame.bytesleft);
return CURLE_OK;
}
static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws,
- bool complete)
+ bool blocking)
{
if(!Curl_bufq_is_empty(&ws->sendbuf)) {
CURLcode result;
@@ -1016,30 +1019,26 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws,
size_t outlen, n;
while(Curl_bufq_peek(&ws->sendbuf, &out, &outlen)) {
- if(data->set.connect_only)
+ if(blocking) {
+ result = ws_send_raw_blocking(data, ws, (char *)out, outlen);
+ n = result? 0 : outlen;
+ }
+ else if(data->set.connect_only || Curl_is_in_callback(data))
result = Curl_senddata(data, out, outlen, &n);
else {
- result = Curl_xfer_send(data, out, outlen, &n);
+ result = Curl_xfer_send(data, out, outlen, FALSE, &n);
if(!result && !n && outlen)
result = CURLE_AGAIN;
}
- if(result) {
- if(result == CURLE_AGAIN) {
- if(!complete) {
- infof(data, "WS: flush EAGAIN, %zu bytes remain in buffer",
- Curl_bufq_len(&ws->sendbuf));
- return result;
- }
- /* TODO: the current design does not allow for buffered writes.
- * We need to flush the buffer now. There is no ws_flush() later */
- n = 0;
- continue;
- }
- else if(result) {
- failf(data, "WS: flush, write error %d", result);
- return result;
- }
+ if(result == CURLE_AGAIN) {
+ CURL_TRC_WS(data, "flush EAGAIN, %zu bytes remain in buffer",
+ Curl_bufq_len(&ws->sendbuf));
+ return result;
+ }
+ else if(result) {
+ failf(data, "WS: flush, write error %d", result);
+ return result;
}
else {
infof(data, "WS: flushed %zu bytes", n);
@@ -1050,6 +1049,83 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws,
return CURLE_OK;
}
+static CURLcode ws_send_raw_blocking(CURL *data, struct websocket *ws,
+ const char *buffer, size_t buflen)
+{
+ CURLcode result = CURLE_OK;
+ size_t nwritten;
+
+ (void)ws;
+ while(buflen) {
+ result = Curl_xfer_send(data, buffer, buflen, FALSE, &nwritten);
+ if(result)
+ return result;
+ DEBUGASSERT(nwritten <= buflen);
+ buffer += nwritten;
+ buflen -= nwritten;
+ if(buflen) {
+ curl_socket_t sock = data->conn->sock[FIRSTSOCKET];
+ timediff_t left_ms;
+ int ev;
+
+ CURL_TRC_WS(data, "ws_send_raw_blocking() partial, %zu left to send",
+ buflen);
+ left_ms = Curl_timeleft(data, NULL, FALSE);
+ if(left_ms < 0) {
+ failf(data, "Timeout waiting for socket becoming writable");
+ return CURLE_SEND_ERROR;
+ }
+
+ /* POLLOUT socket */
+ if(sock == CURL_SOCKET_BAD)
+ return CURLE_SEND_ERROR;
+ ev = Curl_socket_check(CURL_SOCKET_BAD, CURL_SOCKET_BAD, sock,
+ left_ms? left_ms : 500);
+ if(ev < 0) {
+ failf(data, "Error while waiting for socket becoming writable");
+ return CURLE_SEND_ERROR;
+ }
+ }
+ }
+ return result;
+}
+
+static CURLcode ws_send_raw(CURL *data, const void *buffer,
+ size_t buflen, size_t *pnwritten)
+{
+ struct websocket *ws = data->conn->proto.ws;
+ CURLcode result;
+
+ if(!ws) {
+ failf(data, "Not a websocket transfer");
+ return CURLE_SEND_ERROR;
+ }
+ if(!buflen)
+ return CURLE_OK;
+
+ if(Curl_is_in_callback(data)) {
+ /* When invoked from inside callbacks, we do a blocking send as the
+ * callback will probably not implement partial writes that may then
+ * mess up the ws framing subsequently.
+ * We need any pending data to be flushed before sending. */
+ result = ws_flush(data, ws, TRUE);
+ if(result)
+ return result;
+ result = ws_send_raw_blocking(data, ws, buffer, buflen);
+ }
+ else {
+ /* We need any pending data to be sent or EAGAIN this call. */
+ result = ws_flush(data, ws, FALSE);
+ if(result)
+ return result;
+ result = Curl_senddata(data, buffer, buflen, pnwritten);
+ }
+
+ CURL_TRC_WS(data, "ws_send_raw(len=%zu) -> %d, %zu",
+ buflen, result, *pnwritten);
+ return result;
+}
+
CURL_EXTERN CURLcode curl_ws_send(CURL *data, const void *buffer,
size_t buflen, size_t *sent,
curl_off_t fragsize,
@@ -1057,60 +1133,53 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *data, const void *buffer,
{
struct websocket *ws;
ssize_t n;
- size_t nwritten, space;
+ size_t space, payload_added;
CURLcode result;
+ CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T
+ ", flags=%x), raw=%d",
+ buflen, fragsize, flags, data->set.ws_raw_mode);
*sent = 0;
if(!data->conn && data->set.connect_only) {
result = Curl_connect_only_attach(data);
if(result)
- return result;
+ goto out;
}
if(!data->conn) {
failf(data, "No associated connection");
- return CURLE_SEND_ERROR;
+ result = CURLE_SEND_ERROR;
+ goto out;
}
if(!data->conn->proto.ws) {
failf(data, "Not a websocket transfer");
- return CURLE_SEND_ERROR;
+ result = CURLE_SEND_ERROR;
+ goto out;
}
ws = data->conn->proto.ws;
+ /* try flushing any content still waiting to be sent. */
+ result = ws_flush(data, ws, FALSE);
+ if(result)
+ goto out;
+
if(data->set.ws_raw_mode) {
+ /* In raw mode, we write directly to the connection */
if(fragsize || flags) {
- DEBUGF(infof(data, "ws_send: "
- "fragsize and flags cannot be non-zero in raw mode"));
+ failf(data, "ws_send, raw mode: fragsize and flags cannot be non-zero");
return CURLE_BAD_FUNCTION_ARGUMENT;
}
- 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_xfer_send(data, buffer, buflen, &nwritten);
- }
- else
- result = Curl_senddata(data, buffer, buflen, &nwritten);
-
- infof(data, "WS: wanted to send %zu bytes, sent %zu bytes",
- buflen, nwritten);
- *sent = nwritten;
- return result;
+ result = ws_send_raw(data, buffer, buflen, sent);
+ goto out;
}
/* Not RAW mode, buf we do the frame encoding */
- result = ws_flush(data, ws, FALSE);
- if(result)
- return result;
-
- /* TODO: the current design does not allow partial writes, afaict.
- * It is not clear how the application is supposed to react. */
space = Curl_bufq_space(&ws->sendbuf);
- DEBUGF(infof(data, "curl_ws_send(len=%zu), sendbuf len=%zu space %zu",
- buflen, Curl_bufq_len(&ws->sendbuf), space));
- if(space < 14)
- return CURLE_AGAIN;
+ CURL_TRC_WS(data, "curl_ws_send(len=%zu), sendbuf=%zu space_left=%zu",
+ buflen, Curl_bufq_len(&ws->sendbuf), space);
+ if(space < 14) {
+ result = CURLE_AGAIN;
+ goto out;
+ }
if(flags & CURLWS_OFFSET) {
if(fragsize) {
@@ -1118,12 +1187,12 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *data, const void *buffer,
n = ws_enc_write_head(data, &ws->enc, flags, fragsize,
&ws->sendbuf, &result);
if(n < 0)
- return result;
+ goto out;
}
else {
if((curl_off_t)buflen > ws->enc.payload_remain) {
infof(data, "WS: unaligned frame size (sending %zu instead of %"
- CURL_FORMAT_CURL_OFF_T ")",
+ FMT_OFF_T ")",
buflen, ws->enc.payload_remain);
}
}
@@ -1132,16 +1201,66 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *data, const void *buffer,
n = ws_enc_write_head(data, &ws->enc, flags, (curl_off_t)buflen,
&ws->sendbuf, &result);
if(n < 0)
- return result;
+ goto out;
}
n = ws_enc_write_payload(&ws->enc, data,
buffer, buflen, &ws->sendbuf, &result);
if(n < 0)
- return result;
+ goto out;
+ payload_added = (size_t)n;
+
+ while(!result && (buflen || !Curl_bufq_is_empty(&ws->sendbuf))) {
+ /* flush, blocking when in callback */
+ result = ws_flush(data, ws, Curl_is_in_callback(data));
+ if(!result) {
+ DEBUGASSERT(payload_added <= buflen);
+ /* all buffered data sent. Try sending the rest if there is any. */
+ *sent += payload_added;
+ buffer = (const char *)buffer + payload_added;
+ buflen -= payload_added;
+ payload_added = 0;
+ if(buflen) {
+ n = ws_enc_write_payload(&ws->enc, data,
+ buffer, buflen, &ws->sendbuf, &result);
+ if(n < 0)
+ goto out;
+ payload_added = Curl_bufq_len(&ws->sendbuf);
+ }
+ }
+ else if(result == CURLE_AGAIN) {
+ /* partially sent. how much of the call data has been part of it? what
+ * should we report to out caller so it can retry/send the rest? */
+ if(payload_added < buflen) {
+ /* We did not add everything the caller wanted. Return just
+ * the partial write to our buffer. */
+ *sent = payload_added;
+ result = CURLE_OK;
+ goto out;
+ }
+ else if(!buflen) {
+ /* We have no payload to report a partial write. EAGAIN would make
+ * the caller repeat this and add the frame again.
+ * Flush blocking seems the only way out of this. */
+ *sent = (size_t)n;
+ result = ws_flush(data, ws, TRUE);
+ goto out;
+ }
+ /* We added the complete data to our sendbuf. Report one byte less as
+ * sent. This parital success should make the caller invoke us again
+ * with the last byte. */
+ *sent = payload_added - 1;
+ result = Curl_bufq_unwrite(&ws->sendbuf, 1);
+ if(!result)
+ result = CURLE_AGAIN;
+ }
+ }
- *sent = (size_t)n;
- return ws_flush(data, ws, TRUE);
+out:
+ CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T
+ ", flags=%x, raw=%d) -> %d, %zu",
+ buflen, fragsize, flags, data->set.ws_raw_mode, result, *sent);
+ return result;
}
static void ws_free(struct connectdata *conn)
@@ -1156,7 +1275,7 @@ static void ws_free(struct connectdata *conn)
static CURLcode ws_setup_conn(struct Curl_easy *data,
struct connectdata *conn)
{
- /* websockets is 1.1 only (for now) */
+ /* WebSockets is 1.1 only (for now) */
data->state.httpwant = CURL_HTTP_VERSION_1_1;
return Curl_http_setup_conn(data, conn);
}